
瑞芯微RV1106这颗芯片我是从做IPC摄像头方案开始接触的。这颗主打低成本的芯片标称只有0.5TOPS INT8算力很多人第一反应是“这种算力跑个检测都悬分类网络能跑什么”我这次把MobileNetV3、EfficientNet-Lite、ShuffleNetV2、ResNet18、RepVGG-B0这几类主流图像分类模型轮番部署了一遍得出的结论和预期其实差别挺大。这篇就完整记录一下整个实验过程从Buildroot根文件系统搭建、RKNPU驱动与runtime版本对齐到PyTorch模型导出ONNX再转RKNN的完整链路以及量化对精度的影响和板端推理延迟的实测数据。如果你手里正好有RV1103/RV1106的板子或者你正在评估低成本端侧AI方案能不能跑图像分类任务这篇文章可以直接当参考手册用。1. 0.5TOPS的算力到底能跑动什么级别的分类网络先说清楚RV1106的真实定位避免后面所有结论被误解。这颗芯片是瑞芯微面向IPC摄像头、可视化门铃、工业视觉终端这类场景出的CPU部分是一个Cortex-A7单核主频1.2GHz左右关键亮点是集成了RKNPU2的NPU理论算力0.5TOPSINT8。芯片内置DDR不像RK3588那样外挂大内存整颗芯片的功耗和成本都压得非常低。1.1 为什么选图像分类模型做NPU适配性测试比起目标检测或者更复杂的语义分割任务图像分类模型的结构相对规整主要就是卷积、激活、池化、全连接、全局平均池化这些算子分支少后处理也简单。在遇到一个新平台时分类模型是最好的“试金石”——它能在最短时间内暴露转换链路上的问题、量化精度损失、以及NPU真实吞吐量。我在RV1106上首先跑分类本质上就是先摸清这颗NPU的脾气。而且很多实际项目里分类任务本来就是刚需巡检摄像头判定产品有无缺陷、门铃识别是否有人脸、工业相机区分物料类别。这些都是典型低成本终端场景RV1106这一类芯片恰恰是对口方案。分类模型部署跑通了后面换检测模型就有参照系。1.2 实验模型选择逻辑选择实验模型时我确定的几个标准必须主流、必须有公开预训练权重、算力跨度要有区分度。最终敲定的五款如下MobileNetV3-Large轻量级网络的典型代表带SE注意力结构参数量约548万计算量小EfficientNet-Lite0Google EfficientNet系列的移动端变体去掉了对NPU不友好的Swish和SE部分约465万参数ShuffleNetV2 1.0通道混洗结构很多端侧团队的偏好选择ResNet18经典残差网络约1170万参数作为“中等重量级”对照RepVGG-B0重参数化结构训练时多分支、推理时单分支近年非常流行这五款覆盖了从极轻量到中量级的范围基本能代表“RV1106算力边界内”的模型空间。我刻意没选ResNet50或EfficientNet-B2以上量级的网络因为按照0.5TOPS和几十MB内存的规格跑那些基本就是空谈。2. 实验底座搭建Buildroot根文件系统、RKNPU驱动与runtime的版本对齐在动手转换模型之前先把环境搭稳。这一步最容易被低估实际实验里我至少折腾了两天90%的问题都出在版本配套上。2.1 板卡与SDK选择我用的是一块RV1106核心板通过SDK自带的Buildroot构建根文件系统。从瑞芯微的官方仓库拉取SDK后里面一般包含kernel、u-boot、buildroot、rknn-toolkit2几个主要部分。这里有个关键点SDK里buildroot的package列表里有个rockchip-rknn-runtime相关的选项必须编译进去否则板端根本没有NPU推理库可用。Buildroot配置时建议顺手带上opencv和python基础环境。虽然最终生产环境基本都是C API裸写但实验阶段有Python环境调试起来会舒服很多。在menuconfig里勾选br2/package/rockchip/rknpu相关项再确认librknnrt已经选上。这里不要贪多Python环境只需要基础的numpy和opencv-python头文件就够了。2.2 最容易翻车的版本对齐工具链、runtime、内核驱动三件套RKNPU的部署链路和普通Linux程序不一样。你在PC端要装一个rknn-toolkit2来跑模型转换板端要跑一个rknn-toolkit-lite2或者直接调用C API的librknnrt.so底层还需要内核里的rknpu驱动模块。这三者的版本必须严格对齐任何一个版本对不上表现就是模型加载报错、算力异常下降运行起来莫名其妙的Segmentation fault。我自己遇到过的组合如下表具体版本号以SDK release notes为准但对齐逻辑是通用的PC端rknn-toolkit2板端runtime librknnrt内核rknpu驱动实测状态1.6.01.6.0SDK配套驱动0.8.x正常1.5.21.6.0SDK配套驱动0.8.x转换的模型在板端偶发加载失败1.6.01.5.2SDK配套驱动0.7.x部分算子执行错误结果全错所以拿到板子的第一件事不是急着跑模型而是从SDK里确认三者的版本清单统一对齐。PC端的rknn-toolkit2可以通过pip安装但要注意它依赖的rknn-toolkit2包只支持x86_64的LinuxWindows下基本不用考虑。2.3 设备树与摄像头输入的坑如果只是做纯模型推理测试不接摄像头设备树可以不怎么动。但一旦要从摄像头拿画面IPC场景必然如此就得确认MIPI CSI节点和ISP管线已经正确使能。RV1106的ISP支持RAW域预处理可以把resize和格式转换下放到硬件这会极大释放CPU。我这次实验为了控制变量先用本地图片做推理没接摄像头但如果你打算直接跑RTSP推流记得提前把设备树里的rkisp节点打开。提示判断驱动是否加载成功板端执行dmesg | grep rknpu看到rknamd相关初始化日志基本就说明驱动活着。什么输出都没有那就要回内核配置里查CONFIG_ROCKCHIP_RKNPU。3. 从PyTorch到RKNN转换链路、INT8量化与算子兼容性实测环境准备好之后进入最重要的模型转换环节。整个链路是PyTorch训练/预训练模型 → 导出ONNX → 使用rknn-toolkit2转为RKNN格式 → 板端加载推理。这一步的细节决定了后面所有环节的体验。3.1 导出ONNX时的隐藏规则很多人在这里翻车。PyTorch模型直接torch.onnx.export看似简单实际上有几个直接影响后续RKNN转换的坑第一输入尺寸必须固定。RKNN的NPU编译是静态的虽然runtime也支持动态输入但转换阶段固定尺寸会让量化更稳定也能触发NPU算子级的优化。我在导出ResNet18时用的是inputtorch.randn(1,3,224,224)明确把batch设为1。不要用-1RKNN对动态维度的支持远没有ONNX Runtime那么完善。第二opsets不要盲目追新。RKNN-Toolkit2的ONNX解析器对不同opset版本支持程度不同我实验中发现opset 11到12是最稳的。opset 13以上的某些算子如aten::split、aten::mul在某些模型里会解析失败或生成低效算子序列。导出时用opset_version12是比较保险的选择。第三关闭训练头。如果你用的是自己训练的模型导出前记得把dropout、BN层全部冻结或转成eval模式并使用model.eval()。BN层在训练和推理模式下行为完全不同推理模式会走running_mean/running_var导出后的结构才是NPU期望的形式。3.2 RKNN转换脚本骨架转换脚本其实不长核心代码是这样的from rknn.api import RKNN rknn RKNN() # target_platform必须明确指定rv1106/rv1103写成rk3588会导致板端加载失败 rknn.config( target_platformrv1106, mean_values[[123.675, 116.28, 103.53]], std_values[[58.395, 57.12, 57.375]], quantized_dtypeasymmetric_quantized-8 ) ret rknn.load_onnx(modelmobilenetv3_large.onnx) if ret ! 0: print(模型加载失败) exit(1) ret rknn.build(do_quantizationTrue, datasetdataset.txt) if ret ! 0: print(模型构建失败) exit(1) ret rknn.export_rknn(mobilenetv3_large.rknn)注意target_platformrv1106这一行很多人会漏掉或者用默认值结果板端加载模型时报invalid platform。RV1106和RK3588的NPU架构虽然都是RKNPU2但算子支持和优化策略有差异目标平台一定要对准。3.3 dataset校准集与量化精度do_quantizationTrue意味着要做INT8量化而量化需要一组校准数据。dataset.txt里逐行写上图片路径用于统计每层激活值的动态范围。我这次实验用的校准集是100张从ImageNet验证集里均匀抽出来的图片各类别尽量均衡。这里有几个经验校准集不需要大100-200张完全够关键是分布要和真实场景接近。你部署在工厂检测产品缺陷就别拿一堆风景图校准否则量化参数完全对不上实际输入分布。另外校准集里不要出现全黑、全白、纯色图这类极端分布会拉大激活值的min/max范围导致量化有效位宽变低精度损失直接翻倍。3.4 算子兼容性实测哪些模型能一次通过五款模型实际转换中ResNet18是最顺利的经典结构全是Conv/BN/ReLU/AddRKNN的算子映射非常成熟一次转换成功量化后准确率几乎不掉。MobileNetV3稍微复杂一点用了h-swish激活和SE注意力模块好在RKNN-Toolkit2对这两个结构的支持已经比较完善转换也能过但量化后准确率降幅比ResNet18明显。ShuffleNetV2的channels shuffle操作在ONNX里体现为transpose/reshape组合RKNN能解析但是会生成额外的拷贝算子推理延迟比理论值要高一点。真正让我卡住的是RepVGG-B0。它在训练时是三分支重参数化结构导出的ONNX还原成推理形态后包含大量expand、concat和add操作。问题不在算子不支持而在ONNX导出时某些Conv-BN融合没做干净导致RKNN解析时性能骤降。解决办法很简单在PyTorch里手动做一次fuse_bn或者使用官方提供的重参数化推理脚本导出结构就干净了。提示遇到转换报错优先去源模型里改结构而不是在RKNN工具链层面强行加算子映射。RKNN的工具链更新节奏远不如PyTorch快你用太新的网络结构工具链往往还没来得及适配。4. 五款主流分类模型的实测对比延迟、精度损失与资源占用环境通了模型转好了接下来就是大家最关心的环节——实测数据。这里直接上结果然后逐条解读。4.1 测试方法说明测试条件固定如下输入分辨率224x224batch1INT8量化板端NPU推理CPU部分不做任何加速。每款模型连续推理100次取中位数延迟而不是平均值——因为NPU存在时钟波动和缓存冷热平均值容易被前几次的冷启动拖慢。同时记录转换前后模型在ImageNet验证集上的准确率变化用来衡量量化损失。4.2 实测数据总表模型参数量输入尺寸RV1106 INT8延迟中位数量化后准确率降幅实测结论MobileNetV3-Large548万224x224约32ms约1.2%推荐使用EfficientNet-Lite0465万224x224约28ms约0.8%首推ShuffleNetV2 1.0228万224x224约26ms约1.5%延迟最低精度损失稍大ResNet181170万224x224约165ms约0.4%勉强可用资源占用大RepVGG-B0560万224x224约145ms约0.3%量化友好但算力吃紧4.3 逐模型解读MobileNetV3-Large跑32ms说实话超出了我的预期。本来以为SE注意力结构会让NPU有些吃力结果RKNPU2对它的映射相当好算力利用率不错。但MobileNetV3量化后准确率掉了1.2%左右这跟h-swish激活函数的分布特性有关h-swish在零点附近的非线性程度偏高INT8量化后误差被放大。EfficientNet-Lite0是目前综合体验最好的选择延迟只有28ms——这是因为Lite版本特意把Swish换成了ReLUSE模块也简化了算子种类少NPU几乎不用做额外的算子切换流水线跑得更顺。量化损失也控制在0.8%以内如果你对实时性没有变态要求EfficientNet-Lite0就是RV1106上最均衡的答案。ShuffleNetV2的延迟最低26ms但精度损失相对明显。我分析是channels shuffle那步reshape/transpose操作量化时对特征图的通道维度重排引入了额外误差而且这类compact网络本身每层特征图的信息冗余度低量化压缩比大模型更容易伤筋动骨。ResNet18跑165ms这个延迟已经不适合用来做实时逐帧分类只能用在非实时巡检或定时触发场景。它的价值在于量化精度损失极小——经典结构所有激活值分布都很“规整”INT8量化对它的伤害微乎其微。如果你的场景对精度极其敏感、延迟要求不高ResNet18反而是最省心的。RepVGG-B0比较可惜理论上重参数化后的单分支结构非常利于量化事实也如此量化损失只有0.3%但单纯算力需求高145ms的延迟让它卡在一个尴尬区间。这类模型更适合放到RK3588或者更高算力平台上发挥优势。5. 部署优化的三板斧输入预处理、零拷贝buffer与后处理CPU迁移模型能跑通只是第一步实际部署到产品里还要做一轮优化。这一章的内容是我在反复调试中总结出来的每一条都对应一个真实踩过的坑。5.1 预处理成本经常被忽略很多人在PC上测试时本地推理的预处理耗时被高性能CPU隐藏了。RV1106的Cortex-A7单核干不了太多事一张224x224图片的resize BGR转RGB 归一化在纯CPU上可能要花10-20ms接近于EfficientNet-Lite0在NPU上推理的延迟。这意味着如果你的程序每帧在CPU上做预处理总耗时可能直接翻倍。解决思路有两条。一条是使用RKNN的rknn_inputs_set时直接传入原始分辨率图像并让NPU runtime内部的硬件缩放模块处理resize和颜色转换——RKNN的input属性里有pass_through开关关闭它并传入合适尺寸runtime会自动做缩放。另一条是让设备树的ISP直接输出模型输入尺寸彻底去掉CPU参与。我实测下来第一条路径最简单有效代码改动量小延迟能省掉10ms以上。5.2 零拷贝buffer从Python API到C API的关键跨越实验阶段用rknn-toolkit-lite2的Python API非常方便几行代码就能跑推理。但如果要上生产Python API每帧都会涉及多次内存拷贝图片数据从Python对象拷到runtime输入buffer推理输出再从runtime buffer拷回Python列表。RC1106的内存本身就紧张这么来回拷贝既慢又浪费。正确的做法是用C API的rknn_create_mem和rknn_set_input_mem来实现零拷贝rknn_tensor_mem* input_mem rknn_create_mem(ctx, input_attr.size); rknn_set_input_mem(ctx, input_mem, NULL); // 每次推理时直接把图像帧memcpy到input_mem-virt_addr memcpy(input_mem-virt_addr, frame_data, input_attr.size); rknn_run(ctx, NULL);输出也是同样道理预分配rknn_tensor_mem并通过rknn_set_output_mem绑定推理完成后直接读内存不用再走rknn_outputs_get的返回值拷贝。这套零拷贝方案改完之后全链路延迟能再压缩15%左右内存占用也更可控。5.3 后处理优化softmax在单核CPU上的写法分类模型的后处理看起来简单——取softmax后找top-5。但RV1106只有单核用标准方式计算softmax先求exp和sum再逐个除会浪费大量时钟周期。优化思路就两个一是exp用查表近似二是跳过非目标类别的无关计算。实际工程里更常见的是用“稀疏softmax”思路分类模型输出的logits往往只有少数几个类别显著高于其他做一个粗排序只对top-k个候选算精确softmax其余类别直接不管。对于1000类ImageNet模型这个优化能把后处理时间从几毫秒压到微秒级。如果最终只是取argmax做判定连softmax都可以跳过直接在logits上取最大值。6. 工程落地建议什么场景该选什么模型以及测完之后的心里话数据测到这里该给个结论了。以下是个人的工程建议全部基于RV1106这颗芯片的实际限制。6.1 按场景选型参考场景需求推荐模型理由实时逐帧分类、延迟敏感EfficientNet-Lite028ms延迟低量化损失综合最优极致延迟优先、精度可容忍ShuffleNetV2 1.026ms最低延迟非实时巡检、精度优先ResNet18量化损失最小但延迟165ms内存极受限场景EfficientNet-Lite0或ShuffleNetV2模型体积小Runtime buffer占用低已有RepVGG训练权重RepVGG-B0重参数化结构量化友好但要能接受145ms延迟6.2 后续可以怎么扩展这次实验只覆盖了分类但RV1106上的部署链路是完全通用的。下一步比较自然的延伸是检测模型YOLOv5s、YOLOv6s这类轻量检测网络在RKNPU2上同样有成熟的适配。分类模型还有一个典型玩法是当前端粗筛——先用一个极轻量分类模型判断“有没有目标”再决定是否跑重型检测模型这样能把整个系统功耗再压下去一节。另外IPC场景最终肯定要接RTSP推流或本地存储NPU推理结果通过Video Buffer叠加到编码器输入上做成“智能摄像头”标准形态整个部署链路就是完整的。6.3 几点个人体会测完这轮我最大的感受是0.5TOPS算力听起来可怜但得益于RKNPU2成熟的INT8量化方案和工具链图像分类这个级别的任务在RV1106上完全是可用的。真正限制体验的反而经常是CPU侧——预处理、后处理、零拷贝没做好的话NPU再快也被拖死。另外版本对齐真的值得在项目一开始就花时间确认清楚这次实验里八成以上的报错都源自工具链版本不匹配。如果你正在评估RV1106做图像分类项目的可行性希望这份实验记录能帮你少走点弯路。模型选型看第4章的数据表部署优化看第5章的三板斧这两块是最值得直接抄作业的部分。