MPU6050 İvme ve Jiroskop Sensörü
MPU6050, tek bir çipte 3 eksenli ivmeölçer ve 3 eksenli jiroskop barındıran bir hareket sensörüdür. Dengede duran robotlar, drone uçuş kontrolcüleri, hareket algılayan cihazlar ve el hareketiyle kontrol edilen projelerin temelidir.
Toplam 6 eksen veri ürettiği için “6 DOF (Degrees of Freedom)” sensörü olarak anılır.
İvmeölçer ve Jiroskop Farkı
Bu ikisinin ne ölçtüğünü anlamak, sensörün tamamını anlamak demektir.
İvmeölçer (accelerometer)
Doğrusal ivmeyi ölçer — birim: g (yerçekimi ivmesi).
Sensör hareketsiz dururken bile yerçekiminden dolayı 1 g okur. Bu son derece kullanışlıdır: yerçekimi vektörünün hangi eksende olduğuna bakarak sensörün eğimini hesaplayabilirsiniz.
- Avantajı: Uzun vadede kaymaz (drift yapmaz), yerçekimine göre mutlak referansı vardır.
- Dezavantajı: Titreşim ve ani harekete çok duyarlıdır. Robot hareket ederken okuma gürültülüdür.
Jiroskop (gyroscope)
Açısal hızı ölçer — birim: derece/saniye (°/s).
Ne kadar hızlı döndüğünüzü söyler, ama hangi açıda olduğunuzu söylemez. Açıyı bulmak için zaman üzerinden integral almak gerekir:
Açı = Açı_önceki + (AçısalHız × ΔZaman)- Avantajı: Ani hareketlerde çok doğru ve hızlıdır, titreşimden az etkilenir.
- Dezavantajı: İntegral aldığınız için küçük ölçüm hataları birikir. Sensör hareketsiz dururken bile açı yavaşça kayar. Buna drift denir.
Neden ikisi bir arada?
Çünkü zayıflıkları birbirini tamamlar:
| İvmeölçer | Jiroskop | |
|---|---|---|
| Kısa vadede | Gürültülü ✘ | Doğru ✔ |
| Uzun vadede | Doğru ✔ | Kayıyor (drift) ✘ |
İkisini birleştiren bir filtre (tamamlayıcı veya Kalman) hem hızlı hem kararlı bir açı verir.
Teknik Özellikler
| Özellik | Değer |
|---|---|
| Besleme | 3,3 – 5 V (modülde regülatör var) |
| Protokol | I2C |
| I2C adresi | 0x68 (AD0 pini GND) / 0x69 (AD0 pini VCC) |
| İvme aralığı | ±2g, ±4g, ±8g, ±16g |
| Jiroskop aralığı | ±250, ±500, ±1000, ±2000 °/s |
| Çözünürlük | 16 bit |
| Dahili sıcaklık sensörü | Var |
Bağlantı
| MPU6050 | Arduino Uno |
|---|---|
| VCC | 5V (modül üzerinde regülatör varsa) |
| GND | GND |
| SCL | A5 |
| SDA | A4 |
| INT | D2 (isteğe bağlı, kesme için) |
| AD0 | Boş (0x68) veya VCC (0x69) |
ESP32’de SDA genellikle GPIO21, SCL GPIO22’dir. Arduino Mega’da SDA 20, SCL 21’dir.
İki MPU6050’yi aynı I2C hattında kullanmak isterseniz birinin AD0 pinini VCC’ye bağlayın; adresi 0x69 olur.
Kütüphane Kurulumu
En yaygın seçenek MPU6050 kütüphanesidir (Electronic Cats veya jrowberg sürümü). Daha basit bir alternatif olarak Adafruit MPU6050 de kullanılabilir (Adafruit Unified Sensor gerektirir).
Aşağıdaki örnekler kütüphanesiz, doğrudan Wire ile yazılmıştır — böylece hangi register’ın ne yaptığını görürsünüz.
Ham Veri Okuma (Kütüphanesiz)
#include <Wire.h>
const int MPU_ADRES = 0x68;
int16_t axHam, ayHam, azHam;
int16_t gxHam, gyHam, gzHam;
int16_t sicaklikHam;
void setup() {
Serial.begin(9600);
Wire.begin();
// Uyku modundan cikar (PWR_MGMT_1 register = 0x6B)
Wire.beginTransmission(MPU_ADRES);
Wire.write(0x6B);
Wire.write(0);
Wire.endTransmission(true);
Serial.println("MPU6050 hazir.");
}
void loop() {
Wire.beginTransmission(MPU_ADRES);
Wire.write(0x3B); // ACCEL_XOUT_H
Wire.endTransmission(false);
Wire.requestFrom(MPU_ADRES, 14, true);
axHam = Wire.read() << 8 | Wire.read();
ayHam = Wire.read() << 8 | Wire.read();
azHam = Wire.read() << 8 | Wire.read();
sicaklikHam = Wire.read() << 8 | Wire.read();
gxHam = Wire.read() << 8 | Wire.read();
gyHam = Wire.read() << 8 | Wire.read();
gzHam = Wire.read() << 8 | Wire.read();
// Varsayilan olceklerde donusum
float ax = axHam / 16384.0; // g cinsinden (+-2g)
float ay = ayHam / 16384.0;
float az = azHam / 16384.0;
float gx = gxHam / 131.0; // derece/saniye (+-250)
float gy = gyHam / 131.0;
float gz = gzHam / 131.0;
float sicaklik = sicaklikHam / 340.0 + 36.53;
Serial.print("Ivme: ");
Serial.print(ax, 2); Serial.print(", ");
Serial.print(ay, 2); Serial.print(", ");
Serial.print(az, 2);
Serial.print(" | Jiro: ");
Serial.print(gx, 1); Serial.print(", ");
Serial.print(gy, 1); Serial.print(", ");
Serial.print(gz, 1);
Serial.print(" | Sicaklik: ");
Serial.println(sicaklik, 1);
delay(200);
}Bölme sabitleri nereden geliyor?
Sensör 16 bit veri üretir: −32768 ile +32767 arası.
- ±2g aralığında 32768 / 2 = 16384 LSB/g
- ±250 °/s aralığında 32768 / 250 ≈ 131 LSB/(°/s)
Ölçek aralığını değiştirirseniz bu bölenler de değişir.
İvmeölçerden Açı Hesaplama
Yerçekimi vektörünün eksenlere dağılımından eğim açıları bulunur:
#include <math.h>
float rollHesapla(float ax, float ay, float az) {
return atan2(ay, az) * 180.0 / PI;
}
float pitchHesapla(float ax, float ay, float az) {
return atan2(-ax, sqrt(ay * ay + az * az)) * 180.0 / PI;
}Roll, ileri-geri eksende yatma; pitch, öne-arkaya eğilme açısıdır.
Önemli sınır: İvmeölçer yaw (yatay düzlemde dönüş) açısını hesaplayamaz. Çünkü yatay düzlemde dönerken yerçekimi vektörü hiç değişmez. Yaw için manyetometreli bir sensör (MPU9250, HMC5883L) gerekir.
Tamamlayıcı Filtre (Complementary Filter)
İvmeölçerin kararlılığı ile jiroskopun hızını birleştirmenin en pratik yolu budur. Kalman filtresinden çok daha basittir ve çoğu proje için yeterlidir.
#include <Wire.h>
#include <math.h>
const int MPU_ADRES = 0x68;
const float ALFA = 0.96; // jiroskopa verilen agirlik
float aci = 0;
unsigned long oncekiZaman = 0;
void setup() {
Serial.begin(115200);
Wire.begin();
Wire.beginTransmission(MPU_ADRES);
Wire.write(0x6B);
Wire.write(0);
Wire.endTransmission(true);
oncekiZaman = millis();
}
void loop() {
Wire.beginTransmission(MPU_ADRES);
Wire.write(0x3B);
Wire.endTransmission(false);
Wire.requestFrom(MPU_ADRES, 14, true);
int16_t axH = Wire.read() << 8 | Wire.read();
int16_t ayH = Wire.read() << 8 | Wire.read();
int16_t azH = Wire.read() << 8 | Wire.read();
Wire.read(); Wire.read(); // sicaklik atlanir
int16_t gxH = Wire.read() << 8 | Wire.read();
float ax = axH / 16384.0;
float ay = ayH / 16384.0;
float az = azH / 16384.0;
float gx = gxH / 131.0; // derece/saniye
// Gecen sure (saniye)
unsigned long simdi = millis();
float dt = (simdi - oncekiZaman) / 1000.0;
oncekiZaman = simdi;
// Ivmeolcerden mutlak aci
float ivmeAci = atan2(ay, az) * 180.0 / PI;
// Tamamlayici filtre
aci = ALFA * (aci + gx * dt) + (1.0 - ALFA) * ivmeAci;
Serial.print("Aci: ");
Serial.println(aci, 2);
delay(10);
}ALFA katsayısı ne yapar?
- ALFA = 0,96 → açının %96’sı jiroskoptan, %4’ü ivmeölçerden gelir.
- Yüksek ALFA (0,98): Daha hızlı ve yumuşak tepki, ama drift düzeltmesi yavaşlar.
- Düşük ALFA (0,90): Drift daha hızlı düzelir, ama titreşim okumaya daha çok karışır.
Titreşimli bir platformda (motorlu robot) ALFA’yı yükseltin; sabit bir eğim ölçerde düşürün.
Kalibrasyon (Offset Düzeltme)
MPU6050 fabrikadan kalibre çıkmaz. Sensör tamamen düz ve hareketsiz dururken bile jiroskop sıfırdan farklı değerler okur. Bu sabit hataya offset denir ve drift’in ana kaynağıdır.
float gxOffset = 0, gyOffset = 0, gzOffset = 0;
void kalibreEt() {
Serial.println("Sensoru DUZ ve HAREKETSIZ tutun...");
delay(3000);
long tx = 0, ty = 0, tz = 0;
const int ORNEK = 1000;
for (int i = 0; i < ORNEK; i++) {
Wire.beginTransmission(MPU_ADRES);
Wire.write(0x43); // GYRO_XOUT_H
Wire.endTransmission(false);
Wire.requestFrom(MPU_ADRES, 6, true);
tx += (int16_t)(Wire.read() << 8 | Wire.read());
ty += (int16_t)(Wire.read() << 8 | Wire.read());
tz += (int16_t)(Wire.read() << 8 | Wire.read());
delay(2);
}
gxOffset = (tx / (float)ORNEK) / 131.0;
gyOffset = (ty / (float)ORNEK) / 131.0;
gzOffset = (tz / (float)ORNEK) / 131.0;
Serial.print("Offsetler: ");
Serial.print(gxOffset, 3); Serial.print(", ");
Serial.print(gyOffset, 3); Serial.print(", ");
Serial.println(gzOffset, 3);
}Bulduğunuz offset değerlerini her okumadan çıkarın. Kalibrasyonu sensör son montaj konumunda ve çalışma sıcaklığına yakınken yapın; sıcaklık değiştikçe offset de kayar.
Sık Karşılaşılan Sorunlar
| Belirti | Neden |
|---|---|
| Tüm okumalar 0 veya −1 | Sensör uyku modundan çıkarılmamış (0x6B register’ına 0 yazılmamış) |
| I2C’de cihaz görünmüyor | SDA/SCL ters, besleme yok, ortak GND yok |
| Açı sürekli kayıyor | Jiroskop kalibre edilmemiş — offset çıkarın |
| Okumalar aşırı gürültülü | Titreşim; sensörü sünger/silikon üzerine monte edin |
| Yaw açısı anlamsız | MPU6050 yaw ölçemez, manyetometre gerekir |
| Robot hareket edince açı bozuluyor | İvmeölçere fazla ağırlık verilmiş; ALFA’yı yükseltin |
Alternatif Sensörler
| Sensör | Eksen | Not |
|---|---|---|
| MPU6050 | 6 (ivme + jiro) | Ucuz, yaygın, standart |
| MPU9250 | 9 (+ manyetometre) | Yaw ölçebilir |
| BNO055 | 9 + dahili füzyon | Açıyı kendisi hesaplar, filtre yazmanız gerekmez |
| ICM-20948 | 9 | MPU9250’nin güncel halefi |
| ADXL345 | 3 (sadece ivme) | Basit eğim ve darbe algılama |
Filtreyle uğraşmak istemiyorsanız BNO055 doğrudan roll/pitch/yaw açılarını verir; karşılığında belirgin şekilde pahalıdır.
Özet
MPU6050, tek çipte 3 eksen ivmeölçer ve 3 eksen jiroskop barındıran bir I2C hareket sensörüdür. İvmeölçer uzun vadede doğru ama gürültülü, jiroskop kısa vadede doğru ama kayıcıdır.
Bu derste şunları öğrendik:
- İvmeölçer ile jiroskop arasındaki temel farkı
- Drift kavramını ve neden oluştuğunu
- Ham veriyi
Wireile okumayı ve 16384 / 131 bölenlerinin nereden geldiğini atan2()ile eğim açısı hesaplamayı- Tamamlayıcı filtre ve ALFA katsayısını
- Jiroskop offset kalibrasyonunu
- MPU6050’nin yaw ölçemediğini ve alternatiflerini