Apollo 11.0
自动驾驶开放平台
dual_variable_warm_start_osqp_interface.cc
浏览该文件的文档.
1/******************************************************************************
2 * Copyright 2018 The Apollo Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *****************************************************************************/
16
17/*
18 * @file
19 */
20
22
23#include "cyber/common/log.h"
28
29namespace apollo {
30namespace planning {
31
33 size_t horizon,
34 double ts,
35 const Eigen::MatrixXd& ego,
36 const Eigen::MatrixXi& obstacles_edges_num,
37 const size_t obstacles_num,
38 const Eigen::MatrixXd& obstacles_A,
39 const Eigen::MatrixXd& obstacles_b,
40 const Eigen::MatrixXd& xWS,
41 const PlannerOpenSpaceConfig& planner_open_space_config) :
42 ts_(ts),
43 ego_(ego),
44 obstacles_edges_num_(obstacles_edges_num),
45 obstacles_A_(obstacles_A),
46 obstacles_b_(obstacles_b),
47 xWS_(xWS) {
48 ACHECK(horizon < std::numeric_limits<int>::max()) << "Invalid cast on horizon in open space planner";
49 horizon_ = static_cast<int>(horizon);
50 ACHECK(obstacles_num < std::numeric_limits<int>::max()) << "Invalid cast on obstacles_num in open space planner";
51 obstacles_num_ = static_cast<int>(obstacles_num);
52 w_ev_ = ego_(1, 0) + ego_(3, 0);
53 l_ev_ = ego_(0, 0) + ego_(2, 0);
54 g_ = {l_ev_ / 2, w_ev_ / 2, l_ev_ / 2, w_ev_ / 2};
55 offset_ = (ego_(0, 0) + ego_(2, 0)) / 2 - ego_(2, 0);
56 obstacles_edges_sum_ = obstacles_edges_num_.sum();
57 l_start_index_ = 0;
58 n_start_index_ = l_start_index_ + obstacles_edges_sum_ * (horizon_ + 1);
59 l_warm_up_ = Eigen::MatrixXd::Zero(obstacles_edges_sum_, horizon_ + 1);
60 n_warm_up_ = Eigen::MatrixXd::Zero(4 * obstacles_num_, horizon_ + 1);
61
62 // get_nlp_info
63 lambda_horizon_ = obstacles_edges_sum_ * (horizon_ + 1);
64
65 miu_horizon_ = obstacles_num_ * 4 * (horizon_ + 1);
66
67 // number of variables
68 num_of_variables_ = lambda_horizon_ + miu_horizon_;
69 // number of constraints
70 num_of_constraints_ = 3 * obstacles_num_ * (horizon_ + 1) + num_of_variables_;
71
72 min_safety_distance_ = planner_open_space_config.dual_variable_warm_start_config().min_safety_distance();
73 check_mode_ = planner_open_space_config.dual_variable_warm_start_config().debug_osqp();
74 osqp_config_ = planner_open_space_config.dual_variable_warm_start_config().osqp_config();
75}
76
78 size_t horizon,
79 double ts,
80 const Eigen::MatrixXd& ego,
81 const Eigen::MatrixXi& obstacles_edges_num,
82 const size_t obstacles_num,
83 const Eigen::MatrixXd& obstacles_A,
84 const Eigen::MatrixXd& obstacles_b,
85 const Eigen::MatrixXd& xWS,
87 ts_(ts),
88 ego_(ego),
89 obstacles_edges_num_(obstacles_edges_num),
90 obstacles_A_(obstacles_A),
91 obstacles_b_(obstacles_b),
92 xWS_(xWS) {
93 ACHECK(horizon < std::numeric_limits<int>::max()) << "Invalid cast on horizon in open space planner";
94 horizon_ = static_cast<int>(horizon);
95 ACHECK(obstacles_num < std::numeric_limits<int>::max()) << "Invalid cast on obstacles_num in open space planner";
96 obstacles_num_ = static_cast<int>(obstacles_num);
97 w_ev_ = ego_(1, 0) + ego_(3, 0);
98 l_ev_ = ego_(0, 0) + ego_(2, 0);
99 g_ = {l_ev_ / 2, w_ev_ / 2, l_ev_ / 2, w_ev_ / 2};
100 offset_ = (ego_(0, 0) + ego_(2, 0)) / 2 - ego_(2, 0);
101 obstacles_edges_sum_ = obstacles_edges_num_.sum();
102 l_start_index_ = 0;
103 n_start_index_ = l_start_index_ + obstacles_edges_sum_ * (horizon_ + 1);
104 l_warm_up_ = Eigen::MatrixXd::Zero(obstacles_edges_sum_, horizon_ + 1);
105 n_warm_up_ = Eigen::MatrixXd::Zero(4 * obstacles_num_, horizon_ + 1);
106
107 // get_nlp_info
108 lambda_horizon_ = obstacles_edges_sum_ * (horizon_ + 1);
109
110 miu_horizon_ = obstacles_num_ * 4 * (horizon_ + 1);
111
112 // number of variables
113 num_of_variables_ = lambda_horizon_ + miu_horizon_;
114 // number of constraints
115 num_of_constraints_ = 3 * obstacles_num_ * (horizon_ + 1) + num_of_variables_;
116
117 min_safety_distance_ = DualVariableWarmStartConfig.min_safety_distance();
120}
121
123 const int r,
124 const int c,
125 const std::vector<c_float>& P_data,
126 const std::vector<c_int>& P_indices,
127 const std::vector<c_int>& P_indptr) {
128 Eigen::MatrixXf tmp = Eigen::MatrixXf::Zero(r, c);
129
130 for (size_t i = 0; i < P_indptr.size() - 1; ++i) {
131 if (P_indptr[i] < 0 || P_indptr[i] >= static_cast<int>(P_indices.size())) {
132 continue;
133 }
134
135 for (auto idx = P_indptr[i]; idx < P_indptr[i + 1]; ++idx) {
136 int tmp_c = static_cast<int>(i);
137 int tmp_r = static_cast<int>(P_indices[idx]);
138 tmp(tmp_r, tmp_c) = static_cast<float>(P_data[idx]);
139 }
140 }
141
142 AINFO << "row number: " << r;
143 AINFO << "col number: " << c;
144 for (int i = 0; i < r; ++i) {
145 AINFO << "row number: " << i;
146 AINFO << tmp.row(i);
147 }
148}
149
151 const int r,
152 const int c,
153 const std::vector<c_float>& P_data,
154 const std::vector<c_int>& P_indices,
155 const std::vector<c_int>& P_indptr) {
156 constraint_A_ = Eigen::MatrixXf::Zero(r, c);
157
158 for (size_t i = 0; i < P_indptr.size() - 1; ++i) {
159 if (P_indptr[i] < 0 || P_indptr[i] >= static_cast<int>(P_indices.size())) {
160 continue;
161 }
162
163 for (auto idx = P_indptr[i]; idx < P_indptr[i + 1]; ++idx) {
164 int tmp_c = static_cast<int>(i);
165 int tmp_r = static_cast<int>(P_indices[idx]);
166 constraint_A_(tmp_r, tmp_c) = static_cast<float>(P_data[idx]);
167 }
168 }
169}
170
172 int kNumParam = num_of_variables_;
173 int kNumConst = num_of_constraints_;
174
175 bool succ = true;
176 // assemble P, quadratic term in objective
177 std::vector<c_float> P_data;
178 std::vector<c_int> P_indices;
179 std::vector<c_int> P_indptr;
180 assemble_P(&P_data, &P_indices, &P_indptr);
181 if (check_mode_) {
182 AINFO << "print P_data in whole: ";
183 printMatrix(kNumParam, kNumParam, P_data, P_indices, P_indptr);
184 }
185 // assemble q, linear term in objective
186 c_float q[kNumParam];
187 for (int i = 0; i < kNumParam; ++i) {
188 q[i] = 0.0;
189 }
190
191 // assemble A, linear term in constraints
192 std::vector<c_float> A_data;
193 std::vector<c_int> A_indices;
194 std::vector<c_int> A_indptr;
195 assemble_constraint(&A_data, &A_indices, &A_indptr);
196 if (check_mode_) {
197 AINFO << "print A_data in whole: ";
198 printMatrix(kNumConst, kNumParam, A_data, A_indices, A_indptr);
199 assembleA(kNumConst, kNumParam, A_data, A_indices, A_indptr);
200 }
201
202 // assemble lb & ub
203 c_float lb[kNumConst];
204 c_float ub[kNumConst];
205 for (int i = 0; i < kNumConst; ++i) {
206 lb[i] = 0.0;
207 if (i >= 2 * obstacles_num_ * (horizon_ + 1) && i < 3 * obstacles_num_ * (horizon_ + 1)) {
208 lb[i] = min_safety_distance_;
209 }
210 if (i < 2 * obstacles_num_ * (horizon_ + 1)) {
211 ub[i] = 0.0;
212 } else {
213 ub[i] = 2e19;
214 }
215 }
216
217 // Problem settings
218 OSQPSettings* settings = reinterpret_cast<OSQPSettings*>(c_malloc(sizeof(OSQPSettings)));
219
220 // Define Solver settings as default
221 osqp_set_default_settings(settings);
222 settings->alpha = osqp_config_.alpha(); // Change alpha parameter
223 settings->eps_abs = osqp_config_.eps_abs();
224 settings->eps_rel = osqp_config_.eps_rel();
225 settings->max_iter = osqp_config_.max_iter();
226 settings->polish = osqp_config_.polish();
227 settings->verbose = osqp_config_.osqp_debug_log();
228
229 // Populate data
230 OSQPData* data = reinterpret_cast<OSQPData*>(c_malloc(sizeof(OSQPData)));
231 data->n = kNumParam;
232 data->m = kNumConst;
233 data->P = csc_matrix(data->n, data->n, P_data.size(), P_data.data(), P_indices.data(), P_indptr.data());
234 data->q = q;
235 data->A = csc_matrix(data->m, data->n, A_data.size(), A_data.data(), A_indices.data(), A_indptr.data());
236 data->l = lb;
237 data->u = ub;
238
239 // Workspace
240 OSQPWorkspace* work = nullptr;
241 // osqp_setup(&work, data, settings);
242 work = osqp_setup(data, settings);
243
244 // Solve Problem
245 osqp_solve(work);
246
247 // check state
248 if (work->info->status_val != 1 && work->info->status_val != 2) {
249 AWARN << "OSQP dual warm up unsuccess, "
250 << "return status: " << work->info->status;
251 succ = false;
252 }
253
254 // extract primal results
255 int variable_index = 0;
256 // 1. lagrange constraint l, [0, obstacles_edges_sum_ - 1] * [0,
257 // horizon_l
258 for (int i = 0; i < horizon_ + 1; ++i) {
259 for (int j = 0; j < obstacles_edges_sum_; ++j) {
260 l_warm_up_(j, i) = work->solution->x[variable_index];
261 ++variable_index;
262 }
263 }
264
265 // 2. lagrange constraint n, [0, 4*obstacles_num-1] * [0, horizon_]
266 for (int i = 0; i < horizon_ + 1; ++i) {
267 for (int j = 0; j < 4 * obstacles_num_; ++j) {
268 n_warm_up_(j, i) = work->solution->x[variable_index];
269 ++variable_index;
270 }
271 }
272
273 succ = succ & (work->info->obj_val <= 1.0);
274
275 // Cleanup
276 osqp_cleanup(work);
277 c_free(data->A);
278 c_free(data->P);
279 c_free(data);
280 c_free(settings);
281
282 return succ;
283}
284
286 const Eigen::MatrixXd& l_warm_up,
287 const Eigen::MatrixXd& n_warm_up) {
288 Eigen::MatrixXf x(num_of_variables_, 1);
289 Eigen::MatrixXf g(num_of_constraints_, 1);
290
291 // extract primal results
292 int variable_index = 0;
293 // 1. lagrange constraint l, [0, obstacles_edges_sum_ - 1] * [0,
294 // horizon_l
295 for (int i = 0; i < horizon_ + 1; ++i) {
296 for (int j = 0; j < obstacles_edges_sum_; ++j) {
297 x(variable_index, 0) = static_cast<float>(l_warm_up(j, i));
298 ++variable_index;
299 }
300 }
301
302 // 2. lagrange constraint n, [0, 4*obstacles_num-1] * [0, horizon_]
303 for (int i = 0; i < horizon_ + 1; ++i) {
304 for (int j = 0; j < 4 * obstacles_num_; ++j) {
305 x(variable_index, 0) = static_cast<float>(n_warm_up(j, i));
306 ++variable_index;
307 }
308 }
309
310 g = constraint_A_ * x;
311 int r1_index = 0;
312 int r2_index = 2 * obstacles_num_ * (horizon_ + 1);
313 int r3_index = 3 * obstacles_num_ * (horizon_ + 1);
314 int r4_index = 3 * obstacles_num_ * (horizon_ + 1) + lambda_horizon_;
315
316 for (int idx = r1_index; idx < r2_index; ++idx) {
317 if (std::abs(g(idx, 0)) > 1e-6) {
318 AERROR << "G' * mu + R' * A * lambda == 0 constraint fails, "
319 << "constraint_index: " << idx << ", g: " << g(idx, 0);
320 }
321 }
322
323 for (int idx = r2_index; idx < r3_index; ++idx) {
324 if (g(idx, 0) < min_safety_distance_) {
325 AERROR << "-g' * mu + (A * t - b) * lambda) >= d_min constraint fails, "
326 << "constraint_index: " << idx << ", g: " << g(idx, 0);
327 }
328 }
329
330 for (int idx = r3_index; idx < r4_index; ++idx) {
331 if (g(idx, 0) < 0) {
332 AERROR << "lambda box constraint fails, "
333 << "constraint_index: " << idx << ", g: " << g(idx, 0);
334 }
335 }
336
337 for (int idx = r4_index; idx < num_of_constraints_; ++idx) {
338 if (g(idx, 0) < 0) {
339 AERROR << "miu box constraint fails, "
340 << "constraint_index: " << idx << ", g: " << g(idx, 0);
341 }
342 }
343}
344
346 std::vector<c_float>* P_data,
347 std::vector<c_int>* P_indices,
348 std::vector<c_int>* P_indptr) {
349 // the objective function is norm(A' * lambda)
350 std::vector<c_float> P_tmp;
351 int edges_counter = 0;
352
353 for (int j = 0; j < obstacles_num_; ++j) {
354 int current_edges_num = obstacles_edges_num_(j, 0);
355 Eigen::MatrixXd Aj;
356 Aj = obstacles_A_.block(edges_counter, 0, current_edges_num, 2);
357 // Eigen::MatrixXd AAj(current_edges_num, current_edges_num);
358 Aj = Aj * Aj.transpose();
359
360 CHECK_EQ(current_edges_num, Aj.cols());
361 CHECK_EQ(current_edges_num, Aj.rows());
362
363 for (int c = 0; c < current_edges_num; ++c) {
364 for (int r = 0; r < current_edges_num; ++r) {
365 P_tmp.emplace_back(Aj(r, c));
366 }
367 }
368
369 // Update index
370 edges_counter += current_edges_num;
371 }
372
373 int l_index = l_start_index_;
374 int first_row_location = 0;
375 // the objective function is norm(A' * lambda)
376 for (int i = 0; i < horizon_ + 1; ++i) {
377 edges_counter = 0;
378
379 for (auto item : P_tmp) {
380 P_data->emplace_back(item);
381 }
382 // current assume: stationary obstacles
383 for (int j = 0; j < obstacles_num_; ++j) {
384 int current_edges_num = obstacles_edges_num_(j, 0);
385
386 for (int c = 0; c < current_edges_num; ++c) {
387 P_indptr->emplace_back(first_row_location);
388 for (int r = 0; r < current_edges_num; ++r) {
389 P_indices->emplace_back(r + l_index);
390 }
391 first_row_location += current_edges_num;
392 }
393
394 // Update index
395 edges_counter += current_edges_num;
396 l_index += current_edges_num;
397 }
398 }
399
400 CHECK_EQ(P_indptr->size(), static_cast<size_t>(lambda_horizon_));
401 for (int i = lambda_horizon_; i < num_of_variables_ + 1; ++i) {
402 P_indptr->emplace_back(first_row_location);
403 }
404
405 CHECK_EQ(P_data->size(), P_indices->size());
406 CHECK_EQ(P_indptr->size(), static_cast<size_t>(num_of_variables_) + 1);
407}
408
410 std::vector<c_float>* A_data,
411 std::vector<c_int>* A_indices,
412 std::vector<c_int>* A_indptr) {
413 /*
414 * The constraint matrix is as the form,
415 * |R' * A', G'|, #: 2 * obstacles_num_ * (horizon_ + 1)
416 * |A * t - b, -g|, #: obstacles_num_ * (horizon_ + 1)
417 * |I, 0|, #: num_of_lambda
418 * |0, I|, #: num_of_miu
419 */
420 int r1_index = 0;
421 int r2_index = 2 * obstacles_num_ * (horizon_ + 1);
422 int r3_index = 3 * obstacles_num_ * (horizon_ + 1);
423 int r4_index = 3 * obstacles_num_ * (horizon_ + 1) + lambda_horizon_;
424 int first_row_location = 0;
425
426 // lambda variables
427 for (int i = 0; i < horizon_ + 1; ++i) {
428 int edges_counter = 0;
429
430 Eigen::MatrixXd R(2, 2);
431 R << cos(xWS_(2, i)), sin(xWS_(2, i)), sin(xWS_(2, i)), cos(xWS_(2, i));
432
433 Eigen::MatrixXd t_trans(1, 2);
434 t_trans << (xWS_(0, i) + cos(xWS_(2, i)) * offset_), (xWS_(1, i) + sin(xWS_(2, i)) * offset_);
435
436 // assume: stationary obstacles
437 for (int j = 0; j < obstacles_num_; ++j) {
438 int current_edges_num = obstacles_edges_num_(j, 0);
439 Eigen::MatrixXd Aj = obstacles_A_.block(edges_counter, 0, current_edges_num, 2);
440 Eigen::MatrixXd bj = obstacles_b_.block(edges_counter, 0, current_edges_num, 1);
441
442 Eigen::MatrixXd r1_block(2, current_edges_num);
443 r1_block = R * Aj.transpose();
444
445 Eigen::MatrixXd r2_block(1, current_edges_num);
446 r2_block = t_trans * Aj.transpose() - bj.transpose();
447
448 // insert into A matrix, col by col
449 for (int k = 0; k < current_edges_num; ++k) {
450 A_data->emplace_back(r1_block(0, k));
451 A_indices->emplace_back(r1_index);
452
453 A_data->emplace_back(r1_block(1, k));
454 A_indices->emplace_back(r1_index + 1);
455
456 A_data->emplace_back(r2_block(0, k));
457 A_indices->emplace_back(r2_index);
458
459 A_data->emplace_back(1.0);
460 A_indices->emplace_back(r3_index);
461 r3_index++;
462
463 A_indptr->emplace_back(first_row_location);
464 first_row_location += 4;
465 }
466
467 // Update index
468 edges_counter += current_edges_num;
469 r1_index += 2;
470 r2_index += 1;
471 }
472 }
473
474 // miu variables
475 // G: ((1, 0, -1, 0), (0, 1, 0, -1))
476 // g: g_
477 r1_index = 0;
478 r2_index = 2 * obstacles_num_ * (horizon_ + 1);
479 for (int i = 0; i < horizon_ + 1; ++i) {
480 for (int j = 0; j < obstacles_num_; ++j) {
481 for (int k = 0; k < 4; ++k) {
482 // update G
483 if (k < 2) {
484 A_data->emplace_back(1.0);
485 } else {
486 A_data->emplace_back(-1.0);
487 }
488 A_indices->emplace_back(r1_index + k % 2);
489
490 // update g'
491 A_data->emplace_back(-g_[k]);
492 A_indices->emplace_back(r2_index);
493
494 // update I
495 A_data->emplace_back(1.0);
496 A_indices->emplace_back(r4_index);
497 r4_index++;
498
499 // update col index
500 A_indptr->emplace_back(first_row_location);
501 first_row_location += 3;
502 }
503
504 // update index
505 r1_index += 2;
506 r2_index += 1;
507 }
508 }
509
510 A_indptr->emplace_back(first_row_location);
511
512 CHECK_EQ(A_data->size(), A_indices->size());
513 CHECK_EQ(A_indptr->size(), static_cast<size_t>(num_of_variables_) + 1);
514}
515
517 Eigen::MatrixXd* l_warm_up,
518 Eigen::MatrixXd* n_warm_up) const {
519 *l_warm_up = l_warm_up_;
520 *n_warm_up = n_warm_up_;
521}
522} // namespace planning
523} // namespace apollo
DualVariableWarmStartOSQPInterface(size_t horizon, double ts, const Eigen::MatrixXd &ego, const Eigen::MatrixXi &obstacles_edges_num, const size_t obstacles_num, const Eigen::MatrixXd &obstacles_A, const Eigen::MatrixXd &obstacles_b, const Eigen::MatrixXd &xWS, const PlannerOpenSpaceConfig &planner_open_space_config)
void assemble_P(std::vector< c_float > *P_data, std::vector< c_int > *P_indices, std::vector< c_int > *P_indptr)
void check_solution(const Eigen::MatrixXd &l_warm_up, const Eigen::MatrixXd &n_warm_up)
void assembleA(const int r, const int c, const std::vector< c_float > &P_data, const std::vector< c_int > &P_indices, const std::vector< c_int > &P_indptr)
void assemble_constraint(std::vector< c_float > *A_data, std::vector< c_int > *A_indices, std::vector< c_int > *A_indptr)
void get_optimization_results(Eigen::MatrixXd *l_warm_up, Eigen::MatrixXd *n_warm_up) const
Planning module main class.
Math-related util functions.
#define ACHECK(cond)
Definition log.h:80
#define AERROR
Definition log.h:44
#define AINFO
Definition log.h:42
#define AWARN
Definition log.h:43
Some util functions.
void printMatrix(const int r, const int c, const std::vector< c_float > &P_data, const std::vector< c_int > &P_indices, const std::vector< c_int > &P_indptr)
class register implement
Definition arena_queue.h:37
optional DualVariableWarmStartConfig dual_variable_warm_start_config