一、系统架构对比:Apollo的Cyber RT框架 vs Autoware的ROS 2架构
做自动驾驶系统,选对底层框架太重要了。我这些年两个框架都深度用过,今天聊聊它们的架构差异。
先说结论:Apollo的Cyber RT是为自动驾驶量身定做的,Autoware的ROS 2则更通用、生态更丰富。你想想看,一个是从零开始只为无人车设计,另一个是机器人领域的通用方案改过来的,骨子里的基因就不一样。
1.1 模块化设计差异
先看一张整体架构对比图,我手绘的,帮你快速建立认知:
1.2 Apollo Cyber RT 的核心设计
Cyber RT 是百度从零开始写的。我记得2018年第一次接触时,第一反应是「这玩意儿跟ROS完全不一样」。
它的设计哲学是:确定性 + 高性能。说白了,就是让每个模块的执行时间可预测,延迟尽可能低。
Cyber RT 三大核心机制:
- 协程调度:不是线程,是协程。切换成本极低,能跑几千个协程不卡顿
- 共享内存通信:数据不拷贝,直接读内存。延迟能压到微秒级
- Component 模型:每个模块是一个Component,框架帮你管理触发条件
看个实际例子。这是Cyber RT里一个感知模块的写法:
class PerceptionComponent : public apollo::cyber::Component<CameraFrame> {
public:
bool Init() override {
// 初始化模型、分配内存
AINFO << "Perception component initialized";
return true;
}
bool Proc(const std::shared_ptr<CameraFrame>& msg) override {
// 每次收到CameraFrame数据,自动触发
// 注意:这里拿到的msg是共享内存指针,不是拷贝
ProcessFrame(msg);
return true;
}
};
// 注册组件
CYBER_REGISTER_COMPONENT(PerceptionComponent)
你发现没?没有while循环,没有显式的spin。框架帮你搞定了调度。我在项目里最喜欢的就是这个设计——写业务逻辑的人不用操心线程怎么管理。
我的经验:Cyber RT的协程调度在CPU密集型的感知模块里特别香。我曾经把一个激光雷达点云处理模块从ROS迁移到Cyber RT,延迟从15ms降到了3ms。当然,前提是你得理解协程的阻塞问题——协程里千万别做sleep或者同步I/O,否则整个调度器都卡住。
1.3 Autoware ROS 2 的架构特点
ROS 2 走的是另一条路。它继承了ROS 1的生态,但底层换成了DDS(数据分发服务)。
ROS 2 的设计哲学是:分布式 + 标准化。每个节点是独立的进程,通过DDS通信。好处是天然支持多机部署,坏处是——嗯,延迟比共享内存高不少。
ROS 2 四大核心机制:
- Node 模型:每个功能模块是一个Node,独立进程运行
- DDS 通信:使用RTPS协议,支持QoS配置(可靠性、历史记录等)
- 生命周期管理:节点有明确的状态机(未配置→未激活→激活→关闭)
- 工具链丰富:RViz2可视化、rosbag2录包、tf2坐标变换
看一个Autoware里感知模块的典型写法:
class LidarDetectionNode : public rclcpp::Node {
public:
LidarDetectionNode() : Node("lidar_detection_node") {
// 声明参数
this->declare_parameter<double>("confidence_threshold", 0.5);
// 创建订阅者
subscription_ = this->create_subscription<sensor_msgs::msg::PointCloud2>(
"/input/points", 10,
std::bind(&LidarDetectionNode::callback, this, std::placeholders::_1));
// 创建发布者
publisher_ = this->create_publisher<autoware_auto_msgs::msg::DetectedObjects>(
"/output/objects", 10);
}
private:
void callback(const sensor_msgs::msg::PointCloud2::SharedPtr msg) {
// 处理点云数据
auto objects = detect_objects(*msg, confidence_threshold_);
publisher_->publish(objects);
}
rclcpp::Subscription<sensor_msgs::msg::PointCloud2>::SharedPtr subscription_;
rclcpp::Publisher<autoware_auto_msgs::msg::DetectedObjects>::SharedPtr publisher_;
};
注意:ROS 2的回调函数里,拿到的msg是拷贝过来的。如果你处理的是大尺寸数据(比如高清图像、密集点云),内存拷贝的开销不容忽视。我曾经在一个项目中,仅仅因为把图像从sensor_msgs转成cv::Mat,就多花了8ms。解决办法?用shared_ptr传递,或者用zero-copy的DDS配置。
1.4 通信机制深度对比
这是两个框架最核心的差异点。我直接上对比表:
| 对比维度 | Apollo Cyber RT | Autoware ROS 2 |
|---|---|---|
| 底层协议 | 自研共享内存 + Socket | DDS (Data Distribution Service) |
| 数据传输 | 零拷贝(共享内存指针传递) | 序列化拷贝(默认) |
| 延迟典型值 | 10-100 μs(同进程) | 100-1000 μs(同机) |
| 通信模式 | 发布/订阅 + 服务调用 | 发布/订阅 + 服务 + 动作 |
| QoS支持 | 有限(可靠性、优先级) | 丰富(可靠性、持久性、延迟预算等) |
| 跨机通信 | 需要额外配置 | 原生支持(DDS天生分布式) |
| 调试工具 | cyber_monitor, cyber_recorder | ros2 topic, ros2 bag, RViz2 |
看到这个表,你可能会问:「那到底选哪个?」
我的建议是这样的:
- 如果你做的是量产项目,对延迟和确定性要求极高——选Cyber RT。它的协程调度和零拷贝在实车上表现确实好。
- 如果你做的是研发验证,需要快速迭代、用各种现成工具——选ROS 2。生态太丰富了,随便一个算法包都能直接用。
- 如果你需要多车协同或者车路协同——ROS 2的DDS天然支持分布式,省去很多网络编程的麻烦。
避坑指南:我曾经在一个项目中,团队坚持用ROS 2做感知融合,结果发现点云数据在节点间传来传去,延迟累积到了50ms。后来我们改成了进程内通信(用composition的方式把多个节点放到一个进程里),延迟降到了12ms。所以,别被「分布式」这个词迷惑了——同机通信时,进程内通信永远比进程间通信快。
1.5 模块化设计的实际差异
说完了通信,再聊聊模块化。这个差异其实更影响日常开发。
Cyber RT的模块化:
- 每个功能是一个Component,框架管理生命周期
- Component之间通过Channel(通道)连接
- 调度由框架统一管理,开发者不用管线程
- 模块配置通过DAG文件(有向无环图)描述
ROS 2的模块化:
- 每个功能是一个Node,独立进程或组合进程
- Node之间通过Topic/Service/Action连接
- 每个Node有自己的线程池,开发者需要管理回调
- 模块配置通过launch文件和参数服务器
举个例子。在Cyber RT里,你要加一个障碍物预测模块,只需要写一个Component,然后在DAG文件里声明输入输出通道:
# dag文件示例
module_config {
module_library : "libprediction_component.so"
components {
class_name : "PredictionComponent"
config {
name : "prediction"
readers {
channel: "/perception/obstacles"
}
writers {
channel: "/prediction/predicted_obstacles"
}
}
}
}
而在ROS 2里,你需要写一个Node,然后在launch文件里启动它:
# launch文件示例
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(
package='prediction',
executable='prediction_node',
name='prediction_node',
parameters=[{'confidence_threshold': 0.5}],
remappings=[
('/input/obstacles', '/perception/obstacles'),
('/output/predicted', '/prediction/predicted_obstacles')
]
)
])
你看出区别了吗?Cyber RT的DAG文件是静态配置,启动时框架帮你把整个数据流图建好。ROS 2的launch文件是动态启动,每个节点独立运行。前者更可控,后者更灵活。
我的个人习惯:在做原型验证时,我倾向于用ROS 2,因为改一个参数不用重新编译。但在做量产部署时,我更喜欢Cyber RT的DAG配置——整个系统的数据流一目了然,调试起来特别方便。
1.6 小结
两个框架没有绝对的好坏。Cyber RT在性能和确定性上占优,ROS 2在生态和灵活性上胜出。选哪个,取决于你的项目阶段和团队背景。
如果你是从零开始做自动驾驶,我建议先学ROS 2——资料多、社区活跃、上手快。等你把算法验证通了,再考虑迁移到Cyber RT做性能优化。
嗯,这一章就聊到这儿。下一章我们深入看看两个框架的调度机制——那个才是真正拉开差距的地方。