forked from anrp-tri/ros-bridge
-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add ego traffic light sensor #2
Open
hieu-nguyen-tri
wants to merge
11
commits into
windows
Choose a base branch
from
hieu/add-ego-traffic-light-sensor
base: windows
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
4265f43
Update new submodule for carla_msgs
hieu-nguyen-tri a42f5b7
Add ego traffic light sensor
hieu-nguyen-tri 396422d
Add comments and clean up
hieu-nguyen-tri 5324140
Keep traffic light id when inside intersection
hieu-nguyen-tri c65090d
Sort import specify args in constructor
hieu-nguyen-tri b2ff295
Update submodule to use TRI repo
hieu-nguyen-tri 58cf82a
Update submodule to use TRI repo
hieu-nguyen-tri f6df80a
Merge branch 'hieu/add-ego-traffic-light-sensor' of https://github.co…
hieu-nguyen-tri 69ba3fe
Update submodule to use TRI repo
hieu-nguyen-tri 3e97711
Address feedback
hieu-nguyen-tri 7d4f10e
Merge branch 'hieu/add-ego-traffic-light-sensor' of github.com:Toyota…
hieu-nguyen-tri File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
[submodule "carla_msgs"] | ||
path = carla_msgs | ||
url = https://github.com/carla-simulator/ros-carla-msgs | ||
url = https://github.com/ToyotaResearchInstitute/ros-carla-msgs.git | ||
branch = master |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
185 changes: 185 additions & 0 deletions
185
carla_ros_bridge/src/carla_ros_bridge/ego_traffic_light_sensor.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
#!/usr/bin/env python | ||
|
||
from collections import defaultdict | ||
from typing import Dict | ||
|
||
import carla | ||
from carla_msgs.msg import CarlaEgoTrafficLightInfo, CarlaTrafficLightStatus | ||
from ros_compatibility.node import CompatibleNode | ||
from ros_compatibility.qos import DurabilityPolicy, QoSProfile | ||
|
||
from carla_ros_bridge.actor import Actor | ||
from carla_ros_bridge.ego_vehicle import EgoVehicle | ||
from carla_ros_bridge.pseudo_actor import PseudoActor | ||
from carla_ros_bridge.traffic import TrafficLight | ||
|
||
# Publish info no less frequently than this. | ||
_PUBLISH_INTERVAL_SECONDS = 5.0 | ||
|
||
class StoplineInfo: | ||
"""An util class to keep track of a traffic light and its stop location.""" | ||
|
||
def __init__(self, traffic_light_actor: TrafficLight, approaching_road_id: int, approaching_lane_id: int, stop_location: carla.Location): | ||
self.traffic_light_actor = traffic_light_actor | ||
self.stop_location = stop_location | ||
self.approaching_road_id = approaching_road_id | ||
self.approaching_lane_id = approaching_lane_id | ||
|
||
|
||
def create_stop_line_info_map(node: CompatibleNode, actor_list: Dict[int, Actor], search_distance: float = 2.0): | ||
""" Return a dictionary of stop line info. | ||
|
||
Args: | ||
node: ROS node wrapper for logging purposes | ||
actor_list: A dictionary of spawn actors. Use this to search for traffic light actors | ||
search_distance: the approximate distance where to get the previous waypoints | ||
Return: | ||
A map(road_id, lane_id -> StopLineInfo) | ||
""" | ||
results = defaultdict(dict) | ||
for actor in actor_list.values(): | ||
if not isinstance(actor, TrafficLight): | ||
continue | ||
|
||
carla_actor = actor.carla_actor | ||
affected_points = carla_actor.get_affected_lane_waypoints() | ||
if len(affected_points) == 0: | ||
node.logerr(f"Unable to find any affected points for traffic light id: {carla_actor.id}") | ||
continue | ||
|
||
for point in affected_points: | ||
if not point.is_junction: | ||
node.logerr( | ||
f"The affected waypoint from traffic light is not in a junction. Traffic_light_id: {carla_actor.id}, road_id: {point.road_id}, lane_id: {point.lane_id}" | ||
) | ||
continue | ||
|
||
# Go to previous waypoints to get the road id and lane id on which the ego will be approaching. | ||
previous_points = point.previous(search_distance) | ||
if len(previous_points) == 0: | ||
node.logerr( | ||
f"Unable to find previous points for traffic_light_id: {carla_actor.id}, road_id: {point.road_id}, lane_id: {point.lane_id}" | ||
) | ||
continue | ||
|
||
# This stop location is roughly at the stop line. It depends on how the map is created in RoadRunner. | ||
stop_location = point.transform.location | ||
p = previous_points[0] | ||
results[p.road_id][p.lane_id] = StoplineInfo(actor, p.road_id, p.lane_id, stop_location) | ||
|
||
return results | ||
|
||
|
||
def get_stop_line_info(stop_line_info: Dict[int,Dict[int, StoplineInfo]], road_id: int, lane_id: int): | ||
"""Given road_id and lane_id, find and return a StopLineInfo object if any.""" | ||
if stop_line_info is None: | ||
return None | ||
if road_id in stop_line_info and lane_id in stop_line_info[road_id]: | ||
return stop_line_info[road_id][lane_id] | ||
return None | ||
|
||
|
||
class EgoTrafficLightSensor(PseudoActor): | ||
"""A sensor to publish CarlaEgoTrafficLightInfo""" | ||
|
||
def __init__(self, uid: int, name: str, parent: Actor, node: CompatibleNode, actor_list: Dict[int, Actor], carla_map: carla.Map): | ||
super(EgoTrafficLightSensor, self).__init__(uid=uid, name=name, parent=parent, node=node) | ||
|
||
self.pub = node.new_publisher( | ||
CarlaEgoTrafficLightInfo, | ||
self.get_topic_prefix() + "/info", | ||
qos_profile=QoSProfile(depth=10, durability=DurabilityPolicy.TRANSIENT_LOCAL), | ||
) | ||
|
||
self._info_published_at = None | ||
|
||
self.map = carla_map | ||
self.map_name = self.map.name | ||
self.ego = None | ||
self.actor_list = actor_list | ||
self.stop_line_info_map = None | ||
self.cur_sli = None | ||
|
||
self.msg = CarlaEgoTrafficLightInfo() | ||
self.msg.traffic_light_status = CarlaTrafficLightStatus() | ||
self.msg.inside_intersection = False | ||
|
||
def destroy(self): | ||
super(EgoTrafficLightSensor, self).destroy() | ||
self.node.destroy_publisher(self.pub) | ||
self.actor_list = None | ||
self.stop_line_info_map = None | ||
self.node.loginfo("Destroy EgoTrafficLightSensor") | ||
|
||
@staticmethod | ||
def get_blueprint_name(): | ||
return "sensor.pseudo.ego_traffic_light" | ||
|
||
def update(self, frame: int, timestamp: float): | ||
try: | ||
if self.map is None: | ||
self.node.logwarn("Carla Map is not assgined") | ||
return | ||
|
||
if self.stop_line_info_map is None or self.map_name != self.map.name: | ||
self.stop_line_info_map = create_stop_line_info_map(self.node, self.actor_list) | ||
andrewbest-tri marked this conversation as resolved.
Show resolved
Hide resolved
|
||
self.map_name = self.map.name | ||
self.ego = self.get_ego(self.actor_list) | ||
|
||
if self.ego is None: | ||
self.node.logwarn("Unable to find ego.") | ||
return | ||
|
||
ego_location = self.ego.get_location() | ||
wp = self.map.get_waypoint(ego_location) | ||
sli = get_stop_line_info(self.stop_line_info_map, wp.road_id, wp.lane_id) | ||
|
||
inside_intersection = wp.is_junction | ||
cur_status = self.cur_sli.traffic_light_actor.get_status() if self.cur_sli else None | ||
new_status = sli.traffic_light_actor.get_status() if sli else None | ||
|
||
if (self.msg.inside_intersection != inside_intersection | ||
or self.has_traffic_light_status_changes(cur_status, new_status) | ||
or self._info_published_at is None | ||
or timestamp - self._info_published_at > _PUBLISH_INTERVAL_SECONDS | ||
): | ||
self.calculate_and_publish_data(ego_location, sli, inside_intersection, timestamp) | ||
self._info_published_at = timestamp | ||
except Exception as e: | ||
self.node.loginfo("Error: {}".format(e)) | ||
|
||
def calculate_and_publish_data(self, ego_location: carla.Location, new_sli: StoplineInfo, inside_intersection: bool, timestamp: float): | ||
"""Publish CarlaEgoTrafficLightInfo message.""" | ||
if inside_intersection: | ||
self.msg.distance_to_stopline = -1.0 | ||
if self.cur_sli: | ||
self.msg.traffic_light_status = self.cur_sli.traffic_light_actor.get_status() | ||
else: | ||
if new_sli: | ||
self.msg.distance_to_stopline = ego_location.distance(new_sli.stop_location) | ||
self.msg.traffic_light_status = new_sli.traffic_light_actor.get_status() | ||
else: | ||
self.msg.distance_to_stopline = -1.0 | ||
self.msg.traffic_light_status = CarlaTrafficLightStatus() | ||
# Store new StopLineInfo. | ||
self.cur_sli = new_sli | ||
|
||
self.msg.ego_id = self.ego.id | ||
self.msg.inside_intersection = inside_intersection | ||
self.msg.header = self.get_msg_header(timestamp=timestamp) | ||
self.pub.publish(self.msg) | ||
andrewbest-tri marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
def get_ego(self, actor_list: Dict[int, Actor]): | ||
"""Return a CarlaActor representing the ego car.""" | ||
for actor in actor_list.values(): | ||
if isinstance(actor, EgoVehicle): | ||
return actor.carla_actor | ||
return None | ||
|
||
def has_traffic_light_status_changes(self, cur_traffic_light_status: CarlaTrafficLightStatus, new_traffic_light_status: CarlaTrafficLightStatus): | ||
"""Return True if the traffic light status (either traffic light id or state) has been changed; False otherwise.""" | ||
if cur_traffic_light_status is None or new_traffic_light_status is None: | ||
# Don't detect changes when one of the status is None. | ||
return False | ||
|
||
return cur_traffic_light_status != new_traffic_light_status |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since get map is expensive, does it cause lag here? Can we pre-load the map?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you arent seeing lag we can consider not doing that, just curious.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't notice any lag. Now when reading Carla APi documentation, it looks like this call to get the current XODR map. I think if we store it, then when a new map is loaded, I don't think the reference will have latest map. I will check it.
self. map = world.get_map() /// When a map is changed, self.map will have the old value
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I expected, when call
world.load_world()
, the oldself.map
is not updated. We need to call self.world.get_map() again.That means the logic I have for checking if the map has been changed won't work.
Is there a way to detect map change? I tried listen to this topic:
/carla/world_info
but I don't see it outputs anything when the map changes either.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
get map is really expensive but get map name i think is cheap. Call that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need the map object to get the waypoints that ego is currently on. Get map is called once at beginning or when a new map is loaded so I think it should be fine.
Regarding detecting if the map has changed. It will require passing the CarlaClient to ActorFactory, then pass it to whatever sensors we want to detect the map change. The reason are:
I think if we want to load a new map maybe it's better to rerun the motion sim with the new map at this point. That will make sure the rosbag correct and clean; especially, other sensors in carla rosbridge don't detect loading a new map.
Maybe hold off on merging this and let's try it on the motion platform to see if there are any delay then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fyi @anrp-tri