31using common::math::Vec2d;
35 if (ref_points_.empty()) {
36 AERROR <<
"reference points empty, solver early terminates";
40 if (ref_points_.size() != bounds_around_refs_.size()) {
41 AERROR <<
"ref_points and bounds size not equal, solver early terminates";
45 if (ref_points_.size() < 3) {
46 AERROR <<
"ref_points size smaller than 3, solver early terminates";
50 if (ref_points_.size() > std::numeric_limits<int>::max()) {
51 AERROR <<
"ref_points size too large, solver early terminates";
56 num_of_points_ =
static_cast<int>(ref_points_.size());
57 num_of_pos_variables_ = num_of_points_ * 2;
58 num_of_slack_variables_ = num_of_points_ - 2;
59 num_of_variables_ = num_of_pos_variables_ + num_of_slack_variables_;
61 num_of_variable_constraints_ = num_of_variables_;
62 num_of_curvature_constraints_ = num_of_points_ - 2;
64 num_of_variable_constraints_ + num_of_curvature_constraints_;
67 std::vector<c_float> primal_warm_start;
68 SetPrimalWarmStart(ref_points_, &primal_warm_start);
71 std::vector<c_float> P_data;
72 std::vector<c_int> P_indices;
73 std::vector<c_int> P_indptr;
74 CalculateKernel(&P_data, &P_indices, &P_indptr);
77 std::vector<c_float> q;
78 CalculateOffset(ref_points_, &q);
81 std::vector<c_float> A_data;
82 std::vector<c_int> A_indices;
83 std::vector<c_int> A_indptr;
84 std::vector<c_float> lower_bounds;
85 std::vector<c_float> upper_bounds;
86 CalculateAffineConstraint(ref_points_, &A_data, &A_indices, &A_indptr,
87 &lower_bounds, &upper_bounds);
90 OSQPData* data =
reinterpret_cast<OSQPData*
>(c_malloc(
sizeof(OSQPData)));
91 data->n = num_of_variables_;
92 data->m = num_of_constraints_;
93 data->P = csc_matrix(data->n, data->n, P_data.size(), P_data.data(),
94 P_indices.data(), P_indptr.data());
96 data->A = csc_matrix(data->m, data->n, A_data.size(), A_data.data(),
97 A_indices.data(), A_indptr.data());
98 data->l = lower_bounds.data();
99 data->u = upper_bounds.data();
102 OSQPSettings* settings =
103 reinterpret_cast<OSQPSettings*
>(c_malloc(
sizeof(OSQPSettings)));
104 osqp_set_default_settings(settings);
105 settings->max_iter = max_iter_;
106 settings->time_limit = time_limit_;
107 settings->verbose = verbose_;
108 settings->scaled_termination = scaled_termination_;
109 settings->warm_start = warm_start_;
110 settings->polish =
true;
111 settings->eps_abs = 1e-5;
112 settings->eps_rel = 1e-5;
113 settings->eps_prim_inf = 1e-5;
114 settings->eps_dual_inf = 1e-5;
117 OSQPWorkspace* work =
nullptr;
119 work = osqp_setup(data, settings);
122 bool initial_solve_res = OptimizeWithOsqp(primal_warm_start, &work);
124 if (!initial_solve_res) {
125 AERROR <<
"initial iteration solving fails";
138 double original_slack_penalty = weight_curvature_constraint_slack_var_;
139 double last_fvalue = work->info->obj_val;
141 while (pen_itr < sqp_pen_max_iter_) {
143 bool fconverged =
false;
145 while (sub_itr < sqp_sub_max_iter_) {
146 SetPrimalWarmStart(opt_xy_, &primal_warm_start);
147 CalculateOffset(opt_xy_, &q);
148 CalculateAffineConstraint(opt_xy_, &A_data, &A_indices, &A_indptr,
149 &lower_bounds, &upper_bounds);
150 osqp_update_lin_cost(work, q.data());
151 osqp_update_A(work, A_data.data(), OSQP_NULL, A_data.size());
152 osqp_update_bounds(work, lower_bounds.data(), upper_bounds.data());
154 bool iterative_solve_res = OptimizeWithOsqp(primal_warm_start, &work);
155 if (!iterative_solve_res) {
156 AERROR <<
"iteration at " << sub_itr
157 <<
", solving fails with max sub iter " << sqp_sub_max_iter_;
158 weight_curvature_constraint_slack_var_ = original_slack_penalty;
167 double cur_fvalue = work->info->obj_val;
168 double ftol = std::abs((last_fvalue - cur_fvalue) / last_fvalue);
170 if (ftol < sqp_ftol_) {
171 ADEBUG <<
"merit function value converges at sub itr num " << sub_itr;
172 ADEBUG <<
"merit function value converges to " << cur_fvalue
173 <<
", with ftol " << ftol <<
", under max_ftol " << sqp_ftol_;
178 last_fvalue = cur_fvalue;
183 AERROR <<
"Max number of iteration reached";
184 weight_curvature_constraint_slack_var_ = original_slack_penalty;
193 ctol = CalculateConstraintViolation(opt_xy_);
195 ADEBUG <<
"ctol is " << ctol <<
", at pen itr " << pen_itr;
197 if (ctol < sqp_ctol_) {
198 ADEBUG <<
"constraint satisfied at pen itr num " << pen_itr;
199 ADEBUG <<
"constraint voilation value drops to " << ctol
200 <<
", under max_ctol " << sqp_ctol_;
201 weight_curvature_constraint_slack_var_ = original_slack_penalty;
210 weight_curvature_constraint_slack_var_ *= 10;
214 ADEBUG <<
"constraint not satisfied with total itr num " << pen_itr;
215 ADEBUG <<
"constraint voilation value drops to " << ctol
216 <<
", higher than max_ctol " << sqp_ctol_;
217 weight_curvature_constraint_slack_var_ = original_slack_penalty;
226void FemPosDeviationSqpOsqpInterface::CalculateKernel(
227 std::vector<c_float>* P_data, std::vector<c_int>* P_indices,
228 std::vector<c_int>* P_indptr) {
229 CHECK_GT(num_of_points_, 2);
251 std::vector<std::vector<std::pair<c_int, c_float>>> columns;
252 columns.resize(num_of_variables_);
255 for (
int col = 0; col < 2; ++col) {
256 columns[col].emplace_back(col, weight_fem_pos_deviation_ +
257 weight_path_length_ +
258 weight_ref_deviation_);
262 for (
int col = 2; col < 4; ++col) {
263 columns[col].emplace_back(
264 col - 2, -2.0 * weight_fem_pos_deviation_ - weight_path_length_);
265 columns[col].emplace_back(col, 5.0 * weight_fem_pos_deviation_ +
266 2.0 * weight_path_length_ +
267 weight_ref_deviation_);
271 int second_point_from_last_index = num_of_points_ - 2;
272 for (
int point_index = 2; point_index < second_point_from_last_index;
274 int col_index = point_index * 2;
275 for (
int col = 0; col < 2; ++col) {
277 columns[col_index].emplace_back(col_index - 4, weight_fem_pos_deviation_);
278 columns[col_index].emplace_back(
280 -4.0 * weight_fem_pos_deviation_ - weight_path_length_);
281 columns[col_index].emplace_back(
282 col_index, 6.0 * weight_fem_pos_deviation_ +
283 2.0 * weight_path_length_ + weight_ref_deviation_);
288 int second_point_col_from_last_col = num_of_pos_variables_ - 4;
289 int last_point_col_from_last_col = num_of_pos_variables_ - 2;
290 for (
int col = second_point_col_from_last_col;
291 col < last_point_col_from_last_col; ++col) {
292 columns[col].emplace_back(col - 4, weight_fem_pos_deviation_);
293 columns[col].emplace_back(
294 col - 2, -4.0 * weight_fem_pos_deviation_ - weight_path_length_);
295 columns[col].emplace_back(col, 5.0 * weight_fem_pos_deviation_ +
296 2.0 * weight_path_length_ +
297 weight_ref_deviation_);
301 for (
int col = last_point_col_from_last_col; col < num_of_pos_variables_;
303 columns[col].emplace_back(col - 4, weight_fem_pos_deviation_);
304 columns[col].emplace_back(
305 col - 2, -2.0 * weight_fem_pos_deviation_ - weight_path_length_);
306 columns[col].emplace_back(col, weight_fem_pos_deviation_ +
307 weight_path_length_ +
308 weight_ref_deviation_);
312 CHECK_EQ(col_num, num_of_pos_variables_);
315 for (
int i = 0; i < num_of_variables_; ++i) {
316 P_indptr->push_back(ind_p);
317 for (
const auto& row_data_pair : columns[i]) {
320 P_data->push_back(row_data_pair.second * 2.0);
321 P_indices->push_back(row_data_pair.first);
325 P_indptr->push_back(ind_p);
328void FemPosDeviationSqpOsqpInterface::CalculateOffset(
329 std::vector<std::pair<double, double>> points, std::vector<c_float>* q) {
330 q->resize(num_of_variables_);
331 for (
int i = 0; i < num_of_points_; ++i) {
332 const auto& ref_point_xy = ref_points_[i];
333 (*q)[2 * i] = -2.0 * weight_ref_deviation_ * ref_point_xy.first;
334 (*q)[2 * i + 1] = -2.0 * weight_ref_deviation_ * ref_point_xy.second;
336 for (
int i = 0; i < num_of_slack_variables_; ++i) {
337 (*q)[num_of_pos_variables_ + i] = weight_curvature_constraint_slack_var_;
339 AINFO <<
"FLAGS_enable_obstacle_potential_field: "
340 << FLAGS_enable_obstacle_potential_field
341 <<
" num_of_points_: " << num_of_points_
342 <<
"point_box_: " << point_box_.size();
343 if (FLAGS_enable_obstacle_potential_field &&
344 point_box_.size() == num_of_points_ - 1) {
345 AINFO <<
"use obstacle potential field";
346 for (
int i = 1; i < num_of_points_ - 1; ++i) {
349 point_box_[i].push_back(point_box_[i].front());
350 for (
int j = 0; j < 4; j++) {
351 Vec2d edge_vec(point_box_[i][j + 1].x() - point_box_[i][j].x(),
352 point_box_[i][j + 1].y() - point_box_[i][j].y());
353 Vec2d ego_vec(points[i].first - point_box_[i][j].x(),
354 points[i].second - point_box_[i][j].y());
355 grad_x += edge_vec.y() / edge_vec.CrossProd(ego_vec);
356 grad_y += -edge_vec.x() / edge_vec.CrossProd(ego_vec);
359 (*q)[2 * i] += FLAGS_sqp_obstacle_weight * grad_x;
360 (*q)[2 * i + 1] += FLAGS_sqp_obstacle_weight * grad_y;
366FemPosDeviationSqpOsqpInterface::CalculateLinearizedFemPosParams(
367 const std::vector<std::pair<double, double>>& points,
const size_t index) {
369 CHECK_LT(index, points.size() - 1);
371 double x_f = points[index - 1].first;
372 double x_m = points[index].first;
373 double x_l = points[index + 1].first;
374 double y_f = points[index - 1].second;
375 double y_m = points[index].second;
376 double y_l = points[index + 1].second;
378 double linear_term_x_f = 2.0 * x_f - 4.0 * x_m + 2.0 * x_l;
379 double linear_term_x_m = 8.0 * x_m - 4.0 * x_f - 4.0 * x_l;
380 double linear_term_x_l = 2.0 * x_l - 4.0 * x_m + 2.0 * x_f;
381 double linear_term_y_f = 2.0 * y_f - 4.0 * y_m + 2.0 * y_l;
382 double linear_term_y_m = 8.0 * y_m - 4.0 * y_f - 4.0 * y_l;
383 double linear_term_y_l = 2.0 * y_l - 4.0 * y_m + 2.0 * y_f;
385 double linear_approx = (-2.0 * x_m + x_f + x_l) * (-2.0 * x_m + x_f + x_l) +
386 (-2.0 * y_m + y_f + y_l) * (-2.0 * y_m + y_f + y_l) +
387 -x_f * linear_term_x_f + -x_m * linear_term_x_m +
388 -x_l * linear_term_x_l + -y_f * linear_term_y_f +
389 -y_m * linear_term_y_m + -y_l * linear_term_y_l;
391 return {linear_term_x_f, linear_term_y_f, linear_term_x_m, linear_term_y_m,
392 linear_term_x_l, linear_term_y_l, linear_approx};
395void FemPosDeviationSqpOsqpInterface::CalculateAffineConstraint(
396 const std::vector<std::pair<double, double>>& points,
397 std::vector<c_float>* A_data, std::vector<c_int>* A_indices,
398 std::vector<c_int>* A_indptr, std::vector<c_float>* lower_bounds,
399 std::vector<c_float>* upper_bounds) {
400 const double scale_factor = 1;
402 std::vector<std::vector<double>> lin_cache;
403 for (
int i = 1; i < num_of_points_ - 1; ++i) {
404 lin_cache.push_back(CalculateLinearizedFemPosParams(points, i));
407 std::vector<std::vector<std::pair<c_int, c_float>>> columns;
408 columns.resize(num_of_variables_);
410 for (
int i = 0; i < num_of_variables_; ++i) {
411 columns[i].emplace_back(i, 1.0);
414 for (
int i = num_of_pos_variables_; i < num_of_variables_; ++i) {
415 columns[i].emplace_back(i + num_of_slack_variables_, -1.0 * scale_factor);
418 for (
int i = 2; i < num_of_points_; ++i) {
420 columns[index].emplace_back(i - 2 + num_of_variables_,
421 lin_cache[i - 2][4] * scale_factor);
422 columns[index + 1].emplace_back(i - 2 + num_of_variables_,
423 lin_cache[i - 2][5] * scale_factor);
426 for (
int i = 1; i < num_of_points_ - 1; ++i) {
428 columns[index].emplace_back(i - 1 + num_of_variables_,
429 lin_cache[i - 1][2] * scale_factor);
430 columns[index + 1].emplace_back(i - 1 + num_of_variables_,
431 lin_cache[i - 1][3] * scale_factor);
434 for (
int i = 0; i < num_of_points_ - 2; ++i) {
436 columns[index].emplace_back(i + num_of_variables_,
437 lin_cache[i][0] * scale_factor);
438 columns[index + 1].emplace_back(i + num_of_variables_,
439 lin_cache[i][1] * scale_factor);
443 for (
int i = 0; i < num_of_variables_; ++i) {
444 A_indptr->push_back(ind_a);
445 for (
const auto& row_data_pair : columns[i]) {
446 A_data->push_back(row_data_pair.second);
447 A_indices->push_back(row_data_pair.first);
451 A_indptr->push_back(ind_a);
453 lower_bounds->resize(num_of_constraints_);
454 upper_bounds->resize(num_of_constraints_);
456 for (
int i = 0; i < num_of_points_; ++i) {
457 const auto& ref_point_xy = ref_points_[i];
458 (*upper_bounds)[i * 2] = ref_point_xy.first + bounds_around_refs_[i];
459 (*upper_bounds)[i * 2 + 1] = ref_point_xy.second + bounds_around_refs_[i];
460 (*lower_bounds)[i * 2] = ref_point_xy.first - bounds_around_refs_[i];
461 (*lower_bounds)[i * 2 + 1] = ref_point_xy.second - bounds_around_refs_[i];
464 for (
int i = 0; i < num_of_slack_variables_; ++i) {
465 (*upper_bounds)[num_of_pos_variables_ + i] = 1e20;
466 (*lower_bounds)[num_of_pos_variables_ + i] = 0.0;
469 double interval_sqr = average_interval_length_ * average_interval_length_;
470 double curvature_constraint_sqr = (interval_sqr * curvature_constraint_) *
471 (interval_sqr * curvature_constraint_);
472 for (
int i = 0; i < num_of_curvature_constraints_; ++i) {
473 (*upper_bounds)[num_of_variable_constraints_ + i] =
474 (curvature_constraint_sqr - lin_cache[i][6]) * scale_factor;
475 (*lower_bounds)[num_of_variable_constraints_ + i] = -1e20;
479void FemPosDeviationSqpOsqpInterface::SetPrimalWarmStart(
480 const std::vector<std::pair<double, double>>& points,
481 std::vector<c_float>* primal_warm_start) {
482 CHECK_EQ(points.size(),
static_cast<size_t>(num_of_points_));
483 CHECK_GT(points.size(), 1U);
486 primal_warm_start->resize(num_of_variables_);
487 for (
int i = 0; i < num_of_points_; ++i) {
488 (*primal_warm_start)[2 * i] = points[i].first;
489 (*primal_warm_start)[2 * i + 1] = points[i].second;
492 slack_.resize(num_of_slack_variables_);
493 for (
int i = 0; i < num_of_slack_variables_; ++i) {
494 (*primal_warm_start)[num_of_pos_variables_ + i] = slack_[i];
498 double total_length = 0.0;
499 auto pre_point = points.front();
500 for (
int i = 1; i < num_of_points_; ++i) {
501 const auto& cur_point = points[i];
502 total_length += std::sqrt((pre_point.first - cur_point.first) *
503 (pre_point.first - cur_point.first) +
504 (pre_point.second - cur_point.second) *
505 (pre_point.second - cur_point.second));
506 pre_point = cur_point;
508 average_interval_length_ = total_length / (num_of_points_ - 1);
511bool FemPosDeviationSqpOsqpInterface::OptimizeWithOsqp(
512 const std::vector<c_float>& primal_warm_start, OSQPWorkspace** work) {
513 osqp_warm_start_x(*work, primal_warm_start.data());
518 auto status = (*work)->info->status_val;
521 AERROR <<
"failed optimization status:\t" << (*work)->info->status;
525 if (status != 1 && status != 2) {
526 AERROR <<
"failed optimization status:\t" << (*work)->info->status;
531 opt_xy_.resize(num_of_points_);
532 slack_.resize(num_of_slack_variables_);
533 for (
int i = 0; i < num_of_points_; ++i) {
535 opt_xy_.at(i) = std::make_pair((*work)->solution->x[index],
536 (*work)->solution->x[index + 1]);
539 for (
int i = 0; i < num_of_slack_variables_; ++i) {
540 slack_.at(i) = (*work)->solution->x[num_of_pos_variables_ + i];
546double FemPosDeviationSqpOsqpInterface::CalculateConstraintViolation(
547 const std::vector<std::pair<double, double>>& points) {
548 CHECK_GT(points.size(), 2U);
550 double total_length = 0.0;
551 auto pre_point = points.front();
552 for (
int i = 1; i < num_of_points_; ++i) {
553 const auto& cur_point = points[i];
554 total_length += std::sqrt((pre_point.first - cur_point.first) *
555 (pre_point.first - cur_point.first) +
556 (pre_point.second - cur_point.second) *
557 (pre_point.second - cur_point.second));
558 pre_point = cur_point;
560 double average_interval_length = total_length / (num_of_points_ - 1);
561 double interval_sqr = average_interval_length * average_interval_length;
562 double curvature_constraint_sqr = (interval_sqr * curvature_constraint_) *
563 (interval_sqr * curvature_constraint_);
565 double max_cviolation = std::numeric_limits<double>::min();
566 for (
size_t i = 1; i < points.size() - 1; ++i) {
567 double x_f = points[i - 1].first;
568 double x_m = points[i].first;
569 double x_l = points[i + 1].first;
570 double y_f = points[i - 1].second;
571 double y_m = points[i].second;
572 double y_l = points[i + 1].second;
573 double cviolation = curvature_constraint_sqr -
574 (-2.0 * x_m + x_f + x_l) * (-2.0 * x_m + x_f + x_l) +
575 (-2.0 * y_m + y_f + y_l) * (-2.0 * y_m + y_f + y_l);
576 max_cviolation = max_cviolation < cviolation ? cviolation : max_cviolation;
578 return max_cviolation;
Planning module main class.