查看原文
其他

技术文档|分段加加速度路径优化

阿波君 Apollo开发者社区 2022-07-29


从自动驾驶技术出发,Apollo开发者社区带你一起探索自动驾驶开发的奥秘,给每一位对自动驾驶充满热爱的你带来最实质的帮助,助力你的每一次研发。本文将介绍 分段加加速度路径优化,主要从以下几点为大家进行详细讲解。需用在线编辑公式网站浏览Latex等式。


  • 概览

  • 相关代码及对应版本

  • 代码流程及框架

  • 相关算法解析



分段加加速度路径优化是规划模块的任务,属于Task中的Optimizer类别。
规划模块的运动总体流程图如下:


总体流程图以Lane Follow场景为例子进行说明。Task的主要功能位于Process函数中。

Fig.1的具体运行过程可以参考path_bounds_decider

分段加加速度路径优化的流程如下图:



本节说明分段加加速度路径优化代码和算法。
请参考代码Apollo r6.0.0 piecewise_jerk_path_optimization
  • 输入 

    PiecewiseJerkPathOptimizer::Process( const SpeedData& speed_data, const ReferenceLine& reference_line, const common::TrajectoryPoint& init_point, const bool path_reusable, PathData* const final_path_data)

    其中包括参考线,起始点等。


  • 输出

    OptimizePath函数得到最优的路径,信息包括$opt_l, opt_dl, opt_ddl$。在Process函数中最终结果保存到了Task基类的变量reference_line_info_中。



分段加加速度路径优化代码的流程图如下:



  • 如果重复使用Path则Return

common::Status PiecewiseJerkPathOptimizer::Process( const SpeedData& speed_data, const ReferenceLine& reference_line, const common::TrajectoryPoint& init_point, const bool path_reusable, PathData* const final_path_data) { // 跳过piecewise_jerk_path_optimizer 如果路径重复使用 if (FLAGS_enable_skip_path_tasks && path_reusable) { return Status::OK(); }  ... ...


  • adc起始点转化到Frenet坐标

... ... const auto init_frenet_state = reference_line.ToFrenetFrame(planning_start_point);
// 为lane-change选择lane_change_path_config // 否则, 选择default_path_config const auto& config = reference_line_info_->IsChangeLanePath() ? config_.piecewise_jerk_path_optimizer_config() .lane_change_path_config() : config_.piecewise_jerk_path_optimizer_config() .default_path_config(); ... ...


  • 遍历每个路径边界

... ... const auto& path_boundaries = reference_line_info_->GetCandidatePathBoundaries(); ADEBUG << "There are " << path_boundaries.size() << " path boundaries."; const auto& reference_path_data = reference_line_info_->path_data();
std::vector<PathData> candidate_path_data; // 遍历每个路径 for (const auto& path_boundary : path_boundaries) { size_t path_boundary_size = path_boundary.boundary().size(); ... ...


  • 判断是否Pull-over或Regular

1、判断是否是Pull-over

... ... if (!FLAGS_enable_force_pull_over_open_space_parking_test) { // pull over场景 const auto& pull_over_status = injector_->planning_context()->planning_status().pull_over(); if (pull_over_status.has_position() && pull_over_status.position().has_x() && pull_over_status.position().has_y() && path_boundary.label().find("pullover") != std::string::npos) { common::SLPoint pull_over_sl; reference_line.XYToSL(pull_over_status.position(), &pull_over_sl); end_state[0] = pull_over_sl.l(); } } ... ...

2、判断是否是Regular
... ... if (path_boundary.label().find("regular") != std::string::npos && reference_path_data.is_valid_path_reference()) { ADEBUG << "path label is: " << path_boundary.label(); // 当参考路径就位 for (size_t i = 0; i < path_reference_size; ++i) { common::SLPoint path_reference_sl; reference_line.XYToSL( common::util::PointFactory::ToPointENU( reference_path_data.path_reference().at(i).x(), reference_path_data.path_reference().at(i).y()), &path_reference_sl); path_reference_l[i] = path_reference_sl.l(); } end_state[0] = path_reference_l.back(); path_data.set_is_optimized_towards_trajectory_reference(true); is_valid_path_reference = true; } ... ...


  • 优化路径

... ... // 设置参数 const auto& veh_param = common::VehicleConfigHelper::GetConfig().vehicle_param(); const double lat_acc_bound = std::tan(veh_param.max_steer_angle() / veh_param.steer_ratio()) / veh_param.wheel_base(); std::vector<std::pair<double, double>> ddl_bounds; for (size_t i = 0; i < path_boundary_size; ++i) { double s = static_cast<double>(i) * path_boundary.delta_s() + path_boundary.start_s(); double kappa = reference_line.GetNearestReferencePoint(s).kappa(); ddl_bounds.emplace_back(-lat_acc_bound - kappa, lat_acc_bound - kappa); } // 优化算法 bool res_opt = OptimizePath( init_frenet_state.second, end_state, std::move(path_reference_l), path_reference_size, path_boundary.delta_s(), is_valid_path_reference, path_boundary.boundary(), ddl_bounds, w, max_iter, &opt_l, &opt_dl, &opt_ddl); ... ...

优化过程:

1、定义piecewise_jerk_problem变量,优化算法

2、设置变量:权重;D方向距离、速度加速度边界;最大转角速度;jerk bound

3、优化算法

4、获取结果


  • 如果成功将值保存到candidate_path_data

... ... if (res_opt) { for (size_t i = 0; i < path_boundary_size; i += 4) { ADEBUG << "for s[" << static_cast<double>(i) * path_boundary.delta_s() << "], l = " << opt_l[i] << ", dl = " << opt_dl[i]; } auto frenet_frame_path = ToPiecewiseJerkPath(opt_l, opt_dl, opt_ddl, path_boundary.delta_s(), path_boundary.start_s());
path_data.SetReferenceLine(&reference_line); path_data.SetFrenetPath(std::move(frenet_frame_path)); if (FLAGS_use_front_axe_center_in_path_planning) { auto discretized_path = DiscretizedPath( ConvertPathPointRefFromFrontAxeToRearAxe(path_data)); path_data.SetDiscretizedPath(discretized_path); } path_data.set_path_label(path_boundary.label()); path_data.set_blocking_obstacle_id(path_boundary.blocking_obstacle_id()); candidate_path_data.push_back(std::move(path_data)); } ... ...


  • 失败则返回错误码,成功则保存路径点

... ... if (candidate_path_data.empty()) { return Status(ErrorCode::PLANNING_ERROR, "Path Optimizer failed to generate path"); } reference_line_info_->SetCandidatePathData(std::move(candidate_path_data)); return Status::OK(); ... ...


分段加加速度路径优化算法详细介绍在论文《Optimal Vehicle Path Planning Using Quadratic Optimization for Baidu Apollo Open Platform》中。
论文链接:https://ieeexplore.ieee.org/document/9304787



路径优化算法:

  • 根据导引线和障碍物生成路径边界
  • 将导引线在s方向等间隔采样
  • 对每个s方向的离散点迭代的优化 $𝑙, 𝑙^{'}, 𝑙^{''}$


建立数学模型

  • 轨迹平滑


$$ min \sum_{k=1}^{n-2} ||2P_k - P_{k-1} + P_{k+1}||2^2 +| |sum{k=0}^{n-1} ||2P_k - P_{k-ref}||2^2 +| |sum{k=0}^{n-2} ||P_{k+1} - P_k||_2^2 $$
subject to:$$ P_k \in B, for: k = 0,...,n-1 \ ||2P_k - P_{k-1} - P_{k+1}||2 < |frac{d{ref}^2}{R_{min}} \ for: k=1,...,n-2 $$

其中
  • $P_k$是$(x_k, y_k)$

  • $P_{k_ref}$是路由线的原始点

  • $B$是$P_k$在$P_{k_ref}$的边界

  • $\frac{d_{ref}^2}{R_{min}}$是最大曲率约束


  • 优化目标

$$ \tilde{f}(l(s)) = w_l * \sum_{i=0}^{n-1} l_i^2 + w_{l^{'}} * \sum_{i=0}^{n-1} l_i^{'2} + w_{l^{''}} * \sum_{i=0}^{n-1} l_i^{''2} +\ w_{l^{'''}} * \sum_{i=0}^{n-2}(\frac{l_{i+1}^{''} - l_i^{''}}{\Delta s})^2 +\ w_{obs} * \sum_{i=0}^{n-1}(l_i - 0.5*(l_{min}^i + l_{max}^i))^2 $$


  • 约束条件

1、连续性约束

$$ l_{i+1}^{'''} = l_i^{''} + \int_0^{\Delta{s}} l_{i\rightarrow{i+1}}^{'''} ds = l_i^{''} + l_{i\rightarrow{i+1}}^{'''} * \Delta{s} \ l_{i+1}^{'} = l_i^{'} + \int_0^{\Delta{s}}l^{''}(s)ds = l_i^{'} + l_i^{''}\Delta{s} + \frac{1}{2} * l_{i\rightarrow{i+1}}^{'''} * \Delta{s^2} \ l_{i+1} = l_i + \int_0^{\Delta{s}}l^{'}(s)ds \ = l_i + l_i^{'}\Delta(s^2) + \frac{1}{6}l_{i\rightarrow{i+1}}\Delta{s^3} $$

2、安全性约束

$l$方向的点需要在边界内。
$$l(s) \in l_B(s), \forall{s} \in [0, s_{max}]$$


3、曲率约束

自车的转角不能超过最大转角。
$$ tan(\alpha_{max})k_rl - tan(\alpha_{max}) + |k_r|*L \leqslant 0 $$
优化方法采用OSQP方法。



*《分段加加速度路径优化

https://github.com/ApolloAuto/apollo/blob/master/docs/technical_documents/tasks/piece_wise_jerk_path_cn.md


以上就是分段加加速度路径优化的全部内容,如果大家对Apollo开放平台和套件感兴趣,可以添加『Apollo小哥哥』(微信号:apollo_xzs)为好友,进入技术交流群,跟开发者们一起讨论哦。



©️原创归作者所有,如需转载,请注明出处,另有法律责任。

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存