Carma-platform v4.2.0
CARMA Platform is built on robot operating system (ROS) and utilizes open source software (OSS) that enables Cooperative Driving Automation (CDA) features to allow Automated Driving Systems to interact and cooperate with infrastructure and other vehicles through communication.
mobilitypath_visualizer.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2019-2022 LEIDOS.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
15 */
16
18
20
21 namespace std_ph = std::placeholders;
22
23 namespace
24 {
25 // Helper function for computing 2d distance
26 double compute_2d_distance(const geometry_msgs::msg::Point& pt1, const geometry_msgs::msg::Point& pt2)
27 {
28 double dx = pt2.x - pt1.x;
29 double dy = pt2.y - pt1.y;
30 double dz = pt2.z - pt1.z;
31
32 return sqrt(dx * dx + dy * dy + dz * dz);
33 }
34 }
35
36 MobilityPathVisualizer::MobilityPathVisualizer(const rclcpp::NodeOptions &options)
37 : carma_ros2_utils::CarmaLifecycleNode(options)
38 {
39 // Create initial config
40 config_ = Config();
41
42 // Declare parameters
43 config_.timer_cb_rate = declare_parameter<int>("timer_cb_rate", config_.timer_cb_rate);
44 config_.x = declare_parameter<double>("x", config_.x);
45 config_.y = declare_parameter<double>("y", config_.y);
46 config_.z = declare_parameter<double>("z", config_.z);
47 config_.t = declare_parameter<double>("t", config_.t);
48 config_.host_id = declare_parameter<std::string>("vehicle_id", config_.host_id);
49 }
50
51
52 carma_ros2_utils::CallbackReturn MobilityPathVisualizer::handle_on_configure(const rclcpp_lifecycle::State &)
53 {
54 //Reset Config
55 config_ = Config();
56
57 //Load parameters
58 get_parameter<int>("timer_cb_rate", config_.timer_cb_rate);
59 get_parameter<double>("x", config_.x);
60 get_parameter<double>("y", config_.y);
61 get_parameter<double>("z", config_.z);
62 get_parameter<double>("t", config_.t);
63
64 get_parameter("vehicle_id", config_.host_id);
65
66 RCLCPP_INFO_STREAM(get_logger(), "Loaded params "<< config_);
67
68 // init publishers
69 host_marker_pub_ = create_publisher<visualization_msgs::msg::MarkerArray>("host_marker", 1);
70 cav_marker_pub_ = create_publisher<visualization_msgs::msg::MarkerArray>("cav_marker", 1);
71 label_marker_pub_ = create_publisher<visualization_msgs::msg::MarkerArray>("label_marker", 1);
72
73 // init subscribers
74 host_mob_path_sub_ = create_subscription<carma_v2x_msgs::msg::MobilityPath>("mobility_path_msg", 10,
75 std::bind(&MobilityPathVisualizer::callbackMobilityPath, this, std_ph::_1));
76
77 cav_mob_path_sub_ = create_subscription<carma_v2x_msgs::msg::MobilityPath>("incoming_mobility_path", 10,
78 std::bind(&MobilityPathVisualizer::callbackMobilityPath, this, std_ph::_1));
79
80 georeference_sub_ = create_subscription<std_msgs::msg::String>("georeference", 10,
81 std::bind(&MobilityPathVisualizer::georeferenceCallback, this, std_ph::_1));
82
83 // Return success if everthing initialized successfully
84 return CallbackReturn::SUCCESS;
85 }
86
87 carma_ros2_utils::CallbackReturn MobilityPathVisualizer::handle_on_activate(const rclcpp_lifecycle::State &){
88
89 //Setup timer
90 timer_ = create_timer(get_clock(), std::chrono::milliseconds(config_.timer_cb_rate),
92
93 return CallbackReturn::SUCCESS;
94 }
95
97 {
98 // match cav_markers' timestamps to that of host
100 return;
101 }
102
103 // publish host marker
105
107
108 for (auto const &marker: cav_markers_)
109 {
110 cav_marker_pub_->publish(marker);
111 }
112
113 // publish label
116
117 }
118
119 void MobilityPathVisualizer::georeferenceCallback(std_msgs::msg::String::UniquePtr msg)
120 {
121 if (georeference_ != msg->data)
122 {
123 georeference_ = msg->data;
124 map_projector_ = std::make_shared<lanelet::projection::LocalFrameProjector>(msg->data.c_str()); // Build projector from proj string
125 }
126 }
127
128 void MobilityPathVisualizer::callbackMobilityPath(carma_v2x_msgs::msg::MobilityPath::UniquePtr msg)
129 {
130
131 RCLCPP_DEBUG_STREAM(get_logger(), "Received a msg from sender: " << msg->m_header.sender_id << ", and plan id:" << msg->m_header.plan_id << ", for receiver:"
132 << msg->m_header.recipient_id << ", time:" << msg->m_header.timestamp << ", at now: " << std::to_string(this->now().seconds()));
133
134 if (latest_cav_mob_path_msg_.find(msg->m_header.sender_id) != latest_cav_mob_path_msg_.end() &&
135 msg->m_header.plan_id.compare(latest_cav_mob_path_msg_[msg->m_header.sender_id].m_header.plan_id) == 0 &&
136 msg->m_header.timestamp == latest_cav_mob_path_msg_[msg->m_header.sender_id].m_header.timestamp)
137 {
138 RCLCPP_DEBUG_STREAM(get_logger(), "Already received this plan id:" << msg->m_header.plan_id << "from sender_id: " << msg->m_header.sender_id);
139 return;
140 }
141 latest_cav_mob_path_msg_[msg->m_header.sender_id] = *msg;
142
143 if (!map_projector_) {
144 RCLCPP_ERROR(get_logger(), "Cannot visualize mobility path as map projection not yet available");
145 return;
146 }
147
148 if (msg->m_header.timestamp == 0) //if empty
149 {
150 host_marker_received_ = false;
151 return;
152 }
153
154 MarkerColor cav_color;
155 if (msg->m_header.sender_id.compare(config_.host_id) == 0)
156 {
157 cav_color.green = 1.0;
158
161
162 RCLCPP_DEBUG_STREAM(get_logger(), "Composed host marker successfuly!");
163 }
164 else
165 {
166
167 cav_color.blue = 1.0;
168 cav_markers_.push_back(composeVisualizationMarker(*msg,cav_color));
169
170 RCLCPP_DEBUG_STREAM(get_logger(), "Composed cav marker successfuly! with sender_id: " << msg->m_header.sender_id);
171 }
172
173 }
174
175 visualization_msgs::msg::MarkerArray MobilityPathVisualizer::composeVisualizationMarker(const carma_v2x_msgs::msg::MobilityPath& msg, const MarkerColor& color)
176 {
177 visualization_msgs::msg::MarkerArray output;
178
179 visualization_msgs::msg::Marker marker;
180 marker.header.frame_id = "map";
181
182 rclcpp::Time marker_header_stamp = rclcpp::Time((msg.m_header.timestamp/1000.0) * 1e9);
183 marker.header.stamp = builtin_interfaces::msg::Time(marker_header_stamp);
184 marker.type = visualization_msgs::msg::Marker::ARROW;
185 marker.action = visualization_msgs::msg::Marker::ADD;
186 marker.ns = "mobilitypath_visualizer";
187
188 marker.scale.x = config_.x;
189 marker.scale.y = config_.y;
190 marker.scale.z = config_.z;
191 marker.frame_locked = true;
192
193 marker.color.r = (float)color.red;
194 marker.color.g = (float)color.green;
195 marker.color.b = (float)color.blue;
196 marker.color.a = 1.0f;
197
198 size_t count = std::max(prev_marker_list_size_[msg.m_header.sender_id], msg.trajectory.offsets.size());
199
200 auto curr_location_msg = msg; //variable to update on each iteration as offsets are measured since last traj point
201
202 marker.id = 0;
203 geometry_msgs::msg::Point arrow_start;
204 geometry_msgs::msg::Point arrow_end;
205
206 if (msg.trajectory.offsets.empty())
207 {
208 marker.action = visualization_msgs::msg::Marker::DELETE;
209 }
210 else
211 {
212 RCLCPP_DEBUG_STREAM(get_logger(), "ECEF point x: " << curr_location_msg.trajectory.location.ecef_x << ", y:" << curr_location_msg.trajectory.location.ecef_y);
213 arrow_start = ECEFToMapPoint(curr_location_msg.trajectory.location); //also convert from cm to m
214 RCLCPP_DEBUG_STREAM(get_logger(), "Map point x: " << arrow_start.x << ", y:" << arrow_start.y);
215
216 curr_location_msg.trajectory.location.ecef_x += + msg.trajectory.offsets[0].offset_x;
217 curr_location_msg.trajectory.location.ecef_y += + msg.trajectory.offsets[0].offset_y;
218 curr_location_msg.trajectory.location.ecef_z += + msg.trajectory.offsets[0].offset_z;
219 arrow_end = ECEFToMapPoint(curr_location_msg.trajectory.location); //also convert from cm to m
220
221 marker.points.push_back(arrow_start);
222 marker.points.push_back(arrow_end);
223 }
224
225 output.markers.push_back(marker);
226
227 for (size_t i = 1; i < count; i++) // start id = 1 to comply with arrow marker type
228 {
229 marker.id = i;
230 rclcpp::Time marker_cur_time(marker.header.stamp);
231 //Update time by 0.1s
232 rclcpp::Time updated_time = marker_cur_time + rclcpp::Duration(0.1 * 1e9);
233 marker.header.stamp = builtin_interfaces::msg::Time(updated_time);
234
235 if (i >= msg.trajectory.offsets.size()) { // If we need to delete previous points
236 marker.action = visualization_msgs::msg::Marker::DELETE;
237 output.markers.push_back(marker);
238 continue;
239 }
240
241 marker.points = {};
242 RCLCPP_DEBUG_STREAM(get_logger(), "ECEF Point- DEBUG x: " << curr_location_msg.trajectory.location.ecef_x << ", y:" << curr_location_msg.trajectory.location.ecef_y);
243 arrow_start = ECEFToMapPoint(curr_location_msg.trajectory.location); //convert from cm to m
244 RCLCPP_DEBUG_STREAM(get_logger(), "Map Point- DEBUG x: " << arrow_start.x << ", y:" << arrow_start.y);
245
246 curr_location_msg.trajectory.location.ecef_x += msg.trajectory.offsets[i].offset_x;
247 curr_location_msg.trajectory.location.ecef_y += msg.trajectory.offsets[i].offset_y;
248 curr_location_msg.trajectory.location.ecef_z += msg.trajectory.offsets[i].offset_z;
249 arrow_end = ECEFToMapPoint(curr_location_msg.trajectory.location);
250
251 marker.points.push_back(arrow_start);
252 marker.points.push_back(arrow_end);
253
254 output.markers.push_back(marker);
255 }
256 RCLCPP_DEBUG_STREAM(get_logger(), "Last ECEF Point- DEBUG x: " << curr_location_msg.trajectory.location.ecef_x << ", y:" << curr_location_msg.trajectory.location.ecef_y);
257 RCLCPP_DEBUG_STREAM(get_logger(), "Last Map Point- DEBUG x: " << arrow_end.x << ", y:" << arrow_end.y);
258 prev_marker_list_size_[msg.m_header.sender_id] = msg.trajectory.offsets.size();
259
260 return output;
261 }
262
263 geometry_msgs::msg::Point MobilityPathVisualizer::ECEFToMapPoint(const carma_v2x_msgs::msg::LocationECEF& ecef_point) const
264 {
265
266 if (!map_projector_) {
267 throw std::invalid_argument("No map projector available for ecef conversion");
268 }
269 geometry_msgs::msg::Point output;
270
271 lanelet::BasicPoint3d map_point = map_projector_->projectECEF( { (double)ecef_point.ecef_x/100.0, (double)ecef_point.ecef_y/100.0, (double)ecef_point.ecef_z/100.0 } , -1);
272 output.x = map_point.x();
273 output.y = map_point.y();
274 output.z = map_point.z();
275
276 return output;
277 }
278
279
280 visualization_msgs::msg::MarkerArray MobilityPathVisualizer::composeLabelMarker(const visualization_msgs::msg::MarkerArray& host_marker, const std::vector<visualization_msgs::msg::MarkerArray>& cav_markers) const
281 {
282 visualization_msgs::msg::MarkerArray output;
283 visualization_msgs::msg::Marker marker;
284 marker.header.frame_id = "map";
285 marker.type = visualization_msgs::msg::Marker::TEXT_VIEW_FACING;
286 marker.action = visualization_msgs::msg::Marker::ADD;
287 marker.ns = "mobilitypath_visualizer";
288
289 marker.scale.x = config_.x;
290 marker.scale.y = config_.y;
291 marker.scale.z = config_.z;
292 marker.color.r = 1.0;
293 marker.color.g = 1.0;
294 marker.color.b = 1.0;
295 marker.color.a = 1.0;
296 marker.frame_locked = true;
297 marker.lifetime = builtin_interfaces::msg::Duration((rclcpp::Duration(config_.t * 1e9)));
298
299 for (auto const& cav_marker: cav_markers)
300 {
301 size_t idx = 0;
302 while(idx < host_marker.markers.size() && idx < cav_marker.markers.size())
303 {
304 if (compute_2d_distance(cav_marker.markers[idx].points[0], host_marker.markers[idx].points[0]) <= 1.0) // within 1 meter
305 {
306 marker.id = (int32_t)idx;
307 marker.header.stamp = host_marker.markers[idx].header.stamp;
308 marker.pose.position.x = cav_marker.markers[idx].points[0].x;
309 marker.pose.position.y = cav_marker.markers[idx].points[0].y;
310 marker.pose.position.z = cav_marker.markers[idx].points[0].z;
311 marker.pose.orientation.w = 1.0f;
312
313 std::string collision_time = std::to_string((rclcpp::Time(cav_marker.markers[idx].header.stamp) - rclcpp::Time(host_marker.markers[0].header.stamp)).seconds());
314 collision_time = collision_time.substr(0,collision_time.size() - 4); //reduce precision to 2 decimals
315 marker.text = "Collision in " + collision_time + "s!";
316
317 output.markers.push_back(marker);
318 }
319 idx++;
320 }
321
322 if (compute_2d_distance(cav_marker.markers[idx-1].points[1], host_marker.markers[idx-1].points[1]) <= 1.0) // within 1 meter
323 {
324 // last point
325 marker.id = (int32_t)idx;
326 marker.header.stamp = host_marker.markers[idx-1].header.stamp;
327 marker.pose.position.x = cav_marker.markers[idx-1].points[1].x;
328 marker.pose.position.y = cav_marker.markers[idx-1].points[1].y;
329 marker.pose.position.z = cav_marker.markers[idx-1].points[1].z;
330
331 rclcpp::Time cav_marker_stamp(cav_marker.markers[idx-1].header.stamp);
332 rclcpp::Time host_marker_stamp(host_marker.markers[0].header.stamp);
333 rclcpp::Duration duration = cav_marker_stamp - host_marker_stamp;
334 std::string collision_time = std::to_string(duration.seconds());
335 collision_time = collision_time.substr(0,collision_time.size() - 4); //reduce precision to 2 decimals
336 marker.text = "Collision in " + collision_time + "s!";
337 output.markers.push_back(marker);
338 }
339 }
340
341 return output;
342 }
343
344 std::vector<visualization_msgs::msg::MarkerArray> MobilityPathVisualizer::matchTrajectoryTimestamps(const visualization_msgs::msg::MarkerArray& host_marker,
345 const std::vector<visualization_msgs::msg::MarkerArray>& cav_markers) const
346 {
347 if (host_marker.markers.empty() || host_marker.markers[0].action == visualization_msgs::msg::Marker::DELETE)
348 return cav_markers;
349
350 std::vector<visualization_msgs::msg::MarkerArray> synchronized_output;
351 for (auto const& curr_cav: cav_markers)
352 {
353 visualization_msgs::msg::MarkerArray synchronized_marker_array;
354 signed int curr_idx = 0;
355 double time_step = 0.1;
356 // although it is very rare to reach here
357 // we do not need to visualize other car that starts "in the future", so skip
358
359 if (rclcpp::Time(curr_cav.markers[0].header.stamp) > rclcpp::Time(host_marker.markers[0].header.stamp) ||
360 curr_cav.markers[0].action == visualization_msgs::msg::Marker::DELETE)
361 {
362 continue;
363 }
364 // this marker is outdated, drop
365
366 rclcpp::Time curr_cav_marker_stamp(curr_cav.markers.back().header.stamp);
367 rclcpp::Time host_marker_stamp(host_marker.markers[0].header.stamp);
368
369
370 if (curr_cav_marker_stamp + rclcpp::Duration(time_step * 1e9) < host_marker_stamp){
371 continue;
372 }
373
374 // skip points until the idx to start interpolating
375 while (curr_idx < curr_cav.markers.size() && rclcpp::Time(curr_cav.markers[curr_idx].header.stamp) <= rclcpp::Time(host_marker.markers[0].header.stamp))
376 {
377 curr_idx++;
378 }
379
380 curr_idx -= 1; // carma_v2x_msg stamp is before that of host now
381 // interpolate position to match the starting time (dt < time_step)
382
383 if (curr_idx < 0 )
384 curr_idx = 0;
385
386 double dt = (rclcpp::Time(host_marker.markers[0].header.stamp) - rclcpp::Time(curr_cav.markers[curr_idx].header.stamp)).seconds();
387
388 rclcpp::Time curr_time(host_marker.markers[0].header.stamp);
389
390 // only update start_point of each arrow type marker for now
391
392 while (curr_idx < curr_cav.markers.size())
393 {
394 if (curr_cav.markers[curr_idx].action == visualization_msgs::msg::Marker::DELETE)
395 break;
396
397 visualization_msgs::msg::Marker curr_marker = curr_cav.markers[curr_idx]; //copy static info
398 if (dt != 0.0 && !curr_cav.markers[curr_idx].points.empty()) // if not already synchronized
399 {
400 double dx = (curr_cav.markers[curr_idx].points[1].x - curr_cav.markers[curr_idx].points[0].x)/time_step * dt;
401 double dy = (curr_cav.markers[curr_idx].points[1].y - curr_cav.markers[curr_idx].points[0].y)/time_step * dt;
402 double dz = (curr_cav.markers[curr_idx].points[1].z - curr_cav.markers[curr_idx].points[0].z)/time_step * dt;
403
404 curr_marker.points[0].x += dx;
405 curr_marker.points[0].y += dy;
406 curr_marker.points[0].z += dz;
407
408 } //else just loop and copy
409 curr_marker.header.stamp = curr_time;
410 synchronized_marker_array.markers.push_back(curr_marker);
411 curr_idx ++;
412 curr_time += rclcpp::Duration(time_step, 0.0);
413 }
414
415 curr_idx = 0; // resetting idx to work on new, synchronized list
416
417
418 // update end_point of each arrow type marker, except that of last point
419 while (curr_idx < synchronized_marker_array.markers.size()-1)
420 {
421 if (curr_cav.markers[curr_idx].action == visualization_msgs::msg::Marker::DELETE)
422 break;
423
424 if (!synchronized_marker_array.markers[curr_idx].points.empty())
425 synchronized_marker_array.markers[curr_idx].points[1] = synchronized_marker_array.markers[curr_idx + 1].points[0];
426 curr_idx ++;
427 }
428
429 if (!synchronized_marker_array.markers[curr_idx].points.empty())
430 {
431 // extrapolate the last point just to conform with 0.1s interval between points
432 double dx = (synchronized_marker_array.markers[curr_idx].points[1].x - synchronized_marker_array.markers[curr_idx].points[0].x)/(time_step - dt) * time_step;
433 double dy = (synchronized_marker_array.markers[curr_idx].points[1].y - synchronized_marker_array.markers[curr_idx].points[0].y)/(time_step - dt) * time_step;
434 double dz = (synchronized_marker_array.markers[curr_idx].points[1].z - synchronized_marker_array.markers[curr_idx].points[0].z)/(time_step - dt) * time_step;
435
436 synchronized_marker_array.markers[curr_idx].points[1].x = synchronized_marker_array.markers[curr_idx].points[0].x + dx;
437 synchronized_marker_array.markers[curr_idx].points[1].y = synchronized_marker_array.markers[curr_idx].points[0].y + dy;
438 synchronized_marker_array.markers[curr_idx].points[1].z = synchronized_marker_array.markers[curr_idx].points[0].z + dz;
439
440 }
441
442 synchronized_output.push_back(synchronized_marker_array);
443
444 }
445
446 return synchronized_output;
447 }
448
449}
450#include "rclcpp_components/register_node_macro.hpp"
451
452// Register the component with class_loader
453RCLCPP_COMPONENTS_REGISTER_NODE(mobilitypath_visualizer::MobilityPathVisualizer)
carma_ros2_utils::PubPtr< visualization_msgs::msg::MarkerArray > label_marker_pub_
visualization_msgs::msg::MarkerArray composeLabelMarker(const visualization_msgs::msg::MarkerArray &host_marker, const std::vector< visualization_msgs::msg::MarkerArray > &cav_markers) const
Compose a label marker that displays whether if any of the cav's path cross with that of host (respec...
std::unordered_map< std::string, carma_v2x_msgs::msg::MobilityPath > latest_cav_mob_path_msg_
carma_ros2_utils::CallbackReturn handle_on_configure(const rclcpp_lifecycle::State &)
carma_ros2_utils::SubPtr< carma_v2x_msgs::msg::MobilityPath > cav_mob_path_sub_
carma_ros2_utils::SubPtr< std_msgs::msg::String > georeference_sub_
std::vector< visualization_msgs::msg::MarkerArray > matchTrajectoryTimestamps(const visualization_msgs::msg::MarkerArray &host_marker, const std::vector< visualization_msgs::msg::MarkerArray > &cav_markers) const
Matches timestamps of CAV's individual points of cav_markers to that of host_marker and interpolates ...
std::unordered_map< std::string, size_t > prev_marker_list_size_
carma_ros2_utils::PubPtr< visualization_msgs::msg::MarkerArray > cav_marker_pub_
visualization_msgs::msg::MarkerArray label_marker_
MobilityPathVisualizer(const rclcpp::NodeOptions &)
Constructor.
std::vector< visualization_msgs::msg::MarkerArray > cav_markers_
carma_ros2_utils::CallbackReturn handle_on_activate(const rclcpp_lifecycle::State &)
geometry_msgs::msg::Point ECEFToMapPoint(const carma_v2x_msgs::msg::LocationECEF &ecef_point) const
Accepts ECEF point in cm to convert to a point in map in meters.
visualization_msgs::msg::MarkerArray composeVisualizationMarker(const carma_v2x_msgs::msg::MobilityPath &msg, const MarkerColor &color)
Compose a visualization marker for mobilitypath messages.
void callbackMobilityPath(carma_v2x_msgs::msg::MobilityPath::UniquePtr msg)
std::shared_ptr< lanelet::projection::LocalFrameProjector > map_projector_
void georeferenceCallback(std_msgs::msg::String::UniquePtr msg)
Callback for map projection string to define lat/lon -> map conversion.
carma_ros2_utils::PubPtr< visualization_msgs::msg::MarkerArray > host_marker_pub_
carma_ros2_utils::SubPtr< carma_v2x_msgs::msg::MobilityPath > host_mob_path_sub_
auto to_string(const UtmZone &zone) -> std::string
Definition: utm_zone.cpp:21
double compute_2d_distance(const geometry_msgs::msg::Point &pt1, const geometry_msgs::msg::Point &pt2)
Stuct containing the algorithm configuration values for <package_name>