initial version ptv-vissim co-simulation
This commit is contained in:
parent
9dfbb14276
commit
f3bc87928e
|
@ -0,0 +1,7 @@
|
|||
venv
|
||||
|
||||
# Vissim files
|
||||
*.results
|
||||
*.inp0
|
||||
*.layx
|
||||
*.err
|
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"100": [
|
||||
"vehicle.audi.a2",
|
||||
"vehicle.audi.tt",
|
||||
"vehicle.bmw.grandtourer",
|
||||
"vehicle.citroen.c3",
|
||||
"vehicle.jeep.wrangler_rubicon",
|
||||
"vehicle.lincoln.mkz2017",
|
||||
"vehicle.mercedes-benz.coupe",
|
||||
"vehicle.mini.cooperst",
|
||||
"vehicle.mustang.mustang",
|
||||
"vehicle.nissan.micra",
|
||||
"vehicle.nissan.patrol",
|
||||
"vehicle.seat.leon",
|
||||
"vehicle.volkswagen.t2",
|
||||
"vehicle.toyota.prius",
|
||||
"vehicle.tesla.model3",
|
||||
"vehicle.audi.etron"
|
||||
],
|
||||
"200": [
|
||||
"vehicle.carlamotors.carlacola"
|
||||
],
|
||||
"300": [],
|
||||
"400": [],
|
||||
"510": [],
|
||||
"520": [],
|
||||
"610": [
|
||||
"vehicle.yamaha.yzf",
|
||||
"vehicle.harley-davidson.low_rider",
|
||||
"vehicle.kawasaki.ninja",
|
||||
"vehicle.gazelle.omafiets",
|
||||
"vehicle.diamondback.century",
|
||||
"vehicle.bh.crossbike"
|
||||
],
|
||||
"620": [
|
||||
"vehicle.yamaha.yzf",
|
||||
"vehicle.harley-davidson.low_rider",
|
||||
"vehicle.kawasaki.ninja",
|
||||
"vehicle.gazelle.omafiets",
|
||||
"vehicle.diamondback.century",
|
||||
"vehicle.bh.crossbike"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,224 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
# Copyright (c) 2020 Computer Vision Center (CVC) at the Universitat Autonoma de
|
||||
# Barcelona (UAB).
|
||||
#
|
||||
# This work is licensed under the terms of the MIT license.
|
||||
# For a copy, see <https://opensource.org/licenses/MIT>.
|
||||
"""
|
||||
Script to co-simulate CARLA and PTV-Vissim.
|
||||
"""
|
||||
|
||||
# ==================================================================================================
|
||||
# -- imports ---------------------------------------------------------------------------------------
|
||||
# ==================================================================================================
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import logging
|
||||
import random
|
||||
import time
|
||||
|
||||
# ==================================================================================================
|
||||
# -- find carla module -----------------------------------------------------------------------------
|
||||
# ==================================================================================================
|
||||
|
||||
import glob
|
||||
import os
|
||||
import sys
|
||||
|
||||
try:
|
||||
sys.path.append(
|
||||
glob.glob('../../PythonAPI/carla/dist/carla-*%d.%d-%s.egg' %
|
||||
(sys.version_info.major, sys.version_info.minor,
|
||||
'win-amd64' if os.name == 'nt' else 'linux-x86_64'))[0])
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
# ==================================================================================================
|
||||
# -- vissim integration imports --------------------------------------------------------------------
|
||||
# ==================================================================================================
|
||||
|
||||
from vissim_integration.bridge_helper import BridgeHelper
|
||||
from vissim_integration.carla_simulation import CarlaSimulation
|
||||
from vissim_integration.vissim_simulation import PTVVissimSimulation
|
||||
from vissim_integration.constants import INVALID_ACTOR_ID
|
||||
|
||||
# ==================================================================================================
|
||||
# -- synchronization_loop --------------------------------------------------------------------------
|
||||
# ==================================================================================================
|
||||
|
||||
|
||||
class SimulationSynchronization(object):
|
||||
"""
|
||||
SimulationSynchronization class is responsible for the synchronization of ptv-vissim and carla
|
||||
simulations.
|
||||
"""
|
||||
def __init__(self, args):
|
||||
self.args = args
|
||||
|
||||
self.vissim = PTVVissimSimulation(args)
|
||||
self.carla = CarlaSimulation(args)
|
||||
|
||||
# Mapped actor ids.
|
||||
self.vissim2carla_ids = {} # Contains only actors controlled by vissim.
|
||||
self.carla2vissim_ids = {} # Contains only actors controlled by carla.
|
||||
|
||||
BridgeHelper.blueprint_library = self.carla.world.get_blueprint_library()
|
||||
dir_path = os.path.dirname(os.path.realpath(__file__))
|
||||
with open(os.path.join(dir_path, 'data', 'vtypes.json')) as f:
|
||||
BridgeHelper.vtypes = json.load(f)
|
||||
|
||||
def tick(self):
|
||||
"""
|
||||
Tick to simulation synchronization
|
||||
"""
|
||||
# -------------------
|
||||
# vissim-->carla sync
|
||||
# -------------------
|
||||
self.vissim.tick()
|
||||
|
||||
# Spawning vissim controlled vehicles in carla.
|
||||
vissim_spawned_actors = self.vissim.spawned_vehicles - set(self.carla2vissim_ids.values())
|
||||
for vissim_actor_id in vissim_spawned_actors:
|
||||
vissim_actor = self.vissim.get_actor(vissim_actor_id)
|
||||
|
||||
carla_blueprint = BridgeHelper.get_carla_blueprint(vissim_actor)
|
||||
if carla_blueprint is not None:
|
||||
carla_transform = BridgeHelper.get_carla_transform(vissim_actor.get_transform())
|
||||
carla_actor_id = self.carla.spawn_actor(carla_blueprint, carla_transform)
|
||||
|
||||
if carla_actor_id != INVALID_ACTOR_ID:
|
||||
self.vissim2carla_ids[vissim_actor_id] = carla_actor_id
|
||||
|
||||
# Destroying vissim controlled vehicles in carla.
|
||||
for vissim_actor_id in self.vissim.destroyed_vehicles:
|
||||
if vissim_actor_id in self.vissim2carla_ids:
|
||||
self.vissim.destroy_actor(self.vissim2carla_ids.pop(vissim_actor_id))
|
||||
|
||||
# Updating vissim controlled vehicles in carla.
|
||||
for vissim_actor_id in self.vissim2carla_ids:
|
||||
carla_actor_id = self.vissim2carla_ids[vissim_actor_id]
|
||||
|
||||
vissim_actor = self.vissim.get_actor(vissim_actor_id)
|
||||
carla_actor = self.carla.get_actor(carla_actor_id)
|
||||
|
||||
carla_transform = BridgeHelper.get_carla_transform(vissim_actor.get_transform())
|
||||
carla_velocity = BridgeHelper.get_carla_velocity(vissim_actor.get_velocity())
|
||||
self.carla.synchronize_vehicle(carla_actor_id, carla_transform, carla_velocity)
|
||||
|
||||
# -------------------
|
||||
# carla-->vissim sync
|
||||
# -------------------
|
||||
self.carla.tick()
|
||||
|
||||
# Spawning carla controlled vehicles in vissim. This also takes into account carla vehicles
|
||||
# that could not be spawned in vissim in previous time steps.
|
||||
carla_spawned_actors = self.carla.spawned_actors - set(self.vissim2carla_ids.values())
|
||||
carla_spawned_actors.update(
|
||||
[c_id for c_id, v_id in self.carla2vissim_ids.items() if v_id == INVALID_ACTOR_ID])
|
||||
for carla_actor_id in carla_spawned_actors:
|
||||
carla_actor = self.carla.get_actor(carla_actor_id)
|
||||
|
||||
vissim_transform = BridgeHelper.get_vissim_transform(carla_actor.get_transform())
|
||||
vissim_actor_id = self.vissim.spawn_actor(vissim_transform)
|
||||
|
||||
# Add the vissim_actor_id even if it was not possible to spawn it (INVALID_ACTOR_ID) to
|
||||
# try to spawn it again in next time steps.
|
||||
self.carla2vissim_ids[carla_actor_id] = vissim_actor_id
|
||||
|
||||
# Destroying carla controlled vehicles in vissim.
|
||||
for carla_actor_id in self.carla.destroyed_actors:
|
||||
if carla_actor_id in self.carla2vissim_ids:
|
||||
self.vissim.destroy_actor(self.carla2vissim_ids.pop(carla_actor_id))
|
||||
|
||||
# Updating carla controlled vehicles in vissim.
|
||||
for carla_actor_id in self.carla2vissim_ids:
|
||||
vissim_actor_id = self.carla2vissim_ids[carla_actor_id]
|
||||
if vissim_actor_id != INVALID_ACTOR_ID:
|
||||
carla_actor = self.carla.get_actor(carla_actor_id)
|
||||
|
||||
vissim_transform = BridgeHelper.get_vissim_transform(carla_actor.get_transform())
|
||||
vissim_velocity = BridgeHelper.get_vissim_velocity(carla_actor.get_velocity())
|
||||
self.vissim.synchronize_vehicle(vissim_actor_id, vissim_transform, vissim_velocity)
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
Cleans synchronization.
|
||||
"""
|
||||
# Configuring carla simulation in async mode.
|
||||
settings = self.carla.world.get_settings()
|
||||
settings.synchronous_mode = False
|
||||
settings.fixed_delta_seconds = None
|
||||
self.carla.world.apply_settings(settings)
|
||||
|
||||
# Destroying synchronized actors.
|
||||
for carla_actor_id in self.vissim2carla_ids.values():
|
||||
self.carla.destroy_actor(carla_actor_id)
|
||||
|
||||
# Closing PTV-Vissim connection.
|
||||
self.vissim.close()
|
||||
|
||||
|
||||
def synchronization_loop(args):
|
||||
"""
|
||||
Entry point for vissim-carla co-simulation.
|
||||
"""
|
||||
try:
|
||||
synchronization = SimulationSynchronization(args)
|
||||
|
||||
while True:
|
||||
start = time.time()
|
||||
|
||||
synchronization.tick()
|
||||
|
||||
end = time.time()
|
||||
elapsed = end - start
|
||||
if elapsed < args.step_length:
|
||||
time.sleep(args.step_length - elapsed)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
logging.info('Cancelled by user.')
|
||||
|
||||
finally:
|
||||
logging.info('Cleaning synchronization')
|
||||
synchronization.close()
|
||||
|
||||
|
||||
# ==================================================================================================
|
||||
# -- main ------------------------------------------------------------------------------------------
|
||||
# ==================================================================================================
|
||||
|
||||
if __name__ == '__main__':
|
||||
argparser = argparse.ArgumentParser(description=__doc__)
|
||||
argparser.add_argument('vissim_network', type=str, help='vissim network file')
|
||||
argparser.add_argument('--carla-host',
|
||||
metavar='H',
|
||||
default='127.0.0.1',
|
||||
help='IP of the carla host server (default: 127.0.0.1)')
|
||||
argparser.add_argument('--carla-port',
|
||||
metavar='P',
|
||||
default=2000,
|
||||
type=int,
|
||||
help='TCP port to listen to (default: 2000)')
|
||||
argparser.add_argument('--vissim-version',
|
||||
default=2020,
|
||||
type=int,
|
||||
help='ptv-vissim version (default: 2020)')
|
||||
argparser.add_argument('--step-length',
|
||||
default=0.05,
|
||||
type=float,
|
||||
help='set fixed delta seconds (default: 0.05s)')
|
||||
argparser.add_argument('--simulator-vehicles',
|
||||
default=1,
|
||||
type=int,
|
||||
help='number of simulator vehicles to be passed to vissim (default: 1)')
|
||||
argparser.add_argument('--debug', action='store_true', help='enable debug messages')
|
||||
arguments = argparser.parse_args()
|
||||
|
||||
if arguments.debug:
|
||||
logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.DEBUG)
|
||||
else:
|
||||
logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO)
|
||||
|
||||
synchronization_loop(arguments)
|
|
@ -0,0 +1,136 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
# Copyright (c) 2020 Computer Vision Center (CVC) at the Universitat Autonoma de
|
||||
# Barcelona (UAB).
|
||||
#
|
||||
# This work is licensed under the terms of the MIT license.
|
||||
# For a copy, see <https://opensource.org/licenses/MIT>.
|
||||
""" This module provides a helper for the co-simulation between vissim and carla. """
|
||||
|
||||
# ==================================================================================================
|
||||
# -- imports ---------------------------------------------------------------------------------------
|
||||
# ==================================================================================================
|
||||
|
||||
import logging
|
||||
import random
|
||||
|
||||
import carla # pylint: disable=import-error
|
||||
|
||||
# ==================================================================================================
|
||||
# -- Bridge helper (VISSIM <=> CARLA) --------------------------------------------------------------
|
||||
# ==================================================================================================
|
||||
|
||||
|
||||
class BridgeHelper(object):
|
||||
"""
|
||||
BridgeHelper provides methos to ease the co-simulation between vissim and carla.
|
||||
"""
|
||||
|
||||
blueprint_library = []
|
||||
vtypes = {}
|
||||
|
||||
@staticmethod
|
||||
def get_carla_transform(in_vissim_transform):
|
||||
"""
|
||||
Returns carla transform based on vissim transform.
|
||||
"""
|
||||
in_location = in_vissim_transform.location
|
||||
in_rotation = in_vissim_transform.rotation
|
||||
|
||||
# Transform to carla reference system (left-handed system).
|
||||
out_location = (in_location.x, -in_location.y, in_location.z)
|
||||
out_rotation = (in_rotation.pitch, -in_rotation.yaw, in_rotation.roll)
|
||||
|
||||
out_transform = carla.Transform(
|
||||
carla.Location(out_location[0], out_location[1], out_location[2]),
|
||||
carla.Rotation(out_rotation[0], out_rotation[1], out_rotation[2]))
|
||||
|
||||
return out_transform
|
||||
|
||||
@staticmethod
|
||||
def get_vissim_transform(in_carla_transform):
|
||||
"""
|
||||
Returns vissim transform based on carla transform.
|
||||
"""
|
||||
in_location = in_carla_transform.location
|
||||
in_rotation = in_carla_transform.rotation
|
||||
|
||||
# Transform to vissim reference system (right-handed system).
|
||||
out_location = (in_location.x, -in_location.y, in_location.z)
|
||||
out_rotation = (in_rotation.pitch, -in_rotation.yaw, in_rotation.roll)
|
||||
|
||||
out_transform = carla.Transform(
|
||||
carla.Location(out_location[0], out_location[1], out_location[2]),
|
||||
carla.Rotation(out_rotation[0], out_rotation[1], out_rotation[2]))
|
||||
|
||||
return out_transform
|
||||
|
||||
@staticmethod
|
||||
def _flip_y(in_vector):
|
||||
"""
|
||||
Flips y coordinate of the given vector.
|
||||
"""
|
||||
return carla.Vector3D(in_vector.x, -in_vector.y, in_vector.z)
|
||||
|
||||
@staticmethod
|
||||
def get_carla_velocity(in_vissim_velocity):
|
||||
"""
|
||||
Returns carla velocity based on vissim velocity.
|
||||
"""
|
||||
return BridgeHelper._flip_y(in_vissim_velocity)
|
||||
|
||||
@staticmethod
|
||||
def get_vissim_velocity(in_carla_velocity):
|
||||
"""
|
||||
Returns vissim velocity based on carla velocity.
|
||||
"""
|
||||
return BridgeHelper._flip_y(in_carla_velocity)
|
||||
|
||||
@staticmethod
|
||||
def _get_recommended_carla_blueprint(vissim_actor):
|
||||
"""
|
||||
Returns an appropriate blueprint based on the given vissim actor.
|
||||
"""
|
||||
blueprint = BridgeHelper.blueprint_library.filter('vehicle.seat.leon')[0]
|
||||
color = random.choice(blueprint.get_attribute('color').recommended_values)
|
||||
blueprint.set_attribute('color', color)
|
||||
return blueprint
|
||||
|
||||
@staticmethod
|
||||
def get_carla_blueprint(vissim_actor):
|
||||
"""
|
||||
Returns an appropriate blueprint based on the received vissim actor.
|
||||
"""
|
||||
type_id = str(vissim_actor.type)
|
||||
|
||||
if type_id in BridgeHelper.vtypes:
|
||||
candidates = BridgeHelper.vtypes[type_id]
|
||||
if candidates:
|
||||
blueprint_id = random.choice(candidates)
|
||||
else:
|
||||
logging.error(
|
||||
'vissim type %s not supported. No vehicle will be spawned in carla', type_id)
|
||||
return None
|
||||
|
||||
blueprint = BridgeHelper.blueprint_library.filter(blueprint_id)
|
||||
if not blueprint:
|
||||
logging.error(
|
||||
'carla blueprint %s unknown. No vehicle will be spawned', blueprint_id)
|
||||
return None
|
||||
blueprint = blueprint[0]
|
||||
|
||||
if blueprint.has_attribute('color'):
|
||||
color = random.choice(blueprint.get_attribute('color').recommended_values)
|
||||
blueprint.set_attribute('color', color)
|
||||
|
||||
if blueprint.has_attribute('driver_id'):
|
||||
driver_id = random.choice(blueprint.get_attribute('driver_id').recommended_values)
|
||||
blueprint.set_attribute('driver_id', driver_id)
|
||||
|
||||
blueprint.set_attribute('role_name', 'vissim_driver')
|
||||
return blueprint
|
||||
|
||||
else:
|
||||
logging.error(
|
||||
'vissim type %s unknown. No vehicle will be spawned in carla', type_id)
|
||||
return None
|
|
@ -0,0 +1,120 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
# Copyright (c) 2020 Computer Vision Center (CVC) at the Universitat Autonoma de
|
||||
# Barcelona (UAB).
|
||||
#
|
||||
# This work is licensed under the terms of the MIT license.
|
||||
# For a copy, see <https://opensource.org/licenses/MIT>.
|
||||
""" This module is responsible for the management of the carla simulation. """
|
||||
|
||||
# ==================================================================================================
|
||||
# -- imports ---------------------------------------------------------------------------------------
|
||||
# ==================================================================================================
|
||||
|
||||
import logging
|
||||
|
||||
import carla # pylint: disable=import-error
|
||||
|
||||
from .constants import INVALID_ACTOR_ID, CARLA_SPAWN_OFFSET_Z
|
||||
|
||||
# ==================================================================================================
|
||||
# -- carla simulation ------------------------------------------------------------------------------
|
||||
# ==================================================================================================
|
||||
|
||||
|
||||
class CarlaSimulation(object):
|
||||
"""
|
||||
CarlaSimulation is responsible for the management of the carla simulation.
|
||||
"""
|
||||
def __init__(self, args):
|
||||
self.args = args
|
||||
host = args.carla_host
|
||||
port = args.carla_port
|
||||
|
||||
self.client = carla.Client(host, port)
|
||||
self.client.set_timeout(2.0)
|
||||
|
||||
self.world = self.client.get_world()
|
||||
self.blueprint_library = self.world.get_blueprint_library()
|
||||
|
||||
# Configuring carla simulation in sync mode.
|
||||
settings = self.world.get_settings()
|
||||
settings.synchronous_mode = True
|
||||
settings.fixed_delta_seconds = args.step_length
|
||||
self.world.apply_settings(settings)
|
||||
|
||||
# The following sets contain updated information for the current frame.
|
||||
self._active_actors = set()
|
||||
self.spawned_actors = set()
|
||||
self.destroyed_actors = set()
|
||||
|
||||
def get_actor(self, actor_id):
|
||||
"""
|
||||
Accessor for carla actor.
|
||||
"""
|
||||
return self.world.get_actor(actor_id)
|
||||
|
||||
def spawn_actor(self, blueprint, transform):
|
||||
"""
|
||||
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.location + carla.Location(0, 0, CARLA_SPAWN_OFFSET_Z),
|
||||
transform.rotation)
|
||||
|
||||
batch = [
|
||||
carla.command.SpawnActor(blueprint, transform).then(
|
||||
carla.command.SetSimulatePhysics(carla.command.FutureActor, False))
|
||||
]
|
||||
response = self.client.apply_batch_sync(batch, False)[0]
|
||||
if response.error:
|
||||
logging.error('Spawn carla actor failed. %s', response.error)
|
||||
return INVALID_ACTOR_ID
|
||||
|
||||
return response.actor_id
|
||||
|
||||
def destroy_actor(self, actor_id):
|
||||
"""
|
||||
Destroys the given actor.
|
||||
"""
|
||||
actor = self.world.get_actor(actor_id)
|
||||
if actor is not None:
|
||||
return actor.destroy()
|
||||
return False
|
||||
|
||||
def synchronize_vehicle(self, vehicle_id, transform, velocity, 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)
|
||||
if vehicle is None:
|
||||
return False
|
||||
|
||||
vehicle.set_transform(transform)
|
||||
if velocity is not None:
|
||||
vehicle.set_velocity(velocity)
|
||||
|
||||
if lights is not None:
|
||||
vehicle.set_light_state(carla.VehicleLightState(lights))
|
||||
return True
|
||||
|
||||
def tick(self):
|
||||
"""
|
||||
Tick to carla simulation.
|
||||
"""
|
||||
self.world.tick()
|
||||
|
||||
# Update data structures for the current frame.
|
||||
current_actors = set(
|
||||
[vehicle.id for vehicle in self.world.get_actors().filter('vehicle.*')])
|
||||
self.spawned_actors = current_actors.difference(self._active_actors)
|
||||
self.destroyed_actors = self._active_actors.difference(current_actors)
|
||||
self._active_actors = current_actors
|
|
@ -0,0 +1,33 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
# Copyright (c) 2020 Computer Vision Center (CVC) at the Universitat Autonoma de
|
||||
# Barcelona (UAB).
|
||||
#
|
||||
# This work is licensed under the terms of the MIT license.
|
||||
# For a copy, see <https://opensource.org/licenses/MIT>.
|
||||
""" This module defines constants used for the vissim-carla co-simulation. """
|
||||
|
||||
# ==================================================================================================
|
||||
# -- constants -------------------------------------------------------------------------------------
|
||||
# ==================================================================================================
|
||||
|
||||
INVALID_ACTOR_ID = -1
|
||||
CARLA_SPAWN_OFFSET_Z = 25.0 # meters
|
||||
|
||||
# Maximum distance of a Vissim veh/ped from a simulator veh/ped to be seen by the simulator (<=0
|
||||
# means unlimited radius).
|
||||
VISSIM_VISIBILITY_RADIUS = 0.0
|
||||
|
||||
# Maximum number of simulator vehicles/pedestrians/detectors (to be passed to Vissim).
|
||||
VISSIM_MAX_SIMULATOR_VEH = 5000
|
||||
VISSIM_MAX_SIMULATOR_PED = 5000
|
||||
VISSIM_MAX_SIMULATOR_DET = 500
|
||||
|
||||
# Maximum number of vissim vehicles/pedestrians/signal groups (to be passed to the simulator).
|
||||
VISSIM_MAX_VISSIM_VEH = 5000
|
||||
VISSIM_MAX_VISSIM_PED = 5000
|
||||
VISSIM_MAX_VISSIM_SIGGRP = 5000
|
||||
|
||||
# VISSIM Vehicle data constants.
|
||||
NAME_MAX_LENGTH = 100
|
||||
MAX_UDA = 16
|
|
@ -0,0 +1,283 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
# Copyright (c) 2020 Computer Vision Center (CVC) at the Universitat Autonoma de
|
||||
# Barcelona (UAB).
|
||||
#
|
||||
# This work is licensed under the terms of the MIT license.
|
||||
# For a copy, see <https://opensource.org/licenses/MIT>.
|
||||
""" This module is responsible for the management of the ptv-vissim simulation. """
|
||||
|
||||
# ==================================================================================================
|
||||
# -- imports ---------------------------------------------------------------------------------------
|
||||
# ==================================================================================================
|
||||
|
||||
import collections
|
||||
import enum
|
||||
import logging
|
||||
import math
|
||||
import sys
|
||||
|
||||
import carla # pylint: disable=import-error
|
||||
from ctypes import *
|
||||
|
||||
from . import constants
|
||||
|
||||
# ==================================================================================================
|
||||
# -- vissim definitions ----------------------------------------------------------------------------
|
||||
# ==================================================================================================
|
||||
|
||||
|
||||
class Simulator_Veh_Data(Structure):
|
||||
"""
|
||||
Structure to hold the data sent to vissim about the status of the simulator vehicles (i.e.,
|
||||
carla vehicles).
|
||||
"""
|
||||
_fields_ = [
|
||||
('Position_X', c_double), # front center of the vehicle in m
|
||||
('Position_Y', c_double), # front center of the vehicle in m
|
||||
('Position_Z', c_double), # front center of the vehicle in m
|
||||
('Orient_Heading', c_double), # in radians, eastbound = zero, northbound = +Pi/2 */
|
||||
('Orient_Pitch', c_double), # in radians, uphill = positive
|
||||
('Speed', c_double), # in m/s
|
||||
('ControlledByVissim', c_bool), # affects next time step
|
||||
('RoutingDecisionNo', c_long), # used once if ControlledByVissim changed from false to true
|
||||
('RouteNo', c_long) # used once if ControlledByVissim changed from false to true
|
||||
]
|
||||
|
||||
|
||||
class VISSIM_Veh_Data(Structure):
|
||||
"""
|
||||
Structure to hold the data received from vissim about the status of the traffic vehicles (i.e.,
|
||||
vissim vehicles).
|
||||
"""
|
||||
_fields_ = [
|
||||
('VehicleID', c_long),
|
||||
('VehicleType', c_long), # vehicle type number from Vissim
|
||||
('ModelFileName', c_char * constants.NAME_MAX_LENGTH), # .v3d
|
||||
('color', c_long), # RGB
|
||||
('Position_X', c_double), # front center of the vehicle in m
|
||||
('Position_Y', c_double), # front center of the vehicle in m
|
||||
('Position_Z', c_double), # front center of the vehicle in m
|
||||
('Orient_Heading', c_double), # in radians, eastbound = zero, northbound = +Pi/2 */
|
||||
('Orient_Pitch', c_double), # in radians, uphill = positive
|
||||
('Speed', c_double), # in m/s
|
||||
('LeadingVehicleID', c_long), # relevant vehicle in front
|
||||
('TrailingVehicleID', c_long), # next vehicle back on the same lane
|
||||
('LinkID', c_long), # Vissim link attribute “Number”
|
||||
('LinkName', c_char * constants.NAME_MAX_LENGTH), # empty if “Name” not set in Vissim
|
||||
('LinkCoordinate', c_double), # in m
|
||||
('LaneIndex', c_int), # 0 = rightmost
|
||||
('TurningIndicator', c_int), # 1 = left, 0 = none, -1 = right
|
||||
('PreviousIndex', c_long), # for interpolation: index in the array in the previous Vissim time step, < 0 = new in the visibility area
|
||||
('NumUDAs', c_long), # the number of UDA values in the following array
|
||||
('UDA', c_double * constants.MAX_UDA) # the first MAX_UDA user-defined numeric vehicle attributes
|
||||
]
|
||||
|
||||
|
||||
class VissimLightState(enum.Enum):
|
||||
"""
|
||||
VissimLightState contains the different vissim indicator states.
|
||||
"""
|
||||
LEFT = 1
|
||||
NONE = 0
|
||||
RIGHT = -1
|
||||
|
||||
|
||||
class VissimVehicle(object):
|
||||
"""
|
||||
VissimVehicle holds the data relative to traffic vehicles in vissim.
|
||||
"""
|
||||
def __init__(self,
|
||||
id,
|
||||
type,
|
||||
model_filename,
|
||||
color,
|
||||
location,
|
||||
rotation,
|
||||
velocity,
|
||||
lights_state=VissimLightState.NONE):
|
||||
# Static parameters.
|
||||
self.id = id
|
||||
self.type = type
|
||||
self.model_filename = model_filename
|
||||
self.color = color
|
||||
|
||||
# Dynamic attributes.
|
||||
loc = carla.Location(location[0], location[1], location[2])
|
||||
rot = carla.Rotation(math.degrees(rotation[0]), math.degrees(rotation[1]),
|
||||
math.degrees(rotation[2]))
|
||||
self._transform = carla.Transform(loc, rot)
|
||||
self._velocity = carla.Vector3D(
|
||||
velocity * math.cos(math.radians(rot.yaw)) * math.cos(math.radians(rot.pitch)),
|
||||
velocity * math.sin(math.radians(rot.yaw)) * math.cos(math.radians(rot.pitch)),
|
||||
velocity * math.sin(math.radians(rot.pitch)))
|
||||
self._lights_state = lights_state
|
||||
|
||||
def get_velocity(self):
|
||||
"""
|
||||
Returns the vehicle's velocity.
|
||||
"""
|
||||
return self._velocity
|
||||
|
||||
def get_transform(self):
|
||||
"""
|
||||
Returns carla transform.
|
||||
"""
|
||||
return self._transform
|
||||
|
||||
|
||||
# ==================================================================================================
|
||||
# -- vissim simulation -----------------------------------------------------------------------------
|
||||
# ==================================================================================================
|
||||
|
||||
|
||||
class PTVVissimSimulation(object):
|
||||
"""
|
||||
PTVVissimSimulation is responsible for the management of the vissim simulation.
|
||||
"""
|
||||
def __init__(self, args):
|
||||
# Maximum number of simulator vehicles to be tracked by the driving simulator interface.
|
||||
self._max_simulator_vehicles = args.simulator_vehicles
|
||||
|
||||
# Loading driving simulator proxy library.
|
||||
logging.info('Loading DrivingSimulatorProxy library...')
|
||||
self.ds_proxy = cdll.LoadLibrary('DrivingSimulatorProxy.dll')
|
||||
|
||||
# Connection to vissim simulator.
|
||||
logging.info('Establishing a connection with a GUI version of PTV-Vissim')
|
||||
self.ds_proxy.VISSIM_Connect(args.vissim_version, args.vissim_network,
|
||||
int(1. / args.step_length),
|
||||
c_double(constants.VISSIM_VISIBILITY_RADIUS),
|
||||
c_ushort(constants.VISSIM_MAX_SIMULATOR_VEH),
|
||||
c_ushort(constants.VISSIM_MAX_SIMULATOR_PED),
|
||||
c_ushort(constants.VISSIM_MAX_SIMULATOR_DET),
|
||||
c_ushort(constants.VISSIM_MAX_VISSIM_VEH),
|
||||
c_ushort(constants.VISSIM_MAX_VISSIM_PED),
|
||||
c_ushort(constants.VISSIM_MAX_VISSIM_SIGGRP))
|
||||
|
||||
# Structures to keep track of the simulation state at each time step.
|
||||
self._vissim_vehicles = {} # vissim_actor_id: VissimVehicle (only vissim traffic)
|
||||
self._simulator_vehicles = {} # vissim_actor_id: Simulator_Veh_Data
|
||||
|
||||
self.spawned_vehicles = set()
|
||||
self.destroyed_vehicles = set()
|
||||
|
||||
def _get_next_actor_id(self):
|
||||
"""
|
||||
Returns an available actor id. Otherwise, returns INVALID_ACTOR_ID.
|
||||
"""
|
||||
all_ids = set(range(1, self._max_simulator_vehicles + 1))
|
||||
used_ids = set(self._simulator_vehicles.keys())
|
||||
available_ids = all_ids - used_ids
|
||||
if len(available_ids):
|
||||
return available_ids.pop()
|
||||
else:
|
||||
return constants.INVALID_ACTOR_ID
|
||||
|
||||
def get_actor(self, actor_id):
|
||||
"""
|
||||
Accessor for vissim actor.
|
||||
"""
|
||||
return self._vissim_vehicles[actor_id]
|
||||
|
||||
def spawn_actor(self, transform):
|
||||
"""
|
||||
Spawns a new actor.
|
||||
|
||||
Warning: When the maximum number of simulator vehicles being tracked at the same time is
|
||||
reached, no new vehicles are spawned.
|
||||
"""
|
||||
# Checks number of simulator vehicles currently being tracked.
|
||||
if (len(self._simulator_vehicles) < self._max_simulator_vehicles):
|
||||
actor_id = self._get_next_actor_id()
|
||||
self._simulator_vehicles[actor_id] = Simulator_Veh_Data(
|
||||
transform.location.x, transform.location.y, transform.location.z,
|
||||
math.radians(transform.rotation.yaw), math.radians(transform.rotation.pitch), 0.0,
|
||||
False, 0, 0)
|
||||
return actor_id
|
||||
else:
|
||||
logging.warning(
|
||||
'Maximum number of simulator vehicles reached. No vehicle will be spawned.')
|
||||
return constants.INVALID_ACTOR_ID
|
||||
|
||||
def destroy_actor(self, actor_id):
|
||||
"""
|
||||
Destroys the given actor.
|
||||
|
||||
:param actor_id: id of the vehicle to be destroyed.
|
||||
:return: True if successfully destroyed. Otherwise, False.
|
||||
"""
|
||||
if actor_id in self._simulator_vehicles:
|
||||
del self._simulator_vehicles[actor_id]
|
||||
return True
|
||||
return False
|
||||
|
||||
def synchronize_vehicle(self, vehicle_id, transform, velocity):
|
||||
"""
|
||||
Updates vehicle state.
|
||||
|
||||
:param int vehicle_id: id of the vehicle to be updated.
|
||||
:param carla.Transform transform: new vehicle transform (i.e., position and rotation).
|
||||
:param carla.Vector3D velocity: new vehicle velocity.
|
||||
:return: True if successfully updated. Otherwise, False.
|
||||
"""
|
||||
assert vehicle_id in self._simulator_vehicles
|
||||
self._simulator_vehicles[vehicle_id] = Simulator_Veh_Data(
|
||||
transform.location.x, transform.location.y, transform.location.z,
|
||||
math.radians(transform.rotation.yaw), math.radians(transform.rotation.pitch),
|
||||
math.sqrt(velocity.x**2 + velocity.y**2 + velocity.z**2), False, 0, 0)
|
||||
return True
|
||||
|
||||
def _get_simulator_veh_data(self):
|
||||
"""
|
||||
Returns list of Simulator_Veh_Data structures ready to be sent to the driving simulator
|
||||
interface.
|
||||
"""
|
||||
data = []
|
||||
for i in range(1, self._max_simulator_vehicles + 1):
|
||||
if i in self._simulator_vehicles:
|
||||
data.append(self._simulator_vehicles[i])
|
||||
else:
|
||||
# Invalid Simulator_Veh_Data to set the position of simulator vehicles in vissim
|
||||
# that are not yet in carla.
|
||||
data.append(
|
||||
Simulator_Veh_Data(float('inf'), float('inf'), float('inf'), 0.0, 0.0, 0.0,
|
||||
False, 0, 0))
|
||||
return data
|
||||
|
||||
def tick(self):
|
||||
"""
|
||||
Tick to vissim simulation.
|
||||
"""
|
||||
# Updating simulator vehicles data.
|
||||
arr = (Simulator_Veh_Data * self._max_simulator_vehicles)(*self._get_simulator_veh_data())
|
||||
self.ds_proxy.VISSIM_SetDriverVehicles(self._max_simulator_vehicles, byref(arr))
|
||||
|
||||
# Retrieving vissim traffic data.
|
||||
num_vehicles = c_int(0)
|
||||
traffic_data = POINTER(VISSIM_Veh_Data)()
|
||||
self.ds_proxy.VISSIM_GetTrafficVehicles(byref(num_vehicles), byref(traffic_data))
|
||||
|
||||
vehicles = {}
|
||||
for i in range(num_vehicles.value):
|
||||
vehicle_data = traffic_data[i]
|
||||
assert vehicle_data.VehicleID > self._max_simulator_vehicles
|
||||
|
||||
vehicles[vehicle_data.VehicleID] = VissimVehicle(
|
||||
vehicle_data.VehicleID, vehicle_data.VehicleType, vehicle_data.ModelFileName,
|
||||
vehicle_data.color,
|
||||
[vehicle_data.Position_X, vehicle_data.Position_Y, vehicle_data.Position_Z],
|
||||
[vehicle_data.Orient_Pitch, vehicle_data.Orient_Heading, 0.0], vehicle_data.Speed,
|
||||
vehicle_data.TurningIndicator)
|
||||
|
||||
# Update data structures for the current time step.
|
||||
active_vehicles = set(self._vissim_vehicles.keys())
|
||||
current_vehicles = set(vehicles.keys())
|
||||
|
||||
self.spawned_vehicles = current_vehicles.difference(active_vehicles)
|
||||
self.destroyed_vehicles = active_vehicles.difference(current_vehicles)
|
||||
|
||||
self._vissim_vehicles = vehicles
|
||||
|
||||
def close(self):
|
||||
self.ds_proxy.VISSIM_Disconnect()
|
Loading…
Reference in New Issue