Apollo 10.0
自动驾驶开放平台
|
planning
是planning模块的主要流程和入口package,包含planning模块的整体架构和流程。planning模块根据上游模块输入的感知周围环境信息,地图定位导航信息,以及全局路径信息,为自动驾驶车辆规划出一条运动轨迹(包含坐标,速度,加速度,jerk加加速度,时间等信息),然后将这些信息传递给控制模块。
planning模块由以下几个目录构成:
planning模块从apollo 3.5开始使用了双层状态机的场景机制,相比于apollo 3.0之前的版本,每个驾驶用例都被当作不同的驾驶场景,现在在特定场景中报告的问题可以在不影响其他场景的工作的情况下得到修复,其中问题修复不会影响其他驾驶用例。
双层状态机的Top Layer是Scenario状态机,BottomLayer是Stage状态机。在Top Layer中进行场景切换,ScenarioManager根据周围的环境和地图信息,决定需要切换到哪个场景,如LaneFollow沿道路行驶的场景,PullOver靠边停车的场景。在Bottom Layer中进行Stage的切换,如果上个Stage已经执行完成,继续执行下一个Stage。如果所有的Stage都执行完毕,认为当前Scenario也执行完毕。
目前planning支持以下几种场景:
enable_pull_over_at_destination
设置为 true
, 当车辆到达终点附近时,将自动切入 PullOverScenario
并完成靠边停车。planning模块运行流程如下图所示,模块的入口是PlanningComponent,当有预测信息PredictionObstacles输入时,触发它的Proc函数,进行轨迹规划处理。
planning 支持两种规划模式:OnLanePlanning 和 NaviPlanning,前者是基于高精地图的轨迹规划,也是默认的规划模式;后者是相对地图导航规划,主要用于交通规则较简单的高速公路。
每种规划模式可以通过 PlannerDispatcher 选择使用的 Planner ,目前 planning 模块中共有 4 种 Planner:
planning模块中有两个主要的线程,一个是根据输入环境和车辆信息,进行轨迹规划的主流程;另外一个是根据地图和输入的全局路线,生成参考线信息,这个线程是周期运行的线程,主流程规划都是在它生成的参考线基础上进行的。
在planning主流程中,默认使用OnLanePlanning->PublicRoadPlanner进行轨迹规划,在PublicRoadPlanner中,根据周围环境信息,切换到不同的场景中,规划生成轨迹并发送给control模块。
PlanningComponent是planning模块的入口,它是一个由topic触发的Component,接口函数是:
当接收到新的 PredictionObstacles 数据时,会触发执行Proc函数,并获取最新的Chassis车辆信息和 LocalizationEstimate 车辆定位数据进行处理,计算planning轨迹。
planning初始化在PlanningComponent::Init函数中进行,在这里创建PlanningBase对象(默认OnLanePlanning),它是轨迹规划的主体;除此之外,还需要创建planning其他输入消息的订阅对象,以及输出的消息发布对象:
成员对象 | 类型 | 描述 |
---|---|---|
planning_command_reader_ | std::shared_ptr< apollo::cyber::Reader < apollo::planning::PlanningCommand >> | 输入导航命令订阅 |
traffic_light_reader_ | std::shared_ptr< apollo::cyber::Reader < apollo::perception::TrafficLightDetection >> | 交通灯消息订阅 |
pad_msg_reader_ | std::shared_ptr< apollo::cyber::Reader < apollo::planning::PadMessage >> | planning操作命令(start,stop)消息订阅 |
story_telling_reader_ | std::shared_ptr< apollo::cyber::Reader < apollo::storytelling::Stories >> | storytelling消息订阅 |
relative_map_reader_ | std::shared_ptr< apollo::cyber::Reader < apollo::relative_map::MapMsg >> | 实时相对地图消息订阅(用于NaviPlanning) |
planning_writer_ | std::shared_ptr< apollo::cyber::Writer < apollo::planning::ADCTrajectory >> | planning输出轨迹消息发布 |
rerouting_client_ | std::shared_ptr< apollo::cyber::Client < apollo::external_command::LaneFollowCommand , apollo::external_command::CommandStatus >> | planning阻塞时需要重新路由的请求 |
command_status_writer_ | std::shared_ptr< apollo::cyber::Writer < apollo::external_command::CommandStatus >> | planning实时任务状态消息发布 |
planning中目前主要使用 OnLanePlanning->PublicRoadPlanner
,后面的介绍也以PublicRoadPlanner 为主。在PublicRoadPlanner中使用了双层状态机的场景机制,用户希望具体运行时都支持哪些场景的处理,可以在配置文件 modules/planning/planning_component/conf/planning_config.pb.txt
中的 standard_planning_config
字段指定,例如以下配置支持 LaneFollowScenario
和 ValetParkingScenario
两种场景:
配置列表中场景先后按照优先级从高到低排序,如果判断可以切入前面的场景,则后面的场景不再进行检查。
在主流程的线程中每次调用 apollo::planning::PlanningComponent::Proc
函数都要根据当前上下文环境,重新判断需要切入哪种场景。
场景更新切换在 apollo::planning::ScenarioManager::Update
中进行,它对场景列表中的所有场景遍历,调用场景子类的重写函数 IsTransferable
,用于判断当前环境是否能切入到这个场景子类中。因为场景列表优先级从高到低,如果遍历时遇到第一个可以切入的场景,后面的场景不需要再判断,直接使用这个场景作为本次运行周期的当前场景。
场景的运行也是在 apollo::planning::PlanningComponent::Proc
函数中调用,因为每个场景包含多个 Stage
,每个 Stage
又包含多个 Task
,所以执行一个场景,就是顺序执行不同阶段的不同任务。
traffic rules是planning在运行场景之前,根据不同的交通规则,决策车辆是否需要停车,减速或其他操作。因为它是在运行场景之前进行的,所以对所有的场景都会起作用。
目前支持的traffic rules有:
参考线是planning规划算法的基础,ReferenceLineProvider根据车辆的实时位置,计算车辆前后一定范围内(几百米)的参考线信息,相对于全局路由线路来说,参考线是局部路线信息,但参考线中还附加了车辆周围的动态信息,如障碍物,交通灯等。
参考线相关重要的两个数据:
ReferenceLine
的基础上添加了动态信息,如决策信息,ST图等,planning的规划操作基本都在这个数据结 构上进行。可以建立理解为ReferenceLine提供的是轨迹信息,而ReferenceLineInfo在ReferenceLine的基础上新添加了决策信息。参考线生成的流程如下图所示:
其中 CreateRouteSegments
函数是将车辆附近范围内的全局路由线路转换成参考线的格式;SmoothRouteSegment函数是将原始的参考线进行平滑。
参考线一共有三种平滑方式,离散点的平滑(默认)、螺旋线的平滑以及样条曲线的平滑。
planning模块框架如下图所示,planning-base包含了主流程,以及相关的外部接口类。planning模块可以让用户根据自己的场景灵活扩展或改造所需要的功能,主要通过以下几种方式:
扩展插件: 含scenario,task或traffic rule
planning的二次开发扩展都是以开发插件的形式给出的,在开发planning插件之前需要先了 解[插件的相关知识和概念](todo:插件的readme)。
开发 scenario 插件
Scenario 可以根据地理位置来划分,当场景中主车在特定区域规划动作和默认场景(Lane Follow)存在有所不同是,为了避免影响默认场景的运行,可以为其开发一个新的场景,比如前方有红绿灯需要开发红绿灯场景,前方停止标志需要开发停止标志场景;
Scenario 也可根据命令来划分,当接收到紧急靠边停车命令时,进入紧急靠边停车场景。比如接收到泊车命令时,进入泊车场景。开发一个新的Scenario需要继承 apollo::planning::Scenario
基类,并实现以下几个函数:
apollo::planning::Scenario::Init
。apollo::planning::ScenarioManager
中调用,判断是否需要切入该场景: apollo::planning::Scenario::IsTransferable
。apollo::planning::Scenario::Enter
apollo::planning::Scenario::Exit
开发task插件
当Apollo中的任务(Task)无法满足场景需求时,需要开发全新的任务插件。Apollo中存在多种类型的 apollo::planning::Task
基类:
apollo::planning::OpenSpaceTrajectoryProvider
。如果上述任务不能满足场景要求,可以继承Task基类实现这个需求,开发新的Task子类需要实现以下几个函数:
apollo::planning::Stage
在首次运行任务前,会调用任务的 Init
函数对任务进行初始化,初始化函数中主要对任务的成员变量进行初始化,以及加载配置参数: apollo::planning::Task::Init
apollo::planning::Task::Execute
开发traffic rule插件
交通规则插件 traffic rule 主要是在规划模块执行 Scenario 前对交通规则进行处理,当需要增加新的对于全场景生效的决策逻辑时,可以开发新的交通规则插件。
traffic rule 插件继承自 traffic rule 基类,而后由planning_base中的 traffic_decider 对各个插件进行生成并调用。planning 每进行一次规划任务,会通过 traffic_decider 调用各个 traffic rule , 从而使 traffic rule 插件生效。
开发新的 traffic rule 插件子类继承自 apollo::planning::TrafficRule ,并实现以下函数:
apollo::planning::TrafficRule::Init
apollo::planning::TrafficRule::ApplyRule
apollo::planning::PlanningComponent
Planning模块需要获取外部环境信息,车辆自身信息进行轨迹规划,以下是planning的外部输入信息:
Channel 名 | 类型 | 描述 |
---|---|---|
/apollo/prediction | apollo::prediction::PredictionObstacles | 障碍物预测信息,可通过 modules/planning/planning_component/dag/planning.dag 启动文件修改channel名 |
/apollo/perception/traffic_light | apollo::perception::TrafficLight | perception模块输出的交通灯感知信息,包含交通灯亮起的颜色,id等信息 |
/apollo/localization/pose | apollo::localization::LocalizationEstimate | 定位信息,可通过 modules/planning/planning_component/dag/planning.dag 配置文件修改channel名 |
/apollo/canbus/chassis | apollo::canbus::Chassis | canbus模块输出的车辆底盘信息,包含底盘速度,油门,刹车,档位,灯光等状态, modules/planning/planning_component/dag/planning.dag 配置文件修改channel名 |
此外,planning模块还需要外部输入的导航命令信息,用户首先向external_command发送导航命令请求,external_command再将这些命令进行处理后转发给planning模块。下面介绍用户可以发送的几种导航命令:
Channel 名 | 类型 | 描述 |
---|---|---|
/apollo/external_command/lane_follow | apollo::external_command::LaneFollowCommand | 基于高精地图导航的命令,给定终点的位置或朝向,从当前车辆位置导航到目标终点位置 |
/apollo/external_command/valet_parking | apollo::external_command::ValetParkingCommand | 从当前位置导航泊车到停车位上 |
/apollo/external_command/action | apollo::external_command::ActionCommand | HMI发送的流程操作命令 |
Channel 名 | 类型 | 描述 |
---|---|---|
/apollo/planning | apollo::planning::ADCTrajectory | 输出规划轨迹,包含轨迹点,速度和时间等信息 |
/apollo/planning/command_status | apollo::external_command::CommandStatus | 导航命令的执行状态 |
/apollo/external_command/lane_follow | apollo::external_command::LaneFollowCommand | 在道路被阻塞,换道失败超时时,发送重新路由的申请 |
文件路径 | 说明 |
---|---|
modules/planning/planning_component/conf/planning.conf | planning模块的flag配置文件 |
如果您在使用文档的过程中,遇到任何问题,请到我们在【开发者社区】建立的 反馈意见收集问答页面,反馈相关的问题。我们会根据反馈意见对文档进行迭代优化。