trajectory_optimization v1.4.0
Loading...
Searching...
No Matches
trajectory_optimization_node.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 <chrono>
6#include <cmath>
7#include <functional>
8#include <thread>
9
11
17
18namespace {
19using SteadyClock = std::chrono::steady_clock;
20
21double elapsedMilliseconds(const SteadyClock::time_point& start) {
22 return std::chrono::duration<double, std::milli>(SteadyClock::now() - start).count();
23}
24
25double elapsedMilliseconds(const SteadyClock::time_point& start, const SteadyClock::time_point& end) {
26 return std::chrono::duration<double, std::milli>(end - start).count();
27}
28
29} // namespace
30
31TrajectoryOptimizationNode::TrajectoryOptimizationNode(const std::string node_name, const rclcpp::NodeOptions& options)
32 : rclcpp::Node(node_name, options) {
33 // declare and load node parameters
34 this->declareAndLoadParameter("vehicle_frame_id", vehicle_frame_id_,
35 "Frame ID of local vehicle frame (the ocp is defined in this frame)");
36 this->declareAndLoadParameter("trajectory_frame_id", trajectory_frame_id_, "Frame ID of output trajectory");
37 this->declareAndLoadParameter("fixed_over_time_frame_id", fixed_over_time_frame_id_,
38 "Frame ID of frame that is fixed over time for finding temporal transforms");
39 this->declareAndLoadParameter("ego_data_timeout", ego_data_timeout_,
40 "Time after which a received ego vehicle data is considered invalid [s]. Optimization will not "
41 "be run if ego data is invalid.");
42 this->declareAndLoadParameter("model_name", model_name_,
43 "Name of the model to be used for trajectory optimization [karl, shuttle]");
44 this->declareAndLoadParameter("optimization_frequency", optimization_freq_, "Optimization frequency in Hz");
45 this->declareAndLoadParameter("n_shots", n_shots_, "Number of shooting intervals in optimization horizon");
46 this->declareAndLoadParameter("optimization_horizon", optimization_horizon_, "Optimization Horizon in seconds");
47 this->declareAndLoadParameter("verbose", verbose_, "Print solver statistics");
48 this->declareAndLoadParameter("performance_logging", performance_logging_,
49 "Write one CSV record for every completed solver run", false, false, true);
50 this->declareAndLoadParameter("debug_visualization", debug_viz_, "Publish debug visualization markers (e.g. obstacle circles)");
51 this->declareAndLoadParameter("run_as_callback", run_as_callback_,
52 "Run OCP once for each received reference trajectory (true) or on a timer (false)");
53 this->declareAndLoadParameter("cost_weights", cost_weights_, "Cost function weights");
54 this->declareAndLoadParameter("dynamic_weight", dynamic_weight_, "Dynamic weight alpha");
55 this->declareAndLoadParameter("thw", thw_, "Time headway to front vehicle");
56 this->declareAndLoadParameter("d_min_obstacle_long", d_min_obstacle_long_,
57 "Minimum distance to keep to obstacle in longitudinal direction [m]");
58 this->declareAndLoadParameter("d_min_obstacle_lat", d_min_obstacle_lat_,
59 "Minimum distance to keep to obstacle in lateral direction [m]");
60 this->declareAndLoadParameter("d_min_boundary_lat", d_min_boundary_lat_,
61 "Minimum distance to keep to boundary in lateral direction [m]");
62 this->declareAndLoadParameter("standstill_threshold", standstill_threshold_,
63 "Threshold for standstill detection [m/s]. If the velocities of all states are below this "
64 "threshold, publish standstill trajectory");
65 this->declareAndLoadParameter("high_level_stabilization", high_level_stabilization_,
66 "Use high-level stabilization strategy for init state (= init with current EgoData)");
68 "consider_objects", consider_objects_,
69 "consider objects in optimization: 0 = none, 1 = static (no prediction), 2 = dynamic (with prediction)");
70 this->declareAndLoadParameter("min_prediction_probability", min_prediction_probability_,
71 "Minimum probability for predicted object states to be considered", true, false, false, 0.0, 1.0);
73 "consider_boundaries", consider_boundaries_,
74 "consider route boundaries in optimization: 0 = no, 1 = suggested lane, 2 = including adjacent, 3 = drivable space");
75 this->declareAndLoadParameter("bi_level_dV", bi_level_dV_,
76 "Threshold for bi-level stabilization: maximum velocity difference [m/s]");
77 this->declareAndLoadParameter("bi_level_dY", bi_level_dY_, "Threshold for bi-level stabilization: maximum y-offset [m]");
78 this->declareAndLoadParameter("bi_level_dYaw", bi_level_dYaw_,
79 "Threshold for bi-level stabilization: maximum yaw difference [degree]");
81 try {
82 performance_logger_ = std::make_unique<PerformanceLogger>(get_name());
83 RCLCPP_INFO(get_logger(), "Writing benchmark logs to '%s'.", performance_logger_->path().c_str());
84 } catch (const std::exception& error) {
85 RCLCPP_ERROR(get_logger(), "Could not initialize benchmark logging: %s", error.what());
86 }
87 }
88 this->setup();
89}
90
92
93template <typename T>
95 T& param,
96 const std::string& description,
97 const bool add_to_auto_reconfigurable_params,
98 const bool is_required,
99 const bool read_only,
100 const std::optional<double>& from_value,
101 const std::optional<double>& to_value,
102 const std::optional<double>& step_value,
103 const std::string& additional_constraints) {
104 rcl_interfaces::msg::ParameterDescriptor param_desc;
105 param_desc.description = description;
106 param_desc.additional_constraints = additional_constraints;
107 param_desc.read_only = read_only;
108
109 auto type = rclcpp::ParameterValue(param).get_type();
110
111 if (from_value.has_value() && to_value.has_value()) {
112 if constexpr (std::is_integral_v<T>) {
113 rcl_interfaces::msg::IntegerRange range;
114 range.set__from_value(static_cast<T>(from_value.value())).set__to_value(static_cast<T>(to_value.value()));
115 if (step_value.has_value()) range.set__step(static_cast<T>(step_value.value()));
116 param_desc.integer_range = {range};
117 } else if constexpr (std::is_floating_point_v<T>) {
118 rcl_interfaces::msg::FloatingPointRange range;
119 range.set__from_value(static_cast<T>(from_value.value())).set__to_value(static_cast<T>(to_value.value()));
120 if (step_value.has_value()) range.set__step(static_cast<T>(step_value.value()));
121 param_desc.floating_point_range = {range};
122 } else {
123 RCLCPP_WARN(this->get_logger(), "Parameter type of parameter '%s' does not support specifying a range", name.c_str());
124 }
125 }
126
127 this->declare_parameter(name, type, param_desc);
128
129 try {
130 param = this->get_parameter(name).get_value<T>();
131 std::stringstream ss;
132 ss << "Loaded parameter '" << name << "': ";
133 if constexpr (is_vector_v<T>) {
134 ss << "[";
135 for (const auto& element : param) ss << element << (&element != &param.back() ? ", " : "");
136 ss << "]";
137 } else {
138 ss << param;
139 }
140 RCLCPP_INFO_STREAM(this->get_logger(), ss.str());
141 } catch (rclcpp::exceptions::ParameterUninitializedException&) {
142 if (is_required) {
143 RCLCPP_FATAL_STREAM(this->get_logger(), "Missing required parameter '" << name << "', exiting");
144 exit(EXIT_FAILURE);
145 } else {
146 std::stringstream ss;
147 ss << "Missing parameter '" << name << "', using default value: ";
148 if constexpr (is_vector_v<T>) {
149 ss << "[";
150 for (const auto& element : param) ss << element << (&element != &param.back() ? ", " : "");
151 ss << "]";
152 } else {
153 ss << param;
154 }
155 RCLCPP_WARN_STREAM(this->get_logger(), ss.str());
156 this->set_parameters({rclcpp::Parameter(name, rclcpp::ParameterValue(param))});
157 }
158 }
159
160 if (add_to_auto_reconfigurable_params) {
161 std::function<void(const rclcpp::Parameter&)> setter = [&param](const rclcpp::Parameter& p) { param = p.get_value<T>(); };
162 auto_reconfigurable_params_.push_back(std::make_tuple(name, setter));
163 }
164}
165
166rcl_interfaces::msg::SetParametersResult TrajectoryOptimizationNode::parametersCallback(
167 const std::vector<rclcpp::Parameter>& parameters) {
168 for (const auto& param : parameters) {
169 for (auto& auto_reconfigurable_param : auto_reconfigurable_params_) {
170 if (param.get_name() == std::get<0>(auto_reconfigurable_param)) {
171 std::get<1>(auto_reconfigurable_param)(param);
172 RCLCPP_INFO(this->get_logger(), "Reconfigured parameter '%s' to: %s", param.get_name().c_str(),
173 param.value_to_string().c_str());
174 break;
175 }
176 }
177 // handle special cases
178 if (param.get_name() == "run_as_callback") {
180 planning_timer_ = this->create_wall_timer(std::chrono::duration<double>(1 / optimization_freq_),
182 RCLCPP_WARN(this->get_logger(), "OCP runs now periodically with frequency %f Hz", optimization_freq_);
183 } else if (run_as_callback_ && planning_timer_) {
184 planning_timer_->cancel();
185 planning_timer_.reset();
186 RCLCPP_WARN(this->get_logger(), "OCP runs now on reference trajectory callback");
187 }
188 }
189 }
190 // mark parameter change successful
191 rcl_interfaces::msg::SetParametersResult result;
192 result.successful = true;
193
194 return result;
195}
196
198 tf2_buffer_ = std::make_unique<tf2_ros::Buffer>(this->get_clock());
199 tf2_listener_ = std::make_shared<tf2_ros::TransformListener>(*tf2_buffer_);
200
201 // create a callback for dynamic parameter configuration
202 parameters_callback_ = this->add_on_set_parameters_callback(
203 std::bind(&TrajectoryOptimizationNode::parametersCallback, this, std::placeholders::_1));
204
205 // set up subscriber for input topics
206 ego_data_sub_ = this->create_subscription<perception_msgs::msg::EgoData>(
207 "~/ego_data", 1, std::bind(&TrajectoryOptimizationNode::egoDataCallback, this, std::placeholders::_1));
208 RCLCPP_INFO(this->get_logger(), "Subscribed to '%s'", ego_data_sub_->get_topic_name());
209
210 object_list_sub_ = this->create_subscription<perception_msgs::msg::ObjectList>(
211 "~/object_list", 1, std::bind(&TrajectoryOptimizationNode::objectListCallback, this, std::placeholders::_1));
212 RCLCPP_INFO(this->get_logger(), "Subscribed to '%s'", object_list_sub_->get_topic_name());
213
214 route_sub_ = this->create_subscription<route_planning_msgs::msg::Route>(
215 "~/route", 1, std::bind(&TrajectoryOptimizationNode::routeCallback, this, std::placeholders::_1));
216 RCLCPP_INFO(this->get_logger(), "Subscribed to '%s'", route_sub_->get_topic_name());
217
218 reference_trajectory_sub_ = this->create_subscription<trajectory_planning_msgs::msg::Trajectory>(
219 "~/reference_trajectory", 1,
220 std::bind(&TrajectoryOptimizationNode::referenceTrajectoryCallback, this, std::placeholders::_1));
221 RCLCPP_INFO(this->get_logger(), "Subscribed to '%s'", reference_trajectory_sub_->get_topic_name());
222
223 // set up publisher for output topics
224 trajectory_pub_ = this->create_publisher<trajectory_planning_msgs::msg::Trajectory>("~/trajectory", 1);
225 RCLCPP_INFO(this->get_logger(), "Publishing to '%s'", trajectory_pub_->get_topic_name());
226 circles_pub_ = this->create_publisher<visualization_msgs::msg::MarkerArray>("~/visualization/object_circles", 1);
227 RCLCPP_INFO(this->get_logger(), "Publishing to '%s'", circles_pub_->get_topic_name());
228 ego_circles_pub_ = this->create_publisher<visualization_msgs::msg::MarkerArray>("~/visualization/ego_circles", 1);
229 RCLCPP_INFO(this->get_logger(), "Publishing to '%s'", ego_circles_pub_->get_topic_name());
230 boundary_pub_ = this->create_publisher<visualization_msgs::msg::MarkerArray>("~/visualization/boundaries", 1);
231 RCLCPP_INFO(this->get_logger(), "Publishing to '%s'", boundary_pub_->get_topic_name());
232
233 // create timer for planning cycle
234 if (run_as_callback_) {
235 RCLCPP_INFO(this->get_logger(), "OCP runs on reference trajectory callback");
236 } else {
237 RCLCPP_INFO(this->get_logger(), "OCP runs continuously with frequency %f Hz", optimization_freq_);
238 planning_timer_ = this->create_wall_timer(std::chrono::duration<double>(1 / optimization_freq_),
240 }
241
242 // init reference trajectory
243 trajectory_planning_msgs::trajectory_access::initializeTrajectory(
244 reference_trajectory_, trajectory_planning_msgs::msg::REFERENCE::TYPE_ID, n_shots_ + 1);
246
247 // init latest trajectory (doesn't matter which type, only used for standstill detection)
248 trajectory_planning_msgs::trajectory_access::initializeTrajectory(
249 latest_valid_trajectory_, trajectory_planning_msgs::msg::DRIVABLE::TYPE_ID, n_shots_ + 1);
251
252 setupSolver();
253
254 // Annotate message links for tracing: Publish trajectory periodically, which depends an all subscribed topics.
255 std::vector<const void*> link_subs;
256 link_subs.push_back(static_cast<const void*>(ego_data_sub_->get_subscription_handle().get()));
257 link_subs.push_back(static_cast<const void*>(object_list_sub_->get_subscription_handle().get()));
258 link_subs.push_back(static_cast<const void*>(route_sub_->get_subscription_handle().get()));
259 link_subs.push_back(static_cast<const void*>(reference_trajectory_sub_->get_subscription_handle().get()));
260 std::vector<const void*> link_pubs;
261 link_pubs.push_back(static_cast<const void*>(trajectory_pub_->get_publisher_handle().get()));
262 RCLCPP_INFO(get_logger(), "Annotating message links for tracing with %zu subscriptions and %zu publications", link_subs.size(),
263 link_pubs.size());
264 TRACETOOLS_TRACEPOINT(message_link_periodic_async, link_subs.data(), link_subs.size(), link_pubs.data(), link_pubs.size());
265}
266
268 if (n_shots_ <= 0) {
269 RCLCPP_FATAL(this->get_logger(), "n_shots must be > 0, got %d", n_shots_);
270 exit(1);
271 }
272
273 // Create exactly once. This also supports a runtime horizon that differs from the generated default.
275 std::vector<double> new_time_steps(n_shots_, optimization_horizon_ / n_shots_);
276 RCLCPP_INFO(this->get_logger(), "Create OCP with: horizon = %f, n_shots = %d, dt = %f", optimization_horizon_, n_shots_,
277 new_time_steps.front());
279
280 if (status != ACADOS_SUCCESS) {
281 RCLCPP_FATAL(this->get_logger(), "%s_acados_create_with_discretization() returned status %d. Exiting.", model_name_.c_str(),
282 status);
283 exit(1);
284 }
285
292 if (nlp_dims_->N != n_shots_) {
293 RCLCPP_FATAL(this->get_logger(), "Created solver has N=%d, expected n_shots=%d. Exiting.", nlp_dims_->N, n_shots_);
294 exit(1);
295 }
296
297 xtraj_.resize(*nlp_dims_->nx * (n_shots_ + 1));
298 utraj_.resize(*nlp_dims_->nu * n_shots_);
299 control_guess_.clear();
300}
301
303 // free solver
305 if (status != 0) {
306 RCLCPP_ERROR(this->get_logger(), "%s_acados_free() returned status %d.", model_name_.c_str(), status);
307 }
308 // free solver capsule
310 if (status != 0) {
311 RCLCPP_ERROR(this->get_logger(), "%s_acados_free_capsule() returned status %d.", model_name_.c_str(), status);
312 }
313}
314
316 const int status = trajectory_optimization::acados_reset(ocp_capsule_, 1, 0, 0, 0);
317 if (status != ACADOS_SUCCESS) {
318 RCLCPP_ERROR(this->get_logger(), "%s_acados_reset() returned status %d. Recreating solver.", model_name_.c_str(), status);
319 freeSolver();
320 setupSolver();
321 }
322}
323
324bool TrajectoryOptimizationNode::setInitialGuess(const std::vector<double>& x_init, const rclcpp::Time& stamp) {
325 constexpr double MAX_CONTROL_GUESS_AGE_FACTOR = 0.5;
326 const int nx = *nlp_dims_->nx;
327 const int nu = *nlp_dims_->nu;
328 const double time_step = optimization_horizon_ / n_shots_;
329 const size_t expected_control_size = static_cast<size_t>(nu * n_shots_);
330 if (x_init.size() != static_cast<size_t>(nx)) {
331 RCLCPP_ERROR(get_logger(), "Initial state has size %zu, expected %d.", x_init.size(), nx);
332 return false;
333 }
334
335 // Fall back to zero controls if no sufficiently recent solver output is available.
336 std::vector<double> controls(expected_control_size, 0.0);
337 if (control_guess_.size() == expected_control_size) {
338 const double elapsed = (stamp - control_guess_stamp_).seconds();
339 if (elapsed >= 0.0 && elapsed < MAX_CONTROL_GUESS_AGE_FACTOR * optimization_horizon_) {
340 // Shift the previous controls to the current planning time and interpolate between shooting nodes.
341 std::vector<double> lower_controls(nu);
342 std::vector<double> upper_controls(nu);
343 for (int stage = 0; stage < n_shots_; ++stage) {
344 const double previous_stage = (elapsed + stage * time_step) / time_step;
345 if (previous_stage >= n_shots_) break;
346
347 const int lower_stage = static_cast<int>(std::floor(previous_stage));
348 const double interpolation_factor = previous_stage - lower_stage;
349 ocp_nlp_constraints_model_get(nlp_config_, nlp_dims_, nlp_in_, stage, "lbu", lower_controls.data());
350 ocp_nlp_constraints_model_get(nlp_config_, nlp_dims_, nlp_in_, stage, "ubu", upper_controls.data());
351 for (int control = 0; control < nu; ++control) {
352 const double lower_value = control_guess_[lower_stage * nu + control];
353 const double upper_value = lower_stage + 1 < n_shots_ ? control_guess_[(lower_stage + 1) * nu + control] : 0.0;
354 const double interpolated_control = lower_value + interpolation_factor * (upper_value - lower_value);
355 controls[stage * nu + control] = std::clamp(interpolated_control, lower_controls[control], upper_controls[control]);
356 }
357 }
358 }
359 }
360
361 ocp_nlp_out_set_values_to_zero(nlp_config_, nlp_dims_, nlp_out_);
362 std::vector<double> rollout_state = x_init;
363 std::vector<double> intermediate_state(nx);
364 std::vector<double> k1(nx), k2(nx), k3(nx), k4(nx);
365 const double integration_step = time_step / 2.0;
366 for (int stage = 0; stage < n_shots_; ++stage) {
367 double* control = &controls[stage * nu];
368 ocp_nlp_out_set(nlp_config_, nlp_dims_, nlp_out_, nlp_in_, stage, "x", rollout_state.data());
369 ocp_nlp_out_set(nlp_config_, nlp_dims_, nlp_out_, nlp_in_, stage, "u", control);
370
371 // Match sim_method_num_stages=4 and sim_method_num_steps=2 from the generated OCP.
372 for (int integration = 0; integration < 2; ++integration) {
373 trajectory_optimization::acados_evaluate_dynamics(ocp_capsule_, stage, rollout_state.data(), control, k1.data());
374 for (int i = 0; i < nx; ++i) intermediate_state[i] = rollout_state[i] + 0.5 * integration_step * k1[i];
375 trajectory_optimization::acados_evaluate_dynamics(ocp_capsule_, stage, intermediate_state.data(), control, k2.data());
376 for (int i = 0; i < nx; ++i) intermediate_state[i] = rollout_state[i] + 0.5 * integration_step * k2[i];
377 trajectory_optimization::acados_evaluate_dynamics(ocp_capsule_, stage, intermediate_state.data(), control, k3.data());
378 for (int i = 0; i < nx; ++i) intermediate_state[i] = rollout_state[i] + integration_step * k3[i];
379 trajectory_optimization::acados_evaluate_dynamics(ocp_capsule_, stage, intermediate_state.data(), control, k4.data());
380 for (int i = 0; i < nx; ++i) {
381 rollout_state[i] += integration_step / 6.0 * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]);
382 }
383 }
384 }
385 ocp_nlp_out_set(nlp_config_, nlp_dims_, nlp_out_, nlp_in_, n_shots_, "x", rollout_state.data());
386 return true;
387}
388
390 const auto cycle_start = SteadyClock::now();
391 if (debug_viz_) viz_circles_.clear();
392 if (rclcpp::Time(this->now()) - rclcpp::Time(ego_data_.header.stamp) > rclcpp::Duration::from_seconds(ego_data_timeout_)) {
393 RCLCPP_WARN(this->get_logger(), "EgoData outdated. Skipping planning cycle.");
394 return;
395 }
396 // init trajectory message and set header
397 trajectory_planning_msgs::msg::Trajectory::UniquePtr trajectory = std::make_unique<trajectory_planning_msgs::msg::Trajectory>();
398 initializeTrajectory(*trajectory);
399
400 trajectory->header.frame_id = vehicle_frame_id_;
401 trajectory->header.stamp = ego_data_.header.stamp; // use latest ego_data stamp as trajectory stamp
402
403 // init time-steps of trajectory to ensure increasing time-steps even for standstill trajectories
404 double dt = optimization_horizon_ / n_shots_;
405 for (int i = 0; i <= n_shots_; ++i) trajectory_planning_msgs::trajectory_access::setT(*trajectory, i * dt, i);
406
407 // check if the reference trajectory is standstill
408 if (trajectory_planning_msgs::trajectory_access::getStandstill(reference_trajectory_)) {
409 RCLCPP_WARN(this->get_logger(), "Standstill trajectory. Skipping planning cycle. Publish standstill trajectory.");
410 // transform trajectory to output frame
411 if (!trajectory2outputFrame(*trajectory)) {
412 return;
413 }
414 trajectory_planning_msgs::trajectory_access::setStandstill(*trajectory, true);
415 trajectory_pub_->publish(std::move(trajectory));
416 // Invalidate the warm start and reset the solver once when entering standstill.
417 if (!control_guess_.empty()) {
418 control_guess_.clear();
419 resetSolver();
420 }
421 return;
422 }
423
424 PerformanceMetrics metrics;
425 metrics.cycle = ++logging_cycle_;
426 metrics.ego_stamp_ns = rclcpp::Time(ego_data_.header.stamp).nanoseconds();
427 metrics.reference_stamp_ns = rclcpp::Time(reference_trajectory_.header.stamp).nanoseconds();
428 metrics.route_stamp_ns = rclcpp::Time(route_.header.stamp).nanoseconds();
429 metrics.reference_points = trajectory_planning_msgs::trajectory_access::getSamplePointSize(reference_trajectory_);
430 metrics.objects = static_cast<int>(object_list_.objects.size());
431 auto logCompletedCycle = [&]() {
432 metrics.cycle_ms = elapsedMilliseconds(cycle_start);
433 metrics.postprocessing_ms = metrics.cycle_ms - metrics.preprocessing_ms - metrics.solve_wall_ms;
435 performance_logger_->write(metrics);
436 }
437 };
438
439 // set initial state
440 std::vector<double> x_init(*nlp_dims_->nx, 0.0);
441 if (!trajectory_planning_msgs::trajectory_access::getStandstill(latest_valid_trajectory_)) {
443 } else {
444 RCLCPP_WARN(this->get_logger(),
445 "Latest available trajectory is standstill. Using ego data for initial state (high-level initialization).");
446 x_init = getHighLevelX0(ego_data_);
447 }
448
449 // debug print of initial state
450 std::stringstream ss;
451 ss << "Initial state: ";
452 for (size_t i = 0; i < x_init.size(); ++i) ss << "x[" << i << "]: " << x_init[i] << (i != x_init.size() - 1 ? ", " : "");
453 RCLCPP_DEBUG(this->get_logger(), "%s", ss.str().c_str());
454
455 ocp_nlp_constraints_model_set(nlp_config_, nlp_dims_, nlp_in_, nlp_out_, 0, "lbx", x_init.data());
456 ocp_nlp_constraints_model_set(nlp_config_, nlp_dims_, nlp_in_, nlp_out_, 0, "ubx", x_init.data());
457
458 // update inputs to the ocp; skip planning cycle if update fails
460 RCLCPP_WARN(this->get_logger(), "Failed to update inputs. Skipping planning cycle.");
461 return;
462 }
463
464 if (!setInitialGuess(x_init, rclcpp::Time(ego_data_.header.stamp))) {
465 control_guess_.clear();
466 return;
467 }
468
469 // solve the optimization problem
470 const auto solve_start = SteadyClock::now();
471 metrics.preprocessing_ms = elapsedMilliseconds(cycle_start, solve_start);
473 const auto solve_end = SteadyClock::now();
474 metrics.solve_wall_ms = elapsedMilliseconds(solve_start, solve_end);
475
477 performance_logger_ != nullptr || verbose_);
478
479 const bool usable_status =
480 metrics.status == ACADOS_SUCCESS || metrics.status == ACADOS_MAXITER || metrics.status == ACADOS_TIMEOUT;
481 if (usable_status) {
482 for (int ii = 0; ii <= nlp_dims_->N; ++ii) {
483 ocp_nlp_out_get(nlp_config_, nlp_dims_, nlp_out_, ii, "x", &xtraj_[ii * *nlp_dims_->nx]);
484 }
485 for (int ii = 0; ii < nlp_dims_->N; ++ii) {
486 ocp_nlp_out_get(nlp_config_, nlp_dims_, nlp_out_, ii, "u", &utraj_[ii * *nlp_dims_->nu]);
487 }
488 }
489
491 const bool finite_solution = usable_status && std::isfinite(metrics.cost_value) && std::isfinite(metrics.res_eq) &&
492 std::isfinite(metrics.res_ineq) &&
493 std::all_of(xtraj_.begin(), xtraj_.end(), [](double value) { return std::isfinite(value); }) &&
494 std::all_of(utraj_.begin(), utraj_.end(), [](double value) { return std::isfinite(value); });
495 const bool primal_feasible =
496 finite_solution && metrics.res_eq <= solver_opts->tol_eq && metrics.res_ineq <= solver_opts->tol_ineq;
497
498 if (!primal_feasible) {
499 printSolution(metrics);
500 RCLCPP_WARN(this->get_logger(),
501 "Rejecting solver output: status=%d finite=%d primal residuals=[eq=%e, ineq=%e] tolerances=[eq=%e, ineq=%e].",
502 metrics.status, finite_solution, metrics.res_eq, metrics.res_ineq, solver_opts->tol_eq, solver_opts->tol_ineq);
503 if (finite_solution && performance_logger_) {
505 static_cast<int>(p_obstacle_circles_shape_[0]));
506 }
507 if (finite_solution && (metrics.status == ACADOS_MAXITER || metrics.status == ACADOS_TIMEOUT)) {
508 // Preserve progress from a recoverable solve; the states are rolled out again from the next x_init.
510 control_guess_stamp_ = rclcpp::Time(ego_data_.header.stamp);
511 } else {
512 control_guess_.clear();
513 }
514 resetSolver();
515 logCompletedCycle();
516 return;
517 }
518
520 control_guess_stamp_ = rclcpp::Time(ego_data_.header.stamp);
521
522 printSolution(metrics);
523 if (debug_viz_) {
526 }
527
528 // convert output into trajectory message
529 convertToTrajectoryMsg(*trajectory);
530
531 bool standstill = true;
532 for (int i = 0; i < trajectory_planning_msgs::trajectory_access::getSamplePointSize(*trajectory); ++i) {
533 if (trajectory_planning_msgs::trajectory_access::getV(*trajectory, i) > standstill_threshold_) standstill = false;
534 }
535 trajectory_planning_msgs::trajectory_access::setStandstill(*trajectory, standstill);
536
537 // transform trajectory to output frame
538 if (!trajectory2outputFrame(*trajectory)) {
539 logCompletedCycle();
540 return;
541 }
542
543 latest_valid_trajectory_ = *trajectory;
544 trajectory_pub_->publish(std::move(trajectory));
545 metrics.published = true;
546 logCompletedCycle();
547 const char* cycle_time_color = metrics.cycle_ms <= 100.0 ? "\x1b[32m" : "\x1b[31m";
548 RCLCPP_INFO(this->get_logger(), "Published trajectory (cycle: %s%.2f ms\x1b[0m)", cycle_time_color, metrics.cycle_ms);
549}
550
551bool TrajectoryOptimizationNode::updateOcpInputs(const perception_msgs::msg::EgoData& ego_data,
552 const perception_msgs::msg::ObjectList& object_list,
553 const route_planning_msgs::msg::Route& route,
554 const trajectory_planning_msgs::msg::Trajectory& reference_trajectory) {
555 // transform inputs to target base_link frame
556 trajectory_planning_msgs::msg::Trajectory tf_reference_trajectory;
557 perception_msgs::msg::ObjectList tf_object_list;
558 route_planning_msgs::msg::Route tf_route;
559 try {
560 // reference trajectory
561 tf_reference_trajectory =
562 tf2_buffer_->transform(reference_trajectory, vehicle_frame_id_, tf2_ros::fromMsg(ego_data.header.stamp),
563 fixed_over_time_frame_id_, tf2::durationFromSec(0.01));
564 // object list
565 if (!object_list.objects.empty() && object_list.header.frame_id != vehicle_frame_id_) {
566 tf_object_list = tf2_buffer_->transform(object_list, vehicle_frame_id_, tf2_ros::fromMsg(ego_data.header.stamp),
567 fixed_over_time_frame_id_, tf2::durationFromSec(0.01));
568 } else {
569 tf_object_list = object_list;
570 }
571 keepNClosestObjects(tf_object_list, static_cast<int>(p_obstacle_circles_shape_[0]));
572 // route
573 if (!route.route_elements.empty() && route.header.frame_id != vehicle_frame_id_) {
574 tf_route = tf2_buffer_->transform(route, vehicle_frame_id_, tf2_ros::fromMsg(ego_data.header.stamp),
575 fixed_over_time_frame_id_, tf2::durationFromSec(0.1));
576 } else {
577 tf_route = route;
578 }
579 } catch (tf2::TransformException& ex) {
580 RCLCPP_WARN(this->get_logger(), "Transformation is not available. Ex: %s", ex.what());
581 return false;
582 }
583
584 if (trajectory_planning_msgs::trajectory_access::getSamplePointSize(tf_reference_trajectory) <= 0) {
585 RCLCPP_ERROR(this->get_logger(), "Reference trajectory contains no sample points.");
586 return false;
587 }
588
589 // update ocp parameters
590 try {
591 this->setOcpGlobalParameters(cost_weights_, tf_reference_trajectory, tf_route);
592 this->setOcpParameters(ego_data, tf_object_list);
593 } catch (const std::exception& e) {
594 RCLCPP_ERROR(this->get_logger(), "Exception while setting OCP parameters: %s", e.what());
595 return false;
596 }
597
598 return true;
599}
600
601void TrajectoryOptimizationNode::setOcpGlobalParameters(const std::vector<double>& cost_weights,
602 const trajectory_planning_msgs::msg::Trajectory& reference_trajectory,
603 const route_planning_msgs::msg::Route& route) {
604 const auto start_time = std::chrono::steady_clock::now();
605 std::vector<double> global_params;
606
607 // cost weights
608 const auto expected_cost_weights_size = static_cast<size_t>(p_cost_weights_shape_[0] * p_cost_weights_shape_[1]);
609 if (cost_weights.size() != expected_cost_weights_size) {
610 RCLCPP_ERROR(this->get_logger(), "Size of cost_weights (%zu) does not match expected size (%zu).", cost_weights.size(),
611 expected_cost_weights_size);
612 throw std::runtime_error("Size of cost_weights does not match expected size.");
613 }
614 global_params.insert(global_params.end(), cost_weights.begin(), cost_weights.end());
615
616 // other cost params
617 global_params.push_back(thw_);
618 global_params.push_back(d_min_obstacle_long_);
619 global_params.push_back(d_min_obstacle_lat_);
620 global_params.push_back(d_min_boundary_lat_);
621
622 // reference path (including boundaries)
623 const size_t n_ref_states = static_cast<size_t>(p_ref_path_shape_[0] * p_ref_path_shape_[1]);
624 std::vector<std::pair<double, double>> boundary_distances = normalBoundaryDistance(reference_trajectory, route);
625 // fill ref vector for ocp -> psi, x, y, v, d_bound_left, d_bound_right
626 std::vector<double> ref;
627 for (int i = 0; i < trajectory_planning_msgs::trajectory_access::getSamplePointSize(reference_trajectory); ++i) {
628 ref.push_back(trajectory_planning_msgs::trajectory_access::getTheta(reference_trajectory, i));
629 ref.push_back(trajectory_planning_msgs::trajectory_access::getX(reference_trajectory, i));
630 ref.push_back(trajectory_planning_msgs::trajectory_access::getY(reference_trajectory, i));
631 ref.push_back(trajectory_planning_msgs::trajectory_access::getV(reference_trajectory, i));
632 ref.push_back(boundary_distances[i].first); // left boundary distance
633 ref.push_back(boundary_distances[i].second); // right boundary distance
634 }
635 if (ref.size() >= n_ref_states) {
636 global_params.insert(global_params.end(), ref.begin(),
637 ref.begin() + static_cast<std::vector<double>::difference_type>(n_ref_states));
638 } else {
639 // Repeat the final valid state. Infinity padding can propagate NaNs through closest-point calculations.
640 global_params.insert(global_params.end(), ref.begin(), ref.end());
641 const size_t state_width = static_cast<size_t>(p_ref_path_shape_[1]);
642 while (global_params.size() < expected_cost_weights_size + 4 + n_ref_states) {
643 global_params.insert(global_params.end(), ref.end() - static_cast<std::vector<double>::difference_type>(state_width),
644 ref.end());
645 }
646 }
647
648 if (global_params.size() != static_cast<size_t>(nlp_dims_->np_global)) {
649 RCLCPP_ERROR(this->get_logger(), "Size of global parameters (%zu) does not match expected size (%d).", global_params.size(),
650 nlp_dims_->np_global);
651 throw std::runtime_error("Size of global parameters does not match expected size.");
652 }
654 ocp_capsule_, global_params.data(), static_cast<int>(global_params.size()));
655 if (status != ACADOS_SUCCESS) {
656 throw std::runtime_error("acados global parameter update failed with status " + std::to_string(status));
657 }
658 const auto elapsed_ms = std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - start_time).count();
659 RCLCPP_DEBUG(this->get_logger(), "setOcpGlobalParameters duration: %.3f ms", elapsed_ms);
660}
661
662void TrajectoryOptimizationNode::setOcpParameters(const perception_msgs::msg::EgoData& ego_data,
663 const perception_msgs::msg::ObjectList& object_list) {
664 const auto start_time = std::chrono::steady_clock::now();
665 struct PredictionData {
666 std::vector<double> time;
667 std::vector<double> x;
668 std::vector<double> y;
669 std::vector<double> yaw;
670 size_t index;
671 double probability;
672 };
673 std::vector<std::vector<PredictionData>> object_predictions(object_list.objects.size());
674 const double object_stamp = static_cast<double>(rclcpp::Time(object_list.header.stamp).nanoseconds()) / 1e9;
675 for (size_t j = 0; j < object_list.objects.size(); ++j) {
676 const auto& object = object_list.objects[j];
677 if (consider_objects_ != CONSIDER_OBJECTS::PREDICTED_OBJECTS || object.state_predictions.empty()) continue;
678
679 auto append_prediction = [&](const auto& state_prediction, size_t prediction_idx) {
680 PredictionData prediction{{object_stamp},
681 {perception_msgs::object_access::getX(object)},
682 {perception_msgs::object_access::getY(object)},
683 {perception_msgs::object_access::getYaw(object)},
684 prediction_idx,
685 state_prediction.probability};
686 for (const auto& predicted_state : state_prediction.states) {
687 prediction.time.push_back(static_cast<double>(rclcpp::Time(predicted_state.header.stamp).nanoseconds()) / 1e9);
688 prediction.x.push_back(perception_msgs::object_access::getX(predicted_state));
689 prediction.y.push_back(perception_msgs::object_access::getY(predicted_state));
690 prediction.yaw.push_back(perception_msgs::object_access::getYaw(predicted_state));
691 }
692 object_predictions[j].push_back(std::move(prediction));
693 };
694
695 for (size_t prediction_idx = 0; prediction_idx < object.state_predictions.size(); ++prediction_idx) {
696 const auto& state_prediction = object.state_predictions[prediction_idx];
697 if (state_prediction.probability > min_prediction_probability_) {
698 append_prediction(state_prediction, prediction_idx);
699 }
700 }
701 if (object_predictions[j].empty()) append_prediction(object.state_predictions[0], 0);
702 }
703
704 // loop over shooting intervals
705 double floating_dynamic_weight = 1.0;
706 double dt = optimization_horizon_ / n_shots_;
707 for (int i = 0; i <= n_shots_; ++i) {
708 // dynamic weight
709 int idx = 0;
710 int n = 1;
711 std::vector<int> idx_dynamic_weight(n);
712 // fill vector with values from idx to idx + n
713 std::iota(idx_dynamic_weight.begin(), idx_dynamic_weight.end(), idx);
714 int status = trajectory_optimization::acados_update_params_sparse(ocp_capsule_, i, idx_dynamic_weight.data(),
715 &floating_dynamic_weight, n);
716 if (status != ACADOS_SUCCESS) {
717 throw std::runtime_error("acados dynamic-weight update failed with status " + std::to_string(status));
718 }
719 floating_dynamic_weight *= dynamic_weight_;
720
721 // obstacles
722 idx += n;
723 n = static_cast<int>(p_obstacle_circles_shape_[0] * p_obstacle_circles_shape_[1]);
724 std::vector<double> circles; // [x1, y1, r1, x2, y2, r2, ...]
725
726 for (size_t j = 0; j < object_list.objects.size(); ++j) {
727 std::vector<std::tuple<double, double, double>> target_states;
728 if (!object_predictions[j].empty()) {
729 for (const auto& prediction : object_predictions[j]) {
730 double x_tgt = 0.0, y_tgt = 0.0, yaw_tgt = 0.0;
731 double des_time = static_cast<double>(rclcpp::Time(ego_data.header.stamp).nanoseconds()) / 1e9 + dt * i;
732 if (des_time > prediction.time.back()) {
733 const double relative_des_time = des_time - prediction.time.front();
734 const double relative_max_time = prediction.time.back() - prediction.time.front();
735 RCLCPP_WARN(this->get_logger(),
736 "Prediction horizon shorter than requested interpolation time. "
737 "object=%zu prediction=%zu probability=%.3f desired_rel=%.3f s max_rel=%.3f s n_states=%zu. "
738 "Using last prediction state.",
739 j, prediction.index, prediction.probability, relative_des_time, relative_max_time,
740 prediction.time.size() - 1);
741 x_tgt = prediction.x.back();
742 y_tgt = prediction.y.back();
743 yaw_tgt = prediction.yaw.back();
744 } else {
745 linearInterpolation(prediction.time, prediction.x, des_time, x_tgt);
746 linearInterpolation(prediction.time, prediction.y, des_time, y_tgt);
747 linearInterpolation(prediction.time, prediction.yaw, des_time, yaw_tgt, true);
748 }
749 target_states.emplace_back(x_tgt, y_tgt, yaw_tgt);
750 }
751 } else {
752 target_states.emplace_back(perception_msgs::object_access::getX(object_list.objects[j]),
753 perception_msgs::object_access::getY(object_list.objects[j]),
754 perception_msgs::object_access::getYaw(object_list.objects[j]));
755 }
756
757 double alpha = std::atan2(object_list.objects[j].state.reference_point.translation_to_geometric_center.y,
758 object_list.objects[j].state.reference_point.translation_to_geometric_center.x);
759 double a = std::sqrt(std::pow(object_list.objects[j].state.reference_point.translation_to_geometric_center.x, 2) +
760 std::pow(object_list.objects[j].state.reference_point.translation_to_geometric_center.y, 2));
761 for (auto& [x_tgt, y_tgt, yaw_tgt] : target_states) {
762 // ensure that x_tgt and y_tgt represent the geometric center of the object
763 double beta = wrap_angle_rad(yaw_tgt - alpha);
764 x_tgt += a * std::cos(beta);
765 y_tgt += a * std::sin(beta);
766
767 std::vector<double> obj_circles =
768 discretizeBB2Circles(x_tgt, y_tgt, yaw_tgt, perception_msgs::object_access::getLength(object_list.objects[j]),
769 perception_msgs::object_access::getWidth(object_list.objects[j]));
770
771 circles.insert(circles.end(), obj_circles.begin(), obj_circles.end());
772 if (circles.size() >= static_cast<size_t>(n)) {
773 circles.resize(n);
774 break;
775 }
776 }
777 if (circles.size() >= static_cast<size_t>(n)) {
778 break;
779 }
780 }
781 // fill up with dummy "ghost" obstacle circles at (10000, 10000) to avoid NaNs in the optimization problem
782 // TODO: improve this // NOLINT(google-readability-todo)
783 while (circles.size() < static_cast<size_t>(n)) {
784 std::vector<double> dummy_circle = {10000.0, 10000.0, 1.0};
785 circles.insert(circles.end(), dummy_circle.begin(), dummy_circle.end());
786 }
787 if ((circles.size() % p_obstacle_circles_shape_[1]) != 0) {
788 RCLCPP_WARN(this->get_logger(), "Circles vector size is not a multiple of the circle shape. Resizing.");
789 circles.resize(n);
790 }
791 if (debug_viz_) viz_circles_.insert(viz_circles_.end(), circles.begin(), circles.end());
792
793 std::vector<int> idx_obstacles(n);
794 // fill vector with values from idx to idx + n
795 std::iota(idx_obstacles.begin(), idx_obstacles.end(), idx);
796 status = trajectory_optimization::acados_update_params_sparse(ocp_capsule_, i, idx_obstacles.data(), circles.data(), n);
797 if (status != ACADOS_SUCCESS) {
798 throw std::runtime_error("acados obstacle update failed with status " + std::to_string(status));
799 }
800 }
801 const auto elapsed_ms = std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - start_time).count();
802 RCLCPP_DEBUG(this->get_logger(), "setOcpParameters duration: %.3f ms", elapsed_ms);
803}
804
805} // namespace trajectory_optimization
static void collectConstraintDiagnostics(PerformanceMetrics &metrics, ocp_nlp_solver *solver, const ocp_nlp_dims *dims, int obstacle_circles)
Collects the largest equality and inequality constraint violations from acados.
static void collectSolverStatistics(PerformanceMetrics &metrics, ocp_nlp_solver *solver, ocp_nlp_config *config, ocp_nlp_dims *dims, ocp_nlp_in *input, ocp_nlp_out *output, bool collect_details)
Reads timing, iteration, cost, and residual statistics from acados.
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 setupSolver()
Creates the acados solver instance and initializes its state buffers.
trajectory_planning_msgs::msg::Trajectory reference_trajectory_
void setup()
Creates ROS interfaces, initializes cached messages and prepares the solver.
void routeCallback(const route_planning_msgs::msg::Route::ConstSharedPtr msg)
Stores the current route used for boundary constraints when enabled.
Definition callbacks.cpp:31
void freeSolver()
Frees the acados solver and clears cached optimization results.
trajectory_planning_msgs::msg::Trajectory latest_valid_trajectory_
void vizCircles(const std::vector< double > &obstacles)
Publishes visualization markers for the obstacle circles currently used by the optimizer.
Definition utils.cpp:348
virtual void initializeTrajectory(trajectory_planning_msgs::msg::Trajectory &trajectory)=0
Initializes an output trajectory message with the model-specific message type and size.
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
void setOcpParameters(const perception_msgs::msg::EgoData &ego_data, const perception_msgs::msg::ObjectList &object_list)
Writes stage-wise obstacle and dynamic weighting parameters into the OCP.
virtual void convertToTrajectoryMsg(trajectory_planning_msgs::msg::Trajectory &trajectory)=0
Maps the optimized state trajectory into the model-specific output message fields.
void objectListCallback(const perception_msgs::msg::ObjectList::ConstSharedPtr msg)
Stores the current object list when object handling is enabled.
Definition callbacks.cpp:12
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 egoDataCallback(const perception_msgs::msg::EgoData::ConstSharedPtr msg)
Stores the latest ego state used by the optimizer.
Definition callbacks.cpp:7
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
void planningCycle()
Runs one full planning cycle from input preparation, over solver execution, to trajectory publication...
std::vector< std::tuple< std::string, std::function< void(const rclcpp::Parameter &)> > > auto_reconfigurable_params_
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
bool updateOcpInputs(const perception_msgs::msg::EgoData &ego_data, const perception_msgs::msg::ObjectList &object_list, const route_planning_msgs::msg::Route &route, const trajectory_planning_msgs::msg::Trajectory &reference_trajectory)
Transforms external inputs into the optimizer frame and writes them into the OCP.
rclcpp::Subscription< route_planning_msgs::msg::Route >::SharedPtr route_sub_
rcl_interfaces::msg::SetParametersResult parametersCallback(const std::vector< rclcpp::Parameter > &parameters)
Applies parameter updates that can be reconfigured while the node is running.
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
virtual std::vector< double > getBiLevelX0(const perception_msgs::msg::EgoData &ego_data)=0
Computes the initial optimizer state using bi-level stabilizaion based on the model-specific EgoData ...
rclcpp::Subscription< perception_msgs::msg::EgoData >::SharedPtr ego_data_sub_
TrajectoryOptimizationNode(const std::string node_name, const rclcpp::NodeOptions &options)
Initializes the base node and loads the common optimizer configuration.
rclcpp::Publisher< visualization_msgs::msg::MarkerArray >::SharedPtr ego_circles_pub_
void declareAndLoadParameter(const std::string &name, T &param, const std::string &description, const bool add_to_auto_reconfigurable_params=true, const bool is_required=false, const bool read_only=false, const std::optional< double > &from_value=std::nullopt, const std::optional< double > &to_value=std::nullopt, const std::optional< double > &step_value=std::nullopt, const std::string &additional_constraints="")
Declares a ROS parameter, loads its value and optionally registers it for runtime updates.
rclcpp::Subscription< perception_msgs::msg::ObjectList >::SharedPtr object_list_sub_
void referenceTrajectoryCallback(const trajectory_planning_msgs::msg::Trajectory::ConstSharedPtr msg)
Updates the reference trajectory and optionally triggers optimization immediately.
Definition callbacks.cpp:22
~TrajectoryOptimizationNode() override
Releases solver resources owned by the node.
rclcpp::Publisher< trajectory_planning_msgs::msg::Trajectory >::SharedPtr trajectory_pub_
std::shared_ptr< tf2_ros::TransformListener > tf2_listener_
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
rclcpp::Subscription< trajectory_planning_msgs::msg::Trajectory >::SharedPtr reference_trajectory_sub_
void setOcpGlobalParameters(const std::vector< double > &cost_weights, const trajectory_planning_msgs::msg::Trajectory &reference_trajectory, const route_planning_msgs::msg::Route &route)
Writes stage-independent data into the OCP.
bool setInitialGuess(const std::vector< double > &x_init, const rclcpp::Time &stamp)
Builds and sets a dynamically consistent NLP initial guess from the current state and cached controls...
void resetSolver()
Resets the optimizer memory while retaining the generated solver instance.
virtual std::vector< double > getHighLevelX0(const perception_msgs::msg::EgoData &ego_data)=0
Computes the initial optimizer state using high-level stabilization based on the model-specific EgoDa...
Namespace for trajectory_optimization package.
int acados_solve(ocp_model_capsule_t capsule)
Wrapper around the generated acados solve function.
int acados_free(ocp_model_capsule_t capsule)
Wrapper around the generated acados solver cleanup function.
ocp_nlp_solver * acados_get_nlp_solver(ocp_model_capsule_t capsule)
Wrapper around the generated accessor for the OCP solver instance.
ocp_nlp_out * acados_get_nlp_out(ocp_model_capsule_t capsule)
Wrapper around the generated accessor for the OCP output structure.
ocp_nlp_config * acados_get_nlp_config(ocp_model_capsule_t capsule)
Wrapper around the generated accessor for the OCP configuration.
int acados_create_with_discretization(ocp_model_capsule_t capsule, int n_time_steps, double *new_time_steps)
Wrapper around the generated acados create function with custom discretization.
void * acados_get_nlp_opts(ocp_model_capsule_t capsule)
Wrapper around the generated accessor for the OCP solver options.
int acados_free_capsule(ocp_model_capsule_t capsule)
Wrapper around the generated acados capsule cleanup function.
int acados_update_params_sparse(ocp_model_capsule_t capsule, int stage, int *idx, double *p, int n_update)
Wrapper around the generated sparse parameter update function.
ocp_model_capsule_t acados_create_capsule(const std::string &model_name)
Creates the model-specific acados capsule selected by the configured model name.
ocp_nlp_dims * acados_get_nlp_dims(ocp_model_capsule_t capsule)
Wrapper around the generated accessor for the OCP dimensions.
int acados_reset(ocp_model_capsule_t capsule, int reset_qp_solver_mem, int reset_numerical_values, int reset_solver_options, int reset_x_to_x0_bar)
Resets selected solver memory without freeing and recreating the capsule.
const ocp_nlp_opts * acados_get_common_nlp_opts(ocp_nlp_config *config, void *solver_opts)
Returns the common NLP options stored inside the generated solver-specific options.
ocp_nlp_in * acados_get_nlp_in(ocp_model_capsule_t capsule)
Wrapper around the generated accessor for the OCP input structure.
void acados_evaluate_dynamics(ocp_model_capsule_t capsule, int stage, double *x, double *u, double *x_dot)
Evaluates the explicit model dynamics for a shooting stage.
int acados_set_p_global_and_precompute_dependencies(ocp_model_capsule_t capsule, double *data, int data_len)
Wrapper around the generated global-parameter update function.