Introduction: MPU 6050 Gyro,Accelerometer Communication With Arduino (Atmega328p)

About: DIY projects

The MPU6050 IMU has both 3-Axis accelerometer and 3-Axis gyroscope integrated on a single chip.

The gyroscope measures rotational velocity or rate of change of the angular position over time, along the X, Y and Z axis.

The outputs of the gyroscope are in degrees per second, so in order to get the angular position we just need to integrate the angular velocity.

On the other hand, the MPU6050 accelerometer measures acceleration by measuring gravitational acceleration along the 3 axes and using some trigonometry math we can calculate the angle at which the sensor is positioned. So, if we fuse, or combine the accelerometer and gyroscope data we can get very accurate information about the sensor orientation.

3-axis Gyroscope
The MPU-6050 consist of a 3 axis gyroscope which can detect rotational velocity along the x,y,z axis with micro electro mechanical system technology (MEMS). When the sensor is rotated along any axis a vibration is produced due to Coriolis effect which is detected by the MEMS.16-bit ADC is used to digitize voltage to sample each axis.+/- 250, +/- 500, +/- 1000, +/- 2000 are the full scale range of output.Angular velocity is measured along each axis in degree per second unit.

Useful Link:................ http://ecee.colorado.edu/ecen3000/labs/lab6/MotionSensing.pdf

Arduino Board:.…....... https://amzn.to/2R5Zv6D

MPU6050 IMU ……………https://compoindia.com/product/mpu6050-3-axis-accelerometer-and-gyroscope-sensor/

Step 1: MPU-6050 Module

The MPU-6050 module has 8 pins,

INT: Interrupt digital output pin.

AD0: I2C Slave Address LSB pin. This is 0th bit in 7-bit slave address of device. If connected to VCC then it is read as logic one and slave address changes.

XCL: Auxiliary Serial Clock pin. This pin is used to connect other I2C interface enabled sensors SCL pin to MPU-6050.

XDA: Auxiliary Serial Data pin. This pin is used to connect other I2C interface enabled sensors SDA pin to MPU-6050.

SCL: Serial Clock pin. Connect this pin to microcontrollers SCL pin. SDA: Serial Data pin. Connect this pin to microcontrollers SDA pin.

GND: Ground pin. Connect this pin to ground connection.

VCC: Power supply pin. Connect this pin to +5V DC supply. MPU-6050 module has Slave address (When AD0 = 0, i.e. it is not connected to Vcc) as,

Slave Write address(SLA+W): 0xD0

Slave Read address(SLA+R): 0xD1

Step 2: Calculations

Gyroscope and Accelerometer sensor data of MPU6050 module consists of 16-bit raw data in 2’s complement form.

Temperature sensor data of MPU6050 module consists of 16-bit data (not in 2’s complement form).

Now suppose we have selected,

  • - Accelerometer full scale range of +/- 2g with Sensitivity Scale Factor of 16,384 LSB(Count)/g.
  • - Gyroscope full scale range of +/- 250 °/s with Sensitivity Scale Factor of 131 LSB (Count)/°/s. then,

To get sensor raw data, we need to first perform 2’s complement on sensor data of Accelerometer and gyroscope. After getting sensor raw data we can calculate acceleration and angular velocity by dividing sensor raw data with their sensitivity scale factor as follows--

Accelerometer values in g (g force)

  • Acceleration along the X axis = (Accelerometer X axis raw data/16384) g.
  • Acceleration along the Y axis = (Accelerometer Y axis raw data/16384) g.
  • Acceleration along the Z axis = (Accelerometer Z axis raw data/16384) g.

Gyroscope values in °/s (degree per second)

  • Angular velocity along the X axis = (Gyroscope X axis raw data/131) °/s.
  • Angular velocity along the Y axis = (Gyroscope Y axis raw data/131) °/s.
  • Angular velocity along the Z axis = (Gyroscope Z axis raw data/131) °/s.

Temperature value in °/c (degree per Celsius)

Temperature in degrees C = ((temperature sensor data)/340 + 36.53) °/c.

For example,

Suppose, after 2’ complement we get accelerometer X axes raw value = +15454

Then Ax = +15454/16384 = 0.94 g.

More,

So we know we are running at a sensitivity of +/-2G and +/- 250deg/s but how do our values correspond to those accelerations/angles.

These are both straight line graphs and we can work out from them that for 1G we will read 16384 and for 1degree/sec we will read 131.07(Although the .07 will get ignore due to binary) these values were just worked out by drawing the straight line graph with 2G at 32767 and -2G at -32768 and 250/-250 at the same values.

So now we know our sensitivity values (16384 and 131.07) we just need to minus the offsets from our values and then devide by the sensitivity.

These will work fine for the X and Y values but as the Z was recorded at 1G and not 0 we will need to minus off 1G (16384) before we divide by our sensitivity.

Step 3: MPU6050-Atmega328p Connections

  • Just Connect everyting as given in the diagram....

The connections are given as follows:-

  • MPU6050<======> Arduino Nano.

VCC <======> 5v out pin

GND <======>Ground pin

SDA <======> A4 pin //serial data

SCL<======> A5 pin // serial clock

Pitch and Roll Calculation:
Roll is the rotation around x-axis and pitch is the rotation along y-axis.

The result is in radians. (convert to degrees by multiplying by 180 and dividing by pi)

Step 4: Codes and Explanations

/*
   Arduino and MPU6050 Accelerometer and Gyroscope Sensor Tutorial
   by Dejan, <a href="https://howtomechatronics.com" rel="nofollow"> <a href="https://howtomechatronics.com" rel="nofollow"> https://howtomechatronics.com
</a>
</a>
*/

#include <Wire.h>

const int MPU = 0x68; // MPU6050 I2C address
float AccX, AccY, AccZ;
float GyroX, GyroY, GyroZ;
float accAngleX, accAngleY, gyroAngleX, gyroAngleY, gyroAngleZ;
float roll, pitch, yaw;
float AccErrorX, AccErrorY, GyroErrorX, GyroErrorY, GyroErrorZ;
float elapsedTime, currentTime, previousTime;
int c = 0;

void setup() {
  Serial.begin(19200);
  Wire.begin();                      // Initialize comunication
  Wire.beginTransmission(MPU);       // Start communication with MPU6050 // MPU=0x68
  Wire.write(0x6B);                  // Talk to the register 6B
  Wire.write(0x00);                  // Make reset - place a 0 into the 6B register
  Wire.endTransmission(true);        //end the transmission
  /*
  // Configure Accelerometer Sensitivity - Full Scale Range (default +/- 2g)
  Wire.beginTransmission(MPU);
  Wire.write(0x1C);                  //Talk to the ACCEL_CONFIG register (1C hex)
  Wire.write(0x10);                  //Set the register bits as 00010000 (+/- 8g full scale range)
  Wire.endTransmission(true);
  // Configure Gyro Sensitivity - Full Scale Range (default +/- 250deg/s)
  Wire.beginTransmission(MPU);
  Wire.write(0x1B);                   // Talk to the GYRO_CONFIG register (1B hex)
  Wire.write(0x10);                   // Set the register bits as 00010000 (1000deg/s full scale)
  Wire.endTransmission(true);
  delay(20);
  */
  // Call this function if you need to get the IMU error values for your module
  calculate_IMU_error();
  delay(20);

}

void loop() {
  // === Read acceleromter data === //
  Wire.beginTransmission(MPU);
  Wire.write(0x3B); // Start with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU, 6, true); // Read 6 registers total, each axis value is stored in 2 registers
  //For a range of +-2g, we need to divide the raw values by 16384, according to the datasheet
  AccX = (Wire.read() << 8 | Wire.read()) / 16384.0; // X-axis value
  AccY = (Wire.read() << 8 | Wire.read()) / 16384.0; // Y-axis value
  AccZ = (Wire.read() << 8 | Wire.read()) / 16384.0; // Z-axis value
  // Calculating Roll and Pitch from the accelerometer data
  accAngleX = (atan(AccY / sqrt(pow(AccX, 2) + pow(AccZ, 2))) * 180 / PI) - 0.58; // AccErrorX ~(0.58) See the calculate_IMU_error()custom function for more details
  accAngleY = (atan(-1 * AccX / sqrt(pow(AccY, 2) + pow(AccZ, 2))) * 180 / PI) + 1.58; // AccErrorY ~(-1.58)

  // === Read gyroscope data === //
  previousTime = currentTime;        // Previous time is stored before the actual time read
  currentTime = millis();            // Current time actual time read
  elapsedTime = (currentTime - previousTime) / 1000; // Divide by 1000 to get seconds
  Wire.beginTransmission(MPU);
  Wire.write(0x43); // Gyro data first register address 0x43
  Wire.endTransmission(false);
  Wire.requestFrom(MPU, 6, true); // Read 4 registers total, each axis value is stored in 2 registers
  GyroX = (Wire.read() << 8 | Wire.read()) / 131.0; // For a 250deg/s range we have to divide first the raw value by 131.0, according to the datasheet
  GyroY = (Wire.read() << 8 | Wire.read()) / 131.0;
  GyroZ = (Wire.read() << 8 | Wire.read()) / 131.0;
  // Correct the outputs with the calculated error values
  GyroX = GyroX + 0.56; // GyroErrorX ~(-0.56)
  GyroY = GyroY - 2; // GyroErrorY ~(2)
  GyroZ = GyroZ + 0.79; // GyroErrorZ ~ (-0.8)

  // Currently the raw values are in degrees per seconds, deg/s, so we need to multiply by sendonds (s) to get the angle in degrees
  gyroAngleX = gyroAngleX + GyroX * elapsedTime; // deg/s * s = deg
  gyroAngleY = gyroAngleY + GyroY * elapsedTime;
  yaw =  yaw + GyroZ * elapsedTime;

  // Complementary filter - combine acceleromter and gyro angle values
  roll = 0.96 * gyroAngleX + 0.04 * accAngleX;
  pitch = 0.96 * gyroAngleY + 0.04 * accAngleY;
  
  // Print the values on the serial monitor
  Serial.print(roll);
  Serial.print("/");
  Serial.print(pitch);
  Serial.print("/");
  Serial.println(yaw);
}


void calculate_IMU_error() {
  // We can call this funtion in the setup section to calculate the accelerometer and gyro data error. From here we will get the error values used in the above equations printed on the Serial Monitor.
  // Note that we should place the IMU flat in order to get the proper values, so that we then can the correct values
  // Read accelerometer values 200 times
  while (c < 200) {
    Wire.beginTransmission(MPU);
    Wire.write(0x3B);
    Wire.endTransmission(false);
    Wire.requestFrom(MPU, 6, true);
    AccX = (Wire.read() << 8 | Wire.read()) / 16384.0 ;
    AccY = (Wire.read() << 8 | Wire.read()) / 16384.0 ;
    AccZ = (Wire.read() << 8 | Wire.read()) / 16384.0 ;
    // Sum all readings
    AccErrorX = AccErrorX + ((atan((AccY) / sqrt(pow((AccX), 2) + pow((AccZ), 2))) * 180 / PI));
    AccErrorY = AccErrorY + ((atan(-1 * (AccX) / sqrt(pow((AccY), 2) + pow((AccZ), 2))) * 180 / PI));
    c++;
  }
  //Divide the sum by 200 to get the error value
  AccErrorX = AccErrorX / 200;
  AccErrorY = AccErrorY / 200;
  c = 0;
  // Read gyro values 200 times
  while (c < 200) {
    Wire.beginTransmission(MPU);
    Wire.write(0x43);
    Wire.endTransmission(false);
    Wire.requestFrom(MPU, 6, true);
    GyroX = Wire.read() << 8 | Wire.read();
    GyroY = Wire.read() << 8 | Wire.read();
    GyroZ = Wire.read() << 8 | Wire.read();
    // Sum all readings
    GyroErrorX = GyroErrorX + (GyroX / 131.0);
    GyroErrorY = GyroErrorY + (GyroY / 131.0);
    GyroErrorZ = GyroErrorZ + (GyroZ / 131.0);
    c++;
  }
  //Divide the sum by 200 to get the error value
  GyroErrorX = GyroErrorX / 200;
  GyroErrorY = GyroErrorY / 200;
  GyroErrorZ = GyroErrorZ / 200;
  // Print the error values on the Serial Monitor
  Serial.print("AccErrorX: ");
  Serial.println(AccErrorX);
  Serial.print("AccErrorY: ");
  Serial.println(AccErrorY);
  Serial.print("GyroErrorX: ");
  Serial.println(GyroErrorX);
  Serial.print("GyroErrorY: ");
  Serial.println(GyroErrorY);
  Serial.print("GyroErrorZ: ");
  Serial.println(GyroErrorZ);
}


-----------------------------------------------------------------------------------------------
Results:-
X =  Y =  Z = 
--------------------------------------------------------------------------------------------

Important Note:-
----------------
<p>In the loop section we start by reading the accelerometer data. The data for each axis is stored in 2 bytes or registers and we can see the addresses of these registers from the datasheet of the sensor.</p><p>In order to read them all, we start with the first register, and using the requiestFrom() function we request to read all 6 registers for the X, Y and Z axes. Then we read the data from each register, and because the outputs are twos complement, we combine them appropriately to get the correct values.</p>

Step 5: Understanding Tilt Angle

Accelerometer

The earth’s gravity is a constant acceleration where the force is always pointing down to the centre of the Earth.

When the accelerometer is parallel with the gravity, the measured acceleration will be 1G, when the accelerometer is perpendicular with the gravity, it will measure 0G.

Tilt angle can be calculated from the measured acceleration by using this equation:

θ = sin-1 (Measured Acceleration / Gravity Acceleration)

Gyro
Gyro (a.k.a. rate sensor) is used to measure the angular velocity (ω).

In order to get the tilt angle of a robot, we need to integrate the data from the gyro as shown in the equation below:

ω = dθ / dt ,

θ = ∫ ω dt

Gyro and Accelerometer Sensor Fusion
After studying the characteristics of both gyro and accelerometer, we know that they have their own strengths and weakness. The calculated tilt angle from the accelerometer data has slow response time, while the integrated tilt angle from the gyro data is subjected to drift over a period of time. In other words, we can say that the accelerometer data is useful for long term while the gyro data is useful for short term.

Link for better understanding: Click Here