1. 课程导入
欢迎进入《Apollo Cyber RT 中间件工程》课程。在自动驾驶系统中,算法决定车辆“会不会开”,而中间件决定车辆“能不能稳定、实时、可扩展地开”。Apollo Cyber RT 正是百度 Apollo 面向自动驾驶场景自研的实时通信中间件,它把传感器、感知、预测、规划、控制等模块组织成一张高吞吐、低时延、可调度、可观测的数据网络。本课程的目标,是让你不仅会“调用”Cyber RT,更能理解其内部机制,并具备工程化落地与排障能力。
本课程的学习主线可以概括为:通信模型 → 运行时调度 → 数据与参数 → 组件与节点 → 工具链与可观测性 → 工程实践与性能调优。每一章都会围绕一个可验证的工程问题展开,而不是停留在 API 记忆层面。
一、学习目标:从“会用”到“会调、会查、会优化”
本课程结束时,你应当能够独立完成以下任务:
- 理解 Cyber RT 的节点(Node)、通道(Channel)、读者/写者(Reader/Writer)、服务(Service)等核心抽象,并能用它们搭建模块间通信。
- 掌握组件(Component)的加载、调度与生命周期管理,理解协程调度器与线程池如何影响实时性。
- 能够编写、编译并运行基于 Cyber RT 的模块,使用 Bazel 与 cyber 工具链完成构建、启动与调试。
- 能够使用 cyber_monitor、cyber_visualizer、cyber_recorder 等工具定位丢包、时延、频率异常等问题。
- 理解 DDS、ROS 2、Cyber RT 等中间件在自动驾驶技术栈中的定位差异,并能根据场景做技术选型。
二、核心概念:Cyber RT 的“最小知识骨架”
Cyber RT 的设计哲学是“以数据为中心、以组件为执行单元”。下面几个概念贯穿全课程:
| 概念 | 含义 | 工程关注点 |
|---|---|---|
| Node | 通信的基本参与单元,可创建 Reader/Writer/Service | 命名、生命周期、线程安全 |
| Channel | 具名数据通道,承载特定消息类型 | 话题命名规范、QoS、频率 |
| Component | 可被调度器加载执行的模块单元 | 触发方式、调度策略、资源占用 |
| Scheduler | 协程/线程调度器,决定组件何时运行 | 实时性、优先级、CPU 亲和性 |
| Record | 数据录制与回放文件 | 回放验证、问题复现 |
这些概念不是孤立的:Node 通过 Channel 交换数据,Component 封装业务逻辑并由 Scheduler 驱动,Record 则把真实数据流固化为可复现的工程资产。
三、整体框架:一张图看清 Cyber RT 的分层结构
下图展示了 Cyber RT 从应用层到系统层的整体框架,以及它与自动驾驶模块、操作系统之间的关系。请重点观察数据流方向与调度器的位置。
四、方法流程:一个模块从代码到运行经历了什么
理解 Cyber RT 的关键,是把它看成一个“数据驱动的流水线”。一个典型模块的工程流程如下:
- 定义消息类型(.proto),生成 C++ 代码。
- 编写 Component,声明 Reader 与 Writer,实现 Proc 回调。
- 通过 DAG 文件与 launch 文件配置组件、通道与调度参数。
- 使用 Bazel 编译,生成可执行模块。
- 启动 cyber 运行时,加载 DAG,调度器开始驱动组件。
- 用 cyber_monitor 观察通道频率与延迟,用 cyber_recorder 录制数据。
# 典型启动与观察命令
cyber_launch start modules/demo/demo.launch
cyber_monitor
cyber_recorder record -a
cyber_recorder play -f demo.record
五、案例场景:为什么中间件决定自动驾驶的“上限”
假设一辆自动驾驶车辆以 10Hz 运行规划模块,同时有 6 路摄像头、5 路激光雷达、多路毫米波雷达持续输出数据。若中间件采用简单的“发布-订阅 + 线程池”,很容易出现消息堆积、时延抖动、CPU 抢占等问题。Cyber RT 通过共享内存、零拷贝、协程调度与优先级控制,把端到端时延压到毫秒级,并保证关键链路可预测。这就是“算法相同、表现不同”的工程原因。
学习本课程时,建议始终带着三个问题:数据从哪里来?由谁调度?如何验证它真的按时到达?这三个问题能帮你把零散 API 串成完整工程能力。
六、常见误区与复盘
- 误区一:把 Cyber RT 当成“另一个 ROS”。 两者抽象相似,但 Cyber RT 更强调实时调度与量产部署,不能只按 ROS 经验套用。
- 误区二:只关注通信,不关注调度。 很多“丢帧”问题并非通信失败,而是组件未被及时调度。
- 误区三:忽略录制与回放。 没有 Record,问题无法复现,排障只能靠猜。
- 误区四:参数散落在代码中。 应使用 Parameter 与配置文件管理,便于标定与灰度。