sumo net automatically generated

This commit is contained in:
Joel Moriana 2020-04-10 17:50:33 +02:00 committed by bernat
parent 4ec9723df3
commit 02330cd84d
6 changed files with 129 additions and 111 deletions

View File

@ -87,6 +87,12 @@ class SimulationSynchronization(object):
BridgeHelper.blueprint_library = self.carla.world.get_blueprint_library() BridgeHelper.blueprint_library = self.carla.world.get_blueprint_library()
BridgeHelper.offset = self.sumo.get_net_offset() BridgeHelper.offset = self.sumo.get_net_offset()
# Configuring carla simulation in sync mode.
settings = self.carla.world.get_settings()
settings.synchronous_mode = True
settings.fixed_delta_seconds = self.carla.step_length
self.carla.world.apply_settings(settings)
def tick(self): def tick(self):
""" """
Tick to simulation synchronization Tick to simulation synchronization
@ -225,8 +231,8 @@ def synchronization_loop(args):
Entry point for sumo-carla co-simulation. Entry point for sumo-carla co-simulation.
""" """
try: try:
sumo_simulation = SumoSimulation(args.sumo_host, args.sumo_port, args.step_length, sumo_simulation = SumoSimulation(args.sumo_cfg_file, args.step_length, args.sumo_host,
args.sumo_cfg_file, args.sumo_gui) args.sumo_port, args.sumo_gui)
carla_simulation = CarlaSimulation(args.carla_host, args.carla_port, args.step_length) carla_simulation = CarlaSimulation(args.carla_host, args.carla_port, args.step_length)
synchronization = SimulationSynchronization(sumo_simulation, carla_simulation, synchronization = SimulationSynchronization(sumo_simulation, carla_simulation,
@ -254,6 +260,7 @@ def synchronization_loop(args):
if __name__ == '__main__': if __name__ == '__main__':
argparser = argparse.ArgumentParser(description=__doc__) argparser = argparse.ArgumentParser(description=__doc__)
argparser.add_argument('sumo_cfg_file', type=str, help='sumo configuration file')
argparser.add_argument('--carla-host', argparser.add_argument('--carla-host',
metavar='H', metavar='H',
default='127.0.0.1', default='127.0.0.1',
@ -272,14 +279,7 @@ if __name__ == '__main__':
default=None, default=None,
type=int, type=int,
help='TCP port to liston to (default: 8813)') help='TCP port to liston to (default: 8813)')
argparser.add_argument('-c', argparser.add_argument('--sumo-gui', action='store_true', help='run the gui version of sumo')
'--sumo-cfg-file',
default=None,
type=str,
help='sumo configuration file')
argparser.add_argument('--sumo-gui',
action='store_true',
help='run the gui version of sumo')
argparser.add_argument('--step-length', argparser.add_argument('--step-length',
default=0.05, default=0.05,
type=float, type=float,

View File

@ -12,13 +12,13 @@
# ================================================================================================== # ==================================================================================================
import argparse import argparse
import datetime
import json import json
import logging import logging
import time
import random import random
import re import re
import shutil import shutil
import tempfile
import time
import lxml.etree as ET # pylint: disable=wrong-import-position import lxml.etree as ET # pylint: disable=wrong-import-position
@ -59,16 +59,17 @@ from sumo_integration.sumo_simulation import SumoSimulation # pylint: disable=w
from run_synchronization import SimulationSynchronization # pylint: disable=wrong-import-position from run_synchronization import SimulationSynchronization # pylint: disable=wrong-import-position
from util.netconvert_carla import netconvert_carla
# ================================================================================================== # ==================================================================================================
# -- main ------------------------------------------------------------------------------------------ # -- main ------------------------------------------------------------------------------------------
# ================================================================================================== # ==================================================================================================
def write_sumocfg_xml(town_name, tmp_path, net_file, vtypes_file, viewsettings_file): def write_sumocfg_xml(cfg_file, net_file, vtypes_file, viewsettings_file):
""" """
Writes sumo configuration xml file. Writes sumo configuration xml file.
""" """
root = ET.Element('configuration') root = ET.Element('configuration')
input_tag = ET.SubElement(root, 'input') input_tag = ET.SubElement(root, 'input')
@ -79,58 +80,51 @@ def write_sumocfg_xml(town_name, tmp_path, net_file, vtypes_file, viewsettings_f
ET.SubElement(gui_tag, 'gui-settings-file', {'value': viewsettings_file}) ET.SubElement(gui_tag, 'gui-settings-file', {'value': viewsettings_file})
tree = ET.ElementTree(root) tree = ET.ElementTree(root)
filename = os.path.join(tmp_path, town_name + '.sumocfg') tree.write(cfg_file, pretty_print=True, encoding='UTF-8', xml_declaration=True)
tree.write(filename, pretty_print=True, encoding='UTF-8', xml_declaration=True)
return filename
def get_sumo_files(town_name, tmp_path):
"""
Returns sumo configuration file and sumo net.
"""
base_path = os.path.dirname(os.path.realpath(__file__))
net_file = os.path.join(base_path, 'examples', 'net', town_name + '.net.xml')
vtypes_file = os.path.join(base_path, 'examples', 'carlavtypes.rou.xml')
viewsettings_file = os.path.join(base_path, 'examples', 'viewsettings.xml')
cfg_file = write_sumocfg_xml(town_name, tmp_path, net_file, vtypes_file, viewsettings_file)
return cfg_file, net_file
def main(args): def main(args):
# Creating temporal folder to save configuration file. # Temporal folder to save intermediate files.
tmp_path = 'spawn_npc_sumo-{date:%Y-%m-%d_%H-%M-%S-%f}'.format(date=datetime.datetime.now()) tmpdir = tempfile.mkdtemp()
if not os.path.exists(tmp_path):
os.mkdir(tmp_path) # ----------------
# carla simulation
# ----------------
carla_simulation = CarlaSimulation(args.host, args.port, args.step_length)
world = carla_simulation.client.get_world()
current_map = carla_simulation.client.get_world().get_map()
xodr_file = os.path.join(tmpdir, current_map.name + '.xodr')
current_map.save_to_disk(xodr_file)
# ---------------
# sumo simulation
# ---------------
net_file = os.path.join(tmpdir, current_map.name + '.net.xml')
netconvert_carla(xodr_file, net_file, guess_tls=True)
basedir = os.path.dirname(os.path.realpath(__file__))
cfg_file = os.path.join(tmpdir, current_map.name + '.sumocfg')
vtypes_file = os.path.join(basedir, 'examples', 'carlavtypes.rou.xml')
viewsettings_file = os.path.join(basedir, 'examples', 'viewsettings.xml')
write_sumocfg_xml(cfg_file, net_file, vtypes_file, viewsettings_file)
sumo_net = sumolib.net.readNet(net_file)
sumo_simulation = SumoSimulation(cfg_file,
args.step_length,
host=None,
port=None,
sumo_gui=args.sumo_gui)
# ---------------
# synchronization
# ---------------
synchronization = SimulationSynchronization(sumo_simulation, carla_simulation, args.tls_manager,
args.sync_vehicle_color, args.sync_vehicle_lights)
try: try:
# ----------------
# carla simulation
# ----------------
carla_simulation = CarlaSimulation(args.host, args.port, args.step_length)
world = carla_simulation.client.get_world()
current_map = world.get_map().name
if current_map not in ('Town01', 'Town04', 'Town05'):
raise RuntimeError('This script does not support {} yet.'.format(current_map))
# ---------------
# sumo simulation
# ---------------
cfg_file, net_file = get_sumo_files(current_map, tmp_path)
sumo_net = sumolib.net.readNet(net_file)
sumo_simulation = SumoSimulation(None, None, args.step_length, cfg_file, args.sumo_gui)
# ---------------
# synchronization
# ---------------
synchronization = SimulationSynchronization(sumo_simulation, carla_simulation,
args.tls_manager, args.sync_vehicle_color,
args.sync_vehicle_lights)
# ---------- # ----------
# Blueprints # Blueprints
# ---------- # ----------
@ -183,10 +177,12 @@ def main(args):
if index == (len(route) - 1): if index == (len(route) - 1):
current_edge = sumo_net.getEdge(route[index]) current_edge = sumo_net.getEdge(route[index])
next_edge = random.choice(list(current_edge.getAllowedOutgoing(vclass).keys())) available_edges = list(current_edge.getAllowedOutgoing(vclass).keys())
if available_edges:
next_edge = random.choice(available_edges)
new_route = [current_edge.getID(), next_edge.getID()] new_route = [current_edge.getID(), next_edge.getID()]
traci.vehicle.setRoute(vehicle_id, new_route) traci.vehicle.setRoute(vehicle_id, new_route)
end = time.time() end = time.time()
elapsed = end - start elapsed = end - start
@ -197,10 +193,9 @@ def main(args):
logging.info('Cancelled by user.') logging.info('Cancelled by user.')
finally: finally:
if os.path.exists(tmp_path): if os.path.exists(tmpdir):
shutil.rmtree(tmp_path) shutil.rmtree(tmpdir)
logging.info('Destroying %d sumo vehicles.', traci.vehicle.getIDCount())
synchronization.close() synchronization.close()
@ -239,9 +234,7 @@ if __name__ == '__main__':
metavar='PATTERN', metavar='PATTERN',
default='walker.pedestrian.*', default='walker.pedestrian.*',
help='pedestrians filter (default: "walker.pedestrian.*")') help='pedestrians filter (default: "walker.pedestrian.*")')
argparser.add_argument('--sumo-gui', argparser.add_argument('--sumo-gui', action='store_true', help='run the gui version of sumo')
action='store_true',
help='run the gui version of sumo')
argparser.add_argument('--step-length', argparser.add_argument('--step-length',
default=0.05, default=0.05,
type=float, type=float,
@ -272,4 +265,4 @@ if __name__ == '__main__':
else: else:
logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO) logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO)
main(args) main(args)

View File

@ -32,12 +32,7 @@ class CarlaSimulation(object):
self.world = self.client.get_world() self.world = self.client.get_world()
self.blueprint_library = self.world.get_blueprint_library() self.blueprint_library = self.world.get_blueprint_library()
self.step_length = step_length
# Configuring carla simulation in sync mode.
settings = self.world.get_settings()
settings.synchronous_mode = True
settings.fixed_delta_seconds = step_length
self.world.apply_settings(settings)
# The following sets contain updated information for the current frame. # The following sets contain updated information for the current frame.
self._active_actors = set() self._active_actors = set()

View File

@ -14,6 +14,7 @@
import collections import collections
import enum import enum
import logging import logging
import os
import carla # pylint: disable=import-error import carla # pylint: disable=import-error
import sumolib # pylint: disable=import-error import sumolib # pylint: disable=import-error
@ -21,6 +22,8 @@ import traci # pylint: disable=import-error
from .constants import INVALID_ACTOR_ID from .constants import INVALID_ACTOR_ID
import lxml.etree as ET # pylint: disable=import-error
# ================================================================================================== # ==================================================================================================
# -- sumo definitions ------------------------------------------------------------------------------ # -- sumo definitions ------------------------------------------------------------------------------
# ================================================================================================== # ==================================================================================================
@ -99,7 +102,7 @@ class SumoActorClass(enum.Enum):
SumoActor = collections.namedtuple('SumoActor', 'type_id vclass transform signals extent color') SumoActor = collections.namedtuple('SumoActor', 'type_id vclass transform signals extent color')
# ================================================================================================== # ==================================================================================================
# -- sumo simulation ------------------------------------------------------------------------------- # -- sumo traffic lights ---------------------------------------------------------------------------
# ================================================================================================== # ==================================================================================================
@ -277,11 +280,35 @@ class SumoTLManager(object):
self._current_phase[tl_id] = current_phase self._current_phase[tl_id] = current_phase
# ==================================================================================================
# -- sumo simulation -------------------------------------------------------------------------------
# ==================================================================================================
def _get_sumo_net(cfg_file):
"""
Returns sumo net.
This method reads the sumo configuration file and retrieve the sumo net filename to create the
net.
"""
cfg_file = os.path.join(os.getcwd(), cfg_file)
tree = ET.parse(cfg_file)
tag = tree.find('//net-file')
if tag is None:
return None
net_file = os.path.join(os.path.dirname(cfg_file), tag.get('value'))
logging.debug('Reading net file: %s', net_file)
sumo_net = traci.sumolib.net.readNet(net_file)
return sumo_net
class SumoSimulation(object): class SumoSimulation(object):
""" """
SumoSimulation is responsible for the management of the sumo simulation. SumoSimulation is responsible for the management of the sumo simulation.
""" """
def __init__(self, host, port, step_length, cfg_file, sumo_gui=False): def __init__(self, cfg_file, step_length, host=None, port=None, sumo_gui=False):
if sumo_gui is True: if sumo_gui is True:
sumo_binary = sumolib.checkBinary('sumo-gui') sumo_binary = sumolib.checkBinary('sumo-gui')
else: else:
@ -303,6 +330,9 @@ class SumoSimulation(object):
logging.info('Connection to sumo server. Host: %s Port: %s', host, port) logging.info('Connection to sumo server. Host: %s Port: %s', host, port)
traci.init(host=host, port=port) traci.init(host=host, port=port)
# Retrieving net from configuration file.
self.net = _get_sumo_net(cfg_file)
# Creating a random route to be able to spawn carla actors. # Creating a random route to be able to spawn carla actors.
traci.route.add("carla_route", [traci.edge.getIDList()[0]]) traci.route.add("carla_route", [traci.edge.getIDList()[0]])
@ -349,20 +379,11 @@ class SumoSimulation(object):
""" """
traci.vehicle.unsubscribe(actor_id) traci.vehicle.unsubscribe(actor_id)
@staticmethod def get_net_offset(self):
def get_net_offset():
""" """
Accessor for sumo net offset. Accessor for sumo net offset.
""" """
offset = traci.simulation.convertGeo(0, 0) return self.net.getLocationOffset()
return (-offset[0], -offset[1])
@staticmethod
def get_step_length():
"""
Accessor for sumo simulation step length.
"""
return traci.simulation.getDeltaT()
@staticmethod @staticmethod
def get_actor(actor_id): def get_actor(actor_id):

View File

View File

@ -17,10 +17,10 @@ the net and inserts, manually, the traffic light landmarks retrieved from the op
import argparse import argparse
import bisect import bisect
import collections import collections
import datetime
import logging import logging
import shutil import shutil
import subprocess import subprocess
import tempfile
import lxml.etree as ET # pylint: disable=import-error import lxml.etree as ET # pylint: disable=import-error
@ -362,29 +362,30 @@ class SumoTrafficLight(object):
# ================================================================================================== # ==================================================================================================
def netconvert_carla(args, tmp_path): def _netconvert_carla_impl(xodr_file, output, tmpdir, guess_tls=False):
""" """
Generates sumo net. Implements netconvert carla.
""" """
# ---------- # ----------
# netconvert # netconvert
# ---------- # ----------
tmp_file = os.path.splitext(os.path.basename(args.xodr_file))[0] basename = os.path.splitext(os.path.basename(xodr_file))[0]
tmp_sumo_net = os.path.join(tmp_path, tmp_file + '.net.xml') tmp_sumo_net = os.path.join(tmpdir, basename + '.net.xml')
try: try:
basedir = os.path.dirname(os.path.realpath(__file__))
result = subprocess.call(['netconvert', result = subprocess.call(['netconvert',
'--opendrive', args.xodr_file, '--opendrive', xodr_file,
'--output-file', tmp_sumo_net, '--output-file', tmp_sumo_net,
'--geometry.min-radius.fix', '--geometry.min-radius.fix',
'--geometry.remove', '--geometry.remove',
'--opendrive.curve-resolution', '1', '--opendrive.curve-resolution', '1',
'--opendrive.import-all-lanes', '--opendrive.import-all-lanes',
'--type-files', 'data/opendrive_netconvert.typ.xml', '--type-files', os.path.join(basedir, 'data/opendrive_netconvert.typ.xml'),
# Necessary to link odr and sumo ids. # Necessary to link odr and sumo ids.
'--output.original-names', '--output.original-names',
# Discard loading traffic lights as them will be inserted manually afterwards. # Discard loading traffic lights as them will be inserted manually afterwards.
'--tls.discard-loaded', 'true' '--tls.discard-loaded', 'true',
]) ])
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
raise RuntimeError('There was an error when executing netconvert.') raise RuntimeError('There was an error when executing netconvert.')
@ -401,7 +402,7 @@ def netconvert_carla(args, tmp_path):
# --------- # ---------
# Carla map # Carla map
# --------- # ---------
with open(args.xodr_file, 'r') as f: with open(xodr_file, 'r') as f:
carla_map = carla.Map('netconvert', str(f.read())) carla_map = carla.Map('netconvert', str(f.read()))
# --------- # ---------
@ -437,7 +438,7 @@ def netconvert_carla(args, tmp_path):
tls[tlid] = SumoTrafficLight(tlid) tls[tlid] = SumoTrafficLight(tlid)
tl = tls[tlid] tl = tls[tlid]
if args.guess_tls: if guess_tls:
for from_edge, from_lane in sumo_topology.get_incoming(road_id, lane_id): for from_edge, from_lane in sumo_topology.get_incoming(road_id, lane_id):
successors = sumo_topology.get_successors(from_edge, from_lane) successors = sumo_topology.get_successors(from_edge, from_lane)
for to_edge, to_lane in successors: for to_edge, to_lane in successors:
@ -503,25 +504,33 @@ def netconvert_carla(args, tmp_path):
connection.from_road, connection.to_road, connection.from_lane, connection.from_road, connection.to_road, connection.from_lane,
connection.to_lane)) connection.to_lane))
tree.write(args.output, pretty_print=True, encoding='UTF-8', xml_declaration=True) tree.write(output, pretty_print=True, encoding='UTF-8', xml_declaration=True)
def netconvert_carla(xodr_file, output, guess_tls=False):
"""
Generates sumo net.
:param xodr_file: opendrive file (*.xodr)
:param output: output file (*.net.xml)
:param guess_tls: guess traffic lights at intersections.
:returns: path to the generated sumo net.
"""
try:
tmpdir = tempfile.mkdtemp()
_netconvert_carla_impl(xodr_file, output, tmpdir, guess_tls)
finally:
if os.path.exists(tmpdir):
shutil.rmtree(tmpdir)
if __name__ == '__main__': if __name__ == '__main__':
argparser = argparse.ArgumentParser(description=__doc__) argparser = argparse.ArgumentParser(description=__doc__)
argparser.add_argument('xodr_file', help='open drive file (*.xodr') argparser.add_argument('xodr_file', help='opendrive file (*.xodr')
argparser.add_argument('--output', '-o', type=str, help='output file (*.net.xml)') argparser.add_argument('--output', '-o', type=str, help='output file (*.net.xml)')
argparser.add_argument('--guess-tls', argparser.add_argument('--guess-tls',
action='store_true', action='store_true',
help='guess traffic lights at intersections (default: False)') help='guess traffic lights at intersections (default: False)')
args = argparser.parse_args() args = argparser.parse_args()
try: netconvert_carla(args.xodr_file, args.output, args.guess_tls)
tmp_path = 'tmp-{date:%Y-%m-%d_%H-%M-%S-%f}'.format(date=datetime.datetime.now())
if not os.path.exists(tmp_path):
os.mkdir(tmp_path)
netconvert_carla(args, tmp_path)
finally:
if os.path.exists(tmp_path):
shutil.rmtree(tmp_path)