17#include <rclcpp/rclcpp.hpp>
22#include <boost/uuid/uuid_generators.hpp>
23#include <boost/uuid/uuid_io.hpp>
24#include <lanelet2_core/geometry/Point.h>
25#include <trajectory_utils/trajectory_utils.hpp>
26#include <trajectory_utils/conversions/conversions.hpp>
28#include <carma_ros2_utils/carma_lifecycle_node.hpp>
30#include <Eigen/Geometry>
34#include <carma_v2x_msgs/msg/location_ecef.hpp>
35#include <carma_v2x_msgs/msg/trajectory.hpp>
36#include <carma_v2x_msgs/msg/plan_type.hpp>
40#include <unordered_set>
42#include <yield_plugin/yield_plugin_cuda.cuh>
44using oss = std::ostringstream;
52 : nh_(nh), wm_(wm), config_(config),mobility_response_publisher_(mobility_response_publisher), lc_status_publisher_(lc_status_publisher)
59 return rclcpp::Time(trajectory.trajectory_points.back().target_time).seconds();
64 return rclcpp::Time(trajectory.trajectory_points.front().target_time).seconds();
74 return (rclcpp::Time(trajectory.back().header.stamp) - rclcpp::Time(trajectory.front().header.stamp)).seconds();
79 std::vector<std::pair<int, lanelet::BasicPoint2d>> intersection_points;
80 boost::geometry::model::linestring<lanelet::BasicPoint2d> self_traj;
81 for (
auto tpp:self_trajectory)
83 boost::geometry::append(self_traj, tpp);
86 for (
size_t i=0;
i<incoming_trajectory.size();
i++)
88 double res = boost::geometry::distance(incoming_trajectory.at(
i), self_traj);
92 intersection_points.push_back(std::make_pair(
i, incoming_trajectory.at(
i)));
95 return intersection_points;
100 carma_planning_msgs::msg::TrajectoryPlan trajectory_plan;
101 std::vector<lanelet::BasicPoint2d> map_points;
106 auto curr_point = ecef_trajectory.location;
108 for (
size_t i = 0;
i<ecef_trajectory.offsets.size();
i++)
110 lanelet::BasicPoint2d offset_point;
111 curr_point.ecef_x += ecef_trajectory.offsets.at(
i).offset_x;
112 curr_point.ecef_y += ecef_trajectory.offsets.at(
i).offset_y;
113 curr_point.ecef_z += ecef_trajectory.offsets.at(
i).offset_z;
117 map_points.push_back(offset_point);
127 throw std::invalid_argument(
"No map projector available for ecef conversion");
130 lanelet::BasicPoint3d map_point =
map_projector_->projectECEF( {
static_cast<double>(ecef_point.ecef_x)/100.0,
static_cast<double>(ecef_point.ecef_y)/100.0,
static_cast<double>(ecef_point.ecef_z)/100.0 } , 1);
132 return lanelet::traits::to2D(map_point);
139 carma_v2x_msgs::msg::MobilityResponse out_mobility_response;
141 out_mobility_response.m_header.recipient_id = resp_recipient_id;
142 out_mobility_response.m_header.sender_bsm_id =
host_bsm_id_;
143 out_mobility_response.m_header.plan_id = req_plan_id;
144 out_mobility_response.m_header.timestamp =
nh_->now().seconds()*1000;
149 out_mobility_response.is_accepted =
true;
151 else out_mobility_response.is_accepted =
false;
153 return out_mobility_response;
159 carma_v2x_msgs::msg::MobilityRequest incoming_request = *msg;
160 carma_planning_msgs::msg::LaneChangeStatus lc_status_msg;
161 if (incoming_request.strategy ==
"carma/cooperative-lane-change")
164 RCLCPP_ERROR(
nh_->get_logger(),
"Cannot process mobility request as map projection is not yet set!");
167 if (incoming_request.plan_type.type == carma_v2x_msgs::msg::PlanType::CHANGE_LANE_LEFT || incoming_request.plan_type.type == carma_v2x_msgs::msg::PlanType::CHANGE_LANE_RIGHT)
169 RCLCPP_DEBUG(
nh_->get_logger(),
"Cooperative Lane Change Request Received");
170 lc_status_msg.status = carma_planning_msgs::msg::LaneChangeStatus::REQUEST_RECEIVED;
171 lc_status_msg.description =
"Received lane merge request";
175 RCLCPP_DEBUG(
nh_->get_logger(),
"CLC Request correctly received");
179 std::string req_sender_id = incoming_request.m_header.sender_id;
180 std::string req_plan_id = incoming_request.m_header.plan_id;
182 carma_v2x_msgs::msg::LocationECEF ecef_location = incoming_request.location;
183 carma_v2x_msgs::msg::Trajectory incoming_trajectory = incoming_request.trajectory;
184 std::string req_strategy_params = incoming_request.strategy_params;
186 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
"received urgency: " <<
clc_urgency_);
189 using boost::property_tree::ptree;
191 std::istringstream strstream(req_strategy_params);
192 boost::property_tree::json_parser::read_json(strstream, pt);
193 int req_traj_speed_full = pt.get<
int>(
"s");
194 int req_traj_fractional = pt.get<
int>(
"f");
195 int start_lanelet_id = pt.get<
int>(
"sl");
196 int end_lanelet_id = pt.get<
int>(
"el");
197 double req_traj_speed =
static_cast<double>(req_traj_speed_full) +
static_cast<double>(req_traj_fractional)/10.0;
198 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
"req_traj_speed" << req_traj_speed);
199 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
"start_lanelet_id" << start_lanelet_id);
200 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
"end_lanelet_id" << end_lanelet_id);
202 std::vector<lanelet::BasicPoint2d> req_traj_plan = {};
206 double req_expiration_sec =
static_cast<double>(incoming_request.expiration);
207 double current_time_sec =
nh_->now().seconds();
209 bool response_to_clc_req =
false;
211 double req_plan_time = req_expiration_sec - current_time_sec;
212 double req_timestamp =
static_cast<double>(incoming_request.m_header.timestamp) / 1000.0 - current_time_sec;
219 lc_status_msg.status = carma_planning_msgs::msg::LaneChangeStatus::REQUEST_ACCEPTED;
220 lc_status_msg.description =
"Accepted lane merge request";
221 response_to_clc_req =
true;
222 RCLCPP_DEBUG(
nh_->get_logger(),
"CLC accepted");
226 lc_status_msg.status = carma_planning_msgs::msg::LaneChangeStatus::REQUEST_REJECTED;
227 lc_status_msg.description =
"Rejected lane merge request";
228 response_to_clc_req =
false;
229 RCLCPP_DEBUG(
nh_->get_logger(),
"CLC rejected");
231 carma_v2x_msgs::msg::MobilityResponse outgoing_response =
compose_mobility_response(req_sender_id, req_plan_id, response_to_clc_req);
233 lc_status_msg.status = carma_planning_msgs::msg::LaneChangeStatus::RESPONSE_SENT;
234 RCLCPP_DEBUG(
nh_->get_logger(),
"response sent");
252 carma_v2x_msgs::msg::BSMCoreData bsm_core_ = msg->core_data;
257 carma_planning_msgs::srv::PlanTrajectory::Request::SharedPtr req,
258 carma_planning_msgs::srv::PlanTrajectory::Response::SharedPtr resp)
260 RCLCPP_DEBUG(
nh_->get_logger(),
"Yield_plugin was called!");
261 if (req->initial_trajectory_plan.trajectory_points.size() < 2){
262 throw std::invalid_argument(
"Empty Trajectory received by Yield");
264 rclcpp::Clock system_clock(RCL_SYSTEM_TIME);
265 rclcpp::Time start_time = system_clock.now();
267 carma_planning_msgs::msg::TrajectoryPlan original_trajectory = req->initial_trajectory_plan;
268 carma_planning_msgs::msg::TrajectoryPlan yield_trajectory;
271 if (req->vehicle_state.longitudinal_vel >
EPSILON &&
274 RCLCPP_DEBUG(
nh_->get_logger(),
"Using last committed trajectory to stopping");
275 lanelet::BasicPoint2d veh_pos(req->vehicle_state.x_pos_global,
276 req->vehicle_state.y_pos_global);
280 size_t idx_to_start_new_traj =
282 updated_trajectory.trajectory_points,
286 if (!updated_trajectory.trajectory_points.empty()) {
287 updated_trajectory.trajectory_points =
288 std::vector<carma_planning_msgs::msg::TrajectoryPlanPoint>
289 (updated_trajectory.trajectory_points.begin() + idx_to_start_new_traj,
290 updated_trajectory.trajectory_points.end());
295 rclcpp::Time end_time = system_clock.now();
297 auto duration = end_time - start_time;
298 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(
"yield_plugin"),
310 double initial_velocity = req->vehicle_state.longitudinal_vel;
313 if (initial_velocity <
EPSILON)
315 initial_velocity = original_trajectory.initial_longitudinal_velocity;
321 RCLCPP_DEBUG(
nh_->get_logger(),
"First time stopped to prevent collision: %f",
329 RCLCPP_DEBUG(
nh_->get_logger(),
"Only consider high urgency clc");
332 RCLCPP_DEBUG(
nh_->get_logger(),
"Yield for CLC. We haven't received an updated negotiation this timestep");
338 RCLCPP_DEBUG(
nh_->get_logger(),
"unreliable CLC communication, switching to object avoidance");
344 RCLCPP_DEBUG(
nh_->get_logger(),
"Yield for object avoidance");
345 auto _t0_upd = std::chrono::steady_clock::now();
347 const double _upd_ms = std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - _t0_upd).count();
348 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(
"yield_plugin"),
349 "[timing] update_traj_for_object: " << _upd_ms <<
" ms");
357 resp->trajectory_plan = original_trajectory;
365 yield_trajectory.header.frame_id =
"map";
366 yield_trajectory.header.stamp =
nh_->now();
367 yield_trajectory.trajectory_id = original_trajectory.trajectory_id;
368 resp->trajectory_plan = yield_trajectory;
371 catch(
const std::runtime_error& e) {
372 RCLCPP_WARN_STREAM(
nh_->get_logger(),
"Yield Plugin failed to plan trajectory due to known negative time issue: " << e.what());
373 RCLCPP_WARN_STREAM(
nh_->get_logger(),
"Returning the original trajectory, and retrying at the next call.");
374 resp->trajectory_plan = original_trajectory;
377 rclcpp::Time end_time = system_clock.now();
379 auto duration = end_time - start_time;
380 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(
"yield_plugin"),
386 carma_planning_msgs::msg::TrajectoryPlan cooperative_trajectory;
388 double initial_pos = 0;
390 double initial_velocity = current_speed;
394 std::vector<lanelet::BasicPoint2d> host_traj_points = {};
395 for (
size_t i=0;
i<original_tp.trajectory_points.size();
i++)
397 lanelet::BasicPoint2d traj_point;
398 traj_point.x() = original_tp.trajectory_points.at(
i).x;
399 traj_point.y() = original_tp.trajectory_points.at(
i).y;
400 host_traj_points.push_back(traj_point);
404 if (!intersection_points.empty())
406 lanelet::BasicPoint2d intersection_point = intersection_points[0].second;
407 double dx = original_tp.trajectory_points[0].x - intersection_point.x();
408 double dy = original_tp.trajectory_points[0].y - intersection_point.y();
411 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
"digital_gap: " << digital_gap);
413 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
"Goal position (goal_pos): " << goal_pos);
416 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
"Collision time: " << collision_time);
417 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
"intersection num: " << intersection_points[0].first);
418 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
"Planning time: " << planning_time);
423 double incoming_trajectory_speed = sqrt(dx2*dx2 + dy2*dy2)/(intersection_points[0].first *
ecef_traj_timestep_);
425 goal_velocity = std::min(goal_velocity, incoming_trajectory_speed);
428 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
"goal_velocity: " << goal_velocity);
429 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
"incoming_trajectory_speed: " << incoming_trajectory_speed);
431 if (planning_time > min_time)
435 cooperative_trajectory =
generate_JMT_trajectory(original_tp, initial_pos, goal_pos, initial_velocity, goal_velocity, planning_time, original_max_speed);
440 RCLCPP_DEBUG(
nh_->get_logger(),
"The incoming requested trajectory is rejected, due to insufficient gap");
441 cooperative_trajectory = original_tp;
448 RCLCPP_DEBUG(
nh_->get_logger(),
"The incoming requested trajectory does not overlap with host vehicle's trajectory");
449 cooperative_trajectory = original_tp;
452 return cooperative_trajectory;
457 double smallest_time_step = std::numeric_limits<double>::infinity();
458 for (
size_t i = 0;
i < original_tp.trajectory_points.size() - 1;
i ++)
460 smallest_time_step = std::min(smallest_time_step,
461 (rclcpp::Time(original_tp.trajectory_points.at(
i + 1).target_time)
462 - rclcpp::Time(original_tp.trajectory_points.at(
i).target_time)).seconds());
464 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(
"yield_plugin"),
"smallest_time_step: " << smallest_time_step);
466 return smallest_time_step;
470 double initial_velocity,
double goal_velocity,
double planning_time,
double original_max_speed)
472 carma_planning_msgs::msg::TrajectoryPlan jmt_trajectory;
473 std::vector<carma_planning_msgs::msg::TrajectoryPlanPoint> jmt_trajectory_points;
474 jmt_trajectory_points.push_back(original_tp.trajectory_points[0]);
477 std::vector<double> calculated_speeds = {};
478 std::vector<double> new_relative_downtracks = {};
479 new_relative_downtracks.push_back(0.0);
480 calculated_speeds.push_back(initial_velocity);
481 double new_traj_accumulated_downtrack = 0.0;
482 double original_traj_accumulated_downtrack = original_traj_relative_downtracks.at(1);
486 const double initial_time = 0;
487 double initial_accel = 0;
490 initial_accel = (initial_velocity -
last_speed_.value()) /
493 if (!std::isnormal(initial_accel))
495 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(
"yield_plugin"),
"Detecting nan initial_accel set to 0");
499 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(
"yield_plugin"),
"Detecting initial_accel: " << initial_accel
500 <<
", initial_velocity:" << initial_velocity
502 <<
", nh_->now(): " <<
nh_->now().seconds()
506 const double goal_accel = 0;
507 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(
"yield_plugin"),
"Following parameters used for JMT: "
508 "\ninitial_pos: " << initial_pos <<
509 "\ngoal_pos: " << goal_pos <<
510 "\ninitial_velocity: " << initial_velocity <<
511 "\ngoal_velocity: " << goal_velocity <<
512 "\ninitial_accel: " << initial_accel <<
513 "\ngoal_accel: " << goal_accel <<
514 "\nplanning_time: " << planning_time <<
515 "\noriginal_max_speed: " << original_max_speed);
518 std::vector<double> polynomial_coefficients = quintic_coefficient_calculator::quintic_coefficient_calculator(initial_pos,
526 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(
"yield_plugin"),
"Used original_max_speed: " << original_max_speed);
527 for (
size_t i = 0;
i < polynomial_coefficients.size();
i++) {
528 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(
"yield_plugin"),
"Coefficient " <<
i <<
": " << polynomial_coefficients[
i]);
533 int new_traj_idx = 1;
534 int original_traj_idx = 1;
535 while (new_traj_accumulated_downtrack < goal_pos -
EPSILON && original_traj_idx < original_traj_relative_downtracks.size())
537 const double target_time = new_traj_idx * smallest_time_step;
538 const double downtrack_at_target_time =
polynomial_calc(polynomial_coefficients, target_time);
539 double velocity_at_target_time =
polynomial_calc_d(polynomial_coefficients, target_time);
543 if (velocity_at_target_time < 0.0)
549 velocity_at_target_time = std::clamp(velocity_at_target_time, 0.0, original_max_speed);
552 if (downtrack_at_target_time >= original_traj_accumulated_downtrack)
556 calculated_speeds.push_back(velocity_at_target_time);
557 original_traj_accumulated_downtrack += original_traj_relative_downtracks.at(original_traj_idx);
558 original_traj_idx ++;
560 new_traj_accumulated_downtrack = downtrack_at_target_time;
567 std::fill_n(std::back_inserter(calculated_speeds),
568 std::size(original_traj_relative_downtracks) - std::size(calculated_speeds),
574 double prev_speed = filtered_speeds.at(0);
578 for(
size_t i = 1;
i < original_tp.trajectory_points.size();
i++)
580 carma_planning_msgs::msg::TrajectoryPlanPoint jmt_tpp = original_tp.trajectory_points.at(
i);
584 double current_speed = goal_velocity;
586 if (
i < filtered_speeds.size())
588 current_speed = filtered_speeds.at(
i);
599 const double dt = (2 * original_traj_relative_downtracks.at(
i)) / (current_speed + prev_speed);
600 jmt_tpp.target_time = rclcpp::Time(jmt_trajectory_points.back().target_time) + rclcpp::Duration::from_nanoseconds(dt*1e9);
609 jmt_tpp.target_time = rclcpp::Time(jmt_trajectory_points.back().target_time) + rclcpp::Duration::from_nanoseconds(6000 * 1e9);
613 jmt_trajectory_points.push_back(jmt_tpp);
614 prev_speed = current_speed;
617 jmt_trajectory.header = original_tp.header;
618 jmt_trajectory.trajectory_id = original_tp.trajectory_id;
619 jmt_trajectory.trajectory_points = jmt_trajectory_points;
620 jmt_trajectory.initial_longitudinal_velocity = initial_velocity;
621 return jmt_trajectory;
625 const std::vector<carma_perception_msgs::msg::PredictedState>& object_predictions,
double collision_radius,
double ego_max_speed)
629 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
"Starting a new collision detection, trajectory size: "
630 << ego_trajectory.trajectory_points.size() <<
". prediction size: " << object_predictions.size());
633 bool on_route =
false;
634 int on_route_idx = 0;
637 const auto object_speed{std::hypot(object_predictions.front().predicted_velocity.linear.x,
638 object_predictions.front().predicted_velocity.linear.y)};
641 if (object_predictions.size() < 2)
643 throw std::invalid_argument(
"Object on ther road doesn't have enough predicted states! Please check motion_computation is correctly applying predicted states");
645 const double object_prediction_step_duration = (rclcpp::Time(object_predictions.at(1).header.stamp) - rclcpp::Time(object_predictions.front().header.stamp)).seconds();
648 if (object_prediction_step_duration < 0.0)
650 throw std::invalid_argument(
"Predicted states of the object is malformed. Detected trajectory going backwards in time!");
660 int iteration_stride = std::max(1,
static_cast<int>(iteration_stride_max_time_s / object_prediction_step_duration));
662 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
"Determined iteration_stride: " << iteration_stride
663 <<
", with object_speed: " << object_speed
664 <<
", with ego_max_speed: " << ego_max_speed
665 <<
", with object_prediction_step_duration: " << object_prediction_step_duration
666 <<
", iteration_stride_max_time_s: " << iteration_stride_max_time_s);
668 for (
size_t j = 0; j < object_predictions.size(); j += iteration_stride)
670 const lanelet::BasicPoint2d
point(object_predictions.at(j).predicted_position.position.x,
671 object_predictions.at(j).predicted_position.position.y);
674 if (boost::geometry::within(
point, llt.polygon2d()))
681 if (on_route || object_has_zero_speed)
685 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(
"yield_plugin"),
"[CPU] on_route=" << on_route
686 <<
" on_route_idx=" << on_route_idx
687 <<
" speed=" << object_speed
688 <<
" stride=" << iteration_stride);
692 RCLCPP_DEBUG(rclcpp::get_logger(
"yield_plugin"),
"[CPU] Object not on route — skipping");
696 double smallest_dist = std::numeric_limits<double>::infinity();
697 for (
size_t i = 0;
i < ego_trajectory.trajectory_points.size() - 1; ++
i)
699 auto ego_seg_start = ego_trajectory.trajectory_points.at(
i);
700 auto ego_seg_end = ego_trajectory.trajectory_points.at(
i + 1);
701 double previous_distance = std::numeric_limits<double>::infinity();
702 for (
size_t j = on_route_idx; j < object_predictions.size() - 1; j += iteration_stride)
704 auto object_seg_start = object_predictions.at(j);
705 auto object_seg_end = object_predictions.at(j + 1);
706 double ego_seg_start_time = rclcpp::Time(ego_seg_start.target_time).seconds();
707 double ego_seg_end_time = rclcpp::Time(ego_seg_end.target_time).seconds();
708 double object_seg_start_time = rclcpp::Time(object_seg_start.header.stamp).seconds();
709 double object_seg_end_time = rclcpp::Time(object_seg_end.header.stamp).seconds();
711 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
"ego_seg_start.target_time: " <<
std::to_string(ego_seg_start_time) <<
", ego_seg_end.target_time: " <<
std::to_string(ego_seg_end_time));
712 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
"object_seg_start.target_time: " <<
std::to_string(object_seg_start_time) <<
", object_seg_end.target_time: " <<
std::to_string(object_seg_end_time));
713 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
"ego_seg_start.x: " << ego_seg_start.x <<
", ego_seg_start.y: " << ego_seg_start.y);
714 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
"ego_seg_end.x: " << ego_seg_end.x <<
", ego_seg_end.y: " << ego_seg_end.y);
716 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
"object_seg_start.x: " << object_seg_start.predicted_position.position.x <<
", object_seg_start.y: " << object_seg_start.predicted_position.position.y);
717 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
"object_seg_end.x: " << object_seg_end.predicted_position.position.x <<
", object_seg_end.y: " << object_seg_end.predicted_position.position.y);
720 double interp_ratio = (object_seg_start_time - ego_seg_start_time) / (ego_seg_end_time - ego_seg_start_time);
722 if (interp_ratio < 0)
724 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
725 "Negative extrapolation, skipping this pair of points. object_seg_start_time: "
726 <<
std::to_string(object_seg_start_time) <<
", ego_seg_start_time: "
730 double ego_interp_x = ego_seg_start.x + interp_ratio * (ego_seg_end.x - ego_seg_start.x);
731 double ego_interp_y = ego_seg_start.y + interp_ratio * (ego_seg_end.y - ego_seg_start.y);
732 double object_x = object_seg_start.predicted_position.position.x;
733 double object_y = object_seg_start.predicted_position.position.y;
736 const auto distance{std::hypot(ego_interp_x - object_x, ego_interp_y - object_y)};
738 smallest_dist = std::min(distance, smallest_dist);
744 if (previous_distance < distance)
746 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
"Stopping search here because the distance between predictions started to increase");
749 previous_distance = distance;
753 RCLCPP_DEBUG(
nh_->get_logger(),
"Too far away" );
757 if (distance > collision_radius)
764 collision_result.
ego_point = lanelet::BasicPoint2d(ego_interp_x, ego_interp_y);
765 collision_result.
object_point = lanelet::BasicPoint2d(object_x, object_y);
766 collision_result.
collision_time = rclcpp::Time(object_seg_start.header.stamp);
767 return collision_result;
771 rclcpp::get_logger(
"yield_plugin"),
772 "Was not able to find collision: smallest_dist: " << smallest_dist);
787 RCLCPP_INFO_STREAM(
nh_->get_logger(),
"Detected an object nearby might be behind the vehicle at timestamp: " <<
std::to_string(collision_time.seconds()) <<
806 const carma_perception_msgs::msg::ExternalObject& curr_obstacle,
double original_tp_max_speed)
810 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
"Object's back time: " <<
std::to_string(rclcpp::Time(curr_obstacle.predictions.back().header.stamp).seconds())
814 if (rclcpp::Time(curr_obstacle.predictions.back().header.stamp).seconds() <= plan_start_time)
819 std::vector<carma_perception_msgs::msg::PredictedState> new_list;
820 carma_perception_msgs::msg::PredictedState curr_state;
822 curr_state.header.stamp = curr_obstacle.header.stamp;
823 curr_state.predicted_position.position.x = curr_obstacle.pose.pose.position.x;
824 curr_state.predicted_position.position.y = curr_obstacle.pose.pose.position.y;
826 curr_state.predicted_velocity.linear.x = curr_obstacle.velocity.twist.linear.x;
827 curr_state.predicted_velocity.linear.y = curr_obstacle.velocity.twist.linear.y;
828 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
"Object: " << curr_obstacle.id <<
", type: " <<
static_cast<int>(curr_obstacle.object_type)
829 <<
", speed_x: " << curr_obstacle.velocity.twist.linear.x <<
", speed_y: " << curr_obstacle.velocity.twist.linear.y);
830 new_list.push_back(curr_state);
831 new_list.insert(new_list.end(), curr_obstacle.predictions.cbegin(), curr_obstacle.predictions.cend());
835 if (!collision_result)
839 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(
"yield_plugin"),
"[CPU] obj=" << curr_obstacle.id <<
" no collision detected");
844 const double vehicle_downtrack =
wm_->routeTrackPos(collision_result.value().ego_point).downtrack;
845 const double object_downtrack =
wm_->routeTrackPos(collision_result.value().object_point).downtrack;
847 if (
is_object_behind_vehicle(curr_obstacle.id, collision_result.value().collision_time, vehicle_downtrack, object_downtrack))
849 RCLCPP_INFO_STREAM(
nh_->get_logger(),
"Confirmed that the object: " << curr_obstacle.id <<
" is behind the vehicle at timestamp " <<
std::to_string(collision_result.value().collision_time.seconds()));
853 const auto distance{std::hypot(
854 collision_result.value().ego_point.x() - collision_result.value().object_point.x(),
855 collision_result.value().ego_point.y() - collision_result.value().object_point.y()
858 RCLCPP_WARN_STREAM(
nh_->get_logger(),
"Collision detected for object: " << curr_obstacle.id <<
", at timestamp " <<
std::to_string(collision_result.value().collision_time.seconds()) <<
859 ", x: " << collision_result.value().ego_point.x() <<
", y: " << collision_result.value().ego_point.y() <<
860 ", within actual downtrack distance: " << object_downtrack - vehicle_downtrack <<
861 ", and collision distance: " << distance);
862 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(
"yield_plugin"),
"[CPU] obj=" << curr_obstacle.id <<
" collision at t=" << collision_result.value().collision_time.seconds());
864 return collision_result.value().collision_time;
869 const std::vector<CudaPoint>& ego_points,
871 const carma_planning_msgs::msg::TrajectoryPlan& trajectory_plan)
873 lanelet::BasicPoint2d result(ego_points[0].
x, ego_points[0].
y);
874 for (
int i = 0;
i < num_ego_points - 1; ++
i) {
875 const double seg_start_time = rclcpp::Time(trajectory_plan.trajectory_points[
i].target_time).seconds();
876 const double seg_end_time = rclcpp::Time(trajectory_plan.trajectory_points[
i + 1].target_time).seconds();
877 if (seg_start_time <= query_time && query_time <= seg_end_time) {
878 const double interp_ratio = (seg_end_time > seg_start_time) ? (query_time - seg_start_time) / (seg_end_time - seg_start_time) : 0.0;
879 result.x() = trajectory_plan.trajectory_points[
i].x + interp_ratio * (trajectory_plan.trajectory_points[
i+1].x - trajectory_plan.trajectory_points[
i].x);
880 result.y() = trajectory_plan.trajectory_points[
i].y + interp_ratio * (trajectory_plan.trajectory_points[
i+1].y - trajectory_plan.trajectory_points[
i].y);
889 const std::vector<carma_perception_msgs::msg::PredictedState>& predictions,
892 lanelet::BasicPoint2d result(
893 predictions.front().predicted_position.position.x,
894 predictions.front().predicted_position.position.y);
895 for (
int j =
start_index; j < static_cast<int>(predictions.size()) - 1; ++j) {
896 const double seg_start_time = rclcpp::Time(predictions[j].header.stamp).seconds();
897 const double seg_end_time = rclcpp::Time(predictions[j + 1].header.stamp).seconds();
898 if (seg_start_time <= query_time && query_time <= seg_end_time) {
899 const double interp_ratio = (seg_end_time > seg_start_time) ? (query_time - seg_start_time) / (seg_end_time - seg_start_time) : 0.0;
900 result.x() = predictions[j].predicted_position.position.x +
901 interp_ratio * (predictions[j+1].predicted_position.position.x - predictions[j].predicted_position.position.x);
902 result.y() = predictions[j].predicted_position.position.y +
903 interp_ratio * (predictions[j+1].predicted_position.position.y - predictions[j].predicted_position.position.y);
911 const std::vector<carma_perception_msgs::msg::PredictedState>& predictions,
912 int stride,
bool object_has_zero_speed)
const
914 for (
size_t j = 0; j < predictions.size(); j += stride) {
915 const lanelet::BasicPoint2d
point(predictions[j].predicted_position.position.x,
916 predictions[j].predicted_position.position.y);
918 if (boost::geometry::within(
point, llt.polygon2d())) {
919 return {
true,
static_cast<int>(j)};
922 if (object_has_zero_speed)
break;
928 const carma_planning_msgs::msg::TrajectoryPlan& original_tp,
929 const std::vector<carma_perception_msgs::msg::ExternalObject>& external_objects,
930 double original_tp_max_speed)
932 if (!cuda_is_available())
938 const carma_planning_msgs::msg::TrajectoryPlan& original_tp,
939 const std::vector<carma_perception_msgs::msg::ExternalObject>& external_objects,
940 double original_tp_max_speed)
942 std::unordered_map<uint32_t, std::future<std::optional<rclcpp::Time>>> futures;
943 std::unordered_map<uint32_t, rclcpp::Time> collision_times;
944 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(
"yield_plugin"),
945 "[CPU] Launching " << external_objects.size() <<
" async get_collision_time tasks");
946 std::vector<std::thread> threads;
947 threads.reserve(external_objects.size());
948 for (
const auto&
object : external_objects) {
949 std::packaged_task<std::optional<rclcpp::Time>()> task(
950 [
this, &original_tp, &
object, &original_tp_max_speed] {
953 futures[
object.id] = task.get_future();
954 threads.emplace_back(std::move(task));
956 for (
auto& t : threads) t.join();
957 for (
const auto&
object : external_objects) {
958 if (
const auto collision_time{futures.at(
object.
id).get()}) {
959 collision_times[
object.id] = collision_time.value();
962 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(
"yield_plugin"),
963 "[CPU] Done — " << collision_times.size() <<
" collision(s) confirmed");
964 return collision_times;
968 const carma_planning_msgs::msg::TrajectoryPlan& original_tp,
969 const std::vector<carma_perception_msgs::msg::ExternalObject>& external_objects,
970 double original_tp_max_speed)
972 std::unordered_map<uint32_t, rclcpp::Time> collision_times;
974 if (original_tp.trajectory_points.size() < 2)
return collision_times;
979 const double ref_time = plan_start_time;
982 const auto num_ego_points =
static_cast<int>(original_tp.trajectory_points.size());
983 std::vector<CudaPoint> ego_pts;
984 ego_pts.reserve(num_ego_points);
985 for (
const auto& tp : original_tp.trajectory_points) {
987 static_cast<float>(tp.x),
988 static_cast<float>(tp.y),
989 static_cast<float>(rclcpp::Time(tp.target_time).seconds() - ref_time)
994 struct ActiveObject {
997 std::vector<carma_perception_msgs::msg::PredictedState> predictions;
1001 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(
"yield_plugin"),
1002 "[GPU] Processing " << external_objects.size() <<
" external objects");
1004 std::vector<ActiveObject> active;
1005 std::vector<CudaPoint> obs_flat;
1006 std::vector<int> obs_offsets;
1007 std::vector<int> obs_sizes;
1009 for (
const auto& obj : external_objects) {
1011 if (rclcpp::Time(obj.predictions.back().header.stamp).seconds() <= plan_start_time) {
1012 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(
"yield_plugin"),
1013 "[GPU] obj=" << obj.id <<
" skipped — predictions expired before plan start");
1018 std::vector<carma_perception_msgs::msg::PredictedState> pred_list;
1019 pred_list.reserve(obj.predictions.size() + 1);
1021 carma_perception_msgs::msg::PredictedState curr;
1022 curr.header.stamp = obj.header.stamp;
1023 curr.predicted_position.position.x = obj.pose.pose.position.x;
1024 curr.predicted_position.position.y = obj.pose.pose.position.y;
1025 curr.predicted_velocity.linear.x = obj.velocity.twist.linear.x;
1026 curr.predicted_velocity.linear.y = obj.velocity.twist.linear.y;
1027 pred_list.push_back(curr);
1029 pred_list.insert(pred_list.end(), obj.predictions.cbegin(), obj.predictions.cend());
1031 if (pred_list.size() < 2)
continue;
1033 const double object_prediction_step_duration =
1034 (rclcpp::Time(pred_list.at(1).header.stamp) - rclcpp::Time(pred_list.front().header.stamp)).seconds();
1035 if (object_prediction_step_duration < 0.0)
continue;
1040 const double ego_to_object_dx = ego_pts[0].x - obj.pose.pose.position.x;
1041 const double ego_to_object_dy = ego_pts[0].y - obj.pose.pose.position.y;
1042 const double ego_to_object_dist = std::hypot(ego_to_object_dx, ego_to_object_dy);
1044 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(
"yield_plugin"),
1045 "[GPU] obj=" << obj.id <<
" skipped — dist_from_ego=" << ego_to_object_dist
1054 const double object_speed = std::hypot(
1055 pred_list.front().predicted_velocity.linear.x,
1056 pred_list.front().predicted_velocity.linear.y);
1060 std::sqrt(std::pow(object_speed, 2) + std::pow(original_tp_max_speed, 2));
1061 const int iteration_stride = std::max(1,
static_cast<int>(stride_max_t / object_prediction_step_duration));
1065 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(
"yield_plugin"),
1066 "[GPU] obj=" << obj.id <<
" on_route=" << on_route
1067 <<
" on_route_idx=" << on_route_idx
1068 <<
" speed=" << object_speed
1069 <<
" stride=" << iteration_stride);
1077 obs_offsets.push_back(
static_cast<int>(obs_flat.size()));
1079 for (
int j = on_route_idx; j < static_cast<int>(pred_list.size()); ++j) {
1080 obs_flat.push_back({
1081 static_cast<float>(pred_list[j].predicted_position.position.x),
1082 static_cast<float>(pred_list[j].predicted_position.position.y),
1083 static_cast<float>(rclcpp::Time(pred_list[j].header.stamp).seconds() - ref_time)
1087 obs_sizes.push_back(count);
1088 active.push_back({obj.id, std::move(pred_list), on_route_idx});
1091 if (active.empty())
return collision_times;
1096 auto _t0_cuda = std::chrono::steady_clock::now();
1098 const auto cuda_results = cuda_check_all_collisions(
1099 ego_pts, obs_flat, obs_offsets, obs_sizes,
1101 const double _cuda_ms = std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - _t0_cuda).count();
1102 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(
"yield_plugin"),
1103 "[timing] cuda_check_all_collisions: " << _cuda_ms <<
" ms");
1108 for (
size_t k = 0; k < active.size(); ++k) {
1109 const auto& object_result = cuda_results[k];
1111 if (!object_result.has_collision) {
1112 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(
"yield_plugin"),
1113 "[GPU] obj=" << active[k].
id <<
" no collision detected");
1118 const double collision_time_abs =
static_cast<double>(object_result.collision_t_norm) + ref_time;
1119 const rclcpp::Time collision_time(
static_cast<int64_t
>(collision_time_abs * 1e9));
1121 const lanelet::BasicPoint2d ego_collision_point =
interp_trajectory_pt_at_time(collision_time_abs, ego_pts, num_ego_points, original_tp);
1122 const lanelet::BasicPoint2d object_collision_point =
interp_predicted_pt_at_time(collision_time_abs, active[k].predictions, active[k].on_route_idx);
1124 const double vehicle_downtrack =
wm_->routeTrackPos(ego_collision_point).downtrack;
1125 const double object_downtrack =
wm_->routeTrackPos(object_collision_point).downtrack;
1128 vehicle_downtrack, object_downtrack)) {
1129 RCLCPP_INFO_STREAM(
nh_->get_logger(),
1130 "Confirmed that the object: " << active[k].id
1131 <<
" is behind the vehicle at timestamp "
1136 RCLCPP_WARN_STREAM(
nh_->get_logger(),
1137 "Collision detected for object: " << active[k].id
1139 <<
", x: " << ego_collision_point.x() <<
", y: " << ego_collision_point.y()
1140 <<
", within actual downtrack distance: "
1141 << object_downtrack - vehicle_downtrack);
1143 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(
"yield_plugin"),
1144 "[GPU] obj=" << active[k].
id
1145 <<
" collision at t=" << collision_time_abs
1146 <<
" ego=(" << ego_collision_point.x() <<
"," << ego_collision_point.y() <<
")"
1147 <<
" obs=(" << object_collision_point.x() <<
"," << object_collision_point.y() <<
")"
1148 <<
" downtrack_gap=" << (object_downtrack - vehicle_downtrack));
1149 collision_times[active[k].id] = collision_time;
1152 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(
"yield_plugin"),
1153 "[GPU] Done — " << collision_times.size() <<
" collision(s) confirmed");
1155 }
catch (
const std::runtime_error& e) {
1156 std::string error_msg(e.what());
1158 bool is_cuda_error = (error_msg.find(
"CUDA") != std::string::npos ||
1159 error_msg.find(
"cuda") != std::string::npos ||
1160 error_msg.find(
"driver version") != std::string::npos ||
1161 error_msg.find(
"runtime version") != std::string::npos ||
1162 error_msg.find(
"GPU") != std::string::npos);
1164 if (is_cuda_error) {
1165 RCLCPP_WARN_STREAM_ONCE(rclcpp::get_logger(
"yield_plugin"),
1166 "[GPU] CUDA unavailable (" << e.what() <<
"), please make sure GPU is accessible for this node or container. Using CPU fallback");
1168 RCLCPP_ERROR_STREAM_ONCE(rclcpp::get_logger(
"yield_plugin"),
1169 "[GPU] Unexpected error during GPU collision detection: " << e.what() <<
", using CPU fallback");
1172 }
catch (
const std::exception& e) {
1173 RCLCPP_ERROR_STREAM_ONCE(rclcpp::get_logger(
"yield_plugin"),
1174 "[GPU] Unexpected exception during GPU collision detection: " <<
typeid(e).name() <<
" - " << e.what() <<
", using CPU fallback");
1177 return collision_times;
1181 const std::vector<carma_perception_msgs::msg::ExternalObject>& external_objects)
1183 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(
"yield_plugin"),
"ExternalObjects size: " << external_objects.size());
1185 if (!
wm_->getRoute())
1187 RCLCPP_WARN(
nh_->get_logger(),
"Yield plugin was not able to analyze collision since route is not available! Please check if route is set");
1188 return std::nullopt;
1197 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
"External Object List (external_objects) size: " << external_objects.size());
1199 auto _t0_conc = std::chrono::steady_clock::now();
1201 const double _conc_ms = std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - _t0_conc).count();
1202 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(
"yield_plugin"),
1203 "[timing] get_collision_times_concurrently: " << _conc_ms <<
" ms");
1205 if (collision_times.empty()) {
return std::nullopt; }
1207 const auto earliest_colliding_object_id{std::min_element(
1208 std::cbegin(collision_times), std::cend(collision_times),
1209 [](
const auto & a,
const auto & b){
return a.second < b.second; })->first};
1211 const auto earliest_colliding_object{std::find_if(
1212 std::cbegin(external_objects), std::cend(external_objects),
1213 [&earliest_colliding_object_id](
const auto &
object) {
return object.id == earliest_colliding_object_id; })};
1215 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
"earliest object x: " << earliest_colliding_object->velocity.twist.linear.x
1216 <<
", y: " << earliest_colliding_object->velocity.twist.linear.y);
1217 return std::make_pair(*earliest_colliding_object, collision_times.at(earliest_colliding_object_id).seconds());
1222 const carma_planning_msgs::msg::TrajectoryPlan& original_tp,
double timestamp_in_sec_to_predict)
1224 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
"timestamp_in_sec_to_predict: " <<
std::to_string(timestamp_in_sec_to_predict) <<
1227 double point_b_time = 0.0;
1228 carma_planning_msgs::msg::TrajectoryPlanPoint point_a;
1229 carma_planning_msgs::msg::TrajectoryPlanPoint point_b;
1233 for (
size_t i = 0;
i < original_tp.trajectory_points.size() - 1; ++
i)
1235 point_a = original_tp.trajectory_points.at(
i);
1236 point_b = original_tp.trajectory_points.at(
i + 1);
1237 point_b_time = rclcpp::Time(point_b.target_time).seconds();
1238 if (point_b_time >= timestamp_in_sec_to_predict)
1244 auto dx = point_b.x - point_a.x;
1245 auto dy = point_b.y - point_a.y;
1246 const tf2::Vector3 trajectory_direction(
dx,
dy, 0);
1248 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
"timestamp_in_sec_to_predict: " <<
std::to_string(timestamp_in_sec_to_predict)
1250 <<
", dx: " <<
dx <<
", dy: " <<
dy <<
", "
1251 <<
", object_velocity_in_map_frame.x: " << object_velocity_in_map_frame.linear.x
1252 <<
", object_velocity_in_map_frame.y: " << object_velocity_in_map_frame.linear.y);
1254 if (trajectory_direction.length() < 0.001)
1259 const tf2::Vector3 object_direction(object_velocity_in_map_frame.linear.x, object_velocity_in_map_frame.linear.y, 0);
1261 return tf2::tf2Dot(object_direction, trajectory_direction) / trajectory_direction.length();
1265 const std::vector<carma_perception_msgs::msg::ExternalObject>& external_objects,
double initial_velocity)
1267 if (original_tp.trajectory_points.size() < 2)
1269 RCLCPP_WARN(
nh_->get_logger(),
"Yield plugin received less than 2 points in update_traj_for_object, returning unchanged...");
1274 auto _t0_ect = std::chrono::steady_clock::now();
1276 const double _ect_ms = std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - _t0_ect).count();
1277 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(
"yield_plugin"),
1278 "[timing] get_earliest_collision_object_and_time: " << _ect_ms <<
" ms");
1280 if (!earliest_collision_obj_pair)
1282 RCLCPP_DEBUG(
nh_->get_logger(),
"No collision detected, so trajectory not modified.");
1286 carma_perception_msgs::msg::ExternalObject earliest_collision_obj = earliest_collision_obj_pair.value().first;
1287 double earliest_collision_time_in_seconds = earliest_collision_obj_pair.value().second;
1292 const lanelet::BasicPoint2d vehicle_point(original_tp.trajectory_points[0].x,original_tp.trajectory_points[0].y);
1293 auto _rtp_t0_upd = std::chrono::steady_clock::now();
1294 const double vehicle_downtrack =
wm_->routeTrackPos(vehicle_point).downtrack;
1296 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
"vehicle_downtrack: " << vehicle_downtrack);
1298 RCLCPP_WARN_STREAM(
nh_->get_logger(),
"Collision Detected!");
1300 const lanelet::BasicPoint2d object_point(earliest_collision_obj.pose.pose.position.x, earliest_collision_obj.pose.pose.position.y);
1301 const double object_downtrack =
wm_->routeTrackPos(object_point).downtrack;
1303 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
"object_downtrack: " << object_downtrack);
1304 const double _rtp_ms_upd = std::chrono::duration<double, std::milli>(
1305 std::chrono::steady_clock::now() - _rtp_t0_upd).count();
1306 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(
"yield_plugin"),
1307 "[update_traj] routeTrackPos x2: " << _rtp_ms_upd <<
" ms");
1309 const double object_downtrack_lead = std::max(0.0, object_downtrack - vehicle_downtrack);
1310 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
"object_downtrack_lead: " << object_downtrack_lead);
1314 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
"object's speed along trajectory at collision: " << goal_velocity);
1320 RCLCPP_WARN_STREAM(
nh_->get_logger(),
"The obstacle is not moving, goal velocity is set to 0 from: " << goal_velocity);
1321 goal_velocity = 0.0;
1326 if (!std::isnormal(safety_gap))
1328 RCLCPP_WARN_STREAM(rclcpp::get_logger(
"yield_plugin"),
"Detected non-normal (nan, inf, etc.) safety_gap."
1336 auto _t0_gap = std::chrono::steady_clock::now();
1338 const double _gap_ms = std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - _t0_gap).count();
1339 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(
"yield_plugin"),
1340 "[timing] check_traj_for_digital_min_gap: " << _gap_ms <<
" ms");
1341 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
"externally_commanded_safety_gap: " << externally_commanded_safety_gap);
1343 safety_gap = std::max(safety_gap, externally_commanded_safety_gap);
1347 const double initial_pos = 0.0;
1348 const double original_max_speed =
max_trajectory_speed(original_tp.trajectory_points, earliest_collision_time_in_seconds);
1349 const double delta_v_max = fabs(goal_velocity - original_max_speed);
1350 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
"delta_v_max: " << delta_v_max <<
", safety_gap: " << safety_gap);
1357 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
"time_required_for_comfortable_decel_in_s: " << time_required_for_comfortable_decel_in_s <<
", min_time_required_for_comfortable_decel_in_s: " << min_time_required_for_comfortable_decel_in_s);
1359 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
"Object avoidance planning time: " << planning_time_in_s);
1361 auto _t0_jmt = std::chrono::steady_clock::now();
1363 initial_pos, goal_pos, initial_velocity, goal_velocity,
1364 planning_time_in_s, original_max_speed);
1365 const double _jmt_ms = std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - _t0_jmt).count();
1366 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(
"yield_plugin"),
1367 "[timing] generate_JMT_trajectory: " << _jmt_ms <<
" ms");
1372 earliest_collision_time_in_seconds -
nh_->now().seconds()
1377 return jmt_trajectory;
1383 std::vector<double> downtracks;
1384 downtracks.reserve(trajectory_plan.trajectory_points.size());
1386 downtracks.push_back(0.0);
1387 for (
size_t i=1;
i < trajectory_plan.trajectory_points.size();
i++){
1389 double dx = trajectory_plan.trajectory_points.at(
i).x - trajectory_plan.trajectory_points.at(
i-1).x;
1390 double dy = trajectory_plan.trajectory_points.at(
i).y - trajectory_plan.trajectory_points.at(
i-1).y;
1391 downtracks.push_back(sqrt(
dx*
dx +
dy*
dy));
1399 for (
size_t i = 0;
i < coeff.size();
i++)
1401 double value = coeff.at(
i) * pow(
x,
static_cast<int>(coeff.size() - 1 -
i));
1402 result = result + value;
1410 for (
size_t i = 0;
i < coeff.size()-1;
i++)
1412 double value =
static_cast<int>(coeff.size() - 1 -
i) * coeff.at(
i) * pow(
x,
static_cast<int>(coeff.size() - 2 -
i));
1413 result = result + value;
1420 double max_speed = 0;
1421 for(
size_t i = 0;
i < trajectory_points.size() - 2;
i++ )
1423 double dx = trajectory_points.at(
i + 1).x - trajectory_points.at(
i).x;
1424 double dy = trajectory_points.at(
i + 1).y - trajectory_points.at(
i).y;
1426 double t = (rclcpp::Time(trajectory_points.at(
i + 1).target_time).seconds() - rclcpp::Time(trajectory_points.at(
i).target_time).seconds());
1432 if (rclcpp::Time(trajectory_points.at(
i + 1).target_time).seconds() >= timestamp_in_sec_to_search_until)
1443 double desired_gap = 0;
1444 if (!
wm_->getRoute())
return desired_gap;
1446 const lanelet::BasicPoint2d traj_start(original_tp.trajectory_points.front().x,
1447 original_tp.trajectory_points.front().y);
1448 const lanelet::BasicPoint2d traj_end(original_tp.trajectory_points.back().x,
1449 original_tp.trajectory_points.back().y);
1453 auto start_llts =
wm_->getLaneletsFromPoint(traj_start, 4);
1454 auto end_llts =
wm_->getLaneletsFromPoint(traj_end, 4);
1456 if (start_llts.empty() || end_llts.empty())
1460 RCLCPP_WARN_STREAM(
nh_->get_logger(),
"check_traj_for_digital_min_gap: trajectory endpoint "
1461 "not on a lanelet, skipping digital gap check.");
1465 std::unordered_set<lanelet::Id> start_ids;
1466 std::unordered_set<lanelet::Id> end_ids;
1467 for (
const auto& llt : start_llts) start_ids.insert(llt.id());
1468 for (
const auto& llt : end_llts) end_ids.insert(llt.id());
1472 std::optional<size_t> start_pos;
1473 std::optional<size_t> end_pos;
1475 const auto& path =
wm_->getRoute()->shortestPath();
1476 for (
const auto& llt : path)
1478 if (!start_pos.has_value() && start_ids.count(llt.id())) start_pos = pos;
1479 if (end_ids.count(llt.id())) end_pos = pos;
1483 if (!start_pos || !end_pos || *start_pos > *end_pos)
1485 RCLCPP_WARN_STREAM(
nh_->get_logger(),
"check_traj_for_digital_min_gap: trajectory endpoints "
1486 "not found on route shortest path, skipping digital gap check.");
1491 for (
const auto& llt : path)
1493 if (pos > *end_pos)
break;
1494 if (pos >= *start_pos)
1496 auto digital_min_gap = llt.regulatoryElementsAs<lanelet::DigitalMinimumGap>();
1497 if (!digital_min_gap.empty())
1499 double digital_gap = digital_min_gap[0]->getMinimumGap();
1500 RCLCPP_DEBUG_STREAM(
nh_->get_logger(),
"Digital Gap found with value: " << digital_gap);
1501 desired_gap = std::max(desired_gap, digital_gap);
1514 map_projector_ = std::make_shared<lanelet::projection::LocalFrameProjector>(georeference.c_str());
1525 if (!
wm_->getRoute())
1527 RCLCPP_WARN(
nh_->get_logger(),
"update_route_llt_cache called but route is not available");
1532 for (
const auto& llt :
wm_->getRoute()->shortestPath())
std::string georeference_
std::set< lanelet::Id > route_llt_ids_
void plan_trajectory_callback(carma_planning_msgs::srv::PlanTrajectory::Request::SharedPtr req, carma_planning_msgs::srv::PlanTrajectory::Response::SharedPtr resp)
Service callback for trajectory planning.
std::vector< double > get_relative_downtracks(const carma_planning_msgs::msg::TrajectoryPlan &trajectory_plan) const
calculates distance between trajectory points in a plan
LaneChangeStatusCB lc_status_publisher_
void set_incoming_request_info(std::vector< lanelet::BasicPoint2d > req_trajectory, double req_speed, double req_planning_time, double req_timestamp)
set values for member variables related to cooperative behavior
std::vector< lanelet::BasicPoint2d > req_trajectory_points_
void mobilityrequest_cb(const carma_v2x_msgs::msg::MobilityRequest::UniquePtr msg)
callback for mobility request
double check_traj_for_digital_min_gap(const carma_planning_msgs::msg::TrajectoryPlan &original_tp) const
checks trajectory for minimum gap associated with it from the road
double get_predicted_velocity_at_time(const geometry_msgs::msg::Twist &object_velocity_in_map_frame, const carma_planning_msgs::msg::TrajectoryPlan &original_tp, double timestamp_in_sec_to_predict)
Given the object velocity in map frame with x,y components, this function returns the projected veloc...
std::shared_ptr< lanelet::projection::LocalFrameProjector > map_projector_
std::vector< lanelet::BasicPoint2d > convert_eceftrajectory_to_mappoints(const carma_v2x_msgs::msg::Trajectory &ecef_trajectory) const
convert a carma trajectory from ecef frame to map frame ecef trajectory consists of the point and a s...
std::optional< std::pair< carma_perception_msgs::msg::ExternalObject, double > > get_earliest_collision_object_and_time(const carma_planning_msgs::msg::TrajectoryPlan &original_tp, const std::vector< carma_perception_msgs::msg::ExternalObject > &external_objects)
Return the earliest collision object and time of collision pair from the given trajectory and list of...
std::optional< rclcpp::Time > get_collision_time(const carma_planning_msgs::msg::TrajectoryPlan &original_tp, const carma_perception_msgs::msg::ExternalObject &curr_obstacle, double original_tp_max_speed)
Return collision time given two trajectories with one being external object with predicted steps.
std::optional< rclcpp::Time > last_speed_time_
std::unordered_map< uint32_t, rclcpp::Time > get_collision_times_concurrently_cpu(const carma_planning_msgs::msg::TrajectoryPlan &original_tp, const std::vector< carma_perception_msgs::msg::ExternalObject > &external_objects, double original_tp_max_speed)
CPU implementation of get_collision_times_concurrently. Launches one thread per external object and c...
void update_route_llt_cache()
Rebuild the cached route lanelet polygons and IDs from the current route. Should be called once whene...
void bsm_cb(const carma_v2x_msgs::msg::BSM::UniquePtr msg)
callback for bsm message
double ecef_traj_timestep_
MobilityResponseCB mobility_response_publisher_
std::string bsmIDtoString(carma_v2x_msgs::msg::BSMCoreData bsm_core)
void set_external_objects(const std::vector< carma_perception_msgs::msg::ExternalObject > &object_list)
Setter for external objects with predictions in the environment.
YieldPlugin(std::shared_ptr< carma_ros2_utils::CarmaLifecycleNode > nh, carma_wm::WorldModelConstPtr wm, YieldPluginConfig config, MobilityResponseCB mobility_response_publisher, LaneChangeStatusCB lc_status_publisher)
Constructor.
std::shared_ptr< carma_ros2_utils::CarmaLifecycleNode > nh_
void set_georeference_string(const std::string &georeference)
Setter for map projection string to define lat/lon -> map conversion.
std::unordered_map< uint32_t, rclcpp::Time > get_collision_times_concurrently(const carma_planning_msgs::msg::TrajectoryPlan &original_tp, const std::vector< carma_perception_msgs::msg::ExternalObject > &external_objects, double original_tp_max_speed)
Given the list of objects with predicted states, get all collision times concurrently using multi-thr...
YieldPluginConfig config_
std::optional< rclcpp::Time > first_time_stopped_to_prevent_collision_
std::optional< GetCollisionResult > get_collision(const carma_planning_msgs::msg::TrajectoryPlan &ego_trajectory, const std::vector< carma_perception_msgs::msg::PredictedState > &object_predictions, double collision_radius, double ego_max_speed)
Return naive collision time and locations based on collision radius given two trajectories with one b...
std::vector< std::pair< int, lanelet::BasicPoint2d > > detect_trajectories_intersection(std::vector< lanelet::BasicPoint2d > self_trajectory, std::vector< lanelet::BasicPoint2d > incoming_trajectory) const
detect intersection point(s) of two trajectories
std::optional< carma_planning_msgs::msg::TrajectoryPlan > last_traj_plan_committed_to_stopping_
lanelet::BasicPoint2d ecef_to_map_point(const carma_v2x_msgs::msg::LocationECEF &ecef_point) const
convert a point in ecef frame (in cm) into map frame (in meters)
carma_planning_msgs::msg::TrajectoryPlan update_traj_for_object(const carma_planning_msgs::msg::TrajectoryPlan &original_tp, const std::vector< carma_perception_msgs::msg::ExternalObject > &external_objects, double initial_velocity)
trajectory is modified to safely avoid obstacles on the road
carma_planning_msgs::msg::TrajectoryPlan generate_JMT_trajectory(const carma_planning_msgs::msg::TrajectoryPlan &original_tp, double initial_pos, double goal_pos, double initial_velocity, double goal_velocity, double planning_time, double original_max_speed)
generate a Jerk Minimizing Trajectory(JMT) with the provided start and end conditions
carma_v2x_msgs::msg::MobilityResponse compose_mobility_response(const std::string &resp_recipient_id, const std::string &req_plan_id, bool response) const
compose a mobility response message
std::pair< bool, int > find_on_route_in_predictions(const std::vector< carma_perception_msgs::msg::PredictedState > &predictions, int stride, bool zero_speed) const
Check whether any predicted state of an object falls within the route lanelet polygons.
double req_target_plan_time_
bool cooperative_request_acceptable_
std::vector< lanelet::ConstLanelet > route_llt_polygons_
carma_planning_msgs::msg::TrajectoryPlan update_traj_for_cooperative_behavior(const carma_planning_msgs::msg::TrajectoryPlan &original_tp, double current_speed)
update trajectory for yielding to an incoming cooperative behavior
std::unordered_map< uint32_t, rclcpp::Time > get_collision_times_concurrently_cuda(const carma_planning_msgs::msg::TrajectoryPlan &original_tp, const std::vector< carma_perception_msgs::msg::ExternalObject > &external_objects, double original_tp_max_speed)
CUDA implementation of get_collision_times_concurrently. Filters objects to those with on-route predi...
std::unordered_map< uint32_t, int > consecutive_clearance_count_for_obstacles_
std::vector< carma_perception_msgs::msg::ExternalObject > external_objects_
bool is_object_behind_vehicle(uint32_t object_id, const rclcpp::Time &collision_time, double vehicle_point, double object_downtrack)
Check if object location is behind the vehicle using estimates of the vehicle's length and route down...
int timesteps_since_last_req_
double max_trajectory_speed(const std::vector< carma_planning_msgs::msg::TrajectoryPlanPoint > &trajectory_points, double timestamp_in_sec_to_search_until) const
calculates the maximum speed in a set of tajectory points
double polynomial_calc(std::vector< double > coeff, double x) const
calculate quintic polynomial equation for a given x
std::optional< double > last_speed_
double polynomial_calc_d(std::vector< double > coeff, double x) const
calculate derivative of quintic polynomial equation for a given x
carma_wm::WorldModelConstPtr wm_
std::vector< double > moving_average_filter(const std::vector< double > input, int window_size, bool ignore_first_point=true)
Extremely simplie moving average filter.
int get_nearest_point_index(const std::vector< lanelet::BasicPoint2d > &points, const carma_planning_msgs::msg::VehicleState &state)
Returns the nearest point (in terms of cartesian 2d distance) to the provided vehicle pose in the pro...
auto to_string(const UtmZone &zone) -> std::string
std::shared_ptr< const WorldModel > WorldModelConstPtr
std::function< void(const carma_planning_msgs::msg::LaneChangeStatus &)> LaneChangeStatusCB
double get_smallest_time_step_of_traj(const carma_planning_msgs::msg::TrajectoryPlan &original_tp)
double get_trajectory_start_time(const carma_planning_msgs::msg::TrajectoryPlan &trajectory)
double get_trajectory_end_time(const carma_planning_msgs::msg::TrajectoryPlan &trajectory)
static lanelet::BasicPoint2d interp_predicted_pt_at_time(double query_time, const std::vector< carma_perception_msgs::msg::PredictedState > &predictions, int start_index)
std::function< void(const carma_v2x_msgs::msg::MobilityResponse &)> MobilityResponseCB
static lanelet::BasicPoint2d interp_trajectory_pt_at_time(double query_time, const std::vector< CudaPoint > &ego_points, int num_ego_points, const carma_planning_msgs::msg::TrajectoryPlan &trajectory_plan)
double get_trajectory_duration(const carma_planning_msgs::msg::TrajectoryPlan &trajectory)
Stuct containing the algorithm configuration values for the YieldPluginConfig.
bool always_accept_mobility_request
double max_stop_speed_in_ms
double collision_check_radius_in_m
bool enable_adjustable_gap
double speed_moving_average_window_size
int consecutive_clearance_count_for_passed_obstacles_threshold
double acceleration_adjustment_factor
double yield_max_deceleration_in_ms2
double time_horizon_until_collision_to_commit_to_stop_in_s
double intervehicle_collision_distance_in_m
double obstacle_zero_speed_threshold_in_ms
bool enable_cooperative_behavior
double min_obj_avoidance_plan_time_in_s
double minimum_safety_gap_in_meters
int acceptable_passed_timesteps
double safety_collision_time_gap_in_s
Convenience class for saving collision results.
rclcpp::Time collision_time
lanelet::BasicPoint2d object_point
lanelet::BasicPoint2d ego_point