# Unitree G1 Unitree G1 locomanipulation demo The Unitree G1 humanoid is now supported in LeRobot! You can teleoperate, train locomanipulation policies, test in sim, and more. Both 29 and 23 DoF variants are supported. **New: SONIC whole-body control.** The `SonicWholeBodyController` runs NVIDIA's [GEAR-SONIC](https://huggingface.co/nvidia/GEAR-SONIC) decoder on the G1, turning a 64-D latent motion token into full-body joint targets at 50 Hz. This lets you drive the robot from a VLA policy trained on SONIC motion tokens (token in → whole-body motion out) with `lerobot-rollout`, in sim or on the physical robot. See [Whole-body control with SONIC](#whole-body-control-with-sonic) below. --- ## Part 1: Getting Started ### Install the Unitree SDK Follow the [unitree_sdk2_python installation guide](https://github.com/unitreerobotics/unitree_sdk2_python#installation). Tested with `unitree_sdk2py==1.0.1` and `cyclonedds==0.10.2`: ```bash conda create -y -n lerobot python=3.12 conda activate lerobot git clone https://github.com/unitreerobotics/unitree_sdk2_python.git cd unitree_sdk2_python pip install -e . cd .. ``` ### Install LeRobot ```bash conda install ffmpeg -c conda-forge conda install -c conda-forge "pinocchio>=3.0.0,<4.0.0" git clone https://github.com/huggingface/lerobot.git cd lerobot pip install -e '.[unitree_g1]' ``` For now, pinocchio must be installed from conda-forge (not pip) to include the CasADi bindings needed for arm IK. ### Test the Installation (Simulation) The simulation environment has its own dependencies. Check the Simulation environment dependencies: [Unitree G1 Mujoco EnvHub](https://huggingface.co/lerobot/unitree-g1-mujoco/tree/main). ```bash pip install mujoco loguru msgpack msgpack-numpy ``` ```bash lerobot-teleoperate \ --robot.type=unitree_g1 \ --robot.is_simulation=true \ --teleop.type=unitree_g1 \ --teleop.id=wbc_unitree \ --robot.cameras='{"global_view": {"type": "zmq", "server_address": "localhost", "port": 5555, "camera_name": "head_camera", "width": 640, "height": 480, "fps": 30, "warmup_s": 5}}' \ --display_data=true \ --robot.controller=GrootLocomotionController ``` This will launch a [MuJoCo sim instance](https://huggingface.co/lerobot/unitree-g1-mujoco/tree/main) for the G1. You can connect a gamepad to your machine before launching in order to control the robot's locomotion in sim. We support [HolosomaLocomotionController](https://github.com/amazon-far/holosoma), [GrootLocomotionController](https://github.com/NVlabs/GR00T-WholeBodyControl), and [SonicWholeBodyController](https://huggingface.co/nvidia/GEAR-SONIC) via `--robot.controller`. - Press `9` to release the robot - Press `7` / `8` to increase / decrease waist height ### Connect to the Physical Robot The G1's Ethernet IP is fixed at `192.168.123.164`. Your machine must have a static IP on the same subnet: `192.168.123.x` where `x ≠ 164`. ```bash # Replace 'enp131s0' with your ethernet interface name (check with `ip a`) sudo ip addr flush dev enp131s0 sudo ip addr add 192.168.123.200/24 dev enp131s0 sudo ip link set enp131s0 up ``` ### SSH into the Robot ```bash ssh unitree@192.168.123.164 # Password: 123 ``` ### Share Internet via Ethernet The G1 needs internet access to clone repos and install packages. Share your laptop's connection over Ethernet: **On your laptop:** ```bash sudo sysctl -w net.ipv4.ip_forward=1 # Replace wlp132s0f0 with your WiFi interface name sudo iptables -t nat -A POSTROUTING -o wlp132s0f0 -s 192.168.123.0/24 -j MASQUERADE sudo iptables -A FORWARD -i wlp132s0f0 -o enp131s0 -m state --state RELATED,ESTABLISHED -j ACCEPT sudo iptables -A FORWARD -i enp131s0 -o wlp132s0f0 -j ACCEPT ``` **On the G1:** ```bash sudo ip route del default 2>/dev/null || true sudo ip route add default via 192.168.123.200 dev eth0 echo "nameserver 8.8.8.8" | sudo tee /etc/resolv.conf # Verify ping -c 3 8.8.8.8 ``` ### Install the Unitree SDK on the G1 Follow the [unitree_sdk2_python installation guide](https://github.com/unitreerobotics/unitree_sdk2_python#installation): ```bash conda create -y -n lerobot python=3.12 conda activate lerobot git clone https://github.com/unitreerobotics/unitree_sdk2_python.git cd unitree_sdk2_python python -m pip install -e . cd .. ``` ### Install LeRobot on the G1 ```bash git clone https://github.com/huggingface/lerobot.git cd lerobot conda install -c conda-forge "pinocchio>=3.0.0,<4.0.0" python -m pip install -e '.[unitree_g1]' ``` For now, pinocchio must be installed from conda-forge (not pip) to include the CasADi bindings needed for arm IK. ### (Optional) Enable WiFi on the Robot For wireless SSH access, you can enable WiFi on the G1 (it's blocked by default): ```bash sudo rfkill unblock all sudo ip link set wlan0 up sudo nmcli radio wifi on sudo nmcli device set wlan0 managed yes sudo systemctl restart NetworkManager ``` **Connect to a WiFi network:** ```bash nmcli device wifi list sudo nmcli connection add type wifi ifname wlan0 con-name "YourNetwork" ssid "YourNetwork" sudo nmcli connection modify "YourNetwork" wifi-sec.key-mgmt wpa-psk sudo nmcli connection modify "YourNetwork" wifi-sec.psk "YourPassword" sudo nmcli connection modify "YourNetwork" connection.autoconnect yes sudo nmcli connection up "YourNetwork" ip a show wlan0 ``` You can then SSH over WiFi instead of Ethernet: ```bash ssh unitree@ # Password: 123 ``` --- ## Part 2: Teleoperation & Locomotion ### Run the Robot Server On the robot (from `~/lerobot`): ```bash cd ~/lerobot python src/lerobot/robots/unitree_g1/run_g1_server.py --camera ``` ### Run the Locomotion Policy You can run the teleoperation client from your laptop over Ethernet, over WiFi (experimental), or directly on the robot itself. Mind potential latency introduced by your network. **From your laptop:** ```bash lerobot-teleoperate \ --robot.type=unitree_g1 \ --robot.is_simulation=false \ --robot.robot_ip= \ --teleop.type=unitree_g1 \ --teleop.id=wbc_unitree \ --robot.cameras='{"global_view": {"type": "zmq", "server_address": "", "port": 5555, "camera_name": "head_camera", "width": 640, "height": 480, "fps": 30}}' \ --display_data=true \ --robot.controller=HolosomaLocomotionController ``` We support both [GrootLocomotionController](https://github.com/NVlabs/GR00T-WholeBodyControl) and [HolosomaLocomotionController](https://github.com/amazon-far/holosoma) via `--robot.controller`. --- ## Part 3: Loco-Manipulation with the Homunculus Exoskeleton We provide a loco-manipulation solution via the Homunculus Exoskeleton — an open-source 7 DoF exoskeleton for whole-body control. Check it out [here](https://github.com/nepyope/hmc_exo). ### Calibrate ```bash lerobot-calibrate \ --teleop.type=unitree_g1 \ --teleop.left_arm_config.port=/dev/ttyACM1 \ --teleop.right_arm_config.port=/dev/ttyACM0 \ --teleop.id=exo ``` During calibration move each joint through its entire range. After fitting, move the joint in a neutral position and press `n` to advance. ### Record a Dataset ```bash lerobot-record \ --robot.type=unitree_g1 \ --robot.is_simulation=true \ --robot.cameras='{"global_view": {"type": "zmq", "server_address": "localhost", "port": 5555, "camera_name": "head_camera", "width": 640, "height": 480, "fps": 30}}' \ --teleop.type=unitree_g1 \ --teleop.left_arm_config.port=/dev/ttyACM1 \ --teleop.right_arm_config.port=/dev/ttyACM0 \ --teleop.id=exo \ --dataset.repo_id=your-username/dataset-name \ --dataset.single_task="Test" \ --dataset.num_episodes=2 \ --dataset.episode_time_s=5 \ --dataset.reset_time_s=5 \ --dataset.push_to_hub=true \ --dataset.streaming_encoding=true \ --dataset.encoder_threads=2 ``` > **Note:** Omit `--teleop.left_arm_config.port` and `--teleop.right_arm_config.port` if you're only using the joystick. Example dataset: [nepyope/unitree_box_move_blue_full](https://huggingface.co/datasets/nepyope/unitree_box_move_blue_full) --- ## Part 4: Training & Inference ### Train ```bash python src/lerobot/scripts/lerobot_train.py \ --dataset.repo_id=your-username/dataset-name \ --policy.type=pi05 \ --output_dir=./outputs/pi05_training \ --job_name=pi05_training \ --policy.repo_id=your-username/your-repo-id \ --policy.pretrained_path=lerobot/pi05_base \ --policy.compile_model=true \ --policy.gradient_checkpointing=true \ --wandb.enable=true \ --policy.dtype=bfloat16 \ --policy.freeze_vision_encoder=false \ --policy.train_expert_only=false \ --steps=3000 \ --policy.device=cuda \ --batch_size=32 ``` ### Inference with RTC Once trained, we recommend deploying policies using inference-time RTC: ```bash lerobot-rollout \ --strategy.type=base \ --policy.path=your-username/your-repo-id \ --policy.device=cuda \ --robot.type=unitree_g1 \ --robot.is_simulation=false \ --robot.controller=HolosomaLocomotionController \ --robot.cameras='{"global_view": {"type": "zmq", "server_address": "", "port": 5555, "camera_name": "head_camera", "width": 640, "height": 480, "fps": 30}}' \ --task="task_description" \ --duration=1000 \ --fps=30 \ --inference.type=rtc ``` --- ## Whole-body control with SONIC The `SonicWholeBodyController` runs NVIDIA's [GEAR-SONIC](https://huggingface.co/nvidia/GEAR-SONIC) decoder on the G1. Each 50 Hz tick it consumes a **64-D latent motion token** and emits full-body joint targets — the encoder is bypassed, so a policy feeds tokens in and the decoder turns them into motion. Before the first token arrives the controller holds a neutral (idle) pose. This makes the G1 drivable by a VLA policy trained to output SONIC motion tokens (token as both `observation.state` and `action`, e.g. [`nepyope/sonic_walk`](https://huggingface.co/nepyope/sonic_walk)) using the standard `lerobot-rollout`. The controller always runs **onboard** the robot; the laptop is a thin client that streams tokens and receives camera frames over ZMQ. **On the robot** — start the server in handshake mode so it instantiates and runs the controller onboard against local DDS at full rate: ```bash cd ~/lerobot python src/lerobot/robots/unitree_g1/run_g1_server.py --handshake --camera ``` **From your laptop** — run the token policy; `--robot.sonic_token_action=true` switches the robot to the 64-D latent-token action/observation interface: ```bash lerobot-rollout \ --policy.path=nepyope/sonic_walk \ --policy.device=cuda \ --robot.type=unitree_g1 \ --robot.is_simulation=false \ --robot.robot_ip= \ --robot.controller=SonicWholeBodyController \ --robot.sonic_token_action=true \ --robot.cameras='{"ego_view": {"type": "zmq", "server_address": "", "port": 5555, "camera_name": "head_camera", "width": 640, "height": 480, "fps": 30}}' \ --task="walk back and forth" \ --duration=1000 \ --fps=30 ``` SONIC is a token-only decoder in LeRobot: the only input path is the 64-D latent vector. To train your own token policy, expose the 64-D token as the action (a config choice, e.g. `pi05` with a 64-D action dim) — no policy code changes are needed. --- ## Additional Resources - [Unitree SDK Documentation](https://github.com/unitreerobotics/unitree_sdk2_python) - [GR00T-WholeBodyControl](https://github.com/NVlabs/GR00T-WholeBodyControl) - [Holosoma](https://github.com/amazon-far/holosoma) - [LeRobot Documentation](https://github.com/huggingface/lerobot) - [Unitree IL LeRobot](https://github.com/unitreerobotics/unitree_IL_lerobot) --- _Last updated: March 2026_