Configuration

Click and Open In Colab

A MetaDrive instance accepts a dict as the environmental config. For example, you can build a MetaDrive instance with 200 generated maps via

from metadrive import MetaDriveEnv
config = dict(num_scenarios=200, start_seed=0)
env = MetaDriveEnv(config)

In this page, we describe the details of the config system and configurable options for all environments.

Config system

This section discusses how to configure the an environment in MetaDrive and some features of the config system.

Overwriting

Every environment has a default config, which records the parameters required to launch the environment. It is content is actually a nested dictionary whose keys and values represent the parameter names and corresponding values. This default config dict can be accessed via the class method:

default_config = MetaDriveEnv.default_config()

When creating environments, the external config config will overwritten default values of certain fields in the default_config. The following code exemplifies this.

from metadrive import MetaDriveEnv
default_config = MetaDriveEnv.default_config()
env = MetaDriveEnv(dict(num_scenarios=100, log_level=50))
env_config = env.config
print("default_config['num_scenarios']:", default_config["num_scenarios"])
print("env_config['num_scenarios']:", env_config["num_scenarios"])
default_config['num_scenarios']: 1
env_config['num_scenarios']: 100

Sanity Check

There is a check mechanism which prohibit users to set the value for a key that doesn’t exist in the default_config. This is helpful to make sure that users type the correct parameter name and successfully config the target parameter.

try:
    env = MetaDriveEnv(dict(non_exist_key=False))
except KeyError as error:
    print(str(error)[:62] + " ...")
"'{'non_exist_key'}' does not exist in existing config. Please ...

The check mechanism will further ensure if the type of the parameter is correct. For example, the num_scenarios should be an int type, and thus a list type parameter will raise an error.

try:
    env = MetaDriveEnv(dict(num_scenarios=[0, 1]))
except AssertionError as error:
    print(str(error)[:62] + " ...")
TypeError: num_scenarios:[0, 1] ...

Basic Config Sharing

The default configs are different across all environments, but may share some identical fields. Take the MetaDriveEnv and ScenarioEnv as example.

from metadrive.envs import MetaDriveEnv, ScenarioEnv
metadrive_config = set(MetaDriveEnv.default_config().keys())
scenario_config = set(ScenarioEnv.default_config().keys())
print("Number of parameters of MetaDriveEnv: {}".format(len(metadrive_config)))
print("Number of parameters of ScenarioEnv: {}\n".format(len(scenario_config)))

try:
    assert metadrive_config == scenario_config
except AssertionError as error:
    print("The config between MetaDriveEnv and ScenarioEnv is different.\n")
    
identical_parameters = scenario_config.intersection(metadrive_config)
print("Number of identical parameters: \
      {}".format(len(identical_parameters)))
print("Number of unique parameters in MetaDriveEnv: \
      {}".format(len(metadrive_config-identical_parameters)))
print("Number of unique parameters in ScenarioEnv: \
      {}".format(len(scenario_config-identical_parameters)))
Number of parameters of MetaDriveEnv: 110
Number of parameters of ScenarioEnv: 122

The config between MetaDriveEnv and ScenarioEnv is different.

Number of identical parameters:       92
Number of unique parameters in MetaDriveEnv:       18
Number of unique parameters in ScenarioEnv:       30

It is worth mentioning the parameter sharing mechanism, which is helpful when we create a new environment, so we don’t need to copy some common configs to the default_config to the new environments again and again. Let’s first check out how the default_config() function is implemented.

from metadrive.utils import print_source
print_source(ScenarioEnv.default_config)
@classmethod
def default_config(cls):
    config = super(ScenarioEnv, cls).default_config()
    config.update(SCENARIO_ENV_CONFIG)
    return config

It is quite simple and is implemented by overwriting the super(ScenarioEnv, cls).default_config() with Scenario_ENV_CONFIG. If we check the contents of the two config dict, we will find that the BaseEnv.default_config() = super(ScenarioEnv, cls).default_config() is the subset of ScenarioEnv.default_config() and provides the ScenarioEnv with the basic configs.

from metadrive.envs.base_env import BaseEnv
set(BaseEnv.default_config()).issubset(set(ScenarioEnv.default_config()))
True

It is the same for the MetaDriveEnv as well, whose default config is:

print_source(MetaDriveEnv.default_config)
@classmethod
def default_config(cls) -> Config:
    config = super(MetaDriveEnv, cls).default_config()
    config.update(METADRIVE_DEFAULT_CONFIG)
    config.register_type("map", str, int)
    config["map_config"].register_type("config", None)
    return config

As there is an overwriting function is called, it is ok to overwrite the values of parameters in BaseEnv.default_config() when making the default_config for a inherited environment. The following code shows that the config show_sidewalk is True in BaseEnv but is overwritten to False in ScenarioEnv because of the SCENARIO_ENV_CONFIG.

from metadrive.envs.scenario_env import SCENARIO_ENV_CONFIG

assert BaseEnv.default_config()["show_sidewalk"]
assert not ScenarioEnv.default_config()["show_sidewalk"]
assert not SCENARIO_ENV_CONFIG["show_sidewalk"]

Programming with Configs

The configs can be accessed everywhere in the program just like the simulation engine instance, so we can use these parameters to adjust the behavior of the simulation. A tutorial of accessing configs when programming new environments is available at config.

Basic Configs

As all environments are subclass of BaseEnv and share the parameters of BaseEnv, we first discuss the parameters in BaseEnv.default_config(). The available items with annotations are listed as follows. You can check this in the source code as well.

import metadrive.envs.base_env as base_env
from metadrive.utils import print_source, CONFIG
module_source = print_source(base_env, ["BASE_DEFAULT_CONFIG", ")\n\n"], colorscheme=CONFIG)
BASE_DEFAULT_CONFIG = dict(

    # ===== agent =====
    # Whether randomize the car model for the agent, randomly choosing from 4 types of cars
    random_agent_model=False,
    # The ego config is: env_config["vehicle_config"].update(env_config"[agent_configs"]["default_agent"])
    agent_configs={DEFAULT_AGENT: dict(use_special_color=True, spawn_lane_index=None)},

    # ===== multi-agent =====
    # This should be >1 in MARL envs, or set to -1 for spawning as many vehicles as possible.
    num_agents=1,
    # Turn on this to notify the simulator that it is MARL env
    is_multi_agent=False,
    # The number of agent will be fixed adn determined at the start of the episode, if set to False
    allow_respawn=False,
    # How many substeps for the agent to stay static at the death place after done. (Default for MARL: 25)
    delay_done=0,

    # ===== Action/Control =====
    # Please see Documentation: Action and Policy for more details
    # What policy to use for controlling agents
    agent_policy=EnvInputPolicy,
    # If set to True, agent_policy will be overriden and change to ManualControlPolicy
    manual_control=False,
    # What interfaces to use for manual control, options: "steering_wheel" or "keyboard" or "xbos"
    controller="keyboard",
    # Used with EnvInputPolicy. If set to True, the env.action_space will be discrete
    discrete_action=False,
    # If True, use MultiDiscrete action space. Otherwise, use Discrete.
    use_multi_discrete=False,
    # How many discrete actions are used for steering dim
    discrete_steering_dim=5,
    # How many discrete actions are used for throttle/brake dim
    discrete_throttle_dim=5,
    # Check if the action is contained in gym.space. Usually turned off to speed up simulation
    action_check=False,

    # ===== Observation =====
    # Please see Documentation: Observation for more details
    # Whether to normalize the pixel value from 0-255 to 0-1
    norm_pixel=True,
    # The number of timesteps for stacking image observation
    stack_size=3,
    # Whether to use image observation or lidar. It takes effect in get_single_observation
    image_observation=False,
    # Like agent_policy, users can use customized observation class through this field
    agent_observation=None,

    # ===== Termination =====
    # The maximum length of each agent episode. Set to None to remove this constraint
    horizon=None,
    # If set to True, the terminated will be True as well when the length of agent episode exceeds horizon
    truncate_as_terminate=False,

    # ===== Main Camera =====
    # A True value makes the camera follow the reference line instead of the vehicle, making its movement smooth
    use_chase_camera_follow_lane=False,
    # Height of the main camera
    camera_height=2.2,
    # Distance between the camera and the vehicle. It is the distance projecting to the x-y plane.
    camera_dist=7.5,
    # Pitch of main camera. If None, this will be automatically calculated
    camera_pitch=None,  # degree
    # Smooth the camera movement
    camera_smooth=True,
    # How many frames used to smooth the camera
    camera_smooth_buffer_size=20,
    # FOV of main camera
    camera_fov=65,
    # Only available in MARL setting, choosing which agent to track. Values should be "agent0", "agent1" or so on
    prefer_track_agent=None,
    # Setting the camera position for the Top-down Camera for 3D viewer (pressing key "B" to activate it)
    top_down_camera_initial_x=0,
    top_down_camera_initial_y=0,
    top_down_camera_initial_z=200,

    # ===== Vehicle =====
    vehicle_config=dict(
        # Vehicle model. Candidates: "s", "m", "l", "xl", "default". random_agent_model makes this config invalid
        vehicle_model="default",
        # If set to True, the vehicle can go backwards with throttle/brake < -1
        enable_reverse=False,
        # Whether to show the box as navigation points
        show_navi_mark=True,
        # Whether to show a box mark at the destination
        show_dest_mark=False,
        # Whether to draw a line from current vehicle position to the designation point
        show_line_to_dest=False,
        # Whether to draw a line from current vehicle position to the next navigation point
        show_line_to_navi_mark=False,
        # If set to True, the vehicle will be in color green in top-down renderer or MARL setting
        use_special_color=False,
        # Clear wheel friction, so it can not move by setting steering and throttle/brake. Used for ReplayPolicy
        no_wheel_friction=False,

        # ===== image capturing =====
        # Which camera to use for image observation. It should be a sensor registered in sensor config.
        image_source="rgb_camera",

        # ===== vehicle spawn and navigation =====
        # A BaseNavigation instance. It should match the road network type.
        navigation_module=None,
        # A lane id specifies which lane to spawn this vehicle
        spawn_lane_index=None,
        # destination lane id. Required only when navigation module is not None.
        destination=None,
        # the longitudinal and lateral position on the spawn lane
        spawn_longitude=5.0,
        spawn_lateral=0.0,

        # If the following items is assigned, the vehicle will be spawn at the specified position with certain speed
        spawn_position_heading=None,
        spawn_velocity=None,  # m/s
        spawn_velocity_car_frame=False,

        # ==== others ====
        # How many cars the vehicle has overtaken. It is deprecated due to bug.
        overtake_stat=False,
        # If set to True, the default texture for the vehicle will be replaced with a pure color one.
        random_color=False,
        # The shape of vehicle are predefined by its class. But in special scenario (WaymoVehicle) we might want to
        # set to arbitrary shape.
        width=None,
        length=None,
        height=None,
        mass=None,

        # Set the vehicle size only for pygame top-down renderer. It doesn't affect the physical size!
        top_down_width=None,
        top_down_length=None,

        # ===== vehicle module config =====
        lidar=dict(
            num_lasers=240, distance=50, num_others=0, gaussian_noise=0.0, dropout_prob=0.0, add_others_navi=False
        ),
        side_detector=dict(num_lasers=0, distance=50, gaussian_noise=0.0, dropout_prob=0.0),
        lane_line_detector=dict(num_lasers=0, distance=20, gaussian_noise=0.0, dropout_prob=0.0),
        show_lidar=False,
        show_side_detector=False,
        show_lane_line_detector=False,
        # Whether to turn on vehicle light, only available when enabling render-pipeline
        light=False,
    ),

    # ===== Sensors =====
    sensors=dict(lidar=(Lidar, ), side_detector=(SideDetector, ), lane_line_detector=(LaneLineDetector, )),

    # ===== Engine Core config =====
    # If true pop a window to render
    use_render=False,
    # (width, height), if set to None, it will be automatically determined
    window_size=(1200, 900),
    # Physics world step is 0.02s and will be repeated for decision_repeat times per env.step()
    physics_world_step_size=2e-2,
    decision_repeat=5,
    # This is an advanced feature for accessing image without moving them to ram!
    image_on_cuda=False,
    # Don't set this config. We will determine the render mode automatically, it runs at physics-only mode by default.
    _render_mode=RENDER_MODE_NONE,
    # If set to None: the program will run as fast as possible. Otherwise, the fps will be limited under this value
    force_render_fps=None,
    # We will maintain a set of buffers in the engine to store the used objects and can reuse them when possible
    # enhancing the efficiency. If set to True, all objects will be force destroyed when call clear()
    force_destroy=False,
    # Number of buffering objects for each class.
    num_buffering_objects=200,
    # Turn on it to use render pipeline, which provides advanced rendering effects (Beta)
    render_pipeline=False,
    # daytime is only available when using render-pipeline
    daytime="19:00",  # use string like "13:40", We usually set this by editor in toolkit
    # Shadow range, unit: [m]
    shadow_range=50,
    # Whether to use multi-thread rendering
    multi_thread_render=True,
    multi_thread_render_mode="Cull",  # or "Cull/Draw"
    # Model loading optimization. Preload pedestrian for avoiding lagging when creating it for the first time
    preload_models=True,
    # model compression increasing the launch time
    disable_model_compression=True,

    # ===== Terrain =====
    # The size of the square map region, which is centered at [0, 0]. The map objects outside it are culled.
    map_region_size=1024,
    # Whether to remove lanes outside the map region. If True, lane localization only applies to map region
    cull_lanes_outside_map=False,
    # Road will have a flat marin whose width is determined by this value, unit: [m]
    drivable_area_extension=7,
    # Height scale for mountains, unit: [m]. 0 height makes the terrain flat
    height_scale=50,
    # If using mesh collision, mountains will have physics body and thus interact with vehicles.
    use_mesh_terrain=False,
    # If set to False, only the center region of the terrain has the physics body
    full_size_mesh=True,
    # Whether to show crosswalk
    show_crosswalk=True,
    # Whether to show sidewalk
    show_sidewalk=True,

    # ===== Debug =====
    # Please see Documentation: Debug for more details
    pstats=False,  # turn on to profile the efficiency
    debug=False,  # debug, output more messages
    debug_panda3d=False,  # debug panda3d
    debug_physics_world=False,  # only render physics world without model, a special debug option
    debug_static_world=False,  # debug static world
    log_level=logging.INFO,  # log level. logging.DEBUG/logging.CRITICAL or so on
    show_coordinates=False,  # show coordinates for maps and objects for debug

    # ===== GUI =====
    # Please see Documentation: GUI for more details
    # Whether to show these elements in the 3D scene
    show_fps=True,
    show_logo=True,
    show_mouse=True,
    show_skybox=True,
    show_terrain=True,
    show_interface=True,
    # Show marks for policies for debugging multi-policy setting
    show_policy_mark=False,
    # Show an arrow marks for providing navigation information
    show_interface_navi_mark=True,
    # A list showing sensor output on window. Its elements are chosen from sensors.keys() + "dashboard"
    interface_panel=["dashboard"],

    # ===== Record/Replay Metadata =====
    # Please see Documentation: Record and Replay for more details
    # When replay_episode is True, the episode metadata will be recorded
    record_episode=False,
    # The value should be None or the log data. If it is the later one, the simulator will replay logged scenario
    replay_episode=None,
    # When set to True, the replay system will only reconstruct the first frame from the logged scenario metadata
    only_reset_when_replay=False,
    # If True, when creating and replaying object trajectories, use the same ID as in dataset
    force_reuse_object_name=False,

    # ===== randomization =====
    num_scenarios=1  # the number of scenarios in this environment
)

Environment Configs

Please see Environments for unique configs for each environment or check the source code of each environment.