RK3588 高性能相机流水线:从 V4L2 到算法消费者
在 RK3588 上做多路相机,性能问题通常不是某一个算子太慢,而是整条链路反复改变数据形态:采集一次、复制到共享内存、转成 BGR、上传 GPU、读回 CPU,再经 ROS 序列化发给算法。单看每一段都能运行,叠起来却会消耗大量 CPU、内存带宽和延迟预算。
目标主链
更清晰的生产主链可以收敛为:
V4L2 DMA-BUF capture
↓
RGA:旋转 / 缩放 / 必要的格式变换
↓
OpenCL:鱼眼矫正 / 拼接 / GPU 前处理
↓
Frame Broker:按 sequence 发布句柄与元数据
├── VINS
├── RKNN / 视觉算法
└── MPP / 编码推流这里最重要的不是把所有模块都换成硬件加速,而是让同一份帧数据沿主链向下流动。
1. 只保留一个 Camera Owner
相机设备应该只有一个进程负责打开、配置、排队和回收 buffer。其他模块通过 broker 消费帧,不再各自打开 V4L2 或维护另一套采集逻辑。
单一 owner 可以统一处理:
- 设备状态与重连;
- 固定 DMA-BUF 池;
- 多相机 bundle、trigger 与 sequence;
- buffer 的借用和回收;
- 运行时健康状态。
一旦多个服务都能成为采集 owner,设备占用、重复 buffer pool 和重启顺序就会变得难以推理。
2. 同步属于采集合同,不属于 ROS topic
多相机同步信息应和帧一起进入 typed metadata,例如:
struct FrameMeta {
uint64_t bundle_seq;
uint64_t trigger_seq;
uint64_t capture_timestamp_ns;
uint32_t width;
uint32_t height;
uint32_t stride;
PixelFormat format;
};消费者按 bundle_seq 或 trigger_seq 判断同组帧,而不是依赖进程内计数器或可能被中间件改写的通用消息序号。
3. RGA 与 OpenCL 各做擅长的工作
RGA 适合规则明确的二维操作,如 resize、rotate、crop 和部分格式转换。OpenCL 更适合鱼眼 remap、查表和并行像素变换。MPP 则留给编解码。
合理的职责划分能避免为了“GPU 化”把简单缩放也绕进复杂 kernel,也避免把 remap 长期留在 CPU cv::remap 上。
4. Broker 传递的是能力,不是像素副本
Broker 应发送 DMA-BUF fd、几何信息、时间戳和 sequence,而不是重新序列化整张图像。进程间传递可用 Unix Domain Socket + SCM_RIGHTS,进程内消费者则直接持有 frame handle。
但 fd 传递只是接口的一半,另一半是 buffer ownership。生产实现必须定义消费者何时完成、生产者何时可以复用 buffer,不能把“fd 已发送”当作“帧已安全交付”。
5. 生产链路不做静默 runtime fallback
从 GPU 路径静默回退到 CPU,看起来更“稳”,实际会让系统进入一种功能还在、性能合同已经失效的状态。对于固定硬件的生产系统,更可控的策略是:
- 启动时检查能力与配置;
- 主链初始化失败就明确失败;
- health 记录错误原因;
- 由 systemd 或上层 supervisor 重启;
- legacy CPU 路径只保留为诊断工具,不作为生产自动回退。
这样,故障会以可观测的方式暴露,而不是在负载高峰变成随机掉帧。
验收清单
- V4L2 设备只有一个 owner;
- buffer pool 数量固定,生命周期可追踪;
- RGA、OpenCL、NPU、MPP 的输入输出格式明确;
- sequence、trigger、timestamp 随帧传递;
- 消费者超时不会导致生产者复用仍在使用的 buffer;
- GPU/RGA 的真实忙闲增量可观测;
- 主链失败会明确报错,不会悄悄切到另一条性能不可控的路径。
高性能相机系统的核心不是堆更多加速单元,而是建立一条所有权清晰、数据形态稳定、失败可见的主链。