Commit a66808ad authored by Moritz Klischat's avatar Moritz Klischat
Browse files

version 2021.2

parent 5f137045
# SUMO - CommonRoad Interface
This package implements the interface between the framework for motion planning of automated vehicles [CommonRoad_io](https://commonroad.in.tum.de/commonroad-io) and the traffic simulator [SUMO](https://sumo.dlr.de). The interface is presented in detail in our [paper](https://mediatum.ub.tum.de/doc/1486856/344641.pdf) [1].
This package implements the interface between the framework for motion planning of automated vehicles [CommonRoad_io](https://commonroad.in.tum.de/commonroad-io)
and the traffic simulator [SUMO](https://sumo.dlr.de).
The interface is presented in detail in our [paper](https://mediatum.ub.tum.de/doc/1486856/344641.pdf) [1].
## Documentation
Please refer to [commonroad.in.tum.de/sumo-interface](https://commonroad.in.tum.de/sumo-interface) for the documentation and tutorials.
......
......@@ -15,7 +15,6 @@
</report>
<processing>
<time-to-teleport value="-1"/>
<lanechange-output value="output_lanechange.txt"/>
<lanechange.duration value="20"/>
</processing>
</configuration>
......@@ -15,7 +15,6 @@
</report>
<processing>
<time-to-teleport value="-1"/>
<lanechange-output value="output_lanechange.txt"/>
<lanechange.duration value="15"/>
</processing>
</configuration>
decorator==4.3
numpy>=1.13
lxml>=4.2.2
Pillow>=7.0.0
commonroad-io>=2021.1
commonroad-io>=2021.2
# docker_sumo
shapely==1.7.0
shapely>=1.7.0
opendrive2lanelet==1.1.0
grpcio==1.27.2
docker>=4.2.0
packaging>=20.3
ffmpeg==1.4
ffmpeg>=1.4
matplotlib>=3.3.3
ipython
......@@ -8,33 +8,31 @@ with open(path.join(this_directory, 'README.md'), 'r', encoding='utf-8') as f:
setup(
name='sumocr',
version='2021.1',
version='2021.2',
description='Python tool to interface with the SUMO traffic simulator',
keywords='autonomous automated vehicles driving motion planning',
url='https://commonroad.in.tum.de/sumo-interface',
project_urls={
'Documentation':
'https://commonroad.in.tum.de/sumo-interface',
'Documentation': 'https://commonroad.in.tum.de/sumo-interface',
'Forum': 'https://commonroad.in.tum.de/forum/c/commonroad-io',
'Source': 'https://gitlab.lrz.de/tum-cps/commonroad_io',
},
author='Cyber-Physical Systems Group, Technical University of Munich',
author_email='commonroad-i06@in.tum.de',
author_email='commonroad@lists.lrz.de',
license="GNU General Public License v3.0",
packages=find_packages(exclude=['ci','dist','example_scripts','not_for_public_repo','sumocr/docs','tests']),
install_requires=[
# 'decorator==5.0',
'numpy>=1.13',
'lxml>=4.2.2',
'Pillow>=7.0.0',
'commonroad-io>=2021.1',
'commonroad-io>=2021.2',
# docker_sumo
'shapely==1.7.0',
'shapely>=1.7.0',
'opendrive2lanelet==1.1.0',
'grpcio==1.27.2',
'docker>=4.2.0',
'packaging>=20.3',
'ffmpeg==1.4',
'ffmpeg>=1.4',
'matplotlib>=3.3.3',
'ipython',
],
......
interface
==========
This module provides an interace between a local Sumo
This module provides an interface between a local Sumo
installation and an ego vehicle steered by a motion planner.
.. automodule:: interface.sumo_simulation
.. automodule:: sumocr.interface.sumo_simulation
Sumo Simulation
----------------
......@@ -18,7 +18,7 @@ Sumo Simulation
:member-order: bysource
.. automodule:: interface.ego_vehicle
.. automodule:: sumocr.interface.ego_vehicle
Ego Vehicle
----------------
......@@ -34,7 +34,7 @@ Ego Vehicle
Helper Functions
------------------
.. automodule:: interface.util
.. automodule:: sumocr.interface.util
.. autofunction:: get_route_files
.. autofunction:: initialize_id_dicts
......
video
==========
.. automodule:: visualization.video
.. automodule:: sumocr.visualization.video
Video Creation
......
......@@ -36,7 +36,7 @@ copyright = '2021, Moritz Klischat'
author = 'Moritz Klischat'
# The full version, including alpha/beta/rc tags
release = '2021.1'
release = '2021.2'
# -- General configuration ---------------------------------------------------
......@@ -69,8 +69,8 @@ master_doc = 'index'
project = 'commonroad-sumo-interface'
copyright = '2021, Moritz Klischat, Peter Kocsis'
author = 'Moritz Klischat'
version = '2021.1'
release = '2021.1'
version = '2021.2'
release = '2021.2'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
......
......@@ -11,7 +11,8 @@ Testing motion planning algorithms for automated vehicles in realistic simulatio
In this work, we combine the open-source microscopic traffic simulator SUMO with our software framework CommonRoad to test motion planning of automated vehicles.
Since SUMO is not originally designed for simulating automated vehicles, this interface is designed for exchanging the trajectories of vehicles controlled by a motion planner
and the trajectories of other traffic participants between SUMO and CommonRoad.
Furthermore, we ensure realistic dynamic behaviors of other traffic participants by extending the lane changing model in SUMO to implement more realistic lateral dynamics.
To run interactive scenarios fr the CommonRoad database, please use the scripts provided in this repository https://gitlab.lrz.de/tum-cps/commonroad-interactive-scenarios.
More about the interface can be found in our `paper <http://mediatum.ub.tum.de/doc/1486856/344641.pdf>`_:
......@@ -43,4 +44,4 @@ Contact information
=====================
:Website: `http://commonroad.in.tum.de <https://commonroad.in.tum.de/>`_
:Email: `commonroad-i06@in.tum.de <commonroad-i06@in.tum.de>`_
:Email: `commonroad@lists.lrz.de <commonroad@lists.lrz.de>`_
......@@ -12,9 +12,9 @@ from commonroad.scenario.trajectory import State, Trajectory
__author__ = "Moritz Klischat"
__copyright__ = "TUM Cyber-Physical Systems Group"
__credits__ = ["ZIM Projekt ZF4086007BZ8"]
__version__ = "2021.1"
__version__ = "2021.2"
__maintainer__ = "Moritz Klischat"
__email__ = "commonroad-i06@in.tum.de"
__email__ = "commonroad@lists.lrz.de"
__status__ = "Released"
......@@ -34,12 +34,17 @@ class EgoVehicle:
self._state_dict: Dict[State] = dict() # collects driven states
self.shape = Rectangle(self.length, self.width, center=np.array([0, 0]), orientation=0.0)
self._driven_trajectory = None # returns trajectory object of driven
self._planned_trajectories: Dict[List[State]] = {} # collects trajectories from planner for every time step
self._planned_trajectories: Dict[int, List[State]] = {} # collects trajectories from planner for every time step
self._current_time_step = initial_state.time_step
self.delta_steps = delta_steps
if planning_problem is not None:
assert self.initial_state == planning_problem.initial_state, '<sumo_simulation/ego_vehicle> planning problem\' initial_state must equal initial_state'
assert np.all(np.isclose(self.initial_state.position, planning_problem.initial_state.position)), \
'<sumo_simulation/ego_vehicle> planning problem.initial_state must equal initial_state'
assert np.isclose(self.initial_state.orientation, planning_problem.initial_state.orientation), \
'<sumo_simulation/ego_vehicle> planning problem.initial_state must equal initial_state'
assert np.isclose(self.initial_state.velocity, planning_problem.initial_state.velocity), \
'<sumo_simulation/ego_vehicle> planning problem.initial_state must equal initial_state'
self.planning_problem = planning_problem
@property
......@@ -106,6 +111,10 @@ class EgoVehicle:
:param delta_step: get planned state after delta steps
"""
assert self.current_time_step in self._planned_trajectories,\
f"No planned trajectory found at time step {self.current_time_step} for ego vehicle {self.id}! " \
f"Use ego_vehicle.set_planned_trajectory() to set the trajectory."
planned_state: State = copy.deepcopy(self._planned_trajectories[self.current_time_step][0])
if self.delta_steps > 1:
# linear interpolation
......@@ -133,6 +142,7 @@ class EgoVehicle:
:param time_step: the state is returned according to this time step.
"""
if time_step == self.initial_state.time_step:
return self.initial_state
else:
......
......@@ -22,8 +22,10 @@ from commonroad.scenario.trajectory import State, Trajectory
from sumocr.interface.ego_vehicle import EgoVehicle
from sumocr.interface.util import *
from sumocr.maps.scenario_wrapper import AbstractScenarioWrapper
from sumocr.maps.sumo_scenario import ScenarioWrapper
from sumocr.sumo_config import DefaultConfig, EGO_ID_START, ID_DICT
from sumocr.sumo_config.pathConfig import SUMO_BINARY, SUMO_GUI_BINARY
from xml.etree import cElementTree as ET
try:
import traci
......@@ -40,7 +42,7 @@ except ModuleNotFoundError as exp:
__author__ = "Moritz Klischat, Maximilian Frühauf"
__copyright__ = "TUM Cyber-Physical Systems Group"
__credits__ = ["ZIM Projekt ZF4086007BZ8"]
__version__ = "2021.1"
__version__ = "2021.2"
__maintainer__ = "Moritz Klischat"
__email__ = "commonroad@lists.lrz.de"
__status__ = "Released"
......@@ -65,14 +67,14 @@ class SumoSimulation:
self.routedomain: RouteDomain = RouteDomain()
self._current_time_step = 0
self.ids_sumo2cr, self.ids_cr2sumo = initialize_id_dicts(ID_DICT)
self._max_cr_id = 0 # keep track of all IDs in CR scenario
self._max_lanelet_network_id = 0 # keep track of all IDs in CR lanelet_network
# veh_id -> List[SignalState]
self.signal_states: Dict[int, List[SignalState]] = defaultdict(list)
self.obstacle_shapes: Dict[int, Rectangle] = dict()
self.obstacle_types: Dict[int, ObstacleType] = dict()
self.cached_position = {} # caches position for orientation computation
self._scenarios: AbstractScenarioWrapper = None
self._scenarios: ScenarioWrapper = None
self.ego_vehicles: Dict[int, EgoVehicle] = dict()
self.conf = DefaultConfig()
self._silent = False
......@@ -88,7 +90,7 @@ class SumoSimulation:
self.initialized = False
@property
def scenarios(self):
def scenarios(self) -> ScenarioWrapper:
return self._scenarios
@scenarios.setter
......@@ -102,14 +104,18 @@ class SumoSimulation:
if lanelet_network.traffic_lights else 0
max_traffic_sign = np.max([t.traffic_sign_id for t in lanelet_network.traffic_signs]) \
if lanelet_network.traffic_signs else 0
return np.max([max_lanelet, max_intersection, max_traffic_light, max_traffic_sign]).item()
val = np.max([max_lanelet, max_intersection, max_traffic_light, max_traffic_sign])
if isinstance(val, np.generic):
return val.item()
else:
return val
if self.planning_problem_set is not None:
max_pp= max(list(self.planning_problem_set.planning_problem_dict.keys()))
else:
max_pp = 0
self._max_cr_id = max(max_pp,
self._max_lanelet_network_id = max(max_pp,
max_lanelet_network_id(scenarios.lanelet_network))
self._scenarios = scenarios
......@@ -123,6 +129,7 @@ class SumoSimulation:
:param scenario_wrapper: handles all files required for simulation. If None it is initialized with files
folder conf.scenarios_path + conf.scenario_name
:param planning_problem_set: initialize initial state of ego vehicles
(if None, use planning_problem_set from self.scenario)
"""
if conf is not None:
......@@ -136,8 +143,10 @@ class SumoSimulation:
self.dt = self.conf.dt
self.dt_sumo = self.conf.dt / self.conf.delta_steps
self.delta_steps = self.conf.delta_steps
self.planning_problem_set = planning_problem_set
self.planning_problem_set = planning_problem_set \
if planning_problem_set is not None else self.scenarios.planning_problem_set
np.random.seed(self.conf.random_seed)
assert sumocr.sumo_installed, "SUMO not installed or environment variable SUMO_HOME not set."
if self.conf.with_sumo_gui:
......@@ -157,8 +166,7 @@ class SumoSimulation:
"--step-length",
str(self.dt_sumo), "--ignore-route-errors",
"--lateral-resolution",
str(self.conf.lateral_resolution),
"--collision.check-junctions"
str(self.conf.lateral_resolution)
]
if self.conf.lateral_resolution > 0.0:
......@@ -174,14 +182,12 @@ class SumoSimulation:
self.initialized = True
# initializes vehicle positions (and ego vehicles, if defined in .rou file)
if self.planning_problem_set is not None:
# initializes ego vehicle positions if not defined in .rou file
if self.planning_problem_set is not None and len(self._find_ego_vehicles_in_rou_file()) == 0:
if len(self.ego_vehicles) > 0:
self.logger.warning('<SumoSimulation/init_ego_vehicles> Ego vehicles are already defined through .rou'
'file and planning problem!')
self.init_ego_vehicles_from_planning_problem(self.planning_problem_set)
else:
self.dummy_ego_simulation = True
def _init_logging(self):
# Create a custom logger
......@@ -200,6 +206,23 @@ class SumoSimulation:
return logger
def _find_ego_vehicles_in_rou_file(self, n_max=1) -> List[str]:
rou_file = self.scenarios.get_rou_file()
if rou_file is None:
return []
with open(rou_file, 'r') as f:
tree = ET.parse(f)
ego_vehicle_ids = []
vehicles = tree.findall("vehicle")
for v in vehicles:
if v.get("id").startswith(EGO_ID_START):
ego_vehicle_ids.append(v.get("id"))
if len(ego_vehicle_ids) >= n_max:
break
return ego_vehicle_ids
def init_ego_vehicles_from_planning_problem(
self, planning_problem_set: PlanningProblemSet) -> None:
"""
......@@ -235,9 +258,14 @@ class SumoSimulation:
personNumber=0)
sumo_angle = 90.0 - math.degrees(planning_problem.initial_state.orientation)
position_tmp = planning_problem.initial_state.position\
+ 0.5 * length * np.array([math.cos(planning_problem.initial_state.orientation),
math.sin(planning_problem.initial_state.orientation)])
self.vehicledomain.setLength(vehID=sumo_id,length=length)
self.vehicledomain.setWidth(vehID=sumo_id, width=width)
self.vehicledomain.moveToXY(vehID=sumo_id, edgeID='dummy', lane=-1,
x=planning_problem.initial_state.position[0],
y=planning_problem.initial_state.position[1],
x=position_tmp[0],
y=position_tmp[1],
angle=sumo_angle, keepRoute=2)
self.__presimulation_silent(1)
......@@ -310,11 +338,11 @@ class SumoSimulation:
"""
self.cr_scenario = Scenario(self.dt, self.conf.scenario_name)
self.cr_scenario.lanelet_network = self.scenarios.lanelet_network
self.cr_scenario = self.scenarios.create_minimal_scenario()
# remove old obstacles from lanes
# this is only necessary if obstacles are added to lanelets
if self.conf.add_lanelets_to_dyn_obstacles:
for lanelet in self.cr_scenario.lanelet_network.lanelets:
lanelet.dynamic_obstacles_on_lanelet = {}
......@@ -327,8 +355,12 @@ class SumoSimulation:
:param lanelet_network:
:return: list of cr scenarios, list of cr scenarios with ego, list of planning problem sets)
"""
self.cr_scenario = Scenario(self.dt, self.conf.scenario_name)
self.cr_scenario.lanelet_network = self.scenarios.lanelet_network
self.cr_scenario = self.scenarios.create_full_meta_scenario()
# remove old obstacles from lanes
# this is only necessary if obstacles are added to lanelets
if self.conf.add_lanelets_to_dyn_obstacles:
for lanelet in self.cr_scenario.lanelet_network.lanelets:
lanelet.dynamic_obstacles_on_lanelet = {}
self.cr_scenario.add_objects(self._get_cr_obstacles_all())
return self.cr_scenario
......@@ -372,17 +404,17 @@ class SumoSimulation:
for i in range(pre_simulation_steps * self.delta_steps):
traci.simulationStep()
self._silent = False
self._fetch_sumo_vehicles(self.current_time_step)
self._fetch_sumo_pedestrians(self.current_time_step)
self._silent = False
def _fetch_sumo_vehicles(self, time_step: int):
"""
Gets and stores all vehicle states from SUMO. Initializes ego vehicles when they enter simulation.
"""
vehicle_ids = self.vehicledomain.getIDList()
if not vehicle_ids:
vehicle_ids = self.sort_ego_first(self.vehicledomain.getIDList())
if len(vehicle_ids) == 0:
return
if not self.dummy_ego_simulation:
......@@ -390,6 +422,8 @@ class SumoSimulation:
for veh_id in vehicle_ids:
state = self._get_current_state_from_sumo(self.vehicledomain, str(veh_id), SUMO_VEHICLE_PREFIX)
if state is None:
continue
# initializes new vehicle
if veh_id not in self.ids_sumo2cr[SUMO_VEHICLE_PREFIX]:
......@@ -400,9 +434,16 @@ class SumoSimulation:
state.time_step = time_step - 1
else:
state.time_step = time_step
if self.planning_problem_set is not None:
planning_problem = list(self.planning_problem_set.planning_problem_dict.values())[0]
else:
planning_problem = None
self._add_ego_vehicle(EgoVehicle(cr_id, state, self.conf.delta_steps,
self.conf.ego_veh_width,
self.conf.ego_veh_length))
self.conf.ego_veh_length,
planning_problem=planning_problem))
elif not self._silent:
# new obstacle vehicle
cr_id = self._create_cr_id('obstacleVehicle', veh_id, SUMO_VEHICLE_PREFIX)
......@@ -433,7 +474,7 @@ class SumoSimulation:
for t in range(0, self.conf.delta_steps):
state_tmp = copy.deepcopy(state)
state_tmp.position = state.position + (t + 1) * state.velocity \
* self.dt * np.array([np.cos(ori), np.sin(ori)])
* self.dt * np.array([math.cos(ori), math.sin(ori)])
state_tmp.time_step = t + 1
state_list.append(state_tmp)
......@@ -452,6 +493,8 @@ class SumoSimulation:
obstacle_type = "obstaclePedestrian"
for ped_id in person_ids:
state = self._get_current_state_from_sumo(self.persondomain, str(ped_id), SUMO_PEDESTRIAN_PREFIX)
if state is None:
continue
if ped_id not in self.ids_sumo2cr[SUMO_PEDESTRIAN_PREFIX]:
# initialize new pedestrian
cr_id = self._create_cr_id(obstacle_type, ped_id, SUMO_PEDESTRIAN_PREFIX)
......@@ -522,14 +565,14 @@ class SumoSimulation:
return Rectangle(length, width)
def _get_current_state_from_sumo(self, domain, veh_id: str,
sumo_prefix: str) -> State:
sumo_prefix: str) -> Union[None, State]:
"""
Gets the current state from sumo.
:param domain
:type: Union[PersonDomain, VehicleDomain]
:param veh_id: the id of the vehicle, whose state will be returned from SUMO.
:return: the state of the given vehicle
:return: the state of the given vehicle or None, if not in field of view
"""
unique_id = sumo_prefix + veh_id
position = np.array(domain.getPosition(veh_id))
......@@ -551,7 +594,11 @@ class SumoSimulation:
orientation = math.radians(-domain.getAngle(veh_id) + 90)
self.cached_position[unique_id] = position
position -= 0.5 * length * np.array([np.cos(orientation), np.sin(orientation)])
position -= 0.5 * length * np.array([math.cos(orientation), math.sin(orientation)])
in_fov = self.is_in_field_of_view(position, veh_id)
if in_fov is False:
return None
try:
acceleration = domain.getAcceleration(veh_id)
......@@ -599,7 +646,12 @@ class SumoSimulation:
for lanelet_id in lanelet_ids}
obstacle_type = self.obstacle_types[veh_id]
signal_state = next((s for s in self.signal_states[veh_id] if s.time_step == state.time_step), None)
signal_state = next((s for s in self.signal_states[veh_id] if s.time_step == time_step), None)
if start_0:
signal_state.time_step = 0
else:
signal_state.time_step = time_step
dynamic_obstacle = DynamicObstacle(
obstacle_id=veh_id,
obstacle_type=obstacle_type,
......@@ -726,18 +778,8 @@ class SumoSimulation:
f'Trajectory of ego vehicle has not been updated. Still at time_step {ego_vehicle.current_time_step},' \
f'while simulation step {self.current_time_step + 1} should be simulated.'
# Check if there is a lanelet change in the configured time window
lc_future_status, lc_duration = self.check_lanelets_future_change(
ego_vehicle.current_state, ego_vehicle.get_planned_trajectory)
# If there is a lanelet change, check whether the change is just started
lc_status = self._check_lc_start(id_cr, lc_future_status)
# Calculate the sync mechanism based on the future information
sync_mechanism = self._check_sync_mechanism(
lc_status, id_cr, ego_vehicle.current_state)
# Execute MoveXY or SUMO lane change according to the sync mechanism
planned_state = ego_vehicle.get_planned_state(delta_step)
self._forward_info2sumo(planned_state, sync_mechanism, lc_duration,
id_cr)
self._forward_info2sumo(planned_state, "SYNC_MOVE_XY", 0, id_cr)
def _get_ego_ids(self) -> Dict[int, str]:
"""
......@@ -770,14 +812,12 @@ class SumoSimulation:
:return: cr_id as int
"""
cr_id = generate_cr_id(type, sumo_id, sumo_prefix, self.ids_sumo2cr, self._max_cr_id)
cr_id = generate_cr_id(type, sumo_id, sumo_prefix, self.ids_sumo2cr, self._max_lanelet_network_id)
self.ids_sumo2cr[type][sumo_id] = cr_id
self.ids_sumo2cr[sumo_prefix][sumo_id] = cr_id
self.ids_cr2sumo[type][cr_id] = sumo_id
self.ids_cr2sumo[sumo_prefix][cr_id] = sumo_id
self._max_cr_id = max(self._max_cr_id, cr_id)
return cr_id
@property
......@@ -991,3 +1031,32 @@ class SumoSimulation:
pass
else:
pass
def is_in_field_of_view(self, position: np.ndarray, veh_id: str):
"""
Returns True if the position is in the FOV of any ego vehicle.
:param position: position of other vehicle
:return:
"""
if len(self.ego_vehicles) == 0 or self.conf.field_of_view is None \
or veh_id in self.ids_sumo2cr[SUMO_VEHICLE_PREFIX] or veh_id in self.ids_sumo2cr[SUMO_PEDESTRIAN_PREFIX]:
return True
for ego_vehicle in self.ego_vehicles.values():
try:
if np.linalg.norm(ego_vehicle.get_state_at_timestep(self.current_time_step-1).position - position,
ord=np.inf) < self.conf.field_of_view:
return True
except KeyError:
if len(ego_vehicle._state_dict.keys()) == 0:
return False
return False
@staticmethod
def sort_ego_first(vehicle_ids: List[str]):
vehicle_ids = list(vehicle_ids)
for i in range(len(vehicle_ids)):
if vehicle_ids[i].startswith(EGO_ID_START):
vehicle_ids.insert(0, vehicle_ids.pop(i))
return vehicle_ids
......@@ -12,9 +12,9 @@ import enum
__author__ = "Moritz Klischat"
__copyright__ = "TUM Cyber-Physical Systems Group"
__credits__ = ["ZIM Projekt ZF4086007BZ8"]
__version__ = "2021.1"
__version__ = "2021.2"
__maintainer__ = "Moritz Klischat"
__email__ = "commonroad-i06@in.tum.de"
__email__ = "commonroad@lists.lrz.de"
__status__ = "Released"
# CommonRoad obstacle type to sumo type
......@@ -119,10 +119,11 @@ def generate_cr_id(type: str, sumo_id: str, sumo_prefix: str, ids_sumo2cr: dict,
'Two sumo objects of different types seem to have same sumo ID {0}. ID must be unique'
.format(sumo_id))
cr_id = max_cr_id + 1
max_type = max(list(ids_sumo2cr[type].values())) if len(ids_sumo2cr[type]) > 0 else int(str(ID_DICT[type]) + "0")
cr_id = max(max_cr_id, max_type) + 1
if int(str(cr_id)[0]) != ID_DICT[type]:
cr_id = int(str(ID_DICT[type]) + "0" * len(str(cr_id)))
cr_id = int(str(ID_DICT[type]) + "0" + str(cr_id)[1:])
assert cr_id not in list(ids_sumo2cr[type].values())
return cr_id
......
......@@ -4,15 +4,15 @@ from commonroad.scenario.lanelet import LaneletNetwork
__author__ = "Moritz Klischat"
__copyright__ = "TUM Cyber-Physical Systems Group"
__credits__ = ["ZIM Projekt ZF4086007BZ8"]
__version__ = "2021.1"
__version__ = "2021.2"
__maintainer__ = "Moritz Klischat"
__email__ = "commonroad-i06@in.tum.de"
__email__ = "commonroad@lists.lrz.de"
__status__ = "Released"
class AbstractScenarioWrapper(metaclass=ABCMeta):
# these two attributes are abstract and need to be defined by the inheriting subclass
sumo_cfg_file = ""
lanelet_network: LaneletNetwork = None
planning_problem_set = None