Apollo规划代码ros移植-动态障碍物处理(一)

Apollo规划代码ros移植-动态障碍物处理(一)

工程代码看这篇:Apollo6.0规划代码ros移植-路径规划可跑工程分享
关于动态障碍物的处理,目前我认为主要有三种反映:
(1)超车
(2)跟随(减速避让)
(3)停车
目前,在模拟障碍物时,我们提前生成障碍物的行使轨迹点。然后设置一定的速度让障碍物移动。目前通过sleep的方式控制车的移动速度,后面完善一下。还有,目前先学习Lattice规划对动态障碍物的处理。需要说明一下的事情,只能用Lattice采样规划,因为Lattice二次规划在代码里面只用static_obs_sl_boundaries_进行处理,说明只针对静态障碍物。

一.超车
从代码来看,Apollo在巡航模式的纵向轨迹采样后面接了障碍物的ST图采样:

//产生纵向轨迹,即速度规划
void Trajectory1dGenerator::GenerateLongitudinalTrajectoryBundle(const PlanningTarget &planning_target,
                                                                 Trajectory1DBundle *ptr_lon_trajectory_bundle) const
{
  // 巡航轨迹是无规则规划的,不考虑障碍物
  GenerateSpeedProfilesForCruising(planning_target.cruise_speed(), ptr_lon_trajectory_bundle); //巡航

  GenerateSpeedProfilesForPathTimeObstacles(ptr_lon_trajectory_bundle); //超车或者跟随前车

  if (planning_target.has_stop_point()) //停车
  {
    GenerateSpeedProfilesForStopping(planning_target.stop_point(), ptr_lon_trajectory_bundle);
  }
}

GenerateSpeedProfilesForPathTimeObstacles函数如下:

void Trajectory1dGenerator::GenerateSpeedProfilesForPathTimeObstacles(Trajectory1DBundle *ptr_lon_trajectory_bundle) const
{
  auto end_conditions = end_condition_sampler_.SampleLonEndConditionsForPathTimePoints();
  if (end_conditions.empty())
  {
    return;
  }
  GenerateTrajectory1DBundle<5>(init_lon_state_, end_conditions, ptr_lon_trajectory_bundle);
}

采样的时候分两种,即超车和跟随采样:

std::vector<SamplePoint> EndConditionSampler::QueryPathTimeObstacleSamplePoints() const
{
  std::vector<SamplePoint> sample_points;

  for (const auto &path_time_obstacle : ptr_path_time_graph_->GetPathTimeObstacles()) //遍历障碍物的ST信息
  {
    std::string obstacle_id = path_time_obstacle.id();
    QueryFollowPathTimePoints(obstacle_id, &sample_points);   //跟随采样
    QueryOvertakePathTimePoints(obstacle_id, &sample_points); //超车采样
  }
  return sample_points;
}

这里的处理我觉得很奇怪,那是要跟随呢还是超车呢,我在想是不是需要有一个决策,例如EM里面的STBoundary有下面的类型:

  enum class BoundaryType
  {
    UNKNOWN,
    STOP,
    FOLLOW,
    YIELD,
    OVERTAKE,
    KEEP_CLEAR,
  };

根据这些类型来决定如何采样才是清晰的思路。好的,先按照源码的结构来。
超车采样函数:

//超车采样
void EndConditionSampler::QueryOvertakePathTimePoints(const std::string &obstacle_id, std::vector<SamplePoint> *sample_points) const
{
  // 获取障碍物周围点的ST
  std::vector<STPoint> overtake_path_time_points =
      ptr_path_time_graph_->GetObstacleSurroundingPoints(
          obstacle_id, Config_.FLAGS_numerical_epsilon, Config_.FLAGS_time_min_density);
  //取点
  for (const auto &path_time_point : overtake_path_time_points)
  {
    double v = ptr_prediction_querier_->ProjectVelocityAlongReferenceLine(obstacle_id, path_time_point.s(), path_time_point.t());
    SamplePoint sample_point;
    sample_point.path_time_point = path_time_point;
    sample_point.path_time_point.set_s(path_time_point.s() + Config_.FLAGS_default_lon_buffer);
    sample_point.ref_v = v;
    sample_points->push_back(std::move(sample_point));
  }
}

采样原理其实不难理解,就是在取ST图的上面的边界点,每个FLAGS_time_min_density取一个。接着求出映射参考线的速度。最后取边界点往上距FLAGS_default_lon_buffer的点。图可以看这个大佬的解析:
Apollo6.0代码Lattice算法详解——Part5: 生成横纵向轨迹,下面图片取自这位大佬。
在这里插入图片描述

二.跟随(减速避让)
这里的跟随和减速避让应该是有差别的,但是好像采样方式一样,采样原理看上面那个图。代码如下:

//跟车采样
void EndConditionSampler::QueryFollowPathTimePoints(const std::string &obstacle_id, std::vector<SamplePoint> *const sample_points) const
{
  // 获取障碍物周围点的ST
  std::vector<STPoint> follow_path_time_points =
      ptr_path_time_graph_->GetObstacleSurroundingPoints(obstacle_id, -Config_.FLAGS_numerical_epsilon, Config_.FLAGS_time_min_density);
      
  // for循环遍历ST下边界点
  for (const auto &path_time_point : follow_path_time_points)
  {
    // 沿参考线速度投影,求出障碍物的速度在参考线方向的分量
    double v = ptr_prediction_querier_->ProjectVelocityAlongReferenceLine(
        obstacle_id, path_time_point.s(), path_time_point.t());

    // Generate candidate s
    double s_upper = path_time_point.s() - Config_.front_edge_to_center;
    double s_lower = s_upper - Config_.FLAGS_default_lon_buffer;
    // CHECK_GE(FLAGS_num_sample_follow_per_timestamp, 2);

    double s_gap = Config_.FLAGS_default_lon_buffer /
                   static_cast<double>(Config_.FLAGS_num_sample_follow_per_timestamp - 1);

    // 三个点,从s_lower开始,包括s_lower,每隔 s_gap m取一个点
    for (size_t i = 0; i < Config_.FLAGS_num_sample_follow_per_timestamp; ++i)
    {
      double s = s_lower + s_gap * static_cast<double>(i);
      SamplePoint sample_point;
      sample_point.path_time_point = path_time_point;
      sample_point.path_time_point.set_s(s);
      sample_point.ref_v = v;
      sample_points->push_back(std::move(sample_point));
    }
  }
}

三.停车
停车简单,距离障碍物很近自动停下,注意这里停车,不是指停车规划(停到目标位置),而是一种行使反映。

四.动态障碍物预测
以下是我仿照的预测轨迹。通过设定预测时间,可以求出预测轨迹的每个轨迹点的参数。

//产生pre_timepre_time秒的预测
prediction::Ob_Trajectory Obstacle_avoid::Generater_Trajectory(geometry_msgs::Pose ob_pos, double pre_time, double obstacle_threa, double obstacle_velocity)
{
  prediction::Ob_Trajectory result;
  std::vector<TrajectoryPoint> Trajectories;
  Eigen::MatrixXd ob_points;
  ob_points = Eigen::MatrixXd::Zero(2, 3); //初始化零矩阵
  ob_points(0, 0) = ob_pos.position.x;
  ob_points(0, 1) = ob_pos.position.y;
  ob_points(0, 2) = 0;

  ob_points(1, 0) = ob_pos.position.x + pre_time * cos(obstacle_threa);
  ob_points(1, 1) = ob_pos.position.y + pre_time * sin(obstacle_threa);
  ob_points(1, 2) = 0;

  Eigen::MatrixXd path_point_after_interpolation;
  average_interpolation(ob_points, path_point_after_interpolation, pre_time); //线性插值

  double s = 0.0;
  double prev_x = 0.0;
  double prev_y = 0.0;
  double relative_time = 0.0;
  bool empty_path = true;
  std::vector<double> headings;
  std::vector<double> kappas;
  std::vector<double> dkappas;
  std::vector<double> accumulated_s;
  std::vector<std::pair<double, double>> xy_points;

  for (size_t i = 0; i < path_point_after_interpolation.rows(); i++)
  {
    xy_points.emplace_back(path_point_after_interpolation(i, 0), path_point_after_interpolation(i, 1));
  }

  if (!PathMatcher::ComputePathProfile(xy_points, &headings, &accumulated_s, &kappas, &dkappas))
  {
    ROS_WARN("obstacle prediction trajectory generate failed!");
  }
  // std::cout << "path_point_after_interpolation.rows():" << path_point_after_interpolation.rows() << "\n";

  for (int i = 0; i < path_point_after_interpolation.rows(); i++)//遍历每个预测轨迹点
  {
    TrajectoryPoint tra_;
    tra_.set_x(path_point_after_interpolation(i, 0)); // x
    tra_.set_y(path_point_after_interpolation(i, 1)); // y
    tra_.set_theta(headings[i]);
    tra_.set_v(obstacle_velocity); //假设未来匀速
    tra_.set_a(0);                 //匀速,那么a=0
    tra_.set_kappa(kappas[i]);
    tra_.set_dkappa(dkappas[i]);
    tra_.set_s(accumulated_s[i]);

    tra_.set_relative_time(relative_time);

    Trajectories.push_back(tra_);

    relative_time += Config_.FLAGS_trajectory_time_resolution;
  }

  result.Set_Trajectory(Trajectories);
  return result;
}

可看课程系列:
https://www.bilibili.com/video/BV1j94y1B7Ai/?buvid=XY4BB7D48AA71766FB7E422428AB2162768E4&is_story_h5=false&mid=vLcmcVJZwLVM5M3cErNrKw%3D%3D&plat_id=240&share_from=ugc&share_medium=android&share_plat=android&share_source=QQ&share_tag=s_i&timestamp=1689339850&unique_k=CP6TAse&up_id=300556577

需要代码可私信。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

夏融化了这季节

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

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

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

打赏作者

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

抵扣说明:

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

余额充值