diff --git a/.gitignore b/.gitignore index 41d7e0dc..348f1c65 100644 --- a/.gitignore +++ b/.gitignore @@ -170,3 +170,15 @@ CM*_proj/**/Release/* **/.vs/* tests/DelayedConnection/coordMerge.xml CM11_proj/tmp.obj + +# CARLA dependency bundle and local CMake build artifacts +CommonLib/libcarla/ +VirCarlaEnv/build/ +VirCarlaEnv/VirCarlaEnv/CMakeFiles/ +VirCarlaEnv/VirCarlaEnv/CMakeCache.txt +VirCarlaEnv/VirCarlaEnv/cmake_install.cmake +VirCarlaEnv/VirCarlaEnv/*.ninja +VirCarlaEnv/VirCarlaEnv/.ninja* +VirCarlaEnv/VirCarlaEnv/Makefile +VirCarlaEnv/VirCarlaEnv/compile_commands.json +VirCarlaEnv/VirCarlaEnv/*.cmake diff --git a/Carla/Roosevelt.net.xml b/Carla/Roosevelt.net.xml new file mode 100644 index 00000000..57725322 --- /dev/null +++ b/Carla/Roosevelt.net.xml @@ -0,0 +1,5036 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Carla/carla_tl_locs.csv b/Carla/carla_tl_locs.csv new file mode 100644 index 00000000..af90d7f7 --- /dev/null +++ b/Carla/carla_tl_locs.csv @@ -0,0 +1,12 @@ +1375.4974524064373,-1067.6701523048437,182.3,0,0,-91,1356.73,-1077.3400000000001,181.88,-1868_0,373 +1375.4974524064373,-1070.8701523048437,182.3,0,0,-91,1356.73,-1077.3400000000001,181.88,-1868_1,373 +1375.4974524064373,-1074.0701523048438,182.3,0,0,-91,1356.73,-1077.3400000000001,181.88,-1868_2,373 +1375.4974524064373,-1077.2701523048436,182.3,0,0,-91,1356.73,-1077.3400000000001,181.88,-1868_3,373 +1348.71,-1057.2,181.84,0,0,0,1356.73,-1077.3400000000001,181.88,-1871_0,373 +1351.91,-1057.2,181.84,0,0,0,1356.73,-1077.3400000000001,181.88,-1871_1,373 +1355.11,-1057.2,181.84,0,0,0,1356.73,-1077.3400000000001,181.88,-1871_2,373 +1338.0,-1086.48,181.46,0,0,-270,1356.73,-1077.3400000000001,181.88,-1880_0,373 +1338.0,-1083.28,181.46,0,0,-270,1356.73,-1077.3400000000001,181.88,-1880_1,373 +1338.0,-1080.08,181.46,0,0,-270,1356.73,-1077.3400000000001,181.88,-1880_2,373 +1361.5598476951563,-1099.4874524064373,181.95,0,0,-181,1356.73,-1077.3400000000001,181.88,-2001_0,373 +1358.3598476951563,-1099.4874524064373,181.95,0,0,-181,1356.73,-1077.3400000000001,181.88,-2001_1,373 diff --git a/Carla/carla_utils.py b/Carla/carla_utils.py new file mode 100644 index 00000000..addc7790 --- /dev/null +++ b/Carla/carla_utils.py @@ -0,0 +1,58 @@ +from re import T +import carla +import os +from CommonLib.ConfigHelper import ConfigHelper + +config_path = os.path.join(os.getcwd(), 'defaultConfig.yaml') +config_helper = ConfigHelper() +config_helper.getConfig(config_path) +carla_server_ip = config_helper.Carla_setup["CarlaServerIP"] +carla_server_port = config_helper.Carla_setup["CarlaServerPort"] + + +carla_client = carla.Client(carla_server_ip, carla_server_port) + +import carla + +def draw_axes_at_location(world, location, length=5.0, thickness=0.1, life_time=0.0, persistent=True): + """ + Draw X (red), Y (green), Z (blue) axes at the world origin (0,0,0). + """ + debug = world.debug + + # Red X-axis + debug.draw_line(location, carla.Location(x=length, y=0, z=100), + thickness=thickness, color=carla.Color(255, 0, 0), life_time=life_time, persistent_lines=persistent) + + # Green Y-axis + debug.draw_line(location, carla.Location(x=0, y=length, z=100), + thickness=thickness, color=carla.Color(0, 255, 0), life_time=life_time, persistent_lines=persistent) + + # Blue Z-axis + debug.draw_line(location, carla.Location(x=0, y=0, z=length), + thickness=thickness, color=carla.Color(0, 0, 255), life_time=life_time, persistent_lines=persistent) + +def move_spectator_to_location(world, location, pitch=-90.0): + """ + Move the spectator directly above the origin, looking down. + """ + spectator = world.get_spectator() + rotation = carla.Rotation(pitch=pitch, yaw=0) + spectator.set_transform(carla.Transform(location, rotation)) +def try_to_spawn_vehicle(world, location, rotation): + vehicle_bp = world.get_blueprint_library().find('vehicle.mini.cooper_s') + vehicle = world.try_spawn_actor(vehicle_bp, carla.Transform(location, rotation)) + return vehicle + +world = carla_client.get_world() +settings = world.get_settings() +settings.synchronous_mode = False +world.apply_settings(settings) + + +# draw_axes_at_location(world, carla.Location(334.887, 18.39, 0), length=100.0) +# move_spectator_to_location(world, carla.Location(334.887, 18.39, 100)) +# try_to_spawn_vehicle(world, carla.Location(334.887, 18.39, 0.0), carla.Rotation(pitch=0, yaw=90.0346, roll=0)) + + + diff --git a/Carla/dash_view.py b/Carla/dash_view.py new file mode 100644 index 00000000..f6984df2 --- /dev/null +++ b/Carla/dash_view.py @@ -0,0 +1,88 @@ +import carla +import os +from CommonLib.ConfigHelper import ConfigHelper +from dotenv import load_dotenv +import cv2 +import numpy as np +import random +def process_image(image): + array = np.frombuffer(image.raw_data, dtype=np.dtype("uint8")) + array = np.reshape(array, (image.height, image.width, 4)) # RGBA format + rgb_array = array[:, :, :3] # Drop the alpha channel + cv2.imshow("Front Camera", rgb_array) + cv2.waitKey(1) + + + +if __name__ == "__main__": + load_dotenv() + config_path = os.environ["CONFIG_PATH"] + config_helper = ConfigHelper() + config_helper.getConfig(config_path) + + carla_server_ip = config_helper.Carla_setup["CarlaServerIP"] + carla_server_port = config_helper.Carla_setup["CarlaServerPort"] + carla_client = carla.Client(carla_server_ip, carla_server_port) + + carla_world = carla_client.get_world() + carla_settings = carla_world.get_settings() + if not carla_settings.synchronous_mode: + carla_settings.synchronous_mode = True # Enable synchronous mode + carla_world.apply_settings(carla_settings) + carla_blueprint_library = carla_world.get_blueprint_library() + camera_bp = carla_blueprint_library.find('sensor.camera.rgb') + camera_bp.set_attribute('image_size_x', '800') + camera_bp.set_attribute('image_size_y', '600') + camera_bp.set_attribute('fov', '90') + camera_transform = carla.Transform(carla.Location(x=1.5, z=2.4)) # x is forward, z is up + # Wait until the ego vehicle is spawned in the carla world + RANDOM_SPAWN = True + ego_vehicle_role_name = 'ego' + ego_vehicle_carla_actor: carla.Vehicle = None + ego_vehicle_carla_actor_id = '' + while ego_vehicle_carla_actor is None: + try: + carla_vehicle_actors_in_world = carla_world.get_actors().filter('vehicle.*') + carla_vehicle_actor: carla.Vehicle + # if the ego vehicle is not spawned in carla + if RANDOM_SPAWN: + spawn_points = carla_world.get_map().get_spawn_points() + spawn_point = random.choice(spawn_points) + vehicle_blueprint = carla_blueprint_library.find('vehicle.tesla.model3') + vehicle_blueprint.set_attribute('role_name', ego_vehicle_role_name) + ego_vehicle_carla_actor = carla_world.spawn_actor(vehicle_blueprint, spawn_point) + # set as auto pilot + ego_vehicle_carla_actor.set_autopilot(True) + if ego_vehicle_carla_actor is None: + for carla_vehicle_actor in carla_vehicle_actors_in_world: + if 'role_name' in carla_vehicle_actor.attributes.keys(): + carla_actor_role_name = carla_vehicle_actor.attributes.get('role_name', None) + if carla_actor_role_name == ego_vehicle_role_name: + ego_vehicle_carla_actor = carla_vehicle_actor + ego_vehicle_carla_actor_id = carla_vehicle_actor.id + # if the ego vehicle is in carla + camera_actor: carla.Sensor + camera_actor = carla_world.spawn_actor(camera_bp, camera_transform, attach_to=ego_vehicle_carla_actor) + + except Exception as e: + print(f"Error occurred: {str(e)}") + + + camera_actor.listen(lambda image: process_image(image)) + latest_image = None + + try: + while True: + carla_world.tick() + if latest_image is not None: + cv2.imshow("Front Camera", latest_image) + if cv2.waitKey(1) & 0xFF == ord('q'): + break + except KeyboardInterrupt: + print("Stopping the simulation.") + finally: + camera_actor.stop() + ego_vehicle_carla_actor.destroy() + camera_actor.destroy() + cv2.destroyAllWindows() + print("Cleaning up...") \ No newline at end of file diff --git a/Carla/disable_carla_synchronous_mode.py b/Carla/disable_carla_synchronous_mode.py new file mode 100644 index 00000000..021c3bd4 --- /dev/null +++ b/Carla/disable_carla_synchronous_mode.py @@ -0,0 +1,20 @@ +import carla +import os +from CommonLib.ConfigHelper import ConfigHelper +from dotenv import load_dotenv +if __name__ == "__main__": + load_dotenv() + config_path = os.environ["CONFIG_PATH"] + config_helper = ConfigHelper() + config_helper.getConfig(config_path) + + carla_server_ip = config_helper.Carla_setup["CarlaServerIP"] + carla_server_port = config_helper.Carla_setup["CarlaServerPort"] + carla_client = carla.Client(carla_server_ip, carla_server_port) + + world = carla_client.get_world() + settings = world.get_settings() + settings.synchronous_mode = False + world.apply_settings(settings) + + diff --git a/Carla/extract_sumo_tls_as_table.py b/Carla/extract_sumo_tls_as_table.py new file mode 100644 index 00000000..b57f124b --- /dev/null +++ b/Carla/extract_sumo_tls_as_table.py @@ -0,0 +1,269 @@ +import math +import xml.etree.ElementTree as ET +import os +import pandas as pd +from shapely.geometry import LineString, Polygon, Point + +class SumoTLS: + def __init__(self, id, x, y, z, heading, link): + self.id = id + self.x = x + self.y = y + self.z = z + self.heading = heading + self.link = link + +class SumoTLSGroup: + def __init__(self, tl_id, x, y, z): + # this corresponds to the junction id in SUMO + self.id = tl_id + self.x = float(x) if x is not None else 0.0 + self.y = float(y) if y is not None else 0.0 + self.z = float(z) if z is not None else 0.0 + self.traffic_lights = {} + + def print_tls_info(self): + for tls in self.traffic_lights.values(): + print(tls.id, tls.x, tls.y, tls.z, tls.heading) + +# ------------------------------ +# Helpers for 2D/3D coordinates +# ------------------------------ +def parse_point_token(token): + """Parse 'x,y' or 'x,y,z' -> tuple of floats (2D or 3D). Return None if malformed.""" + parts = token.split(',') + try: + if len(parts) == 2: + x, y = map(float, parts) + return (x, y) + elif len(parts) == 3: + x, y, z = map(float, parts) + return (x, y, z) + except Exception: + return None + return None + +def to_xy(seq): + """Map list of 2D/3D points to list of (x,y).""" + return [(p[0], p[1]) for p in seq] + +def get_z(pt): + """Get z from a 2D/3D point; default 0.0 if missing.""" + return pt[2] if isinstance(pt, (list, tuple)) and len(pt) >= 3 else 0.0 + +def compute_perp_offsets(lane_width, count, idx): + """ + Given: + - lane_width: total width of that inLane + - count: total number of markers to place across this lane + - idx: zero‐based index (0 .. count-1) for the current marker + Returns offset_along_normal so that markers run from left edge → right edge. + If count == 1, returns 0.0 (center). + """ + if count == 1: + return 0.0 + span = lane_width + delta = span / (count * 2.0) + # center them about 0: from -span/2 to +span/2 + return -span/2 + (2 * idx + 1) * delta + +def parse_sumo_tls_from_netxml( + sumo_net_file, + offset_forward=0.0, + apply_linkwise_offset=True, + extend_back=50.0, + default_lane_width=3.2 +): + + tree = ET.parse(sumo_net_file) + root = tree.getroot() + + # 1) Junction polygons (for type="traffic_light") + junction_shapes = {} + junction_xyz = {} # (x,y,z) if present on the junction element (strings here; converted in SumoTLSGroup) + for jn in root.iter('junction'): + if jn.get('type') == 'traffic_light': + shape_str = jn.get('shape') + if shape_str: + coords_raw = [] + for tok in shape_str.strip().split(): + pt = parse_point_token(tok) + if pt is not None: + coords_raw.append(pt) + # Use only (x, y) for Polygon, ignore z if present + if len(coords_raw) >= 3: + junction_shapes[jn.get('id')] = Polygon(to_xy(coords_raw)) + # store x, y, z if provided (SUMO may omit z) + x = jn.get('x') + y = jn.get('y') + z = jn.get('z') # may be None + junction_xyz[jn.get('id')] = (x, y, z) + + # 2) Lane shapes & widths + lane_shape = {} # laneId -> list of 2D/3D tuples + lane_width = {} # laneId -> float + for edge in root.iter('edge'): + for ln in edge.iter('lane'): + lid = ln.get('id') + shp = ln.get('shape') + if shp: + pts = [] + for tok in shp.strip().split(): + pt = parse_point_token(tok) + if pt is not None: + pts.append(pt) + if len(pts) >= 2: + lane_shape[lid] = pts + w = ln.get('width') + lane_width[lid] = float(w) if w is not None else default_lane_width + + # 3) Connections by TLS and linkIndex (this replaces traci.trafficlight.getControlledLinks) + # build: tls_links[tls_id][linkIndex] -> list of (inLane, outLane, viaLane) + tls_links = {} + for conn in root.iter('connection'): + tls_id = conn.get('tl') + if not tls_id: + continue # not controlled by a traffic light + link_idx = conn.get('linkIndex') + if link_idx is None: + link_idx = "0" + + # from/to lane IDs come as indices; reconstruct lane IDs as "_" + from_edge = conn.get('from') + to_edge = conn.get('to') + from_lane_idx = conn.get('fromLane') + to_lane_idx = conn.get('toLane') + via_lane = conn.get('via') # usually present for internal links + + if from_edge is None or from_lane_idx is None: + continue + in_lane = f"{from_edge}_{from_lane_idx}" + out_lane = f"{to_edge}_{to_lane_idx}" if (to_edge is not None and to_lane_idx is not None) else None + + tls_links.setdefault(tls_id, {}) + tls_links[tls_id].setdefault(int(link_idx), []) + tls_links[tls_id][int(link_idx)].append((in_lane, out_lane, via_lane)) + + # 4) Build groups & compute positions + traffic_light_groups = {} + in_lane_links_total = {} + in_lane_links_placed = {} + + # Pre-count links per inLane for lateral spacing + for _, linkIndexGroups in tls_links.items(): + for _, triplets in linkIndexGroups.items(): + for inLane, _, _ in triplets: + in_lane_links_total[inLane] = in_lane_links_total.get(inLane, 0) + 1 + in_lane_links_placed.setdefault(inLane, 0) + + for tls_id, linkIndexGroups in tls_links.items(): + # ensure we have a group object + if tls_id not in traffic_light_groups: + jx, jy, jz = junction_xyz.get(tls_id, (None, None, None)) + traffic_light_groups[tls_id] = SumoTLSGroup(tls_id, jx, jy, jz) + + junction_poly = junction_shapes.get(tls_id) + + for link_idx, triplets in sorted(linkIndexGroups.items()): + for inLane, outLane, viaLane in triplets: + shape_pts = lane_shape.get(inLane) + if not shape_pts or len(shape_pts) < 2: + # cannot compute heading or intersection + continue + + # Use XY for geometry, keep Z from the lane end if present + shape_xy = to_xy(shape_pts) + (x1, y1) = shape_xy[-2] + (x2, y2) = shape_xy[-1] + end_z = get_z(shape_pts[-1]) # Z from lane end if available + + # last segment heading points "into" the junction + heading = math.atan2(y1 - y2, x1 - x2) + tx, ty = math.cos(heading), math.sin(heading) # tangent (toward lane end) + nx, ny = -ty, tx # left-hand normal + + # extend backwards from lane end (reverse of travel) in XY + reversed_extension = (x2 - extend_back * tx, y2 - extend_back * ty) + + # IMPORTANT: build a proper XY list for Shapely + extended_coords_xy = list(shape_xy) + [reversed_extension] + extended_line = LineString(extended_coords_xy) + + # intersect with junction polygon (if any) + inter_points = [] + if junction_poly: + inter = extended_line.intersection(junction_poly) + if isinstance(inter, Point): + inter_points = [(inter.x, inter.y)] + elif inter.geom_type == 'MultiPoint': + inter_points = [(p.x, p.y) for p in inter.geoms] + elif inter.geom_type == 'LineString': + inter_points = list(inter.coords) + + # pick farthest intersection point from lane end; else, use lane end + if inter_points: + end_pt = Point(shape_xy[-1]) + intersection_pt = max(inter_points, key=lambda p: Point(p).distance(end_pt)) + base_x, base_y = intersection_pt + base_z = end_z # no Z from intersection; keep lane end Z + else: + base_x, base_y = x2, y2 + base_z = end_z + + # If lane had no Z, fall back to junction Z; if missing, 0.0 + if base_z == 0.0: + base_z = traffic_light_groups[tls_id].z if traffic_light_groups[tls_id].z is not None else 0.0 + + # lateral offset across multiple links from the same inLane + L = in_lane_links_total.get(inLane, 1) + i = in_lane_links_placed.get(inLane, 0) + this_lane_width = lane_width.get(inLane, default_lane_width) + d_perp = -compute_perp_offsets(this_lane_width, L, i) if apply_linkwise_offset else 0.0 + + # forward offset along the tangent + bx = offset_forward * tx + by = -offset_forward * ty # keep your original sign convention + + px = base_x + bx + d_perp * nx + py = base_y + by + d_perp * ny + pz = base_z # keep Z constant (no vertical offset) + + traffic_light_groups[tls_id].traffic_lights.setdefault(link_idx, []).append( + SumoTLS(link_idx, px, py, pz, heading, (inLane, outLane, viaLane)) + ) + in_lane_links_placed[inLane] = i + 1 + + return traffic_light_groups, junction_shapes + +def tls_groups_to_df(traffic_light_groups): + data = [] + for junction_id, group in traffic_light_groups.items(): + for link_id, link in group.traffic_lights.items(): + for tls in link: + data.append({ + 'junction_id': junction_id, + 'link_id': link_id, + 'x': tls.x, + 'y': tls.y, + 'z': tls.z, + 'heading': tls.heading, + }) + return pd.DataFrame(data, columns=['junction_id', 'link_id', 'x', 'y', 'z', 'heading']) + + +if __name__ == "__main__": + sumo_net_file = 'test_scenarios\MLK\MLK_final_elevation.net.xml' + output_path = 'test_scenarios\MLK' + + traffic_light_groups, _ = parse_sumo_tls_from_netxml( + sumo_net_file, + offset_forward=0.0, + apply_linkwise_offset=True, + extend_back=50.0, + default_lane_width=3.2 + ) + traffic_light_groups_df = tls_groups_to_df(traffic_light_groups) + traffic_light_groups_df.to_csv( + os.path.join(output_path, 'traffic_light_table.csv'), + index=False + ) diff --git a/Carla/gen_tl_pose.py b/Carla/gen_tl_pose.py new file mode 100644 index 00000000..800f6267 --- /dev/null +++ b/Carla/gen_tl_pose.py @@ -0,0 +1,134 @@ +import pandas as pd +import xml.etree.ElementTree as ET +import math +import numpy as np +import csv +from sumo2carla import get_carla_transform + +xml_file = 'Roosevelt.net.xml' + +tree = ET.parse(xml_file) +root = tree.getroot() +tl_loc = [] +carla_tl_loc = [] + +junc_edges = {'373' : ['-1871', '-1880', '-1868', '-2001']} + +loc = root.find('location') +offset = [float(l) for l in loc.get('netOffset').split(',')] + +new_dict = {} + +for jn in junc_edges: + + new_dict[jn] = [] + + for j in junc_edges[jn]: + for c in root.findall('connection'): + if c.attrib['from'] == str(j) and c.attrib['dir'] == 's': + entry = {"from": c.attrib['from'], "to": c.attrib['to']} + if entry not in new_dict[jn]: + new_dict[jn].append(entry) + + for child in root: + + if child.tag == 'edge': + # go through the edges that are at junctions and are inc_lanes parents for ex [-116_0] is -116 child + edge_id = child.attrib['id'] + if edge_id in junc_edges[jn]: + + fr, to = child.attrib['from'], child.attrib['to'] + nxt_jn = [n for n in root.findall('junction') if n.attrib['id'] == str(to)] + nxt_jn = nxt_jn[0].attrib + jn_coords = get_carla_transform(offset=offset, + in_location= [float(nxt_jn['x']), float(nxt_jn['y']), float(nxt_jn['z'])], + in_rotation=(0,0,0)) + + for n in new_dict[jn]: + if n['from'] == edge_id: + to_edge = n['to'] + for n in root.findall('edge'): + if n.attrib['id'] == to_edge: + lane = n.attrib['shape'] + to_shape_start = [float(c) for c in lane.split(' ')[0].split(',')] + to_shape_end = [float(c) for c in lane.split(' ')[-1].split(',')] + + for l in child: + lane = l.attrib['shape'] + if lane: + coords = lane.split(' ') + shape_start =[float(c) for c in coords[0].split(',')] + shape_end = [float(c) for c in coords[-1].split(',')] + + dist_start = math.sqrt( (float(nxt_jn['x']) - shape_start[0])**2 + (float(nxt_jn['y']) - shape_start[1])**2 ) + dist_end = math.sqrt( (float(nxt_jn['x']) - shape_end[0])**2 + (float(nxt_jn['y']) - shape_end[1])**2 ) + dists = [dist_start, dist_end] + min_idx = (dists.index(min(dists))) + + # fyi, in rotation[2] has now been changed to be yaw instead of roll initially it was pitch,yaw,roll as output + # now it is roll, pitch, yaw. The traffic light asset faces you at yaw = 0. So if you need the light to face the + # end of the lane, it should be oriented the same way. + if min_idx == 0: + # This means that the shape_start is closer + bearing = math.degrees( math.atan2( (shape_start[1] - shape_end[1]), (shape_start[0] - shape_end[0]) ) ) + shape_delta = [to_shape_start[0] - shape_start[0], to_shape_start[1] - shape_start[1]] + carla_coords = get_carla_transform(offset=offset, + in_location= [shape_start[0] + shape_delta[0], shape_start[1], shape_start[2]], + in_rotation=(0,0, round(-bearing)) ) + tl_loc.append([shape_start[0], shape_start[1], l.attrib['id'], jn]) + carla_tl_loc.append( [carla_coords[0], + carla_coords[1], + carla_coords[2], + carla_coords[3], + carla_coords[4], + carla_coords[5], + jn_coords[0], + jn_coords[1], + jn_coords[2], + l.attrib['id'], + jn] ) + + elif min_idx == 1: + # This means that the shape_end is closer + bearing = math.degrees( math.atan2( (shape_end[1] - shape_start[1]), (shape_end[0] - shape_start[0]) ) ) + + shape_delta = [to_shape_start[0] - shape_end[0], to_shape_start[1] - shape_end[1]] + + if abs(bearing - 90) < 5 or abs(bearing + 90) < 5: + carla_coords = get_carla_transform(offset=offset, + in_location= [shape_end[0], shape_end[1] + shape_delta[1], shape_end[2]], + in_rotation=(0,0, round(-bearing)) ) + else: + carla_coords = get_carla_transform(offset=offset, + in_location= [shape_end[0] + shape_delta[0], shape_end[1], shape_end[2]], + in_rotation=(0,0, round(-bearing)) ) + + tl_loc.append([shape_end[0], shape_end[1], l.attrib['id'], jn]) + carla_tl_loc.append( [carla_coords[0], + carla_coords[1], + carla_coords[2], + carla_coords[3], + carla_coords[4], + carla_coords[5], + jn_coords[0], + jn_coords[1], + jn_coords[2], + l.attrib['id'], + jn] ) + else: + None + else: + print('No internal lanes found') + else: + pass + +tl_loc = np.array(tl_loc) +carla_tl_loc = np.array(carla_tl_loc) + +with open('tl_locs.csv','w') as file: + writer = csv.writer(file) + writer.writerows(tl_loc) + +with open('carla_tl_locs.csv','w') as file1: + writer = csv.writer(file1) + writer.writerows(carla_tl_loc) \ No newline at end of file diff --git a/Carla/spawn_ue.py b/Carla/spawn_ue.py new file mode 100644 index 00000000..030df4f6 --- /dev/null +++ b/Carla/spawn_ue.py @@ -0,0 +1,43 @@ +import unreal +import csv + +actors = unreal.EditorLevelLibrary.get_all_level_actors() + + +### Spawn Traffic Lights and TL Group according to intersection + +bp_path = '/Game/Carla/Static/TrafficLight/Streetlights_01/BP_TrafficLight.BP_TrafficLight' +blueprint_path = unreal.EditorAssetLibrary.load_blueprint_class(bp_path) + +tl_bp_path = '/Game/Carla/Static/TrafficLight/Streetlights_01/BP_TrafficLightGroup.BP_TrafficLightGroup' +tl_blueprint_path = unreal.EditorAssetLibrary.load_blueprint_class(tl_bp_path) + +jn_spawned = {} +id = 1 + +with open('/home/arms/carlatlgen/carla_tl_locs.csv', newline='') as csvfile: + csv_reader = csv.reader(csvfile) + + for row in csv_reader: + + if row[6] not in jn_spawned: + jn_spawned[row[6]] = True + location = unreal.Vector() + location.x = float(row[6]) * 100 + location.y = float(row[7]) * 100 + location.z = float(row[8]) * 100 + rotation = unreal.Rotator(0,0,0) + actor = unreal.EditorLevelLibrary.spawn_actor_from_class(tl_blueprint_path, location, rotation) + actor.set_actor_label(f'BP_TrafficLightGroup{row[-1]}') + + location = unreal.Vector() + location.x = float(row[0]) * 100 + location.y = float(row[1]) * 100 + location.z = float(row[2]) * 100 + rotation = unreal.Rotator(float(row[3]), + float(row[4]), + float(row[5])) + + + actor = unreal.EditorLevelLibrary.spawn_actor_from_class(blueprint_path, location, rotation) + actor.set_actor_label(f'{row[-2]}') diff --git a/Carla/sumo2carla.py b/Carla/sumo2carla.py new file mode 100644 index 00000000..6fd09d56 --- /dev/null +++ b/Carla/sumo2carla.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Thu Mar 20 13:43:01 2025 + +@author: arms +""" + +import carla +import math + +def get_carla_transform(offset=0, in_location=(0,0,0), in_rotation=(0,0,0)): + """ + Returns carla transform based on sumo transform. + """ + # offset = (801.49,264.53) + # in_location = (1223.40,331.17,181) + # in_rotation = (0,0,90) + + # From front-center-bumper to center (sumo reference system). + # (http://sumo.sourceforge.net/userdoc/Purgatory/Vehicle_Values.html#angle) + yaw = -1 * in_rotation[2] + 90 + pitch = in_rotation[1] + out_location = (in_location[0] - math.cos(math.radians(yaw)) * 1, + in_location[1] - math.sin(math.radians(yaw)) * 1, + in_location[2] - math.sin(math.radians(pitch)) * 1) + out_rotation = (in_rotation[0], in_rotation[1], in_rotation[2]) + + # Applying offset sumo-carla net. + out_location = (out_location[0] - offset[0], out_location[1] - offset[1], out_location[2]) + + ## Transform to carla reference system (left-handed system). + # out_transform = carla.Transform( + # carla.Location(out_location[0], -out_location[1], out_location[2]), + # carla.Rotation(out_rotation[0], out_rotation[1] - 90, out_rotation[2])) + + out_transform = (out_location[0], -out_location[1], out_location[2], out_rotation[0], out_rotation[1], out_rotation[2]-90) + + return out_transform diff --git a/Carla/tl_locs.csv b/Carla/tl_locs.csv new file mode 100644 index 00000000..0aaadaeb --- /dev/null +++ b/Carla/tl_locs.csv @@ -0,0 +1,12 @@ +1337.6,323.1,-1868_0,373 +1337.54,326.3,-1868_1,373 +1337.47,329.5,-1868_2,373 +1337.41,332.7,-1868_3,373 +1349.22,353.8,-1871_0,373 +1352.42,353.8,-1871_1,373 +1355.62,353.8,-1871_2,373 +1375.09,339.91,-1880_0,373 +1375.09,336.71,-1880_1,373 +1375.09,333.51,-1880_2,373 +1360.07,311.69,-2001_0,373 +1356.87,311.66,-2001_1,373 diff --git a/Carla/trafficlight_helper.py b/Carla/trafficlight_helper.py new file mode 100644 index 00000000..5d60e2bb --- /dev/null +++ b/Carla/trafficlight_helper.py @@ -0,0 +1,141 @@ +#!/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 . +""" This module provides a helper for the co-simulation between sumo and carla .""" + +# ================================================================================================== +# -- imports --------------------------------------------------------------------------------------- +# ================================================================================================== + +import collections +import carla # pylint: disable=import-error +import math + +# Conditional import for unreal module +try: + import unreal + UNREAL_AVAILABLE = True +except ImportError: + UNREAL_AVAILABLE = False + + +SumoTrafficLight = collections.namedtuple('SumoTrafficLight', 'junction_id link_id x y z heading') + + +# ================================================================================================== +# -- Bridge helper (SUMO <=> CARLA) ---------------------------------------------------------------- +# ================================================================================================== + + +class TrafficLightHelper(object): + """ + BridgeHelper provides methos to ease the co-simulation between sumo and carla. + """ + offset = (0, 0) + + @staticmethod + def set_offset(offset): + TrafficLightHelper.offset = offset + + @staticmethod + def create_sumo_traffic_light(junction_id, link_id, x, y, z, heading): + return SumoTrafficLight(junction_id, link_id, x, y, z, heading) + + @staticmethod + def create_sumo_transform(x, y, z, heading): + # heading is in radians, convert to degrees + x = float(x) + y = float(y) + z = float(z) + heading = float(heading) + heading = math.degrees(heading) + heading = (heading + 360) % 360 + location = carla.Location(x, y, z) + rotation = carla.Rotation(0, heading, 0) + return location, rotation + + + @staticmethod + def sumo_transform_to_carla_transform(in_sumo_location, in_sumo_rotation): + """ + Returns carla transform based on sumo transform. + """ + offset = TrafficLightHelper.offset + in_location = in_sumo_location + in_rotation = in_sumo_rotation + + out_location = (in_location.x, + in_location.y, + in_location.z) + out_rotation = (in_rotation.pitch, in_rotation.yaw, in_rotation.roll) + + # Applying offset sumo-carla net. + out_location = (out_location[0] - offset[0], out_location[1] - offset[1], out_location[2]) + + # Transform to carla reference system (left-handed system). + + return carla.Location(out_location[0], -out_location[1], out_location[2]), carla.Rotation(out_rotation[0], out_rotation[1] - 90, out_rotation[2]) + + + @staticmethod + def is_unreal_available(): + """ + Check if the unreal module is available. + """ + return UNREAL_AVAILABLE + + @staticmethod + def carla_transform_to_unreal_transform(carla_location, carla_rotation): + """ + Convert a CARLA transform (meters) to an Unreal Engine transform (centimeters). + """ + if not UNREAL_AVAILABLE: + raise ImportError("Unreal module is not available. This method requires the unreal module to be installed.") + + loc = carla_location + rot = carla_rotation + + # Convert meters to centimeters + unreal_location = unreal.Vector(loc.x * 100, loc.y * 100, loc.z * 100) + unreal_rotation = unreal.Rotator(rot.roll, rot.pitch, -rot.yaw + 180) + + return unreal.Vector(unreal_location.x, unreal_location.y, unreal_location.z), unreal.Rotator(unreal_rotation.roll, unreal_rotation.pitch, unreal_rotation.yaw) + + @staticmethod + def get_trafficlight_group_transform_from_trafficlight_unreal_transforms(trafficlight_unreal_transforms): + """ + Get the transform of a traffic light group from a list of traffic lights, by calculating the center of the traffic lights. + Returns the unreal transform of the traffic light group. + """ + if not UNREAL_AVAILABLE: + raise ImportError("Unreal module is not available. This method requires the unreal module to be installed.") + + # calculate the center of the traffic lights + center_x = sum(trafficlight_unreal_location.x for trafficlight_unreal_location, _ in trafficlight_unreal_transforms) / len(trafficlight_unreal_transforms) + center_y = sum(trafficlight_unreal_location.y for trafficlight_unreal_location, _ in trafficlight_unreal_transforms) / len(trafficlight_unreal_transforms) + center_z = sum(trafficlight_unreal_location.z for trafficlight_unreal_location, _ in trafficlight_unreal_transforms) / len(trafficlight_unreal_transforms) + + + # create the unreal transform of the traffic light group + return unreal.Vector(center_x, center_y, center_z), unreal.Rotator(0, 0, 0) + + @staticmethod + def carla_location_to_sumo_location(carla_location): + """ + Convert a CARLA location (meters) to a SUMO location (meters). + """ + offset = TrafficLightHelper.offset + in_location = carla_location + + out_location = (in_location.x, + -in_location.y, + in_location.z) + + # Applying offset sumo-carla net. + out_location = (out_location[0] + offset[0], out_location[1] + offset[1], out_location[2]) + return carla.Location(out_location[0], out_location[1], out_location[2]) + \ No newline at end of file diff --git a/Carla/unreal_scripts/placing_tls.py b/Carla/unreal_scripts/placing_tls.py new file mode 100644 index 00000000..3f8a4232 --- /dev/null +++ b/Carla/unreal_scripts/placing_tls.py @@ -0,0 +1,90 @@ +import unreal +import sys +import os +from dotenv import load_dotenv +# Add the script directories to sys.path +script_dir = os.path.dirname(__file__) +sys.path.append(script_dir) +sys.path.append(os.path.join(script_dir, "..")) +sys.path.append(os.path.join(script_dir, "..", "carla_scripts")) +sys.path.append(os.path.join(script_dir, "..", "utils")) +sys.path.append(os.path.join(script_dir, "..", "test_scenarios")) +import pandas as pd +import typing +import importlib +import utils.trafficlight_helper as trafficlight_helper +importlib.reload(trafficlight_helper) + +TrafficLightHelper = trafficlight_helper.TrafficLightHelper +# TrafficLightHelper.set_offset((0.06, 328.61)) # this is the offset for the CARLA town01 + + +load_dotenv(os.path.join(script_dir, "..", ".env")) + +TRAFFICLIGHT_GROUP_BLUEPRINT_PATH = "/Game/Carla/Static/TrafficLight/Streetlights_01/BP_TrafficLightGroup" +TRAFFICLIGHT_BLUEPRINT_PATH = "/Game/Carla/Static/TrafficLight/Streetlights_01/BP_TrafficLight" +TRAFFICLIGHT_HEAD_ONLY_BLUEPRINT_PATH = "/Game/Carla/Static/TrafficLight/Streetlights_01/BP_TrafficLight_Head_Only" +# load the tls table +TLS_TABLE_PATH = os.environ["SUMO_TLS_TABLE_PATH"] +tls_table = pd.read_csv(TLS_TABLE_PATH) +# junction_id,link_id,x,y,z,heading + +# this stores the trafficlight groups, key is the junction_id, value is a list of trafficlight_unreal_transforms +trafficlight_groups = typing.DefaultDict(list) + +for junction_id, group in tls_table.groupby('junction_id'): + for link_id, link_group in group.groupby('link_id'): + position_x = link_group['x'].values[0] + position_y = link_group['y'].values[0] + position_z = link_group['z'].values[0] + heading = link_group['heading'].values[0] + traffic_sumo_location, traffic_sumo_rotation = TrafficLightHelper.create_sumo_transform(position_x, position_y, position_z, heading) + trafficlight_carla_location, trafficlight_carla_rotation = TrafficLightHelper.sumo_transform_to_carla_transform(traffic_sumo_location, traffic_sumo_rotation) + trafficlight_unreal_location, trafficlight_unreal_rotation = TrafficLightHelper.carla_transform_to_unreal_transform(trafficlight_carla_location, trafficlight_carla_rotation) + trafficlight_groups[junction_id].append((trafficlight_unreal_location, trafficlight_unreal_rotation)) + +for junction_id, trafficlight_transforms in trafficlight_groups.items(): + trafficlight_group_location, trafficlight_group_rotation = TrafficLightHelper.get_trafficlight_group_transform_from_trafficlight_unreal_transforms(trafficlight_transforms) + + # spawn the trafficlight group + trafficlight_class = unreal.EditorAssetLibrary.load_blueprint_class(TRAFFICLIGHT_GROUP_BLUEPRINT_PATH) + trafficlight_group_actor = unreal.EditorLevelLibrary.spawn_actor_from_class(trafficlight_class, + trafficlight_group_location, + trafficlight_group_rotation + ) + trafficlight_group_actor.set_actor_label(f"TrafficLightGroup{junction_id}", mark_dirty=True) + unreal.log(f"Spawned TrafficLightGroup{junction_id} at {trafficlight_group_location}") + + # spawn the trafficlights + for (trafficlight_unreal_location, trafficlight_unreal_rotation) in trafficlight_transforms: + trafficlight_unreal_location.z = 300 + trafficlight_class = unreal.EditorAssetLibrary.load_blueprint_class(TRAFFICLIGHT_HEAD_ONLY_BLUEPRINT_PATH) + trafficlight_actor = unreal.EditorLevelLibrary.spawn_actor_from_class(trafficlight_class, + trafficlight_unreal_location, + trafficlight_unreal_rotation + ) + trafficlight_actor.set_actor_label(f"TrafficLight{trafficlight_unreal_location.x}_{trafficlight_unreal_location.y}", mark_dirty=True) + unreal.log(f"Spawned TrafficLight{trafficlight_unreal_location.x}_{trafficlight_unreal_location.y} at {trafficlight_unreal_location}") + # add the trafficlight to the trafficlight group's trafficlights list + current_list = trafficlight_group_actor.get_editor_property("TrafficLights") + current_list.append(trafficlight_actor) + trafficlight_group_actor.set_editor_property("TrafficLights", current_list) + # show the available properties of the trafficlight_group_actor + + + + + + + + + + + + + + + + + + diff --git a/Carla/unreal_scripts/test_placing_tls.py b/Carla/unreal_scripts/test_placing_tls.py new file mode 100644 index 00000000..0515396d --- /dev/null +++ b/Carla/unreal_scripts/test_placing_tls.py @@ -0,0 +1,33 @@ +import unreal +import sys +import os + +# Add the script directories to sys.path +script_dir = os.path.dirname(__file__) +sys.path.append(script_dir) +sys.path.append(os.path.join(script_dir, "..")) +sys.path.append(os.path.join(script_dir, "..", "carla_scripts")) +sys.path.append(os.path.join(script_dir, "..", "utils")) +# Asset path (relative to /Game) +blueprint_path = "/Game/Carla/Static/TrafficLight/Streetlights_01/BP_TrafficLight" + +# Load the Blueprint class +bp_class = unreal.EditorAssetLibrary.load_blueprint_class(blueprint_path) + +if not bp_class: + unreal.log_error(f"Failed to load blueprint class at: {blueprint_path}") +else: + # Define spawn location and rotation + location = unreal.Vector(32341.009766, 20203.007812, 100.0) + rotation = unreal.Rotator(0.0, 0.0, 90.0) + + # Spawn the actor in the level + actor = unreal.EditorLevelLibrary.spawn_actor_from_class(bp_class, location, rotation) + + if actor: + # Assign a custom label + custom_label = "TrafficLight37" + actor.set_actor_label(custom_label, mark_dirty=True) + unreal.log(f"✅ Spawned and labeled as: {custom_label}") + else: + unreal.log_error("❌ Failed to spawn actor.") \ No newline at end of file diff --git a/Carla/unreal_scripts/visualize_tls_table.py b/Carla/unreal_scripts/visualize_tls_table.py new file mode 100644 index 00000000..e171c611 --- /dev/null +++ b/Carla/unreal_scripts/visualize_tls_table.py @@ -0,0 +1,38 @@ +import matplotlib.pyplot as plt +import math +import pandas as pd + +def visualize_tls_positions(tls_data): + plt.figure(figsize=(10, 8)) + + for index, entry in tls_data.iterrows(): + x = entry["x"] + y = entry["y"] + heading = entry["heading"] + # Arrow parameters + heading = math.degrees(heading) + heading = (heading + 360) % 360 + + heading = math.radians(heading) + dx = math.cos(heading) * 1.5 # Arrow length + dy = math.sin(heading) * 1.5 + + # Plot arrow and label + plt.arrow(x, y, dx, dy, head_width=0.5, head_length=0.7, fc='green', ec='green') + # plt.text(x, y + 0.5, f"J{entry['junction_id']}-L{entry['link_id']}", fontsize=9, ha='center', color='black') + # mark the heading in text + heading = math.degrees(heading) + plt.text(x, y + 0.5, f"{heading:.2f}", fontsize=9, ha='center', color='black') + + plt.xlabel("X (m)") + plt.ylabel("Y (m)") + plt.title("Traffic Light Positions and Headings") + plt.grid(True) + plt.axis("equal") + # plt.show() + plt.savefig('tls_positions.png') + +if __name__ == "__main__": + tls_table_path = 'test_scenarios/Town01_with_ego_type_as_blueprint/traffic_light_table.csv' + tls_table = pd.read_csv(tls_table_path) + visualize_tls_positions(tls_table) \ No newline at end of file diff --git a/CommonLib/ConfigHelper.cpp b/CommonLib/ConfigHelper.cpp index 9c43740b..255513c7 100644 --- a/CommonLib/ConfigHelper.cpp +++ b/CommonLib/ConfigHelper.cpp @@ -385,8 +385,128 @@ int ConfigHelper::getConfig(string configName) { SumoSetup.SpeedMode = 0; //printf("\nXil not specified as server or client! Will set Xil as client!\n"); } + if (node["ExecutionOrder"]) { + SumoSetup.ExecutionOrder = parserInteger(node, "ExecutionOrder"); + } + else { + SumoSetup.ExecutionOrder = 1; + printf("\nSumo Execution Order not specified! Will use 1 as default!\n"); + } + + // =========================================================================== + // READ Carla Setup section + // =========================================================================== + node = config["CarlaSetup"]; + if (node["EnableVerboseLog"]) { + CarlaSetup.EnableVerboseLog = parserFlag(node, "EnableVerboseLog"); + } + else { + CarlaSetup.EnableVerboseLog = false; + printf("\nWill disable verbose log as default!\n"); + } + if (node["EnableCosimulation"]) { + CarlaSetup.EnableCosimulation = parserFlag(node, "EnableCosimulation"); + } + else { + CarlaSetup.EnableCosimulation = false; + } + + //if (node["EnableEgoSimulink"]) { + // CarlaSetup.EnableEgoSimulink = parserFlag(node, "EnableEgoSimulink"); + //} + //else { + // CarlaSetup.EnableEgoSimulink = false; + //} + if (node["EnableExternalControl"]) { + CarlaSetup.EnableExternalControl = parserFlag(node, "EnableExternalControl"); + } + else { + CarlaSetup.EnableExternalControl = false; + } + if (node["UseVehicleTypeAsBlueprint"]) { + CarlaSetup.UseVehicleTypeAsBlueprint = parserFlag(node, "UseVehicleTypeAsBlueprint"); + } + else { + CarlaSetup.UseVehicleTypeAsBlueprint = false; + } + + if (node["CenteredViewId"]) { + CarlaSetup.CenteredViewId = parserString(node, "CenteredViewId"); + } + else { + CarlaSetup.CenteredViewId = "ego"; + printf("\nCentered View Id not specified! Will use ego as default!\n"); + } + if (node["CarlaServerIP"]) { + CarlaSetup.CarlaServerIP = parserString(node, "CarlaServerIP"); + } + else { + CarlaSetup.CarlaServerIP = "127.0.0.1"; + } + + if (node["CarlaServerPort"]) { + CarlaSetup.CarlaServerPort = parserInteger(node, "CarlaServerPort"); + } + else { + CarlaSetup.CarlaServerPort = 2000; + } + if (node["CarlaClientIP"]) { + CarlaSetup.CarlaClientIP = parserString(node, "CarlaClientIP"); + } + else { + if (SubscriptionVehicleList.vehicleSubscribeId_v.size() == 1) { + if (!ApplicationSetup.EnableApplicationLayer && XilSetup.EnableXil) { + CarlaSetup.CarlaClientIP = get<2>(XilSetup.VehicleSubscription[0])[0]; + } + else { + CarlaSetup.CarlaClientIP = get<2>(ApplicationSetup.VehicleSubscription[0])[0]; + } + } + else { + CarlaSetup.CarlaClientIP = "127.0.0.1"; + } + } + + if (node["CarlaClientPort"]) { + CarlaSetup.CarlaClientPort = parserInteger(node, "CarlaClientPort"); + } + else { + if (SubscriptionVehicleList.vehicleSubscribeId_v.size() == 1) { + if (!ApplicationSetup.EnableApplicationLayer && XilSetup.EnableXil) { + CarlaSetup.CarlaClientPort = get<3>(XilSetup.VehicleSubscription[0])[0]; + } + else { + CarlaSetup.CarlaClientPort = get<3>(ApplicationSetup.VehicleSubscription[0])[0]; + } + } + else { + CarlaSetup.CarlaClientPort = 2001; + } + } + if (node["CarlaMap"]) { + CarlaSetup.CarlaMapName = parserString(node, "CarlaMapName"); + } + else { + CarlaSetup.CarlaMapName = "Town01"; + printf("\nCarla Map not specified! Will use Town01 as default!\n"); + } + if (node["TrafficRefreshRate"]) { + CarlaSetup.TrafficRefreshRate = parserDouble(node, "TrafficRefreshRate"); + } + else { + CarlaSetup.TrafficRefreshRate = 0.1; + //printf("\nCarMaker Port not specified! Will use 7331 as default!\n"); + } + + if (node["InterestedIds"]) { + parserStringVector(node, "InterestedIds", CarlaSetup.InterestedIds); + } + else { + CarlaSetup.InterestedIds = {"ego"}; + printf("\nDefault to track actor with id ego\n"); + } return 0; } diff --git a/CommonLib/ConfigHelper.h b/CommonLib/ConfigHelper.h index 4720ada6..f6c36e73 100644 --- a/CommonLib/ConfigHelper.h +++ b/CommonLib/ConfigHelper.h @@ -109,11 +109,37 @@ struct CarMakerSetup_t { }; +struct CarlaSetup_t { + bool EnableVerboseLog; + + bool EnableCosimulation; + + bool EnableExternalControl; + + bool UseVehicleTypeAsBlueprint; + + std::string CarlaServerIP; + + int CarlaServerPort; + + std::string CarlaClientIP; + + int CarlaClientPort; + + std::string CarlaMapName; + + std::string CenteredViewId; + + double TrafficRefreshRate; + + std::vector InterestedIds; + +}; struct SumoSetup_t { int SpeedMode; - + int ExecutionOrder; }; typedef struct SubscriptionVehicleList_t { @@ -178,6 +204,7 @@ class ConfigHelper XilSetup_t XilSetup; CarMakerSetup_t CarMakerSetup; SumoSetup_t SumoSetup; + CarlaSetup_t CarlaSetup; diff --git a/CommonLib/ConfigHelper.py b/CommonLib/ConfigHelper.py new file mode 100644 index 00000000..25835f3a --- /dev/null +++ b/CommonLib/ConfigHelper.py @@ -0,0 +1,120 @@ +import yaml +import os +from collections import defaultdict + +# Enum-like mapping +class TypeNamesEnum: + ego = 0 + link = 1 + point = 2 + vehicleType = 3 + intersection = 4 + detector = 5 + +# Map to associate the strings with enum values +s_mapTypeValues = { + "ego": TypeNamesEnum.ego, + "link": TypeNamesEnum.link, + "point": TypeNamesEnum.point, + "vehicleType": TypeNamesEnum.vehicleType, + "intersection": TypeNamesEnum.intersection, + "detector": TypeNamesEnum.detector +} + +class ConfigHelper: + def __init__(self): + # Initialize the s_mapTypeValues + self.simulation_setup = defaultdict(lambda: None) + self.application_setup = defaultdict(lambda: None) + self.Xil_setup = defaultdict(lambda: None) + self.CarMaker_setup = defaultdict(lambda: None) + self.Sumo_setup = defaultdict(lambda: None) + self.Carla_setup = defaultdict(lambda: None) + def getConfig(self, configName): + path = os.path.normpath(configName) + with open(path, 'r') as file: + config = yaml.safe_load(file) + + # Simulation Setup + simulation_node = config.get("SimulationSetup", {}) + self.simulation_setup["EnableRealSim"] = self.parserFlag(simulation_node, "EnableRealSim", True) + self.simulation_setup["EnableVerboseLog"] = self.parserFlag(simulation_node, "EnableVerboseLog", False) + self.simulation_setup["SimulationEndTime"] = self.parserDouble(simulation_node, "SimulationEndTime", 90000) + self.simulation_setup["EnableExternalDynamics"] = self.parserFlag(simulation_node, "EnableExternalDynamics", False) + self.simulation_setup["SelectedTrafficSimulator"] = self.parserString(simulation_node, "SelectedTrafficSimulator", "SUMO") + self.simulation_setup["TrafficSimulatorIP"] = self.parserString(simulation_node, "TrafficSimulatorIP", "127.0.0.1") + self.simulation_setup["TrafficSimulatorPort"] = self.parserInteger(simulation_node, "TrafficSimulatorPort", 1337) + self.simulation_setup["SimulationMode"] = self.parserInteger(simulation_node, "SimulationMode", 0) + self.simulation_setup["SimulationModeParameter"] = self.parserDouble(simulation_node, "SimulationModeParameter", 0) + self.simulation_setup["VehicleMessageField"] = self.parserStringVector(simulation_node, "VehicleMessageField", ["id", "type", "speed", "positionX", "positionY"]) + # Sumo Setup + sumo_node = config.get("SumoSetup", {}) + self.Sumo_setup["SpeedMode"] = sumo_node.get("SpeedMode", 0) + # Application Setup + app_node = config.get("ApplicationSetup", {}) + self.application_setup["EnableApplicationLayer"] = self.parserFlag(app_node, "EnableApplicationLayer", False) + self.application_setup["VehicleSubscription"] = self.parseVehicleSubscription(app_node, "VehicleSubscription", []) + + # Xil Setup + xil_node = config.get("XilSetup", {}) + self.Xil_setup["EnableXil"] = self.parserFlag(xil_node, "EnableXil", False) + self.Xil_setup["VehicleSubscription"] = self.parseVehicleSubscription(xil_node, "VehicleSubscription", []) + + # Carla Setup + carla_node = config.get("CarlaSetup", {}) + self.Carla_setup["EnableVerboseLog"] = self.parserFlag(carla_node, "EnableVerboseLog", False) + self.Carla_setup["EnableCosimulation"] = self.parserFlag(carla_node, "EnableCosimulation", True) + self.Carla_setup["EnableEgoSimulink"] = self.parserFlag(carla_node, "EnableEgoSimulink", False) + self.Carla_setup["CarlaServerIP"] = self.parserString(carla_node, "CarlaServerIP", "127.0.0.1") + self.Carla_setup["CarlaServerPort"] = self.parserInteger(carla_node, "CarlaServerPort", 420) + self.Carla_setup["CarlaClientIP"] = self.parserString(carla_node, "CarlaClientIP", "127.0.0.1") + self.Carla_setup["CarlaClientPort"] = self.parserInteger(carla_node, "CarlaClientPort", 430) + self.Carla_setup["CarlaMapName"] = self.parserString(carla_node, "CarlaMapName", "Town01") + self.Carla_setup["TrafficRefreshRate"] = self.parserDouble(carla_node, "TrafficRefreshRate", 0.1) + self.Carla_setup["InterestedIds"] = self.parserStringVector(carla_node, "InterestedIds", ["ego"]) + + def resetConfig(self): + # Clear all config settings + self.simulation_setup.clear() + self.application_setup.clear() + self.Xil_setup.clear() + self.CarMaker_setup.clear() + + def parserFlag(self, node, name, default=False): + return node.get(name, default) in ['true', True] + + def parserString(self, node, name, default=""): + return node.get(name, default) + + def parserDouble(self, node, name, default=0.0): + return float(node.get(name, default)) + + def parserInteger(self, node, name, default=0): + return int(node.get(name, default)) + + def parserStringVector(self, node, name, default): + return node.get(name, default) + + + def parseVehicleSubscription(self, node, name, default=[]): + # Process each subscription in the list + subscription_list = node.get(name, default) + if subscription_list is None: + return [] + parsed_subscriptions = [] + for sub in subscription_list: + parsed_subscription = { + "type": sub.get("type"), + "attribute": sub.get("attribute", {}), + "ip": sub.get("ip", []), + "port": sub.get("port", []) + } + parsed_subscriptions.append(parsed_subscription) + return parsed_subscriptions + + + +if __name__ == "__main__": + config_helper = ConfigHelper() + config_path = 'defaultConfig.yaml' + config_helper.getConfig(config_path) diff --git a/CommonLib/MsgHelper.cpp b/CommonLib/MsgHelper.cpp index a917b095..eb850069 100644 --- a/CommonLib/MsgHelper.cpp +++ b/CommonLib/MsgHelper.cpp @@ -117,6 +117,15 @@ void MsgHelper::printVehData(VehFullData_t VehData) { if (VehicleMessageField_set.find("activeLaneChange") != VehicleMessageField_set.end()) { printf("\t activeLaneChange: %d\n", VehData.activeLaneChange); } + if (VehicleMessageField_set.find("length") != VehicleMessageField_set.end()) { + printf("\t length: %f\n", VehData.length); + } + if (VehicleMessageField_set.find("width") != VehicleMessageField_set.end()) { + printf("\t width: %f\n", VehData.width); + } + if (VehicleMessageField_set.find("height") != VehicleMessageField_set.end()) { + printf("\t height: %f\n", VehData.height); + } if (VehicleMessageField_set.find("lightIndicators") != VehicleMessageField_set.end()) { printf("\t lightIndicators: %d\n", VehData.lightIndicators); } @@ -218,7 +227,18 @@ void MsgHelper::printVehDataToFile(const std::string fileName, VehFullData_t Veh if (VehicleMessageField_set.find("activeLaneChange") != VehicleMessageField_set.end()) { fprintf(f, "\t activeLaneChange: %d\n", VehData.activeLaneChange); } - + if (VehicleMessageField_set.find("length") != VehicleMessageField_set.end()) { + fprintf(f, "\t length: %f\n", VehData.length); + } + if (VehicleMessageField_set.find("width") != VehicleMessageField_set.end()) { + fprintf(f, "\t width: %f\n", VehData.width); + } + if (VehicleMessageField_set.find("height") != VehicleMessageField_set.end()) { + fprintf(f, "\t height: %f\n", VehData.height); + } + if (VehicleMessageField_set.find("lightIndicators") != VehicleMessageField_set.end()) { + fprintf(f, "\t lightIndicators: %d\n", VehData.lightIndicators); + } fclose(f); } @@ -360,8 +380,11 @@ void MsgHelper::packVehData(VehFullData_t VehData, char* buffer, int* iByte) { stringVehDataToBuffer(VehData.linkIdNext, "linkIdNext", buffer, iByte); numericVehDataToBuffer(VehData.grade, "grade", buffer, iByte); numericVehDataToBuffer(VehData.activeLaneChange, "activeLaneChange", buffer, iByte); + numericVehDataToBuffer(VehData.length, "length", buffer, iByte); + numericVehDataToBuffer(VehData.width, "width", buffer, iByte); + numericVehDataToBuffer(VehData.height, "height", buffer, iByte); numericVehDataToBuffer(VehData.lightIndicators, "lightIndicators", buffer, iByte); - + // packVehData: add new vehicle message field here // go back to the first byte location and parser message size @@ -412,6 +435,9 @@ void MsgHelper::depackVehData(char* buffer, VehFullData_t& VehData) { bufferToStringVehData(buffer, &iByte, "linkIdNext", tempString); VehData.linkIdNext = tempString; bufferToNumericVehData(buffer, &iByte, "grade", tempFloat); VehData.grade = tempFloat; bufferToNumericVehData(buffer, &iByte, "activeLaneChange", tempInt8); VehData.activeLaneChange = tempInt8; + bufferToNumericVehData(buffer, &iByte, "length", tempFloat); VehData.length = tempFloat; + bufferToNumericVehData(buffer, &iByte, "width", tempFloat); VehData.width = tempFloat; + bufferToNumericVehData(buffer, &iByte, "height", tempFloat); VehData.height = tempFloat; bufferToNumericVehData(buffer, &iByte, "lightIndicators", tempUint16); VehData.lightIndicators = tempUint16; // depackVehData: add new vehicle message field here diff --git a/CommonLib/MsgHelper.py b/CommonLib/MsgHelper.py new file mode 100644 index 00000000..f1624408 --- /dev/null +++ b/CommonLib/MsgHelper.py @@ -0,0 +1,605 @@ +from re import M +from typing import List + +from numpy import byte +from CommonLib.VehDataMsgDefs import VehData, TrafficLightData, DetectorData +import struct + + +class MessageType: + vehicle_data = 1 + traffic_light_data = 2 + detector_data = 3 + +# VehicleMessageField: [id, type, vehicleClass, speed, acceleration, positionX, positionY, positionZ, heading, +# color, linkId, laneId, distanceTravel, speedDesired, grade, length, width, height] + +class MsgHelper: + + def __init__(self): + self.vehicle_msg_field_valid = { + 'id': False, + 'type': False, + 'vehicleClass': False, + 'speed': False, + 'acceleration': False, + 'positionX': False, + 'positionY': False, + 'positionZ': False, + 'heading': False, + 'color': False, + 'linkId': False, + 'laneId': False, + 'distanceTravel': False, + 'speedDesired': False, + 'accelerationDesired': False, + 'hasPrecedingVehicle': False, + 'precedingVehicleId': False, + 'precedingVehicleDistance': False, + 'precedingVehicleSpeed': False, + 'signalLightId': False, + 'signalLightHeadId': False, + 'signalLightDistance': False, + 'signalLightColor': False, + 'speedLimit': False, + 'speedLimitNext': False, + 'speedLimitChangeDistance': False, + 'linkIdNext': False, + 'grade': False, + 'length': False, + 'width': False, + 'height': False, + 'activeLaneChange': False + } + self.traffic_light_msg_field_valid = { + 'id': False, + 'name': False, + 'state': False + } + self.detector_msg_field_valid = { + 'id': False, + 'name': False, + 'state': False, + } + # DO NOT change. These are predetermined message header size + # specified for Real-Sim + self.msg_header_size = 9 + self.msg_each_header_size = 3 + + + def set_vehicle_message_field(self, vehicle_msg_field: List[str]): + for field in vehicle_msg_field: + self.vehicle_msg_field_valid[field] = True + + def set_traffic_light_message_field(self, traffic_light_msg_field: List[str]): + for field in traffic_light_msg_field: + self.traffic_light_msg_field_valid[field] = True + + def set_detector_message_field(self, detector_msg_field: List[str]): + for field in detector_msg_field: + self.detector_msg_field_valid[field] = True + + @ staticmethod + def unpack_float(data, index): + value = struct.unpack('f', data[index:index+4])[0] + return value, index + 4 + # Helper function to unpack a single int32 + @ staticmethod + def unpack_int32(data, index): + value = struct.unpack('i', data[index:index+4])[0] + return value, index + 4 + # Helper function to unpack a single uint32 + @ staticmethod + def unpack_uint32(data, index): + value = struct.unpack('I', data[index:index+4])[0] + return value, index + 4 + @ staticmethod + def unpack_uint16(data, index): + value = struct.unpack('H', data[index:index+2])[0] + return value, index + 2 + @ staticmethod + def unpack_uint8(data, index): + value = struct.unpack('B', data[index:index+1])[0] + return value, index + 1 + # Helper function to unpack a single int8 + @ staticmethod + def unpack_int8(data, index): + value = struct.unpack('b', data[index:index+1])[0] + return value, index + 1 + + def depack_veh_data(self, byte_data: bytes)-> VehData: + veh_data = VehData() + byte_index = 0 # Index in byte_data + # byte_index += self.msg_header_size # Skip the message header + # byte_index += self.msg_each_header_size # Skip the message type header + # Helper function to unpack a single float + + # Unpack fields based on vehicle_msg_field_valid + if self.vehicle_msg_field_valid.get('id'): + str_data, str_len, byte_index, uint8Arr = MsgHelper.depack_string(byte_data, byte_index) + veh_data.id = str_data + + if self.vehicle_msg_field_valid.get('type'): + str_data, str_len, byte_index, uint8Arr = MsgHelper.depack_string(byte_data, byte_index) + veh_data.type = str_data + + if self.vehicle_msg_field_valid.get('vehicleClass'): + str_data, str_len, byte_index, uint8Arr = MsgHelper.depack_string(byte_data, byte_index) + veh_data.vehicleClass = str_data + + if self.vehicle_msg_field_valid.get('speed'): + veh_data.speed, byte_index = MsgHelper.unpack_float(byte_data, byte_index) + + if self.vehicle_msg_field_valid.get('acceleration'): + veh_data.acceleration, byte_index = MsgHelper.unpack_float(byte_data, byte_index) + + if self.vehicle_msg_field_valid.get('positionX'): + veh_data.positionX, byte_index = MsgHelper.unpack_float(byte_data, byte_index) + + if self.vehicle_msg_field_valid.get('positionY'): + veh_data.positionY, byte_index = MsgHelper.unpack_float(byte_data, byte_index) + + if self.vehicle_msg_field_valid.get('positionZ'): + veh_data.positionZ, byte_index = MsgHelper.unpack_float(byte_data, byte_index) + + if self.vehicle_msg_field_valid.get('heading'): + veh_data.heading, byte_index = MsgHelper.unpack_float(byte_data, byte_index) + + if self.vehicle_msg_field_valid.get('color'): + veh_data.color, byte_index = MsgHelper.unpack_uint32(byte_data, byte_index) + + if self.vehicle_msg_field_valid.get('linkId'): + str_data, str_len, byte_index, uint8Arr = MsgHelper.depack_string(byte_data, byte_index) + veh_data.linkId = str_data + + if self.vehicle_msg_field_valid.get('laneId'): + veh_data.laneId, byte_index = MsgHelper.unpack_int32(byte_data, byte_index) + + if self.vehicle_msg_field_valid.get('distanceTravel'): + veh_data.distanceTravel, byte_index = MsgHelper.unpack_float(byte_data, byte_index) + + if self.vehicle_msg_field_valid.get('speedDesired'): + veh_data.speedDesired, byte_index = MsgHelper.unpack_float(byte_data, byte_index) + + if self.vehicle_msg_field_valid.get('accelerationDesired'): + veh_data.accelerationDesired, byte_index = MsgHelper.unpack_float(byte_data, byte_index) + + if self.vehicle_msg_field_valid.get('hasPrecedingVehicle'): + veh_data.hasPrecedingVehicle, byte_index = MsgHelper.unpack_int8(byte_data, byte_index) + + if self.vehicle_msg_field_valid.get('precedingVehicleId'): + str_data, str_len, byte_index, uint8Arr = MsgHelper.depack_string(byte_data, byte_index) + veh_data.precedingVehicleId = str_data + + if self.vehicle_msg_field_valid.get('precedingVehicleDistance'): + veh_data.precedingVehicleDistance, byte_index = MsgHelper.unpack_float(byte_data, byte_index) + + if self.vehicle_msg_field_valid.get('precedingVehicleSpeed'): + veh_data.precedingVehicleSpeed, byte_index = MsgHelper.unpack_float(byte_data, byte_index) + + if self.vehicle_msg_field_valid.get('signalLightId'): + str_data, str_len, byte_index, uint8Arr = MsgHelper.depack_string(byte_data, byte_index) + veh_data.signalLightId = str_data + + if self.vehicle_msg_field_valid.get('signalLightHeadId'): + veh_data.signalLightHeadId, byte_index = MsgHelper.unpack_int32(byte_data, byte_index) + + if self.vehicle_msg_field_valid.get('signalLightDistance'): + veh_data.signalLightDistance, byte_index = MsgHelper.unpack_float(byte_data, byte_index) + + if self.vehicle_msg_field_valid.get('signalLightColor'): + veh_data.signalLightColor, byte_index = MsgHelper.unpack_int8(byte_data, byte_index) + + if self.vehicle_msg_field_valid.get('speedLimit'): + veh_data.speedLimit, byte_index = MsgHelper.unpack_float(byte_data, byte_index) + + if self.vehicle_msg_field_valid.get('speedLimitNext'): + veh_data.speedLimitNext, byte_index = MsgHelper.unpack_float(byte_data, byte_index) + + if self.vehicle_msg_field_valid.get('speedLimitChangeDistance'): + veh_data.speedLimitChangeDistance, byte_index = MsgHelper.unpack_float(byte_data, byte_index) + + if self.vehicle_msg_field_valid.get('linkIdNext'): + str_data, str_len, byte_index, uint8Arr = MsgHelper.depack_string(byte_data, byte_index) + veh_data.linkIdNext = str_data + + if self.vehicle_msg_field_valid.get('grade'): + veh_data.grade, byte_index = MsgHelper.unpack_float(byte_data, byte_index) + + if self.vehicle_msg_field_valid.get('activeLaneChange'): + veh_data.activeLaneChange, byte_index = MsgHelper.unpack_int8(byte_data, byte_index) + + if self.vehicle_msg_field_valid.get('length'): + veh_data.length, byte_index = MsgHelper.unpack_float(byte_data, byte_index) + + if self.vehicle_msg_field_valid.get('width'): + veh_data.width, byte_index = MsgHelper.unpack_float(byte_data, byte_index) + + if self.vehicle_msg_field_valid.get('height'): + veh_data.height, byte_index = MsgHelper.unpack_float(byte_data, byte_index) + + return veh_data + + def pack_veh_data(self, byte_data: bytearray, byte_index, veh_data: VehData): + # Calculate nMsgSize based on vehicle_msg_field_valid flags and veh_data field lengths + + + msg_size = ( + round(self.vehicle_msg_field_valid.get('id', 0) * (1 + len(veh_data.id)) # id + + self.vehicle_msg_field_valid.get('type', 0) * (1 + len(veh_data.type)) # type + + self.vehicle_msg_field_valid.get('vehicleClass', 0) * (1 + len(veh_data.vehicleClass)) # vehicleClass + + self.vehicle_msg_field_valid.get('speed', 0) * 4 # speed + + self.vehicle_msg_field_valid.get('acceleration', 0) * 4 # acceleration + + self.vehicle_msg_field_valid.get('positionX', 0) * 4 # positionX + + self.vehicle_msg_field_valid.get('positionY', 0) * 4 # positionY + + self.vehicle_msg_field_valid.get('positionZ', 0) * 4 # positionZ + + self.vehicle_msg_field_valid.get('heading', 0) * 4 # heading + + self.vehicle_msg_field_valid.get('color', 0) * 4 # color + + self.vehicle_msg_field_valid.get('linkId', 0) * (1 + len(veh_data.linkId)) # linkId + + self.vehicle_msg_field_valid.get('laneId', 0) * 4 # laneId + + self.vehicle_msg_field_valid.get('distanceTravel', 0) * 4 # distanceTravel + + self.vehicle_msg_field_valid.get('speedDesired', 0) * 4 # speedDesired + + self.vehicle_msg_field_valid.get('accelerationDesired', 0) * 4 # accelerationDesired + + self.vehicle_msg_field_valid.get('hasPrecedingVehicle', 0) * 1 # hasPrecedingVehicle + + self.vehicle_msg_field_valid.get('precedingVehicleId', 0) * (1 + len(veh_data.precedingVehicleId)) # precedingVehicleId + + self.vehicle_msg_field_valid.get('precedingVehicleDistance', 0) * 4 # precedingVehicleDistance + + self.vehicle_msg_field_valid.get('precedingVehicleSpeed', 0) * 4 # precedingVehicleSpeed + + self.vehicle_msg_field_valid.get('signalLightId', 0) * (1 + len(veh_data.signalLightId)) # signalLightId + + self.vehicle_msg_field_valid.get('signalLightHeadId', 0) * 4 # signalLightHeadId + + self.vehicle_msg_field_valid.get('signalLightDistance', 0) * 4 # signalLightDistance + + self.vehicle_msg_field_valid.get('signalLightColor', 0) * 1 # signalLightColor + + self.vehicle_msg_field_valid.get('speedLimit', 0) * 4 # speedLimit + + self.vehicle_msg_field_valid.get('speedLimitNext', 0) * 4 # speedLimitNext + + self.vehicle_msg_field_valid.get('speedLimitChangeDistance', 0) * 4 # speedLimitChangeDistance + + self.vehicle_msg_field_valid.get('linkIdNext', 0) * (1 + len(veh_data.linkIdNext)) # linkIdNext + + self.vehicle_msg_field_valid.get('grade', 0) * 4 # grade + + self.vehicle_msg_field_valid.get('activeLaneChange', 0) * 1 # activeLaneChange + + self.vehicle_msg_field_valid.get('length', 0) * 4 # length + + self.vehicle_msg_field_valid.get('width', 0) * 4 # width + + self.vehicle_msg_field_valid.get('height', 0) * 4 # height + ) + ) + veh_msg_size = round(msg_size) + self.msg_each_header_size + + # Pack message size as uint16, 2 bytes + byte_data[byte_index:byte_index+2] = struct.pack('H', veh_msg_size) + byte_index += 2 + # Pack message type as uint8, 1 byte + byte_data[byte_index] = MessageType.vehicle_data + byte_index += 1 + + + # Pack fields conditionally based on vehicle_msg_field_valid + if self.vehicle_msg_field_valid.get('id'): + byte_data, byte_index = MsgHelper.pack_string(byte_data, byte_index, veh_data.id) + + if self.vehicle_msg_field_valid.get('type'): + byte_data, byte_index = MsgHelper.pack_string(byte_data, byte_index, veh_data.type) + + if self.vehicle_msg_field_valid.get('vehicleClass'): + byte_data, byte_index = MsgHelper.pack_string(byte_data, byte_index, veh_data.vehicleClass) + + if self.vehicle_msg_field_valid.get('speed'): + byte_data[byte_index:byte_index+4] = struct.pack('f', veh_data.speed) + byte_index += 4 + + if self.vehicle_msg_field_valid.get('acceleration'): + byte_data[byte_index:byte_index+4] = struct.pack('f', veh_data.acceleration) + byte_index += 4 + + if self.vehicle_msg_field_valid.get('positionX'): + byte_data[byte_index:byte_index+4] = struct.pack('f', veh_data.positionX) + byte_index += 4 + + if self.vehicle_msg_field_valid.get('positionY'): + byte_data[byte_index:byte_index+4] = struct.pack('f', veh_data.positionY) + byte_index += 4 + + if self.vehicle_msg_field_valid.get('positionZ'): + byte_data[byte_index:byte_index+4] = struct.pack('f', veh_data.positionZ) + byte_index += 4 + + if self.vehicle_msg_field_valid.get('heading'): + byte_data[byte_index:byte_index+4] = struct.pack('f', veh_data.heading) + byte_index += 4 + + if self.vehicle_msg_field_valid.get('color'): + byte_data[byte_index:byte_index+4] = struct.pack('I', veh_data.color) + byte_index += 4 + + if self.vehicle_msg_field_valid.get('linkId'): + byte_data, byte_index = MsgHelper.pack_string(byte_data, byte_index, veh_data.linkId) + + if self.vehicle_msg_field_valid.get('laneId'): + byte_data[byte_index:byte_index+4] = struct.pack('i', veh_data.laneId) + byte_index += 4 + + if self.vehicle_msg_field_valid.get('distanceTravel'): + byte_data[byte_index:byte_index+4] = struct.pack('f', veh_data.distanceTravel) + byte_index += 4 + + if self.vehicle_msg_field_valid.get('speedDesired'): + byte_data[byte_index:byte_index+4] = struct.pack('f', veh_data.speedDesired) + byte_index += 4 + + if self.vehicle_msg_field_valid.get('accelerationDesired'): + byte_data[byte_index:byte_index+4] = struct.pack('f', veh_data.accelerationDesired) + byte_index += 4 + + if self.vehicle_msg_field_valid.get('hasPrecedingVehicle'): + byte_data[byte_index] = veh_data.hasPrecedingVehicle + byte_index += 1 + + if self.vehicle_msg_field_valid.get('precedingVehicleId'): + byte_data, byte_index = MsgHelper.pack_string(byte_data, byte_index, veh_data.precedingVehicleId) + + if self.vehicle_msg_field_valid.get('precedingVehicleDistance'): + byte_data[byte_index:byte_index+4] = struct.pack('f', veh_data.precedingVehicleDistance) + byte_index += 4 + + if self.vehicle_msg_field_valid.get('precedingVehicleSpeed'): + byte_data[byte_index:byte_index+4] = struct.pack('f', veh_data.precedingVehicleSpeed) + byte_index += 4 + + if self.vehicle_msg_field_valid.get('signalLightId'): + byte_data, byte_index = MsgHelper.pack_string(byte_data, byte_index, veh_data.signalLightId) + + if self.vehicle_msg_field_valid.get('signalLightHeadId'): + byte_data[byte_index:byte_index+4] = struct.pack('i', veh_data.signalLightHeadId) + byte_index += 4 + + if self.vehicle_msg_field_valid.get('signalLightDistance'): + byte_data[byte_index:byte_index+4] = struct.pack('f', veh_data.signalLightDistance) + byte_index += 4 + + if self.vehicle_msg_field_valid.get('signalLightColor'): + byte_data[byte_index] = veh_data.signalLightColor + byte_index += 1 + + if self.vehicle_msg_field_valid.get('speedLimit'): + byte_data[byte_index:byte_index+4] = struct.pack('f', veh_data.speedLimit) + byte_index += 4 + + if self.vehicle_msg_field_valid.get('speedLimitNext'): + byte_data[byte_index:byte_index+4] = struct.pack('f', veh_data.speedLimitNext) + byte_index += 4 + + if self.vehicle_msg_field_valid.get('speedLimitChangeDistance'): + byte_data[byte_index:byte_index+4] = struct.pack('f', veh_data.speedLimitChangeDistance) + byte_index += 4 + + if self.vehicle_msg_field_valid.get('linkIdNext'): + byte_data, byte_index = MsgHelper.pack_string(byte_data, byte_index, veh_data.linkIdNext) + + if self.vehicle_msg_field_valid.get('grade'): + byte_data[byte_index:byte_index+4] = struct.pack('f', veh_data.grade) + byte_index += 4 + + if self.vehicle_msg_field_valid.get('activeLaneChange'): + byte_data[byte_index] = veh_data.activeLaneChange + byte_index += 1 + + if self.vehicle_msg_field_valid.get('length'): + byte_data[byte_index:byte_index+4] = struct.pack('f', veh_data.length) + byte_index += 4 + + if self.vehicle_msg_field_valid.get('width'): + byte_data[byte_index:byte_index+4] = struct.pack('f', veh_data.width) + byte_index += 4 + + if self.vehicle_msg_field_valid.get('height'): + byte_data[byte_index:byte_index+4] = struct.pack('f', veh_data.height) + byte_index += 4 + + return byte_data, msg_size, byte_index + + def depack_traffic_light_data(self, byte_data: bytes)-> TrafficLightData: + traffic_light_data = TrafficLightData() + byte_index = 0 + if self.traffic_light_msg_field_valid.get('id'): + traffic_light_data.id, byte_index = MsgHelper.unpack_uint32(byte_data, byte_index) + if self.traffic_light_msg_field_valid.get('name'): + str_data, str_len, byte_index, uint8Arr = MsgHelper.depack_string(byte_data, byte_index) + traffic_light_data.name = str_data + if self.traffic_light_msg_field_valid.get('state'): + str_data, str_len, byte_index, uint8Arr = MsgHelper.depack_string(byte_data, byte_index) + traffic_light_data.state = str_data + + return traffic_light_data + + def pack_traffic_light_data(self, byte_data: bytearray, byte_index, traffic_light_data: TrafficLightData): + # Calculate nMsgSize based on traffic_light_msg_field_valid flags and traffic_light_data field lengths + msg_size = ( + round(self.traffic_light_msg_field_valid.get('id', 0) * 4 # id + + self.traffic_light_msg_field_valid.get('name', 0) * (1 + len(traffic_light_data.name)) # name + + self.traffic_light_msg_field_valid.get('state', 0) * (1 + len(traffic_light_data.state)) # state + ) + ) + traffic_light_msg_size = round(msg_size) + self.msg_each_header_size + + # Pack message size as uint16, 2 bytes + byte_data[byte_index:byte_index+2] = struct.pack('H', traffic_light_msg_size) + byte_index += 2 + # Pack message type as uint8, 1 byte + byte_data[byte_index] = MessageType.traffic_light_data + byte_index += 1 + + # Pack fields conditionally based on traffic_light_msg_field_valid + if self.traffic_light_msg_field_valid.get('id'): + byte_data[byte_index:byte_index+4] = struct.pack('I', traffic_light_data.id) + byte_index += 4 + + if self.traffic_light_msg_field_valid.get('name'): + byte_data, byte_index = MsgHelper.pack_string(byte_data, byte_index, traffic_light_data.name) + + if self.traffic_light_msg_field_valid.get('state'): + byte_data, byte_index = MsgHelper.pack_string(byte_data, byte_index, traffic_light_data.state) + + return byte_data, msg_size, byte_index + + def pack_msg_header(self, byte_data: bytearray, simulation_state: int, t: float, total_msg_size: int): + byte_index = 0 + # Pack simulation_state as uint8 + byte_data[byte_index] = simulation_state + byte_index += 1 + + # Pack t as float + byte_data[byte_index:byte_index+4] = struct.pack('f', t) + byte_index += 4 + + # Pack total_msg_size as uint32 + byte_data[byte_index:byte_index+4] = struct.pack('I', total_msg_size) + byte_index += 4 + + return byte_data, byte_index + + def pack_detector_data(self, byte_data: bytearray, byte_index, detector_data: DetectorData): + # Calculate nMsgSize based on detector_msg_field_valid flags and detector_data field lengths + msg_size = ( + round(self.detector_msg_field_valid.get('id', 0) * 1 # id + + self.detector_msg_field_valid.get('name', 0) * (1 + len(detector_data.name)) # name + + self.detector_msg_field_valid.get('state', 0) * 1 # state + ) + ) + detector_msg_size = round(msg_size) + self.msg_each_header_size + + # Pack message size as uint16, 2 bytes + byte_data[byte_index:byte_index+2] = struct.pack('H', detector_msg_size) + byte_index += 2 + # Pack message type as uint8, 1 byte + byte_data[byte_index] = MessageType.detector_data + byte_index += 1 + + # Pack fields conditionally based on detector_msg_field_valid + if self.detector_msg_field_valid.get('id'): + byte_data[byte_index] = detector_data.id + byte_index += 1 + + if self.detector_msg_field_valid.get('name'): + byte_data, byte_index = MsgHelper.pack_string(byte_data, byte_index, detector_data.name) + + if self.detector_msg_field_valid.get('state'): + byte_data[byte_index] = detector_data.state + byte_index += 1 + + return byte_data, msg_size, byte_index + + def depack_detector_data(self, byte_data: bytes)-> DetectorData: + detector_data = DetectorData() + byte_index = 0 + if self.detector_msg_field_valid.get('id'): + detector_data.id, byte_index = MsgHelper.unpack_uint8(byte_data, byte_index) + if self.detector_msg_field_valid.get('name'): + str_data, str_len, byte_index, uint8Arr = MsgHelper.depack_string(byte_data, byte_index) + detector_data.name = str_data + if self.detector_msg_field_valid.get('state'): + detector_data.state, byte_index = MsgHelper.unpack_uint8(byte_data, byte_index) + + return detector_data + # the primary level header of the message + def depack_msg_header(self, byte_data: bytearray): + + byte_index = 0 + sim_state, byte_index = MsgHelper.unpack_uint8(byte_data, byte_index) + sim_time, byte_index = MsgHelper.unpack_float(byte_data, byte_index) + total_msg_size, byte_index = MsgHelper.unpack_uint32(byte_data, byte_index) + + return sim_state, sim_time, total_msg_size + + # the secondary level header of each message + def depack_msg_type(self, byte_data: bytearray): + + byte_index = 0 + msg_size, byte_index = MsgHelper.unpack_uint16(byte_data, byte_index) + msg_type, byte_index = MsgHelper.unpack_int8(byte_data, byte_index) + + return msg_size, msg_type + + @ staticmethod + def depack_string(byte_data: bytearray, byte_index: int): + + # Read the length of the string + strLen = struct.unpack('B', byte_data[byte_index:byte_index+1])[0] + + byte_index += 1 + + # Initialize a string of size 50 and a uint8 array of size 50 + str_data = [''] * strLen + uint8Arr = [0] * strLen + + # Read characters from byte_data according to strLen + for i in range(strLen): + byte_value = byte_data[byte_index] + str_data[i] = chr(byte_value) # Convert byte to character + uint8Arr[i] = byte_value + byte_index += 1 + + # Convert list of characters to a single string + str_data = ''.join(str_data[:strLen]) + + return str_data, strLen, byte_index, uint8Arr + + @ staticmethod + def pack_string(byte_data: bytearray, byte_index: int, str_data: str): + # Get the length of the string + # str_data = str_data.encode('utf-8') # Convert string to bytes + str_data = str_data.encode('utf-8') + str_len = len(str_data) + # Pack the length of the string as a single byte + byte_data[byte_index] = str_len + byte_index += 1 + + # Pack the string itself up to str_len + byte_data[byte_index:byte_index+str_len] = str_data[:str_len] + byte_index += str_len + + return byte_data, byte_index + + + + +def test_pack_simple_message(): + msg_helper = MsgHelper() + + # Set fields to be packed + msg_helper.vehicle_msg_field_valid['id'] = True + msg_helper.vehicle_msg_field_valid['speed'] = True + + # Sample veh_data + veh_data = VehData() + veh_data.id = 'vehicle123' + veh_data.speed = 88.5 + + # Allocate byte_data buffer + byte_data = bytearray(100) # Pre-allocate buffer size + byte_index = 0 + # Pack data + packed_data, nMsgSize, byte_index = msg_helper.pack_veh_data(byte_data=byte_data, byte_index=byte_index, veh_data=veh_data) + + print("Packed byte_data:", packed_data[:nMsgSize]) + print("Packed message size:", nMsgSize) + + msg_size, msg_type = msg_helper.depack_msg_type(packed_data) + print("Unpacked msg_type:", msg_type) + print("Unpacked msg_size:", msg_size) + # veh_data = {} + unpacked_data = msg_helper.depack_veh_data(packed_data[msg_helper.msg_each_header_size:]) + + # print("Unpacked veh_data:", unpacked_data) + print(unpacked_data.id) + +def test_pack_string(): + + msg_helper = MsgHelper() + byte_data = bytearray(100) # Pre-allocate buffer size + byte_index = 0 + str_data = "Hello" + packed_data, byte_index = msg_helper.pack_string(byte_data, byte_index, str_data) + print("Packed string:", packed_data[:byte_index]) + + unpacked_str, str_len, byte_index, uint8Arr = msg_helper.depack_string(packed_data, 0) + print("Unpacked string:", unpacked_str) + +if __name__ == "__main__": + test_pack_simple_message() + test_pack_string() \ No newline at end of file diff --git a/CommonLib/RealSimPack.m b/CommonLib/RealSimPack.m index 9283920b..fd0b66b2 100644 --- a/CommonLib/RealSimPack.m +++ b/CommonLib/RealSimPack.m @@ -72,7 +72,7 @@ function setupImpl(obj) % % try % initialize ByteData - ByteData = zeros(200, 1, 'uint8'); + ByteData = zeros(1024, 1, 'uint8'); % parser ByteData [ByteData, nMsgSize] = obj.packVehData(simState, t, ByteData, VehDataBus); @@ -98,7 +98,7 @@ function validateInputsImpl(obj, simState, t, VehData) function [sz1, sz2] = getOutputSizeImpl(obj) % Maximum length of linear indices and element vector is the % number of elements in the input - sz1 = [200 1]; sz2 = [1 1]; + sz1 = [1024 1]; sz2 = [1 1]; end function [fz1, fz2] = isOutputFixedSizeImpl(~) diff --git a/CommonLib/RealSimSocket.cpp b/CommonLib/RealSimSocket.cpp index 4de866e4..efba51ab 100644 --- a/CommonLib/RealSimSocket.cpp +++ b/CommonLib/RealSimSocket.cpp @@ -39,7 +39,7 @@ #pragma comment (lib, "AdvApi32.lib") #define RECVBUFFERSIZE 1024 -#define SENDBUFFERSIZE 200 +#define SENDBUFFERSIZE 1024 // s-function parameters diff --git a/CommonLib/RealSimSocket.mexw64 b/CommonLib/RealSimSocket.mexw64 index ed035f5d..a4f4ab4a 100644 Binary files a/CommonLib/RealSimSocket.mexw64 and b/CommonLib/RealSimSocket.mexw64 differ diff --git a/CommonLib/SocketHelper.cpp b/CommonLib/SocketHelper.cpp index cc7fd3ba..1d6bd4ba 100644 --- a/CommonLib/SocketHelper.cpp +++ b/CommonLib/SocketHelper.cpp @@ -253,7 +253,7 @@ void SocketHelper::socketShutdown() { #else shutdown(selfServerSock[iS], SHUT_RDWR); shutdown(clientSock[iS], SHUT_RDWR); - #ifdef DSRTLX + #ifdef RS_DSPACE close(selfServerSock[iS]); close(clientSock[iS]); #endif @@ -265,7 +265,7 @@ void SocketHelper::socketShutdown() { closesocket(serverSock[iS]); #else shutdown(serverSock[iS], SHUT_RDWR); - #ifdef DSRTLX + #ifdef RS_DSPACE close(serverSock[iS]); #endif #endif @@ -401,7 +401,7 @@ int SocketHelper::initConnection(std::string errorLogName) { printSocketErrorMessage(WSAGetLastError()); #else printf("Unable to connect to server! error\n"); - #ifdef DSRTLX + #ifdef RS_DSPACE close(serverSock[iS]); #endif #endif @@ -439,7 +439,7 @@ int SocketHelper::initConnection(std::string errorLogName) { WSACleanup(); #else fprintf(stderr, "%s: \n", "socket() failed"); - #ifdef DSRTLX + #ifdef RS_DSPACE close(selfServerSock[iS]); #endif #endif @@ -470,7 +470,7 @@ int SocketHelper::initConnection(std::string errorLogName) { f << "bind() failed! error: " << endl; f.close(); } - #ifdef DSRTLX + #ifdef RS_DSPACE close(selfServerSock[iS]); #endif #endif @@ -495,7 +495,7 @@ int SocketHelper::initConnection(std::string errorLogName) { f.close(); } fprintf(stderr, "%s:\n", "listen() failed"); - #ifdef DSRTLX + #ifdef RS_DSPACE close(selfServerSock[iS]); #endif #endif @@ -581,7 +581,7 @@ int SocketHelper::initConnection(std::string errorLogName) { f.close(); } fprintf(stderr, "%s: \n", "accept() failed"); - #ifdef DSRTLX + #ifdef RS_DSPACE close(clientSock[iS]); #endif #endif @@ -684,7 +684,7 @@ int SocketHelper::initConnection(std::string errorLogName) { } fprintf(stderr, "%s: \n", "accept() failed"); //exit(1); - #ifdef DSRTLX + #ifdef RS_DSPACE close(clientSock[iS]); #endif #endif @@ -698,7 +698,7 @@ int SocketHelper::initConnection(std::string errorLogName) { } /* clientSock is connected to a client! */ - printf("Handling VISSIM client #%d %s\n", iS + 1 - N_ACT_CLIENT, inet_ntoa(clientAddr[iS].sin_addr)); + printf("Handling VISSIM client #%zu %s\n", iS + 1 - N_ACT_CLIENT, inet_ntoa(clientAddr[iS].sin_addr)); ClientConnected[iS] = 1; } @@ -747,7 +747,7 @@ int SocketHelper::initConnection(std::string errorLogName) { } printf("recv() failed with error code "); //exit(EXIT_FAILURE); - #ifdef DSRTLX + #ifdef RS_DSPACE close(clientSock[iS]); #endif #endif @@ -797,7 +797,7 @@ int SocketHelper::initConnection(std::string errorLogName) { f.close(); } printf("send failed with error:\n"); - #ifdef DSRTLX + #ifdef RS_DSPACE close(serverSock[iS]); #endif #endif @@ -992,4 +992,4 @@ int SocketHelper::sendData(int sock, int iClient, float simTimeSend, uint8_t sim return 0; -} \ No newline at end of file +} diff --git a/CommonLib/SocketHelper.h b/CommonLib/SocketHelper.h index 2f54f927..3843307a 100644 --- a/CommonLib/SocketHelper.h +++ b/CommonLib/SocketHelper.h @@ -39,7 +39,9 @@ #define SOCKET_ERROR (-1) //extern int close(int __fildes); +#ifndef RS_SKIP_CARMAKER #include +#endif #else #include @@ -147,14 +149,9 @@ class SocketHelper std::vector selfServerAddr; /* Local a ddress */ std::vector clientAddr; /* Client address */ -#ifndef WIN32 - std::vector clientAddrLen; /* Length of client address data structure */ -#else - std::vector clientAddrLen; /* Length of client address data structure */ -#endif + std::vector clientAddrLen; /* Length of client address data structure */ std::vector sendClientByte; std::vector recvClientMsgSize; /* Size of received message */ }; - diff --git a/CommonLib/SocketHelper.py b/CommonLib/SocketHelper.py index 43e595f0..2e4494cd 100644 --- a/CommonLib/SocketHelper.py +++ b/CommonLib/SocketHelper.py @@ -1,36 +1,44 @@ from struct import unpack, pack +from CommonLib.VehDataMsgDefs import VehData +from CommonLib.MsgHelper import MsgHelper, MessageType +from CommonLib.ConfigHelper import ConfigHelper +import typing class SocketHelper: - MSG_HEADER_SIZE = 9 - MSG_EACH_HEADER_SIZE = 3 MSG_CODER = 'utf-8' - def __init__(self): + def __init__(self, config_helper: ConfigHelper, msg_helper: MsgHelper): # empty constructer aa = 1 + self.config_helper = config_helper + self.msg_helper = msg_helper + self.msg_header_size = self.msg_helper.msg_header_size + self.msg_each_header_size = self.msg_helper.msg_each_header_size - def packHeader(self, simState, simTime, totalMsgSize): - buffer = pack(' laneList = Lane::getIDList(); + vector laneList = traci.lane.getIDList(); vector vehClassList; - vector vehTypeList = VehicleType::getIDList(); + vector vehTypeList = traci.vehicletype.getIDList(); for (int i = 0; i < vehTypeList.size(); i++) { string vehType = vehTypeList[i]; - string vehClass = VehicleType::getVehicleClass(vehType); + string vehClass = traci.vehicletype.getVehicleClass(vehType); vehClassList.push_back(vehClass); } for (int i = 0; i < laneList.size(); i++) { string laneId = laneList[i]; - string edgeId = Lane::getEdgeID(laneId); + string edgeId = traci.lane.getEdgeID(laneId); - vector allowClassList = Lane::getAllowed(laneId); - vector disallowClassList = Lane::getDisallowed(laneId); + vector allowClassList = traci.lane.getAllowed(laneId); + vector disallowClassList = traci.lane.getDisallowed(laneId); if (allowClassList.size() == 0 && disallowClassList.size() == 0) { for (int iC = 0; iC < vehClassList.size(); iC++) { string vClass = vehClassList[iC]; - LaneVehClass2SpeedLimit_um[make_pair(laneId, vClass)] = Lane::getMaxSpeed(laneId); + LaneVehClass2SpeedLimit_um[make_pair(laneId, vClass)] = traci.lane.getMaxSpeed(laneId); - EdgeVehClass2SpeedLimit_um[make_pair(edgeId, vClass)] = Lane::getMaxSpeed(laneId); + EdgeVehClass2SpeedLimit_um[make_pair(edgeId, vClass)] = traci.lane.getMaxSpeed(laneId); } } else { for (int iC = 0; iC < allowClassList.size(); iC++) { string vClass = allowClassList[iC]; - LaneVehClass2SpeedLimit_um[make_pair(laneId, vClass)] = Lane::getMaxSpeed(laneId); + LaneVehClass2SpeedLimit_um[make_pair(laneId, vClass)] = traci.lane.getMaxSpeed(laneId); - EdgeVehClass2SpeedLimit_um[make_pair(edgeId, vClass)] = Lane::getMaxSpeed(laneId); + EdgeVehClass2SpeedLimit_um[make_pair(edgeId, vClass)] = traci.lane.getMaxSpeed(laneId); } } } - vector edgeList = Edge::getIDList(); + vector edgeList = traci.edge.getIDList(); for (int i = 0; i < edgeList.size(); i++) { AllEdgeList.insert(edgeList[i]); } @@ -240,7 +244,8 @@ void TrafficHelper::selectSUMO() { void TrafficHelper::close() { if (SUMO_OR_VISSIM.compare("SUMO") == 0) { - Simulation::close(); + /*Simulation::close();*/ + traci.close(); } else if (SUMO_OR_VISSIM.compare("VISSIM") == 0) { @@ -364,12 +369,39 @@ int TrafficHelper::addEgoVehicle(double simTime) { // if is empty if (typeStr.size() == 0) { - Vehicle::add(idStr, ""); + traci.vehicle.add(idStr, ""); } else { - Vehicle::add(idStr, "", typeStr); + traci.vehicle.add(idStr, "", typeStr); } - Vehicle::setColor(idStr, libsumo::TraCIColor(255, 0, 0)); + traci.vehicle.setColor(idStr, libsumo::TraCIColor(255, 0, 0)); + } + + return 1; + } + else { + return 0; + } + +} + +int TrafficHelper::addEgoVehicleFromXY(double simTime, std::string vehicleId, std::string vehicleType, double positionX, double positionY) { + + if (SUMO_OR_VISSIM.compare("SUMO") == 0) { + if (ENABLE_VEH_SIMULATOR) { + // Map the x y positon to an edge for spawing the ego vehicle + libsumo::TraCIRoadPosition edgePosition = traci.simulation.convertRoad(positionX, positionY, false); + // Create a dummy route for the ego vehicle + std::string dummyedgeID = edgePosition.edgeID; + double lanePos = edgePosition.pos; // position along the edge + int laneIndex = edgePosition.laneIndex; + std::string dummyRouteId = "route_" + vehicleId; + std::vector dummyRoute; + dummyRoute.push_back(dummyedgeID); + traci.route.add(dummyRouteId, dummyRoute); + + traci.vehicle.add(vehicleId, dummyRouteId, vehicleType); + traci.vehicle.setColor(vehicleId, libsumo::TraCIColor(255, 0, 0)); } return 1; @@ -384,9 +416,9 @@ int TrafficHelper::addEgoVehicle(double simTime) { int TrafficHelper::checkIfEgoExist(double* simTime) { if (SUMO_OR_VISSIM.compare("SUMO") == 0) { - *simTime = Simulation::getTime(); - - vector VehIdInSimulator = Vehicle::getIDList(); + /**simTime = Simulation::getTime();*/ + *simTime = traci.simulation.getTime(); + vector VehIdInSimulator = traci.vehicle.getIDList(); // check if subscribed vheicle is in the network for (auto& iter : vehicleSubscribeId_v) { @@ -412,7 +444,8 @@ int TrafficHelper::checkIfEgoExist(double* simTime) { int TrafficHelper::getSimulationTime(double* simTime) { if (SUMO_OR_VISSIM.compare("SUMO") == 0) { - *simTime = Simulation::getTime(); + /**simTime = Simulation::getTime();*/ + *simTime = traci.simulation.getTime(); return 1; } else { @@ -422,7 +455,8 @@ int TrafficHelper::getSimulationTime(double* simTime) { int TrafficHelper::runSimulation(double endTime) { if (SUMO_OR_VISSIM.compare("SUMO") == 0) { - Simulation::step(endTime); + /*Simulation::step(endTime);*/ + traci.simulationStep(endTime); return 1; } else { @@ -452,16 +486,16 @@ int TrafficHelper::sendToSUMO(double simTime, MsgHelper Msg_c) { // //unsigned int id = stoi(idStr.substr(12)); // //if (idStr.compare("flow_0.10") == 0) { - // // Vehicle::setColor(idStr, libsumo::TraCIColor(255, 0, 0, 255)); + // // traci.vehicle.setColor(idStr, libsumo::TraCIColor(255, 0, 0, 255)); // //} // //if (idStr.compare("flow_1.9") == 0) { - // // Vehicle::setColor(idStr, libsumo::TraCIColor(0, 166, 255, 255)); + // // traci.vehicle.setColor(idStr, libsumo::TraCIColor(0, 166, 255, 255)); // //} // //if (std::find(Msg_c.VehIdRecv_v.begin(), Msg_c.VehIdRecv_v.end(), idStr) != Msg_c.VehIdRecv_v.end()) { // if (Msg_c.VehDataRecv_um.find(idStr) != Msg_c.VehDataRecv_um.end()) { - // Vehicle::setSpeed(idStr, Msg_c.VehDataRecv_um[idStr].speed); - // Vehicle::setSpeedMode(idStr, 0); // most checks off + // traci.vehicle.setSpeed(idStr, Msg_c.VehDataRecv_um[idStr].speed); + // traci.vehicle.setSpeedMode(idStr, 0); // most checks off // if (ENABLE_VERBOSE) { @@ -471,7 +505,7 @@ int TrafficHelper::sendToSUMO(double simTime, MsgHelper Msg_c) { // } // } // else { - // //Vehicle::setSpeedMode(idStr, 31); + // //traci.vehicle.setSpeedMode(idStr, 31); // } // } @@ -489,17 +523,16 @@ int TrafficHelper::sendToSUMO(double simTime, MsgHelper Msg_c) { //} try { - vector VehIdInSimulator = Vehicle::getIDList(); - + vector VehIdInSimulator = traci.vehicle.getIDList(); //uint32_t color = 4278190335; //uint8_t r = (color >> 24) & 0xFF; //uint8_t g = (color >> 16) & 0xFF; //uint8_t b = (color >> 8) & 0xFF; //uint8_t a = (color) & 0xFF; - //Vehicle::setColor("flow_0.0", libsumo::TraCIColor(r, g, b, a)); + //traci.vehicle.setColor("flow_0.0", libsumo::TraCIColor(r, g, b, a)); for (int iV = 0; iV < VehIdInSimulator.size(); iV++) { - //Vehicle::setSpeedMode(VehIdInSimulator[iV], 31); // default speed mode + //traci.vehicle.setSpeedMode(VehIdInSimulator[iV], 31); // default speed mode } for (int iV = 0; iV < Msg_c.VehDataSend_um[0].size(); iV++) { @@ -520,7 +553,7 @@ int TrafficHelper::sendToSUMO(double simTime, MsgHelper Msg_c) { if (ENABLE_VERBOSE) { if (!ENABLE_VEH_SIMULATOR && find(VehIdInSimulator.begin(), VehIdInSimulator.end(), idStr) != VehIdInSimulator.end()) { - double speedOld = Vehicle::getSpeed(idStr); + double speedOld = traci.vehicle.getSpeed(idStr); printf("Set SUMO id %s from speed %.4f to speed %.4f\n", idStr.c_str(), speedOld, speed); FILE* f = fopen(MasterLogName.c_str(), "a"); @@ -540,17 +573,17 @@ int TrafficHelper::sendToSUMO(double simTime, MsgHelper Msg_c) { // // if is empty // if (typeStr.size() == 0) { - // Vehicle::add(idStr, ""); + // traci.vehicle.add(idStr, ""); // } // else { - // Vehicle::add(idStr, "", typeStr); + // traci.vehicle.add(idStr, "", typeStr); // } - // Vehicle::setColor(idStr, libsumo::TraCIColor(255, 0, 0)); + // traci.vehicle.setColor(idStr, libsumo::TraCIColor(255, 0, 0)); //} // otherwise, move it { if (ENABLE_EXT_DYN) { - Vehicle::setPreviousSpeed(idStr, speed); // setting speed at (k) will be reflected at (k) "immediately", i.e., be considered in the next integration + traci.vehicle.setPreviousSpeed(idStr, speed); // setting speed at (k) will be reflected at (k) "immediately", i.e., be considered in the next integration } else { @@ -559,7 +592,7 @@ int TrafficHelper::sendToSUMO(double simTime, MsgHelper Msg_c) { double positionZ = (double)Msg_c.VehDataSend_um[0][iV].positionZ; double heading = (double)Msg_c.VehDataSend_um[0][iV].heading; - Vehicle::moveToXY(idStr, "", -1, positionX, positionY, heading, 6); // keepRoute 110 => 6 + traci.vehicle.moveToXY(idStr, "", -1, positionX, positionY, heading, 6); // keepRoute 110 => 6 //bit0(keepRoute = 1 when only this bit is set) //1: The vehicle is mapped to the closest edge within it's existing route. If no suitable position is found within 100m mapping fails with an error. //0 : The vehicle is mapped to the closest edge within the network.If that edge does not belong to the original route, the current route is replaced by a new route which consists of that edge only.If no suitable position is found within 100m mapping fails with an error.When using the sublane model the best lateral position that is fully within the lane will be used.Otherwise, the vehicle will drive in the center of the closest lane. @@ -573,19 +606,41 @@ int TrafficHelper::sendToSUMO(double simTime, MsgHelper Msg_c) { } if (VehicleMessageField_set.find("lightIndicators") != VehicleMessageField_set.end()) { - Vehicle::setSignals(idStr, (int)Msg_c.VehDataSend_um[0][iV].lightIndicators); + traci.vehicle.setSignals(idStr, (int)Msg_c.VehDataSend_um[0][iV].lightIndicators); } } } + // if carla is enabled and the reveiced id is within the interested ids + else if (ENABLE_CARLA&&ENABLE_CARLA_EXTERNAL_CONTROL&&find(Config_c->CarlaSetup.InterestedIds.begin(), Config_c->CarlaSetup.InterestedIds.end(), idStr) != Config_c->CarlaSetup.InterestedIds.end()) { + + double positionX = (double)Msg_c.VehDataSend_um[0][iV].positionX; + double positionY = (double)Msg_c.VehDataSend_um[0][iV].positionY; + double positionZ = (double)Msg_c.VehDataSend_um[0][iV].positionZ; + double heading = (double)Msg_c.VehDataSend_um[0][iV].heading; + string vehicleType = Msg_c.VehDataSend_um[0][iV].type; + // If the Intertested Vehicle is not in sumo + bool vehicleExist = false; + for (const std::string& vehId : VehIdInSimulator) { + if (vehId == idStr) { + vehicleExist = true; + } + } + if (!vehicleExist) { + addEgoVehicleFromXY(simTime, idStr, vehicleType, positionX, positionY); + } + traci.vehicle.moveToXY(idStr, "", -1, positionX, positionY, heading, 6); + traci.vehicle.setSpeed(idStr, speed); + + } else { if (1 && find(VehIdInSimulator.begin(), VehIdInSimulator.end(), idStr) != VehIdInSimulator.end()) { if (ENABLE_EXT_DYN) { - Vehicle::setPreviousSpeed(idStr, speed); // setting speed at (k) will be reflected at (k) "immediately", i.e., be considered in the next integration + traci.vehicle.setPreviousSpeed(idStr, speed); // setting speed at (k) will be reflected at (k) "immediately", i.e., be considered in the next integration } else { - Vehicle::setSpeed(idStr, speed); // speed set at (k) essentially will be reflected at (k+1), not considered in the integration - + traci.vehicle.setSpeed(idStr, speed); // speed set at (k) essentially will be reflected at (k+1), not considered in the integration + std::cout << "Set SUMO id " << idStr << " to speed " << speed << std::endl; /* bit0: Regard safe speed bit1 : Regard maximum acceleration @@ -595,12 +650,12 @@ int TrafficHelper::sendToSUMO(double simTime, MsgHelper Msg_c) { bit5 : Disregard right of way within intersections(only applies to foe vehicles that have entered the intersection). */ - Vehicle::setSpeedMode(idStr, Config_c->SumoSetup.SpeedMode); // 000000 most checks off - //Vehicle::setSpeedMode(idStr, 0); // 000000 most checks off - //Vehicle::setSpeedMode(idStr, 24); // 011000 - //Vehicle::setSpeedMode(idStr, 8); // 001000 + //traci.vehicle.setSpeedMode(idStr, Config_c->SumoSetup.SpeedMode); // 000000 most checks off + //traci.vehicle.setSpeedMode(idStr, 0); // 000000 most checks off + //traci.vehicle.setSpeedMode(idStr, 24); // 011000 + //traci.vehicle.setSpeedMode(idStr, 8); // 001000 - //Vehicle::setSpeedFactor(idStr, 1); + //traci.vehicle.setSpeedFactor(idStr, 1); } // change vehicle color if needed @@ -610,7 +665,7 @@ int TrafficHelper::sendToSUMO(double simTime, MsgHelper Msg_c) { uint8_t g = (color >> 16) & 0xFF; uint8_t b = (color >> 8) & 0xFF; uint8_t a = (color) & 0xFF; - Vehicle::setColor(idStr, libsumo::TraCIColor(r, g, b, a)); + traci.vehicle.setColor(idStr, libsumo::TraCIColor(r, g, b, a)); } } @@ -624,7 +679,7 @@ int TrafficHelper::sendToSUMO(double simTime, MsgHelper Msg_c) { for (int iS = 0; iS < Msg_c.TlsDataSend_um[0].size(); iS++) { string idStr = Msg_c.TlsDataSend_um[0][iS].name; - TrafficLight::setRedYellowGreenState(idStr, Msg_c.TlsDataSend_um[0][iS].state); + traci.trafficlights.setRedYellowGreenState(idStr, Msg_c.TlsDataSend_um[0][iS].state); } } @@ -763,8 +818,8 @@ void TrafficHelper::parseSendMsg(MsgHelper MsgIn_c, MsgHelper& MsgOut_c) { void TrafficHelper::runOneStepSimulation() { if (SUMO_OR_VISSIM.compare("SUMO") == 0) { - Simulation::step(); - //traci.simulationStep(); + /*Simulation::step();*/ + traci.simulationStep(); } else if (SUMO_OR_VISSIM.compare("VISSIM") == 0) { @@ -797,8 +852,9 @@ int TrafficHelper::recvFromSUMO(double* simTime, MsgHelper& Msg_c) { VehIdInSimulator.clear(); - *simTime = Simulation::getTime(); - VehIdInSimulator = Vehicle::getIDList(); + /**simTime = Simulation::getTime();*/ + *simTime = traci.simulation.getTime(); + VehIdInSimulator = traci.vehicle.getIDList(); int nVeh = VehIdInSimulator.size(); // number of vehicles @@ -830,7 +886,7 @@ int TrafficHelper::recvFromSUMO(double* simTime, MsgHelper& Msg_c) { double radius = 0; string id = iter; - Edge::subscribeContext(id, libsumo::CMD_GET_VEHICLE_VARIABLE, 100, VehDataSubscribeList, 0, tSimuEnd); + traci.edge.subscribeContext(id, libsumo::CMD_GET_VEHICLE_VARIABLE, 100, VehDataSubscribeList, 0, tSimuEnd); } edgeHasSubscribed = true; @@ -869,8 +925,8 @@ int TrafficHelper::recvFromSUMO(double* simTime, MsgHelper& Msg_c) { double width = 0; // width float width of rendered image in meters double height = 0; // height float height of rendered image in meters double angle = 0; // angle float angle of rendered image in degree - POI::add(poiName, x, y, color, type, layer, imgFile, width, height, angle); - POI::subscribeContext(poiName, libsumo::CMD_GET_VEHICLE_VARIABLE, r, VehDataSubscribeList, 0, tSimuEnd); + traci.poi.add(poiName, x, y, color, type, layer, imgFile, width, height, angle); + traci.poi.subscribeContext(poiName, libsumo::CMD_GET_VEHICLE_VARIABLE, r, VehDataSubscribeList, 0, tSimuEnd); //pointNamePoi_v.push_back(poiName); i++; @@ -886,8 +942,8 @@ int TrafficHelper::recvFromSUMO(double* simTime, MsgHelper& Msg_c) { // ------------------- // get list of all vehicles entered network - vector vehDepartedId_v = Simulation::getDepartedIDList(); - + /*vector vehDepartedId_v = Simulation::getDepartedIDList();*/ + vector vehDepartedId_v = traci.simulation.getDepartedIDList(); allVehicleHasSubscribed = true; // only able to get vehicle subscription for vehicles already in the network int i = 0; @@ -901,7 +957,7 @@ int TrafficHelper::recvFromSUMO(double* simTime, MsgHelper& Msg_c) { if (find(vehDepartedId_v.begin(), vehDepartedId_v.end(), id)!=vehDepartedId_v.end()) { double radius = iter.second; - Vehicle::subscribeContext(id, libsumo::CMD_GET_VEHICLE_VARIABLE, radius, VehDataSubscribeList, 0, tSimuEnd); + traci.vehicle.subscribeContext(id, libsumo::CMD_GET_VEHICLE_VARIABLE, radius, VehDataSubscribeList, 0, tSimuEnd); vehicleHasSubscribed_v[i] = true; } @@ -915,8 +971,8 @@ int TrafficHelper::recvFromSUMO(double* simTime, MsgHelper& Msg_c) { //while Simulation::getMinExpectedNumber() > 0: //for veh_id in Simulation::getDepartedIDList() : - // Vehicle::subscribe(veh_id, [traci.constants.VAR_POSITION]) - // positions = Vehicle::getAllSubscriptionResults() + // traci.vehicle.subscribe(veh_id, [traci.constants.VAR_POSITION]) + // positions = traci.vehicle.getAllSubscriptionResults() // traci.simulationStep() @@ -935,7 +991,7 @@ int TrafficHelper::recvFromSUMO(double* simTime, MsgHelper& Msg_c) { // GET SUBSCRIBED VEHICLE // =========================================================================== libsumo::ContextSubscriptionResults VehicleSubscribeRaw; - VehicleSubscribeRaw = Vehicle::getAllContextSubscriptionResults(); + VehicleSubscribeRaw = traci.vehicle.getAllContextSubscriptionResults(); //{ //int i = 0; @@ -989,7 +1045,7 @@ int TrafficHelper::recvFromSUMO(double* simTime, MsgHelper& Msg_c) { // GET SUBSCRIBED point // =========================================================================== libsumo::ContextSubscriptionResults PointSubscribeRaw; - PointSubscribeRaw = POI::getAllContextSubscriptionResults(); + PointSubscribeRaw = traci.poi.getAllContextSubscriptionResults(); for (auto& it : PointSubscribeRaw) { string poiName = it.first; @@ -1033,7 +1089,7 @@ int TrafficHelper::recvFromSUMO(double* simTime, MsgHelper& Msg_c) { // GET SUBSCRIBED EDGE // =========================================================================== libsumo::ContextSubscriptionResults EdgeSubscribeRaw; - EdgeSubscribeRaw = Edge::getAllContextSubscriptionResults(); + EdgeSubscribeRaw = traci.edge.getAllContextSubscriptionResults(); if (edgeHasSubscribed) { @@ -1106,7 +1162,7 @@ int TrafficHelper::recvFromSUMO(double* simTime, MsgHelper& Msg_c) { // !!!temporary fix // if doing vehicle simulator, e.g., CarMaker, only send limited number of vehicles if (ENABLE_VEH_SIMULATOR) { - libsumo::TraCIPosition posEgo = Vehicle::getPosition(Config_c->CarMakerSetup.EgoId); + libsumo::TraCIPosition posEgo = traci.vehicle.getPosition(Config_c->CarMakerSetup.EgoId); // sort distance, pair distance to ego, vehId vector > dist2ego_v; @@ -1143,7 +1199,8 @@ int TrafficHelper::recvFromSUMO(double* simTime, MsgHelper& Msg_c) { //================= // remove vehicle from list //================= - vector vehArrivedIdList = Simulation::getArrivedIDList(); + //vector vehArrivedIdList = Simulation::getArrivedIDList(); + vector vehArrivedIdList = traci.simulation.getArrivedIDList(); for (int i = 0; i < vehArrivedIdList.size(); i++) { VehicleId2EdgeList_um.erase(vehArrivedIdList[i]); } @@ -1158,8 +1215,8 @@ int TrafficHelper::recvFromSUMO(double* simTime, MsgHelper& Msg_c) { //=================================================== // Retreive DETECTOR configuration for the scenario BEFORE simulation starts //=================================================== - vector detAreaAllId_v = LaneArea::getIDList(); - vector detInductAllId_v = InductionLoop::getIDList(); + vector detAreaAllId_v = traci.lanearea.getIDList(); + vector detInductAllId_v = traci.inductionloop.getIDList(); // obtain detector ids of the selected intersection and subscribe to results @@ -1168,7 +1225,7 @@ int TrafficHelper::recvFromSUMO(double* simTime, MsgHelper& Msg_c) { for (auto it: Config_c->SubscriptionDetectorList.pattern_v){ for (int iD = 0; iD < detAreaAllId_v.size(); iD++) { if (detAreaAllId_v[iD].find(it) != std::string::npos) { - LaneArea::subscribe(detAreaAllId_v[iD], detSubscribeList, 0, tSimuEnd); + traci.lanearea.subscribe(detAreaAllId_v[iD], detSubscribeList, 0, tSimuEnd); } } } @@ -1178,7 +1235,7 @@ int TrafficHelper::recvFromSUMO(double* simTime, MsgHelper& Msg_c) { } libsumo::SubscriptionResults DetSubscribeRaw; - DetSubscribeRaw = LaneArea::getAllSubscriptionResults(); + DetSubscribeRaw = traci.lanearea.getAllSubscriptionResults(); vector tempDetData_v; @@ -1210,7 +1267,7 @@ int TrafficHelper::recvFromSUMO(double* simTime, MsgHelper& Msg_c) { //=================================================== // Retreive DETECTOR configuration for the scenario BEFORE simulation starts //=================================================== - vector sigAllId_v = TrafficLight::getIDList(); + vector sigAllId_v = traci.trafficlights.getIDList(); // obtain detector ids of the selected intersection and subscribe to results @@ -1218,12 +1275,12 @@ int TrafficHelper::recvFromSUMO(double* simTime, MsgHelper& Msg_c) { if (!Config_c->SubscriptionSignalList.subAllSignalFlag) { for (auto it : Config_c->SubscriptionSignalList.signalId_v) { - TrafficLight::subscribe(it.c_str(), sigSubscribeList, 0, tSimuEnd); + traci.trafficlights.subscribe(it.c_str(), sigSubscribeList, 0, tSimuEnd); } } else { for (auto it : sigAllId_v) { - TrafficLight::subscribe(it.c_str(), sigSubscribeList, 0, tSimuEnd); + traci.trafficlights.subscribe(it.c_str(), sigSubscribeList, 0, tSimuEnd); } } @@ -1233,7 +1290,7 @@ int TrafficHelper::recvFromSUMO(double* simTime, MsgHelper& Msg_c) { // if already subscribed, then get signal data out libsumo::SubscriptionResults SigSubscribeRaw; - SigSubscribeRaw = TrafficLight::getAllSubscriptionResults(); + SigSubscribeRaw = traci.trafficlights.getAllSubscriptionResults(); vector tempSigData_v; @@ -1302,9 +1359,9 @@ void TrafficHelper::parserSumoSubscription(libsumo::TraCIResults VehDataSubscrib // if does not have this vehicle yet if (VehicleId2EdgeList_um.find(vehId) == VehicleId2EdgeList_um.end()) { - vector edgeList = Vehicle::getRoute(vehId); + vector edgeList = traci.vehicle.getRoute(vehId); VehicleId2EdgeList_um[vehId] = edgeList; - //vector nextLinkList = Vehicle::getNextLinks(vehId); + //vector nextLinkList = traci.vehicle.getNextLinks(vehId); //int aa = 1; } @@ -1364,20 +1421,20 @@ void TrafficHelper::parserSumoSubscription(libsumo::TraCIResults VehDataSubscrib //================= // get preceding vehicle //================= - pair leaderIdNSpeed = Vehicle::getLeader(vehId, 1000); + pair leaderIdNSpeed = traci.vehicle.getLeader(vehId, 1000); CurVehData.precedingVehicleId = get<0>(leaderIdNSpeed); CurVehData.precedingVehicleDistance = get<1>(leaderIdNSpeed); CurVehData.hasPrecedingVehicle = 0; CurVehData.precedingVehicleSpeed = -1.0; if (CurVehData.precedingVehicleId.compare("") != 0) { CurVehData.hasPrecedingVehicle = 1; - CurVehData.precedingVehicleSpeed = Vehicle::getSpeed(CurVehData.precedingVehicleId); + CurVehData.precedingVehicleSpeed = traci.vehicle.getSpeed(CurVehData.precedingVehicleId); } //================= // get signal information //================= - vector nextTlsList = Vehicle::getNextTLS(vehId); + vector nextTlsList = traci.vehicle.getNextTLS(vehId); if (nextTlsList.size() > 0) { CurVehData.signalLightId = nextTlsList[0].id; @@ -1454,9 +1511,9 @@ void TrafficHelper::parserSumoSubscription(libsumo::TraCIResults VehDataSubscrib // for following information, need to get them one by one rather than through subscription CurVehData.speedLimitChangeDistance = -1; - if (AllEdgeList.find(CurVehData.linkIdNext) != AllEdgeList.end()) { - CurVehData.speedLimitChangeDistance = max(Vehicle::getDrivingDistance(vehId, CurVehData.linkIdNext, 0), -1.0); - } + //if (AllEdgeList.find(CurVehData.linkIdNext) != AllEdgeList.end()) { + // CurVehData.speedLimitChangeDistance = max(traci.vehicle.getDrivingDistance(vehId, CurVehData.linkIdNext, 0), -1.0); + //} //================= // grade @@ -1467,11 +1524,21 @@ void TrafficHelper::parserSumoSubscription(libsumo::TraCIResults VehDataSubscrib //================= // get lane change //================= - //vector bestLanesData = Vehicle::getBestLanes(vehId); + //vector bestLanesData = traci.vehicle.getBestLanes(vehId); CurVehData.activeLaneChange = 0; + //================= + // get length, width, height + //================= + tempDoublePtr = static_pointer_cast (VehDataSubscribeTraciResults[libsumo::VAR_LENGTH]); + CurVehData.length = tempDoublePtr->value; + + tempDoublePtr = static_pointer_cast (VehDataSubscribeTraciResults[libsumo::VAR_WIDTH]); + CurVehData.width = tempDoublePtr->value; + tempDoublePtr = static_pointer_cast (VehDataSubscribeTraciResults[libsumo::VAR_HEIGHT]); + CurVehData.height = tempDoublePtr->value; //================= // get vehicle indicators //================= diff --git a/CommonLib/TrafficHelper.h b/CommonLib/TrafficHelper.h index db44f357..a0746554 100644 --- a/CommonLib/TrafficHelper.h +++ b/CommonLib/TrafficHelper.h @@ -10,8 +10,8 @@ //#include "TraCIAPI.h" //#define NOMINMAX -#include - +//#include +#include "traci/TraCIAPI.h" #include "SocketHelper.h" @@ -23,7 +23,7 @@ class TrafficHelper TrafficHelper(); void connectionSetup(int nClient); - void connectionSetup(std::string trafficIp, int trafficPort, int nClientInput); + void connectionSetup(std::string trafficIp, int trafficPort, int nClientInput, int order); void enableVehSub(); void disableVehSub(); @@ -40,7 +40,7 @@ class TrafficHelper int recvFromTrafficSimulator(double* simTime, MsgHelper& Msg_c); int addEgoVehicle(double simTime); - + int addEgoVehicleFromXY(double simTime, std::string vehicleId, std::string vehicleType, double positionX, double positionY); int checkIfEgoExist(double* simTime); int getSimulationTime(double* simTime); @@ -71,13 +71,13 @@ class TrafficHelper //====================== // This is for SUMO //====================== - //class Client : public TraCIAPI { - //public: - // Client() {}; - // ~Client() {}; - //}; + class Client : public TraCIAPI { + public: + Client() {}; + ~Client() {}; + }; - //Client traci; + Client traci; /******************************************** @@ -139,6 +139,9 @@ class TrafficHelper bool ENABLE_VEH_SIMULATOR = false; + bool ENABLE_CARLA = false; + bool ENABLE_CARLA_EXTERNAL_CONTROL = false; + double tSimuEnd = 90000; diff --git a/CommonLib/VehDataMsgDefs.h b/CommonLib/VehDataMsgDefs.h index b5d9072f..38946a21 100644 --- a/CommonLib/VehDataMsgDefs.h +++ b/CommonLib/VehDataMsgDefs.h @@ -3,7 +3,10 @@ //#include #include #include - +#include +#ifdef RS_DSPACE +#include +#endif // MESSAGE IDENTIFIER 1 // Full vehicle data structure that will be shared between SUMO and other simulators // !!! This does not necessary mean all data directly communicated between simulators @@ -43,9 +46,11 @@ typedef struct { int8_t activeLaneChange; // 1 to the left, -1 to the right, 0 stay on the lane, - // variables not retrievable yet + // variables not retrievable from VISSIM yet float length; float width; + float height; + // variables not retrievable yet float weight; diff --git a/CommonLib/VehDataMsgDefs.py b/CommonLib/VehDataMsgDefs.py new file mode 100644 index 00000000..164484e3 --- /dev/null +++ b/CommonLib/VehDataMsgDefs.py @@ -0,0 +1,67 @@ +from dataclasses import dataclass, field +import ctypes +from typing import List + +@dataclass +class VehData: + # Fixed-size arrays for string-like fields (50 bytes each) + id: str = field(default_factory=lambda: ' ' * 50) # char[50] + type: str = field(default_factory=lambda: ' ' * 50) # char[50] + vehicleClass: str = field(default_factory=lambda: ' ' * 50) # char[50] + + # Floating point and integer fields + speed: float = 0.0 + acceleration: float = 0.0 + positionX: float = 0.0 + positionY: float = 0.0 + positionZ: float = 0.0 + heading: float = 0.0 + color: int = 0 # uint32_t + + linkId: str = field(default_factory=lambda: ' ' * 50) # char[50] + laneId: int = 0 # uint32_t + distanceTravel: float = 0.0 + speedDesired: float = 0.0 + accelerationDesired: float = 0.0 + + hasPrecedingVehicle: int = 0 # Boolean-like integer (0 or 1) + precedingVehicleId: str = field(default_factory=lambda: ' ' * 50) # char[50] + precedingVehicleDistance: float = 0.0 + precedingVehicleSpeed: float = 0.0 + + signalLightId: str = field(default_factory=lambda: ' ' * 50) # char[50] + signalLightHeadId: int = 0 + signalLightDistance: float = 0.0 + signalLightColor: int = 0 # int8_t + + speedLimit: float = 0.0 + speedLimitNext: float = 0.0 + speedLimitChangeDistance: float = 0.0 + + linkIdNext: str = field(default_factory=lambda: ' ' * 50) # char[50] + grade: float = 0.0 + + length: float = 0.0 + width: float = 0.0 + height: float = 0.0 + + activeLaneChange: int = 0 # Boolean-like integer (-1, 0, or 1) + + def get(self, field_name, default=None): + return getattr(self, field_name, default) + + +@dataclass +class TrafficLightData: + id: int # uint16_t + name: str + state: str + + def get(self, field_name, default=None): + return getattr(self, field_name, default) + +@dataclass +class DetectorData: + id: int # uint8_t + name: str + state: int # uint8_t \ No newline at end of file diff --git a/CommonLib/VirEnvHelper.cpp b/CommonLib/VirEnvHelper.cpp index 3d6b05e7..93ca9852 100644 --- a/CommonLib/VirEnvHelper.cpp +++ b/CommonLib/VirEnvHelper.cpp @@ -35,7 +35,7 @@ void VirEnvHelper::shutdown() { catch (...) { Log("Warning: RealSim shutdown failed\n"); } -#else +//#else //try { // Sock_c.socketShutdown(); // Sock_c.socketReset(); @@ -52,13 +52,15 @@ void VirEnvHelper::shutdown() { // Log("Warning: RealSim shutdown failed\n"); //} #endif - veryFirstStep = 1; - TrafficSimulatorId2CarMakerId.clear(); - CmAvailableCarId_queue = queue(); - CmAvailableTruckId_queue = queue(); - CmAvailableBusId_queue = queue(); - TrafficSimulatorId2Remove.clear(); + veryFirstStep = 1; + + TrafficSimulatorId2CarMakerId.clear(); + CmAvailableCarId_queue = queue(); + CmAvailableTruckId_queue = queue(); + CmAvailableBusId_queue = queue(); + TrafficSimulatorId2Remove.clear(); + } @@ -461,6 +463,9 @@ int VirEnvHelper::runStep(double simTime, const char** errorMsg) { } else { + // ELSE means idTs exists in previous time step and continues to exist in this time step + // TrafficSimulatorId2Remove contains all mapped ids from previous synchronization step + // since this idTs is still in the simulation, we need to remove it from TrafficSimulatorId2Remove set if (TrafficSimulatorId2Remove.find(idTs) != TrafficSimulatorId2Remove.end()) { TrafficSimulatorId2Remove.erase(TrafficSimulatorId2Remove.find(idTs)); } @@ -484,7 +489,7 @@ int VirEnvHelper::runStep(double simTime, const char** errorMsg) { // =========================================================================== try { // remove those ids that not exists since last time step - for (auto &it : TrafficSimulatorId2Remove) { + for (auto& it : TrafficSimulatorId2Remove) { string idTs = it; @@ -535,7 +540,7 @@ int VirEnvHelper::runStep(double simTime, const char** errorMsg) { // =========================================================================== try { // update state - TrafficSimulatorId2Remove.clear(); + TrafficSimulatorId2Remove.clear(); // we would need to update vehicle in current simulation step for (auto iter : TrafficSimulatorId2CarMakerId) { string idTs = iter.first; int idCm = iter.second; @@ -602,39 +607,37 @@ int VirEnvHelper::runStep(double simTime, const char** errorMsg) { return ERROR_STEP_UPDATE_STATE; } - } - - - // =========================================================================== - // sync traffic signal light - // =========================================================================== - if (SYNCHRONIZE_TRAFFIC_SIGNAL) { - try { - // loop over each signal light - for (auto it : Msg_c.TlsDataRecv_um) { - string tlsId = it.second.name; - string tlsState = it.second.state; - - // if can find the tlsId, then synchronize it - if (SignalController2HeadIdTrfLightIndex.find(tlsId) != SignalController2HeadIdTrfLightIndex.end()) { - // the unordered_map contains a list of head id and TrfLight index pair, so loop over each to synchronize - for (auto it : SignalController2HeadIdTrfLightIndex[tlsId]) { - TrfLight.Objs[get<1>(it)].State = tlsChar2CmState(tlsState.at(get<0>(it))); + // =========================================================================== + // sync traffic signal light + // =========================================================================== + if (SYNCHRONIZE_TRAFFIC_SIGNAL) { + try { + // loop over each signal light + for (auto it : Msg_c.TlsDataRecv_um) { + string tlsId = it.second.name; + string tlsState = it.second.state; + + // if can find the tlsId, then synchronize it + if (SignalController2HeadIdTrfLightIndex.find(tlsId) != SignalController2HeadIdTrfLightIndex.end()) { + // the unordered_map contains a list of head id and TrfLight index pair, so loop over each to synchronize + for (auto it : SignalController2HeadIdTrfLightIndex[tlsId]) { + TrfLight.Objs[get<1>(it)].State = tlsChar2CmState(tlsState.at(get<0>(it))); + } } - } + } + } + catch (const std::exception& e) { + std::cout << e.what(); + errorMsgStr = "RealSim: Sync traffic signal light failed"; + *errorMsg = errorMsgStr.c_str(); + return ERROR_STEP_SYNC_TRAFFIC_SIGNAL; + } + catch (...) { + errorMsgStr = "RealSim: Sync traffic signal light failed"; + *errorMsg = errorMsgStr.c_str(); + return ERROR_STEP_SYNC_TRAFFIC_SIGNAL; } - } - catch (const std::exception& e) { - std::cout << e.what(); - errorMsgStr = "RealSim: Sync traffic signal light failed"; - *errorMsg = errorMsgStr.c_str(); - return ERROR_STEP_SYNC_TRAFFIC_SIGNAL; - } - catch (...) { - errorMsgStr = "RealSim: Sync traffic signal light failed"; - *errorMsg = errorMsgStr.c_str(); - return ERROR_STEP_SYNC_TRAFFIC_SIGNAL; } } diff --git a/CommonLib/buildRS_2024a.bat b/CommonLib/buildRS_2024a.bat new file mode 100644 index 00000000..8195e05e --- /dev/null +++ b/CommonLib/buildRS_2024a.bat @@ -0,0 +1,2 @@ +start cmd /d /e:on /k ""C:\Program Files\dSPACE ConfigurationDesk 2024-A (24.1)\CFD_vars.bat " & ""dsmake -f DsBuildLibrary_2024a.mk output_filename=RealSimDsLib_2024a source_files="SocketHelper.cpp MsgHelper.cpp VirEnvHelper.cpp VirEnv_Wrapper.cpp" custom_cpp_options="-std=c++11 -IC:\IPG\carmaker\win64-13.1.2\include -DDSRTLX -DRS_DSPACE -DRS_CAVE -DRS_DEBUG" target=Dsx86_32"" " + diff --git a/CommonLib/libRealSimDsLib_2021b.a b/CommonLib/libRealSimDsLib_2021b.a index abf218b8..ad7f54df 100644 Binary files a/CommonLib/libRealSimDsLib_2021b.a and b/CommonLib/libRealSimDsLib_2021b.a differ diff --git a/CommonLib/libRealSimDsLib_2024a.a b/CommonLib/libRealSimDsLib_2024a.a new file mode 100644 index 00000000..963f19ad Binary files /dev/null and b/CommonLib/libRealSimDsLib_2024a.a differ diff --git a/CommonLib/libsumo/CMakeSettings.json b/CommonLib/libsumo/CMakeSettings.json new file mode 100644 index 00000000..9204f06e --- /dev/null +++ b/CommonLib/libsumo/CMakeSettings.json @@ -0,0 +1,15 @@ +{ + "configurations": [ + { + "name": "x64-Debug", + "generator": "Ninja", + "configurationType": "Debug", + "inheritEnvironments": [ "msvc_x64_x64" ], + "buildRoot": "${projectDir}\\out\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "", + "ctestCommandArgs": "" + } + ] +} \ No newline at end of file diff --git a/CommonLib/yaml-cpp/CMakeSettings.json b/CommonLib/yaml-cpp/CMakeSettings.json new file mode 100644 index 00000000..9204f06e --- /dev/null +++ b/CommonLib/yaml-cpp/CMakeSettings.json @@ -0,0 +1,15 @@ +{ + "configurations": [ + { + "name": "x64-Debug", + "generator": "Ninja", + "configurationType": "Debug", + "inheritEnvironments": [ "msvc_x64_x64" ], + "buildRoot": "${projectDir}\\out\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "", + "ctestCommandArgs": "" + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index 9465c2a5..ef0c4906 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ This README contains general information of the interface. For specific document * ### [VISSIM Interface](doc/VISSIMdoc.md) * ### [SUMO Interface](doc/SUMOdoc.md) * ### [CarMaker](doc/CarMakerDoc.md) +* ### [CARLA](doc/CARLAdoc.md) Current software versions used in FIXS development CM13.1.2, Carla 0.9.15, VISSIM 2022, SUMO 1.21, dSPACE/Matlab 2024a @@ -51,7 +52,8 @@ Before testing the Real-Sim interface, please - read through this README - check comments in config.yaml which specifies how to properly write/modify the configuration file - check annotations in the Simulink template which specifies how to properly use each block -- RealSimPack.m and RealSimDepack.m files are currently open-source. check the comments before modifying. +- RealSimPack.m and RealSimDepack.m files are currently open-source. check the comments before modifying. + ## General Setups The interface runs the connections to different software, simulators by itself to provide plug-in-and-play experience for the users. A config.yaml file is critical to setup the interface parameters and configure different scenarios. @@ -63,10 +65,10 @@ There are different mode of synchronization and opeartion of the Real-Sim interf - **binary 001, integer 1**: Application/Xil will connect with the Simulator and in wait mode until the ego vehicle enters the network then sync. This is suitable for simulation environment where users want to run the SUMO simulation at fast pace without running a Simulink model until the ego vehicle enters. - + --> - **binary 100, integer 4**: Application/Xil will connect with the Simulator and in wait mode until the specified initial simulation seconds (parameter **SimulationModeParamter**) then sync This is suitable for simulation environment where users want to run the SUMO simulation at fast pace without running a Simulink model until the a specific seconds later. @@ -83,31 +85,35 @@ SimulationModeParamter is a double variable that currently only used for mode bi This section is about how to build the source code, and then how to dispatch a released executable version. ### Prerequisite -Several libraries need to be compiled first. You can use the ```compileExternalLibraries.bat``` to do the following steps automatically or manually execute the following steps. -Build libevent -https://github.com/libevent/libevent/blob/master/Documentation/Building.md#building-on-windows +#### 1. Download and install Visual Studio 17 2022 community version. + +#### 2. Several libraries need to be compiled first. You can use the ```compileExternalLibraries.bat``` to do the following steps automatically or manually execute the following steps. Make sure you have CMake installed and added to PATH +* Build libevent (Currently this lib is not used, you can skip)\ +reference: https://github.com/libevent/libevent/blob/master/Documentation/Building.md#building-on-windows \ +Note: libevent source is already included in the CommonLib folder. Using the following command to build +or "start libevent.sln" and build with menu in Visual Studio. ``` cd .\CommonLib\libevent md build && cd build -cmake -G "Visual Studio 16 2019" -DEVENT__DISABLE_MBEDTLS=ON .. # Or use any generator you want to use. Run cmake --help for a list -cmake --build . --config Release # Or "start libevent.sln" and build with menu in Visual Studio. +cmake -G "Visual Studio 17 2022" -DEVENT__DISABLE_MBEDTLS=ON .. # Or use any generator you want to use. Run cmake --help for a list +cmake --build . --config Release ``` -Note: build in Release version if also compiling Release version of TrafficLayer.exe, CoordMerge.exe, etc. build in Debug if compiling Debug version of TrafficLayer.exe, CoordMerge.exe, etc. - -Build yaml-cpp -https://github.com/jbeder/yaml-cpp +* Build yaml-cpp\ + reference: https://github.com/jbeder/yaml-cpp \ + Note: Build in Release version if also compiling Release version of `VirtualEnvironment.lib` and CarMaker executable. Build in Debug if compiling Debug version of `VirtualEnvironment.lib` and CarMaker executable** ``` cd .\CommonLib\yaml-cpp md build && cd build -cmake -G "Visual Studio 16 2019" .. # Or use any generator you want to use. Run cmake --help for a list -cmake --build . --config Release # Or "start libevent.sln" and build with menu in Visual Studio. +cmake -G "Visual Studio 17 2022" .. # Or use any generator you want to use. Run cmake --help for a list +cmake --build . --config Release ``` + ### Dispatch a release -The source code uses ```msbuild``` as the default compiler, command ```msbuild``` must be known in the environmental variable before dispatching. The path to be added is ```%ProgramFiles(x86)%\Microsoft Visual Studio\2019\\MSBuild\Current\Bin```. +The source code uses ```msbuild``` as the default compiler, command ```msbuild``` must be known in the environmental variable before dispatching. The path to be added is ```%ProgramFiles(x86)%\Microsoft Visual Studio\2022\\MSBuild\Current\Bin```. Additionally, python >= 3.8 is required. It is recommended to create a dedicated conda environment and name it as ```realsimdev```. diff --git a/VirCarlaEnv/.gitignore b/VirCarlaEnv/.gitignore new file mode 100644 index 00000000..5f3b165c --- /dev/null +++ b/VirCarlaEnv/.gitignore @@ -0,0 +1,28 @@ +# Local build artifacts +/build/ +CMakeFiles/ +CMakeCache.txt +cmake_install.cmake +*.cmake +*.a +*.o +*.obj +*.pdb +*.ilk +*.sln +*.vcxproj* +*.ninja +.ninja* +Makefile +compile_commands.json + +# CARLA dependencies bundle +CommonLib/libcarla/ + +# CLion/VSCode etc +.idea/ +.vscode/ + +# Python cache +__pycache__/ +*.pyc diff --git a/VirCarlaEnv/VirCarlaEnv/BridgeHelper.cpp b/VirCarlaEnv/VirCarlaEnv/BridgeHelper.cpp new file mode 100644 index 00000000..aa2839bf --- /dev/null +++ b/VirCarlaEnv/VirCarlaEnv/BridgeHelper.cpp @@ -0,0 +1,298 @@ +#include "BridgeHelper.h" + + +// Default value for offset +// Town01 0.06,328.61 +// Town04 503.02,423.76 +//carla::geom::Location BridgeHelper::offset = carla::geom::Location(0.06f, 328.61f, 0.0f); +carla::geom::Location BridgeHelper::offset = carla::geom::Location(0.0f, 0.0f, 0.0f); +carla::geom::Transform BridgeHelper::map_transfrom_Sumo_to_Carla(const carla::geom::Transform& in_sumo_transform, + const carla::geom::Vector3D& extent) { + + carla::geom::Location in_location = in_sumo_transform.location; + carla::geom::Rotation in_rotation = in_sumo_transform.rotation; + + float yaw = -1.0f * in_rotation.yaw + 90.0f; + float pitch = in_rotation.pitch; + + float x = in_location.x - std::cos(yaw * M_PI / 180.0f) * extent.x; + float y = in_location.y - std::sin(yaw * M_PI / 180.0f) * extent.x; + float z = in_location.z - std::sin(pitch * M_PI / 180.0f) * extent.x; + + x -= offset.x; + y -= offset.y; + + carla::geom::Location out_location{ x, -y, z }; + carla::geom::Rotation out_rotation{ in_rotation.pitch, in_rotation.yaw - 90.0f, in_rotation.roll }; + + return carla::geom::Transform(out_location, out_rotation); +} + +carla::geom::Transform BridgeHelper::map_transfrom_Carla_to_Sumo(const carla::geom::Transform& in_carla_transform, + const carla::geom::Vector3D& extent) { + + carla::geom::Location in_location = in_carla_transform.location; + carla::geom::Rotation in_rotation = in_carla_transform.rotation; + + float yaw = -1.0f * in_rotation.yaw; + float pitch = in_rotation.pitch; + + float x = in_location.x + std::cos(yaw * M_PI / 180.0f) * extent.x; + float y = in_location.y - std::sin(yaw * M_PI / 180.0f) * extent.x; + float z = in_location.z - std::sin(pitch * M_PI / 180.0f) * extent.x; + + x += offset.x; + y -= offset.y; + + carla::geom::Location out_location{ x, -y, z }; + carla::geom::Rotation out_rotation{ in_rotation.pitch, in_rotation.yaw + 90.0f, in_rotation.roll }; + + return carla::geom::Transform(out_location, out_rotation); +} + +carla::geom::Location BridgeHelper::map_location_Carla_to_Sumo(const carla::geom::Location& in_carla_location) { + + carla::geom::Location in_location = in_carla_location; + + float x = in_location.x; + float y = in_location.y; + float z = in_location.z; + + x += offset.x; + y -= offset.y; + + carla::geom::Location out_location{ x, -y, z }; + + return out_location; +} + +std::string BridgeHelper::map_Sumo_vClass_to_Carla_blueprintId(const std::string& vClass) +{ + static const std::unordered_set carlaCarsBlueprints = { + "vehicle.audi.a2", + "vehicle.audi.etron", + "vehicle.audi.tt", + "vehicle.bmw.grandtourer", + "vehicle.chevrolet.impala", + "vehicle.citroen.c3", + "vehicle.dodge.charger_2020", + "vehicle.ford.mustang", + "vehicle.jeep.wrangler_rubicon", + "vehicle.lincoln.mkz_2017", + "vehicle.lincoln.mkz_2020", + "vehicle.mercedes.coupe", + "vehicle.mercedes.coupe_2020", + "vehicle.micro.microlino", + "vehicle.mini.cooper_s", + "vehicle.mini.cooper_s_2021", + "vehicle.nissan.micra", + "vehicle.nissan.patrol", + "vehicle.nissan.patrol_2021", + "vehicle.seat.leon", + "vehicle.tesla.model3", + "vehicle.toyota.prius", + "vehicle.ford.crown" // Also used for taxi + }; + static const std::unordered_set carlaTrucksBlueprints = { + "vehicle.carlamotors.carlacola", + "vehicle.carlamotors.european_hgv", + "vehicle.tesla.cybertruck", + }; + static const std::unordered_set carlaVansBlueprints = { + "vehicle.mercedes.sprinter", + "vehicle.volkswagen.t2", + "vehicle.volkswagen.t2_2021", + }; + static const std::unordered_set carlaBusesBlueprints = { + "vehicle.mitsubishi.fusorosa", + }; + static const std::unordered_set carlaMotorcyclesBlueprints = { + "vehicle.harley-davidson.low_rider", + "vehicle.kawasaki.ninja", + "vehicle.vespa.zx125", + "vehicle.yamaha.yzf", + }; + static const std::unordered_set carlaBicyclesBlueprints = { + "vehicle.bh.crossbike", + "vehicle.diamondback.century", + "vehicle.gazelle.omafiets", + }; + + static const std::unordered_set carlaPedestriansBlueprints = { + "walker.pedestrian.0001", + "walker.pedestrian.0002", + "walker.pedestrian.0003", + "walker.pedestrian.0004", + "walker.pedestrian.0005", + }; + //Note in the Carla Beprints, the emergency vehicles are not separated by type + static const std::unordered_set carlaEmergencyBlueprints = { + "vehicle.ford.ambulance", // Vans + "vehicle.carlamotors.firetruck", // Truck + "vehicle.dodge.charger_police", // Car + "vehicle.dodge.charger_police_2020", // Car + }; + + + std::string carlaBlueprintId = ""; + //std::string carlaBlueprintId = "vehicle.tesla.model3"; // Default to be passenger car + if (vClass == "passenger"){ + carlaBlueprintId = random_select_from_set(carlaCarsBlueprints); + } + else if (vClass == "truck") { + carlaBlueprintId = random_select_from_set(carlaTrucksBlueprints); + } + else if (vClass == "van") { + carlaBlueprintId = random_select_from_set(carlaVansBlueprints); + } + else if (vClass == "bus") { + carlaBlueprintId = random_select_from_set(carlaBusesBlueprints); + } + else if (vClass == "motorcycle") { + carlaBlueprintId = random_select_from_set(carlaMotorcyclesBlueprints); + } + else if (vClass == "bicycle") { + carlaBlueprintId = random_select_from_set(carlaBicyclesBlueprints); + } + else if (vClass == "pedestrian") { + carlaBlueprintId = random_select_from_set(carlaPedestriansBlueprints); + } + else if (vClass == "emergency") { + carlaBlueprintId = random_select_from_set(carlaEmergencyBlueprints); + } + else { + std::cerr << "Unknown vClass: " << vClass << std::endl + << "Currently supported vClasses are:" << std::endl + << "passenger, truck, van, bus, motorcycle, bicycle, pedestrian, emergency." << std::endl + << "Defaulting to vehicle.tesla.model3." << std::endl; + carlaBlueprintId = "vehicle.tesla.model3"; // Default to be passenger car + } + return carlaBlueprintId; +} + +SumoTrafficLightState BridgeHelper::map_Carla_traffic_light_state_to_Sumo(carla::rpc::TrafficLightState carlaTrafficLightState) { + + switch (carlaTrafficLightState) { + case carla::rpc::TrafficLightState::Red: + return SumoTrafficLightState::RED; + case carla::rpc::TrafficLightState::Yellow: + return SumoTrafficLightState::YELLOW; + case carla::rpc::TrafficLightState::Green: + return SumoTrafficLightState::GREEN; + case carla::rpc::TrafficLightState::Off: + return SumoTrafficLightState::OFF; + case carla::rpc::TrafficLightState::Unknown: + default: + return SumoTrafficLightState::OFF; + } +} + + +carla::rpc::TrafficLightState BridgeHelper::map_Sumo_traffic_light_state_to_Carla(SumoTrafficLightState& sumoTrafficLightState) { + // Map SumoTrafficLightState to carla::rpc::TrafficLightState + if (sumoTrafficLightState == SumoTrafficLightState::RED || + sumoTrafficLightState == SumoTrafficLightState::RED_YELLOW) { + return carla::rpc::TrafficLightState::Red; + } + else if (sumoTrafficLightState == SumoTrafficLightState::YELLOW) { + return carla::rpc::TrafficLightState::Yellow; + } + else if (sumoTrafficLightState == SumoTrafficLightState::GREEN || + sumoTrafficLightState == SumoTrafficLightState::GREEN_WITHOUT_PRIORITY) { + return carla::rpc::TrafficLightState::Green; + } + else if (sumoTrafficLightState == SumoTrafficLightState::OFF) { + return carla::rpc::TrafficLightState::Off; + } + else { // SumoTrafficLightState::GREEN_RIGHT_TURN and SumoTrafficLightState::OFF_BLINKING + return carla::rpc::TrafficLightState::Unknown; + } +} + + +SumoTrafficLightState BridgeHelper::get_Sumo_traffic_light_state_from_char(char c) { + switch (c) { + case 'r': return SumoTrafficLightState::RED; + case 'y': return SumoTrafficLightState::YELLOW; + case 'G': return SumoTrafficLightState::GREEN; + case 'g': return SumoTrafficLightState::GREEN_WITHOUT_PRIORITY; + case 's': return SumoTrafficLightState::GREEN_RIGHT_TURN; + case 'u': return SumoTrafficLightState::RED_YELLOW; + case 'o': return SumoTrafficLightState::OFF_BLINKING; + case 'O': return SumoTrafficLightState::OFF; + default: + throw std::invalid_argument("Unknown SumoTrafficLightState char: " + std::string(1, c)); + } +} + +char BridgeHelper::Sumo_traffic_light_state_to_char(SumoTrafficLightState state) { + return static_cast(state); +} + +std::unordered_map> BridgeHelper::readTrafficLightTable(const std::string& filename) { + std::unordered_map> trafficLightMap; + std::ifstream file(filename); + std::string line; + + if (!file.is_open()) { + std::cerr << "Failed to open file: " << filename << std::endl; + return trafficLightMap; + } + + // Skip header + std::getline(file, line); + + while (std::getline(file, line)) { + std::stringstream ss(line); + std::string token; + + std::string junctionId; + int linkId; + double x, y, z, heading; + + std::getline(ss, junctionId, ','); + std::getline(ss, token, ','); + linkId = std::stoi(token); + std::getline(ss, token, ','); + x = std::stod(token); + std::getline(ss, token, ','); + y = std::stod(token); + std::getline(ss, token, ','); + z = std::stod(token); + std::getline(ss, token, ','); + heading = std::stod(token); + + TrafficLight trafficLight(junctionId, linkId, x, y, z, heading); + trafficLightMap[junctionId][linkId] = trafficLight; + } + + return trafficLightMap; +} + + +std::pair BridgeHelper::find_closest_trafficLight_id( + std::unordered_map>& trafficLightMap, + double x, double y +) { + double min_dist = std::numeric_limits::max(); + std::pair closest_ids = { "", -1 }; + + for (const std::pair>& pair : trafficLightMap) { + const std::string& junctionId = pair.first; + const std::unordered_map& linkMap = pair.second; + for (const std::pair& pair : linkMap) { + const int& linkId = pair.first; + const TrafficLight& trafficLight = pair.second; + double dx = trafficLight.x - x; + double dy = trafficLight.y - y; + double dist_sq = dx * dx + dy * dy; + + if (dist_sq < min_dist) { + min_dist = dist_sq; + closest_ids = { junctionId, linkId }; + } + } + } + + return closest_ids; +} \ No newline at end of file diff --git a/VirCarlaEnv/VirCarlaEnv/BridgeHelper.h b/VirCarlaEnv/VirCarlaEnv/BridgeHelper.h new file mode 100644 index 00000000..d1889b28 --- /dev/null +++ b/VirCarlaEnv/VirCarlaEnv/BridgeHelper.h @@ -0,0 +1,114 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "RandomUtils.h" +#include +#include +#include +#include +#include +#include +#include "carla/rpc/TrafficLightState.h" +#define M_PI 3.14159265358979323846 +#define SET_CONTAINS_ID(set, value) ((set).find(value) != (set).end()) + +struct SumoActor { + std::string id; + std::string vType; + std::string vClass; + carla::geom::Transform sumoTransform; + carla::geom::Transform carlaTransform; + // The extent of the actor is the size of the bounding box in Carla + carla::geom::Vector3D extent; + // Additional properties can be added as needed, e.g.: + // Indicates if the actor is spawned in Carla + bool spawnedInCarla = false; + carla::SharedPtr carlaVehicleActorPtr = nullptr; + //std::vector signals; + //carla::rpc::Color color; + + SumoActor() = default; + // Initialize an Actor with all required fields + // (vehicle id, vehicle type, vehicle class, transform, extent) + SumoActor(const std::string& _id, + const std::string& _vType, + const std::string& _vClass, + const carla::geom::Transform& _sumoTransform, + const carla::geom::Vector3D& _extent) + : id(_id), + vType(_vType), + vClass(_vClass), + sumoTransform(_sumoTransform), + extent(_extent) { + } +}; + +// Sumo signal state +enum class SumoTrafficLightState : char { + RED = 'r', + YELLOW = 'y', + GREEN = 'G', + GREEN_WITHOUT_PRIORITY = 'g', + GREEN_RIGHT_TURN = 's', + RED_YELLOW = 'u', + OFF_BLINKING = 'o', + OFF = 'O' +}; + +struct TrafficLight { + std::string junctionId; + int linkId; + double x, y, z; + double heading; + SumoTrafficLightState state = SumoTrafficLightState::OFF; + std::string carlaTrafficLightActorId = ""; + carla::SharedPtr carlaTrafficLightActorPtr = nullptr; + // Current state of the traffic light + + TrafficLight() = default; + TrafficLight(std::string _junctionId, int _linkId, double _x, double _y, double _z, double _heading) + : junctionId(_junctionId), linkId(_linkId), x(_x), y(_y), z(_z), heading(_heading) {} +}; + + +class BridgeHelper { +public: + // Global offset between SUMO and Carla coordinate systems + static carla::geom::Location offset; + + // Convert SUMO → Carla + static carla::geom::Transform map_transfrom_Sumo_to_Carla(const carla::geom::Transform& in_sumo_transform, + const carla::geom::Vector3D& extent); + + // Convert Carla → SUMO + static carla::geom::Transform map_transfrom_Carla_to_Sumo(const carla::geom::Transform& in_carla_transform, + const carla::geom::Vector3D& extent); + + static carla::geom::Location map_location_Carla_to_Sumo(const carla::geom::Location& in_carla_location); + + static std::string map_Sumo_vClass_to_Carla_blueprintId(const std::string& vclass); + + static std::unordered_map> readTrafficLightTable(const std::string& filename); + + static char Sumo_traffic_light_state_to_char(SumoTrafficLightState state); + + static SumoTrafficLightState get_Sumo_traffic_light_state_from_char(char c); + + static SumoTrafficLightState map_Carla_traffic_light_state_to_Sumo(carla::rpc::TrafficLightState carlaTrafficLightState); + + static carla::rpc::TrafficLightState map_Sumo_traffic_light_state_to_Carla(SumoTrafficLightState& sumoTrafficLightState); + + static std::pair find_closest_trafficLight_id( + std::unordered_map>& trafficLightMap, + double x, double y); + +}; + + diff --git a/VirCarlaEnv/VirCarlaEnv/CMakeLists.txt b/VirCarlaEnv/VirCarlaEnv/CMakeLists.txt new file mode 100644 index 00000000..fa68414a --- /dev/null +++ b/VirCarlaEnv/VirCarlaEnv/CMakeLists.txt @@ -0,0 +1,166 @@ +cmake_minimum_required(VERSION 3.16) +project(VirCarlaEnv LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +# ------------------------------------------------------------------ +# Paths to external dependencies (CARLA) +# Looks first for CommonLib/libcarla (populated by copyCarlaDependencies), +# otherwise uses CARLA_ROOT (env or cache) and searches standard layouts. +# ------------------------------------------------------------------ +set(FIXS_ROOT "${CMAKE_SOURCE_DIR}/../.." CACHE PATH "FIXS repository root") +set(DEFAULT_CARLA_ROOT "${FIXS_ROOT}/CommonLib/libcarla") + +if(NOT DEFINED CARLA_ROOT OR CARLA_ROOT STREQUAL "") + if(DEFINED ENV{CARLA_ROOT} AND NOT "$ENV{CARLA_ROOT}" STREQUAL "") + set(CARLA_ROOT "$ENV{CARLA_ROOT}") + elseif(EXISTS "${DEFAULT_CARLA_ROOT}") + set(CARLA_ROOT "${DEFAULT_CARLA_ROOT}") + else() + set(CARLA_ROOT "" ) + endif() +endif() +set(CARLA_ROOT "${CARLA_ROOT}" CACHE PATH "Path to CARLA checkout/install or extracted dependencies (include/ and lib/)") +if(CARLA_ROOT STREQUAL "") + message(FATAL_ERROR "Set CARLA_ROOT (or copy dependencies to CommonLib/libcarla).") +endif() + +# Pick an include dir by probing common layouts +set(CARLA_INCLUDE_CANDIDATES + "${CARLA_ROOT}/include" + "${CARLA_ROOT}/include/carla" + "${CARLA_ROOT}/LibCarla/source" +) +foreach(dir IN LISTS CARLA_INCLUDE_CANDIDATES) + if(EXISTS "${dir}/carla/client/Client.h") + set(CARLA_INCLUDE_DIR "${dir}") + break() + endif() +endforeach() +set(CARLA_INCLUDE_DIR "${CARLA_INCLUDE_DIR}" CACHE PATH "Directory containing CARLA headers (expects carla/client/Client.h)") +if(NOT CARLA_INCLUDE_DIR OR NOT EXISTS "${CARLA_INCLUDE_DIR}/carla/client/Client.h") + message(FATAL_ERROR "Could not find carla/client/Client.h under ${CARLA_ROOT}; set CARLA_INCLUDE_DIR explicitly.") +endif() + +# Locate the client library in common lib locations +set(CARLA_LIB_CANDIDATES + "${CARLA_ROOT}/lib" + "${CARLA_ROOT}/lib64" + "${CARLA_ROOT}/Build/libcarla-client-release" +) +find_library(CARLA_CLIENT_LIBRARY NAMES carla_client carla_client.lib PATHS ${CARLA_LIB_CANDIDATES} NO_DEFAULT_PATH) +if(NOT CARLA_CLIENT_LIBRARY) + find_library(CARLA_CLIENT_LIBRARY NAMES carla_client carla_client.lib) +endif() +set(CARLA_CLIENT_LIBRARY "${CARLA_CLIENT_LIBRARY}" CACHE FILEPATH "Path to CARLA client library") +if(NOT CARLA_CLIENT_LIBRARY) + message(FATAL_ERROR "Could not find carla_client library. Set CARLA_ROOT or CARLA_CLIENT_LIBRARY.") +endif() +get_filename_component(CARLA_LIB_DIR "${CARLA_CLIENT_LIBRARY}" DIRECTORY) + +find_library(BOOST_FILESYSTEM_LIB NAMES boost_filesystem PATHS "${CARLA_LIB_DIR}" "${CARLA_LIB_DIR}/.." NO_DEFAULT_PATH) +find_library(BOOST_SYSTEM_LIB NAMES boost_system PATHS "${CARLA_LIB_DIR}" "${CARLA_LIB_DIR}/.." NO_DEFAULT_PATH) +find_library(RPC_LIB NAMES rpc PATHS "${CARLA_LIB_DIR}" "${CARLA_LIB_DIR}/.." NO_DEFAULT_PATH) +find_library(DETOUR_LIB NAMES Detour PATHS "${CARLA_LIB_DIR}" "${CARLA_LIB_DIR}/.." NO_DEFAULT_PATH) +find_library(DETOUR_CROWD_LIB NAMES DetourCrowd PATHS "${CARLA_LIB_DIR}" "${CARLA_LIB_DIR}/.." NO_DEFAULT_PATH) +find_library(DETOUR_TILE_LIB NAMES DetourTileCache PATHS "${CARLA_LIB_DIR}" "${CARLA_LIB_DIR}/.." NO_DEFAULT_PATH) +find_library(RECAST_LIB NAMES Recast PATHS "${CARLA_LIB_DIR}" "${CARLA_LIB_DIR}/.." NO_DEFAULT_PATH) +if(NOT RPC_LIB) + message(WARNING "rpclib (librpc) not found alongside CARLA libs; set RPC_LIB or adjust CARLA_LIB_DIR.") +endif() + +# ------------------------------------------------------------------ +# Third-party: yaml-cpp (use system if present, otherwise the vendored copy) +# ------------------------------------------------------------------ +find_package(yaml-cpp QUIET) +if(NOT yaml-cpp_FOUND) + set(YAML_BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) + set(YAML_CPP_BUILD_TESTS OFF CACHE BOOL "" FORCE) + set(YAML_CPP_BUILD_TOOLS OFF CACHE BOOL "" FORCE) + add_subdirectory(${FIXS_ROOT}/CommonLib/yaml-cpp ${CMAKE_BINARY_DIR}/yaml-cpp EXCLUDE_FROM_ALL) +endif() + +find_package(Threads REQUIRED) + +# ------------------------------------------------------------------ +# Common library (shared helpers used by the Carla bridge) +# ------------------------------------------------------------------ +set(COMMONLIB_DIR "${FIXS_ROOT}/CommonLib" CACHE PATH "FIXS CommonLib directory") +set(COMMONLIB_SOURCES + ${COMMONLIB_DIR}/ConfigHelper.cpp + ${COMMONLIB_DIR}/MsgHelper.cpp + ${COMMONLIB_DIR}/SocketHelper.cpp +) + +add_library(commonlib STATIC ${COMMONLIB_SOURCES}) +target_include_directories(commonlib PUBLIC ${COMMONLIB_DIR}) +target_link_libraries(commonlib PUBLIC yaml-cpp) +target_compile_definitions(commonlib PUBLIC RS_SKIP_CARMAKER) + +# ------------------------------------------------------------------ +# VirCarlaEnv executable +# ------------------------------------------------------------------ +set(VIRCARLAENV_DIR "${CMAKE_SOURCE_DIR}") +set(VIRCARLAENV_SOURCES + ${VIRCARLAENV_DIR}/mainVirCarla.cpp + ${VIRCARLAENV_DIR}/BridgeHelper.cpp + ${VIRCARLAENV_DIR}/DebugHelper.cpp +) + +add_executable(VirCarlaEnv ${VIRCARLAENV_SOURCES}) +target_include_directories(VirCarlaEnv PRIVATE + ${VIRCARLAENV_DIR} + ${COMMONLIB_DIR} + ${CARLA_INCLUDE_DIR} + ${CARLA_INCLUDE_DIR}/system +) + +target_link_directories(VirCarlaEnv PRIVATE ${CARLA_LIB_DIR}) +target_link_libraries(VirCarlaEnv PRIVATE + commonlib + ${CARLA_CLIENT_LIBRARY} + ${RPC_LIB} + ${DETOUR_LIB} + ${DETOUR_CROWD_LIB} + ${DETOUR_TILE_LIB} + ${RECAST_LIB} + Threads::Threads +) + +if(BOOST_FILESYSTEM_LIB) + target_link_libraries(VirCarlaEnv PRIVATE ${BOOST_FILESYSTEM_LIB}) +else() + message(WARNING "boost_filesystem not found alongside CARLA libs; add it to CARLA_LIB_DIR or system paths.") +endif() + +if(BOOST_SYSTEM_LIB) + target_link_libraries(VirCarlaEnv PRIVATE ${BOOST_SYSTEM_LIB}) +else() + message(WARNING "boost_system not found alongside CARLA libs; add it to CARLA_LIB_DIR or system paths.") +endif() + +if(UNIX AND NOT APPLE) + target_link_libraries(VirCarlaEnv PRIVATE dl) +endif() + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + +# Ensure the executable can find CARLA shared libs at runtime +# - prefer a relative rpath so it works from the build tree without env tweaks +set(CMAKE_SKIP_BUILD_RPATH FALSE) +set(CMAKE_BUILD_RPATH_USE_ORIGIN TRUE) +set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) +file(RELATIVE_PATH CARLA_LIB_RPATH_FROM_BIN + "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}" + "${CARLA_LIB_DIR}" +) +set(CARLA_RPATH_ENTRIES + "${CARLA_LIB_DIR}" + "\$ORIGIN/${CARLA_LIB_RPATH_FROM_BIN}" +) +set_target_properties(VirCarlaEnv PROPERTIES + BUILD_RPATH "${CARLA_RPATH_ENTRIES}" + INSTALL_RPATH "${CARLA_RPATH_ENTRIES}" +) diff --git a/VirCarlaEnv/VirCarlaEnv/DebugHelper.cpp b/VirCarlaEnv/VirCarlaEnv/DebugHelper.cpp new file mode 100644 index 00000000..ae7291be --- /dev/null +++ b/VirCarlaEnv/VirCarlaEnv/DebugHelper.cpp @@ -0,0 +1,44 @@ +#include "DebugHelper.h" + + +void drawCircle(carla::client::DebugHelper debug, + const carla::geom::Location& center, + float radius, + int segments, + float z_offset, + float thickness, + float life_time, // 0 = forever + bool persistent, + bool fill) { + + using carla::geom::Location; + using carla::rpc::Color; + carla::geom::Location adjusted_center(center.x, center.y, 0.0f); + adjusted_center = adjusted_center + carla::geom::Location{ 0.0f, 0.0f, z_offset }; + + // Adjust center Z level + + for (int i = 0; i < segments; ++i) { + float angle1 = (2 * M_PI * i) / segments; + float angle2 = (2 * M_PI * (i + 1)) / segments; + + Location p1 = adjusted_center + Location{ + radius * std::cos(angle1), + radius * std::sin(angle1), + 0.0f + }; + Location p2 = adjusted_center + Location{ + radius * std::cos(angle2), + radius * std::sin(angle2), + 0.0f + }; + + // Perimeter circle + debug.DrawLine(p1, p2, thickness, carla::client::DebugHelper::Color(255, 0, 0), life_time, persistent); + + if (fill) { + // Fill with "triangle fans" (radial lines) + debug.DrawLine(adjusted_center, p1, thickness * 0.5f, carla::client::DebugHelper::Color(255, 100, 100), life_time, persistent); + } + } +} \ No newline at end of file diff --git a/VirCarlaEnv/VirCarlaEnv/DebugHelper.h b/VirCarlaEnv/VirCarlaEnv/DebugHelper.h new file mode 100644 index 00000000..f4d35b7a --- /dev/null +++ b/VirCarlaEnv/VirCarlaEnv/DebugHelper.h @@ -0,0 +1,14 @@ +#pragma once +#include +#include + +#define M_PI 3.14159265358979323846 +void drawCircle(carla::client::DebugHelper debug, + const carla::geom::Location& center, + float radius = 50.0f, + int segments = 64, + float z_offset = 0.5f, + float thickness = 0.1f, + float life_time = 0.1f, // 0 = forever + bool persistent = false, + bool fill = false); \ No newline at end of file diff --git a/VirCarlaEnv/VirCarlaEnv/RandomUtils.h b/VirCarlaEnv/VirCarlaEnv/RandomUtils.h new file mode 100644 index 00000000..7ee2455f --- /dev/null +++ b/VirCarlaEnv/VirCarlaEnv/RandomUtils.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +template +T random_select_from_set(const std::unordered_set& set) { + if (set.empty()) throw std::runtime_error("Set is empty!"); + + // Random number generation + static std::random_device rd; + static std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(0, set.size() - 1); + + int index = dis(gen); + auto it = set.begin(); + std::advance(it, index); // move iterator forward by index + return *it; +} \ No newline at end of file diff --git a/VirCarlaEnv/VirCarlaEnv/defaultConfig.yaml b/VirCarlaEnv/VirCarlaEnv/defaultConfig.yaml new file mode 100644 index 00000000..3edaa2ba --- /dev/null +++ b/VirCarlaEnv/VirCarlaEnv/defaultConfig.yaml @@ -0,0 +1,81 @@ +#pragma once +# Global Simulation setup +SimulationSetup: + # Master Switch to turn on/off RealSim interface + # if turned off, VISSIM will just run without RealSim + # SUMO needs to run without traci + EnableRealSim: true + + # Whether or not to save verbose log during the simulation. skip log can potentially speed up + EnableVerboseLog: false + + # Simulation end time + # if NOT specificed, SimulationEndTime will be set to a large value (90000 seconds) + #-------------------------------------------------- + SimulationEndTime: 250 + + # specify which traffic simulator + #-------------------------------------------------- + SelectedTrafficSimulator: "SUMO" + + # default will send all + VehicleMessageField: [id, type, vehicleClass, speed, acceleration, positionX, positionY, positionZ, heading, + color, linkId, laneId, distanceTravel, speedDesired, grade, length, width, height] + + # by default it is false + EnableExternalDynamics: true # use this so that simulink will control the response + + TrafficSimulatorIP: "127.0.0.1" + TrafficSimulatorPort: 1337 +SumoSetup: + # set the speed mode, in integer. default value is 0 + # check Sumo documentation https://sumo.dlr.de/docs/TraCI/Change_Vehicle_State.html#speed_mode_0xb3 + + SpeedMode: 32 # 31: default mode. 32: all checks off + +# ip and port are server ip and port + +# setup Application Layer +ApplicationSetup: + # turn on/off application layer + EnableApplicationLayer: true + + #-------------------------------------------------- + VehicleSubscription: + # Application from where to accept the vehicle data + # 1. Receive data from FIXS LAYER + # 2. Send data to XIL LAYER / Back to FIXS LAYER + - type: "ego" + attribute: {id: ["ego"], radius: [100]} + ip: ["127.0.0.1"] + port: + - 430 +XilSetup: + # enable/disable XIL + EnableXil: false + +CarlaSetup: + EnableVerboseLog: false + # whether or not enable Carla (default: false) + EnableCosimulation: true + + # if true, ego state is sent from Simulink. + # if false, ego state is sent from User.cpp + EnableEgoSimulink: false + + # Carla Server Ip and Port settings + CarlaServerIP: 127.0.0.1 + + CarlaServerPort: 422 + # Carla Client Ip and Port settings + CarlaClientIP: 127.0.0.1 + + CarlaClientPort: 430 + + CarlaMapName: "Town01" + + # default Traffic Objects updates every 0.1 seconds + TrafficRefreshRate: 0.1 + + # ego ids should be a subset of the vehicleSubscription ids + InterestedIds: ["ego"] \ No newline at end of file diff --git a/VirCarlaEnv/VirCarlaEnv/mainVirCarla.cpp b/VirCarlaEnv/VirCarlaEnv/mainVirCarla.cpp new file mode 100644 index 00000000..7a32aeb7 --- /dev/null +++ b/VirCarlaEnv/VirCarlaEnv/mainVirCarla.cpp @@ -0,0 +1,709 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "BridgeHelper.h" +#include "MsgHelper.h" +#include "SocketHelper.h" +#include "ConfigHelper.h" +#include "DebugHelper.h" + +//preset delay durations +carla::time_duration timeout_10s = std::chrono::seconds(10); +carla::time_duration timeout_1s = std::chrono::seconds(1); +carla::time_duration timeout_50ms = std::chrono::milliseconds(50); +carla::time_duration timeout_100ms = std::chrono::duration(0.1); // 0.1 second + +using namespace std::chrono_literals; +using namespace std::string_literals; + +#define EXPECT_TRUE(pred) if (!(pred)) { throw std::runtime_error(#pred); } +#define SET_CONTAINS_ID(set, value) ((set).find(value) != (set).end()) +#define MAP_CONTAINS_KEY(map, key) ((map).find(key) != (map).end()) +#define SPAWN_OFFSET_Z 0.1f // Offset for spawning actors above the ground + +static void show_usage(std::string name) +{ + std::cerr << "Usage: " << name << std::endl + << "Options:\n" + << "\t-h,--help\t\tShow this help message\n" + << "\t-f,--file PATH\\FILENAME\tSpecify the path and filename of configuration yaml file" + << std::endl; +} + +double periodicCosineSpeed(double t, double T_period, double v_max) { + if (T_period <= 0.0) return 0.0; + + // Time within the current period + double t_in_period = std::fmod(t, T_period); + + // Compute speed using cosine profile within the period + return 0.5 * v_max * (1 - std::cos(2 * M_PI * t_in_period / T_period)); +} + +float lowPassFilter(float currentValue, float lastValue, float alpha) { + return alpha * currentValue + (1 - alpha) * lastValue; +} + +int main(int argc, const char* argv[]) { + std::string CarlaClientLogFile = "CarlaClient.log"; + + time_t simStartTimestamp = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); + char simStartTimestampChar[100]; +#if defined(_WIN32) + ctime_s(simStartTimestampChar, sizeof simStartTimestampChar, &simStartTimestamp); +#else + std::tm tm_buf{}; + localtime_r(&simStartTimestamp, &tm_buf); + std::strftime(simStartTimestampChar, sizeof simStartTimestampChar, "%c\n", &tm_buf); +#endif + std::fstream f(CarlaClientLogFile, std::fstream::in | std::fstream::out | std::fstream::app); + f << std::endl << "=============================================" << std::endl; + f << "Carla Client Starts at " << simStartTimestampChar << std::endl; + f.close(); + MsgHelper msgHelper; + ConfigHelper configHelper; + SocketHelper socketHelper; + std::string configPath = ""; + std::string trafficLightMapPath = ""; + // =========================================================================== + // Parse Arguments + // =========================================================================== + for (int i = 1; i < argc; i++) { + std::string arg = argv[i]; + if (arg == "-h" || arg == "--help") { + show_usage(argv[0]); + return 0; + } + else if (arg == "-f" || arg == "--file") { + if (i + 1 < argc) { + configPath = argv[++i]; + } + else { + std::cerr << "--path option requires one argument." << std::endl; + return -1; + } + } + else if (arg == "-t" || arg == "--tls") { + if (i + 1 < argc) { + trafficLightMapPath = argv[++i]; + } + else { + std::cerr << "--path option requires one argument." << std::endl; + return -1; + } + } + else { + printf("Check options\n"); + show_usage(argv[0]); + return 0; + } + } + // =========================================================================== + // READ Config File + // =========================================================================== + printf("Reading Configuration file %s\n", configPath.c_str()); + if (configHelper.getConfig(configPath) < 0) { + printf("Please check path and filename of the configuration yaml\n"); + show_usage(argv[0]); + exit(-1); + } + else { + printf("Read configuration file success\n"); + } + // Set the simulation end time + uint32_t simEndTime = configHelper.SimulationSetup.SimulationEndTime; + bool enableVerboseLog = configHelper.CarlaSetup.EnableVerboseLog; + CarlaSetup_t carlaSetup = configHelper.CarlaSetup; + + std::string carlaServerIp = carlaSetup.CarlaServerIP; + int carlaServerPort = carlaSetup.CarlaServerPort; + std::string carlaClientIp = carlaSetup.CarlaClientIP; + int carlaClientPort = carlaSetup.CarlaClientPort; + //std::string carlaMapName = carlaSetup.CarlaMapName; + double trafficRefreshRate = carlaSetup.TrafficRefreshRate; + const std::chrono::milliseconds step_duration_ms(static_cast(trafficRefreshRate * 1000)); + // If the vehicle type is used as the blueprint ID, set this to true + bool useVhicleTypeAsBlueprint = carlaSetup.UseVehicleTypeAsBlueprint; + // Enable external control for interested vehicles, if turned off, the interested vehicles will be controlled by the Sumo + bool enableExternalControl = carlaSetup.EnableExternalControl; + // centeredViewId is the id of the camera that is used to render the view in Carla + std::string centeredViewId = configHelper.CarlaSetup.CenteredViewId; + // Enable traffic light synchronization + /*bool enableTlsSync = configHelper.CarlaSetup.EnableTrafficLightSync;*/ + bool enableTlsSync = true; + std::unordered_set setInterestedIds; + for (const std::string& id : carlaSetup.InterestedIds) { + setInterestedIds.insert(id); + } + // This is important for the reveived data parser to work correctly + msgHelper.getConfig(configHelper); + // Setup Connection to the Traffic Server + //void SocketHelper::socketSetup(vector SERVERADDR_UserInput, vector SERVERPORT_UserInput) { + std::vector SERVERADDR_UserInput = { carlaClientIp }; + std::vector SERVERPORT_UserInput = { carlaClientPort }; + socketHelper.disableWaitClientTrigger(); + socketHelper.disableServerTrigger(); + socketHelper.socketSetup(SERVERADDR_UserInput, SERVERPORT_UserInput); + // =========================================================================== + // READ Traffic Light Table + // =========================================================================== + std::unordered_map> trafficLightMap = BridgeHelper::readTrafficLightTable(trafficLightMapPath); + + if (trafficLightMap.empty()) { + std::cerr << "No traffic light data found in the file: " << trafficLightMapPath << std::endl; + return -1; + } + + if (socketHelper.initConnection(CarlaClientLogFile) < 0) { + printf("Connect to Traffic Layer failed!\n"); + exit(-1); + } + // there is only one traffic layer server + int sockId = 0; + try { + carla::client::Client carlaClient = carla::client::Client(carlaServerIp, carlaServerPort); + // initialize carla client and world + std::cout << "Client API version : " << carlaClient.GetClientVersion() << '\n'; + std::cout << "Server API version : " << carlaClient.GetServerVersion() << '\n'; + carlaClient.SetTimeout(timeout_10s); + carla::client::World carlaWorld = carlaClient.GetWorld(); + carla::SharedPtr carlaSpectator = carlaWorld.GetSpectator(); + + // Enable synchronous mode + carla::rpc::EpisodeSettings carlaWorldSettings = carlaWorld.GetSettings(); + if (!carlaWorldSettings.synchronous_mode) { + carlaWorldSettings.synchronous_mode = true; // Turn on synchronous mode + carlaWorldSettings.fixed_delta_seconds = trafficRefreshRate; // time step of 0.1 seconds + //the time_out is of carla::time_duration + carlaWorld.ApplySettings(carlaWorldSettings, timeout_1s); + if (enableVerboseLog) std::cout << "Synchronous mode enabled.\n"; + } + carla::SharedPtr blueprint_library = carlaWorld.GetBlueprintLibrary(); + + // Map the Sumo Ids to Carla Ids + std::unordered_map mapSumoToCarla; + // Map the Carla Ids to Sumo Ids + std::unordered_map mapCarlaToSumo; + + float simTime = 0; // Initialize the current simulation time + std::unordered_map mapSumoActor; + // command batch to set the transform of the actors (non-interested actors) + std::vector transformCommandBatch; + + if (enableTlsSync) { + carla::SharedPtr carlaTrafficLightActors = carlaWorld.GetActors()->Filter("traffic.traffic_light"); + + for (const carla::SharedPtr& carlaActor : *carlaTrafficLightActors) { + carla::SharedPtr carlaTrafficLightActorPtr = boost::static_pointer_cast(carlaActor); + carlaTrafficLightActorPtr->Freeze(true); + std::string carlaTrafficLightActorId = std::to_string(carlaActor->GetId()); + // Convert Carla location to SUMO coordinate + carla::geom::Location carlaLocation = carlaActor->GetLocation(); + carla::geom::Location sumoLocation = BridgeHelper::map_location_Carla_to_Sumo(carlaLocation); + + // Find closest TLS from map + std::pair trafficLightId = BridgeHelper::find_closest_trafficLight_id( + trafficLightMap, + sumoLocation.x, + sumoLocation.y + ); + std::string junctionId = trafficLightId.first; + int linkId = trafficLightId.second; + + if (junctionId == "") { + std::cerr << "No matching traffic light found for actor " << carlaTrafficLightActorId << "\n"; + continue; + } + + // Assign traffic light attributes + TrafficLight& trafficLight = trafficLightMap[junctionId][linkId]; + trafficLight.carlaTrafficLightActorId = carlaTrafficLightActorId; // Store the Carla actor ID in the traffic light data + trafficLight.carlaTrafficLightActorPtr = carlaTrafficLightActorPtr; // Store the Carla actor pointer in the traffic light data + } + } + + while (simTime < simEndTime) { + std::chrono::steady_clock::time_point loop_start_time = std::chrono::steady_clock::now(); + ///*********************** + // RUN one-step simulation + ///*********************** + + + // ========================================== + // Receive data from the traffic layer server + // ========================================== + int simStateRecv; + float simTimeRecv; + msgHelper.clearRecvStorage(); + for (unsigned int iServer = 0; iServer < socketHelper.serverSock.size(); iServer++) { + + int simStateRecv; + float simTimeRecv; + + if (enableVerboseLog) { + printf("receiving server at port %d\n", socketHelper.SERVERPORT[iServer]); + + FILE* f = fopen(CarlaClientLogFile.c_str(), "a"); + fprintf(f, "recv server: %d\n", socketHelper.SERVERPORT[iServer]); + fclose(f); + } + + // save received message into Msg_c recv storages + if (socketHelper.recvData(socketHelper.serverSock[iServer], &simStateRecv, &simTimeRecv, msgHelper) < 0) { +#if defined(_WIN32) + if (WSAGetLastError() != WSAEINTR && WSAGetLastError() != WSAEFAULT) { + printf("ERROR: receive from server fails\n"); + } +#else + if (errno != EINTR && errno != EFAULT) { + printf("ERROR: receive from server fails\n"); + } +#endif + socketHelper.socketShutdown(); + exit(-1); + }; + } + + std::unordered_set setCurrentSumoIds; + for (const std::pair& pair : msgHelper.VehDataRecv_um) { + const VehFullData_t& tmpVehData = pair.second; + carla::geom::Location tmpLocation(tmpVehData.positionX, tmpVehData.positionY, tmpVehData.positionZ); + // The grade received from FIXS is in radians, convert to degrees + //carla::geom::Rotation tmpRotation(tmpVehData.grade * 180/M_PI, tmpVehData.heading, 0.0f); + carla::geom::Rotation tmpRotation(0.0f, std::round(tmpVehData.heading / 5.0f) * 5.0f, 0.0f); + //carla::geom::Vector3D tmpExtent(tmpVehData.length / 2, tmpVehData.width / 2, tmpVehData.height / 2); + carla::geom::Vector3D tmpExtent(4.8f, 2.0f, 1.8f); // Default extent, can be modified later + carla::geom::Transform tmpTransform(tmpLocation, tmpRotation); + + if (!MAP_CONTAINS_KEY(mapSumoActor, tmpVehData.id)) { + mapSumoActor[tmpVehData.id] = SumoActor(tmpVehData.id, tmpVehData.type, tmpVehData.vehicleClass, tmpTransform, tmpExtent); + std::cout << "Vehicle Class: " << tmpVehData.vehicleClass << std::endl; + } + else { + //// Update the existing actor's transform and extent + //carla::geom::Location prvLocation = mapSumoActor[tmpVehData.id].sumoTransform.location; + //carla::geom::Rotation prvRotation = mapSumoActor[tmpVehData.id].sumoTransform.rotation; + //carla::geom::Transform updatedTransform( + // carla::geom::Location( + // lowPassFilter(tmpTransform.location.x, prvLocation.x, 0.5f), + // lowPassFilter(tmpTransform.location.y, prvLocation.y, 0.5f), + // lowPassFilter(tmpTransform.location.z, prvLocation.z, 0.5f) + // ), carla::geom::Rotation( + // lowPassFilter(tmpTransform.rotation.pitch, prvRotation.pitch, 0.5f), + // lowPassFilter(tmpTransform.rotation.yaw, prvRotation.yaw, 0.5f), + // lowPassFilter(tmpTransform.rotation.roll, prvRotation.roll, 0.5f) + // ) + //); + mapSumoActor[tmpVehData.id].sumoTransform = tmpTransform; + mapSumoActor[tmpVehData.id].extent = tmpExtent; + } + setCurrentSumoIds.insert(tmpVehData.id); + } + if (enableTlsSync) { + for (const std::pair& pair : msgHelper.TlsDataRecv_um) { + const std::string& junctionID = pair.first; + const TrafficLightData_t& tmpTrafficLightData = pair.second; + const std::string& tafficLightStateStr = tmpTrafficLightData.state; + if (enableVerboseLog) { + std::cout << "Received Traffic Light Data for junction: " << junctionID << std::endl; + std::cout << "ID: " << tmpTrafficLightData.id << std::endl; + std::cout << "Name: " << tmpTrafficLightData.name << std::endl; + std::cout << "State: " << tmpTrafficLightData.state << std::endl; + } + // traverse each char in the state str + for (size_t linkId = 0; linkId < tafficLightStateStr.size(); ++linkId) { + char stateChar = tafficLightStateStr[linkId]; + SumoTrafficLightState sumoTrafficLightState = BridgeHelper::get_Sumo_traffic_light_state_from_char(stateChar); + carla::rpc::TrafficLightState carlaTrafficLightState = BridgeHelper::map_Sumo_traffic_light_state_to_Carla(sumoTrafficLightState); + // set the traffic light state in Carla, if the traffic light exists in the map + TrafficLight& trafficLight = trafficLightMap[junctionID][linkId]; + + if (trafficLight.carlaTrafficLightActorPtr) { + trafficLight.carlaTrafficLightActorPtr->SetState(carlaTrafficLightState); + } + else { + if (enableVerboseLog) std::cerr << "Carla actor for traffic light " << tmpTrafficLightData.id << "Junction Id:" << junctionID << "Link Id: " << linkId << " not found." << std::endl; + } + } + } + } + + // Check if the mapSumoActor contains vehicles that are not in the current step (vehicles have left the simulation) + // If so, remove them from the mapSumoActor and mapCarlaActor + for (std::unordered_map::iterator it = mapSumoActor.begin(); it != mapSumoActor.end(); ) { + std::string sumoActorId = it->first; + SumoActor& sumoActor = it->second; + if (!SET_CONTAINS_ID(setCurrentSumoIds, sumoActorId)) { + if(enableVerboseLog) std::cout << "Removing Sumo actor with ID: " << sumoActorId << std::endl; + // Remove associated Carla actor + std::string carlaActorId = mapSumoToCarla[sumoActorId]; + carla::SharedPtr carlaVehicleActorPtr = sumoActor.carlaVehicleActorPtr; + if (carlaVehicleActorPtr) { + carlaVehicleActorPtr->Destroy(); + if (enableVerboseLog) std::cout << "Destroyed Carla actor with ID: " << carlaActorId << std::endl; + } + mapSumoToCarla.erase(sumoActorId); + mapCarlaToSumo.erase(carlaActorId); + // Safely erase from mapSumoActor and advance the iterator + sumoActor.carlaVehicleActorPtr = nullptr; // Clear the pointer to avoid dangling references + it = mapSumoActor.erase(it); + } + else { + ++it; + } + } + + // Get the Carla Actor Role Name Mapping + carla::SharedPtr carlaActors = carlaWorld.GetActors()->Filter("vehicle.*"); + std::unordered_map mapActorIdToRoleName; + std::unordered_map mapRoleNameToActorId; + for (const carla::SharedPtr& carlaActor : *carlaActors) { + const std::vector carlaAttributes = carlaActor->GetAttributes(); // Ensure the attributes are loaded + for (const carla::client::ActorAttributeValue& attr : carlaAttributes) { + //std::cout << "Attribute ID: " << attr.GetId() << ", value: " << attr.GetValue() << std::endl; + if (attr.GetId() == "role_name") { + std::string carlaRoleName = attr.GetValue(); + std::string carlaActorId = std::to_string(carlaActor->GetId()); + mapActorIdToRoleName[carlaActorId] = carlaRoleName; + mapRoleNameToActorId[carlaRoleName] = carlaActorId; + break; // No need to check further attributes for this actor + } + + } + } + + for (std::pair& pair : mapSumoActor) { + const std::string& sumoActorId = pair.first; + SumoActor& sumoActor = pair.second; + + + carla::geom::Transform carlaTransform = BridgeHelper::map_transfrom_Sumo_to_Carla(sumoActor.sumoTransform, sumoActor.extent); + + //carla::SharedPtr carlaWaypoint = carlaMap->GetWaypoint(carlaTransform.location); + //carla::geom::Transform waypointTransform = carlaWaypoint->GetTransform(); + // ======================================================= + // [Optianal] Get the waypoint of the carla transform + // This is to ensure that the carla transform is on the road + // ======================================================= + //carlaTransform.location = waypointTransform.location; + //Use the waypoint's rotation to ensure the vehicle is aligned with the road + //carlaTransform.rotation = waypointTransform.rotation; + //print the sumo and carla transform + + + // ======================================================= + // Spawn the Sumo actors that are not in the current step + // ======================================================= + if (!MAP_CONTAINS_KEY(mapSumoToCarla , sumoActorId) || !sumoActor.spawnedInCarla || sumoActor.carlaVehicleActorPtr ==nullptr) { + // Add a small offset to the z coordinate to avoid collision with the ground + carlaTransform.location.z = carlaTransform.location.z + SPAWN_OFFSET_Z; + + + std::string carlaActorTypeId; + if (useVhicleTypeAsBlueprint) { + carlaActorTypeId = sumoActor.vType; + } + else { + carlaActorTypeId = BridgeHelper::map_Sumo_vClass_to_Carla_blueprintId(sumoActor.vClass); + } + + const carla::client::ActorBlueprint* vehicle_blueprint = blueprint_library->Find(carlaActorTypeId); + // C++ API does not allow modifying a blueprint after retrieval. + // To work around this in C++, we need to make a copy of the ActorBlueprint, + // then modify the attributes on the copy � not the original const pointer returned by Find. + carla::client::ActorBlueprint vehicle_blueprint_local = *vehicle_blueprint; + vehicle_blueprint_local.SetAttribute("role_name", sumoActorId); // Set the role name to the Sumo actor ID + + if (!vehicle_blueprint) { + std::cerr << "Blueprint not found: " << carlaActorTypeId << std::endl; + return 1; + } + + carla::SharedPtr carlaVehicleActorPtr; + // If the Intertested vehicle has been spawned in the Carla + // Note: For the interested vehicles spawned by the extrernal control script, its role_name should be set to the sumo id name + // And their blueprint should be the same as its vehicle type + if (SET_CONTAINS_ID(setInterestedIds, sumoActorId) && MAP_CONTAINS_KEY(mapRoleNameToActorId, sumoActorId) && useVhicleTypeAsBlueprint) { + carlaVehicleActorPtr = boost::static_pointer_cast(carlaWorld.GetActor(std::stoul(mapRoleNameToActorId[sumoActorId]))); + carlaVehicleActorPtr->SetTransform(carlaTransform); // Update the transform of the existing actor to the sumo position + if (enableVerboseLog) std::cout << "Found Intertested Vehicle with Carla Type ID: " << carlaActorTypeId << " SUMO ID:" << sumoActorId << std::endl; + } + else { + if (enableVerboseLog) std::cout << "Spawning actor with Carla Type ID: " << carlaActorTypeId << " SUMO ID:" << sumoActorId << std::endl; + + carlaVehicleActorPtr = boost::static_pointer_cast(carlaWorld.TrySpawnActor(vehicle_blueprint_local, carlaTransform)); + + if (carlaVehicleActorPtr != nullptr) { + + carlaVehicleActorPtr->SetSimulatePhysics(false); + sumoActor.spawnedInCarla = true; // Mark the Sumo actor as spawned in Carla + sumoActor.carlaVehicleActorPtr = carlaVehicleActorPtr; // Store the Carla actor in the SumoActor + + if (enableVerboseLog) std::cout << "Spawned actor with Carla ID: " << carlaVehicleActorPtr->GetId() << " SUMO ID:" << sumoActorId << std::endl; + if (enableVerboseLog && sumoActorId == "ego") { + std::cout << "Sumo Transform:" << std::endl; + std::cout << " Location -> x: " << sumoActor.sumoTransform.location.x + << ", y: " << sumoActor.sumoTransform.location.y + << ", z: " << sumoActor.sumoTransform.location.z << std::endl; + + std::cout << " Rotation -> pitch: " << sumoActor.sumoTransform.rotation.pitch + << ", yaw: " << sumoActor.sumoTransform.rotation.yaw + << ", roll: " << sumoActor.sumoTransform.rotation.roll << std::endl; + + std::cout << "Carla Transform:" << std::endl; + std::cout << " Location -> x: " << carlaTransform.location.x + << ", y: " << carlaTransform.location.y + << ", z: " << carlaTransform.location.z << std::endl; + + std::cout << " Rotation -> pitch: " << carlaTransform.rotation.pitch + << ", yaw: " << carlaTransform.rotation.yaw + << ", roll: " << carlaTransform.rotation.roll << std::endl; + } + // convert the carla actor id (uint_32 to string) + std::string carlaActorId = std::to_string(carlaVehicleActorPtr->GetId()); + mapCarlaToSumo[carlaActorId] = sumoActorId; + mapSumoToCarla[sumoActorId] = carlaActorId; + sumoActor.carlaTransform = carlaTransform; // Store the Carla transform in the SumoActor + } + else { + std::cout << "[Warning] Failed to spawn actor with SUMO ID:" << sumoActorId << std::endl; + } + + } + + } + else { + // ============================================================================================================== + // Update the existing carla actor's transform (except for the Interested Ids, which are controlled by the user) + // ============================================================================================================== + + // string to uint32_t conversion + carla::rpc::ActorId carlaActorId = static_cast(std::stoul(mapSumoToCarla[sumoActorId])); + + if (sumoActor.spawnedInCarla && sumoActor.carlaVehicleActorPtr !=nullptr) { + if (SET_CONTAINS_ID(setInterestedIds, sumoActorId) && enableExternalControl) { + if (enableVerboseLog) { + carla::SharedPtr carlaActor = mapSumoActor[mapSumoToCarla[sumoActorId]].carlaVehicleActorPtr; + carla::geom::Transform carlaTransform = carlaActor->GetTransform(); + std::cout << "Carla Transform:" << std::endl; + std::cout << " Location -> x: " << carlaTransform.location.x + << ", y: " << carlaTransform.location.y + << ", z: " << carlaTransform.location.z << std::endl; + } + } + else { + // If not interested or the lateral control is not enabled, update its position according to the sumo actor + // convert the id back to uint32_t + //carla::SharedPtr carlaActor = carlaWorld.GetActor(std::stoul(carlaActorId)); + carla::SharedPtr& carlaActor = sumoActor.carlaVehicleActorPtr; + const std::vector carlaAttributes = carlaActor->GetAttributes(); // Ensure the attributes are loaded + if (enableVerboseLog) { + for (const carla::client::ActorAttributeValue& attr : carlaAttributes) { + + //std::cout << "Attribute ID: " << attr.GetId() << ", value: " << attr.GetValue() << std::endl; + + if (attr.GetId() == "role_name") { + std::string role_name = attr.GetValue(); + + std::cout << "This vehicle's role_name is: " << role_name << std::endl; + } + } + } + // update the actor's transform + carla::rpc::Command::ApplyTransform applyTransformCommand(carlaActorId, carlaTransform); + transformCommandBatch.push_back(applyTransformCommand); + if (enableVerboseLog) std::cout << "Updating actor with ID: " << carlaActorId << " SUMO ID:" << sumoActorId << std::endl; + if (enableVerboseLog && sumoActorId == "ego") { + std::cout << "Sumo Transform:" << std::endl; + std::cout << " Location -> x: " << sumoActor.sumoTransform.location.x + << ", y: " << sumoActor.sumoTransform.location.y + << ", z: " << sumoActor.sumoTransform.location.z << std::endl; + + std::cout << " Rotation -> pitch: " << sumoActor.sumoTransform.rotation.pitch + << ", yaw: " << sumoActor.sumoTransform.rotation.yaw + << ", roll: " << sumoActor.sumoTransform.rotation.roll << std::endl; + + std::cout << "Carla Transform:" << std::endl; + std::cout << " Location -> x: " << carlaTransform.location.x + << ", y: " << carlaTransform.location.y + << ", z: " << carlaTransform.location.z << std::endl; + + std::cout << " Rotation -> pitch: " << carlaTransform.rotation.pitch + << ", yaw: " << carlaTransform.rotation.yaw + << ", roll: " << carlaTransform.rotation.roll << std::endl; + } + sumoActor.carlaTransform = carlaTransform; // Update the Carla transform in the SumoActor + + } + } else { + std::cerr << "Carla actor not found in the Actor Map for ID: " << carlaActorId << std::endl; + } + } + } + // false means that the command is not applied immediately, but in the next tick + // true means that the command is applied immediately (i.e., advance the world one frame) + carlaClient.ApplyBatch(transformCommandBatch, false); + // After updating the non-interested actors, we can now handle the interested actors. + // We use another script (another Carla client) to control the interested actors; + // these are the ego vehicles in this case. We want to get the updated positions of the interested actors + // by sending back the updated positions to the Sumo server. + // Note: In the control script, the control commands should be applied before the world.wait_for_tick() function. + carlaWorld.Tick(timeout_1s); + + // Clear the command batch for the next iteration + transformCommandBatch.clear(); + + //The posions (transform) of the interested actors should be updated by the control script, so we just retrive the current transform + //of the interested actors + for (const auto& sumoId : setInterestedIds) { + SumoActor& sumoActor = mapSumoActor[sumoId]; + if (sumoActor.spawnedInCarla && sumoActor.carlaVehicleActorPtr != nullptr) { + carla::SharedPtr& carlaActor = sumoActor.carlaVehicleActorPtr; + carla::geom::Transform carlaTransform = carlaActor->GetTransform(); + carla::geom::Vector3D carlaExtent = carlaActor->GetBoundingBox().extent; + carla::geom::Vector3D carlaVelocity = carlaActor->GetVelocity(); // This will update the velocity of the actor + carla::geom::Transform sumoTransform = BridgeHelper::map_transfrom_Carla_to_Sumo(carlaTransform, carlaExtent); + double carlaSpeed = std::sqrt(carlaVelocity.x * carlaVelocity.x + carlaVelocity.y * carlaVelocity.y); + + // Update the Sumo actor's transform + + mapSumoActor[sumoId].sumoTransform = sumoTransform; + carla::geom::Location sumoLocation = sumoTransform.location; + carla::geom::Rotation sumoRotation = sumoTransform.rotation; + VehFullData_t tmpVehData; + tmpVehData.id = sumoId; + tmpVehData.type = mapSumoActor[sumoId].vClass; + double simulatedSpeed = periodicCosineSpeed(simTime, 8.0f, 20.0f); + double speedDesired = carlaSpeed; + // if (enableExternalControl) { + // // If the vehicle is controlled by the external script, use the speed from the script + // speedDesired = carlaSpeed; // Use the speed from the Carla actor + // } + // else { + //speedDesired = simulatedSpeed; // Use the simulated speed, this can be further changed with desired speed like from the Eco-Pilot + // } + if (enableVerboseLog) std::cout << "Speed of the Sumo actor with ID: " << sumoId << " is: " << speedDesired << std::endl; + if (enableExternalControl) { + tmpVehData.speedDesired = speedDesired; + tmpVehData.positionX = sumoLocation.x; + tmpVehData.positionY = sumoLocation.y; + tmpVehData.positionZ = sumoLocation.z; + tmpVehData.heading = sumoRotation.yaw; + tmpVehData.grade = sumoRotation.pitch * M_PI / 180.0; // Convert to radians + + //tmpVehData.length = carlaExtent.x * 2; // The extent is half the length, so multiply by 2 + //tmpVehData.width = carlaExtent.y * 2; // The extent is half the width, so multiply by 2 + //tmpVehData.height = carlaExtent.z * 2; // The extent is half the height, so multiply by 2 + msgHelper.VehDataSend_um[socketHelper.serverSock[sockId]].push_back(tmpVehData); + } + + if (sumoId == centeredViewId) { + + // Set the spectator to follow the centered view actor + // Note: The spectator is a special camera that follows the actor + if (enableVerboseLog) std::cout << "Setting spectator to follow actor with ID: " << sumoId << std::endl; + static carla::geom::Location smoothedLocation; + carla::geom::Location tmpLocation = carlaTransform.location; + tmpLocation.z += 100.0f;// Set height to 100 meters above the ground + smoothedLocation = 0.9f * smoothedLocation + 0.1f * tmpLocation; + //pitch = -90.0, yaw = 0.0, roll = 0.0 + carla::geom::Rotation tmpRotation(-90.0f, -90.0f, 0.0f); + carla::geom::Transform tmpTransform(smoothedLocation, tmpRotation); + carlaSpectator->SetTransform(tmpTransform); + //drawCircle(carlaWorld.MakeDebugHelper(), tmpLocation, 2.5f, 32, 0.5f, 0.1f, 0.2f); + } + } + } + + // ======================================================================================= + // Semd data to the traffic layer server, to update the positions of the interested actors + // ======================================================================================= + uint8_t simStateSend = 1; + + for (unsigned int iServer = 0; iServer < socketHelper.serverSock.size(); iServer++) { + + int simStateRecv; + float simTimeRecv; + + if (enableVerboseLog) { + printf("sending server at port %d\n", socketHelper.SERVERPORT[iServer]); + + FILE* f = fopen(CarlaClientLogFile.c_str(), "a"); + fprintf(f, "send server: %d\n", socketHelper.SERVERPORT[iServer]); + fclose(f); + } + + // send data to the traffic layer server + if (socketHelper.sendData(socketHelper.serverSock[iServer], iServer, simTime, simStateSend, msgHelper) < 0) { +#if defined(_WIN32) + if (WSAGetLastError() != WSAEINTR && WSAGetLastError() != WSAEFAULT) { + printf("ERROR: send to server fails\n"); + } +#else + if (errno != EINTR && errno != EFAULT) { + printf("ERROR: send to server fails\n"); + } +#endif + socketHelper.socketShutdown(); + exit(-1); + }; + } + simTime += trafficRefreshRate; + + msgHelper.clearRecvStorage(); + msgHelper.clearSendStorage(); + //const std::chrono::steady_clock::time_point loop_end_time = std::chrono::steady_clock::now(); + //std::chrono::milliseconds elapsed = std::chrono::duration_cast(loop_end_time - loop_start_time); + //std::chrono::milliseconds sleep_duration = std::chrono::milliseconds(step_duration_ms) - elapsed; + //if (sleep_duration.count() > 0) { + // std::this_thread::sleep_for(sleep_duration); + //} + } + + carlaWorldSettings = carlaWorld.GetSettings(); + if (carlaWorldSettings.synchronous_mode) { + carlaWorldSettings.synchronous_mode = false; // Turn off synchronous mode + carlaWorld.ApplySettings(carlaWorldSettings, timeout_1s); + if (enableVerboseLog) std::cout << "Synchronous mode disabled.\n"; + } + } + catch (const carla::client::TimeoutException& e) { + socketHelper.socketShutdown(); + std::cout << '\n' << e.what() << std::endl; + return 1; + } + catch (const std::exception& e) { + socketHelper.socketShutdown(); + std::cout << "\nException: " << e.what() << std::endl; + return 2; + } +} diff --git a/VirtualEnvironment/VirtualEnvironment/VirtualEnvironment.vcxproj b/VirtualEnvironment/VirtualEnvironment/VirtualEnvironment.vcxproj index dd363ccb..b4577d97 100644 --- a/VirtualEnvironment/VirtualEnvironment/VirtualEnvironment.vcxproj +++ b/VirtualEnvironment/VirtualEnvironment/VirtualEnvironment.vcxproj @@ -44,26 +44,26 @@ StaticLibrary true - v142 + v143 Unicode StaticLibrary false - v142 + v143 true Unicode StaticLibrary true - v142 + v143 Unicode StaticLibrary false - v142 + v143 true Unicode @@ -133,7 +133,8 @@ true WIN64;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) true - C:\IPG\carmaker\win64-11.0.1\include\Car;C:\IPG\carmaker\win64-11.0.1\include;..\..\CommonLib;..\..\CommonLib\yaml-cpp\include;..\..\CommonLib\yaml-cpp\build\include + C:\IPG\carmaker\win64-13.1.2\include\Car;C:\IPG\carmaker\win64-13.1.2\include;..\..\CommonLib;..\..\CommonLib\yaml-cpp\include;..\..\CommonLib\yaml-cpp\build\include + MultiThreadedDLL @@ -156,7 +157,7 @@ true WIN64;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) true - C:\IPG\carmaker\win64-11.0.1\include\Car;C:\IPG\carmaker\win64-11.0.1\include;..\..\CommonLib;..\..\CommonLib\yaml-cpp\include;..\..\CommonLib\yaml-cpp\build\include + C:\IPG\carmaker\win64-13.1.2\include\Car;C:\IPG\carmaker\win64-13.1.2\include;..\..\CommonLib;..\..\CommonLib\yaml-cpp\include;..\..\CommonLib\yaml-cpp\build\include false diff --git a/compileExternalLibraries.bat b/compileExternalLibraries.bat index 24ed0f43..55787e4b 100644 --- a/compileExternalLibraries.bat +++ b/compileExternalLibraries.bat @@ -1,7 +1,7 @@ cd .\CommonLib\libevent if not exist build md build cd build -cmake -G "Visual Studio 16 2019" -DEVENT__DISABLE_MBEDTLS=ON .. +cmake -G "Visual Studio 17 2022" -DEVENT__DISABLE_MBEDTLS=ON .. cmake --build . --config Release cmake --build . --config Debug @@ -9,7 +9,7 @@ cd ..\..\..\ cd .\CommonLib\yaml-cpp if not exist build md build cd build -cmake -G "Visual Studio 16 2019" .. +cmake -G "Visual Studio 17 2022" .. cmake --build . --config Release cmake --build . --config Debug diff --git a/copyCarlaDependencies.ps1 b/copyCarlaDependencies.ps1 new file mode 100644 index 00000000..9f43268d --- /dev/null +++ b/copyCarlaDependencies.ps1 @@ -0,0 +1,48 @@ +param( + [Parameter(Mandatory = $true, Position = 0)] + [string]$CarlaRoot +) + +$ErrorActionPreference = "Stop" + +function Show-Usage { + Write-Host "Usage: .\copy_carla_dependencies.ps1 " + Write-Host "Copies CARLA's PythonAPI dependency bundle into CommonLib\libcarla." + Write-Host " Source: \PythonAPI\carla\dependencies" + Write-Host " Dest : \CommonLib\libcarla" +} + +if ($CarlaRoot -eq "-h" -or $CarlaRoot -eq "--help") { + Show-Usage + exit 0 +} + +try { + $CarlaRoot = (Resolve-Path -LiteralPath $CarlaRoot).Path +} catch { + Write-Error "Invalid CARLA_ROOT path: $CarlaRoot" + exit 1 +} + +$src = Join-Path $CarlaRoot "PythonAPI\carla\dependencies" +$repoRoot = Split-Path -Parent $MyInvocation.MyCommand.Path +$dst = Join-Path $repoRoot "CommonLib\libcarla" + +if (-not (Test-Path -LiteralPath $src)) { + Write-Error "Dependencies directory not found at: $src" + exit 2 +} + +if (Test-Path -LiteralPath $dst) { + Write-Error "Destination already exists: $dst" + Write-Error "Remove it first if you want to replace it." + exit 3 +} + +Write-Host "Copying CARLA dependencies..." +Write-Host " from: $src" +Write-Host " to : $dst" + +Copy-Item -LiteralPath $src -Destination $dst -Recurse + +Write-Host "Done. CARLA dependencies copied to $dst" diff --git a/copyCarlaDependencies.sh b/copyCarlaDependencies.sh new file mode 100755 index 00000000..7b7798f6 --- /dev/null +++ b/copyCarlaDependencies.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +set -euo pipefail + +usage() { + cat <<'EOF' +Usage: copy_carla_dependencies.sh /path/to/CARLA_ROOT + +Copies CARLA's PythonAPI dependency bundle into FIXS CommonLib as "libcarla". +- Source: /PythonAPI/carla/dependencies +- Dest: /CommonLib/libcarla + +If the destination already exists, the script will abort to avoid clobbering it. +EOF +} + +if [[ "${1:-}" == "-h" || "${1:-}" == "--help" || $# -ne 1 ]]; then + usage + exit 1 +fi + +CARLA_ROOT="$(cd "$1" && pwd)" +SRC="${CARLA_ROOT}/PythonAPI/carla/dependencies" +# Script lives in the repo root; use its directory as FIXS root +FIXS_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +DST="${FIXS_ROOT}/CommonLib/libcarla" + +if [[ ! -d "$SRC" ]]; then + echo "Error: dependencies directory not found at: $SRC" >&2 + exit 2 +fi + +if [[ -e "$DST" ]]; then + echo "Error: destination already exists: $DST" >&2 + echo "Remove it first if you want to replace it." >&2 + exit 3 +fi + +echo "Copying CARLA dependencies..." +echo " from: $SRC" +echo " to : $DST" +cp -a "$SRC" "$DST" + +echo "Done. CARLA dependencies copied to $DST" diff --git a/requirements.txt b/requirements.txt index ad88712c..55bd938f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,18 @@ -vissim = 11.0 -sumo = 1.13.0 -carmaker = 10.0.1 -matlab = 9.7 \ No newline at end of file +# External software (not installed via pip) +# vissim==11.0 +# sumo==1.13.0 +# carmaker==10.0.1 +# matlab==9.7 + +# Python packages +easydict>=1.9 +matplotlib>=3.5.0 +numpy>=1.23.0 +pandas>=1.5.0 +pyyaml>=6.0 +ruamel.yaml>=0.17.0 +sphinx>=5.0.0 +sphinx_rtd_theme>=1.0.0 +# SUMO Python libraries +traci +sumolib diff --git a/tests/VissimSpeedLimit/speedLimit.inpx b/tests/VissimSpeedLimit/speedLimit.inpx index fdf941b9..d2000bbd 100644 --- a/tests/VissimSpeedLimit/speedLimit.inpx +++ b/tests/VissimSpeedLimit/speedLimit.inpx @@ -1,5 +1,5 @@ - + @@ -533,7 +533,7 @@ - + @@ -541,7 +541,7 @@ - + @@ -549,7 +549,7 @@ - + @@ -557,55 +557,55 @@ - + - + - + - + - + - + - + - + - + @@ -1052,7 +1052,7 @@ - + @@ -1126,7 +1126,7 @@ - + @@ -1139,7 +1139,7 @@ - + @@ -1152,7 +1152,7 @@ - + @@ -1165,7 +1165,7 @@ - + @@ -1178,7 +1178,7 @@ - + @@ -1191,7 +1191,7 @@ - + @@ -1202,7 +1202,7 @@ - + @@ -1213,7 +1213,7 @@ - + @@ -1225,7 +1225,7 @@ - + @@ -1236,7 +1236,7 @@ - + @@ -1247,7 +1247,7 @@ - + @@ -1260,7 +1260,7 @@ - + @@ -1271,7 +1271,7 @@ - + @@ -1286,7 +1286,7 @@ - + @@ -1301,7 +1301,7 @@ - + @@ -1316,7 +1316,7 @@ - + @@ -1332,7 +1332,7 @@ - + @@ -1348,7 +1348,7 @@ - + @@ -1364,7 +1364,7 @@ - + @@ -1380,7 +1380,7 @@ - + @@ -1396,7 +1396,7 @@ - + @@ -1412,7 +1412,7 @@ - + @@ -1428,7 +1428,7 @@ - + @@ -1444,7 +1444,7 @@ - + @@ -1926,63 +1926,63 @@ - + - + - + - + - + - + - + - + - + @@ -1995,7 +1995,7 @@ - + @@ -2004,7 +2004,7 @@ - + @@ -2015,7 +2015,7 @@ - + @@ -2028,16 +2028,16 @@ - + - + - + - + @@ -2054,84 +2054,84 @@ - + - + - + - + - + - + - + - + - + - + - + - + @@ -2238,39 +2238,39 @@ - + - + - + - + - + - - + + diff --git a/tests/VissimSpeedLimit/speedLimit.layx b/tests/VissimSpeedLimit/speedLimit.layx index 60e8b734..956cb9fa 100644 --- a/tests/VissimSpeedLimit/speedLimit.layx +++ b/tests/VissimSpeedLimit/speedLimit.layx @@ -1,5 +1,5 @@ - + @@ -29,7 +29,7 @@ - + @@ -41,9 +41,9 @@ - + - + @@ -51,33 +51,33 @@ - - - - - + + + + + - - + + - + - + - - - - + + + + @@ -97,26 +97,26 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - + @@ -124,27 +124,29 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + @@ -175,17 +177,19 @@ - - - - - - - - - - - + + + + + + + + + + + + + @@ -256,23 +260,25 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + @@ -373,11 +379,11 @@ - + - + @@ -534,7 +540,7 @@ - + @@ -583,27 +589,29 @@ - + - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + @@ -618,18 +626,20 @@ - - - - - - - - - - - - + + + + + + + + + + + + + + @@ -654,27 +664,29 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + @@ -713,7 +725,7 @@ - + @@ -733,7 +745,7 @@ - + @@ -769,21 +781,23 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + @@ -807,7 +821,7 @@ - + @@ -828,33 +842,35 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -878,7 +894,7 @@ - + @@ -893,54 +909,56 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -981,7 +999,7 @@ - + @@ -1040,7 +1058,7 @@ - + @@ -1057,7 +1075,7 @@ - + @@ -1081,7 +1099,7 @@ - + @@ -1118,17 +1136,19 @@ - - - - - - - - - - - + + + + + + + + + + + + + @@ -1154,23 +1174,25 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + @@ -1186,32 +1208,34 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1230,7 +1254,7 @@ - + @@ -1247,7 +1271,7 @@ - + @@ -1264,9 +1288,9 @@ - + - + @@ -1289,15 +1313,15 @@ - + - + - + @@ -1314,9 +1338,9 @@ - + - + @@ -1339,7 +1363,7 @@ - + @@ -1347,7 +1371,7 @@ - + @@ -1364,7 +1388,7 @@ - + @@ -1402,7 +1426,7 @@ - + @@ -1425,7 +1449,7 @@ - + @@ -1433,7 +1457,7 @@ - + @@ -1450,7 +1474,7 @@ - + @@ -1488,7 +1512,7 @@ - + @@ -1511,14 +1535,14 @@ - + - + @@ -1577,7 +1601,7 @@ - + @@ -1592,7 +1616,7 @@ - + @@ -1656,7 +1680,7 @@ - + @@ -1680,7 +1704,7 @@ - + @@ -1709,7 +1733,7 @@ - + @@ -1748,7 +1772,7 @@ - + @@ -1768,12 +1792,12 @@ - + - + @@ -1787,7 +1811,7 @@ - + @@ -1797,7 +1821,7 @@ - + @@ -1896,7 +1920,7 @@ - + @@ -1930,11 +1954,11 @@ - + - + @@ -1959,7 +1983,7 @@ - + @@ -2018,7 +2042,7 @@ - + @@ -2037,7 +2061,7 @@ - + @@ -2047,7 +2071,7 @@ - + @@ -2061,7 +2085,7 @@ - + @@ -2090,7 +2114,7 @@ - + @@ -2129,7 +2153,7 @@ - + @@ -2153,7 +2177,7 @@ - + @@ -2168,7 +2192,7 @@ - + diff --git a/tests/VissimSpeedLimit/startVissim.m b/tests/VissimSpeedLimit/startVissim.m index 2b5d4eab..7c885045 100644 --- a/tests/VissimSpeedLimit/startVissim.m +++ b/tests/VissimSpeedLimit/startVissim.m @@ -5,7 +5,7 @@ function startVissim(netName, stopTime, configFilename) %% Connecting the COM Server => Open a new Vissim Window: % vis = actxserver('Vissim.Vissim'); -Vissim=actxserver('VISSIM.Vissim.2100'); +Vissim=actxserver('VISSIM.Vissim.2200'); % access_path=pwd; % vis.LoadNet([access_path '\TrafficModel\Headquarters 11.inpx']); % vis.LoadLayout([access_path '\TrafficModel\Headquarters 10.layx']);