이번 단계 목표: 걷는 G1을 옆·앞·뒤에서 밀쳐도 휘청하고 회복하게 만듭니다. 학습 중에 로봇을 계속 밀쳐서, “밀리면 버티는 법”까지 스스로 배우게 하는 방식입니다. 로봇 회사 데모 영상에서 사람이 로봇을 발로 차는 장면, 그걸 우리도 해봅니다.
재검증 노트 — “학습 중 밀치면 회복을 배운다”는 이 글의 원리와 4/4 회복 수치는 유효합니다. 하지만 나중에 밝혀진 두 가지: ① 이 정책의 걸음은 셔플이었고(10편), ② 이 회복력의 상당 부분은 셔플의 넓은 스탠스(발 간격 23cm+잔발)라는 방어적 자세 덕이었습니다 — 실제로 걸음을 교정하자 회복력이 사라졌다가(0/4), 결합 학습으로 되찾았습니다. 그 반전은 11. 걸음과 강건함에서.
지금까지의 로봇은 밀면 넘어질까?
8편까지의 정책은 평평한 바닥 + 아무도 안 건드리는 세상에서만 살아봤습니다. 관측에 몸 기울기·속도가 들어가니 작은 흔들림은 잡겠지만, 학습 중 한 번도 겪어 본 적 없는 ‘강한 밀침’에도 대응할 수 있을까요?
궁금하면 실험해 보면 됩니다. 이번 단계의 평가 시나리오는 걷는 로봇을 3초 간격으로 오른쪽 → 왼쪽 → 앞 → 뒤에서 밀치는 것입니다. 이 시나리오를 8편 정책(밀침 없이 학습)에게 먼저 시켜 봤습니다:
| 밀치기 (0.8~1.0 m/s로 순간 밀기) | 8편 정책(밀침 무경험) |
|---|---|
| 오른쪽에서 밀기(왼쪽으로 밀림) | 회복 |
| 왼쪽에서 밀기(오른쪽으로 밀림) | 회복 |
| 앞에서 밀기(뒤로 밀림) | 넘어짐 |
| 뒤에서 밀기(앞으로 밀림) | 회복 |
의외로 선전합니다! 관측 속 기울기·속도를 보고 매 순간 다리를 고치는 피드백 제어를 이미 배웠기 때문에, 겪어 본 적 없는 밀침도 어느 정도 처리하는 겁니다(이걸 일반화라고 부릅니다). 하지만 뒤로 밀리는 상황엔 무너졌습니다. 앞으로 걷도록 학습된 로봇에게 ‘뒤로 가는 몸’은 학습 중에 겪어 본 상황들(학습 분포)에서 가장 먼 상황이었기 때문으로 보입니다. 이 구멍을 메워 봅시다.
해법: 학습 중에 계속 밀쳐 버릇하기
원리는 8편과 똑같습니다. 8편에서 “명령을 알아듣게 하려면 학습 중 명령을 계속 바꿔라”였다면, 이번엔 “밀침에 버티게 하려면 학습 중 계속 밀쳐라”입니다. RL에서는 이렇게 학습 환경에 무작위 변화를 섞는 걸 통틀어 도메인 랜덤화(domain randomization)라 부릅니다.
‘밀치기’는 물리적으로 베이스(몸통)의 수평 속도에 순간적으로 속도를 더하는 것으로 구현했습니다(속도 임펄스). 옆에서 훅 미는 것과 같은 효과입니다 (g1_env.py):
def push_robots(self, mask=None, delta=None):
"""베이스의 수평 속도에 임펄스를 더한다 — 옆에서 훅 밀치는 것과 같은 효과."""
base_xy_dofs = [0, 1] # 몸통의 x·y 이동 속도
# (학습 땐 크기 0.3~1.0 m/s, 방향 360° 무작위)
vel = self.robot.get_dofs_velocity(base_xy_dofs)
self.robot.set_dofs_velocity(vel + dv, base_xy_dofs)
학습 설정(g1_train.py --task push)은 8편(steer)에 딱 두 줄을 얹은 것입니다:
env_cfg["push_interval_s"] = 4.0 # 4초마다 밀친다
env_cfg["push_vel_range"] = [0.3, 1.0] # 세기는 0.3~1.0 m/s에서 무작위
4096개 환경 각각이 서로 다른 타이밍·방향·세기로 얻어맞으며 걷습니다. 밀려서 넘어지면 에피소드가 끝나 보상을 못 받으니, 버티고 회복하는 것 자체가 보상이 됩니다. 보상 함수는 하나도 안 바꿨습니다.
cd scripts/train
python g1_train.py --task push --max_iterations 2000 # 학습 (GPU)
python g1_eval.py --task push --ckpt 1999 --record # 밀치기 시나리오 + 녹화
python g1_eval.py --task push -e g1-steering --ckpt 1499 --record # 8편 정책과 비교
sim-to-real의 핵심 재료. 시뮬레이션에서 학습한 정책을 실제 로봇에 옮기면, 시뮬레이션과 현실의 미세한 차이(마찰, 모터 특성, 예상 못 한 접촉…) 때문에 잘 안 되는 경우가 많습니다. 그래서 실전에서는 밀치기뿐 아니라 마찰·질량·지연까지 온갖 것을 랜덤화해 “어떤 세상에 떨어져도 대충 통하는” 정책을 만듭니다. 이번 단계는 그 세계로 들어가는 첫걸음입니다.
학습 결과
4096개 병렬 환경 × 2000 iteration, 24분 걸렸습니다.
| 학습 진행 | 평균 보상 | 평균 에피소드 길이 | 상태 |
|---|---|---|---|
| iter 1 | −0.6 | 48 스텝 (~1초) | 픽픽 쓰러짐 |
| iter 101 | 6.9 | 866 스텝 | 웬만한 밀침엔 버팀 |
| iter 301 | 15.6 | 1001 (최대) | 무작위 밀침 속에서도 안 넘어짐 |
| iter 1999 | 17.3 | 1001 (최대) | 보상 수렴 |
수렴 보상(17.3)이 8편(27.1)보다 낮은데, 이건 못 배웠다는 뜻이 아닙니다. 4초마다 얻어맞으니 그때마다 명령 추종 보상을 잃는 게 당연합니다. 과제가 다르면 보상 수렴값을 직접 비교하면 안 됩니다. 진짜 지표는 에피소드 길이가 최대치라는 것 — 계속 밀쳐도 20초를 버틴다는 뜻입니다.
같은 밀치기 시나리오를 두 정책에게 시킨 결과:
| 밀치기 | 8편 정책(밀침 무경험) | 이번 정책(밀침 단련) |
|---|---|---|
| 오른쪽에서 0.8 m/s(왼쪽으로 밀림) | 회복 (기울기 2.6°) | 회복 (기울기 3.6°) |
| 왼쪽에서 0.8 m/s(오른쪽으로 밀림) | 회복 (4.7°) | 회복 (2.3°) |
| 앞에서 1.0 m/s | 넘어짐 | 회복 (2.3°) |
| 뒤에서 1.0 m/s | 회복 (3.5°) | 회복 (2.2°) |
| 8편 정책 — 뒤로 밀리자 넘어짐 | 이번 정책 — 같은 밀침을 받아냄 |
|---|---|
![]() |
![]() |
한계는 어디일까요? 학습 때 최대 1.0 m/s로 밀쳤는데, 더 세게 밀어 봤습니다(세기마다 1회씩 민 결과):
| 임펄스 세기 | 옆에서 | 앞에서(뒤로 밀림) |
|---|---|---|
| 1.5 m/s | 회복 | 회복 |
| 2.0 m/s | 넘어짐 | 회복 |
| 2.5 m/s | 넘어짐 | 회복 |
학습 범위 밖인 1.5 m/s까지도 회복합니다(일반화). 그리고 뒤로 밀리는 방향(2.5까지 회복)이 옆 방향(2.0에서 넘어짐)보다 강한 비대칭이 보입니다(각 1회). 앞으로 밀리는 쪽은 이 세기로 시험하지 않았습니다.

전체 영상:
(비교: 8편 정책이 같은 시나리오에서 넘어지는 영상
)
회고 — 막혔던 점 & 배운 것
- “겪게 하면 배운다”의 재확인. 8편(명령을 바꿔 주면 명령을 알아듣는다)과 정확히 같은 원리였습니다. 밀쳐 버릇하니 버티는 법을 배웁니다. 보상 함수는 한 줄도 안 바꿨는데도 그랬습니다 — “넘어지면 보상이 끊긴다”는 기존 구조만으로 회복 기술이 유도됐습니다. RL에서는 무엇을 경험시키느냐가 무엇을 채점하느냐만큼 중요합니다.
- 기준선(8편 정책)이 의외로 선전했다. 밀침을 한 번도 안 겪은 정책이 밀치기 4회 중 3회를 버텼습니다. 피드백 제어를 배웠다는 건 ‘기울면 고친다’는 일반 능력이 생겼다는 뜻이라, 처음 보는 외란도 어느 정도 처리합니다. 하지만 뒤로 밀리는, 학습 분포에서 가장 먼 상황에서 무너졌고, 밀침을 겪은 정책만 그 구멍을 메웠습니다.
- 회복의 비대칭 — 4편 교훈의 반전. 4편의 정적 실험에선 좌우 지지면이 넓어 ‘옆’이 유리했는데, 걷는 중 회복은 반대로 뒤로 밀리는 방향이 옆보다 강했습니다(2.5 vs 2.0 m/s, 세기마다 1회). 걷기란 원래 앞뒤로 발을 내딛는 동작이라, 앞뒤 외란은 ‘걸음을 조금 고치는 것’으로 흡수되지만 옆 외란은 배운 적 없는 ‘옆으로 발 내딛기’를 요구하기 때문일 겁니다. 앞으로 밀리는 쪽은 시험하지 않았지만, 같은 로봇이라도 정적 균형과 동적 균형의 논리가 다르다는 걸 이 한 방향의 수치로 봤습니다.
- 학습 범위 밖에서도 통했다. 최대 1.0 m/s로 학습했는데 1.5 m/s도 회복했습니다. 무작위 경험이 만드는 여유(강건성 마진, robustness margin)입니다. 실물로 옮길 때 이런 여유가 있어야 합니다.
- 보상 숫자는 과제끼리 비교하지 말 것. 이번 정책의 수렴 보상(17.3)은 8편(27.1)보다 낮지만 더 어려운 과제를 더 잘하는 정책입니다. 곡선의 ‘수렴 여부’와 ‘에피소드 길이’를 보는 게 옳았고, 절대값 비교는 함정이었습니다.
다음 단계
여기까지의 로봇, 정말 ‘걷고’ 있는 걸까요? 그 의심에서 다음 이야기가 시작됩니다 → 10. 제대로 된 걸음
이 시리즈의 전체 글 목록 → 시리즈 목차

