14. 팔 스윙 — 상체를 정책에게 넘기다

이번 단계 목표: 06번부터 지금까지 팔·허리는 PD로 기본 자세에 고정돼 있었습니다 (“상체는 나중에”라던 그 나중이 왔습니다). 팔 6관절(어깨 pitch/roll·팔꿈치)을 정책 제어로 넘겨 18관절 보행을 학습시키고, 사람처럼 팔을 흔드는지 봅니다.


왜 팔을 흔들까? — 각운동량의 상쇄

사람은 걸을 때 오른발이 나가면 왼팔이 나갑니다. 멋이 아니라 물리입니다: 다리가 만드는 몸통 비틀림(각운동량)을 반대 위상의 팔 스윙이 상쇄해 몸통을 곧게 유지해 줍니다. 로봇도 같은 물리 위에 있으니, 팔을 쓸 수 있게 해 주면 도움이 될 겁니다 — 다만 “팔을 흔들어라”라고 시키지는 않을 겁니다. 쓸 수 있게만 해 주고, 흔들지는 스스로 정하게 합니다.

구성: 채점표는 그대로, 자유도만 추가

13번(sym2)의 채점표를 그대로 두고 바꾼 것:

  • 행동 12 → 18관절: 다리 12 + 어깨 pitch/roll·팔꿈치 좌우 6 (손목·허리는 계속 PD 고정)
  • 관측도 자동으로 45+6×3 = 65차원(위상 포함 67 → 정확히는 18관절 기준으로 재계산)
  • 팔 PD 게인: kp 40 / kd 1 (가볍고 부드럽게)
  • 약한 팔 자세 정규화(−0.3)만 추가: 팔이 이상한 정지 자세로 새는 것만 막고, 움직임은 자유
  • 흥미로운 부분: 대칭 보상이 팔에도 자동 적용됩니다. ‘왼팔(지금) = 반 박자 전 오른팔의 거울상’ — 사람의 팔 스윙이 정확히 이 패턴입니다. 흔들려면 사람처럼 흔들게 되는 구조인 거죠.
cd scripts/train
python g1_train.py --task arms --max_iterations 3000
python g1_eval.py  --task arms --ckpt 2999 --record

결과

학습 후(3000 iteration, 보상 21.7 수렴), 걸음 품질은 그대로였고(1.25Hz, 발 들림 7.2~7.9cm, 보폭 30cm, 무릎 대칭) — 팔에서 놀라운 것이 나왔습니다.

팔 스윙의 위상 분석 (10초 보행 중 관절각 상관계수):

관절 쌍 상관계수 사람의 걸음
왼어깨 ↔ 오른엉덩이 +0.92 + (같이 움직임 — 오른발 나갈 때 왼팔)
왼어깨 ↔ 왼엉덩이 −0.87 − (반대)
왼어깨 ↔ 오른어깨 −0.97 − (두 팔은 반대 위상)

정확히 사람의 팔 스윙 패턴입니다. “팔을 흔들어라”라는 채점 항목은 어디에도 없는데, 팔을 쓸 수 있게 해 주자마자, 각운동량 상쇄라는 물리가 이 패턴을 이끌어냈습니다. 대칭 보상 (‘왼팔 지금 = 반 박자 전 오른팔의 거울상’)도 같은 방향으로 밀었고요.

다만 진폭이 소심합니다: 어깨 ±5° 안팎(사람 보행은 ±15~30°). 0.4m/s의 느린 걸음이라 필요한 상쇄량 자체가 작은 데다, 토크 벌점과 팔 자세 정규화(−0.3)가 큰 스윙을 눌렀습니다. “박자는 완벽, 스윙은 절약형” — 물리가 시키는 만큼만 흔드는 검소한 걸음입니다.

팔을 흔들며 걷는 G1

전체 영상:


회고

  • 채점하지 않았는데 나온 첫 번째 행동. 지금까지는 “채점 안 하면 엉뚱한 데로 샌다”(셔플, 절뚝임, 토크 페달)의 연속이었는데, 처음으로 채점하지 않았는데 올바른 행동이 공짜로 나왔습니다. 차이는 물리입니다 — 팔 스윙은 보상의 빈틈이 아니라 물리적 필요(각운동량 상쇄)였고, 물리에 부합하는 행동은 시키지 않아도 나옵니다. 보상 설계의 반대편 교훈: 물리가 대신 채점해 주는 것은 채점표에 쓸 필요가 없다.
  • 진폭까지 원하면 그건 다시 채점의 영역. 사람 같은 큰 스윙을 원한다면 빠른 보행(상쇄 필요량 증가)이나 모방학습이 다음 수단입니다. ‘기능적으로 옳은 스윙’은 여기서 달성됐다고 보고 멈춥니다.

다음: 15. 거친 지형 — 평지를 벗어난다.

이 시리즈의 전체 글 목록 → 시리즈 목차