How to Find Heading Angle from MPU9250 with Raspberry Pico W

The MPU9250 is a compact and versatile motion-tracking device that combines a 3-axis accelerometer, 3-axis gyroscope, and 3-axis magnetometer into a single package. Its onboard magnetometer can be used to estimate the "heading" angle. The heading angle, also known as the azimuth, refers to the direction in which an object or person is pointing or facing, relative to a reference direction, typically north. It is usually measured in degrees, with 0 degrees indicating a direction pointing towards true north, 90 degrees, 180 degrees, and 270 degrees indicating east, south, and west, respectively. The heading angle or azimuth can be determined using readings from a magnetometer with other calibration techniques to accurately estimate which way the direction. This can be useful for navigation purposes. We will be showing how to get such a value in this tutorial, which can be translated into a direction (North, South, East, West, etc).

Step 1-) Understanding Hard-Iron and Soft-Iron Biases

Because we live in an age where we are surrounded by magnetic material and magnetic fields induced by electronics around us, such as our phones and laptop, we need to be able to remove the fluctuations these variables can cause in our experiment. From our surroundings, there are mainly two biases we need to consider in calibration: hard-iron and soft-iron biases.

Hard iron bias refers to a type of systematic error that is caused by the presence of a fixed magnetic field, such as the Earth's magnetic field or nearby ferromagnetic materials, that can distort the magnetometer readings. Hard iron bias can be corrected by subtracting a fixed offset value from the magnetometer readings, which can be determined by calibrating the sensor in a known magnetic field environment.

On the other hand, soft iron bias is caused by the presence of a non-uniform magnetic field, such as the distortion of the Earth's magnetic field by nearby objects or materials. Soft iron bias can result in the magnetometer readings being skewed towards a particular direction or axis. Soft iron bias can be corrected by using a calibration process that involves rotating the sensor in a 3D space and collecting magnetometer data, which can be used to calculate correction values that can be applied to the magnetometer readings to compensate for the non-uniform magnetic field.

In our MicroPython code, we will use an MPU9250 library that has a calibration function that accounts for both biases. For more details on the nature of the calibration please see

Step 2-) Understanding Low-Pass Filter

Despite calibration, we can still get sudden fluctuations, that is, random spikes in magnetic readings in our magnetometer! We do not want sudden fluctuations to deter us from the signal we are trying to look for, which is Earth’s magnetic field. To deal with spikes of useless data we can implement a low-pass filter in a couple of lines of code shown here:

def low_pass_filter(prev_value, new_value):
    return 0.85 * prev_value + 0.15 * new_value

You can see that the weights in the filter add up to 1 (0.85 + 0.15), this has to be the case. The premise of the filter is that we essentially trust the previous value more than we trust a new value. By taking this approach to sensor readings, we can eliminate or reduce this noise and improve the overall quality of our signal measuring.

The video shows an example of how this can be applied to an acceleration signal. You can see in red that the unfiltered raw value for acceleration is susceptible to sudden change while the filtered signal in blue is much more stable. While this does introduce a bias in our measurements it is okay because the signal always converges to the true signal over some time if the stimulus is consistent. We will apply this filter to our magnetometer readings to get a more accurate heading angle estimation.

Step 3-) Physical Setup

Make connections as shown

Step 4-) Code and Library

You will need to add three libraries to your code to get readings, all found here

  • MPU9250
  • ak8963
  • MPU6500

    Once you have such libraries saved you can use my example code here to start getting readings

    from machine import I2C, Pin
    import math
    import utime
    from mpu9250 import MPU9250
    i2c = I2C(0, sda=Pin(0), scl=Pin(1), freq=400000)
    sensor = MPU9250(i2c)
    filtered_magx, filtered_magy = 0, 0
    DECLINATION = -1 * 3.19
    # Want to thank Kevin McAleer
    def low_pass_filter(prev_value, new_value):
        return 0.85 * prev_value + 0.15 * new_value
    def compass(angle):
        if angle > 337 or angle <= 22:
            direction = 'North'
        elif angle > 22 and angle <= 67:
            direction = 'North East'
        elif angle > 67 and angle <= 112:
            direction = "East"
        elif angle > 112 and angle <= 157:
            direction = "South East"
        elif angle > 157 and angle <= 202:
            direction = "South"
        elif angle > 202 and angle <= 247:
            direction = "South West"
        elif angle > 247 and angle <= 292:
            direction = "West"
        elif angle > 292 and angle <= 337:
            direction = "North West"
        return direction
    while True:
        # We do not need the z value
        magx_new, magy_new, _ = sensor.magnetic
        filtered_magx = low_pass_filter(filtered_magx, magx_new)
        filtered_magy = low_pass_filter(filtered_magy, magy_new)
        heading_angle_in_degrees = math.atan2(filtered_magx, filtered_magy) * (180 / math.pi)
        heading_angle_in_degrees_plus_declination = heading_angle_in_degrees + DECLINATION
        if heading_angle_in_degrees_plus_declination < 0:
            heading_angle_in_degrees += 360
            heading_angle_in_degrees_plus_declination += 360
        print('###Without Declination###')
        print('###Plus Declination###')

    The main thing in this code is the calibration, which you will need to ensure the accelerometer is on a flat horizontal surface. Once calibration begins be sure to move your accelerometer around in a figure 8 fashion (shown in my Youtube video).

    Another important thing is the concept of declination. Which will be needed to calculate the direction of the true north pole rather than the magnetic north pole. You can find your declination angle by searching your location online on Google! Everyone's declination can be dramatically different depending on what area you are on the Earth, so be sure to change it in the code...

    If you have done everything accurately you will get results on par with the compass app on the iPhone or other apps.


    Hope you learned something awesome in this tutorial and you got great results. For any confusion please watch the Youtube Video as I go into more detail there. Do not forget to like, comment, and subscribe. As always, thanks for reading, and stay tuned.

    Back to blog

    Leave a comment

    Please note, comments need to be approved before they are published.