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.
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.
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ı |