Apollo 2.0 框架及源码分析(一) | 软硬件框架

Apollo 2.0 框架及源码分析(一) | 软硬件框架

冀渊冀渊

前言

如引言中介绍的,这篇软硬件框架多为现有消息的整合加一些个人的想法。关于 Apollo 介绍的文章已经有许多,很多要点大家都已经足够了解,这些点在本篇中会被略写。本篇文章如有错误、不足之处,还请大家多多指教。

Apollo 官方 GitHub 链接:Apollo Auto


在历经1.0,1.5 两个版本更新之后,百度无人驾驶平台 Apollo 2.0 在2018年初正式发布。其技术框架如下图所示 [1]

图片来源:微信公众号:自动驾驶干货铺

如上图所示,Apollo 技术框架主要由四部分组成,从下往上,依次为:

  • Reference Vehicle Platform: 参考汽车层,用于对汽车实现电子化的控制。这一层涉及到的 CAN 协议一般由主机厂制订,且严格保密。百度早期使用了 AutonomouStuff 公司改装的 Lincoln MKZ 来规避这一问题[2]
  • Reference Hardware Platform: 参考硬件层,为 Apollo 推荐的硬件设备,如: 计算单元(工控机),GPS/IMU,LiDAR,Radar 等等。
  • Open Software Platform: 开放软件平台, 为 Apollo 的核心部分。
  • Cloud Service Platform: 云端服务层,为百度可提供的数据,高精度地图等各项云服务,应该也是百度之后盈利的主要来源。

本文重点叙述 Reference Hardware Platform 和 Open Software Platform 这两层。

Reference Hardware Platform

百度建议的硬件安装示意图如下所示 [3]:


侧视图 (图片来源:Apollo 技术社区)
右视图 (图片来源:Apollo 技术社区)

可见其采用的感知方案还是为 LiDAR, Radar,摄像头相配合的传统方案。Apollo 推荐的核心部件为:

推荐的硬件配置

从价格上看,百度 Apollo 2.0 不可能是一个量产的方案。作为大脑的工控机价格在 $2000 美元左右,Velodyne HDL-64E 的售价至少在 $75,000 美元。 这些过于昂贵的传感器使得 Apollo 更像是一个 Demo 的解决方案。

值得一提的是,从 Apollo 感知模块的代码中可以看到,Apollo 2.0 也同时为 Velodyne 16线激光雷达留下接口。

enum SensorType {
  VELODYNE_64 = 0,
  VELODYNE_16 = 1,
  RADAR = 2,
  CAMERA = 3,
  UNKNOWN_SENSOR_TYPE = 10,
};

联想到最近 Velodyne 宣布16线激光雷达最高降价50%的消息,售价可能会低至 $4000 美元[4]。那么随着激光雷达价格近一步的降低,Apollo 感知融合算法的进一步完善,从传感器的成本来看,在特定场景下的自动驾驶还是有望实现的。

另外 Apollo 的传感器配置也显得不太丰富,在车身侧面和后面都没有安放雷达。相比较奥迪A8 几乎武装到牙齿,Apollo 传感器上的冗余度总觉得是有些不足 (虽然这样比可能有点不公平,奥迪A8是量产车型,且使用的是4线的激光雷达)。

另外摄像头安装在了车顶而不是车窗内,那么如何在雨天保证摄像头不受影响也是一个问题。


Open Software Platform

Apollo 的 Open Software Platform 分三个部分 RTOS,Runtime Framework 及各个功能模块 Modules。

RTOS

Apollo RTOS的主体部分是为 Ubuntu 14.04 + ApolloAuto/apollo-kernel 的组合。 Apollo 安装指南上推荐的在工控机上安装的软件及版本如下表所示。

IPC 安装的软件

Apollo-kerne 的存在是因为 Ubuntu 并不是一个实时系统。

实时系统的定义如下:

实时操作系统(RTOS)是指当外界事件或数据产生时,能够接受并以足够快的速度予以处理,其处理的结果又能在规定的时间之内来控制生产过程或对处理系统做出快速响应,调度一切可利用的资源完成实时任务,并控制所有实时任务协调一致运行的操作系统。提供及时响应和高可靠性是其主要特点。[实时操作系统_百度百科]

简单说,实时系统保证了某任务在 deadline 之前被完成。系统的实时性在汽车上的重要性是不言而喻的,我用一个视频来说明下其重要程度 (最早看到这段视频是在学校实时系统课的官网上,十分震撼,二话没说直接就去上课了...)。

https://www.zhihu.com/video/940563694309113856

针对于此,百度在 Ubuntu 的基础上提供了 apollo-kerne 来保证系统的实时性。Apollo-kerne 主要使用了一个名为 PREEMPT_RT 的 Linux 实时补丁包,并在此基础上做了部分改动。这个实时内核在非生产环境下不是必须的,在生产环境中 Apollo 官方是建议安装这个内核的[5]


Runtime Framework

Runtime Framework 是 Apollo 软件的运行环境,即 ApolloAuto/apollo-platform 的部分。Apollo 的 Runtime Framework 是一个定制版的 Robot Operating System (ROS) 。针对 ROS 系统的不足百度对 ROS 做了以下三方面的改动 [6]

1. ROS Decentralization Feature
2. High Efficient Communication based on Shared Memory Transport Feature
3. Native Support with Protobuf Feature

简单翻译下是

  • 去中心化
  • 共享内存以提升节点间的传输效率
  • 原生支持 Protobuf

这里有篇文章很详细的阐述了 Apollo 对 ROS 的做的改动,我在这里只把这三点再简单叙述一下。

干货曝光(二)|资深架构师首次解密Apollo ROS有何不同!mp.weixin.qq.com图标


去中心化

ROS 在安全性上的一个不足是 ROS 需要有一个节点作为主服务器,用于建立各节点之间的通信连接。这一机制使得ROS节点的容错性增强,各模块的隔离程度增高,但也带来了单点失效(single-point failure)的风险。由于 ROS 本身缺乏针对这种状况的异常恢复机制,当服务器宕机时,整个系统会崩溃。这种情况如果发生在自动驾驶行驶过程中,无疑会造成车毁人忙的后果。针对于此,Apollo 采用了FAST RTPS (real-time Publish/Subscribe)来实现去中心化。

共享内存

ROS节点之间的通信是通过 socket 完成的,在进行数据广播的时候,底层使用的是多个点对点的传送。这种方式速度比较缓慢,且使用了较多的资源。Apollo 使用共享内存的方式对其进行改进,加快了通信速率,减少了CPU损耗。

支持Protobuf

Apollo 将 google 的 Protobuf 与 ROS 深度集成,用于提高数据的版本兼容性。其优势在于当模块接口升级以后,通讯的数据也可以相互兼容。另一个好处是宝贵的自动驾驶的历史数据在模块升级后也可以一直被使用。


其实关于 ROS 的优劣和在自动驾驶中的修改解决方案在不少文章中都有被提到。我比较喜欢的一篇是刘少山团队的介绍。

【无人驾驶系列】基于ROS的无人驾驶系统-极客geek.csdn.net图标

用下面这个表格来对比下百度和刘少山对 ROS 的改进。

ROS 的不足及改进

由表格可以看出,

  • 使用共享内存的方式来提升 ROS 节点之间的通讯是一个常用的方式。
  • 刘少山团队使用了 Zookeeper 的机制来应对 ROS 单点失效的问题。也就是说,当旧的主节点失效之后,其余节点会选举出新的主节点,使得系统可以正常运行。这个问题上 Apollo 使用了 RTPS。
  • 刘少山团队使用了 Linux Container 对 ROS 的节点进行了隔离,确保当某一节点被劫持时,此节点不会影响整个系统。在 Apollo 的文档里,我没找到相对应的信息。

此外再想提一下 ROS 实时性的问题,ROS 本身不是一个实时系统。而在汽车领域,对实时的要求却很高。虽然百度使用了 PREEMPT_RT 内核来增强了 ROS 所在的 linux 环境的实时性,但我不认为这会彻底解决 ROS 本身弱实时性的问题。如果这一问题能这么轻易的被一个补丁包解决的话,ROS 2.0 也不会进展如此缓慢了。想必百度也深知这一点,所以才会寻求和黑莓的嵌入式实时系统 QNX 的合作吧。

或许 Apollo 使用 ROS 也只是为了快速开发和推广宣传,在之后的量产过程中百度改用其他的框架也不是没有可能。


Modules

各个功能模块的实现是 Apollo 代码的一个重点。Modules 的代码在 Apollo/apollo/modules 中,如下表所示 :

各个模块的简单描述许多文章都有介绍,这里就不再赘述了,具体可参考以下两篇文章。

李科男:百度Apollo1.5 自动驾驶开源模块分析zhuanlan.zhihu.com图标xinhe sun:百度无人驾驶系统Apollo 2.0小解析zhuanlan.zhihu.com图标

Apollo 工作的整体流程也可参考上面提到的这篇文章 xinhe sun:百度无人驾驶系统Apollo 2.0小解析 。为方便阅读,这里把它摘过来。

首先,用户输入目的地,routing模块就可以根据终点位置计算出具体的导航信息。激光雷达、毫米波雷达和摄像头拍摄到的数据配合高精度地图由percepting模块计算出3D的障碍物信息并识别交通标志及交通信号,这些数据进入perdiction模块,计算出障碍物的可能轨迹,如此就可以结合以上信息并根据车辆定位模块localizationg提供的车辆位置由planning模块得到车辆应该走的具体车道。

得到车道后车辆control模块结合车辆的当前状态计算加速、刹车和方向的操作信号,此信号进入CAN卡后输出到车内,如此实现了车辆的自动驾驶。

在整个流程中,monitor模块会及时监测硬件及系统的健康状况,出现问题肯定就会中止驾驶过程。对于驾驶中的信息,用户可以通过web应用dreamview来查看

对于各个模块的源码实现,可以参考这篇文章。这篇文章侧重分析各模块中函数的含义代码技巧。虽然 Apollo 1.0 稍微旧了一点,但代码量相对小,阅读一下有助于理解系统的代码框架。

slamcode:百度 自动驾驶框架 Apollo 1.0 -源码分析zhuanlan.zhihu.com图标


几个重要的功能的模块之间的关系如下图所示 [7]

Apollo 2.0 Software Architecture (图片来源:Apollo 技术社区)

从此图可以看出定位高精度地图是 Apollo 整个软件体系的基石,在代码中也会看到高精度地图起到极其重要的作用。比如在感知环节,摄像头会通过高精度地图知道交通灯所在的位置,确定 ROI 区域,从而减少计算量。这个构架也佐证了,百度一定会试图在高精度地图上分一杯羹[8]

为了后面方便叙述各模块的工作,这里简单介绍两个名词。在实时系统中一个任务的调度方式通常可大致分为两类:时间驱动(timer-triggered) 和 事件驱动(event-triggered)。

所谓时间驱动是指该任务的触发条件是时间,通常该任务为周期性的任务。比如某任务每 10ms 执行一次,那么该任务为时间驱动的任务。顺便安利嵌入式的一本书 《时间触发嵌入式系统设计模式》,思路清晰,相当实用。在 Apollo 的代码里,以时间为触发条件的模块都会提供 OnTimer 这样的一个接口。

事件驱动是指任务的触发条件为某一事件。例如驾驶员看到红灯会踩刹车,在这一操作中,踩刹车举动是由于红灯亮这一事件触发的。

了解这两个概念更有助于进一步了解每个模块是如何被调用的。接下来选几个重要的模块简单分析一下。


Localization

Localization

在这个模块里,Apollo提供了两种定位模式

  1. RTK-based,即 GNSS+IMU的传统定位方式。 该模式属于 timer-triggered, 该模式下的定位应该会被周期性的调用。
  2. 多传感器融合 (Multi-sensor Fusion Localization),即 GNSS, IMU 和 Lidar 三者配合使用,完成定位。如下图所示,大体原理应该是这样的:首先比对 Lidar 采集的点云事先建好的地图,得到 Lidar 的定位结果。之后 Lidar 定位 ,GNSS 定位,IMU 三者用卡尔曼滤波做融合。实现的具体原理参见这篇论文 [1711.05805] Robust and Precise Vehicle Localization based on Multi-sensor Fusion in Diverse City Scenes
Robust and Precise Vehicle Localization based on Multi-sensor Fusionin Diverse City Scenes


Perception

Preception

Perception 模块有两个重要的内容,即 3D障碍物感知 交通灯感知

需要交通灯感知是因为 Apollo 2.0 是简单城市路况下的自动驾驶。在高速公路之类的限定场景的自动驾驶中,交通灯的检测就不是必须了。在这个部分中,摄像头只负责感知交通灯的状态,而不负责其他障碍物的检测。

3D 障碍物感知部分使用卡尔曼滤波,融合了 lidar 和 radar 检测的结果。正常情况下,视觉检测障碍物应该也是一个很重要的部分。Apollo 2.0 没有做视觉的融合,可能是因为时间有限,该功能要在下一个版本才出现?

有趣的是当我们观察 apollo 官方库的时候,会发现有个分支叫 "mobileye_radar", 该分支4个月前就停止更新了,但该版本中也未看到 mobileye 或者视觉检测障碍物的影子。这个名称说明百度曾经也考虑过使用 mobileye 的方案?不知道基于什么样的考虑,百度没有继续采用这个方案。这些背后的事情,想想很有趣啊。

Apollo/apollo,branches: mobileye_radar

个人倾向于百度藏拙了。因为在百度其自定位模块 elo (见下图) 已经使用了摄像头来提取环境特征。那么用摄像头来完善感知部分,技术上肯定可以实现的。或者百度觉得现有的感知方案已经足够,即使再融合视觉对整体性能提高也有限。

百度自定位模块:Ego Localization (图片来源:Apollo 技术社区)


Planning

Planning 模块是 timer-triggered的, 以固定的频率被调用,该模块提供了两种方案:

1. RTK replay planner (since Apollo 1.0)
2. EM planner (since Apollo 1.5)

这里引用下 @xinhe sun 的回答 [9]

规划车道有两种方式,一种是提前把轨迹存入程序,然后根据车辆状态和位置提取轨迹,另外一种是实时计算。

Apollo 最新在开发的是 lattice planner 决策算法[10] (应该不属于 Apollo 2.0 的范畴了)。

Lattice Planner是基于斯坦福大学在DARPA挑战赛中使用过的一种路径规划算法,优势在于更好地理解交通情况,降低规划的复杂性,高速情况表现优异。感兴趣的同学可以参考Moritz Werling等人所著论文:
<Optimal Trajectory Generation for Dynamic Street Scenarios in a Frene´t Frame>
pdfs.semanticscholar.org


Control & Canbus


Control & Canbus


如图所示,Control 控制模块提供了三个主要的数据接口: OnPad,OnMonitor 和 OnTimer。 其中 OnPad 和 OnMonitor 用于仿真和人机交互,OnTimer 用于周期性的指令传递。

控制模块以决策模块生成的轨迹为输入,计算出底层所需的具体的指令,比如加速,减速,转向等等,并通过OnTimer 接口,周期性的向 Canbus 传递控制指令。Canbus 一旦接收到具体的指令之后就会调用对应的函数执行此操作。与此同时,Canbus 也会周期性的向 Control 模块发送汽车当前的信息。

我比较好奇的一点是,Apollo 会怎么使用这些汽车底盘信息。光从图中的信息的流动上看,Control 端并没有继续向 Planning 或者其他上层模块发送底盘信息。但根据技术文档,在Planning 模块中这些信息有被使用 [7]

Finally, the planning module needs to know the location (Localization: where I am) as well as the current autonomous vehicle information (Chassis: what is my status).

我的一个猜测是这些信息目前只被用于监控汽车的当前状况。从这一点看,Apollo 也许可以考虑在将来结合更多的底盘信息来进一步优化整个系统。譬如结合汽车的里程数、轮速等等来提高汽车定位的精度。


以上,感谢阅读。

如需转载,请注明出处。


相关链接:

Apollo 2.0 框架及源码分析(零) | 引言


参考来源:

[1] 无人驾驶技术入门(二)——不会写代码也能做无人驾驶工程师

[2] 无人驾驶技术入门(一)| 百度无人驾驶的引路人

[3] apollo_2_0_hardware_system_installation_guide_v1

[4] 新年伊始,Velodyne带来利好消息:16线激光雷达价格减半 | 雷锋网

[5]【Apollo直答号】Apollo的实时内核必须安装吗?安装和不安装有什么区别?

[6] ApolloAuto/apollo-platform

[7] Apollo_2.0_Software_Architecture

[8] 大家都在关注百度Apollo,但你们的重点选错了 【图】- 车云网

[9] 百度无人驾驶系统Apollo 2.0小解析

[10] 【Apollo直答号】Apollo2.0的定位模块中LiDAR定位使用的预建地图是LiDAR-SLAM地图吗?

文章被以下专栏收录
31 条评论