diff --git a/src/dcaiti_control/CMakeLists.txt b/src/dcaiti_control/CMakeLists.txt index 96da4cb..0875add 100644 --- a/src/dcaiti_control/CMakeLists.txt +++ b/src/dcaiti_control/CMakeLists.txt @@ -33,8 +33,9 @@ if(BUILD_TESTING) endif() install( - DIRECTORY config description launch worlds tracks + DIRECTORY config description launch worlds DESTINATION share/${PROJECT_NAME} ) +ament_environment_hooks("${CMAKE_CURRENT_SOURCE_DIR}/env-hooks/${PROJECT_NAME}.dsv.in") ament_package() diff --git a/src/dcaiti_control/config/dcaiti_config.yml b/src/dcaiti_control/config/dcaiti_config.yml index 46ca605..f04465e 100644 --- a/src/dcaiti_control/config/dcaiti_config.yml +++ b/src/dcaiti_control/config/dcaiti_config.yml @@ -25,11 +25,11 @@ ackermann_steering_controller: rear_wheels_names: [rear_right_wheel_joint, rear_left_wheel_joint] front_wheels_names: [front_right_steer_joint, front_left_steer_joint] - wheelbase: 3.24644 - front_wheel_track: 2.12321 - rear_wheel_track: 1.76868 - front_wheels_radius: 0.45 - rear_wheels_radius: 0.45 + wheelbase: 3.0 + front_wheel_track: 3.0 + rear_wheel_track: 3.0 + front_wheels_radius: 0.2 + rear_wheels_radius: 0.2 joint_limits: diff --git a/src/dcaiti_control/description/blue_cone.xacro b/src/dcaiti_control/description/blue_cone.xacro index 8fd54e3..cf90b90 100644 --- a/src/dcaiti_control/description/blue_cone.xacro +++ b/src/dcaiti_control/description/blue_cone.xacro @@ -3,12 +3,12 @@ - - - true - - + + + + + diff --git a/src/dcaiti_control/description/robot.urdf.xacro b/src/dcaiti_control/description/robot.urdf.xacro index da9598c..994d34d 100644 --- a/src/dcaiti_control/description/robot.urdf.xacro +++ b/src/dcaiti_control/description/robot.urdf.xacro @@ -13,21 +13,21 @@ - - + + - - + + - - + + diff --git a/src/dcaiti_control/description/yellow_cone.xacro b/src/dcaiti_control/description/yellow_cone.xacro deleted file mode 100644 index e1ecf39..0000000 --- a/src/dcaiti_control/description/yellow_cone.xacro +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - true - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/dcaiti_control/env-hooks/dcaiti_control.dsv.in b/src/dcaiti_control/env-hooks/dcaiti_control.dsv.in new file mode 100644 index 0000000..b604faf --- /dev/null +++ b/src/dcaiti_control/env-hooks/dcaiti_control.dsv.in @@ -0,0 +1 @@ +prepend-non-duplicate;IGN_GAZEBO_RESOURCE_PATH;share/@PROJECT_NAME@/worlds \ No newline at end of file diff --git a/src/dcaiti_control/launch/launch_sim.py b/src/dcaiti_control/launch/launch_sim.py index e7ae612..ddc09f2 100644 --- a/src/dcaiti_control/launch/launch_sim.py +++ b/src/dcaiti_control/launch/launch_sim.py @@ -17,140 +17,6 @@ from launch_ros.actions import Node from launch.actions import RegisterEventHandler from launch.event_handlers import OnProcessExit -from pathlib import Path -from typing import List, Tuple, Union, cast, Dict -from struct import Struct -from enum import IntEnum - -import numpy as np - - - -class ConeTypes(IntEnum): - """ - Enum for all possible cone types - """ - - UNKNOWN = 0 - YELLOW = 1 - RIGHT = 1 - BLUE = 2 - LEFT = 2 - ORANGE_SMALL = 3 - START_FINISH_AREA = 3 - ORANGE_BIG = 4 - START_FINISH_LINE = 4 - - -HEADER_STRUCT = Struct("6sBBhBB") -BLOCK_STRUCT = Struct("2h4B") - -LytObjectIndexToConeType: Dict[int, ConeTypes] = { - 25: ConeTypes.UNKNOWN, - 29: ConeTypes.YELLOW, - 30: ConeTypes.YELLOW, - 23: ConeTypes.BLUE, - 24: ConeTypes.BLUE, - 27: ConeTypes.ORANGE_BIG, - 20: ConeTypes.ORANGE_SMALL, -} - -def split_header_blocks(data: bytes) -> Tuple[bytes, bytes]: - """ - Split the content of the lyt file into header and block. This split is easy because - the header has a fixed size - - Args: - data (bytes): The content of the lyt file - - Returns: - Tuple[bytes, bytes]: The header and the block - """ - return data[: HEADER_STRUCT.size], data[HEADER_STRUCT.size :] - - -def verify_lyt_header(header_data: bytes) -> None: - """ - Parse the header and perform some sanity checks suggested by the LFS documentation - - Args: - header_data (bytes): The header bytes of the `.lyt` file - """ - - header = cast( - Tuple[bytes, int, int, int, int, int], HEADER_STRUCT.unpack(header_data) - ) - - file_type, version, revision, _, _, _ = header - assert file_type == b"LFSLYT" - assert version <= 0, version - # revision allowed up to 252 - # https://www.lfs.net/forum/thread/96153-LYT-revision-252-in-W-patch - assert revision <= 252, revision - - -def extract_cone_lists(blocks_data: bytes) -> List[List[Tuple[float, float]]]: - """ - Extract the cone object positions from the object blocks bytes of a lyt file - - Args: - blocks_data (bytes): The data in the lyt file that is not the header - - Returns: - List[List[Tuple[int, int]]]: The cone positions split by cone type - """ - decoded_blocks = BLOCK_STRUCT.iter_unpack(blocks_data) - all_cones_per_type: List[List[Tuple[float, float]]] = [[] for _ in ConeTypes] - - # cone_info: - for cone_info in decoded_blocks: - obj_x, obj_y, _, _, lyt_obj_idx, _ = cast( - Tuple[int, int, int, int, int, int], cone_info - ) - - try: - cone_type = LytObjectIndexToConeType[lyt_obj_idx] - except KeyError: - # not a cone - continue - - # the stored x,y pos is multiplied by - # 16 in the file so we need to convert it back - # (and cast to a float by using real div) - obj_x_meters = obj_x / 16 - obj_y_meters = obj_y / 16 - all_cones_per_type[cone_type].append((obj_x_meters, obj_y_meters)) - return all_cones_per_type - - -def load_lyt_file(filename: Union[Path, str]) -> List[np.ndarray]: - """ - Load a `.lyt` file and return the positions of the cone objects inside it split - according to `ConeTypes` - - Args: - filename (Path): The path to the `.lyt` file - - Returns: - List[np.ndarray]: A list of 2d np.ndarrays representing the cone positions of - for all cone types - """ - if isinstance(filename, str): - filename = Path(filename) - assert filename.is_file(), filename - assert filename.suffix == ".lyt", filename - data = filename.read_bytes() - header_data, blocks_data = split_header_blocks(data) - verify_lyt_header(header_data) - - all_cones_per_type = extract_cone_lists(blocks_data) - - all_cones_per_type_arrays = [ - np.array(cone_list) for cone_list in all_cones_per_type - ] - - return all_cones_per_type_arrays - def generate_launch_description(): # Include the robot_state_publisher launch file, provided by our own package. Force sim time to be enabled @@ -175,7 +41,7 @@ def generate_launch_description(): gazebo = IncludeLaunchDescription( PythonLaunchDescriptionSource([os.path.join( get_package_share_directory('ros_gz_sim'), 'launch', 'gz_sim.launch.py')]), - launch_arguments=[('gz_args', [f" -r -v 1 {world_path}/empty.sdf"])], + launch_arguments=[('gz_args', [f" -r -v 1 {world_path}/generated_worlds/AU2_skidpad.sdf"])], ) # Run the spawner node from the gazebo_ros package. The entity name doesn't really matter if you only have a single robot. @@ -189,50 +55,6 @@ def generate_launch_description(): output='screen' ) - cone_positions = [x.reshape(-1,2) for x in load_lyt_file(base_path / 'tracks' / 'AU2_skidpad.lyt')] - center = np.mean(cone_positions[ConeTypes.ORANGE_BIG], axis=0) - - cone_positions_centered = [x - center for x in cone_positions] - - - cones_dict = { - "unknown": cone_positions_centered[ConeTypes.UNKNOWN].tolist(), - "yellow": cone_positions_centered[ConeTypes.YELLOW].tolist(), - "blue": cone_positions_centered[ConeTypes.BLUE].tolist(), - "orange_small": cone_positions_centered[ConeTypes.ORANGE_SMALL].tolist(), - "orange_big": cone_positions_centered[ConeTypes.ORANGE_BIG].tolist(), - } - - yellow_cone_xacro = f'{description_path}/yellow_cone.xacro' - yellow_cone_config = Command(['xacro ', yellow_cone_xacro]) - blue_cone_xacro = f'{description_path}/blue_cone.xacro' - blue_cone_config = Command(['xacro ', blue_cone_xacro]) - cone_spawner = [] - for i, (x,y) in enumerate(cones_dict['yellow']): - spawn_cone = Node(package='ros_gz_sim', executable='create', - arguments=[ - '-string', yellow_cone_config, - '-name', f'yellow_cone_{i}', - '-z', '0.3', - '-y', str(y), - '-x', str(x), - ], - output='screen' - ) - cone_spawner.append(spawn_cone) - for i, (x,y) in enumerate(cones_dict['blue']): - spawn_cone = Node(package='ros_gz_sim', executable='create', - arguments=[ - '-string', blue_cone_config, - '-name', f'blue_cone_{i}', - '-z', '0.3', - '-y', str(y), - '-x', str(x), - ], - output='screen' - ) - cone_spawner.append(spawn_cone) - # Bridge bridge = Node( package='ros_gz_bridge', @@ -240,8 +62,6 @@ def generate_launch_description(): arguments=[ '/clock@rosgraph_msgs/msg/Clock[ignition.msgs.Clock', '/boxes@vision_msgs/msg/Detection2DArray@ignition.msgs.AnnotatedAxisAligned2DBox_V', - '/boxes_image@sensor_msgs/msg/Image@ignition.msgs.Image', - '/camera_info@sensor_msgs/msg/CameraInfo@ignition.msgs.CameraInfo', '/lidar@sensor_msgs/msg/LaserScan@ignition.msgs.LaserScan', '/lidar/points@sensor_msgs/msg/PointCloud2@ignition.msgs.PointCloudPacked' ], @@ -291,4 +111,4 @@ def generate_launch_description(): spawn_entity, delayed_diff_drive_spawner, delayed_joint_broad_spawner, - ]+ cone_spawner ) + ]) diff --git a/src/dcaiti_control/description/assets/blue_cone.dae b/src/dcaiti_control/worlds/assets/blue_cone.dae similarity index 100% rename from src/dcaiti_control/description/assets/blue_cone.dae rename to src/dcaiti_control/worlds/assets/blue_cone.dae diff --git a/src/dcaiti_control/description/assets/yellow_cone.dae b/src/dcaiti_control/worlds/assets/yellow_cone.dae similarity index 100% rename from src/dcaiti_control/description/assets/yellow_cone.dae rename to src/dcaiti_control/worlds/assets/yellow_cone.dae diff --git a/src/dcaiti_control/worlds/empty.sdf.template b/src/dcaiti_control/worlds/empty.sdf.template new file mode 100644 index 0000000..3329b81 --- /dev/null +++ b/src/dcaiti_control/worlds/empty.sdf.template @@ -0,0 +1,84 @@ + + + + + 0.005 + 1.0 + + + quick + true + true + cone_model + + + + + + + + + + + + + ogre2 + + + false + false + false + + + + 0 0 10 0 0 0 + 0.8 0.8 0.8 1 + 0.2 0.2 0.2 1 + + 1000 + 0.9 + 0.01 + 0.001 + + -0.5 0.1 -0.9 + + + + true + + + + + 0 0 1 + 100 100 + + + + + + + 0 0 1 + 100 100 + + + + 0.8 0.8 0.8 1 + 0.8 0.8 0.8 1 + 0.8 0.8 0.8 1 + + + + + + {{cones}} + + + \ No newline at end of file diff --git a/src/dcaiti_control/worlds/empty.world b/src/dcaiti_control/worlds/empty.world deleted file mode 100644 index 5321672..0000000 --- a/src/dcaiti_control/worlds/empty.world +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - model://sun - - - - model://ground_plane - - - diff --git a/src/dcaiti_control/worlds/generate_track_world.py b/src/dcaiti_control/worlds/generate_track_world.py new file mode 100644 index 0000000..8c3f916 --- /dev/null +++ b/src/dcaiti_control/worlds/generate_track_world.py @@ -0,0 +1,208 @@ +from pathlib import Path +from typing import List, Tuple, Union, cast, Dict +from struct import Struct +from enum import IntEnum + +import numpy as np + + + +class ConeTypes(IntEnum): + """ + Enum for all possible cone types + """ + + UNKNOWN = 0 + YELLOW = 1 + RIGHT = 1 + BLUE = 2 + LEFT = 2 + ORANGE_SMALL = 3 + START_FINISH_AREA = 3 + ORANGE_BIG = 4 + START_FINISH_LINE = 4 + + +HEADER_STRUCT = Struct("6sBBhBB") +BLOCK_STRUCT = Struct("2h4B") + +LytObjectIndexToConeType: Dict[int, ConeTypes] = { + 25: ConeTypes.UNKNOWN, + 29: ConeTypes.YELLOW, + 30: ConeTypes.YELLOW, + 23: ConeTypes.BLUE, + 24: ConeTypes.BLUE, + 27: ConeTypes.ORANGE_BIG, + 20: ConeTypes.ORANGE_SMALL, +} + +def split_header_blocks(data: bytes) -> Tuple[bytes, bytes]: + """ + Split the content of the lyt file into header and block. This split is easy because + the header has a fixed size + + Args: + data (bytes): The content of the lyt file + + Returns: + Tuple[bytes, bytes]: The header and the block + """ + return data[: HEADER_STRUCT.size], data[HEADER_STRUCT.size :] + + +def verify_lyt_header(header_data: bytes) -> None: + """ + Parse the header and perform some sanity checks suggested by the LFS documentation + + Args: + header_data (bytes): The header bytes of the `.lyt` file + """ + + header = cast( + Tuple[bytes, int, int, int, int, int], HEADER_STRUCT.unpack(header_data) + ) + + file_type, version, revision, _, _, _ = header + assert file_type == b"LFSLYT" + assert version <= 0, version + # revision allowed up to 252 + # https://www.lfs.net/forum/thread/96153-LYT-revision-252-in-W-patch + assert revision <= 252, revision + + +def extract_cone_lists(blocks_data: bytes) -> List[List[Tuple[float, float]]]: + """ + Extract the cone object positions from the object blocks bytes of a lyt file + + Args: + blocks_data (bytes): The data in the lyt file that is not the header + + Returns: + List[List[Tuple[int, int]]]: The cone positions split by cone type + """ + decoded_blocks = BLOCK_STRUCT.iter_unpack(blocks_data) + all_cones_per_type: List[List[Tuple[float, float]]] = [[] for _ in ConeTypes] + + # cone_info: + for cone_info in decoded_blocks: + obj_x, obj_y, _, _, lyt_obj_idx, _ = cast( + Tuple[int, int, int, int, int, int], cone_info + ) + + try: + cone_type = LytObjectIndexToConeType[lyt_obj_idx] + except KeyError: + # not a cone + continue + + # the stored x,y pos is multiplied by + # 16 in the file so we need to convert it back + # (and cast to a float by using real div) + obj_x_meters = obj_x / 16 + obj_y_meters = obj_y / 16 + all_cones_per_type[cone_type].append((obj_x_meters, obj_y_meters)) + return all_cones_per_type + + +def load_lyt_file(filename: Union[Path, str]) -> List[np.ndarray]: + """ + Load a `.lyt` file and return the positions of the cone objects inside it split + according to `ConeTypes` + + Args: + filename (Path): The path to the `.lyt` file + + Returns: + List[np.ndarray]: A list of 2d np.ndarrays representing the cone positions of + for all cone types + """ + if isinstance(filename, str): + filename = Path(filename) + assert filename.is_file(), filename + assert filename.suffix == ".lyt", filename + data = filename.read_bytes() + header_data, blocks_data = split_header_blocks(data) + verify_lyt_header(header_data) + + all_cones_per_type = extract_cone_lists(blocks_data) + + all_cones_per_type_arrays = [ + np.array(cone_list) for cone_list in all_cones_per_type + ] + + return all_cones_per_type_arrays + +if __name__ == "__main__": + import argparse + import json + import jinja2 + + # one argument for the lyt file + parser = argparse.ArgumentParser(description="Parse a lyt file") + parser.add_argument("lyt_file", type=str, help="The lyt file to parse") + + args = parser.parse_args() + + # load the lyt file + lyt_file = Path(args.lyt_file) + cone_positions = [x.reshape(-1,2) for x in load_lyt_file(lyt_file)] + + + center = np.mean(cone_positions[ConeTypes.ORANGE_BIG], axis=0) + + cone_positions_centered = [x - center for x in cone_positions] + + + cones_dict = { + "unknown": cone_positions_centered[ConeTypes.UNKNOWN].tolist(), + "yellow_cone": cone_positions_centered[ConeTypes.YELLOW].tolist(), + "blue_cone": cone_positions_centered[ConeTypes.BLUE].tolist(), + "orange_small_cone": cone_positions_centered[ConeTypes.ORANGE_SMALL].tolist(), + "orange_big_cone": cone_positions_centered[ConeTypes.ORANGE_BIG].tolist(), + } + + cone_list_sdf = [] + z = 1 + + for cone_type in ['blue_cone', 'yellow_cone']: + for i, (x,y) in enumerate(cones_dict[cone_type]): + cone_sdf = f''' + + + {x} {y} {z} 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/{cone_type}.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + ''' + cone_list_sdf.append(cone_sdf) + # read template and fill in the cone positions + template = jinja2.Template(Path("empty.sdf.template").read_text()) + filled_template = template.render(cones="\n".join(cone_list_sdf)) + + # write the filled template string to a sdf file + with open(lyt_file.parent.parent / "generated_worlds" / lyt_file.with_suffix('.sdf').name, "w+") as f: + f.write(filled_template) + diff --git a/src/dcaiti_control/worlds/generated_worlds/AU2_skidpad.sdf b/src/dcaiti_control/worlds/generated_worlds/AU2_skidpad.sdf new file mode 100644 index 0000000..a9df3cc --- /dev/null +++ b/src/dcaiti_control/worlds/generated_worlds/AU2_skidpad.sdf @@ -0,0 +1,1881 @@ + + + + + 0.005 + 1.0 + + + quick + true + true + cone_model + + + + + + + + + + + + + ogre2 + + + false + false + false + + + + 0 0 10 0 0 0 + 0.8 0.8 0.8 1 + 0.2 0.2 0.2 1 + + 1000 + 0.9 + 0.01 + 0.001 + + -0.5 0.1 -0.9 + + + + true + + + + + 0 0 1 + 100 100 + + + + + + + 0 0 1 + 100 100 + + + + 0.8 0.8 0.8 1 + 0.8 0.8 0.8 1 + 0.8 0.8 0.8 1 + + + + + + + + + -2.078125 2.96875 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/blue_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + -3.703125 5.46875 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/blue_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + -6.203125 7.09375 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/blue_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + -9.078125 7.65625 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/blue_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + -12.015625 7.09375 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/blue_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + -14.515625 5.46875 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/blue_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + -16.140625 2.96875 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/blue_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + -16.703125 0.09375 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/blue_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + -16.140625 -2.84375 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/blue_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + -14.515625 -5.34375 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/blue_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + -12.015625 -6.96875 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/blue_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + -9.140625 -7.53125 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/blue_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + -6.203125 -6.96875 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/blue_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + -3.703125 -5.34375 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/blue_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + -2.078125 -2.90625 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/blue_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + 3.359375 -8.84375 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/blue_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + 4.984375 -9.71875 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/blue_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + 9.109375 -10.59375 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/blue_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + 13.109375 -9.78125 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/blue_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + 16.671875 -7.46875 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/blue_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + 18.984375 -3.90625 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/blue_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + 19.734375 0.03125 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/blue_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + 18.984375 4.03125 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/blue_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + 16.671875 7.59375 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/blue_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + 13.109375 9.90625 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/blue_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + 9.109375 10.65625 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/blue_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + 4.984375 9.84375 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/blue_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + 3.359375 8.96875 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/blue_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + -1.515625 -0.03125 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/blue_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + -3.328125 -8.84375 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/yellow_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + 2.109375 -2.84375 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/yellow_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + 3.734375 -5.34375 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/yellow_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + 6.234375 -6.96875 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/yellow_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + 9.109375 -7.53125 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/yellow_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + 12.046875 -6.96875 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/yellow_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + 14.546875 -5.34375 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/yellow_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + 16.171875 -2.84375 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/yellow_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + 16.734375 0.03125 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/yellow_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + 16.171875 2.96875 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/yellow_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + 14.546875 5.46875 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/yellow_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + 12.046875 7.09375 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/yellow_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + 9.171875 7.65625 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/yellow_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + 6.234375 7.09375 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/yellow_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + 3.734375 5.46875 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/yellow_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + 2.109375 3.03125 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/yellow_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + -3.328125 8.96875 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/yellow_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + -4.953125 9.84375 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/yellow_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + -9.140625 10.65625 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/yellow_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + -13.078125 9.90625 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/yellow_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + -16.640625 7.59375 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/yellow_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + -18.953125 4.03125 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/yellow_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + -19.765625 0.03125 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/yellow_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + -18.953125 -3.90625 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/yellow_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + -16.640625 -7.46875 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/yellow_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + -13.078125 -9.78125 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/yellow_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + -9.140625 -10.59375 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/yellow_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + -4.953125 -9.71875 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/yellow_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + + 1.484375 -0.03125 1 0 0 0 + + + + 0.112 0.112 0.3 + + + + + + model://assets/yellow_cone.dae + + + + + collision + + + + + vehicle_blue + wall + + true + + + + + + \ No newline at end of file diff --git a/src/dcaiti_control/tracks/AU2_skidpad.lyt b/src/dcaiti_control/worlds/tracks/AU2_skidpad.lyt similarity index 100% rename from src/dcaiti_control/tracks/AU2_skidpad.lyt rename to src/dcaiti_control/worlds/tracks/AU2_skidpad.lyt