ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

香橙派5 Pro实战:YOLOv8模型RKNN转换与部署全流程解析

香橙派5 Pro实战:YOLOv8模型RKNN转换与部署全流程解析 香橙派5 Pro这块板子五百元上下就能拿到手板载RK3588系列SoC下方压着一颗6 TOPS算力的NPU。最近我在帮朋友做YOLO目标检测上RKNN的方案逛了一圈社区发现一个普遍问题模型明明已经转成了.rknn可一上板不是算子报错就是推理速度远低于预期最后又退回CPU裸跑。所以这次我把整个流程重新走了一遍从ONNX导出、RKNN量化、板端推理到性能调优完整记录下来希望能给同样在RK3588上折腾YOLO的人节省点时间。这篇内容不只给命令每个关键选择的背后原因都会尽量讲透。1. 香橙派5 Pro在500元价位到底能干什么1.1 RK3588芯片的算力底细香橙派5 Pro用的是RK3588S属于RK3588系列的高性价比分支。很多人一听到RK3588第一反应是这也算旗舰芯片吧其实以今天嵌入式AI的卷度来看RK3588定位中高端但胜在NPU规格非常均衡不搞花活。RK3588S的内部组合是4核Cortex-A76加4核Cortex-A55最高主频2.4GHzGPU是Mali-G610 MP4最关键的是那颗3核心的NPU算力标称6 TOPS。这个6 TOPS是什么概念它足够在INT8精度下实时跑大多数轻量目标检测模型。YOLOv8s这种几个兆参数量的模型在它上面跑视频流完全没压力哪怕是分割、姿态估计只要不是特别重的backbone也能勉强hold住。项目参数SoCRK3588SRK3588系列CPU4×Cortex-A76 4×Cortex-A55最高2.4GHzGPUMali-G610 MP4NPU3核6 TOPSINT8内存4GB / 8GB / 16GB / 32GB 可选存储M.2 PCIe / SATA / NVMe / TF卡 / eMMC网络2.5G网口Wi-Fi 6选配视频双HDMI输出、MIPI-CSI摄像头接口这里需要明确一下RK3588与RK3588S的差异S版本在外设通道上做了裁剪比如PCIe/SATA数量、部分显示接口规格有所不同但NPU算力和CPU核心配置完全一致RKNN工具链也同源。也就是说这篇文章讲的转换和部署流程对市面上绝大多数RK3588/RK3588S板卡都通用只是板级外设差异需要你自己额外适配。1.2 为什么偏偏选YOLO以及哪些版本适合RKNNYOLO系列在部署端流行的核心原因是训练生态成熟、导出链路短、后处理可控。相比Transformer类检测器动辄几百兆的参数YOLO在精度与推理开销之间平衡得好社区资料也最多。更关键的YOLO的检测头结构相对规整导出ONNX以后算子类型集中RKNN编译器处理起来不容易翻车。在RK3588上我建议按以下优先级选择模型版本YOLOv5系列算子兼容性最稳LeakyReLU激活函数在RKNN上从来没有出过问题。但v5的检测头解码需要自己写对新手略麻烦。YOLOv8系列检测头在导出时自带sigmoid后处理直白文档多。SiLU激活函数在rknn-toolkit2 1.6.0以上版本支持良好是当前最推荐的入门组合。YOLO11系列最新一代ReLU激活函数占比高C2PSA模块在转换时偶尔会报不支持算子需要降级或手动替换源码老手再考虑。YOLOv10/YOLOv12等架构更新有的包含注意力模块或自定义卷积转换风险偏高不建议作为第一次上手的模型。选择YOLOv8作为本篇主流程不是因为它精度最高而是因为它在上板成功率、后处理简洁度、参考代码量三个维度同时满足新手能跑通和老手能优化的需求。如果后面遇到模型转换不过的情况我建议先退回YOLOv5s或者YOLOv8n验证工具链再逐步引入更重的模型。2. 开工前的软硬件准备少折腾一个月2.1 板子、电源、系统镜像的选型与烧录香橙派5 Pro虽然才卖五百元但配套绝对不能省钱。特别是电源很多人在这个坑里栽过跟头为了省几块钱用了普通手机充电头结果高负载推理时系统随机重启、USB摄像头掉线。这块板子官方推荐12V/3A的DC电源别用PD诱骗线兼容供电老老实实买配套电源最稳。系统方面我推荐直接去香橙派官网下载官方Ubuntu镜像22.04或者Debian镜像而不是随手找个第三方魔改包。原因很简单官方镜像的内核自带NPU驱动和VPU编解码模块RKNN运行时不用额外折腾内核第三方精简镜像常常会把/dev/rknpu节点弄丢到时候板端推理会直接报找不到设备。烧录工具用balenaEtcher或者dd都行。如果想把系统放在TF卡上选Class 10或者U3规格的卡16GB起步如果追求速度买官方eMMC模块插到板上效果会好很多但价格也上去一点。首次开机后建议连HDMI显示器操作一次确认NPU设备节点存在ls /dev/rknpu # 期望输出/dev/rknpu如果看不到这个节点检查内核日志确认执行的是官方内核。接下来网络配置板子插上USB摄像头连上有线网然后通过SSH远程操作效率最高。ssh orangepi板子IP # 默认用户/密码一般写在官方文档里不同镜像略有不同adb调试也有用处。某些情况下你只想快速把rknn文件和脚本推到板子不想开SSH可以启用板端的adb服务并用adb connect 板子IP:5555连接如果板子和电脑直接USB OTG相连用adb devices也能看到设备。根据我的经验SSH负责日常开发adb负责快速传文件和板端调试两者都配好比较省心。2.2 RKNN工具链的整体分工RKNN工具链最大的坑在于一套工具两种用途没有被明确区分rknn-toolkit2运行在x86的PC端负责把ONNX/PyTorch/TensorFlow模型转成.rknn文件同时可以在PC上做模拟推理验证转换结果。它也能通过adb连接开发板把板子当作远程NPU执行引擎。rknn-toolkit-lite2运行在开发板上的Python包依赖板端的librknnrt.so负责加载.rknn文件并调用NPU推理。性能比PC模拟器高很多是板端验证的主力工具。rknpu2板端C/C运行时仓库里面包含完整源码、librknnrt.so以及YOLO系列C示例适合最终产品或追求性能时使用。开发流程通常是在PC上用rknn-toolkit2完成模型转换和量化生成.rknn文件然后推到板子用rknn-toolkit-lite2先跑通Python脚本验证功能和精度最后再根据需要把后处理改成C版本。不要试图把整个rknn-toolkit2装到板子上占空间且没必要板子定位是推理设备不是编译设备。2.3 PC侧与板侧的安装清单PC侧环境我强烈建议用独立虚拟环境而不是直接装进系统Python。rknn-toolkit2对Python版本非常矫情我用过1.6.0版本它要求Python 3.8到3.10到了3.11直接装不上。推荐Ubuntu 20.04或22.04的x86_64主机配Python 3.10虚拟环境。python3 -m venv ~/rknn_venv source ~/rknn_venv/bin/activate pip install --upgrade pip # 进入下载好的rknn-toolkit2-1.6.0/packages目录 pip install -r requirements_cp310-1.6.0.txt -i https://pypi.tuna.tsinghua.edu.cn/simple pip install rknn_toolkit2-1.6.0-cp310-cp310-linux_x86_64.whl # 验证 python -c from rknn.api import RKNN; print(rknn ok)板子侧相对简单因为香橙派官方Ubuntu镜像自带Python 3.10直接装带lite的包pip install rknn-toolkit-lite21.6.0完成后做个验证python -c from rknnlite.api import RKNNLite; print(lite ok)版本必须对应。rknn-toolkit2 1.6.0转换生成的.rknn要考虑兼容性如果板端lite版本相差太大经常出现old RKNN model之类的提示。所以下载工具链时rknn-toolkit2、rknn-toolkit-lite2、rknpu2三个仓库的版本号要尽量保持同一主版本。宁可保守一点选1.5.2或者1.6.0的稳定版也别为了新功能追到预发布版本。3. 从PyTorch到ONNX导出前必须处理的三个问题3.1 导出命令与参数选择RKNN-Toolkit2并不直接吃PyTorch模型完整的链路是PyTorch - ONNX - RKNN。这个中间层的好处是ONNX作为开放中间格式你能在导出后立刻用Netron可视化检查网络结构定位问题算子不用反复回头改PyTorch代码。以YOLOv8s为例先安装Ultralytics库pip install ultralytics onnx onnxruntime然后导出ONNXyolo export modelyolov8s.pt formatonnx opset12 simplifyTrue这里有两个参数要重点解释。第一opset12RKNN编译器对ONNX opset的兼容性不是无限的opset太高时一些新算子会导致转换报错opset12是经过大量社区验证后最稳妥的档位。第二simplifyTrue它会用onnx-simplifier对计算图做常量折叠和冗余消除让导出后的算子数量更少RKNN编译阶段的压力小很多。3.2 不要把NMS带进模型这是YOLO上RKNN最容易踩的坑。Ultralytics导出的默认ONNX是不带后处理的但如果你在图省事勾选了End2End或NMS相关的导出参数ONNX计算图里就会出现NonMaxSuppression算子。RKNN的NPU本身没有硬件加速NMS编译器对NMS算子的支持也时好时坏不同版本工具链表现还不一样。运气好能转过去但性能极差运气不好直接报错。正确做法是模型只输出原始预测结果把NMS和置信度过滤全部放在CPU端后处理。这部分计算量很小对RK3588的CPU来说根本不构成压力。如果你用的不是Ultralytics官方库而是自己改的YOLOv8源码保存ONNX时请确认以下三个输出输出shape一般是[1, 84, 8400]其中84表示4个bbox坐标加80个类别分数8400表示所有尺度下的候选框数量。如果模型有多个输出检查是否有concat合并为了一个输出有多个输出也可以转换但后处理麻烦一点。检查分类分支是否已经经过了sigmoid。如果输出分数可能很大几百上千说明没有sigmoid后面后处理要补上。3.3 导出后的校验这一步很多人跳过但它能帮你省下大量排查时间。在PC端用onnxruntime跑一遍导出的模型记下输出和PyTorch原模型在同样输入下的结果做对比。数值不可能完全一致但类别置信度的Top1应该一致。import onnxruntime as ort import numpy as np from PIL import Image session ort.InferenceSession(yolov8s.onnx, providers[CPUExecutionProvider]) image Image.open(demo.jpg).resize((640, 640)) image_array np.array(image).astype(np.float32) / 255.0 image_array np.transpose(image_array, (2, 0, 1))[None, ...] output_names [o.name for o in session.get_outputs()] onnx_output session.run(output_names, {images: image_array}) print([out.shape for out in onnx_output])把这份结果保存成npy文件后面RKNN转换完做模拟推理时可以直接对比两者余弦相似度。这一步的另一个隐藏收益是你能确认输入节点名、输出节点名转换脚本里不需要猜测。4. RKNN转换全流程参数、量化与调试4.1 第一个转换脚本框架先给一个最基本的转换脚本我尽量把注释写到人能看懂的程度from rknn.api import RKNN rknn RKNN(verboseTrue) # 1. 配置转换目标 rknn.config( mean_values[[0, 0, 0]], std_values[[255, 255, 255]], target_platformrk3588, optimization_level3 ) # 2. 加载ONNX ret rknn.load_onnx(modelyolov8s.onnx) assert ret 0, load_onnx failed # 3. 构建RKNN模型开启INT8量化 ret rknn.build( do_quantizationTrue, datasetdataset.txt ) assert ret 0, build failed # 4. 导出.rknn文件 ret rknn.export_rknn(yolov8s.rknn) assert ret 0, export failed # 5. 释放资源 rknn.release()很多教程到这里就结束了这是不对的。转换成功不等于部署成功。我强烈建议在生成.rknn之后、上传板子之前先在PC端用模拟器做一次推理确认量化后的模型逻辑没有坏rknn RKNN() rknn.load_rknn(yolov8s.rknn) # targetNone 表示用PC模拟器而不是连板子 rknn.init_runtime(targetNone) import cv2 img cv2.imread(demo.jpg) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img cv2.resize(img, (640, 640)) outputs rknn.inference(inputs[img]) print(outputs[0].shape, outputs[0].dtype, outputs[0].min(), outputs[0].max())把outputs[0]和之前onnxruntime保存的npy做对比计算余弦相似度或通道级别的均值差异。如果量化后的输出和FP32差距在可接受范围再推板子。如果模拟器上就出现了大量NaN或全零输出别急着上板问题一定在转换或量化环节。有一点要注意init_runtime(targetNone)的模拟器性能很差只是用来验证数值不要用它评估帧率。4.2 量化数据集的影响do_quantizationTrue会触发INT8量化。很多人觉得量化就是一个开关打开就完了其实量化数据集的选取直接决定最终模型精度。量化集合的原则是每行一张图片路径覆盖你实际业务中最常见的场景包含目标特征。比如你要在园区里检测行人就放园区白天、夜晚、逆光、远距离、近距离的行人图片你要检测工业零件缺陷就放产线上各种光照和角度的零件图。我一般习惯放30到50张不是越多越好但绝对不能只放三五张。数据集里如果没有包含目标的小图、暗光图、大目标图量化后模型就可能在那些场景下漏检。dataset.txt格式如下./quant_data/road_001.jpg ./quant_data/road_002.jpg ./quant_data/road_003.jpg还有两个细节量化图片预处理要和推理一致。如果config里设置mean 0、std 255那么量化阶段工具读取图片时会自动做归一化如果mean/std设置不正确输出会偏差巨大。不要用纯黑或纯白图片。这类图片在量化时会导致激活值统计失真某些通道的scale参数被拉到极端值最后结果灾难性。如果量产后精度下降严重有两个降级方案一是do_quantizationFalse转FP16模型体积大一些但数值接近FP32适合先确认逻辑正确性二是保留INT8但换一批量化图片再重新转换。4.3 转换报错的典型解法RKNN转换报错信息有时很抽象但归纳下来就几类。我遇到过最多的几个E load_onnx: Unsupported OpONNX中的某个算子RKNN编译器不支持。先看错误里指名道姓的算子名再去PyTorch源码里找到对应层替换掉比如SiLU在旧版本工具链可能有问题可以换成ReLU或者升级rknn-toolkit2到1.6.0以上。E build: please check your rknn config大概率是target_platform写错或者输出节点名称与实际ONNX不符。E build: failed to create rknn model, code xxx这串code每个数字都有具体含义最常见的是输入shape不匹配检查ONNX输入是否固定为640x640且batch1。E quantize: can not find any valid image in datasetdataset.txt里的图片路径不对或图片本身损坏打不开。一个比较有效的定位思路是先转一个官方示例YOLOv8s的onnx确认工具链没问题再转你自己的模型。如果官方模型能转、你的模型不能转那差距就在模型改造部分查自定义算子如果官方模型都不能转那就是工具链或环境问题优先查版本对应关系。5. 板端部署推理脚本与后处理5.1 板端AI推理库与加载把生成的.rknn文件推到板子上scp yolov8s.rknn orangepi板子IP:/home/orangepi/然后写一个最简推理脚本import cv2 import numpy as np from rknnlite.api import RKNNLite rknn RKNNLite() ret rknn.load_rknn(yolov8s.rknn) assert ret 0, load rknn failed # 使用全部3个NPU核心 ret rknn.init_runtime(core_maskRKNNLite.NPU_CORE_0_1_2) assert ret 0, init runtime failed cap cv2.VideoCapture(0) # USB摄像头 while True: ret, frame cap.read() if not ret: break # 前处理保持宽高比的同时放缩到640x640并做letterbox img, ratio, pad letterbox(frame, (640, 640)) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) outputs rknn.inference(inputs[img]) # 后处理 dets postprocess(outputs[0], ratio, pad, conf_thres0.25, iou_thres0.45) # 画框... cv2.imshow(yolov8s, frame) if cv2.waitKey(1) 27: break cap.release() rknn.release()这里有两个容易忽略的点。第一个是core_mask参数默认情况下init_runtime可能只用一个NPU核心你跑出来的性能会非常弱。使用NPU_CORE_0_1_2显式开启3核推理后帧率通常能翻倍甚至更多。第二个是letterbox处理。简单粗暴的cv2.resize(img, (640, 640))会让图片变形检测小目标时精度下降肉眼可见。letterbox的思路是保持原始宽高比将图片放在640x640的画布中央其余部分用灰色填充。5.2 YOLOv8输出解码与NMSYOLOv8的ONNX输出是[1, 84, 8400]。其中8400是三个尺度特征图上的候选框总数84表示4个框坐标加80个类别分数。解码逻辑如下def letterbox(img, new_shape(640, 640), color(114, 114, 114)): shape img.shape[:2] r min(new_shape[0] / shape[0], new_shape[1] / shape[1]) new_unpad (int(round(shape[1] * r)), int(round(shape[0] * r))) img cv2.resize(img, new_unpad, interpolationcv2.INTER_LINEAR) dw new_shape[1] - new_unpad[0] dh new_shape[0] - new_unpad[1] dw / 2 dh / 2 top, bottom int(round(dh - 0.1)), int(round(dh 0.1)) left, right int(round(dw - 0.1)), int(round(dw 0.1)) img cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, valuecolor) return img, r, (left, top) def postprocess(output, ratio, pad, conf_thres0.25, iou_thres0.45): pred output.squeeze().T # [8400, 84] boxes pred[:, :4] # xywh class_scores pred[:, 4:] class_ids np.argmax(class_scores, axis1) scores class_scores[np.arange(len(class_ids)), class_ids] mask scores conf_thres boxes, scores, class_ids boxes[mask], scores[mask], class_ids[mask] if len(boxes) 0: return np.empty((0, 4)), np.empty((0,)), np.empty((0,)) # xywh - xyxy x_center, y_center, w, h boxes[:, 0], boxes[:, 1], boxes[:, 2], boxes[:, 3] x1 x_center - w / 2 y1 y_center - h / 2 x2 x_center w / 2 y2 y_center h / 2 boxes_xyxy np.stack([x1, y1, x2, y2], axis1) # 去除letterbox的padding并缩放回原图尺寸 boxes_xyxy[:, [0, 2]] (boxes_xyxy[:, [0, 2]] - pad[0]) / ratio boxes_xyxy[:, [1, 3]] (boxes_xyxy[:, [1, 3]] - pad[1]) / ratio indices cv2.dnn.NMSBoxes( boxes_xyxy.tolist(), scores.tolist(), conf_thres, iou_thres ) if len(indices) 0: return np.empty((0, 4)), np.empty((0,)), np.empty((0,)) indices indices.flatten() return boxes_xyxy[indices], scores[indices], class_ids[indices]两个调参点conf_thres是置信度门限调高可以减少误检但可能漏掉小目标iou_thres是NMS的门限调高会让重叠物体更难被分离开。如果你在实际使用中发现同一辆车出了两三个框就把iou_thres调低到0.4如果发现重叠的两个人只能框住一个说明阈值太低了往0.6方向调。具体数值要在你的数据集上用验证集测不要照抄别人的。5.3 用PC模拟器先跑通数值在写板端代码前先用PC模拟器验证一遍数值是对的能帮你排除掉板端环境问题。具体方法是同一张图onnxruntime输出保存一份RKNN模拟器输出保存一份算一下两者经过同样后处理后的检测框是否一致。如果框位置和类别基本一致说明转换链路没问题板端只要环境正常结果也不会跑偏。如果在这个阶段发现RKNN的类别置信度和ONNX差距很大优先怀疑量化数据集。可以临时用do_quantizationFalse转一版FP16模型如果FP16正常、INT8不正常那就是量化图片选取的问题不是模型本身的问题。6. 性能实测与优化方向6.1 不同模型尺寸的帧率对比我手上的这台香橙派5 Pro是8GB内存版本系统为官方Ubuntu 22.04接的是树莓派官方的USB摄像头不同模型在640x640输入下的实测参考值如下模型精度推理耗时3核NPU帧率参考YOLOv8nINT88~12ms80~120 FPSYOLOv8sINT815~25ms40~65 FPSYOLOv8mINT830~50ms20~30 FPSYOLOv8sFP1635~60ms15~28 FPS这些数字会因为你用的rknpu2版本、散热条件、是否开启在线NPU调频而浮动但它能给你一个概念YOLOv8s是你既能保持精度又能稳定跑40帧的选择YOLOv8n则是真正为实时性准备的。很多人问FP16模型为什么比INT8慢那么多NPU的算力指标6 TOPS本身就是指INT8峰值FP16会折半甚至更多所以部署推理时默认INT8不要舍不得那一点精度损失。6.2 三核NPU与多线程预处理的提升先把init_runtime里设置三核的影响量化一下。我试过同一份YOLOv8s模型默认NPU_CORE_AUTO的话推理耗时大概35~40ms改成NPU_CORE_0_1_2以后直接降到15~25ms。这个提升不需要改模型也不需要重写C是最划算的优化。另一个关键优化在视频流场景把摄像头读取resizeletterbox放到独立线程。NPU推理是异步的如果代码是串行的——读完一帧预处理推理后处理显示再读下一帧——那CPU上的预处理和后处理时间会完全拖垮视频流帧率。我的做法是开一个采集线程一个推理线程一个显示线程用队列把帧传递起来。实测同样的模型在只改线程结构的情况下从20FPS提升到接近35FPS。这也是为什么很多博主说帧率不够就上多线程确实是性价比最高的无脑优化。另外rknn.inference支持传入data_formatnhwc这是NPU原生友好格式可以小幅减少一次格式转换的开销。不熟悉的话直接省略也行。6.3 C与zero-copy还能再压一截Python板端验证通过后如果要做产品就应该尽快转C。rknpu2仓库里自带的examples/rknn_yolov5_demo和rknn_yolov8_demo就是很好的起点里面实现了完整的letterbox、推理、后处理、画框逻辑你只需要替换标号数量和类别名称。C版最大的性能增益点有两个避免Python GIL带来的多线程瓶颈。使用zero-copy内存注册能力。NPU推理时不需要把图像数据从CPU内存拷到NPU内存而是通过rknn_create_mem申请NPU可访问的内存再把图像数据直接写入那块内存推理输出也复用同一块内存。视频流场景下这个优化通常能再带来20%~30%的吞吐提升。实现zero-copy需要看rknpu2的include/rknn_api.h和源码里的rknn_create_mem使用示例。不要一开始就上这个先用Python把模型和参数验证完再用C做性能落地。7. 我踩过的坑和最终建议7.1 典型问题汇总把这轮实操里踩过最顺手的坑列成一张表方便你排查现象原因解决办法板端推理报错fail to load rknn板端lite版本与PC端工具链版本不匹配统一rknn-toolkit2与rknn-toolkit-lite2版本号检测框大量偏移前处理没有做letterbox直接拉伸变形改为letterbox保存宽高比物体检测出来了但类别错误率高BGR/RGB通道顺序没转输入推理前cv2.cvtColor(img, cv2.COLOR_BGR2RGB)推理速度半小时后明显下降NPU过热降频加装带风扇散热器检查系统温度同一个目标输出双重框NMS阈值过高调低iou_thres到0.4左右量化后某个不常见类别全部消失量化图片中没有该类目标在量化数据集加入该类别的代表性图片转换报错Unsupported Op模型版本过新或交叉注意力算子改用YOLOv8n/官方模型或替换问题激活函数摄像头采集帧率只有几帧视频流串行处理拆成采集线程推理线程显示线程7.2 根据实际项目给出的选择建议如果是个人学习、课程设计、算法验证香橙派5 Pro加YOLOv8s的组合完全够用。五百元的板子在主流大模型推理、视频分析、边缘计算场景里已经能提供不错的ROI比动辄上千的Jetson系列便宜太多学习门槛也更低。如果是做产品原型建议从YOLOv8n入手先把链路跑通再根据实测精度和帧率权衡是否升到YOLOv8s同时从第一天就把输入分辨率固定好不要中途改输入尺寸否则整个量化模型需要重新生成会耽误大量时间。如果是工业生产环境系统的长期稳定性比单帧速度更重要。建议用官方Ubuntu镜像不要随意升级内核跑推理的服务用systemd托管异常退出自动拉起NPU温度通过/sys/class/thermal/thermal_zone0/temp持续监控过热时主动降频或报警。最后分享一个我觉得很实用的调试习惯每次改动模型或转换参数后先跑一张固定图片把输出结果和旧版并排对比确认没有回归。这个习惯帮我省了大量在板子上反复烧录、反复推文件的时间。整个RKNN链路本质上就是工具链版本一致、量化图片靠谱、后处理细心三件事把它磨透RK3588这块板子会非常听话。
返回列表