Skip to content

RK3588 高性能相机流水线:从 V4L2 到算法消费者

在 RK3588 上做多路相机,性能问题通常不是某一个算子太慢,而是整条链路反复改变数据形态:采集一次、复制到共享内存、转成 BGR、上传 GPU、读回 CPU,再经 ROS 序列化发给算法。单看每一段都能运行,叠起来却会消耗大量 CPU、内存带宽和延迟预算。

目标主链

更清晰的生产主链可以收敛为:

text
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,例如:

cpp
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_seqtrigger_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,看起来更“稳”,实际会让系统进入一种功能还在、性能合同已经失效的状态。对于固定硬件的生产系统,更可控的策略是:

  1. 启动时检查能力与配置;
  2. 主链初始化失败就明确失败;
  3. health 记录错误原因;
  4. 由 systemd 或上层 supervisor 重启;
  5. legacy CPU 路径只保留为诊断工具,不作为生产自动回退。

这样,故障会以可观测的方式暴露,而不是在负载高峰变成随机掉帧。

验收清单

  • V4L2 设备只有一个 owner;
  • buffer pool 数量固定,生命周期可追踪;
  • RGA、OpenCL、NPU、MPP 的输入输出格式明确;
  • sequence、trigger、timestamp 随帧传递;
  • 消费者超时不会导致生产者复用仍在使用的 buffer;
  • GPU/RGA 的真实忙闲增量可观测;
  • 主链失败会明确报错,不会悄悄切到另一条性能不可控的路径。

高性能相机系统的核心不是堆更多加速单元,而是建立一条所有权清晰、数据形态稳定、失败可见的主链。