1.Cyber RT框架介绍
1.1 Cyber RT简介
Apollo Cyber是首个专为自动驾驶定制的高性能且开源的实时通信框架,于2019年与Apollo 3.5开放平台同期发布,它主要解决了自动驾驶系统的高并发、低延迟、高吞吐、任务调度等问题,同时还提供了多种通信机制和用户级的协程,在资源有限的情况下会根据任务的优先级来进行调度处理。
如下图所示,Cyber RT通过Component来封装每个算法模块,通过有向无环图(DAG)来描述Components之间的逻辑关系。对于每个算法模块,都有其优先级、运行时间、使用资源等方面的配置。系统启动时,系统会结合DAG文件、调度配置等信息,创建相应的任务,从框架内部来讲,就是协程,然后图中间的调度器把任务放到各个处理器的队列中,最后由左上角Sensor输入的数据,驱动整个系统运转。
(1)Cyber RT定义
Cyber RT是百度Apollo推出的代替ROS的消息中间件,他是一个开源、高性能的运行实时框架。
(2) Cyber RT的应用背景
Apollo 3.5以前使用的系统为ROS,各节点之间的通信方式为进程间的通信。在实际的应用中,ROS在自动驾驶
领域遇到很多挑战:
首先ROS的算法模块以独立进程的形式存在,独立进程的节点的运行顺序无法确定,因此业务逻辑的调度顺序无法保证。
其次,ROS是一个分布式的系统,存在通信的开销。
此外,ROS系统中还存在其他很多不确定的地方,比如内存的动态申请。ROS的资源分配时不确定的,
(3) Cyber RT主要作用
Cyber主要的作用就是一个消息中间件,它们需要管理不同的模块,并让它们互相之间可以高效通信。在Apollo6.0中,它作为RTOS和自动驾驶各个模块的中间通信接口。
(4) Cyber RT 主要功能
Cyber是一个分布式收发消息,和调度的框架,同时对外提供一系列的工具和接口来辅助开发和定位问题。Cyber提供的功能主要包括一下方面:
消息队列: 主要作用是接收和发送各个节点的消息,涉及到消息的发布、订阅以及消息的buffer缓存等。
实时调度: 主要作用是调度处理上述消息的算法模块,保证算法模块能够实时调度处理消息。
用户接口: Cyber提供了灵活的用户接口
开发工具: 提供了一系列的工具包括消息监控(Cyber_monitor),消息可视化(Cyber_visualizer),录制/回放工具(Cyber_recorder), ros包录制(rosbag_to_recorder)。
1.1.1 框架优势
Apollo Cyber框架的优点主要有以下几点:
* 高性能:Apollo Cyber是专为自动驾驶定制的高性能运行时框架,能够处理高并发、低延迟、高吞吐等自动驾驶系统中的复杂任务。
* 灵活性:Apollo Cyber提供了多种通信机制和用户级的协程,可以根据任务的优先级来进行调度处理,能够灵活地应对各种自动驾驶场景。
* 可靠性:Apollo Cyber通过Component来封装每个算法模块,通过有向无环图(DAG)来描述Components之间的逻辑关系,能够确保系统各个模块之间的稳定性和可靠性。
* 扩展性:Apollo Cyber的架构可以支持各种不同类型和不同层次的自动驾驶任务,并且可以方便地扩展和升级。
*
1.1.2 框架结构
(1)粗略划分
Cyber RT的框架可以粗略的分为以上几个模块:
消息队列:主要作用是接收和发送各个节点的消息,涉及到消息的发布、订阅以及消息的缓存等
实时调度:主要是调度处理上述消息的算法模块,保证算法模块能够实时调度处理消息
用户接口:提供相关接口,将算法模块接入CyberRT的框架之内。
Log+Tool:提供高效的日志打印,以及一系列的工具,比如bag包播放,点云可视化,消息监控灯
总结起来就是,cyber是一个分布式收发消息,和调度框架,同时对外提供一系列的工具和接口来辅助开发和定位问题。
(2)详细划分
详细一点的框架结构如上图所示:
* 第一层:主要是Apollo实现的基础库,比如有Lock-Free的对象池,Lock-Free的队列等。实现基础库可以减少相关的依赖并提高运行效率。
* 第二层、三层:主要是负责管理Cyber的通信机制,包括服务发现(负责管理通信中的Node节点)和Publish-Subscribe通信机制。并且Cyber也支持跨机、进程间、进程内通信,而且会根据不同的数据传输和业务逻辑自动选择效率最高最匹配的通信方式来进行通信。
* 第四层:主要是对传输过后的数据进行缓存,并会根据不同传感器得到的数据进行融合得到一个可处理可读的数据发送给另一个模块。
* 第五、六层:主要是管理每一个任务的调度和数据处理。
* 第七层:提供给开发者的一些API接口,让开发者有更多可操作性,提高开发效率。
1.2 框架各部分数据处理流程
我们先看下cyber中整个的数据处理流程,通过理解数据流程中各个模块如何工作,来搞清楚每个模块的作用。
如上图所示,cyber的数据流程可以分为6个过程:
Node节点中的Write往通道里面写数据
通道中的Transmitter发布消息,通道中的Receiver接收消息
Receiver接收到消息之后,触发回调,触发DataDispather进行消息分发
DataDispather接收到消息之后,把消息放入CacheBuffer中,并且触发Notifier,通知对应的DataVisitor处理消息
DataVisitor把数据从CacheBuffer中读出,并且进行融合,然后通过notifier_唤醒对应的协程
协程执行对应的注册回调函数,进行数据处理,处理完成之后进入睡眠状态
1.3 运行流程
上图为Cyber RT在开发过程中的运行流程:
算法模块通过有向无环图(DAG),配置任务之间的逻辑关系。对于每个算法,也有其优先级,运行时间,使用资源等方面的配置。
系统启用时,结合而dag,调度配置等,创建相应的任务,从框架内部来讲,就是协程,调度器把任务放到各个Processor队列中
然后,由Sensor输入数据,驱动整个系统运转
1.4 Scheduler调度器简介
ROS 的主要挑战之一是没有调度,为了解决 ROS 遇到的问题,Cyber RT 的核心设计将调度、任务从内核空间搬到了用户空间,在原生的thread上加了一层协程(Coroutine),Cyber RT主要调度的就是协程。调度可以和算法业务逻辑紧密结合。
从 Cyber RT 角度,OS 的 Native thread 相当于物理 CPU。在 OS 中,是内核中的调度器负责调度任务(进程、线程…)到物理 CPU 上运行。而在 Cyber RT 中,Cyber RT 中的调度器调度协程(Coroutine)在 Native Thread 上有序运行。
上面设计到的Native Thread与Processer是相同的概念。
1.4.1 调度策略
1.4.2 协程
协程,即线程更上一层的载体,Cyber RT调度器调度有状态的协程在各个线程上运行。协程不仅切换快,而且调度有着高确定性,不像线程的调度完全依赖操作系统。
1.4.3 调度
如何能够有效分配计算资源,是调度主要解决的问题。Cyber的调度主要是基于任务优先级来并行运行,主要的目的就是保证系统在资源有限的情况下来提高运行效率。如图所示是一个任务调度的示意图,首先在系统初始化时,会对每一个模块的Component进行创建并初始化,每个Component都会有一个proc()函数,proc函数主要是描述了每个需要处理的数据和数据处理的方法。在运行时,每当生成新的数据时,调度器会将数据和对应的proc函数绑定程一个可执行的task,然后放到Task queue中执行,Task queue是一个有序的列表,每当任务入队的时候,会根据任务本身的优先级和周期状态对Task queue里的人物进行重新排序,以确保高优先级的任务能被第一时间执行,Thread Pool是实际执行任务的线程池,根据不同的硬件配置会有固定数量的Worker,每个Worker会持续得从Task queue中读取可执行的任务并进行执行。
1.5 代码框架与层级简介
核心类是:
(1)Component和TimerComponent
(2)支撑component的是Node,Scheduler,Timer,DataVisitor
(3)Reader,Writer,ChannelImple,TimerTask
1.5.1 启动顺序
从代码启动顺序上来说如下图所示:
1.5.2 开发流程
从开发流程上来说,可以分为以下几个层级:
1.6 总结
(1)CyberRT 是基于 Fast-RTPS 进行消息驱动的,所以业务模块可以基于此进行数据通讯,这个和 ROS2 没有多大差别;
(2)CyberRT 通过 Node 节点进行通信的底层对接,而 Component 则负责具体业务相关的逻辑;CRoutine 基于消息驱动的基础上,将 Component 中的 Proc 回调作为基础的协程执行单元,然后根据 Sheduler 相应的调度策略进行调度,它保证了多任务的执行顺序;
(3)TimerComponent 依靠 Timer 进行定时触发,它的 proc 方法被封装成为 TimeTask 中的回调,TimingWheel 根据调度策略进行定时任务执行;
(4)CyberRT 强大之处在于它的 3 个拓扑网络,基于 fast-rtps 通过服务发现,能够快速找到相应的 Node 状态,也因为这个特性,这在我之前分析的系统监控 Monitor 中,能够轻松监控每个模块的健康状态。