Carma-platform v4.10.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.
yield_plugin::YieldPlugin Class Reference

Class containing primary business logic for the In-Lane Cruising Plugin. More...

#include <yield_plugin.hpp>

Collaboration diagram for yield_plugin::YieldPlugin:
Collaboration graph

Public Member Functions

 YieldPlugin (std::shared_ptr< carma_ros2_utils::CarmaLifecycleNode > nh, carma_wm::WorldModelConstPtr wm, YieldPluginConfig config, MobilityResponseCB mobility_response_publisher, LaneChangeStatusCB lc_status_publisher)
 Constructor. More...
 
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. More...
 
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 More...
 
double polynomial_calc (std::vector< double > coeff, double x) const
 calculate quintic polynomial equation for a given x More...
 
double polynomial_calc_d (std::vector< double > coeff, double x) const
 calculate derivative of quintic polynomial equation for a given x More...
 
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 More...
 
std::vector< double > get_relative_downtracks (const carma_planning_msgs::msg::TrajectoryPlan &trajectory_plan) const
 calculates distance between trajectory points in a plan More...
 
void mobilityrequest_cb (const carma_v2x_msgs::msg::MobilityRequest::UniquePtr msg)
 callback for mobility request More...
 
void bsm_cb (const carma_v2x_msgs::msg::BSM::UniquePtr msg)
 callback for bsm message More...
 
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 set of offsets with reference to the point More...
 
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) More...
 
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 More...
 
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 More...
 
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 More...
 
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 More...
 
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 More...
 
void lookup_ecef_to_map_transform ()
 Looks up the transform between map and earth frames, and sets the member variable. More...
 
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 More...
 
void set_georeference_string (const std::string &georeference)
 Setter for map projection string to define lat/lon -> map conversion. More...
 
void set_external_objects (const std::vector< carma_perception_msgs::msg::ExternalObject > &object_list)
 Setter for external objects with predictions in the environment. More...
 
std::optional< GetCollisionResultget_collision (const carma_planning_msgs::msg::TrajectoryPlan &trajectory1, const std::vector< carma_perception_msgs::msg::PredictedState > &trajectory2, double collision_radius, double trajectory1_max_speed)
 Return naive collision time and locations based on collision radius given two trajectories with one being obstacle's predicted steps. More...
 
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. More...
 
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 downtracks. More...
 
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 external objects with predicted states. Function first filters obstacles based on whether if their any of predicted state will be on the route. Only then, the logic compares trajectory and predicted states. More...
 
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-threading. More...
 
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 velocity along the trajectory at given time. More...
 

Private Member Functions

std::string bsmIDtoString (carma_v2x_msgs::msg::BSMCoreData bsm_core)
 

Private Attributes

carma_wm::WorldModelConstPtr wm_
 
YieldPluginConfig config_
 
MobilityResponseCB mobility_response_publisher_
 
LaneChangeStatusCB lc_status_publisher_
 
std::shared_ptr< carma_ros2_utils::CarmaLifecycleNode > nh_
 
std::set< lanelet::Id > route_llt_ids_
 
lanelet::Id previous_llt_id_
 
std::vector< carma_perception_msgs::msg::ExternalObject > external_objects_
 
std::unordered_map< uint32_t, int > consecutive_clearance_count_for_obstacles_
 
bool cooperative_request_acceptable_ = false
 
std::vector< lanelet::BasicPoint2d > req_trajectory_points_
 
double req_target_speed_ = 0
 
double req_timestamp_ = 0
 
double req_target_plan_time_ = 0
 
int timesteps_since_last_req_ = 0
 
int clc_urgency_ = 0
 
std::optional< double > last_speed_ = std::nullopt
 
std::optional< rclcpp::Time > last_speed_time_ = std::nullopt
 
std::optional< rclcpp::Time > first_time_stopped_to_prevent_collision_ = std::nullopt
 
std::optional< carma_planning_msgs::msg::TrajectoryPlan > last_traj_plan_committed_to_stopping_ = std::nullopt
 
double ecef_traj_timestep_ = 0.1
 
geometry_msgs::msg::Vector3 host_vehicle_size
 
double current_speed_
 
std::string host_bsm_id_
 
std::string georeference_ {""}
 
std::shared_ptr< lanelet::projection::LocalFrameProjector > map_projector_
 

Detailed Description

Class containing primary business logic for the In-Lane Cruising Plugin.

Definition at line 77 of file yield_plugin.hpp.

Constructor & Destructor Documentation

◆ YieldPlugin()

yield_plugin::YieldPlugin::YieldPlugin ( std::shared_ptr< carma_ros2_utils::CarmaLifecycleNode >  nh,
carma_wm::WorldModelConstPtr  wm,
YieldPluginConfig  config,
MobilityResponseCB  mobility_response_publisher,
LaneChangeStatusCB  lc_status_publisher 
)

Constructor.

Parameters
wmPointer to intialized instance of the carma world model for accessing semantic map data
configThe configuration to be used for this object
mobility_response_publisherCallback which will publish the mobility response
lc_status_publisherCallback which will publish the cooperative lane change status

Definition at line 46 of file yield_plugin.cpp.

49 : nh_(nh), wm_(wm), config_(config),mobility_response_publisher_(mobility_response_publisher), lc_status_publisher_(lc_status_publisher)
50 {
51
52 }
LaneChangeStatusCB lc_status_publisher_
MobilityResponseCB mobility_response_publisher_
std::shared_ptr< carma_ros2_utils::CarmaLifecycleNode > nh_
YieldPluginConfig config_
carma_wm::WorldModelConstPtr wm_

Member Function Documentation

◆ bsm_cb()

void yield_plugin::YieldPlugin::bsm_cb ( const carma_v2x_msgs::msg::BSM::UniquePtr  msg)

callback for bsm message

Parameters
msgmobility bsm message

Definition at line 247 of file yield_plugin.cpp.

248 {
249 carma_v2x_msgs::msg::BSMCoreData bsm_core_ = msg->core_data;
250 host_bsm_id_ = bsmIDtoString(bsm_core_);
251 }
std::string bsmIDtoString(carma_v2x_msgs::msg::BSMCoreData bsm_core)

References bsmIDtoString(), and host_bsm_id_.

Referenced by yield_plugin::YieldPluginNode::on_configure_plugin().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ bsmIDtoString()

std::string yield_plugin::YieldPlugin::bsmIDtoString ( carma_v2x_msgs::msg::BSMCoreData  bsm_core)
inlineprivate

Definition at line 346 of file yield_plugin.hpp.

347 {
348 std::string res = "";
349 for (size_t i=0; i<bsm_core.id.size(); i++)
350 {
351 res+=std::to_string(bsm_core.id[i]);
352 }
353 return res;
354 }
auto to_string(const UtmZone &zone) -> std::string
Definition: utm_zone.cpp:21

References process_bag::i, and carma_cooperative_perception::to_string().

Referenced by bsm_cb().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ check_traj_for_digital_min_gap()

double yield_plugin::YieldPlugin::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

Parameters
original_tporiginal trajectory plan
Returns
minumum required min_gap from the road, if none exists, return default minimum_safety_gap_in_meters

Definition at line 1139 of file yield_plugin.cpp.

1140 {
1141 double desired_gap = 0;
1142
1143 for (size_t i = 0; i < original_tp.trajectory_points.size(); i++)
1144 {
1145 lanelet::BasicPoint2d veh_pos(original_tp.trajectory_points.at(i).x, original_tp.trajectory_points.at(i).y);
1146 auto llts = wm_->getLaneletsFromPoint(veh_pos, 1);
1147 if (llts.empty())
1148 {
1149 // This should technically never happen
1150 // However, trajectory generation currently may fail due to osm map issue https://github.com/usdot-fhwa-stol/carma-platform/issues/2503
1151 RCLCPP_WARN_STREAM(nh_->get_logger(),"Trajectory point: x= " << original_tp.trajectory_points.at(i).x << "y="<< original_tp.trajectory_points.at(i).y);
1152 RCLCPP_WARN_STREAM(nh_->get_logger(),"Trajectory is not on the road, so was unable to get the digital minimum gap. Returning default minimum_safety_gap_in_meters: " << config_.minimum_safety_gap_in_meters);
1153 return desired_gap;
1154 }
1155 auto digital_min_gap = llts[0].regulatoryElementsAs<lanelet::DigitalMinimumGap>(); //Returns a list of these elements)
1156 if (!digital_min_gap.empty())
1157 {
1158 double digital_gap = digital_min_gap[0]->getMinimumGap(); // Provided gap is in meters
1159 RCLCPP_DEBUG_STREAM(nh_->get_logger(),"Digital Gap found with value: " << digital_gap);
1160 desired_gap = std::max(desired_gap, digital_gap);
1161 }
1162 }
1163 return desired_gap;
1164 }
double minimum_safety_gap_in_meters

References config_, process_bag::i, YieldPluginConfig::minimum_safety_gap_in_meters, nh_, and wm_.

Referenced by update_traj_for_cooperative_behavior(), and update_traj_for_object().

Here is the caller graph for this function:

◆ compose_mobility_response()

carma_v2x_msgs::msg::MobilityResponse yield_plugin::YieldPlugin::compose_mobility_response ( const std::string &  resp_recipient_id,
const std::string &  req_plan_id,
bool  response 
) const

compose a mobility response message

Parameters
resp_recipient_idvehicle id of the recipient of the message
req_plan_idplan id from the requested message
responseaccept/reject to the response based on conditions
Returns
filled mobility response

Definition at line 134 of file yield_plugin.cpp.

135 {
136 carma_v2x_msgs::msg::MobilityResponse out_mobility_response;
137 out_mobility_response.m_header.sender_id = config_.vehicle_id;
138 out_mobility_response.m_header.recipient_id = resp_recipient_id;
139 out_mobility_response.m_header.sender_bsm_id = host_bsm_id_;
140 out_mobility_response.m_header.plan_id = req_plan_id;
141 out_mobility_response.m_header.timestamp = nh_->now().seconds()*1000;
142
143
145 {
146 out_mobility_response.is_accepted = true;
147 }
148 else out_mobility_response.is_accepted = false;
149
150 return out_mobility_response;
151 }
bool always_accept_mobility_request
std::string vehicle_id

References YieldPluginConfig::always_accept_mobility_request, config_, host_bsm_id_, nh_, and YieldPluginConfig::vehicle_id.

Referenced by mobilityrequest_cb().

Here is the caller graph for this function:

◆ convert_eceftrajectory_to_mappoints()

std::vector< lanelet::BasicPoint2d > yield_plugin::YieldPlugin::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 set of offsets with reference to the point

Parameters
ecef_trajectorycarma trajectory (ecef frame)
Returns
vector of 2d points in map frame

Definition at line 95 of file yield_plugin.cpp.

96 {
97 carma_planning_msgs::msg::TrajectoryPlan trajectory_plan;
98 std::vector<lanelet::BasicPoint2d> map_points;
99
100 lanelet::BasicPoint2d first_point = ecef_to_map_point(ecef_trajectory.location);
101
102 map_points.push_back(first_point);
103 auto curr_point = ecef_trajectory.location;
104
105 for (size_t i = 0; i<ecef_trajectory.offsets.size(); i++)
106 {
107 lanelet::BasicPoint2d offset_point;
108 curr_point.ecef_x += ecef_trajectory.offsets.at(i).offset_x;
109 curr_point.ecef_y += ecef_trajectory.offsets.at(i).offset_y;
110 curr_point.ecef_z += ecef_trajectory.offsets.at(i).offset_z;
111
112 offset_point = ecef_to_map_point(curr_point);
113
114 map_points.push_back(offset_point);
115 }
116
117 return map_points;
118 }
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)
list first_point
Definition: process_bag.py:52

References ecef_to_map_point(), process_bag::first_point, and process_bag::i.

Referenced by mobilityrequest_cb().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ detect_trajectories_intersection()

std::vector< std::pair< int, lanelet::BasicPoint2d > > yield_plugin::YieldPlugin::detect_trajectories_intersection ( std::vector< lanelet::BasicPoint2d >  self_trajectory,
std::vector< lanelet::BasicPoint2d >  incoming_trajectory 
) const

detect intersection point(s) of two trajectories

Parameters
trajectory1vector of 2d trajectory points
trajectory2vector of 2d trajectory points
Returns
vector of pairs of 2d intersection points and index of the point in trajectory array

Definition at line 74 of file yield_plugin.cpp.

75 {
76 std::vector<std::pair<int, lanelet::BasicPoint2d>> intersection_points;
77 boost::geometry::model::linestring<lanelet::BasicPoint2d> self_traj;
78 for (auto tpp:self_trajectory)
79 {
80 boost::geometry::append(self_traj, tpp);
81 }
82 // distance to consider trajectories colliding (chosen based on lane width and vehicle size)
83 for (size_t i=0; i<incoming_trajectory.size(); i++)
84 {
85 double res = boost::geometry::distance(incoming_trajectory.at(i), self_traj);
86
88 {
89 intersection_points.push_back(std::make_pair(i, incoming_trajectory.at(i)));
90 }
91 }
92 return intersection_points;
93 }
double intervehicle_collision_distance_in_m

References config_, process_bag::i, and YieldPluginConfig::intervehicle_collision_distance_in_m.

Referenced by update_traj_for_cooperative_behavior().

Here is the caller graph for this function:

◆ ecef_to_map_point()

lanelet::BasicPoint2d yield_plugin::YieldPlugin::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)

Parameters
ecef_pointcarma point ecef frame in cm
map_in_earthtranslate frame
Returns
2d point in map frame

Definition at line 120 of file yield_plugin.cpp.

121 {
122
123 if (!map_projector_) {
124 throw std::invalid_argument("No map projector available for ecef conversion");
125 }
126
127 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);
128
129 return lanelet::traits::to2D(map_point);
130 }
std::shared_ptr< lanelet::projection::LocalFrameProjector > map_projector_

References map_projector_.

Referenced by convert_eceftrajectory_to_mappoints().

Here is the caller graph for this function:

◆ generate_JMT_trajectory()

carma_planning_msgs::msg::TrajectoryPlan yield_plugin::YieldPlugin::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

Parameters
original_tporiginal trajectory plan
intial_posstart position
goal_posfinal position
initial_velocitystart velocity
goal_velocityend velocity
planning_timetime duration of the planning
originaloriginal_max_speed from original_tp up until the goal_pos portion NOTE: the function would generate trajectory duration arbitrarily high if stopping motion is needed. This is to keep the original trajectory's shape in terms of location so that the vehicle steers toward the direction of travel even when stopping.
Returns
updated JMT trajectory

Definition at line 462 of file yield_plugin.cpp.

464 {
465 carma_planning_msgs::msg::TrajectoryPlan jmt_trajectory;
466 std::vector<carma_planning_msgs::msg::TrajectoryPlanPoint> jmt_trajectory_points;
467 jmt_trajectory_points.push_back(original_tp.trajectory_points[0]);
468
469 std::vector<double> original_traj_relative_downtracks = get_relative_downtracks(original_tp);
470 std::vector<double> calculated_speeds = {};
471 std::vector<double> new_relative_downtracks = {};
472 new_relative_downtracks.push_back(0.0);
473 calculated_speeds.push_back(initial_velocity);
474 double new_traj_accumulated_downtrack = 0.0;
475 double original_traj_accumulated_downtrack = original_traj_relative_downtracks.at(1);
476
477 // Up until goal_pos (which also can be until end of the entire original trajectory), generate new speeds at
478 // or near original trajectory points by generating them at a fixed time interval using the JMT polynomial equation
479 const double initial_time = 0;
480 double initial_accel = 0;
482 {
483 initial_accel = (initial_velocity - last_speed_.value()) /
484 (nh_->now() - last_speed_time_.value()).seconds();
485
486 if (!std::isnormal(initial_accel))
487 {
488 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("yield_plugin"),"Detecting nan initial_accel set to 0");
489 initial_accel = 0.0;
490 }
491
492 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("yield_plugin"),"Detecting initial_accel: " << initial_accel
493 << ", initial_velocity:" << initial_velocity
494 << ", last_speed_: " << last_speed_.value()
495 << ", nh_->now(): " << nh_->now().seconds()
496 << ", last_speed_time_.get(): " << last_speed_time_.value().seconds());
497 }
498
499 const double goal_accel = 0;
500 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("yield_plugin"),"Following parameters used for JMT: "
501 "\ninitial_pos: " << initial_pos <<
502 "\ngoal_pos: " << goal_pos <<
503 "\ninitial_velocity: " << initial_velocity <<
504 "\ngoal_velocity: " << goal_velocity <<
505 "\ninitial_accel: " << initial_accel <<
506 "\ngoal_accel: " << goal_accel <<
507 "\nplanning_time: " << planning_time <<
508 "\noriginal_max_speed: " << original_max_speed);
509
510 // Get the polynomial solutions used to generate the trajectory
511 std::vector<double> polynomial_coefficients = quintic_coefficient_calculator::quintic_coefficient_calculator(initial_pos,
512 goal_pos,
513 initial_velocity,
514 goal_velocity,
515 initial_accel,
516 goal_accel,
517 initial_time,
518 planning_time);
519 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("yield_plugin"),"Used original_max_speed: " << original_max_speed);
520 for (size_t i = 0; i < polynomial_coefficients.size(); i++) {
521 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("yield_plugin"),"Coefficient " << i << ": " << polynomial_coefficients[i]);
522 }
523 const auto smallest_time_step = get_smallest_time_step_of_traj(original_tp);
524 int new_traj_idx = 1;
525 int original_traj_idx = 1;
526 while (new_traj_accumulated_downtrack < goal_pos - EPSILON && original_traj_idx < original_traj_relative_downtracks.size())
527 {
528 const double target_time = new_traj_idx * smallest_time_step;
529 const double downtrack_at_target_time = polynomial_calc(polynomial_coefficients, target_time);
530 double velocity_at_target_time = polynomial_calc_d(polynomial_coefficients, target_time);
531
532 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("yield_plugin"), "Calculated speed velocity_at_target_time: " << velocity_at_target_time
533 << ", downtrack_at_target_time: "<< downtrack_at_target_time << ", target_time: " << target_time);
534
535 // if the speed becomes negative, the downtrack starts reversing to negative as well
536 // which will never reach the goal_pos, so break here.
537 if (velocity_at_target_time < 0.0)
538 {
539 break;
540 }
541
542 // Cannot have a negative speed or have a higher speed than that of the original trajectory
543 velocity_at_target_time = std::clamp(velocity_at_target_time, 0.0, original_max_speed);
544
545 // Pick the speed if it matches with the original downtracks
546 if (downtrack_at_target_time >= original_traj_accumulated_downtrack)
547 {
548 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("yield_plugin"), "Picked calculated speed velocity_at_target_time: " << velocity_at_target_time
549 << ", downtrack_at_target_time: "<< downtrack_at_target_time << ", target_time: " << target_time);
550 // velocity_at_target_time doesn't exactly correspond to original_traj_accumulated_downtrack but does for new_traj_accumulated_downtrack.
551 // however, the logic is assuming they are close enough that the speed is usable
552 calculated_speeds.push_back(velocity_at_target_time);
553 original_traj_accumulated_downtrack += original_traj_relative_downtracks.at(original_traj_idx);
554 original_traj_idx ++;
555 }
556 new_traj_accumulated_downtrack = downtrack_at_target_time;
557 new_traj_idx++;
558
559 }
560
561 // if the loop above finished prematurely due to negative speed, fill with 0.0 speeds
562 // since the speed crossed 0.0 and algorithm indicates stopping
563 std::fill_n(std::back_inserter(calculated_speeds),
564 std::size(original_traj_relative_downtracks) - std::size(calculated_speeds),
565 0.0);
566
567 // Moving average filter to smoothen the speeds
568 std::vector<double> filtered_speeds = basic_autonomy::smoothing::moving_average_filter(calculated_speeds, config_.speed_moving_average_window_size);
569 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("yield_plugin"), "filtered_speeds size: " << filtered_speeds.size());
570 for (const auto& speed : filtered_speeds)
571 {
572 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("yield_plugin"), "filtered speed: " << speed);
573 }
574 // Replace the original trajectory's associated timestamps based on the newly calculated speeds
575 double prev_speed = filtered_speeds.at(0);
576 last_speed_ = prev_speed;
577 last_speed_time_ = nh_->now();
578 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("yield_plugin"), "start speed: " << prev_speed << ", target_time: " << std::to_string(rclcpp::Time(original_tp.trajectory_points[0].target_time).seconds()));
579
580 for(size_t i = 1; i < original_tp.trajectory_points.size(); i++)
581 {
582 carma_planning_msgs::msg::TrajectoryPlanPoint jmt_tpp = original_tp.trajectory_points.at(i);
583
584 // In case only subset of original trajectory needs modification,
585 // the rest of the points should keep the last speed to cruise
586 double current_speed = goal_velocity;
587
588 if (i < filtered_speeds.size())
589 {
590 current_speed = filtered_speeds.at(i);
591 }
592
593 //Force the speed to 0 if below configured value for more control over stopping behavior
594 if (current_speed < config_.max_stop_speed_in_ms)
595 {
596 current_speed = 0;
597 }
598
599 // Derived from constant accelaration kinematic equation: (vi + vf) / 2 * dt = d_dist
600 // This also handles a case correctly when current_speed is 0, but prev_speed is not 0 yet
601 const double dt = (2 * original_traj_relative_downtracks.at(i)) / (current_speed + prev_speed);
602 jmt_tpp.target_time = rclcpp::Time(jmt_trajectory_points.back().target_time) + rclcpp::Duration::from_nanoseconds(dt*1e9);
603
604 if (prev_speed < EPSILON) // Handle a special case if prev_speed (thus current_speed too) is 0
605 {
606 // NOTE: Assigning arbitrary 100 mins dt between points where normally dt is only 1 sec to model a stopping behavior.
607 // Another way to model it is to keep the trajectory point at a same location and increment time slightly. However,
608 // if the vehicle goes past the point, it may cruise toward undesirable location (for example into the intersection).
609 // Keeping the points help the controller steer the vehicle toward direction of travel even when stopping.
610 // Only downside is the trajectory plan is huge where only 15 sec is expected, but since this is stopping case, it shouldn't matter.
611 jmt_tpp.target_time = rclcpp::Time(jmt_trajectory_points.back().target_time) + rclcpp::Duration::from_nanoseconds(6000 * 1e9);
612 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("yield_plugin"), "Zero speed = x: " << jmt_tpp.x << ", y:" << jmt_tpp.y
613 << ", t:" << std::to_string(rclcpp::Time(jmt_tpp.target_time).seconds())
614 << ", prev_speed: " << prev_speed << ", current_speed: " << current_speed);
615 }
616 else
617 {
618 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("yield_plugin"), "Non-zero speed = x: " << jmt_tpp.x << ", y:" << jmt_tpp.y
619 << ", t:" << std::to_string(rclcpp::Time(jmt_tpp.target_time).seconds())
620 << ", prev_speed: " << prev_speed << ", current_speed: " << current_speed);
621 }
622
623 jmt_trajectory_points.push_back(jmt_tpp);
624 double insta_decel = (current_speed - prev_speed) / (rclcpp::Time(jmt_trajectory_points.at(i).target_time).seconds() - rclcpp::Time(jmt_trajectory_points.at(i - 1).target_time).seconds());
625 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("yield_plugin"), "insta_decel: " << insta_decel );
626 prev_speed = current_speed;
627 }
628
629 jmt_trajectory.header = original_tp.header;
630 jmt_trajectory.trajectory_id = original_tp.trajectory_id;
631 jmt_trajectory.trajectory_points = jmt_trajectory_points;
632 jmt_trajectory.initial_longitudinal_velocity = initial_velocity;
633 return jmt_trajectory;
634 }
std::vector< double > get_relative_downtracks(const carma_planning_msgs::msg::TrajectoryPlan &trajectory_plan) const
calculates distance between trajectory points in a plan
std::optional< rclcpp::Time > last_speed_time_
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
std::vector< double > moving_average_filter(const std::vector< double > input, int window_size, bool ignore_first_point=true)
Extremely simplie moving average filter.
Definition: filters.cpp:24
double get_smallest_time_step_of_traj(const carma_planning_msgs::msg::TrajectoryPlan &original_tp)
double max_stop_speed_in_ms
double speed_moving_average_window_size
constexpr auto EPSILON

References config_, EPSILON, get_relative_downtracks(), yield_plugin::get_smallest_time_step_of_traj(), process_bag::i, last_speed_, last_speed_time_, YieldPluginConfig::max_stop_speed_in_ms, basic_autonomy::smoothing::moving_average_filter(), nh_, polynomial_calc(), polynomial_calc_d(), YieldPluginConfig::speed_moving_average_window_size, and carma_cooperative_perception::to_string().

Referenced by update_traj_for_cooperative_behavior(), and update_traj_for_object().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_collision()

std::optional< GetCollisionResult > yield_plugin::YieldPlugin::get_collision ( const carma_planning_msgs::msg::TrajectoryPlan &  trajectory1,
const std::vector< carma_perception_msgs::msg::PredictedState > &  trajectory2,
double  collision_radius,
double  trajectory1_max_speed 
)

Return naive collision time and locations based on collision radius given two trajectories with one being obstacle's predicted steps.

Parameters
trajectory1trajectory of the ego vehicle
trajectory2trajectory of predicted steps
collision_radiusa distance to check between two trajectory points at a same timestamp that is considered a collision
trajectory1_max_speedmax speed of the trajectory1 to efficiently traverse through possible collision combination of the two trajectories NOTE: Currently Traj2 is assumed to be a simple cv model to save computational performance NOTE: Collisions are based on only collision radius at the same predicted time even if ego vehicle maybe past the obstacle. To filter these cases, see is_object_behind_vehicle()
Returns
data of time of collision if detected, otherwise, std::nullopt

Definition at line 636 of file yield_plugin.cpp.

638 {
639
640 // Iterate through each pair of consecutive points in the trajectories
641 RCLCPP_DEBUG_STREAM(nh_->get_logger(), "Starting a new collision detection, trajectory size: "
642 << trajectory1.trajectory_points.size() << ". prediction size: " << trajectory2.size());
643
644 // Iterate through the object to check if it's on the route
645 bool on_route = false;
646 int on_route_idx = 0;
647
648 // A flag to stop searching more than one lanelet if the object has no velocity
649 const auto traj2_speed{std::hypot(trajectory2.front().predicted_velocity.linear.x,
650 trajectory2.front().predicted_velocity.linear.y)};
651 bool traj2_has_zero_speed = traj2_speed < config_.obstacle_zero_speed_threshold_in_ms;
652
653 if (trajectory2.size() < 2)
654 {
655 throw std::invalid_argument("Object on ther road doesn't have enough predicted states! Please check motion_computation is correctly applying predicted states");
656 }
657 const double predict_step_duration = (rclcpp::Time(trajectory2.at(1).header.stamp) - rclcpp::Time(trajectory2.front().header.stamp)).seconds();
658 const double predict_total_duration = get_trajectory_duration(trajectory2);
659
660 if (predict_step_duration < 0.0)
661 {
662 throw std::invalid_argument("Predicted states of the object is malformed. Detected trajectory going backwards in time!");
663 }
664
665 // In order to optimize the for loops for comparing two trajectories, following logic skips every iteration_stride-th points of the traj2.
666 // Since skipping number of points from the traj2 may result in ignoring potential collisions, its value is dependent on two
667 // trajectories' speeds and intervehicle_collision_distance_in_m radius.
668 // Therefore, the derivation first calculates the max time, t, that both actors can move while still being in collision radius:
669 // sqrt( (v1 * t / 2)^2 + (v2 * t / 2)^2 ) = collision_radius. Here v1 and v2 are assumed to be perpendicular to each other and
670 // intersecting at t/2 to get max possible collision_radius. Solving for t gives following:
671 double iteration_stride_max_time_s = 2 * config_.intervehicle_collision_distance_in_m / sqrt(pow(traj2_speed, 2) + pow(trajectory1_max_speed, 2));
672 int iteration_stride = std::max(1, static_cast<int>(iteration_stride_max_time_s / predict_step_duration));
673
674 RCLCPP_DEBUG_STREAM(nh_->get_logger(), "Determined iteration_stride: " << iteration_stride
675 << ", with traj2_speed: " << traj2_speed
676 << ", with trajectory1_max_speed: " << trajectory1_max_speed
677 << ", with predict_step_duration: " << predict_step_duration
678 << ", iteration_stride_max_time_s: " << iteration_stride_max_time_s);
679
680 for (size_t j = 0; j < trajectory2.size(); j += iteration_stride) // Saving computation time aiming for 1.5 meter interval
681 {
682 lanelet::BasicPoint2d curr_point;
683 curr_point.x() = trajectory2.at(j).predicted_position.position.x;
684 curr_point.y() = trajectory2.at(j).predicted_position.position.y;
685
686 auto corresponding_lanelets = wm_->getLaneletsFromPoint(curr_point, 8); // some intersection can have 8 overlapping lanelets
687
688 for (const auto& llt: corresponding_lanelets)
689 {
690 RCLCPP_DEBUG_STREAM(nh_->get_logger(), "Checking llt: " << llt.id());
691
692 if (route_llt_ids_.find(llt.id()) != route_llt_ids_.end())
693 {
694 on_route = true;
695 on_route_idx = j;
696 break;
697 }
698 }
699 if (on_route || traj2_has_zero_speed)
700 break;
701 }
702
703 if (!on_route)
704 {
705 RCLCPP_DEBUG(nh_->get_logger(), "Predicted states are not on the route! ignoring");
706 return std::nullopt;
707 }
708
709 double smallest_dist = std::numeric_limits<double>::infinity();
710 for (size_t i = 0; i < trajectory1.trajectory_points.size() - 1; ++i)
711 {
712 auto p1a = trajectory1.trajectory_points.at(i);
713 auto p1b = trajectory1.trajectory_points.at(i + 1);
714 double previous_distance_between_predictions = std::numeric_limits<double>::infinity();
715 for (size_t j = on_route_idx; j < trajectory2.size() - 1; j += iteration_stride)
716 {
717 auto p2a = trajectory2.at(j);
718 auto p2b = trajectory2.at(j + 1);
719 double p1a_t = rclcpp::Time(p1a.target_time).seconds();
720 double p1b_t = rclcpp::Time(p1b.target_time).seconds();
721 double p2a_t = rclcpp::Time(p2a.header.stamp).seconds();
722 double p2b_t = rclcpp::Time(p2b.header.stamp).seconds();
723
724 RCLCPP_DEBUG_STREAM(nh_->get_logger(), "p1a.target_time: " << std::to_string(p1a_t) << ", p1b.target_time: " << std::to_string(p1b_t));
725 RCLCPP_DEBUG_STREAM(nh_->get_logger(), "p2a.target_time: " << std::to_string(p2a_t) << ", p2b.target_time: " << std::to_string(p2b_t));
726 RCLCPP_DEBUG_STREAM(nh_->get_logger(), "p1a.x: " << p1a.x << ", p1a.y: " << p1a.y);
727 RCLCPP_DEBUG_STREAM(nh_->get_logger(), "p1b.x: " << p1b.x << ", p1b.y: " << p1b.y);
728
729 RCLCPP_DEBUG_STREAM(nh_->get_logger(), "p2a.x: " << p2a.predicted_position.position.x << ", p2a.y: " << p2a.predicted_position.position.y);
730 RCLCPP_DEBUG_STREAM(nh_->get_logger(), "p2b.x: " << p2b.predicted_position.position.x << ", p2b.y: " << p2b.predicted_position.position.y);
731
732 // Linearly interpolate positions at a common timestamp for both trajectories
733 double dt = (p2a_t - p1a_t) / (p1b_t - p1a_t);
734 // if negative extrapolation, skip because car wouldn't go backwards
735 if (dt < 0)
736 {
737 continue;
738 }
739 double x1 = p1a.x + dt * (p1b.x - p1a.x);
740 double y1 = p1a.y + dt * (p1b.y - p1a.y);
741 double x2 = p2a.predicted_position.position.x;
742 double y2 = p2a.predicted_position.position.y;
743
744 // Calculate the distance between the two interpolated points
745 const auto distance{std::hypot(x1 - x2, y1 - y2)};
746
747 smallest_dist = std::min(distance, smallest_dist);
748
749 // Following "if logic" assumes the traj2 is a simple cv model, aka, traj2 point is a straight line over time.
750 // And current traj1 point is fixed in this iteration.
751 // Then once the distance between the two start to increase over traj2 iteration,
752 // the distance will always increase and it's unnecessary to continue the logic to find the smallest_dist
753 if (previous_distance_between_predictions < distance)
754 {
755 RCLCPP_DEBUG_STREAM(nh_->get_logger(), "Stopping search here because the distance between predictions started to increase");
756 break;
757 }
758 previous_distance_between_predictions = distance;
759
760 if (i == 0 && j == 0 && distance > config_.collision_check_radius_in_m)
761 {
762 RCLCPP_DEBUG(nh_->get_logger(), "Too far away" );
763 return std::nullopt;
764 }
765
766 if (distance > collision_radius)
767 {
768 // continue searching for collision
769 continue;
770 }
771
772 GetCollisionResult collision_result;
773 collision_result.point1 = lanelet::BasicPoint2d(x1,y1);
774 collision_result.point2 = lanelet::BasicPoint2d(x2,y2);
775 collision_result.collision_time = rclcpp::Time(p2a.header.stamp);
776 return collision_result;
777 }
778 }
779 RCLCPP_DEBUG_STREAM(
780 rclcpp::get_logger("yield_plugin"),
781 "Was not able to find collision: smallest_dist: " << smallest_dist);
782
783 // No collision detected
784 return std::nullopt;
785 }
std::set< lanelet::Id > route_llt_ids_
double get_trajectory_duration(const carma_planning_msgs::msg::TrajectoryPlan &trajectory)
double collision_check_radius_in_m
double obstacle_zero_speed_threshold_in_ms

References YieldPluginConfig::collision_check_radius_in_m, yield_plugin::GetCollisionResult::collision_time, config_, yield_plugin::get_trajectory_duration(), process_bag::i, YieldPluginConfig::intervehicle_collision_distance_in_m, nh_, YieldPluginConfig::obstacle_zero_speed_threshold_in_ms, yield_plugin::GetCollisionResult::point1, yield_plugin::GetCollisionResult::point2, route_llt_ids_, carma_cooperative_perception::to_string(), and wm_.

Referenced by get_collision_time().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_collision_time()

std::optional< rclcpp::Time > yield_plugin::YieldPlugin::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.

Parameters
trajectory1trajectory of the ego vehicle
trajectory2trajectory of the obstacle
original_tp_max_speedmax speed of the original_tp to efficiently traverse through possible collision combination of the two trajectories NOTE: Currently curr_obstacle is assumed to be using a simple cv model to save computational performance
Returns
time_of_collision if collision detected, otherwise, std::nullopt

Definition at line 814 of file yield_plugin.cpp.

816 {
817 auto plan_start_time = get_trajectory_start_time(original_tp);
818
819 RCLCPP_DEBUG_STREAM(nh_->get_logger(), "Object's back time: " << std::to_string(rclcpp::Time(curr_obstacle.predictions.back().header.stamp).seconds())
820 << ", plan_start_time: " << std::to_string(plan_start_time));
821
822 // do not process outdated objects
823 if (rclcpp::Time(curr_obstacle.predictions.back().header.stamp).seconds() <= plan_start_time)
824 {
825 return std::nullopt;
826 }
827
828 std::vector<carma_perception_msgs::msg::PredictedState> new_list;
829 carma_perception_msgs::msg::PredictedState curr_state;
830 // artificially include current position as one of the predicted states
831 curr_state.header.stamp = curr_obstacle.header.stamp;
832 curr_state.predicted_position.position.x = curr_obstacle.pose.pose.position.x;
833 curr_state.predicted_position.position.y = curr_obstacle.pose.pose.position.y;
834 // NOTE: predicted_velocity is not used for collision calculation, but timestamps
835 curr_state.predicted_velocity.linear.x = curr_obstacle.velocity.twist.linear.x;
836 curr_state.predicted_velocity.linear.y = curr_obstacle.velocity.twist.linear.y;
837 RCLCPP_DEBUG_STREAM(nh_->get_logger(), "Object: " << curr_obstacle.id <<", type: " << static_cast<int>(curr_obstacle.object_type)
838 << ", speed_x: " << curr_obstacle.velocity.twist.linear.x << ", speed_y: " << curr_obstacle.velocity.twist.linear.y);
839 new_list.push_back(curr_state);
840 new_list.insert(new_list.end(), curr_obstacle.predictions.cbegin(), curr_obstacle.predictions.cend());
841
842 const auto collision_result = get_collision(original_tp, new_list, config_.intervehicle_collision_distance_in_m, original_tp_max_speed);
843
844 if (!collision_result)
845 {
846 // reset the consecutive clearance counter because no collision was detected at this iteration
847 consecutive_clearance_count_for_obstacles_[curr_obstacle.id] = 0;
848 return std::nullopt;
849 }
850
851 // if within collision radius, it is not a collision if obstacle is behind the vehicle despite being in collision radius
852 const double vehicle_downtrack = wm_->routeTrackPos(collision_result.value().point1).downtrack;
853 const double object_downtrack = wm_->routeTrackPos(collision_result.value().point2).downtrack;
854
855 if (is_object_behind_vehicle(curr_obstacle.id, collision_result.value().collision_time, vehicle_downtrack, object_downtrack))
856 {
857 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()));
858 return std::nullopt;
859 }
860
861 const auto distance{std::hypot(
862 collision_result.value().point1.x() - collision_result.value().point2.x(),
863 collision_result.value().point1.y() - collision_result.value().point2.y()
864 )}; //for debug
865
866 RCLCPP_WARN_STREAM(nh_->get_logger(), "Collision detected for object: " << curr_obstacle.id << ", at timestamp " << std::to_string(collision_result.value().collision_time.seconds()) <<
867 ", x: " << collision_result.value().point1.x() << ", y: " << collision_result.value().point1.y() <<
868 ", within actual downtrack distance: " << object_downtrack - vehicle_downtrack <<
869 ", and collision distance: " << distance);
870
871 return collision_result.value().collision_time;
872 }
std::optional< GetCollisionResult > get_collision(const carma_planning_msgs::msg::TrajectoryPlan &trajectory1, const std::vector< carma_perception_msgs::msg::PredictedState > &trajectory2, double collision_radius, double trajectory1_max_speed)
Return naive collision time and locations based on collision radius given two trajectories with one b...
std::unordered_map< uint32_t, int > consecutive_clearance_count_for_obstacles_
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...
double get_trajectory_start_time(const carma_planning_msgs::msg::TrajectoryPlan &trajectory)

References config_, consecutive_clearance_count_for_obstacles_, get_collision(), yield_plugin::get_trajectory_start_time(), YieldPluginConfig::intervehicle_collision_distance_in_m, is_object_behind_vehicle(), nh_, carma_cooperative_perception::to_string(), and wm_.

Referenced by get_collision_times_concurrently().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_collision_times_concurrently()

std::unordered_map< uint32_t, rclcpp::Time > yield_plugin::YieldPlugin::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-threading.

Parameters
original_tptrajectory of the ego vehicle
external_objectslist of external objects with predicted states
original_tp_max_speedmax speed of the original_tp to efficiently traverse through possible collision combination of the two trajectories
Returns
mapping of objects' ids and their corresponding collision times (non-colliding objects are omitted)

Definition at line 874 of file yield_plugin.cpp.

876 {
877
878 std::unordered_map<uint32_t, std::future<std::optional<rclcpp::Time>>> futures;
879 std::unordered_map<uint32_t, rclcpp::Time> collision_times;
880
881 // Launch asynchronous tasks to check for collision times
882 for (const auto& object : external_objects) {
883 futures[object.id] = std::async(std::launch::async,[this, &original_tp, &object, &original_tp_max_speed]{
884 return get_collision_time(original_tp, object, original_tp_max_speed);
885 });
886 }
887
888 // Collect results from futures and update collision_times
889 for (const auto& object : external_objects) {
890 if (const auto collision_time{futures.at(object.id).get()}) {
891 collision_times[object.id] = collision_time.value();
892 }
893 }
894
895 return collision_times;
896 }
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.

References get_collision_time().

Referenced by get_earliest_collision_object_and_time().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_earliest_collision_object_and_time()

std::optional< std::pair< carma_perception_msgs::msg::ExternalObject, double > > yield_plugin::YieldPlugin::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 external objects with predicted states. Function first filters obstacles based on whether if their any of predicted state will be on the route. Only then, the logic compares trajectory and predicted states.

Parameters
original_tptrajectory of the ego vehicle
external_objectslist of external objects with predicted states
Returns
earliest collision object and its collision time if collision detected. std::nullopt if no collision is detected or if route is not available.

Definition at line 898 of file yield_plugin.cpp.

900 {
901 RCLCPP_DEBUG_STREAM(nh_->get_logger(), "ExternalObjects size: " << external_objects.size());
902
903 if (!wm_->getRoute())
904 {
905 RCLCPP_WARN(nh_->get_logger(), "Yield plugin was not able to analyze collision since route is not available! Please check if route is set");
906 return std::nullopt;
907 }
908
909 // save route Ids for faster access
910 for (const auto& llt: wm_->getRoute()->shortestPath())
911 {
912 // TODO: Enhancement https://github.com/usdot-fhwa-stol/carma-platform/issues/2316
913 route_llt_ids_.insert(llt.id());
914 }
915
916 RCLCPP_DEBUG_STREAM(nh_->get_logger(),"External Object List (external_objects) size: " << external_objects.size());
917 const double original_max_speed = max_trajectory_speed(original_tp.trajectory_points, get_trajectory_end_time(original_tp));
918 std::unordered_map<uint32_t, rclcpp::Time> collision_times = get_collision_times_concurrently(original_tp,external_objects, original_max_speed);
919
920 if (collision_times.empty()) { return std::nullopt; }
921
922 const auto earliest_colliding_object_id{std::min_element(
923 std::cbegin(collision_times), std::cend(collision_times),
924 [](const auto & a, const auto & b){ return a.second < b.second; })->first};
925
926 const auto earliest_colliding_object{std::find_if(
927 std::cbegin(external_objects), std::cend(external_objects),
928 [&earliest_colliding_object_id](const auto & object) { return object.id == earliest_colliding_object_id; })};
929
930 RCLCPP_DEBUG_STREAM(nh_->get_logger(),"earliest object x: " << earliest_colliding_object->velocity.twist.linear.x
931 << ", y: " << earliest_colliding_object->velocity.twist.linear.y);
932 return std::make_pair(*earliest_colliding_object, collision_times.at(earliest_colliding_object_id).seconds());
933
934 }
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...
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 get_trajectory_end_time(const carma_planning_msgs::msg::TrajectoryPlan &trajectory)

References get_collision_times_concurrently(), yield_plugin::get_trajectory_end_time(), max_trajectory_speed(), nh_, route_llt_ids_, and wm_.

Referenced by update_traj_for_object().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_predicted_velocity_at_time()

double yield_plugin::YieldPlugin::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 velocity along the trajectory at given time.

Parameters
object_velocity_in_map_frametrajectory of the ego vehicle
original_tptrajectory of the ego vehicle
timestamp_in_sec_to_predicttimestamp in seconds along the trajectory to return the projected velocity NOTE: returns the last point's speed if the specified time is past the trajectory's planning time
Returns
get_predicted_velocity_at_time

Definition at line 936 of file yield_plugin.cpp.

938 {
939 RCLCPP_DEBUG_STREAM(nh_->get_logger(), "timestamp_in_sec_to_predict: " << std::to_string(timestamp_in_sec_to_predict) <<
940 ", trajectory_end_time: " << std::to_string(get_trajectory_end_time(original_tp)));
941
942 double point_b_time = 0.0;
943 carma_planning_msgs::msg::TrajectoryPlanPoint point_a;
944 carma_planning_msgs::msg::TrajectoryPlanPoint point_b;
945
946 // trajectory points' time is guaranteed to be increasing
947 // then find the corresponding point at timestamp_in_sec_to_predict
948 for (size_t i = 0; i < original_tp.trajectory_points.size() - 1; ++i)
949 {
950 point_a = original_tp.trajectory_points.at(i);
951 point_b = original_tp.trajectory_points.at(i + 1);
952 point_b_time = rclcpp::Time(point_b.target_time).seconds();
953 if (point_b_time >= timestamp_in_sec_to_predict)
954 {
955 break;
956 }
957 }
958
959 auto dx = point_b.x - point_a.x;
960 auto dy = point_b.y - point_a.y;
961 const tf2::Vector3 trajectory_direction(dx, dy, 0);
962
963 RCLCPP_DEBUG_STREAM(nh_->get_logger(), "timestamp_in_sec_to_predict: " << std::to_string(timestamp_in_sec_to_predict)
964 << ", point_b_time: " << std::to_string(point_b_time)
965 << ", dx: " << dx << ", dy: " << dy << ", "
966 << ", object_velocity_in_map_frame.x: " << object_velocity_in_map_frame.linear.x
967 << ", object_velocity_in_map_frame.y: " << object_velocity_in_map_frame.linear.y);
968
969 if (trajectory_direction.length() < 0.001) //EPSILON
970 {
971 return 0.0;
972 }
973
974 const tf2::Vector3 object_direction(object_velocity_in_map_frame.linear.x, object_velocity_in_map_frame.linear.y, 0);
975
976 return tf2::tf2Dot(object_direction, trajectory_direction) / trajectory_direction.length();
977 }

References visualize_xodr::dx, visualize_xodr::dy, yield_plugin::get_trajectory_end_time(), process_bag::i, nh_, and carma_cooperative_perception::to_string().

Referenced by update_traj_for_object().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_relative_downtracks()

std::vector< double > yield_plugin::YieldPlugin::get_relative_downtracks ( const carma_planning_msgs::msg::TrajectoryPlan &  trajectory_plan) const

calculates distance between trajectory points in a plan

Parameters
trajectory_planinput trajectory plan
Returns
vector of relative distances between trajectory points

Definition at line 1079 of file yield_plugin.cpp.

1080 {
1081 std::vector<double> downtracks;
1082 downtracks.reserve(trajectory_plan.trajectory_points.size());
1083 // relative downtrack distance of the fist Point is 0.0
1084 downtracks.push_back(0.0);
1085 for (size_t i=1; i < trajectory_plan.trajectory_points.size(); i++){
1086
1087 double dx = trajectory_plan.trajectory_points.at(i).x - trajectory_plan.trajectory_points.at(i-1).x;
1088 double dy = trajectory_plan.trajectory_points.at(i).y - trajectory_plan.trajectory_points.at(i-1).y;
1089 downtracks.push_back(sqrt(dx*dx + dy*dy));
1090 }
1091 return downtracks;
1092 }

References visualize_xodr::dx, visualize_xodr::dy, and process_bag::i.

Referenced by generate_JMT_trajectory().

Here is the caller graph for this function:

◆ is_object_behind_vehicle()

bool yield_plugin::YieldPlugin::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 downtracks.

Parameters
object_idobject id to use for the consecutive_clearance_count_for_obstacles_
collision_timepredicted time of collision
vehicle_downtrackat the time of collision
object_downtrackat the time of collision NOTE: Uses internal counter low pass filter to confirm the object is behind only if it counted continuously above config_.consecutive_clearance_count_for_passed_obstacles_threshold
Returns
return true if object is behind the vehicle

Definition at line 788 of file yield_plugin.cpp.

789 {
790 const auto previous_clearance_count = consecutive_clearance_count_for_obstacles_[object_id];
791 // if the object's location is half a length of the vehicle past its rear-axle, it is considered behind
792 // half a length of the vehicle to conservatively estimate the rear axle to rear bumper length
793 if (object_downtrack < vehicle_downtrack - config_.vehicle_length / 2)
794 {
796 RCLCPP_INFO_STREAM(nh_->get_logger(), "Detected an object nearby might be behind the vehicle at timestamp: " << std::to_string(collision_time.seconds()) <<
797 ", and consecutive_clearance_count_for obstacle: " << object_id << ", is: " << consecutive_clearance_count_for_obstacles_[object_id]);
798 }
799 // confirmed false positive for a collision
801 {
802 return true;
803 }
804 // if the clearance counter didn't increase by this point, true collision was detected
805 // therefore reset the consecutive clearance counter as it is no longer consecutive
806 if (consecutive_clearance_count_for_obstacles_[object_id] == previous_clearance_count)
807 {
809 }
810
811 return false;
812 }
int consecutive_clearance_count_for_passed_obstacles_threshold

References config_, consecutive_clearance_count_for_obstacles_, YieldPluginConfig::consecutive_clearance_count_for_passed_obstacles_threshold, nh_, carma_cooperative_perception::to_string(), and YieldPluginConfig::vehicle_length.

Referenced by get_collision_time().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ lookup_ecef_to_map_transform()

void yield_plugin::YieldPlugin::lookup_ecef_to_map_transform ( )

Looks up the transform between map and earth frames, and sets the member variable.

◆ max_trajectory_speed()

double yield_plugin::YieldPlugin::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

Parameters
trajectory_pointstrajectory points

\param timestamp_in_sec_to_search_until before which to look for max trajectory speed

Returns
maximum speed

Definition at line 1116 of file yield_plugin.cpp.

1117 {
1118 double max_speed = 0;
1119 for(size_t i = 0; i < trajectory_points.size() - 2; i++ )
1120 {
1121 double dx = trajectory_points.at(i + 1).x - trajectory_points.at(i).x;
1122 double dy = trajectory_points.at(i + 1).y - trajectory_points.at(i).y;
1123 double d = sqrt(dx*dx + dy*dy);
1124 double t = (rclcpp::Time(trajectory_points.at(i + 1).target_time).seconds() - rclcpp::Time(trajectory_points.at(i).target_time).seconds());
1125 double v = d/t;
1126 if(v > max_speed)
1127 {
1128 max_speed = v;
1129 }
1130 if (rclcpp::Time(trajectory_points.at(i + 1).target_time).seconds() >= timestamp_in_sec_to_search_until)
1131 {
1132 break;
1133 }
1134
1135 }
1136 return max_speed;
1137 }

References visualize_xodr::dx, visualize_xodr::dy, and process_bag::i.

Referenced by get_earliest_collision_object_and_time(), update_traj_for_cooperative_behavior(), and update_traj_for_object().

Here is the caller graph for this function:

◆ mobilityrequest_cb()

void yield_plugin::YieldPlugin::mobilityrequest_cb ( const carma_v2x_msgs::msg::MobilityRequest::UniquePtr  msg)

callback for mobility request

Parameters
msgmobility request message

Definition at line 154 of file yield_plugin.cpp.

155 {
156 carma_v2x_msgs::msg::MobilityRequest incoming_request = *msg;
157 carma_planning_msgs::msg::LaneChangeStatus lc_status_msg;
158 if (incoming_request.strategy == "carma/cooperative-lane-change")
159 {
160 if (!map_projector_) {
161 RCLCPP_ERROR(nh_->get_logger(),"Cannot process mobility request as map projection is not yet set!");
162 return;
163 }
164 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)
165 {
166 RCLCPP_DEBUG(nh_->get_logger(),"Cooperative Lane Change Request Received");
167 lc_status_msg.status = carma_planning_msgs::msg::LaneChangeStatus::REQUEST_RECEIVED;
168 lc_status_msg.description = "Received lane merge request";
169
170 if (incoming_request.m_header.recipient_id == config_.vehicle_id)
171 {
172 RCLCPP_DEBUG(nh_->get_logger(),"CLC Request correctly received");
173 }
174
175 // extract mobility header
176 std::string req_sender_id = incoming_request.m_header.sender_id;
177 std::string req_plan_id = incoming_request.m_header.plan_id;
178 // extract mobility request
179 carma_v2x_msgs::msg::LocationECEF ecef_location = incoming_request.location;
180 carma_v2x_msgs::msg::Trajectory incoming_trajectory = incoming_request.trajectory;
181 std::string req_strategy_params = incoming_request.strategy_params;
182 clc_urgency_ = incoming_request.urgency;
183 RCLCPP_DEBUG_STREAM(nh_->get_logger(),"received urgency: " << clc_urgency_);
184
185 // Parse strategy parameters
186 using boost::property_tree::ptree;
187 ptree pt;
188 std::istringstream strstream(req_strategy_params);
189 boost::property_tree::json_parser::read_json(strstream, pt);
190 int req_traj_speed_full = pt.get<int>("s");
191 int req_traj_fractional = pt.get<int>("f");
192 int start_lanelet_id = pt.get<int>("sl");
193 int end_lanelet_id = pt.get<int>("el");
194 double req_traj_speed = static_cast<double>(req_traj_speed_full) + static_cast<double>(req_traj_fractional)/10.0;
195 RCLCPP_DEBUG_STREAM(nh_->get_logger(),"req_traj_speed" << req_traj_speed);
196 RCLCPP_DEBUG_STREAM(nh_->get_logger(),"start_lanelet_id" << start_lanelet_id);
197 RCLCPP_DEBUG_STREAM(nh_->get_logger(),"end_lanelet_id" << end_lanelet_id);
198
199 std::vector<lanelet::BasicPoint2d> req_traj_plan = {};
200
201 req_traj_plan = convert_eceftrajectory_to_mappoints(incoming_trajectory);
202
203 double req_expiration_sec = static_cast<double>(incoming_request.expiration);
204 double current_time_sec = nh_->now().seconds();
205
206 bool response_to_clc_req = false;
207 // ensure there is enough time for the yield
208 double req_plan_time = req_expiration_sec - current_time_sec;
209 double req_timestamp = static_cast<double>(incoming_request.m_header.timestamp) / 1000.0 - current_time_sec;
210 set_incoming_request_info(req_traj_plan, req_traj_speed, req_plan_time, req_timestamp);
211
212
213 if (req_expiration_sec - current_time_sec >= config_.min_obj_avoidance_plan_time_in_s && cooperative_request_acceptable_)
214 {
216 lc_status_msg.status = carma_planning_msgs::msg::LaneChangeStatus::REQUEST_ACCEPTED;
217 lc_status_msg.description = "Accepted lane merge request";
218 response_to_clc_req = true;
219 RCLCPP_DEBUG(nh_->get_logger(),"CLC accepted");
220 }
221 else
222 {
223 lc_status_msg.status = carma_planning_msgs::msg::LaneChangeStatus::REQUEST_REJECTED;
224 lc_status_msg.description = "Rejected lane merge request";
225 response_to_clc_req = false;
226 RCLCPP_DEBUG(nh_->get_logger(),"CLC rejected");
227 }
228 carma_v2x_msgs::msg::MobilityResponse outgoing_response = compose_mobility_response(req_sender_id, req_plan_id, response_to_clc_req);
229 mobility_response_publisher_(outgoing_response);
230 lc_status_msg.status = carma_planning_msgs::msg::LaneChangeStatus::RESPONSE_SENT;
231 RCLCPP_DEBUG(nh_->get_logger(),"response sent");
232 }
233 }
234 lc_status_publisher_(lc_status_msg);
235
236 }
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 > 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...
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
double min_obj_avoidance_plan_time_in_s

References clc_urgency_, compose_mobility_response(), config_, convert_eceftrajectory_to_mappoints(), cooperative_request_acceptable_, lc_status_publisher_, map_projector_, YieldPluginConfig::min_obj_avoidance_plan_time_in_s, mobility_response_publisher_, nh_, set_incoming_request_info(), timesteps_since_last_req_, and YieldPluginConfig::vehicle_id.

Referenced by yield_plugin::YieldPluginNode::on_configure_plugin().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ plan_trajectory_callback()

void yield_plugin::YieldPlugin::plan_trajectory_callback ( carma_planning_msgs::srv::PlanTrajectory::Request::SharedPtr  req,
carma_planning_msgs::srv::PlanTrajectory::Response::SharedPtr  resp 
)

Service callback for trajectory planning.

Parameters
srv_headerheader
reqThe service request
respThe service response

Definition at line 253 of file yield_plugin.cpp.

256{
257 RCLCPP_DEBUG(nh_->get_logger(),"Yield_plugin was called!");
258 if (req->initial_trajectory_plan.trajectory_points.size() < 2){
259 throw std::invalid_argument("Empty Trajectory received by Yield");
260 }
261 rclcpp::Clock system_clock(RCL_SYSTEM_TIME);
262 rclcpp::Time start_time = system_clock.now(); // Start timing the execution time for planning so it can be logged
263
264 carma_planning_msgs::msg::TrajectoryPlan original_trajectory = req->initial_trajectory_plan;
265 carma_planning_msgs::msg::TrajectoryPlan yield_trajectory;
266
267 // if ego is not stopped and we committed to stopping, use the last committed trajectory
268 if (req->vehicle_state.longitudinal_vel > EPSILON &&
270 {
271 RCLCPP_DEBUG(nh_->get_logger(), "Using last committed trajectory to stopping");
272 lanelet::BasicPoint2d veh_pos(req->vehicle_state.x_pos_global,
273 req->vehicle_state.y_pos_global);
274 auto updated_trajectory = last_traj_plan_committed_to_stopping_.value();
275
276 // Find closest point in last trajectory to current vehicle position
277 size_t idx_to_start_new_traj =
279 updated_trajectory.trajectory_points,
280 veh_pos);
281
282 // Update last trajectory to start from closest point (remove passed points)
283 if (!updated_trajectory.trajectory_points.empty()) {
284 updated_trajectory.trajectory_points =
285 std::vector<carma_planning_msgs::msg::TrajectoryPlanPoint>
286 (updated_trajectory.trajectory_points.begin() + idx_to_start_new_traj,
287 updated_trajectory.trajectory_points.end());
288 }
289 last_traj_plan_committed_to_stopping_ = updated_trajectory;
290 resp->trajectory_plan = last_traj_plan_committed_to_stopping_.value();
291
292 rclcpp::Time end_time = system_clock.now(); // Planning complete
293
294 auto duration = end_time - start_time;
295 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("yield_plugin"),
296 "ExecutionTime Yield: " << std::to_string(duration.seconds()));
297 return;
298 }
299
300 // Otherwise, we are planning a new trajectory by checking collision
301 try
302 {
303 // NOTE: Wrapping entire plan_trajectory logic with try catch because there is intermittent
304 // open issue of which cause is uncertain:
305 // https://github.com/usdot-fhwa-stol/carma-platform/issues/2501
306
307 double initial_velocity = req->vehicle_state.longitudinal_vel;
308 // If vehicle_state is stopped, non-zero velocity from the trajectory
309 // should be used. Otherwise, vehicle will not move.
310 if (initial_velocity < EPSILON)
311 {
312 initial_velocity = original_trajectory.initial_longitudinal_velocity;
313 // Record the time when vehicle was stopped first due to collision avoidance
316 {
318 RCLCPP_DEBUG(nh_->get_logger(), "First time stopped to prevent collision: %f",
320 }
321 }
322
323 // seperating cooperative yield with regular object detection for better performance.
325 {
326 RCLCPP_DEBUG(nh_->get_logger(),"Only consider high urgency clc");
328 {
329 RCLCPP_DEBUG(nh_->get_logger(),"Yield for CLC. We haven't received an updated negotiation this timestep");
330 yield_trajectory = update_traj_for_cooperative_behavior(original_trajectory, initial_velocity);
332 }
333 else
334 {
335 RCLCPP_DEBUG(nh_->get_logger(),"unreliable CLC communication, switching to object avoidance");
336 yield_trajectory = update_traj_for_object(original_trajectory, external_objects_, initial_velocity); // Compute the trajectory
337 }
338 }
339 else
340 {
341 RCLCPP_DEBUG(nh_->get_logger(),"Yield for object avoidance");
342 yield_trajectory = update_traj_for_object(original_trajectory, external_objects_, initial_velocity); // Compute the trajectory
343 }
344
345
346 // return original trajectory if no difference in trajectory points a.k.a no collision
347 if (fabs(get_trajectory_end_time(original_trajectory) - get_trajectory_end_time(yield_trajectory)) < EPSILON)
348 {
349
350 resp->trajectory_plan = original_trajectory;
351 // Reset the collision prevention variables
354
355 }
356 else
357 {
358 yield_trajectory.header.frame_id = "map";
359 yield_trajectory.header.stamp = nh_->now();
360 yield_trajectory.trajectory_id = original_trajectory.trajectory_id;
361 resp->trajectory_plan = yield_trajectory;
362 }
363 }
364 catch(const std::runtime_error& e) {
365 RCLCPP_WARN_STREAM(nh_->get_logger(), "Yield Plugin failed to plan trajectory due to known negative time issue: " << e.what());
366 RCLCPP_WARN_STREAM(nh_->get_logger(), "Returning the original trajectory, and retrying at the next call.");
367 resp->trajectory_plan = original_trajectory;
368 }
369
370 rclcpp::Time end_time = system_clock.now(); // Planning complete
371
372 auto duration = end_time - start_time;
373 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("yield_plugin"),
374 "ExecutionTime Yield: " << std::to_string(duration.seconds()));
375 }
std::optional< rclcpp::Time > first_time_stopped_to_prevent_collision_
std::optional< carma_planning_msgs::msg::TrajectoryPlan > last_traj_plan_committed_to_stopping_
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 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::vector< carma_perception_msgs::msg::ExternalObject > external_objects_
int get_nearest_point_index(const std::vector< lanelet::BasicPoint2d > &points, const carma_planning_msgs::msg::VehicleState &state)
Returns the nearest point (in terms of cartesian 2d distance) to the provided vehicle pose in the pro...
bool enable_cooperative_behavior

References YieldPluginConfig::acceptable_passed_timesteps, YieldPluginConfig::acceptable_urgency, clc_urgency_, config_, YieldPluginConfig::enable_cooperative_behavior, EPSILON, external_objects_, first_time_stopped_to_prevent_collision_, basic_autonomy::waypoint_generation::get_nearest_point_index(), yield_plugin::get_trajectory_end_time(), last_traj_plan_committed_to_stopping_, nh_, timesteps_since_last_req_, carma_cooperative_perception::to_string(), update_traj_for_cooperative_behavior(), and update_traj_for_object().

Here is the call graph for this function:

◆ polynomial_calc()

double yield_plugin::YieldPlugin::polynomial_calc ( std::vector< double >  coeff,
double  x 
) const

calculate quintic polynomial equation for a given x

Parameters
coeffvector including polynomial coefficiencrs
xinput variable to the polynomial
Returns
value of polynomial for given input

Definition at line 1094 of file yield_plugin.cpp.

1095 {
1096 double result = 0;
1097 for (size_t i = 0; i < coeff.size(); i++)
1098 {
1099 double value = coeff.at(i) * pow(x, static_cast<int>(coeff.size() - 1 - i));
1100 result = result + value;
1101 }
1102 return result;
1103 }

References process_bag::i, and osm_transform::x.

Referenced by generate_JMT_trajectory().

Here is the caller graph for this function:

◆ polynomial_calc_d()

double yield_plugin::YieldPlugin::polynomial_calc_d ( std::vector< double >  coeff,
double  x 
) const

calculate derivative of quintic polynomial equation for a given x

Parameters
coeffvector including polynomial coefficiencrs
xinput variable to the polynomial
Returns
value of derivative polynomial for given input

Definition at line 1105 of file yield_plugin.cpp.

1106 {
1107 double result = 0;
1108 for (size_t i = 0; i < coeff.size()-1; i++)
1109 {
1110 double value = static_cast<int>(coeff.size() - 1 - i) * coeff.at(i) * pow(x, static_cast<int>(coeff.size() - 2 - i));
1111 result = result + value;
1112 }
1113 return result;
1114 }

References process_bag::i, and osm_transform::x.

Referenced by generate_JMT_trajectory().

Here is the caller graph for this function:

◆ set_external_objects()

void yield_plugin::YieldPlugin::set_external_objects ( const std::vector< carma_perception_msgs::msg::ExternalObject > &  object_list)

Setter for external objects with predictions in the environment.

Parameters
object_listThe object list.

Definition at line 1175 of file yield_plugin.cpp.

1176 {
1177 external_objects_ = object_list;
1178 }

References external_objects_.

◆ set_georeference_string()

void yield_plugin::YieldPlugin::set_georeference_string ( const std::string &  georeference)

Setter for map projection string to define lat/lon -> map conversion.

Parameters
georeferenceThe proj string defining the projection.

Definition at line 1166 of file yield_plugin.cpp.

1167 {
1168 if (georeference_ != georeference)
1169 {
1170 georeference_ = georeference;
1171 map_projector_ = std::make_shared<lanelet::projection::LocalFrameProjector>(georeference.c_str()); // Build projector from proj string
1172 }
1173 }

References georeference_, and map_projector_.

◆ set_incoming_request_info()

void yield_plugin::YieldPlugin::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

Parameters
req_trajectoryrequested trajectory
req_speedspeed of requested cooperative behavior
req_planning_timeplanning time for the requested cooperative behavior
req_timestampthe mobility request time stamp

Definition at line 238 of file yield_plugin.cpp.

239 {
240 req_trajectory_points_ = req_trajectory;
241 req_target_speed_ = req_speed;
242 req_target_plan_time_ = req_planning_time;
243 RCLCPP_DEBUG_STREAM(nh_->get_logger(),"req_target_plan_time_" << req_target_plan_time_);
244 req_timestamp_ = req_timestamp;
245 }
std::vector< lanelet::BasicPoint2d > req_trajectory_points_

References nh_, req_target_plan_time_, req_target_speed_, req_timestamp_, and req_trajectory_points_.

Referenced by mobilityrequest_cb().

Here is the caller graph for this function:

◆ update_traj_for_cooperative_behavior()

carma_planning_msgs::msg::TrajectoryPlan yield_plugin::YieldPlugin::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

Parameters
original_tporiginal trajectory plan
current_speedcurrent speed of the vehicle
Returns
updated trajectory for cooperative behavior

Definition at line 377 of file yield_plugin.cpp.

378 {
379 carma_planning_msgs::msg::TrajectoryPlan cooperative_trajectory;
380
381 double initial_pos = 0;
382 double goal_pos;
383 double initial_velocity = current_speed;
384 double goal_velocity = req_target_speed_;
385 double planning_time = req_target_plan_time_;
386
387 std::vector<lanelet::BasicPoint2d> host_traj_points = {};
388 for (size_t i=0; i<original_tp.trajectory_points.size(); i++)
389 {
390 lanelet::BasicPoint2d traj_point;
391 traj_point.x() = original_tp.trajectory_points.at(i).x;
392 traj_point.y() = original_tp.trajectory_points.at(i).y;
393 host_traj_points.push_back(traj_point);
394 }
395
396 std::vector<std::pair<int, lanelet::BasicPoint2d>> intersection_points = detect_trajectories_intersection(host_traj_points, req_trajectory_points_);
397 if (!intersection_points.empty())
398 {
399 lanelet::BasicPoint2d intersection_point = intersection_points[0].second;
400 double dx = original_tp.trajectory_points[0].x - intersection_point.x();
401 double dy = original_tp.trajectory_points[0].y - intersection_point.y();
402 // check if a digital_gap is available
403 double digital_gap = check_traj_for_digital_min_gap(original_tp);
404 RCLCPP_DEBUG_STREAM(nh_->get_logger(),"digital_gap: " << digital_gap);
405 goal_pos = sqrt(dx*dx + dy*dy) - std::max(config_.minimum_safety_gap_in_meters, digital_gap);
406 RCLCPP_DEBUG_STREAM(nh_->get_logger(),"Goal position (goal_pos): " << goal_pos);
407 double collision_time = req_timestamp_ + (intersection_points[0].first * ecef_traj_timestep_) - config_.safety_collision_time_gap_in_s;
408 RCLCPP_DEBUG_STREAM(nh_->get_logger(),"req time stamp: " << req_timestamp_);
409 RCLCPP_DEBUG_STREAM(nh_->get_logger(),"Collision time: " << collision_time);
410 RCLCPP_DEBUG_STREAM(nh_->get_logger(),"intersection num: " << intersection_points[0].first);
411 RCLCPP_DEBUG_STREAM(nh_->get_logger(),"Planning time: " << planning_time);
412 // calculate distance traveled from beginning of trajectory to collision point
413 double dx2 = intersection_point.x() - req_trajectory_points_[0].x();
414 double dy2 = intersection_point.y() - req_trajectory_points_[0].y();
415 // calculate incoming trajectory speed from time and distance between trajectory points
416 double incoming_trajectory_speed = sqrt(dx2*dx2 + dy2*dy2)/(intersection_points[0].first * ecef_traj_timestep_);
417 // calculate goal velocity from request trajectory
418 goal_velocity = std::min(goal_velocity, incoming_trajectory_speed);
419 double min_time = (initial_velocity - goal_velocity)/config_.yield_max_deceleration_in_ms2;
420
421 RCLCPP_DEBUG_STREAM(nh_->get_logger(),"goal_velocity: " << goal_velocity);
422 RCLCPP_DEBUG_STREAM(nh_->get_logger(),"incoming_trajectory_speed: " << incoming_trajectory_speed);
423
424 if (planning_time > min_time)
425 {
427 double original_max_speed = max_trajectory_speed(original_tp.trajectory_points, get_trajectory_end_time(original_tp));
428 cooperative_trajectory = generate_JMT_trajectory(original_tp, initial_pos, goal_pos, initial_velocity, goal_velocity, planning_time, original_max_speed);
429 }
430 else
431 {
433 RCLCPP_DEBUG(nh_->get_logger(),"The incoming requested trajectory is rejected, due to insufficient gap");
434 cooperative_trajectory = original_tp;
435 }
436
437 }
438 else
439 {
441 RCLCPP_DEBUG(nh_->get_logger(),"The incoming requested trajectory does not overlap with host vehicle's trajectory");
442 cooperative_trajectory = original_tp;
443 }
444
445 return cooperative_trajectory;
446 }
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
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
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
double yield_max_deceleration_in_ms2
double safety_collision_time_gap_in_s

References check_traj_for_digital_min_gap(), config_, cooperative_request_acceptable_, detect_trajectories_intersection(), visualize_xodr::dx, visualize_xodr::dy, ecef_traj_timestep_, generate_JMT_trajectory(), yield_plugin::get_trajectory_end_time(), process_bag::i, max_trajectory_speed(), YieldPluginConfig::minimum_safety_gap_in_meters, nh_, req_target_plan_time_, req_target_speed_, req_timestamp_, req_trajectory_points_, YieldPluginConfig::safety_collision_time_gap_in_s, and YieldPluginConfig::yield_max_deceleration_in_ms2.

Referenced by plan_trajectory_callback().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ update_traj_for_object()

carma_planning_msgs::msg::TrajectoryPlan yield_plugin::YieldPlugin::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

Parameters
original_tporiginal trajectory plan without object avoidance
current_speed_current speed of the vehicle

return modified trajectory plan

Definition at line 979 of file yield_plugin.cpp.

981 {
982 if (original_tp.trajectory_points.size() < 2)
983 {
984 RCLCPP_WARN(nh_->get_logger(), "Yield plugin received less than 2 points in update_traj_for_object, returning unchanged...");
985 return original_tp;
986 }
987
988 // Get earliest collision object
989 const auto earliest_collision_obj_pair = get_earliest_collision_object_and_time(original_tp, external_objects);
990
991 if (!earliest_collision_obj_pair)
992 {
993 RCLCPP_DEBUG(nh_->get_logger(),"No collision detected, so trajectory not modified.");
994 return original_tp;
995 }
996
997 carma_perception_msgs::msg::ExternalObject earliest_collision_obj = earliest_collision_obj_pair.value().first;
998 double earliest_collision_time_in_seconds = earliest_collision_obj_pair.value().second;
999
1000 // Issue (https://github.com/usdot-fhwa-stol/carma-platform/issues/2155): If the yield_plugin can detect if the roadway object is moving along the route,
1001 // it is able to plan yielding much earlier and smoother using on_route_vehicle_collision_horizon_in_s.
1002
1003 const lanelet::BasicPoint2d vehicle_point(original_tp.trajectory_points[0].x,original_tp.trajectory_points[0].y);
1004 const double vehicle_downtrack = wm_->routeTrackPos(vehicle_point).downtrack;
1005
1006 RCLCPP_DEBUG_STREAM(nh_->get_logger(),"vehicle_downtrack: " << vehicle_downtrack);
1007
1008 RCLCPP_WARN_STREAM(nh_->get_logger(),"Collision Detected!");
1009
1010 const lanelet::BasicPoint2d object_point(earliest_collision_obj.pose.pose.position.x, earliest_collision_obj.pose.pose.position.y);
1011 const double object_downtrack = wm_->routeTrackPos(object_point).downtrack;
1012
1013 RCLCPP_DEBUG_STREAM(nh_->get_logger(),"object_downtrack: " << object_downtrack);
1014
1015 const double object_downtrack_lead = std::max(0.0, object_downtrack - vehicle_downtrack);
1016 RCLCPP_DEBUG_STREAM(nh_->get_logger(),"object_downtrack_lead: " << object_downtrack_lead);
1017
1018 // The vehicle's goal velocity of the yielding behavior is to match the velocity of the object along the trajectory.
1019 double goal_velocity = get_predicted_velocity_at_time(earliest_collision_obj.velocity.twist, original_tp, earliest_collision_time_in_seconds);
1020 RCLCPP_DEBUG_STREAM(nh_->get_logger(),"object's speed along trajectory at collision: " << goal_velocity);
1021
1022 // roadway object position
1023 const double gap_time_until_min_gap_distance = std::max(0.0, object_downtrack_lead - config_.minimum_safety_gap_in_meters)/initial_velocity;
1024
1025 if (goal_velocity <= config_.obstacle_zero_speed_threshold_in_ms){
1026 RCLCPP_WARN_STREAM(nh_->get_logger(),"The obstacle is not moving, goal velocity is set to 0 from: " << goal_velocity);
1027 goal_velocity = 0.0;
1028 }
1029
1030 // determine the safety inter-vehicle gap based on speed
1031 double safety_gap = std::max(goal_velocity * gap_time_until_min_gap_distance, config_.minimum_safety_gap_in_meters);
1032 if (!std::isnormal(safety_gap))
1033 {
1034 RCLCPP_WARN_STREAM(rclcpp::get_logger("yield_plugin"),"Detected non-normal (nan, inf, etc.) safety_gap."
1035 "Making it desired safety gap configured at config_.minimum_safety_gap_in_meters: " << config_.minimum_safety_gap_in_meters);
1037 }
1039 {
1040 // externally_commanded_safety_gap is desired distance gap commanded from external sources
1041 // such as different plugin, map, or infrastructure depending on the use case
1042 double externally_commanded_safety_gap = check_traj_for_digital_min_gap(original_tp);
1043 RCLCPP_DEBUG_STREAM(nh_->get_logger(),"externally_commanded_safety_gap: " << externally_commanded_safety_gap);
1044 // if a digital gap is available, it is replaced as safety gap
1045 safety_gap = std::max(safety_gap, externally_commanded_safety_gap);
1046 }
1047
1048 const double goal_pos = std::max(0.0, object_downtrack_lead - safety_gap - config_.vehicle_length);
1049 const double initial_pos = 0.0; //relative initial position (first trajectory point)
1050 const double original_max_speed = max_trajectory_speed(original_tp.trajectory_points, earliest_collision_time_in_seconds);
1051 const double delta_v_max = fabs(goal_velocity - original_max_speed);
1052 RCLCPP_DEBUG_STREAM(nh_->get_logger(),"delta_v_max: " << delta_v_max << ", safety_gap: " << safety_gap);
1053
1054 const double time_required_for_comfortable_decel_in_s = config_.acceleration_adjustment_factor * 2 * goal_pos / delta_v_max;
1055 const double min_time_required_for_comfortable_decel_in_s = delta_v_max / config_.yield_max_deceleration_in_ms2;
1056
1057 // planning time for object avoidance
1058 double planning_time_in_s = std::max({config_.min_obj_avoidance_plan_time_in_s, time_required_for_comfortable_decel_in_s, min_time_required_for_comfortable_decel_in_s});
1059 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);
1060
1061 RCLCPP_DEBUG_STREAM(nh_->get_logger(),"Object avoidance planning time: " << planning_time_in_s);
1062
1063 auto jmt_trajectory = generate_JMT_trajectory(original_tp,
1064 initial_pos, goal_pos, initial_velocity, goal_velocity,
1065 planning_time_in_s, original_max_speed);
1066
1067 // If expected to stop to prevent collision, we should save this trajectory to commit to it
1068 if (!last_traj_plan_committed_to_stopping_.has_value() &&
1069 goal_velocity < EPSILON &&
1070 earliest_collision_time_in_seconds - nh_->now().seconds()
1072 {
1074 }
1075 return jmt_trajectory;
1076 }
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::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...
double acceleration_adjustment_factor
double time_horizon_until_collision_to_commit_to_stop_in_s

References YieldPluginConfig::acceleration_adjustment_factor, check_traj_for_digital_min_gap(), config_, YieldPluginConfig::enable_adjustable_gap, EPSILON, generate_JMT_trajectory(), get_earliest_collision_object_and_time(), get_predicted_velocity_at_time(), last_traj_plan_committed_to_stopping_, max_trajectory_speed(), YieldPluginConfig::min_obj_avoidance_plan_time_in_s, YieldPluginConfig::minimum_safety_gap_in_meters, nh_, YieldPluginConfig::obstacle_zero_speed_threshold_in_ms, YieldPluginConfig::time_horizon_until_collision_to_commit_to_stop_in_s, YieldPluginConfig::vehicle_length, wm_, and YieldPluginConfig::yield_max_deceleration_in_ms2.

Referenced by plan_trajectory_callback().

Here is the call graph for this function:
Here is the caller graph for this function:

Member Data Documentation

◆ clc_urgency_

int yield_plugin::YieldPlugin::clc_urgency_ = 0
private

Definition at line 329 of file yield_plugin.hpp.

Referenced by mobilityrequest_cb(), and plan_trajectory_callback().

◆ config_

◆ consecutive_clearance_count_for_obstacles_

std::unordered_map<uint32_t, int> yield_plugin::YieldPlugin::consecutive_clearance_count_for_obstacles_
private

Definition at line 319 of file yield_plugin.hpp.

Referenced by get_collision_time(), and is_object_behind_vehicle().

◆ cooperative_request_acceptable_

bool yield_plugin::YieldPlugin::cooperative_request_acceptable_ = false
private

Definition at line 321 of file yield_plugin.hpp.

Referenced by mobilityrequest_cb(), and update_traj_for_cooperative_behavior().

◆ current_speed_

double yield_plugin::YieldPlugin::current_speed_
private

Definition at line 339 of file yield_plugin.hpp.

◆ ecef_traj_timestep_

double yield_plugin::YieldPlugin::ecef_traj_timestep_ = 0.1
private

Definition at line 336 of file yield_plugin.hpp.

Referenced by update_traj_for_cooperative_behavior().

◆ external_objects_

std::vector<carma_perception_msgs::msg::ExternalObject> yield_plugin::YieldPlugin::external_objects_
private

Definition at line 318 of file yield_plugin.hpp.

Referenced by plan_trajectory_callback(), and set_external_objects().

◆ first_time_stopped_to_prevent_collision_

std::optional<rclcpp::Time> yield_plugin::YieldPlugin::first_time_stopped_to_prevent_collision_ = std::nullopt
private

Definition at line 332 of file yield_plugin.hpp.

Referenced by plan_trajectory_callback().

◆ georeference_

std::string yield_plugin::YieldPlugin::georeference_ {""}
private

Definition at line 343 of file yield_plugin.hpp.

Referenced by set_georeference_string().

◆ host_bsm_id_

std::string yield_plugin::YieldPlugin::host_bsm_id_
private

Definition at line 341 of file yield_plugin.hpp.

Referenced by bsm_cb(), and compose_mobility_response().

◆ host_vehicle_size

geometry_msgs::msg::Vector3 yield_plugin::YieldPlugin::host_vehicle_size
private

Definition at line 338 of file yield_plugin.hpp.

◆ last_speed_

std::optional<double> yield_plugin::YieldPlugin::last_speed_ = std::nullopt
private

Definition at line 330 of file yield_plugin.hpp.

Referenced by generate_JMT_trajectory().

◆ last_speed_time_

std::optional<rclcpp::Time> yield_plugin::YieldPlugin::last_speed_time_ = std::nullopt
private

Definition at line 331 of file yield_plugin.hpp.

Referenced by generate_JMT_trajectory().

◆ last_traj_plan_committed_to_stopping_

std::optional< carma_planning_msgs::msg::TrajectoryPlan> yield_plugin::YieldPlugin::last_traj_plan_committed_to_stopping_ = std::nullopt
private

Definition at line 334 of file yield_plugin.hpp.

Referenced by plan_trajectory_callback(), and update_traj_for_object().

◆ lc_status_publisher_

LaneChangeStatusCB yield_plugin::YieldPlugin::lc_status_publisher_
private

Definition at line 314 of file yield_plugin.hpp.

Referenced by mobilityrequest_cb().

◆ map_projector_

std::shared_ptr<lanelet::projection::LocalFrameProjector> yield_plugin::YieldPlugin::map_projector_
private

◆ mobility_response_publisher_

MobilityResponseCB yield_plugin::YieldPlugin::mobility_response_publisher_
private

Definition at line 313 of file yield_plugin.hpp.

Referenced by mobilityrequest_cb().

◆ nh_

◆ previous_llt_id_

lanelet::Id yield_plugin::YieldPlugin::previous_llt_id_
private

Definition at line 317 of file yield_plugin.hpp.

◆ req_target_plan_time_

double yield_plugin::YieldPlugin::req_target_plan_time_ = 0
private

◆ req_target_speed_

double yield_plugin::YieldPlugin::req_target_speed_ = 0
private

◆ req_timestamp_

double yield_plugin::YieldPlugin::req_timestamp_ = 0
private

◆ req_trajectory_points_

std::vector<lanelet::BasicPoint2d> yield_plugin::YieldPlugin::req_trajectory_points_
private

◆ route_llt_ids_

std::set<lanelet::Id> yield_plugin::YieldPlugin::route_llt_ids_
private

Definition at line 316 of file yield_plugin.hpp.

Referenced by get_collision(), and get_earliest_collision_object_and_time().

◆ timesteps_since_last_req_

int yield_plugin::YieldPlugin::timesteps_since_last_req_ = 0
private

Definition at line 328 of file yield_plugin.hpp.

Referenced by mobilityrequest_cb(), and plan_trajectory_callback().

◆ wm_


The documentation for this class was generated from the following files: