DriveAPI로 바퀴를 굴린다는 것
로봇을 움직이는 방법은 두 가지입니다.
- 위치를 직접 옮긴다 — "이 로봇을 1m 앞으로 순간이동" (kinematic)
- 바퀴에 힘을 준다 — 바퀴 관절에 토크를 걸어 마찰로 굴러간다 (PhysX)
겉보기엔 둘 다 로봇이 앞으로 가지만, 검증 가능한 시뮬레이션은 2번입니다. 그리고 그 2번을 가능하게 하는 게 DriveAPI입니다.
바퀴는 관절이고, DriveAPI는 그 관절의 모터다
로봇의 바퀴·팔 관절은 모두 articulation(연결된 강체 묶음) 의 joint입니다. 각 joint에 DriveAPI를 붙이면, 목표 속도(또는 위치) + 강성(stiffness) + 감쇠(damping) 를 주는 것만으로 PhysX가 알아서 토크를 계산해 줍니다.
도식 렌더링 중…
핵심은 DriveAPI가 스프링처럼 동작한다는 점입니다. "현재 속도"와 "목표 속도"의 차이에 비례해 토크를 냅니다(강성). 그래서 바퀴가 미끄러지거나 막히면 실제처럼 반응합니다 — 위치를 강제로 박는 kinematic과 완전히 다릅니다.
- differential drive: 좌/우 바퀴 속도 차이로 회전 (대부분의 AMR)
- ackermann: 앞바퀴 조향각 + 뒷바퀴 구동 (차량형)
차동 구동(differential drive)의 휠 속도 분배는 두 줄이면 됩니다.
# cmd_vel(전진 vx, 회전 ωz) → 좌/우 바퀴 각속도
def differential_drive(vx, wz, wheel_radius, wheel_base):
v_left = (vx - wz * wheel_base / 2) / wheel_radius
v_right = (vx + wz * wheel_base / 2) / wheel_radius
return v_left, v_right # → 각 바퀴 DriveAPI의 target velocity 로
cmd_vel이 바퀴 토크가 되기까지
내비게이션 스택이 내보낸 cmd_vel(전진 속도 vx, 회전 속도 ωz)이 실제 바퀴 회전이 되는 사슬은 이렇습니다.
도식 렌더링 중…
💡 DriveAPI는 스프링이라 에너지를 쌓는다 — 바퀴가 벽에 막혀 목표 속도에 못 미치면, 차이만큼 토크가 계속 쌓여 갑자기 튀어나갈 수 있습니다. 그래서 명령을 적절히 감쇠시켜 에너지 폭주를 막아야 합니다.
⚠️ 속도·토크 상한을 시스템 코드에 하드코딩하면 로봇마다 맞지 않습니다. 이런 값은 각 로봇의 명세(manifest) 파라미터로 분리하는 게 맞습니다 — 새 로봇은 명세 한 장으로 추가되니까요.
왜 굳이 토크인가
위치 텔레포트가 더 쉬운데 왜 토크로 풀까요? 현실과 같아야 하기 때문입니다. 경사로를 오르다 토크가 부족해 느려지는 것, 바닥이 미끄러워 헛도는 것, 무거운 짐에 가속이 둔해지는 것 — 이 모든 게 토크 기반 PhysX에서만 자연히 재현됩니다. 그게 시뮬에서 학습·검증한 정책이 실제 로봇에서도 통하는(Sim-to-Real) 이유입니다.
한 줄 정리
📌 바퀴·관절은 articulation의 joint이고, DriveAPI(목표 속도 + 강성/감쇠) 로 PhysX가 토크를 만들어 마찰로 굴린다. 위치를 박는 kinematic과 달리 경사·미끄러짐·하중이 자연히 재현되며, 이것이 Sim-to-Real의 토대다.
