自动驾驶---Motion Planning之轨迹Speed优化

1 背景

        在之前的几篇文章中,不管是通过构建SL图《自动驾驶---Motion Planning之Path Boundary》,ST图《自动驾驶---Motion Planning之Speed Boundary》,又或者是构建SLT图《自动驾驶---Motion Planning之构建SLT Driving Corridor》,最终我们都是为了得到boundary的信息。

        构造优化问题求解的前提:首先确定问题的代价函数,有初值,有边界(约束),接下来就可以进行求解了。 在之前的博客《自动驾驶---Motion Planning之轨迹Path优化》中讨论过横向的优化过程。本篇博客笔者继续阐述Apollo中纵向轨迹优化的问题。

2 轨迹纵向优化

        前面也提到过,在Apollo中,横纵向的优化求解是解耦开的,因此在横纵向分别需要求解一次,最终得到得到横纵向的轨迹信息(s,s',s'',l,l',l'')------这里的 l' 是 l 关于 s 的一阶导数, s' 是 s 关于 t 的一阶导数

        整体轨迹优化器的代码位于/apollo/modules/planning/tasks/optimizers。

2.1 构造优化问题  

        二次规划问题的形式:

min(\cfrac{1}{2}x^THx+g^Tx) \\ \\ s.t. a_i^Tx = b_i, i\in{E} \\ h_j^Tx\le{t_j}, j\in{I}

        其中H是由二阶导构成的Hessian矩阵,g^T是由梯度构成的Jacobi矩阵;然后包括m个等式约束集合和n个不等约束集合。

        Speed优化问题的自变量为:x=[s_0,s_1...s_{n-1},s'_0,s'_1,s'_2...s'_{n-1},s''_0,s''_1...s''_{n-1}]

        论文中主要考虑了纵向加速度、纵向Jerk、向心加速度、接近参考速度、终点的s,s'以及s''的限制,同样的在Apollo开源项目中增加了所有点的更接近参考线的惩罚函数。

论文中的代价函数如下所示:

 Apollo开源项目中的代价函数如下所示:

J=w_{s_{ref}}\sum_{i=0}^{n-1}(s_i-s_{iref})^2+w_k*\sum_{i=0}^{n-1} (s'_i)^2*Kappa(s_i)\\\\+w_{s'}*\sum_{i=0}^{n-1}(s'_i-s'_{iref})^2+w_{s''}*\sum_{i=0}^{n-1} (s''_i)^2\\\\+w_{s'''}*\sum_{i=0}^{n-2}((s''_{i+1}-s''_{i})/\Delta s)^2+w_{send}(s_{n-1}- s_{endl})\\\\+w_{s'end}(s'_{n-1}-s'_{ends})+w_{s''end}(s''_{n-1}-s''_{endl})

2.2 约束条件

        接下来考虑的是约束条件(不等式约束+等式约束)。不等式约束主要考虑包括自车的速度,加速度,jerk以及向心加速度,等式约束主要考虑的是采样点之间s,s',s''的连续性。

2.3 求解二次规划问题

将2.1.2小节得到的代价函数依据阶次整理得到:

J=w_{s_{ref}}\sum_{i=0}^{n-1}(s_i-s_{iref})^2\\\\+w_k*\sum_{i=0}^{n-1} (s'_i)^2*Kappa(s_i)+w_{s'}*\sum_{i=0}^{n-1}(s'_i-s'_{iref})^2\\\\+w_{s''}*\sum_{i=0}^{n-1} (s''_i)^2+w_{s'''}*\sum_{i=0}^{n-2}((s''_{i+1}-s''_{i})/\Delta s)^2\\\\+w_{send}(s_{n-1}- s_{endl})+w_{s'end}(s'_{n-1}-s'_{ends})+w_{s''end}(s''_{n-1}-s''_{endl})

先看speed optimizer的Process()函数:

Status PiecewiseJerkSpeedOptimizer::Process(const PathData& path_data,const TrajectoryPoint& init_point,SpeedData* const speed_data) {if (reference_line_info_->ReachedDestination()) {return Status::OK();}ACHECK(speed_data != nullptr);// 获取speed初值SpeedData reference_speed_data = *speed_data;if (path_data.discretized_path().empty()) {std::string msg("Empty path data");AERROR << msg;return Status(ErrorCode::PLANNING_ERROR, msg);}// 得到speed decider等模块获取的st图StGraphData& st_graph_data = *reference_line_info_->mutable_st_graph_data();const auto& veh_param =common::VehicleConfigHelper::GetConfig().vehicle_param();std::array<double, 3> init_s = {0.0, st_graph_data.init_point().v(),st_graph_data.init_point().a()};double delta_t = 0.1;double total_length = st_graph_data.path_length();double total_time = st_graph_data.total_time_by_conf();int num_of_knots = static_cast<int>(total_time / delta_t) + 1;PiecewiseJerkSpeedProblem piecewise_jerk_problem(num_of_knots, delta_t,init_s);// 设置权重系数和部分约束const auto& config = config_.piecewise_jerk_speed_optimizer_config();piecewise_jerk_problem.set_weight_ddx(config.acc_weight());piecewise_jerk_problem.set_weight_dddx(config.jerk_weight());piecewise_jerk_problem.set_x_bounds(0.0, total_length);piecewise_jerk_problem.set_dx_bounds(0.0, std::fmax(FLAGS_planning_upper_speed_limit,st_graph_data.init_point().v()));piecewise_jerk_problem.set_ddx_bounds(veh_param.max_deceleration(),veh_param.max_acceleration());piecewise_jerk_problem.set_dddx_bound(FLAGS_longitudinal_jerk_lower_bound,FLAGS_longitudinal_jerk_upper_bound);piecewise_jerk_problem.set_dx_ref(config.ref_v_weight(),reference_line_info_->GetCruiseSpeed());// Update STBoundarystd::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 sides_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) {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_sstd::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_sSpeedPoint sp;reference_speed_data.EvaluateByTime(curr_t, &sp);const double path_s = sp.s();x_ref.emplace_back(path_s);// get curvaturePathPoint path_point = path_data.GetPathPointWithPathS(path_s);penalty_dx.push_back(std::fabs(path_point.kappa()) *config.kappa_penalty_weight());// get v_upper_boundconst 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));}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));// Solve the problemif (!piecewise_jerk_problem.Optimize()) {std::string msg("Piecewise jerk speed optimizer failed!");AERROR << msg;speed_data->clear();return Status(ErrorCode::PLANNING_ERROR, msg);}// Extract outputconst std::vector<double>& s = piecewise_jerk_problem.opt_x();const std::vector<double>& ds = piecewise_jerk_problem.opt_dx();const std::vector<double>& dds = piecewise_jerk_problem.opt_ddx();for (int i = 0; i < num_of_knots; ++i) {ADEBUG << "For t[" << i * delta_t << "], s = " << s[i] << ", v = " << ds[i]<< ", a = " << dds[i];}speed_data->clear();speed_data->AppendSpeedPoint(s[0], 0.0, ds[0], dds[0], 0.0);for (int i = 1; i < num_of_knots; ++i) {// Avoid the very last points when already stoppedif (ds[i] <= 0.0) {break;}speed_data->AppendSpeedPoint(s[i], delta_t * i, ds[i], dds[i],(dds[i] - dds[i - 1]) / delta_t);}SpeedProfileGenerator::FillEnoughSpeedPoints(speed_data);RecordDebugInfo(*speed_data, st_graph_data.mutable_st_graph_debug());return Status::OK();
}

其中的二次项系数构建P矩阵的过程:

void PiecewiseJerkSpeedProblem::CalculateKernel(std::vector<c_float>* P_data,std::vector<c_int>* P_indices,std::vector<c_int>* P_indptr) {const int n = static_cast<int>(num_of_knots_);const int kNumParam = 3 * n;const int kNumValue = 4 * n - 1;std::vector<std::vector<std::pair<c_int, c_float>>> columns;columns.resize(kNumParam);int value_index = 0;// x(i)^2 * w_x_reffor (int i = 0; i < n - 1; ++i) {columns[i].emplace_back(i, weight_x_ref_ / (scale_factor_[0] * scale_factor_[0]));++value_index;}// x(n-1)^2 * (w_x_ref + w_end_x)columns[n - 1].emplace_back(n - 1, (weight_x_ref_ + weight_end_state_[0]) /(scale_factor_[0] * scale_factor_[0]));++value_index;// x(i)'^2 * (w_dx_ref + penalty_dx)for (int i = 0; i < n - 1; ++i) {columns[n + i].emplace_back(n + i,(weight_dx_ref_ + penalty_dx_[i]) /(scale_factor_[1] * scale_factor_[1]));++value_index;}// x(n-1)'^2 * (w_dx_ref + penalty_dx + w_end_dx)columns[2 * n - 1].emplace_back(2 * n - 1, (weight_dx_ref_ + penalty_dx_[n - 1] + weight_end_state_[1]) /(scale_factor_[1] * scale_factor_[1]));++value_index;auto delta_s_square = delta_s_ * delta_s_;// x(i)''^2 * (w_ddx + 2 * w_dddx / delta_s^2)columns[2 * n].emplace_back(2 * n,(weight_ddx_ + weight_dddx_ / delta_s_square) /(scale_factor_[2] * scale_factor_[2]));++value_index;for (int i = 1; i < n - 1; ++i) {columns[2 * n + i].emplace_back(2 * n + i, (weight_ddx_ + 2.0 * weight_dddx_ / delta_s_square) /(scale_factor_[2] * scale_factor_[2]));++value_index;}columns[3 * n - 1].emplace_back(3 * n - 1,(weight_ddx_ + weight_dddx_ / delta_s_square + weight_end_state_[2]) /(scale_factor_[2] * scale_factor_[2]));++value_index;// -2 * w_dddx / delta_s^2 * x(i)'' * x(i + 1)''for (int i = 0; i < n - 1; ++i) {columns[2 * n + i].emplace_back(2 * n + i + 1,-2.0 * weight_dddx_ / delta_s_square /(scale_factor_[2] * scale_factor_[2]));++value_index;}CHECK_EQ(value_index, kNumValue);int ind_p = 0;for (int i = 0; i < kNumParam; ++i) {P_indptr->push_back(ind_p);for (const auto& row_data_pair : columns[i]) {P_data->push_back(row_data_pair.second * 2.0);P_indices->push_back(row_data_pair.first);++ind_p;}}P_indptr->push_back(ind_p);
}

构建一次项矩阵的过程:

void PiecewiseJerkSpeedProblem::CalculateOffset(std::vector<c_float>* q) {CHECK_NOTNULL(q);const int n = static_cast<int>(num_of_knots_);const int kNumParam = 3 * n;q->resize(kNumParam);for (int i = 0; i < n; ++i) {if (has_x_ref_) {q->at(i) += -2.0 * weight_x_ref_ * x_ref_[i] / scale_factor_[0];}if (has_dx_ref_) {q->at(n + i) += -2.0 * weight_dx_ref_ * dx_ref_ / scale_factor_[1];}}if (has_end_state_ref_) {q->at(n - 1) +=-2.0 * weight_end_state_[0] * end_state_ref_[0] / scale_factor_[0];q->at(2 * n - 1) +=-2.0 * weight_end_state_[1] * end_state_ref_[1] / scale_factor_[1];q->at(3 * n - 1) +=-2.0 * weight_end_state_[2] * end_state_ref_[2] / scale_factor_[2];}
}

约束矩阵的构造同Path Optimizer:

void PiecewiseJerkProblem::CalculateAffineConstraint(std::vector<c_float>* A_data, std::vector<c_int>* A_indices,std::vector<c_int>* A_indptr, std::vector<c_float>* lower_bounds,std::vector<c_float>* upper_bounds) {// 3N params bounds on x, x', x''// 3(N-1) constraints on x, x', x''// 3 constraints on x_init_const int n = static_cast<int>(num_of_knots_);const int num_of_variables = 3 * n;const int num_of_constraints = num_of_variables + 3 * (n - 1) + 3;lower_bounds->resize(num_of_constraints);upper_bounds->resize(num_of_constraints);std::vector<std::vector<std::pair<c_int, c_float>>> variables(num_of_variables);int constraint_index = 0;// set x, x', x'' boundsfor (int i = 0; i < num_of_variables; ++i) {if (i < n) {variables[i].emplace_back(constraint_index, 1.0);lower_bounds->at(constraint_index) =x_bounds_[i].first * scale_factor_[0];upper_bounds->at(constraint_index) =x_bounds_[i].second * scale_factor_[0];} else if (i < 2 * n) {variables[i].emplace_back(constraint_index, 1.0);lower_bounds->at(constraint_index) =dx_bounds_[i - n].first * scale_factor_[1];upper_bounds->at(constraint_index) =dx_bounds_[i - n].second * scale_factor_[1];} else {variables[i].emplace_back(constraint_index, 1.0);lower_bounds->at(constraint_index) =ddx_bounds_[i - 2 * n].first * scale_factor_[2];upper_bounds->at(constraint_index) =ddx_bounds_[i - 2 * n].second * scale_factor_[2];}++constraint_index;}CHECK_EQ(constraint_index, num_of_variables);// x(i->i+1)''' = (x(i+1)'' - x(i)'') / delta_sfor (int i = 0; i + 1 < n; ++i) {variables[2 * n + i].emplace_back(constraint_index, -1.0);variables[2 * n + i + 1].emplace_back(constraint_index, 1.0);lower_bounds->at(constraint_index) =dddx_bound_.first * delta_s_ * scale_factor_[2];upper_bounds->at(constraint_index) =dddx_bound_.second * delta_s_ * scale_factor_[2];++constraint_index;}// x(i+1)' - x(i)' - 0.5 * delta_s * x(i)'' - 0.5 * delta_s * x(i+1)'' = 0for (int i = 0; i + 1 < n; ++i) {variables[n + i].emplace_back(constraint_index, -1.0 * scale_factor_[2]);variables[n + i + 1].emplace_back(constraint_index, 1.0 * scale_factor_[2]);variables[2 * n + i].emplace_back(constraint_index,-0.5 * delta_s_ * scale_factor_[1]);variables[2 * n + i + 1].emplace_back(constraint_index,-0.5 * delta_s_ * scale_factor_[1]);lower_bounds->at(constraint_index) = 0.0;upper_bounds->at(constraint_index) = 0.0;++constraint_index;}// x(i+1) - x(i) - delta_s * x(i)'// - 1/3 * delta_s^2 * x(i)'' - 1/6 * delta_s^2 * x(i+1)''auto delta_s_sq_ = delta_s_ * delta_s_;for (int i = 0; i + 1 < n; ++i) {variables[i].emplace_back(constraint_index,-1.0 * scale_factor_[1] * scale_factor_[2]);variables[i + 1].emplace_back(constraint_index,1.0 * scale_factor_[1] * scale_factor_[2]);variables[n + i].emplace_back(constraint_index, -delta_s_ * scale_factor_[0] * scale_factor_[2]);variables[2 * n + i].emplace_back(constraint_index,-delta_s_sq_ / 3.0 * scale_factor_[0] * scale_factor_[1]);variables[2 * n + i + 1].emplace_back(constraint_index,-delta_s_sq_ / 6.0 * scale_factor_[0] * scale_factor_[1]);lower_bounds->at(constraint_index) = 0.0;upper_bounds->at(constraint_index) = 0.0;++constraint_index;}// constrain on x_initvariables[0].emplace_back(constraint_index, 1.0);lower_bounds->at(constraint_index) = x_init_[0] * scale_factor_[0];upper_bounds->at(constraint_index) = x_init_[0] * scale_factor_[0];++constraint_index;variables[n].emplace_back(constraint_index, 1.0);lower_bounds->at(constraint_index) = x_init_[1] * scale_factor_[1];upper_bounds->at(constraint_index) = x_init_[1] * scale_factor_[1];++constraint_index;variables[2 * n].emplace_back(constraint_index, 1.0);lower_bounds->at(constraint_index) = x_init_[2] * scale_factor_[2];upper_bounds->at(constraint_index) = x_init_[2] * scale_factor_[2];++constraint_index;CHECK_EQ(constraint_index, num_of_constraints);int ind_p = 0;for (int i = 0; i < num_of_variables; ++i) {A_indptr->push_back(ind_p);for (const auto& variable_nz : variables[i]) {// coefficientA_data->push_back(variable_nz.second);// constraint indexA_indices->push_back(variable_nz.first);++ind_p;}}// We indeed need this line because of// https://github.com/oxfordcontrol/osqp/blob/master/src/cs.c#L255A_indptr->push_back(ind_p);
}

最终根据不同地决策结果优化求解可得到下图中类似的曲线:

3 总结

        总体看下来,如果对path optimizer比较熟悉的读者,对于speed optimizer应该可以很快的看懂整个过程,因为两者有着比较高的相似度。同时读者也会对二次规划问题有了更深的认识,二次规划问题的“三部曲”(1)构造代价函数(2)构造约束(3)QP求解。

        自此,整个行车部分的planning模块讲解基本完结(还剩下STSC的轨迹优化问题),前面对Routing,Behavior Planning,Motion Planning等模块的内容都分别做了比较详细的阐述。后续会针对泊车部分、感知以及控制等其他模块开设博客,欢迎读者朋友们关注。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/585488.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

无锡国家集成电路设计中心某公司的单锂小电机直流电机H桥驱动电路

H桥驱动 L9110S是一款直流电机驱动电路&#xff0c;适合单节锂电池应用。输出电流0.4A。价格约3毛。 推荐原因&#xff1a; 某些人应该知道这个地方&#xff0c;大多数人应该不知道这个地方&#xff0c;所以推荐一下。 这个地方去过几次&#xff0c;某公司与某方走的“近”&…

https安全性 带给im 消息加密的启发

大家好&#xff0c;我是蓝胖子&#xff0c;在之前# MYSQL 是如何保证binlog 和redo log同时提交的&#xff1f;这篇文章里&#xff0c;我们可以从mysql的设计中学会如何让两个服务的调用逻辑达到最终一致性&#xff0c;这也是分布式事务实现方式之一。今天来看看我们能够从http…

Ubuntu joystick 测试手柄 xbox

Ubuntu joystick 测试手柄 xbox 测试使用Ubuntu20.04 测试环境在工控机 安装测试 实际测试使用的手柄是北通阿修罗2pro 兼容xbox Ubuntu20.04主机 连接手柄或者无线接收器后查看是否已经检测到&#xff1a; ls /dev/input找到输入中的 js0 即为手柄输入 需要安装joysti…

uni app 扫雷

闲来无聊。做个扫雷玩玩吧&#xff0c;点击打开&#xff0c;长按标记&#xff0c;标记的点击两次或长按取消标记。所有打开结束 <template><view class"page_main"><view class"add_button" style"width: 100vw; margin-bottom: 20r…

【C语言】联合和枚举

个人主页点这里~ 联合和枚举 一、联合体1、联合体类型的声明2、联合体成员的特点3、与结构体对比4、计算联合体大小 二、枚举1、枚举的声明2、枚举的优点3、枚举类型的使用 一、联合体 1、联合体类型的声明 联合体的定义与结构体相似&#xff0c;但是联合体往往会节省更多的空…

日记本(源码+文档)

日记本&#xff08;小程序、ios、安卓都可部署&#xff09; 文件包含内容程序简要说明功能项目截图客户端首页日记列表 书写日记个人中心设置密码锁拨打客服热线修改信息退出登录登录页输入密码锁注册页 后端管理登录页首页管理员列表管理用户管理日记列表管理日记数据 文件包含…

最优算法100例之20-旋转数组求最小值

专栏主页:计算机专业基础知识总结(适用于期末复习考研刷题求职面试)系列文章https://blog.csdn.net/seeker1994/category_12585732.html 题目描述 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非递减排序的数组的一个旋转,输出旋转数组的…

nut-ui中的menu 菜单组件的二次封装

这个菜单组件 一般可以直接用到项目里 如果复用性不强的话 直接使用 但是有一个问题 如果很多地方都需要用到这个组件 我们可以把这个组件二次封装一下 <template><div class"cinema-search-filter-component"><nut-menu><template #icon>&…

网络基础——ISIS

名词 ISIS&#xff1a;中间系统到中间系统&#xff0c;优先级是15集成化ISIS&#xff1a;这是在优化后&#xff0c;可以使用在OSI模型上的NET地址&#xff1a;由区域ID、系统ID和SEL组成&#xff0c;一台设备上最多配置3个NET地址&#xff0c;条件是区域号要不一致&#xff0c;…

springcloud基本使用二(跨域访问)

创建两个springboot maven子项目 子项目名称分别为order-server和user-server 配置user-server子项目: 所需依赖: <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId> </dependenc…

Java项目:84 springboot人事系统

作者主页&#xff1a;舒克日记 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 本基于vue的人事系统有管理员和员工两个角色。 管理员功能有个人中心&#xff0c;部门信息管理&#xff0c;员工信息管理&#xff0c;考勤信息管理&a…

AcWing刷题-游戏

游戏 DP l lambda: [int(x) for x in input().split()]n l()[0] w [0] while len(w) < n:w l()s [0] * (n 1) for i in range(1, n 1): s[i] s[i - 1] w[i]f [[0] * (n 1) for _ in range(n 1)]for i in range(1, n 1): f[i][i] w[i]for length in range(2, …