Filtering Gyro Data

I need some pointers on filtering gyro data. The gyroscope that I’m using is the Adafruit L3GD20H which can be found here.

I’ve been doing some research and have come across a few different options for filtering gyro data. The most popular seem to be either the complementary filter or the Kalman filter. I think for simplicity sake that I’d like to implement the complementary filter, but, being that I’ve never had to filter data before, I’m not sure how to use one.

Also, I should note that I’m just using a gyroscope for my project, no accelerometer. Do I need accelerometer data to implement a complementary filter?

Below is a screen shot of my raw gyroscope data. Figure 1 on the left shows the angular velocity data and Figure 2 on the right shows the integrated angular position data.

Gyro Data Plots

Note that this data was recorded with the gyro at rest on a flat surface for approximately 1.5 minutes. As you can see, I’ve got alot of noise and considerable drift over time.

Just for reference, my current Arduino sketch is below:

// include required libraries
#include <SPI.h>
#include <SD.h>
#include <Adafruit_L3GD20.h>

// define variables for SD card
File myGyroData;
unsigned long int time;
float valuesX;
float valuesY;
float valuesZ;
const int chipSelect = 4;

// define pins for SPI communication protocal
#define GYRO_CS 4   // labeled CS
#define GYRO_DO 5   // labeled SA0
#define GYRO_DI 6   // labeled SDA
#define GYRO_CLK 7  // labeled SCL
Adafruit_L3GD20 gyro(GYRO_CS, GYRO_DO, GYRO_DI, GYRO_CLK);

void setup()
{
  // set baud rate
  Serial.begin(9600);

  // initialize gyroscope
  Serial.println("Initializing gyroscope...");
  if (!gyro.begin(gyro.L3DS20_RANGE_250DPS))
  {
    Serial.println("Unable to initialize gyroscope! Please check your wiring and restart program.");
    delay(1000);
    exit(0);
    // end program if unable to initialize gyro
  }
  Serial.println("Gyroscope initiated.");

  // initialize SD card
  Serial.println("Initializing SD card...");
  pinMode(10, OUTPUT);
  // verify SD card is inserted
  if (!SD.begin(chipSelect))
  {
    Serial.println("Unable to initialize SD card! SD card failed or not inserted. Please restart program.");
    delay(1000);
    exit(0);
    // end program if unable to initialize SD card
  }
  //  SD.begin(4);
  Serial.println("SD card initialized.");
  Serial.println("Gyroscope and SD card ready.");
}

void loop()
{
  // read gyroscope data
  gyro.read();

  // save data to SD card (note: keep the name of the csv file short; too long and it won't write >:( )
  myGyroData = SD.open("float3.txt", FILE_WRITE);
  time = millis();
  valuesX = gyro.data.x;
  valuesY = gyro.data.y;
  valuesZ = gyro.data.z;

  // print to data to serial monitor and SD card if file opens correctly
  if (myGyroData)
  {
    // print gyroscope values to serial monitor
    Serial.print("X:"); Serial.print((int)gyro.data.x);   Serial.print(" ");
    Serial.print("Y:"); Serial.print((int)gyro.data.y);   Serial.print(" ");
    Serial.print("Z:"); Serial.println((int)gyro.data.z); Serial.print(" ");

    // save gyroscope data to SD card
    myGyroData.print(time);
    myGyroData.print(",");
    myGyroData.print(valuesX);
    myGyroData.print(",");
    myGyroData.print(valuesY);
    myGyroData.print(",");
    myGyroData.println(valuesZ);
  }
  myGyroData.close();
  // delay between data points for stability
  delay(20);
}

What filter do you think would work best for my situation? How would I go about implementing a filter into my code? If you know of any good tutorials that could help me, I’d love to have a look as well.

Thank you for your help in advance.

First I you should assume the average for the values you read as zero-rate level for your gyro. The sensor does provide a non zero low value constant for actual zero (not moving) value, for reasons explained in datasheet - paragraph 2.6.2

You should read values much faster than 500ms, mabe 20ms?

Regarding filters - a simple low pass filter is to average the last 10-20 values read. Otherwise, the calculated angle lines seem quite linear, mabe that's not the problem.

Regarding SD card files - they are not meant to be opened on every write(), open the file for writing when button is pressed, close it when it is released and do all your write(s) i between a single open() and close(). For several reasons. One is file open/close is time consuming, it affects your writing rate if you have to do it any faster than few ms. Another one is file system library might update file directory data every time you close an writable file, if you do that for each of 100000 lines (a few megabyte file) it might exhaust the SD card life in a few days.

Your code does an useless exit() when button is released... I expect that will lock the arduino and prevent further readings when button is pressed again.

blimpyway: First I you should assume the average for the values you read as zero-rate level for your gyro. The sensor does provide a non zero low value constant for actual zero (not moving) value, for reasons explained in datasheet - paragraph 2.6.2

You should read values much faster than 500ms, mabe 20ms?

Regarding filters - a simple low pass filter is to average the last 10-20 values read. Otherwise, the calculated angle lines seem quite linear, mabe that's not the problem.

Regarding SD card files - they are not meant to be opened on every write(), open the file for writing when button is pressed, close it when it is released and do all your write(s) i between a single open() and close(). For several reasons. One is file open/close is time consuming, it affects your writing rate if you have to do it any faster than few ms. Another one is file system library might update file directory data every time you close an writable file, if you do that for each of 100000 lines (a few megabyte file) it might exhaust the SD card life in a few days.

Your code does an useless exit() when button is released... I expect that will lock the arduino and prevent further readings when button is pressed again.

Thanks for the reply. I've gone ahead and made some changes to my code per your advice.

First, I've changed data collection to 20ms rather than 500ms.

Regarding the SD card files: I must admit, I am new to coding for Arduino and am learning as I go. In my sketch's current form, I only just started to implement the use of buttons as they will be useful later on for activating sections of my code. The purpose of that button was just to end the program after collecting data. If I'm understanding your suggestion correctly, you're saying that I should only collect data while someone is physically pressing the button. This, however, will not work for my application. When finished, this device will be strapped to somebody while the gyro is collecting data. Because of this, it would not be practical to have someone pressing the button the entire time.

To simplify things, I will alter my code and remove the use of a button completely for now. My priority and focus at the moment is just collecting, smoothing, saving, and plotting the data in a useful and reliable form. Once this is done, I will then revisit implementing buttons and the like.

Regarding filters: So your suggestion is a low pass filter. I will look into this and see if I can find something that I can apply to my project. Do you have any examples of this? From what I've collected from your suggestion, I am to average the last ~20 values of data, but I'm not so sure what I would do with that average.

Here is a filter designer that may help.

Arctic_Eddie: Here is a filter designer that may help.

Thanks for that. Unfortunately, being the novice I am, most of what is asked for to fill in the blanks to create the filter is beyond my knowledge. And even when I just tried to submit what I thought was the information for my gyro, I was met with a server error for that site.

Just checked and they appear to be having a problem. Too bad as it is quite useful. After getting the filter coefficients, it also gives sample C++ code. However, there are links on the input page that might be useful.

Here's a set of filter designers of various kinds that are directly executable.

The purpose of that button was just to end the program after collecting data. If I'm understanding your suggestion correctly, you're saying that I should only collect data while someone is physically pressing the button. This, however, will not work for my application. When finished, this device will be strapped to somebody while the gyro is collecting data. Because of this, it would not be practical to have someone pressing the button the entire time.

Now that you mentioned what is this for, here-s my advice: - there is no need and not recommended to filter data on arduino, just save the raw gyro readings on the card, and do whatever filtering/processing you want on PC. - choose whatever reading rate is appropriate for this task, the more frequent the updates the bigger the files, I don't know how long does it need to log and keep the saved files before transfering them on PC. Faster updates provide more accurate readings, but require longer time to read and process the files later. - A push button to start/stop recording, with a red led blinking so whoever is using the device knows it's working - that's also usefull. It could also enter a low-power mode when not recording, to keep battery life. So no need to keep button pressed, push to start, next push to stop. - My gut feeling is any rate between 10 or 20 samples/second (50-100ms between readings) is a good start.

blimpyway: Now that you mentioned what is this for, here-s my advice: - there is no need and not recommended to filter data on arduino, just save the raw gyro readings on the card, and do whatever filtering/processing you want on PC. - choose whatever reading rate is appropriate for this task, the more frequent the updates the bigger the files, I don't know how long does it need to log and keep the saved files before transfering them on PC. Faster updates provide more accurate readings, but require longer time to read and process the files later. - A push button to start/stop recording, with a red led blinking so whoever is using the device knows it's working - that's also usefull. It could also enter a low-power mode when not recording, to keep battery life. So no need to keep button pressed, push to start, next push to stop. - My gut feeling is any rate between 10 or 20 samples/second (50-100ms between readings) is a good start.

I was also considering that method for filtering, but wasn't sure if it's better to filter within the sketch or after the data is collected and transferred. Being that I have to use MATLAB to operate on the data anyways, I was going to explore the use of MATLAB to try and filter the data being that I have a lot more experience programming with it than for Arduino. I will look into different methods for doing this.

I was actually experimenting with reading rates when I posted this. I slowed it down as I didn't want massive amounts of data to look over at the time; I was in the testing process yet. For my application, there isn't going to be a lot of fast movement, so I feel that 100ms would produce more than accurate enough.

You're thinking exactly as I am. I will most definitely be implementing an LED and at least one push-button after I can get this data filtered.

Arctic_Eddie: Here's a set of filter designers of various kinds that are directly executable.

Thank you for those. I'll have a look and see if I can find something to apply to my project.