trajectory_optimization v1.4.0
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.
 
TrajectoryOptimizationNode & operator= (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.
 
TrajectoryOptimizationNode & operator= (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.
 
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)
 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
 
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_ = 2.0
 
double bi_level_dY_ = 0.3
 
double bi_level_dYaw_ = 89.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< PerformanceLogger > performance_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 31 of file trajectory_optimization_node.cpp.

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

91{ 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 94 of file trajectory_optimization_node.cpp.

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

302 {
303 // free solver
305 if (status != 0) {
306 RCLCPP_ERROR(this->get_logger(), "%s_acados_free() returned status %d.", model_name_.c_str(), status);
307 }
308 // free solver capsule
310 if (status != 0) {
311 RCLCPP_ERROR(this->get_logger(), "%s_acados_free_capsule() returned status %d.", model_name_.c_str(), status);
312 }
313}
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

◆ 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; // finite sentinel avoids NaNs during reference-path interpolation
157 constexpr double MAX_ROUTE_S_DIFFERENCE = 20.0;
158 constexpr double LOOK_BEHIND_REMAINING_ROUTE_S = 2.0;
159
160 struct Boundaries {
161 std::vector<std::pair<double, double>> min_normal_distances;
162 std::vector<Eigen::Vector2d> left_boundary_points;
163 std::vector<Eigen::Vector2d> right_boundary_points;
164 std::vector<double> left_boundary_route_s;
165 std::vector<double> right_boundary_route_s;
166 std::vector<Eigen::Vector2d> left_boundary_intersections;
167 std::vector<Eigen::Vector2d> right_boundary_intersections;
168 };
169
170 Boundaries boundaries;
171 auto route_elements = route_planning_msgs::route_access::getRemainingRouteElements(route, true);
172 const int ref_sample_size = trajectory_planning_msgs::trajectory_access::getSamplePointSize(reference_trajectory);
173
174 if (route_elements.empty()) {
176 RCLCPP_WARN(get_logger(), "Remaining route is empty. Do not constrain boundaries.");
177 }
178 for (int i = 0; i < ref_sample_size; ++i) {
179 boundaries.min_normal_distances.emplace_back(NO_BOUNDARY_DISTANCE, NO_BOUNDARY_DISTANCE);
180 }
181 return boundaries.min_normal_distances;
182 }
183
184 const double current_route_s = route_elements.front().s;
185 const size_t current_route_index = std::min(static_cast<size_t>(route.current_route_element_idx), route.route_elements.size());
186 size_t overlap_begin = current_route_index;
187 while (overlap_begin > 0 && current_route_s - route.route_elements[overlap_begin - 1].s <= LOOK_BEHIND_REMAINING_ROUTE_S) {
188 --overlap_begin;
189 }
190 route_elements.insert(route_elements.begin(), route.route_elements.begin() + static_cast<std::ptrdiff_t>(overlap_begin),
191 route.route_elements.begin() + static_cast<std::ptrdiff_t>(current_route_index));
192
193 boundaries.left_boundary_points.reserve(route_elements.size());
194 boundaries.right_boundary_points.reserve(route_elements.size());
195 boundaries.left_boundary_route_s.reserve(route_elements.size());
196 boundaries.right_boundary_route_s.reserve(route_elements.size());
197 boundaries.min_normal_distances.reserve(ref_sample_size);
198 boundaries.left_boundary_intersections.reserve(ref_sample_size);
199 boundaries.right_boundary_intersections.reserve(ref_sample_size);
200
201 for (const auto& route_element : route_elements) {
202 if (route_element.is_enriched) {
204 route_planning_msgs::msg::LaneElement suggested_lane =
205 route_planning_msgs::route_access::getSuggestedLaneElement(route_element);
206 boundaries.left_boundary_points.emplace_back(suggested_lane.left_boundary.point.x, suggested_lane.left_boundary.point.y);
207 boundaries.right_boundary_points.emplace_back(suggested_lane.right_boundary.point.x,
208 suggested_lane.right_boundary.point.y);
209 boundaries.left_boundary_route_s.push_back(route_element.s);
210 boundaries.right_boundary_route_s.push_back(route_element.s);
212 const auto& lane_elements = route_element.lane_elements;
213 if (!lane_elements.empty()) {
214 boundaries.left_boundary_points.emplace_back(lane_elements.front().left_boundary.point.x,
215 lane_elements.front().left_boundary.point.y);
216 boundaries.right_boundary_points.emplace_back(lane_elements.back().right_boundary.point.x,
217 lane_elements.back().right_boundary.point.y);
218 boundaries.left_boundary_route_s.push_back(route_element.s);
219 boundaries.right_boundary_route_s.push_back(route_element.s);
220 }
222 boundaries.left_boundary_points.emplace_back(route_element.left_boundary.x, route_element.left_boundary.y);
223 boundaries.right_boundary_points.emplace_back(route_element.right_boundary.x, route_element.right_boundary.y);
224 boundaries.left_boundary_route_s.push_back(route_element.s);
225 boundaries.right_boundary_route_s.push_back(route_element.s);
226 }
227 }
228 }
229
230 // Helper lambda to find intersection
231 auto findIntersection = [&](const Eigen::Vector2d& ref_pos, double sin_yaw, double cos_yaw, double expected_route_s,
232 const std::vector<Eigen::Vector2d>& boundary_points, const std::vector<double>& boundary_route_s,
233 bool isLeft) -> std::pair<double, Eigen::Vector2d> {
234 std::pair<double, Eigen::Vector2d> intersection_result = {
235 std::numeric_limits<double>::infinity(),
236 Eigen::Vector2d(std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity())};
237 double best_route_s_difference = std::numeric_limits<double>::infinity();
238
239 const Eigen::Vector2d normal_dir = isLeft ? Eigen::Vector2d(-sin_yaw, cos_yaw) : Eigen::Vector2d(sin_yaw, -cos_yaw);
240 const auto cross2d = [](const Eigen::Vector2d& u, const Eigen::Vector2d& v) { return u.x() * v.y() - u.y() * v.x(); };
241
242 for (size_t i = 0; i + 1 < boundary_points.size(); ++i) {
243 const Eigen::Vector2d& a = boundary_points[i];
244 const Eigen::Vector2d& b = boundary_points[i + 1];
245 Eigen::Vector2d seg = b - a;
246 Eigen::Vector2d ap = ref_pos - a;
247
248 const double denom = cross2d(seg, normal_dir);
249 if (std::abs(denom) < 1e-9) {
250 continue; // Lines are close to parallel; ignore this segment
251 }
252
253 const double s = cross2d(ap, normal_dir) / denom;
254 const double t = cross2d(ap, seg) / denom;
255
256 if (s >= 0.0 && s <= 1.0 && t >= 0.0) {
257 Eigen::Vector2d intersection = a + s * seg;
258 double euclidean_distance = (ref_pos - intersection).norm();
259 const double intersection_route_s = boundary_route_s[i] + s * (boundary_route_s[i + 1] - boundary_route_s[i]);
260 const double route_s_difference = std::abs(intersection_route_s - expected_route_s);
261
262 if (route_s_difference <= MAX_ROUTE_S_DIFFERENCE &&
263 (route_s_difference < best_route_s_difference ||
264 (std::abs(route_s_difference - best_route_s_difference) < 1e-9 && euclidean_distance < intersection_result.first))) {
265 best_route_s_difference = route_s_difference;
266 intersection_result = {euclidean_distance, intersection};
267 }
268 }
269 }
270 return intersection_result;
271 };
272
273 // Loop over trajectory points and compute intersections
274 double reference_progress = 0.0;
275 Eigen::Vector2d previous_ref_pos = Eigen::Vector2d::Zero();
276 for (int i = 0; i < ref_sample_size; ++i) {
277 Eigen::Vector2d ref_pos(trajectory_planning_msgs::trajectory_access::getX(reference_trajectory, i),
278 trajectory_planning_msgs::trajectory_access::getY(reference_trajectory, i));
279 reference_progress += (ref_pos - previous_ref_pos).norm();
280 previous_ref_pos = ref_pos;
281 const double expected_route_s = current_route_s + reference_progress;
282
283 double yaw = trajectory_planning_msgs::trajectory_access::getTheta(reference_trajectory, i);
284 const double sin_yaw = std::sin(yaw);
285 const double cos_yaw = std::cos(yaw);
286
287 auto left_intersection = findIntersection(ref_pos, sin_yaw, cos_yaw, expected_route_s, boundaries.left_boundary_points,
288 boundaries.left_boundary_route_s, true);
289 auto right_intersection = findIntersection(ref_pos, sin_yaw, cos_yaw, expected_route_s, boundaries.right_boundary_points,
290 boundaries.right_boundary_route_s, false);
291 if (left_intersection.first != std::numeric_limits<double>::infinity() &&
292 right_intersection.first != std::numeric_limits<double>::infinity()) {
293 boundaries.min_normal_distances.emplace_back(left_intersection.first, right_intersection.first);
294 boundaries.left_boundary_intersections.emplace_back(left_intersection.second);
295 boundaries.right_boundary_intersections.emplace_back(right_intersection.second);
296 RCLCPP_DEBUG(this->get_logger(), "Minimum left boundary distance: %.2f m", left_intersection.first);
297 RCLCPP_DEBUG(this->get_logger(), "Minimum right boundary distance: %.2f m", right_intersection.first);
298 } else {
299 boundaries.min_normal_distances.emplace_back(NO_BOUNDARY_DISTANCE, NO_BOUNDARY_DISTANCE);
300 RCLCPP_WARN(get_logger(),
301 "No boundary intersection found for trajectory point %d. Do not constrain boundaries at this point.", i);
302 }
303 }
304 if (debug_viz_) {
305 vizBoundaryPoints(boundaries.left_boundary_intersections, boundaries.right_boundary_intersections);
306 }
307
308 return boundaries.min_normal_distances;
309}
void vizBoundaryPoints(const std::vector< Eigen::Vector2d > &left_boundary_points, const std::vector< Eigen::Vector2d > &right_boundary_points)
Publishes the boundary intersections corresponding to the distances passed to the OCP.
Definition utils.cpp:311

◆ 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 166 of file trajectory_optimization_node.cpp.

167 {
168 for (const auto& param : parameters) {
169 for (auto& auto_reconfigurable_param : auto_reconfigurable_params_) {
170 if (param.get_name() == std::get<0>(auto_reconfigurable_param)) {
171 std::get<1>(auto_reconfigurable_param)(param);
172 RCLCPP_INFO(this->get_logger(), "Reconfigured parameter '%s' to: %s", param.get_name().c_str(),
173 param.value_to_string().c_str());
174 break;
175 }
176 }
177 // handle special cases
178 if (param.get_name() == "run_as_callback") {
180 planning_timer_ = this->create_wall_timer(std::chrono::duration<double>(1 / optimization_freq_),
182 RCLCPP_WARN(this->get_logger(), "OCP runs now periodically with frequency %f Hz", optimization_freq_);
183 } else if (run_as_callback_ && planning_timer_) {
184 planning_timer_->cancel();
185 planning_timer_.reset();
186 RCLCPP_WARN(this->get_logger(), "OCP runs now on reference trajectory callback");
187 }
188 }
189 }
190 // mark parameter change successful
191 rcl_interfaces::msg::SetParametersResult result;
192 result.successful = true;
193
194 return result;
195}
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 389 of file trajectory_optimization_node.cpp.

389 {
390 const auto cycle_start = SteadyClock::now();
391 if (debug_viz_) viz_circles_.clear();
392 if (rclcpp::Time(this->now()) - rclcpp::Time(ego_data_.header.stamp) > rclcpp::Duration::from_seconds(ego_data_timeout_)) {
393 RCLCPP_WARN(this->get_logger(), "EgoData outdated. Skipping planning cycle.");
394 return;
395 }
396 // init trajectory message and set header
397 trajectory_planning_msgs::msg::Trajectory::UniquePtr trajectory = std::make_unique<trajectory_planning_msgs::msg::Trajectory>();
398 initializeTrajectory(*trajectory);
399
400 trajectory->header.frame_id = vehicle_frame_id_;
401 trajectory->header.stamp = ego_data_.header.stamp; // use latest ego_data stamp as trajectory stamp
402
403 // init time-steps of trajectory to ensure increasing time-steps even for standstill trajectories
404 double dt = optimization_horizon_ / n_shots_;
405 for (int i = 0; i <= n_shots_; ++i) trajectory_planning_msgs::trajectory_access::setT(*trajectory, i * dt, i);
406
407 // check if the reference trajectory is standstill
408 if (trajectory_planning_msgs::trajectory_access::getStandstill(reference_trajectory_)) {
409 RCLCPP_WARN(this->get_logger(), "Standstill trajectory. Skipping planning cycle. Publish standstill trajectory.");
410 // transform trajectory to output frame
411 if (!trajectory2outputFrame(*trajectory)) {
412 return;
413 }
414 trajectory_planning_msgs::trajectory_access::setStandstill(*trajectory, true);
415 trajectory_pub_->publish(std::move(trajectory));
416 // Invalidate the warm start and reset the solver once when entering standstill.
417 if (!control_guess_.empty()) {
418 control_guess_.clear();
419 resetSolver();
420 }
421 return;
422 }
423
424 PerformanceMetrics metrics;
425 metrics.cycle = ++logging_cycle_;
426 metrics.ego_stamp_ns = rclcpp::Time(ego_data_.header.stamp).nanoseconds();
427 metrics.reference_stamp_ns = rclcpp::Time(reference_trajectory_.header.stamp).nanoseconds();
428 metrics.route_stamp_ns = rclcpp::Time(route_.header.stamp).nanoseconds();
429 metrics.reference_points = trajectory_planning_msgs::trajectory_access::getSamplePointSize(reference_trajectory_);
430 metrics.objects = static_cast<int>(object_list_.objects.size());
431 auto logCompletedCycle = [&]() {
432 metrics.cycle_ms = elapsedMilliseconds(cycle_start);
433 metrics.postprocessing_ms = metrics.cycle_ms - metrics.preprocessing_ms - metrics.solve_wall_ms;
435 performance_logger_->write(metrics);
436 }
437 };
438
439 // set initial state
440 std::vector<double> x_init(*nlp_dims_->nx, 0.0);
441 if (!trajectory_planning_msgs::trajectory_access::getStandstill(latest_valid_trajectory_)) {
443 } else {
444 RCLCPP_WARN(this->get_logger(),
445 "Latest available trajectory is standstill. Using ego data for initial state (high-level initialization).");
446 x_init = getHighLevelX0(ego_data_);
447 }
448
449 // debug print of initial state
450 std::stringstream ss;
451 ss << "Initial state: ";
452 for (size_t i = 0; i < x_init.size(); ++i) ss << "x[" << i << "]: " << x_init[i] << (i != x_init.size() - 1 ? ", " : "");
453 RCLCPP_DEBUG(this->get_logger(), "%s", ss.str().c_str());
454
455 ocp_nlp_constraints_model_set(nlp_config_, nlp_dims_, nlp_in_, nlp_out_, 0, "lbx", x_init.data());
456 ocp_nlp_constraints_model_set(nlp_config_, nlp_dims_, nlp_in_, nlp_out_, 0, "ubx", x_init.data());
457
458 // update inputs to the ocp; skip planning cycle if update fails
460 RCLCPP_WARN(this->get_logger(), "Failed to update inputs. Skipping planning cycle.");
461 return;
462 }
463
464 if (!setInitialGuess(x_init, rclcpp::Time(ego_data_.header.stamp))) {
465 control_guess_.clear();
466 return;
467 }
468
469 // solve the optimization problem
470 const auto solve_start = SteadyClock::now();
471 metrics.preprocessing_ms = elapsedMilliseconds(cycle_start, solve_start);
473 const auto solve_end = SteadyClock::now();
474 metrics.solve_wall_ms = elapsedMilliseconds(solve_start, solve_end);
475
477 performance_logger_ != nullptr || verbose_);
478
479 const bool usable_status =
480 metrics.status == ACADOS_SUCCESS || metrics.status == ACADOS_MAXITER || metrics.status == ACADOS_TIMEOUT;
481 if (usable_status) {
482 for (int ii = 0; ii <= nlp_dims_->N; ++ii) {
483 ocp_nlp_out_get(nlp_config_, nlp_dims_, nlp_out_, ii, "x", &xtraj_[ii * *nlp_dims_->nx]);
484 }
485 for (int ii = 0; ii < nlp_dims_->N; ++ii) {
486 ocp_nlp_out_get(nlp_config_, nlp_dims_, nlp_out_, ii, "u", &utraj_[ii * *nlp_dims_->nu]);
487 }
488 }
489
491 const bool finite_solution = usable_status && std::isfinite(metrics.cost_value) && std::isfinite(metrics.res_eq) &&
492 std::isfinite(metrics.res_ineq) &&
493 std::all_of(xtraj_.begin(), xtraj_.end(), [](double value) { return std::isfinite(value); }) &&
494 std::all_of(utraj_.begin(), utraj_.end(), [](double value) { return std::isfinite(value); });
495 const bool primal_feasible =
496 finite_solution && metrics.res_eq <= solver_opts->tol_eq && metrics.res_ineq <= solver_opts->tol_ineq;
497
498 if (!primal_feasible) {
499 printSolution(metrics);
500 RCLCPP_WARN(this->get_logger(),
501 "Rejecting solver output: status=%d finite=%d primal residuals=[eq=%e, ineq=%e] tolerances=[eq=%e, ineq=%e].",
502 metrics.status, finite_solution, metrics.res_eq, metrics.res_ineq, solver_opts->tol_eq, solver_opts->tol_ineq);
503 if (finite_solution && performance_logger_) {
505 static_cast<int>(p_obstacle_circles_shape_[0]));
506 }
507 if (finite_solution && (metrics.status == ACADOS_MAXITER || metrics.status == ACADOS_TIMEOUT)) {
508 // Preserve progress from a recoverable solve; the states are rolled out again from the next x_init.
510 control_guess_stamp_ = rclcpp::Time(ego_data_.header.stamp);
511 } else {
512 control_guess_.clear();
513 }
514 resetSolver();
515 logCompletedCycle();
516 return;
517 }
518
520 control_guess_stamp_ = rclcpp::Time(ego_data_.header.stamp);
521
522 printSolution(metrics);
523 if (debug_viz_) {
526 }
527
528 // convert output into trajectory message
529 convertToTrajectoryMsg(*trajectory);
530
531 bool standstill = true;
532 for (int i = 0; i < trajectory_planning_msgs::trajectory_access::getSamplePointSize(*trajectory); ++i) {
533 if (trajectory_planning_msgs::trajectory_access::getV(*trajectory, i) > standstill_threshold_) standstill = false;
534 }
535 trajectory_planning_msgs::trajectory_access::setStandstill(*trajectory, standstill);
536
537 // transform trajectory to output frame
538 if (!trajectory2outputFrame(*trajectory)) {
539 logCompletedCycle();
540 return;
541 }
542
543 latest_valid_trajectory_ = *trajectory;
544 trajectory_pub_->publish(std::move(trajectory));
545 metrics.published = true;
546 logCompletedCycle();
547 const char* cycle_time_color = metrics.cycle_ms <= 100.0 ? "\x1b[32m" : "\x1b[31m";
548 RCLCPP_INFO(this->get_logger(), "Published trajectory (cycle: %s%.2f ms\x1b[0m)", cycle_time_color, metrics.cycle_ms);
549}
static void collectConstraintDiagnostics(PerformanceMetrics &metrics, ocp_nlp_solver *solver, const ocp_nlp_dims *dims, int obstacle_circles)
Collects the largest equality and inequality constraint violations from acados.
static void collectSolverStatistics(PerformanceMetrics &metrics, ocp_nlp_solver *solver, ocp_nlp_config *config, ocp_nlp_dims *dims, ocp_nlp_in *input, ocp_nlp_out *output, bool collect_details)
Reads timing, iteration, cost, and residual statistics from acados.
void printSolution(const PerformanceMetrics &metrics)
Logs solver status and optional debug statistics for the last optimization run.
Definition utils.cpp:451
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:348
virtual void initializeTrajectory(trajectory_planning_msgs::msg::Trajectory &trajectory)=0
Initializes an output trajectory message with the model-specific message type and size.
virtual void convertToTrajectoryMsg(trajectory_planning_msgs::msg::Trajectory &trajectory)=0
Maps the optimized state trajectory into the model-specific output message fields.
void vizEgoCircles(const std::vector< double > &x_trajectory, const std::string &model_name)
Publishes the ego-vehicle circle approximation used by the selected OCP model.
Definition utils.cpp:371
bool trajectory2outputFrame(trajectory_planning_msgs::msg::Trajectory &trajectory)
Transforms the planned trajectory into the configured output frame (trajectory_frame_id_) if required...
Definition utils.cpp:71
bool updateOcpInputs(const perception_msgs::msg::EgoData &ego_data, const perception_msgs::msg::ObjectList &object_list, const route_planning_msgs::msg::Route &route, const trajectory_planning_msgs::msg::Trajectory &reference_trajectory)
Transforms external inputs into the optimizer frame and writes them into the OCP.
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.
const ocp_nlp_opts * acados_get_common_nlp_opts(ocp_nlp_config *config, void *solver_opts)
Returns the common NLP options stored inside the generated solver-specific options.

◆ 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 451 of file utils.cpp.

451 {
452 // Status codes:
453 // 0: Success (ACADOS_SUCCESS)
454 // 1: NaN detected (ACADOS_NAN_DETECTED)
455 // 2: Maximum number of iterations reached (ACADOS_MAXITER)
456 // 3: Minimum step size reached (ACADOS_MINSTEP)
457 // 4: QP solver failed (ACADOS_QP_FAILURE)
458 // 5: Solver created (ACADOS_READY)
459 // 6: Problem unbounded (ACADOS_UNBOUNDED)
460 // 7: Solver timeout (ACADOS_TIMEOUT)
461 if (metrics.status == ACADOS_SUCCESS && verbose_) {
462 RCLCPP_INFO(get_logger(), "\033[1;32mOptimization: SUCCESS!\033[0m");
463 } else if (metrics.status == ACADOS_MAXITER) {
464 RCLCPP_WARN(get_logger(), "Optimization failed with status %d (max iterations).", metrics.status);
465 } else if (metrics.status == ACADOS_TIMEOUT) {
466 RCLCPP_WARN(get_logger(), "\033[38;5;214mOptimization failed with status %d (timeout).\033[0m", metrics.status);
467 } else if (metrics.status != ACADOS_SUCCESS) {
468 RCLCPP_ERROR(get_logger(), "%s_acados_solve() failed with status %d.", model_name_.c_str(), metrics.status);
469 }
470
471 if (verbose_) {
472 RCLCPP_INFO(get_logger(), "Optimization took %.3f ms (SQP iter: %d; QP iter: %d; KKT: %e)", metrics.acados_total_ms,
473 metrics.sqp_iter, metrics.qp_iter, metrics.kkt_norm_inf);
474 RCLCPP_INFO(get_logger(), "cost_value: %f; residuals: stat=%e eq=%e ineq=%e comp=%e", metrics.cost_value, metrics.res_stat,
475 metrics.res_eq, metrics.res_ineq, metrics.res_comp);
476
477 std::fputs("\n--- xtraj ---\n", stdout);
478 d_print_exp_tran_mat(*nlp_dims_->nx, n_shots_ + 1, xtraj_.data(), *nlp_dims_->nx);
479 std::fputs("\n--- utraj ---\n", stdout);
480 d_print_exp_tran_mat(*nlp_dims_->nu, n_shots_, utraj_.data(), *nlp_dims_->nu);
482 }
483}
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 315 of file trajectory_optimization_node.cpp.

315 {
316 const int status = trajectory_optimization::acados_reset(ocp_capsule_, 1, 0, 0, 0);
317 if (status != ACADOS_SUCCESS) {
318 RCLCPP_ERROR(this->get_logger(), "%s_acados_reset() returned status %d. Recreating solver.", model_name_.c_str(), status);
319 freeSolver();
320 setupSolver();
321 }
322}
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 324 of file trajectory_optimization_node.cpp.

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

603 {
604 const auto start_time = std::chrono::steady_clock::now();
605 std::vector<double> global_params;
606
607 // cost weights
608 const auto expected_cost_weights_size = static_cast<size_t>(p_cost_weights_shape_[0] * p_cost_weights_shape_[1]);
609 if (cost_weights.size() != expected_cost_weights_size) {
610 RCLCPP_ERROR(this->get_logger(), "Size of cost_weights (%zu) does not match expected size (%zu).", cost_weights.size(),
611 expected_cost_weights_size);
612 throw std::runtime_error("Size of cost_weights does not match expected size.");
613 }
614 global_params.insert(global_params.end(), cost_weights.begin(), cost_weights.end());
615
616 // other cost params
617 global_params.push_back(thw_);
618 global_params.push_back(d_min_obstacle_long_);
619 global_params.push_back(d_min_obstacle_lat_);
620 global_params.push_back(d_min_boundary_lat_);
621
622 // reference path (including boundaries)
623 const size_t n_ref_states = static_cast<size_t>(p_ref_path_shape_[0] * p_ref_path_shape_[1]);
624 std::vector<std::pair<double, double>> boundary_distances = normalBoundaryDistance(reference_trajectory, route);
625 // fill ref vector for ocp -> psi, x, y, v, d_bound_left, d_bound_right
626 std::vector<double> ref;
627 for (int i = 0; i < trajectory_planning_msgs::trajectory_access::getSamplePointSize(reference_trajectory); ++i) {
628 ref.push_back(trajectory_planning_msgs::trajectory_access::getTheta(reference_trajectory, i));
629 ref.push_back(trajectory_planning_msgs::trajectory_access::getX(reference_trajectory, i));
630 ref.push_back(trajectory_planning_msgs::trajectory_access::getY(reference_trajectory, i));
631 ref.push_back(trajectory_planning_msgs::trajectory_access::getV(reference_trajectory, i));
632 ref.push_back(boundary_distances[i].first); // left boundary distance
633 ref.push_back(boundary_distances[i].second); // right boundary distance
634 }
635 if (ref.size() >= n_ref_states) {
636 global_params.insert(global_params.end(), ref.begin(),
637 ref.begin() + static_cast<std::vector<double>::difference_type>(n_ref_states));
638 } else {
639 // Repeat the final valid state. Infinity padding can propagate NaNs through closest-point calculations.
640 global_params.insert(global_params.end(), ref.begin(), ref.end());
641 const size_t state_width = static_cast<size_t>(p_ref_path_shape_[1]);
642 while (global_params.size() < expected_cost_weights_size + 4 + n_ref_states) {
643 global_params.insert(global_params.end(), ref.end() - static_cast<std::vector<double>::difference_type>(state_width),
644 ref.end());
645 }
646 }
647
648 if (global_params.size() != static_cast<size_t>(nlp_dims_->np_global)) {
649 RCLCPP_ERROR(this->get_logger(), "Size of global parameters (%zu) does not match expected size (%d).", global_params.size(),
650 nlp_dims_->np_global);
651 throw std::runtime_error("Size of global parameters does not match expected size.");
652 }
654 ocp_capsule_, global_params.data(), static_cast<int>(global_params.size()));
655 if (status != ACADOS_SUCCESS) {
656 throw std::runtime_error("acados global parameter update failed with status " + std::to_string(status));
657 }
658 const auto elapsed_ms = std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - start_time).count();
659 RCLCPP_DEBUG(this->get_logger(), "setOcpGlobalParameters duration: %.3f ms", elapsed_ms);
660}
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 662 of file trajectory_optimization_node.cpp.

663 {
664 const auto start_time = std::chrono::steady_clock::now();
665 struct PredictionData {
666 std::vector<double> time;
667 std::vector<double> x;
668 std::vector<double> y;
669 std::vector<double> yaw;
670 size_t index;
671 double probability;
672 };
673 std::vector<std::vector<PredictionData>> object_predictions(object_list.objects.size());
674 const double object_stamp = static_cast<double>(rclcpp::Time(object_list.header.stamp).nanoseconds()) / 1e9;
675 for (size_t j = 0; j < object_list.objects.size(); ++j) {
676 const auto& object = object_list.objects[j];
677 if (consider_objects_ != CONSIDER_OBJECTS::PREDICTED_OBJECTS || object.state_predictions.empty()) continue;
678
679 auto append_prediction = [&](const auto& state_prediction, size_t prediction_idx) {
680 PredictionData prediction{{object_stamp},
681 {perception_msgs::object_access::getX(object)},
682 {perception_msgs::object_access::getY(object)},
683 {perception_msgs::object_access::getYaw(object)},
684 prediction_idx,
685 state_prediction.probability};
686 for (const auto& predicted_state : state_prediction.states) {
687 prediction.time.push_back(static_cast<double>(rclcpp::Time(predicted_state.header.stamp).nanoseconds()) / 1e9);
688 prediction.x.push_back(perception_msgs::object_access::getX(predicted_state));
689 prediction.y.push_back(perception_msgs::object_access::getY(predicted_state));
690 prediction.yaw.push_back(perception_msgs::object_access::getYaw(predicted_state));
691 }
692 object_predictions[j].push_back(std::move(prediction));
693 };
694
695 for (size_t prediction_idx = 0; prediction_idx < object.state_predictions.size(); ++prediction_idx) {
696 const auto& state_prediction = object.state_predictions[prediction_idx];
697 if (state_prediction.probability > min_prediction_probability_) {
698 append_prediction(state_prediction, prediction_idx);
699 }
700 }
701 if (object_predictions[j].empty()) append_prediction(object.state_predictions[0], 0);
702 }
703
704 // loop over shooting intervals
705 double floating_dynamic_weight = 1.0;
706 double dt = optimization_horizon_ / n_shots_;
707 for (int i = 0; i <= n_shots_; ++i) {
708 // dynamic weight
709 int idx = 0;
710 int n = 1;
711 std::vector<int> idx_dynamic_weight(n);
712 // fill vector with values from idx to idx + n
713 std::iota(idx_dynamic_weight.begin(), idx_dynamic_weight.end(), idx);
714 int status = trajectory_optimization::acados_update_params_sparse(ocp_capsule_, i, idx_dynamic_weight.data(),
715 &floating_dynamic_weight, n);
716 if (status != ACADOS_SUCCESS) {
717 throw std::runtime_error("acados dynamic-weight update failed with status " + std::to_string(status));
718 }
719 floating_dynamic_weight *= dynamic_weight_;
720
721 // obstacles
722 idx += n;
723 n = static_cast<int>(p_obstacle_circles_shape_[0] * p_obstacle_circles_shape_[1]);
724 std::vector<double> circles; // [x1, y1, r1, x2, y2, r2, ...]
725
726 for (size_t j = 0; j < object_list.objects.size(); ++j) {
727 std::vector<std::tuple<double, double, double>> target_states;
728 if (!object_predictions[j].empty()) {
729 for (const auto& prediction : object_predictions[j]) {
730 double x_tgt = 0.0, y_tgt = 0.0, yaw_tgt = 0.0;
731 double des_time = static_cast<double>(rclcpp::Time(ego_data.header.stamp).nanoseconds()) / 1e9 + dt * i;
732 if (des_time > prediction.time.back()) {
733 const double relative_des_time = des_time - prediction.time.front();
734 const double relative_max_time = prediction.time.back() - prediction.time.front();
735 RCLCPP_WARN(this->get_logger(),
736 "Prediction horizon shorter than requested interpolation time. "
737 "object=%zu prediction=%zu probability=%.3f desired_rel=%.3f s max_rel=%.3f s n_states=%zu. "
738 "Using last prediction state.",
739 j, prediction.index, prediction.probability, relative_des_time, relative_max_time,
740 prediction.time.size() - 1);
741 x_tgt = prediction.x.back();
742 y_tgt = prediction.y.back();
743 yaw_tgt = prediction.yaw.back();
744 } else {
745 linearInterpolation(prediction.time, prediction.x, des_time, x_tgt);
746 linearInterpolation(prediction.time, prediction.y, des_time, y_tgt);
747 linearInterpolation(prediction.time, prediction.yaw, des_time, yaw_tgt, true);
748 }
749 target_states.emplace_back(x_tgt, y_tgt, yaw_tgt);
750 }
751 } else {
752 target_states.emplace_back(perception_msgs::object_access::getX(object_list.objects[j]),
753 perception_msgs::object_access::getY(object_list.objects[j]),
754 perception_msgs::object_access::getYaw(object_list.objects[j]));
755 }
756
757 double alpha = std::atan2(object_list.objects[j].state.reference_point.translation_to_geometric_center.y,
758 object_list.objects[j].state.reference_point.translation_to_geometric_center.x);
759 double a = std::sqrt(std::pow(object_list.objects[j].state.reference_point.translation_to_geometric_center.x, 2) +
760 std::pow(object_list.objects[j].state.reference_point.translation_to_geometric_center.y, 2));
761 for (auto& [x_tgt, y_tgt, yaw_tgt] : target_states) {
762 // ensure that x_tgt and y_tgt represent the geometric center of the object
763 double beta = wrap_angle_rad(yaw_tgt - alpha);
764 x_tgt += a * std::cos(beta);
765 y_tgt += a * std::sin(beta);
766
767 std::vector<double> obj_circles =
768 discretizeBB2Circles(x_tgt, y_tgt, yaw_tgt, perception_msgs::object_access::getLength(object_list.objects[j]),
769 perception_msgs::object_access::getWidth(object_list.objects[j]));
770
771 circles.insert(circles.end(), obj_circles.begin(), obj_circles.end());
772 if (circles.size() >= static_cast<size_t>(n)) {
773 circles.resize(n);
774 break;
775 }
776 }
777 if (circles.size() >= static_cast<size_t>(n)) {
778 break;
779 }
780 }
781 // fill up with dummy "ghost" obstacle circles at (10000, 10000) to avoid NaNs in the optimization problem
782 // TODO: improve this // NOLINT(google-readability-todo)
783 while (circles.size() < static_cast<size_t>(n)) {
784 std::vector<double> dummy_circle = {10000.0, 10000.0, 1.0};
785 circles.insert(circles.end(), dummy_circle.begin(), dummy_circle.end());
786 }
787 if ((circles.size() % p_obstacle_circles_shape_[1]) != 0) {
788 RCLCPP_WARN(this->get_logger(), "Circles vector size is not a multiple of the circle shape. Resizing.");
789 circles.resize(n);
790 }
791 if (debug_viz_) viz_circles_.insert(viz_circles_.end(), circles.begin(), circles.end());
792
793 std::vector<int> idx_obstacles(n);
794 // fill vector with values from idx to idx + n
795 std::iota(idx_obstacles.begin(), idx_obstacles.end(), idx);
796 status = trajectory_optimization::acados_update_params_sparse(ocp_capsule_, i, idx_obstacles.data(), circles.data(), n);
797 if (status != ACADOS_SUCCESS) {
798 throw std::runtime_error("acados obstacle update failed with status " + std::to_string(status));
799 }
800 }
801 const auto elapsed_ms = std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - start_time).count();
802 RCLCPP_DEBUG(this->get_logger(), "setOcpParameters duration: %.3f ms", elapsed_ms);
803}
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 197 of file trajectory_optimization_node.cpp.

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

267 {
268 if (n_shots_ <= 0) {
269 RCLCPP_FATAL(this->get_logger(), "n_shots must be > 0, got %d", n_shots_);
270 exit(1);
271 }
272
273 // Create exactly once. This also supports a runtime horizon that differs from the generated default.
275 std::vector<double> new_time_steps(n_shots_, optimization_horizon_ / n_shots_);
276 RCLCPP_INFO(this->get_logger(), "Create OCP with: horizon = %f, n_shots = %d, dt = %f", optimization_horizon_, n_shots_,
277 new_time_steps.front());
279
280 if (status != ACADOS_SUCCESS) {
281 RCLCPP_FATAL(this->get_logger(), "%s_acados_create_with_discretization() returned status %d. Exiting.", model_name_.c_str(),
282 status);
283 exit(1);
284 }
285
292 if (nlp_dims_->N != n_shots_) {
293 RCLCPP_FATAL(this->get_logger(), "Created solver has N=%d, expected n_shots=%d. Exiting.", nlp_dims_->N, n_shots_);
294 exit(1);
295 }
296
297 xtraj_.resize(*nlp_dims_->nx * (n_shots_ + 1));
298 utraj_.resize(*nlp_dims_->nu * n_shots_);
299 control_guess_.clear();
300}
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 )
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.
Returns
true if all optimizer inputs were updated successfully.

Definition at line 551 of file trajectory_optimization_node.cpp.

554 {
555 // transform inputs to target base_link frame
556 trajectory_planning_msgs::msg::Trajectory tf_reference_trajectory;
557 perception_msgs::msg::ObjectList tf_object_list;
558 route_planning_msgs::msg::Route tf_route;
559 try {
560 // reference trajectory
561 tf_reference_trajectory =
562 tf2_buffer_->transform(reference_trajectory, vehicle_frame_id_, tf2_ros::fromMsg(ego_data.header.stamp),
563 fixed_over_time_frame_id_, tf2::durationFromSec(0.01));
564 // object list
565 if (!object_list.objects.empty() && object_list.header.frame_id != vehicle_frame_id_) {
566 tf_object_list = tf2_buffer_->transform(object_list, vehicle_frame_id_, tf2_ros::fromMsg(ego_data.header.stamp),
567 fixed_over_time_frame_id_, tf2::durationFromSec(0.01));
568 } else {
569 tf_object_list = object_list;
570 }
571 keepNClosestObjects(tf_object_list, static_cast<int>(p_obstacle_circles_shape_[0]));
572 // route
573 if (!route.route_elements.empty() && route.header.frame_id != vehicle_frame_id_) {
574 tf_route = tf2_buffer_->transform(route, vehicle_frame_id_, tf2_ros::fromMsg(ego_data.header.stamp),
575 fixed_over_time_frame_id_, tf2::durationFromSec(0.1));
576 } else {
577 tf_route = route;
578 }
579 } catch (tf2::TransformException& ex) {
580 RCLCPP_WARN(this->get_logger(), "Transformation is not available. Ex: %s", ex.what());
581 return false;
582 }
583
584 if (trajectory_planning_msgs::trajectory_access::getSamplePointSize(tf_reference_trajectory) <= 0) {
585 RCLCPP_ERROR(this->get_logger(), "Reference trajectory contains no sample points.");
586 return false;
587 }
588
589 // update ocp parameters
590 try {
591 this->setOcpGlobalParameters(cost_weights_, tf_reference_trajectory, tf_route);
592 this->setOcpParameters(ego_data, tf_object_list);
593 } catch (const std::exception& e) {
594 RCLCPP_ERROR(this->get_logger(), "Exception while setting OCP parameters: %s", e.what());
595 return false;
596 }
597
598 return true;
599}
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 311 of file utils.cpp.

312 {
313 visualization_msgs::msg::MarkerArray marker_array;
314 int id = 0;
315
316 auto createMarker = [&](int id, const std::string& ns, const Eigen::Vector2d& pos, float r, float g, float b) {
317 visualization_msgs::msg::Marker m;
318 m.header.frame_id = vehicle_frame_id_;
319 m.header.stamp = rclcpp::Time(ego_data_.header.stamp);
320 m.lifetime = rclcpp::Duration::from_seconds(0.5);
321 m.ns = ns;
322 m.id = id;
323 m.type = visualization_msgs::msg::Marker::SPHERE;
324 m.action = visualization_msgs::msg::Marker::ADD;
325 m.pose.position.x = pos.x();
326 m.pose.position.y = pos.y();
327 m.pose.position.z = 0.0;
328 m.scale.x = m.scale.y = m.scale.z = 0.4;
329 m.color.a = 1.0;
330 m.color.r = r;
331 m.color.g = g;
332 m.color.b = b;
333 return m;
334 };
335
336 auto addMarkers = [&](const std::vector<Eigen::Vector2d>& points, const std::string& ns, float r, float g, float b) {
337 for (const auto& point : points) {
338 marker_array.markers.push_back(createMarker(id++, ns, point, r, g, b));
339 }
340 };
341
342 addMarkers(left_boundary_points, "left_boundary_constraint", 0.0F, 1.0F, 0.0F);
343 addMarkers(right_boundary_points, "right_boundary_constraint", 1.0F, 0.0F, 0.0F);
344
345 boundary_pub_->publish(marker_array);
346}

◆ 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 348 of file utils.cpp.

348 {
349 visualization_msgs::msg::MarkerArray marker_array;
350 const int n_circles = static_cast<int>(obstacles.size() / static_cast<size_t>(p_obstacle_circles_shape_[1]));
351 for (int j = 0; j < n_circles; ++j) {
352 visualization_msgs::msg::Marker marker;
353 marker.header.frame_id = vehicle_frame_id_;
354 marker.header.stamp = rclcpp::Time(ego_data_.header.stamp);
355 marker.lifetime = rclcpp::Duration::from_seconds(0.5);
356 marker.ns = "obstacle-circles";
357 marker.id = j;
358 marker.type = visualization_msgs::msg::Marker::CYLINDER;
359 marker.action = visualization_msgs::msg::Marker::ADD;
360 marker.pose.position.x = obstacles[p_obstacle_circles_shape_[1] * j + 0];
361 marker.pose.position.y = obstacles[p_obstacle_circles_shape_[1] * j + 1];
362 marker.scale.x = obstacles[p_obstacle_circles_shape_[1] * j + 2] * 2.0;
363 marker.scale.y = obstacles[p_obstacle_circles_shape_[1] * j + 2] * 2.0;
364 marker.scale.z = 0.1;
365 marker.color.a = 0.3;
366 marker.color.r = 1.0;
367 marker_array.markers.push_back(marker);
368 }
369 circles_pub_->publish(marker_array);
370}

◆ 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 371 of file utils.cpp.

371 {
372 if (!ego_circles_pub_) return;
373
374 double ego_length = 0.0, ego_width = 0.0;
375 int n_ego_circles = 0;
376 std::vector<double> ego_offset2geocenter;
377
378 // define vehicle geometry based on model name (should match the OCP definition)
379 if (model_name == "karl") {
380 ego_length = 5.173;
381 ego_width = 1.94;
382 ego_offset2geocenter = {1.4895, 0.0};
383 n_ego_circles = 5;
384 } else if (model_name == "shuttle") {
385 ego_length = 4.97;
386 ego_width = 2.12;
387 ego_offset2geocenter = {0.0, 0.0};
388 n_ego_circles = 3;
389 } else {
390 RCLCPP_WARN(this->get_logger(), "Unknown model '%s'. Could not visualize ego circles.", model_name.c_str());
391 return;
392 }
393
394 visualization_msgs::msg::MarkerArray marker_array;
395
396 if (x_trajectory.empty() || n_ego_circles <= 0) {
397 visualization_msgs::msg::Marker delete_marker;
398 delete_marker.action = visualization_msgs::msg::Marker::DELETEALL;
399 marker_array.markers.push_back(delete_marker);
400 ego_circles_pub_->publish(marker_array);
401 return;
402 }
403
404 const double offset_x = !ego_offset2geocenter.empty() ? ego_offset2geocenter[0] : 0.0;
405 const double offset_y = ego_offset2geocenter.size() > 1 ? ego_offset2geocenter[1] : 0.0;
406
407 const double radius = std::sqrt(std::pow(ego_length / (2.0 * n_ego_circles), 2) + std::pow(ego_width / 2.0, 2));
408 const int state_dim = *nlp_dims_->nx;
409
410 int marker_id = 0;
411 for (int stage = 0; stage <= n_shots_; ++stage) {
412 const size_t state_offset = static_cast<size_t>(stage) * static_cast<size_t>(state_dim);
413 const double base_x = x_trajectory[state_offset + 0];
414 const double base_y = x_trajectory[state_offset + 1];
415 const double psi = x_trajectory[state_offset + 5];
416
417 const double ego_center_x = base_x + offset_x * std::cos(psi) - offset_y * std::sin(psi);
418 const double ego_center_y = base_y + offset_x * std::sin(psi) + offset_y * std::cos(psi);
419
420 for (int i = 0; i < n_ego_circles; ++i) {
421 const double lon_offset = -ego_length / 2.0 + (2 * i + 1) * ego_length / (2.0 * n_ego_circles);
422 const double x_offset = lon_offset * std::cos(psi);
423 const double y_offset = lon_offset * std::sin(psi);
424
425 visualization_msgs::msg::Marker marker;
426 marker.header.frame_id = vehicle_frame_id_;
427 marker.header.stamp = rclcpp::Time(ego_data_.header.stamp);
428 marker.lifetime = rclcpp::Duration::from_seconds(0.5);
429 marker.ns = "ego-circles";
430 marker.id = marker_id++;
431 marker.type = visualization_msgs::msg::Marker::CYLINDER;
432 marker.action = visualization_msgs::msg::Marker::ADD;
433 marker.pose.position.x = ego_center_x + x_offset;
434 marker.pose.position.y = ego_center_y + y_offset;
435 marker.pose.position.z = 0.0;
436 marker.pose.orientation.w = 1.0;
437 marker.scale.x = radius * 2.0;
438 marker.scale.y = radius * 2.0;
439 marker.scale.z = 0.05;
440 marker.color.a = 0.4;
441 marker.color.r = 0.0F;
442 marker.color.g = 0.6F;
443 marker.color.b = 1.0F;
444 marker_array.markers.push_back(marker);
445 }
446 }
447
448 ego_circles_pub_->publish(marker_array);
449}

◆ 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

◆ 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 379 of file trajectory_optimization_node.hpp.

◆ bi_level_dV_

double trajectory_optimization::TrajectoryOptimizationNode::bi_level_dV_ = 2.0
protected

Definition at line 398 of file trajectory_optimization_node.hpp.

◆ bi_level_dY_

double trajectory_optimization::TrajectoryOptimizationNode::bi_level_dY_ = 0.3
protected

Definition at line 399 of file trajectory_optimization_node.hpp.

◆ bi_level_dYaw_

double trajectory_optimization::TrajectoryOptimizationNode::bi_level_dYaw_ = 89.0
protected

Definition at line 400 of file trajectory_optimization_node.hpp.

◆ boundary_pub_

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

Definition at line 365 of file trajectory_optimization_node.hpp.

◆ circles_pub_

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

Definition at line 363 of file trajectory_optimization_node.hpp.

◆ consider_boundaries_

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

Definition at line 394 of file trajectory_optimization_node.hpp.

◆ consider_objects_

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

Definition at line 393 of file trajectory_optimization_node.hpp.

◆ control_guess_

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

Definition at line 406 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 407 of file trajectory_optimization_node.hpp.

407{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 413 of file trajectory_optimization_node.hpp.

◆ d_min_boundary_lat_

double trajectory_optimization::TrajectoryOptimizationNode::d_min_boundary_lat_ = 0.0
protected

Definition at line 418 of file trajectory_optimization_node.hpp.

◆ d_min_obstacle_lat_

double trajectory_optimization::TrajectoryOptimizationNode::d_min_obstacle_lat_ = 0.5
protected

Definition at line 417 of file trajectory_optimization_node.hpp.

◆ d_min_obstacle_long_

double trajectory_optimization::TrajectoryOptimizationNode::d_min_obstacle_long_ = 5.0
protected

Definition at line 416 of file trajectory_optimization_node.hpp.

◆ debug_viz_

bool trajectory_optimization::TrajectoryOptimizationNode::debug_viz_ = false
protected

Definition at line 390 of file trajectory_optimization_node.hpp.

◆ dynamic_weight_

double trajectory_optimization::TrajectoryOptimizationNode::dynamic_weight_ = 1.0
protected

Definition at line 414 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 364 of file trajectory_optimization_node.hpp.

◆ ego_data_

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

Definition at line 373 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 357 of file trajectory_optimization_node.hpp.

◆ ego_data_timeout_

double trajectory_optimization::TrajectoryOptimizationNode::ego_data_timeout_ = 1.0
protected

Definition at line 384 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 382 of file trajectory_optimization_node.hpp.

◆ high_level_stabilization_

bool trajectory_optimization::TrajectoryOptimizationNode::high_level_stabilization_ = false
protected

Definition at line 392 of file trajectory_optimization_node.hpp.

◆ latest_valid_trajectory_

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

Definition at line 403 of file trajectory_optimization_node.hpp.

◆ logging_cycle_

uint64_t trajectory_optimization::TrajectoryOptimizationNode::logging_cycle_ = 0
protected

Definition at line 438 of file trajectory_optimization_node.hpp.

◆ min_prediction_probability_

double trajectory_optimization::TrajectoryOptimizationNode::min_prediction_probability_ = 0.0
protected

Definition at line 419 of file trajectory_optimization_node.hpp.

◆ model_name_

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

Definition at line 383 of file trajectory_optimization_node.hpp.

◆ n_shots_

int trajectory_optimization::TrajectoryOptimizationNode::n_shots_ = 50
protected

Definition at line 386 of file trajectory_optimization_node.hpp.

◆ nlp_config_

ocp_nlp_config* trajectory_optimization::TrajectoryOptimizationNode::nlp_config_
protected

Definition at line 429 of file trajectory_optimization_node.hpp.

◆ nlp_dims_

ocp_nlp_dims* trajectory_optimization::TrajectoryOptimizationNode::nlp_dims_
protected

Definition at line 430 of file trajectory_optimization_node.hpp.

◆ nlp_in_

ocp_nlp_in* trajectory_optimization::TrajectoryOptimizationNode::nlp_in_
protected

Definition at line 431 of file trajectory_optimization_node.hpp.

◆ nlp_opts_

void* trajectory_optimization::TrajectoryOptimizationNode::nlp_opts_
protected

Definition at line 434 of file trajectory_optimization_node.hpp.

◆ nlp_out_

ocp_nlp_out* trajectory_optimization::TrajectoryOptimizationNode::nlp_out_
protected

Definition at line 432 of file trajectory_optimization_node.hpp.

◆ nlp_solver_

ocp_nlp_solver* trajectory_optimization::TrajectoryOptimizationNode::nlp_solver_
protected

Definition at line 433 of file trajectory_optimization_node.hpp.

◆ object_list_

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

Definition at line 374 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 358 of file trajectory_optimization_node.hpp.

◆ ocp_capsule_

ocp_model_capsule_t trajectory_optimization::TrajectoryOptimizationNode::ocp_capsule_
protected

Definition at line 428 of file trajectory_optimization_node.hpp.

◆ optimization_freq_

double trajectory_optimization::TrajectoryOptimizationNode::optimization_freq_ = 10.0
protected

Definition at line 385 of file trajectory_optimization_node.hpp.

◆ optimization_horizon_

double trajectory_optimization::TrajectoryOptimizationNode::optimization_horizon_ = 1.0
protected

Definition at line 387 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 423 of file trajectory_optimization_node.hpp.

423{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 425 of file trajectory_optimization_node.hpp.

425{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 424 of file trajectory_optimization_node.hpp.

424{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 355 of file trajectory_optimization_node.hpp.

◆ performance_logger_

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

Definition at line 439 of file trajectory_optimization_node.hpp.

◆ performance_logging_

bool trajectory_optimization::TrajectoryOptimizationNode::performance_logging_ = false
protected

Definition at line 389 of file trajectory_optimization_node.hpp.

◆ planning_timer_

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

Definition at line 367 of file trajectory_optimization_node.hpp.

◆ reference_trajectory_

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

Definition at line 376 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 360 of file trajectory_optimization_node.hpp.

◆ route_

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

Definition at line 375 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 359 of file trajectory_optimization_node.hpp.

◆ run_as_callback_

bool trajectory_optimization::TrajectoryOptimizationNode::run_as_callback_ = false
protected

Definition at line 395 of file trajectory_optimization_node.hpp.

◆ standstill_threshold_

double trajectory_optimization::TrajectoryOptimizationNode::standstill_threshold_ = 0.45
protected

Definition at line 391 of file trajectory_optimization_node.hpp.

◆ tf2_buffer_

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

Definition at line 369 of file trajectory_optimization_node.hpp.

◆ tf2_listener_

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

Definition at line 370 of file trajectory_optimization_node.hpp.

◆ thw_

double trajectory_optimization::TrajectoryOptimizationNode::thw_ = 2.0
protected

Definition at line 415 of file trajectory_optimization_node.hpp.

◆ trajectory_frame_id_

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

Definition at line 381 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 362 of file trajectory_optimization_node.hpp.

◆ utraj_

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

Definition at line 437 of file trajectory_optimization_node.hpp.

◆ vehicle_frame_id_

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

Definition at line 380 of file trajectory_optimization_node.hpp.

◆ verbose_

bool trajectory_optimization::TrajectoryOptimizationNode::verbose_ = false
protected

Definition at line 388 of file trajectory_optimization_node.hpp.

◆ viz_circles_

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

Definition at line 410 of file trajectory_optimization_node.hpp.

◆ xtraj_

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

Definition at line 436 of file trajectory_optimization_node.hpp.


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