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.
plan_delegator.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2022-2023 LEIDOS.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
15 */
16
17#include <stdexcept>
18#include <unordered_set>
19#include <carma_wm/Geometry.hpp>
20#include "plan_delegator.hpp"
21
22namespace plan_delegator
23{
24 namespace std_ph = std::placeholders;
25
26 namespace
27 {
33 void setManeuverStartingLaneletId(carma_planning_msgs::msg::Maneuver& mvr, lanelet::Id start_id) {
34 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"),"Updating maneuver starting_lane_id to " << start_id);
35
36 switch(mvr.type) {
37 case carma_planning_msgs::msg::Maneuver::LANE_CHANGE:
38 mvr.lane_change_maneuver.starting_lane_id = std::to_string(start_id);
39 break;
40 case carma_planning_msgs::msg::Maneuver::INTERSECTION_TRANSIT_STRAIGHT:
41 mvr.intersection_transit_straight_maneuver.starting_lane_id = std::to_string(start_id);
42 break;
43 case carma_planning_msgs::msg::Maneuver::INTERSECTION_TRANSIT_LEFT_TURN:
44 mvr.intersection_transit_left_turn_maneuver.starting_lane_id = std::to_string(start_id);
45 break;
46 case carma_planning_msgs::msg::Maneuver::INTERSECTION_TRANSIT_RIGHT_TURN:
47 mvr.intersection_transit_right_turn_maneuver.starting_lane_id = std::to_string(start_id);
48 break;
49 case carma_planning_msgs::msg::Maneuver::STOP_AND_WAIT:
50 mvr.stop_and_wait_maneuver.starting_lane_id = std::to_string(start_id);
51 break;
52 default:
53 throw std::invalid_argument("Maneuver type does not have starting and ending lane ids");
54 }
55 }
56
62 void setManeuverEndingLaneletId(carma_planning_msgs::msg::Maneuver& mvr, lanelet::Id end_id) {
63 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"),"Updating maneuver ending_lane_id to " << end_id);
64
65 switch(mvr.type) {
66 case carma_planning_msgs::msg::Maneuver::LANE_CHANGE:
67 mvr.lane_change_maneuver.ending_lane_id = std::to_string(end_id);
68 break;
69 case carma_planning_msgs::msg::Maneuver::INTERSECTION_TRANSIT_STRAIGHT:
70 mvr.intersection_transit_straight_maneuver.ending_lane_id = std::to_string(end_id);
71 break;
72 case carma_planning_msgs::msg::Maneuver::INTERSECTION_TRANSIT_LEFT_TURN:
73 mvr.intersection_transit_left_turn_maneuver.ending_lane_id = std::to_string(end_id);
74 break;
75 case carma_planning_msgs::msg::Maneuver::INTERSECTION_TRANSIT_RIGHT_TURN:
76 mvr.intersection_transit_right_turn_maneuver.ending_lane_id = std::to_string(end_id);
77 break;
78 case carma_planning_msgs::msg::Maneuver::STOP_AND_WAIT:
79 mvr.stop_and_wait_maneuver.ending_lane_id = std::to_string(end_id);
80 break;
81 default:
82 throw std::invalid_argument("Maneuver type does not have starting and ending lane ids");
83 }
84 }
85
91 std::string getManeuverStartingLaneletId(carma_planning_msgs::msg::Maneuver mvr) {
92 switch(mvr.type) {
93 case carma_planning_msgs::msg::Maneuver::LANE_CHANGE:
94 return mvr.lane_change_maneuver.starting_lane_id;
95 case carma_planning_msgs::msg::Maneuver::INTERSECTION_TRANSIT_STRAIGHT:
96 return mvr.intersection_transit_straight_maneuver.starting_lane_id;
97 case carma_planning_msgs::msg::Maneuver::INTERSECTION_TRANSIT_LEFT_TURN:
98 return mvr.intersection_transit_left_turn_maneuver.starting_lane_id;
99 case carma_planning_msgs::msg::Maneuver::INTERSECTION_TRANSIT_RIGHT_TURN:
100 return mvr.intersection_transit_right_turn_maneuver.starting_lane_id;
101 case carma_planning_msgs::msg::Maneuver::STOP_AND_WAIT:
102 return mvr.stop_and_wait_maneuver.starting_lane_id;
103 default:
104 throw std::invalid_argument("Maneuver type does not have starting and ending lane ids");
105 }
106 }
107
113 std::string getManeuverEndingLaneletId(carma_planning_msgs::msg::Maneuver mvr) {
114 switch(mvr.type) {
115 case carma_planning_msgs::msg::Maneuver::LANE_CHANGE:
116 return mvr.lane_change_maneuver.ending_lane_id;
117 case carma_planning_msgs::msg::Maneuver::INTERSECTION_TRANSIT_STRAIGHT:
118 return mvr.intersection_transit_straight_maneuver.ending_lane_id;
119 case carma_planning_msgs::msg::Maneuver::INTERSECTION_TRANSIT_LEFT_TURN:
120 return mvr.intersection_transit_left_turn_maneuver.ending_lane_id;
121 case carma_planning_msgs::msg::Maneuver::INTERSECTION_TRANSIT_RIGHT_TURN:
122 return mvr.intersection_transit_right_turn_maneuver.ending_lane_id;
123 case carma_planning_msgs::msg::Maneuver::STOP_AND_WAIT:
124 return mvr.stop_and_wait_maneuver.ending_lane_id;
125 default:
126 throw std::invalid_argument("Maneuver type does not have starting and ending lane ids");
127 }
128 }
129 } //namespace
130
131
132 PlanDelegator::PlanDelegator(const rclcpp::NodeOptions &options) : carma_ros2_utils::CarmaLifecycleNode(options),
133 tf2_buffer_(std::make_shared<tf2_ros::Buffer>(this->get_clock())),
134 wml_(this->get_node_base_interface(), this->get_node_logging_interface(),
135 this->get_node_topics_interface(), this->get_node_parameters_interface())
136 {
137 // Create initial config
138 config_ = Config();
139
140 config_.planning_topic_prefix = declare_parameter<std::string>("planning_topic_prefix", config_.planning_topic_prefix);
141 config_.planning_topic_suffix = declare_parameter<std::string>("planning_topic_suffix", config_.planning_topic_suffix);
142 config_.trajectory_planning_rate = declare_parameter<double>("trajectory_planning_rate", config_.trajectory_planning_rate);
143 config_.max_trajectory_duration = declare_parameter<double>("trajectory_duration_threshold", config_.max_trajectory_duration);
144 config_.min_crawl_speed = declare_parameter<double>("min_speed", config_.min_crawl_speed);
145 config_.duration_to_signal_before_lane_change = declare_parameter<double>("duration_to_signal_before_lane_change", config_.duration_to_signal_before_lane_change);
146 config_.max_traj_generation_reattempt = declare_parameter<int>("max_traj_generation_reattempt", config_.max_traj_generation_reattempt);
147 config_.tactical_plugin_service_call_timeout = declare_parameter<int>("tactical_plugin_service_call_timeout", config_.tactical_plugin_service_call_timeout);
148 }
149
150 carma_ros2_utils::CallbackReturn PlanDelegator::handle_on_configure(const rclcpp_lifecycle::State &)
151 {
152 // Reset config
153 config_ = Config();
154
155 get_parameter<std::string>("planning_topic_prefix", config_.planning_topic_prefix);
156 get_parameter<std::string>("planning_topic_suffix", config_.planning_topic_suffix);
157 get_parameter<double>("trajectory_planning_rate", config_.trajectory_planning_rate);
158 get_parameter<double>("trajectory_duration_threshold", config_.max_trajectory_duration);
159 get_parameter<double>("min_speed", config_.min_crawl_speed);
160 get_parameter<double>("duration_to_signal_before_lane_change", config_.duration_to_signal_before_lane_change);
161 get_parameter<int>("tactical_plugin_service_call_timeout", config_.tactical_plugin_service_call_timeout);
162 get_parameter<int>("max_traj_generation_reattempt", config_.max_traj_generation_reattempt);
163
164 RCLCPP_INFO_STREAM(rclcpp::get_logger("plan_delegator"),"Done loading parameters: " << config_);
165
166 // Setup publishers
167 traj_pub_ = create_publisher<carma_planning_msgs::msg::TrajectoryPlan>("plan_trajectory", 5);
168 upcoming_lane_change_status_pub_ = create_publisher<carma_planning_msgs::msg::UpcomingLaneChangeStatus>("upcoming_lane_change_status", 1);
169 turn_signal_command_pub_ = create_publisher<autoware_msgs::msg::LampCmd>("lamp_cmd", 1);
170
171 // Setup subscribers
172 plan_sub_ = create_subscription<carma_planning_msgs::msg::ManeuverPlan>("final_maneuver_plan", 5, std::bind(&PlanDelegator::maneuverPlanCallback, this, std_ph::_1));
173 twist_sub_ = create_subscription<geometry_msgs::msg::TwistStamped>("current_velocity", 5,
174 [this](geometry_msgs::msg::TwistStamped::UniquePtr twist) {this->latest_twist_ = *twist;});
175 pose_sub_ = create_subscription<geometry_msgs::msg::PoseStamped>("current_pose", 5, std::bind(&PlanDelegator::poseCallback, this, std_ph::_1));
176 guidance_state_sub_ = create_subscription<carma_planning_msgs::msg::GuidanceState>("guidance_state", 5, std::bind(&PlanDelegator::guidanceStateCallback, this, std_ph::_1));
177
180 return CallbackReturn::SUCCESS;
181 }
182
183 carma_ros2_utils::CallbackReturn PlanDelegator::handle_on_activate(const rclcpp_lifecycle::State &)
184 {
185 timer_callback_group_ = create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
186 traj_timer_ = create_timer(get_clock(),
187 std::chrono::milliseconds((int)(1 / config_.trajectory_planning_rate * 1000)),
189 return CallbackReturn::SUCCESS;
190 }
191
192 void PlanDelegator::guidanceStateCallback(carma_planning_msgs::msg::GuidanceState::UniquePtr msg)
193 {
195 }
196
197 void PlanDelegator::maneuverPlanCallback(carma_planning_msgs::msg::ManeuverPlan::UniquePtr plan)
198 {
199 RCLCPP_INFO_STREAM(rclcpp::get_logger("plan_delegator"),"Received request to delegate plan ID " << std::string(plan->maneuver_plan_id));
200 // do basic check to see if the input is valid
201 auto copy_plan = *plan;
203 if (isManeuverPlanValid(copy_plan))
204 {
205 latest_maneuver_plan_ = copy_plan;
206 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"),"Received plan with " << latest_maneuver_plan_.maneuvers.size() << " maneuvers");
207
208 // Update the parameters associated with each maneuver
209 for (auto& maneuver : latest_maneuver_plan_.maneuvers) {
210 updateManeuverParameters(maneuver);
211 }
212 }
213 else {
214 RCLCPP_WARN_STREAM(rclcpp::get_logger("plan_delegator"),"Received empty plan, no maneuvers found in plan ID " << std::string(plan->maneuver_plan_id));
215 }
216
217 // Update upcoming_lane_change_information_ and current_lane_change_information_ based on the received maneuver plan
218 if(!latest_maneuver_plan_.maneuvers.empty()){
219 // Get ego vehicle's current downtrack
220 lanelet::BasicPoint2d current_loc(latest_pose_.pose.position.x, latest_pose_.pose.position.y);
221 double current_downtrack = wm_->routeTrackPos(current_loc).downtrack;
222
223 // Set upcoming_lane_change_information_ based on the first found lane change in the plan that begins after current_downtrack, if one exists
224 upcoming_lane_change_information_ = boost::optional<LaneChangeInformation>(); // Reset to empty optional
225 for(const auto& maneuver : latest_maneuver_plan_.maneuvers){
226 if(maneuver.type == carma_planning_msgs::msg::Maneuver::LANE_CHANGE){
227 if(current_downtrack >= maneuver.lane_change_maneuver.start_dist){
228 // Skip this lane change maneuver since ego vehicle has passed the lane change start point (this is not an 'upcoming' lane change)
229 continue;
230 }
231 else{
232 LaneChangeInformation upcoming_lane_change_information = getLaneChangeInformation(maneuver);
233 upcoming_lane_change_information_ = boost::optional<LaneChangeInformation>(upcoming_lane_change_information);
234 break;
235 }
236 }
237 }
238
239 // Set current_lane_change_information_ if the first maneuver is a lane change
240 current_lane_change_information_ = boost::optional<LaneChangeInformation>(); // Reset to empty optional
241 if(latest_maneuver_plan_.maneuvers[0].type == carma_planning_msgs::msg::Maneuver::LANE_CHANGE){
242 LaneChangeInformation current_lane_change_information = getLaneChangeInformation(latest_maneuver_plan_.maneuvers[0]);
243 current_lane_change_information_ = boost::optional<LaneChangeInformation>(current_lane_change_information);
244 }
245 }
246 }
247
248 void PlanDelegator::poseCallback(geometry_msgs::msg::PoseStamped::UniquePtr pose_msg)
249 {
250 latest_pose_ = *pose_msg;
251
252 // Publish the upcoming lane change status
254
255 // Publish the current turn signal command
257 }
258
259 LaneChangeInformation PlanDelegator::getLaneChangeInformation(const carma_planning_msgs::msg::Maneuver& lane_change_maneuver){
260 LaneChangeInformation lane_change_information;
261
262 lane_change_information.starting_downtrack = lane_change_maneuver.lane_change_maneuver.start_dist;
263
264 // Get the starting and ending lanelets for this lane change maneuver
265 lanelet::ConstLanelet starting_lanelet = wm_->getMap()->laneletLayer.get(std::stoi(lane_change_maneuver.lane_change_maneuver.starting_lane_id));
266 lanelet::ConstLanelet ending_lanelet = wm_->getMap()->laneletLayer.get(std::stoi(lane_change_maneuver.lane_change_maneuver.ending_lane_id));
267
268 // Determine if lane change is a left or right lane change and update lane_change_information accordingly.
269 // This function runs directly inside the final_maneuver_plan subscription callback (once per received
270 // plan), so it must never throw here: an uncaught exception in a subscription callback can crash this
271 // node outright. Previously it did throw whenever the walk from starting_lanelet to a shared boundary
272 // with ending_lanelet hit a lanelet with no routable successor -- which happens whenever an intervening
273 // lanelet is closed (e.g. by a TCM) or simply missing adjacency data in the map. Below, that same walk
274 // is attempted first since it is exact when it works, but any inability to complete it (no following
275 // lanelet, a routing loop) now falls through to a purely geometric left/right estimate instead of
276 // failing, so a lane change is always reported instead of crashing.
277 boost::optional<bool> is_right_lane_change;
278
279 if(starting_lanelet.leftBound() == ending_lanelet.rightBound()){
280 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"), "Lanelet " << std::to_string(starting_lanelet.id()) << " shares left boundary with " << std::to_string(ending_lanelet.id()));
281 is_right_lane_change = false;
282 }
283 else if(starting_lanelet.rightBound() == ending_lanelet.leftBound()){
284 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"), "Lanelet " << std::to_string(starting_lanelet.id()) << " shares right boundary with " << std::to_string(ending_lanelet.id()));
285 is_right_lane_change = true;
286 }
287 else
288 {
289 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"), "Searching for shared boundary with starting lanechange lanelet " << std::to_string(starting_lanelet.id()) << " and ending lanelet " << std::to_string(ending_lanelet.id()));
290 lanelet::ConstLanelet current_lanelet = starting_lanelet;
291 std::unordered_set<lanelet::Id> visited{current_lanelet.id()};
292
293 while(!is_right_lane_change){
294 // Assumption: Adjacent lanelets share lane boundary
295 auto following_lanelets = wm_->getMapRoutingGraph()->following(current_lanelet, false);
296 bool no_successor = following_lanelets.empty();
297 lanelet::ConstLanelet candidate_lanelet = no_successor ? current_lanelet : following_lanelets.front();
298 bool loop_detected = !no_successor && visited.count(candidate_lanelet.id()) > 0;
299
300 if(no_successor)
301 {
302 RCLCPP_WARN_STREAM(rclcpp::get_logger("plan_delegator"), "No following lanelets from lanelet " << current_lanelet.id()
303 << " reachable without a lane change (possibly closed or missing from the map); "
304 << "falling back to a geometric left/right estimate for lane change from "
305 << starting_lanelet.id() << " to " << ending_lanelet.id());
306 }
307
308 if (loop_detected)
309 {
310 RCLCPP_WARN_STREAM(rclcpp::get_logger("plan_delegator"), "Detected a routing loop while searching for a shared boundary between lanelet "
311 << starting_lanelet.id() << " and " << ending_lanelet.id() << "; falling back to a geometric left/right estimate");
312 }
313
314 if(no_successor || loop_detected)
315 {
316 break;
317 }
318
319 current_lanelet = candidate_lanelet;
320 visited.insert(current_lanelet.id());
321
322 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"), "Now checking for shared lane boundary with lanelet " << std::to_string(current_lanelet.id()) << " and ending lanelet " << std::to_string(ending_lanelet.id()));
323 if(current_lanelet.leftBound() == ending_lanelet.rightBound()){
324 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"), "Lanelet " << std::to_string(current_lanelet.id()) << " shares left boundary with " << std::to_string(ending_lanelet.id()));
325 is_right_lane_change = false;
326 }
327 else if(current_lanelet.rightBound() == ending_lanelet.leftBound()){
328 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"), "Lanelet " << std::to_string(current_lanelet.id()) << " shares right boundary with " << std::to_string(ending_lanelet.id()));
329 is_right_lane_change = true;
330 }
331 }
332 }
333
334 if(!is_right_lane_change)
335 {
336 // Could not confirm a shared boundary via routing (e.g. an intervening lanelet was closed or missing
337 // from the map). Fall back to pure geometry: which side of the starting lanelet's heading does the
338 // ending lanelet fall on? This only needs the two lanelets' own centerlines, so it works even when
339 // they are otherwise disconnected in the routing graph.
340 lanelet::BasicLineString2d starting_centerline = starting_lanelet.centerline2d().basicLineString();
341 lanelet::BasicPoint2d start_pt = starting_centerline.front();
342 lanelet::BasicPoint2d heading_vec = starting_centerline.back() - start_pt;
343 lanelet::BasicPoint2d to_target = ending_lanelet.centerline2d().basicLineString().front() - start_pt;
344 double cross = heading_vec.x() * to_target.y() - heading_vec.y() * to_target.x();
345
346 RCLCPP_WARN_STREAM(rclcpp::get_logger("plan_delegator"), "Could not find a shared lane boundary between starting lanelet "
347 << starting_lanelet.id() << " and ending lanelet " << ending_lanelet.id()
348 << "; estimating lane change direction geometrically instead.");
349
350 is_right_lane_change = (cross < 0.0);
351 }
352
353 lane_change_information.is_right_lane_change = is_right_lane_change.get();
354 return lane_change_information;
355 }
356
357 void PlanDelegator::publishUpcomingLaneChangeStatus(const boost::optional<LaneChangeInformation>& upcoming_lane_change_information){
358 // Initialize an UpcomingLaneChangeStatus message, which will be populated based on upcoming_lane_change_information
359 carma_planning_msgs::msg::UpcomingLaneChangeStatus upcoming_lane_change_status;
360
361 // Update upcoming_lane_change_status
362 if(upcoming_lane_change_information){
363 // Get the downtrack distance between the ego vehicle and the start of the upcoming lane change maneuver
364 lanelet::BasicPoint2d current_loc(latest_pose_.pose.position.x, latest_pose_.pose.position.y);
365 double current_downtrack = wm_->routeTrackPos(current_loc).downtrack;
366 upcoming_lane_change_status.downtrack_until_lanechange = std::max(0.0, upcoming_lane_change_information.get().starting_downtrack - current_downtrack);
367
368 // Set upcoming lane change status as a right lane change or left lane change
369 if(upcoming_lane_change_information.get().is_right_lane_change){
370 upcoming_lane_change_status.lane_change = carma_planning_msgs::msg::UpcomingLaneChangeStatus::RIGHT;
371 }
372 else{
373 upcoming_lane_change_status.lane_change = carma_planning_msgs::msg::UpcomingLaneChangeStatus::LEFT;
374 }
375 }
376 else{
377 upcoming_lane_change_status.lane_change = carma_planning_msgs::msg::UpcomingLaneChangeStatus::NONE;
378 }
379
380 // Publish upcoming_lane_change_status
381 upcoming_lane_change_status_pub_->publish(upcoming_lane_change_status);
382
383 // Store UpcomingLaneChangeStatus in upcoming_lane_change_status_
384 upcoming_lane_change_status_ = upcoming_lane_change_status;
385 }
386
387 void PlanDelegator::publishTurnSignalCommand(const boost::optional<LaneChangeInformation>& current_lane_change_information, const carma_planning_msgs::msg::UpcomingLaneChangeStatus& upcoming_lane_change_status)
388 {
389 // Initialize turn signal command message
390 // NOTE: A LampCmd message can have its 'r' OR 'l' field set to 1 to indicate an activated right or left turn signal, respectively. Both fields cannot be set to 1 at the same time.
391 autoware_msgs::msg::LampCmd turn_signal_command;
392
393 // Publish turn signal command with priority placed on the current lane change, if one exists
394 if(current_lane_change_information){
395 // Publish turn signal command for the current lane change based on the lane change direction
396 if(current_lane_change_information.get().is_right_lane_change){
397 turn_signal_command.r = 1;
398 }
399 else{
400 turn_signal_command.l = 1;
401 }
402 turn_signal_command_pub_->publish(turn_signal_command);
403 }
404 else if(upcoming_lane_change_status.lane_change != carma_planning_msgs::msg::UpcomingLaneChangeStatus::NONE){
405 // Only publish turn signal command for upcoming lane change if it will begin in less than the time defined by config_.duration_to_signal_before_lane_change
406 if((upcoming_lane_change_status.downtrack_until_lanechange / latest_twist_.twist.linear.x) <= config_.duration_to_signal_before_lane_change){
407 if(upcoming_lane_change_status.lane_change == carma_planning_msgs::msg::UpcomingLaneChangeStatus::RIGHT){
408 turn_signal_command.r = 1;
409 }
410 else{
411 turn_signal_command.l = 1;
412 }
413 turn_signal_command_pub_->publish(turn_signal_command);
414 }
415 }
416 else{
417 // Publish turn signal command with neither turn signal activated
418 turn_signal_command_pub_->publish(turn_signal_command);
419 }
420
421 // Store turn signal command in latest_turn_signal_command_
422 latest_turn_signal_command_ = turn_signal_command;
423 }
424
425 carma_ros2_utils::ClientPtr<carma_planning_msgs::srv::PlanTrajectory> PlanDelegator::getPlannerClientByName(const std::string& planner_name)
426 {
427 if(planner_name.size() == 0)
428 {
429 throw std::invalid_argument("Invalid trajectory planner name because it has zero length!");
430 }
431 if(trajectory_planners_.find(planner_name) == trajectory_planners_.end())
432 {
433 RCLCPP_INFO_STREAM(rclcpp::get_logger("plan_delegator"),"Discovered new trajectory planner: " << planner_name);
434
435 trajectory_planners_.emplace(
436 planner_name, create_client<carma_planning_msgs::srv::PlanTrajectory>(config_.planning_topic_prefix + planner_name + config_.planning_topic_suffix));
437 }
438 return trajectory_planners_[planner_name];
439 }
440
441 bool PlanDelegator::isManeuverPlanValid(const carma_planning_msgs::msg::ManeuverPlan& maneuver_plan) const noexcept
442 {
443 // currently it only checks if maneuver list is empty
444 return !maneuver_plan.maneuvers.empty();
445 }
446
447 bool PlanDelegator::isTrajectoryValid(const carma_planning_msgs::msg::TrajectoryPlan& trajectory_plan) const noexcept
448 {
449 // currently it only checks if trajectory contains less than 2 points
450 return !(trajectory_plan.trajectory_points.size() < 2);
451 }
452
453 bool PlanDelegator::isManeuverExpired(const carma_planning_msgs::msg::Maneuver& maneuver, rclcpp::Time current_time) const
454 {
455 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"), "maneuver start time:" << std::to_string(rclcpp::Time(GET_MANEUVER_PROPERTY(maneuver, start_time)).seconds()));
456 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"), "maneuver end time:" << std::to_string(rclcpp::Time(GET_MANEUVER_PROPERTY(maneuver, end_time)).seconds()));
457 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"), "current time:" << std::to_string(now().seconds()));
458 bool isexpired = rclcpp::Time(GET_MANEUVER_PROPERTY(maneuver, end_time), get_clock()->get_clock_type()) <= current_time; // TODO maneuver expiration should maybe be based off of distance not time? https://github.com/usdot-fhwa-stol/carma-platform/issues/1107
459 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"), "isexpired:" << isexpired);
460 // TODO: temporary disabling expiration check
461 return false;
462 }
463
464 std::shared_ptr<carma_planning_msgs::srv::PlanTrajectory::Request>
466 const carma_planning_msgs::msg::TrajectoryPlan& latest_trajectory_plan,
467 const carma_planning_msgs::msg::ManeuverPlan& locked_maneuver_plan,
468 const uint16_t& current_maneuver_index) const
469 {
470 auto plan_req = std::make_shared<carma_planning_msgs::srv::PlanTrajectory::Request>();
471 plan_req->maneuver_plan = locked_maneuver_plan;
472
473 // set current vehicle state if we have NOT planned any previous trajectories
474 if(latest_trajectory_plan.trajectory_points.empty())
475 {
476 plan_req->header.stamp = latest_pose_.header.stamp;
477 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"), "latest_pose_.header.stamp: " << std::to_string(rclcpp::Time(latest_pose_.header.stamp).seconds()));
478 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"), "plan_req->header.stamp: " << std::to_string(rclcpp::Time(plan_req->header.stamp).seconds()));
479
480 plan_req->vehicle_state.longitudinal_vel = latest_twist_.twist.linear.x;
481 plan_req->vehicle_state.x_pos_global = latest_pose_.pose.position.x;
482 plan_req->vehicle_state.y_pos_global = latest_pose_.pose.position.y;
483 double roll, pitch, yaw;
484 carma_wm::geometry::rpyFromQuaternion(latest_pose_.pose.orientation, roll, pitch, yaw);
485 plan_req->vehicle_state.orientation = yaw;
486 plan_req->maneuver_index_to_plan = current_maneuver_index;
487 }
488 // set vehicle state based on last two planned trajectory points
489 else
490 {
491 carma_planning_msgs::msg::TrajectoryPlanPoint last_point = latest_trajectory_plan.trajectory_points.back();
492 carma_planning_msgs::msg::TrajectoryPlanPoint second_last_point = *(latest_trajectory_plan.trajectory_points.rbegin() + 1);
493 plan_req->vehicle_state.x_pos_global = last_point.x;
494 plan_req->vehicle_state.y_pos_global = last_point.y;
495 auto distance_diff = std::sqrt(std::pow(last_point.x - second_last_point.x, 2) + std::pow(last_point.y - second_last_point.y, 2));
496 rclcpp::Duration time_diff = rclcpp::Time(last_point.target_time) - rclcpp::Time(second_last_point.target_time);
497 auto time_diff_sec = time_diff.seconds();
498 plan_req->maneuver_index_to_plan = current_maneuver_index;
499 // this assumes the vehicle does not have significant lateral velocity
500 plan_req->header.stamp = latest_trajectory_plan.trajectory_points.back().target_time;
501 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"), "plan_req->header.stamp: " << std::to_string(rclcpp::Time(plan_req->header.stamp).seconds()));
502
503 plan_req->vehicle_state.longitudinal_vel = distance_diff / time_diff_sec;
504 // TODO develop way to set yaw value for future points
505 }
506 return plan_req;
507 }
508
509 bool PlanDelegator::isTrajectoryLongEnough(const carma_planning_msgs::msg::TrajectoryPlan& plan) const noexcept
510 {
511 rclcpp::Duration time_diff = rclcpp::Time(plan.trajectory_points.back().target_time) - rclcpp::Time(plan.trajectory_points.front().target_time);
512 return time_diff.seconds() >= config_.max_trajectory_duration;
513 }
514
515 void PlanDelegator::updateManeuverParameters(carma_planning_msgs::msg::Maneuver& maneuver)
516 {
517 if (!wm_->getMap())
518 {
519 RCLCPP_ERROR_STREAM(rclcpp::get_logger("plan_delegator"), "Map is not set yet");
520 return;
521 }
522
523 // Update maneuver starting and ending downtrack distances
524 double original_start_dist = GET_MANEUVER_PROPERTY(maneuver, start_dist);
525 double original_end_dist = GET_MANEUVER_PROPERTY(maneuver, end_dist);
526 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"),"Changing maneuver distances for planner: " << GET_MANEUVER_PROPERTY(maneuver, parameters.planning_tactical_plugin));
527 double adjusted_start_dist = original_start_dist - length_to_front_bumper_;
528 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"),"original_start_dist:" << original_start_dist);
529 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"),"adjusted_start_dist:" << adjusted_start_dist);
530 double adjusted_end_dist = original_end_dist - length_to_front_bumper_;
531 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"),"original_end_dist:" << original_end_dist);
532 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"),"adjusted_end_dist:" << adjusted_end_dist);
533 SET_MANEUVER_PROPERTY(maneuver, start_dist, adjusted_start_dist);
534 SET_MANEUVER_PROPERTY(maneuver, end_dist, adjusted_end_dist);
535
536 // Shift maneuver starting and ending lanelets
537 // NOTE: Assumes that maneuver start and end downtrack distances have not been shifted by more than one lanelet
538 if(maneuver.type == carma_planning_msgs::msg::Maneuver::LANE_FOLLOWING && !maneuver.lane_following_maneuver.lane_ids.empty()){
539 // (1) Add new beginning lanelet to maneuver if necessary and (2) remove ending lanelet from maneuver if necessary
540
541 // Obtain the original starting lanelet from the maneuver
542 lanelet::Id original_starting_lanelet_id = std::stoi(maneuver.lane_following_maneuver.lane_ids.front());
543 lanelet::ConstLanelet original_starting_lanelet = wm_->getMap()->laneletLayer.get(original_starting_lanelet_id);
544
545 // Get the downtrack of the start of the original starting lanelet
546 lanelet::BasicPoint2d original_starting_lanelet_centerline_start_point = lanelet::utils::to2D(original_starting_lanelet.centerline()).front();
547 double original_starting_lanelet_centerline_start_point_dt = wm_->routeTrackPos(original_starting_lanelet_centerline_start_point).downtrack;
548
549 if(adjusted_start_dist < original_starting_lanelet_centerline_start_point_dt){
550
551 auto previous_lanelets = wm_->getMapRoutingGraph()->previous(original_starting_lanelet, false);
552
553 if(!previous_lanelets.empty()){
554
555 auto llt_on_route_optional = wm_->getFirstLaneletOnShortestPath(previous_lanelets);
556
557 lanelet::ConstLanelet previous_lanelet_to_add;
558
559 if (llt_on_route_optional){
560 previous_lanelet_to_add = llt_on_route_optional.value();
561 }
562 else{
563 RCLCPP_WARN_STREAM(rclcpp::get_logger("plan_delegator"), "When adjusting maneuver for lane follow, no previous lanelet found on the shortest path for lanelet "
564 << original_starting_lanelet.id() << ". Picking arbitrary lanelet: " << previous_lanelets[0].id() << ", instead");
565 previous_lanelet_to_add = previous_lanelets[0];
566 }
567
568 // lane_ids array is ordered by increasing downtrack, so this new starting lanelet is inserted at the front
569 maneuver.lane_following_maneuver.lane_ids.insert(maneuver.lane_following_maneuver.lane_ids.begin(), std::to_string(previous_lanelet_to_add.id()));
570
571 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"), "Inserted lanelet " << std::to_string(previous_lanelet_to_add.id()) << " to beginning of maneuver.");
572 }
573 else{
574 RCLCPP_WARN_STREAM(rclcpp::get_logger("plan_delegator"), "No previous lanelet was found for lanelet " << original_starting_lanelet.id());
575 }
576 }
577
578 // Obtain the maneuver ending lanelet
579 lanelet::Id original_ending_lanelet_id = std::stoi(maneuver.lane_following_maneuver.lane_ids.back());
580 lanelet::ConstLanelet original_ending_lanelet = wm_->getMap()->laneletLayer.get(original_ending_lanelet_id);
581
582 // Get the downtrack of the start of the maneuver ending lanelet
583 lanelet::BasicPoint2d original_ending_lanelet_centerline_start_point = lanelet::utils::to2D(original_ending_lanelet.centerline()).front();
584 double original_ending_lanelet_centerline_start_point_dt = wm_->routeTrackPos(original_ending_lanelet_centerline_start_point).downtrack;
585
586 if(adjusted_end_dist < original_ending_lanelet_centerline_start_point_dt){
587 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"),"Original ending lanelet " << original_ending_lanelet.id() << " removed from lane_ids since the updated maneuver no longer crosses it");
588
589 // lane_ids array is ordered by increasing downtrack, so the last element in the array corresponds to the original ending lanelet
590 maneuver.lane_following_maneuver.lane_ids.pop_back();
591 }
592 }
593 else if (maneuver.type != carma_planning_msgs::msg::Maneuver::LANE_FOLLOWING){
594 // (1) Update starting maneuver lanelet if necessary and (2) Update ending maneuver lanelet if necessary
595
596 // Obtain the original starting lanelet from the maneuver
597 lanelet::Id original_starting_lanelet_id = std::stoi(getManeuverStartingLaneletId(maneuver));
598 lanelet::ConstLanelet original_starting_lanelet = wm_->getMap()->laneletLayer.get(original_starting_lanelet_id);
599
600 // Get the downtrack of the start of the lanelet
601 lanelet::BasicPoint2d original_starting_lanelet_centerline_start_point = lanelet::utils::to2D(original_starting_lanelet.centerline()).front();
602 double original_starting_lanelet_centerline_start_point_dt = wm_->routeTrackPos(original_starting_lanelet_centerline_start_point).downtrack;
603
604 if(adjusted_start_dist < original_starting_lanelet_centerline_start_point_dt){
605 auto previous_lanelets = wm_->getMapRoutingGraph()->previous(original_starting_lanelet, false);
606 if(!previous_lanelets.empty()){
607 auto llt_on_route_optional = wm_->getFirstLaneletOnShortestPath(previous_lanelets);
608 lanelet::ConstLanelet previous_lanelet_to_add;
609
610 if (llt_on_route_optional){
611 previous_lanelet_to_add = llt_on_route_optional.value();
612 }
613 else{
614 RCLCPP_WARN_STREAM(rclcpp::get_logger("plan_delegator"), "When adjusting non-lane follow maneuver, no previous lanelet found on the shortest path for lanelet "
615 << original_starting_lanelet.id() << ". Picking arbitrary lanelet: " << previous_lanelets[0].id() << ", instead");
616 previous_lanelet_to_add = previous_lanelets[0];
617 }
618 setManeuverStartingLaneletId(maneuver, previous_lanelet_to_add.id());
619 }
620 else{
621 RCLCPP_WARN_STREAM(rclcpp::get_logger("plan_delegator"), "No previous lanelet was found for lanelet " << original_starting_lanelet.id());
622 }
623 }
624
625 // Obtain the original ending lanelet from the maneuver
626 lanelet::Id original_ending_lanelet_id = std::stoi(getManeuverEndingLaneletId(maneuver));
627 lanelet::ConstLanelet original_ending_lanelet = wm_->getMap()->laneletLayer.get(original_ending_lanelet_id);
628
629 // Get the downtrack of the start of the ending lanelet
630 lanelet::BasicPoint2d original_ending_lanelet_centerline_start_point = lanelet::utils::to2D(original_ending_lanelet.centerline()).front();
631 double original_ending_lanelet_centerline_start_point_dt = wm_->routeTrackPos(original_ending_lanelet_centerline_start_point).downtrack;
632
633 if(adjusted_end_dist < original_ending_lanelet_centerline_start_point_dt){
634 auto previous_lanelets = wm_->getMapRoutingGraph()->previous(original_ending_lanelet, false);
635
636 if(!previous_lanelets.empty()){
637 setManeuverEndingLaneletId(maneuver, previous_lanelets[0].id());
638 }
639 else{
640 RCLCPP_WARN_STREAM(rclcpp::get_logger("plan_delegator"), "No previous lanelet was found for lanelet " << original_starting_lanelet.id());
641 }
642 }
643 }
644 }
645
646 carma_planning_msgs::msg::TrajectoryPlan PlanDelegator::planTrajectory()
647 {
648 carma_planning_msgs::msg::TrajectoryPlan latest_trajectory_plan;
649 bool full_plan_generation_failed = false;
651 {
652 RCLCPP_INFO_STREAM(rclcpp::get_logger("plan_delegator"),"Guidance is not engaged. Plan delegator will not plan trajectory.");
653 return latest_trajectory_plan;
654 }
655 // latest_maneuver_plan may get updated, so local copy to avoid race condition
656 auto locked_maneuver_plan = latest_maneuver_plan_;
657
658 // Flag for the first received trajectory plan service response
659 bool first_trajectory_plan = true;
660
661 // Track the index of the starting maneuver in the maneuver plan that this trajectory plan service request is for
662 uint16_t current_maneuver_index = 0;
663
664 // Loop through maneuver list to make service call to applicable Tactical Plugin
665 while(current_maneuver_index < locked_maneuver_plan.maneuvers.size())
666 {
667 auto& maneuver = locked_maneuver_plan.maneuvers[current_maneuver_index];
668
669 // ignore expired maneuvers
670 if(isManeuverExpired(maneuver, get_clock()->now()))
671 {
672 RCLCPP_INFO_STREAM(rclcpp::get_logger("plan_delegator"),"Dropping expired maneuver: " << GET_MANEUVER_PROPERTY(maneuver, parameters.maneuver_id));
673 // Update the maneuver plan index for the next loop
674 ++current_maneuver_index;
675 continue;
676 }
677 lanelet::BasicPoint2d current_loc(latest_pose_.pose.position.x, latest_pose_.pose.position.y);
678 double current_downtrack = wm_->routeTrackPos(current_loc).downtrack;
679 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"),"current_downtrack" << current_downtrack);
680 double maneuver_end_dist = GET_MANEUVER_PROPERTY(maneuver, end_dist);
681 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"),"maneuver_end_dist" << maneuver_end_dist);
682
683 // ignore maneuver that is passed.
684 if (current_downtrack > maneuver_end_dist)
685 {
686 RCLCPP_INFO_STREAM(rclcpp::get_logger("plan_delegator"),"Dropping passed maneuver: " << GET_MANEUVER_PROPERTY(maneuver, parameters.maneuver_id));
687 // Update the maneuver plan index for the next loop
688 ++current_maneuver_index;
689 continue;
690 }
691
692 // get corresponding ros service client for plan trajectory
693 auto maneuver_planner = GET_MANEUVER_PROPERTY(maneuver, parameters.planning_tactical_plugin);
694
695 auto client = getPlannerClientByName(maneuver_planner);
696
697 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"),"Current planner: " << maneuver_planner);
698
699 // compose service request
700 auto plan_req = composePlanTrajectoryRequest(
701 latest_trajectory_plan, locked_maneuver_plan, current_maneuver_index);
702
703 auto future_response = client->async_send_request(plan_req);
704
705 auto future_status = future_response.wait_for(std::chrono::milliseconds(config_.tactical_plugin_service_call_timeout));
706
707 if (future_status != std::future_status::ready)
708 {
709 RCLCPP_WARN_STREAM(rclcpp::get_logger("plan_delegator"),"Unsuccessful service call to trajectory planner:" << maneuver_planner << " for plan ID " << std::string(locked_maneuver_plan.maneuver_plan_id));
710 // if one service call fails, it should end plan immediately because it is there is no point to generate plan with empty space
711 full_plan_generation_failed = true;
712 break;
713 }
714
715 // If successful service request
716 auto plan_response = future_response.get();
717 // validate trajectory before add to the plan
718 if(!isTrajectoryValid(plan_response->trajectory_plan))
719 {
720 RCLCPP_WARN_STREAM(rclcpp::get_logger("plan_delegator"),
721 "Found invalid trajectory with less than 2 trajectory "
722 << "points for maneuver_plan_id: "
723 << std::string(locked_maneuver_plan.maneuver_plan_id));
724 full_plan_generation_failed = true;
725 break;
726 }
727 //Remove duplicate point from start of trajectory
728 if(latest_trajectory_plan.trajectory_points.size() != 0 &&
729 latest_trajectory_plan.trajectory_points.back().target_time == plan_response->trajectory_plan.trajectory_points.front().target_time)
730 {
731 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"),"Removing duplicate point for planner: " << maneuver_planner);
732 plan_response->trajectory_plan.trajectory_points.erase(plan_response->trajectory_plan.trajectory_points.begin());
733 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"),"plan_response->trajectory_plan size: " << plan_response->trajectory_plan.trajectory_points.size());
734 }
735 latest_trajectory_plan.trajectory_points.insert(latest_trajectory_plan.trajectory_points.end(),
736 plan_response->trajectory_plan.trajectory_points.begin(),
737 plan_response->trajectory_plan.trajectory_points.end());
738 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"),"new latest_trajectory_plan size: " << latest_trajectory_plan.trajectory_points.size());
739
740 // Assign the trajectory plan's initial longitudinal velocity based on the first tactical plugin's response
741 if(first_trajectory_plan == true)
742 {
743 latest_trajectory_plan.initial_longitudinal_velocity = plan_response->trajectory_plan.initial_longitudinal_velocity;
744 first_trajectory_plan = false;
745 }
746
747 if(isTrajectoryLongEnough(latest_trajectory_plan))
748 {
749 RCLCPP_INFO_STREAM(rclcpp::get_logger("plan_delegator"),"Plan Trajectory completed for " << std::string(locked_maneuver_plan.maneuver_plan_id));
750 break;
751 }
752
753 // Update the maneuver plan index based on the last maneuver index converted to a trajectory
754 // This is required since inlanecruising_plugin can plan a trajectory over contiguous LANE_FOLLOWING maneuvers
755 if(plan_response->related_maneuvers.size() > 0)
756 {
757 current_maneuver_index = plan_response->related_maneuvers.back() + 1;
758 }
759 }
760
761 if (full_plan_generation_failed)
762 {
763 RCLCPP_WARN_STREAM(rclcpp::get_logger("plan_delegator"),
764 "Plan_delegator's current run wasn't fully able to generate trajectory!");
765
766 carma_planning_msgs::msg::TrajectoryPlan empty_plan;
767 return empty_plan;
768 }
769
770 return latest_trajectory_plan;
771 }
772
774 {
775 // Guidance not engaged or haven't received a maneuver plan yet
777 {
778 return;
779 }
780 carma_planning_msgs::msg::TrajectoryPlan trajectory_plan = planTrajectory();
781
782 // Check if planned trajectory is valid before send out
783 if(isTrajectoryValid(trajectory_plan))
784 {
785 trajectory_plan.header.stamp = get_clock()->now();
786 last_successful_traj_ = trajectory_plan;
787 traj_pub_->publish(trajectory_plan);
789 }
790 else
791 {
793 RCLCPP_WARN_STREAM(rclcpp::get_logger("plan_delegator"),
794 "Guidance is engaged, but new planned trajectory has less than 2 points. " <<
795 "It will not be published! Consecutive failure count: "
797
798 // Case where traj generation fails after a successful one
799 if (last_successful_traj_.has_value()
802 {
803 RCLCPP_WARN_STREAM(rclcpp::get_logger("plan_delegator"),
804 "Instead, last available trajectory is published with outdated timestamp of:"
806 rclcpp::Time(last_successful_traj_.value().header.stamp).seconds()));
807 traj_pub_->publish(last_successful_traj_.value());
808 }
809 // Case where traj generation fails from the beginning.
810 // Attempt replanning for configured number of tries before throwing runtime error.
811 else if (!last_successful_traj_.has_value() &&
813 {
814 RCLCPP_WARN_STREAM(rclcpp::get_logger("plan_delegator"),
815 "Instead, tried publishing last available trajectory, but it's not available!");
816 }
817 else
818 {
819 RCLCPP_ERROR_STREAM(rclcpp::get_logger("plan_delegator"),
820 "No valid trajectory is available to publish! "
821 "Please check the planner plugins and their configurations.");
822 throw std::runtime_error("No valid trajectory is available to publish!");
823 }
824 }
825 }
826
828 {
829 tf2_listener_ = std::make_shared<tf2_ros::TransformListener>(*tf2_buffer_);
830 tf2_buffer_->setUsingDedicatedThread(true);
831 try
832 {
833 geometry_msgs::msg::TransformStamped tf = tf2_buffer_->lookupTransform("base_link", "vehicle_front", rclcpp::Time(0), rclcpp::Duration(20.0, 0)); //save to local copy of transform 20 sec timeout
834 length_to_front_bumper_ = tf.transform.translation.x;
835 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"),"length_to_front_bumper_: " << length_to_front_bumper_);
836
837 }
838 catch (const tf2::TransformException &ex)
839 {
840 RCLCPP_WARN_STREAM(rclcpp::get_logger("plan_delegator"), ex.what());
841 }
842 }
843
844} // namespace plan_delegator
845
846
847#include "rclcpp_components/register_node_macro.hpp"
848
849// Register the component with class_loader
850RCLCPP_COMPONENTS_REGISTER_NODE(plan_delegator::PlanDelegator)
#define GET_MANEUVER_PROPERTY(mvr, property)
Macro definition to enable easier access to fields shared across the maneuver types.
WorldModelConstPtr getWorldModel()
Returns a pointer to an intialized world model instance.
Definition: WMListener.cpp:184
void onTrajPlanTick()
Callback function for triggering trajectory planning.
carma_ros2_utils::SubPtr< carma_planning_msgs::msg::GuidanceState > guidance_state_sub_
carma_planning_msgs::msg::TrajectoryPlan planTrajectory()
Plan trajectory based on latest maneuver plan via ROS service call to plugins.
LaneChangeInformation getLaneChangeInformation(const carma_planning_msgs::msg::Maneuver &lane_change_maneuver)
Function for generating a LaneChangeInformation object from a provided lane change maneuver.
carma_ros2_utils::ClientPtr< carma_planning_msgs::srv::PlanTrajectory > getPlannerClientByName(const std::string &planner_name)
Get PlanTrajectory service client by plugin name and create new PlanTrajectory service client if spec...
void poseCallback(geometry_msgs::msg::PoseStamped::UniquePtr pose_msg)
Callback function for vehicle pose subscriber. Updates latest_pose_ and makes calls to publishUpcomin...
carma_wm::WorldModelConstPtr wm_
carma_ros2_utils::SubPtr< geometry_msgs::msg::TwistStamped > twist_sub_
std::shared_ptr< carma_planning_msgs::srv::PlanTrajectory::Request > composePlanTrajectoryRequest(const carma_planning_msgs::msg::TrajectoryPlan &latest_trajectory_plan, const carma_planning_msgs::msg::ManeuverPlan &locked_maneuver_plan, const uint16_t &current_maneuver_index) const
Generate new PlanTrajecory service request based on current planning progress.
carma_ros2_utils::CallbackReturn handle_on_activate(const rclcpp_lifecycle::State &)
boost::optional< LaneChangeInformation > upcoming_lane_change_information_
carma_ros2_utils::PubPtr< carma_planning_msgs::msg::UpcomingLaneChangeStatus > upcoming_lane_change_status_pub_
carma_planning_msgs::msg::UpcomingLaneChangeStatus upcoming_lane_change_status_
bool isTrajectoryValid(const carma_planning_msgs::msg::TrajectoryPlan &trajectory_plan) const noexcept
Example if a trajectory plan contains at least two trajectory points.
carma_ros2_utils::CallbackReturn handle_on_configure(const rclcpp_lifecycle::State &)
bool isManeuverExpired(const carma_planning_msgs::msg::Maneuver &maneuver, rclcpp::Time current_time) const
Example if a maneuver end time has passed current system time.
void guidanceStateCallback(carma_planning_msgs::msg::GuidanceState::UniquePtr plan)
Callback function of guidance state subscriber.
void updateManeuverParameters(carma_planning_msgs::msg::Maneuver &maneuver)
Update the starting downtrack, ending downtrack, and maneuver-specific Lanelet ID parameters associat...
rclcpp::TimerBase::SharedPtr traj_timer_
std::shared_ptr< tf2_ros::TransformListener > tf2_listener_
carma_ros2_utils::SubPtr< carma_planning_msgs::msg::ManeuverPlan > plan_sub_
bool isTrajectoryLongEnough(const carma_planning_msgs::msg::TrajectoryPlan &plan) const noexcept
Example if a trajectory plan is longer than configured time thresheld.
carma_planning_msgs::msg::ManeuverPlan latest_maneuver_plan_
carma_ros2_utils::PubPtr< carma_planning_msgs::msg::TrajectoryPlan > traj_pub_
carma_ros2_utils::PubPtr< autoware_msgs::msg::LampCmd > turn_signal_command_pub_
boost::optional< LaneChangeInformation > current_lane_change_information_
std::unordered_map< std::string, carma_ros2_utils::ClientPtr< carma_planning_msgs::srv::PlanTrajectory > > trajectory_planners_
bool isManeuverPlanValid(const carma_planning_msgs::msg::ManeuverPlan &maneuver_plan) const noexcept
Example if a maneuver plan contains at least one maneuver.
carma_ros2_utils::SubPtr< geometry_msgs::msg::PoseStamped > pose_sub_
geometry_msgs::msg::TwistStamped latest_twist_
std::optional< carma_planning_msgs::msg::TrajectoryPlan > last_successful_traj_
rclcpp::CallbackGroup::SharedPtr timer_callback_group_
void lookupFrontBumperTransform()
Lookup transfrom from front bumper to base link.
geometry_msgs::msg::PoseStamped latest_pose_
PlanDelegator(const rclcpp::NodeOptions &)
PlanDelegator constructor.
void maneuverPlanCallback(carma_planning_msgs::msg::ManeuverPlan::UniquePtr plan)
Callback function of maneuver plan subscriber.
std::shared_ptr< tf2_ros::Buffer > tf2_buffer_
autoware_msgs::msg::LampCmd latest_turn_signal_command_
void publishTurnSignalCommand(const boost::optional< LaneChangeInformation > &current_lane_change_information, const carma_planning_msgs::msg::UpcomingLaneChangeStatus &upcoming_lane_change_status)
Function for processing an optional LaneChangeInformation object pertaining to the currently-occurrin...
void publishUpcomingLaneChangeStatus(const boost::optional< LaneChangeInformation > &upcoming_lane_change_information)
Function for processing an optional LaneChangeInformation object pertaining to an upcoming lane chang...
auto to_string(const UtmZone &zone) -> std::string
Definition: utm_zone.cpp:21
void rpyFromQuaternion(const tf2::Quaternion &q, double &roll, double &pitch, double &yaw)
Extract extrinsic roll-pitch-yaw from quaternion.
Definition: Geometry.cpp:52
std::string getManeuverEndingLaneletId(carma_planning_msgs::msg::Maneuver mvr)
Anonymous function to get the ending lanelet id for all maneuver types except lane following....
void setManeuverEndingLaneletId(carma_planning_msgs::msg::Maneuver &mvr, lanelet::Id end_id)
Anonymous function to set the ending_lane_id for all maneuver types except lane following....
std::string getManeuverStartingLaneletId(carma_planning_msgs::msg::Maneuver mvr)
Anonymous function to get the starting lanelet id for all maneuver types except lane following....
void setManeuverStartingLaneletId(carma_planning_msgs::msg::Maneuver &mvr, lanelet::Id start_id)
Anonymous function to set the starting_lane_id for all maneuver types except lane following....
#define SET_MANEUVER_PROPERTY(mvr, property, value)
std::string planning_topic_suffix
std::string planning_topic_prefix
double duration_to_signal_before_lane_change
Convenience struct for storing information regarding a lane change maneuver.