ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

FPGA+RK3588+NPU三核协同:超高清图像预处理与AI推理实战

FPGA+RK3588+NPU三核协同:超高清图像预处理与AI推理实战 1. 三块“芯”的分工逻辑为什么这么组队接到这个需求的第一反应其实不是“选哪颗芯片”而是“单靠 RK3588 到底行不行”。RK3588 这颗处理器本身已经很强8 核 CPU、Mali-G610 GPU、6 TOPS 算力的 NPU还有双 MIPI CSI 口和 8K 解码能力。但一遇到超高清图像处理里的“原始数据入口”问题就暴露出来了。工业相机、医疗内镜、车载前视这类场景输出往往是 8K30 甚至 4K120 的 RAW Bayer 流一秒钟就是几十 Gbps 的数据量。如果把这些原始数据直接灌给 RK3588内存带宽和 ISP 都会被瞬间打满NPU 还没有看到图系统已经先卡死了。所以这个项目最终采用了“FPGA 做入口预处理 RK3588 做调度与 AI 推理 NPU 做核心分析”的三核协同架构FPGA 挡在第一道数据洪流前面RK3588 只负责处理已经梳洗过的“干净数据”AI 则跑在最适合它的算力单元上。1.1 单芯方案的真实瓶颈数据流而不是算力很多人会先入为主地认为视频分析的瓶颈是“算力不足”但实际做下来会发现最大的问题是数据流。以 8K30 的 12-bit RAW 为例理论带宽是 7680×4320×30×12 bit换算过来大约是 1.49 GB/s这还不考虑行场消隐、协议开销和格式转换时的额外带宽。RK3588 的内存带宽虽然不低但 CPU、GPU、NPU、VPU 全在一起抢带宽一旦 RAW 数据直接进内存很快就会遇到延迟抖动和帧率不稳。FPGA 在这里解决的问题不是替代 ISP而是给 RK3588 减负。它在最前端完成去马赛克、降噪、坏点校正、自动白平衡、裁剪缩放这些“脏活累活”最终输出一路标准的 YUV 或 RGB 视频给 RK3588。这样 RK3588 的 ISP 和 VPU 就不需要处理 Bayer 插值RGA 也不需要做超大尺寸的缩放整条链路从入口就干净了。1.2 三颗算力单元各自的“最舒服区间”我在这类方案里喜欢画一张分工表把每一级的任务边界定死避免出现“谁都能干、谁都干不彻底”的情况。单元擅长的工作不擅长的工作对应本项目的任务FPGA低延迟像素级流水线、位宽可控、时序并行复杂算法迭代、AI 模型灵活性RAW 输入、去马赛克、降噪、缩放、MIPI 输出RK3588 CPU控制流、调度、协议栈、管理高强度重复计算驱动控制、任务分发、结果后处理、显示输出RK3588 NPU/GPU大数据量矩阵运算、CNN 推理高吞吐像素级预处理YOLOv8 目标检测、图像分类、分割等 AI 任务FPGA 属于“专用加速器”它的优势是确定性和并行性。RK3588 的 NPU 则是“可编程加速器”它跑模型比 FPGA 灵活得多哪怕模型从 YOLOv8 换成另一个结构也只是转换一次 ONNX 的问题而 FPGA 如果要改网络结构通常意味着重新做综合布线。把这两者的边界划清楚整个项目的节奏就会顺很多。2. 从 FPGA 到 RK3588 的图像通路MIPI、设备树与驱动调试硬件连通是整个项目的第一个关键节点。FPGA 与 RK3588 之间最常见的视频接口不是 PCIe而是 MIPI CSI-2。原因很实际RK3588 原生支持 MIPI CSILinux 下有完整的 V4L2 驱动生态而且 MIPI 数据流简单延迟低特别适合 FPGA 这种“主动吐数据”的角色。2.1 MIPI 链路的设计参数与板级连接RK3588 的 MIPI CSI 接口支持 D-PHY数据通路可以灵活配置为双 4-lane 或单 8-lane。以我做的 4K60 预处理方案为例用的是 4-lane MIPI每条 lane 跑 1.5 Gbps总带宽 6 Gbps足够承载 4K60 的 YUV422 数据流。如果要用 8K30一条 4-lane 链路就不够了通常会把 FPGA 输出的数据切分为两条 MIPI 流分别送到 RK3588 的两个 CSI 口然后在驱动层做拼接。这里最容易踩的坑是两个口的时钟同步问题FPGA 必须保证两路 MIPI 的像素时钟来自同一个 PLL否则 RK3588 收到两路视频后会因为行场相位不一致而出现画面撕裂。实际连线时FPGA 要复用一个虚拟 sensor 的角色也就是把 FPGA 的 MIPI TX 接口接到 RK3588 的 MIPI RX并通过 I2C 提供模拟的 sensor 寄存器读写能力。RK3588 端的驱动并不会关心对面是真正的摄像头还是 FPGA它只认 I2C 地址和寄存器的交互行为。这样做的最大好处是我们不用改动内核里面复杂的 sensor 驱动框架只需要写一个“虚拟 sensor 驱动”把 FPGA 的命令通道映射成普通的寄存器读写即可。2.2 设备树与 v4l2 子设备拓扑的调试经验设备树这一层的坑往往比驱动本身多。RK3588 的摄像头通路一般会抽象成“Sensor - MIPI DPHY - CSI2 Host - ISP/Receiver”几层每一层都要在设备树里正确连接。我调试时通常先用下面几步把链路拉通先用media-ctl -d /dev/media0 -p查看当前 media 拓扑确认 FPGA 虚拟 sensor 节点是否出现在链路里。再用v4l2-ctl --list-devices查看对应的 video 节点是否创建成功。接着做一次抓帧测试用v4l2-ctl --stream-mmap --stream-count1 --stream-to/tmp/frame.yuv抓一帧 YUV 数据确认数据真的进来了。如果抓不到帧优先怀疑寄存器地址是否匹配其次是 MIPI lane 数和时钟频率是否与 FPGA 实际输出一致。RK3588 的 DPHY 驱动里一般有 lane 数配置FPGA 端如果用了 4-lane而设备树里配置成 2-lane驱动不会立刻报错但抓帧一定是黑屏或者花屏。这种问题非常隐蔽因为 log 看起来一切正常数据通路却没有信号。我建议在 FPGA 端保留一个调试用的测试图生成模块平时可以切到彩条测试图这样排查起来能快速确定是链路问题还是后续 ISP 处理问题。根文件系统层面如果是用 RK3588 板卡调试有时候不方便接显示器我一般直接用 adb 连接板子既可以用adb push/pull传文件也可以进 shell 看内核日志。这个习惯在项目前期帮了大忙因为 FPGA 和 RK3588 之间的握手过程经常要反复改固件开着 adb 能省去来回插拔 SD 卡的麻烦。3. FPGA 内部的实际工程去马赛克、定点数与稳定运行细节三核协同里FPGA 是唯一需要写 RTL 的部分也是整个项目里工程量最大、最不可控的一环。很多人对 FPGA 图像处理有个误解觉得就是流水线堆数据写完就能跑。实际上像素级的定点数设计、复位处理和时钟域交叉才是真正决定项目成败的地方。3.1 ISP 预处理流水线从 RAW 到 YUV 的取舍典型的 FPGA ISP 流水线大概是这样黑电平校正把 sensor 的暗电流偏移减掉坏点校正用邻域像素替代异常点去马赛克把 Bayer 格式插值成 RGB降噪做一定程度的空域滤波白平衡和色彩校正矩阵Gamma 校正得到 8-bit 数据缩放和裁切输出到 MIPI TX。这里必须做一个所谓“功能取舍”不是所有步骤都要放在 FPGA 里。比如自动曝光、自动白平衡的统计计算我之前在 FPGA 里做了不少统计模块后来发现不如把统计值通过中断上报给 CPU由 RK3588 的算力来完成FPGA 只负责执行最终参数。因为 RK3588 的 Linux 里跑算法太方便了没必要用 RTL 去折腾复杂的迭代逻辑而且 FPGA 里的动态迭代一旦 bug 就要重新综合布线调试周期太长。3.2 定点数位宽选型精度、资源与数值溢出的平衡FPGA 没有浮点单元所有系数和像素都必须转成定点数。这里最容易出问题的就是位宽选择。以去马赛克后的 RGB 数据为例如果 sensor 输出的是 12-bit RAW经过增益和矩阵乘法之后中间数据位宽需要预留足够的余量不然会把高光细节削掉。我常用的做法是12-bit 输入黑电平校正后保持 12-bit增益乘法用 Q4.12 格式也就是 4 位整数位、12 位小数位系数范围从 0 到 15.999精度 1/4096这个精度对增益调校完全够用去马赛克后的 RGB 各自扩到 14-bit给重叠的加法运算留出两个 bit 的余量色彩校正矩阵用 Q0.16 或 Q1.15 格式输出前做截断和饱和处理最后的 Gamma 查表输出 8-bit完成位宽收敛。这里需要特别提醒的是“截断和饱和”。如果用简单的截断而不做饱和任何超过 8-bit 的数都会直接回卷成一个小数画面上会出现严重的异常伪彩。正确做法是先判断是否超出最大值超出则强制设为最大值低于最小值则设为 0这段代码在 RTL 里只占几行但很多初学者会漏掉。3.3 复位亚稳态与时钟管理FPGA 稳定性最容易忽略的部分复位信号亚稳态是 FPGA 温控、逻辑稳定之外最容易被低估的问题。你从热词里也能看到不少人搜“fpga 复位信号亚稳态”这说明实际项目中确实经常翻车。FPGA 的复位信号如果来自板载按键、I2C 配置芯片或者外部设备往往没有和 FPGA 内部时钟同步。当复位信号的有效沿刚好落在时钟沿附近时触发器输出端就会出现亚稳态导致部分寄存器复位成功、部分没有复位成功逻辑行为变得无法复现。我现在的做法一律是“异步复位、同步释放”也就是先把外部复位源经过两级 D 触发器打拍消除亚稳态之后再作为内部全局复位使用。打完拍之后的复位信号再去驱动各个模块的复位端口。这样既保留了异步复位的快速响应又能保证所有模块在同一时钟周期内完成复位。时钟方面FPGA 端通常有多个时钟域比如 sensor 输入像素时钟、滤波模块工作时钟、MIPI TX 串行时钟。只要跨时钟域就必须用异步 FIFO 来转接数据。这个环节我见过太多“偶尔卡一帧”的怪象最后查下来都是直接拿一个时钟域的使能信号去采另一个时钟域的数据。换句话说异步 FIFO 的钱不能省。3.4 功耗与散热温控风扇不能靠感觉FPGA 跑 8K 图像流水线时发热量比想象中大得多。尤其是大规模去马赛克和降噪滤波器内部翻转率很高片上温度轻松超过 70 摄氏度。原厂的开发板一般只做散热片被动散热在持续满负荷下压不住必须上主动风扇。我之前有一版设计直接给 FPGA 散热片上贴了风扇但风扇接的是常电转速恒定低温时噪声大高温时又不一定够。后来改成了温度反馈控制利用 FPGA 内部的 XADC 或者板上的温度传感器读取 die 温度后输出 PWM 控制风扇转速。这个控制逻辑用状态机或者简单的 PID 都能实现核心结论是把温控阈值设成两档低于 60 度跑 30% 转速超过 70 度直接拉满中间用线性插值过渡。这套机制在长时间运行测试里非常管用温度能稳定在 85 度以内而风扇噪声也不是特别明显。关键是不要在 FPGA 逻辑里引入大延迟温度采样周期有个 100ms 左右就够了毕竟热惯性很小。4. RK3588 侧的软件基础Ubuntu 移植、MPP/RGA 和 VPU 管控RK3588 端的系统搭建是整个项目里“资料最杂、坑也最多”的一环。热门词里能看到一堆人在搜“rk3588 移植 ubuntu 26”“rk3588 ubuntu”“ubuntu rockchip 社区项目”说明大家都是在 Linux 环境下做开发但真正把系统跑稳、把多媒体链路调通的人并不多。4.1 Ubuntu 移植过程中的分区与打包问题从官方 SDK 编译得到的内核和根文件系统通常需要自己整合进启动镜像里。移植 Ubuntu 时我建议直接基于 Rockchip 官方维护的 Linux SDK而不是从网上下载一个别人做好的镜像来魔改因为不同板卡的设备树、DDR 配置和 PMIC 驱动差异太大拿到手往往不知道里面改了什么。分区设计上现在主流 .img 打包都采用 A/B 分区方案也就是把根文件系统复制两份启动时挂在当前 active 分区升级时写入 inactive 分区最后切换引导标志位。这种方案的优势是升级失败还能回滚适合需要长期运行的工业设备。但 A/B 分区也意味着根文件系统的空间会翻倍如果你的 eMMC 只有 32GB那么一个根分区可能只有 12GB 左右跑大模型或者装很多依赖就得精打细算。打包流程我自己习惯写成脚本每次改完内核或根文件系统就自动执行省去手工操作。重点是把 boot.img、dtb、rootfs.img 之间的版本匹配关系固定下来。很多时候 RK3588 起不来并不是内核有问题而是 DTB 和内核版本不匹配或者是 uboot 里的 fdt 地址没有对齐。移植最烦的不是编译而是这些“看运气”的启动细节。4.2 MPP 和 RGA视频编解码与格式转换的硬件加速数据从 FPGA 到 RK3588 之后还要经过一道或多道格式转换。RK3588 的 VPU 由一个强力的 MPP多媒体处理平台驱动管理负责视频编解码。也就是说解码好一帧 4K 视频后需要转成 YUV420SP 并缩放到 640×640 再送给 NPU这些活如果直接用 CPU不但慢还会拖垮整个系统。我的经验是格式转换、旋转、镜像、缩放全部交给 RGA不要自己写循环。RGA 是 Rockchip 的 2D 图形加速硬件RK3588 上的 RGA 性能足够做 4K 到 1080p 的实时缩放而且调用方式非常简单构造一个rga_info结构体传入 src 和 dst 的 fd 与宽高再调用ioctl就能完成。比起用自己的 C 代码在 CPU 上像素级搬运速度快了几个数量级。MPP 则主要用于视频流编码。如果整个系统要对处理后的画面做本地存储或者推流就用 MPP 的 VENC 接口做 H.264/H.265 编码。使用 VENC 时要注意它的帧缓冲必须是从mpp_buffer_group_get_internal获取的内存直接 malloc 出来的内存会导致编码器无法访问。5. AI 推理环节怎么提速YOLOv8 在 RKNN 上的部署与流水线三核协同的最终目的是把 AI 分析引擎跑得又快又稳。RK3588 的 NPU 支持 RKNN 格式模型我们最常见的部署流程就是把训练好的 YOLOv8 导出为 ONNX再通过 RKNN-Toolkit2 转换成 RKNN 格式。这个过程非常成熟真正决定性能的是如何设计流水线而不是如何转换模型。5.1 模型转换的注意事项先把训练好的 PyTorch 模型导出为 ONNX导出时要注意两个点固定输入尺寸YOLOv8 默认用了多尺度训练导出时固定为 640×640 或 1280×1280否则 RKNN 转换器可能会因为动态维度而报错确定输出节点名方便后面解析模型输出。然后使用 RKNN-Toolkit2 加载 ONNX配置量化数据集。RK3588 的 NPU 对 INT8 量化非常擅长如果不做量化模型就跑不了因为 NPU 本身是以 INT8 为主要数据类型的。量化数据集最好从实际采集的图像里抽取不要用网上下载的通用数据集。我见过不少项目在量化时用了 ImageNet 的图片结果部署到自己的工业场景后检测精度掉得一塌糊涂重新用现场数据量化就好了。5.2 三核协同下的推理流水线从 MIPI 到结果的几个并行阶段整个系统的实时性取决于流水线设计。数据链路可以简单地分成四段FPGA 采集并预处理图像RK3588 的 MIPI 驱动把帧送到内存RGA 把帧缩放并转换成 NPU 需要的格式NPU 跑模型把结果交给 CPU 处理业务逻辑。如果把这四段串行执行帧率会很低。更好的做法是用三缓冲或四缓冲机制让 MIPI 的写指针、RGA 的处理指针和 NPU 的读指针各自向前滚动。也就是说FPGA 在输出第 N2 帧时RGA 正在处理第 N1 帧而 NPU 刚好在推理第 N 帧。这样的流水线可以把端到端延迟控制在 2 帧以内吞吐量却能接近单级处理的最快速度。5.3 RKNN 接口的实际调用方式在代码层面RKNN 的推理接口主要就几个rknn_init创建上下文rknn_query查询输入或输出属性rknn_inputs_set设置输入数据rknn_run执行推理rknn_outputs_get获取输出结果。实际使用时要特别注意输入的buf必须是 NPU 可访问的内存。最简单的方式是先把 RGA 处理完的 buffer 映射到连续物理内存再传给rknn_inputs_set。如果直接用普通malloc的内存NPU 读写时会因为缺页而出现间歇性卡顿帧率不稳定排查起来还特别恼火。我把推理结果做后处理时通常是在 CPU 线程里进行比如解析 YOLOv8 输出的 84 维张量4 个框坐标 80 个类别做 NMS 去掉重叠框再对检测结果做业务上的过滤。70 00 多个候选框里挑出几十个目标的 NMS 计算量并不大CPU 完全可以轻松处理。6. 实测中绕不开的坑和我的经验尺度最后这部分聊几个真正值得反复提醒的工程细节。这些细节如果没人告诉你你可能要花一两周才能定位到根因。6.1 帧同步与 FIFO 溢出为什么偶尔会丢帧FPGA 输出 MIPI 流是“只管发送”的如果 RK3588 端没有及时消费前一帧FPGA 里面的 FIFO 就会溢出。最直接的表现是画面每隔几秒卡顿一帧CPU 使用率不高内存也充足但就是掉帧。解决办法是两个方向同时做。一是让 FPGA 支持背压也就是通过 I2C 寄存器通知 RK3588 端当前 FIFO 水位如果水位过高就跳帧二是把 RK3588 端的 V4L2 queue 设置成足够深的缓冲区我一般设 8 个 buffer比默认的 4 个稳妥得多。6.2 不要在调试阶段过分相信“看起来正常的画面”去马赛克和降噪这类算法模块在测试图模式下看起来毫无问题一换真实场景就会出现摩尔纹、伪彩或者边缘锯齿。原因是测试图是静态的、频率单一而真实场景的纹理和噪点非常复杂。我的建议是准备三组测试素材一是标准彩条图验证链路时序二是高细节图像验证去马赛克和锐化效果三是真实动态视频验证整体流水线的实时性和稳定性。三组都通过才敢提交给后端的 AI 团队。6.3 板卡和固件版本要保持“快照”这个项目里最影响效率的是不同版本的板卡硬件、Ubuntu 根文件系统、RKNN Toolkit、FPGA bitfile 之间的组合太多了经常因为版本不一致导致问题无法复现。我后来给项目建了一个“版本基线表”把 FPGA 工程的 git commit ID、RKNN Toolkit 版本、Ubuntu 内核版本、分区表版本全部打成一个字段串每次测试记录都写上这个字段串。排查问题时可以快速确定是哪一层发生了变化而不是重新从设备树开始查。7. 三核协同方案的扩展方向与我的维护心得这个架构不只是一个项目的“一次性拼装”它在一定程度上可以复用到很多类似需求上。如果你已经解决了 FPGA 到 RK3588 的 MIPI 通路后面换不同型号的 sensor、换不同的 AI 模型改动量其实都集中在比较小的范围内。7.1 从图像检测扩展到视频结构化分析当 FPGA 的预处理管线稳定之后可以把 AI 分析从简单的目标检测扩展到视频结构化。比如在 FPGA 端划分兴趣区域只在 ROI 内做去马赛克和裁剪输出这样 RGA 和 NPU 的计算量都能进一步降低。如果业务需要识别人脸、车牌或者其他目标RK3588 的 NPU 可以同时挂载多个模型只要在调度层做好分时或分核分配即可。7.2 让我最有收获的一点维护经验如果要在经验层面只提炼一句话我会说三核协同的项目最大的风险不在某一颗芯片上而在“级间握手”上。MIPI 时序、中断上报、buffer 管理、NPU 输入内存这些跨模块的接口才是问题的重灾区。我在后续维护时会把大量精力放在把每个级间的握手协议先用简单版本跑通再逐步完善功能而不是一上来就并行开发 FPGA 全功能管线和 RKNN 推理流水线否则一旦出问题定位周期会非常长。另外我强烈建议在 FPGA 端保留一个彩条测试模式在 RK3588 端保留一个纯 CPU 的 YUV 转 JPEG 打印工具。这样在系统联调时所有团队都可以快速判断“这帧数据到底是格式错了、内容错了还是颜色错了”用最短链路把问题定位到具体的层级。这套方法论看着简单但在超高清图像处理加 AI 分析的项目里真的能省下大把时间。
返回列表