架构设计对比:Apollo的基于ROS的微服务架构 vs Autoware的基于ROS2的模块化架构
聊到自动驾驶系统的架构设计,我脑子里第一个蹦出来的词就是「分而治之」。你想想看,一辆车上跑着感知、定位、规划、控制这么多模块,要是全揉在一起,那调试起来简直是一场噩梦。
今天咱们就来掰扯掰扯,Apollo 和 Autoware 这两大开源框架,在架构层面到底走了两条什么样的路。我个人习惯先看骨架,再看血肉——架构就是骨架,决定了系统能长多大、能跑多稳。
Apollo:基于ROS的微服务架构
Apollo 早期版本是基于 ROS1 搭建的。但百度团队做了个关键决定:没有直接用原生 ROS,而是搞了一套自己的微服务框架。为什么?
我记得 2018 年第一次接触 Apollo 3.0 时,就被它的「Cyber RT」吸引住了。说白了,百度觉得 ROS1 的通信机制在量产车上不够靠谱——单点故障、缺乏 QoS 控制、调度不可预期,这些都是硬伤。
Apollo 的架构核心是 Cyber RT,一个去中心化的通信中间件。它把每个功能模块都封装成独立的「Component」,组件之间通过 Channel 通信。每个 Component 可以独立启停、独立升级,互不干扰。
- 去中心化调度:没有 Master 节点,避免单点故障
- 基于协程的任务调度:轻量级,切换成本低
- 支持 QoS 策略:可以控制消息的可靠性、历史数据保留等
- 共享内存通信:零拷贝传输,延迟极低
我曾在项目中遇到过一个问题:用原生 ROS 跑多传感器融合,激光雷达和摄像头的数据频率不一样,结果经常丢帧。换成 Apollo 的 Cyber RT 后,通过配置 QoS 的 HistoryPolicy 和 ReliabilityPolicy,这个问题就解决了。
来看一个典型的 Component 定义示例:
// Apollo Cyber RT Component 示例
class PlanningComponent : public Component<PredictionObstacles, LocalizationEstimate> {
public:
bool Init() override {
// 初始化规划器
planner_ = std::make_shared<EMPlanner>();
return true;
}
bool Proc(const std::shared_ptr<PredictionObstacles>& pred,
const std::shared_ptr<LocalizationEstimate>& loc) override {
// 主处理逻辑
auto trajectory = planner_->Plan(pred, loc);
writer_->Write(trajectory);
return true;
}
};
看到没?Init() 做初始化,Proc() 做循环处理。每个 Component 只关心自己的输入输出,耦合度极低。这就是微服务架构的精髓——高内聚、低耦合。
Autoware:基于ROS2的模块化架构
Autoware 走的是另一条路。它直接拥抱了 ROS2,把 ROS2 的 DDS(数据分发服务)作为通信底座。ROS2 相比 ROS1 最大的改进是什么?我总结三点:去中心化、实时性、安全性。
Autoware 的架构是典型的「模块化」设计。每个功能模块——比如感知、规划、控制——都是一个独立的 ROS2 Node。Node 之间通过 Topic、Service、Action 三种方式通信。
我个人觉得,Autoware 的架构更「学术范儿」。它把每个模块的接口定义得清清楚楚,非常适合做算法研究和快速原型验证。但说实话,在实车部署时,ROS2 的 DDS 配置有时候挺让人头疼的。
我曾经踩过一个坑:Autoware 的感知模块和规划模块跑在不同的 DDS Domain 上,结果死活收不到消息。查了半天才发现是 ROS_DOMAIN_ID 没设对。嗯,这种问题在 Apollo 的 Cyber RT 里基本不会出现。
来看 Autoware 的一个典型 Node 结构:
// Autoware ROS2 Node 示例
class PlanningNode : public rclcpp::Node {
public:
PlanningNode() : Node("planning_node") {
// 订阅预测和定位话题
sub_pred_ = this->create_subscription<PredictedObjects>(
"prediction/objects", 10,
std::bind(&PlanningNode::onPrediction, this, _1));
sub_loc_ = this->create_subscription<Odometry>(
"localization/kinematic_state", 10,
std::bind(&PlanningNode::onLocalization, this, _1));
// 发布轨迹话题
pub_traj_ = this->create_publisher<Trajectory>("planning/trajectory", 10);
}
private:
void onPrediction(const PredictedObjects::SharedPtr msg) {
// 处理预测数据
}
void onLocalization(const Odometry::SharedPtr msg) {
// 处理定位数据
}
};
核心差异对比
说了这么多,咱们用一张表来总结一下两者的核心差异:
| 对比维度 | Apollo (Cyber RT) | Autoware (ROS2) |
|---|---|---|
| 通信中间件 | 自研 Cyber RT | ROS2 + DDS |
| 调度模型 | 协程 + 线程池 | 多线程 + 回调 |
| 组件粒度 | Component(微服务) | Node(模块化) |
| 通信方式 | Channel(共享内存优先) | Topic/Service/Action(DDS) |
| QoS 支持 | 内置,配置简单 | 依赖 DDS,配置复杂 |
| 实时性 | 高(协程调度) | 中(依赖 DDS 实现) |
| 部署难度 | 中等(自研工具链) | 较低(ROS2 生态) |
| 适合场景 | 量产、实车验证 | 研究、快速原型 |
看到这张表,你可能会问:「那我到底该选哪个?」
我的回答是:看你的目标。如果你要做量产级的自动驾驶系统,Apollo 的 Cyber RT 架构更成熟、更稳定。如果你在做算法研究或学术项目,Autoware 的 ROS2 架构更灵活、上手更快。
架构对比的核心逻辑
为了让你更直观地理解两者的架构差异,我画了一张对比图:
从这张图你能看到,Apollo 的 Component 之间通过共享内存 Channel 直接通信,调度由 Cyber RT 统一管理。而 Autoware 的 Node 之间通过 DDS 网络通信,调度由 ROS2 执行器管理。
说白了,Apollo 更像一个「精密仪器」——每个零件都经过精心打磨,配合默契。Autoware 更像一个「乐高积木」——你可以自由组合,但需要自己保证拼装质量。
message_filters::TimeSynchronizer 来做时间同步。
最后说一句:架构没有绝对的好坏,只有适不适合。Apollo 的微服务架构更适合「确定性」要求高的场景,Autoware 的模块化架构更适合「灵活性」要求高的场景。选哪个,取决于你的项目阶段和团队能力。
可可精品资料库