Introduction

AHRS is an acronym for Attitude and Heading Reference System, a system generally used for aircraft of any sort to determine heading, pitch, roll, altitude etc.

A basic IMU (Intertial Measurement Unit) generally provides raw sensor data, whereas an AHRS takes this data one step further, converting it into heading or direction in degrees, converting the raw altitude data into standard units like feet or meters, etc.

To help you get started designing your own AHRS system, or just tp help convert raw sensor data into useful numbers that you can relate to the real world, we've created a sample AHRS sketch for the Adafruit 10-DOF IMU, Adafruit 9-DOF IMU, and Adafruit_LSM9DS0 breakouts.
The Adafruit 10-DOF breakout is required for a real AHRS system -- only the 10-DOF breakout incorporates a barometric pressure sensor capable of measuring altitude -- but we also reference the 9-DOF and LSM9DS0 breakout since this code can be used with either breakout for orientation calculations.

Installing the Software

The current example Arduino sketches are available on Github as part of the Adafruit AHRS libray.

Downloading the AHRS Sample Code

To install the drivers and AHRS sketch for your breakout, you simply need to install the Adafruit_AHRS library and it's dependencies on your PC (the exact dependencies you choose will depend on which IMU board you are currently using).

We've put together a tutorial showing you everything you need to do to install dependent libraries if you're just getting started with your breakout. Follow the guide below to install the right libraries depending on which board you are using:
In addition to the dependencies above, make sure to install the Adafruit_AHRS library. You can find a link to download the library below:
If you aren't familiar with how to install an Arduino library, check out this guide on installing libraries.

Loading the Sample Sketch

Once the repository has been added to your libraries folder ("/libraries/Adafruit_AHRS/", etc.) you should be able to access the AHRS examples from 'File > Examples > Adafruit_AHRS' in the Arduino IDE. There should be an ahrs_9dof, ahrs_10dof, and ahrs_lsm9ds0 exampe.

Pick the appropriate example for your board, for example the ahrs_10dof example should be used with the 10-DOF board:
If you compile the sketch and then program your Uno with the code, you should be able to open up the Serial Monitor (Tools > Serial Monitor), set the baud rate to 115200, and see the following output:
This raw data shows the main orientation data, consisting of 'roll', 'pitch' and 'heading' (or 'yaw) in degrees, followed by the current altitude and temperature if you are are using the 10-DOF breakout (the 9-DOF and LSM9DSO breakouts can't measure altitude).

Now close the Serial Monitor and read on to the next page to do something useful with this data!

Using AHRS Data

This simple example uses uncalibrated data and doesn't integrate the on board gyroscope, but is fast and easy to use. For a more advanced example, see Sensor Fusion Algorithms later in this guide.
The AHRS example sketchs reads raw data from the board's accelerometer/magnetometer and converts the raw data into easy to understand Euler angles.

It does this with a bit of trigonometry (you remember high school math, right!?), but to save you from dusting off the textbooks or wading through endless application notes, we've wrapped up all of the calculation to convert raw accelerometer and magnetometer data to degrees in a convenient helper function:

    Download: file
    bool getOrientation(sensors_vec_t *orientation)
    The AHRS sketch creates an Adafruit_Simple_AHRS object which takes an accelerometer and magnetometer sensor as input to its constructor. You can actually pass any accelerometer or magnetometer object which supports the Adafruit unified sensor library in to the AHRS algorithm, and the examples use the 9-DOF, 10-DOF, and LSM9DS0 sensors.

    Once the simple AHRS object is created the getOrientation function is called to retrieve the current orientation. Internally the function will read raw data from the accelerometer and magnetomber, converts the data to Euler angles, and spits the three-dimensional orientation data out. If you're interested in the math, you can have a look at the source code on github, but for now we'll just treat it as a mathemagical black box:

    Euler Angles

    Euler angles describe orientation (in degrees) around a single reference point in three-dimensional space.

    Various names are employed for the three angles, but the most common terminology with aircraft is Roll (x), Pitch (y) and Yaw (z).

    The illustration below from the Wikipedia article on Euler angles should illustrate the concept clearly. You normally have both positive and negative angles (-180° to 180°) depending on the direction the airplane is tilted, with 0° in every direction corresponding to the airplane being perfectly aligned with each axis:
    If you run the AHRS sketch, you should see something similar to the following in the Serial Monitor, where the three values after 'orientation' are the Euler angles:
    In this case, we can see that the roll is about 18°, the pitch is about 78° and the heading or yaw is about 32°, and the sketch will keep updating itself with the latest values at whatever speed we've set in the example sketch.

    Visualizing Data

    To help you visualize the data, we've put together a basic Processing sketch that loads a 3D model (in the .obj file format) and renders it using the data generated by the AHRS sketch on the Uno. The ahsr sketch on the uno published data over UART, which the Processing sketch reads in, rotating the 3D model based on the incoming orientation data.

    Requirements

    The OBJ library is required to load 3D models. It isn't strictly necessary and you could also render a boring cube in Processing, but why play with cubes when you have rabbits?!

    Opening the Processing Sketch

    The processing sketch to render the 3D model is contained in the sample folder as the ahrs sketch for the Uno.

    With Processing open, navigate to you Adafruit_AHRS library folder (ex.: 'libraries/Adafruit_AHRS'), and open 'processing/bunnyrotate/bunnyrotate.pde'. You should see something like this in Processing:
    Please use Processing 2.2.1 with this sketch ... newer versions have breaking changes at the API level!

    Run the AHRS Sketch on the Uno

    Make sure that the appropriate AHRS example sketch is running on the Uno (as described on the previous page), and that the Serial Monitor is closed.

    With the sample sketch running on the Uno, click the triangular 'play' icon in Processing to start the sketch.

    Rabbit Disco!

    You should see a rabbit similar to the following image:
    Before the rabbit will rotate you will need to click the : to the right of the serial port name. This will open a list of available serial ports, and you will need to click the appropriate serial port that your Arduino uses (check the Arduino IDE to see the port name if you're unsure). The chosen serial port should be remembered if you later run the sketch again.

    As you rotate your breakout board, the rabbit should rotate to reflect the movement of the breakout in 3D-space, as seen in the video below (note that this video is using an older version of the cuberotate example and won't look exactly like what you see now):

    A Note on Accuracy and Calibration

    Note that the movement seen on the screen won't correlate exactly to the breakout since we are currently using uncalibrated sensor data, specifically uncalibrated magnetometer data which will throw the heading off. The magnetometer is required to generate 360° data, and if you want to improve the accuracy of your orientation data we have a tutorial on calibrating the LSM303, but this particular example starts with raw data for simplicity sake.

    Magnetometer Calibration

    For more accurate orientation output, the magnetometer needs to be adjusted to compensate for offset errors (meaning a shift in either direction on the X/Y/Z axis), as well as something called 'soft iron error', which causes what should be spherical output of the magnetometer to actually be an elongated 'pill' shape or something similar.

    In order to compensate for these two problems and generate accurate magnetometer data, which is critical to getting high quality orientation results, you will need to calibrate your magnetometer in the final enclosure and setup in which the device will be used.

    The calibration process should be done when the project is in it's final form and inside any enclosure being used, etc:

    PJRC MotionCal

    There are a variety of calibration tools out there, but MotionCal from always awesome PJRC (Teensy, etc.) is probably the easiest to use, and they provide pre-compiled binaries for all major operating systems.

    Download the Motion Sensor Calibration tool for your OS from the following link and open it up on your system: http://www.pjrc.com/store/prop_shield.html

    By default, you should see something like this:

    Next open up the ahrs_calibration example from Adafruit_AHRS:

    If you don't see this example, you may need to go into the Library Manager (Sketch > Include Library > Manage Libraries ...) and update your Adafruit_AHRS library to the latest version.

    Be default this is setup for the sensors found on the Adafruit 9DOF and 10DOF breakouts, but can be adjusted to work with any accelerometer, magnetometer and gyroscope that is based on the Adafruit Unified Sensor system.

    Generating Calibration Data

    Next run the sketch, and if it isn't already open, open the MotionCal app.

    Select the appropriate serial port for your board, and once select start rotating the board around gently in figure eight type movements.

    As you move the device, points should start to appear in a point cloud.

    You need to keep moving the device in as many orientations as possible until the sphere is mostly complete and the Gaps percentage on the bottom of the app is reasonably low.

    A good example of what you should aim for is shown below:

    Note Offsets and Magnetic Mapping Values

    Now that you have determined the values to compensate for magnetometer offset and soft-iron error, make a note of the values in the top-right hand corner of the app, which in this case is:

    Magnetic Offset

    -2.20, -5.53, -26.34

    Magnetic Mapping

    0.934, 0.005, 0.013
    0.005, 0.948, 0.012
    0.013, 0.012, 1.129

    Magnetic Field

    48.41

    The magnetic field value is not currently used but should be noted anyway.

    That's It!

    Congratulations, you have everything you need to generate reasonably accurate ouput on the magnetometer taking into account any local interference in your environment or caused by the board itself.

    You'll need these values in the next step, which is feeding your compensated sensor output into a sensor fusion algorithm, but you can close the MotionCal app for now until you need to calibrate the device again, such as if you move it to a different setup or environment.

    Sensor Fusion Algorithms

    There are a variety of sensor fusion algorithms out there, but the two most common in small embedded systems are the Mahony and Madgwick filters.

    Mahony is more appropriate for very small processors, whereas Madgwick can be more accurate with 9DOF systems at the cost of requiring extra processing power (it isn't appropriate for 6DOF systems where no magnetometer is present, for example).

    We will use the Mahony fusion algorithm in this example since it is the most relevant to a wide variety of devices, but it's easy to switch to Madgwick if you prefer to test another algorithm.

    This page and code is still a work in progress and subject to change in the future!

    MahonyAHRS and MadgwickAHRS Libraries

    For best results, use the fork of MahonyAHRS and MadgwickAHRS from PJRC (based on the original Arduino libraries) available here:

    You will need to download these as a zip file and then install them in your libraries folder, or git clone them in the Arduino libraries folder (ex. 'git clone https://github.com/PaulStoffregen/MahonyAHRS.git').

    For help installing libraries, see our Arduino Libraries Learning Guide.

    Enter Calibration Data into ahrs_mahony

    To get started, open the ahrs_mahony example from the Adafruit_AHRS folder.

    You will need to enter the magnetometer calibration values calculated earlier in this guide in the appropriate field, as shown in the screenshot below:

    These values are based on the following calibration data:

    Run the Sketch

    Next, compiled and run your sketch.

    Open the Serial Monitor, and you should see a millisecond timestamp, followed by the output of the sensor fusion algorithm, which will give you Euler Angles for HeadingPitch and Roll in that order:

    Tuning the Filter

    One particularity of fusion algorithms (and most DSP algorithms) is that they are sensitive to timing. Make a note of the millisecond timestamp before the individual samples, can count approximately how many samples per second your device is outputting.

    You need to tell the fusion algorithm how many samples per second we are generating to more accurately estimate positions in 3D space based on the accel, gyro and mag data.

    In the filters setup() function, find the following lines and adjust this value to match the number of samples per second your system is outputting:

    Download: file
      // Filter expects 50 samples per second
      filter.begin(50);
    

    Run the filter again and you should get slightly more accurate results.

    Switching to Madgwick

    To switch to the Madgwick filter, simply comment the appropriate class instance at the top of the sketch and run the code again. The two filters have identical APIs and are easily interchangeable.

    Change the code from this ...

    Download: file
    // Mahony is lighter weight as a filter and should be used
    // on slower systems
    Mahony filter;
    //Madgwick filter;

    ... to this:

    Download: file
    //Mahony filter;
    Madgwick filter;

    A Note on Orientation Values

    Please note that at present all orientation values are relative and not absolute. What this means is that the orientation values will be relative to the starting position of the device, not absolute magnetic north or south, etc.

    Before starting, put your device in a known, repeatable position if you require reasonably repeatable, consistent output.

    This guide was first published on Mar 19, 2014. It was last updated on Mar 19, 2014.