Arduino與MPU-6050的通信
為避免糾纏于電路細(xì)節(jié),我們直接使用集成的MPU6050模塊。MPU6050的數(shù)據(jù)接口用的是I2C總線協(xié)議,因此我們需要Wire程序庫(kù)的幫助來(lái)實(shí)現(xiàn)Arduino與MPU6050之間的通信。請(qǐng)先確認(rèn)你的Arduino編程環(huán)境中已安裝Wire庫(kù)。
Wire庫(kù)的官方文檔(http://www.arduino.cc/en/Reference/Wire)中指出:在UNO板子上,SDA接口對(duì)應(yīng)的是A4引腳,SCL對(duì)應(yīng)的是A5引腳。MPU6050需要5V的電源,可由UNO板直接供電。按照下圖連線。
?。ㄗ仙€是中斷線,這里用不到,可以不接)
MPU6050的數(shù)據(jù)寫入和讀出均通過(guò)其芯片內(nèi)部的寄存器實(shí)現(xiàn),這些寄存器的地址都是1個(gè)字節(jié),也就是8位的尋址空間,其寄存器的詳細(xì)列表說(shuō)明書請(qǐng)點(diǎn)擊下載:https://www.olimex.com/Products/Modules/Sensors/MOD-MPU6050/resources/RM-MPU-60xxA_rev_4.pdf
1.1 將數(shù)據(jù)寫入MPU-6050
在每次向器件寫入數(shù)據(jù)前要先打開Wire的傳輸模式,并指定器件的總線地址,MPU6050的總線地址是0x68(AD0引腳為高電平時(shí)地址為0x69)。然后寫入一個(gè)字節(jié)的寄存器起始地址,再寫入任意長(zhǎng)度的數(shù)據(jù)。這些數(shù)據(jù)將被連續(xù)地寫入到指定的起始地址中,超過(guò)當(dāng)前寄存器長(zhǎng)度的將寫入到后面地址的寄存器中。寫入完成后關(guān)閉Wire的傳輸模式。下面的示例代碼是向MPU6050的0x6B寄存器寫入一個(gè)字節(jié)0。
Wire.beginTransmission(0x68); //開啟MPU6050的傳輸
Wire.write(0x6B); //指定寄存器地址
Wire.write(0); //寫入一個(gè)字節(jié)的數(shù)據(jù)
Wire.endTransmission(true); //結(jié)束傳輸,true表示釋放總線
1.2 從MPU-6050讀出數(shù)據(jù)
讀出和寫入一樣,要先打開Wire的傳輸模式,然后寫一個(gè)字節(jié)的寄存器起始地址。接下來(lái)將指定地址的數(shù)據(jù)讀到Wire庫(kù)的緩存中,并關(guān)閉傳輸模式。最后從緩存中讀取數(shù)據(jù)。下面的示例代碼是從MPU6050的0x3B寄存器開始讀取2個(gè)字節(jié)的數(shù)據(jù):
Wire.beginTransmission(0x68); //開啟MPU6050的傳輸
Wire.write(0x3B); //指定寄存器地址
Wire.requestFrom(0x68, 2, true); //將輸據(jù)讀出到緩存
Wire.endTransmission(true); //關(guān)閉傳輸模式
int val = Wire.read() 《《 8 | Wire.read(); //兩個(gè)字節(jié)組成一個(gè)16位整數(shù)
1.3 具體實(shí)現(xiàn)
通常應(yīng)當(dāng)在setup函數(shù)中對(duì)Wire庫(kù)進(jìn)行初始化:
Wire.begin();
在對(duì)MPU6050進(jìn)行各項(xiàng)操作前,必須啟動(dòng)該器件,向它的0x6B寫入一個(gè)字節(jié)0即可啟動(dòng)。通常也是在setup函數(shù)完成,代碼見1.1節(jié)。
MPU6050芯片提供的數(shù)據(jù)夾雜有較嚴(yán)重的噪音,在芯片處理靜止?fàn)顟B(tài)時(shí)數(shù)據(jù)擺動(dòng)都可能超過(guò)2%。除了噪音,各項(xiàng)數(shù)據(jù)還會(huì)有偏移的現(xiàn)象,也就是說(shuō)數(shù)據(jù)并不是圍繞靜止工作點(diǎn)擺動(dòng),因此要先對(duì)數(shù)據(jù)偏移進(jìn)行校準(zhǔn) ,再通過(guò)濾波算法消除噪音。
對(duì)于夾雜了大量噪音的數(shù)據(jù),卡爾曼濾波器的效果無(wú)疑是最好的。如果不想考慮算法細(xì)節(jié),可以直接使用Arduino的Klaman Filter庫(kù)完成。在我們的模型中,一個(gè)卡爾曼濾波器接受一個(gè)軸上的角度值、角速度值以及時(shí)間增量,估計(jì)出一個(gè)消除噪音的角度值。跟據(jù)當(dāng)前的角度值和上一輪估計(jì)的角度值,以及這兩輪估計(jì)的間隔時(shí)間,我們還可以反推出消除噪音的角速度。
arduino讀取MPU6050數(shù)據(jù)源代碼參考:
#include “Wire.h” //包含頭文件
#define SMPLRT_DIV 0x19 //陀螺儀采樣率,典型值:0x07(125Hz)
#define CONFIG 0x1A //低通濾波頻率,典型值:0x06(5Hz)
#define GYRO_CONFIG 0x1B //陀螺儀自檢及測(cè)量范圍,典型值:0x18(不自檢,2000deg/s)
#define ACCEL_CONFIG 0x1C //加速計(jì)自檢、測(cè)量范圍及高通濾波頻率,典型值:0x01(不自檢,2G,5Hz)
#define ACCEL_XOUT_H 0x3B //定義加速度x軸的高八位[15:8]
#define ACCEL_XOUT_L 0x3C //定義加速度x軸的低八位[7:0]
#define ACCEL_YOUT_H 0x3D //定義加速度y軸的高八位[15:8]
#define ACCEL_YOUT_L 0x3E //定義加速度y軸的低八位[7:0]
#define ACCEL_ZOUT_H 0x3F //定義加速度z軸的高八位[15:8]
#define ACCEL_ZOUT_L 0x40 //定義加速度z軸的低八位[7:0]
#define TEMP_OUT_H 0x41
#define TEMP_OUT_L 0x42
#define GYRO_XOUT_H 0x43 //定義加速度x軸的高八位[15:8]
#define GYRO_XOUT_L 0x44 //定義加速度x軸的低八位[7:0]
#define GYRO_YOUT_H 0x45 //定義加速度y軸的高八位[15:8]
#define GYRO_YOUT_L 0x46 //定義加速度y軸的低八位[7:0]
#define GYRO_ZOUT_H 0x47 //定義加速度z軸的高八位[15:8]
#define GYRO_ZOUT_L 0x48 //定義加速度z軸的低八位[7:0]
#define PWR_MGMT_1 0x6B //電源管理,典型值:0x00(正常啟用)
#define WHO_AM_I 0x75 //IIC地址寄存器(默認(rèn)數(shù)值0x68,只讀)
#define SlaveAddress 0xD0 //IIC寫入時(shí)的地址字節(jié)數(shù)據(jù),+1為讀取
int ax0,ax1,axout; //定義加速度傳感器從寄存器地址獲取的高八位低八位數(shù)據(jù)以及輸出的模擬量
int ay0,ay1,ayout;
int az0,az1,azout;
int gx0,gx1,gxout; //定義陀螺儀傳感器從寄存器地址獲取的高八位低八位數(shù)據(jù)以及輸出的模擬量
int gy0,gy1,gyout;
int gz0,gz1,gzout;
double Xa,Ya,Za;
double Xg,Yg,Zg;
//float aax,aay;
//float pi=3.1415926;
int MPU6050address = 0x68;//MPU6050的地址
void setup()
{
Wire.begin(); //設(shè)置I2通信時(shí)的本機(jī)地址
Serial.begin(9600);
Wire.beginTransmission(MPU6050address);//啟動(dòng)I2通信,讀取MPU6050地址
// Wire.write(GYRO_CONFIG);//從傳感器寫入數(shù)據(jù)
// Wire.write(ACCEL_CONFIG);
Wire.write(8); //寫入8位字節(jié)
Wire.endTransmission();//結(jié)束通信
}
void loop()
{
Wire.beginTransmission(MPU6050address);
Wire.write(ACCEL_XOUT_H);//寫加速度計(jì)x軸數(shù)據(jù)
Wire.write(ACCEL_XOUT_L);
Wire.write(GYRO_XOUT_H);//寫陀螺儀計(jì)x軸數(shù)據(jù)
Wire.write(GYRO_XOUT_L);
Wire.endTransmission();
Wire.requestFrom(MPU6050address,2);
if(Wire.available()《=2);//用于返回接受的字節(jié)數(shù)
{
ax0 = Wire.read();
ax1 = Wire.read();
ax1 = ax1《《8;
axout = ax0+ax1;
gx0 = Wire.read();
gx1 = Wire.read();
gx1 = gx1《《8;
gxout = gx0+gx1;
}
Wire.beginTransmission(MPU6050address);
Wire.write(ACCEL_YOUT_H);
Wire.write(ACCEL_YOUT_L);
Wire.write(GYRO_YOUT_
H);
Wire.write(GYRO_YOUT_L);
Wire.endTransmission();
Wire.requestFrom(MPU6050address,2);
if(Wire.available()《=2);
{
ay0 = Wire.read();
ay1 = Wire.read();
ay1 = ay1《《8;
ayout = ay0+ay1;
gy0 = Wire.read();
gy1 = Wire.read();
gy1 = gy1《《8;
gyout = gy0+gy1;
}
Wire.beginTransmission(MPU6050address);
Wire.write(ACCEL_ZOUT_H);
Wire.write(ACCEL_ZOUT_L);
Wire.write(GYRO_ZOUT_H);
Wire.write(GYRO_ZOUT_L);
Wire.endTransmission();
Wire.requestFrom(MPU6050address,2);
if(Wire.available()《=2);
{
az0 = Wire.read();
az1 = Wire.read();
az1 = ax1《《8;
azout = az0+az1;
gz0 = Wire.read();
gz1 = Wire.read();
gz1 = gz1《《8;
gzout = gz0+gz1;
}
Xa=axout/256.00; //把輸出結(jié)果轉(zhuǎn)換為重力加速度g,精確到小數(shù)點(diǎn)后2位
Ya=ayout/256.00;
Za=azout/256.00;
Xg=gxout/256.00;
Yg=gyout/256.00;
Zg=gzout/256.00;
// aax = atan(Xa/Za) * (-180) / pi; //想轉(zhuǎn)化為角度的,可是感覺到有點(diǎn)不對(duì),就沒寫進(jìn)去
// aay = atan(Ya/Xa) * (-180) / pi;
Serial.println(“Xa:”);//輸出6050不同軸采集到的數(shù)據(jù)
Serial.print(Xa,DEC);
Serial.println(‘\n’);
Serial.println(“Ya:”);
Serial.print(Ya,DEC);
Serial.println(‘\n’);
Serial.println(“Za:”);
Serial.print(Za,DEC);
Serial.println(‘\n’);
delay(500);
Serial.println(“Xg:”);
Serial.print(Xg,DEC);
Serial.println(‘\n’);
Serial.println(“Yg:”);
Serial.print(Yg,DEC);
Serial.println(‘\n’);
Serial.println(“Zg:”);
Serial.print(Zg,DEC);
Serial.println(‘\n’);
delay(500);
}
評(píng)論
查看更多