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
133 if(this->rs_worker_.getRouteState() == RouteStateWorker::RouteState::LOADING)
134 {
135 this->rs_worker_.onRouteEvent(RouteStateWorker::RouteEvent::ROUTE_LOADED);
136 publishRouteEvent(carma_planning_msgs::msg::RouteEvent::ROUTE_LOADED);
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
146 this->rs_worker_.onRouteEvent(RouteStateWorker::RouteEvent::ROUTE_LOADED);
147 publishRouteEvent(carma_planning_msgs::msg::RouteEvent::ROUTE_LOADED);
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
155 if(this->rs_worker_.getRouteState() != RouteStateWorker::RouteState::SELECTION)
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
166 this->rs_worker_.onRouteEvent(RouteStateWorker::RouteEvent::ROUTE_SELECTED);
167 publishRouteEvent(carma_planning_msgs::msg::RouteEvent::ROUTE_SELECTED);
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;
172 this->rs_worker_.onRouteEvent(RouteStateWorker::RouteEvent::ROUTE_GEN_FAILED);
173 publishRouteEvent(carma_planning_msgs::msg::RouteEvent::ROUTE_GEN_FAILED);
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;
181 this->rs_worker_.onRouteEvent(RouteStateWorker::RouteEvent::ROUTE_GEN_FAILED);
182 publishRouteEvent(carma_planning_msgs::msg::RouteEvent::ROUTE_GEN_FAILED);
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;
205 this->rs_worker_.onRouteEvent(RouteStateWorker::RouteEvent::ROUTE_GEN_FAILED);
206 publishRouteEvent(carma_planning_msgs::msg::RouteEvent::ROUTE_GEN_FAILED);
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;
213 this->rs_worker_.onRouteEvent(RouteStateWorker::RouteEvent::ROUTE_GEN_FAILED);
214 publishRouteEvent(carma_planning_msgs::msg::RouteEvent::ROUTE_GEN_FAILED);
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;
236 this->rs_worker_.onRouteEvent(RouteStateWorker::RouteEvent::ROUTE_GEN_FAILED);
237 publishRouteEvent(carma_planning_msgs::msg::RouteEvent::ROUTE_GEN_FAILED);
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;
255 this->rs_worker_.onRouteEvent(RouteStateWorker::RouteEvent::ROUTE_GEN_FAILED);
256 publishRouteEvent(carma_planning_msgs::msg::RouteEvent::ROUTE_GEN_FAILED);
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;
264 this->rs_worker_.onRouteEvent(RouteStateWorker::RouteEvent::ROUTE_GEN_FAILED);
265 publishRouteEvent(carma_planning_msgs::msg::RouteEvent::ROUTE_GEN_FAILED);
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
289 this->rs_worker_.onRouteEvent(RouteStateWorker::RouteEvent::ROUTE_STARTED);
290 publishRouteEvent(carma_planning_msgs::msg::RouteEvent::ROUTE_STARTED);
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
475 if(this->rs_worker_.getRouteState() == RouteStateWorker::RouteState::FOLLOWING)
476 {
477 this->rs_worker_.onRouteEvent(RouteStateWorker::RouteEvent::ROUTE_ABORTED);
478 resp->error_status = carma_planning_msgs::srv::AbortActiveRoute::Response::NO_ERROR;
479 publishRouteEvent(carma_planning_msgs::msg::RouteEvent::ROUTE_ABORTED);
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(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
512 if(this->rs_worker_.getRouteState() == RouteStateWorker::RouteState::FOLLOWING) {
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 {
563 this->rs_worker_.onRouteEvent(RouteStateWorker::RouteEvent::ROUTE_DEPARTED);
564 publishRouteEvent(carma_planning_msgs::msg::RouteEvent::ROUTE_DEPARTED);
565 }
566
567 // check if we reached our destination be remaining down track distance
568 double route_length_2d = world_model_->getRouteEndTrackPos().downtrack;
570 {
571 this->rs_worker_.onRouteEvent(RouteStateWorker::RouteEvent::ROUTE_COMPLETED);
572 publishRouteEvent(carma_planning_msgs::msg::RouteEvent::ROUTE_COMPLETED);
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");
644 this->rs_worker_.onRouteEvent(RouteStateWorker::RouteEvent::ROUTE_INVALIDATION);
645 publishRouteEvent(carma_planning_msgs::msg::RouteEvent::ROUTE_INVALIDATION);
647
648 // check if route successed
649 if(!route)
650 {
651 RCLCPP_ERROR_STREAM(logger_->get_logger(), "Cannot find a route passing all destinations.");
652 this->rs_worker_.onRouteEvent(RouteStateWorker::RouteEvent::ROUTE_GEN_FAILED);
653 publishRouteEvent(carma_planning_msgs::msg::RouteEvent::ROUTE_GEN_FAILED);
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.");
659 this->rs_worker_.onRouteEvent(RouteStateWorker::RouteEvent::ROUTE_GEN_FAILED);
660 publishRouteEvent(carma_planning_msgs::msg::RouteEvent::ROUTE_GEN_FAILED);
661 return true;
662 }
663 else
664 {
665 this->rs_worker_.onRouteEvent(RouteStateWorker::RouteEvent::ROUTE_STARTED);
666 publishRouteEvent(carma_planning_msgs::msg::RouteEvent::ROUTE_STARTED);
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 {
690 carma_planning_msgs::msg::RouteState state_msg;
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!");
707 route_event_queue.pop();
708 }
709 return true;
710 }
711
712 bool RouteGeneratorWorker::crosstrackErrorCheck(const std::shared_ptr<geometry_msgs::msg::PoseStamped>& msg, lanelet::ConstLanelet current)
713 {
714 lanelet::BasicPoint2d position;
715
716 position.x()= msg->pose.position.x;
717 position.y()= msg->pose.position.y;
718
719 if(boost::geometry::within(position, current.polygon2d())) //If vehicle is inside current_lanelet, there is no crosstrack error
720 {
721 cte_count_ = 0;
722 return false;
723 }
724
725 RCLCPP_DEBUG_STREAM(logger_->get_logger(), "LLt Polygon Dimensions1: " << current.polygon2d().front().x()<< ", "<< current.polygon2d().front().y());
726 RCLCPP_DEBUG_STREAM(logger_->get_logger(), "LLt Polygon Dimensions2: " << current.polygon2d().back().x()<< ", "<< current.polygon2d().back().y());
727 RCLCPP_DEBUG_STREAM(logger_->get_logger(), "Distance1: " << boost::geometry::distance(position, current.polygon2d()) << " Max allowed Crosstrack: " << cross_track_dist_ );
728
729 if (boost::geometry::distance(position, current.polygon2d()) > cross_track_dist_) //Evaluate lanelet crosstrack distance from vehicle
730 {
731 cte_count_++;
732
733 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
734 {
735 cte_count_ = 0;
736 return true;
737 }
738 else
739 return false;
740 }
741 else
742 {
743 cte_count_ = 0;
744 return false;
745 }
746
747 }
748
749
750
751 lanelet::ConstLanelet RouteGeneratorWorker::getClosestLaneletFromRouteLanelets(lanelet::BasicPoint2d position) const
752 {
753 double min = std::numeric_limits<double>::infinity();
754 lanelet::ConstLanelet min_llt;
755 for (const auto& i: route_llts)
756 {
757 double dist = boost::geometry::distance(position, i.polygon2d());
758 if (dist < min)
759 {
760 min = dist;
761 min_llt = i;
762 }
763 }
764 return min_llt;
765 }
766
768 {
769 cross_track_dist_ = cte_dist;
770 }
771
773 {
774 cte_count_max_ = cte_max;
775
776 }
777
778 void RouteGeneratorWorker::addLanelet(lanelet::ConstLanelet llt)
779 {
780 route_llts.push_back(llt);
781 }
782
783} // 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)
RouteState getRouteState() const
Get current route state machine state.
void setLoggerInterface(rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr logger)
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:452
std::shared_ptr< const lanelet::routing::RoutingGraph > LaneletRoutingGraphConstPtr
Definition: WorldModel.hpp:51