RV1106嵌入式AI开发全攻略:从环境搭建到NPU部署实战
1. 项目概述:为什么RV1106值得你投入时间?
如果你正在寻找一款既能跑轻量级Linux,又内置了MCU和0.5T NPU的嵌入式芯片,并且对成本、功耗和图像处理有综合要求,那么瑞芯微的RV1106很可能就是你的“梦中情U”。我最近刚用它完成了一个智能门铃的项目,从选型、画板、烧写到应用开发,算是把这条路上的坑都踩了一遍。今天就来聊聊,基于RV1106做开发,到底是怎么一回事,以及有哪些你绕不开的关键点。
RV1106这颗芯片的定位非常清晰:它不是追求极致性能的RK3588,而是瞄准了智能视觉IoT终端市场。它把一颗Cortex-A7应用处理器、一个实时MCU、一个0.5TOPS的NPU、以及一套不错的ISP(图像信号处理器)打包在一起。这意味着你可以用一颗芯片,同时搞定视频流的采集、AI分析、逻辑控制和网络通信,省去了外挂MCU或NPU的麻烦和成本。对于像人脸识别门锁、低功耗摄像头、工业视觉检测盒子这类产品,RV1106提供了一个相当优雅的“单芯片解决方案”。
2. 开发环境搭建与SDK获取
上手RV1106,第一步不是急着写代码,而是把“战场”布置好。官方的开发环境基于Linux,强烈建议你使用Ubuntu 18.04或20.04 LTS版本,这是经过官方验证最稳定的。别用太新的发行版,各种依赖库的版本冲突能让你怀疑人生。
2.1 获取官方SDK
瑞芯微的SDK主要通过其官方的“瑞芯微驱动助手”工具进行下载和管理。你需要先在瑞芯微的开发者网站注册账号并通过审核。这个过程可能需要一点时间,建议提前准备。
登录后,在“下载中心”找到RV1106的SDK包。通常它会被命名为类似rv1106_linux_sdk_release_YYYYMMDD.tgz这样的格式。这个SDK包体积不小,包含了U-Boot、Kernel、Buildroot/Yocto根文件系统、交叉编译工具链、以及各种外设的驱动和示例代码。
注意:官方SDK更新可能不那么频繁,但遇到问题首先检查SDK版本。有时一个已知的Bug在最新版本中已被修复。下载后务必校验MD5或SHA256,文件损坏会导致后续编译出现各种诡异错误。
2.2 搭建交叉编译环境
SDK里一般会自带交叉编译工具链,通常在prebuilts/gcc/linux-x86/arm目录下。你需要做的是将其路径加入到系统的PATH环境变量中。
# 假设你的SDK解压目录为 /home/yourname/rv1106_sdk export RK_TOOLCHAIN=/home/yourname/rv1106_sdk/prebuilts/gcc/linux-x86/arm/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/bin export PATH=$RK_TOOLCHAIN:$PATH然后,通过arm-linux-gnueabihf-gcc -v命令验证工具链是否生效。接下来,安装一些必要的编译依赖包:
sudo apt-get update sudo apt-get install git-core gitk git-gui gcc-arm-linux-gnueabihf \ u-boot-tools device-tree-compiler mtools \ libssl-dev ncurses-dev libncurses5-dev \ parted libudev-dev libusb-1.0-0-dev python-linaro-image-tools \ linaro-image-tools gcc-aarch64-linux-gnu g++-aarch64-linux-gnu \ autoconf autotools-dev libsigsegv2 m4 intltool libdrm-dev \ curl sed make binutils build-essential gcc g++ bash \ patch gzip bzip2 perl tar cpio python unzip rsync file bc wget \ libncurses5 libqt4-dev libglib2.0-dev libgtk2.0-dev \ libglade2-dev cvs git mercurial rsync openssh-client subversion \ asciidoc w3m dblatex graphviz python-matplotlib libc6-i386 \ lib32stdc++6 lib32z1这个列表看起来很长,但很多是构建完整Linux系统所必需的。一次性装好能避免后续编译时频繁中断。
2.3 编译基础固件
SDK目录下通常有一个顶层的build.sh脚本,这是编译的入口。但在第一次编译前,你需要选择板级配置。RV1106有不同的内存配置(如RV1106G2内置内存,标准版外接DDR),对应的配置文件也不同。
cd /home/yourname/rv1106_sdk # 查看可用的板级配置,通常类似 rv1106-emmc-tb.config, rv1106-nand-tb.config 等 ls rockdev/ | grep .config # 加载配置,例如选择EMMC版本的测试板配置 ./build.sh rv1106-emmc-tb.config # 开始全自动编译,这会依次编译U-Boot, Kernel, Rootfs并打包成固件 ./build.sh第一次编译耗时较长,可能需要一两个小时,取决于你的电脑性能。编译成功后,生成的固件(通常是rockdev/目录下的update.img)就是可以烧录到板子上的完整系统镜像。
3. 系统启动流程与固件烧录
理解RV1106的启动顺序,对于调试和故障排查至关重要。它的启动流程是典型的Rockchip芯片流程:
BootROM -> SPL (U-Boot Minimal) -> U-Boot Proper -> Kernel -> Rootfs
- BootROM:芯片内部掩膜ROM,出厂即固化。它负责检测启动介质(如eMMC、SPI NAND、SD卡),并加载位于特定位置的SPL。
- SPL:这是一个极其精简的U-Boot,主要作用是初始化DRAM,为加载完整U-Boot做准备。因为片内SRAM很小,完整的U-Boot必须载入到外部DDR中运行。
- U-Boot Proper:完整的引导加载程序。它进一步初始化硬件,加载设备树(DTS),最后从存储介质中加载Linux内核和设备树到内存,并跳转执行。
- Kernel:Linux内核启动,接管硬件,挂载根文件系统。
- Rootfs:根文件系统启动,运行初始化进程(如systemd或busybox init),最终启动你的应用程序。
3.1 烧录模式与工具
RV1106支持两种主要的烧录方式:
- Loader模式:也称为MaskROM模式。这是芯片最底层的烧录模式。通常需要短接板子上的测试点,让芯片在上电时直接进入BootROM,等待主机通过USB OTG口发送烧录指令。这种方式用于板子“变砖”后的救砖,或者第一次烧写空片。
- U-Boot模式:当板子上已有可运行的U-Boot时,可以通过串口中断U-Boot启动,进入U-Boot命令行,使用
rockusb或ums命令让板子模拟成一个USB存储设备,然后进行烧录。这种方式更常用,也更安全。
官方提供的烧录工具是RKDevTool(Windows)或upgrade_tool(Linux)。我强烈推荐在Linux下使用upgrade_tool,脚本化操作更高效。
3.2 使用 upgrade_tool 烧录
首先,确保你的开发板通过USB OTG口连接到电脑,并进入Loader模式(具体操作需参考你的底板原理图,通常是按住某个按键或短接测试点上电)。
# 1. 查看设备是否被识别 lsusb | grep -i rockchip # 应该能看到类似 “2207:1106” 的ID,其中1106代表RV1106。 # 2. 使用upgrade_tool烧录 cd /home/yourname/rv1106_sdk # 假设工具在 tools/linux/Linux_Upgrade_Tool/ 下 sudo ./tools/linux/Linux_Upgrade_Tool/upgrade_tool uf rockdev/update.img烧录过程会有进度条显示。完成后,让板子重新上电,就能从串口看到启动日志了。
实操心得:烧录失败最常见的原因有两个。一是USB线或接口接触不良,换条质量好的USB线试试。二是没有正确进入Loader模式,仔细核对板子的进入方法。另外,
upgrade_tool可能需要sudo权限才能访问USB设备。
4. 外设驱动与硬件接口调试
RV1106的引脚功能复用非常灵活,几乎所有GPIO都可以通过配置复用为不同的功能(UART, I2C, SPI, PWM等)。这带来了设计的灵活性,也增加了软件配置的复杂性。
4.1 设备树(DTS)配置
Linux内核通过设备树(Device Tree)来描述硬件。RV1106的DTS文件位于SDK的kernel/arch/arm/boot/dts/目录下。你需要修改的是对应你板型的DTS文件,例如rv1106-evb.dts。
假设你要启用一个I2C接口连接一个传感器(如光照传感器BH1750),你需要:
- 确认引脚复用:查阅RV1106的Datasheet,找到支持I2C功能的引脚组。例如,I2C1可能对应GPIO0_C0(SCL)和GPIO0_C1(SDA)。
- 在DTS中启用I2C控制器并配置引脚:
// 在 &i2c1 节点中取消状态禁用,并设置时钟频率 &i2c1 { status = "okay"; // 将默认的"disabled"改为"okay" clock-frequency = <100000>; // 标准模式100kHz pinctrl-names = "default"; pinctrl-0 = <&i2c1m0_xfer>; // 使用引脚复用组 i2c1m0_xfer // 添加你的设备子节点 bh1750: light-sensor@23 { compatible = "rohm,bh1750"; reg = <0x23>; status = "okay"; }; }; - 配置引脚控制(Pinctrl):确保在
pinctrl节点中,对应的引脚复用组(如i2c1m0_xfer)已经正确定义了引脚和上下拉等电气属性。这部分通常在通用的DTSI文件中已经定义好,你只需要引用即可。
修改DTS后,需要重新编译内核并更新到板子上。
4.2 调试外设:以I2C为例
驱动加载成功后,如何在用户空间验证?
# 查看I2C总线是否被识别 ls /dev/i2c-* # 应该能看到 /dev/i2c-1 # 使用i2c-tools工具进行扫描 sudo apt-get install i2c-tools sudo i2cdetect -y 1 # 这个命令会扫描I2C总线1上的所有地址,如果BH1750(地址0x23)连接正常,你会看到对应的地址被显示出来。如果扫描不到设备,按以下顺序排查:
- 硬件连接:用万用表测量SDA/SCL线是否通,电压是否正常(通常是3.3V)。上拉电阻是否焊接(通常4.7K-10K欧姆)。
- 引脚复用:确认DTS配置的引脚与实际硬件连接的引脚是否一致。可以通过
cat /sys/kernel/debug/pinctrl/pinctrl/pinmux-pins查看当前引脚的复用状态。 - 设备地址:确认传感器地址是否正确(BH1750地址可以是0x23或0x5C,取决于ADDR引脚电平)。
4.3 利用内置MCU
RV1106内置的MCU是一个亮点,它可以独立于A7核心运行,处理实时性要求高的任务(如电机控制、ADC高速采样),或者在外设休眠时维持低功耗运行。瑞芯微提供了MCU的SDK(通常是基于RTOS,如FreeRTOS)。开发流程是:
- 在A7侧的Linux系统中,通过一个特定的驱动程序(如
rpmsg)与MCU建立通信通道。 - MCU程序独立编译,生成二进制固件。
- 在Linux启动后,将MCU固件加载到其专属内存中,并启动它。
- 双方通过共享内存或消息队列进行数据交换。
这部分的开发相对独立,需要参考瑞芯微提供的《RV1106 MCU开发指南》。
5. NPU开发:释放0.5T算力
RV1106的NPU(神经网络处理单元)是其智能化的核心。它支持INT8/INT16量化,能高效运行诸如人脸检测、车牌识别、手势识别等模型。
5.1 模型转换与部署流程
你不能直接把PyTorch或TensorFlow训练出的.pt或.pb模型扔给NPU。需要经过瑞芯微的RKNN-Toolkit2工具链进行转换。
标准流程如下:
- 模型训练与导出:在PC上使用主流框架训练模型,并导出为ONNX格式。这是目前兼容性最好的中间格式。
- 模型转换:在PC上安装RKNN-Toolkit2。这是一个Python工具包,它负责将ONNX模型转换成RV1106 NPU能识别的
.rknn格式。在这个过程中,你可以进行量化(精度从FP32降到INT8,以提升速度、降低功耗和内存占用)、模型优化(算子融合、内存优化)和性能模拟。from rknn.api import RKNN rknn = RKNN() # 加载ONNX模型 ret = rknn.load_onnx(model='./mobilenet_v2.onnx') # 构建模型,指定目标平台为RV1106 ret = rknn.build(do_quantization=True, dataset='./dataset.txt', target_platform='rv1106') # 导出RKNN模型 ret = rknn.export_rknn('./mobilenet_v2.rknn') rknn.release()关键点:量化需要提供一个校准数据集(
dataset.txt里是图片路径列表),用于统计激活值的分布。量化是精度和速度的权衡,需要仔细评估。 - 模型部署:将生成的
.rknn模型文件放到RV1106板子的文件系统中。在C/C++应用程序中,调用瑞芯微提供的RKNN API来加载模型、创建推理会话、输入数据并获取输出。#include “rknn_api.h” rknn_context ctx; // 1. 加载模型 rknn_init(&ctx, model_path, 0, 0, NULL); // 2. 获取模型输入输出信息 rknn_input_output_num io_num; rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num)); // 3. 设置输入 rknn_input inputs[1]; inputs[0].index = 0; inputs[0].buf = image_data; inputs[0].size = input_size; inputs[0].pass_through = RKNN_INPUT_PASS_THROUGH; // 或进行预处理 rknn_inputs_set(ctx, io_num.n_input, inputs); // 4. 运行推理 rknn_run(ctx, nullptr); // 5. 获取输出 rknn_output outputs[io_num.n_output]; rknn_outputs_get(ctx, io_num.n_output, outputs, NULL); // 6. 后处理 outputs[0].buf 中的数据... // 7. 释放资源 rknn_outputs_release(ctx, io_num.n_output, outputs); rknn_destroy(ctx);
5.2 NPU开发避坑指南
- 算子支持:不是所有TensorFlow/PyTorch算子都被NPU支持。在转换前,务必查阅《RKNN-Toolkit2 OP支持列表》。遇到不支持的算子,可能需要修改模型结构或用支持的算子组合替代。
- 内存限制:RV1106的NPU有专用的内部SRAM,但容量有限。过大的模型(如一些大型目标检测网络)可能无法运行。RKNN-Toolkit2在转换时会评估内存占用,如果超限会报错。这时需要考虑模型剪枝、量化或选择更轻量的模型。
- 预处理对齐:模型训练时的数据预处理(归一化、减均值除方差)必须与推理代码中的预处理完全一致,否则精度会严重下降。RKNN API提供了
RKNN_INPUT_PASS_THROUGH模式,允许你在CPU上完成复杂的预处理,再将数据传给NPU;也支持直接在NPU内部进行简单的归一化。 - 多模型切换:如果需要动态切换不同模型,注意
rknn_destroy和rknn_init是有开销的。对于实时性要求高的场景,可以考虑预加载多个模型上下文,或者使用模型流水线技术。
6. ISP与摄像头开发
RV1106集成了第三代ISP,支持多帧HDR、3DNR(时域降噪)、WDR(宽动态)等算法,能显著提升图像质量,这对于视觉AI应用至关重要。
6.1 摄像头接口与配置
RV1106支持MIPI CSI和DVP两种摄像头接口,最多可接3个sensor。驱动开发主要围绕Media Controller (媒体控制器)和V4L2 (Video for Linux 2)框架。
- Sensor驱动:首先确保你的摄像头Sensor(如OV5648、SC2310)的驱动已经在内核中启用(编译为模块或内置)。这需要在Linux内核配置中打开对应的
CONFIG_VIDEO_XXX。 - 设备树配置:在DTS中描述摄像头硬件连接。这包括:
- 指定I2C总线(用于配置sensor寄存器)。
- 配置MIPI CSI或DVP接口的引脚。
- 定义时钟树(sensor需要主时钟MCLK)。
- 链接到V4L2的subdev架构。
&i2c1 { status = "okay"; ov5648: ov5648@36 { compatible = "ovti,ov5648"; reg = <0x36>; clocks = <&cru CLK_CIF_OUT>; clock-names = "xvclk"; // ... 其他属性如复位引脚、电源控制等 port { ov5648_out: endpoint { remote-endpoint = <&mipi_in_ucam0>; ># 查看识别到的视频设备 v4l2-ctl --list-devices # 通常会是 /dev/video0, /dev/video1... # 获取设备支持格式 v4l2-ctl -d /dev/video0 --list-formats-ext # 使用GStreamer或ffmpeg进行抓图预览 gst-launch-1.0 v4l2src device=/dev/video0 ! videoconvert ! ximagesink
6.2 ISP调优
出厂默认的ISP参数可能不适合你的具体场景(如室内、室外、低光)。瑞芯微提供了rkisp-tuning工具(一个运行在板子上的GUI或命令行工具),可以实时调整3A(自动对焦、自动曝光、自动白平衡)参数、色彩矩阵、降噪强度、锐化等级等。
调优是一个迭代过程:
- 在标准光照环境下,使用标定板(如24色卡)进行色彩和灰阶校准。
- 针对不同场景(如人脸识别需要肤色还原准确,车牌识别需要增强边缘对比度),微调相关参数。
- 将调优后的参数保存为IQ文件,并集成到固件中,开机自动加载。
注意事项:ISP调优专业性较强,如果对图像质量没有极端要求,使用默认参数或参考公版参数通常也能获得不错的效果。盲目调整可能使图像质量变差。
7. 系统优化与电源管理
对于电池供电的IoT设备,功耗是生命线。RV1106在这方面提供了多种机制。
7.1 CPU频率与功耗模式
RV1106的Cortex-A7核心支持动态电压频率调整(DVFS)和多级休眠。
CPU调频:Linux内核的
cpufreq子系统默认是启用的。你可以通过以下命令或API调整策略。# 查看可用策略 cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors # 通常有 performance, powersave, ondemand, conservative, userspace # 设置为按需调频(平衡性能与功耗) echo ondemand > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor # 查看当前频率 cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq对于大多数视觉AI应用,建议使用
ondemand或conservative策略。在持续推理时,CPU频率会自动升至最高以保证流畅;在空闲时自动降频省电。系统休眠:Linux支持
suspend-to-RAM(mem) 和suspend-to-idle(freeze) 等休眠状态。可以通过echo mem > /sys/power/state触发。但需要注意的是,当系统休眠时,大部分外设(包括NPU、ISP)都会掉电。如果你的应用需要低功耗下持续感知(如PIR触发唤醒),可能需要利用内置MCU来实现。
7.2 使用内置MCU实现低功耗待机
这是RV1106的杀手级特性。一个典型的设计是:
- 主A7 Linux系统完成初始化、加载模型后,进入深度休眠(甚至完全断电)。
- 内置MCU保持运行,以极低的功耗(毫瓦级别)监控一个GPIO(如连接PIR传感器)或定时器。
- 当MCU检测到唤醒事件(如有人移动),它可以通过一个GPIO中断或直接控制电源管理IC(PMIC)来唤醒A7核心和整个Linux系统。
- A7系统快速启动(可以从休眠中恢复,或冷启动),调用NPU进行AI识别,完成任务后再次进入休眠。
这种“MCU哨兵 + A7/NPU突击队”的模式,可以轻松将设备的待机功耗从几百毫瓦降到几毫瓦,极大延长电池寿命。实现它需要仔细设计硬件电源树和相应的MCU/Linux协同驱动。
8. 常见问题与排查实录
在实际开发中,你肯定会遇到各种问题。这里记录几个我踩过的典型深坑。
8.1 系统启动失败
- 现象:上电后串口无任何输出。
- 排查:
- 电源:首先用万用表测量核心电压(如VDD_LOGIC, VDD_CPU)和DDR电压是否正常、稳定。电源不稳是首要嫌疑。
- 时钟:检查24MHz主晶振是否起振。可以用示波器测量。
- Boot介质:确认EMMC或SPI NAND的焊接是否良好,数据线是否有短路或断路。检查原理图中Boot引脚(如BOOT_SEL)的上拉/下拉电阻配置是否正确,这决定了芯片从哪个介质启动。
- 串口:确认串口线连接正确(TX/RX是否交叉),波特率是否设置为1500000(瑞芯微U-Boot早期打印常用这个波特率)。
8.2 内核启动卡住
- 现象:串口有输出,但卡在某个地方,例如“Starting kernel ...”,或者卡在文件系统挂载。
- 排查:
- 内核日志:仔细查看卡住前最后几行打印的信息,通常会有错误提示。
- 设备树:最常见的原因是设备树(DTB)不匹配或错误。确认你编译和烧录的DTB文件是否对应你的板型(内存型号、外设接口)。可以在U-Boot阶段使用
fdt print命令查看当前加载的设备树信息。 - 文件系统:如果是挂载根文件系统失败,检查
root=内核参数指向的分区是否正确,文件系统镜像是否损坏。可以尝试在U-Boot下使用ext4ls mmc 0:1之类的命令查看EMMC分区内容。 - 驱动冲突:某个外设驱动初始化失败导致内核恐慌。可以尝试在内核命令行添加
loglevel=8或ignore_loglevel来打印更详细的内核信息,或者逐个禁用可疑的外设驱动(通过修改DTS的status = “disabled”)。
8.3 NPU推理结果异常
- 现象:模型能运行,但识别结果完全不对,或者精度远低于PC端模拟。
- 排查:
- 输入数据:这是最高频的问题。用
printf或写文件的方式,把实际传给rknn_inputs_set的原始数据 dump 出来,与PC端预处理后的数据进行逐字节比对。确保尺寸、颜色通道顺序(RGB/BGR)、数值范围(归一化到0-1还是0-255)完全一致。 - 量化误差:INT8量化必然会引入精度损失。对于某些敏感的任务(如关键点检测),可以尝试使用INT16量化,或者使用更复杂的量化校准方法(如使用有代表性的校准数据集)。
- 模型转换配置:检查RKNN转换时设置的
mean_values和std_values是否与训练时一致。channel_mean_value这个参数很容易配错。 - RKNN API版本:确保PC端转换模型使用的RKNN-Toolkit2版本与板端运行时库(librknnrt.so)的版本兼容。不匹配的版本可能导致未知行为。
- 输入数据:这是最高频的问题。用
8.4 摄像头无图像或花屏
- 现象:
v4l2-ctl --list-devices能看到设备,但gst-launch预览黑屏、绿屏或花屏。 - 排查:
- 电源和时钟:确认给摄像头模组的供电(DOVDD, AVDD, DVDD)是否正常,MCLK是否有输出且频率正确。
- I2C通信:使用
i2cdetect检查能否扫描到sensor的I2C地址。扫描不到,检查I2C线路和上拉电阻。 - 数据链路:对于MIPI接口,检查差分对(CLK+, CLK-, DATA+, DATA-)是否连接正确,有没有阻抗不连续。可以用示波器粗略看下MIPI波形(需要高速示波器)。
- 驱动配置:检查DTS中配置的
>
