--- tags: - lerobot - mujoco - unitree-g1 --- # Unitree G1 MuJoCo model for LeRobot The default model has two **Dex1-1 parallel grippers** and three published cameras. The articulated Dex3 hands and the bare no-hand robot are also selectable, and their URDF, MJCF and meshes remain included. This is the model repository loaded dynamically by LeRobot through `env.py`. The existing `make_env()` entry point, 29 body motor commands, body state DDS topics, simulation stepping and ZMQ image message format are preserved. MuJoCo loads `assets/scene_33dof.xml`, which includes the gripper MJCF derived from the supplied URDF. The portable URDF is also included at `assets/g1_29dof_with_dex1_1.urdf`; runtime cameras and actuators are defined in MJCF. ## Choose the end effectors End effectors are named after the hardware. `config.yaml` defaults to `END_EFFECTOR: dex1`. Scene, finger counts, effort limits and the published camera list all follow that selection automatically. | Selection | Runtime scene | Actuated joints | Cameras | | --- | --- | --- | --- | | `dex1` (default) | `assets/scene_33dof.xml` | 29 body + 2 fingers per gripper | head + both wrists | | `dex3` | `assets/scene_hands_cameras.xml` | 29 body + 7 joints per hand | head + both wrists | | `dummy` | `assets/scene_29dof.xml` | 29 body only | head | `dummy` is the bare robot with nothing attached to the wrists, so it publishes only `head_camera`. The earlier names `grippers`, `hands` and `none` are still accepted as aliases for `dex1`, `dex3` and `dummy`. Direct users of this repository can also call: ```python from env import make_env if __name__ == "__main__": env = make_env(end_effector="dex3") # Omit the argument for dex1. try: env.reset() while True: env.step() # Body commands continue to arrive over DDS. finally: env.close() ``` The original `assets/scene_43dof.xml`, `assets/g1_29dof_with_hand.xml`, `assets/g1_body29_hand14.urdf` and no-hand model are retained unchanged. ## Cameras All three streams are enabled by default on **`tcp://127.0.0.1:5555`**, with 640 × 480 images and the existing approximately 30 Hz publishing setting. | Stream name | Mount | | --- | --- | | `head_camera` | Existing head camera | | `left_wrist_cam` | Left gripper base / wrist | | `right_wrist_cam` | Right gripper base / wrist | Each stream is advertised through the same top-level JPEG key and nested `images` / `timestamps` entries as `head_camera`. The existing `view_cameras_live.py` discovers the names from those messages. They are also listed in `env.camera_configs`, `env.camera_names` and `env.metadata["cameras"]`. The wrist cameras move with their respective wrists, with 95° vertical field of view and the approximate extrinsics from the HIW-500 model builder. LeRobot's ZMQ cameras require explicit client configuration, just as the head camera does. Pass this dictionary as `UnitreeG1Config(..., cameras=cameras)`: ```python from lerobot.cameras.zmq.configuration_zmq import ZMQCameraConfig cameras = { name: ZMQCameraConfig( server_address="127.0.0.1", port=5555, camera_name=name, width=640, height=480, fps=30, ) for name in ("head_camera", "left_wrist_cam", "right_wrist_cam") } ``` An equivalent camera configuration is in `lerobot_cameras.json`. `make_env(cameras=["head_camera"])` selects a subset; `publish_images=False` disables publishing. `onscreen=False` disables the viewer for headless use. ## Gripper control Dex1 fingers use force actuators, driven by the existing bridge's external PD controller. They start open at 0.0245 m. To preserve the simulator's hand transport, the first two motor entries on `rt/dex3/left/cmd` and `rt/dex3/right/cmd` command fingers 1 and 2, with matching state topics. Gripper `q` is in metres and `tau` is in newtons; each finger is limited to 20 N. Command both fingers to the same position for symmetric opening or closing. Original hand mode retains all seven rotational command entries per side. The simulation lower limit is -0.023 m, following HIW-500's mesh closure trim. The source and portable URDF retain the official -0.020 m lower limit. Run `python build_gripper_model.py --official-limits` to use that limit in MJCF too; it leaves approximately 5.88 mm between the supplied finger pads. This model update does not add finger actions to LeRobot's 29-body-motor `UnitreeG1` action schema. ## Development and checks Install the existing Unitree SDK2 / CycloneDDS prerequisites, then `python -m pip install -r requirements.txt`. Regenerate the variants with `python build_gripper_model.py`. ```bash MUJOCO_GL=egl python -m unittest discover -s tests -v MUJOCO_GL=egl python tests/smoke_live.py MUJOCO_GL=egl python tests/smoke_live.py --end-effector dex3 ``` The regression suite uses real MuJoCo for both variants, motor/observation mapping, force-controlled closure, wrist camera motion, rendering and camera publisher shared-memory buffers. It isolates DDS with a test double. `smoke_live.py` additionally requires the real Gymnasium, Unitree SDK2 and ZMQ/OpenCV dependencies and checks the live environment and transports. See `VALIDATION.md` for what was executed for this update. ## Sources - Base model/runtime: [lerobot/unitree-g1-mujoco](https://huggingface.co/lerobot/unitree-g1-mujoco/tree/a38dc8617f0fca51b38e9354dc58ee35ad850fb5). - Dex1-1 URDF, meshes and wrist-camera geometry: [Hxxxz0/HIW-500-controoler](https://github.com/Hxxxz0/HIW-500-controoler/tree/c69d89d88bb51774fe9a3684b90d9ff1abf801da). - LeRobot loading and camera configuration checked against [main at b6ec006](https://github.com/huggingface/lerobot/tree/b6ec0060779550c0a157ae34feb89e0cf86012a8). The original Dex1 source URDF, Apache 2.0 license and attribution notice are in `reference/hiw500/`. Unitree mesh assets retain their upstream terms.