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

在这里插入图片描述

优点

  • 参考线或者规划路径的曲率 κ \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¨

在这里插入图片描述

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

优点

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

缺点

  • 由于曲率是 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图中的可通行区域,如下图中的浅蓝色区域。

在这里插入图片描述

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<int>(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<const STBoundary*> 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<STPoint> upper_points_;
  std::vector<STPoint> lower_points_;

  std::string id_;  // obstacle ID
  double characteristic_length_ = 1.0;  // ??? 
  double min_s_ = std::numeric_limits<double>::max();
  double max_s_ = std::numeric_limits<double>::lowest();
  double min_t_ = std::numeric_limits<double>::max();
  double max_t_ = std::numeric_limits<double>::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中速度决策规划的实现流程如下:

在这里插入图片描述

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<double, double> STDrivingLimits::GetVehicleDynamicsLimits(
    const double t) const {
  std::pair<double, double> 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)更大;

在这里插入图片描述

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算法快速生成粗糙的初始解,并从该初始解的领域开展局部优化可以使最优化问题收敛到全局近似解。

在这里插入图片描述

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<std::pair<double, double>> 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<double> x_ref;
  std::vector<double> penalty_dx;
  std::vector<std::pair<double, double>> 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));
  • 22
    点赞
  • 132
    收藏
    觉得还不错? 一键收藏
  • 15
    评论
### 回答1: Apollo Planning决策规划算法在无人驾驶领域中被广泛应用,在自动驾驶车辆中起着至关重要的作用。该算法主要通过对车辆周围环境的感知和分析,实现智能驾驶路线的规划决策,确保车辆安全行驶。 该算法的代码主要包含三个部分:感知模块、规划模块和控制模块。其中感知模块主要负责采集车辆周围的环境信息,包括车辆所处的位置、路况、障碍物等。规划模块通过对这些信息的分析,提出一系列可能的驾驶路线,并通过评估这些路线的优劣来选择最佳驾驶路线。控制模块负责实现规划模块中选择的最佳驾驶路线,并控制车辆按照路线行驶。 在Apollo Planning决策规划算法中,规划模块是实现驾驶决策的最重要模块,也是最具技术难度的模块。规划模块通过对车辆当前状态和环境信息的分析,提出一系列汽车驾驶路线。该算法采用在线生成路线方案的方法,路线生成的步骤如下: 1. 动态路径规划:根据车辆的位置和行驶状态,实时选择当前最佳的驾驶路线。 2. 静态路线生成:基于当前车辆所处的环境信息,生成固定的驾驶路线。 3. 组合路径规划:将动态路径规划和静态路线生成相结合,生成最终的驾驶路线。 除此之外,Apollo Planning决策规划算法还包括计算机视觉、机器学习、深度学习和人工智能等技术,这些技术的综合应用使得Apollo Planning决策规划算法成为无人驾驶领域中应用最广泛的决策规划算法。 ### 回答2: Apollo Planning决策规划算法是一种用于自动驾驶系统的规划算法。该算法的主要作用是实时生成安全、有效且符合路况的路径以实现自动驾驶功能。本文将对该算法进行详细解析。 Apollo Planning决策规划算法主要包括三个步骤:路线规划、运动规划决策规划。具体代码如下: 1. 路线规划 ```c++ bool Planning::PlanOnReferenceLine() { std::vector<const hdmap::HDMap*> hdmap_vec; hdmap_vec.reserve(1); if (!GetHdmapOnRouting(current_routing_, &hdmap_vec)) { AERROR << "Failed to get hdmap on current routing with " << current_routing_.ShortDebugString(); return false; } const auto& reference_line_info = reference_line_infos_.front(); std::vector<ReferencePoint> ref_points; if (!CreateReferenceLineInfo(hdmap_vec.front(), reference_line_info, &ref_points)) { AERROR << "Failed to create reference line from routing"; return false; } // Smooth reference line Spline2d smoothed_ref_line; std::vector<double> s_refs; std::vector<double> l_refs; std::vector<double> headings; std::vector<double> kappas; std::vector<double> dkappas; if (!SmoothReferenceLine(ref_points, &smoothed_ref_line, &s_refs, &l_refs, &headings, &kappas, &dkappas)) { AERROR << "Failed to smooth reference line"; return false; } reference_line_info.SetTrajectory(&smoothed_ref_line); reference_line_info.SetReferenceLine(&ref_points); // set origin point if (!reference_line_info.SLToXY(s_refs.front(), 0.0, &origin_point_)) { AERROR << "Failed to get origin point on reference line"; return false; } return true; } ``` 在路线规划阶段中,Apollo Planning决策规划算法首先获取当前行驶路线和高精度地图数据。然后根据行驶路线和地图数据构建参考线,对参考线进行平滑处理,得到平滑后的参考线。此时我们可以得到平滑后的参考线的位置、方向和曲率等信息,这些信息将作为后面的运动和决策规划的输入。 2. 运动规划 ```c++ bool Planning::PlanOnPrediction() { PredictionObstacles prediction_obstacles; if (!GetPrediction(&prediction_obstacles)) { AERROR << "Prediction failed"; return false; } std::vector<Obstacle> obstacles; if (!BuildObstacle(prediction_obstacles, &obstacles)) { AERROR << "Unable to build obstacle"; return false; } const auto& reference_line_info = reference_line_infos_.front(); const auto& reference_line = reference_line_info.reference_line(); SpeedData speed_data; Cruiser::PlanningTarget planning_target; Status status = cruiser_->Plan(reference_line_info, obstacles, 0.0, reference_line.Length(), &speed_data, &planning_target); if (status != Status::OK()) { AERROR << "Failed to plan path with status: " << status; return false; } RecordDebugInfo(reference_line_info, obstacles, speed_data); return true; } ``` 运动规划主要用于生成车辆在参考线上的运行轨迹。在运动规划阶段,Apollo Planning决策规划算法首先获取预测障碍物信息,将预测的障碍物转化为Obstacle对象。然后根据当前平滑后的参考线、障碍物等信息进行运动规划。运动规划的目标是生成符合规划目标的速度曲线。最后,Apollo Planning决策规划算法记录调试信息,以便后续分析调试。 3. 决策规划 ```c++ bool Planning::MakeDecision() { const auto& reference_line_info = reference_line_infos_.front(); const auto& reference_line = reference_line_info.reference_line(); std::vector<const Obstacle*> obstacles; if (!Obstacle::CreateObstacleRegions(FLAGS_max_distance_obstacle, reference_line_info, &obstacles)) { AERROR << "Failed to create obstacle regions"; return false; } for (auto obstacle_ptr : obstacles) { const auto& obstacle = *obstacle_ptr; if (obstacle.IsVirtual()) { continue; } if (obstacle.IsStatic()) { continue; } if (obstacle.type() == PerceptionObstacle::BICYCLE || obstacle.type() == PerceptionObstacle::PEDESTRIAN) { continue; } const auto& nearest_path_point = obstacle.nearest_point(); const SLPoint obstacle_sl = reference_line_info.xy_to_sl(nearest_path_point); if (obstacle_sl.s() < -FLAGS_max_distance_obstacle || obstacle_sl.s() > reference_line.Length() + FLAGS_max_distance_obstacle) { continue; } ObjectDecisionType decision; decision.mutable_avoid(); decision.mutable_avoid()->set_distance_s(-obstacle_sl.s()); reference_line_info.AddCost(&obstacle, &decision); } std::vector<ObjectDecisionType> decisions; if (!traffic_rule_manager_.ApplyRule(reference_line_info, &decisions)) { AERROR << "Failed to apply traffic rule manager"; return false; } reference_line_info.SetDecision(decisions); return true; } ``` 决策规划是基于当前环境信息和规划的路径等输入信息,实时生成控制命令的过程。在Apollo Planning决策规划算法中,决策规划阶段根据当前参考线、障碍物等信息生成决策。该算法根据不同的规则和策略,生成不同的控制命令,以保证车辆安全、有效地运行。 综上,Apollo Planning决策规划算法是自动驾驶系统中重要的规划算法之一,它通过路线规划、运动规划决策规划三个步骤,实现了安全、有效和符合路况的路径规划,为自动驾驶车辆的控制提供了重要的支持。 ### 回答3: Apollo Planning(阿波罗规划)是百度自动驾驶平台Apollo中的一种决策规划算法,主要用于规划车辆的驾驶行为。该算法基于深度强化学习,使用了运动学模型和环境感知技术,可以根据车辆当前位置和目的地,生成一条最优的行驶路径,包括车辆的控制指令和行驶速度等。 该算法的核心技术是深度强化学习,它通过对驾驶过程进行大量的仿真,让计算机通过自我学习得到驾驶规则,使车辆能够根据不同的场景做出最优的决策。具体而言,算法先通过神经网络生成一系列潜在的行动策略,然后通过与环境进行交互、执行行动并接收环境反馈来评估每个策略的优劣,最终选取最优策略进行执行。 在实现上,Apollo Planning算法主要由四个模块构成:感知模块、规划模块、执行模块和控制模块。感知模块主要用于获取车辆周围环境的信息,包括车辆位置、速度、道路情况、交通灯等;规划模块根据感知模块提供的信息和车辆的目的地,生成一条最优的行驶路径;执行模块则根据规划模块生成的路径信息,实现车辆的自主驾驶;控制模块则根据执行模块生成的控制指令,控制车辆的加速、刹车、转向等行为。 在算法实现上,Apollo Planning采用了C++编程语言,结合ROS框架实现各模块之间的数据交互和代码复用,保证了算法的高效性和可维护性。算法代码实现方面还采用了许多优化技术,包括多线程并发执行、高效的数据结构和算法等,以提升算法的运行效率和稳定性。 总之,Apollo Planning是一种基于深度强化学习的决策规划算法,具有高效、自主、可靠等特点,在智能驾驶领域具有广泛应用前景。
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值