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.
WorldModelUtils.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 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 carma_wm
20{
21namespace query
22{
23
24std::vector<lanelet::ConstLanelet> getLaneletsFromPoint(const lanelet::LaneletMapConstPtr& semantic_map, const lanelet::BasicPoint2d& point,
25 const unsigned int n)
26{
27 // Check if the map is loaded yet
28 if (!semantic_map || semantic_map->laneletLayer.size() == 0)
29 {
30 throw std::invalid_argument("Map is not set or does not contain lanelets");
31 }
32 std::vector<lanelet::ConstLanelet> possible_lanelets;
33 auto nearestLanelets = lanelet::geometry::findNearest(semantic_map->laneletLayer, point, n);
34 if (nearestLanelets.size() == 0)
35 return {};
36
37 size_t id = 0; // closest ones are in the back
38 // loop through until the point is no longer geometrically in the lanelet
39
40 while (boost::geometry::within(point, nearestLanelets[id].second.polygon2d()))
41 {
42 possible_lanelets.push_back(nearestLanelets[id].second);
43 id++;
44 if (id >= nearestLanelets.size())
45 break;
46 }
47 return possible_lanelets;
48}
49
50std::vector<lanelet::Lanelet> getLaneletsFromPoint(const lanelet::LaneletMapPtr& semantic_map, const lanelet::BasicPoint2d& point,
51 const unsigned int n)
52{
53 lanelet::LaneletMapConstPtr const_ptr = semantic_map;
54 auto possible_lanelets = getLaneletsFromPoint(const_ptr, point, n);
55 std::vector<lanelet::Lanelet> return_lanelets;
56 for (auto llt : possible_lanelets)
57 {
58 return_lanelets.push_back(semantic_map->laneletLayer.get(llt.id()));
59 }
60 return return_lanelets;
61}
62
63std::vector<lanelet::Lanelet> nonConnectedAdjacentLeft(const lanelet::LaneletMapPtr& semantic_map, const lanelet::BasicPoint2d& point,
64 const unsigned int n)
65{
66
67 lanelet::LaneletMapConstPtr const_ptr = semantic_map;
68 auto possible_lanelets = nonConnectedAdjacentLeft(const_ptr, point, n);
69
70
71 std::vector<lanelet::Lanelet> return_lanelets;
72 for (auto llt : possible_lanelets)
73 {
74 return_lanelets.push_back(semantic_map->laneletLayer.get(llt.id()));
75 }
76 return return_lanelets;
77}
78
79std::vector<lanelet::ConstLanelet> nonConnectedAdjacentLeft(const lanelet::LaneletMapConstPtr& semantic_map, const lanelet::BasicPoint2d& input_point,
80 const unsigned int n)
81{
82
83 // Check if the map is loaded yet
84 if (!semantic_map || semantic_map->laneletLayer.size() == 0)
85 {
86 throw std::invalid_argument("Map is not set or does not contain lanelets");
87 }
88
89 auto input_lanelets = getLaneletsFromPoint(semantic_map, input_point);
90
91 if (input_lanelets.empty())
92 {
93 throw std::invalid_argument("Input point x: " + std::to_string(input_point.x()) + ", y: " + std::to_string(input_point.y()) + " is not in the map");
94 }
95
96 auto input_lanelet = input_lanelets[0];
97
98 auto point_downtrack = carma_wm::geometry::trackPos(input_lanelet, input_point).downtrack;
99
100 auto point_downtrack_ratio = point_downtrack / carma_wm::geometry::trackPos(input_lanelet, input_lanelet.centerline().back().basicPoint2d()).downtrack;
101
102 auto point_on_ls = input_lanelet.leftBound2d()[std::round((input_lanelet.leftBound2d().size()- 1) * point_downtrack_ratio) ];
103
104 // point_on_opposite_lane coord is acquired by extrapolating the line from input_point to point_on_ls with same distance.
105 // threfore point_on_opposite_lane.x() = input_point.x() + 2dx, where dx = point_on_ls.x() - input_point.x(). Here, one of input_point.x() cancels out, results in:
106 auto point_on_opposite_lane = lanelet::BasicPoint2d{2 * point_on_ls.x() - input_point.x(), 2 * point_on_ls.y() - input_point.y()};
107
108 auto opposite_lanelets = getLaneletsFromPoint(semantic_map, point_on_opposite_lane, n);
109
110 // TODO: create opposite direction protection throw
111 // currently the function doesn't recognize if adjacent lane is opposite direction, but assumes it
112
113 return opposite_lanelets;
114}
115
116
117lanelet::ConstLaneletOrAreas getAffectedLaneletOrAreas(const lanelet::Points3d& gf_pts, const lanelet::LaneletMapPtr& lanelet_map, std::shared_ptr<const lanelet::routing::RoutingGraph> routing_graph, double max_lane_width)
118{
119 // Logic to detect which part is affected
120 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm::query"), "Get affected lanelets loop");
121 std::unordered_set<lanelet::Lanelet> affected_lanelets;
122 for (size_t idx = 0; idx < gf_pts.size(); idx ++)
123 {
124 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm::query"), "Index: " << idx << " Point: " << gf_pts[idx].x() << ", " << gf_pts[idx].y());
125 std::unordered_set<lanelet::Lanelet> possible_lanelets;
126
127 // This loop identifes the lanelets which this point lies within that could be impacted by the geofence
128 // This loop somewhat inefficiently calls the findNearest method iteratively until all the possible lanelets are identified.
129 // The reason findNearest is used instead of nearestUntil is because that method orders results by bounding box which
130 // can give invalid sequences when dealing with large curved lanelets.
131 bool continue_search = true;
132 size_t nearest_count = 0;
133 while (continue_search) {
134
135 nearest_count += 10; // Increase the index search radius by 10 each loop until all nearby lanelets are found
136
137 for (const auto& ll_pair : lanelet::geometry::findNearest(lanelet_map->laneletLayer, gf_pts[idx].basicPoint2d(), nearest_count)) { // Get the nearest lanelets and iterate over them
138 auto ll = std::get<1>(ll_pair);
139
140 if (possible_lanelets.find(ll) != possible_lanelets.end()) { // Skip if already found
141 continue;
142 }
143
144 double dist = std::get<0>(ll_pair);
145 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm::query"), "Distance to lanelet " << ll.id() << ": " << dist << " max_lane_width: " << max_lane_width);
146
147 if (dist > max_lane_width) { // Only save values closer than max_lane_width. Since we are iterating in distance order when we reach this distance the search can stop
148 continue_search = false;
149 break;
150 }
151
152 // Check if the point is inside this lanelet
153 if(dist == 0.0) { // boost geometry uses a distance of 0 to indicate a point is within a polygon
154 possible_lanelets.insert(ll);
155 }
156
157 }
158
159 if (nearest_count >= lanelet_map->laneletLayer.size()) { // if we are out of lanelets to evaluate then end the search
160 continue_search = false;
161 }
162 }
163
164 // among these llts, filter the ones that are on same direction as the geofence using routing
165 if (idx + 1 == gf_pts.size()) // we only check this for the last gf_pt after saving everything
166 {
167 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm::query"), "Last point");
168 std::unordered_set<lanelet::Lanelet> filtered = filterSuccessorLanelets(possible_lanelets, affected_lanelets, lanelet_map, routing_graph);
169 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm::query"), "Got successor lanelets of size: " << filtered.size());
170 affected_lanelets.insert(filtered.begin(), filtered.end());
171
172
173 if (affected_lanelets.empty() && !possible_lanelets.empty() && idx !=0 )
174 {
175 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm::query"), "Checking if it is the edge case where only last point falls on a valid (correct direction) lanelet");
176 for (auto llt: possible_lanelets)
177 {
178 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm::query"), "Evaluating lanelet: " << llt.id());
179 lanelet::BasicLineString2d gf_dir_line({gf_pts[idx - 1].basicPoint2d(), gf_pts[idx].basicPoint2d()});
180 lanelet::BasicLineString2d llt_boundary({(llt.leftBound2d().begin())->basicPoint2d(), (llt.rightBound2d().begin())->basicPoint2d()});
181
182 // record the llts that are on the same dir
183 if (boost::geometry::intersects(llt_boundary, gf_dir_line))
184 {
185 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm::query"), "Overlaps starting line... Picking llt: " << llt.id());
186 affected_lanelets.insert(llt);
187 }
188 }
189 }
190
191 break;
192 }
193
194 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm::query"), "Checking possible lanelets");
195 // check if each lines connecting end points of the llt is crossing with the line connecting current and next gf_pts
196 for (auto llt: possible_lanelets)
197 {
198 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm::query"), "Evaluating lanelet: " << llt.id());
199 lanelet::BasicLineString2d gf_dir_line({gf_pts[idx].basicPoint2d(), gf_pts[idx+1].basicPoint2d()});
200 lanelet::BasicLineString2d llt_boundary({(llt.leftBound2d().end() -1)->basicPoint2d(), (llt.rightBound2d().end() - 1)->basicPoint2d()});
201
202 // record the llts that are on the same dir
203 if (boost::geometry::intersects(llt_boundary, gf_dir_line))
204 {
205 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm::query"), "Overlaps end line");
206 affected_lanelets.insert(llt);
207 }
208 // check condition if two geofence points are in one lanelet then check matching direction and record it also
209 else if (boost::geometry::within(gf_pts[idx+1].basicPoint2d(), llt.polygon2d()) &&
210 affected_lanelets.find(llt) == affected_lanelets.end())
211 {
212 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm::query"), "Within new lanelet");
213 lanelet::BasicPoint2d median({((llt.leftBound2d().end() - 1)->basicPoint2d().x() + (llt.rightBound2d().end() - 1)->basicPoint2d().x())/2 ,
214 ((llt.leftBound2d().end() - 1)->basicPoint2d().y() + (llt.rightBound2d().end() - 1)->basicPoint2d().y())/2});
215 // turn into vectors
216 Eigen::Vector2d vec_to_median(median);
217 Eigen::Vector2d vec_to_gf_start(gf_pts[idx].basicPoint2d());
218 Eigen::Vector2d vec_to_gf_end(gf_pts[idx + 1].basicPoint2d());
219
220 // Get vector from start to external point
221 Eigen::Vector2d start_to_median = vec_to_median - vec_to_gf_start;
222
223 // Get vector from start to end point
224 Eigen::Vector2d start_to_end = vec_to_gf_end - vec_to_gf_start;
225
226 // Get angle between both vectors
227 double interior_angle = carma_wm::geometry::getAngleBetweenVectors(start_to_median, start_to_end);
228
229 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm::query"), "vec_to_median: " << vec_to_median.x() << ", " << vec_to_median.y());
230 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm::query"), "vec_to_gf_start: " << vec_to_gf_start.x() << ", " << vec_to_gf_start.y());
231 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm::query"), "vec_to_gf_end: " << vec_to_gf_end.x() << ", " << vec_to_gf_end.y());
232 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm::query"), "start_to_median: " << start_to_median.x() << ", " << start_to_median.y());
233 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm::query"), "start_to_end: " << start_to_end.x() << ", " << start_to_end.y());
234 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm::query"), "interior_angle: " << interior_angle);
235 // Save the lanelet if the direction of two points inside aligns with that of the lanelet
236
237 if (interior_angle < M_PI_2 && interior_angle >= 0)
238 affected_lanelets.insert(llt);
239 }
240 else
241 {
242 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm::query"), "------ Did not record anything...");
243 }
244
245 }
246 }
247
248 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm::query"), "affected_lanelets size: " << affected_lanelets.size());
249 // Currently only returning lanelet, but this could be expanded to LanelerOrArea compound object
250 // by implementing non-const version of that LaneletOrArea
251 lanelet::ConstLaneletOrAreas affected_parts;
252 // return results in ascending downtrack order from the first point of geofence
253 std::vector<std::pair<double, lanelet::Lanelet>> sorted_parts;
254
255 for (auto llt : affected_lanelets)
256 {
257 sorted_parts.push_back(std::make_pair(carma_wm::geometry::trackPos(llt, gf_pts.front().basicPoint2d()).downtrack, llt));
258 }
259 std::sort(sorted_parts.begin(), sorted_parts.end(), [](const auto& x, const auto& y){return x.first > y.first;});
260
261 for (auto pair : sorted_parts)
262 {
263 affected_parts.push_back(pair.second);
264 }
265
266 return affected_parts;
267}
268
269// helper function that filters successor lanelets of root_lanelets from possible_lanelets
270std::unordered_set<lanelet::Lanelet> filterSuccessorLanelets(const std::unordered_set<lanelet::Lanelet>& possible_lanelets, const std::unordered_set<lanelet::Lanelet>& root_lanelets,
271 const lanelet::LaneletMapPtr& lanelet_map, std::shared_ptr<const lanelet::routing::RoutingGraph> routing_graph)
272{
273 if (!routing_graph) {
274 throw std::invalid_argument("No routing graph available");
275 }
276
277 std::unordered_set<lanelet::Lanelet> filtered_lanelets;
278 // we utilize routes to filter llts that are overlapping but not connected
279 // as this is the last lanelet
280 // we have to filter the llts that are only geometrically overlapping yet not connected to prev llts
281 for (auto recorded_llt: root_lanelets)
282 {
283 for (auto following_llt: routing_graph->following(recorded_llt, false))
284 {
285 auto mutable_llt = lanelet_map->laneletLayer.get(following_llt.id());
286 auto it = possible_lanelets.find(mutable_llt);
287 if (it != possible_lanelets.end())
288 {
289 filtered_lanelets.insert(mutable_llt);
290 }
291 }
292 }
293 return filtered_lanelets;
294}
295
296
297} // namespace query
298
299namespace utils
300{
301
302uint32_t get32BitId(uint16_t intersection_id, uint8_t signal_group_id)
303{
304 uint32_t temp = 0;
305 temp |= intersection_id;
306 temp = temp << 8;
307 temp |= signal_group_id;
308 return temp;
309}
310
311
312} // namespace utils
313} // namespace carma_wm
auto to_string(const UtmZone &zone) -> std::string
Definition: utm_zone.cpp:21
double getAngleBetweenVectors(const Eigen::Vector2d &vec1, const Eigen::Vector2d &vec2)
Calculates the angle between two vectors.
Definition: Geometry.cpp:65
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::unordered_set< lanelet::Lanelet > filterSuccessorLanelets(const std::unordered_set< lanelet::Lanelet > &possible_lanelets, const std::unordered_set< lanelet::Lanelet > &root_lanelets, const lanelet::LaneletMapPtr &lanelet_map, std::shared_ptr< const lanelet::routing::RoutingGraph > routing_graph)
A function that filters successor lanelets of root_lanelets from possible_lanelets.
std::vector< lanelet::ConstLanelet > getLaneletsFromPoint(const lanelet::LaneletMapConstPtr &semantic_map, const lanelet::BasicPoint2d &point, const unsigned int n=10)
carma_wm::query namespace contains implementations for query functions (input and output read or writ...
lanelet::ConstLaneletOrAreas getAffectedLaneletOrAreas(const lanelet::Points3d &gf_pts, const lanelet::LaneletMapPtr &lanelet_map, std::shared_ptr< const lanelet::routing::RoutingGraph > routing_graph, double max_lane_width)
Gets the affected lanelet or areas based on the points in the given map's frame.
std::vector< lanelet::ConstLanelet > nonConnectedAdjacentLeft(const lanelet::LaneletMapConstPtr &semantic_map, const lanelet::BasicPoint2d &input_point, const unsigned int n=10)
Given the cartesian point on the map, tries to get the opposite direction lanelet on the left This fu...
uint32_t get32BitId(uint16_t intersection_id, uint8_t signal_group_id)
Get 32bit id by concatenating 16bit id with 8bit signal_group_id.