前言
本课程由Apollo资深工程师是夏黎明主讲,一共是7堂课,属于框架性讲解,需课后自行补充理论知识。下面是随堂记录。
Apollo_ROS背景介绍
- 课程目录
- 背景
为了应对这么复杂的系统,因此选择了ROS:它是一种基于消息传递的分布式多进程框架。
ROS概述
- 本节课程目录
- ROS比较明显的几个特征:点对点通信,支持分布式,跨语言(有专门的订阅接口),轻量级,开源。
- ROS的安装:
- ROS的master
节点的启动没有先后关系(但ROScore要在所有节点之前启动),如perception先启动,向ROScore(roscore作用是启动一个节点管理器(rosmaster),它是ros节点运行的前提,所以在执行启动ros节点前,第一步都需要执行roscore。)注册,并说明会发出obstacles的topic,然后planning启动向ROScore注册名说明会接收obstacles的topic。然后ROScore会向planning说明有个叫perception的节点发出该消息,planning则会寻找perception并与之握手,握手成功后通信建立(基于TCP协议)。MSG定义了通信的数据格式:基本类型和自定义类型。
- ROS的node
6. ROS的topic:/topic是全局变量,所有人都可以看到这个topic。如果不加/,在发送时会自动在topic前加上发送node的namespace,以防命名冲突。
7. ROS的messages:
8. 为解决太多节点启动(需要用启动窗口挨个启动),Apollo提出了ROSlaunch描述文件,用于描述所有节点启动行为。它会做两件事情:找到描述文件中节点的位置,并启动该节点。该描述文件中虽不包含启动ROScore,但ROSlaunch启动时会先检测ROScore,如果未启动则会自行启动core。后面会举个实例(序号11)
9. 实例演示
启动单独节点(如下图中的talker,如发送obstacles的perception),启动单独节点,除了通过ROScore启动外,还可以用rosrun命令,前面是package包,后面是可执行文件。
ROSout是ROScore启动时默认启动的隐藏节点,是记录日志相关的节点。并且每个节点会默认启动两个service:get_loggers和set_logger_level,为了设置节点的日志层级。
Rostopic echo相当其启用了一个lisener节点来展示某个topic中MSG的具体信息,hz是统计发送频率(一般激光雷达是10hz)
可以模拟发送节点,发送自己想要的topic
10. ROS所采用的编译系统,它是基于cmake的一套自己的catkin编译系统,catkin底层就是一些cmake的封装。类似Cpp工程的建立,使用catkin create建立一个节点工程包(会生成一个文件夹如src文件夹放cpp,cmakelist文件夹放整个package编译的过程)。使用catkin build可以编译工程包,该命令执行后会多两个文件夹:DEVEL(其中setup.bash可以把节点程序放到ROS环境里执行)和BUILD(编译中间过程中产生的一些文件)。
一般先catkin clean,再进行catkin build编译。
Cmakelists里指定了文件在编译过程中所依赖的库文件,和所要产生的可执行文件,以及可执行文件与依赖库文件之间的链接关系等。
11. 下图是举例roslaunch,是个shell脚本文件:根据预先定义的xml文件,去寻找节点所在位置,然后去执行它。Roslaunch的执行格式是前面加上package name,后面加上实际的launch文件.
下图中的output是指定节点的输出是打印到log文件中还是打印到屏幕上。
Roslaunch中还提供了几个功能点再启动rosnode时进行一些配置操作,比如参数arg,可以node启动时传参数进去。参数arg如果写在Roslaunch外则会对所有节点有效。
Roslaunch也支持嵌套,如启动一个节点时需要先启动其他节点,可以通过#include包含进来。
12. Gazebo是仿真工具,其本质也是一个节点
13. 可参考的网站:
Apollo的ROS原理一
- 本堂课目录
- Ros在工程化时候的不足如下图,因此Apollo对此做出了优化
- 针对通信性能的优化
通过共享内存减少数据拷贝,提升传输速度。在ROS原生通讯框架里,数据从发送到到接收方要经过4次copy,第一次是发送节点到用户态内存copy,然后从发送节点到内核态之间的copy,第三次是内核态向接收节点的copy,最后是接收节点进行反序列化的copy。Apollo可减少2此copy,第一次是发送节点把消息序列化成流式数据,接收节点从共享内存拿到相应消息的指针,并读取反序列化为结构信息。他减少了从用户态到内核态和从内核态到用户态的copy。
Apollo的ROS原理二
- 去中心化拓扑网络。
原ROS通信过于依赖master:第一步发送节点先向master节点进行注册,第二步接收节点在master注册,第三步master告知接收节点已有发送节点的信息拓扑,第四步接收节点拿到信息拓扑后向相关发送节点请求TCP连接,第五步是发送节点和接收节点建立连接。在通信建立之后基本就不再依赖master。
且原生ROS没有异常恢复机制。
Apollo去除了中心化的拓扑,使用了点对点的通信拓扑,它用到了RTPS服务发现协议解决该问题。下图右侧是ros node的框架图,左下角是RTPS服务相关的功能。可以看出node是分层的,最上层是handler句柄用于ROS的通信交互,第二层定义了该节点的channel信息(publisher/subscriber和servicer/client),然后是middleware用于通信链路的建立和数据的接收发送。
第一步:这种拓扑下,当一个节点启动后会向所有节点发送信息:有新的节点加入拓扑网络里,退出时也会发送通知消息。
第二步:所有节点接收到该信息后,会和新节点建立通信
第三步:旧的节点会向新节点发送“在新节点加入前的自己的拓扑信息”,供新节点用于更新自己的网络拓扑结构。
第四步:当新节点接收到所有的历史拓扑信息后,会根据自己注册的实际消息内容决定和哪些节点建立实际的通信连接
- 数据集兼容
原生ROS的发送和接收方需要提前定义消息类型,Apollo用protobuf解决该问题,也可以解决向后兼容问题。
是需要在使用过程中定义好固定字段和新增字段(用optional描述,这样进行节点接口升级时,下游节点可不用关注无关的新增字段。)
ROS深入介绍
- 介绍一些ROS不太常见的属性
- ROS package的组织方式:创建ROS和写一个cpp工程有点类似,catkin create可以创建一个ROS工程,src文件夹存放源文件,msg文件夹存放节点之间通信的消息定义,srv存放节点之间服务通信的服务定义,config存放配置文件,include存放头文件相关的信息,launch存放节点的启动文件。不是必须的,是官方推荐的。另外devel和build是在编译过程中临时的产出目录
另外还有文件,这两个文件描述了ros package的工作区:package.xml和cmakelists.txt。package.xml定义了可执行文件在编译和运行时依赖的库,同时定义了软件版本和开发信息等。cmakelists.txt定义了我们怎么去编译ROS package工程,其中指定了cmake的版本,并定义了工程package name,定义了工程所需要的头文件信息,指定依赖的库文件(这一部分类似package.xml),还有install是把编译过程中的产出放到指定的文件里。
下面是cmakelist.txt的一个例子
设置好后即可对工程进行编译,可以用catkin buil+某一个package名,对某个包进行单独编译避免内容过大,提升编译效率。
- 介绍一下在eclipse下如何编译一个ros工程
和写一个简单的java程序一样,可以通过导入来引入一个ros相关的工作区
可以通过eclipse里提供的build或run等功能进行调试ros工程
- 下面举一个简单的node例子,下面展示了ros中写node的核心要素,#include了ros的基本环境,mian里init了节点基本环境,并指定了node name等参数信息。另外node最顶层有node handle(类似matlab中的句柄)。然后接下来也定义了数据的发送频率。下面的while循环就是以10Hz的频率发送,其中的spinonce()是如果有消息则立马发送,然后进行下一轮的消息等待。
这里再介绍一下node handle:他是一个句柄指针,用于方便通信、函数和参数的访问。如可以通过Node handle创建takler和listener等节点。
Ros info是ros提供的日志系统:1)分级分类(如warnning,error,fatal等),ros会把不同的分级分类进行不同的格式和颜色展示。2)这个分级是为了帮助开发者快速定位问题,不会对工程由实质性的影响。
ROS_INFO默认打印到屏幕,而ROS_INFO_STREAM是流式数据默认打印到后台。全是大写(是个宏定义)。
- 下面是一个实际写subscriber的例子,他和publisher有两个明显的区别:1)subscriber作为信息接收方,在接收到信息后需要做处理,因此他有一个回调函数的概念:针对介绍到的每一帧信息应该做怎样的处理,这是通过回调函数来定义的(下图示例是做了一个打印处理)。2)在进行init和node handle后,用句柄定义了这个subscriber,参数有三个:所订阅的channel信息,queuesize和回调函数指针。这里说下queue_size队列缓存:发送节点产生消息后先放到队列里,然后由另一个线程进行监听和发送,送用户态发送到核心态,传输给接收节点后由核心态发送到用户态,然后再进入接收节点的队列,由接收节点的线程进行监听和调用回调函数进行挨个读取。这样可以使消息的产生和发送分离,接收和消费分离,最大限度的降低消息传输的延时影响。3)最后是ros spin,因为ros不会主动调用回调函数,而是通过ros spin进行调用,他是阻塞性的,程序运行到这里会停止并一直监听是否有新消息,得到新消息后立马调用回调函数。ros::spin() 和 ros::spinOnce(),两者区别在于前者调用后不会再返回,也就是你的主程序到这儿就不往下执行了,而后者在调用后还可以继续执行之后的程序。一个是一直调用;另一个是只调用一次,如果还想再调用,就需要加上循环了。
- 下面就是和subscriber对应的publisher的写法
- Ros节点间通信处理发布和订阅外,还有两外两种方式:1)service概念,和server-client概念比较类似,它可以启动一个server去注册一个服务,另外一个节点可以直接call service进行消息读取。和发布订阅模式不同,client向server发送service请求后会实时等待service response消息到达(发布和订阅是,发送节点不知道接收节点是谁,接收节点也不知道发送节点是谁,这可以实现松耦合)。2)parameter通讯:借鉴了server,是parameter server,是一个全局的server服务器。(应用场景有点类似于cpp中define和const的那种程序运行过程中不会改变全局参数。)通过get和set进行参数的读取和写入。但是当一个节点set后,其他节点并不是立马更新参数的,除非用了get,但getparamcache可以实时监听参数。
- 可视化工具:RViz。可以把它看成一个节点。
Rviz提供了很多插件,可以放到eclipse这样的功能插件里,在使用eclipse开发时可以通过plugin调用rviz的功能。
Apollo ROS深入介绍一
- 本节课的目录
- 为何需要TF坐标呢,因为ROS是松耦合,TF用来统一各个节点的坐标系。
TF世界坐标转换也是通过发布订阅的方式实现的,当接收节点想要使用一个topic时,会更根据自己的TF查询该topic处于哪个世界坐标系下的哪个位置,从而进行坐标转化。
Rosrun提供了TF的monitor监控的节点。
TF也提供了plugin的插件
下面就是结合publisher/subscriber写的TF的实例,改动两个地方:1)定义TF对象,2)使用TF进行查询。
- RQT是Ros+QT,是ROS给开发者提供的一套比较方便的图形化展示的工具。
下面介绍几个rqt相关的功能:这个是rqt_imageview,用来实时观测camera信息是否正确。
Multiplot是二维数据进行绘图可视化
Rqt_graph可以把整个的节点网络拓扑以图形化的方式展现出来。
Rqt_console就是把分级信息log(debug、info、warnning、error、fatal)规整到一个可视化工具里
Ros loglevel也是ros提供的日志系统另外的可视化工具,该工具可实时调整哪些级别的log信息,相当于一个过滤器,只看自己想看的信息。
- 机器人领域用的比较广泛的一套架构:统一机器人描述格式语言(URDF),其本质是xm描述文件,其描述了两个核心概念,一个是节点,一个是节点间的连接关系。可以理解为对网络拓扑结构的xml描述。
下图中link可看作节点,joint是节点之间的连接关系,
Rviz侧重于消息收发的可视化,而下面的这个gazebo(其语言为SDF,也是xml)是ros另一个工具,主要用于仿真模拟。
Apollo ROS深入介绍二
- Service
Ros提供了3中节点间的通信方式:1)基于消息的订阅发布模型,2)基于service的消息传递,3)基于parameter的变量传递。
Topic底层是通过msg描述文件进行定义。
Service是通过srv描述文件和msg类似,唯一不同是srv里定义了两种消息(用三个-分隔开):1)请求信息的格式定义,2)响应消息的格式定义。
下面是一个srv文件的一个实例,用三个“-”分隔开
- Service启动的也需要先启动roscore(service可以看作一个节点),下面是service实例
然后可以通过list或type查看service,和topic非常类似。
- 下面是service的cpp实例
- 下面是client的cpp实例
- 还有另外一个:action。他和service相比多了cancel和feedback。正常发出service请求后在等到response后才会结束,但Acton发出service请求后可再发送cancel取消。他有个action文件(与msg/srv等描述文件类似)。
- ROS Time是仿真时间
- 实车录制的数据可以存放到某个bag中,用于回放或仿真测试。
- RQT提供了一些诊断调试用的工具