Apollo Planning模块中的Hybird A*算法

流程图

OpenSpacePlanner算法与周边模块关系

在这里插入图片描述

OpenSpacePlanner与PublicRoadPlanner关系

Apollo中的PublicRoadPlanner涵盖了OpenSpacePlanner。
OpenSpacePlanner用来处理public road所包含的2个场景

  • u-turn
  • valet parking

在apollo/modules/planning/conf/planning_config.pb.txt 配置文件中,
PublicRoadPlanner 支持 VALET_PARKING 场景,而该场景分为2个stage

  • VALET_PARKING_APPROACHING_PARKING_SPOT
  • VALET_PARKING_PARKING

VALET_PARKING_PARKING stage 包含task:OPEN_SPACE_TRAJECTORY_PROVIDER ,该task就是OpenSpacePlanner的工作。

Hybird A*流程

Hybird A*外部调用入口

代码目录:apollo/modules/planning/tasks/optimizers/open_space_trajectory_generation。代码文件:
(1)open_space_trajectory_provider.h
OpenSpaceTrajectoryProvider类,在OpenSpaceTrajectoryProvider::GenerateTrajectoryThread()中会调用open_space_trajectory_optimizer_->Plan()。
(2)open_space_trajectory_optimizer.h
OpenSpaceTrajectoryOptimizer类,有一个std::unique_ptr warm_start_ 成员变量。在 OpenSpaceTrajectoryOptimizer::Plan()中,会调用 warm_start_->Plan()。

Hybird A*内部流程

  • 碰撞检测
  • ReedShepp曲线加速搜索
  • 扩展相邻的节点
  • 计算节点的代价
  • 路径后处理
    – 路径分割
    – 轨迹平滑,计算动态信息,完善轨迹
    – 计算动态信息,完善轨迹

Hybird A*代码逻辑

主函数Plan

apollo-master\modules\planning\planning_open_space\coarse_trajectory_generator\hybrid_a_star.cc

    bool HybridAStar::Plan(double sx, double sy, double sphi, double ex, double ey, double ephi,
    const std::vector<double>& XYbounds,
    const std::vector<std::vector<common::math::Vec2d>>& obstacles_vertices_vec,
    HybridAStartResult* result,
    const std::vector<std::vector<common::math::Vec2d>>&
        soft_boundary_vertices_vec,
    bool reeds_sheep_last_straight) {
  reed_shepp_generator_->reeds_sheep_last_straight_ = reeds_sheep_last_straight;
  // clear containers
  //每次规划,清空之前的缓存数据
  open_set_.clear();
  close_set_.clear();
  open_pq_ = decltype(open_pq_)();
  final_node_ = nullptr;
  PrintCurves print_curves;
  std::vector<std::vector<common::math::LineSegment2d>>
      obstacles_linesegments_vec;
      
  //构造障碍物轮廓线段
  for (const auto& obstacle_vertices : obstacles_vertices_vec) {
    size_t vertices_num = obstacle_vertices.size();
    std::vector<common::math::LineSegment2d> obstacle_linesegments;
    for (size_t i = 0; i < vertices_num - 1; ++i) {
      common::math::LineSegment2d line_segment =
          common::math::LineSegment2d(
              obstacle_vertices[i], obstacle_vertices[i + 1]);
      obstacle_linesegments.emplace_back(line_segment);
    }
    obstacles_linesegments_vec.emplace_back(obstacle_linesegments);
  }
  obstacles_linesegments_vec_ = std::move(obstacles_linesegments_vec);
  for (size_t i = 0; i < obstacles_linesegments_vec_.size(); i++) {
    for (auto linesg : obstacles_linesegments_vec_[i]) {
      std::string name = std::to_string(i) + "roi_boundary";
      print_curves.AddPoint(name, linesg.start());
      print_curves.AddPoint(name, linesg.end());
    }
  }
  Vec2d sposition(sx, sy);
  Vec2d svec_to_center(
          (vehicle_param_.front_edge_to_center() -
           vehicle_param_.back_edge_to_center()) / 2.0,
          (vehicle_param_.left_edge_to_center() -
           vehicle_param_.right_edge_to_center()) / 2.0);
  Vec2d scenter(sposition + svec_to_center.rotate(sphi));
  Box2d sbox(scenter, sphi, vehicle_param_.length(), vehicle_param_.width());
  print_curves.AddPoint("vehicle_start_box", sbox.GetAllCorners());
  Vec2d eposition(ex, ey);
  print_curves.AddPoint("end_position", eposition);
  Vec2d evec_to_center(
          (vehicle_param_.front_edge_to_center() -
           vehicle_param_.back_edge_to_center()) / 2.0,
          (vehicle_param_.left_edge_to_center() -
           vehicle_param_.right_edge_to_center()) / 2.0);
  Vec2d ecenter(eposition + evec_to_center.rotate(ephi));
  Box2d ebox(ecenter, ephi, vehicle_param_.length(), vehicle_param_.width());
  print_curves.AddPoint("vehicle_end_box", ebox.GetAllCorners());
  //构造规划的起点和终点,并检查其有效性
  XYbounds_ = XYbounds;
// load nodes and obstacles
  start_node_.reset(
      new Node3d({sx}, {sy}, {sphi}, XYbounds_, planner_open_space_config_));
  end_node_.reset(
      new Node3d({ex}, {ey}, {ephi}, XYbounds_, planner_open_space_config_));
  AINFO << "start node" << sx << "," << sy << "," << sphi;
  AINFO << "end node " << ex << "," << ey << "," << ephi;
  if (!ValidityCheck(start_node_)) {
    AERROR << "start_node in collision with obstacles";
    AERROR << start_node_->GetX()
           << "," << start_node_->GetY()
           << "," << start_node_->GetPhi();
    print_curves.PrintToLog();
    return false;
  }
  if (!ValidityCheck(end_node_)) {
    AERROR << "end_node in collision with obstacles";
    print_curves.PrintToLog();
    return false;
  }
  double map_time = Clock::NowInSeconds();
  //使用动态规划DP来计算目标点到某点的启发代价(以目标点为DP的起点),即A*中的H
  //生成graph的同时获得了目标点到图中任一点的cost,作为缓存,这就是DPMap的用处
  grid_a_star_heuristic_generator_->GenerateDpMap(
      ex, ey, XYbounds_, obstacles_linesegments_vec_);
  ADEBUG << "map time " << Clock::NowInSeconds() - map_time;
  // load open set, pq
  open_set_.insert(start_node_->GetIndex());
  open_pq_.emplace(start_node_, start_node_->GetCost());
  // Hybrid A* begins
  size_t explored_node_num = 0;
  size_t available_result_num = 0;
  auto best_explored_num = explored_node_num;
  auto best_available_result_num = available_result_num;
  double astar_start_time = Clock::NowInSeconds();
  double heuristic_time = 0.0;
  double rs_time = 0.0;
  double node_generator_time = 0.0;
  double validity_check_time = 0.0;
  size_t max_explored_num =
      planner_open_space_config_.warm_start_config().max_explored_num();
  size_t desired_explored_num = std::min(
      planner_open_space_config_.warm_start_config().desired_explored_num(),
      planner_open_space_config_.warm_start_config().max_explored_num());
  static constexpr int kMaxNodeNum = 200000;
  std::vector<std::shared_ptr<Node3d>> candidate_final_nodes;
  while (!open_pq_.empty() &&
         open_pq_.size() < kMaxNodeNum &&
         available_result_num < desired_explored_num &&
         explored_node_num < max_explored_num) {
    std::shared_ptr<Node3d> current_node = open_pq_.top().first;
    open_pq_.pop();
    const double rs_start_time = Clock::NowInSeconds();
    std::shared_ptr<Node3d> final_node = nullptr;
     //true:如果生成了一条从当前点到目标点的ReedShepp曲线,就找到了最短路径
    //false:否则,继续Hybrid A*扩展节点
    if (AnalyticExpansion(current_node, &final_node)) {
      if (final_node_ == nullptr ||
          final_node_->GetTrajCost() > final_node->GetTrajCost()) {
        final_node_ = final_node;
        best_explored_num = explored_node_num + 1;
        best_available_result_num = available_result_num + 1;
      }
      available_result_num++;
    }
    explored_node_num++;
    const double rs_end_time = Clock::NowInSeconds();
    rs_time += rs_end_time - rs_start_time;
    close_set_.insert(current_node->GetIndex());

    if (Clock::NowInSeconds() - astar_start_time >
            planner_open_space_config_.warm_start_config()
                .astar_max_search_time() &&
        available_result_num > 0) {
      break;
    }

    size_t begin_index = 0;
    size_t end_index = next_node_num_;
    std::unordered_set<std::string> temp_set;
    for (size_t i = begin_index; i < end_index; ++i) {
      const double gen_node_time = Clock::NowInSeconds();
       //一个grid内的最后一个路径点叫node,该grid内可以有多个路径点,
      //该node的next_node一定在相邻的其他grid内
      std::shared_ptr<Node3d> next_node = Next_node_generator(current_node, i);
      node_generator_time += Clock::NowInSeconds() - gen_node_time;

      // boundary check failure handle
      if (next_node == nullptr) {
        continue;
      }
      // check if the node is already in the close set
      if (close_set_.count(next_node->GetIndex()) > 0) {
        continue;
      }
      // collision check
      const double validity_check_start_time = Clock::NowInSeconds();
      if (!ValidityCheck(next_node)) {
        continue;
      }
      validity_check_time += Clock::NowInSeconds() - validity_check_start_time;
      if (open_set_.count(next_node->GetIndex()) == 0) {
        const double start_time = Clock::NowInSeconds();
        CalculateNodeCost(current_node, next_node);
        const double end_time = Clock::NowInSeconds();
        heuristic_time += end_time - start_time;
        temp_set.insert(next_node->GetIndex());
        open_pq_.emplace(next_node, next_node->GetCost());
      }
    }
    open_set_.insert(temp_set.begin(), temp_set.end());
  }

  if (final_node_ == nullptr) {
    AERROR << "Hybird A* cannot find a valid path";
    print_curves.PrintToLog();
    return false;
  }

  AINFO << "open_pq_.empty()" << (open_pq_.empty() ? "true" : "false");
  AINFO << "open_pq_.size()" << open_pq_.size();
  AINFO << "desired_explored_num" << desired_explored_num;
  AINFO << "min cost is : " << final_node_->GetTrajCost();
  AINFO << "max_explored_num is " << max_explored_num;
  AINFO << "explored node num is " << explored_node_num
        << "available_result_num " << available_result_num;
  AINFO << "best_explored_num is " << best_explored_num
        << "best_available_result_num is " << best_available_result_num;
  AINFO << "cal node time is " << heuristic_time
        << "validity_check_time " << validity_check_time
        << "node_generator_time " << node_generator_time;
  AINFO << "reed shepp time is " << rs_time;
  AINFO << "hybrid astar total time is "
        << Clock::NowInSeconds() - astar_start_time;

  print_curves.AddPoint(
      "rs_point", final_node_->GetXs().front(), final_node_->GetYs().front());
  if (!GetResult(result)) {
    AERROR << "GetResult failed";
    print_curves.PrintToLog();
    return false;
  }
  for (size_t i = 0; i < result->x.size(); i++) {
    print_curves.AddPoint("warm_path", result->x[i], result->y[i]);
  }
  // PrintBox print_box("warm_path_box");
  // for (size_t i = 0; i < result->x.size(); i = i + 5) {
  //   print_box.AddAdcBox(result->x[i], result->y[i], result->phi[i]);
  // }
  // print_box.PrintToLog();
  print_curves.PrintToLog();
  return true;
}

碰撞检测

详见HybridAStar::ValidityCheck

bool HybridAStar::ValidityCheck(std::shared_ptr<Node3d> node) {
...
}

ReedsShepp曲线加速搜索

HybridAStar::AnalyticExpansion()

//尝试使用ReedsShepp曲线连接当前点与目标点,若成功,则Hybrid A*规划完成
//允许返回false;只返回一次true
bool HybridAStar::AnalyticExpansion(
    std::shared_ptr<Node3d> current_node,
    std::shared_ptr<Node3d>* candidate_final_node) {
  std::shared_ptr<ReedSheppPath> reeds_shepp_to_check =
      std::make_shared<ReedSheppPath>();
       //ReedsShepp曲线都是从当前点到终点的
  if (!reed_shepp_generator_->ShortestRSP(current_node, end_node_,
                                          reeds_shepp_to_check)) {
    return false;
  }
   //ReedsShepp曲线段的碰撞检测与越界检测
  if (!RSPCheck(reeds_shepp_to_check)) {
    return false;
  }
  // load the whole RSP as nodes and add to the close set
  //将连接到目标点的一段ReedsShepp曲线封装成node,放入Hybrid A*的集合中
  *candidate_final_node = LoadRSPinCS(reeds_shepp_to_check, current_node);
  return true;
}

扩展相邻的节点

//扩展节点的重点在于把车辆运动学模型的约束考虑进去,根据限定的steering angle 去搜索相邻的grid。
//扩展节点,扩展一个node就是扩展了一个grid,但是会产生多个在同一grid内的路径点
std::shared_ptr<Node3d> HybridAStar::Next_node_generator(
    std::shared_ptr<Node3d> current_node, size_t next_node_index) {
  double steering = 0.0;
  double traveled_distance = 0.0;
  //首先,根据next_node_index与next_node_num_的对比是可以区分运动方向的
  //这里的if-else就是区分运动正反方向讨论的(前进和倒车)
  //其次,车辆在当前的姿态下,既可以向左转、又可以向右转,那么steering angle的取值范围其实是
  //[-max_steer_angle_, max_steer_angle_],在正向或反向下,能取next_node_num_/2个有效值
  //即,把[-max_steer_angle_, max_steer_angle_]分为(next_node_num_/2-1)份
  //所以,steering = 初始偏移量 + 单位间隔 × index
  //steering angle的正负取决于车的转向,而非前进的正反
  if (next_node_index < static_cast<double>(next_node_num_) / 2) {
    steering = -max_steer_angle_ +
               (2 * max_steer_angle_ /
               (static_cast<double>(next_node_num_) / 2 - 1))
               * static_cast<double>(next_node_index);
    traveled_distance = step_size_;
  } else {
    size_t index = next_node_index - next_node_num_ / 2;
    steering = -max_steer_angle_
            + (2 * max_steer_angle_
            / (static_cast<double>(next_node_num_) / 2 - 1))
            * static_cast<double>(index);
    traveled_distance = -step_size_;
  }
  // take above motion primitive to generate a curve driving the car to a
  // different grid
  std::vector<double> intermediate_x;
  std::vector<double> intermediate_y;
  std::vector<double> intermediate_phi;
  double last_x = current_node->GetX();
  double last_y = current_node->GetY();
  double last_phi = current_node->GetPhi();
  intermediate_x.push_back(last_x);
  intermediate_y.push_back(last_y);
  intermediate_phi.push_back(last_phi);
  
  //从当前grid前进到下一个grid,一个grid内可能有多个路径点
  for (size_t i = 0; i < arc_length_ / step_size_; ++i) {
    const double next_phi = last_phi +
                            traveled_distance /
                            vehicle_param_.wheel_base() *
                            std::tan(steering);
    const double next_x = last_x +
                          traveled_distance *
                          std::cos((last_phi + next_phi) / 2.0);
    const double next_y = last_y +
                          traveled_distance *
                          std::sin((last_phi + next_phi) / 2.0);
    intermediate_x.push_back(next_x);
    intermediate_y.push_back(next_y);
     //看车辆运动学模型——自行车模型
    intermediate_phi.push_back(common::math::NormalizeAngle(next_phi));
    last_x = next_x;
    last_y = next_y;
    last_phi = next_phi;
  }
  // check if the vehicle runs outside of XY boundary
  if (intermediate_x.back() > XYbounds_[1] ||
      intermediate_x.back() < XYbounds_[0] ||
      intermediate_y.back() > XYbounds_[3] ||
      intermediate_y.back() < XYbounds_[2]) {
    return nullptr;
  }
  std::shared_ptr<Node3d> next_node = std::shared_ptr<Node3d>(
      new Node3d(intermediate_x, intermediate_y, intermediate_phi, XYbounds_,
                 planner_open_space_config_));
  next_node->SetPre(current_node);
  next_node->SetDirec(traveled_distance > 0.0);
  next_node->SetSteer(steering);
  return next_node;
}

计算节点的代价

// 分别计算路径代价和启发代价,就是A*中的G和H。这里计算启发代价的方法很巧妙,用了DP,请参看 grid_a_star_heuristic_generator_->GenerateDpMap() 处的注释。
void HybridAStar::CalculateNodeCost(
    std::shared_ptr<Node3d> current_node, std::shared_ptr<Node3d> next_node) {
      //A*中走过的轨迹的代价G
  next_node->SetTrajCost(
      current_node->GetTrajCost() + TrajCost(current_node, next_node));
  // evaluate heuristic cost
  double optimal_path_cost = 0.0;
  //A*中从当前点到目标点的启发式代价H,采用了动态规划DP来计算(以目标点为DP的起点)
  optimal_path_cost += HoloObstacleHeuristic(next_node);
  next_node->SetHeuCost(optimal_path_cost);
}

路径后处理

//将Hybrid A*计算的轨迹结果,按照行驶的正反方向切换,分割为数段,分别逆向翻转轨迹点
//然后重新拼接在一起,就是最终可以发布供车行驶的轨迹
bool HybridAStar::GetTemporalProfile(HybridAStartResult* result) {
  std::vector<HybridAStartResult> partitioned_results;
  if (!TrajectoryPartition(*result, &partitioned_results)) {
    AERROR << "TrajectoryPartition fail";
    return false;
  }
  ADEBUG << "PARTION SIZE " << partitioned_results.size();
  //将分段的轨迹拼接起来
  HybridAStartResult stitched_result;
  for (const auto& result : partitioned_results) {
    std::copy(result.x.begin(), result.x.end() - 1,
              std::back_inserter(stitched_result.x));
    std::copy(result.y.begin(), result.y.end() - 1,
              std::back_inserter(stitched_result.y));
    std::copy(result.phi.begin(), result.phi.end() - 1,
              std::back_inserter(stitched_result.phi));
    std::copy(result.v.begin(), result.v.end() - 1,
              std::back_inserter(stitched_result.v));
    std::copy(result.a.begin(), result.a.end(),
              std::back_inserter(stitched_result.a));
    std::copy(result.steer.begin(), result.steer.end(),
              std::back_inserter(stitched_result.steer));
  }
  stitched_result.x.push_back(partitioned_results.back().x.back());
  stitched_result.y.push_back(partitioned_results.back().y.back());
  stitched_result.phi.push_back(partitioned_results.back().phi.back());
  stitched_result.v.push_back(partitioned_results.back().v.back());
  *result = stitched_result;
  return true;
}

路径分割

//将Hybrid A*计算的轨迹结果,按照行驶的正反方向切换,分割为数段,并完善轨迹的静态、动态信息
bool HybridAStar::TrajectoryPartition(
        const HybridAStartResult& result,
        std::vector<HybridAStartResult>* partitioned_result) {
  const auto& x = result.x;
  const auto& y = result.y;
  const auto& phi = result.phi;
  if (x.size() != y.size() || x.size() != phi.size()) {
    AERROR << "states sizes are not equal when do trajectory partitioning of "
              "Hybrid A Star result";
    return false;
  }

  size_t horizon = x.size();
  partitioned_result->clear();
  partitioned_result->emplace_back();
  auto* current_traj = &(partitioned_result->back());
  double heading_angle = phi.front();
  const Vec2d init_tracking_vector(x[1] - x[0], y[1] - y[0]);
  double tracking_angle = init_tracking_vector.Angle();
  bool current_gear =
      std::abs(common::math::NormalizeAngle(tracking_angle - heading_angle))
      <
      (M_PI_2);
      //此时的result只有路径静态信息,x,y,phi
  //将Hybrid A*计算的轨迹结果,按照行驶的正反方向切换,分割为数段
  for (size_t i = 0; i < horizon - 1; ++i) {
    heading_angle = phi[i];
    const Vec2d tracking_vector(x[i + 1] - x[i], y[i + 1] - y[i]);
    tracking_angle = tracking_vector.Angle();
    bool gear =
        std::abs(common::math::NormalizeAngle(tracking_angle - heading_angle)) <
        (M_PI_2);
    if (gear != current_gear) {
      current_traj->x.push_back(x[i]);
      current_traj->y.push_back(y[i]);
      current_traj->phi.push_back(phi[i]);
      partitioned_result->emplace_back();
      current_traj = &(partitioned_result->back());
      current_gear = gear;
    }
    current_traj->x.push_back(x[i]);
    current_traj->y.push_back(y[i]);
    current_traj->phi.push_back(phi[i]);
  }
  current_traj->x.push_back(x.back());
  current_traj->y.push_back(y.back());
  current_traj->phi.push_back(phi.back());

  const auto start_timestamp = std::chrono::system_clock::now();

  // Retrieve v, a and steer from path
  for (auto& result : *partitioned_result) {
     //2种不同的方式获取轨迹动态信息,v,a,steer。区别: 前者用数值优化的方法,后者用相邻点静态信息
    if (FLAGS_use_s_curve_speed_smooth) {
      //使用QP优化方法求frenet系下的轨迹,但是结果只有动态信息 s,v,a,steer
      if (!GenerateSCurveSpeedAcceleration(&result)) {
        AERROR << "GenerateSCurveSpeedAcceleration fail";
        return false;
      }
    } else {
      //根据result中的静态信息x,y,phi,利用相邻点、逐点求动态信息v,a,steer
      if (!GenerateSpeedAcceleration(&result)) {
        AERROR << "GenerateSpeedAcceleration fail";
        return false;
      }
    }
  }

  const auto end_timestamp = std::chrono::system_clock::now();
  std::chrono::duration<double> diff = end_timestamp - start_timestamp;
  ADEBUG << "speed profile total time: " << diff.count() * 1000.0 << " ms.";
  return true;
}

轨迹平滑,计算动态信息,完善轨迹

//使用QP优化方法求frenet系下的轨迹,但是结果只有动态信息 s,v,a,steer
bool HybridAStar::GenerateSCurveSpeedAcceleration(HybridAStartResult* result) {
  AINFO << "GenerateSCurveSpeedAcceleration";
  // sanity check
  CHECK_NOTNULL(result);
  if (result->x.size() < 2 || result->y.size() < 2 || result->phi.size() < 2) {
    AERROR << "result size check when generating speed and acceleration fail";
    return false;
  }
  if (result->x.size() != result->y.size() ||
      result->x.size() != result->phi.size()) {
    AERROR << "result sizes not equal";
    return false;
  }

  // get gear info
  double init_heading = result->phi.front();
  const Vec2d init_tracking_vector(result->x[1] - result->x[0],
                                   result->y[1] - result->y[0]);
  const double gear =
      std::abs(common::math::NormalizeAngle(
          init_heading - init_tracking_vector.Angle())) < M_PI_2;

  // get path lengh
  size_t path_points_size = result->x.size();

  double accumulated_s = 0.0;
  result->accumulated_s.clear();
  auto last_x = result->x.front();
  auto last_y = result->y.front();
  for (size_t i = 0; i < path_points_size; ++i) {
    double x_diff = result->x[i] - last_x;
    double y_diff = result->y[i] - last_y;
    accumulated_s += std::sqrt(x_diff * x_diff + y_diff * y_diff);
    result->accumulated_s.push_back(accumulated_s);
    last_x = result->x[i];
    last_y = result->y[i];
  }
  // assume static initial state
  const double init_v = 0.0;
  const double init_a = 0.0;

  // minimum time speed optimization
  // TODO(Jinyun): move to confs

  SpeedData speed_data;

  // TODO(Jinyun): explore better time horizon heuristic
  const double path_length = result->accumulated_s.back();
  const double total_t =
      std::max(gear ? 1.5 *
                          (max_forward_v_ * max_forward_v_ +
                           path_length * max_forward_acc_) /
                          (max_forward_acc_ * max_forward_v_)
                    : 1.5 *
                          (max_reverse_v_ * max_reverse_v_ +
                           path_length * max_reverse_acc_) /
                          (max_reverse_acc_ * max_reverse_v_),
               10.0);
  if (total_t + delta_t_ >= delta_t_ * std::numeric_limits<size_t>::max()) {
    AERROR << "Number of knots overflow. total_t: " << total_t
           << ", delta_t: " << delta_t_;
    return false;
  }
  const size_t num_of_knots = static_cast<size_t>(total_t / delta_t_) + 1;
 //使用QP优化方法求frenet系下的轨迹
  PiecewiseJerkSpeedProblem piecewise_jerk_problem(
      num_of_knots, delta_t_, {0.0, std::abs(init_v), std::abs(init_a)});

  // set end constraints
  std::vector<std::pair<double, double>> x_bounds(num_of_knots,
                                                  {0.0, path_length});

  const double max_v = gear ? max_forward_v_ : max_reverse_v_;
  const double max_acc = gear ? max_forward_acc_ : max_reverse_acc_;

  const auto upper_dx = std::fmax(max_v, std::abs(init_v));
  std::vector<std::pair<double, double>> dx_bounds(num_of_knots,
                                                   {0.0, upper_dx});
  std::vector<std::pair<double, double>> ddx_bounds(num_of_knots,
                                                    {-max_acc, max_acc});

  x_bounds[num_of_knots - 1] = std::make_pair(path_length, path_length);
  dx_bounds[num_of_knots - 1] = std::make_pair(0.0, 0.0);
  ddx_bounds[num_of_knots - 1] = std::make_pair(0.0, 0.0);

  // TODO(Jinyun): move to confs
  std::vector<double> x_ref(num_of_knots, path_length);
  piecewise_jerk_problem.set_x_ref(ref_s_weight_, std::move(x_ref));
  piecewise_jerk_problem.set_dx_ref(ref_v_weight_, max_v * 0.8);
  piecewise_jerk_problem.set_weight_ddx(acc_weight_);
  piecewise_jerk_problem.set_weight_dddx(jerk_weight_);
  piecewise_jerk_problem.set_x_bounds(std::move(x_bounds));
  piecewise_jerk_problem.set_dx_bounds(std::move(dx_bounds));
  piecewise_jerk_problem.set_ddx_bounds(std::move(ddx_bounds));
  piecewise_jerk_problem.set_dddx_bound(max_acc_jerk_);

  // solve the problem
  if (!piecewise_jerk_problem.Optimize()) {
    AERROR << "Piecewise jerk speed optimizer failed!";
    return false;
  }

  // extract output
  const std::vector<double>& s = piecewise_jerk_problem.opt_x();
  const std::vector<double>& ds = piecewise_jerk_problem.opt_dx();
  const std::vector<double>& dds = piecewise_jerk_problem.opt_ddx();

  // assign speed point by gear
  speed_data.AppendSpeedPoint(s[0], 0.0, ds[0], dds[0], 0.0);
  const double kEpislon = 1.0e-6;
  const double sEpislon = 1.0e-6;
  for (size_t i = 1; i < num_of_knots; ++i) {
    if (s[i - 1] - s[i] > kEpislon) {
      ADEBUG << "unexpected decreasing s in speed smoothing at time "
             << static_cast<double>(i) * delta_t_
             << "with total time " << total_t;
      break;
    }
    speed_data.AppendSpeedPoint(
        s[i], delta_t_ * static_cast<double>(i), ds[i],
        dds[i], (dds[i] - dds[i - 1]) / delta_t_);
    // cut the speed data when it is about to meet end condition
    if (path_length - s[i] < sEpislon) {
      break;
    }
  }

  // combine speed and path profile
  DiscretizedPath path_data;
  for (size_t i = 0; i < path_points_size; ++i) {
    common::PathPoint path_point;
    path_point.set_x(result->x[i]);
    path_point.set_y(result->y[i]);
    path_point.set_theta(result->phi[i]);
    path_point.set_s(result->accumulated_s[i]);
    path_data.push_back(std::move(path_point));
  }

  HybridAStartResult combined_result;

  // TODO(Jinyun): move to confs
  const double kDenseTimeResoltuion = 0.5;
  const double time_horizon = speed_data.TotalTime() +
                              kDenseTimeResoltuion * 1.0e-6;
  if (path_data.empty()) {
    AERROR << "path data is empty";
    return false;
  }
  for (double cur_rel_time = 0.0; cur_rel_time < time_horizon;
       cur_rel_time += kDenseTimeResoltuion) {
    common::SpeedPoint speed_point;
    if (!speed_data.EvaluateByTime(cur_rel_time, &speed_point)) {
      AERROR << "Fail to get speed point with relative time " << cur_rel_time;
      return false;
    }

    if (speed_point.s() > path_data.Length()) {
      break;
    }

    common::PathPoint path_point = path_data.Evaluate(speed_point.s());

    combined_result.x.push_back(path_point.x());
    combined_result.y.push_back(path_point.y());
    combined_result.phi.push_back(path_point.theta());
    combined_result.accumulated_s.push_back(path_point.s());
    if (!gear) {
      combined_result.v.push_back(-speed_point.v());
      combined_result.a.push_back(-speed_point.a());
    } else {
      combined_result.v.push_back(speed_point.v());
      combined_result.a.push_back(speed_point.a());
    }
  }

  combined_result.a.pop_back();

  // recalc step size
  path_points_size = combined_result.x.size();

  // load steering from phi
  for (size_t i = 0; i + 1 < path_points_size; ++i) {
    double discrete_steer =
        (combined_result.phi[i + 1] - combined_result.phi[i]) *
        vehicle_param_.wheel_base() /
        (combined_result.accumulated_s[i + 1] -
         combined_result.accumulated_s[i]);
    discrete_steer =
        gear ? std::atan(discrete_steer) : std::atan(-discrete_steer);
    combined_result.steer.push_back(discrete_steer);
  }

  *result = combined_result;
  return true;
}

计算动态信息,完善轨迹

//使用QP优化方法求frenet系下的轨迹,但是结果只有动态信息 s,v,a,steer
bool HybridAStar::GenerateSpeedAcceleration(
    HybridAStartResult* result) {
  AINFO << "GenerateSpeedAcceleration";
  // Sanity Check
  if (result->x.size() < 2 || result->y.size() < 2 || result->phi.size() < 2) {
    AERROR << "result size check when generating speed and acceleration fail";
    return false;
  }
  const size_t x_size = result->x.size();

  // load velocity from position
  // initial and end speed are set to be zeros
  result->v.push_back(0.0);
  for (size_t i = 1; i + 1 < x_size; ++i) {
    double discrete_v = (((result->x[i + 1] - result->x[i]) / delta_t_) *
                             std::cos(result->phi[i]) +
                         ((result->x[i] - result->x[i - 1]) / delta_t_) *
                             std::cos(result->phi[i])) /
                            2.0 +
                        (((result->y[i + 1] - result->y[i]) / delta_t_) *
                             std::sin(result->phi[i]) +
                         ((result->y[i] - result->y[i - 1]) / delta_t_) *
                             std::sin(result->phi[i])) /
                            2.0;
    result->v.push_back(discrete_v);
  }
  result->v.push_back(0.0);

  // load acceleration from velocity
  for (size_t i = 0; i + 1 < x_size; ++i) {
    const double discrete_a = (result->v[i + 1] - result->v[i]) / delta_t_;
    result->a.push_back(discrete_a);
  }

  // load steering from phi
  for (size_t i = 0; i + 1 < x_size; ++i) {
    double discrete_steer = (result->phi[i + 1] - result->phi[i]) *
                            vehicle_param_.wheel_base() / step_size_;
    if (result->v[i] > 0.0) {
      discrete_steer = std::atan(discrete_steer);
    } else {
      discrete_steer = std::atan(-discrete_steer);
    }
    result->steer.push_back(discrete_steer);
  }
  return true;
}

参考资料:

https://apollo.baidu.com/community/article/4
https://blog.csdn.net/linxigjs/article/details/103419697

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值