无需开发板,用RKNN-Toolkit2模拟器完成YOLOv5s模型转换与推理仿真

发布时间:2026/10/2 12:11:39
无需开发板,用RKNN-Toolkit2模拟器完成YOLOv5s模型转换与推理仿真
上一讲我们把香橙派 RK3588 的 NPU 基础和工具链理了一遍不少新手朋友反馈说板子还在路上或者系统刚烧好不敢乱动。所以这一讲专门安排一个“不用碰板子”的环节在 PC 上装好 RKNN-Toolkit2用它的内置模拟器把 YOLOv5s 从 PyTorch 权重一路转成 RKNN 模型再通过模拟器完成一次真正意义上的 NPU 推理仿真。这一讲包含 RK3588 目标检测部署完整链路里的模型转换、量化、推理验证三大关键步骤适合尚未收到开发板、想提前熟悉 RKNN 转换流程的读者也适合已经在板子上部署过、想回头理解模拟器与真机差异的朋友。说句大实话很多人在 RK3588 上折腾 YOLOv5最后卡住的地方往往不是板子本身而是模型转换和参数设置。模型在 PC 上没转对、没仿真好拿真机烧进去照样是黑屏、报错、结果不对。这一讲把“仿真”这一步做扎实后面真机部署就是水到渠成的事。1. 为什么选择 RKNN 模拟器先把部署链路走通再碰真机1.1 避免反复烧系统的低效循环我见过不少第一次玩 RK3588 的朋友拿到香橙派之后第一件事就是刷系统、连屏幕、敲命令结果一遇到问题就开始重刷镜像一晚上刷了四五次时间全浪费在等待和重复操作上。实际上如果你的目标是跑通 YOLOv5那么在 PC 上把模型转换和推理逻辑验证完再上板子可以把排错范围缩小到硬件和驱动层面而不是在模型格式、算子兼容性、后处理代码这些“软件坑”里挣扎。RKNN-Toolkit2 提供的模拟器模式就是在没有连接 RK3588 硬件的情况下在 x86 的 PC 上模拟 NPU 的计算过程。你可以直接加载 ONNX 模型转换成 RKNN 格式跑一次完整的推理输出结果与真机 NPU 的执行结果高度一致。这个流程的价值在于它把“模型能不能在瑞芯微平台上跑”这个问题从“需要一块板子才能回答”变成了“在 PC 上花几分钟就能验证”。1.2 模拟器与真机 NPU 的“同构”关系不少读者第一次听到“模拟器”三个字第一反应是 Android 模拟器或者 QEMU 那种指令级虚拟机。这两类方案我都试过先说结论跑 YOLOv5 都不合适。Android 模拟器侧重交互应用纯 CPU 推理效率低QEMU 模拟 ARM64 Linux 环境在技术上可行但 PyTorch 的算子执行效率惨不忍睹跑一张 640×640 的图可能要几十秒完全不具备实用性。RKNN-Toolkit2 的模拟器不一样。它并不是模拟整个 ARM CPU而是直接模拟 RK3588 上那个 NPU 的算子计算图。你调用init_runtime(targetsimulator)的时候RKNN 运行时会在 PC 的 CPU 上按照 RKNN 计算图逐算子执行模型转换时的量化参数、算子融合逻辑、内存布局优化都和真机保持一致。换句话说模拟器和你真机上的librknnmrt.so执行的是同一套推理逻辑只是底层硬件从 NPU 换成了 x86 CPU。这也是为什么我们敢用模拟器结果作为真机部署的预验收依据。2. 搭建 PC 端仿真环境RKNN-Toolkit2 安装与工程规划2.1 Python 虚拟环境与依赖隔离先强调一个坑RKNN-Toolkit2 的依赖里有特定版本的 numpy、torch、opencv 等库如果你直接用系统 Python 环境安装很容易把本来能用的开发环境搅乱。我习惯的做法是给 RKNN 单独建一个虚拟环境不用 conda 也至少用 venv。python3.10 -m venv ~/rknn_env source ~/rknn_env/bin/activate重点说一下 Python 版本的选择。RKNN-Toolkit2 官方长期测试的是 Python 3.8 到 3.11 这个区间我用 3.10 踩坑最少。如果你机器上只有更高版本的 Python建议先装一个 3.10避免后续因为 Cython 绑定的兼容性而报错。2.2 安装 RKNN-Toolkit2 的两种途径RKNN-Toolkit2 现在可以直接通过 pip 安装源在 PyPI 上pip install rknn-toolkit2不过要注意直接 pip 安装有时会拉取到较新版本新版本对旧模型的兼容性不一定好。更稳妥的方式是从瑞芯微官方的 GitHub 仓库下载对应版本的 wheel 文件离线安装# 以 v2.2.0 版本为例先下载对应 Python 版本的 whl pip install rknn_toolkit2-2.2.0-cp310-cp310-manylinux_2_17_x86_64.whl我个人倾向于官方仓库的 release 版本因为模型转换和板端运行时版本是配套的。你之后在香橙派上安装的rknn-toolkit-lite2或者librknnmrt.so最好和 PC 端的 RKNN-Toolkit2 保持同一版本号这样生成的 RKNN 模型不会出现版本兼容问题。2.3 快速验证环境是否可用装完之后建议先跑一段极简脚本验证依赖不要直接上 YOLOv5否则出了问题很难判断是环境问题还是模型问题。from rknn.api import RKNN rknn RKNN() print(RKNN-Toolkit2 loaded successfully, version:, rknn.get_version()) rknn.release()能输出版本号说明基本依赖正常。如果这里报错无非是三种情况numpy 版本冲突、缺少onnx依赖、Python 版本不匹配。逐个解决即可。3. YOLOv5 模型准备与 RKNN 转换3.1 拉取权重与导出 ONNXRK3588 的 NPU 不能直接吃 PyTorch 权重中间需要一个 ONNX 作为中转。具体流程是 PyTorch 权重转 ONNX再用 RKNN-Toolkit2 把 ONNX 转成 RKNN。先拉取 YOLOv5 源码并安装依赖git clone https://github.com/ultralytics/yolov5.git cd yolov5 git checkout v7.0 pip install -r requirements.txt下载 YOLOv5s 预训练权重wget https://github.com/ultralytics/yolov5/releases/download/v7.0/yolov5s.pt然后导出 ONNX。这里有一个关键经验opset 版本不要用默认的 17建议固定到 12。RKNN-Toolkit2 对 ONNX 算子的支持覆盖主流算子但新版本算子集引入的一些写法会增加转换失败的概率。导出命令如下python export.py --weights yolov5s.pt --img 640 --batch 1 --include onnx --opset 12 --simplify--simplify参数会调用 onnx-simplifier 对计算图做常量折叠和算子化简这一步对后续转换帮助很大能减少不少不被 NPU 支持的冗余算子。如果你的环境没有安装onnx-simplifier记得先pip install onnx-simplifier。导出成功后你会得到一个yolov5s.onnx用 Netron 打开可以观察到 YOLOv5 v7.0 的输出实际上是三个尺度的特征图形状分别是[1, 255, 80, 80]、[1, 255, 40, 40]、[1, 255, 20, 20]这里的 255 表示每个网格位置预测 3 个 anchor、每个 anchor 有 85 个参数4 个坐标 1 个物体置信度 80 个类别概率。3.2 ONNX 转 RKNN 的关键配置新建一个convert.py脚本内容如下from rknn.api import RKNN output_path ./yolov5s_rk3588.rknn rknn RKNN() # 关键配置 rknn.config( mean_values[[0, 0, 0]], std_values[[255, 255, 255]], target_platformrk3588, optimization_level3 ) # 加载 ONNX ret rknn.load_onnx(model./yolov5s.onnx) assert ret 0, load_onnx failed # 构建 RKNN 模型 ret rknn.build(do_quantizationTrue, dataset./dataset.txt) assert ret 0, build failed # 导出 RKNN ret rknn.export_rknn(output_path) assert ret 0, export failed rknn.release() print(convert done:, output_path)这段代码有几个细节需要解释。mean_values和std_values的含义是推理时 NPU 内部执行(input - mean) / std。YOLOv5 原始预处理是像素值除以 255 归一化到 [0,1]所以 mean 设为 0、std 设为 255。如果你在导出 ONNX 时已经在 PyTorch 侧做了归一化这里的参数要相应调整否则推理结果会异常。target_platformrk3588告诉转换器生成针对 RK3588 NPU 的计算图。optimization_level3表示开启最大程度优化会做算子融合、内存复用等。如果转换遇到算子不支持可以把优化级别调到 2 甚至 1用性能换兼容。3.3 量化与 dataset 准备上面的脚本里do_quantizationTrue这是 RK3588 NPU 部署的常见选择。RK3588 NPU 对 INT8 的支持非常完善量化之后模型体积缩小约 3.75 倍推理速度也有明显提升。但量化需要一个校准数据集。dataset.txt每一行写一张用于校准的图片路径不要用训练集用有代表性的、场景多样的图片即可。通常会准备 100 张左右images/coco_0001.jpg images/coco_0002.jpg ... images/coco_0100.jpg图片不需要和训练分辨率完全一致但建议统一缩放到 640×640。量化过程会用这些图片统计各层的激活值分布确定 INT8 量化范围。我实测的经验是校准图片太少会导致量化误差大边缘小目标容易丢图片超过 200 张则收益递减。如果你发现量化后精度下降比较多可以尝试do_quantizationFalse先跑一版 FP16 的 RKNN 模型验证整个链路是否通畅。等确定没问题再回来做量化。RKNN-Toolkit2 支持两种模式自由切换不需要改其他代码。3.4 转换过程的常见输出解读转换过程会在终端打印不少信息新手容易被吓到。其实只需要关心几类Loading ONNX model说明 ONNX 加载成功。Quantizing开始逐层量化校准等它跑完。build success模型构建成功。WARNING: op ... not support有算子不被支持或执行失败需要看具体是哪个算子。转换完成后会生成yolov5s_rk3588.rknn这就是后续要部署到香橙派上的模型文件。转换这一步是最容易出问题的地方我在第 5 节会专门列排查经验。4. PC 模拟器仿真推理完整实现4.1 图片前处理letterbox 与格式转换在跑推理之前前处理必须先做好。YOLOv5 的训练过程把输入图片做了 letterbox也就是保持长宽比缩放到 640×640剩余区域填充 114。推理时也必须做同样的操作否则目标位置会偏移。这里给出一个可直接用的 letterbox 实现import cv2 import numpy as np def letterbox(img, new_shape(640, 640), color(114, 114, 114)): h, w img.shape[:2] target_h, target_w new_shape ratio min(target_h / h, target_w / w) new_w, new_h int(round(w * ratio)), int(round(h * ratio)) resized cv2.resize(img, (new_w, new_h), interpolationcv2.INTER_LINEAR) dw target_w - new_w dh target_h - new_h top, bottom dh // 2, dh - dh // 2 left, right dw // 2, dw - dw // 2 canvas np.full((target_h, target_w, 3), color, dtypenp.uint8) canvas[top:top new_h, left:left new_w] resized return canvas, ratio, (top, left)这段代码返回的ratio和(top, left)在后面逆映射检测框坐标时要用到别扔掉。4.2 在 simulator 模式下运行推理接下来写推理脚本核心就一行init_runtime(targetsimulator)。from rknn.api import RKNN import cv2 rknn RKNN() rknn.load_rknn(./yolov5s_rk3588.rknn) # 关键指定 simulator而不是 rk3588 ret rknn.init_runtime(targetsimulator) assert ret 0, init runtime failed img0 cv2.imread(./test.jpg) img, ratio, (top, left) letterbox(img0, (640, 640)) # 转为 RGB img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 推理 outputs rknn.inference(inputs[img]) print(outputs:, len(outputs)) for i, out in enumerate(outputs): print(output, i, shape:, out.shape) rknn.release()这里inputs传的是HWC排布的 uint8 数组RKNN API 内部会做后续处理。模拟器模式下计算完全在 CPU 上进行速度比真机 NPU 慢很多所以不要指望它能实时跑视频。等这一步跑通再换到板子上之后你会明显感受到 NPU 的性能优势。4.3 YOLOv5 输出的后处理解析模拟器推理输出的outputs是三个特征图格式为[1, 255, 80, 80]、[1, 255, 40, 40]、[1, 255, 20, 20]。后处理需要把这些原始特征图解码成检测框。这里概述解码逻辑具体可以配合官方 rknn_model_zoo 的 YOLOv5 demo 学习。解码步骤大致分三步第一步把输出通道维度拆成3 x 85对应 3 个 anchor、每个 anchor 的(x, y, w, h, objectness, cls_scores)。第二步利用锚点公式还原坐标x_center (sigmoid(x) * 2 - 0.5 grid_x) * stride y_center (sigmoid(y) * 2 - 0.5 grid_y) * stride w (sigmoid(w) * 2) ** 2 * anchor_w h (sigmoid(h) * 2) ** 2 * anchor_hYOLOv5 各尺度使用的锚点如下特征图尺度锚点80x80[10,13], [16,30], [33,23]40x40[30,61], [62,45], [59,119]20x20[116,90], [156,198], [373,326]第三步根据 objectness 阈值过滤低置信度框再做 NMS 去除重叠框。类别置信度用 objectness 乘以各类别概率得到。后处理过程中要把框坐标乘回原图尺寸公式是原图坐标 (模型坐标 - (top, left)) / ratio很多人最后画框偏移了几个像素就是忘了这一步。4.4 可视化与结果验证用 OpenCV 画出检测结果for obj in detections: x1, y1, x2, y2, conf, cls_id obj cv2.rectangle(img0, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img0, f{classes[cls_id]} {conf:.2f}, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2) cv2.imwrite(./result.jpg, img0)如果你用的是 COCO 预训练权重拿官方测试的 bus 图片跑应该看到 person、bus 等检测框。如果框的位置准确、置信度正常说明模型转换和推理链路已经验证完毕。此时这个.rknn文件完全可以直接复制到香橙派上加载使用。5. 仿真阶段遇到的坑与排查经验5.1 Python 版本与依赖冲突仿真环境最常见的坑就是依赖混乱。有一次我在系统 Python 里直接pip install rknn-toolkit2装完发现 PyTorch 版本被强制降级了原来用 YOLOv5 训练好的工程全崩了折腾了大半天才恢复。所以再次强调一定要用独立虚拟环境。另外如果你系统里也装了onnx或者onnx-simplifier尽量在虚拟环境里重新装避免因为二进制扩展库冲突导致转换时段错误崩溃。5.2 算子不支持的应对策略ONNX 转 RKNN 最容易报错的就是某个算子不兼容。常见的有Gather的高维索引、Resize的特定模式、以及动态形状操作。前几年 YOLOv5 新版本在导出 ONNX 时会引入比较新的算子写法所以我建议固定使用opset 12并且开启--simplify。这样可以规避大量兼容问题。如果simplify之后仍然报算子不支持可以换个思路把 YOLOv5 的 detect 层直接裁掉只导出 backbone head 的特征图后处理全部挪到板子端用 C 或 Python 实现。这个方案也是 rknn_model_zoo 官方 demo 的做法转换稳定性最高只是后处理需要自己多写一些代码。5.3 模拟器推理速度的真相明确告诉大家模拟器模式下推理速度非常慢。一张 640×640 的图片PC 上模拟器可能要 2~5 秒甚至更久这取决于 PC 的 CPU 性能。这不是配置错了模拟器本质上是把 NPU 的计算图用 CPU 逐算子跑一遍线程调度、内存布局完全模拟硬件性能当然不可能比真机 NPU 快。我见过有人拿模拟器跑视频发现只有两三帧每秒当场怀疑 RK3588 性能不行这完全是误解。RK3588 的 NPU 算力在 6 TOPS 左右跑 YOLOv5s INT8 量化后真机一帧 640×640 大约二三十毫秒和模拟器不是一个量级。模拟器只用于验证正确性不要用于性能评估。5.4 仿真结果与真机结果的差异模拟器输出和真机 NPU 输出理论上高度一致但不是绝对一致。原因有两个一是模拟器用 CPU 计算浮点运算的细节和 NPU 有所差别二是 INT8 量化模型在模拟器和真机上执行的算子优化路径不完全相同极少数情况下置信度会有零点零几的偏差。所以你如果在模拟器上看到某个目标置信度 0.52上板之后可能是 0.48也可能反过来。这属于正常误差范围只要检出和漏检情况基本一致模型部署就算成功。如果差异很大优先排查是否用了不同版本的 RKNN-Toolkit2 和板端 runtime。5.5 常见问题速查表现象常见原因处理方法导入 RKNN 报 module 不存在wheel 与 Python 版本不匹配重新下载对应版本 whlload_onnx 失败ONNX 存在动态 shape导出时把 batch 固定为 1img 固定 640build 时报 op 不支持算子集过高或模型含特殊算子降低 opset开 simplify裁掉 detect 层推理结果全为 0 或全黑前处理参数与 config 不一致核对 mean/std 与输入图像排列检测框位置偏移明显未做 letterbox 或逆映射错误补 letterbox 并还原坐标模拟器速度太慢正常现象耐心等待流程验证后直接上真机结果与真机略有偏差浮点运算差异确认工具链版本一致属正常现象6. 仿真通过之后下一步真机部署的衔接建议这一讲虽然在 PC 上就能完成全部实验但最终目标仍然是香橙派 RK3588。仿真通过之后下一步是把yolov5s_rk3588.rknn模型文件、后处理代码和推理脚本一起拷贝到板子上。在板端部署时你不再需要 RKNN-Toolkit2 这部重工具换成轻量级的rknn-toolkit-lite2或者直接用 C API 调用librknnmrt.so。推理代码的接口几乎一样唯一的区别是init_runtime时不再填targetsimulator而是通过RKNN_Flag连接板卡设备。我在实际部署中的体会是如果你在 PC 模拟器阶段已经把所有后处理逻辑调试到画框准确上板之后大概率一次就能跑通反过来如果拿着未经验证的模型直接上板一旦推理结果乱了你都不知道该查模型还是查代码还是查硬件。另外补充一个小技巧在模拟器阶段可以把转换生成的 RKNN 模型复制一份留档并记录下当时的转换参数mean/std、量化开关、优化级别、RKNN-Toolkit2 版本号。这个记录在之后更换工具链版本、重新量化的取舍时非常有用。我在 RK3588 上部署 YOLOv5s 的过程中因为版本迁移导致模型异常最后就是靠这份记录定位到量化参数被改了才解决问题。如果你手头有自己训练的权重比如检测锥桶、特定器材等场景只需要把yolov5s.pt换成自己的权重文件按照同样的流程导出 ONNX、转换 RKNN、模拟器验证即可。这个过程和 COCO 预训练权重完全一致唯一的差异是类别数和后处理里的 class names 要同步修改。自己训练的数据集类别多、目标尺寸变化大时量化校准集的选择更讲究一些建议校准图片尽量覆盖各个类别的分布量化后的精度损失会小很多。仿真跑通了香橙派 RK3588 的 YOLOv5 部署已经完成了一大半。下一篇我们就在真机上把 RKNN 模型和推理代码完整跑起来把那块 NPU 真正用起来。