1. 为什么要在香橙派5上折腾YOLO11全链路
香橙派5这块板子,RK3588的芯片,6TOPS的NPU算力,价格却只有同级别开发板的一半不到。我第一次拿到手的时候,第一反应就是——这玩意儿不拿来跑视觉模型简直浪费。但真正开始折腾才发现,从YOLO11训练到RKNN部署,中间要跨过的坑比想象中多得多。
这篇文章要聊的,就是怎么在香橙派5上把YOLO11从零训练出来,再通过RKNN工具链部署到NPU上跑起来。整个过程涉及模型训练、ONNX导出、RKNN转换、板端推理四个大环节,每个环节都有各自的脾气。适合谁看?如果你手头正好有一块RK3588的板子,想跑自己的检测模型,或者你正在评估RK3588能不能满足项目的视觉需求,那这篇内容应该能帮你省下不少查文档和试错的时间。
我用的环境是Ubuntu 22.04的PC做训练和转换,香橙派5官方Ubuntu镜像做部署。YOLO11选的是Ultralytics的11n版本,轻量、速度快,适合边缘端。RKNN Toolkit2的版本是2.0.0b0,这个版本对YOLO11的支持已经比较完善了。下面按实际操作的顺序,把每个环节拆开讲。
2. 环境搭建与工具链选型
2.1 训练端环境配置的取舍
训练端我建议直接用带NVIDIA显卡的机器,别想着在香橙派5上训练。RK3588的CPU性能虽然不错,但训练YOLO11这种模型,没有CUDA加速的话,一个epoch可能要跑几个小时,完全不现实。我用的是一台RTX 3060的台式机,12GB显存,跑YOLO11n的640x640输入,batch size设16,显存占用大概在8GB左右,刚好够用。
Python环境用conda建一个独立环境,Python版本选3.10。为什么不用3.11或3.12?因为RKNN Toolkit2在3.10上的兼容性最好,官方文档也是基于3.10写的。PyTorch选2.1.0版本,对应CUDA 11.8的cu118版本。Ultralytics直接pip install ultralytics就行,但要注意版本,我用的8.3.0,这个版本对YOLO11的支持是完整的。
conda create -n yolo11 python=3.10 conda activate yolo11 pip install torch==2.1.0 torchvision==0.16.0 --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics==8.3.0 pip install onnx==1.15.0 onnxruntime==1.17.0ONNX的版本要特别注意,1.15.0是经过验证能和RKNN Toolkit2配合的版本。我试过1.16和1.17,转换的时候会报一些奇怪的算子不支持错误,换回1.15就正常了。onnxruntime装CPU版本就行,主要用来验证导出的ONNX模型推理结果是否正确。
2.2 RKNN Toolkit2的安装与验证
RKNN Toolkit2是瑞芯微提供的模型转换和推理工具包,跑在PC端做模型转换,板端用RKNN Runtime做推理。安装方式有两种:pip直接装或者从GitHub拉源码装。我建议用pip装,省事。
pip install rknn-toolkit2==2.0.0b0装完之后验证一下:
from rknn.api import RKNN rknn = RKNN() print(rknn.version())如果输出版本号就说明装好了。这里有个坑要注意:RKNN Toolkit2对numpy的版本有要求,必须是1.26.4以下,如果numpy版本太高,import的时候会报错。我一开始用numpy 2.0,直接崩了,降回1.26.4就好了。
板端这边,香橙派5的官方Ubuntu镜像里已经预装了RKNN Runtime的库,但版本可能比较老。我建议去瑞芯微的GitHub仓库下载最新的rknpu2运行时库,替换掉系统里的旧版本。具体操作是把librknnrt.so拷贝到/usr/lib/目录下,然后ldconfig刷新一下。
注意:板端RKNN Runtime的版本必须和PC端RKNN Toolkit2的版本匹配,否则转换出来的模型在板端加载会失败。我用的组合是PC端2.0.0b0 + 板端2.0.0b0,这个组合是验证过的。
2.3 香橙派5系统准备与NPU驱动检查
香橙派5的Ubuntu镜像刷好之后,第一件事是检查NPU驱动是否正常。运行:
cat /sys/kernel/debug/rknpu/version如果输出类似“RKNPU driver version: 0.9.6”这样的信息,说明NPU驱动已经加载了。如果没有这个文件,可能需要更新内核或者手动加载驱动模块。
另一个要检查的是RGA(Raster Graphic Acceleration)和MPP(Media Process Platform)这两个硬件加速模块。RGA负责图像缩放和格式转换,MPP负责视频编解码。YOLO11推理的时候,图像预处理用RGA来做,比CPU快很多。
ls /dev/rga /dev/mpp_service这两个设备节点存在就说明驱动正常。如果不存在,需要重新编译内核或者找官方提供的驱动包安装。
3. YOLO11模型训练与导出细节
3.1 数据集准备与训练参数设置
数据集用YOLO格式,目录结构是这样的:
dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yamldata.yaml里配置好路径和类别数。我这次用的自建数据集,5个类别,大概3000张图。训练参数方面,YOLO11n的默认配置其实已经调得不错了,但有几个参数我习惯改一下:
from ultralytics import YOLO model = YOLO('yolo11n.pt') model.train( data='data.yaml', epochs=100, imgsz=640, batch=16, device=0, workers=8, optimizer='AdamW', lr0=0.001, patience=20, save=True, pretrained=True )imgsz设640是因为RK3588的NPU对640x640的输入支持最好,再大推理时间会明显增加。batch size根据显存来,12GB显存跑16没问题。patience设20,意思是20个epoch验证集指标不提升就早停,避免过拟合。
训练完之后,用验证集跑一下mAP:
metrics = model.val() print(metrics.box.map) # mAP50-95 print(metrics.box.map50) # mAP50我这次训练的结果是mAP50大概0.89,mAP50-95大概0.67,对于5个类别的检测任务来说够用了。
3.2 导出ONNX模型的正确姿势
训练完的.pt模型不能直接转RKNN,必须先导出成ONNX。Ultralytics提供了export接口:
model = YOLO('runs/detect/train/weights/best.pt') model.export( format='onnx', imgsz=640, batch=1, simplify=True, opset=12, dynamic=False )这里有几个关键点。opset选12,不要选太高的版本,RKNN Toolkit2对opset 12的支持最稳定。simplify=True会用onnx-simplifier对模型做简化,去掉一些冗余算子,转换的时候成功率更高。dynamic=False表示固定输入尺寸,RKNN转换的时候不需要动态shape,固定尺寸性能更好。
导出之后,用onnxruntime验证一下ONNX模型的输出:
import onnxruntime as ort import numpy as np sess = ort.InferenceSession('best.onnx') input_name = sess.get_inputs()[0].name dummy = np.random.randn(1, 3, 640, 640).astype(np.float32) outputs = sess.run(None, {input_name: dummy}) print(outputs[0].shape)YOLO11的ONNX输出shape是(1, 84, 8400),84是4个坐标加80个类别分数(COCO数据集),8400是预测框数量。如果是自定义数据集,类别数不同,这个维度会变。
实操心得:导出ONNX的时候,如果遇到“Unsupported operator”的错误,大概率是opset版本太高。降到12或者11试试。另外,如果模型里有自定义算子,需要先在PyTorch里替换成标准算子再导出。
3.3 ONNX模型结构分析与RKNN适配性评估
在转RKNN之前,最好先看一下ONNX模型的结构,确认没有RKNN不支持的算子。用netron打开ONNX文件,或者用代码打印节点信息:
import onnx model = onnx.load('best.onnx') for node in model.graph.node: print(node.op_type)YOLO11主要用到的算子有Conv、Concat、Resize、Sigmoid、Mul、Add、Transpose、Softmax等,这些都是RKNN支持的。需要特别注意的是SiLU激活函数,YOLO11用的是SiLU,RKNN Toolkit2从1.6版本开始就支持SiLU了,所以没问题。
如果模型里有GridSample、NonMaxSuppression这类算子,RKNN可能不支持,需要把NMS后处理从模型里拿出来,放到CPU上做。YOLO11的导出默认是不带NMS的,所以这一步可以跳过。
4. RKNN模型转换与量化调优
4.1 RKNN转换脚本编写与参数解读
RKNN转换的核心是写一个转换脚本,配置好模型路径、目标平台、量化方式等参数。下面是我用的脚本:
from rknn.api import RKNN rknn = RKNN(verbose=True) # 配置模型预处理 rknn.config( mean_values=[[0, 0, 0]], std_values=[[255, 255, 255]], target_platform='rk3588', quantized_dtype='asymmetric_quantized-8', optimization_level=3 ) # 加载ONNX模型 ret = rknn.load_onnx(model='best.onnx') if ret != 0: print('Load ONNX failed') exit(ret) # 构建RKNN模型 ret = rknn.build(do_quantization=True, dataset='dataset.txt') if ret != 0: print('Build RKNN failed') exit(ret) # 导出RKNN模型 ret = rknn.export_rknn('best.rknn') if ret != 0: print('Export RKNN failed') exit(ret)mean_values和std_values是预处理参数。YOLO11训练的时候输入是0-1之间的浮点数,而RKNN推理时输入是0-255的uint8,所以需要做归一化。mean设0,std设255,就是把输入除以255,映射到0-1范围。
target_platform必须设成rk3588,不能设成rk3588s或者其他,否则转换出来的模型在板端加载会报错。quantized_dtype选asymmetric_quantized-8,这是8位非对称量化,精度和速度的平衡最好。optimization_level设3,表示最高级别的图优化。
4.2 量化数据集准备与精度调优
do_quantization=True的时候,需要提供一个量化数据集,也就是dataset.txt文件。这个文件里列出了一些图片的路径,RKNN会用这些图片来做量化校准。
./quant_images/001.jpg ./quant_images/002.jpg ./quant_images/003.jpg ...量化图片的数量建议在100-200张之间,太少会导致量化精度下降,太多会拖慢转换速度。图片要覆盖各种场景,最好从训练集里随机抽,不要只用一类图片。
我这次用了150张量化图片,转换出来的RKNN模型在板端跑,mAP50从0.89降到了0.86,降了3个点,可以接受。如果精度降得太多,可以试试混合量化,把某些层保持浮点:
rknn.config( ..., quantized_dtype='asymmetric_quantized-8', quantized_algorithm='normal', quantized_method='channel' )quantized_algorithm选normal,quantized_method选channel,这两个组合对YOLO系列模型的量化精度最好。我试过用kl_divergence算法,精度反而更差。
注意:量化数据集里的图片尺寸不需要和模型输入尺寸一致,RKNN会自动做resize。但图片的格式必须是RGB,如果是BGR需要提前转换。
4.3 转换结果验证与常见报错处理
转换完成后,RKNN Toolkit2提供了仿真推理功能,可以在PC上验证RKNN模型的输出:
rknn.load_rknn('best.rknn') rknn.init_runtime(target='rk3588') outputs = rknn.inference(inputs=[dummy_input]) print(outputs[0].shape)如果仿真推理的结果和ONNX推理的结果差距不大,说明转换成功。如果差距很大,可能是量化参数没设对,或者量化数据集有问题。
常见的报错和处理方法:
| 报错信息 | 原因 | 解决方法 |
|---|---|---|
| E init_runtime: Invalid RKNN model | 模型版本不匹配 | 检查PC端和板端RKNN版本是否一致 |
| E build: Unsupport op: GridSample | 算子不支持 | 替换算子或把该部分放到CPU |
| E load_onnx: Invalid ONNX model | ONNX版本问题 | 降级onnx到1.15.0 |
| W quantize: Quantize dataset is too small | 量化图片太少 | 增加到100张以上 |
5. 板端部署与NPU推理实战
5.1 板端环境配置与模型传输
板端这边,先把RKNN模型传到香橙派5上。用scp或者adb都行:
scp best.rknn orangepi@192.168.1.100:/home/orangepi/models/然后确认板端的RKNN Runtime库版本:
strings /usr/lib/librknnrt.so | grep -i version如果版本和PC端不一致,去瑞芯微GitHub下载对应的rknpu2运行时库,替换掉系统里的。
板端推理的Python脚本大概长这样:
from rknnlite.api import RKNNLite import cv2 import numpy as np rknn = RKNNLite() ret = rknn.load_rknn('best.rknn') ret = rknn.init_runtime(core_mask=RKNNLite.NPU_CORE_0_1_2) img = cv2.imread('test.jpg') img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img = cv2.resize(img, (640, 640)) img = np.expand_dims(img, axis=0) outputs = rknn.inference(inputs=[img])core_mask设成NPU_CORE_0_1_2表示用三个NPU核心一起跑,RK3588有三个NPU核心,可以并行推理。如果模型比较小,用一个核心就够了,设成NPU_CORE_0。
5.2 推理性能实测与优化技巧
我实测下来,YOLO11n在RK3588上的推理时间大概是25-30ms,也就是30-40 FPS。这个速度对于大多数边缘视觉应用来说够用了。如果开三个NPU核心,推理时间可以降到15ms左右,但功耗会上去。
优化技巧方面,有几个点可以关注:
第一,图像预处理用RGA来做。OpenCV的resize是CPU做的,640x640的图大概要5ms,用RGA可以降到1ms以内。RGA的Python接口在rknpu2的示例代码里有,可以直接拿来用。
第二,后处理用C++写。Python做NMS后处理大概要10ms,用C++可以降到2ms以内。如果对帧率要求高,建议把后处理用C++实现,通过pybind11暴露给Python调用。
第三,模型输入尺寸不要盲目加大。640x640已经能覆盖大多数场景,如果换成1280x1280,推理时间会翻倍,但精度提升有限。
5.3 完整推理Pipeline搭建
一个完整的推理Pipeline包括:图像采集、预处理、NPU推理、后处理、结果输出。我用的是USB摄像头,通过OpenCV的VideoCapture采集:
cap = cv2.VideoCapture(0) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) while True: ret, frame = cap.read() if not ret: break # 预处理 img = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) img = cv2.resize(img, (640, 640)) img = np.expand_dims(img, axis=0) # NPU推理 outputs = rknn.inference(inputs=[img]) # 后处理 boxes, scores, classes = post_process(outputs[0]) # 画框 for box, score, cls in zip(boxes, scores, classes): x1, y1, x2, y2 = box cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(frame, f'{cls}:{score:.2f}', (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2) cv2.imshow('result', frame) if cv2.waitKey(1) & 0xFF == ord('q'): break后处理函数需要根据YOLO11的输出格式来写。YOLO11的输出是(1, 84, 8400),84是4个坐标加80个类别分数。需要先转置成(8400, 84),然后过滤掉置信度低的框,再做NMS。
def post_process(output, conf_thres=0.25, iou_thres=0.45): output = output[0].transpose(1, 0) # (8400, 84) boxes = output[:, :4] scores = output[:, 4:] class_ids = np.argmax(scores, axis=1) confidences = np.max(scores, axis=1) mask = confidences > conf_thres boxes = boxes[mask] confidences = confidences[mask] class_ids = class_ids[mask] # NMS indices = cv2.dnn.NMSBoxes( boxes.tolist(), confidences.tolist(), conf_thres, iou_thres ) return boxes[indices], confidences[indices], class_ids[indices]实操心得:YOLO11的输出坐标是cxcywh格式,需要转换成x1y1x2y2才能画框。转换公式是x1=cx-w/2, y1=cy-h/2, x2=cx+w/2, y2=cy+h/2。别忘了乘以原图的缩放比例。
6. 踩坑记录与性能调优经验
6.1 常见问题速查与排查思路
整个流程走下来,我遇到的最典型的问题有这么几个:
第一个是RKNN模型加载失败,报“Invalid RKNN model”。这个问题90%的情况是PC端和板端的RKNN版本不一致。解决方法很简单,统一版本就行。我现在的做法是PC端装什么版本,板端就换什么版本的librknnrt.so。
第二个是推理结果全乱,框的位置完全不对。这个问题一般是预处理参数没设对。检查mean_values和std_values,确认和训练时的归一化方式一致。YOLO11训练时输入是0-1,所以mean=0, std=255。如果训练时用了其他归一化方式,这里要对应修改。
第三个是量化后精度掉太多,mAP降了10个点以上。这种情况一般是量化数据集的问题。量化图片要覆盖各种场景,数量要够,最好从训练集里随机抽。另外可以试试把quantized_algorithm改成normal,quantized_method改成channel。
第四个是推理速度慢,只有几FPS。检查core_mask有没有设对,RK3588有三个NPU核心,设成NPU_CORE_0_1_2可以并行推理。另外检查图像预处理是不是用CPU做的,换成RGA可以省不少时间。
6.2 NPU核心调度与多模型并行
RK3588的三个NPU核心可以独立调度,也可以组合使用。如果只有一个模型,设成NPU_CORE_0_1_2可以让三个核心一起跑一个模型,速度最快。如果有多个模型需要同时跑,可以每个模型分配一个核心:
# 模型A用核心0 rknn_a.init_runtime(core_mask=RKNNLite.NPU_CORE_0) # 模型B用核心1 rknn_b.init_runtime(core_mask=RKNNLite.NPU_CORE_1) # 模型C用核心2 rknn_c.init_runtime(core_mask=RKNNLite.NPU_CORE_2)这种调度方式适合多模型并行的场景,比如同时跑检测和分类。但要注意,三个核心共享内存带宽,如果模型都很大,并行跑可能会互相拖慢。
6.3 从YOLO11到其他模型的迁移经验
这套流程不只适用于YOLO11,YOLOv8、YOLOv5、甚至RT-DETR都可以用类似的方式部署。区别主要在导出ONNX的步骤和后处理逻辑。YOLOv8的导出方式和YOLO11基本一样,后处理也类似。RT-DETR的输出格式不同,后处理需要单独写。
如果要把DINOv3这类Transformer模型转到RKNN,需要注意几点:Transformer里的MultiHeadAttention算子RKNN支持,但需要确认opset版本。另外Transformer模型对量化更敏感,建议用混合量化,把注意力层保持浮点。
迁移的时候,核心思路是一样的:先导出ONNX,确认算子支持,然后写RKNN转换脚本,准备量化数据集,最后板端部署。区别只是模型结构不同,需要调整的地方主要在预处理和后处理。
最后分享一个小技巧:RKNN Toolkit2的仿真推理功能可以在PC上验证模型转换的正确性,不用每次都传到板子上试。仿真推理的结果和板端推理的结果基本一致,可以省很多时间。但要注意,仿真推理不支持RGA和MPP这些硬件加速模块,所以性能数据只能参考,不能作为最终依据。