43 int num_path_points,
const std::function<
int(
int)>& circular_index,
44 const std::function<
void(
int,
int,
double)>& add_coefficient)
const {
48 for (
int point_idx = 0; point_idx < num_path_points; ++point_idx) {
49 int prev_point = circular_index(point_idx - 1);
50 int next_point = circular_index(point_idx + 1);
53 int x_prev = 2 * prev_point;
54 int x_current = 2 * point_idx;
55 int x_next = 2 * next_point;
65 int y_prev = 2 * prev_point + 1;
66 int y_current = 2 * point_idx + 1;
67 int y_next = 2 * next_point + 1;
90 std::vector<OSQPFloat>& constraint_values, std::vector<OSQPInt>& constraint_row_indices,
91 std::vector<OSQPInt>& constraint_col_indices, std::vector<OSQPFloat>& constraint_lower_bounds,
92 std::vector<OSQPFloat>& constraint_upper_bounds,
int& constraint_count,
93 const std::vector<PathPoint>& left,
const std::vector<PathPoint>& right,
int num_path_points,
94 double safety_margin)
const {
97 for (
int point_idx = 0; point_idx < num_path_points; ++point_idx) {
98 Eigen::Vector2d left_boundary_point(left[point_idx].position.x, left[point_idx].position.y);
99 Eigen::Vector2d right_boundary_point(right[point_idx].position.x, right[point_idx].position.y);
102 Eigen::Vector2d lateral_direction = (left_boundary_point - right_boundary_point).normalized();
105 constraint_row_indices.push_back(constraint_count);
106 constraint_col_indices.push_back(2 * point_idx);
107 constraint_values.push_back(lateral_direction.x());
109 constraint_row_indices.push_back(constraint_count);
110 constraint_col_indices.push_back(2 * point_idx + 1);
111 constraint_values.push_back(lateral_direction.y());
113 constraint_row_indices.push_back(constraint_count);
114 constraint_col_indices.push_back(2 * num_path_points + 2 * point_idx);
115 constraint_values.push_back(1.0);
117 double right_boundary_constraint = right_boundary_point.dot(lateral_direction) + safety_margin;
118 constraint_lower_bounds.push_back(right_boundary_constraint);
119 constraint_upper_bounds.push_back(OSQP_INFTY);
123 constraint_row_indices.push_back(constraint_count);
124 constraint_col_indices.push_back(2 * point_idx);
125 constraint_values.push_back(-lateral_direction.x());
127 constraint_row_indices.push_back(constraint_count);
128 constraint_col_indices.push_back(2 * point_idx + 1);
129 constraint_values.push_back(-lateral_direction.y());
131 constraint_row_indices.push_back(constraint_count);
132 constraint_col_indices.push_back(2 * num_path_points + 2 * point_idx + 1);
133 constraint_values.push_back(1.0);
135 double left_boundary_constraint = -left_boundary_point.dot(lateral_direction) + safety_margin;
136 constraint_lower_bounds.push_back(left_boundary_constraint);
137 constraint_upper_bounds.push_back(OSQP_INFTY);
162 const std::vector<OSQPInt>& row_indices,
163 const std::vector<OSQPInt>& col_indices,
164 int total_variables, std::vector<OSQPFloat>& csc_x,
165 std::vector<OSQPInt>& csc_i,
166 std::vector<OSQPInt>& csc_p)
const {
168 std::vector<std::vector<std::pair<OSQPInt, OSQPFloat>>> columns(total_variables);
169 for (
size_t entry = 0; entry < values.size(); ++entry) {
170 columns[col_indices[entry]].push_back({row_indices[entry], values[entry]});
174 for (
int col = 0; col < total_variables; ++col) {
175 std::sort(columns[col].begin(), columns[col].end(),
176 [](
const auto& a,
const auto& b) {
return a.first < b.first; });
180 csc_x.reserve(values.size());
181 csc_i.reserve(values.size());
182 csc_p.resize(total_variables + 1);
185 for (
int col = 0; col < total_variables; ++col) {
186 for (
const auto& [row, value] : columns[col]) {
187 csc_i.push_back(row);
188 csc_x.push_back(value);
190 csc_p[col + 1] = csc_x.size();
195 const std::vector<PathPoint>& left,
196 const std::vector<PathPoint>& right)
const {
197 if (center.size() != left.size() || center.size() != right.size() ||
198 left.size() != center.size()) {
199 RCLCPP_INFO(rclcpp::get_logger(
"rclcpp"),
200 "The splines have different sizes. Right - %ld, Left - %ld, Center - %ld",
201 center.size(), left.size(), right.size());
203 const int num_path_points = center.size();
206 if (num_path_points < 5) {
207 RCLCPP_INFO(rclcpp::get_logger(
"rclcpp"),
208 "Too few points for OSQP optimization (%d points). Minimum is 5.", num_path_points);
216 auto circular_index = [&](
int i) {
return (i + num_path_points) % num_path_points; };
220 const int num_slack_variables = 2 * num_path_points;
221 const int total_variables = 2 * num_path_points + num_slack_variables;
225 std::map<std::pair<int, int>,
double> quadratic_terms;
228 auto add_quadratic_coefficient = [&](
int row_idx,
int col_idx,
double coefficient) {
229 if (row_idx > col_idx) {
230 std::swap(row_idx, col_idx);
232 quadratic_terms[{row_idx, col_idx}] += coefficient;
240 std::vector<OSQPFloat> linear_objective(total_variables, 0.0);
243 std::vector<OSQPFloat> P_values;
244 std::vector<OSQPInt> P_row_indices, P_col_indices;
246 P_values.reserve(quadratic_terms.size());
247 P_row_indices.reserve(quadratic_terms.size());
248 P_col_indices.reserve(quadratic_terms.size());
250 for (
const auto& [indices, value] : quadratic_terms) {
251 P_row_indices.push_back(indices.first);
252 P_col_indices.push_back(indices.second);
253 P_values.push_back(value);
257 std::vector<OSQPFloat> constraint_values;
258 std::vector<OSQPInt> constraint_row_indices, constraint_col_indices;
259 std::vector<OSQPFloat> constraint_lower_bounds, constraint_upper_bounds;
261 int constraint_count = 0;
264 constraint_lower_bounds, constraint_upper_bounds, constraint_count, left,
265 right, num_path_points, safety_margin);
268 constraint_col_indices, constraint_lower_bounds,
269 constraint_upper_bounds, constraint_count, num_path_points);
271 const int total_constraints = constraint_count;
274 std::vector<OSQPFloat> P_x;
275 std::vector<OSQPInt> P_i;
276 std::vector<OSQPInt> P_p;
280 std::vector<OSQPFloat> A_x;
281 std::vector<OSQPInt> A_i;
282 std::vector<OSQPInt> A_p;
284 total_variables, A_x, A_i, A_p);
287 OSQPCscMatrix objective_matrix;
288 objective_matrix.m = total_variables;
289 objective_matrix.n = total_variables;
290 objective_matrix.nzmax = P_x.size();
291 objective_matrix.nz = -1;
292 objective_matrix.x = P_x.data();
293 objective_matrix.i = P_i.data();
294 objective_matrix.p = P_p.data();
296 OSQPCscMatrix constraint_matrix;
297 constraint_matrix.m = total_constraints;
298 constraint_matrix.n = total_variables;
299 constraint_matrix.nzmax = A_x.size();
300 constraint_matrix.nz = -1;
301 constraint_matrix.x = A_x.data();
302 constraint_matrix.i = A_i.data();
303 constraint_matrix.p = A_p.data();
306 OSQPSettings solver_settings;
307 ::osqp_set_default_settings(&solver_settings);
308 solver_settings.verbose = 1;
312 solver_settings.polishing = 1;
315 OSQPSolver* solver =
nullptr;
317 OSQPInt setup_status =
318 ::osqp_setup(&solver, &objective_matrix, linear_objective.data(), &constraint_matrix,
319 constraint_lower_bounds.data(), constraint_upper_bounds.data(), total_variables,
320 total_constraints, &solver_settings);
322 if (setup_status != 0) {
323 RCLCPP_ERROR(rclcpp::get_logger(
"rclcpp"),
"OSQP setup failed with status %lld", setup_status);
325 (void)::osqp_cleanup(solver);
331 OSQPInt solve_status = ::osqp_solve(solver);
334 std::vector<PathPoint> optimized_path = center;
336 if ((solver->solution !=
nullptr) && (solver->solution->x !=
nullptr)) {
337 for (
int point_idx = 0; point_idx < num_path_points; ++point_idx) {
338 optimized_path[point_idx].position.x = solver->solution->x[2 * point_idx];
339 optimized_path[point_idx].position.y = solver->solution->x[2 * point_idx + 1];
342 RCLCPP_DEBUG(rclcpp::get_logger(
"rclcpp"),
343 "OSQP optimization completed successfully with %d points (status: %lld)",
344 num_path_points, solve_status);
346 RCLCPP_WARN(rclcpp::get_logger(
"rclcpp"),
"OSQP solution is null, returning original path");
350 (void)::osqp_cleanup(solver);
352 return optimized_path;