原创文章,转载请注明: 转载自慢慢的回味
Apollo里面最重要的模块就是感知和规划模块,规划模块最重要的是路径规划和速度规划任务,对应ROS机器人里面的局部规划。Apollo的规划模块首先根据当前的情况进行多场景(scenario)调度,每个场景又包含一个或者多个阶段(stage),每个阶段又由多个具有独立功能的小模块任务(task)完成。前面介绍了路径规划piecewise jerk speed optimizer这个任务,这篇文章就解读一下速度规划piecewise jerk speed optimizer这个任务。SL规划保证车辆的横向偏移足够平滑,ST规划保证车辆的前进方向速度变化足够平滑。
Content:
Apollo 中二次规划问题的构造
首先列出代价函数:
(1)
其中:
(2)
(3)
带入:
注意到和为常数,略去:
注意上式最后一项为常数,略去:
(4)
(5)
(6)
(7)
(8)
(9)
其中,公式8中从上到下依次对应 , , 。
这样代价函数1就由上面的,,表示了。
然后现在构建约束条件:
这个和上文Apollo二次规划算法(piecewise jerk path optimizer)解析中的一致,只是把l对s的限制修改为s对t的限制。
Apollo代码的实现
同上文一样,只是构建P和Q不一样:
构建P:
参见公式8。
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_ref 参见公式5 for (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) 参见公式6 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)'' 参见公式7 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); } |
构建Q:
参见公式9。
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]; } } |
本作品采用知识共享署名 4.0 国际许可协议进行许可。