ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

RK3588+麒麟V10跑YOLOv5:RKNN转换与NPU推理全流程实战

2026/10/6 10:56:46 拓冰建站 浏览量
RK3588+麒麟V10跑YOLOv5:RKNN转换与NPU推理全流程实战 最近在 RK3588 板子上跑 YOLOv5配的系统是麒麟V10 ARM 桌面版。很多人一听“麒麟V10 RK3588 NPU”这个组合就头大既不是常见的 Ubuntu也不是安卓驱动怎么装、模型格式怎么转、NPU 怎么调全靠自己一步步趟。这篇文章就把我踩过的坑和验证过的完整流程写出来从系统准备、RKNN 工具链安装、YOLOv5 模型转换到最终在板端调用 NPU 推理、后处理出检测框全都给到可直接跑的代码。适合刚拿到开发板、或者被客户系统绑定到麒麟V10又必须上模型推理的工程师参考照着多数环境能够一次跑通。1. 整体设计思路与方案选型1.1 为什么选择 RK3588 的 NPU 跑 YOLOv5RK3588 的 NPU 官方标称是 6 TOPS 算力支持 int4、int8、int16 量化兼容大量主流模型的转换。拿 YOLOv5s 来说如果纯靠 CPU 推理RK3588 的四个 A76 大核跑一张 640x640 的图大概需要一两百毫秒这个速度做实时视频流检测显然紧张。而把模型丢给 NPU整图推理可以压到 30ms 到 50ms 左右帧率提升非常明显功耗还低很多很适合边缘盒子、巡检机器人、工业质检这类场景。选择 RK3588 还有一个考虑它自带丰富的异构算力除了 CPU 和 NPU还有 2D 硬件加速模块 RGA、视频编解码模块 MPP。实际部署的时候解码用 MPP图像预处理缩放可以用 RGA推理交给 NPU整个流水线不占 CPU资源利用率很高。后面我会专门讲图像预处理怎么利用 RGA 加速这里先不展开。1.2 麒麟V10能用Ubuntu的思路吗麒麟V10 ARM 版底层和 Ubuntu 20.04 非常接近很多用户态软件可以直接从 Ubuntu 20.04 源安装环境适配成本比想象中低。但要注意内核、桌面组件和部分系统服务是定制过的不能完全照搬 Ubuntu 文档操作尤其是更新内核和安装驱动时得先确认是否影响 NPU 的运行时环境。RK3588 的 NPU 驱动在官方固件里通常已经编译进内核或作为独立模块存在麒麟V10 刷机后一般能直接识别到 NPU 设备。保险起见先检查一下设备节点是否正常这个在系统准备一节会专门说。如果设备节点没出来大概率是内核没有包含或者被安全模块挡住了这时候优先排查内核版本和固件匹配性不要一上来就重刷系统。1.3 RKNN工具链的完整流转路径RK3588 的 NPU 推理不能用常见的 PyTorch 或 ONNX Runtime 直接跑需要经过 Rockchip 官方的 RKNN 工具链转换。整个流程可以概括为训练或获取 PyTorch 格式的 YOLOv5 权重.pt把 .pt 导出为 ONNX在 PC 端或开发板上用 rknn-toolkit2 将 ONNX 转成 RKNN 格式将 .rknn 模型拷贝到 RK3588 板子用 rknn-toolkit-lite2 加载并调用 NPU 推理关键点在于模型转换这一步。RKNN-Toolkit2 支持在 x86 的 PC 上运行也可以在 ARM 板子上运行。我的建议是模型转换在 PC 上做因为转换工具依赖很多板子上跑会吃不少内存而最终的运行时只依赖一个 librknnrt.so 和轻量的 Python API板端部署非常干净。2. 环境准备与工具链安装2.1 制作麒麟V10系统启动盘与系统安装麒麟V10 ARM 版的镜像下载后制作启动盘推荐用 RufusWindows或者 balenaEtcher跨平台不要在 Windows 下用 UltraISO 直接写入镜像容易导致引导分区损坏。Rufus 写入时选择 DD 镜像模式而不是 ISO 模式这样在 ARM 设备上启动成功率最高。系统安装过程没什么特殊需要注意分区时给根目录留足空间。很多 RK3588 板子默认烧录的固件根分区偏小跑几个模型和依赖库就满了。我在安装时习惯手动分区/ 给 30GB 以上/home 单独挂避免后期扩容麻烦。安装完系统第一件事就是确认 NPU 设备节点。ls /dev/rknpu* dmesg | grep -i rknpu如果能看到 /dev/rknpu0 或者类似的设备节点说明驱动已经正常加载。如果什么都没有先检查内核版本麒麟V10 桌面版内核版本不同对 RK3588 外设的支持差异很大最稳妥的方式是刷入板厂提供的对应麒麟版本内核固件。2.2 解决软件商店空白与基础环境麒麟V10 上有一个很常见的怪毛病软件商店打开后一片空白装不了软件更新也失败。这个坑我遇到过好几次通常不是网络断了而是软件商店的后台服务或缓存挂了。简单的处理办法是killall kylin-software-center 2/dev/null rm -rf ~/.cache/kylin-software-center sudo apt update sudo apt install --reinstall kylin-software-center如果还是空白检查软件源配置。麒麟V10 有时候会把软件源指到内网地址在外网环境下自然是空的把源改回官方的在线源再apt update一次就能恢复。注意修改源的时候备份原始文件后续系统升级还需要用。系统基础环境方面麒麟V10 自带的 Python 3.8 满足大部分需求但建议不要直接用系统 Python 装深度学习依赖很容易把系统环境弄乱。我习惯装一个 Anaconda 或 Miniconda 的 ARM 版本单独给 RKNN 创建虚拟环境。2.3 安装RKNN-Toolkit2与Python依赖RKNN-Toolkit2 的安装包需要从 Rockchip 官方仓库下载有 x86 和 ARM 两个平台的版本。我在 x86 的 PC 上做模型转换所以装的是 x86 版。conda create -n rknn python3.8 -y conda activate rknn pip install rknn-toolkit2-x.x.x-cp38-cp38-linux_x86_64.whl这里特别强调一下安装 RKNN-Toolkit2 时不要贪新把 Python 装成 3.11很多版本的 RKNN-Toolkit2 对高版本 Python 的兼容性不好转换过程中容易报出莫名其妙的问题。我测试下来 Python 3.8 搭配最新的 rknn-toolkit2 是最稳的。安装完成后验证一下导入是否成功python -c from rknn.api import RKNN; print(rknn ok)如果导入时报GLIBCXX相关的错误一般是 conda 环境里的 GCC 库版本和系统不匹配可以用conda install libstdcxx-ng升级解决。另外新版 numpy 会把np.int移除导致旧版 RKNN-Toolkit2 转模型时报AttributeError: module numpy has no attribute int遇到这种情况把 numpy 降到 1.23 左右。3. YOLOv5模型转换全流程3.1 导出ONNX模型的关键设置YOLOv5 官方仓库的 export.py 就可以导出 ONNX关键是几个参数要设置对python export.py --weights yolov5s.pt --include onnx --opset 12 --simplifyopset 版本不要太高RKNN 工具链对 opset 12 支持最成熟opset 太新可能遇到算子不兼容。--simplify会调用 onnx-simplifier 做一些结构简化和常量折叠这一步不是必须的但能让后续转换少报不少算子错误。导出成功后最好先用 onnxruntime 跑一遍 ONNX确认模型输出和输入尺寸。YOLOv5 新版本的输出 shape 是 (1, 25200, 85)分别代表 batch、候选框数量、以及每个框的 4 个坐标 1 个置信度 80 个类别分数。知道了输出 shape后面写后处理才能心里有底。如果自己训练过 YOLOv5导出的模型默认只输出 (1, 25200, 85) 这一个输出。千万不要额外加什么 NMS 层或者解码层进去RKNN 转换和板端后处理都期望拿原始输出NMS 交给 CPU 端做反而更灵活。3.2 RKNN转换脚本与量化配置转换脚本的核心逻辑就几步完整代码如下from rknn.api import RKNN rknn RKNN(verboseTrue) rknn.config( mean_values[[0, 0, 0]], std_values[[255, 255, 255]], target_platformrk3588 ) if rknn.load_onnx(modelyolov5s.onnx) ! 0: print(load onnx failed) exit(-1) if rknn.build(do_quantizationTrue, datasetdataset.txt) ! 0: print(build failed) exit(-1) rknn.export_rknn(yolov5s.rknn) rknn.release()很多第一次做 RKNN 转换的人会搞不清 mean_values 和 std_values 怎么填。这个参数对应的是 NPU 硬件预处理单元对输入图像做的归一化操作。YOLOv5 训练的时候在模型内部已经包含了归一化层输入是 0 到 255 的 RGB 原始像素值所以这里的 mean 填 0、std 填 255意思就是让硬件把输入像素除以 255范围缩放到 0 到 1。如果填错了模型检测精度会断崖式下降尤其是小目标直接丢光。dataset.txt 文件里每一行写一张用于量化校准的图片路径图片最好是训练集里有代表性的样本数量建议 20 到 50 张。量化校准集太少会导致每一层激活值的统计范围不准进而影响量化精度太多又拖慢转换时间实测 50 张足够。./images/0001.jpg ./images/0002.jpg ./images/0003.jpg3.3 量化的坑精度下降怎么办开启do_quantizationTrue之后模型从 FP32 变成 INT8推理速度大幅提升但精度几乎必然有轻微损失。YOLOv5 这类检测模型对量化还算友好大多数情况下 mAP 下降不超过 1%肉眼几乎看不出差异。但如果你的模型精度本身就不高或者数据分布和量化校准集差异较大就需要注意以下几点。第一检查校准集是否覆盖了实际场景。比如检测工业零件时校准图里大部分是空背景模型就会把背景部分的激活范围统计得很好但对零件特征的量化误差很大。解决方法是把校准集替换成包含目标的图片越多目标越好。第二某些层对量化特别敏感。此时可以关闭整图量化采用混合量化方案只对敏感层做 FP16 或保留 FP32。RKNN 工具链的 config 里可以设置quantized_dtypew8a8等方式详细参数要查对应版本的 API这里不展开但记住一点先跑通整图 int8再根据精度问题针对性地调整不要一开始就上混合量化排查成本很高。第三转换前去掉模型里的 NMS 层。YOLOv5 在导出时如果带了端到端 NMS 的选项转换到 RKNN 会涉及大量自定义算子板端推理反而容易出错宁可把原始输出拿出来在 CPU 上做后处理。4. 板端NPU推理与后处理实战4.1 板端runtime环境搭建板端部署不需要安装完整的 RKNN-Toolkit2只需要两个东西librknnrt.so 和 rknn-toolkit-lite2。librknnrt.so 是 NPU 的运行时库在板子系统里的路径通常是/usr/lib/librknnrt.so如果系统里没有可以从板厂提供的库目录中找。检查方法find / -name librknnrt.so 2/dev/null如果连这个库都没有说明 NPU 驱动或 runtime 缺了一个环需要从板厂的开发包中拷贝安装。这个库版本必须和 PC 端转换用的 RKNN-Toolkit2 版本大版本一致否则加载模型时会报invalid rknn model version。Python 侧的依赖安装很简单pip install rknn-toolkit-lite2-x.x.x-cp38-cp38-linux_aarch64.whl注意板端是 ARM64 架构要下载 aarch64 的 whl 包。4.2 RKNNLite推理主流程RKNNLite 是板端轻量级的 Python API和 PC 端的 RKNN 类不同它只负责加载模型和推理不负责模型转换。推理代码很简单import cv2 import numpy as np from rknnlite.api import RKNNLite rknn_lite RKNNLite() ret rknn_lite.load_rknn(./yolov5s.rknn) if ret ! 0: print(load rknn failed) exit(-1) ret rknn_lite.init_runtime(core_maskRKNNLite.NPU_CORE_AUTO) if ret ! 0: print(init runtime failed) exit(-1) img cv2.imread(./bus.jpg) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img cv2.resize(img, (640, 640)) outputs rknn_lite.inference(inputs[img]) print(len(outputs), outputs[0].shape) rknn_lite.release()有几个细节要提醒。第一输入图像必须是 RGB 顺序。OpenCV 读进来是 BGR直接喂给 NPU 会导致颜色通道错乱检测框还在但识别类别会乱掉。第二core_mask参数决定 NPU 使用哪些核心。NPU_CORE_AUTO表示自动调度所有核心适合大多数场景。如果你同时在跑其他 NPU 任务可以手动指定某一个核心避免抢占。RK3588 的 NPU 在 API 上暴露了三个核心实际跑 YOLOv5s 时自动调度效果就很好不需要手动指定。第三连续推理时要注意输出的内存复用问题。RKNNLite 的 inference 返回数组在某些版本里会复用内存如果你需要把多帧输出保存到列表里一定要用np.copy()拷贝一份否则等下一次推理后列表里全是同一份最新的数据。4.3 YOLOv5输出解码与NMSRKNN 推理得到的outputs[0]是 shape 为 (1, 25200, 85) 或者 (1, 85, 25200) 的数组。新版 YOLOv5 导出 ONNX 时一般是前者但为了稳妥代码里判断一下维度大小。解码过程就是把前 4 个值当作中心点坐标和宽高第 5 个值当作目标置信度后面的 80 个值是一组类别得分。先做置信度过滤再做 NMS 去掉重复框。完整后处理代码如下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 nms(boxes, scores, iou_thres): x1 boxes[:, 0] y1 boxes[:, 1] x2 boxes[:, 2] y2 boxes[:, 3] areas (x2 - x1 1) * (y2 - y1 1) order scores.argsort()[::-1] keep [] while order.size 0: i order[0] keep.append(i) xx1 np.maximum(x1[i], x1[order[1:]]) yy1 np.maximum(y1[i], y1[order[1:]]) xx2 np.minimum(x2[i], x2[order[1:]]) yy2 np.minimum(y2[i], y2[order[1:]]) w np.maximum(0.0, xx2 - xx1 1) h np.maximum(0.0, yy2 - yy1 1) inter w * h ious inter / (areas[i] areas[order[1:]] - inter) inds np.where(ious iou_thres)[0] order order[inds 1] return keep def postprocess(outputs, conf_thres0.25, iou_thres0.45): pred outputs[0][0] if pred.shape[0] pred.shape[1]: pred pred.T boxes pred[:, :4] scores pred[:, 4] * pred[:, 5:].max(axis1) class_ids pred[:, 5:].argmax(axis1) mask scores conf_thres boxes boxes[mask] scores scores[mask] class_ids class_ids[mask] if len(boxes) 0: return [], [], [] boxes xywh2xyxy(boxes) keep nms(boxes, scores, iou_thres) return boxes[keep], scores[keep], class_ids[keep]建议把置信度阈值和 IoU 阈值设成可配置参数不同场景下 YOLOv5 的输出差异很大。例如密集检测场景IoU 阈值要调高一些避免相邻目标被合并小目标多的时候置信度阈值要降下来一些因为小目标得分普遍偏低。后处理得到的坐标是相对于 640x640 输入图归一化后的值要映射回原图只需要除以 640 再乘以原图宽高即可。4.4 性能调优实测RK3588 的 NPU 跑 YOLOv5s int8 格式在我的测试环境下单帧推理大约 30ms 到 40ms加上图像缩放和 NMS 后处理整体在 45ms 左右大致 20FPS 到 25FPS。如果还想提速有几个方向可以尝试。第一个方向是使用 RGA 硬件缩放代替 OpenCV 的 resize。RK3588 的 RGA 模块可以异步执行图像缩放、格式转换不占 CPU 和 NPU。RKNN-Toolkit2 的 examples 里有 RGA 相关接口但不同版本的 API 不稳定如果你的时间有限先用 OpenCV 跑通流程性能优化后续再做。第二个方向是减少输入分辨率。YOLOv5 默认 640x640 输入如果你检测的目标不是特别小可以缩到 480x480 甚至 416x416推理速度和后处理时间几乎成平方下降。当然训练时最好也用相同分辨率微调过否则精度下降明显。第三个方向是连续帧场景下开启多线程流水线。解码线程、预处理线程、推理线程、后处理线程分离用队列传递帧数据实测能将整体帧率提升 30% 以上。RK3588 有多个 CPU 核心完全撑得住这种多线程模型。5. 高频报错与排查速查表5.1 torch_npu相关报错网上搜 RK3588 NPU 推理问题时经常会看到npu is selected as device, but torch_npu is not available. please ensure...这类报错。这个报错的本质是代码里明确指定了使用npu设备但当前环境并没有安装华为昇腾芯片对应的 torch_npu 扩展。这个和 RK3588 平台的标的不是一回事。RK3588 的 NPU 推理不要用torch.device(npu)这套 PyTorch 设备体系RKNN 模型和 PyTorch 模型不是同一个东西。如果你的旧代码里写了model.to(npu)或者outputs model(inputs)请全部替换为 RKNNLite 的rknn_lite.inference(inputs[img])调用方式。如果只是跑转换和部署板端环境不需要安装 PyTorch也不需要安装 torch_npu。5.2 RKNN模型加载与运行时错误下面是我实际遇到过的几个高频报错整理成一个速查表方便大家对照排查。报错信息可能原因解决方式load_rknn failed. error code: -1模型路径错误、文件损坏或runtime版本不匹配重新从 PC 端导出 RKNN确保工具版本和板端 librknnrt.so 版本一致E Invalid RKNN model versionRKNN模型由新版本或旧版本工具生成当前runtime不支持用匹配的 RKNN-Toolkit2 重新转换AttributeError: module numpy has no attribute intnumpy 版本过高旧版RKNN调用np.int降低 numpy 到 1.23 以下GLIBCXX_3.4.30 not foundconda 环境中 libstdc 版本太旧执行conda install libstdcxx-ng升级No module named rknn安装的是 lite 包但脚本里 import 了rknn.api.RKNN板端脚本使用from rknnlite.api import RKNNLite遇到加载失败时最笨但最有效的方法就是把 verbose 打开。RKNNLite(verboseTrue)会打印更详细的日志通常可以定位到是哪一步出了问题。5.3 系统与依赖层面的坑麒麟V10 上还有一个比较隐蔽的问题系统自带的 Python 3.8 直接pip install可能装进~/.local目录但 root 用户或 sudo 执行时又找不到这些包。所以强烈建议部署时固定使用一个虚拟环境并且明确指定解释器路径。无线网卡的问题也不容忽视。很多 RK3588 开发板没有板载有线网口依赖 USB 无线网卡。麒麟V10 内核版本如果比较旧一些新出的 AX210、AX211 网卡可能识别不了。优先用有线连接或者借 USB 转千兆网口实在要用无线网卡需要确认内核支持对应驱动必要时升级内核到 5.10 以上。系统盘空间不足也是常见坑。RKNN 模型虽然只有几十 MB但 conda 环境、OpenCV、各种依赖加起来很容易吃掉十几个 GB。部署前用df -h检查根分区剩余空间不够就先扩分区或清理 apt 缓存不要等到推理到一半报磁盘写入失败再处理。6. 写在最后的一些经验整套流程跑下来我最深的体会是RK3588 的 NPU 部署本身不算难难点在环境和版本匹配。RKNN-Toolkit2 的版本、librknnrt.so 的版本、Python 版本、numpy 版本任何一个对不上报错都会非常隐晦。所以拿到一个新板子第一件事是确认板厂 SDK 自带的 RKNN 版本然后 PC 端安装完全一致的版本这样能省掉 80% 的折腾时间。另外一个小技巧是无论你最终要部署的模型是自己训练的 YOLOv5还是改过的 YOLOv5 变体都建议先拿官方 yolov5s.pt 完整跑通一遍转换和板端推理确认整个工具链没问题后再替换成自己的权重。因为一旦工具链不熟自己的模型又不出框你很难判断到底是模型问题还是流程问题。如果后期想把检测性能再往上推可以从固定输入尺寸、RGA 预处理、多线程流水线这几个方向入手。RK3588 的可玩性很高跑通只是第一步把性能压榨出来才更有意思。