天天看点

Baidu Apollo代码解析之EM Planner中的QP Speed Optimizer 2

大家好,我已经把CSDN上的博客迁移到了知乎上,欢迎大家在知乎关注我的专栏慢慢悠悠小马车(https://zhuanlan.zhihu.com/duangduangduang)。希望大家可以多多交流,互相学习。

上文添加优化问题的约束后,本文接着讲如何添加目标函数,即QpSplineStGraph::AddKernel()函数。这部分的代码比较抽象,但是底层逻辑与QP Path Optimizer是相同的,类和函数也是相同的,大家可以参考Baidu Apollo代码解析之EM Planner中的QP Path Optimizer 3 来理解下面的代码。

当然,对于speed的优化,Apollo使用了不同的目标函数,分为7部分,在代码注释中说明。

Status QpSplineStGraph::AddKernel(
    const std::vector<const STBoundary*>& boundaries,
    const SpeedLimit& speed_limit) {
  Spline1dKernel* spline_kernel = spline_solver_->mutable_spline_kernel();

  if (qp_st_speed_config_.qp_spline_config().accel_kernel_weight() > 0) {
    //1、优化二阶导,即减小加速度
    spline_kernel->AddSecondOrderDerivativeMatrix(
        qp_st_speed_config_.qp_spline_config().accel_kernel_weight());
  }

  if (qp_st_speed_config_.qp_spline_config().jerk_kernel_weight() > 0) {
    //2、优化三阶导,即减小jerk
    spline_kernel->AddThirdOrderDerivativeMatrix(
        qp_st_speed_config_.qp_spline_config().jerk_kernel_weight());
  }

  //3、优化目标:cruise对应的参考速度,即以最短时间、最快速度行驶
  if (!AddCruiseReferenceLineKernel(
           qp_st_speed_config_.qp_spline_config().cruise_weight()).ok()) {
    return Status(ErrorCode::PLANNING_ERROR, "QpSplineStGraph::AddKernel");
  }

  //4、优化目标:follow决策的参考速度
  if (!AddFollowReferenceLineKernel(
           boundaries, qp_st_speed_config_.qp_spline_config().follow_weight()).ok()) {
    return Status(ErrorCode::PLANNING_ERROR, "QpSplineStGraph::AddKernel");
  }

  //5、优化目标:yield决策的参考速度
  if (!AddYieldReferenceLineKernel(
           boundaries, qp_st_speed_config_.qp_spline_config().yield_weight()).ok()) {
    return Status(ErrorCode::PLANNING_ERROR, "QpSplineStGraph::AddKernel");
  }

  //6、优化目标:EM的DP输出的参考速度
  if (!AddDpStReferenceKernel(
          qp_st_speed_config_.qp_spline_config().dp_st_reference_weight())) {
    return Status(ErrorCode::PLANNING_ERROR, "QpSplineStGraph::AddKernel");
  }

  // init point jerk continuous kernel 看不懂??
  (*spline_kernel->mutable_kernel_matrix())(2, 2) += 2.0 * 4.0 *
      qp_st_speed_config_.qp_spline_config().init_jerk_kernel_weight();
  (*spline_kernel->mutable_offset())(2, 0) += -4.0 * init_point_.a() *
      qp_st_speed_config_.qp_spline_config().init_jerk_kernel_weight();

  //7、正则项
  spline_kernel->AddRegularization(
      qp_st_speed_config_.qp_spline_config().regularization_weight());

  return Status::OK();
}
           

我将目标函数分为3类讨论。每一类中的不同的Kernel,套路是一样的。

1. 对n阶导的优化

指第1、2条。先调用Spline1dKernel::AddNthDerivativekernelMatrix(),再调用SplineSegKernel::NthDerivativeKernel(),与Baidu Apollo代码解析之EM Planner中的QP Path Optimizer 3 中的介绍一致,不再赘述。

2. 对不同来源的参考速度的优化

指第3~6条。对于某种场景(如cruise)、某种决策(如follow、yield)、某历史数据(如DP规划的speed),使现在的规划尽量向其靠近,最小化两者之间的差别。内层都是调用了Spline1dKernel::AddReferenceLineKernelMatrix()。这里与地图中的reference line 没有太大关系,而是指提供的先验数据本身便是一种reference,只是先验数据的获取方式不同、预处理的步骤不同。

第3条:

Status QpSplineStGraph::AddCruiseReferenceLineKernel(const double weight) {
  ...
  double dist_ref = total_path_length_;
  for (uint32_t i = 0; i < t_evaluated_.size(); ++i) {
    cruise_.push_back(dist_ref);
  }
  ...
  if (t_evaluated_.size() > 0) {
    //cruise_中的元素都是total_path_length_
    //即希望以最短时间到达total_path_length_
    spline_kernel->AddReferenceLineKernelMatrix(t_evaluated_, cruise_,
        weight * total_time_ / static_cast<double>(t_evaluated_.size()));
  }
  return Status::OK();
}
           

第4条: 

Status QpSplineStGraph::AddFollowReferenceLineKernel(
    const std::vector<const STBoundary*>& boundaries, const double weight) {
  ...  
  for (size_t i = 0; i < t_evaluated_.size(); ++i) {
    const double curr_t = t_evaluated_[i];
    ...
    for (const auto* boundary : boundaries) {
      ... 
      if (boundary->GetUnblockSRange(curr_t, &s_upper, &s_lower)) {
        success = true;
        //s_min表示自车与前方最近障碍物可保持的follow距离
        s_min = std::min(s_min, s_upper - boundary->characteristic_length() -
                qp_st_speed_config_.qp_spline_config().follow_drag_distance());
      }
    }
    if (success && s_min < cruise_[i]) {
      filtered_evaluate_t.push_back(curr_t);
      ref_s.push_back(s_min);
      ...
    }
  } 

  //优化目标是尽可能按照follow决策对应的<filtered_evaluate_t, ref_s>
  //这种状态行驶,相当于给了参考速度
  if (!ref_s.empty()) {
    spline_kernel->AddReferenceLineKernelMatrix(filtered_evaluate_t, ref_s,
        weight * total_time_ / static_cast<double>(t_evaluated_.size()));
  }
  ...
}
           

第5条:

Status QpSplineStGraph::AddYieldReferenceLineKernel(
    const std::vector<const STBoundary*>& boundaries, const double weight) {
  ...
  for (size_t i = 0; i < t_evaluated_.size(); ++i) {
    const double curr_t = t_evaluated_[i];
    ...
    for (const auto* boundary : boundaries) {
      ...
      if (boundary->GetUnblockSRange(curr_t, &s_upper, &s_lower)) {
        success = true;
        //s_min表示自车与前方最近障碍物可保持的yield距离
        s_min = std::min(s_min, s_upper - boundary->characteristic_length() -
                qp_st_speed_config_.qp_spline_config().yield_drag_distance());
      }
    }
    if (success && s_min < cruise_[i]) {
      filtered_evaluate_t.push_back(curr_t);
      ref_s.push_back(s_min);
    }
  }

  //优化目标是尽可能按照yield决策对应的<filtered_evaluate_t, ref_s>
  //这种状态行驶,相当于给了参考速度
  if (!ref_s.empty()) {
    spline_kernel->AddReferenceLineKernelMatrix(filtered_evaluate_t, ref_s,
        weight * total_time_ / static_cast<double>(t_evaluated_.size()));
  }
  ...
}
           

 第6条:

bool QpSplineStGraph::AddDpStReferenceKernel(const double weight) const {
  std::vector<double> t_pos;
  std::vector<double> s_pos;
  for (auto point : reference_dp_speed_points_) {
    t_pos.push_back(point.t());
    s_pos.push_back(point.s());
  }
  auto* spline_kernel = spline_solver_->mutable_spline_kernel();
  if (!t_pos.empty()) {
    spline_kernel->AddReferenceLineKernelMatrix(
        t_pos, s_pos, weight * total_time_ / static_cast<double>(t_pos.size()));
  }
  return true;
}
           

共同点: 

//优化目标是尽可能按照对应的<x_coord, ref_x> 这种状态行驶
//相当于给了参考目标,如速度规划时的<t,s>,即希望达到的参考速度
bool Spline1dKernel::AddReferenceLineKernelMatrix(
    const std::vector<double>& x_coord, const std::vector<double>& ref_x,
    const double weight) { ... }
           

3. 正则项

指第7条。

void Spline1dKernel::AddRegularization(const double regularized_param) {
  Eigen::MatrixXd id_matrix =
      Eigen::MatrixXd::Identity(kernel_matrix_.rows(), kernel_matrix_.cols());
  kernel_matrix_ += 2.0 * id_matrix * regularized_param;
}
           

继续阅读