Apollo Planning决策规划算法代码详细解析 (3):stage执行

通过之前章节的介绍,在经过Scenario的决策与执行之后,Apollo已经可以确定目前处于对应场景下的对应stage,接下来就进入stage类的内部,通过Process() 函数来进行具体的规划过程。本章节将以 LANE_FOLLOW 场景的 LANE_FOLLOW_DEFAULT_STAGE 为例,讲解stage的执行流程。

Apollo Planning决策规划系列文章:

Apollo Planning决策规划代码详细解析 (1):Scenario选择

Apollo Planning决策规划代码详细解析 (2):Scenario执行

Apollo Planning决策规划代码详细解析 (4):Stage逻辑详解

Apollo Planning决策规划代码详细解析 (5):规划算法流程介绍

Apollo Planning决策规划代码详细解析 (6):LaneChangeDecider

Apollo Planning决策规划代码详细解析 (7): PathReuseDecider

Apollo Planning决策规划代码详细解析 (8): PathLaneBorrowDecider

Apollo Planning决策规划代码详细解析 (9): PathBoundsDecider

Apollo Planning决策规划代码详细解析 (10):PiecewiseJerkPathOptimizer

Apollo Planning决策规划代码详细解析 (11): PathAssessmentDecider

Apollo Planning决策规划代码详细解析 (12): PathDecider

Apollo Planning决策规划代码详细解析 (13): RuleBasedStopDecider

算法详细介绍系列文章: 

Apollo决策规划算法Planning : Piecewise Jerk Path Optimizer

仿真技术介绍:

prescan联合simulink进行FCW的仿真_自动驾驶 Player的博客-CSDN博客

通过之前章节的介绍,在经过Scenario的决策与执行之后,Apollo已经可以确定目前处于对应场景下的对应stage,接下来就进入stage类的内部,通过Process() 函数来进行具体的规划过程。本章节将以 LANE_FOLLOW 场景的 LANE_FOLLOW_DEFAULT_STAGE 为例,讲解stage的执行流程。

码字不易,喜欢的朋友们麻烦点个关注与赞。

在本文你将学到下面这些内容:

  1. stage模块的注册与管理机制;
  2. stage模块调用的接口与顺序;
  3. NOP功能最常用stage:LANE_FOLLOW_DEFAULT_STAGE;
  4. LANE_FOLLOW_DEFAULT_STAGE中对变道场景的处理;
  5. LANE_FOLLOW_DEFAULT_STAGE的执行过程;
  6. LANE_FOLLOW_DEFAULT_STAGE的规划逻辑。

代码具体过程如下:

1、前文提到在planning模块执行的过程中,会根据配置来依次实例化每个Scenario当前所处的stage,LANE_FOLLOW这个Scenario只有LANE_FOLLOW_DEFAULT_STAGE一个stage,所以它的 CreateStage() 函数如下:

std::unique_ptr<Stage> LaneFollowScenario::CreateStage(
    const ScenarioConfig::StageConfig& stage_config,
    const std::shared_ptr<DependencyInjector>& injector) {
  if (stage_config.stage_type() != ScenarioConfig::LANE_FOLLOW_DEFAULT_STAGE) {
    AERROR << "Follow lane does not support stage type: "
           << ScenarioConfig::StageType_Name(stage_config.stage_type());
    return nullptr;
  }
  return std::unique_ptr<Stage>(new LaneFollowStage(stage_config, injector));
}

其它的场景可能有多个stage,就需要先注册所有stage,然后再根据type创建当前stage:

void ParkAndGoScenario::RegisterStages() {
  if (!s_stage_factory_.Empty()) {
    s_stage_factory_.Clear();
  }
  s_stage_factory_.Register(
      ScenarioConfig::PARK_AND_GO_CHECK,
      [](const ScenarioConfig::StageConfig& config,
         const std::shared_ptr<DependencyInjector>& injector) -> Stage* {
        return new ParkAndGoStageCheck(config, injector);
      });
  s_stage_factory_.Register(
      ScenarioConfig::PARK_AND_GO_ADJUST,
      [](const ScenarioConfig::StageConfig& config,
         const std::shared_ptr<DependencyInjector>& injector) -> Stage* {
        return new ParkAndGoStageAdjust(config, injector);
      });
  s_stage_factory_.Register(
      ScenarioConfig::PARK_AND_GO_PRE_CRUISE,
      [](const ScenarioConfig::StageConfig& config,
         const std::shared_ptr<DependencyInjector>& injector) -> Stage* {
        return new ParkAndGoStagePreCruise(config, injector);
      });
  s_stage_factory_.Register(
      ScenarioConfig::PARK_AND_GO_CRUISE,
      [](const ScenarioConfig::StageConfig& config,
         const std::shared_ptr<DependencyInjector>& injector) -> Stage* {
        return new ParkAndGoStageCruise(config, injector);
      });
}

std::unique_ptr<Stage> ParkAndGoScenario::CreateStage(
    const ScenarioConfig::StageConfig& stage_config,
    const std::shared_ptr<DependencyInjector>& injector) {
  if (s_stage_factory_.Empty()) {
    RegisterStages();
  }
  auto ptr = s_stage_factory_.CreateObjectOrNull(stage_config.stage_type(),
                                                 stage_config, injector);
  if (ptr) {
    ptr->SetContext(&context_);
  }
  return ptr;
}

2、当前stage创建好之后,在Scenario的Process()函数处会调用当前stage的Process()函数,完成当前stage具体的规划任务。

Scenario::ScenarioStatus Scenario::Process(
    const common::TrajectoryPoint& planning_init_point, Frame* frame) {
  // stage类型unknow
  if (current_stage_ == nullptr) {
    AWARN << "Current stage is a null pointer.";
    return STATUS_UNKNOWN;
  }
  // stage全部执行完成
  if (current_stage_->stage_type() == ScenarioConfig::NO_STAGE) {
    scenario_status_ = STATUS_DONE;
    return scenario_status_;
  }
  // 当前处于某一stage,调用这个stage的Process()函数,处理具体规划逻辑
  auto ret = current_stage_->Process(planning_init_point, frame);
  // 读取当前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], injector_);
        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;
    }
  }
  return scenario_status_;
}

3、在当前stage,即LANE_FOLLOW_DEFAULT_STAGE中,调用Process()函数,执行具体的固化任务。该函数的定义如下,关键步骤加了备注,稍后会详细讲解这部分代码。

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 refactor
    if (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;
    }

    // 执行具体规划任务
    auto cur_status =
        PlanOnReferenceLine(planning_start_point, frame, &reference_line_info);
    // 判断规划结果是否OK
    if (cur_status.ok()) {
      // 如果发生lanechange,判断reference_line的cost
      if (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 version
          has_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";
        }
      } 
      // 如果没有lanechange,stage执行结果为OK,则has_drivable_reference_line置位true
      else {
        ADEBUG << "reference line is NOT lane change ref.";
        has_drivable_reference_line = true;
      }
    } else {
      reference_line_info.SetDrivable(false);
    }
  }
  
  // 根据has_drivable_reference_line这个标志位的结果,返回stage执行的结果
  return has_drivable_reference_line ? StageStatus::RUNNING
                                     : StageStatus::ERROR;
}

通过以上代码看到,当变道时针对目标车道进行规划,如果规划成功后,还需要判断目标车道的变道cost,如果cost太高,那么就会舍弃掉这条目标车道的reference_line, 此时放弃变道的规划,继续循环使用原车道的reference_line进行规划。

4、LANE_FOLLOW_DEFAULT_STAGE的规划逻辑在 PlanOnReferenceLine() 函数,这个函数的实现如下,关键步骤加了备注:

Status LaneFollowStage::PlanOnReferenceLine(
    const TrajectoryPoint& planning_start_point, Frame* frame,
    ReferenceLineInfo* reference_line_info) {
  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();

  auto ret = Status::OK();
  for (auto* task : task_list_) {
    const double start_timestamp = Clock::NowInSeconds();

    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);

    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 fallback
  reference_line_info->set_trajectory_type(ADCTrajectory::NORMAL);
  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();
}

5、本章节主要以LANE_FOLLOW_DEFAULT_STAGE为例,介绍了apollo中stage这个模块的运行机制,LANE_FOLLOW_DEFAULT_STAGE是NOP功能下最常用到的一个stage,在下一章节会详细介绍这个stage的逻辑。

  • 16
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
### 回答1: Apollo Planning决策规划算法在无人驾驶领域中被广泛应用,在自动驾驶车辆中起着至关重要的作用。该算法主要通过对车辆周围环境的感知和分析,实现智能驾驶路线的规划决策,确保车辆安全行驶。 该算法代码主要包含三个部分:感知模块、规划模块和控制模块。其中感知模块主要负责采集车辆周围的环境信息,包括车辆所处的位置、路况、障碍物等。规划模块通过对这些信息的分析,提出一系列可能的驾驶路线,并通过评估这些路线的优劣来选择最佳驾驶路线。控制模块负责实现规划模块中选择的最佳驾驶路线,并控制车辆按照路线行驶。 在Apollo Planning决策规划算法中,规划模块是实现驾驶决策的最重要模块,也是最具技术难度的模块。规划模块通过对车辆当前状态和环境信息的分析,提出一系列汽车驾驶路线。该算法采用在线生成路线方案的方法,路线生成的步骤如下: 1. 动态路径规划:根据车辆的位置和行驶状态,实时选择当前最佳的驾驶路线。 2. 静态路线生成:基于当前车辆所处的环境信息,生成固定的驾驶路线。 3. 组合路径规划:将动态路径规划和静态路线生成相结合,生成最终的驾驶路线。 除此之外,Apollo Planning决策规划算法还包括计算机视觉、机器学习、深度学习和人工智能等技术,这些技术的综合应用使得Apollo Planning决策规划算法成为无人驾驶领域中应用最广泛的决策规划算法。 ### 回答2: Apollo Planning决策规划算法是一种用于自动驾驶系统的规划算法。该算法的主要作用是实时生成安全、有效且符合路况的路径以实现自动驾驶功能。本文将对该算法进行详细解析Apollo Planning决策规划算法主要包括三个步骤:路线规划、运动规划决策规划。具体代码如下: 1. 路线规划 ```c++ bool Planning::PlanOnReferenceLine() { std::vector<const hdmap::HDMap*> hdmap_vec; hdmap_vec.reserve(1); if (!GetHdmapOnRouting(current_routing_, &hdmap_vec)) { AERROR << "Failed to get hdmap on current routing with " << current_routing_.ShortDebugString(); return false; } const auto& reference_line_info = reference_line_infos_.front(); std::vector<ReferencePoint> ref_points; if (!CreateReferenceLineInfo(hdmap_vec.front(), reference_line_info, &ref_points)) { AERROR << "Failed to create reference line from routing"; return false; } // Smooth reference line Spline2d smoothed_ref_line; std::vector<double> s_refs; std::vector<double> l_refs; std::vector<double> headings; std::vector<double> kappas; std::vector<double> dkappas; if (!SmoothReferenceLine(ref_points, &smoothed_ref_line, &s_refs, &l_refs, &headings, &kappas, &dkappas)) { AERROR << "Failed to smooth reference line"; return false; } reference_line_info.SetTrajectory(&smoothed_ref_line); reference_line_info.SetReferenceLine(&ref_points); // set origin point if (!reference_line_info.SLToXY(s_refs.front(), 0.0, &origin_point_)) { AERROR << "Failed to get origin point on reference line"; return false; } return true; } ``` 在路线规划阶段中,Apollo Planning决策规划算法首先获取当前行驶路线和高精度地图数据。然后根据行驶路线和地图数据构建参考线,对参考线进行平滑处理,得到平滑后的参考线。此时我们可以得到平滑后的参考线的位置、方向和曲率等信息,这些信息将作为后面的运动和决策规划的输入。 2. 运动规划 ```c++ bool Planning::PlanOnPrediction() { PredictionObstacles prediction_obstacles; if (!GetPrediction(&prediction_obstacles)) { AERROR << "Prediction failed"; return false; } std::vector<Obstacle> obstacles; if (!BuildObstacle(prediction_obstacles, &obstacles)) { AERROR << "Unable to build obstacle"; return false; } const auto& reference_line_info = reference_line_infos_.front(); const auto& reference_line = reference_line_info.reference_line(); SpeedData speed_data; Cruiser::PlanningTarget planning_target; Status status = cruiser_->Plan(reference_line_info, obstacles, 0.0, reference_line.Length(), &speed_data, &planning_target); if (status != Status::OK()) { AERROR << "Failed to plan path with status: " << status; return false; } RecordDebugInfo(reference_line_info, obstacles, speed_data); return true; } ``` 运动规划主要用于生成车辆在参考线上的运行轨迹。在运动规划阶段,Apollo Planning决策规划算法首先获取预测障碍物信息,将预测的障碍物转化为Obstacle对象。然后根据当前平滑后的参考线、障碍物等信息进行运动规划。运动规划的目标是生成符合规划目标的速度曲线。最后,Apollo Planning决策规划算法记录调试信息,以便后续分析调试。 3. 决策规划 ```c++ bool Planning::MakeDecision() { const auto& reference_line_info = reference_line_infos_.front(); const auto& reference_line = reference_line_info.reference_line(); std::vector<const Obstacle*> obstacles; if (!Obstacle::CreateObstacleRegions(FLAGS_max_distance_obstacle, reference_line_info, &obstacles)) { AERROR << "Failed to create obstacle regions"; return false; } for (auto obstacle_ptr : obstacles) { const auto& obstacle = *obstacle_ptr; if (obstacle.IsVirtual()) { continue; } if (obstacle.IsStatic()) { continue; } if (obstacle.type() == PerceptionObstacle::BICYCLE || obstacle.type() == PerceptionObstacle::PEDESTRIAN) { continue; } const auto& nearest_path_point = obstacle.nearest_point(); const SLPoint obstacle_sl = reference_line_info.xy_to_sl(nearest_path_point); if (obstacle_sl.s() < -FLAGS_max_distance_obstacle || obstacle_sl.s() > reference_line.Length() + FLAGS_max_distance_obstacle) { continue; } ObjectDecisionType decision; decision.mutable_avoid(); decision.mutable_avoid()->set_distance_s(-obstacle_sl.s()); reference_line_info.AddCost(&obstacle, &decision); } std::vector<ObjectDecisionType> decisions; if (!traffic_rule_manager_.ApplyRule(reference_line_info, &decisions)) { AERROR << "Failed to apply traffic rule manager"; return false; } reference_line_info.SetDecision(decisions); return true; } ``` 决策规划是基于当前环境信息和规划的路径等输入信息,实时生成控制命令的过程。在Apollo Planning决策规划算法中,决策规划阶段根据当前参考线、障碍物等信息生成决策。该算法根据不同的规则和策略,生成不同的控制命令,以保证车辆安全、有效地运行。 综上,Apollo Planning决策规划算法是自动驾驶系统中重要的规划算法之一,它通过路线规划、运动规划决策规划三个步骤,实现了安全、有效和符合路况的路径规划,为自动驾驶车辆的控制提供了重要的支持。 ### 回答3: Apollo Planning(阿波罗规划)是百度自动驾驶平台Apollo中的一种决策规划算法,主要用于规划车辆的驾驶行为。该算法基于深度强化学习,使用了运动学模型和环境感知技术,可以根据车辆当前位置和目的地,生成一条最优的行驶路径,包括车辆的控制指令和行驶速度等。 该算法的核心技术是深度强化学习,它通过对驾驶过程进行大量的仿真,让计算机通过自我学习得到驾驶规则,使车辆能够根据不同的场景做出最优的决策。具体而言,算法先通过神经网络生成一系列潜在的行动策略,然后通过与环境进行交互、执行行动并接收环境反馈来评估每个策略的优劣,最终选取最优策略进行执行。 在实现上,Apollo Planning算法主要由四个模块构成:感知模块、规划模块、执行模块和控制模块。感知模块主要用于获取车辆周围环境的信息,包括车辆位置、速度、道路情况、交通灯等;规划模块根据感知模块提供的信息和车辆的目的地,生成一条最优的行驶路径;执行模块则根据规划模块生成的路径信息,实现车辆的自主驾驶;控制模块则根据执行模块生成的控制指令,控制车辆的加速、刹车、转向等行为。 在算法实现上,Apollo Planning采用了C++编程语言,结合ROS框架实现各模块之间的数据交互和代码复用,保证了算法的高效性和可维护性。算法代码实现方面还采用了许多优化技术,包括多线程并发执行、高效的数据结构和算法等,以提升算法的运行效率和稳定性。 总之,Apollo Planning是一种基于深度强化学习的决策规划算法,具有高效、自主、可靠等特点,在智能驾驶领域具有广泛应用前景。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

自动驾驶Player

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值