Environment Definition#
An Arena environment is three parts — scene, embodiment, and task (see the section overview). This page covers the two ways to define that composition, and where they diverge.
Both produce the same object: an IsaacLabArenaEnvironment.
Environment Builder turns that into a runnable Isaac Lab environment:
Python — a registered
ArenaEnvironmentCfg.build()constructs the scene, embodiment, and task in code.YAML — an
ArenaEnvGraphSpec: a declarative environment graph. Nodes are assets; edges are spatial relations and task parameters.from_yaml()validates on load;to_arena_env()returns the sameIsaacLabArenaEnvironment.
Fixed poses in YAML#
For assets whose constructors accept initial_pose, set it through
params.initial_pose on embodiment, background or object nodes:
params:
initial_pose:
position_xyz: [-1.0, 0.0, 0.0]
rotation_xyzw: [0.0, 0.0, 0.0, 1.0]
Positions are environment-local, in metres; quaternions use xyzw order.
position_xyz is required; omitted rotation_xyzw defaults to identity,
as in Pose.from_dict.
The pose is applied at construction. Rigid objects and articulations also
restore it on reset when pose resets are enabled; static background geometry
remains at its construction pose. Do not combine it with relation placement for
the same movable asset.
The same environment, side by side#
Same pick-and-place setup in both columns: a DROID arm picks a Rubik’s cube off a
table and places it in a bowl. Full examples, same order, matching # markers —
read across to see the correspondence.
YAML |
Python |
|---|---|
# Environment name
env_name: pick_and_place_maple_table
# Embodiment
embodiment:
id: droid_abs_joint_pos
registry_name: droid_abs_joint_pos
# Scene (lighting injected automatically)
background:
id: maple_table
registry_name: maple_table_robolab
objects:
- id: cube
registry_name: rubiks_cube_hot3d_robolab
- id: bowl
registry_name: bowl_ycb_robolab
# Sub-asset prims
object_references:
- id: table
parent_id: maple_table
prim_path: table
object_type: rigid
# Layout
relations:
- kind: is_anchor
subject: table
- kind: 'on'
subject: cube
reference: table
- kind: 'on'
subject: bowl
reference: table
# Task
task:
composition: atomic
description: Pick up the Rubik's cube and place it in the bowl.
subtasks:
- kind: PickAndPlaceTask
params:
pick_up_object: cube
destination_location: bowl
background_scene: maple_table
episode_length_s: 20.0
|
@register_environment
class PickAndPlaceMapleTable(ArenaEnvironmentFactory):
# Identity
name = "pick_and_place_maple_table"
def build(self, cfg):
get = self.asset_registry.get_asset_by_name
# Embodiment
embodiment = get("droid_abs_joint_pos")(
enable_cameras=cfg.enable_cameras,
)
# Scene (light must be added explicitly)
maple_table = get("maple_table_robolab")()
cube = get("rubiks_cube_hot3d_robolab")()
bowl = get("bowl_ycb_robolab")()
light = get("light")()
# Sub-asset prims
table = ObjectReference(
name="table",
prim_path="{ENV_REGEX_NS}"
"/maple_table_robolab/table",
parent_asset=maple_table,
object_type=ObjectType.RIGID,
)
# Layout
table.add_relation(IsAnchor())
table.set_initial_pose(Pose.identity())
cube.add_relation(On(table))
bowl.add_relation(On(table))
# Task
task = PickAndPlaceTask(
pick_up_object=cube,
destination_location=bowl,
background_scene=maple_table,
episode_length_s=20.0,
)
return IsaacLabArenaEnvironment(
name=self.name,
embodiment=embodiment,
scene=Scene(assets=[
maple_table, light, cube, bowl, table,
]),
task=task,
)
|
Where the two formats differ#
Three buckets:
Only Python
Only YAML
Same key, different behavior
Only in Python#
IsaacLabArenaEnvironment takes ten constructor arguments.
build_arena_env_from_graph_spec() fills the graph-owned scene, task,
placement, physics-backend, and compiled-config callback fields. The remaining
runtime integrations have no YAML key.
Teleoperation device. YAML never sets env_cfg.teleop_devices. Python can
pass a device that drives the embodiment:
teleop_device = self.device_registry.get_device_by_name(cfg.teleop_device)()
return IsaacLabArenaEnvironment(..., teleop_device=teleop_device)
RL interoperability. Gym kwargs for RL frameworks are Python-only. You cannot define an RL-training environment in YAML:
return IsaacLabArenaEnvironment(
...,
rl_framework_entry_point="rsl_rl_cfg_entry_point",
rl_policy_cfg="my_module:RLPolicyCfg",
)
Patching the compiled config. env_cfg_callback runs after Arena builds the
ManagerBasedRLEnvCfg. Use it for viewport, decimation, physics, or anything
else on that config:
def set_viewport(env_cfg):
env_cfg.viewer.eye = (1.5, 1.5, 1.0)
env_cfg.viewer.lookat = (0.0, 0.0, 0.5)
return IsaacLabArenaEnvironment(..., env_cfg_callback=set_viewport)
Extra episode recorder terms. Per-episode signals beyond the built-ins:
return IsaacLabArenaEnvironment(
...,
episode_recorder_terms={"cube_pose": EpisodeRecorderTermCfg(func=record_cube_pose)},
)
Extra variations. Both formats get the defaults an asset attaches in its constructor (a light brings intensity, color, and HDR). Anything the asset class does not declare needs Python:
pick_up_object.add_variation(MyCustomVariation(pick_up_object))
Calling methods on assets. YAML params go to constructors only. Method
calls on a live instance have no YAML form:
directional_light.set_dome_light(dome_light)
Only in YAML#
Load-time validation. The spec is Pydantic. Registry names, node ids,
relation arity, task params, and check subsets fail when the file loads — before
Isaac Sim starts. Python has no equivalent; the same mistakes show up later as an
AttributeError or a broken scene:
AssertionError: Unknown asset registry_name 'rubiks_cube_hot3d'
AssertionError: Relation kind 'is_anchor' must not define relation.reference
AssertionError: composition 'atomic' requires exactly one atomic task
Automatic default lighting. No light-tagged asset and no light in the
background USD? The loader adds a dome light and a directional light (off until a
lighting-direction variation turns it on). In Python, no light means no light.
Reuse across task files. external_yaml reuses external definitions across
tasks. It takes a single path or a list of paths, relative to the including file. For example:
external_yaml:
- ../scenes/bagel_plate_banana_bowl.yaml
- ../shared_defaults.yaml
env_name: banana_on_plate
task:
composition: atomic
...
All external YAML files are merged into one spec under the following rules:
A value can be defined in only one file. A file can never silently override another, even if both files hold the same value.
Exception: lists of dicts with an
id(such asobjects:) are concatenated in file order. Eachidmay appear only once across all files. Other lists, such asrelations, may be defined in only one file.Nested includes are not allowed. An included file cannot declare
external_yamlitself.
Declared CLI overrides. cli_override_specs turns a node into a CLI flag
with no code change. Below, --object replaces that node’s registry_name:
cli_override_specs:
- arg: object
target_node_id: rubiks_cube_hot3d_robolab
Machine authoring. Graph YAML is the output of
Agentic Environment Generation: natural language in,
validated ArenaEnvGraphSpec out. Pydantic checks registry names, node ids,
relation arity, and task params before the simulator sees anything.
Graph specs may also declare default_physics_backend and env_cfg_override;
see Graph YAML env_cfg_override and Physics backend selection.
Same key, different behavior#
Skip these when porting and you will not get the same environment.
Placement tuning. YAML placer_params is a validated nested override of
ObjectPlacerParams and its data-only child configs. Field names and types
come directly from those dataclasses, so nested solver settings do not need a
second schema:
placer_params:
placement_seed: 42
random_yaw_init: true
resolve_on_reset: false
min_unique_layouts_per_env: 20
allow_best_loss_fallbacks: false
solver_params:
clearance_m: 0.0
The same block selects enabled_checks, required_checks, and placement
debug output. Runtime/code-bearing values such as
reachability_config.embodiment and solver_params.strategies are
rejected. Explicit builder/CLI
placement_seed and resolve_on_reset values take precedence over YAML.
Duplicate instances need unique names. YAML sets name to the node
id. Library object classes default it to the registry name, so in Python two objects created
from the same class share one name unless you set name. Prim paths, event
terms, and observation keys all key off that name:
# Two objects of one class. Give each its own name.
banana_a = get("banana_ycb_robolab")(name="banana_a")
banana_b = get("banana_ycb_robolab")(name="banana_b")
An anchor without a pose. YAML gives an is_anchor subject with no pose
Pose.identity() — the layout origin for the relation solver. Python leaves
the anchor with no pose until you set one:
table.add_relation(IsAnchor())
table.set_initial_pose(Pose.identity())
Relative prim paths. An object-reference prim_path that does not start
with {ENV_REGEX_NS}/ expands to
{ENV_REGEX_NS}/<background registry_name>/<prim_path>. Always the background
registry name — even if parent_id points elsewhere. Python takes the path
verbatim; it must be the full runtime path.
Cameras. YAML forwards enable_cameras into the embodiment params. A
Python factory that ignores cfg.enable_cameras builds a camera-less env even
when the flag is set. Policies that expect images will not be amused:
embodiment = get("droid_abs_joint_pos")(enable_cameras=cfg.enable_cameras)
Openable references. YAML picks the class from the fields: openable_joint_name
in params → OpenableObjectReference (and object_type: articulation);
otherwise → ObjectReference. Python: you pick. Use the plain class for a door
and you get a door with nothing to open.
Object set members. YAML constructs set members with no constructor args, so
they cannot carry a usd_path. SimReady assets fail at load time and cannot be
set members. Python RigidObjectSet takes live instances — SimReady is fine:
RigidObjectSet(name="bottles", objects=[simready_bottle, ycb_bottle])
How to spawn an environment#
YAML |
Python |
|---|---|
python isaaclab_arena/scripts/environment_runner.py \
--env_spec \
isaaclab_arena_environments/robolab/tasks/banana_on_plate.yaml
|
python isaaclab_arena/scripts/environment_runner.py \
pick_and_place_maple_table \
--embodiment droid_rel_joint_pos \
--pick_up_object banana_ycb_robolab \
--destination_location bowl_ycb_robolab
|
Or load a graph YAML directly:
spec = ArenaEnvGraphSpec.from_yaml("path/to/env_graph.yaml")
environment = spec.to_arena_env()
env = ArenaEnvBuilder(environment, ArenaEnvBuilderCfg()).make_registered()
Choosing between them#
Many assets and relations? Start with YAML as it is validated, and machine-generatable so you
can focus on the scene layout and task definition. Reach for Python when the YAML cannot
express what you need: RL registration, teleop, ManagerBasedRLEnvCfg patches,
placement parameters, and so on.
Next Steps#
Environment Builder shows how either definition becomes a runnable Isaac Lab environment.