# Unitree G1

<video
  src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/lerobot/unitree_g1_passing_can.mp4"
  autoplay
  loop
  muted
  playsinline
  style={{ width: "100%" }}
/>

Unitree G1 휴머노이드는 LeRobot에서 지원됩니다. 원격 조작, 로코마니퓰레이션 정책 학습, 시뮬레이션 내 테스트 등을 수행할 수 있습니다. 29 DoF 및 23 DoF 변형 모델 모두 지원됩니다.

## 구현 선택

`UnitreeG1` 클래스는 `UnitreeG1Config.embodiment`를 통해 관절 정의를 선택합니다. 기본값은 `g1_29`이며, 따라서 기존 구성은 G1-29 동작을 유지합니다. `--robot.embodiment=g1_23` 또는 Python에서 23-DoF 레이아웃을 선택할 수 있습니다:

```python
from lerobot.robots.unitree_g1 import UnitreeG1Config

config = UnitreeG1Config(embodiment="g1_23", is_simulation=False)
```

| 기능                                          | G1-29                         | G1-23                                        |
| --------------------------------------------------- | ----------------------------- | -------------------------------------------- |
| 조인트 구성 및 액션/관측 특징 | 29개의 몸체 관절, 14개의 팔 관절 | 23개의 몸체 관절, 10개의 팔 관절                |
| DDS 명령/피드백 레이아웃                         | 29개의 활성 슬롯               | 29개의 슬롯에서 부재한 조인트를 비활성 상태로 유지    |
| MuJoCo 시뮬레이션                                   | 지원                     | 아직 지원되지 않으며, 네이티브 모델이 계획되어 있습니다      |
| Locomotion controllers                              | GR00T 및 Holosoma            | GR00T 및 Holosoma                           |
| SONIC 전신 컨트롤러                         | 지원                     | 지원되지 않습니다. 정책은 29 DoF를 요구합니다    |
| 중력 보상                                | 지원되며, 기본적으로 비활성화되어 있습니다     | 아직 지원되지 않으며, 네이티브 키네틱스는 계획 중입니다 |

두 변형 모두 동일한 29슬롯 DDS 메시지 레이아웃을 사용합니다. G1-29는 29개 슬롯을 모두 사용하는 반면, G1-23은 23개를 사용하고 존재하지 않는 관절에 해당하는 6개 슬롯을 비활성 상태로 둡니다. 따라서 `kp`, `kd`, `default_positions` 벡터는 여전히 29개 요소의 길이를 유지하며 DDS 슬롯을 인덱스로 사용합니다. G1-23의 활성 팔 슬롯은 `15..19`와 `22..26`입니다. `13, 14, 20, 21, 27, 28` 슬롯에는 활성 관절이 없으며, 해당 설정 값은 0이어야 합니다. 이러한 슬롯은 action/observation feature에서 제외되며 번호가 다시 매겨지지 않습니다. G1-23 메시지를 게시하기 전에 비활성 및 예약된 명령 항목은 초기화됩니다. 명시적인 gain 오버라이드는 유한하고 음이 아닌 값이어야 하며 동일한 sparse layout을 사용해야 합니다. 기본 gain은 두 변형 모두에서 관절 이름으로 공유되므로, 동일한 물리적 관절은 동일한 gain을 사용하고 G1-23에 없는 슬롯은 0으로 유지됩니다.

Native G1-23 MuJoCo 시뮬레이션은 아직 지원되지 않습니다. 시뮬레이션이 활성화된 상태로 G1-23 embodiment를 선택하면 transport 초기화 전에 실패합니다. Native G1-23 MuJoCo 모델은 향후 버전에서 지원될 예정입니다.

---

## Part 1: 시작하기

### Unitree SDK 설치

[unitree_sdk2_python 설치 가이드](https://github.com/unitreerobotics/unitree_sdk2_python#installation)를 따르세요. `unitree_sdk2py==1.0.1` 및 `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 ..
```

### 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]'
```

  현재 arm IK에 필요한 CasADi 바인딩을 포함하려면 pinocchio를 conda-forge(파이프가 아닌)에서 설치해야 합니다.

### 설치 테스트(시뮬레이션)

시뮬레이션 환경에는 자체적인 종속성이 있습니다. 시뮬레이션 환경의 종속성을 확인하세요: [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
```

이 명령을 실행하면 G1에 대한 [MuJoCo sim instance](https://huggingface.co/lerobot/unitree-g1-mujoco/tree/main)가 시작됩니다. 시뮬레이션에서 로봇의 이동 운동을 제어하려면 실행 전에 게임패드를 머신에 연결할 수 있습니다. `--robot.controller`를 통해 [HolosomaLocomotionController](https://github.com/amazon-far/holosoma)와 [GrootLocomotionController](https://github.com/NVlabs/GR00T-WholeBodyControl)를 모두 지원합니다.

- `9`를 눌러 로봇을 해제합니다
- `7` / `8` 키를 눌러 허리 높이를 증가 / 감소

#### 엔드 이펙터 선택

`--robot.end_effector`는 팔이 무엇을 장착하는지 지정합니다: `dex1`은 병렬 그립퍼(기본값), `dex3`는 세 손가락 손, `dummy`는 맨 손목입니다. 시뮬레이션에서는 MuJoCo 모델을 선택하며, 이는 손가락 액추에이터와 장면이 포함하는 카메라를 결정합니다. 손목 카메라는 엔드 이펙터에 장착되므로 `dummy`에는 `head_camera`만 있습니다.

```bash
lerobot-teleoperate \
  --robot.type=unitree_g1 \
  --robot.is_simulation=true \
  --robot.end_effector=dex3 \
  --teleop.id=wbc_unitree \
  --robot.controller=GrootLocomotionController
```

sim은 모델의 카메라를 `--robot.sim_camera_port`(기본값 5555) 포트의 ZMQ를 통해 게시하며, `--robot.cameras`의 `zmq` 카메라는 이 포트에서 이를 읽습니다. MuJoCo의 인터랙티브 창을 대신 열려면 `--robot.sim_publish_images=false`를 전달하세요. 오프스크린 컨텍스트는 뷰어가 아닌 컨텍스트를 생성한 스레드에 속하기 때문에, sim은 카메라와 창 중 하나만 렌더링합니다. `--robot.sim_onscreen`는 카메라에 위임하는 대신 이 선택을 명시적으로 지정하며, 둘 다 요청하는 것은 거부됩니다. 디스플레이가 없는 머신에서 수행할 수 있는 것처럼 둘 다 렌더링하지 않으려면 `--robot.sim_onscreen`와 `--robot.sim_publish_images`를 모두 false로 설정하세요.

### 물리적 로봇에 연결

G1의 Ethernet IP는 `192.168.123.164`로 고정되어 있습니다. 사용자의 머신은 동일한 서브넷의 정적 IP, 즉 `x ≠ 164`인 `192.168.123.x`를 가져야 합니다.

```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로 접속

```bash
ssh unitree@192.168.123.164
# Password: 123
```

### 이더넷으로 인터넷 공유

G1은 저장소를 클론하고 패키지를 설치하기 위해 인터넷 접속이 필요합니다. 노트북의 연결을 이더넷으로 공유하세요:

**노트북에서:**

```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
```

**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
```

### G1에 Unitree SDK 설치

[unitree_sdk2_python 설치 가이드](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 ..
```

### G1에 LeRobot 설치

```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]'
```

  현재 arm IK에 필요한 CasADi 바인딩을 포함하려면 pinocchio를 conda-forge(파이프가 아닌)에서 설치해야 합니다.

### (선택 사항) 로봇에서 WiFi 활성화

무선 SSH 액세스를 위해 G1에서 WiFi를 활성화할 수 있습니다(기본적으로 차단됨):

```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
```

**WiFi 네트워크에 연결:**

```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
```

이후에는 이더넷 대신 WiFi를 통해 SSH로 연결할 수 있습니다:

```bash
ssh unitree@<ROBOT_WIFI_IP>
# Password: 123
```

---

## Part 2: 원격 조작 & 이동

### 로봇 서버 실행

로봇에서(`~/lerobot`에서):

```bash
cd ~/lerobot
python src/lerobot/robots/unitree_g1/run_g1_server.py --camera
```

### Locomotion Policy 실행

Ethernet, WiFi(실험적), 또는 로봇 자체에서 teleoperation 클라이언트를 실행할 수 있습니다. 네트워크로 인해 발생할 수 있는 지연(latency)에 유의하세요.

**노트북에서:**

```bash
lerobot-teleoperate \
  --robot.type=unitree_g1 \
  --robot.is_simulation=false \
  --robot.robot_ip=<ROBOT_IP> \
  --teleop.type=unitree_g1 \
  --teleop.id=wbc_unitree \
  --robot.cameras='{"global_view": {"type": "zmq", "server_address": "<ROBOT_IP>", "port": 5555, "camera_name": "head_camera", "width": 640, "height": 480, "fps": 30}}' \
  --display_data=true \
  --robot.controller=HolosomaLocomotionController
```

`--robot.controller`를 통해 [GrootLocomotionController](https://github.com/NVlabs/GR00T-WholeBodyControl)와 [HolosomaLocomotionController](https://github.com/amazon-far/holosoma)를 모두 지원합니다.

---

## Part 3: Homunculus Exoskeleton를 이용한 Loco-Manipulation

Homunculus Exoskeleton를 통해 loco-manipulation 솔루션을 제공합니다. 이는 전체 신체를 제어하기 위한 오픈소스 7 DoF 엑소스켈레톤입니다. [여기](https://github.com/nepyope/hmc_exo)에서 확인해 보세요.

### 교정

```bash
lerobot-calibrate \
  --teleop.type=unitree_g1 \
  --teleop.left_arm_config.port=/dev/ttyACM1 \
  --teleop.right_arm_config.port=/dev/ttyACM0 \
  --teleop.id=exo
```

교정 중에 각 관절을 전체 범위로 이동합니다. 조정이 완료되면 관절을 중립 위치로 이동한 후 `n`을 눌러 다음으로 진행합니다.

### 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
```

> **참고:** 조이스틱만 사용하는 경우 `--teleop.left_arm_config.port`와 `--teleop.right_arm_config.port`를 생략하세요.

예시 데이터셋: [nepyope/unitree_box_move_blue_full](https://huggingface.co/datasets/nepyope/unitree_box_move_blue_full)

---

## Part 4: 학습 및 추론

### 학습

```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 \
  --trackio.enable=true \
  --policy.dtype=bfloat16 \
  --policy.freeze_vision_encoder=false \
  --policy.train_expert_only=false \
  --steps=3000 \
  --policy.device=cuda \
  --batch_size=32
```

### RTC를 사용한 추론

호환되는 학습된 정책은 로컬 추론 시 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": "<ROBOT_IP>", "port": 5555, "camera_name": "head_camera", "width": 640, "height": 480, "fps": 30}}' \
  --task="task_description" \
  --duration=1000 \
  --fps=30 \
  --inference.type=rtc
```

G1 locomotion 컨트롤러는 rollout의 recoverable waiting에서 사용하는 retained-target 계약을 선언하지 않습니다. 동작이 시작된 후, action buffer가 소진되면 grace-period 대기 대신 terminal fault가 발생합니다. 요청 마감 시간과 관찰 연령 제한도 적용됩니다. 이 레시피를 사용하기 전에 정책의 사용 가능한 chunk가 추론 지연을 커버하는지 검증하십시오. [로컬 RTC 대기 및 예산](./inference#inference-backends)을 참조하십시오. SONIC에는 아래에 설명된 별도의 retained-token 계약이 있습니다.

---

## Part 5: SONIC 전신 제어(잠재 토큰 정책)

LeRobot는 이제 NVIDIA의 [SONIC](https://nvlabs.github.io/GEAR-SONIC/) 전체 본체 배포 스택의 **decoder** 부분을 `SonicWholeBodyController`로 이식했습니다. 관절을 직접 명령하는 대신, 정책은 각 틱마다 64차원 **latent motion token**(`motion_token.{i}.pos`)을 출력합니다. SONIC decoder는 해당 토큰과 최근 고유수용감각(proprioception) 히스토리를 받아 잔차(residual) 동작을 생성하며, 이 동작은 스케일링된 후 서 있는 자세에 더해져 29개 자유도(DoF) 전체에 대한 50 Hz 관절 위치 목표를 산출합니다.

컨트롤러는 ONNX 디코더와 모든 배포 상수(PD 이득, `default_angles`, `action_scale`, 그리고 중립 토큰)를 [`lerobot/sonic_decoder`](https://huggingface.co/lerobot/sonic_decoder)에서 로드합니다. 저장소에는 컨트롤러의 `policy_type="low_latency"` 인수로 선택할 수 있는 증류된 저지연 디코더도 함께 제공됩니다(기본값은 `"default"`).

### 시뮬레이션에서 테스트

SONIC 모션 토큰으로 학습된 모든 토큰 출력 정책(예: [`nepyope/sonic_walk`](https://huggingface.co/nepyope/sonic_walk))이 이를 구동할 수 있습니다:

```bash
lerobot-rollout \
  --strategy.type=base \
  --policy.path=nepyope/sonic_walk \
  --policy.device=cuda \
  --robot.type=unitree_g1 \
  --robot.is_simulation=true \
  --robot.controller=SonicWholeBodyController \
  --robot.cameras='{"ego_view": {"type": "zmq", "server_address": "localhost", "port": 5555, "camera_name": "head_camera", "width": 640, "height": 480, "fps": 30, "warmup_s": 15}}'
```

### 실제 로봇에서 실행

GPU 머신에서 [remote inference](./remote_inference) 정책 서버를 실행하고, 로봇 브리지, SONIC 디코더 및 rollout 클라이언트는 G1 Jetson에 유지합니다. 네트워크를 통해 오가는 것은 64-D 토큰 청크뿐이며, 디코더와 50 Hz `rt/lowcmd` 루프는 온보드에서 유지됩니다.

두 컴퓨터 모두에 remote extra를 설치하세요. G1에는 Unitree extra를 유지하세요:

```bash
# GPU machine
python -m pip install -e '.[remote]'

# G1 Jetson
python -m pip install -e '.[unitree_g1,remote]'
```

GPU 머신에서 저장소 루트에서 정책 서버를 시작합니다. 유지 관리되는 프로필은 `nepyope/sonic_walk`와 일치합니다. 다른 정책을 사용하려면 해당 체크포인트와 기능 계약을 함께 복사하고 업데이트하세요.

```bash
lerobot-policy-server \
  --config_path=examples/remote_inference/unitree_g1_sonic.yaml
```

G1에서는 브리지와 카메라를 계속 실행 상태로 유지하세요:

```bash
python src/lerobot/robots/unitree_g1/run_g1_server.py --camera
```

그 다음 두 번째 G1 터미널에서 rollout 클라이언트를 시작하고 `<GPU_MACHINE_IP>`를 대체합니다:

```bash
lerobot-rollout \
  --strategy.type=base \
  --inference.type=remote \
  --inference.endpoint=tcp/<GPU_MACHINE_IP>:7447 \
  --inference.deployment=g1-sonic \
  --inference.semantics=unitree-g1-sonic-motion-token-v1 \
  --inference.encoding=jpeg \
  --inference.action_starvation_grace_s=1.0 \
  --robot.type=unitree_g1 \
  --robot.is_simulation=false \
  --robot.robot_ip=127.0.0.1 \
  --robot.controller=SonicWholeBodyController \
  --robot.cameras='{"ego_view": {"type": "zmq", "server_address": "127.0.0.1", "port": 5555, "camera_name": "head_camera", "width": 640, "height": 480, "fps": 30, "warmup_s": 5}}' \
  --task='walk forward' \
  --fps=30 \
  --return_to_initial_position=false \
  --interactive=true
```

G1의 이동이 명확해졌을 때에만 `/start`를 입력하십시오. SONIC는 마지막 완전한 토큰을 재전송하면 온보드 디코더의 밸런싱이 유지되므로 대기 지원을 선언합니다. 첫 번째 정책 결과가 나오기 전에 rollout은 디코더의 중립 토큰을 전송합니다. 액션 버퍼가 소진되면, 마지막에 적용된 토큰을 최대 `action_starvation_grace_s`(위에서 1초) 동안 반복합니다. 새로운 예측이 도착하지 않으면 rollout이 장애를 일으키며 구성된 종료/연결 해제 경로를 따릅니다. 조이스틱 속도를 이 방식으로 유지할 수 없으므로 G1 로코모션 컨트롤러는 여전히 지원되지 않습니다.

이 레시피는 초기 위치 복귀를 비활성화합니다. SONIC의 잠재 토큰 공간을 보간하는 것은 검증된 물리적 복귀 궤적이 아니기 때문입니다. 같은 이유로 `/reset`도 피해야 합니다. 이는 토큰을 연결 시 값으로 되돌리기 때문입니다. `/stop`은 여전히 원격 세션을 종료하고 일반적인 컨트롤러 종료 경로를 통해 로봇을 연결 해제합니다.

`SonicWholeBodyController`를 사용하면 관찰 상태는 29개의 관절 위치가 아니라 64차원 토큰 에코이므로, 해당 상태에 대해 학습된 호환 가능한 체크포인트가 필요합니다. 연결 시 로봇은 컨트롤러가 제어권을 넘겨받기 전에 관절을 디코더의 `default_angles`으로 부드럽게 이동시킵니다.

---

## 추가 자료

- [Unitree SDK 문서](https://github.com/unitreerobotics/unitree_sdk2_python)
- [GR00T-WholeBodyControl](https://github.com/NVlabs/GR00T-WholeBodyControl)
- [Holosoma](https://github.com/amazon-far/holosoma)
- [LeRobot 문서](https://github.com/huggingface/lerobot)
- [Unitree IL LeRobot](https://github.com/unitreerobotics/unitree_IL_lerobot)

---

_마지막 업데이트: 2026년 7월_

