2. Cyber RT 基础概念
学习 Cyber RT,第一步不是急着写代码,而是把它的“词汇表”建立起来。Cyber RT 是一套面向自动驾驶的高性能运行时框架,它的设计目标可以概括为:让数据在正确的时刻,以正确的形式,到达正确的计算单元。围绕这个目标,框架把系统拆解成若干职责清晰的概念对象——节点负责组织计算,通道负责传输数据,消息负责承载内容,服务负责请求应答,参数负责运行期配置,组件负责封装可复用模块,调度器负责决定谁先执行,通信中间件负责把这一切连起来。理解它们之间的分工与协作,是后续开发组件、调优性能的基础。
一、核心对象的分工与关系
可以把 Cyber RT 想象成一座城市的物流系统:节点(Node)是分布在城市各处的收发站,它本身不生产货物,而是注册和管理自己关心的收发线路;通道(Channel)是连接收发站的专用运输线路,每条线路有唯一名称和明确的数据类型;消息(Message)是线路上运输的货物,通常由 Protobuf 定义结构;服务(Service)则像“呼叫—应答”专线,客户端发出请求,服务端处理后返回结果,适合配置查询、状态确认等交互;参数(Parameter)是挂在节点上的可调旋钮,运行期可读可改,用于避免硬编码;组件(Component)是封装好的业务模块,内部通常包含若干节点、通道和处理逻辑,是工程复用的基本单位;调度器(Scheduler)是交通指挥中心,决定哪些任务在哪个线程、以什么优先级执行;通信中间件则是底层的道路与信号系统,负责发现、序列化、传输和唤醒。
二、一次数据流转的完整过程
以“摄像头图像进入感知组件”为例,可以清楚看到这些概念如何协同工作。首先,感知组件被加载,内部创建了一个名为 camera 的节点;该节点通过 CreateReader 订阅通道 /camera/image,并声明消息类型为 Image。当摄像头驱动节点通过 CreateWriter 向同一通道写入一帧图像时,通信中间件完成序列化与传输,调度器根据通道的优先级和回调策略唤醒感知节点的处理函数。处理函数拿到 Image 消息后执行推理,再把结果写入 /perception/obstacles 通道,供下游规划节点消费。如果规划节点需要查询感知模块的健康状态,则通过服务 /get_perception_status 发起请求,而不是再开一条数据通道。
// 节点创建与通道读写示意
auto node = cyber::CreateNode("camera");
auto reader = node->CreateReader<Image>(
"/camera/image",
[](const std::shared_ptr<Image>& msg) {
// 消息回调:处理一帧图像
});
auto writer = node->CreateWriter<Image>("/camera/image");
writer->Write(image_msg);
三、概念对照与常见误区
| 概念 | 核心职责 | 典型使用场景 | 常见误区 |
|---|---|---|---|
| 节点 Node | 注册与管理通道、服务、参数 | 按传感器或算法划分逻辑单元 | 把节点当成线程,认为一个节点就是一个执行体 |
| 通道 Channel | 命名化的数据传输管道 | 传感器数据、算法结果发布订阅 | 通道名随意起,导致上下游对不上 |
| 消息 Message | 承载结构化数据 | Protobuf 定义的图像、点云、轨迹 | 消息体过大,频繁拷贝拖慢性能 |
| 服务 Service | 请求—应答式交互 | 状态查询、配置下发、一次性计算 | 用服务传高频数据,造成阻塞和延迟 |
| 参数 Parameter | 运行期可读写的配置项 | 阈值、开关、标定参数动态调整 | 把参数当全局变量滥用,破坏模块边界 |
| 组件 Component | 业务模块的封装与复用 | 感知、定位、规划、控制等模块 | 组件内塞入过多职责,难以独立测试 |
| 调度器 Scheduler | 决定任务执行顺序与资源分配 | 高优先级控制任务优先执行 | 忽视调度策略,导致关键任务被饿死 |
| 通信中间件 | 发现、序列化、传输与唤醒 | 共享内存、零拷贝、跨进程通信 | 认为中间件“透明”,不关注传输开销 |
四、案例复盘:通道命名不一致导致的“无数据”故障
某团队在联调时发现感知组件始终收不到图像。排查日志显示节点创建成功、回调已注册,但没有任何消息触发。最终定位到:摄像头驱动写入的通道是 /camera/image,而感知组件订阅的是 /camera/Image,大小写不一致。Cyber RT 的通道名是严格区分大小写的,且不会自动做模糊匹配。这个问题看似低级,却暴露了对“通道是显式契约”这一概念理解不深。复盘结论有三点:第一,通道名应纳入接口文档统一管理;第二,组件启动时应打印实际订阅的通道列表;第三,对关键通道增加心跳或超时告警,而不是等到“无数据”才被动发现。
掌握这些基础概念后,后续无论是编写一个最小组件,还是排查通信故障,都可以沿着“组件—节点—通道—消息—中间件—调度器”这条链路逐层定位。概念清晰,工程才不会迷路。