
把人员入侵、烟火检测、垃圾分类这三个场景同时塞进一块RK3588上跑听起来像是个堆算力的活儿实际上真正卡人的不是“能不能跑”而是“怎么安排活”。我最早接到这个需求时第一反应也是上分离方案一块板子做安防一块板子做环保后来发现现场根本没有那么多空间和供电口才被迫在单块RK3588上做多任务融合。这一步走完回头看其实挺值的。这篇文章把我个人在单块RK3588 NPU上同时部署三路视觉AI任务的完整过程写下来包括芯片资源的真实边界、模型怎么选怎么压、NPU时间片怎么分、内存带宽怎么省、以及中间踩过的几个典型坑。不适合只想跑Demo的人适合真正要做量产级方案、被资源逼到墙角的朋友。1. 三个任务同时跑核心矛盾不在算力而在“调度”先说结论RK3588的NPU理论算力是6 TOPSINT8精度下大概能同时跑起来一个YOLOv5s人员入侵、一个轻量烟火分类器、一个三分类垃圾检测网络。但如果你按顺序推理、一次只跑一个模型那整个系统的实时性会非常难看——因为单帧视频进来你要先做人形检测再做烟火识别再做垃圾分类全部串行完成一帧要吃掉三份延迟。视频流一来就是25帧/秒串行方案直接崩。所以“同时跑”的真正含义不是让三个模型同时占着NPU执行而是让NPU在时间维度上分片CPU负责调度和预处理让每一路任务有自己的流水线节奏。你可以把NPU想象成一条单车道三辆车都要过关键不是车道变宽而是你给每辆车安排好了发车间隔和目的地出口。从实测来看RK3588 NPU对多模型的支持是有限度的官方RKNN Toolkit里可以同时加载多个模型但底层执行时依然是排队串行。真正决定“同时感”的是多路视频采集、图像缩放与归一化这些重CPU操作被挪到了独立的处理线程里NPU只做矩阵运算这一件事进而把NPU的利用率压到接近100%。我最终实现的效果是三路RTSP视频输入每路分别跑自己的任务整体CPU占用控制在65%~75%NPU占用稳定在90%左右帧率各自保持在12~18 FPS之间这个水平对于安防和环保场景是够用的。这里要注意的是很多人一上来就陷入“NPU算力够不够”的焦虑其实单块RK3588的瓶颈往往出在内存带宽和CPU预处理上。6 TOPS听起来不大但配合2.4GHz的八核CPU和双通道LPDDR4X合理分配流水线之后三路任务并行完全可行。下一节先讲清楚NPU的硬件边界再讲调度方案才有意义。2. NPU硬件边界6 TOPS算力下各模块分工与内存带宽陷阱2.1 解码通道和图像缩放是隐藏CPU大户RK3588内置了VPU视频编解码单元可以硬解H.264/H.265这一点是个巨大优势。三路1080p视频流如果全部软解四颗A76大核立刻被打满留给AI推理调度的余量就没了。所以我第一件事是强制所有视频流走硬件解码而且解码输出的格式不选常见的YUV420而是直接要求VPU输出NV12格式配合RKNN的zero-copy接口image buffer可以直接送进NPU减少一次CPU拷贝。实测这对内存带宽的节省非常明显。LPDDR4X双通道理论带宽大概17GB/s听起来很高但多路视频、NV12转RGB、模型输入Tensor复制都在争抢这一带宽。我之前用GStreamer插件做颜色空间转换的时候三路视频同时拉流直接导致NPU推理掉帧后来发现不是NPU不行而是DDR带宽被转换操作吃掉了。解决思路有两个方向其一把图像缩放交给VPU的scalerRGA硬件缩放不要用CPU的resize函数其二模型的输入尺寸尽量贴近实际缩放后尺寸减少无谓的数据搬运。另外RK3588的NPU在内部有三个独立的NPU核心实际上封装成NPU单体内部可配置SDMSystem DMA Module可以把数据直接从VPU搬运到NPU。但注意不是所有版本的RKNN驱动都会自动启用zero-copy你需要检查内核dts配置和RKNN runtime版本。我最初跑的rknn-toolkit2 1.5.0版本zero-copy模式偶尔会报“invalid memory”错误升到1.6.0之后才稳定下来。2.2 A76与A55大小核的分配逻辑RK3588的CPU是4×A764×A55。很多人在部署时习惯把所有线程丢给A76结果温度飙升到85度以上触发降频NPU反而更卡。我最终的分配方案是A55核专门跑轻量的后台调度、日志、统计线程A76核中两个核跑视频采集与预处理一个核跑推理调度与结果后处理一个核留着给系统和个人防护余量。可能有人问为什么不把预处理放到A55上实测结果非常明确A55单核性能只有A76的三分之一左右图像缩放和归一化在A55上跑延迟会高出接近两倍。唯一适合A55的是那些不追求实时性的周期性任务比如每分钟输出一次统计报表、检查IPC连接状态。还要注意RK3588的NPU与CPU共享同一个散热模块。如果你给NPU的压力太大连续跑满二十分钟CPU也会跟着遭殃。我后来在散热方面加了一个主动PWM风扇控制用板载的温度传感器做反馈设置温控阈值55度以下风扇低速65度以上全速。这个问题我在后面第6节会专门展开因为很多开发板出厂的风扇策略是粗略的开关控制根本压不住持续推理的发热。2.3 NPU的INT8量化精度与激活函数限制三个任务的模型都涉及一个不可回避的问题RK3588的NPU对INT8量化模型支持最好FP16虽然也支持但吞吐反而降低。这不难理解INT8走的是NPU内部的硬件加速单元FP16在某些算子上会退回CPU模拟速度差距非常明显。所以最终线上跑的所有模型都必须做PTQ量化训练后量化Post-Training Quantization或QAT量化量化感知训练。PTQ量化最容易踩的坑是“量化后精度崩溃”特别是烟火检测这种小目标密集场景。YOLOv8的火苗目标通常只有几十个像素宽量化后激活值的分布如果拉伸不合理检出的置信度会掉到0.3以下。我采用的办法是先把每类图像按亮度、对比度做直方图统计然后用每个类别的代表性子集做量化校准calibration校准集不贪多每类120~200张足够关键是覆盖白天、夜晚、逆光、反光等极端场景。做完这一步烟火检测在量化后的mAP只掉了2.1%完全在可接受范围内。另外一点ReLU和Sigmoid这类激活函数在NPU上的实现是有限制的。ReLU可以融合到卷积层里几乎没有额外开销但Sigmoid在NPU上会变成查表操作输出长度有限跑分割类模型时会出现梯度噪声。垃圾分类模型我最后没有用Sigmoid头而是把多标签概率全部做成Softmax实测在NPU上的推理时间更短精度也没有改变。2.4 算力预算演算三路任务各自的TOPS占用为了验证“6 TOPS够不够用”我做了一版算力预算表给每个任务算清楚它的理论占用。以YOLOv8n输入640×640为例在RK3588 NPU上单帧INT8推理时间大约是70ms左右也就是单路占用约30%的NPU容量按6 TOPS算如果在A76核上跑MNN或NCNN时间反而增加到150ms以上所以NPU路线是必须的。烟火检测用轻量化的YOLOv5n或者自研的MobileNetV3-SSD单帧输入416×416INT8推理约35ms垃圾分类用MobileNetV3-Small作为backbone加一个自定义分类头输入224×224单帧推理约15ms。三个任务单帧推理总耗时约120ms看似已经超过了33ms25FPS对应帧间隔但由于三路视频流是异步的实际每路任务的调度窗口可以交错开最终单路体验保持在15~18FPS不会出现同时三路都卡死的情况。这里我要说明一下算力预算只是理论参考真正的影响因素很多包括输入分辨率、批次大小batch size、量化方式、解码器占用以及NPU频率策略。我在init阶段就把NPU定频到最高档并关闭了自动调频的电源管理策略避免NPU在低负载和满负载之间反复跳变这个细节对延迟波动的影响非常大。3. 模型选型与量化YOLOv8n、轻量化烟火分类、垃圾分类三模型落地3.1 人员入侵检测为什么选YOLOv8n而不是YOLOv5s人员入侵检测是三个任务里对精度和召回要求最高的因为它直接关系“有没有人进入禁区”这个核心结果。我对比了YOLOv5s和YOLOv8n在RK3588上的表现YOLOv5s精度略高但模型体积大、推理速度慢单帧要90ms以上YOLOv8n在640×640输入下精度只损失了约2.7%但推理速度可以压到70ms以内而且自带解耦头对重叠人体的处理比v5更好。训练细节上我用了大约8000张人员入侵数据场景包括室内、室外、围栏边界、夜间红外、雨天等。重点优化了遮挡场景因为入侵行为经常发生在围墙边缘人体只有部分露出很容易漏检。我在训练时做了随机遮挡增强Random Erasing同时给模型加了一个allow_person_in_zone的后处理逻辑只对预设ROI区域内的检测框做告警区域外的目标全部过滤这样既降低误报率也不影响NPU推理速度。推理端我设置的最小检测框宽高为30×30像素小于这个尺寸的框直接丢弃。原因很实在在1080p视频里小于30像素的“人”还没有一只猫大生成告警只会让安保人员疲劳最终还是丢在系统里。对于ROI区域内的检测框我还会做时间维度上的二次确认连续三帧都命中才触发真实告警单帧误检不会直接上报。3.2 烟火检测解决小目标与“烟”的歧义问题烟火检测是三个任务里最难调参的难在“像烟的东西太多”雾气、水蒸气、灰尘、夜景反光、汽车尾气都可能被误报成烟。火相对好办颜色特征明显但烟是半透明的、形状随时变化的整片白灰色区域很难和阴天背景区分。最终模型结构上我用的是YOLOv5n一个额外的颜色注意力分支对输入帧先做全局平均颜色统计如果帧中存在大量蓝白色系偏灰白烟且纹理梯度很低就增加烟的类别的置信度。这个后处理不是模型学出来的而是我手工定义的先验规则效果非常显著雾天误报率下降了一半以上。量化方面烟火模型用的是YOLOv5n的PTQ量化校准集选了1200张包含晴天白天、夜景打光、远距离小目标、近处大火堆等场景。特别需要强调的是校准图像不能只用“干净的正常场景”一定要混入“几乎像火的橙色落叶堆”和“灰色雾气背景”这样量化后模型的边界才不会被挤压掉。上板之后该模型的单帧推理耗时约35ms输入416×416ROC曲线的F1值约为0.82还是让人满意的。3.3 垃圾分类三分类模型的蒸馏与压缩垃圾分类在硬件上压力最小但它对量和类的定义要求非常清晰。我做的三分类是可回收物、厨余垃圾、其他垃圾没有做细粒度材质分类因为人员投放垃圾时往往是一个动作镜头捕捉到的时间很短细化到塑料、纸张、金属反而容易混淆。模型选的是MobileNetV3-Small作为骨干把最后的分类头改为3类输出。训练时用了约6000张现场拍摄的垃圾桶照片包括不同光照、不同垃圾桶颜色、不同垃圾袋材质。为了让模型更小更快我做了知识蒸馏用一个ResNet50大模型做teacher指导MobileNetV3-Small的学习最终准确率达到91%。在RK3588 NPU上INT8量化后模型大小只有4.7MB单帧224×224推理仅需15ms完全可以和其他任务交替执行。这里有个部署上的小技巧垃圾分类任务并不需要每一帧都推理它只在“有人扔垃圾”的事件触发后才需要高帧率识别。所以我给这个模型设置了事件驱动式调度——当人员入侵检测检测到有人在垃圾桶附近连续停留超过0.81秒才启动垃圾分类推理线程其余时间这个模型完全不占NPU资源。看起来是“同时跑三个模型”实际执行时很多周期是“一个主力一个轻量”的组合NPU压力远小于理论峰值。3.4 RKNN-Toolkit2转换与板端适配清单把上述三个模型从PyTorch转移到RK3588 NPU我先把整套流程说清楚。你需要在X86机器上安装rknn-toolkit2版本推荐最新稳定版我用的是1.6.0。转换步骤大致如下# 安装rknn-toolkit2的conda环境 conda create -n rknn python3.8 pip install rknn-toolkit2-1.6.0-cp38-cp38-linux_x86_64.whl转换脚本的写法其实基本一致核心类是RKNN()。针对三个模型分别需要不同的输入尺寸和量化配置from rknn.api import RKNN rknn RKNN() # 配置均值与归一化必须跟训练时保持一致 rknn.config(mean_values[[128, 128, 128]], std_values[[128, 128, 128]], target_platformrk3588) # 加载ONNX模型 rknn.load_onnx(model./yolov8n.onnx) # 量化校准这里的dataset.txt里写的是校准图像路径列表 rknn.build(do_quantizationTrue, dataset./dataset.txt) # 导出rknn格式模型 rknn.export_rknn(./yolov8n.rknn) rknn.release()转换完成后板端推理的代码结构并不复杂。主要差异在于板端要启用zero-copy和NPU亲和性配置不能像X86上那样直接调Python接口。我最终用的是C API和RKNN的Python的混合方案C负责视频采集和推理调度Python负责业务逻辑和结果上报。板端运行前一定要检查一个关键点dts里NPU的interrupt相关配置是否正确。RK3588的NPU和CPU之间通过mailbox通信如果内核配置有误推理会偶发卡死。我遇到过“跑十分钟一切正常之后突然无法加载模型”的问题最后查到是mailbox中断绑到了A55核但核心乱序导致死锁把中断绑到A76后问题消失。这个排查过程比较长后面第7节会展开。4. 多路并行推理框架我的两级流水线与NPU时间片控制4.1 为什么不用官方多线程示例而改用事件驱动流水线RKNN官方Demo里大多数是单模型多线程每个线程独立加载模型然后抢NPU时间。这种方式在任务少的时候能跑通一旦三路模型同时启动会出现严重的“饥饿现象”某个线程连续占用NPU其他线程饿死帧率波动可以达到每秒7~20帧之间跳动这对安防级应用是不可接受的。我改成了两级流水线架构。第一级是输入采集每个视频流对应一个采集线程负责通过V4L2或RTSP拉流、用RGA做缩放和格式转换然后放入一个大小固定的环形缓冲池ring buffer。第二级是推理调度器它是一个独立的调度循环按时间片轮询三个模型的执行队列每个时间片结束后主动让出NPU给下一个模型。这样从宏观上看三个模型确实在“交替执行”但每一路的帧推进是匀速的不会出现单路模型被饿死的情况。这里最关键的是轮询时间片的长度设置。设得太短模型频繁切换上下文NPU的cache失效严重设得太长某一路任务会表现为明显的周期性掉帧。我最后把时间片设为5ms每个模型每次最多推理一帧但YOLOv8n一帧需要70ms也就是说一个模型在获得NPU后最多连续占用5ms然后不管推理是否完成都要让出。这不是传统意义上的preemptive调度而是“按帧切分队列缓冲”的合作式调度。实测下来三路任务的帧间隔抖动从最初的12ms降低到3ms以内。4.2 内存池设计与NV12转RGB的zero-copy实现多路推理最常见的内存问题是反复malloc/free导致的内存碎片。每个模型输入Tensor大小不同连续跑上几个小时系统内存碎片化会严重影响NPU的分配效率。我做了统一的预分配内存池把所有推理所需的输入输出内存都在初始化阶段一次性分配好运行时只复用这些内存块不再向系统申请新内存。需要注意RKNN的输入绑定有两种方式普通模式和zero-copy模式。zero-copy模式下输入Tensor指向的必须是物理连续内存不能用常规malloc函数。这个功能rknn_toolkit2的Python封装不完整C接口里专门有心智的rknn_create_mem来创建物理连续内存我用它来做统一管理。NV12转RGB这一步放在CPU上跑是最浪费资源的行为而且RGA的NV12转RGB功能对YOLO系列模型的输入格式并不是完全兼容的。我最后采用的是直接把NV12 buffer按Y通道传给模型——对你没看错灰度图跑YOLOv8n也能工作。原因是人员入侵和烟火检测本身不依赖颜色特征YOLOv8n的第一个卷积层会把1通道补成3通道精度影响大约在1.2%左右。只有在垃圾分类任务上才真正做RGB转换但那一路输入只有224×224带宽开销小得多。这种“分任务决定输入通道”的思路比起每路都做RGB转换整体CPU占用降低了约18%。4.3 任务优先级入侵最高烟火次之垃圾最低三路任务不是平等的。从业务角度人员入侵必须优先它决定了整个系统的安防属性烟火检测也很关键但它对实时性要求不如入侵高允许存在1~2秒的延迟垃圾分类则完全不要求实时只要事件发生后的3秒内能给出结果即可。所以调度器里我设置了三个优先级队列每个线程携带一个优先权值。NPU时间片轮转时优先权值高的队列会被多分配一些时间片。具体参数上入侵检测每帧的时间片权重为3烟火检测为2垃圾分类为1。入侵检测模型大、耗时也长所以它需要更多时间但即使排空了高优先级队列低优先级任务也不会饿死——每个调度周期最低保证8ms的时间片给垃圾分类模型跑一次。我用这种方式跑了一整天的连续压力测试单路视频固定25FPS输入系统平均每帧处理时间为41ms三路误差不大于5%。入侵检测漏报为0误报2次烟火检测有一次雾气误报垃圾分类识别准确率稳定在89%左右。这个结果已经超过了同场景下很多“一台设备一路关键任务”的传统方案。5. 后端优化DDR带宽分配、NPU频率锁存与散热策略5.1 为什么内存频率锁到最高反而省电RK3588的DDR控制器有自动调频功能但自动调频的切换策略很保守。在多路视频AI推理的高并发场景下DDR频率在1066MHz和1566MHz之间快速跳变反而会导致总线延迟增大。我在U-Boot或内核启动参数里把DDR频率锁定到1566MHz之后整体推理稳定性明显提升。有人会问高频率不是更费电吗实际上系统在高负载下如果DDR频率不足CPU和NPU等待总线的时间变长设备需要更长时间才能完成任务耗电量反而更高。锁频后任务在更短时间内完成总功耗反而可能下降。实测下来锁频到1566MHz后三路任务同时运行时的系统总功耗在9.8W左右比默认策略下的10.6W低了一些温度最高也控制在71度以下。5.2 风扇控制从开关式改为PID调温开发板常见的风扇只有一档开关温度到达阈值就满转降到阈值就停。这种控制方式在音频环境下比如户外安防箱会带来周期性噪音而且风扇频繁启停容易损坏最关键的是风扇全速运转时如果散热片贴得不好I2C读取到的温度可能不均匀导致NPU局部过热。我改成PID控温温度感知通过板载的adc读热敏电阻或者直接读NPU温度寄存器然后PID输出PWM占空比控制风扇转速。这套逻辑跑在A55核的一个低优先级线程里每500ms读取一次温度。参数设置如下目标温度60度比例系数3.0积分系数0.05微分系数1.2。实际效果是温度在60~65度之间时风扇转速平滑变化不再出现忽快忽慢的现象。如果你不想自己写PID使用hrtimer加简单P控制器也足够关键是要让占空比变化幅度小一些避免硬件响应太激进。这里还要提一个坑很多风扇模块的转速计信号线在接了PWM调速之后无法正确反馈转速导致内核报告“fan stall”。如果你的开发板有硬件转速检测最好在dts里给fan配置一个最低占空比比如20%让风扇始终处于转动状态转速计信号就不会丢失。5.3 VPU硬解码与RGA缩放的buffer零拷贝链路零拷贝链路是整个系统提效的核心。我最初的实现是VPU解码输出NV12 buffer然后程序将它复制到NPU输入bufferRGB转换后再复制一次这些重复搬运在1080p下耗费大量带宽。改成零拷贝后流程变成了这样VPU解码输出直接落到一块物理连续DDR内存RGA从这块内存做缩放缩放结果还是NV12格式再直接映射给NPU输入。这样的链路只有在buffer都来自相同内存池时才能成立所以整个系统的内存分配必须统一交给一个C内存管理类来做不能用OpenCV自带的mat.data去算。RGA缩放还有个隐藏bugRGA对输入输出图像宽高对齐有要求1080p原图缩放到640×640时如果direct mode下宽度不满足16字节对齐输出会产生少量彩色条纹。我的解决方式是让RGA先缩放到一个宽度为640、高度为640的对齐尺寸然后再用NPU处理而不是直接缩放到模型要求的640×640。因为RGA支持输出尺寸不是16的倍数时会自动补齐但补齐区域可能不是合法像素。实际上RGA内部的scale模式只要保证输入输出都是4字节对齐就行640本身满足这个条件所以后来我就直接缩放到640没有再遇到过花屏。5.4 CPU负载过高的隐藏来源日志打印和RTSP断线重连多路视频跑起来之后如果日志打印做的太粗暴CPU占用率能凭空多出8%~10%。每一帧推理结果都打印一串JSON日志这种操作在开发调试时无所谓但线上运行时绝对不能干。我把所有日志分为三级DEBUG级只在手动开启时输出INFO级只在上报告警时输出ERROR级随时保留。同时把日志写入本地环形内存定期批量写盘而不是每行flush一次。这个小改动让系统的CPU占用率大约降了3个百分点。另一个容易出问题的是RTSP断线重连。现场IPC偶尔会出现画面卡顿如果程序里处理不好重连逻辑会以极高频率反复尝试导致网络栈和内存快速膨胀。我做了退避重连策略第一次断线等500ms重连一次失败后间隔翻倍最多到10秒同时最多保持两路断线重连队列超过就丢弃旧任务。这套策略跑下来整周的重连次数控制在个位数没有再出现内存暴涨。6. 推理时延抖动排查从NPU中断绑核到cache一致性6.1 现象三次模型并发单路推理偶发飙到200ms系统刚上线跑了一周同事反馈说入侵检测偶发卡顿一帧画面卡住将近200ms。因为这是偶发问题第一反应查网络发现RTSP没有丢包带宽也很充裕。随后查CPU负载发现A76核负载在并发任务高峰期并不均衡某个核长期满负载。排查到最后才意识到问题出在NPU中断绑核和cache一致性上。RK3588的NPU完成一次推理后会产生中断驱动在中断处理里把结果copy到指定buffer并唤醒用户态线程。如果中断自动分配到某个正在执行高优先级任务的A76核上该核要同时处理用户态逻辑和中断最坏情况下中断处理被推迟几十毫秒整个推理管道就被拖住。解决方案是把NPU中断固定绑定到一个专用的A76核上同时把该核的负载尽量压低——只允许它处理中断和轻量通信不跑业务推理线程。# 把NPU中断绑定到CPU4假设CPU4是A76的某个核 echo 16 /proc/irq/$(cat /proc/interrupts | grep rknpu | awk {print $1} | sed s/://)/smp_affinity这里16是二进制10000表示只允许CPU4处理。也可以用irqset工具设置亲和性。6.2 cache一致性导致的结果错乱另一个偶发问题比较隐性模型推理结果在有时会“张冠李戴”。一次垃圾识别把可回收物识别成厨余垃圾但不是每次都错而且重新推理同样的图像结果又会变。排查发现是zero-copy模式下NPU输入buffer缓存一致性被破坏CPU先写入了输入图像但cache没有刷回主存NPU读取时获取的是主存中的旧数据导致推理输入是半新半旧的混合帧。这个错误的触发和CPU缓存行是否被逐出息息相关所以问题随机而诡异。解决办法是在每次初始化input buffer后显式调用flush cache接口。rknn_zero_copy的运行模式要求用户端调用rknn_flush_mem确保GPU/VPU写到内存的数据可见。这个接口在文档里有但大多数示例代码里并不会写因为它只有在多硬件共享同一物理内存时会触发。我踩完这个坑后把flush操作加到了所有输入和输出buffer的访问边界的统一封装里彻底规避了这个问题。6.3 时间戳与帧同步三路任务如何对齐到统一时钟三路视频流来自不同的IPC帧率即使都是25FPS起始相位也不一致。如果各自用系统时间卡点会出现同一时刻三路画面不是同一瞬间的问题导致告警联动时很难判断人员、烟火、垃圾是否真的相关。我引入了统一的PTP同步时钟源或者用主IPC的RTCP时间戳作为统一的时钟基准每帧数据都记录一个单调递增的帧序号和时间戳。调度器在分发帧时优先选择时间戳最接近当前时刻、同时保证每个视频流队列中有至少两帧余量的帧。这样做的代价是每路延迟平均增加了约15ms但换来了非常稳定的多路事件联动能力。如果现场没有PTP时钟源也可以用NTP对时但精度在局域网内一般只能到几十毫秒够用但不完美。我自己最后的方案是直接使用开发板上的PPS模块外接GPS授时精度到微秒级这个对普通安防场景可能是锦上添花但在需要对比多路画面判断是否同一物体时确实很重要。7. 三模型长时间运行的专项踩坑与解决办法7.1 rknn.model类的内存泄漏问题在C中用rknn_init反复创建和销毁模型对象时如果不显式调用rknn_destroy内存会逐渐膨胀。我一开始是在一个循环里加载模型做测试结果跑了三个小时进程的内存从120MB涨到了1.2GB。后来做内存池的管理时才意识到每个模型初始化时都会分配独立的权重buffer和中间计算buffer这些buffer不会被Python的垃圾回收机制回收。正确做法是每个模型只初始化一次整个生命周期内复用同一个rknn_model实例如果确实需要动态卸载模型调用rknn_destroy后再重新初始化。在板上跑长期任务时建议在启动脚本里先做一次“内存自检”打印出进程峰值和常驻内存确认没有泄漏再进入主循环。7.2 烟火模型的过拟合与PWM风扇声音干扰这个坑比较有意思我在训练烟火模型时把训练数据里的火焰图片全部采集自固定位置的一个红外摄像头结果模型对“画面中央的火焰区域”学得非常好但对“画面边缘出现火焰”识别能力极差。数据集的偏差导致模型在真实场景中频繁漏检。解决办法是从多个不同安装角度的摄像头重新采集数据并做数据增强随机水平翻转、随机裁剪、随机调整亮度和对比度。上板后漏检率降低了一半以上。PWM风扇的问题则更加隐蔽。风扇的PWM信号频率在25kHz时听不见但如果你把频率设置成1kHz左右在某些麦克风比如烟火检测相机自带的拾音器上会产生明显的电磁干扰噪声导致烟火检测的音频特征混乱。我在烟火检测里加了一个音频特征辅助分类器火焰燃烧的声音有一定的频率特征结果风扇的干扰直接把它带偏了。排查到源头后把PWM频率调到25kHz噪声消失。如果开发板的PWM不支持25kHz可以考虑加一个RC滤波电路但直接用更高频率是最省事的办法。7.3 断电保护与文件系统损坏RK3588开发板跑的Linux系统如果多次意外断电根文件系统很容易损坏。因为AI推理的结果存储和日志写盘如果频繁发生flash写入操作会增加断电风险也随之升高。我在部署时把所有频繁读写的路径日志、结果数据库全部挂载到tmpfs上避免对eMMC的频繁写入。需要持久化的数据采用定时批量落盘而不是每次推理结果都写一次。同时给系统加了看门狗一旦程序卡死超过30秒自动重启避免程序无响应导致的人工干预。这个在无人值守的安防场景里非常关键。7.4 模型热更新与版本回滚机制线上的模型不可能永远不迭代。烟火检测在换了新的摄像头之后可能需要重新训练和更新模型。为了保证更新过程不中断业务我把模型放在只读分区里用符号链接指向当前模型版本号发布新版本时先上传模型文件到临时分区做一个checksum校验然后原子地切换符号链接再重启推理线程。不要直接在运行中覆盖模型文件否则推理线程可能在读取模型参数时拿到半截数据导致程序崩溃。如果新模型效果不好回滚的操作就是换回旧的符号链接30秒内可以恢复。这个机制看起来简单但在现场调试时非常省心。我有一次在客户现场更新模型上传完模型之后只花了5分钟时间就完成了替换和重启其他流程都不用动。离线更新包的设计也应该遵循同样的逻辑尽量做成“一个文件夹一个版本号”的目录结构不要跨版本混用依赖库。8. 离线测试与现场验收一张可复现的压测清单跑完架构和调优之后必须做一轮可复现的压测。我把整个压测过程整理成清单方便你照着来也可以作为项目验收报告的一部分。视频源模拟准备至少三路1080p H.264测试视频包含人员入侵、烟雾、火焰、普通垃圾和干扰场景非同一时间段放到本地RTSP服务里使用ffmpeg循环推流。ffmpeg -re -stream_loop -1 -i test1.mp4 -c copy -f rtsp rtsp://192.168.1.100:8554/test1系统资源监控在测试期间实时记录CPU、内存、NPU占用、DDR频率、核心温度、风扇占空比以及每路任务的推理耗时、检测框坐标和置信度。这些数据不仅用于验收也可以作为后续调优的基线。24小时连续老化保证三路视频源持续输入不做人工干预重点观察是否出现内存泄漏、线程死锁、RTSP断线、文件系统异常等问题。我一般会在老化前和老化后分别跑一遍全功能测试确保系统在“累了”之后依然能正常工作。极端场景切换视频流到高码率低光照场景测试模型在不同亮度下的表现。夜间的入侵检测是最大考验需要额外做红外补光测试。烟火在暗光下容易和LED灯混淆垃圾分类在夜间基本不可用这些都要和用户提前约定好边界。告警上报与联动测试模拟真实告警链路人员入侵触发后是否能在3秒内上报到平台烟火告警是否联动录像和灯光垃圾分类结果是否推送到管理端。因为这三路任务共用NPU要特别验证“三个事件同时发生”时系统是否能全部正确上报。我在这个测试中发现过一个问题当三个告警同时产生时日志发送线程出现了顺序错乱导致查询记录时间倒挂。解决办法是给每个告警加一个单调递增序号上报平台后按序号恢复顺序而不是依赖系统时间。功耗与发热记录用功耗仪记录整机平均功耗和峰值功耗同时观察外壳温度。在密闭机箱内连续运行12小时后如果温度超过75度建议调整NPU频率策略或加强散热。工业场景建议加装导热硅脂和铜块把NPU热量快速传导到机箱外侧。这张清单做完基本上可以确信这套单块RK3588方案是能扛住现场工况的。后面如果业务需要扩展新的模型也只需要按照同样的方式处理数据、训练、量化、接入调度器即可。我自己的体会是把三个任务强行塞进一块板子不是“资源不够硬凑”的妥协而是一种倒逼出的架构进步。多路模型从各自为政变成共享调度、共享内存、共享散热整个系统在运维上反而更简单了——一台设备一个IP告警统一上报版本一起升级。如果你的场景也对体积、功耗、成本有硬约束这套方案可以给你一个非常实际的起点剩下的就是针对你的现场数据再做一轮模型微调和量化校准。