trajectory_optimization v1.4.0
Loading...
Searching...
No Matches
utils.cpp
Go to the documentation of this file.
1// Copyright Institute for Automotive Engineering (ika), RWTH Aachen University
2// SPDX-License-Identifier: Apache-2.0
3
4#include <algorithm>
5#include <cmath>
6#include <cstdio>
7
9
10#include <blasfeo_d_aux_ext_dep.h> // for printing dense matrices
11
13
14double TrajectoryOptimizationNode::wrap_angle_rad(double angle_rad, double min_val, double max_val) {
15 double capped_angle_rad = angle_rad;
16 while (capped_angle_rad > max_val) capped_angle_rad -= 2 * M_PI;
17 while (capped_angle_rad < min_val) capped_angle_rad += 2 * M_PI;
18 return capped_angle_rad;
19}
20
22 const std::vector<double>& X, const std::vector<double>& Y, const double& desired_x, double& output_y, bool wrap_angle) {
23 if (desired_x == X.front()) {
24 RCLCPP_DEBUG(get_logger(), "Desired Time is equal to Time-Min of the given vector!");
25 output_y = Y.front();
26 return true;
27 } else if (desired_x == X.back()) {
28 RCLCPP_DEBUG(get_logger(), "Desired Time is equal to Time-Max of the given vector!");
29 output_y = Y.back();
30 return true;
31 } else if (desired_x < *min_element(X.begin(), X.end())) {
32 RCLCPP_WARN(get_logger(), "Desired Time is smaller than Time-Min of the given vector! Using first valid value.");
33 RCLCPP_DEBUG(get_logger(), "Desired Time: %f s", desired_x);
34 RCLCPP_DEBUG(get_logger(), "Time-Min: %f s", *min_element(X.begin(), X.end()));
35 output_y = Y.front();
36 return false;
37 } else if (desired_x > *max_element(X.begin(), X.end())) {
38 RCLCPP_WARN(get_logger(), "Desired Time is greater than Time-Max of the given vector! Using last valid value.");
39 RCLCPP_DEBUG(get_logger(), "Desired Time: %f s", desired_x);
40 RCLCPP_DEBUG(get_logger(), "Time-Max: %f s", *max_element(X.begin(), X.end()));
41 output_y = Y.back();
42 return false;
43 } else if (X.size() != Y.size()) {
44 RCLCPP_ERROR(get_logger(), "Input vectors don't have the same length!");
45 return false;
46 }
47
48 //go through array and search for sampling points
49 size_t i = 0;
50 for (i = 0; i < X.size(); i++) {
51 if (X[i] < desired_x) {
52 continue;
53 } else if (X[i] == desired_x) {
54 output_y = Y[i];
55 return true;
56 } else {
57 break;
58 }
59 }
60 double diff = Y[i] - Y[i - 1];
61 if (wrap_angle) {
62 diff = wrap_angle_rad(diff);
63 }
64 output_y = Y[i - 1] + (diff / (X[i] - X[i - 1])) * (desired_x - X[i - 1]);
65 if (wrap_angle) {
66 output_y = wrap_angle_rad(output_y);
67 }
68 return true;
69}
70
71bool TrajectoryOptimizationNode::trajectory2outputFrame(trajectory_planning_msgs::msg::Trajectory& trajectory) {
73 trajectory_planning_msgs::msg::Trajectory tf_trajectory;
74 try {
75 tf_trajectory = tf2_buffer_->transform(trajectory, trajectory_frame_id_, tf2::durationFromSec(0.01));
76 } catch (tf2::TransformException& ex) {
77 RCLCPP_WARN(this->get_logger(), "Transformation into output frame is not available. Publishing no trajectory. Ex: %s",
78 ex.what());
79 return false;
80 }
81 trajectory = tf_trajectory;
82 }
83 return true;
84}
85
86void TrajectoryOptimizationNode::keepNClosestObjects(perception_msgs::msg::ObjectList& object_list, const int n_objects) {
87 // calculate distance to each object
88 std::vector<double> distances;
89 for (size_t i = 0; i < object_list.objects.size(); ++i) {
90 double distance = std::sqrt(std::pow(perception_msgs::object_access::getX(object_list.objects[i]), 2) +
91 std::pow(perception_msgs::object_access::getY(object_list.objects[i]), 2));
92 distances.push_back(distance);
93 }
94
95 // sort objects by distance
96 std::vector<size_t> indices_sorted_by_distance(distances.size());
97 std::iota(indices_sorted_by_distance.begin(), indices_sorted_by_distance.end(), 0);
98 std::sort(indices_sorted_by_distance.begin(), indices_sorted_by_distance.end(),
99 [&distances](size_t i1, size_t i2) { return distances[i1] < distances[i2]; });
100
101 // keep only the closest objects
102 std::vector<perception_msgs::msg::Object> closest_objects;
103 const auto n_objects_to_keep = std::min(static_cast<size_t>(n_objects), indices_sorted_by_distance.size());
104 int i = 0;
105 while (closest_objects.size() < static_cast<size_t>(n_objects_to_keep)) {
106 if (static_cast<size_t>(i) >= indices_sorted_by_distance.size()) break;
107 // ignore object with negative x-coordinate (behind the ego vehicle)
108 if (perception_msgs::object_access::getX(object_list.objects[indices_sorted_by_distance[i]]) > 0.0) {
109 closest_objects.push_back(object_list.objects[indices_sorted_by_distance[i]]);
110 }
111 ++i;
112 }
113 object_list.objects = closest_objects;
114}
115
117 const double x, const double y, const double yaw, const double length, const double width) {
118 uint8_t n_circles = 1;
119 if (length <= 0.0 || width <= 0.0) {
120 RCLCPP_WARN(get_logger(), "Invalid bounding box dimensions: length = %f, width = %f. Setting n_circles = 1.", length, width);
121 } else {
122 double aspect_ratio = length / width;
123 if (aspect_ratio > 8.0) {
124 n_circles = 9;
125 } else if (aspect_ratio > 6.0) {
126 n_circles = 7;
127 } else if (aspect_ratio > 4.0) {
128 n_circles = 5;
129 } else if (aspect_ratio > 1.8) {
130 n_circles = 3;
131 } else if (aspect_ratio > 1.3) {
132 n_circles = 2;
133 } else {
134 n_circles = 1;
135 }
136 }
137
138 double radius = std::sqrt(std::pow(length / (2 * n_circles), 2) + std::pow(width / 2.0, 2));
139
140 std::vector<double> circles(p_obstacle_circles_shape_[1] * n_circles);
141
142 for (int i = 0; i < n_circles; i++) {
143 double lon_offset = -length / 2 + (2 * i + 1) * length / (2 * n_circles);
144 double x_offset = lon_offset * std::cos(yaw);
145 double y_offset = lon_offset * std::sin(yaw);
146 circles[p_obstacle_circles_shape_[1] * i + 0] = x + x_offset;
147 circles[p_obstacle_circles_shape_[1] * i + 1] = y + y_offset;
148 circles[p_obstacle_circles_shape_[1] * i + 2] = radius;
149 }
150
151 return circles;
152}
153
154std::vector<std::pair<double, double>> TrajectoryOptimizationNode::normalBoundaryDistance(
155 const trajectory_planning_msgs::msg::Trajectory& reference_trajectory, const route_planning_msgs::msg::Route& route) {
156 const double NO_BOUNDARY_DISTANCE = 1e6; // finite sentinel avoids NaNs during reference-path interpolation
157 constexpr double MAX_ROUTE_S_DIFFERENCE = 20.0;
158 constexpr double LOOK_BEHIND_REMAINING_ROUTE_S = 2.0;
159
160 struct Boundaries {
161 std::vector<std::pair<double, double>> min_normal_distances;
162 std::vector<Eigen::Vector2d> left_boundary_points;
163 std::vector<Eigen::Vector2d> right_boundary_points;
164 std::vector<double> left_boundary_route_s;
165 std::vector<double> right_boundary_route_s;
166 std::vector<Eigen::Vector2d> left_boundary_intersections;
167 std::vector<Eigen::Vector2d> right_boundary_intersections;
168 };
169
170 Boundaries boundaries;
171 auto route_elements = route_planning_msgs::route_access::getRemainingRouteElements(route, true);
172 const int ref_sample_size = trajectory_planning_msgs::trajectory_access::getSamplePointSize(reference_trajectory);
173
174 if (route_elements.empty()) {
176 RCLCPP_WARN(get_logger(), "Remaining route is empty. Do not constrain boundaries.");
177 }
178 for (int i = 0; i < ref_sample_size; ++i) {
179 boundaries.min_normal_distances.emplace_back(NO_BOUNDARY_DISTANCE, NO_BOUNDARY_DISTANCE);
180 }
181 return boundaries.min_normal_distances;
182 }
183
184 const double current_route_s = route_elements.front().s;
185 const size_t current_route_index = std::min(static_cast<size_t>(route.current_route_element_idx), route.route_elements.size());
186 size_t overlap_begin = current_route_index;
187 while (overlap_begin > 0 && current_route_s - route.route_elements[overlap_begin - 1].s <= LOOK_BEHIND_REMAINING_ROUTE_S) {
188 --overlap_begin;
189 }
190 route_elements.insert(route_elements.begin(), route.route_elements.begin() + static_cast<std::ptrdiff_t>(overlap_begin),
191 route.route_elements.begin() + static_cast<std::ptrdiff_t>(current_route_index));
192
193 boundaries.left_boundary_points.reserve(route_elements.size());
194 boundaries.right_boundary_points.reserve(route_elements.size());
195 boundaries.left_boundary_route_s.reserve(route_elements.size());
196 boundaries.right_boundary_route_s.reserve(route_elements.size());
197 boundaries.min_normal_distances.reserve(ref_sample_size);
198 boundaries.left_boundary_intersections.reserve(ref_sample_size);
199 boundaries.right_boundary_intersections.reserve(ref_sample_size);
200
201 for (const auto& route_element : route_elements) {
202 if (route_element.is_enriched) {
204 route_planning_msgs::msg::LaneElement suggested_lane =
205 route_planning_msgs::route_access::getSuggestedLaneElement(route_element);
206 boundaries.left_boundary_points.emplace_back(suggested_lane.left_boundary.point.x, suggested_lane.left_boundary.point.y);
207 boundaries.right_boundary_points.emplace_back(suggested_lane.right_boundary.point.x,
208 suggested_lane.right_boundary.point.y);
209 boundaries.left_boundary_route_s.push_back(route_element.s);
210 boundaries.right_boundary_route_s.push_back(route_element.s);
212 const auto& lane_elements = route_element.lane_elements;
213 if (!lane_elements.empty()) {
214 boundaries.left_boundary_points.emplace_back(lane_elements.front().left_boundary.point.x,
215 lane_elements.front().left_boundary.point.y);
216 boundaries.right_boundary_points.emplace_back(lane_elements.back().right_boundary.point.x,
217 lane_elements.back().right_boundary.point.y);
218 boundaries.left_boundary_route_s.push_back(route_element.s);
219 boundaries.right_boundary_route_s.push_back(route_element.s);
220 }
222 boundaries.left_boundary_points.emplace_back(route_element.left_boundary.x, route_element.left_boundary.y);
223 boundaries.right_boundary_points.emplace_back(route_element.right_boundary.x, route_element.right_boundary.y);
224 boundaries.left_boundary_route_s.push_back(route_element.s);
225 boundaries.right_boundary_route_s.push_back(route_element.s);
226 }
227 }
228 }
229
230 // Helper lambda to find intersection
231 auto findIntersection = [&](const Eigen::Vector2d& ref_pos, double sin_yaw, double cos_yaw, double expected_route_s,
232 const std::vector<Eigen::Vector2d>& boundary_points, const std::vector<double>& boundary_route_s,
233 bool isLeft) -> std::pair<double, Eigen::Vector2d> {
234 std::pair<double, Eigen::Vector2d> intersection_result = {
235 std::numeric_limits<double>::infinity(),
236 Eigen::Vector2d(std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity())};
237 double best_route_s_difference = std::numeric_limits<double>::infinity();
238
239 const Eigen::Vector2d normal_dir = isLeft ? Eigen::Vector2d(-sin_yaw, cos_yaw) : Eigen::Vector2d(sin_yaw, -cos_yaw);
240 const auto cross2d = [](const Eigen::Vector2d& u, const Eigen::Vector2d& v) { return u.x() * v.y() - u.y() * v.x(); };
241
242 for (size_t i = 0; i + 1 < boundary_points.size(); ++i) {
243 const Eigen::Vector2d& a = boundary_points[i];
244 const Eigen::Vector2d& b = boundary_points[i + 1];
245 Eigen::Vector2d seg = b - a;
246 Eigen::Vector2d ap = ref_pos - a;
247
248 const double denom = cross2d(seg, normal_dir);
249 if (std::abs(denom) < 1e-9) {
250 continue; // Lines are close to parallel; ignore this segment
251 }
252
253 const double s = cross2d(ap, normal_dir) / denom;
254 const double t = cross2d(ap, seg) / denom;
255
256 if (s >= 0.0 && s <= 1.0 && t >= 0.0) {
257 Eigen::Vector2d intersection = a + s * seg;
258 double euclidean_distance = (ref_pos - intersection).norm();
259 const double intersection_route_s = boundary_route_s[i] + s * (boundary_route_s[i + 1] - boundary_route_s[i]);
260 const double route_s_difference = std::abs(intersection_route_s - expected_route_s);
261
262 if (route_s_difference <= MAX_ROUTE_S_DIFFERENCE &&
263 (route_s_difference < best_route_s_difference ||
264 (std::abs(route_s_difference - best_route_s_difference) < 1e-9 && euclidean_distance < intersection_result.first))) {
265 best_route_s_difference = route_s_difference;
266 intersection_result = {euclidean_distance, intersection};
267 }
268 }
269 }
270 return intersection_result;
271 };
272
273 // Loop over trajectory points and compute intersections
274 double reference_progress = 0.0;
275 Eigen::Vector2d previous_ref_pos = Eigen::Vector2d::Zero();
276 for (int i = 0; i < ref_sample_size; ++i) {
277 Eigen::Vector2d ref_pos(trajectory_planning_msgs::trajectory_access::getX(reference_trajectory, i),
278 trajectory_planning_msgs::trajectory_access::getY(reference_trajectory, i));
279 reference_progress += (ref_pos - previous_ref_pos).norm();
280 previous_ref_pos = ref_pos;
281 const double expected_route_s = current_route_s + reference_progress;
282
283 double yaw = trajectory_planning_msgs::trajectory_access::getTheta(reference_trajectory, i);
284 const double sin_yaw = std::sin(yaw);
285 const double cos_yaw = std::cos(yaw);
286
287 auto left_intersection = findIntersection(ref_pos, sin_yaw, cos_yaw, expected_route_s, boundaries.left_boundary_points,
288 boundaries.left_boundary_route_s, true);
289 auto right_intersection = findIntersection(ref_pos, sin_yaw, cos_yaw, expected_route_s, boundaries.right_boundary_points,
290 boundaries.right_boundary_route_s, false);
291 if (left_intersection.first != std::numeric_limits<double>::infinity() &&
292 right_intersection.first != std::numeric_limits<double>::infinity()) {
293 boundaries.min_normal_distances.emplace_back(left_intersection.first, right_intersection.first);
294 boundaries.left_boundary_intersections.emplace_back(left_intersection.second);
295 boundaries.right_boundary_intersections.emplace_back(right_intersection.second);
296 RCLCPP_DEBUG(this->get_logger(), "Minimum left boundary distance: %.2f m", left_intersection.first);
297 RCLCPP_DEBUG(this->get_logger(), "Minimum right boundary distance: %.2f m", right_intersection.first);
298 } else {
299 boundaries.min_normal_distances.emplace_back(NO_BOUNDARY_DISTANCE, NO_BOUNDARY_DISTANCE);
300 RCLCPP_WARN(get_logger(),
301 "No boundary intersection found for trajectory point %d. Do not constrain boundaries at this point.", i);
302 }
303 }
304 if (debug_viz_) {
305 vizBoundaryPoints(boundaries.left_boundary_intersections, boundaries.right_boundary_intersections);
306 }
307
308 return boundaries.min_normal_distances;
309}
310
311void TrajectoryOptimizationNode::vizBoundaryPoints(const std::vector<Eigen::Vector2d>& left_boundary_points,
312 const std::vector<Eigen::Vector2d>& right_boundary_points) {
313 visualization_msgs::msg::MarkerArray marker_array;
314 int id = 0;
315
316 auto createMarker = [&](int id, const std::string& ns, const Eigen::Vector2d& pos, float r, float g, float b) {
317 visualization_msgs::msg::Marker m;
318 m.header.frame_id = vehicle_frame_id_;
319 m.header.stamp = rclcpp::Time(ego_data_.header.stamp);
320 m.lifetime = rclcpp::Duration::from_seconds(0.5);
321 m.ns = ns;
322 m.id = id;
323 m.type = visualization_msgs::msg::Marker::SPHERE;
324 m.action = visualization_msgs::msg::Marker::ADD;
325 m.pose.position.x = pos.x();
326 m.pose.position.y = pos.y();
327 m.pose.position.z = 0.0;
328 m.scale.x = m.scale.y = m.scale.z = 0.4;
329 m.color.a = 1.0;
330 m.color.r = r;
331 m.color.g = g;
332 m.color.b = b;
333 return m;
334 };
335
336 auto addMarkers = [&](const std::vector<Eigen::Vector2d>& points, const std::string& ns, float r, float g, float b) {
337 for (const auto& point : points) {
338 marker_array.markers.push_back(createMarker(id++, ns, point, r, g, b));
339 }
340 };
341
342 addMarkers(left_boundary_points, "left_boundary_constraint", 0.0F, 1.0F, 0.0F);
343 addMarkers(right_boundary_points, "right_boundary_constraint", 1.0F, 0.0F, 0.0F);
344
345 boundary_pub_->publish(marker_array);
346}
347
348void TrajectoryOptimizationNode::vizCircles(const std::vector<double>& obstacles) {
349 visualization_msgs::msg::MarkerArray marker_array;
350 const int n_circles = static_cast<int>(obstacles.size() / static_cast<size_t>(p_obstacle_circles_shape_[1]));
351 for (int j = 0; j < n_circles; ++j) {
352 visualization_msgs::msg::Marker marker;
353 marker.header.frame_id = vehicle_frame_id_;
354 marker.header.stamp = rclcpp::Time(ego_data_.header.stamp);
355 marker.lifetime = rclcpp::Duration::from_seconds(0.5);
356 marker.ns = "obstacle-circles";
357 marker.id = j;
358 marker.type = visualization_msgs::msg::Marker::CYLINDER;
359 marker.action = visualization_msgs::msg::Marker::ADD;
360 marker.pose.position.x = obstacles[p_obstacle_circles_shape_[1] * j + 0];
361 marker.pose.position.y = obstacles[p_obstacle_circles_shape_[1] * j + 1];
362 marker.scale.x = obstacles[p_obstacle_circles_shape_[1] * j + 2] * 2.0;
363 marker.scale.y = obstacles[p_obstacle_circles_shape_[1] * j + 2] * 2.0;
364 marker.scale.z = 0.1;
365 marker.color.a = 0.3;
366 marker.color.r = 1.0;
367 marker_array.markers.push_back(marker);
368 }
369 circles_pub_->publish(marker_array);
370}
371void TrajectoryOptimizationNode::vizEgoCircles(const std::vector<double>& x_trajectory, const std::string& model_name) {
372 if (!ego_circles_pub_) return;
373
374 double ego_length = 0.0, ego_width = 0.0;
375 int n_ego_circles = 0;
376 std::vector<double> ego_offset2geocenter;
377
378 // define vehicle geometry based on model name (should match the OCP definition)
379 if (model_name == "karl") {
380 ego_length = 5.173;
381 ego_width = 1.94;
382 ego_offset2geocenter = {1.4895, 0.0};
383 n_ego_circles = 5;
384 } else if (model_name == "shuttle") {
385 ego_length = 4.97;
386 ego_width = 2.12;
387 ego_offset2geocenter = {0.0, 0.0};
388 n_ego_circles = 3;
389 } else {
390 RCLCPP_WARN(this->get_logger(), "Unknown model '%s'. Could not visualize ego circles.", model_name.c_str());
391 return;
392 }
393
394 visualization_msgs::msg::MarkerArray marker_array;
395
396 if (x_trajectory.empty() || n_ego_circles <= 0) {
397 visualization_msgs::msg::Marker delete_marker;
398 delete_marker.action = visualization_msgs::msg::Marker::DELETEALL;
399 marker_array.markers.push_back(delete_marker);
400 ego_circles_pub_->publish(marker_array);
401 return;
402 }
403
404 const double offset_x = !ego_offset2geocenter.empty() ? ego_offset2geocenter[0] : 0.0;
405 const double offset_y = ego_offset2geocenter.size() > 1 ? ego_offset2geocenter[1] : 0.0;
406
407 const double radius = std::sqrt(std::pow(ego_length / (2.0 * n_ego_circles), 2) + std::pow(ego_width / 2.0, 2));
408 const int state_dim = *nlp_dims_->nx;
409
410 int marker_id = 0;
411 for (int stage = 0; stage <= n_shots_; ++stage) {
412 const size_t state_offset = static_cast<size_t>(stage) * static_cast<size_t>(state_dim);
413 const double base_x = x_trajectory[state_offset + 0];
414 const double base_y = x_trajectory[state_offset + 1];
415 const double psi = x_trajectory[state_offset + 5];
416
417 const double ego_center_x = base_x + offset_x * std::cos(psi) - offset_y * std::sin(psi);
418 const double ego_center_y = base_y + offset_x * std::sin(psi) + offset_y * std::cos(psi);
419
420 for (int i = 0; i < n_ego_circles; ++i) {
421 const double lon_offset = -ego_length / 2.0 + (2 * i + 1) * ego_length / (2.0 * n_ego_circles);
422 const double x_offset = lon_offset * std::cos(psi);
423 const double y_offset = lon_offset * std::sin(psi);
424
425 visualization_msgs::msg::Marker marker;
426 marker.header.frame_id = vehicle_frame_id_;
427 marker.header.stamp = rclcpp::Time(ego_data_.header.stamp);
428 marker.lifetime = rclcpp::Duration::from_seconds(0.5);
429 marker.ns = "ego-circles";
430 marker.id = marker_id++;
431 marker.type = visualization_msgs::msg::Marker::CYLINDER;
432 marker.action = visualization_msgs::msg::Marker::ADD;
433 marker.pose.position.x = ego_center_x + x_offset;
434 marker.pose.position.y = ego_center_y + y_offset;
435 marker.pose.position.z = 0.0;
436 marker.pose.orientation.w = 1.0;
437 marker.scale.x = radius * 2.0;
438 marker.scale.y = radius * 2.0;
439 marker.scale.z = 0.05;
440 marker.color.a = 0.4;
441 marker.color.r = 0.0F;
442 marker.color.g = 0.6F;
443 marker.color.b = 1.0F;
444 marker_array.markers.push_back(marker);
445 }
446 }
447
448 ego_circles_pub_->publish(marker_array);
449}
450
452 // Status codes:
453 // 0: Success (ACADOS_SUCCESS)
454 // 1: NaN detected (ACADOS_NAN_DETECTED)
455 // 2: Maximum number of iterations reached (ACADOS_MAXITER)
456 // 3: Minimum step size reached (ACADOS_MINSTEP)
457 // 4: QP solver failed (ACADOS_QP_FAILURE)
458 // 5: Solver created (ACADOS_READY)
459 // 6: Problem unbounded (ACADOS_UNBOUNDED)
460 // 7: Solver timeout (ACADOS_TIMEOUT)
461 if (metrics.status == ACADOS_SUCCESS && verbose_) {
462 RCLCPP_INFO(get_logger(), "\033[1;32mOptimization: SUCCESS!\033[0m");
463 } else if (metrics.status == ACADOS_MAXITER) {
464 RCLCPP_WARN(get_logger(), "Optimization failed with status %d (max iterations).", metrics.status);
465 } else if (metrics.status == ACADOS_TIMEOUT) {
466 RCLCPP_WARN(get_logger(), "\033[38;5;214mOptimization failed with status %d (timeout).\033[0m", metrics.status);
467 } else if (metrics.status != ACADOS_SUCCESS) {
468 RCLCPP_ERROR(get_logger(), "%s_acados_solve() failed with status %d.", model_name_.c_str(), metrics.status);
469 }
470
471 if (verbose_) {
472 RCLCPP_INFO(get_logger(), "Optimization took %.3f ms (SQP iter: %d; QP iter: %d; KKT: %e)", metrics.acados_total_ms,
473 metrics.sqp_iter, metrics.qp_iter, metrics.kkt_norm_inf);
474 RCLCPP_INFO(get_logger(), "cost_value: %f; residuals: stat=%e eq=%e ineq=%e comp=%e", metrics.cost_value, metrics.res_stat,
475 metrics.res_eq, metrics.res_ineq, metrics.res_comp);
476
477 std::fputs("\n--- xtraj ---\n", stdout);
478 d_print_exp_tran_mat(*nlp_dims_->nx, n_shots_ + 1, xtraj_.data(), *nlp_dims_->nx);
479 std::fputs("\n--- utraj ---\n", stdout);
480 d_print_exp_tran_mat(*nlp_dims_->nu, n_shots_, utraj_.data(), *nlp_dims_->nu);
482 }
483}
484
485} // namespace trajectory_optimization
static void keepNClosestObjects(perception_msgs::msg::ObjectList &object_list, const int n_objects)
Keeps the nearest forward objects and discards the remaining entries.
Definition utils.cpp:86
rclcpp::Publisher< visualization_msgs::msg::MarkerArray >::SharedPtr circles_pub_
void printSolution(const PerformanceMetrics &metrics)
Logs solver status and optional debug statistics for the last optimization run.
Definition utils.cpp:451
void vizCircles(const std::vector< double > &obstacles)
Publishes visualization markers for the obstacle circles currently used by the optimizer.
Definition utils.cpp:348
static double wrap_angle_rad(double angle_rad, double min_val=-M_PI, double max_val=M_PI)
Wraps an angle into a configured interval.
Definition utils.cpp:14
bool linearInterpolation(const std::vector< double > &X, const std::vector< double > &Y, const double &desired_x, double &output_y, const bool wrap_angle=false)
Interpolates a value from sampled data and optionally handles angle wrap-around.
Definition utils.cpp:21
void vizEgoCircles(const std::vector< double > &x_trajectory, const std::string &model_name)
Publishes the ego-vehicle circle approximation used by the selected OCP model.
Definition utils.cpp:371
bool trajectory2outputFrame(trajectory_planning_msgs::msg::Trajectory &trajectory)
Transforms the planned trajectory into the configured output frame (trajectory_frame_id_) if required...
Definition utils.cpp:71
rclcpp::Publisher< visualization_msgs::msg::MarkerArray >::SharedPtr boundary_pub_
std::vector< std::pair< double, double > > normalBoundaryDistance(const trajectory_planning_msgs::msg::Trajectory &reference_trajectory, const route_planning_msgs::msg::Route &route)
Computes minimum normal distances from the reference path to the active route boundaries.
Definition utils.cpp:154
rclcpp::Publisher< visualization_msgs::msg::MarkerArray >::SharedPtr ego_circles_pub_
void vizBoundaryPoints(const std::vector< Eigen::Vector2d > &left_boundary_points, const std::vector< Eigen::Vector2d > &right_boundary_points)
Publishes the boundary intersections corresponding to the distances passed to the OCP.
Definition utils.cpp:311
std::vector< double > discretizeBB2Circles(const double x, const double y, const double yaw, const double length, const double width)
Approximates an oriented bounding box with a set of obstacle circles.
Definition utils.cpp:116
Namespace for trajectory_optimization package.
void acados_print_stats(ocp_model_capsule_t capsule)
Wrapper around the generated acados statistics printer.