Apollo进阶课程[6]——ROS介绍

背景介绍

自动驾驶系统包括障碍物检测、行为决策、路径规划等一系列复杂的工程模块,同时还要支持激光雷达、相机、GPS等一系列传感器的实时数据收集和实时处理。如何将这些功能模块相互独立又相互交互集成一起,构建成一个稳定的自动驾驶系统是一个巨大的挑战,也是自动驾驶计算框架所承载的基本功能。

什么是ROS?

  • 开发工具包:完整的包管理和工程结构、庞大的基础库、多语言接口支持
  • 计算调度模型:消息驱动的异步运行模型、抽象的P2P通信接口、自定义的消息格式
  • 调试工具:可视化的调试工具、消息查看、存储、回放工具

ROS 是一个强大而比较灵活的机器人编程框架,从软件架构的层面来说,它是一种基于消息传递的分布式多进程框架,开发者可以根据功能把软件拆分成独立的子模块,子模块通过不断的组合能够建立起比较复杂的系统来完成复杂的功能。使用比较广泛。

ROS基本概念

ROS特征:

  • 点对点:两个Node之间进行消息通讯是一个点对点的行为。
  • 支持分布式:在部署多机之间的消息通讯时,ROS提供了一个天然的支持。
  • 跨语言:它并不关注每个节点之间是用什么语言来写的。只需要按照ROS提供的一些接口完成消息的订阅和分发即可以完成一个消息之间的通信。
  • 轻量级:用户只需要关注自己核心模块的算法逻辑,不需要关注底层是如何通信、如何断开通信、如何进行Service 和Param之间的一些交互的。
  • 开源的框架

ROS核心概念:

  • 松耦合:ROS是一个松耦合的框架,松耦合就是各个节点之间的通信是一个解耦合的关系。
  • 节点:一个算法模块,比如自动驾驶系统里面的感知模块、定位模块、决策模块或者控制模块,这些模块就是一个简单的算法集合,在ROS里面被称为一个节点。
  • 节点管理器:在ROS里面被定义为Master,用来集中式管理各个独立的、松耦合、无序节点之间的逻辑关系,它是轻量级的介入,当各个节点启动完成以后,他们在通信连接完成之前起到中转也就是类似于交换机的作用。
  • Topic:两个节点之间的通信主题。Topic内部使用的数据格式是Message。Message是一系统简单的基础数据类型或者是一些自定义的复杂数据类型,所组装成的一个文本描述文件。

几个概念之间的相互关系:
Apollo进阶课程[6]——ROS介绍
松耦合在此有两种体现:
Node 1和Node2两者之间的启动没有先后关系。
当通信链路建立之后,RosMaster的功能就暂时完成了。

节点常用命令:
Rosnode list:可以列出当前系统里面所存在的节点。
Rosnode info:查看某一节点的具体的一些信息。
Topic常用命令:
Rostopic list:可以查看所存在Topic的一些列表。
Rostopic info:可以查看到发送这个Topic的发送方,订阅这个Topic的订阅方。
Rostopic type:查看Topic内部所使用的MSG的数据结构。
Rostopic pub:调试计算节点模块的一些基本功能。

+’/'代表变量域是全局变量

ROS实践

启动Roscore

Apollo进阶课程[6]——ROS介绍
启动Roscore:通过一个简单的命令行Roscore就可以启动一个节点管理器。不需要进行任何参数的传入,也不需要进行任何配置。
如果有很多个节点启动的时候,会使用Roslaunch。Roslaunch就是把所有启动节点的行为放到统一的描述文件里,在启动的时候会在描述文件里找到定义的各个节点的位置,然后启动节点。
Roslaunch启动会先检测是否有Roscore的存在:

  • 有Roscore,直接启动定义好的节点程序
  • 没有,先启动Roscore,再启动定义好的节点程序

启动一个简单的Talker程序

Apollo进阶课程[6]——ROS介绍
Talker就是一个发送节点的程序。
单独启动一个节点,除了Roscore启动之外,其它的节点启动ROS提供了一个Rosrun命令。Rosrun,前面是Package包,后面是实际的可执行文件。
通过这样简单的一个命令可以直接启动一个Talker。

通过命令查看Talker node

Apollo进阶课程[6]——ROS介绍
当启动这个节点之后,用Rosnode list,见第一个图有名叫Talker的Node文件,还有一个Rosout的程序节点。
Rosout:Roscore默认启动的时候启动了一个隐藏节点,它是一个记录日志相关的节点,所有节点发生的Log都会被Roscore启动的Rosout所订阅,订阅完之后会根据一些特定的规则把这些Log分级,然后分模块、分文件打印到对应的模块日志里。

Rosnode info查看Talker相关的一些节点,Talker发送的Topic以及它发送的Service。
见第二图,它有两个Service:Setlogger、Getlogger。这两个是每一个节点都会默认启动的两个Service,这两个Service的作用是设置这一个节点里面的日志层级,如果日志层级是INFO,那么它打印的Debug信息就不会记录在Roscore的Rosout节点里面。

Rostopic info,通过这个命令我们能看到Topic的发送方和接收方。见第三个图。

分析chatter topic

Apollo进阶课程[6]——ROS介绍
Rostopic type是查看Topic的一个Message的消息类型。
Rostopic echo是相当于起了一个Listener节点,去展示Talker发的Topic包含的具体信息。
Rostopic还提供了HZ和BW功能:

  • HZ是统计Talker节点发送Obstacle topic的频率,根据此频率能简单的探测系统是否按照我们所预期的方向来执行。

例如自动驾驶车系统里面每一个传感器有一定的频率,激光雷达是十赫兹,一秒钟发十帧点云图像。可以通过Rostopic HZ去检测Topic是不是一秒钟发送十赫兹,如果低于十赫兹,说明当前系统肯定是有异常,要么是激光雷达扫描的过程受到影响,要么是顶层的Driver节点在处理激光雷达顶层信息的时候中间出现了一些故障。

启动一个Listener节点

Apollo进阶课程[6]——ROS介绍
启动Listener节点之后整个拓扑会有一个比较明显的变化,Listener启动向Roscore发送一个注册信息,同时会订阅Topic。
Roscore会发送一个通知信息给Listener:在它发送注册信息之前已经有一个节点启动了。此时,Listener会向Talker发送消息请求通信连接,Listener收到消息之后会在Listener和Talker两个节点中间建立一个基于TCP的实时通信链路。T建立起来之后Talker就持续不断的发送信息,Listener接到信息之后去做回调处理供实际的决策和执行。
Apollo进阶课程[6]——ROS介绍
通过Rosnode list看到它包含了一个完整的拓扑:包含发送节点和接收节点。

ROS的编译系统

ROS是基于Cmake编写的Catkin编译系统。

如何建立一个工程包:

  • 在ROS里面写一个节点,通过Catkin create 可以简单创建一个文件夹,这个文件夹里面会预先设置一些文件目录。
  • 通过Catkin build编译建立软件包的过程。Catkin build执行之后,里面会多两个文件夹:DEVEL、BUILD 。BUILD是编译中间过程产生的文件。
  • 编译完成之后,通过Source devel下面的Setup bash就可以把自己编写的节点程序给Source到ROS的环境里面,然后去执行我们节点里面的一些基本功能。

Apollo进阶课程[6]——ROS介绍
src:用来放源文件的一些目录。
build,devel:这两个是在Catkin build的过程当中产生的临时文件夹。
想重编译的话可以直接Catkin build,如果环境里面有一些冲突,可以通过Catkin clean 简单的去把编译产生的临时文件和之前的一些产出文件直接清除掉。
Apollo进阶课程[6]——ROS介绍
Catkin config指定了命令行编译的一些方式,这些方式可以在Cmakelists里面进行编写。Cmakelists里面指定了这个文件编译过程当中所依赖的一些库、产出的一些可执行文件和这些可执行文件链接了一些什么库。
Apollo进阶课程[6]——ROS介绍
Roslaunch是一个Shell脚本文件,Shell脚本文件里面根据语言定义的一些Xml格式去找到运行的一系列节点所在的位置然后执行它。它的执行格式是前面加上Package Name,后面加上实际的Launch文件。
Launch文件:
Apollo进阶课程[6]——ROS介绍

ROS仿真工具Gazebo

Apollo进阶课程[6]——ROS介绍
ROS提供了仿真功能Gazebo,定义的节点在里面是实体的存在,通过控制一些参数和变量去模拟他们之间的一些交互,去验证算法在实际的运行中是否按预期进行表现。

Apollo ROS原理1

ROS在实际的自动驾驶工程化需求面前有很多明显不足:

  • 大数据传输性能瓶颈
    • 实验性项目里面采用的Topic是Message,数据量是比较小的,但在实际自动驾驶场景里面数据量非常大。
    • ROS架构对大数据传输存在很大的性能瓶颈,带来的直接后果是时延非常高,这在自动驾驶整个系统里面是非常危险的。
  • 单中心的网络存在单点风险
    • 整个ROS虽然是一个松耦合的架构,它包含一个节点管理器,节点管理器介入的时候,只是在节点建立通信之前有一个简单的拓扑映射,这种关系虽说极大程度释放了各个节点之间开发的耦合,但同时也带来了比较大的风险。
    • 如果Roscore存在一些故障退出,同时节点之间使用了需要不定时的交互方式,像Service 、Parem进行数据交互的时候就会存在一定的风险。
    • 如果是分布式系统, Roscore只存在于一台机器上,Roscore如果出现故障,两台机器之间通信就处于一个不可信的状态。
  • 数据传输格式缺乏后向兼容
    • ROS是基于Message的分发和订阅的消息通讯框架,使用Message需要提前设置Message包含哪些类型的数据。把这个模块放到一个更复杂的系统里面的时候,要格外注意Message之间的数据兼容。
    • 根据实际的场景需求,在定义的Obstacle信息里面加一段文字,那么相应的下游所有订阅此Obstacle的节点都要去做对应的适配,同时基于之前的Message所录制的一些实验数据,想在新的框架下使用也都需要一个批量的转化。ROS现有的数据格式缺少后向兼容,此问题在Apollo ROS里面得到解决。

Apollo ROS对ROS的改进

通信性能优化

进行通信优化的原因:

  • 自动驾驶大量使用传感器引发很大的传输带宽需求。
  • 单路传感器消息有多个消费者时负载成倍增长。
    Apollo进阶课程[6]——ROS介绍
    • 自动驾驶系统是一个比较复杂的拓扑结构,一个传感器数据可能会有很多的下游订阅节点。在单点的情况下是一对一,如果是一对多,传输的数据会被复制多次,造成网络负载成倍增加。

Apollo进阶课程[6]——ROS介绍
Apollo ROS做了一个基于共享内存的通信机制来减少数据的复制次数,从而提升这种通信模式的效率。
左侧是ROS原生的通讯框架,一个数据从发送方到接收方经历四次数据复制:

  • 从节点到用户内存的数据复制。
  • 从发送方到内核的数据复制。
  • 经过TCP连接,从内核再向接收节点用户态空间的复制。
  • 接收节点拿到这个信息之后,通过反序列化把信息取出来组成一个结构变化的信息。

右侧是Apollo ROS优化后的框架,它基于共享内存改进,可以减少两次数据拷贝:

  • 发送节点把消息序列化成流式数据
  • 接收节点直接从共享内存里面取相应的消息指针,把共享内存消息取出来进行反序列化成结构化信息进行使用。
  • 减少了从用户到内核态以及从内核态到用户的两次数据拷贝。

数据进行传输的时候,他必须是一个二进制相关的流式数据,所以再发送节点发送数据之前会进行一个序列化,接收节点从内核态接收消息后会进行反序列化成一个结构化的信息,同时对这个结构化的信息做一些回调处理。

Apollo进阶课程[6]——ROS介绍
对于有多个订阅节点的情况,例如Camera下游会有很多订阅节点,如果是三个节点,会有三条通信链路,分别是四次的内存拷贝,也就是12次数据拷贝。而在基于共享内存的通信方式下,每一条链路内存拷贝的次数只需要两次,三条链路只需要六次。
Apollo进阶课程[6]——ROS介绍