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 |
|---|---|
|
Raw accelerometer-derived pitch (noisy, no drift) |
|
Raw accelerometer-derived roll |
|
Pure gyro-integrated pitch (drifts) |
|
Pure gyro-integrated yaw (drifts) |
|
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)