Inertial Measurement Units (IMUs)are everywhere in the modern world, whether smartphones, game controllers or even aircraft. Inevitably, there will come a day when your project requires an IMU. This article discusses how to integrate one of the most popular IMUs with an Arduino – the MPU6050.
The MPU6050 can measure 6 degrees of freedom. Specifically, the MPU6050 can measure 3 linear accelerations (X, Y and Z) as well as 3 angular velocities (About X, Y and Z). Using some clever maths – which we will discuss in a future post, it is possible, to use the measured angular velocities and accelerations to calculate the current orientation of the MPU6050. Before we can do this, we must first learn how to integrate an MPU6050 with an Arduino.
How does the MPU6050 work?
The MPU6050 utilizes MEMs technology to measure linear acceleration and angular velocities. This is a complicated technology and the specifics on how this works are out of the scope of this article. The processing units, internal to the MPU6050, will use the MEMs measurements and convert them into raw acceleration and angular velocity measurements which can be accessed through the I2C Bus. We can then use an Arduino to read these values from the I2C bus and apply the appropriate conversions to ascertain the true accelerations and velocities. More information on how to use I2C with an Arduino can be found here.
Arduino-MPU6050 Integration
Firstly, to integrate the MPU6050 module with the Arduino, you need to connect the MPU6050 module and Arduino as shown below. The image below shows a nano board, the correct I2C pins for your Arduino can be found here.
MPU6050 Module Pin Out
- VCC – Power In. You should connect this pin to a +3.3V or +5V pin. Be careful not to exceed +5V as you could damage your module.
- GND – Connect this pin to the ground of your Arduino.
- SCL – Serial Clock. This pin should be connected to the I2C Serial Clock pin.
- SDA – Serial Data. This pin should be connected to the I2C Serial Data pin.
- XDA – Auxiliary Serial Data. This pin can be used to connect additional I2C modules to the MPU6050. This pin should be left disconnected for this tutorial.
- XCL – Auxiliary Serial Clock. This pin can be used to connect additional I2C modules to the MPU6050. This pin should be left disconnected for this tutorial.
- AD0 – If you desire to use more than one MPU6050 on a single I2C bus, you will need to change the device address. Leave this disconnected for a device address 0x68 (HEX). Connect to VCC for 0x69 (HEX).
- INT – Interrupt. This pin indicates to the Arduino that the MPU6050 has fresh data.
Software Implementation
You could use an existing arduino library, there are plenty available on the internet. However, these options severely limit our understanding and can make more complex projects significantly more difficult in the future. In this article, we will be creating our own Arduino sketch to read the data from an MPU6050.
Device Data Sheet
Whenever you develop for a new device, you should always make reference to the device datasheet. In this case, you should make reference to the MPU6050 register map and descriptions, available here.
Software
Initialising MPU6050
The procedure to initialise the MPU6050 requires three steps.
- Activate the MPU6050 / Wake from sleep – to do this, you need to set register 0x6B to 0.
- Set the Accelerometer Sensitivity. The MPU6050 supports four different levels of sensitivity. These are ±2g, ±4g, ±8g and ±16g.
- Set the Gyroscope Sensitivity. As for acceleration, the MPU6050 supports four different gyroscope sensitivities. These are ±250deg/s, ±500deg/s, ±1000deg/s and ±2000deg/s.
All of this can be achieved using the code below:
void initilise_MPU6050() {
//Activate the MPU-6050
Wire.beginTransmission(0x68);// Device Address
Wire.write(0x6B);// Register to write too
Wire.write(0x00);// Value to write
Wire.endTransmission();
// Set Accelerometer sensitivity
// Wire.write; 2g -> 0x00, 4g -> 0x08, 8g -> 0x10, 16g -> 0x18
Wire.beginTransmission(0x68);
Wire.write(0x1C);
Wire.write(0x08);
Wire.endTransmission();
// Set Gyro sensitivity
// 250 deg/s -> 0x00, 500 deg/s -> 0x08, 1000 deg/s -> 0x10, 2000 deg/s -> 0x18
Wire.beginTransmission(0x68);
Wire.write(0x1B);
Wire.write(0x08);
Wire.endTransmission();
}
Reading data from the MPU6050
Reading data from the MPU6050 can be achieved in much the same way as any other I2C device. You should first establish which register you want to read from and then read a number of bytes from the I2C device.
As shown in the register map referenced above, the acceleration and gyro data (along with the device temperature) lie in registers 0x3B to 0x47. We can also see, that each degree of freedom is also represented by a 16 bit (2 byte) value. This, therefore, means we must read 14 bytes from the MPU6050. To ascertain the raw linear acceleration and gyro scope readings, we must combine the high and low bytes for each degree of freedom. To do this, we must shift the high byte 8 bits to the left. The resulting 16-bit value should then be or’d with the low byte. This can be done as follows:
int16_t value = (highByte << 8 | lowByte)
Once we have the data in the appropriate 16-bit format, we must then apply the appropriate conversion to the data read.
For Acceleration in units of g, this can be achieved as follows:
a_{correct}=\frac{a_{raw}}{S_f}
Accelerometer Sensitivity | Scale Factor |
---|---|
±2g | 16374 LSB/g |
±4g | 8192 LSB/g |
±8g | 4096 LSB/g |
±16g | 2048 LSB/g |
For the angular velocity in units of deg/s, this can be achieved as follows:
\omega_{correct}=\frac{\omega_{raw}}{S_f}
Gyroscope Sensitivity | Scale Factor |
---|---|
±250 deg/s | 131 LSB/deg/s |
±500 deg/s | 65.5 LSB/deg/s |
±1000 deg/s | 32.8 LSB/deg/s |
±2000 deg/s | 16.4 LSB/deg/s |
For temperature in units of Degrees Celsius, this can be done as follows:
T_{correct}=\frac{T_{raw}}{340.0}+36.53
This can be implemented in code as follows:
void read_MPU6050() {
// Set the Register to read from
Wire.beginTransmission(0x68);
Wire.write(0x3B);
Wire.endTransmission();
// Request 14 bytes from MPU6050
Wire.requestFrom(0x68, 14);
// Read data 6x Acceleration Bytes, 2x Temp Bytes, 6x Gyro Bytes
// High Byte Low Byte
int16_t acc_x = (Wire.read() << 8 | Wire.read());
int16_t acc_y = Wire.read() << 8 | Wire.read();
int16_t acc_z = Wire.read() << 8 | Wire.read();
int16_t temperature = Wire.read() <<8 | Wire.read();
int16_t gyro_x = Wire.read()<<8 | Wire.read();
int16_t gyro_y = Wire.read()<<8 | Wire.read();
int16_t gyro_z = Wire.read()<<8 | Wire.read();
//Acceleration Conversion
double aX = acc_x/8192.0;
double aY = acc_y/8192.0;
double aZ = acc_z/8192.0;
// Temperature Conversion
float temp = (temperature/340.0)+36.53;
// Gyroscope Conversion
double gX = gyro_x/65.5;
double gY = gyro_y/65.5;
double gZ = gyro_z/65.5;
}
Finally, if we combine the two function presented above, as well as adding some Serial print functions, we achieve the following:
#include <Wire.h>
void setup() {
Wire.begin();
Serial.begin(115200);
initilise_MPU6050();
}
void loop() {
read_MPU6050();
delay(100);
}
void initilise_MPU6050() {
//Activate the MPU-6050
Wire.beginTransmission(0x68);// Device Address
Wire.write(0x6B);// Register to write too
Wire.write(0x00);// Value to write
Wire.endTransmission();
// Set Accelerometer sensitivity
// Wire.write; 2g -> 0x00, 4g -> 0x08, 8g -> 0x10, 16g -> 0x18
Wire.beginTransmission(0x68);
Wire.write(0x1C);
Wire.write(0x08);
Wire.endTransmission();
// Set Gyro sensitivity
// 250 deg/s -> 0x00, 500 deg/s -> 0x08, 1000 deg/s -> 0x10, 2000 deg/s -> 0x18
Wire.beginTransmission(0x68);
Wire.write(0x1B);
Wire.write(0x08);
Wire.endTransmission();
}
void read_MPU6050() {
// Set the Register to read from
Wire.beginTransmission(0x68);
Wire.write(0x3B);
Wire.endTransmission();
// Request 14 bytes from MPU6050
Wire.requestFrom(0x68, 14);
// Read data 6x Acceleration Bytes, 2x Temp Bytes, 6x Gyro Bytes
// High Byte Low Byte
int16_t acc_x = (Wire.read() << 8 | Wire.read());
int16_t acc_y = Wire.read() << 8 | Wire.read();
int16_t acc_z = Wire.read() << 8 | Wire.read();
int16_t temperature = Wire.read() <<8 | Wire.read();
int16_t gyro_x = Wire.read()<<8 | Wire.read();
int16_t gyro_y = Wire.read()<<8 | Wire.read();
int16_t gyro_z = Wire.read()<<8 | Wire.read();
//Acceleration Conversion (into g)
double aX = acc_x/8192.0;
double aY = acc_y/8192.0;
double aZ = acc_z/8192.0;
// Temperature Conversion (into deg C)
float temp = (temperature/340.0)+36.53;
// Gyroscope Conversion (into degrees/second)
double gX = gyro_x/65.5;
double gY = gyro_y/65.5;
double gZ = gyro_z/65.5;
Serial.print(aZ);
Serial.print(" | ");
Serial.print(temp);
Serial.print(" | ");
Serial.println(gZ);
}
All being well, you should see something similar in your serial console:
0.88 | 24.67 | 0.08
0.88 | 24.72 | 0.08
0.89 | 24.72 | 0.08
0.89 | 24.67 | 0.17
0.89 | 24.62 | 0.02
0.88 | 24.67 | 0.26
0.88 | 24.72 | -0.03
0.87 | 24.72 | 0.00
0.89 | 24.62 | 0.05
0.88 | 24.77 | 0.03
0.89 | 24.62 | 0.09
0.89 | 24.77 | 0.14