【Baidu Apollo】Apollo 3.5 计算框架(Cyber RT)

时间:2024-03-31 08:41:50

文章转载自Apollo开发者社区

【Baidu Apollo】Apollo 3.5 计算框架(Cyber RT)

Apollo 3.5 总体架构

最新发布的 Apollo 3.5 总体架构从上到下仍分为四层,最底层为车辆平台,自动驾驶汽车需要对车辆进行线控改造,使得车载大脑可以通过电信号来控制车辆的执行器;

往上一层是硬件平台,包括计算单元、传感器以及 V2X 相关接收设备等。

再上一层是软件平台,主要包括操作系统、中间件、算法模块等。

最顶层的是云端服务,主要包括地图、OTA 服务升级、数据平台、语音交互等方面。

此次发布的Apollo 3.5,解锁了Apollo 在复杂城市道路中的自动驾驶能力,开源Cyber RT 计算框架、V2X 车路协同方案以及硬件平台的一些能力

【Baidu Apollo】Apollo 3.5 计算框架(Cyber RT)

Apollo所使用的 ROS 系统

Apollo最初使用的中间件是 ROS(机器人操作系统)。概括来说,ROS系统主要包含三方面内容:

  • 第一,通信系统。

ROS是个分布式的松耦合系统,算法模块以独立进程形式存在,也就是我们常说的Node。

ROS基于Socket实现了Pub/Sub的通信方式,不同的算法节点(Node)之间通过Pub/Sub发送/接收消息。

  • 第二,Framework&Tools (框架和工具)。

开发者可基于ROS提供的Client Library和通信层,方便地收发消息。

开发者只需要关注消息处理相关的算法,而至于算法何时被调用,全部由框架来处理。

  • 第三,生态系统。

从社区内,开发者可以很方便地寻找到很多现成的「传感器驱动」和「算法实现」等进行参考。

【Baidu Apollo】Apollo 3.5 计算框架(Cyber RT)

ROS自动驾驶实践中遇到很多挑战

随着自动驾驶的发展,不少开发者,包括Apollo平台,把ROS应用于自动驾驶系统,毕竟自动驾驶汽车也相当于一个大的机器人。但是我们在实践中仍然遇到很多挑战。

  • 首先,ROS 中的算法模块是以独立的进程形式存在的,进程之间该以什么样的顺序去执行?

实际上,Linux 本身是一个通用系统,内核中的调度器对上面的算法业务逻辑并不清楚。它只是在尽量满足公平的情况下让大家都得到调度。所以,ROS Node运行顺序并无任何逻辑。但本质上自动驾驶是一个专用系统,任务应按照一定的业务逻辑执行。

那么是在ROS层加一个Node,由其来同步各个算法任务的运行,还是在Linux内核中实现新的调度策略,使其结合算法业务逻辑进行调度?前者的开销,后者的迁移性,都是需要思考的问题。

  • 其次,ROS是一个分布式的系统。

既然是分布式系统,就要有通信的开销。即使在同一个物理节点上,依然存在着通信的开销。所以Apollo前期曾经使用共享内存去降低ROS原生的基于Socket通信的开销。ROS2 也在使用 DDS 解决通信方面的实时性。ROS也支持Nodelet模式,这可以去掉进程间通信的开销,但是调度的挑战依然存在。

  • 第三,除了调度的不确定性,ROS系统中还存在其他很多不确定的地方,比如内存的动态申请。

【Baidu Apollo】Apollo 3.5 计算框架(Cyber RT)

Cyber RT的运行流程

算法模块通过有向无环图(DAG),配置任务间的逻辑关系。对于每个算法,也有其优先级、运行时间、使用资源等方面的配置。系统启动时,结合DAG、调度配置等,创建相应的任务,从框架内部来讲,就是协程,调度器把任务放到各个Processor的队列中。然后,由Sensor输入的数据,驱动整个系统运转。

【Baidu Apollo】Apollo 3.5 计算框架(Cyber RT)

Cyber RT的架构及包含地软件模块

基本上,Cyber RT包括如下软件模块: 

  • 最下面一层是基础库,为了高效,Cyber RT实现了自己的基础库。

比如我们实现了Lock-Free的对象池,实现了Lock-Free的队列,随着成熟,会陆续开放更多。除了框架自身外,将来也会逐渐应用于算法模块。除了效率原因以外,也希望Cyber RT减少依赖。

  • 再往上是通信相关的。包括服务发现,还有Publish-Subscribe通信机制。

Cyber RT也支持跨进程、跨机通信,上层业务逻辑无需关心,通信层会根据算法模块的部署,自动选择相应通信机制。 

通信层之上是数据缓存/融合层。

  • 多路传感器之间数据需要融合,而且算法可能需要缓存一定的数据。

比如典型的仿真应用,不同算法模块之间需要有一个数据桥梁,数据层起到了这个模块间通信的桥梁的作用。

  • 再往上是计算模型,计算模型包括刚才前面提到的调度和任务,后面我们会详细讨论。
  • 计算模型之上是为开发者提供的接口。

Cyber RT为开发者提供了Component类,开发者的算法业务模块只需要继承该类,实现其中的Proc接口即可。

该接口类似于ROS中的Callback,消息通过参数的方式传递,用户只要在Proc中实现算法、消息处理相关的逻辑。

  • Cyber RT也基于协程,为开发者提供了并行计算相关的接口。
  • Cyber RT也为开发者提供了开发调试、录制回放等工具,未来还会开放性能调试工具。

【Baidu Apollo】Apollo 3.5 计算框架(Cyber RT)

将调度、任务从内核空间搬到用户空间

ROS的主要挑战之一是没有调度。为了解决ROS遇到的问题,Cyber RT的核心设计将调度、任务从内核空间搬到了用户空间。

调度可以和算法业务逻辑紧密结合。

从Cyber RT角度,OS的Native thread相当于物理CPU。

在OS中,是内核中的调度器负责调度任务(进程、线程……)到物理CPU上运行。

而在Cyber RT中,是Cyber RT中的调度器调度协程(Coroutine)在Native Thread上有序运行。

【Baidu Apollo】Apollo 3.5 计算框架(Cyber RT)

任务编排策略

每个Processor (Native Thread) 一个任务队列,由调度器编排队列中的任务。

任务在哪个CPU上运行?任务之间是否需要相邻运行?哪些先运行?哪些后运行?

这些都由调度器统一调度,任务基于协程实现。在任务阻塞时,快速让出CPU

每个物理CPU上除运行1个Normal级别的Thread 外,运行着另外1+个高优先级的Thread。

基于此,实现用户空间的高优先级的任务抢占运行。

比如,之前去GPU运行的算法,在GPU上完成运行返回后,应该尽快的得到运行。

这种调度策略,很好的结合了业务逻辑、数据共享和算力的平衡。并且任务不会在不同CPU上随机的调度来调度去,具有非常好的Cache友好性。

【Baidu Apollo】Apollo 3.5 计算框架(Cyber RT)

任务编排策略,不仅需要对业务逻辑的深度理解,也需要结合计算机的算力等综合考虑。

因此,Cyber RT也提供类似经典线程池模式的调度算法,这种模式几乎不存在配置的代价。

对此,Cyber RT也做了一些改进,比如为了减小锁的瓶颈,任务是多队列的。

任务队列也支持优先级,后续还会支持分组,通过组控制算法对资源的使用。

协程

【Baidu Apollo】Apollo 3.5 计算框架(Cyber RT)

Cyber RT使用了协程作为算法任务的载体。

协程之于线程,就类似于线程之于物理CPU,由Cyber RT中的调度器负责在各个线程之上周而复始,切换调度协程。

为了算法模块在其他协处理器执行计算时,可以让出Processor(Native Thread),并在完成之后,回来时可以再次运行,Cyber RT采用了有状态的协程。

那么Cyber RT为什么采用协程呢?

除了协程的切换非常快之外,调度的确定性是另外一个重要的原因

举个典型的例子,假设用Native Thread去执行一个任务。

当任务因为去GPU等加速器运算时,或者因为资源原因被Block时。在Thread就绪时,什么时候调度上来,其实是一个非确定的过程,完全依赖于操作系统以及其上任务的情况。

【Baidu Apollo】Apollo 3.5 计算框架(Cyber RT)

Cyber RT也支持跨进程、跨机通信。

实际部署中,也存在着比如某个工具需要运行在独立的进程,安全系统部署在另外的节点。

因此,Cyber RT也支持跨进程、跨机通信。上层业务逻辑无需关心,通信层会根据算法模块的部署,自动选择相应通信机制。