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 https://www.appelsiini.net/2018/calibrate-magnetometer/

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

https://github.com/kevinmcaleer/mpu9250

  • 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
    
    
    sensor.ak8963.calibrate()
    
    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(heading_angle_in_degrees)
        print(compass(heading_angle_in_degrees))
        print('###Plus Declination###')
        print(heading_angle_in_degrees_plus_declination)
        print(compass(heading_angle_in_degrees_plus_declination))
        
        utime.sleep_ms(100)

    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.

    Conclusion

    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.

    Create a free account to access full content.

    All access to code and resources on ShillehTek.

    Signup Now

    Already a member? Sign In

    Explore More on Our Blog

    How to Use ADS1115 with the Raspberry Pi (Part 1)

    How to Use ADS1115 with the Raspberry Pi (Part 1)

    Discover how to expand your Raspberry Pi projects by integrating the ADS1115 ADC for precise analog signal reading....

    How to Install Pip Packages in AWS Lambda Using Docker and ECR

    How to Install Pip Packages in AWS Lambda Using Docker and ECR

    Learn how to streamline AWS Lambda deployments by using Docker and Amazon Elastic Container Registry (ECR) to package...

    Create Tabular Product Descriptions on Your Shopify Store

    Create Tabular Product Descriptions on Your Shopify Store

    Enhance your Shopify store's product pages with our comprehensive guide on implementing tabular descriptions. Learn how to add a...

    SSH Into Raspberry Pi with Tailscale VPN

    SSH Into Raspberry Pi with Tailscale VPN

    Effortlessly access and manage your Raspberry Pi from anywhere using Tailscale's secure mesh VPN.

    Send Email with Lua and the ESP32

    Send Email with Lua and the ESP32

    In this tutorial, we delve into sending emails with the ESP32-S3 using Lua, focusing on the Xedge IDE's built-in SMTP...

    How to Code with Lua on ESP32 with XEdge32

    How to Code with Lua on ESP32 with XEdge32

    Learn how to set up Xedge32 and start coding on the ESP32-S3 with Lua programming!

    Stream Audio From Raspberry Pi to Local Computer

    Stream Audio From Raspberry Pi to Local Computer

    Discover the simplicity of streaming live audio directly from a USB microphone connected to your Raspberry Pi to...

    SSH Raspberry Pi via Cell Phone

    SSH Raspberry Pi via Cell Phone

    This beginner-friendly guide will walk you through remotely controlling your Raspberry Pi using SSH through your cell phone.

    Remotely Control Raspberry Pi via SSH from External Network

    Remotely Control Raspberry Pi via SSH from External Network

    Learn how to SSH into your Raspberry Pi from any network. This is critical in IoT since you can control...

    Stream Video from Raspberry Pi Camera to YouTube Live

    Stream Video from Raspberry Pi Camera to YouTube Live

    Learn how to stream to YouTube from a Raspberry Pi Camera.

    Back to blog

    Leave a comment

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