trajectory_optimization v1.3.1
Loading...
Searching...
No Matches
trajectory_optimization::TrajectoryOptimizationNode Class Referenceabstract

#include <trajectory_optimization_node.hpp>

Inheritance diagram for trajectory_optimization::TrajectoryOptimizationNode:
trajectory_optimization::TrajectoryOptimizationAckermannNode trajectory_optimization::TrajectoryOptimizationRWSNode

Public Member Functions

 TrajectoryOptimizationNode (const std::string node_name, const rclcpp::NodeOptions &options)
 Initializes the base node and loads the common optimizer configuration.
 
 ~TrajectoryOptimizationNode () override
 Releases solver resources owned by the node.
 
 TrajectoryOptimizationNode (const TrajectoryOptimizationNode &)=delete
 Copy construction is disabled because the node owns non-copyable runtime resources.
 
TrajectoryOptimizationNodeoperator= (const TrajectoryOptimizationNode &)=delete
 Copy assignment is disabled because the node owns non-copyable runtime resources.
 
 TrajectoryOptimizationNode (TrajectoryOptimizationNode &&)=delete
 Move construction is disabled to keep solver and ROS handles bound to a single instance.
 
TrajectoryOptimizationNodeoperator= (TrajectoryOptimizationNode &&)=delete
 Move assignment is disabled to keep solver and ROS handles bound to a single instance.
 

Protected Types

enum  CONSIDER_BOUNDARIES { NO_BOUNDS = 0 , SUGGESTED_LANE = 1 , INCLUDING_ADJACENT = 2 , DRIVABLE_SPACE = 3 }
 
enum  CONSIDER_OBJECTS { NO_OBJECTS = 0 , STATIC_OBJECTS = 1 , PREDICTED_OBJECTS = 2 }
 

Protected Member Functions

template<typename T >
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.
 
rcl_interfaces::msg::SetParametersResult parametersCallback (const std::vector< rclcpp::Parameter > &parameters)
 Applies parameter updates that can be reconfigured while the node is running.
 
void setup ()
 Creates ROS interfaces, initializes cached messages and prepares the solver.
 
void resetSolver ()
 Resets the optimizer memory while retaining the generated solver instance.
 
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 setupSolver ()
 Creates the acados solver instance and initializes its state buffers.
 
void freeSolver ()
 Frees the acados solver and clears cached optimization results.
 
void printSolution (const PerformanceMetrics &metrics)
 Logs solver status and optional debug statistics for the last optimization run.
 
void logPerformance (const PerformanceMetrics &metrics)
 Emits one machine-readable performance record when performance logging is enabled.
 
bool trajectory2outputFrame (trajectory_planning_msgs::msg::Trajectory &trajectory)
 Transforms the planned trajectory into the configured output frame (trajectory_frame_id_) if required.
 
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.
 
void egoDataCallback (const perception_msgs::msg::EgoData::ConstSharedPtr msg)
 Stores the latest ego state used by the optimizer.
 
void objectListCallback (const perception_msgs::msg::ObjectList::ConstSharedPtr msg)
 Stores the current object list when object handling is enabled.
 
void referenceTrajectoryCallback (const trajectory_planning_msgs::msg::Trajectory::ConstSharedPtr msg)
 Updates the reference trajectory and optionally triggers optimization immediately.
 
void routeCallback (const route_planning_msgs::msg::Route::ConstSharedPtr msg)
 Stores the current route used for boundary constraints when enabled.
 
void planningCycle ()
 Runs one full planning cycle from input preparation, over solver execution, to trajectory publication.
 
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, const std::vector< double > &x_init)
 Transforms external inputs into the optimizer frame and writes them into the OCP.
 
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.
 
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.
 
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.
 
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.
 
void vizCircles (const std::vector< double > &obstacles)
 Publishes visualization markers for the obstacle circles currently used by the optimizer.
 
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.
 
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.
 
virtual void initializeTrajectory (trajectory_planning_msgs::msg::Trajectory &trajectory)=0
 Initializes an output trajectory message with the model-specific message type and size.
 
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 type.
 
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 EgoData type.
 
virtual void convertToTrajectoryMsg (trajectory_planning_msgs::msg::Trajectory &trajectory)=0
 Maps the optimized state trajectory into the model-specific output message fields.
 

Static Protected Member Functions

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.
 
static void keepNClosestObjects (perception_msgs::msg::ObjectList &object_list, const int n_objects)
 Keeps the nearest forward objects and discards the remaining entries.
 

Protected Attributes

OnSetParametersCallbackHandle::SharedPtr parameters_callback_
 
rclcpp::Subscription< perception_msgs::msg::EgoData >::SharedPtr ego_data_sub_
 
rclcpp::Subscription< perception_msgs::msg::ObjectList >::SharedPtr object_list_sub_
 
rclcpp::Subscription< route_planning_msgs::msg::Route >::SharedPtr route_sub_
 
rclcpp::Subscription< trajectory_planning_msgs::msg::Trajectory >::SharedPtr reference_trajectory_sub_
 
rclcpp::Publisher< trajectory_planning_msgs::msg::Trajectory >::SharedPtr trajectory_pub_
 
rclcpp::Publisher< visualization_msgs::msg::MarkerArray >::SharedPtr circles_pub_
 
rclcpp::Publisher< visualization_msgs::msg::MarkerArray >::SharedPtr ego_circles_pub_
 
rclcpp::Publisher< visualization_msgs::msg::MarkerArray >::SharedPtr boundary_pub_
 
rclcpp::TimerBase::SharedPtr planning_timer_
 
std::unique_ptr< tf2_ros::Buffer > tf2_buffer_
 
std::shared_ptr< tf2_ros::TransformListener > tf2_listener_
 
perception_msgs::msg::EgoData ego_data_
 
perception_msgs::msg::ObjectList object_list_
 
route_planning_msgs::msg::Route route_
 
trajectory_planning_msgs::msg::Trajectory reference_trajectory_
 
std::vector< std::tuple< std::string, std::function< void(const rclcpp::Parameter &)> > > auto_reconfigurable_params_
 
std::string vehicle_frame_id_ = "base_link"
 
std::string trajectory_frame_id_ = "base_link"
 
std::string fixed_over_time_frame_id_ = "map"
 
std::string model_name_ = "karl"
 
double ego_data_timeout_ = 1.0
 
double optimization_freq_ = 10.0
 
int n_shots_ = 50
 
double optimization_horizon_ = 1.0
 
bool verbose_ = false
 
bool performance_logging_ = false
 
bool debug_viz_ = false
 
double standstill_threshold_ = 0.45
 
bool high_level_stabilization_ = false
 
bool add_x_init_to_ref_ = false
 
uint8_t consider_objects_ = CONSIDER_OBJECTS::PREDICTED_OBJECTS
 
uint8_t consider_boundaries_ = CONSIDER_BOUNDARIES::SUGGESTED_LANE
 
bool run_as_callback_ = false
 
double bi_level_dV_ = 5.0
 
double bi_level_dA_ = 2.0
 
double bi_level_dY_ = 0.1
 
double bi_level_dYaw_ = 5.0
 
trajectory_planning_msgs::msg::Trajectory latest_valid_trajectory_
 
std::vector< double > control_guess_
 
rclcpp::Time control_guess_stamp_ {0, 0, RCL_ROS_TIME}
 
std::vector< double > viz_circles_
 
std::vector< double > cost_weights_ = std::vector<double>(12, 1.0)
 
double dynamic_weight_ = 1.0
 
double thw_ = 2.0
 
double d_min_obstacle_long_ = 5.0
 
double d_min_obstacle_lat_ = 0.5
 
double d_min_boundary_lat_ = 0.0
 
double min_prediction_probability_ = 0.0
 
std::vector< int64_t > p_cost_weights_shape_ = {12, 1}
 
std::vector< int64_t > p_ref_path_shape_ = {51, 6}
 
std::vector< int64_t > p_obstacle_circles_shape_ = {30, 3}
 
ocp_model_capsule_t ocp_capsule_
 
ocp_nlp_config * nlp_config_
 
ocp_nlp_dims * nlp_dims_
 
ocp_nlp_in * nlp_in_
 
ocp_nlp_out * nlp_out_
 
ocp_nlp_solver * nlp_solver_
 
void * nlp_opts_
 
std::vector< double > xtraj_
 
std::vector< double > utraj_
 
uint64_t logging_cycle_ = 0
 
std::unique_ptr< PerformanceLoggerperformance_logger_
 

Detailed Description

Definition at line 45 of file trajectory_optimization_node.hpp.

Member Enumeration Documentation

◆ CONSIDER_BOUNDARIES

◆ CONSIDER_OBJECTS

Constructor & Destructor Documentation

◆ TrajectoryOptimizationNode() [1/3]

trajectory_optimization::TrajectoryOptimizationNode::TrajectoryOptimizationNode ( const std::string node_name,
const rclcpp::NodeOptions & options )
explicit

Initializes the base node and loads the common optimizer configuration.

Parameters
[in]node_nameName of the ROS node instance.
[in]optionsROS node options used for construction.

Definition at line 30 of file trajectory_optimization_node.cpp.

31 : rclcpp::Node(node_name, options) {
32 // declare and load node parameters
33 this->declareAndLoadParameter("vehicle_frame_id", vehicle_frame_id_,
34 "Frame ID of local vehicle frame (the ocp is defined in this frame)");
35 this->declareAndLoadParameter("trajectory_frame_id", trajectory_frame_id_, "Frame ID of output trajectory");
36 this->declareAndLoadParameter("fixed_over_time_frame_id", fixed_over_time_frame_id_,
37 "Frame ID of frame that is fixed over time for finding temporal transforms");
38 this->declareAndLoadParameter("ego_data_timeout", ego_data_timeout_,
39 "Time after which a received ego vehicle data is considered invalid [s]. Optimization will not "
40 "be run if ego data is invalid.");
41 this->declareAndLoadParameter("model_name", model_name_,
42 "Name of the model to be used for trajectory optimization [karl, shuttle]");
43 this->declareAndLoadParameter("optimization_frequency", optimization_freq_, "Optimization frequency in Hz");
44 this->declareAndLoadParameter("n_shots", n_shots_, "Number of shooting intervals in optimization horizon");
45 this->declareAndLoadParameter("optimization_horizon", optimization_horizon_, "Optimization Horizon in seconds");
46 this->declareAndLoadParameter("verbose", verbose_, "Print solver statistics");
47 this->declareAndLoadParameter("performance_logging", performance_logging_,
48 "Write one CSV record for every completed solver run", false, false, true);
49 this->declareAndLoadParameter("debug_visualization", debug_viz_, "Publish debug visualization markers (e.g. obstacle circles)");
50 this->declareAndLoadParameter("run_as_callback", run_as_callback_,
51 "Run OCP once for each received reference trajectory (true) or on a timer (false)");
52 this->declareAndLoadParameter("cost_weights", cost_weights_, "Cost function weights");
53 this->declareAndLoadParameter("dynamic_weight", dynamic_weight_, "Dynamic weight alpha");
54 this->declareAndLoadParameter("thw", thw_, "Time headway to front vehicle");
55 this->declareAndLoadParameter("d_min_obstacle_long", d_min_obstacle_long_,
56 "Minimum distance to keep to obstacle in longitudinal direction [m]");
57 this->declareAndLoadParameter("d_min_obstacle_lat", d_min_obstacle_lat_,
58 "Minimum distance to keep to obstacle in lateral direction [m]");
59 this->declareAndLoadParameter("d_min_boundary_lat", d_min_boundary_lat_,
60 "Minimum distance to keep to boundary in lateral direction [m]");
61 this->declareAndLoadParameter("standstill_threshold", standstill_threshold_,
62 "Threshold for standstill detection [m/s]. If the velocities of all states are below this "
63 "threshold, publish standstill trajectory");
64 this->declareAndLoadParameter("high_level_stabilization", high_level_stabilization_,
65 "Use high-level stabilization strategy for init state (= init with current EgoData)");
67 "add_x_init_to_ref", add_x_init_to_ref_,
68 "add initial state of OCP to beginning of reference trajectory if this starts in front of ego vehicle");
70 "consider_objects", consider_objects_,
71 "consider objects in optimization: 0 = none, 1 = static (no prediction), 2 = dynamic (with prediction)");
72 this->declareAndLoadParameter("min_prediction_probability", min_prediction_probability_,
73 "Minimum probability for predicted object states to be considered", true, false, false, 0.0, 1.0);
75 "consider_boundaries", consider_boundaries_,
76 "consider route boundaries in optimization: 0 = no, 1 = suggested lane, 2 = including adjacent, 3 = drivable space");
77 this->declareAndLoadParameter("bi_level_dV", bi_level_dV_,
78 "Threshold for bi-level stabilization: maximum velocity difference [m/s]");
79 this->declareAndLoadParameter("bi_level_dA", bi_level_dA_,
80 "Threshold for bi-level stabilization: maximum acceleration difference [m/s^2]");
81 this->declareAndLoadParameter("bi_level_dY", bi_level_dY_, "Threshold for bi-level stabilization: maximum y-offset [m]");
82 this->declareAndLoadParameter("bi_level_dYaw", bi_level_dYaw_,
83 "Threshold for bi-level stabilization: maximum yaw difference [degree]");
85 try {
86 performance_logger_ = std::make_unique<PerformanceLogger>(get_name());
87 RCLCPP_INFO(get_logger(), "Writing benchmark logs to '%s'.", performance_logger_->path().c_str());
88 } catch (const std::exception& error) {
89 RCLCPP_ERROR(get_logger(), "Could not initialize benchmark logging: %s", error.what());
90 }
91 }
92 this->setup();
93}
void setup()
Creates ROS interfaces, initializes cached messages and prepares the solver.
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.

◆ ~TrajectoryOptimizationNode()

trajectory_optimization::TrajectoryOptimizationNode::~TrajectoryOptimizationNode ( )
override

Releases solver resources owned by the node.

Definition at line 95 of file trajectory_optimization_node.cpp.

95{ freeSolver(); }
void freeSolver()
Frees the acados solver and clears cached optimization results.

◆ TrajectoryOptimizationNode() [2/3]

trajectory_optimization::TrajectoryOptimizationNode::TrajectoryOptimizationNode ( const TrajectoryOptimizationNode & )
delete

Copy construction is disabled because the node owns non-copyable runtime resources.

◆ TrajectoryOptimizationNode() [3/3]

trajectory_optimization::TrajectoryOptimizationNode::TrajectoryOptimizationNode ( TrajectoryOptimizationNode && )
delete

Move construction is disabled to keep solver and ROS handles bound to a single instance.

Member Function Documentation

◆ convertToTrajectoryMsg()

virtual void trajectory_optimization::TrajectoryOptimizationNode::convertToTrajectoryMsg ( trajectory_planning_msgs::msg::Trajectory & trajectory)
protectedpure virtual

Maps the optimized state trajectory into the model-specific output message fields.

Parameters
[in,out]trajectoryOutput trajectory message.

Implemented in trajectory_optimization::TrajectoryOptimizationAckermannNode, and trajectory_optimization::TrajectoryOptimizationRWSNode.

◆ declareAndLoadParameter()

template<typename T >
void trajectory_optimization::TrajectoryOptimizationNode::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 = "" )
protected

Declares a ROS parameter, loads its value and optionally registers it for runtime updates.

Template Parameters
TParameter value type.
Parameters
[in]nameParameter name.
[out]paramMember variable to store the parameter value.
[in]descriptionHuman-readable parameter description.
[in]add_to_auto_reconfigurable_paramsWhether parameter updates automatically update the member variable.
[in]is_requiredWhether the node should fail if the parameter is not set.
[in]read_onlyWhether the parameter is exposed as read-only.
[in]from_valueOptional lower bound for numeric parameters.
[in]to_valueOptional upper bound for numeric parameters.
[in]step_valueOptional step size for numeric parameters.
[in]additional_constraintsAdditional free-form constraint text for the parameter descriptor.

Definition at line 98 of file trajectory_optimization_node.cpp.

107 {
108 rcl_interfaces::msg::ParameterDescriptor param_desc;
109 param_desc.description = description;
110 param_desc.additional_constraints = additional_constraints;
111 param_desc.read_only = read_only;
112
113 auto type = rclcpp::ParameterValue(param).get_type();
114
115 if (from_value.has_value() && to_value.has_value()) {
116 if constexpr (std::is_integral_v<T>) {
117 rcl_interfaces::msg::IntegerRange range;
118 range.set__from_value(static_cast<T>(from_value.value())).set__to_value(static_cast<T>(to_value.value()));
119 if (step_value.has_value()) range.set__step(static_cast<T>(step_value.value()));
120 param_desc.integer_range = {range};
121 } else if constexpr (std::is_floating_point_v<T>) {
122 rcl_interfaces::msg::FloatingPointRange range;
123 range.set__from_value(static_cast<T>(from_value.value())).set__to_value(static_cast<T>(to_value.value()));
124 if (step_value.has_value()) range.set__step(static_cast<T>(step_value.value()));
125 param_desc.floating_point_range = {range};
126 } else {
127 RCLCPP_WARN(this->get_logger(), "Parameter type of parameter '%s' does not support specifying a range", name.c_str());
128 }
129 }
130
131 this->declare_parameter(name, type, param_desc);
132
133 try {
134 param = this->get_parameter(name).get_value<T>();
135 std::stringstream ss;
136 ss << "Loaded parameter '" << name << "': ";
137 if constexpr (is_vector_v<T>) {
138 ss << "[";
139 for (const auto& element : param) ss << element << (&element != &param.back() ? ", " : "");
140 ss << "]";
141 } else {
142 ss << param;
143 }
144 RCLCPP_INFO_STREAM(this->get_logger(), ss.str());
145 } catch (rclcpp::exceptions::ParameterUninitializedException&) {
146 if (is_required) {
147 RCLCPP_FATAL_STREAM(this->get_logger(), "Missing required parameter '" << name << "', exiting");
148 exit(EXIT_FAILURE);
149 } else {
150 std::stringstream ss;
151 ss << "Missing parameter '" << name << "', using default value: ";
152 if constexpr (is_vector_v<T>) {
153 ss << "[";
154 for (const auto& element : param) ss << element << (&element != &param.back() ? ", " : "");
155 ss << "]";
156 } else {
157 ss << param;
158 }
159 RCLCPP_WARN_STREAM(this->get_logger(), ss.str());
160 this->set_parameters({rclcpp::Parameter(name, rclcpp::ParameterValue(param))});
161 }
162 }
163
164 if (add_to_auto_reconfigurable_params) {
165 std::function<void(const rclcpp::Parameter&)> setter = [&param](const rclcpp::Parameter& p) { param = p.get_value<T>(); };
166 auto_reconfigurable_params_.push_back(std::make_tuple(name, setter));
167 }
168}
std::vector< std::tuple< std::string, std::function< void(const rclcpp::Parameter &)> > > auto_reconfigurable_params_

◆ discretizeBB2Circles()

std::vector< double > trajectory_optimization::TrajectoryOptimizationNode::discretizeBB2Circles ( const double x,
const double y,
const double yaw,
const double length,
const double width )
protected

Approximates an oriented bounding box with a set of obstacle circles.

Parameters
[in]xBounding-box center x-coordinate.
[in]yBounding-box center y-coordinate.
[in]yawBounding-box heading.
[in]lengthBounding-box length.
[in]widthBounding-box width.
Returns
Flattened circle list in the configured obstacle parameter layout.

Definition at line 116 of file utils.cpp.

117 {
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}

◆ egoDataCallback()

void trajectory_optimization::TrajectoryOptimizationNode::egoDataCallback ( const perception_msgs::msg::EgoData::ConstSharedPtr msg)
protected

Stores the latest ego state used by the optimizer.

Parameters
[in]msgIncoming ego vehicle message.

Definition at line 7 of file callbacks.cpp.

7 {
8 RCLCPP_DEBUG(this->get_logger(), "Received ego data");
9 ego_data_ = *msg;
10}

◆ freeSolver()

void trajectory_optimization::TrajectoryOptimizationNode::freeSolver ( )
protected

Frees the acados solver and clears cached optimization results.

Definition at line 306 of file trajectory_optimization_node.cpp.

306 {
307 // free solver
309 if (status != 0) {
310 RCLCPP_ERROR(this->get_logger(), "%s_acados_free() returned status %d.", model_name_.c_str(), status);
311 }
312 // free solver capsule
314 if (status != 0) {
315 RCLCPP_ERROR(this->get_logger(), "%s_acados_free_capsule() returned status %d.", model_name_.c_str(), status);
316 }
317}
int acados_free(ocp_model_capsule_t capsule)
Wrapper around the generated acados solver cleanup function.
int acados_free_capsule(ocp_model_capsule_t capsule)
Wrapper around the generated acados capsule cleanup function.

◆ getBiLevelX0()

virtual std::vector< double > trajectory_optimization::TrajectoryOptimizationNode::getBiLevelX0 ( const perception_msgs::msg::EgoData & ego_data)
protectedpure virtual

Computes the initial optimizer state using bi-level stabilizaion based on the model-specific EgoData type.

This function uses bi-level stabilization for initializing the optimization problem. In general the initial state is interpolated from the latest trajectory (-> low-level stabilization). But if the difference between the interpolated state and the ego state is too large, the ego state is used instead (-> high-level stabilization). This combination of low- and high-level stabilization is called bi-level stabilization.

Parameters
[in]ego_dataCurrent ego state.
Returns
Initial state vector for the OCP.

Implemented in trajectory_optimization::TrajectoryOptimizationAckermannNode, and trajectory_optimization::TrajectoryOptimizationRWSNode.

◆ getHighLevelX0()

virtual std::vector< double > trajectory_optimization::TrajectoryOptimizationNode::getHighLevelX0 ( const perception_msgs::msg::EgoData & ego_data)
protectedpure virtual

Computes the initial optimizer state using high-level stabilization based on the model-specific EgoData type.

This function uses high-level stabilization for initializing the optimization problem. -> initial state = current state of the ego vehicle.

Parameters
[in]ego_dataCurrent ego state.
Returns
Initial state vector for the OCP.

Implemented in trajectory_optimization::TrajectoryOptimizationAckermannNode, and trajectory_optimization::TrajectoryOptimizationRWSNode.

◆ initializeTrajectory()

virtual void trajectory_optimization::TrajectoryOptimizationNode::initializeTrajectory ( trajectory_planning_msgs::msg::Trajectory & trajectory)
protectedpure virtual

Initializes an output trajectory message with the model-specific message type and size.

Parameters
[out]trajectoryTrajectory message to initialize.

Implemented in trajectory_optimization::TrajectoryOptimizationAckermannNode, and trajectory_optimization::TrajectoryOptimizationRWSNode.

◆ keepNClosestObjects()

void trajectory_optimization::TrajectoryOptimizationNode::keepNClosestObjects ( perception_msgs::msg::ObjectList & object_list,
const int n_objects )
staticprotected

Keeps the nearest forward objects and discards the remaining entries.

Parameters
[in,out]object_listObject list to filter.
[in]n_objectsMaximum number of objects to retain.

Definition at line 86 of file utils.cpp.

86 {
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}

◆ linearInterpolation()

bool trajectory_optimization::TrajectoryOptimizationNode::linearInterpolation ( const std::vector< double > & X,
const std::vector< double > & Y,
const double & desired_x,
double & output_y,
const bool wrap_angle = false )
protected

Interpolates a value from sampled data and optionally handles angle wrap-around.

Parameters
[in]XSample positions.
[in]YSample values.
[in]desired_xQuery position.
[out]output_yInterpolated result.
[in]wrap_angleWhether angular differences should be wrapped to [-pi, pi].
Returns
true if the query lies within the sample range, otherwise false.

Definition at line 21 of file utils.cpp.

22 {
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}
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

◆ logPerformance()

void trajectory_optimization::TrajectoryOptimizationNode::logPerformance ( const PerformanceMetrics & metrics)
protected

Emits one machine-readable performance record when performance logging is enabled.

Parameters
[in]metricsPerformance metrics to write to the log.

Definition at line 475 of file utils.cpp.

475 {
477 performance_logger_->write(metrics);
478 }
479}

◆ normalBoundaryDistance()

std::vector< std::pair< double, double > > trajectory_optimization::TrajectoryOptimizationNode::normalBoundaryDistance ( const trajectory_planning_msgs::msg::Trajectory & reference_trajectory,
const route_planning_msgs::msg::Route & route )
protected

Computes minimum normal distances from the reference path to the active route boundaries.

Parameters
[in]reference_trajectoryReference trajectory in optimizer frame.
[in]routeRoute data used to derive active boundaries.
Returns
Left and right boundary distances for each reference sample.

Definition at line 154 of file utils.cpp.

155 {
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}
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

◆ objectListCallback()

void trajectory_optimization::TrajectoryOptimizationNode::objectListCallback ( const perception_msgs::msg::ObjectList::ConstSharedPtr msg)
protected

Stores the current object list when object handling is enabled.

Parameters
[in]msgIncoming object list message.

Definition at line 12 of file callbacks.cpp.

12 {
14 RCLCPP_DEBUG(this->get_logger(), "Received object list");
15 object_list_ = *msg;
16 } else {
17 // reset object list to empty
18 object_list_ = perception_msgs::msg::ObjectList();
19 }
20}

◆ operator=() [1/2]

TrajectoryOptimizationNode & trajectory_optimization::TrajectoryOptimizationNode::operator= ( const TrajectoryOptimizationNode & )
delete

Copy assignment is disabled because the node owns non-copyable runtime resources.

Returns
Reference to this node. The operator is deleted.

◆ operator=() [2/2]

TrajectoryOptimizationNode & trajectory_optimization::TrajectoryOptimizationNode::operator= ( TrajectoryOptimizationNode && )
delete

Move assignment is disabled to keep solver and ROS handles bound to a single instance.

Returns
Reference to this node. The operator is deleted.

◆ parametersCallback()

rcl_interfaces::msg::SetParametersResult trajectory_optimization::TrajectoryOptimizationNode::parametersCallback ( const std::vector< rclcpp::Parameter > & parameters)
protected

Applies parameter updates that can be reconfigured while the node is running.

Parameters
[in]parametersParameters requested by the ROS parameter service.
Returns
Result of the parameter update request.

Definition at line 170 of file trajectory_optimization_node.cpp.

171 {
172 for (const auto& param : parameters) {
173 for (auto& auto_reconfigurable_param : auto_reconfigurable_params_) {
174 if (param.get_name() == std::get<0>(auto_reconfigurable_param)) {
175 std::get<1>(auto_reconfigurable_param)(param);
176 RCLCPP_INFO(this->get_logger(), "Reconfigured parameter '%s' to: %s", param.get_name().c_str(),
177 param.value_to_string().c_str());
178 break;
179 }
180 }
181 // handle special cases
182 if (param.get_name() == "run_as_callback") {
184 planning_timer_ = this->create_wall_timer(std::chrono::duration<double>(1 / optimization_freq_),
186 RCLCPP_WARN(this->get_logger(), "OCP runs now periodically with frequency %f Hz", optimization_freq_);
187 } else if (run_as_callback_ && planning_timer_) {
188 planning_timer_->cancel();
189 planning_timer_.reset();
190 RCLCPP_WARN(this->get_logger(), "OCP runs now on reference trajectory callback");
191 }
192 }
193 }
194 // mark parameter change successful
195 rcl_interfaces::msg::SetParametersResult result;
196 result.successful = true;
197
198 return result;
199}
void planningCycle()
Runs one full planning cycle from input preparation, over solver execution, to trajectory publication...

◆ planningCycle()

void trajectory_optimization::TrajectoryOptimizationNode::planningCycle ( )
protected

Runs one full planning cycle from input preparation, over solver execution, to trajectory publication.

Definition at line 388 of file trajectory_optimization_node.cpp.

388 {
389 const auto cycle_start = SteadyClock::now();
390 if (debug_viz_) viz_circles_.clear();
391 if (rclcpp::Time(this->now()) - rclcpp::Time(ego_data_.header.stamp) > rclcpp::Duration::from_seconds(ego_data_timeout_)) {
392 RCLCPP_WARN(this->get_logger(), "EgoData outdated. Skipping planning cycle.");
393 return;
394 }
395 // init trajectory message and set header
396 trajectory_planning_msgs::msg::Trajectory::UniquePtr trajectory = std::make_unique<trajectory_planning_msgs::msg::Trajectory>();
397 initializeTrajectory(*trajectory);
398
399 trajectory->header.frame_id = vehicle_frame_id_;
400 trajectory->header.stamp = ego_data_.header.stamp; // use latest ego_data stamp as trajectory stamp
401
402 // init time-steps of trajectory to ensure increasing time-steps even for standstill trajectories
403 double dt = optimization_horizon_ / n_shots_;
404 for (int i = 0; i <= n_shots_; ++i) trajectory_planning_msgs::trajectory_access::setT(*trajectory, i * dt, i);
405
406 // check if the reference trajectory is standstill
407 if (trajectory_planning_msgs::trajectory_access::getStandstill(reference_trajectory_)) {
408 RCLCPP_WARN(this->get_logger(), "Standstill trajectory. Skipping planning cycle. Publish standstill trajectory.");
409 // transform trajectory to output frame
410 if (!trajectory2outputFrame(*trajectory)) {
411 return;
412 }
413 trajectory_planning_msgs::trajectory_access::setStandstill(*trajectory, true);
414 trajectory_pub_->publish(std::move(trajectory));
415 // Invalidate the warm start and reset the solver once when entering standstill.
416 if (!control_guess_.empty()) {
417 control_guess_.clear();
418 resetSolver();
419 }
420 return;
421 }
422
423 PerformanceMetrics metrics;
424 metrics.cycle = ++logging_cycle_;
425 metrics.ego_stamp_ns = rclcpp::Time(ego_data_.header.stamp).nanoseconds();
426 metrics.reference_stamp_ns = rclcpp::Time(reference_trajectory_.header.stamp).nanoseconds();
427 metrics.route_stamp_ns = rclcpp::Time(route_.header.stamp).nanoseconds();
428 metrics.reference_points = trajectory_planning_msgs::trajectory_access::getSamplePointSize(reference_trajectory_);
429 metrics.objects = static_cast<int>(object_list_.objects.size());
430 auto logCompletedCycle = [&]() {
431 metrics.cycle_ms = elapsedMilliseconds(cycle_start);
432 metrics.postprocessing_ms = metrics.cycle_ms - metrics.preprocessing_ms - metrics.solve_wall_ms;
433 logPerformance(metrics);
434 };
435
436 // set initial state
437 std::vector<double> x_init(*nlp_dims_->nx, 0.0);
438 if (!trajectory_planning_msgs::trajectory_access::getStandstill(latest_valid_trajectory_)) {
440 } else {
441 RCLCPP_WARN(this->get_logger(),
442 "Latest available trajectory is standstill. Using ego data for initial state (high-level initialization).");
443 x_init = getHighLevelX0(ego_data_);
444 }
445
446 // debug print of initial state
447 std::stringstream ss;
448 ss << "Initial state: ";
449 for (size_t i = 0; i < x_init.size(); ++i) ss << "x[" << i << "]: " << x_init[i] << (i != x_init.size() - 1 ? ", " : "");
450 RCLCPP_DEBUG(this->get_logger(), "%s", ss.str().c_str());
451
452 ocp_nlp_constraints_model_set(nlp_config_, nlp_dims_, nlp_in_, nlp_out_, 0, "lbx", x_init.data());
453 ocp_nlp_constraints_model_set(nlp_config_, nlp_dims_, nlp_in_, nlp_out_, 0, "ubx", x_init.data());
454
455 // update inputs to the ocp; skip planning cycle if update fails
457 RCLCPP_WARN(this->get_logger(), "Failed to update inputs. Skipping planning cycle.");
458 return;
459 }
460
461 if (!setInitialGuess(x_init, rclcpp::Time(ego_data_.header.stamp))) {
462 control_guess_.clear();
463 return;
464 }
465
466 // solve the optimization problem
467 const auto solve_start = SteadyClock::now();
468 metrics.preprocessing_ms = elapsedMilliseconds(cycle_start, solve_start);
470 const auto solve_end = SteadyClock::now();
471 metrics.solve_wall_ms = elapsedMilliseconds(solve_start, solve_end);
472
474
475 if (metrics.status == ACADOS_NAN_DETECTED || metrics.status == ACADOS_MINSTEP || metrics.status == ACADOS_QP_FAILURE) {
476 printSolution(metrics);
477 // Keep the last accepted controls; the failed solver output is not added to the cache.
478 resetSolver();
479 logCompletedCycle();
480 return;
481 }
482
483 // get solution
484 for (int ii = 0; ii <= nlp_dims_->N; ++ii) {
485 ocp_nlp_out_get(nlp_config_, nlp_dims_, nlp_out_, ii, "x", &xtraj_[ii * *nlp_dims_->nx]);
486 }
487 for (int ii = 0; ii < nlp_dims_->N; ++ii) {
488 ocp_nlp_out_get(nlp_config_, nlp_dims_, nlp_out_, ii, "u", &utraj_[ii * *nlp_dims_->nu]);
489 }
491 control_guess_stamp_ = rclcpp::Time(ego_data_.header.stamp);
492
493 printSolution(metrics);
494 if (debug_viz_) {
497 }
498
499 // convert output into trajectory message
500 convertToTrajectoryMsg(*trajectory);
501
502 bool standstill = true;
503 for (int i = 0; i < trajectory_planning_msgs::trajectory_access::getSamplePointSize(*trajectory); ++i) {
504 if (trajectory_planning_msgs::trajectory_access::getV(*trajectory, i) > standstill_threshold_) standstill = false;
505 }
506 trajectory_planning_msgs::trajectory_access::setStandstill(*trajectory, standstill);
507
508 // transform trajectory to output frame
509 if (!trajectory2outputFrame(*trajectory)) {
510 logCompletedCycle();
511 return;
512 }
513
514 latest_valid_trajectory_ = *trajectory;
515 trajectory_pub_->publish(std::move(trajectory));
516 metrics.published = true;
517 logCompletedCycle();
518 const char* cycle_time_color = metrics.cycle_ms <= 100.0 ? "\x1b[32m" : "\x1b[31m";
519 RCLCPP_INFO(this->get_logger(), "Published trajectory (cycle: %s%.2f ms\x1b[0m)", cycle_time_color, metrics.cycle_ms);
520}
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)
Reads timing, iteration, cost, and residual statistics from acados.
void printSolution(const PerformanceMetrics &metrics)
Logs solver status and optional debug statistics for the last optimization run.
Definition utils.cpp:441
trajectory_planning_msgs::msg::Trajectory reference_trajectory_
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:338
virtual void initializeTrajectory(trajectory_planning_msgs::msg::Trajectory &trajectory)=0
Initializes an output trajectory message with the model-specific message type and size.
void logPerformance(const PerformanceMetrics &metrics)
Emits one machine-readable performance record when performance logging is enabled.
Definition utils.cpp:475
virtual void convertToTrajectoryMsg(trajectory_planning_msgs::msg::Trajectory &trajectory)=0
Maps the optimized state trajectory into the model-specific output message fields.
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, const std::vector< double > &x_init)
Transforms external inputs into the optimizer frame and writes them into the OCP.
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
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::Publisher< trajectory_planning_msgs::msg::Trajectory >::SharedPtr trajectory_pub_
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...
int acados_solve(ocp_model_capsule_t capsule)
Wrapper around the generated acados solve function.

◆ printSolution()

void trajectory_optimization::TrajectoryOptimizationNode::printSolution ( const PerformanceMetrics & metrics)
protected

Logs solver status and optional debug statistics for the last optimization run.

Parameters
[in]metricsPerformance metrics collected for the optimization run.

Definition at line 441 of file utils.cpp.

441 {
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}
void acados_print_stats(ocp_model_capsule_t capsule)
Wrapper around the generated acados statistics printer.

◆ referenceTrajectoryCallback()

void trajectory_optimization::TrajectoryOptimizationNode::referenceTrajectoryCallback ( const trajectory_planning_msgs::msg::Trajectory::ConstSharedPtr msg)
protected

Updates the reference trajectory and optionally triggers optimization immediately.

Parameters
[in]msgIncoming reference trajectory message.

Definition at line 22 of file callbacks.cpp.

23 {
24 RCLCPP_DEBUG(this->get_logger(), "Received reference trajectory");
26 if (run_as_callback_) {
28 }
29}

◆ resetSolver()

void trajectory_optimization::TrajectoryOptimizationNode::resetSolver ( )
protected

Resets the optimizer memory while retaining the generated solver instance.

Definition at line 319 of file trajectory_optimization_node.cpp.

319 {
320 const int status = trajectory_optimization::acados_reset(ocp_capsule_, 1, 0, 0, 0);
321 if (status != ACADOS_SUCCESS) {
322 RCLCPP_ERROR(this->get_logger(), "%s_acados_reset() returned status %d. Recreating solver.", model_name_.c_str(), status);
323 freeSolver();
324 setupSolver();
325 }
326}
void setupSolver()
Creates the acados solver instance and initializes its state buffers.
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.

◆ routeCallback()

void trajectory_optimization::TrajectoryOptimizationNode::routeCallback ( const route_planning_msgs::msg::Route::ConstSharedPtr msg)
protected

Stores the current route used for boundary constraints when enabled.

Parameters
[in]msgIncoming route message.

Definition at line 31 of file callbacks.cpp.

31 {
33 RCLCPP_DEBUG(this->get_logger(), "Received route");
34 route_ = *msg;
35 } else {
36 // reset route to empty
37 route_ = route_planning_msgs::msg::Route();
38 }
39}

◆ setInitialGuess()

bool trajectory_optimization::TrajectoryOptimizationNode::setInitialGuess ( const std::vector< double > & x_init,
const rclcpp::Time & stamp )
protected

Builds and sets a dynamically consistent NLP initial guess from the current state and cached controls.

Parameters
[in]x_initHard initial state of the OCP.
[in]stampAbsolute time corresponding to x_init.
Returns
true if the state rollout succeeded.

Definition at line 328 of file trajectory_optimization_node.cpp.

328 {
329 constexpr double MAX_CONTROL_GUESS_AGE_FACTOR = 0.5;
330 const int nx = *nlp_dims_->nx;
331 const int nu = *nlp_dims_->nu;
332 const double time_step = optimization_horizon_ / n_shots_;
333 const size_t expected_control_size = static_cast<size_t>(nu * n_shots_);
334 if (x_init.size() != static_cast<size_t>(nx)) {
335 RCLCPP_ERROR(get_logger(), "Initial state has size %zu, expected %d.", x_init.size(), nx);
336 return false;
337 }
338
339 // Fall back to zero controls if no sufficiently recent accepted solution is available.
340 std::vector<double> controls(expected_control_size, 0.0);
341 if (control_guess_.size() == expected_control_size) {
342 const double elapsed = (stamp - control_guess_stamp_).seconds();
343 if (elapsed >= 0.0 && elapsed < MAX_CONTROL_GUESS_AGE_FACTOR * optimization_horizon_) {
344 // Shift the previous controls to the current planning time and interpolate between shooting nodes.
345 for (int stage = 0; stage < n_shots_; ++stage) {
346 const double previous_stage = (elapsed + stage * time_step) / time_step;
347 if (previous_stage >= n_shots_) break;
348
349 const int lower_stage = static_cast<int>(std::floor(previous_stage));
350 const double interpolation_factor = previous_stage - lower_stage;
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 controls[stage * nu + control] = lower_value + interpolation_factor * (upper_value - lower_value);
355 }
356 }
357 }
358 }
359
360 ocp_nlp_out_set_values_to_zero(nlp_config_, nlp_dims_, nlp_out_);
361 std::vector<double> rollout_state = x_init;
362 std::vector<double> intermediate_state(nx);
363 std::vector<double> k1(nx), k2(nx), k3(nx), k4(nx);
364 const double integration_step = time_step / 2.0;
365 for (int stage = 0; stage < n_shots_; ++stage) {
366 double* control = &controls[stage * nu];
367 ocp_nlp_out_set(nlp_config_, nlp_dims_, nlp_out_, nlp_in_, stage, "x", rollout_state.data());
368 ocp_nlp_out_set(nlp_config_, nlp_dims_, nlp_out_, nlp_in_, stage, "u", control);
369
370 // Match sim_method_num_stages=4 and sim_method_num_steps=2 from the generated OCP.
371 for (int integration = 0; integration < 2; ++integration) {
372 trajectory_optimization::acados_evaluate_dynamics(ocp_capsule_, stage, rollout_state.data(), control, k1.data());
373 for (int i = 0; i < nx; ++i) intermediate_state[i] = rollout_state[i] + 0.5 * integration_step * k1[i];
374 trajectory_optimization::acados_evaluate_dynamics(ocp_capsule_, stage, intermediate_state.data(), control, k2.data());
375 for (int i = 0; i < nx; ++i) intermediate_state[i] = rollout_state[i] + 0.5 * integration_step * k2[i];
376 trajectory_optimization::acados_evaluate_dynamics(ocp_capsule_, stage, intermediate_state.data(), control, k3.data());
377 for (int i = 0; i < nx; ++i) intermediate_state[i] = rollout_state[i] + integration_step * k3[i];
378 trajectory_optimization::acados_evaluate_dynamics(ocp_capsule_, stage, intermediate_state.data(), control, k4.data());
379 for (int i = 0; i < nx; ++i) {
380 rollout_state[i] += integration_step / 6.0 * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]);
381 }
382 }
383 }
384 ocp_nlp_out_set(nlp_config_, nlp_dims_, nlp_out_, nlp_in_, n_shots_, "x", rollout_state.data());
385 return true;
386}
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.

◆ setOcpGlobalParameters()

void trajectory_optimization::TrajectoryOptimizationNode::setOcpGlobalParameters ( const std::vector< double > & cost_weights,
const trajectory_planning_msgs::msg::Trajectory & reference_trajectory,
const route_planning_msgs::msg::Route & route )
protected

Writes stage-independent data into the OCP.

Parameters
[in]cost_weightsConfigured cost weights.
[in]reference_trajectoryReference trajectory in optimizer frame.
[in]routeRoute data used for boundary distances.

Definition at line 578 of file trajectory_optimization_node.cpp.

580 {
581 const auto start_time = std::chrono::steady_clock::now();
582 std::vector<double> global_params;
583
584 // cost weights
585 const auto expected_cost_weights_size = static_cast<size_t>(p_cost_weights_shape_[0] * p_cost_weights_shape_[1]);
586 if (cost_weights.size() != expected_cost_weights_size) {
587 RCLCPP_ERROR(this->get_logger(), "Size of cost_weights (%zu) does not match expected size (%zu).", cost_weights.size(),
588 expected_cost_weights_size);
589 throw std::runtime_error("Size of cost_weights does not match expected size.");
590 }
591 global_params.insert(global_params.end(), cost_weights.begin(), cost_weights.end());
592
593 // other cost params
594 global_params.push_back(thw_);
595 global_params.push_back(d_min_obstacle_long_);
596 global_params.push_back(d_min_obstacle_lat_);
597 global_params.push_back(d_min_boundary_lat_);
598
599 // reference path (including boundaries)
600 const size_t n_ref_states = static_cast<size_t>(p_ref_path_shape_[0] * p_ref_path_shape_[1]);
601 std::vector<std::pair<double, double>> boundary_distances = normalBoundaryDistance(reference_trajectory, route);
602 // fill ref vector for ocp -> psi, x, y, v, d_bound_left, d_bound_right
603 std::vector<double> ref;
604 for (int i = 0; i < trajectory_planning_msgs::trajectory_access::getSamplePointSize(reference_trajectory); ++i) {
605 ref.push_back(trajectory_planning_msgs::trajectory_access::getTheta(reference_trajectory, i));
606 ref.push_back(trajectory_planning_msgs::trajectory_access::getX(reference_trajectory, i));
607 ref.push_back(trajectory_planning_msgs::trajectory_access::getY(reference_trajectory, i));
608 ref.push_back(trajectory_planning_msgs::trajectory_access::getV(reference_trajectory, i));
609 ref.push_back(boundary_distances[i].first); // left boundary distance
610 ref.push_back(boundary_distances[i].second); // right boundary distance
611 }
612 if (ref.size() >= n_ref_states) {
613 global_params.insert(global_params.end(), ref.begin(),
614 ref.begin() + static_cast<std::vector<double>::difference_type>(n_ref_states));
615 } else {
616 // Repeat the final valid state. Infinity padding can propagate NaNs through closest-point calculations.
617 global_params.insert(global_params.end(), ref.begin(), ref.end());
618 const size_t state_width = static_cast<size_t>(p_ref_path_shape_[1]);
619 while (global_params.size() < expected_cost_weights_size + 4 + n_ref_states) {
620 global_params.insert(global_params.end(), ref.end() - static_cast<std::vector<double>::difference_type>(state_width),
621 ref.end());
622 }
623 }
624
625 if (global_params.size() != static_cast<size_t>(nlp_dims_->np_global)) {
626 RCLCPP_ERROR(this->get_logger(), "Size of global parameters (%zu) does not match expected size (%d).", global_params.size(),
627 nlp_dims_->np_global);
628 throw std::runtime_error("Size of global parameters does not match expected size.");
629 }
631 ocp_capsule_, global_params.data(), static_cast<int>(global_params.size()));
632 if (status != ACADOS_SUCCESS) {
633 throw std::runtime_error("acados global parameter update failed with status " + std::to_string(status));
634 }
635 const auto elapsed_ms = std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - start_time).count();
636 RCLCPP_DEBUG(this->get_logger(), "setOcpGlobalParameters duration: %.3f ms", elapsed_ms);
637}
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
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.

◆ setOcpParameters()

void trajectory_optimization::TrajectoryOptimizationNode::setOcpParameters ( const perception_msgs::msg::EgoData & ego_data,
const perception_msgs::msg::ObjectList & object_list )
protected

Writes stage-wise obstacle and dynamic weighting parameters into the OCP.

Parameters
[in]ego_dataCurrent ego state.
[in]object_listObject list in optimizer frame.

Definition at line 639 of file trajectory_optimization_node.cpp.

640 {
641 const auto start_time = std::chrono::steady_clock::now();
642 // loop over shooting intervals
643 double floating_dynamic_weight = 1.0;
644 double dt = optimization_horizon_ / n_shots_;
645 for (int i = 0; i <= n_shots_; ++i) {
646 // dynamic weight
647 int idx = 0;
648 int n = 1;
649 std::vector<int> idx_dynamic_weight(n);
650 // fill vector with values from idx to idx + n
651 std::iota(idx_dynamic_weight.begin(), idx_dynamic_weight.end(), idx);
652 int status = trajectory_optimization::acados_update_params_sparse(ocp_capsule_, i, idx_dynamic_weight.data(),
653 &floating_dynamic_weight, n);
654 if (status != ACADOS_SUCCESS) {
655 throw std::runtime_error("acados dynamic-weight update failed with status " + std::to_string(status));
656 }
657 floating_dynamic_weight *= dynamic_weight_;
658
659 // obstacles
660 idx += n;
661 n = static_cast<int>(p_obstacle_circles_shape_[0] * p_obstacle_circles_shape_[1]);
662 std::vector<double> circles; // [x1, y1, r1, x2, y2, r2, ...]
663
664 for (size_t j = 0; j < object_list.objects.size(); ++j) {
665 std::vector<double> TIME, X, Y, YAW;
666 std::vector<std::tuple<double, double, double>> target_states;
667 // TODO(ika): Build prediction arrays once per object outside the shooting-interval loop.
668 TIME.push_back(static_cast<double>(rclcpp::Time(object_list.header.stamp).nanoseconds()) / 1e9);
669 X.push_back(perception_msgs::object_access::getX(object_list.objects[j]));
670 Y.push_back(perception_msgs::object_access::getY(object_list.objects[j]));
671 YAW.push_back(perception_msgs::object_access::getYaw(object_list.objects[j]));
672 if (consider_objects_ == CONSIDER_OBJECTS::PREDICTED_OBJECTS && !object_list.objects[j].state_predictions.empty()) {
673 // build one target state for a single prediction hypothesis at the current shooting interval
674 auto appendPredictionTargetState = [&](const auto& state_prediction, size_t prediction_idx) {
675 std::vector<double> prediction_time = TIME;
676 std::vector<double> prediction_x = X;
677 std::vector<double> prediction_y = Y;
678 std::vector<double> prediction_yaw = YAW;
679 for (const auto& predicted_state : state_prediction.states) {
680 prediction_time.push_back(static_cast<double>(rclcpp::Time(predicted_state.header.stamp).nanoseconds()) / 1e9);
681 prediction_x.push_back(perception_msgs::object_access::getX(predicted_state));
682 prediction_y.push_back(perception_msgs::object_access::getY(predicted_state));
683 prediction_yaw.push_back(perception_msgs::object_access::getYaw(predicted_state));
684 }
685 double x_tgt = 0.0, y_tgt = 0.0, yaw_tgt = 0.0;
686 double des_time = static_cast<double>(rclcpp::Time(ego_data.header.stamp).nanoseconds()) / 1e9 + dt * i;
687 if (des_time > prediction_time.back()) {
688 const double relative_des_time = des_time - prediction_time.front();
689 const double relative_max_time = prediction_time.back() - prediction_time.front();
690 RCLCPP_WARN(this->get_logger(),
691 "Prediction horizon shorter than requested interpolation time. "
692 "object=%zu prediction=%zu probability=%.3f desired_rel=%.3f s max_rel=%.3f s n_states=%zu. "
693 "Using last prediction state.",
694 j, prediction_idx, state_prediction.probability, relative_des_time, relative_max_time,
695 state_prediction.states.size());
696 x_tgt = prediction_x.back();
697 y_tgt = prediction_y.back();
698 yaw_tgt = prediction_yaw.back();
699 } else {
700 linearInterpolation(prediction_time, prediction_x, des_time, x_tgt);
701 linearInterpolation(prediction_time, prediction_y, des_time, y_tgt);
702 linearInterpolation(prediction_time, prediction_yaw, des_time, yaw_tgt, true);
703 }
704 target_states.emplace_back(x_tgt, y_tgt, yaw_tgt);
705 };
706
707 // consider all prediction hypotheses whose probability exceeds the configured threshold
708 bool has_prediction_above_threshold = false;
709 for (size_t prediction_idx = 0; prediction_idx < object_list.objects[j].state_predictions.size(); ++prediction_idx) {
710 const auto& state_prediction = object_list.objects[j].state_predictions[prediction_idx];
711 if (state_prediction.probability <= min_prediction_probability_) {
712 continue;
713 }
714 has_prediction_above_threshold = true;
715 appendPredictionTargetState(state_prediction, prediction_idx);
716 }
717 if (!has_prediction_above_threshold) {
718 // always keep at least one prediction hypothesis for predicted objects
719 appendPredictionTargetState(object_list.objects[j].state_predictions[0], 0);
720 }
721 } else {
722 // static object handling or missing predictions: use the current object state
723 target_states.emplace_back(X.front(), Y.front(), YAW.front());
724 }
725
726 double alpha = std::atan2(object_list.objects[j].state.reference_point.translation_to_geometric_center.y,
727 object_list.objects[j].state.reference_point.translation_to_geometric_center.x);
728 double a = std::sqrt(std::pow(object_list.objects[j].state.reference_point.translation_to_geometric_center.x, 2) +
729 std::pow(object_list.objects[j].state.reference_point.translation_to_geometric_center.y, 2));
730 for (auto& [x_tgt, y_tgt, yaw_tgt] : target_states) {
731 // ensure that x_tgt and y_tgt represent the geometric center of the object
732 double beta = wrap_angle_rad(yaw_tgt - alpha);
733 x_tgt += a * std::cos(beta);
734 y_tgt += a * std::sin(beta);
735
736 std::vector<double> obj_circles =
737 discretizeBB2Circles(x_tgt, y_tgt, yaw_tgt, perception_msgs::object_access::getLength(object_list.objects[j]),
738 perception_msgs::object_access::getWidth(object_list.objects[j]));
739
740 circles.insert(circles.end(), obj_circles.begin(), obj_circles.end());
741 if (circles.size() >= static_cast<size_t>(n)) {
742 circles.resize(n);
743 break;
744 }
745 }
746 if (circles.size() >= static_cast<size_t>(n)) {
747 break;
748 }
749 }
750 // fill up with dummy "ghost" obstacle circles at (10000, 10000) to avoid NaNs in the optimization problem
751 // TODO: improve this // NOLINT(google-readability-todo)
752 while (circles.size() < static_cast<size_t>(n)) {
753 std::vector<double> dummy_circle = {10000.0, 10000.0, 1.0};
754 circles.insert(circles.end(), dummy_circle.begin(), dummy_circle.end());
755 }
756 if ((circles.size() % p_obstacle_circles_shape_[1]) != 0) {
757 RCLCPP_WARN(this->get_logger(), "Circles vector size is not a multiple of the circle shape. Resizing.");
758 circles.resize(n);
759 }
760 if (debug_viz_) viz_circles_.insert(viz_circles_.end(), circles.begin(), circles.end());
761
762 std::vector<int> idx_obstacles(n);
763 // fill vector with values from idx to idx + n
764 std::iota(idx_obstacles.begin(), idx_obstacles.end(), idx);
765 status = trajectory_optimization::acados_update_params_sparse(ocp_capsule_, i, idx_obstacles.data(), circles.data(), n);
766 if (status != ACADOS_SUCCESS) {
767 throw std::runtime_error("acados obstacle update failed with status " + std::to_string(status));
768 }
769 }
770 const auto elapsed_ms = std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - start_time).count();
771 RCLCPP_DEBUG(this->get_logger(), "setOcpParameters duration: %.3f ms", elapsed_ms);
772}
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
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
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.

◆ setup()

void trajectory_optimization::TrajectoryOptimizationNode::setup ( )
protected

Creates ROS interfaces, initializes cached messages and prepares the solver.

Definition at line 201 of file trajectory_optimization_node.cpp.

201 {
202 tf2_buffer_ = std::make_unique<tf2_ros::Buffer>(this->get_clock());
203 tf2_listener_ = std::make_shared<tf2_ros::TransformListener>(*tf2_buffer_);
204
205 // create a callback for dynamic parameter configuration
206 parameters_callback_ = this->add_on_set_parameters_callback(
207 std::bind(&TrajectoryOptimizationNode::parametersCallback, this, std::placeholders::_1));
208
209 // set up subscriber for input topics
210 ego_data_sub_ = this->create_subscription<perception_msgs::msg::EgoData>(
211 "~/ego_data", 1, std::bind(&TrajectoryOptimizationNode::egoDataCallback, this, std::placeholders::_1));
212 RCLCPP_INFO(this->get_logger(), "Subscribed to '%s'", ego_data_sub_->get_topic_name());
213
214 object_list_sub_ = this->create_subscription<perception_msgs::msg::ObjectList>(
215 "~/object_list", 1, std::bind(&TrajectoryOptimizationNode::objectListCallback, this, std::placeholders::_1));
216 RCLCPP_INFO(this->get_logger(), "Subscribed to '%s'", object_list_sub_->get_topic_name());
217
218 route_sub_ = this->create_subscription<route_planning_msgs::msg::Route>(
219 "~/route", 1, std::bind(&TrajectoryOptimizationNode::routeCallback, this, std::placeholders::_1));
220 RCLCPP_INFO(this->get_logger(), "Subscribed to '%s'", route_sub_->get_topic_name());
221
222 reference_trajectory_sub_ = this->create_subscription<trajectory_planning_msgs::msg::Trajectory>(
223 "~/reference_trajectory", 1,
224 std::bind(&TrajectoryOptimizationNode::referenceTrajectoryCallback, this, std::placeholders::_1));
225 RCLCPP_INFO(this->get_logger(), "Subscribed to '%s'", reference_trajectory_sub_->get_topic_name());
226
227 // set up publisher for output topics
228 trajectory_pub_ = this->create_publisher<trajectory_planning_msgs::msg::Trajectory>("~/trajectory", 1);
229 RCLCPP_INFO(this->get_logger(), "Publishing to '%s'", trajectory_pub_->get_topic_name());
230 circles_pub_ = this->create_publisher<visualization_msgs::msg::MarkerArray>("~/visualization/object_circles", 1);
231 RCLCPP_INFO(this->get_logger(), "Publishing to '%s'", circles_pub_->get_topic_name());
232 ego_circles_pub_ = this->create_publisher<visualization_msgs::msg::MarkerArray>("~/visualization/ego_circles", 1);
233 RCLCPP_INFO(this->get_logger(), "Publishing to '%s'", ego_circles_pub_->get_topic_name());
234 boundary_pub_ = this->create_publisher<visualization_msgs::msg::MarkerArray>("~/visualization/boundaries", 1);
235 RCLCPP_INFO(this->get_logger(), "Publishing to '%s'", boundary_pub_->get_topic_name());
236
237 // create timer for planning cycle
238 if (run_as_callback_) {
239 RCLCPP_INFO(this->get_logger(), "OCP runs on reference trajectory callback");
240 } else {
241 RCLCPP_INFO(this->get_logger(), "OCP runs continuously with frequency %f Hz", optimization_freq_);
242 planning_timer_ = this->create_wall_timer(std::chrono::duration<double>(1 / optimization_freq_),
244 }
245
246 // init reference trajectory
247 trajectory_planning_msgs::trajectory_access::initializeTrajectory(
248 reference_trajectory_, trajectory_planning_msgs::msg::REFERENCE::TYPE_ID, n_shots_ + 1);
250
251 // init latest trajectory (doesn't matter which type, only used for standstill detection)
252 trajectory_planning_msgs::trajectory_access::initializeTrajectory(
253 latest_valid_trajectory_, trajectory_planning_msgs::msg::DRIVABLE::TYPE_ID, n_shots_ + 1);
255
256 setupSolver();
257
258 // Annotate message links for tracing: Publish trajectory periodically, which depends an all subscribed topics.
259 std::vector<const void*> link_subs;
260 link_subs.push_back(static_cast<const void*>(ego_data_sub_->get_subscription_handle().get()));
261 link_subs.push_back(static_cast<const void*>(object_list_sub_->get_subscription_handle().get()));
262 link_subs.push_back(static_cast<const void*>(route_sub_->get_subscription_handle().get()));
263 link_subs.push_back(static_cast<const void*>(reference_trajectory_sub_->get_subscription_handle().get()));
264 std::vector<const void*> link_pubs;
265 link_pubs.push_back(static_cast<const void*>(trajectory_pub_->get_publisher_handle().get()));
266 RCLCPP_INFO(get_logger(), "Annotating message links for tracing with %zu subscriptions and %zu publications", link_subs.size(),
267 link_pubs.size());
268 TRACETOOLS_TRACEPOINT(message_link_periodic_async, link_subs.data(), link_subs.size(), link_pubs.data(), link_pubs.size());
269}
rclcpp::Publisher< visualization_msgs::msg::MarkerArray >::SharedPtr circles_pub_
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 objectListCallback(const perception_msgs::msg::ObjectList::ConstSharedPtr msg)
Stores the current object list when object handling is enabled.
Definition callbacks.cpp:12
void egoDataCallback(const perception_msgs::msg::EgoData::ConstSharedPtr msg)
Stores the latest ego state used by the optimizer.
Definition callbacks.cpp:7
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_
rclcpp::Subscription< perception_msgs::msg::EgoData >::SharedPtr ego_data_sub_
rclcpp::Publisher< visualization_msgs::msg::MarkerArray >::SharedPtr ego_circles_pub_
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
std::shared_ptr< tf2_ros::TransformListener > tf2_listener_
rclcpp::Subscription< trajectory_planning_msgs::msg::Trajectory >::SharedPtr reference_trajectory_sub_

◆ setupSolver()

void trajectory_optimization::TrajectoryOptimizationNode::setupSolver ( )
protected

Creates the acados solver instance and initializes its state buffers.

Definition at line 271 of file trajectory_optimization_node.cpp.

271 {
272 if (n_shots_ <= 0) {
273 RCLCPP_FATAL(this->get_logger(), "n_shots must be > 0, got %d", n_shots_);
274 exit(1);
275 }
276
277 // Create exactly once. This also supports a runtime horizon that differs from the generated default.
279 std::vector<double> new_time_steps(n_shots_, optimization_horizon_ / n_shots_);
280 RCLCPP_INFO(this->get_logger(), "Create OCP with: horizon = %f, n_shots = %d, dt = %f", optimization_horizon_, n_shots_,
281 new_time_steps.front());
283
284 if (status != ACADOS_SUCCESS) {
285 RCLCPP_FATAL(this->get_logger(), "%s_acados_create_with_discretization() returned status %d. Exiting.", model_name_.c_str(),
286 status);
287 exit(1);
288 }
289
296 if (nlp_dims_->N != n_shots_) {
297 RCLCPP_FATAL(this->get_logger(), "Created solver has N=%d, expected n_shots=%d. Exiting.", nlp_dims_->N, n_shots_);
298 exit(1);
299 }
300
301 xtraj_.resize(*nlp_dims_->nx * (n_shots_ + 1));
302 utraj_.resize(*nlp_dims_->nu * n_shots_);
303 control_guess_.clear();
304}
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.
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.
ocp_nlp_in * acados_get_nlp_in(ocp_model_capsule_t capsule)
Wrapper around the generated accessor for the OCP input structure.

◆ trajectory2outputFrame()

bool trajectory_optimization::TrajectoryOptimizationNode::trajectory2outputFrame ( trajectory_planning_msgs::msg::Trajectory & trajectory)
protected

Transforms the planned trajectory into the configured output frame (trajectory_frame_id_) if required.

Parameters
[in,out]trajectoryTrajectory to transform.
Returns
true if the trajectory is already in the target frame or the transformation succeeded.

Definition at line 71 of file utils.cpp.

71 {
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}

◆ updateOcpInputs()

bool trajectory_optimization::TrajectoryOptimizationNode::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,
const std::vector< double > & x_init )
protected

Transforms external inputs into the optimizer frame and writes them into the OCP.

Parameters
[in]ego_dataCurrent ego state.
[in]object_listCurrent object list.
[in]routeCurrent route data.
[in]reference_trajectoryCurrent reference trajectory.
[in]x_initInitial optimizer state.
Returns
true if all optimizer inputs were updated successfully.

Definition at line 522 of file trajectory_optimization_node.cpp.

526 {
527 // transform inputs to target base_link frame
528 trajectory_planning_msgs::msg::Trajectory tf_reference_trajectory;
529 perception_msgs::msg::ObjectList tf_object_list;
530 route_planning_msgs::msg::Route tf_route;
531 try {
532 // reference trajectory
533 tf_reference_trajectory =
534 tf2_buffer_->transform(reference_trajectory, vehicle_frame_id_, tf2_ros::fromMsg(ego_data.header.stamp),
535 fixed_over_time_frame_id_, tf2::durationFromSec(0.01));
536 if (add_x_init_to_ref_ && trajectory_planning_msgs::trajectory_access::getX(tf_reference_trajectory, 0) > 0.0) {
537 RCLCPP_INFO(this->get_logger(), "Adding x_init to beginning of reference trajectory");
538 std::vector<double> x_0_ref = {0.0, x_init[0], x_init[1], x_init[3]};
539 tf_reference_trajectory.states.insert(tf_reference_trajectory.states.begin(), x_0_ref.begin(), x_0_ref.end());
540 }
541 // object list
542 if (!object_list.objects.empty() && object_list.header.frame_id != vehicle_frame_id_) {
543 tf_object_list = tf2_buffer_->transform(object_list, vehicle_frame_id_, tf2_ros::fromMsg(ego_data.header.stamp),
544 fixed_over_time_frame_id_, tf2::durationFromSec(0.01));
545 } else {
546 tf_object_list = object_list;
547 }
548 keepNClosestObjects(tf_object_list, static_cast<int>(p_obstacle_circles_shape_[0]));
549 // route
550 if (!route.route_elements.empty() && route.header.frame_id != vehicle_frame_id_) {
551 tf_route = tf2_buffer_->transform(route, vehicle_frame_id_, tf2_ros::fromMsg(ego_data.header.stamp),
552 fixed_over_time_frame_id_, tf2::durationFromSec(0.1));
553 } else {
554 tf_route = route;
555 }
556 } catch (tf2::TransformException& ex) {
557 RCLCPP_WARN(this->get_logger(), "Transformation is not available. Ex: %s", ex.what());
558 return false;
559 }
560
561 if (trajectory_planning_msgs::trajectory_access::getSamplePointSize(tf_reference_trajectory) <= 0) {
562 RCLCPP_ERROR(this->get_logger(), "Reference trajectory contains no sample points.");
563 return false;
564 }
565
566 // update ocp parameters
567 try {
568 this->setOcpGlobalParameters(cost_weights_, tf_reference_trajectory, tf_route);
569 this->setOcpParameters(ego_data, tf_object_list);
570 } catch (const std::exception& e) {
571 RCLCPP_ERROR(this->get_logger(), "Exception while setting OCP parameters: %s", e.what());
572 return false;
573 }
574
575 return true;
576}
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
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.
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.

◆ vizBoundaryPoints()

void trajectory_optimization::TrajectoryOptimizationNode::vizBoundaryPoints ( const std::vector< Eigen::Vector2d > & left_boundary_points,
const std::vector< Eigen::Vector2d > & right_boundary_points )
protected

Publishes the boundary intersections corresponding to the distances passed to the OCP.

Parameters
[in]left_boundary_pointsPoints on the left side.
[in]right_boundary_pointsPoints on the right side.

Definition at line 301 of file utils.cpp.

302 {
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}

◆ vizCircles()

void trajectory_optimization::TrajectoryOptimizationNode::vizCircles ( const std::vector< double > & obstacles)
protected

Publishes visualization markers for the obstacle circles currently used by the optimizer.

Parameters
[in]obstaclesFlattened obstacle circle list.

Definition at line 338 of file utils.cpp.

338 {
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}

◆ vizEgoCircles()

void trajectory_optimization::TrajectoryOptimizationNode::vizEgoCircles ( const std::vector< double > & x_trajectory,
const std::string & model_name )
protected

Publishes the ego-vehicle circle approximation used by the selected OCP model.

Parameters
[in]x_trajectoryOptimizer state trajectory.
[in]model_nameName of the active OCP model.

Definition at line 361 of file utils.cpp.

361 {
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}

◆ wrap_angle_rad()

double trajectory_optimization::TrajectoryOptimizationNode::wrap_angle_rad ( double angle_rad,
double min_val = -M_PI,
double max_val = M_PI )
staticprotected

Wraps an angle into a configured interval.

Parameters
[in]angle_radAngle in radians.
[in]min_valLower bound of the target interval.
[in]max_valUpper bound of the target interval.
Returns
Wrapped angle in radians.

Definition at line 14 of file utils.cpp.

14 {
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}

Member Data Documentation

◆ add_x_init_to_ref_

bool trajectory_optimization::TrajectoryOptimizationNode::add_x_init_to_ref_ = false
protected

Definition at line 402 of file trajectory_optimization_node.hpp.

◆ auto_reconfigurable_params_

std::vector<std::tuple<std::string, std::function<void(const rclcpp::Parameter&)> > > trajectory_optimization::TrajectoryOptimizationNode::auto_reconfigurable_params_
protected

Definition at line 388 of file trajectory_optimization_node.hpp.

◆ bi_level_dA_

double trajectory_optimization::TrajectoryOptimizationNode::bi_level_dA_ = 2.0
protected

Definition at line 409 of file trajectory_optimization_node.hpp.

◆ bi_level_dV_

double trajectory_optimization::TrajectoryOptimizationNode::bi_level_dV_ = 5.0
protected

Definition at line 408 of file trajectory_optimization_node.hpp.

◆ bi_level_dY_

double trajectory_optimization::TrajectoryOptimizationNode::bi_level_dY_ = 0.1
protected

Definition at line 410 of file trajectory_optimization_node.hpp.

◆ bi_level_dYaw_

double trajectory_optimization::TrajectoryOptimizationNode::bi_level_dYaw_ = 5.0
protected

Definition at line 411 of file trajectory_optimization_node.hpp.

◆ boundary_pub_

rclcpp::Publisher<visualization_msgs::msg::MarkerArray>::SharedPtr trajectory_optimization::TrajectoryOptimizationNode::boundary_pub_
protected

Definition at line 374 of file trajectory_optimization_node.hpp.

◆ circles_pub_

rclcpp::Publisher<visualization_msgs::msg::MarkerArray>::SharedPtr trajectory_optimization::TrajectoryOptimizationNode::circles_pub_
protected

Definition at line 372 of file trajectory_optimization_node.hpp.

◆ consider_boundaries_

uint8_t trajectory_optimization::TrajectoryOptimizationNode::consider_boundaries_ = CONSIDER_BOUNDARIES::SUGGESTED_LANE
protected

Definition at line 404 of file trajectory_optimization_node.hpp.

◆ consider_objects_

uint8_t trajectory_optimization::TrajectoryOptimizationNode::consider_objects_ = CONSIDER_OBJECTS::PREDICTED_OBJECTS
protected

Definition at line 403 of file trajectory_optimization_node.hpp.

◆ control_guess_

std::vector<double> trajectory_optimization::TrajectoryOptimizationNode::control_guess_
protected

Definition at line 417 of file trajectory_optimization_node.hpp.

◆ control_guess_stamp_

rclcpp::Time trajectory_optimization::TrajectoryOptimizationNode::control_guess_stamp_ {0, 0, RCL_ROS_TIME}
protected

Definition at line 418 of file trajectory_optimization_node.hpp.

418{0, 0, RCL_ROS_TIME};

◆ cost_weights_

std::vector<double> trajectory_optimization::TrajectoryOptimizationNode::cost_weights_ = std::vector<double>(12, 1.0)
protected

Definition at line 424 of file trajectory_optimization_node.hpp.

◆ d_min_boundary_lat_

double trajectory_optimization::TrajectoryOptimizationNode::d_min_boundary_lat_ = 0.0
protected

Definition at line 429 of file trajectory_optimization_node.hpp.

◆ d_min_obstacle_lat_

double trajectory_optimization::TrajectoryOptimizationNode::d_min_obstacle_lat_ = 0.5
protected

Definition at line 428 of file trajectory_optimization_node.hpp.

◆ d_min_obstacle_long_

double trajectory_optimization::TrajectoryOptimizationNode::d_min_obstacle_long_ = 5.0
protected

Definition at line 427 of file trajectory_optimization_node.hpp.

◆ debug_viz_

bool trajectory_optimization::TrajectoryOptimizationNode::debug_viz_ = false
protected

Definition at line 399 of file trajectory_optimization_node.hpp.

◆ dynamic_weight_

double trajectory_optimization::TrajectoryOptimizationNode::dynamic_weight_ = 1.0
protected

Definition at line 425 of file trajectory_optimization_node.hpp.

◆ ego_circles_pub_

rclcpp::Publisher<visualization_msgs::msg::MarkerArray>::SharedPtr trajectory_optimization::TrajectoryOptimizationNode::ego_circles_pub_
protected

Definition at line 373 of file trajectory_optimization_node.hpp.

◆ ego_data_

perception_msgs::msg::EgoData trajectory_optimization::TrajectoryOptimizationNode::ego_data_
protected

Definition at line 382 of file trajectory_optimization_node.hpp.

◆ ego_data_sub_

rclcpp::Subscription<perception_msgs::msg::EgoData>::SharedPtr trajectory_optimization::TrajectoryOptimizationNode::ego_data_sub_
protected

Definition at line 366 of file trajectory_optimization_node.hpp.

◆ ego_data_timeout_

double trajectory_optimization::TrajectoryOptimizationNode::ego_data_timeout_ = 1.0
protected

Definition at line 393 of file trajectory_optimization_node.hpp.

◆ fixed_over_time_frame_id_

std::string trajectory_optimization::TrajectoryOptimizationNode::fixed_over_time_frame_id_ = "map"
protected

Definition at line 391 of file trajectory_optimization_node.hpp.

◆ high_level_stabilization_

bool trajectory_optimization::TrajectoryOptimizationNode::high_level_stabilization_ = false
protected

Definition at line 401 of file trajectory_optimization_node.hpp.

◆ latest_valid_trajectory_

trajectory_planning_msgs::msg::Trajectory trajectory_optimization::TrajectoryOptimizationNode::latest_valid_trajectory_
protected

Definition at line 414 of file trajectory_optimization_node.hpp.

◆ logging_cycle_

uint64_t trajectory_optimization::TrajectoryOptimizationNode::logging_cycle_ = 0
protected

Definition at line 449 of file trajectory_optimization_node.hpp.

◆ min_prediction_probability_

double trajectory_optimization::TrajectoryOptimizationNode::min_prediction_probability_ = 0.0
protected

Definition at line 430 of file trajectory_optimization_node.hpp.

◆ model_name_

std::string trajectory_optimization::TrajectoryOptimizationNode::model_name_ = "karl"
protected

Definition at line 392 of file trajectory_optimization_node.hpp.

◆ n_shots_

int trajectory_optimization::TrajectoryOptimizationNode::n_shots_ = 50
protected

Definition at line 395 of file trajectory_optimization_node.hpp.

◆ nlp_config_

ocp_nlp_config* trajectory_optimization::TrajectoryOptimizationNode::nlp_config_
protected

Definition at line 440 of file trajectory_optimization_node.hpp.

◆ nlp_dims_

ocp_nlp_dims* trajectory_optimization::TrajectoryOptimizationNode::nlp_dims_
protected

Definition at line 441 of file trajectory_optimization_node.hpp.

◆ nlp_in_

ocp_nlp_in* trajectory_optimization::TrajectoryOptimizationNode::nlp_in_
protected

Definition at line 442 of file trajectory_optimization_node.hpp.

◆ nlp_opts_

void* trajectory_optimization::TrajectoryOptimizationNode::nlp_opts_
protected

Definition at line 445 of file trajectory_optimization_node.hpp.

◆ nlp_out_

ocp_nlp_out* trajectory_optimization::TrajectoryOptimizationNode::nlp_out_
protected

Definition at line 443 of file trajectory_optimization_node.hpp.

◆ nlp_solver_

ocp_nlp_solver* trajectory_optimization::TrajectoryOptimizationNode::nlp_solver_
protected

Definition at line 444 of file trajectory_optimization_node.hpp.

◆ object_list_

perception_msgs::msg::ObjectList trajectory_optimization::TrajectoryOptimizationNode::object_list_
protected

Definition at line 383 of file trajectory_optimization_node.hpp.

◆ object_list_sub_

rclcpp::Subscription<perception_msgs::msg::ObjectList>::SharedPtr trajectory_optimization::TrajectoryOptimizationNode::object_list_sub_
protected

Definition at line 367 of file trajectory_optimization_node.hpp.

◆ ocp_capsule_

ocp_model_capsule_t trajectory_optimization::TrajectoryOptimizationNode::ocp_capsule_
protected

Definition at line 439 of file trajectory_optimization_node.hpp.

◆ optimization_freq_

double trajectory_optimization::TrajectoryOptimizationNode::optimization_freq_ = 10.0
protected

Definition at line 394 of file trajectory_optimization_node.hpp.

◆ optimization_horizon_

double trajectory_optimization::TrajectoryOptimizationNode::optimization_horizon_ = 1.0
protected

Definition at line 396 of file trajectory_optimization_node.hpp.

◆ p_cost_weights_shape_

std::vector<int64_t> trajectory_optimization::TrajectoryOptimizationNode::p_cost_weights_shape_ = {12, 1}
protected

Definition at line 434 of file trajectory_optimization_node.hpp.

434{12, 1}; // nWeights x weightDim

◆ p_obstacle_circles_shape_

std::vector<int64_t> trajectory_optimization::TrajectoryOptimizationNode::p_obstacle_circles_shape_ = {30, 3}
protected

Definition at line 436 of file trajectory_optimization_node.hpp.

436{30, 3}; // nObstacleCircles x [x, y, radius]

◆ p_ref_path_shape_

std::vector<int64_t> trajectory_optimization::TrajectoryOptimizationNode::p_ref_path_shape_ = {51, 6}
protected

Definition at line 435 of file trajectory_optimization_node.hpp.

435{51, 6}; // nStates x [psi, x, y, v, d_bound_left, d_bound_right]

◆ parameters_callback_

OnSetParametersCallbackHandle::SharedPtr trajectory_optimization::TrajectoryOptimizationNode::parameters_callback_
protected

Definition at line 364 of file trajectory_optimization_node.hpp.

◆ performance_logger_

std::unique_ptr<PerformanceLogger> trajectory_optimization::TrajectoryOptimizationNode::performance_logger_
protected

Definition at line 450 of file trajectory_optimization_node.hpp.

◆ performance_logging_

bool trajectory_optimization::TrajectoryOptimizationNode::performance_logging_ = false
protected

Definition at line 398 of file trajectory_optimization_node.hpp.

◆ planning_timer_

rclcpp::TimerBase::SharedPtr trajectory_optimization::TrajectoryOptimizationNode::planning_timer_
protected

Definition at line 376 of file trajectory_optimization_node.hpp.

◆ reference_trajectory_

trajectory_planning_msgs::msg::Trajectory trajectory_optimization::TrajectoryOptimizationNode::reference_trajectory_
protected

Definition at line 385 of file trajectory_optimization_node.hpp.

◆ reference_trajectory_sub_

rclcpp::Subscription<trajectory_planning_msgs::msg::Trajectory>::SharedPtr trajectory_optimization::TrajectoryOptimizationNode::reference_trajectory_sub_
protected

Definition at line 369 of file trajectory_optimization_node.hpp.

◆ route_

route_planning_msgs::msg::Route trajectory_optimization::TrajectoryOptimizationNode::route_
protected

Definition at line 384 of file trajectory_optimization_node.hpp.

◆ route_sub_

rclcpp::Subscription<route_planning_msgs::msg::Route>::SharedPtr trajectory_optimization::TrajectoryOptimizationNode::route_sub_
protected

Definition at line 368 of file trajectory_optimization_node.hpp.

◆ run_as_callback_

bool trajectory_optimization::TrajectoryOptimizationNode::run_as_callback_ = false
protected

Definition at line 405 of file trajectory_optimization_node.hpp.

◆ standstill_threshold_

double trajectory_optimization::TrajectoryOptimizationNode::standstill_threshold_ = 0.45
protected

Definition at line 400 of file trajectory_optimization_node.hpp.

◆ tf2_buffer_

std::unique_ptr<tf2_ros::Buffer> trajectory_optimization::TrajectoryOptimizationNode::tf2_buffer_
protected

Definition at line 378 of file trajectory_optimization_node.hpp.

◆ tf2_listener_

std::shared_ptr<tf2_ros::TransformListener> trajectory_optimization::TrajectoryOptimizationNode::tf2_listener_
protected

Definition at line 379 of file trajectory_optimization_node.hpp.

◆ thw_

double trajectory_optimization::TrajectoryOptimizationNode::thw_ = 2.0
protected

Definition at line 426 of file trajectory_optimization_node.hpp.

◆ trajectory_frame_id_

std::string trajectory_optimization::TrajectoryOptimizationNode::trajectory_frame_id_ = "base_link"
protected

Definition at line 390 of file trajectory_optimization_node.hpp.

◆ trajectory_pub_

rclcpp::Publisher<trajectory_planning_msgs::msg::Trajectory>::SharedPtr trajectory_optimization::TrajectoryOptimizationNode::trajectory_pub_
protected

Definition at line 371 of file trajectory_optimization_node.hpp.

◆ utraj_

std::vector<double> trajectory_optimization::TrajectoryOptimizationNode::utraj_
protected

Definition at line 448 of file trajectory_optimization_node.hpp.

◆ vehicle_frame_id_

std::string trajectory_optimization::TrajectoryOptimizationNode::vehicle_frame_id_ = "base_link"
protected

Definition at line 389 of file trajectory_optimization_node.hpp.

◆ verbose_

bool trajectory_optimization::TrajectoryOptimizationNode::verbose_ = false
protected

Definition at line 397 of file trajectory_optimization_node.hpp.

◆ viz_circles_

std::vector<double> trajectory_optimization::TrajectoryOptimizationNode::viz_circles_
protected

Definition at line 421 of file trajectory_optimization_node.hpp.

◆ xtraj_

std::vector<double> trajectory_optimization::TrajectoryOptimizationNode::xtraj_
protected

Definition at line 447 of file trajectory_optimization_node.hpp.


The documentation for this class was generated from the following files: