lanelet2_route_planning v2.0.0
Loading...
Searching...
No Matches
utils.cpp
Go to the documentation of this file.
1// Copyright Institute for Automotive Engineering (ika), RWTH Aachen University
2// SPDX-License-Identifier: Apache-2.0
3
4#include <algorithm>
5#include <cmath>
6#include <limits>
7#include <optional>
8#include <regex>
9#include <tuple>
10#include <unordered_map>
11#include <unordered_set>
12#include <utility>
13
14#include <lanelet2_core/geometry/LaneletMap.h>
15#include <lanelet2_core/utility/Units.h>
16#include <lanelet2_routing/Route.h>
17#include <lanelet2_traffic_rules/TrafficRulesFactory.h>
18#include <route_planning_msgs_utils/route_access.hpp>
19
23
25
26std::optional<lanelet::routing::Route> getRoute(const lanelet::routing::RoutingGraphUPtr& routing_graph,
27 const std::vector<lanelet::ConstLanelet>& route_lanelets) {
28 if (route_lanelets.empty()) {
29 return std::nullopt;
30 }
31
32 const lanelet::ConstLanelet& start_lanelet = route_lanelets.front();
33 std::vector<lanelet::ConstLanelet> intermediate_lanelets(route_lanelets.begin() + 1, route_lanelets.end() - 1);
34 const lanelet::ConstLanelet& destination_lanelet = route_lanelets.back();
35
36 // compute default route
37 const int routing_cost_id = 0; // RoutingCostDistance
38 const bool with_lane_changes = true;
39 auto route =
40 routing_graph->getRouteVia(start_lanelet, intermediate_lanelets, destination_lanelet, routing_cost_id, with_lane_changes);
41
42 // compute route alternatives with inverted start/destination lanelets (useful, if they are bidirectional)
43 auto route_alternative1 =
44 routing_graph->getRouteVia(start_lanelet.invert(), intermediate_lanelets, destination_lanelet, routing_cost_id);
45 auto route_alternative2 =
46 routing_graph->getRouteVia(start_lanelet, intermediate_lanelets, destination_lanelet.invert(), routing_cost_id);
47 auto route_alternative3 =
48 routing_graph->getRouteVia(start_lanelet.invert(), intermediate_lanelets, destination_lanelet.invert(), routing_cost_id);
49 std::vector<lanelet::routing::Route> route_alternatives;
50 if (route) route_alternatives.push_back(std::move(*route));
51 if (route_alternative1) route_alternatives.push_back(std::move(*route_alternative1));
52 if (route_alternative2) route_alternatives.push_back(std::move(*route_alternative2));
53 if (route_alternative3) route_alternatives.push_back(std::move(*route_alternative3));
54
55 // select shortest route
56 auto shortest_route_ptr = std::min_element(
57 route_alternatives.begin(), route_alternatives.end(),
58 [](const lanelet::routing::Route& a, const lanelet::routing::Route& b) { return a.length2d() < b.length2d(); });
59
60 if (shortest_route_ptr == route_alternatives.end()) {
61 return std::nullopt;
62 }
63 auto shortest_route = std::optional<lanelet::routing::Route>(std::move(*shortest_route_ptr));
64 return shortest_route;
65}
66
67size_t indexOfLineStringPointClosestToPoint(const std::vector<Eigen::Vector2d>& line_string,
68 const Eigen::Vector2d& point,
69 const bool consider_order,
70 const bool behind) {
71 if (line_string.empty()) {
72 return 0;
73 }
74
75 // loop over all points in line string to find closest one to given point
76 size_t idx_closest = 0;
77 double min_distance = std::numeric_limits<double>::infinity();
78 for (size_t i = 0; i < line_string.size(); ++i) {
79 double distance = (line_string[i] - point).norm();
80 if (distance < min_distance) {
81 min_distance = distance;
82 idx_closest = i;
83 }
84 }
85
86 // if considering order, make sure to return the point behind or ahead of the given point
87 if (consider_order) {
88 idx_closest = considerOrderForPointMatchedToLineString(line_string, point, idx_closest, behind);
89 }
90
91 return idx_closest;
92}
93
94size_t matchPointToLineString(const std::vector<Eigen::Vector2d>& line_string,
95 const Eigen::Vector2d& point,
96 const size_t idx_indication,
97 const bool consider_order,
98 const bool behind) {
99 if (line_string.empty()) {
100 return 0;
101 }
102
103 // constants
104 const double max_delta_s = 10.0;
105 const double max_local_distance = 10.0;
106
107 size_t idx_closest = 0;
108 double min_distance = std::numeric_limits<double>::infinity();
109 const size_t start_idx = std::min(idx_indication, line_string.size() - 1);
110
111 // loop over points in front of and behind the given index
112 for (int direction : {1, -1}) {
113 double delta_s = 0.0;
114 size_t idx = start_idx;
115 // only check points within the local range of max_delta_s
116 while (delta_s <= max_delta_s && idx < line_string.size()) {
117 double distance = (line_string[idx] - point).norm();
118 if (distance < min_distance) {
119 min_distance = distance;
120 idx_closest = idx;
121 }
122 if (direction == 1) {
123 if (idx + 1 >= line_string.size()) break;
124 delta_s += (line_string[idx + 1] - line_string[idx]).norm();
125 } else {
126 if (idx == 0) break;
127 delta_s += (line_string[idx] - line_string[idx - 1]).norm();
128 }
129 idx += direction;
130 }
131 }
132
133 // check if closest local point is within max distance, else find globally closest point
134 if (min_distance > max_local_distance) {
135 idx_closest = indexOfLineStringPointClosestToPoint(line_string, point, consider_order, behind);
136 } else if (consider_order) {
137 // if considering order, make sure to return the point behind or ahead of the given point
138 idx_closest = considerOrderForPointMatchedToLineString(line_string, point, idx_closest, behind);
139 }
140
141 return idx_closest;
142}
143
144size_t considerOrderForPointMatchedToLineString(const std::vector<Eigen::Vector2d>& line_string,
145 const Eigen::Vector2d& point,
146 const size_t idx_closest,
147 const bool behind) {
148 if (line_string.empty()) {
149 return 0;
150 }
151
152 size_t new_idx_closest = idx_closest;
153 Eigen::Vector2d closest_point_to_next;
154 const Eigen::Vector2d closest_point_to_point = point - line_string[idx_closest];
155 if (idx_closest + 1 < line_string.size()) {
156 closest_point_to_next = line_string[idx_closest + 1] - line_string[idx_closest];
157 } else if (idx_closest > 0) {
158 closest_point_to_next = line_string[idx_closest] - line_string[idx_closest - 1];
159 } else {
160 return idx_closest;
161 }
162
163 // use angle to check if closest point is behind or ahead of the given point
164 const double angle = angleBetweenVectors(closest_point_to_point, closest_point_to_next);
165 if (behind && std::abs(angle) > M_PI_2) {
166 new_idx_closest = idx_closest > 0 ? idx_closest - 1 : 0;
167 } else if (!behind && std::abs(angle) < M_PI_2) {
168 new_idx_closest = idx_closest + 1;
169 } else {
170 new_idx_closest = idx_closest;
171 }
172 new_idx_closest = std::clamp(new_idx_closest, size_t{0}, line_string.size() - 1);
173
174 return new_idx_closest;
175}
176
177bool changesLaneFromPointToPoint(const Eigen::Vector2d& point,
178 const Eigen::Vector2d& next_point,
179 const double sampling_distance) {
180 const double epsilon = 1e-6;
181 return ((next_point - point).norm() > (sampling_distance + epsilon));
182}
183
184std::vector<lanelet::ConstLanelet> adjacentLeftOrRightLanelets(const lanelet::ConstLanelet& lanelet,
185 const lanelet::routing::RoutingGraphUPtr& routing_graph,
186 bool left,
187 bool sort_from_left) {
188 std::vector<lanelet::ConstLanelet> adjacent_lanelets;
189 const int routing_cost_id = 0; // RoutingCostDistance
190 lanelet::routing::LaneletRelations relations =
191 left ? routing_graph->leftRelations(lanelet, routing_cost_id) : routing_graph->rightRelations(lanelet, routing_cost_id);
192 for (const auto& relation : relations) {
193 if ((left && (relation.relationType == lanelet::routing::RelationType::Left ||
194 relation.relationType == lanelet::routing::RelationType::AdjacentLeft)) ||
195 (!left && (relation.relationType == lanelet::routing::RelationType::Right ||
196 relation.relationType == lanelet::routing::RelationType::AdjacentRight))) {
197 adjacent_lanelets.push_back(relation.lanelet);
198 }
199 }
200
201 if ((left && sort_from_left) || (!left && !sort_from_left)) {
202 std::reverse(adjacent_lanelets.begin(), adjacent_lanelets.end());
203 }
204
205 return adjacent_lanelets;
206}
207
208std::vector<ProjectedLaneletPoints> projectPointToLaneletLines(const Eigen::Vector2d& point,
209 const Eigen::Vector2d& prev_point,
210 const Eigen::Vector2d& next_point,
211 const std::vector<lanelet::ConstLanelet>& lanelets,
212 const rclcpp::Logger& logger) {
213 std::vector<ProjectedLaneletPoints> projected_points_per_lanelet;
214
215 // loop over lanelets
216 for (const auto& lanelet : lanelets) {
217 ProjectedLaneletPoints projected_points;
218
219 // project point to left bounds
220 if (auto result = projectPointToLineStringAlongNormal(point, prev_point, next_point,
221 toEigen(lanelet.leftBound2d().basicLineString()))) {
222 projected_points.left_bound_point = result->projected_point;
223 } else {
224 RCLCPP_WARN(logger, "Failed to project point (%.3f, %.3f) to left bounds of lanelet %ld", point.x(), point.y(),
225 lanelet.id());
226 }
227
228 // project point to centerline
229 if (auto result = projectPointToLineStringAlongNormal(point, prev_point, next_point,
230 toEigen(lanelet.centerline2d().basicLineString()))) {
231 projected_points.centerline_point = result->projected_point;
232 } else {
233 RCLCPP_WARN(logger, "Failed to project point (%.3f, %.3f) to centerline of lanelet %ld", point.x(), point.y(),
234 lanelet.id());
235 }
236
237 // project point to right bounds
238 if (auto result = projectPointToLineStringAlongNormal(point, prev_point, next_point,
239 toEigen(lanelet.rightBound2d().basicLineString()))) {
240 projected_points.right_bound_point = result->projected_point;
241 } else {
242 RCLCPP_WARN(logger, "Failed to project point (%.3f, %.3f) to right bounds of lanelet %ld", point.x(), point.y(),
243 lanelet.id());
244 }
245
246 projected_points_per_lanelet.push_back(projected_points);
247 }
248
249 return projected_points_per_lanelet;
250}
251
252std::optional<int> computeFollowingLaneIdxOffset(const lanelet::ConstLanelet& lanelet,
253 const lanelet::ConstLanelet& lanelet_of_next_point,
254 const lanelet::routing::RoutingGraphUPtr& routing_graph) {
255 int following_lane_idx_offset = 0;
256 if (lanelet_of_next_point.id() != lanelet.id()) {
257 // get adjacent lanelets of current lanelet
258 std::vector<lanelet::ConstLanelet> adjacent_left_lanelets = adjacentLeftOrRightLanelets(lanelet, routing_graph, true);
259 std::vector<lanelet::ConstLanelet> adjacent_right_lanelets = adjacentLeftOrRightLanelets(lanelet, routing_graph, false);
260 const int suggested_lane_idx = static_cast<int>(adjacent_left_lanelets.size());
261
262 // get adjacent lanelets of next lanelet (lanelet of next point)
263 std::vector<lanelet::ConstLanelet> adjacent_left_lanelets_of_next_lanelet =
264 adjacentLeftOrRightLanelets(lanelet_of_next_point, routing_graph, true);
265 std::vector<lanelet::ConstLanelet> adjacent_right_lanelets_of_next_lanelet =
266 adjacentLeftOrRightLanelets(lanelet_of_next_point, routing_graph, false);
267 std::vector<lanelet::ConstLanelet> adjacent_lanelets_of_next_lanelet = adjacent_left_lanelets_of_next_lanelet;
268 adjacent_lanelets_of_next_lanelet.push_back(lanelet_of_next_point);
269 adjacent_lanelets_of_next_lanelet.insert(adjacent_lanelets_of_next_lanelet.end(),
270 adjacent_right_lanelets_of_next_lanelet.begin(),
271 adjacent_right_lanelets_of_next_lanelet.end());
272
273 // find following lanelet of current lanelet and adjacent lanelets in adjacent lanelets of next lanelet
274 std::vector<std::vector<lanelet::ConstLanelet>> lanelet_groups = {{lanelet}, adjacent_left_lanelets, adjacent_right_lanelets};
275 std::vector<int> lanelet_group_offset_factors = {0, 1, -1};
276 following_lane_idx_offset = std::numeric_limits<int>::max();
277
278 // first try to match following lanelet of current lanelet, then of adjacent left lanelets, then of adjacent right lanelets
279 for (size_t group_idx = 0; group_idx < lanelet_groups.size(); ++group_idx) {
280 const auto& lanelet_group = lanelet_groups[group_idx];
281 int group_offset_factor = lanelet_group_offset_factors[group_idx];
282
283 // loop over all lanelets in group
284 for (size_t a = 0; a < lanelet_group.size(); ++a) {
285 auto following_lanelets = routing_graph->following(lanelet_group[a], false);
286 if (following_lanelets.empty()) {
287 continue;
288 }
289 auto following_lanelet = following_lanelets.front();
290 size_t following_lanelet_idx = 0;
291 size_t follow_further_idx =
292 0; // counter for following lanelets more than once (e.g., if sampling skipped short lanelets)
293 size_t max_follow_further_iterations = 3; // maximum number of following lanelets to check
294
295 // loop until following lanelet is found in adjacent lanelets of next lanelet
296 while (following_lane_idx_offset == std::numeric_limits<int>::max()) {
297 // check all adjacent lanelets of next lanelet
298 for (size_t l = 0; l < adjacent_lanelets_of_next_lanelet.size(); ++l) {
299 if (following_lanelet.id() == adjacent_lanelets_of_next_lanelet[l].id()) {
300 following_lane_idx_offset =
301 static_cast<int>(l) - suggested_lane_idx + group_offset_factor * static_cast<int>(a + 1);
302 break;
303 }
304 }
305
306 // check abort conditions
307 follow_further_idx++;
308 if (follow_further_idx >= max_follow_further_iterations) {
309 break;
310 }
311
312 // get next following lanelet
313 following_lanelet_idx++;
314 if (following_lanelet_idx < following_lanelets.size()) {
315 following_lanelet = following_lanelets[following_lanelet_idx];
316 } else {
317 following_lanelets = routing_graph->following(following_lanelet, false);
318 if (following_lanelets.empty()) {
319 continue;
320 }
321 following_lanelet = following_lanelets.front();
322 following_lanelet_idx = 0;
323 }
324 }
325
326 if (following_lane_idx_offset != std::numeric_limits<int>::max()) {
327 break;
328 }
329 }
330
331 if (following_lane_idx_offset != std::numeric_limits<int>::max()) {
332 break;
333 }
334 }
335
336 if (following_lane_idx_offset == std::numeric_limits<int>::max()) {
337 // could not match following lanelets to adjacent lanelets of lanelet of next point
338 return std::nullopt;
339 }
340 }
341
342 return following_lane_idx_offset;
343}
344
345route_planning_msgs::msg::RouteElement createMinimalRouteElement(const geometry_msgs::msg::Point& position,
346 const geometry_msgs::msg::Quaternion& orientation,
347 double s,
348 bool will_change_suggested_lane,
349 uint8_t speed_limit) {
350 // create RouteElement
351 route_planning_msgs::msg::RouteElement route_element_msg;
352 route_element_msg.suggested_lane_idx = 0;
353 route_element_msg.will_change_suggested_lane = will_change_suggested_lane;
354 route_element_msg.s = s;
355 route_element_msg.is_enriched = false;
356 // route_element_msg.left_boundary not set in global route
357 // route_element_msg.right_boundary not set in global route
358 // route_element_msg.regulatory_elements not set in global route
359
360 // create LaneElement
361 route_planning_msgs::msg::LaneElement lane_element_msg;
362 lane_element_msg.reference_pose.position = position;
363 lane_element_msg.reference_pose.orientation = orientation;
364 // lane_element_msg.left_boundary not set in global route
365 // lane_element_msg.right_boundary not set in global route
366 lane_element_msg.speed_limit = speed_limit;
367 // lane_element_msg.regulatory_element_idcs not set in global route
368 lane_element_msg.following_lane_idx = 0;
369 lane_element_msg.has_following_lane_idx = !will_change_suggested_lane;
370 route_element_msg.lane_elements.push_back(lane_element_msg);
371
372 return route_element_msg;
373}
374
375std::pair<Eigen::Vector2d, Eigen::Vector2d> extractDrivableSpace(const lanelet::LineStringLayer& line_string_layer,
376 const PointSequence& point_sequence,
377 const double max_distance) {
378 // find all line strings within max_distance
379 const lanelet::BasicLineString2d line_to_search_around = {point_sequence.current, point_sequence.next};
380 std::vector<std::pair<double, lanelet::ConstLineString3d>> line_strings_and_distances =
381 lanelet::geometry::findWithin2d(line_string_layer, line_to_search_around, max_distance);
382
383 // collect all intersections of the normal through the current point with nearby line strings
384 const std::vector<Eigen::Vector2d> normal_line = {
385 point_sequence.current,
386 point_sequence.current + normalOfPointAlongLineString(point_sequence.current, point_sequence.prev, point_sequence.next)};
387 std::vector<std::pair<Eigen::Vector2d, size_t>> projected_points_and_line_string_idcs;
388 for (size_t l = 0; l < line_strings_and_distances.size(); ++l) {
389 const auto& line_string = line_strings_and_distances[l].second;
390 const std::vector<Eigen::Vector2d> line_string_2d = to2d(toEigen(line_string.basicLineString()));
391 for (size_t i = 1; i < line_string_2d.size(); ++i) {
392 const std::vector<Eigen::Vector2d> line_segment = {line_string_2d[i - 1], line_string_2d[i]};
393 auto intersection = intersectionOfLines(normal_line, line_segment);
394 if (!intersection || !intersection->intersects_line2) {
395 continue;
396 }
397
398 const Eigen::Vector2d& projected_point = intersection->intersection;
399 if ((projected_point - point_sequence.current).norm() > max_distance) {
400 continue;
401 }
402
403 projected_points_and_line_string_idcs.emplace_back(projected_point, l);
404 }
405 }
406
407 // sort projected points by distance to current point
408 std::sort(projected_points_and_line_string_idcs.begin(), projected_points_and_line_string_idcs.end(),
409 [&point_sequence](const auto& a, const auto& b) {
410 return (a.first - point_sequence.current).norm() < (b.first - point_sequence.current).norm();
411 });
412
413 // split projected points into those left and those right of current point
414 const Eigen::Vector2d tangent = tangentOfPointAlongLineString(point_sequence.current, point_sequence.prev, point_sequence.next);
415 std::vector<std::pair<Eigen::Vector2d, size_t>> left_projected_points_and_line_string_idcs,
416 right_projected_points_and_line_string_idcs;
417 for (const auto& projected_point_and_line_string_idx : projected_points_and_line_string_idcs) {
418 // determine left/right based on angle between tangent and vector to projected point
419 const Eigen::Vector2d point_to_projected_point = projected_point_and_line_string_idx.first - point_sequence.current;
420 const double angle = angleBetweenVectors(tangent, point_to_projected_point);
421 if (angle > 0) {
422 left_projected_points_and_line_string_idcs.emplace_back(projected_point_and_line_string_idx);
423 } else {
424 right_projected_points_and_line_string_idcs.emplace_back(projected_point_and_line_string_idx);
425 }
426 }
427
428 // find drivable space bounds by following projected points until corresponding line string is not passable anymore
429 Eigen::Vector2d drivable_space_left, drivable_space_right;
430 bool is_drivable_space_left_limited_by_line_strings = false;
431 bool is_drivable_space_right_limited_by_line_strings = false;
432 for (const auto& projected_point_and_line_string_idx : left_projected_points_and_line_string_idcs) {
433 const auto& line_string = line_strings_and_distances[projected_point_and_line_string_idx.second].second;
434 if (!isLineStringDrivable(line_string)) {
435 drivable_space_left = projected_point_and_line_string_idx.first;
436 is_drivable_space_left_limited_by_line_strings = true;
437 break;
438 }
439 }
440 for (const auto& projected_point_and_line_string_idx : right_projected_points_and_line_string_idcs) {
441 const auto& line_string = line_strings_and_distances[projected_point_and_line_string_idx.second].second;
442 if (!isLineStringDrivable(line_string)) {
443 drivable_space_right = projected_point_and_line_string_idx.first;
444 is_drivable_space_right_limited_by_line_strings = true;
445 break;
446 }
447 }
448
449 // if drivable space is not limited by line strings, use maximum distance
450 const Eigen::Vector2d normal = normalOfPointAlongLineString(point_sequence.current, point_sequence.prev, point_sequence.next);
451 if (!is_drivable_space_left_limited_by_line_strings) {
452 drivable_space_left = point_sequence.current - normal * max_distance;
453 }
454 if (!is_drivable_space_right_limited_by_line_strings) {
455 drivable_space_right = point_sequence.current + normal * max_distance;
456 }
457
458 return {drivable_space_left, drivable_space_right};
459}
460
461bool isLineStringDrivable(const lanelet::ConstLineString3d& line_string) {
462 const std::unordered_set<std::string> drivable_types = {"arrow", "bike_marking", "centerline", "curbstone",
463 "lane_center", "line_thick", "line_thin", "pedestrian_marking",
464 "roadpainting", "stop_line", "traffic_light", "virtual",
465 "zebra_marking"};
466 if (line_string.hasAttribute("type")) {
467 std::string type = line_string.attribute("type").value();
468 if (drivable_types.count(type) > 0) {
469 if (type == "curbstone") {
470 if (!line_string.hasAttribute("subtype") || line_string.attribute("subtype").value() != "low") {
471 return false;
472 }
473 }
474 return true;
475 }
476 return line_string.hasAttribute("HoldingLine");
477 }
478 return false;
479}
480
482 const std::vector<lanelet::ConstLanelet>& adjacent_left_lanelets,
483 const std::vector<lanelet::ConstLanelet>& adjacent_right_lanelets,
484 const PointSequence& point_sequence) {
485 // init result
487 result.adjacent_left_regulatory_element_idcs.resize(adjacent_left_lanelets.size());
488 result.adjacent_right_regulatory_element_idcs.resize(adjacent_right_lanelets.size());
489
490 // gather lanelets in single vector (left adjacent, current, right adjacent)
491 std::vector<lanelet::ConstLanelet> lanelets = adjacent_left_lanelets;
492 lanelets.push_back(lanelet);
493 lanelets.insert(lanelets.end(), adjacent_right_lanelets.begin(), adjacent_right_lanelets.end());
494
495 // loop over lanelets
496 std::unordered_map<size_t, size_t> regulatory_element_msg_idx_by_id;
497 for (size_t l = 0; l < lanelets.size(); ++l) {
498 const auto& current_lanelet = lanelets[l];
499
500 // loop over regulatory elements of lanelet
501 const auto regulatory_elements = current_lanelet.regulatoryElements();
502 for (const auto& regulatory_element : regulatory_elements) {
503 // create RegulatoryElement
504 route_planning_msgs::msg::RegulatoryElement regulatory_element_msg;
505 regulatory_element_msg.has_validity_stamp = false;
506 regulatory_element_msg.validity_stamp = builtin_interfaces::msg::Time();
507
508 // extract reference line
509 if (auto reference_line = regulatoryElementReferenceLine(regulatory_element)) {
510 regulatory_element_msg.reference_line = *reference_line;
511
512 // only consider regulatory element if reference line intersects with point sequence
513 std::vector<Eigen::Vector2d> reference_line_2d = {toEigen2d(reference_line->at(0)), toEigen2d(reference_line->at(1))};
514 std::vector<Eigen::Vector2d> line_to_next_point = {point_sequence.current, point_sequence.next};
515 std::vector<Eigen::Vector2d> line_to_prev_point = {point_sequence.current, point_sequence.prev};
516 if (auto result = intersectionOfLines(reference_line_2d, line_to_next_point)) {
517 if (!result->intersects_line2) {
518 if (auto inner_result = intersectionOfLines(reference_line_2d, line_to_prev_point)) {
519 if (!inner_result->intersects_line2) {
520 continue;
521 }
522 }
523 }
524 }
525 } else {
526 continue;
527 }
528
529 // extract sign positions and type
530 regulatory_element_msg.positions = regulatoryElementPositions(regulatory_element);
531 std::tie(regulatory_element_msg.type, regulatory_element_msg.meta_value) = regulatoryElementType(regulatory_element);
532
533 // check if regulatory element has already been extracted (by another lanelet)
534 size_t regulatory_element_msg_idx = 0;
535 if (regulatory_element_msg_idx_by_id.count(regulatory_element->id()) > 0) {
536 regulatory_element_msg_idx = regulatory_element_msg_idx_by_id[regulatory_element->id()];
537 } else {
538 // add regulatory element to result
539 regulatory_element_msg_idx = result.regulatory_element_msgs.size();
540 regulatory_element_msg_idx_by_id[regulatory_element->id()] = regulatory_element_msg_idx;
541 result.regulatory_element_msgs.push_back(regulatory_element_msg);
542 }
543
544 // assign regulatory element to respective lanelet in result
545 if (l < adjacent_left_lanelets.size()) {
546 result.adjacent_left_regulatory_element_idcs[l].push_back(regulatory_element_msg_idx);
547 } else if (l < adjacent_left_lanelets.size() + 1) {
548 result.regulatory_element_idcs.push_back(regulatory_element_msg_idx);
549 } else {
550 size_t l_right = l - adjacent_left_lanelets.size() - 1;
551 result.adjacent_right_regulatory_element_idcs[l_right].push_back(regulatory_element_msg_idx);
552 }
553 }
554 }
555
556 return result;
557}
558
559std::optional<std::array<geometry_msgs::msg::Point, 2>> regulatoryElementReferenceLine(
560 const std::shared_ptr<const lanelet::RegulatoryElement>& regulatory_element) {
561 const std::vector<lanelet::ConstLineString3d> reference_lines =
562 regulatory_element->getParameters<lanelet::ConstLineString3d>(lanelet::RoleName::RefLine);
563 if (reference_lines.empty()) {
564 return std::nullopt;
565 }
566 const std::vector<Eigen::Vector3d> reference_line = reference_lines.front().basicLineString();
567 if (reference_line.size() < 2) {
568 return std::nullopt;
569 }
570 std::array<geometry_msgs::msg::Point, 2> reference_line_ros = {toRos(reference_line.front()), toRos(reference_line.back())};
571 return reference_line_ros;
572}
573
574std::optional<std::array<geometry_msgs::msg::Point, 2>> regulatoryElementCancelLine(
575 const std::shared_ptr<const lanelet::RegulatoryElement>& regulatory_element) {
576 const std::vector<lanelet::ConstLineString3d> cancel_lines =
577 regulatory_element->getParameters<lanelet::ConstLineString3d>(lanelet::RoleName::CancelLine);
578 if (cancel_lines.empty()) {
579 return std::nullopt;
580 }
581 const std::vector<Eigen::Vector3d> cancel_line = cancel_lines.front().basicLineString();
582 if (cancel_line.size() < 2) {
583 return std::nullopt;
584 }
585 std::array<geometry_msgs::msg::Point, 2> cancel_line_ros = {toRos(cancel_line.front()), toRos(cancel_line.back())};
586 return cancel_line_ros;
587}
588
589std::vector<geometry_msgs::msg::Point> regulatoryElementPositions(
590 const std::shared_ptr<const lanelet::RegulatoryElement>& regulatory_element) {
591 std::vector<geometry_msgs::msg::Point> positions;
592 const std::vector<lanelet::ConstLineString3d> sign_lines =
593 regulatory_element->getParameters<lanelet::ConstLineString3d>(lanelet::RoleName::Refers);
594 for (const auto& const_sign_line : sign_lines) {
595 const std::vector<Eigen::Vector3d> sign_line = const_sign_line.basicLineString();
596 if (!sign_line.empty()) {
597 positions.push_back(toRos(sign_line.front()));
598 }
599 }
600 return positions;
601}
602
603std::pair<uint8_t, uint8_t> regulatoryElementType(const std::shared_ptr<const lanelet::RegulatoryElement>& regulatory_element) {
604 uint8_t type = route_planning_msgs::msg::RegulatoryElement::TYPE_UNKNOWN;
605 uint8_t meta_value = 0;
606
607 // https://github.com/fzi-forschungszentrum-informatik/Lanelet2/blob/master/lanelet2_core/doc/RegulatoryElementTagging.md
608 if (regulatory_element->hasAttribute("subtype")) {
609 std::string subtype = regulatory_element->attribute("subtype").value();
610 if (subtype == "traffic_light") {
611 type = route_planning_msgs::msg::RegulatoryElement::TYPE_TRAFFIC_LIGHT;
612 } else if (subtype == "speed_limit") {
613 type = route_planning_msgs::msg::RegulatoryElement::TYPE_SPEED_LIMIT;
614 meta_value = regulatoryElementSpeedLimit(regulatory_element);
615 } else if (subtype == "right_of_way") {
616 type = route_planning_msgs::msg::RegulatoryElement::TYPE_YIELD;
617 const auto traffic_signs = regulatory_element->getParameters<lanelet::ConstLineString3d>(lanelet::RoleName::Refers);
618 const bool has_stop_sign = std::any_of(traffic_signs.begin(), traffic_signs.end(), [](const auto& traffic_sign) {
619 return traffic_sign.hasAttribute("subtype") && traffic_sign.attribute("subtype").value() == "de206";
620 });
621 if (has_stop_sign) {
622 type = route_planning_msgs::msg::RegulatoryElement::TYPE_STOP;
623 }
624 } else if (subtype == "all_way_stop") {
625 type = route_planning_msgs::msg::RegulatoryElement::TYPE_STOP;
626 }
627 }
628 return {type, meta_value};
629}
630
631uint8_t regulatoryElementSpeedLimit(const std::shared_ptr<const lanelet::RegulatoryElement>& regulatory_element) {
632 uint8_t speed_limit = route_planning_msgs::msg::RegulatoryElement::META_VALUE_SPEED_UNKNOWN;
633
634 // https://github.com/fzi-forschungszentrum-informatik/Lanelet2/blob/master/lanelet2_core/doc/RegulatoryElementTagging.md#speed-limit
635 if (regulatory_element->hasAttribute("subtype")) {
636 std::string subtype = regulatory_element->attribute("subtype").value();
637 if (subtype == "speed_limit") {
638 if (regulatory_element->hasAttribute("sign_type")) {
639 std::string sign_type = regulatory_element->attribute("sign_type").value();
640 std::smatch match;
641 std::regex regex("(\\d+)\\s*(km/h|mph|mps)");
642 if (std::regex_search(sign_type, match, regex)) {
643 int speed = std::stoi(match.str(1));
644 if (match.str(2) == "mph") {
645 speed = static_cast<int>(speed * 1.60934); // mph to km/h
646 } else if (match.str(2) == "mps") {
647 speed = static_cast<int>(speed * 3.6); // mps to km/h
648 }
649 speed_limit = static_cast<uint8_t>(speed);
650 } else {
651 std::regex regex("(\\d+)"); // assume km/h if no unit is specified
652 if (std::regex_search(sign_type, match, regex)) {
653 speed_limit = static_cast<uint8_t>(std::stoi(match.str(1)));
654 }
655 }
656 }
657 }
658 }
659
660 uint8_t unlimited = route_planning_msgs::msg::RegulatoryElement::META_VALUE_SPEED_UNLIMITED;
661 speed_limit = std::clamp(speed_limit, static_cast<uint8_t>(0), unlimited);
662
663 return speed_limit;
664}
665
666uint8_t laneBoundaryType(const lanelet::ConstLineString2d& line) {
667 uint8_t lane_boundary_type = route_planning_msgs::msg::LaneBoundary::TYPE_UNKNOWN;
668
669 // get type attribute
670 lanelet::Attribute type;
671 if (line.hasAttribute("type")) {
672 type = line.attribute("type");
673 } else {
674 lane_boundary_type = route_planning_msgs::msg::LaneBoundary::TYPE_UNKNOWN;
675 return lane_boundary_type;
676 }
677
678 // map lanelet type to lane boundary type
679 if (type == "road_boarder" || type == "barrier") {
680 lane_boundary_type = route_planning_msgs::msg::LaneBoundary::TYPE_CROSSING_RESTRICTED;
681 } else if (type == "line_thin" || type == "line_thick") {
682 lane_boundary_type = route_planning_msgs::msg::LaneBoundary::TYPE_UNKNOWN;
683 if (line.hasAttribute("subtype")) {
684 lanelet::Attribute subtype = line.attribute("subtype");
685 if (subtype == "solid" || subtype == "solid_solid") {
686 lane_boundary_type = route_planning_msgs::msg::LaneBoundary::TYPE_CROSSING_RESTRICTED;
687 } else if (subtype == "dashed") {
688 lane_boundary_type = route_planning_msgs::msg::LaneBoundary::TYPE_CROSSING_ALLOWED;
689 } else if (subtype == "dashed_solid") {
690 lane_boundary_type = route_planning_msgs::msg::LaneBoundary::TYPE_CROSSING_ALLOWED_FROM_LEFT;
691 } else if (subtype == "solid_dashed") {
692 lane_boundary_type = route_planning_msgs::msg::LaneBoundary::TYPE_CROSSING_ALLOWED_FROM_RIGHT;
693 }
694 }
695 } else if (type == "virtual") {
696 lane_boundary_type = route_planning_msgs::msg::LaneBoundary::TYPE_UNKNOWN;
697 } else {
698 lane_boundary_type = route_planning_msgs::msg::LaneBoundary::TYPE_UNKNOWN;
699 }
700
701 return lane_boundary_type;
702}
703
704uint8_t speedLimit(const lanelet::ConstLanelet& lanelet, const bool consider_regulatory_elements) {
705 lanelet::traffic_rules::TrafficRulesPtr traffic_rules = getTrafficRules();
706 lanelet::traffic_rules::SpeedLimitInformation speed_limit_info;
707 if (consider_regulatory_elements) {
708 speed_limit_info = traffic_rules->speedLimit(lanelet);
709 } else {
710 uint8_t unlimited = route_planning_msgs::msg::RegulatoryElement::META_VALUE_SPEED_UNLIMITED;
711 if (!lanelet.hasAttribute(lanelet::AttributeName::SpeedLimit)) {
712 return unlimited;
713 }
714 auto speed_limit = lanelet.attribute(lanelet::AttributeName::SpeedLimit).asVelocity();
715 if (!speed_limit) {
716 return unlimited;
717 }
718 bool is_mandatory = true;
719 const std::string speed_limit_mandatory_attribute =
720 lanelet::AttributeNamesString::SpeedLimitMandatory; // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
721 if (lanelet.hasAttribute(speed_limit_mandatory_attribute)) {
722 is_mandatory = lanelet.attribute(speed_limit_mandatory_attribute).value() != std::string("no");
723 }
724 speed_limit_info = lanelet::traffic_rules::SpeedLimitInformation{*speed_limit, is_mandatory};
725 }
726 uint8_t unlimited = route_planning_msgs::msg::RegulatoryElement::META_VALUE_SPEED_UNLIMITED;
727 if (speed_limit_info.isMandatory) {
728 int speed_limit = static_cast<int>(std::round(lanelet::units::KmHQuantity(speed_limit_info.speedLimit).value()));
729 speed_limit = std::clamp(speed_limit, 0, static_cast<int>(unlimited));
730 return static_cast<uint8_t>(speed_limit);
731 }
732 return unlimited;
733}
734
735uint8_t speedLimit(const lanelet::ConstLanelet& lanelet, const Eigen::Vector2d& point) {
736 uint8_t speed_limit = route_planning_msgs::msg::RegulatoryElement::META_VALUE_SPEED_UNLIMITED;
737 std::vector<Eigen::Vector2d> centerline = toEigen(lanelet.centerline2d().basicLineString());
738 double best_reference_arc_length = 0.0;
739 bool found_valid_regulatory_element_speed_limit = false;
740 bool found_future_reference_line = false;
741 bool found_past_cancel_line = false;
742
743 // loop over regulatory elements of lanelet
744 const auto regulatory_elements = lanelet.regulatoryElements();
745 for (const auto& regulatory_element : regulatory_elements) {
746 // extract type and meta value, skip non-speed-limit regulatory elements
747 uint8_t regulatory_element_type = route_planning_msgs::msg::RegulatoryElement::TYPE_UNKNOWN;
748 uint8_t regulatory_element_meta_value = route_planning_msgs::msg::RegulatoryElement::META_VALUE_UNKNOWN;
749 std::tie(regulatory_element_type, regulatory_element_meta_value) = regulatoryElementType(regulatory_element);
750 if (regulatory_element_type != route_planning_msgs::msg::RegulatoryElement::TYPE_SPEED_LIMIT) {
751 continue;
752 }
753
754 // extract reference line
755 if (auto reference_line = regulatoryElementReferenceLine(regulatory_element)) {
756 std::vector<Eigen::Vector2d> reference_line_2d = {toEigen2d(reference_line->at(0)), toEigen2d(reference_line->at(1))};
757
758 // check if reference line intersects lanelet centerline
759 bool reference_line_intersects_centerline = false;
760 double reference_arc_length = 0.0;
761 for (size_t i = 0; i < centerline.size() - 1; ++i) {
762 std::vector<Eigen::Vector2d> centerline_segment = {centerline[i], centerline[i + 1]};
763 if (auto result = intersectionOfLines(reference_line_2d, centerline_segment)) {
764 if (result->intersects_line1 && result->intersects_line2) {
765 reference_arc_length =
766 lanelet::geometry::toArcCoordinates(lanelet.centerline2d(), toLanelet(result->intersection)).length;
767 reference_line_intersects_centerline = true;
768 break;
769 }
770 }
771 }
772
773 // check if cancel line intersects lanelet centerline
774 bool cancel_line_intersects_centerline = false;
775 double cancel_arc_length = 0.0;
776 if (auto cancel_line = regulatoryElementCancelLine(regulatory_element)) {
777 std::vector<Eigen::Vector2d> cancel_line_2d = {toEigen2d(cancel_line->at(0)), toEigen2d(cancel_line->at(1))};
778 for (size_t i = 0; i < centerline.size() - 1; ++i) {
779 std::vector<Eigen::Vector2d> centerline_segment = {centerline[i], centerline[i + 1]};
780 if (auto result = intersectionOfLines(cancel_line_2d, centerline_segment)) {
781 if (result->intersects_line1 && result->intersects_line2) {
782 cancel_arc_length =
783 lanelet::geometry::toArcCoordinates(lanelet.centerline2d(), toLanelet(result->intersection)).length;
784 cancel_line_intersects_centerline = true;
785 break;
786 }
787 }
788 }
789 }
790
791 // if given point is behind reference line and before cancel line, use speed limit of regulatory element
792 double point_arc_length = lanelet::geometry::toArcCoordinates(lanelet.centerline2d(), toLanelet(point)).length;
793 bool point_is_behind_reference_line = (!reference_line_intersects_centerline || (point_arc_length >= reference_arc_length));
794 bool point_is_before_cancel_line = (!cancel_line_intersects_centerline || (point_arc_length < cancel_arc_length));
795 if (!point_is_behind_reference_line) {
796 found_future_reference_line = true;
797 }
798 if (!point_is_before_cancel_line) {
799 found_past_cancel_line = true;
800 }
801 if (point_is_behind_reference_line && point_is_before_cancel_line) {
802 if (!found_valid_regulatory_element_speed_limit || reference_arc_length > best_reference_arc_length) {
803 // only update speed limit if regulatory element is ahead of previously found regulatory element
804 // (in case of multiple speed limit regulatory elements on lanelet)
805 speed_limit = regulatory_element_meta_value;
806 best_reference_arc_length = reference_arc_length;
807 found_valid_regulatory_element_speed_limit = true;
808 }
809 }
810
811 } else {
812 continue;
813 }
814 }
815
816 // if no valid regulatory element speed limit is found, fall back to lanelet speed limit;
817 // that function will still yield regulatory-element-based speed limits, if related to the lanelet
818 // even though there are no intersecting reference lines
819 if (!found_valid_regulatory_element_speed_limit) {
820 if (found_future_reference_line || found_past_cancel_line) {
821 speed_limit = speedLimit(lanelet, false);
822 } else {
823 speed_limit = speedLimit(lanelet, true);
824 }
825 }
826
827 speed_limit =
828 std::clamp(speed_limit, static_cast<uint8_t>(0), route_planning_msgs::msg::RegulatoryElement::META_VALUE_SPEED_UNLIMITED);
829
830 return speed_limit;
831}
832
833std::tuple<uint8_t, int> suggestedTurnSignal(const lanelet::ConstLanelet& lanelet, const rclcpp::Logger& logger) {
834 uint8_t suggested_turn_signal = route_planning_msgs::msg::LaneElement::SUGGESTED_TURN_SIGNAL_NONE;
835 int suggested_turn_signal_distance_ahead = -1;
836
837 // parse suggested turn signal attribute
838 if (lanelet.hasAttribute("suggested_turn_signal")) {
839 std::string suggested_turn_signal_str = lanelet.attribute("suggested_turn_signal").value();
840 if (suggested_turn_signal_str == "left") {
841 suggested_turn_signal = route_planning_msgs::msg::LaneElement::SUGGESTED_TURN_SIGNAL_LEFT;
842 } else if (suggested_turn_signal_str == "right") {
843 suggested_turn_signal = route_planning_msgs::msg::LaneElement::SUGGESTED_TURN_SIGNAL_RIGHT;
844 } else if (suggested_turn_signal_str == "hazard") {
845 suggested_turn_signal = route_planning_msgs::msg::LaneElement::SUGGESTED_TURN_SIGNAL_HAZARD;
846 } else {
847 suggested_turn_signal = route_planning_msgs::msg::LaneElement::SUGGESTED_TURN_SIGNAL_NONE;
848 RCLCPP_ERROR(logger, "Could not parse 'suggested_turn_signal' attribute value of lanelet '%ld': '%s'", lanelet.id(),
849 suggested_turn_signal_str.c_str());
850 return std::make_tuple(suggested_turn_signal, suggested_turn_signal_distance_ahead);
851 }
852
853 // parse suggested turn signal distance ahead attribute
854 if (lanelet.hasAttribute("suggested_turn_signal_distance_ahead")) {
855 std::string suggested_turn_signal_distance_ahead_str = lanelet.attribute("suggested_turn_signal_distance_ahead").value();
856 try {
857 suggested_turn_signal_distance_ahead = std::stoi(suggested_turn_signal_distance_ahead_str);
858 } catch (const std::exception&) {
859 RCLCPP_ERROR(logger, "Could not parse 'suggested_turn_signal_distance_ahead' attribute value of lanelet '%ld': '%s'",
860 lanelet.id(), suggested_turn_signal_distance_ahead_str.c_str());
861 return std::make_tuple(suggested_turn_signal, suggested_turn_signal_distance_ahead);
862 }
863 if (suggested_turn_signal_distance_ahead < 0) {
864 RCLCPP_ERROR(logger,
865 "'suggested_turn_signal_distance_ahead' attribute value of lanelet '%ld' is negative, clamping to 0: %d",
866 lanelet.id(), suggested_turn_signal_distance_ahead);
867 suggested_turn_signal_distance_ahead = 0;
868 }
869 } else {
870 RCLCPP_ERROR(logger,
871 "Lanelet '%ld' has 'suggested_turn_signal' attribute but no 'suggested_turn_signal_distance_ahead', ignoring "
872 "suggested turn signal",
873 lanelet.id());
874 }
875 }
876 return std::make_tuple(suggested_turn_signal, suggested_turn_signal_distance_ahead);
877}
878
879lanelet::traffic_rules::TrafficRulesPtr getTrafficRules() {
880 auto location = lanelet::Locations::Germany; // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
881 auto vehicle_type = std::string(lanelet::Participants::Vehicle); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
882 return lanelet::traffic_rules::TrafficRulesFactory::create(location, vehicle_type);
883}
884
885std::optional<lanelet::ConstLanelet> laneletAtPoint(const Eigen::Vector2d& point,
886 const lanelet::LaneletMapConstPtr& map,
887 const std::optional<lanelet::traffic_rules::TrafficRulesPtr> traffic_rules) {
888 // parameters for lanelet matching
889 const unsigned int number_of_nearest_lanelets = 5;
890 const double max_lanelet_matching_distance = 5.0;
891
892 // find nearest lanelets
893 std::vector<std::pair<double, lanelet::ConstLanelet>> nearest_lanelets =
894 lanelet::geometry::findNearest(map->laneletLayer, point, number_of_nearest_lanelets);
895
896 // find best matching lanelet
897 if (traffic_rules) {
898 sortLaneletsByMatchingCost(point, nearest_lanelets, traffic_rules);
899 for (const auto& nearest_lanelet : nearest_lanelets) {
900 if (nearest_lanelet.first <= max_lanelet_matching_distance && traffic_rules.value()->canPass(nearest_lanelet.second)) {
901 return nearest_lanelet.second;
902 }
903 }
904 } else if (!nearest_lanelets.empty()) {
905 sortLaneletsByMatchingCost(point, nearest_lanelets);
906 if (nearest_lanelets[0].first <= max_lanelet_matching_distance) {
907 return nearest_lanelets[0].second;
908 }
909 }
910
911 return std::nullopt;
912}
913
914lanelet::ConstLanelet followLaneletsAlongRoutingGraph(const lanelet::routing::RoutingGraphUPtr& routing_graph,
915 const lanelet::ConstLanelet& lanelet,
916 const Eigen::Vector2d& position,
917 const double distance) {
918 lanelet::ConstLanelet followed_lanelet = lanelet;
919 double remaining_length = 0.0;
920 if (distance > 0) {
921 remaining_length = static_cast<double>(lanelet::geometry::length(lanelet.centerline2d())) -
922 lanelet::geometry::toArcCoordinates(lanelet.centerline2d(), position).length;
923 } else {
924 remaining_length = lanelet::geometry::toArcCoordinates(lanelet.centerline2d(), position).length;
925 }
926 double remaining_distance = std::abs(distance);
927
928 while (remaining_distance > remaining_length) {
929 lanelet::ConstLanelets next_lanelets;
930 if (distance > 0) {
931 next_lanelets = routing_graph->following(followed_lanelet, false);
932 } else {
933 next_lanelets = routing_graph->previous(followed_lanelet);
934 }
935 if (next_lanelets.empty()) {
936 break;
937 }
938 followed_lanelet = next_lanelets.front();
939 remaining_distance -= remaining_length;
940 remaining_length = static_cast<double>(lanelet::geometry::length(followed_lanelet.centerline2d()));
941 }
942
943 return followed_lanelet;
944}
945
946ResampleCenterlinesAlongPathResult resampleCenterlinesAlongPath(const lanelet::routing::LaneletPath& path,
947 const double delta_s,
948 bool monotonically) {
949 // init variables
951 double resampling_offset = 0.0;
952 Eigen::Vector2d prev_sampled_point = Eigen::Vector2d(0.0, 0.0);
953 Eigen::Vector2d prev_sampled_point_orientation = Eigen::Vector2d(0.0, 0.0);
954
955 // loop over lanelets in path
956 for (size_t l = 0; l < path.size(); ++l) {
957 // get centerline
958 const lanelet::ConstLanelet& lanelet = path[l];
959 lanelet::BasicLineString3d centerline = lanelet.centerline().basicLineString();
960
961 // skip point if behind previous sampled point, e.g., if on adjacent lanelet in shortest path due to lane change
962 if (monotonically && !result.centerline.empty()) {
963 for (auto cit = centerline.begin(); cit != centerline.end();) {
964 auto& centerline_point = *cit;
965 if ((to2d(centerline_point) - prev_sampled_point).dot(prev_sampled_point_orientation) < 0) { // angle > 90deg
966 cit = centerline.erase(cit);
967 } else {
968 break;
969 }
970 }
971 }
972
973 // resample lanelet centerline
974 std::vector<Eigen::Vector3d> resampled_centerline = resampleLineString(toEigen(centerline), delta_s, resampling_offset);
975 result.centerline.insert(result.centerline.end(), resampled_centerline.begin(), resampled_centerline.end());
976 result.lanelet_idx_by_point.insert(result.lanelet_idx_by_point.end(), resampled_centerline.size(), l);
977
978 // update information for monotonicity check
979 if (monotonically && !resampled_centerline.empty()) {
980 if (resampled_centerline.size() > 1) {
981 prev_sampled_point = to2d(resampled_centerline[resampled_centerline.size() - 2]);
982 }
983 const Eigen::Vector2d current_sampled_point = to2d(resampled_centerline.back());
984 prev_sampled_point_orientation =
985 tangentOfPointAlongLineString(current_sampled_point, prev_sampled_point, current_sampled_point);
986 prev_sampled_point = current_sampled_point;
987 }
988 }
989
990 return result;
991}
992
993double distanceTraveled(const route_planning_msgs::msg::Route& route) {
994 if (route.current_route_element_idx >= route.route_elements.size() ||
995 route.starting_route_element_idx >= route.route_elements.size()) {
996 return 0.0;
997 }
998 double to_current = route.route_elements[route.current_route_element_idx].s;
999 double ahead_of_starting_point = route.route_elements[route.starting_route_element_idx].s;
1000 double traveled = to_current - ahead_of_starting_point;
1001 return traveled;
1002}
1003
1004double distanceRemaining(const route_planning_msgs::msg::Route& route) {
1005 if (route.current_route_element_idx >= route.route_elements.size() ||
1006 route.destination_route_element_idx >= route.route_elements.size()) {
1007 return 0.0;
1008 }
1009 double to_current = route.route_elements[route.current_route_element_idx].s;
1010 double to_destination = route.route_elements[route.destination_route_element_idx].s;
1011 double remaining = to_destination - to_current;
1012 return remaining;
1013}
1014
1015double estimateRemainingTime(const route_planning_msgs::msg::Route& route, const double reference_speed) {
1016 double remaining_time = 0.0;
1017 for (size_t r = route.current_route_element_idx; r < route.route_elements.size() - 1; ++r) {
1018 const auto& route_element = route.route_elements[r];
1019 const auto& next_route_element = route.route_elements[r + 1];
1020 double speed_limit = route_planning_msgs::route_access::getSuggestedLaneElement(route_element).speed_limit;
1021 if (speed_limit == 0) {
1022 speed_limit = reference_speed;
1023 }
1024 if (speed_limit > 0) {
1025 remaining_time += (next_route_element.s - route_element.s) / speed_limit;
1026 }
1027 }
1028 return remaining_time;
1029}
1030
1032 route_planning_msgs::msg::Route& route_msg,
1033 std::vector<std::vector<int>>& suggested_turn_signal_distance_ahead_by_route_element_by_lane_element) {
1034 // loop over route elements to set orientations and following lane indices
1035 std::vector<route_planning_msgs::msg::RouteElement>& route_elements = route_msg.route_elements;
1036 for (size_t r = 0; r < route_elements.size(); ++r) {
1037 // get current, previous and next route element
1038 auto& route_element = route_elements[r];
1039 auto& prev_route_element = (r > 0) ? route_elements[r - 1] : route_element;
1040 const auto& next_route_element = (r < route_elements.size() - 1) ? route_elements[r + 1] : route_element;
1041
1042 // fix following lane index at the last non-enriched route element before route elements are enriched
1043 if (r > 0) {
1044 if (!prev_route_element.is_enriched) {
1045 prev_route_element.lane_elements[0].has_following_lane_idx = true;
1046 prev_route_element.lane_elements[0].following_lane_idx = route_element.suggested_lane_idx;
1047 }
1048 }
1049
1050 // loop over lane elements of current route element
1051 for (size_t l = 0; l < route_element.lane_elements.size(); ++l) {
1052 // get current, previous and next lane element
1053 auto& lane_element = route_element.lane_elements[l];
1054 const auto prev_lane_element_opt = route_planning_msgs::route_access::getPrecedingLaneElement(l, prev_route_element);
1055 const auto next_lane_element_opt =
1056 route_planning_msgs::route_access::getFollowingLaneElement(lane_element, next_route_element);
1057
1058 // find current, previous and next points to compute orientation
1059 const auto point = toEigen2d(lane_element.reference_pose.position);
1060 const bool changes_lane_from_prev_point = prev_route_element.will_change_suggested_lane;
1061 const bool changes_lane_to_next_point = next_route_element.will_change_suggested_lane;
1062 const auto prev_point_for_orientation = (changes_lane_from_prev_point || !prev_lane_element_opt)
1063 ? point
1064 : toEigen2d(prev_lane_element_opt->reference_pose.position);
1065 const auto next_point_for_orientation = (changes_lane_to_next_point || !next_lane_element_opt)
1066 ? point
1067 : toEigen2d(next_lane_element_opt->reference_pose.position);
1068
1069 // compute orientation of current point
1070 const auto orientation = tangentOfPointAlongLineString(point, prev_point_for_orientation, next_point_for_orientation);
1071 lane_element.reference_pose.orientation = toRosQuaternion(orientation);
1072 }
1073 }
1074
1075 // loop over route elements in reverse to propagate suggested turn signals backwards
1076 for (int r = static_cast<int>(route_elements.size()) - 1; r >= 0; --r) {
1077 auto& route_element = route_elements[r];
1078 if (!route_element.is_enriched) continue; // only handle turn signal information for enriched route elements
1079
1080 // loop over lane elements of current route element
1081 for (size_t l = 0; l < route_element.lane_elements.size(); ++l) {
1082 auto& lane_element = route_element.lane_elements[l];
1083
1084 if (suggested_turn_signal_distance_ahead_by_route_element_by_lane_element[r].empty()) continue;
1085 int suggested_turn_signal_distance_ahead = suggested_turn_signal_distance_ahead_by_route_element_by_lane_element[r][l];
1086
1087 // get preceding lane element
1088 if (r <= 0) break;
1089 int curr_r = r;
1090 auto* curr_lane_element = &lane_element;
1091 auto* prev_route_element = &route_elements[r - 1];
1092 auto prev_lane_element_idx_opt = route_planning_msgs::route_access::getPrecedingLaneElementIdx(l, *prev_route_element);
1093
1094 // iterate over preceding lane elements within suggested distance to set suggested turn signal
1095 while (suggested_turn_signal_distance_ahead > 0 && prev_lane_element_idx_opt) {
1096 auto& prev_lane_element = prev_route_element->lane_elements[*prev_lane_element_idx_opt];
1097 if (!prev_route_element->is_enriched) break; // only propagate through enriched route elements
1098
1099 // stop if preceding lane element has its own suggested turn signal information
1100 if (!suggested_turn_signal_distance_ahead_by_route_element_by_lane_element[curr_r - 1].empty()) {
1101 int prev_suggested_turn_signal_distance_ahead =
1102 suggested_turn_signal_distance_ahead_by_route_element_by_lane_element[curr_r - 1][*prev_lane_element_idx_opt];
1103 if (prev_suggested_turn_signal_distance_ahead >= 0) {
1104 break;
1105 }
1106 }
1107
1108 // check distance to preceding lane element
1109 const auto point = toEigen2d(curr_lane_element->reference_pose.position);
1110 const auto prev_point = toEigen2d(prev_lane_element.reference_pose.position);
1111 const double distance_to_prev_point = (point - prev_point).norm();
1112 if (distance_to_prev_point > suggested_turn_signal_distance_ahead) {
1113 break; // stop if distance to preceding point exceeds remaining distance ahead
1114 }
1115
1116 // set suggested turn signal of preceding lane element
1117 prev_lane_element.suggested_turn_signal = lane_element.suggested_turn_signal;
1118
1119 // update remaining distance and move to next preceding lane element
1120 suggested_turn_signal_distance_ahead -= static_cast<uint8_t>(std::round(distance_to_prev_point));
1121 curr_r -= 1;
1122 if (curr_r <= 0) break;
1123 curr_lane_element = &prev_lane_element;
1124 prev_route_element = &route_elements[curr_r - 1];
1125 prev_lane_element_idx_opt =
1126 route_planning_msgs::route_access::getPrecedingLaneElementIdx(*prev_lane_element_idx_opt, *prev_route_element);
1127 }
1128 }
1129 }
1130}
1131
1132void sortLaneletsByMatchingCost(const lanelet::BasicPoint2d& point,
1133 std::vector<std::pair<double, lanelet::ConstLanelet>>& lanelets_with_distances,
1134 const std::optional<lanelet::traffic_rules::TrafficRulesPtr>& traffic_rules) {
1135 struct LaneletMatchingCost {
1136 std::pair<double, lanelet::ConstLanelet> lanelet_with_distance;
1137 double cost;
1138 };
1139
1140 std::vector<LaneletMatchingCost> lanelet_matching_costs;
1141 lanelet_matching_costs.reserve(lanelets_with_distances.size());
1142
1143 // compute the matching cost for each candidate lanelet
1144 for (const auto& lanelet_with_distance : lanelets_with_distances) {
1145 const lanelet::ConstLanelet& lanelet = lanelet_with_distance.second;
1146 const lanelet::ArcCoordinates arc_coordinates = lanelet::geometry::toArcCoordinates(lanelet.centerline2d(), point);
1147 const bool point_is_inside_lanelet = lanelet::geometry::inside(lanelet, point);
1148
1149 // favor lanelets containing the point and penalize lateral distance quadratically
1150 double cost = (point_is_inside_lanelet ? 0.5 : 2.0) * arc_coordinates.distance * arc_coordinates.distance;
1151
1152 // keep non-passable lanelets as fallback candidates, but rank them lower
1153 if (traffic_rules && !traffic_rules.value()->canPass(lanelet)) {
1154 cost *= 2.0;
1155 }
1156
1157 lanelet_matching_costs.push_back({lanelet_with_distance, cost});
1158 }
1159
1160 // apply the sorted order to the original lanelet-distance pairs
1161 std::sort(lanelet_matching_costs.begin(), lanelet_matching_costs.end(),
1162 [](const LaneletMatchingCost& lhs, const LaneletMatchingCost& rhs) { return lhs.cost < rhs.cost; });
1163
1164 for (size_t i = 0; i < lanelet_matching_costs.size(); ++i) {
1165 lanelets_with_distances.at(i) = lanelet_matching_costs[i].lanelet_with_distance;
1166 }
1167}
1168
1169LocalRouteWindow extractLocalRouteWindow(const route_planning_msgs::msg::Route& full_route,
1170 const size_t current_global_idx,
1171 const double distance_behind,
1172 const double distance_ahead) {
1173 LocalRouteWindow result;
1174 result.route.header = full_route.header;
1175 result.route.destination = full_route.destination;
1176 result.route.intermediate_destinations = full_route.intermediate_destinations;
1177 const auto& full_route_elements = full_route.route_elements;
1178 if (current_global_idx >= full_route_elements.size()) {
1179 result.route.route_elements.clear();
1180 return result;
1181 }
1182
1183 // find the route-element bounds by their absolute distances
1184 const double current_s = full_route_elements[current_global_idx].s;
1185 const auto first_it = std::lower_bound(
1186 full_route_elements.begin(), full_route_elements.end(), current_s - distance_behind,
1187 [](const route_planning_msgs::msg::RouteElement& route_element, const double s) { return route_element.s < s; });
1188 const auto last_it = std::upper_bound(
1189 full_route_elements.begin(), full_route_elements.end(), current_s + distance_ahead,
1190 [](const double s, const route_planning_msgs::msg::RouteElement& route_element) { return s < route_element.s; });
1191 result.first_global_idx = std::distance(full_route_elements.begin(), first_it);
1192 const size_t last_global_idx = std::distance(full_route_elements.begin(), last_it);
1193 result.route.route_elements.assign(first_it, last_it);
1194
1195 // remap route indices from the complete route to the local window
1196 const auto remap_global_idx = [first_global_idx = result.first_global_idx, last_global_idx](const uint64_t global_idx) {
1197 return (global_idx >= first_global_idx && global_idx < last_global_idx)
1198 ? global_idx - first_global_idx
1199 : route_planning_msgs::msg::Route::INVALID_ROUTE_ELEMENT_IDX;
1200 };
1201 result.route.starting_route_element_idx = remap_global_idx(full_route.starting_route_element_idx);
1202 result.route.current_route_element_idx = current_global_idx - result.first_global_idx;
1203 result.route.destination_route_element_idx = remap_global_idx(full_route.destination_route_element_idx);
1204 return result;
1205}
1206
1207} // namespace lanelet2_route_planning
std::optional< ProjectPointToLineStringAlongAxisResult > projectPointToLineStringAlongNormal(const Eigen::Vector2d &point, const Eigen::Vector2d &prev_point, const Eigen::Vector2d &next_point, const std::vector< Eigen::Vector2d > &line_string)
Projects a point to the closest line segment of a line string along the normal to the tangent at the ...
Definition geometry.cpp:279
uint8_t laneBoundaryType(const lanelet::ConstLineString2d &line)
Extracts the lane boundary type of a lanelet line.
Definition utils.cpp:666
lanelet::ConstLanelet followLaneletsAlongRoutingGraph(const lanelet::routing::RoutingGraphUPtr &routing_graph, const lanelet::ConstLanelet &lanelet, const Eigen::Vector2d &position, const double distance)
Follows a lanelet's and following lanelets' centerline for a given distance.
Definition utils.cpp:914
double distanceTraveled(const route_planning_msgs::msg::Route &route)
Computes the traveled distance along the route.
Definition utils.cpp:993
double angleBetweenVectors(const Eigen::Vector2d &v1, const Eigen::Vector2d &v2)
Computes the angle between two 2D vectors.
Definition geometry.cpp:80
lanelet::traffic_rules::TrafficRulesPtr getTrafficRules()
Get traffic rules.
Definition utils.cpp:879
bool changesLaneFromPointToPoint(const Eigen::Vector2d &point, const Eigen::Vector2d &next_point, const double sampling_distance)
Identifies a lane change based on the distance between two reference line points.
Definition utils.cpp:177
std::optional< lanelet::routing::Route > getRoute(const lanelet::routing::RoutingGraphUPtr &routing_graph, const std::vector< lanelet::ConstLanelet > &route_lanelets)
Computes a route from start to destination along intermediate destinations.
Definition utils.cpp:26
std::optional< std::array< geometry_msgs::msg::Point, 2 > > regulatoryElementReferenceLine(const std::shared_ptr< const lanelet::RegulatoryElement > &regulatory_element)
Extracts the reference/effect line of a regulatory element.
Definition utils.cpp:559
std::vector< geometry_msgs::msg::Point > regulatoryElementPositions(const std::shared_ptr< const lanelet::RegulatoryElement > &regulatory_element)
Extracts the sign/signal positions of a regulatory element.
Definition utils.cpp:589
bool isLineStringDrivable(const lanelet::ConstLineString3d &line_string)
Checks if lanelet line string has a type that is considered drivable.
Definition utils.cpp:461
size_t matchPointToLineString(const std::vector< Eigen::Vector2d > &line_string, const Eigen::Vector2d &point, const size_t idx_indication, const bool consider_order=false, const bool behind=true)
Finds the index of a point in a line string that is locally closest to another point.
Definition utils.cpp:94
Eigen::Vector2d tangentOfPointAlongLineString(const Eigen::Vector2d &point, const Eigen::Vector2d &prev_point, const Eigen::Vector2d &next_point)
Computes a unit vector tangential to a point along a line string.
Definition geometry.cpp:123
LocalRouteWindow extractLocalRouteWindow(const route_planning_msgs::msg::Route &full_route, size_t current_global_idx, double distance_behind, double distance_ahead)
Extracts a local route window while preserving absolute route data.
Definition utils.cpp:1169
ExtractRegulatoryElementsResult extractRegulatoryElements(const lanelet::ConstLanelet &lanelet, const std::vector< lanelet::ConstLanelet > &adjacent_left_lanelets, const std::vector< lanelet::ConstLanelet > &adjacent_right_lanelets, const PointSequence &point_sequence)
Extracts regulatory element information for a route element.
Definition utils.cpp:481
double estimateRemainingTime(const route_planning_msgs::msg::Route &route, const double reference_speed=50.0/3.6)
Estimate remaining time for a route based on speed limits.
Definition utils.cpp:1015
route_planning_msgs::msg::RouteElement createMinimalRouteElement(const geometry_msgs::msg::Point &position, const geometry_msgs::msg::Quaternion &orientation, double s=0.0, bool will_change_suggested_lane=false, uint8_t speed_limit=0)
Create a minimal route element message.
Definition utils.cpp:345
lanelet::BasicPoint2d toLanelet(const Eigen::Vector2d &point)
Converts a 2D Eigen point to a Lanelet point.
uint8_t speedLimit(const lanelet::ConstLanelet &lanelet, const bool consider_regulatory_elements=true)
Extracts the speed limit of a lanelet.
Definition utils.cpp:704
std::optional< std::array< geometry_msgs::msg::Point, 2 > > regulatoryElementCancelLine(const std::shared_ptr< const lanelet::RegulatoryElement > &regulatory_element)
Extracts the cancel line of a regulatory element.
Definition utils.cpp:574
geometry_msgs::msg::Quaternion toRosQuaternion(const Eigen::Vector2d &vector)
Converts a 2D Eigen vector pointing in a specific direction to a ROS quaternion.
Eigen::Vector2d normalOfPointAlongLineString(const Eigen::Vector2d &point, const Eigen::Vector2d &prev_point, const Eigen::Vector2d &next_point)
Computes a unit vector normal to a point along a line string.
Definition geometry.cpp:143
std::vector< Eigen::Vector3d > resampleLineString(const std::vector< Eigen::Vector3d > &line_string, const double delta, double &offset)
Resamples a 3D line string with a constant sampling distance.
Definition geometry.cpp:152
std::optional< int > computeFollowingLaneIdxOffset(const lanelet::ConstLanelet &lanelet, const lanelet::ConstLanelet &lanelet_of_next_point, const lanelet::routing::RoutingGraphUPtr &routing_graph)
Computes the offset of lane element indices from current to next route element.
Definition utils.cpp:252
void sortLaneletsByMatchingCost(const lanelet::BasicPoint2d &point, std::vector< std::pair< double, lanelet::ConstLanelet > > &lanelets_with_distances, const std::optional< lanelet::traffic_rules::TrafficRulesPtr > &traffic_rules=std::nullopt)
Sorts lanelet candidates by their matching cost for a 2D point.
Definition utils.cpp:1132
size_t indexOfLineStringPointClosestToPoint(const std::vector< Eigen::Vector2d > &line_string, const Eigen::Vector2d &point, const bool consider_order=false, const bool behind=true)
Finds the index of a point in a line string that is closest to another point.
Definition utils.cpp:67
Eigen::Vector2d to2d(const Eigen::Vector3d &point)
Converts a 3D Eigen point to a 2D Eigen point.
geometry_msgs::msg::Point toRos(const Eigen::Vector2d &point)
Converts a 2D Eigen point to a ROS point.
ResampleCenterlinesAlongPathResult resampleCenterlinesAlongPath(const lanelet::routing::LaneletPath &path, const double delta_s, bool monotonically)
Equidistantly resamples lanelet centerlines along a path to one joint centerline.
Definition utils.cpp:946
size_t considerOrderForPointMatchedToLineString(const std::vector< Eigen::Vector2d > &line_string, const Eigen::Vector2d &point, const size_t idx_closest, const bool behind)
Takes a closest point in a line string and guarantees that it is behind or ahead of the given point.
Definition utils.cpp:144
std::optional< IntersectionOfLinesResult > intersectionOfLines(const std::vector< Eigen::Vector2d > &line1, const std::vector< Eigen::Vector2d > &line2)
Computes the intersection of two 2D lines.
Definition geometry.cpp:87
Eigen::Vector2d toEigen2d(const geometry_msgs::msg::Point &point)
Converts a ROS point to a 2D Eigen point.
void postprocessRouteMessage(route_planning_msgs::msg::Route &route_msg, std::vector< std::vector< int > > &suggested_turn_signal_distance_ahead_by_route_element_by_lane_element)
Postprocesses a route message, filling missing information that can be inferred from other message co...
Definition utils.cpp:1031
std::pair< Eigen::Vector2d, Eigen::Vector2d > extractDrivableSpace(const lanelet::LineStringLayer &line_string_layer, const PointSequence &point_sequence, const double max_distance)
Extracts drivable space boundaries for a route element.
Definition utils.cpp:375
std::pair< uint8_t, uint8_t > regulatoryElementType(const std::shared_ptr< const lanelet::RegulatoryElement > &regulatory_element)
Extracts the type and meta value of a regulatory element.
Definition utils.cpp:603
std::vector< lanelet::ConstLanelet > adjacentLeftOrRightLanelets(const lanelet::ConstLanelet &lanelet, const lanelet::routing::RoutingGraphUPtr &routing_graph, bool left, bool sort_from_left=true)
Finds lanelets adjacent to the left or right of a given lanelet.
Definition utils.cpp:184
std::optional< lanelet::ConstLanelet > laneletAtPoint(const Eigen::Vector2d &point, const lanelet::LaneletMapConstPtr &map, const std::optional< lanelet::traffic_rules::TrafficRulesPtr > traffic_rules=std::nullopt)
Find lanelet at arbitrary point.
Definition utils.cpp:885
std::vector< ProjectedLaneletPoints > projectPointToLaneletLines(const Eigen::Vector2d &point, const Eigen::Vector2d &prev_point, const Eigen::Vector2d &next_point, const std::vector< lanelet::ConstLanelet > &lanelets, const rclcpp::Logger &logger=rclcpp::get_logger("lanelet2_route_planning"))
Projects a point to the centerline and bounds of a set of lanelets.
Definition utils.cpp:208
uint8_t regulatoryElementSpeedLimit(const std::shared_ptr< const lanelet::RegulatoryElement > &regulatory_element)
Extracts the speed limit of a regulatory element of subtype 'speed_limit'.
Definition utils.cpp:631
std::tuple< uint8_t, int > suggestedTurnSignal(const lanelet::ConstLanelet &lanelet, const rclcpp::Logger &logger)
Extracts the suggested turn signal of a lanelet.
Definition utils.cpp:833
Eigen::Vector3d toEigen(const geometry_msgs::msg::Point &point)
Converts a ROS point to a 3D Eigen point.
double distanceRemaining(const route_planning_msgs::msg::Route &route)
Computes the remaining distance along the route.
Definition utils.cpp:1004
Return type of extractRegulatoryElements.
Definition utils.hpp:280
std::vector< std::vector< uint8_t > > adjacent_right_regulatory_element_idcs
indices of regulatory elements belonging to right adjacent lanes
Definition utils.hpp:287
std::vector< uint8_t > regulatory_element_idcs
indices of regulatory elements belonging to main lane
Definition utils.hpp:283
std::vector< std::vector< uint8_t > > adjacent_left_regulatory_element_idcs
indices of regulatory elements belonging to left adjacent lanes
Definition utils.hpp:285
std::vector< route_planning_msgs::msg::RegulatoryElement > regulatory_element_msgs
regulatory element messages for route element
Definition utils.hpp:282
A local route window and its offset in the complete route.
Definition utils.hpp:29
route_planning_msgs::msg::Route route
Definition utils.hpp:30
Helper type for a sequence of three points.
Definition utils.hpp:217
Eigen::Vector2d next
next point
Definition utils.hpp:220
Eigen::Vector2d prev
previous point
Definition utils.hpp:218
Eigen::Vector2d current
current point
Definition utils.hpp:219
Eigen::Vector2d centerline_point
projected centerline point
Definition utils.hpp:150
Eigen::Vector2d right_bound_point
projected right bound point
Definition utils.hpp:151
Eigen::Vector2d left_bound_point
projected left bound point
Definition utils.hpp:149
Return type of resampleCenterlinesAlongPath.
Definition utils.hpp:469
std::vector< Eigen::Vector3d > centerline
resampled centerline
Definition utils.hpp:470
std::vector< size_t > lanelet_idx_by_point
lanelet index in path for each point
Definition utils.hpp:471