Mpu6050 kalman library problem

Hey! Ive tried to combine a code from the TJK's kalman filter library's example. Code compiles without problem but kalman and complementary roll angle outputs "nan" pitch angle is true. Can you help me what causes pitch to be nan?

#include <Wire.h>
#include <Kalman.h>
#include <MPU6050.h>
MPU6050 mpu;

Kalman kalmanX;  // Create the Kalman instances
Kalman kalmanY;

/* IMU Data */
int16_t accX, accY, accZ;
int16_t gyroX, gyroY, gyroZ;

double gyroXangle, gyroYangle;  // Angle calculate using the gyro only
double compAngleX, compAngleY;  // Calculated angle using a complementary filter
double kalAngleX, kalAngleY;    // Calculated angle using a Kalman filter

uint32_t timer;

void setup() {
  Serial.begin(9600);
  Wire.begin();
  mpu.initialize();
  delay(100);
}
void loop() {

  mpu.getMotion6(&accX, &accY, &accZ, &gyroX, &gyroY, &gyroZ);
  timer = micros();
  double dt = (double)(micros() - timer) / 1000000;  // Calculate delta time
  

  double roll = atan(accY / sqrt(accX * accX + accZ * accZ)) * RAD_TO_DEG;
  double pitch = atan2(-accX, accZ) * RAD_TO_DEG;
  double gyroXrate = gyroX / 131.0;  // Convert to deg/s
  double gyroYrate = gyroY / 131.0;  // Convert to deg/s


  kalmanY.setAngle(pitch);
  compAngleY = pitch;
  kalAngleY = pitch;
  gyroYangle = pitch;

  kalAngleY = kalmanY.getAngle(pitch, gyroYrate, dt);  // Calculate the angle using a Kalman filter

 kalmanX.setAngle(roll);
  compAngleX = roll;
  kalAngleX = roll;
  gyroXangle = roll;
  
  kalAngleX = kalmanX.getAngle(roll, gyroXrate, dt);  // Calculate the angle using a Kalman filter

  gyroXangle += gyroXrate * dt;  // Calculate gyro angle without any filter
  gyroYangle += gyroYrate * dt;
  //gyroXangle += kalmanX.getRate() * dt; // Calculate gyro angle using the unbiased rate
  //gyroYangle += kalmanY.getRate() * dt;

  compAngleX = 0.93 * (compAngleX + gyroXrate * dt) + 0.07 * roll;  // Calculate the angle using a Complimentary filter
  compAngleY = 0.93 * (compAngleY + gyroYrate * dt) + 0.07 * pitch;

  // Reset the gyro angle when it has drifted too much
  if (gyroXangle < -180 || gyroXangle > 180) {
    gyroXangle = kalAngleX;
  }
  if (gyroYangle < -180 || gyroYangle > 180) {
    gyroYangle = kalAngleY;
  }


  Serial.println(kalAngleX);
  delay(100);
}

I suppose that you used the Arduino IDE to install "Kalman Filter Library by Kristian Lauszus (TKJ Electronics)". That is this library: https://github.com/TKJElectronics/KalmanFilter
He does have a small bug in an example. The bug is not a problem, but that he is not willing to fix it is questionable: https://github.com/TKJElectronics/KalmanFilter/issues/9

I don't know which MPU6050 library you use.

Yes Ive used that library. Thank you anyway for your help

Which MPU-6050 library do you use ?
Which Arduino board do you use ?
Which MPU-6050 module do you use, can you give a link to where you bought it ?
Do you know that your MPU6050 might be fake ? It comes in many different flavors of fake. Some sensors do something, some work for a part, some don't work at all. You should do a minimal test with the MPU-6050 to check if it does something.

MPU6050 Library ; GitHub - ElectronicCats/mpu6050: MPU6050 Arduino Library
Arduino Uno
Ive tested mpu6050 without filtering and it works fine

The issue of the pitch angle being NaN (Not a Number) in your code is likely due to a mathematical error. Specifically, the division by zero in the calculation of the roll angle is causing the problem.

In this line of code:

double roll = atan(accY / sqrt(accX * accX + accZ * accZ)) * RAD_TO_DEG;

The atan function expects its argument to be a double type, but you are performing integer division, which can result in an incorrect value or zero. When the denominator (sqrt(accX * accX + accZ * accZ)) becomes zero, you are effectively dividing by zero, leading to undefined behavior and producing NaN as the result.

To fix this issue, you need to ensure that the division is performed using double values. You can do this by explicitly casting the variables to double before the division:

double roll = atan((double)accY / sqrt((double)(accX * accX) + (double)(accZ * accZ))) * RAD_TO_DEG;

By casting accY, accX, and accZ to double, you ensure that the division is performed with floating-point arithmetic, avoiding division by zero.

After making this change, the calculation of the pitch angle should no longer produce NaN, and you should see valid values for the pitch angle in the output.

That one is also in the Library Manager of the Arduino IDE, as "MPU6050 by Electronic Cats".

When I look at this example: https://github.com/TKJElectronics/KalmanFilter/blob/master/examples/MPU6050/MPU6050.ino
then you have changed the order of the two lines for 'micros()' and 'dt'.

Thank you @barshatriplee
I see now that the example also uses "double" for the 'accX' variables.
How about this:

  int16_t _accX, _accY, _accZ;
  int16_t _gyroX, _gyroY, _gyroZ;
  mpu.getMotion6(&_accX, &_accY, &_accZ, &_gyroX, &_gyroY, &_gyroZ);
  
  double accX = (double) _accX;
  double accY = (double) _accY;
  double accZ = (double) _accZ;
  double gyroX = (double) _gyroX;
  double gyroY = (double) _gyroY;
  double gyroZ = (double) _gyroZ;

@fatihbb You are using large libraries which may have issues. The MPU6050 code by ElectronicCats has a copy of the I2Cdev library which as issues.
This is a simpler way (but not the same): https://github.com/jremington/MPU-6050-Fusion

Ive changed the code as you said. I dont know ıf ı made it correct but nan problem is solved. Now I ve another question. Complementary filter's kalman filter's and unfiltered value output is same. İs it possible do you have any idea?

#include <Wire.h>
#include <Kalman.h>
#include <MPU6050.h>
MPU6050 mpu;

Kalman kalmanX;  // Create the Kalman instances
Kalman kalmanY;

/* IMU Data */
int16_t _accX, _accY, _accZ;
int16_t _gyroX, _gyroY, _gyroZ;

double gyroXangle, gyroYangle;  // Angle calculate using the gyro only
double compAngleX, compAngleY;  // Calculated angle using a complementary filter
double kalAngleX, kalAngleY;    // Calculated angle using a Kalman filter

uint32_t timer;

void setup() {
  Serial.begin(115200);
  Wire.begin();
  mpu.initialize();
  delay(100);
}
void loop() {

  mpu.getMotion6(&_accX, &_accY, &_accZ, &_gyroX, &_gyroY, &_gyroZ);
   double accX = (double) _accX;
  double accY = (double) _accY;
  double accZ = (double) _accZ;
  double gyroX = (double) _gyroX;
  double gyroY = (double) _gyroY;
  double gyroZ = (double) _gyroZ;
  timer = micros();
  double dt = (double)(micros() - timer) / 1000000;  // Calculate delta time


  double roll = atan((double)accY / sqrt((double)(accX * accX) + (double)(accZ * accZ))) * RAD_TO_DEG;
  double pitch = atan2(-accX, accZ) * RAD_TO_DEG;
  double gyroXrate = gyroX / 131.0;  // Convert to deg/s
  double gyroYrate = gyroY / 131.0;  // Convert to deg/s


  kalmanY.setAngle(pitch);
  compAngleY = pitch;
  kalAngleY = pitch;
  gyroYangle = pitch;

  kalAngleY = kalmanY.getAngle(pitch, gyroYrate, dt);  // Calculate the angle using a Kalman filter

  kalmanX.setAngle(roll);
  compAngleX = roll;
  kalAngleX = roll;
  gyroXangle = roll;

  kalAngleX = kalmanX.getAngle(roll, gyroXrate, dt);  // Calculate the angle using a Kalman filter

  gyroXangle += gyroXrate * dt;  // Calculate gyro angle without any filter
  gyroYangle += gyroYrate * dt;
  //gyroXangle += kalmanX.getRate() * dt; // Calculate gyro angle using the unbiased rate
  //gyroYangle += kalmanY.getRate() * dt;

  compAngleX = 0.93 * (compAngleX + gyroXrate * dt) + 0.07 * roll;  // Calculate the angle using a Complimentary filter
  compAngleY = 0.93 * (compAngleY + gyroYrate * dt) + 0.07 * pitch;

  // Reset the gyro angle when it has drifted too much
  if (gyroXangle < -180 || gyroXangle > 180) {
    gyroXangle = kalAngleX;
  }
  if (gyroYangle < -180 || gyroYangle > 180) {
    gyroYangle = kalAngleY;
  }


  Serial.print(kalAngleX);
  Serial.print(",");
  Serial.println(compAngleX);

  delay(100);
}

@barshatriplee btw when ı tried just your suggestion nan problem was still there

Ive arrenged the code as below complementary filter seems to work good but kalman filter seems to have a huge delay because the output when I roll the mpu6050 ,the kalmans output degreee starts to increase band stops when ı stop rolling but ıt increases very slow like assume mp6050 is rolled to -30 degrees kalman filters output starts from -1.28 to -1.40 in ten seconds.

#include <Wire.h>
#include <Kalman.h>
#include <MPU6050.h>
MPU6050 mpu;

Kalman kalmanX;  // Create the Kalman instances
Kalman kalmanY;

/* IMU Data */
int16_t _accX, _accY, _accZ;
int16_t _gyroX, _gyroY, _gyroZ;

double gyroXangle, gyroYangle;  // Angle calculate using the gyro only
double compAngleX, compAngleY;  // Calculated angle using a complementary filter
double kalAngleX, kalAngleY;    // Calculated angle using a Kalman filter

uint32_t timer;

void setup() {
  Serial.begin(115200);
  Wire.begin();
  mpu.initialize();
  delay(100);
}
void loop() {

  mpu.getMotion6(&_accX, &_accY, &_accZ, &_gyroX, &_gyroY, &_gyroZ);
  double accX = (double)_accX;
  double accY = (double)_accY;
  double accZ = (double)_accZ;
  double gyroX = (double)_gyroX;
  double gyroY = (double)_gyroY;
  double gyroZ = (double)_gyroZ;

  timer = micros();
  double dt = (double)(micros() - timer) / 1000000;  // Calculate delta time


  double roll = atan((double)accY / sqrt((double)(accX * accX) + (double)(accZ * accZ))) * RAD_TO_DEG;
  double pitch = atan2(-accX, accZ) * RAD_TO_DEG;
  double gyroXrate = gyroX / 131.0;  // Convert to deg/s
  double gyroYrate = gyroY / 131.0;  // Convert to deg/s


  // This fixes the transition problem when the accelerometer angle jumps between -180 and 180 degrees
  if ((pitch < -90 && kalAngleY > 90) || (pitch > 90 && kalAngleY < -90)) {
    kalmanY.setAngle(pitch);
    compAngleY = pitch;
    kalAngleY = pitch;
    gyroYangle = pitch;
  } else
    kalAngleY = kalmanY.getAngle(pitch, gyroYrate, dt); // Calculate the angle using a Kalman filter

  if (abs(kalAngleY) > 90)
    gyroXrate = -gyroXrate; // Invert rate, so it fits the restriced accelerometer reading
  kalAngleX = kalmanX.getAngle(roll, gyroXrate, dt);

  gyroXangle += gyroXrate * dt;  // Calculate gyro angle without any filter
  gyroYangle += gyroYrate * dt;
  //gyroXangle += kalmanX.getRate() * dt; // Calculate gyro angle using the unbiased rate
  //gyroYangle += kalmanY.getRate() * dt;

  compAngleX = 0.93 * (compAngleX + gyroXrate * dt) + 0.07 * roll;  // Calculate the angle using a Complimentary filter
  compAngleY = 0.93 * (compAngleY + gyroYrate * dt) + 0.07 * pitch;

  // Reset the gyro angle when it has drifted too much
  if (gyroXangle < -180 || gyroXangle > 180) {
    gyroXangle = kalAngleX;
  }
  if (gyroYangle < -180 || gyroYangle > 180) {
    gyroYangle = kalAngleY;
  }

  Serial.print(roll);
  Serial.print(",");
  Serial.print(kalAngleX);
  Serial.print(",");
  Serial.println(compAngleX);

  delay(100);
}

If you don't understand something, then please ask what I mean.
Did you check the link to the example and compare it with the two lines in your sketch for 'micros()' and 'dt' ?
Spot the difference !

Thanks that solved the problem. Ive seen your warning about micros() order while ım on the phone then on pc ı couldnt see it. So that is why I didnt change it. Thank you again.

Big red arrows to the rescue :wink:

It's not a Kalman Filter. The code does something (no one is quite sure what), but not well. The Mahony or Madgwick filters work much better, and the algorithms are well understood.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.