Skip to content

VINS 直连灰度 DMA-BUF:为什么不再走 ROS 图像 Topic

VINS 的视觉前端最终需要的是灰度图。如果相机流水线已经产出 NV12,Y 平面本身就是灰度数据。继续把它转成 BGR、封装成 ROS Image、跨进程复制,再由 VINS 转回灰度,是一条成本很高的绕路。

原来的数据路径

text
Camera buffer
  → BGR materialization
  → ROS Image serialization
  → ROS transport / copy
  → cv_bridge
  → grayscale conversion
  → VINS frontend

这条路径的优点是接口通用、调试方便,但在多目、固定频率的板端系统里,CPU 和内存带宽会被格式转换与复制持续占用。

目标路径

text
Camera Pipeline
  → NV12 DMA-BUF atlas
  → 取 Y plane 的 geometry + fd
  → Unix SOCK_SEQPACKET + SCM_RIGHTS
  → VINS mmap 为 CV_8UC1
  → feature tracking

图像不再通过 ROS topic 进入 VINS,但这不意味着整个 VINS 都脱离 ROS:

  • IMU 仍然通过 ROS 输入;
  • odometry 与状态仍然通过 ROS 输出;
  • 可选的调试图像 topic 可以继续保留;
  • 只把高带宽、可零拷贝的图像主链迁出 ROS。

为什么使用 atlas

多目系统可以把同侧多个方向的图像纵向放在一张 atlas 中。VINS 根据 ROI 的 Y 区间还原 camera id 和局部坐标,不必为每个方向单独物化一张新图。

text
left atlas             right atlas
┌──────────┐           ┌──────────┐
│ camera 0 │           │ camera 1 │
├──────────┤           ├──────────┤
│ camera 2 │           │ camera 3 │
├──────────┤           ├──────────┤
│ camera 4 │           │ camera 5 │
├──────────┤           ├──────────┤
│ camera 6 │           │ camera 7 │
└──────────┘           └──────────┘

四个方向仍共享同一个机体状态。每个相机通过标定外参从 T_world_body 得到自己的 pose,各方向的视觉 residual、IMU 因子和边缘化先验共同进入同一个滑窗优化,而不是四套独立 VINS 最后再拼接。

跨进程 packet 至少要带什么

cpp
struct StereoGrayPacketV1 {
    uint32_t magic;
    uint16_t version;
    uint64_t sequence;
    uint64_t left_timestamp_ns;
    uint64_t right_timestamp_ns;
    PlaneDesc left;
    PlaneDesc right;
};

struct PlaneDesc {
    uint32_t width;
    uint32_t height;
    uint32_t stride;
    uint32_t offset;
    uint32_t size;
};

接收端要验证 magic、版本、sequence、左右时间差、geometry、stride、offset 与溢出边界。对于 fd,还要在 mmap 前用 fstat 或等价方式确认真实容量足够覆盖 offset + stride × height

功能跑通不等于生命周期安全

如果发送端在 SCM_RIGHTS 发送 fd 后立即归还 buffer,慢消费者可能读到已经被相机重新写入的内容。表面上 sequence 正常、频率正常,像素却可能属于下一帧,甚至出现撕裂。

因此,完整迁移必须补上 ACK/lease retention 或等价的回收协议。减少复制是性能优化,定义所有权才是生产化。

验收要点

  • VINS 不再订阅左右 ROS 图像 topic;
  • IMU 输入与 odometry 输出仍保持兼容;
  • fd、geometry、stride 与 capacity 全部校验;
  • 断开与重连后 sequence 连续性可解释;
  • 慢消费者不会读到已复用 buffer;
  • 性能收益来自板端 A/B,而不是只看代码路径。

这类迁移的价值不只在于“绕开 ROS”,而是让高带宽图像数据遵循硬件原生内存路径,同时把 ROS 留在它更擅长的控制、状态和低带宽消息层。