lanelet2_map_server v1.2.3
Loading...
Searching...
No Matches
lanelet2_lichtblick_display.cpp
Go to the documentation of this file.
1// Copyright Institute for Automotive Engineering (ika), RWTH Aachen University
2// SPDX-License-Identifier: Apache-2.0
3
5
7
8Lanelet2LichtblickDisplay::Lanelet2LichtblickDisplay() : Node("lanelet2_lichtblick_display") {
9 this->declareAndLoadParameter("left_bound_line_width", left_bound_line_width_, "Width of the left boundary lines", true, false,
10 false);
11 this->declareAndLoadParameter("left_bound_color_hex", left_bound_color_hex_, "Color of the left boundary lines", true, false,
12 false);
13 this->declareAndLoadParameter("left_bound_line_opacity", left_bound_line_opacity_, "Opacity of the left boundary lines", true,
14 false, false);
15
16 this->declareAndLoadParameter("right_bound_line_width", right_bound_line_width_, "Width of the right boundary lines", true,
17 false, false);
18 this->declareAndLoadParameter("right_bound_color_hex", right_bound_color_hex_, "Color of the right boundary lines", true, false,
19 false);
20 this->declareAndLoadParameter("right_bound_line_opacity", right_bound_line_opacity_, "Opacity of the right boundary lines",
21 true, false, false);
22
23 this->declareAndLoadParameter("centerline_line_width", centerline_line_width_, "Width of the centerlines", true, false, false);
24 this->declareAndLoadParameter("centerline_color_hex", centerline_color_hex_, "Color of the centerlines", true, false, false);
25 this->declareAndLoadParameter("centerline_line_opacity", centerline_line_opacity_, "Opacity of the centerlines", true, false,
26 false);
27
28 this->declareAndLoadParameter("lanelet_text_scale", lanelet_text_scale_, "Scale of the lanelet ID text", true, false, false);
29 this->declareAndLoadParameter("lanelet_text_color", lanelet_text_color_hex_, "Color of the lanelet ID text", true, false,
30 false);
31 this->declareAndLoadParameter("lanelet_text_opacity", lanelet_text_opacity_, "Opacity of the lanelet ID text", true, false,
32 false);
33
34 this->declareAndLoadParameter("reference_line_width", reference_line_width_, "Width of the reference lines", true, false,
35 false);
36 this->declareAndLoadParameter("reference_line_color_hex", reference_line_color_hex_, "Color of the reference lines", true,
37 false, false);
38 this->declareAndLoadParameter("reference_line_opacity", reference_line_opacity_, "Opacity of the reference lines", true, false,
39 false);
40
41 this->declareAndLoadParameter("traffic_light_mesh_resource", traffic_light_mesh_resource_,
42 "Link to the traffic light model to use", true, false, false);
43 this->declareAndLoadParameter("traffic_light_scale", traffic_light_scale_, "Scale of the traffic lights models", true, false,
44 false);
45 this->declareAndLoadParameter("traffic_light_z_offset", traffic_light_z_offset_,
46 "Offset in z-direction of the traffic lights models (depends on model and scale)", true, false,
47 false);
48 this->declareAndLoadParameter("traffic_light_opacity", traffic_light_opacity_, "Opacity of the traffic lights", true, false,
49 false);
50
51 this->declareAndLoadParameter("yield_sign_mesh_resource", yield_sign_mesh_resource_, "Link to the yield sign model to use",
52 true, false, false);
53 this->declareAndLoadParameter("yield_sign_scale", yield_sign_scale_, "Scale of the yield sign models", true, false, false);
54 this->declareAndLoadParameter("yield_sign_z_offset", yield_sign_z_offset_,
55 "Offset in z-direction of the yield sign models (depends on model and scale)", true, false,
56 false);
57 this->declareAndLoadParameter("yield_sign_opacity", yield_sign_opacity_, "Opacity of the yield signs", true, false, false);
58
59 this->setup();
60}
61
62template <typename T>
64 T& param,
65 const std::string& description,
66 const bool add_to_auto_reconfigurable_params,
67 const bool is_required,
68 const bool read_only,
69 const std::optional<double>& from_value,
70 const std::optional<double>& to_value,
71 const std::optional<double>& step_value,
72 const std::string& additional_constraints) {
73 rcl_interfaces::msg::ParameterDescriptor param_desc;
74 param_desc.description = description;
75 param_desc.additional_constraints = additional_constraints;
76 param_desc.read_only = read_only;
77
78 auto type = rclcpp::ParameterValue(param).get_type();
79
80 if (from_value.has_value() && to_value.has_value()) {
81 if constexpr (std::is_integral_v<T>) {
82 rcl_interfaces::msg::IntegerRange range;
83 T step = static_cast<T>(step_value.has_value() ? step_value.value() : 1);
84 range.set__from_value(static_cast<T>(from_value.value())).set__to_value(static_cast<T>(to_value.value())).set__step(step);
85 param_desc.integer_range = {range};
86 } else if constexpr (std::is_floating_point_v<T>) {
87 rcl_interfaces::msg::FloatingPointRange range;
88 T step = static_cast<T>(step_value.has_value() ? step_value.value() : 1.0);
89 range.set__from_value(static_cast<T>(from_value.value())).set__to_value(static_cast<T>(to_value.value())).set__step(step);
90 param_desc.floating_point_range = {range};
91 } else {
92 RCLCPP_WARN(this->get_logger(), "Parameter type of parameter '%s' does not support specifying a range", name.c_str());
93 }
94 }
95
96 this->declare_parameter(name, type, param_desc);
97
98 try {
99 param = this->get_parameter(name).get_value<T>();
100 std::stringstream ss;
101 ss << "Loaded parameter '" << name << "': ";
102 if constexpr (is_vector_v<T>) {
103 ss << "[";
104 for (const auto& element : param) ss << element << (&element != &param.back() ? ", " : "");
105 ss << "]";
106 } else {
107 ss << param;
108 }
109 RCLCPP_INFO_STREAM(this->get_logger(), ss.str());
110 } catch (rclcpp::exceptions::ParameterUninitializedException&) {
111 if (is_required) {
112 RCLCPP_FATAL_STREAM(this->get_logger(), "Missing required parameter '" << name << "', exiting");
113 exit(EXIT_FAILURE);
114 } else {
115 std::stringstream ss;
116 ss << "Missing parameter '" << name << "', using default value: ";
117 if constexpr (is_vector_v<T>) {
118 ss << "[";
119 for (const auto& element : param) ss << element << (&element != &param.back() ? ", " : "");
120 ss << "]";
121 } else {
122 ss << param;
123 }
124 RCLCPP_WARN_STREAM(this->get_logger(), ss.str());
125 this->set_parameters({rclcpp::Parameter(name, rclcpp::ParameterValue(param))});
126 }
127 }
128
129 if (add_to_auto_reconfigurable_params) {
130 std::function<void(const rclcpp::Parameter&)> setter = [&param](const rclcpp::Parameter& p) { param = p.get_value<T>(); };
131 auto_reconfigurable_params_.push_back(std::make_tuple(name, setter));
132 }
133}
134
135rcl_interfaces::msg::SetParametersResult Lanelet2LichtblickDisplay::parametersCallback(
136 const std::vector<rclcpp::Parameter>& parameters) {
137 for (const auto& param : parameters) {
138 for (auto& auto_reconfigurable_param : auto_reconfigurable_params_) {
139 if (param.get_name() == std::get<0>(auto_reconfigurable_param)) {
140 std::get<1>(auto_reconfigurable_param)(param);
141 RCLCPP_INFO(this->get_logger(), "Reconfigured parameter '%s'", param.get_name().c_str());
142 need_republish_.store(true);
143 break;
144 }
145 }
146 }
147
148 rcl_interfaces::msg::SetParametersResult result;
149 result.successful = true;
150
151 return result;
152}
153
155 // callback for dynamic parameter configuration
156 parameters_callback_ = this->add_on_set_parameters_callback(
157 std::bind(&Lanelet2LichtblickDisplay::parametersCallback, this, std::placeholders::_1));
158
159 // publisher for visualization markers
160 auto qos = rclcpp::QoS(rclcpp::KeepLast(1)).transient_local();
161 marker_array_publisher_ = this->create_publisher<visualization_msgs::msg::MarkerArray>("~/lichtblick_lanelet2_map", qos);
162 RCLCPP_INFO(this->get_logger(), "Publishing to '%s' with transient_local QoS", marker_array_publisher_->get_topic_name());
163
164 lanelet2_map_interface_ = std::make_shared<Lanelet2MapInterface>(*this, "lanelet2_map_server");
165
166 // periodically check for map updates
167 timer_ = this->create_wall_timer(std::chrono::milliseconds(2000), std::bind(&Lanelet2LichtblickDisplay::checkMapStatus, this));
168}
169
170void Lanelet2LichtblickDisplay::hexToRgb(const std::string& hex, float& r, float& g, float& b) {
171 std::string clean_hex = hex;
172 if (clean_hex.rfind("0x", 0) == 0) {
173 clean_hex = clean_hex.substr(2);
174 } else if (clean_hex.rfind("#", 0) == 0) {
175 clean_hex = clean_hex.substr(1);
176 }
177 if (clean_hex.length() != 6) {
178 RCLCPP_WARN(this->get_logger(), "Invalid hex color string: %s. Defaulting to black.", hex.c_str());
179 r = g = b = 0.0F;
180 return;
181 }
182
183 unsigned int r_int = 0U;
184 unsigned int g_int = 0U;
185 unsigned int b_int = 0U;
186
187 std::istringstream rs(clean_hex.substr(0, 2));
188 rs >> std::hex >> r_int;
189 r = static_cast<float>(r_int) / 255.0F;
190
191 std::istringstream gs(clean_hex.substr(2, 2));
192 gs >> std::hex >> g_int;
193 g = static_cast<float>(g_int) / 255.0F;
194
195 std::istringstream bs(clean_hex.substr(4, 2));
196 bs >> std::hex >> b_int;
197 b = static_cast<float>(b_int) / 255.0F;
198}
199
200geometry_msgs::msg::Point Lanelet2LichtblickDisplay::toRos(const Eigen::Vector3d& point) {
201 geometry_msgs::msg::Point ros_point;
202 ros_point.x = point.x();
203 ros_point.y = point.y();
204 ros_point.z = point.z();
205 return ros_point;
206}
207
209 visualization_msgs::msg::MarkerArray marker_array_msg;
210 visualization_msgs::msg::Marker delete_marker;
211
212 delete_marker.header.frame_id = map_frame_id;
213 delete_marker.header.stamp = this->now();
214 delete_marker.action = visualization_msgs::msg::Marker::DELETEALL;
215
216 marker_array_msg.markers.push_back(delete_marker);
217 marker_array_publisher_->publish(marker_array_msg);
218}
219
220void Lanelet2LichtblickDisplay::addLineStripMarker(visualization_msgs::msg::MarkerArray& marker_array_msg,
221 int& current_marker_id,
222 const std::string& ns,
223 double line_width,
224 const std::string& color_hex,
225 double opacity,
226 const std_msgs::msg::Header& header,
227 const std::vector<geometry_msgs::msg::Point>& points) {
228 if (points.empty()) {
229 return;
230 }
231
232 visualization_msgs::msg::Marker marker;
233 marker.header = header;
234 marker.ns = ns;
235 marker.id = current_marker_id++; // give every marker a unique ID
236 marker.lifetime = rclcpp::Duration(std::chrono::milliseconds(0)); // indefinite since QoS is set to transient local
237 marker.type = visualization_msgs::msg::Marker::LINE_STRIP;
238 marker.action = visualization_msgs::msg::Marker::ADD;
239 marker.scale.x = line_width;
240
241 float r = 0.0F;
242 float g = 0.0F;
243 float b = 0.0F;
244 hexToRgb(color_hex, r, g, b);
245 marker.color.r = r;
246 marker.color.g = g;
247 marker.color.b = b;
248 marker.color.a = static_cast<float>(opacity);
249 marker.points = points;
250
251 marker_array_msg.markers.push_back(marker);
252}
253
254void Lanelet2LichtblickDisplay::addMeshMarker(visualization_msgs::msg::MarkerArray& marker_array_msg,
255 int& current_marker_id,
256 const std::string& ns,
257 const std::string& mesh_resource,
258 const geometry_msgs::msg::Point& position,
259 double scale,
260 double z_offset,
261 double opacity,
262 const std_msgs::msg::Header& header,
263 double yaw_ref_line,
264 const geometry_msgs::msg::Point& middle_point) {
265 visualization_msgs::msg::Marker marker;
266 marker.header = header;
267 marker.ns = ns;
268 marker.id = current_marker_id++; // give every marker a unique ID
269 marker.lifetime = rclcpp::Duration(std::chrono::milliseconds(0)); // indefinite since QoS is set to transient local
270 marker.action = visualization_msgs::msg::Marker::ADD;
271 marker.type = visualization_msgs::msg::Marker::MESH_RESOURCE;
272 marker.mesh_resource = mesh_resource;
273 // uniform scale
274 marker.scale.x = scale;
275 marker.scale.y = scale;
276 marker.scale.z = scale;
277
278 // set the position of the mesh marker (depends on scale and model)
279 geometry_msgs::msg::Point adjusted_position = position;
280 adjusted_position.z = z_offset;
281 marker.pose.position = adjusted_position;
282
283 // calculate orientation orthogonal to the reference line
284 // 1. need vector from traffic sign to the reference line
285 double dx_to_ref = middle_point.x - position.x;
286 double dy_to_ref = middle_point.y - position.y;
287 double yaw_to_ref = std::atan2(dy_to_ref, dx_to_ref);
288 // 2. two possible perpendicular orientations to the reference line
289 double cand1 = yaw_ref_line + M_PI_2; // +90°
290 double cand2 = yaw_ref_line - M_PI_2; // -90°
291 // measure angular difference (normalized) using atan2(sin,cos)
292 double d1 = std::abs(std::atan2(std::sin(yaw_to_ref - cand1), std::cos(yaw_to_ref - cand1)));
293 double d2 = std::abs(std::atan2(std::sin(yaw_to_ref - cand2), std::cos(yaw_to_ref - cand2)));
294 double final_yaw = (d1 < d2) ? cand1 : cand2;
295
296 tf2::Quaternion q;
297 q.setRPY(0, 0, final_yaw + M_PI_2);
298 marker.pose.orientation = tf2::toMsg(q);
299
300 marker.mesh_use_embedded_materials = true;
301 marker.color.a = static_cast<float>(opacity);
302
303 marker_array_msg.markers.push_back(marker);
304}
305
306std::optional<std::array<geometry_msgs::msg::Point, 2>> Lanelet2LichtblickDisplay::regulatoryElementReferenceLine(
307 const std::shared_ptr<const lanelet::RegulatoryElement>& regulatory_element) {
308 const std::vector<lanelet::ConstLineString3d> reference_lines =
309 regulatory_element->getParameters<lanelet::ConstLineString3d>(lanelet::RoleName::RefLine);
310 if (reference_lines.empty()) {
311 return std::nullopt;
312 }
313 const std::vector<Eigen::Vector3d> reference_line = reference_lines.front().basicLineString();
314 if (reference_line.size() < 2) {
315 return std::nullopt;
316 }
317 std::array<geometry_msgs::msg::Point, 2> reference_line_ros = {toRos(reference_line.front()), toRos(reference_line.back())};
318 return reference_line_ros;
319}
320
321std::vector<geometry_msgs::msg::Point> Lanelet2LichtblickDisplay::regulatoryElementPositions(
322 const std::shared_ptr<const lanelet::RegulatoryElement>& regulatory_element) {
323 std::vector<geometry_msgs::msg::Point> positions;
324 const std::vector<lanelet::ConstLineString3d> sign_lines =
325 regulatory_element->getParameters<lanelet::ConstLineString3d>(lanelet::RoleName::Refers);
326 for (const auto& const_sign_line : sign_lines) {
327 const std::vector<Eigen::Vector3d> sign_line = const_sign_line.basicLineString();
328 if (!sign_line.empty()) {
329 positions.push_back(toRos(sign_line.front()));
330 }
331 }
332 return positions;
333}
334
336 lanelet::LaneletMapConstPtr current_map_ptr = lanelet2_map_interface_->getMapPtr();
337
338 // Check if the map has been loaded or if a new map has been received
339 if (current_map_ptr && current_map_ptr != last_map_ptr_) {
340 RCLCPP_INFO(this->get_logger(), "New Lanelet2 map detected. Publishing markers...");
341
342 map_frame_id = lanelet2_map_interface_->map_frame_id_;
343 this->clearAllMarkers();
344
345 this->publishMarker(current_map_ptr);
346 last_map_ptr_ = current_map_ptr;
347 need_republish_.store(false); // cleared after publish
348 }
349
350 // Check if parameters changed
351 if (need_republish_.load() && last_map_ptr_) {
352 RCLCPP_INFO(this->get_logger(), "Parameters changed. Republishing markers...");
354 need_republish_.store(false);
355 }
356}
357
358void Lanelet2LichtblickDisplay::publishMarker(const lanelet::LaneletMapConstPtr& lanelet_map) {
359 if (lanelet_map->laneletLayer.empty()) {
360 RCLCPP_WARN(this->get_logger(), "Lanelet2 map is empty.");
361 return;
362 }
363 visualization_msgs::msg::MarkerArray marker_array_msg;
364 int current_marker_id = 0;
365 rclcpp::Time current_timestamp = this->now();
366
367 std_msgs::msg::Header header;
368 header.frame_id = map_frame_id;
369 header.stamp = current_timestamp;
370 float r = 0.0F;
371 float g = 0.0F;
372 float b = 0.0F;
373
374 // Iterate through all lanelets in the map
375 for (const auto& lanelet : lanelet_map->laneletLayer) {
376 // --- Visualize Lines ---
377 std::vector<geometry_msgs::msg::Point> left_points, right_points, centerline_points;
378 for (const auto& p : lanelet.leftBound()) left_points.push_back(toRos(p.basicPoint()));
379 for (const auto& p : lanelet.rightBound()) right_points.push_back(toRos(p.basicPoint()));
380 for (const auto& p : lanelet.centerline()) centerline_points.push_back(toRos(p.basicPoint()));
381
382 addLineStripMarker(marker_array_msg, current_marker_id, "lanelet_left_boundaries", left_bound_line_width_,
383 left_bound_color_hex_, left_bound_line_opacity_, header, left_points);
384 addLineStripMarker(marker_array_msg, current_marker_id, "lanelet_right_boundaries", right_bound_line_width_,
386 addLineStripMarker(marker_array_msg, current_marker_id, "lanelet_centerlines", centerline_line_width_, centerline_color_hex_,
387 centerline_line_opacity_, header, centerline_points);
388
389 // --- Visualize Lanelet ID Text ---
390 if (!lanelet.centerline().empty()) {
391 visualization_msgs::msg::Marker text_marker;
392 text_marker.header.frame_id = map_frame_id;
393 text_marker.header.stamp = current_timestamp;
394 text_marker.ns = "lanelet_ids";
395 text_marker.id = current_marker_id++;
396 text_marker.action = visualization_msgs::msg::Marker::ADD;
397 text_marker.lifetime = rclcpp::Duration(std::chrono::milliseconds(0));
398 text_marker.type = visualization_msgs::msg::Marker::TEXT_VIEW_FACING;
399
400 // position the text at the geometric center of the lanelet's centerline
401 geometry_msgs::msg::Point centerline_center;
402 double sum_x = 0.0, sum_y = 0.0, sum_z = 0.0;
403 for (const auto& p : lanelet.centerline()) {
404 sum_x += p.x();
405 sum_y += p.y();
406 sum_z += p.z();
407 }
408 if (!lanelet.centerline().empty()) {
409 const auto centerline_size = static_cast<double>(lanelet.centerline().size());
410 centerline_center.x = sum_x / centerline_size;
411 centerline_center.y = sum_y / centerline_size;
412 centerline_center.z = sum_z / centerline_size;
413 }
414 text_marker.pose.position = centerline_center;
415 text_marker.scale.z = lanelet_text_scale_; // For TEXT_VIEW_FACING, scale.z controls the character height
416
418 text_marker.color.r = r;
419 text_marker.color.g = g;
420 text_marker.color.b = b;
421 text_marker.color.a = static_cast<float>(lanelet_text_opacity_);
422
423 text_marker.text = std::to_string(lanelet.id());
424
425 marker_array_msg.markers.push_back(text_marker);
426 }
427
428 const auto& regulatory_elements = lanelet.regulatoryElements();
429 for (const auto& regulatory_element : regulatory_elements) {
430 // Check if the element is a reference line
431 if (auto reference_line = regulatoryElementReferenceLine(regulatory_element)) {
432 std::vector<geometry_msgs::msg::Point> ref_points = {reference_line->at(0), reference_line->at(1)};
433 addLineStripMarker(marker_array_msg, current_marker_id, "reference_lines", reference_line_width_,
435
436 // Common regulatory element logic
437 // https://github.com/fzi-forschungszentrum-informatik/Lanelet2/blob/master/lanelet2_core/doc/RegulatoryElementTagging.md
438 std::string subtype = regulatory_element->attribute("subtype").value();
439 double dx = reference_line->at(1).x - reference_line->at(0).x;
440 double dy = reference_line->at(1).y - reference_line->at(0).y;
441 double yaw_ref_line = std::atan2(dy, dx);
442 geometry_msgs::msg::Point middle_point;
443 middle_point.x = (reference_line->at(0).x + reference_line->at(1).x) / 2.0;
444 middle_point.y = (reference_line->at(0).y + reference_line->at(1).y) / 2.0;
445
446 std::vector<geometry_msgs::msg::Point> positions = regulatoryElementPositions(regulatory_element);
447 for (const auto& p : positions) {
448 if (subtype == "traffic_light") {
449 addMeshMarker(marker_array_msg, current_marker_id, "traffic_lights", traffic_light_mesh_resource_, p,
451 middle_point);
452 } else if (subtype == "right_of_way") {
453 addMeshMarker(marker_array_msg, current_marker_id, "yield_signs", yield_sign_mesh_resource_, p, yield_sign_scale_,
454 yield_sign_z_offset_, yield_sign_opacity_, header, yaw_ref_line, middle_point);
455 }
456 }
457 }
458 }
459 }
460
461 // Publish the marker array
462 marker_array_publisher_->publish(marker_array_msg);
463}
464
465} // namespace lanelet2_lichtblick_display
466
474int main(int argc, char* argv[]) {
475 rclcpp::init(argc, argv);
476 rclcpp::spin(std::make_shared<lanelet2_lichtblick_display::Lanelet2LichtblickDisplay>());
477 rclcpp::shutdown();
478
479 return 0;
480}
static void addMeshMarker(visualization_msgs::msg::MarkerArray &marker_array_msg, int &current_marker_id, const std::string &ns, const std::string &mesh_resource, const geometry_msgs::msg::Point &position, double scale, double z_offset, double opacity, const std_msgs::msg::Header &header, double yaw_ref_line, const geometry_msgs::msg::Point &middle_point)
Creates and adds a MESH_RESOURCE marker for regulatory elements.
void checkMapStatus()
Check for a new or changed Lanelet2 map and publish markers if present.
static geometry_msgs::msg::Point toRos(const Eigen::Vector3d &point)
Converts an Eigen::Vector3d into a ROS geometry_msgs::msg::Point.
void hexToRgb(const std::string &hex, float &r, float &g, float &b)
Convert a hex color string into normalized RGB float components.
void clearAllMarkers()
Publishes a DELETEALL action to clear all markers from the topic.
void publishMarker(const lanelet::LaneletMapConstPtr &lanelet_map)
Converts a Lanelet2 map into a MarkerArray and publishes it.
Lanelet2LichtblickDisplay()
Creates the Lichtblick visualization node and declares its rendering parameters.
static std::optional< std::array< geometry_msgs::msg::Point, 2 > > regulatoryElementReferenceLine(const std::shared_ptr< const lanelet::RegulatoryElement > &regulatory_element)
Extracts the reference/effect line of a regulatory element.
OnSetParametersCallbackHandle::SharedPtr parameters_callback_
Callback handle for dynamic parameter reconfiguration.
void addLineStripMarker(visualization_msgs::msg::MarkerArray &marker_array_msg, int &current_marker_id, const std::string &ns, double line_width, const std::string &color_hex, double opacity, const std_msgs::msg::Header &header, const std::vector< geometry_msgs::msg::Point > &points)
Creates and adds a LINE_STRIP marker to a marker array.
static std::vector< geometry_msgs::msg::Point > regulatoryElementPositions(const std::shared_ptr< const lanelet::RegulatoryElement > &regulatory_element)
Extracts the sign/signal positions of a regulatory element.
rcl_interfaces::msg::SetParametersResult parametersCallback(const std::vector< rclcpp::Parameter > &parameters)
Handles reconfiguration when a parameter value is changed.
void setup()
Sets up subscribers, publishers, etc. to configure the node.
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 and loads a ROS parameter.
rclcpp::Publisher< visualization_msgs::msg::MarkerArray >::SharedPtr marker_array_publisher_
Publisher Marker Array.
std::vector< std::tuple< std::string, std::function< void(const rclcpp::Parameter &)> > > auto_reconfigurable_params_
Auto-reconfigurable parameters for dynamic reconfiguration.
int main(int argc, char *argv[])
Starts the Lanelet2 Lichtblick display node.