IMU

hub.imu exposes the fused inertial state (see hub._imu_module). Two backends are supported behind the same API:

  • BNO085 — 9-DoF fusion done on-chip via SH-2 Rotation Vector. Yaw has an absolute reference (magnetometer).

  • LSM6DSL — complementary filter on pitch/roll; gyro-integrated yaw (drifts, no magnetometer).

Attitude

Angles are degrees, wrapped to [-180, 180]:

import hub
print(hub.imu.pitch, hub.imu.roll, hub.imu.yaw)

Additional un-fused readings for debugging:

Attribute

Meaning

pitch_accel

Raw accelerometer-derived pitch (noisy, no drift)

roll_accel

Raw accelerometer-derived roll

pitch_gyro

Pure gyro-integrated pitch (drifts)

yaw_gyro

Pure gyro-integrated yaw (drifts)

roll_gyro

Pure gyro-integrated roll

Raw samples

a = hub.imu.acceleration   # _vec3, mG (hub frame)
g = hub.imu.gyro_rate      # _vec3, dps, calibrated-bias subtracted
print(a.x, a.y, a.z, a.length())

Calibration

  • calibrated — LSM6DSL: gyro-bias averaging finished (~2 s after boot). BNO085: rotation-vector accuracy >= medium.

  • reset() — zero yaw. LSM6DSL restarts bias calibration (hold hub still); BNO085 captures current fused yaw as new zero (Tare).

  • start_calibration() — enable all backend- supported calibrators. BNO085 wants a figure-8 motion (mag) and several flat orientations (accel/gyro).

  • save_calibration() — persist calibration. BNO085 saves DCD to flash; LSM6DSL is a no-op (bias re-runs each boot).

Example loop

import hub, time

while not hub.imu.calibrated:
    time.sleep_ms(50)

hub.imu.reset()

while True:
    print(f"pitch {hub.imu.pitch:+6.1f}  yaw {hub.imu.yaw:+6.1f}")
    time.sleep_ms(50)