diff --git a/.gitignore b/.gitignore index 7b73dd0d3e..abc9cab2a9 100644 --- a/.gitignore +++ b/.gitignore @@ -120,12 +120,17 @@ build.db # run logs **/examples/**/logs +**/examples/**/videos _run_logs results supervisord.log supervisord.pid xdummy.log +# Argoverse dataset +log_map_archive_[a-z0-9-]*.json +scenario_[a-z0-9-]*.parquet + # replay data *.jsonl diff --git a/CHANGELOG.md b/CHANGELOG.md index a1a77fdce9..cc5f464db2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,9 +13,11 @@ Copy and pasting the git commit messages is __NOT__ enough. - Added an actor capture manager interface, `ActorCaptureManager`, which describes a manager that handles the change of control of actors. Operations in an actor manager step should not cause conflict in the simulation. - Added a new entry tactic, `IdEntryTactic`, which provides the scenario the ability to select a specific actor for an agent to take over. - Registered a new `chase-via-points-agent-v0` agent in agent zoo, which can effectively chase via points across different road sections by using the waypoints. +- Added new driving-smarts-v2023 benchmark consisting of new (i) driving-smarts-v2023 env and (ii) platoon-v0 env. ### Changed - The trap manager, `TrapManager`, is now a subclass of `ActorCaptureManager`. - Considering lane-change time ranges between 3s and 6s, assuming a speed of 13.89m/s, the via sensor lane acquisition range was increased from 40m to 80m, for better driving ability. +- Modified naming of benchmark used in NeurIPS 2022 from driving-smarts-competition-env to driving-smarts-v2022. ### Deprecated ### Fixed - Fixed an issue where Argoverse scenarios with a `Mission` would not run properly. diff --git a/docs/benchmarks/agent_zoo.rst b/docs/benchmarks/agent_zoo.rst index 4cf250d055..0e522025c4 100644 --- a/docs/benchmarks/agent_zoo.rst +++ b/docs/benchmarks/agent_zoo.rst @@ -50,19 +50,19 @@ Available zoo agents - Remarks * - | zoo.policies:interaction-aware-motion-prediction-agent-v0 | zoo/policies/interaction_aware_motion_prediction - - driving_smarts==0.0 + - driving_smarts_2022==0.0 - :attr:`~smarts.core.controllers.ActionSpaceType.TargetPose` - `code `__ - Contributed as part of `NeurIPS 2022 Driving SMARTS `__ competition. * - | zoo.policies:control-and-supervised-learning-agent-v0 | zoo/policies/control_and_supervised_learning - - driving_smarts==0.0 + - driving_smarts_2022==0.0 - :attr:`~smarts.core.controllers.ActionSpaceType.TargetPose` - `code `__ - Contributed as part of `NeurIPS 2022 Driving SMARTS `__ competition. * - | zoo.policies:discrete-soft-actor-critic-agent-v0 | zoo/policies/discrete_soft_actor_critic - - driving_smarts==0.0 + - driving_smarts_2022==0.0 - :attr:`~smarts.core.controllers.ActionSpaceType.TargetPose` - `code `__ - Contributed as part of `NeurIPS 2022 Driving SMARTS `__ competition. diff --git a/docs/benchmarks/benchmark.rst b/docs/benchmarks/benchmark.rst index 3e5d0ac70c..09db1c5ec6 100644 --- a/docs/benchmarks/benchmark.rst +++ b/docs/benchmarks/benchmark.rst @@ -24,8 +24,10 @@ benchmark version, then the benchmark's latest version is run by default. .. code:: bash - $ scl benchmark run driving_smarts smarts.zoo:random-relative-target-pose-agent-v0 --auto-install - Starting `Driving SMARTS V1` benchmark. + $ scl benchmark run driving_smarts_2022 smarts.zoo:random-relative-target-pose-agent-v0 --auto-install + + <-- Starting `Driving SMARTS 2022` benchmark --> + This is a cleaned up version of the Driving SMARTS benchmark. Using `TargetPose` agent action has an applied 28m/s cap for agent motion. @@ -33,19 +35,21 @@ benchmark version, then the benchmark's latest version is run by default. For history see: - https://codalab.lisn.upsaclay.fr/competitions/6618 - - https://smarts-project.github.io/archive/2022_nips_driving_smarts/ + - https://smarts-project.github.io/archive/2022_nips_driving_smarts/competition/ + Evaluating 1_to_2lane_left_turn_c... Evaluating 3lane_merge_multi_agent... ... Scoring 1_to_2lane_left_turn_c... - Evaluation complete... - `Driving SMARTS V0` result: - - completion: 1 - - humanness: 0.7 - - rules: 0.9 - - time: 0.2 - - overall: 0.504 + SCORE + {'overall': 0.424, + 'dist_to_destination': 0.925, + 'humanness': 0.769, + 'rules': 1.0, + 'time': 0.265} + + <-- Evaluation complete --> See available benchmarks ------------------------ @@ -56,7 +60,7 @@ The ``scl benchmark list`` command can be used to see the list of available benc $ scl benchmark list BENCHMARK_NAME BENCHMARK_ID VERSIONS - - Driving SMARTS: driving_smarts 0.0 0.1 + - Driving SMARTS 2022: driving_smarts_2022 0.0 0.1 Custom benchmark listing ------------------------ @@ -92,17 +96,17 @@ The benchmark listing file is organised as below. # smarts/benchmark/benchmark_listing.yaml --- benchmarks: # The root element (required) - driving_smarts: # The id of the benchmark for reference - name: "Driving SMARTS" # The human readable name of the benchmark + driving_smarts_2022: # The id of the benchmark for reference + name: "Driving SMARTS 2022" # The human readable name of the benchmark versions: # A list of benchmark versions - - # the version of the benchmark, higher is newer + # The version of the benchmark, higher is newer version: 0.0 - # the entrypoint for the benchmark, it must have `agent_config`, and `debug_log` as params + # The entrypoint for the benchmark, it must have `agent_config`, and `debug_log` as params entrypoint: "smarts.benchmark.entrypoints.benchmark_runner_v0.benchmark_from_configs" - requirements: ["ray<=2.2.0,>2.0"] # requirements to install if `--auto-install`. - params: # additional values to pass into the entrypoint as named keyword arguments. - benchmark_config: ${{smarts.benchmark.driving_smarts.v0}}/config.yaml + requirements: ["ray<=2.2.0,>2.0"] # Requirements to install if `--auto-install`. + params: # Additional values to pass into the entrypoint as named keyword arguments. + benchmark_config: ${{smarts.benchmark.driving_smarts.v2022}}/config.yaml .. note:: diff --git a/docs/benchmarks/driving_smarts_2022.rst b/docs/benchmarks/driving_smarts_2022.rst index 53c95db6ba..ea256c1c40 100644 --- a/docs/benchmarks/driving_smarts_2022.rst +++ b/docs/benchmarks/driving_smarts_2022.rst @@ -3,14 +3,14 @@ Driving SMARTS 2022 =================== -The Driving SMARTS 2022 is a benchmark derived from the +The Driving SMARTS 2022 is a benchmark used in the `NeurIPS 2022 Driving SMARTS `_ competition. Objective --------- -Objective is to develop a single policy capable of controlling single-agent or multi-agent to complete different driving scenarios in the ``driving-smarts-competition-v0`` environment. -Refer to :func:`~smarts.env.gymnasium.driving_smarts_competition_env.driving_smarts_competition_v0_env` for environment details. +Objective is to develop a single policy capable of controlling single-agent or multi-agent to complete different driving scenarios in the ``driving-smarts-v2022`` environment. +Refer to :func:`~smarts.env.gymnasium.driving_smarts_2022_env.driving_smarts_2022_env` for environment details. In each driving scenario, the ego-agents must drive towards their respective mission goal locations. Each agent's mission goal is given in the observation returned by the environment at each time step. The mission goal could be accessed as ``observation.ego_vehicle_state.mission.goal.position`` which gives an ``(x, y, z)`` map coordinate of the goal location. @@ -72,5 +72,5 @@ See the list of :ref:`available zoo agents ` which are com $ cd /SMARTS $ scl zoo install # e.g., scl zoo install zoo/policies/interaction_aware_motion_prediction - $ scl benchmark run driving_smarts==0.0 --auto_install - # e.g., scl benchmark run driving_smarts==0.0 zoo.policies:interaction-aware-motion-prediction-agent-v0 --auto-install \ No newline at end of file + $ scl benchmark run driving_smarts_2022==0.0 --auto_install + # e.g., scl benchmark run driving_smarts_2022==0.0 zoo.policies:interaction-aware-motion-prediction-agent-v0 --auto-install \ No newline at end of file diff --git a/smarts/benchmark/benchmark_listing.yaml b/smarts/benchmark/benchmark_listing.yaml index ab40af7e6f..226bee50dc 100644 --- a/smarts/benchmark/benchmark_listing.yaml +++ b/smarts/benchmark/benchmark_listing.yaml @@ -1,13 +1,43 @@ # smarts/benchmark/benchmark_listing.yaml --- benchmarks: - driving_smarts: - name: "Driving SMARTS" + + driving_smarts_2022: + name: "Driving SMARTS 2022" versions: - version: 0.0 entrypoint: "smarts.benchmark.entrypoints.benchmark_runner_v0.benchmark_from_configs" requirements: ["ray<=2.2.0,>2.0"] params: - benchmark_config: ${{smarts.benchmark.driving_smarts.v0}}/config.yaml - \ No newline at end of file + benchmark_config: ${{smarts.benchmark.driving_smarts.v2022}}/config.yaml + + driving_smarts_2023_1: + name: "Driving SMARTS 2023.1" + versions: + - + version: 0.0 + entrypoint: "smarts.benchmark.entrypoints.benchmark_runner_v0.benchmark_from_configs" + requirements: ["ray<=2.2.0,>2.0"] + params: + benchmark_config: ${{smarts.benchmark.driving_smarts.v2023}}/config_1.yaml + + driving_smarts_2023_2: + name: "Driving SMARTS 2023.2" + versions: + - + version: 0.0 + entrypoint: "smarts.benchmark.entrypoints.benchmark_runner_v0.benchmark_from_configs" + requirements: ["ray<=2.2.0,>2.0"] + params: + benchmark_config: ${{smarts.benchmark.driving_smarts.v2023}}/config_2.yaml + + driving_smarts_2023_3: + name: "Driving SMARTS 2023.3" + versions: + - + version: 0.0 + entrypoint: "smarts.benchmark.entrypoints.benchmark_runner_v0.benchmark_from_configs" + requirements: ["ray<=2.2.0,>2.0"] + params: + benchmark_config: ${{smarts.benchmark.driving_smarts.v2023}}/config_3.yaml diff --git a/smarts/benchmark/driving_smarts/v0/__init__.py b/smarts/benchmark/driving_smarts/v2022/__init__.py similarity index 100% rename from smarts/benchmark/driving_smarts/v0/__init__.py rename to smarts/benchmark/driving_smarts/v2022/__init__.py diff --git a/smarts/benchmark/driving_smarts/v0/config.yaml b/smarts/benchmark/driving_smarts/v2022/config.yaml similarity index 64% rename from smarts/benchmark/driving_smarts/v0/config.yaml rename to smarts/benchmark/driving_smarts/v2022/config.yaml index 13c24c8b24..8daf2982b2 100644 --- a/smarts/benchmark/driving_smarts/v0/config.yaml +++ b/smarts/benchmark/driving_smarts/v2022/config.yaml @@ -1,16 +1,16 @@ -# smarts/benchmark/driving_smarts_v0/config.yaml +# smarts/benchmark/driving_smarts_v2022/config.yaml --- benchmark: - name: "Driving SMARTS V0" + name: "Driving SMARTS 2022" message: | # A useful message given at the start of the benchmark. - This is a cleaned up version of the Driving SMARTS benchmark. + This is the Driving SMARTS 2022 benchmark. Using `TargetPose` agent action has an applied 28m/s cap for agent motion. Using `RelativeTargetPose` agent action, the constraint is inbuilt into the action space. For history see: - - https://codalab.lisn.upsaclay.fr/competitions/6618 - https://smarts-project.github.io/archive/2022_nips_driving_smarts/ + - https://codalab.lisn.upsaclay.fr/competitions/6618 eval_episodes: 50 debug: serial: False @@ -19,7 +19,7 @@ benchmark: headless: true envs: standard: - loc: "smarts.env:driving-smarts-competition-v0" + loc: "smarts.env:driving-smarts-v2022" scenarios: - scenarios/sumo/intersections/1_to_2lane_left_turn_c_agents_1 - scenarios/sumo/intersections/1_to_2lane_left_turn_t_agents_1 @@ -31,16 +31,16 @@ benchmark: - scenarios/sumo/straight/3lane_overtake_agents_1 kwargs: seed: 42 - # bubble: # reserved for bubble env - # loc: "smarts.env:driving-smarts-competition-bubble-env-v0" - # scenarios: - # - 6 - # - 4 - # - 42 + # bubble: + # loc: "smarts.env:driving-smarts-bubble-v2022" + # scenarios: + # - 6 + # - 4 + # - 42 # kwargs: - # naturalistic: # reserved for driving_smarts_competition_naturalistic-v0 - # loc: "smarts.env:driving_smarts_competition_naturalistic-v0" + # naturalistic: + # loc: "smarts.env:driving-smarts-naturalistic-v2022" # kwargs: # scenario_dirs: - # - "./scenarios/naturalistic/waymo" - # - "./scenarios/naturalistic/ngsim" \ No newline at end of file + # - scenarios/naturalistic/waymo + # - scenarios/naturalistic/ngsim \ No newline at end of file diff --git a/smarts/benchmark/driving_smarts/v2023/__init__.py b/smarts/benchmark/driving_smarts/v2023/__init__.py new file mode 100644 index 0000000000..84a9e0a91c --- /dev/null +++ b/smarts/benchmark/driving_smarts/v2023/__init__.py @@ -0,0 +1,24 @@ +# MIT License +# +# Copyright (C) 2023. Huawei Technologies Co., Ltd. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +from pathlib import Path + +DEFAULT_CONFIG = str((Path(__file__).parent / "config.yaml").absolute()) diff --git a/smarts/benchmark/driving_smarts/v2023/config_1.yaml b/smarts/benchmark/driving_smarts/v2023/config_1.yaml new file mode 100644 index 0000000000..9596a271b2 --- /dev/null +++ b/smarts/benchmark/driving_smarts/v2023/config_1.yaml @@ -0,0 +1,22 @@ +--- +benchmark: + name: "Driving SMARTS 2023.1" + message: | + This is the Driving SMARTS 2023.1 benchmark. + + For context see: + - https://smarts-project.github.io/competition/2023_driving_smarts/ + - https://codalab.lisn.upsaclay.fr/competitions/ + eval_episodes: 50 + debug: + serial: True + shared_env_kwargs: + seed: 42 + headless: True + envs: + standard: + loc: "smarts.env:driving-smarts-v2023" + scenarios: + - scenarios/sumo/straight/3lane_cruise_agents_1 + kwargs: + seed: 42 diff --git a/smarts/benchmark/driving_smarts/v2023/config_2.yaml b/smarts/benchmark/driving_smarts/v2023/config_2.yaml new file mode 100644 index 0000000000..ed9d023f6d --- /dev/null +++ b/smarts/benchmark/driving_smarts/v2023/config_2.yaml @@ -0,0 +1,22 @@ +--- +benchmark: + name: "Driving SMARTS 2023.2" + message: | + This is the Driving SMARTS 2023.2 benchmark. + + For context see: + - https://smarts-project.github.io/competition/2023_driving_smarts/ + - https://codalab.lisn.upsaclay.fr/competitions/ + eval_episodes: 50 + debug: + serial: True + shared_env_kwargs: + seed: 42 + headless: True + envs: + turns: + loc: "smarts.env:driving-smarts-v2023" + scenarios: + - scenarios/sumo/intersections/1_to_2lane_left_turn_c_agents_1 + kwargs: + seed: 42 diff --git a/smarts/benchmark/driving_smarts/v2023/config_3.yaml b/smarts/benchmark/driving_smarts/v2023/config_3.yaml new file mode 100644 index 0000000000..b14320d850 --- /dev/null +++ b/smarts/benchmark/driving_smarts/v2023/config_3.yaml @@ -0,0 +1,23 @@ +--- +benchmark: + name: "Driving SMARTS 2023.3" + message: | + This is the Driving SMARTS 2023.3 benchmark. + + For context see: + - https://smarts-project.github.io/competition/2023_driving_smarts/ + - https://codalab.lisn.upsaclay.fr/competitions/ + eval_episodes: 50 + debug: + serial: True + shared_env_kwargs: + seed: 42 + headless: True + envs: + platoon: + loc: "smarts.env:platoon" + scenarios: + - scenarios/sumo/platoon/straight_sumo_t_agents_1 + kwargs: + seed: 42 + # metric_formula: smarts/benchmark/driving_smarts/v2023/metric_formula_platoon.py diff --git a/smarts/core/external_provider.py b/smarts/core/external_provider.py index f725f84cc8..982ff9a5a7 100644 --- a/smarts/core/external_provider.py +++ b/smarts/core/external_provider.py @@ -20,12 +20,9 @@ import weakref from typing import Iterable, List, Sequence, Set -import numpy as np - from .actor import ActorRole from .controllers import ActionSpaceType from .provider import Provider, ProviderManager, ProviderRecoveryFlags, ProviderState -from .road_map import RoadMap from .scenario import Scenario from .utils.file import replace from .vehicle import VehicleState diff --git a/smarts/core/plan.py b/smarts/core/plan.py index 5b0fd8085a..00a28aa782 100644 --- a/smarts/core/plan.py +++ b/smarts/core/plan.py @@ -330,7 +330,7 @@ def create_route(self, mission: Mission, radius: Optional[float] = None): mission. Defaults to `_default_lane_width` of the underlying road_map. """ - assert not self._route, "already called create_route()" + assert not self._route, "Already called create_route()." self._mission = mission or Mission.random_endless_mission(self._road_map) if not self._mission.requires_route: diff --git a/smarts/core/smarts.py b/smarts/core/smarts.py index b53e87dea7..8cf7c23f0e 100644 --- a/smarts/core/smarts.py +++ b/smarts/core/smarts.py @@ -55,7 +55,6 @@ from .scenario import Mission, Scenario from .signal_provider import SignalProvider from .signals import SignalLightState, SignalState -from .sumo_traffic_simulation import SumoTrafficSimulation from .traffic_history_provider import TrafficHistoryProvider from .traffic_provider import TrafficProvider from .trap_manager import TrapManager diff --git a/smarts/core/traffic_provider.py b/smarts/core/traffic_provider.py index 0b09119da6..eef2560acb 100644 --- a/smarts/core/traffic_provider.py +++ b/smarts/core/traffic_provider.py @@ -19,7 +19,7 @@ # THE SOFTWARE. -from typing import Optional, Sequence +from typing import Optional from shapely.geometry import Polygon diff --git a/smarts/env/gymnasium/__init__.py b/smarts/env/gymnasium/__init__.py index a766a429b4..78633a58d6 100644 --- a/smarts/env/gymnasium/__init__.py +++ b/smarts/env/gymnasium/__init__.py @@ -28,10 +28,24 @@ ) register( - id="driving-smarts-competition-v0", - entry_point="smarts.env.gymnasium.driving_smarts_competition_env:driving_smarts_competition_v0_env", + id="driving-smarts-v2022", + entry_point="smarts.env.gymnasium.driving_smarts_2022_env:driving_smarts_2022_env", disable_env_checker=True, ) + + register( + id="platoon-v0", + entry_point="smarts.env.gymnasium.platoon_env:platoon_env", + disable_env_checker=True, + ) + + register( + id="driving-smarts-v2023", + entry_point="smarts.env.gymnasium.driving_smarts_2023_env:driving_smarts_2023_env", + disable_env_checker=True, + ) + + except ModuleNotFoundError: import warnings diff --git a/smarts/env/gymnasium/driving_smarts_competition_env.py b/smarts/env/gymnasium/driving_smarts_2022_env.py similarity index 93% rename from smarts/env/gymnasium/driving_smarts_competition_env.py rename to smarts/env/gymnasium/driving_smarts_2022_env.py index 41ed666cf5..e3e15118cd 100644 --- a/smarts/env/gymnasium/driving_smarts_competition_env.py +++ b/smarts/env/gymnasium/driving_smarts_2022_env.py @@ -21,8 +21,6 @@ import copy import logging import math -import os -import pathlib from functools import partial from typing import Any, Dict, Optional, Tuple @@ -40,6 +38,7 @@ from smarts.core.controllers import ActionSpaceType from smarts.env.gymnasium.hiway_env_v1 import HiWayEnvV1, SumoOptions from smarts.env.utils.observation_conversion import ObservationOptions +from smarts.env.utils.scenario import get_scenario_specs from smarts.sstudio.scenario_construction import build_scenario logger = logging.getLogger(__file__) @@ -52,7 +51,7 @@ MAXIMUM_SPEED_MPS = 28 # 28m/s = 100.8 km/h. This is a safe maximum speed. -def driving_smarts_competition_v0_env( +def driving_smarts_2022_env( scenario: str, agent_interface: AgentInterface, headless: bool = True, @@ -122,7 +121,7 @@ def driving_smarts_competition_v0_env( An environment described by the input argument `scenario`. """ - env_specs = _get_env_specs(scenario) + env_specs = get_scenario_specs(scenario) build_scenario(scenario=env_specs["scenario"]) resolved_agent_interface = resolve_agent_interface(agent_interface) @@ -161,36 +160,6 @@ def driving_smarts_competition_v0_env( return env -def _get_env_specs(scenario: str): - """Returns the appropriate environment parameters for each scenario. - - Args: - scenario (str): Scenario - - Returns: - Dict[str, Any]: A parameter dictionary. - """ - - if os.path.isdir(scenario): - import re - - regexp_agent = re.compile(r"agents_\d+") - regexp_num = re.compile(r"\d+") - matches_agent = regexp_agent.search(scenario) - if not matches_agent: - raise Exception( - f"Scenario path should match regexp of 'agents_\\d+', but got {scenario}" - ) - num_agent = regexp_num.search(matches_agent.group(0)) - - return { - "scenario": str(scenario), - "num_agent": int(num_agent.group(0)), - } - else: - raise Exception(f"Unknown scenario {scenario}.") - - def resolve_agent_action_space(agent_interface: AgentInterface): """Get the competition action space for the given agent interface.""" assert ( diff --git a/smarts/env/gymnasium/driving_smarts_2023_env.py b/smarts/env/gymnasium/driving_smarts_2023_env.py new file mode 100644 index 0000000000..562d747f7f --- /dev/null +++ b/smarts/env/gymnasium/driving_smarts_2023_env.py @@ -0,0 +1,176 @@ +# Copyright (C) 2023. Huawei Technologies Co., Ltd. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import logging +from functools import partial +from typing import Optional + +from envision.client import Client as Envision +from envision.client import EnvisionDataFormatterArgs +from smarts.core.agent_interface import ( + AgentInterface, + DoneCriteria, + NeighborhoodVehicles, + Waypoints, +) +from smarts.core.controllers import ActionSpaceType +from smarts.env.gymnasium.hiway_env_v1 import HiWayEnvV1, SumoOptions +from smarts.env.utils.observation_conversion import ObservationOptions +from smarts.env.utils.scenario import get_scenario_specs +from smarts.sstudio.scenario_construction import build_scenario + +logger = logging.getLogger(__file__) +logger.setLevel(logging.WARNING) + +SUPPORTED_ACTION_TYPES = (ActionSpaceType.Continuous,) + + +def driving_smarts_2023_env( + scenario: str, + agent_interface: AgentInterface, + seed: int = 42, + headless: bool = True, + visdom: bool = False, + sumo_headless: bool = True, + envision_record_data_replay_path: Optional[str] = None, +): + """An environment with a mission to be completed by a single or multiple + ego agents. Episode ends when ego agents reach their respective destinations + specified by their mission goals. + + Observation space for each agent: + Formatted :class:`~smarts.core.observations.Observation` using + :attr:`~smarts.env.utils.observation_conversion.ObservationOptions.multi_agent` + option is returned as observation. See + :class:`~smarts.env.utils.observation_conversion.ObservationSpacesFormatter` for + a sample formatted observation data structure. + + Action space for each agent: + Action space for each agent is :attr:`~smarts.core.controllers.ActionSpaceType.Continuous`. + + Agent interface: + Using the input argument agent_interface, users may configure all the fields of + :class:`~smarts.core.agent_interface.AgentInterface` except for the (i) accelerometer, + (ii) done_criteria, (iii) max_episode_steps, (iv) neighborhood_vehicle_states, and + (v) waypoint_paths. + + Reward: + Default reward is distance travelled (in meters) in each step, including the termination step. + + Episode termination: + Episode is terminated if any of the following occurs. + + 1. Egos reach their respective destinations specified by their mission goals. + 2. Steps per episode exceed 1000. + 3. Agent collides or drives off road. + + Args: + scenario (str): Scenario name or path to scenario folder. + agent_interface (AgentInterface): Agent interface specification. + seed (int, optional): Random number generator seed. Defaults to 42. + headless (bool, optional): If True, disables visualization in + Envision. Defaults to False. + visdom (bool, optional): If True, enables visualization of observed + RGB images in Visdom. Defaults to False. + sumo_headless (bool, optional): If True, disables visualization in + SUMO GUI. Defaults to True. + envision_record_data_replay_path (Optional[str], optional): + Envision's data replay output directory. Defaults to None. + + Returns: + An environment described by the input argument `scenario`. + """ + + # Check for supported action space + assert agent_interface.action in SUPPORTED_ACTION_TYPES, ( + f"Got unsupported action type `{agent_interface.action}`, which is not " + f"in supported action types `{SUPPORTED_ACTION_TYPES}`." + ) + + # Build scenario + env_specs = get_scenario_specs(scenario) + build_scenario(scenario=env_specs["scenario"]) + + # Resolve agent interface + resolved_agent_interface = resolve_agent_interface(agent_interface) + agent_interfaces = { + f"Agent_{i}": resolved_agent_interface for i in range(env_specs["num_agent"]) + } + + visualization_client_builder = None + if not headless: + visualization_client_builder = partial( + Envision, + endpoint=None, + output_dir=envision_record_data_replay_path, + headless=headless, + data_formatter_args=EnvisionDataFormatterArgs( + "base", enable_reduction=False + ), + ) + + env = HiWayEnvV1( + scenarios=[env_specs["scenario"]], + agent_interfaces=agent_interfaces, + sim_name="Driving_Smarts_2023", + headless=headless, + visdom=visdom, + seed=seed, + sumo_options=SumoOptions(headless=sumo_headless), + visualization_client_builder=visualization_client_builder, + observation_options=ObservationOptions.multi_agent, + ) + + return env + + +def resolve_agent_interface(agent_interface: AgentInterface): + """Resolve the agent interface for a given environment. Some interface + values can be configured by the user, but others are pre-determined and + fixed. + """ + + done_criteria = DoneCriteria( + collision=True, + off_road=True, + off_route=False, + on_shoulder=False, + wrong_way=False, + not_moving=False, + agents_alive=None, + ) + max_episode_steps = 1000 + waypoints_lookahead = 80 + neighborhood_radius = 50 + return AgentInterface( + accelerometer=True, + action=agent_interface.action, + done_criteria=done_criteria, + drivable_area_grid_map=agent_interface.drivable_area_grid_map, + lane_positions=agent_interface.lane_positions, + lidar_point_cloud=agent_interface.lidar_point_cloud, + max_episode_steps=max_episode_steps, + neighborhood_vehicle_states=NeighborhoodVehicles(radius=neighborhood_radius), + occupancy_grid_map=agent_interface.occupancy_grid_map, + top_down_rgb=agent_interface.top_down_rgb, + road_waypoints=agent_interface.road_waypoints, + waypoint_paths=Waypoints(lookahead=waypoints_lookahead), + signals=agent_interface.signals, + ) diff --git a/smarts/env/gymnasium/platoon_env.py b/smarts/env/gymnasium/platoon_env.py new file mode 100644 index 0000000000..02194f4db5 --- /dev/null +++ b/smarts/env/gymnasium/platoon_env.py @@ -0,0 +1,177 @@ +# Copyright (C) 2023. Huawei Technologies Co., Ltd. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import logging +from functools import partial +from typing import Optional + +from envision.client import Client as Envision +from envision.client import EnvisionDataFormatterArgs +from smarts.core.agent_interface import ( + ActorsAliveDoneCriteria, + AgentInterface, + DoneCriteria, + NeighborhoodVehicles, + Waypoints, +) +from smarts.core.controllers import ActionSpaceType +from smarts.env.gymnasium.hiway_env_v1 import HiWayEnvV1, SumoOptions +from smarts.env.utils.observation_conversion import ObservationOptions +from smarts.env.utils.scenario import get_scenario_specs +from smarts.sstudio.scenario_construction import build_scenario + +logger = logging.getLogger(__file__) +logger.setLevel(logging.WARNING) + +SUPPORTED_ACTION_TYPES = (ActionSpaceType.Continuous,) + + +def platoon_env( + scenario: str, + agent_interface: AgentInterface, + seed: int = 42, + headless: bool = True, + visdom: bool = False, + sumo_headless: bool = True, + envision_record_data_replay_path: Optional[str] = None, +): + """Each ego is supposed to track and follow its specified leader (i.e., lead + vehicle) in a single file or in a platoon fashion. Name of the lead vehicle + to be followed is given to the ego through its + `agent_interface.done_criteria.actors_alive.actors_of_interest` attribute. + The episode ends for an ego when its assigned leader reaches the leader's + destination. Egos do not have prior knowledge of the leader's destination. + + Observation space for each agent: + Formatted :class:`~smarts.core.observations.Observation` using + :attr:`~smarts.env.utils.observation_conversion.ObservationOptions.multi_agent` + option is returned as observation. See + :class:`~smarts.env.utils.observation_conversion.ObservationSpacesFormatter` for + a sample formatted observation data structure. + + Action space for each agent: + Action space for each agent is :attr:`~smarts.core.controllers.ActionSpaceType.Continuous`. + + Reward: + Default reward is distance travelled (in meters) in each step, including the termination step. + + Episode termination: + Episode is terminated if any of the following occurs. + + 1. Lead vehicle reaches its pre-programmed destination. + 2. Steps per episode exceed 1000. + 3. Agent collides or drives off road. + + Args: + scenario (str): Scenario name or path to scenario folder. + agent_interface (AgentInterface): Agent interface specification. + seed (int, optional): Random number generator seed. Defaults to 42. + headless (bool, optional): If True, disables visualization in + Envision. Defaults to False. + visdom (bool, optional): If True, enables visualization of observed + RGB images in Visdom. Defaults to False. + sumo_headless (bool, optional): If True, disables visualization in + SUMO GUI. Defaults to True. + envision_record_data_replay_path (Optional[str], optional): + Envision's data replay output directory. Defaults to None. + + Returns: + An environment described by the input argument `scenario`. + """ + + # Check for supported action space + assert agent_interface.action in SUPPORTED_ACTION_TYPES, ( + f"Got unsupported action type `{agent_interface.action}`, which is not " + f"in supported action types `{SUPPORTED_ACTION_TYPES}`." + ) + + # Build scenario + env_specs = get_scenario_specs(scenario) + build_scenario(scenario=env_specs["scenario"]) + + # Resolve agent interface + resolved_agent_interface = resolve_agent_interface(agent_interface) + agent_interfaces = { + f"Agent_{i}": resolved_agent_interface for i in range(env_specs["num_agent"]) + } + + visualization_client_builder = None + if not headless: + visualization_client_builder = partial( + Envision, + endpoint=None, + output_dir=envision_record_data_replay_path, + headless=headless, + data_formatter_args=EnvisionDataFormatterArgs( + "base", enable_reduction=False + ), + ) + + env = HiWayEnvV1( + scenarios=[env_specs["scenario"]], + agent_interfaces=agent_interfaces, + sim_name="Platoon", + headless=headless, + visdom=visdom, + seed=seed, + sumo_options=SumoOptions(headless=sumo_headless), + visualization_client_builder=visualization_client_builder, + observation_options=ObservationOptions.multi_agent, + ) + + return env + + +def resolve_agent_interface(agent_interface: AgentInterface): + """Resolve the agent interface for a given environment. Some interface + values can be configured by the user, but others are pre-determined and + fixed. + """ + + done_criteria = DoneCriteria( + collision=True, + off_road=True, + off_route=False, + on_shoulder=False, + wrong_way=False, + not_moving=False, + actors_alive=ActorsAliveDoneCriteria( + actors_of_interest=("Leader-007",), + strict=True, + ), + ) + max_episode_steps = 1000 + waypoints_lookahead = 80 + neighborhood_radius = 50 + return AgentInterface( + accelerometer=True, + action=agent_interface.action, + done_criteria=done_criteria, + drivable_area_grid_map=agent_interface.drivable_area_grid_map, + lane_positions=agent_interface.lane_positions, + lidar_point_cloud=agent_interface.lidar_point_cloud, + max_episode_steps=max_episode_steps, + neighborhood_vehicle_states=NeighborhoodVehicles(radius=neighborhood_radius), + occupancy_grid_map=agent_interface.occupancy_grid_map, + top_down_rgb=agent_interface.top_down_rgb, + road_waypoints=agent_interface.road_waypoints, + waypoint_paths=Waypoints(lookahead=waypoints_lookahead), + signals=agent_interface.signals, + ) diff --git a/smarts/env/tests/test_hiway_env_v1.py b/smarts/env/tests/test_hiway_env_v1.py index c6d24a6784..31cd1b7ee0 100644 --- a/smarts/env/tests/test_hiway_env_v1.py +++ b/smarts/env/tests/test_hiway_env_v1.py @@ -104,6 +104,6 @@ def test_hiway_env_v1_reset_with_scenario(env: HiWayEnvV1): scenario: Scenario = next(Scenario.scenario_variations(scenarios, [AGENT_ID])) env.reset(options={"scenario": scenario, "start_time": 1000}) - assert "figure_eight" in env.scenario.root_filepath + assert "figure_eight" in env.smarts.scenario.root_filepath assert env.smarts.elapsed_sim_time >= 1000 env.step({AGENT_ID: "keep_lane"}) diff --git a/smarts/env/utils/observation_conversion.py b/smarts/env/utils/observation_conversion.py index 8a64595dd0..c03ea862fb 100644 --- a/smarts/env/utils/observation_conversion.py +++ b/smarts/env/utils/observation_conversion.py @@ -29,14 +29,13 @@ from cached_property import cached_property from smarts.core.agent_interface import AgentInterface -from smarts.core.events import Events from smarts.core.observations import Observation, SignalObservation, VehicleObservation from smarts.core.plan import Mission from smarts.core.road_map import Waypoint _LIDAR_SHP = 300 _NEIGHBOR_SHP = 10 -_WAYPOINT_SHP = (4, 20) +_WAYPOINT_SHP = (12, 80) _SIGNALS_SHP = (3,) _POSITION_SHP = (3,) _WAYPOINT_NAME_LIMIT = 50 @@ -945,7 +944,7 @@ class ObservationSpacesFormatter: AgentInterface, else absent. shape=(3,). dtype=np.float32. "linear_velocity": Vehicle velocity in x, y, and z axes. shape=(3,). dtype=np.float32. - "pos": + "position": Coordinate of the center of the vehicle bounding box's bottom plane. shape=(3,). dtype=np.float64. "speed": @@ -959,6 +958,8 @@ class ObservationSpacesFormatter: A dictionary of event markers. "events": dict({ + "actors_alive_done": + 1 if `DoneCriteria.actors_alive` is triggered, else 0. "agents_alive_done": 1 if `DoneCriteria.agents_alive` is triggered, else 0. "collisions": @@ -997,7 +998,7 @@ class ObservationSpacesFormatter: Mission details for the ego agent. "mission": dict({ - "goal_pos": + "goal_position": Achieve goal by reaching the end position. Defaults to np.array([0,0,0]) for no mission. shape=(3,). dtype=np.float64. }) diff --git a/smarts/env/utils/scenario.py b/smarts/env/utils/scenario.py new file mode 100644 index 0000000000..749c887bdc --- /dev/null +++ b/smarts/env/utils/scenario.py @@ -0,0 +1,53 @@ +# MIT License + +# Copyright (C) 2023. Huawei Technologies Co., Ltd. All rights reserved. + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import os + + +def get_scenario_specs(scenario: str): + """Returns the appropriate scenario specification. + + Args: + scenario (str): Scenario + + Returns: + Dict[str, Any]: A parameter dictionary. + """ + + if os.path.isdir(scenario): + import re + + regexp_agent = re.compile(r"agents_\d+") + regexp_num = re.compile(r"\d+") + matches_agent = regexp_agent.search(scenario) + if not matches_agent: + raise Exception( + f"Scenario path should match regexp of 'agents_\\d+', but got {scenario}" + ) + num_agent = regexp_num.search(matches_agent.group(0)) + + return { + "scenario": str(scenario), + "num_agent": int(num_agent.group(0)), + } + else: + raise Exception(f"Unknown scenario {scenario}.") diff --git a/smarts/zoo/registry.py b/smarts/zoo/registry.py index 497298d56d..2ba3c053bb 100644 --- a/smarts/zoo/registry.py +++ b/smarts/zoo/registry.py @@ -88,6 +88,6 @@ def make_agent(locator: str, **kwargs): Additional arguments to be passed to the constructed class. """ - agent_spec = make(locator, kwargs=kwargs) + agent_spec = make(locator, **kwargs) return agent_spec.build_agent()