Make Environment ================== ## Python script and YAML configuration file To start the simulation, you need to create an environment. The environment is a container for all the objects in the simulation. It is also responsible for updating the state of the simulation at each time step. ::::{tab-set} :::{tab-item} Python Script Create your environment with a simple Python script: ```python import irsim env = irsim.make('empty_world.yaml') ``` The `make` function creates an environment from a configuration file. Supported parameters include: - **`world_name`** (str, optional): Path to the world YAML configuration file - **`projection`** (str, optional): Projection type ("3d" for 3D environment, None for 2D) - **`display`** (bool): Whether to display the environment visualization (default: True) - **`save_ani`** (bool): Whether to save the simulation as an animation (default: False) - **`log_level`** (str): Logging level for the environment (default: "INFO") - **`seed`** (int, optional): Seed for IR-SIM's project RNG. If provided, random elements produced by IR-SIM become reproducible. If omitted/``None``, a new unseeded generator is used (non-reproducible). Custom extensions using ``np.random`` or Python ``random`` should either switch to IR-SIM's RNG or be seeded separately. For more details, see the {py:class}`~irsim.env.env_base.EnvBase` class documentation. ::: :::{tab-item} YAML Configuration Define your world properties in a YAML file (`empty_world.yaml`): ```yaml world: height: 10 # the height of the world (meters) width: 10 # the width of the world (meters) step_time: 0.1 # simulation time step (seconds) - 10Hz sample_time: 0.1 # rendering frequency (seconds) - 10Hz offset: [0, 0] # the offset of the world origin [x, y] control_mode: 'auto' # control mode: 'auto', 'keyboard' collision_mode: 'stop' # collision behavior: 'stop', 'unobstructed', 'unobstructed_obstacles' obstacle_map: null # path to obstacle map file (optional) ``` The configuration file is a YAML file that specifies the properties of the environment. The `empty_world.yaml` file is a simple configuration file that creates an empty environment. ::: :::: ## Important Parameters Explanation ### World Configuration - **`world`**: Main section that defines the simulation environment properties - **`height`** and **`width`**: Specify the size of the simulation world in meters - **`step_time`**: Controls simulation accuracy and speed (smaller = more accurate, slower) - **`sample_time`**: Controls rendering frequency (larger = faster simulation, less smooth visualization) - **`offset`**: Shifts the world coordinate system origin [x, y] in meters - **`control_mode`**: Determines how the simulation is controlled - `'auto'`: Automatic simulation execution - `'keyboard'`: Manual keyboard control - **`collision_mode`**: Defines collision detection behavior - `'stop'`: Stop simulation when collision occurs (default) - `'unobstructed'`: Ignore all collisions - `'unobstructed_obstacles'`: Ignore only obstacle collisions - **`obstacle_map`**: Optional. Path to an obstacle map image, or a generator spec (e.g. `{ name: perlin, ... }`). See [Configure grid map](configure_grid_map). ### Performance Considerations - **Smaller `step_time`**: More accurate physics but slower simulation - **Larger `sample_time`**: Faster simulation but less smooth visualization - **World size**: Larger worlds require more computational resources ```{tip} You can use `sample_time` to control the rendering frequency and accelerate the simulation speed. The default value of `sample_time` is the same as `step_time`. ``` For more detailed parameter information, see the [YAML Configuration](../yaml_config/index) reference. :::{tip} **Automatic Configuration Detection**: The default YAML configuration file has the same name as your Python script. For example, if you create a script named `test.py`, IR-SIM automatically looks for `test.yaml` in the same directory. ```python import irsim # Automatically uses 'test.yaml' if this file is 'test.py' env = irsim.make() ``` This feature simplifies development by eliminating the need to specify configuration files explicitly. ::: ## Basic Simulation Loop After creating an environment, you'll typically run a simulation loop: ```python import irsim env = irsim.make('config.yaml') # Main simulation loop for i in range(1000): env.step() # Update simulation state env.render(0.05) # Render with 0.05 second interval (20Hz) if env.done(): # Check if simulation should end break env.end() # Clean up resources ``` ### Core Methods Explained - **`env.step()`**: Advances the simulation by one time step - **`env.render(interval)`**: Updates the visualization with specified time interval between frames - **`env.done()`**: Returns `True` if simulation completion conditions are met - **`env.reset(random=False)`**: Restores objects to their initial states. With `random=True`, rebuilds the world from the cached YAML parse so any randomized elements (e.g. `distribution: random`, random shape generators) are re-sampled from the current RNG state — use together with `irsim.util.random.set_seed(seed)` for reproducible fresh scenes. The YAML file on disk is **not** re-read; to pick up on-disk edits, use `env.reload()` instead. - **`env.refresh()`**: Refreshes state-derived attributes (geometry, sensor readings, collision tree, and status) without advancing the simulation. Useful after mutating object states directly (e.g. `robot.set_state(...)`) when you need sensors and collisions brought up to date before the next `env.step()`. - **`env.reload(world_name=None)`**: Re-parses the YAML file (optionally a different one) and rebuilds world/objects. - **`env.end()`**: Properly closes the environment and releases resources - **`env.close()`**: Alias for `env.end()`, provided for [Gym](https://gymnasium.farama.org/)-style API compatibility ::::{tip} Update order The environment advances all objects first, then updates all sensors. This two-phase update ensures sensors read the latest world state consistently. If you step objects manually, either pass `sensor_step=True` to `ObjectBase.step(...)` or call `obj.sensor_step()` after updating states. :::: ## Environment Control and Status ### Status Management ```python import irsim env = irsim.make('config.yaml') # Check current status print(f"Current status: {env.status}") print(f"Current time: {env.time}") # Control simulation state env.pause() # Pause the simulation env.resume() # Resume the simulation # Simulation loop with status checking for i in range(50): env.step() env.render(0.05) if i < 10: env.pause() elif i > 20 and i < 30: env.resume() if env.status == "Pause": print("Environment is paused") if env.done(): print("Simulation completed successfully") env.end() ``` ### Available Status Values - **`"Running"`**: The environment is running in auto control mode - **`"Running (keyboard)"`**: The environment is running in keyboard control mode - **`"Pause"`**: Simulation is paused - **`"Done"`**: Simulation has completed - **`"Arrived"`**: All robots have arrived at their goals - **`"Collision"`**: A collision has occurred - **`"Pause (Debugging)"`**: Debugging mode (when `F5` key is pressed) - **`"Reset"`**: The environment is reset - **`"Reload"`**: The environment is reloaded - **`"Save Figure"`**: The figure is saved - **`"Quit"`**: The environment is quit ## Configure Environment Title By default, the simulation time and status are shown in the environment title. You can customize this behavior by setting the `show_title` and customizing the title by `env.set_title()`. ::::{tab-set} :::{tab-item} Custom Title Configuration ```python import irsim env = irsim.make('config.yaml') # Set custom title env.set_title("Multi-Robot Navigation Simulation") # Update title dynamically for i in range(100): env.step() # Update title every 10 steps if i % 10 == 0: env.set_title(f"Simulation Step: {i}") env.render(0.05) env.end() ``` ::: :::{tab-item} Enable/Disable Title Display ```yaml world: height: 20 width: 20 control_mode: 'auto' plot: show_title: true # Show title with time and status show_axis: true # Optional: show axis labels ``` ::: :::: ## Dynamic Object Management In addition to defining objects in YAML configuration files, you can create and add objects programmatically at runtime. This is useful for spawning objects dynamically during simulation, such as generating random obstacles or adding robots on-the-fly. ### Creating Objects Programmatically Use `env.create_robot()` and `env.create_obstacle()` to create objects with keyword arguments: ```python import irsim env = irsim.make('empty_world.yaml') # Create a differential-drive robot robot = env.create_robot( kinematics={"name": "diff"}, shape={"name": "circle", "radius": 0.2}, state=[1, 1, 0], goal=[8, 8, 0], name="robot_0", ) # Create a static circular obstacle obstacle = env.create_obstacle( shape={"name": "circle", "radius": 0.5}, state=[5, 5, 0], name="obs_0", ) ``` Common keyword arguments include: - **`kinematics`** (dict): Kinematics model, e.g. `{"name": "diff"}`, `{"name": "omni"}`, `{"name": "acker"}`. Omit or use `{"name": "static"}` for static objects. - **`shape`** (dict): Shape definition, e.g. `{"name": "circle", "radius": 0.5}` or `{"name": "polygon", "vertices": [[0,0],[1,0],[0,1]]}`. - **`state`** (list): Initial state `[x, y, theta, ...]`. - **`goal`** (list): Goal state `[x, y, theta, ...]`. - **`color`** (str): Object color (default varies by kinematics type). - **`name`** (str): Unique object name. Must not conflict with existing objects. - **`goal_threshold`** (float): Distance threshold for arrival detection (default: 0.1). For the full list of parameters, see the {py:class}`~irsim.world.object_base.ObjectBase` class documentation. ### Adding Objects to the Environment After creating objects, add them to the environment with `env.add_object()` or `env.add_objects()`: ```python # Add a single object env.add_object(robot) # Add multiple objects at once env.add_objects([obstacle]) ``` Each object must have a unique name. Adding an object with a duplicate name raises a `ValueError`. ## Multiple Environments IR-SIM supports creating and running multiple environments simultaneously. Each environment maintains its own isolated state, including world parameters, objects, and simulation time. This is useful for: - **Parallel simulations**: Running multiple scenarios simultaneously - **Comparison studies**: Comparing different algorithms or configurations - **Training**: Running multiple instances for reinforcement learning ### Creating Multiple Environments ```python import irsim # Create two separate environments env1 = irsim.make('scenario_a.yaml') env2 = irsim.make('scenario_b.yaml') # Each environment has its own state print(f"Env1 robots: {env1.robot_number}") print(f"Env2 robots: {env2.robot_number}") ``` ### Isolated Parameters Each environment has completely separate parameter instances: ```python import irsim env1 = irsim.make('world1.yaml') env2 = irsim.make('world2.yaml') # World parameters are isolated env1.world_param.control_mode = 'keyboard' print(f"Env1 control mode: {env1.world_param.control_mode}") # keyboard print(f"Env2 control mode: {env2.world_param.control_mode}") # auto (unchanged) # Simulation time is independent for _ in range(10): env1.step() for _ in range(5): env2.step() print(f"Env1 time: {env1.time}") # 1.0 (10 steps * 0.1) print(f"Env2 time: {env2.time}") # 0.5 (5 steps * 0.1) ``` ### Running Multiple Environments ::::{tab-set} :::{tab-item} Sequential Execution ```python import irsim env1 = irsim.make('scenario_a.yaml', display=True) env2 = irsim.make('scenario_b.yaml', display=True) # Run both environments in the same loop for i in range(500): # Step both environments env1.step() env2.step() # Render both (creates two windows) env1.render(0.01) env2.render(0.01) # Check completion independently if env1.done() and env2.done(): break env1.end() env2.end() ``` ::: :::{tab-item} Headless Parallel (for Training) ```python import irsim # Create multiple headless environments for training num_envs = 4 envs = [ irsim.make(f'training_world.yaml', display=False, seed=i) for i in range(num_envs) ] # Run training loop for episode in range(100): # Reset all environments for env in envs: env.reset() # Collect experiences from all environments for step in range(1000): actions = [get_action(env) for env in envs] # Your policy for env, action in zip(envs, actions): env.step(action) # Check if any environment is done if all(env.done() for env in envs): break # Clean up for env in envs: env.end() ``` ::: :::: ### Parameter Isolation Details Each environment instance creates and binds its own parameter objects: **`world_param`** - Simulation state and settings: | Attribute | Type | Default | Description | |-----------|------|---------|-------------| | `time` | `float` | `0.0` | Current simulation time | | `control_mode` | `str` | `"auto"` | Control mode (`"auto"` or `"keyboard"`) | | `collision_mode` | `str` | `"stop"` | Collision handling mode | | `step_time` | `float` | `0.1` | Time step duration (seconds) | | `count` | `int` | `0` | Simulation step counter | **`env_param`** - Environment objects and utilities: | Attribute | Type | Description | |-----------|------|-------------| | `objects` | `list[ObjectBase]` | List of all objects in the environment | | `logger` | `EnvLogger` | Logger instance for this environment | | `GeometryTree` | `STRtree` | Spatial index for collision detection | | `platform_name` | `str` | Operating system name | **`path_param`** - File path management: | Attribute | Type | Description | |-----------|------|-------------| | `root_path` | `str` | Path to irsim package directory | | `ani_buffer_path` | `str` | Path for animation frame buffer | | `ani_path` | `str` | Path for saved animations | | `fig_path` | `str` | Path for saved figures | Objects within each environment reference their own environment's parameters: :::{note} When creating multiple environments with `display=True`, each environment opens its own visualization window. For training or batch simulations, use `display=False` to disable rendering and improve performance. :::