【Apollo学习笔记】—— Planning模块

在这里插入图片描述

前言

本文记录学习planning模块时的一些笔记,总体流程参照https://zhuanlan.zhihu.com/p/61982682中的流程图,如上图所示。

planning_component

modules/planning/planning_component.cc
在这里插入图片描述

PlanningComponent::Init部分首先完成规划模式的选择:

  if (FLAGS_use_navigation_mode) {planning_base_ = std::make_unique<NaviPlanning>(injector_);} else {planning_base_ = std::make_unique<OnLanePlanning>(injector_);}

再完成相应的消息订阅

routing_reader_ = node_->CreateReader<RoutingResponse>(
traffic_light_reader_ = node_->CreateReader<TrafficLightDetection>(
pad_msg_reader_ = node_->CreateReader<PadMessage>(
story_telling_reader_ = node_->CreateReader<Stories>(
relative_map_reader_ = node_->CreateReader<MapMsg>(

最后消息发布

  planning_writer_ = node_->CreateWriter<ADCTrajectory>(rerouting_writer_ = node_->CreateWriter<RoutingRequest>(planning_learning_data_writer_ = node_->CreateWriter<PlanningLearningData>(

PlanningComponent::Proc的主要是检查数据,并且执行注册好的Planning,生成路线并且发布。

bool PlanningComponent::Proc(...) {// 1. 检查是否需要重新规划线路。CheckRerouting();// 2. 数据放入local_view_中,并且检查输入数据。...// 3. 执行注册好的Planning,生成线路。planning_base_->RunOnce(local_view_, &adc_trajectory_pb);// 4. 发布消息planning_writer_->Write(std::make_shared<ADCTrajectory>(adc_trajectory_pb));// 5. 最后记录history->Add(adc_trajectory_pb);
}

RunOnce在NaviPlanning和OnLanePlanning都有,本文以常用的OnLanePlanning为例。

PS1

  • prediction_obstacles 从channel中获得的指针,类型为 PredictionObstacles,代码文件在 modules/prediction/proto/prediction_obstacle.proto 中,该类型的消息来自预测模块,包含了以下信息:
    • Header 头结构:包含了这条消息发布的时刻(以秒为单位),目前位置的模块名,该消息的序列号(每个模块各自维护的)等,几乎每个消息都包含有这个头结构,下面就不提及了。
    • PredictionObstacle 预测模块中的若干个障碍物行为:有感知模块中的障碍物(PerceptionObstacle),它包括障碍物的 id,它的三维坐标、速度加速度、边界大小(长高宽)、特殊的形状、类型(行人、非机动车、机动车)、运动轨迹等;还有时间戳,记录 GPS 给出的时刻;还有预测的时间长度;多种可能的运动轨迹;障碍物的运动趋势、优先级等。
    • 开始的时间戳:记录预测开始的时刻
    • 结束时间戳:预测结束的时刻
    • 自动驾驶车辆的运动趋势,停止、正常行驶还是正在变道等
    • Scenery 现在的场景
  • chassis 从channel获得的指针,类型为 Chassis,这条消息直接来自总线 CanBus,代码文件在 modules/canbus/proto/chassis.proto 中,该类包含了很多信息,主要是汽车底盘所给出的机械状态信息,比如:
    • 驾驶模式,有手动驾驶、自动驾驶、仅控制方向、仅控制速度以及紧急模式
    • 档位情况、引擎转速、车辆速度、里程表、燃油情况、电池电量等
    • 刹车、油门踏板的力度,方向盘的旋转角度,车轮转速
    • 转向灯、雾灯、大灯的工作情况
  • localization_estimate 从channel中获得的指针,类型为 LocalizationEstimate,代码文件在 modules/localization/proto/localization.proto 中,这条消息来自定位模块,该类主要包含了:
    • Pose 位置姿势,包括车头朝向,在地图上的线速度、线加速度和相对位置,角速度、仰角度等
    • 测量上述姿势的时刻
    • 车辆已经经过的轨迹点列
    • MSF 定位状态与质量

PS2

std::mutex是C++11标准中提供的一种互斥锁机制用于保护共享资源的访问。当多个线程需要同时访问共享资源时,为了避免出现数据竞争等问题,需要使用互斥锁进行同步控制。

std::mutex是一种基本的互斥锁类型,可以通过lock()函数锁定互斥锁,通过unlock()函数释放互斥锁。当一个线程持有互斥锁时,其他线程想要获取该互斥锁将会被阻塞,直到持有该互斥锁的线程将锁释放。

例如,当多个线程需要同时访问同一个共享变量时,可以在访问之前使用std::mutex进行锁定,保证每个线程都可以安全地访问该变量。例如:

#include <mutex>
#include <thread>
#include <iostream>int shared_data = 0;
std::mutex mutex;void work(int id) {for (int i = 0; i < 10000; ++i) {mutex.lock();shared_data++;mutex.unlock();}std::cout << "Thread " << id << " finished." << std::endl;
}int main() {std::thread t1(work, 1);std::thread t2(work, 2);t1.join();t2.join();std::cout << "Shared data: " << shared_data << std::endl;return 0;
}

在上述例子中,两个线程都会对shared_data变量进行10000次的自增操作,为了避免出现数据竞争,我们在对shared_data进行访问之前都使用了std::mutex进行了锁定,保证了线程的安全性。

on_lane_planning

modules/planning/on_lane_planning.cc
在这里插入图片描述

OnLanePlanning::Init主要实现分配具体的Planner,启动参考线提供器(reference_line_provider_)。启动参考线提供器,会另启动一个线程,执行一个定时任务,每隔50ms提供一次参考线。注意:分配Planner的部分在OnLanePlanning实例化时就已经完成。

分配Planner的实现部分

modules/planning/planner/on_lane_planner_dispatcher.cc
std::unique_ptr<Planner> OnLanePlannerDispatcher::DispatchPlanner(const PlanningConfig& planning_config,const std::shared_ptr<DependencyInjector>& injector) {return planner_factory_.CreateObject(planning_config.standard_planning_config().planner_type(0), injector);
}

可以在modules/planning/conf/planning_config.pb.txt配置文件中看到配置Planner的类型:

standard_planning_config {planner_type: PUBLIC_ROADplanner_public_road_config {}
}

OnLanePlanning 的主逻辑在OnLanePlanning::RunOnce中,内容比较多,有些还没仔细看,大致完成了一下的内容:

void OnLanePlanning::RunOnce(const LocalView& local_view,ADCTrajectory* const ptr_trajectory_pb) {//  更新汽车状态和参考线的状态,如果状态无效,直接返回//  ...  // 是否为出现导航路线变换,如果是 初始化 planner//  加上预估的规划触发的周期 得到 stitchingTrajectory// planning is triggered by prediction data, but we can still use an estimated// cycle time for stitchingstatus = InitFrame(frame_num, stitching_trajectory.back(), vehicle_state);//  判断是否符合交通规则//  开始正在的规划 planner 开始规划status = Plan(start_timestamp, stitching_trajectory, ptr_trajectory_pb);//  记录规划所花费的时间//  ...
}

status = Plan(start_timestamp, stitching_trajectory, ptr_trajectory_pb);中有如下代码,此部分的Plan是指分配的planner的Plan()函数

  auto status = planner_->Plan(stitching_trajectory.back(), frame_.get(),ptr_trajectory_pb);

PS1

make_unique是一个C++11中的函数模板,用于创建并返回一个独占指针(unique_ptr)。它的作用是将一个裸指针封装成一个unique_ptr对象,并确保该对象是唯一持有它所指向的对象。这可以有效避免内存泄漏和多个指针指向同一个对象的问题

public_road_planner

modules/planning/planner/public_road/public_road_planner.cc
接着来看public_road_planner的实现。

在这里插入图片描述

PublicRoadPlanner::Init实例化一个全局的scenario_manager_对象来进行场景管理。

Status PublicRoadPlanner::Init(const PlanningConfig& config) {config_ = config;scenario_manager_.Init(config);return Status::OK();
}

PublicRoadPlanner::Plan主要完成以下工作

Status PublicRoadPlanner::Plan(const TrajectoryPoint& planning_start_point,Frame* frame,ADCTrajectory* ptr_computed_trajectory) {// 更新场景,决策当前应该执行什么场景scenario_manager_.Update(planning_start_point, *frame);// 获取当前场景scenario_ = scenario_manager_.mutable_scenario();// 调用Scenario的Process函数,对具体的场景进行处理auto result = scenario_->Process(planning_start_point, frame);if (FLAGS_enable_record_debug) {auto scenario_debug = ptr_computed_trajectory->mutable_debug()->mutable_planning_data()->mutable_scenario();scenario_debug->set_scenario_type(scenario_->scenario_type());scenario_debug->set_stage_type(scenario_->GetStage());scenario_debug->set_msg(scenario_->GetMsg());}// 当前场景完成if (result == scenario::Scenario::STATUS_DONE) {// only updates scenario manager when previous scenario's status is// STATUS_DONEscenario_manager_.Update(planning_start_point, *frame);} else if (result == scenario::Scenario::STATUS_UNKNOWN) {// 当前场景失败return Status(common::PLANNING_ERROR, "scenario returned unknown");}return Status::OK();
}

Scenario

Introduction

在这里插入图片描述

Apollo规划模块的框架以场景调度为基本决策框架,场景调度本质上是一个双层状态机,顶层是基于场景的管理器,底层是一系列小模块的任务组合,由此来构建出Apollo planning的整体框架。

在这里插入图片描述在执行的每一个场景中,又分为一个或多个阶段,称为stage;而每一个stage又包含一个或多个任务,称为task。执行一个场景,最终就是顺序执行不同阶段的不同任务。由此可见,Scenario在Apollo规划决策中占据着重要的地位。

Scenario 类型

在apollo中,Scenario被定义了十余种类型,在官方文档Planing 2.0综述中有对各个场景的直观描述。例如下图是LANE_FOLLOW场景的示意图。

在这里插入图片描述

在apollo8.0中,可以从modules/planning/proto/planning_config.proto文件中看到关于Scenario的相关类型定义:

message ScenarioConfig {message StageConfig {// 阶段配置// 阶段类型optional StageType stage_type = 1;// 是否启用该阶段配置,默认值为trueoptional bool enabled = 2 [default = true];// 运行时使用的任务列表,按照顺序排列// 决定了这些任务的运行时顺序// 任务类型repeated TaskConfig.TaskType task_type = 3;// 未排序的任务配置列表// 任务配置repeated TaskConfig task_config = 4;}// 场景类型optional ScenarioType scenario_type = 1;// 场景配置,使用oneof是为了确保只有一个字段被设置oneof scenario_config {// 车道跟随场景配置ScenarioLaneFollowConfig lane_follow_config = 2;// 无保护交叉口场景配置ScenarioBareIntersectionUnprotectedConfig bare_intersection_unprotected_config = 3;// 紧急靠边停车场景配置ScenarioEmergencyPullOverConfig emergency_pull_over_config = 4;// 紧急停车场景配置ScenarioEmergencyStopConfig emergency_stop_config = 5;// 学习模型样本场景配置ScenarioLearningModelSampleConfig learning_model_sample_config = 6;// 窄街道U型转弯场景配置ScenarioNarrowStreetUTurnConfig narrow_street_u_turn_config = 7;// 停车即走场景配置ScenarioParkAndGoConfig park_and_go_config = 8;// 靠边停车场景配置ScenarioPullOverConfig pull_over_config = 9;// 无保护停车标志左转场景配置ScenarioStopSignUnprotectedConfig stop_sign_unprotected_config = 10;// 交通信号灯保护左转场景配置ScenarioTrafficLightProtectedConfig traffic_light_protected_config = 11;// 无保护交通信号灯左转场景配置ScenarioTrafficLightUnprotectedLeftTurnConfig traffic_light_unprotected_left_turn_config = 12;// 无保护交通信号灯右转场景配置ScenarioTrafficLightUnprotectedRightTurnConfig traffic_light_unprotected_right_turn_config = 13;// 代客停车场景配置ScenarioValetParkingConfig valet_parking_config = 14;// 让路标志场景配置ScenarioYieldSignConfig yield_sign_config = 15;// 死胡同场景配置(掉头)ScenarioDeadEndTurnAroundConfig deadend_turnaround_config = 18;}// 运行时使用的阶段列表,第一个是默认阶段// 阶段类型repeated StageType stage_type = 16;// 运行时使用的阶段配置列表,未排序// 阶段配置repeated StageConfig stage_config = 17;
}

接着来看看Scenario Manager做了些什么。
modules/planning/scenarios/scenario_manager.cc
在这里插入图片描述

场景注册

// modules/planning/scenarios/scenario_manager.cc
bool ScenarioManager::Init(const PlanningConfig& planning_config) {planning_config_.CopyFrom(planning_config);// 注册场景RegisterScenarios();// 默认为LANE_FOLLOWdefault_scenario_type_ = ScenarioType::LANE_FOLLOW;current_scenario_ = CreateScenario(default_scenario_type_);return true;
}

ScenarioManager负责实际的场景注册,Init函数负责对场景的初始化,首先调用::RegisterScenario这个函数对各个场景配置文件进行读取和注册;在开始创建LANE_FOLLOW作为默认运行场景。

// modules/planning/planner/public_road/public_road_planner.cc
Status PublicRoadPlanner::Init(const PlanningConfig& config) {config_ = config;scenario_manager_.Init(config);return Status::OK();
}

PublicRoadPlanner初始化时会调用配置文件里的参数来建立这个对象,ScenarioManager会实例化一个全局的scenario_manager_对象来进行场景管理。

场景转换

// 更新场景
void ScenarioManager::Update(const common::TrajectoryPoint& ego_point,const Frame& frame) {ACHECK(!frame.reference_line_info().empty());// 获取当前要处理的overlapsObserve(frame);// 场景分发ScenarioDispatch(frame);
}

ScenarioManager除了负责场景的配置与注册外也负责对场景进行转换。场景转换在ScenarioManager::Update中实现,这个函数有两个输入,车辆当前的状态信息ego_point,和planning循环计算所需要的其他相关信息frameScenarioManager::Update首先调用Observe函数,用以获取当前要处理的overlaps.

// 获取当前要处理的overlaps
void ScenarioManager::Observe(const Frame& frame) {// init first_encountered_overlap_map_first_encountered_overlap_map_.clear();const auto& reference_line_info = frame.reference_line_info().front();const auto& first_encountered_overlaps =reference_line_info.FirstEncounteredOverlaps();for (const auto& overlap : first_encountered_overlaps) {if (overlap.first == ReferenceLineInfo::PNC_JUNCTION ||overlap.first == ReferenceLineInfo::SIGNAL ||overlap.first == ReferenceLineInfo::STOP_SIGN ||overlap.first == ReferenceLineInfo::YIELD_SIGN) {first_encountered_overlap_map_[overlap.first] = overlap.second;}}
}

在apollo里 overlap 是指地图上任意重合的东西,比如PNC_JUNCTION里是道路之间有相互重合,SIGNAL是信号灯与道路有重合,STOP_SIGN是停止标志与道路有重合,YIELD_SIGN是让行标志与道路有重合。

// 通过一个有限状态机,决定当前的场景
// 会根据配置选择基于规则还是基于学习的决策方法。
void ScenarioManager::ScenarioDispatch(const Frame& frame) {ACHECK(!frame.reference_line_info().empty());ScenarioType scenario_type;int history_points_len = 0;if (injector_->learning_based_data() &&injector_->learning_based_data()->GetLatestLearningDataFrame()) {history_points_len = injector_->learning_based_data()->GetLatestLearningDataFrame()->adc_trajectory_point_size();}if ((planning_config_.learning_mode() == PlanningConfig::E2E ||planning_config_.learning_mode() == PlanningConfig::E2E_TEST) &&history_points_len >= FLAGS_min_past_history_points_len) {scenario_type = ScenarioDispatchLearning();} else {scenario_type = ScenarioDispatchNonLearning(frame);}ADEBUG << "select scenario: " << ScenarioType_Name(scenario_type);// update PlanningContextUpdatePlanningContext(frame, scenario_type);if (current_scenario_->scenario_type() != scenario_type) {current_scenario_ = CreateScenario(scenario_type);}
}

当遇到这些overlaps时,在函数ScenarioDispatch中对其进行具体的处理或者场景切换。ScenarioDispatch会根据配置选择基于规则还是基于学习的决策方法。

接着来看ScenarioDispatchNonLearning函数:

ScenarioType ScenarioManager::ScenarioDispatchNonLearning(const Frame& frame) {// default: LANE_FOLLOWScenarioType scenario_type = default_scenario_type_;// Pad Msg scenario(驾驶员意图?)scenario_type = SelectPadMsgScenario(frame);if (scenario_type == default_scenario_type_) {// check current_scenario (not switchable)switch (current_scenario_->scenario_type()) {case ScenarioType::LANE_FOLLOW:case ScenarioType::PULL_OVER:break;case ScenarioType::BARE_INTERSECTION_UNPROTECTED:case ScenarioType::EMERGENCY_PULL_OVER:case ScenarioType::PARK_AND_GO:case ScenarioType::STOP_SIGN_PROTECTED:case ScenarioType::STOP_SIGN_UNPROTECTED:case ScenarioType::TRAFFIC_LIGHT_PROTECTED:case ScenarioType::TRAFFIC_LIGHT_UNPROTECTED_LEFT_TURN:case ScenarioType::TRAFFIC_LIGHT_UNPROTECTED_RIGHT_TURN:case ScenarioType::VALET_PARKING:case ScenarioType::YIELD_SIGN:// must continue until finishif (current_scenario_->GetStatus() !=Scenario::ScenarioStatus::STATUS_DONE) {scenario_type = current_scenario_->scenario_type();}break;default:break;}}// ParkAndGo / starting scenarioif (scenario_type == default_scenario_type_) {if (FLAGS_enable_scenario_park_and_go) {scenario_type = SelectParkAndGoScenario(frame);}}// intersection scenariosif (scenario_type == default_scenario_type_) {scenario_type = SelectInterceptionScenario(frame);}// pull-over scenarioif (scenario_type == default_scenario_type_) {if (FLAGS_enable_scenario_pull_over) {scenario_type = SelectPullOverScenario(frame);}}// VALET_PARKING scenarioif (scenario_type == default_scenario_type_) {scenario_type = SelectValetParkingScenario(frame);}return scenario_type;
}

首先,确定一个默认的场景类型,即LANE_FOLLOW。然后,判断是否有驾驶员意图,如果有,则将场景类型改为相应的意图场景类型。如果没有,则进入下一步逻辑。接着检查当前的场景类型,如果是LANE_FOLLOW或PULL_OVER,直接break;若是其他场景,且其他场景还未执行完毕,则继续执行当前场景。如果当前场景已完成,则进入下一步逻辑。PARK_AND_GO和PULL_OVER若有相关配置,则执行相应的函数。注意:每次切换场景都从默认场景开始,最后回到默认场景。

Scenario::Process

PublicRoadPlanner::Plan的流程可知,在获取完当前场景之后,会调用Scenario的Process函数,对具体的场景进行处理。接下来便来看看Scenario的Process函数:
modules/planning/scenarios/scenario.cc

Scenario::ScenarioStatus Scenario::Process(const common::TrajectoryPoint& planning_init_point, Frame* frame) {// stage类型unknowif (current_stage_ == nullptr) {AWARN << "Current stage is a null pointer.";return STATUS_UNKNOWN;}// stage全部执行完成if (current_stage_->stage_type() == StageType::NO_STAGE) {scenario_status_ = STATUS_DONE;return scenario_status_;}// 当前处于某一stage,调用这个stage的Process()函数,处理具体规划逻辑auto ret = current_stage_->Process(planning_init_point, frame);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;}// 读取当前stage完成的状态,并对下一个stage进行处理case Stage::FINISHED: {auto next_stage = current_stage_->NextStage();if (next_stage != current_stage_->stage_type()) {AINFO << "switch stage from " << current_stage_->Name() << " to "<< StageType_Name(next_stage);if (next_stage == StageType::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], injector_);if (current_stage_ == nullptr) {AWARN << "Current stage is a null pointer.";return STATUS_UNKNOWN;}}if (current_stage_ != nullptr &&current_stage_->stage_type() != StageType::NO_STAGE) {scenario_status_ = STATUS_PROCESSING;} else {scenario_status_ = STATUS_DONE;}break;}default: {AWARN << "Unexpected Stage return value: " << ret;scenario_status_ = STATUS_UNKNOWN;}}return scenario_status_;
}

Scenario都是顺序执行,只需要判断这一阶段是否结束,然后转到下一个阶段就可以了。CreateStage生成相应的stage,之后再进行stage的process。

Stage

以Lanefollowstage为例进行介绍。
modules/planning/scenarios/lane_follow/lane_follow_stage.cc

Stage::StageStatus LaneFollowStage::Process(const TrajectoryPoint& planning_start_point, Frame* frame) {bool has_drivable_reference_line = false;ADEBUG << "Number of reference lines:\t"<< frame->mutable_reference_line_info()->size();unsigned int count = 0;// 遍历所有的参考线,直到找到可用来规划的参考线后退出for (auto& reference_line_info : *frame->mutable_reference_line_info()) {// TODO(SHU): need refactorif (count++ == frame->mutable_reference_line_info()->size()) {break;}ADEBUG << "No: [" << count << "] Reference Line.";ADEBUG << "IsChangeLanePath: " << reference_line_info.IsChangeLanePath();// 找到可用来行驶的参考线,退出循环if (has_drivable_reference_line) {reference_line_info.SetDrivable(false);break;}// 执行LaneFollow的规划auto cur_status =PlanOnReferenceLine(planning_start_point, frame, &reference_line_info);// 判断规划结果是否OKif (cur_status.ok()) {// 如果发生lanechange,判断reference_line的costif (reference_line_info.IsChangeLanePath()) {ADEBUG << "reference line is lane change ref.";ADEBUG << "FLAGS_enable_smarter_lane_change: "<< FLAGS_enable_smarter_lane_change;if (reference_line_info.Cost() < kStraightForwardLineCost &&(LaneChangeDecider::IsClearToChangeLane(&reference_line_info) ||FLAGS_enable_smarter_lane_change)) {// If the path and speed optimization succeed on target lane while// under smart lane-change or IsClearToChangeLane under older versionhas_drivable_reference_line = true;reference_line_info.SetDrivable(true);LaneChangeDecider::UpdatePreparationDistance(true, frame, &reference_line_info, injector_->planning_context());ADEBUG << "\tclear for lane change";} else {LaneChangeDecider::UpdatePreparationDistance(false, frame, &reference_line_info,injector_->planning_context());reference_line_info.SetDrivable(false);ADEBUG << "\tlane change failed";}} else {// 如果没有lanechange,stage执行结果为OK,则has_drivable_reference_line置位trueADEBUG << "reference line is NOT lane change ref.";has_drivable_reference_line = true;}} else {reference_line_info.SetDrivable(false);}}return has_drivable_reference_line ? StageStatus::RUNNING: StageStatus::ERROR;
}

Process主要是查找可供规划的参考线,之后在PlanOnReferenceLine函数中进行具体的执行。同时还对变道的状况进行规划,如果规划成功后,还需要判断目标车道的变道cost,如果cost太高,那么就会舍弃掉这条目标车道的reference_line, 此时放弃变道的规划,继续循环使用原车道的reference_line进行规划。

PlanOnReferenceLine函数

Status LaneFollowStage::PlanOnReferenceLine(const TrajectoryPoint& planning_start_point, Frame* frame,ReferenceLineInfo* reference_line_info) {// 判断是否有lanechange意图,如果否增加当前参考线的cost?有点疑问,增加了变道的可能if (!reference_line_info->IsChangeLanePath()) {reference_line_info->AddCost(kStraightForwardLineCost);}ADEBUG << "planning start point:" << planning_start_point.DebugString();ADEBUG << "Current reference_line_info is IsChangeLanePath: "<< reference_line_info->IsChangeLanePath();// 顺序执行stage中的任务auto ret = Status::OK();for (auto* task : task_list_) {const double start_timestamp = Clock::NowInSeconds();// 执行每个task的具体逻辑ret = task->Execute(frame, reference_line_info);const double end_timestamp = Clock::NowInSeconds();const double time_diff_ms = (end_timestamp - start_timestamp) * 1000;ADEBUG << "after task[" << task->Name()<< "]:" << reference_line_info->PathSpeedDebugString();ADEBUG << task->Name() << " time spend: " << time_diff_ms << " ms.";RecordDebugInfo(reference_line_info, task->Name(), time_diff_ms);// 如果task执行失败,退出task执行序列,并且记录失败信息if (!ret.ok()) {AERROR << "Failed to run tasks[" << task->Name()<< "], Error message: " << ret.error_message();break;}// TODO(SHU): disable reference line order changes for now// updated reference_line_info, because it is changed in// lane_change_decider by PrioritizeChangeLane().// reference_line_info = &frame->mutable_reference_line_info()->front();// ADEBUG << "Current reference_line_info is IsChangeLanePath: "//        << reference_line_info->IsChangeLanePath();}RecordObstacleDebugInfo(reference_line_info);// check path and speed results for path or speed fallbackreference_line_info->set_trajectory_type(ADCTrajectory::NORMAL);// 如果task执行失败,则使用备用的规划轨迹if (!ret.ok()) {PlanFallbackTrajectory(planning_start_point, frame, reference_line_info);}// 对规划的轨迹进行合成,如果合成失败,返回失败状态DiscretizedTrajectory trajectory;if (!reference_line_info->CombinePathAndSpeedProfile(planning_start_point.relative_time(),planning_start_point.path_point().s(), &trajectory)) {const std::string msg = "Fail to aggregate planning trajectory.";AERROR << msg;return Status(ErrorCode::PLANNING_ERROR, msg);}// determine if there is a destination on reference line.double dest_stop_s = -1.0;for (const auto* obstacle :reference_line_info->path_decision()->obstacles().Items()) {if (obstacle->LongitudinalDecision().has_stop() &&obstacle->LongitudinalDecision().stop().reason_code() ==STOP_REASON_DESTINATION) {SLPoint dest_sl = GetStopSL(obstacle->LongitudinalDecision().stop(),reference_line_info->reference_line());dest_stop_s = dest_sl.s();}}// 增加障碍物的代价for (const auto* obstacle :reference_line_info->path_decision()->obstacles().Items()) {if (obstacle->IsVirtual()) {continue;}if (!obstacle->IsStatic()) {continue;}if (obstacle->LongitudinalDecision().has_stop()) {bool add_stop_obstacle_cost = false;if (dest_stop_s < 0.0) {add_stop_obstacle_cost = true;} else {SLPoint stop_sl = GetStopSL(obstacle->LongitudinalDecision().stop(),reference_line_info->reference_line());if (stop_sl.s() < dest_stop_s) {add_stop_obstacle_cost = true;}}if (add_stop_obstacle_cost) {static constexpr double kReferenceLineStaticObsCost = 1e3;reference_line_info->AddCost(kReferenceLineStaticObsCost);}}}if (FLAGS_enable_trajectory_check) {if (ConstraintChecker::ValidTrajectory(trajectory) !=ConstraintChecker::Result::VALID) {const std::string msg = "Current planning trajectory is not valid.";AERROR << msg;return Status(ErrorCode::PLANNING_ERROR, msg);}}reference_line_info->SetTrajectory(trajectory);reference_line_info->SetDrivable(true);return Status::OK();
}

Task

还是以Lanefollow为例,Task的配置部分在此处。modules/planning/conf/scenario/lane_follow_config.pb.txt

stage_config: {stage_type: LANE_FOLLOW_DEFAULT_STAGEenabled: truetask_type: LANE_CHANGE_DECIDERtask_type: PATH_REUSE_DECIDERtask_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: SPEED_BOUNDS_PRIORI_DECIDERtask_type: SPEED_HEURISTIC_OPTIMIZERtask_type: SPEED_DECIDERtask_type: SPEED_BOUNDS_FINAL_DECIDERtask_type: PIECEWISE_JERK_SPEED_OPTIMIZER# task_type: PIECEWISE_JERK_NONLINEAR_SPEED_OPTIMIZERtask_type: RSS_DECIDER

参考以及推荐阅

[1] Apollo星火计划学习笔记——第七讲自动驾驶规划技术原理1
[2] Apollo Planning决策规划代码详细解析 (1):Scenario选择
[3] apollo介绍之planning模块(四)
[4] 解析百度Apollo之决策规划模块
[5] Apollo规划模块(一):scenario
[6] Apollo星火计划学习笔记——Apollo决策规划技术详解及实现(以交通灯场景检测为例)
[7] Apollo Planning 模块

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/41885.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Linux】POSIX信号量和基于环形队列的生产消费者模型

目录 写在前面的话 什么是POSIX信号量 POSIX信号量的使用 基于环形队列的生产消费者模型 写在前面的话 本文章主要先介绍POSIX信号量&#xff0c;以及一些接口的使用&#xff0c;然后再编码设计一个基于环形队列的生产消费者模型来使用这些接口。 讲解POSIX信号量时&#x…

windows结束explorer进程后桌面白屏解决

背景 结束进程时一不小心一起删掉explorer.exe &#xff0c;这个文件结束桌面就一片白 &#xff0c; 解决&#xff1a; 不要关机&#xff0c;同时按键盘上ctrlshiftesc ,重新进入任务管理器&#xff0c;接着点“进程”选项&#xff0c;按左上角文件选项&#xff0c;进入下拉菜单…

备份或同步数据?跨国大文件传输的不同需求与解决方案

信息化时代的到来&#xff0c;使得许多个人、组织、企业在日常生活中都需要对数据进行备份或同步。但不同的应用场景和需求&#xff0c;也需要不同的备份和同步方式。而在跨国大文件传输方面&#xff0c;更是需要根据不同需求选择合适的传输方案。下面将分别介绍备份与同步数据…

BeanFactoryApplicationContext之间的关系

1**.BeanFactory与ApplicationContext之间的关系** &#xff08;1&#xff09;从继承关系上来看&#xff1a; ​ BeanFactory它是ApplicationContext 的父接口 &#xff08;2&#xff09;从功能上来看&#xff1a; ​ BeanFactory才是spring中的核心容器&#xff0c;而Appli…

【数学建模】-- 数学规划模型

概述&#xff1a; 什么是数学规划&#xff1f; 数学建模中的数学规划是指利用数学方法和技巧对问题进行数学建模&#xff0c;并通过数学规划模型求解最优解的过程。数学规划是一种数学优化方法&#xff0c;旨在找到使目标函数达到最大值或最小值的变量取值&#xff0c;同时满足…

VGG简单学习

VGG简单学习 简单介绍 在AlexNet网络的基础上&#xff0c;为了设计深层神经网络&#xff0c;牛津大学设计了VGG网络,采用块的设计理念&#xff0c;将AlexNet中多个重复的卷积层和池化层组成一个块 论文中&#xff0c;使用3x3卷积核&#xff0c;padding1的卷积层 和带有2x2的汇…

WebMagic - 创意前端项目集合(点击链接可在电脑上查看效果)

WebMagic - 创意前端项目集合 欢迎来到 WebMagic 仓库&#xff01;这里汇集了一系列令人惊叹的前端项目&#xff0c;涵盖了HTML5、CSS3和JS等多项技术。无论你是前端开发者、设计师&#xff0c;还是对创意互动内容感兴趣的人&#xff0c;这个仓库都将为你带来无尽的惊喜。 每…

LangChain手记 Chains

整理并翻译自DeepLearning.AILangChain的官方课程&#xff1a;Chains&#xff08;源代码可见&#xff09; Chains 直译链&#xff0c;表达的意思更像是对话链&#xff0c;对话链的背后是思维链 LLM Chain&#xff08;LLM链&#xff09; 首先介绍了一个最简单的例子&#xff0c…

代码审计-java项目-组件漏洞审计

代码审计必备知识点&#xff1a; 1、代码审计开始前准备&#xff1a; 环境搭建使用&#xff0c;工具插件安装使用&#xff0c;掌握各种漏洞原理及利用,代码开发类知识点。 2、代码审计前信息收集&#xff1a; 审计目标的程序名&#xff0c;版本&#xff0c;当前环境(系统,中间件…

图数据库_Neo4j和SpringBoot整合使用_实战创建明星关系图谱---Neo4j图数据库工作笔记0010

然后我们再来看一下这个明星关系图谱 可以看到这里 这个是原来的startRelation 我们可以写CQL去查询对应的关系 可以看到,首先查询出来以后,然后就可以去创建 我们可以把写的创建明星关系的CQL,拿到 springboot中去执行 可以看到,这里我们先写一个StarRelationRepository,然…

Java二分法查找

二分法&#xff1a;首先需要一个由小到大排序好的数组&#xff0c;先找到其中间值&#xff0c;然后进行比较如果比较中间值大的话则向前找。如果比要找的小&#xff0c;则向后找。 代码实现&#xff1a; //定义查询方法 public static int searchTarget(int[] nums, int targ…

wireshark界面内容含义

网络分析工具——WireShark的使用&#xff08;超详细&#xff09;_世间繁华梦一出的博客-CSDN博客 wireshark抓包数据&#xff1a;理解与分析_wireshark里面length_ 佚名的博客-CSDN博客

【图书推荐 | 测试】—《测试设计思想》

前言 随着科技的不断发展&#xff0c;互联网的不断进步&#xff0c;日益出现了一种趋势&#xff1a;测试设计将成为一种跨领域的综合性工作&#xff0c;测试者将成为一种跨领域的通用型人才。由此清华大学出版社推出了一本名为《测试设计思想》的书籍&#xff0c;由知名专家周…

ios swift5 collectionView 瀑布流(两列)

文章目录 1.瀑布流1.1 demo地址1.2 记得把部署的最低版本由8改成11,13甚至更高。不然编译会报错 2.动态计算图片和文字的高度 1.瀑布流 1.1 demo地址 CollectionViewWaterfallLayout - github 1.2 记得把部署的最低版本由8改成11,13甚至更高。不然编译会报错 2.动态计算图片和…

产业园区数字孪生3d可视化全景展示方案

随着数字经济的发展&#xff0c;数字技术给企业发展带来了机遇的同时&#xff0c;也为企业管理带来挑战。比如园区运维&#xff0c;不仅体量大&#xff0c;复杂的运维管理系统&#xff0c;落地难度也较高。那么如何通过数字化手段重塑园区运营&#xff0c;打通园区各业务数据孤…

SQLyog中导入CSV文件入库到MySQL中

1.在数据库中新建一个表&#xff0c;设置列名&#xff08;与待导入文件一致&#xff09;&#xff0c;字段可以多出几个都可以 2.右键表名&#xff0c;导入- - >导入使用本地加载的CSV数据 选择使用加载本地CVS数据 3.指定好转义字符&#xff0c;将终止设置为,号(英文状态下…

idea常见错误大全之:解决全局搜索失效+搜索条件失效(条件为空)+F8失灵

问题一&#xff1a;全局搜索快捷键ctrlshiftf 突然失灵了&#xff0c;键盘敲烂了 都没反应&#xff0c;这是为什么呢&#xff1f; 肯定不是idea本身的原因&#xff0c;那么就是其它外在因素影响到了idea的快捷键&#xff0c;那么其它的快捷键为什么没失效呢&#xff0c;原因只有…

服务器扩展未生效

服务器扩容未生效 在阿里云付费扩容后&#xff0c;在服务器里面看未生效。 阿里云->实例与镜像->实例->选择实例->云盘->扩容进入linux服务器查看&#xff1a; df -h vda1扩容未生效。原40g->扩容后100g 解决方法&#xff1a; 1、安装growpart yum inst…

广州华锐互动:奶牛难产原因及救治VR仿真实训系统

奶牛难产是一种常见的疾病&#xff0c;对奶牛的健康和生产造成很大的影响。为了解决这一问题&#xff0c;许多奶牛养殖场开始采用VR仿真技术来培训奶牛兽医&#xff0c;帮助学生更好地理解奶牛养殖的实际过程&#xff0c;提高他们的实践能力的教学方式。 VR技术开发公司广州华锐…

【算法】双指针划分思想妙解移动零

Problem: 283. 移动零 文章目录 思路算法图解分析复杂度Code 思路 首先我们来讲一下本题的思路 本题主要可以归到【数组划分/数组分块】这一类的题型。我们将一个数组中的所有元素划分为两段区间&#xff0c;左侧是非零元素&#xff0c;右侧是零元素 那解决这一类的题我们首先想…