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.
basic_autonomy.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2021-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
19
20namespace basic_autonomy
21{
22 namespace waypoint_generation
23 {
24 std::vector<PointSpeedPair> create_geometry_profile(const std::vector<carma_planning_msgs::msg::Maneuver> &maneuvers, double max_starting_downtrack,const carma_wm::WorldModelConstPtr &wm,
25 carma_planning_msgs::msg::VehicleState &ending_state_before_buffer,const carma_planning_msgs::msg::VehicleState& state,
26 const GeneralTrajConfig &general_config, const DetailedTrajConfig &detailed_config){
27 std::vector<PointSpeedPair> points_and_target_speeds;
28
29 bool first = true;
30 std::unordered_set<lanelet::Id> visited_lanelets;
31
32 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "VehDowntrack:"<<max_starting_downtrack);
33 for(const auto &maneuver : maneuvers)
34 {
35 double starting_downtrack = GET_MANEUVER_PROPERTY(maneuver, start_dist);
36
37 if(first){
38 starting_downtrack = std::min(starting_downtrack, max_starting_downtrack);
39 first = false;
40 }
41 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Used downtrack: " << starting_downtrack);
42
43 if(maneuver.type == carma_planning_msgs::msg::Maneuver::LANE_FOLLOWING){
44 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER),"Creating Lane Follow Geometry");
45 std::vector<PointSpeedPair> lane_follow_points = create_lanefollow_geometry(maneuver, starting_downtrack, wm, general_config, detailed_config, visited_lanelets);
46 points_and_target_speeds.insert(points_and_target_speeds.end(), lane_follow_points.begin(), lane_follow_points.end());
47 }
48 else if(maneuver.type == carma_planning_msgs::msg::Maneuver::LANE_CHANGE){
49 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Creating Lane Change Geometry");
50 std::vector<PointSpeedPair> lane_change_points = get_lanechange_points_from_maneuver(maneuver, starting_downtrack, wm, ending_state_before_buffer, state, general_config, detailed_config);
51 points_and_target_speeds.insert(points_and_target_speeds.end(), lane_change_points.begin(), lane_change_points.end());
52 }
53 else{
54 throw std::invalid_argument("This maneuver type is not supported");
55 }
56
57 }
58
59 //Add buffer ending to lane follow points at the end of maneuver(s) end dist
60 if(maneuvers.back().type == carma_planning_msgs::msg::Maneuver::LANE_FOLLOWING){
61 points_and_target_speeds = add_lanefollow_buffer(wm, points_and_target_speeds, maneuvers, ending_state_before_buffer, detailed_config);
62
63 }
64 return points_and_target_speeds;
65
66 }
67
68 std::vector<PointSpeedPair> create_lanefollow_geometry(const carma_planning_msgs::msg::Maneuver &maneuver, double starting_downtrack,
69 const carma_wm::WorldModelConstPtr &wm, const GeneralTrajConfig &general_config,
70 const DetailedTrajConfig &detailed_config, std::unordered_set<lanelet::Id> &visited_lanelets)
71 {
72 if(maneuver.type != carma_planning_msgs::msg::Maneuver::LANE_FOLLOWING){
73 throw std::invalid_argument("Create_lanefollow called on a maneuver type which is not LANE_FOLLOW");
74 }
75 std::vector<PointSpeedPair> points_and_target_speeds;
76
77 carma_planning_msgs::msg::LaneFollowingManeuver lane_following_maneuver = maneuver.lane_following_maneuver;
78
79 if (maneuver.lane_following_maneuver.lane_ids.empty())
80 {
81 throw std::invalid_argument("No lanelets are defined for lanefollow maneuver");
82 }
83
84 std::vector<lanelet::ConstLanelet> lanelets = { wm->getMap()->laneletLayer.get(stoi(lane_following_maneuver.lane_ids[0]))}; // Accept first lanelet reguardless
85 for (size_t i = 1; i < lane_following_maneuver.lane_ids.size(); i++) // Iterate over remaining lanelets and check if they are followers of the previous lanelet
86 {
87 auto ll_id = lane_following_maneuver.lane_ids[i];
88 int cur_id = stoi(ll_id);
89 auto cur_ll = wm->getMap()->laneletLayer.get(cur_id);
90 auto following_lanelets = wm->getMapRoutingGraph()->following(lanelets.back());
91
92 bool is_follower = false;
93 for (auto follower_ll : following_lanelets )
94 {
95 if (follower_ll.id() == cur_ll.id())
96 {
97 is_follower = true;
98 break;
99 }
100 }
101
102 if (!is_follower)
103 {
104 throw std::invalid_argument("Invalid list of lanelets they are not followers");
105 }
106
107 lanelets.push_back(cur_ll); // Keep lanelet
108
109 }
110
111 // Add extra lanelet to ensure there are sufficient points for buffer
112 auto extra_following_lanelets = wm->getMapRoutingGraph()->following(lanelets.back());
113
114 for (auto llt : wm->getRoute()->shortestPath())
115 {
116 for (size_t i = 0; i < extra_following_lanelets.size(); i++)
117 {
118 if (llt.id() == extra_following_lanelets[i].id())
119 {
120 lanelets.push_back(extra_following_lanelets[i]);
121 break;
122 }
123 }
124 }
125
126 if (lanelets.empty())
127 {
128 RCLCPP_ERROR_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Detected no lanelets between starting downtrack: "<< starting_downtrack << ", and lane_following_maneuver.end_dist: "<< lane_following_maneuver.end_dist);
129 throw std::invalid_argument("Detected no lanelets between starting_downtrack and end_dist");
130 }
131
132 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Maneuver");
133
134 lanelet::BasicLineString2d downsampled_centerline;
135 // 400 value here is an arbitrary attempt at improving inlane-cruising performance by reducing copy operations.
136 // Value picked based on annecdotal evidence from STOL system testing
137 downsampled_centerline.reserve(400);
138
139 //getLaneletsBetween is inclusive of lanelets between its two boundaries
140 //which may return lanechange lanelets, so
141 //exclude lanechanges and plan for only the straight part
142 size_t curr_idx = 0;
143 auto following_lanelets = wm->getMapRoutingGraph()->following(lanelets[curr_idx]);
144 lanelet::ConstLanelets straight_lanelets;
145
146 if(lanelets.size() <= 1) //no lane change anyways if only size 1
147 {
148 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Detected one straight lanelet Id:" << lanelets[curr_idx].id());
149 straight_lanelets = lanelets;
150 }
151 else
152 {
153 // skip all lanechanges until lane follow starts
154 while (curr_idx + 1 < lanelets.size() &&
155 std::find(following_lanelets.begin(),following_lanelets.end(), lanelets[curr_idx + 1]) == following_lanelets.end())
156 {
157 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "As there were no directly following lanelets after this, skipping lanelet id: " << lanelets[curr_idx].id());
158 curr_idx ++;
159 following_lanelets = wm->getMapRoutingGraph()->following(lanelets[curr_idx]);
160 }
161
162 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Added lanelet Id for lane follow: " << lanelets[curr_idx].id());
163 // guaranteed to have at least one "straight" lanelet (e.g the last one in the list)
164 straight_lanelets.push_back(lanelets[curr_idx]);
165 // add all lanelets on the straight road until next lanechange
166 while (curr_idx + 1 < lanelets.size() &&
167 std::find(following_lanelets.begin(),following_lanelets.end(), lanelets[curr_idx + 1]) != following_lanelets.end())
168 {
169 curr_idx++;
170 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Added lanelet Id forlane follow: " << lanelets[curr_idx].id());
171 straight_lanelets.push_back(lanelets[curr_idx]);
172 following_lanelets = wm->getMapRoutingGraph()->following(lanelets[curr_idx]);
173 }
174
175 }
176
177 for (auto l : straight_lanelets)
178 {
179 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Processing lanelet ID: " << l.id());
180 if (visited_lanelets.find(l.id()) == visited_lanelets.end())
181 {
182
183 bool is_turn = false;
184 if(l.hasAttribute("turn_direction")) {
185 std::string turn_direction = l.attribute("turn_direction").value();
186 is_turn = turn_direction.compare("left") == 0 || turn_direction.compare("right") == 0;
187 }
188
189 lanelet::BasicLineString2d centerline = l.centerline2d().basicLineString();
190 lanelet::BasicLineString2d downsampled_points;
191 if (is_turn) {
192 downsampled_points = carma_ros2_utils::containers::downsample_vector(centerline, general_config.turn_downsample_ratio);
193 } else {
194 downsampled_points = carma_ros2_utils::containers::downsample_vector(centerline, general_config.default_downsample_ratio);
195 }
196
197 if(downsampled_centerline.size() != 0 && downsampled_points.size() != 0 // If this is not the first lanelet and the points are closer than 1m drop the first point to prevent overlap
198 && lanelet::geometry::distance2d(downsampled_points.front(), downsampled_centerline.back()) <1.2){
199 downsampled_points = lanelet::BasicLineString2d(downsampled_points.begin() + 1, downsampled_points.end());
200 }
201
202 downsampled_centerline = carma_wm::geometry::concatenate_line_strings(downsampled_centerline, downsampled_points);
203 visited_lanelets.insert(l.id());
204 }
205 }
206
207 bool first = true;
208 for (auto p : downsampled_centerline)
209 {
210 if (first && !points_and_target_speeds.empty())
211 {
212 first = false;
213 continue; // Skip the first point if we have already added points from a previous maneuver to avoid duplicates
214 }
215 PointSpeedPair pair;
216 pair.point = p;
217 pair.speed = lane_following_maneuver.end_speed;
218 points_and_target_speeds.push_back(pair);
219 }
220
221 return points_and_target_speeds;
222
223 }
224
225 std::vector<PointSpeedPair> add_lanefollow_buffer(const carma_wm::WorldModelConstPtr &wm, std::vector<PointSpeedPair>& points_and_target_speeds, const std::vector<carma_planning_msgs::msg::Maneuver> &maneuvers,
226 carma_planning_msgs::msg::VehicleState &ending_state_before_buffer, const DetailedTrajConfig &detailed_config){
227
228
229 double starting_route_downtrack = wm->routeTrackPos(points_and_target_speeds.front().point).downtrack;
230
231 // Always try to add the maximum buffer. Even if the route ends it may still be possible to add buffered points.
232 // This does mean that downstream components might not be able to assume the buffer points are on the route
233 // though this is not likely to be an issue as they are buffer only
234 double ending_downtrack = maneuvers.back().lane_following_maneuver.end_dist + detailed_config.buffer_ending_downtrack;
235
236 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Add lanefollow buffer: ending_downtrack: " << ending_downtrack << ", maneuvers.back().lane_following_maneuver.end_dist: " << maneuvers.back().lane_following_maneuver.end_dist <<
237 ", detailed_config.buffer_ending_downtrack: " << detailed_config.buffer_ending_downtrack);
238
239 size_t max_i = points_and_target_speeds.size() - 1;
240 size_t unbuffered_idx = points_and_target_speeds.size() - 1;
241 bool found_unbuffered_idx = false;
242 double dist_accumulator = starting_route_downtrack;
243 lanelet::BasicPoint2d prev_point;
244
245 boost::optional<lanelet::BasicPoint2d> delta_point;
246 for (size_t i = 0; i < points_and_target_speeds.size(); ++i) {
247 auto current_point = points_and_target_speeds[i].point;
248
249 if (i == 0) {
250 prev_point = current_point;
251 continue;
252 }
253
254 double delta_d = lanelet::geometry::distance2d(prev_point, current_point);
255
256 dist_accumulator += delta_d;
257 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Index i: " << i << ", delta_d: " << delta_d << ", dist_accumulator:" << dist_accumulator <<", current_point.x():" << current_point.x() <<
258 "current_point.y():" << current_point.y());
259 if (dist_accumulator > maneuvers.back().lane_following_maneuver.end_dist && !found_unbuffered_idx)
260 {
261 unbuffered_idx = i - 1;
262 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Found index unbuffered_idx at: " << unbuffered_idx);
263 found_unbuffered_idx = true;
264 }
265
266 if (dist_accumulator > ending_downtrack) {
267 max_i = i;
268 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Max_i breaking at: i: " << i << ", max_i: " << max_i);
269 break;
270 }
271
272 // If there are no more points to add but we haven't reached the ending downtrack then
273 // construct an extrapolated straight line from the final point and keep adding to this line until the downtrack is met
274 // Since this is purely needed to allow for a spline fit edge case, it should have minimal impact on the actual steering behavior of the vehicle
275 if (i == points_and_target_speeds.size() - 1) // dist_accumulator < ending_downtrack is guaranteed by earlier conditional
276 {
277
278 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Extending trajectory using buffer beyond end of target lanelet");
279 int j = i - 1;
280 while (delta_d < epsilon_ && j >= 0 && !delta_point)
281 {
282 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Looking at index j: " << j << ", where i: " << i);
283 prev_point = points_and_target_speeds.at(j).point;
284 j--;
285 delta_d = lanelet::geometry::distance2d(prev_point, current_point);
286 }
287
288 if (j < 0 && delta_d < epsilon_) //a very rare case where only duplicate points exist in the entire trajectory, so it wasn't possible to extend
289 break;
290
291 if (!delta_point) { // Set the step size based on last two distinct points
292 delta_point = (current_point - prev_point) * 0.25; // Use a smaller step size then default to help ensure enough points are generated;
293 }
294
295 // Create an extrapolated new point
296 auto new_point = current_point + delta_point.get();
297
298 PointSpeedPair new_pair;
299 new_pair.point = new_point;
300 new_pair.speed = points_and_target_speeds.back().speed;
301
302
303 points_and_target_speeds.push_back(new_pair);
304 }
305
306 prev_point = current_point;
307 }
308
309 ending_state_before_buffer.x_pos_global = points_and_target_speeds[unbuffered_idx].point.x();
310 ending_state_before_buffer.y_pos_global = points_and_target_speeds[unbuffered_idx].point.y();
311 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Here ending_state_before_buffer.x_pos_global: " << ending_state_before_buffer.x_pos_global <<
312 ", and y_pos_global" << ending_state_before_buffer.y_pos_global);
313
314 std::vector<PointSpeedPair> constrained_points(points_and_target_speeds.begin(), points_and_target_speeds.begin() + max_i);
315
316 return constrained_points;
317 }
318
319 std::vector<lanelet::BasicPoint2d> create_lanechange_geometry(lanelet::Id starting_lane_id, lanelet::Id ending_lane_id, double starting_downtrack, double ending_downtrack,
320 const carma_wm::WorldModelConstPtr &wm, int downsample_ratio, double buffer_ending_downtrack)
321 {
322 std::vector<lanelet::BasicPoint2d> centerline_points;
323
324 //Get starting lanelet and ending lanelets
325 lanelet::ConstLanelet starting_lanelet = wm->getMap()->laneletLayer.get(starting_lane_id);
326 lanelet::ConstLanelet ending_lanelet = wm->getMap()->laneletLayer.get(ending_lane_id);
327
328 lanelet::ConstLanelets starting_lane;
329 starting_lane.push_back(starting_lanelet);
330
331 std::vector<lanelet::BasicPoint2d> reference_centerline;
332 // 400 value here is an arbitrary attempt at improving performance by reducing copy operations.
333 // Value picked based on annecdotal evidence from STOL system testing
334 reference_centerline.reserve(400);
335 bool shared_boundary_found = false;
336 bool is_lanechange_left = false;
337
338 lanelet::BasicLineString2d current_lanelet_centerline = starting_lanelet.centerline2d().basicLineString();
339 lanelet::ConstLanelet current_lanelet = starting_lanelet;
340 reference_centerline.insert(reference_centerline.end(), current_lanelet_centerline.begin(), current_lanelet_centerline.end());
341
342 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Searching for shared boundary with starting lanechange lanelet " << std::to_string(current_lanelet.id()) << " and ending lanelet " << std::to_string(ending_lanelet.id()));
343 while(!shared_boundary_found){
344 //Assumption- Adjacent lanelets share lane boundary
345 if(current_lanelet.leftBound() == ending_lanelet.rightBound()){
346 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Lanelet " << std::to_string(current_lanelet.id()) << " shares left boundary with " << std::to_string(ending_lanelet.id()));
347 is_lanechange_left = true;
348 shared_boundary_found = true;
349 }
350
351 else if(current_lanelet.rightBound() == ending_lanelet.leftBound()){
352 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Lanelet " << std::to_string(current_lanelet.id()) << " shares right boundary with " << std::to_string(ending_lanelet.id()));
353 shared_boundary_found = true;
354 }
355
356 else{
357 //If there are no following lanelets on route, lanechange should be completing before reaching it
358 if(wm->getMapRoutingGraph()->following(current_lanelet, false).empty())
359 {
360 // Maneuver requires we travel further before completing lane change, but no routable lanelet directly ahead
361 //In this case we have reached a lanelet which does not have a routable lanelet ahead + isn't adjacent to the lanelet where lane change ends
362 //A lane change should have already happened at this point
363 throw(std::invalid_argument("No following lanelets from current lanelet reachable without a lane change, incorrectly chosen end lanelet"));
364 }
365
366 current_lanelet = wm->getMapRoutingGraph()->following(current_lanelet, false).front();
367 if(current_lanelet.id() == starting_lanelet.id()){
368 //Looped back to starting lanelet
369 throw(std::invalid_argument("No lane change in path"));
370 }
371 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Now checking for shared lane boundary with lanelet " << std::to_string(current_lanelet.id()) << " and ending lanelet " << std::to_string(ending_lanelet.id()));
372 auto current_lanelet_linestring = current_lanelet.centerline2d().basicLineString();
373 //Concatenate linestring starting from + 1 to avoid overlap
374 reference_centerline.insert(reference_centerline.end(), current_lanelet_linestring.begin() + 1, current_lanelet_linestring.end());
375 starting_lane.push_back(current_lanelet);
376 }
377 }
378
379 // Create the target lane centerline using lanelets adjacent to the lanechange lanelets in the starting lane
380 std::vector<lanelet::BasicPoint2d> target_lane_centerline;
381 for(size_t i = 0;i<starting_lane.size();++i){
382 lanelet::ConstLanelet curr_end_lanelet;
383
384 if(is_lanechange_left){
385
386 //get left lanelet
387 if(wm->getMapRoutingGraph()->left(starting_lane[i])){
388 curr_end_lanelet = wm->getMapRoutingGraph()->left(starting_lane[i]).get();
389 }
390 else{
391 curr_end_lanelet = wm->getMapRoutingGraph()->adjacentLeft(starting_lane[i]).get();
392 }
393 }
394 else{
395
396 //get right lanelet
397 if(wm->getMapRoutingGraph()->right(starting_lane[i])){
398 curr_end_lanelet = wm->getMapRoutingGraph()->right(starting_lane[i]).get();
399 }
400 else{
401 curr_end_lanelet = wm->getMapRoutingGraph()->adjacentRight(starting_lane[i]).get();
402 }
403 }
404
405 auto target_lane_linestring = curr_end_lanelet.centerline2d().basicLineString();
406 //Concatenate linestring starting from + 1 to avoid overlap
407 target_lane_centerline.insert(target_lane_centerline.end(), target_lane_linestring.begin() + 1, target_lane_linestring.end());
408
409 }
410
411 //Downsample centerlines
412 // 400 value here is an arbitrary attempt at improving performance by reducing copy operations.
413 // Value picked based on annecdotal evidence from STOL system testing
414
415 std::vector<lanelet::BasicPoint2d> downsampled_starting_centerline;
416 downsampled_starting_centerline.reserve(400);
417 downsampled_starting_centerline = carma_ros2_utils::containers::downsample_vector(reference_centerline, downsample_ratio);
418
419 std::vector<lanelet::BasicPoint2d> downsampled_target_centerline;
420 downsampled_target_centerline.reserve(400);
421 downsampled_target_centerline = carma_ros2_utils::containers::downsample_vector(target_lane_centerline, downsample_ratio);
422
423 // Constrain centerlines to starting and ending downtrack
424 int start_index_starting_centerline = waypoint_generation::get_nearest_index_by_downtrack(downsampled_starting_centerline, wm, starting_downtrack);
425 carma_planning_msgs::msg::VehicleState start_state;
426 start_state.x_pos_global = downsampled_starting_centerline[start_index_starting_centerline].x();
427 start_state.y_pos_global = downsampled_starting_centerline[start_index_starting_centerline].y();
428 int start_index_target_centerline = waypoint_generation::get_nearest_point_index(downsampled_target_centerline, start_state);
429
430 int end_index_target_centerline = waypoint_generation::get_nearest_index_by_downtrack(downsampled_target_centerline, wm, ending_downtrack);
431 carma_planning_msgs::msg::VehicleState end_state;
432 end_state.x_pos_global = downsampled_target_centerline[end_index_target_centerline].x();
433 end_state.y_pos_global = downsampled_target_centerline[end_index_target_centerline].y();
434 int end_index_starting_centerline = waypoint_generation::get_nearest_point_index(downsampled_starting_centerline, end_state);
435
436 std::vector<lanelet::BasicPoint2d> constrained_start_centerline(downsampled_starting_centerline.begin() + start_index_starting_centerline, downsampled_starting_centerline.begin() + end_index_starting_centerline);
437 std::vector<lanelet::BasicPoint2d> constrained_target_centerline(downsampled_target_centerline.begin() + start_index_target_centerline, downsampled_target_centerline.begin() + end_index_target_centerline);
438
439 // If constrained centerlines are not the same size - resample to ensure same size along both centerlines
440 if(constrained_start_centerline.size() != constrained_target_centerline.size())
441 {
442 auto centerlines = resample_linestring_pair_to_same_size(constrained_start_centerline, constrained_target_centerline);
443 constrained_start_centerline = centerlines[0];
444 constrained_target_centerline = centerlines[1];
445
446 }
447
448 //Create Trajectory geometry
449 double delta_step = 1.0 / constrained_start_centerline.size();
450
451 for (size_t i = 0; i < constrained_start_centerline.size(); ++i)
452 {
453 lanelet::BasicPoint2d current_position;
454 lanelet::BasicPoint2d start_lane_pt = constrained_start_centerline[i];
455 lanelet::BasicPoint2d target_lane_pt = constrained_target_centerline[i];
456 double delta = delta_step * i;
457 current_position.x() = target_lane_pt.x() * delta + (1 - delta) * start_lane_pt.x();
458 current_position.y() = target_lane_pt.y() * delta + (1 - delta) * start_lane_pt.y();
459
460 centerline_points.push_back(current_position);
461 }
462
463 // Add points from the remaining length of the target lanelet to provide sufficient distance for adding buffer
464 double dist_to_target_lane_end = lanelet::geometry::distance2d(centerline_points.back(), downsampled_target_centerline.back());
465 centerline_points.insert(centerline_points.end(), downsampled_target_centerline.begin() + end_index_target_centerline, downsampled_target_centerline.end());
466
467 // If the additional distance from the remaining length of the target lanelet does not provide more than the required
468 // buffer_ending_downtrack, then also add points from the lanelet following the target lanelet
469 if (dist_to_target_lane_end < buffer_ending_downtrack) {
470 auto following_lanelets = wm->getMapRoutingGraph()->following(ending_lanelet, false);
471 if(!following_lanelets.empty()){
472 //Arbitrarily choosing first following lanelet for buffer since points are only being used to fit spline
473 auto following_lanelet_centerline = following_lanelets.front().centerline2d().basicLineString();
474 centerline_points.insert(centerline_points.end(), following_lanelet_centerline.begin(),
475 following_lanelet_centerline.end());
476 }
477 }
478
479 return centerline_points;
480 }
481
482 std::vector<std::vector<lanelet::BasicPoint2d>> resample_linestring_pair_to_same_size(std::vector<lanelet::BasicPoint2d>& line_1, std::vector<lanelet::BasicPoint2d>& line_2){
483
484 auto start_time = std::chrono::high_resolution_clock::now(); // Start timing the execution time for planning so it can be logged
485
486 std::vector<std::vector<lanelet::BasicPoint2d>> output;
487
488 //Fit centerlines to a spline
489 std::unique_ptr<smoothing::SplineI> fit_curve_1 = compute_fit(line_1); // Compute splines based on curve points
490 if (!fit_curve_1)
491 {
492 throw std::invalid_argument("Could not fit a spline curve along the starting_lane centerline points!");
493 }
494
495 std::unique_ptr<smoothing::SplineI> fit_curve_2 = compute_fit(line_2); // Compute splines based on curve points
496 if (!fit_curve_2)
497 {
498 throw std::invalid_argument("Could not fit a spline curve along the ending_lane centerline points!");
499 }
500
501 //Sample spline to get centerlines of equal size
502 std::vector<lanelet::BasicPoint2d> all_sampling_points_line1;
503 std::vector<lanelet::BasicPoint2d> all_sampling_points_line2;
504
505 size_t total_point_size = std::min(line_1.size(), line_2.size());
506
507 all_sampling_points_line1.reserve(1 + total_point_size * 2);
508 std::vector<double> downtracks_raw_line1 = carma_wm::geometry::compute_arc_lengths(line_1);
509 //int total_step_along_curve1 = static_cast<int>(downtracks_raw_line1.back() / 2.0);
510 //double step_threshold_line1 = (double)total_step_along_curve1 / (double)total_point_size;
511 //TODO: are we missing some computation here? step_threshold_line1 and step_threshold_line2 are not used anywhere
512 // and these calcs can be deleted (see below also).
513
514 all_sampling_points_line2.reserve(1 + total_point_size * 2);
515 std::vector<double> downtracks_raw_line2 = carma_wm::geometry::compute_arc_lengths(line_2);
516 //TODO: unused variable: int total_step_along_curve2 = static_cast<int>(downtracks_raw_line2.back() / 2.0);
517 //TODO: unused variable: double step_threshold_line2 = (double)total_step_along_curve2 / (double)total_point_size;
518
519 double scaled_steps_along_curve = 0.0; // from 0 (start) to 1 (end) for the whole trajectory
520
521
522 all_sampling_points_line2.reserve(1 + total_point_size * 2);
523
524 for(size_t i = 0;i<total_point_size; ++i){
525 lanelet::BasicPoint2d p1 = (*fit_curve_1)(scaled_steps_along_curve);
526 lanelet::BasicPoint2d p2 = (*fit_curve_2)(scaled_steps_along_curve);
527 all_sampling_points_line1.push_back(p1);
528 all_sampling_points_line2.push_back(p2);
529
530 scaled_steps_along_curve += 1.0 / total_point_size; //adding steps_along_curve_step_size
531 }
532
533 output.push_back(all_sampling_points_line1);
534 output.push_back(all_sampling_points_line2);
535
536 auto end_time = std::chrono::high_resolution_clock::now();
537
538 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
539 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "ExecutionTime for resample lane change centerlines: " << duration.count() << " milliseconds");
540
541 return output;
542 }
543
544 std::vector<PointSpeedPair> get_lanechange_points_from_maneuver(const carma_planning_msgs::msg::Maneuver &maneuver, double starting_downtrack,
545 const carma_wm::WorldModelConstPtr &wm, carma_planning_msgs::msg::VehicleState &ending_state_before_buffer,
546 const carma_planning_msgs::msg::VehicleState &state, const GeneralTrajConfig &general_config,const DetailedTrajConfig &detailed_config)
547 {
548 if(maneuver.type != carma_planning_msgs::msg::Maneuver::LANE_CHANGE){
549 throw std::invalid_argument("Create_lanechange called on a maneuver type which is not LANE_CHANGE");
550 }
551 std::vector<PointSpeedPair> points_and_target_speeds;
552 std::unordered_set<lanelet::Id> visited_lanelets;
553
554 carma_planning_msgs::msg::LaneChangeManeuver lane_change_maneuver = maneuver.lane_change_maneuver;
555 double ending_downtrack = lane_change_maneuver.end_dist;
556 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Maneuver ending downtrack:"<<ending_downtrack);
557 if(starting_downtrack >= ending_downtrack)
558 {
559 throw(std::invalid_argument("Start distance is greater than or equal to ending distance"));
560 }
561
562 //get route between starting and ending downtracks - downtracks should be constant for complete length of maneuver
563 std::vector<lanelet::BasicPoint2d> route_geometry = create_lanechange_geometry(std::stoi(lane_change_maneuver.starting_lane_id),std::stoi(lane_change_maneuver.ending_lane_id),
564 starting_downtrack, ending_downtrack, wm, general_config.default_downsample_ratio, detailed_config.buffer_ending_downtrack);
565 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Route geometry size:"<<route_geometry.size());
566
567 lanelet::BasicPoint2d state_pos(state.x_pos_global, state.y_pos_global);
568 double current_downtrack = wm->routeTrackPos(state_pos).downtrack;
569 int nearest_pt_index = get_nearest_index_by_downtrack(route_geometry, wm, current_downtrack);
570 int ending_pt_index = get_nearest_index_by_downtrack(route_geometry, wm, ending_downtrack);
571 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Nearest pt index in maneuvers to points: "<< nearest_pt_index);
572 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Ending pt index in maneuvers to points: "<< ending_pt_index);
573
574 ending_state_before_buffer.x_pos_global = route_geometry[ending_pt_index].x();
575 ending_state_before_buffer.y_pos_global = route_geometry[ending_pt_index].y();
576
577 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "ending_state_before_buffer_:"<<ending_state_before_buffer.x_pos_global <<
578 ", ending_state_before_buffer_.y_pos_global" << ending_state_before_buffer.y_pos_global);
579
580
581 double route_length = wm->getRouteEndTrackPos().downtrack;
582
583 if (ending_downtrack + detailed_config.buffer_ending_downtrack < route_length)
584 {
585 ending_pt_index = get_nearest_index_by_downtrack(route_geometry, wm, ending_downtrack + detailed_config.buffer_ending_downtrack);
586 }
587 else
588 {
589 ending_pt_index = route_geometry.size() - 1;
590 }
591
592 lanelet::BasicLineString2d future_route_geometry(route_geometry.begin() + nearest_pt_index, route_geometry.begin() + ending_pt_index);
593 bool first = true;
594 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Future geom size:"<< future_route_geometry.size());
595
596 for (auto p : future_route_geometry)
597 {
598 if (first && !points_and_target_speeds.empty())
599 {
600 first = false;
601 continue; // Skip the first point if we have already added points from a previous maneuver to avoid duplicates
602 }
603 PointSpeedPair pair;
604 pair.point = p;
605 //If current speed is above min speed, keep at current speed. Otherwise use end speed from maneuver.
606 pair.speed = (state.longitudinal_vel > detailed_config.minimum_speed) ? state.longitudinal_vel : lane_change_maneuver.end_speed;
607 points_and_target_speeds.push_back(pair);
608
609 }
610 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Const speed assigned:"<<points_and_target_speeds.back().speed);
611 return points_and_target_speeds;
612
613
614 }
615
616 std::vector<double> apply_speed_limits(const std::vector<double> speeds,
617 const std::vector<double> speed_limits)
618 {
619 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Speeds list size: " << speeds.size());
620 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "SpeedLimits list size: " << speed_limits.size());
621
622 if (speeds.size() != speed_limits.size())
623 {
624 throw std::invalid_argument("Speeds and speed limit lists not same size");
625 }
626 std::vector<double> out;
627 for (size_t i = 0; i < speeds.size(); i++)
628 {
629 out.push_back(std::min(speeds[i], speed_limits[i]));
630 }
631
632 return out;
633 }
634
635 Eigen::Isometry2d compute_heading_frame(const lanelet::BasicPoint2d &p1,
636 const lanelet::BasicPoint2d &p2)
637 {
638 Eigen::Rotation2Dd yaw(atan2(p2.y() - p1.y(), p2.x() - p1.x()));
639
641 }
642
643 std::vector<PointSpeedPair> constrain_to_time_boundary(const std::vector<PointSpeedPair> &points,
644 double time_span)
645 {
646 std::vector<lanelet::BasicPoint2d> basic_points;
647 std::vector<double> speeds;
648 split_point_speed_pairs(points, &basic_points, &speeds);
649
650 std::vector<double> downtracks = carma_wm::geometry::compute_arc_lengths(basic_points);
651
652 size_t time_boundary_exclusive_index =
653 trajectory_utils::time_boundary_index(downtracks, speeds, time_span);
654
655 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "time_boundary_exclusive_index = " << time_boundary_exclusive_index);
656
657 if (time_boundary_exclusive_index == 0)
658 {
659 throw std::invalid_argument("No points to fit in timespan");
660 }
661
662 std::vector<PointSpeedPair> time_bound_points;
663 time_bound_points.reserve(time_boundary_exclusive_index);
664
665 if (time_boundary_exclusive_index == points.size())
666 {
667 time_bound_points.insert(time_bound_points.end(), points.begin(),
668 points.end()); // All points fit within time boundary
669 }
670 else
671 {
672 time_bound_points.insert(time_bound_points.end(), points.begin(),
673 points.begin() + time_boundary_exclusive_index - 1); // Limit points by time boundary
674 }
675
676 return time_bound_points;
677 }
678
679 std::pair<double, size_t> min_with_exclusions(const std::vector<double> &values, const std::unordered_set<size_t> &excluded)
680 {
681 double min = std::numeric_limits<double>::max();
682 size_t best_idx = -1;
683 for (size_t i = 0; i < values.size(); i++)
684 {
685 if (excluded.find(i) != excluded.end())
686 {
687 continue;
688 }
689
690 if (values[i] < min)
691 {
692 min = values[i];
693 best_idx = i;
694 }
695 }
696 return std::make_pair(min, best_idx);
697 }
698
699 std::vector<double> optimize_speed(const std::vector<double> &downtracks, const std::vector<double> &curv_speeds, double accel_limit)
700 {
701 if (downtracks.size() != curv_speeds.size())
702 {
703 throw std::invalid_argument("Downtracks and speeds do not have the same size");
704 }
705
706 if (accel_limit <= 0)
707 {
708 throw std::invalid_argument("Accel limits should be positive");
709 }
710
711 bool optimize = true;
712 std::unordered_set<size_t> visited_idx;
713 visited_idx.reserve(curv_speeds.size());
714
715 std::vector<double> output = curv_speeds;
716
717 while (optimize)
718 {
719 auto min_pair = min_with_exclusions(curv_speeds, visited_idx);
720 int min_idx = std::get<1>(min_pair);
721 if (min_idx == -1)
722 {
723 break;
724 }
725
726 visited_idx.insert(min_idx); // Mark this point as visited
727
728 double v_i = std::get<0>(min_pair);
729 double x_i = downtracks[min_idx];
730 for (int i = min_idx - 1; i > 0; i--)
731 { // NOTE: Do not use size_t for i type here as -- with > 0 will result in overflow
732 // First point's speed is left unchanged as it is current speed of the vehicle
733 double v_f = curv_speeds[i];
734 double dv = v_f - v_i;
735
736 double x_f = downtracks[i];
737 double dx = x_f - x_i;
738
739 if (dv > 0)
740 {
741 v_f = std::min(v_f, sqrt(v_i * v_i - 2 * accel_limit * dx)); // inverting accel as we are only visiting deceleration case
742 visited_idx.insert(i);
743 }
744 else if (dv < 0)
745 {
746 break;
747 }
748 output[i] = v_f;
749 v_i = v_f;
750 x_i = x_f;
751 }
752 }
753
754 log::printDoublesPerLineWithPrefix("only_reverse[i]: ", output);
755
756 output = trajectory_utils::apply_accel_limits_by_distance(downtracks, output, accel_limit, accel_limit);
757 log::printDoublesPerLineWithPrefix("after_forward[i]: ", output);
758
759 return output;
760 }
761
762 std::vector<carma_planning_msgs::msg::TrajectoryPlanPoint> trajectory_from_points_times_orientations(
763 const std::vector<lanelet::BasicPoint2d> &points, const std::vector<double> &times, const std::vector<double> &yaws,
764 rclcpp::Time startTime, const std::string &desired_controller_plugin)
765 {
766 if (points.size() != times.size() || points.size() != yaws.size())
767 {
768 throw std::invalid_argument("All input vectors must have the same size");
769 }
770
771 std::vector<carma_planning_msgs::msg::TrajectoryPlanPoint> traj;
772 traj.reserve(points.size());
773
774 for (size_t i = 0; i < points.size(); i++)
775 {
776 carma_planning_msgs::msg::TrajectoryPlanPoint tpp;
777 rclcpp::Duration relative_time(times[i] * 1e9); // Conversion of times[i] from seconds to nanoseconds
778 tpp.target_time = startTime + relative_time;
779 tpp.x = points[i].x();
780 tpp.y = points[i].y();
781 tpp.yaw = yaws[i];
782
783 tpp.controller_plugin_name = desired_controller_plugin;
784 //tpp.planner_plugin_name //Planner plugin name is filled in the tactical plugin
785
786 traj.push_back(tpp);
787 }
788
789 return traj;
790 }
791
792
793 std::vector<PointSpeedPair> attach_past_points(const std::vector<PointSpeedPair> &points_set, std::vector<PointSpeedPair> future_points,
794 const int nearest_pt_index, double back_distance)
795 {
796 std::vector<PointSpeedPair> back_and_future;
797 back_and_future.reserve(points_set.size());
798 double total_dist = 0;
799 int min_i = 0;
800
801 // int must be used here to avoid overflow when i = 0
802 for (int i = nearest_pt_index; i >= 0; --i)
803 {
804 min_i = i;
805 total_dist += lanelet::geometry::distance2d(points_set[i].point, points_set[i - 1].point);
806
807 if (total_dist > back_distance)
808 {
809 break;
810 }
811 }
812
813 back_and_future.insert(back_and_future.end(), points_set.begin() + min_i, points_set.begin() + nearest_pt_index + 1);
814 back_and_future.insert(back_and_future.end(), future_points.begin(), future_points.end());
815 return back_and_future;
816 }
817
818 std::unique_ptr<basic_autonomy::smoothing::SplineI> compute_fit(const std::vector<lanelet::BasicPoint2d> &basic_points)
819 {
820 if (basic_points.size() < 4)
821 {
822 RCLCPP_WARN_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Insufficient Spline Points");
823 return nullptr;
824 }
825
826 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Original basic_points size: " << basic_points.size());
827
828 std::vector<lanelet::BasicPoint2d> resized_basic_points = basic_points;
829
830 // The large the number of points, longer it takes to calculate a spline fit
831 // So if the basic_points vector size is large, only the first 400 points are used to compute a spline fit.
832 if (resized_basic_points.size() > 400)
833 {
834 resized_basic_points.resize(400);
835 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Resized basic_points size: " << resized_basic_points.size());
836
837 size_t left_points_size = basic_points.size() - resized_basic_points.size();
838 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Number of left out basic_points size: " << left_points_size);
839
840 float percent_points_lost = 100.0 * (float)left_points_size/basic_points.size();
841
842 if (percent_points_lost > 50.0)
843 {
844 RCLCPP_WARN_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "More than half of basic points are ignored for spline fitting");
845 }
846 }
847
848 std::unique_ptr<basic_autonomy::smoothing::SplineI> spl = std::make_unique<basic_autonomy::smoothing::BSpline>();
849
850 spl->setPoints(resized_basic_points);
851
852 return spl;
853 }
854
855 double compute_curvature_at(const basic_autonomy::smoothing::SplineI &fit_curve, double step_along_the_curve)
856 {
857 lanelet::BasicPoint2d f_prime_pt = fit_curve.first_deriv(step_along_the_curve);
858 lanelet::BasicPoint2d f_prime_prime_pt = fit_curve.second_deriv(step_along_the_curve);
859 // Convert to 3d vector to do 3d vector operations like cross.
860 Eigen::Vector3d f_prime = {f_prime_pt.x(), f_prime_pt.y(), 0};
861 Eigen::Vector3d f_prime_prime = {f_prime_prime_pt.x(), f_prime_prime_pt.y(), 0};
862 return (f_prime.cross(f_prime_prime)).norm() / (pow(f_prime.norm(), 3));
863 }
864
865 std::vector<carma_planning_msgs::msg::TrajectoryPlanPoint> compose_lanefollow_trajectory_from_path(
866 const std::vector<PointSpeedPair> &points, const carma_planning_msgs::msg::VehicleState &state, const rclcpp::Time &state_time, const carma_wm::WorldModelConstPtr &wm,
867 const carma_planning_msgs::msg::VehicleState &ending_state_before_buffer, carma_debug_ros2_msgs::msg::TrajectoryCurvatureSpeeds& debug_msg, const DetailedTrajConfig &detailed_config)
868 {
869 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "VehicleState: "
870 << " x: " << state.x_pos_global << " y: " << state.y_pos_global << " yaw: " << state.orientation
871 << " speed: " << state.longitudinal_vel);
872
874
875 int nearest_pt_index = get_nearest_point_index(points, state);
876
877 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "NearestPtIndex: " << nearest_pt_index);
878
879 std::vector<PointSpeedPair> future_points(points.begin() + nearest_pt_index + 1, points.end()); // Points in front of current vehicle position
880
881 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Ready to call constrain_to_time_boundary: future_points size = " << future_points.size() << ", trajectory_time_length = " << detailed_config.trajectory_time_length);
882
883 auto time_bound_points = constrain_to_time_boundary(future_points, detailed_config.trajectory_time_length);
884
885 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Got time_bound_points with size:" << time_bound_points.size());
887
888 std::vector<PointSpeedPair> back_and_future = attach_past_points(points, time_bound_points, nearest_pt_index, detailed_config.back_distance);
889
890 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Got back_and_future points with size" << back_and_future.size());
892
893 std::vector<double> speed_limits;
894 std::vector<lanelet::BasicPoint2d> curve_points;
895 split_point_speed_pairs(back_and_future, &curve_points, &speed_limits);
896
897 std::unique_ptr<smoothing::SplineI> fit_curve = compute_fit(curve_points); // Compute splines based on curve points
898 if (!fit_curve)
899 {
900 throw std::invalid_argument("Could not fit a spline curve along the given trajectory!");
901 }
902
903 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Got fit");
904
905 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "speed_limits.size() " << speed_limits.size());
906
907 std::vector<lanelet::BasicPoint2d> all_sampling_points;
908 all_sampling_points.reserve(1 + curve_points.size() * 2);
909
910 std::vector<double> distributed_speed_limits;
911 distributed_speed_limits.reserve(1 + curve_points.size() * 2);
912
913 // compute total length of the trajectory to get correct number of points
914 // we expect using curve_resample_step_size
915 std::vector<double> downtracks_raw = carma_wm::geometry::compute_arc_lengths(curve_points);
916
917 auto total_step_along_curve = static_cast<int>(downtracks_raw.back() / detailed_config.curve_resample_step_size);
918
919 int current_speed_index = 0;
920 size_t total_point_size = curve_points.size();
921
922 double step_threshold_for_next_speed = (double)total_step_along_curve / (double)total_point_size;
923 double scaled_steps_along_curve = 0.0; // from 0 (start) to 1 (end) for the whole trajectory
924 std::vector<double> better_curvature;
925 better_curvature.reserve(1 + curve_points.size() * 2);
926
927 for (int steps_along_curve = 0; steps_along_curve < total_step_along_curve; steps_along_curve++) // Resample curve at tighter resolution
928 {
929 lanelet::BasicPoint2d p = (*fit_curve)(scaled_steps_along_curve);
930
931 all_sampling_points.push_back(p);
932 double c = compute_curvature_at((*fit_curve), scaled_steps_along_curve);
933 better_curvature.push_back(c);
934 if ((double)steps_along_curve > step_threshold_for_next_speed)
935 {
936 step_threshold_for_next_speed += (double)total_step_along_curve / (double)total_point_size;
937 current_speed_index++;
938 }
939 distributed_speed_limits.push_back(speed_limits[current_speed_index]); // Identify speed limits for resampled points
940 scaled_steps_along_curve += 1.0 / total_step_along_curve; //adding steps_along_curve_step_size
941 }
942
943 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Got sampled points with size:" << all_sampling_points.size());
944 log::printDebugPerLine(all_sampling_points, &log::basicPointToStream);
945
946 std::vector<double> final_yaw_values = carma_wm::geometry::compute_tangent_orientations(all_sampling_points);
947
948 log::printDoublesPerLineWithPrefix("raw_curvatures[i]: ", better_curvature);
949
950 std::vector<double> curvatures = smoothing::moving_average_filter(better_curvature, detailed_config.curvature_moving_average_window_size, false);
951 std::vector<double> ideal_speeds =
952 trajectory_utils::constrained_speeds_for_curvatures(curvatures, detailed_config.lateral_accel_limit);
953
954 log::printDoublesPerLineWithPrefix("curvatures[i]: ", curvatures);
955 log::printDoublesPerLineWithPrefix("ideal_speeds: ", ideal_speeds);
956 log::printDoublesPerLineWithPrefix("final_yaw_values[i]: ", final_yaw_values);
957
958 std::vector<double> constrained_speed_limits = apply_speed_limits(ideal_speeds, distributed_speed_limits);
959
960 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Processed all points in computed fit");
961
962 if (all_sampling_points.empty())
963 {
964 RCLCPP_WARN_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "No trajectory points could be generated");
965 return {};
966 }
967
968 // Add current vehicle point to front of the trajectory
969
970 nearest_pt_index = get_nearest_index_by_downtrack(all_sampling_points, wm, state);
971 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Current state's nearest_pt_index: " << nearest_pt_index);
972 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Curvature right now: " << better_curvature[nearest_pt_index] << ", at state x: " << state.x_pos_global << ", state y: " << state.y_pos_global);
973 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Corresponding to point: x: " << all_sampling_points[nearest_pt_index].x() << ", y:" << all_sampling_points[nearest_pt_index].y());
974
975 int buffer_pt_index = get_nearest_index_by_downtrack(all_sampling_points, wm, ending_state_before_buffer);
976 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Ending state's index before applying buffer (buffer_pt_index): " << buffer_pt_index);
977 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Corresponding to point: x: " << all_sampling_points[buffer_pt_index].x() << ", y:" << all_sampling_points[buffer_pt_index].y());
978
979 if(nearest_pt_index + 1 >= buffer_pt_index){
980
981 lanelet::BasicPoint2d current_pos(state.x_pos_global, state.y_pos_global);
982 lanelet::BasicPoint2d ending_pos(ending_state_before_buffer.x_pos_global, ending_state_before_buffer.y_pos_global);
983
984 if(wm->routeTrackPos(ending_pos).downtrack < wm->routeTrackPos(current_pos).downtrack ){
985
986 RCLCPP_WARN_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Current state is at or past the planned end distance. Couldn't generate trajectory");
987 return {};
988 }
989 else{
990 //Current point is behind the ending state of maneuver and a valid trajectory is possible
991 RCLCPP_WARN_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Returning the two remaining points in the maneuver");
992
993 std::vector<lanelet::BasicPoint2d> remaining_traj_points = {current_pos, ending_pos};
994
995 std::vector<double> downtracks = carma_wm::geometry::compute_arc_lengths(remaining_traj_points);
996 std::vector<double> speeds = {state.longitudinal_vel, state.longitudinal_vel};//Keep current speed
997 std::vector<double> times;
998 trajectory_utils::conversions::speed_to_time(downtracks, speeds, &times);
999 std::vector<double> yaw = {state.orientation, state.orientation}; //Keep current orientation
1000
1001 std::vector<carma_planning_msgs::msg::TrajectoryPlanPoint> traj_points =
1002 trajectory_from_points_times_orientations(remaining_traj_points, times, yaw, state_time, detailed_config.desired_controller_plugin);
1003
1004 return traj_points;
1005
1006 }
1007 }
1008
1009 //drop buffer points here
1010
1011 std::vector<lanelet::BasicPoint2d> future_basic_points(all_sampling_points.begin() + nearest_pt_index + 1,
1012 all_sampling_points.begin()+ buffer_pt_index); // Points in front of current vehicle position
1013
1014 std::vector<double> future_speeds(constrained_speed_limits.begin() + nearest_pt_index + 1,
1015 constrained_speed_limits.begin() + buffer_pt_index); // Points in front of current vehicle position
1016 std::vector<double> future_yaw(final_yaw_values.begin() + nearest_pt_index + 1,
1017 final_yaw_values.begin() + buffer_pt_index); // Points in front of current vehicle position
1018 std::vector<double> final_actual_speeds = future_speeds;
1019 all_sampling_points = future_basic_points;
1020 final_yaw_values = future_yaw;
1021 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Trimmed future points to size: "<< future_basic_points.size());
1022
1023 lanelet::BasicPoint2d cur_veh_point(state.x_pos_global, state.y_pos_global);
1024
1025 all_sampling_points.insert(all_sampling_points.begin(),
1026 cur_veh_point); // Add current vehicle position to front of sample points
1027
1028 final_actual_speeds.insert(final_actual_speeds.begin(), state.longitudinal_vel);
1029
1030 final_yaw_values.insert(final_yaw_values.begin(), state.orientation);
1031
1032 // Compute points to local downtracks
1033 std::vector<double> downtracks = carma_wm::geometry::compute_arc_lengths(all_sampling_points);
1034
1035 // Apply accel limits
1036 final_actual_speeds = optimize_speed(downtracks, final_actual_speeds, detailed_config.max_accel);
1037
1038 log::printDoublesPerLineWithPrefix("postAccel[i]: ", final_actual_speeds);
1039
1040 final_actual_speeds = smoothing::moving_average_filter(final_actual_speeds, detailed_config.speed_moving_average_window_size);
1041
1042 log::printDoublesPerLineWithPrefix("post_average[i]: ", final_actual_speeds);
1043
1044 for (auto &s : final_actual_speeds) // Limit minimum speed. TODO how to handle stopping?
1045 {
1046 s = std::max(s, detailed_config.minimum_speed);
1047 }
1048
1049 log::printDoublesPerLineWithPrefix("post_min_speed[i]: ", final_actual_speeds);
1050
1051 // Convert speeds to times
1052 std::vector<double> times;
1053 trajectory_utils::conversions::speed_to_time(downtracks, final_actual_speeds, &times);
1054
1055 log::printDoublesPerLineWithPrefix("times[i]: ", times);
1056
1057 // Build trajectory points
1058 std::vector<carma_planning_msgs::msg::TrajectoryPlanPoint> traj_points =
1059 trajectory_from_points_times_orientations(all_sampling_points, times, final_yaw_values, state_time, detailed_config.desired_controller_plugin);
1060
1061 //debug msg
1062 carma_debug_ros2_msgs::msg::TrajectoryCurvatureSpeeds msg;
1063 msg.velocity_profile = final_actual_speeds;
1064 msg.relative_downtrack = downtracks;
1065 msg.tangent_headings = final_yaw_values;
1066 std::vector<double> aligned_speed_limits(constrained_speed_limits.begin() + nearest_pt_index,
1067 constrained_speed_limits.end());
1068
1069 msg.speed_limits = aligned_speed_limits;
1070 std::vector<double> aligned_curvatures(curvatures.begin() + nearest_pt_index,
1071 curvatures.end());
1072 msg.curvatures = aligned_curvatures;
1073 msg.lat_accel_limit = detailed_config.lateral_accel_limit;
1074 msg.lon_accel_limit = detailed_config.max_accel;
1075 msg.starting_state = state;
1076 debug_msg = msg;
1077
1078
1079 return traj_points;
1080 }
1081
1083 double curve_resample_step_size,
1084 double minimum_speed,
1085 double max_accel,
1086 double lateral_accel_limit,
1087 int speed_moving_average_window_size,
1088 int curvature_moving_average_window_size,
1089 double back_distance,
1090 double buffer_ending_downtrack,
1091 std::string desired_controller_plugin)
1092 {
1093 DetailedTrajConfig detailed_config;
1094
1095 detailed_config.trajectory_time_length = trajectory_time_length;
1096 detailed_config.curve_resample_step_size = curve_resample_step_size;
1097 detailed_config.minimum_speed = minimum_speed;
1098 detailed_config.max_accel = max_accel;
1099 detailed_config.lateral_accel_limit = lateral_accel_limit;
1100 detailed_config.speed_moving_average_window_size = speed_moving_average_window_size;
1101 detailed_config.curvature_moving_average_window_size = curvature_moving_average_window_size;
1102 detailed_config.back_distance = back_distance;
1103 detailed_config.buffer_ending_downtrack = buffer_ending_downtrack;
1104 detailed_config.desired_controller_plugin = desired_controller_plugin;
1105
1106 return detailed_config;
1107 }
1108
1109 GeneralTrajConfig compose_general_trajectory_config(const std::string& trajectory_type,
1110 int default_downsample_ratio,
1111 int turn_downsample_ratio)
1112 {
1113 GeneralTrajConfig general_config;
1114
1115 general_config.trajectory_type = trajectory_type;
1116 general_config.default_downsample_ratio = default_downsample_ratio;
1117 general_config.turn_downsample_ratio = turn_downsample_ratio;
1118
1119
1120 return general_config;
1121 }
1122
1123
1124 std::vector<carma_planning_msgs::msg::TrajectoryPlanPoint> compose_lanechange_trajectory_from_path(
1125 const std::vector<PointSpeedPair> &points, const carma_planning_msgs::msg::VehicleState &state, const rclcpp::Time &state_time,
1126 const carma_wm::WorldModelConstPtr &wm, const carma_planning_msgs::msg::VehicleState &ending_state_before_buffer, const DetailedTrajConfig &detailed_config)
1127 {
1128 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Input points size in compose traj from centerline: "<< points.size());
1129 int nearest_pt_index = get_nearest_index_by_downtrack(points, wm, state);
1130 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "nearest_pt_index: "<< nearest_pt_index);
1131
1132 std::vector<PointSpeedPair> future_points(points.begin() + nearest_pt_index + 1, points.end());
1133 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "future_points size: "<< future_points.size());
1134
1135 //Compute yaw values from original trajectory.
1136 std::vector<lanelet::BasicPoint2d> future_geom_points;
1137 std::vector<double> final_actual_speeds;
1138 split_point_speed_pairs(future_points, &future_geom_points, &final_actual_speeds);
1139
1140 std::unique_ptr<smoothing::SplineI> fit_curve = compute_fit(future_geom_points);
1141 if(!fit_curve){
1142 throw std::invalid_argument("Could not fit a spline curve along the given trajectory!");
1143 }
1144 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Got fit");
1145 std::vector<lanelet::BasicPoint2d> all_sampling_points;
1146 all_sampling_points.reserve(1 + future_geom_points.size() * 2);
1147
1148 lanelet::BasicPoint2d current_vehicle_point(state.x_pos_global, state.y_pos_global);
1149
1150 future_geom_points.insert(future_geom_points.begin(),
1151 current_vehicle_point); // Add current vehicle position to front of future geometry points
1152
1153 final_actual_speeds.insert(final_actual_speeds.begin(), state.longitudinal_vel);
1154
1155 //Compute points to local downtracks
1156 std::vector<double> downtracks = carma_wm::geometry::compute_arc_lengths(future_geom_points);
1157
1158 auto total_step_along_curve = static_cast<int>(downtracks.back() /detailed_config.curve_resample_step_size);
1159
1160 double scaled_steps_along_curve = 0.0; //from 0 (start) to 1 (end) for the whole trajectory
1161
1162 for(int steps_along_curve = 0; steps_along_curve < total_step_along_curve; steps_along_curve++){
1163 lanelet::BasicPoint2d p = (*fit_curve)(scaled_steps_along_curve);
1164
1165 all_sampling_points.push_back(p);
1166
1167 scaled_steps_along_curve += 1.0 / total_step_along_curve;
1168 }
1169 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Got sampled points with size:" << all_sampling_points.size());
1170
1171 std::vector<double> final_yaw_values = carma_wm::geometry::compute_tangent_orientations(future_geom_points);
1172 if(final_yaw_values.size() > 0) {
1173 final_yaw_values[0] = state.orientation; // Set the initial yaw value based on the initial state
1174 }
1175
1176 final_actual_speeds = smoothing::moving_average_filter(final_actual_speeds, detailed_config.speed_moving_average_window_size);
1177
1178 //Convert speeds to time
1179 std::vector<double> times;
1180 trajectory_utils::conversions::speed_to_time(downtracks, final_actual_speeds, &times);
1181
1182 //Remove extra points
1183 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Before removing extra buffer points, future_geom_points.size()"<< future_geom_points.size());
1184 int end_dist_pt_index = get_nearest_index_by_downtrack(future_geom_points, wm, ending_state_before_buffer);
1185 future_geom_points.resize(end_dist_pt_index + 1);
1186 times.resize(end_dist_pt_index + 1);
1187 final_yaw_values.resize(end_dist_pt_index + 1);
1188 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "After removing extra buffer points, future_geom_points.size():"<< future_geom_points.size());
1189
1190 std::vector<carma_planning_msgs::msg::TrajectoryPlanPoint> traj_points =
1191 trajectory_from_points_times_orientations(future_geom_points, times, final_yaw_values, state_time, detailed_config.desired_controller_plugin);
1192
1193 return traj_points;
1194 }
1195
1196 autoware_auto_msgs::msg::Trajectory process_trajectory_plan(const carma_planning_msgs::msg::TrajectoryPlan& tp, double vehicle_response_lag )
1197 {
1198 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Processing latest TrajectoryPlan message");
1199
1200 std::vector<double> times;
1201 std::vector<double> downtracks;
1202
1203 std::vector<carma_planning_msgs::msg::TrajectoryPlanPoint> trajectory_points = tp.trajectory_points;
1204
1205 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Original Trajectory size:"<<trajectory_points.size());
1206
1207
1208 trajectory_utils::conversions::trajectory_to_downtrack_time(trajectory_points, &downtracks, &times);
1209
1210 //detect stopping case
1211 size_t stopping_index = 0;
1212 for (size_t i = 1; i < times.size(); i++)
1213 {
1214 if (times[i] == times[i - 1]) //if exactly same, it is stopping case
1215 {
1216 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Detected a stopping case where times is exactly equal: " << times[i-1]);
1217 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "And index of that is: " << i << ", where size is: " << times.size());
1218 stopping_index = i;
1219 break;
1220 }
1221 }
1222
1223 std::vector<double> speeds;
1224 try
1225 {
1226 trajectory_utils::conversions::time_to_speed(downtracks, times, tp.initial_longitudinal_velocity, &speeds);
1227 }
1228 catch(const std::runtime_error& error)
1229 {
1230 // This can only happen if there was negative speed in trajectory generation which usually happens when intending to stop.
1231 // The plugin is catching that error and logging it for the user to correct the origin plugin's logic, but continues
1232 // the operation by forcing the negative values to be 0, which is the intention usually.
1233 RCLCPP_WARN_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Detected a negative speed from <point,time> to <point,speed> trajectory conversion with error: "
1234 << error.what() << ". Replacing the negative speed with 0.0 speed, but please revisit the trajectory logic. "
1235 "Responsible plugin is: " << trajectory_points[std::find(speeds.begin(), speeds.end(), 0.0) - speeds.begin()].planner_plugin_name);
1236 }
1237
1238 if (speeds.size() != trajectory_points.size())
1239 {
1240 throw std::invalid_argument("Speeds and trajectory points sizes do not match");
1241 }
1242
1243 for (size_t i = 0; i < speeds.size(); i++) { // Ensure 0 is min speed
1244 if (stopping_index != 0 && i >= stopping_index - 1)
1245 {
1246 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Made it to 0, i: " << i);
1247
1248 speeds[i] = 0.0; //stopping case
1249 }
1250 else
1251 {
1252 speeds[i] = std::max(0.0, speeds[i]);
1253 }
1254 }
1255
1256 std::vector<double> lag_speeds;
1257 lag_speeds = apply_response_lag(speeds, downtracks, vehicle_response_lag); // This call requires that the first speed point be current speed to work as expected
1258
1259 autoware_auto_msgs::msg::Trajectory autoware_trajectory;
1260 autoware_trajectory.header = tp.header;
1261 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "size: " << trajectory_points.size());
1262
1263 auto max_size = std::min(99, (int)trajectory_points.size()); //NOTE: more than this size autoware auto raises exception with "Exceeded upper bound while in ACTIVE state."
1264 //large portion of the points are not needed anyways
1265 for (int i = 0; i < max_size; i++)
1266 {
1267 autoware_auto_msgs::msg::TrajectoryPoint autoware_point;
1268
1269 autoware_point.x = trajectory_points[i].x;
1270 autoware_point.y = trajectory_points[i].y;
1271 autoware_point.longitudinal_velocity_mps = lag_speeds[i];
1272 double yaw = 0.0;
1273 if (i< max_size-1)
1274 {
1275 yaw = std::atan2(trajectory_points[i+1].y - trajectory_points[i].y, trajectory_points[i+1].x - trajectory_points[i].x);
1276
1277 }
1278 else
1279 {
1280 yaw = std::atan2(trajectory_points[max_size-1].y - trajectory_points[max_size-2].y, trajectory_points[max_size-1].x - trajectory_points[max_size-2].x);
1281 // last point in the trajectory will have yaw value of its previous point to avoid sudden steering in some conditions
1282 }
1283 autoware_point.heading.real = std::cos(yaw/2);
1284 autoware_point.heading.imag = std::sin(yaw/2);
1285
1286 autoware_point.time_from_start = rclcpp::Duration(times[i] * 1e9);
1287 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(BASIC_AUTONOMY_LOGGER), "Setting waypoint idx: " << i <<", with planner: << " << trajectory_points[i].planner_plugin_name << ", x: " << trajectory_points[i].x <<
1288 ", y: " << trajectory_points[i].y <<
1289 ", speed: " << lag_speeds[i]* 2.23694 << "mph");
1290 autoware_trajectory.points.push_back(autoware_point);
1291 }
1292
1293 return autoware_trajectory;
1294 }
1295
1296
1297 std::vector<double> apply_response_lag(const std::vector<double>& speeds, const std::vector<double> downtracks, double response_lag)
1298 { // Note first speed is assumed to be vehicle speed
1299 if (speeds.size() != downtracks.size()) {
1300 throw std::invalid_argument("Speed list and downtrack list are not the same size.");
1301 }
1302
1303 std::vector<double> output;
1304 if (speeds.empty()) {
1305 return output;
1306 }
1307
1308 double lookahead_distance = speeds[0] * response_lag;
1309
1310 double downtrack_cutoff = downtracks[0] + lookahead_distance;
1311 size_t lookahead_count = std::lower_bound(downtracks.begin(),downtracks.end(), downtrack_cutoff) - downtracks.begin(); // Use binary search to find lower bound cutoff point
1312 output = trajectory_utils::shift_by_lookahead(speeds, (unsigned int) lookahead_count);
1313 return output;
1314 }
1315
1316 bool is_valid_yield_plan(const std::shared_ptr<carma_ros2_utils::CarmaLifecycleNode>& node_handler, const carma_planning_msgs::msg::TrajectoryPlan& yield_plan)
1317 {
1318 if (yield_plan.trajectory_points.size() < 2)
1319 {
1320 RCLCPP_WARN(node_handler->get_logger(), "Invalid Yield Trajectory");
1321 }
1322
1323 RCLCPP_DEBUG_STREAM(node_handler->get_logger(), "Yield Trajectory Time" << rclcpp::Time(yield_plan.trajectory_points[0].target_time).seconds());
1324 RCLCPP_DEBUG_STREAM(node_handler->get_logger(), "Now:" << node_handler->now().seconds());
1325
1326 if (rclcpp::Time(yield_plan.trajectory_points[0].target_time) + rclcpp::Duration(5.0, 0) > node_handler->now())
1327 {
1328 return true;
1329 }
1330 else
1331 {
1332 RCLCPP_DEBUG(node_handler->get_logger(), "Old Yield Trajectory");
1333 }
1334
1335 return false;
1336 }
1337
1338 carma_planning_msgs::srv::PlanTrajectory::Response::SharedPtr modify_trajectory_to_yield_to_obstacles(
1339 const std::shared_ptr<carma_ros2_utils::CarmaLifecycleNode>& node_handler,
1340 const carma_planning_msgs::srv::PlanTrajectory::Request::SharedPtr& req,
1341 const carma_planning_msgs::srv::PlanTrajectory::Response::SharedPtr& resp,
1342 const carma_ros2_utils::ClientPtr<carma_planning_msgs::srv::PlanTrajectory>& yield_client,
1343 int yield_plugin_service_call_timeout)
1344 {
1345 RCLCPP_DEBUG(node_handler->get_logger(), "Activate Object Avoidance");
1346
1347 if (!yield_client || !yield_client->service_is_ready())
1348 {
1349 throw std::runtime_error("Yield Client is not set or unavailable after configuration state of lifecycle");
1350 }
1351
1352 RCLCPP_DEBUG(node_handler->get_logger(), "Yield Client is valid");
1353
1354 auto yield_srv = std::make_shared<carma_planning_msgs::srv::PlanTrajectory::Request>();
1355 yield_srv->initial_trajectory_plan = resp->trajectory_plan;
1356 yield_srv->vehicle_state = req->vehicle_state;
1357
1358 auto yield_resp = yield_client->async_send_request(yield_srv);
1359
1360 auto future_status = yield_resp.wait_for(std::chrono::milliseconds(yield_plugin_service_call_timeout));
1361
1362 if (future_status != std::future_status::ready)
1363 {
1364 // Sometimes the yield plugin's service call may be unsuccessful due to its computationally expensive logic.
1365 // However, consecutive calls can be successful, so return original trajectory for now
1366 RCLCPP_WARN(node_handler->get_logger(), "Service request to yield plugin timed out waiting on a reply from the service server");
1367 return resp;
1368 }
1369
1370 RCLCPP_DEBUG(node_handler->get_logger(), "Received Traj from Yield");
1371 carma_planning_msgs::msg::TrajectoryPlan yield_plan = yield_resp.get()->trajectory_plan;
1372 if (is_valid_yield_plan(node_handler, yield_plan))
1373 {
1374 RCLCPP_DEBUG(node_handler->get_logger(), "Yield trajectory validated");
1375 resp->trajectory_plan = yield_plan;
1376 }
1377 else
1378 {
1379 throw std::invalid_argument("Invalid Yield Trajectory");
1380 }
1381
1382 return resp;
1383 }
1384
1385 } // namespace waypoint_generation
1386
1387} // basic_autonomy
#define GET_MANEUVER_PROPERTY(mvr, property)
Macro definition to enable easier access to fields shared across the maneuver types.
Interface to a spline interpolator that can be used to smoothly interpolate between points.
Definition: SplineI.hpp:30
virtual lanelet::BasicPoint2d first_deriv(double x) const =0
Get the BasicPoint2d representing the first_deriv along the curve at t-th step.
virtual lanelet::BasicPoint2d second_deriv(double x) const =0
Get the BasicPoint2d representing the first_deriv along the curve at t-th step.
void printDoublesPerLineWithPrefix(const std::string &prefix, const std::vector< double > &values)
Print a RCLCPP_DEBUG_STREAM for each value in values where the printed value is << prefix << value.
Definition: log.cpp:45
std::string basicPointToStream(lanelet::BasicPoint2d point)
Helper function to convert a lanelet::BasicPoint2d to a string.
Definition: log.cpp:26
std::string pointSpeedPairToStream(waypoint_generation::PointSpeedPair point)
Helper function to convert a PointSpeedPair to a string.
Definition: log.cpp:35
void printDebugPerLine(const std::vector< T > &values, std::function< std::string(T)> func)
Print a RCLCPP_DEBUG_STREAM for each value in values where the printed value is a string returned by ...
Definition: log.hpp:40
std::vector< double > moving_average_filter(const std::vector< double > input, int window_size, bool ignore_first_point=true)
Extremely simplie moving average filter.
Definition: filters.cpp:24
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.
GeneralTrajConfig compose_general_trajectory_config(const std::string &trajectory_type, int default_downsample_ratio, int turn_downsample_ratio)
std::vector< std::vector< lanelet::BasicPoint2d > > resample_linestring_pair_to_same_size(std::vector< lanelet::BasicPoint2d > &line_1, std::vector< lanelet::BasicPoint2d > &line_2)
Resamples a pair of basicpoint2d lines to get lines of same number of points.
std::vector< lanelet::BasicPoint2d > create_lanechange_geometry(lanelet::Id starting_lane_id, lanelet::Id ending_lane_id, double starting_downtrack, double ending_downtrack, const carma_wm::WorldModelConstPtr &wm, int downsample_ratio, double buffer_ending_downtrack)
Creates a vector of lane change points using parameters defined.
carma_planning_msgs::srv::PlanTrajectory::Response::SharedPtr modify_trajectory_to_yield_to_obstacles(const std::shared_ptr< carma_ros2_utils::CarmaLifecycleNode > &node_handler, const carma_planning_msgs::srv::PlanTrajectory::Request::SharedPtr &req, const carma_planning_msgs::srv::PlanTrajectory::Response::SharedPtr &resp, const carma_ros2_utils::ClientPtr< carma_planning_msgs::srv::PlanTrajectory > &yield_client, int yield_plugin_service_call_timeout)
Applies a yield trajectory to the original trajectory set in response.
std::vector< double > apply_response_lag(const std::vector< double > &speeds, const std::vector< double > downtracks, double response_lag)
Applies a specified response lag in seconds to the trajectory shifting the whole thing by the specifi...
static const std::string BASIC_AUTONOMY_LOGGER
std::vector< carma_planning_msgs::msg::TrajectoryPlanPoint > compose_lanechange_trajectory_from_path(const std::vector< PointSpeedPair > &points, const carma_planning_msgs::msg::VehicleState &state, const rclcpp::Time &state_time, const carma_wm::WorldModelConstPtr &wm, const carma_planning_msgs::msg::VehicleState &ending_state_before_buffer, const DetailedTrajConfig &detailed_config)
Method converts a list of lanelet centerline points and current vehicle state into a usable list of t...
std::vector< double > optimize_speed(const std::vector< double > &downtracks, const std::vector< double > &curv_speeds, double accel_limit)
Applies the longitudinal acceleration limit to each point's speed.
DetailedTrajConfig compose_detailed_trajectory_config(double trajectory_time_length, double curve_resample_step_size, double minimum_speed, double max_accel, double lateral_accel_limit, int speed_moving_average_window_size, int curvature_moving_average_window_size, double back_distance, double buffer_ending_downtrack, std::string desired_controller_plugin="default")
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...
bool is_valid_yield_plan(const std::shared_ptr< carma_ros2_utils::CarmaLifecycleNode > &node_handler, const carma_planning_msgs::msg::TrajectoryPlan &yield_plan)
Helper function to verify if the input yield trajectory plan is valid.
std::pair< double, size_t > min_with_exclusions(const std::vector< double > &values, const std::unordered_set< size_t > &excluded)
Returns the min, and its idx, from the vector of values, excluding given set of values.
std::unique_ptr< basic_autonomy::smoothing::SplineI > compute_fit(const std::vector< lanelet::BasicPoint2d > &basic_points)
Computes a spline based on the provided points.
std::vector< PointSpeedPair > create_lanefollow_geometry(const carma_planning_msgs::msg::Maneuver &maneuver, double max_starting_downtrack, const carma_wm::WorldModelConstPtr &wm, const GeneralTrajConfig &general_config, const DetailedTrajConfig &detailed_config, std::unordered_set< lanelet::Id > &visited_lanelets)
Converts a set of requested LANE_FOLLOWING maneuvers to point speed limit pairs.
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 ...
std::vector< double > apply_speed_limits(const std::vector< double > speeds, const std::vector< double > speed_limits)
Applies the provided speed limits to the provided speeds such that each element is capped at its corr...
std::vector< carma_planning_msgs::msg::TrajectoryPlanPoint > trajectory_from_points_times_orientations(const std::vector< lanelet::BasicPoint2d > &points, const std::vector< double > &times, const std::vector< double > &yaws, rclcpp::Time startTime, const std::string &desired_controller_plugin)
Method combines input points, times, orientations, and an absolute start time to form a valid carma p...
std::vector< PointSpeedPair > create_geometry_profile(const std::vector< carma_planning_msgs::msg::Maneuver > &maneuvers, double max_starting_downtrack, const carma_wm::WorldModelConstPtr &wm, carma_planning_msgs::msg::VehicleState &ending_state_before_buffer, const carma_planning_msgs::msg::VehicleState &state, const GeneralTrajConfig &general_config, const DetailedTrajConfig &detailed_config)
Creates geometry profile to return a point speed pair struct for LANE FOLLOW and LANE CHANGE maneuver...
std::vector< PointSpeedPair > add_lanefollow_buffer(const carma_wm::WorldModelConstPtr &wm, std::vector< PointSpeedPair > &points_and_target_speeds, const std::vector< carma_planning_msgs::msg::Maneuver > &maneuvers, carma_planning_msgs::msg::VehicleState &ending_state_before_buffer, const DetailedTrajConfig &detailed_config)
Adds extra centerline points beyond required message length to lane follow maneuver points so that th...
std::vector< carma_planning_msgs::msg::TrajectoryPlanPoint > compose_lanefollow_trajectory_from_path(const std::vector< PointSpeedPair > &points, const carma_planning_msgs::msg::VehicleState &state, const rclcpp::Time &state_time, const carma_wm::WorldModelConstPtr &wm, const carma_planning_msgs::msg::VehicleState &ending_state_before_buffer, carma_debug_ros2_msgs::msg::TrajectoryCurvatureSpeeds &debug_msg, const DetailedTrajConfig &detailed_config)
Method converts a list of lanelet centerline points and current vehicle state into a usable list of t...
Eigen::Isometry2d compute_heading_frame(const lanelet::BasicPoint2d &p1, const lanelet::BasicPoint2d &p2)
Returns a 2D coordinate frame which is located at p1 and oriented so p2 lies on the +X axis.
double compute_curvature_at(const basic_autonomy::smoothing::SplineI &fit_curve, double step_along_the_curve)
Given the curvature fit, computes the curvature at the given step along the curve.
autoware_auto_msgs::msg::Trajectory process_trajectory_plan(const carma_planning_msgs::msg::TrajectoryPlan &tp, double vehicle_response_lag)
Given a carma type of trajectory_plan, generate autoware type of trajectory accounting for speed_lag ...
std::vector< PointSpeedPair > constrain_to_time_boundary(const std::vector< PointSpeedPair > &points, double time_span)
Reduces the input points to only those points that fit within the provided time boundary.
std::vector< PointSpeedPair > get_lanechange_points_from_maneuver(const carma_planning_msgs::msg::Maneuver &maneuver, double max_starting_downtrack, const carma_wm::WorldModelConstPtr &wm, carma_planning_msgs::msg::VehicleState &ending_state_before_buffer, const carma_planning_msgs::msg::VehicleState &state, const GeneralTrajConfig &general_config, const DetailedTrajConfig &detailed_config)
Converts a set of requested LANE_CHANGE maneuvers to point speed limit pairs.
std::vector< PointSpeedPair > attach_past_points(const std::vector< PointSpeedPair > &points_set, std::vector< PointSpeedPair > future_points, const int nearest_pt_index, double back_distance)
Attaches back_distance length of points behind the future points.
auto to_string(const UtmZone &zone) -> std::string
Definition: utm_zone.cpp:21
Eigen::Isometry2d build2dEigenTransform(const Eigen::Vector2d &position, const Eigen::Rotation2Dd &rotation)
Builds a 2D Eigen coordinate frame transform with not applied scaling (only translation and rotation)...
Definition: Geometry.cpp:570
std::vector< double > compute_tangent_orientations(const lanelet::BasicLineString2d &centerline)
Compute an approximate orientation for the vehicle at each point along the provided centerline.
Definition: Geometry.cpp:559
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
lanelet::BasicLineString2d concatenate_line_strings(const lanelet::BasicLineString2d &l1, const lanelet::BasicLineString2d &l2)
Helper function to concatenate 2 linestrings together and return the result. Neither LineString is mo...
Definition: Geometry.cpp:373
std::shared_ptr< const WorldModel > WorldModelConstPtr
Definition: WorldModel.hpp:452