Carma-platform v4.2.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.
approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin Class Reference

Class that implements the Approaching Emergency Vehicle Plugin (ERV) strategic plugin. This class is primarily responsible for handling ROS topic subscriptions, publications, and service callbacks for processing incoming BSMs from ERV's, generating manuever plans that account for a nearby ERV, and publishing warning message(s) to an approaching ERV when the ego vehicle is in its path. More...

#include <approaching_emergency_vehicle_plugin_node.hpp>

Inheritance diagram for approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin:
Inheritance graph
Collaboration diagram for approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin:
Collaboration graph

Public Member Functions

 ApproachingEmergencyVehiclePlugin (const rclcpp::NodeOptions &)
 ApproachingEmergencyVehiclePlugin constructor. More...
 
rcl_interfaces::msg::SetParametersResult parameter_update_callback (const std::vector< rclcpp::Parameter > &parameters)
 Callback for dynamic parameter updates. More...
 
void incomingBsmCallback (carma_v2x_msgs::msg::BSM::UniquePtr msg)
 Incoming BSM subscription callback, which determines whether a BSM should be processed, and extracts ERV information from the BSM through a call to getErvInformationFromBsm(). This function is responsible for updating this plugin's tracked_erv_ object and setting has_tracked_erv_ to true when a new ERV becomes tracked. More...
 
void routeStateCallback (carma_planning_msgs::msg::RouteState::UniquePtr msg)
 Route State subscription callback, with is used to update this plugin's latest_route_state_ object. More...
 
void routeCallback (carma_planning_msgs::msg::Route::UniquePtr msg)
 Route subscription callback, with is used to update this plugin's future_route_lanelet_ids_ object. More...
 
void georeferenceCallback (const std_msgs::msg::String::UniquePtr msg)
 Subscription callback for the georeference. More...
 
void incomingEmergencyVehicleAckCallback (const carma_v2x_msgs::msg::EmergencyVehicleAck::UniquePtr msg)
 Subscription callback for incoming EmergencyVehicleAck messages. If the message is from the currently tracked ERV and is intended for the ego vehicle, then the plugin will reset num_warnings_broadcasted_ to zero and will reset the should_broadcast_warnings_ flag to false. More...
 
void twistCallback (geometry_msgs::msg::TwistStamped::UniquePtr msg)
 Subscription callback for the twist subscriber, which will store latest current velocity of the ego vehicle. More...
 
void guidanceStateCallback (const carma_planning_msgs::msg::GuidanceState::UniquePtr msg)
 Subscription callback to process the latest guidance state and update the is_guidance_engaged_ flag accordingly. More...
 
void plan_maneuvers_callback (std::shared_ptr< rmw_request_id_t >, carma_planning_msgs::srv::PlanManeuvers::Request::SharedPtr req, carma_planning_msgs::srv::PlanManeuvers::Response::SharedPtr resp) override
 Extending class provided callback which should return a planned trajectory based on the provided trajectory planning request. More...
 
bool get_availability () override
 Get the availability status of this plugin based on the current operating environment. Method must be overriden by extending classes. More...
 
std::string get_version_id () override
 Returns the version id of this plugin. More...
 
carma_ros2_utils::CallbackReturn on_configure_plugin ()
 This method is used to load parameters and will be called on the configure state transition. More...
 
carma_ros2_utils::CallbackReturn on_activate_plugin ()
 This method is used to create a timer and will be called on the activate transition. More...
 
- Public Member Functions inherited from carma_guidance_plugins::StrategicPlugin
 StrategicPlugin (const rclcpp::NodeOptions &)
 StrategicPlugin constructor. More...
 
virtual ~StrategicPlugin ()=default
 Virtual destructor for safe deletion. More...
 
virtual void plan_maneuvers_callback (std::shared_ptr< rmw_request_id_t > srv_header, carma_planning_msgs::srv::PlanManeuvers::Request::SharedPtr req, carma_planning_msgs::srv::PlanManeuvers::Response::SharedPtr resp)=0
 Extending class provided callback which should return a planned trajectory based on the provided trajectory planning request. More...
 
std::string get_capability () override
 Get the capability string representing this plugins capabilities Method must be overriden by extending classes. Expectation is that abstract plugin type parent classes will provide a default implementation. More...
 
uint8_t get_type () override final
 Returns the type of this plugin according to the carma_planning_msgs::Plugin type enum. Extending classes for the specific type should override this method. More...
 
carma_ros2_utils::CallbackReturn handle_on_configure (const rclcpp_lifecycle::State &) override final
 
carma_ros2_utils::CallbackReturn handle_on_activate (const rclcpp_lifecycle::State &) override final
 
carma_ros2_utils::CallbackReturn handle_on_deactivate (const rclcpp_lifecycle::State &) override final
 
carma_ros2_utils::CallbackReturn handle_on_cleanup (const rclcpp_lifecycle::State &) override final
 
carma_ros2_utils::CallbackReturn handle_on_shutdown (const rclcpp_lifecycle::State &) override final
 
carma_ros2_utils::CallbackReturn handle_on_error (const rclcpp_lifecycle::State &, const std::string &exception_string) override final
 
- Public Member Functions inherited from carma_guidance_plugins::PluginBaseNode
 PluginBaseNode (const rclcpp::NodeOptions &)
 PluginBaseNode constructor. More...
 
virtual ~PluginBaseNode ()=default
 Virtual destructor for safe deletion. More...
 
virtual std::shared_ptr< carma_wm::WMListenerget_world_model_listener () final
 Method to return the default world model listener provided as a convience by this base class If this method or get_world_model() are not called then the world model remains uninitialized and will not create unnecessary subscriptions. More...
 
virtual carma_wm::WorldModelConstPtr get_world_model () final
 Method to return the default world model provided as a convience by this base class If this method or get_world_model_listener() are not called then the world model remains uninitialized and will not create unnecessary subscriptions. More...
 
virtual bool get_activation_status () final
 Returns the activation status of this plugin. The plugins API callbacks will only be triggered when this method returns true. More...
 
virtual uint8_t get_type ()
 Returns the type of this plugin according to the carma_planning_msgs::Plugin type enum. Extending classes for the specific type should override this method. More...
 
std::string get_plugin_name_and_ns () const
 Return the name of this plugin with namespace. NOTE: If only the name of the plugin is required, use get_plugin_name() More...
 
std::string get_plugin_name () const
 Return the name of this plugin. More...
 
virtual bool get_availability ()=0
 Get the availability status of this plugin based on the current operating environment. Method must be overriden by extending classes. More...
 
virtual std::string get_capability ()=0
 Get the capability string representing this plugins capabilities Method must be overriden by extending classes. Expectation is that abstract plugin type parent classes will provide a default implementation. More...
 
virtual std::string get_version_id ()=0
 Returns the version id of this plugin. More...
 
virtual carma_ros2_utils::CallbackReturn on_configure_plugin ()=0
 Method which is triggered when this plugin is moved from the UNCONFIGURED to INACTIVE states. This method should be used to load parameters and is required to be implemented. More...
 
virtual carma_ros2_utils::CallbackReturn on_activate_plugin ()
 Method which is triggered when this plugin is moved from the INACTIVE to ACTIVE states. This method should be used to prepare for future callbacks for plugin's capabilites. More...
 
virtual carma_ros2_utils::CallbackReturn on_deactivate_plugin ()
 Method which is triggered when this plugin is moved from the ACTIVE to INACTIVE states. This method should be used to disable any functionality which should cease execution when plugin is inactive. More...
 
virtual carma_ros2_utils::CallbackReturn on_cleanup_plugin ()
 Method which is triggered when this plugin is moved from the INACTIVE to UNCONFIGURED states. This method should be used to fully reset the plugin such that a future call to on_configure_plugin would leave the plugin in a fresh state as though just launched. More...
 
virtual carma_ros2_utils::CallbackReturn on_shutdown_plugin ()
 Method which is triggered when this plugin is moved from any state to FINALIZED This method should be used to generate any shutdown logs or final cleanup. More...
 
virtual carma_ros2_utils::CallbackReturn on_error_plugin (const std::string &exception_string)
 Method which is triggered when an unhandled exception occurs in this plugin This method should be used to cleanup such that the plugin could be moved to UNCONFIGURED state if possible. More...
 
carma_ros2_utils::CallbackReturn handle_on_configure (const rclcpp_lifecycle::State &) override
 
carma_ros2_utils::CallbackReturn handle_on_activate (const rclcpp_lifecycle::State &) override
 
carma_ros2_utils::CallbackReturn handle_on_deactivate (const rclcpp_lifecycle::State &) override
 
carma_ros2_utils::CallbackReturn handle_on_cleanup (const rclcpp_lifecycle::State &) override
 
carma_ros2_utils::CallbackReturn handle_on_shutdown (const rclcpp_lifecycle::State &) override
 
carma_ros2_utils::CallbackReturn handle_on_error (const rclcpp_lifecycle::State &, const std::string &exception_string) override
 
 FRIEND_TEST (carma_guidance_plugins_test, connections_test)
 

Public Attributes

carma_wm::WorldModelConstPtr wm_
 

Private Member Functions

boost::optional< lanelet::BasicPoint2d > getErvPositionInMap (const double &current_latitude, const double &current_longitude)
 Helper function to obtain an ERV's position in the map frame from its current latitude and longitude. More...
 
lanelet::Optional< lanelet::routing::Route > generateErvRoute (double current_latitude, double current_longitude, std::vector< carma_v2x_msgs::msg::Position3D > erv_destination_points)
 Helper function to generate an ERV's route based on its current position and its future route destination points. More...
 
boost::optional< lanelet::ConstLanelet > getRouteIntersectingLanelet (const lanelet::routing::Route &erv_future_route)
 Helper function to obtain the earliest lanelet that exists on both an ERV's future route and the ego vehicle's future shortest path. Accesses the ego vehicle's future shortest path using wm_ object. More...
 
boost::optional< double > getSecondsUntilPassing (lanelet::Optional< lanelet::routing::Route > &erv_future_route, const lanelet::BasicPoint2d &erv_position_in_map, const double &erv_current_speed, lanelet::ConstLanelet &intersecting_lanelet)
 Helper function to calculate the estimated seconds until an ERV will pass the ego vehicle. This is an estimate since it assumes both the ERV and the ego vehicle will continue travelling at their current speed. More...
 
boost::optional< ErvInformationgetErvInformationFromBsm (carma_v2x_msgs::msg::BSM::UniquePtr msg)
 Through internal logic and calls to separate helper functions, this function processes a received BSM to obtain all necessary information for an ERV to be tracked. This "necessary information" includes all fields contained within an ErvInformation struct. More...
 
void checkForErvTimeout ()
 This is a callback function for the erv_timeout_timer_ timer, and is called to determine whether a timeout has occurred for the currently tracked ERV by comparing the duration since the latest ERV BSM was received with config_.timeout_duration. If a timeout has occurred, an ERV_UPDATE_TIMEOUT event will be triggered for this plugin's transition_table_ object, and the has_tracked_erv_ boolean flag will be set to false. More...
 
void broadcastWarningToErv ()
 This is a callback function for the warning_broadcast_timer_, and is called to broadcast an EmergencyVehicleResponse warning message to the currently tracked ERV when the ego vehicle is in the ERV's path, but is unable to change lanes because the ERV is estimated to pass the ego vehicle in under config_.passing_threshold. It increases the num_warnings_broadcasted_ counter, and resets it to 0 when it has reached config_.max_warning_broadcasts. Additionally, when config_.max_warning_broadcasts is reached, this method sets should_broadcast_warnings_ to false. More...
 
void publishApproachingErvStatus ()
 This is a callback function for the approaching_emergency_vehicle_status_timer_. It makes a call to generateApproachingErvStatusMessage() and publishes the generated message to approaching_erv_status_pub_. More...
 
void publishHazardLightStatus ()
 This is a callback function for publishing turn ON/OFF (true/false) hazard light command to the ssc driver. More...
 
carma_msgs::msg::UIInstructions generateApproachingErvStatusMessage ()
 Function to generate a carma_msgs::msg::UIInstructions message that describes whether there is currently an approaching ERV that is being tracked by this plugin. The generated message will follow the format described for this plugin's APPROACHING_ERV_STATUS_PARAMS string object. More...
 
boost::optional< lanelet::ConstLanelet > getLaneletOnEgoRouteFromMapPosition (const double &x_position, const double &y_position)
 Helper function to convert a map x,y coordinate pair to a lanelet on the ego vehicle's route. More...
 
double getLaneletSpeedLimit (const lanelet::ConstLanelet &lanelet)
 Helper function to extract the speed limit (m/s) from a provided lanelet. More...
 
rclcpp::Duration getManeuverDuration (const carma_planning_msgs::msg::Maneuver &maneuver, double epsilon) const
 Helper function to obtain the (seconds) of a provided maneuver. More...
 
carma_planning_msgs::msg::Maneuver composeLaneFollowingManeuverMessage (double start_dist, double end_dist, double start_speed, double target_speed, int lanelet_id, rclcpp::Time &start_time) const
 Function to compose a lane following maneuver message based on the provided maneuver parameters. More...
 
carma_planning_msgs::msg::Maneuver composeLaneChangeManeuverMessage (double start_dist, double end_dist, double start_speed, double target_speed, lanelet::Id starting_lane_id, lanelet::Id ending_lane_id, rclcpp::Time &start_time) const
 Function to compose a lane change maneuver message based on the provided maneuver parameters. More...
 
carma_planning_msgs::msg::Maneuver composeStopAndWaitManeuverMessage (double start_dist, double end_dist, double start_speed, lanelet::Id starting_lane_id, lanelet::Id ending_lane_id, double stopping_deceleration, rclcpp::Time &start_time) const
 Function to compose a stop and wait maneuver message based on the provided maneuver parameters. More...
 
void addStopAndWaitToEndOfPlan (carma_planning_msgs::srv::PlanManeuvers::Response::SharedPtr resp, double downtrack_progress, double stop_maneuver_beginning_downtrack, double end_of_route_downtrack, double stopping_entry_speed, double stopping_deceleration, double current_lanelet_ending_downtrack, lanelet::ConstLanelet current_lanelet, rclcpp::Time time_progress)
 Function to add a stop and wait maneuver to the end of the maneuver plan contained in the provided 'resp' object. If there is signficant distance between the provided downtrack_progress and stop_maneuver_beginning_downtrack, a lane follow maneuver will be added first to fill this distance. Otherwise, the stop and wait maneuver will at the downtrack provided by downtrack_progress. More...
 
void generateReducedSpeedLaneFollowingeManeuverPlan (carma_planning_msgs::srv::PlanManeuvers::Response::SharedPtr resp, lanelet::ConstLanelet current_lanelet, double downtrack_progress, double current_lanelet_ending_downtrack, double speed_progress, double target_speed, rclcpp::Time time_progress, bool is_maintaining_non_reduced_speed)
 Function to generate a maneuver plan when the ego vehicle must remain in its lane due to being in the SLOWING_DOWN_FOR_ERV state. The target speed of lane follow maneuvers in the generated plan will have a reduced value. More...
 
void generateMoveOverManeuverPlan (carma_planning_msgs::srv::PlanManeuvers::Response::SharedPtr resp, lanelet::ConstLanelet current_lanelet, double downtrack_progress, double current_lanelet_ending_downtrack, double speed_progress, double target_speed, rclcpp::Time time_progress, int ego_lane_index, int erv_lane_index)
 Function to generate a maneuver plan when the ego vehicle must change lanes due to being in the MOVING_OVER_FOR_APPROACHING_ERV state. If both the ego vehicle and the ERV are in the rightmost lane, a left lane change will be contained in the plan; otherwise a right lane change will be planned. More...
 
std::vector< lanelet::BasicPoint2d > filter_points_ahead (const lanelet::BasicPoint2d &reference_point, const std::vector< lanelet::BasicPoint2d > &original_points) const
 Helper function that return points ahead of the given reference point accounting for the list of point's direction (idx 0 is the beginning). Used for getting route destination points ahead of the ERV. More...
 
 FRIEND_TEST (Testapproaching_emergency_vehicle_plugin, testStateMachineTransitions)
 
 FRIEND_TEST (Testapproaching_emergency_vehicle_plugin, testBSMProcessing)
 
 FRIEND_TEST (Testapproaching_emergency_vehicle_plugin, testRouteConflict)
 
 FRIEND_TEST (Testapproaching_emergency_vehicle_plugin, testManeuverPlanWhenSlowingDownForErv)
 
 FRIEND_TEST (Testapproaching_emergency_vehicle_plugin, testManeuverPlanWhenMovingOverForErv)
 
 FRIEND_TEST (Testapproaching_emergency_vehicle_plugin, testWarningBroadcast)
 
 FRIEND_TEST (Testapproaching_emergency_vehicle_plugin, testApproachingErvStatusMessage)
 
 FRIEND_TEST (Testapproaching_emergency_vehicle_plugin, filter_points_ahead)
 

Private Attributes

carma_ros2_utils::SubPtr< carma_v2x_msgs::msg::BSM > incoming_bsm_sub_
 
carma_ros2_utils::SubPtr< std_msgs::msg::String > georeference_sub_
 
carma_ros2_utils::SubPtr< carma_planning_msgs::msg::RouteState > route_state_sub_
 
carma_ros2_utils::SubPtr< geometry_msgs::msg::TwistStamped > twist_sub_
 
carma_ros2_utils::SubPtr< carma_v2x_msgs::msg::EmergencyVehicleAck > incoming_emergency_vehicle_ack_sub_
 
carma_ros2_utils::SubPtr< carma_planning_msgs::msg::GuidanceState > guidance_state_sub_
 
carma_ros2_utils::SubPtr< carma_planning_msgs::msg::Route > route_sub_
 
carma_ros2_utils::PubPtr< carma_v2x_msgs::msg::EmergencyVehicleResponse > outgoing_emergency_vehicle_response_pub_
 
carma_ros2_utils::PubPtr< carma_msgs::msg::UIInstructions > approaching_erv_status_pub_
 
carma_ros2_utils::PubPtr< std_msgs::msg::Bool > hazard_light_cmd_pub_
 
Config config_
 
ApproachingEmergencyVehicleTransitionTable transition_table_
 
ErvInformation tracked_erv_
 
bool has_tracked_erv_ = false
 
int ego_lane_index_
 
carma_planning_msgs::msg::ManeuverPlan latest_maneuver_plan_
 
const std::string APPROACHING_ERV_STATUS_PARAMS = "HAS_APPROACHING_ERV:%1%,TIME_UNTIL_PASSING:%2$.1f,EGO_VEHICLE_ACTION:%3%"
 
rclcpp::TimerBase::SharedPtr erv_timeout_timer_
 
bool has_broadcasted_warning_messages_ = false
 
bool should_broadcast_warnings_ = false
 
int num_warnings_broadcasted_ = 0
 
rclcpp::TimerBase::SharedPtr warning_broadcast_timer_
 
rclcpp::TimerBase::SharedPtr approaching_emergency_vehicle_status_timer_
 
rclcpp::TimerBase::SharedPtr hazard_light_timer_
 
UpcomingLaneChangeParameters upcoming_lc_params_
 
bool has_planned_upcoming_lc_ = false
 
bool hazard_light_cmd_ = false
 
const double MAINTAIN_SPEED_THRESHOLD = 8.0
 
bool is_maintaining_non_reduced_speed_ = false
 
double non_reduced_speed_to_maintain_ = 4.4704
 
bool is_guidance_engaged_ = false
 
std::unordered_map< std::string, bool > is_same_direction_
 
std::unordered_map< std::string, rclcpp::Time > latest_erv_update_times_
 
boost::optional< std::string > map_projector_
 
carma_planning_msgs::msg::RouteState latest_route_state_
 
double current_speed_
 
bool has_received_route_state_ = false
 
std::vector< int > future_route_lanelet_ids_
 
std::string logger_name = "approaching_emergency_vehicle_plugin"
 
std::string strategic_plugin_name_ = "approaching_emergency_vehicle_plugin"
 
std::shared_ptr< carma_wm::CARMAWorldModelerv_world_model_
 
double epsilon_ = 0.0001
 

Detailed Description

Class that implements the Approaching Emergency Vehicle Plugin (ERV) strategic plugin. This class is primarily responsible for handling ROS topic subscriptions, publications, and service callbacks for processing incoming BSMs from ERV's, generating manuever plans that account for a nearby ERV, and publishing warning message(s) to an approaching ERV when the ego vehicle is in its path.

Definition at line 110 of file approaching_emergency_vehicle_plugin_node.hpp.

Constructor & Destructor Documentation

◆ ApproachingEmergencyVehiclePlugin()

approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::ApproachingEmergencyVehiclePlugin ( const rclcpp::NodeOptions &  options)
explicit

ApproachingEmergencyVehiclePlugin constructor.

Definition at line 41 of file approaching_emergency_vehicle_plugin_node.cpp.

43 {
44 // Create initial config
45 config_ = Config();
46
47 // Declare parameters
48 config_.passing_threshold = declare_parameter<double>("passing_threshold", config_.passing_threshold);
49 config_.approaching_threshold = declare_parameter<double>("approaching_threshold", config_.approaching_threshold);
50 config_.finished_passing_threshold = declare_parameter<double>("finished_passing_threshold", config_.finished_passing_threshold);
51 config_.min_lane_following_duration_before_lane_change = declare_parameter<double>("min_lane_following_duration_before_lane_change", config_.min_lane_following_duration_before_lane_change);
52 config_.bsm_processing_frequency = declare_parameter<double>("bsm_processing_frequency", config_.bsm_processing_frequency);
53 config_.speed_limit_reduction_during_passing = declare_parameter<double>("speed_limit_reduction_during_passing", config_.speed_limit_reduction_during_passing);
54 config_.minimum_reduced_speed_limit = declare_parameter<double>("minimum_reduced_speed_limit", config_.minimum_reduced_speed_limit);
55 config_.default_speed_limit = declare_parameter<double>("default_speed_limit", config_.default_speed_limit);
56 config_.reduced_speed_buffer = declare_parameter<double>("reduced_speed_buffer", config_.reduced_speed_buffer);
57 config_.timeout_check_frequency = declare_parameter<double>("timeout_check_frequency", config_.timeout_check_frequency);
58 config_.timeout_duration = declare_parameter<double>("timeout_duration", config_.timeout_duration);
59 config_.minimal_plan_duration = declare_parameter<double>("minimal_plan_duration", config_.minimal_plan_duration);
60 config_.buffer_distance_before_stopping = declare_parameter<double>("buffer_distance_before_stopping", config_.buffer_distance_before_stopping);
61 config_.stopping_accel_limit_multiplier = declare_parameter<double>("stopping_accel_limit_multiplier", config_.stopping_accel_limit_multiplier);
62 config_.vehicle_acceleration_limit = declare_parameter<double>("vehicle_acceleration_limit", config_.vehicle_acceleration_limit);
63 config_.route_end_point_buffer = declare_parameter<double>("route_end_point_buffer", config_.route_end_point_buffer);
64 config_.approaching_erv_status_publication_frequency = declare_parameter<double>("approaching_erv_status_publication_frequency", config_.approaching_erv_status_publication_frequency);
65 config_.warning_broadcast_frequency = declare_parameter<double>("warning_broadcast_frequency", config_.warning_broadcast_frequency);
66 config_.max_warning_broadcasts = declare_parameter<int>("max_warning_broadcasts", config_.max_warning_broadcasts);
67 config_.vehicle_length = declare_parameter<double>("vehicle_length", config_.vehicle_length);
68 config_.lane_following_plugin = declare_parameter<std::string>("lane_following_plugin", config_.lane_following_plugin);
69 config_.lane_change_plugin = declare_parameter<std::string>("lane_change_plugin", config_.lane_change_plugin);
70 config_.vehicle_id = declare_parameter<std::string>("vehicle_id", config_.vehicle_id);
71
73 }
StrategicPlugin base class which can be extended by user provided plugins which wish to implement the...
Class which implements the WorldModel interface. In addition this class provides write access to the ...

References approaching_emergency_vehicle_plugin::Config::approaching_erv_status_publication_frequency, approaching_emergency_vehicle_plugin::Config::approaching_threshold, approaching_emergency_vehicle_plugin::Config::bsm_processing_frequency, approaching_emergency_vehicle_plugin::Config::buffer_distance_before_stopping, config_, approaching_emergency_vehicle_plugin::Config::default_speed_limit, erv_world_model_, approaching_emergency_vehicle_plugin::Config::finished_passing_threshold, approaching_emergency_vehicle_plugin::Config::lane_change_plugin, approaching_emergency_vehicle_plugin::Config::lane_following_plugin, approaching_emergency_vehicle_plugin::Config::max_warning_broadcasts, approaching_emergency_vehicle_plugin::Config::min_lane_following_duration_before_lane_change, approaching_emergency_vehicle_plugin::Config::minimal_plan_duration, approaching_emergency_vehicle_plugin::Config::minimum_reduced_speed_limit, approaching_emergency_vehicle_plugin::Config::passing_threshold, approaching_emergency_vehicle_plugin::Config::reduced_speed_buffer, approaching_emergency_vehicle_plugin::Config::route_end_point_buffer, approaching_emergency_vehicle_plugin::Config::speed_limit_reduction_during_passing, approaching_emergency_vehicle_plugin::Config::stopping_accel_limit_multiplier, approaching_emergency_vehicle_plugin::Config::timeout_check_frequency, approaching_emergency_vehicle_plugin::Config::timeout_duration, approaching_emergency_vehicle_plugin::Config::vehicle_acceleration_limit, approaching_emergency_vehicle_plugin::Config::vehicle_id, approaching_emergency_vehicle_plugin::Config::vehicle_length, and approaching_emergency_vehicle_plugin::Config::warning_broadcast_frequency.

Member Function Documentation

◆ addStopAndWaitToEndOfPlan()

void approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::addStopAndWaitToEndOfPlan ( carma_planning_msgs::srv::PlanManeuvers::Response::SharedPtr  resp,
double  downtrack_progress,
double  stop_maneuver_beginning_downtrack,
double  end_of_route_downtrack,
double  stopping_entry_speed,
double  stopping_deceleration,
double  current_lanelet_ending_downtrack,
lanelet::ConstLanelet  current_lanelet,
rclcpp::Time  time_progress 
)
private

Function to add a stop and wait maneuver to the end of the maneuver plan contained in the provided 'resp' object. If there is signficant distance between the provided downtrack_progress and stop_maneuver_beginning_downtrack, a lane follow maneuver will be added first to fill this distance. Otherwise, the stop and wait maneuver will at the downtrack provided by downtrack_progress.

Parameters
respThe service response for the Plan Maneuvers service, which will be updated by this function.
downtrack_progressThe current downtrack that the first maneuver generated by this function will begin at.
stop_maneuver_beginning_downtrackThe downtrack that the stop and wait maneuver must begin by.
end_of_route_downtrackThe ending downtrack of the ego vehicle's route.
stopping_entry_speedThe estimated speed (m/s) that the vehicle will begin the stop and wait maneuver at. If a lane follow is created before the stop and wait maneuver, the lane follow maneuver will include a start_speed and target_speed equal to this speed.
current_lanelet_ending_downtrackThe ending downtrack of the current lanelet that the first maneuver generated by this function will use.
stopping_decelerationThe stopping deceleration rate for the planned stop and wait maneuver.
current_laneletThe current lanelet that the first maneuver generated by this function will use.
time_progressThe time that the first maneuver generated by this function will begin at.
Returns
None; but function updates the provided 'resp', which is the service response for the Plan Maneuvers service. The maneuver plan in this response will be updated to have a stop and wait maneuver added at its end.

Definition at line 1150 of file approaching_emergency_vehicle_plugin_node.cpp.

1154 {
1155 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Maneuver plan is reaching the end of the route");
1156
1157 // Add a lane follow maneuver before the stop and wait maneuver if the distance before stop_maneuver_beginning_downtrack is too large
1158 if((stop_maneuver_beginning_downtrack - downtrack_progress) >= config_.buffer_distance_before_stopping){
1159 // Compose lane follow maneuver and update downtrack_progress
1160 resp->new_plan.maneuvers.push_back(composeLaneFollowingManeuverMessage(downtrack_progress, stop_maneuver_beginning_downtrack,
1161 stopping_entry_speed, stopping_entry_speed, current_lanelet.id(), time_progress));
1162
1163 downtrack_progress = stop_maneuver_beginning_downtrack;
1164 time_progress += getManeuverDuration(resp->new_plan.maneuvers.back(), epsilon_);
1165 }
1166
1167 // Get the starting lanelet ID for the stop and wait maneuver
1168 lanelet::Id start_lane_id = current_lanelet.id();
1169
1170 // Identify the ending lanelet for the stop and wait maneuver
1171 while(current_lanelet_ending_downtrack < end_of_route_downtrack){
1172 // Get the next lanelet in the current lane if it exists
1173 if(!wm_->getMapRoutingGraph()->following(current_lanelet, false).empty()){
1174 current_lanelet = wm_->getMapRoutingGraph()->following(current_lanelet, false).front();
1175 }
1176 else{
1177 RCLCPP_WARN_STREAM(rclcpp::get_logger(logger_name), "A following lanelet in the current lane could not be found; returning empty plan");
1178 resp->new_plan.maneuvers = {};
1179 return;
1180 }
1181
1182 // Check whether the next lanelet is on the route
1183 if(!wm_->getRoute()->contains(current_lanelet)){
1184 RCLCPP_WARN_STREAM(rclcpp::get_logger(logger_name), "The next lanelet in the current lane is not on the route; returning empty plan");
1185 resp->new_plan.maneuvers = {};
1186 return;
1187 }
1188
1189 // Break from loop if lanelet's ending downtrack is after the route end downtrack; this is the maneuver's ending lanelet
1190 current_lanelet_ending_downtrack = wm_->routeTrackPos(current_lanelet.centerline2d().back()).downtrack;
1191 if(current_lanelet_ending_downtrack >= end_of_route_downtrack){
1192 break;
1193 }
1194 }
1195
1196 // Add stop and wait maneuver to maneuver plan
1197 resp->new_plan.maneuvers.push_back(composeStopAndWaitManeuverMessage(downtrack_progress, end_of_route_downtrack,
1198 stopping_entry_speed, start_lane_id, current_lanelet.id(), stopping_deceleration, time_progress));
1199 }
rclcpp::Duration getManeuverDuration(const carma_planning_msgs::msg::Maneuver &maneuver, double epsilon) const
Helper function to obtain the (seconds) of a provided maneuver.
carma_planning_msgs::msg::Maneuver composeLaneFollowingManeuverMessage(double start_dist, double end_dist, double start_speed, double target_speed, int lanelet_id, rclcpp::Time &start_time) const
Function to compose a lane following maneuver message based on the provided maneuver parameters.
carma_planning_msgs::msg::Maneuver composeStopAndWaitManeuverMessage(double start_dist, double end_dist, double start_speed, lanelet::Id starting_lane_id, lanelet::Id ending_lane_id, double stopping_deceleration, rclcpp::Time &start_time) const
Function to compose a stop and wait maneuver message based on the provided maneuver parameters.

References approaching_emergency_vehicle_plugin::Config::buffer_distance_before_stopping, composeLaneFollowingManeuverMessage(), composeStopAndWaitManeuverMessage(), config_, epsilon_, getManeuverDuration(), logger_name, and wm_.

Referenced by generateMoveOverManeuverPlan(), and generateReducedSpeedLaneFollowingeManeuverPlan().

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

◆ broadcastWarningToErv()

void approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::broadcastWarningToErv ( )
private

This is a callback function for the warning_broadcast_timer_, and is called to broadcast an EmergencyVehicleResponse warning message to the currently tracked ERV when the ego vehicle is in the ERV's path, but is unable to change lanes because the ERV is estimated to pass the ego vehicle in under config_.passing_threshold. It increases the num_warnings_broadcasted_ counter, and resets it to 0 when it has reached config_.max_warning_broadcasts. Additionally, when config_.max_warning_broadcasts is reached, this method sets should_broadcast_warnings_ to false.

Definition at line 249 of file approaching_emergency_vehicle_plugin_node.cpp.

249 {
251 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Sending a warning message to " << tracked_erv_.vehicle_id);
252
253 carma_v2x_msgs::msg::EmergencyVehicleResponse msg;
254 msg.m_header.sender_id = config_.vehicle_id;
255 msg.m_header.recipient_id = tracked_erv_.vehicle_id;
256
257 msg.can_change_lanes = false;
258 msg.reason_exists = true;
259 msg.reason = "Vehicle " + config_.vehicle_id + " is unable to change lanes.";
260
262
264
265 // Reset counter and boolean flag if the maximum number of warnings have been broadcasted
267 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Maximum number of warning messages sent to " << tracked_erv_.vehicle_id);
270 }
271 }
272 }
carma_ros2_utils::PubPtr< carma_v2x_msgs::msg::EmergencyVehicleResponse > outgoing_emergency_vehicle_response_pub_

References config_, has_tracked_erv_, logger_name, approaching_emergency_vehicle_plugin::Config::max_warning_broadcasts, num_warnings_broadcasted_, outgoing_emergency_vehicle_response_pub_, should_broadcast_warnings_, tracked_erv_, approaching_emergency_vehicle_plugin::Config::vehicle_id, and approaching_emergency_vehicle_plugin::ErvInformation::vehicle_id.

Referenced by on_activate_plugin().

Here is the caller graph for this function:

◆ checkForErvTimeout()

void approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::checkForErvTimeout ( )
private

This is a callback function for the erv_timeout_timer_ timer, and is called to determine whether a timeout has occurred for the currently tracked ERV by comparing the duration since the latest ERV BSM was received with config_.timeout_duration. If a timeout has occurred, an ERV_UPDATE_TIMEOUT event will be triggered for this plugin's transition_table_ object, and the has_tracked_erv_ boolean flag will be set to false.

Definition at line 235 of file approaching_emergency_vehicle_plugin_node.cpp.

235 {
236 // Trigger timeout event if a timeout has occurred for the currently tracked ERV
238 double seconds_since_prev_update = (this->get_clock()->now() - tracked_erv_.latest_update_time).seconds();
239
240 if(seconds_since_prev_update >= config_.timeout_duration){
241 RCLCPP_ERROR_STREAM(rclcpp::get_logger(logger_name), "Timeout occurred for ERV " << tracked_erv_.vehicle_id);
242 has_tracked_erv_ = false;
245 }
246 }
247 }
void event(ApproachingEmergencyVehicleEvent event)
Trigger event for the transition table.

References config_, approaching_emergency_vehicle_plugin::ERV_UPDATE_TIMEOUT, approaching_emergency_vehicle_plugin::ApproachingEmergencyVehicleTransitionTable::event(), has_planned_upcoming_lc_, has_tracked_erv_, approaching_emergency_vehicle_plugin::ErvInformation::latest_update_time, logger_name, approaching_emergency_vehicle_plugin::Config::timeout_duration, tracked_erv_, transition_table_, and approaching_emergency_vehicle_plugin::ErvInformation::vehicle_id.

Referenced by on_activate_plugin().

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

◆ composeLaneChangeManeuverMessage()

carma_planning_msgs::msg::Maneuver approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::composeLaneChangeManeuverMessage ( double  start_dist,
double  end_dist,
double  start_speed,
double  target_speed,
lanelet::Id  starting_lane_id,
lanelet::Id  ending_lane_id,
rclcpp::Time &  start_time 
) const
private

Function to compose a lane change maneuver message based on the provided maneuver parameters.

Parameters
start_distThe starting downtrack for the maneuver.
end_distThe ending downtrack for the maneuver.
start_speedThe start speed (m/s) for the maneuver.
target_speedThe target speed (m/s) for the maneuver.
starting_lane_idThe ID of the starting lanelet for this lane change maneuver.
ending_lane_idThe ID of the ending lanelet for this lane change maneuver.
start_timeThe estimated time that the ego vehicle will start this maneuver.
Returns
A lane change maneuver message composed based on the provided maneuver parameters.

Definition at line 1080 of file approaching_emergency_vehicle_plugin_node.cpp.

1082 {
1083 carma_planning_msgs::msg::Maneuver maneuver_msg;
1084 maneuver_msg.type = carma_planning_msgs::msg::Maneuver::LANE_CHANGE;
1085 maneuver_msg.lane_change_maneuver.parameters.negotiation_type = carma_planning_msgs::msg::ManeuverParameters::NO_NEGOTIATION;
1086 maneuver_msg.lane_change_maneuver.parameters.presence_vector = carma_planning_msgs::msg::ManeuverParameters::HAS_TACTICAL_PLUGIN;
1087 maneuver_msg.lane_change_maneuver.parameters.planning_tactical_plugin = config_.lane_change_plugin;
1088 maneuver_msg.lane_change_maneuver.parameters.planning_strategic_plugin = strategic_plugin_name_;
1089 maneuver_msg.lane_change_maneuver.start_dist = start_dist;
1090 maneuver_msg.lane_change_maneuver.start_speed = start_speed;
1091 maneuver_msg.lane_change_maneuver.start_time = start_time;
1092 maneuver_msg.lane_change_maneuver.end_dist = end_dist;
1093 maneuver_msg.lane_change_maneuver.end_speed = target_speed;
1094 maneuver_msg.lane_change_maneuver.starting_lane_id = std::to_string(starting_lane_id);
1095 maneuver_msg.lane_change_maneuver.ending_lane_id = std::to_string(ending_lane_id);
1096
1097 rclcpp::Duration maneuver_duration = getManeuverDuration(maneuver_msg, epsilon_);
1098 maneuver_msg.lane_change_maneuver.end_time = start_time + maneuver_duration;
1099
1100 // Preserve lane change maneuver ID from previous plan to maintain identical trajectory generation across separate maneuver plans
1102 maneuver_msg.lane_change_maneuver.parameters.maneuver_id = upcoming_lc_params_.maneuver_id;
1103 }
1104 else{
1105 static auto gen = boost::uuids::random_generator(); // Initialize uuid generator
1106 maneuver_msg.lane_change_maneuver.parameters.maneuver_id = boost::lexical_cast<std::string>(gen()); // generate uuid and convert to string
1107 }
1108
1109 RCLCPP_DEBUG_STREAM(get_logger(),"Creating lane change id: " << maneuver_msg.lane_change_maneuver.parameters.maneuver_id << "start dist: " << start_dist << " end dist: " << end_dist << " Starting llt: " << starting_lane_id << " Ending llt: " << ending_lane_id);
1110 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "start speed: " << start_speed << ", end speed: " << target_speed << ", duration: " << maneuver_duration.seconds());
1111 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "start dist: " << start_dist << ", end dist: " << end_dist);
1112
1113 return maneuver_msg;
1114 }
auto to_string(const UtmZone &zone) -> std::string
Definition: utm_zone.cpp:21

References config_, epsilon_, getManeuverDuration(), has_planned_upcoming_lc_, approaching_emergency_vehicle_plugin::Config::lane_change_plugin, logger_name, approaching_emergency_vehicle_plugin::UpcomingLaneChangeParameters::maneuver_id, strategic_plugin_name_, carma_cooperative_perception::to_string(), and upcoming_lc_params_.

Referenced by generateMoveOverManeuverPlan().

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

◆ composeLaneFollowingManeuverMessage()

carma_planning_msgs::msg::Maneuver approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::composeLaneFollowingManeuverMessage ( double  start_dist,
double  end_dist,
double  start_speed,
double  target_speed,
int  lanelet_id,
rclcpp::Time &  start_time 
) const
private

Function to compose a lane following maneuver message based on the provided maneuver parameters.

Parameters
start_distThe starting downtrack for the maneuver.
end_distThe ending downtrack for the maneuver.
start_speedThe start speed (m/s) for the maneuver.
target_speedThe target speed (m/s) for the maneuver.
lanelet_idThe ID of the lanelet that this maneuver is for.
start_timeThe estimated time that the ego vehicle will start this maneuver.
Returns
A lane following maneuver message composed based on the provided maneuver parameters.

Definition at line 1053 of file approaching_emergency_vehicle_plugin_node.cpp.

1055 {
1056 carma_planning_msgs::msg::Maneuver maneuver_msg;
1057
1058 maneuver_msg.type = carma_planning_msgs::msg::Maneuver::LANE_FOLLOWING;
1059 maneuver_msg.lane_following_maneuver.parameters.negotiation_type = carma_planning_msgs::msg::ManeuverParameters::NO_NEGOTIATION;
1060 maneuver_msg.lane_following_maneuver.parameters.presence_vector = carma_planning_msgs::msg::ManeuverParameters::HAS_TACTICAL_PLUGIN;
1061 maneuver_msg.lane_following_maneuver.parameters.planning_tactical_plugin = config_.lane_following_plugin;
1062 maneuver_msg.lane_following_maneuver.parameters.planning_strategic_plugin = strategic_plugin_name_;
1063 maneuver_msg.lane_following_maneuver.start_dist = start_dist;
1064 maneuver_msg.lane_following_maneuver.start_speed = start_speed;
1065 maneuver_msg.lane_following_maneuver.start_time = start_time;
1066 maneuver_msg.lane_following_maneuver.end_dist = end_dist;
1067 maneuver_msg.lane_following_maneuver.end_speed = target_speed;
1068 maneuver_msg.lane_following_maneuver.lane_ids = { std::to_string(lanelet_id) };
1069
1070 rclcpp::Duration maneuver_duration = getManeuverDuration(maneuver_msg, epsilon_);
1071 maneuver_msg.lane_following_maneuver.end_time = start_time + maneuver_duration;
1072
1073 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Composed lane follow maneuver for lanelet ID:" << lanelet_id << " with duration " << maneuver_duration.seconds());
1074 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "start speed: " << start_speed << ", end speed: " << target_speed);
1075 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "start dist: " << start_dist << ", end dist: " << end_dist);
1076
1077 return maneuver_msg;
1078 }

References config_, epsilon_, getManeuverDuration(), approaching_emergency_vehicle_plugin::Config::lane_following_plugin, logger_name, strategic_plugin_name_, and carma_cooperative_perception::to_string().

Referenced by addStopAndWaitToEndOfPlan(), generateMoveOverManeuverPlan(), and generateReducedSpeedLaneFollowingeManeuverPlan().

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

◆ composeStopAndWaitManeuverMessage()

carma_planning_msgs::msg::Maneuver approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::composeStopAndWaitManeuverMessage ( double  start_dist,
double  end_dist,
double  start_speed,
lanelet::Id  starting_lane_id,
lanelet::Id  ending_lane_id,
double  stopping_deceleration,
rclcpp::Time &  start_time 
) const
private

Function to compose a stop and wait maneuver message based on the provided maneuver parameters.

Parameters
start_distThe starting downtrack for the maneuver.
end_distThe ending downtrack for the maneuver.
start_speedThe start speed (m/s) for the maneuver.
starting_lane_idThe ID of the starting lanelet for this stop and wait maneuver.
ending_lane_idThe ID of the ending lanelet for this stop and wait maneuver.
stopping_decelerationThe stopping deceleration rate for the planned stop and wait maneuver.
start_timeThe estimated time that the ego vehicle will start this maneuver.
Returns
A lane change maneuver message composed based on the provided maneuver parameters.

Definition at line 1116 of file approaching_emergency_vehicle_plugin_node.cpp.

1118 {
1119 carma_planning_msgs::msg::Maneuver maneuver_msg;
1120 maneuver_msg.type = carma_planning_msgs::msg::Maneuver::STOP_AND_WAIT;
1121 maneuver_msg.stop_and_wait_maneuver.parameters.negotiation_type = carma_planning_msgs::msg::ManeuverParameters::NO_NEGOTIATION;
1122 maneuver_msg.stop_and_wait_maneuver.parameters.presence_vector = carma_planning_msgs::msg::ManeuverParameters::HAS_TACTICAL_PLUGIN
1123 | carma_planning_msgs::msg::ManeuverParameters::HAS_FLOAT_META_DATA;
1124 maneuver_msg.stop_and_wait_maneuver.parameters.planning_tactical_plugin = config_.stop_and_wait_plugin;
1125 maneuver_msg.stop_and_wait_maneuver.parameters.planning_strategic_plugin = strategic_plugin_name_;
1126 maneuver_msg.stop_and_wait_maneuver.start_time = start_time;
1127 maneuver_msg.stop_and_wait_maneuver.start_dist = start_dist;
1128 maneuver_msg.stop_and_wait_maneuver.start_speed = start_speed;
1129 maneuver_msg.stop_and_wait_maneuver.end_dist = end_dist;
1130 maneuver_msg.stop_and_wait_maneuver.starting_lane_id = std::to_string(starting_lane_id);
1131 maneuver_msg.stop_and_wait_maneuver.ending_lane_id = std::to_string(ending_lane_id);
1132
1133 rclcpp::Duration maneuver_duration = getManeuverDuration(maneuver_msg, epsilon_);
1134 maneuver_msg.stop_and_wait_maneuver.end_time = start_time + maneuver_duration;
1135
1136 // Set the meta-data for the StopAndWait Maneuver to define the buffer in the route end point stopping location
1137 maneuver_msg.stop_and_wait_maneuver.parameters.float_valued_meta_data.push_back(config_.route_end_point_buffer);
1138 maneuver_msg.stop_and_wait_maneuver.parameters.float_valued_meta_data.push_back(stopping_deceleration);
1139
1140 static auto gen = boost::uuids::random_generator(); // Initialize uuid generator
1141 maneuver_msg.stop_and_wait_maneuver.parameters.maneuver_id = boost::lexical_cast<std::string>(gen()); // generate uuid and convert to string
1142
1143 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Composed stop and wait maneuver for with start lanelet:" << starting_lane_id << ", end lanelet: " << ending_lane_id << " with duration " << maneuver_duration.seconds());
1144 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "start speed: " << start_speed);
1145 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "start dist: " << start_dist << ", end dist: " << end_dist);
1146
1147 return maneuver_msg;
1148 }

References config_, epsilon_, getManeuverDuration(), logger_name, approaching_emergency_vehicle_plugin::Config::route_end_point_buffer, approaching_emergency_vehicle_plugin::Config::stop_and_wait_plugin, strategic_plugin_name_, and carma_cooperative_perception::to_string().

Referenced by addStopAndWaitToEndOfPlan().

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

◆ filter_points_ahead()

std::vector< lanelet::BasicPoint2d > approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::filter_points_ahead ( const lanelet::BasicPoint2d &  reference_point,
const std::vector< lanelet::BasicPoint2d > &  original_points 
) const
private

Helper function that return points ahead of the given reference point accounting for the list of point's direction (idx 0 is the beginning). Used for getting route destination points ahead of the ERV.

Parameters
reference_pointReference point to check against the list of points
original_pointsList of points.
Returns
Points ahead of the reference_point. Empty if the point is determined to have passed the destination points. NOTE: if the list only has 1 point, it returns it as it is not possible to determine direction.

Definition at line 737 of file approaching_emergency_vehicle_plugin_node.cpp.

738 {
739 if (original_points.size() <= 1)
740 {
741 return original_points;
742 }
743
744 // extend the list by extrapolating last two points
745 auto extended_points = original_points;
746 double last_dx = (original_points.end() - 1 ) ->x() - (original_points.end() - 2 ) ->x();
747 double last_dy = (original_points.end() - 1 ) ->y() - (original_points.end() - 2 ) ->y();
748 lanelet::BasicPoint2d extended_point = {(original_points.end() - 1 ) ->x() + last_dx, (original_points.end() - 1 ) ->y() + last_dy};
749 extended_points.push_back(extended_point);
750
751 size_t i = 0;
752 size_t closest_idx;
753 double closest_dist = DBL_MAX;
754 while (i < extended_points.size() - 1)
755 {
756 // Calculate vectors
757 double v1x = reference_point.x() - extended_points[i].x();
758
759 double v1y = reference_point.y() - extended_points[i].y();
760 double v2x = extended_points[i+1].x() - extended_points[i].x();
761 double v2y = extended_points[i+1].y() - extended_points[i].y();
762
763 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "reference_point x: " << reference_point.x() << ", y: " << reference_point.y());
764 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "extended_points x: " << extended_points[i].x() << ", y: " << extended_points[i].y());
765
766
767 // Calculate dot product
768 double dotProduct = v1x * v2x + v1y * v2y;
769
770 // Calculate magnitudes
771 double v1Mag = sqrt(v1x * v1x + v1y * v1y);
772 double v2Mag = sqrt(v2x * v2x + v2y * v2y);
773
774 // Calculate angle in radians
775 double angleRad = acos(dotProduct / (v1Mag * v2Mag));
776
777 // Angle between the vectors above 90 degrees means the point i is ahead
778 if (angleRad >= M_PI / 2)
779 {
780 double dx = reference_point.x() - extended_points[i].x();
781 double dy = reference_point.y() - extended_points[i].y();
782 double distance = sqrt (dx * dx + dy * dy);
783 if (distance < closest_dist) // get closest point to the reference point from the multiple possible points
784 {
785 closest_dist = distance;
786 closest_idx = i;
787 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "closest_dist: " << closest_dist << ", closest_idx" << closest_idx);
788
789 }
790 }
791 i ++;
792 }
793
794 double dx = reference_point.x() - original_points.back().x();
795 double dy = reference_point.y() - original_points.back().y();
796 double distance = sqrt (dx * dx + dy * dy);
797
798 // last point is still closer to the reference point, all points have passed
799 // note that if the optimal point is the last one, this check fails as intended
800 if (distance < closest_dist)
801 {
802 RCLCPP_ERROR_STREAM(rclcpp::get_logger(logger_name), "Returning empty here");
803
804 return {};
805 }
806
807 return std::vector<lanelet::BasicPoint2d>(original_points.begin() + closest_idx, original_points.end());
808 }

References process_bag::i, logger_name, process_traj_logs::x, and process_traj_logs::y.

Referenced by generateErvRoute().

Here is the caller graph for this function:

◆ FRIEND_TEST() [1/8]

approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::FRIEND_TEST ( Testapproaching_emergency_vehicle_plugin  ,
filter_points_ahead   
)
private

◆ FRIEND_TEST() [2/8]

approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::FRIEND_TEST ( Testapproaching_emergency_vehicle_plugin  ,
testApproachingErvStatusMessage   
)
private

◆ FRIEND_TEST() [3/8]

approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::FRIEND_TEST ( Testapproaching_emergency_vehicle_plugin  ,
testBSMProcessing   
)
private

◆ FRIEND_TEST() [4/8]

approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::FRIEND_TEST ( Testapproaching_emergency_vehicle_plugin  ,
testManeuverPlanWhenMovingOverForErv   
)
private

◆ FRIEND_TEST() [5/8]

approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::FRIEND_TEST ( Testapproaching_emergency_vehicle_plugin  ,
testManeuverPlanWhenSlowingDownForErv   
)
private

◆ FRIEND_TEST() [6/8]

approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::FRIEND_TEST ( Testapproaching_emergency_vehicle_plugin  ,
testRouteConflict   
)
private

◆ FRIEND_TEST() [7/8]

approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::FRIEND_TEST ( Testapproaching_emergency_vehicle_plugin  ,
testStateMachineTransitions   
)
private

◆ FRIEND_TEST() [8/8]

approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::FRIEND_TEST ( Testapproaching_emergency_vehicle_plugin  ,
testWarningBroadcast   
)
private

◆ generateApproachingErvStatusMessage()

carma_msgs::msg::UIInstructions approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::generateApproachingErvStatusMessage ( )
private

Function to generate a carma_msgs::msg::UIInstructions message that describes whether there is currently an approaching ERV that is being tracked by this plugin. The generated message will follow the format described for this plugin's APPROACHING_ERV_STATUS_PARAMS string object.

Returns
A carma_msgs::msg::UIInstructions message with a 'msg' string field that follows the format described for this plugin's APPROACHING_ERV_STATUS_PARAMS string object.

Note: APPROACHING_ERV_STATUS_PARAMS format: "HAS_APPROACHING_ERV:%1%,TIME_UNTIL_PASSING:%2$.1f,EGO_VEHICLE_ACTION:%3%" |-----—0-------------------—1-------------------—2-----------—| Index 0: (Boolean) Value indicating whether the ego vehicle is tracking an approaching ERV Index 1: (Double; rounded to first decimal place) If an approaching ERV exists, this indicates the estimated seconds until the ERV passes the ego vehicle. Index 2: (String) If an approaching ERV exists, this describes the current action of the ego vehicle in response to the approaching ERV. NOTE: The values of indexes 1 and 2 can be ignored if index 0 indicates that no approaching ERV is being tracked.

Definition at line 280 of file approaching_emergency_vehicle_plugin_node.cpp.

280 {
281 // Initialize the message that will be returned by this function
282 carma_msgs::msg::UIInstructions status_msg;
283 status_msg.type = carma_msgs::msg::UIInstructions::INFO;
284
294 boost::format fmter(APPROACHING_ERV_STATUS_PARAMS);
295
296 // Index 0 of formatted string; indicates whether an ERV that is approaching the ego vehicle is being tracked
298 fmter %true;
299 }
300 else{
301 fmter %false;
302 }
303
304 // Index 1 of formatted string; indicates estimated time until tracked ERV passes the ego vehicle
306
307 // Add Index 2 of formatted string based on this plugin's current ApproachingEmergencyVehicleState
308 switch (transition_table_.getState())
309 {
311 fmter %"No action.";
312 break;
313
315 // Add ego vehicle action based on the direction of the upcoming lane change
317 fmter %"Approaching ERV is in our lane. Attempting to change lanes to the right.";
318 }
319 else{
320 fmter %"Approaching ERV is in our lane. Attempting to change lanes to the left.";
321 }
322
323 break;
324
326 // Add ego vehicle action based on whether the current speed is near the reduced target speed of the latest maneuver plan's first maneuver
327 if(!latest_maneuver_plan_.maneuvers.empty()){
328 // Extract the target speed in m/s from the latest maneuver plan's first maneuver and convert it to mph
329 double target_speed_ms = getManeuverEndSpeed(latest_maneuver_plan_.maneuvers[0]);
330 int target_speed_mph = std::round(target_speed_ms * METERS_PER_SEC_TO_MILES_PER_HOUR);
331
334 if(abs(current_speed_ - target_speed_ms) <= config_.reduced_speed_buffer){
335 fmter %("Approaching ERV is in our lane and a lane change is not possible. Remaining in the current lane at a reduced speed of " + std::to_string(target_speed_mph) + " mph.");
336 }
337 else{
338 fmter %("Approaching ERV is in our lane and a lane change is not possible. Remaining in the current lane and slowing down to a reduced speed of " + std::to_string(target_speed_mph) + " mph.");
339 }
340 }
341 else{
342 int non_reduced_speed_to_maintain_mph = std::round(non_reduced_speed_to_maintain_ * METERS_PER_SEC_TO_MILES_PER_HOUR);
343 fmter %("Approaching ERV is in our lane and a lane change is not possible. Remaining in the current lane and maintaining a speed of " + std::to_string(non_reduced_speed_to_maintain_mph) + " mph.");
344 }
345 }
346 else{
347 if(abs(current_speed_ - target_speed_ms) <= config_.reduced_speed_buffer){
348 fmter %("Approaching ERV is in adjacent lane. Remaining in the current lane at a reduced speed of " + std::to_string(target_speed_mph) + " mph.");
349 }
350 else{
351 fmter %("Approaching ERV is in adjacent lane. Remaining in the current lane and slowing down to a reduced speed of " + std::to_string(target_speed_mph) + " mph.");
352 }
353 }
354 }
355 else{
356 throw std::invalid_argument("State is SLOWING_DOWN_FOR_ERV but latest maneuver plan is empty!");
357 }
358 break;
359
360 default:
361 throw std::invalid_argument("Transition table in unsupported state");
362 }
363
364 // Add formatted string to status message
365 std::string str = fmter.str();
366 status_msg.msg = str;
367
368 return status_msg;
369 }
double getManeuverEndSpeed(const carma_planning_msgs::msg::Maneuver &mvr)
Anonymous function to extract maneuver end speed which cannot be obtained with GET_MANEUVER_PROPERY c...

References APPROACHING_ERV_STATUS_PARAMS, approaching_emergency_vehicle_plugin::Config::approaching_threshold, config_, current_speed_, ego_lane_index_, approaching_emergency_vehicle_plugin::anonymous_namespace{approaching_emergency_vehicle_plugin_node.cpp}::getManeuverEndSpeed(), approaching_emergency_vehicle_plugin::ApproachingEmergencyVehicleTransitionTable::getState(), has_tracked_erv_, is_maintaining_non_reduced_speed_, approaching_emergency_vehicle_plugin::UpcomingLaneChangeParameters::is_right_lane_change, approaching_emergency_vehicle_plugin::ErvInformation::lane_index, latest_maneuver_plan_, approaching_emergency_vehicle_plugin::METERS_PER_SEC_TO_MILES_PER_HOUR, approaching_emergency_vehicle_plugin::MOVING_OVER_FOR_APPROACHING_ERV, approaching_emergency_vehicle_plugin::NO_APPROACHING_ERV, non_reduced_speed_to_maintain_, approaching_emergency_vehicle_plugin::Config::reduced_speed_buffer, approaching_emergency_vehicle_plugin::ErvInformation::seconds_until_passing, approaching_emergency_vehicle_plugin::SLOWING_DOWN_FOR_ERV, carma_cooperative_perception::to_string(), tracked_erv_, transition_table_, and upcoming_lc_params_.

Referenced by publishApproachingErvStatus().

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

◆ generateErvRoute()

lanelet::Optional< lanelet::routing::Route > approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::generateErvRoute ( double  current_latitude,
double  current_longitude,
std::vector< carma_v2x_msgs::msg::Position3D >  erv_destination_points 
)
private

Helper function to generate an ERV's route based on its current position and its future route destination points.

Parameters
current_latitudeThe current latitude of the ERV.
current_longitudeThe current longitude of the ERV.
erv_destination_pointsThe ERV's future destination points.
Returns
An optional lanelet::routing::Route object, which is empty if the generation of the ERV's route was not successful.

Definition at line 633 of file approaching_emergency_vehicle_plugin_node.cpp.

634 {
635 // Check if the map projection is available
636 if (!map_projector_) {
637 throw std::invalid_argument("Attempting to generate an ERV's route before map projection was set");
638 }
639
640 // Build map projector
641 lanelet::projection::LocalFrameProjector projector(map_projector_.get().c_str());
642
643 // Create vector to hold ERV's destination points
644 std::vector<lanelet::BasicPoint3d> erv_destination_points_projected;
645
646 // Add ERV's current location to the beginning of erv_destination_points
647 lanelet::GPSPoint current_erv_location;
648
649 current_erv_location.lat = current_latitude;
650 current_erv_location.lon = current_longitude;
651
652
653
654 // Add ERV's future destination points to erv_destination_points
655 if(erv_destination_points.size() > 0){
656 for(size_t i = 0; i < erv_destination_points.size(); ++i){
657 carma_v2x_msgs::msg::Position3D position_3d_point = erv_destination_points[i];
658
659 lanelet::GPSPoint erv_destination_point;
660 erv_destination_point.lon = position_3d_point.longitude;
661 erv_destination_point.lat = position_3d_point.latitude;
662
663 if(position_3d_point.elevation_exists){
664 erv_destination_point.ele = position_3d_point.elevation;
665 }
666
667 erv_destination_points_projected.emplace_back(projector.forward(erv_destination_point));
668 }
669 }
670
671 // Convert ERV destination points to map frame
672 auto erv_destination_points_in_map = lanelet::utils::transform(erv_destination_points_projected, [](auto a) { return lanelet::traits::to2D(a); });
673
674 auto cmv_location = lanelet::traits::to2D(projector.forward(current_erv_location));
675 auto shortened_erv_destination_points_in_map = filter_points_ahead(cmv_location, erv_destination_points_in_map);
676
677 if(shortened_erv_destination_points_in_map.empty())
678 {
679 RCLCPP_ERROR_STREAM(rclcpp::get_logger(logger_name), "ERV has passed all the destination points!");
680
681 // Return empty route
682 return lanelet::Optional<lanelet::routing::Route>();
683 }
684
685 // Verify that ERV destination points are geometrically in the map
686 for(size_t i = 0; i < shortened_erv_destination_points_in_map.size(); ++i){
687 auto pt = shortened_erv_destination_points_in_map[i];
688
689 // Return empty route if a destination point is not contained within the map
690 if((wm_->getLaneletsFromPoint(shortened_erv_destination_points_in_map[i])).empty()){
691 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "ERV destination point " << i
692 << " is not contained in a lanelet map; x: " << pt.x() << " y: " << pt.y());
693
694 return lanelet::Optional<lanelet::routing::Route>();
695 }
696 }
697
698 // Obtain ERV's starting lanelet
699 auto starting_lanelet_vector = lanelet::geometry::findNearest(wm_->getMap()->laneletLayer, cmv_location, 1);
700 if(starting_lanelet_vector.empty())
701 {
702 RCLCPP_ERROR_STREAM(rclcpp::get_logger(logger_name), "Found no lanelets in the map. ERV routing cannot be completed.");
703
704 // Return empty route
705 return lanelet::Optional<lanelet::routing::Route>();
706 }
707 auto starting_lanelet = lanelet::ConstLanelet(starting_lanelet_vector[0].second.constData());
708
709 // Obtain ERV's ending lanelet
710 auto ending_lanelet_vector = lanelet::geometry::findNearest(wm_->getMap()->laneletLayer, shortened_erv_destination_points_in_map.back(), 1);
711 auto ending_lanelet = lanelet::ConstLanelet(ending_lanelet_vector[0].second.constData());
712 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "ending_lanelet: " << ending_lanelet.id());
713 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "starting_lanelet: " << starting_lanelet.id());
714
715 // Obtain ERV's via lanelets
716 std::vector<lanelet::BasicPoint2d> via = std::vector<lanelet::BasicPoint2d>(shortened_erv_destination_points_in_map.begin(), shortened_erv_destination_points_in_map.end() - 1);
717 lanelet::ConstLanelets via_lanelets_vector;
718 for(const auto& point : via){
719 auto lanelet_vector = lanelet::geometry::findNearest(wm_->getMap()->laneletLayer, point, 1);
720 auto chosen_lanelet_to_emplace = lanelet::ConstLanelet(lanelet_vector[0].second.constData());
721 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "chosen_lanelet_to_emplace: " << chosen_lanelet_to_emplace.id());
722
723 if (chosen_lanelet_to_emplace.id() != starting_lanelet.id()) // if id is same, it fails to route
724 via_lanelets_vector.emplace_back(chosen_lanelet_to_emplace);
725 else
726 {
727 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "======> id was found same: " << chosen_lanelet_to_emplace.id());
728 }
729 }
730
731 // Generate the ERV's route
732 auto erv_route = wm_->getMapRoutingGraph()->getRouteVia(starting_lanelet, via_lanelets_vector, ending_lanelet);
733
734 return erv_route;
735 }
std::vector< lanelet::BasicPoint2d > filter_points_ahead(const lanelet::BasicPoint2d &reference_point, const std::vector< lanelet::BasicPoint2d > &original_points) const
Helper function that return points ahead of the given reference point accounting for the list of poin...

References filter_points_ahead(), process_bag::i, logger_name, map_projector_, process_traj_logs::point, and wm_.

Referenced by getErvInformationFromBsm().

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

◆ generateMoveOverManeuverPlan()

void approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::generateMoveOverManeuverPlan ( carma_planning_msgs::srv::PlanManeuvers::Response::SharedPtr  resp,
lanelet::ConstLanelet  current_lanelet,
double  downtrack_progress,
double  current_lanelet_ending_downtrack,
double  speed_progress,
double  target_speed,
rclcpp::Time  time_progress,
int  ego_lane_index,
int  erv_lane_index 
)
private

Function to generate a maneuver plan when the ego vehicle must change lanes due to being in the MOVING_OVER_FOR_APPROACHING_ERV state. If both the ego vehicle and the ERV are in the rightmost lane, a left lane change will be contained in the plan; otherwise a right lane change will be planned.

Parameters
respThe service response for the Plan Maneuvers service, which will be updated by this function.
current_laneletThe current lanelet that the first maneuver generated by this function will use.
downtrack_progressThe current downtrack that the first maneuver generated by this function will begin at.
current_lanelet_ending_downtrackThe ending downtrack of the current lanelet that the first maneuver generated by this function will use.
speed_progressThe current speed that the first maneuver generated by this function will begin at.
target_speedThe target speed of the initial lane follow maneuver generated by this function.
time_progressThe time that the first maneuver generated by this function will begin at.
ego_lane_indexThe ego vehicle's current lane index (NOTE: For 'lane index', 0 is rightmost lane, 1 is second rightmost, etc.; Only the current travel direction is considered)
erv_lane_indexThe ERV's current lane index (NOTE: For 'lane index', 0 is rightmost lane, 1 is second rightmost, etc.; Only the current travel direction is considered)
Returns
None; but function updates the provided 'resp', which is the service response for the Plan Maneuvers service. The maneuver plan in this response will be updated to contain lane following maneuvers along with one lane change maneuver. A stop and wait maneuver will be included at the end if the maneuver plan reaches the end of the route.

Definition at line 1295 of file approaching_emergency_vehicle_plugin_node.cpp.

1299 {
1300 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Generating move-over maneuver plan");
1301
1302 double maneuver_plan_ending_downtrack = downtrack_progress + config_.minimal_plan_duration * target_speed;
1303 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Ending downtrack based on plan duration: " << maneuver_plan_ending_downtrack);
1304
1305 maneuver_plan_ending_downtrack = std::min(maneuver_plan_ending_downtrack, wm_->getRouteEndTrackPos().downtrack);
1306 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Ending downtrack based on end of route: " << wm_->getRouteEndTrackPos().downtrack);
1307 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Selected ending downtrack: " << maneuver_plan_ending_downtrack);
1308
1309 // Compute target deceleration for stopping
1311
1312 // Compute stopping distance where v_f = 0
1313 // (v_f^2 - v_i^2) / (2*a) = d
1314 double stopping_distance = (target_speed * target_speed) / (2.0 * stopping_deceleration);
1315 double begin_stopping_downtrack = wm_->getRouteEndTrackPos().downtrack - stopping_distance;
1316 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Stop and wait maneuver must begin by downtrack " << begin_stopping_downtrack);
1317
1318 // Generate maneuver plan when there is already a planned upcoming lane change
1320 while(downtrack_progress < maneuver_plan_ending_downtrack){
1321 if(begin_stopping_downtrack <= current_lanelet_ending_downtrack){
1322 // Complete maneuver plan with a stop and wait maneuver if the current lanelet intercepts the stopping downtrack
1323 addStopAndWaitToEndOfPlan(resp, downtrack_progress, begin_stopping_downtrack, wm_->getRouteEndTrackPos().downtrack,
1324 speed_progress, stopping_deceleration, current_lanelet_ending_downtrack, current_lanelet, time_progress);
1325 return;
1326 }
1327 else{
1328 // Plan lane change maneuver if current downtrack progress is between the starting and ending downtracks of the planned upcoming lane change, otherwise plan lane follow maneuver
1329 if(((upcoming_lc_params_.start_dist - epsilon_) <= downtrack_progress) && (downtrack_progress < (upcoming_lc_params_.end_dist - epsilon_))){
1332
1333 current_lanelet = upcoming_lc_params_.ending_lanelet;
1334 }
1335 else{
1336 resp->new_plan.maneuvers.push_back(composeLaneFollowingManeuverMessage(downtrack_progress, current_lanelet_ending_downtrack,
1337 speed_progress, target_speed, current_lanelet.id(), time_progress));
1338 }
1339
1340 // Get the next lanelet in the current lane if it exists
1341 if(!wm_->getMapRoutingGraph()->following(current_lanelet, false).empty())
1342 {
1343 current_lanelet = wm_->getMapRoutingGraph()->following(current_lanelet, false).front();
1344 }
1345 else
1346 {
1347 RCLCPP_WARN_STREAM(rclcpp::get_logger(logger_name), "A following lanelet in the current lane could not be found; returning empty plan");
1348 resp->new_plan.maneuvers = {};
1349 return;
1350 }
1351
1352 // Check whether the next lanelet is on the route
1353 if(!wm_->getRoute()->contains(current_lanelet)){
1354 RCLCPP_WARN_STREAM(rclcpp::get_logger(logger_name), "The next lanelet in the current lane is not on the route; returning empty plan");
1355 resp->new_plan.maneuvers = {};
1356 return;
1357 }
1358
1359 // Update the maneuver parameters for the next maneuver in the plan
1360 downtrack_progress = wm_->routeTrackPos(current_lanelet.centerline2d().front()).downtrack;
1361 current_lanelet_ending_downtrack = wm_->routeTrackPos(current_lanelet.centerline2d().back()).downtrack;
1362 speed_progress = getManeuverEndSpeed(resp->new_plan.maneuvers.back());
1363 target_speed = getLaneletSpeedLimit(current_lanelet);
1364 time_progress += getManeuverDuration(resp->new_plan.maneuvers.back(), epsilon_);
1365
1366 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Next maneuver starting downtrack is " << downtrack_progress << ", end of plan is at " << maneuver_plan_ending_downtrack);
1367 }
1368 }
1369 }
1370 else{
1371 bool completed_initial_lane_following = false; // Flag to indicate whether sufficient lane following has been planned before the lane change
1372 double initial_lane_following_duration = 0.0; // Object to store the duration of lane following planned before the lane change
1373
1374 while(downtrack_progress < maneuver_plan_ending_downtrack){
1375
1376 if(begin_stopping_downtrack <= current_lanelet_ending_downtrack){
1377 // Complete maneuver plan with a stop and wait maneuver if the current lanelet intercepts the stopping downtrack
1378 addStopAndWaitToEndOfPlan(resp, downtrack_progress, begin_stopping_downtrack, wm_->getRouteEndTrackPos().downtrack,
1379 speed_progress, stopping_deceleration, current_lanelet_ending_downtrack, current_lanelet, time_progress);
1380 return;
1381 }
1382 else{
1383 // First maneuver(s) are lane-following to enable sufficient time to activate turn signals before conducting lane change
1384 if(!completed_initial_lane_following){
1385 resp->new_plan.maneuvers.push_back(composeLaneFollowingManeuverMessage(downtrack_progress, current_lanelet_ending_downtrack,
1386 speed_progress, target_speed, current_lanelet.id(), time_progress));
1387
1388 initial_lane_following_duration += getManeuverDuration(resp->new_plan.maneuvers.back(), epsilon_).seconds();
1389 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Seconds of lane following before lane change: " << initial_lane_following_duration);
1390
1391 if(initial_lane_following_duration >= config_.min_lane_following_duration_before_lane_change){
1392 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Minimum lane following duration before lane change satisfied.");
1393 completed_initial_lane_following = true;
1394 }
1395 }
1396 else{
1397
1399 // Initialize UpcomingLaneChangeParameters object to store information regarding the planned lane change maneuver
1400 UpcomingLaneChangeParameters new_lc_params;
1401
1402 // Determine if left lane change or right lane change required, and get the target lanelet
1403 lanelet::Optional<lanelet::ConstLanelet> target_lanelet;
1404 if(ego_lane_index != erv_lane_index){
1405 RCLCPP_WARN_STREAM(rclcpp::get_logger(logger_name), "Planning a move-over maneuver plan when the ego vehicle is not in the ERV's path! Returning an empty maneuver plan");
1406 resp->new_plan.maneuvers = {};
1407 return;
1408 }
1409 else if((ego_lane_index == 0) && (erv_lane_index == 0)){
1410 // Ego vehicle and ERV are both in the rightmost lane so a left lane change is required
1411 new_lc_params.is_right_lane_change = false;
1412 target_lanelet = wm_->getMapRoutingGraph()->left(current_lanelet);
1413 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Composing a left lane change maneuver from lanelet " << current_lanelet.id());
1414 }
1415 else{
1416 // A right lane change is required
1417 new_lc_params.is_right_lane_change = true;
1418 target_lanelet = wm_->getMapRoutingGraph()->right(current_lanelet);
1419 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Composing a right lane change maneuver from lanelet " << current_lanelet.id());
1420 }
1421
1422 // Only compose lane change maneuver if the target lanelet exists, otherwise compose lane follow maneuver
1423 if(target_lanelet){
1424 resp->new_plan.maneuvers.push_back(composeLaneChangeManeuverMessage(downtrack_progress, current_lanelet_ending_downtrack,
1425 speed_progress, target_speed, current_lanelet.id(), target_lanelet.get().id(), time_progress));
1426
1428
1429 new_lc_params.starting_lanelet = current_lanelet;
1430 new_lc_params.ending_lanelet = target_lanelet.get();
1431 new_lc_params.start_dist = downtrack_progress;
1432 new_lc_params.end_dist = current_lanelet_ending_downtrack;
1433 new_lc_params.start_speed = speed_progress;
1434 new_lc_params.end_speed = target_speed;
1435 new_lc_params.maneuver_id = resp->new_plan.maneuvers.back().lane_change_maneuver.parameters.maneuver_id;
1436 upcoming_lc_params_ = new_lc_params;
1437
1438 current_lanelet = *target_lanelet;
1439 }
1440 else{
1441 RCLCPP_WARN_STREAM(rclcpp::get_logger(logger_name), "A lane change maneuver from " << current_lanelet.id() << " could not be composed since no target lanelet was found!");
1442 resp->new_plan.maneuvers.push_back(composeLaneFollowingManeuverMessage(downtrack_progress, current_lanelet_ending_downtrack,
1443 speed_progress, target_speed, current_lanelet.id(), time_progress));
1444 }
1445 }
1446 else{
1447 resp->new_plan.maneuvers.push_back(composeLaneFollowingManeuverMessage(downtrack_progress, current_lanelet_ending_downtrack,
1448 speed_progress, target_speed, current_lanelet.id(), time_progress));
1449 }
1450 }
1451
1452 // Get the next lanelet in the current lane if it exists
1453 if(!wm_->getMapRoutingGraph()->following(current_lanelet, false).empty())
1454 {
1455 current_lanelet = wm_->getMapRoutingGraph()->following(current_lanelet, false).front();
1456 }
1457 else
1458 {
1459 RCLCPP_WARN_STREAM(rclcpp::get_logger(logger_name), "A following lanelet in the current lane could not be found; returning empty plan");
1460 resp->new_plan.maneuvers = {};
1461 return;
1462 }
1463
1464 // Check whether the next lanelet is on the route
1465 if(!wm_->getRoute()->contains(current_lanelet)){
1466 RCLCPP_WARN_STREAM(rclcpp::get_logger(logger_name), "The next lanelet in the current lane is not on the route; returning empty plan");
1467 resp->new_plan.maneuvers = {};
1468 return;
1469 }
1470
1471 // Update the maneuver parameters for the next maneuver in the plan
1472 downtrack_progress = wm_->routeTrackPos(current_lanelet.centerline2d().front()).downtrack;
1473 current_lanelet_ending_downtrack = wm_->routeTrackPos(current_lanelet.centerline2d().back()).downtrack;
1474 speed_progress = getManeuverEndSpeed(resp->new_plan.maneuvers.back());
1475 target_speed = getLaneletSpeedLimit(current_lanelet);
1476 time_progress += getManeuverDuration(resp->new_plan.maneuvers.back(), epsilon_);
1477
1478 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Next maneuver starting downtrack is " << downtrack_progress << ", end of plan is at " << maneuver_plan_ending_downtrack);
1479 }
1480 }
1481 }
1482 }
void addStopAndWaitToEndOfPlan(carma_planning_msgs::srv::PlanManeuvers::Response::SharedPtr resp, double downtrack_progress, double stop_maneuver_beginning_downtrack, double end_of_route_downtrack, double stopping_entry_speed, double stopping_deceleration, double current_lanelet_ending_downtrack, lanelet::ConstLanelet current_lanelet, rclcpp::Time time_progress)
Function to add a stop and wait maneuver to the end of the maneuver plan contained in the provided 'r...
carma_planning_msgs::msg::Maneuver composeLaneChangeManeuverMessage(double start_dist, double end_dist, double start_speed, double target_speed, lanelet::Id starting_lane_id, lanelet::Id ending_lane_id, rclcpp::Time &start_time) const
Function to compose a lane change maneuver message based on the provided maneuver parameters.
double getLaneletSpeedLimit(const lanelet::ConstLanelet &lanelet)
Helper function to extract the speed limit (m/s) from a provided lanelet.

References addStopAndWaitToEndOfPlan(), composeLaneChangeManeuverMessage(), composeLaneFollowingManeuverMessage(), config_, approaching_emergency_vehicle_plugin::UpcomingLaneChangeParameters::end_dist, approaching_emergency_vehicle_plugin::UpcomingLaneChangeParameters::end_speed, approaching_emergency_vehicle_plugin::UpcomingLaneChangeParameters::ending_lanelet, epsilon_, getLaneletSpeedLimit(), getManeuverDuration(), approaching_emergency_vehicle_plugin::anonymous_namespace{approaching_emergency_vehicle_plugin_node.cpp}::getManeuverEndSpeed(), has_planned_upcoming_lc_, approaching_emergency_vehicle_plugin::UpcomingLaneChangeParameters::is_right_lane_change, logger_name, approaching_emergency_vehicle_plugin::UpcomingLaneChangeParameters::maneuver_id, approaching_emergency_vehicle_plugin::Config::min_lane_following_duration_before_lane_change, approaching_emergency_vehicle_plugin::Config::minimal_plan_duration, approaching_emergency_vehicle_plugin::UpcomingLaneChangeParameters::start_dist, approaching_emergency_vehicle_plugin::UpcomingLaneChangeParameters::start_speed, approaching_emergency_vehicle_plugin::UpcomingLaneChangeParameters::starting_lanelet, approaching_emergency_vehicle_plugin::Config::stopping_accel_limit_multiplier, upcoming_lc_params_, approaching_emergency_vehicle_plugin::Config::vehicle_acceleration_limit, and wm_.

Referenced by plan_maneuvers_callback().

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

◆ generateReducedSpeedLaneFollowingeManeuverPlan()

void approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::generateReducedSpeedLaneFollowingeManeuverPlan ( carma_planning_msgs::srv::PlanManeuvers::Response::SharedPtr  resp,
lanelet::ConstLanelet  current_lanelet,
double  downtrack_progress,
double  current_lanelet_ending_downtrack,
double  speed_progress,
double  target_speed,
rclcpp::Time  time_progress,
bool  is_maintaining_non_reduced_speed 
)
private

Function to generate a maneuver plan when the ego vehicle must remain in its lane due to being in the SLOWING_DOWN_FOR_ERV state. The target speed of lane follow maneuvers in the generated plan will have a reduced value.

Parameters
respThe service response for the Plan Maneuvers service, which will be updated by this function.
current_laneletThe current lanelet that the first maneuver generated by this function will use.
downtrack_progressThe current downtrack that the first maneuver generated by this function will begin at.
current_lanelet_ending_downtrackThe ending downtrack of the current lanelet that the first maneuver generated by this function will use.
speed_progressThe current speed that the first maneuver generated by this function will begin at.
target_speedThe target speed of the initial lane follow maneuver generated by this function.
time_progressThe time that the first maneuver generated by this function will begin at.
is_maintaining_non_reduced_speedFlag to indicate whether the ego vehicle should maintain its current speed since it is in the same lane as the approaching ERV, and the time until the ERV will pass the ego vehicle is so slow that if the ego vehicle slows down, it could cause a safety hazard.
Returns
None; but function updates the provided 'resp', which is the service response for the Plan Maneuvers service. The maneuver plan in this response will be updated to contain all lane following maneuvers. A stop and wait maneuver will be included at the end if the maneuver plan reaches the end of the route.

Definition at line 1201 of file approaching_emergency_vehicle_plugin_node.cpp.

1205 {
1206 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Generating remain-in-lane maneuver plan");
1207
1208 // Reset planned lane change flag since plugin will not be planning for an upcoming lane change
1210
1211 // Update maneuver target speed to be a speed being maintained or a reduced speed below the speed limit
1212 if(is_maintaining_non_reduced_speed){
1213 // ERV is close enough that if the ego vehicle slows down it could cause a safety hazard; set target speed to maintain a non-reduced speed
1214 target_speed = non_reduced_speed_to_maintain_;
1215 }
1216 else{
1217 // Reduce target_speed since this method is triggered when an ERV is approaching the ego vehicle
1218 target_speed = std::max((target_speed - config_.speed_limit_reduction_during_passing), config_.minimum_reduced_speed_limit);
1219 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Maneuver target speed reduced to " << target_speed);
1220 }
1221
1222 double maneuver_plan_ending_downtrack = downtrack_progress + config_.minimal_plan_duration * target_speed;
1223 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Ending downtrack based on plan duration: " << maneuver_plan_ending_downtrack);
1224
1225 maneuver_plan_ending_downtrack = std::min(maneuver_plan_ending_downtrack, wm_->getRouteEndTrackPos().downtrack);
1226 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Ending downtrack based on end of route: " << wm_->getRouteEndTrackPos().downtrack);
1227 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Selected ending downtrack: " << maneuver_plan_ending_downtrack);
1228
1229 // Compute target deceleration for stopping
1231
1232 // Compute stopping distance where v_f = 0
1233 // (v_f^2 - v_i^2) / (2*a) = d
1234 double stopping_distance = (target_speed * target_speed) / (2.0 * stopping_deceleration);
1235 double begin_stopping_downtrack = wm_->getRouteEndTrackPos().downtrack - stopping_distance;
1236 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Stop and wait maneuver must begin by downtrack " << begin_stopping_downtrack);
1237
1238 // Generate maneuver plan
1239 while(downtrack_progress < maneuver_plan_ending_downtrack){
1240
1241 if(begin_stopping_downtrack <= current_lanelet_ending_downtrack){
1242 // Complete maneuver plan with a stop and wait maneuver if the current lanelet intercepts the stopping downtrack
1243 addStopAndWaitToEndOfPlan(resp, downtrack_progress, begin_stopping_downtrack, wm_->getRouteEndTrackPos().downtrack,
1244 speed_progress, stopping_deceleration, current_lanelet_ending_downtrack, current_lanelet, time_progress);
1245 return;
1246 }
1247 else{
1248 // Compose lane following maneuver and add it to the response's maneuver plan
1249 resp->new_plan.maneuvers.push_back(composeLaneFollowingManeuverMessage(downtrack_progress, current_lanelet_ending_downtrack,
1250 speed_progress, target_speed, current_lanelet.id(), time_progress));
1251
1252 // Get the next lanelet in the current lane if it exists
1253 if(!wm_->getMapRoutingGraph()->following(current_lanelet, false).empty())
1254 {
1255 current_lanelet = wm_->getMapRoutingGraph()->following(current_lanelet, false).front();
1256 }
1257 else
1258 {
1259 RCLCPP_WARN_STREAM(rclcpp::get_logger(logger_name), "A following lanelet in the current lane could not be found; returning empty plan");
1260 resp->new_plan.maneuvers = {};
1261 return;
1262 }
1263
1264 // Check whether the next lanelet is on the route
1265 if(!wm_->getRoute()->contains(current_lanelet)){
1266 RCLCPP_WARN_STREAM(rclcpp::get_logger(logger_name), "The next lanelet in the current lane is not on the route; returning empty plan");
1267 resp->new_plan.maneuvers = {};
1268 return;
1269 }
1270
1271 // Update the lane follow maneuver parameters for the next maneuver in the plan
1272 downtrack_progress = wm_->routeTrackPos(current_lanelet.centerline2d().front()).downtrack;
1273 current_lanelet_ending_downtrack = wm_->routeTrackPos(current_lanelet.centerline2d().back()).downtrack;
1274 speed_progress = getManeuverEndSpeed(resp->new_plan.maneuvers.back());
1275 target_speed = getLaneletSpeedLimit(current_lanelet);
1276
1277 // Update maneuver target speed to be a speed being maintained or a reduced speed below the speed limit
1278 if(is_maintaining_non_reduced_speed){
1279 // ERV is close enough that if the ego vehicle slows down it could cause a safety hazard; set target speed to maintain a non-reduced speed
1280 target_speed = non_reduced_speed_to_maintain_;
1281 }
1282 else{
1283 // Reduce target_speed if an ERV is actively passing the ego vehicle
1284 target_speed = std::max((target_speed - config_.speed_limit_reduction_during_passing), config_.minimum_reduced_speed_limit);
1285 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Maneuver target speed reduced to " << target_speed);
1286 }
1287
1288 time_progress += getManeuverDuration(resp->new_plan.maneuvers.back(), epsilon_);
1289
1290 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Next maneuver starting downtrack is " << downtrack_progress << ", end of plan is at " << maneuver_plan_ending_downtrack);
1291 }
1292 }
1293 }

References addStopAndWaitToEndOfPlan(), composeLaneFollowingManeuverMessage(), config_, epsilon_, getLaneletSpeedLimit(), getManeuverDuration(), approaching_emergency_vehicle_plugin::anonymous_namespace{approaching_emergency_vehicle_plugin_node.cpp}::getManeuverEndSpeed(), has_planned_upcoming_lc_, logger_name, approaching_emergency_vehicle_plugin::Config::minimal_plan_duration, approaching_emergency_vehicle_plugin::Config::minimum_reduced_speed_limit, non_reduced_speed_to_maintain_, approaching_emergency_vehicle_plugin::Config::speed_limit_reduction_during_passing, approaching_emergency_vehicle_plugin::Config::stopping_accel_limit_multiplier, approaching_emergency_vehicle_plugin::Config::vehicle_acceleration_limit, and wm_.

Referenced by plan_maneuvers_callback().

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

◆ georeferenceCallback()

void approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::georeferenceCallback ( const std_msgs::msg::String::UniquePtr  msg)

Subscription callback for the georeference.

Parameters
msgThe latest georeference message.

Definition at line 614 of file approaching_emergency_vehicle_plugin_node.cpp.

615 {
616 // Build projector from proj string
617 map_projector_ = msg->data;
618 }

References map_projector_.

Referenced by on_configure_plugin().

Here is the caller graph for this function:

◆ get_availability()

bool approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::get_availability ( )
overridevirtual

Get the availability status of this plugin based on the current operating environment. Method must be overriden by extending classes.

Returns
This method should return true if the plugin's current understanding of the world means it would be capable of planning or executing its capability.

Implements carma_guidance_plugins::PluginBaseNode.

Definition at line 1635 of file approaching_emergency_vehicle_plugin_node.cpp.

1635 {
1636 return true;
1637 }

◆ get_version_id()

std::string approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::get_version_id ( )
overridevirtual

Returns the version id of this plugin.

Returns
The version id represented as a string

Implements carma_guidance_plugins::PluginBaseNode.

Definition at line 1639 of file approaching_emergency_vehicle_plugin_node.cpp.

1639 {
1640 return "v1.0";
1641 }

◆ getErvInformationFromBsm()

boost::optional< ErvInformation > approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::getErvInformationFromBsm ( carma_v2x_msgs::msg::BSM::UniquePtr  msg)
private

Through internal logic and calls to separate helper functions, this function processes a received BSM to obtain all necessary information for an ERV to be tracked. This "necessary information" includes all fields contained within an ErvInformation struct.

Parameters
msgThe BSM message to be processed. It may or may not be from an active ERV.
Returns
An optional ErvInformation object, which is empty if BSM processing could not extract all necessary information required for an ERV to be tracked.

Definition at line 401 of file approaching_emergency_vehicle_plugin_node.cpp.

401 {
402 // Initialize ErvInformation object, which will be populated with information from this BSM if it is from an ERV
403 ErvInformation erv_information;
404
405 // Get vehicle_id from the BSM
406 std::stringstream ss;
407 for(size_t i = 0; i < msg->core_data.id.size(); ++i){
408 ss << std::setfill('0') << std::setw(2) << std::hex << (unsigned)msg->core_data.id.at(i);
409 }
410 erv_information.vehicle_id = ss.str();
411
412 // Check whether vehicle's lights and sirens are active
413 bool has_active_lights_and_sirens = false;
414 if(msg->presence_vector & carma_v2x_msgs::msg::BSM::HAS_PART_II){
415
416 // Parse BSM Part II Content to determine whether the vehicle's lights and sirens are active
417 if(msg->part_ii.size() > 0){
418 for(size_t i = 0; i < msg->part_ii.size(); ++i){
419
420 // special_vehicle_extensions contain the status of the vehicle's lights and sirens
421 if(msg->part_ii[i].part_ii_id == carma_v2x_msgs::msg::BSMPartIIExtension::SPECIAL_VEHICLE_EXT){
422 carma_v2x_msgs::msg::SpecialVehicleExtensions special_vehicle_ext = msg->part_ii[i].special_vehicle_extensions;
423
424 if(special_vehicle_ext.presence_vector & carma_v2x_msgs::msg::SpecialVehicleExtensions::HAS_VEHICLE_ALERTS){
425 if(special_vehicle_ext.vehicle_alerts.siren_use.siren_in_use == j2735_v2x_msgs::msg::SirenInUse::IN_USE
426 && special_vehicle_ext.vehicle_alerts.lights_use.lightbar_in_use == j2735_v2x_msgs::msg::LightbarInUse::IN_USE){
427 has_active_lights_and_sirens = true;
428 }
429 }
430 }
431 }
432 }
433 }
434
435 if(!has_active_lights_and_sirens){
436 // BSM is not a valid ERV BSM since the lights and sirens are not both active; return an empty object
437 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "BSM is not a valid ERV BSM since the lights and sirens are not both active; return an empty object");
438
439 return boost::optional<ErvInformation>();
440 }
441
442 // Get vehicle's current speed from the BSM
443 if(msg->core_data.presence_vector & carma_v2x_msgs::msg::BSMCoreData::SPEED_AVAILABLE){
444 erv_information.current_speed = msg->core_data.speed;
445 }
446 else{
447 // BSM is not a valid ERV BSM since current speed is not included; return an empty object
448 RCLCPP_ERROR_STREAM(rclcpp::get_logger(logger_name), "BSM is not a valid ERV BSM since current speed is not included; return an empty object");
449
450 return boost::optional<ErvInformation>();
451 }
452
453 if(erv_information.current_speed <= current_speed_){
454 RCLCPP_ERROR_STREAM(rclcpp::get_logger(logger_name), "Ignoring received BSM since ERV speed of " << erv_information.current_speed << " is less than ego speed of " << current_speed_);
455 return boost::optional<ErvInformation>();
456 }
457
458 // Get vehicle's current latitude from the BSM
459 if(msg->core_data.presence_vector & carma_v2x_msgs::msg::BSMCoreData::LATITUDE_AVAILABLE){
460 erv_information.current_latitude = msg->core_data.latitude;
461 }
462 else{
463 RCLCPP_ERROR_STREAM(rclcpp::get_logger(logger_name), "No latitude available");
464
465 // BSM is not a valid ERV BSM since current latitude is not included; return an empty object
466 return boost::optional<ErvInformation>();
467 }
468
469 // Get vehicle's current longitude from the BSM
470 if(msg->core_data.presence_vector & carma_v2x_msgs::msg::BSMCoreData::LONGITUDE_AVAILABLE){
471
472 erv_information.current_longitude = msg->core_data.longitude;
473 }
474 else{
475 // BSM is not a valid ERV BSM since current longitude is not included; return an empty object
476 RCLCPP_ERROR_STREAM(rclcpp::get_logger(logger_name), "No longitude available");
477
478 return boost::optional<ErvInformation>();
479 }
480
481 // Get vehicle's current position in the map frame from the BSM
482 boost::optional<lanelet::BasicPoint2d> erv_position_in_map = getErvPositionInMap(erv_information.current_latitude, erv_information.current_longitude);
483
484 if(erv_position_in_map){
485 erv_information.current_position_in_map = *erv_position_in_map;
486 }
487
488 // Get vehicle's route destination points from the BSM
489 std::vector<carma_v2x_msgs::msg::Position3D> erv_destination_points;
490 if(msg->presence_vector & carma_v2x_msgs::msg::BSM::HAS_REGIONAL){
491
492 // Parse BSM Regional Extensions to determine whether BSM includes the ERV's route destination points
493 if(msg->regional.size() > 0){
494 for(size_t i = 0; i < msg->regional.size(); ++i){
495 if(msg->regional[i].regional_extension_id == carma_v2x_msgs::msg::BSMRegionalExtension::ROUTE_DESTINATIONS){
496 erv_destination_points = msg->regional[i].route_destination_points;
497 break;
498 }
499 }
500 }
501 }
502
503 if(erv_destination_points.empty()){
504 RCLCPP_ERROR_STREAM(rclcpp::get_logger(logger_name), "ERV's BSM does not contain any future destination points");
505
506 // BSM is not a valid ERV BSM since it does not include destination points; return an empty object
507 return boost::optional<ErvInformation>();
508 }
509
510 // Update the latest processing time of this ERV
511 latest_erv_update_times_[erv_information.vehicle_id] = this->now();
512
513 // Generate ERV's route based on its current position and its destination points
514 lanelet::Optional<lanelet::routing::Route> erv_future_route = generateErvRoute(erv_information.current_latitude, erv_information.current_longitude, erv_destination_points);
515
516 if(!erv_future_route){
517 // ERV cannot be tracked since its route could not be generated; return an empty object
518 RCLCPP_ERROR_STREAM(rclcpp::get_logger(logger_name), "The ERV's route could not be generated");
519
520 return boost::optional<ErvInformation>();
521 }
522
523 // Determine the ERV's current lane index
524 // Note: For 'lane index', 0 is rightmost lane, 1 is second rightmost, etc.; Only the current travel direction is considered
525 if(!erv_future_route.get().shortestPath().empty()){
526 lanelet::ConstLanelet erv_current_lanelet = erv_future_route.get().shortestPath()[0];
527
528 // NOTE: this logic checks if the ERV and CMV are on a same direction or not.
529 // Currently this check is sufficient to happen only once due to the use case scenarios
530 if (is_same_direction_.find(erv_information.vehicle_id) == is_same_direction_.end())
531 {
532 is_same_direction_[erv_information.vehicle_id] = false;
533 for (auto llt: erv_future_route.get().shortestPath()) // checks if ERV is on the same path assuming CMV got all of its planned route when detected
534 {
535 if (wm_->getRoute()->contains(llt))
536 {
537 is_same_direction_[erv_information.vehicle_id] = true;
538
539 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Detected that ERV: " << erv_information.vehicle_id << " and CMV are travelling in the SAME direction");
540 break;
541 }
542 }
543 }
544
545 if (!is_same_direction_[erv_information.vehicle_id]) // opposite direction
546 {
547 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Detected that ERV and CMV are travelling in DIFFERENT directions");
548 return boost::optional<ErvInformation>(); // if opposite direction, do not track
549 }
550
551 // Get ERV's lane index
552 int lane_index = wm_->getMapRoutingGraph()->rights(erv_current_lanelet).size();
553
554 // A currently-tracked ERV must report the same lane index twice in a row before it is assigned the new lane index
556 if(lane_index == tracked_erv_.previous_lane_index){
557 erv_information.lane_index = lane_index;
558 }
559 else{
560 erv_information.lane_index = tracked_erv_.previous_lane_index;
561 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Detected first new lane index of " << lane_index << ", ERV's lane index will remain " << tracked_erv_.previous_lane_index);
562 }
563
564 erv_information.previous_lane_index = lane_index;
565 }
566 else{
567 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "First time detecting this ERV, it's lane index will be " << lane_index);
568 erv_information.lane_index = lane_index;
569 erv_information.previous_lane_index = lane_index;
570 }
571
572
573 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "ERV's lane index is " << erv_information.lane_index);
574
575 }
576 else{
577 RCLCPP_ERROR_STREAM(rclcpp::get_logger(logger_name), "ERV's shortest path is empty!");
578 }
579
580 // Get intersecting lanelet between ERV's future route and ego vehicle's future shortest path
581 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Calling getRouteIntersectingLanelet");
582 boost::optional<lanelet::ConstLanelet> intersecting_lanelet = getRouteIntersectingLanelet(erv_future_route.get());
583 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Done calling getRouteIntersectingLanelet");
584
585 if(intersecting_lanelet){
586 erv_information.intersecting_lanelet = *intersecting_lanelet;
587 }
588 else{
589 // No intersecting lanelet between ERV and ego vehicle was found; return an empty object
590 RCLCPP_ERROR_STREAM(rclcpp::get_logger(logger_name), "No intersecting lanelet between the ERV's future route and the ego vehicle's future route was found.");
591
592 return boost::optional<ErvInformation>();
593 }
594
595 // Get the time (seconds) until the ERV passes the ego vehicle
596 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Calling getSecondsUntilPassing()");
597 boost::optional<double> seconds_until_passing = getSecondsUntilPassing(erv_future_route, erv_information.current_position_in_map, erv_information.current_speed, erv_information.intersecting_lanelet);
598 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Done calling getSecondsUntilPassing()");
599
600 if(seconds_until_passing){
601 erv_information.seconds_until_passing = seconds_until_passing.get();
602 RCLCPP_INFO_STREAM(rclcpp::get_logger(logger_name), "Detected approaching ERV that is passing ego vehicle in " << seconds_until_passing.get() << " seconds");
603 }
604 else{
605 RCLCPP_ERROR_STREAM(rclcpp::get_logger(logger_name), "Detected ERV is not approaching the ego vehicle");
606
607 // ERV will not be tracked since it is not considered to be approaching the ego vehicle; return an empty object
608 return boost::optional<ErvInformation>();
609 }
610
611 return erv_information;
612 }
boost::optional< lanelet::ConstLanelet > getRouteIntersectingLanelet(const lanelet::routing::Route &erv_future_route)
Helper function to obtain the earliest lanelet that exists on both an ERV's future route and the ego ...
lanelet::Optional< lanelet::routing::Route > generateErvRoute(double current_latitude, double current_longitude, std::vector< carma_v2x_msgs::msg::Position3D > erv_destination_points)
Helper function to generate an ERV's route based on its current position and its future route destina...
boost::optional< double > getSecondsUntilPassing(lanelet::Optional< lanelet::routing::Route > &erv_future_route, const lanelet::BasicPoint2d &erv_position_in_map, const double &erv_current_speed, lanelet::ConstLanelet &intersecting_lanelet)
Helper function to calculate the estimated seconds until an ERV will pass the ego vehicle....
boost::optional< lanelet::BasicPoint2d > getErvPositionInMap(const double &current_latitude, const double &current_longitude)
Helper function to obtain an ERV's position in the map frame from its current latitude and longitude.

References approaching_emergency_vehicle_plugin::ErvInformation::current_latitude, approaching_emergency_vehicle_plugin::ErvInformation::current_longitude, approaching_emergency_vehicle_plugin::ErvInformation::current_position_in_map, approaching_emergency_vehicle_plugin::ErvInformation::current_speed, current_speed_, generateErvRoute(), getErvPositionInMap(), getRouteIntersectingLanelet(), getSecondsUntilPassing(), has_tracked_erv_, process_bag::i, approaching_emergency_vehicle_plugin::ErvInformation::intersecting_lanelet, is_same_direction_, approaching_emergency_vehicle_plugin::ErvInformation::lane_index, latest_erv_update_times_, logger_name, approaching_emergency_vehicle_plugin::ErvInformation::previous_lane_index, approaching_emergency_vehicle_plugin::ErvInformation::seconds_until_passing, tracked_erv_, approaching_emergency_vehicle_plugin::ErvInformation::vehicle_id, and wm_.

Referenced by incomingBsmCallback().

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

◆ getErvPositionInMap()

boost::optional< lanelet::BasicPoint2d > approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::getErvPositionInMap ( const double &  current_latitude,
const double &  current_longitude 
)
private

Helper function to obtain an ERV's position in the map frame from its current latitude and longitude.

Parameters
current_latitudeThe current latitude of the ERV.
current_longitudeThe current longitude of the ERV.
Returns
An optional lanelet::BasicPoint2d object, which is empty if the projection of the ERV's current position into the map frame was not successful.

Definition at line 371 of file approaching_emergency_vehicle_plugin_node.cpp.

371 {
372 // Check if the map projection is available
373 if (!map_projector_) {
374 throw std::invalid_argument("Attempting to get ERV's current position in map before map projection was set");
375 }
376
377 // Build map projector
378 lanelet::projection::LocalFrameProjector projector(map_projector_.get().c_str());
379
380 // Create vector to hold ERV's projected current position
381 std::vector<lanelet::BasicPoint3d> erv_current_position_projected_vec;
382
383 // Add ERV's current location to the beginning of erv_current_position_projected_vec
384 lanelet::GPSPoint current_erv_location;
385 current_erv_location.lat = current_latitude;
386 current_erv_location.lon = current_longitude;
387
388 // Convert ERV's projected position to its position in the map frame
389 erv_current_position_projected_vec.emplace_back(projector.forward(current_erv_location));
390 auto erv_current_position_in_map_vec = lanelet::utils::transform(erv_current_position_projected_vec, [](auto a) { return lanelet::traits::to2D(a); });
391
392 // Conduct size check since only the first element is being returned
393 if(!erv_current_position_in_map_vec.empty()){
394 return erv_current_position_in_map_vec[0];
395 }
396 else{
397 return boost::optional<lanelet::BasicPoint2d>();
398 }
399 }

References map_projector_.

Referenced by getErvInformationFromBsm().

Here is the caller graph for this function:

◆ getLaneletOnEgoRouteFromMapPosition()

boost::optional< lanelet::ConstLanelet > approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::getLaneletOnEgoRouteFromMapPosition ( const double &  x_position,
const double &  y_position 
)
private

Helper function to convert a map x,y coordinate pair to a lanelet on the ego vehicle's route.

Parameters
x_positionA map x-coordinate.
y_positionA map y-coordinate.
Returns
An optional lanelet::ConstLanelet object corresponding to the lanelet on the ego vehicle's route that the map x,y coordinates are positioned within. If empty, the map x,y coordinates are not positioned within a lanelet on the ego vehicle's route.

Definition at line 1484 of file approaching_emergency_vehicle_plugin_node.cpp.

1485 {
1486 // Get lanelet(s) that the provided map coordinates are contained within
1487 std::vector<lanelet::ConstLanelet> ego_route_lanelets = wm_->getLaneletsFromPoint({x_position, y_position});
1488 boost::optional<lanelet::ConstLanelet> ego_route_lanelet;
1489
1490 if (ego_route_lanelets.empty())
1491 {
1492 RCLCPP_WARN_STREAM(rclcpp::get_logger(logger_name), "Given vehicle position is not on the road.");
1493 return boost::optional<lanelet::ConstLanelet>();
1494 }
1495
1496 // Get the lanelet that is on the ego vehicle's route in case vehicle is located on overlapping lanelets
1497 for(const auto& lanelet : ego_route_lanelets)
1498 {
1499 if(wm_->getRoute()->contains(lanelet)){
1500 ego_route_lanelet = lanelet;
1501 break;
1502 }
1503 }
1504
1505 return ego_route_lanelet;
1506 }

References logger_name, and wm_.

Referenced by plan_maneuvers_callback().

Here is the caller graph for this function:

◆ getLaneletSpeedLimit()

double approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::getLaneletSpeedLimit ( const lanelet::ConstLanelet &  lanelet)
private

Helper function to extract the speed limit (m/s) from a provided lanelet.

Parameters
laneletA lanelet in the loaded vector map used by the CARMA System.
Returns
A double containing the speed limit (m/s) for the provided lanelet.

Definition at line 1015 of file approaching_emergency_vehicle_plugin_node.cpp.

1016 {
1017 double speed_limit = config_.default_speed_limit;
1018
1019 lanelet::Optional<carma_wm::TrafficRulesConstPtr> traffic_rules = wm_->getTrafficRules();
1020 if (traffic_rules)
1021 {
1022 speed_limit =(*traffic_rules)->speedLimit(lanelet).speedLimit.value();
1023 }
1024 else
1025 {
1026 throw std::invalid_argument("No speed limit could be found since valid traffic rules object could not be built");
1027 }
1028
1029 return speed_limit;
1030 }

References config_, approaching_emergency_vehicle_plugin::Config::default_speed_limit, and wm_.

Referenced by generateMoveOverManeuverPlan(), generateReducedSpeedLaneFollowingeManeuverPlan(), and plan_maneuvers_callback().

Here is the caller graph for this function:

◆ getManeuverDuration()

rclcpp::Duration approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::getManeuverDuration ( const carma_planning_msgs::msg::Maneuver &  maneuver,
double  epsilon 
) const
private

Helper function to obtain the (seconds) of a provided maneuver.

Parameters
maneuverThe maneuver from which that duration is desired.
epsilonA double used for comparisons to zero.
Returns
The total duration (seconds) of the provided maneuver.

Definition at line 1032 of file approaching_emergency_vehicle_plugin_node.cpp.

1033 {
1034 double maneuver_start_speed = GET_MANEUVER_PROPERTY(maneuver, start_speed);
1035 double maneuver_end_speed = getManeuverEndSpeed(maneuver);
1036 double sum_start_and_end_speed = maneuver_start_speed + maneuver_end_speed;
1037
1038 if(sum_start_and_end_speed < epsilon){
1039 throw std::invalid_argument("Maneuver start and ending speed is zero");
1040 }
1041
1042 rclcpp::Duration maneuver_duration{0,0};
1043 double maneuver_start_dist = GET_MANEUVER_PROPERTY(maneuver, start_dist);
1044 double maneuver_end_dist = GET_MANEUVER_PROPERTY(maneuver, end_dist);
1045
1046 RCLCPP_ERROR_STREAM(rclcpp::get_logger(logger_name),"maneuver_end_dist: " << maneuver_end_dist << ", maneuver_start_dist: " << maneuver_start_dist << ", sum_start_and_end_speed: " << sum_start_and_end_speed);
1047
1048 maneuver_duration = rclcpp::Duration((maneuver_end_dist - maneuver_start_dist) / (0.5 * sum_start_and_end_speed) * 1e9);
1049
1050 return maneuver_duration;
1051 }
#define GET_MANEUVER_PROPERTY(mvr, property)
Macro definition to enable easier access to fields shared across the maneuver types.

References GET_MANEUVER_PROPERTY, approaching_emergency_vehicle_plugin::anonymous_namespace{approaching_emergency_vehicle_plugin_node.cpp}::getManeuverEndSpeed(), and logger_name.

Referenced by addStopAndWaitToEndOfPlan(), composeLaneChangeManeuverMessage(), composeLaneFollowingManeuverMessage(), composeStopAndWaitManeuverMessage(), generateMoveOverManeuverPlan(), and generateReducedSpeedLaneFollowingeManeuverPlan().

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

◆ getRouteIntersectingLanelet()

boost::optional< lanelet::ConstLanelet > approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::getRouteIntersectingLanelet ( const lanelet::routing::Route &  erv_future_route)
private

Helper function to obtain the earliest lanelet that exists on both an ERV's future route and the ego vehicle's future shortest path. Accesses the ego vehicle's future shortest path using wm_ object.

Parameters
erv_future_routeThe ERV's future route
Returns
An optional lanelet::ConstLanelet object, which is empty if no intersecting lanelet was found.

Definition at line 944 of file approaching_emergency_vehicle_plugin_node.cpp.

944 {
945
946 if(future_route_lanelet_ids_.empty()){
947 RCLCPP_WARN_STREAM(rclcpp::get_logger(logger_name), "Remaining route lanelets for the ego vehicle not found; plugin cannot compute the intersecting lanelet.");
948 return boost::optional<lanelet::ConstLanelet>();
949 }
950
951 // Get current downtrack from latest_route_state_
952 double current_downtrack = latest_route_state_.down_track;
953
954 // Find first successful intersecting lanelet between ERV route and ego route with a lanelet starting downtrack greater than current downtrack.
955 // Additionally, remove lanelets from future_route_lanelet_ids_ that the ego vehicle has passed.
956 for(auto it = future_route_lanelet_ids_.begin(); it != future_route_lanelet_ids_.end();){
957 // Get lanelet
958 lanelet::Id ego_route_lanelet_id = *it;
959 lanelet::ConstLanelet ego_route_lanelet = wm_->getMap()->laneletLayer.get(ego_route_lanelet_id);
960
961 // Get lanelet's centerline end point downtrack
962 lanelet::BasicPoint2d ego_route_lanelet_centerline_end = lanelet::utils::to2D(ego_route_lanelet.centerline()).back();
963 double ego_route_lanelet_centerline_end_downtrack = wm_->routeTrackPos(ego_route_lanelet_centerline_end).downtrack;
964
965 if(current_downtrack > ego_route_lanelet_centerline_end_downtrack){
966 // If current downtrack is greater than lanelet ending downtrack, remove lanelet from future route lanelet and continue
967 // NOTE: Iterator is not updated since an element is being erased
968 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Removing passed lanelet " << ego_route_lanelet_id);
970 }
971 else{
972 // If lanelet exists in both, return it as the intersecting lanelet
973 if(erv_future_route.contains(ego_route_lanelet)){
974 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Found intersecting lanelet " << ego_route_lanelet_id);
975 return boost::optional<lanelet::ConstLanelet>(ego_route_lanelet);
976 }
977 else{
978 // Increase iterator
979 ++it;
980 }
981 }
982 }
983 }

References future_route_lanelet_ids_, latest_route_state_, logger_name, and wm_.

Referenced by getErvInformationFromBsm().

Here is the caller graph for this function:

◆ getSecondsUntilPassing()

boost::optional< double > approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::getSecondsUntilPassing ( lanelet::Optional< lanelet::routing::Route > &  erv_future_route,
const lanelet::BasicPoint2d &  erv_position_in_map,
const double &  erv_current_speed,
lanelet::ConstLanelet &  intersecting_lanelet 
)
private

Helper function to calculate the estimated seconds until an ERV will pass the ego vehicle. This is an estimate since it assumes both the ERV and the ego vehicle will continue travelling at their current speed.

Parameters
erv_future_routeThe ERV's future route
erv_position_in_mapThe ERV's current position in the map frame
erv_current_speedThe ERV's current speed (m/s)
intersecting_laneletThe earliest lanelet that exists on both the ERV's future route and the ego vehicle's future shortest path.
Returns
An optional double, which includes the estimated seconds until the ERV will pass the ego vehicle. If empty, the ERV is behind the ego vehicle and travelling slower than the ego vehicle, or the ERV is in front of the ego vehicle while not actively passing the ego vehicle.

Definition at line 883 of file approaching_emergency_vehicle_plugin_node.cpp.

884 {
885
886 // Obtain ego vehicle and ERV distances to the end of the intersecting lanelet so neither vehicle will currently be past that point
887 lanelet::ConstLineString2d intersecting_centerline = lanelet::utils::to2D(intersecting_lanelet.centerline());
888 lanelet::BasicPoint2d intersecting_end_point = intersecting_centerline.back();
889
890 // Get ego vehicle's (its rear bumper) distance to the intersecting lanelet's centerline endpoint
891 double ego_dist_to_lanelet = wm_->routeTrackPos(intersecting_end_point).downtrack - (latest_route_state_.down_track - config_.vehicle_length);
892
893 // Set erv_world_model_ route to the erv_future_route
894 lanelet::routing::Route route = std::move(*erv_future_route);
895 carma_wm::LaneletRoutePtr erv_future_route_ptr = std::make_shared<lanelet::routing::Route>(std::move(route));
896 erv_world_model_->setRoute(erv_future_route_ptr);
897
898 // Get ERV's distance to the intersecting lanelet's centerline endpoint
899 double erv_dist_to_lanelet = erv_world_model_->routeTrackPos(intersecting_end_point).downtrack - erv_world_model_->routeTrackPos(erv_position_in_map).downtrack;
900
901 if(erv_dist_to_lanelet < ego_dist_to_lanelet){
902 // When ERV is in front of the ego vehicle, only process further if the ERV is actively passing the ego vehicle
903
905 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Detected ERV is in front of the ego vehicle");
906 return boost::optional<double>();
907 }
908 else{
909 // This ERV is actively passing the ego vehicle until its distance in front of the ego vehicle is at least config_.finished_passing_threshold
910 if((ego_dist_to_lanelet - erv_dist_to_lanelet) < config_.finished_passing_threshold){
911 // Return zero to indicate that the ERV is currently passing the ego vehicle
912 return 0.0;
913 }
914 else{
915 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "ERV has passed the ego vehicle");
916 has_tracked_erv_ = false;
919 return boost::optional<double>();
920 }
921 }
922 }
923 else{
924 // When ERV is behind the ego vehicle, only process further if the ERV is travelling faster than the ego vehicle
925
926 if(erv_current_speed < current_speed_){
927 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Detected ERV is travelling slower than the ego vehicle, and will not pass the ego vehicle");
928 return boost::optional<double>();
929 }
930 else{
931 // Calculate seconds_until_passing and protect against division by zero
932 double delta_speed = erv_current_speed - current_speed_;
933
934 if(delta_speed == 0.0){
935 delta_speed = epsilon_;
936 }
937 double seconds_until_passing = (erv_dist_to_lanelet - ego_dist_to_lanelet) / delta_speed;
938
939 return seconds_until_passing;
940 }
941 }
942 }
std::shared_ptr< lanelet::routing::Route > LaneletRoutePtr
Definition: WorldModel.hpp:45

References config_, current_speed_, epsilon_, approaching_emergency_vehicle_plugin::ERV_PASSED, erv_world_model_, approaching_emergency_vehicle_plugin::ApproachingEmergencyVehicleTransitionTable::event(), approaching_emergency_vehicle_plugin::Config::finished_passing_threshold, approaching_emergency_vehicle_plugin::ApproachingEmergencyVehicleTransitionTable::getState(), has_planned_upcoming_lc_, has_tracked_erv_, latest_route_state_, logger_name, approaching_emergency_vehicle_plugin::SLOWING_DOWN_FOR_ERV, transition_table_, approaching_emergency_vehicle_plugin::Config::vehicle_length, and wm_.

Referenced by getErvInformationFromBsm().

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

◆ guidanceStateCallback()

void approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::guidanceStateCallback ( const carma_planning_msgs::msg::GuidanceState::UniquePtr  msg)

Subscription callback to process the latest guidance state and update the is_guidance_engaged_ flag accordingly.

Definition at line 994 of file approaching_emergency_vehicle_plugin_node.cpp.

References lightbar_manager::ENGAGED, and is_guidance_engaged_.

Referenced by on_configure_plugin().

Here is the caller graph for this function:

◆ incomingBsmCallback()

void approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::incomingBsmCallback ( carma_v2x_msgs::msg::BSM::UniquePtr  msg)

Incoming BSM subscription callback, which determines whether a BSM should be processed, and extracts ERV information from the BSM through a call to getErvInformationFromBsm(). This function is responsible for updating this plugin's tracked_erv_ object and setting has_tracked_erv_ to true when a new ERV becomes tracked.

Parameters
msgThe incoming BSM message.

Definition at line 809 of file approaching_emergency_vehicle_plugin_node.cpp.

810 {
811 // Only process incoming BSMs if guidance is currently engaged
813 return;
814 }
815
816 // Get the vehicle ID associated with the received BSM
817 std::stringstream ss;
818 for(size_t i = 0; i < msg->core_data.id.size(); ++i){
819 ss << std::setfill('0') << std::setw(2) << std::hex << (unsigned)msg->core_data.id.at(i);
820 }
821 std::string erv_vehicle_id = ss.str();
822 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Received a BSM from " << erv_vehicle_id);
823
825 // If there is already an ERV approaching the ego vehicle, only process this BSM further if it is from that ERV and enough time has passed since the previously processed BSM
826
827 if(erv_vehicle_id == tracked_erv_.vehicle_id){
828 double seconds_since_prev_processed_bsm = (this->now() - tracked_erv_.latest_update_time).seconds();
829
830 if(seconds_since_prev_processed_bsm < (1.0 / config_.bsm_processing_frequency)){
831 // Do not process ERV's BSM further since not enough time has passed since its previously processed BSM
832 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Ignoring BSM from tracked ERV " << erv_vehicle_id << " since a BSM from it was processed " << seconds_since_prev_processed_bsm << " seconds ago");
833 return;
834 }
835 }
836 else{
837 // Do not process BSM further since it is not for the currently tracked ERV
838 return;
839 }
840 }
841 else{
842
843 // If BSM is from a detected active ERV, only process it further if enough time has passed since the previously processed BSM from this ERV
844 if (latest_erv_update_times_.find(erv_vehicle_id) != latest_erv_update_times_.end()){
845 double seconds_since_prev_processed_bsm = (this->now() - latest_erv_update_times_[erv_vehicle_id]).seconds();
846
847 if(seconds_since_prev_processed_bsm < (1.0 / config_.bsm_processing_frequency)){
848 // Do not process ERV's BSM further since not enough time has passed since its previously processed BSM
849 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Ignoring BSM from non tracked ERV " << erv_vehicle_id << " since a BSM from it was processed " << seconds_since_prev_processed_bsm << " seconds ago");
850 return;
851 }
852 }
853 }
854
855 // Get ErvInformation object with information from the BSM if it is from an active ERV that is approaching the ego vehicle
856 boost::optional<ErvInformation> erv_information = getErvInformationFromBsm(std::move(msg));
857 if(!erv_information){
858 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "BSM is not from an active ERV that is approaching the ego vehicle.");
859
861 RCLCPP_INFO_STREAM(rclcpp::get_logger(logger_name), "BSM was from the currently tracked approaching ERV! ERV is no longer approaching.");
862 has_tracked_erv_ = false;
865 }
866
867 // BSM is not from an active ERV that is approaching the ego vehicle
868 return;
869 }
870 else{
871 // Update tracked ERV information since this is an active ERV that is approaching the ego vehicle
872 tracked_erv_ = *erv_information;
873 tracked_erv_.latest_update_time = this->now();
874
875 if(!has_tracked_erv_){
876 has_tracked_erv_ = true;
877 }
878 }
879
880 return;
881 }
boost::optional< ErvInformation > getErvInformationFromBsm(carma_v2x_msgs::msg::BSM::UniquePtr msg)
Through internal logic and calls to separate helper functions, this function processes a received BSM...

References approaching_emergency_vehicle_plugin::Config::bsm_processing_frequency, config_, approaching_emergency_vehicle_plugin::ApproachingEmergencyVehicleTransitionTable::event(), getErvInformationFromBsm(), has_planned_upcoming_lc_, has_tracked_erv_, process_bag::i, is_guidance_engaged_, latest_erv_update_times_, approaching_emergency_vehicle_plugin::ErvInformation::latest_update_time, logger_name, approaching_emergency_vehicle_plugin::NO_APPROACHING_ERV, tracked_erv_, transition_table_, and approaching_emergency_vehicle_plugin::ErvInformation::vehicle_id.

Referenced by on_configure_plugin().

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

◆ incomingEmergencyVehicleAckCallback()

void approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::incomingEmergencyVehicleAckCallback ( const carma_v2x_msgs::msg::EmergencyVehicleAck::UniquePtr  msg)

Subscription callback for incoming EmergencyVehicleAck messages. If the message is from the currently tracked ERV and is intended for the ego vehicle, then the plugin will reset num_warnings_broadcasted_ to zero and will reset the should_broadcast_warnings_ flag to false.

Definition at line 620 of file approaching_emergency_vehicle_plugin_node.cpp.

621 {
622 // Only process message if it is from the currently-tracked ERV and it is intended for the ego vehicle
623 if(has_tracked_erv_ && (msg->m_header.sender_id == tracked_erv_.vehicle_id) && (msg->m_header.recipient_id == config_.vehicle_id)){
624 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "EmergencyVehicleAck received from ERV " << tracked_erv_.vehicle_id);
625
626 // Reset counter and boolean flag to stop warning messages from being broadcasted
629 }
630 }

References config_, has_tracked_erv_, logger_name, num_warnings_broadcasted_, should_broadcast_warnings_, tracked_erv_, approaching_emergency_vehicle_plugin::Config::vehicle_id, and approaching_emergency_vehicle_plugin::ErvInformation::vehicle_id.

Referenced by on_configure_plugin().

Here is the caller graph for this function:

◆ on_activate_plugin()

carma_ros2_utils::CallbackReturn approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::on_activate_plugin ( )
virtual

This method is used to create a timer and will be called on the activate transition.

Reimplemented from carma_guidance_plugins::PluginBaseNode.

Definition at line 189 of file approaching_emergency_vehicle_plugin_node.cpp.

190 {
191 // Timer setup for generating a BSM
192 int erv_timeout_check_period_ms = (1 / config_.timeout_check_frequency) * 1000; // Conversion from frequency (Hz) to milliseconds time period
193 erv_timeout_timer_ = create_timer(get_clock(),
194 std::chrono::milliseconds(erv_timeout_check_period_ms),
196
197 // Timer setup for broadcasting EmergencyVehicleResponse warning message to an approaching ERV when the ego vehicle cannot change lanes out of the ERV's path
198 int emergency_vehicle_response_period_ms = (1 / config_.warning_broadcast_frequency) * 1000; // Conversion from frequency (Hz) to milliseconds time period
199 warning_broadcast_timer_ = create_timer(get_clock(),
200 std::chrono::milliseconds(emergency_vehicle_response_period_ms),
202
203 // Timer setup for publishing approaching ERV status update to the Web UI
204 int approaching_erv_status_period_ms = (1 / config_.approaching_erv_status_publication_frequency) * 1000; // Conversion from frequency (Hz) to milliseconds time period
205 approaching_emergency_vehicle_status_timer_ = create_timer(get_clock(),
206 std::chrono::milliseconds(approaching_erv_status_period_ms),
208
209 // Timer setup for publishing hazard light ON/OFF status boolean
210 int hazard_light_status_ms = (1 / 30) * 1000; // Conversion from frequency 30(Hz) to milliseconds time period
211 hazard_light_timer_ = create_timer(get_clock(),
212 std::chrono::milliseconds(hazard_light_status_ms),
214
215 return CallbackReturn::SUCCESS;
216 }
void publishApproachingErvStatus()
This is a callback function for the approaching_emergency_vehicle_status_timer_. It makes a call to g...
void broadcastWarningToErv()
This is a callback function for the warning_broadcast_timer_, and is called to broadcast an Emergency...
void checkForErvTimeout()
This is a callback function for the erv_timeout_timer_ timer, and is called to determine whether a ti...
void publishHazardLightStatus()
This is a callback function for publishing turn ON/OFF (true/false) hazard light command to the ssc d...

References approaching_emergency_vehicle_status_timer_, approaching_emergency_vehicle_plugin::Config::approaching_erv_status_publication_frequency, broadcastWarningToErv(), checkForErvTimeout(), config_, erv_timeout_timer_, hazard_light_timer_, publishApproachingErvStatus(), publishHazardLightStatus(), approaching_emergency_vehicle_plugin::Config::timeout_check_frequency, approaching_emergency_vehicle_plugin::Config::warning_broadcast_frequency, and warning_broadcast_timer_.

Here is the call graph for this function:

◆ on_configure_plugin()

carma_ros2_utils::CallbackReturn approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::on_configure_plugin ( )
virtual

This method is used to load parameters and will be called on the configure state transition.

Implements carma_guidance_plugins::PluginBaseNode.

Definition at line 117 of file approaching_emergency_vehicle_plugin_node.cpp.

118 {
119 RCLCPP_INFO_STREAM(rclcpp::get_logger(logger_name), "ApproachingEmergencyVehiclePlugin trying to configure");
120
121 // Reset config
122 config_ = Config();
123
124 // Load parameters
125 get_parameter<double>("passing_threshold", config_.passing_threshold);
126 get_parameter<double>("approaching_threshold", config_.approaching_threshold);
127 get_parameter<double>("finished_passing_threshold", config_.finished_passing_threshold);
128 get_parameter<double>("min_lane_following_duration_before_lane_change", config_.min_lane_following_duration_before_lane_change);
129 get_parameter<double>("bsm_processing_frequency", config_.bsm_processing_frequency);
130 get_parameter<double>("speed_limit_reduction_during_passing", config_.speed_limit_reduction_during_passing);
131 get_parameter<double>("minimum_reduced_speed_limit", config_.minimum_reduced_speed_limit);
132 get_parameter<double>("default_speed_limit", config_.default_speed_limit);
133 get_parameter<double>("reduced_speed_buffer", config_.reduced_speed_buffer);
134 get_parameter<double>("timeout_check_frequency", config_.timeout_check_frequency);
135 get_parameter<double>("timeout_duration", config_.timeout_duration);
136 get_parameter<double>("minimal_plan_duration", config_.minimal_plan_duration);
137 get_parameter<double>("buffer_distance_before_stopping", config_.buffer_distance_before_stopping);
138 get_parameter<double>("stopping_accel_limit_multiplier", config_.stopping_accel_limit_multiplier);
139 get_parameter<double>("vehicle_acceleration_limit", config_.vehicle_acceleration_limit);
140 get_parameter<double>("route_end_point_buffer", config_.route_end_point_buffer);
141 get_parameter<double>("approaching_erv_status_publication_frequency", config_.approaching_erv_status_publication_frequency);
142 get_parameter<double>("warning_broadcast_frequency", config_.warning_broadcast_frequency);
143 get_parameter<double>("vehicle_length", config_.vehicle_length);
144 get_parameter<int>("max_warning_broadcasts", config_.max_warning_broadcasts);
145 get_parameter<std::string>("lane_following_plugin", config_.lane_following_plugin);
146 get_parameter<std::string>("lane_change_plugin", config_.lane_change_plugin);
147 get_parameter<std::string>("vehicle_id", config_.vehicle_id);
148
149 RCLCPP_INFO_STREAM(rclcpp::get_logger(logger_name), "ApproachingEmergencyVehiclePlugin Config: " << config_);
150
151 // Register runtime parameter update callback
152 add_on_set_parameters_callback(std::bind(&ApproachingEmergencyVehiclePlugin::parameter_update_callback, this, std_ph::_1));
153
154 // Setup subscribers
155 incoming_bsm_sub_ = create_subscription<carma_v2x_msgs::msg::BSM>("incoming_bsm", 1,
156 std::bind(&ApproachingEmergencyVehiclePlugin::incomingBsmCallback, this, std_ph::_1));
157
158 georeference_sub_ = create_subscription<std_msgs::msg::String>("georeference", 10,
159 std::bind(&ApproachingEmergencyVehiclePlugin::georeferenceCallback, this, std_ph::_1));
160
161 route_state_sub_ = create_subscription<carma_planning_msgs::msg::RouteState>("route_state", 10,
162 std::bind(&ApproachingEmergencyVehiclePlugin::routeStateCallback, this, std_ph::_1));
163
164 twist_sub_ = create_subscription<geometry_msgs::msg::TwistStamped>("current_velocity", 10,
165 std::bind(&ApproachingEmergencyVehiclePlugin::twistCallback, this, std_ph::_1));
166
167 incoming_emergency_vehicle_ack_sub_ = create_subscription<carma_v2x_msgs::msg::EmergencyVehicleAck>("incoming_emergency_vehicle_ack", 10,
169
170 guidance_state_sub_ = create_subscription<carma_planning_msgs::msg::GuidanceState>("state", 1,
172
173 route_sub_ = create_subscription<carma_planning_msgs::msg::Route>("route", 1,
174 std::bind(&ApproachingEmergencyVehiclePlugin::routeCallback, this, std_ph::_1));
175
176 // Setup publishers
177 outgoing_emergency_vehicle_response_pub_ = create_publisher<carma_v2x_msgs::msg::EmergencyVehicleResponse>("outgoing_emergency_vehicle_response", 10);
178
179 approaching_erv_status_pub_ = create_publisher<carma_msgs::msg::UIInstructions>("approaching_erv_status", 10);
180
181 hazard_light_cmd_pub_ = create_publisher<std_msgs::msg::Bool>("hazard_light_status", 10);
182
184
185 // Return success if everything initialized successfully
186 return CallbackReturn::SUCCESS;
187 }
rcl_interfaces::msg::SetParametersResult parameter_update_callback(const std::vector< rclcpp::Parameter > &parameters)
Callback for dynamic parameter updates.
carma_ros2_utils::SubPtr< carma_v2x_msgs::msg::EmergencyVehicleAck > incoming_emergency_vehicle_ack_sub_
void routeStateCallback(carma_planning_msgs::msg::RouteState::UniquePtr msg)
Route State subscription callback, with is used to update this plugin's latest_route_state_ object.
void guidanceStateCallback(const carma_planning_msgs::msg::GuidanceState::UniquePtr msg)
Subscription callback to process the latest guidance state and update the is_guidance_engaged_ flag a...
void incomingEmergencyVehicleAckCallback(const carma_v2x_msgs::msg::EmergencyVehicleAck::UniquePtr msg)
Subscription callback for incoming EmergencyVehicleAck messages. If the message is from the currently...
void incomingBsmCallback(carma_v2x_msgs::msg::BSM::UniquePtr msg)
Incoming BSM subscription callback, which determines whether a BSM should be processed,...
carma_ros2_utils::SubPtr< carma_planning_msgs::msg::RouteState > route_state_sub_
void routeCallback(carma_planning_msgs::msg::Route::UniquePtr msg)
Route subscription callback, with is used to update this plugin's future_route_lanelet_ids_ object.
void twistCallback(geometry_msgs::msg::TwistStamped::UniquePtr msg)
Subscription callback for the twist subscriber, which will store latest current velocity of the ego v...
carma_ros2_utils::SubPtr< carma_planning_msgs::msg::GuidanceState > guidance_state_sub_
void georeferenceCallback(const std_msgs::msg::String::UniquePtr msg)
Subscription callback for the georeference.
virtual carma_wm::WorldModelConstPtr get_world_model() final
Method to return the default world model provided as a convience by this base class If this method or...

References approaching_erv_status_pub_, approaching_emergency_vehicle_plugin::Config::approaching_erv_status_publication_frequency, approaching_emergency_vehicle_plugin::Config::approaching_threshold, approaching_emergency_vehicle_plugin::Config::bsm_processing_frequency, approaching_emergency_vehicle_plugin::Config::buffer_distance_before_stopping, config_, approaching_emergency_vehicle_plugin::Config::default_speed_limit, approaching_emergency_vehicle_plugin::Config::finished_passing_threshold, georeference_sub_, georeferenceCallback(), carma_guidance_plugins::PluginBaseNode::get_world_model(), guidance_state_sub_, guidanceStateCallback(), hazard_light_cmd_pub_, incoming_bsm_sub_, incoming_emergency_vehicle_ack_sub_, incomingBsmCallback(), incomingEmergencyVehicleAckCallback(), approaching_emergency_vehicle_plugin::Config::lane_change_plugin, approaching_emergency_vehicle_plugin::Config::lane_following_plugin, logger_name, approaching_emergency_vehicle_plugin::Config::max_warning_broadcasts, approaching_emergency_vehicle_plugin::Config::min_lane_following_duration_before_lane_change, approaching_emergency_vehicle_plugin::Config::minimal_plan_duration, approaching_emergency_vehicle_plugin::Config::minimum_reduced_speed_limit, outgoing_emergency_vehicle_response_pub_, parameter_update_callback(), approaching_emergency_vehicle_plugin::Config::passing_threshold, approaching_emergency_vehicle_plugin::Config::reduced_speed_buffer, approaching_emergency_vehicle_plugin::Config::route_end_point_buffer, route_state_sub_, route_sub_, routeCallback(), routeStateCallback(), approaching_emergency_vehicle_plugin::Config::speed_limit_reduction_during_passing, approaching_emergency_vehicle_plugin::Config::stopping_accel_limit_multiplier, approaching_emergency_vehicle_plugin::Config::timeout_check_frequency, approaching_emergency_vehicle_plugin::Config::timeout_duration, twist_sub_, twistCallback(), approaching_emergency_vehicle_plugin::Config::vehicle_acceleration_limit, approaching_emergency_vehicle_plugin::Config::vehicle_id, approaching_emergency_vehicle_plugin::Config::vehicle_length, approaching_emergency_vehicle_plugin::Config::warning_broadcast_frequency, and wm_.

Here is the call graph for this function:

◆ parameter_update_callback()

rcl_interfaces::msg::SetParametersResult approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::parameter_update_callback ( const std::vector< rclcpp::Parameter > &  parameters)

Callback for dynamic parameter updates.

Definition at line 75 of file approaching_emergency_vehicle_plugin_node.cpp.

76 {
77 auto error_1 = update_params<double>({
78 {"passing_threshold", config_.passing_threshold},
79 {"approaching_threshold", config_.approaching_threshold},
80 {"finished_passing_threshold", config_.finished_passing_threshold},
81 {"min_lane_following_duration_before_lane_change", config_.min_lane_following_duration_before_lane_change},
82 {"bsm_processing_frequency", config_.bsm_processing_frequency},
83 {"speed_limit_reduction_during_passing", config_.speed_limit_reduction_during_passing},
84 {"minimum_reduced_speed_limit", config_.minimum_reduced_speed_limit},
85 {"default_speed_limit", config_.default_speed_limit},
86 {"reduced_speed_buffer", config_.reduced_speed_buffer},
87 {"timeout_check_frequency", config_.timeout_check_frequency},
88 {"timeout_duration", config_.timeout_duration},
89 {"minimal_plan_duration", config_.minimal_plan_duration},
90 {"buffer_distance_before_stopping", config_.buffer_distance_before_stopping},
91 {"stopping_accel_limit_multiplier", config_.stopping_accel_limit_multiplier},
92 {"vehicle_acceleration_limit", config_.vehicle_acceleration_limit},
93 {"route_end_point_buffer", config_.route_end_point_buffer},
94 {"approaching_erv_status_publication_frequency", config_.approaching_erv_status_publication_frequency},
95 {"warning_broadcast_frequency", config_.warning_broadcast_frequency},
96 {"vehicle_length", config_.vehicle_length}
97 }, parameters);
98
99 auto error_2 = update_params<int>({
100 {"max_warning_broadcasts", config_.max_warning_broadcasts}
101 }, parameters);
102
103 auto error_3 = update_params<std::string>({
104 {"lane_following_plugin", config_.lane_following_plugin},
105 {"lane_change_plugin", config_.lane_change_plugin},
106 {"stop_and_wait_plugin", config_.stop_and_wait_plugin},
107 {"vehicle_id", config_.vehicle_id}
108 }, parameters);
109
110 rcl_interfaces::msg::SetParametersResult result;
111
112 result.successful = !error_1 && !error_2 && !error_3;
113
114 return result;
115 }

References approaching_emergency_vehicle_plugin::Config::approaching_erv_status_publication_frequency, approaching_emergency_vehicle_plugin::Config::approaching_threshold, approaching_emergency_vehicle_plugin::Config::bsm_processing_frequency, approaching_emergency_vehicle_plugin::Config::buffer_distance_before_stopping, config_, approaching_emergency_vehicle_plugin::Config::default_speed_limit, approaching_emergency_vehicle_plugin::Config::finished_passing_threshold, approaching_emergency_vehicle_plugin::Config::lane_change_plugin, approaching_emergency_vehicle_plugin::Config::lane_following_plugin, approaching_emergency_vehicle_plugin::Config::max_warning_broadcasts, approaching_emergency_vehicle_plugin::Config::min_lane_following_duration_before_lane_change, approaching_emergency_vehicle_plugin::Config::minimal_plan_duration, approaching_emergency_vehicle_plugin::Config::minimum_reduced_speed_limit, approaching_emergency_vehicle_plugin::Config::passing_threshold, approaching_emergency_vehicle_plugin::Config::reduced_speed_buffer, approaching_emergency_vehicle_plugin::Config::route_end_point_buffer, approaching_emergency_vehicle_plugin::Config::speed_limit_reduction_during_passing, approaching_emergency_vehicle_plugin::Config::stop_and_wait_plugin, approaching_emergency_vehicle_plugin::Config::stopping_accel_limit_multiplier, approaching_emergency_vehicle_plugin::Config::timeout_check_frequency, approaching_emergency_vehicle_plugin::Config::timeout_duration, approaching_emergency_vehicle_plugin::Config::vehicle_acceleration_limit, approaching_emergency_vehicle_plugin::Config::vehicle_id, approaching_emergency_vehicle_plugin::Config::vehicle_length, and approaching_emergency_vehicle_plugin::Config::warning_broadcast_frequency.

Referenced by on_configure_plugin().

Here is the caller graph for this function:

◆ plan_maneuvers_callback()

void approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::plan_maneuvers_callback ( std::shared_ptr< rmw_request_id_t >  srv_header,
carma_planning_msgs::srv::PlanManeuvers::Request::SharedPtr  req,
carma_planning_msgs::srv::PlanManeuvers::Response::SharedPtr  resp 
)
overridevirtual

Extending class provided callback which should return a planned trajectory based on the provided trajectory planning request.

Parameters
srv_headerRCL header for services calls. Can usually be ignored by implementers.
reqThe service request containing the previously planned maneuvers and vehicle state
respThe response containing the additional maneuvers generated by this plugin

Implements carma_guidance_plugins::StrategicPlugin.

Definition at line 1508 of file approaching_emergency_vehicle_plugin_node.cpp.

1512 {
1513 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Received request to plan maneuvers");
1514
1515 // Check whether ego vehicle is currently in the middle of a lane change, or whether its planned lane change has been completed
1516 bool is_currently_lane_changing = false;
1518 if((upcoming_lc_params_.start_dist < req->veh_downtrack) && (req->veh_downtrack < upcoming_lc_params_.end_dist)){
1519 is_currently_lane_changing = true;
1520 }
1521 else if(req->veh_downtrack > upcoming_lc_params_.end_dist){
1522 // Update flag since the planned lane change has been completed
1524 }
1525 }
1526
1527 boost::optional<lanelet::ConstLanelet> ego_current_lanelet_optional = getLaneletOnEgoRouteFromMapPosition(req->veh_x, req->veh_y);
1528
1529 if(ego_current_lanelet_optional){
1530 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "starting lanelet for maneuver plan is " << ego_current_lanelet_optional.get().id());
1531 }
1532 else{
1533 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Given vehicle position is not within a lanelet on the route. Returning empty maneuver plan");
1534 return;
1535 }
1536
1537 // Get ego vehicle's current lane index
1538 // Note: For 'lane index', 0 is rightmost lane, 1 is second rightmost, etc.; Only the current travel direction is considered
1539 ego_lane_index_ = wm_->getMapRoutingGraph()->rights(ego_current_lanelet_optional.get()).size();
1540
1541 // Update state machine if there is currently an ERV being tracked and if ego vehicle is not currently in the middle of a lane change
1542 if(has_tracked_erv_ && !is_currently_lane_changing){
1544 // Trigger state machine transition for case in which the ego vehicle is in the ERV's path
1545 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Ego vehicle and ERV are both in lane index " << ego_lane_index_);
1546
1550 }
1553 }
1556
1559 }
1560 else{
1561 // ERV is close enough (in the same lane) that if the ego vehicle slows down it could cause a safety hazard; set internal members for ego vehicle to maintain its current speed
1564 non_reduced_speed_to_maintain_ = req->veh_logitudinal_velocity;
1565 }
1566 }
1567
1568 // Set flag to broadcast EmergencyVehicleResponse warning messages to ERV if they have not already been broadcasted
1572 }
1573 }
1574 else{
1575 RCLCPP_WARN_STREAM(rclcpp::get_logger(logger_name), "Unsupported seconds_until_passing of " << tracked_erv_.seconds_until_passing << ", returning empty maneuver plan");
1576 resp->new_plan.maneuvers = {};
1577 return;
1578 }
1579 }
1580 else{
1581 // Trigger state machine transition for case in which the ego vehicle is not in the ERV's path
1582 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Ego vehicle is not in ERV's path");
1583
1587 }
1590 }
1591 else{
1592 RCLCPP_WARN_STREAM(rclcpp::get_logger(logger_name), "Unsupported seconds_until_passing of " << tracked_erv_.seconds_until_passing << ", returning empty maneuver plan");
1593 resp->new_plan.maneuvers = {};
1594 return;
1595 }
1596 }
1597 }
1598
1599 // Set initial maneuver plan parameters based on the ego vehicle's initial state
1600 double downtrack_progress = req->veh_downtrack;
1601 double current_lanelet_ending_downtrack = wm_->routeTrackPos(ego_current_lanelet_optional.get().centerline2d().back()).downtrack;
1602 double speed_progress = req->veh_logitudinal_velocity;
1603 double target_speed = getLaneletSpeedLimit(ego_current_lanelet_optional.get());
1604 rclcpp::Time time_progress = rclcpp::Time(req->header.stamp);
1605
1606 // Generate maneuver plan based on the current state
1607 switch (transition_table_.getState())
1608 {
1610 resp->new_plan.maneuvers = {};
1611 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "No approaching ERV. Returning empty maneuver plan");
1612 is_maintaining_non_reduced_speed_ = false; // Reset flag since ego vehicle does not need to maintain a non-reduced speed
1613 break;
1614
1616 is_maintaining_non_reduced_speed_ = false; // Reset flag since ego vehicle does not need to maintain a non-reduced speed
1617
1618 generateMoveOverManeuverPlan(resp, ego_current_lanelet_optional.get(), downtrack_progress, current_lanelet_ending_downtrack, speed_progress,
1619 target_speed, time_progress, ego_lane_index_, tracked_erv_.lane_index);
1620 break;
1621
1623 // Generate a maneuver plan consisting of only lane-following maneuvers at a reduced speed since the ERV is actively passing the ego vehicle
1624 generateReducedSpeedLaneFollowingeManeuverPlan(resp, ego_current_lanelet_optional.get(), downtrack_progress, current_lanelet_ending_downtrack, speed_progress,
1625 target_speed, time_progress, is_maintaining_non_reduced_speed_);
1626 break;
1627
1628 default:
1629 throw std::invalid_argument("Transition table in unsupported state");
1630 }
1631
1632 latest_maneuver_plan_ = resp->new_plan;
1633 }
void generateMoveOverManeuverPlan(carma_planning_msgs::srv::PlanManeuvers::Response::SharedPtr resp, lanelet::ConstLanelet current_lanelet, double downtrack_progress, double current_lanelet_ending_downtrack, double speed_progress, double target_speed, rclcpp::Time time_progress, int ego_lane_index, int erv_lane_index)
Function to generate a maneuver plan when the ego vehicle must change lanes due to being in the MOVIN...
boost::optional< lanelet::ConstLanelet > getLaneletOnEgoRouteFromMapPosition(const double &x_position, const double &y_position)
Helper function to convert a map x,y coordinate pair to a lanelet on the ego vehicle's route.
void generateReducedSpeedLaneFollowingeManeuverPlan(carma_planning_msgs::srv::PlanManeuvers::Response::SharedPtr resp, lanelet::ConstLanelet current_lanelet, double downtrack_progress, double current_lanelet_ending_downtrack, double speed_progress, double target_speed, rclcpp::Time time_progress, bool is_maintaining_non_reduced_speed)
Function to generate a maneuver plan when the ego vehicle must remain in its lane due to being in the...

References approaching_emergency_vehicle_plugin::APPROACHING_ERV_IN_PATH, approaching_emergency_vehicle_plugin::APPROACHING_ERV_NOT_IN_PATH, approaching_emergency_vehicle_plugin::Config::approaching_threshold, config_, ego_lane_index_, approaching_emergency_vehicle_plugin::UpcomingLaneChangeParameters::end_dist, approaching_emergency_vehicle_plugin::ERV_PASSING_IN_PATH, approaching_emergency_vehicle_plugin::ApproachingEmergencyVehicleTransitionTable::event(), generateMoveOverManeuverPlan(), generateReducedSpeedLaneFollowingeManeuverPlan(), getLaneletOnEgoRouteFromMapPosition(), getLaneletSpeedLimit(), approaching_emergency_vehicle_plugin::ApproachingEmergencyVehicleTransitionTable::getState(), has_broadcasted_warning_messages_, has_planned_upcoming_lc_, has_tracked_erv_, is_maintaining_non_reduced_speed_, approaching_emergency_vehicle_plugin::ErvInformation::lane_index, latest_maneuver_plan_, logger_name, MAINTAIN_SPEED_THRESHOLD, approaching_emergency_vehicle_plugin::MOVING_OVER_FOR_APPROACHING_ERV, approaching_emergency_vehicle_plugin::NO_APPROACHING_ERV, non_reduced_speed_to_maintain_, approaching_emergency_vehicle_plugin::Config::passing_threshold, approaching_emergency_vehicle_plugin::ErvInformation::seconds_until_passing, should_broadcast_warnings_, approaching_emergency_vehicle_plugin::SLOWING_DOWN_FOR_ERV, approaching_emergency_vehicle_plugin::UpcomingLaneChangeParameters::start_dist, tracked_erv_, transition_table_, upcoming_lc_params_, and wm_.

Here is the call graph for this function:

◆ publishApproachingErvStatus()

void approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::publishApproachingErvStatus ( )
private

This is a callback function for the approaching_emergency_vehicle_status_timer_. It makes a call to generateApproachingErvStatusMessage() and publishes the generated message to approaching_erv_status_pub_.

Definition at line 274 of file approaching_emergency_vehicle_plugin_node.cpp.

274 {
275 // Generate the status message and publish it to the applicable ROS topic
276 carma_msgs::msg::UIInstructions status_msg = generateApproachingErvStatusMessage();
277 approaching_erv_status_pub_->publish(status_msg);
278 }
carma_msgs::msg::UIInstructions generateApproachingErvStatusMessage()
Function to generate a carma_msgs::msg::UIInstructions message that describes whether there is curren...

References approaching_erv_status_pub_, and generateApproachingErvStatusMessage().

Referenced by on_activate_plugin().

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

◆ publishHazardLightStatus()

void approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::publishHazardLightStatus ( )
private

This is a callback function for publishing turn ON/OFF (true/false) hazard light command to the ssc driver.

Definition at line 218 of file approaching_emergency_vehicle_plugin_node.cpp.

References ego_lane_index_, approaching_emergency_vehicle_plugin::ApproachingEmergencyVehicleTransitionTable::getState(), has_tracked_erv_, hazard_light_cmd_, hazard_light_cmd_pub_, approaching_emergency_vehicle_plugin::ErvInformation::lane_index, approaching_emergency_vehicle_plugin::SLOWING_DOWN_FOR_ERV, tracked_erv_, and transition_table_.

Referenced by on_activate_plugin().

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

◆ routeCallback()

void approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::routeCallback ( carma_planning_msgs::msg::Route::UniquePtr  msg)

Route subscription callback, with is used to update this plugin's future_route_lanelet_ids_ object.

Parameters
msgThe latest Route message.

Definition at line 1003 of file approaching_emergency_vehicle_plugin_node.cpp.

1003 {
1004 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "Received route callback");
1005 std::vector<int> new_future_route_lanelet_ids;
1006
1007 for(size_t i = 0; i < msg->route_path_lanelet_ids.size(); ++i){
1008 RCLCPP_DEBUG_STREAM(rclcpp::get_logger(logger_name), "New route lanelet: " << msg->route_path_lanelet_ids[i]);
1009 new_future_route_lanelet_ids.push_back(msg->route_path_lanelet_ids[i]);
1010 }
1011
1012 future_route_lanelet_ids_ = new_future_route_lanelet_ids;
1013 }

References future_route_lanelet_ids_, process_bag::i, and logger_name.

Referenced by on_configure_plugin().

Here is the caller graph for this function:

◆ routeStateCallback()

void approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::routeStateCallback ( carma_planning_msgs::msg::RouteState::UniquePtr  msg)

Route State subscription callback, with is used to update this plugin's latest_route_state_ object.

Parameters
msgThe latest Route State message.

Definition at line 985 of file approaching_emergency_vehicle_plugin_node.cpp.

986 {
987 latest_route_state_ = *msg;
988 }

References latest_route_state_.

Referenced by on_configure_plugin().

Here is the caller graph for this function:

◆ twistCallback()

void approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::twistCallback ( geometry_msgs::msg::TwistStamped::UniquePtr  msg)

Subscription callback for the twist subscriber, which will store latest current velocity of the ego vehicle.

Parameters
msgLatest twist message

Definition at line 990 of file approaching_emergency_vehicle_plugin_node.cpp.

990 {
991 current_speed_ = msg->twist.linear.x;
992 }

References current_speed_.

Referenced by on_configure_plugin().

Here is the caller graph for this function:

Member Data Documentation

◆ approaching_emergency_vehicle_status_timer_

rclcpp::TimerBase::SharedPtr approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::approaching_emergency_vehicle_status_timer_
private

Definition at line 409 of file approaching_emergency_vehicle_plugin_node.hpp.

Referenced by on_activate_plugin().

◆ APPROACHING_ERV_STATUS_PARAMS

const std::string approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::APPROACHING_ERV_STATUS_PARAMS = "HAS_APPROACHING_ERV:%1%,TIME_UNTIL_PASSING:%2$.1f,EGO_VEHICLE_ACTION:%3%"
private

Formatted string for conveying the status of this plugin and the ego vehicle's current action in response to an approaching ERV.

  • Index 0: (Boolean) Value indicating whether the ego vehicle is tracking an approaching ERV
  • Index 1: (Double; rounded to first decimal place) If an approaching ERV exists, this indicates the estimated seconds until the ERV passes the ego vehicle.
  • Index 2: (String) If an approaching ERV exists, this describes the current action of the ego vehicle in response to the approaching ERV.
  • NOTE: The values of indexes 1 and 2 can be ignored if index 0 indicates that no approaching ERV is being tracked.

Definition at line 390 of file approaching_emergency_vehicle_plugin_node.hpp.

Referenced by generateApproachingErvStatusMessage().

◆ approaching_erv_status_pub_

carma_ros2_utils::PubPtr<carma_msgs::msg::UIInstructions> approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::approaching_erv_status_pub_
private

◆ config_

◆ current_speed_

double approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::current_speed_
private

◆ ego_lane_index_

int approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::ego_lane_index_
private

◆ epsilon_

◆ erv_timeout_timer_

rclcpp::TimerBase::SharedPtr approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::erv_timeout_timer_
private

Definition at line 393 of file approaching_emergency_vehicle_plugin_node.hpp.

Referenced by on_activate_plugin().

◆ erv_world_model_

std::shared_ptr<carma_wm::CARMAWorldModel> approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::erv_world_model_
private

◆ future_route_lanelet_ids_

std::vector<int> approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::future_route_lanelet_ids_
private

◆ georeference_sub_

carma_ros2_utils::SubPtr<std_msgs::msg::String> approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::georeference_sub_
private

Definition at line 117 of file approaching_emergency_vehicle_plugin_node.hpp.

Referenced by on_configure_plugin().

◆ guidance_state_sub_

carma_ros2_utils::SubPtr<carma_planning_msgs::msg::GuidanceState> approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::guidance_state_sub_
private

Definition at line 125 of file approaching_emergency_vehicle_plugin_node.hpp.

Referenced by on_configure_plugin().

◆ has_broadcasted_warning_messages_

bool approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::has_broadcasted_warning_messages_ = false
private

◆ has_planned_upcoming_lc_

bool approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::has_planned_upcoming_lc_ = false
private

◆ has_received_route_state_

bool approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::has_received_route_state_ = false
private

◆ has_tracked_erv_

◆ hazard_light_cmd_

bool approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::hazard_light_cmd_ = false
private

◆ hazard_light_cmd_pub_

carma_ros2_utils::PubPtr<std_msgs::msg::Bool> approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::hazard_light_cmd_pub_
private

◆ hazard_light_timer_

rclcpp::TimerBase::SharedPtr approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::hazard_light_timer_
private

Definition at line 412 of file approaching_emergency_vehicle_plugin_node.hpp.

Referenced by on_activate_plugin().

◆ incoming_bsm_sub_

carma_ros2_utils::SubPtr<carma_v2x_msgs::msg::BSM> approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::incoming_bsm_sub_
private

Definition at line 115 of file approaching_emergency_vehicle_plugin_node.hpp.

Referenced by on_configure_plugin().

◆ incoming_emergency_vehicle_ack_sub_

carma_ros2_utils::SubPtr<carma_v2x_msgs::msg::EmergencyVehicleAck> approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::incoming_emergency_vehicle_ack_sub_
private

Definition at line 123 of file approaching_emergency_vehicle_plugin_node.hpp.

Referenced by on_configure_plugin().

◆ is_guidance_engaged_

bool approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::is_guidance_engaged_ = false
private

◆ is_maintaining_non_reduced_speed_

bool approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::is_maintaining_non_reduced_speed_ = false
private

◆ is_same_direction_

std::unordered_map<std::string, bool> approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::is_same_direction_
private

◆ latest_erv_update_times_

std::unordered_map<std::string, rclcpp::Time> approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::latest_erv_update_times_
private

◆ latest_maneuver_plan_

carma_planning_msgs::msg::ManeuverPlan approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::latest_maneuver_plan_
private

◆ latest_route_state_

carma_planning_msgs::msg::RouteState approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::latest_route_state_
private

◆ logger_name

◆ MAINTAIN_SPEED_THRESHOLD

const double approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::MAINTAIN_SPEED_THRESHOLD = 8.0
private

◆ map_projector_

boost::optional<std::string> approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::map_projector_
private

◆ non_reduced_speed_to_maintain_

double approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::non_reduced_speed_to_maintain_ = 4.4704
private

◆ num_warnings_broadcasted_

int approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::num_warnings_broadcasted_ = 0
private

◆ outgoing_emergency_vehicle_response_pub_

carma_ros2_utils::PubPtr<carma_v2x_msgs::msg::EmergencyVehicleResponse> approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::outgoing_emergency_vehicle_response_pub_
private

◆ route_state_sub_

carma_ros2_utils::SubPtr<carma_planning_msgs::msg::RouteState> approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::route_state_sub_
private

Definition at line 119 of file approaching_emergency_vehicle_plugin_node.hpp.

Referenced by on_configure_plugin().

◆ route_sub_

carma_ros2_utils::SubPtr<carma_planning_msgs::msg::Route> approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::route_sub_
private

Definition at line 127 of file approaching_emergency_vehicle_plugin_node.hpp.

Referenced by on_configure_plugin().

◆ should_broadcast_warnings_

bool approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::should_broadcast_warnings_ = false
private

◆ strategic_plugin_name_

std::string approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::strategic_plugin_name_ = "approaching_emergency_vehicle_plugin"
private

◆ tracked_erv_

◆ transition_table_

◆ twist_sub_

carma_ros2_utils::SubPtr<geometry_msgs::msg::TwistStamped> approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::twist_sub_
private

Definition at line 121 of file approaching_emergency_vehicle_plugin_node.hpp.

Referenced by on_configure_plugin().

◆ upcoming_lc_params_

UpcomingLaneChangeParameters approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::upcoming_lc_params_
private

◆ warning_broadcast_timer_

rclcpp::TimerBase::SharedPtr approaching_emergency_vehicle_plugin::ApproachingEmergencyVehiclePlugin::warning_broadcast_timer_
private

Definition at line 406 of file approaching_emergency_vehicle_plugin_node.hpp.

Referenced by on_activate_plugin().

◆ wm_


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