42 "Link to the traffic light model to use",
true,
false,
false);
46 "Offset in z-direction of the traffic lights models (depends on model and scale)",
true,
false,
55 "Offset in z-direction of the yield sign models (depends on model and scale)",
true,
false,
65 const std::string& description,
66 const bool add_to_auto_reconfigurable_params,
67 const bool is_required,
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;
78 auto type = rclcpp::ParameterValue(param).get_type();
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};
92 RCLCPP_WARN(this->get_logger(),
"Parameter type of parameter '%s' does not support specifying a range", name.c_str());
96 this->declare_parameter(name, type, param_desc);
99 param = this->get_parameter(name).get_value<T>();
100 std::stringstream ss;
101 ss <<
"Loaded parameter '" << name <<
"': ";
104 for (
const auto& element : param) ss << element << (&element != ¶m.back() ?
", " :
"");
109 RCLCPP_INFO_STREAM(this->get_logger(), ss.str());
110 }
catch (rclcpp::exceptions::ParameterUninitializedException&) {
112 RCLCPP_FATAL_STREAM(this->get_logger(),
"Missing required parameter '" << name <<
"', exiting");
115 std::stringstream ss;
116 ss <<
"Missing parameter '" << name <<
"', using default value: ";
119 for (
const auto& element : param) ss << element << (&element != ¶m.back() ?
", " :
"");
124 RCLCPP_WARN_STREAM(this->get_logger(), ss.str());
125 this->set_parameters({rclcpp::Parameter(name, rclcpp::ParameterValue(param))});
129 if (add_to_auto_reconfigurable_params) {
130 std::function<void(
const rclcpp::Parameter&)> setter = [¶m](
const rclcpp::Parameter& p) { param = p.get_value<T>(); };
136 const std::vector<rclcpp::Parameter>& parameters) {
137 for (
const auto& param : parameters) {
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());
148 rcl_interfaces::msg::SetParametersResult result;
149 result.successful =
true;
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());
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);
177 if (clean_hex.length() != 6) {
178 RCLCPP_WARN(this->get_logger(),
"Invalid hex color string: %s. Defaulting to black.", hex.c_str());
183 unsigned int r_int = 0U;
184 unsigned int g_int = 0U;
185 unsigned int b_int = 0U;
187 std::istringstream rs(clean_hex.substr(0, 2));
188 rs >> std::hex >> r_int;
189 r =
static_cast<float>(r_int) / 255.0F;
191 std::istringstream gs(clean_hex.substr(2, 2));
192 gs >> std::hex >> g_int;
193 g =
static_cast<float>(g_int) / 255.0F;
195 std::istringstream bs(clean_hex.substr(4, 2));
196 bs >> std::hex >> b_int;
197 b =
static_cast<float>(b_int) / 255.0F;
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();
209 visualization_msgs::msg::MarkerArray marker_array_msg;
210 visualization_msgs::msg::Marker delete_marker;
213 delete_marker.header.stamp = this->now();
214 delete_marker.action = visualization_msgs::msg::Marker::DELETEALL;
216 marker_array_msg.markers.push_back(delete_marker);
221 int& current_marker_id,
222 const std::string& ns,
224 const std::string& color_hex,
226 const std_msgs::msg::Header& header,
227 const std::vector<geometry_msgs::msg::Point>& points) {
228 if (points.empty()) {
232 visualization_msgs::msg::Marker marker;
233 marker.header = header;
235 marker.id = current_marker_id++;
236 marker.lifetime = rclcpp::Duration(std::chrono::milliseconds(0));
237 marker.type = visualization_msgs::msg::Marker::LINE_STRIP;
238 marker.action = visualization_msgs::msg::Marker::ADD;
239 marker.scale.x = line_width;
248 marker.color.a =
static_cast<float>(opacity);
249 marker.points = points;
251 marker_array_msg.markers.push_back(marker);
255 int& current_marker_id,
256 const std::string& ns,
257 const std::string& mesh_resource,
258 const geometry_msgs::msg::Point& position,
262 const std_msgs::msg::Header& header,
264 const geometry_msgs::msg::Point& middle_point) {
265 visualization_msgs::msg::Marker marker;
266 marker.header = header;
268 marker.id = current_marker_id++;
269 marker.lifetime = rclcpp::Duration(std::chrono::milliseconds(0));
270 marker.action = visualization_msgs::msg::Marker::ADD;
271 marker.type = visualization_msgs::msg::Marker::MESH_RESOURCE;
272 marker.mesh_resource = mesh_resource;
274 marker.scale.x = scale;
275 marker.scale.y = scale;
276 marker.scale.z = scale;
279 geometry_msgs::msg::Point adjusted_position = position;
280 adjusted_position.z = z_offset;
281 marker.pose.position = adjusted_position;
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);
289 double cand1 = yaw_ref_line + M_PI_2;
290 double cand2 = yaw_ref_line - M_PI_2;
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;
297 q.setRPY(0, 0, final_yaw + M_PI_2);
298 marker.pose.orientation = tf2::toMsg(q);
300 marker.mesh_use_embedded_materials =
true;
301 marker.color.a =
static_cast<float>(opacity);
303 marker_array_msg.markers.push_back(marker);
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()) {
313 const std::vector<Eigen::Vector3d> reference_line = reference_lines.front().basicLineString();
314 if (reference_line.size() < 2) {
317 std::array<geometry_msgs::msg::Point, 2> reference_line_ros = {
toRos(reference_line.front()),
toRos(reference_line.back())};
318 return reference_line_ros;
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()));
340 RCLCPP_INFO(this->get_logger(),
"New Lanelet2 map detected. Publishing markers...");
352 RCLCPP_INFO(this->get_logger(),
"Parameters changed. Republishing markers...");
359 if (lanelet_map->laneletLayer.empty()) {
360 RCLCPP_WARN(this->get_logger(),
"Lanelet2 map is empty.");
363 visualization_msgs::msg::MarkerArray marker_array_msg;
364 int current_marker_id = 0;
367 std_msgs::msg::Header header;
375 for (
const auto& lanelet : lanelet_map->laneletLayer) {
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()));
390 if (!lanelet.centerline().empty()) {
391 visualization_msgs::msg::Marker text_marker;
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;
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()) {
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;
414 text_marker.pose.position = centerline_center;
418 text_marker.color.r = r;
419 text_marker.color.g = g;
420 text_marker.color.b = b;
423 text_marker.text = std::to_string(lanelet.id());
425 marker_array_msg.markers.push_back(text_marker);
428 const auto& regulatory_elements = lanelet.regulatoryElements();
429 for (
const auto& regulatory_element : regulatory_elements) {
432 std::vector<geometry_msgs::msg::Point> ref_points = {reference_line->at(0), reference_line->at(1)};
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;
447 for (
const auto& p : positions) {
448 if (subtype ==
"traffic_light") {
452 }
else if (subtype ==
"right_of_way") {
474int main(
int argc,
char* argv[]) {
475 rclcpp::init(argc, argv);
476 rclcpp::spin(std::make_shared<lanelet2_lichtblick_display::Lanelet2LichtblickDisplay>());
double traffic_light_scale_
static void addMeshMarker(visualization_msgs::msg::MarkerArray &marker_array_msg, int ¤t_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.
double left_bound_line_width_
double yield_sign_opacity_
std::string centerline_color_hex_
void checkMapStatus()
Check for a new or changed Lanelet2 map and publish markers if present.
std::string lanelet_text_color_hex_
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.
std::string yield_sign_mesh_resource_
double yield_sign_z_offset_
Lanelet2LichtblickDisplay()
Creates the Lichtblick visualization node and declares its rendering parameters.
double reference_line_width_
double traffic_light_opacity_
static std::optional< std::array< geometry_msgs::msg::Point, 2 > > regulatoryElementReferenceLine(const std::shared_ptr< const lanelet::RegulatoryElement > ®ulatory_element)
Extracts the reference/effect line of a regulatory element.
double traffic_light_z_offset_
std::shared_ptr< Lanelet2MapInterface > lanelet2_map_interface_
OnSetParametersCallbackHandle::SharedPtr parameters_callback_
Callback handle for dynamic parameter reconfiguration.
void addLineStripMarker(visualization_msgs::msg::MarkerArray &marker_array_msg, int ¤t_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.
rclcpp::TimerBase::SharedPtr timer_
std::string left_bound_color_hex_
static std::vector< geometry_msgs::msg::Point > regulatoryElementPositions(const std::shared_ptr< const lanelet::RegulatoryElement > ®ulatory_element)
Extracts the sign/signal positions of a regulatory element.
double centerline_line_width_
double reference_line_opacity_
std::atomic< bool > need_republish_
lanelet::LaneletMapConstPtr last_map_ptr_
rcl_interfaces::msg::SetParametersResult parametersCallback(const std::vector< rclcpp::Parameter > ¶meters)
Handles reconfiguration when a parameter value is changed.
void setup()
Sets up subscribers, publishers, etc. to configure the node.
double lanelet_text_opacity_
void declareAndLoadParameter(const std::string &name, T ¶m, 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.
double right_bound_line_opacity_
double right_bound_line_width_
rclcpp::Publisher< visualization_msgs::msg::MarkerArray >::SharedPtr marker_array_publisher_
Publisher Marker Array.
std::string reference_line_color_hex_
rclcpp::Time current_timestamp
std::string traffic_light_mesh_resource_
std::string right_bound_color_hex_
double left_bound_line_opacity_
double lanelet_text_scale_
double centerline_line_opacity_
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.
constexpr bool is_vector_v