스쿼트는 쉬워 보입니다. 사람은 의자에 앉듯 무릎을 굽히고 다시 일어납니다. 화면 속 휴머노이드도 조금만 내려가면 된다고 생각하기 쉽습니다. 실제로는 전혀 달랐습니다. 로봇에게 스쿼트는 "낮아지는 동작"이 아닙니다. 발을 붙인 채 균형을 잃지 않고, 무릎과 고관절을 충분히 접고, 다시 서는 일입니다.
처음에는 저도 높이만 봤습니다. 골반이 내려가면 스쿼트에 가까워졌다고 생각했습니다. 그런데 실험이 쌓이면서 기준이 바뀌었습니다. 1cm 내려간 안정적인 동작은 스쿼트가 아니었습니다. 8cm 이상 내려가도 발이 미끄러지거나 돌아오지 못하면 실패였습니다. 무릎이 충분히 접히지 않아도 실패로 봤습니다.
결국 성공은 한 장면이 아니라 gate였습니다. pelvis drop, knee flexion, hip pitch가 먼저 필요했습니다. 여기에 contact, slip, return, browser replay도 같이 지나가야 했습니다. 122번째 실험에서야 그 묶음이 닫혔습니다.
- 01G1 스쿼트는 한 번의 성공 장면이 아니라, 실패 기준을 계속 더 엄격하게 만든 끝에 통과한 검증 문제였습니다.
- 02초기 controller와 reward 실험은 안정적이지만 얕거나, 깊지만 넘어지거나, 접촉과 복귀를 잃었습니다.
- 03마지막 통과 지점은 GR00T Decoupled WBC measured MuJoCo trace였습니다. 11.6cm 하강, 무릎 0.707rad, 고관절 0.427rad, 양발 접촉 1.00, 발 미끄러짐 0.003m를 기록했습니다.
- 04이 결과는 실제 로봇 텔레메트리가 아니라 시뮬레이션 기준의 visible squat입니다. 그래도 브라우저에서 재생 가능한 검증 산출물로 남겼습니다.

실패는 처음부터 같은 모양이 아니었습니다
처음 목표는 단순했습니다. Unitree G1 모델에서 g1_squat이라는 skill을 만들고, 브라우저에서 보여줄 수 있는 자세 전환으로 닫는 것. 걷기 정책은 이미 있었고, 디지털 트윈도 있었습니다. 다음 단계는 "걷는 로봇이 원하는 자세를 할 수 있는가"였습니다.
처음 만든 scripted baseline은 1.24초 전후로 넘어졌습니다. scratch PPO도, recovery reward도, reference tracking도 같은 벽에 부딪혔습니다. 기존 G1 walking policy를 stabilizer prior로 가져오면 6초 동안 넘어지지 않는 standing은 만들 수 있었습니다. 문제는 그다음입니다. 그 로봇은 거의 내려가지 않았습니다.
이때 생긴 첫 교훈은 분명했습니다. 안정성만 보면 로봇은 서 있기만 합니다. 높이 reward를 키우면 내려가지만 곧 넘어집니다. 스쿼트는 두 목표 사이의 평균이 아닙니다. 둘을 동시에 만족해야 하는 동작입니다.
그래서 실험의 언어를 바꿨습니다. g1_squat을 "낮아지는 것"으로 두지 않았습니다. controlled lowering과 hold를 분리했습니다. return도 별도 기준으로 뺐습니다. 내려가고, 잠깐 버티고, 다시 서야 합니다. 이 정의가 없으면 작은 흔들림도 성공처럼 보입니다.
- 01자연어 목표
- 02behavior spec
- 03native rollout
- 04visible gate
- 05browser replay
- 06public artifact
1cm는 성공이 아니었습니다
한 번은 성공처럼 보인 적이 있었습니다. calibrated reference controller로 6초 no-fall을 만들었고, foot contact도 유지됐습니다. 숫자만 보면 stage 0.74 gate를 통과했습니다. 브라우저 replay에도 연결했습니다. 이름도 g1-controlled-squat으로 붙였습니다.
그런데 화면에서 보니 거의 앉지 않았습니다. 사용자 visual review와 trajectory audit을 다시 해보니 약 1cm micro-dip이었습니다. 안정적인 lowering probe였지만 스쿼트라고 부르기에는 부족했습니다. 이 지점이 중요했습니다. 코드가 PASS라고 말해도 사람이 보면 아닌 경우가 있습니다.
이후 exp29에서 visible squat gate를 새로 만들었습니다. 골반은 8cm 이상 내려가야 합니다. 무릎 굴곡은 0.60rad 이상이 기준입니다. 고관절 pitch도 0.35rad 이상이어야 합니다. 단순 높이 대신 사람 눈에 보이는 자세 변화를 수치로 묶은 셈입니다.
이 gate는 이후 모든 실험을 괴롭혔습니다. weak blend는 안정적이지만 1.2cm만 내려갔습니다. strong blend는 visible depth로 들어가지만 2초쯤 넘어졌습니다. guarded descent는 8.7cm를 만들었습니다. 대신 contact가 0.85로 떨어졌고 foot slip이 1.267m까지 튀었습니다. stance-anchor와 early-return도 fall을 피하지 못했습니다.
| 단계 | 겉으로 보인 진전 | 실패 이유 |
|---|---|---|
| 초기 PPO와 reference tracking | 스쿼트 목표를 reward에 넣음 | 1.22초에서 1.25초 사이 fall |
| stabilizer prior | 6초 no-fall standing 확보 | height drop이 거의 없음 |
| stage 0.74 controller | 브라우저 replay까지 연결 | 약 1cm micro-dip이라 visible squat 실패 |
| visible-depth controller | 8cm 이상 내려가는 branch 발견 | fall, slip, contact, return 중 하나가 깨짐 |
| Decoupled WBC trace | 11.6cm drop과 자세 gate 통과 | 실물 텔레메트리는 아니므로 별도 future work |
문제는 자세가 아니라 동역학이었습니다
중간에 중요한 분리 작업도 있었습니다. 혹시 G1 모델 자체가 visible squat 자세를 담을 수 없는 것은 아닐까. 이 질문을 확인하지 않으면 계속 엉뚱한 controller만 고치게 됩니다.
정적 검사에서는 가능성이 보였습니다. local G1 lower-body joint range는 visible squat target 후보를 담을 수 있었습니다. static inverse-dynamics contact QP에서도 visible pose 자체는 plausible했습니다. 즉 병목은 "그 자세가 수학적으로 불가능하다"가 아니었습니다.
진짜 병목은 동역학과 접촉이었습니다. 깊게 내려갈수록 support와 ZMP가 흔들렸습니다. contact force와 foot slip도 같이 움직였습니다. return phase는 더 예민했습니다. 어떤 실험은 contact와 slip을 잘 지키면 얕아졌습니다. 어떤 실험은 20cm 넘게 내려갔지만 무릎이나 고관절 gate가 맞지 않았습니다. return을 못 하는 branch도 많았습니다. 깊이 하나만 밀면 다른 축이 무너졌습니다.
이때부터 실험은 reward scale 싸움이 아니게 됐습니다. controller와 selector를 분리했습니다. stance curriculum과 qfrc도 따로 봤습니다. contact-force audit도 별도로 기록했습니다. QP-lite와 static ID-QP도 붙였습니다. dynamic tracking, retrain, GR00T adapter까지 갈라졌습니다. 실패가 많아진 것이 아니라 질문이 더 잘게 나뉜 셈입니다.
마지막 통과는 Decoupled WBC였습니다
GR00T/SONIC 쪽으로 넘어간 이유는 단순했습니다. local controller family가 계속 contact, slip, return을 동시에 못 닫았기 때문입니다. NVIDIA의 GR00T Whole-Body Control 문서에는 Decoupled WBC가 Unitree G1을 중심으로 whole-body control policies를 제공한다고 적혀 있습니다. teleoperation stack과 data exporter도 함께 제공합니다. 이쪽 trace를 현재 browser contract에 넣을 수 있는지 보기 시작했습니다.
처음부터 곧장 성공한 것은 아니었습니다. WSL runtime과 Git LFS를 따로 확인했습니다. Hugging Face model download도 점검 대상이었습니다. MuJoCo sim runtime도 먼저 열어야 했습니다. C++ deployment path는 TensorRT가 없어 막혔습니다. 최종 실험에서는 GR00T Decoupled WBC의 G1 Balance ONNX를 WSL-native MuJoCo에서 headless로 돌렸습니다.
pelvis height command를 네 가지로 나눴습니다. low_0p70, low_0p68, low_0p66, low_0p64였습니다. 첫 1초 settle 구간은 버렸습니다. 그 뒤 50Hz로 6초 measured qpos trace를 평가했습니다. 그중 low_0p64가 처음으로 모든 gate를 통과했습니다.
| Metric | Result | Gate |
|---|---|---|
| Pelvis drop | 0.1159m | >= 0.0800m |
| Knee flexion delta | 0.7069rad | >= 0.6000rad |
| Hip pitch delta | 0.4272rad | >= 0.3500rad |
| Final height error | 0.0036m | <= 0.0150m |
| Bilateral contact ratio | 1.000 | >= 0.950 |
| Max foot slip | 0.0028m | <= 0.0500m |
여기서 끝내지 않았습니다. trace를 physical-ai-web-trajectory-v1로 변환하고, g1-decoupled-wbc-squat이라는 browser replay로 등록했습니다. Playwright visual QA는 300 frames를 확인했습니다. nq=36이었고 console errors는 0이었습니다. 지금은 https://robotics.askewly.com/?exp=g1-decoupled-wbc-squat에서 볼 수 있습니다.
122번이라는 숫자가 남긴 것
122번이라는 숫자는 멋진 집념의 표식처럼 보일 수 있습니다. 실제로는 조금 다릅니다. 같은 실험을 122번 반복한 것이 아닙니다. 실패가 나올 때마다 성공의 정의를 다시 쪼갰고, 쪼개진 기준마다 별도 실험이 생겼습니다.
처음에는 fall time을 봤습니다. 그다음에는 no-fall과 height를 함께 봤습니다. micro-dip을 거친 뒤에는 visible depth가 들어왔습니다. visible depth를 통과하자 contact와 slip이 문제로 올라왔습니다. contact와 slip을 잡으니 return이 남았습니다. return을 보니 browser replay가 필요했습니다.
이 흐름은 로봇 실험에서 꽤 중요합니다. 실패를 하나의 빨간불로만 보면 다음 행동이 막힙니다. "안 된다"는 말만 남습니다. 반대로 실패를 나누면 다음 실험이 정해집니다. fall이면 안정화 문제입니다. 얕으면 target과 reward 문제입니다. 미끄러지면 stance와 contact 문제입니다. 돌아오지 못하면 phase와 terminal objective 문제입니다.
돌아보면 이 프로젝트에서 가장 많이 바뀐 것은 controller가 아니었습니다. 기준이었습니다. 기준이 바뀌자 실패도 더 쓸모 있는 데이터가 됐습니다. 이전 실패가 사라진 것이 아닙니다. 다음 실패를 더 빨리 구분해주는 표지판이 됐습니다.
이런 식의 기록은 느립니다. 대신 과장할 여지가 줄어듭니다. "조금 움직였다"를 "성공했다"로 부풀리지 않게 해줍니다. 작은 팀이 로봇 실험을 할 때 특히 필요한 태도입니다. 시간이 걸려도, 다음 사람이 같은 착각을 덜 하게 만듭니다. 기록은 시행착오를 자산으로 바꿉니다.
그래도 실물 성공은 아닙니다
이 글에서 가장 조심해야 할 문장은 "스쿼트를 성공했다"입니다. 네, 시뮬레이션 기준으로는 성공했습니다. visible squat gate와 native measured WBC rollout, browser replay QA를 함께 통과했습니다. 하지만 실제 Unitree G1 로봇이 제 앞에서 앉았다 일어난 것은 아닙니다.
이 차이를 흐리면 안 됩니다. 이번 결과는 measured MuJoCo WBC trace입니다. 브라우저 replay도 그 qpos trajectory를 운동학적으로 재생한 것입니다. 실물 텔레메트리는 아직 없습니다. DDS stream과 actuator current도 없습니다. 실제 바닥 접촉과 안전 정지까지 닫은 것은 아닙니다. 이 때문에 ROADMAP에도 real robot telemetry twin은 future work로 남겼습니다.
그럼에도 의미는 있습니다. 첫째, "G1 geometry가 squat를 못 담는다"는 의심을 넘겼습니다. 둘째, local reward/controller가 어디서 막혔는지 분리했습니다. 셋째, 성공 장면을 mp4 하나로 남기지 않았습니다. 재생 가능한 browser artifact와 raw verify 파일로 남겼습니다. 실패와 성공을 같은 장부에 둘 수 있게 된 것입니다.
피지컬 AI를 공부하며 점점 더 분명해지는 사실이 있습니다. 로봇은 멋진 한 장면으로 배울 수 없습니다. 한 장면을 만들기까지 어떤 실패가 있었는지 봐야 합니다. 어떤 기준을 버렸고, 어떤 기준을 새로 세웠는지도 봐야 합니다. 이번 스쿼트도 그랬습니다. 조금 앉는 장면보다 더 중요했던 것은, 그 장면을 스쿼트라고 불러도 되는 기준을 끝까지 고친 일이었습니다.
- GR00T-WholeBodyControl Decoupled WBC documentation, accessed 2026-06-18
- GR00T-WholeBodyControl model checkpoint documentation, accessed 2026-06-18
- Unitree G1 official product page, accessed 2026-06-18
- physical-ai experiments 15 to 122
- robotics.askewly.com visible G1 squat replay



