Sim Environment Code Review#

In this lesson, we’ll review the code that defines and registers the Unitree G1 static apple-to-plate environment in Isaac Lab-Arena.

  • Identify the environment registration entry point for galileo_g1_static_pick_and_place.

  • Trace how the background, apple, plate, embodiment, and teleoperation device are assembled.

  • Explain why teleoperation records with g1_wbc_agile_pink and evaluation runs with g1_wbc_agile_joint.

  • Review the task success logic used by the pick-and-place environment.

Tip

This section is theory-only. Prefer to skip to hands-on activities? Move on to Sim Teleop and Whole-Body Control.

Return here afterwards to help you understand the code that defines and registers the environment.

Review the Environment#

Let’s take a look at the code used to define and register this environment in Isaac Lab-Arena.

The static apple-to-plate workflow ships its own GalileoG1StaticPickAndPlaceEnvironment registered under galileo_g1_static_pick_and_place. It reuses the galileo_locomanip background USD environment and the same OpenXR retargeter as the loco-manipulation apple-to-plate environment.

The static task defaults to the g1_wbc_agile_pink embodiment, which uses AGILE’s end-to-end velocity policy, instead of g1_wbc_pink, which uses the HOMIE stand/walk pair.

The current source implementation keeps the apple and destination plate fixed for deterministic demonstrations, adds an invisible shelf support patch, locks the waist by default, applies tuned finger friction, and deactivates background boxes that would clutter the task workspace.

Why AGILE? (click to expand)

The static task never walks, so AGILE’s single-policy backend fits the no-locomotion task better. The apple and destination plate are placed on the same shelf so the robot never needs to drive its base. WBC holds the standing pose.

Recording vs. Evaluation Embodiment (click to expand)

Teleoperation uses the default g1_wbc_agile_pink embodiment, which uses PinkIK plus AGILE.

Closed-loop policy evaluation uses g1_wbc_agile_joint, which uses direct joint control plus AGILE.

Both share the same AGILE lower-body backend. The _joint twin bypasses PinkIK at inference because the policy trains on the joint-space targets that PinkIK produced during recording.

Inspect the Environment Implementation#

The environment registers a single-shelf pick-and-place scene with the G1, an apple, a clay plate, and an invisible support patch under the shelf workspace.

Starting with snippets, let’s analyze the code and build up to the complete environment registration.

How the Environment Is Built#

Tuned Constants#

The environment starts with tuned constants for shelf height, object placement, asset scale, shelf support, finger friction, and background prim cleanup.

Source: tuned constants in galileo_g1_static_pick_and_place_environment.py

 1SHELF_SURFACE_Z = -0.030
 2SHELF_AIRGAP = 0.005
 3SHELF_SUPPORT_PATCH_SIZE = (0.8, 1.5, 0.04)
 4SHELF_SUPPORT_PATCH_CENTER = (0.62, 0.0, SHELF_SURFACE_Z - SHELF_SUPPORT_PATCH_SIZE[2] / 2.0)
 5
 6PICK_UP_OBJECT_SPAWN_XY = (0.5785, 0.27)
 7DESTINATION_SPAWN_XY = (0.5785, 0.06)
 8APPLE_SPAWN_XY_RANGE_M = 0.0
 9
10_USD_ORIGIN_ABOVE_BOTTOM_M: dict[str, float] = {
11    "apple_01_objaverse_robolab": 0.0171,
12    "clay_plates_hot3d_robolab": 0.0,
13}
14
15_TUNED_SCALES: dict[str, tuple[float, float, float]] = {
16    TUNED_PICK_UP_OBJECT_NAME: (0.009, 0.009, 0.009),
17    TUNED_DESTINATION_NAME: (0.5, 0.5, 0.5),
18}

APPLE_SPAWN_XY_RANGE_M is currently 0.0, so the apple spawn stays deterministic. The plate also remains fixed so the place target stays consistent across episodes.

Asset and Device Registry#

The environment loads the background, pick-up object, destination, embodiment, and optional teleoperation device through Arena’s registries.

The invisible shelf support is defined inline as a small Object spawner because it is specific to this static apple workspace.

Source: asset setup in galileo_g1_static_pick_and_place_environment.py

 1class StaticShelfSupport(Object):
 2    def __init__(self):
 3        self.spawner_cfg = sim_utils.CuboidCfg(
 4            size=SHELF_SUPPORT_PATCH_SIZE,
 5            collision_props=sim_utils.CollisionPropertiesCfg(contact_offset=0.005),
 6            visible=False,
 7        )
 8        super().__init__(
 9            name="static_pick_place_shelf_support",
10            prim_path="{ENV_REGEX_NS}/static_pick_place_shelf_support",
11            object_type=ObjectType.SPAWNER,
12            initial_pose=Pose(
13                position_xyz=SHELF_SUPPORT_PATCH_CENTER,
14                rotation_xyzw=(0.0, 0.0, 0.0, 1.0),
15            ),
16            tags=["background", "procedural"],
17        )
18
19background = self.asset_registry.get_asset_by_name("galileo_locomanip")()
20shelf_support = StaticShelfSupport()
21pick_up_object = self.asset_registry.get_asset_by_name(args_cli.object)(scale=_asset_scale(args_cli.object))
22destination = self.asset_registry.get_asset_by_name(args_cli.destination)(
23    scale=_asset_scale(args_cli.destination)
24)
25embodiment = self.asset_registry.get_asset_by_name(args_cli.embodiment)(
26    enable_cameras=args_cli.enable_cameras,
27    lock_waist=args_cli.lock_waist,
28)
29embodiment.set_finger_contact_friction(
30    material_path=G1_STATIC_FINGER_FRICTION_MATERIAL_PATH,
31    static_friction=G1_STATIC_FINGER_STATIC_FRICTION,
32    dynamic_friction=G1_STATIC_FINGER_DYNAMIC_FRICTION,
33    prim_name_markers=G1_STATIC_FINGER_PRIM_NAME_MARKERS,
34)

The static workflow shares galileo_locomanip with the loco-manipulation variant because the lighting, shelf geometry, and 23-D action layout are already tuned. The invisible shelf support patch gives the task objects a clean collision surface on the shelf workspace. The default g1_wbc_agile_pink embodiment swaps HOMIE’s stand/walk pair for AGILE’s single end-to-end velocity policy. The g1_wbc_pink embodiment remains available as an override. The current source locks the waist by default for this upper-body-only task and applies tuned finger friction to improve grasp behavior.

Object Placement#

Source: object placement in galileo_g1_static_pick_and_place_environment.py

 1embodiment.set_initial_pose(
 2    Pose(position_xyz=(0.25, 0.08, 0.0), rotation_xyzw=(0.0, 0.0, 0.0, 1.0))
 3)
 4embodiment.set_joint_initial_pos(G1_STATIC_OPEN_ARM_JOINT_POS)
 5pick_up_object.set_initial_pose(
 6    PoseRange(
 7        position_xyz_min=(
 8            pick_up_object_x - APPLE_SPAWN_XY_RANGE_M,
 9            pick_up_object_y - APPLE_SPAWN_XY_RANGE_M,
10            pick_up_object_z,
11        ),
12        position_xyz_max=(
13            pick_up_object_x + APPLE_SPAWN_XY_RANGE_M,
14            pick_up_object_y + APPLE_SPAWN_XY_RANGE_M,
15            pick_up_object_z,
16        ),
17        rpy_min=(0.0, 0.0, 0.0),
18        rpy_max=(0.0, 0.0, 0.0),
19    )
20)
21destination.set_initial_pose(
22    Pose(position_xyz=(destination_x, destination_y, _shelf_spawn_z(args_cli.destination)),
23         rotation_xyzw=(0.0, 0.0, 0.0, 1.0))
24)

The robot pose is tuned for the same-shelf static task: slightly forward toward the table while preserving the lateral offset that keeps both arms usable. The controller dynamically lifts the pelvis at runtime, so the initial z=0.0 pose is intentional. The apple still uses PoseRange, but its current range is zero for deterministic demonstrations. The plate stays fixed and within arm’s reach.

Scene Composition#

Source: scene composition in galileo_g1_static_pick_and_place_environment.py

1scene = Scene(assets=[background, shelf_support, pick_up_object, destination])

The static environment uses one shelf-anchored background, an invisible shelf support patch, the pick-up object, and the destination. No second table is needed because the plate sits on the same shelf as the apple.

Environment Configuration Callback#

The source implementation deactivates selected background boxes and forces a camera rerender after reset.

Source: environment configuration callback in galileo_g1_static_pick_and_place_environment.py

 1def env_cfg_callback(env_cfg):
 2    from isaaclab.managers import EventTermCfg
 3
 4    env_cfg.events.deactivate_static_pick_place_background_prims = EventTermCfg(
 5        func=_deactivate_background_prims,
 6        mode="prestartup",
 7        params={"prim_relative_paths": _BACKGROUND_PRIMS_TO_DEACTIVATE},
 8    )
 9    env_cfg.num_rerenders_on_reset = 1
10    return env_cfg

The background deactivation removes clutter from the apple-to-plate workspace. The rerender prevents the first policy query after reset from seeing the previous episode’s final camera frame.

Pick-and-Place Task#

Source: task setup in galileo_g1_static_pick_and_place_environment.py

 1task_description = args_cli.task_description
 2
 3task = PickAndPlaceTask(
 4    pick_up_object=pick_up_object,
 5    destination_location=destination,
 6    background_scene=background,
 7    episode_length_s=6.0,
 8    task_description=task_description,
 9    force_threshold=0.5,
10    velocity_threshold=0.1,
11)

The static environment uses PickAndPlaceTask directly. The task wires the apple, plate, background, six-second timeout, task description, and standard pick-and-place termination logic together. The CLI default for --task_description is move the apple to the plate. If you change --object or --destination, pass a matching --task_description explicitly. The force_threshold and velocity_threshold values mirror the loco-manipulation environment so success metrics are comparable.

Isaac Lab-Arena Environment#

Source: environment assembly in galileo_g1_static_pick_and_place_environment.py

1isaaclab_arena_environment = IsaacLabArenaEnvironment(
2    name=self.name,
3    embodiment=embodiment,
4    scene=scene,
5    task=task,
6    teleop_device=teleop_device,
7    env_cfg_callback=env_cfg_callback,
8)

This assembles the scene, embodiment, task, optional teleoperation device, and environment configuration callback into a runnable environment.

Reviewing the Complete Registration Pattern#

Now let’s look at the complete registration pattern, trimmed to the source sections that matter most for this workflow. Helper functions such as _asset_scale(), _shelf_spawn_z(), _deactivate_background_prims(), and the full env_cfg_callback() body are summarized above.

Static Apple-to-Plate Environment Definition

Source: full environment definition in galileo_g1_static_pick_and_place_environment.py

  1from isaaclab_arena_environments.example_environment_base import ExampleEnvironmentBase
  2
  3SHELF_SURFACE_Z = -0.030
  4SHELF_AIRGAP = 0.005
  5SHELF_SUPPORT_PATCH_SIZE = (0.8, 1.5, 0.04)
  6SHELF_SUPPORT_PATCH_CENTER = (0.62, 0.0, SHELF_SURFACE_Z - SHELF_SUPPORT_PATCH_SIZE[2] / 2.0)
  7PICK_UP_OBJECT_SPAWN_XY = (0.5785, 0.27)
  8DESTINATION_SPAWN_XY = (0.5785, 0.06)
  9APPLE_SPAWN_XY_RANGE_M = 0.0
 10
 11_USD_ORIGIN_ABOVE_BOTTOM_M = {
 12    "apple_01_objaverse_robolab": 0.0171,
 13    "clay_plates_hot3d_robolab": 0.0,
 14}
 15
 16TUNED_PICK_UP_OBJECT_NAME = "apple_01_objaverse_robolab"
 17TUNED_DESTINATION_NAME = "clay_plates_hot3d_robolab"
 18
 19
 20class GalileoG1StaticPickAndPlaceEnvironment(ExampleEnvironmentBase):
 21
 22    name: str = "galileo_g1_static_pick_and_place"
 23
 24    def get_env(self, args_cli):
 25        from isaaclab import sim as sim_utils
 26
 27        from isaaclab_arena.assets.object import Object
 28        from isaaclab_arena.assets.object_base import ObjectType
 29        from isaaclab_arena.environments.isaaclab_arena_environment import IsaacLabArenaEnvironment
 30        from isaaclab_arena.scene.scene import Scene
 31        from isaaclab_arena.tasks.pick_and_place_task import PickAndPlaceTask
 32        from isaaclab_arena.utils.pose import Pose, PoseRange
 33
 34        background = self.asset_registry.get_asset_by_name("galileo_locomanip")()
 35
 36        class StaticShelfSupport(Object):
 37            def __init__(self):
 38                self.spawner_cfg = sim_utils.CuboidCfg(
 39                    size=SHELF_SUPPORT_PATCH_SIZE,
 40                    collision_props=sim_utils.CollisionPropertiesCfg(contact_offset=0.005),
 41                    visible=False,
 42                )
 43                super().__init__(
 44                    name="static_pick_place_shelf_support",
 45                    prim_path="{ENV_REGEX_NS}/static_pick_place_shelf_support",
 46                    object_type=ObjectType.SPAWNER,
 47                    initial_pose=Pose(
 48                        position_xyz=SHELF_SUPPORT_PATCH_CENTER,
 49                        rotation_xyzw=(0.0, 0.0, 0.0, 1.0),
 50                    ),
 51                    tags=["background", "procedural"],
 52                )
 53
 54        shelf_support = StaticShelfSupport()
 55        pick_up_object = self.asset_registry.get_asset_by_name(args_cli.object)(scale=_asset_scale(args_cli.object))
 56        destination = self.asset_registry.get_asset_by_name(args_cli.destination)(scale=_asset_scale(args_cli.destination))
 57        embodiment = self.asset_registry.get_asset_by_name(args_cli.embodiment)(
 58            enable_cameras=args_cli.enable_cameras,
 59            lock_waist=args_cli.lock_waist,
 60        )
 61        embodiment.set_finger_contact_friction(
 62            material_path=G1_STATIC_FINGER_FRICTION_MATERIAL_PATH,
 63            static_friction=G1_STATIC_FINGER_STATIC_FRICTION,
 64            dynamic_friction=G1_STATIC_FINGER_DYNAMIC_FRICTION,
 65            prim_name_markers=G1_STATIC_FINGER_PRIM_NAME_MARKERS,
 66        )
 67
 68        teleop_device = (
 69            self.device_registry.get_device_by_name(args_cli.teleop_device)()
 70            if args_cli.teleop_device is not None else None
 71        )
 72
 73        embodiment.set_initial_pose(
 74            Pose(position_xyz=(0.25, 0.08, 0.0), rotation_xyzw=(0.0, 0.0, 0.0, 1.0))
 75        )
 76        embodiment.set_joint_initial_pos(G1_STATIC_OPEN_ARM_JOINT_POS)
 77        pick_up_object_x, pick_up_object_y = PICK_UP_OBJECT_SPAWN_XY
 78        destination_x, destination_y = DESTINATION_SPAWN_XY
 79        pick_up_object_z = _shelf_spawn_z(args_cli.object)
 80        pick_up_object.set_initial_pose(
 81            PoseRange(
 82                position_xyz_min=(
 83                    pick_up_object_x - APPLE_SPAWN_XY_RANGE_M,
 84                    pick_up_object_y - APPLE_SPAWN_XY_RANGE_M,
 85                    pick_up_object_z,
 86                ),
 87                position_xyz_max=(
 88                    pick_up_object_x + APPLE_SPAWN_XY_RANGE_M,
 89                    pick_up_object_y + APPLE_SPAWN_XY_RANGE_M,
 90                    pick_up_object_z,
 91                ),
 92                rpy_min=(0.0, 0.0, 0.0),
 93                rpy_max=(0.0, 0.0, 0.0),
 94            )
 95        )
 96        destination.set_initial_pose(
 97            Pose(position_xyz=(destination_x, destination_y, _shelf_spawn_z(args_cli.destination)),
 98                 rotation_xyzw=(0.0, 0.0, 0.0, 1.0))
 99        )
100
101        task_description = args_cli.task_description
102        # env_cfg_callback deactivates cluttering background prims and forces one camera rerender on reset.
103        scene = Scene(assets=[background, shelf_support, pick_up_object, destination])
104        return IsaacLabArenaEnvironment(
105            name=self.name,
106            embodiment=embodiment,
107            scene=scene,
108            task=PickAndPlaceTask(
109                pick_up_object=pick_up_object,
110                destination_location=destination,
111                background_scene=background,
112                episode_length_s=6.0,
113                task_description=task_description,
114                force_threshold=0.5,
115                velocity_threshold=0.1,
116            ),
117            teleop_device=teleop_device,
118            env_cfg_callback=env_cfg_callback,
119        )

Key Takeaways#

We reviewed how Isaac Lab-Arena registers the static apple-to-plate environment, places the robot and task objects, keeps the current apple and plate poses deterministic, adds shelf support, locks the waist by default, applies tuned finger friction, chooses the AGILE-based embodiment, and wires success conditions through PickAndPlaceTask.