【规划】如何添加新的scenario
1. 添加场景的基本步骤
1.1 步骤
- 添加scenario
- 新增相关scenario的文件夹及相关文件:
BUILD
,***(scenario name)_scenario.h/.cc
,***(scenario name)_scenario_test.cc
,***(stage name)_stage.h/.cc
- 在
scenario_manager.h/.cc
中添加相关注册信息以及相关函数:CreateScenario()
,RegisterScenarios()
,Select***(scenario name)Scenario()
,ScenarioDispatch()
,Is***(scenario name)Scenario()
,UpdatePlanningContext***(scenario name)Scenario()
,同时修改BUILD
文件 - 在相关proto中添加scenario/stage的信息:
planning_config.proto
- 新增相关scenario的文件夹及相关文件:
- 添加task
- 新增相关task的文件夹及相关文件:
BUILD
,***(task name).h/.cc
,test.cc
- 在
task_factory.cc
中添加相关注册信息以及相关函数:Init()
,同时修改BUILD
文件 - 在相关proto中添加task的信息:
BUILD
,planning_config.proto
,***(task name)_config.proto
- 添加相关配置信息:
planning_config_navi.pb.txt
(保存了planning所包括的planner配置信息以及planner所包含的task的配置信息,以及其他task的默认配置,格式由planning_config.proto
决定,由planning_navi.conf
决定使用该配置文件),***(scenario name)_conf.pb.txt
(scenario所包括的stage及stage所包括的task的配置信息均在此处,格式由planning_config.proto
决定) - 在
planning_gflags.h/.cc
中添加配置文件***(scenario name)_conf.pb.txt
路径的宏(在scenario_manager.cc
的RegisterScenario()
中使用)
- 新增相关task的文件夹及相关文件:
- 添加
planning_status.proto
中的相关信息- 添加scenario相关的status信息,用于上下文、场景切换(见
UpdatePlanningContext***(scenario name)Scenario()
)
- 添加scenario相关的status信息,用于上下文、场景切换(见
- 将新增的相关.h/.cc文件中的基本信息写入,并进行编译以检查proto设置是否成功
- 进行功能开发,并根据需求动态修改proto及相关配置文件
1.2 详解
1.2.1 添加scenario
1.2.1.1 新增相关scenario的文件夹及相关文件
假设目前要创建一个名为supervised_bypass的scenario,其中包含forward_stage、supervised_bypass_stage两个stage,则需要创建:
一个名为supervised_bypass的文件夹
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vvMrU2in-1629511083672)(HowToAddScenario.assets/Screenshot from 2021-04-12 15-33-24.png)]BUILD文件,中间包含各文件之间的依赖信息(对照.h/.cc文件的依赖)
load("//tools:cpplint.bzl", "cpplint")package(default_visibility = ["//visibility:public"])cc_library(name = "supervised_bypass",copts = ["-DMODULE_NAME=\\\"planning\\\""],deps = [":supervised_bypass_scenario",":supervised_bypass_stage",":forward_stage",], )cc_library(name = "supervised_bypass_stage",srcs = ["supervised_bypass_stage.cc"],hdrs = ["supervised_bypass_stage.h"],copts = ["-DMODULE_NAME=\\\"planning\\\""],deps = ["//cyber/common:log","//external:gflags","//modules/common/proto:pnc_point_proto","//modules/common/status","//modules/common/time","//modules/common/util","//modules/common/util:factory","//modules/common/vehicle_state:vehicle_state_provider","//modules/map/hdmap","//modules/planning/common:planning_common","//modules/planning/common:speed_profile_generator","//modules/planning/constraint_checker","//modules/planning/math/curve1d:quartic_polynomial_curve1d","//modules/planning/proto:planning_proto","//modules/planning/reference_line","//modules/planning/reference_line:qp_spline_reference_line_smoother","//modules/planning/scenarios:scenario","//modules/planning/tasks/generators/supervised_bypass_path_generator:supervised_bypass_path_generator","//modules/planning/tasks/generators/st_speed_generator:st_speed_generator","@eigen","//modules/planning/common:planning_context",], )cc_library(name = "forward_stage",srcs = ["forward_stage.cc"],hdrs = ["forward_stage.h"],copts = ["-DMODULE_NAME=\\\"planning\\\""],deps = ["//cyber/common:log","//external:gflags","//modules/common/proto:pnc_point_proto","//modules/common/status","//modules/common/time","//modules/common/util","//modules/common/util:factory","//modules/common/vehicle_state:vehicle_state_provider","//modules/map/hdmap","//modules/planning/common:planning_common","//modules/planning/common:speed_profile_generator","//modules/planning/constraint_checker","//modules/planning/math/curve1d:quartic_polynomial_curve1d","//modules/planning/proto:planning_proto","//modules/planning/reference_line","//modules/planning/reference_line:qp_spline_reference_line_smoother","//modules/planning/scenarios:scenario","//modules/planning/tasks/generators/supervised_bypass_path_generator:supervised_bypass_path_generator","//modules/planning/tasks/generators/st_speed_generator:st_speed_generator","@eigen","//modules/planning/common:planning_context",], )cc_library(name = "supervised_bypass_scenario",srcs = ["supervised_bypass_scenario.cc"],hdrs = ["supervised_bypass_scenario.h"],copts = ["-DMODULE_NAME=\\\"planning\\\""],deps = [":supervised_bypass_stage",":forward_stage","//cyber/common:log","//external:gflags","//modules/common/proto:pnc_point_proto","//modules/common/status","//modules/common/time","//modules/common/util","//modules/common/util:factory","//modules/common/vehicle_state:vehicle_state_provider","//modules/map/hdmap","//modules/planning/common:planning_common","//modules/planning/common:speed_profile_generator","//modules/planning/constraint_checker","//modules/planning/math/curve1d:quartic_polynomial_curve1d","//modules/planning/proto:planning_proto","//modules/planning/reference_line","//modules/planning/reference_line:qp_spline_reference_line_smoother","//modules/planning/scenarios:scenario","//modules/planning/tasks/generators/supervised_bypass_path_generator:supervised_bypass_path_generator","//modules/planning/tasks/generators/st_speed_generator:st_speed_generator","@eigen",], )cc_test(name = "supervised_bypass_scenario_test",size = "small",srcs = ["supervised_bypass_scenario_test.cc"],data = ["//modules/planning:planning_conf",],deps = [":supervised_bypass","@gtest//:main",], )cpplint()
supervised_bypass_scenario.h/.cc以及对应的test文件,其中应包含的重要函数:
RegisterStages()
,用于注册如上所述的两个stagevoid SupervisedBypassScenario::RegisterStages() {if (!s_stage_factory_.Empty()) {s_stage_factory_.Clear();}s_stage_factory_.Register(ScenarioConfig::FORWARD_STAGE,[](const ScenarioConfig::StageConfig& config) -> Stage* {return new ForwardStage(config);});s_stage_factory_.Register(ScenarioConfig::SUPERVISED_BYPASS_STAGE,[](const ScenarioConfig::StageConfig& config) -> Stage* {return new SupervisedBypassStage(config);}); }
apollo::common::util::Factory<...>::Register()
中的第一个参数需在planning_config.proto中添加
forward_stage.h/.cc、supervised_bypass.h/.cc,其中应包含的重要函数:
Process()
,内部调用FinishStage()
判断stage结束和切换,内部调用PlanOnReferenceLine()
执行task(Process()
在planner层Plan()
函数调用的Scenario::Process()
中调用)Stage::StageStatus ForwardStage::Process(const TrajectoryPoint& planning_start_point, Frame* frame) {bool has_drivable_reference_line = false;...auto result = CheckForwardFinish(frame);if (result) {return FinishStage(frame);}const auto start_lane_id = PlanningContext::Instance()->planning_status().supervised_bypass().start_lane_id();for (auto& reference_line_info : *ref_line_info_group) {// start_lane is the only drivable reference_line_infoif (reference_line_info.Lanes().Id() == start_lane_id) {auto status = PlanOnReferenceLine(planning_start_point, frame,&reference_line_info);if (status.ok() && reference_line_info.IsDrivable()) {has_drivable_reference_line = true;} else {reference_line_info.SetDrivable(false);}} else {reference_line_info.SetDrivable(false);}}return has_drivable_reference_line ? StageStatus::RUNNING: StageStatus::ERROR; }
FinishStage()
执行stage的结束和切换,其中ScenarioConfig::FORWARD_STAGE
、ScenarioConfig::SUPERVISED_BYPASS_STAGE
需要在planning_config.proto中添加
Stage::StageStatus ForwardStage::FinishStage(Frame* frame) {
auto trigger = frame->local_view().hmi_msg->res_id();
auto* supervised_bypass_context = PlanningContext::Instance()
->mutable_planning_status()
->mutable_supervised_bypass();
std::vector lane_info_group;
std::vector left_lane_info_group;
std::vector right_lane_info_group;
GetValidBypassLanesInfo(frame, &lane_info_group, &left_lane_info_group,
&right_lane_info_group);
if (trigger == 1) {
next_stage_ = ScenarioConfig::SUPERVISED_BYPASS_STAGE;
supervised_bypass_context->set_end_lane_id(
left_lane_info_group.front().first);
}if (trigger == 2) {next_stage_ = ScenarioConfig::SUPERVISED_BYPASS_STAGE;supervised_bypass_context->set_end_lane_id(right_lane_info_group.front().first); }supervised_bypass_context->clear_timestamp(); supervised_bypass_context->set_in_bypass_stage(true); return Stage::FINISHED;
}
- **注意:**场景切换在stage层的Process()函数中完成,不要下放到task中。以`park_and_go`为例,stage切换在`Process()`中调用的`FinishStage()`中实现:```cpp Stage::StageStatus ParkAndGoStageCheck::FinishStage(const bool success) {if (success) {next_stage_ = ScenarioConfig::PARK_AND_GO_CRUISE;} else {next_stage_ = ScenarioConfig::PARK_AND_GO_ADJUST;}PlanningContext::Instance()->mutable_planning_status()->mutable_park_and_go()->set_in_check_stage(false);return Stage::FINISHED; }
1.2.1.2 scenario_manager.h/.cc
中添加相关注册信息以及相关函数
假设新添加的场景为supervised_bypass,在scenario_manager层需要进行注册scenario、切换scenario等工作,涉及的改动包含:
RegisterScenarios()
void ScenarioManager::RegisterScenarios() {...// supervised_bypassCHECK(Scenario::LoadConfig(FLAGS_scenario_supervised_bypass_config_file,&config_map_[ScenarioConfig::SUPERVISED_BYPASS])); }
CreateScenario()
std::unique_ptr<Scenario> ScenarioManager::CreateScenario(ScenarioConfig::ScenarioType scenario_type) {std::unique_ptr<Scenario> ptr;switch (scenario_type) {...case ScenarioConfig::SUPERVISED_BYPASS:ptr.reset(new scenario::supervised_bypass::SupervisedBypassScenario(config_map_[scenario_type], &scenario_context_));break;default:return nullptr;}if (ptr != nullptr) {ptr->Init();}return ptr; }
ScenarioDispatch()
,scenario切换的执行函数:void ScenarioManager::ScenarioDispatch(const common::TrajectoryPoint& ego_point,const Frame& frame) {CHECK(!frame.reference_line_info().empty());ScenarioConfig::ScenarioType scenario_type = default_scenario_type_;if (scenario_type == default_scenario_type_) {// check current_scenario (not switchable)switch (current_scenario_->scenario_type()) {...// scenario结束的判断case ScenarioConfig::SUPERVISED_BYPASS:if (current_scenario_->GetStatus() !=Scenario::ScenarioStatus::STATUS_DONE) {scenario_type = current_scenario_->scenario_type();}break;default:break;}}...// scenario切换的判断if (scenario_type == default_scenario_type_) {scenario_type = SelectSupervisedBypassScenario(frame);}ADEBUG << "select scenario: "<< ScenarioConfig::ScenarioType_Name(scenario_type);// update PlanningContext// 内部调用了UpdatePlanningContext***(scenario name)Scenario()UpdatePlanningContext(frame, scenario_type);if (current_scenario_->scenario_type() != scenario_type) {current_scenario_ = CreateScenario(scenario_type);} }
Select***(scenario name)Scenario()
,切换scenario的条件判断ScenarioConfig::ScenarioType ScenarioManager::SelectSupervisedBypassScenario(const Frame& frame) {// TODO: use hmi_msg->res_id() to simulate turn light information and// trigger supervised bypass for now// res_id: 1 for turn left, 2 for turn rightauto trigger = frame.local_view().hmi_msg->res_id();for (auto& reference_line_info : frame.reference_line_info()) {if (reference_line_info.reference_line().GetPriority() == 0) {if (reference_line_info.reference_line().IsOnLane(reference_line_info.AdcSlBoundary())) {if (trigger == 1 || trigger == 2) {return ScenarioConfig::SUPERVISED_BYPASS;}} else {AINFO << "vehicle is not on the main reference line now";return default_scenario_type_;}}}return default_scenario_type_; }
UpdatePlanningContext***(scenario name)Scenario()
void ScenarioManager::UpdatePlanningContextSupervisedBypassScenario(const Frame& frame, const ScenarioConfig::ScenarioType& scenario_type) {auto* supervised_bypass = PlanningContext::Instance()->mutable_planning_status()->mutable_supervised_bypass();// 如果不是该scenario则清除上下文,保证上下文安全if (!IsSupervisedBypassScenario(scenario_type)) {supervised_bypass->Clear();return;} }
Is***(scenario name)Scenario()
bool ScenarioManager::IsSupervisedBypassScenario(const ScenarioConfig::ScenarioType& scenario_type) {return (scenario_type == ScenarioConfig::SUPERVISED_BYPASS); }
同时修改
BUILD
文件,scenario_manager中有新scenario的依赖load("//tools:cpplint.bzl", "cpplint")package(default_visibility = ["//visibility:public"])cc_library(name = "scenario",srcs = ["scenario.cc"],hdrs = ["scenario.h"],copts = ["-DMODULE_NAME=\\\"planning\\\""],deps = [":stage","//modules/planning/common:planning_common","//modules/planning/common/util:util_lib","//modules/planning/tasks:task",], )cc_library(name = "stage",srcs = ["stage.cc"],hdrs = ["stage.h"],copts = ["-DMODULE_NAME=\\\"planning\\\""],deps = ["//modules/planning/common:planning_common","//modules/planning/common/util:util_lib","//modules/planning/tasks:task","//modules/planning/tasks:task_factory",], )cc_library(name = "scenario_manager",srcs = ["scenario_manager.cc"],hdrs = ["scenario_manager.h"],copts = ["-DMODULE_NAME=\\\"planning\\\""],deps = [...,"//modules/planning/scenarios/supervised_bypass",], )cpplint()
1.2.1.3 在相关proto中添加scenario/stage的信息
在上两节中提到:在scenario_manager.cc
中需要进行注册scenario、切换scenario等工作,scenario文件中需要注册所包含的对应stage,在stage文件中需要进行stage切换,这些功能的实现均需要在planning_config.proto
中添加相对应的信息。
具体的工作如下:
planning_config.proto
中增加相关scenario的信息:// 没有的话可以空着 message ScenarioSupervisedBypassConfig{ } ... message ScenarioConfig {enum ScenarioType {...// supervised_bypassSUPERVISED_BYPASS = 22;}...oneof scenario_config {...ScenarioSupervisedBypassConfig supervised_bypass_config = 24;}... }
planning_config.proto
中增加相关stage的信息:message ScenarioConfig {...enum StageType {NO_STAGE = 0;...// supervised_bypass scenarioSUPERVISED_BYPASS_STAGE = 1700;FORWARD_STAGE = 1701;};... }
1.2.2 添加task
1.2.2.1 新增相关task的文件夹及相关文件
假设添加的task为supervised_bypass_path_generator,则需在tasks文件夹下新增BUILD
, ***(task name).h/.cc
, test.cc
:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lvvD4G17-1629511083673)(HowToAddScenario.assets/Screenshot from 2021-04-12 19-34-45.png)]
1.2.2.2 在task_factory.cc
中添加相关注册信息以及相关函数
Init()
中进行了task的注册工作:void TaskFactory::Init(const PlanningConfig& config) {...task_factory_.Register(TaskConfig::SUPERVISED_BYPASS_PATH_GENERATOR,[](const TaskConfig& config) -> Task* {return dynamic_cast<Task*>(new SupervisedBypassPathGenerator(config));});for (const TaskConfig& default_task_config : config.default_task_config()) {default_task_configs_[default_task_config.task_type()] =default_task_config;} }
修改
BUILD
文件,由于task_factory.cc中依赖了task的头文件,因此需要在BUILD
文件中添加相应信息:task_factory.cc
:#include "modules/planning/tasks/generators/supervised_bypass_path_generator/supervised_bypass_path_generator.h"
BUILD
:cc_library(name = "task_factory",srcs = ["task_factory.cc"],hdrs = ["task_factory.h"],copts = ["-DMODULE_NAME=\\\"planning\\\"","-fopenmp",],deps = [...,"//modules/planning/tasks/generators/supervised_bypass_path_generator:supervised_bypass_path_generator",], )
1.2.2.3 在相关proto中添加task的信息
planning_config.proto
:message TaskConfig {enum TaskType {...SUPERVISED_BYPASS_PATH_GENERATOR = 45;};optional TaskType task_type = 1;oneof task_config {...SupervisedBypassPathGeneratorConfig supervised_bypass_path_generator_config = 32;} }
***(task name)_config.proto
,planning_config.proto
中SupervisedBypassPathGeneratorConfig
对象由supervised_bypass_path_generator_config.proto
决定:syntax = "proto2";package apollo.planning;message SupervisedBypassPathGeneratorConfig {optional double min_path_length = 1 [default = 5];... }
BUILD
,由于planning_config.proto
中依赖了supervised_bypass_path_generator_config.proto
,因此需要在BUILD
中增加相应信息:planning_config.proto
:import "modules/planning/proto/supervised_bypass_path_generator_config.proto";
BUILD
:... proto_library(name = "planning_config_proto_lib",srcs = ["planning_config.proto"],deps = [...,":supervised_bypass_path_generator_config_proto_lib",], ) ... proto_library(name = "supervised_bypass_path_generator_config_proto_lib",srcs = ["supervised_bypass_path_generator_config.proto"], )cc_proto_library(name = "supervised_bypass_path_generator_config_proto",deps = [":supervised_bypass_path_generator_config_proto_lib",], ) ...
1.2.2.4 添加相关配置信息
planning_config_navi.pb.txt
,其中保存了planning所包括的planner配置信息以及planner所包含的task的配置信息,以及其他task的默认配置,格式由planning_config.proto
决定,由planning_navi.conf
决定使用该配置文件navigation_planning_config {planner_type: PUBLIC_ROADplanner_type: NAVIplanner_public_road_config {}planner_navi_config {...} } ... default_task_config: {task_type: SUPERVISED_BYPASS_PATH_GENERATORsupervised_bypass_path_generator_config {min_path_length: 5...} }
***(scenario name)_conf.pb.txt
:scenario所包括的stage及stage所包括的task的配置信息均在此处,格式由planning_config.proto
决定scenario_type: SUPERVISED_BYPASSstage_type: SUPERVISED_BYPASS_STAGE stage_type: FORWARD_STAGEstage_config: {stage_type: SUPERVISED_BYPASS_STAGEenabled: truetask_type : SUPERVISED_BYPASS_PATH_GENERATORtask_type : ST_SPEED_GENERATORtask_config: {task_type: SUPERVISED_BYPASS_PATH_GENERATORsupervised_bypass_path_generator_config {min_path_length: 5...}}task_config: {task_type: ST_SPEED_GENERATORst_speed_generator_config {preferred_accel: 0.30...}} }stage_config: {stage_type: FORWARD_STAGEenabled: truetask_type : SUPERVISED_BYPASS_PATH_GENERATORtask_type : ST_SPEED_GENERATORtask_config: {task_type: SUPERVISED_BYPASS_PATH_GENERATORsupervised_bypass_path_generator_config {min_path_length: 5...}}task_config: {task_type: ST_SPEED_GENERATORst_speed_generator_config {preferred_accel: 0.30...}} }
1.2.2.5 gflags修改
在
planning_gflags.h/.cc
中添加配置文件***(scenario name)_conf.pb.txt
路径的宏(在scenario_manager.cc
的RegisterScenario()
中使用)planning_gflags.h
DECLARE_string(scenario_supervised_bypass_config_file);
planning_gflags.cc
DEFINE_string(scenario_supervised_bypass_config_file,"/apollo/modules/planning/conf/""scenario/supervised_bypass_config.pb.txt","supervised_bypass scenario config file");
1.2.3 添加上下文信息
添加planning_status.proto
中的相关信息,上下文用于在不同帧之间传递必要的信息。添加scenario相关的status信息,用于场景切换(见UpdatePlanningContext***(scenario name)Scenario()
)
message SupervisedBypassStatus {optional bool in_bypass_stage = 1 [default = false];optional string main_lane_id = 2;optional string start_lane_id = 3;optional string end_lane_id = 4;optional apollo.common.Point3D bypass_end_position = 5;// the time stamp when the state started.optional double timestamp = 6;optional bool has_bypass_path = 7 [default = false];
}
...
message PlanningStatus {...optional SupervisedBypassStatus supervised_bypass = 16;
}
1.2.4 编译检查
将新增的相关.h/.cc文件中的基本信息(虚函数)写入,并进行编译以检查proto设置是否成功。
1.2.5 功能开发
进行功能开发,并根据需求动态修改proto及相关配置文件。
2. planning模块结构简析
假设从planning.dag启动模块,并进入YIELD_SIGN场景为例
2.1 进入规划模块
2.1.1 dag
通过planning.dag进入规划模块:
- components—>class_name: "PlanningComponent"说明使用的是PlanningComponent库
- flag_file_path: "/apollo/modules/planning/conf/planning.conf"说明对应的配置文件为planning.conf
- readers说明订阅的消息包含"/apollo/prediction"、"/apollo/canbus/chassis"、"/apollo/localization/pose"(可能订阅其他消息,详见component)
module_config {module_library : "/apollo/bazel-bin/modules/planning/libplanning_component.so"components {class_name : "PlanningComponent"config {name: "planning"flag_file_path: "/apollo/modules/planning/conf/planning.conf"readers: [{channel: "/apollo/prediction"},{channel: "/apollo/canbus/chassis"qos_profile: {depth : 15}pending_queue_size: 50},{channel: "/apollo/localization/pose"qos_profile: {depth : 15}pending_queue_size: 50}]}}
}
2.1.2 planning.conf
配置文件planning.conf中包含了一些对应配置文件信息和标志位信息,特别是:
--flagfile=/apollo/modules/common/data/global_flagfile.txt
--traffic_rule_config_filename=/apollo/modules/planning/conf/traffic_rule_config.pb.txt
...
2.2 component
2.2.1 从mainboard进入component层
mainboard使用
module_args.ParseArgument()
解析planning.dag 文件内容mainboard通过
controller.Init()
,根据dag内容进行模块的初始化以及轮询调用模块内的 proc 函数。controller.Init()
—>
LoadAll()
—>
LoadModule(module_path)
,其中module_path
应该为dag文件路径—>
base->initialize()
,其中base是ComponentBase类,initialize()
为虚函数,根据dag文件中的class_name
判断初始化为PlanningComponent并使用其initialize()
。由于PlanningComponent类中没有initialize()
的实现,在其父类Component中找initialize()
。—>
Component类的
initialize()
。PlanningComponent对应的initialize()
属于含有三个类型名的重构函数。调用
Init()
初始化组件,component_base.h中的Init()
为纯虚函数,这里调用的是PlanningComponent::Init()
创建特定component必要的reader,例如PlanningComponent对应的Component类的
initialize()
有三个类型名,则通过CreateReader()
创建了reader0-reader2。在PlanningComponent的头文件中可以看到,必要的reader所对应的类为prediction::PredictionObstacles
、canbus::Chassis
、localization::LocalizationEstimate
class PlanningComponent final: public cyber::Component<prediction::PredictionObstacles, canbus::Chassis,localization::LocalizationEstimate> {
同时判断了cyber是不是实时模式,如果不是数据直接进入缓存==(不确定)==
构造匿名函数func, 内部调用 Process 函数,并把该函数加入任务调度里面, 通过消息进行触发。
Process
函数则调用Proc
函数,进入PlanningComponent::Proc(…)
处理流程
2.2.2 PlanningComponent::Init()、Proc()
Init()
包括planning_base_
的创建和初始化(TaskFactory的初始化)、额外消息的订阅、writer的创建,内部的FLAGS_planning_config_file
表示从planning_config.pb.txt读取配置信息(默认)Proc()
包括订阅消息的转存(转存入local_view_
),进入OnLanePlanning::RunOnce()
主逻辑生成规划轨迹,生成轨迹的时间戳处理、发布以及存入History::Instance()
- 使用
OnLanePlanning
是因为Init()
中将planning_base_
初始化为该类
2.3 planning
通过PlanningComponent::Proc()
调用OnLanePlanning::RunOnce()
进入planning层。
2.3.1 RunOnce()
- 将调用该函数的时间记为开始时间
start_timestamp
- 更新
VehicleStateProvider::Instance()
- 更新
EgoInfo::Instance()
- 初始化
frame
,内部包含参考线生成的功能 - 调用
OnLanePlanning::Plan()
生成轨迹 - 将轨迹存入
frame
的current_frame_planned_trajectory()
- 将
frame
存入FrameHistory::Instance()
2.3.2 Plan()
调用
Planner_
的Plan()
函数进入planner层,生成轨迹Planner_
的类型由OnLanePlanning::Init()
中的planner_dispatcher_->Init()
、planner_dispatcher_->DispatcherPlanner()
确定。其中Init()
进行Planner注册,DispatcherPlanner()
根据配置文件FLAGS_planning_config_file
创建相应类型planner对象。此处FLAGS_planning_config_file
为planning_config.pb.txt,对应的planner为PUBLIC_ROAD
standard_planning_config {planner_type: PUBLIC_ROADplanner_public_road_config {} }
注意:每个planning类型中的
planner_dispatcher_
在其头文件中指定,OnLanePlanning
中为planner_dispatcher_ = std::make_unique<OnLanePlannerDispatcher>();
将最优轨迹对应的
reference_line_info
存入best_info
并进行后处理,包括将stitching_trajectory
拼接到生成轨迹上、将生成轨迹存入last_publishable_trajectory_
等
2.4 planner
PublicRoadPlanner::Plan()
中进行了场景判断(ScenarioManager::Update()
)、执行(Scenario::Process
)和结束(ScenarioManager::Update()
)的功能
2.5 scenario manager
Update()
中调用ScenarioDispatch()
函数判断是否进行场景切换
其中,以下代码用于判断YEID_SIGN
是否结束:
if (scenario_type == default_scenario_type_) {// check current_scenario (not switchable)switch (current_scenario_->scenario_type()) {case ScenarioConfig::LANE_FOLLOW:case ScenarioConfig::PULL_OVER:break;case ScenarioConfig::BARE_INTERSECTION_UNPROTECTED:case ScenarioConfig::EMERGENCY_PULL_OVER:case ScenarioConfig::PARK_AND_GO:case ScenarioConfig::STOP_SIGN_PROTECTED:case ScenarioConfig::STOP_SIGN_UNPROTECTED:case ScenarioConfig::TRAFFIC_LIGHT_PROTECTED:case ScenarioConfig::TRAFFIC_LIGHT_UNPROTECTED_LEFT_TURN:case ScenarioConfig::TRAFFIC_LIGHT_UNPROTECTED_RIGHT_TURN:case ScenarioConfig::VALET_PARKING:case ScenarioConfig::YIELD_SIGN:// must continue until finishif (current_scenario_->GetStatus() !=Scenario::ScenarioStatus::STATUS_DONE) {scenario_type = current_scenario_->scenario_type();}break;default:break;}}
SelectInterceptionScenario()
中的SelectYieldSignScenario()
用于判断是否进入YEID_SIGN
场景。
UpdatePlanningContext()
用于更新场景所需的上下文信息。
2.6 scenario
Scenario::Process()
中:
执行
current_stage_
的process程序,进入stage层current_stage_
由Scenario::Init()
决定,其调用路径为ScenarioManager::Init()
—>ScenarioManager::CreateScenario()
—>Scenario::Init()
,首次进入场景时current_stage_
为配置文件中第一个stage,即YIELD_SIGN_APPROACH
配置文件见ScenarioManager::Init()
—>ScenarioManager::RegisterScenarios()
—>FLAGS_scenario_yield_sign_config_file
,即yield_sign_config.pb.txtscenario_type: YIELD_SIGN yield_sign_config: {start_yield_sign_scenario_distance: 10.0max_valid_stop_distance: 4.5min_pass_s_distance: 3.0creep_timeout_sec: 10.0 } stage_type: YIELD_SIGN_APPROACH stage_type: YIELD_SIGN_CREEPstage_config: {stage_type: YIELD_SIGN_APPROACHenabled: truetask_type: PATH_LANE_BORROW_DECIDERtask_type: PATH_BOUNDS_DECIDERtask_type: PIECEWISE_JERK_PATH_OPTIMIZERtask_type: PATH_ASSESSMENT_DECIDERtask_type: PATH_DECIDERtask_type: RULE_BASED_STOP_DECIDERtask_type: ST_BOUNDS_DECIDERtask_type: SPEED_BOUNDS_PRIORI_DECIDERtask_type: DP_ST_SPEED_OPTIMIZERtask_type: SPEED_DECIDERtask_type: SPEED_BOUNDS_FINAL_DECIDERtask_type: PIECEWISE_JERK_NONLINEAR_SPEED_OPTIMIZER
stage切换的判断
switch (ret) {case Stage::ERROR: {AERROR << "Stage '" << current_stage_->Name() << "' returns error";scenario_status_ = STATUS_UNKNOWN;break;}case Stage::RUNNING: {scenario_status_ = STATUS_PROCESSING;break;}case Stage::FINISHED: {auto next_stage = current_stage_->NextStage();if (next_stage != current_stage_->stage_type()) {AINFO << "switch stage from " << current_stage_->Name() << " to "<< ScenarioConfig::StageType_Name(next_stage);if (next_stage == ScenarioConfig::NO_STAGE) {scenario_status_ = STATUS_DONE;return scenario_status_;}if (stage_config_map_.find(next_stage) == stage_config_map_.end()) {AERROR << "Failed to find config for stage: " << next_stage;scenario_status_ = STATUS_UNKNOWN;return scenario_status_;}current_stage_ = CreateStage(*stage_config_map_[next_stage]);if (current_stage_ == nullptr) {AWARN << "Current stage is a null pointer.";return STATUS_UNKNOWN;}}if (current_stage_ != nullptr &¤t_stage_->stage_type() != ScenarioConfig::NO_STAGE) {scenario_status_ = STATUS_PROCESSING;} else {scenario_status_ = STATUS_DONE;}break;}default: {AWARN << "Unexpected Stage return value: " << ret;scenario_status_ = STATUS_UNKNOWN;}}
2.7 stage
由Scenario::Process()
进入stage的执行函数Process()
,其中执行了Stage::ExecuteTaskOnReferenceLine()
用于依次执行配置文件中各task的Excute()
函数并进入task层。
bool Stage::ExecuteTaskOnReferenceLine(const common::TrajectoryPoint& planning_start_point, Frame* frame) {for (auto& reference_line_info : *frame->mutable_reference_line_info()) {if (!reference_line_info.IsDrivable()) {AERROR << "The generated path is not drivable";return false;}auto ret = common::Status::OK();for (auto* task : task_list_) {ret = task->Execute(frame, &reference_line_info);if (!ret.ok()) {AERROR << "Failed to run tasks[" << task->Name()<< "], Error message: " << ret.error_message();break;}}...
task的执行顺序见yield_sign_config.pb.txt
stage_config: {stage_type: YIELD_SIGN_APPROACHenabled: truetask_type: PATH_LANE_BORROW_DECIDERtask_type: PATH_BOUNDS_DECIDERtask_type: PIECEWISE_JERK_PATH_OPTIMIZERtask_type: PATH_ASSESSMENT_DECIDERtask_type: PATH_DECIDERtask_type: RULE_BASED_STOP_DECIDERtask_type: ST_BOUNDS_DECIDERtask_type: SPEED_BOUNDS_PRIORI_DECIDERtask_type: DP_ST_SPEED_OPTIMIZERtask_type: SPEED_DECIDERtask_type: SPEED_BOUNDS_FINAL_DECIDERtask_type: PIECEWISE_JERK_NONLINEAR_SPEED_OPTIMIZER
2.8 task
task层作为planning的最底层,进行实际的功能实现(轨迹生成等等)。
在这个例子中,执行的第一个task为PATH_LANE_BORROW_DECIDER
,为Decider
类的子类。首先调用父类的Excute()
函数,并在内部调用PathLaneBorrowDecider::Process()
,处理完成后返回Status::OK()
并进入下一个task。
【规划】如何添加新的scenario相关推荐
- Ubuntu下添加新分区并设置挂载点
Ubuntu下添加新分区并设置挂载点 最近在做Android项目,可是解压根文件系统以后,就报警说硬盘不够.当初设置使用的大小为15G.不过扩展分区还是很方便的.当然首先你得设置添加使用的硬盘大小,这 ...
- Android学习笔记之Android Studio添加新的Activity
1.创建Android项目工程:AndroidTest 创建过程可参考网上诸多教程. 2.添加新的Activity,步骤如下 a. 在layout文件夹上右键,New-Activity-相应Activ ...
- linux系统下添加新硬盘的方法详解
对于linux新手来说,在linux上添加新硬盘,是很有挑战性的一项工作. 在Linux服务器上把硬盘接好,启动linux,以root登陆. fdisk -l ## 这里是查看目前系统上有几块硬盘 D ...
- 深度学习目标检测指南:如何过滤不感兴趣的分类及添加新分类?
编译 | 庞佳 责编 | Leo 出品 | AI 科技大本营(公众号ID:rgznai100) AI 科技大本营按:本文编译自 Adrian Rosebrock 发表在 PyImageSearch 上 ...
- 【MySQL】面试官:如何添加新数据库到MySQL主从复制环境?
今天,一名读者反馈说:自己出去面试,被面试官一顿虐啊!为什么呢?因为这名读者面试的是某大厂的研发工程师,偏技术型的.所以,在面试过程中,面试官比较偏向于问技术型的问题.不过,技术终归还是要服务于业务的 ...
- Linux下对文件的操作及添加新用户
Linux下对文件的操作及添加新用户 一.对文件的操作 1.打包压缩文件 2.解压缩文件 3.对文件操作的其他命令 二.创建新用户 一.对文件的操作 1.打包压缩文件 2.解压缩文件 3.对文件操作的 ...
- R语言为dataframe添加新的数据列(add new columns):使用R原生方法、data.table、dplyr等方案
R语言为dataframe添加新的数据列(add new columns):使用R原生方法.data.table.dplyr等方案 目录 R语言为dataframe
- R语言为dataframe添加新的数据列(横向拼接、Appending columns,Unioning columns):使用R原生方法、data.table、dplyr等方案
R语言为dataframe添加新的数据列(横向拼接.Appending columns,Unioning columns):使用R原生方法.data.table.dplyr等方案 目录 R语言为dat
- pandas在dataframe指定位置添加新的数据列、使用insert函数
pandas在dataframe指定位置添加新的数据列.使用insert函数 目录 pandas在dataframe指定位置添加新的数据列.使用insert函数 #仿真数据
最新文章
- 损坏防浪涌电插排内部电路
- linux ps mysql_linux系统中ps指令使用详解
- 面试过程中千万不要犯这 5 点低级错误
- Dev Express Report 学习总结(五)在分组中使用聚集表达式AggregateExpression
- python编程入门 适合于零基础朋友-零基础能学好python吗?教女朋友学python是送命题吗?...
- mooc中习题--简单运算器
- 拓端tecdat|R语言法国足球联赛球员多重对应分析(MCA)
- 缺页异常(Page Faults) 和 Kernel Oops打印调用流程
- 省会、自治区、直辖市、特别行政区
- win10任务栏无反应假死解决办法
- 启用或禁用笔记本自带键盘
- dlna投屏显示服务器没互动,Dlna投屏
- ISCC -MISC-Retrieve_the_passcode
- 数据分析模型篇—安索夫矩阵
- 人人开源搭建后台管理系统 逆向工程生成CRUD代码
- coreldraw x4怎么会蓝屏_cdr点另存为没反应 步骤流程了解了么
- 将现有android项目打包成aar包供第三方应用调用
- python正则表达式编译_用Python编译正则表达式
- 边缘检测:更丰富的卷积特征 Richer Convolutional Features for Edge Detection
- python F5创建pool和创建member(一)