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 config_.enable_object_avoidance = declare_parameter<bool>("enable_object_avoidance", config_.enable_object_avoidance);
149 }
150
151 carma_ros2_utils::CallbackReturn PlanDelegator::handle_on_configure(const rclcpp_lifecycle::State &)
152 {
153 // Reset config
154 config_ = Config();
155
156 get_parameter<std::string>("planning_topic_prefix", config_.planning_topic_prefix);
157 get_parameter<std::string>("planning_topic_suffix", config_.planning_topic_suffix);
158 get_parameter<double>("trajectory_planning_rate", config_.trajectory_planning_rate);
159 get_parameter<double>("trajectory_duration_threshold", config_.max_trajectory_duration);
160 get_parameter<double>("min_speed", config_.min_crawl_speed);
161 get_parameter<double>("duration_to_signal_before_lane_change", config_.duration_to_signal_before_lane_change);
162 get_parameter<int>("tactical_plugin_service_call_timeout", config_.tactical_plugin_service_call_timeout);
163 get_parameter<int>("max_traj_generation_reattempt", config_.max_traj_generation_reattempt);
164 get_parameter<bool>("enable_object_avoidance", config_.enable_object_avoidance);
165
166 RCLCPP_INFO_STREAM(rclcpp::get_logger("plan_delegator"),"Done loading parameters: " << config_);
167
168 // Setup publishers
169 traj_pub_ = create_publisher<carma_planning_msgs::msg::TrajectoryPlan>("plan_trajectory", 5);
170 upcoming_lane_change_status_pub_ = create_publisher<carma_planning_msgs::msg::UpcomingLaneChangeStatus>("upcoming_lane_change_status", 1);
171 turn_signal_command_pub_ = create_publisher<autoware_msgs::msg::LampCmd>("lamp_cmd", 1);
172
173 // Setup yield_plugin client; plan_delegator is the sole caller of yield_plugin, invoking it on the
174 // final trajectory before publishing rather than having every tactical plugin call it independently
175 yield_client_ = create_client<carma_planning_msgs::srv::PlanTrajectory>("plugins/yield_plugin/plan_trajectory");
176
177 // Setup subscribers
178 plan_sub_ = create_subscription<carma_planning_msgs::msg::ManeuverPlan>("final_maneuver_plan", 5, std::bind(&PlanDelegator::maneuverPlanCallback, this, std_ph::_1));
179 twist_sub_ = create_subscription<geometry_msgs::msg::TwistStamped>("current_velocity", 5,
180 [this](geometry_msgs::msg::TwistStamped::UniquePtr twist) {this->latest_twist_ = *twist;});
181 pose_sub_ = create_subscription<geometry_msgs::msg::PoseStamped>("current_pose", 5, std::bind(&PlanDelegator::poseCallback, this, std_ph::_1));
182 guidance_state_sub_ = create_subscription<carma_planning_msgs::msg::GuidanceState>("guidance_state", 5, std::bind(&PlanDelegator::guidanceStateCallback, this, std_ph::_1));
183
186 return CallbackReturn::SUCCESS;
187 }
188
189 carma_ros2_utils::CallbackReturn PlanDelegator::handle_on_activate(const rclcpp_lifecycle::State &)
190 {
191 timer_callback_group_ = create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
192 traj_timer_ = create_timer(get_clock(),
193 std::chrono::milliseconds((int)(1 / config_.trajectory_planning_rate * 1000)),
195 return CallbackReturn::SUCCESS;
196 }
197
198 void PlanDelegator::guidanceStateCallback(carma_planning_msgs::msg::GuidanceState::UniquePtr msg)
199 {
201 }
202
203 void PlanDelegator::maneuverPlanCallback(carma_planning_msgs::msg::ManeuverPlan::UniquePtr plan)
204 {
205 RCLCPP_INFO_STREAM(rclcpp::get_logger("plan_delegator"),"Received request to delegate plan ID " << std::string(plan->maneuver_plan_id));
206 // do basic check to see if the input is valid
207 auto copy_plan = *plan;
209 if (isManeuverPlanValid(copy_plan))
210 {
211 latest_maneuver_plan_ = copy_plan;
212 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"),"Received plan with " << latest_maneuver_plan_.maneuvers.size() << " maneuvers");
213
214 // Update the parameters associated with each maneuver
215 for (auto& maneuver : latest_maneuver_plan_.maneuvers) {
216 updateManeuverParameters(maneuver);
217 }
218 }
219 else {
220 RCLCPP_WARN_STREAM(rclcpp::get_logger("plan_delegator"),"Received empty plan, no maneuvers found in plan ID " << std::string(plan->maneuver_plan_id));
221 }
222
223 // Update upcoming_lane_change_information_ and current_lane_change_information_ based on the received maneuver plan
224 if(!latest_maneuver_plan_.maneuvers.empty()){
225 // Get ego vehicle's current downtrack
226 lanelet::BasicPoint2d current_loc(latest_pose_.pose.position.x, latest_pose_.pose.position.y);
227 double current_downtrack = wm_->routeTrackPos(current_loc).downtrack;
228
229 // Set upcoming_lane_change_information_ based on the first found lane change in the plan that begins after current_downtrack, if one exists
230 upcoming_lane_change_information_ = boost::optional<LaneChangeInformation>(); // Reset to empty optional
231 for(const auto& maneuver : latest_maneuver_plan_.maneuvers){
232 if(maneuver.type == carma_planning_msgs::msg::Maneuver::LANE_CHANGE){
233 if(current_downtrack >= maneuver.lane_change_maneuver.start_dist){
234 // Skip this lane change maneuver since ego vehicle has passed the lane change start point (this is not an 'upcoming' lane change)
235 continue;
236 }
237 else{
238 LaneChangeInformation upcoming_lane_change_information = getLaneChangeInformation(maneuver);
239 upcoming_lane_change_information_ = boost::optional<LaneChangeInformation>(upcoming_lane_change_information);
240 break;
241 }
242 }
243 }
244
245 // Set current_lane_change_information_ if the first maneuver is a lane change
246 current_lane_change_information_ = boost::optional<LaneChangeInformation>(); // Reset to empty optional
247 if(latest_maneuver_plan_.maneuvers[0].type == carma_planning_msgs::msg::Maneuver::LANE_CHANGE){
248 LaneChangeInformation current_lane_change_information = getLaneChangeInformation(latest_maneuver_plan_.maneuvers[0]);
249 current_lane_change_information_ = boost::optional<LaneChangeInformation>(current_lane_change_information);
250 }
251 }
252 }
253
254 void PlanDelegator::poseCallback(geometry_msgs::msg::PoseStamped::UniquePtr pose_msg)
255 {
256 latest_pose_ = *pose_msg;
257
258 // Publish the upcoming lane change status
260
261 // Publish the current turn signal command
263 }
264
265 LaneChangeInformation PlanDelegator::getLaneChangeInformation(const carma_planning_msgs::msg::Maneuver& lane_change_maneuver){
266 LaneChangeInformation lane_change_information;
267
268 lane_change_information.starting_downtrack = lane_change_maneuver.lane_change_maneuver.start_dist;
269
270 // Get the starting and ending lanelets for this lane change maneuver
271 lanelet::ConstLanelet starting_lanelet = wm_->getMap()->laneletLayer.get(std::stoi(lane_change_maneuver.lane_change_maneuver.starting_lane_id));
272 lanelet::ConstLanelet ending_lanelet = wm_->getMap()->laneletLayer.get(std::stoi(lane_change_maneuver.lane_change_maneuver.ending_lane_id));
273
274 // Determine if lane change is a left or right lane change and update lane_change_information accordingly.
275 // This function runs directly inside the final_maneuver_plan subscription callback (once per received
276 // plan), so it must never throw here: an uncaught exception in a subscription callback can crash this
277 // node outright. Previously it did throw whenever the walk from starting_lanelet to a shared boundary
278 // with ending_lanelet hit a lanelet with no routable successor -- which happens whenever an intervening
279 // lanelet is closed (e.g. by a TCM) or simply missing adjacency data in the map. Below, that same walk
280 // is attempted first since it is exact when it works, but any inability to complete it (no following
281 // lanelet, a routing loop) now falls through to a purely geometric left/right estimate instead of
282 // failing, so a lane change is always reported instead of crashing.
283 boost::optional<bool> is_right_lane_change;
284
285 if(starting_lanelet.leftBound() == ending_lanelet.rightBound()){
286 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()));
287 is_right_lane_change = false;
288 }
289 else if(starting_lanelet.rightBound() == ending_lanelet.leftBound()){
290 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()));
291 is_right_lane_change = true;
292 }
293 else
294 {
295 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()));
296 lanelet::ConstLanelet current_lanelet = starting_lanelet;
297 std::unordered_set<lanelet::Id> visited{current_lanelet.id()};
298
299 while(!is_right_lane_change){
300 // Assumption: Adjacent lanelets share lane boundary
301 auto following_lanelets = wm_->getMapRoutingGraph()->following(current_lanelet, false);
302 bool no_successor = following_lanelets.empty();
303 lanelet::ConstLanelet candidate_lanelet = no_successor ? current_lanelet : following_lanelets.front();
304 bool loop_detected = !no_successor && visited.count(candidate_lanelet.id()) > 0;
305
306 if(no_successor)
307 {
308 RCLCPP_WARN_STREAM(rclcpp::get_logger("plan_delegator"), "No following lanelets from lanelet " << current_lanelet.id()
309 << " reachable without a lane change (possibly closed or missing from the map); "
310 << "falling back to a geometric left/right estimate for lane change from "
311 << starting_lanelet.id() << " to " << ending_lanelet.id());
312 }
313
314 if (loop_detected)
315 {
316 RCLCPP_WARN_STREAM(rclcpp::get_logger("plan_delegator"), "Detected a routing loop while searching for a shared boundary between lanelet "
317 << starting_lanelet.id() << " and " << ending_lanelet.id() << "; falling back to a geometric left/right estimate");
318 }
319
320 if(no_successor || loop_detected)
321 {
322 break;
323 }
324
325 current_lanelet = candidate_lanelet;
326 visited.insert(current_lanelet.id());
327
328 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()));
329 if(current_lanelet.leftBound() == ending_lanelet.rightBound()){
330 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()));
331 is_right_lane_change = false;
332 }
333 else if(current_lanelet.rightBound() == ending_lanelet.leftBound()){
334 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()));
335 is_right_lane_change = true;
336 }
337 }
338 }
339
340 if(!is_right_lane_change)
341 {
342 // Could not confirm a shared boundary via routing (e.g. an intervening lanelet was closed or missing
343 // from the map). Fall back to pure geometry: which side of the starting lanelet's heading does the
344 // ending lanelet fall on? This only needs the two lanelets' own centerlines, so it works even when
345 // they are otherwise disconnected in the routing graph.
346 lanelet::BasicLineString2d starting_centerline = starting_lanelet.centerline2d().basicLineString();
347 lanelet::BasicPoint2d start_pt = starting_centerline.front();
348 lanelet::BasicPoint2d heading_vec = starting_centerline.back() - start_pt;
349 lanelet::BasicPoint2d to_target = ending_lanelet.centerline2d().basicLineString().front() - start_pt;
350 double cross = heading_vec.x() * to_target.y() - heading_vec.y() * to_target.x();
351
352 RCLCPP_WARN_STREAM(rclcpp::get_logger("plan_delegator"), "Could not find a shared lane boundary between starting lanelet "
353 << starting_lanelet.id() << " and ending lanelet " << ending_lanelet.id()
354 << "; estimating lane change direction geometrically instead.");
355
356 is_right_lane_change = (cross < 0.0);
357 }
358
359 lane_change_information.is_right_lane_change = is_right_lane_change.get();
360 return lane_change_information;
361 }
362
363 void PlanDelegator::publishUpcomingLaneChangeStatus(const boost::optional<LaneChangeInformation>& upcoming_lane_change_information){
364 // Initialize an UpcomingLaneChangeStatus message, which will be populated based on upcoming_lane_change_information
365 carma_planning_msgs::msg::UpcomingLaneChangeStatus upcoming_lane_change_status;
366
367 // Update upcoming_lane_change_status
368 if(upcoming_lane_change_information){
369 // Get the downtrack distance between the ego vehicle and the start of the upcoming lane change maneuver
370 lanelet::BasicPoint2d current_loc(latest_pose_.pose.position.x, latest_pose_.pose.position.y);
371 double current_downtrack = wm_->routeTrackPos(current_loc).downtrack;
372 upcoming_lane_change_status.downtrack_until_lanechange = std::max(0.0, upcoming_lane_change_information.get().starting_downtrack - current_downtrack);
373
374 // Set upcoming lane change status as a right lane change or left lane change
375 if(upcoming_lane_change_information.get().is_right_lane_change){
376 upcoming_lane_change_status.lane_change = carma_planning_msgs::msg::UpcomingLaneChangeStatus::RIGHT;
377 }
378 else{
379 upcoming_lane_change_status.lane_change = carma_planning_msgs::msg::UpcomingLaneChangeStatus::LEFT;
380 }
381 }
382 else{
383 upcoming_lane_change_status.lane_change = carma_planning_msgs::msg::UpcomingLaneChangeStatus::NONE;
384 }
385
386 // Publish upcoming_lane_change_status
387 upcoming_lane_change_status_pub_->publish(upcoming_lane_change_status);
388
389 // Store UpcomingLaneChangeStatus in upcoming_lane_change_status_
390 upcoming_lane_change_status_ = upcoming_lane_change_status;
391 }
392
393 void PlanDelegator::publishTurnSignalCommand(const boost::optional<LaneChangeInformation>& current_lane_change_information, const carma_planning_msgs::msg::UpcomingLaneChangeStatus& upcoming_lane_change_status)
394 {
395 // Initialize turn signal command message
396 // 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.
397 autoware_msgs::msg::LampCmd turn_signal_command;
398
399 // Publish turn signal command with priority placed on the current lane change, if one exists
400 if(current_lane_change_information){
401 // Publish turn signal command for the current lane change based on the lane change direction
402 if(current_lane_change_information.get().is_right_lane_change){
403 turn_signal_command.r = 1;
404 }
405 else{
406 turn_signal_command.l = 1;
407 }
408 turn_signal_command_pub_->publish(turn_signal_command);
409 }
410 else if(upcoming_lane_change_status.lane_change != carma_planning_msgs::msg::UpcomingLaneChangeStatus::NONE){
411 // 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
412 if((upcoming_lane_change_status.downtrack_until_lanechange / latest_twist_.twist.linear.x) <= config_.duration_to_signal_before_lane_change){
413 if(upcoming_lane_change_status.lane_change == carma_planning_msgs::msg::UpcomingLaneChangeStatus::RIGHT){
414 turn_signal_command.r = 1;
415 }
416 else{
417 turn_signal_command.l = 1;
418 }
419 turn_signal_command_pub_->publish(turn_signal_command);
420 }
421 }
422 else{
423 // Publish turn signal command with neither turn signal activated
424 turn_signal_command_pub_->publish(turn_signal_command);
425 }
426
427 // Store turn signal command in latest_turn_signal_command_
428 latest_turn_signal_command_ = turn_signal_command;
429 }
430
431 carma_ros2_utils::ClientPtr<carma_planning_msgs::srv::PlanTrajectory> PlanDelegator::getPlannerClientByName(const std::string& planner_name)
432 {
433 if(planner_name.size() == 0)
434 {
435 throw std::invalid_argument("Invalid trajectory planner name because it has zero length!");
436 }
437 if(trajectory_planners_.find(planner_name) == trajectory_planners_.end())
438 {
439 RCLCPP_INFO_STREAM(rclcpp::get_logger("plan_delegator"),"Discovered new trajectory planner: " << planner_name);
440
441 trajectory_planners_.emplace(
442 planner_name, create_client<carma_planning_msgs::srv::PlanTrajectory>(config_.planning_topic_prefix + planner_name + config_.planning_topic_suffix));
443 }
444 return trajectory_planners_[planner_name];
445 }
446
447 bool PlanDelegator::isManeuverPlanValid(const carma_planning_msgs::msg::ManeuverPlan& maneuver_plan) const noexcept
448 {
449 // currently it only checks if maneuver list is empty
450 return !maneuver_plan.maneuvers.empty();
451 }
452
453 bool PlanDelegator::isTrajectoryValid(const carma_planning_msgs::msg::TrajectoryPlan& trajectory_plan) const noexcept
454 {
455 // currently it only checks if trajectory contains less than 2 points
456 return !(trajectory_plan.trajectory_points.size() < 2);
457 }
458
459 bool PlanDelegator::isManeuverExpired(const carma_planning_msgs::msg::Maneuver& maneuver, rclcpp::Time current_time) const
460 {
461 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"), "maneuver start time:" << std::to_string(rclcpp::Time(GET_MANEUVER_PROPERTY(maneuver, start_time)).seconds()));
462 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"), "maneuver end time:" << std::to_string(rclcpp::Time(GET_MANEUVER_PROPERTY(maneuver, end_time)).seconds()));
463 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"), "current time:" << std::to_string(now().seconds()));
464 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
465 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"), "isexpired:" << isexpired);
466 // TODO: temporary disabling expiration check
467 return false;
468 }
469
470 std::shared_ptr<carma_planning_msgs::srv::PlanTrajectory::Request>
472 const carma_planning_msgs::msg::TrajectoryPlan& latest_trajectory_plan,
473 const carma_planning_msgs::msg::ManeuverPlan& locked_maneuver_plan,
474 const uint16_t& current_maneuver_index) const
475 {
476 auto plan_req = std::make_shared<carma_planning_msgs::srv::PlanTrajectory::Request>();
477 plan_req->maneuver_plan = locked_maneuver_plan;
478
479 // set current vehicle state if we have NOT planned any previous trajectories
480 if(latest_trajectory_plan.trajectory_points.empty())
481 {
482 plan_req->header.stamp = latest_pose_.header.stamp;
483 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"), "latest_pose_.header.stamp: " << std::to_string(rclcpp::Time(latest_pose_.header.stamp).seconds()));
484 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"), "plan_req->header.stamp: " << std::to_string(rclcpp::Time(plan_req->header.stamp).seconds()));
485
486 plan_req->vehicle_state.longitudinal_vel = latest_twist_.twist.linear.x;
487 plan_req->vehicle_state.x_pos_global = latest_pose_.pose.position.x;
488 plan_req->vehicle_state.y_pos_global = latest_pose_.pose.position.y;
489 double roll, pitch, yaw;
490 carma_wm::geometry::rpyFromQuaternion(latest_pose_.pose.orientation, roll, pitch, yaw);
491 plan_req->vehicle_state.orientation = yaw;
492 plan_req->maneuver_index_to_plan = current_maneuver_index;
493 }
494 // set vehicle state based on last two planned trajectory points
495 else
496 {
497 carma_planning_msgs::msg::TrajectoryPlanPoint last_point = latest_trajectory_plan.trajectory_points.back();
498 carma_planning_msgs::msg::TrajectoryPlanPoint second_last_point = *(latest_trajectory_plan.trajectory_points.rbegin() + 1);
499 plan_req->vehicle_state.x_pos_global = last_point.x;
500 plan_req->vehicle_state.y_pos_global = last_point.y;
501 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));
502 rclcpp::Duration time_diff = rclcpp::Time(last_point.target_time) - rclcpp::Time(second_last_point.target_time);
503 auto time_diff_sec = time_diff.seconds();
504 plan_req->maneuver_index_to_plan = current_maneuver_index;
505 // this assumes the vehicle does not have significant lateral velocity
506 plan_req->header.stamp = latest_trajectory_plan.trajectory_points.back().target_time;
507 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"), "plan_req->header.stamp: " << std::to_string(rclcpp::Time(plan_req->header.stamp).seconds()));
508
509 plan_req->vehicle_state.longitudinal_vel = distance_diff / time_diff_sec;
510 // TODO develop way to set yaw value for future points
511 }
512 return plan_req;
513 }
514
515 bool PlanDelegator::isTrajectoryLongEnough(const carma_planning_msgs::msg::TrajectoryPlan& plan) const noexcept
516 {
517 rclcpp::Duration time_diff = rclcpp::Time(plan.trajectory_points.back().target_time) - rclcpp::Time(plan.trajectory_points.front().target_time);
518 return time_diff.seconds() >= config_.max_trajectory_duration;
519 }
520
521 void PlanDelegator::updateManeuverParameters(carma_planning_msgs::msg::Maneuver& maneuver)
522 {
523 if (!wm_->getMap())
524 {
525 RCLCPP_ERROR_STREAM(rclcpp::get_logger("plan_delegator"), "Map is not set yet");
526 return;
527 }
528
529 // Update maneuver starting and ending downtrack distances
530 double original_start_dist = GET_MANEUVER_PROPERTY(maneuver, start_dist);
531 double original_end_dist = GET_MANEUVER_PROPERTY(maneuver, end_dist);
532 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"),"Changing maneuver distances for planner: " << GET_MANEUVER_PROPERTY(maneuver, parameters.planning_tactical_plugin));
533 double adjusted_start_dist = original_start_dist - length_to_front_bumper_;
534 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"),"original_start_dist:" << original_start_dist);
535 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"),"adjusted_start_dist:" << adjusted_start_dist);
536 double adjusted_end_dist = original_end_dist - length_to_front_bumper_;
537 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"),"original_end_dist:" << original_end_dist);
538 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"),"adjusted_end_dist:" << adjusted_end_dist);
539 SET_MANEUVER_PROPERTY(maneuver, start_dist, adjusted_start_dist);
540 SET_MANEUVER_PROPERTY(maneuver, end_dist, adjusted_end_dist);
541
542 // Shift maneuver starting and ending lanelets
543 // NOTE: Assumes that maneuver start and end downtrack distances have not been shifted by more than one lanelet
544 if(maneuver.type == carma_planning_msgs::msg::Maneuver::LANE_FOLLOWING && !maneuver.lane_following_maneuver.lane_ids.empty()){
545 // (1) Add new beginning lanelet to maneuver if necessary and (2) remove ending lanelet from maneuver if necessary
546
547 // Obtain the original starting lanelet from the maneuver
548 lanelet::Id original_starting_lanelet_id = std::stoi(maneuver.lane_following_maneuver.lane_ids.front());
549 lanelet::ConstLanelet original_starting_lanelet = wm_->getMap()->laneletLayer.get(original_starting_lanelet_id);
550
551 // Get the downtrack of the start of the original starting lanelet
552 lanelet::BasicPoint2d original_starting_lanelet_centerline_start_point = lanelet::utils::to2D(original_starting_lanelet.centerline()).front();
553 double original_starting_lanelet_centerline_start_point_dt = wm_->routeTrackPos(original_starting_lanelet_centerline_start_point).downtrack;
554
555 if(adjusted_start_dist < original_starting_lanelet_centerline_start_point_dt){
556
557 auto previous_lanelets = wm_->getMapRoutingGraph()->previous(original_starting_lanelet, false);
558
559 if(!previous_lanelets.empty()){
560
561 auto llt_on_route_optional = wm_->getFirstLaneletOnShortestPath(previous_lanelets);
562
563 lanelet::ConstLanelet previous_lanelet_to_add;
564
565 if (llt_on_route_optional){
566 previous_lanelet_to_add = llt_on_route_optional.value();
567 }
568 else{
569 RCLCPP_WARN_STREAM(rclcpp::get_logger("plan_delegator"), "When adjusting maneuver for lane follow, no previous lanelet found on the shortest path for lanelet "
570 << original_starting_lanelet.id() << ". Picking arbitrary lanelet: " << previous_lanelets[0].id() << ", instead");
571 previous_lanelet_to_add = previous_lanelets[0];
572 }
573
574 // lane_ids array is ordered by increasing downtrack, so this new starting lanelet is inserted at the front
575 maneuver.lane_following_maneuver.lane_ids.insert(maneuver.lane_following_maneuver.lane_ids.begin(), std::to_string(previous_lanelet_to_add.id()));
576
577 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"), "Inserted lanelet " << std::to_string(previous_lanelet_to_add.id()) << " to beginning of maneuver.");
578 }
579 else{
580 RCLCPP_WARN_STREAM(rclcpp::get_logger("plan_delegator"), "No previous lanelet was found for lanelet " << original_starting_lanelet.id());
581 }
582 }
583
584 // Obtain the maneuver ending lanelet
585 lanelet::Id original_ending_lanelet_id = std::stoi(maneuver.lane_following_maneuver.lane_ids.back());
586 lanelet::ConstLanelet original_ending_lanelet = wm_->getMap()->laneletLayer.get(original_ending_lanelet_id);
587
588 // Get the downtrack of the start of the maneuver ending lanelet
589 lanelet::BasicPoint2d original_ending_lanelet_centerline_start_point = lanelet::utils::to2D(original_ending_lanelet.centerline()).front();
590 double original_ending_lanelet_centerline_start_point_dt = wm_->routeTrackPos(original_ending_lanelet_centerline_start_point).downtrack;
591
592 if(adjusted_end_dist < original_ending_lanelet_centerline_start_point_dt){
593 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");
594
595 // lane_ids array is ordered by increasing downtrack, so the last element in the array corresponds to the original ending lanelet
596 maneuver.lane_following_maneuver.lane_ids.pop_back();
597 }
598 }
599 else if (maneuver.type != carma_planning_msgs::msg::Maneuver::LANE_FOLLOWING){
600 // (1) Update starting maneuver lanelet if necessary and (2) Update ending maneuver lanelet if necessary
601
602 // Obtain the original starting lanelet from the maneuver
603 lanelet::Id original_starting_lanelet_id = std::stoi(getManeuverStartingLaneletId(maneuver));
604 lanelet::ConstLanelet original_starting_lanelet = wm_->getMap()->laneletLayer.get(original_starting_lanelet_id);
605
606 // Get the downtrack of the start of the lanelet
607 lanelet::BasicPoint2d original_starting_lanelet_centerline_start_point = lanelet::utils::to2D(original_starting_lanelet.centerline()).front();
608 double original_starting_lanelet_centerline_start_point_dt = wm_->routeTrackPos(original_starting_lanelet_centerline_start_point).downtrack;
609
610 if(adjusted_start_dist < original_starting_lanelet_centerline_start_point_dt){
611 auto previous_lanelets = wm_->getMapRoutingGraph()->previous(original_starting_lanelet, false);
612 if(!previous_lanelets.empty()){
613 auto llt_on_route_optional = wm_->getFirstLaneletOnShortestPath(previous_lanelets);
614 lanelet::ConstLanelet previous_lanelet_to_add;
615
616 if (llt_on_route_optional){
617 previous_lanelet_to_add = llt_on_route_optional.value();
618 }
619 else{
620 RCLCPP_WARN_STREAM(rclcpp::get_logger("plan_delegator"), "When adjusting non-lane follow maneuver, no previous lanelet found on the shortest path for lanelet "
621 << original_starting_lanelet.id() << ". Picking arbitrary lanelet: " << previous_lanelets[0].id() << ", instead");
622 previous_lanelet_to_add = previous_lanelets[0];
623 }
624 setManeuverStartingLaneletId(maneuver, previous_lanelet_to_add.id());
625 }
626 else{
627 RCLCPP_WARN_STREAM(rclcpp::get_logger("plan_delegator"), "No previous lanelet was found for lanelet " << original_starting_lanelet.id());
628 }
629 }
630
631 // Obtain the original ending lanelet from the maneuver
632 lanelet::Id original_ending_lanelet_id = std::stoi(getManeuverEndingLaneletId(maneuver));
633 lanelet::ConstLanelet original_ending_lanelet = wm_->getMap()->laneletLayer.get(original_ending_lanelet_id);
634
635 // Get the downtrack of the start of the ending lanelet
636 lanelet::BasicPoint2d original_ending_lanelet_centerline_start_point = lanelet::utils::to2D(original_ending_lanelet.centerline()).front();
637 double original_ending_lanelet_centerline_start_point_dt = wm_->routeTrackPos(original_ending_lanelet_centerline_start_point).downtrack;
638
639 if(adjusted_end_dist < original_ending_lanelet_centerline_start_point_dt){
640 auto previous_lanelets = wm_->getMapRoutingGraph()->previous(original_ending_lanelet, false);
641
642 if(!previous_lanelets.empty()){
643 setManeuverEndingLaneletId(maneuver, previous_lanelets[0].id());
644 }
645 else{
646 RCLCPP_WARN_STREAM(rclcpp::get_logger("plan_delegator"), "No previous lanelet was found for lanelet " << original_starting_lanelet.id());
647 }
648 }
649 }
650 }
651
652 carma_planning_msgs::msg::TrajectoryPlan PlanDelegator::planTrajectory()
653 {
654 carma_planning_msgs::msg::TrajectoryPlan latest_trajectory_plan;
655 bool full_plan_generation_failed = false;
657 {
658 RCLCPP_INFO_STREAM(rclcpp::get_logger("plan_delegator"),"Guidance is not engaged. Plan delegator will not plan trajectory.");
659 return latest_trajectory_plan;
660 }
661 // latest_maneuver_plan may get updated, so local copy to avoid race condition
662 auto locked_maneuver_plan = latest_maneuver_plan_;
663
664 // Flag for the first received trajectory plan service response
665 bool first_trajectory_plan = true;
666
667 // Track the index of the starting maneuver in the maneuver plan that this trajectory plan service request is for
668 uint16_t current_maneuver_index = 0;
669
670 // Loop through maneuver list to make service call to applicable Tactical Plugin
671 while(current_maneuver_index < locked_maneuver_plan.maneuvers.size())
672 {
673 auto& maneuver = locked_maneuver_plan.maneuvers[current_maneuver_index];
674
675 // ignore expired maneuvers
676 if(isManeuverExpired(maneuver, get_clock()->now()))
677 {
678 RCLCPP_INFO_STREAM(rclcpp::get_logger("plan_delegator"),"Dropping expired maneuver: " << GET_MANEUVER_PROPERTY(maneuver, parameters.maneuver_id));
679 // Update the maneuver plan index for the next loop
680 ++current_maneuver_index;
681 continue;
682 }
683 lanelet::BasicPoint2d current_loc(latest_pose_.pose.position.x, latest_pose_.pose.position.y);
684 double current_downtrack = wm_->routeTrackPos(current_loc).downtrack;
685 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"),"current_downtrack" << current_downtrack);
686 double maneuver_end_dist = GET_MANEUVER_PROPERTY(maneuver, end_dist);
687 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"),"maneuver_end_dist" << maneuver_end_dist);
688
689 // ignore maneuver that is passed.
690 if (current_downtrack > maneuver_end_dist)
691 {
692 RCLCPP_INFO_STREAM(rclcpp::get_logger("plan_delegator"),"Dropping passed maneuver: " << GET_MANEUVER_PROPERTY(maneuver, parameters.maneuver_id));
693 // Update the maneuver plan index for the next loop
694 ++current_maneuver_index;
695 continue;
696 }
697
698 // get corresponding ros service client for plan trajectory
699 auto maneuver_planner = GET_MANEUVER_PROPERTY(maneuver, parameters.planning_tactical_plugin);
700
701 auto client = getPlannerClientByName(maneuver_planner);
702
703 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"),"Current planner: " << maneuver_planner);
704
705 // compose service request
706 auto plan_req = composePlanTrajectoryRequest(
707 latest_trajectory_plan, locked_maneuver_plan, current_maneuver_index);
708
709 auto future_response = client->async_send_request(plan_req);
710
711 auto future_status = future_response.wait_for(std::chrono::milliseconds(config_.tactical_plugin_service_call_timeout));
712
713 if (future_status != std::future_status::ready)
714 {
715 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));
716 // if one service call fails, it should end plan immediately because it is there is no point to generate plan with empty space
717 full_plan_generation_failed = true;
718 break;
719 }
720
721 // If successful service request
722 auto plan_response = future_response.get();
723 // validate trajectory before add to the plan
724 if(!isTrajectoryValid(plan_response->trajectory_plan))
725 {
726 RCLCPP_WARN_STREAM(rclcpp::get_logger("plan_delegator"),
727 "Found invalid trajectory with less than 2 trajectory "
728 << "points for maneuver_plan_id: "
729 << std::string(locked_maneuver_plan.maneuver_plan_id));
730 full_plan_generation_failed = true;
731 break;
732 }
733 //Remove duplicate point from start of trajectory
734 if(latest_trajectory_plan.trajectory_points.size() != 0 &&
735 latest_trajectory_plan.trajectory_points.back().target_time == plan_response->trajectory_plan.trajectory_points.front().target_time)
736 {
737 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"),"Removing duplicate point for planner: " << maneuver_planner);
738 plan_response->trajectory_plan.trajectory_points.erase(plan_response->trajectory_plan.trajectory_points.begin());
739 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"),"plan_response->trajectory_plan size: " << plan_response->trajectory_plan.trajectory_points.size());
740 }
741 latest_trajectory_plan.trajectory_points.insert(latest_trajectory_plan.trajectory_points.end(),
742 plan_response->trajectory_plan.trajectory_points.begin(),
743 plan_response->trajectory_plan.trajectory_points.end());
744 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"),"new latest_trajectory_plan size: " << latest_trajectory_plan.trajectory_points.size());
745
746 // Assign the trajectory plan's initial longitudinal velocity based on the first tactical plugin's response
747 if(first_trajectory_plan == true)
748 {
749 latest_trajectory_plan.initial_longitudinal_velocity = plan_response->trajectory_plan.initial_longitudinal_velocity;
750 first_trajectory_plan = false;
751 }
752
753 if(isTrajectoryLongEnough(latest_trajectory_plan))
754 {
755 RCLCPP_INFO_STREAM(rclcpp::get_logger("plan_delegator"),"Plan Trajectory completed for " << std::string(locked_maneuver_plan.maneuver_plan_id));
756 break;
757 }
758
759 // Update the maneuver plan index based on the last maneuver index converted to a trajectory
760 // This is required since inlanecruising_plugin can plan a trajectory over contiguous LANE_FOLLOWING maneuvers
761 if(plan_response->related_maneuvers.size() > 0)
762 {
763 current_maneuver_index = plan_response->related_maneuvers.back() + 1;
764 }
765 }
766
767 if (full_plan_generation_failed)
768 {
769 RCLCPP_WARN_STREAM(rclcpp::get_logger("plan_delegator"),
770 "Plan_delegator's current run wasn't fully able to generate trajectory!");
771
772 carma_planning_msgs::msg::TrajectoryPlan empty_plan;
773 return empty_plan;
774 }
775
776 return latest_trajectory_plan;
777 }
778
780 {
781 // Guidance not engaged or haven't received a maneuver plan yet
783 {
784 return;
785 }
786 carma_planning_msgs::msg::TrajectoryPlan trajectory_plan = planTrajectory();
787
788 // Aside from the flag, yield_plugin should not be called on invalid trajectories
789 if (config_.enable_object_avoidance && isTrajectoryValid(trajectory_plan))
790 {
791 auto yield_req = std::make_shared<carma_planning_msgs::srv::PlanTrajectory::Request>();
792 yield_req->vehicle_state.longitudinal_vel = latest_twist_.twist.linear.x;
793 yield_req->vehicle_state.x_pos_global = latest_pose_.pose.position.x;
794 yield_req->vehicle_state.y_pos_global = latest_pose_.pose.position.y;
795 double roll, pitch, yaw;
796 carma_wm::geometry::rpyFromQuaternion(latest_pose_.pose.orientation, roll, pitch, yaw);
797 yield_req->vehicle_state.orientation = yaw;
798
799 auto yield_resp = std::make_shared<carma_planning_msgs::srv::PlanTrajectory::Response>();
800 yield_resp->trajectory_plan = trajectory_plan;
801
803 shared_from_this(), yield_req, yield_resp, yield_client_, config_.tactical_plugin_service_call_timeout);
804
805 trajectory_plan = yield_resp->trajectory_plan;
806 }
807 else
808 {
809 RCLCPP_DEBUG(rclcpp::get_logger("plan_delegator"), "Ignored Object Avoidance");
810 }
811
812 // Check if planned trajectory is valid before send out
813 if(isTrajectoryValid(trajectory_plan))
814 {
815 trajectory_plan.header.stamp = get_clock()->now();
816 last_successful_traj_ = trajectory_plan;
817 traj_pub_->publish(trajectory_plan);
819 }
820 else
821 {
823 RCLCPP_WARN_STREAM(rclcpp::get_logger("plan_delegator"),
824 "Guidance is engaged, but new planned trajectory has less than 2 points. " <<
825 "It will not be published! Consecutive failure count: "
827
828 // Case where traj generation fails after a successful one
829 if (last_successful_traj_.has_value()
832 {
833 RCLCPP_WARN_STREAM(rclcpp::get_logger("plan_delegator"),
834 "Instead, last available trajectory is published with outdated timestamp of:"
836 rclcpp::Time(last_successful_traj_.value().header.stamp).seconds()));
837 traj_pub_->publish(last_successful_traj_.value());
838 }
839 // Case where traj generation fails from the beginning.
840 // Attempt replanning for configured number of tries before throwing runtime error.
841 else if (!last_successful_traj_.has_value() &&
843 {
844 RCLCPP_WARN_STREAM(rclcpp::get_logger("plan_delegator"),
845 "Instead, tried publishing last available trajectory, but it's not available!");
846 }
847 else
848 {
849 RCLCPP_ERROR_STREAM(rclcpp::get_logger("plan_delegator"),
850 "No valid trajectory is available to publish! "
851 "Please check the planner plugins and their configurations.");
852 throw std::runtime_error("No valid trajectory is available to publish!");
853 }
854 }
855 }
856
858 {
859 tf2_listener_ = std::make_shared<tf2_ros::TransformListener>(*tf2_buffer_);
860 tf2_buffer_->setUsingDedicatedThread(true);
861 try
862 {
863 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
864 length_to_front_bumper_ = tf.transform.translation.x;
865 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("plan_delegator"),"length_to_front_bumper_: " << length_to_front_bumper_);
866
867 }
868 catch (const tf2::TransformException &ex)
869 {
870 RCLCPP_WARN_STREAM(rclcpp::get_logger("plan_delegator"), ex.what());
871 }
872 }
873
874} // namespace plan_delegator
875
876
877#include "rclcpp_components/register_node_macro.hpp"
878
879// Register the component with class_loader
880RCLCPP_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 > yield_client_
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...
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.
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
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.