06. RL 보행 학습 — G1에게 걷는 법을 ‘학습’시키기

이번 단계 목표: 손으로 규칙을 짜는 대신, G1이 시뮬레이션 안에서 수천 번 넘어져 보며 스스로 걷는 법을 터득하게 합니다. 강화학습(RL)의 개념을 잡고, 실제로 학습을 돌려 걷는 정책을 얻습니다.

재검증 노트 (나중에 알게 된 사실) — 이 글의 결과(속도 추종·안 넘어짐)는 전부 사실이지만, 석 달 뒤 걸음을 수치로 재 보니 이 정책의 ‘걸음’은 발을 1cm만 들고 초당 16번 잔발을 치는 셔플이었고, 발목 토크도 전체 시간의 31% 동안 실물 한계치까지 끌어 쓰고 있었습니다. 영상만 보고는 몰랐습니다. 진단과 교정 과정은 10. 제대로 된 걸음에서, 왜 RL이 이런 빈틈을 반드시 찾아내는지는 연구 노트 R01에서 다룹니다. 이 글은 당시 기록 그대로 남겨 둡니다 — 이 시행착오 자체가 시리즈의 가장 큰 배움이었기 때문입니다.


강화학습(RL)이 뭔가요? — “잘하면 칭찬, 못하면 꾸중”

강아지에게 “앉아”를 가르치는 장면을 떠올려 보세요. 강아지가 우연히 앉으면 간식을 주고, 엉뚱한 행동엔 안 줍니다. 이걸 반복하면 강아지는 “앉으면 좋은 일이 생긴다”를 학습합니다.

강화학습(Reinforcement Learning) 이 정확히 이 방식입니다.

강아지 훈련 강화학습 용어 G1 보행에서는
강아지 에이전트(agent) 걷는 법을 배우는 정책(policy)
강아지가 보는 상황 관측(observation) 몸 기울기·관절 각도·속도 등
강아지의 행동 행동(action) 다리 관절 목표각
간식(칭찬) 보상(reward) 앞으로 잘 가고 안 넘어지면 +
훈련 반복 학습(training) 수천만 번의 시행착오

핵심은 “무엇을 하라고 알려주지 않는다”는 점입니다. 우리는 “이렇게 걸어라”라고 동작을 지정하지 않습니다. 그저 “앞으로 잘 가면 칭찬, 넘어지면 꽝”이라는 점수표(보상)만 주고, 어떻게 걸을지는 로봇이 스스로 찾아내게 합니다. 04번에서 손으로 짠 규칙이 실패한 것과 정반대 접근이에요.

정책(policy)이란? “지금 이런 상황(관측)이면 → 이렇게 움직여라(행동)”를 정해 주는 함수입니다. 우리 경우엔 작은 신경망이고, 학습이란 이 신경망의 숫자(가중치)를 보상이 커지는 방향으로 조금씩 조정하는 일입니다. 우리가 쓰는 학습 알고리즘은 PPO(Proximal Policy Optimization)로, 로봇 보행 학습에서 가장 널리 쓰이는 검증된 방법입니다.


왜 시뮬레이터인가 — “동시에 4096마리”의 마법

“수천만 번 시행착오”라니, 실제 로봇으로 하면 며칠이 걸리고 로봇도 부서집니다. 그래서 시뮬레이션에서 합니다. 게다가 Genesis는 GPU로 로봇 수천 마리를 동시에 굴립니다.

이번 학습에서는 4096마리의 G1을 한꺼번에 시뮬레이션했습니다. 4096개의 서로 다른 시도가 매 순간 동시에 진행되니, 경험이 4096배 빨리 쌓입니다. 00번에서 본 “초당 수천 step”의 속도가 바로 이걸 위한 것이었어요.

관측 → [정책 신경망] → 행동 → 시뮬레이션 한 step → 보상 측정 → ...
       ↑__________ 보상이 커지도록 신경망 업데이트 __________|

이 고리를 4096마리 × 수천 번 반복하면, 처음엔 픽픽 쓰러지던 로봇들이 점점 오래 서 있고, 결국 앞으로 걷기 시작합니다.


보상 설계 — 이 단계의 진짜 기술

RL에서 가장 중요한 건 보상을 어떻게 주느냐입니다. “잘하면 칭찬”의 ‘잘함’을 숫자로 정의하는 일이거든요. 우리가 G1에게 준 점수표(scripts/train/g1_train.pyreward_scales):

보상 항목 부호 의미(왜 주는가)
tracking_lin_vel + 명령한 속도(앞으로 0.5m/s)에 맞게 가면 칭찬 — 가장 중요
orientation 몸통이 기울면 꾸중 (똑바로 서서 걷게)
base_height 키가 목표(0.78m)에서 벗어나면 꾸중 (주저앉지 말 것)
lin_vel_z 위아래로 통통 튀면 꾸중
ang_vel_xy 좌우·앞뒤로 휘청이면 꾸중
action_rate 관절을 급격히 휘저으면 꾸중 (부드럽게)
similar_to_default 기본 자세에서 너무 벗어나면 살짝 꾸중

이 점수표가 곧 “우리가 원하는 걸음”의 정의입니다. 예를 들어 orientation(몸통 똑바로) 항목이 없으면, 로봇은 몸을 기괴하게 비틀어서라도 앞으로만 가려 합니다. 항목 하나하나가 걸음의 ‘품질’을 빚어냅니다.

이게 04번 실패의 해법입니다. 04번에서 우리 로봇은 ‘현재 상태’를 안 봤습니다. RL의 관측(observation)에는 몸이 지금 얼마나 기울었는지·어디로 쏠리는지가 들어갑니다. 정책은 이걸 보고 매 순간 다리를 조정합니다 — 바로 우리가 04번에서 없다고 한 닫힌 루프 (피드백) 제어입니다. RL이 그 피드백 규칙을 스스로 학습해 준 거죠.


어떻게 구성했나 (코드 구조)

표준 라이브러리 rsl_rl(PPO 구현)을 사용하고, 환경은 Genesis 공식 go2(4족보행) 예제를 G1 2족보행용으로 개조했습니다. 파일은 scripts/train/에 있습니다.

  • g1_env.py — 시뮬레이션 환경: 관측을 만들고, 행동을 받아 한 step 진행하고, 보상을 계산하고, 넘어지면 리셋. 정책은 다리 12관절만 제어하고, 허리·팔은 PD로 고정했습니다(상체까지 한꺼번에 학습하면 너무 불안정해서, 먼저 다리부터).
  • g1_train.py — 학습 설정(보상·PD게인·기본자세 등)과 학습 루프 실행.
  • g1_eval.py — 학습된 정책을 불러와 재생/녹화.

실행:

cd scripts/train
python g1_train.py -e g1-walking -B 4096 --max_iterations 3000   # 학습 (GPU, 약 30분)
python g1_eval.py  -e g1-walking --ckpt 2999 --record            # 결과 mp4로 녹화

개조에서 가장 까다로웠던 점. go2(강아지)는 다리 12관절이 데이터상 나란히 붙어 있어 한 번에 슬라이스로 제어했는데, G1은 다리·허리·팔 관절이 뒤섞여 있었습니다. 그래서 “관절 이름 → 데이터 위치”로 일일이 매핑해, 다리 12개만 골라 제어하도록 일반화해야 했습니다(g1_env.py). 또 G1은 무거워서, 처음엔 기본 자세조차 못 버티고 주저앉았습니다. 무릎 각도와 PD 게인을 여러 번 실험해(03번의 그 교훈!) 자립 가능한 기본 자세를 먼저 찾고서야 학습이 안정됐습니다.


학습 결과

학습은 GPU(RTX 4060 Ti)에서 4096개 환경을 병렬로 돌렸고, 진행은 놀랍도록 빨랐습니다.

학습 진행 평균 보상 평균 에피소드 길이 상태
iter 47 0.5 88 스텝 (~1.8초) 픽픽 쓰러짐
iter 201 18.5 1001 스텝 (최대) 더 이상 안 넘어짐
iter ~700+ ~20.3 1001 (최대) 보상 수렴, 걸음 다듬기
  • 초당 약 155,000 step, iteration당 0.6초로 학습됐습니다(4096마리 동시 시뮬레이션의 힘).
  • iteration 200 무렵 이미 균형을 터득해 20초 에피소드를 끝까지 버텼고, 이후 보상이 ~20.3에서 수렴했습니다. 보상이 평평해진 시점(약 1000 iteration, 10분 남짓)에서 학습을 멈추고 정책을 평가했습니다.

학습된 정책으로 10초간 걷게 한 결과:

전진 거리: 4.84 m   (명령 0.5 m/s × 10초 → 목표 ~5 m)
평균 전진속도: 0.49 m/s   (명령 0.5 m/s와 거의 일치 — 속도 추종 성공)
몸통 높이: 평균 0.771 m, 최소 0.768 m   (거의 흔들림 없음)
결과: 끝까지 넘어지지 않고 걸음

강화학습으로 걷는 G1

전체 영상:

04번에서 손으로 짠 보행은 2.2초 만에 넘어졌는데, 학습한 정책은 명령 속도에 딱 맞춰 똑바로 서서 안정적으로 걷습니다. 우리가 “걸음”을 일일이 짜준 게 아니라, 점수표(보상)만 주고 로봇이 스스로 찾아낸 결과라는 점이 핵심입니다.


회고 — 막혔던 점 & 배운 것

  • “동시에 4096마리”의 위력을 체감. 학습이 이렇게 빠를 줄 몰랐습니다. iteration당 0.6초, 10분 만에 걷는 정책이 나왔습니다. 실제 로봇 한 대로는 상상도 못 할 속도예요. 이게 시뮬레이션 + GPU 병렬화의 힘이고, Genesis가 빠른 이유입니다.
  • 기본 자세부터 막혔다. 가장 애먹은 건 의외로 RL 자체가 아니라 그 전 단계였습니다. G1이 무거워서, 정책이 학습을 시작하기도 전에 기본 자세조차 못 버티고 주저앉았습니다. 무릎을 깊게 굽힌 자세(0.3rad)는 PD 게인을 아무리 올려도 무너졌고, 무릎을 적게(0.2rad) 굽히고 PD를 세게(kp 350) 줘서야 자립했습니다 — 03번에서 배운 “버티기 쉬운 자세”의 교훈 그대로였습니다.
  • 다리부터, 상체는 나중에. 처음엔 29관절 전부를 학습시키려다 너무 불안정했습니다. 그래서 다리 12관절만 정책에게 맡기고 팔·허리는 PD로 고정했습니다. 문제를 작게 쪼개니 학습이 안정됐어요. (상체까지 쓰는 보행은 다음 도전 과제로 남겨둡니다.)
  • 보상 수렴을 보고 멈췄다. 보상이 ~20.3에서 평평해지고 에피소드 길이가 최대치에 붙은 걸 보고, 더 돌려도 큰 향상이 없다고 판단해 학습을 멈추고 평가로 넘어갔습니다. “언제 멈출지”를 보상 곡선으로 판단하는 것도 RL의 실전 감각입니다.
  • go2(강아지) 예제를 G1(사람)으로 옮기며. 관절 구조가 달라 코드를 일반화해야 했고(관절 이름 매핑), 4족과 2족은 균형 난이도가 차원이 다르지만, 보상 설계와 학습 골격은 그대로 재사용할 수 있었습니다. 잘 만든 예제 하나가 큰 출발점이 됩니다.

다음 단계

학습한 보행을 멋진 영상으로 뽑아 블로그에 올릴 준비를 합니다 → 07. 영상 렌더링