解析百度Apollo自动驾驶平台

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 最近对百度的自动驾驶平台Apollo项目做了一些了解。下面将我所了解到的一些信息分享给大家。 # Apollo项目介绍 阿波罗(Apollo)是百度发布的面向汽车行业及自动驾驶领域的合作伙伴提供的软件平台。发布时间是2017年4月19日,旨在向汽车行业及自动驾驶领域的合作伙伴提供一个开放、完整、安全的软件平台,帮助他们结合车辆和硬件系统,快速搭建一套属于自己的完整的自动驾驶系统。而将这

最近对百度的自动驾驶平台Apollo项目做了一些了解。下面将我所了解到的一些信息分享给大家。

Apollo项目介绍

阿波罗(Apollo)是百度发布的面向汽车行业及自动驾驶领域的合作伙伴提供的软件平台。发布时间是2017年4月19日,旨在向汽车行业及自动驾驶领域的合作伙伴提供一个开放、完整、安全的软件平台,帮助他们结合车辆和硬件系统,快速搭建一套属于自己的完整的自动驾驶系统。而将这个计划命名为“Apollo”计划,就是借用了阿波罗登月计划的含义。

可以在这里感受一下Apollo的实车驾车体验:CES 2018 百度Apollo 2.0无人车美国桑尼维尔试乘

SAE Level

对于自动驾驶,SAE(Society of Automotive Engineers,美国汽车工程师学会) International于2014年发布了从全手动系统到全自动系统六个不同级别的分类系统,这6个级别的描述如下:

SAE Level Name System capability Driver involvement
0 No Automation None The human at the wheel steers, brakes, accelerates, and negotiates traffic.
1 Drive Assistance Under certain conditions, the car controls either the steering or the vehicle speed, but not both simultaneously. The driver performs all other aspects of driving and has full responsibility for monitoring the road and taking over if the assistance system fails to act appropriately.
2 Partial Automation The car can steer, accelerate, and brake in certain circumstances. Tactical maneuvers such as responding to traffic signals or changing lanes largely fall to the driver, as does scanning for hazards. The driver may have to keep a hand on the wheel as a proxy for paying attention.
3 Conditional Automatio In the right conditions, the car can manage most aspects of driving, including monitoring the environment. The system prompts the driver to intervene when it encounters a scenario it can’t navigate. The driver must be available to take over at any time.
4 High Automation The car can operate without human input or oversight but only under select conditions defined by factors such as road type or geographic area. In a shared car restricted to a defined area, there may not be any. But in a privately owned Level 4 car, the driver might manage all driving duties on surface streets then become a passenger as the car enters a highway.
5 Full Automation The driverless car can operate on any road and in any conditions a human driver could negotiate. Entering a destination.

阿波罗项目的官网地址如下:http://apollo.auto

在阿波罗项目的官网,介绍了该项目有如下特点:

  • 开放能力:Apollo(阿波罗)是一个开放的、完整的、安全的平台,将帮助汽车行业及自动驾驶领域的合作伙伴结合车辆和硬件系统,快速搭建一套属于自己的自动驾驶系统。
  • 共享资源、加速创新:Apollo开放平台,为你提供技术领先、覆盖广、高自动化的高精地图服务;全球唯一开放,拥有海量数据的仿真引擎;全球开放数据量第一,基于深度学习自动驾驶算法End-to-End。
  • 持续共赢:Apollo开放平台,你可以更快地研发、测试和部署自动驾驶车辆。参与者越多,积累的行驶数据就越多。与封闭的系统相比,Apollo能以更快的速度成熟,让每个参与者得到更多的受益,同时Apollo平台也将在你的参与之下变得更好!

目前,其官网上列出的合作伙伴已经接近100家。

阿波罗项目的蓝图如下:

  • 2017-07:封闭场地的自动驾驶能力
  • 2017-12:在城市简单路况下的自动驾驶能力
  • 2020-12:高速公路和普通城市道路上的全自动驾驶

最新发布的Apollo 2.5版本主要目标是L2级自动驾驶。

详细的Apollo版本演进信息如下图所示:

源码

可以在这里获取到阿波罗项目的源码:https://github.com/ApolloAuto。这个路径中包含了5个开源项目:

  • apollo:Apollo自动驾驶平台的源码。
  • apollo-platform:Apollo项目基于Robot Operating System (ROS),这里是相关代码。目前发布的源码基于ROS Indigo。
  • apollo-DuerOS:Apollo-DuerOS是一套与Apollo相关的远程信息处理产品,这其中包含了几个开源产品。关于DuerOS,请看这里:DuerOS
  • apollo-kernel:Apollo项目的Linux内核。
  • ApolloAuto.github.io:Apollo相关文档,可以访通过https://apolloauto.github.io访问这些文档。

编译和运行

关于如何编译和运行阿波罗项目请参见这里:https://apolloauto.github.io

执行该任务需要Ubuntu和Docker环境。

编译完成之后,可以在电脑上通过该项目提供的Dreamview功能来熟悉环境,Dreamview通过浏览器访问,其界面看起来是这个样子:

dreamview_2_5.png

关于Dreamview的更多说明,请参见这里:Dreamview Usage Table

开发

阿布罗平台的开发包含下面几个步骤:

  1. 了解离线模拟引擎Dreamviewer和ApolloAuto核心软件模块

    • 了解算法如何在汽车上运作
    • 不需要使用真正的汽车或硬件,就立即开始开发
  2. 核心模块集成

    • Location模块
    • Perception模块:(支持第三方解决方案,如基于Mobileye ES4芯片的摄像头,用于L2开发)处理来自Lidar的点云数据,并根据请求返回分段对象信息。
    • Planning模块:计算微调路径,为路径服务的路径段提供汽车动态控制信息。
    • Routine模块:通过Navigator接口查找路径段的本地实现。
  3. 高清地图。L4级别的自动驾驶需要高清地图。由于自动驾驶汽车需要在系统中重建3D世界,因此参考对象坐标在重新定位地图和现实世界中的自动驾驶方面发挥着重要作用。
  4. 基于云的在线仿真驱动场景引擎和数据中心。

    • 作为百度的合作伙伴,将被授予Docker证书来提交新图像并重播你在云上开发的算法。
    • 创建和管理复杂的场景以模拟真实世界的驾驶体验

Apollo与ROS

ROS全称是Robot Operating System。它包含了一套开源的软件库和工具,专门用来构建机器人应用。其官网地址在这里:http://www.ros.org

在一个ROS系统中,包含了一系列的独立节点(nodes)。这些节点之间,通过发布/订阅的消息模型进行通信。例如,某个传感器的驱动可以实现为一个节点,然后以发布消息的形式对外发送传感器数据。这些数据可以被多个其他节点接收,例如:过滤器,日志系统等等。

ROS系统中的节点可能位于不同的主机上,例如:在一个Arduino设备上发布消息,一台笔记本电脑订阅这些消息,一个Android手机也监测这些消息。

ROS系统中包含了一个主(Master)节点。主节点使得其他节点可以查询彼此以进行通讯。所有节点都需要在主节点上进行注册,然后就可以与其他节点通讯了。如下图所示:

熟悉Android系统的人可能很容易发现,这和Binder中的ServiceManager的作用是类似的。

ros101-1.png

节点之间通过发布和订阅主题(Topics)进行通讯。例如,在某个机器人系统中,位于机器人上有一个相机模块可以获取图像数据。另外在机器人上有一个图像处理模块需要获取图像数据,与此同时还有另外一个位于个人PC上的模块也需要这些图像数据。那么,相机模块可以发布/image_data这个主题供其他两个模块来订阅。其结构如下图所示:

ros101-3.png

Apollo项目基于ROS,但是对其进行了改造,主要包括下面三个方面:

  1. 通信性能优化
  2. 去中心化网络拓扑
  3. 数据兼容性扩展

通信性能优化

自动驾驶车辆中包含了大量的传感器,这些传感器可能以非常高频的速度产生数据,所以整个系统对于数据传输效率要求很高。在ROS系统中,从数据的发布到订阅节点之间需要进行数据的拷贝。在数据量很大的情况下,很显然这会影响数据的传输效率。所以Apollo项目对于ROS第一个改造就是将通过共享内存来减少数据拷贝,以提升通信性能。如下图所示:

去中心化网络拓扑

前文我们提到,ROS系统中包含了一个通信的主节点,所有其他节点都要借助于这个节点来进行通信。所以,很显然的,假如这个节点发生了通信故障,就会影响整个系统的通信。并且,整个结构还缺乏异常恢复机制。

所以Apollo项目对于ROS的第二个改造就是去除这种中心化的网络结构。Apollo使用RTPS(Real-Time Publish-Subscribe)服务发现协议实现完全的P2P网络拓扑。整个通信过程包含下面四个步骤:

关于RTPS详见这里:Real-Time Publish-Subscribe

rtps_step1.png

rtps_step2.png

rtps_step3.png

rtps_step4.png

数据兼容性扩展

Apollo项目对于ROS最后一个较大的改进就是对于数据格式的调整。

在ROS系统中,使用msg描述文件定义模块间的消息接口。但不幸的是,接口升级之后不同的版本的模块难以兼容。

data_not_compatible.png

因此,Apollo选择了Google的Protocol Buffers格式数据来解决这个问题。

Protocol Buffers,是Google公司开发的一种数据描述语言,类似于XML能够将结构化数据序列化,可用于数据存储、通信协议等方面。它不依赖于语言和平台并且可扩展性极强。现阶段官方支持C++、JAVA、Python三种编程语言,但可以找到大量的几乎涵盖所有语言的第三方拓展包。

注:如果你查看了Apollo项目的源码,可以看到很多名称为“proto”的文件夹,这些文件夹中包含的就是Protocol Buffers(简称protobuf)格式的数据结构。

apollo_protobuf.png

硬件架构

Apollo 2.5上必须的硬件如下表所示:

perception_required_hardware.png

外设包括下面这些:

perception_peripherals.png

硬件架构如下图所示:

Hardware_overview.png

软件架构

Apollo平台的软件架构如下图所示:

Apollo_2_5.png

在Apollo上,运行的核心软件模块包括:

  • Perception:感知模块识别自动车辆周围的世界。在Perception模块中有两个重要的子模块:障碍物检测和交通灯检测。
  • Prediction:Prediction模块预测未来的感知障碍物的运动轨迹。
  • Routing:Routing模块告诉自动车辆如何通过一系列车道或道路到达目的地。
  • Planning:Planning模块计划自主车辆的时空轨迹。
  • Control:Control模块通过生成诸如节流阀,制动器和转向的控制命令来执行计划的时空轨迹。
  • CanBus:CanBus将控制命令传递给车辆硬件的接口。它还将机架信息传递给软件系统。
  • HD-Map:提供有关道路特定结构化的信息。
  • Localization:该模块利用各种信息来源,例如GPS,LiDAR和IMU来估计自动车辆所在的位置。

这些模块的交互结构如下图所示:

Apollo_2_0_Software_Arch.png

每个模块都作为独立的基于CarOS的ROS节点运行。每个模块节点都会发布和订阅某些主题。订阅的主题用作数据输入,而发布的主题用作数据输出。

关于Apollo平台的系统架构可以阅读这篇文档:HOW TO UNDERSTAND ARCHITECTURE AND WORKFLOW

从这篇文档中我们看到:

  • 自动驾驶车辆由规划引擎通过CAN总线(Controller Area Network bus)来进行控制。
  • 为了计算效率,Location模块,Perception模块,Planning模块作为独立的输入源和输出源通过P2P一起工作。
  • 通过阅读源码${MODULE_NAME}/conf目录下的配置文件,我们可以获得有关模块订阅和发布的主题的基本信息。
  • 每个模块通过触发 Init 接口和注册回调开始。
  • 所有模块都会在下面这个点上注册:AdapterManager::Init。该函数部分代码片段如下:
void AdapterManager::Init(const AdapterManagerConfig &configs) {
  if (Initialized()) {
    return;
  }

  instance()->initialized_ = true;
  if (configs.is_ros()) {
    instance()->node_handle_.reset(new ros::NodeHandle());
  }

  for (const auto &config : configs.config()) {
    switch (config.type()) {
      case AdapterConfig::POINT_CLOUD:
        EnablePointCloud(FLAGS_pointcloud_topic, config);
        break;
      case AdapterConfig::GPS:
        EnableGps(FLAGS_gps_topic, config);
        break;
      case AdapterConfig::IMU:
        EnableImu(FLAGS_imu_topic, config);
        break;
      case AdapterConfig::RAW_IMU:
        EnableRawImu(FLAGS_raw_imu_topic, config);
        break;
      case AdapterConfig::CHASSIS:
        EnableChassis(FLAGS_chassis_topic, config);
        break;
      case AdapterConfig::LOCALIZATION:
        EnableLocalization(FLAGS_localization_topic, config);
        break;
      case AdapterConfig::PERCEPTION_OBSTACLES:
        EnablePerceptionObstacles(FLAGS_perception_obstacle_topic, config);
        break;
      case AdapterConfig::TRAFFIC_LIGHT_DETECTION:
        EnableTrafficLightDetection(FLAGS_traffic_light_detection_topic,
                                    config);
     ...

下面是对系统中主要的核心模块的一些解析。

核心模块

apollo/modules/中包含了系统中的各个模块的源码。

阅读这些源码会发现,这些核心模块的类都继承自一个公共基类ApolloApp,相关结构如下图所示:

modules.png

ApolloApp类的结构如下图所示:

ApolloApp.png

该类中的主要函数说明如下:

函数名 说明
std::string Name() const 返回模块名称
apollo::common::Status Init() 模块的初始化函数,模块启动的第一个函数
apollo::common::Status Start() 模块的启动函数
int Spin() 模块的入口点
void Stop() 模块的停止函数

apollo_app.h这个头文件中,还包含了一个宏以方便每个模块声明main函数,相关代码如下:

#define APOLLO_MAIN(APP)                                       \
  int main(int argc, char **argv) {                            \
    google::InitGoogleLogging(argv[0]);                        \
    google::ParseCommandLineFlags(&argc, &argv, true);         \
    signal(SIGINT, apollo::common::apollo_app_sigint_handler); \
    APP apollo_app_;                                           \
    ros::init(argc, argv, apollo_app_.Name());                 \
    apollo_app_.Spin();                                        \
    return 0;                                                  \
  }

每个模块的根目录都包含了一个README.md文件,是对这个模块的说明。我们可以以此为入口来了解模块的实现。

Perception(感知)

模块介绍

自动驾驶车辆通过前置摄像头和雷达与最近的车辆(closest in-path vehicle,简称CIPV)保持距离。子模块还预测障碍物运动和位置信息(例如,航向和速度)。Apollo 2.5支持高速公路上的高速自动驾驶,无需任何地图。深度网络算法已经学会处理图像数据。随着收集更多数据,深度网络的性能将随着时间的推移而提高。

模块输入:

  • 雷达数据
  • 图像数据
  • 雷达传感器校准的外部参数(来自YAML文件)
  • 前置相机校准的外部和内部参数(来自YAML文件)
  • 车辆的速度和角速度

模块输出:

  • 3D障碍物跟踪航向,速度和分类信息
  • 带有拟合曲线参数的车道标记信息,空间信息以及语义信息

模块解析

Perception模块需要根据输入信息快速的解析出两类信息,即:道路和物体。其中的深入网络基于YOLO算法[1][2]

Apollo 2.5不支持高曲率,没有车道标志的道路,包括当地道路和交叉路口。感知模块基于使用具有有限数据的深度网络的视觉检测。因此,在发布更好的网络之前,驾驶员在驾驶时应小心谨慎,并始终准备好通过将车轮转向正确的方向来解除自主驾驶。

  • 推荐道路

    • 两侧清晰的白色车道线
  • 不推荐道路

    • 高曲率的道路
    • 没有车道线标记的道路
    • 路口
    • 对接点或虚线车道线
    • 公共道路

而对于物体来说,又分为静态物体和动态物体。静态物体包括道路和交通灯等。动态物体包括机动车,自行车,行人,动物等。

为了保持车辆在车道上,需要一系列模块的配合,相关流程图如下所示:

perception_flow_chart_apollo_2.5.png

Perception模块在Init函数中会注册一系列类以完成模块启动后的正常工作,相关代码如下:

void Perception::RegistAllOnboardClass() {
  /// regist sharedata
  RegisterFactoryLidarObjectData();
  RegisterFactoryRadarObjectData();
  RegisterFactoryCameraObjectData();
  RegisterFactoryCameraSharedData();
  RegisterFactoryCIPVObjectData();
  RegisterFactoryLaneSharedData();
  RegisterFactoryFusionSharedData();
  traffic_light::RegisterFactoryTLPreprocessingData();

  /// regist subnode
  RegisterFactoryLidarProcessSubnode();
  RegisterFactoryRadarProcessSubnode();
  RegisterFactoryCameraProcessSubnode();
  RegisterFactoryCIPVSubnode();
  RegisterFactoryLanePostProcessingSubnode();
  RegisterFactoryAsyncFusionSubnode();
  RegisterFactoryFusionSubnode();
  RegisterFactoryMotionService();
  lowcostvisualizer::RegisterFactoryVisualizationSubnode();
  traffic_light::RegisterFactoryTLPreprocessorSubnode();
  traffic_light::RegisterFactoryTLProcSubnode();
}

我们可以以这里为入口了解各个子模块的逻辑。

RegisterFactoryLidarProcessSubnode为例。

代码中其实并不存在RegisterFactoryLidarProcessSubnode这个函数,该函数的定义其实是由宏完成的。相关代码如下:

Lidar(也称之为LIDAR,LiDAR,或LADAR)的全称是Light Detection And Ranging,即激光探测与测量。


// /modules/perception/onboard/subnode.h
#define REGISTER_SUBNODE(name) REGISTER_CLASS(Subnode, name)


// /modules/perception/lib/base/registerer.h
#define REGISTER_CLASS(clazz, name)                                           \
  class ObjectFactory##name : public apollo::perception::ObjectFactory {      \
   public:                                                                    \
    virtual ~ObjectFactory##name() {}                                         \
    virtual perception::Any NewInstance() {                                   \
      return perception::Any(new name());                                     \
    }                                                                         \
  };                                                                          \
  inline void RegisterFactory##name() {                                       \
    perception::FactoryMap &map = perception::GlobalFactoryMap()[#clazz];     \
    if (map.find(#name) == map.end()) map[#name] = new ObjectFactory##name(); \
  }

而在lidar_process_subnode.h中使用了上面这个宏。

REGISTER_SUBNODE(LidarProcessSubnode);

于是就会生成一个名称为ObjectFactoryLidarProcessSubnode的类,该类继承自apollo::perception::ObjectFactory,并且其中包含了名称为RegisterFactoryLidarProcessSubnode的函数。

Prediction(预测)

模块介绍

Prediction模块从Perception模块接受障碍物信息。该模块需要的信息包括位置,航向,速度,加速度,并产生具有障碍概率的预测轨迹。

模块输入:

  • 来自Prediction模块的障碍物信息
  • 来自Localizaton模块的位置信息

模块输出:

  • 障碍物的预测轨迹

模块解析

Prediction的Init函数中添加了三个回调用来从其他模块获取信息的更新:

AdapterManager::AddLocalizationCallback(&Prediction::OnLocalization, this);
AdapterManager::AddPlanningCallback(&Prediction::OnPlanning, this);
AdapterManager::AddPerceptionObstaclesCallback(&Prediction::RunOnce, this);

这里最重要的就是Prediction::RunOnce这个函数。这个函数中包含了Prediction模块的主要逻辑,它会在接收到一个新的障碍物消息时触发。

Prediction模块中有三类重要的子模块。

第一类是Container,用来存储从订阅频道获取的数据。包括:

  • 感到到的障碍物信息
  • 车辆位置信息
  • 车辆计划信息

第二类是Evaluator,用来针对指定的障碍物预测路线和速度。目前有三类Evaluator,包括:

  • Cost evaluator:通过一组代价函数来计算可能性
  • MLP evaluator:通过MLP模型来计算可能性
  • RNN evaluator:通过RNN模型来计算可能性

Evaluator通过EvaluatorManager类管理,Evaluator类结构如下图所示:

Evaluator.png

Prediction模块中第三类重要的子模块就是Predictor。它用来预测障碍物的轨迹。

不同的障碍物运动的轨迹会不一样,因此实现中包含了很多个类型的Predictor,它们的结构如下图所示。

Predictor.png

类似的,会有一个PredictorManager来管理Predictor。

Routing(路由)

模块介绍

Routing模块根据请求生成导航信息。

模块输入:

  • 地图数据
  • 请求,包括:开始和结束位置

模块输出:

  • 路由导航信息

模块解析

Routing模块的内部结构如下图所示:

Routing_module.png

Routing模块的输入是地图数据和导航请求,因此其Init函数就是围绕这个逻辑的:

apollo::common::Status Routing::Init() {
  const auto routing_map_file = apollo::hdmap::RoutingMapFile();
  AINFO << "Use routing topology graph path: " << routing_map_file;
  navigator_ptr_.reset(new Navigator(routing_map_file));
  CHECK(common::util::GetProtoFromFile(FLAGS_routing_conf_file, &routing_conf_))
      << "Unable to load routing conf file: " + FLAGS_routing_conf_file;

  AINFO << "Conf file: " << FLAGS_routing_conf_file << " is loaded.";

  hdmap_ = apollo::hdmap::HDMapUtil::BaseMapPtr();
  CHECK(hdmap_) << "Failed to load map file:" << apollo::hdmap::BaseMapFile();

  AdapterManager::Init(FLAGS_routing_adapter_config_filename);
  AdapterManager::AddRoutingRequestCallback(&Routing::OnRoutingRequest, this);
  return apollo::common::Status::OK();
}

这段代码的重点是下面三个地方:

  • apollo::hdmap::RoutingMapFile()包含了HD地图数据。
  • Navigator负责导航,我们很容易想到这个类应当是该模块的核心。
  • Routing::OnRoutingRequest是接收导航请求的回调函数。

Routing::OnRoutingRequest中,最主要的就是通过Navigator::SearchRoute来搜索导航路径。

void Routing::OnRoutingRequest(const RoutingRequest& routing_request) {
  AINFO << "Get new routing request:" << routing_request.DebugString();
  RoutingResponse routing_response;
  apollo::common::monitor::MonitorLogBuffer buffer(&monitor_logger_);
  const auto& fixed_request = FillLaneInfoIfMissing(routing_request);
  if (!navigator_ptr_->SearchRoute(fixed_request, &routing_response)) {
    AERROR << "Failed to search route with navigator.";

    buffer.WARN("Routing failed! " + routing_response.status().msg());
    return;
  }
  buffer.INFO("Routing success!");
  AdapterManager::PublishRoutingResponse(routing_response);
  return;
}

目前,Apollo 2.5版本中的导航基于A*算法。这是一种在图形平面上,有多个节点的路径,求出最低通过成本的算法。该算法综合了Best-First Search和Dijkstra算法的优点:在进行启发式搜索提高算法效率的同时,可以保证找到一条最优路径(基于评估函数)。

在A*算法计算的过程中,会尝试多条路径。一旦遇到障碍物,便将该路径上的点标记为不需要继续探索(图中的实心点)。继续以剩下的空心点为基础探索。最终求得最优路径。

下图动态描述了A*算法查找目标路径的算法过程。

Astar_progress_animation.gif

Planing(计划)

模块介绍

Planing模块根据定位信息,车辆状态(位置,速度,加速度,底盘),地图,路线,感知和预测,计算出安全和舒适的形式线路让控制器执行。

目前的系统实现中包含了四种计划器:

  • RTKReplayPlanner(自Apollo 1.0以来):RTK重放计划器首先在初始化时加载记录的轨迹,并根据当前系统时间和车辆位置发送适当的轨迹段。
  • EMPlanner(自Apollo1.5以来。EM是Expectation Maximization的缩写):EM计划器,会根据地图,路线和障碍物计算驾驶决策和线路。基于动态规划(Dynamic programming,简称DP)的方法首先用于确定原始路径和速度曲线,然后使用基于二次规划(Quadratic programming,简称QP)的方法来进一步优化路径和速度曲线以获得平滑的轨迹。
  • LatticePlanner:网格计划器
  • NaviPlanner:这是一个基于实时相对地图的计划器。它使用车辆的FLU(Front-Left-Up)坐标系来完成巡航,跟随,超车,接近,变道和停车任务。

模块输入:

  • RTK重放计划器:

    • Localization
    • 记录的RTK轨迹
  • EM计划器:

    • Localization
    • Perception
    • Prediction
    • HD Map
    • Routing

模块输出:

  • 无碰撞和舒适的轨迹让控制模块的执行

模块解析

Planing模块在初始化的Init函数中,这里面会注册所有的计划器,然后根据配置文件中的配置确定当前所使用的计划器。目前,配置文件中配置的是EM计划器。

Planing模块在Start函数中设定了一个Timer用来完成定时任务:

Status Planning::Start() {
  timer_ = AdapterManager::CreateTimer(
      ros::Duration(1.0 / FLAGS_planning_loop_rate), &Planning::OnTimer, this);
...

Planning::OnTimer最主要的就是调用RunOnce(),而后者包含了Planing模块的核心逻辑。目前,FLAGS_planning_loop_rate值是10。也就是说,Planing模块运行的频度是每秒钟10次。

在Planning::Plan函数中,更通过配置的计划器进行路线的计算,然后将结果对外发布。关键代码如下:

Status Planning::Plan(const double current_time_stamp,
                      const std::vector<TrajectoryPoint>& stitching_trajectory,
                      ADCTrajectory* trajectory_pb) {
  auto* ptr_debug = trajectory_pb->mutable_debug();
  if (FLAGS_enable_record_debug) {
    ptr_debug->mutable_planning_data()->mutable_init_point()->CopyFrom(
        stitching_trajectory.back());
  }

  auto status = planner_->Plan(stitching_trajectory.back(), frame_.get());

  ExportReferenceLineDebug(ptr_debug);

  const auto* best_ref_info = frame_->FindDriveReferenceLineInfo();
  if (!best_ref_info) {
    std::string msg("planner failed to make a driving plan");
    AERROR << msg;
    if (last_publishable_trajectory_) {
      last_publishable_trajectory_->Clear();
    }
    return Status(ErrorCode::PLANNING_ERROR, msg);
  }
  ptr_debug->MergeFrom(best_ref_info->debug());
  trajectory_pb->mutable_latency_stats()->MergeFrom(
      best_ref_info->latency_stats());
  // set right of way status
  trajectory_pb->set_right_of_way_status(best_ref_info->GetRightOfWayStatus());
  for (const auto& id : best_ref_info->TargetLaneId()) {
    trajectory_pb->add_lane_id()->CopyFrom(id);
  }

  best_ref_info->ExportDecision(trajectory_pb->mutable_decision());

  ...
  
  last_publishable_trajectory_->PrependTrajectoryPoints(
      stitching_trajectory.begin(), stitching_trajectory.end() - 1);

  for (size_t i = 0; i < last_publishable_trajectory_->NumOfPoints(); ++i) {
    if (last_publishable_trajectory_->TrajectoryPointAt(i).relative_time() >
        FLAGS_trajectory_time_high_density_period) {
      break;
    }
    ADEBUG << last_publishable_trajectory_->TrajectoryPointAt(i)
                  .ShortDebugString();
  }

  last_publishable_trajectory_->PopulateTrajectoryProtobuf(trajectory_pb);

  best_ref_info->ExportEngageAdvice(trajectory_pb->mutable_engage_advice());

  return status;
}

Control(控制)

模块介绍

控制模块根据计划和当前的汽车状态,使用不同的控制算法来生成舒适的驾驶体验。控制模块可以在正常模式和导航模式下工作。

模块输入:

  • 计划的线路
  • 车辆状态
  • 定位信息
  • Dreamview AUTO模式更改请求

模块输出:

  • 控制命令(转向,油门,刹车)到底盘

模块解析

Control模块的主体逻辑也是通过Timer定时执行的。在定时触发的函数Control::OnTimer中,会生成命令然后派发出去:

void Control::OnTimer(const ros::TimerEvent &) {
  double start_timestamp = Clock::NowInSeconds();

  if (FLAGS_is_control_test_mode && FLAGS_control_test_duration > 0 &&
      (start_timestamp - init_time_) > FLAGS_control_test_duration) {
    AERROR << "Control finished testing. exit";
    ros::shutdown();
  }

  ControlCommand control_command;

  Status status = ProduceControlCommand(&control_command);
  AERROR_IF(!status.ok()) << "Failed to produce control command:"
                          << status.error_message();

  double end_timestamp = Clock::NowInSeconds();

  if (pad_received_) {
    control_command.mutable_pad_msg()->CopyFrom(pad_msg_);
    pad_received_ = false;
  }

  const double time_diff_ms = (end_timestamp - start_timestamp) * 1000;
  control_command.mutable_latency_stats()->set_total_time_ms(time_diff_ms);
  control_command.mutable_latency_stats()->set_total_time_exceeded(
      time_diff_ms < control_conf_.control_period());
  ADEBUG << "control cycle time is: " << time_diff_ms << " ms.";
  status.Save(control_command.mutable_header()->mutable_status());

  SendCmd(&control_command);
}

Control模块内置了三个控制器,它们的结构和说明如下:

Controller.png

  • LatController:基于LQR的横向控制器,计算转向目标。详见:Vehicle Dynamics and Control
  • LonController:纵向控制器,计算制动/油门值。
  • MPCController:MPC控制器,组合了横向和纵向控制器。

结束语

本文主要以Apollo项目2.5版本为基础做了一些调查分析。

上文中我们也提到,目前的2.5版本仅仅是针对L2级自动驾驶的,而百度计划在2019年实现L3级自动驾驶,2021年实现L4级自动驾驶。可见,这个项目接下来的时间里将会非常高速的发展。

另外,今年我刚好有机会参加了上海的CES展。在展会上也看到了百度展出的两款自动驾驶车型:

一款是小型巴士。

IMG_0619.jpg

还有一款是小型物流车。

IMG_0620.jpg

今后我也会继续保持对该项目的关注,如果有更多的信息会继续分享给大家。

参考资料与推荐读物

目录
相关文章
|
2月前
|
机器学习/深度学习 人工智能 自然语言处理
Hugging Face 论文平台 Daily Papers 功能全解析
【9月更文挑战第23天】Hugging Face 是一个专注于自然语言处理领域的开源机器学习平台。其推出的 Daily Papers 页面旨在帮助开发者和研究人员跟踪 AI 领域的最新进展,展示经精心挑选的高质量研究论文,并提供个性化推荐、互动交流、搜索、分类浏览及邮件提醒等功能,促进学术合作与知识共享。
|
14天前
|
机器学习/深度学习 人工智能 自然语言处理
医疗行业的语音识别技术解析:AI多模态能力平台的应用与架构
AI多模态能力平台通过语音识别技术,实现实时转录医患对话,自动生成结构化数据,提高医疗效率。平台具备强大的环境降噪、语音分离及自然语言处理能力,支持与医院系统无缝集成,广泛应用于门诊记录、多学科会诊和急诊场景,显著提升工作效率和数据准确性。
|
17天前
|
供应链 安全 BI
CRM系统功能深度解析:为何这些平台排名靠前
本文深入解析了市场上排名靠前的CRM系统,如纷享销客、用友CRM、金蝶CRM、红圈CRM和销帮帮CRM,探讨了它们在功能性、用户体验、集成能力、数据安全和客户支持等方面的优势,以及如何满足企业的关键需求,助力企业实现数字化转型和业务增长。
|
6月前
|
边缘计算 Cloud Native 数据管理
【阿里云云原生专栏】云原生背景下的AIoT布局:阿里云Link平台解析
【5月更文挑战第29天】阿里云Link平台,作为阿里云在AIoT领域的核心战略,借助云原生技术,为开发者打造一站式物联网服务平台。平台支持多协议设备接入与标准化管理,提供高效数据存储、分析及可视化,集成边缘计算实现低延时智能分析。通过实例代码展示,平台简化设备接入,助力智能家居等领域的创新应用,赋能开发者构建智能生态系统。
180 3
|
2月前
|
数据挖掘 BI UED
B2B 领域 CRM 平台全景解析
在快节奏的商业环境中,移动CRM应用让企业随时随地管理客户关系,成为不可或缺的利器。本文深入探讨了七款优秀移动CRM应用:销售易Mobile、Salesforce Mobile、纷享销客、Zoho CRM Mobile、HubSpot Mobile、金蝶云·星辰移动端及用友U8+移动端,详细分析了各自的优势和适用场景。企业可根据具体需求、预算和行业特点,选择最适合的移动CRM解决方案,提升销售效率与管理水平,为企业发展注入新活力。
B2B 领域 CRM 平台全景解析
|
3月前
|
vr&ar
简单易懂的 全景图高清下载方法以及原理简要解析(支持下载建E、720yun、酷雷曼、景站、酷家乐、百度街景原图)
这篇文章介绍了一种简单易懂的全景图高清下载方法,使用在线网站全景管家,支持下载包括建E、720yun、酷雷曼等多个平台的全景图原图,并简要解析了全景图的原理和制作方法。
简单易懂的 全景图高清下载方法以及原理简要解析(支持下载建E、720yun、酷雷曼、景站、酷家乐、百度街景原图)
|
2月前
|
API 云计算 开发者
使用宜搭平台带来的便利:技术解析与实践
【9月更文第8天】随着企业信息化建设的不断深入,业务流程自动化的需求日益增长。宜搭平台作为一种高效的应用构建工具,为企业提供了快速搭建各类业务系统的可能。本文将探讨使用宜搭平台给企业和开发者带来的便利,并通过具体的代码示例展示其优势。
89 11
|
2月前
|
监控 算法 数据可视化
深入解析Android应用开发中的高效内存管理策略在移动应用开发领域,Android平台因其开放性和灵活性备受开发者青睐。然而,随之而来的是内存管理的复杂性,这对开发者提出了更高的要求。高效的内存管理不仅能够提升应用的性能,还能有效避免因内存泄漏导致的应用崩溃。本文将探讨Android应用开发中的内存管理问题,并提供一系列实用的优化策略,帮助开发者打造更稳定、更高效的应用。
在Android开发中,内存管理是一个绕不开的话题。良好的内存管理机制不仅可以提高应用的运行效率,还能有效预防内存泄漏和过度消耗,从而延长电池寿命并提升用户体验。本文从Android内存管理的基本原理出发,详细讨论了几种常见的内存管理技巧,包括内存泄漏的检测与修复、内存分配与回收的优化方法,以及如何通过合理的编程习惯减少内存开销。通过对这些内容的阐述,旨在为Android开发者提供一套系统化的内存优化指南,助力开发出更加流畅稳定的应用。
70 0
|
3月前
|
传感器 自动驾驶 安全
计算机视觉在自动驾驶中的应用:技术解析与未来展望
【8月更文挑战第4天】自动驾驶依托计算机视觉实现环境感知与决策,通过目标检测、跟踪及车道识别等技术保障行车安全与效率。面对数据处理、场景理解等挑战,未来技术将持续优化,深化智能驾驶体验,引领交通行业变革。
|
2月前
|
图形学 iOS开发 Android开发
从Unity开发到移动平台制胜攻略:全面解析iOS与Android应用发布流程,助你轻松掌握跨平台发布技巧,打造爆款手游不是梦——性能优化、广告集成与内购设置全包含
【8月更文挑战第31天】本书详细介绍了如何在Unity中设置项目以适应移动设备,涵盖性能优化、集成广告及内购功能等关键步骤。通过具体示例和代码片段,指导读者完成iOS和Android应用的打包与发布,确保应用顺利上线并获得成功。无论是性能调整还是平台特定的操作,本书均提供了全面的解决方案。
150 0

推荐镜像

更多