Introduction: MPU 6050 Gyro,Accelerometer Communication With Arduino (Atmega328p)
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)
Attachments
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