X-VLA SimplerEnv Guide#

Before running xvla + SimplerEnv evaluation, execute the check in the Patch requirement check section below and confirm the absolute EE controller is present. Skipping this usually surfaces as a 0% success run after hundreds of steps.

Impact boundary#

  • The patches only (a) register one new controller entry and (b) change the dead non-delta branch of PDEEPoseController.compute_target_pose (use_delta=False, unreachable on stock upstream).

  • Delta control modes (arm_pd_ee_target_delta_pose_align2_*, used by pi05 and GR00T) are unaffected; models on the delta path can share the same environment.

  • Written against ManiSkill2_real2sim@54ae2e0e (the official 255isWhite fork pin). Option 2 strictly follows that fork; apply on the same base version or verify the surrounding code first.

Background#

The official X-VLA SIMPLER evaluation (evaluation/simpler) does not use upstream simpler-env/SimplerEnv. It uses the official fork 255isWhite/SimplerEnv (including the ManiSkill2_real2sim submodule pinned at commit 54ae2e0e). From the official README:

The evaluation follows SimplerEnv, with minor environment modifications to support absolute end-effector (EE) control.

Reason: X-VLA emits absolute EE poses (target position + Euler orientation in the base frame), while upstream SimplerEnv WidowX only provides delta control modes. Mismatch leads to two failure modes:

Environment state

Symptom

Upstream + config arm_pd_ee_target_base_pose_*

KeyError at env create — control mode missing; eval never starts

Upstream + fallback delta mode (e.g. arm_pd_ee_target_delta_pose_align2_*)

Runs, but absolute actions are applied as deltas; arm flies out on the first step; success rate stays 0

Measured on 2026-07-21: before the patch, stack_cube failed for all steps through 120/300; after the patch and switching to absolute control, episode 0 succeeded in 55 steps.

Patch requirement check#

Run (replace python and paths):

python -c "
import sys
sys.path.insert(0, '/path/to/SimplerEnv')
sys.path.insert(0, '/path/to/SimplerEnv/ManiSkill2_real2sim')
from mani_skill2_real2sim.agents.configs.widowx.defaults import WidowXDefaultConfig
print('arm_pd_ee_target_base_pose_gripper_pd_joint_pos' in WidowXDefaultConfig().controllers)
"
  • Prints True: nothing to do (official fork or already patched).

  • Prints False: apply one of the two options below.

Option 1: use the official fork#

This is the recommended path.

git clone https://github.com/255isWhite/SimplerEnv.git --recurse-submodules
cd SimplerEnv/ManiSkill2_real2sim && pip install -e .
cd .. && pip install -e .

Point env.simplerenv_root in the eval YAML at that directory.

Option 2: apply manual patches#

These changes strictly follow the official fork (simpler-env/ManiSkill2_real2sim@54ae2e0e). Delta control modes are unchanged (pi0.5 and other models on the delta path are unaffected).

Patch 1: register absolute EE pose controller#

File: mani_skill2_real2sim/agents/configs/widowx/defaults.py

Add a controller config in WidowXDefaultConfig.controllers and insert it into _C["arm"]:

# 255isWhite/SimplerEnv fork (X-VLA): absolute EE pose control in base frame
arm_pd_ee_target_base_pose = PDEEPoseControllerConfig(
    *arm_common_args, frame="base", use_target=True, use_delta=False, **arm_common_kwargs
)

_C["arm"] = dict(
    ...,  # keep existing entries
    arm_pd_ee_target_base_pose=arm_pd_ee_target_base_pose,
)

The combined control mode used by the eval YAML is arm_pd_ee_target_base_pose_gripper_pd_joint_pos.

Patch 2: parse rotation as Euler xyz on the non-delta branch#

File: mani_skill2_real2sim/agents/controllers/pd_ee_pose.py

In PDEEPoseController.compute_target_pose, on the use_delta=False branch, parse rotation as Euler xyz instead of rotvec (axis-angle):

else:
    assert self.config.frame == "base", self.config.frame
    target_pos, target_rot = action[0:3], action[3:6]
    # 255isWhite/SimplerEnv fork (X-VLA): absolute action rotation is Euler xyz
    target_quat = Rotation.from_euler("xyz", target_rot).as_quat()[[3, 0, 1, 2]]
    target_pose = sapien.Pose(target_pos, target_quat)

Why this is required: the X-VLA client protocol outputs Euler xyz (rot6d → euler, then add bias [0, π/2, 0]; see post-processor ee6d_to_simpler_abs_euler). Upstream dead code used from_rotvec on the same three numbers, which yields a totally different orientation — position may look roughly right while the gripper is twisted, IK fails often, and grasps fail.

Note: on stock upstream, no control mode reaches this non-delta branch (dead code), so the edit does not change any existing upstream behavior.

Eval config#

See examples/embodied/xvla/eval/configs/simplerenv/widowx_stack_cube_smoke.yaml:

benchmark:
  control_mode: arm_pd_ee_target_base_pose_gripper_pd_joint_pos  # needs patch 1
  max_steps: 1200          # official horizon
  robot_init_x: 0.147
  robot_init_y: 0.028
model:
  model_type: xvla
  state_encoding: ee6d_widowx   # WidowX proprio (closed-loop backfill)
  action_encoding: ee6d         # decoder auto-composed → ee6d_to_simpler_abs_euler
  domain_id: 0                  # WidowX (Bridge)

Rollback#

Patches are not committed to git; inspect or restore with:

git -C /path/to/SimplerEnv/ManiSkill2_real2sim diff      # inspect
git -C /path/to/SimplerEnv/ManiSkill2_real2sim checkout -- .  # restore

References#