MPU6050简介

The MPU-6050 is the world’s first and only 6-axis motion tracking devices designed for the low power, low cost, and high performance requirements of smartphones, tablets and wearable sensors.

应用领域

  • BlurFree™ technology (for Video/Still Image Stabilization)
  • AirSign™ technology (for Security/Authentication)
  • TouchAnywhere™ technology (for “no touch” UI Application Control/Navigation)
  • MotionCommand™ technology (for Gesture Short-cuts)
  • Motion-enabled game and application framework
  • InstantGesture™ iG™ gesture recognition
  • Location based services, points of interest, and dead reckoning
  • Handset and portable gaming
  • Motion-based game controllers
  • 3D remote controls for Internet connected DTVs and set top boxes, 3D mice
  • Wearable sensors for health, fitness and sports
  • Toys

Arduino案例

​ Arduino使用现成的库很容易上手。

使用Arduino进行获取数据

  1. 使用IIC进行通信
  2. 使用Wire库

row data

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include <Wire.h>

const int mpuAddress= 0x68;

long accelX, accelY, accelZ;//xyz方向的加速度
float gForceX, gForceY, gForceZ;

long gyroX, gyroY, gyroZ;
float rotX, rotY, rotZ;

long tmpDataX, tmpDataY, tmpDataZ;

void setup() {

Serial.begin(9600);
Wire.begin();
setupMPU();
pinMode(3, OUTPUT);
pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
pinMode(9, OUTPUT);

//testOfThefourPins();//okay
}


void loop() {
recordAccelRegisters();
recordGyroRegisters();
printData();
connecMPU6050WithLed();
//printTmpData();
delay(200);
}
void connecMPU6050WithLed(){
if(rotX>0) analogWrite(3, 255);
else analogWrite(3, 0);
if(rotY>0) analogWrite(5, 255);
else analogWrite(5, 0);
if(rotZ>0) analogWrite(6, 255);
else analogWrite(6, 0);
}
void testOfThefourPins() {
analogWrite(3, 255);
analogWrite(5, 255);
analogWrite(6, 255);
analogWrite(9, 255);
}
void setupMPU() {
Wire.beginTransmission(mpuAddress); //This is the I2C address of the MPU (b1101000/b1101001 for AC0 low/high datasheet sec. 9.2)
Wire.write(0x6B); //Accessing the register 6B - Power Management (Sec. 4.28)
Wire.write(0b00000000); //Setting SLEEP register to 0. (Required; see Note on p. 9)
Wire.endTransmission();
Wire.beginTransmission(mpuAddress); //I2C address of the MPU
Wire.write(0x1B); //Accessing the register 1B - Gyroscope Configuration (Sec. 4.4)
Wire.write(0x00000000); //Setting the gyro to full scale +/- 250deg./s
Wire.endTransmission();
Wire.beginTransmission(mpuAddress); //I2C address of the MPU
Wire.write(0x1C); //Accessing the register 1C - Acccelerometer Configuration (Sec. 4.5)
Wire.write(0b00000000); //Setting the accel to +/- 2g
Wire.endTransmission();
}

void recordAccelRegisters() {
Wire.beginTransmission(mpuAddress); //I2C address of the MPU
Wire.write(0x3B); //Starting register for Accel Readings
Wire.endTransmission();
Wire.requestFrom(mpuAddress, 6); //Request Accel Registers (3B - 40)
while (Wire.available() < 6)
;
accelX = Wire.read() << 8 | Wire.read(); //Store first two bytes into accelX
accelY = Wire.read() << 8 | Wire.read(); //Store middle two bytes into accelY
accelZ = Wire.read() << 8 | Wire.read(); //Store last two bytes into accelZ
tmpDataX = accelX;
tmpDataY = accelY;
tmpDataZ = accelZ;
processAccelData();
}

void processAccelData() {
gForceX = accelX / 16384.0;
gForceY = accelY / 16384.0;
gForceZ = accelZ / 16384.0;
}

void recordGyroRegisters() {
Wire.beginTransmission(mpuAddress); //I2C address of the MPU
Wire.write(0x43); //Starting register for Gyro Readings
Wire.endTransmission();
Wire.requestFrom(mpuAddress, 6); //Request Gyro Registers (43 - 48)
while (Wire.available() < 6)
;
gyroX = Wire.read() << 8 | Wire.read(); //Store first two bytes into accelX
gyroY = Wire.read() << 8 | Wire.read(); //Store middle two bytes into accelY
gyroZ = Wire.read() << 8 | Wire.read(); //Store last two bytes into accelZ
processGyroData();
}

void processGyroData() {
rotX = gyroX / 131.0;
rotY = gyroY / 131.0;
rotZ = gyroZ / 131.0;
}

void printData() {
Serial.print("Gyro (deg)");
Serial.print(" X=");
Serial.print(rotX);
Serial.print(" Y=");
Serial.print(rotY);
Serial.print(" Z=");
Serial.print(rotZ);
Serial.print(" Accel (g)");
Serial.print(" X=");
Serial.print(gForceX);
Serial.print(" Y=");
Serial.print(gForceY);
Serial.print(" Z=");
Serial.println(gForceZ);
}
void printTmpData() {
Serial.print(tmpDataX);
Serial.print("\t");
Serial.print(tmpDataY);
Serial.print("\t");
Serial.print(tmpDataZ);
Serial.print("\n");
}

使用Arduino进行姿态解算

  1. 使用卡尔曼滤波进行提高数据精准度

  2. 需要进行一些数学计算

    pitch Roll

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#include <Kalman.h>
#include <Wire.h>
#include <Math.h>

float fRad2Deg = 57.295779513f; //将弧度转为角度的乘数
const int MPU = 0x68; //MPU-6050的I2C地址
const int nValCnt = 7; //一次读取寄存器的数量

const int nCalibTimes = 1000; //校准时读数的次数
int calibData[nValCnt]; //校准数据

unsigned long nLastTime = 0; //上一次读数的时间
float fLastRoll = 0.0f; //上一次滤波得到的Roll角
float fLastPitch = 0.0f; //上一次滤波得到的Pitch角
Kalman kalmanRoll; //Roll角滤波器
Kalman kalmanPitch; //Pitch角滤波器

void setup() {
Serial.begin(9600); //初始化串口,指定波特率
Wire.begin(); //初始化Wire库
WriteMPUReg(0x6B, 0); //启动MPU6050设备

Calibration(); //执行校准
nLastTime = micros(); //记录当前时间
}

void loop() {
int readouts[nValCnt];
ReadAccGyr(readouts); //读出测量值

float realVals[7];
Rectify(readouts, realVals); //根据校准的偏移量进行纠正

//计算加速度向量的模长,均以g为单位
float fNorm = sqrt(realVals[0] * realVals[0] + realVals[1] * realVals[1] + realVals[2] * realVals[2]);
float fRoll = GetRoll(realVals, fNorm); //计算Roll角
if (realVals[1] > 0) {
fRoll = -fRoll;
}
float fPitch = GetPitch(realVals, fNorm); //计算Pitch角
if (realVals[0] < 0) {
fPitch = -fPitch;
}

//计算两次测量的时间间隔dt,以秒为单位
unsigned long nCurTime = micros();
float dt = (double)(nCurTime - nLastTime) / 1000000.0;
//对Roll角和Pitch角进行卡尔曼滤波
float fNewRoll = kalmanRoll.getAngle(fRoll, realVals[4], dt);
float fNewPitch = kalmanPitch.getAngle(fPitch, realVals[5], dt);
//跟据滤波值计算角度速
float fRollRate = (fNewRoll - fLastRoll) / dt;
float fPitchRate = (fNewPitch - fLastPitch) / dt;

//更新Roll角和Pitch角
fLastRoll = fNewRoll;
fLastPitch = fNewPitch;
//更新本次测的时间
nLastTime = nCurTime;

//向串口打印输出Roll角和Pitch角,运行时在Arduino的串口监视器中查看
Serial.print("Roll:");
Serial.print(fNewRoll); Serial.print('(');
Serial.print(fRollRate); Serial.print("),\tPitch:");
Serial.print(fNewPitch); Serial.print('(');
Serial.print(fPitchRate); Serial.print(")\n");
delay(10);
}

//向MPU6050写入一个字节的数据
//指定寄存器地址与一个字节的值
void WriteMPUReg(int nReg, unsigned char nVal) {
Wire.beginTransmission(MPU);
Wire.write(nReg);
Wire.write(nVal);
Wire.endTransmission(true);
}

//从MPU6050读出一个字节的数据
//指定寄存器地址,返回读出的值
unsigned char ReadMPUReg(int nReg) {
Wire.beginTransmission(MPU);
Wire.write(nReg);
Wire.requestFrom(MPU, 1, true);
Wire.endTransmission(true);
return Wire.read();
}

//从MPU6050读出加速度计三个分量、温度和三个角速度计
//保存在指定的数组中
void ReadAccGyr(int *pVals) {
Wire.beginTransmission(MPU);
Wire.write(0x3B);
Wire.requestFrom(MPU, nValCnt * 2, true);
Wire.endTransmission(true);
for (long i = 0; i < nValCnt; ++i) {
pVals[i] = Wire.read() << 8 | Wire.read();
}
}

//对大量读数进行统计,校准平均偏移量
void Calibration()
{
float valSums[7] = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0};
//先求和
for (int i = 0; i < nCalibTimes; ++i) {
int mpuVals[nValCnt];
ReadAccGyr(mpuVals);
for (int j = 0; j < nValCnt; ++j) {
valSums[j] += mpuVals[j];
}
}
//再求平均
for (int i = 0; i < nValCnt; ++i) {
calibData[i] = int(valSums[i] / nCalibTimes);
}
calibData[2] += 16384; //设芯片Z轴竖直向下,设定静态工作点。
}

//算得Roll角。算法见文档。
float GetRoll(float *pRealVals, float fNorm) {
float fNormXZ = sqrt(pRealVals[0] * pRealVals[0] + pRealVals[2] * pRealVals[2]);
float fCos = fNormXZ / fNorm;
return acos(fCos) * fRad2Deg;
}

//算得Pitch角。算法见文档。
float GetPitch(float *pRealVals, float fNorm) {
float fNormYZ = sqrt(pRealVals[1] * pRealVals[1] + pRealVals[2] * pRealVals[2]);
float fCos = fNormYZ / fNorm;
return acos(fCos) * fRad2Deg;
}

//对读数进行纠正,消除偏移,并转换为物理量。公式见文档。
void Rectify(int *pReadout, float *pRealVals) {
for (int i = 0; i < 3; ++i) {
pRealVals[i] = (float)(pReadout[i] - calibData[i]) / 16384.0f;
}
pRealVals[3] = pReadout[3] / 340.0f + 36.53;
for (int i = 4; i < 7; ++i) {
pRealVals[i] = (float)(pReadout[i] - calibData[i]) / 131.0f;
}
}