009、嵌入式部署:YOLO在Jetson、RKNN、STM32MP1等边缘设备上的移植与优化

张开发
2026/4/16 14:10:18 15 分钟阅读

分享文章

009、嵌入式部署:YOLO在Jetson、RKNN、STM32MP1等边缘设备上的移植与优化
009、嵌入式部署YOLO在Jetson、RKNN、STM32MP1等边缘设备上的移植与优化深夜两点Jetson Nano的风扇还在嘶吼。屏幕上卡在98%的TensorRT引擎构建进度条旁边是第17杯冷掉的咖啡。这行trtexec命令已经跑了三个小时最后一次尝试是因为忘了加--fp16参数又得重来。隔壁工位的RK3566开发板更安静——它直接黑屏了连串口都没反应估计是NPU内存超了。至于那块STM32MP157的板子正安静地躺在静电袋里等着我解决完前两个再“宠幸”它。这就是边缘部署的日常没有云端充裕的算力没有统一的内存模型每个设备都是一座需要重新征服的孤岛。今天我们就聊聊怎么把YOLO这座“大模型”塞进这些各怀绝技的小盒子里。一、Jetson系列TensorRT的“甜蜜”与“陷阱”拿到Jetson设备Nano、Xavier NX、Orin等第一反应往往是直接跑官方Demo。这没问题但当你换上自己的YOLOv5s模型时问题就来了。模型转换的坑ONNX导出是关键别直接用PyTorch的torch.onnx.export默认参数特别是动态轴。NPU喜欢静态形状。# 错误示范动态batchtorch.onnx.export(model,im,model.onnx,input_names[images],output_names[output],dynamic_axes{images:{0:batch},# 这里会坑死你output:{0:batch}})# 建议写法固定batch推理时batch1就行torch.onnx.export(model,im,model.onnx,input_names[images],output_names[output],opset_version12,# 别用太老的opsetdo_constant_foldingTrue)导出后用trtexec构建引擎时记得加上--fp16。Jetson的GPU对半精度优化极好速度几乎翻倍精度损失微乎其微。但要注意如果你的模型里有某些特殊操作如早期版本的Focus模块可能不支持FP16需要部分层保持FP32。内存管理是门艺术Jetson Nano的4GB内存是共享的GPU和CPU。如果你在Python里用OpenCV读图用NumPy做预处理这些数据都在CPU内存里但TensorRT推理需要GPU内存。来回拷贝能吃掉一半时间。// C部署时考虑使用零拷贝cudaHostAlloc(cpuBuffer,size,cudaHostAllocMapped);cudaHostGetDevicePointer(gpuBuffer,cpuBuffer,0);// 这样CPU预处理完的数据GPU直接能用省一次memcpy二、RKNN平台NPU的“方言”编译瑞芯微的NPU很强但它的“方言”RKNN需要你把模型翻译一遍。这个过程比TensorRT更“黑盒”。量化是必选项RKNN通常只支持INT8量化。好消息是量化后速度飞快功耗也低坏消息是精度可能跳水。官方提供的rknn-toolkit里有量化校准工具但那个默认的校准数据集通常用COCO的几百张图可能不适合你的场景。# 量化校准别偷懒用自己的数据rknn.config(mean_values[[0,0,0]],std_values[[255,255,255]])rknn.build(do_quantizationTrue,dataset./calib_data.txt)# 这里放你自己的校准图片列表预处理对齐要命RGB还是BGR归一化到[0,1]还是[0,255]减均值除标准差还是直接缩放这里错一步输出就是一堆天文数字。建议先在PC上用rknn-toolkit模拟运行对比输出和PyTorch原始输出完全一致再上板。内存溢出黑屏RKNN的内存管理比较刚性。如果模型太大或者同时跑多个模型NPU内存可能直接崩掉表现就是板子黑屏、串口无响应。解决方法只有优化模型、减少输入尺寸、或者拆解任务。记得用rknn.query_sdk_version()看看内存限制。三、STM32MP1没有NPU的“硬扛”到了这种Cortex-A核的Linux MPUNPU就别想了。但别忘了它还有M4核的MCU。一种思路是A核跑Linux做图像采集、预处理、显示M4核跑轻量级推理引擎如TFLite Micro或自写算子。模型必须瘦身YOLOv5s想都别想。考虑YOLO-Fastest、Nanodet或者自己重训一个极简版。输入尺寸压到160x160通道数砍半后处理简化。定点化是朋友ARM CMSIS-NN库对INT8/INT16支持很好。用训练后量化PTQ把权重和激活都定点化。注意M4核没有硬件除法器除法操作要用移位近似。// 避免浮点除法// 原式: box_x (grid_x sigmoid(tx)) * stride// 改为: box_x (grid_x sigmoid_int8(tx)) * stride; // stride是2的幂次用移位双核通信开销A核和M4核通过共享内存或RPMsg通信。数据传递的延迟可能比推理本身还高。尽量让数据流单向化一次传递多帧或者让M4核直接访问摄像头缓冲区如果硬件支持。四、一些共通的“生存经验”预处理别在CPU上磨蹭能放在GPU/NPU里做的预处理归一化、缩放就别让CPU插手。CPU忙不过来的。后处理才是隐藏的性能杀手模型推理可能只要10ms但NMS、画框、标签解析花了50ms。试试CUDA核函数做NMS或者用OpenMP并行化CPU后处理。功耗与散热的博弈边缘设备往往被动散热。持续高负载会触发温控降频速度直接打对折。必要时自己加个小风扇或者用jetson_clocks锁频谨慎使用。日志与调试的“土法炼钢”串口日志最可靠。在关键路径打时间戳用GPIO点个LED甚至用示波器量中断引脚——这些“土法”在查实时性问题时比GDB管用。版本固化的教训驱动、TensorRT、RKNN Toolkit、OpenCV……任何一个版本升级都可能带来惊喜吓。部署稳定的项目记得把整个工具链版本锁死镜像备份。最后说点虚的。边缘部署就像在螺丝壳里做道场。你得学会和硬件妥协和内存讨价还价和散热斗智斗勇。没有“标准答案”只有针对这块板子、这个场景、这个功耗预算的“最优解”。每次调通一个设备就像在陌生城市交了个朋友——你知道它的脾气它也愿意为你干活。下次深夜调试时如果风扇突然安静了进度条终于走到100%不妨站起来走走。窗外可能天快亮了而你的YOLO正在那个巴掌大的板子里安静地看着这个世界。本篇不涉及具体代码仓库或工具链下载链接所有操作建议以官方最新文档为准。版本迭代快一切以你手上的板子能跑通为最终标准。

更多文章