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.
light_controlled_intersection_tactical_plugin.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 */
17#include <valarray>
19
21{
22
23
26 const Config& config,
27 const DebugPublisher& debug_publisher,
28 const std::string& plugin_name,
29 std::shared_ptr<carma_ros2_utils::CarmaLifecycleNode> nh)
30 :wm_(wm), config_(config), nh_(nh), plugin_name_(plugin_name),
31 debug_publisher_(debug_publisher)
32 {
33 basic_autonomy::set_logger(nh_->get_logger().get_child("basic_autonomy"));
34 }
35
37 const rclcpp::Time& current_time,
38 double min_remaining_time_seconds) const
39 {
40 // Check if we have at least 2 points in the trajectory
41 if (last_trajectory_time_unbound_.trajectory_points.size() < 2)
42 {
43 return false;
44 }
45
46 // Check if the last point's time is sufficiently in the future
47 auto last_point_time = rclcpp::Time(last_trajectory_time_unbound_.trajectory_points.back().target_time);
48
49
50 if (rclcpp::Duration min_time_remaining =
51 rclcpp::Duration::from_seconds(min_remaining_time_seconds);
52 last_point_time <= current_time + min_time_remaining)
53 {
54 return false;
55 }
56
57 // Check if we have case information from previous planning
58 if (is_last_case_successful_ == boost::none || last_case_ == boost::none)
59 {
60 return false;
61 }
62
63 // Ensure we have consistent speed data
64 if (last_speeds_time_unbound_.size() != last_trajectory_time_unbound_.trajectory_points.size())
65 {
66 return false;
67 }
68
69 return true;
70 }
71
73 TSCase new_case, bool is_new_case_successful, const rclcpp::Time& current_time)
74 {
75 // Validate trajectory with 1 second minimum remaining time or vehicle_response_lag seconds
76 // This is because the vehicle executes speed command of vehicle_response_lag secs ahead
77 // Therefore, the trajectory should be enough to cover the vehicle_response_lag
78 if (!isLastTrajectoryValid(current_time, std::max(config_.vehicle_response_lag, 1.0)))
79 {
80 return false;
81 }
82
83 // New case is successful and is same as the last case
84 if (last_case_.get() == new_case && is_new_case_successful == true)
85 {
86 return true;
87 }
88
89 // Edge case - TS Alorithm successful to unsuccessful transition (Case (1-7) to 8)
90 // near the intersection. The vehicle should "lock in" to the last trajectory as it is
91 // expected for the TS algorithm to be unsuccessful near the intersection.
92 if (is_last_case_successful_.get() == true &&
93 is_new_case_successful == false &&
96 last_successful_scheduled_entry_time_ - current_time.seconds() <
98 {
99 return true;
100 }
101
102 // Returning false here means that it should use the new trajectory because:
103 // New case is not same as last case and not within the "lock in" distance
104 return false;
105 }
106
107 carma_planning_msgs::msg::TrajectoryPlan LightControlledIntersectionTacticalPlugin::
109 const std::vector<carma_planning_msgs::msg::Maneuver>& maneuver_plan,
110 const carma_planning_msgs::srv::PlanTrajectory::Request::SharedPtr& req,
111 std::vector<double>& final_speeds)
112 {
113 DetailedTrajConfig wpg_detail_config;
114 GeneralTrajConfig wpg_general_config;
115
117 "intersection_transit",
120
122 99.0, // trajectory time length in duration (seconds) is arbitrarily selected
123 // high to generate all at once
130
131 // Create trajectory with raw speed limits from maneuver
132 auto points_and_target_speeds = createGeometryProfile(
133 maneuver_plan, std::max((double)0, current_downtrack_ - config_.back_distance),
134 wm_, ending_state_before_buffer_, req->vehicle_state,
135 wpg_general_config, wpg_detail_config);
136
137 // Apply optimized speed profile
138 applyOptimizedTargetSpeedProfile(maneuver_plan.front(), req->vehicle_state.longitudinal_vel,
139 points_and_target_speeds);
140
141 // Create new trajectory
142 carma_planning_msgs::msg::TrajectoryPlan new_trajectory;
143 new_trajectory.header.frame_id = "map";
144 new_trajectory.header.stamp = req->header.stamp;
145 new_trajectory.trajectory_id = boost::uuids::to_string(boost::uuids::random_generator()());
146
147 // Generate points
148 new_trajectory.trajectory_points =
150 points_and_target_speeds,
151 req->vehicle_state, req->header.stamp, wm_, ending_state_before_buffer_, debug_msg_,
152 wpg_detail_config);
153
154 // Save final speeds
155 final_speeds = debug_msg_.velocity_profile;
156
157 return new_trajectory;
158 }
159
161 {
162 if (is_last_case_successful_ != boost::none && last_case_ != boost::none)
163 {
164 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(LCI_TACTICAL_LOGGER),
165 "all variables are set!");
166 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(LCI_TACTICAL_LOGGER),
167 "is_last_case_successful_.get(): " << (int)is_last_case_successful_.get());
168 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(LCI_TACTICAL_LOGGER),
169 "evaluation distance: " << last_successful_ending_downtrack_ - current_downtrack_);
170 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(LCI_TACTICAL_LOGGER),
173 }
174 else
175 {
176 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(LCI_TACTICAL_LOGGER),
177 "Not all variables are set...");
178 }
179
180 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(LCI_TACTICAL_LOGGER),
181 "traj points size: " << last_trajectory_time_unbound_.trajectory_points.size() <<
182 ", last_speeds_time_unbound_ size: " << last_speeds_time_unbound_.size());
183 }
184
186 carma_planning_msgs::srv::PlanTrajectory::Request::SharedPtr req,
187 carma_planning_msgs::srv::PlanTrajectory::Response::SharedPtr resp)
188 {
189 // Validate request
190 if(req->maneuver_index_to_plan >= req->maneuver_plan.maneuvers.size())
191 {
192 throw std::invalid_argument(
193 "Light Control Intersection Tactical Plugin was asked to plan invalid "
194 "maneuver index: " + std::to_string(req->maneuver_index_to_plan)
195 + " for plan of size: " + std::to_string(req->maneuver_plan.maneuvers.size()));
196 }
197
198 // Extract maneuver plan
199 std::vector<carma_planning_msgs::msg::Maneuver> maneuver_plan;
200 if(req->maneuver_plan.maneuvers[req->maneuver_index_to_plan].type ==
201 carma_planning_msgs::msg::Maneuver::LANE_FOLLOWING
202 && GET_MANEUVER_PROPERTY(req->maneuver_plan.maneuvers[req->maneuver_index_to_plan],
203 parameters.string_valued_meta_data.front()) == light_controlled_intersection_strategy_)
204 {
205 maneuver_plan.push_back(req->maneuver_plan.maneuvers[req->maneuver_index_to_plan]);
206 resp->related_maneuvers.push_back(req->maneuver_index_to_plan);
207 }
208 else
209 {
210 throw std::invalid_argument("Light Control Intersection Tactical Plugin "
211 "was asked to plan unsupported maneuver");
212 }
213
214 // Get vehicle position and update tracking variables
215 lanelet::BasicPoint2d veh_pos(req->vehicle_state.x_pos_global,
216 req->vehicle_state.y_pos_global);
217 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(LCI_TACTICAL_LOGGER),
218 "Planning state x:" << req->vehicle_state.x_pos_global
219 << " , y: " << req->vehicle_state.y_pos_global);
220
221 current_downtrack_ = wm_->routeTrackPos(veh_pos).downtrack;
222 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(LCI_TACTICAL_LOGGER),
223 "Current_downtrack: "<< current_downtrack_);
224
225 // Find current lanelet
226 auto current_lanelets = wm_->getLaneletsFromPoint({req->vehicle_state.x_pos_global,
227 req->vehicle_state.y_pos_global});
228
229 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(LCI_TACTICAL_LOGGER), "size: "
230 << current_lanelets.size());
231
232 lanelet::ConstLanelet current_lanelet;
233
234 if (current_lanelets.empty())
235 {
236 RCLCPP_ERROR_STREAM(rclcpp::get_logger(LCI_TACTICAL_LOGGER),
237 "Given vehicle position is not on the road! Returning...");
238 return;
239 }
240
241 // Get the lanelet that is on the route in case overlapping ones found
242 if (auto llt_on_route_optional = wm_->getFirstLaneletOnShortestPath(current_lanelets);
243 llt_on_route_optional)
244 {
245 current_lanelet = llt_on_route_optional.value();
246 }
247 else
248 {
249 RCLCPP_WARN_STREAM(rclcpp::get_logger(LCI_TACTICAL_LOGGER),
250 "When identifying the corresponding lanelet for requested trajectory plan's state, "
251 << "x: " << req->vehicle_state.x_pos_global
252 << ", y: " << req->vehicle_state.y_pos_global
253 << ", no possible lanelet was found to be on the shortest path."
254 << "Picking arbitrary lanelet: " << current_lanelets[0].id() << ", instead");
255
256 current_lanelet = current_lanelets[0];
257 }
258
259 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(LCI_TACTICAL_LOGGER),
260 "Current_lanelet: " << current_lanelet.id());
261
262 speed_limit_ = findSpeedLimit(current_lanelet, wm_);
263 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(LCI_TACTICAL_LOGGER),
264 "speed_limit_: " << speed_limit_);
265
266 // Get new case parameters
267 bool is_new_case_successful =
268 GET_MANEUVER_PROPERTY(maneuver_plan.front(), parameters.int_valued_meta_data[1]);
269
270 auto new_case =
271 static_cast<TSCase>GET_MANEUVER_PROPERTY(
272 maneuver_plan.front(), parameters.int_valued_meta_data[0]);
273
274 // Log debug info about previous trajectory
276
277 // Find closest point in last trajectory to current vehicle position
278 size_t idx_to_start_new_traj =
280 last_trajectory_time_unbound_.trajectory_points,
281 veh_pos);
282
283 // Update last trajectory to start from closest point (remove passed points)
284 if (!last_trajectory_time_unbound_.trajectory_points.empty()) {
285 last_speeds_time_unbound_ = std::vector<double>(
286 last_speeds_time_unbound_.begin() + idx_to_start_new_traj,
288 last_trajectory_time_unbound_.trajectory_points =
289 std::vector<carma_planning_msgs::msg::TrajectoryPlanPoint>
290 (last_trajectory_time_unbound_.trajectory_points.begin() + idx_to_start_new_traj,
291 last_trajectory_time_unbound_.trajectory_points.end());
292 }
293
294 // Check if we should use the last trajectory completely
295 auto current_time = rclcpp::Time(req->header.stamp);
296
297
298 auto last_trajectory_time_bound =
301 if (shouldUseLastTrajectory(new_case, is_new_case_successful, current_time))
302 {
303 resp->trajectory_plan = last_trajectory_time_unbound_;
304 resp->trajectory_plan.trajectory_points = last_trajectory_time_bound;
305
306 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(LCI_TACTICAL_LOGGER),
307 "USING LAST TRAJ WITH CASE: " << (int)last_case_.get());
308
309 resp->trajectory_plan.initial_longitudinal_velocity = last_speeds_time_unbound_.front();
310
311 // Set the planning plugin field name
312 for (auto& p : resp->trajectory_plan.trajectory_points) {
313 p.planner_plugin_name = plugin_name_;
314 }
315
316 debug_msg_.trajectory_plan = resp->trajectory_plan;
317 debug_msg_.velocity_profile = last_speeds_time_unbound_;
319 return;
320 }
321
322 // Reaching here means the plugin needs to generate a new trajectory
323 std::vector<double> new_final_speeds;
324 if (carma_planning_msgs::msg::TrajectoryPlan new_trajectory =
325 generateNewTrajectory(maneuver_plan, req, new_final_speeds);
326 new_trajectory.trajectory_points.size() >= 2)
327 {
328 // New trajectory, by default, extends for the whole maneuver duration, so time bound it
329 auto new_trajectory_time_bound =
331 new_trajectory.trajectory_points, config_.trajectory_time_length);
332
333 resp->trajectory_plan = new_trajectory;
334 resp->trajectory_plan.trajectory_points = new_trajectory_time_bound;
335
336 // Update stored trajectories
337 last_trajectory_time_unbound_ = new_trajectory;
338 last_speeds_time_unbound_ = new_final_speeds;
339
340 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(LCI_TACTICAL_LOGGER),
341 "USING NEW TRAJECTORY for case: " << (int)new_case);
342 }
343 // Fall back to last trajectory if new is invalid but last is valid
344 else if (last_trajectory_time_unbound_.trajectory_points.size() >= 2 &&
345 rclcpp::Time(last_trajectory_time_unbound_.trajectory_points.back().target_time) > current_time)
346 {
347 resp->trajectory_plan = last_trajectory_time_unbound_;
348 resp->trajectory_plan.trajectory_points = last_trajectory_time_bound;
349
350 RCLCPP_WARN_STREAM(rclcpp::get_logger(LCI_TACTICAL_LOGGER),
351 "Failed to generate a new trajectory, so using last valid trajectory!");
352 }
353 // Last resort - return the invalid new trajectory
354 else
355 {
356 resp->trajectory_plan = new_trajectory;
357 RCLCPP_WARN_STREAM(rclcpp::get_logger(LCI_TACTICAL_LOGGER),
358 "Failed to generate a new trajectory or use old valid trajectory, "
359 "so returning empty/invalid trajectory!");
360 }
361
362 // Update stored case information
363
364 last_case_ = new_case;
365 is_last_case_successful_ = is_new_case_successful;
366
367 // Update variables for next evaluation
368 if (is_new_case_successful) {
369 // LCI Tactical plugin receives only single maneuver that can span multiple lanelets
370 // end of the maneuver is expected to be the start of the intersection
372 GET_MANEUVER_PROPERTY(maneuver_plan.front(), end_dist);
373
374 // Entry time to the intersection
376 rclcpp::Time(GET_MANEUVER_PROPERTY(maneuver_plan.front(), end_time)).seconds();
377
378 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(LCI_TACTICAL_LOGGER),
379 "last_successful_ending_downtrack_:" << last_successful_ending_downtrack_ <<
380 ", last_successful_scheduled_entry_time_: " <<
382 }
383
384 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(LCI_TACTICAL_LOGGER),
385 "Debug: new case:" << (int) new_case << ", is_new_case_successful: "
386 << is_new_case_successful);
387
388 resp->maneuver_status.push_back(
389 carma_planning_msgs::srv::PlanTrajectory::Response::MANEUVER_IN_PROGRESS);
390 resp->trajectory_plan.initial_longitudinal_velocity = last_speeds_time_unbound_.front();
391
392 // Set the planning plugin field name
393 for (auto& p : resp->trajectory_plan.trajectory_points) {
394 p.planner_plugin_name = plugin_name_;
395 }
396
397 debug_msg_.trajectory_plan = resp->trajectory_plan;
398 debug_msg_.velocity_profile = last_speeds_time_unbound_;
400 }
401
403 carma_planning_msgs::srv::PlanTrajectory::Request::SharedPtr req,
404 carma_planning_msgs::srv::PlanTrajectory::Response::SharedPtr resp)
405 {
406 std::chrono::system_clock::time_point start_time = std::chrono::system_clock::now();
407
408 latest_traj_request_header_stamp_ = rclcpp::Time(req->header.stamp); //for debugging
409
410 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(LCI_TACTICAL_LOGGER),
411 "Starting light controlled intersection trajectory planning");
412
413 planTrajectorySmoothing(req, resp);
414
415 std::chrono::system_clock::time_point end_time = std::chrono::system_clock::now();
416 auto duration = end_time - start_time;
417
418 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(LCI_TACTICAL_LOGGER),
419 "ExecutionTime: " << std::chrono::duration<double>(duration).count());
420 }
421
422 void LightControlledIntersectionTacticalPlugin::applyTrajectorySmoothingAlgorithm(const carma_wm::WorldModelConstPtr& wm, std::vector<PointSpeedPair>& points_and_target_speeds, double start_dist, double remaining_dist,
423 double starting_speed, double departure_speed, TrajectoryParams tsp)
424 {
425 if (points_and_target_speeds.empty())
426 {
427 throw std::invalid_argument("Point and target speed list is empty! Unable to apply case one speed profile...");
428 }
429
430 // Checking route geometry start against start_dist and adjust profile
431 double planning_downtrack_start = wm->routeTrackPos(points_and_target_speeds[0].point).downtrack; // this can include buffered points earlier than maneuver start_dist
432
433 //Check calculated total dist against maneuver limits
434 double total_distance_needed = remaining_dist;
435 double dist1 = tsp.x1_ - start_dist;
436 double dist2 = tsp.x2_ - start_dist;
437 double dist3 = tsp.x3_ - start_dist;
438
439 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(LCI_TACTICAL_LOGGER), "total_distance_needed: " << total_distance_needed << "\n" <<
440 "dist1: " << dist1 << "\n" <<
441 "dist2: " << dist2 << "\n" <<
442 "dist3: " << dist3);
443 double algo_min_speed = std::min({tsp.v1_,tsp.v2_,tsp.v3_});
444 double algo_max_speed = std::max({tsp.v1_,tsp.v2_,tsp.v3_});
445
446 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(LCI_TACTICAL_LOGGER), "found algo_minimum_speed: " << algo_min_speed << "\n" <<
447 "algo_max_speed: " << algo_max_speed);
448
449 double total_dist_planned = 0; //Starting dist for maneuver treated as 0.0
450
451 if (planning_downtrack_start < start_dist)
452 {
453 //Account for the buffer distance that is technically not part of this maneuver
454
455 total_dist_planned = planning_downtrack_start - start_dist;
456 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(LCI_TACTICAL_LOGGER), "buffered section is present. Adjusted total_dist_planned to: " << total_dist_planned);
457 }
458
459 double prev_speed = starting_speed;
460 auto prev_point = points_and_target_speeds.front();
461
462 for(auto& p : points_and_target_speeds)
463 {
464 double delta_d = lanelet::geometry::distance2d(prev_point.point, p.point);
465 total_dist_planned += delta_d;
466
467 //Apply the speed from algorithm at dist covered
468 //Kinematic: v_f = sqrt(v_o^2 + 2*a*d)
469 double speed_i;
470 if (total_dist_planned <= epsilon_)
471 {
472 //Keep target speed same for buffer distance portion
473 speed_i = starting_speed;
474 }
475 else if(total_dist_planned <= dist1 + epsilon_){
476 //First segment
477 speed_i = sqrt(pow(starting_speed, 2) + 2 * tsp.a1_ * total_dist_planned);
478 }
479 else if(total_dist_planned > dist1 && total_dist_planned <= dist2 + epsilon_){
480 //Second segment
481 speed_i = sqrt(std::max(pow(tsp.v1_, 2) + 2 * tsp.a2_ * (total_dist_planned - dist1), 0.0)); //std::max to ensure negative value is not sqrt
482 }
483 else if (total_dist_planned > dist2 && total_dist_planned <= dist3 + epsilon_)
484 {
485 //Third segment
486 speed_i = sqrt(std::max(pow(tsp.v2_, 2) + 2 * tsp.a3_ * (total_dist_planned - dist2), 0.0)); //std::max to ensure negative value is not sqrt
487 }
488 else
489 {
490 //buffer points that will be cut
491 speed_i = prev_speed;
492 }
493
494 if (isnan(speed_i))
495 {
496 speed_i = std::max(config_.minimum_speed, algo_min_speed);
497 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(LCI_TACTICAL_LOGGER), "Detected nan number from equations. Set to " << speed_i);
498 }
499
500 p.speed = std::max({speed_i, config_.minimum_speed, algo_min_speed});
501 p.speed = std::min({p.speed, speed_limit_, algo_max_speed});
502 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(LCI_TACTICAL_LOGGER), "Applied speed: " << p.speed << ", at dist: " << total_dist_planned);
503
504 prev_point = p;
505 prev_speed = p.speed;
506 }
507 }
508
509 void LightControlledIntersectionTacticalPlugin::applyOptimizedTargetSpeedProfile(const carma_planning_msgs::msg::Maneuver& maneuver, const double starting_speed, std::vector<PointSpeedPair>& points_and_target_speeds)
510 {
511 if(GET_MANEUVER_PROPERTY(maneuver, parameters.float_valued_meta_data).size() < 9 ||
512 GET_MANEUVER_PROPERTY(maneuver, parameters.int_valued_meta_data).size() < 2 ){
513 throw std::invalid_argument("There must be 9 float_valued_meta_data and 2 int_valued_meta_data to apply algorithm's parameters.");
514 }
515
517
518 tsp.a1_ = GET_MANEUVER_PROPERTY(maneuver, parameters.float_valued_meta_data[0]);
519 tsp.v1_ = GET_MANEUVER_PROPERTY(maneuver, parameters.float_valued_meta_data[1]);
520 tsp.x1_ = GET_MANEUVER_PROPERTY(maneuver, parameters.float_valued_meta_data[2]);
521
522 tsp.a2_ = GET_MANEUVER_PROPERTY(maneuver, parameters.float_valued_meta_data[3]);
523 tsp.v2_ = GET_MANEUVER_PROPERTY(maneuver, parameters.float_valued_meta_data[4]);
524 tsp.x2_ = GET_MANEUVER_PROPERTY(maneuver, parameters.float_valued_meta_data[5]);
525
526 tsp.a3_ = GET_MANEUVER_PROPERTY(maneuver, parameters.float_valued_meta_data[6]);
527 tsp.v3_ = GET_MANEUVER_PROPERTY(maneuver, parameters.float_valued_meta_data[7]);
528 tsp.x3_ = GET_MANEUVER_PROPERTY(maneuver, parameters.float_valued_meta_data[8]);
529
530 double starting_downtrack = GET_MANEUVER_PROPERTY(maneuver, start_dist);
531 double ending_downtrack = GET_MANEUVER_PROPERTY(maneuver, end_dist);
532 double departure_speed = GET_MANEUVER_PROPERTY(maneuver, end_speed);
533 double scheduled_entry_time = rclcpp::Time(GET_MANEUVER_PROPERTY(maneuver, end_time)).seconds();
534 double entry_dist = ending_downtrack - starting_downtrack;
535
536 // change speed profile depending on algorithm case starting from maneuver start_dist
537 applyTrajectorySmoothingAlgorithm(wm_, points_and_target_speeds, starting_downtrack,
538 entry_dist, starting_speed, departure_speed, tsp);
539 }
540
541 double LightControlledIntersectionTacticalPlugin::findSpeedLimit(const lanelet::ConstLanelet& llt, const carma_wm::WorldModelConstPtr &wm) const
542 {
543 lanelet::Optional<carma_wm::TrafficRulesConstPtr> traffic_rules = wm->getTrafficRules();
544 if (traffic_rules)
545 {
546 return (*traffic_rules)->speedLimit(llt).speedLimit.value();
547 }
548 else
549 {
550 throw std::invalid_argument("Valid traffic rules object could not be built");
551 }
552 }
553
554 std::vector<PointSpeedPair> LightControlledIntersectionTacticalPlugin::createGeometryProfile(const std::vector<carma_planning_msgs::msg::Maneuver> &maneuvers, double max_starting_downtrack,const carma_wm::WorldModelConstPtr &wm,
555 carma_planning_msgs::msg::VehicleState &ending_state_before_buffer,const carma_planning_msgs::msg::VehicleState& state,
556 const GeneralTrajConfig &general_config, const DetailedTrajConfig &detailed_config)
557 {
558 std::vector<PointSpeedPair> points_and_target_speeds;
559
560 bool first = true;
561 std::unordered_set<lanelet::Id> visited_lanelets;
562 std::vector<carma_planning_msgs::msg::Maneuver> processed_maneuvers;
563 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(LCI_TACTICAL_LOGGER), "VehDowntrack: "<<max_starting_downtrack);
564
565 // Only one maneuver is expected in the received maneuver plan
566 if(maneuvers.size() == 1)
567 {
568 auto maneuver = maneuvers.front();
569
570 double starting_downtrack = GET_MANEUVER_PROPERTY(maneuver, start_dist);
571
572 starting_downtrack = std::min(starting_downtrack, max_starting_downtrack);
573
574 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(LCI_TACTICAL_LOGGER), "Used downtrack: " << starting_downtrack);
575
576 // check if required parameter from strategic planner is present
577 if(GET_MANEUVER_PROPERTY(maneuver, parameters.float_valued_meta_data).empty())
578 {
579 throw std::invalid_argument("No time_to_schedule_entry is provided in float_valued_meta_data");
580 }
581
582 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(LCI_TACTICAL_LOGGER), "Creating Lane Follow Geometry");
583 std::vector<PointSpeedPair> lane_follow_points = basic_autonomy::waypoint_generation::create_lanefollow_geometry(maneuver, starting_downtrack, wm, general_config, detailed_config, visited_lanelets);
584 points_and_target_speeds.insert(points_and_target_speeds.end(), lane_follow_points.begin(), lane_follow_points.end());
585 processed_maneuvers.push_back(maneuver);
586 }
587 else
588 {
589 throw std::invalid_argument("Light Control Intersection Tactical Plugin currently can"
590 " only create a geometry profile for one maneuver");
591 }
592
593 //Add buffer ending to lane follow points at the end of maneuver(s) end dist
594 if(!processed_maneuvers.empty() && processed_maneuvers.back().type == carma_planning_msgs::msg::Maneuver::LANE_FOLLOWING){
595 points_and_target_speeds = add_lanefollow_buffer(wm, points_and_target_speeds, processed_maneuvers, ending_state_before_buffer, detailed_config);
596 }
597
598 return points_and_target_speeds;
599 }
600
602 {
603 config_ = config;
604 }
605
606} // light_controlled_intersection_tactical_plugin
#define GET_MANEUVER_PROPERTY(mvr, property)
Macro definition to enable easier access to fields shared across the maneuver types.
void planTrajectoryCB(carma_planning_msgs::srv::PlanTrajectory::Request::SharedPtr req, carma_planning_msgs::srv::PlanTrajectory::Response::SharedPtr resp)
Function to process the light controlled intersection tactical plugin service call for trajectory pla...
void applyTrajectorySmoothingAlgorithm(const carma_wm::WorldModelConstPtr &wm, std::vector< PointSpeedPair > &points_and_target_speeds, double start_dist, double remaining_dist, double starting_speed, double departure_speed, TrajectoryParams tsp)
Creates a speed profile according to case one or two of the light controlled intersection,...
bool isLastTrajectoryValid(const rclcpp::Time &current_time, double min_remaining_time_seconds=0.0) const
Checks if the last trajectory plan remains valid based on the current time.
void applyOptimizedTargetSpeedProfile(const carma_planning_msgs::msg::Maneuver &maneuver, const double starting_speed, std::vector< PointSpeedPair > &points_and_target_speeds)
Apply optimized target speeds to the trajectory determined for fixed-time and actuated signals....
void planTrajectorySmoothing(carma_planning_msgs::srv::PlanTrajectory::Request::SharedPtr req, carma_planning_msgs::srv::PlanTrajectory::Response::SharedPtr resp)
Smooths the trajectory as part of the trajectory planning process.
carma_planning_msgs::msg::TrajectoryPlan generateNewTrajectory(const std::vector< carma_planning_msgs::msg::Maneuver > &maneuver_plan, const carma_planning_msgs::srv::PlanTrajectory::Request::SharedPtr &req, std::vector< double > &final_speeds)
Generates a new trajectory plan based on the provided maneuver plan and request. NOTE: This function ...
LightControlledIntersectionTacticalPlugin(carma_wm::WorldModelConstPtr wm, const Config &config, const DebugPublisher &debug_publisher, const std::string &plugin_name, std::shared_ptr< carma_ros2_utils::CarmaLifecycleNode > nh)
LightControlledIntersectionTacticalPlugin constructor.
double findSpeedLimit(const lanelet::ConstLanelet &llt, const carma_wm::WorldModelConstPtr &wm) const
Given a Lanelet, find its associated Speed Limit.
bool shouldUseLastTrajectory(TSCase new_case, bool is_new_case_successful, const rclcpp::Time &current_time)
Determines whether the last trajectory should be reused based on the planning case....
std::vector< PointSpeedPair > createGeometryProfile(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 INTERSECTION_TRANSIT maneuver types ...
GeneralTrajConfig compose_general_trajectory_config(const std::string &trajectory_type, int default_downsample_ratio, int turn_downsample_ratio)
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...
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.
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...
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.
void set_logger(rclcpp::Logger logger)
Replace the module-level logger used by all basic_autonomy functions.
Definition: log.cpp:33
rclcpp::Logger get_logger()
Return the module-level logger used by all basic_autonomy functions.
Definition: log.cpp:32
auto to_string(const UtmZone &zone) -> std::string
Definition: utm_zone.cpp:21
std::shared_ptr< const WorldModel > WorldModelConstPtr
Definition: WorldModel.hpp:454
std::function< void(const carma_debug_ros2_msgs::msg::TrajectoryCurvatureSpeeds &)> DebugPublisher
Stuct containing the algorithm configuration values for light_controlled_intersection_tactical_plugin...