Are these Gyroscope readings correct?

Hi, i am trying to calibrate my MPU-6050 and GY-271 (magnetometer) for use in an AHRS.

Here is the code i used to find the offsets for the MPU-6050:

// Arduino sketch that returns calibration offsets for MPU6050 
//   Version 1.1  (31th January 2014)
// Done by Luis Ródenas <luisrodenaslorda@gmail.com>
// Based on the I2Cdev library and previous work by Jeff Rowberg <jeff@rowberg.net>
// Updates (of the library) should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib

// These offsets were meant to calibrate MPU6050's internal DMP, but can be also useful for reading sensors. 
// The effect of temperature has not been taken into account so I can't promise that it will work if you 
// calibrate indoors and then use it outdoors. Best is to calibrate and use at the same room temperature.


// Code website: http://wired.chillibasket.com/2015/01/calibrating-mpu6050/ 



/* ==========  ======  ==================================
 NOTE: In addition to connection 3.3v, GND, SDA, and SCL,  For this 
 connect the pin labelled as SDA on the MPU 6050
 to the arduino’s (A4) analog pin 4 (SDA). And the pin labelled as SCL on the MPU
 6050 to the arduino’s (A5) analog pin 5 (SCL).
 =========================================================
 */

// I2Cdev and MPU6050 must be installed as libraries
#include "I2Cdev.h"
#include "MPU6050.h"
#include "Wire.h"

///////////////////////////////////   CONFIGURATION   /////////////////////////////
//Change this 3 variables if you want to fine tune the skecth to your needs.
int buffersize=1000;     //Amount of readings used to average, make it higher to get more precision but sketch will be slower  (default:1000)
int acel_deadzone=8;     //Acelerometer error allowed, make it lower to get more precision, but sketch may not converge  (default:8)
int giro_deadzone=1;     //Giro error allowed, make it lower to get more precision, but sketch may not converge  (default:1)

// default I2C address is 0x68
// specific I2C addresses may be passed as a parameter here
// AD0 low = 0x68 (default for InvenSense evaluation board)
// AD0 high = 0x69
MPU6050 accelgyro;
//MPU6050 accelgyro(0x68); // <-- use for AD0 high

int16_t ax, ay, az,gx, gy, gz;

int mean_ax,mean_ay,mean_az,mean_gx,mean_gy,mean_gz,state=0;
int ax_offset,ay_offset,az_offset,gx_offset,gy_offset,gz_offset;

///////////////////////////////////   SETUP   ////////////////////////////////////
void setup() {
  // join I2C bus (I2Cdev library doesn't do this automatically)
  Wire.begin();
  // COMMENT NEXT LINE IF YOU ARE USING ARDUINO DUE
  //TWBR = 24; // 400kHz I2C clock (200kHz if CPU is 8MHz). Leonardo measured 250kHz.

  // initialize serial communication
  Serial.begin(115200);

  // initialize device
  accelgyro.initialize();
           
  while (Serial.available() && Serial.read()); // empty buffer again

  // start message
  Serial.println("\nMPU6050 Calibration Sketch");
  delay(2000);
  Serial.println("\nYour MPU6050 should be placed in horizontal position, with package letters facing up. \nDon't touch it until you see a finish message.\n");
  delay(3000);
  // verify connection
  Serial.println(accelgyro.testConnection() ? "MPU6050 connection successful" : "MPU6050 connection failed");
  delay(1000);
  // reset offsets
  accelgyro.setXAccelOffset(0);
  accelgyro.setYAccelOffset(0);
  accelgyro.setZAccelOffset(0);
  accelgyro.setXGyroOffset(0);
  accelgyro.setYGyroOffset(0);
  accelgyro.setZGyroOffset(0);
}

///////////////////////////////////   LOOP   ////////////////////////////////////
void loop() {
  if (state==0){
    Serial.println("\nReading sensors for first time...");
    meansensors();
    state++;
    delay(1000);
  }

  if (state==1) {
    Serial.println("\nCalculating offsets...");
    calibration();
    state++;
    delay(1000);
  }

  if (state==2) {
    meansensors();
    Serial.println("\nFINISHED!");
    Serial.print("\nSensor readings with offsets:\t");
    Serial.print(mean_ax); 
    Serial.print("\t");
    Serial.print(mean_ay); 
    Serial.print("\t");
    Serial.print(mean_az); 
    Serial.print("\t");
    Serial.print(mean_gx); 
    Serial.print("\t");
    Serial.print(mean_gy); 
    Serial.print("\t");
    Serial.println(mean_gz);
    Serial.print("Your offsets:\t");
    Serial.print(ax_offset); 
    Serial.print("\t");
    Serial.print(ay_offset); 
    Serial.print("\t");
    Serial.print(az_offset); 
    Serial.print("\t");
    Serial.print(gx_offset); 
    Serial.print("\t");
    Serial.print(gy_offset); 
    Serial.print("\t");
    Serial.println(gz_offset); 
    Serial.println("\nData is printed as: acelX acelY acelZ giroX giroY giroZ");
    Serial.println("Check that your sensor readings are close to 0 0 16384 0 0 0");
    Serial.println("If calibration was succesful write down your offsets so you can set them in your projects using something similar to mpu.setXAccelOffset(youroffset)");
    while (1);
  }
}

///////////////////////////////////   FUNCTIONS   ////////////////////////////////////
void meansensors(){
  long i=0,buff_ax=0,buff_ay=0,buff_az=0,buff_gx=0,buff_gy=0,buff_gz=0;

  while (i<(buffersize+101)){
    // read raw accel/gyro measurements from device
    accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
    
    if (i>100 && i<=(buffersize+100)){ //First 100 measures are discarded
      buff_ax=buff_ax+ax;
      buff_ay=buff_ay+ay;
      buff_az=buff_az+az;
      buff_gx=buff_gx+gx;
      buff_gy=buff_gy+gy;
      buff_gz=buff_gz+gz;
    }
    if (i==(buffersize+100)){
      mean_ax=buff_ax/buffersize;
      mean_ay=buff_ay/buffersize;
      mean_az=buff_az/buffersize;
      mean_gx=buff_gx/buffersize;
      mean_gy=buff_gy/buffersize;
      mean_gz=buff_gz/buffersize;
    }
    i++;
    delay(2); //Needed so we don't get repeated measures
  }
}

void calibration(){
  ax_offset=-mean_ax/8;
  ay_offset=-mean_ay/8;
  az_offset=(16384-mean_az)/8;

  gx_offset=-mean_gx/4;
  gy_offset=-mean_gy/4;
  gz_offset=-mean_gz/4;
  while (1){
    int ready=0;
    accelgyro.setXAccelOffset(ax_offset);
    accelgyro.setYAccelOffset(ay_offset);
    accelgyro.setZAccelOffset(az_offset);

    accelgyro.setXGyroOffset(gx_offset);
    accelgyro.setYGyroOffset(gy_offset);
    accelgyro.setZGyroOffset(gz_offset);

    meansensors();
    Serial.println("...");

    if (abs(mean_ax)<=acel_deadzone) ready++;
    else ax_offset=ax_offset-mean_ax/acel_deadzone;

    if (abs(mean_ay)<=acel_deadzone) ready++;
    else ay_offset=ay_offset-mean_ay/acel_deadzone;

    if (abs(16384-mean_az)<=acel_deadzone) ready++;
    else az_offset=az_offset+(16384-mean_az)/acel_deadzone;

    if (abs(mean_gx)<=giro_deadzone) ready++;
    else gx_offset=gx_offset-mean_gx/(giro_deadzone+1);

    if (abs(mean_gy)<=giro_deadzone) ready++;
    else gy_offset=gy_offset-mean_gy/(giro_deadzone+1);

    if (abs(mean_gz)<=giro_deadzone) ready++;
    else gz_offset=gz_offset-mean_gz/(giro_deadzone+1);

    if (ready==6) break;
  }
}

I uploaded the code and it gave me some offsets for the accelerometer and gyroscope, which i then used in my own code here:

mpu.setXAccelOffset(-2333);
mpu.setYAccelOffset(371);
mpu.setZAccelOffset(5050);
mpu.setXGyroOffset(114);
mpu.setYGyroOffset(-30);
mpu.setZGyroOffset(-5);

Here are the offset gyroscope readings:

Gyro: X,Y,Z  -0.02,  -0.15,  -0.24
Gyro: X,Y,Z  0.09,  -0.11,  -0.37
Gyro: X,Y,Z  -0.01,  -0.02,  -0.21
Gyro: X,Y,Z  0.02,  0.02,  -0.14
Gyro: X,Y,Z  0.18,  0.08,  -0.18
Gyro: X,Y,Z  0.06,  -0.08,  -0.44
Gyro: X,Y,Z  0.17,  0.02,  -0.28
Gyro: X,Y,Z  0.07,  -0.05,  -0.25
Gyro: X,Y,Z  -0.16,  0.11,  -0.31
Gyro: X,Y,Z  0.00,  0.08,  -0.41
Gyro: X,Y,Z  -0.12,  0.01,  -0.29
Gyro: X,Y,Z  0.20,  -0.02,  -0.43
Gyro: X,Y,Z  0.28,  0.07,  -0.25
Gyro: X,Y,Z  0.00,  -0.13,  -0.37
Gyro: X,Y,Z  0.05,  0.04,  -0.29
Gyro: X,Y,Z  -0.06,  0.01,  -0.58
Gyro: X,Y,Z  -0.09,  -0.08,  -0.40
Gyro: X,Y,Z  -0.15,  -0.18,  -0.30
Gyro: X,Y,Z  -0.04,  -0.01,  -0.28
Gyro: X,Y,Z  -0.10,  -0.15,  -0.42
Gyro: X,Y,Z  -0.02,  -0.02,  -0.41
Gyro: X,Y,Z  -0.02,  0.15,  -0.44
Gyro: X,Y,Z  0.05,  0.18,  -0.25
Gyro: X,Y,Z  -0.07,  -0.04,  -0.23
Gyro: X,Y,Z  -0.09,  -0.10,  -0.33
Gyro: X,Y,Z  0.01,  -0.06,  -0.42
Gyro: X,Y,Z  -0.10,  0.08,  -0.43
Gyro: X,Y,Z  0.01,  -0.14,  -0.43
Gyro: X,Y,Z  0.06,  0.00,  -0.34
Gyro: X,Y,Z  0.08,  -0.16,  -0.51
Gyro: X,Y,Z  0.01,  0.05,  -0.62
Gyro: X,Y,Z  0.01,  -0.06,  -0.49
Gyro: X,Y,Z  -0.02,  0.15,  -0.41

Do these look correct? I know that the offset gyro readings should about 0 but in some instances the readings are going all the way up to ±0.6. Is this normal? Or did i do something wrong?

I have attached the graph plots of the offset gyroscope readings along with the non-offset gyroscope readings.

Well it looks like the Z is miscalibrated - but what are the units?

What is the rms noise spec in those units? Is the variability higher than you would expect
from the noise specs?

[ Why not calculate the mean and standard deviation and print those out too ]

Well it looks like the Z is miscalibrated - but what are the units?

The units are angular velocity (degrees/second). The readings i posted are when the MPU-6050 was completely still.

What is the rms noise spec in those units? Is the variability higher than you would expect
from the noise specs?

Sorry, i'm not that familiar with RMS noise specs.

[ Why not calculate the mean and standard deviation and print those out too ]

Do you mean the mean and standard deviation of a sample of the offset readings or the non-offset readings?

Here is the datasheet for the module i'm using in case you want it: https://www.invensense.com/wp-content/uploads/2015/02/MPU-6000-Register-Map1.pdf

Also, i edited the original post to include graph plots of the readings in case you wanted to have a look at that. :slight_smile:

mean/sd of the corrected readings - the mean ought to be close to zero if calibration worked, the sd should
match the spec's of the gyro from the datasheet.

Offhand those deg/s readings seem reasonable - you get a lot of noise from MEMS sensors, but once its
integrated/low-pass-filtered that's much less of a problem.

The graphs look like its basically Gaussian noise, no big spikes or transients.

I took a sample of 200 readings and the found that the population standard deviation is 0.24673806213. The mean i found to be -0.17535776614.

Just by looking at that graph plot, the z axis (in green) could definitely be miscalibrated. However, the graph plot of the non-calibrated readings also have the same issue. It seems like the calibration hasn't changed much on the z-axis readings. Either my offsets are off or maybe this is just an issue with the sensor itself?

EDIT: I realized that the plot for the non-offset gyroscope readings was incorrect and fixed it.

It looks like i seemed to have fixed the issue. I have attached the graph plot of the new values to this post.

I fixed it by uploading and running the auto-calibration code but instead of taking the offset values and using them in my code like it said in the serial monitor:

MPU6050 Calibration Sketch

Your MPU6050 should be placed in horizontal position, with package letters facing up. 
Don't touch it until you see a finish message.

MPU6050 connection successful

Reading sensors for first time...

Calculating offsets...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...

FINISHED!

Sensor readings with offsets:	1	-1	16393	1	0	0
Your offsets:	-2386	365	5056	114	-29	10

Data is printed as: acelX acelY acelZ giroX giroY giroZ
Check that your sensor readings are close to 0 0 16384 0 0 0
If calibration was succesful write down your offsets so you can set them in your projects using something similar to mpu.setXAccelOffset(youroffset)

This time i did not use the offset values in my code and just started readings values instead, this is what came up:

Gyro: X,Y,Z  -0.15,0.12,0.01,
Gyro: X,Y,Z  0.07,0.05,-0.17,
Gyro: X,Y,Z  0.02,-0.11,0.13,
Gyro: X,Y,Z  0.08,0.02,0.02,
Gyro: X,Y,Z  -0.02,0.07,-0.24,
Gyro: X,Y,Z  0.07,0.05,-0.25,
Gyro: X,Y,Z  0.03,-0.08,0.06,
Gyro: X,Y,Z  0.02,0.10,0.06,
Gyro: X,Y,Z  -0.02,0.07,-0.08,
Gyro: X,Y,Z  0.02,-0.03,0.04,
Gyro: X,Y,Z  -0.14,-0.07,-0.05,
Gyro: X,Y,Z  -0.05,0.08,0.28,
Gyro: X,Y,Z  0.00,0.01,-0.18,
Gyro: X,Y,Z  0.05,0.07,-0.14,
Gyro: X,Y,Z  0.05,-0.03,0.02,
Gyro: X,Y,Z  0.05,-0.05,-0.09,
Gyro: X,Y,Z  -0.05,0.11,-0.06,
Gyro: X,Y,Z  0.17,0.03,-0.17,
Gyro: X,Y,Z  0.03,0.04,0.11,
Gyro: X,Y,Z  -0.08,0.18,0.11,
Gyro: X,Y,Z  0.04,-0.04,-0.05,
Gyro: X,Y,Z  0.15,0.05,0.01,
Gyro: X,Y,Z  0.00,-0.10,0.04,
Gyro: X,Y,Z  0.05,-0.07,0.00,
Gyro: X,Y,Z  -0.01,-0.01,0.04,
Gyro: X,Y,Z  0.12,0.14,0.01,
Gyro: X,Y,Z  -0.08,0.13,0.14,
Gyro: X,Y,Z  0.03,-0.07,-0.06,
Gyro: X,Y,Z  -0.04,0.13,0.12,
Gyro: X,Y,Z  0.21,-0.03,0.15,
Gyro: X,Y,Z  -0.06,0.04,0.05,
Gyro: X,Y,Z  -0.02,0.01,-0.03,
Gyro: X,Y,Z  -0.03,0.09,-0.15,
Gyro: X,Y,Z  0.04,0.19,0.01,
Gyro: X,Y,Z  -0.02,0.04,-0.05,
Gyro: X,Y,Z  -0.13,-0.04,-0.02,
Gyro: X,Y,Z  0.01,0.09,0.06,
Gyro: X,Y,Z  -0.08,0.14,-0.04,
Gyro: X,Y,Z  -0.02,0.03,0.05,
Gyro: X,Y,Z  0.05,-0.10,-0.07,
Gyro: X,Y,Z  0.05,0.11,-0.04,
Gyro: X,Y,Z  0.04,-0.02,0.04,
Gyro: X,Y,Z  0.06,0.03,0.05,
Gyro: X,Y,Z  0.11,-0.02,0.05,
Gyro: X,Y,Z  0.05,-0.03,0.11,
Gyro: X,Y,Z  0.11,-0.07,-0.02,
Gyro: X,Y,Z  0.02,-0.08,-0.04,
Gyro: X,Y,Z  0.00,0.15,0.02,
Gyro: X,Y,Z  -0.06,-0.15,-0.05,
Gyro: X,Y,Z  0.05,-0.05,-0.07,
Gyro: X,Y,Z  -0.03,-0.02,-0.02,
Gyro: X,Y,Z  0.02,-0.08,-0.11,
Gyro: X,Y,Z  0.03,-0.09,0.12,
Gyro: X,Y,Z  -0.06,-0.09,-0.31,
Gyro: X,Y,Z  0.10,-0.05,0.14,
Gyro: X,Y,Z  -0.10,-0.27,-0.31,
Gyro: X,Y,Z  0.06,0.04,0.09,
Gyro: X,Y,Z  -0.08,0.03,-0.02,
Gyro: X,Y,Z  -0.01,0.17,0.08,
Gyro: X,Y,Z  0.05,-0.21,-0.12,
Gyro: X,Y,Z  -0.05,-0.05,0.00,
Gyro: X,Y,Z  0.02,-0.11,-0.21,
Gyro: X,Y,Z  -0.10,-0.08,-0.12,
Gyro: X,Y,Z  -0.05,0.16,0.09,
Gyro: X,Y,Z  0.19,-0.10,-0.11,

Its still not perfect, but it’s a big improvement. I took another sample of 200 readings and found a population standard deviation of 0.106646222 and a mean of -0.0135.

found a population standard deviation of 0.106646222 and a mean of -0.0135.

The three axes are independent, so you should be calculating 3 mean values and 3 standard deviations.

The 3 mean values should all be about zero.

Sorry for the late response, my internet went down. However, during that time i was able to calculate the mean and standard deviation values for each axis. I also experimented with the z-axis offset value and compared means and std devs.

So let’s start off with the offset value the auto-calibration sketch gave me, -5. Here are the results, the mean and std dev is at the bottom:

Sample: 300 (for each axis - overall 900 values)

X Offset: 114	Y Offset: -30	Z Offset: -5



  X     Y    Z

-0.20,0.19,-0.34,
0.16,-0.08,-0.15,
-0.05,-0.08,-0.19,
0.05,0.19,-0.12,
0.05,0.07,-0.28,
-0.08,-0.11,-0.18,
0.02,0.05,-0.15,
0.00,0.00,-0.10,
0.11,-0.14,-0.31,
0.09,0.03,-0.34,
0.01,-0.03,-0.10,
0.03,-0.03,-0.24,
0.11,0.06,-0.39,
-0.07,0.05,-0.34,
-0.08,0.02,-0.31,
-0.28,-0.18,-0.31,
-0.03,-0.08,-0.37,
-0.06,-0.39,-0.20,
-0.06,-0.06,-0.05,
0.08,0.16,-0.27,
...

X Mean: -0.00		Y Mean: -0.03		Z Mean: -0.26

X Standard Dev: 0.09	Y Standard Dev: 0.12	Z Standard Dev: 0.29

The offset it suggested me obviously doesn’t work very well, so then i tried an offset of 10:

Sample: 300 (for each axis - overall 900 values)

X Offset: 114	Y Offset: -30	Z Offset: 10



  X     Y    Z

-0.08,-0.02,0.18,
-0.07,0.04,0.05,
0.05,0.00,0.05,
-0.15,-0.20,0.29,
0.03,0.00,0.24,
0.15,-0.01,0.30,
-0.15,-0.09,0.16,
-0.05,-0.11,0.17,
-0.13,-0.05,0.15,
0.11,-0.02,0.15,
-0.05,-0.09,0.35,
0.16,0.04,0.14,
-0.08,-0.12,0.20,
-0.24,0.04,0.30,
0.11,-0.13,0.23,
0.10,0.11,0.11,
-0.08,-0.24,0.16,
0.05,-0.02,-0.02,
0.19,0.04,0.06,
0.11,-0.01,0.25,
...


X Mean: -0.03		Y Mean: -0.04		Z Mean: 0.18

X Standard Dev: 0.10	Y Standard Dev: 0.12	Z Standard Dev: 0.22

This seems to produce slightly better results but still not what i wanted. Then i tried it with an offset of 5:

Sample: 300 (for each axis - overall 900 values)

X Offset: 114	Y Offset: -30	Z Offset: 5



  X     Y    Z

-0.18,0.08,-0.02,
0.08,-0.05,-0.02,
-0.05,-0.07,-0.15,
0.05,-0.31,-0.05,
-0.05,0.03,-0.20,
-0.15,-0.13,0.30,
-0.26,0.02,0.00,
-0.09,-0.17,0.08,
0.01,-0.18,-0.02,
-0.05,-0.08,0.04,
-0.02,0.00,-0.11,
-0.10,-0.19,-0.02,
0.05,-0.07,0.02,
-0.04,-0.03,0.05,
-0.02,0.15,-0.05,
-0.21,-0.18,0.01,
-0.12,-0.11,0.08,
-0.18,0.12,-0.11,
0.01,-0.11,-0.27,
-0.09,0.12,-0.24,
...

X Mean: -0.05		Y Mean: -0.05		Z Mean: 0.01

X Standard Dev: 0.10 	Y Standard Dev: 0.11 	Z Standard Dev: 0.11

An offset of 5 seemed to do the trick with a mean lower than the X and Y. I also tried it with an offset of 4 to see if i can get even better results:

Sample: 300 (for each axis - overall 900 values)

X Offset: 114	Y Offset: -30	Z Offset: 4



  X     Y     Z

-0.02,-0.07,-0.02,
-0.15,-0.14,0.06,
-0.01,-0.02,0.09,
0.08,0.08,-0.11,
-0.07,-0.26,0.21,
-0.08,0.11,-0.17,
-0.02,0.05,0.05,
-0.05,-0.20,-0.07,
-0.11,-0.09,0.15,
-0.17,-0.05,0.08,
-0.02,-0.11,-0.02,
-0.03,0.07,0.03,
-0.14,-0.04,0.07,
0.04,-0.15,0.06,
-0.08,-0.12,-0.21,
-0.07,-0.06,0.04,
-0.11,-0.11,-0.18,
0.03,-0.24,-0.07,
0.05,-0.03,-0.08,
0.01,-0.22,0.14,
...



X Mean: -0.01		Y Mean: -0.03		Z Mean: 0.00

X Standard Dev: 0.10 	Y Standard Dev: 0.12 	Z Standard Dev: 0.12

An offset of 4 seems to be the sweetspot. However, i still feel i little but unsure about the standard deviation because i don’t know as much about it. Are these standard deviation values good enough?

I have also attached a graph plot of the gyroscope with an offset value of 4 while it was completely stationary (the graph with an offset of 5 is pretty much identical).

The data sheet tells you what to expect for noise.

For the gyro, if the output is scaled to degrees/second, then you expect a standard deviation from the mean of about 0.05 degrees/second:

...
GYROSCOPE NOISE PERFORMANCE FS_SEL=0
Total RMS Noise DLPFCFG=2 (100Hz) 0.05 º/s-rms

So do you think this large standard deviation is a result of sensor bias (and maybe temperature?? At the time of testing it was roughly 25-30 C) and that there is nothing that can be done to reduce this?

Can i not use a magnetometer to reduce the bias of the gyroscope (and maybe accelerometer)?