架构设计对比: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 只关心自己的输入输出,耦合度极低。这就是微服务架构的精髓——高内聚、低耦合。

我的建议:如果你在做量产项目,Apollo 的 Cyber RT 架构更值得参考。它的调度确定性、资源隔离能力,都是为实车验证打磨过的。

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) {
    // 处理定位数据
  }
};
注意:ROS2 的 QoS 配置是个双刃剑。配置不当会导致消息丢失或延迟暴增。我建议你在做系统集成时,一定要仔细测试不同 QoS 策略下的端到端延迟。

核心差异对比

说了这么多,咱们用一张表来总结一下两者的核心差异:

对比维度 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 (Cyber RT) 感知 Component 定位 Component 预测 Component 规划 Component 控制 Component Cyber RT 协程调度器 共享内存 Channel Autoware (ROS2) 感知 Node 定位 Node 预测 Node 规划 Node 控制 Node DDS 数据分发服务 ROS2 多线程执行器 微服务架构 vs 模块化架构

从这张图你能看到,Apollo 的 Component 之间通过共享内存 Channel 直接通信,调度由 Cyber RT 统一管理。而 Autoware 的 Node 之间通过 DDS 网络通信,调度由 ROS2 执行器管理。

说白了,Apollo 更像一个「精密仪器」——每个零件都经过精心打磨,配合默契。Autoware 更像一个「乐高积木」——你可以自由组合,但需要自己保证拼装质量。

避坑指南:我曾经在 Autoware 上做多传感器融合时,发现激光雷达和相机的数据时间戳对不上。后来查出来是 ROS2 的时钟同步机制没配置好。如果你也遇到类似问题,建议用 message_filters::TimeSynchronizer 来做时间同步。

最后说一句:架构没有绝对的好坏,只有适不适合。Apollo 的微服务架构更适合「确定性」要求高的场景,Autoware 的模块化架构更适合「灵活性」要求高的场景。选哪个,取决于你的项目阶段和团队能力。

可可精品资料库