unitree go2 - imu - specific force

전찬욱·2025년 12월 1일
  1. Full GT Acceleration Computation (lines 153-207)
    Implemented the complete formula: a_base = R_br * f_sensor + g_base - α×r - ω×(ω×r)

    Components:

  • Kinematic acceleration: From velocity differentiation
  • Gravity: projected_gravity * 9.81 (converted from normalized direction)
  • Tangential acceleration: -α × r (from angular acceleration)
  • Centripetal acceleration: -ω × (ω × r) (from angular velocity)
  1. IMU Offset (line 60)
  • Uses exact offset from go2.xml: r = [-0.02557, 0, 0.04232] m

    You should now see:

  • IMU and GT curves that closely match (small error)

  • Z-axis showing ~9.81 m/s² (not 0) when robot is stationary

그림처럼 z가 9.78정도를 가진다. 로봇의 바디가 기울어져있으므로 R*accelermeter 의 영향으로x와 y도 값을 가진다.

observation_processor.py - IMU 원시 데이터

imu_acc = np.array(lowstate.imu_state.accelerometer) # Specific force!

imu_plotter_ros2.py - GT 계산

gt_acc = kinematic_acc - gravity_acc # = 0 - (-9.81) = +9.81

  • Z = 9.788 m/s²는 정지 상태에서 정상적인 specific force 값

  • 이것은 바닥의 수직항력을 나타냄

  • IMU accelerometer는 항상 specific force (a - g)를 측정

  • 자유낙하 중일 때만 specific force = 0 (무중력 상태)

    Specific force는 "중력을 느끼지 않는 가속도"가 아니라 "중력을 제외한 힘에 의한 가속도"입니다! 정지해 있을 때 오히려 +g만큼 큰
    값이 나오는 것이 정상입니다. 🎯

  1. 데이터 변환 관계

    ┌─────────────────┐
    │ Velocity (v) │ ← ROS2 topic: /go2/imu/base_velocity
    └────────┬────────┘
    │ differentiate

    ┌─────────────────┐
    │ Acceleration(a) │ = f + g
    └────────┬────────┘
    │ subtract gravity

    ┌─────────────────┐
    │ Specific Force │ = a - g (IMU measures this!)
    │ (f) │
    └─────────────────┘

a_base = R_br * f_sensor + g_base - α×r - ω×(ω×r)

이것을 specific force로 변환하면:
f_base = a_base - g_base
= R_br * f_sensor - α×r - ω×(ω×r)

문제 1: R_br 행렬이 없음

  • Unitree SDK의 imu_state.accelerometer는 이미 base frame에 있지만
  • IMU location에서 측정된 값입니다
  • test_imu_frame.py에서 확인했듯이 R_br = identity (회전 없음)
  • 따라서 R_br 곱셈은 생략 가능

결론적으로 순수한 Linear Acceleration을 얻을 수 있다.
그 방법은 f+gprojected_to_bodyf + g_{projected\_to\_body} 를 통해 할 수 있다.

Linear Acceleration은 specific_force + gravity로 계산되는 값

1. IMU에서 받는 데이터

  # imu_plotter_ros2.py:108
  linear_specific_force = np.array([msg.vector.x, msg.vector.y, msg.vector.z])
  - /go2/imu/accelerometer 토픽에서 받음
  - Specific Force (f): IMU가 측정하는 비력 = a - g

2. Gravity 받기

  # gravity_callback:122
  self.latest_gravity = np.array([msg.vector.x, msg.vector.y, msg.vector.z])
  - /go2/imu/projected_gravity 토픽에서 받음
  - Normalized direction: 크기가 1인 단위 벡터 (body frame)

Projected Gravity 계산 과정

  1. 개념

    "Projected"의 의미: World frame의 gravity를 Body frame으로 투영(회전 변환)한 것

    World frame: gravity = [0, 0, -9.81] (항상 아래로)
    Body frame: gravity = ? (로봇 자세에 따라 변함)

Step 1: Quaternion 받기
quat = np.array(lowstate.imu_state.quaternion) # [w, x, y, z]

  • IMU에서 제공하는 현재 자세 (orientation)

    Step 2: Quaternion → Rotation Matrix

  w, x, y, z = quat

  # World-to-Body rotation matrix (transpose of body-to-world)
  R_T = np.array([
      [1-2*(y*y+z*z), 2*(x*y+w*z), 2*(x*z-w*y)],
      [2*(x*y-w*z), 1-2*(x*x+z*z), 2*(y*z+w*x)],
      [2*(x*z+w*y), 2*(y*z-w*x), 1-2*(x*x+y*y)]
  ])
  • R_T: World → Body 변환 행렬

Step 4: Body frame으로 변환

# Transform to body frame
gravity_body = R_T @ gravity_world_normalized

# Ensure normalized
gravity_body = gravity_body / np.linalg.norm(gravity_body)

3. Linear Acceleration 계산

  # imu_callback:112-115
  if self.latest_gravity is not None:
      gravity_vector = self.latest_gravity * 9.81  # m/s²로 변환
      self.latest_linear_acceleration = linear_specific_force + gravity_vector

공식:
a = f + g

  • a: Linear acceleration (kinematic acceleration)
  • f: Specific force (IMU 측정값)
  • g: Gravity vector in body frame

0개의 댓글