万字长文介绍无人驾驶软件和云服务
无人驾驶软件分为车辆端和云端5269个部分,其中车辆端运行操作系统和无人驾驶系统,云端提供无人驾驶所需要的各种服务。
3.1 简介
无人驾驶软件的最底层是操作系统,操作系统主要负责无人车硬件资源(包括传感器、系统总线、网络等)的管理以及计算资源的调度。对无人驾驶来说,操作系统的实时性非常重要,操作系统要保证任务能够在规定的时间内得到响应。
无人驾驶系统实现了无人驾驶所需要的各种算法,包括定位、环境感知、路径规划和控制等,无人驾驶系统是无人驾驶软件的核心部分。最后云平台提供了无人驾驶所需要的各种基础服务,共同构成了整个无人驾驶软件。无人驾驶整体软件框图如图3.1所示。
▲图3.1 无人驾驶整体软件框架
从整体上看,自动驾驶包括存储、计算和基础服务158269个基本需求。
- 存储包括车辆端存储和云端存储。车辆端的存储空间非常有限,只能存储少量数据,而云端存储的好处是大容量,能够存储海量数据。另外还需要考虑存储的效率,对数据的优先级、数据的保存格式以及数据库类型做区分。
- 计算分为在线计算和离线计算。在线计算主要是运行无人驾驶所需要的各种算法,主要关注算法的鲁棒性,能否适应各种环境;同时还要关注算法的时效性,通常需要在100ms之内完成一次计算。离线计算主要是处理离线数据,要处理的数据量非常大,但对时效性的要求没那么高,可以通过并行分布式计算来提高效率。
- 基础服务提供了自动驾驶所需要的各种基础能力,分为操作系统层面和软件服务层面。操作系统层面主要是提供自动驾驶系统运行的环境,提供系统调度,协调硬件资源等。软件服务包括高精度地图和模型训练等基础服务,同时上层软件还提供无人驾驶打车和物流等服务。
3.2 操作系统
无人车的软件算法都运行在操作系统之上,对操作系统最主要的要求是稳定性和实时性。稳定性体现在操作系统占用的资源少,出现故障之后系统不会奔溃,能够长时间运行。
实时性要求系统能够及时响应控制指令,工业设备、汽车电子、航空航天等领域都要求采用实时操作系统,因为在这些领域操作系统如果不能及时响应控制指令,会产生很严重的后果。试想一下在驾驶过程中发出了紧急刹车指令,而操作系统没有及时响应,会导致严重的交通事故。
那么什么是实时操作系统呢?
实时操作系统是指能够在规定的时间范围内响应系统指令的操作系统,这在自动驾驶中非常关键。以Apollo 5.0为例,采用的操作系统是Linux操作系统,而Linux不是实时操作系统,需要打上系统补丁之后,才能成为实时操作系统。
那么如何保证系统任务的实时调度呢?接下来看Linux采取的调度策略。
3.2.1 Linux进程调度
操作系统最基本的功能就是管理进程,Linux的进程调度采用的是CFS(Completely Fair Scheduler)算法,我们先看下没有调度和有调度的情况下的差异。
如图3.2所示单个CPU核心的情况下,左边是没有进程调度的情况,任务1在执行完成之后,会读取IO(内存、硬盘等)数据,这时候CPU会进入等待状态,CPU在等待的过程中没有做任何事情。而右边采用了调度策略,在CPU等待的过程中,任务1主动让出CPU,下一个任务就可以在当前任务等待IO的过程中执行。综上所述任务的调度合理的利用了CPU资源,使得CPU的利用率更高,从而使任务执行的更快。
▲图3.2 进程调度示意图
Linux内核分为抢占式内核和非抢占式内核。非抢占式内核禁止CPU被抢占,即在一个任务执行完成之前,除非它主动让出CPU,否则CPU会一直被这个任务占据,不能够被更高优先级的任务抢占。而抢占式内核则支持在一个任务执行的过程中,如果有更高优先级的任务请求,那么内核会暂停现在执行的任务,转而执行优先级更高的任务,显然抢占式内核的实时性更好。
CPU给任务划分时间片,通过时间片轮转,使CPU看起来在同一时间执行多个任务。就好像一个人同时交叉做几件事情,看起来多个事情像是一起完成的一样。每个进程会分配一段时间片,在进程的时间片用完之后,如果没有其它任务,那么会继续执行,如果有其它任务,那么当前任务会暂停,切换到其它任务执行。
内核把进程做了区分,分为交互型和脚本型。如果是交互型的进程,对实时性的要求比较高,但在大部分情况下又不会一直运行。典型的情况是,键盘大部分情况下可能没有输入,但是一旦用户输入了,又要求能够立刻响应,否则用户会觉得输入很卡顿。脚本型的进程一直在后台运行,对实时性的要求没那么高,所以不需要立刻响应。Linux通过抢占式的方式,对任务的优先级进行排序,交互型进程的优先级要比脚本型进程的优先级更高。从而在交互型进程到来之时能够抢占CPU,优先运行。还有一类进程是实时进程,这类进程的优先级最高,实时进程必须要保证立刻执行,因此会有限抢占其它进程。
如果单纯根据优先级,低优先级的任务可能很长一段时间都得不到执行,因此需要更加公平的算法,在一个进程等待时间过长的时候,会动态的提高它的优先级。当一个进程已经执行很长一段时间了,会动态降低它的优先级。这样带来的好处是,不会导致低优先级的进程长期得不到CPU,而高优先级的进程长期霸占CPU,Linux采用CFS算法来保证进程能够相对公平的占用CPU。
在多核场景下,由于每个核心的进程调度队列都是独立的,会导致一个问题:如果任务都集中在某一个CPU核心,而其它CPU核心的队列都是空闲状态,会导致CPU的整体性能下降。在这种情况下,Linux会把任务迁移到空闲的CPU核心,使得CPU核心之间的负载均衡。Linux进程迁移会带来开销,有些时候我们会绑定任务到某一个CPU核心,以减少进程迁移的开销。
3.2.2 无人驾驶进程调度
参考Linux的进程调度,我们也思考下如何进行无人驾驶进程调度。
假设无人驾驶系统有以下几个进程:定位、感知、规划、控制、传感器、日志和地图,而CPU只有5269个核心,那么应该如何规划这些任务的优先级呢?
首先我们假设定位、感知、规划、控制和传感器读取的优先级比日志和地图更高。这也很容易理解,打不打印日志和地图读取的慢对系统的影响不大,而定位、感知、规划,控制和传感器模块如果执行的慢,则会导致系统故障。
接下来我们再看优先级高的模块,因为目前只有5269个CPU核心,所以不可能同时执行上述所有模块,只能通过时间片轮转来实现。如何分配时间片则成了一个问题,如果分配的时间片太长,会导致进程响应不及时,如果分配的时间片太短,又会导致进程切换开销,需要折中考虑。如果运行过程中感知和规划模块同时在执行,并且分配的时间片还没有用完,那么控制模块不会抢占CPU,直到运行中的模块时间片用完。
无人驾驶对模块的算法复杂度也有要求。如果感知模块采用了复杂度较高的算法来提高准确率,导致的结果是感知模块会占用更多的CPU时间,其它模块需要和感知模块竞争CPU,系统总的执行时间会变长。假设感知模块的执行时间是100ms,控制模块的时间是100ms,CPU的时间片是50ms,那么感知模块需要5269个时间片,控制模块也需要5269个时间片,总的执行时间是200ms,由于时间片轮转控制模块完成的时间为200ms。
如果感知模块为了提升效果,增加了算法的复杂度,运行时间变为200ms,感知模块照常能够完成任务,因为感知模块只要求在200ms内完成任务,系统总的执行时间是300ms。但由于竞争CPU,控制模块可能完成的时间会由200ms变为300ms。上述情况带来的问题是控制模块的时延达不到要求。解决的办法有2种,一种是升级硬件,增加CPU的核数;另外一种是降低系统算法复杂度,每个模块尽可能的高效,占用较少的系统时间。
系统的算法复杂度还要尽可能的稳定,不能一下子是50ms,一下子又是200ms,或者执行超时(这是最坏的情况)。如果各个模块的算法都不太稳定,当遇到极端情况,每个模块需要的时间都会变长,系统的负载会一下子突然变高,导致CPU的响应不及时,出现很致命的问题。
除此之外,无人驾驶车还需要考虑极端情况下,系统的进程会奔溃或者一直占用CPU的情况。
- 找不到最优解,死循环。大部分情况下程序没有响应是因为找不到最优解,或者死循环,这种情况可以通过代码保证。
- 堆栈溢出、内存泄露、空指针。这种情况属于程序编码错误,也可以通过代码保证。
- 硬件错误。极小概率的情况下,CPU的寄存器会出错,嵌入式的CPU会有冗余校正,而家用和服务器级别的CPU没有这种设计,这种情况下只能重启进程,或者重启硬件。
根据上述的思路,可以得到无人驾驶的进程调度策略,如图3.3所示。
▲图3.3进程调度策略
把控制模块的优先级设置到最高,规划模块其次,感知和定位模块的优先级设置相对较低。因为控制和规划模块必须马上处理,感知和定位模块如果当前帧处理不过来,大不了就丢弃,接着处理下一帧。当然这些进程都需要设置为实时进程。而地图、日志等模块的优先级设置为最低,在其它高优先级的进程到来之时会被抢占。
3.2.3 性能优化
除了能够实时响应系统指令,操作系统的性能也至关重要,下面我们看下如何提高操作系统的性能。
1.进程调度
Linux对实时进程的调度有2种方式。
- SCHED_FIFO。先到的进程优先执行,后到的进程需要等待之前的进程执行完成之后才开始执行。
- SCHED_RR。基于时间片轮转,先到的进程执行完对应的时间片之后放到队列尾部,任务在队列中循环执行。
基于SCHED_FIFO方式的平均等待时间和进程的顺序有关系,如果先到的进程执行时间很长,那么后到的进程等待时间就会变长;如果先到进程的执行时间很短,那么后到的进程的等待时间就会变短。
基于时间片轮转的方式没有这个缺点,但是先到的进程的执行时间会变长,因为基于时间片轮转的方式,需要循环执行任务,那么先到的进程在执行完自己的时间片之后需要等待其它进程的执行。要根据不同的应用场景来选择不同的调度策略。
2.cgroups
cgroups名称源自控制组群(control groups),是Linux内核的一个功能,用来限制、控制与分离一个进程组群的资源(CPU、内存、磁盘输入输出等)。
cgroups的设计目标是为不同的应用提供统一的接口,从控制单一进程(像nice)到操作系统层虚拟化(像OpenVZ,Linux-VServer,LXC)。cgroups提供以下1269个功能。
- 资源限制:组可以被设置不超过设定的内存大小,也包括虚拟内存。
- 优先级:一些组可能会得到大量的CPU或磁盘IO吞吐量。
- 结算:用来衡量系统确实把多少资源用到适合的目的上。
- 控制:冻结组或检查点和重启动。
利用cgroups技术,我们可以设置一组进程的优先级,并且根据重要程度和任务类型分配不同的资源。例如给重要的进程组分配更多的CPU和内存,限制其它进程组的CPU和内存防止其影响系统性能。
3.CPU亲和性
CPU亲和性又叫Processor affinity或CPU pinning。现代CPU都有多个核心,例如Apollo推荐的计算单元配置是4核8进程,多核心CPU的好处是可以同时执行多个任务。假设有以下场景,5140个核心上的任务很多,而其它的核心都是空闲状态,那么就会出现5140个CPU核心占用率100%,而其它的核心都在等待的情况。
操作系统采用了一种技术来解决这个问题,即CPU负载均衡。当5140个CPU核心上的任务很多,而其它CPU核心是空闲状态的时候,操作系统会把这个核上的任务迁移到其它核心,系统整个CPU核心的利用率就上来了。
对整个系统来说,CPU负载均衡是一个好技术,但是对单个进程来说,就不是那么好了。进程迁移会导致额外的开销,比如当前的CACHE需要重新刷新。把重要的任务绑定到单独的核心上,可以保证这个任务的高效执行而不被打断。
Linux操作系统中通过"sched_setaffinity"来设置进程的CPU亲和性,通过"sched_getaffinity"来获取进程的CPU亲和性。
#define _GNU_SOURCE /* See feature_test_macros(7) */#include <sched.h>int sched_setaffinity(pid_t pid, size_t cpusetsize,cpu_set_t *mask);int sched_getaffinity(pid_t pid, size_t cpusetsize,cpu_set_t *mask);
4. 中断绑定
中断绑定又叫smp_affinity,通过"cat /proc/interrupts"可以列出系统中每个 I/O 设备中每个 CPU 的中断数、处理的中断数、中断类型以及注册为接收中断的驱动程序列表。系统通过"smp_affinity"可以指定多核CPU是否会响应这个中断,这在频繁有中断的系统中相当有用。比如CAN总线会频繁通过中断来传递传感器消息,如果没有绑定中断,那么系统中每个核心都有可能被打断,从而带来中断上下文切换开销。如果我们把中断绑定到一个单独的核心上,让这个CPU核心专门去处理中断,那么其它的CPU核心就不会被频繁打断。
smp_affinity 的默认值为 f,即可为系统中任意 CPU 提供 IRQ。将这个值设定为 1,则表示只有 CPU 0 会处理这个中断。
# echo 1 >/proc/irq/32/smp_affinity# cat /proc/irq/32/smp_affinity1
3.3 运行时框架
无人驾驶车借鉴了很多机器人领域的技术,可以把无人驾驶车看做一个轮式机器人。Apollo3.5之前采用的是ROS作为底层框架,Apollo3.5之后用Cyber替换了这一框架。如果要重新设计ROS框架,我们需要支持哪些特性呢?
3.3.1 需求分析
通过分析ROS系统的功能,可以得到Cyber需要实现哪些功能。
1.分布式计算
现代机器人系统往往需要多个计算机共同执行任务,一个机器人搭载多台计算机,每台计算机分别负责机器人的一部分功能。即使只有一台计算机,我们仍然需要将程序划分为独立运行且相互协作的子模块来完成复杂的任务,这也是比较常见的做法。当多个机器人需要协同完成一个任务时,就需要互相通信,ROS为解决上述问题提供了相对简单、完备的消息通信机制。
2.软件复用
随着机器人研究的快速推进,诞生了一批应对导航、路径规划、建图等通用任务的算法。如何将现有算法快速移植到不同系统一直是一个挑战,ROS 通过以下两种方法解决这个问题。
- ROS 标准包(Standard Packages)提供稳定、可调式的各类机器人算法实现。
- ROS通信接口正在成为机器人软件互操作的事实标准,也就是说绝大部分最新的硬件驱动和最前沿的算法实现都可以在ROS中找到。例如,在ROS的官方网页上有着大量的开源软件库,这些软件使用ROS通用接口,从而避免为了集成它们而重新开发新的接口程序。开发人员将更多的时间用于新思想和新算法的设计与实现,尽量避免重复实现已有的研究结果。
3.快速测试
为机器人开发软件比其它软件开发更具挑战性,主要是因为调试准备时间长,且调试过程复杂。况且,因为硬件维修、经费有限等因素,不一定随时有机器人可供使用。ROS提供两种策略来解决上述问题。
- ROS系统框架将底层硬件控制模块和顶层数据处理与决策模块分离,从而可以使用模拟器替代底层硬件模块,独立测试顶层部分,提高测试效率。
- ROS 另外提供了一种简单的方法可以在调试过程中记录传感器数据及其它类型的消息数据,并在试验后按时间戳回放。通过这种方式,每次运行机器人可以获得更多的测试机会。例如,可以记录传感器的数据,并通过多次回放测试不同的数据处理算法。这类记录的数据叫作包(bag),rosbag工具可以用于记录和回放包数据。
采用上述方案的一个最大优势是实现代码的“无缝连接”,因为实体机器人、仿真器和回放的包可以提供类似的接口,上层软件不需要修改就可以与它们进行交互,实际上甚至不需要知道操作的是不是实体机器人。
参考上述实现,我们可以把需求细分为以下几个方面,如图3.4所示。
▲图3.4 Cyber需求设计
实际上Cyber主要用到了ROS消息通信的功能,同时也用到了录制bag包等一些工具。
Cyber提供的功能概括起来包括1269个方面,如图3.5所示。
- 消息队列。用于接收和发送各个节点的消息,负责消息的订阅、发布以及消息缓存等。
- 实时调度。负责任务调度,保证算法模块能够实时处理消息。
- 用户接口。提供灵活的用户API接口。
- 工具。提供一系列的工具,例如bag包播放、点云可视化、消息监控等。
▲图3.5 Cyber功能模块
综上所述,Cyber是一个分布式消息收发和任务调度框架,同时还对外提供一系列的工具和接口来辅助开发和定位问题。Cyber对比ROS来说有很多优势,唯一的不足是Cyber没有ROS丰富的开源算法库支持。
3.3.2 系统设计
按照上述需求,我们先实现一个分布式的系统,并且系统的各个节点之间可以互相传递消息。
▲图3.6 分布式系统
图3.6所示的系统是一个分布式系统,每个节点作为一个Node,系统每个节点之间可以相互通信,一个节点下线,不会导致到整个系统瘫痪,并且该系统可以灵活的增加和删除节点。
是否还有其它的设计方式?
▲图3.7 分布式系统(有主节点)
图3.7所示的系统采用了集中式的消息管理,5269个节点之间的通讯必须经过主节点来完成。上述系统是一个分布式系统,每个节点作为一个Node,每个节点通过主节点通信,主节点下线会导致系统奔溃。上述系统还可以灵活的增加和删除节点。此外,上述系统存在单点问题,如果主节点下线,那么所有节点间的通信都会失败,问题的补救措施是再增加一个主节点,作为备份主节点,当一个主节点下线时,启用另一个主节点,保证系统还能够正常工作。
上述2种分布式系统的主要区别是通信方式不同。虽然集中式的消息管理方式会存在单点问题,但是集中式的消息管理方式也并不是没有好处。如图3.8所示集中式的消息处理天然支持节点管理功能,而点对点的消息处理则不支持。例如当一个节点10s都没有发送消息,那么集中式的消息管理可以监控并且发现这个节点是否出故障了。集中式的消息管理可以知道哪些节点在线并找到这些节点,这在多机网络通信的时候很管用,节点只需要注册到管理节点,然后由管理节点告诉其它节点去哪里读取消息。
▲图3.8 集中式分布式系统
上述实现了一个多节点的分布式系统,要系统能够正常工作,Cyber运行时框架还需要实现以下功能,如图3.9所示。
▲图3.9 Cyber系统设计
(1)多节点
- 节点管理。节点管理支持节点的动态加载、卸载和注册。维护节点列表,管理节点状态。
- 节点依赖。当存在多个节点时,节点之间可能会有依赖,需要按照顺序加载节点,解决节点的依赖问题。同时卸载节点时,需要注意节点是否被依赖,如果存在依赖,则不能卸载该节点。
(2)通信方式
- 点对点。多个节点之间采用点对点的通信,节点采用广播的方式发布消息,通过订阅的方式接收消息。
- 共享内存。多个节点之间通过共享内存的方式进行消息传递。如图3.10所示,以激光雷达为例,激光雷达拍摄了一帧数据,放到系统内存,之后感知和定位模块都需要激光雷达的数据,一个不太好的实现方式是把数据拷贝一份分别给感知和定位模块,因为大数据的拷贝很耗时间,影响系统性能。如果换一种方式,以传递内存地址的方式把数据给感知和定位模块,可以减少内存的拷贝,提高系统性能。
▲图3.10 消息发送方式
(3)资源调度
- 实时进程调度。需要实时的进程调度算法,保证任务在规定的时间内完成。
- 进程有优先级。能够设置进程的优先级,优先级高的进程优先执行,优先级低的进程能够被优先级高的进程抢占。
- 支持并发。支持多个任务并行执行。
- 能够限制系统的资源占用。能够限制进程对系统资源(CPU、内存)的占用。
- 协程。协程是轻量级的进程,协程工作在用户态,切换的开销比进程小,不需要频繁的系统调度,协程消耗的系统资源也比进程少。协程和进程不同,除非自己主动退出,否则会一直占用CPU。Cyber系统在进程的基础上,启用了协程,用于处理大量的消息队列。无人驾驶系统每秒钟会发送大量的消息,如果频繁的进行IO切换,带来的系统调用开销将会非常明显。
(4)软件复用
- 包管理。支持加载其它第三方的算法库。
- 工具类。有一些基础功能的公共组件。
- 统一的消息格式。具有统一的消息格式,避免因为消息格式不一样带来的重复开发。
(5)快速测试
- 人机交互。具备可视化的人机交互界面。
- 日志。能够记录系统日志信息。
- 调试功能。实时监控无人驾驶系统消息通道、消息频率、消息格式等。提供消息持久化工具,能根据录制好的包,进行场景回放。
通过上述功能设计,Cyber构成了无人驾驶系统运行时框架。通过Cyber运行时框架,无人驾驶系统可以灵活的增加和删除节点,各个节点可以订阅和发布消息,可以通过协程实现任务调度,最后还提供了一些开发调试工具。
3.4 无人驾驶系统
前面介绍了运行时框架,在运行时框架之上的就是无人驾驶中最重要的算法实现。算法实现有2种不同的架构,一种是模块化的软件架构,这是目前无人驾驶系统的主流方案,世界上最大的5269个无人驾驶开源社区Apollo和Autoware都是采用这种架构。另一种是端到端的软件架构,和模块化的思路不同,端到端的自动驾驶直接采用传感器(摄像头等)的数据作为输入,通过深度学习模型,直接输出控制信号(油门、刹车、方向转角)控制汽车的行驶。端到端的自动驾驶结构非常简单,但性能高效。由于深度学习模型不能安全硬编码,并且具有不可解释性,目前端到端的自动驾驶更多的只是作为研究手段。
根据无人车是否联网,可以将无人驾驶车分为单车智能和网联智能,单车智能强调车本身的智能,即使在没有网络的情况下,也具备完全自动驾驶能力。而网联车则强调车和车、车和环境的交互,通过整个车联网来实现更高级的智能,车本身可以具备自动驾驶能力,也可以只具备部分自动驾驶能力,通过网络获取更高级的智能。从目前的发展趋势来看,无人驾驶车要更快的落地,单车智能和网联智能二者需要互相融合,共同发展。
▲图3.11 无人驾驶软件架构
3.4.1 端到端
1.什么是端到端的自动驾驶?
端到端的自动驾驶,在功能实现上直接把传感器的数据当做输入,经过深度学习模型,直接输出汽车的控制信号(油门,刹车、方向转角),如图3.12所示。相比基于模块的设计,端到端的结构更加简单,同时不依赖高精度地图。因为高精度地图的构建和更新成本很高,对于自动驾驶大规模部署是很大的挑战,所以不需要高精度地图是端到端架构很大的一个优势。
▲图3.12 端到端结构框图
2.端到端自动驾驶的原理
实际上端到端的自动驾驶可以追述到1989年卡内基梅隆大学尝试用ALVINN网络模型来控制汽车行驶,当时的输入只有一个30×32像素的相机和激光测距仪,实现了车以0.5m/s的速度行驶了大概400m。之后,2016年英伟达发布了端到端的自动驾驶论文《End to End Learning for Self-Driving Cars》,采用PilotNet网络模型在真实道路测试中实现了98%的时间都保持自动驾驶,并且还有一段10英里长的路段实现了零人工干预。PilotNet网络模型的输入为左、中、右158269个摄像头,通过真实数据和随机信号之间的差值进行训练,先是在仿真器中运行,最后在真实的道路上进行了测试。
3.端到端自动驾驶存在的问题
尽管端到端的自动驾驶方法和人类的驾驶方式很相似,结构简单高效,并不依赖高精度地图,但用于生产实践还需要解决以下1269个问题。
- 端到端的自动驾驶需要对不同的车型重新训练模型,生产和制造过程中迁移和升级的成本太高,达不到软件的零边际成本。即使车辆型号一致,车辆出厂之前还需要对摄像头等传感器的参数进行微调,以达到最佳效果。
- 可解释性差。自动驾驶过程中的感知、决策和规划都是通过深度学习模型完成的,出现故障后,没法分析具体的原因。
- 只能根据目前已知的数据进行推断,对于陌生的数据适应性会变差,可能会作出错误的决策。也就是说在特殊场景下,算法可能会失控。
- 不能硬编码安全规则。在算法失控的时候,不能保证安全。
4.未来展望
由于端到端自动驾驶的局限性,目前主要还在实验阶段。相信未来随着人工智能的发展,神经进化和深度强化学习等方法将推动端到端自动驾驶的发展。
3.4.2 模块化
模块化的思想是将无人驾驶这个复杂问题划分为几个相对容易解决的子问题,这些子问题可以在机器人技术、计算机视觉和汽车动力学方面找到解决问题的思路,通过之前积累的经验来快速的解决问题。此外模块化的设计更加方便定界问题,修改一个模块的问题,不会影响到其它模块,这也是现代软件大量采用模块化设计的原因。
根据无人驾驶需要处理的任务类型,可以把无人驾驶分为:高精度地图、定位、感知、规划、控制、人机交互等269个模块。
(1)高精度地图比传统的地图包含的信息更多,传统的地图只有道路信息,而高精度地图除了包含道路信息之外,还包含车道信息、交通规则信息、红绿灯的位置信息等。同时高精度地图的精度需要达到厘米级,确保无人驾驶车通过高精度地图能够安全的行驶。
(2)定位模块主要解决无人车当前在哪里的问题,定位模块会实时更新无人车在地图上的精确位置,并提供给规划和控制模块使用。
(3)感知模块负责获取无人车周围的障碍物信息例如:汽车、行人、自行车等。同时还负责判断红绿灯状态、识别车道线、跟踪和预测障碍物的运动轨迹等,感知是无人驾驶中最难解决的问题之一。
(4)规划模块分为2块:长期规划和短期规划。长期规划任务是根据车当前在哪里,要去哪里,生成一条导航线路,和手机地图上的导航功能类似。而短期规划则是根据车当前的状态输出一条可行驶的轨迹,需要考虑如何避开障碍物,遵守交通规则等。短期规划只考虑未来200米左右的行驶距离,并且每隔一小段时间根据汽车的行驶状态实时修正行驶轨迹。长期规划是开车之前查询地图导航,而短期规划则是人开车在路上考虑如何驾驶汽车。
(5)控制模块根据规划模块的输出,在满足汽车运动学和动力学模型的前提下,控制汽车按照规划好的线路行驶。
(6)人机交互界面实现人和车的交互,比如让乘客设置线路的起点和终点,同时提供界面展示无人驾驶车当前的状态。
▲图3.13 模块化结构框图
模块化设计的优点在于算法都是可控的,可以硬编码一些规则在系统中,确保算法出错的时候,无人车依然安全。模块化的设计方案是目前无人驾驶的主流方案,后面几章我们会详细介绍无人驾驶各个模块的功能实现。
模块化的设计同时也存在一些问题。模块化的设计结构过于复杂,一个模块的错误会传导到其它模块,例如定位模块输出了错误的位置,会导致规划模块输出错误的行驶轨迹。
3.4.3 网联智能
无人驾驶根据是否需要网络,可以分为网联智能和单车智能。网联智能的好处在于通过网络可以和其它车以及环境进行交互。目前无人驾驶车主要通过V2X作为对外通信的标准。网联智能有以下3种应用场景。
1.车辆编队
车辆编队有2种模式,一种是前车为人类驾驶员,通过记录人类驾驶员的驾驶行为,控制后面的车辆编队行驶;另一种是前车和后车都是自动驾驶车,区别是前车的自动驾驶能力更强,后车只具备部分自动驾驶能力,一部分的感知和控制任务通过前车完成,可以节省无人驾驶的落地成本。无人驾驶卡车编队如图3.14所示。
▲图3.14 无人驾驶卡车编队
2.远程控制
在无人驾驶车出现紧急或者无法处理的情况时,通过远程控制的方式可以减少现场维护的成本。远程控制的难点在于需要360°还原现场的视频画面,对传输的带宽和延时都有要求,5G网络的带宽和延时可能会使远程驾驶变得可能。
3.车联网
车联网是指车和车,车和环境之间通信。车联网是智慧交通的基础,通过整个汽车的联网,可以进行动态交通调度,合理规划交通线路,进一步提高城市的交通效率。
车联网对无人驾驶来说是很好的补充,并且5G网络的高带宽,低延迟特性,将来一定会在无人驾驶中有更多用武之地。
3.4.4 单车智能
目前无人驾驶的发展还主要集中在单车智能之上,全部依靠车辆自身的传感器和算法实现自动驾驶,因为不需要依赖网络,所以在没有网络的地方同样能够实现自动驾驶。
单车智能的很多问题都能够被网联智能很好的解决,目前的发展趋势是单车智能和网联智能二者相互结合,共同发展,一起加快无人驾驶的进程。
3.5 云服务
云服务是自动驾驶不可或缺的一环,自动驾驶相关的高精度地图、数据存储、模型训练、自动驾驶仿真等都依赖于云服务。目前已经宣布能提供自动驾驶服务的云平台有百度Apollo、亚马逊AWS和华为Octopus,提供的主要功能包括:数据采集和存储、数据Pipeline、模型训练部署以及自动驾驶仿真。
3.5.1 数据存储
一辆无人驾驶车配置了多种传感器包括摄像头、激光雷达、毫米波雷达、GPS、IMU等。每天使用到的数据量高达4000GB,这些数据需要收集并存储,用于高精度地图制作和模型训练。大数据存储涉及到2方面的问题:一是数据传输,二是数据存储。
1.数据传输
数据传输需要对不同的数据类型进行分别对待。需要实时处理的数据,可以通过网络实时传回到数据中心。实时传输数据对网络的高带宽提出了要求,5G网络支持的上行速率约为50Mbps,也就是说每分钟可以上传375MB数据,还远远低于无人驾驶车的数据上传需求。这就要求我们对无人驾驶车产生的数据做分级,把高优先级的数据优先发送,低优先级的数据先保存在本地,等网络空闲之后再上传。
为了保证数据传输的经济性,需要先在本地对数据做预处理,只缓存最近2分钟的数据,对于不需要保存的数据,处理完之后直接丢弃掉,需要保存的数据压缩之后再上传。同时大部分不需要实时传输的数据可以在回到车库之后,通过WIFI进行数据传输,降低5G网络数据传输的成本。
2.数据管理
数据上传到数据中心之后,还需要对数据进行元信息管理,详细记录数据的各项信息,包括数据录制时间、车辆编号、软件版本、天气情况以及行驶路段等,如表3.1所示。通过数据元信息管理,我们可以快速的找到所需的数据,否则光有数据,但数据的信息缺失,还是找不到需要的数据。类似图书馆没有对书的类目做划分,而是胡乱摆放,那么如何才能找到所需的书呢?再加上自动驾驶的数据都是二进制文件,不具备可视化;场景也多种多样,路况、时间、天气不一样,产生的数据也不一样。数据管理的另一个应用场景是测试数据管理,每天测试产生的大量数据,什么时候因为什么原因接管的,在回归测试中很关键。如果没有这些信息,假如我们需要查找所有因为红绿灯引发的问题进行回归测试,该如何查找呢?
3.数据存储
数据存储首先需要的是一个分布式的文件系统,大数据时代已经被广泛证明了分布式文件系统的好处,最主要的好处是容量可以水平扩展,而且可靠性高。自动驾驶每天产生的大量数据都可以通过分布式文件系统保存下来。
接下来是数据库的选择,我们先分析下自动驾驶大数据应用场景和传统互联网的区别。互联网的数据生产方式是几亿用户,每人每天产生几条数据,合起来几个T的数据,而自动驾驶是一辆车每天产生几T数据,数据生产的方式差别很大。
互联网针对几亿用户,一般是选择key-value结构的数据库,例如HBase。但如果把HBase照搬到自动驾驶的场景就很别扭,因为HBase的单条数据最好是10M以内,否则会影响读写性能。一种办法是对数据做拆分,把几个T的数据,根据地理位置信息或时间做拆分,把地理位置信息或时间作为key,对应的数据作为value,就可以实现一条数据很小,拆分成很多key-value结构的小数据了。
再回过头去看互联网的应用场景,互联网场景是拿用户的ID作为key,如果同时频繁的命中相邻的ID,被称为单点问题,每次访问都到一台机器上去了,导致容量上不去。而按照地理位置或时间的方式刚好又导致了这个问题,因为无人驾驶中数据读取是按照地理位置顺序读取的,刚好每次都命中到一台机器,导致整个系统的容量上不去。如果我们把key做哈希散列,把地理位置信息打散,这样容量提高上去了,而这又恰恰和应用场景有冲突。所以自动驾驶需要的不是高并发读取,即同时几十万的并发,而是一个用户连续读取大量数据,单台机器能够对数据做预取,这样反而是单台读取的性能最高。
高精度地图也不应该直接以XML格式保存,占用的空间太大,应该把地图分块序列化之后再保存,压缩之后存储的效率会高很多。日志文件则采用时间序列型数据库保存,当通过日志文件获取无人驾驶车位置的时候,可以准确的反应出无人车从起点到终点的时间序列。
综上所述,不同的数据需要选择不同类型的存储和数据库。自动驾驶的一些大数据场景可能根本不需要数据库,只需要文件系统就可以了,如果需要管理结构化的数据,可以用数据库存储文件路径,而把文件本身放到文件系统中。例如存储图片文件,可以只保存路径到HBase数据库,把真实的图片文件压缩之后放在对象存储中。当需要查找图片时,先通过索引找到图片对应的路径,然后再从对象存储中解压出图片。导航路线、车和用户信息等需要多用户并发访问的数据可以采用HBase保存。
3.5.2 数据处理
深度学习模型训练、高精度地图生成以及自动驾驶仿真等都需要进行数据处理。自动驾驶的数据处理流程包括:收集、清洗、标注、训练和部署。
用于自动驾驶模型训练的数据首先需要人工标注,然后再进行模型训练,最后才能得到能够识别车辆和行人的深度学习模型。数据的自动化标注是很大的挑战,通过工程的方法尽量减少人工标注,可以大幅度提高标注效率。实现自动标注通常有2种方法:一是通过机器自动标注,然后人工修正部分数据;二是通过仿真模拟生成大量标注好的数据。
数据处理的另一个挑战是大规模并行处理数据,由于数据量巨大,如何快速的处理数据是瓶颈。有很多优秀的分布式计算框架,其中Apache Spark可以构建大规模集群并发执行多个任务,在大规模数据处理中有非常好的实践。
还有一部分离线计算是利用空间换时间,比如planning模块reference line的生成;routing线路事先计算保存;感知的ROI区域;定位用到的点云数据等。
总之能够快速高效的处理数据,是自动驾驶数据处理的核心竞争力。
3.5.3 地图服务
云端应该实时提供自动驾驶所需要的地图服务。地图包括道路信息和动态信息,道路信息不用再过多介绍,就是传统的道路信息,动态信息是地图上出现堵车或交通管制等需要实时动态更新的信息。
高精度地图的道路信息比传统地图的要求更加精细,不仅仅包括道路信息,还包括车道信息、红绿灯信息、交通标志信息等。同时高精度地图的精度也比传统地图要求更高,需要达到厘米级。
除了地图本身,一些动态信息可以通过地图服务的方式下发给无人车,在高精度地图中,这部分信息被称为动态图层。例如某条路突然发生交通事故,这个信息就会下发到动态图层,无人车接收到信息之后选择避开拥堵路段。动态图层包括:交通管制、交通拥堵状态、交通规则等,还包括周围的银行、医院、便利店等生活信息,为无人驾驶提供更多更有价值的服务。
高精度地图的维护是目前面临的最大问题,因为涉及到整个地图的采集、加工和标注,实时维护大体量的高精度地图目前来说成本高昂,一些高精度地图服务提供商提出采用众包的方式更新高精度地图。
3.5.4 仿真
自动驾驶仿真的目的是为了更早的发现问题,业界预测要确保安全,自动驾驶的安全性测试需要行驶至少2.5亿英里。如果全部采用真实环境测试,需要1000辆无人驾驶测试车每天测试100英里,不间断测试6.8年,短期内不可能实现。如果采用自动驾驶仿真,通过模拟真实场景的数据,让无人车大规模部署在虚拟环境中测试,然后再去真实场景路测,可以极大提高发现问题的效率。自动驾驶仿真测试流程如图3.15所示。目前Waymo宣称已经在现实世界中路测了1000万英里,在模拟世界中测试了100多亿英里。
▲图3.15 无人驾驶测试流程
除了要求能够大规模部署,仿真的另一个需求是问题快照,在测试出现问题的时候能够保存现场。在测试过程中,仿真系统通过判断车辆是否发生碰撞、是否开出路面、是否遵守交通规则以及是否安全距离过短等,对错误现场进行保存,记录在发生错误一小段时间内的数据快照,用来做数据回放解决问题。
除了自动驾驶功能测试,仿真还可以通过生成数据来帮助模型训练。数据的生成方式有2种,一种方式是生成标注好的数据,通过在仿真环境中模拟真实的车辆、行人、建筑物等,这些信息在仿真环境中都是已知的,可以直接生成标注好的数据用来进行模型训练。另一种方式是利用强化学习在仿真环境中模拟开车,进行端到端的自动驾驶模型训练,英伟达发布的端到端自动驾驶训练就是采用的此方法。
3.5.5 模型服务
自动驾驶需要深度学习模型来做感知任务。深度学习模型需要通过模型训练得到,模型训练包括数据采集、标注、模型训练、调参、测试、部署等269个步骤。主流的云平台目前都支持上述的模型训练部署过程,在云上的好处在于,计算资源动态调配,扩展和部署起来相当方便。同时云平台支持各种主流的机器学习框架,不需要自己一步一步从头搭建环境。
3.5.6 配置和升级服务
1.配置
要运营数量庞大的无人驾驶车队,车辆的配置管理必不可少。即使无人驾驶车的硬件配置基本一样,每辆无人驾驶车的参数还需要微调,同时根据运营的区域不同,获取的地图和交通规则也不同。
配置服务通过以下方式对无人驾驶车进行配置管理:给每辆无人车设置一个唯一的车辆ID,在无人车启动的时候,先检查配置是否正确,然后检查配置是否需要更新,如果需要更新,则根据唯一的车辆ID去数据中心下载这辆车的配置信息。以下是无人车需要下载的配置参数。
- 车辆的唯一ID
- 车的型号
- 传感器的参数
- 地图
- 各个模块的策略等配置信息
除了上述配置,无人驾驶车还用到了数目众多的传感器,虽然出厂的时候已经对传感器参数做了校准,但在车驾驶之前,传感器参数都要进行轻微的校准,以达到最佳效果。以特斯拉为例,当model3车主开车行驶大概20-25英里之后才会完成摄像头参数的校准。
2.升级
在线升级服务(OTA,Over The Air),通过网络更新汽车的软件,OTA在汽车软件迭代快速的情况下通过软件升级汽车系统。在线升级服务不需要召回汽车,通过软件升级的方式来增加新功能、修复bug,既提升了用户体验,又节省了成本。比较典型的场景是特斯拉一夜之间升级了model3,让车具备了辅助驾驶能力。
3.5.7 实时监控和调度
(1)无人车监控
无人车的监控包括5269个层面:应用层面和系统层面。
- 应用层面。为了确保安全,需要实时监控无人车的位置,如果无人车的位置丢失,或者偏离预定线路,监控系统需要及时给出警告,通知维护人员去处理。同时监控系统也需要在无人车主动触发警报之后,及时通知救援。
- 系统层面。在自动驾驶模式启动之前进行自检,确保汽车的各项指标都没有问题之后,才开始进入自动驾驶模式。在自动驾驶过程中,通过记录当前无人驾驶系统的日志信息,可以实时监控无人车的状态。
(2)无人车调度
自动驾驶打车服务和货运服务都需要车辆调度算法。自动驾驶打车服务要及时找到乘客附近的车辆,好的调度算法可以避免用户等待。在无人驾驶货运场景,特别是在一些港口,调度系统需要和专业的港口调度软件相结合,实现智慧物流,提高港口货运效率。
调度平台还可以结合智慧城市,通过城市大脑动态的调度无人车,从而改善交通拥堵,实现智慧出行。
3.5.8 应用服务
无人驾驶最主要的5269个应用服务是无人驾驶出租车和无人驾驶物流,无人驾驶打车服务和现在的滴滴打车类似,只是不需要司机。而无人驾驶物流服务,包括长途货运,港口运输,以及快递小车等。
除了上述服务之外,随着无人车高度智能化,会出现更多的应用软件,提供资讯、天气、支付等服务。在解放了人类驾驶员之后,汽车可能会变为另一个娱乐和办公场所,用户可以选择音乐、游戏、以及视频会议等服务。
可以预见未来无人驾驶车将会给人们的生活带来非常巨大的改变。
总结
无人驾驶车的软件涉及到方方面面,不仅包括操作系统,中间件,云服务的构建,还包括各种软件算法,调试诊断工具,可视化工具,人机交互界面,同时还包括上层应用软件,以及为这些软件提供升级的OTA服务等。正如汽车是工业之母,无人驾驶车软件未来也将会成为继PC、手机之后最大的软件终端之一。