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.
carma_wm_ctrl::WMBroadcaster Class Reference

Class which provies exposes map publication and carma_wm update logic. More...

#include <WMBroadcaster.hpp>

Collaboration diagram for carma_wm_ctrl::WMBroadcaster:
Collaboration graph

Public Types

using PublishMapCallback = std::function< void(const autoware_lanelet2_msgs::msg::MapBin &)>
 
using PublishMapUpdateCallback = std::function< void(const autoware_lanelet2_msgs::msg::MapBin &)>
 
using PublishCtrlRequestCallback = std::function< void(const carma_v2x_msgs::msg::TrafficControlRequest &)>
 
using PublishActiveGeofCallback = std::function< void(const carma_perception_msgs::msg::CheckActiveGeofence &)>
 
using PublishMobilityOperationCallback = std::function< void(const carma_v2x_msgs::msg::MobilityOperation &)>
 

Public Member Functions

 WMBroadcaster (const PublishMapCallback &map_pub, const PublishMapUpdateCallback &map_update_pub, const PublishCtrlRequestCallback &control_msg_pub, const PublishActiveGeofCallback &active_pub, std::shared_ptr< carma_ros2_utils::timers::TimerFactory > timer_factory, const PublishMobilityOperationCallback &tcm_ack_pub)
 Constructor. More...
 
void baseMapCallback (autoware_lanelet2_msgs::msg::MapBin::UniquePtr map_msg)
 Callback to set the base map when it has been loaded. More...
 
void geoReferenceCallback (std_msgs::msg::String::UniquePtr geo_ref)
 Callback to set the base map georeference (proj string) More...
 
void geofenceCallback (carma_v2x_msgs::msg::TrafficControlMessage::UniquePtr geofence_msg)
 Callback to add a geofence to the map. Currently only supports version 1 TrafficControlMessage. More...
 
void externalMapMsgCallback (carma_v2x_msgs::msg::MapData::UniquePtr map_msg)
 Callback to MAP.msg which contains intersections' static info such geometry and lane ids. More...
 
void addGeofence (std::shared_ptr< Geofence > gf_ptr)
 Adds a geofence to the current map and publishes the ROS msg. More...
 
void removeGeofence (std::shared_ptr< Geofence > gf_ptr)
 Removes a geofence from the current map and publishes the ROS msg. More...
 
void routeCallbackMessage (carma_planning_msgs::msg::Route::UniquePtr route_msg)
 Calls controlRequestFromRoute() and publishes the TrafficControlRequest Message returned after the completed operations. More...
 
visualization_msgs::msg::Marker composeVisualizerMarkerFromPts (const visualization_msgs::msg::MarkerArray &marker_array, const std::vector< lanelet::Point3d > &input)
 Visualizes in Rviz geometry points related to MAP msg or TrafficControlMessage. More...
 
carma_v2x_msgs::msg::TrafficControlRequestPolygon composeTCRStatus (const lanelet::BasicPoint3d &localPoint, const carma_v2x_msgs::msg::TrafficControlBounds &cB, const lanelet::projection::LocalFrameProjector &local_projector)
 composeTCRStatus() compose TCM Request visualization on UI More...
 
carma_v2x_msgs::msg::TrafficControlRequest controlRequestFromRoute (const carma_planning_msgs::msg::Route &route_msg, std::shared_ptr< j2735_v2x_msgs::msg::Id64b > req_id_for_testing=NULL)
 Pulls vehicle information from CARMA Cloud at startup by providing its selected route in a TrafficControlRequest message that is published after a route is selected. During operation at ~10s intervals the vehicle will make another control request for the remainder of its route. More...
 
lanelet::Points3d getPointsInLocalFrame (const carma_v2x_msgs::msg::TrafficControlMessageV01 &geofence_msg)
 Extract geofence points from geofence message using its given proj and datum fields. More...
 
lanelet::ConstLaneletOrAreas getAffectedLaneletOrAreas (const lanelet::Points3d &gf_pts)
 Gets the affected lanelet or areas based on the points in local frame. More...
 
void setMaxLaneWidth (double max_lane_width)
 Sets the max lane width in meters. Geofence points are associated to a lanelet if they are within this distance to a lanelet as geofence points are guaranteed to apply to a single lane. More...
 
void setIntersectionCoordCorrection (const std::vector< int64_t > &intersection_ids_for_correction, const std::vector< double > &intersection_correction)
 Sets the coordinate correction for intersection. More...
 
void setConfigSpeedLimit (double cL)
 Sets the configured speed limit. More...
 
void setVehicleParticipationType (std::string participant)
 Set the Vehicle Participation Type. More...
 
std::string getVehicleParticipationType ()
 Get the Vehicle Participation Type object. More...
 
void geofenceFromMsg (std::shared_ptr< Geofence > gf_ptr, const carma_v2x_msgs::msg::TrafficControlMessageV01 &geofence_msg)
 Fills geofence object from TrafficControlMessageV01 ROS Msg. More...
 
std::vector< std::shared_ptr< Geofence > > geofenceFromMapMsg (std::shared_ptr< Geofence > gf_ptr, const carma_v2x_msgs::msg::MapData &map_msg)
 Fills geofence object from MAP Data ROS Msg which contains intersections' static data such as geometry and signal_group. More...
 
double distToNearestActiveGeofence (const lanelet::BasicPoint2d &curr_pos)
 Returns the route distance (downtrack or crosstrack in meters) to the nearest active geofence lanelet. More...
 
void currentLocationCallback (geometry_msgs::msg::PoseStamped::UniquePtr current_pos)
 
carma_perception_msgs::msg::CheckActiveGeofence checkActiveGeofenceLogic (const geometry_msgs::msg::PoseStamped &current_pos)
 Returns a message indicating whether or not the vehicle is inside of an active geofence lanelet. More...
 
void addRegionAccessRule (std::shared_ptr< Geofence > gf_ptr, const carma_v2x_msgs::msg::TrafficControlMessageV01 &msg_v01, const std::vector< lanelet::Lanelet > &affected_llts) const
 Adds RegionAccessRule to the map. More...
 
void addRegionMinimumGap (std::shared_ptr< Geofence > gf_ptr, const carma_v2x_msgs::msg::TrafficControlMessageV01 &msg_v01, double min_gap, const std::vector< lanelet::Lanelet > &affected_llts, const std::vector< lanelet::Area > &affected_areas) const
 Adds Minimum Gap to the map. More...
 
std::vector< std::string > participantsChecker (const carma_v2x_msgs::msg::TrafficControlMessageV01 &msg_v01) const
 Generates participants list. More...
 
std::vector< std::string > invertParticipants (const std::vector< std::string > &input_participants) const
 Generates inverse participants list of the given participants. More...
 
std::vector< std::string > combineParticipantsToVehicle (const std::vector< std::string > &input_participants) const
 Combines a list of the given participants into a single "vehicle" type if participants cover all possible vehicle types. Returns the input with no change if it doesn't cover all. More...
 
carma_planning_msgs::msg::Route getRoute ()
 Returns the most recently recieved route message. More...
 
std::shared_ptr< GeofencecreateWorkzoneGeofence (std::unordered_map< uint8_t, std::shared_ptr< Geofence > > work_zone_geofence_cache)
 Creates a single workzone geofence (in the vector) that includes all additional lanelets (housing traffic lights) and update_list that blocks old lanelets. Geofence will have minimum of 6 lanelet_additions_ (front parallel, front diagonal, middle lanelet(s), back diagonal, back parallel, 1 opposing lanelet with trafficlight). And regulatory_element_ of this geofence will be region_access_rule, where it blocks entire affected_parts of CLOSED, TAPERRIGHT, OPENRIGHT. It also blocks the opposing lane's lanelet that would have had the trafficlight, and new lanelet(s) would be added to replace it. More...
 
void preprocessWorkzoneGeometry (std::unordered_map< uint8_t, std::shared_ptr< Geofence > > work_zone_geofence_cache, std::shared_ptr< std::vector< lanelet::Lanelet > > parallel_llts, std::shared_ptr< std::vector< lanelet::Lanelet > > opposite_llts)
 Preprocess for workzone area. Parallel_llts will have front_parallel and back_parallel lanelets that were created from splitting (if necessary) TAPERRIGHT and OPENRIGHT lanelets. Opposite_llts will have new lanelets split from REVERSE part with same direction as parallel lanelets that connect to diagonal ones. More...
 
std::shared_ptr< GeofencecreateWorkzoneGeometry (std::unordered_map< uint8_t, std::shared_ptr< Geofence > > work_zone_geofence_cache, lanelet::Lanelet parallel_llt_front, lanelet::Lanelet parallel_llt_back, std::shared_ptr< std::vector< lanelet::Lanelet > > middle_opposite_lanelets)
 Create workzone geofence. Create diagonal lanelets and a lanelet that houses opposing lane's trafficlight. Fill lanelet_additions_ with every newly created lanelets New lanelets will have traffic light. Old lanelets (and those from CLOSED) will be blocked using update_list as region_access_rule. More...
 
std::vector< lanelet::Lanelet > splitLaneletWithPoint (const std::vector< lanelet::BasicPoint2d > &input_pts, const lanelet::Lanelet &input_llt, double error_distance)
 Split given lanelet with same proportion as the given points' downtrack relative to the lanelet. Newly created lanelet will have old regulatory elements copied into each of them. From the front and back boundaries, it is deemed not necessary to split if the ratios are within error_distance from either of it. For example, if front and back points of 3 points given are both within error_distance, only middle point will be used to split into 2 lanelets. It will return duplicate of old lanelet (with different id) if no splitting was done. More...
 
lanelet::Lanelets splitOppositeLaneletWithPoint (std::shared_ptr< std::vector< lanelet::Lanelet > > opposite_llts, const lanelet::BasicPoint2d &input_pt, const lanelet::Lanelet &input_llt, double error_distance)
 Split given lanelet's adjacent, OPPOSITE lanelet with same proportion as the given point's downtrack relative to the lanelet. Newly created lanelet will have old regulatory elements copied into each of them. From the front and back boundaries, it is deemed not necessary to split if the ratios are within error_distance from either of it. It will return duplicate of old lanelet (with different id) if no splitting was done. More...
 
std::vector< lanelet::Lanelet > splitLaneletWithRatio (std::vector< double > ratios, lanelet::Lanelet input_lanelet, double error_distance) const
 Split given lanelet with given downtrack ratios relative to the lanelet. Newly created lanelet will have old regulatory elements copied into each of them. From the front and back boundaries, it is deemed not necessary to split if the ratios are within error_distance from either of it. For example, if front and back points of 3 ratios given are both within error_distance, only middle ratio will be used to split, so 2 lanelets will be returned. It will return duplicate of old lanelet (with different id) if no splitting was done. More...
 
uint32_t generate32BitId (const std::string &label)
 helper for generating 32bit traffic light Id from TCM label field consisting workzone intersection/signal group ids More...
 
bool convertLightIdToInterGroupId (unsigned &intersection_id, unsigned &group_id, const lanelet::Id &lanelet_id)
 helper for generating intersection and group Id of a traffic light from lanelet id More...
 
void setErrorDistance (double error_distance)
 
void publishLightId ()
 helps to populate upcoming_intersection_ids_ from local traffic lanelet ids More...
 
void setConfigVehicleId (const std::string &vehicle_id)
 Retrieve the vehicle ID from global vehicle parameters, and set instance memeber vehicle id. More...
 
void setConfigACKPubTimes (int ack_pub_times)
 Sets the TCM Acknowledgement publish times. More...
 
void pubTCMACK (j2735_v2x_msgs::msg::Id64b tcm_req_id, uint16_t msgnum, int ack_status, const std::string &ack_reason)
 Construct TCM acknowledgement object and populate it with params. Publish the object for a configured number of times. More...
 
void updateUpcomingSGIntersectionIds ()
 populate upcoming_intersection_ids_ from local traffic lanelet ids More...
 

Public Attributes

visualization_msgs::msg::MarkerArray tcm_marker_array_
 
visualization_msgs::msg::MarkerArray j2735_map_msg_marker_array_
 
carma_v2x_msgs::msg::TrafficControlRequestPolygon tcr_polygon_
 
std_msgs::msg::Int32MultiArray upcoming_intersection_ids_
 

Private Types

enum class  AcknowledgementStatus { ACKNOWLEDGED = 1 , REJECTED = 2 }
 

Private Member Functions

void addRegulatoryComponent (std::shared_ptr< Geofence > gf_ptr) const
 
void addBackRegulatoryComponent (std::shared_ptr< Geofence > gf_ptr) const
 
void removeGeofenceHelper (std::shared_ptr< Geofence > gf_ptr) const
 
void addGeofenceHelper (std::shared_ptr< Geofence > gf_ptr)
 
bool shouldChangeControlLine (const lanelet::ConstLaneletOrArea &el, const lanelet::RegulatoryElementConstPtr &regem, std::shared_ptr< Geofence > gf_ptr) const
 This is a helper function that returns true if the provided regem is marked to be changed by the geofence as there are usually multiple passing control lines are in the lanelet. More...
 
bool shouldChangeTrafficSignal (const lanelet::ConstLaneletOrArea &el, const lanelet::RegulatoryElementConstPtr &regem, std::shared_ptr< carma_wm::SignalizedIntersectionManager > sim) const
 This is a helper function that returns true if signal in the lanelet should be changed according to the records of signalizer intersection manager Used in managing multiple signal_groups in a single entry lanelet for example. More...
 
void addPassingControlLineFromMsg (std::shared_ptr< Geofence > gf_ptr, const carma_v2x_msgs::msg::TrafficControlMessageV01 &msg_v01, const std::vector< lanelet::Lanelet > &affected_llts) const
 
void addScheduleFromMsg (std::shared_ptr< Geofence > gf_ptr, const carma_v2x_msgs::msg::TrafficControlMessageV01 &msg_v01)
 Populates the schedules member of the geofence object from given TrafficControlMessageV01 message. More...
 
void scheduleGeofence (std::shared_ptr< carma_wm_ctrl::Geofence > gf_ptr_list)
 
lanelet::LineString3d createLinearInterpolatingLinestring (const lanelet::Point3d &front_pt, const lanelet::Point3d &back_pt, double increment_distance=0.25)
 
lanelet::Lanelet createLinearInterpolatingLanelet (const lanelet::Point3d &left_front_pt, const lanelet::Point3d &right_front_pt, const lanelet::Point3d &left_back_pt, const lanelet::Point3d &right_back_pt, double increment_distance=0.25)
 
std::unordered_set< lanelet::Lanelet > filterSuccessorLanelets (const std::unordered_set< lanelet::Lanelet > &possible_lanelets, const std::unordered_set< lanelet::Lanelet > &root_lanelets)
 

Private Attributes

double error_distance_ = 5
 
lanelet::ConstLanelets route_path_
 
std::unordered_set< lanelet::Id > active_geofence_llt_ids_
 
std::unordered_map< uint8_t, std::shared_ptr< Geofence > > work_zone_geofence_cache_
 
std::unordered_map< uint32_t, lanelet::Id > traffic_light_id_lookup_
 
lanelet::LaneletMapPtr base_map_
 
lanelet::LaneletMapPtr current_map_
 
lanelet::routing::RoutingGraphPtr current_routing_graph_
 
lanelet::Velocity config_limit
 
std::string participant_ = lanelet::Participants::VehicleCar
 
std::unordered_set< std::string > checked_geofence_ids_
 
std::unordered_set< std::string > generated_geofence_reqids_
 
std::vector< lanelet::LaneletMapPtr > cached_maps_
 
std::mutex map_mutex_
 
PublishMapCallback map_pub_
 
PublishMapUpdateCallback map_update_pub_
 
PublishCtrlRequestCallback control_msg_pub_
 
PublishActiveGeofCallback active_pub_
 
GeofenceScheduler scheduler_
 
PublishMobilityOperationCallback tcm_ack_pub_
 
std::string base_map_georef_
 
double max_lane_width_
 
std::vector< carma_v2x_msgs::msg::TrafficControlMessageV01 > workzone_remaining_msgs_
 
bool workzone_geometry_published_ = false
 
size_t current_map_version_ = 0
 
carma_planning_msgs::msg::Route current_route
 
std::vector< autoware_lanelet2_msgs::msg::MapBin > map_update_message_queue_
 
size_t update_count_ = -1
 
std::shared_ptr< carma_wm::SignalizedIntersectionManagersim_
 
const std::string geofence_ack_strategy_ = "carma3/Geofence_Acknowledgement"
 
int ack_pub_times_ = 1
 
std::string vehicle_id_
 

Detailed Description

Class which provies exposes map publication and carma_wm update logic.

The WMBroadcaster handles updating the lanelet2 base map and publishing the new versions to the rest of the CARMA Platform ROS network. The broadcaster also provides functions for adding or removing geofences from the map and notifying the rest of the system.

Definition at line 72 of file WMBroadcaster.hpp.

Member Typedef Documentation

◆ PublishActiveGeofCallback

using carma_wm_ctrl::WMBroadcaster::PublishActiveGeofCallback = std::function<void(const carma_perception_msgs::msg::CheckActiveGeofence&)>

Definition at line 78 of file WMBroadcaster.hpp.

◆ PublishCtrlRequestCallback

using carma_wm_ctrl::WMBroadcaster::PublishCtrlRequestCallback = std::function<void(const carma_v2x_msgs::msg::TrafficControlRequest&)>

Definition at line 77 of file WMBroadcaster.hpp.

◆ PublishMapCallback

using carma_wm_ctrl::WMBroadcaster::PublishMapCallback = std::function<void(const autoware_lanelet2_msgs::msg::MapBin&)>

Definition at line 75 of file WMBroadcaster.hpp.

◆ PublishMapUpdateCallback

using carma_wm_ctrl::WMBroadcaster::PublishMapUpdateCallback = std::function<void(const autoware_lanelet2_msgs::msg::MapBin&)>

Definition at line 76 of file WMBroadcaster.hpp.

◆ PublishMobilityOperationCallback

using carma_wm_ctrl::WMBroadcaster::PublishMobilityOperationCallback = std::function<void(const carma_v2x_msgs::msg::MobilityOperation&)>

Definition at line 79 of file WMBroadcaster.hpp.

Member Enumeration Documentation

◆ AcknowledgementStatus

Enumerator
ACKNOWLEDGED 
REJECTED 

Definition at line 469 of file WMBroadcaster.hpp.

469 {
470 ACKNOWLEDGED = 1,
471 REJECTED = 2
472 };

Constructor & Destructor Documentation

◆ WMBroadcaster()

carma_wm_ctrl::WMBroadcaster::WMBroadcaster ( const PublishMapCallback map_pub,
const PublishMapUpdateCallback map_update_pub,
const PublishCtrlRequestCallback control_msg_pub,
const PublishActiveGeofCallback active_pub,
std::shared_ptr< carma_ros2_utils::timers::TimerFactory >  timer_factory,
const PublishMobilityOperationCallback tcm_ack_pub 
)

Constructor.

Definition at line 47 of file WMBroadcaster.cpp.

49 : map_pub_(map_pub), map_update_pub_(map_update_pub), control_msg_pub_(control_msg_pub), active_pub_(active_pub), scheduler_(timer_factory), tcm_ack_pub_(tcm_ack_pub)
50{
53 std::bind(&WMBroadcaster::routeCallbackMessage, this, _1);
54};
void onGeofenceActive(std::function< void(std::shared_ptr< Geofence >)> active_callback)
Method which allows the user to set a callback which will be triggered when a geofence becomes active...
void onGeofenceInactive(std::function< void(std::shared_ptr< Geofence >)> inactive_callback)
Method which allows the user to set a callback which will be triggered when a geofence becomes in-act...
PublishCtrlRequestCallback control_msg_pub_
void removeGeofence(std::shared_ptr< Geofence > gf_ptr)
Removes a geofence from the current map and publishes the ROS msg.
void addGeofence(std::shared_ptr< Geofence > gf_ptr)
Adds a geofence to the current map and publishes the ROS msg.
void routeCallbackMessage(carma_planning_msgs::msg::Route::UniquePtr route_msg)
Calls controlRequestFromRoute() and publishes the TrafficControlRequest Message returned after the co...
PublishMobilityOperationCallback tcm_ack_pub_
PublishActiveGeofCallback active_pub_
PublishMapUpdateCallback map_update_pub_

References addGeofence(), carma_wm_ctrl::GeofenceScheduler::onGeofenceActive(), carma_wm_ctrl::GeofenceScheduler::onGeofenceInactive(), removeGeofence(), routeCallbackMessage(), and scheduler_.

Here is the call graph for this function:

Member Function Documentation

◆ addBackRegulatoryComponent()

void carma_wm_ctrl::WMBroadcaster::addBackRegulatoryComponent ( std::shared_ptr< Geofence gf_ptr) const
private

Definition at line 1464 of file WMBroadcaster.cpp.

1465{
1466 // First loop is to remove the relation between element and regulatory element that this geofence added initially
1467
1468 for (auto el: gf_ptr->affected_parts_)
1469 {
1470 for (auto regem : el.regulatoryElements())
1471 {
1472 if (!shouldChangeControlLine(el, regem, gf_ptr)) continue;
1473
1474 if (regem->attribute(lanelet::AttributeName::Subtype).value() == gf_ptr->regulatory_element_->attribute(lanelet::AttributeName::Subtype).value())
1475 {
1476 auto nonconst_regem = current_map_->regulatoryElementLayer.get(regem->id());
1477 gf_ptr->remove_list_.push_back(std::make_pair(el.id(), nonconst_regem));
1478 current_map_->remove(current_map_->laneletLayer.get(el.lanelet()->id()), nonconst_regem);
1479 }
1480 }
1481 }
1482
1483 // As this gf received is the first gf that was sent in through addGeofence,
1484 // we have prev speed limit information inside it to put them back
1485 for (auto pair : gf_ptr->prev_regems_)
1486 {
1487 if (pair.second->attribute(lanelet::AttributeName::Subtype).value() == gf_ptr->regulatory_element_->attribute(lanelet::AttributeName::Subtype).value())
1488 {
1489 current_map_->update(current_map_->laneletLayer.get(pair.first), pair.second);
1490 gf_ptr->update_list_.push_back(pair);
1491 }
1492 }
1493}
lanelet::LaneletMapPtr current_map_
bool shouldChangeControlLine(const lanelet::ConstLaneletOrArea &el, const lanelet::RegulatoryElementConstPtr &regem, std::shared_ptr< Geofence > gf_ptr) const
This is a helper function that returns true if the provided regem is marked to be changed by the geof...

References current_map_, and shouldChangeControlLine().

Referenced by removeGeofenceHelper().

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

◆ addGeofence()

void carma_wm_ctrl::WMBroadcaster::addGeofence ( std::shared_ptr< Geofence gf_ptr)

Adds a geofence to the current map and publishes the ROS msg.

Definition at line 1495 of file WMBroadcaster.cpp.

1496{
1497
1498 std::lock_guard<std::mutex> guard(map_mutex_);
1499 RCLCPP_INFO_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Adding active geofence to the map with geofence id: " << gf_ptr->id_);
1500
1501 // if applying workzone geometry geofence, utilize workzone chache to create one
1502 // also multiple map updates can be sent from one geofence object
1503 std::vector<std::shared_ptr<Geofence>> updates_to_send;
1504
1505 bool detected_workzone_signal = gf_ptr->msg_.package.label_exists && gf_ptr->msg_.package.label.find("SIG_WZ") != std::string::npos;
1506 bool detected_map_msg_signal = gf_ptr->msg_.package.label_exists && gf_ptr->msg_.package.label.find("MAP_MSG") != std::string::npos;
1507 if (detected_workzone_signal && gf_ptr->msg_.params.detail.choice != carma_v2x_msgs::msg::TrafficControlDetail::MAXSPEED_CHOICE)
1508 {
1509 for (auto gf_cache_ptr : work_zone_geofence_cache_)
1510 {
1511 geofenceFromMsg(gf_cache_ptr.second, gf_cache_ptr.second->msg_);
1512 }
1513 updates_to_send.push_back(createWorkzoneGeofence(work_zone_geofence_cache_));
1514 }
1515 else if (detected_map_msg_signal)
1516 {
1517 updates_to_send = geofenceFromMapMsg(gf_ptr, gf_ptr->map_msg_);
1518 }
1519 else
1520 {
1521 geofenceFromMsg(gf_ptr, gf_ptr->msg_);
1522 updates_to_send.push_back(gf_ptr);
1523 }
1524
1525 for (auto update : updates_to_send)
1526 {
1527 // add marker to rviz
1528 if (!update->gf_pts.empty())
1529 {
1530 if (update->label_ == carma_wm_ctrl::MAP_MSG_INTERSECTION)
1531 {
1533 }
1534 else
1535 {
1536 tcm_marker_array_.markers.push_back(composeVisualizerMarkerFromPts(tcm_marker_array_, update->gf_pts));
1537 }
1538 }
1539
1540 if (update->affected_parts_.empty())
1541 continue;
1542
1543 // Process the geofence object to populate update remove lists
1544 addGeofenceHelper(update);
1545
1546 if (!detected_map_msg_signal)
1547 {
1548 for (auto pair : update->update_list_) active_geofence_llt_ids_.insert(pair.first);
1549 }
1550
1551 autoware_lanelet2_msgs::msg::MapBin gf_msg;
1552
1553 // If the geofence invalidates the route graph then recompute the routing graph now that the map has been updated
1554 if (update->invalidate_route_) {
1555
1556 RCLCPP_INFO_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Rebuilding routing graph after is was invalidated by geofence");
1557
1558 lanelet::traffic_rules::TrafficRulesUPtr traffic_rules_car = lanelet::traffic_rules::TrafficRulesFactory::create(
1559 lanelet::traffic_rules::CarmaUSTrafficRules::Location, participant_);
1560 current_routing_graph_ = lanelet::routing::RoutingGraph::build(*current_map_, *traffic_rules_car);
1561
1562 RCLCPP_INFO_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Done rebuilding routing graph after is was invalidated by geofence");
1563
1564 // Populate routing graph structure
1565 RCLCPP_INFO_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Creating routing graph message");
1566
1567 auto readable_graph = std::static_pointer_cast<RoutingGraphAccessor>(current_routing_graph_);
1568
1569 gf_msg.routing_graph = readable_graph->routingGraphToMsg(participant_);
1570 gf_msg.has_routing_graph = true;
1571
1572 RCLCPP_INFO_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Done creating routing graph message");
1573 }
1574
1575
1576 // Publish
1577 auto send_data = std::make_shared<carma_wm::TrafficControl>(carma_wm::TrafficControl(update->id_, update->update_list_, update->remove_list_, update->lanelet_additions_));
1578 send_data->traffic_light_id_lookup_ = update->traffic_light_id_lookup_;
1579
1580 if (detected_map_msg_signal && updates_to_send.back() == update) // if last update
1581 {
1582 send_data->sim_ = *sim_;
1583 }
1584
1585 carma_wm::toBinMsg(send_data, &gf_msg);
1586 update_count_++; // Update the sequence count for the geofence messages
1587 gf_msg.seq_id = update_count_;
1588 gf_msg.invalidates_route=update->invalidate_route_;
1589 gf_msg.map_version = current_map_version_;
1590 map_update_pub_(gf_msg);
1591 }
1592
1593}
visualization_msgs::msg::MarkerArray tcm_marker_array_
visualization_msgs::msg::MarkerArray j2735_map_msg_marker_array_
std::unordered_map< uint8_t, std::shared_ptr< Geofence > > work_zone_geofence_cache_
lanelet::routing::RoutingGraphPtr current_routing_graph_
visualization_msgs::msg::Marker composeVisualizerMarkerFromPts(const visualization_msgs::msg::MarkerArray &marker_array, const std::vector< lanelet::Point3d > &input)
Visualizes in Rviz geometry points related to MAP msg or TrafficControlMessage.
void addGeofenceHelper(std::shared_ptr< Geofence > gf_ptr)
std::shared_ptr< Geofence > createWorkzoneGeofence(std::unordered_map< uint8_t, std::shared_ptr< Geofence > > work_zone_geofence_cache)
Creates a single workzone geofence (in the vector) that includes all additional lanelets (housing tra...
std::unordered_set< lanelet::Id > active_geofence_llt_ids_
std::shared_ptr< carma_wm::SignalizedIntersectionManager > sim_
std::vector< std::shared_ptr< Geofence > > geofenceFromMapMsg(std::shared_ptr< Geofence > gf_ptr, const carma_v2x_msgs::msg::MapData &map_msg)
Fills geofence object from MAP Data ROS Msg which contains intersections' static data such as geometr...
void geofenceFromMsg(std::shared_ptr< Geofence > gf_ptr, const carma_v2x_msgs::msg::TrafficControlMessageV01 &geofence_msg)
Fills geofence object from TrafficControlMessageV01 ROS Msg.
const std::string MAP_MSG_INTERSECTION
Definition: Geofence.hpp:40
void toBinMsg(std::shared_ptr< carma_wm::TrafficControl > gf_ptr, autoware_lanelet2_msgs::msg::MapBin *msg)

References active_geofence_llt_ids_, addGeofenceHelper(), composeVisualizerMarkerFromPts(), createWorkzoneGeofence(), current_map_, current_map_version_, current_routing_graph_, geofenceFromMapMsg(), geofenceFromMsg(), j2735_map_msg_marker_array_, carma_wm_ctrl::MAP_MSG_INTERSECTION, map_mutex_, map_update_pub_, participant_, sim_, tcm_marker_array_, carma_wm::toBinMsg(), update_count_, and work_zone_geofence_cache_.

Referenced by WMBroadcaster().

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

◆ addGeofenceHelper()

void carma_wm_ctrl::WMBroadcaster::addGeofenceHelper ( std::shared_ptr< Geofence gf_ptr)
private

Definition at line 1928 of file WMBroadcaster.cpp.

1929{
1930 // resetting the information inside geofence
1931 gf_ptr->remove_list_ = {};
1932 gf_ptr->update_list_ = {};
1933
1934 // add additional lanelets that need to be added
1935 for (auto llt : gf_ptr->lanelet_additions_)
1936 {
1937 current_map_->add(llt);
1938 }
1939
1940 // add trafficlight id mapping
1941 for (auto pair : gf_ptr->traffic_light_id_lookup_)
1942 {
1943 traffic_light_id_lookup_[pair.first] = pair.second;
1944 }
1945
1946 // Logic to determine what type of geofence
1947 addRegulatoryComponent(gf_ptr);
1948}
std::unordered_map< uint32_t, lanelet::Id > traffic_light_id_lookup_
void addRegulatoryComponent(std::shared_ptr< Geofence > gf_ptr) const

References addRegulatoryComponent(), current_map_, and traffic_light_id_lookup_.

Referenced by addGeofence().

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

◆ addPassingControlLineFromMsg()

void carma_wm_ctrl::WMBroadcaster::addPassingControlLineFromMsg ( std::shared_ptr< Geofence gf_ptr,
const carma_v2x_msgs::msg::TrafficControlMessageV01 &  msg_v01,
const std::vector< lanelet::Lanelet > &  affected_llts 
) const
private

Definition at line 887 of file WMBroadcaster.cpp.

888{
889 carma_v2x_msgs::msg::TrafficControlDetail msg_detail;
890 msg_detail = msg_v01.params.detail;
891 // Get affected bounds
892 lanelet::LineStrings3d pcl_bounds;
893 if (msg_detail.lataffinity == carma_v2x_msgs::msg::TrafficControlDetail::LEFT)
894 {
895 for (auto llt : affected_llts) pcl_bounds.push_back(llt.leftBound());
896 gf_ptr->pcl_affects_left_ = true;
897 }
898 else //right
899 {
900 for (auto llt : affected_llts) pcl_bounds.push_back(llt.rightBound());
901 gf_ptr->pcl_affects_right_ = true;
902 }
903
904 // Get specified participants
905 std::vector<std::string> left_participants;
906 std::vector<std::string> right_participants;
907 std::vector<std::string> participants=participantsChecker(msg_v01);
908
909 // Create the pcl depending on the allowed passing control direction, left, right, or both
910 if (msg_detail.latperm[0] == carma_v2x_msgs::msg::TrafficControlDetail::PERMITTED ||
911 msg_detail.latperm[0] == carma_v2x_msgs::msg::TrafficControlDetail::PASSINGONLY)
912 {
913 left_participants = participants;
914 }
915 else if (msg_detail.latperm[0] == carma_v2x_msgs::msg::TrafficControlDetail::EMERGENCYONLY)
916 {
917 left_participants.push_back(lanelet::Participants::VehicleEmergency);
918 }
919 if (msg_detail.latperm[1] == carma_v2x_msgs::msg::TrafficControlDetail::PERMITTED ||
920 msg_detail.latperm[1] == carma_v2x_msgs::msg::TrafficControlDetail::PASSINGONLY)
921 {
922 right_participants = participants;
923 }
924 else if (msg_detail.latperm[1] == carma_v2x_msgs::msg::TrafficControlDetail::EMERGENCYONLY)
925 {
926 right_participants.push_back(lanelet::Participants::VehicleEmergency);
927 }
928
929 gf_ptr->regulatory_element_ = std::make_shared<lanelet::PassingControlLine>(lanelet::PassingControlLine::buildData(
930 lanelet::utils::getId(), pcl_bounds, left_participants, right_participants));
931}
std::vector< std::string > participantsChecker(const carma_v2x_msgs::msg::TrafficControlMessageV01 &msg_v01) const
Generates participants list.

References participantsChecker().

Referenced by geofenceFromMsg().

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

◆ addRegionAccessRule()

void carma_wm_ctrl::WMBroadcaster::addRegionAccessRule ( std::shared_ptr< Geofence gf_ptr,
const carma_v2x_msgs::msg::TrafficControlMessageV01 &  msg_v01,
const std::vector< lanelet::Lanelet > &  affected_llts 
) const

Adds RegionAccessRule to the map.

Parameters
gf_ptrgeofence pointer
msg_v01message type
afffected_lltsaffected lanelets

Definition at line 933 of file WMBroadcaster.cpp.

934{
935 const std::string& reason = msg_v01.package.label;
936 gf_ptr->label_ = msg_v01.package.label;
937 auto regulatory_element = std::make_shared<lanelet::RegionAccessRule>(lanelet::RegionAccessRule::buildData(lanelet::utils::getId(),affected_llts,{},invertParticipants(participantsChecker(msg_v01)), reason));
938
939 if(!regulatory_element->accessable(lanelet::Participants::VehicleCar) || !regulatory_element->accessable(lanelet::Participants::VehicleTruck ))
940 {
941 gf_ptr->invalidate_route_=true;
942 }
943 else
944 {
945 gf_ptr->invalidate_route_=false;
946 }
947 gf_ptr->regulatory_element_ = regulatory_element;
948}
std::vector< std::string > invertParticipants(const std::vector< std::string > &input_participants) const
Generates inverse participants list of the given participants.

References invertParticipants(), and participantsChecker().

Referenced by createWorkzoneGeometry(), and geofenceFromMsg().

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

◆ addRegionMinimumGap()

void carma_wm_ctrl::WMBroadcaster::addRegionMinimumGap ( std::shared_ptr< Geofence gf_ptr,
const carma_v2x_msgs::msg::TrafficControlMessageV01 &  msg_v01,
double  min_gap,
const std::vector< lanelet::Lanelet > &  affected_llts,
const std::vector< lanelet::Area > &  affected_areas 
) const

Adds Minimum Gap to the map.

Parameters
gf_ptrgeofence pointer
doublemin_gap
afffected_lltsaffected lanelets
affected_areasaffected areas

Definition at line 950 of file WMBroadcaster.cpp.

951{
952 auto regulatory_element = std::make_shared<lanelet::DigitalMinimumGap>(lanelet::DigitalMinimumGap::buildData(lanelet::utils::getId(),
953 min_gap, affected_llts, affected_areas,participantsChecker(msg_v01) ));
954
955 gf_ptr->regulatory_element_ = regulatory_element;
956}

References participantsChecker().

Referenced by geofenceFromMsg().

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

◆ addRegulatoryComponent()

void carma_wm_ctrl::WMBroadcaster::addRegulatoryComponent ( std::shared_ptr< Geofence gf_ptr) const
private

Definition at line 1423 of file WMBroadcaster.cpp.

1424{
1425 // First loop is to save the relation between element and regulatory element
1426 // so that we can add back the old one after geofence deactivates
1427 for (auto el: gf_ptr->affected_parts_)
1428 {
1429 for (auto regem : el.regulatoryElements())
1430 {
1431 if (!shouldChangeControlLine(el, regem, gf_ptr) ||
1432 !shouldChangeTrafficSignal(el, regem, sim_))
1433 continue;
1434
1435 if (regem->attribute(lanelet::AttributeName::Subtype).value() == gf_ptr->regulatory_element_->attribute(lanelet::AttributeName::Subtype).value())
1436 {
1437 lanelet::RegulatoryElementPtr nonconst_regem = current_map_->regulatoryElementLayer.get(regem->id());
1438 gf_ptr->prev_regems_.push_back(std::make_pair(el.id(), nonconst_regem));
1439 gf_ptr->remove_list_.push_back(std::make_pair(el.id(), nonconst_regem));
1440 current_map_->remove(current_map_->laneletLayer.get(el.lanelet()->id()), nonconst_regem);
1441 }
1442 }
1443 }
1444
1445
1446 // this loop is also kept separately because previously we assumed
1447 // there was existing regem, but this handles changes to all of the elements
1448 for (auto el: gf_ptr->affected_parts_)
1449 {
1450 // update it with new regem
1451 if (gf_ptr->regulatory_element_->id() != lanelet::InvalId)
1452 {
1453 current_map_->update(current_map_->laneletLayer.get(el.id()), gf_ptr->regulatory_element_);
1454 gf_ptr->update_list_.push_back(std::pair<lanelet::Id, lanelet::RegulatoryElementPtr>(el.id(), gf_ptr->regulatory_element_));
1455 } else {
1456 RCLCPP_WARN_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Regulatory element with invalid id in geofence cannot be added to the map");
1457 }
1458 }
1459
1460
1461
1462}
bool shouldChangeTrafficSignal(const lanelet::ConstLaneletOrArea &el, const lanelet::RegulatoryElementConstPtr &regem, std::shared_ptr< carma_wm::SignalizedIntersectionManager > sim) const
This is a helper function that returns true if signal in the lanelet should be changed according to t...

References current_map_, shouldChangeControlLine(), shouldChangeTrafficSignal(), and sim_.

Referenced by addGeofenceHelper().

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

◆ addScheduleFromMsg()

void carma_wm_ctrl::WMBroadcaster::addScheduleFromMsg ( std::shared_ptr< Geofence gf_ptr,
const carma_v2x_msgs::msg::TrafficControlMessageV01 &  msg_v01 
)
private

Populates the schedules member of the geofence object from given TrafficControlMessageV01 message.

Parameters
gf_ptrgeofence pointer
msg_v01TrafficControlMessageV01 (geofence msg)

Definition at line 117 of file WMBroadcaster.cpp.

118{
119 // Handle schedule processing
120 carma_v2x_msgs::msg::TrafficControlSchedule msg_schedule = msg_v01.params.schedule;
121 double ns_to_sec = 1.0e9;
122 auto clock_type = scheduler_.getClockType();
123
124 rclcpp::Time end_time = {msg_schedule.end, clock_type};
125 if (!msg_schedule.end_exists) {
126 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "No end time for geofence, using rclcpp::Time::max()");
127 end_time = {rclcpp::Time::max(), clock_type}; // If there is no end time use the max time
128 }
129
130 // If the days of the week are specified then convert them to the boost format.
131 GeofenceSchedule::DayOfTheWeekSet week_day_set = { 0, 1, 2, 3, 4, 5, 6 }; // Default to all days 0==Sun to 6==Sat
132 if (msg_schedule.dow_exists) {
133 // sun (6), mon (5), tue (4), wed (3), thu (2), fri (1), sat (0)
134 week_day_set.clear();
135 for (size_t i = 0; i < msg_schedule.dow.dow.size(); i++) {
136 if (msg_schedule.dow.dow[i] == 1) {
137 switch(i) {
138 case 6: // sun
139 week_day_set.emplace(boost::gregorian::greg_weekday::weekday_enum::Sunday);
140 break;
141 case 5: // mon
142 week_day_set.emplace(boost::gregorian::greg_weekday::weekday_enum::Monday);
143 break;
144 case 4: // tue
145 week_day_set.emplace(boost::gregorian::greg_weekday::weekday_enum::Tuesday);
146 break;
147 case 3: // wed
148 week_day_set.emplace(boost::gregorian::greg_weekday::weekday_enum::Wednesday);
149 break;
150 case 2: // thur
151 week_day_set.emplace(boost::gregorian::greg_weekday::weekday_enum::Thursday);
152 break;
153 case 1: // fri
154 week_day_set.emplace(boost::gregorian::greg_weekday::weekday_enum::Friday);
155 break;
156 case 0: // sat
157 week_day_set.emplace(boost::gregorian::greg_weekday::weekday_enum::Saturday);
158 break;
159 default:
160 throw std::invalid_argument("Unrecognized weekday value: " + std::to_string(i));
161 }
162 }
163 }
164 }
165
166 if (msg_schedule.between_exists) {
167
168 for (auto daily_schedule : msg_schedule.between)
169 {
170 if (msg_schedule.repeat_exists) {
171 gf_ptr->schedules.push_back(GeofenceSchedule({msg_schedule.start, clock_type},
172 end_time,
173 rclcpp::Duration(daily_schedule.begin),
174 rclcpp::Duration(daily_schedule.duration),
175 rclcpp::Duration(msg_schedule.repeat.offset),
176 rclcpp::Duration(msg_schedule.repeat.span),
177 rclcpp::Duration(msg_schedule.repeat.period),
178 week_day_set));
179 } else {
180 gf_ptr->schedules.push_back(GeofenceSchedule({msg_schedule.start, clock_type},
181 end_time,
182 rclcpp::Duration(daily_schedule.begin),
183 rclcpp::Duration(daily_schedule.duration),
184 rclcpp::Duration(0.0), // No offset
185 rclcpp::Duration(daily_schedule.duration) - rclcpp::Duration(daily_schedule.begin), // Compute schedule portion end time
186 rclcpp::Duration(daily_schedule.duration) - rclcpp::Duration(daily_schedule.begin), // No repetition so same as portion end time
187 week_day_set));
188 }
189
190 }
191 }
192 else {
193 if (msg_schedule.repeat_exists) {
194 gf_ptr->schedules.push_back(GeofenceSchedule({msg_schedule.start, clock_type},
195 end_time,
196 rclcpp::Duration(0.0),
197 rclcpp::Duration(86400.0e9), // 24 hr daily application
198 rclcpp::Duration(msg_schedule.repeat.offset),
199 rclcpp::Duration(msg_schedule.repeat.span),
200 rclcpp::Duration(msg_schedule.repeat.period),
201 week_day_set));
202 } else {
203 gf_ptr->schedules.push_back(GeofenceSchedule({msg_schedule.start, clock_type},
204 end_time,
205 rclcpp::Duration(0.0),
206 rclcpp::Duration(86400.0e9), // 24 hr daily application
207 rclcpp::Duration(0.0), // No offset
208 rclcpp::Duration(86400.0e9), // Applied for full lenth of 24 hrs
209 rclcpp::Duration(86400.0e9), // No repetition
210 week_day_set));
211 }
212
213 }
214}
std::unordered_set< boost::gregorian::greg_weekday, std::hash< int > > DayOfTheWeekSet
rcl_clock_type_t getClockType()
Get the clock type of the clock being created by the timer factory.
auto to_string(const UtmZone &zone) -> std::string
Definition: utm_zone.cpp:21

References carma_wm_ctrl::GeofenceScheduler::getClockType(), process_bag::i, scheduler_, and carma_cooperative_perception::to_string().

Referenced by externalMapMsgCallback(), and geofenceCallback().

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

◆ baseMapCallback()

void carma_wm_ctrl::WMBroadcaster::baseMapCallback ( autoware_lanelet2_msgs::msg::MapBin::UniquePtr  map_msg)

Callback to set the base map when it has been loaded.

Parameters
map_msgThe map message to use as the base map

Definition at line 56 of file WMBroadcaster.cpp.

57{
58 std::lock_guard<std::mutex> guard(map_mutex_);
59
60 static bool firstCall = true;
61 // This function should generally only ever be called one time so log a warning if it occurs multiple times
62 if (firstCall)
63 {
64 firstCall = false;
65 RCLCPP_INFO_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "WMBroadcaster::baseMapCallback called for first time with new map message");
66 }
67 else
68 {
69 RCLCPP_WARN_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "WMBroadcaster::baseMapCallback called multiple times in the same node");
70 }
71
72 lanelet::LaneletMapPtr new_map(new lanelet::LaneletMap);
73 lanelet::LaneletMapPtr new_map_to_change(new lanelet::LaneletMap);
74
76 lanelet::utils::conversion::fromBinMsg(*map_msg, new_map_to_change);
77
78 base_map_ = new_map; // Store map
79 current_map_ = new_map_to_change; // broadcaster makes changes to this
80
81 lanelet::MapConformer::ensureCompliance(base_map_, config_limit); // Update map to ensure it complies with expectations
83
84 RCLCPP_INFO_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Building routing graph for base map");
85
86 lanelet::traffic_rules::TrafficRulesUPtr traffic_rules_car = lanelet::traffic_rules::TrafficRulesFactory::create(
87 lanelet::traffic_rules::CarmaUSTrafficRules::Location, participant_);
88 current_routing_graph_ = lanelet::routing::RoutingGraph::build(*current_map_, *traffic_rules_car);
89
90 RCLCPP_INFO_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Done building routing graph for base map");
91
92 // Publish map
93 current_map_version_ += 1; // Increment the map version. It should always start from 1 for the first map
94
95 autoware_lanelet2_msgs::msg::MapBin compliant_map_msg;
96
97 // Populate the routing graph message
98 RCLCPP_INFO_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Creating routing graph message.");
99
100 auto readable_graph = std::static_pointer_cast<RoutingGraphAccessor>(current_routing_graph_);
101
102 compliant_map_msg.routing_graph = readable_graph->routingGraphToMsg(participant_);
103 compliant_map_msg.has_routing_graph = true;
104
105 RCLCPP_INFO_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Done creating routing graph message.");
106
108 compliant_map_msg.map_version = current_map_version_;
109 map_pub_(compliant_map_msg);
110};
lanelet::LaneletMapPtr base_map_
lanelet::Velocity config_limit
void fromBinMsg(const autoware_lanelet2_msgs::msg::MapBin &msg, std::shared_ptr< carma_wm::TrafficControl > gf_ptr, lanelet::LaneletMapPtr lanelet_map=nullptr)
void ensureCompliance(lanelet::LaneletMapPtr map, lanelet::Velocity config_limit=80_mph)
Function modifies an existing map to make a best effort attempt at ensuring the map confroms to the e...

References base_map_, config_limit, current_map_, current_map_version_, current_routing_graph_, lanelet::MapConformer::ensureCompliance(), carma_wm::fromBinMsg(), map_mutex_, map_pub_, participant_, and carma_wm::toBinMsg().

Referenced by carma_wm_ctrl::WMBroadcasterNode::handle_on_activate().

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

◆ checkActiveGeofenceLogic()

carma_perception_msgs::msg::CheckActiveGeofence carma_wm_ctrl::WMBroadcaster::checkActiveGeofenceLogic ( const geometry_msgs::msg::PoseStamped &  current_pos)

Returns a message indicating whether or not the vehicle is inside of an active geofence lanelet.

Parameters
current_posCurrent position of the vehicle
Returns
0 if vehicle is not on an active geofence

Definition at line 2028 of file WMBroadcaster.cpp.

2029{
2030
2031 if (!current_map_ || current_map_->laneletLayer.size() == 0)
2032 {
2033 throw lanelet::InvalidObjectStateError(std::string("Lanelet map 'current_map_' is not loaded to the WMBroadcaster"));
2034 }
2035
2036 // Store current position values to be compared to geofence boundary values
2037 double current_pos_x = current_pos.pose.position.x;
2038 double current_pos_y = current_pos.pose.position.y;
2039
2040
2041
2042
2043 lanelet::BasicPoint2d curr_pos;
2044 curr_pos.x() = current_pos_x;
2045 curr_pos.y() = current_pos_y;
2046
2047 carma_perception_msgs::msg::CheckActiveGeofence outgoing_geof; //message to publish
2048 double next_distance = 0 ; //Distance to next geofence
2049
2050 if (active_geofence_llt_ids_.size() <= 0 )
2051 {
2052 return outgoing_geof;
2053 }
2054
2055 RCLCPP_INFO_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Active geofence llt ids are loaded to the WMBroadcaster");
2056
2057 // Obtain the closest lanelet to the vehicle's current position
2058 auto current_llt = lanelet::geometry::findNearest(current_map_->laneletLayer, curr_pos, 1)[0].second;
2059
2060
2061 /* determine whether or not the vehicle's current position is within an active geofence */
2062 if (boost::geometry::within(curr_pos, current_llt.polygon2d().basicPolygon()))
2063 {
2064 next_distance = distToNearestActiveGeofence(curr_pos);
2065 outgoing_geof.distance_to_next_geofence = next_distance;
2066
2067 for(auto id : active_geofence_llt_ids_)
2068 {
2069 if (id == current_llt.id())
2070 {
2071 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Vehicle is on Lanelet " << current_llt.id() << ", which has an active geofence");
2072 outgoing_geof.is_on_active_geofence = true;
2073 for (auto regem: current_llt.regulatoryElements())
2074 {
2075 // Assign active geofence fields based on the speed limit associated with this lanelet
2076 if (regem->attribute(lanelet::AttributeName::Subtype).value().compare(lanelet::DigitalSpeedLimit::RuleName) == 0)
2077 {
2078 lanelet::DigitalSpeedLimitPtr speed = std::dynamic_pointer_cast<lanelet::DigitalSpeedLimit>
2079 (current_map_->regulatoryElementLayer.get(regem->id()));
2080 outgoing_geof.value = speed->speed_limit_.value();
2081 outgoing_geof.advisory_speed = speed->speed_limit_.value();
2082 outgoing_geof.reason = speed->getReason();
2083
2084 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Active geofence has a speed limit of " << speed->speed_limit_.value());
2085
2086 // Cannot overrule outgoing_geof.type if it is already set to LANE_CLOSED
2087 if(outgoing_geof.type != carma_perception_msgs::msg::CheckActiveGeofence::LANE_CLOSED)
2088 {
2089 outgoing_geof.type = carma_perception_msgs::msg::CheckActiveGeofence::SPEED_LIMIT;
2090 }
2091 }
2092
2093 // Assign active geofence fields based on the minimum gap associated with this lanelet (if it exists)
2094 if(regem->attribute(lanelet::AttributeName::Subtype).value().compare(lanelet::DigitalMinimumGap::RuleName) == 0)
2095 {
2096 lanelet::DigitalMinimumGapPtr min_gap = std::dynamic_pointer_cast<lanelet::DigitalMinimumGap>
2097 (current_map_->regulatoryElementLayer.get(regem->id()));
2098 outgoing_geof.minimum_gap = min_gap->getMinimumGap();
2099 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Active geofence has a minimum gap of " << min_gap->getMinimumGap());
2100 }
2101
2102 // Assign active geofence fields based on whether the current lane is closed or is immediately adjacent to a closed lane
2103 if(regem->attribute(lanelet::AttributeName::Subtype).value().compare(lanelet::RegionAccessRule::RuleName) == 0)
2104 {
2105 lanelet::RegionAccessRulePtr accessRuleReg = std::dynamic_pointer_cast<lanelet::RegionAccessRule>
2106 (current_map_->regulatoryElementLayer.get(regem->id()));
2107
2108 // Update the 'type' and 'reason' for this active geofence if the vehicle is in a closed lane
2109 if(!accessRuleReg->accessable(lanelet::Participants::VehicleCar) || !accessRuleReg->accessable(lanelet::Participants::VehicleTruck))
2110 {
2111 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Active geofence is a closed lane.");
2112 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Closed lane reason: " << accessRuleReg->getReason());
2113 outgoing_geof.reason = accessRuleReg->getReason();
2114 outgoing_geof.type = carma_perception_msgs::msg::CheckActiveGeofence::LANE_CLOSED;
2115 }
2116 // Otherwise, update the 'type' and 'reason' for this active geofence if the vehicle is in a lane immediately adjacent to a closed lane with the same travel direction
2117 else
2118 {
2119 // Obtain all same-direction lanes sharing the right lane boundary (will include the current lanelet)
2120 auto right_boundary_lanelets = current_map_->laneletLayer.findUsages(current_llt.rightBound());
2121
2122 // Check if the adjacent right lane is closed
2123 if(right_boundary_lanelets.size() > 1)
2124 {
2125 for(auto lanelet : right_boundary_lanelets)
2126 {
2127 // Only check the adjacent right lanelet; ignore the current lanelet
2128 if(lanelet.id() != current_llt.id())
2129 {
2130 for (auto rightRegem: lanelet.regulatoryElements())
2131 {
2132 if(rightRegem->attribute(lanelet::AttributeName::Subtype).value().compare(lanelet::RegionAccessRule::RuleName) == 0)
2133 {
2134 lanelet::RegionAccessRulePtr rightAccessRuleReg = std::dynamic_pointer_cast<lanelet::RegionAccessRule>
2135 (current_map_->regulatoryElementLayer.get(rightRegem->id()));
2136 if(!rightAccessRuleReg->accessable(lanelet::Participants::VehicleCar) || !rightAccessRuleReg->accessable(lanelet::Participants::VehicleTruck))
2137 {
2138 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Right adjacent Lanelet " << lanelet.id() << " is CLOSED");
2139 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Assigning LANE_CLOSED type to active geofence");
2140 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Assigning reason " << rightAccessRuleReg->getReason());
2141 outgoing_geof.reason = rightAccessRuleReg->getReason();
2142 outgoing_geof.type = carma_perception_msgs::msg::CheckActiveGeofence::LANE_CLOSED;
2143 }
2144 }
2145 }
2146 }
2147 }
2148 }
2149
2150 // Check if the adjacent left lane is closed
2151 auto left_boundary_lanelets = current_map_->laneletLayer.findUsages(current_llt.leftBound());
2152 if(left_boundary_lanelets.size() > 1)
2153 {
2154 for(auto lanelet : left_boundary_lanelets)
2155 {
2156 // Only check the adjacent left lanelet; ignore the current lanelet
2157 if(lanelet.id() != current_llt.id())
2158 {
2159 for (auto leftRegem: lanelet.regulatoryElements())
2160 {
2161 if(leftRegem->attribute(lanelet::AttributeName::Subtype).value().compare(lanelet::RegionAccessRule::RuleName) == 0)
2162 {
2163 lanelet::RegionAccessRulePtr leftAccessRuleReg = std::dynamic_pointer_cast<lanelet::RegionAccessRule>
2164 (current_map_->regulatoryElementLayer.get(leftRegem->id()));
2165 if(!leftAccessRuleReg->accessable(lanelet::Participants::VehicleCar) || !leftAccessRuleReg->accessable(lanelet::Participants::VehicleTruck))
2166 {
2167 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Left adjacent Lanelet " << lanelet.id() << " is CLOSED");
2168 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Assigning LANE_CLOSED type to active geofence");
2169 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Assigning reason " << leftAccessRuleReg->getReason());
2170 outgoing_geof.reason = leftAccessRuleReg->getReason();
2171 outgoing_geof.type = carma_perception_msgs::msg::CheckActiveGeofence::LANE_CLOSED;
2172 }
2173 }
2174 }
2175 }
2176 }
2177 }
2178 }
2179 }
2180 }
2181 }
2182 }
2183 }
2184 return outgoing_geof;
2185}
double distToNearestActiveGeofence(const lanelet::BasicPoint2d &curr_pos)
Returns the route distance (downtrack or crosstrack in meters) to the nearest active geofence lanelet...

References active_geofence_llt_ids_, current_map_, and distToNearestActiveGeofence().

Referenced by currentLocationCallback().

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

◆ combineParticipantsToVehicle()

std::vector< std::string > carma_wm_ctrl::WMBroadcaster::combineParticipantsToVehicle ( const std::vector< std::string > &  input_participants) const

Combines a list of the given participants into a single "vehicle" type if participants cover all possible vehicle types. Returns the input with no change if it doesn't cover all.

Parameters
std::vector<std::string>participants vector of strings

Definition at line 1016 of file WMBroadcaster.cpp.

1017{
1018 std::vector<std::string> participants;
1019
1020 if(std::find(input_participants.begin(),input_participants.end(),lanelet::Participants::VehicleMotorcycle)!= input_participants.end() &&
1021 std::find(input_participants.begin(),input_participants.end(),lanelet::Participants::VehicleBus) != input_participants.end() &&
1022 std::find(input_participants.begin(),input_participants.end(),lanelet::Participants::VehicleCar) != input_participants.end() &&
1023 std::find(input_participants.begin(),input_participants.end(),lanelet::Participants::VehicleTruck) != input_participants.end())
1024 {
1025 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Detected participants to cover all possible vehicle types");
1026 participants.emplace_back(lanelet::Participants::Vehicle);
1027 }
1028 else
1029 {
1030 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Not making any changes to the participants list");
1031 participants = input_participants;
1032 }
1033
1034 return participants;
1035}

Referenced by participantsChecker().

Here is the caller graph for this function:

◆ composeTCRStatus()

carma_v2x_msgs::msg::TrafficControlRequestPolygon carma_wm_ctrl::WMBroadcaster::composeTCRStatus ( const lanelet::BasicPoint3d &  localPoint,
const carma_v2x_msgs::msg::TrafficControlBounds &  cB,
const lanelet::projection::LocalFrameProjector &  local_projector 
)

composeTCRStatus() compose TCM Request visualization on UI

Parameters
inputThe message containing tcr information

Definition at line 1809 of file WMBroadcaster.cpp.

1810{
1811 carma_v2x_msgs::msg::TrafficControlRequestPolygon output;
1812 lanelet::BasicPoint3d local_point_tmp;
1813
1814 int i = -1;
1815 while (i < 3) // three offsets; offsets.size() doesn't return accurate size
1816 {
1817 if (i == -1)
1818 {
1819 local_point_tmp.x() = localPoint.x();
1820 local_point_tmp.y() = localPoint.y();
1821 }
1822 else
1823 {
1824 local_point_tmp.x() = localPoint.x() + cB.offsets[i].deltax;;
1825 local_point_tmp.y() = localPoint.y() + cB.offsets[i].deltay;;
1826 }
1827 lanelet::GPSPoint gps_vertex = local_projector.reverse(local_point_tmp);
1828
1829 carma_v2x_msgs::msg::Position3D gps_msg;
1830 gps_msg.elevation = gps_vertex.ele;
1831 gps_msg.latitude = gps_vertex.lat;
1832 gps_msg.longitude = gps_vertex.lon;
1833 output.polygon_list.push_back(gps_msg);
1834
1835 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "TCR Vertex Lat: "<< std::to_string(gps_vertex.lat));
1836 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "TCR Vertex Lon: "<<std::to_string(gps_vertex.lon));
1837
1838 i++;
1839 }
1840 return output;
1841}

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

Referenced by controlRequestFromRoute().

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

◆ composeVisualizerMarkerFromPts()

visualization_msgs::msg::Marker carma_wm_ctrl::WMBroadcaster::composeVisualizerMarkerFromPts ( const visualization_msgs::msg::MarkerArray &  marker_array,
const std::vector< lanelet::Point3d > &  input 
)

Visualizes in Rviz geometry points related to MAP msg or TrafficControlMessage.

Parameters
marker_arrayPreviously visualized markers if available to continue marker id
inputgeometry points to visualize
Returns
MarkerArray containing only the new visualization from the gf pts intended to be appended to the previous markers

Definition at line 1843 of file WMBroadcaster.cpp.

1844{
1845 // create the marker msgs
1846 visualization_msgs::msg::Marker marker;
1847 marker.header.frame_id = "map";
1848 marker.header.stamp = rclcpp::Time();
1849 marker.type = visualization_msgs::msg::Marker::SPHERE_LIST;
1850 marker.action = visualization_msgs::msg::Marker::ADD;
1851 marker.ns = "map_update_visualizer";
1852
1853 marker.scale.x = 0.65;
1854 marker.scale.y = 0.65;
1855 marker.scale.z = 0.65;
1856 marker.frame_locked = true;
1857
1858 if (!marker_array.markers.empty())
1859 {
1860 marker.id = marker_array.markers.back().id + 1;
1861 }
1862 else
1863 {
1864 marker.id = 0;
1865 }
1866 marker.color.r = 0.0F;
1867 marker.color.g = 1.0F;
1868 marker.color.b = 0.0F;
1869 marker.color.a = 1.0F;
1870
1871 for (int i = 0; i < input.size(); i++)
1872 {
1873 geometry_msgs::msg::Point temp_point;
1874 temp_point.x = input[i].x();
1875 temp_point.y = input[i].y();
1876 temp_point.z = 2; //to show up on top of the lanelet lines
1877
1878 marker.points.push_back(temp_point);
1879 }
1880
1881 return marker;
1882 }

References process_bag::i.

Referenced by addGeofence().

Here is the caller graph for this function:

◆ controlRequestFromRoute()

carma_v2x_msgs::msg::TrafficControlRequest carma_wm_ctrl::WMBroadcaster::controlRequestFromRoute ( const carma_planning_msgs::msg::Route &  route_msg,
std::shared_ptr< j2735_v2x_msgs::msg::Id64b >  req_id_for_testing = NULL 
)

Pulls vehicle information from CARMA Cloud at startup by providing its selected route in a TrafficControlRequest message that is published after a route is selected. During operation at ~10s intervals the vehicle will make another control request for the remainder of its route.

Parameters
route_msgThe message containing route information pulled from routeCallbackMessage()
req_id_for_testingthis ptr is optional. it gives req_id for developer to test TrafficControlMessage as it needs it

Definition at line 1658 of file WMBroadcaster.cpp.

1659{
1660 lanelet::ConstLanelets path;
1661
1662 if (!current_map_ || current_map_->laneletLayer.size() == 0)
1663 {
1664 // Return / log warning etc.
1665 RCLCPP_INFO_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Value 'current_map_' does not exist.");
1666 throw lanelet::InvalidObjectStateError(std::string("Base lanelet map is not loaded to the WMBroadcaster"));
1667
1668 }
1669
1670 for(auto id : route_msg.route_path_lanelet_ids)
1671 {
1672 auto laneLayer = current_map_->laneletLayer.get(id);
1673 path.push_back(laneLayer);
1674 }
1675
1676 // update local copy
1677 route_path_ = path;
1678
1679 if(path.size() == 0) throw lanelet::InvalidObjectStateError(std::string("No lanelets available in path."));
1680
1681 /*logic to determine route bounds*/
1682 std::vector<lanelet::ConstLanelet> llt;
1683 std::vector<lanelet::BoundingBox2d> pathBox;
1684 double minX = std::numeric_limits<double>::max();
1685 double minY = std::numeric_limits<double>::max();
1686 double maxX = std::numeric_limits<double>::lowest();
1687 double maxY = std::numeric_limits<double>::lowest();
1688
1689 while (path.size() != 0) //Continue until there are no more lanelet elements in path
1690 {
1691 llt.push_back(path.back()); //Add a lanelet to the vector
1692
1693
1694 pathBox.push_back(lanelet::geometry::boundingBox2d(llt.back())); //Create a bounding box of the added lanelet and add it to the vector
1695
1696
1697 if (pathBox.back().corner(lanelet::BoundingBox2d::BottomLeft).x() < minX)
1698 minX = pathBox.back().corner(lanelet::BoundingBox2d::BottomLeft).x(); //minimum x-value
1699
1700
1701 if (pathBox.back().corner(lanelet::BoundingBox2d::BottomLeft).y() < minY)
1702 minY = pathBox.back().corner(lanelet::BoundingBox2d::BottomLeft).y(); //minimum y-value
1703
1704
1705
1706 if (pathBox.back().corner(lanelet::BoundingBox2d::TopRight).x() > maxX)
1707 maxX = pathBox.back().corner(lanelet::BoundingBox2d::TopRight).x(); //maximum x-value
1708
1709
1710 if (pathBox.back().corner(lanelet::BoundingBox2d::TopRight).y() > maxY)
1711 maxY = pathBox.back().corner(lanelet::BoundingBox2d::TopRight).y(); //maximum y-value
1712
1713
1714 path.pop_back(); //remove the added lanelet from path an reduce pack.size() by 1
1715 } //end of while loop
1716
1717
1718 std::string target_frame = base_map_georef_;
1719 if (target_frame.empty())
1720 {
1721 // Return / log warning etc.
1722 RCLCPP_INFO_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Value 'target_frame' is empty.");
1723 throw lanelet::InvalidObjectStateError(std::string("Base georeference map may not be loaded to the WMBroadcaster"));
1724
1725 }
1726
1727 // Convert the minimum point to latlon
1728 lanelet::projection::LocalFrameProjector local_projector(target_frame.c_str());
1729 lanelet::BasicPoint3d localPoint;
1730
1731 localPoint.x()= minX;
1732 localPoint.y()= minY;
1733
1734 lanelet::GPSPoint gpsRoute = local_projector.reverse(localPoint); //If the appropriate library is included, the reverse() function can be used to convert from local xyz to lat/lon
1735
1736 // Create a local transverse mercator frame at the minimum point to allow us to get east,north oriented bounds
1737 std::string local_tmerc_enu_proj = "+proj=tmerc +datum=WGS84 +h_0=0 +lat_0=" + std::to_string(gpsRoute.lat) + " +lon_0=" + std::to_string(gpsRoute.lon);
1738
1739 // Create transform from map frame to local transform mercator frame at bounds min point
1740 PJ* tmerc_proj = proj_create_crs_to_crs(PJ_DEFAULT_CTX, target_frame.c_str(), local_tmerc_enu_proj.c_str(), nullptr);
1741
1742 if (tmerc_proj == nullptr) { // proj_create_crs_to_crs returns 0 when there is an error in the projection
1743
1744 RCLCPP_ERROR_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Failed to generate projection between request bounds frame and map with error number: " << proj_context_errno(PJ_DEFAULT_CTX)
1745 << " MapProjection: " << target_frame << " Message Projection: " << local_tmerc_enu_proj);
1746
1747 return {}; // Ignore geofence if it could not be projected into the map frame
1748
1749 }
1750
1751 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Before conversion: Top Left: ("<< minX <<", "<<maxY<<")");
1752 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Before conversion: Top Right: ("<< maxX <<", "<<maxY<<")");
1753 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Before conversion: Bottom Left: ("<< minX <<", "<<minY<<")");
1754 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Before conversion: Bottom Right: ("<< maxX <<", "<<minY<<")");
1755
1756 PJ_COORD pj_min {{minX, minY, 0, 0}}; // z is not currently used
1757 PJ_COORD pj_min_tmerc;
1758 PJ_COORD pj_max {{maxX, maxY, 0, 0}}; // z is not currently used
1759 PJ_COORD pj_max_tmerc;
1760 pj_min_tmerc = proj_trans(tmerc_proj, PJ_FWD, pj_min);
1761 pj_max_tmerc = proj_trans(tmerc_proj, PJ_FWD, pj_max);
1762
1763 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "After conversion: MinPoint ( "<< pj_min_tmerc.xyz.x <<", " << pj_min_tmerc.xyz.y <<" )");
1764 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "After conversion: MaxPoint ( "<< pj_max_tmerc.xyz.x <<", " << pj_max_tmerc.xyz.y <<" )");
1765
1766 carma_v2x_msgs::msg::TrafficControlRequest cR; /*Fill the latitude value in message cB with the value of lat */
1767 carma_v2x_msgs::msg::TrafficControlBounds cB; /*Fill the longitude value in message cB with the value of lon*/
1768
1769 cB.reflat = gpsRoute.lat;
1770 cB.reflon = gpsRoute.lon;
1771
1772 cB.offsets[0].deltax = pj_max_tmerc.xyz.x - pj_min_tmerc.xyz.x; // Points in clockwise order min,min (lat,lon point) -> max,min -> max,max -> min,max
1773 cB.offsets[0].deltay = 0.0; // calculating the offsets
1774 cB.offsets[1].deltax = pj_max_tmerc.xyz.x - pj_min_tmerc.xyz.x;
1775 cB.offsets[1].deltay = pj_max_tmerc.xyz.y - pj_min_tmerc.xyz.y;
1776 cB.offsets[2].deltax = 0.0;
1777 cB.offsets[2].deltay = pj_max_tmerc.xyz.y - pj_min_tmerc.xyz.y;
1778
1779 tcr_polygon_ = composeTCRStatus(localPoint, cB, local_projector); // TCR polygon can be visualized in UI
1780
1781 cB.oldest =rclcpp::Time(0.0,0.0, scheduler_.getClockType()); // TODO this needs to be set to 0 or an older value as otherwise this will filter out all controls
1782
1783 cR.choice = carma_v2x_msgs::msg::TrafficControlRequest::TCRV01;
1784
1785 // create 16 byte uuid
1786 boost::uuids::uuid uuid_id = boost::uuids::random_generator()();
1787 // take half as string
1788 std::string reqid = boost::uuids::to_string(uuid_id).substr(0, 8);
1789 std::string req_id_test = "12345678"; // TODO this is an extremely risky way of performing a unit test. This method needs to be refactored so that unit tests cannot side affect actual implementations
1790 generated_geofence_reqids_.insert(req_id_test);
1791 generated_geofence_reqids_.insert(reqid);
1792
1793
1794 // copy to reqid array
1795 boost::array<uint8_t, 16UL> req_id;
1796 std::copy(uuid_id.begin(),uuid_id.end(), req_id.begin());
1797 for (auto i = 0; i < 8; i ++)
1798 {
1799 cR.tcr_v01.reqid.id[i] = req_id[i];
1800 if (req_id_for_testing) req_id_for_testing->id[i] = req_id[i];
1801 }
1802
1803 cR.tcr_v01.bounds.push_back(cB);
1804
1805 return cR;
1806
1807}
lanelet::ConstLanelets route_path_
carma_v2x_msgs::msg::TrafficControlRequestPolygon composeTCRStatus(const lanelet::BasicPoint3d &localPoint, const carma_v2x_msgs::msg::TrafficControlBounds &cB, const lanelet::projection::LocalFrameProjector &local_projector)
composeTCRStatus() compose TCM Request visualization on UI
carma_v2x_msgs::msg::TrafficControlRequestPolygon tcr_polygon_
std::unordered_set< std::string > generated_geofence_reqids_

References base_map_georef_, composeTCRStatus(), current_map_, generated_geofence_reqids_, carma_wm_ctrl::GeofenceScheduler::getClockType(), process_bag::i, route_path_, scheduler_, tcr_polygon_, and carma_cooperative_perception::to_string().

Referenced by routeCallbackMessage().

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

◆ convertLightIdToInterGroupId()

bool carma_wm_ctrl::WMBroadcaster::convertLightIdToInterGroupId ( unsigned &  intersection_id,
unsigned &  group_id,
const lanelet::Id &  lanelet_id 
)

helper for generating intersection and group Id of a traffic light from lanelet id

Parameters
[in]trafficlanelet_id
[out]intersection_idand group_id
Returns
return true if conversion was successful

Definition at line 1972 of file WMBroadcaster.cpp.

1973{
1974 for (auto it = traffic_light_id_lookup_.begin(); it != traffic_light_id_lookup_.end(); ++it)
1975 {
1976 // Reverse of the logic for generating the lanelet_id. Reference function generate32BitId(const std::string& label)
1977 if (it -> second == lanelet_id)
1978 {
1979 group_id = (it -> first & 0xFF);
1980 intersection_id = (it -> first >> 8);
1981 return true;
1982 }
1983 }
1984 return false;
1985}

References traffic_light_id_lookup_.

Referenced by publishLightId().

Here is the caller graph for this function:

◆ createLinearInterpolatingLanelet()

lanelet::Lanelet carma_wm_ctrl::WMBroadcaster::createLinearInterpolatingLanelet ( const lanelet::Point3d &  left_front_pt,
const lanelet::Point3d &  right_front_pt,
const lanelet::Point3d &  left_back_pt,
const lanelet::Point3d &  right_back_pt,
double  increment_distance = 0.25 
)
private

Definition at line 2208 of file WMBroadcaster.cpp.

2209{
2210 return lanelet::Lanelet(lanelet::utils::getId(), createLinearInterpolatingLinestring(left_front_pt, left_back_pt, increment_distance), createLinearInterpolatingLinestring(right_front_pt, right_back_pt, increment_distance));
2211}
lanelet::LineString3d createLinearInterpolatingLinestring(const lanelet::Point3d &front_pt, const lanelet::Point3d &back_pt, double increment_distance=0.25)

References createLinearInterpolatingLinestring().

Referenced by createWorkzoneGeometry().

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

◆ createLinearInterpolatingLinestring()

lanelet::LineString3d carma_wm_ctrl::WMBroadcaster::createLinearInterpolatingLinestring ( const lanelet::Point3d &  front_pt,
const lanelet::Point3d &  back_pt,
double  increment_distance = 0.25 
)
private

Definition at line 2187 of file WMBroadcaster.cpp.

2188{
2189 double dx = back_pt.x() - front_pt.x();
2190 double dy = back_pt.y() - front_pt.y();
2191
2192 std::vector<lanelet::Point3d> points;
2193 double distance = std::sqrt(pow(dx, 2) + pow(dy,2));
2194 double cos = dx / distance;
2195 double sin = dy / distance;
2196 points.push_back(front_pt);
2197 double sum = increment_distance;
2198 while ( sum < distance)
2199 {
2200 points.push_back(lanelet::Point3d(lanelet::utils::getId(),front_pt.x() + sum * cos, front_pt.y() + sum * sin, 0.0));
2201 sum += increment_distance;
2202 }
2203 points.push_back(back_pt);
2204
2205 return lanelet::LineString3d(lanelet::utils::getId(), points);
2206}

Referenced by createLinearInterpolatingLanelet().

Here is the caller graph for this function:

◆ createWorkzoneGeofence()

std::shared_ptr< Geofence > carma_wm_ctrl::WMBroadcaster::createWorkzoneGeofence ( std::unordered_map< uint8_t, std::shared_ptr< Geofence > >  work_zone_geofence_cache)

Creates a single workzone geofence (in the vector) that includes all additional lanelets (housing traffic lights) and update_list that blocks old lanelets. Geofence will have minimum of 6 lanelet_additions_ (front parallel, front diagonal, middle lanelet(s), back diagonal, back parallel, 1 opposing lanelet with trafficlight). And regulatory_element_ of this geofence will be region_access_rule, where it blocks entire affected_parts of CLOSED, TAPERRIGHT, OPENRIGHT. It also blocks the opposing lane's lanelet that would have had the trafficlight, and new lanelet(s) would be added to replace it.

Parameters
work_zone_geofence_cacheGeofence map with size of 4 corresponding to CLOSED, TAPERRIGHT, OPENRIGHT, REVERSE TrafficControlMessages. Each should have gf_pts, affected_parts, schedule, and id filled. TAPERRIGHT's id and schedule is used as all should have same schedule.
Exceptions
InvalidObjectStateErrorif no map is available
Returns
geofence housing all necessary workzone elements

Definition at line 400 of file WMBroadcaster.cpp.

401{
402 std::shared_ptr<std::vector<lanelet::Lanelet>> parallel_llts = std::make_shared<std::vector<lanelet::Lanelet>>(std::vector<lanelet::Lanelet>());
403 std::shared_ptr<std::vector<lanelet::Lanelet>> middle_opposite_lanelets = std::make_shared<std::vector<lanelet::Lanelet>>(std::vector<lanelet::Lanelet>());
404
405 // Split existing lanelets and filter into parallel_llts and middle_opposite_lanelets
406 preprocessWorkzoneGeometry(work_zone_geofence_cache, parallel_llts, middle_opposite_lanelets);
407
408 // Create geofence and rest of the required lanelets along with traffic light for completing workzone area
409 auto gf_ptr = createWorkzoneGeometry(work_zone_geofence_cache, parallel_llts->front(), parallel_llts->back(), middle_opposite_lanelets );
410
411 // copy static info from the existing workzone
412 gf_ptr->id_ = work_zone_geofence_cache[WorkZoneSection::TAPERRIGHT]->id_; //using taperright's id as the whole geofence's id
413
414 // schedule
415 gf_ptr->schedules = work_zone_geofence_cache[WorkZoneSection::TAPERRIGHT]->schedules; //using taperright's schedule as the whole geofence's schedule
416
417 // erase cache now that it is processed
418 for (auto pair : work_zone_geofence_cache)
419 {
420 RCLCPP_INFO_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Workzone geofence finished processing. Therefore following geofence id is being dropped from cache as it is processed as part of it: " << pair.second->id_);
421 }
422 work_zone_geofence_cache.clear();
423
424 return gf_ptr;
425}
std::shared_ptr< Geofence > createWorkzoneGeometry(std::unordered_map< uint8_t, std::shared_ptr< Geofence > > work_zone_geofence_cache, lanelet::Lanelet parallel_llt_front, lanelet::Lanelet parallel_llt_back, std::shared_ptr< std::vector< lanelet::Lanelet > > middle_opposite_lanelets)
Create workzone geofence. Create diagonal lanelets and a lanelet that houses opposing lane's trafficl...
void preprocessWorkzoneGeometry(std::unordered_map< uint8_t, std::shared_ptr< Geofence > > work_zone_geofence_cache, std::shared_ptr< std::vector< lanelet::Lanelet > > parallel_llts, std::shared_ptr< std::vector< lanelet::Lanelet > > opposite_llts)
Preprocess for workzone area. Parallel_llts will have front_parallel and back_parallel lanelets that ...
static const uint8_t TAPERRIGHT

References createWorkzoneGeometry(), preprocessWorkzoneGeometry(), and carma_wm_ctrl::WorkZoneSection::TAPERRIGHT.

Referenced by addGeofence().

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

◆ createWorkzoneGeometry()

std::shared_ptr< Geofence > carma_wm_ctrl::WMBroadcaster::createWorkzoneGeometry ( std::unordered_map< uint8_t, std::shared_ptr< Geofence > >  work_zone_geofence_cache,
lanelet::Lanelet  parallel_llt_front,
lanelet::Lanelet  parallel_llt_back,
std::shared_ptr< std::vector< lanelet::Lanelet > >  middle_opposite_lanelets 
)

Create workzone geofence. Create diagonal lanelets and a lanelet that houses opposing lane's trafficlight. Fill lanelet_additions_ with every newly created lanelets New lanelets will have traffic light. Old lanelets (and those from CLOSED) will be blocked using update_list as region_access_rule.

Parameters
work_zone_geofence_cacheGeofence map with size of 4 corresponding to CLOSED, TAPERRIGHT, OPENRIGHT, REVERSE TrafficControlMessages. Each should have gf_pts, affected_parts.
parallel_llt_frontA lanelet whose end should connect to front diagonal lanelet
parallel_llt_backA lanelet whose start should connect to back diagonal lanelet
middle_opposite_laneletsA getInterGroupIdsByLightReg() connects to back diagonal (their directions are expected to be opposite of parallel ones)
Exceptions
InvalidObjectStateErrorif no map is available

Definition at line 427 of file WMBroadcaster.cpp.

429{
430 auto gf_ptr = std::make_shared<Geofence>();
431
433 //FRONT DIAGONAL LANELET
435
436 lanelet::Lanelet front_llt_diag = createLinearInterpolatingLanelet(parallel_llt_front.leftBound3d().back(), parallel_llt_front.rightBound3d().back(),
437 middle_opposite_lanelets->back().rightBound3d().back(), middle_opposite_lanelets->back().leftBound3d().back());
438 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Created diag front_llt_diag id:" << front_llt_diag.id());
439 for (auto regem : middle_opposite_lanelets->back().regulatoryElements()) //copy existing regem into the new llts
440 {
441 front_llt_diag.addRegulatoryElement(regem);
442 }
443
445 //BACK DIAGONAL LANELET
447
448 lanelet::Lanelet back_llt_diag = createLinearInterpolatingLanelet(middle_opposite_lanelets->front().rightBound3d().front(), middle_opposite_lanelets->front().leftBound3d().front(),
449 parallel_llt_back.leftBound3d().front(), parallel_llt_back.rightBound3d().front());
450 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Created back_llt_diag diag id:" << back_llt_diag.id());
451 for (auto regem : parallel_llt_back.regulatoryElements()) //copy existing regem into the new llts
452 {
453 back_llt_diag.addRegulatoryElement(regem);
454 }
455
457 //MIDDLE LANELETS TO MATCH PARALLEL DIRECTION
459
460 std::vector<lanelet::Lanelet> middle_llts;
461 for (int i = middle_opposite_lanelets->size() - 1; i >= 0; i--) //do no use size_t as it might overflow
462 {
463 lanelet::Lanelet middle_llt (lanelet::utils::getId(), (*(middle_opposite_lanelets.get()))[i].rightBound3d().invert(), (*(middle_opposite_lanelets.get()))[i].leftBound3d().invert());
464 for (auto regem : (*(middle_opposite_lanelets.get()))[i].regulatoryElements())
465 {
466 middle_llt.addRegulatoryElement(regem); //copy existing regem into the new llts
467 }
468 middle_llts.push_back(middle_llt);
469 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Created matching direction of middle_llt id:" << middle_llt.id());
470 }
471
473 //ADD TF_LIGHT TO PARALLEL LANELET
475 lanelet::LineString3d parallel_stop_ls(lanelet::utils::getId(), {parallel_llt_front.leftBound3d().back(), parallel_llt_front.rightBound3d().back()});
476 // controlled lanelet:
477 std::vector<lanelet::Lanelet> controlled_taper_right;
478
479 controlled_taper_right.push_back(parallel_llt_front); //which has the light
480
481 controlled_taper_right.push_back(front_llt_diag);
482
483 controlled_taper_right.insert(controlled_taper_right.end(), middle_llts.begin(), middle_llts.end());
484
485 controlled_taper_right.push_back(back_llt_diag);
486
487 lanelet::CarmaTrafficSignalPtr tfl_parallel = std::make_shared<lanelet::CarmaTrafficSignal>(lanelet::CarmaTrafficSignal::buildData(lanelet::utils::getId(), {parallel_stop_ls}, {controlled_taper_right.front()}, {controlled_taper_right.back()}));
488
489 gf_ptr->traffic_light_id_lookup_.push_back({generate32BitId(work_zone_geofence_cache[WorkZoneSection::TAPERRIGHT]->label_),tfl_parallel->id()});
490
491 parallel_llt_front.addRegulatoryElement(tfl_parallel);
492 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Created TF_LIGHT of Id: " << tfl_parallel->id() << ", to parallel_llt_front id:" << parallel_llt_front.id());
493
495 //ADD TF_LIGHT TO OPPOSITE LANELET
497 std::shared_ptr<std::vector<lanelet::Lanelet>> opposite_llts_with_stop_line = std::make_shared<std::vector<lanelet::Lanelet>>(std::vector<lanelet::Lanelet>());
498
499 auto old_opposite_llts = splitOppositeLaneletWithPoint(opposite_llts_with_stop_line, work_zone_geofence_cache[WorkZoneSection::OPENRIGHT]->gf_pts.back().basicPoint2d(),
500 current_map_->laneletLayer.get(work_zone_geofence_cache[WorkZoneSection::OPENRIGHT]->affected_parts_.back().lanelet().get().id()), error_distance_);
501
502 lanelet::LineString3d opposite_stop_ls(lanelet::utils::getId(), {opposite_llts_with_stop_line->front().leftBound3d().back(), opposite_llts_with_stop_line->front().rightBound3d().back()});
503
504 std::vector<lanelet::Lanelet> controlled_open_right;
505
506 controlled_open_right.insert(controlled_open_right.end(), opposite_llts_with_stop_line->begin(), opposite_llts_with_stop_line->end());; //split lanelet one of which has light
507
508 for (auto llt : work_zone_geofence_cache[WorkZoneSection::REVERSE]->affected_parts_)
509 {
510 controlled_open_right.push_back(current_map_->laneletLayer.get(llt.lanelet().get().id()));
511 }
512
513 lanelet::CarmaTrafficSignalPtr tfl_opposite = std::make_shared<lanelet::CarmaTrafficSignal>(lanelet::CarmaTrafficSignal::buildData(lanelet::utils::getId(), {opposite_stop_ls}, {controlled_open_right.front()}, {controlled_open_right.back()}));
514
515 gf_ptr->traffic_light_id_lookup_.push_back({generate32BitId(work_zone_geofence_cache[WorkZoneSection::OPENRIGHT]->label_), tfl_opposite->id()});
516
517 opposite_llts_with_stop_line->front().addRegulatoryElement(tfl_opposite);
518 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Created TF_LIGHT of Id: " << tfl_opposite->id() << ", to opposite_llts_with_stop_line->front() id:" << opposite_llts_with_stop_line->front().id());
519
521 //ADD ALL NEWLY CREATED LANELETS INTO GEOFENCE
522 //OBJECTS TO BE PROCESSED LATER BY SCHEDULER
524 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Added parallel_llt_front id:" << parallel_llt_front.id());
525 gf_ptr->lanelet_additions_.push_back(parallel_llt_front);
526
527 gf_ptr->lanelet_additions_.push_back(front_llt_diag);
528
529 gf_ptr->lanelet_additions_.insert(gf_ptr->lanelet_additions_.end(), middle_llts.begin(), middle_llts.end());
530
531 gf_ptr->lanelet_additions_.push_back(back_llt_diag);
532 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Added parallel_llt_back id:" << parallel_llt_back.id());
533 gf_ptr->lanelet_additions_.push_back(parallel_llt_back);
534
535 gf_ptr->lanelet_additions_.insert(gf_ptr->lanelet_additions_.end(), opposite_llts_with_stop_line->begin(), opposite_llts_with_stop_line->end());;
536
538 // ADD REGION_ACCESS_RULE REGEM TO THE OUTDATED LANELETS
539 // AS WELL AS LANELETS BEING BLOCKED BY WORKZONE GEOFENCE
541
542 // fill information for paricipants and reason for blocking
543 carma_v2x_msgs::msg::TrafficControlMessageV01 participants_and_reason_only;
544
545 j2735_v2x_msgs::msg::TrafficControlVehClass participant; // sending all possible VEHICLE will be processed as they are not accessuble by regionAccessRule
546
547 participant.vehicle_class = j2735_v2x_msgs::msg::TrafficControlVehClass::MICROMOBILE;
548
549 participants_and_reason_only.params.vclasses.push_back(participant);
550
551 participant.vehicle_class = j2735_v2x_msgs::msg::TrafficControlVehClass::BUS;
552
553 participants_and_reason_only.params.vclasses.push_back(participant);
554
555 participant.vehicle_class = j2735_v2x_msgs::msg::TrafficControlVehClass::PASSENGER_CAR;
556
557 participants_and_reason_only.params.vclasses.push_back(participant);
558
559 participant.vehicle_class = j2735_v2x_msgs::msg::TrafficControlVehClass::TWO_AXLE_SIX_TIRE_SINGLE_UNIT_TRUCK;
560
561 participants_and_reason_only.params.vclasses.push_back(participant);
562
563 participants_and_reason_only.package.label = "SIG_WZ";
564
565 std::vector<lanelet::Lanelet> old_or_blocked_llts; // this is needed to addRegionAccessRule input signatures
566
567 // from all affected parts, remove duplicate entries
568 for (auto llt : work_zone_geofence_cache[WorkZoneSection::TAPERRIGHT]->affected_parts_)
569 {
570 if (std::find(old_or_blocked_llts.begin(), old_or_blocked_llts.end(), llt) == old_or_blocked_llts.end())
571 {
572 gf_ptr->affected_parts_.push_back(current_map_->laneletLayer.get(llt.lanelet()->id()));
573 old_or_blocked_llts.push_back(current_map_->laneletLayer.get(llt.lanelet()->id()));
574 }
575 }
576 for (auto llt : work_zone_geofence_cache[WorkZoneSection::CLOSED]->affected_parts_)
577 {
578 if (std::find(old_or_blocked_llts.begin(), old_or_blocked_llts.end(), llt) == old_or_blocked_llts.end())
579 {
580 gf_ptr->affected_parts_.push_back(current_map_->laneletLayer.get(llt.lanelet()->id()));
581 old_or_blocked_llts.push_back(current_map_->laneletLayer.get(llt.lanelet()->id()));
582 }
583 }
584 for (auto llt : work_zone_geofence_cache[WorkZoneSection::OPENRIGHT]->affected_parts_)
585 {
586 if (std::find(old_or_blocked_llts.begin(), old_or_blocked_llts.end(), llt) == old_or_blocked_llts.end())
587 {
588 gf_ptr->affected_parts_.push_back(current_map_->laneletLayer.get(llt.lanelet()->id()));
589 old_or_blocked_llts.push_back(current_map_->laneletLayer.get(llt.lanelet()->id()));
590 }
591 }
592
593 gf_ptr->affected_parts_.push_back(old_opposite_llts[0]); // block old lanelet in the opposing lanelet that will be replaced with split lanelets that have trafficlight
594 old_or_blocked_llts.push_back(old_opposite_llts[0]);
595
596 // actual regulatory element adder
597 addRegionAccessRule(gf_ptr, participants_and_reason_only, old_or_blocked_llts);
598
599 return gf_ptr;
600}
lanelet::Lanelet createLinearInterpolatingLanelet(const lanelet::Point3d &left_front_pt, const lanelet::Point3d &right_front_pt, const lanelet::Point3d &left_back_pt, const lanelet::Point3d &right_back_pt, double increment_distance=0.25)
lanelet::Lanelets splitOppositeLaneletWithPoint(std::shared_ptr< std::vector< lanelet::Lanelet > > opposite_llts, const lanelet::BasicPoint2d &input_pt, const lanelet::Lanelet &input_llt, double error_distance)
Split given lanelet's adjacent, OPPOSITE lanelet with same proportion as the given point's downtrack ...
uint32_t generate32BitId(const std::string &label)
helper for generating 32bit traffic light Id from TCM label field consisting workzone intersection/si...
void addRegionAccessRule(std::shared_ptr< Geofence > gf_ptr, const carma_v2x_msgs::msg::TrafficControlMessageV01 &msg_v01, const std::vector< lanelet::Lanelet > &affected_llts) const
Adds RegionAccessRule to the map.
static const uint8_t CLOSED
static const uint8_t OPENRIGHT
static const uint8_t REVERSE

References addRegionAccessRule(), carma_wm_ctrl::WorkZoneSection::CLOSED, createLinearInterpolatingLanelet(), current_map_, error_distance_, generate32BitId(), process_bag::i, carma_wm_ctrl::WorkZoneSection::OPENRIGHT, carma_wm_ctrl::WorkZoneSection::REVERSE, splitOppositeLaneletWithPoint(), and carma_wm_ctrl::WorkZoneSection::TAPERRIGHT.

Referenced by createWorkzoneGeofence().

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

◆ currentLocationCallback()

void carma_wm_ctrl::WMBroadcaster::currentLocationCallback ( geometry_msgs::msg::PoseStamped::UniquePtr  current_pos)

Definition at line 1962 of file WMBroadcaster.cpp.

1963{
1964 if (current_map_ && current_map_->laneletLayer.size() != 0) {
1965 carma_perception_msgs::msg::CheckActiveGeofence check = checkActiveGeofenceLogic(*current_pos);
1966 active_pub_(check);//Publish
1967 } else {
1968 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Could not check active geofence logic because map was not loaded");
1969 }
1970}
carma_perception_msgs::msg::CheckActiveGeofence checkActiveGeofenceLogic(const geometry_msgs::msg::PoseStamped &current_pos)
Returns a message indicating whether or not the vehicle is inside of an active geofence lanelet.

References active_pub_, checkActiveGeofenceLogic(), and current_map_.

Referenced by carma_wm_ctrl::WMBroadcasterNode::handle_on_activate().

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

◆ distToNearestActiveGeofence()

double carma_wm_ctrl::WMBroadcaster::distToNearestActiveGeofence ( const lanelet::BasicPoint2d &  curr_pos)

Returns the route distance (downtrack or crosstrack in meters) to the nearest active geofence lanelet.

Parameters
curr_posCurrent position in local coordinates
Exceptions
InvalidObjectStateErrorif base_map is not set
std::invalid_argumentif curr_pos is not on the road
Returns
0 if there is no active geofence on the vehicle's route

Definition at line 1884 of file WMBroadcaster.cpp.

1885{
1886 std::lock_guard<std::mutex> guard(map_mutex_);
1887
1888 if (!current_map_ || current_map_->laneletLayer.size() == 0)
1889 {
1890 throw lanelet::InvalidObjectStateError(std::string("Lanelet map (current_map_) is not loaded to the WMBroadcaster"));
1891 }
1892
1893 // filter only the lanelets in the route
1894 std::vector<lanelet::Id> active_geofence_on_route;
1895 for (auto llt : route_path_)
1896 {
1897 if (active_geofence_llt_ids_.find(llt.id()) != active_geofence_llt_ids_.end())
1898 active_geofence_on_route.push_back(llt.id());
1899 }
1900 // Get the lanelet of this point
1901 auto curr_lanelet = lanelet::geometry::findNearest(current_map_->laneletLayer, curr_pos, 1)[0].second;
1902
1903 // Check if this point at least is actually within this lanelets
1904 if (!boost::geometry::within(curr_pos, curr_lanelet.polygon2d().basicPolygon()))
1905 throw std::invalid_argument("Given point is not within any lanelet");
1906
1907 // get route distance (downtrack + cross_track) distances to every lanelets by their ids
1908 std::vector<double> route_distances;
1909 // and take abs of cross_track to add them to get route distance
1910 for (auto id: active_geofence_on_route)
1911 {
1912 carma_wm::TrackPos tp = carma_wm::geometry::trackPos(current_map_->laneletLayer.get(id), curr_pos);
1913 // downtrack needs to be negative for lanelet to be in front of the point,
1914 // also we don't account for the lanelet that the vehicle is on
1915 if (tp.downtrack < 0 && id != curr_lanelet.id())
1916 {
1917 double dist = fabs(tp.downtrack) + fabs(tp.crosstrack);
1918 route_distances.push_back(dist);
1919 }
1920 }
1921 std::sort(route_distances.begin(), route_distances.end());
1922
1923 if (route_distances.size() != 0 ) return route_distances[0];
1924 else return 0.0;
1925
1926}
Position in a track based coordinate system where the axis are downtrack and crosstrack....
Definition: TrackPos.hpp:35
TrackPos trackPos(const lanelet::ConstLanelet &lanelet, const lanelet::BasicPoint2d &point)
Returns the TrackPos, computed in 2d, of the provided point relative to the centerline of the provide...
Definition: Geometry.cpp:120

References active_geofence_llt_ids_, carma_wm::TrackPos::crosstrack, current_map_, carma_wm::TrackPos::downtrack, map_mutex_, route_path_, and carma_wm::geometry::trackPos().

Referenced by checkActiveGeofenceLogic().

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

◆ externalMapMsgCallback()

void carma_wm_ctrl::WMBroadcaster::externalMapMsgCallback ( carma_v2x_msgs::msg::MapData::UniquePtr  map_msg)

Callback to MAP.msg which contains intersections' static info such geometry and lane ids.

Parameters
map_msgThe ROS msg of the MAP.msg to process

Definition at line 1037 of file WMBroadcaster.cpp.

1038{
1039 auto gf_ptr = std::make_shared<Geofence>();
1040
1041 if (!current_map_ || current_map_->laneletLayer.size() == 0)
1042 {
1043 RCLCPP_INFO_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Map is not available yet. Skipping MAP msg");
1044 return;
1045 }
1046
1047 // check if we have seen this message already
1048 bool up_to_date = false;
1049 if (sim_->intersection_id_to_regem_id_.size() == map_msg->intersections.size())
1050 {
1051 up_to_date = true;
1052 // check id of the intersection only
1053 for (auto intersection : map_msg->intersections)
1054 {
1055 if (sim_->intersection_id_to_regem_id_.find(intersection.id.id) == sim_->intersection_id_to_regem_id_.end())
1056 {
1057 up_to_date = false;
1058 break;
1059 }
1060 }
1061 }
1062
1063 if(up_to_date)
1064 {
1065 return;
1066 }
1067
1068 gf_ptr->map_msg_ = *map_msg;
1069 gf_ptr->msg_.package.label_exists = true;
1070 gf_ptr->msg_.package.label = "MAP_MSG";
1071 gf_ptr->id_ = boost::uuids::random_generator()();
1072
1073 // create dummy traffic Control message to add instant activation schedule
1074 carma_v2x_msgs::msg::TrafficControlMessageV01 traffic_control_msg;
1075
1076 // process schedule from message
1077 addScheduleFromMsg(gf_ptr, traffic_control_msg);
1078
1079 scheduleGeofence(gf_ptr);
1080
1081}
void scheduleGeofence(std::shared_ptr< carma_wm_ctrl::Geofence > gf_ptr_list)
void addScheduleFromMsg(std::shared_ptr< Geofence > gf_ptr, const carma_v2x_msgs::msg::TrafficControlMessageV01 &msg_v01)
Populates the schedules member of the geofence object from given TrafficControlMessageV01 message.

References addScheduleFromMsg(), current_map_, scheduleGeofence(), and sim_.

Referenced by carma_wm_ctrl::WMBroadcasterNode::handle_on_activate().

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

◆ filterSuccessorLanelets()

std::unordered_set< lanelet::Lanelet > carma_wm_ctrl::WMBroadcaster::filterSuccessorLanelets ( const std::unordered_set< lanelet::Lanelet > &  possible_lanelets,
const std::unordered_set< lanelet::Lanelet > &  root_lanelets 
)
private

◆ generate32BitId()

uint32_t carma_wm_ctrl::WMBroadcaster::generate32BitId ( const std::string &  label)

helper for generating 32bit traffic light Id from TCM label field consisting workzone intersection/signal group ids

Definition at line 1259 of file WMBroadcaster.cpp.

1260{
1261 auto pos1 = label.find("INT_ID:") + 7;
1262 auto pos2 = label.find("SG_ID:") + 6;
1263
1264 uint16_t intersection_id = std::stoi(label.substr(pos1 , 4));
1265 uint8_t signal_id = std::stoi(label.substr(pos2 , 3));
1266
1267 return carma_wm::utils::get32BitId(intersection_id, signal_id);
1268}
uint32_t get32BitId(uint16_t intersection_id, uint8_t signal_group_id)
Get 32bit id by concatenating 16bit id with 8bit signal_group_id.

References carma_wm::utils::get32BitId().

Referenced by createWorkzoneGeometry().

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

◆ geofenceCallback()

void carma_wm_ctrl::WMBroadcaster::geofenceCallback ( carma_v2x_msgs::msg::TrafficControlMessage::UniquePtr  geofence_msg)

Callback to add a geofence to the map. Currently only supports version 1 TrafficControlMessage.

Parameters
geofence_msgThe ROS msg of the geofence to add.

Definition at line 1084 of file WMBroadcaster.cpp.

1085{
1086
1087 std::lock_guard<std::mutex> guard(map_mutex_);
1088 std::stringstream reason_ss;
1089 // quickly check if the id has been added
1090 if (geofence_msg->choice != carma_v2x_msgs::msg::TrafficControlMessage::TCMV01) {
1091 reason_ss << "Dropping received geofence for unsupported TrafficControl version: " << geofence_msg->choice;
1092 RCLCPP_WARN_STREAM(rclcpp::get_logger("carma_wm_ctrl"), reason_ss.str());
1093 pubTCMACK(geofence_msg->tcm_v01.reqid, geofence_msg->tcm_v01.msgnum, static_cast<int>(AcknowledgementStatus::REJECTED), reason_ss.str());
1094 return;
1095 }
1096
1097 boost::uuids::uuid id;
1098 std::copy(geofence_msg->tcm_v01.id.id.begin(), geofence_msg->tcm_v01.id.id.end(), id.begin());
1100 reason_ss.str("");
1101 reason_ss << "Dropping received TrafficControl message with already handled id: " << boost::uuids::to_string(id);
1102 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), reason_ss.str());
1103 pubTCMACK(geofence_msg->tcm_v01.reqid, geofence_msg->tcm_v01.msgnum, static_cast<int>(AcknowledgementStatus::ACKNOWLEDGED), reason_ss.str());
1104 return;
1105 }
1106
1107 // convert reqid to string check if it has been seen before
1108 boost::array<uint8_t, 16UL> req_id;
1109 for (auto i = 0; i < 8; i ++) req_id[i] = geofence_msg->tcm_v01.reqid.id[i];
1110 boost::uuids::uuid uuid_id;
1111 std::copy(req_id.begin(),req_id.end(), uuid_id.begin());
1112 std::string reqid = boost::uuids::to_string(uuid_id).substr(0, 8);
1113 // drop if the req has never been sent
1114 if (generated_geofence_reqids_.find(reqid) == generated_geofence_reqids_.end() && reqid.compare("00000000") != 0)
1115 {
1116 RCLCPP_WARN_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "CARMA_WM_CTRL received a TrafficControlMessage with unknown TrafficControlRequest ID (reqid): " << reqid);
1117 return;
1118 }
1119
1121
1122 auto gf_ptr = std::make_shared<Geofence>();
1123
1124 gf_ptr->msg_ = geofence_msg->tcm_v01;
1125
1126 try
1127 {
1128 // process schedule from message
1129 addScheduleFromMsg(gf_ptr, geofence_msg->tcm_v01);
1130 scheduleGeofence(gf_ptr);
1131 reason_ss.str("");
1132 reason_ss << "Successfully processed TCM.";
1133 pubTCMACK(geofence_msg->tcm_v01.reqid, geofence_msg->tcm_v01.msgnum, static_cast<int>(AcknowledgementStatus::ACKNOWLEDGED), reason_ss.str());
1134 }
1135 catch(std::exception& ex)
1136 {
1137 reason_ss.str("");
1138 reason_ss << "Failed to process TCM. " << ex.what();
1139 pubTCMACK(geofence_msg->tcm_v01.reqid, geofence_msg->tcm_v01.msgnum, static_cast<int>(AcknowledgementStatus::REJECTED), reason_ss.str());
1140 throw; //rethrows the exception object
1141 }
1142};
void pubTCMACK(j2735_v2x_msgs::msg::Id64b tcm_req_id, uint16_t msgnum, int ack_status, const std::string &ack_reason)
Construct TCM acknowledgement object and populate it with params. Publish the object for a configured...
std::unordered_set< std::string > checked_geofence_ids_

References ACKNOWLEDGED, addScheduleFromMsg(), checked_geofence_ids_, generated_geofence_reqids_, process_bag::i, map_mutex_, pubTCMACK(), REJECTED, scheduleGeofence(), and carma_cooperative_perception::to_string().

Referenced by carma_wm_ctrl::WMBroadcasterNode::handle_on_activate().

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

◆ geofenceFromMapMsg()

std::vector< std::shared_ptr< Geofence > > carma_wm_ctrl::WMBroadcaster::geofenceFromMapMsg ( std::shared_ptr< Geofence gf_ptr,
const carma_v2x_msgs::msg::MapData &  map_msg 
)

Fills geofence object from MAP Data ROS Msg which contains intersections' static data such as geometry and signal_group.

Parameters
Geofenceobject to fill with information extracted from this msg
geofence_msgThe MAP ROS msg that contains intersection information
Exceptions
InvalidObjectStateErrorif base_map is not set or the base_map's georeference is empty

Definition at line 217 of file WMBroadcaster.cpp.

218{
219 std::vector<std::shared_ptr<Geofence>> updates_to_send;
220 std::vector<std::shared_ptr<lanelet::SignalizedIntersection>> intersections;
221 std::vector<std::shared_ptr<lanelet::CarmaTrafficSignal>> traffic_signals;
222
223 auto sim_copy = std::make_shared<carma_wm::SignalizedIntersectionManager>(*sim_);
224
225 sim_->createIntersectionFromMapMsg(intersections, traffic_signals, map_msg, current_map_, current_routing_graph_);
226
227 if (*sim_ == *sim_copy) // if no change
228 {
229 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), ">>> Detected no change from previous, ignoring duplicate message! with gf id: " << gf_ptr->id_);
230 return {};
231 }
232
233 for (auto intersection : intersections)
234 {
235 auto update = std::make_shared<Geofence>();
236 update->id_ = boost::uuids::random_generator()();
238 update->regulatory_element_ = intersection;
239 for (auto llt : intersection->getEntryLanelets())
240 {
241 update->affected_parts_.push_back(llt);
242 }
243 // For debug purpose we add the intersection geometry points to visualize later
244 auto j2735_intersection_id = sim_->regem_id_to_intersection_id_[intersection->id()];
245 for (auto pt: sim_->intersection_nodes_[j2735_intersection_id])
246 {
247 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "J2735 MAP msg road geometry points: x: " << pt.x() << ", y: " << pt.y());
248 }
249 update->gf_pts.insert(update->gf_pts.end(), sim_->intersection_nodes_[j2735_intersection_id].begin(), sim_->intersection_nodes_[j2735_intersection_id].end());
250 updates_to_send.push_back(update);
251 }
252
253 for (auto signal : traffic_signals)
254 {
255 auto update = std::make_shared<Geofence>();
256 update->id_ = boost::uuids::random_generator()();
257 update->label_ = carma_wm_ctrl::MAP_MSG_TF_SIGNAL;
258 update->regulatory_element_ = signal;
259 for (auto llt : signal->getControlStartLanelets())
260 {
261 update->affected_parts_.push_back(llt);
262 }
263 updates_to_send.push_back(update);
264 }
265
266 return updates_to_send;
267}
const std::string MAP_MSG_TF_SIGNAL
Definition: Geofence.hpp:41

References current_map_, current_routing_graph_, carma_wm_ctrl::MAP_MSG_INTERSECTION, carma_wm_ctrl::MAP_MSG_TF_SIGNAL, and sim_.

Referenced by addGeofence().

Here is the caller graph for this function:

◆ geofenceFromMsg()

void carma_wm_ctrl::WMBroadcaster::geofenceFromMsg ( std::shared_ptr< Geofence gf_ptr,
const carma_v2x_msgs::msg::TrafficControlMessageV01 &  geofence_msg 
)

Fills geofence object from TrafficControlMessageV01 ROS Msg.

Parameters
Geofenceobject to fill with information extracted from this msg and previously cached msgs that are relevant
geofence_msgThe ROS msg that contains geofence information
Exceptions
InvalidObjectStateErrorif base_map is not set or the base_map's georeference is empty

Definition at line 269 of file WMBroadcaster.cpp.

270{
271 bool detected_workzone_signal = msg_v01.package.label_exists && msg_v01.package.label.find("SIG_WZ") != std::string::npos;
272 carma_v2x_msgs::msg::TrafficControlDetail msg_detail = msg_v01.params.detail;
273
274 // Get ID
275 std::copy(msg_v01.id.id.begin(), msg_v01.id.id.end(), gf_ptr->id_.begin());
276
277 gf_ptr->gf_pts = getPointsInLocalFrame(msg_v01);
278
279 gf_ptr->affected_parts_ = getAffectedLaneletOrAreas(gf_ptr->gf_pts);
280
281 if (gf_ptr->affected_parts_.size() == 0) {
282 RCLCPP_WARN_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "There is no applicable component in map for the new geofence message received by WMBroadcaster with id: " << gf_ptr->id_);
283 return; // Return empty geofence list
284 }
285
286 std::vector<lanelet::Lanelet> affected_llts;
287 std::vector<lanelet::Area> affected_areas;
288
289 // used for assigning them to the regem as parameters
290 for (auto llt_or_area : gf_ptr->affected_parts_)
291 {
292
293 if (llt_or_area.isLanelet()) affected_llts.push_back(current_map_->laneletLayer.get(llt_or_area.lanelet()->id()));
294 if (llt_or_area.isArea()) affected_areas.push_back(current_map_->areaLayer.get(llt_or_area.area()->id()));
295 }
296
297 // TODO: logic to determine what type of geofence goes here
298 // currently only converting portion of control message that is relevant to:
299 // - digital speed limit, passing control line, digital minimum gap, region access rule, and series of workzone related messages
300 lanelet::Velocity sL;
301
302
303 if (msg_detail.choice == carma_v2x_msgs::msg::TrafficControlDetail::MAXSPEED_CHOICE)
304 {
305 //Acquire speed limit information from TafficControlDetail msg
306 sL = lanelet::Velocity(msg_detail.maxspeed * lanelet::units::MPS());
307 std::string reason = "";
308 if (msg_v01.package.label_exists)
309 reason = msg_v01.package.label;
310
311 if(config_limit > 0_mph && config_limit < 80_mph && config_limit < sL)//Accounting for the configured speed limit, input zero when not in use
312 sL = config_limit;
313 //Ensure Geofences do not provide invalid speed limit data (exceed predetermined maximum value)
314 // @SONAR_STOP@
315 if(sL > 80_mph )
316 {
317 RCLCPP_WARN_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Digital maximum speed limit is invalid. Value capped at max speed limit."); //Output warning message
318 sL = 80_mph; //Cap the speed limit to the predetermined maximum value
319
320 }
321 if(sL < 0_mph)
322 {
323 RCLCPP_WARN_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Digital speed limit is invalid. Value set to 0mph.");
324 sL = 0_mph;
325 }// @SONAR_START@
326
327 gf_ptr->regulatory_element_ = std::make_shared<lanelet::DigitalSpeedLimit>(lanelet::DigitalSpeedLimit::buildData(lanelet::utils::getId(),
328 sL, affected_llts, affected_areas, participantsChecker(msg_v01), reason));
329 }
330 if (msg_detail.choice == carma_v2x_msgs::msg::TrafficControlDetail::MINSPEED_CHOICE)
331 {
332 //Acquire speed limit information from TafficControlDetail msg
333 sL = lanelet::Velocity(msg_detail.minspeed * lanelet::units::MPS());
334 if(config_limit > 0_mph && config_limit < 80_mph)//Accounting for the configured speed limit, input zero when not in use
335 sL = config_limit;
336
337 std::string reason = "";
338 if (msg_v01.package.label_exists)
339 reason = msg_v01.package.label;
340
341 //Ensure Geofences do not provide invalid speed limit data
342 // @SONAR_STOP@
343 if(sL > 80_mph )
344 {
345 RCLCPP_WARN_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Digital speed limit is invalid. Value capped at max speed limit.");
346 sL = 80_mph;
347 }
348 if(sL < 0_mph)
349 {
350 RCLCPP_WARN_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Digital speed limit is invalid. Value set to 0mph.");
351 sL = 0_mph;
352 }// @SONAR_START@
353 gf_ptr->regulatory_element_ = std::make_shared<lanelet::DigitalSpeedLimit>(lanelet::DigitalSpeedLimit::buildData(lanelet::utils::getId(),
354 sL, affected_llts, affected_areas, participantsChecker(msg_v01), reason));
355 }
356 if (msg_detail.choice == carma_v2x_msgs::msg::TrafficControlDetail::LATPERM_CHOICE || msg_detail.choice == carma_v2x_msgs::msg::TrafficControlDetail::LATAFFINITY_CHOICE)
357 {
358 addPassingControlLineFromMsg(gf_ptr, msg_v01, affected_llts);
359 }
360 if (msg_detail.choice == carma_v2x_msgs::msg::TrafficControlDetail::MINHDWY_CHOICE)
361 {
362
363 double min_gap = (double)msg_detail.minhdwy;
364
365 if(min_gap < 0)
366 {
367 RCLCPP_WARN_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Digital min gap is invalid. Value set to 0 meter.");
368 min_gap = 0;
369 }
370 addRegionMinimumGap(gf_ptr,msg_v01, min_gap, affected_llts, affected_areas);
371 }
372
373 if (detected_workzone_signal && msg_detail.choice != carma_v2x_msgs::msg::TrafficControlDetail::MAXSPEED_CHOICE) // if workzone message detected, save to cache to process later
374 {
375 gf_ptr->label_ = msg_v01.package.label; // to extract intersection, and signal group id
376 if (msg_detail.choice == carma_v2x_msgs::msg::TrafficControlDetail::CLOSED_CHOICE && (msg_detail.closed == carma_v2x_msgs::msg::TrafficControlDetail::CLOSED ||
377 msg_detail.closed == carma_v2x_msgs::msg::TrafficControlDetail::TAPERRIGHT ||
378 msg_detail.closed == carma_v2x_msgs::msg::TrafficControlDetail::OPENRIGHT))
379 {
380 work_zone_geofence_cache_[msg_detail.closed] = gf_ptr;
381 }
382 else if (msg_detail.choice == carma_v2x_msgs::msg::TrafficControlDetail::DIRECTION_CHOICE && msg_detail.direction == carma_v2x_msgs::msg::TrafficControlDetail::REVERSE)
383 {
385 }
386 return;
387 }
388 else if (msg_detail.choice == carma_v2x_msgs::msg::TrafficControlDetail::CLOSED_CHOICE && msg_detail.closed==carma_v2x_msgs::msg::TrafficControlDetail::CLOSED) // if stand-alone closed signal apart from Workzone
389 {
390 addRegionAccessRule(gf_ptr,msg_v01,affected_llts);
391 }
392
393 if (msg_detail.choice == carma_v2x_msgs::msg::TrafficControlDetail::RESTRICTED_CHOICE) {
394 addRegionAccessRule(gf_ptr, msg_v01, affected_llts);
395 }
396
397 return;
398}
void addRegionMinimumGap(std::shared_ptr< Geofence > gf_ptr, const carma_v2x_msgs::msg::TrafficControlMessageV01 &msg_v01, double min_gap, const std::vector< lanelet::Lanelet > &affected_llts, const std::vector< lanelet::Area > &affected_areas) const
Adds Minimum Gap to the map.
lanelet::ConstLaneletOrAreas getAffectedLaneletOrAreas(const lanelet::Points3d &gf_pts)
Gets the affected lanelet or areas based on the points in local frame.
lanelet::Points3d getPointsInLocalFrame(const carma_v2x_msgs::msg::TrafficControlMessageV01 &geofence_msg)
Extract geofence points from geofence message using its given proj and datum fields.
void addPassingControlLineFromMsg(std::shared_ptr< Geofence > gf_ptr, const carma_v2x_msgs::msg::TrafficControlMessageV01 &msg_v01, const std::vector< lanelet::Lanelet > &affected_llts) const

References addPassingControlLineFromMsg(), addRegionAccessRule(), addRegionMinimumGap(), config_limit, current_map_, getAffectedLaneletOrAreas(), getPointsInLocalFrame(), participantsChecker(), carma_wm_ctrl::WorkZoneSection::REVERSE, and work_zone_geofence_cache_.

Referenced by addGeofence().

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

◆ geoReferenceCallback()

void carma_wm_ctrl::WMBroadcaster::geoReferenceCallback ( std_msgs::msg::String::UniquePtr  geo_ref)

Callback to set the base map georeference (proj string)

Parameters
georef_msgProj string that specifies the georeference of the map. It is used for transfering frames between that of geofence and that of the vehicle

Definition at line 1205 of file WMBroadcaster.cpp.

1206{
1207 std::lock_guard<std::mutex> guard(map_mutex_);
1208 sim_->setTargetFrame(geo_ref->data);
1209 base_map_georef_ = geo_ref->data;
1210}

References base_map_georef_, map_mutex_, and sim_.

Referenced by carma_wm_ctrl::WMBroadcasterNode::handle_on_activate().

Here is the caller graph for this function:

◆ getAffectedLaneletOrAreas()

lanelet::ConstLaneletOrAreas carma_wm_ctrl::WMBroadcaster::getAffectedLaneletOrAreas ( const lanelet::Points3d &  gf_pts)

Gets the affected lanelet or areas based on the points in local frame.

Parameters
geofence_msglanelet::Points3d in local frame NOTE:Currently this function only checks lanelets and will be expanded to areas in the future.

Definition at line 1351 of file WMBroadcaster.cpp.

1352{
1354}
lanelet::ConstLaneletOrAreas getAffectedLaneletOrAreas(const lanelet::Points3d &gf_pts, const lanelet::LaneletMapPtr &lanelet_map, std::shared_ptr< const lanelet::routing::RoutingGraph > routing_graph, double max_lane_width)
Gets the affected lanelet or areas based on the points in the given map's frame.

References current_map_, current_routing_graph_, carma_wm::query::getAffectedLaneletOrAreas(), and max_lane_width_.

Referenced by geofenceFromMsg().

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

◆ getPointsInLocalFrame()

lanelet::Points3d carma_wm_ctrl::WMBroadcaster::getPointsInLocalFrame ( const carma_v2x_msgs::msg::TrafficControlMessageV01 &  geofence_msg)

Extract geofence points from geofence message using its given proj and datum fields.

Parameters
geofence_msgThe ROS msg that contains proj and any point that lie on the target lanelet or area
Exceptions
InvalidObjectStateErrorif base_map is not set or the base_map's georeference is empty
Returns
lanelet::Points3d in local frame

Definition at line 1271 of file WMBroadcaster.cpp.

1272{
1273 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Getting affected lanelets");
1274 if (!current_map_ || current_map_->laneletLayer.size() == 0)
1275 {
1276 throw lanelet::InvalidObjectStateError(std::string("Base lanelet map is not loaded to the WMBroadcaster"));
1277 }
1278 if (base_map_georef_ == "")
1279 throw lanelet::InvalidObjectStateError(std::string("Base lanelet map has empty proj string loaded as georeference. Therefore, WMBroadcaster failed to\n ") +
1280 std::string("get transformation between the geofence and the map"));
1281
1282 // This next section handles the geofence projection conversion
1283 // The datum field is used to identify the frame for the provided referance lat/lon.
1284 // This reference is then converted to the provided projection as a reference origin point
1285 // From the reference the message projection to map projection transformation is used to convert the nodes in the TrafficControlMessage
1286 std::string projection = tcm_v01.geometry.proj;
1287 std::string datum = tcm_v01.geometry.datum;
1288 if (datum.empty()) {
1289 RCLCPP_WARN_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Datum field not populated. Attempting to use WGS84");
1290 datum = "WGS84";
1291 }
1292
1293 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Projection field: " << projection);
1294 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Datum field: " << datum);
1295
1296 std::string universal_frame = datum; //lat/long included in TCM is in this datum
1297
1298
1299 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Traffic Control heading provided: " << tcm_v01.geometry.heading << " System understanding is that this value will not affect the projection and is only provided for supporting derivative calculations.");
1300
1301 // Create the resulting projection transformation
1302 PJ* universal_to_target = proj_create_crs_to_crs(PJ_DEFAULT_CTX, universal_frame.c_str(), projection.c_str(), nullptr);
1303 if (universal_to_target == nullptr) { // proj_create_crs_to_crs returns 0 when there is an error in the projection
1304
1305 RCLCPP_ERROR_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Failed to generate projection between geofence and map with error number: " << proj_context_errno(PJ_DEFAULT_CTX)
1306 << " universal_frame: " << universal_frame << " projection: " << projection);
1307
1308 return {}; // Ignore geofence if it could not be projected from universal to TCM frame
1309 }
1310
1311 PJ* target_to_map = proj_create_crs_to_crs(PJ_DEFAULT_CTX, projection.c_str(), base_map_georef_.c_str(), nullptr);
1312
1313 if (target_to_map == nullptr) { // proj_create_crs_to_crs returns 0 when there is an error in the projection
1314
1315 RCLCPP_ERROR_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Failed to generate projection between geofence and map with error number: " << proj_context_errno(PJ_DEFAULT_CTX)
1316 << " target_to_map: " << target_to_map << " base_map_georef_: " << base_map_georef_);
1317
1318 return {}; // Ignore geofence if it could not be projected into the map frame
1319
1320 }
1321
1322 // convert all geofence points into our map's frame
1323 std::vector<lanelet::Point3d> gf_pts;
1324 carma_v2x_msgs::msg::PathNode prev_pt;
1325 PJ_COORD c_init_latlong{{tcm_v01.geometry.reflat, tcm_v01.geometry.reflon, tcm_v01.geometry.refelv}};
1326 PJ_COORD c_init = proj_trans(universal_to_target, PJ_FWD, c_init_latlong);
1327
1328 prev_pt.x = c_init.xyz.x;
1329 prev_pt.y = c_init.xyz.y;
1330
1331 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "In TCM's frame, initial Point X "<< prev_pt.x<<" Before conversion: Point Y "<< prev_pt.y );
1332 for (auto pt : tcm_v01.geometry.nodes)
1333 {
1334 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Before conversion in TCM frame: Point X "<< pt.x <<" Before conversion: Point Y "<< pt.y);
1335
1336 PJ_COORD c {{prev_pt.x + pt.x, prev_pt.y + pt.y, 0, 0}}; // z is not currently used
1337 PJ_COORD c_out;
1338 c_out = proj_trans(target_to_map, PJ_FWD, c);
1339
1340 gf_pts.push_back(lanelet::Point3d{current_map_->pointLayer.uniqueId(), c_out.xyz.x, c_out.xyz.y});
1341 prev_pt.x += pt.x;
1342 prev_pt.y += pt.y;
1343
1344 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "After conversion in Map frame: Point X "<< gf_pts.back().x() <<" After conversion: Point Y "<< gf_pts.back().y());
1345 }
1346
1347 // save the points converted to local map frame
1348 return gf_pts;
1349}

References base_map_georef_, process_traj_logs::c, and current_map_.

Referenced by geofenceFromMsg().

Here is the caller graph for this function:

◆ getRoute()

carma_planning_msgs::msg::Route carma_wm_ctrl::WMBroadcaster::getRoute ( )

Returns the most recently recieved route message.

Returns
The most recent route message.

Definition at line 1644 of file WMBroadcaster.cpp.

1645{
1646 return current_route;
1647}
carma_planning_msgs::msg::Route current_route

References current_route.

◆ getVehicleParticipationType()

std::string carma_wm_ctrl::WMBroadcaster::getVehicleParticipationType ( )

Get the Vehicle Participation Type object.

Returns
Current Vehicle Participation Type being used in this World Model Instance

Definition at line 1254 of file WMBroadcaster.cpp.

1255{
1256 return participant_;
1257}

References participant_.

◆ invertParticipants()

std::vector< std::string > carma_wm_ctrl::WMBroadcaster::invertParticipants ( const std::vector< std::string > &  input_participants) const

Generates inverse participants list of the given participants.

Parameters
std::vector<std::string>participants vector of strings

Definition at line 1000 of file WMBroadcaster.cpp.

1001{
1002 std::vector<std::string> participants;
1003
1004 if(std::find(input_participants.begin(),input_participants.end(),lanelet::Participants::Pedestrian ) == input_participants.end()) participants.emplace_back(lanelet::Participants::Pedestrian);
1005 if(std::find(input_participants.begin(),input_participants.end(),lanelet::Participants::Bicycle ) == input_participants.end()) participants.emplace_back(lanelet::Participants::Bicycle);
1006 if(std::find(input_participants.begin(),input_participants.end(),lanelet::Participants::Vehicle ) == input_participants.end())
1007 {
1008 if(std::find(input_participants.begin(),input_participants.end(),lanelet::Participants::VehicleMotorcycle)== input_participants.end()) participants.emplace_back(lanelet::Participants::VehicleMotorcycle);
1009 if(std::find(input_participants.begin(),input_participants.end(),lanelet::Participants::VehicleBus)== input_participants.end()) participants.emplace_back(lanelet::Participants::VehicleBus);
1010 if(std::find(input_participants.begin(),input_participants.end(),lanelet::Participants::VehicleCar)== input_participants.end()) participants.emplace_back(lanelet::Participants::VehicleCar);
1011 if(std::find(input_participants.begin(),input_participants.end(),lanelet::Participants::VehicleTruck)== input_participants.end()) participants.emplace_back(lanelet::Participants::VehicleTruck);
1012 }
1013 return participants;
1014}

Referenced by addRegionAccessRule().

Here is the caller graph for this function:

◆ participantsChecker()

std::vector< std::string > carma_wm_ctrl::WMBroadcaster::participantsChecker ( const carma_v2x_msgs::msg::TrafficControlMessageV01 &  msg_v01) const

Generates participants list.

Parameters
msg_v01message type

Definition at line 958 of file WMBroadcaster.cpp.

959{
960 std::vector<std::string> participants;
961 for (j2735_v2x_msgs::msg::TrafficControlVehClass participant : msg_v01.params.vclasses)
962 {
963 // Currently j2735_v2x_msgs::msg::TrafficControlVehClass::RAIL is not supported
964 if (participant.vehicle_class == j2735_v2x_msgs::msg::TrafficControlVehClass::ANY)
965 {
966 participants = {lanelet::Participants::Vehicle, lanelet::Participants::Pedestrian, lanelet::Participants::Bicycle};
967 break;
968 }
969 else if (participant.vehicle_class == j2735_v2x_msgs::msg::TrafficControlVehClass::PEDESTRIAN)
970 {
971 participants.push_back(lanelet::Participants::Pedestrian);
972 }
973 else if (participant.vehicle_class == j2735_v2x_msgs::msg::TrafficControlVehClass::BICYCLE)
974 {
975 participants.push_back(lanelet::Participants::Bicycle);
976 }
977 else if (participant.vehicle_class == j2735_v2x_msgs::msg::TrafficControlVehClass::MICROMOBILE ||
978 participant.vehicle_class == j2735_v2x_msgs::msg::TrafficControlVehClass::MOTORCYCLE)
979 {
980 participants.push_back(lanelet::Participants::VehicleMotorcycle);
981 }
982 else if (participant.vehicle_class == j2735_v2x_msgs::msg::TrafficControlVehClass::BUS)
983 {
984 participants.push_back(lanelet::Participants::VehicleBus);
985 }
986 else if (participant.vehicle_class == j2735_v2x_msgs::msg::TrafficControlVehClass::LIGHT_TRUCK_VAN ||
987 participant.vehicle_class == j2735_v2x_msgs::msg::TrafficControlVehClass::PASSENGER_CAR)
988 {
989 participants.push_back(lanelet::Participants::VehicleCar);
990 }
991 else if (8<= participant.vehicle_class && participant.vehicle_class <= 16) // Truck enum definition range from 8-16 currently
992 {
993 participants.push_back(lanelet::Participants::VehicleTruck);
994 }
995 }
996 // combine to single vehicle type if possible, otherwise pass through
997 return combineParticipantsToVehicle(participants);
998}
std::vector< std::string > combineParticipantsToVehicle(const std::vector< std::string > &input_participants) const
Combines a list of the given participants into a single "vehicle" type if participants cover all poss...

References combineParticipantsToVehicle().

Referenced by addPassingControlLineFromMsg(), addRegionAccessRule(), addRegionMinimumGap(), and geofenceFromMsg().

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

◆ preprocessWorkzoneGeometry()

void carma_wm_ctrl::WMBroadcaster::preprocessWorkzoneGeometry ( std::unordered_map< uint8_t, std::shared_ptr< Geofence > >  work_zone_geofence_cache,
std::shared_ptr< std::vector< lanelet::Lanelet > >  parallel_llts,
std::shared_ptr< std::vector< lanelet::Lanelet > >  opposite_llts 
)

Preprocess for workzone area. Parallel_llts will have front_parallel and back_parallel lanelets that were created from splitting (if necessary) TAPERRIGHT and OPENRIGHT lanelets. Opposite_llts will have new lanelets split from REVERSE part with same direction as parallel lanelets that connect to diagonal ones.

Parameters
work_zone_geofence_cacheGeofence map with size of 4 corresponding to CLOSED, TAPERRIGHT, OPENRIGHT, REVERSE TrafficControlMessages. Each should have gf_pts, affected_parts.
parallel_lltslist holding front_parallel and back_parallel. These are created from splitting old ones in place such that they connect to diagonal ones
opposite_lltslist holding split and filtered lanelets from REVERSE part that connect to diagonal ones
Exceptions
InvalidObjectStateErrorif no map is available NOTE: REVERSE part's geofence points should have same direction as opposite lane's as that is what getAffectedLaneletOrArea requires.

PARALLEL FRONT (TAPERRIGHT side)

PARALLEL BACK (OPENRIGHT side)

HANDLE MID HERE

OPPOSITE FRONT (OPENRIGHT side)

Fill in the middle part of middle lanelets

OPPOSITE BACK (TAPERRIGHT side)

Definition at line 607 of file WMBroadcaster.cpp.

608{
609 if (!current_map_ || current_map_->laneletLayer.size() == 0)
610 {
611 throw lanelet::InvalidObjectStateError(std::string("Base lanelet map is not loaded to the WMBroadcaster"));
612 }
616 std::vector <lanelet::Lanelet> new_taper_right_llts;
617 auto taper_right_first_pt = work_zone_geofence_cache[WorkZoneSection::TAPERRIGHT]->gf_pts.front().basicPoint2d();
618 auto taper_right_first_llt = current_map_->laneletLayer.get(work_zone_geofence_cache[WorkZoneSection::TAPERRIGHT]->affected_parts_.front().lanelet().get().id());
619
620 new_taper_right_llts = splitLaneletWithPoint({taper_right_first_pt}, taper_right_first_llt, error_distance_);
621 double check_dist_tpr = lanelet::geometry::distance2d(taper_right_first_llt.centerline2d().front().basicPoint2d(), taper_right_first_pt);
622
623 // if no splitting happened and taperright's first point is close to starting boundary, we need to create duplicate of previous lanelet of TAPERRIGHT's first lanelet
624 // to match the output expected of this function as if split happened
625 if (new_taper_right_llts.size() == 1 && check_dist_tpr <= error_distance_)
626 {
627 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Creating duplicate lanelet of 'previous lanelet' due to TAPERRIGHT using entire lanelet...");
628 auto previous_lanelets = current_routing_graph_->previous(work_zone_geofence_cache[WorkZoneSection::TAPERRIGHT]->affected_parts_.front().lanelet().get());
629 if (previous_lanelets.empty()) //error if bad match
630 {
631 RCLCPP_ERROR_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Workzone area starts from lanelet with no previous lanelet (Id : " << work_zone_geofence_cache[WorkZoneSection::TAPERRIGHT]->affected_parts_.front().lanelet().get().id()
632 << ". This case is rare and not supported at the moment.");
633 return;
634 }
635
636 // get previous lanelet of affected part of TAPERRIGHT (doesn't matter which previous, as the new lanelet will only be duplicate anyways)
637 auto prev_lanelet_to_copy = current_map_->laneletLayer.get(previous_lanelets.front().id());
638
639 // parallel_llts will have a copy of `prev_lanelet_to_copy` with new id to be used as part of workzone area
640 new_taper_right_llts = splitLaneletWithPoint({prev_lanelet_to_copy.centerline2d().back()}, prev_lanelet_to_copy, error_distance_);
641 }
642 parallel_llts->insert(parallel_llts->end(), new_taper_right_llts.begin(), new_taper_right_llts.end());
643 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Finished TAPERRIGHT processing of size: " << new_taper_right_llts.size());
644
648 std::vector <lanelet::Lanelet> new_open_right_llts;
649 auto open_right_last_pt = work_zone_geofence_cache[WorkZoneSection::OPENRIGHT]->gf_pts.back().basicPoint2d();
650 auto open_right_last_llt = current_map_->laneletLayer.get(work_zone_geofence_cache[WorkZoneSection::OPENRIGHT]->affected_parts_.back().lanelet().get().id());
651
652 new_open_right_llts = splitLaneletWithPoint({open_right_last_pt}, open_right_last_llt, error_distance_);
653 double check_dist_opr = lanelet::geometry::distance2d(open_right_last_llt.centerline2d().back().basicPoint2d(), open_right_last_pt);
654
655 // if no splitting happened, we need to create duplicate of next lanelet of OPENRIGHT's last lanelet
656 // to match the output expected of this function as if split happened
657 if (new_open_right_llts.size() == 1 && check_dist_opr <= error_distance_)
658 {
659 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Creating duplicate lanelet of 'next lanelet' due to OPENRIGHT using entire lanelet...");
660 auto next_lanelets = current_routing_graph_->following(work_zone_geofence_cache[WorkZoneSection::OPENRIGHT]->affected_parts_.back().lanelet().get());
661 if (next_lanelets.empty()) //error if bad match
662 {
663 RCLCPP_ERROR_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Workzone area ends at lanelet with no following lanelet (Id : " << work_zone_geofence_cache[WorkZoneSection::OPENRIGHT]->affected_parts_.back().lanelet().get().id()
664 << ". This case is rare and not supported at the moment.");
665 return;
666 }
667
668 // get next lanelet of affected part of OPENRIGHT (doesn't matter which next, as the new lanelet will only be duplicate anyways)
669 auto next_lanelet_to_copy = current_map_->laneletLayer.get(next_lanelets.front().id());
670
671 // parallel_llts will have a copy of `next_lanelet_to_copy` with new id to be used as part of workzone area
672 new_open_right_llts = splitLaneletWithPoint({next_lanelet_to_copy.centerline2d().back()}, next_lanelet_to_copy, error_distance_);
673 }
674 parallel_llts->insert(parallel_llts->end(), new_open_right_llts.begin(), new_open_right_llts.end());
675 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Finished OPENRIGHT processing of size: " << new_open_right_llts.size());
676
680
681 auto reverse_back_llts = carma_wm::query::getLaneletsFromPoint(current_map_, work_zone_geofence_cache[WorkZoneSection::REVERSE]->gf_pts.back().basicPoint2d());
682 auto reverse_back_llt = reverse_back_llts[0];
683 auto reverse_front_llts = carma_wm::query::getLaneletsFromPoint(current_map_, work_zone_geofence_cache[WorkZoneSection::REVERSE]->gf_pts.front().basicPoint2d());
684 auto reverse_front_llt = reverse_front_llts[0];
685
686 if (reverse_back_llt.id() == reverse_front_llt.id()) //means there is only 1 middle lanelet, which needs to be split into 3 lanelets
687 {
688 std::vector<lanelet::Lanelet> temp_llts;
689 temp_llts = splitLaneletWithPoint({work_zone_geofence_cache[WorkZoneSection::REVERSE]->gf_pts.front().basicPoint2d(),
690 work_zone_geofence_cache[WorkZoneSection::REVERSE]->gf_pts.back().basicPoint2d()},
691 current_map_->laneletLayer.get(work_zone_geofence_cache[WorkZoneSection::REVERSE]->affected_parts_.front().lanelet().get().id()), error_distance_);
692
693 if (temp_llts.size() < 2) // if there is only 1 lanelet
694 {
695 // we found what we want, so return
696 opposite_llts->insert(opposite_llts->end(), temp_llts.begin(), temp_llts.end());
697 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Ended preprocessWorkzoneGeometry with opposite_llts.size()" << opposite_llts->size() << ", and parallel_llts.size()" << parallel_llts->size());
698 return;
699 }
700 else if (temp_llts.size() == 2) // determine which
701 {
702 // back gap is bigger than front's, we should lose front lanelet from the two
703 if (lanelet::geometry::distance2d(work_zone_geofence_cache[WorkZoneSection::REVERSE]->gf_pts.back().basicPoint2d(), reverse_front_llt.centerline2d().back().basicPoint2d()) >
704 lanelet::geometry::distance2d(work_zone_geofence_cache[WorkZoneSection::REVERSE]->gf_pts.front().basicPoint2d(), reverse_front_llt.centerline2d().front().basicPoint2d()))
705 {
706 opposite_llts->push_back(temp_llts.back());
707 }
708 else
709 {
710 opposite_llts->push_back(temp_llts.front());
711 }
712 }
713 else if (temp_llts.size() == 3) // leave only middle lanelets from 3
714 {
715 opposite_llts->insert(opposite_llts->end(), temp_llts.begin() + 1, temp_llts.end()- 1);
716 }
717 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Finished REVERSE processing of size: " << opposite_llts->size() << " from original of 1 REVERSE lanelet size");
718 }
719 else //if there are two or more lanelets
720 {
722 auto reverse_first_pt = work_zone_geofence_cache[WorkZoneSection::REVERSE]->gf_pts.front().basicPoint2d();
723 auto reverse_first_llt = current_map_->laneletLayer.get(work_zone_geofence_cache[WorkZoneSection::REVERSE]->affected_parts_.front().lanelet().get().id());
724 std::vector<lanelet::Lanelet> temp_opposite_front_llts;
725 temp_opposite_front_llts = splitLaneletWithPoint( {reverse_first_pt}, reverse_first_llt, error_distance_);
726 if (temp_opposite_front_llts.size() > 1)
727 {
728 opposite_llts->insert(opposite_llts->end(), temp_opposite_front_llts.begin() + 1, temp_opposite_front_llts.end());
729 }
730 else
731 {
732 opposite_llts->insert(opposite_llts->end(), temp_opposite_front_llts.begin(), temp_opposite_front_llts.end());
733 }
735 if (work_zone_geofence_cache[WorkZoneSection::REVERSE]->affected_parts_.size() > 2)
736 {
737 for (int i = 1; i < work_zone_geofence_cache[WorkZoneSection::REVERSE]->affected_parts_.size() - 1; i ++)
738 {
739 opposite_llts->push_back(current_map_->laneletLayer.get(work_zone_geofence_cache[WorkZoneSection::REVERSE]->affected_parts_[i].id()));
740 }
741 }
743
744 auto reverse_last_pt = work_zone_geofence_cache[WorkZoneSection::REVERSE]->gf_pts.back().basicPoint2d();
745 auto reverse_last_llt = current_map_->laneletLayer.get(work_zone_geofence_cache[WorkZoneSection::REVERSE]->affected_parts_.back().lanelet().get().id());
746 std::vector<lanelet::Lanelet> temp_opposite_back_llts;
747 temp_opposite_back_llts = splitLaneletWithPoint({reverse_last_pt}, reverse_last_llt, error_distance_);
748 if (temp_opposite_back_llts.size() > 1)
749 {
750 opposite_llts->insert(opposite_llts->end(), temp_opposite_back_llts.begin(), temp_opposite_back_llts.end()- 1);
751 }
752 else
753 {
754 opposite_llts->insert(opposite_llts->end(), temp_opposite_back_llts.begin(), temp_opposite_back_llts.end());
755 }
756 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Finished REVERSE processing of size: " << opposite_llts->size() << " from original of more than one REVERSE lanelet size");
757 }
758
759 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Ended preprocessWorkzoneGeometry with opposite_llts.size()" << opposite_llts->size() << ", and parallel_llts.size()" << parallel_llts->size());
760}
std::vector< lanelet::Lanelet > splitLaneletWithPoint(const std::vector< lanelet::BasicPoint2d > &input_pts, const lanelet::Lanelet &input_llt, double error_distance)
Split given lanelet with same proportion as the given points' downtrack relative to the lanelet....
std::vector< lanelet::ConstLanelet > getLaneletsFromPoint(const lanelet::LaneletMapConstPtr &semantic_map, const lanelet::BasicPoint2d &point, const unsigned int n=10)
carma_wm::query namespace contains implementations for query functions (input and output read or writ...

References current_map_, current_routing_graph_, error_distance_, carma_wm::query::getLaneletsFromPoint(), process_bag::i, carma_wm_ctrl::WorkZoneSection::OPENRIGHT, carma_wm_ctrl::WorkZoneSection::REVERSE, splitLaneletWithPoint(), and carma_wm_ctrl::WorkZoneSection::TAPERRIGHT.

Referenced by createWorkzoneGeofence().

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

◆ publishLightId()

void carma_wm_ctrl::WMBroadcaster::publishLightId ( )

helps to populate upcoming_intersection_ids_ from local traffic lanelet ids

Definition at line 1987 of file WMBroadcaster.cpp.

1988{
1989 if (traffic_light_id_lookup_.empty())
1990 {
1991 return;
1992 }
1993 for(auto id : current_route.route_path_lanelet_ids)
1994 {
1995 bool convert_success = false;
1996 unsigned intersection_id = 0;
1997 unsigned group_id = 0;
1998 auto route_lanelet= current_map_->laneletLayer.get(id);
1999 auto traffic_lights = route_lanelet.regulatoryElementsAs<lanelet::CarmaTrafficSignal>();
2000
2001 if (!traffic_lights.empty())
2002 {
2003 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Found Traffic Light Regulatory Element id: " << traffic_lights.front()->id());
2004 convert_success = convertLightIdToInterGroupId(intersection_id,group_id, traffic_lights.front()->id());
2005 }
2006
2007 if (!convert_success)
2008 continue;
2009
2010 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Found Traffic Light with Intersection id: " << intersection_id << " Group id:" << group_id);
2011 bool id_exists = false;
2012 for (int idx = 0; idx < upcoming_intersection_ids_.data.size(); idx +2)
2013 {
2014 if (upcoming_intersection_ids_.data[idx] == intersection_id && upcoming_intersection_ids_.data[idx + 1] == group_id) //check if already there
2015 {
2016 id_exists = true;
2017 break;
2018 }
2019 }
2020
2021 if (id_exists)
2022 continue;
2023
2024 upcoming_intersection_ids_.data.push_back(static_cast<int>(intersection_id));
2025 upcoming_intersection_ids_.data.push_back(static_cast<int>(group_id));
2026 }
2027}
bool convertLightIdToInterGroupId(unsigned &intersection_id, unsigned &group_id, const lanelet::Id &lanelet_id)
helper for generating intersection and group Id of a traffic light from lanelet id
std_msgs::msg::Int32MultiArray upcoming_intersection_ids_

References convertLightIdToInterGroupId(), current_map_, current_route, traffic_light_id_lookup_, and upcoming_intersection_ids_.

Here is the call graph for this function:

◆ pubTCMACK()

void carma_wm_ctrl::WMBroadcaster::pubTCMACK ( j2735_v2x_msgs::msg::Id64b  tcm_req_id,
uint16_t  msgnum,
int  ack_status,
const std::string &  ack_reason 
)

Construct TCM acknowledgement object and populate it with params. Publish the object for a configured number of times.

Definition at line 2278 of file WMBroadcaster.cpp.

2279{
2280 carma_v2x_msgs::msg::MobilityOperation mom_msg;
2281 mom_msg.m_header.timestamp = scheduler_.now().nanoseconds()/1000000;
2282 mom_msg.m_header.sender_id = vehicle_id_;
2283 mom_msg.strategy = geofence_ack_strategy_;
2284 std::stringstream ss;
2285 for(size_t i=0; i < tcm_req_id.id.size(); i++)
2286 {
2287 ss << std::setfill('0') << std::setw(2) << std::hex << (unsigned) tcm_req_id.id.at(i);
2288 }
2289 std::string tcmv01_req_id_hex = ss.str();
2290 ss.str("");
2291 ss << "traffic_control_id:" << tcmv01_req_id_hex << ", msgnum:"<< msgnum << ", acknowledgement:" << ack_status << ", reason:" << ack_reason;
2292 mom_msg.strategy_params = ss.str();
2293 for(int i = 0; i < ack_pub_times_; i++)
2294 {
2295 tcm_ack_pub_(mom_msg);
2296 }
2297}
rclcpp::Time now()
Get current time used by scheduler.
const std::string geofence_ack_strategy_

References ack_pub_times_, geofence_ack_strategy_, process_bag::i, carma_wm_ctrl::GeofenceScheduler::now(), scheduler_, tcm_ack_pub_, and vehicle_id_.

Referenced by geofenceCallback().

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

◆ removeGeofence()

void carma_wm_ctrl::WMBroadcaster::removeGeofence ( std::shared_ptr< Geofence gf_ptr)

Removes a geofence from the current map and publishes the ROS msg.

Definition at line 1595 of file WMBroadcaster.cpp.

1596{
1597 std::lock_guard<std::mutex> guard(map_mutex_);
1598 RCLCPP_INFO_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Removing inactive geofence from the map with geofence id: " << gf_ptr->id_);
1599
1600 // Process the geofence object to populate update remove lists
1601 if (gf_ptr->affected_parts_.empty())
1602 return;
1603
1604 removeGeofenceHelper(gf_ptr);
1605
1606 for (auto pair : gf_ptr->remove_list_) active_geofence_llt_ids_.erase(pair.first);
1607
1608 // publish
1609 autoware_lanelet2_msgs::msg::MapBin gf_msg_revert;
1610 auto send_data = std::make_shared<carma_wm::TrafficControl>(carma_wm::TrafficControl(gf_ptr->id_, gf_ptr->update_list_, gf_ptr->remove_list_, {}));
1611
1612 if (gf_ptr->invalidate_route_) { // If a geofence initially invalidated the route it stands to reason its removal should as well
1613
1614 RCLCPP_INFO_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Rebuilding routing graph after is was invalidated by geofence removal");
1615
1616 lanelet::traffic_rules::TrafficRulesUPtr traffic_rules_car = lanelet::traffic_rules::TrafficRulesFactory::create(
1617 lanelet::traffic_rules::CarmaUSTrafficRules::Location, participant_
1618 );
1619 current_routing_graph_ = lanelet::routing::RoutingGraph::build(*current_map_, *traffic_rules_car);
1620
1621 RCLCPP_INFO_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Done rebuilding routing graph after is was invalidated by geofence removal");
1622
1623 // Populate routing graph structure
1624 RCLCPP_INFO_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Creating routing graph message for geofence removal");
1625
1626 auto readable_graph = std::static_pointer_cast<RoutingGraphAccessor>(current_routing_graph_);
1627
1628 gf_msg_revert.routing_graph = readable_graph->routingGraphToMsg(participant_);
1629 gf_msg_revert.has_routing_graph = true;
1630
1631 RCLCPP_INFO_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Done creating routing graph message for geofence removal");
1632 }
1633
1634 carma_wm::toBinMsg(send_data, &gf_msg_revert);
1635 update_count_++; // Update the sequence count for geofence messages
1636 gf_msg_revert.seq_id = update_count_;
1637 gf_msg_revert.map_version = current_map_version_;
1638
1639 map_update_pub_(gf_msg_revert);
1640
1641
1642}
void removeGeofenceHelper(std::shared_ptr< Geofence > gf_ptr) const

References active_geofence_llt_ids_, current_map_, current_map_version_, current_routing_graph_, map_mutex_, map_update_pub_, participant_, removeGeofenceHelper(), carma_wm::toBinMsg(), and update_count_.

Referenced by WMBroadcaster().

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

◆ removeGeofenceHelper()

void carma_wm_ctrl::WMBroadcaster::removeGeofenceHelper ( std::shared_ptr< Geofence gf_ptr) const
private

Definition at line 1951 of file WMBroadcaster.cpp.

1952{
1953 // Logic to determine what type of geofence
1954 // reset the info inside geofence
1955 gf_ptr->remove_list_ = {};
1956 gf_ptr->update_list_ = {};
1958 // as all changes are reverted back, we no longer need prev_regems
1959 gf_ptr->prev_regems_ = {};
1960}
void addBackRegulatoryComponent(std::shared_ptr< Geofence > gf_ptr) const

References addBackRegulatoryComponent().

Referenced by removeGeofence().

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

◆ routeCallbackMessage()

void carma_wm_ctrl::WMBroadcaster::routeCallbackMessage ( carma_planning_msgs::msg::Route::UniquePtr  route_msg)

Calls controlRequestFromRoute() and publishes the TrafficControlRequest Message returned after the completed operations.

Parameters
route_msgThe message containing route information

Definition at line 1649 of file WMBroadcaster.cpp.

1650{
1651 current_route = *route_msg;
1652 carma_v2x_msgs::msg::TrafficControlRequest cR;
1653 cR = controlRequestFromRoute(*route_msg);
1654 control_msg_pub_(cR);
1655
1656}
carma_v2x_msgs::msg::TrafficControlRequest controlRequestFromRoute(const carma_planning_msgs::msg::Route &route_msg, std::shared_ptr< j2735_v2x_msgs::msg::Id64b > req_id_for_testing=NULL)
Pulls vehicle information from CARMA Cloud at startup by providing its selected route in a TrafficCon...

References control_msg_pub_, controlRequestFromRoute(), and current_route.

Referenced by WMBroadcaster(), and carma_wm_ctrl::WMBroadcasterNode::handle_on_activate().

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

◆ scheduleGeofence()

void carma_wm_ctrl::WMBroadcaster::scheduleGeofence ( std::shared_ptr< carma_wm_ctrl::Geofence gf_ptr_list)
private

Definition at line 1144 of file WMBroadcaster.cpp.

1145{
1146 RCLCPP_INFO_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Scheduling new geofence message received by WMBroadcaster with id: " << gf_ptr->id_);
1147
1148 bool detected_workzone_signal = gf_ptr->msg_.package.label_exists && gf_ptr->msg_.package.label.find("SIG_WZ") != std::string::npos;
1149
1150 carma_v2x_msgs::msg::TrafficControlDetail msg_detail = gf_ptr->msg_.params.detail;
1151
1152 // create workzone specific extra speed geofence
1153 if (detected_workzone_signal && msg_detail.choice == carma_v2x_msgs::msg::TrafficControlDetail::MAXSPEED_CHOICE)
1154 {
1155 // duplicate the messages with inverted points to support new lanelets created from workzone
1156 // as carma-cloud currently does not support geofence points with direction opposite to that of the road
1157
1158 auto gf_ptr_speed = std::make_shared<Geofence>();
1159 gf_ptr_speed->schedules = gf_ptr->schedules;
1160
1161 carma_v2x_msgs::msg::TrafficControlMessageV01 duplicate_msg = gf_ptr->msg_;
1162
1163 std::reverse(duplicate_msg.geometry.nodes.begin() + 1, duplicate_msg.geometry.nodes.end());
1164 double first_x = 0;
1165 double first_y = 0;
1166
1167 // this fancy logic is needed as each node is expressed as an offset from the last one
1168 for (auto& pt: duplicate_msg.geometry.nodes)
1169 {
1170 first_x+= pt.x;
1171 first_y+= pt.y;
1172 pt.x = -1* pt.x;
1173 pt.y = -1* pt.y;
1174 }
1175 duplicate_msg.geometry.nodes[0].x = first_x;
1176 duplicate_msg.geometry.nodes[0].y = first_y;
1177
1178 gf_ptr_speed->msg_ = duplicate_msg;
1179 scheduler_.addGeofence(gf_ptr_speed);
1180 }
1181 if (detected_workzone_signal && msg_detail.choice != carma_v2x_msgs::msg::TrafficControlDetail::MAXSPEED_CHOICE) // if workzone message detected, save to cache to process later
1182 {
1183 gf_ptr->label_ = gf_ptr->msg_.package.label; // to extract intersection, and signal group id
1184 if (msg_detail.choice == carma_v2x_msgs::msg::TrafficControlDetail::CLOSED_CHOICE && (msg_detail.closed == carma_v2x_msgs::msg::TrafficControlDetail::CLOSED ||
1185 msg_detail.closed == carma_v2x_msgs::msg::TrafficControlDetail::TAPERRIGHT ||
1186 msg_detail.closed == carma_v2x_msgs::msg::TrafficControlDetail::OPENRIGHT))
1187 {
1188 work_zone_geofence_cache_[msg_detail.closed] = gf_ptr;
1189 }
1190 else if (msg_detail.choice == carma_v2x_msgs::msg::TrafficControlDetail::DIRECTION_CHOICE && msg_detail.direction == carma_v2x_msgs::msg::TrafficControlDetail::REVERSE)
1191 {
1193 }
1195 {
1196 RCLCPP_INFO_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Received 'SIG_WZ' signal. Waiting for the rest of the messages, returning for now...");
1197 return;
1198 }
1199 }
1200
1201 scheduler_.addGeofence(gf_ptr); // Add the geofence to the scheduler
1202
1203}
void addGeofence(std::shared_ptr< Geofence > gf_ptr)
Add a geofence to the scheduler. This will cause it to trigger an event when it becomes active or goe...
const int WORKZONE_TCM_REQUIRED_SIZE

References carma_wm_ctrl::GeofenceScheduler::addGeofence(), carma_wm_ctrl::WorkZoneSection::REVERSE, scheduler_, work_zone_geofence_cache_, and carma_wm_ctrl::WORKZONE_TCM_REQUIRED_SIZE.

Referenced by externalMapMsgCallback(), and geofenceCallback().

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

◆ setConfigACKPubTimes()

void carma_wm_ctrl::WMBroadcaster::setConfigACKPubTimes ( int  ack_pub_times)

Sets the TCM Acknowledgement publish times.

Parameters
ack_pub_timesthe number of times it publishes TCM Acknowledgement

Definition at line 1245 of file WMBroadcaster.cpp.

1245 {
1246 ack_pub_times_ = ack_pub_times;
1247}

References ack_pub_times_.

◆ setConfigSpeedLimit()

void carma_wm_ctrl::WMBroadcaster::setConfigSpeedLimit ( double  cL)

Sets the configured speed limit.

Definition at line 1235 of file WMBroadcaster.cpp.

1236{
1237 /*Logic to change config_lim to Velocity value config_limit*/
1238 config_limit = lanelet::Velocity(cL * lanelet::units::MPH());
1239}

References config_limit.

◆ setConfigVehicleId()

void carma_wm_ctrl::WMBroadcaster::setConfigVehicleId ( const std::string &  vehicle_id)

Retrieve the vehicle ID from global vehicle parameters, and set instance memeber vehicle id.

Parameters
vehicle_idVehicle ID from UniqueVehicleParams.yaml

Definition at line 1241 of file WMBroadcaster.cpp.

1241 {
1242 vehicle_id_ = vehicle_id;
1243}

References vehicle_id_.

◆ setErrorDistance()

void carma_wm_ctrl::WMBroadcaster::setErrorDistance ( double  error_distance)

Definition at line 602 of file WMBroadcaster.cpp.

603{
604 error_distance_ = error_distance;
605}

References error_distance_.

◆ setIntersectionCoordCorrection()

void carma_wm_ctrl::WMBroadcaster::setIntersectionCoordCorrection ( const std::vector< int64_t > &  intersection_ids_for_correction,
const std::vector< double > &  intersection_correction 
)

Sets the coordinate correction for intersection.

Parameters
listof intersection_ids corresponding to every two elements in correction list
listof intersection coord correction parameters in double in every 2 elements: delta_x, delta_y

Definition at line 1220 of file WMBroadcaster.cpp.

1221{
1222 if (intersection_correction.size() % 2 != 0 || intersection_ids_for_correction.size() != intersection_correction.size() / 2)
1223 {
1224 throw std::invalid_argument("Some of intersection coordinate correction parameters are not fully set!");
1225 }
1226
1227 for (auto i = 0; i < intersection_correction.size(); i = i + 2)
1228 {
1229 sim_->intersection_coord_correction_[(uint16_t)intersection_ids_for_correction[i/2]].first = intersection_correction[i]; //x
1230 sim_->intersection_coord_correction_[(uint16_t)intersection_ids_for_correction[i/2]].second = intersection_correction[i + 1]; //y
1231 }
1232
1233}

References process_bag::i, and sim_.

◆ setMaxLaneWidth()

void carma_wm_ctrl::WMBroadcaster::setMaxLaneWidth ( double  max_lane_width)

Sets the max lane width in meters. Geofence points are associated to a lanelet if they are within this distance to a lanelet as geofence points are guaranteed to apply to a single lane.

Definition at line 1212 of file WMBroadcaster.cpp.

1213{
1214 sim_ = std::make_shared<carma_wm::SignalizedIntersectionManager>();
1215
1216 max_lane_width_ = max_lane_width;
1217 sim_->setMaxLaneWidth(max_lane_width_);
1218}

References max_lane_width_, and sim_.

◆ setVehicleParticipationType()

void carma_wm_ctrl::WMBroadcaster::setVehicleParticipationType ( std::string  participant)

Set the Vehicle Participation Type.

Parameters
participantvehicle participation type

Definition at line 1249 of file WMBroadcaster.cpp.

1250{
1251 participant_ = participant;
1252}

References participant_.

◆ shouldChangeControlLine()

bool carma_wm_ctrl::WMBroadcaster::shouldChangeControlLine ( const lanelet::ConstLaneletOrArea &  el,
const lanelet::RegulatoryElementConstPtr &  regem,
std::shared_ptr< Geofence gf_ptr 
) const
private

This is a helper function that returns true if the provided regem is marked to be changed by the geofence as there are usually multiple passing control lines are in the lanelet.

Parameters
geofence_msgThe ROS msg that contains geofence information
elThe LaneletOrArea that houses the regem
regemThe regulatoryElement that needs to be checked NOTE: Currently this function only works on lanelets. It returns true if the regem is not passingControlLine or if the pcl should be changed.

Definition at line 1364 of file WMBroadcaster.cpp.

1365{
1366 // should change if if the regem is not a passing control line or area, which is not supported by this logic
1367 if (regem->attribute(lanelet::AttributeName::Subtype).value().compare(lanelet::PassingControlLine::RuleName) != 0 || !el.isLanelet())
1368 {
1369 return true;
1370 }
1371
1372 lanelet::PassingControlLinePtr pcl = std::dynamic_pointer_cast<lanelet::PassingControlLine>(current_map_->regulatoryElementLayer.get(regem->id()));
1373 // if this geofence's pcl doesn't match with the lanelets current bound side, return false as we shouldn't change
1374 bool should_change_pcl = false;
1375 for (auto control_line : pcl->controlLine())
1376 {
1377 if ((control_line.id() == el.lanelet()->leftBound2d().id() && gf_ptr->pcl_affects_left_) ||
1378 (control_line.id() == el.lanelet()->rightBound2d().id() && gf_ptr->pcl_affects_right_))
1379 {
1380 should_change_pcl = true;
1381 break;
1382 }
1383 }
1384 return should_change_pcl;
1385}

References current_map_.

Referenced by addBackRegulatoryComponent(), and addRegulatoryComponent().

Here is the caller graph for this function:

◆ shouldChangeTrafficSignal()

bool carma_wm_ctrl::WMBroadcaster::shouldChangeTrafficSignal ( const lanelet::ConstLaneletOrArea &  el,
const lanelet::RegulatoryElementConstPtr &  regem,
std::shared_ptr< carma_wm::SignalizedIntersectionManager sim 
) const
private

This is a helper function that returns true if signal in the lanelet should be changed according to the records of signalizer intersection manager Used in managing multiple signal_groups in a single entry lanelet for example.

Parameters
elThe LaneletOrArea that houses the regem
regemThe regulatoryElement that needs to be checked
simThe signalized intersection manager that has records regems and corresponding lanelets NOTE: Currently this function only works on lanelets. It returns true if the regem is not CarmaTrafficSignal or if the signal should be changed.

Definition at line 1395 of file WMBroadcaster.cpp.

1396{
1397 // should change if the regem is not a CarmaTrafficSignal, which is not supported by this logic
1398 if (regem->attribute(lanelet::AttributeName::Subtype).value().compare(lanelet::CarmaTrafficSignal::RuleName) != 0 || !el.isLanelet() || !sim_)
1399 {
1400 return true;
1401 }
1402
1403 lanelet::CarmaTrafficSignalPtr traffic_signal = std::dynamic_pointer_cast<lanelet::CarmaTrafficSignal>(current_map_->regulatoryElementLayer.get(regem->id()));
1404 uint8_t signal_id = 0;
1405
1406 for (auto it = sim->signal_group_to_traffic_light_id_.begin(); it != sim->signal_group_to_traffic_light_id_.end(); ++it)
1407 {
1408 if (regem->id() == it->second)
1409 {
1410 signal_id = it->first;
1411 }
1412 }
1413
1414 if (signal_id == 0) // doesn't exist in the record
1415 return true;
1416
1417 if (sim->signal_group_to_entry_lanelet_ids_[signal_id].find(el.id()) != sim->signal_group_to_entry_lanelet_ids_[signal_id].end())
1418 return false; // signal group's entry lane is still part of the intersection, so don't change
1419
1420 return true;
1421}

References current_map_, and sim_.

Referenced by addRegulatoryComponent().

Here is the caller graph for this function:

◆ splitLaneletWithPoint()

std::vector< lanelet::Lanelet > carma_wm_ctrl::WMBroadcaster::splitLaneletWithPoint ( const std::vector< lanelet::BasicPoint2d > &  input_pts,
const lanelet::Lanelet &  input_llt,
double  error_distance 
)

Split given lanelet with same proportion as the given points' downtrack relative to the lanelet. Newly created lanelet will have old regulatory elements copied into each of them. From the front and back boundaries, it is deemed not necessary to split if the ratios are within error_distance from either of it. For example, if front and back points of 3 points given are both within error_distance, only middle point will be used to split into 2 lanelets. It will return duplicate of old lanelet (with different id) if no splitting was done.

Parameters
input_ptsPoints whose downtrack ratio relative to given lanelet will be used to split the lanelet
input_lltA lanelet to split
error_distanceif within this distance (in meters) the point will be ignored
Returns
lanelets split form original one. lanelets sorted from front to back.
Exceptions
InvalidObjectStateErrorif no map is available. NOTE: this is requried to return mutable objects.

Definition at line 762 of file WMBroadcaster.cpp.

763{
764 // get ratio of this point and split
765 std::vector<lanelet::Lanelet> parallel_llts;
766 double llt_downtrack = carma_wm::geometry::trackPos(input_llt, input_llt.centerline().back().basicPoint2d()).downtrack;
767 std::vector<double> ratios;
768
769 for (auto pt : input_pts)
770 {
771 double point_downtrack = carma_wm::geometry::trackPos(input_llt, pt).downtrack;
772 double point_downtrack_ratio = point_downtrack / llt_downtrack;
773 ratios.push_back(point_downtrack_ratio);
774 }
775
776 auto new_parallel_llts = splitLaneletWithRatio(ratios, input_llt, error_distance);
777
778 parallel_llts.insert(parallel_llts.end(),new_parallel_llts.begin(), new_parallel_llts.end());
779 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "splitLaneletWithPoint returning lanelets size: " << parallel_llts.size());
780 return parallel_llts;
781}
std::vector< lanelet::Lanelet > splitLaneletWithRatio(std::vector< double > ratios, lanelet::Lanelet input_lanelet, double error_distance) const
Split given lanelet with given downtrack ratios relative to the lanelet. Newly created lanelet will h...

References carma_wm::TrackPos::downtrack, splitLaneletWithRatio(), and carma_wm::geometry::trackPos().

Referenced by preprocessWorkzoneGeometry().

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

◆ splitLaneletWithRatio()

std::vector< lanelet::Lanelet > carma_wm_ctrl::WMBroadcaster::splitLaneletWithRatio ( std::vector< double >  ratios,
lanelet::Lanelet  input_lanelet,
double  error_distance 
) const

Split given lanelet with given downtrack ratios relative to the lanelet. Newly created lanelet will have old regulatory elements copied into each of them. From the front and back boundaries, it is deemed not necessary to split if the ratios are within error_distance from either of it. For example, if front and back points of 3 ratios given are both within error_distance, only middle ratio will be used to split, so 2 lanelets will be returned. It will return duplicate of old lanelet (with different id) if no splitting was done.

Parameters
ratiosRatios relative to given lanelet will be used to split the lanelet
input_lltA lanelet to split
error_distanceif within this distance (in meters) the ratio will be ignored as it is too small
Returns
parallel_llts return lanelets split form original one. lanelets sorted from front to back.
Exceptions
InvalidObjectStateErrorif no map is available. NOTE: this is requried to return mutable objects.

Definition at line 804 of file WMBroadcaster.cpp.

805{
806 if (!current_map_ || current_map_->laneletLayer.size() == 0)
807 {
808 throw lanelet::InvalidObjectStateError(std::string("Base lanelet map is not loaded to the WMBroadcaster"));
809 }
810 if (ratios.empty())
811 {
812 throw lanelet::InvalidInputError(std::string("Ratios list is empty! Cannot split"));
813 }
814
815 std::vector<lanelet::Lanelet> created_llts;
816
817 std::sort(ratios.begin(), ratios.end());
818 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "splitLaneletWithRatio evaluating input ratios of size: " << ratios.size());
819
820 ratios.push_back(1.0); //needed to complete the loop
821
822 int left_ls_size = input_lanelet.leftBound2d().size();
823 int right_ls_size = input_lanelet.rightBound2d().size();
824
825 int left_next_pt_idx = 0;
826 int left_prev_pt_idx = 0;
827 int right_next_pt_idx = 0;
828 int right_prev_pt_idx = 0;
829 for (int i = 0 ; i < ratios.size(); i ++)
830 {
831 left_next_pt_idx = std::round(ratios[i] * (left_ls_size - 1));
832 right_next_pt_idx = std::round(ratios[i] * (right_ls_size - 1));
833 // check if edge ratios are too close to any boundaries. if so, skip
834 if (lanelet::geometry::distance2d(input_lanelet.leftBound2d().front().basicPoint2d(), input_lanelet.leftBound2d()[left_next_pt_idx].basicPoint2d()) <= error_distance)
835 {
836 // assuming both linestrings have roughly the same number of points and
837 // assuming distance between 0th and index-th points are small enough we can approximate the curve between them as a line:
838 RCLCPP_INFO_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Ratio: " << ratios[i] << ", is too close to the lanelet's front boundary! Therefore, ignoring... Allowed error_distance: " << error_distance << ", Distance: "
839 << lanelet::geometry::distance2d(input_lanelet.leftBound2d().front().basicPoint2d(), input_lanelet.leftBound2d()[left_next_pt_idx].basicPoint2d()));
840 continue;
841 }
842 if (lanelet::geometry::distance2d(input_lanelet.leftBound2d().back().basicPoint2d(), input_lanelet.leftBound2d()[left_next_pt_idx].basicPoint2d()) <= error_distance)
843 {
844 RCLCPP_INFO_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "Ratio: " << ratios[i] << ", is too close to the lanelet's back boundary! Therefore, ignoring... Allowed error_distance: " << error_distance << ", Distance: "
845 << lanelet::geometry::distance2d(input_lanelet.leftBound2d().back().basicPoint2d(), input_lanelet.leftBound2d()[left_next_pt_idx].basicPoint2d()));
846
847 left_next_pt_idx = left_ls_size - 1;
848 right_next_pt_idx = right_ls_size - 1;
849 }
850 // create lanelet
851 std::vector<lanelet::Point3d> left_pts;
852 left_pts.insert(left_pts.end(), current_map_->laneletLayer.get(input_lanelet.id()).leftBound3d().begin() + left_prev_pt_idx, current_map_->laneletLayer.get(input_lanelet.id()).leftBound3d().begin() + left_next_pt_idx + 1);
853
854 lanelet::LineString3d left_ls(lanelet::utils::getId(), left_pts, current_map_->laneletLayer.get(input_lanelet.id()).leftBound3d().attributes());
855
856 std::vector<lanelet::Point3d> right_pts;
857 right_pts.insert(right_pts.end(), current_map_->laneletLayer.get(input_lanelet.id()).rightBound3d().begin() + right_prev_pt_idx, current_map_->laneletLayer.get(input_lanelet.id()).rightBound3d().begin() + right_next_pt_idx + 1);
858
859 lanelet::LineString3d right_ls(lanelet::utils::getId(), right_pts);
860
861 lanelet::Lanelet new_llt (lanelet::utils::getId(), left_ls, right_ls, current_map_->laneletLayer.get(input_lanelet.id()).rightBound3d().attributes());
862
863 for (auto regem : current_map_->laneletLayer.get(input_lanelet.id()).regulatoryElements()) //copy existing regem into new llts
864 {
865 new_llt.addRegulatoryElement(current_map_->regulatoryElementLayer.get(regem->id()));
866 }
867
868 left_prev_pt_idx = left_next_pt_idx;
869 right_prev_pt_idx = right_next_pt_idx;
870
871 created_llts.push_back(new_llt);
872
873 // Detected the end already. Exiting now
874 if (left_prev_pt_idx == left_ls_size - 1 || right_prev_pt_idx == right_ls_size - 1)
875 {
876 break;
877 }
878
879 }
880
881 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "splitLaneletWithRatio returning lanelets size: " << created_llts.size());
882
883 return created_llts;
884}

References current_map_, and process_bag::i.

Referenced by splitLaneletWithPoint(), and splitOppositeLaneletWithPoint().

Here is the caller graph for this function:

◆ splitOppositeLaneletWithPoint()

lanelet::Lanelets carma_wm_ctrl::WMBroadcaster::splitOppositeLaneletWithPoint ( std::shared_ptr< std::vector< lanelet::Lanelet > >  opposite_llts,
const lanelet::BasicPoint2d &  input_pt,
const lanelet::Lanelet &  input_llt,
double  error_distance 
)

Split given lanelet's adjacent, OPPOSITE lanelet with same proportion as the given point's downtrack relative to the lanelet. Newly created lanelet will have old regulatory elements copied into each of them. From the front and back boundaries, it is deemed not necessary to split if the ratios are within error_distance from either of it. It will return duplicate of old lanelet (with different id) if no splitting was done.

Parameters
opposite_lltsreturn lanelets split form original one. lanelets sorted from front to back.
input_ptA point whose downtrack ratio relative to given lanelet will be used to split the lanelet
input_lltA lanelet to split
error_distanceif within this distance (in meters) the point will be ignored
Returns
return original opposite lanelet(s if overlapping)
Exceptions
InvalidObjectStateErrorif no map is available. NOTE: this is requried to return mutable objects. NOTE: Opposite lanelet doesn't have to share points with current lanelet

Definition at line 783 of file WMBroadcaster.cpp.

784{
785 // get ratio of this point and split
786 auto point_downtrack = carma_wm::geometry::trackPos(input_llt, input_pt).downtrack;
787 auto point_downtrack_ratio = point_downtrack / carma_wm::geometry::trackPos(input_llt, input_llt.centerline().back().basicPoint2d()).downtrack;
788
789 // get opposing lanelets and split
790 auto opposing_llts = carma_wm::query::nonConnectedAdjacentLeft(current_map_, input_pt);
791
792 if (opposing_llts.empty())
793 {
794 RCLCPP_ERROR_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "WMBroadcaster was not able to find opposing lane for given point in geofence related to Work Zone! Returning");
795 return {};
796 }
797
798 auto new_llts_opposite = splitLaneletWithRatio({1 - point_downtrack_ratio}, opposing_llts[0], error_distance);
799 opposite_llts->insert(opposite_llts->begin(),new_llts_opposite.begin(), new_llts_opposite.end());
800 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "splitOppositeLaneletWithPoint returning lanelets size: " << opposite_llts->size());
801 return opposing_llts;
802}
std::vector< lanelet::ConstLanelet > nonConnectedAdjacentLeft(const lanelet::LaneletMapConstPtr &semantic_map, const lanelet::BasicPoint2d &input_point, const unsigned int n=10)
Given the cartesian point on the map, tries to get the opposite direction lanelet on the left This fu...

References current_map_, carma_wm::TrackPos::downtrack, carma_wm::query::nonConnectedAdjacentLeft(), splitLaneletWithRatio(), and carma_wm::geometry::trackPos().

Referenced by createWorkzoneGeometry().

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

◆ updateUpcomingSGIntersectionIds()

void carma_wm_ctrl::WMBroadcaster::updateUpcomingSGIntersectionIds ( )

populate upcoming_intersection_ids_ from local traffic lanelet ids

Definition at line 2213 of file WMBroadcaster.cpp.

2214{
2215 uint16_t map_msg_intersection_id = 0;
2216 uint16_t cur_signal_group_id = 0;
2217 std::vector<lanelet::CarmaTrafficSignalPtr> traffic_lights;
2218 lanelet::Lanelet route_lanelet;
2219 lanelet::Ids cur_route_lanelet_ids = current_route.route_path_lanelet_ids;
2220 bool isLightFound = false;
2221
2222 for(auto id : cur_route_lanelet_ids)
2223 {
2224 route_lanelet= current_map_->laneletLayer.get(id);
2225 traffic_lights = route_lanelet.regulatoryElementsAs<lanelet::CarmaTrafficSignal>();
2226 if(!traffic_lights.empty())
2227 {
2228 isLightFound = true;
2229 break;
2230 }
2231 }
2232
2233 if(isLightFound && sim_)
2234 {
2235 for(auto itr = sim_->signal_group_to_traffic_light_id_.begin(); itr != sim_->signal_group_to_traffic_light_id_.end(); itr++)
2236 {
2237 if(itr->second == traffic_lights.front()->id())
2238 {
2239 cur_signal_group_id = itr->first;
2240 }
2241 }
2242 }
2243 else{
2244 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "NO matching Traffic lights along the route");
2245 }//END Traffic signals
2246
2247 auto intersections = route_lanelet.regulatoryElementsAs<lanelet::SignalizedIntersection>();
2248 if (intersections.empty())
2249 {
2250 // no match if any of the entry lanelet is not part of any intersection.
2251 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "NO matching intersection for current lanelet. lanelet id = " << route_lanelet.id());
2252 }
2253 else
2254 {
2255 //Currently, each lanelet has only one intersection
2256 lanelet::Id intersection_id = intersections.front()->id();
2257 if(intersection_id != lanelet::InvalId)
2258 {
2259 for(auto itr = sim_->intersection_id_to_regem_id_.begin(); itr != sim_->intersection_id_to_regem_id_.end(); itr++)
2260 {
2261 if(itr->second == intersection_id)
2262 {
2263 map_msg_intersection_id = itr->first;
2264 }
2265 }
2266 }
2267 } //END intersections
2268
2269 RCLCPP_DEBUG_STREAM(rclcpp::get_logger("carma_wm_ctrl"), "MAP msg: Intersection ID = " << map_msg_intersection_id << ", Signal Group ID =" << cur_signal_group_id );
2270 if(map_msg_intersection_id != 0 && cur_signal_group_id != 0)
2271 {
2272 upcoming_intersection_ids_.data.clear();
2273 upcoming_intersection_ids_.data.push_back(static_cast<int>(map_msg_intersection_id));
2274 upcoming_intersection_ids_.data.push_back(static_cast<int>(cur_signal_group_id));
2275 }
2276}

References current_map_, current_route, sim_, and upcoming_intersection_ids_.

Member Data Documentation

◆ ack_pub_times_

int carma_wm_ctrl::WMBroadcaster::ack_pub_times_ = 1
private

Definition at line 474 of file WMBroadcaster.hpp.

Referenced by pubTCMACK(), and setConfigACKPubTimes().

◆ active_geofence_llt_ids_

std::unordered_set<lanelet::Id> carma_wm_ctrl::WMBroadcaster::active_geofence_llt_ids_
private

◆ active_pub_

PublishActiveGeofCallback carma_wm_ctrl::WMBroadcaster::active_pub_
private

Definition at line 444 of file WMBroadcaster.hpp.

Referenced by currentLocationCallback().

◆ base_map_

lanelet::LaneletMapPtr carma_wm_ctrl::WMBroadcaster::base_map_
private

Definition at line 432 of file WMBroadcaster.hpp.

Referenced by baseMapCallback().

◆ base_map_georef_

std::string carma_wm_ctrl::WMBroadcaster::base_map_georef_
private

◆ cached_maps_

std::vector<lanelet::LaneletMapPtr> carma_wm_ctrl::WMBroadcaster::cached_maps_
private

Definition at line 439 of file WMBroadcaster.hpp.

◆ checked_geofence_ids_

std::unordered_set<std::string> carma_wm_ctrl::WMBroadcaster::checked_geofence_ids_
private

Definition at line 437 of file WMBroadcaster.hpp.

Referenced by geofenceCallback().

◆ config_limit

lanelet::Velocity carma_wm_ctrl::WMBroadcaster::config_limit
private

Definition at line 435 of file WMBroadcaster.hpp.

Referenced by baseMapCallback(), geofenceFromMsg(), and setConfigSpeedLimit().

◆ control_msg_pub_

PublishCtrlRequestCallback carma_wm_ctrl::WMBroadcaster::control_msg_pub_
private

Definition at line 443 of file WMBroadcaster.hpp.

Referenced by routeCallbackMessage().

◆ current_map_

◆ current_map_version_

size_t carma_wm_ctrl::WMBroadcaster::current_map_version_ = 0
private

Definition at line 455 of file WMBroadcaster.hpp.

Referenced by addGeofence(), baseMapCallback(), and removeGeofence().

◆ current_route

carma_planning_msgs::msg::Route carma_wm_ctrl::WMBroadcaster::current_route
private

◆ current_routing_graph_

lanelet::routing::RoutingGraphPtr carma_wm_ctrl::WMBroadcaster::current_routing_graph_
private

◆ error_distance_

double carma_wm_ctrl::WMBroadcaster::error_distance_ = 5
private

◆ generated_geofence_reqids_

std::unordered_set<std::string> carma_wm_ctrl::WMBroadcaster::generated_geofence_reqids_
private

Definition at line 438 of file WMBroadcaster.hpp.

Referenced by controlRequestFromRoute(), and geofenceCallback().

◆ geofence_ack_strategy_

const std::string carma_wm_ctrl::WMBroadcaster::geofence_ack_strategy_ = "carma3/Geofence_Acknowledgement"
private

Definition at line 473 of file WMBroadcaster.hpp.

Referenced by pubTCMACK().

◆ j2735_map_msg_marker_array_

visualization_msgs::msg::MarkerArray carma_wm_ctrl::WMBroadcaster::j2735_map_msg_marker_array_

Definition at line 408 of file WMBroadcaster.hpp.

Referenced by addGeofence().

◆ map_mutex_

std::mutex carma_wm_ctrl::WMBroadcaster::map_mutex_
private

◆ map_pub_

PublishMapCallback carma_wm_ctrl::WMBroadcaster::map_pub_
private

Definition at line 441 of file WMBroadcaster.hpp.

Referenced by baseMapCallback().

◆ map_update_message_queue_

std::vector<autoware_lanelet2_msgs::msg::MapBin> carma_wm_ctrl::WMBroadcaster::map_update_message_queue_
private

Queue which stores the map updates applied to the current map version as a sequence of diffs This queue is implemented as a vector because it gets reused by each new subscriber connection NOTE: This queue should be cleared each time the current_map_version changes

Definition at line 463 of file WMBroadcaster.hpp.

◆ map_update_pub_

PublishMapUpdateCallback carma_wm_ctrl::WMBroadcaster::map_update_pub_
private

Definition at line 442 of file WMBroadcaster.hpp.

Referenced by addGeofence(), and removeGeofence().

◆ max_lane_width_

double carma_wm_ctrl::WMBroadcaster::max_lane_width_
private

Definition at line 448 of file WMBroadcaster.hpp.

Referenced by getAffectedLaneletOrAreas(), and setMaxLaneWidth().

◆ participant_

std::string carma_wm_ctrl::WMBroadcaster::participant_ = lanelet::Participants::VehicleCar
private

◆ route_path_

lanelet::ConstLanelets carma_wm_ctrl::WMBroadcaster::route_path_
private

Definition at line 414 of file WMBroadcaster.hpp.

Referenced by controlRequestFromRoute(), and distToNearestActiveGeofence().

◆ scheduler_

GeofenceScheduler carma_wm_ctrl::WMBroadcaster::scheduler_
private

◆ sim_

◆ tcm_ack_pub_

PublishMobilityOperationCallback carma_wm_ctrl::WMBroadcaster::tcm_ack_pub_
private

Definition at line 446 of file WMBroadcaster.hpp.

Referenced by pubTCMACK().

◆ tcm_marker_array_

visualization_msgs::msg::MarkerArray carma_wm_ctrl::WMBroadcaster::tcm_marker_array_

Definition at line 407 of file WMBroadcaster.hpp.

Referenced by addGeofence().

◆ tcr_polygon_

carma_v2x_msgs::msg::TrafficControlRequestPolygon carma_wm_ctrl::WMBroadcaster::tcr_polygon_

Definition at line 409 of file WMBroadcaster.hpp.

Referenced by controlRequestFromRoute().

◆ traffic_light_id_lookup_

std::unordered_map<uint32_t, lanelet::Id> carma_wm_ctrl::WMBroadcaster::traffic_light_id_lookup_
private

◆ upcoming_intersection_ids_

std_msgs::msg::Int32MultiArray carma_wm_ctrl::WMBroadcaster::upcoming_intersection_ids_

Definition at line 410 of file WMBroadcaster.hpp.

Referenced by publishLightId(), and updateUpcomingSGIntersectionIds().

◆ update_count_

size_t carma_wm_ctrl::WMBroadcaster::update_count_ = -1
private

Definition at line 465 of file WMBroadcaster.hpp.

Referenced by addGeofence(), and removeGeofence().

◆ vehicle_id_

std::string carma_wm_ctrl::WMBroadcaster::vehicle_id_
private

Definition at line 475 of file WMBroadcaster.hpp.

Referenced by pubTCMACK(), and setConfigVehicleId().

◆ work_zone_geofence_cache_

std::unordered_map<uint8_t, std::shared_ptr<Geofence> > carma_wm_ctrl::WMBroadcaster::work_zone_geofence_cache_
private

Definition at line 416 of file WMBroadcaster.hpp.

Referenced by addGeofence(), geofenceFromMsg(), and scheduleGeofence().

◆ workzone_geometry_published_

bool carma_wm_ctrl::WMBroadcaster::workzone_geometry_published_ = false
private

Definition at line 450 of file WMBroadcaster.hpp.

◆ workzone_remaining_msgs_

std::vector<carma_v2x_msgs::msg::TrafficControlMessageV01> carma_wm_ctrl::WMBroadcaster::workzone_remaining_msgs_
private

Definition at line 449 of file WMBroadcaster.hpp.


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