fixing codacy warnings

This commit is contained in:
Joel Moriana 2020-03-05 16:49:16 +01:00 committed by Marc Garcia Puig
parent d429a22978
commit 708f3ccfe3
7 changed files with 406 additions and 349 deletions

View File

@ -10,9 +10,17 @@
Script to integrate CARLA and SUMO simulations Script to integrate CARLA and SUMO simulations
""" """
# ============================================================================== # ==================================================================================================
# -- find carla module --------------------------------------------------------- # -- imports ---------------------------------------------------------------------------------------
# ============================================================================== # ==================================================================================================
import argparse
import logging
import time
# ==================================================================================================
# -- find carla module -----------------------------------------------------------------------------
# ==================================================================================================
import glob import glob
import os import os
@ -26,34 +34,33 @@ try:
except IndexError: except IndexError:
pass pass
# ============================================================================== # ==================================================================================================
# -- find traci module --------------------------------------------------------- # -- find traci module -----------------------------------------------------------------------------
# ============================================================================== # ==================================================================================================
if 'SUMO_HOME' in os.environ: if 'SUMO_HOME' in os.environ:
tools = os.path.join(os.environ['SUMO_HOME'], 'tools') sys.path.append(os.path.join(os.environ['SUMO_HOME'], 'tools'))
sys.path.append(tools)
else: else:
sys.exit("please declare environment variable 'SUMO_HOME'") sys.exit("please declare environment variable 'SUMO_HOME'")
# ============================================================================== # ==================================================================================================
# -- imports ------------------------------------------------------------------- # -- sumo integration importants -------------------------------------------------------------------
# ============================================================================== # ==================================================================================================
import argparse from sumo_integration.bridge_helper import BridgeHelper # pylint: disable=wrong-import-position
import logging from sumo_integration.carla_simulation import CarlaSimulation # pylint: disable=wrong-import-position
import time from sumo_integration.constants import INVALID_ACTOR_ID # pylint: disable=wrong-import-position
from sumo_integration.sumo_simulation import SumoSimulation # pylint: disable=wrong-import-position
from sumo_integration.bridge_helper import BridgeHelper # ==================================================================================================
from sumo_integration.carla_simulation import CarlaSimulation # -- simulation synchro ----------------------------------------------------------------------------
from sumo_integration.constants import * # ==================================================================================================
from sumo_integration.sumo_simulation import SumoSimulation
# ==============================================================================
# -- simulation synchro --------------------------------------------------------
# ==============================================================================
def main(args): def main(args):
"""
Entry point sumo-carla co-simulation.
"""
sumo = SumoSimulation(args) sumo = SumoSimulation(args)
carla = CarlaSimulation(args) carla = CarlaSimulation(args)
@ -61,8 +68,8 @@ def main(args):
sumo2carla_ids = {} # Contains only actors controlled by sumo. sumo2carla_ids = {} # Contains only actors controlled by sumo.
carla2sumo_ids = {} # Contains only actors controlled by carla. carla2sumo_ids = {} # Contains only actors controlled by carla.
BridgeHelper._blueprint_library = carla.world.get_blueprint_library() BridgeHelper.blueprint_library = carla.world.get_blueprint_library()
BridgeHelper._offset = sumo.get_net_offset() BridgeHelper.offset = sumo.get_net_offset()
try: try:
while True: while True:
@ -81,7 +88,8 @@ def main(args):
carla_blueprint = BridgeHelper.get_carla_blueprint(sumo_actor) carla_blueprint = BridgeHelper.get_carla_blueprint(sumo_actor)
if carla_blueprint is not None: if carla_blueprint is not None:
carla_transform = BridgeHelper.get_carla_transform(sumo_actor.transform, sumo_actor.extent) carla_transform = BridgeHelper.get_carla_transform(
sumo_actor.transform, sumo_actor.extent)
carla_actor_id = carla.spawn_actor(carla_blueprint, carla_transform) carla_actor_id = carla.spawn_actor(carla_blueprint, carla_transform)
if carla_actor_id != INVALID_ACTOR_ID: if carla_actor_id != INVALID_ACTOR_ID:
@ -101,9 +109,11 @@ def main(args):
sumo_actor = sumo.get_actor(sumo_actor_id) sumo_actor = sumo.get_actor(sumo_actor_id)
carla_actor = carla.get_actor(carla_actor_id) carla_actor = carla.get_actor(carla_actor_id)
carla_transform = BridgeHelper.get_carla_transform(sumo_actor.transform, sumo_actor.extent) carla_transform = BridgeHelper.get_carla_transform(sumo_actor.transform,
sumo_actor.extent)
if args.sync_vehicle_lights: if args.sync_vehicle_lights:
carla_lights = BridgeHelper.get_carla_lights_state(carla_actor.get_light_state(), sumo_actor.signals) carla_lights = BridgeHelper.get_carla_lights_state(
carla_actor.get_light_state(), sumo_actor.signals)
else: else:
carla_lights = None carla_lights = None
@ -138,11 +148,13 @@ def main(args):
carla_actor = carla.get_actor(carla_actor_id) carla_actor = carla.get_actor(carla_actor_id)
sumo_actor = sumo.get_actor(sumo_actor_id) sumo_actor = sumo.get_actor(sumo_actor_id)
sumo_transform = BridgeHelper.get_sumo_transform(carla_actor.get_transform(), carla_actor.bounding_box.extent) sumo_transform = BridgeHelper.get_sumo_transform(carla_actor.get_transform(),
carla_actor.bounding_box.extent)
if args.sync_vehicle_lights: if args.sync_vehicle_lights:
carla_lights = carla.get_actor_light_state(carla_actor_id) carla_lights = carla.get_actor_light_state(carla_actor_id)
if carla_lights is not None: if carla_lights is not None:
sumo_lights = BridgeHelper.get_sumo_lights_state(sumo_actor.signals, carla_lights) sumo_lights = BridgeHelper.get_sumo_lights_state(
sumo_actor.signals, carla_lights)
else: else:
sumo_lights = None sumo_lights = None
else: else:
@ -158,9 +170,6 @@ def main(args):
except KeyboardInterrupt: except KeyboardInterrupt:
logging.info('Cancelled by user.') logging.info('Cancelled by user.')
except Exception as error:
logging.error('Synchronization failed. {}'.format(error))
finally: finally:
logging.info('Cleaning up synchronization') logging.info('Cleaning up synchronization')
@ -180,68 +189,49 @@ def main(args):
# Closing sumo client. # Closing sumo client.
sumo.close() sumo.close()
if __name__ == '__main__': if __name__ == '__main__':
argparser = argparse.ArgumentParser( argparser = argparse.ArgumentParser(description=__doc__)
description=__doc__ argparser.add_argument('--carla-host',
)
argparser.add_argument(
'--carla-host',
metavar='H', metavar='H',
default='127.0.0.1', default='127.0.0.1',
help='IP of the carla host server (default: 127.0.0.1)' help='IP of the carla host server (default: 127.0.0.1)')
) argparser.add_argument('--carla-port',
argparser.add_argument(
'--carla-port',
metavar='P', metavar='P',
default=2000, default=2000,
type=int, type=int,
help='TCP port to listen to (default: 2000)' help='TCP port to listen to (default: 2000)')
) argparser.add_argument('--sumo-host',
argparser.add_argument(
'--sumo-host',
metavar='H', metavar='H',
default=None, default=None,
help='IP of the sumo host server (default: 127.0.0.1)' help='IP of the sumo host server (default: 127.0.0.1)')
) argparser.add_argument('--sumo-port',
argparser.add_argument(
'--sumo-port',
metavar='P', metavar='P',
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-cfg-file',
'-c', '--sumo-cfg-file',
default=None, default=None,
type=str, type=str,
help='sumo configuration file' help='sumo configuration file')
) argparser.add_argument('--sumo-gui',
argparser.add_argument(
'--sumo-gui',
default=True, default=True,
help='run the gui version of sumo (default: True)' help='run the gui version of sumo (default: True)')
) argparser.add_argument('--step-length',
argparser.add_argument(
'--step-length',
default=0.05, default=0.05,
type=float, type=float,
help='set fixed delta seconds (default: 0.05s)' help='set fixed delta seconds (default: 0.05s)')
)
argparser.add_argument( argparser.add_argument(
'--sync-vehicle-lights', '--sync-vehicle-lights',
action='store_true', action='store_true',
help='synchronize vehicle lights state between simulations (default: False)' help='synchronize vehicle lights state between simulations (default: False)')
) argparser.add_argument('--debug', action='store_true', help='enable debug messages')
argparser.add_argument( arguments = argparser.parse_args()
'--debug',
action='store_true',
help='enable debug messages'
)
args = argparser.parse_args()
if args.debug: if arguments.debug:
logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.DEBUG) logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.DEBUG)
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(arguments)

View File

@ -6,38 +6,44 @@
# This work is licensed under the terms of the MIT license. # This work is licensed under the terms of the MIT license.
# For a copy, see <https://opensource.org/licenses/MIT>. # For a copy, see <https://opensource.org/licenses/MIT>.
# ============================================================================== """ This module provides a helper for the co-simulation between sumo and carla ."""
# -- imports -------------------------------------------------------------------
# ============================================================================== # ==================================================================================================
# -- imports ---------------------------------------------------------------------------------------
# ==================================================================================================
import json import json
import logging import logging
import math import math
import random import random
import carla import carla # pylint: disable=import-error
import traci import traci # pylint: disable=import-error
from .sumo_simulation import SumoVehSignal from .sumo_simulation import SumoVehSignal
# ============================================================================== # ==================================================================================================
# -- Bridge helper (SUMO <=> CARLA) -------------------------------------------- # -- Bridge helper (SUMO <=> CARLA) ----------------------------------------------------------------
# ============================================================================== # ==================================================================================================
class BridgeHelper(object): class BridgeHelper(object):
"""
BridgeHelper provides methos to ease the co-simulation between sumo and carla.
"""
_blueprint_library = None blueprint_library = []
_offset = (0, 0) offset = (0, 0)
with open('data/vtypes.json') as f: with open('data/vtypes.json') as f:
_VTYPES = json.load(f)['carla_blueprints'] _VTYPES = json.load(f)['carla_blueprints']
@staticmethod @staticmethod
def get_carla_transform(in_sumo_transform, extent): def get_carla_transform(in_sumo_transform, extent):
"""Returns carla transform based on sumo transform.
""" """
offset = BridgeHelper._offset Returns carla transform based on sumo transform.
"""
offset = BridgeHelper.offset
in_location = in_sumo_transform.location in_location = in_sumo_transform.location
in_rotation = in_sumo_transform.rotation in_rotation = in_sumo_transform.rotation
@ -45,93 +51,80 @@ class BridgeHelper(object):
# (http://sumo.sourceforge.net/userdoc/Purgatory/Vehicle_Values.html#angle) # (http://sumo.sourceforge.net/userdoc/Purgatory/Vehicle_Values.html#angle)
yaw = -1 * in_rotation.yaw + 90 yaw = -1 * in_rotation.yaw + 90
length = 2.0 * extent.x length = 2.0 * extent.x
out_location = ( out_location = (in_location.x - math.cos(math.radians(yaw)) * (length / 2.0),
in_location.x - math.cos(math.radians(yaw)) * (length / 2.0), in_location.y - math.sin(math.radians(yaw)) * (length / 2.0), in_location.z)
in_location.y - math.sin(math.radians(yaw)) * (length / 2.0),
in_location.z
)
out_rotation = (in_rotation.pitch, in_rotation.yaw, in_rotation.roll) out_rotation = (in_rotation.pitch, in_rotation.yaw, in_rotation.roll)
# Applying offset sumo-carla net. # Applying offset sumo-carla net.
out_location = ( out_location = (out_location[0] - offset[0], out_location[1] - offset[1], out_location[2])
out_location[0] - offset[0],
out_location[1] - offset[1],
out_location[2]
)
out_rotation = out_rotation
# Transform to carla reference system (left-handed system). # Transform to carla reference system (left-handed system).
return carla.Transform( return carla.Transform(
carla.Location(out_location[0], -out_location[1], out_location[2]), carla.Location(out_location[0], -out_location[1], out_location[2]),
carla.Rotation(out_rotation[0], out_rotation[1] - 90, out_rotation[2]) carla.Rotation(out_rotation[0], out_rotation[1] - 90, out_rotation[2]))
)
@staticmethod @staticmethod
def get_sumo_transform(in_carla_transform, extent): def get_sumo_transform(in_carla_transform, extent):
"""Returns sumo transform based on carla transform.
""" """
offset = BridgeHelper._offset Returns sumo transform based on carla transform.
"""
offset = BridgeHelper.offset
in_location = in_carla_transform.location in_location = in_carla_transform.location
in_rotation = in_carla_transform.rotation in_rotation = in_carla_transform.rotation
# From center to front-center-bumper (carla reference system). # From center to front-center-bumper (carla reference system).
yaw = -1 * in_rotation.yaw yaw = -1 * in_rotation.yaw
length = 2.0 * extent.x length = 2.0 * extent.x
out_location = ( out_location = (in_location.x + math.cos(math.radians(yaw)) * (length / 2.0),
in_location.x + math.cos(math.radians(yaw)) * (length / 2.0), in_location.y - math.sin(math.radians(yaw)) * (length / 2.0), in_location.z)
in_location.y - math.sin(math.radians(yaw)) * (length / 2.0),
in_location.z
)
out_rotation = (in_rotation.pitch, in_rotation.yaw, in_rotation.roll) out_rotation = (in_rotation.pitch, in_rotation.yaw, in_rotation.roll)
# Applying offset carla-sumo net # Applying offset carla-sumo net
out_location = ( out_location = (out_location[0] + offset[0], out_location[1] - offset[1], out_location[2])
out_location[0] + offset[0],
out_location[1] - offset[1],
out_location[2]
)
out_rotation = out_rotation
# Transform to sumo reference system. # Transform to sumo reference system.
return carla.Transform( return carla.Transform(
carla.Location(out_location[0], -out_location[1], out_location[2]), carla.Location(out_location[0], -out_location[1], out_location[2]),
carla.Rotation(out_rotation[0], out_rotation[1] + 90, out_rotation[2]) carla.Rotation(out_rotation[0], out_rotation[1] + 90, out_rotation[2]))
)
@staticmethod @staticmethod
def _get_recommended_carla_blueprint(sumo_actor): def _get_recommended_carla_blueprint(sumo_actor):
"""Returns an appropriate blueprint based on the given sumo actor. """
Returns an appropriate blueprint based on the given sumo actor.
""" """
vclass = sumo_actor.vclass.value vclass = sumo_actor.vclass.value
blueprints = [] blueprints = []
for blueprint in BridgeHelper._blueprint_library: for blueprint in BridgeHelper.blueprint_library:
if blueprint.id in BridgeHelper._VTYPES and \ if blueprint.id in BridgeHelper._VTYPES and \
BridgeHelper._VTYPES[blueprint.id]['vClass'] == vclass: BridgeHelper._VTYPES[blueprint.id]['vClass'] == vclass:
blueprints.append(blueprint) blueprints.append(blueprint)
if len(blueprints) == 0: if not blueprints:
return None return None
return random.choice(blueprints) return random.choice(blueprints)
@staticmethod @staticmethod
def get_carla_blueprint(sumo_actor): def get_carla_blueprint(sumo_actor):
"""Returns an appropriate blueprint based on the received sumo actor.
""" """
blueprint_library = BridgeHelper._blueprint_library Returns an appropriate blueprint based on the received sumo actor.
"""
blueprint_library = BridgeHelper.blueprint_library
type_id = sumo_actor.type_id type_id = sumo_actor.type_id
if type_id in [bp.id for bp in blueprint_library]: if type_id in [bp.id for bp in blueprint_library]:
blueprint = blueprint_library.filter(type_id)[0] blueprint = blueprint_library.filter(type_id)[0]
logging.debug('[BridgeHelper] sumo vtype {} found in carla blueprints'.format(type_id)) logging.debug('[BridgeHelper] sumo vtype %s found in carla blueprints', type_id)
else: else:
blueprint = BridgeHelper._get_recommended_carla_blueprint(sumo_actor) blueprint = BridgeHelper._get_recommended_carla_blueprint(sumo_actor)
if blueprint is not None: if blueprint is not None:
logging.warning( logging.warning(
'sumo vtype {} not found in carla blueprints. The following blueprint will be used: {}'.format(type_id, blueprint.id)) 'sumo vtype %s not found in carla. The following blueprint will be used: %s'
, type_id, blueprint.id)
else: else:
logging.error('sumo vtype {} not supported. No vehicle will be spawned in carla'.format(type_id)) logging.error(
'sumo vtype %s not supported. No vehicle will be spawned in carla', type_id)
return None return None
if blueprint.has_attribute('color'): if blueprint.has_attribute('color'):
@ -139,26 +132,23 @@ class BridgeHelper(object):
blueprint.set_attribute('color', color) blueprint.set_attribute('color', color)
if blueprint.has_attribute('driver_id'): if blueprint.has_attribute('driver_id'):
driver_id = random.choice( driver_id = random.choice(blueprint.get_attribute('driver_id').recommended_values)
blueprint.get_attribute('driver_id').recommended_values)
blueprint.set_attribute('driver_id', driver_id) blueprint.set_attribute('driver_id', driver_id)
blueprint.set_attribute('role_name', 'sumo_driver') blueprint.set_attribute('role_name', 'sumo_driver')
logging.debug('''[BridgeHelper] sumo vtype {vtype} will be spawned in carla with the following attributes: logging.debug(
\tblueprint: {bp_id:} '''[BridgeHelper] sumo vtype %s will be spawned in carla with the following attributes:
\tcolor: {color:}'''.format( \tblueprint: %s
vtype=type_id, \tcolor: %s''', type_id, blueprint.id,
bp_id=blueprint.id, sumo_actor.color if blueprint.has_attribute('color') else (-1, -1, -1))
color=sumo_actor.color if blueprint.has_attribute('color') else (-1, -1, -1)
)
)
return blueprint return blueprint
@staticmethod @staticmethod
def _create_sumo_vtype(carla_actor): def _create_sumo_vtype(carla_actor):
"""Creates an appropriate vtype based on the given carla_actor. """
Creates an appropriate vtype based on the given carla_actor.
""" """
type_id = carla_actor.type_id type_id = carla_actor.type_id
attrs = carla_actor.attributes attrs = carla_actor.attributes
@ -186,47 +176,46 @@ class BridgeHelper(object):
traci.vehicletype.setWidth(type_id, 2.0 * extent.y) traci.vehicletype.setWidth(type_id, 2.0 * extent.y)
traci.vehicletype.setHeight(type_id, 2.0 * extent.z) traci.vehicletype.setHeight(type_id, 2.0 * extent.z)
logging.debug('''[BridgeHelper] blueprint{bp_id} not found in sumo vtypes logging.debug(
\tdefault vtype: {dvtype:} '''[BridgeHelper] blueprint %s not found in sumo vtypes
\tvtype: {vtype:} \tdefault vtype: %s
\tclass: {_class:} \tvtype: %s
\tshape: {shape:} \tclass: %s
\tcolor: {color:} \tshape: %s
\tlenght: {lenght:} \tcolor: %s
\twidth: {width:} \tlenght: %s
\theight: {height:}'''.format( \twidth: %s
bp_id=type_id, \theight: %s''', type_id,
dvtype='DEFAULT_BIKETYPE' if int(attrs['number_of_wheels']) == 2 else 'DEFAULT_VEHTYPE', 'DEFAULT_BIKETYPE' if int(attrs['number_of_wheels']) == 2 else 'DEFAULT_VEHTYPE',
vtype=type_id, type_id, traci.vehicletype.getVehicleClass(type_id),
_class=traci.vehicletype.getVehicleClass(type_id), traci.vehicletype.getShapeClass(type_id), traci.vehicletype.getColor(type_id),
shape=traci.vehicletype.getShapeClass(type_id), traci.vehicletype.getLength(type_id), traci.vehicletype.getWidth(type_id),
color=traci.vehicletype.getColor(type_id), traci.vehicletype.getHeight(type_id))
lenght=traci.vehicletype.getLength(type_id),
width=traci.vehicletype.getWidth(type_id),
height=traci.vehicletype.getHeight(type_id)
)
)
return type_id return type_id
@staticmethod @staticmethod
def get_sumo_vtype(carla_actor): def get_sumo_vtype(carla_actor):
"""Returns an appropriate vtype based on the type id and attributes. """
Returns an appropriate vtype based on the type id and attributes.
""" """
type_id = carla_actor.type_id type_id = carla_actor.type_id
if not type_id.startswith('vehicle'): if not type_id.startswith('vehicle'):
logging.error('[BridgeHelper] Blueprint {} not supported. No vehicle will be spawned in sumo'.format(type_id)) logging.error(
'[BridgeHelper] Blueprint %s not supported. No vehicle will be spawned in sumo',
type_id)
return None return None
if type_id in traci.vehicletype.getIDList(): if type_id in traci.vehicletype.getIDList():
logging.debug('[BridgeHelper] blueprint {} found in sumo vtypes'.format(type_id)) logging.debug('[BridgeHelper] blueprint %s found in sumo vtypes', type_id)
return type_id return type_id
return BridgeHelper._create_sumo_vtype(carla_actor) return BridgeHelper._create_sumo_vtype(carla_actor)
@staticmethod @staticmethod
def get_carla_lights_state(current_carla_lights, sumo_lights): def get_carla_lights_state(current_carla_lights, sumo_lights):
"""Returns carla vehicle light state based on sumo signals. """
Returns carla vehicle light state based on sumo signals.
""" """
current_lights = current_carla_lights current_lights = current_carla_lights
@ -280,7 +269,8 @@ class BridgeHelper(object):
@staticmethod @staticmethod
def get_sumo_lights_state(current_sumo_lights, carla_lights): def get_sumo_lights_state(current_sumo_lights, carla_lights):
"""Returns sumo signals based on carla vehicle light state. """
Returns sumo signals based on carla vehicle light state.
""" """
current_lights = current_sumo_lights current_lights = current_sumo_lights

View File

@ -6,32 +6,34 @@
# This work is licensed under the terms of the MIT license. # This work is licensed under the terms of the MIT license.
# For a copy, see <https://opensource.org/licenses/MIT>. # For a copy, see <https://opensource.org/licenses/MIT>.
# ============================================================================== """ This module is responsible for the management of the carla simulation. """
# -- imports -------------------------------------------------------------------
# ============================================================================== # ==================================================================================================
# -- imports ---------------------------------------------------------------------------------------
# ==================================================================================================
import logging import logging
import carla import carla # pylint: disable=import-error
SpawnActor = carla.command.SpawnActor
FutureActor = carla.command.FutureActor
SetSimulatePhysics = carla.command.SetSimulatePhysics
from .constants import * from .constants import INVALID_ACTOR_ID, SPAWN_OFFSET_Z
# ============================================================================== # ==================================================================================================
# -- carla simulation ---------------------------------------------------------- # -- carla simulation ------------------------------------------------------------------------------
# ============================================================================== # ==================================================================================================
class CarlaSimulation(object): class CarlaSimulation(object):
"""
CarlaSimulation is responsible for the management of the carla simulation.
"""
def __init__(self, args): def __init__(self, args):
self.args = args self.args = args
self.host = args.carla_host host = args.carla_host
self.port = args.carla_port port = args.carla_port
self.step_length = args.step_length
self.client = carla.Client(self.host, self.port) self.client = carla.Client(host, port)
self.client.set_timeout(2.0) self.client.set_timeout(2.0)
self.world = self.client.get_world() self.world = self.client.get_world()
@ -40,7 +42,7 @@ class CarlaSimulation(object):
# Configuring carla simulation in sync mode. # Configuring carla simulation in sync mode.
settings = self.world.get_settings() settings = self.world.get_settings()
settings.synchronous_mode = True settings.synchronous_mode = True
settings.fixed_delta_seconds = self.step_length settings.fixed_delta_seconds = args.step_length
self.world.apply_settings(settings) 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.
@ -49,12 +51,20 @@ class CarlaSimulation(object):
self.destroyed_actors = set() self.destroyed_actors = set()
def get_actor(self, actor_id): def get_actor(self, actor_id):
"""
Accessor for carla actor.
"""
return self.world.get_actor(actor_id) return self.world.get_actor(actor_id)
# This is a workaround to fix synchronization issues when other carla # This is a workaround to fix synchronization issues when other carla
# clients remove an actor in carla without waiting for tick (e.g., # clients remove an actor in carla without waiting for tick (e.g.,
# running sumo co-simulation and manual control at the same time) # running sumo co-simulation and manual control at the same time)
def get_actor_light_state(self, actor_id): def get_actor_light_state(self, actor_id):
"""
Accessor for carla actor light state.
If the actor is not alive, returns None.
"""
try: try:
actor = self.get_actor(actor_id) actor = self.get_actor(actor_id)
return actor.get_light_state() return actor.get_light_state()
@ -62,30 +72,46 @@ class CarlaSimulation(object):
return None return None
def spawn_actor(self, blueprint, transform): def spawn_actor(self, blueprint, transform):
"""Spawns a new actor. """
Spawns a new actor.
:param blueprint: blueprint of the actor to be spawned.
:param transform: transform where the actor will be spawned.
:return: actor id if the actor is successfully spawned. Otherwise, INVALID_ACTOR_ID.
""" """
transform = carla.Transform( transform = carla.Transform(
transform.location + carla.Location(0, 0, SPAWN_OFFSET_Z), transform.location + carla.Location(0, 0, SPAWN_OFFSET_Z),
transform.rotation) transform.rotation)
batch = [ batch = [
SpawnActor(blueprint,transform) carla.command.SpawnActor(blueprint, transform)
.then(SetSimulatePhysics(FutureActor, False)) .then(carla.command.SetSimulatePhysics(carla.command.FutureActor, False))
] ]
response = self.client.apply_batch_sync(batch, True)[0] response = self.client.apply_batch_sync(batch, True)[0]
if response.error: if response.error:
logging.error('Spawn carla actor failed. {}'.format(response.error)) logging.error('Spawn carla actor failed. %s', response.error)
return INVALID_ACTOR_ID return INVALID_ACTOR_ID
else:
return response.actor_id return response.actor_id
def destroy_actor(self, actor_id): def destroy_actor(self, actor_id):
"""
Destroys the given actor.
"""
actor = self.world.get_actor(actor_id) actor = self.world.get_actor(actor_id)
if actor is not None: if actor is not None:
return actor.destroy() return actor.destroy()
return False return False
def synchronize_vehicle(self, vehicle_id, transform, lights=None): def synchronize_vehicle(self, vehicle_id, transform, lights=None):
"""
Updates vehicle state.
:param vehicle_id: id of the actor to be updated.
:param transform: new vehicle transform (i.e., position and rotation).
:param lights: new vehicle light state.
:return: True if successfully updated. Otherwise, False.
"""
vehicle = self.world.get_actor(vehicle_id) vehicle = self.world.get_actor(vehicle_id)
if vehicle is None: if vehicle is None:
return False return False
@ -93,11 +119,12 @@ class CarlaSimulation(object):
vehicle.set_transform(transform) vehicle.set_transform(transform)
if lights is not None and self.args.sync_vehicle_lights: if lights is not None and self.args.sync_vehicle_lights:
vehicle.set_light_state(carla.VehicleLightState(lights)) vehicle.set_light_state(carla.VehicleLightState(lights))
return True
def synchronize_walker(self, walker_id, transform):
pass
def tick(self): def tick(self):
"""
Tick to carla simulation.
"""
self.world.tick() self.world.tick()
# Update data structures for the current frame. # Update data structures for the current frame.

View File

@ -6,9 +6,11 @@
# This work is licensed under the terms of the MIT license. # This work is licensed under the terms of the MIT license.
# For a copy, see <https://opensource.org/licenses/MIT>. # For a copy, see <https://opensource.org/licenses/MIT>.
# ============================================================================== """ This module defines constants used for the sumo-carla co-simulation. """
# -- constants -----------------------------------------------------------------
# ============================================================================== # ==================================================================================================
# -- constants -------------------------------------------------------------------------------------
# ==================================================================================================
INVALID_ACTOR_ID = -1 INVALID_ACTOR_ID = -1
SPAWN_OFFSET_Z = 5.0 # meters SPAWN_OFFSET_Z = 5.0 # meters

View File

@ -6,28 +6,32 @@
# This work is licensed under the terms of the MIT license. # This work is licensed under the terms of the MIT license.
# For a copy, see <https://opensource.org/licenses/MIT>. # For a copy, see <https://opensource.org/licenses/MIT>.
# ============================================================================== """ This module is responsible for the management of the sumo simulation. """
# -- imports -------------------------------------------------------------------
# ============================================================================== # ==================================================================================================
# -- imports ---------------------------------------------------------------------------------------
# ==================================================================================================
import collections import collections
import enum import enum
import logging import logging
import carla import carla # pylint: disable=import-error
import sumolib import sumolib # pylint: disable=import-error
import traci import traci # pylint: disable=import-error
from .constants import * from .constants import INVALID_ACTOR_ID
# ============================================================================== # ==================================================================================================
# -- sumo definitions ---------------------------------------------------------- # -- sumo definitions ------------------------------------------------------------------------------
# ============================================================================== # ==================================================================================================
# Sumo vehicle signals.
# https://sumo.dlr.de/docs/TraCI/Vehicle_Signalling.html # https://sumo.dlr.de/docs/TraCI/Vehicle_Signalling.html
class SumoVehSignal(object): class SumoVehSignal(object):
"""
SumoVehSignal contains the different sumo vehicle signals.
"""
BLINKER_RIGHT = 1 << 0 BLINKER_RIGHT = 1 << 0
BLINKER_LEFT = 1 << 1 BLINKER_LEFT = 1 << 1
BLINKER_EMERGENCY = 1 << 2 BLINKER_EMERGENCY = 1 << 2
@ -46,6 +50,9 @@ class SumoVehSignal(object):
# https://sumo.dlr.de/docs/Definition_of_Vehicles,_Vehicle_Types,_and_Routes.html#abstract_vehicle_class # https://sumo.dlr.de/docs/Definition_of_Vehicles,_Vehicle_Types,_and_Routes.html#abstract_vehicle_class
class SumoActorClass(enum.Enum): class SumoActorClass(enum.Enum):
"""
SumoActorClass enumerates the different sumo actor classes.
"""
IGNORING = "ignoring" IGNORING = "ignoring"
PRIVATE = "private" PRIVATE = "private"
EMERGENCY = "emergency" EMERGENCY = "emergency"
@ -84,13 +91,16 @@ SumoActor = collections.namedtuple(
class SumoSimulation(object): class SumoSimulation(object):
"""
SumoSimulation is responsible for the management of the sumo simulation.
"""
def __init__(self, args): def __init__(self, args):
self.args = args self.args = args
self.host = args.sumo_host host = args.sumo_host
self.port = args.sumo_port port = args.sumo_port
self.sumo_gui = args.sumo_gui if args.sumo_gui is True:
if self.sumo_gui is True:
sumo_binary = sumolib.checkBinary('sumo-gui') sumo_binary = sumolib.checkBinary('sumo-gui')
else: else:
sumo_binary = sumolib.checkBinary('sumo') sumo_binary = sumolib.checkBinary('sumo')
@ -105,11 +115,11 @@ class SumoSimulation(object):
'--collision.check-junctions' '--collision.check-junctions'
]) ])
if self.sumo_gui: if args.sumo_gui:
logging.info('Remember to press the play button to start the simulation') logging.info('Remember to press the play button to start the simulation')
else: else:
logging.info('Connection to sumo server. Host: {} Port: {}'.format(self.host, self.port)) logging.info('Connection to sumo server. Host: %s Port: %s', host, port)
traci.init(host=self.host, port=self.port) traci.init(host=host, port=port)
# Structures to keep track of the spawned and destroyed vehicles at each time step. # Structures to keep track of the spawned and destroyed vehicles at each time step.
self.spawned_actors = set() self.spawned_actors = set()
@ -123,6 +133,19 @@ class SumoSimulation(object):
@staticmethod @staticmethod
def subscribe(actor_id): def subscribe(actor_id):
"""
Subscribe the given actor to the following variables:
* Type.
* Vehicle class.
* Color.
* Length, Width, Height.
* Position3D (i.e., x, y, z).
* Angle, Slope.
* Speed.
* Lateral speed.
* Signals.
"""
traci.vehicle.subscribe(actor_id, [ traci.vehicle.subscribe(actor_id, [
traci.constants.VAR_TYPE, traci.constants.VAR_VEHICLECLASS, traci.constants.VAR_TYPE, traci.constants.VAR_VEHICLECLASS,
traci.constants.VAR_COLOR, traci.constants.VAR_LENGTH, traci.constants.VAR_COLOR, traci.constants.VAR_LENGTH,
@ -134,16 +157,28 @@ class SumoSimulation(object):
@staticmethod @staticmethod
def unsubscribe(actor_id): def unsubscribe(actor_id):
"""
Unsubscribe the given actor from receiving updated information each step.
"""
traci.vehicle.unsubscribe(actor_id) traci.vehicle.unsubscribe(actor_id)
def get_net_offset(self): def get_net_offset(self):
"""
Accessor for sumo net offset.
"""
offset = traci.simulation.convertGeo(0, 0) offset = traci.simulation.convertGeo(0, 0)
return (-offset[0], -offset[1]) return (-offset[0], -offset[1])
def get_step_length(self): def get_step_length(self):
"""
Accessor for sumo simulation step length.
"""
return traci.simulation.getDeltaT() return traci.simulation.getDeltaT()
def get_actor(self, actor_id): def get_actor(self, actor_id):
"""
Accessor for sumo actor.
"""
results = traci.vehicle.getSubscriptionResults(actor_id) results = traci.vehicle.getSubscriptionResults(actor_id)
type_id = results[traci.constants.VAR_TYPE] type_id = results[traci.constants.VAR_TYPE]
@ -169,16 +204,22 @@ class SumoSimulation(object):
return SumoActor(type_id, vclass, transform, signals, extent, color) return SumoActor(type_id, vclass, transform, signals, extent, color)
def spawn_actor(self, type_id, attrs={}): def spawn_actor(self, type_id, attrs=None):
"""Spawns a new actor based on given type. """
Spawns a new actor.
:param type_id: vtype to be spawned.
:param attrs: dictionary with additional attributes for this specific actor.
:return: actor id if the actor is successfully spawned. Otherwise, INVALID_ACTOR_ID.
""" """
actor_id = 'carla' + str(self._sequential_id) actor_id = 'carla' + str(self._sequential_id)
try: try:
traci.vehicle.add(actor_id, 'carla_route', typeID=type_id) traci.vehicle.add(actor_id, 'carla_route', typeID=type_id)
except Exception as error: except traci.exceptions.TraCIException as error:
logging.error('Spawn sumo actor failed: {}'.format(error)) logging.error('Spawn sumo actor failed: %s', error)
return INVALID_ACTOR_ID return INVALID_ACTOR_ID
if attrs is not None:
if 'color' in attrs: if 'color' in attrs:
color = attrs['color'].split(',') color = attrs['color'].split(',')
traci.vehicle.setColor(actor_id, color) traci.vehicle.setColor(actor_id, color)
@ -188,20 +229,32 @@ class SumoSimulation(object):
return actor_id return actor_id
def destroy_actor(self, actor_id): def destroy_actor(self, actor_id):
"""
Destroys the given actor.
"""
traci.vehicle.remove(actor_id) traci.vehicle.remove(actor_id)
def synchronize_vehicle(self, vehicle_id, transform, signals=None): def synchronize_vehicle(self, vehicle_id, transform, signals=None):
x, y = transform.location.x, transform.location.y """
Updates vehicle state.
:param vehicle_id: id of the actor to be updated.
:param transform: new vehicle transform (i.e., position and rotation).
:param signals: new vehicle signals.
:return: True if successfully updated. Otherwise, False.
"""
loc_x, loc_y = transform.location.x, transform.location.y
yaw = transform.rotation.yaw yaw = transform.rotation.yaw
traci.vehicle.moveToXY(vehicle_id, "", 0, x, y, angle=yaw, keepRoute=2) traci.vehicle.moveToXY(vehicle_id, "", 0, loc_x, loc_y, angle=yaw, keepRoute=2)
if signals is not None and self.args.sync_vehicle_lights: if signals is not None and self.args.sync_vehicle_lights:
traci.vehicle.setSignals(vehicle_id, signals) traci.vehicle.setSignals(vehicle_id, signals)
return True
def synchronize_walker(self, walker_id, transform):
pass
def tick(self): def tick(self):
"""
Tick to sumo simulation.
"""
traci.simulationStep() traci.simulationStep()
# Update data structures for the current frame. # Update data structures for the current frame.
@ -209,4 +262,7 @@ class SumoSimulation(object):
self.destroyed_actors = set(traci.simulation.getArrivedIDList()) self.destroyed_actors = set(traci.simulation.getArrivedIDList())
def close(self): def close(self):
"""
Closes traci client.
"""
traci.close() traci.close()

View File

@ -10,37 +10,38 @@
Script to create sumo vtypes based on carla blueprints. Script to create sumo vtypes based on carla blueprints.
""" """
# ============================================================================== # ==================================================================================================
# -- find carla module --------------------------------------------------------- # -- imports ---------------------------------------------------------------------------------------
# ============================================================================== # ==================================================================================================
import argparse
import glob import glob
import datetime
import json
import logging
import os import os
import sys import sys
import lxml.etree as ET # pylint: disable=import-error
# ==================================================================================================
# -- find carla module -----------------------------------------------------------------------------
# ==================================================================================================
try: try:
sys.path.append(glob.glob('../../../PythonAPI/carla/dist/carla-*%d.%d-%s.egg' % ( sys.path.append(
sys.version_info.major, glob.glob('../../../PythonAPI/carla/dist/carla-*%d.%d-%s.egg' %
sys.version_info.minor, (sys.version_info.major, sys.version_info.minor,
'win-amd64' if os.name == 'nt' else 'linux-x86_64'))[0]) 'win-amd64' if os.name == 'nt' else 'linux-x86_64'))[0])
except IndexError: except IndexError:
pass pass
# ============================================================================== import carla # pylint: disable=import-error, wrong-import-position
# -- imports -------------------------------------------------------------------
# ==============================================================================
import carla
import argparse # ==================================================================================================
import datetime # -- load specs definition -------------------------------------------------------------------------
import json # ==================================================================================================
import logging
import lxml.etree as ET
# ==============================================================================
# -- load specs definition -----------------------------------------------------
# ==============================================================================
with open('../data/vtypes.json') as f: with open('../data/vtypes.json') as f:
SPECS = json.load(f) SPECS = json.load(f)
@ -49,19 +50,20 @@ with open('../data/vtypes.json') as f:
DEFAULT_WHEELED_VEHICLE = SPECS['DEFAULT_WHEELED_VEHICLE'] DEFAULT_WHEELED_VEHICLE = SPECS['DEFAULT_WHEELED_VEHICLE']
CARLA_BLUEPRINTS_SPECS = SPECS['carla_blueprints'] CARLA_BLUEPRINTS_SPECS = SPECS['carla_blueprints']
# ============================================================================== # ==================================================================================================
# -- main ---------------------------------------------------------------------- # -- main ------------------------------------------------------------------------------------------
# ============================================================================== # ==================================================================================================
def write_vtype_xml(filename, vtypes): def write_vtype_xml(filename, vtypes):
"""
Write route xml file.
"""
root = ET.Element('routes') root = ET.Element('routes')
root.addprevious(ET.Comment( root.addprevious(
'generated on {date:%Y-%m-%d %H:%M:%S} by {script:}'.format( ET.Comment('generated on {date:%Y-%m-%d %H:%M:%S} by {script:}'.format(
date=datetime.datetime.now(), date=datetime.datetime.now(), script=os.path.basename(__file__))))
script=os.path.basename(__file__))
)
)
for vtype in vtypes: for vtype in vtypes:
ET.SubElement(root, 'vType', vtype) ET.SubElement(root, 'vType', vtype)
@ -69,6 +71,7 @@ def write_vtype_xml(filename, vtypes):
tree = ET.ElementTree(root) tree = ET.ElementTree(root)
tree.write(filename, pretty_print=True, encoding='UTF-8', xml_declaration=True) tree.write(filename, pretty_print=True, encoding='UTF-8', xml_declaration=True)
def generate_vtype(vehicle): def generate_vtype(vehicle):
"""Generates sumo vtype specification for a given carla vehicle. """Generates sumo vtype specification for a given carla vehicle.
@ -80,23 +83,21 @@ def generate_vtype(vehicle):
if type_id not in CARLA_BLUEPRINTS_SPECS: if type_id not in CARLA_BLUEPRINTS_SPECS:
number_of_wheels = int(vehicle.attributes['number_of_wheels']) number_of_wheels = int(vehicle.attributes['number_of_wheels'])
if number_of_wheels == 2: if number_of_wheels == 2:
logging.warning('type id {id:} not mapped to any sumo vtype. Using default specification for two-wheeled vehicles: {specs:}'.format( logging.warning(
id=type_id, '''type id %s not mapped to any sumo vtype.
specs=DEFAULT_2_WHEELED_VEHICLE) \tUsing default specification for two-wheeled vehicles: %s''', type_id,
) DEFAULT_2_WHEELED_VEHICLE)
user_specs = DEFAULT_2_WHEELED_VEHICLE user_specs = DEFAULT_2_WHEELED_VEHICLE
else: else:
logging.warning('type id {id:} not mapped to any sumo vtype. Using default specification for wheeled vehicles: {specs:}'.format( logging.warning(
id=type_id, '''type id %s not mapped to any sumo vtype.
specs=DEFAULT_WHEELED_VEHICLE) \tUsing default specification for wheeled vehicles: %s''', type_id,
) DEFAULT_WHEELED_VEHICLE)
user_specs = DEFAULT_WHEELED_VEHICLE user_specs = DEFAULT_WHEELED_VEHICLE
else: else:
logging.info('type id {id:} mapped to the following specifications: {specs:}'.format( logging.info('type id %s mapped to the following specifications: %s', type_id,
id=type_id, CARLA_BLUEPRINTS_SPECS[type_id])
specs=CARLA_BLUEPRINTS_SPECS[type_id])
)
user_specs = CARLA_BLUEPRINTS_SPECS[type_id] user_specs = CARLA_BLUEPRINTS_SPECS[type_id]
specs = { specs = {
@ -109,7 +110,11 @@ def generate_vtype(vehicle):
specs.update(user_specs) specs.update(user_specs)
return specs return specs
def main(args): def main(args):
"""
Main method.
"""
client = carla.Client(args.carla_host, args.carla_port) client = carla.Client(args.carla_host, args.carla_port)
client.set_timeout(2.0) client.set_timeout(2.0)
@ -122,14 +127,15 @@ def main(args):
vtypes = [] vtypes = []
for blueprint in vehicle_blueprints: for blueprint in vehicle_blueprints:
logging.info('processing vtype for {}'.format(blueprint.id)) logging.info('processing vtype for %s', blueprint.id)
vehicle = world.spawn_actor(blueprint, transform) vehicle = world.spawn_actor(blueprint, transform)
vtype = generate_vtype(vehicle) vtype = generate_vtype(vehicle)
if vtype: if vtype:
vtypes.append(vtype) vtypes.append(vtype)
else: else:
logging.error('type id {id:} could no be mapped to any vtype'.format(id=vehicle.type_id)) logging.error(
'type id %s could no be mapped to any vtype', vehicle.type_id)
vehicle.destroy() vehicle.destroy()
@ -138,37 +144,32 @@ def main(args):
finally: finally:
logging.info('done') logging.info('done')
if __name__ == '__main__': if __name__ == '__main__':
# Define arguments that will be received and parsed. # Define arguments that will be received and parsed.
argparser = argparse.ArgumentParser( argparser = argparse.ArgumentParser(description=__doc__)
description=__doc__) argparser.add_argument('--carla-host',
argparser.add_argument(
'--carla-host',
metavar='H', metavar='H',
default='127.0.0.1', default='127.0.0.1',
help='IP of the host server (default: 127.0.0.1)') help='IP of the host server (default: 127.0.0.1)')
argparser.add_argument( argparser.add_argument('--carla-port',
'--carla-port',
metavar='P', metavar='P',
default=2000, default=2000,
type=int, type=int,
help='TCP port to listen to (default: 2000)') help='TCP port to listen to (default: 2000)')
argparser.add_argument( argparser.add_argument(
'--output-file', '-o', '--output-file',
'-o',
metavar='FILE', metavar='FILE',
default='carlavtypes.rou.xml', default='carlavtypes.rou.xml',
type=str, type=str,
help='the generated vtypes will be written to FILE (default: carlavtypes.rou.xml)') help='the generated vtypes will be written to FILE (default: carlavtypes.rou.xml)')
argparser.add_argument( argparser.add_argument('--verbose', '-v', action='store_true', help='increase output verbosity')
'--verbose', '-v', arguments = argparser.parse_args()
action='store_true',
help='increase output verbosity'
)
args = argparser.parse_args()
if args.verbose: if arguments.verbose:
logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO) logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO)
else: else:
logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.WARNING) logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.WARNING)
main(args) main(arguments)

View File

@ -10,29 +10,33 @@
Script to modify automatically vtypes to carla type ids in sumo route files. Script to modify automatically vtypes to carla type ids in sumo route files.
""" """
# ============================================================================== # ==================================================================================================
# -- imports ------------------------------------------------------------------- # -- imports ---------------------------------------------------------------------------------------
# ============================================================================== # ==================================================================================================
import argparse import argparse
import fnmatch import fnmatch
import json import json
import logging import logging
import lxml.etree as ET
import random import random
# ============================================================================== import lxml.etree as ET # pylint: disable=import-error
# -- load vtypes ---------------------------------------------------------------
# ============================================================================== # ==================================================================================================
# -- load vtypes -----------------------------------------------------------------------------------
# ==================================================================================================
with open('../data/vtypes.json') as f: with open('../data/vtypes.json') as f:
VTYPES = json.load(f)['carla_blueprints'].keys() VTYPES = json.load(f)['carla_blueprints'].keys()
# ============================================================================== # ==================================================================================================
# -- main ---------------------------------------------------------------------- # -- main ------------------------------------------------------------------------------------------
# ============================================================================== # ==================================================================================================
def main(route_files, vtypes, _random=False): def main(route_files, vtypes, _random=False):
"""
Main method to automatically modify vtypes to carla type ids in sumo route files.
"""
for filename in route_files: for filename in route_files:
tree = ET.parse(filename) tree = ET.parse(filename)
root = tree.getroot() root = tree.getroot()
@ -54,39 +58,26 @@ def main(route_files, vtypes, _random=False):
tree = ET.ElementTree(root) tree = ET.ElementTree(root)
tree.write(filename, pretty_print=True, encoding='UTF-8', xml_declaration=True) tree.write(filename, pretty_print=True, encoding='UTF-8', xml_declaration=True)
logging.info('modified {counter:} vtype(s) in {file:}'.format( logging.info('modified %d vtype(s) in %s', counter, filename)
counter=counter,
file=filename
))
if __name__ == '__main__': if __name__ == '__main__':
# Define arguments that will be received and parsed. # Define arguments that will be received and parsed.
argparser = argparse.ArgumentParser( argparser = argparse.ArgumentParser(description=__doc__)
description=__doc__ argparser.add_argument('--route-files',
) '-r',
argparser.add_argument(
'--route-files', '-r',
metavar='FILES', metavar='FILES',
nargs='+', nargs='+',
default=[], default=[],
help='sumo route files' help='sumo route files')
) argparser.add_argument('--random',
argparser.add_argument(
'--random',
action='store_true', action='store_true',
help='apply vtypes randomly or sequentially' help='apply vtypes randomly or sequentially')
) argparser.add_argument('--filterv',
argparser.add_argument(
'--filterv',
metavar='PATTERN', metavar='PATTERN',
default='vehicle.*', default='vehicle.*',
help='vehicles filter (default: "vehicle.*")' help='vehicles filter (default: "vehicle.*")')
) argparser.add_argument('--verbose', '-v', action='store_true', help='increase output verbosity')
argparser.add_argument(
'--verbose', '-v',
action='store_true',
help='increase output verbosity'
)
args = argparser.parse_args() args = argparser.parse_args()
if args.verbose: if args.verbose:
@ -94,5 +85,5 @@ if __name__ == '__main__':
else: else:
logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.WARNING) logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.WARNING)
vtypes = [vtype for vtype in VTYPES if fnmatch.fnmatch(vtype, args.filterv)] filtered_vtypes = [vtype for vtype in VTYPES if fnmatch.fnmatch(vtype, args.filterv)]
main(args.route_files, vtypes, args.random) main(args.route_files, filtered_vtypes, args.random)