Člověk se umí skvěle orientovat v prostoru, ale umí to také Arduino? Neumí, ale my mu k tomu dopomůžeme. Tedy spíše gyroskop a akcelerometr v jednom, a to hned 3osý! V našem případě se bude jednat o elektronický modul senzor GY-521, který disponuje MPU-6050. Tyto moduly najdou v dnešní době využití především v kvadrokoptérách nebo dronech. Dnešní článek bude celý o využití akcelerometru a gyra GY-521 s čipem MPU6050.
Zapojení GY-521
Modul GY-521 vyžaduje 3,3V, avšak se zde nachází regulátor, který nám umožnuje připojit 5V, tím je vyřešené napájení. Komunikace probíhá pomocí I2C sběrnice tzn. Potřebujeme pull-up rezistor. Ten je však už také zabudovaný v modulu. Ostatní piny slouží pro různé buzení, nebo přerušení signálu, hraní si s FIFO bufferem atd. Tím se my zabývat nebudeme, zapojte tedy modul podle schématu.
Program pro GY-521
Pro správnou funkčnost musíme importovat 2 knihovny, které jsou ke stažení na konci článku. Pokud nevíte, jak správně importovat knihovnu, podívejte se na návod, kde se věnujeme knihovnám na stránce Jak správně naimportovat knihovnu?. Knihovna nabízí nepřeberné množství nových funkcí, my si ukážeme jen ty základní.
První program, bude mít za úkol otestovat zařízení (připojení a komunikaci) a poté vypisovat všech 6 naměřených hodnot po seriál monitoru.
// importování knihoven #include "I2Cdev.h" #include "MPU6050.h" // knihovna "Wire" je vyžadována, pokud je knhovna I2Cdev obsažena v programu #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE #include "Wire.h" #endif MPU6050 accelgyro; // vytvoříme proměné int16_t ax, ay, az; int16_t gx, gy, gz; // pokud chceme, aby hodnoty byly v tabulce, napíšeme: #define OUTPUT_READABLE_ACCELGYRO void setup() { // přopojíme se na I2C sběrnici, knihovna to sama neumí #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE Wire.begin(); #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE Fastwire::setup(400, true); #endif // zvolíme rychlsot sériové komunikace Serial.begin(9600); // inicializujem (sputíme) zařízení Serial.println("Spousteni zarizeni MPU6050"); accelgyro.initialize(); // oveříme funkčnost zařízení Serial.println("Testovani zarizeni"); Serial.println(accelgyro.testConnection() ? "Zarizeni funguje spravne" : "Zarizeni se nepodarilo pripojit"); } void loop() { // přečteme neupravená data ze senzoru accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz); // jelikož jsme si na 17. řádku nastavili "READABLE" #ifdef OUTPUT_READABLE_ACCELGYRO // vypíšeme hodnoty po sériové lince // "/t" zalamuje řádek Serial.print("a|g:\t"); Serial.print(ax); Serial.print("\t"); Serial.print(ay); Serial.print("\t"); Serial.print(az); Serial.print("\t"); Serial.print(gx); Serial.print("\t"); Serial.print(gy); Serial.print("\t"); Serial.println(gz); #endif }
Pokud vše funguje, jak má, uvidíme po zapnutí seriál monitoru toto:
Zdá se vám, že čidlo v klidu měří různé hodnoty? Ano, máte pravdu. Čidlo je neskutečně citlivé a pouhé bouchnutí do stolu (vibrace) změní výsledek měření. Pokud by jste chtěli o tomtu problému vědět více, navštivte stránku playground.arduino.cc/Main/MPU-6050, kde se těmto problémům věnují dopodrobna. My si alespoň ukážeme, jak čidlo kalibrovat.
Kompenzace odchylek/kalibrování GY-521
Kalibrace samozřejmě probíhá pomocí programu. Program otestuje funkčnost zařízení, poté vypíše nastavené odchylky. Pak přijde část, kde odchylky změníme a znova si je necháme vypsat. Aby jste v seriál monitoru viděli změnu, zapněte ho ještě před náhráním programu. Pokud by jste program nahráli a až pak otevřeli seriál monitor (program by proběhl 2x), tak už byste změnu neviděli.
#include "I2Cdev.h" #include "MPU6050.h" #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE #include "Wire.h" #endif MPU6050 accelgyro; int16_t ax, ay, az; int16_t gx, gy, gz; #define OUTPUT_READABLE_ACCELGYRO void setup() { #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE Wire.begin(); #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE Fastwire::setup(400, true); #endif Serial.begin(9600); Serial.println("Spousteni zarizeni MPU6050"); accelgyro.initialize(); Serial.println("Testovani zarizeni"); Serial.println(accelgyro.testConnection() ? "Zarizeni funguje spravne" : "Zarizeni se nepodarilo pripojit"); // vypíšeme si současnou hodnotu odchylek Serial.println("Uprava/kompenzovani odchylek zarizeni"); Serial.print(accelgyro.getXAccelOffset()); Serial.print("\t"); Serial.print(accelgyro.getYAccelOffset()); Serial.print("\t"); Serial.print(accelgyro.getZAccelOffset()); Serial.print("\t"); Serial.print(accelgyro.getXGyroOffset()); Serial.print("\t"); Serial.print(accelgyro.getYGyroOffset()); Serial.print("\t"); Serial.print(accelgyro.getZGyroOffset()); Serial.print("\t"); Serial.print("\n"); // změníme hodnotu odchylek accelgyro.setXAccelOffset(20); accelgyro.setYAccelOffset(30); accelgyro.setZAccelOffset(40); accelgyro.setXGyroOffset(-20); accelgyro.setYGyroOffset(-40); accelgyro.setZGyroOffset(-60); // znova vypíšeme hodnotu odchylek Serial.print(accelgyro.getXAccelOffset()); Serial.print("\t"); Serial.print(accelgyro.getYAccelOffset()); Serial.print("\t"); Serial.print(accelgyro.getZAccelOffset()); Serial.print("\t"); Serial.print(accelgyro.getXGyroOffset()); Serial.print("\t"); Serial.print(accelgyro.getYGyroOffset()); Serial.print("\t"); Serial.print(accelgyro.getZGyroOffset()); Serial.print("\t"); Serial.print("\n"); } void loop() { // sem nic nepíšeme }
Pokud vše uděláte správně, uvidíte toto:
Vlastní vodováha pomocí MPU-6050
Tak jsme dostali ze senzoru tzv. „raw data“, které se sice nějak logicky mění, ale nic nám neříkají, proto si je převedeme na stupně, které už budou mít daleko větší vypovídací hodnotu. Můžete použít jinou knihovnu, která to udělá za vás. Jelikož my už nějakou knihovnu máme, tak si vystačíme s ní. Na internetu si najdeme vzorečky, které právě umí z naměřených hodnot vypočítat sklon v ose x a v ose y. Tyto vzorečky mají tvar:
Pokud máte strach z radiánů, tak výsledek ještě podělte (π/180) – výsledek tedy bude ve °. Program bude pak vypadat takle:
#include "Wire.h" #include "Math.h" #include "I2Cdev.h" #include "MPU6050.h" MPU6050 accelgyro; const float pi = 3.141592; const int counter_w = 100; int16_t ax, ay, az; float x, y, z; int counter; float angle_x, angle_y, angle_z, _angle_x, _angle_y, _angle_z; long ax_p, ay_p, az_p; void setup() { Wire.begin(); Serial.begin(9600); Serial.println("Spousteni zarizeni MPU6050"); accelgyro.initialize(); Serial.println("Testovani zarizeni"); Serial.println(accelgyro.testConnection() ? "Zarizeni funguje spravne" : "Zarizeni se nepodarilo pripojit"); } void loop() { // zjistí všechny hodnoty z akcelerometru accelgyro.getAcceleration(&ax, &ay, &az); // sčítáme potřebný počet hodnot ax_p = ax_p + ax; ay_p = ay_p + ay; az_p = az_p + az; // pocitadlo mereni counter++; // az bude mereni counter_w (100), tak: if (counter == counter_w) { //zjistíme průmerné hodnoty x = ax_p/counter; y = ay_p/counter; z = az_p/counter; // vypočteme sklon a náklon [°] angle_x = atan2(x, sqrt(square(y) + square(z)))/(pi/180); angle_y = atan2(y, sqrt(square(x) + square(z)))/(pi/180); angle_z = atan2(z, sqrt(square(x) + square(y)))/(pi/180); // vynulujeme hodnoty counter = 0; ax_p = 0; ay_p = 0; az_p = 0; // hodnoty si vypíšme do portu Serial.print(angle_x); Serial.print("\t"); Serial.print(angle_y); Serial.print("\t"); Serial.println(angle_z); } }
MPU-6050 s výstupem na i2c LED displej
Ne každý se spokojí s výstupem do Seriál monitoru, proto si připojíme také LCD displej. Použijeme LCD s I2C převodníkem, který můžeme klidně zapojit do stejných I2C pinů, protože LCD displej má definovanou adresu.
#include "Wire.h" #include "Math.h" #include "I2Cdev.h" #include "MPU6050.h" // přidáme knihovnu pro display #include "LiquidCrystal_I2C.h" MPU6050 accelgyro; // definujeme LCD displej LiquidCrystal_I2C lcd(0x27, 16, 2); const float pi = 3.141592; const int counter_w = 100; int16_t ax, ay, az; float x, y, z; int counter; float angle_x, angle_y, angle_z, _angle_x, _angle_y, _angle_z; long ax_p, ay_p, az_p; void setup() { Wire.begin(); Serial.begin(9600); // spustíme LCD display lcd.begin(); lcd.backlight(); Serial.println("Spousteni zarizeni MPU6050"); accelgyro.initialize(); Serial.println("Testovani zarizeni"); Serial.println(accelgyro.testConnection() ? "Zarizeni funguje spravne" : "Zarizeni se nepodarilo pripojit"); } void loop() { accelgyro.getAcceleration(&ax, &ay, &az); ax_p = ax_p + ax; ay_p = ay_p + ay; az_p = az_p + az; counter++; if (counter == counter_w) { x = ax_p/counter; y = ay_p/counter; z = az_p/counter; angle_x = atan2(x, sqrt(square(y) + square(z)))/(pi/180); angle_y = atan2(y, sqrt(square(x) + square(z)))/(pi/180); angle_z = atan2(z, sqrt(square(x) + square(y)))/(pi/180); counter = 0; ax_p = 0; ay_p = 0; az_p = 0; // před novým výpisem, ten starý smažeme lcd.clear(); // zobrazíme nový výpis lcd.print(angle_x); lcd.print(" "); lcd.print(angle_y); lcd.print(" "); lcd.print(angle_z); delay(500); } }
jednoduchý alarm s mpu-6050
Plán je jednoduchý - zjistíme jaké hodnoty vypisuje senzor v klidovém stavu, to budou naše referenční hodnoty, ke kterým budeme porovnávat naměřené hodnoty. Pokud se budou lišit o naši toleranci (5°), tak se alarm spustí. Do programu tedy přidáme proměnné referenčních hodnot a toleranci, pak už jen díky jednoduchým podmínkám uděláme zbytek. Pokud tedy někdo zvedne, nakloní, nebo otočí předmět, ve kterém je MPU-6050, tak se sepne alarm.
#include "Wire.h" #include "Math.h" #include "I2Cdev.h" #include "MPU6050.h" MPU6050 accelgyro; const float pi = 3.141592; const int counter_w = 100; int16_t ax, ay, az; float x, y, z; int counter; float angle_x, angle_y, angle_z, _angle_x, _angle_y, _angle_z; long ax_p, ay_p, az_p; // proměnné, které senzor vypisuje v klidovém stavu int alarm_x = 0; int alarm_y = 0; int alarm_z = 90; // tolerance pro spuštění alarmu int tolerance = 5; void setup() { Wire.begin(); Serial.begin(9600); Serial.println("Spousteni zarizeni MPU6050"); accelgyro.initialize(); Serial.println("Testovani zarizeni"); Serial.println(accelgyro.testConnection() ? "Zarizeni funguje spravne" : "Zarizeni se nepodarilo pripojit"); } void loop() { accelgyro.getAcceleration(&ax, &ay, &az); ax_p = ax_p + ax; ay_p = ay_p + ay; az_p = az_p + az; counter++; if (counter == counter_w) { //zjistíme průmerné hodnoty x = ax_p/counter; y = ay_p/counter; z = az_p/counter; angle_x = atan2(x, sqrt(square(y) + square(z)))/(pi/180); angle_y = atan2(y, sqrt(square(x) + square(z)))/(pi/180); angle_z = atan2(z, sqrt(square(x) + square(y)))/(pi/180); counter = 0; ax_p = 0; ay_p = 0; az_p = 0; if (angle_x > alarm_x + tolerance || angle_x < alarm_x - tolerance) { Serial.println("Alarm spusten"); } else if (angle_y > alarm_y + tolerance || angle_y < alarm_y - tolerance) { Serial.println("Alarm spusten"); } else if (angle_z > alarm_z + tolerance || angle_z < alarm_z - tolerance) { Serial.println("Alarm spusten"); } } }
Slovo na závěr
Ukázali jsme si absolutní základ práce s GY-521, ale je toho mnohem více k prozkoumání. Nezapomeňte na jeho přesnost, pouhé špatné uchycení v breadboadru dělá chybu!
Bastlení ZDAR!