Planning-Apollo速度决策规划

Apollo采用路径规划和速度规划解耦的方式,先进行路径规划,在得到的规划路径上再进行速度规划。利用ST Graph,将障碍物、限速等投影在ST图上,利用全局搜索方法DP算法得到决策,在利用最优化方法进行速度规划。

1. 算法原理

百度已经将算法原理发表在论文《Optimal Trajectory Generation for Autonomous Vehicles Under Centripetal Acceleration Constraints for In-lane Driving Scenarios》中。

1.1 优化模型离散方式

在处理最优化问题时,一般会转化成离散形式,将轨迹 s ( t ) s(t) s(t)按照某参数离散,并计算离散点处的约束和 c o s t cost cost。不同的离散方式会构建不同的优化模型。对于速度规划问题,一般可以按照等间距离散(Spatial Parameter Discretization)和等时间离散(Temporal Parameter Discretization)

1.1.1 Spatial Parameter Discretization

使用纵向距离 s s s作为离散采样参数,假设第一个采样点 s = 0 s=0 s=0,连续采样离散点之间等间隔 Δ s \Delta s Δs离散,那么优化问题的决策变量为每个离散点的 s ˙ , s ¨ \dot{s},\ddot{s} s˙,s¨和相邻采样点之间的时间间隔 Δ t \Delta t Δt

Planning-Apollo速度决策规划_第1张图片

优点

  • 参考线或者规划路径的曲率 κ \kappa κ是纵向距离 s s s的函数,因此向心加速度约束是容易处理的;
  • 地图限速等速度约束是和纵向距离 s s s相关的,那么优化问题的 s ˙ \dot{s} s˙约束容易计算;

缺点

  • 优化问题的目标函数难以建模。Apollo规划算法用来优化纵向冲击度最小,冲击度使用相邻两个离散点的差分计算,如果采用 Δ s \Delta s Δs离散会使决策变量之间有除法运算,形成非凸优化问题;
  • 难以施加安全约束。障碍物一般投影在ST Graph中,会得到每个时刻下的安全行驶范围 ( s m i n , s m a x ) (s_{min},s_{max}) (smin,smax),即可行驶区域 s f r e e ( t ) s_{free}(t) sfree(t)是时间 t t t的函数。

1.1.2 Temporal Parameter Discretization

使用时间 t t t作为离散采样参数,假设第一个采样点 t = 0 t=0 t=0,连续采样离散点之间等间隔 Δ t \Delta t Δt离散,那么优化问题的决策变量为每个离散点的 s , s ˙ , s ¨ s,\dot{s},\ddot{s} s,s˙,s¨

Planning-Apollo速度决策规划_第2张图片

使用等时间间隔离散的方式,和使用等距离离散方式的优缺点恰好相反。

优点

  • 优化模型可以保持凸包性质,冲击度的计算仍然是线性形式;
  • 障碍物的安全约束容易施加在优化模型中;

缺点

  • 由于曲率是 s s s的函数,因此向心加速度约束难以处理;
  • 速度约束难以处理;
  • 最小任务完成时间的优化目标难以处理;

处理方法

  • 使用规划路径的最大曲率计算向心加速度限制;
  • 将离散点处的位置 s s sDP得到纵向位置 s s s的差作为目标函数的一部分,使用DP结果在采样时刻t处的s处对应的速度限值;
  • 将离散采样点处的速度 s ˙ \dot{s} s˙与目标速度(reference speed)的差作为目标函数的一部分。以保证完成任务花费的时间最少;

此外,Apollo还设计了非线性速度规划来处理以上问题。

1.2 优化问题建模

1.2.1 cost function

J = w s ¨ ∗ ∑ i = 0 n − 1 s ¨ i 2 + w s ( 3 ) ∗ ∑ i = 0 n − 2 ( s ( 3 ) ) i → i + 1 2 + w a c ∗ ∑ i = 0 n − 1 s ˙ i 2 ∗ κ ( s i ) + w s r e f ∗ ∑ i = 0 n − 1 ( s i − s i , r e f ) 2 + w s ˙ r e f ∗ ∑ i = 0 n − 1 ( s i ˙ − s ˙ i , r e f ) 2 + w s t a s k ∗ ( s n − 1 − s t a s k ) 2 + w s ˙ t a s k ∗ ( s ˙ n − 1 − s ˙ t a s k ) 2 + w s ¨ t a s k ∗ ( s ¨ n − 1 − s ¨ t a s k ) 2 (1-1) J = w_{\ddot{s}} * \sum^{n-1}_{i=0}{\ddot{s}^2_{i}} + w_{{s}^{(3)}} * \sum^{n-2}_{i=0}{({s}^{(3)})^2_{i \rightarrow i+1}} + w_{a_{c}} * \sum^{n-1}_{i=0}{\dot{s}^2_{i} * \kappa (s_{i})} \\ + w_{s_{ref}} * \sum^{n-1}_{i=0}({s_{i} - s_{i,ref})^2} + w_{\dot{s}_{ref}} * \sum^{n-1}_{i=0}{(\dot{s_{i}} - \dot{s}_{i,ref})^2} \\ + w_{s_{task}} * ({s_{n-1} - s_{task})^2} + w_{\dot{s}_{task}} * (\dot{s}_{n-1} - \dot{s}_{task})^2 + w_{\ddot{s}_{task}} * (\ddot{s}_{n-1} - \ddot{s}_{task})^2 \tag{1-1} J=ws¨i=0n1s¨i2+ws(3)i=0n2(s(3))ii+12+waci=0n1s˙i2κ(si)+wsrefi=0n1(sisi,ref)2+ws˙refi=0n1(si˙s˙i,ref)2+wstask(sn1stask)2+ws˙task(s˙n1s˙task)2+ws¨task(s¨n1s¨task)2(1-1)

其中,
s i → i + 1 ( 3 ) = s ¨ i + 1 − s ¨ i Δ s t (1-2) {s}^{(3)}_{i \rightarrow i+1} = \frac{\ddot{s}_{i+1} - \ddot{s}_{i}}{\Delta st} \tag{1-2} sii+1(3)=Δsts¨i+1s¨i(1-2)

1.2.2 constraints

不等式约束:
s i ∈ s f r e e ( i ∗ Δ t ) s ˙ i ∈ [ 0 , s ˙ m a x ] s ¨ i ∈ [ s ¨ m i n , s ¨ m a x ] s i → i + 1 ( 3 ) ∈ [ s m i n ( 3 ) , s m a x ( 3 ) ] a c i ∈ [ − a c m a x , a c m a x ] , a c i = s ˙ i 2 ∗ κ ( s i ) (1-3) s_i \in s_{free}(i * \Delta t) \\ \dot{s}_{i} \in [0, \dot{s}_{max}] \\ \ddot{s}_{i} \in [\ddot{s}_{min}, \ddot{s}_{max}] \\ {s}^{(3)}_{i \rightarrow i+1} \in [{s}^{(3)}_{min}, {s}^{(3)}_{max}] \\ a_{c_i} \in [-a_{c_{max}}, a_{c_{max}}], a_{c_i} = \dot{s}^2_{i} * \kappa(s_{i}) \tag{1-3} sisfree(iΔt)s˙i[0,s˙max]s¨i[s¨min,s¨max]sii+1(3)[smin(3),smax(3)]aci[acmax,acmax],aci=s˙i2κ(si)(1-3)
等式约束:
s ¨ i + 1 = s ¨ i + ∫ 0 Δ t s i → i + 1 ( 3 ) d t = s ¨ i + s i → i + 1 ( 3 ) × Δ t s ˙ i + 1 = s ˙ i + ∫ 0 Δ t s ¨ i → i + 1 d t = s ˙ i + s ¨ i × Δ t + 1 2 s i → i + 1 ( 3 ) × Δ t 2 s i + 1 = s i + ∫ 0 Δ t s ˙ i → i + 1 d t = s l i + s ˙ i × Δ t + 1 2 s ¨ i × Δ t 2 + 1 6 s i → i + 1 ( 3 ) × Δ t 3 (1-4) \begin{aligned} \ddot{s}_{i+1} &= \ddot{s}_{i} + \int^{\Delta t}_{0} {{s}^{(3)}_{i \rightarrow i+1}} dt = \ddot{s}_{i} +{s}^{(3)}_{i \rightarrow i+1} \times \Delta t \\ \dot{s}_{i+1} &= \dot{s}_{i} + \int^{\Delta t}_{0} {\ddot{s}_{i \rightarrow i+1}} dt = \dot{s}_{i} + \ddot{s}_{i} \times \Delta t + \frac{1}{2} {s}^{(3)}_{i \rightarrow i+1} \times {\Delta t}^2 \\ s_{i+1} &= s_{i} + \int^{\Delta t}_{0} {\dot{s}_{i \rightarrow i+1}} dt = sl_{i} + \dot{s}_{i} \times \Delta t + \frac{1}{2} \ddot{s}_{i} \times {\Delta t}^2 + \frac{1}{6} {s}^{(3)}_{i \rightarrow i+1} \times {\Delta t}^3 \end{aligned} \tag{1-4} s¨i+1s˙i+1si+1=s¨i+0Δtsii+1(3)dt=s¨i+sii+1(3)×Δt=s˙i+0Δts¨ii+1dt=s˙i+s¨i×Δt+21sii+1(3)×Δt2=si+0Δts˙ii+1dt=sli+s˙i×Δt+21s¨i×Δt2+61sii+1(3)×Δt3(1-4)

2. ST Graph

ST Graph即是以时间 t t t为横轴,以纵向位移 s s s为纵轴的直角坐标系,将PredictionObstaclesSpeed limit等投影在坐标系上,计算出ADC在每个采样时间 t s a m p l e t_{sample} tsample上的 s , s ˙ {s,\dot{s}} s,s˙约束边界。class StGraphData的成员变量有st_boundaries_st_drivable_boundary_,两者的区别是st_boundaries_是障碍物在ST图上的投影区域,是ADC的不可通行区域,如下图中的浅黄色区域。st_drivable_boundary_是ADC在ST图中的可通行区域,如下图中的浅蓝色区域。

Planning-Apollo速度决策规划_第3张图片

lanw_follow_config.pb.txt的默认配置中,会分别计算st_boundaries_st_drivable_boundary_,但是具体使用哪个boundary计算ADC的可行驶区域会通过FLAGS_use_st_drivable_boundary进行控制。例如在DP过程中计算障碍物的cost:

// dp_st_cost.cc
double DpStCost::GetObstacleCost(const StGraphPoint& st_graph_point) {
  const double s = st_graph_point.point().s();
  const double t = st_graph_point.point().t();

  double cost = 0.0;
  // default: FLAGS_use_st_drivable_boundary = false
  // d判断StGraphPoint是否在可行驶范围内
  if (FLAGS_use_st_drivable_boundary) {
    // TODO(Jiancheng): move to configs
    static constexpr double boundary_resolution = 0.1;
    int index = static_cast(t / boundary_resolution);
    const double lower_bound =
        st_drivable_boundary_.st_boundary(index).s_lower();
    const double upper_bound =
        st_drivable_boundary_.st_boundary(index).s_upper();

    if (s > upper_bound || s < lower_bound) {
      return kInf;
    }
  }
  // 遍历所有的obstacle的cost求和
  for (const auto* obstacle : obstacles_) {
    // Not applying obstacle approaching cost to virtual obstacle like created
    // stop fences
    if (obstacle->IsVirtual()) {
      continue;
    }

    // Stop obstacles are assumed to have a safety margin when mapping them out,
    // so repelling force in dp st is not needed as it is designed to have adc
    // stop right at the stop distance we design in prior mapping process
    if (obstacle->LongitudinalDecision().has_stop()) {
      continue;
    }

    auto boundary = obstacle->path_st_boundary();

    if (boundary.min_s() > FLAGS_speed_lon_decision_horizon) {
      continue;
    }
    if (t < boundary.min_t() || t > boundary.max_t()) {
      continue;
    }
    // 碰撞的cost为无穷大
    if (boundary.IsPointInBoundary(st_graph_point.point())) {
      return kInf;
    }
    double s_upper = 0.0;
    double s_lower = 0.0;
    // 计算不碰撞时的cost
    int boundary_index = boundary_map_[boundary.id()];
    // 尚未计算obstacle在时间t处的上下边界,此时调用GetBoundarySRange计算边界,并储存起来,下次就不用计算了
    if (boundary_cost_[boundary_index][st_graph_point.index_t()].first < 0.0) {
      boundary.GetBoundarySRange(t, &s_upper, &s_lower);
      boundary_cost_[boundary_index][st_graph_point.index_t()] =
          std::make_pair(s_upper, s_lower);
    } else {
      s_upper = boundary_cost_[boundary_index][st_graph_point.index_t()].first;
      s_lower = boundary_cost_[boundary_index][st_graph_point.index_t()].second;
    }
    if (s < s_lower) {
      // proto default: safe_distance = 20.0
      const double follow_distance_s = config_.safe_distance();
      if (s + follow_distance_s < s_lower) {
        // 距离障碍物较远,cost = 0
        continue;
      } else {
        // 距离障碍物较近,越近cost越大
        auto s_diff = follow_distance_s - s_lower + s;
        // default: obstacle_weight: 1.0, proto default: default_obstacle_cost = 1e10
        cost += config_.obstacle_weight() * config_.default_obstacle_cost() *
                s_diff * s_diff;
      }
    } else if (s > s_upper) {
      const double overtake_distance_s =
          StGapEstimator::EstimateSafeOvertakingGap();  // default: 20.0m
      if (s > s_upper + overtake_distance_s) {  // or calculated from velocity
        // 越过obstacle较远,cost = 0
        continue;
      } else {
        // 距离障碍物越近cost越大
        auto s_diff = overtake_distance_s + s_upper - s;
        cost += config_.obstacle_weight() * config_.default_obstacle_cost() *
                s_diff * s_diff;
      }
    }
  }
  return cost * unit_t_;
}

以下为StGraphData、STBoundary、STDrivableBoundary的定义:

// st_graph_data.h
class StGraphData {
 public:
  StGraphData() = default;
  // ...
 private:
  // ...
  std::vector st_boundaries_;
  STDrivableBoundary st_drivable_boundary_;
};
class STBoundary : public common::math::Polygon2d {
 public:
  enum class BoundaryType {
    UNKNOWN,
    STOP,
    FOLLOW,
    YIELD,
    OVERTAKE,
    KEEP_CLEAR,
  };
  // ...
 private:
  BoundaryType boundary_type_ = BoundaryType::UNKNOWN;  // boundary type,可以看做是纵向决策

  std::vector upper_points_;
  std::vector lower_points_;

  std::string id_;  // obstacle ID
  double characteristic_length_ = 1.0;  // ??? 
  double min_s_ = std::numeric_limits::max();
  double max_s_ = std::numeric_limits::lowest();
  double min_t_ = std::numeric_limits::max();
  double max_t_ = std::numeric_limits::lowest();

  STPoint bottom_left_point_;
  STPoint bottom_right_point_;
  STPoint upper_left_point_;
  STPoint upper_right_point_;
  // ...
};
syntax = "proto2";

package apollo.planning;

message STDrivableBoundaryInstance {
  optional double t = 1;
  optional double s_lower = 2;
  optional double s_upper = 3;
  optional double v_obs_lower = 4;
  optional double v_obs_upper = 5;
}

message STDrivableBoundary {
  repeated STDrivableBoundaryInstance st_boundary = 1;
}

3. 代码实现

Apollo中速度决策规划的实现流程如下:

Planning-Apollo速度决策规划_第4张图片

3.1 STBoundsDecider

STBoundsDecider是来计算ST Graphst_drivable_boundary_。由三个模块的计算组成:

  • 车辆动力学约束计算得到的 s m i n , s m a x s_{min}, s_{max} smin,smax
  • 由参考速度行驶得到的 s m i n , s m a x s_{min}, s_{max} smin,smax
  • 由障碍物计算得到的可行驶区域 s m i n , s m a x s_{min}, s_{max} smin,smax

3.1.1 车辆动力学约束

其计算依据为车辆运动学公式:
s = s 0 + v 0 t + 1 2 a t 2 (3-1) s = s_{0} + v_{0}t + \frac{1}{2} a t^2 \tag{3-1} s=s0+v0t+21at2(3-1)
以下为计算ADC动力学运动范围的代码实现:

// st_driving_limits.cc
// 根据adc的最大动力学边界计算s的范围
std::pair STDrivingLimits::GetVehicleDynamicsLimits(
    const double t) const {
  std::pair dynamic_limits;
  // Process lower bound: (constant deceleration)
  // 以最大减速度减速到0的时间
  double dec_time = lower_v0_ / max_dec_;
  if (t - lower_t0_ < dec_time) {
    dynamic_limits.first =
        lower_s0_ + (lower_v0_ - max_dec_ * (t - lower_t0_) + lower_v0_) *
                        (t - lower_t0_) * 0.5;
  } else {
    dynamic_limits.first = lower_s0_ + (lower_v0_ * dec_time) * 0.5;
  }

  // Process upper bound: (constant acceleration)
  // 以最大加速度加速到最大速度的时间
  double acc_time = (max_v_ - upper_v0_) / max_acc_;
  if (t - upper_t0_ < acc_time) {
    dynamic_limits.second =
        upper_s0_ + (upper_v0_ + max_acc_ * (t - upper_t0_) + upper_v0_) *
                        (t - upper_t0_) * 0.5;
  } else {
    dynamic_limits.second = upper_s0_ + (upper_v0_ + max_v_) * acc_time * 0.5 +
                            (t - upper_t0_ - acc_time) * max_v_;
  }

  return dynamic_limits;
}

3.1.2 参考速度的行驶范围约束

其计算依据为运动学公式:
s = s 0 + v r e f t (3-2) s = s_{0} + v_{ref}t \tag{3-2} s=s0+vreft(3-2)

3.1.3 障碍物计算的行驶范围约束

计算出障碍物的STBoundary和相应的限速。

在这里存在的问题是由于没有决策信息,或者说决策信息不完整,对于障碍物形成的多个STDrivableBoundary应该选择哪一个。如下图所示,两个障碍物在ST Graph上会形成三个STDrivableBoundary。在STBoundsDecider中会根据下面三个条件来进行选择:

  • STDrivableBoundary是否在车辆动力学约束范围附近;
  • STDrivableBoundary是否包括按照参考速度行驶时的位移;
  • STDrivableBoundary哪个的区域 ( s m a x − s m i n ) (s_{max}-s_{min}) (smaxsmin)更大;

Planning-Apollo速度决策规划_第5张图片

3.2 SpeedBoundsPrioriDecider

SpeedBoundsPrioriDecider对应的Decider或者说classSpeedBoundsDecider,是计算每个障碍物的STBoundary得到ST Graph中的st_boundaries_speed_limit_(SpeedLimit)。其中每个障碍物的STBoundary的计算其实是根据遍历规划时间内障碍物和规划路径的碰撞关系来计算的,并且会根据是否对障碍物做过纵向决策以及决策类型设置其STBoundarycharacteristic_length

speed_limit_(SpeedLimit)则是根据三个方面计算的:

  • 地图限速;
  • 向心加速度限制;
  • 对于近距离nudge障碍物的减速避让;

限速值是上述三种限速的最小值。

3.3 PathTimeHeuristicOptimizer

PathTimeHeuristicOptimizer是速度规划的DP过程。速度规划需要在凸空间内求解,然而由于障碍物等原因会使求解空间变成非凸的,因此需要DP算法得到对不同障碍物的决策,从而得到一个凸空间。另一方面,最优化问题一般都仅有局部寻优能力,可能会收敛到某个局部最优解。为了保障优化问题的求解质量,使用全局“视野”的DP算法快速生成粗糙的初始解,并从该初始解的领域开展局部优化可以使最优化问题收敛到全局近似解。

Planning-Apollo速度决策规划_第6张图片

3.4 SpeedDecider

SpeedDecider根据DP算法生成的速度曲线 s ( t ) s(t) s(t)和障碍物的STBoundary的位置关系生成Ignore、Stop、Follow、Yield、Overtake的纵向决策。

  • Ignore
    • 障碍物的STBoundaryST Graph的范围内;
    • 已经有纵向决策的障碍物;
    • 类型是禁停区的obstacleSTBoundary位于速度曲线的下方;
  • Stop
    • 前方是行人的停车决策;
    • 类型是禁停区的obstacleSTBoundary位于速度曲线的上方;
    • ADC前方低速行驶的堵塞道路的障碍物;
    • STBoundary和速度曲线相交的障碍物;
  • Follow
    • STBoundary位于速度曲线上方,且不需要Stop的障碍物;
  • Yield
    • STBoundary位于速度曲线上方,且不需要StopFollow的障碍物;
  • Overtake
    • STBoundary位于速度曲线下方的障碍物;

3.5 SpeedBoundsFinalDecider

SpeedBoundsFinalDecider对应的Decider同样是SpeedBoundsDecider,和SpeedBoundsPrioriDecider不同的是配置参数,从Apollo中的默认配置参数来看,SpeedBoundsFinalDecider会根据DP过程生成的决策结果和更小的boundary_buffer生成更加精确的STBoundary

default_task_config: {
  task_type: SPEED_BOUNDS_PRIORI_DECIDER
  speed_bounds_decider_config {
    total_time: 7.0
    boundary_buffer: 0.25
    max_centric_acceleration_limit: 2.0
    point_extension: 0.0
    lowest_speed: 2.5
    static_obs_nudge_speed_ratio: 0.6
    dynamic_obs_nudge_speed_ratio: 0.8
  }
}
default_task_config: {
  task_type: SPEED_BOUNDS_FINAL_DECIDER
  speed_bounds_decider_config {
    total_time: 7.0
    boundary_buffer: 0.1
    max_centric_acceleration_limit: 2.0
    point_extension: 0.0
    lowest_speed: 2.5
    static_obs_nudge_speed_ratio: 0.6
    dynamic_obs_nudge_speed_ratio: 0.8
  }
}

3.6 PiecewiseJerkSpeedOptimizer

速度规划的优化求解即是按照上述的算法原理实现的。可以看到,在此会依据纵向决策结果生成纵向位移的约束边界,将每个时刻和cruise speed的误差作为优化目标的一部分,并且根据DP结果在每个时刻处位移的速度约束作为优化问题的速度约束边界,因此将每个时刻的位移和DP的位置之差作为了优化目标的一部分,但是这样只能实现速度的软约束。

  piecewise_jerk_problem.set_dx_ref(config.ref_v_weight(),
                                    reference_line_info_->GetCruiseSpeed());

  // Update STBoundary
  std::vector> s_bounds;
  for (int i = 0; i < num_of_knots; ++i) {
    double curr_t = i * delta_t;
    double s_lower_bound = 0.0;
    double s_upper_bound = total_length;
    for (const STBoundary* boundary : st_graph_data.st_boundaries()) {
      double s_lower = 0.0;
      double s_upper = 0.0;
      if (!boundary->GetUnblockSRange(curr_t, &s_upper, &s_lower)) {
        continue;
      }
      switch (boundary->boundary_type()) {
        case STBoundary::BoundaryType::STOP:
        case STBoundary::BoundaryType::YIELD:
          s_upper_bound = std::fmin(s_upper_bound, s_upper);
          break;
        case STBoundary::BoundaryType::FOLLOW:
          // TODO(Hongyi): unify follow buffer on decision side
          s_upper_bound = std::fmin(s_upper_bound, s_upper - 8.0);
          break;
        case STBoundary::BoundaryType::OVERTAKE:
          s_lower_bound = std::fmax(s_lower_bound, s_lower);
          break;
        default:
          break;
      }
    }
    if (s_lower_bound > s_upper_bound) {
      const std::string msg =
          "s_lower_bound larger than s_upper_bound on STGraph";
      AERROR << msg;
      speed_data->clear();
      return Status(ErrorCode::PLANNING_ERROR, msg);
    }
    s_bounds.emplace_back(s_lower_bound, s_upper_bound);
  }
  piecewise_jerk_problem.set_x_bounds(std::move(s_bounds));

  // Update SpeedBoundary and ref_s
  std::vector x_ref;
  std::vector penalty_dx;
  std::vector> s_dot_bounds;
  const SpeedLimit& speed_limit = st_graph_data.speed_limit();
  for (int i = 0; i < num_of_knots; ++i) {
    double curr_t = i * delta_t;
    // get path_s
    SpeedPoint sp;
    // DP过程计算的结果
    reference_speed_data.EvaluateByTime(curr_t, &sp);
    const double path_s = sp.s();
    x_ref.emplace_back(path_s);
    // get curvature
    PathPoint path_point = path_data.GetPathPointWithPathS(path_s);
    // default: kappa_penalty_weight = 2000.0
    // 对横向加速度进行约束
    // 用DP的结果查询t时刻的曲率(t时刻的曲率本是不可知的),将非线性约束转换为线性约束
    penalty_dx.push_back(std::fabs(path_point.kappa()) *
                         config.kappa_penalty_weight());
    // get v_upper_bound
    const double v_lower_bound = 0.0;
    double v_upper_bound = FLAGS_planning_upper_speed_limit;
    v_upper_bound = speed_limit.GetSpeedLimitByS(path_s);
    s_dot_bounds.emplace_back(v_lower_bound, std::fmax(v_upper_bound, 0.0));
  }
  // default: ref_s_weight = 10.0
  piecewise_jerk_problem.set_x_ref(config.ref_s_weight(), std::move(x_ref));
  piecewise_jerk_problem.set_penalty_dx(penalty_dx);
  piecewise_jerk_problem.set_dx_bounds(std::move(s_dot_bounds));

你可能感兴趣的:(Auto,Driving,动态规划,算法,人工智能)