이번 단계 목표: 03번에서는 한 자세를 ‘유지’만 했습니다. 이번엔 목표 자세를 시간에 따라 바꿔서 동작을 만듭니다. PD 제어는 그대로, ‘목표값’만 움직이면 동작이 된다는 걸 체험합니다.
핵심 아이디어 — “목표를 움직이면 동작이 된다”
03번에서 배운 PD 제어는 “정해진 목표 각도로 관절을 데려가는” 장치였습니다. 그럼 그 목표 각도 자체를 매 순간 조금씩 바꾸면 어떻게 될까요? 관절이 그 목표를 쫓아가면서 움직임(동작) 이 만들어집니다. 애니메이션이 정지 그림을 빠르게 넘겨 움직임을 만드는 것과 같아요.
for i in range(n_steps):
t = i * dt
target = pose_at(t) # 시각 t에 따라 목표 자세를 계산
robot.control_dofs_position(target, motor_dofs)
scene.step()
pose_at(t) 함수가 이 단계의 전부입니다. “1~5초엔 스쿼트, 5~8초엔 팔 흔들기”처럼 시간표대로 목표 자세를 돌려주는 함수예요.
부드럽게 움직이는 비결 — 보간(interpolation). 목표를 갑자기 확 바꾸면 로봇이 휘청합니다. 그래서 코사인 곡선
0.5*(1-cos(...))으로 목표를 서서히 바꿉니다. 0에서 1까지 천천히 출발해 천천히 멈추는, 자연스러운 가감속 곡선입니다.
실행:
python scripts/03_motion_squat_wave.py # 헤드리스 + mp4 저장
python scripts/03_motion_squat_wave.py --vis # 실시간으로 보기
결과 — 스쿼트 2회 + 한 손 흔들기
| 스쿼트(앉기) | 손 흔들기 |
|---|---|
![]() |
![]() |
전체 동작은 영상으로:
스쿼트 2회 + 팔 흔들기 동작 완료
스쿼트 최저점 몸통 높이: 0.747 m (서 있을 때 ≈0.79 m)
동작 후 몸통 높이: 0.790 m
넘어지지 않고 동작 수행!
회고 — 이 단계에서 제대로 한 수 배웠습니다
블로그에 정직하게 남기려고, 넘어졌던 시도들을 그대로 적습니다. 여기서 얻은 직관이 뒤(보행·RL)에서 계속 쓰입니다.
① “보기 좋은 깊은 스쿼트”는 넘어졌다
처음엔 무릎을 깊게(0.95rad) 굽혀 멋진 풀스쿼트를 시도 → 그대로 주저앉음. 깊이 앉을수록 무게중심이 발 뒤/밖으로 벗어나기 쉽고, 모터가 버텨야 할 힘도 급격히 커집니다. 얕게(무릎 0.6rad) 하되, 무릎·고관절·발목을 같은 비율로 굽혀 무게중심을 발 위에 유지하니 성공.
② 손 흔들기에서 계속 앞으로 고꾸라졌다 — 그리고 결정적 깨달음
팔을 앞으로 들어 흔들게 했더니, 천천히/작게 들어도 번번이 앞으로 넘어졌습니다. 구간별 높이를 찍어 보니 스쿼트는 멀쩡한데 팔을 드는 순간(5초)부터 무너지더군요.
그래서 4가지 팔 동작을 실험해 봤습니다(표로 정리):
| 동작 | 결과 |
|---|---|
| 양팔을 옆으로 들기 | 안정 |
| 오른 팔뚝만 흔들기 | 안정 |
| 오른팔을 옆으로 들고 흔들기 | 안정 |
| 양팔을 앞으로 들기 | 넘어짐 |
패턴이 보였습니다: ‘앞으로’는 넘어지고 ‘옆으로’는 안 넘어진다.
왜? 로봇의 발은 앞뒤로 짧고, 두 발은 좌우로 벌어져 있습니다. 그래서 무게중심이 머물 수 있는 안전지대(지지면, support polygon)가 앞뒤로는 좁고 좌우로는 넓습니다. 팔을 앞으로 들면 무게중심이 좁은 앞쪽 경계(발끝)를 금방 넘어 고꾸라지지만, 옆으로 들면 넓은 좌우 범위 안에 머물러 버팁니다. 그래서 최종 동작은 팔을 옆으로 들어 흔드는 것으로 정했습니다.
③ 이게 왜 중요한가 — 다음 단계의 복선
지금 우리 로봇은 미리 정한 목표 자세를 그대로 따라갈 뿐, 스스로 균형을 잡지 못합니다. 그래서 무게중심이 조금만 벗어나도 회복하지 못하고 넘어집니다. “넘어지려 하면 발목으로 버티고 팔을 휘저어 균형을 되찾는” 능력이 없는 거죠. 이게 바로 다음 두 단계의 주제입니다.
재검증 노트 (2026-08-31 추가)
10번 문서에서 “영상은 검증이 아니다”를 배운 뒤, 이 모션도 같은 눈으로 재검증했습니다. 이번엔 걸음이 아니라 “실제 G1 모터로 재현 가능한 동작인가”가 질문입니다. 스쿼트+팔 흔들기 8초 동안 관절별로 실제 가한 토크를 기록해 실물 토크 한계(URDF effort: 무릎 139Nm·엉덩이 88Nm· 발목 35Nm·어깨/팔꿈치 25Nm)와 비교한 결과:
- 합격. 최대 토크가 발목 17Nm(한계의 49%), 무릎 11Nm(8%), 팔꿈치도 여유 — 전 구간 포화 0%. 추종 오차도 최대 14.5°(빠른 팔 흔들기 구간)로 준수합니다. 이 동작은 실물 로봇에 그대로 가져가도 무리가 없는 얌전한 궤적입니다.
- 스쿼트 중 앞기울임 12.8°는 버그가 아니라 설계였습니다. 목표 자세(엉덩이 −31°+무릎 40°+ 발목 −19°)의 합쳐지면 골반이 약 −10° 숙여지게 되어 있고, 사람 스쿼트의 상체 숙임과 같은 성격입니다. 좌우 흔들림(roll)은 0.0° — 완전히 안정적이었습니다.
- 솔직한 고백 하나: ‘스쿼트’라기엔 얕습니다(몸통 하강 4.3cm, 무릎 40° — 쿼터 스쿼트). 깊은 스쿼트는 무릎 90° 이상이 필요한데, 그건 03번에서 배운 “깊게 굽히면 못 버틴다” 제약과 다시 만나는 문제라 이 단계에선 여기까지가 적절한 절충이었습니다.
재검증에 쓴 도구: Genesis의
get_dofs_control_force()(실제 가한 토크)와get_dofs_force_range()(토크 클램프 범위 — Genesis는 URDF effort 한계로 자동 클램프).
다음 단계
균형이 이렇게 까다로운데, 하물며 걷기는 어떨까요? 한 발로 서는 순간이 반복되는 보행을 손으로 직접 짜보고, 왜 그렇게 어려운지 확인합니다 → 05. 보행 도전

