2. 自动驾驶通信中间件概览
自动驾驶系统本质上是一台“移动的实时分布式计算机”:感知、定位、预测、规划、控制等模块以不同频率持续产生和消费数据,激光雷达点云单帧可达数 MB,摄像头图像、毫米波雷达目标列表、组合导航位姿、底盘反馈等数据在毫秒级时间窗内必须完成端到端流转。通信中间件正是承载这些数据流转的“神经系统”。Cyber RT 是百度 Apollo 面向自动驾驶场景自研的通信中间件,它并不只是一个消息总线,而是一套包含调度、通信、组件模型与服务发现的运行时框架。
一、Cyber RT 的定位:介于操作系统与算法模块之间的实时运行时
从系统分层看,Cyber RT 位于操作系统与自动驾驶算法模块之间。向下,它依赖 Linux 的线程、共享内存、epoll、CPU 亲和性等能力;向上,它为感知、规划等模块提供统一的 Component 编程模型、Channel 通信抽象和参数/服务接口。其核心定位可以概括为三点:
- 确定性调度:通过协程(Coroutine)与调度器(Scheduler)把不同频率的任务编排到固定 CPU 核上,降低抖动。
- 高效通信:同一主机内优先走共享内存,跨主机走 RTPS/DDS 等网络通道,实现“零拷贝”或“少拷贝”传输。
- 模块解耦:算法开发者只关心“订阅什么、发布什么”,无需关心底层是共享内存还是网络。
二、架构分层与模块组成
Cyber RT 自上而下大致可分为四层,每层职责清晰:
| 层次 | 主要模块 | 核心职责 |
|---|---|---|
| 应用层 | Component、TimerComponent、消息定义(.proto) | 算法以组件形式注册,声明发布/订阅通道 |
| 通信层 | Node、Channel、Reader/Writer、Transport | 提供发布订阅语义,屏蔽底层传输差异 |
| 传输层 | Shared Memory、RTPS、Intra-process | 按同机/跨机、进程内/进程外选择最优路径 |
| 运行时层 | Scheduler、Coroutine、Task、CRoutine | 任务编排、协程调度、CPU 亲和性绑定 |
| 基础层 | Parameter、Service、Logger、Time、Record | 参数服务、日志、时间同步与数据回放 |
其中,Transport(传输层)是性能关键路径。Cyber RT 会根据通信双方的拓扑关系自动选择传输方式:同一进程内直接指针传递;同一主机不同进程优先使用共享内存;跨主机则回退到 RTPS 网络传输。这种“自适应传输”是 Cyber RT 区别于普通消息队列的重要特征。
三、共享内存通信在 Cyber RT 中的角色
共享内存是 Cyber RT 实现零拷贝的核心机制。其基本思路是:发送方把消息序列化写入一块由多个进程共同映射的共享内存段,接收方通过同一块内存直接读取,从而避免“用户态→内核态→用户态”的多次数据搬运。Cyber RT 的共享内存传输通常包含以下关键对象:
- Segment(内存段):由 shm 创建的大块共享内存区域,按固定大小分块管理。
- Block(内存块):实际承载消息数据的单元,带引用计数,支持多读者并发读取。
- Channel 索引:记录某通道下可读 Block 的位置,读者据此定位数据。
- Notifier:通过条件变量或事件通知读者“有新数据可读”,避免忙等。
下面这张图展示了 Cyber RT 中一条典型共享内存通信链路的结构关系:
从图中可以看到,共享内存通信把“数据搬运”变成了“数据共享”:发布方写入一次,多个订阅方各自读取,互不干扰。引用计数保证 Block 在所有读者释放后才被回收,Notifier 则解决“何时读”的同步问题。
四、典型场景:激光雷达点云从驱动到感知
以激光雷达为例,驱动进程以 10 Hz 发布 PointCloud 消息,感知进程订阅同一通道。若使用传统 socket 或普通队列,每帧数 MB 的点云需要经历多次内存拷贝,CPU 占用和延迟都会显著上升。在 Cyber RT 中,该链路大致如下:
# 伪代码示意:组件声明通道
class LidarDriver : public cyber::Component<PointCloud> {
bool Init() {
writer_ = node_->CreateWriter<PointCloud>("/apollo/sensor/lidar/front");
return true;
}
bool Proc(const std::shared_ptr<PointCloud>& msg) {
writer_->Write(msg); // 同机默认走共享内存
return true;
}
};
class Perception : public cyber::Component<PointCloud> {
bool Init() {
reader_ = node_->CreateReader<PointCloud>(
"/apollo/sensor/lidar/front",
[this](const std::shared_ptr<PointCloud>& msg) { Process(msg); });
return true;
}
};
当驱动与感知位于同一主机时,Cyber RT 的 Transport 会自动选择 SHM 通道;若感知被部署到另一台计算单元,则自动切换为 RTPS 网络传输。算法代码无需修改,这正是中间件“屏蔽传输差异”的价值。
五、常见误区与复盘
- 误区一:用了 Cyber RT 就一定零拷贝。零拷贝主要发生在同机共享内存路径;跨机 RTPS 仍需序列化与网络发送,并非零拷贝。
- 误区二:共享内存越大越好。Segment 过大浪费内存,过小则频繁分配回收;应结合消息大小与并发读者数估算 Block 数量。
- 误区三:忽略读者释放。若读者长期持有 Block 引用,发布方会因无可用 Block 而阻塞或丢弃数据,需关注引用计数与超时策略。
- 误区四:把通信中间件当业务框架。Cyber RT 负责调度与传输,业务逻辑、数据校验、异常恢复仍需算法模块自身保证。
理解 Cyber RT 的定位、分层与共享内存角色,是后续深入零拷贝工程实践的基础:只有先看清数据在“组件—通道—传输—共享内存”这条链路上如何流动,才能在调优时准确判断瓶颈究竟在调度、序列化还是内存管理环节。