MPU6050

Hello, i want to measure the angle of a flat platform when it tilts from zero (set by the user).
This deviation will be about + -3 degrees so I need an accuracy of + -0.1 (+-0.5 degrees is stil acceptable).
Is it possible to achieve this with the MPU6050?

If you very carefully calibrate the MPU-6050 accelerometer, following these instructions, and then average a number of measurements, you might be able to achieve 0.1 degree accuracy with tilt and roll measurements.

Both steps, calibration and averaging, are essential.

This is Snipped from the MPU6050.cpp library

//***************************************************************************************
//**********************           Calibration Routines            **********************
//***************************************************************************************
/**
  @brief      Fully calibrate Gyro from ZERO in about 6-7 Loops 600-700 readings
*/
void MPU6050::CalibrateGyro(uint8_t Loops ) {
  double kP = 0.3;
  double kI = 90;
  float x;
  x = (100 - map(Loops, 1, 5, 20, 0)) * .01;
  kP *= x;
  kI *= x;
  
  PID( 0x43,  kP, kI,  Loops);
}

/**
  @brief      Fully calibrate Accel from ZERO in about 6-7 Loops 600-700 readings
*/
void MPU6050::CalibrateAccel(uint8_t Loops ) {

	float kP = 0.3;
	float kI = 20;
	float x;
	x = (100 - map(Loops, 1, 5, 20, 0)) * .01;
	kP *= x;
	kI *= x;
	PID( 0x3B, kP, kI,  Loops);
}

void MPU6050::PID(uint8_t ReadAddress, float kP,float kI, uint8_t Loops){
	uint8_t SaveAddress = (ReadAddress == 0x3B)?((getDeviceID() < 0x38 )? 0x06:0x77):0x13;

	int16_t  Data;
	float Reading;
	int16_t BitZero[3];
	uint8_t shift =(SaveAddress == 0x77)?3:2;
	float Error, PTerm, ITerm[3];
	int16_t eSample;
	uint32_t eSum ;
	Serial.write('>');
	for (int i = 0; i < 3; i++) {
		I2Cdev::readWords(devAddr, SaveAddress + (i * shift), 1, (uint16_t *)&Data); // reads 1 or more 16 bit integers (Word)
		Reading = Data;
		if(SaveAddress != 0x13){
			BitZero[i] = Data & 1;										 // Capture Bit Zero to properly handle Accelerometer calibration
			ITerm[i] = ((float)Reading) * 8;
			} else {
			ITerm[i] = Reading * 4;
		}
	}
	for (int L = 0; L < Loops; L++) {
		eSample = 0;
		for (int c = 0; c < 100; c++) {// 100 PI Calculations
			eSum = 0;
			for (int i = 0; i < 3; i++) {
				I2Cdev::readWords(devAddr, ReadAddress + (i * 2), 1, (uint16_t *)&Data); // reads 1 or more 16 bit integers (Word)
				Reading = Data;
				if ((ReadAddress == 0x3B)&&(i == 2)) Reading -= 16384;	//remove Gravity
				Error = -Reading;
				eSum += abs(Reading);
				PTerm = kP * Error;
				ITerm[i] += (Error * 0.001) * kI;				// Integral term 1000 Calculations a second = 0.001
				if(SaveAddress != 0x13){
					Data = round((PTerm + ITerm[i] ) / 8);		//Compute PID Output
					Data = ((Data)&0xFFFE) |BitZero[i];			// Insert Bit0 Saved at beginning
				} else Data = round((PTerm + ITerm[i] ) / 4);	//Compute PID Output
				I2Cdev::writeWords(devAddr, SaveAddress + (i * shift), 1, (uint16_t *)&Data);
			}
			if((c == 99) && eSum > 1000){						// Error is still to great to continue 
				c = 0;
				Serial.write('*');
			}
			if((eSum * ((ReadAddress == 0x3B)?.05: 1)) < 5) eSample++;	// Successfully found offsets prepare to  advance
			if((eSum < 100) && (c > 10) && (eSample >= 10)) break;		// Advance to next Loop
			delay(1);
		}
		Serial.write('.');
		kP *= .75;
		kI *= .75;
		for (int i = 0; i < 3; i++){
			if(SaveAddress != 0x13) {
				Data = round((ITerm[i] ) / 8);		//Compute PID Output
				Data = ((Data)&0xFFFE) |BitZero[i];	// Insert Bit0 Saved at beginning
			} else Data = round((ITerm[i]) / 4);
			I2Cdev::writeWords(devAddr, SaveAddress + (i * shift), 1, (uint16_t *)&Data);
		}
	}
	resetFIFO();
	resetDMP();
}

#define printfloatx(Name,Variable,Spaces,Precision,EndTxt) { Serial.print(F(Name)); {char S[(Spaces + Precision + 3)];Serial.print(F(" ")); Serial.print(dtostrf((float)Variable,Spaces,Precision ,S));}Serial.print(F(EndTxt)); }//Name,Variable,Spaces,Precision,EndTxt
void MPU6050::PrintActiveOffsets() {
	uint8_t AOffsetRegister = (getDeviceID() < 0x38 )? MPU6050_RA_XA_OFFS_H:0x77;
	int16_t Data[3];
	//Serial.print(F("Offset Register 0x"));
	//Serial.print(AOffsetRegister>>4,HEX);Serial.print(AOffsetRegister&0x0F,HEX);
	Serial.print(F("\n//           X Accel  Y Accel  Z Accel   X Gyro   Y Gyro   Z Gyro\n//OFFSETS   "));
	if(AOffsetRegister == 0x06)	I2Cdev::readWords(devAddr, AOffsetRegister, 3, (uint16_t *)Data);
	else {
		I2Cdev::readWords(devAddr, AOffsetRegister, 1, (uint16_t *)Data);
		I2Cdev::readWords(devAddr, AOffsetRegister+3, 1, (uint16_t *)Data+1);
		I2Cdev::readWords(devAddr, AOffsetRegister+6, 1, (uint16_t *)Data+2);
	}
	//	A_OFFSET_H_READ_A_OFFS(Data);
	printfloatx("", Data[0], 5, 0, ",  ");
	printfloatx("", Data[1], 5, 0, ",  ");
	printfloatx("", Data[2], 5, 0, ",  ");
	I2Cdev::readWords(devAddr, 0x13, 3, (uint16_t *)Data);
	//	XG_OFFSET_H_READ_OFFS_USR(Data);
	printfloatx("", Data[0], 5, 0, ",  ");
	printfloatx("", Data[1], 5, 0, ",  ");
	printfloatx("", Data[2], 5, 0, "\n");
}

With the sensor level and still, it will calibrate the offsets for the Accelerometer and Gyro. It does this by achieving as close to zero deviation error. It uses a PID loop to directly alter the offsets and find the optimal offsets. This is done quickly
The number of loops for the initial calibration should be at least 6 for both the gyro and the accelerometer. additional fine-tuning can be achieved by increasing the loop count I dought that a loop count over 20 will have much effect on the final results.
Use the readings from PrintActiveOffsets() to initialize the MPU on startup and then recalibrate it again for a quicker calibration in the future.
The calibration function could easily be separated from the MPU6050.cpp library and become a stand-alone function for your project.

Z

With the sensor level and still, it will calibrate the offsets for the Accelerometer

No, the above WILL NOT correctly calibrate the accelerometer.

The gyro is completely irrelevant and useless for accurate tilt measurements on a platform.

jremington:
No, the above WILL NOT correctly calibrate the accelerometer.

The gyro is completely irrelevant and useless for accurate tilt measurements.

So @jremington, if you have The MPU6050 level and gravity is Down in the Z-axis. and the output is reading zero for X and Y or 1G for Z-axis Wouldn't that be calibrated?
Z

No, because X and Y won't read +/-1 g when they are pointing up or down.

At minimum, three offsets and three scale factors must all be determined, or the tilt calculations will be in error. The method I linked above also calculates 3 off-axis corrections.

jremington:
No, because X and Y won't read +/-1 g when they are pointing up or down.

At minimum, three offsets and three scale factors must all be determined, or the tilt calculations will be in error. The method I linked above also calculates 3 off-axis corrections.

So the MPU6050 has firmware with 6 axis Digital Motion Processing. where it provides Quaternion values that can be used to give angles. MPU6050 Library is here the latest firmware is used in this example here MPU6050_DMP6_using_DMP_V6.12. it contains the calibration routine and is ready for him to try.
The link you shared mostly shows magnetometer calibration instructions it has very little on accelerometer calibration.
The question I have is if we were to use 6 axes provided by the Manufacturer to generate the angles from there chip would I need corrections as you mentioned and would the only requirement is to make sure the MPU is calibrated to a level state before assuming the angles would be correct. The manufacturing process created a need for an offset to be used. the offset literally adds to the calculation of the acceleration (and Gyro) routines to adjust for these errors This does it internally in the MPU6050. Without this calibration, your suggested accelerometer "3 off-axis corrections" would not work.

Please note I only provide the solution for the calibration of the offsets "calibrate the offsets for the Accelerometer and Gyro" and not the math to generate Quaternion or other angles from acceleration accurately so I am lost as to why your statement @jremington of "No, the above WILL NOT correctly calibrate the accelerometer." is legit.

Z

magnetometer calibration instructions it has very little on accelerometer calibration.

It is exactly the same problem and the same technique is used to fix the problem, for either a magnetometer or an accelerometer.

That is why the title of the tutorial is "calibrating-any-compass-or-accelerometer-for-arduino"

I am lost as to why your statement ...

To calibrate a magnetometer or accelerometer, you need to determine three offsets, one for each axis, and three scale factors, one for each axis, at minimum.

The method you propose does not do this.

jremington:
It is exactly the same problem and the same technique is used to fix the problem.

So, @jremington you are saying if the MPU6050 offsets are accurate (or not?), you can calibrate the Accelerometers mathematically using this method, the same method that is used to calibrate a compass?
I will concede and let your method win.

Z

You should try the procedure. I'm confident you will be impressed at how well it works.

This forum post demonstrates a working example (again, a magnetometer, but works for accelerometer too).