Blog

How to Properly Calibrate an IMU on a Mobile Robot (And Why Most Teams Skip It)

Adrian Krzemiński,

How to Properly Calibrate an IMU on a Mobile Robot

TL;DR: Proper IMU calibration in ROS 2 means three things: removing static bias from accelerometers and gyroscopes, characterizing noise with Allan variance, and feeding the resulting parameters into your sensor fusion stack (typically robot_localization EKF). Skip this step and your odometry can drift meters per minute, your EKF can diverge under vibration, and SLAM will often blame the LiDAR. This guide shows the exact procedure – static bias capture, Allan variance with a ROS 2-compatible Allan variance tool, and integration with an EKF on a mobile robot like the Leo Rover.

Why IMU Calibration in ROS 2 Matters More Than You Think

An uncalibrated MEMS IMU is not a bad sensor. It is an unknown sensor. The MPU-9250, BNO055, ICM-20948, and similar chips used on many mobile robots ship with factory trims that drift with temperature, soldering stress, and mounting orientation. Without IMU calibration in ROS 2, your /imu/data topic publishes plausible-looking values that quietly corrupt every downstream estimator.

Most teams skip calibration for three reasons: the BNO055 advertises “self-calibration” (which helps its internal fusion output but does not replace characterizing the raw inertial data and covariances for your own estimator), the EKF tuning loop hides bias as process noise, and nobody fails a demo because of 2 percent odometry drift. Until they do, in a 200-meter field run.

Here is what uncalibrated IMU data costs you in practice on a UGV:

  • Yaw drift: An uncompensated gyro bias of 0.5 deg/s integrates to 30 degrees per minute of heading error.
  • Wheel-IMU fusion divergence: The EKF in robot_localization assumes zero-mean Gaussian noise. A constant accelerometer bias violates this and pulls position estimates sideways.
  • SLAM map distortion: Cartographer and many LiDAR-inertial or visual-inertial SLAM stacks use IMU for motion priors; SLAM Toolbox usually sees the effect indirectly through odometry and TF. Biased angular velocity can contribute to map distortion and loop closure failures.

What You Need Before You Start

Calibration is reproducible only if the setup is controlled. For a mobile robot like the Leo Rover, the IMU is mounted in the robot and the robot itself becomes the calibration jig.

  • A flat, vibration-free surface (a granite slab or a stable workbench).
  • The robot powered up and thermally stabilized – run it for at least 15 minutes before recording.
  • ROS 2 (Humble or Jazzy) with the IMU driver publishing sensor_msgs/Imu at a fixed rate (100 Hz minimum, 200 Hz preferred).
  • ros2 bag for recording, and a ROS 2-compatible Allan variance tool such as allan_ros2 for noise analysis.

Verify your topic rate before anything else:

ros2 topic hz /imu/data_raw

If the rate jitters by more than 5 percent, fix that first. Allan variance assumes uniform sampling.

How to Perform Static Bias Calibration

Static bias is the simplest and highest-impact step. With the robot perfectly still, every gyroscope axis should read zero for UGV-grade MEMS purposes and the accelerometer magnitude should equal local gravity (approximately 9.81 m/s², check your latitude and altitude for a precise value).

Record 5 minutes of stationary data:

ros2 bag record -o imu_static /imu/data_raw

Then compute the mean of each channel. A minimal Python script using the rosbags Python library:

import numpy as np
from rosbags.rosbag2 import Reader
from rosbags.serde import deserialize_cdr

gx, gy, gz, ax, ay, az = [], [], [], [], [], []
with Reader('imu_static') as reader:
    for conn, _, raw in reader.messages():
        msg = deserialize_cdr(raw, conn.msgtype)
        gx.append(msg.angular_velocity.x)
        gy.append(msg.angular_velocity.y)
        gz.append(msg.angular_velocity.z)
        ax.append(msg.linear_acceleration.x)
        ay.append(msg.linear_acceleration.y)
        az.append(msg.linear_acceleration.z)

print(f"Gyro bias (rad/s): {np.mean(gx):.5f}, {np.mean(gy):.5f}, {np.mean(gz):.5f}")
print(f"Accel mean (m/s^2): {np.mean(ax):.4f}, {np.mean(ay):.4f}, {np.mean(az):.4f}")
print(f"Gravity magnitude: {np.sqrt(np.mean(ax)**2 + np.mean(ay)**2 + np.mean(az)**2):.4f}")

The gyro means are your bias offsets – subtract them in the driver or in a preprocessing node. The accelerometer is harder: a naive constant subtraction from one static pose confuses bias with gravity. Use a six-position tumble (each axis up and down) to separate bias from scale factor. The imu_utils and imu_tk packages, plus community ROS 2 forks and wrappers, can automate this, but check maintenance status before using them in a production ROS 2 stack.

How to Run Allan Variance Analysis with allan_ros2

Static bias removes the DC offset. Allan variance tells you how noisy the sensor is at every time scale, which is exactly what an EKF needs to weight the IMU correctly. The output gives you coefficients such as angle random walk or velocity random walk, bias instability, and rate random walk; these can be converted into the white-noise densities used by estimation stacks. White-noise density maps to robot_localization measurement covariance entries, while bias instability is mainly used to understand unmodeled drift or to configure estimators that explicitly estimate IMU bias, such as many VIO stacks like VINS-Fusion or OpenVINS.

Record a long stationary log – 3 hours minimum, 6 hours recommended:

ros2 bag record -o imu_allan /imu/data_raw

Install a ROS 2-compatible Allan variance tool such as allan_ros2, or a ROS 2 branch/fork of the original allan_variance_ros. Do not use an unmodified ROS 1 package on Humble or Jazzy:

cd ~/ros2_ws/src
git clone <your-allan-ros2-fork-url> allan_ros2
cd ~/ros2_ws
rosdep install --from-paths src --ignore-src -r -y
colcon build --packages-select allan_ros2

Configure a YAML with your IMU rate and message type, then run the analyzer. The result is a log-log plot of Allan deviation versus averaging time. Read the slopes: -1/2 slope at short tau gives white noise density, the flat minimum gives bias instability, +1/2 slope gives rate random walk.

For a typical low-cost MEMS IMU (MPU-9250 or ICM-20948 class), expect:

  • Gyro white noise: approximately 0.0002 to 0.005 rad/s/√Hz
  • Gyro bias instability: approximately 10 to 200 deg/h
  • Accel white noise: approximately 0.0005 to 0.005 m/s²/√Hz

These figures are order-of-magnitude expectations from datasheet families; always use your own measured values.

How to Feed Calibration Results into robot_localization

The EKF in robot_localization consumes covariance matrices on every sensor_msgs/Imu message. The correct values are the variances of your measurements, not arbitrary tuning knobs.

For angular velocity covariance (diagonal entry), use the square of the gyro white noise multiplied by the sample rate, assuming the usual one-sided noise-density convention used by IMU datasheets and estimator tools. For a gyro at 200 Hz with 0.003 rad/s/√Hz noise density: variance = (0.003)² × 200 = 0.0018 (rad/s)². Same logic for acceleration.

In your EKF YAML, when /imu/data contains a valid orientation estimate:

imu0: /imu/data
imu0_config: [false, false, false,
            true,  true,  true,
            false, false, false,
            true,  true,  true,
            true,  true,  true]
imu0_differential: false
imu0_remove_gravitational_acceleration: true

If you only publish raw gyro and accelerometer data without a valid orientation estimate, set the three orientation booleans to false and do not enable gravity removal unless another valid orientation source is available. Then set the covariances inside the publishing node, not in the EKF. The EKF reads the message covariance directly.

What Changes on a Real Robot After Calibration

On a differential-drive UGV running wheel odometry plus IMU fusion, properly calibrated IMU data typically reduces heading drift from several degrees per minute to a fraction of a degree per minute over a 10-minute closed-loop trajectory. This is the difference between a robot that arrives at the goal and one that needs GPS or a beacon to recover. For outdoor field robotics applications, where the platform spends hours navigating without map updates, calibration is not optional. The same applies to research deployments documented under Fictionlab’s research applications, where reproducibility of the sensor stack is a publication requirement.

FAQ

How often should you recalibrate the IMU?

Gyro bias drifts with temperature and time. Recalibrate static bias before every deployment session if you need sub-degree heading accuracy. Allan variance characterization is a one-time procedure per hardware unit unless you replace the IMU or change the mounting.

Does the BNO055 need calibration if it has a built-in fusion engine?

The internal fusion handles orientation estimation but does not replace measuring the raw gyro and accel noise and covariances for your own EKF. If you use only the quaternion output for simple orientation use, the built-in calibration may be sufficient. If you fuse raw gyro and accel in your own EKF, treat the BNO055 like any other MEMS IMU and calibrate it.

Can you calibrate the IMU while it is mounted on the robot?

Yes, and you should. Mounting stress changes accelerometer bias. Calibrate in situ with the robot powered, motors idle, and the chassis on a level surface.

What sample rate is enough for Allan variance?

100 Hz is a practical minimum for many mobile-robot IMUs. 200 Hz or higher captures short-tau white noise more accurately. The recording should be at least 10 to 100 times longer than the longest tau you want to analyze, and longer logs give more reliable long-tau estimates.

Why does the EKF still diverge after calibration?

Common causes: wrong frame transforms between base_link and imu_link, gravity removal enabled while the IMU orientation or transform is invalid, the IMU frame not following REP-103 conventions, or wheel odometry covariance set too low.

Do you need a turntable or rate table for ground robots?

No. Rate tables are for aerospace-grade calibration. For a wheeled UGV, static bias plus Allan variance covers the dominant error sources.

Where does temperature compensation fit in?

If your operating environment varies by more than approximately 20 degrees Celsius, characterize bias at three temperatures and fit a local linear model. Most indoor robots do not need this; field robots in winter do.

Ready to apply this to your platform? Start with the Leo Rover documentation to find the IMU driver topics and frame definitions, then run the static bias capture as the first step in your sensor commissioning checklist.


Read more