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
  • 添加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.ccRegisterScenario()中使用)
  • 添加planning_status.proto中的相关信息
    • 添加scenario相关的status信息,用于上下文、场景切换(见UpdatePlanningContext***(scenario name)Scenario()
  • 将新增的相关.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(),用于注册如上所述的两个stage

      void 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_STAGEScenarioConfig::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.protoplanning_config.protoSupervisedBypassPathGeneratorConfig对象由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.ccRegisterScenario()中使用)

    • 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()属于含有三个类型名的重构函数。

    1. 调用Init()初始化组件,component_base.h中的Init()为纯虚函数,这里调用的是PlanningComponent::Init()

    2. 创建特定component必要的reader,例如PlanningComponent对应的Component类的initialize()有三个类型名,则通过CreateReader()创建了reader0-reader2。在PlanningComponent的头文件中可以看到,必要的reader所对应的类为prediction::PredictionObstaclescanbus::Chassislocalization::LocalizationEstimate

      class PlanningComponent final: public cyber::Component<prediction::PredictionObstacles, canbus::Chassis,localization::LocalizationEstimate> {

      同时判断了cyber是不是实时模式,如果不是数据直接进入缓存==(不确定)==

    3. 构造匿名函数func, 内部调用 Process 函数,并把该函数加入任务调度里面, 通过消息进行触发。

    4. 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()生成轨迹
  • 将轨迹存入framecurrent_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.txt

    scenario_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 &&current_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相关推荐

  1. Ubuntu下添加新分区并设置挂载点

    Ubuntu下添加新分区并设置挂载点 最近在做Android项目,可是解压根文件系统以后,就报警说硬盘不够.当初设置使用的大小为15G.不过扩展分区还是很方便的.当然首先你得设置添加使用的硬盘大小,这 ...

  2. Android学习笔记之Android Studio添加新的Activity

    1.创建Android项目工程:AndroidTest 创建过程可参考网上诸多教程. 2.添加新的Activity,步骤如下 a. 在layout文件夹上右键,New-Activity-相应Activ ...

  3. linux系统下添加新硬盘的方法详解

    对于linux新手来说,在linux上添加新硬盘,是很有挑战性的一项工作. 在Linux服务器上把硬盘接好,启动linux,以root登陆. fdisk -l ## 这里是查看目前系统上有几块硬盘 D ...

  4. 深度学习目标检测指南:如何过滤不感兴趣的分类及添加新分类?

    编译 | 庞佳 责编 | Leo 出品 | AI 科技大本营(公众号ID:rgznai100) AI 科技大本营按:本文编译自 Adrian Rosebrock 发表在 PyImageSearch 上 ...

  5. 【MySQL】面试官:如何添加新数据库到MySQL主从复制环境?

    今天,一名读者反馈说:自己出去面试,被面试官一顿虐啊!为什么呢?因为这名读者面试的是某大厂的研发工程师,偏技术型的.所以,在面试过程中,面试官比较偏向于问技术型的问题.不过,技术终归还是要服务于业务的 ...

  6. Linux下对文件的操作及添加新用户

    Linux下对文件的操作及添加新用户 一.对文件的操作 1.打包压缩文件 2.解压缩文件 3.对文件操作的其他命令 二.创建新用户 一.对文件的操作 1.打包压缩文件 2.解压缩文件 3.对文件操作的 ...

  7. R语言为dataframe添加新的数据列(add new columns):使用R原生方法、data.table、dplyr等方案

    R语言为dataframe添加新的数据列(add new columns):使用R原生方法.data.table.dplyr等方案 目录 R语言为dataframe

  8. R语言为dataframe添加新的数据列(横向拼接、Appending columns,Unioning columns):使用R原生方法、data.table、dplyr等方案

    R语言为dataframe添加新的数据列(横向拼接.Appending columns,Unioning columns):使用R原生方法.data.table.dplyr等方案 目录 R语言为dat

  9. pandas在dataframe指定位置添加新的数据列、使用insert函数

    pandas在dataframe指定位置添加新的数据列.使用insert函数 目录 pandas在dataframe指定位置添加新的数据列.使用insert函数 #仿真数据

最新文章

  1. 损坏防浪涌电插排内部电路
  2. linux ps mysql_linux系统中ps指令使用详解
  3. 面试过程中千万不要犯这 5 点低级错误
  4. Dev Express Report 学习总结(五)在分组中使用聚集表达式AggregateExpression
  5. python编程入门 适合于零基础朋友-零基础能学好python吗?教女朋友学python是送命题吗?...
  6. mooc中习题--简单运算器
  7. 拓端tecdat|R语言法国足球联赛球员多重对应分析(MCA)
  8. 缺页异常(Page Faults) 和 Kernel Oops打印调用流程
  9. 省会、自治区、直辖市、特别行政区
  10. win10任务栏无反应假死解决办法
  11. 启用或禁用笔记本自带键盘
  12. dlna投屏显示服务器没互动,Dlna投屏
  13. ISCC -MISC-Retrieve_the_passcode
  14. 数据分析模型篇—安索夫矩阵
  15. 人人开源搭建后台管理系统 逆向工程生成CRUD代码
  16. coreldraw x4怎么会蓝屏_cdr点另存为没反应 步骤流程了解了么
  17. 将现有android项目打包成aar包供第三方应用调用
  18. python正则表达式编译_用Python编译正则表达式
  19. 边缘检测:更丰富的卷积特征 Richer Convolutional Features for Edge Detection
  20. python F5创建pool和创建member(一)

热门文章

  1. 17joys项目配置
  2. 杰理AC695X系列---us定时器(12)
  3. AS中码云和GitHub的使用入门
  4. 中文信息处理——纵览与建议
  5. CSS样式表中的颜色表
  6. 医疗机械公司网站网页
  7. 计算机中遇到的问题英语,电脑故障英语对话
  8. 深度产教融合的“山东经验”
  9. 对flash cs5的展望
  10. python能写app吗_Python可以开发APP吗?