trajectory_optimization v1.3.1
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; // should be smaller than MAX_BOUNDARY_CONSTRAINT from ocp
157 constexpr double MAX_ROUTE_S_DIFFERENCE = 20.0;
158
159 struct Boundaries {
160 std::vector<std::pair<double, double>> min_normal_distances;
161 std::vector<Eigen::Vector2d> left_boundary_points;
162 std::vector<Eigen::Vector2d> right_boundary_points;
163 std::vector<double> left_boundary_route_s;
164 std::vector<double> right_boundary_route_s;
165 std::vector<Eigen::Vector2d> left_boundary_intersections;
166 std::vector<Eigen::Vector2d> right_boundary_intersections;
167 };
168
169 Boundaries boundaries;
170 const auto remaining_route = route_planning_msgs::route_access::getRemainingRouteElements(route, true);
171 const int ref_sample_size = trajectory_planning_msgs::trajectory_access::getSamplePointSize(reference_trajectory);
172 boundaries.left_boundary_points.reserve(remaining_route.size());
173 boundaries.right_boundary_points.reserve(remaining_route.size());
174 boundaries.left_boundary_route_s.reserve(remaining_route.size());
175 boundaries.right_boundary_route_s.reserve(remaining_route.size());
176 boundaries.min_normal_distances.reserve(ref_sample_size);
177 boundaries.left_boundary_intersections.reserve(ref_sample_size);
178 boundaries.right_boundary_intersections.reserve(ref_sample_size);
179
180 if (remaining_route.empty()) {
182 RCLCPP_WARN(get_logger(), "Remaining route is empty. Do not constrain boundaries.");
183 }
184 for (int i = 0; i < ref_sample_size; ++i) {
185 boundaries.min_normal_distances.emplace_back(NO_BOUNDARY_DISTANCE, NO_BOUNDARY_DISTANCE);
186 }
187 return boundaries.min_normal_distances;
188 }
189
190 for (const auto& route_element : remaining_route) {
191 if (route_element.is_enriched) {
193 route_planning_msgs::msg::LaneElement suggested_lane =
194 route_planning_msgs::route_access::getSuggestedLaneElement(route_element);
195 boundaries.left_boundary_points.emplace_back(suggested_lane.left_boundary.point.x, suggested_lane.left_boundary.point.y);
196 boundaries.right_boundary_points.emplace_back(suggested_lane.right_boundary.point.x,
197 suggested_lane.right_boundary.point.y);
198 boundaries.left_boundary_route_s.push_back(route_element.s);
199 boundaries.right_boundary_route_s.push_back(route_element.s);
201 const auto& lane_elements = route_element.lane_elements;
202 if (!lane_elements.empty()) {
203 boundaries.left_boundary_points.emplace_back(lane_elements.front().left_boundary.point.x,
204 lane_elements.front().left_boundary.point.y);
205 boundaries.right_boundary_points.emplace_back(lane_elements.back().right_boundary.point.x,
206 lane_elements.back().right_boundary.point.y);
207 boundaries.left_boundary_route_s.push_back(route_element.s);
208 boundaries.right_boundary_route_s.push_back(route_element.s);
209 }
211 boundaries.left_boundary_points.emplace_back(route_element.left_boundary.x, route_element.left_boundary.y);
212 boundaries.right_boundary_points.emplace_back(route_element.right_boundary.x, route_element.right_boundary.y);
213 boundaries.left_boundary_route_s.push_back(route_element.s);
214 boundaries.right_boundary_route_s.push_back(route_element.s);
215 }
216 }
217 }
218
219 // Helper lambda to find intersection
220 auto findIntersection = [&](const Eigen::Vector2d& ref_pos, double sin_yaw, double cos_yaw, double expected_route_s,
221 const std::vector<Eigen::Vector2d>& boundary_points, const std::vector<double>& boundary_route_s,
222 bool isLeft) -> std::pair<double, Eigen::Vector2d> {
223 std::pair<double, Eigen::Vector2d> intersection_result = {
224 std::numeric_limits<double>::infinity(),
225 Eigen::Vector2d(std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity())};
226 double best_route_s_difference = std::numeric_limits<double>::infinity();
227
228 const Eigen::Vector2d normal_dir = isLeft ? Eigen::Vector2d(-sin_yaw, cos_yaw) : Eigen::Vector2d(sin_yaw, -cos_yaw);
229 const auto cross2d = [](const Eigen::Vector2d& u, const Eigen::Vector2d& v) { return u.x() * v.y() - u.y() * v.x(); };
230
231 for (size_t i = 0; i + 1 < boundary_points.size(); ++i) {
232 const Eigen::Vector2d& a = boundary_points[i];
233 const Eigen::Vector2d& b = boundary_points[i + 1];
234 Eigen::Vector2d seg = b - a;
235 Eigen::Vector2d ap = ref_pos - a;
236
237 const double denom = cross2d(seg, normal_dir);
238 if (std::abs(denom) < 1e-9) {
239 continue; // Lines are close to parallel; ignore this segment
240 }
241
242 const double s = cross2d(ap, normal_dir) / denom;
243 const double t = cross2d(ap, seg) / denom;
244
245 if (s >= 0.0 && s <= 1.0 && t >= 0.0) {
246 Eigen::Vector2d intersection = a + s * seg;
247 double euclidean_distance = (ref_pos - intersection).norm();
248 const double intersection_route_s = boundary_route_s[i] + s * (boundary_route_s[i + 1] - boundary_route_s[i]);
249 const double route_s_difference = std::abs(intersection_route_s - expected_route_s);
250
251 if (route_s_difference <= MAX_ROUTE_S_DIFFERENCE &&
252 (route_s_difference < best_route_s_difference ||
253 (std::abs(route_s_difference - best_route_s_difference) < 1e-9 && euclidean_distance < intersection_result.first))) {
254 best_route_s_difference = route_s_difference;
255 intersection_result = {euclidean_distance, intersection};
256 }
257 }
258 }
259 return intersection_result;
260 };
261
262 // Loop over trajectory points and compute intersections
263 double reference_progress = 0.0;
264 Eigen::Vector2d previous_ref_pos = Eigen::Vector2d::Zero();
265 const double current_route_s = remaining_route.front().s;
266 for (int i = 0; i < ref_sample_size; ++i) {
267 Eigen::Vector2d ref_pos(trajectory_planning_msgs::trajectory_access::getX(reference_trajectory, i),
268 trajectory_planning_msgs::trajectory_access::getY(reference_trajectory, i));
269 reference_progress += (ref_pos - previous_ref_pos).norm();
270 previous_ref_pos = ref_pos;
271 const double expected_route_s = current_route_s + reference_progress;
272
273 double yaw = trajectory_planning_msgs::trajectory_access::getTheta(reference_trajectory, i);
274 const double sin_yaw = std::sin(yaw);
275 const double cos_yaw = std::cos(yaw);
276
277 auto left_intersection = findIntersection(ref_pos, sin_yaw, cos_yaw, expected_route_s, boundaries.left_boundary_points,
278 boundaries.left_boundary_route_s, true);
279 auto right_intersection = findIntersection(ref_pos, sin_yaw, cos_yaw, expected_route_s, boundaries.right_boundary_points,
280 boundaries.right_boundary_route_s, false);
281 if (left_intersection.first != std::numeric_limits<double>::infinity() &&
282 right_intersection.first != std::numeric_limits<double>::infinity()) {
283 boundaries.min_normal_distances.emplace_back(left_intersection.first, right_intersection.first);
284 boundaries.left_boundary_intersections.emplace_back(left_intersection.second);
285 boundaries.right_boundary_intersections.emplace_back(right_intersection.second);
286 RCLCPP_DEBUG(this->get_logger(), "Minimum left boundary distance: %.2f m", left_intersection.first);
287 RCLCPP_DEBUG(this->get_logger(), "Minimum right boundary distance: %.2f m", right_intersection.first);
288 } else {
289 boundaries.min_normal_distances.emplace_back(NO_BOUNDARY_DISTANCE, NO_BOUNDARY_DISTANCE);
290 RCLCPP_WARN(get_logger(),
291 "No boundary intersection found for trajectory point %d. Do not constrain boundaries at this point.", i);
292 }
293 }
294 if (debug_viz_) {
295 vizBoundaryPoints(boundaries.left_boundary_intersections, boundaries.right_boundary_intersections);
296 }
297
298 return boundaries.min_normal_distances;
299}
300
301void TrajectoryOptimizationNode::vizBoundaryPoints(const std::vector<Eigen::Vector2d>& left_boundary_points,
302 const std::vector<Eigen::Vector2d>& right_boundary_points) {
303 visualization_msgs::msg::MarkerArray marker_array;
304 int id = 0;
305
306 auto createMarker = [&](int id, const std::string& ns, const Eigen::Vector2d& pos, float r, float g, float b) {
307 visualization_msgs::msg::Marker m;
308 m.header.frame_id = vehicle_frame_id_;
309 m.header.stamp = rclcpp::Time(ego_data_.header.stamp);
310 m.lifetime = rclcpp::Duration::from_seconds(0.5);
311 m.ns = ns;
312 m.id = id;
313 m.type = visualization_msgs::msg::Marker::SPHERE;
314 m.action = visualization_msgs::msg::Marker::ADD;
315 m.pose.position.x = pos.x();
316 m.pose.position.y = pos.y();
317 m.pose.position.z = 0.0;
318 m.scale.x = m.scale.y = m.scale.z = 0.4;
319 m.color.a = 1.0;
320 m.color.r = r;
321 m.color.g = g;
322 m.color.b = b;
323 return m;
324 };
325
326 auto addMarkers = [&](const std::vector<Eigen::Vector2d>& points, const std::string& ns, float r, float g, float b) {
327 for (const auto& point : points) {
328 marker_array.markers.push_back(createMarker(id++, ns, point, r, g, b));
329 }
330 };
331
332 addMarkers(left_boundary_points, "left_boundary_constraint", 0.0F, 1.0F, 0.0F);
333 addMarkers(right_boundary_points, "right_boundary_constraint", 1.0F, 0.0F, 0.0F);
334
335 boundary_pub_->publish(marker_array);
336}
337
338void TrajectoryOptimizationNode::vizCircles(const std::vector<double>& obstacles) {
339 visualization_msgs::msg::MarkerArray marker_array;
340 const int n_circles = static_cast<int>(obstacles.size() / static_cast<size_t>(p_obstacle_circles_shape_[1]));
341 for (int j = 0; j < n_circles; ++j) {
342 visualization_msgs::msg::Marker marker;
343 marker.header.frame_id = vehicle_frame_id_;
344 marker.header.stamp = rclcpp::Time(ego_data_.header.stamp);
345 marker.lifetime = rclcpp::Duration::from_seconds(0.5);
346 marker.ns = "obstacle-circles";
347 marker.id = j;
348 marker.type = visualization_msgs::msg::Marker::CYLINDER;
349 marker.action = visualization_msgs::msg::Marker::ADD;
350 marker.pose.position.x = obstacles[p_obstacle_circles_shape_[1] * j + 0];
351 marker.pose.position.y = obstacles[p_obstacle_circles_shape_[1] * j + 1];
352 marker.scale.x = obstacles[p_obstacle_circles_shape_[1] * j + 2] * 2.0;
353 marker.scale.y = obstacles[p_obstacle_circles_shape_[1] * j + 2] * 2.0;
354 marker.scale.z = 0.1;
355 marker.color.a = 0.3;
356 marker.color.r = 1.0;
357 marker_array.markers.push_back(marker);
358 }
359 circles_pub_->publish(marker_array);
360}
361void TrajectoryOptimizationNode::vizEgoCircles(const std::vector<double>& x_trajectory, const std::string& model_name) {
362 if (!ego_circles_pub_) return;
363
364 double ego_length = 0.0, ego_width = 0.0;
365 int n_ego_circles = 0;
366 std::vector<double> ego_offset2geocenter;
367
368 // define vehicle geometry based on model name (should match the OCP definition)
369 if (model_name == "karl") {
370 ego_length = 5.173;
371 ego_width = 1.94;
372 ego_offset2geocenter = {1.4895, 0.0};
373 n_ego_circles = 5;
374 } else if (model_name == "shuttle") {
375 ego_length = 4.97;
376 ego_width = 2.12;
377 ego_offset2geocenter = {0.0, 0.0};
378 n_ego_circles = 3;
379 } else {
380 RCLCPP_WARN(this->get_logger(), "Unknown model '%s'. Could not visualize ego circles.", model_name.c_str());
381 return;
382 }
383
384 visualization_msgs::msg::MarkerArray marker_array;
385
386 if (x_trajectory.empty() || n_ego_circles <= 0) {
387 visualization_msgs::msg::Marker delete_marker;
388 delete_marker.action = visualization_msgs::msg::Marker::DELETEALL;
389 marker_array.markers.push_back(delete_marker);
390 ego_circles_pub_->publish(marker_array);
391 return;
392 }
393
394 const double offset_x = !ego_offset2geocenter.empty() ? ego_offset2geocenter[0] : 0.0;
395 const double offset_y = ego_offset2geocenter.size() > 1 ? ego_offset2geocenter[1] : 0.0;
396
397 const double radius = std::sqrt(std::pow(ego_length / (2.0 * n_ego_circles), 2) + std::pow(ego_width / 2.0, 2));
398 const int state_dim = *nlp_dims_->nx;
399
400 int marker_id = 0;
401 for (int stage = 0; stage <= n_shots_; ++stage) {
402 const size_t state_offset = static_cast<size_t>(stage) * static_cast<size_t>(state_dim);
403 const double base_x = x_trajectory[state_offset + 0];
404 const double base_y = x_trajectory[state_offset + 1];
405 const double psi = x_trajectory[state_offset + 5];
406
407 const double ego_center_x = base_x + offset_x * std::cos(psi) - offset_y * std::sin(psi);
408 const double ego_center_y = base_y + offset_x * std::sin(psi) + offset_y * std::cos(psi);
409
410 for (int i = 0; i < n_ego_circles; ++i) {
411 const double lon_offset = -ego_length / 2.0 + (2 * i + 1) * ego_length / (2.0 * n_ego_circles);
412 const double x_offset = lon_offset * std::cos(psi);
413 const double y_offset = lon_offset * std::sin(psi);
414
415 visualization_msgs::msg::Marker marker;
416 marker.header.frame_id = vehicle_frame_id_;
417 marker.header.stamp = rclcpp::Time(ego_data_.header.stamp);
418 marker.lifetime = rclcpp::Duration::from_seconds(0.5);
419 marker.ns = "ego-circles";
420 marker.id = marker_id++;
421 marker.type = visualization_msgs::msg::Marker::CYLINDER;
422 marker.action = visualization_msgs::msg::Marker::ADD;
423 marker.pose.position.x = ego_center_x + x_offset;
424 marker.pose.position.y = ego_center_y + y_offset;
425 marker.pose.position.z = 0.0;
426 marker.pose.orientation.w = 1.0;
427 marker.scale.x = radius * 2.0;
428 marker.scale.y = radius * 2.0;
429 marker.scale.z = 0.05;
430 marker.color.a = 0.4;
431 marker.color.r = 0.0F;
432 marker.color.g = 0.6F;
433 marker.color.b = 1.0F;
434 marker_array.markers.push_back(marker);
435 }
436 }
437
438 ego_circles_pub_->publish(marker_array);
439}
440
442 // Status codes:
443 // 0: Success (ACADOS_SUCCESS)
444 // 1: NaN detected (ACADOS_NAN_DETECTED)
445 // 2: Maximum number of iterations reached (ACADOS_MAXITER)
446 // 3: Minimum step size reached (ACADOS_MINSTEP)
447 // 4: QP solver failed (ACADOS_QP_FAILURE)
448 // 5: Solver created (ACADOS_READY)
449 // 6: Problem unbounded (ACADOS_UNBOUNDED)
450 // 7: Solver timeout (ACADOS_TIMEOUT)
451 if (metrics.status == ACADOS_SUCCESS && verbose_) {
452 RCLCPP_INFO(get_logger(), "\033[1;32mOptimization: SUCCESS!\033[0m");
453 } else if (metrics.status == ACADOS_MAXITER) {
454 RCLCPP_WARN(get_logger(), "Optimization failed with status %d (max iterations).", metrics.status);
455 } else if (metrics.status == ACADOS_TIMEOUT) {
456 RCLCPP_WARN(get_logger(), "\033[38;5;214mOptimization failed with status %d (timeout).\033[0m", metrics.status);
457 } else if (metrics.status != ACADOS_SUCCESS) {
458 RCLCPP_ERROR(get_logger(), "%s_acados_solve() failed with status %d.", model_name_.c_str(), metrics.status);
459 }
460
461 if (verbose_) {
462 RCLCPP_INFO(get_logger(), "Optimization took %.3f ms (SQP iter: %d; QP iter: %d; KKT: %e)", metrics.acados_total_ms,
463 metrics.sqp_iter, metrics.qp_iter, metrics.kkt_norm_inf);
464 RCLCPP_INFO(get_logger(), "cost_value: %f; residuals: stat=%e eq=%e ineq=%e comp=%e", metrics.cost_value, metrics.res_stat,
465 metrics.res_eq, metrics.res_ineq, metrics.res_comp);
466
467 std::fputs("\n--- xtraj ---\n", stdout);
468 d_print_exp_tran_mat(*nlp_dims_->nx, n_shots_ + 1, xtraj_.data(), *nlp_dims_->nx);
469 std::fputs("\n--- utraj ---\n", stdout);
470 d_print_exp_tran_mat(*nlp_dims_->nu, n_shots_, utraj_.data(), *nlp_dims_->nu);
472 }
473}
474
477 performance_logger_->write(metrics);
478 }
479}
480
481} // 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:441
void vizCircles(const std::vector< double > &obstacles)
Publishes visualization markers for the obstacle circles currently used by the optimizer.
Definition utils.cpp:338
void logPerformance(const PerformanceMetrics &metrics)
Emits one machine-readable performance record when performance logging is enabled.
Definition utils.cpp:475
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:361
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:301
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.