From 1d3e58bc764c5edb612a10465de94dd94dcd0f33 Mon Sep 17 00:00:00 2001 From: Montgomery Alban Date: Wed, 3 Jan 2024 22:47:45 +0000 Subject: [PATCH] Update AgentInterface with vehicle class. --- CHANGELOG.md | 2 + .../vehicles/vehicle_definitions_list.yaml | 6 +-- smarts/core/agent_interface.py | 29 +++++++------ smarts/core/agents_provider.py | 1 - smarts/core/chassis.py | 4 +- smarts/core/controllers/__init__.py | 5 --- .../core/tests/test_trajectory_controller.py | 10 +++-- smarts/core/utils/resources.py | 18 ++++---- smarts/core/vehicle.py | 17 +++++++- smarts/core/vehicle_index.py | 41 ++++++++++--------- 10 files changed, 75 insertions(+), 58 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d2bdf98dce..a543e70c29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,9 +24,11 @@ Copy and pasting the git commit messages is __NOT__ enough. - Renamed `MACOS` `pybullet` gui utility from `smarts.core.utils.bullet.BulletClient` to `smarts.core.utils.pybullet.BulletClientMACOS`. - `Vehicle.build_agent_vehicle()` and `Vehicle.build_social_vehicle()` moved to `VehicleIndex`. - `smarts.core.configuration.Configuration.get_settings()` now uses the `PyYAML` default instead of forcefully casting to `str`. +- Added `AgentInterface.vehicle_class` which allows selection of a dynamics vehicle from the vehicle definitions list file. ### Deprecated - Module `smarts.core.models` is now deprecated in favour of `smarts.assets`. - Deprecated a few things related to vehicles in the `Scenario` class, including the `vehicle_filepath`, `tire_parameters_filepath`, and `controller_parameters_filepath`. The functionality is now handled through the vehicle definitions. +- `AgentInterface.vehicle_type` is now deprecated with potential to be restored. ### Fixed - `SumoTrafficSimulation` gives clearer reasons as to why it failed to connect to the TraCI server. - Suppressed an issue where `pybullet_utils.pybullet.BulletClient` would cause an error because it was catching a non `BaseException` type. diff --git a/smarts/assets/vehicles/vehicle_definitions_list.yaml b/smarts/assets/vehicles/vehicle_definitions_list.yaml index c2fc360b52..4a6c9bef06 100644 --- a/smarts/assets/vehicles/vehicle_definitions_list.yaml +++ b/smarts/assets/vehicles/vehicle_definitions_list.yaml @@ -1,6 +1,6 @@ bus: ${SMARTS_ASSETS_PATH}/vehicles/definitions/bus.yaml moving_truck_empty: ${SMARTS_ASSETS_PATH}/vehicles/definitions/moving_truck_empty.yaml moving_truck_loaded: ${SMARTS_ASSETS_PATH}/vehicles/definitions/moving_truck_loaded.yaml -sedan: &SEDAN ${SMARTS_ASSETS_PATH}/vehicles/definitions/sedan.yaml -truck: ${SMARTS_ASSETS_PATH}/vehicles/definitions/moving_truck_empty.yaml -passenger: *SEDAN \ No newline at end of file +generic_sedan: &GENERIC_SEDAN ${SMARTS_ASSETS_PATH}/vehicles/definitions/sedan.yaml +sedan: *GENERIC_SEDAN +truck: ${SMARTS_ASSETS_PATH}/vehicles/definitions/moving_truck_empty.yaml \ No newline at end of file diff --git a/smarts/core/agent_interface.py b/smarts/core/agent_interface.py index ee7b640756..e4e78cc74b 100644 --- a/smarts/core/agent_interface.py +++ b/smarts/core/agent_interface.py @@ -320,13 +320,16 @@ class AgentInterface: The choice of action space; this also decides the controller that will be enabled and the chassis type that will be used. """ - vehicle_type: Literal[ - "bus", "coach", "motorcycle", "pedestrian", "sedan", "trailer", "truck" - ] = "sedan" + vehicle_type: str = "" """ The choice of vehicle type. """ + vehicle_class: str = "generic_sedan" + """ + The choice of vehicle classes from the vehicle definition list. + """ + accelerometer: Union[Accelerometer, bool] = True """ Enable acceleration and jerk observations. @@ -369,15 +372,17 @@ def __post_init__(self): self.lane_positions, LanePositions ) self.signals = AgentInterface._resolve_config(self.signals, Signals) - assert self.vehicle_type in { - "bus", - "coach", - "motorcycle", - "pedestrian", - "sedan", - "trailer", - "truck", - } + if self.vehicle_type != "": + warnings.warn( + "`vehicle_type` is now deprecated. Instead use `vehicle_class`.", + category=DeprecationWarning, + ) + assert self.vehicle_type in ( + "sedan", + "bus", + ), "Only these values were supported at deprecation." + self.vehicle_class = self.vehicle_type + assert isinstance(self.vehicle_class, str) @staticmethod def from_type(requested_type: AgentType, **kwargs): diff --git a/smarts/core/agents_provider.py b/smarts/core/agents_provider.py index 2c3d0186c7..5f33a85149 100644 --- a/smarts/core/agents_provider.py +++ b/smarts/core/agents_provider.py @@ -169,7 +169,6 @@ def perform_agent_actions(self, agent_actions: Dict[str, Any]): controller_state, sensor_state, agent_interface.action, - agent_interface.vehicle_type, ) @property diff --git a/smarts/core/chassis.py b/smarts/core/chassis.py index fbb15ceb3b..b4946ff65f 100644 --- a/smarts/core/chassis.py +++ b/smarts/core/chassis.py @@ -346,7 +346,7 @@ def __init__( self, pose: Pose, bullet_client: bc.BulletClient, - vehicle_filepath=DEFAULT_VEHICLE_FILEPATH, + vehicle_dynamics_filepath=DEFAULT_VEHICLE_FILEPATH, tire_parameters_filepath=None, friction_map=None, controller_parameters=DEFAULT_CONTROLLER_PARAMETERS, @@ -381,7 +381,7 @@ def __init__( self._road_wheel_frictions = None self._bullet_id = self._client.loadURDF( - vehicle_filepath, + vehicle_dynamics_filepath, [0, 0, 0], [0, 0, 0, 1], useFixedBase=False, diff --git a/smarts/core/controllers/__init__.py b/smarts/core/controllers/__init__.py index 7a82e443a7..029d60dd07 100644 --- a/smarts/core/controllers/__init__.py +++ b/smarts/core/controllers/__init__.py @@ -70,7 +70,6 @@ def perform_action( controller_state, sensor_state, action_space, - vehicle_type, ): """Calls control for the given vehicle based on a given action space and action. @@ -89,13 +88,9 @@ def perform_action( The state of a vehicle sensor as relates to vehicle sensors. action_space: The action space of the provided action. - vehicle_type: - Vehicle type information about the given vehicle. """ if action is None: return - if vehicle_type == "bus": - assert action_space == ActionSpaceType.Trajectory if action_space == ActionSpaceType.Continuous: vehicle.control( throttle=np.clip(action[0], 0.0, 1.0), diff --git a/smarts/core/tests/test_trajectory_controller.py b/smarts/core/tests/test_trajectory_controller.py index 091ba00bae..ab84c79bad 100644 --- a/smarts/core/tests/test_trajectory_controller.py +++ b/smarts/core/tests/test_trajectory_controller.py @@ -36,6 +36,7 @@ from smarts.core.utils import pybullet from smarts.core.utils.pybullet import SafeBulletClient from smarts.core.utils.resources import ( + VehicleDefinitions, load_vehicle_definitions_list, load_yaml_config_with_substitution, ) @@ -45,13 +46,14 @@ @pytest.fixture -def vehicle_definitions_list(): +def vehicle_definitions_list() -> VehicleDefinitions: return load_vehicle_definitions_list(None) @pytest.fixture(params=["bus", "sedan", "truck"]) -def vehicle_definition(vehicle_definitions_list, request: pytest.FixtureRequest): - +def vehicle_definition( + vehicle_definitions_list: VehicleDefinitions, request: pytest.FixtureRequest +): return vehicle_definitions_list.load_vehicle_definition(request.param) @@ -79,7 +81,7 @@ def vehicle(bullet_client, vehicle_definition, fixed_timestep_sec=time_step): chassis=AckermannChassis( pose=pose, bullet_client=bullet_client, - vehicle_filepath=vehicle_definition["dynamics_model"], + vehicle_dynamics_filepath=vehicle_definition["dynamics_model"], controller_parameters=load_yaml_config_with_substitution( vehicle_definition["controller_params"] ), diff --git a/smarts/core/utils/resources.py b/smarts/core/utils/resources.py index f1f2d10f23..e3da5f8316 100644 --- a/smarts/core/utils/resources.py +++ b/smarts/core/utils/resources.py @@ -39,7 +39,7 @@ def load_vehicle_definitions_list(vehicle_list_filepath: Optional[str]): vehicle_list_filepath = config()("assets", "default_vehicle_definitions_list") vehicle_list_filepath = Path(vehicle_list_filepath).absolute() - return VehicleDefintions( + return VehicleDefinitions( data=load_yaml_config_with_substitution(vehicle_list_filepath), filepath=vehicle_list_filepath, ) @@ -96,7 +96,7 @@ def load_yaml_config_with_substitution( @dataclass(frozen=True) -class VehicleDefintions: +class VehicleDefinitions: """This defines a set of vehicle definitions and loading utilities.""" data: Dict[str, Any] @@ -105,25 +105,25 @@ class VehicleDefintions: """The path to the vehicle definitions file.""" @lru_cache(maxsize=20) - def load_vehicle_definition(self, vehicle_type: str): + def load_vehicle_definition(self, vehicle_class: str): """Loads in a particular vehicle definition.""" - if vehicle_definition_filepath := self.data.get(vehicle_type): + if vehicle_definition_filepath := self.data.get(vehicle_class): return load_yaml_config_with_substitution(Path(vehicle_definition_filepath)) raise OSError( - f"Vehicle '{vehicle_type}' is not defined in {list(self.data.keys())}" + f"Vehicle '{vehicle_class}' is not defined in {list(self.data.keys())}" ) @lru_cache(maxsize=20) - def controller_params_for_vehicle_type(self, vehicle_type: str): + def controller_params_for_vehicle_class(self, vehicle_class: str): """Get the controller parameters for the given vehicle type""" - vehicle_definition = self.load_vehicle_definition(vehicle_type) + vehicle_definition = self.load_vehicle_definition(vehicle_class) controller_params = Path(vehicle_definition["controller_params"]) return load_yaml_config_with_substitution(controller_params) @lru_cache(maxsize=20) - def chassis_params_for_vehicle_type(self, vehicle_type: str): + def chassis_params_for_vehicle_class(self, vehicle_class: str): """Get the controller parameters for the given vehicle type""" - vehicle_definition = self.load_vehicle_definition(vehicle_type) + vehicle_definition = self.load_vehicle_definition(vehicle_class) chassis_parms = Path(vehicle_definition["chassis_params"]) return load_yaml_config_with_substitution(chassis_parms) diff --git a/smarts/core/vehicle.py b/smarts/core/vehicle.py index 4770bed80f..8abfc68085 100644 --- a/smarts/core/vehicle.py +++ b/smarts/core/vehicle.py @@ -69,7 +69,8 @@ def __init__( id: str, chassis: Chassis, visual_model_filepath: Optional[str], - vehicle_config_type: str = "passenger", + vehicle_config_type: str = "sedan", + vehicle_class: str = "generic_sedan", color: Optional[SceneColors] = None, action_space=None, ): @@ -80,6 +81,7 @@ def __init__( if vehicle_config_type == "sedan": vehicle_config_type = "passenger" self._vehicle_config_type = vehicle_config_type + self._vehicle_class = vehicle_class self._action_space = action_space self._meta_create_sensor_functions() @@ -115,6 +117,7 @@ def __repr__(self): pose={self.pose}, speed={self.speed}, type={self.vehicle_type}, + class={self.vehicle_class}, w={self.width}, l={self.length}, h={self.height} @@ -250,9 +253,19 @@ def bounding_box(self) -> List[np.ndarray]: @property def vehicle_type(self) -> str: - """Get the vehicle type identifier.""" + """Get the vehicle type name as recognized by SMARTS. (e.g. 'car')""" return VEHICLE_CONFIGS[self._vehicle_config_type].vehicle_type + @property + def vehicle_config_type(self) -> str: + """Get the vehicle type identifier. (e.g. 'sedan')""" + return self._vehicle_config_type + + @property + def vehicle_class(self) -> str: + """Get the custom class of vehicle this is. (e.g. 'ford_f150')""" + return self._vehicle_class + @property def valid(self) -> bool: """Check if the vehicle still `exists` and is still operable.""" diff --git a/smarts/core/vehicle_index.py b/smarts/core/vehicle_index.py index a513b6f942..94a8a99612 100644 --- a/smarts/core/vehicle_index.py +++ b/smarts/core/vehicle_index.py @@ -118,7 +118,7 @@ def __init__(self): self._controller_states = {} # Loaded from yaml file on scenario reset - self._vehicle_definitions: resources.VehicleDefintions = {} + self._vehicle_definitions: resources.VehicleDefinitions = {} @classmethod def identity(cls): @@ -477,23 +477,20 @@ def switch_control_to_agent( vehicle = self._vehicles[vehicle_id] chassis = None if agent_interface and agent_interface.action in sim.dynamic_action_spaces: + vehicle_definition = self._vehicle_definitions.load_vehicle_definition( + agent_interface.vehicle_class + ) chassis = AckermannChassis( pose=vehicle.pose, bullet_client=sim.bc, - vehicle_filepath=self._vehicle_definitions.load_vehicle_definition( - agent_interface.vehicle_type - ).get("dynamics_model"), - tire_parameters_filepath=self._vehicle_definitions.load_vehicle_definition( - agent_interface.vehicle_type - ).get( - "tire_params" - ), + vehicle_dynamics_filepath=vehicle_definition.get("dynamics_model"), + tire_parameters_filepath=vehicle_definition.get("tire_params"), friction_map=sim.scenario.surface_patches, - controller_parameters=self._vehicle_definitions.controller_params_for_vehicle_type( - agent_interface.vehicle_type + controller_parameters=self._vehicle_definitions.controller_params_for_vehicle_class( + agent_interface.vehicle_class ), - chassis_parameters=self._vehicle_definitions.chassis_params_for_vehicle_type( - agent_interface.vehicle_type + chassis_parameters=self._vehicle_definitions.chassis_params_for_vehicle_class( + agent_interface.vehicle_class ), initial_speed=vehicle.speed, ) @@ -646,7 +643,7 @@ def _switch_control_to_agent_recreate( plan = sensor_state.get_plan(sim.road_map) vehicle_definition = self._vehicle_definitions.load_vehicle_definition( - agent_interface.vehicle_type + agent_interface.vehicle_class ) # Create a new vehicle to replace the old one new_vehicle = VehicleIndex._build_agent_vehicle( @@ -654,6 +651,7 @@ def _switch_control_to_agent_recreate( vehicle.id, agent_interface.action, vehicle_definition.get("type"), + agent_interface.vehicle_class, plan, vehicle_definition.get("dynamics_model"), vehicle_definition.get("tire_params"), @@ -705,6 +703,7 @@ def _build_agent_vehicle( vehicle_id: str, action: Optional[ActionSpaceType], vehicle_type: str, + vehicle_class: str, plan: Plan, vehicle_dynamics_filepath: Optional[str], tire_filepath: str, @@ -731,13 +730,13 @@ def _build_agent_vehicle( vehicle_color = SceneColors.Agent if trainable else SceneColors.SocialAgent controller_parameters = ( - sim.vehicle_index._vehicle_definitions.controller_params_for_vehicle_type( - vehicle_type + sim.vehicle_index._vehicle_definitions.controller_params_for_vehicle_class( + vehicle_class ) ) chassis_parameters = ( - sim.vehicle_index._vehicle_definitions.chassis_params_for_vehicle_type( - vehicle_type + sim.vehicle_index._vehicle_definitions.chassis_params_for_vehicle_class( + vehicle_class ) ) @@ -751,7 +750,7 @@ def _build_agent_vehicle( chassis = AckermannChassis( pose=start_pose, bullet_client=sim.bc, - vehicle_filepath=vehicle_dynamics_filepath, + vehicle_dynamics_filepath=vehicle_dynamics_filepath, tire_parameters_filepath=tire_filepath, friction_map=surface_patches, controller_parameters=controller_parameters, @@ -771,6 +770,7 @@ def _build_agent_vehicle( chassis=chassis, color=vehicle_color, vehicle_config_type=vehicle_type, + vehicle_class=vehicle_class, visual_model_filepath=visual_model_filepath, ) @@ -790,13 +790,14 @@ def build_agent_vehicle( ): """Build an entirely new vehicle for an agent.""" vehicle_definition = self._vehicle_definitions.load_vehicle_definition( - agent_interface.vehicle_type + agent_interface.vehicle_class ) vehicle = VehicleIndex._build_agent_vehicle( sim=sim, vehicle_id=vehicle_id or agent_id, action=agent_interface.action, vehicle_type=vehicle_definition.get("type"), + vehicle_class=agent_interface.vehicle_class, plan=plan, vehicle_dynamics_filepath=vehicle_definition.get("dynamics_model"), tire_filepath=vehicle_definition.get("tire_params"),