《MuJoCo로 알아보는 Bio-inspired Robotics》 2부작 집필 — 사족보행편·이족보행편
2026.08 · 단독 저자 — 커리큘럼 설계, 로봇 모델링, 실습 코드 126편, 본문·그림 전부 직접 구현·검증
MuJoCo 3.12, MJCF, Python 3.12, NumPy, Gymnasium, Stable Baselines3 (PPO), PyTorch, MuJoCo MJX / JAX, matplotlib, uv, EPUB 3

- 사족보행편 — 위키독스(한국어) · 영문판 Bio-Inspired Robotics with MuJoCo: Quadruped Locomotion · 실습 코드 kr · 실습 코드 en
- 이족보행편 — 위키독스(한국어) · 실습 코드
문제 정의
- 보행 로봇 논문은 결과만 제시하고, 교과서는 완성된 이론을 위에서 아래로 쏟아붓는다. "그래서 내 시뮬레이터에서 뭘 어떻게 해야 하는데?"에 답하는 한국어 자료가 없었다.
- 목표는 로봇 한 대를 직접 만들고 → 걷게 하고 → 걸음을 측정하고 → 강화학습으로 보정하는 전 과정을 GPU 없이 노트북 CPU만으로 끝까지 재현할 수 있는 출발점을 만드는 것. 본문의 모든 코드 블록은 첫 줄에 출처 경로가 있고, 모든 실행 결과와 그림은 저장소의 스크립트가 실제로 낸 것만 싣는다는 원칙으로 썼다.
- 사족보행편은 걸음의 풍부함(walk·trot·pace·gallop)을, 이족보행편은 균형(support polygon·ZMP·capture point)을 축으로 같은 구조를 두 번 완주한다. 네 발은 서 있는 것이 기본값이고 두 발은 넘어지는 것이 기본값이라는 차이가 6장 이후의 모든 설계를 갈랐다.
커리큘럼 — "이해 → 도구 → 구현 → 측정 → 학습"의 나선형
| Part | 장 | 사족보행편 | 이족보행편 |
|---|---|---|---|
| 서장 | 1장 | AI 시대에 기본기를 말하다 | 가장 불안정한 걸음을 배우는 이유 |
| 1 이해 | 2~3장 | 생체모방의 세 추상화 수준 / stance·swing, duty factor, 위상, gait 전환과 Froude number, support polygon | 사람의 보행 주기(heel strike~toe off), step·stride·cadence·double support, 도립진자 모델, ZMP·capture point |
| 2 도구 | 4~5장 | MuJoCo의 사고방식(mjModel·mjData, timestep·solver·접촉) / MJCF로 12관절 사족 로봇 설계·검증 | 같은 도구 위에 도립진자 균형 제어 / 10관절 이족 로봇(발목 roll·pitch 포함) 설계·검증 |
| 3 구현 | 6~8장 | PD 제어와 gain 튜닝 / 결합 위상 진동자(CPG)로 trot·walk 구현 / 발 접촉 로깅과 gait 분석, 개의 trot과 비교 | 지지 다리 중력 보상과 ankle strategy / 무게 옮기기 + 발 내딛기의 정적 보행 / 사람 걸음과 비교, ZMP·capture point로 넘어짐 해부 |
| 4 학습 | 9~12장 | RL·PPO 최소 이론 → residual learning 전략 / Gymnasium 환경(관측 53·행동 12) / SB3 PPO 학습 / reward 항 하나씩 더하는 통제 실험, reward hacking 박물관, 밀기 curriculum | 같은 흐름을 이족 맥락으로(관측 43·행동 10) / 방향 이탈·좌우 비대칭·접지 충격을 reward 항으로 하나씩 제거 |
| 5 확장 | 13장 | 거친 지형·경사·마찰·짐, MJX 병렬화 벤치마크, 관측 잡음·지연(sim-to-real 간극), 생성기 매개변수 개방 | 보정 폭 실험("20 N의 벽"), 생성기 개방, MJX, 경사 두 방식 비교, 짐 들고 걷기 |
| 부록 | A~D | 트러블슈팅 25항목 / MJCF 레퍼런스 / 용어집 / 논문·오픈소스 로드맵 | 같은 구성 + ZMP·capture point·수동 보행·Cassie~휴머노이드 계보 |
- gait 분석이 3장(이론) → 8장(내 로봇 측정) → 12장(개선 검증)에서 세 번 재등장하도록 설계 — 같은 자로 하드코딩 걸음과 학습된 걸음을 재야 "얼마나 좋아졌는가"를 말할 수 있다.
- 실패를 정면으로 다루는 절(7.6 open-loop 한계 목격, 8.5 넘어짐의 해부, 12.3 reward hacking 사례집)을 두 권 모두의 차별점으로 고정하고 축소하지 않았다.
핵심 기술 스택 — 무엇을 어디에 썼나
- 물리 시뮬레이션 — MuJoCo 3.12 C 엔진 + Python 바인딩. timestep·solver·접촉 파라미터(condim, solref)의 의미를 실험으로 확인하고,
mj_setConst·mj_subtreeVel처럼 빠뜨리면 조용히 틀리는 호출까지 본문에 명시. - 로봇 모델링 — MJCF를 손으로 작성. 사족 12관절(±12 N·m 토크 모터), 이족 10관절(hip roll/pitch, knee, ankle pitch/roll, 10.5 kg, 토크 한계 40/25 N·m). 낙하·밀기·센서 검증 스크립트로 모델을 의심하는 절차를 5장에 포함.
- 고전 제어 — 관절 PD, 관성 기반 임계 감쇠(
critical_kd), 중력 보상(공중 관절·지지 다리 분리), 목표 각속도 feedforward, 500 Hz 물리 / 100 Hz 상위 제어의 이중 루프. - 보행 생성 — 2D/3D FK·IK, 위상 → 발 궤적(지지기 직선 + 유각기 호), 결합 위상 진동자(CPG)와 gait별 위상표, 이족용 무게 옮기기(sway) IK.
- 측정 — 접촉 신호 디바운스, 자기상관·FFT로 주기 추정, duty factor·상대 위상·stride·cadence·double support, support margin·CoP·capture point, Froude number와 Alexander의 동적 유사성으로 동물·사람과 비교.
- 강화학습 — Gymnasium 환경(관측 정규화·행동 스케일·종료 조건 설계), SB3 PPO(SubprocVecEnv 8개 + VecNormalize), residual learning(정책은 CPG 목표 위 ±0.1 rad 보정만 출력), reward 항별 크기 측정 → 가중치 결정, 밀기 curriculum. MJX/JAX 배치 시뮬레이션 벤치마크.
- 출판 파이프라인 — XHTML 단일 원본 → EPUB 3(epubcheck 통과) + 위키독스 동기화(해시 비교로 변경분만 업로드) + 영문판 빌드까지 스크립트화(아래 참조).
사족보행편 — 12관절 로봇을 0.29 m/s 하드코딩 trot에서 0.49 m/s·50 N 밀기 100% 정책으로

- 서기(6장) — 위치 제어 vs 토크 제어, PD 각 항의 역할, gain sweep과 제어 주기 실험을 거쳐 kp 80 + 임계 감쇠 + feedforward를 표준 걸음 제어기로 확정. 서기 자세의 밀기 한계 40 N을 측정.
- 첫 보행(7장) — trot(2 Hz, duty 0.5, 보폭 0.08 m)으로 0.29 m/s. 걷는 중에는 40 N까지 버티지만 60 N에 넘어지고, 20 N에 밀리면 0.14 m 옆으로 밀린 채 새 경로로 걸어간다. "개루프 걸음은 넘어지지 않을 수는 있어도 목적지에 갈 수는 없다"를 실험으로 목격시키는 절.
- 측정(8장) — 발 접촉을 로깅해 gait diagram을 그리고 duty factor·위상을 명령값과 대조. Froude number로 개의 trot과 비교해 우리 로봇이 동물 기준으로 얼마나 느린 걸음인지 정량화. 넘어지는 순간의 support margin 시계열을 해부.
- 학습(10~11장) — 관측 53차원(관절 오프셋·각속도·몸통 좌표 중력·gyro·선속도·CPG 위상 sin/cos·직전 행동), 행동 12차원의 residual 환경. 단일 환경 약 5,000 스텝/s, PPO 3M 스텝이 CPU 약 8분. 첫 정책은 0.84 m/s로 빨라졌지만 yaw 40° 방향 이탈과 밀기 회복력 미개선을 남긴다.

- reward 설계(12장) — 한 번에 항 하나만 바꾸고 나머지(seed·스텝·PPO 설정·정규화)를 고정하는 통제 실험. heading·lateral 항으로 yaw 이탈을 제거하고, 속도 추종 항으로 "빨라질 이득"을 없애자 충격 93 → 64 N, CoT 0.58 → 0.45로 걸음 전체가 달라지는 과정을 기록.
- curriculum(12.4) — 처음부터 60 N vs 20 → 60 N 램프 vs 램프 후 유지, 여섯 조건을 비교. 최종 정책은 0.49 m/s, yaw +4°, 접지 충격 68 N, CoT 0.55, 밀기 50 N 100% 회복(60 N 17%).

- 확장(13장) — 최종 정책을 거친 지형·마찰·짐·경사에 놓아 강건성 지도를 그림. 거친 지형과 짐에는 강하지만 경사에서 yaw가 60° 이상 흐르고 10° 경사에서 넘어진다는 한계를 그대로 실음. MJX는 CPU에서 배치 1024로도 C 엔진보다 느리다(12k vs 67k 스텝/s)는 벤치마크 결과 포함.
이족보행편 — 서는 것부터가 균형 문제

- 서기와 균형(6장) — 관절 PD만으로는 26 N에 넘어진다. capture point 식으로 "발을 옮기지 않는 한 어떤 제어기도 약 31.5 N을 넘지 못한다"는 상한을 먼저 계산하고, 사람의 ankle strategy를 gain sweep으로 구현해 그 상한에 접근시킨다.
- 정적 보행(7장) — 무게 옮기기(sway) IK + 결합 위상 진동자로 한 번에 한 발씩 걷는 걸음. 0.8 Hz에서 0.114 m/s, cadence 96/min, double support 0.36. 주파수×sway 84칸 지도에서 57칸이 넘어지고 살아남는 곳은 "느릴수록 큰 sway"의 대각선 띠라는 것을 sweep으로 밝힘. 20 N 밀기까지 버티고 25 N에 넘어진다.

- 넘어짐의 해부(8.5) — −10 N 밀기에서 넘어지기까지 사건의 순서를 ms 단위로 재구성: CoP가 발 모서리에 닿음(678 ms 전) → roll 10°(670 ms) → capture point 이탈(650 ms) → CoM 이탈(526 ms). "무엇이 먼저 경고하는가"를 데이터로 답한다.
- 학습(10~12장) — 첫 PPO 정책은 0.44 m/s(하드코딩의 4배)로 빨라졌지만 yaw −53°, 좌우 비대칭(오른발 위상 0.68), 접지 충격 144 N, 앞기울임 +7.4°를 남긴다. 12장에서 heading·lateral·attitude·symmetry(반 주기 전 반대쪽 다리 보정과의 거울 대칭)·impact·action_rate 항을 하나씩 더해 yaw −7°, 위상 0.50, 충격 103 N, CoT 0.50, 옆 밀기 15 N 100% 회복의 최종 정책에 도달.

- 넘지 못한 벽도 기록 — 밀기 curriculum(10 → 30 N)은 20 N을 넘는 순간 에피소드 길이가 1000 → 450으로 붕괴했고, 13장에서 보정 폭을 0.15·0.20 rad로 넓혀도 정책은 평균 2.6°밖에 쓰지 않아 20 N 벽은 그대로였다. 결과가 가설을 기각하면 숨기지 않고 원리로 설명하는 것이 이 책의 방식.

실습 A to Z — 독자가 손에 쥐는 것
- 장 사이 공유 패키지
quadbook/·bipedbook/(robot·control·cpg·sim·gait·analysis·env·render, 각 약 1,000~1,200줄)을 직접 설계. 각 장의 스크립트가 이 패키지 위에서 절 하나와 1:1로 대응하고, 뒤 장이 앞 장의 산출물(정책 zip, 접촉 로그 npz, 지표 json)을 읽는 파이프라인. - 모든 실습 스크립트가
--view --speed 0.5로 같은 장면을 viewer에서 실시간 재생 — 책의 그림이 곧 독자의 화면이 되도록. 그래프만으로 끝내지 않고 실습 단위마다 렌더링 프레임을 남기는 규칙을 두 권 228장의 그림에 적용. - 학습 결과(정책 zip + VecNormalize 통계 + 학습 곡선 CSV + 평가 지표 json)를 저장소에 동봉해 학습을 건너뛰고도 12·13장의 분석을 재현할 수 있게 구성. 버전 고정(
mujoco==3.12.0, Python 3.12, uv lock)과 macOSmjpython수정 스크립트까지 포함.
집필·출판 파이프라인 — 책도 소프트웨어처럼
- XHTML 단일 원본(
src/OEBPS) →build.sh(줄표 검사 → zip → epubcheck 5.3) → EPUB 3. 본문 수정은 원본에서만, 두 출판물은 스크립트가 만든다. audit.py로 교차 참조·그림/표 번호·이미지 manifest·목차↔h1·코드 출처 주석·조사·용어를 전수 감사,termify.py로 영어 원어 용어 표기와 뒤따르는 조사(policy가/observation이)를 일괄 교정.- 위키독스 동기화 도구: 페이지 id·본문 해시·이미지 해시를 상태 파일에 두고 바뀐 페이지와 이미지만 업로드(Cloudflare 429 지수 백오프 포함). 사족보행편은 영문판 빌드(
build_en.py)가 장마다 그림·표·코드·수식 개수를 한국어판과 대조하고 한글 잔존을 검사. - 문체 규칙(줄표 금지, 영어 원어 용어, "~습니다" 경어체)을 훅과 빌드 검사로 강제해 두 권의 톤을 일치시킴.
규모
- 사족보행편: 13장 + 부록 4, 본문 한글 약 10.6만 자, 그림 103장, 코드 블록 141개. 실습 스크립트 61편(5,070줄) + 공유 패키지 973줄. 한국어·영어 두 판.
- 이족보행편: 13장 + 부록 4, 본문 한글 약 18.5만 자, 그림 125장, 코드 블록 187개. 실습 스크립트 65편(7,523줄) + 공유 패키지 1,168줄.
- 학습 실험: 두 권 합쳐 PPO 설정 30여 개를 seed 5개로 반복 평가. 이족보행편 12·13장 학습만 CPU 약 125분.
배움
- 보상 설계는 명세가 아니라 실험 과학 — 항의 크기를 틀린 것보다 항을 빠뜨린 것이 훨씬 위험했다. 박물관에 전시할 만한 이상한 걸음은 전부 "무엇을 묻지 않았는가"에서 나왔다.
- residual 구조의 양면 — CPG 위에 ±0.1 rad 보정만 얹는 구조는 백지 학습의 붕괴를 막아 주지만, 그 폭이 밀기 회복력의 병목이 되기도 한다. 이족보행편의 20 N 벽이 그 증거.
- 측정 도구가 먼저 — duty factor·위상·CoP·capture point를 먼저 손에 쥐어야 "걷는다"는 인상이 숫자가 되고, 그 숫자가 있어야 학습이 무엇을 고쳤는지 말할 수 있다.
- 두 권을 같은 뼈대로 쓰면서 확인한 것 — 네 발에서 배운 원리는 두 발에서도 통하지만, 균형이라는 전제가 빠지면 같은 코드가 첫 걸음부터 넘어진다.