工程框架升级之PnC包管理2.0解读
上周我们发布了Apollo开放平台9.0版本,为大家的学习和开发带来了新的体验。本节课我们将对planning模块的升级,进行详细的解读。Planning 模块框架比较复杂,代码量多,为了方便用户学习和上手,并能快速根据自己的需求开发扩展功能,planning 在框架上进行了一些调整,具体有如下内容:
- 统一的对外接口:升级后 pnc 的接口统一封装在 external_command 模块处理,隔离了上层业务调用和 PNC 模块的接口变化。
- 分级参数配置机制:划分全局参数和局部参数,局部参数放在插件中独立管理,便于开发者查询和修改。
- 全新插件扩展机制:将 scenario,task 和 traffic rules 插件化,便于用户独立开发部署自己的插件,通过配置流程来启动运行插件。
统一的对外接口
apollo原来的接口是通过topic发送,接口命令直接分发到PNC的各个模块,数据流如下图所示:
在9.0版本中对这些接口进行了优化和升级:
- 接口统一封装,增加了接口处理层external_command,隔离了业务层和PNC内部模块,两者中某个接口发生变化时不影响另外一个
- 调用接口时,用户不需要关心命令发送给哪个模块,在external_command模块中可以统一查找所有支持的命令列表。
- 改用cyber中service-client机制调用,用户可以通过client查询当前任务的执行状态。
- 对常用导航命令RoutingRequest做了精简:
- 原来的RoutingRequest需要查询地图,找到路由点和终点最近的车道,并得到在车道上对应的投影点;精简后的命令只需要给出坐标和朝向即可。
- 发送导航命令不再需要发送车辆当前的位置作为起点位置,PNC会自动获取并处理起点位置。
- 支持用户灵活扩展自己的命令,如清扫车添加打开盘刷的命令等
升级后的命令数据流程如下图:
命令升级后兼容旧的功能接口,对照关系如下表所示:
功能 | 升级前命令 | 升级后命令 | 升级说明 |
点到点沿道路行驶 | routing::RoutingRequest | LaneFollowCommand | 精简了路由点信息,新的命令给出坐标和朝向,不需要查询地图找到最近的LaneWayPoint |
泊车 | routing::RoutingRequest(包含parking_space_id) | ValetParkingCommand | 升级前后都是给定parking_space_id进行泊车 |
PULL_OVER,START,STOP流程控制 | planning::PadMessage | ActionCommand | 升级后合并流程操作到一个命令中 |
切换自动/手动模式 | control::PadMessage | ActionCommand | 升级后合并流程操作到一个命令中 |
例如,当用户希望发送一个点到点行驶的命令时,按照以下步骤进行即可:
- 创建发送命令请求的cyber::Client
// 注册Node节点,节点名称用户自己定义std::shared_ptr<apollo::cyber::Node> node = apollo::cyber::CreateNode("my_node");// 注册client发送Command命令,Client为模板类,模板第一个参数是发送的命令类型,第二个参数类型固定,为planning模块反馈的任务执行状态类型apollo::cyber::Client<apollo::external_command::LaneFollowCommand, apollo::external_command::CommandStatus>> client =std::shared_ptr<node_->CreateClient<apollo::external_command::LaneFollowCommand, CommandStatus>("/apollo/external_command/lane_follow");
- 生成命令数据
auto command = std::make_shared<apollo::external_command::LaneFollowCommand>();// 填充命令header信息,第一个参数为当前运行的模块名称,用户自定义apollo::common::util::FillHeader("my_module", command.get());command->set_command_id(1);// 设置LaneFollow的终点坐标,在此给定终点坐标位姿即可,不再需要从地图中查询终点附近车道的信息command->mutable_end_pose()->set_x(10.0);command->mutable_end_pose()->set_y(3.5);command->mutable_end_pose()->set_heading(0.5);// 设置巡航速度。不设定的情况下,会按照planning中配置的默认巡航速度行驶command->set_target_speed(3.0);
- 发送命令
auto response = client->SendRequest(command);// 在此可以获得命令发送的结果if(nullptr == response){std::cout <<"Command sending failed, please check the service is on!\n"<< std::endl;}else{std::cout <<"******Finish sending command.******\n"<< std::endl;}
从以上步骤可以看出,发送点到点行驶的命令时,用户需要发送的信息比原来简化了很多,并且可以获取命令接收的状态。
分级参数配置机制
planning中的配置参数量较大,入门调试时难度较高,用户想要修改的功能对应的参数不直观,并且不易快速定位需要修改的参数。针对这些问题,将参数分成全局变量和局部变量:
- 全局变量是多个算法或插件中共同使用的参数;
- 局部变量是专属于某个算法或插件的参数。如果用户需要调整某个插件的参数,直接在插件的目录中查找。
分级后的参数配置目录结构如下:
具体修改后主要参数的分布如下:
- 全局配置参数:
全局配置参数 | 文件目录结构 | 文件 | 说明 |
planning_base | proto | planning_config.proto | planing全局配置参数定义 |
traffic_rule_config.proto | traffic_rule生效列表配置定义 | ||
其他基础算法配置参数.proto | 其他算法配置参数定义 | ||
conf | planning.conf | 全局flag变量 | |
planning_config.pb.txt | planning全局配置参数 | ||
traffic_rule_config.pb.txt | 选择traffic_rule生成列表 | ||
其他基础算法配置参数.pb.txt | 其他算法配置参数定义 | ||
common | planning_gflags.h | 全局flag变量定义 | |
scenario_base | proto/scenario_pipeline.proto | scenaro的pipeline定义 |
- 局部配置参数scenarios:
局部配置参数 | 场景 | 文件目录结构 | 一级目录 | 二级目录 | 说明 |
scenarios | park_and_go | proto | park_and_go.proto | Scenario:park_and_go scenario的配置参数定义 | |
conf | pipeline.pb.txt | Scenario Pipeline :park_and_go scenario的PiPeline参数配置 | |||
scenario_conf.pb.txt | Scenario :park_and_go scenario的配置参数 | ||||
park_and_go_adjustStage目录:park_and_go just stage的task配置参数 | open_space_roi_decider.pb.txt | Task:open_space_roi_decider task的配置参数 | |||
open_space_trajectory_provider.pb.txt | Task:open_space_trajectory_provider的配置参数 | ||||
park_and_go_checkStage目录:park_and_go_check stage的task配置参数 | open_space_roi_decider.pb.txt | Task:open_space_roi_decider task的配置参数 | |||
lane_follow | proto | Scenario :lane_follow的配置参数定义 | |||
conf | Scenario :lane_follow的配置参数 | ||||
bare_intersection_unprotected | proto | Scenario :bare_intersection_unprotected的配置参数定义 | |||
conf | Scenario :bare_intersection_unprotected的配置参数 | ||||
… |
- 局部配置参数tasks:
局部配置参数 | 场景 | 文件目录结构 | 文件 | 说明 |
tasks | lane_borrow_pathtask默认配置 | proto | lane_borrow_path.proto | lane_borrow_path task的配置参数定义 |
conf | default_conf.pb.txt | lane_borrow_path task的配置参数 | ||
open_space_roi_decidertask默认配置 | proto | open_space_roi_decider.proto | open_space_roi_decider的配置参数定义 | |
conf | default_conf.pb.txt | open_space_roi_decider的配置参数 | ||
path_decidertask默认配置 | proto | path_decider.proto | path_decider的配置参数定义 | |
conf | default_conf.pb.txt | path_decider的配置参数 | ||
… |
- 局部配置参数traffic_rules:
局部配置参数 | 场景 | 文件目录结构 | 文件 | 说明 |
traffic_rules | baskside_vehicletask默认配置 | proto | backside_vihicle.proto | backside_vihicle的配置参数定义 |
conf | default_conf.pb.txt | backside_vihicle的配置参数 | ||
crosswalktraffic_rules默认配置 | proto | crosswalk.proto | crosswalk的配置参数定义 | |
conf | default_conf.pb.txt | crosswalk的配置参数 | ||
keepcleartraffic_rules默认配置 | proto | backside_vihicle.proto | keepclear的配置参数定义 | |
conf | keepclearpb.txt | keepclear的配置参数 | ||
… |
总结来说,9.0版本对参数配置进行了以下升级:
- 添加对常用功能使用到的参数的说明文档,方便用户调试时查询。
- 参数的目录跟随作用范围和功能,这样对参数的定位更清晰和直观。
- 用户新增的插件所使用的参数,可以跟随插件进行发布和管理。
参数配置相关的云实验:Apollo规划之减速带配置仿真实践
全新插件扩展机制
插件是9.0版本中的支持用户灵活扩展新功能的一种方式,用户新扩展的插件符合父类程序接口规范,通过重新实现接口来增加新的功能。对planning模块进行扩展是通过插件的形式,通常有三种类型的插件:
- scenario:定义一种新的场景,在这个场景下使用特定的交通规则或行驶流程,例如新增左转待转场景,用于处理在有红绿灯路口的左转待转区的驾驶行为
- task:在scenario的处理过程,添加一些额外的处理,例如在ValetParkingScenario中,添加一个ReplanTask,用于在遇到障碍物时重新规划路径
- traffic rule:在运行scenario流程之前的处理,如添加一个在学校附近行驶时限制巡航速度的traffic rule
用户可以根据实际业务的需要,自定义添加自己的scenario,task或traffic rule。增加的插件在一个独立的包中,用户只需要实现插件的接口,以及配置是否要加载这个插件即可。例如用户新增左转待转场景插件,增加一个left_turn_waiting_zone包,在这个包中添加左转待转场景的实现代码,以及相关的工程文件,编译调试后发布即可。
需要运行这个场景时,在planning的配置文件中,添加这个场景的pipeline:
旧版本中没有使用插件的方式扩展,用户想新增一个scenario,流程比较繁琐,除了创建场景本身的源代码之外,还需要额外做以下改动:
- 修改原有的ScenaioType和StageType,新增场景类型
- 修改原有的ScenaioConfig,新增场景配置定义
- 在原有的场景配置文件中,新增新的场景配置参数
- 修改ScenarioManager类:
- 修改RegisterScenarios函数
- 修改CreateScenario函数
- 修改Observe函数
- 修改ScenarioDispatchNonLearning函数
- 修改SelectTrafficLightScenario函数
- 修改UpdatePlanningContextTrafficLightScenario函数
- 增加scenario_manager.cc对新场景的BUILD依赖
- 修改planning模块对新场景的BUILD依赖
由此可见,旧版本中的改动比较分散,修改的原则不容易记忆,新增场景的门槛比较高。使用9.0版本中插件的方式扩展场景时,只需要做以下修改即可:
- 使用工具模板创建scenario,实现新scenario的相关接口和业务代码
- 在配置文件planning_config.pb.txt文件的pipeline中,增加新的场景
升级后新增场景代码集中,容易掌握。此外用插件开发的模式还有以下好处:
- 用户根据自己的场景,使用包管理的方式,选择性下载安装自己需要的scenario,task,traffic rules即可。
- 用户新增的插件独立开发,编译,发布和运行。
- 用户新增了插件后,可以直接跟随apollo一起升级。
扫码关注【Apollo开发者社区】第一时间获取Apollo动态!
扫码关注【百度技术沙龙】获取更多百度技术动态!