RK3568边缘AI部署实战:PC端模型转换工具与三层分工详解
2026/9/5 3:01:49 网站建设 项目流程

RK3568 边缘 AI 从零上手(三):PC 端装转换工具,搞懂“三层分工”与 15 条命令

RK3568 的 AI 部署链路,光靠一块开发板是跑不完整的。模型训练、模型转换、目标板推理,这三个环节各干各的活,很多人一上来就急着在板子上敲命令,结果卡在模型格式不对、量化报错、推理结果全是乱码。这一篇我专门讲 PC 端该装什么、三端之间怎么分工,以及我日常用得最顺手的 15 条命令。

先说清楚这篇文章适合谁。如果你已经能点亮 RK3568 开发板、能跑通 Linux 系统,但还没碰过 RKNN-Toolkit2,不知道 modelzoo 里的模型怎么变成 .rknn 文件,也不知道 PC 端和开发板各自该装哪些东西,那这篇就是给你准备的。我会尽量把每一层职责讲透,命令也给全,你照着敲就能把环境跑起来。

1. 内容整体设计与思路拆解

1.1 “三层分工”到底分的是哪三层

RK3568 的边缘 AI 部署,我习惯把它拆成三个逻辑层,理解这三层比记住任何一条命令都重要。

第一层是训练端。这一层跑的是 TensorFlow、PyTorch、ONNX 这类框架,负责训练出模型权重。训练端一般不需要跟 RK3568 有任何直接关系,甚至可以在没有 RK3568 的纯 x86 服务器上完成。你只需要保证训练出来的模型能导出成 ONNX 格式,这是后续转换的前提。

第二层是转换端。这是 RK3568 AI 部署里最容易被忽略、也最容易出问题的一环。它运行在 PC 上,负责把训练端导出的 ONNX、TensorFlow 等模型,通过瑞芯微官方的 RKNN-Toolkit2 工具链转换成 RK3568 NPU 能识别的 RKNN 格式,同时还能做量化、剪枝、精度分析。转换结束后,会生成一个 .rknn 文件,这个文件才是最终要丢到开发板上跑的模型。

第三层是推理端。这一层就是 RK3568 开发板本身。板子上跑着 Linux 系统,通过 RKNPU2 的运行时库(librknnmrt.so)来加载并执行 .rknn 模型。推理端不需要安装完整的 RKNN-Toolkit2,只需要装 runtime 库和对应的 Python API 即可。

这三层的关系可以理解为:训练端生产原料,转换端加工成半成品,推理端负责使用。很多人把转换工具直接装在开发板上,这是不推荐的,因为 RKNN-Toolkit2 对 x86 平台支持最完善,而且模型转换和量化非常吃 CPU 和内存,在 RK3568 上跑转换既慢又容易 OOM。

1.2 为什么转换工具要装在 PC 端而不是开发板

这是新手最容易犯的迷糊。RK3568 自己就是一个带 NPU 的 SoC,为什么不能在板子上直接把 ONNX 转成 RKNN?

技术上并不是完全不行,但实际工程里没人这么干。原因有三个。

第一,RKNN-Toolkit2 最稳定的运行环境是 x86_64 的 Ubuntu。官方提供了完整的 pip 包、conda 环境配置和 Docker 镜像,而在 RK3568 的 arm64 环境下,虽然也有对应版本,但依赖项处理麻烦,性能也差很多。模型转换过程涉及大量的数值计算、量化校准、图优化,这些在 x86 上几秒钟能完成的事,在板子上可能要几分钟。

第二,量化校准需要跑数据集。RKNN-Toolkit2 在做 INT8 量化时,需要输入一批校准图片(通常是几百张),让工具去统计每一层的激活值分布。这个过程的计算量不小,放在 PC 上做会舒服得多,也方便把校准数据准备好再一次性喂进去。

第三,开发和调试的便利性。你在 PC 上可以随时修改脚本、换模型、对比不同量化策略的效果,甚至可以把 RKNN 模型放到 PC 上的模拟器里先跑一遍,确认精度没问题再部署到开发板。如果在板子上做转换,调试起来非常痛苦,改一个参数就要重新上传、重新执行,整个迭代周期被拉长好几倍。

所以实践上,PC 端(x86 Ubuntu)是转换的主战场,开发板只负责最终推理。

1.3 整体部署方案选型与预期效果

我的推荐方案是这样的:PC 端装 Ubuntu 20.04 或 22.04,用 conda 管理 Python 环境,安装 RKNN-Toolkit2 的 x86 版本;开发板保持官方系统镜像不动,只需要 push 一个 rknn-toolkit2 runtime 的 arm64 版本进去。模型训练端用什么框架无所谓,只要最终导出成 ONNX 就行。

这套方案跑起来之后,整个部署流程是线性的:训练/导出 ONNX → PC 端 RKNN-Toolkit2 转换并量化 → 生成 .rknn → 拷贝到开发板 → 用 RKNN Python API 加载推理。每一层都可以独立验证,出了问题也知道该查哪一段。

2. 核心细节解析与实操要点

2.1 认识 RKNN-Toolkit2 的整体组件

RKNN-Toolkit2 并不是一个单一的工具,它是一整套工具链的集合,各自分工明确。我列几个最核心的组件,你会经常跟它们打交道。

  • rknn-toolkit2:这是主工具包,提供 Python API,用来加载原始模型、量化、转换、导出 .rknn 文件,还包含模拟器功能,可以在 PC 上先验证转换结果。
  • rknn-toolkit-lite2:这是轻量版 Python API,运行在开发板上,用于加载 .rknn 模型并执行推理。它依赖 librknnmrt.so 运行时库。
  • librknnmrt.so:NPU 的运行时库,是连接应用层与 NPU 驱动之间的桥梁。你写的 Python 或 C 程序最终都是通过它来调用 NPU 的。
  • rknn_model_zoo:瑞芯微官方的模型仓库,里面有大量现成模型的定义、转换脚本和部署示例,是最重要的参考和学习资料。

这里有个需要特别强调的小点:rknn-toolkit2 和 rknn-toolkit-lite2 的 API 几乎相同,但底层定位完全不同。前者跑在 x86 上做转换和模拟,后者跑在板子上做真实推理。你如果在 PC 上装的是完整版,不能直接拿它去板子上用,模型格式虽然都是 .rknn,但运行环境是两套东西。

2.2 RKNN 模型格式与跨平台特性

RKNN 是瑞芯微自定义的模型格式,经过加密和优化,专门针对自家 NPU 的指令集设计。它不像 ONNX 那样是个通用格式,而是和芯片绑定的——同一个 .rknn 文件,不能拿去跑 RK3588 或者 RV1126,必须针对具体芯片平台单独转换。

这一点新手经常踩坑。你如果用 RK3588 的 rknn-toolkit2 转换出来的模型放到 RK3568 上跑,会直接报错说模型平台不匹配。所以在转换时,target_platform 参数一定要明确指定为 rk3568。

另一个特性是 RKNN 模型会把量化参数、权重、图结构全部打包在一起,部署时只需要一个文件就行,不需要把原始权重和结构分开管理。这在工程上非常友好,拷贝、升级、回滚都方便。

2.3 转换前的模型准备与格式要求

RKNN-Toolkit2 支持的输入格式包括 PyTorch、ONNX、TensorFlow、Caffe、TFLite 等,但我强烈建议你统一走 ONNX 这条路

原因有几点:ONNX 是开放的中间表示格式,几乎主流框架都能导出;RKNN-Toolkit2 对 ONNX 的支持最稳定,文档和示例也最全;排查问题的时候,ONNX 可以用 Netron 可视化,非常直观。

PyTorch 模型导出 ONNX 时要特别注意动态轴的问题。比如你的模型输入是 [1, 3, 224, 224],导出时如果 batch 维度是动态的,RKNN-Toolkit2 在解析时可能会报错。我一般都在导出 ONNX 时固定 batch=1,反正边缘设备推理本来就是单张图片一来的场景,不需要动态 batch。

TensorFlow 模型转 ONNX 需要用到 tf2onnx,Caffe 模型现在用得少了,但如果你手头有老的 Caffe 模型,RKNN-Toolkit2 也支持直接转换,只是某些算子的兼容性需要额外留意。

2.4 量化原理与校准数据集

RK3568 的 NPU 对 INT8 支持最好,INT8 模型的推理速度通常是 FP16 的 2 到 3 倍,内存占用也小得多。所以实际部署时几乎都是 INT8 量化模型。

量化的本质是把浮点权重和激活值映射到 8bit 的整数范围。比如某层输出的分布是 [-3.2, 7.5],那么量化就是要找到合适的 scale 和 zero-point,把这个范围映射到 [-128, 127]。

RKNN-Toolkit2 做量化时,需要提供一个 calibration dataset,一般就是几百张有代表性的图片。工具会把这些图片逐张喂给模型,统计每一层激活值的 min/max 或者直方图分布,从而确定 scale 和 zero-point。

这里有个实操要点:校准集不需要有标注(label),只需要是真实场景下的图片即可。校准集里的图片不要跟训练集完全一样,但分布要接近推理时的真实场景。如果你想做目标检测,就放一些目标出现在不同位置的图片;如果是做分类,就放各类别均衡的图片。校准集数量我一般控制在 200 到 500 张之间,太多会影响转换时间,太少则量化精度可能不够。

2.5 RKNPU2 运行时的工作机制

RKNPU2 是瑞芯微 NPU 的二代运行时架构,对应 RK3568、RK3588 这一代芯片。它的工作机制可以理解为:应用 CPU(跑应用逻辑)和 NPU(跑模型计算)通过共享内存进行数据交互。

具体到流程上:应用先把输入图片(通常是 NV12 或 RGB 格式)写入内存,然后调用 librknnmrt.so 的接口,把内存地址、模型输入尺寸、通道信息告诉 NPU;NPU 完成计算后,把结果写回内存,应用再从内存里取出来做后处理。整个过程是异步流水线式的,高吞吐场景下可以同时有多个输入在排队。

理解这个机制对性能调优非常关键。比如你要提升推理吞吐,不是简单地换个更快模型,而是要减少 CPU 和 NPU 之间的数据拷贝次数,尽量让输入数据直接指向共享内存,避免不必要的 memcpy。

3. 实操过程与核心环节实现

3.1 PC 端环境准备与 RKNN-Toolkit2 安装

我先讲 PC 端怎么部署转换环境。推荐使用 Ubuntu 20.04 或 22.04,Python 用 3.8 到 3.11 均可,我实测 3.8 和 3.10 都没问题。

环境准备分三步走。

第一步是安装 conda,如果你已经有 conda 就跳过这一步。用 Miniconda 最省事,下载安装脚本后直接执行即可。装完后创建一个独立的 Python 环境,避免跟系统 Python 打架污染全局环境。

conda create -n rknn python=3.8 conda activate rknn

第二步是安装 RKNN-Toolkit2。瑞芯微官方提供了 pip wheels 文件和完整的依赖清单。我这里贴出最干净的安装方式,注意安装的是 x86_64 版本,如果你用的是 M 系列 Mac 或者树莓派,这条链路就不适用。

# 安装系统依赖 sudo apt-get install libxslt1-dev zlib1g-dev libglib2.0-dev \ libsm6 libxext6 libxrender-dev libgstreamer1.0-dev \ libgstreamer-plugins-base1.0-dev gcc g++ # 安装 Python 依赖 pip install numpy==1.24.4 onnx==1.13.1 \ onnxoptimizer==0.3.6 onnxruntime==1.14.1 \ torch==2.0.1 torchvision==0.15.2 \ opencv-python==4.8.1.78 protobuf==3.20.2 \ psutil==5.9.5 ruamel.yaml==0.17.21 \ flatbuffers==23.5.26 requests==2.31.0 # 安装 RKNN-Toolkit2 主包 pip install rknn_toolkit2-2.0.0b0-py3-none-any.whl

wheel 文件要从瑞芯微官方的 rknn-toolkit2 仓库下载,具体路径一般是在 GitHub 的 airockchip/rknn-toolkit2 的 releases 页面里。下载时注意区分版本号和你本机的 Python 版本,别下错。

第三步是验证安装是否成功。这一步非常重要,很多人装完就以为万事大吉,结果 import 都报错,后面转换的时候才发现环境有问题。

python -c "from rknn.api import RKNN; print('OK')"

如果输出 OK,说明环境就绪。如果报错,绝大多数情况是某个 Python 依赖版本不对,或者系统库缺失。我建议先看报错信息里缺哪个 so 文件,用 apt 装上对应的系统库就行。

3.2 准备 ONNX 模型与校准图片

拿到环境之后,先别急着转换大模型。我建议新手先用一个小模型把整个链路跑通,比如官方 modelzoo 里的 mobilenet_v2 或者 resnet18,这些模型转换速度快、问题少,适合用来验证环境。

模型准备我通常这样处理。如果你手头有 PyTorch 模型,先导出 ONNX。下面这条命令是 PyTorch 导出的标准姿势。

import torch model = torch.load('./yolov5s.pt')['model'].float() model.eval() dummy_input = torch.randn(1, 3, 640, 640) torch.onnx.export( model, dummy_input, './yolov5s.onnx', opset_version=11, input_names=['images'], output_names=['output'] ) print("ONNX exported")

导出 ONNX 时有两个点要特别留意。

第一个是opset_version。RKNN-Toolkit2 对 opset 版本有兼容范围,过高或过低都可能解析失败。我实测 opset 11 是兼容性最好的,yolov5 导出时默认也是 11 或 12,如果导出时版本更高,建议显式指定低一点。

第二个是模型输入尺寸需要固定。RKNN-Toolkit2 虽然支持动态输入,但解析和优化效果都不如静态输入好。你在导出 ONNX 时一定要用具体的 shape 生成 dummy input,这样导出的 ONNX 就带上了固定 shape。

校准图片的准备就简单了。你找一个真实场景的图片文件夹,用 Python 读取并 resize 到模型输入尺寸,逐个保存为 npy 或者由转换脚本直接读取。RKNN-Toolkit2 支持传入图片路径列表,也支持传入 numpy 数组列表,我一般用后者,因为读取和预处理都在同一份代码里可控。

3.3 15 条高频命令与对应功能拆解

这条条命令是我在 RK3568 部署中反复使用的,按功能分组说明。

第 1 条:查看 RKNN-Toolkit2 版本

python -c "from rknn.api import RKNN; print(RKNN().get_sdk_version())"

这条命令能输出当前环境中 RKNN-Toolkit2 的版本。为什么重要?因为不同版本对模型算子支持、量化算法、runtime 兼容性都不一样。你开发板上装的 runtime 库版本必须和 PC 端转换工具版本匹配,否则会出现模型能转换但板子跑不了的情况。每次开始新项目前,先敲一下这条命令确认版本。

第 2 条:全量导出 ONNX 模型结构

python -m onnxruntime.tools.make_dynamic_shape_fixed --input_names images --input_shapes 1,3,640,640 model.onnx model_fixed.onnx

这条命令的作用是把 ONNX 里的动态轴全部固定下来。很多模型导出时 batch 维度是动态的,RKNN-Toolkit2 解析时可能报错,或者生成的 RKNN 模型在推理时行为异常。用这条命令先固定 shape,再从 model_fixed.onnx 继续转换,可以省掉很多坑。

第 3 条:查看 ONNX 模型算子列表

python -c "import onnx; model=onnx.load('model.onnx'); print(sorted(set(node.op_type for node in model.graph.node)))"

这条命令用来列出模型里用到的所有算子类型。转 RKNN 之前先扫一眼算子列表,能快速判断模型是否适合部署。比如看到自定义算子或者某些不常见的算子,就要提前查一下 RKNN 是否支持,避免转换到一半才报错。

第 4 条:初始化 RKNN 对象

python -c "from rknn.api import RKNN; rknn=RKNN(); print('init ok')"

这是 RKNN 转换流程的第一步。后面所有操作都基于这个 RKNN 对象来调用,包括配置、加载模型、转换、导出。

第 5 条:模型转换全流程脚本(包含配置、加载、构建、导出)

python convert.py

这条代表的是一段完整脚本,内容如下。convert.py 是转换的骨架,你以后所有模型转换都可以基于它改。

from rknn.api import RKNN rknn = RKNN() ret = rknn.config( mean_values=[[0, 0, 0]], std_values=[[255, 255, 255]], target_platform='rk3568' ) assert ret == 0, 'config failed' ret = rknn.load_onnx(model='./model.onnx') assert ret == 0, 'load onnx failed' ret = rknn.build(do_quantization=True, dataset='./dataset.txt') assert ret == 0, 'build failed' ret = rknn.export_rknn('./model.rknn') assert ret == 0, 'export failed' print('convert done')

这里有三个参数值得解释。

mean_values 和 std_values:这两个是输入图像的归一化参数。RKNN 在 NPU 上做推理时,会先按这些值对输入图像做归一化。如果你训练模型时用的是 ImageNet 的 mean=[0.485, 0.456, 0.406]、std=[0.229, 0.224, 0.225],那你需要在 config 里设置对应的 RGB 通道均值和标准差。如果训练时用的是简单的 0~255 映射到 0~1,那 mean_values 就是 [0,0,0]、std_values 是 [255,255,255]。归一化参数错误会导致推理结果完全不对,但不会报错,这是非常隐蔽的坑。

target_platform:必须指定为 rk3568。不写或者写错(比如用默认值 rk3588),生成的模型在 RK3568 上跑不了。

dataset.txt:这个文件里每一行是一个校准图片的路径。构建时 do_quantization=True 会走量化流程,读取这些图片来统计激活值分布。

第 6 条:查看生成的 RKNN 模型信息

python -c "import os; print(os.path.getsize('model.rknn'))"

模型大小是判断模型是否符合预期的最快方式。比如 FCOS 类的检测模型,INT8 量化后应该在 10 到 20 MB 之间。如果模型过大、过小或者出现异常,往往说明转换过程有问题,需要回查。

第 7 条:用自带模拟器验证 RKNN 模型

python run_simulator.py

这条代表脚本 run_simulator.py,内容如下:

from rknn.api import RKNN rknn = RKNN() rknn.load_rknn('./model.rknn') rknn.init_runtime(target=None) img = preprocess('./test.jpg') # 自行实现预处理 outputs = rknn.inference(inputs=[img]) print(outputs)

这里的 target=None 表示在 PC 上使用模拟器进行推理,不需要连接开发板。模拟器的意义在于:模型转换后、部署到板子前,先在 PC 端跑通一次推理,确认输出结果合理再上板。这个测试环节很关键,能省下大量上板调试的时间。模拟器的精度与真实 NPU 的精度有细微差异,但不会差太多。

第 8 条:init_runtime 指定连接开发板

rknn.init_runtime(target='rk3568', device_id='adb')

当你要在真实开发板上跑推理验证时,init_runtime 需要指定 target 为 rk3568,并且通过 adb 连接。连接成功后,后面的 inference 就会在开发板的 NPU 上执行。注意这里的 device_id 是你 adb devices 里看到的设备序列号,如果只有一个设备连接,可以省略。

第 9 条:批量精度评估脚本

python eval_accuracy.py

这条代表的是一段精度评估脚本,作用是拿原始模型和量化模型在测试集上分别跑一遍,对比精度差异。内容大概是加载同一个测试集,分别调用原始模型和 RKNN 模型的推理接口,计算 mAP、accuracy 等指标,最后打印差值。如果量化后精度下降超过可接受范围,就需要考虑改用量化感知训练或者混合量化。

第 10 条:查看开发板 NPU 进程和占用

adb shell "cat /sys/kernel/debug/rknpu/load"

RK3568 的 NPU 驱动提供了实时负载查询接口。这条命令会输出各核心的占用率百分比。当你在板子上跑模型推理时,用这个命令可以确认 NPU 是否真的在工作,占用率是否正常。如果占用率很低,说明模型可能被 CPU 模拟执行了,或者推理调用失败降级了,需要排查 runtime 配置。

第 11 条:查看开发板上 runtime 库版本

adb shell "strings /usr/lib/librknnmrt.so | grep -i version"

开发板上装的 runtime 库版本,直接决定了它能跑哪个版本的 RKNN 模型。如果 PC 端转换工具是 2.0.0b0,而开发板上 runtime 库是 1.6.0,那基本没法跑。用这条命令确认两端版本匹配,是排查问题的第一动作。

第 12 条:向开发板推送模型文件

adb push model.rknn /userdata/

这个命令把 PC 端生成的模型拷贝到开发板的 /userdata 目录。注意开发板路径要选择可写的分区,系统分区一般是只读的,传上去重启就丢了。我一般统一放到 /userdata 或者 /oem 下,工程上方便管理。

第 13 条:在开发板用 lite2 推理验证

python test_rknn_lite.py

这条对应的脚本内容如下,运行在开发板上,使用 rknn-toolkit-lite2 加载模型并推理:

from rknnlite.api import RKNNLite rknn_lite = RKNNLite() rknn_lite.load_rknn('./model.rknn') rknn_lite.init_runtime() img = preprocess('./test.jpg') outputs = rknn_lite.inference(inputs=[img]) print(outputs)

注意这里 import 的是 rknnlite.api 而不是 rknn.api,这是开发板上的专属接口,不要搞混。

第 14 条:查看开发板 CPU 与内存状态

adb shell "top -d 1 -n 3"

板子推理性能不仅取决于 NPU,还跟 CPU 预处理、内存带宽有关。用 top 可以实时观察 CPU 占用和内存使用情况。如果 CPU 占用率一直是 100%,说明预处理或后处理代码可能把 NPU 的提速全抵消了,需要优化图片的 resize、格式转换或后处理的效率。

第 15 条:清理板子上无用的临时文件

adb shell "rm -rf /userdata/*.tmp"

调试过程中会反复推送模型和测试图片,板子空间本来就有限,养成顺手清理的习惯能避免奇怪的磁盘写满问题。

这 15 条命令覆盖了从环境验证、模型转换、量化、模拟器验证、板上推理、性能分析到清理的完整链路。每条命令背后的逻辑,比命令本身更重要——要知道自己每一步在做什么。

3.4 一次完整的模型转换实操演示

我拿一个实际例子,完整走一遍转换流程。假设你有一个训练好的 yolov5s.onnx,输入尺寸是 640x640。

第一步,先看模型算子列表,确认没有冷门算子。

python -c "import onnx; model=onnx.load('yolov5s.onnx'); print(sorted(set(node.op_type for node in model.graph.node)))"

第二步,打开 convert.py 脚本,填入模型路径、校准数据路径和相关参数。注意这里 mean_values 和 std_values 要和训练时保持一致。yolov5 训练时一般是对 RGB 图像做 /255 归一化,因此 mean=[0,0,0]、std=[255,255,255]。

dataset.txt 里每行一个校准图片路径:

./calib/000001.jpg ./calib/000002.jpg ./calib/000003.jpg ...

校准图片 200 张左右就够。不需要对应的标注文件,只要图片是接近真实场景的。

第三步,执行转换。

python convert.py

如果一切顺利,你会看到日志依次输出 config ok、load onnx ok、build ok、export rknn ok。最后目录下会出现 yolov5s.rknn,文件大小大概 14 MB 左右。

第四步,先在 PC 上用模拟器验证一次。

python run_simulator.py

输入一张测试图片,检查输出 shape 是否和预期一致,数值范围是否合理。如果你对模型的输出格式有了解,可以简单检查一下是否有目标框坐标和置信度出现。

第五步,push 到板子,用 lite2 推理验证。到这一步,整个链路就算彻底跑通了。

3.5 板上推理代码骨架与性能调优要点

开发板上的推理代码骨架,比转换脚本要简单得多。核心就三件事:加载模型、预处理输入、推理并解析输出。我贴一段常用的代码结构。

import cv2 import numpy as np from rknnlite.api import RKNNLite # 初始化 rknn_lite = RKNNLite() rknn_lite.load_rknn('./yolov5s.rknn') rknn_lite.init_runtime(core_mask=RKNNLite.NPU_CORE_0_1_2) # 读取并预处理 img = cv2.imread('./test.jpg') img = cv2.resize(img, (640, 640)) # 推理 outputs = rknn_lite.inference(inputs=[img]) # 后处理 boxes = postprocess(outputs[0]) print(boxes) rknn_lite.release()

性能调优主要看三个参数。

一个是core_mask,RK3568 有 3 个 NPU 核心,可以通过 core_mask 指定使用哪些核心。默认是 NPU_CORE_AUTO,系统会自动调度,但如果你想独占所有核心跑单个模型,可以显式指定 NPU_CORE_0_1_2。对于单模型场景,三个核一起上能最大化吞吐。

另一个是inference 的异步模式。RKNNLite 的 inference 默认是同步的,即调用后阻塞等待 NPU 完成计算。如果你的应用是视频流处理,建议开启异步模式,通过轮询或者回调来获取结果,这样可以同时做下一帧的预处理,隐藏掉 NPU 的计算延迟。

还有一个是内存复用。对于连续帧的处理,尽量复用同一块输入内存,避免每一帧都重新分配和释放。这在 C 接口下尤其明显,Python 的 numpy 数组复用也不难实现。

3.6 常见报错与排查方法

我在 RK3568 部署中遇到过各种报错,挑几个典型的说说。

报错一:E build failed because ops not supported

这个报错说明模型里有 RKNN 不支持的算子。常见的解决思路是:先在 PC 上查一下是哪个算子不兼容,找到之后,如果是 YOLO 系模型,通常可以把部分后处理算子(如 NMS)从模型里摘除,改到 CPU 上用 Python 实现;如果是自定义算子,需要重写网络结构,用支持的算子替代。

报错二:E load rknn model failed, check if target_platform is correct

这个报错通常因为你用 RK3588 的转换工具生成了模型,或者 target_platform 没写 rk3568,跑去板子上加载。解决方法是回到 PC 端重新转换,把 target_platform 明确指定为 rk3568,确认两端的 SDK 版本匹配。

报错三:E init runtime failed, please check NPU driver version

这个报错和系统镜像里的 NPU 驱动版本有关。RK3568 的驱动和 runtime 库需要配套,升级了系统镜像之后,原来 push 的 librknnmrt.so 旧版本可能就失效了。解决方法是回读官方文档里的版本对照表,要么把升级系统镜像,要么下载对应版本的 runtime 库重新替换。

报错四:推理输出的结果全是 0 或者全 NaN

这个报错八成不是转换的问题,而是输入数据的预处理和训练时不匹配。比如训练时用的是 BGR 顺序,你推理时传了 RGB;或者训练时是 /255 归一化,而你在 config 里写的 std_values 不是 255;又或者图像 resize 的方式不一样。这类问题不会报错,但输出彻底不对,排查起来很费时间。我的习惯是转换前就把预处理逻辑和 config 参数反复对几遍,确保一致。

报错五:板子上 runtime 库缺失

有些精简版系统镜像不带 librknnmrt.so,需要手动拷贝。解决方法是把 PC 端 rknn-toolkit2 包里的 runtime 目录(一般是 rknpu2/runtime/Linux/librknn_api/aarch64/)下的 librknnmrt.so 推到板子的 /usr/lib 目录,并设置好可执行权限。

4. 常见问题与排查技巧实录

4.1 问题速查表

现象可能原因解决方向
import rknn 报错Python 依赖版本冲突用 conda 重新建环境,按官方 requirements 逐一装
转换时算子不支持模型包含 RKNN 无法解析的算子摘除后处理算子,或查找替代实现
量化后精度下降严重校准图片分布不合理换更贴近真实场景的校准集,增加数量
板子加载模型提示平台错误target_platform 设置不对重新转换并指定 rk3568
NPU 占用率一直是 0runtime 库不匹配或推理降级到 CPU检查 librknnmrt.so 版本,确认 init_runtime 正常
推理结果全是 NaN输入预处理与训练不一致核对 mean_values、std_values、颜色通道顺序

4.2 排查效率提升技巧

排查 RKNN 问题,我总结了一个最有效率的手段:隔离变量

每次只改一个变量。比如先固定 PC 端转换参数不变,只测板子上的 runtime 是不是好的;确认 runtime 没问题后,再动转换参数。如果同时改了好几个东西再测,一出问题根本定位不了。

另外要学会看日志。RKNN-Toolkit2 的日志级别可以通过 config 或者环境变量控制,把日志打到 DEBUG 级别,能看到每一步的详细输出。转化模型时如果卡住或者报错,DEBUG 日志里通常会有具体到算子层面的信息。

还有一个技巧:多利用官方 modelzoo 作为对照组。当你自己的模型转换失败时,找 modelzoo 里结构相似的标准模型,先按官方脚本跑一遍。如果官方模型可以成功转换和推理,说明工具链本身没坏,问题出在你自己的模型上;如果官方模型也报错,那就要检查环境或版本了。

4.3 版本兼容性

RKNN 工具链的版本对应关系比较敏感,我在实践中形成了一个保险策略:PC 端全量工具、开发板 runtime、系统镜像里预装的驱动,官方推荐什么版本就用什么版本。别自作主张去升级某个单独的组件。

具体来说,以 rknn-toolkit2 2.0.0b0 为例,开发板上对应的 runtime 是 2.0.0b0,对应的系统镜像驱动要求一般在发布说明里写清楚了。如果你手头的板子刷的是较老的系统,而 PC 端工具是新版本,转换出来的模型上板可能会报兼容性错误。这时候最简单的做法是降低 PC 端工具版本去适配旧的驱动,而不是硬着头皮升级整个系统。

4.4 个人心得体会

走了几轮从零到部署的流程,我想把最想告诉你的经验浓缩成几句话。

环境准备阶段,花半天时间把 PC 端 RKNN-Toolkit2 装顺、跑通一个官方示例模型,是最划算的投资。这一步做扎实,后面对所有项目都是通用的,不会白费。

模型转换阶段,每次只改一个变量。把 target_platform、mean_values、std_values、量化开关这些参数当成实验变量来处理,量化精度不达标时,优先换校准集,再考虑混合量化,最后才想换模型结构。

部署阶段,先在 PC 模拟器上跑通,再上板,这个顺序能省一大半调试时间。真正到了板子上,多数问题都出在预处理和后处理的细节上,而不是模型本身。

另外我强烈建议你养成记录的好习惯。每次转换成功的模型,连同它的原始模型版本、onnx 导出参数、RKNN 转换配置、校准集位置、runtime 版本,全部记录到一个文档里。换模型、升级工具链、复现结果的时候,这份记录能帮你省下无数时间。

5. 推理性能分析与硬件协同优化

5.1 RK3568 NPU 性能边界

RK3568 的 NPU 算力标称是 0.8 TOPS(INT8),在 AI 芯片里算入门级别。它跟 RK3588 的 6 TOPS 相比有明显差距,所以你不能指望在 RK3568 上跑大模型。yolov5s INT8 模型,在 640x640 输入下,实测 NPU 推理耗时大约 80 到 100 毫秒(单帧),也就是 10 到 12 FPS 左右。如果是 yolov5n 这种更小的模型,输入缩到 320x320,推理耗时可以降到 30 毫秒左右,能到 30 FPS。

这个性能边界决定了你的模型选型策略。在 RK3568 上,目标检测首选 yolov5n 或 yolov6n 这类 lightweight 模型;分类模型要控制在 10 MB 以内;分割模型尽量选 lightweight 结构,别碰那些动辄上百 MB 的模型。

5.2 减少 CPU 与 NPU 数据拷贝

RKNN 推理链路里,耗时不仅在 NPU 计算本身,更在数据搬运。图片从摄像头采集到内存,再从内存拷贝到 NPU 可访问的内存,这个过程如果效率低,会大幅度拉低整体帧率。

一种常见优化是:直接用 shared memory 或者 DMA 映射的方式,让摄像头驱动把帧数据直接写到 NPU 可以访问的内存区域,避免额外的 memcpy。这种方法需要用 C/C++ 写代码,Python 接口下不容易直接操作,但如果你做的是工业级部署,这个优化能带来明显收益。

还有一种优化是减少预处理开销。opencv 的 cv2.resize 和颜色转换在 CPU 上跑,如果你在板子上用 Python 逐帧处理 1080p 的图像,CPU 占用可能直接飙到 80% 以上。这时可以考虑用板载的 RGA 硬件加速做图像缩放和格式转换,RK3568 的 RGA 支持 NV12/RGB 转换和缩放,能大幅降低 CPU 负载。

5.3 多线程与流水线设计

边缘 AI 应用往往是多路视频流或者连续帧处理。RKNNLite 在多线程下的表现,我实测过:单模型多线程推理是可以的,但线程数并不是越多越好。比核心数多一点点的线程数目能达到峰值,继续增加反而会引入调度开销和内存带宽竞争。

更推荐的方案是流水线设计。把整个链路拆成采集、预处理、推理、后处理四个阶段,用生产者-消费者模式连接,让每个阶段都在独立的线程里跑。这样即使 NPU 推理速度只有 10 FPS,整体系统的吞吐也会比串行处理高不少,因为采集和预处理可以重叠在推理时间内完成。

具体到 RKNNLite 接口上,要注意 inference 的异步模式。开启异步后,inference 函数会立即返回,你可以继续做后面的事;在下一帧需要结果时,再调用等待完成。这套机制配合多线程预处理,能把帧率再提一个台阶。

5.4 模型层面的加速策略

如果 NPU 推理时间已经压不下去,那就从模型层面想办法。

一个方向是减小输入分辨率。例如目标检测,640 降到 480,推理时间可能从 100 毫秒降到 50 毫秒,但 mAP 会掉一些。这个取舍必须根据实际场景评估。

另一个方向是模型剪枝和蒸馏。在 PC 上先把模型做结构化剪枝,剪掉冗余的通道,再蒸馏恢复精度,最后导出 ONNX 并转换。这种做法通常能让模型体积和推理时间同时压缩 20% 到 50%,但需要你对训练有一定的驾驭能力。

还有一个方向是更换更高效的模型结构。比如用 RK3568 上实测高效的 ghost 结构、mobilenetv3 的 bottleneck,或者 center 这类不需要 NMS 的检测头,它们在 NPU 上的表现往往比传统结构更友好,因为用到的算子类型更少,NPU 流水线更容易满负荷运行。

5.5 端到端性能评估方法

我自己在做性能评估时,不看单次推理时间,而是看端到端的帧率。因为板上推理通常还有采集、预处理、后处理,单看 NPU 时间并不能反映真实性能。

评估方法很简单:跑一个循环处理 200 帧,统计总耗时,得出平均 FPS。过程中同时用 rknpu 的 load 接口记录 NPU 占用率。如果 NPU 占用率很高,说明瓶颈在 NPU,优化模型结构优先;如果 NPU 占用率不高,但整体帧率上不去,瓶颈可能在 CPU 预处理或数据拷贝,优先优化代码效率。

这套评估方法能帮你快速找到短板,避免盲目调参浪费精力。

6. 进阶探索与我的踩坑经验

6.1 从模拟器到真实 NPU 的差异

RKNN-Toolkit2 的模拟器适合快速验证转换是否正确,但输出结果跟真实 NPU 有细微差别。主要来源是量化后的数值精度在不同硬件上的表现不太一样,以及 NPU 的算子实现和模拟器有所差异。

我碰到过一次情况:模拟器上推理一切正常,检测框也很准,但上了板之后发现某个类别的置信度明显偏低。排查了半天,发现问题出在模型里某个算子在真实 NPU 上的数值稳定性和模拟器不一致,导致后续输出偏差被放大。最后通过调整量化策略,改用混合量化,问题才解决。

所以我的建议是:模拟器只做基本检查,一切以板上实测为准。如果板上精度跟预期差距较大,优先考虑重新做量化。

6.2 量化感知训练值得做吗

如果你有训练数据,而且时间允许,量化感知训练是提升 RK3568 部署精度最有效的方法。普通的后训练量化(PTQ)丢精度是常态,特别是在小模型上。QAT 在训练阶段就把量化误差纳入考虑,模型权重会自适应调整,最后部署时的精度损失往往能控制在 1% 以内。

我在实际项目中做过对比,同样是 yolov5s,用 PTQ 转 INT8 后 mAP 下降了 3% 左右;而用 QAT 训练的模型再转换,mAP 只下降 0.5% 左右。对于检测类任务,3% 的 mAP 降幅可能会造成漏检率上升很多,QAT 的优势非常明显。

不过 QAT 的复杂度也摆在面前。PyTorch 的 QAT 需要改训练代码,要加入伪量化节点,训练时间也变长。如果不是精度敏感型场景,PTQ 加几轮校准集优化已经够用;如果精度要求高,投入 QAT 是值得的。

6.3 多模型同时调度注意事项

有些场景需要同时跑多个模型,比如行人检测加车牌识别。RK3568 的 NPU 是支持多模型并行的,多个模型可以分配到不同的核心上。

多模型并行部署时,要特别注意内存占用。每个模型都要加载到内存里,加上内部中间结果的缓冲,几个模型加起来很容易吃光内存。我建议先估算每个模型占用的内存,再决定同时加载多少个模型。如果内存不够,就按需动态加载,模型切换时从存储加载到内存,推理完释放,避免全部常驻。

另外多模型并行会争抢 NPU 的带宽和内存带宽,导致单个模型的推理时间可能变长。实测下来,两个小模型并行跑的吞吐通常优于串行跑两个模型,但三个以上就得慎重了,收益很可能被调度开销抵消。

6.4 部署现场容易忽略的细节

真实项目部署时,有一个细节容易被忽略:板子系统的时间同步。如果板子的系统时间不准,日志的时间戳就不可靠,排查问题时会对不上。建议在部署初始化时加上 NTP 同步或者手动校准。

还有一个细节是存储空间。RK3568 的 eMMC 一般不大,在调试阶段模型和测试数据会堆积,建议定期清理。我会写一个清理脚本,把 /userdata 下的临时文件、旧的 test.jpg、转换日志定期归档或删除。

最后是电源的稳定性。RK3568 在满负载推理时功耗波动明显,供电不足会导致 NPU 计算错误,出现偶发的推理失败。这不是软件 bug,但会让你排查到怀疑人生。建议用质量可靠的电源适配器,并且不要在同一个电源上挂太多外设。

6.5 基于 RK3568 的下一步扩展方向

打通了模型转换、部署、推理的整条链路之后,很多方向都可以继续深入。

嵌入式端的实时视频流分析是一个很自然的扩展方向,结合 RTSP 拉流、V4L2 采集和 RKNN 推理,可以做一个完整的边缘智能盒子的原型。工业质检方向上,可以把异常检测模型部署上去,配合工业相机做缺陷检测。在智能家居、闸机安防等场景的双目摄像头、多路视频拼接、行为识别等,也都能在 RK3568 上落地,只是模型和工程复杂度要合理把控。

我个人在实际操作中体会最深的一件事是:RK3568 的这套工具链,虽然前期配置有一点门槛,但一旦把 PC 端转换环境和板上 runtime 打通,后面做不同项目的效率会高很多。只要你会训练模型、会基础 Linux 操作,照着这套流程走,基本不会卡在方向上。最后再分享一个小技巧:每次拿到新的模型或者新的需求,先跑通最小可行性验证,也就是一个最简单模型从转换到板上推理,再在这个基础上迭代功能,这样可以尽早暴露工具链层面的坑,而不是等项目做到一半才发现环境有问题。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询