apollo学习笔记十六:apollo ROS(中)

时间:2024-03-16 20:21:29

Apollo ROS原理-2

ROS的不足

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

apollo ROS对ROS的改进

apollo学习笔记十六:apollo ROS(中)
(1)通信性能的优化:见上一篇博客
(2)去中心化网络拓扑:

  • 去中心化网络拓扑的原因apollo学习笔记十六:apollo ROS(中)

    • 节点之间的通信过于依赖Rosmaster单点。两个节点进行通信的链路过程,大概分为五部。第一步:发送节点去向Master注册一个发送节点。第二步:接收节点去向Master注册一个接收节点。第三步:Master向接收节点发送一个已有发送节点的一个信息拓扑。第四步:接收节点拿到这个拓扑信息之后去向发送节点请求建立一个tcp连接。第五步:在发送节点和接收节点建立一个P2P的单点拓扑连接之后就持续不断的向接收节点发送信息。整个过程中对Master依赖包含三步,在建立实际通信之后,对Master的依赖可能会降低很多,但是在建立之前是比较依赖Master节点的。

    • ROS没有提供一种异常恢复机制。如果某一个节点挂掉,尤其是Master节点挂掉,其它的节点却不知道发生了这样的行为,还会认为整个系统运行仍然处在正确的状态中。比如发送节点里面有一些Service或者Param相关的请求,它还是会照常去发请求或者是设置这个参数信息,这样就会产生一些不可控的行为。
      apollo学习笔记十六:apollo ROS(中)

    • Master单点在多机的方案里,这个单机单点的不足就会更加凸显。比如现在很多自动驾驶厂商所采用的比较主流的Nvidia Drive PX2板卡,它就包括两个系统:一个主系统,一个是冗余备份系统即容错系统。如果使用ROS通信在PX2上进行部署,Master只能起在一个节点上。如上图所示:左侧是它的主系统,右侧是它的冗余备份系统。当主系统里面Rosmaster宕机之后,备份系统里面的节点其实并不知道Rosmaster已经处于一个宕机的状态,那么备份系统就起不到其目的和意义了。因为此时整个系统处于一个功能不完整的状态,所以就失去了冗余备份的意义。

  • 使用RTPS服务发现协议实现完全的P2P网络拓扑
    Apollo ROS对ROS进行了比较大的改造:先把这个中心化的网络拓扑给去掉,然后建立了一个点对点之间的一个复杂网络拓扑,主要是使用RTPS服务发现协议去完成P2P网络拓扑。
    apollo学习笔记十六:apollo ROS(中)

    • 右侧是ROS Node的一个框架图。
    • 左侧上半部分是ROS Node现有的一些功能。Ros Node是分层级式的结构,最上层是Handler,Handler提供节点和ROS整个通信的基本交互的句柄。下一层左侧和右侧定义了这个节点发送和订阅的Channel信息。再下一层是Middleware,Middleware是这个节点和其它节点进行通信的时候去完成链路的建立和数据的发送。
    • 左侧下半部分RTPS是新引入的一个功能。改造之后的ROS Node架构,当一个节点被启动的时候,它会通过RTPS向所有的节点发送信息:现在有一个新的节点要加入到这个拓扑网络。当它离开的时候,也会发送消息告诉所有的节点:现在这个节点要退出。以前这些功能都是通过Rosmaster来完成的。
  • 节点建立连接和通讯的一个主要流程。

    • Sub节点启动,通过组播向网络注册。
      apollo学习笔记十六:apollo ROS(中)
      订阅节点在启动的时候,它会向当前这个域里面所有的节点发送信息:现在有一个新的节点要启动。
    • 通过节点发现,两两建立unicast。
      apollo学习笔记十六:apollo ROS(中)
      所有的节点在接收到新加入这个节点发生拓扑信息变更之后,会和新加入这个节点分别建立两两连接关系。
    • 向新加入的节点发送它们已经有拓扑信息。
      apollo学习笔记十六:apollo ROS(中)
      所有已经存在的节点会向新加入的节点发送它们已经有拓扑信息,也就是在新节点加入之前每个节点其实是维护了它和其它所有节点的一个连接关系,这个连接关系发送给接收节点,供接收节点去更新自己的网络拓扑结构。
    • 收发双方建立连接,开始通信。
      apollo学习笔记十六:apollo ROS(中)
      当新加入节点接到所有节点发送出来的历史拓扑信息之后,它会根据它自己注册的实际消息内容去决定和哪些节点建立实际的通信连接。如上图所示:新加入节点只和右下角的一个节点之间有拓扑关系,它除了维护所有的节点给它发送出的整个网络拓扑信息之外,同时会和发送节点建立点对点的通信连接。
    • 通过RTPS拓扑发现方式,Apollo ROS去除了对Rosmaster这一个单点的依赖,从而提升整个系统的鲁棒性。这个修改完全是对ROS底层的修改,用户基于原生ROS代码写的节点程序,到Apollo ROS是完全兼容的一个迁移即开发者不需要去改动任何的接口,就可以直接使用RTPS网络拓扑这种新的关系建立。

(3)数据兼容性拓展:

  • 数据兼容性扩展的原因
    apollo学习笔记十六:apollo ROS(中)

    • 原生ROS基于Message的订阅发布消息模型。发送者和接收者在进行实际通讯之前需要进行消息格式定义,其包含字段:基础的数据类型或者复杂的数据类型。在它们进行消息通信的时候,才可以有选择性的去建立通信连接和数据实际发送。如果有一个节点订阅的消息类型不是Channel预先指定的消息类型,这种通信连接是建立不起来的。或是强制指定一个节点去订阅某一种类型的Channel信息,但是它的实际回调函数里却写的是另外一种消息类型,这种编译可能在实际运行的时候就会报相关的一些错误。

    • Message是两个节点进行消息通信的抽象描述文件。这个描述文件提前定义好两个节点之间进行消息通信的基本数据类型。ROS采用这种方式是因为能比较大概率地对两个节点之间进行解耦合,同时两个节点之间也是跨语言的,即不需要关注两个节点是用什么语言写的,都可以通过这种描述文件去进行实际的消息通信。通过Message通信的时候接、收节点在接受到信息之后,会进行MD5的校验、验证这个消息是否符合它的预先订阅,或者是在使用消息之后才会去进行消息的回调处理。
      apollo学习笔记十六:apollo ROS(中)

    • 但是ROS基于Message这种通讯方式有很多的缺点。它最大限度解放两个点之点的一个耦合关系也带来了一些问题。比如Message接口升级,不同版本之间的兼容是需要做大量的适配工作。再如某个模块进行升级,之前所录制的一些实验数据,在进行回放的时候就会产生不匹配的现象。

  • 深度整合Protobuf功能,实现数据兼容性扩展
    Apollo ROS实践里面引入了一种新的消息描述的格式去实现很好的向后兼容即Protobuf。只需要在使用的过程中,定义好必须的字段或者是一些新增的字段,新增的字段我们可以使用Optional属性去描述。在进行模块升级或者是模块之间的消息接口升级的时候,下游模块其实不需要关注新增字段对它来说会造成什么样的影响。如果它要去使用这个字段的话才需要去进行一定程度的适配。如果它的程序不使用这个新增的字段,就不需要做任何的修改。
    apollo学习笔记十六:apollo ROS(中)
    上图是原生ROS和Apollo ROS对数据兼容支持的对比。
    为了做好数据兼容,在原生ROS里面,开发者使用了一个trick:将Proto文件序列化成一个字符串信息放到Message信息里面,完成消息的向后兼容。比起Apollo ROS这个方式有两个明显的缺点:

    • 它增加了一次数据序列化和反序列化。并把Proto序列化信息压到Message里面,增加了两次额外的数据Copy。
    • 如果想实时调试信息,通过Rostopic echo打印出来Message里面那个序列化的字母串,若是采用Wrapper的方式,则这个字符串信息在屏幕上就会是一堆乱码。

Apollo ROS 为了满足数据兼容,深度整合了Protobuf的功能。用户可以直接定义Proto的字段信息,同时信息传递的过程不需要再进行额外的Message的数据转化。另外,在使用调试工具的时候,通过Rostopic echo可以看出原始消息传递的实际展示。

Apollo ROS原理-3

TF坐标系转换

apollo学习笔记十六:apollo ROS(中)

  • 为什么需要TF坐标系转换?
    因为自动驾驶使用的ROS架构是一个松耦合关系,每个节点独立运行,节点有一套自己的XYZ坐标系,当把他们组装到一块时,每个节点的坐标系都是相对独立的,但整个自动驾驶系统需要把每一个节点所使用的信息和一些参数转化到同一个世界坐标系里。TF节点就提供了对应的坐标系转换功能,TF消息也是通过基于Message的订阅和发布消息来完成的。
    apollo学习笔记十六:apollo ROS(中)

  • 当下游的Planning节点想使用Obstacle信息时,需要将Obstacle信息转化到同一个世界坐标系,这时候它会发起一个TF去查询Obstacle处于哪一个世界坐标系里面的哪一个位置,从而感知整个车身周围的情况,基于此再做一个合理的规划和决策行为。

  • ROS也提供了一些基本功能查看TF的运行机制。例如Rosrun提供了TF监控节点,通过这个节点我们能看到整个复杂网络拓扑结构里面节点之间的关系;每个节点之间进行TF转换时所用到的TF树的结构。此外,还提供了tf_echo命令,可以打印从A节点到B节点,例如Perception节点到Planning节点中间所使用了TF转变树的结构。

  • TF也提供了一个插件供开发者使用,可以用一些主流开发工具进行ROS工程开发。如下图所示:
    apollo学习笔记十六:apollo ROS(中)

  • 想使用TF,只需要改动两部分:第一是定义TF的对象;第二是直接进行TF数据查询,然后就可以得到一个世界坐标系。下图是结合之前将Publiser使用TF的一个例子:
    apollo学习笔记十六:apollo ROS(中)

RQT用户接口

apollo学习笔记十六:apollo ROS(中)

  • RQT顾名思义,R是ROS的缩写,QT是可视化的图形工具,RQT是ROS给开发者提供的一套比较方便的图形化相关展示的一套工具。下面介绍几个比较常用的RQT功能:
    • RQT imageview,这个主要是为自动驾驶顶层的一些传感器设计的,例如Camera图像,如果你想实时查看Driver接收图像是否正确,颜色、方位是否有问题,可以通过此工具简单选择对应Camera的channel,用 Camera的topic信息实时查看图像状态。apollo学习笔记十六:apollo ROS(中)

    • Multipot可以将二维的数据在一个二维坐标系里面进行实时展示,这样可以更直观地看到我们所需要的数据是否符合我们的预期。
      apollo学习笔记十六:apollo ROS(中)

    • RQT的graph工具,在开发的实际过程当中使用得比较广泛,这个工具把整个网络拓扑用图形化的方式展现出来。例如启动Perception、 Planning和Roscore这三个节点,它都会在RQT graph工具里面进行实时展示,同时两个节点之间所用的topic信息也会在里面实时展示。apollo学习笔记十六:apollo ROS(中)

    • RQT console是对应ROS日志系统所提供的一套可视化工具。ROS提供了五种级别的LOG展示:DEBUG 、IFNO、WARNING、ERROR、FATAL。每个模块在某一时刻都会产生大量的日志信息,RQT console把这些信息统一规整到一个可视化工具里面,用户可以通过配置的方式快速定位和找到自己所需要的一些相关信息。
      apollo学习笔记十六:apollo ROS(中)

    • RQT logleve是为ROS日志系统所提供的另外一个可视化工具。在写代码的时候,可能5种类型的日志都会使用,但是在实际调试过程中可能只想看到某几种类别的实时信息,通过这个工具可以实时调整,让节点输出我们想要的级别的一些信息。例如我们只想看到ERROR或者FATAL信息,就可以把某一个节点的信息级别设置为ERROR,这样这个节点所打印的ERROR和FATAL的信息可以通过命令行或者LOG文件里面去看到,其它级别的信息不会干扰实时调试。apollo学习笔记十六:apollo ROS(中)

Robot Models URDF

apollo学习笔记十六:apollo ROS(中)

  • 在进行实际模拟的时候,可以用一套语言来定义机器人模型,这套语言被定义为统一机器人描述格式语言URDF。
    apollo学习笔记十六:apollo ROS(中)

  • 它也是一套xml的语言描述,这个描述格式里面包含两个核心的概念:

    • 一个是节点Link
    • 一个是节点之间的连接关系Joint。
    • Joint会指定Parent节点和Child节点,这样就可以描述一个完整的拓扑结构,也就是对整个网络拓扑结构的xml语言化描述。在进行仿真的时候,通过加载对应的URDF文件,在仿真环境里面实时地展示所需要调试的信息。

SDF Simulation Description Format

apollo学习笔记十六:apollo ROS(中)

  • Simulation Description Format(SDF)是另外一个调试工具。
  • 之前介绍的Rviz调试工具,更多的是看到消息收发之间的实体化展示,例如展示点云、图像和其它一些信息。如果进行仿真模拟,如机器人模拟的时候,就用另一套工具Gazebo。Gazebo是ROS的一个开发包,它里面所使用的描述语言就是Simulation Description Format。
  • 用Gazebo加载URDF时,Gazebo首先把URDF描述语言转换成SDF语言,然后再进行加载和展示。

Apollo ROS原理-4

ROS Service

apollo学习笔记十六:apollo ROS(中)

  • ROS提供了三种节点之间通信的方式,第一种是常用的基于消息的一个订阅发布模型,第二种就是上图所示Service,第三种是Param。
  • Service同基于消息的发布订阅模型类似,基于消息的通讯模型上面有一个Topic的概念,Topic底层是通过具体的Massage,来进行消息的定义,与此对应的Service,有一个Service Name与Topic底层相对应。Service下是SRV描述文件,SRV描述文件与MSG描述文件也是比较类似的,唯一一点不同的是在SRV的描述文件里面定义了两种消息,中间以三个短横杠封,该上面是我们请求信息的消息格式定义,下面是响应格式的一个消息格式定义。
  • 下图为Service的一个基本描述:
    apollo学习笔记十六:apollo ROS(中)
    Service对应的SRV文件描述的时候,都有一个Request和Response的方式,这两种方式都可以置空。
  • Service Example
    apollo学习笔记十六:apollo ROS(中)
    apollo学习笔记十六:apollo ROS(中)
  • Service与C++结合
    apollo学习笔记十六:apollo ROS(中)

ROS Action

apollo学习笔记十六:apollo ROS(中)
另一种通信方式,不常用。相比于Service,多了取消和FeedBack相应的功能。

ROS Time

apollo学习笔记十六:apollo ROS(中)
Time的时间源基于PC的系统时间。基于此,提供了一种仿真时间,在自动驾驶开发的过程中,仿真模拟是一个不可缺少的环节,在使用仿真工具的时候,由于使用的数据,可能是很早之前录制的,也有可能在其他地方录制的,在仿真环境下进行模拟的时候,如何回放当时的一个场景或者如何把当时的一个时间给转化为现在的一个时间。ROSTime的话,提供了一个虚拟时钟的功能,其能保证在回放一些历史实验数据,或者其他地方实验数据的时候,来进行一个仿真环境的模拟,即让仿真系统认为现在的场景,就是我们所需要的系统时间和系统场景。

ROS Bags

apollo学习笔记十六:apollo ROS(中)

  • 两个重要的功能:
  • 第一个是把实际车上调试,或者自动驾驶实际进行道路测试的时候的一些原始的传感器数据,按一定的格式录制到某一个bag文件里面。这个bag文件拿到实验室环境或者开发环境后,可以通过不断的回放去复现当时的网络场景,观察新开发的算法是否符合实验预期。
    -第二个是在模型训练时也会用到大量的ROS bag数据,比方同一个路段,在不同天气状况下,算法做出的响应是不同的。可以通过同一个道路场景的不同时段的ROS bag,去对模型进行训练和调优。

调试相关

apollo学习笔记十六:apollo ROS(中)