← BlogIsaac Sim 작동 원리· 5/10

DriveAPI로 바퀴를 굴린다는 것 — 텔레포트가 아니라 토크

바퀴·관절이 articulation의 joint이고, DriveAPI(목표 속도 + 강성/감쇠)로 PhysX가 토크를 만들어 실제로 굴린다는 것. 위치를 직접 옮기는 kinematic과의 근본 차이.

약 3분
Isaac-SimPhysXDriveAPIarticulationlocomotion

DriveAPI로 바퀴를 굴린다는 것

로봇을 움직이는 방법은 두 가지입니다.

  1. 위치를 직접 옮긴다 — "이 로봇을 1m 앞으로 순간이동" (kinematic)
  2. 바퀴에 힘을 준다 — 바퀴 관절에 토크를 걸어 마찰로 굴러간다 (PhysX)

겉보기엔 둘 다 로봇이 앞으로 가지만, 검증 가능한 시뮬레이션은 2번입니다. 그리고 그 2번을 가능하게 하는 게 DriveAPI입니다.

바퀴는 관절이고, DriveAPI는 그 관절의 모터다

로봇의 바퀴·팔 관절은 모두 articulation(연결된 강체 묶음) 의 joint입니다. 각 joint에 DriveAPI를 붙이면, 목표 속도(또는 위치) + 강성(stiffness) + 감쇠(damping) 를 주는 것만으로 PhysX가 알아서 토크를 계산해 줍니다.

도식 렌더링 중…

핵심은 DriveAPI가 스프링처럼 동작한다는 점입니다. "현재 속도"와 "목표 속도"의 차이에 비례해 토크를 냅니다(강성). 그래서 바퀴가 미끄러지거나 막히면 실제처럼 반응합니다 — 위치를 강제로 박는 kinematic과 완전히 다릅니다.

한 런의 두 시점. 옅은 막대가 DriveAPI 에 넣은 바퀴 목표 각속도이고, 진한 막대가 그 틱에 실제로 측정된 관절 각속도다. 순항 중(왼쪽)에는 목표와 실측이 거의 겹치지만, 회피로 감속하는 중(오른쪽)에는 목표 [0.89, 1.00] 에 실측 [1.13, 0.76] 으로 어긋난다 — 바퀴에 속도를 «박는» 게 아니라 오차만큼 토크를 주기 때문이다. 그런데도 몸체 속도는 명령 0.156 에 실측 0.152 로 붙는다.한 런의 두 시점. 옅은 막대가 DriveAPI 에 넣은 바퀴 목표 각속도이고, 진한 막대가 그 틱에 실제로 측정된 관절 각속도다. 순항 중(왼쪽)에는 목표와 실측이 거의 겹치지만, 회피로 감속하는 중(오른쪽)에는 목표 [0.89, 1.00] 에 실측 [1.13, 0.76] 으로 어긋난다 — 바퀴에 속도를 «박는» 게 아니라 오차만큼 토크를 주기 때문이다. 그런데도 몸체 속도는 명령 0.156 에 실측 0.152 로 붙는다.

여기서 한 가지가 더 따라옵니다 — 감쇠(damping) 값을 무엇으로 정하느냐입니다. 같은 런의 설정 로그는 이렇게 찍힙니다.

[PhysX V16] ... mass=56.58kg(USD), manifest=50.0kg, scale=1.23, drive=differential
[Isaac] HO_A: P0-3 DriveAPI damping=38760 (mass=56.58kg, factor=0.863)

감쇠가 질량에서 유도되고, 그 질량은 매니페스트에 적힌 50.0 kg 이 아니라 USD 자산이 들고 있는 56.58 kg 입니다. 둘이 어긋나 있으면, 문서에 적어 둔 값이 아니라 자산 안의 값이 바퀴를 굴립니다. 「사양서에는 50 kg 인데 왜 이렇게 굼뜨지」의 답이 여기 있는 경우가 많습니다.

⚠️ 로그에서 actual_spdfb_spd 를 섞어 읽지 마십시오 — 앞은 최근 5초 이동거리 평균이고 뒤가 순간 몸체 속도입니다. 감속 직후에는 앞 값이 아직 높아서, 그것만 보면 「명령이 안 먹는다」로 잘못 읽힙니다.

  • 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의 토대다.