一、Cyber框架
- Cyber实现的功能主要包括如下几方面:
---消息队列,主要作用是接收和发送各个节点的消息,设计到消息的发布,订阅以及消息的buffer缓存等.
---实时调度,主要作用是调度处理上述消息的算法模块,保证算法模块能够实时调度处理消息.
---用户接口,主要提供灵活的用户接口.
---工具,提供一系列的工具,如bag包,点云可视化,消息监控等.
- Cyber的入口
---cyber的入口在"cyber/mainboard"目录中:
|---mainboard.cc //主函数
|---module_argument.cc //模块输入参数,主要是解析加载DAG文件时候带的参数.
|---module_argument.h
|---module_controller.cc //模块加载,卸载
|---module_controller.h
上述cyber mainboard的整个流程,cyber main 函数中先解析dag参数,然后根据解析的参数,通过类加载器动态的加载对应的模块,然后调用Initalize方法初始化模块.
---类加载器(class_loader):类加载器的作用就是动态的加载动态库,然后实例化对象.
- Cyber的数据流程
Cyber的数据流程主要分为6个过程:
1、Node节点中的Writer往通道里面写数据.
2、通道中的Transmitter发布消息,通道中的Receiver订阅消息.
3、Receiver接收到消息后,触发回调,触发DataDispather进行消息分发.
4、DataDispather接收到消息后,把消息放入CacheBuffer,并且触发Notifier,通知对应的DataVisitor处理消息.
5、DataVisitor把数据从CacheBuffer中读出,并且进行融合,然后通过notifer_唤醒对应的协程.
6、协程进行对应的注册回调函数,进行数据处理,处理完成之后接着进入睡眠状态.
数据流程各个模块之间的关系:
1、Component和Node之间的关系
Component是Cyber中封装好的数据处理流程,对用户来说,对应自动驾驶中的Planning Component, Perception Component等,目的是帮助我们更方便的订阅和处理消息.实际上Component模块在加载之后会执行"Initialize()"函数,这是个隐藏的初始化过程,对用户不可见.在"Initialize"中,Component会创建一个Node节点,概念上对应ROS的节点,每个Component模块只能有一个Node节点,也就是说每个Component模块有且只能有一个节点,在Node节点中进行消息订阅和发布.
2、Node和Reader\Writer的关系
在Node节点中,可以创建Reader订阅,也可以创建Writer订阅,每个Node节点中可以创建多个Reader和Writer.
3、Reader和Receiver,Writer和Transmitter,Channel的关系
一个Channel对应一个Topic,概念上对应ROS的消息通道,每个Topic都是唯一的,而Channel中包括一个发送器(Transmitter)和接收器(Receiver),通过Reiceiver接收消息,通过Transmitter发送消息.
一个Reader只能订阅一个通道的消息,如果一个Node需要订阅多个通道的消息,需要创建多个Reader。同理一个Writer也只能发布一个通道的消息,如果需要发布多个消息,需要创建多个Writer.
Reader中调用Receiver订阅消息,而Writer通过Transmitter发布消息.
4、Receiver, DataDispather 和 DataVistor的关系
每一个Receiver接收到消息之后,都发触发回调,回调中触发DataDispather(消息分发器)发布消息,DataDispather是一个单例,所有的数据分发都在数据分发器中进行,DataDispather会把数据放到对应的缓存中,然后Notify(通知)对应的协程(实际上这里是调用的DataVisitor中注册的Notify)去处理消息.
DataVistor(消息访问器)是一个辅助的类,一个数据处理过程对应一个DataVistor,通过在DataVistor中注册Notify(唤醒对应的协程,协程执行绑定的回调函数),并且注册对应的Buffer到DataDispather,这样在DataDispather的时候通知对应的DataVistor去唤醒对应的协程.
也就是说DataDispather(消息分发器)发布对应的消息到DataVistor,DataVistor(消息访问器)唤醒对应的协程,协程中执行绑定的数据预处理回调函数.
5、DataVisitor和Croutine的关系
实际上DataVisitor中的Notify是通过唤醒协程(为了方便理解也可以理解为线程,可以理解为你有一个线程池,通过线程池绑定数据处理函数,数据到来之后就唤醒对应的线程去执行任务),每个协程绑定了一个数据处理函数和一个DataVisitor,数据到达之后,通过DataVisitor中的Notify唤醒对应的协程,执行数据处理回调,执行完成之后协程进入休眠状态.
6、Scheduler, Task和Croutine
通过上述分析,数据处理的过程实际上就是通过协程完成的,每一个协程被称为一个Task,所有的Task(任务)都由Scheduler进行调度。从这里我们可以分析得出实际上Cyber的实时调度由协程去保障,并且可以灵活的通过协程去设置对应的调度策略,当然协程依赖于进程,Apollo在linux中设置进程的优先级为实时轮转,先保障进程的优先级最高,然后内部再通过协程实现对应的调度策略.
二、Cyber Component 介绍
Component的流程
1、创建node节点(1个component只能有一个node节点,之后用户可以用node_在init中自己创建reader和writer).
2、调用用户自定义的初始化函数Init()(子类的Init方法)
3、创建reader,订阅几个消息就创建几个reader.
4、创建回调函数,实际上是执行用户定义算法Proc()函数.
5、创建数据访问器,数据访问器的用途为接收数据(融合多个通道的数据),唤醒对应的协程执行任务.
6、创建协程任务绑定回调函数,并且绑定数据访问器到对应的协程任务,用户唤醒对应的任务.