Add G1 AGILE tabletop apple-to-plate environment#562
Conversation
New environment where a G1 robot with WBC-AGILE policy stands at a Seattle Lab table and moves an apple onto a plate. Reuses the existing PickAndPlaceTask for contact-sensor success detection and object-dropped termination. - g1_agile_tabletop_apple_to_plate_environment.py: scene setup with table, apple, plate, and G1WBCAgileJoint embodiment - cli.py: register the new environment in ExampleEnvironments - test_g1_agile_tabletop_apple_to_plate.py: initial-state and teleport-success tests using run_simulation_app_function pattern Signed-off-by: Lionel Gulich <lgulich@nvidia.com>
The first test used step_zeros_and_call which sends zero actions including a zero base-height command. For the G1 WBC embodiment this causes the robot to squat to the ground, leading to physics instability. Set actions[:, -4] = 0.75 consistent with the second test and the existing G1 WBC test patterns in test_g1_wbc_embodiment.py. Signed-off-by: Lionel Gulich <lgulich@nvidia.com>
There was a problem hiding this comment.
Review Summary
Clean new environment addition that follows established patterns well. The G1AgileTabletopAppleToPlateEnvironment correctly wires G1WBCAgileJointEmbodiment with PickAndPlaceTask and the CLI/test integration is solid. Pre-commit and docs CI pass. Two minor suggestions below.
✅ What looks good
- Pattern adherence: Environment class, CLI registration, and test structure all follow the established conventions (matches
KitchenPickAndPlaceEnvironment,GalileoPickAndPlaceEnvironment, etc.) - Correct base-height command (0.75): Both tests properly set
actions[:, -4] = 0.75to keep the AGILE WBC robot standing, matching the pattern established intest_g1_wbc_embodiment.py - Good test design: The two-test approach (initial state not terminated + teleport-to-success) provides meaningful coverage of the task lifecycle without requiring full manipulation
- Clean reuse: No new task class needed —
PickAndPlaceTaskhandles everything
📝 Suggestions
1. Task description is hardcoded to "apple" but --object is configurable
File: g1_agile_tabletop_apple_to_plate_environment.py, line 66
The task_description always says "Pick up the apple from the table and place it onto the plate" even when a different object is passed via --object. Consider making it dynamic:
task_description=f"Pick up the {args_cli.object.replace('_', ' ')} from the table and place it onto the plate.",Or if the description is only used for display/logging and the default object is always apple, this is fine as-is — just worth noting.
2. Test duplicates environment setup instead of reusing the environment class
File: test_g1_agile_tabletop_apple_to_plate.py
The test's get_test_environment() manually reconstructs the entire environment (assets, poses, task, embodiment) rather than instantiating G1AgileTabletopAppleToPlateEnvironment and calling get_env(). This means if positions or assets change in the environment class, the test won't reflect those changes.
That said, I see other tests in this repo follow the same pattern (e.g., test_g1_wbc_embodiment.py), so this may be an intentional convention to keep test setup explicit. If so, no change needed — just flagging the drift risk.
Verdict
LGTM with minor nits. This is a straightforward, well-structured environment addition. The two suggestions above are non-blocking. 👍
Add warmup steps (50) before checking termination in both tests. The G1 AGILE WBC policy needs time to stabilise the robot's lower body; during the first few dozen sim steps physics transients can nudge the apple and trigger the object-dropped termination spuriously. This mirrors the warmup pattern used in test_g1_agile_policy.py (WARMUP_STEPS = 50) and extracts a shared _step_with_standing_actions helper that both test functions now use. Signed-off-by: Lionel Gulich <lgulich@nvidia.com>
…ination The G1 AGILE WBC policy's stabilisation during warmup physically knocks the apple off the table, causing its root height to drop below the -0.05m threshold and triggering the object_dropped termination before the actual test assertions begin. Add _replace_apple_at_initial_pose() which teleports the apple back to its starting position with zero velocity after warmup completes. This ensures test_initial_state_not_terminated validates steady-state behaviour rather than warmup transients. Signed-off-by: Lionel Gulich <lgulich@nvidia.com>
…odiment - Switch from Seattle Lab table to maple_table_robolab with On() relations for automatic object placement on table surface - Add dome light (intensity 500) and correct object scales (apple 0.01, plate 0.5) - Position robot at (1.2, 0, 0) facing the table - Add G1WBCAgilePinkEmbodiment (g1_wbc_agile_pink) combining AGILE WBC with Pink IK upper body control for OpenXR teleoperation - Add OpenXR retargeter for the new embodiment Signed-off-by: Lionel Gulich <lgulich@nvidia.com>
There was a problem hiding this comment.
🤖 Isaac Lab Review Bot
Reviewed 5 changed files. One bug and a couple of style nits:
🐛 Bug: Missing @register_asset decorator
isaaclab_arena/embodiments/g1/g1.py — G1WBCAgilePinkEmbodiment is missing the @register_asset decorator. Every other embodiment class (G1WBCJointEmbodiment, G1WBCPinkEmbodiment, G1WBCAgileJointEmbodiment) has it. Without it, asset_registry.get_asset_by_name("g1_wbc_agile_pink") will fail at runtime.
# Missing this:
@register_asset
class G1WBCAgilePinkEmbodiment(G1EmbodimentBase):📝 Style nits (non-blocking)
-
g1_agile_tabletop_apple_to_plate_environment.py—import isaaclab.sim as sim_utilsandfrom isaaclab_arena.utils.pose import Poseare placed mid-function between asset creation statements (lines ~36 and ~51). Move them to the top ofget_env()with the other imports for readability. -
Retargeter —
G1WbcAgilePinkIsaacTeleopRetargeterimports the same_build_g1_pink_locomanipulation_pipelineas the non-AGILE retargeter. This is fine if the pipeline is truly the same for both, but worth a comment noting that the AGILE variant shares the same teleop pipeline.
Overall the environment follows existing patterns well. Tests are solid — the warmup + re-place strategy for the initial state test is a nice touch. Just needs the decorator fix.
The GPU physics pipeline reports spurious contact-sensor forces between objects that are far apart, causing the object_on_destination success termination to fire incorrectly. Add an optional proximity guard (destination_cfg + max_distance) that requires the pick-up object to be within a configurable distance of the destination before the contact force is considered valid. Enable the guard in PickAndPlaceTask with max_distance=0.15 m to fix the G1 AGILE tabletop apple-to-plate test failure where the apple was being catapulted upward by robot vibrations yet phantom contact forces still triggered the success termination. Signed-off-by: Lionel Gulich <lgulich@nvidia.com>
Fix: False success termination in pick-and-place tasksRoot CauseThe FixAdded a proximity guard to
Test Results
VideoEnvironment recording attached — shows G1 robot standing at the table with the apple, plate, and table visible. AGILE WBC policy maintains balance. Generated by autodev — Claude Code |
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previous black run used default line-length (88) instead of the project's configured 120. Re-ran with correct settings. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The destination scene entity may be an XformPrimView (not a RigidObject) and lacks a .data attribute. Fall back to get_world_poses() for position retrieval when .data is unavailable. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The proximity guard was breaking existing tests because it was always-on for all PickAndPlaceTask instances. Make it opt-in via success_proximity_max_distance parameter (default 0.0 = disabled). Only the G1 tabletop environment enables it (0.15m) since that's where the phantom contact force issue manifests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The test creates its own PickAndPlaceTask instance separately from the environment file, so it also needs success_proximity_max_distance=0.15 to prevent phantom contact forces from triggering false success. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Greptile SummaryThis PR adds a new Confidence Score: 5/5Safe to merge; all findings are P2 style/quality suggestions that do not block correct runtime behaviour. No P0 or P1 defects were found. The proximity guard fix is backward-compatible and the new environment follows established patterns. The three P2 comments (apple scale, hardcoded re-placement position, simplified test scene) are informational quality improvements that should not block the PR. isaaclab_arena_environments/g1_agile_tabletop_apple_to_plate_environment.py — verify the apple scale=(0.01,0.01,0.01) is correct for the USD asset's native units. Important Files Changed
Sequence DiagramsequenceDiagram
participant CLI as CLI / ArenaEnvBuilder
participant Env as G1AgileTabletopAppleToPlateEnvironment
participant Scene as Scene (maple_table_robolab + apple + plate)
participant Task as PickAndPlaceTask
participant Term as object_on_destination
participant Prox as proximity guard
CLI->>Env: get_env(args_cli)
Env->>Scene: assemble assets + On(table_reference) relations
Env->>Task: PickAndPlaceTask(success_proximity_max_distance=0.15)
Task->>Term: register termination with destination_cfg + max_distance
CLI->>Scene: ArenaEnvBuilder.make_registered()
loop Each physics step
Term->>Term: check contact force > 1 N
Term->>Term: check object velocity < 0.1 m/s
Term->>Prox: distance(apple, plate) < 0.15 m ?
Prox-->>Term: close_enough
Term-->>CLI: condition_met (success)
end
Reviews (1): Last reviewed commit: "fix(test): enable proximity guard in tab..." | Re-trigger Greptile |
| from isaaclab_arena.tasks.pick_and_place_task import PickAndPlaceTask | ||
|
|
||
| background = self.asset_registry.get_asset_by_name("maple_table_robolab")() | ||
| pick_up_object = self.asset_registry.get_asset_by_name(args_cli.object)(scale=(0.01, 0.01, 0.01)) |
There was a problem hiding this comment.
Verify apple scale is intentional
scale=(0.01, 0.01, 0.01) reduces the asset to 1% of its original size. No other environment in the codebase applies a scale this small to a pick-up object — the analogous PickAndPlaceMapleTableEnvironment uses the default scale of 1.0. If the apple_01_objaverse_robolab USD is already in meter units an apple would end up roughly 1 cm in diameter, which would make grasping and contact detection unreliable. If the asset is authored in centimetres then this conversion is correct; please add a brief comment explaining the intent so future readers don't wonder whether this is a typo.
| init_pos = torch.tensor([[0.15, 0.15, 0.05]], device=env.unwrapped.device) | ||
| init_quat = torch.tensor([[1.0, 0.0, 0.0, 0.0]], device=env.unwrapped.device) | ||
| apple_object.write_root_pose_to_sim(root_pose=torch.cat([init_pos, init_quat], dim=-1)) |
There was a problem hiding this comment.
Hardcoded re-placement position may silently drift from the test setup
init_pos is hardcoded to [0.15, 0.15, 0.05] instead of being derived from the apple asset configured in get_test_environment. If the initial pose on line 37 of that function is updated, these two values will silently diverge and the "re-placed" apple will be at the wrong location, potentially making the test pass or fail for the wrong reason. Consider reading the value from the apple object or from a shared constant:
# option: derive from apple's stored initial pose
init_pos = torch.tensor([apple.initial_pose.position_xyz], device=env.unwrapped.device)| background = asset_registry.get_asset_by_name("table")() | ||
| apple = asset_registry.get_asset_by_name("apple_01_objaverse_robolab")() | ||
| plate = asset_registry.get_asset_by_name("clay_plates_hot3d_robolab")() | ||
|
|
||
| apple.set_initial_pose(Pose(position_xyz=(0.15, 0.15, 0.05), rotation_xyzw=(0.0, 0.0, 0.0, 1.0))) | ||
| plate.set_initial_pose(Pose(position_xyz=(0.15, -0.15, 0.02), rotation_xyzw=(0.0, 0.0, 0.0, 1.0))) | ||
|
|
||
| embodiment = G1WBCAgileJointEmbodiment(enable_cameras=ENABLE_CAMERAS) | ||
| embodiment.set_initial_pose(Pose(position_xyz=(-0.4, 0.0, 0.0), rotation_xyzw=(0.0, 0.0, 0.0, 1.0))) |
There was a problem hiding this comment.
Test scene layout differs significantly from the production environment
The test uses the generic table background with the robot at (-0.4, 0, 0) facing +x (identity quaternion), whereas the production environment uses maple_table_robolab with the robot at (1.2, 0, 0) facing -x (180° z-rotation). The tests exercise the underlying component logic well, but they will not catch issues specific to the Seattle Lab table geometry, the actual G1 arm-reach envelope from that position, or the On(table_reference) spatial relation used in the environment. A short comment here explaining why a simplified layout is used would help future contributors understand the coverage gap.
|
Hi @alexmillane — friendly reminder that this PR is ready for your review. All CI checks are passing (pre-commit, tests, GR00T policy tests, docs build). Let me know if you have any questions! Generated by autodev — Claude Code |
Problem
IsaacLab-Arena needs a tabletop manipulation task where the G1 robot uses the WBC-AGILE locomotion policy to pick up an apple and place it on a plate, while balancing in place.
Ref: ISAAC-12630
Solution
Add a new
G1AgileTabletopAppleToPlateEnvironmentthat wires theG1WBCAgileJointEmbodiment(from PR #489) with the existingPickAndPlaceTask, a Seattle Lab table scene, and appropriate object assets.Changes
isaaclab_arena_environments/g1_agile_tabletop_apple_to_plate_environment.py— New environment class: G1 robot at (-0.4, 0, 0) facing a table with an apple (pick object) and a clay plate (target). UsesG1WBCAgileJointEmbodimentfor balance + upper body control. 30-second episodes. Supports--object,--embodiment,--teleop_deviceCLI args.isaaclab_arena_environments/cli.py— Register the new environment in theExampleEnvironmentsdict.isaaclab_arena/tests/test_g1_agile_tabletop_apple_to_plate.py— Two tests: (1) initial state is not terminated (apple starts away from plate), (2) teleporting apple onto plate triggers success termination. Uses correct base-height command (0.75) to keep the robot stable.Testing
Notes
PickAndPlaceTaskhandles contact-sensor success detection, object-dropped termination, and metrics.test_g1_wbc_embodiment.py).Generated by autodev — Claude Code