去年年底我拿到一块RK3588的开发板,想着用它跑完整的嵌入式AI视觉方案:屏幕显示、实时画面采集、OpenCV图像处理、NPU推理全链路打通。结果光是点亮那块MIPI屏就折腾了快两周,中间还踩了OpenCV编译、RKNN模型转换的无数坑。这个项目的核心就是用RK3588把LCD显示和OpenCV图像处理串起来,再把YOLOv8这类模型部署到ARM平台的NPU上,做一个完整的嵌入式AI开发闭环。适合正在做RK3588、RK3588S、RK3568等ARM平台开发的朋友参考,尤其是屏幕适配卡住、OpenCV装不上、模型转换报错这些绕不过去的坎,这篇都会讲透。
1. 为什么是RK3588:这块芯片在嵌入式AI开发中的定位
1.1 从ARM到RK3588:嵌入式开发的硬件底座
先说结论:ARM架构是嵌入式开发的绝对主流,RK3588则是当前ARM SoC里性价比和综合能力最均衡的选择之一。RK3588采用ARM Cortex-A76和Cortex-A55组成的大小核架构,8核心设计,大核主频最高能跑到2.4GHz,小核干低功耗任务,配合Mali-G610 GPU和6 TOPS算力的NPU,这一套组合在嵌入式视觉场景里非常够用。
我在实际开发中的体会是,RK3588最舒服的地方在于接口齐全:MIPI DSI和MIPI CSI都有多路,可以同时接屏幕和摄像头;PCIe、USB 3.0、千兆网口基本都配齐了。这意味着你可以在一块板子上完成"采集-处理-显示-AI推理"整个流水线,不用像以前那样用多块板子拼。相比树莓派CM4或者老一代RK3399,RK3588的视频编解码单元也强很多,H.264/H.265的硬编解码都有,做实时视频流处理的时候CPU占用明显低。
选择RK3588还有一个重要的原因:瑞芯微的文档和工具链这几年完善了很多,特别是NPU相关的RKNN-Toolkit2,从模型转换到板端推理都有相对完整的流程。虽然和NVIDIA的JetPack生态比还有差距,但至少不用靠玄学调参了。对于刚入门的朋友,我建议先从RK3588的标准Linux SDK开始,不要一上来就搞Buildroot定制或者安卓系统,Linux环境下排查问题效率高得多。
1.2 LCD显示在AI开发流程中扮演的角色
很多做算法出身的朋友容易忽略显示环节,觉得屏幕就是个外设,随便接上能亮就行。实际做完整产品的时候,显示恰恰是交付体验里最直观的部分。嵌入式AI应用里,LCD屏幕通常承担两个职责:一是调试可视化,把摄像头实时画面、检测框、识别结果叠加显示,方便开发时确认算法效果;二是产品交互界面,比如工业检测设备要有状态显示面板,门禁设备要有人脸提示界面。
在这个项目里,LCD显示还有一层作用:验证系统整体的图形栈是否正常工作。当你的OpenCV能在屏幕上实时显示图像、当你在屏幕上绘制检测框无撕裂、无延迟,说明从显示驱动到图形库到应用层的整条链路是通的。很多RK3588项目卡在"屏幕能亮但是画面花屏"或者"跑GUI只有命令行界面"这种半通不通的状态,问题往往出在framebuffer或者DRM/KMS的配置上。
另一个容易忽略的点是屏幕分辨率对AI管线的影响。RK3588的VPU和NPU都有特定的输入尺寸范围,如果你的LCD屏幕分辨率是1080p或者2K,摄像头采集的画面通常要缩放或者裁剪到这个量级再送进模型。显示链路的尺寸规格会影响你整个图像处理管线的设计,所以LCD适配不是"点亮就结束",而是要从显示时序、图层叠加、分辨率适配三个层面都确认无误。我这次用的是一块1080p的MIPI DSI屏幕,像素时钟、HFP、HBP这些时序参数都在设备树里手动核对过,这个流程后面细说。
2. LCD显示实战:从点亮屏幕到调好背光
2.1 MIPI DSI屏幕适配全流程
RK3588的LCD接口主要有两种:EDP和MIPI DSI。EDP一般接笔记本拆机屏,MIPI DSI接的是工控和开发板常用的RGB/MIPI接口屏。从实际出货量和资料丰富度来看,MIPI DSI屏是绝对的主流,这次项目用的就是一块RK3588官方评估板配套的10.1寸MIPI屏幕。
屏幕适配的第一步,也是最容易出问题的一步,是确认屏参。你需要从屏幕数据手册或者供应商那里拿到初始化序列(init code)和时序参数。初始化序列是一串寄存器写入指令,通常在几十到几百字节不等,包含了屏幕的上电时序、分辨率设置、扫描方向、伽马校正等关键信息。这些数据不是随便填的,写错了屏幕要么不亮、要么颜色错乱、要么花屏。我的建议是,如果能拿到官方SDK里同型号或同系列屏幕的dts配置,优先参考,不要自己从零推导。
设备树配置是这个流程的核心。以RK3588的SDK为例,你需要在dts里配置DSI控制器节点、panel节点和背光节点。关键属性包括compatible字符串、reg(寄存器地址)、dsi的data-lanes(数据通道数,通常4条lane)、clock-frequency(时钟频率)、以及panel的时序参数。时钟频率尤其关键,MIPI DSI的时钟频率需要根据屏幕像素时钟和位数计算,1080p屏幕一般在500MHz到800MHz之间。如果你发现屏幕点亮但是画面闪动或者有条纹,优先检查这个参数。
注意:千万不要在屏幕未确认参数正确之前就贸然上电。错误的面板电压或者时序配置存在烧毁屏幕背光IC的风险。稳妥的做法是先用官方的测试demo,确认屏幕本身是完好的,再去做系统级适配。
2.2 屏幕参数确认与设备树配置
设备树里panel节点最重要的几个字段我列一下,这些是排查问题的第一现场。首先是compatible,它决定了内核加载哪个panel驱动,不同厂商的panel驱动初始化方式差异很大,务必对应正确。其次是enable-gpios和reset-gpios,分别控制屏幕的供电使能和硬件复位,GPIO的极性配置错误会导致屏幕完全无响应。再者就是display-timings,这里面的hactive、vactive、hfp、hbp、vfp、vbp、clock-frequency等参数构成了一帧画面的完整时序。
我这次踩过的一个坑是时钟频率对不上。屏幕数据手册上标称的像素时钟是148.5MHz(1080p@60Hz典型值),但是MIPI DSI的clock-frequency和像素时钟不是一个概念,需要乘以lane数相关的系数。RK3588的DSI控制器在计算时序的时候有自己的一套逻辑,最靠谱的办法是直接用SDK提供的计算工具或者参照同类panel的配置,先求稳不求极限。
另外,还要留意屏幕的burst mode设置。MIPI DSI支持多种传输模式,常见的是burst mode和sync pulse mode。RK3588默认配置通常能自适应,但某些屏幕只支持特定模式,配置不对就会出现"时而正常、时而花屏"的诡异现象。这类间歇性问题是最难排查的,因为它不是固定的逻辑错误,而是协议层面的时序协商失败。
背光部分我单独说,因为很多人在这一步没有概念。LCD背光用的是PWM调光,你需要在设备树里配置PWM控制器节点、频率和默认亮度。背光PWM频率尽量选在1kHz以上,过低会有可见频闪,用手机拍视频的时候尤其明显。默认亮度值我习惯设在50%到70%之间,太亮既费电又刺眼,太暗又会让你误以为系统没有正常启动。
2.3 背光亮度控制与显示效果调优
背光调通了之后,显示效果调优是另一个容易忽视的环节。我刚点亮屏幕时,画面整体偏暗淡,然后发现白平衡也不对,颜色明显偏蓝。这个问题的根源在于屏幕的伽马曲线和RGB增益参数没有校准。Linux下可以通过/ sys/class/backlight节点调节亮度,或者通过DRM的color management接口调整色彩参数。如果你用OpenCV直接绘制UI,还可以在软件层做颜色校正,但那是治标不治本,硬件层面的调整才是优先方案。
色彩偏差的具体定位方法是这样:先用纯白、纯黑、纯红、纯绿、纯蓝的画面测试屏幕,确认每种颜色的显示是否准确。如果白色不纯,通常RGB三色通道的增益不平衡;如果白色本身正常但图像偏色,问题往往出在色彩空间转换上。RK3588的显示控制器支持RGB、YUV等多种格式,如果你的framebuffer格式跟panel的物理特性不匹配,也会出现色彩异常。
实操建议:把SDK里的gamma默认配置导出来对照读一遍,用一份标准的gamma曲线表格替换,再根据实测微调。很多屏幕供应商会提供推荐的颜色配置参数,直接问FAE要是最快的。
图层叠加也是一个值得提的点。RK3588的显示控制器支持多个plane叠加,可以同时显示视频层、UI层和光标层。理解了plane之后,你就能在OpenCV里做到"摄像头画面在一个plane显示、检测结果叠加层在另一个plane"这种高效的显示设计,避免通过CPU做图像合成。用DRM的modetest工具可以很方便地查看当前显示状态和plane能力,这是排查显示问题时最高效的诊断手段之一。
3. OpenCV在ARM平台上的部署与图像处理
3.1 编译OpenCV还是直接装包
OpenCV在ARM平台上的部署路径大致分三种:直接安装发行版的opencv-python包、用系统包管理器装libopencv-dev、以及从源码编译。很多人图省事直接pip install opencv-python,这条路在RK3588的Ubuntu系统上大概率能跑,但你要注意两点:一是pip装的版本可能不带GUI模块和高阶特性,imshow这类函数可能用不了;二是性能优化的空间几乎没有,因为发行版编译时没有针对ARMv8架构做充分的优化指令集配置。
我的做法是源码编译,并且只编译自己需要的模块。RK3588是ARMv8.2-A架构,支持NEON和可选的SVE指令。编译时打开NEON优化选项,再确保启用了CPU亲和性相关的配置,图像处理性能会有肉眼可见的提升。OpenCV的编译参数极其庞大,我用CMake配置的时候,会通过BUILD_LIST精简模块,把不需要的contrib模块、视频后端、3D视觉模块统统关掉,只保留core、imgproc、imgcodecs、highgui、calib3d、objdetect这些核心模块。这样编译时间大幅缩短,生成的库文件体积也小很多,对嵌入式环境更友好。
依赖处理是编译过程中最常见的坑。OpenCV编译需要ffmpeg、libjpeg、libpng、libtiff等一堆依赖,这些依赖有没有系统库版本、版本够不够新,都会影响最终的编译结果。我建议先apt安装所有必须的系统依赖,然后用pkg-config逐一确认版本。这里有个小技巧:在cmake配置阶段最终输出的SUMMARY里,会列出哪些模块被启用、哪些被禁用,务必把这一段完整读一遍,它比任何报错日志都能说明问题。
3.2 交叉编译OpenCV的完整过程
交叉编译和板端直接编译是两回事。如果你只是在自己的一块板子上做开发,板端源码编译就够了。但如果你要做量产、要在不同批次板卡上部署,或者在开发机上编译好后统一发布,那交叉编译是必须掌握的技能。
交叉编译OpenCV的核心难点不在OpenCV本身,而在于交叉编译工具链和目标系统三方库的匹配。RK3588的SDK提供了完整的交叉编译工具链,一般是基于GCC的aarch64-linux-gnu工具链。你需要确保的头文件和库文件都来自同一套rootfs,写CMake工具链文件时,CMAKE_SYSTEM_NAME、CMAKE_SYSTEM_PROCESSOR、CMAKE_C_COMPILER、CMAKE_CXX_COMPILER这几个变量是必须设置的。我还会额外设置CMAKE_BUILD_TYPE为Release,并且给编译器加上-march=armv8.2-a+optpro这类的指令集优化标志。
交叉编译完成后,最常遇到的问题就是运行时找不到共享库。因为交叉编译生成的库链接的是目标系统的路径,比如/usr/lib/aarch64-linux-gnu,如果你板端的库路径和编译时指定的不一致,运行时会报error while loading shared libraries。解决方案一个是在编译时统一指定前缀,另一个是把编译好的库直接拷贝到板端对应的系统路径下,并且确认ldconfig缓存更新了。
提示:在交叉编译之前,先在板端用apt装好基础的运行库(如libgcc、libstdc++),然后对比一下工具链自带的sysroot和板端实际的库版本。版本不一致是交叉编译程序跑到板子上莫名崩溃的常见原因。
3.3 solvePnP:从图像坐标到空间姿态
OpenCV在嵌入式AI里的作用远不止画框和调颜色。solvePnP这个函数,是很多视觉应用从2D图像走向3D定位的钥匙。它的核心功能是:已知物体在图像上的2D坐标点和对应的3D空间坐标点,求解相机相对于物体的旋转向量和平移向量。说得更直白一点,就是告诉你相机在3D空间里处于什么位置、朝向哪边。
这个函数在RK3588上跑起来非常轻松,因为它是纯粹的数值计算,CPU算力绰绰有余。难点往往在前面:怎么获取准确的2D点坐标和3D点坐标。3D坐标来自你对目标物体的建模,比如你测一块电路板,板上几个焊盘的三维位置是固定的,可以用游标卡尺量出来;2D坐标来自视觉检测,比如OpenCV的角点检测或者AI模型的landmark输出。两者配合,solvePnP才能算出稳定的姿态。
我在项目里用solvePnP做了一个应用场景:机械臂抓取前的目标定位。摄像头安装在一个固定位置,下面放一个带有ArUco标记的平面,先用solvePnP算出ArUco标记在相机坐标系下的位姿,再转换成机械臂需要执行的运动指令。实战中有一个经验值得分享:solvePnP对2D点的误差极其敏感,如果检测到的2D坐标有2到3个像素的随机抖动,计算出的姿态角度可能有几度的偏差。解决办法是使用solvePnPRansac替代普通版本,它能在有外点的情况下依然求出稳健解,代价是多花一点计算时间,在RK3588上完全可接受。
4. 嵌入式AI开发核心:把YOLOv8跑上RK3588
4.1 RKNN工具链与模型转换流程
有了屏幕和OpenCV的基础,终于到了项目最核心的部分——AI模型部署。RK3588的AI算力来自NPU,而NPU认识的模型格式不是PyTorch的pt文件、也不是ONNX,而是RKNN格式。所以整个部署流程可以概括为:训练好的PyTorch模型导出为ONNX,再用RKNN-Toolkit2在PC上转换成RKNN,最后在板端用RKNN Runtime加载推理。
听起来简单,实际操作中每一步都可能出问题。导出ONNX这一步,最容易翻车的是模型的动态尺寸和多输出解耦。YOLOv8的输出层包含三个不同尺度的检测头,如果导出时未正确指定输出节点名称,转换工具很可能认不出模型的输出。我的做法是:在PyTorch代码里显式定义输入的尺寸,导出时设置opset版本为12或更高(太低不支持某些算子),并且在转换前用ONNX Runtime先跑一遍,确认ONNX在CPU上能正常输出结果,再进入RKNN转换环节。
RKNN-Toolkit2的使用相对友好,核心逻辑就几行代码:加载ONNX模型、配置量化设置、生成RKNN文件。但这里有一个决定模型最终精度和速度的关键参数——量化。RK3588的NPU默认用INT8量化,从FP32到INT8会有一定的精度损失。我试过用官方提供的yolov8s.onnx直接转,默认的量化设置下mAP确实会掉一点;如果改用混合量化,只量化部分对精度敏感度低的层,精度会明显回升。这部分的调参空间很大,它和实际部署场景强相关,没有一套配置能吃遍所有模型。
重要提醒:转换RKNN时,输入图像的归一化方式必须和训练时一致。YOLOv8训练时用的是0-255直线归一化,然后除以255转换到0-1,RKNN转换时也要保持同样的preprocess配置。这个不一致是模型推理结果明显异常的最常见原因,我排查了大半天才发现问题不在于模型转换,而在于输入的预处理没有对齐。
4.2 NPU推理与OpenCV的衔接
模型转换完成后,板端开发的核心工作就是让OpenCV和RKNN Runtime协同工作。整个图像处理的pipeline是这样:摄像头采集的RAW图像送入OpenCV,经过resize、颜色空间转换、格式对齐等预处理,再喂给RKNN推理接口;推理返回的检测结果(类别、置信度、边界框坐标)回到OpenCV,由OpenCV负责画框、画标签、叠加UI,最终在LCD屏幕上显示出来。
格式对齐是最容易被忽视的细节。OpenCV默认处理的图像格式是BGR,而RKNN模型输入通常要求RGB,且是特定尺寸的张量,如果是NCHW布局还涉及维度的重新排列。如果你直接把OpenCV的Mat数据传给NPU,不做任何处理,轻则颜色通道错乱,重则内存访问越界导致程序崩溃。我习惯的做法是:从OpenCV Mat里取出数据指针,然后用np.transpose或cv2.cvtColor做显式的格式转换,最后再把buffer以字节流形式传给RKNN接口。这个流程你可以在PC上用同样尺寸的随机数据做一遍,提前发现维度问题。
性能调优也是这个环节的重头戏。RK3588的NPU支持零拷贝(zero copy)输入输出,即输入数据不需要在CPU和NPU之间做多余的拷贝。开启零拷贝之后,你需要在申请输入buffer时使用RKNN运行时提供的专用内存分配接口(如rknn_create_mem),而不是直接使用malloc。我实测开启零拷贝后,整个推理链路的端到端延迟能降低20%到30%,这个优化对于实时视频应用非常关键。如果你想榨干性能,还可以尝试把NPU推理和图像采集放到不同线程,用双缓冲机制把IO延迟隐藏掉,但线程同步和缓冲管理会增加不少代码复杂度,要根据项目阶段决定值不值得投入。
4.3 性能优化与功耗约束下的取舍
嵌入式AI开发里有句老话:能在PC上跑通不算本事,能在板子上跑得又快又稳才是本事。RK3588的NPU算力是6 TOPS,跑YOLOv8的nano或者small版本可以达到不错的帧率。但把帧率跑满并不意味着系统整体健康,你的CPU、内存带宽、热设计功耗都是共享的。如果NPU推理占满了,CPU要去做图像解码、显示合成、网络传输,每条通路都可能成为瓶颈。
我这次实测的配置是yolov8s模型,输入尺寸640x640,开启零拷贝和双线程流水线后,NPU单次推理耗时大约在40到60毫秒,折合下来每秒能跑15到20帧。看起来不高,但针对目标检测场景这个数据已经够用。如果你需要更高的帧率,有两条路径:一是换更小的模型,比如yolov8n,推理时间能砍一半;二是降低输入分辨率,从640降到416,精度会轻微下降,但帧率提升非常明显。我个人一般优先保模型输入尺寸,因为检测小目标本身对分辨率敏感,分辨率太低会漏检。
功耗和散热是另一个常被忽略的现实问题。RK3588在满载跑AI推理时,核心温度上升很快,如果散热没做好,会触发降频策略,性能表现剧烈波动。我给板子加了一个主动散热风扇之后,连续跑一小时的推理帧率曲线稳定非常多。部署到产品里时,还要考虑功耗墙上限的设定,比如把CPU的大核频率限制在一定范围,让NPU拿走大部分功耗预算。瑞芯微的SDK里提供了CPU调频接口和NPU的工作模式配置,这些参数建议在生产前做足压力测试再定。
5. 常见问题排查与避坑实录
5.1 LCD适配排查速查表
LCD问题占了整个项目调试时间的一大半,我把排查思路整理成一个速查表,照着查效率很高。
| 现象 | 可能原因 | 排查顺序 |
|---|---|---|
| 屏幕完全不亮,背光也没有 | 供电或GPIO配置错误,设备树节点没被加载 | 先看dmesg,确认dsi控制器和panel驱动是否匹配,再量电压 |
| 背光亮但无画面 | 面板初始化序列缺失或错误,时序参数不对 | 用示波器量MIPI lane是否有数据,检查display-timings |
| 画面花屏或条纹 | DSI时钟频率过高或过低,lane数不匹配 | 逐步降低clock-frequency,核对lane配置 |
| 显示颜色异常 | 色彩空间配置错误,RGB增益失配 | 用纯色测试画面逐通道排查,调整伽马配置 |
| 显示正常但闪烁 | PWM背光频率过低 | 提高PWM频率到1kHz以上 |
| 触摸和画面错位 | 触摸驱动和屏幕参数不一致 | 确认触摸控制器型号匹配,校准坐标映射 |
从这个表可以看到,屏幕问题的根源绝大多数集中在时序、时钟、GPIO和初始化序列这四个维度。我把dmesg的输出当成第一诊断工具,它通常能直接告诉你驱动是哪一步失败了。有一个经验值得分享:在看dmesg之前,先确认你的内核自带这个panel的驱动,而不是依赖外部动态加载的模块。内核内置demo驱动,调试效率会高很多,因为模块加载的顺序和依赖关系会引入额外的变量。
屏幕调试还有一个很实战的建议:准备一套"最小验证方案"。也就是先不跑复杂的桌面环境,直接用DRM的modetest命令显示一条彩条或者纯色画面,确认底层显示链路是通的。这个方案能把"内核驱动问题"和"用户态应用问题"迅速隔离开来。很多时候OpenCV显示黑屏,其实屏幕链路本身没问题,是应用层没有正确获取到drm设备或者framebuffer的属性。
5.2 OpenCV部署相关问题
OpenCV装好后跑起来,常见的问题集中在依赖库冲突、编译选项遗漏和运行时报错三个层面。
依赖库冲突最常见的表现是"undefined symbol"错误,尤其是同时装了系统版OpenCV和源码编译版OpenCV的时候。两个版本的库同时存在于系统路径,运行时链接器加载了错误版本,就会报出各种找不到符号的问题。解决方法是维持单一来源:要么全用apt版,要么全用源码版,不要混装。如果必须共存,用LD_LIBRARY_PATH或者显式指定rpath来控制加载顺序。
编译选项遗漏的问题主要体现在功能缺失上。比如编译时没开WITH_GSTREAMER,那么OpenCV读不了RTSP视频流;没开WITH_GTK或者WITH_QT,imshow就无法弹窗显示。RK3588通常跑的是Ubuntu或者Debian类系统,GStreamer是多媒体处理的标配,建议无论如何都要编进OpenCV。如果你要接USB摄像头,还要确认V4L2选项是开启的,这是Linux下访问摄像头的基础路径。
运行时报错方面,最常见的还是"could not find a writer for the specified extension",也就是编码器没装好。图像保存需要libjpeg、libpng这些库,视频保存则依赖ffmpeg或者GStreamer后端。如果你的OpenCV编译时没有链接到这些库,保存功能就会静默失败或者直接抛异常。确认方法很简单,运行cv2.getBuildInformation(),编译器会把当前库的特征信息完整打印出来,重点看Video I/O和ImgCodecs两段。
5.3 模型部署与推理实例的坑
模型部署和推理阶段的坑,与前面LCD、OpenCV的问题性质完全不同,更难排查,因为错误逻辑往往不在你写的代码里,而在转换工具的"黑盒"逻辑中。
最典型的问题是rknn-toolkit2在PC上转换时报"op not supported"或者"unknown layer"。出现这个问题的原因通常有两个:一是ONNX模型里有RKNN工具链不支持的算子;二是算子版本的兼容性出了问题。排查思路是逐层分析,先把模型结构可视化(netron工具很方便),对照RKNN文档确认哪些层不支持。YOLOv8的某些特殊结构(比如SiLU激活函数在旧版本工具链中的支持情况)就曾经在转换时出过问题,后面通过升级工具链版本解决了。所以我的建议是:遇到转换报错,先升级到最新的RKNN-Toolkit2版本,不要花太多时间手动修改模型结构。
推理结果完全不对,但程序没有报错,这是最令人头疼的情况。常见原因有三个:输入数据预处理不对(尺寸、颜色通道、归一化方式)、输出后处理解析错误(把不同检测头的输出维度搞混)、以及量化精度损失过大。排查方法也是分步走:先用一张固定图片在PC上用ONNX Runtime跑出FP32的参考结果,再在RK3588上跑RKNN推理,对比两者的输出张量。通常在哪个环节出现差异,问题就在哪个环节。这相当于给你的模型推理做了一次差分测试,是唯一靠谱的定位方法。
还有一个推理现场的稳定性问题:内存泄漏。嵌入式设备长时间运行,内存泄漏会慢慢吃掉可用内存,最终导致malloc失败或者进程被OOM killer杀掉。RKNN Runtime的输入输出buffer如果不正确释放,很容易产生累积性泄漏。我写了一个简单的监控脚本,统计进程的RES内存和板子的可用内存,让设备连续跑48小时以上,通过内存曲线来判断是否存在泄漏。这个问题在一两次的单次运行中完全看不出来,但产品上市之后就会变成致命事故。
6. 一些只有踩过坑才有的经验
最后分享几个这次项目里沉淀下来的习惯,不一定能在任何教程里找到。
第一,在RK3588上做开发,一定要养成分层验证的习惯。屏幕单独验证、摄像头单独验证、OpenCV单独验证、模型推理单独验证,每一层都通了再往上层叠。这个习惯让我省下了大量回头排查的时间,因为问题一旦出现,就能立刻锁定在哪一层。
第二,把板端的运行环境做成标准化的脚本。我从系统依赖、OpenCV编译参数、RKNN Runtime安装,到模型文件路径、运行参数全部固化成脚本,换一块新板子可以在半个小时左右恢复全部环境。一开始觉得写脚本麻烦,后来开发了二块三块板子之后才发现,这套脚本节约的时间远大于书写成本。
第三,尽量在开发阶段多记录log。嵌入式设备不像PC那样能随时打断点,靠的就是足够详细的日志输出。我在关键路径上加上了耗时统计、内存统计、帧率统计,程序异常的时候,看日志比看代码更快定位。特别是NPU推理延迟这类硬性指标,要把历史数据保存下来,用来判断性能是否发生回退。
RK3588的LCD显示、OpenCV图像处理、NPU模型推理这条链路,至今仍然是嵌入式AI开发里覆盖面很广、同时坑也很多的组合方向。如果你正在做类似的项目,希望这篇分享能帮你少走一些弯路。按照上面的步骤来,多数问题都能在一两个小时内找到方向。剩下的,就看你的具体应用场景了。