Commit 69b0766e authored by Moritz Klischat's avatar Moritz Klischat

updated documenatation

parent 10e0c8db
pathConfig.py
scenarios/*
## Core latex/pdflatex auxiliary files:
*.aux
*.lof
*.log
*.lot
*.fls
*.out
*.toc
*.fmt
*.fot
*.cb
*.cb2
## Intermediate documents:
*.dvi
*-converted-to.*
# these rules might exclude image files for figures etc.
*.ps
# *.eps
# *.pdf
## Generated if empty string is given at "Please type another file name for output:"
.pdf
## Bibliography auxiliary files (bibtex/biblatex/biber):
*.bbl
*.bcf
*.blg
*-blx.aux
*-blx.bib
*.brf
*.run.xml
## Build tool auxiliary files:
*.fdb_latexmk
*.synctex
*.synctex(busy)
*.synctex.gz
*.synctex.gz(busy)
*.pdfsync
## Auxiliary and intermediate files from other packages:
# algorithms
*.alg
*.loa
# achemso
acs-*.bib
# amsthm
*.thm
# beamer
*.nav
*.snm
*.vrb
# cprotect
*.cpt
# fixme
*.lox
# feynmf/feynmp
*.mf
*.mp
*.t[1-9]
*.t[1-9][0-9]
*.tfm
*.[1-9]
*.[1-9][0-9]
#(r)(e)ledmac/(r)(e)ledpar
*.end
*.?end
*.[1-9]
*.[1-9][0-9]
*.[1-9][0-9][0-9]
*.[1-9]R
*.[1-9][0-9]R
*.[1-9][0-9][0-9]R
*.eledsec[1-9]
*.eledsec[1-9]R
*.eledsec[1-9][0-9]
*.eledsec[1-9][0-9]R
*.eledsec[1-9][0-9][0-9]
*.eledsec[1-9][0-9][0-9]R
# glossaries
*.acn
*.acr
*.glg
*.glo
*.gls
*.glsdefs
# gnuplottex
*-gnuplottex-*
# gregoriotex
*.gaux
*.gtex
# hyperref
*.brf
# knitr
*-concordance.tex
# TODO Comment the next line if you want to keep your tikz graphics files
*.tikz
*-tikzDictionary
# listings
*.lol
# makeidx
*.idx
*.ilg
*.ind
*.ist
# minitoc
*.maf
*.mlf
*.mlt
*.mtc
*.mtc[0-9]
*.mtc[1-9][0-9]
# minted
_minted*
*.pyg
# morewrites
*.mw
# mylatexformat
*.fmt
# nomencl
*.nlo
# sagetex
*.sagetex.sage
*.sagetex.py
*.sagetex.scmd
# scrwfile
*.wrt
# sympy
*.sout
*.sympy
sympy-plots-for-*.tex/
# pdfcomment
*.upa
*.upb
# pythontex
*.pytxcode
pythontex-files-*/
# thmtools
*.loe
# TikZ & PGF
*.dpth
*.md5
*.auxlock
# todonotes
*.tdo
# easy-todo
*.lod
# xindy
*.xdy
# xypic precompiled matrices
*.xyc
# endfloat
*.ttt
*.fff
# Latexian
TSWLatexianTemp*
## Editors:
# WinEdt
*.bak
*.sav
# Texpad
.texpadtmp
# Kile
*.backup
# KBibTeX
*~[0-9]*
# build files doc
tools/Python/commonroad/doc/build/*
# auto folder when using emacs and auctex
/auto/*
# python
*__pycache__/
*.cache
*pytest_cache/
*.egg-info
# SUMO - CommonRoad Interface
This interface couples the framework for motion planning of automated vehicles based on [CommonRoad_io](https://pypi.org/project/commonroad-io/) and the traffic simulator [SUMO](https://sumo.dlr.de).
This package implements the interface between the framework for motion planning of automated vehicles [CommonRoad_io](https://pypi.org/project/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] and a documentation of the API can be found [here]( https://commonroad.in.tum.de/static/docs/commonroad-sumo-interface/index.html).
# Prerequisites
The package is written in Python 3.6 and tested on Linux.
......@@ -17,6 +17,7 @@ And add the absolute path of `commonroad-sumo-interface` to your Python interpre
Clone a customized version of SUMO for smooth lane changes from https://github.com/octavdragoi/sumo and check out branch `smooth-lane-change`.
For installation we recommend building with:
```
sudo apt-get install cmake python g++ libxerces-c-dev libfox-1.6-dev libgdal-dev libproj-dev libgl2ps-dev swig
cd sumo
......@@ -25,6 +26,7 @@ mkdir build && cd build
cmake ..
make -j8
```
More options on the installation can be found here: https://sumo.dlr.de/wiki/Installing/Linux_Build .
## Configure SUMO and local environment
......@@ -82,5 +84,8 @@ for t in range(conf.simulation_steps):
sumo_sim.simulate_step()
sumo_sim.stop()
create_video(sumo_sim, conf.video_start, conf.video_end, output_folder)
```
[1] Moritz Klischat, Octav Dragoi, Mostafa Eissa, and Matthias Althoff, Coupling SUMO with a Motion Planning Framework for Automated Vehicles, SUMO 2019: Simulating Connected Urban Mobility
"""
Converts net.xml to Commonroad xml files and creates rou files for simulation.
"""
import matplotlib.pyplot as plt
from typing import List
from commonroad.visualization.draw_dispatch_cr import draw_object
from sumo2cr.maps.sumo_scenario import ScenarioWrapper
from commonroad.common.util import Interval
from commonroad.scenario.trajectory import State
import os
__author__ = "Moritz Klischat"
__copyright__ = "TUM Cyber-Physical Systems Group"
__credits__ = ["ZIM Projekt ZF4086007BZ8"]
__version__ = "1.0.0"
__maintainer__ = "Moritz Klischat"
__email__ = "commonroad-i06@in.tum.de"
__status__ = "Released"
scenario_folder = os.path.join(os.path.dirname(__file__),'../scenarios')
net_file = os.path.join(scenario_folder, 'a9/a9.net.xml')
sumo_cfg_file = os.path.join(scenario_folder, 'a9/a9.sumo.cfg')
# parameters
dt = 0.1
convert_map = True
n_vehicles_max:int = 30
veh_per_second = 50
n_ego_vehicles:int = 1
ego_ids:List[int] = []
initial_states:List[State] = []
ego_start_time:int=10
departure_time_ego = 3
departure_interval_vehicles = Interval(0,20)
if convert_map:
# generate commonroad map + rou file + cfg file
scenario = ScenarioWrapper.full_conversion_from_net(net_file, dt, n_vehicles_max, n_ego_vehicles, ego_ids, ego_start_time, departure_time_ego, departure_interval_vehicles,veh_per_second)
else:
# generate rou file
scenario = ScenarioWrapper.recreate_route_file(sumo_cfg_file, dt, n_vehicles_max, n_ego_vehicles, ego_ids, ego_start_time, departure_time_ego, departure_interval_vehicles,veh_per_second)
plt.figure(figsize=(25, 25))
draw_object(scenario.lanelet_network)
plt.autoscale()
plt.axis('equal')
plt.xlim([290,380])
plt.ylim([195,250])
"""
Converts net.xml to Commonroad xml files and creates rou files for simulation.
"""
import matplotlib.pyplot as plt
from typing import List
from commonroad.visualization.draw_dispatch_cr import draw_object
from sumo2cr.maps.sumo_scenario import ScenarioWrapper
from commonroad.common.util import Interval
from commonroad.scenario.trajectory import State
import os
__author__ = "Moritz Klischat"
__copyright__ = "TUM Cyber-Physical Systems Group"
__credits__ = ["ZIM Projekt ZF4086007BZ8"]
__version__ = "1.0.0"
__maintainer__ = "Moritz Klischat"
__email__ = "commonroad-i06@in.tum.de"
__status__ = "Released"
scenario_folder = os.path.join(os.path.dirname(__file__),'../scenarios')
net_file = os.path.join(scenario_folder, 'a9/a9.net.xml')
sumo_cfg_file = os.path.join(scenario_folder, 'a9/a9.sumo.cfg')
# parameters
dt = 0.1
convert_map = True
n_vehicles_max:int = 30
veh_per_second = 50
n_ego_vehicles:int = 1
ego_ids:List[int] = []
initial_states:List[State] = []
ego_start_time:int=10
departure_time_ego = 3
departure_interval_vehicles = Interval(0,20)
if convert_map:
# generate commonroad map + rou file + cfg file
scenario = ScenarioWrapper.full_conversion_from_net(net_file, dt, n_vehicles_max, n_ego_vehicles, ego_ids, ego_start_time, departure_time_ego, departure_interval_vehicles,veh_per_second)
else:
# generate rou file
scenario = ScenarioWrapper.recreate_route_file(sumo_cfg_file, dt, n_vehicles_max, n_ego_vehicles, ego_ids, ego_start_time, departure_time_ego, departure_interval_vehicles,veh_per_second)
plt.figure(figsize=(25, 25))
draw_object(scenario.lanelet_network)
plt.autoscale()
plt.axis('equal')
plt.xlim([290,380])
plt.ylim([195,250])
plt.show()
\ No newline at end of file
......@@ -30,5 +30,7 @@ for t in range(conf.simulation_steps):
sumo_sim.simulate_step()
sumo_sim.stop()
create_video(sumo_sim, conf.video_start, conf.video_end, output_folder)
'''
Copy this file and rename to path_config.py, afterwards set paths according to your local configuration
'''
# set installation locations
SUMO_GUI_BINARY = '/usr/bin/sumo-gui'
# path to binary of adapted sumo repository (see readme)
SUMO_BINARY = '/home/user/sumo/bin/sumo'
# by default port 8873 is used, you can modify the port number
TRACI_PORT = 8873
# default files (no adaption required)
'''
Copy this file and rename to path_config.py, afterwards set paths according to your local configuration
'''
# set installation locations
SUMO_GUI_BINARY = '/usr/bin/sumo-gui'
# path to binary of adapted sumo repository (see readme)
SUMO_BINARY = '/home/user/sumo/bin/sumo'
# by default port 8873 is used, you can modify the port number
TRACI_PORT = 8873
# default files (no adaption required)
DEFAULT_CFG_FILE = 'sumo_config/.sumo.cfg'
\ No newline at end of file
......@@ -3,7 +3,7 @@ from typing import Dict, List, Union
import numpy as np
import copy
from commonroad.geometry.shape import Rectangle
from commonroad.planning.planning_problem import PlanningProblem
from commonroad.planning.planning_problem import PlanningProblem, GoalRegion
from commonroad.prediction.prediction import TrajectoryPrediction
from commonroad.scenario.obstacle import DynamicObstacle, ObstacleType
from commonroad.scenario.trajectory import State, Trajectory
......@@ -40,10 +40,10 @@ class EgoVehicle:
def set_planned_trajectory(self, planned_state_list:List[State]) -> None:
"""
Set planned trajectory beginning with current time step.
:param planned_state_list:
:param initial_time_step. If None, add at current time step
:return:
Sets planned trajectory beginning with current time step.
:param planned_state_list: the planned trajectory
"""
assert len(planned_state_list) >= self.delta_steps,\
......@@ -57,15 +57,16 @@ class EgoVehicle:
@property
def get_planned_trajectory(self) -> List[State]:
"""Get planned trajectory from current time step"""
"""Gets planned trajectory according to the current time step"""
return self._planned_trajectories[self.current_time_step]
def get_dynamic_obstacle(self,time_step: Union[int,None]=None) -> DynamicObstacle:
"""
If time step is false, add complete driven trajectory.
If time step is int: start from given step and add planned trajectory
If time step is false, adds complete driven trajectory and returns the dynamic obstacles.
If time step is int: starts from given step and adds planned trajectory and returns the dynamic obstacles.
:param time_step: initial time step of vehicle
:return: DynamicObstacle
:return: DynamicObstacle object of the ego vehicle.
"""
if time_step is False:
return DynamicObstacle(self.id,obstacle_type=ObstacleType.CAR,
......@@ -86,6 +87,12 @@ class EgoVehicle:
raise ValueError('time needs to be None or integer')
def get_planned_state(self, delta_step: int=0):
"""
Returns the planned state.
:param delta_step: get plannedd state after delta steps
"""
planned_state:State = copy.deepcopy(self._planned_trajectories[self.current_time_step][0])
if self.delta_steps > 1:
# linear interpolation
......@@ -98,12 +105,20 @@ class EgoVehicle:
@property
def current_state(self) -> State:
"""
Returns the current state.
"""
if self.current_time_step == self.initial_state.time_step:
return self.initial_state
else:
return self._state_dict[self.current_time_step]
def get_state_at_timestep(self,time_step: int):
def get_state_at_timestep(self,time_step: int) -> State:
"""
Returns the state according to the given time step.
: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:
......@@ -116,7 +131,10 @@ class EgoVehicle:
raise PermissionError('current_state cannot be set manually, use set_planned_trajectory()')
@property
def current_time_step(self):
def current_time_step(self) -> int:
"""
Returns current time step.
"""
return self._current_time_step
@current_time_step.setter
......@@ -124,17 +142,26 @@ class EgoVehicle:
raise PermissionError('current_state cannot be set manually, use set_planned_trajectory()')
@property
def goal(self):
def goal(self) -> GoalRegion:
"""
Returns the goal of the planning problem.
"""
return self.planning_problem.goal
def add_state(self,state):
def add_state(self, state: State) -> None:
"""
Adds a state to the current state dictionary.
:param state: the state to be added
"""
self._state_dict[self._current_time_step+1] = state
@property
def driven_trajectory(self) -> TrajectoryPrediction:
"""
Returns trajectory prediction object for driven trajectory (mainly for plotting)
:return:
"""
state_dict_tmp ={}
for t, state in self._state_dict.items():
......@@ -152,7 +179,10 @@ class EgoVehicle:
return
@property
def width(self):
def width(self) -> float:
"""
Returns the width of the ego vehicle.
"""
return self._width
@width.setter
......@@ -162,7 +192,10 @@ class EgoVehicle:
return
@property
def length(self):
def length(self) -> float:
"""
Returns the length of the ego vehicle.
"""
return self._length
@length.setter
......@@ -172,7 +205,10 @@ class EgoVehicle:
return
@property
def initial_state(self):
def initial_state(self) -> State:
"""
Returns the initial state of the ego vehicle.
"""
return self._initial_state
@initial_state.setter
......
This diff is collapsed.
import os
import xml.etree.ElementTree as et
import warnings
import sumo_config.default
from sumo_config import plot_params
__author__ = "Moritz Klischat"
__copyright__ = "TUM Cyber-Physical Systems Group"
__credits__ = ["ZIM Projekt ZF4086007BZ8"]
__version__ = "1.0.0"
__maintainer__ = "Moritz Klischat"
__email__ = "commonroad-i06@in.tum.de"
__status__ = "Released"
def get_route_files(config_file):
"""
:param config_file: SUMO sumo_config file (.sumocfg)
:return: net-file and route-files specified in the sumo_config file
"""
if not os.path.isfile(config_file):
raise FileNotFoundError(config_file)
tree = et.parse(config_file)
file_directory = os.path.dirname(config_file)
# find route-files
all_route_files = tree.findall('*/route-files')
route_files = []
if len(all_route_files) < 1:
raise RouteError()
for item in all_route_files:
attributes = item.attrib['value'].split(',')
for route in attributes:
route_files.append(os.path.join(file_directory, route))
return route_files
def initialize_id_dicts(id_convention):
"""From id_convention, create empty nested dict structure for sumo2cr and cr2sumo dicts.
id_convention: dict with mapping of object type to start number of id"""
sumo2cr = {}
cr2sumo = {}
for k in id_convention:
sumo2cr[k] = {}
cr2sumo[k] = {}
sumo2cr['all_ids'] = {}
cr2sumo['all_ids'] = {}
return sumo2cr, cr2sumo
def generate_cr_id(type, sumo_id, ids_sumo2cr):
""" Generates a new commonroad ID without adding it to any ID dictionary."""
if type not in sumo_config.default.ID_DICT:
raise ValueError(
'{0} is not a valid type of id_convention. Only allowed: {1}'.format(type, sumo_config.default.ID_DICT.keys()))
if sumo_id in ids_sumo2cr[type]:
warnings.warn('For this sumo_id there is already a commonroad id. No cr ID is generated')
return ids_sumo2cr[type][sumo_id]
elif sumo_id in ids_sumo2cr['all_ids']:
raise ValueError(
'Two sumo objects of different types seem to have same sumo ID {0}. ID must be unique'.format(sumo_id))
# If this case happens for auto-generated nets, this code will have to be changed. Conversion of SUMO id to
# cr id would have to incorporate type.
cr_id = int(str(sumo_config.default.ID_DICT[type]) + str(len(ids_sumo2cr[type])))
return cr_id
def cr2sumo(cr_id, ids_cr2sumo):
""" gets CommonRoad ID and returns corresponding SUMO ID
:param ids_cr2sumo:
"""
if type(cr_id) == list:
print("id: " + str(cr_id) + ": " + str(ids_cr2sumo['all_ids']))
print("\n")
if cr_id is None:
return None
elif cr_id in ids_cr2sumo['all_ids']:
return ids_cr2sumo['all_ids'][cr_id]
else:
raise ValueError('Commonroad id {0} does not exist.'.format(cr_id))
def sumo2cr(sumo_id, ids_sumo2cr):
""" gets SUMO ID and returns corresponding CommonRoad ID """
if sumo_id is None:
return None
elif sumo_id in ids_sumo2cr['all_ids']:
return ids_sumo2cr['all_ids'][sumo_id]
else:
if sumo_id == "":
warnings.warn('Tried to convert id <empty string>. \
Check if your net file is complete (e. g. having internal-links,...)')
raise ValueError('Sumo id \'%s\' does not exist.' % sumo_id)
class NetError(Exception):
""" Exception raised if there is no net-file or multiple net-files """
def __init__(self, len):
self.len = len
def __str__(self):
if self.len == 0:
return repr('There is no net-file.')
else:
return repr('There are more than one net-files.')
class RouteError(Exception):
""" Exception raised if there is no route-file """
def __str__(self):
return repr('There is no route-file.')
class OutOfLanelet(Exception):
""" Exception raised if the position of the ego vehicle is outside all lanelets """
def __init__(self, position):
self.position = position
def __str__(self):
return repr('The position %s is not part of any lanelet.' % self.position)
import os
import xml.etree.ElementTree as et
import warnings
import sumo_config.default
from sumo_config import plot_params
from typing import List, Tuple
__author__ = "Moritz Klischat"
__copyright__ = "TUM Cyber-Physical Systems Group"
__credits__ = ["ZIM Projekt ZF4086007BZ8"]
__version__ = "1.0.0"
__maintainer__ = "Moritz Klischat"
__email__ = "commonroad-i06@in.tum.de"
__status__ = "Released"
def get_route_files(config_file) -> List[str]:
"""
Returns net-file and route-files specified in the config file.
:param config_file: SUMO config file (.sumocfg)
"""
if not os.path.isfile(config_file):
raise FileNotFoundError(config_file)
tree = et.parse(config_file)
file_directory = os.path.dirname(config_file)
# find route-files
all_route_files = tree.findall('*/route-files')
route_files = []
if len(all_route_files) < 1:
raise RouteError()
for item in all_route_files:
attributes = item.attrib['value'].split(',')
for route in attributes:
route_files.append(os.path.join(file_directory, route))
return route_files
def initialize_id_dicts(id_convention: dict) -> Tuple[dict, dict]:
"""
Creates empty nested dict structure for sumo2cr and cr2sumo dicts from id_convention and returns them.
:param id_convention: dict with mapping from object type to id start number
"""
sumo2cr = {}
cr2sumo = {}
for k in id_convention:
sumo2cr[k] = {}
cr2sumo[k] = {}
sumo2cr['all_ids'] = {}
cr2sumo['all_ids'] = {}
return sumo2cr, cr2sumo
def generate_cr_id(type:str, sumo_id:int, ids_sumo2cr:dict) -> int:
"""
Generates a new commonroad ID without adding it to any ID dictionary.
:param type: one of the keys in params.id_convention; the type defines the first digit of the cr_id
:param sumo_id: id in sumo simulation
:param ids_sumo2cr: dictionary of ids in sumo2cr
"""
if type not in sumo_config.default.ID_DICT:
raise ValueError(
'{0} is not a valid type of id_convention. Only allowed: {1}'.format(type, sumo_config.default.ID_DICT.keys()))
if sumo_id in ids_sumo2cr[type]:
warnings.warn('For this sumo_id there is already a commonroad id. No cr ID is generated')
return ids_sumo2cr[type][sumo_id]
elif sumo_id in ids_sumo2cr['all_ids']:
raise ValueError(
'Two sumo objects of different types seem to have same sumo ID {0}. ID must be unique'.format(sumo_id))
# If this case happens for auto-generated nets, this code will have to be changed. Conversion of SUMO id to
# cr id would have to incorporate type.
cr_id = int(str(sumo_config.default.ID_DICT[type]) + str(len(ids_sumo2cr[type])))
return cr_id
def cr2sumo(cr_id:int, ids_cr2sumo:dict) -> int:
"""