Apollo 10.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, double ts, const Eigen::MatrixXd& ego,
34 const Eigen::MatrixXi& obstacles_edges_num, const size_t obstacles_num,
35 const Eigen::MatrixXd& obstacles_A, const Eigen::MatrixXd& obstacles_b,
36 const Eigen::MatrixXd& xWS,
37 const PlannerOpenSpaceConfig& planner_open_space_config)
38 : ts_(ts),
39 ego_(ego),
40 obstacles_edges_num_(obstacles_edges_num),
41 obstacles_A_(obstacles_A),
42 obstacles_b_(obstacles_b),
43 xWS_(xWS) {
44 ACHECK(horizon < std::numeric_limits<int>::max())
45 << "Invalid cast on horizon in open space planner";
46 horizon_ = static_cast<int>(horizon);
47 ACHECK(obstacles_num < std::numeric_limits<int>::max())
48 << "Invalid cast on obstacles_num in open space planner";
49 obstacles_num_ = static_cast<int>(obstacles_num);
50 w_ev_ = ego_(1, 0) + ego_(3, 0);
51 l_ev_ = ego_(0, 0) + ego_(2, 0);
52 g_ = {l_ev_ / 2, w_ev_ / 2, l_ev_ / 2, w_ev_ / 2};
53 offset_ = (ego_(0, 0) + ego_(2, 0)) / 2 - ego_(2, 0);
54 obstacles_edges_sum_ = obstacles_edges_num_.sum();
55 l_start_index_ = 0;
56 n_start_index_ = l_start_index_ + obstacles_edges_sum_ * (horizon_ + 1);
57 l_warm_up_ = Eigen::MatrixXd::Zero(obstacles_edges_sum_, horizon_ + 1);
58 n_warm_up_ = Eigen::MatrixXd::Zero(4 * obstacles_num_, horizon_ + 1);
59
60 // get_nlp_info
61 lambda_horizon_ = obstacles_edges_sum_ * (horizon_ + 1);
62
63 miu_horizon_ = obstacles_num_ * 4 * (horizon_ + 1);
64
65 // number of variables
66 num_of_variables_ = lambda_horizon_ + miu_horizon_;
67 // number of constraints
68 num_of_constraints_ = 3 * obstacles_num_ * (horizon_ + 1) + num_of_variables_;
69
70 min_safety_distance_ =
71 planner_open_space_config.dual_variable_warm_start_config()
73 check_mode_ =
74 planner_open_space_config.dual_variable_warm_start_config().debug_osqp();
75 osqp_config_ =
76 planner_open_space_config.dual_variable_warm_start_config().osqp_config();
77}
78
79void printMatrix(const int r, const int c, const std::vector<c_float>& P_data,
80 const std::vector<c_int>& P_indices,
81 const std::vector<c_int>& P_indptr) {
82 Eigen::MatrixXf tmp = Eigen::MatrixXf::Zero(r, c);
83
84 for (size_t i = 0; i < P_indptr.size() - 1; ++i) {
85 if (P_indptr[i] < 0 || P_indptr[i] >= static_cast<int>(P_indices.size())) {
86 continue;
87 }
88
89 for (auto idx = P_indptr[i]; idx < P_indptr[i + 1]; ++idx) {
90 int tmp_c = static_cast<int>(i);
91 int tmp_r = static_cast<int>(P_indices[idx]);
92 tmp(tmp_r, tmp_c) = static_cast<float>(P_data[idx]);
93 }
94 }
95
96 AINFO << "row number: " << r;
97 AINFO << "col number: " << c;
98 for (int i = 0; i < r; ++i) {
99 AINFO << "row number: " << i;
100 AINFO << tmp.row(i);
101 }
102}
103
105 const int r, const int c, const std::vector<c_float>& P_data,
106 const std::vector<c_int>& P_indices, const std::vector<c_int>& P_indptr) {
107 constraint_A_ = Eigen::MatrixXf::Zero(r, c);
108
109 for (size_t i = 0; i < P_indptr.size() - 1; ++i) {
110 if (P_indptr[i] < 0 || P_indptr[i] >= static_cast<int>(P_indices.size())) {
111 continue;
112 }
113
114 for (auto idx = P_indptr[i]; idx < P_indptr[i + 1]; ++idx) {
115 int tmp_c = static_cast<int>(i);
116 int tmp_r = static_cast<int>(P_indices[idx]);
117 constraint_A_(tmp_r, tmp_c) = static_cast<float>(P_data[idx]);
118 }
119 }
120}
121
123 int kNumParam = num_of_variables_;
124 int kNumConst = num_of_constraints_;
125
126 bool succ = true;
127 // assemble P, quadratic term in objective
128 std::vector<c_float> P_data;
129 std::vector<c_int> P_indices;
130 std::vector<c_int> P_indptr;
131 assemble_P(&P_data, &P_indices, &P_indptr);
132 if (check_mode_) {
133 AINFO << "print P_data in whole: ";
134 printMatrix(kNumParam, kNumParam, P_data, P_indices, P_indptr);
135 }
136 // assemble q, linear term in objective
137 c_float q[kNumParam];
138 for (int i = 0; i < kNumParam; ++i) {
139 q[i] = 0.0;
140 }
141
142 // assemble A, linear term in constraints
143 std::vector<c_float> A_data;
144 std::vector<c_int> A_indices;
145 std::vector<c_int> A_indptr;
146 assemble_constraint(&A_data, &A_indices, &A_indptr);
147 if (check_mode_) {
148 AINFO << "print A_data in whole: ";
149 printMatrix(kNumConst, kNumParam, A_data, A_indices, A_indptr);
150 assembleA(kNumConst, kNumParam, A_data, A_indices, A_indptr);
151 }
152
153 // assemble lb & ub
154 c_float lb[kNumConst];
155 c_float ub[kNumConst];
156 for (int i = 0; i < kNumConst; ++i) {
157 lb[i] = 0.0;
158 if (i >= 2 * obstacles_num_ * (horizon_ + 1) &&
159 i < 3 * obstacles_num_ * (horizon_ + 1)) {
160 lb[i] = min_safety_distance_;
161 }
162 if (i < 2 * obstacles_num_ * (horizon_ + 1)) {
163 ub[i] = 0.0;
164 } else {
165 ub[i] = 2e19;
166 }
167 }
168
169 // Problem settings
170 OSQPSettings* settings =
171 reinterpret_cast<OSQPSettings*>(c_malloc(sizeof(OSQPSettings)));
172
173 // Define Solver settings as default
174 osqp_set_default_settings(settings);
175 settings->alpha = osqp_config_.alpha(); // Change alpha parameter
176 settings->eps_abs = osqp_config_.eps_abs();
177 settings->eps_rel = osqp_config_.eps_rel();
178 settings->max_iter = osqp_config_.max_iter();
179 settings->polish = osqp_config_.polish();
180 settings->verbose = osqp_config_.osqp_debug_log();
181
182 // Populate data
183 OSQPData* data = reinterpret_cast<OSQPData*>(c_malloc(sizeof(OSQPData)));
184 data->n = kNumParam;
185 data->m = kNumConst;
186 data->P = csc_matrix(data->n, data->n, P_data.size(), P_data.data(),
187 P_indices.data(), P_indptr.data());
188 data->q = q;
189 data->A = csc_matrix(data->m, data->n, A_data.size(), A_data.data(),
190 A_indices.data(), A_indptr.data());
191 data->l = lb;
192 data->u = ub;
193
194 // Workspace
195 OSQPWorkspace* work = nullptr;
196 // osqp_setup(&work, data, settings);
197 work = osqp_setup(data, settings);
198
199 // Solve Problem
200 osqp_solve(work);
201
202 // check state
203 if (work->info->status_val != 1 && work->info->status_val != 2) {
204 AWARN << "OSQP dual warm up unsuccess, "
205 << "return status: " << work->info->status;
206 succ = false;
207 }
208
209 // extract primal results
210 int variable_index = 0;
211 // 1. lagrange constraint l, [0, obstacles_edges_sum_ - 1] * [0,
212 // horizon_l
213 for (int i = 0; i < horizon_ + 1; ++i) {
214 for (int j = 0; j < obstacles_edges_sum_; ++j) {
215 l_warm_up_(j, i) = work->solution->x[variable_index];
216 ++variable_index;
217 }
218 }
219
220 // 2. lagrange constraint n, [0, 4*obstacles_num-1] * [0, horizon_]
221 for (int i = 0; i < horizon_ + 1; ++i) {
222 for (int j = 0; j < 4 * obstacles_num_; ++j) {
223 n_warm_up_(j, i) = work->solution->x[variable_index];
224 ++variable_index;
225 }
226 }
227
228 succ = succ & (work->info->obj_val <= 1.0);
229
230 // Cleanup
231 osqp_cleanup(work);
232 c_free(data->A);
233 c_free(data->P);
234 c_free(data);
235 c_free(settings);
236
237 return succ;
238}
239
241 const Eigen::MatrixXd& l_warm_up, const Eigen::MatrixXd& n_warm_up) {
242 Eigen::MatrixXf x(num_of_variables_, 1);
243 Eigen::MatrixXf g(num_of_constraints_, 1);
244
245 // extract primal results
246 int variable_index = 0;
247 // 1. lagrange constraint l, [0, obstacles_edges_sum_ - 1] * [0,
248 // horizon_l
249 for (int i = 0; i < horizon_ + 1; ++i) {
250 for (int j = 0; j < obstacles_edges_sum_; ++j) {
251 x(variable_index, 0) = static_cast<float>(l_warm_up(j, i));
252 ++variable_index;
253 }
254 }
255
256 // 2. lagrange constraint n, [0, 4*obstacles_num-1] * [0, horizon_]
257 for (int i = 0; i < horizon_ + 1; ++i) {
258 for (int j = 0; j < 4 * obstacles_num_; ++j) {
259 x(variable_index, 0) = static_cast<float>(n_warm_up(j, i));
260 ++variable_index;
261 }
262 }
263
264 g = constraint_A_ * x;
265 int r1_index = 0;
266 int r2_index = 2 * obstacles_num_ * (horizon_ + 1);
267 int r3_index = 3 * obstacles_num_ * (horizon_ + 1);
268 int r4_index = 3 * obstacles_num_ * (horizon_ + 1) + lambda_horizon_;
269
270 for (int idx = r1_index; idx < r2_index; ++idx) {
271 if (std::abs(g(idx, 0)) > 1e-6) {
272 AERROR << "G' * mu + R' * A * lambda == 0 constraint fails, "
273 << "constraint_index: " << idx << ", g: " << g(idx, 0);
274 }
275 }
276
277 for (int idx = r2_index; idx < r3_index; ++idx) {
278 if (g(idx, 0) < min_safety_distance_) {
279 AERROR << "-g' * mu + (A * t - b) * lambda) >= d_min constraint fails, "
280 << "constraint_index: " << idx << ", g: " << g(idx, 0);
281 }
282 }
283
284 for (int idx = r3_index; idx < r4_index; ++idx) {
285 if (g(idx, 0) < 0) {
286 AERROR << "lambda box constraint fails, "
287 << "constraint_index: " << idx << ", g: " << g(idx, 0);
288 }
289 }
290
291 for (int idx = r4_index; idx < num_of_constraints_; ++idx) {
292 if (g(idx, 0) < 0) {
293 AERROR << "miu box constraint fails, "
294 << "constraint_index: " << idx << ", g: " << g(idx, 0);
295 }
296 }
297}
298
300 std::vector<c_float>* P_data, std::vector<c_int>* P_indices,
301 std::vector<c_int>* P_indptr) {
302 // the objective function is norm(A' * lambda)
303 std::vector<c_float> P_tmp;
304 int edges_counter = 0;
305
306 for (int j = 0; j < obstacles_num_; ++j) {
307 int current_edges_num = obstacles_edges_num_(j, 0);
308 Eigen::MatrixXd Aj;
309 Aj = obstacles_A_.block(edges_counter, 0, current_edges_num, 2);
310 // Eigen::MatrixXd AAj(current_edges_num, current_edges_num);
311 Aj = Aj * Aj.transpose();
312
313 CHECK_EQ(current_edges_num, Aj.cols());
314 CHECK_EQ(current_edges_num, Aj.rows());
315
316 for (int c = 0; c < current_edges_num; ++c) {
317 for (int r = 0; r < current_edges_num; ++r) {
318 P_tmp.emplace_back(Aj(r, c));
319 }
320 }
321
322 // Update index
323 edges_counter += current_edges_num;
324 }
325
326 int l_index = l_start_index_;
327 int first_row_location = 0;
328 // the objective function is norm(A' * lambda)
329 for (int i = 0; i < horizon_ + 1; ++i) {
330 edges_counter = 0;
331
332 for (auto item : P_tmp) {
333 P_data->emplace_back(item);
334 }
335 // current assume: stationary obstacles
336 for (int j = 0; j < obstacles_num_; ++j) {
337 int current_edges_num = obstacles_edges_num_(j, 0);
338
339 for (int c = 0; c < current_edges_num; ++c) {
340 P_indptr->emplace_back(first_row_location);
341 for (int r = 0; r < current_edges_num; ++r) {
342 P_indices->emplace_back(r + l_index);
343 }
344 first_row_location += current_edges_num;
345 }
346
347 // Update index
348 edges_counter += current_edges_num;
349 l_index += current_edges_num;
350 }
351 }
352
353 CHECK_EQ(P_indptr->size(), static_cast<size_t>(lambda_horizon_));
354 for (int i = lambda_horizon_; i < num_of_variables_ + 1; ++i) {
355 P_indptr->emplace_back(first_row_location);
356 }
357
358 CHECK_EQ(P_data->size(), P_indices->size());
359 CHECK_EQ(P_indptr->size(), static_cast<size_t>(num_of_variables_) + 1);
360}
361
363 std::vector<c_float>* A_data, std::vector<c_int>* A_indices,
364 std::vector<c_int>* A_indptr) {
365 /*
366 * The constraint matrix is as the form,
367 * |R' * A', G'|, #: 2 * obstacles_num_ * (horizon_ + 1)
368 * |A * t - b, -g|, #: obstacles_num_ * (horizon_ + 1)
369 * |I, 0|, #: num_of_lambda
370 * |0, I|, #: num_of_miu
371 */
372 int r1_index = 0;
373 int r2_index = 2 * obstacles_num_ * (horizon_ + 1);
374 int r3_index = 3 * obstacles_num_ * (horizon_ + 1);
375 int r4_index = 3 * obstacles_num_ * (horizon_ + 1) + lambda_horizon_;
376 int first_row_location = 0;
377
378 // lambda variables
379 for (int i = 0; i < horizon_ + 1; ++i) {
380 int edges_counter = 0;
381
382 Eigen::MatrixXd R(2, 2);
383 R << cos(xWS_(2, i)), sin(xWS_(2, i)), sin(xWS_(2, i)), cos(xWS_(2, i));
384
385 Eigen::MatrixXd t_trans(1, 2);
386 t_trans << (xWS_(0, i) + cos(xWS_(2, i)) * offset_),
387 (xWS_(1, i) + sin(xWS_(2, i)) * offset_);
388
389 // assume: stationary obstacles
390 for (int j = 0; j < obstacles_num_; ++j) {
391 int current_edges_num = obstacles_edges_num_(j, 0);
392 Eigen::MatrixXd Aj =
393 obstacles_A_.block(edges_counter, 0, current_edges_num, 2);
394 Eigen::MatrixXd bj =
395 obstacles_b_.block(edges_counter, 0, current_edges_num, 1);
396
397 Eigen::MatrixXd r1_block(2, current_edges_num);
398 r1_block = R * Aj.transpose();
399
400 Eigen::MatrixXd r2_block(1, current_edges_num);
401 r2_block = t_trans * Aj.transpose() - bj.transpose();
402
403 // insert into A matrix, col by col
404 for (int k = 0; k < current_edges_num; ++k) {
405 A_data->emplace_back(r1_block(0, k));
406 A_indices->emplace_back(r1_index);
407
408 A_data->emplace_back(r1_block(1, k));
409 A_indices->emplace_back(r1_index + 1);
410
411 A_data->emplace_back(r2_block(0, k));
412 A_indices->emplace_back(r2_index);
413
414 A_data->emplace_back(1.0);
415 A_indices->emplace_back(r3_index);
416 r3_index++;
417
418 A_indptr->emplace_back(first_row_location);
419 first_row_location += 4;
420 }
421
422 // Update index
423 edges_counter += current_edges_num;
424 r1_index += 2;
425 r2_index += 1;
426 }
427 }
428
429 // miu variables
430 // G: ((1, 0, -1, 0), (0, 1, 0, -1))
431 // g: g_
432 r1_index = 0;
433 r2_index = 2 * obstacles_num_ * (horizon_ + 1);
434 for (int i = 0; i < horizon_ + 1; ++i) {
435 for (int j = 0; j < obstacles_num_; ++j) {
436 for (int k = 0; k < 4; ++k) {
437 // update G
438 if (k < 2) {
439 A_data->emplace_back(1.0);
440 } else {
441 A_data->emplace_back(-1.0);
442 }
443 A_indices->emplace_back(r1_index + k % 2);
444
445 // update g'
446 A_data->emplace_back(-g_[k]);
447 A_indices->emplace_back(r2_index);
448
449 // update I
450 A_data->emplace_back(1.0);
451 A_indices->emplace_back(r4_index);
452 r4_index++;
453
454 // update col index
455 A_indptr->emplace_back(first_row_location);
456 first_row_location += 3;
457 }
458
459 // update index
460 r1_index += 2;
461 r2_index += 1;
462 }
463 }
464
465 A_indptr->emplace_back(first_row_location);
466
467 CHECK_EQ(A_data->size(), A_indices->size());
468 CHECK_EQ(A_indptr->size(), static_cast<size_t>(num_of_variables_) + 1);
469}
470
472 Eigen::MatrixXd* l_warm_up, Eigen::MatrixXd* n_warm_up) const {
473 *l_warm_up = l_warm_up_;
474 *n_warm_up = n_warm_up_;
475}
476} // namespace planning
477} // 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.
#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
Math-related util functions.
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