1. 项目概述:为什么是RV1106?
如果你最近在关注智能视觉终端,比如智能门锁、可视门铃、工业相机或者一些需要本地AI处理的小型设备,那么“瑞芯微RV1106”这个名字大概率已经进入了你的视野。作为瑞芯微在边缘AI视觉处理领域的拳头产品,RV1106精准地卡位在了一个甜点区间:它不像一些高端SoC那样追求极致的通用算力,而是将有限的芯片面积和功耗预算,全部砸在了“看得懂”这件事上。
简单来说,RV1106是一颗为“端侧智能视觉”量身定制的芯片。它的核心配置非常有意思:一个主频约1.0GHz的Cortex-A7应用处理器,搭配一个独立的MCU(微控制器单元),以及最关键的——一颗0.5TOPS(每秒万亿次运算)算力的NPU(神经网络处理器)。这种“A7+MCU+NPU”的三核异构架构,是它区别于传统嵌入式MPU(微处理器)的最大特点。A7负责运行Linux系统、处理复杂的应用逻辑和网络通信;MCU则可以在系统深度休眠时,独立维持一些低功耗的传感器监听和简单事件触发;而0.5T的NPU,就是让它能实时运行人脸识别、人形检测、车辆识别等轻量级AI模型的“大脑”。
我接触RV1106是从一个智能猫眼项目开始的。当时的需求很明确:设备需要持续供电(电池或弱电),要求功耗极低,但又要能实现本地的人脸识别比对,响应速度要在1秒以内,并且成本必须严格控制。在对比了多家方案后,RV1106几乎成了唯一的选择。比它便宜的,没有NPU,AI能力靠主CPU软算,功耗和速度都达不到要求;比它强的,要么功耗超标,要么成本翻倍,对于这种海量出货的消费级硬件来说,每一分钱和每一毫安时的电量都至关重要。所以,当你决定使用RV1106时,你本质上是在选择一个在成本、功耗和端侧AI能力之间取得了绝佳平衡的解决方案,它的主战场就是那些需要“哑终端”变“聪明”,但又必须精打细算的亿级物联网市场。
2. 开发环境搭建与SDK获取
搞RV1106开发,第一步不是急着写代码,而是把“地基”打牢。这个地基,就是官方的软件开发套件(SDK)和与之匹配的编译环境。瑞芯微的SDK是基于Buildroot构建的,这是一个非常经典和高效的嵌入式Linux构建系统,特别适合RV1106这种资源相对有限的设备。
2.1 获取官方SDK
RV1106的SDK通常不会直接公开下载,你需要通过正式的商务渠道联系瑞芯微或其授权代理商获取。拿到手的通常是一个名为rv1106_linux_sdk_release_yyyymmdd.tgz这样的压缩包。这里有个关键点:务必确认SDK的版本号。瑞芯微的SDK更新比较频繁,不同版本在内核配置、驱动支持、NPU工具链上可能有细微差别。我的经验是,除非有明确的新特性需求,否则建议使用项目初期评估时所用的稳定版本,避免在开发中期升级SDK引入不必要的兼容性问题。
解压SDK后,目录结构大致如下:
rv1106_linux_sdk/ ├── buildroot/ # Buildroot根文件系统构建目录 ├── kernel/ # Linux内核源码(通常是4.19或5.10版本) ├── uboot/ # U-Boot引导程序源码 ├── external/ # 第三方库(如mjpeg-streamer, rknpu等) ├── device/ # 设备树和板级配置文件 ├── docs/ # 开发文档(极其重要!) └── build.sh # 顶层编译脚本拿到SDK后,第一件事不是./build.sh,而是仔细阅读docs/目录下的所有文档,特别是《Linux软件开发指南》和《编译环境搭建说明》。这些文档会详细说明所需的主机系统版本(通常是Ubuntu 18.04或20.04)、依赖包列表以及编译步骤。
2.2 配置编译环境
根据文档,安装必要的依赖包。这个过程比较常规,但有几个坑我踩过:
- Python版本:Buildroot对Python3的版本可能有要求,文档里如果写了需要Python 3.8,你就别用系统自带的3.6或自己装的3.10,否则在编译一些Python工具时可能会报奇怪的错误。用
pyenv或virtualenv管理一个独立的Python环境是稳妥的做法。 - 交叉编译工具链:SDK通常会自带预编译好的工具链(如
prebuilts/gcc/linux-x86/aarch64/)。你需要将其路径加入到系统的PATH环境变量中。我习惯在~/.bashrc里添加一行:
然后执行export PATH=/path/to/your/sdk/prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin:$PATHsource ~/.bashrc。验证是否成功:aarch64-linux-gnu-gcc -v。 - 磁盘空间:完整编译一次SDK(包括内核、uboot、rootfs以及所有选中的包),大概需要20-30GB的磁盘空间。确保你的开发机有足够空间,否则编译到一半失败会很头疼。
环境配好后,可以尝试进行一次“午餐”配置。在SDK根目录执行:
./build.sh lunch这会弹出一个菜单,让你选择目标板型。RV1106可能有多个参考设计板,比如rv1106-evb1-ddr3-v10或rv1106-ipc-ddr3-v10。选择与你硬件最接近的配置。这个选择会影响后续编译出的内核设备树(.dts文件)和根文件系统的默认配置。
3. 系统镜像构建与烧录实战
系统镜像的构建是产品化过程中最核心的环节之一。RV1106的最终系统通常由四个部分组成:Loader(最小引导程序)、U-Boot(第二阶段引导程序)、Kernel(内核)和Rootfs(根文件系统)。瑞芯微的build.sh脚本帮我们把这些步骤串联了起来。
3.1 完整系统镜像编译
最常用的命令是:
./build.sh这个命令会执行全自动编译,依次编译U-Boot、Kernel,并使用Buildroot构建根文件系统,最后将所有部件打包成一个完整的update.img文件。这个镜像文件就是我们可以直接烧录到设备闪存(通常是eMMC或SPI NAND Flash)中的“总包”。
如果你想单独编译某个部分,可以使用:
./build.sh uboot: 仅编译U-Boot。./build.sh kernel: 仅编译Linux内核。./build.sh rootfs: 仅使用Buildroot重新构建根文件系统。./build.sh recovery: 编译恢复模式(recovery)镜像,用于系统升级或恢复。
在编译内核时,经常需要修改配置。不要直接去kernel/目录下用make menuconfig,因为那会使用主机架构的配置。正确的做法是:
./build.sh kernel-menuconfig这个命令会调用正确的交叉编译环境,弹出内核配置菜单。对于RV1106,有几个关键配置需要关注:
- CPU频率与电压调节:在
CPU Power Management中,可以配置DVFS(动态电压频率调节),这对功耗控制至关重要。 - NPU驱动:确保
RKNPU驱动被编译进内核(=y)而不是模块(=m),这样能确保系统启动后NPU设备立即可用。 - Sensor驱动:根据你使用的摄像头传感器(如OV系列、GC系列),在
Device Drivers -> Multimedia support -> Video capture adapters中找到并启用对应的V4L2驱动。 - 文件系统支持:确保内核支持你的存储介质(如
MTD对于SPI NAND,MMC对于eMMC)以及根文件系统格式(如SquashFS,EXT4)。
3.2 镜像烧录与设备启动
烧录需要用到瑞芯微的专用工具RKDevTool(Windows)或upgrade_tool(Linux)。这里以Windows下的RKDevTool为例,讲解关键步骤:
设备进入Loader模式:这是瑞芯微芯片的底层烧录模式。通常有两种方式:
- 硬件方式:板子上有一个特殊的“Maskrom”按键或测试点。设备断电,用镊子短接该测试点(或按住按键),然后给设备上电,再松开短接,设备就会进入Loader模式。
- 软件方式:在设备已启动到系统的情况下,通过串口或ADB执行命令
echo reboot loader > /proc/rk_loader,设备会重启进入Loader模式。这种方式在调试阶段非常方便。
连接与识别:用USB Type-C数据线连接设备的OTG口和电脑。打开RKDevTool,如果设备成功进入Loader模式,工具下方会显示“发现一个LOADER设备”。
加载配置与烧录:
- 点击“升级固件”选项卡。
- 点击“固件”按钮,选择编译生成的
update.img文件。 - 工具会自动解析镜像,并在列表中显示各个镜像部分(Loader, Parameter, Uboot, Boot, Rootfs等)。这里有个重要检查点:确认“地址”(Address)列是否正确。特别是Rootfs的地址,必须与你的存储介质分区表对应。这个分区表信息由
parameter.txt文件定义,在SDK的device/rockchip/rv1106/目录下可以找到模板。 - 确认无误后,点击“升级”按钮。进度条会开始走动,烧录时间取决于镜像大小和USB速度,通常几分钟内完成。
首次启动与调试:烧录完成后,设备会自动重启。此时,你需要通过串口(UART)连接设备,查看启动日志。串口配置通常是115200波特率,8数据位,1停止位,无校验。在串口终端(如MobaXterm, SecureCRT, Minicom)中,你应该能看到U-Boot和内核的启动信息。如果卡在某个阶段(例如“Starting kernel ...”后没反应),就需要根据日志排查问题,常见原因有设备树不匹配、内核驱动缺失、根文件系统挂载失败等。
注意:烧录有风险,特别是擦写Loader和U-Boot阶段。务必确保供电稳定,不要中途断开USB。对于量产,通常会采用“Maskrom”模式烧录,并利用
RKDevTool的“下载镜像”功能进行批量烧录,这个功能可以跳过已烧录且校验正确的部分,提高效率。
4. NPU开发:从模型转换到推理部署
RV1106的灵魂在于它的NPU。这0.5T的算力,让你能在端侧实时运行一些经过优化的神经网络模型,实现真正的本地智能。整个NPU开发流程可以概括为:模型训练 -> 模型转换 -> 模型部署。
4.1 模型选择与转换工具链
RV1106的NPU有其特定的指令集和内存布局,因此不能直接运行来自PyTorch或TensorFlow的原始模型(.pt或 .pb文件)。必须使用瑞芯微提供的RKNN-Toolkit2工具链将模型转换成专用的.rknn格式。
模型选择上的心得:不是所有模型都适合RV1106。由于算力和内存有限(NPU专用内存通常与芯片型号绑定,RV1106可能只有几百KB到1MB左右),你需要选择轻量级网络。
- 人脸识别:MobileFaceNet, ShuffleFaceNet 是不错的选择。
- 目标检测:YOLO系列的轻量变种,如YOLOv5n, YOLOv8n, 或者专为边缘设计的NanoDet, PP-PicoDet。
- 关键点检测:Lite-HRNet, MoveNet。
在PC上使用RKNN-Toolkit2进行转换的基本步骤:
- 安装RKNN-Toolkit2:按照官方文档,在Python环境中安装对应版本的RKNN包。
- 准备模型:准备好你的训练好的模型文件(如ONNX格式)和少量校准图片。
- 创建转换脚本:
关键点解析:from rknn.api import RKNN # 1. 创建RKNN对象 rknn = RKNN(verbose=True) # 2. 配置模型预处理参数(至关重要!) rknn.config(mean_values=[[127.5, 127.5, 127.5]], # 均值 std_values=[[127.5, 127.5, 127.5]], # 标准差 target_platform='rv1106') # 指定平台 # 3. 加载模型 ret = rknn.load_onnx(model='./mobilenet_v2.onnx') if ret != 0: print('Load model failed!') exit(ret) # 4. 构建模型 # `do_quantization=True` 表示进行量化,这是提升NPU推理速度的关键,但会轻微损失精度。 # `dataset` 指向一个包含几十到几百张图片路径的文本文件,用于量化校准。 ret = rknn.build(do_quantization=True, dataset='./dataset.txt') if ret != 0: print('Build model failed!') exit(ret) # 5. 导出RKNN模型 ret = rknn.export_rknn('./mobilenet_v2.rknn') if ret != 0: print('Export rknn model failed!') exit(ret) # 6. 释放资源 rknn.release()mean_values和std_values:必须与模型训练时使用的归一化参数完全一致,否则推理结果会完全错误。这是最容易出错的地方之一。do_quantization:量化是将模型权重和激活值从浮点数(FP32)转换为低精度整数(INT8)的过程。它能大幅减少模型体积、提升推理速度、降低内存占用和功耗,是边缘AI的标配。量化带来的精度损失通常在1-3%以内,对于大多数应用是可接受的。dataset.txt:里面的图片应该是训练集或验证集的一个子集,覆盖各种场景,用于统计激活值的分布,指导量化过程。图片不需要标签。
4.2 在RV1106上部署与推理
转换好的.rknn模型文件需要放到RV1106设备的文件系统中。在应用程序中,你需要使用RKNN SDK(C/C++ API)来加载和运行模型。
一个最简单的C语言推理流程如下:
#include <stdio.h> #include "rknn_api.h" int main(int argc, char* argv[]) { const char* model_path = "./model.rknn"; rknn_context ctx = 0; rknn_input_output_num io_num; // 1. 初始化RKNN上下文 int ret = rknn_init(&ctx, model_path, 0, 0, NULL); if (ret < 0) { printf("rknn_init fail! ret=%d\n", ret); return -1; } // 2. 获取模型输入输出信息 ret = rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num)); if (ret != RKNN_SUCC) { printf("rknn_query fail! ret=%d\n", ret); goto cleanup; } printf("model input num: %d, output num: %d\n", io_num.n_input, io_num.n_output); // 3. 设置输入数据(假设输入是224x224的RGB图像) rknn_input inputs[1]; inputs[0].index = 0; inputs[0].type = RKNN_TENSOR_UINT8; // 量化后模型通常是UINT8输入 inputs[0].fmt = RKNN_TENSOR_NHWC; // 数据布局 inputs[0].size = 224 * 224 * 3; inputs[0].buf = image_data; // image_data是准备好的图像数据指针 ret = rknn_inputs_set(ctx, io_num.n_input, inputs); if (ret < 0) { printf("rknn_inputs_set fail! ret=%d\n", ret); goto cleanup; } // 4. 执行推理 ret = rknn_run(ctx, nullptr); if (ret < 0) { printf("rknn_run fail! ret=%d\n", ret); goto cleanup; } // 5. 获取输出结果 rknn_output outputs[io_num.n_output]; memset(outputs, 0, sizeof(outputs)); for (int i = 0; i < io_num.n_output; i++) { outputs[i].want_float = 1; // 希望以浮点数形式获取输出(SDK内部会反量化) } ret = rknn_outputs_get(ctx, io_num.n_output, outputs, NULL); if (ret < 0) { printf("rknn_outputs_get fail! ret=%d\n", ret); goto cleanup; } // 6. 处理输出(例如,对于分类模型,找到最大概率的类别) float* output_data = (float*)outputs[0].buf; int max_index = 0; for (int i = 1; i < outputs[0].size / sizeof(float); i++) { if (output_data[i] > output_data[max_index]) { max_index = i; } } printf("Predicted class index: %d, score: %f\n", max_index, output_data[max_index]); // 7. 释放输出缓冲区 rknn_outputs_release(ctx, io_num.n_output, outputs); cleanup: // 8. 释放RKNN上下文 if (ctx > 0) { rknn_destroy(ctx); } return 0; }部署时的核心优化点:
- 零拷贝内存:RKNN SDK支持从特定内存(如通过
dma_buf分配的ION内存)直接输入数据,避免在用户空间和NPU驱动之间复制大块图像数据,能显著降低推理延迟。这需要与V4L2摄像头采集流程配合。 - 多模型流水线:你可以同时加载多个
.rknn模型到NPU内存中。通过合理的调度,可以实现“检测->跟踪->属性分析”的流水线,充分利用NPU算力。 - 动态频率调节:通过系统接口(如
sysfs)可以调节NPU的工作频率。在低负载时降低频率可以省电,在高负载时提升频率保证性能。
5. 外设驱动与硬件适配
RV1106作为一颗SoC,集成了丰富的外设控制器。你的硬件设计决定了需要适配哪些驱动。这部分工作是嵌入式开发中最具挑战性也最体现功力的地方。
5.1 摄像头(MIPI CSI)接入
视觉应用的核心是摄像头。RV1106通常支持1-2路MIPI CSI接口。驱动适配主要围绕Sensor驱动和V4L2(Video for Linux 2)子系统展开。
确认Sensor型号与驱动:首先,确认你使用的图像传感器(如OV5648, GC2053)。在Linux内核的
drivers/media/i2c/目录下查找是否有对应的驱动源文件(如ov5648.c)。如果没有,你可能需要从Sensor厂商或瑞芯微获取,或者参考类似Sensor的驱动进行移植。配置设备树(Device Tree):这是硬件描述的核心。你需要修改SDK中对应板型的设备树文件(如
kernel/arch/arm/boot/dts/rv1106-evb.dts)。- I2C总线:Sensor通常通过I2C总线配置。在设备树中,找到对应的I2C控制器节点(如
&i2c1),在其中添加你的Sensor子节点。 - MIPI CSI接口:配置CSI主机控制器节点,并将其与Sensor节点通过
port子节点关联起来。 - 时钟与电源:确保Sensor所需的MCLK(主时钟)、DOVDD(数字IO电源)、AVDD(模拟电源)、DVDD(数字核心电源)等引脚和电压 regulator 配置正确。一个配置示例的框架如下:
&i2c1 { status = "okay"; ov5648: ov5648@36 { compatible = "ovti,ov5648"; reg = <0x36>; clocks = <&cru CLK_CIF_OUT>; clock-names = "xvclk"; powerdown-gpios = <&gpio1 RK_PB0 GPIO_ACTIVE_LOW>; reset-gpios = <&gpio1 RK_PB1 GPIO_ACTIVE_LOW>; // ... 其他引脚和属性 port { ov5648_out: endpoint { remote-endpoint = <&csi_dphy_input>; ># 在目标板 gdbserver :2345 ./your_app # 在主机 aarch64-linux-gnu-gdb ./your_app (gdb) target remote 192.168.1.xxx:2345- I2C总线:Sensor通常通过I2C总线配置。在设备树中,找到对应的I2C控制器节点(如
7.2 RV1106典型问题排查表
| 问题现象 | 可能原因 | 排查步骤 |
|---|---|---|
| 系统无法启动,串口无输出 | 1. 供电异常 2. 启动介质损坏 3. Bootloader损坏 | 1. 测量核心电压(如1.0V, 1.8V)是否正常。 2. 尝试重新烧录完整的 update.img。3. 检查是否进入Maskrom模式,尝试仅烧写Loader和U-Boot。 |
| 内核启动后卡住,提示“Failed to mount /dev/root” | 1. 根文件系统镜像损坏 2. 内核命令行参数 root=设置错误3. 存储介质驱动未加载 | 1. 检查update.img中rootfs分区数据。2. 检查U-Boot环境变量 bootargs中的root=/dev/mmcblk0pX或root=/dev/mtdblockX是否正确。3. 检查内核启动日志,看是否识别到eMMC或SPI NAND设备。 |
摄像头无法打开,v4l2-ctl报错 | 1. Sensor驱动未加载 2. 设备树配置错误(I2C地址、时钟、电源) 3. 摄像头硬件连接问题 | 1.ls /dev/video*查看设备节点是否存在。2. dmesg | grep -i ov5648查看Sensor驱动加载日志。3. 用 i2cdetect扫描I2C总线,确认Sensor是否应答。4. 用万用表测量Sensor的电源和时钟引脚。 |
| NPU推理结果完全错误 | 1. 模型转换时的归一化参数(mean/std)错误 2. 输入数据格式(RGB/BGR, NHWC/NCHW)不匹配 3. 模型本身训练有问题 | 1.重点检查:核对转换脚本中的mean_values和std_values是否与训练时完全一致。2. 检查 rknn_input结构体中的fmt和type是否与模型预期匹配。3. 在PC上用RKNN-Toolkit2的模拟推理功能,输入相同数据,对比结果。 |
| 系统运行一段时间后死机或重启 | 1. 内存泄漏 2. 散热不良导致过热保护 3. 电源纹波过大,动态负载下电压跌落 | 1. 使用valgrind(交叉编译)或监控/proc/meminfo检查内存使用趋势。2. 触摸芯片表面,感觉温度。加强散热或降低CPU/NPU频率。 3. 使用示波器测量核心电源在NPU满负载时的电压波形。 |
| 网络(以太网/Wi-Fi)不稳定 | 1. 网线或天线问题 2. 驱动兼容性问题 3. 系统负载过高,中断处理不及时 | 1. 更换网线或调整天线位置。 2. 检查内核网络驱动版本,尝试更新驱动。 3. 使用 sar -n DEV 1查看网络错误计数和吞吐量。使用irqtop查看网络中断是否被及时处理。 |
调试是一个需要耐心和逻辑推理的过程。养成从底层到上层、从硬件到软件的排查习惯,先确认电源、时钟、复位等基础信号,再检查驱动加载,最后分析应用逻辑,往往能事半功倍。RV1106的开发,是硬件、底层软件和AI算法的深度结合,每一个环节的扎实理解,最终都会体现在产品的稳定性和竞争力上。