kriswiner / MPU9250

Arduino sketches for MPU9250 9DoF with AHRS sensor fusion
1.03k stars 471 forks source link

Accelerometer calibration + Bias question #324

Open laurentvm opened 5 years ago

laurentvm commented 5 years ago

Hi Kris,

I'm using a MPU9250 mounted on a Freematics One + https://freematics.com/pages/products/freematics-one-plus/

And I would have 2 questions regarding the calibration of the accelerometer.

First, you calibrate and store the bias in the register. Why do you then substract the bias in the read function? I guess storing bias in the register is for the sensor to take into account the bias (https://github.com/kriswiner/MPU9250/blob/master/MPU9250_MS5637_AHRS_t3.ino#L479).

Second, every time I run my code (using your functions), I get different accelero values while not changing the device at all.

Run 1:
MEMS... BIAS 0.10 0.04 -0.03
[13715] MEMS{1}>  -0.12 -0.12 0.87 acc(x/y/z): -0.220459/-0.162170/0.902649, gyro(x/y/z): -0.106812,0.122070,0.022888, temp: 331, ori (p/y/r): 13.677728,-128.688858,-10.231514.
Run 2:
MEMS... BIAS 0.10 0.04 -0.03
[12715] MEMS{1}>  -0.13 -0.12 1.00 acc(x/y/z): -0.228394/-0.165344/1.028320, gyro(x/y/z): -0.167847,-0.122070,-0.015259, temp: 334, ori (p/y/r): 12.326733,-129.217880,-9.172193.
Run 3:
MEMS... BIAS 0.10 0.04 -0.03
[8415] MEMS{1}>  0.00 0.00 0.87 acc(x/y/z): -0.098511/-0.040955/0.897217, gyro(x/y/z): -0.061035,0.114441,0.030518, temp: 336, ori (p/y/r): 6.931838,-128.683151,-3.189526.

I do not always get 0 on x, y and 1 on the z axis.

The first 3 values are:

Serial.print(" ");
    Serial.print((float)accelCount[0]*aRes);
    Serial.print(" ");
    Serial.print((float)accelCount[1]*aRes);
    Serial.print(" ");
    Serial.print((float)accelCount[2]*aRes);
    Serial.print(" ");

Then
    acc[0] = (float)accelCount[0]*aRes - accelBias[0];  // get actual g value, this depends on scale being set
    acc[1] = (float)accelCount[1]*aRes - accelBias[1];
    acc[2] = (float)accelCount[2]*aRes - accelBias[2];

Any Idea? Comment ? I am calling read function every 100 ms using a task scheduler.

kriswiner commented 5 years ago

I am using the internal offset registers to store gyro bias. I don;t use these for the accel since the LS bit is a temperature compensation bit that must be preserved and my attempts to do so have produced poor accel offet bias compensation. So I don;t use the accel offset bias registers and correct manually for accel offset bias.

Are you expecting the accel data to be exactly the same?

On Sun, Nov 11, 2018 at 3:59 AM laurentvm notifications@github.com wrote:

Hi Kris,

I'm using a MPU9250 mounted on a Freematics One + https://freematics.com/pages/products/freematics-one-plus/

And I would have 2 questions regarding the calibration of the accelerometer.

First, you calibrate and store the bias in the register. Why do you then substract the bias in the read function? I guess storing bias in the register is for the sensor to take into account the bias ( https://github.com/kriswiner/MPU9250/blob/master/MPU9250_MS5637_AHRS_t3.ino#L479 ).

Second, every time I run my code (using your functions), I get different accelero values while not changing the device at all.

Run 1: MEMS... BIAS 0.10 0.04 -0.03 [13715] MEMS{1}> -0.12 -0.12 0.87 acc(x/y/z): -0.220459/-0.162170/0.902649, gyro(x/y/z): -0.106812,0.122070,0.022888, temp: 331, ori (p/y/r): 13.677728,-128.688858,-10.231514. Run 2: MEMS... BIAS 0.10 0.04 -0.03 [12715] MEMS{1}> -0.13 -0.12 1.00 acc(x/y/z): -0.228394/-0.165344/1.028320, gyro(x/y/z): -0.167847,-0.122070,-0.015259, temp: 334, ori (p/y/r): 12.326733,-129.217880,-9.172193.

The first 3 values are:

Serial.print(" "); Serial.print((float)accelCount[0]aRes); Serial.print(" "); Serial.print((float)accelCount[1]aRes); Serial.print(" "); Serial.print((float)accelCount[2]*aRes); Serial.print(" ");

Then acc[0] = (float)accelCount[0]aRes - accelBias[0]; // get actual g value, this depends on scale being set acc[1] = (float)accelCount[1]aRes - accelBias[1]; acc[2] = (float)accelCount[2]*aRes - accelBias[2];

Any Idea? Comment ?

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/kriswiner/MPU9250/issues/324, or mute the thread https://github.com/notifications/unsubscribe-auth/AGY1qkXHQznkHwrO1rnDGKgBU4rspy4pks5uuBETgaJpZM4YYcsY .

laurentvm commented 5 years ago

Hi Kris,

So in this snippet of code, the writeByte should be removed???

// Construct the accelerometer biases for push to the hardware accelerometer bias registers. These registers contain
// factory trim values which must be added to the calculated accelerometer biases; on boot up these registers will hold
// non-zero values. In addition, bit 0 of the lower byte must be preserved since it is used for temperature
// compensation calculations. Accelerometer bias registers expect bias input as 2048 LSB per g, so that
// the accelerometer biases calculated above must be divided by 8.

  int32_t accel_bias_reg[3] = {0, 0, 0}; // A place to hold the factory accelerometer trim biases
  readBytes(XA_OFFSET_H, 2, &data[0]); // Read factory accelerometer trim values
  accel_bias_reg[0] = (int32_t) (((int16_t)data[0] << 8) | data[1]);
  readBytes(YA_OFFSET_H, 2, &data[0]);
  accel_bias_reg[1] = (int32_t) (((int16_t)data[0] << 8) | data[1]);
  readBytes(ZA_OFFSET_H, 2, &data[0]);
  accel_bias_reg[2] = (int32_t) (((int16_t)data[0] << 8) | data[1]);

  uint32_t mask = 1uL; // Define mask for temperature compensation bit 0 of lower byte of accelerometer bias registers
  uint8_t mask_bit[3] = {0, 0, 0}; // Define array to hold mask bit for each accelerometer bias axis

  for(ii = 0; ii < 3; ii++) {
    if((accel_bias_reg[ii] & mask)) mask_bit[ii] = 0x01; // If temperature compensation bit is set, record that fact in mask_bit
  }

  // Construct total accelerometer bias, including calculated average accelerometer bias from above
  accel_bias_reg[0] -= (accel_bias[0]/8); // Subtract calculated averaged accelerometer bias scaled to 2048 LSB/g (16 g full scale)
  accel_bias_reg[1] -= (accel_bias[1]/8);
  accel_bias_reg[2] -= (accel_bias[2]/8);

  data[0] = (accel_bias_reg[0] >> 8) & 0xFF;
  data[1] = (accel_bias_reg[0])      & 0xFF;
  data[1] = data[1] | mask_bit[0]; // preserve temperature compensation bit when writing back to accelerometer bias registers
  data[2] = (accel_bias_reg[1] >> 8) & 0xFF;
  data[3] = (accel_bias_reg[1])      & 0xFF;
  data[3] = data[3] | mask_bit[1]; // preserve temperature compensation bit when writing back to accelerometer bias registers
  data[4] = (accel_bias_reg[2] >> 8) & 0xFF;
  data[5] = (accel_bias_reg[2])      & 0xFF;
  data[5] = data[5] | mask_bit[2]; // preserve temperature compensation bit when writing back to accelerometer bias registers

// Apparently this is not working for the acceleration biases in the MPU-9250
// Are we handling the temperature correction bit properly?
// Push accelerometer biases to hardware registers
  writeByte(XA_OFFSET_H, data[0]);
  writeByte(XA_OFFSET_L, data[1]);
  writeByte(YA_OFFSET_H, data[2]);
  writeByte(YA_OFFSET_L, data[3]);
  writeByte(ZA_OFFSET_H, data[4]);
  writeByte(ZA_OFFSET_L, data[5]);

// Output scaled accelerometer biases for display in the main program
   accelBias[0] = (float)accel_bias[0]/(float)accelsensitivity;
   accelBias[1] = (float)accel_bias[1]/(float)accelsensitivity;
   accelBias[2] = (float)accel_bias[2]/(float)accelsensitivity;

   Serial.print(" ");
   Serial.print(accelBias[0]);
   Serial.print(" ");
   Serial.print(accelBias[1]);
   Serial.print(" ");
   Serial.print(accelBias[2]);
   Serial.println(" ");

Concerning the accel values, yes after calibration I'm expecting to get something like 0,0,1 or quite close. For me, this values -0.12 -0.12 0.87 are not close enough. More than 10 % error is quite a lot no?

Also, the Bias is always the same which is a good thing. How can we have the same Bias and not the same measurement later?

kriswiner commented 5 years ago

Yes, it's because you are using the accel bias registers, I think.

On Sun, Nov 11, 2018 at 9:36 AM laurentvm notifications@github.com wrote:

Hi Kris,

So this snippet of code, writeByte should be removed???

// Construct the accelerometer biases for push to the hardware accelerometer bias registers. These registers contain // factory trim values which must be added to the calculated accelerometer biases; on boot up these registers will hold // non-zero values. In addition, bit 0 of the lower byte must be preserved since it is used for temperature // compensation calculations. Accelerometer bias registers expect bias input as 2048 LSB per g, so that // the accelerometer biases calculated above must be divided by 8.

int32_t accel_bias_reg[3] = {0, 0, 0}; // A place to hold the factory accelerometer trim biases readBytes(XA_OFFSET_H, 2, &data[0]); // Read factory accelerometer trim values accel_bias_reg[0] = (int32_t) (((int16_t)data[0] << 8) | data[1]); readBytes(YA_OFFSET_H, 2, &data[0]); accel_bias_reg[1] = (int32_t) (((int16_t)data[0] << 8) | data[1]); readBytes(ZA_OFFSET_H, 2, &data[0]); accel_bias_reg[2] = (int32_t) (((int16_t)data[0] << 8) | data[1]);

uint32_t mask = 1uL; // Define mask for temperature compensation bit 0 of lower byte of accelerometer bias registers uint8_t mask_bit[3] = {0, 0, 0}; // Define array to hold mask bit for each accelerometer bias axis

for(ii = 0; ii < 3; ii++) { if((accel_bias_reg[ii] & mask)) mask_bit[ii] = 0x01; // If temperature compensation bit is set, record that fact in mask_bit }

// Construct total accelerometer bias, including calculated average accelerometer bias from above accel_bias_reg[0] -= (accel_bias[0]/8); // Subtract calculated averaged accelerometer bias scaled to 2048 LSB/g (16 g full scale) accel_bias_reg[1] -= (accel_bias[1]/8); accel_bias_reg[2] -= (accel_bias[2]/8);

data[0] = (accel_bias_reg[0] >> 8) & 0xFF; data[1] = (accel_bias_reg[0]) & 0xFF; data[1] = data[1] | mask_bit[0]; // preserve temperature compensation bit when writing back to accelerometer bias registers data[2] = (accel_bias_reg[1] >> 8) & 0xFF; data[3] = (accel_bias_reg[1]) & 0xFF; data[3] = data[3] | mask_bit[1]; // preserve temperature compensation bit when writing back to accelerometer bias registers data[4] = (accel_bias_reg[2] >> 8) & 0xFF; data[5] = (accel_bias_reg[2]) & 0xFF; data[5] = data[5] | mask_bit[2]; // preserve temperature compensation bit when writing back to accelerometer bias registers

// Apparently this is not working for the acceleration biases in the MPU-9250 // Are we handling the temperature correction bit properly? // Push accelerometer biases to hardware registers writeByte(XA_OFFSET_H, data[0]); writeByte(XA_OFFSET_L, data[1]); writeByte(YA_OFFSET_H, data[2]); writeByte(YA_OFFSET_L, data[3]); writeByte(ZA_OFFSET_H, data[4]); writeByte(ZA_OFFSET_L, data[5]);

// Output scaled accelerometer biases for display in the main program accelBias[0] = (float)accel_bias[0]/(float)accelsensitivity; accelBias[1] = (float)accel_bias[1]/(float)accelsensitivity; accelBias[2] = (float)accel_bias[2]/(float)accelsensitivity;

Serial.print(" "); Serial.print(accelBias[0]); Serial.print(" "); Serial.print(accelBias[1]); Serial.print(" "); Serial.print(accelBias[2]); Serial.println(" ");

Concerning the accel values, yes after calibration I'm expecting to get something like 0,0,1 are quite close. For me, this values -0.12 -0.12 0.87 are not close enough. More than 10 % error is quite a lot no?

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/kriswiner/MPU9250/issues/324#issuecomment-437688978, or mute the thread https://github.com/notifications/unsubscribe-auth/AGY1qsVCco4r4KplFSWt9RFrgUhJFkk4ks5uuGAOgaJpZM4YYcsY .

laurentvm commented 5 years ago

Great Kris,

Changing to get your latest code calibration corrected the issue. Thanks Great job!

gkcng commented 4 years ago

Hi Kris,

I know where the bug is with the temperature compensation bit. The original code makes no attempt in actually adding back the preserved bit nor adjusting for the fact that the calculated accel_bias[] is actually one bit off. The raw accel offset register value needs to be right shifted by 1 bit before subtracting the calculated accel_bias. To yield the write back value, left shifted the result by one and OR the preserved masked bit.

Here is my python port of that portion. I am sure the subtraction, shifting and OR'ing can all be as oneliners in C. In fact, even in python, see the end of the comment.

    mask = 0x01 # Define mask for temperature compensation bit 0 of lower byte of accelerometer bias registers                                    
    mask_bit = [0, 0, 0] # Define array to hold mask bit for each accelerometer bias axis

    for ii in range(0,3):                                                                                                                         
        if ((accel_bias_reg[ii] & mask)):                                                                                                         
            mask_bit[ii] = 0x01 # If temperature compensation bit is set, record that fact in mask_bit                                            
    print(mask, mask_bit)                                                                                                                         

    # Construct total accelerometer bias, including calculated average accelerometer bias from above                                              
    print("Actual Accel Bias values...")                                                                                                          
    accel_bias_reg[0] >>= 1 # shift away bit0 before calculations                                                                                 
    accel_bias_reg[1] >>= 1                                                                                                                       
    accel_bias_reg[2] >>= 1                                                                                                                       
    print(accel_bias_reg)                                                                                                                         

    print("New Accel Bias values...")                                                                                                             
    accel_bias_reg[0] -= round(accel_bias[0]/8) # Subtract calculated averaged accelerometer bias scaled to 2048 LSB/g (16 g full scale)          
    accel_bias_reg[1] -= round(accel_bias[1]/8)                                                                                                   
    accel_bias_reg[2] -= round(accel_bias[2]/8)                                                                                                   
    print(accel_bias_reg)                                                                                                                         

    # preserve temperature compensation bit when writing back to accelerometer bias registers                                                     
    accel_bias_reg[0] = (accel_bias_reg[0] << 1) | mask_bit[0]                                                                                    
    accel_bias_reg[1] = (accel_bias_reg[1] << 1) | mask_bit[1]                                                                                    
    accel_bias_reg[2] = (accel_bias_reg[2] << 1) | mask_bit[2]                                                                                    
    print("New Accel Bias register values...")                                                                                                    
    print(accel_bias_reg)    `

BTW, thanks for this repo, I am new to IMU in general and this taught me a lot.

The above verbose subtraction and bit preservation can in fact be done all in one line by shifting accel_bias left instead:

accel_bias_reg[0] -= (round(accel_bias[0]/8) << 1) # ready to be written back                                                                
accel_bias_reg[1] -= (round(accel_bias[1]/8) << 1) # ready to be written back                                                                
accel_bias_reg[2] -= (round(accel_bias[2]/8) << 1) # ready to be written back