返回 登录
0

基于Spark与ROS分布式无人驾驶模拟平台

阅读14758

作者:刘少山,PerceptIn联合创始人。加州大学欧文分校博士,研究方向包括智能感知计算、系统软件、体系结构与异构计算。现在主要专注于SLAM技术及其在智能硬件上的实现与优化。曾负责百度无人车系统架构与产品化。
本文为《程序员》原创文章,未经允许不得转载,更多精彩文章请订阅2016年《程序员》

本文是无人驾驶技术系列的第四篇,着重介绍基于Spark与ROS的分布式无人驾驶模拟平台。无人驾驶的安全性和可靠性是通过海量的功能和性能测试来保证的。无人驾驶系统是一个复杂的系统工程,在它的整个研发流程中,测试工作至关重要同时也繁重复杂。显然将全部测试工作都集中在真车上进行是一种成本异常高昂且安全系数非常低的方案。通过综合考虑测试中各种可能发生的正常或异常状况,软件模拟成为了面向无人驾驶系统的更安全且更经济有效的替代测试手段。

无人驾驶模拟技术

无人车驾驶系统由感知、预测、决策、控制等众多功能模块组成,每个模块都各自拥有复杂的结构和算法。绝大部分情况下,系统开发人员在测试过程中很难对海量的输出参数作评价。同时,开发人员不仅需要单独测试一个功能模块,也需要集合联调多个模块。因此,系统开发人员所需要的模拟器必须能够直观正确地反映出输出参数的意义,同时还要既能对各个模块进行单一的测试,又能将各个模块按照不同需求组合后进行集成测试。

模拟器技术主要有两种:第一种是基于合成数据对环境、感知以及车辆进行模拟,这种模拟器主要用于控制与规划算法的初步开发上;第二种是基于真实数据的回放以测试无人驾驶不同部件的功能及性能。在本文中,我们主要讨论基于数据回放的模拟器。

出于需尽量真实地模拟真车环境的需求,我们的模拟器采用了和真车相同的机器人操作系统ROS。ROS是一种基于消息传递通信的分布式计算框架。这种框架方便开发人员进行模块化编程,这一特性对于模拟器来说至关重要。在无人驾驶系统中,每一个功能模块在ROS中都部署在一个节点上,节点间的通信依靠事先定义好格式的message完成。在模拟器中开发人员只需要使用相同的通信格式,针对每个功能模块制作模拟模块,然后根据测试需求搭配真实功能模块和模拟模块。例如,如果想进行决策模块和控制模块的功能联调,我们需要将决策模块、控制模块搭配其他的模拟模块,安装到模拟器中进行测试。如果决策模块需要单独测试新的决策算法,我们可以只将新的决策模块搭配其他的模拟模块安装到模拟器上,这样的测试结果就是只针对决策模块的。

模拟器的组成元素

首先,无人驾驶汽车模拟器中包含了车的动态模型,用来加载测试无人车驾驶系统,并模拟无人驾驶汽车自身的行为。其次,需要模拟的是外部环境,包括静态和动态的场景。静态场景中包括各种静态的交通标志,例如停止线、交通指示牌等;动态场景主要指车周围的动态交通流模型,例如车辆、行人、交通灯等。所有这些元素构建了与现实环境相对应的模拟世界。

模拟器的应用

无人驾驶汽车真实上路后所要面临的外部环境是复杂多变的。模拟器在模拟测试中需要做的就是将复杂的外部环境拆解成最简单的元素,然后重新排列组合,生成各种测试用例。

图1 模拟器应用

图1 模拟器应用

拿一组简单的测试用例举例。图1是一个简单的直线行驶的车道,需要测试的是无人驾驶汽车对于一辆障碍车的反应。按照障碍车可能出现的起始位置划分,它可能出现于无人驾驶汽车的左前、左中、左后、前、后、右前、右中、右后总计八个位置。按照障碍车和无人驾驶汽车的相对速度,可分为比无人驾驶汽车快、和无人驾驶汽车速度相等以及比无人驾驶汽车慢三类。按照障碍车的行为划分则分为直行、向左变道和向右变道三种。将这些变量相乘,再去掉其中不需要的个例,就得到了一组我们需要的测试用例。

模拟器面临的问题

模拟器的核心问题在于“真”上,人工模拟的场景和真实场景多少会有差异,真实场景中仍然会存在许多令人想象不到的突发事件。因此,如果能采用真实的行车数据复现真实场景,就会得到比人工模拟的场景更好的测试效果。但采用真实数据复现的方案带来的问题就是海量数据的处理。如果我们想在模拟器上复现真实世界中每一段道路的场景,就需要让无人驾驶汽车去采集每一段道路的信息,这些海量的信息是单机无法处理的,而且在每个场景下拆解元素重新排列组合生成测试用例的做法会使计算量翻倍。因此,将模拟器搭载到分布式系统上就成为了无人驾驶模拟测试的最佳选择。

基于ROS的无人驾驶模拟器

ROS是一种基于消息传递通信的分布式计算框架。它的通信模式可以抽象为一种message pool架构,消息发送节点调用advertise方法向指定Topic发送ROS message,消息接收节点调用subscribe方法从指定Topic接收ROS message。

ROSBAG

Rosbag是利用这一架构从Topic中录制并且向Topic中重新播放ROS message的工具,无人驾驶汽车在数据采集过程中使用的正是Rosbag。它的功能主要分为Record和Play两类。Record功能是在ROS中建立一个record节点,调用subscribe方法向所有或指定Topic接收ROS message,然后将message写入Bag文件。Play功能则是在ROS中建立一个play节点,调用advertise方法将bag中message按照时间节点发送至指定Topic。图2展示了一个LiDAR数据在ROS中回放的实例,在这个场景中,LiDAR数据是以10Hz帧率记录的。

图2 ROS BAG LiDAR数据回放

图2 ROS BAG LiDAR数据回放

Rosbag生成的数据格式是Bag,这是一个拥有两层逻辑结构的文件格式。如图3所示,上层的Bag类对上抽象提供了用户操作文件的方法,对下封装了对ChunkedFile的操作方法;ChunkedFile类主要对数据进行了分隔存储,而存储的数据为一条条ROS message,不仅包含文字信息,也包含大量的二进制数据,后者主要是无人驾驶汽车传感器发送的图片或3D点云文件数据。这就给传统的主要用来处理文字日志的分布式计算系统应用带来了新挑战。

图3 ROS BAG 结构图

图3 ROS BAG 结构图

模拟测试数据集

如前所述,我们主要关注基于真实数据回放的模拟器,数据量有多大呢?以来自真实世界的KITTI数据集为例(由KIT和TTIC在2012年启动的一个合作项目,网站为http://www.cvlibs.net/datasets/kitti/,更详细的介绍请参见《程序员》2016年7月刊《基于计算机视觉的无人驾驶感知系统》),KITTI的研究人员录制了6个小时的真实数据,数据量为720GB。但是6小时的数据仅够完成一些算法的简单验证,无人驾驶产品所需求的数据量远大于此。比如谷歌的无人车在过去几年中收集了超过40000小时的真实数据,总数据量估计超过了5PB。如此大量的数据,基于单机的模拟远不能支撑其处理,所以我们必须为基于真实数据回放的模拟器设计一个高效的分布式计算平台。

计算量的挑战

巨大的数据处理量对计算平台造成很大的压力。例如,KITTI数据整集6小时的原数据包括了超过1000000张140万像素的彩图,如果使用单机的基于深度学习的图像识别平台,每张彩图分析时间大概是0.3秒。这样,仅是分析KITTI数据集的图片,就需要超过100小时,而如果分析谷歌无人车级别的整体图片数据,在单机处理上就需要超过60万个小时。

基于Spark的分布式模拟平台

Spark是UC Berkeley AMPLab开源的通用并行计算框架。Spark基于内存实现的分布式计算,拥有Hadoop所具有的优点;但不同于Hadoop,Spark Job的中间输出和结果可以保存在内存中,从而不再需要读写HDFS,因此Spark能更好地应用于需要迭代的Map-Reduce算法。

图4 分布式模拟平台总体架构

图4 分布式模拟平台总体架构

如图4所示,为了高效地进行无人驾驶回放模拟,我们设计了基于Spark的分布式模拟平台框架。我们使用Spark进行资源的分配管理、数据的读写以及ROS的节点管理。在Spark Driver上,我们可以触发不同的模拟应用,比如基于LiDAR的定位、基于图片的物体识别、车辆决策与控制等。Spark Driver会根据数据量与计算量等需求请求Spark worker资源。每个Spark worker首先会把Rosbag数据读入内存,然后通过pipe启动ROS Node进程进行计算。我们也可以使用JNI方式连接Spark worker以及ROS Node,但这将涉及对ROS的修改,使得整个系统难以维护与迭代。经过权衡之后,我们最终选择了pipe的设计方案。

在pipe的设计方案中,有两个问题需要解决:第一,Spark本身支持读取文本数据,但并不支持多媒体数据读取,我们需要设计一个高效的二进制文件读取方法。第二,Rosbag的play功能如何从内存中读取缓存的数据,record功能如何将数据缓存至内存中。以下我们将讨论这些设计。

二进制文件流式管道处理

Spark操作数据的核心是弹性分布式数据集(RDD),它允许程序员以一种容错的方式在一个大型集群上执行内存计算。百度美国研发中心之前的一个工作就是在这一数据结构的基础上引入了新的RDD来实现二进制文件流式管道处理。其结构如图5所示(关于这个设计的细节请参考《程序员》2016年1月刊《基于Spark的百度图搜变现系统架构》)。

图5  BinPiped RDD的总体设计和主要功能

图5 BinPiped RDD的总体设计和主要功能

在每一个Spark的worker上,worker根据Binpiped RDD的信息通过标准输入流在内存中将数据传送给用户程序,用户程序处理完数据后通过标准输出流在内存中将数据传回给Spark的worker。worker将数据汇集存储到HDFS上。

Rosbag缓存数据读取

在当前使用场景下(如图6),我们的输入是一定量的Bag二进制文件,以某种形式存储在分布式文件系统上面,而用户想要的输出是所有这些Bag文件在每一个worker上回放信息进入模拟器后经过处理得到的数据,显然这一过程通过Rosbag的play和record功能最易实现。

图6  模拟器在分布式平台的运作流程

图6 模拟器在分布式平台的运作流程

图7  MemoryChunkedFile设计

图7 MemoryChunkedFile设计

不过这一过程中还存在缺失的环节,即Rosbag的play功能如何从内存中读取缓存的数据,以及record功能如何将数据缓存至内存中。为了实现这一功能,我们为原来的Bag和ChunkedFile的两层逻辑结构增加了一个分支逻辑层。如图7所示,MemoryChunkedFile类继承于ChunkedFile类并且重写了ChunkedFile所有的方法。MemoryChunkedFile在向下层读写文件时是向内存读写数据,而不是像ChunkedFile类一样向硬盘读写数据。这样做的一个好处就是worker通过标准输入流传给模拟器的数据不用经过磁盘I/O读写就可以被直接读入,经过模拟器处理的数据也不用经过磁盘I/O读写就可以由内存直接传回worker。这样的读写模式极大地缩短了模拟器处理数据的时间。

通过这一逻辑层的添加,我们可以将模拟器部署到Spark集群内的每一台worker机器上。通过加载不同的配置文件使每台机器运行不同的模块;也可以通过部署相同模块不同模型的条件下运行相同数据,以比较模型的不同;还可以在相同模块相同模型的条件下运行不同数据,比对不同数据的差异。由此可见,分布式系统的使用为模拟器添加了无数扩展的可能。

性能评估

在设计实现的同时,我们对系统进行了性能评估。随着计算资源的增加,计算时间也在线性地降低,系统表现出很强的可扩展性,可以承受很大的数据量与计算量。在一个图像识别测试集中,使用单机处理图像数据耗时为3个小时,而使用8个Spark worker后,耗时仅25分钟。假设我们使用10000个Spark worker对谷歌无人车级别的数据进行大规模的图像识别模拟测试,整个实验也可以在100小时内完成。

结论

使用分布式系统能够极大提升模拟器的工作能力,使无人驾驶系统的测试工作得以大规模有序地扩展开来。这一结果是建立在模拟器架构模块化,以及测试用例组合模块化的基础之上的。采用分布式系统搭建模拟平台,使得在真车上路之前测试无人驾驶汽车将要行驶的每一条道路成为现实。当然无人驾驶汽车在真实道路上的测试依然必不可少,但是模拟器已经为无人驾驶系统测试了海量的基础情景,使我们可以以最低的成本最大限度地保障真车测试时的安全。


无人驾驶技术系列

评论