From 97c2f9323e8de397620f11176c284d65117b1e60 Mon Sep 17 00:00:00 2001 From: Changhe Date: Wed, 27 Jul 2022 19:57:56 +0000 Subject: [PATCH 01/12] added trip dataclass for single vehicle in types.py. added writetripxml method in generators.py. --- smarts/sstudio/generators.py | 33 +++++++++++++++++++++++++++++++++ smarts/sstudio/types.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/smarts/sstudio/generators.py b/smarts/sstudio/generators.py index 35a62be783..132056c56e 100644 --- a/smarts/sstudio/generators.py +++ b/smarts/sstudio/generators.py @@ -368,3 +368,36 @@ def _resolve_log_dir(self, log_dir): log_dir = make_dir_in_smarts_log_dir("_duarouter_routing") return os.path.abspath(log_dir) + + + def tripwritexml(resolve_route, traffic, doc): + """Wrtes a traffic spec into a route file. Typically this would be the source + data to Sumo's DUAROUTER. + """ + # Make sure all routes are "resolved" (e.g. `RandomRoute` are converted to + # `Route`) so that we can write them all to file. + resolved_routes = {} + for route in {trip.route for trip in traffic.trips}: + resolved_routes[route] = resolve_route(route) + + for route in set(resolved_routes.values()): + doc.stag("route", id=route.id + "trip", edges=" ".join(route.roads)) + + # We don't de-dup flows since defining the same flow multiple times should + # create multiple traffic flows. Since IDs can't be reused, we also unique + # them here. + for trip_idx, trip in enumerate(traffic.trips): + route = resolved_routes[trip.route] + actor = trip.actor + doc.stag( + "vehicle", + id="{}".format(trip.vehicle_name), + type=actor.id, + route=route.id, + depart=trip.depart, + departLane=route.begin[1], + departPos=route.begin[2], + departSpeed=actor.depart_speed, + arrivalLane=route.end[1], + arrivalPos=route.end[2], + ) \ No newline at end of file diff --git a/smarts/sstudio/types.py b/smarts/sstudio/types.py index bbc444fe5d..e5f35d41b2 100644 --- a/smarts/sstudio/types.py +++ b/smarts/sstudio/types.py @@ -501,6 +501,37 @@ def __hash__(self): def __eq__(self, other): return self.__class__ == other.__class__ and hash(self) == hash(other) +@dataclass(frozen=True) +class Trip: + """A route with a single actor type with name and unique id.""" + def __init__( + self, + vehicle_name: str, + actor: TrafficActor, + route: Union[RandomRoute, Route], + depart: float = 0, + ): + self.vehicle_name = vehicle_name + self.actor = TrafficActor(self.vehicle_name) + self.route = route + """The route for the actor to attempt to follow.""" + self.depart = depart + """Start time in seconds.""" + # XXX: Defaults to 1 hour of traffic. We may want to change this to be "continual + # traffic", effectively an infinite end. + + @property + def id(self) -> str: + """The unique id of this trip.""" + return self.vehicle_name + + def __hash__(self): + # Custom hash since self.actors is not hashable, here we first convert to a + # frozenset. + return _pickle_hash((self.route, self.actor)) + + def __eq__(self, other): + return self.__class__ == other.__class__ and hash(self) == hash(other) @dataclass(frozen=True) class JunctionEdgeIDResolver: @@ -547,6 +578,8 @@ class Traffic: """Flows are used to define a steady supply of vehicles.""" # TODO: consider moving TrafficHistory stuff in here (and rename to Trajectory) # TODO: - treat history points like Vias (no guarantee on history timesteps anyway) + trips: Sequence[Trip] + """Trips are used to define a series of single vehicle trip.""" engine: str = "SUMO" """The traffic-generation engine to use. Supported values include: SUMO, SMARTS. SUMO requires using a SumoRoadNetwork for the RoadMap.""" From 698e6cc5b10e1e5c6f39f3b7fb32036574bd0b4a Mon Sep 17 00:00:00 2001 From: Changhe Date: Wed, 27 Jul 2022 20:26:50 +0000 Subject: [PATCH 02/12] fixed minor error for generators.py --- smarts/sstudio/generators.py | 64 +++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/smarts/sstudio/generators.py b/smarts/sstudio/generators.py index 132056c56e..9c6b2ecacc 100644 --- a/smarts/sstudio/generators.py +++ b/smarts/sstudio/generators.py @@ -281,6 +281,8 @@ def _writexml( end=flow.end, **rate_option, ) + # write trip into xml format + tripwritexml(self.resolve_route, traffic, doc) with open(route_path, "w") as f: f.write( @@ -370,34 +372,34 @@ def _resolve_log_dir(self, log_dir): return os.path.abspath(log_dir) - def tripwritexml(resolve_route, traffic, doc): - """Wrtes a traffic spec into a route file. Typically this would be the source - data to Sumo's DUAROUTER. - """ - # Make sure all routes are "resolved" (e.g. `RandomRoute` are converted to - # `Route`) so that we can write them all to file. - resolved_routes = {} - for route in {trip.route for trip in traffic.trips}: - resolved_routes[route] = resolve_route(route) - - for route in set(resolved_routes.values()): - doc.stag("route", id=route.id + "trip", edges=" ".join(route.roads)) - - # We don't de-dup flows since defining the same flow multiple times should - # create multiple traffic flows. Since IDs can't be reused, we also unique - # them here. - for trip_idx, trip in enumerate(traffic.trips): - route = resolved_routes[trip.route] - actor = trip.actor - doc.stag( - "vehicle", - id="{}".format(trip.vehicle_name), - type=actor.id, - route=route.id, - depart=trip.depart, - departLane=route.begin[1], - departPos=route.begin[2], - departSpeed=actor.depart_speed, - arrivalLane=route.end[1], - arrivalPos=route.end[2], - ) \ No newline at end of file +def tripwritexml(resolve_route, traffic, doc): + """Wrtes a traffic spec into a route file. Typically this would be the source + data to Sumo's DUAROUTER. + """ + # Make sure all routes are "resolved" (e.g. `RandomRoute` are converted to + # `Route`) so that we can write them all to file. + resolved_routes = {} + for route in {trip.route for trip in traffic.trips}: + resolved_routes[route] = resolve_route(route) + + for route in set(resolved_routes.values()): + doc.stag("route", id=route.id + "trip", edges=" ".join(route.roads)) + + # We don't de-dup flows since defining the same flow multiple times should + # create multiple traffic flows. Since IDs can't be reused, we also unique + # them here. + for trip_idx, trip in enumerate(traffic.trips): + route = resolved_routes[trip.route] + actor = trip.actor + doc.stag( + "vehicle", + id="{}".format(trip.vehicle_name), + type=actor.id, + route=route.id, + depart=trip.depart, + departLane=route.begin[1], + departPos=route.begin[2], + departSpeed=actor.depart_speed, + arrivalLane=route.end[1], + arrivalPos=route.end[2], + ) \ No newline at end of file From b443f00cd41dbb956379ecb217cd43a59ceb4242 Mon Sep 17 00:00:00 2001 From: Changhe Date: Wed, 27 Jul 2022 20:38:05 +0000 Subject: [PATCH 03/12] making traffic.trips optional. --- smarts/sstudio/generators.py | 3 ++- smarts/sstudio/types.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/smarts/sstudio/generators.py b/smarts/sstudio/generators.py index 9c6b2ecacc..f3301ebf65 100644 --- a/smarts/sstudio/generators.py +++ b/smarts/sstudio/generators.py @@ -282,7 +282,8 @@ def _writexml( **rate_option, ) # write trip into xml format - tripwritexml(self.resolve_route, traffic, doc) + if traffic.trips is not None: + tripwritexml(self.resolve_route, traffic, doc) with open(route_path, "w") as f: f.write( diff --git a/smarts/sstudio/types.py b/smarts/sstudio/types.py index e5f35d41b2..30d8122702 100644 --- a/smarts/sstudio/types.py +++ b/smarts/sstudio/types.py @@ -578,7 +578,7 @@ class Traffic: """Flows are used to define a steady supply of vehicles.""" # TODO: consider moving TrafficHistory stuff in here (and rename to Trajectory) # TODO: - treat history points like Vias (no guarantee on history timesteps anyway) - trips: Sequence[Trip] + trips: Sequence[Trip] = None """Trips are used to define a series of single vehicle trip.""" engine: str = "SUMO" """The traffic-generation engine to use. Supported values include: SUMO, SMARTS. SUMO requires using a SumoRoadNetwork for the RoadMap.""" From 987b0b78523a3827d209e11c3ac4746019a5b516 Mon Sep 17 00:00:00 2001 From: Changhe Date: Wed, 27 Jul 2022 20:47:11 +0000 Subject: [PATCH 04/12] fixed minor error in types.py. --- smarts/sstudio/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smarts/sstudio/types.py b/smarts/sstudio/types.py index 30d8122702..c646710e92 100644 --- a/smarts/sstudio/types.py +++ b/smarts/sstudio/types.py @@ -578,7 +578,7 @@ class Traffic: """Flows are used to define a steady supply of vehicles.""" # TODO: consider moving TrafficHistory stuff in here (and rename to Trajectory) # TODO: - treat history points like Vias (no guarantee on history timesteps anyway) - trips: Sequence[Trip] = None + trips: Optional[Sequence[Trip]] = None """Trips are used to define a series of single vehicle trip.""" engine: str = "SUMO" """The traffic-generation engine to use. Supported values include: SUMO, SMARTS. SUMO requires using a SumoRoadNetwork for the RoadMap.""" From 95dcf32566f36723ebf1784ded95123051a7166d Mon Sep 17 00:00:00 2001 From: AisenGinn Date: Tue, 15 Nov 2022 09:46:27 -0500 Subject: [PATCH 05/12] modified changelog. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa722a4cff..d2d8d43374 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ Copy and pasting the git commit messages is __NOT__ enough. - Added ego's mission details into the `FormatObs` wrapper. - Added `SmartsLaneChangingModel` and `SmartsJunctionModel` to types available for use with the new smarts traffic engine within Scenario Studio. - Added option to `AgentInterface` to include traffic signals (lights) in `EgoVehicleObservation` objects. +- Added single vehicle `Trip` into type. ### Deprecated - Deprecated a few things related to traffic in the `Scenario` class, including the `route` argument to the `Scenario` initializer, the `route`, `route_filepath` and `route_files_enabled` properties, and the `discover_routes()` static method. In general, the notion of "route" (singular) here is being replaced with "`traffic_specs`" (plural) that allow for specifying traffic controlled by the SMARTS engine as well as Sumo. From 5eaf0e8f4a5a72f95b4711a4a04c14c998915187 Mon Sep 17 00:00:00 2001 From: AisenGinn Date: Tue, 15 Nov 2022 15:06:55 -0500 Subject: [PATCH 06/12] modified name of tripwritexml to be write_trip_xml and incorporated it into class method of TrafficGenerator. --- smarts/sstudio/generators.py | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/smarts/sstudio/generators.py b/smarts/sstudio/generators.py index f3301ebf65..e45dd48b51 100644 --- a/smarts/sstudio/generators.py +++ b/smarts/sstudio/generators.py @@ -283,7 +283,7 @@ def _writexml( ) # write trip into xml format if traffic.trips is not None: - tripwritexml(self.resolve_route, traffic, doc) + self.write_trip_xml(traffic, doc) with open(route_path, "w") as f: f.write( @@ -292,6 +292,38 @@ def _writexml( ) ) + def write_trip_xml(self, traffic, doc): + """Wrtes a traffic spec into a route file. Typically this would be the source + data to Sumo's DUAROUTER. + """ + # Make sure all routes are "resolved" (e.g. `RandomRoute` are converted to + # `Route`) so that we can write them all to file. + resolved_routes = {} + for route in {trip.route for trip in traffic.trips}: + resolved_routes[route] = self.resolve_route(route) + + for route in set(resolved_routes.values()): + doc.stag("route", id=route.id + "trip", edges=" ".join(route.roads)) + + # We don't de-dup flows since defining the same flow multiple times should + # create multiple traffic flows. Since IDs can't be reused, we also unique + # them here. + for trip_idx, trip in enumerate(traffic.trips): + route = resolved_routes[trip.route] + actor = trip.actor + doc.stag( + "vehicle", + id="{}".format(trip.vehicle_name), + type=actor.id, + route=route.id, + depart=trip.depart, + departLane=route.begin[1], + departPos=route.begin[2], + departSpeed=actor.depart_speed, + arrivalLane=route.end[1], + arrivalPos=route.end[2], + ) + def _cache_road_network(self): if not self._road_network: from smarts.core.sumo_road_network import SumoRoadNetwork @@ -403,4 +435,4 @@ def tripwritexml(resolve_route, traffic, doc): departSpeed=actor.depart_speed, arrivalLane=route.end[1], arrivalPos=route.end[2], - ) \ No newline at end of file + ) From fa12cc4289aeb83cf2c7234896c36058f0a9c7cb Mon Sep 17 00:00:00 2001 From: AisenGinn Date: Tue, 15 Nov 2022 15:07:31 -0500 Subject: [PATCH 07/12] formatting. --- smarts/sstudio/generators.py | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/smarts/sstudio/generators.py b/smarts/sstudio/generators.py index e45dd48b51..143292570d 100644 --- a/smarts/sstudio/generators.py +++ b/smarts/sstudio/generators.py @@ -403,36 +403,3 @@ def _resolve_log_dir(self, log_dir): log_dir = make_dir_in_smarts_log_dir("_duarouter_routing") return os.path.abspath(log_dir) - - -def tripwritexml(resolve_route, traffic, doc): - """Wrtes a traffic spec into a route file. Typically this would be the source - data to Sumo's DUAROUTER. - """ - # Make sure all routes are "resolved" (e.g. `RandomRoute` are converted to - # `Route`) so that we can write them all to file. - resolved_routes = {} - for route in {trip.route for trip in traffic.trips}: - resolved_routes[route] = resolve_route(route) - - for route in set(resolved_routes.values()): - doc.stag("route", id=route.id + "trip", edges=" ".join(route.roads)) - - # We don't de-dup flows since defining the same flow multiple times should - # create multiple traffic flows. Since IDs can't be reused, we also unique - # them here. - for trip_idx, trip in enumerate(traffic.trips): - route = resolved_routes[trip.route] - actor = trip.actor - doc.stag( - "vehicle", - id="{}".format(trip.vehicle_name), - type=actor.id, - route=route.id, - depart=trip.depart, - departLane=route.begin[1], - departPos=route.begin[2], - departSpeed=actor.depart_speed, - arrivalLane=route.end[1], - arrivalPos=route.end[2], - ) From 31aa52b63203d1d4fb2106787f63010623713847 Mon Sep 17 00:00:00 2001 From: AisenGinn Date: Tue, 15 Nov 2022 15:40:16 -0500 Subject: [PATCH 08/12] added fill_in_gaps argument to resolve_route. --- smarts/sstudio/generators.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/smarts/sstudio/generators.py b/smarts/sstudio/generators.py index 143292570d..985717dea9 100644 --- a/smarts/sstudio/generators.py +++ b/smarts/sstudio/generators.py @@ -283,7 +283,7 @@ def _writexml( ) # write trip into xml format if traffic.trips is not None: - self.write_trip_xml(traffic, doc) + self.write_trip_xml(traffic, doc, fill_in_route_gaps) with open(route_path, "w") as f: f.write( @@ -292,7 +292,7 @@ def _writexml( ) ) - def write_trip_xml(self, traffic, doc): + def write_trip_xml(self, traffic, doc, fill_in_gaps): """Wrtes a traffic spec into a route file. Typically this would be the source data to Sumo's DUAROUTER. """ @@ -300,7 +300,7 @@ def write_trip_xml(self, traffic, doc): # `Route`) so that we can write them all to file. resolved_routes = {} for route in {trip.route for trip in traffic.trips}: - resolved_routes[route] = self.resolve_route(route) + resolved_routes[route] = self.resolve_route(route, fill_in_gaps) for route in set(resolved_routes.values()): doc.stag("route", id=route.id + "trip", edges=" ".join(route.roads)) From 15b8b52db8987f03947b419859c0f6a765bafd61 Mon Sep 17 00:00:00 2001 From: AisenGinn Date: Wed, 16 Nov 2022 14:23:46 -0500 Subject: [PATCH 09/12] modified trip to be standard frozen dataclass. Added vehicle_type option for trip. Added Vtype in writexml for trip. --- smarts/sstudio/generators.py | 21 ++++++++++++++++++--- smarts/sstudio/types.py | 35 ++++++++++++++++++++--------------- 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/smarts/sstudio/generators.py b/smarts/sstudio/generators.py index 985717dea9..e531144ab8 100644 --- a/smarts/sstudio/generators.py +++ b/smarts/sstudio/generators.py @@ -236,6 +236,21 @@ def _writexml( **actor.junction_model, ) + if traffic.trips is not None: + for actor in {trip.actor for trip in traffic.trips}: + doc.stag( + "vType", + id=actor.id, + accel=actor.accel, + decel=actor.decel, + vClass=actor.vehicle_type, + speedFactor=actor.speed.mean, + speedDev=actor.speed.sigma, + maxSpeed=actor.max_speed, + **actor.lane_changing_model, + **actor.junction_model, + ) + # Make sure all routes are "resolved" (e.g. `RandomRoute` are converted to # `Route`) so that we can write them all to file. resolved_routes = {} @@ -281,9 +296,9 @@ def _writexml( end=flow.end, **rate_option, ) - # write trip into xml format - if traffic.trips is not None: - self.write_trip_xml(traffic, doc, fill_in_route_gaps) + # write trip into xml format + if traffic.trips is not None: + self.write_trip_xml(traffic, doc, fill_in_route_gaps) with open(route_path, "w") as f: f.write( diff --git a/smarts/sstudio/types.py b/smarts/sstudio/types.py index c646710e92..deb253522c 100644 --- a/smarts/sstudio/types.py +++ b/smarts/sstudio/types.py @@ -501,24 +501,28 @@ def __hash__(self): def __eq__(self, other): return self.__class__ == other.__class__ and hash(self) == hash(other) + @dataclass(frozen=True) class Trip: """A route with a single actor type with name and unique id.""" - def __init__( - self, - vehicle_name: str, - actor: TrafficActor, - route: Union[RandomRoute, Route], - depart: float = 0, - ): - self.vehicle_name = vehicle_name - self.actor = TrafficActor(self.vehicle_name) - self.route = route - """The route for the actor to attempt to follow.""" - self.depart = depart - """Start time in seconds.""" - # XXX: Defaults to 1 hour of traffic. We may want to change this to be "continual - # traffic", effectively an infinite end. + + vehicle_name: str + """The name of the vehicle. It must be unique. """ + route: Union[RandomRoute, Route] + """The route for the actor to attempt to follow.""" + vehicle_type: str = "passenger" + """The type of the vehicle""" + depart: float = 0 + """Start time in seconds.""" + actor: TrafficActor = field(init=False) + """The traffic actor for the vehicle""" + + def __post_init__(self): + object.__setattr__( + self, + "actor", + TrafficActor(name=self.vehicle_name, vehicle_type=self.vehicle_type), + ) @property def id(self) -> str: @@ -533,6 +537,7 @@ def __hash__(self): def __eq__(self, other): return self.__class__ == other.__class__ and hash(self) == hash(other) + @dataclass(frozen=True) class JunctionEdgeIDResolver: """A utility for resolving a junction connection edge""" From ec75a4f60dcb0a18a684f40c401b5fab45502d7c Mon Sep 17 00:00:00 2001 From: AisenGinn Date: Wed, 16 Nov 2022 16:46:56 -0500 Subject: [PATCH 10/12] added set for flows and trips to ensure actor id is unique. --- smarts/sstudio/generators.py | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/smarts/sstudio/generators.py b/smarts/sstudio/generators.py index e531144ab8..8d714e1b8f 100644 --- a/smarts/sstudio/generators.py +++ b/smarts/sstudio/generators.py @@ -216,9 +216,13 @@ def _writexml( ): # Actors and routes may be declared once then reused. To prevent creating # duplicates we unique them here. - for actor in { + actors_for_vtypes = { actor for flow in traffic.flows for actor in flow.actors.keys() - }: + } + if traffic.trips: + actors_for_vtypes |= {trip.actor for trip in traffic.trips} + + for actor in actors_for_vtypes: sigma = min(1, max(0, actor.imperfection.sample())) # range [0,1] min_gap = max(0, actor.min_gap.sample()) # range >= 0 doc.stag( @@ -236,21 +240,6 @@ def _writexml( **actor.junction_model, ) - if traffic.trips is not None: - for actor in {trip.actor for trip in traffic.trips}: - doc.stag( - "vType", - id=actor.id, - accel=actor.accel, - decel=actor.decel, - vClass=actor.vehicle_type, - speedFactor=actor.speed.mean, - speedDev=actor.speed.sigma, - maxSpeed=actor.max_speed, - **actor.lane_changing_model, - **actor.junction_model, - ) - # Make sure all routes are "resolved" (e.g. `RandomRoute` are converted to # `Route`) so that we can write them all to file. resolved_routes = {} @@ -297,7 +286,7 @@ def _writexml( **rate_option, ) # write trip into xml format - if traffic.trips is not None: + if traffic.trips: self.write_trip_xml(traffic, doc, fill_in_route_gaps) with open(route_path, "w") as f: From 3a5b19f5fb0a4c4f904223a75f24c3741ca10464 Mon Sep 17 00:00:00 2001 From: AisenGinn Date: Wed, 16 Nov 2022 16:48:10 -0500 Subject: [PATCH 11/12] format --- smarts/sstudio/generators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smarts/sstudio/generators.py b/smarts/sstudio/generators.py index 8d714e1b8f..0beecda397 100644 --- a/smarts/sstudio/generators.py +++ b/smarts/sstudio/generators.py @@ -221,7 +221,7 @@ def _writexml( } if traffic.trips: actors_for_vtypes |= {trip.actor for trip in traffic.trips} - + for actor in actors_for_vtypes: sigma = min(1, max(0, actor.imperfection.sample())) # range [0,1] min_gap = max(0, actor.min_gap.sample()) # range >= 0 From 8bd8d1fca30d5c170909ce5022afd56668d0c1bc Mon Sep 17 00:00:00 2001 From: AisenGinn Date: Thu, 17 Nov 2022 10:23:04 -0500 Subject: [PATCH 12/12] added repeated name check for single_vehicle(trip). --- CHANGELOG.md | 2 +- smarts/sstudio/generators.py | 8 ++++++-- smarts/sstudio/types.py | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 579f6e5faf..14de8e8f91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Copy and pasting the git commit messages is __NOT__ enough. ## [Unreleased] ### Added +- Added single vehicle `Trip` into type. ### Deprecated ### Changed ### Removed @@ -26,7 +27,6 @@ Copy and pasting the git commit messages is __NOT__ enough. - Added ego's mission details into the `FormatObs` wrapper. - Added `SmartsLaneChangingModel` and `SmartsJunctionModel` to types available for use with the new smarts traffic engine within Scenario Studio. - Added option to `AgentInterface` to include traffic signals (lights) in `EgoVehicleObservation` objects. -- Added single vehicle `Trip` into type. ### Deprecated - Deprecated a few things related to traffic in the `Scenario` class, including the `route` argument to the `Scenario` initializer, the `route`, `route_filepath` and `route_files_enabled` properties, and the `discover_routes()` static method. In general, the notion of "route" (singular) here is being replaced with "`traffic_specs`" (plural) that allow for specifying traffic controlled by the SMARTS engine as well as Sumo. diff --git a/smarts/sstudio/generators.py b/smarts/sstudio/generators.py index 0beecda397..56eaa034dc 100644 --- a/smarts/sstudio/generators.py +++ b/smarts/sstudio/generators.py @@ -221,6 +221,10 @@ def _writexml( } if traffic.trips: actors_for_vtypes |= {trip.actor for trip in traffic.trips} + vehicle_id_set = {trip.vehicle_name for trip in traffic.trips} + vehilce_ids_list = [trip.vehicle_name for trip in traffic.trips] + if len(vehicle_id_set) != len(vehilce_ids_list): + raise ValueError("Repeated single vehicle names is not allowed.") for actor in actors_for_vtypes: sigma = min(1, max(0, actor.imperfection.sample())) # range [0,1] @@ -297,8 +301,8 @@ def _writexml( ) def write_trip_xml(self, traffic, doc, fill_in_gaps): - """Wrtes a traffic spec into a route file. Typically this would be the source - data to Sumo's DUAROUTER. + """Writes a trip spec into a route file. Typically this would be the source + data to SUMO's DUAROUTER. """ # Make sure all routes are "resolved" (e.g. `RandomRoute` are converted to # `Route`) so that we can write them all to file. diff --git a/smarts/sstudio/types.py b/smarts/sstudio/types.py index deb253522c..58cd1f202a 100644 --- a/smarts/sstudio/types.py +++ b/smarts/sstudio/types.py @@ -515,7 +515,7 @@ class Trip: depart: float = 0 """Start time in seconds.""" actor: TrafficActor = field(init=False) - """The traffic actor for the vehicle""" + """The traffic actor model (usually vehicle) that will be used for the trip.""" def __post_init__(self): object.__setattr__(