Arduino ile Gyroscope ve Accelerometer (MPU-6050)

watch_later 2/14/2016
Accelerometer (İvme Ölçer)
İvme ölçer üzerine düşen statik(yerçekimi) veya dinamik (aniden hızlanma veya durma) ivmeyi ölçmektedirler. Sensörden aldığımız değer m/s2 veya yer çekimi türünden ifade edilebilir. Eğer uzayda veya herhangi bir çekim alanının kapsamında değilse sensör üzerine 1g’lik bir yerçekimi kuvveti etki etmektedir. Bu kuvvet de yaklaşık olarak 9.8 m/s2 'dir. Sensör sürekli olarak yer çekimi etkisi altında kaldığından eğim ölçer yani akıllı cep telefonlarında telefon dikey veya yatay konuma getirdiğinde telefonun ekranı hareke göre değişmektedir veya hareket algılar yani Nintendo wii gibi ürünlerde wii remote sallandığında oynanan oyundaki karakterlerde benzer hareketi yapar. Ölçü aralığı olarak ± 1g, ± 2g, ± 4g vb. değerler ile ifade edilmektedir ve bir, iki ve üç eksende ölçüm yapabilen türevleri vardır.


arduino

Gyroscope
Tekerleğin etrafındaki çembere dik açıyla bağlanmış başka bir çember ve bağlantılı bu çemberlere dik açıyla tutturulmuş başka bir çember gyroscope’u modeller. Gyroscope’un önemli iki özelliği vardır. İlk özelliği yatay eksende dönmekte olan bir gyroscope’a yatay eksen doğrultusunda bir kuvvet uyguladığında yatay eksen etrafında dönmek yerine eksen etrafında dönmeye başlar, diğer özelliği ise gyroscope’un dönmeye başladığı eksenin jiroskobun durduğu yüzey ne açıyla oynatılırsa oynatılsın jiroskobun dönüş ekseni sabit kalır. Bu özelliğinden dolayı uyduların sürekli olarak dünyaya dönük kalması, uçaklarda ve çeşitli araçlarda yapay ufuk oluşturulması ve otopilot gibi uygulamalarda kullanılmaktadır. 
Gyroscope Çalışma Simülasyonu

IMU (Inertial Measurement Unit)
Gyroscope ve accelerometer tek başına sağlıklı bir bilgi veremez. Bunun için bu iki sensörü birleştirerek yönelim, hız, pozisyon bilgileri tek bir birimden alınır. İşte oluşturulan bu birime IMU (Inertial Measurement Unit) denilir. Serbestlik derecesi DOF (Degrees of Freedom) ile ifade edilir(2 eksen gyroscope ve 3 eksen ivme ölçer, 5 DOF IMU’yu ifade eder).

Gyroscope ve accelerometer kayma(bias drift) yaparlar ve bundan için hassas açı ölçümünde tek başlarına kullanılamaz. Ayrıca accelerometer’lar kuvvete karşı çok duyarlı olduğundan en ufak titreşimlerde çok yüksek gürültüler oluşturmaktadır. Filtreleme için çeşitli algoritmalar kullanılmaktadır. En yaygın olarak kullanılanlarından birisi Kalman filtresidir.

MPU-6050 Yongası
MPU-6050 hava araçlarında, denge robotlarında ve daha birçok alanda kullanılan sensörlerdendir. Üzerinde 3 eksenli gyroscope ve 3 eksen açısal ivme ölçer bulunan IMU yongasıdır. Bu nedenle MPU-6050 X,Y ve Z kanallarını aynı anda yakalayabilmektedir.  MPU-6050 I2C protokolünü desteklediğinden üzerindeki pinler standart I2C pinleridir. Üzerinde voltaj regülatörü bulunduğundan 3V-5V arası gerilimle beslenebilmektedir. Açısal ivme ölçerin aralığı ± 2g, ± 4g, ± 8g ve ± 16g, gyroscope’un ölçüm aralığı ise 250°/s, 500°/s, 1000°/s ve 2000°/s ‘dir.


Gyroscope Bb-8 Robot Simülasyonu
İvme ölçer ve gyroscope için gerekli ham değerleri okumak kolaydır. Sensör 1024 byte’lık FIFO tampon içermektedir. Sensör değerlerini FIFO tampon içine yerleştirilir ve bu tampon Arduino tarafından okunur. FIFO tampon kesme sinyali ile birlikte kullanılır. Eğer MPU-6050 içindeki FIFO tampon bölgesinde veri varsa, bu kesme sinyali ile Arduino, MPU-6050’deki FIFO tampon bölgesinde okunmak için bir veri olduğunu anlar.
MPU-6050 her zaman I2C protokolüne göre bağlı SDA ve SCL pinleri ile Arduino’ya slave cihaz olur. Ayrıca  kendi içinde I2C'yi kontrol etmek için diğer bir master I2C'ye sahiptir. Sensör DMP (Digital Motion Processor)(Dijital Hareket İşlemci)’ye sahiptir ve bu DMP ile karmaşık hesaplamaları hızlı yapmak mümkündür (DMP firmware ile programlanabilir). Bu işlem Arduino’nun üzerindeki iş gücünü azaltır.

Processing'de Oluşturulmuş Horizon ve Compass Simülasyonu

Arduino Sketch:
/*
 * gerekli kütüphaneler dahil edildi.
 */
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
#include "Wire.h"

MPU6050 mpu;
#define LED_PIN 13
bool ledState = false;

// MPU-6050 kontrol değişkenleri
bool dmpReady = false;  // DMP init düzgün çalışması durumunda true yapılır.
uint8_t mpuIntStatus;   // MPU kesme durumu
uint8_t devStatus;      /*Her işlemden sonra durum değeri 
                          geri verilir. (0 = başarılı, 1 = hatalı)*/
uint16_t packetSize;    // DMP paket boyutu (varsayılan 42 bytes)
uint16_t fifoCount;     // FIFO sayısı
uint8_t fifoBuffer[64]; // FIFO tamponu

Quaternion q;           // [w, x, y, z]
VectorInt16 aa;         // [x, y, z]
VectorInt16 aaReal;     // [x, y, z]
VectorInt16 aaWorld;    // [x, y, z]
VectorFloat gravity;    // [x, y, z]
float euler[3];         // [psi, theta, phi]
float ypr[3];           // [yaw, pitch, roll]
uint8_t teapotPacket[14] = { '$', 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x00, '\r', '\n' };

volatile bool mpuInterrupt = false;
void dmpDataReady() {
  mpuInterrupt = true;
}

void setup() {
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
  Wire.begin();
  TWBR = 24; // 400kHz I2C clock (200kHz CPU 8MHz)
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
  Fastwire::setup(400, true);
#endif

  // serial port aktif hale getirilir.
  // (115200 seçildi çünkü prossesing ile aynı baud hızı 
  // ile iletişime geçmek için.
  Serial.begin(115200);
  while (!Serial); // bekle
  mpu.initialize();
  devStatus = mpu.dmpInitialize();

  // gyroscope uzaklık offset değerleri
  mpu.setXGyroOffset(220);
  mpu.setYGyroOffset(76);
  mpu.setZGyroOffset(-85);
  mpu.setZAccelOffset(1788); // 1688 varsayılan değeridir.

  if (devStatus == 0) {
    mpu.setDMPEnabled(true);
    // Arduino interrupt kontrol sistemi aktif edildi.
    attachInterrupt(0, dmpDataReady, RISING);
    mpuIntStatus = mpu.getIntStatus();
    // loop() döngüsüne girmek için DMP hazır bayrağı ayarlanır.
    dmpReady = true;
    // sonrasında karşılaştırma için beklenen DMP paket boyutu alınır.
    packetSize = mpu.dmpGetFIFOPacketSize();
  } else {
    Serial.print(devStatus);
    Serial.println(F(")"));
  }
  // LED'e çıkış ver.
  pinMode(LED_PIN, OUTPUT);
}

void loop() {
  // başarısızlık durumunda birşey yapma
  if (!dmpReady) return;
  // MPU kesme veya ekstra paketleri bekle
  while (!mpuInterrupt && fifoCount < packetSize) {
  }
  // kesme bayrağı sıfırlanır ve INT_STATUS byte değerinde
  mpuInterrupt = false;
  mpuIntStatus = mpu.getIntStatus();
  // Geçerli FIFO değeri alınır.
  fifoCount = mpu.getFIFOCount();
  // overflow kontrolü
  if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
    // resetle
    mpu.resetFIFO();
    // DMP hazır veriler için kesme kontrolü
  } else if (mpuIntStatus & 0x02) {
    // doğru veri uzunluğu için bekle
    while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();
    // FIFO paketini okuma
    mpu.getFIFOBytes(fifoBuffer, packetSize);
    fifoCount -= packetSize;

#ifdef QUATERNION_CIKTI
    mpu.dmpGetQuaternion(&q, fifoBuffer);
    Serial.print("quat\t");
    Serial.print(q.w);
    Serial.print("\t");
    Serial.print(q.x);
    Serial.print("\t");
    Serial.print(q.y);
    Serial.print("\t");
    Serial.println(q.z);
#endif

#ifdef EULER_CIKTI
    // Euler derece açıları
    mpu.dmpGetQuaternion(&q, fifoBuffer);
    mpu.dmpGetEuler(euler, &q);
    Serial.print("euler\t");
    Serial.print(euler[0] * 180 / M_PI);
    Serial.print("\t");
    Serial.print(euler[1] * 180 / M_PI);
    Serial.print("\t");
    Serial.println(euler[2] * 180 / M_PI);
#endif

#ifdef YAWPITCHROLL_CIKTI
    // Euler derece açıları
    mpu.dmpGetQuaternion(&q, fifoBuffer);
    mpu.dmpGetGravity(&gravity, &q);
    mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
    Serial.print("Phi: ");
    Serial.print(ypr[2] * 18 / M_PI);
    Serial.print("\t theta: ");
    Serial.print(" ");
    Serial.print(ypr[1] * 180 / M_PI);
    Serial.print("\t Psi: ");
    Serial.print(" ");
    Serial.println(ypr[0] * 180 / M_PI);
#endif

#ifdef REALACCEL_CIKTI
    // yerçekimi kaldırmak için gerçek ivme çıktıları
    mpu.dmpGetQuaternion(&q, fifoBuffer);
    mpu.dmpGetAccel(&aa, fifoBuffer);
    mpu.dmpGetGravity(&gravity, &q);
    mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity);
    Serial.print("areal\t");
    Serial.print(aaReal.x);
    Serial.print("\t");
    Serial.print(aaReal.y);
    Serial.print("\t");
    Serial.println(aaReal.z);
#endif

#ifdef WORLDACCEL_CIKTI
    // yerçekimi kaldırmak için ayarlanmış dünya çerçeve hızlanma çıktıları
    mpu.dmpGetQuaternion(&q, fifoBuffer);
    mpu.dmpGetAccel(&aa, fifoBuffer);
    mpu.dmpGetGravity(&gravity, &q);
    mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity);
    mpu.dmpGetLinearAccelInWorld(&aaWorld, &aaReal, &q);
    Serial.print("aworld\t");
    Serial.print(aaWorld.x);
    Serial.print("\t");
    Serial.print(aaWorld.y);
    Serial.print("\t");
    Serial.println(aaWorld.z);
#endif

#ifdef TEAPOT_CIKTI
    teapotPacket[2] = fifoBuffer[0];
    teapotPacket[3] = fifoBuffer[1];
    teapotPacket[4] = fifoBuffer[4];
    teapotPacket[5] = fifoBuffer[5];
    teapotPacket[6] = fifoBuffer[8];
    teapotPacket[7] = fifoBuffer[9];
    teapotPacket[8] = fifoBuffer[12];
    teapotPacket[9] = fifoBuffer[13];
    Serial.write(teapotPacket, 14);
    teapotPacket[11]++; // paket sayısı, loops 0xFF sonlanma değeri
#endif
    ledState = !ledState;
    digitalWrite(LED_PIN, ledState);
  }
}
MPU-6050 Fritzing Şeması
 Processing Sketch Download


     
Bir sonraki yazımda görüşmek üzere...



sentiment_satisfied Emoticon