500元RK3588开发板跑YOLOv5:从训练到RKNN部署全流程 五百块买一块能跑YOLOv5的开发板这事放在两年前根本不敢想。那时候RK3588平台的板子要么上千要么常年缺货现在香橙派5Pro把8G版本压到五百块上下我第一时间就买了回来。折腾了一个多月把YOLOv5从PC机上训练、导出ONNX、转RKNN、再到RK3588端侧NPU推理整条链路跑通中间换过系统、重装过工具链、踩了不少模型转换的坑。这篇就把完整过程拆开揉碎讲清楚从硬件选型到训练参数从RKNN转换到板端部署代码每一步都带参数、命令和操作截图式的描述保证你照着走也能把模型跑起来。适合手里有RK3588系列板子、想把YOLOv5部署上去但一直没跑通的朋友也适合刚刚接触边缘AI、想低成本入门的新手。1. 方案选型与硬件准备1.1 香橙派5Pro的硬件底子先说结论这块板子的性价比核心就在RK3588S这颗芯片上。香橙派5Pro用的是RK3588S可以理解为RK3588的简化版砍掉了一部分不常用的高速接口但最重要的计算单元全部保留。CPU是4颗Cortex-A76大核加4颗Cortex-A55小核大核最高主频能摸到2.4GHzGPU是Mali-G610 MP4最关键的NPU是3核设计总算力6TOPS。对目标检测部署来说判断一块板子能不能跑YOLO第一看NPU第二看内存带宽第三才轮得到CPU。RK3588S这颗NPU在YOLOv5s这种7.5M参数左右的模型上INT8量化后跑640分辨率输入实测能做到30到45帧完全达到实时检测的水平。我入手的这台是8GB LPDDR4x内存版本价格在五百出头。如果你是做多路视频分析或者要顺带跑大模型建议直接上16GB版本价格也就贵一百多但能做的事情完全不是一个量级。板载存储方面香橙派5Pro提供了一个M.2 NVMe接口强烈建议配一块固态硬盘系统响应速度和模型加载速度都会有肉眼可见的提升。另外它还有双HDMI输出、2.5G网口、多个USB 3.0接口MIPI-CSI和MIPI-DSI接口也都齐全做视觉项目的时候外接摄像头或屏幕都很方便。1.2 为什么在这个价位的板子里选它很多人会拿来跟树莓派5、Jetson Nano这类板子对比。树莓派5的CPU性能确实不错但完全没有NPU跑YOLOv5s在CPU上只能到三四帧只能算能跑Jetson Nano有128核Maxwell GPU理论算力有472 GFLOPS但显存只有4GB跑YOLOv5s明显吃力而且价格并不比香橙派5Pro便宜生态也相对封闭。对比项香橙派5Pro树莓派5Jetson Nano芯片RK3588SBCM2712Tegra X1NPU/GPU算力6TOPS NPU无NPU472 GFLOPS GPU内存8G/16G LPDDR4x8G/16G LPDDR4x4G LPDDR4YOLOv5s实测30-45 FPS3-5 FPS8-15 FPS五百元档位热销是新晋热门常年断货溢价老产品但价格稳当然RK3588S也有它的短板主要是软件生态没有树莓派那么傻瓜化很多工具要自己装、自己配置。但它的优势恰恰在于给了你一个完整的NPU算力平台经过一番折腾你能学到整套算法落地的流程这个收益是单纯的开箱即用给不了的。1.3 系统烧录与基础配置系统方面不建议自己折腾移植系统直接用官方编译好的Ubuntu 22.04镜像就行。网上那些从零移植Ubuntu到RK3588的教程更多是为了理解内核和根文件系统的构建流程实际部署算法时没有必要自己造轮子。烧录很简单去香橙派官网下载Ubuntu 22.04镜像用balenaEtcher或者dd命令写到TF卡里。如果是用NVMe固态可以先写到TF卡开机后在系统里用rsetup工具把系统复制到NVMe之后就能完全抛弃TF卡运行。sudo apt update sudo apt install rsetup -y sudo rsetup在rsetup界面里可以设置overlay、调整内存频率、配置风扇策略。我习惯先做两件基础事启用SSH服务方便后面远程调试把GPU对应的NPU相关服务确认开启。香橙派的Ubuntu镜像默认自带NPU驱动但不同版本镜像自带的librknnrt版本不一样这一点我后面专门讲因为版本不匹配是部署时最常遇到的坑。另外建议给系统换一个稳定的软件源用国内的镜像加速能省掉很多等下载的时间。配置完重启一次sudo apt full-upgrade跑一遍基础环境就算就绪了。2. YOLOv5训练从数据集到权重文件2.1 训练环境搭建先明确一点香橙派5Pro不是用来训练的。虽然有8核CPU和NPU但NPU并不擅长反向传播跑训练任务体验会很差。正确的做法是在自己的电脑或者云GPU上完成训练然后把训练好的模型拿到板子上做推理。我这里用conda管理环境Python版本用3.8这也是YOLOv5 v7.0官方推荐的版本之一。conda create -n yolov5 python3.8 conda activate yolov5 git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt如果你的电脑有NVIDIA独立显卡需要再装匹配CUDA版本的PyTorch用下面的命令装GPU版本pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118装完后输入python -c import torch; print(torch.cuda.is_available())如果输出True就说明GPU能用。没显卡也不慌YOLOv5用CPU也能训练只是速度慢很多建议把模型换成YOLOv5n或者把输入分辨率降到320整个训练时间能缩短到可以接受的范围。2.2 数据集准备与标注如果你用的是公开数据集比如COCO、VOC那直接下载转成YOLO格式就行。如果是自己的业务场景一般需要自己标注。标注工具我常用labelImg现在也比较流行X-AnyLabeling支持自动标注辅助能省不少时间。YOLOv5训练需要的数据格式是一张图片对应一个同名txt文件txt里每一行是cls x_center y_center width height坐标值都是归一化到0到1的。0 0.521875 0.357031 0.123438 0.182813 1 0.785156 0.517188 0.096094 0.223438目录结构最好严格按照下面这样组织YOLOv5读取时会自动找datasets/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yamldata.yaml内容需要写清楚路径和类别列表path: /home/user/datasets train: images/train val: images/val nc: 2 names: [person, car]这里有个经验训练集和验证集的数据分布尽量一致尤其是光照条件、拍摄角度、目标尺度如果训练集都是高清正脸照片验证集却全是模糊侧脸mAP会非常难看。2.3 训练参数与超参设置数据准备好了就可以开始训练基本命令python train.py --data data.yaml --weights yolov5s.pt --img 640 --batch 32 --epochs 100--weights这里强烈建议使用官方预训练权重yolov5s.pt作为起点而不是从零开始。预训练权重已经在大规模数据集上学到了丰富的底层特征你只需要微调顶层来适应自己的数据。在数据量不足五千张的情况下用预训练权重训练的mAP通常比从零训练高出很多而且收敛速度更快。关键的训练超参数我展开说一下batch-size显卡显存允许下尽量取大一点32或64都行。如果中途显存不够把batch调小以后学习率最好也跟着调小否则容易震荡不收敛。epoch先跑100轮看曲线。如果val曲线在最后十几轮还在上升就继续加。如果train的loss降得很低但val开始往上走就是过拟合的前兆。输入分辨率imgsz默认640效果最好但板端推理速度会下降。如果后面目标是高帧率部署可以从训练时就用416甚至320模型会适应低分辨率精度损失反而比你训练640然后推理缩到320要小。数据增强参数在data/hyps/hyp.scratch-low.yaml里比较重要的是hsv_h、hsv_s、hsv_v三个颜色增强参数它们能有效提高模型对光照变化的鲁棒性。如果你的场景光线统一可以适当调低如果场景多变我建议在原基础上加一点点。设好之后跑训练命令用--freeze 10可以冻结前10层加快训练速度并减少过拟合特别是数据量不大的场景可以试试。python train.py --data data.yaml --weights yolov5s.pt --img 640 --batch 32 --epochs 100 --freeze 10训练过程中可以用TensorBoard监控loss走向YOLOv5默认会在runs/train/exp下生成results.png包含box_loss、obj_loss、cls_loss、mAP等曲线一眼就能看出训练状态是否正常。2.4 训练结果评估与导出ONNX训练完成后在runs/train/exp/weights目录下会有两个文件best.pt和last.pt。best.pt是验证集上mAP最高的权重后面所有操作都用它。先用val.py验证一下最终指标python val.py --data data.yaml --weights runs/train/exp/weights/best.pt --img 640输出会显示各类别的precision、recall、mAP50、mAP50-95。只要mAP50在0.9以上说明模型已经学到比较有效的特征如果只有0.5以下先别急着部署回头检查数据集和训练参数更实际。接下来导出ONNX格式这是转RKNN的前置步骤python export.py --weights runs/train/exp/weights/best.pt --include onnx --opset 12 --imgsz 640这里opset选12是当前RKNN-Toolkit2兼容性最好的版本不要贸然用更高的opset后面转到RKNN时容易碰到算子不支持的错误。导出成功后会在同目录生成best.onnx。可以再用Netron打开看看网络结构确认输出节点是不是形如[1, 25200, 85]85 4个位置 1个置信度 80个类别如果你自定义类别数就相应变化。认清输出结构后面写后处理代码需要用到。3. 模型转换从ONNX到RKNN3.1 RKNN-Toolkit2环境搭建RKNN是瑞芯微NPU的专属模型格式PyTorch的.pt文件或者ONNX文件都不能直接上板跑必须先通过RKNN-Toolkit2转换成.rknn文件。这个转换动作一般在PC上完成因为RKNN-Toolkit2完整版支持x86架构在板子上只能装调用NPU的Lite版本。先拉rknn-toolkit2的代码仓库git clone https://github.com/airockchip/rknn-toolkit2 cd rknn-toolkit2 pip install packages/rknn_toolkit2-1.6.0-cp38-cp38-linux_x86_64.whl版本选择上我用的1.6.0对应的runtime是1.6.0整体比较稳定。装完之后验证一下python -c from rknn.api import RKNN; print(rknn toolkit ok)没报错就说明环境OK。需要提醒的是rknn-toolkit2对numpy版本比较敏感装完之后不要去升级numpy否则容易遇到ABI不兼容的报错。3.2 转换脚本与关键参数写一个标准的转换脚本核心流程是config、load、build、export四步from rknn.api import RKNN rknn RKNN() rknn.config( mean_values[[0, 0, 0]], std_values[[255, 255, 255]], target_platformrk3588 ) rknn.load_onnx(modelbest.onnx) rknn.build(do_quantizationTrue, datasetdataset.txt) rknn.export_rknn(yolov5s.rknn) rknn.release()代码很简单但参数背后的含义很容易踩坑。mean_values和std_values是NPU做输入归一化用的YOLOv5官方的预处理是直接把0到255的RGB像素除到0到1所以mean填[[0,0,0]]std填[[255,255,255]]。如果训练时做了其他归一化这里必须和训练时保持一致很多人在PC上测试精度很高转换到板子上精度崩溃九成就是这里填错了。dataset.txt是量化校准图片列表每行一个图片路径图片不需要太多几十到一百张就够但一定要从训练数据里随机挑不要全用同一类场景。量化校准的本质是让NPU统计真实输入的数值分布从而确定INT8量化参数如果校准图片和实际推理的图片分布差异太大量化后精度损耗会明显放大。执行转换后终端会打印每一层的量化信息最后看到Convert done.就表示成功了。生成的.rknn文件就是最终要拷到板子上的产物。3.3 INT8量化的取舍RK3588的NPU支持INT8、INT16和FP16三种精度。FP16转换最简单精度损失几乎为零但推理速度只有INT8的一半甚至更低。INT8速度快但模型的权重和激活值都要量化成8位整数精度会有一定程度的下降。量化对YOLOv5来说通常是比较友好的。我在自己的数据集上测过mAP50从0.965降到0.944只掉了两个多点完全在可接受范围内但推理速度整整快了一倍。如果业务对精度极其敏感建议先转一个FP16的版本做备用到时候根据实际效果决定用哪个。如果你发现掉点超过预期有几个优化思路增加dataset.txt里的校准图片数量并尽量贴近真实场景。把转换时的batch_size参数调小有时能减小量化误差。在模型导出ONNX时去掉检测头的部分把两三个输出分支拆开分别转换这种方式复杂些但某些场景下能减少量化干扰。最后实在不行就把对精度影响大的层保留为FP16其他层用INT8混精度推理RKNN在部分版本支持这种配置。这部分如果展开会是一篇文章的长度现阶段先掌握整体流程遇到问题再逐个排查。4. 香橙派5Pro端侧推理4.1 板端运行时环境把前面生成的yolov5s.rknn文件传上板子可以用scpscp yolov5s.rknn user板子的IP:/home/orangepi/板端推理不装完整的RKNN-Toolkit2只需要runtime版本的librknnrt.so。最简单的方式是从rknn-toolkit2仓库里的runtime目录拷贝或者直接pip安装rknn-toolkit-litepip install rknn_toolkit_lite2装完后把runtime/Linux/rknn_server对应版本的库加上或者直接确认系统里存在/usr/lib/librknnrt.so。如果用了官方Ubuntu镜像内核里NPU驱动一般已经内置无需重新编译。还要装一个OpenCV和一个numpy。OpenCV官方apt源里的版本就够用sudo apt install python3-opencv python3-numpy这里建议把Python运行时版本和转换时保持一致。你是用PC上的Python3.8转的模型板子系统的Python3.10也能正常加载但不同Python大版本下的numpy行为略有差异运行时如果出现类型转换问题优先排查是不是numpy版本过新导致的。4.2 推理与后处理代码直接给一份可运行的Python推理代码加载模型、前处理、推理、后处理都写进去了import cv2 import numpy as np from rknnlite.api import RKNNLite CLASSES [person, car] # 改成自己数据集的类别 CONF_THRES 0.25 NMS_THRES 0.45 INPUT_SIZE 640 def letterbox(img, new_shape(INPUT_SIZE, INPUT_SIZE), 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))) dw (new_shape[1] - new_unpad[0]) / 2 dh (new_shape[0] - new_unpad[1]) / 2 if r ! 1: img cv2.resize(img, new_unpad, interpolationcv2.INTER_LINEAR) 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, (dw, dh) def xywh2xyxy(x): y np.copy(x) y[..., 0] x[..., 0] - x[..., 2] / 2 y[..., 1] x[..., 1] - x[..., 3] / 2 y[..., 2] x[..., 0] x[..., 2] / 2 y[..., 3] x[..., 1] x[..., 3] / 2 return y def postprocess(outputs, ratio, pad): pred outputs[0][0] obj_conf pred[:, 4] pred pred[obj_conf CONF_THRES] if len(pred) 0: return [] cls_conf pred[:, 5:].max(axis1) cls_ids pred[:, 5:].argmax(axis1) box xywh2xyxy(pred[:, :4]) boxes box scores obj_conf[obj_conf CONF_THRES] * cls_conf keep cv2.dnn.NMSBoxes(boxes.tolist(), scores.tolist(), CONF_THRES, NMS_THRES) result [] for idx in keep: if isinstance(idx, (list, tuple)): idx idx[0] x1, y1 (boxes[idx][0] - pad[0]) / ratio, (boxes[idx][1] - pad[1]) / ratio x2, y2 (boxes[idx][2] - pad[0]) / ratio, (boxes[idx][3] - pad[1]) / ratio result.append((int(cls_ids[idx]), float(scores[idx]), int(x1), int(y1), int(x2), int(y2))) return result rknn RKNNLite() rknn.load_rknn(yolov5s.rknn) rknn.init_runtime(core_maskRKNNLite.NPU_CORE_0_1_2) img cv2.imread(test.jpg) input_img, ratio, pad letterbox(img) input_img cv2.cvtColor(input_img, cv2.COLOR_BGR2RGB) outputs rknn.inference(inputs[input_img], data_formatnhwc) dets postprocess(outputs, ratio, pad) for cls_id, score, x1, y1, x2, y2 in dets: cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, f{CLASSES[cls_id]} {score:.2f}, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imwrite(result.jpg, img)代码里我用了RKNNLite接口init_runtime(core_maskRKNNLite.NPU_CORE_0_1_2)表示三个NPU核心一起用。data_formatnhwc的意思是输入维度直接按高度、宽度、通道的顺序给不用手动转成NCHW能省一次内存拷贝。后处理的逻辑和官方YOLOv5基本一致先按置信度过滤再做NMS。注意letterbox的等比缩放和填充操作一定要和训练时的预处理保持一致否则边框位置会整体偏移而且模型输入分布的变化会让量化模型更容易误检。4.3 摄像头实时检测Demo图片检测跑通以后实时摄像头检测就是水到渠成的事。用OpenCV读USB摄像头是多数人最方便的方式cap cv2.VideoCapture(0) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) while True: ret, frame cap.read() if not ret: break input_img, ratio, pad letterbox(frame) input_img cv2.cvtColor(input_img, cv2.COLOR_BGR2RGB) outputs rknn.inference(inputs[input_img], data_formatnhwc) dets postprocess(outputs, ratio, pad) for cls_id, score, x1, y1, x2, y2 in dets: cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(frame, f{CLASSES[cls_id]} {score:.2f}, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imshow(yolov5-rk3588, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()如果使用MIPI-CSI摄像头香橙派5Pro的MIPI接口配合官方镜像里的摄像头驱动方案可以用v4l2或rkmpp相关接口读取。调试MIPI摄像头时经常会遇到显示画面颜色偏紫或者绿的问题优先检查摄像头的sensor型号和驱动overlay是否匹配。实时检测的瓶颈往往不在NPU推理本身而是在图像的采集、缩放格式转换和显示这几步。在Python里做RGB转BGR、resize都会占用CPU时间。想提高整体吞吐可以用多线程把采集、推理、显示三个环节并行起来或者直接降采集分辨率。举个例子640分辨率推理只需要几十毫秒但如果采集是4096x2160每次缩放都可能把帧率拖低很多。4.4 性能实测与优化方向我在自己这台香橙派5Pro 8G上用YOLOv5s INT8 640输入三个NPU核心全开推理延迟大概在28到35毫秒换算过来就是30到45帧。换成YOLOv5n延迟能压到15毫秒以下跑实时检测非常轻松。这个数据和我查到的社区测试基本一致不同固件版本会有浮动但整体量级就是这样。想让性能更上一层楼可以从几个方向入手如果对精度要求不高推理输入分辨率从640降到416或320帧率能翻倍。后处理用numpy向量化操作不要用纯Python循环逐条过滤。一次推理多张图时用rknn.inference(inputs[img1, img2, ...])做batch推理对1280分辨率的多路场景很有帮助。需要极低延迟的时候可以上C API用librknnrt写C推理代码省掉Python解释开销。C API用起来确实麻烦一些但做工业级应用时这是绕不开的路径。板卡供电也要注意。别用劣质USB电源我在调试过程中遇到过一次间歇性掉帧最后发现是供电不足导致NPU降频换了一个5V/3A的电源就稳定了。5. 常见问题与避坑指南5.1 模型转换报错怎么办RKNN转换过程中最常见的报错是算子不支持。YOLOv5的ONNX模型虽然结构不复杂但某些opset版本或特殊算子会让RKNN-Toolkit2解析失败。遇到这种情况先确认导出ONNX时opset是不是12再确认有没有用torch.onnx.simplify做化简。很多时候用python -m onnxsim best.onnx best_sim.onnx简化一遍算子类型会变得标准转换就能通过。另外一个典型问题是rknn.load_onnx后build阶段报维度错误。这种情况多半是动态shape导致的。YOLOv5导出的ONNX默认是固定shape如果导出时没有固定imgsz输入维度会是[1, 3, -1, -1]RKNN解析不了动态维度。导出命令里必须带上--imgsz 640这种明确尺寸。报错信息常见原因解决办法Unknown layer / not supportONNX封装后算子太杂用onnxsim化简或检查opset版本load_onnx fail模型输入含动态维度重新固定imgsz导出mean/std shape mismatch通道顺序理解错误确认是RGB还是BGR对应调整参数build fail at quant layer校准图读取失败检查dataset.txt路径换成绝对路径5.2 板端运行报错排查板端部署最常见的报错是找不到librknnrt.so。如果你看到ImportError: librockchip_mpp.so.1: cannot open shared object file或类似提示多半是系统的runtime库路径没配置好。可以手动把librknnrt.so所在目录加入LD_LIBRARY_PATHexport LD_LIBRARY_PATH/usr/lib:$LD_LIBRARY_PATH如果用rknn.load_rknn之后init_runtime报eRKNNGraphError优先怀疑板端runtime版本和PC端转换版本不一致。比如PC端是1.6.0生成的.rknn板端装的是老版本1.4.0就一定会出现这种问题。解决办法是升级板端runtime到与PC端一致的版本。还有一类问题是NPU驱动没有加载。在板子上执行sudo dmesg | grep rknpu如果没有任何输出说明NPU驱动没起来需要确认内核里rockchip-npu模块是否加载。官方Ubuntu镜像一般没问题但如果你自己编译过内核这个坑很容易踩到。5.3 精度下降排查模型在PC上精度很高转成RKNN后明显变差这是最多人问的问题。排查顺序我建议分三走第一先看前处理是否一致。如果PC测试用的是BGR输入RKNN转换时却按RGB配置了mean和std图像通道被交换精度必然崩。反过来也一样。第二看letterbox的填充值。YOLOv5官方用的是114不要把填充色改成0或者255量化模型对边界填充值非常敏感评测的时候可能差别不大但实际部署中经常造成远距离目标漏检。第三看量化校准图片。前面强调过校准图要和真实推理数据同分布。如果你用纯室内灯光的数据做校准拿到室外强光场景去推理INT8模型精度可能直接掉十几个点。解决方法是校准图集里混入多种场景或者干脆换成FP16模型对比测试一起判断是否量化引起的。5.4 这块板子还能怎么玩整个流程跑通以后香橙派5Pro其实还可以做很多事情。RK3588S的6TOPS NPU在500元这个价位基本没有对手它不只是能跑YOLOv5YOLOv8、YOLO系列各种变体都有对应的RKNN转换方案rknn_model_zoo官方仓库里已经提供了一批现成例程拿自己的数据重新训练后按同样的路径转换就行。另外很多人在折腾AI大模型的本地部署香橙派5Pro的16GB版本完全可以跑Qwen2.5、Llama 3.2这类7B模型的INT4量化版本。虽然速度没法跟带独立显卡的台式机比但作为私有本地服务不需要联网就能运行这对一些敏感场景是很有吸引力的。板子有NVMe接口跑模型的时候性能瓶颈主要在内存带宽但体验一把本地大模型是完全没问题的。这也是我前面说为什么不差那一百块就上16GB版本的原因。树莓派上能做的那些GPIO控制、家庭服务器、NAS方案香橙派5Pro基本也能做。换句话说这张板子等于一个带NPU的小电脑打通目标检测部署流程之后边缘AI的方向就彻底打开了。整套流程跑通之后给我最大的感受是RK3588的边缘AI部署链路已经非常成熟香橙派5Pro把价格压到500元档位确实是把入门的门槛拉了下来。最后再分享一个很实在的建议如果你之前没接触过RKNN拿到板子第一天别急着转自己的模型先花半小时把rknn_model_zoo里的yolov5例程原样跑通一遍让板子能出画面、能画出框。先有完整的成功体验再换成自己的数据集和模型出问题的时候排查范围会小很多。这方法我后来教过好几个朋友全都说省了不少熬夜时间。