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.
route_generator_worker.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2020-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
19namespace route {
20
21 RouteGeneratorWorker::RouteGeneratorWorker(tf2_ros::Buffer& tf2_buffer)
22 : tf2_buffer_(tf2_buffer) {}
23
25 {
26 this->world_model_ = wm;
27 }
28
29 void RouteGeneratorWorker::setLoggerInterface(rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr logger)
30 {
31 this->logger_ = logger;
32 this->rs_worker_.setLoggerInterface(logger);
33 }
34
35 void RouteGeneratorWorker::setClock(rclcpp::Clock::SharedPtr clock)
36 {
37 this->clock_ = clock;
38 }
39
40 lanelet::Optional<lanelet::routing::Route> RouteGeneratorWorker::routing(const lanelet::BasicPoint2d start,
41 const std::vector<lanelet::BasicPoint2d>& via,
42 const lanelet::BasicPoint2d end,
43 const lanelet::LaneletMapConstPtr map_pointer,
44 const carma_wm::LaneletRoutingGraphConstPtr graph_pointer) const
45 {
46 // find start lanelet
47 auto start_lanelet_vector = lanelet::geometry::findNearest(map_pointer->laneletLayer, start, 1);
48 // check if there are any lanelets in the map
49 if(start_lanelet_vector.empty())
50 {
51 RCLCPP_ERROR_STREAM(logger_->get_logger(), "Found no lanelets in the map. Routing cannot be done.");
52 return lanelet::Optional<lanelet::routing::Route>();
53 }
54 // extract starting lanelet
55 auto start_lanelet = lanelet::ConstLanelet(start_lanelet_vector[0].second.constData());
56 // find end lanelet
57 auto end_lanelet_vector = lanelet::geometry::findNearest(map_pointer->laneletLayer, end, 1);
58 // extract end lanelet
59 auto end_lanelet = lanelet::ConstLanelet(end_lanelet_vector[0].second.constData());
60 // find all via lanelets
61 lanelet::ConstLanelets via_lanelets_vector;
62 for(lanelet::BasicPoint2d point : via)
63 {
64 auto via_lanelet_vector = lanelet::geometry::findNearest(map_pointer->laneletLayer, point, 1);
65 via_lanelets_vector.emplace_back(lanelet::ConstLanelet(via_lanelet_vector[0].second.constData()));
66 }
67 // routing
68 return graph_pointer->getRouteVia(start_lanelet, via_lanelets_vector, end_lanelet);
69 }
70
71 void RouteGeneratorWorker::setReroutingChecker(const std::function<bool()> inputFunction)
72 {
73 reroutingChecker=inputFunction;
74 }
75
76 bool RouteGeneratorWorker::getAvailableRouteCb(const std::shared_ptr<rmw_request_id_t>,
77 const std::shared_ptr<carma_planning_msgs::srv::GetAvailableRoutes::Request>,
78 std::shared_ptr<carma_planning_msgs::srv::GetAvailableRoutes::Response> resp)
79 {
80 // Return if the the directory specified by route_file_path_ does not exist
81 if(!boost::filesystem::exists(boost::filesystem::path(this->route_file_path_)))
82 {
83 RCLCPP_ERROR_STREAM(logger_->get_logger(), "No directory exists at " << route_file_path_);
84 return true;
85 }
86
87 // Read all route files in the given directory
88 boost::filesystem::directory_iterator end_point;
89 for(boost::filesystem::directory_iterator itr(boost::filesystem::path(this->route_file_path_)); itr != end_point; ++itr)
90 {
91 // Skip if the iterator has landed on a folder
92 if(boost::filesystem::is_directory(itr->status()))
93 {
94 continue;
95 }
96
97 auto full_file_name = itr->path().filename().generic_string();
98 carma_planning_msgs::msg::Route route_msg;
99
100 // Skip if '.csv' is not found in the file name
101 if(full_file_name.find(".csv") == std::string::npos)
102 {
103 continue;
104 }
105
106 // Assume route files ending with ".csv", before that is the actual route name
107 route_msg.route_id = full_file_name.substr(0, full_file_name.find(".csv"));
108 std::ifstream fin(itr->path().generic_string());
109 std::string dest_name;
110 if(fin.is_open())
111 {
112 while (!fin.eof())
113 {
114 std::string temp;
115 std::getline(fin, temp);
116 if(temp != "") dest_name = temp;
117 }
118 fin.close();
119 }
120 else
121 {
122 RCLCPP_ERROR_STREAM(logger_->get_logger(), "File open failed...");
123 }
124 auto last_comma = dest_name.find_last_of(',');
125 if(!std::isdigit(dest_name.substr(last_comma + 1).at(0)))
126 {
127 route_msg.route_name = dest_name.substr(last_comma + 1);
128 resp->available_routes.push_back(move(route_msg));
129 }
130 }
131
132 //after route path object is available to select, worker will able to transit state and provide route selection service
134 {
137 }
138
139 return true;
140 }
141
142 void RouteGeneratorWorker::setRouteFilePath(const std::string& path)
143 {
144 this->route_file_path_ = path;
145 // after route path is set, worker will able to transit state and provide route selection service
148 }
149
150 bool RouteGeneratorWorker::setActiveRouteCb(const std::shared_ptr<rmw_request_id_t>,
151 const std::shared_ptr<carma_planning_msgs::srv::SetActiveRoute::Request> req,
152 std::shared_ptr<carma_planning_msgs::srv::SetActiveRoute::Response> resp)
153 {
154 // only allow a new route to be activated in route selection state
156 {
157 RCLCPP_ERROR_STREAM(logger_->get_logger(), "System is already following a route.");
158 resp->error_status = carma_planning_msgs::srv::SetActiveRoute::Response::ALREADY_FOLLOWING_ROUTE;
159
160 return true;
161 }
162
163 RCLCPP_DEBUG_STREAM(logger_->get_logger(), "Valid state proceeding with selection");
164
165 // entering to routing state once destinations are picked
168
169 if (!vehicle_pose_) {
170 RCLCPP_ERROR_STREAM(logger_->get_logger(), "No vehicle position. Routing cannot be completed.");
171 resp->error_status = carma_planning_msgs::srv::SetActiveRoute::Response::ROUTING_FAILURE;
174 return true;
175 }
176
177 // Check if the map projection is available
178 if (!map_proj_) {
179 RCLCPP_ERROR_STREAM(logger_->get_logger(), "Could not generate route as there was no map projection available");
180 resp->error_status = carma_planning_msgs::srv::SetActiveRoute::Response::ROUTING_FAILURE;
183 return true;
184 }
185
186 // load destination points in map frame
187 std::vector<lanelet::BasicPoint3d> destination_points;
188 if(req->choice == carma_planning_msgs::srv::SetActiveRoute::Request::ROUTE_ID)
189 {
190 RCLCPP_INFO_STREAM(logger_->get_logger(), "set_active_route_cb: Selected Route ID: " << req->route_id);
191 std::vector<carma_v2x_msgs::msg::Position3D> gps_destination_points = loadRouteDestinationGpsPointsFromRouteId(req->route_id);
192 destination_points = loadRouteDestinationsInMapFrame(gps_destination_points);
193 }
194 else if(req->choice == carma_planning_msgs::srv::SetActiveRoute::Request::DESTINATION_POINTS_ARRAY)
195 {
196 RCLCPP_INFO_STREAM(logger_->get_logger(), "set_active_route_cb: Destination Points array of size " << req->destination_points.size());
197 destination_points = loadRouteDestinationsInMapFrame(req->destination_points);
198 }
199
200 // Check that the requested route is valid with at least one destination point
201 if(destination_points.size() < 1)
202 {
203 RCLCPP_ERROR_STREAM(logger_->get_logger(), "Provided route contains no destination points. Routing cannot be completed.");
204 resp->error_status = carma_planning_msgs::srv::SetActiveRoute::Response::ROUTE_FILE_ERROR;
207 return true;
208 }
209
210 if (!world_model_ || !world_model_->getMap()) {
211 RCLCPP_ERROR_STREAM(logger_->get_logger(), "World model has not been initialized.");
212 resp->error_status = carma_planning_msgs::srv::SetActiveRoute::Response::ROUTING_FAILURE;
215 return true;
216 }
217
218 // convert points in 2d to map frame
219 destination_points_in_map_ = lanelet::utils::transform(destination_points, [](auto a) { return lanelet::traits::to2D(a); });
220
221 // Add vehicle as first destination point
222 auto destination_points_in_map_with_vehicle = destination_points_in_map_;
223
224 lanelet::BasicPoint2d vehicle_position(vehicle_pose_->pose.position.x, vehicle_pose_->pose.position.y);
225 destination_points_in_map_with_vehicle.insert(destination_points_in_map_with_vehicle.begin(), vehicle_position);
226
227 int idx = 0;
228 // validate if the points are geometrically in the map
229 for (auto pt : destination_points_in_map_with_vehicle)
230 {
231 if ((world_model_->getLaneletsFromPoint(pt, 1)).empty())
232 {
233 RCLCPP_ERROR_STREAM(logger_->get_logger(), "Route Generator: " << idx
234 << "th destination point is not in the map, x: " << pt.x() << " y: " << pt.y());
235 resp->error_status = carma_planning_msgs::srv::SetActiveRoute::Response::ROUTE_FILE_ERROR;
238 return true;
239 }
240 idx ++;
241 }
242
243 // get route graph from world model object
244 auto p = world_model_->getMapRoutingGraph();
245 // generate a route
246 auto route = routing(destination_points_in_map_with_vehicle.front(),
247 std::vector<lanelet::BasicPoint2d>(destination_points_in_map_with_vehicle.begin() + 1, destination_points_in_map_with_vehicle.end() - 1),
248 destination_points_in_map_with_vehicle.back(),
249 world_model_->getMap(), world_model_->getMapRoutingGraph());
250 // check if route succeeded
251 if(!route)
252 {
253 RCLCPP_ERROR_STREAM(logger_->get_logger(), "Cannot find a route passing all destinations.");
254 resp->error_status = carma_planning_msgs::srv::SetActiveRoute::Response::ROUTING_FAILURE;
257 return true;
258 }
259
261 {
262 RCLCPP_ERROR_STREAM(logger_->get_logger(), "At least one duplicate Lanelet ID occurs in the shortest path. Routing cannot be completed.");
263 resp->error_status = carma_planning_msgs::srv::SetActiveRoute::Response::ROUTING_FAILURE;
266 return true;
267 }
268
269 // Specify the end point of the route that is inside the last lanelet
270 lanelet::Point3d end_point{lanelet::utils::getId(), destination_points_in_map_with_vehicle.back().x(), destination_points_in_map_with_vehicle.back().y(), 0};
271
272 route->setEndPoint(end_point);
273
274 // update route message
276
277 for(auto id : route_msg_.route_path_lanelet_ids)
278 {
279 auto ll = world_model_->getMap()->laneletLayer.get(id);
280 route_llts.push_back(ll);
281 }
282
283 route_msg_.route_name = req->route_id;
285 route_msg_.header.stamp = clock_->now();
286 route_msg_.header.frame_id = "map";
287 route_msg_.map_version = world_model_->getMapVersion();
288 // since routing is done correctly, transit to route following state
291 // set publish flag such that updated msg will be published in the next spin
293 return true;
294 }
295
297 {
298 // Create a vector for the lanelet IDs in the shortest path
299 std::vector<lanelet::Id> shortest_path_lanelet_ids;
300
301 // Iterate through the shortest path to populate shortest_path_lanelet_ids with lanelet IDs
302 for(const auto& ll : route.shortestPath())
303 {
304 shortest_path_lanelet_ids.push_back(ll.id());
305 }
306
307 // Verify that there are no duplicate lanelet IDs in the shortest path
308 std::sort(shortest_path_lanelet_ids.begin(), shortest_path_lanelet_ids.end());
309
310 if (std::adjacent_find(shortest_path_lanelet_ids.begin(), shortest_path_lanelet_ids.end()) != shortest_path_lanelet_ids.end())
311 {
312 // Route's shortest path contains duplicate lanelet IDs
313 return true;
314 }
315
316 // Route's shortest path does not duplicate lanelet IDs
317 return false;
318 }
319
320 std::vector<lanelet::BasicPoint3d> RouteGeneratorWorker::loadRouteDestinationsInMapFrame(const std::vector<carma_v2x_msgs::msg::Position3D>& destinations) const
321 {
322 if (!map_proj_) {
323 throw std::invalid_argument("loadRouteDestinationsInMapFrame (using destination points array) before map projection was set");
324 }
325
326 lanelet::projection::LocalFrameProjector projector(map_proj_.get().c_str()); // Build map projector
327
328 // Process each point in 'destinations'
329 std::vector<lanelet::BasicPoint3d> destination_points;
330 for (const auto& destination : destinations)
331 {
332 lanelet::GPSPoint coordinate;
333 coordinate.lon = destination.longitude;
334 coordinate.lat = destination.latitude;
335
336 if(destination.elevation_exists)
337 {
338 coordinate.ele = destination.elevation;
339 }
340 else
341 {
342 coordinate.ele = 0.0;
343 }
344
345 destination_points.emplace_back(projector.forward(coordinate));
346 }
347
348 return destination_points;
349 }
350
351 std::vector<carma_v2x_msgs::msg::Position3D> RouteGeneratorWorker::loadRouteDestinationGpsPointsFromRouteId(const std::string& route_id) const
352 {
353 // compose full path of the route file
354 std::string route_file_name = route_file_path_ + route_id + ".csv";
355 std::ifstream fs(route_file_name);
356 std::string line;
357
358 // read each line in route file (if any)
359 std::vector<carma_v2x_msgs::msg::Position3D> destination_points;
360 while(std::getline(fs, line))
361 {
362 carma_v2x_msgs::msg::Position3D gps_point;
363
364 // lat lon and elev is seperated by comma
365 auto comma = line.find(",");
366 // convert lon value in degrees from string
367 gps_point.longitude = std::stof(line.substr(0, comma));
368 line.erase(0, comma + 1);
369 comma = line.find(",");
370 // convert lat value in degrees from string
371 gps_point.latitude = std::stof(line.substr(0, comma));
372 // elevation is in meters
373 line.erase(0, comma + 1);
374 comma = line.find(",");
375 gps_point.elevation = std::stof(line.substr(0, comma));
376 gps_point.elevation_exists = true;
377
378 destination_points.push_back(gps_point);
379 }
380
381 return destination_points;
382 }
383
384 visualization_msgs::msg::Marker RouteGeneratorWorker::composeRouteMarkerMsg(const lanelet::Optional<lanelet::routing::Route>& route)
385 {
386 std::vector<lanelet::ConstPoint3d> points;
387 auto end_point_3d = route.get().getEndPoint();
388 auto last_ll = route.get().shortestPath().back();
389 double end_point_downtrack = carma_wm::geometry::trackPos(last_ll, {end_point_3d.x(), end_point_3d.y()}).downtrack;
390 double lanelet_downtrack = carma_wm::geometry::trackPos(last_ll, last_ll.centerline().back().basicPoint2d()).downtrack;
391 // get number of points to display using ratio of the downtracks
392 auto points_until_end_point = int (last_ll.centerline().size() * (end_point_downtrack / lanelet_downtrack));
393
394 for(const auto& ll : route.get().shortestPath())
395 {
396 if (ll.id() == last_ll.id())
397 {
398 for (int i = 0; i < points_until_end_point; i++)
399 {
400 points.push_back(ll.centerline()[i]);
401 }
402 continue;
403 }
404 for(const auto& pt : ll.centerline())
405 {
406 points.push_back(pt);
407 }
408 }
409
410 route_marker_msg_.points={};
411
412 // create the marker msgs
413 visualization_msgs::msg::Marker marker;
414 marker.header.frame_id = "map";
415 marker.header.stamp = rclcpp::Time();
416 marker.type = visualization_msgs::msg::Marker::SPHERE_LIST;//
417 marker.action = visualization_msgs::msg::Marker::ADD;
418 marker.ns = "route_visualizer";
419
420 marker.scale.x = 0.65;
421 marker.scale.y = 0.65;
422 marker.scale.z = 0.65;
423 marker.frame_locked = true;
424
425 marker.id = 0;
426 marker.color.r = 1.0F;
427 marker.color.g = 1.0F;
428 marker.color.b = 1.0F;
429 marker.color.a = 1.0F;
430
431 if (points.empty())
432 {
433 RCLCPP_WARN_STREAM(logger_->get_logger(), "No central line points! Returning");
434 return route_marker_msg_;
435 }
436
437 for (int i = 0; i < points.size(); i=i+5)
438 {
439 geometry_msgs::msg::Point temp_point;
440 temp_point.x = points[i].x();
441 temp_point.y = points[i].y();
442 temp_point.z = 1; //to show up on top of the lanelet lines
443
444 marker.points.push_back(temp_point);
445 }
447 return marker;
448 }
449
450 carma_planning_msgs::msg::Route RouteGeneratorWorker::composeRouteMsg(const lanelet::Optional<lanelet::routing::Route>& route) const
451 {
452 carma_planning_msgs::msg::Route msg;
453 // iterate through the shortest path to populate shortest_path_lanelet_ids
454 for(const auto& ll : route.get().shortestPath())
455 {
456 msg.shortest_path_lanelet_ids.push_back(ll.id());
457 }
458 // iterate through all lanelet in the route to populate route_path_lanelet_ids
459 for(const auto& ll : route.get().laneletSubmap()->laneletLayer)
460 {
461 msg.route_path_lanelet_ids.push_back(ll.id());
462 }
463 msg.end_point.x = route->getEndPoint().x();
464 msg.end_point.y = route->getEndPoint().y();
465 msg.end_point.z = route->getEndPoint().z();
466
467 return msg;
468 }
469
470 bool RouteGeneratorWorker::abortActiveRouteCb(const std::shared_ptr<rmw_request_id_t>,
471 const std::shared_ptr<carma_planning_msgs::srv::AbortActiveRoute::Request>,
472 std::shared_ptr<carma_planning_msgs::srv::AbortActiveRoute::Response> resp)
473 {
474 // only make sense to abort when it is in route following state
476 {
478 resp->error_status = carma_planning_msgs::srv::AbortActiveRoute::Response::NO_ERROR;
480 route_msg_ = carma_planning_msgs::msg::Route{};
481 } else {
482 // service call successed but there is not active route
483 resp->error_status = carma_planning_msgs::srv::AbortActiveRoute::Response::NO_ACTIVE_ROUTE;
484 }
485 return true;
486 }
487
489 {
490 tf2_listener_.reset(new tf2_ros::TransformListener(tf2_buffer_));
491 tf2_buffer_.setUsingDedicatedThread(true);
492 }
493
495 {
496 try
497 {
498 tf_ = tf2_buffer_.lookupTransform("map", "vehicle_front", rclcpp::Time(0,0), rclcpp::Duration::from_nanoseconds(1.0*1e9)); //save to local copy of transform 0.1 sec timeout
499 tf2::fromMsg(tf_, frontbumper_transform_);
500 }
501 catch (const tf2::TransformException &ex)
502 {
503 RCLCPP_WARN_STREAM(logger_->get_logger(), ex.what());
504 }
505
506 geometry_msgs::msg::PoseStamped updated_vehicle_pose;
507 updated_vehicle_pose.pose.position.x = frontbumper_transform_.getOrigin().getX();
508 updated_vehicle_pose.pose.position.y = frontbumper_transform_.getOrigin().getY();
509 updated_vehicle_pose.pose.position.z = frontbumper_transform_.getOrigin().getZ();
510 vehicle_pose_ = updated_vehicle_pose;
511
513 // convert from pose stamp into lanelet basic 2D point
514 current_loc_ = lanelet::BasicPoint2d(vehicle_pose_->pose.position.x, vehicle_pose_->pose.position.y);
515 // get dt ct from world model
516 carma_wm::TrackPos track(0.0, 0.0);
517 try {
518 track = this->world_model_->routeTrackPos(current_loc_);
519 } catch (std::invalid_argument ex) {
520 RCLCPP_WARN_STREAM(logger_->get_logger(), "Routing has finished but carma_wm has not receive it!");
521 return;
522 }
523
524 // Return if world model has not yet been updated with the current active route
525 if ((this->world_model_->getRouteName()).compare(route_msg_.route_name) != 0) {
526 RCLCPP_WARN_STREAM(logger_->get_logger(), "Current active route name is " << route_msg_.route_name << ", WorldModel is using " << this->world_model_->getRouteName());
527 return;
528 }
529
530 auto current_lanelet = getClosestLaneletFromRouteLanelets(current_loc_);
531 auto lanelet_track = carma_wm::geometry::trackPos(current_lanelet, current_loc_);
532 ll_id_ = current_lanelet.id();
533 ll_crosstrack_distance_ = lanelet_track.crosstrack;
534 ll_downtrack_distance_ = lanelet_track.downtrack;
537 // Determine speed limit
538 lanelet::Optional<carma_wm::TrafficRulesConstPtr> traffic_rules = world_model_->getTrafficRules();
539
540 if (traffic_rules)
541 {
542 auto laneletIterator = world_model_->getMap()->laneletLayer.find(ll_id_);
543 if (laneletIterator != world_model_->getMap()->laneletLayer.end()) {
544 speed_limit_ = (*traffic_rules)->speedLimit(*laneletIterator).speedLimit.value();
545 }
546 else
547 {
548 RCLCPP_ERROR_STREAM(logger_->get_logger(), "Failed to set the current speed limit. The lanelet_id: "
549 << ll_id_ << " could not be matched with a lanelet in the map. The previous speed limit of "
550 << speed_limit_ << " will be used.");
551 }
552
553 }
554 else
555 {
556 RCLCPP_ERROR_STREAM(logger_->get_logger(), "Failed to set the current speed limit. Valid traffic rules object could not be built.");
557 }
558 std::shared_ptr<geometry_msgs::msg::PoseStamped> pose_ptr(new geometry_msgs::msg::PoseStamped(*vehicle_pose_));
559
560 // check if we left the seleted route by cross track error
561 if (crosstrackErrorCheck(pose_ptr, current_lanelet))
562 {
565 }
566
567 // check if we reached our destination be remaining down track distance
568 double route_length_2d = world_model_->getRouteEndTrackPos().downtrack;
570 {
573 }
574 }
575 }
576
577 void RouteGeneratorWorker::twistCb(geometry_msgs::msg::TwistStamped::UniquePtr msg)
578 {
579 current_speed_ = msg->twist.linear.x;
580 }
581
582 void RouteGeneratorWorker::georeferenceCb(std_msgs::msg::String::UniquePtr msg)
583 {
584 map_proj_ = msg->data;
585 }
586
587 void RouteGeneratorWorker::setPublishers(const carma_ros2_utils::PubPtr<carma_planning_msgs::msg::RouteEvent>& route_event_pub,
588 const carma_ros2_utils::PubPtr<carma_planning_msgs::msg::RouteState>& route_state_pub,
589 const carma_ros2_utils::PubPtr<carma_planning_msgs::msg::Route>& route_pub,
590 const carma_ros2_utils::PubPtr<visualization_msgs::msg::Marker>& route_marker_pub)
591 {
592 route_event_pub_ = route_event_pub;
593 route_state_pub_ = route_state_pub;
594 route_pub_ = route_pub;
595 route_marker_pub_= route_marker_pub;
596 }
597
599 {
600 this->down_track_target_range_ = dt_dest_range;
601 }
602
604 {
605 route_event_queue.push(event_type);
606 }
607
608 lanelet::Optional<lanelet::routing::Route> RouteGeneratorWorker::rerouteAfterRouteInvalidation(const std::vector<lanelet::BasicPoint2d>& destination_points_in_map)
609 {
610 std::vector<lanelet::BasicPoint2d> destination_points_in_map_temp;
611
612 for(const auto &i:destination_points_in_map) // Identify all route points that we have not yet passed
613 {
614 double destination_down_track=world_model_->routeTrackPos(i).downtrack;
615
616 if( current_downtrack_distance_< destination_down_track)
617 {
618 destination_points_in_map_temp.push_back(i);
619 RCLCPP_DEBUG_STREAM(logger_->get_logger(), "current_downtrack_distance_:" << current_downtrack_distance_);
620 RCLCPP_DEBUG_STREAM(logger_->get_logger(), "destination_down_track:" << destination_down_track);
621 }
622 }
623
624 destination_points_in_map_ = destination_points_in_map_temp; // Update our route point list
625
626 RCLCPP_DEBUG_STREAM(logger_->get_logger(), "New destination_points_in_map.size:" << destination_points_in_map_.size());
627
628 auto route=routing(current_loc_, // Route from current location through future destinations
629 std::vector<lanelet::BasicPoint2d>(destination_points_in_map_.begin(), destination_points_in_map_.end() - 1),
631 world_model_->getMap(), world_model_->getMapRoutingGraph());
632
633 return route;
634 }
635
637 {
638 // Update vehicle position
639 bumperPoseCb();
640
641 if(reroutingChecker()==true)
642 {
643 RCLCPP_DEBUG_STREAM(logger_->get_logger(), "Rerouting required");
647
648 // check if route successed
649 if(!route)
650 {
651 RCLCPP_ERROR_STREAM(logger_->get_logger(), "Cannot find a route passing all destinations.");
654 return true;
655 }
657 {
658 RCLCPP_ERROR_STREAM(logger_->get_logger(), "At least one duplicate Lanelet ID occurs in the shortest path. Routing cannot be completed.");
661 return true;
662 }
663 else
664 {
667 }
668 std::string original_route_name = route_msg_.route_name;
670 route_msg_.route_name = original_route_name;
671 route_msg_.is_rerouted = true;
672 route_msg_.map_version = world_model_->getMapVersion();
676 }
677
678 // publish new route and set new route flag back to false
680 {
681 route_pub_->publish(route_msg_);
685 route_msg_.is_rerouted = false;
686 }
687 // publish route state messsage if a route is selected
688 if(route_msg_.route_name != "")
689 {
691 state_msg.header.stamp = clock_->now();
692 state_msg.route_id = route_msg_.route_name;
693 state_msg.cross_track = current_crosstrack_distance_;
694 state_msg.down_track = current_downtrack_distance_;
695 state_msg.lanelet_downtrack = ll_downtrack_distance_;
696 state_msg.state = this->rs_worker_.getRouteState();
697 state_msg.lanelet_id = ll_id_;
698 state_msg.speed_limit = speed_limit_;
699 route_state_pub_->publish(state_msg);
700 }
701 // publish route event in order if any
702 while(!route_event_queue.empty())
703 {
704 route_event_msg_.event = route_event_queue.front();
705 RCLCPP_INFO_STREAM(logger_->get_logger(), "Publishing a route event: "
706 << static_cast<RouteEvent>(route_event_msg_.event);
707 );
709 route_event_queue.pop();
710 }
711 return true;
712 }
713
714 bool RouteGeneratorWorker::crosstrackErrorCheck(const std::shared_ptr<geometry_msgs::msg::PoseStamped>& msg, lanelet::ConstLanelet current)
715 {
716 lanelet::BasicPoint2d position;
717
718 position.x()= msg->pose.position.x;
719 position.y()= msg->pose.position.y;
720
721 if(boost::geometry::within(position, current.polygon2d())) //If vehicle is inside current_lanelet, there is no crosstrack error
722 {
723 cte_count_ = 0;
724 return false;
725 }
726
727 RCLCPP_DEBUG_STREAM(logger_->get_logger(), "LLt Polygon Dimensions1: " << current.polygon2d().front().x()<< ", "<< current.polygon2d().front().y());
728 RCLCPP_DEBUG_STREAM(logger_->get_logger(), "LLt Polygon Dimensions2: " << current.polygon2d().back().x()<< ", "<< current.polygon2d().back().y());
729 RCLCPP_DEBUG_STREAM(logger_->get_logger(), "Distance1: " << boost::geometry::distance(position, current.polygon2d()) << " Max allowed Crosstrack: " << cross_track_dist_ );
730
731 if (boost::geometry::distance(position, current.polygon2d()) > cross_track_dist_) //Evaluate lanelet crosstrack distance from vehicle
732 {
733 cte_count_++;
734
735 if(cte_count_ > cte_count_max_) //If the distance exceeds the crosstrack distance a certain number of times, report that the route has been departed
736 {
737 cte_count_ = 0;
738 return true;
739 }
740 else
741 return false;
742 }
743 else
744 {
745 cte_count_ = 0;
746 return false;
747 }
748
749 }
750
751
752
753 lanelet::ConstLanelet RouteGeneratorWorker::getClosestLaneletFromRouteLanelets(lanelet::BasicPoint2d position) const
754 {
755 double min = std::numeric_limits<double>::infinity();
756 lanelet::ConstLanelet min_llt;
757 for (const auto& i: route_llts)
758 {
759 double dist = boost::geometry::distance(position, i.polygon2d());
760 if (dist < min)
761 {
762 min = dist;
763 min_llt = i;
764 }
765 }
766 return min_llt;
767 }
768
770 {
771 cross_track_dist_ = cte_dist;
772 }
773
775 {
776 cte_count_max_ = cte_max;
777
778 }
779
780 void RouteGeneratorWorker::addLanelet(lanelet::ConstLanelet llt)
781 {
782 route_llts.push_back(llt);
783 }
784
785} // route
Position in a track based coordinate system where the axis are downtrack and crosstrack....
Definition: TrackPos.hpp:35
tf2::Stamped< tf2::Transform > frontbumper_transform_
bool setActiveRouteCb(const std::shared_ptr< rmw_request_id_t >, const std::shared_ptr< carma_planning_msgs::srv::SetActiveRoute::Request > req, std::shared_ptr< carma_planning_msgs::srv::SetActiveRoute::Response > resp)
Set_active_route service callback. User can select a route to start following by calling this service...
void setCrosstrackErrorDistance(double cte_dist)
set the maximum crosstrack error distance
bool getAvailableRouteCb(const std::shared_ptr< rmw_request_id_t >, const std::shared_ptr< carma_planning_msgs::srv::GetAvailableRoutes::Request >, std::shared_ptr< carma_planning_msgs::srv::GetAvailableRoutes::Response > resp)
Get_available_route service callback. Calls to this service will respond with a list of route names f...
void addLanelet(lanelet::ConstLanelet llt)
void setLoggerInterface(rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr logger)
Dependency injection for logger interface.
void setWorldModelPtr(carma_wm::WorldModelConstPtr wm)
Dependency injection for world model pointer.
std::vector< lanelet::BasicPoint2d > destination_points_in_map_
void setRouteFilePath(const std::string &path)
Set method for configurable parameter.
void initializeBumperTransformLookup()
Initialize transform lookup from front bumper to map.
void setClock(rclcpp::Clock::SharedPtr clock)
Dependency injection for clock object.
void bumperPoseCb()
Callback for the front bumper pose transform.
void setReroutingChecker(const std::function< bool()> inputFunction)
setReroutingChecker function to set the rerouting flag
lanelet::Optional< lanelet::routing::Route > rerouteAfterRouteInvalidation(const std::vector< lanelet::BasicPoint2d > &destination_points_in_map)
After route is invalidated, this function returns a new route based on the destinations points.
void setCrosstrackErrorCountMax(int cte_max)
set the crosstrack error counter maximum limit
void setDowntrackDestinationRange(double dt_dest_range)
Set method for configurable parameter.
lanelet::ConstLanelet getClosestLaneletFromRouteLanelets(lanelet::BasicPoint2d position) const
"Get the closest lanelet on the route relative to the vehicle's current position. If the input list d...
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr logger_
boost::optional< std::string > map_proj_
visualization_msgs::msg::Marker composeRouteMarkerMsg(const lanelet::Optional< lanelet::routing::Route > &route)
composeRouteMarkerMsg is a function to generate route rviz markers
carma_ros2_utils::PubPtr< carma_planning_msgs::msg::Route > route_pub_
visualization_msgs::msg::Marker route_marker_msg_
bool spinCallback()
Spin callback which will be called frequently based on spin rate.
geometry_msgs::msg::TransformStamped tf_
void twistCb(geometry_msgs::msg::TwistStamped::UniquePtr msg)
Callback for the twist subscriber, which will store latest twist locally.
RouteGeneratorWorker(tf2_ros::Buffer &tf2_buffer)
Constructor for RouteGeneratorWorker class taking in dependencies via dependency injection.
carma_ros2_utils::PubPtr< carma_planning_msgs::msg::RouteEvent > route_event_pub_
std::vector< lanelet::BasicPoint3d > loadRouteDestinationsInMapFrame(const std::vector< carma_v2x_msgs::msg::Position3D > &destinations) const
Function to take route destination points from a vector of 3D points and convert them from lat/lon va...
carma_ros2_utils::PubPtr< carma_planning_msgs::msg::RouteState > route_state_pub_
std::function< bool()> reroutingChecker
reroutingChecker function to set the rerouting flag locally
carma_wm::WorldModelConstPtr world_model_
carma_planning_msgs::msg::RouteEvent route_event_msg_
lanelet::Optional< lanelet::routing::Route > routing(const lanelet::BasicPoint2d start, const std::vector< lanelet::BasicPoint2d > &via, const lanelet::BasicPoint2d end, const lanelet::LaneletMapConstPtr map_pointer, const carma_wm::LaneletRoutingGraphConstPtr graph_pointer) const
Generate Lanelet2 route based on input destinations.
carma_planning_msgs::msg::Route route_msg_
std::vector< carma_v2x_msgs::msg::Position3D > loadRouteDestinationGpsPointsFromRouteId(const std::string &route_id) const
Function to load route destination points from a route file and store them in a vector of 3D points.
carma_planning_msgs::msg::Route composeRouteMsg(const lanelet::Optional< lanelet::routing::Route > &route) const
Helper function to generate a CARMA route message based on planned lanelet route.
void setPublishers(const carma_ros2_utils::PubPtr< carma_planning_msgs::msg::RouteEvent > &route_event_pub, const carma_ros2_utils::PubPtr< carma_planning_msgs::msg::RouteState > &route_state_pub, const carma_ros2_utils::PubPtr< carma_planning_msgs::msg::Route > &route_pub, const carma_ros2_utils::PubPtr< visualization_msgs::msg::Marker > &route_marker_pub)
Method to pass publishers into worker class.
std::unique_ptr< tf2_ros::TransformListener > tf2_listener_
bool crosstrackErrorCheck(const std::shared_ptr< geometry_msgs::msg::PoseStamped > &msg, lanelet::ConstLanelet current_llt)
crosstrackErrorCheck is a function that determines when the vehicle has left the route and reports wh...
void georeferenceCb(std_msgs::msg::String::UniquePtr msg)
Callback for the georeference subscriber used to set the map projection.
carma_ros2_utils::PubPtr< visualization_msgs::msg::Marker > route_marker_pub_
bool abortActiveRouteCb(const std::shared_ptr< rmw_request_id_t >, const std::shared_ptr< carma_planning_msgs::srv::AbortActiveRoute::Request >, std::shared_ptr< carma_planning_msgs::srv::AbortActiveRoute::Response > resp)
Abort_active_route service callback. User can call this service to abort a current following route an...
bool checkForDuplicateLaneletsInShortestPath(const lanelet::routing::Route &route) const
Helper function to check whether a route's shortest path contains any duplicate Lanelet IDs....
boost::optional< geometry_msgs::msg::PoseStamped > vehicle_pose_
std::queue< uint8_t > route_event_queue
void publishRouteEvent(uint8_t event_type)
void setLoggerInterface(rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr logger)
RouteState getRouteState() const
Get current route state machine state.
void onRouteEvent(RouteEvent event)
Process route event based on designed state machine diagram.
TrackPos trackPos(const lanelet::ConstLanelet &lanelet, const lanelet::BasicPoint2d &point)
Returns the TrackPos, computed in 2d, of the provided point relative to the centerline of the provide...
Definition: Geometry.cpp:120
std::shared_ptr< const WorldModel > WorldModelConstPtr
Definition: WorldModel.hpp:454
std::shared_ptr< const lanelet::routing::RoutingGraph > LaneletRoutingGraphConstPtr
Definition: WorldModel.hpp:53