随着自动驾驶技术不断发展,Apollo 已经从研发走向量产产品落地。作为 Apollo 开源软件平台的一部分,Apollo Cyber RT处于底层的实时操作系统(RTOS) 和算法模块之间,能够在保证高吞吐的情况下,又能低延迟的实时响应上层任务,并保证整个系统确定性的运转。
Apollo Cyber RT 框架核心理念是基于组件,组件有预先设定的输入输出。实际上,每个组件就代表一个专用的算法模块。框架可以根据所有预定义的组件生成有向无环图 (DAG)。
在运行时刻,框架把融合好的传感器数据和预定义的组件打包在一起形成用户级轻量任务,之后,框架的调度器可以根据资源可用性和任务优先级来派发这些任务。
TIPS本次沙龙课程,我们有请到Apollo开发者社区荣誉布道师——贺志国来详细讲解Apollo3.5技术架构。
贺志国,毕业于国防科技大学信息与通信工程专业,曾从事部队装备信息化方面的工作,目前负责CiDi(希迪智驾)重卡L4级自动驾驶项目。擅长C++编程,及Apollo实时相对地图及规划模块。在Apollo GitHub上贡献过65 commits,代码9117行(10,450+, 1,333-),解答issues 80+条,近一年来位于Apollo GitHub贡献榜排名第25位。
在这次的分享中,贺志国将从Cyber RT运行原理及如何使用全新的计算框架出发,全面解读Cyber RT计算框架,带领大家更好的学习上手Cyber RT。
以下,ENJOY
Apollo 3.5提供了Cyber RT作为中间件,对计算任务和通信进行优化。本节描述如何将以前的Apollo项目迁移到Cyber RT框架下,主要分三个部分:功能模块代码的迁移,辅助工具的迁移和调试工具的迁移。
功能模块迁移的基本步骤
Cyber RT框架基于组件的概念加载各个功能模块,很多模块都是作为Cyber RT的组件而存在,所有这些组件都是基于Cyber RT提供的调度程序mainboard加载运行。
整个功能模块迁移,是不需要修改任何算法逻辑的,它仅仅需要修改消息接收发布的代码以及基于Cyber RT进行调度的一些配置文件,整个迁移的工作量占整体开发工作量的10%,如下图所示。
基于Cyber RT框架创建和发布新的功能模块组件的基本步骤
下面以规划模块Planning进行阐述,迁移包含五个步骤:
按照Cyber RT框架设置组件的文件结构;
实现Cyber RT的组件类;
提供一个BUILD文件,绝大多数和之前ROS版本没有什么太大差别,主要有两处修改,目的是给bazel编译工具提供生成Cyber RT共享库文件的配置信息。
加载Cyber RT框架的一个配置文件。
启动组件的方式。
在路径Apollo_Home/modules/planning下组件文件结构如下,后面都会以路径Apollo为准。
基于路径Apollo-Home /modules/planning设置组件文件结构
设置组件的文件结构要进行以下的操作。
首先创建一个planning_component.h的类;
接下来提供一个构建文件BUILD;
提供一个DAG配置文件还有一个Launch配置文件。
实现的组件类就是这个头文件.h和.cc的类。要基于组件类Component派生出规划组件组件类PlanningComponent,在派生类planningComponent中重载虚函数Init()及Proc()函数,如果不重载这两个虚函数就无法加载我们的规划组件。此外,必须使用宏CYBER_REGISTER_COMPONENT(PlanningComponent)注册,以便Cyber RT能够找到规划组件。
实现组件类
来看PlanningComponent的声明也就是头文件,这个类是基于一个模块类,Component现在接受是三个模块参数,分别是预测的障碍物消息(障碍物短时间内的预测轨迹)、底盘消息(车辆自身的状态,包括速度、变速箱档位等)、定位消息(车辆的位置和车头朝向)。
实现组件类—PlanningComponent.h
还包括重载的两个虚函数和一些消息接收、发布对象。 这里要注意的是,和ROS版本有最大的差异是:ROS版本是从定时器出发的,每0.1秒触发一次,现在的planning模块不是通过定时器而是通过消息触发,这三个消息必须同时到达, planning模块才会被触发。
这种机制最大的优势就是处理接收到的消息肯定是一致的,并能节约计算资源。
消息处理的回调函数:每收到一个Routing Response消息,就会通过一个Lambda表达式展现的回调函数来进行处理。处理的逻辑很简单,第一步就是输出日志,第二把发过来的消息拷到成员变量里面,这个Lock Guard是一个RAII(Resource Acquisition Is Initialization,资源获取即初始化)类型的变量,所以就不需要对锁进行显式地释放。
Planning模块有两个输出,第一个是消息输出,就是规划的轨迹,可以通过Planning Writer创建消息发布对象。再就是Rerouting Writer,创建一个重新寻找全局导航路径的消息发布器对象。
实现组件类—PlanningComponent::Proc()
消息处理Proc函数的处理逻辑实际上很简单。第一部分就是检查输入是否正确。第二步调用实际的规划算法生成局部的规划轨迹。第三步是将规划轨迹发布出去。
BUILD文件主要是给bazel编译工具使用的,和之前的BUILD文件相比,有两点不同。一是要创建planning_component_lib组件库;二是基于该组件库创建一个lib_planning_component.so的共享库文件。这里面有两个编译选项,就是Linkshared=True,Linkstatic=False。
提供构建文件
有向无环图文件。它有几个配置项,第一是共享库的文件路径,第二是类名。
提供DAG配置文件
注意这些flag file path,要把它的绝对路径写出来,否则无法加载。
Cyber RT提供了一个Python工具来加载launch加载组件,launch里面元素很简单,第一是模块名,第二是dag_conf的路径,再就是显示的进程名,模块可以配置多个。
提供Launch配置文件
组件启动有三种方式,第一种调试程序用得比较多,直接使用mainboard程序加载。
第二是使用launch的文件启动,加载命令永远只有一个launch文件,如果你需要加载多个组件配置多个组件就可以。
基于Dreamview的Hmi_modes组件启动,实际上调用的全是mainboard。在目录下有很多的配置文件,这个配置文件分为两组,第一是compute这个组,全部是使用compute_sched这个组调度的。第二组是controller,优先级更高,前面compute的组件优先级低一些。
启动组件
可以通过上述创建功能模块组件,并使用mainboard程序来加载Cyber RT组件,还可以在main函数里面直接使用Cyber RT框架接收或发布消息。下图是一个接收消息的示例,首先初始化Cyber RT的框架,然后创建一个接收的对象节点,第三步是根据这个节点创建一个消息接收对象,并且注册回调函数MessageCallback。一旦有Chatter类型的消息传入,则自动进行回调函数MessageCallback进行处理。
在main函数中单独使用Cyber RT
发布消息也是类似的逻辑。
下图是ROS和Cyber RT常用辅助工具的对比,包括处理数据文件、查看通道消息以及可视化等,每一个ROS的常用工具在Cyber RT中都有对应的模块。
常见工具对比
下图是ROS和Cyber RT中常用命令的对比情况,比如播放一个包,查看一个包的信息,录制topic,过滤topic等等。
常用命令迁移
另外,之前使用ROS录制了很多bag,不能浪费了。Cyber RT提供了一个工具Rosbag_to_record,将ROS的bag包转换为对应的Cyber record,需要注意的是,该转换工具只对一些常见的topic进行转换,如果是自己写的topic,需要自己写逻辑。具体可以参考Apollo提供的帮助文档,如下图所示。
ROS bag转换为Cyber record
此外,Cyber RT提供了相应的API,直接从record文件读写数据,类似ROS读写bag文件的操作。具体的API接口可以参考Apollo/cyber/record目录下相应的文件。
直接使用ROS API读写bag文件代码的迁移
下图给出了Cyber RT中读写record文件的一段示例代码。
Cyber RT中读写record文件的示例代码
Cyber RT提供了Python接口,供开发者进行数据分析,具体可以参见apollo/cyber/python/examples文件。
可以使用VSCode调试和GDB对相应的功能模块进行调试。关于使用VSCode编译、调试Apollo项目,可以参见下图给出的链接。需要注意的是VSCode是基于脚本语言TypeScript开发,调试效率比较低,要等半天才能出来结果。建议直接使用GDB命令调试。
使用VSCode调试
GDB调试基于ROS的Apollo项目有两种方式,第一直接启动模块调试,第二是通过Dreamview启动相关模块进行调试,具体调试命令如下图所示。
使用命令行调试—ROS版本
同样,也可以使用GDB对基于Cyber RT的Apollo项目进行调试。需要注意的是,基于Cyber RT的Apollo项目调试的主程序永远是mainboard,其后接的是将要调试的模块,如下图所示。
使用命令调试—Cyber RT版本
下面我们看看如何调试Planning 模块。
首先下载并构建Apollo Master分支,启动并进入Docker编译原代码,接下来启动Dreamview程序。
下载并构建Apollo master分支和启动Dreamview
在Module Controdler标签中打开Routing及Planning模块。在Docker内使用cyber_monitor可以查看各个模块的消息。
打开Routing及Planning模块
如果要调试Planning模块,首先通过相应的命令查找到Planning 对应的进程,然后使用下图给出的命令,基于GDB对该模块进行调试,进入到对应模块之后,可以使用相应的命令设置断点,整个过程如下图所示。
在Docker内部通过指令调试Planning模块
下面是一个调试Planning模块的小视频,仅供大家参考。
调试Planning模块的小视频
还有一些相关的内容可以参考下图给出的链接。
其他内容参考链接
*贺志国博客链接:
https://blog.csdn.net/davidhopper
原版PPT获取方式:
进入公众号
回复关键词『3.5技术沙龙3』
根据提示获得本次分享资料
点击文章左下角『』
可看视频回顾
自Apollo平台开放已来,越来越多开发者积极地参与到我们所开设的沙龙课程当中,并基于Apollo擦出更多火花。
欢迎开发者继续关注Apollo开发者社区每月的课程分享,获取更多学习资料和自动驾驶相关技术内容。
开发者还可以添加Apollo小哥哥(微信号: apollo_xzs)为好友,加入开发者交流社群与业内开发者进行互动,期待大家的沟通交流!