首页 > 其他分享 >Apollo 决策规划代码详细解析 :Stage逻辑详解

Apollo 决策规划代码详细解析 :Stage逻辑详解

时间:2024-06-15 20:32:57浏览次数:23  
标签:task reference type 详解 line Apollo config stage Stage

Apollo 决策规划代码详细解析 :Stage逻辑详解

附赠自动驾驶最全的学习资料和量产经验:链接

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

  • Stage中如何对task进行注册;

  • Stage中如何调用task执行规划任务;

  • LaneFollowStage 包含哪些task;

  • LaneFollowStage 的详细运行逻辑;

  • PlanOnReferenceLine() 规划算法的运行逻辑

代码具体过程如下:

1、之前的章节讲到,Apollo在每个palnning周期首先决策当前处于哪个场景Scenario下面的哪个状态stage当中,当确认好stage之后,便会调用这个stage的Process() 函数来执行具体的规划逻辑。stage::Process() 主要逻辑是根据配置文件,来依次执行每一个注册的task的Execute() 函数,从而把具体的规划任务分散到每个task当中。stage并不属于某个特定的Scenario,task也不属于某个特定的stage,这些任务的组合通过配置文件进行配置,这样便保证了整个规划模块的解耦与可扩展性。

image

2、接下来将介绍最常用的LaneFollowStage,task的注册发生在 LaneFollowStage 对象建立阶段,LaneFollowStage的构造函数继承了Stage类的构造函数,在建立对象时读取配置文件,并对当前stage包含的task进行注册。

LaneFollowStage的构造函数对Stage的构造函数进行继承:

LaneFollowStage::LaneFollowStage(
    const ScenarioConfig::StageConfig& config,
    const std::shared_ptr<DependencyInjector>& injector)
    : Stage(config, injector) {}

Stage的构造函数中读取配置文件,并且注册每个task,并保存至全局变量task_list_:

Stage::Stage(const ScenarioConfig::StageConfig& config,
             const std::shared_ptr<DependencyInjector>& injector)
    : config_(config), injector_(injector) {
  // set stage_type in PlanningContext
  injector->planning_context()
      ->mutable_planning_status()
      ->mutable_scenario()
      ->set_stage_type(stage_type());
 
  name_ = ScenarioConfig::StageType_Name(config_.stage_type());
  next_stage_ = config_.stage_type();
  std::unordered_map<TaskConfig::TaskType, const TaskConfig*, std::hash<int>>
      config_map;
  for (const auto& task_config : config_.task_config()) {
    config_map[task_config.task_type()] = &task_config;
  }
  for (int i = 0; i < config_.task_type_size(); ++i) {
    auto task_type = config_.task_type(i);
    ACHECK(config_map.find(task_type) != config_map.end())
        << "Task: " << TaskConfig::TaskType_Name(task_type)
        << " used but not configured";
    auto iter = tasks_.find(task_type);
    if (iter == tasks_.end()) {
      auto ptr = TaskFactory::CreateTask(*config_map[task_type], injector_);
      task_list_.push_back(ptr.get());
      tasks_[task_type] = std::move(ptr);
    } else {
      task_list_.push_back(iter->second.get());
    }
  }
}

3、LaneFollowStage 的Process() 函数执行主要的规划逻辑,在函数内部会对变道的效率来进行判断从而选择是否按照变道进行规划,或者保持本车道运行,关于变道判断逻辑上一章节已经分析。在LaneFollowStage 的Process() 函数中,另外一个重要的函数是PlanOnReferenceLine() , 在这个函数中实现了对每个task的依次调用,关于这个函数的定义及备注如下:

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();
  // 遍历每个task,即把注册的task运行一遍
  auto ret = Status::OK();
  for (auto* task : task_list_) {
    // 记录起始时间戳
    const double start_timestamp = Clock::NowInSeconds();
    // 执行每个task的具体逻辑
    ret = task->Execute(frame, reference_line_info);
    // 记录当前task的结束时间
    const double end_timestamp = Clock::NowInSeconds();
    // 计算当前task的运行时间
    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 fallback
  reference_line_info->set_trajectory_type(ADCTrajectory::NORMAL);
  if (!ret.ok()) {
    // 如果task执行失败,则使用备用的规划轨迹
    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();
}

4、LaneFollowStage 注册了以下这些task,都会被顺序执行,这些task封装了具体的规划逻辑,后续的章节会介绍具体的规划算法。

scenario_type: LANE_FOLLOW
stage_type: LANE_FOLLOW_DEFAULT_STAGE
stage_config: {
  stage_type: LANE_FOLLOW_DEFAULT_STAGE
  enabled: true
  task_type: LANE_CHANGE_DECIDER
  task_type: PATH_REUSE_DECIDER
  task_type: PATH_LANE_BORROW_DECIDER
  task_type: PATH_BOUNDS_DECIDER
  task_type: PIECEWISE_JERK_PATH_OPTIMIZER
  task_type: PATH_ASSESSMENT_DECIDER
  task_type: PATH_DECIDER
  task_type: RULE_BASED_STOP_DECIDER
  task_type: ST_BOUNDS_DECIDER
  task_type: SPEED_BOUNDS_PRIORI_DECIDER
  task_type: SPEED_HEURISTIC_OPTIMIZER
  task_type: SPEED_DECIDER
  task_type: SPEED_BOUNDS_FINAL_DECIDER
  # task_type: PIECEWISE_JERK_SPEED_OPTIMIZER
  task_type: PIECEWISE_JERK_NONLINEAR_SPEED_OPTIMIZER
  task_type: RSS_DECIDER
}

5、task的被调用流程图如下:

image

标签:task,reference,type,详解,line,Apollo,config,stage,Stage
From: https://blog.csdn.net/NEON7788/article/details/139653558

相关文章

  • 自动驾驶 Apollo 源码分析:ProcessMonitor
    自动驾驶 Apollo 源码分析:ProcessMonitor本篇文章分析 Apollo 中监控模块中监控进程状态的相关代码。附赠自动驾驶最全的学习资料和量产经验:链接1. ProcessMonitorProcessMonitor 是一个普通的定时器组件,内部函数也只是常规的 RunOnce 和 UpdateStatus,所以,......
  • NLP - word2vec详解
    Word2Vec是一种用于将词汇映射到高维向量空间的自然语言处理技术。由Google在2013年提出,它利用浅层神经网络模型来学习词汇的分布式表示。Word2Vec有两种主要模型:CBOW(ContinuousBagofWords)和Skip-gram。1.模型介绍ContinuousBagofWords(CBOW)CBOW模型的目标是通......
  • Spark RDD与算子详解:案例解析(第3天)
    系列文章目录1-RDD的基本介绍(了解)2-如何构建RDD(熟悉)3-RDD的相关算子(案例详解)(掌握)4-sparkRDD算子相关面试题(重点)文章目录系列文章目录前言一、RDD的基本介绍(了解)1、什么是RDD2、RDD的五大特性3、RDD的五大特点二、如何构建RDD(熟悉)1、并行化本地集合方式2、读......
  • 什么是深拷贝;深拷贝和浅拷贝有什么区别;深拷贝和浅拷贝有哪些方法(详解)
    目录一、为什么要区别深拷贝和浅拷贝二、浅拷贝2.1、什么是浅拷贝2.2、浅拷贝的方法使用Object.assign()使用展开运算符(...)使用数组的slice()方法(仅适用于数组)2.3、关于赋值运算符(=)三、深拷贝3.1、什么是深拷贝3.2、实现深拷贝的三种常见方法使用JSON.parse(JSON......
  • Arduino单片机详解
    Arduino单片机详解Arduino是一款开源的电子原型平台,广泛应用于各种电子项目和物联网开发。它通过简单易用的硬件和软件,使得电子制造和编程变得更加容易。Arduino的核心是单片机(Microcontroller),它是一个集成了CPU、内存和外设的芯片。下面将详细介绍Arduino单片机的各个方面,包......
  • 虚拟化 之一 详解 jailhouse 架构及原理、软硬件要求、源码文件、基本组件
      Jailhouse是一个基于Linux实现的针对创建工业级应用程序的小型Hypervisor,是由西门子公司的JanKiszka于2013年开发的,并得到了官方Linux内核的支持,在开源社区中获得了知名度和吸引力。Jailhouse  Jailhouse是一种轻量级的虚拟化技术,可以将多个操作系统(或......
  • Cookie、Session、LocalStorage 和 SessionStorage 的区别详解
    前言在前端开发中,数据存储和状态管理是非常重要的内容。常用的存储方式有Cookie、Session、LocalStorage和SessionStorage。本文将详细介绍这四者的区别,帮助开发者更好地理解和选择合适的存储方案。一、Cookie和Session的区别1.什么是Cookie?Cookie是由服务器生成......
  • PDF标准详解(三)—— PDF坐标系统和坐标变换
    之前我们了解了PDF文档的基本结构,并且展示了一个简单的helloworld。这个helloworld虽然只在页面中显示一个helloworld文字,但是包含的内容却是不少。这次我们仍然以它为切入点,来了解PDF的坐标系统以及坐标变换的相关知识图形学中二维图形变换中学我们学习了平面直角坐标系,x......
  • 【BERT】详解BERT
    一、为什么要提出BERT?传统的RNN类模型,包括LSTM,GRU以及其他各种变体,最大的问题在于提取能力不足。在《WhySelf-Attention?ATargetedEvaluationofNeuralMachineTranslationArchitectures》中证明了RNN的长距离特征提取能力甚至不亚于Transformer,并且比CNN强。其主要问题......
  • 传统后端SQL数据层替代解决方案: 内置数据源+JdbcTemplate+H2数据库 详解
    内置数据源我们回顾一下druid数据源的配置方式通过type属性指定数据源的类型导入依赖starter就使用了spring的自动装配功能格式二是在引入druid的依赖的基础上进行的一种配置方式Tomcat内部也可以进行数据源的配置轻量级中最快的数据源对象我们切换德鲁伊连接池......