이번 단계 목표: 10번에서 걸음은 고쳤는데, 뜻밖의 문제가 드러났습니다. 제대로 걷게 만들었더니 밀치기에 전패한 것. 걸음 품질과 강건함이 왜 충돌하는지 확인하고, 두 가지를 한 학습에서 같이 요구해 둘 다 잡습니다.
발단: 재검토에서 드러난 전패
10번의 gait 정책(제대로 걷는 정책)에게 09번의 밀치기 시나리오를 시켜 봤습니다. 결과는 충격적이었습니다:
| 정책 | 밀치기 4회(좌/우/앞/뒤) 결과 |
|---|---|
| 08번 steer (셔플, 밀침 무경험) | 3/4 회복 |
| 09번 push (셔플, 밀침 단련) | 4/4 회복 |
| 10번 gait (정상 보행, 밀침 무경험) | 0/4 — 전패 |
밀침을 안 겪었다는 조건은 08번과 같은데, 08번은 3/4를 버텼고 10번은 전패했습니다. 걸음을 고친 것이 오히려 로봇을 약하게 만든 겁니다.
해석: 셔플은 사실 ‘강건해서’ 선택된 자세였다
돌이켜 보면 앞뒤가 맞습니다. 06번의 셔플을 다시 보면 —
- 발 간격 55cm: 지지면이 넓어 옆에서 밀려도 무게중심이 그 안에 머뭅니다.
- 발이 늘 땅 근처(들림 1cm): 어느 발이든 즉시 디딜 수 있습니다.
- 8Hz 잔발: 초당 16번 접지점을 고칠 기회가 있습니다.
즉 셔플은 게으른 해법이 아니라, “넘어지면 보상 끝”이라는 조건에서 최적화가 고른, 지극히 방어적인 자세였던 겁니다. 반면 제대로 된 걸음은 좁은 스탠스(15cm)로 340ms씩 한 발을 공중에 띄우는, 본질적으로 더 위태로운 운동입니다. 게다가 10번의 박자 보상은 “항상 박자대로”를 요구했으니, 밀쳤을 때 필요한 박자를 깨는 비상 스텝은 배운 적도, 허용된 적도 없었습니다.
일반 교훈: 품질 축과 강건성 축은 따로 논다. 한 축을 고치는 개선이 다른 축을 후퇴시킬 수 있습니다. 그래서 개선을 하나 넣을 때마다 이전 검증들을 전부 다시 돌려야 합니다 — 소프트웨어의 회귀 테스트(regression test)와 정확히 같은 이유입니다. (우리의 재검토 체크리스트가 그 역할입니다.)
처방: 같이 요구하면 같이 배운다
해법은 08→09 때와 같은 원리입니다. 밀침을 버티게 하려면 학습 중 밀치면 됩니다 — 이번엔 걸음 채점을 켜 둔 채로:
# --task gaitpush = gait의 14개 채점 항목 그대로 + push의 무작위 밀치기
env_cfg["push_interval_s"] = 4.0 # 4초마다
env_cfg["push_vel_range"] = [0.3, 1.0] # 0.3~1.0 m/s 임펄스
이제 정책은 “박자대로 예쁘게 걷기”와 “밀치면 어떻게든 회복하기”를 동시에 요구받습니다. 밀친 직후엔 박자 보상을 좀 잃더라도 회복하는 게(에피소드 유지) 이득이므로, “평소엔 박자, 비상시엔 박자를 깨고 회복 스텝”이라는 우선순위를 스스로 찾아야 합니다.
cd scripts/train
python g1_train.py --task gaitpush --max_iterations 3000 # 학습 (GPU)
python g1_eval.py --task push -e g1-gaitpush --ckpt 2999 --record # 밀치기 재시험
python g1_gait_metrics.py -e g1-gaitpush --ckpt 2999 --vx 0.4 # 걸음 품질 재측정
결과
학습은 39분(3000 iteration), 보상 29.5에서 수렴했고 에피소드 길이는 최대치 — 밀침 속에서도 안 넘어집니다. 세 정책을 같은 잣대(밀치기 시나리오 + 걸음 지표)로 나란히 세우면:
| 09 push (셔플+밀침) | 10 gait (정상보행) | 11 gaitpush (결합) | |
|---|---|---|---|
| 밀치기 4회 | 4/4 회복 (기울기 2~4°) | 0/4 전패 | 4/4 회복 (기울기 8~16°) |
| 걸음 빈도 | 발당 9.1 Hz | 1.25 Hz | 1.25 Hz |
| 보폭 | 3cm | 32cm | 32cm |
| 발 들림 | 0.4cm | 3.6~4.5cm | 2~4cm |
| 발 간격 | 23cm | 15cm | 15cm |
| 조종(회전 오차) | — | 3~5° | 2~7° |
두 마리 토끼를 잡았습니다. 박자(1.25Hz)·보폭(32cm)·좁은 스탠스(15cm)를 유지하면서 밀치기 4회를 전부 회복합니다. 흥미로운 디테일 둘:
- 회복할 때 더 크게 휘청입니다(8~16°, 셔플 정책은 2~4°). 넓게 벌리고 서 있던 셔플과 달리, 제대로 걷는 자세에선 밀리면 크게 휘청했다가 회복 스텝으로 되잡는 수밖에 없습니다. 실제 사람이 밀렸을 때와 같은 모습이고, 영상에서도 그렇게 보입니다.
- 왼 무릎을 거의 안 굽힙니다(−6° 고정; 오른쪽은 −2~44°로 정상). 걸음 자체는 유지되지만 왼다리를 뻗정다리로 쓰는 비대칭 걸음(절뚝임)이 생겼습니다. 우리 채점표 어디에도 “좌우 대칭”은 없으니, 더 어려워진 과제(걸음+밀침) 앞에서 최적화가 또 채점 없는 자유도를 희생한 겁니다 — R01의 교훈이 한 번 더.
(재검증 추가 발견) 실제로 걸린 토크를 재 보니 절뚝임의 물리적 실체가 드러났습니다: 이 정책은 무릎 토크를 시간의 97% 동안 한계(139Nm)까지 채워서 씁니다. PD 목표를 실제 각도에서 일부러 멀리 던져 토크 클램프(Genesis는 URDF 한계로 자동 클램프)를 ‘최대 힘 페달’처럼 밟는, 또 하나의 시뮬레이터 빈틈 활용입니다. 참고로 06번 셔플 정책도 발목 토크를 31% 포화시키고 있었습니다. 실물 모터는 피크 토크를 상시 낼 수 없으므로 이대로는 실물 로봇에 옮기기 어렵습니다 — 다음 개선 후보: 토크 벌점 + 좌우 대칭 보상을 넣은 재학습.

전체 영상:
(조종 시나리오),
(밀치기 시나리오)
회고 — 막혔던 점 & 배운 것
- 개선은 회귀를 부른다. 걸음을 고쳤더니 강건함이 무너졌고(0/4), 결합 학습으로 둘을 잡았더니 이번엔 대칭이 깨졌습니다. 요구 조건이 늘 때마다 채점표 밖의 무언가가 희생됩니다. 개선 → 전체 재검증 → 새 구멍 발견 → 채점표 보강의 순환이 이 작업의 실제 리듬이고, 우리가 만든 검증 도구(
g1_gait_metrics.py, 시나리오 평가)가 그 순환을 돌게 하는 엔진입니다. - 강건함에는 겉모습이 있다. 같은 “4/4 회복”이라도 셔플의 회복(2~4° 흔들림)과 정상 보행의 회복(8~16° 휘청 후 회복 스텝)은 질적으로 다릅니다. 후자가 사람의 실제 반응과 같고, 실물 로봇에서도 통할 가능성이 높은 쪽입니다. 수치 하나만 보지 말고 어떻게 그 수치를 냈는지 봐야 한다는 10번 교훈의 반복.
- 트레이드오프는 학습으로 녹일 수 있다. 품질과 강건함이 충돌할 때, 하나를 포기하는 대신 두 요구를 같은 학습 안에 넣으면 정책이 절충점(평소엔 박자, 비상시엔 회복 스텝)을 스스로 찾습니다. 08번(명령), 09번(밀침), 10번(걸음 형태)에 이어 네 번째로 확인한, 이 프로젝트의 가장 일관된 교훈입니다: 원하는 게 있으면 채점하고, 겪게 하라.
다음 단계
재검증 계측이 이 정책에서 두 가지 문제(절뚝임, 무릎 토크 97% 포화)를 더 찾아냈습니다. 그 교정이 다음 이야기입니다 → 12. 절뚝임 교정
이 시리즈의 전체 글 목록 → 시리즈 목차