Carma-platform v4.11.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.
helper_functions.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2021-2024 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
17#include <algorithm>
20
21
22namespace basic_autonomy
23{
24namespace waypoint_generation
25{
26 int get_nearest_point_index(const std::vector<lanelet::BasicPoint2d>& points,
27 const carma_planning_msgs::msg::VehicleState& state)
28 {
29 lanelet::BasicPoint2d veh_point(state.x_pos_global, state.y_pos_global);
30 double min_distance = std::numeric_limits<double>::max();
31 int i = 0;
32 int best_index = 0;
33 for (const auto& p : points)
34 {
35 double distance = lanelet::geometry::distance2d(p, veh_point);
36 if (distance < min_distance)
37 {
38 best_index = i;
39 min_distance = distance;
40 }
41 i++;
42 }
43 return best_index;
44 }
45
46 int get_nearest_point_index(const std::vector<PointSpeedPair>& points,
47 const carma_planning_msgs::msg::VehicleState& state)
48 {
49 lanelet::BasicPoint2d veh_point(state.x_pos_global, state.y_pos_global);
50 RCLCPP_DEBUG_STREAM(basic_autonomy::get_logger(), "veh_point: " << veh_point.x() << ", " << veh_point.y());
51 double min_distance = std::numeric_limits<double>::max();
52 int i = 0;
53 int best_index = 0;
54 for (const auto& p : points)
55 {
56 double distance = lanelet::geometry::distance2d(p.point, veh_point);
57 if (distance < min_distance)
58 {
59 best_index = i;
60 min_distance = distance;
61 }
62 i++;
63 }
64 return best_index;
65 }
66
68 const std::vector<carma_planning_msgs::msg::TrajectoryPlanPoint>& trajectory,
69 const lanelet::BasicPoint2d& position)
70 {
71 size_t closest_idx = 0;
72 double min_dist = std::numeric_limits<double>::max();
73
74 for (size_t i = 0; i < trajectory.size(); i++)
75 {
76 auto dist = sqrt(pow(position.x() - trajectory.at(i).x, 2) +
77 pow(position.y() - trajectory.at(i).y, 2));
78
79 if (dist < min_dist)
80 {
81 min_dist = dist;
82 closest_idx = i;
83 }
84 }
85
86 return closest_idx;
87 }
88
89 int get_nearest_index_by_downtrack(const std::vector<lanelet::BasicPoint2d>& points, const carma_wm::WorldModelConstPtr& wm, double target_downtrack)
90 {
91 if(std::empty(points)){
92 RCLCPP_WARN_STREAM(basic_autonomy::get_logger(), "Empty points vector received, returning -1");
93 return -1;
94 }
95
96 // Find first point with a downtrack greater than target_downtrack
97 const auto itr = std::find_if(std::cbegin(points), std::cend(points),
98 [&wm = std::as_const(wm), target_downtrack](const auto & point) { return wm->routeTrackPos(point).downtrack > target_downtrack; });
99
100 int best_index = std::size(points) - 1;
101
102 // Set best_index to the last point with a downtrack less than target_downtrack
103 if(itr != std::cbegin(points)){
104 best_index = std::distance(std::cbegin(points), std::prev(itr));
105 }
106 else{
107 best_index = 0;
108 }
109
110 RCLCPP_DEBUG_STREAM(basic_autonomy::get_logger(), "get_nearest_index_by_downtrack>> Found best_index: " << best_index<<", points[i].x(): " << points.at(best_index).x() << ", points[i].y(): " << points.at(best_index).y());
111
112 return best_index;
113 }
114
115 void split_point_speed_pairs(const std::vector<PointSpeedPair>& points,
116 std::vector<lanelet::BasicPoint2d>* basic_points,
117 std::vector<double>* speeds)
118 {
119 basic_points->reserve(points.size());
120 speeds->reserve(points.size());
121
122 for (const auto& p : points)
123 {
124 basic_points->push_back(p.point);
125 speeds->push_back(p.speed);
126 }
127 }
128
129 int get_nearest_index_by_downtrack(const std::vector<PointSpeedPair>& points, const carma_wm::WorldModelConstPtr& wm,
130 const carma_planning_msgs::msg::VehicleState& state)
131 {
132 lanelet::BasicPoint2d state_pos(state.x_pos_global, state.y_pos_global);
133 double ending_downtrack = wm->routeTrackPos(state_pos).downtrack;
134 std::vector<lanelet::BasicPoint2d> basic_points;
135 std::vector<double> speeds;
136 split_point_speed_pairs(points, &basic_points, &speeds);
137 return get_nearest_index_by_downtrack(basic_points, wm, ending_downtrack);
138 }
139
140 int get_nearest_index_by_downtrack(const std::vector<lanelet::BasicPoint2d>& points, const carma_wm::WorldModelConstPtr& wm,
141 const carma_planning_msgs::msg::VehicleState& state)
142 {
143 lanelet::BasicPoint2d state_pos(state.x_pos_global, state.y_pos_global);
144 double ending_downtrack = wm->routeTrackPos(state_pos).downtrack;
145 return get_nearest_index_by_downtrack(points, wm, ending_downtrack);
146 }
147
148 std::vector<lanelet::BasicPoint2d> build_chain_centerline(const carma_wm::WorldModelConstPtr &wm,
149 lanelet::ConstLanelet pivot,
150 double backward_length,
151 double forward_length)
152 {
153 std::vector<lanelet::ConstLanelet> chain{pivot};
154 std::unordered_set<lanelet::Id> visited{pivot.id()};
155
156 double covered_back = carma_wm::geometry::get_lanelet_centerline_length(pivot);
157 while (covered_back < backward_length)
158 {
159 auto previous = wm->getMapRoutingGraph()->previous(chain.front(), false);
160 bool no_predecessor = previous.empty();
161 bool loop_detected = !no_predecessor && visited.count(previous.front().id()) > 0;
162
163 if (no_predecessor)
164 {
165 RCLCPP_WARN_STREAM(basic_autonomy::get_logger(),
166 "create_lanechange_geometry: No routable predecessor lanelet found before lanelet "
167 << chain.front().id() << " (possibly closed or missing from the map). Using the "
168 << covered_back << "m of centerline that was reachable going backward.");
169 }
170
171 if (loop_detected)
172 {
173 RCLCPP_WARN_STREAM(basic_autonomy::get_logger(),
174 "create_lanechange_geometry: Detected a loop in lanelet connectivity before lanelet "
175 << chain.front().id() << "; stopping centerline extension.");
176 }
177
178 if (no_predecessor || loop_detected)
179 {
180 break;
181 }
182
183 lanelet::ConstLanelet prev = previous.front();
184 visited.insert(prev.id());
186 chain.insert(chain.begin(), prev);
187 }
188
189 double covered_fwd = 0.0;
190 while (covered_fwd < forward_length)
191 {
192 auto following = wm->getMapRoutingGraph()->following(chain.back(), false);
193 bool no_successor = following.empty();
194 bool loop_detected = !no_successor && visited.count(following.front().id()) > 0;
195
196 if (no_successor)
197 {
198 RCLCPP_WARN_STREAM(basic_autonomy::get_logger(),
199 "create_lanechange_geometry: No routable successor lanelet found after lanelet "
200 << chain.back().id() << " (possibly closed or missing from the map). Using the "
201 << covered_fwd << "m of centerline that was reachable going forward.");
202 }
203
204 if (loop_detected)
205 {
206 RCLCPP_WARN_STREAM(basic_autonomy::get_logger(),
207 "create_lanechange_geometry: Detected a loop in lanelet connectivity after lanelet "
208 << chain.back().id() << "; stopping centerline extension.");
209 }
210
211 if (no_successor || loop_detected)
212 {
213 break;
214 }
215
216 lanelet::ConstLanelet next = following.front();
217 visited.insert(next.id());
219 chain.push_back(next);
220 }
221
222 std::vector<lanelet::BasicPoint2d> centerline;
223 centerline.reserve(400);
224 for (size_t i = 0; i < chain.size(); ++i)
225 {
226 auto ls = chain[i].centerline2d().basicLineString();
227 if (i == 0)
228 {
229 centerline.insert(centerline.end(), ls.begin(), ls.end());
230 }
231 else
232 {
233 // Concatenate linestring starting from + 1 to avoid duplicating the shared endpoint
234 centerline.insert(centerline.end(), ls.begin() + 1, ls.end());
235 }
236 }
237 return centerline;
238 }
239
240 void extrapolate_to_length(std::vector<lanelet::BasicPoint2d>& centerline, double target_length, const std::string& description)
241 {
242 if (centerline.size() < 2)
243 {
244 throw std::invalid_argument("create_lanechange_geometry: " + description +
245 " has fewer than 2 centerline points; cannot build or extrapolate a lane change trajectory from this map data");
246 }
247
248 double current_length = carma_wm::geometry::compute_arc_lengths(centerline).back();
249 if (current_length >= target_length)
250 {
251 return;
252 }
253
254 RCLCPP_WARN_STREAM(basic_autonomy::get_logger(),
255 "create_lanechange_geometry: Only " << current_length << "m of connected lanelet centerline was "
256 << "available for " << description << " (needed " << target_length << "m). Extrapolating a "
257 << "straight line from the last known heading so a lane change trajectory can still be produced.");
258
259 lanelet::BasicPoint2d last = centerline.back();
260 lanelet::BasicPoint2d prev = centerline[centerline.size() - 2];
261 lanelet::BasicPoint2d direction = last - prev;
262 if (direction.norm() < 1e-6)
263 {
264 // Degenerate direction from the last segment; fall back to the vector from the first to last point
265 direction = last - centerline.front();
266 }
267 direction.normalize();
268
269 constexpr double step = 1.0; // meters between synthetic points, similar to typical map point spacing
270 double remaining = target_length - current_length;
271 for (int step_count = 1; step_count * step < remaining; ++step_count)
272 {
273 centerline.push_back(last + direction * (step_count * step));
274 }
275 centerline.push_back(last + direction * remaining); // ensure the full requested length is covered
276 }
277
278} // namespace waypoint_generation
279} // namespace basic_autonomy
void split_point_speed_pairs(const std::vector< PointSpeedPair > &points, std::vector< lanelet::BasicPoint2d > *basic_points, std::vector< double > *speeds)
Helper method to split a list of PointSpeedPair into separate point and speed lists.
void extrapolate_to_length(std::vector< lanelet::BasicPoint2d > &centerline, double target_length, const std::string &description)
Pads a centerline out to target_length by extrapolating a straight line from its last known heading,...
std::vector< lanelet::BasicPoint2d > build_chain_centerline(const carma_wm::WorldModelConstPtr &wm, lanelet::ConstLanelet pivot, double backward_length, double forward_length)
Builds a centerline covering [pivot_end_point - backward_length, pivot_end_point + forward_length] by...
int get_nearest_point_index(const std::vector< lanelet::BasicPoint2d > &points, const carma_planning_msgs::msg::VehicleState &state)
Returns the nearest point (in terms of cartesian 2d distance) to the provided vehicle pose in the pro...
int get_nearest_index_by_downtrack(const std::vector< lanelet::BasicPoint2d > &points, const carma_wm::WorldModelConstPtr &wm, double target_downtrack)
Returns the nearest "less than" point to the provided vehicle pose in the provided list by utilizing ...
rclcpp::Logger get_logger()
Return the module-level logger used by all basic_autonomy functions.
Definition: log.cpp:32
double get_lanelet_centerline_length(const lanelet::ConstLanelet &ll)
Returns the total 2d arc length of a lanelet's centerline.
Definition: Geometry.cpp:504
std::vector< double > compute_arc_lengths(const std::vector< lanelet::BasicPoint2d > &data)
Compute the arc length at each point around the curve.
Definition: Geometry.cpp:498
std::shared_ptr< const WorldModel > WorldModelConstPtr
Definition: WorldModel.hpp:454