Opis
Kontroler lotu wyposażony w 3-osiowy żyroskop i akcelerometr, 3-osiowy magnetometr oraz barometr. CaÅ‚ość sterowana mikrokontrolerem ATmega 2560 znanym z Arduino Mega. Posiada 8 wejść odbiornika, wyjÅ›cia do podÅ‚Ä…czenia serwomechanizmów oraz interfejsy: UART, I2C i analogowe.
Specyfikacja
W zestawie
Przydatne linki |
HMC5883L jest cyfrowym, 3-osiowym magnetometrem pozwalajÄ…cym na pomiar szerokiego zakresu wielkoÅ›ci pola magnetycznego Ziemi wynoszÄ…cego ±8 gausa. Jego rozdzielczość 12 bitów umożliwia pomiar z dokÅ‚adnoÅ›ciÄ… do 2 miligausów przy poborze prÄ…du zaledwie 100µA. HMC5883L komunikuje siÄ™ z mikrokontrolerem za pomocÄ… szyny I2C z maksymalnÄ… czÄ™stotliwoÅ›ciÄ… pomiarów wynoszÄ…cÄ… 75 Hz w trybie ciÄ…gÅ‚ym.
Zakres pomiarowy | Rozdzielczość | Wzmocnienie |
± 0.88 Ga | 0.73 mG | 1370 |
± 1.3 Ga | 0.92 mG | 1090 |
± 1.9 Ga | 1.22 mG | 820 |
± 2.5 Ga | 1.52 mG | 660 |
± 4 Ga | 2.27 mG | 440 |
± 4.7 Ga | 2.56 mG | 390 |
± 5.6 Ga | 3.03 mG | 330 |
± 8.1 Ga | 4.35 mG | 230 |
HMC5883L pozwala na osiÄ…gniÄ™cie czÄ™stotliwoÅ›ci pomiarów nawet do 160 Hz, jeÅ›li skorzystamy z trybu pojedyÅ„czego pomiaru oraz monitorowania przerwania DRDY. CiekawÄ… możliwoÅ›ciÄ… jakÄ… daje ten ukÅ‚ad, to wybór iloÅ›ci próbek (1, 2, 4 lub 8), które podlegajÄ… uÅ›rednieniu koÅ„cowego wyniku.
NapiÄ™cie zasilania mieÅ›ci siÄ™ w zakresie od 2.0 do 3.6V, dlatego ukÅ‚ad również nie toleruje zasilania wyższego (5V) - należy zwrócić szczególnÄ… uwagÄ™ na to, czy nasz moduÅ‚ posiada możliwość zasilania takim napiÄ™ciem. UkÅ‚ad jest zamkniÄ™ty w obudowie LCC o wymiarach 3mm x 3mm i wysokoÅ›ci 0.91mm .
Pełna dokumentacja techniczna: http://www.jarzebski.pl/datasheets/HMC5883L.pdf
W przypadku moduł IMU GY-80, możemy skorzystać z 5V zasilania. Pin oznaczony SCL (adapter) podłączamy do pinu A5 (Arduino), natomiast pin SDA (adapter) do pinu A4 (Arduino).
Do obsÅ‚ugi moduÅ‚ów z ukÅ‚adami HMC5883L skorzystamy z przygotowanej na tÄ… okazjÄ™ biblioteki dla Arduino, którÄ… można pobrać z repozytorium Git: https://github.com/jarzebski/Arduino-HMC5883L
Pierwszym przykładem będzie odczyt surowych wartości oraz znormalizowanych (mg):
#include <Wire.h>
#include <HMC5883L.h>
HMC5883L compass;
void setup()
{
Serial.begin(9600);
// Inicjalizacja HMC5883L
Serial.println("Initialize HMC5883L");
while (!compass.begin())
{
Serial.println("Nie odnaleziono HMC5883L, sprawdz polaczenie!");
delay(500);
}
// Ustawienie zakresu pomiarowego
// +/- 0.88 Ga: HMC5883L_RANGE_0_88GA
// +/- 1.30 Ga: HMC5883L_RANGE_1_3GA (domyslny)
// +/- 1.90 Ga: HMC5883L_RANGE_1_9GA
// +/- 2.50 Ga: HMC5883L_RANGE_2_5GA
// +/- 4.00 Ga: HMC5883L_RANGE_4GA
// +/- 4.70 Ga: HMC5883L_RANGE_4_7GA
// +/- 5.60 Ga: HMC5883L_RANGE_5_6GA
// +/- 8.10 Ga: HMC5883L_RANGE_8_1GA
compass.setRange(HMC5883L_RANGE_1_3GA);
// Ustawienie trybu pracy
// Uspienie: HMC5883L_IDLE
// Pojedynczy pomiar: HMC5883L_SINGLE
// Ciagly pomiar: HMC5883L_CONTINOUS (domyslny)
compass.setMeasurementMode(HMC5883L_CONTINOUS);
// Ustawienie czestotliwosci pomiarow
// 0.75Hz: HMC5883L_DATARATE_0_75HZ
// 1.50Hz: HMC5883L_DATARATE_1_5HZ
// 3.00Hz: HMC5883L_DATARATE_3HZ
// 7.50Hz: HMC5883L_DATARATE_7_50HZ
// 15.00Hz: HMC5883L_DATARATE_15HZ (domyslny)
// 30.00Hz: HMC5883L_DATARATE_30HZ
// 75.00Hz: HMC5883L_DATARATE_75HZ
compass.setDataRate(HMC5883L_DATARATE_15HZ);
// Liczba usrednionych probek
// 1 probka: HMC5883L_SAMPLES_1 (domyslny)
// 2 probki: HMC5883L_SAMPLES_2
// 4 probki: HMC5883L_SAMPLES_4
// 8 probki: HMC5883L_SAMPLES_8
compass.setSamples(HMC5883L_SAMPLES_1);
}
void loop()
{
// Pobranie pomiarow surowych
Vector raw = compass.readRaw();
// Pobranie pomiarow znormalizowanych
Vector norm = compass.readNormalize();
// Wyswielnie wynikow
Serial.print(" Xraw = ");
Serial.print(raw.XAxis);
Serial.print(" Yraw = ");
Serial.print(raw.YAxis);
Serial.print(" Zraw = ");
Serial.print(raw.ZAxis);
Serial.print(" Xnorm = ");
Serial.print(norm.XAxis);
Serial.print(" Ynorm = ");
Serial.print(norm.YAxis);
Serial.print(" ZNorm = ");
Serial.print(norm.ZAxis);
Serial.println();
delay(100);
}
Wynik działania
Znajomość wartości pola magnetycznego Ziemi (wektor X i wektor Y) pozwala na określenie bieżącego kierunku południka magnetycznego, a tym samym uzyskanie cyfrowego kompasu. Kierunek południka magnetycznego można obliczyć z prostej zależności:
kierunek_pomiaru (rad) = atan( wektor_y, wektor_x );
Aby poprawnie wyznaczyć kierunek, konieczne jest również uwzglÄ™dnienie czynnika bÅ‚Ä™du - deklinacji magnetycznej. Deklinacja magnetyczna spowodowana jest zarówno poÅ‚ożeniem bieguna magnetycznego Ziemi w innym miejscu niż biegun geograficzny oraz zróżnicowanymi warunkami magnetycznymi w miejscu pomiaru (np. poprzez wystÄ™powaniem dużej iloÅ›ci rud żelaza). Warto zauważyć, że deklinacja magnetyczna jest parametrem zmiennym w czasie, bowiem biegun magnetyczny Ziemi stale siÄ™ przemieszcza. Wartość aktualnej deklinacji magnetycznej znajdziemy na specjalnych mapach magnetycznych, a także na mapach nawigacyjnych.
Na szczęście żyjemy w czasach Internetu i odpowiednią mapę znajdziemy pod adresem: http://magnetic-declination.com/
Deklinacja magnetyczna dla Bytomia
Jak widzimy, deklinacja magnetyczna dla mojej lokalizacji wynosi plus 4 stopnie i 26 minut (wschód). Wartość tÄ… musimy przeliczyć na radiany:
kÄ…t_deklinacji = (stopnie + (minuty / 60.0)) / (180 / Pi);
kÄ…t_deklinacji = (4.0 + (26.0 / 60.0)) / (180 / Pi);
Obliczoną wartość kąta deklinacji dodajemy (wynik POSITIVE) lub odejmujemy (wynik NEGATIVE) od wartości zmierzonej z magnetometru:
kierunek = kierunek_pomiaru ± kÄ…t_deklinacji
JeÅ›li nie znamy kÄ…ta deklinacji, możemy przyjąć wartość zero. W nastÄ™pnej kolejnoÅ›ci musimy zadbać o to, aby otrzymany wynik kierunku bieguna magnetycznego mieÅ›ciÅ‚ siÄ™ w zakresie 0π - 2π (chyba, że chcemy mieć kompas, który pokazuje na przykÅ‚ad 370° zamiast 10°).
jeÅ›li kierunek < 0 to dodajemy do niego 2π
jeÅ›li kierunek > 2π to odejmujemy od niego 2π
Teraz możemy zamienić już radiany na stopnie:
kierunek (deg) = kierunek (rad) * (180 / π)
Istotnym problemem (brzydkÄ… cechÄ…) magnetometru HMC5883L jest nierównomierny pomiar pola magnetycznego w zakresie od 1° ÷ 180° oraz od 180° ÷ 360°. Dla pierwszego przedziaÅ‚u nasz magnetometr bÄ™dzie generowaÅ‚ przekÅ‚amane wyniki od 1° ÷ 240°, natomiast dla drugiego od 240° ÷ 360°. Można w Å‚atwy sposób to skorygować funkcjÄ… map() (patrz poniższy przykÅ‚ad programu). Do peÅ‚ni szczęścia możemy jeszcze wygÅ‚adzić wskazania naszego kompasu, ustawiajać jego reakcjÄ™ na zmianÄ™ o 3°.
Nasz program przedstawia się więc następująco:
#include <Wire.h>
#include <HMC5883L.h>
HMC5883L compass;
int previousDegree;
void setup()
{
Serial.begin(9600);
// Inicjalizacja HMC5883L
Serial.println("Initialize HMC5883L");
while (!compass.begin())
{
Serial.println("Nie odnaleziono HMC5883L, sprawdz polaczenie!");
delay(500);
}
// Ustawienie zakresu pomiarowego
compass.setRange(HMC5883L_RANGE_1_3GA);
// Ustawienie trybu pracy
compass.setMeasurementMode(HMC5883L_CONTINOUS);
// Ustawienie czestotliwosci pomiarow
compass.setDataRate(HMC5883L_DATARATE_15HZ);
// Liczba usrednionych probek
compass.setSamples(HMC5883L_SAMPLES_4);
}
void loop()
{
// Pobranie wektorów znormalizowanych
Vector norm = compass.readNormalize();
// Obliczenie kierunku (rad)
float heading = atan2(norm.YAxis, norm.XAxis);
// Ustawienie kata deklinacji dla Bytomia 4'26E (positive)
// Formula: (deg + (min / 60.0)) / (180 / M_PI);
float declinationAngle = (4.0 + (26.0 / 60.0)) / (180 / M_PI);
heading += declinationAngle;
// Korekta katow
if (heading < 0)
{
heading += 2 * PI;
}
if (heading > 2 * PI)
{
heading -= 2 * PI;
}
// Zamiana radianow na stopnie
float headingDegrees = heading * 180/M_PI;
// Output
Serial.print(" Heading = ");
Serial.print(heading);
Serial.print(" Degress = ");
Serial.print(headingDegrees);
Serial.println();
delay(100);
delay(100);
}
Wynik działania:
Program do processingu znajdziecie w archwium biblioteki.
Niestety kompas jest wrażliwy na wszelkie przechylenia, faÅ‚szujÄ…c kalkulacjÄ™ kierunku poÅ‚udnika magnetycznego. Musi siÄ™ wiÄ™c znajdować na pÅ‚askiej powierzchni, aby wyniki byÅ‚y poprawne. Niepożądany efekt wpÅ‚ywu przechyleÅ„ można na szczęście wyeliminować za pomocÄ… akcelerometru (na przykÅ‚ad ADXL345 lub MPU6050). Jak tego dokonać? Dowiesz siÄ™ z tego artykuÅ‚u - Komepnsacja przechyleÅ„ kompasów cyfrowych.
Biblioteka HMC5883L: https://github.com/jarzebski/Arduino-HMC5883L
Dokumentacja techniczna: http://www.jarzebski.pl/datasheets/HMC5883L.pdf
MPU6050 jest ukÅ‚adem, który Å‚Ä…czy w sobie 3-osiowy żyroskop, 3-osiowy akcelerometr oraz cyfrowy termometr. Z powodzeniem może zastÄ…pić opisywane już wczeÅ›niej ukÅ‚ady ADXL345 oraz L3G4200D. Jego szczególnÄ… cechÄ… jest wbudowana sprzÄ™towa jednostka DMP (Digital Motion Processor), która uÅ‚atwia przeliczanie przetwarzanych danych z wszystkich trzech czujników na konkretne poÅ‚ożenie wzglÄ™dem Ziemi, odciążajÄ…c tym samym mikrokontroler. JednostkÄ™ DMP można zaprogramować tak, aby wykorzystywaÅ‚a do swoich obliczeÅ„ również zewnÄ™trzny magnetometr.
W odróżnieniu od opisywanego już żyroskopu L3G4200D, oprócz możliwoÅ›ci pracy w zakresach pomiarowych ±250°/s, ±500°/s oraz ±2000°/s posiada dodatkowy tryb ±1000°/s, co czyni go bardziej uniwersalnym rozwiÄ…zaniem. Za przetwarzanie danych odpowiada również 16 bitowy przetwornik. PrzewagÄ… MPU6050 nad ADXL345 jest również dokÅ‚adniejszy 16-bitowy przetwornik zamiast 13-bitowego. Praktycznie MPU6050 posiada takie same funkcje jak ADXL345 oraz L3G4200D, jednak rozszerza swoje wÅ‚aÅ›ciwoÅ›ci o programowalny filtr dolnoprzepustowy, dodatkowÄ… szynÄ™ do komunikacji z innymi ukÅ‚adami oraz dodatkowe tryby oszczÄ™dzania energii. Wszystko to przy maksymalnym poborze prÄ…du 4.1mA (w duecie L3G4200D oraz ADXL345 potrafi przekroczyć 6mA)
NapiÄ™cie zasilania może mieÅ›cić siÄ™ w zakresie od 2.375V ÷ 3.46V, natomiast poziomy logiczne mogÄ… mieÅ›cić siÄ™ w zakresie od 1.71V do wartoÅ›ci napiÄ™cia zasilania
UkÅ‚ad MPU6050 toleruje zasilanie z przedziaÅ‚u 2.375V ÷ 3.46V, wiÄ™c jeÅ›li planujemy podÅ‚Ä…czyć go do Arduino UNO, nie zapomnijmy o konwerterze napięć poziomów logicznych. W przypadku moduÅ‚u IMU GY-86 oraz IMU GY-87, możemy skorzystać z 5V zasilania. Pin oznaczony SCL (adapter) podÅ‚Ä…czamy do pinu A5 (Arduino), natomiast pin SDA (adapter) do pinu A4 (Arduino). W moim ukÅ‚adzie wykorzystaÅ‚em również dwie diody z rezystorami 220Ω, sterowane wyjÅ›ciami cyfrowymi Arduino (4,7) do sygnalizacji przerwaÅ„.
Do obsÅ‚ugi moduÅ‚ów z ukÅ‚adami MPU6050 przygotowaÅ‚em również odpowiedniÄ… do nich bibliotekÄ™ dla Arduino, którÄ… można pobrać z repozytorium Git: https://github.com/jarzebski/Arduino-MPU6050
Odczyt z żyroskopu realizowany jest w podobny sposób jak w L3G4200D. Mamy możliwość odczytu danych bezpoÅ›rednich za pomocÄ… funkcji readRawGyro() oraz przeliczonych na jednostkÄ™ dps za pomocÄ… funkcji readNormalizeGyro(). Praktycznie jedynÄ… różnicÄ… jest inicjalizacja ukÅ‚adu, gdzie konfigurujemy jednoczeÅ›nie akcelerometr jak i żyroskop.
#include <Wire.h>
#include <MPU6050.h>
MPU6050 mpu;
void setup()
{
Serial.begin(115200);
Serial.println("Inicjalizacja MPU6050");
while(!mpu.begin(MPU6050_SCALE_2000DPS, MPU6050_RANGE_2G))
{
Serial.println("Nie mozna znalezc MPU6050 - sprawdz polaczenie!");
delay(500);
}
// Kalibracja żyroskopu
mpu.calibrateGyro();
// Ustawienie czułości
mpu.setThreshold(3);
}
void loop()
{
Vector rawGyro = mpu.readRawGyro();
Vector normGyro = mpu.readNormalizeGyro();
Serial.print(" Xraw = ");
Serial.print(rawGyro.XAxis);
Serial.print(" Yraw = ");
Serial.print(rawGyro.YAxis);
Serial.print(" Zraw = ");
Serial.println(rawGyro.ZAxis);
Serial.print(" Xnorm = ");
Serial.print(normGyro.XAxis);
Serial.print(" Ynorm = ");
Serial.print(normGyro.YAxis);
Serial.print(" Znorm = ");
Serial.println(normGyro.ZAxis);
delay(10);
}
W przypadku odczytu danych z akcelerometru, analogicznie posługujemy się funkcjami readRawAccel() oraz readNormalizeAccel(). Znormalizowaną jednostką są tutaj m/s.
#include <Wire.h>
#include <MPU6050.h>
MPU6050 mpu;
void setup()
{
Serial.begin(115200);
Serial.println("Inicjalizacja MPU6050");
while(!mpu.begin(MPU6050_SCALE_2000DPS, MPU6050_RANGE_2G))
{
Serial.println("Nie mozna znalezc MPU6050 - sprawdz polaczenie!");
delay(500);
}
}
void loop()
{
Vector rawAccel = mpu.readRawAccel();
Vector normAccel = mpu.readNormalizeAccel();
Serial.print(" Xraw = ");
Serial.print(rawAccel.XAxis);
Serial.print(" Yraw = ");
Serial.print(rawAccel.YAxis);
Serial.print(" Zraw = ");
Serial.println(rawAccel.ZAxis);
Serial.print(" Xnorm = ");
Serial.print(normAccel.XAxis);
Serial.print(" Ynorm = ");
Serial.print(normAccel.YAxis);
Serial.print(" Znorm = ");
Serial.println(normAccel.ZAxis);
delay(10);
}
Jeśli chodzi odczyt temperatury, realizujemy ją za pomocą funkcji readTemperature().
#include <Wire.h>
#include <MPU6050.h>
MPU6050 mpu;
void setup()
{
Serial.begin(115200);
Serial.println("Inicjalizacja MPU6050");
while(!mpu.begin(MPU6050_SCALE_2000DPS, MPU6050_RANGE_2G))
{
Serial.println("Nie mozna znalezc MPU6050 - sprawdz polaczenie!");
delay(500);
}
}
void loop()
{
float temp = mpu.readTemperature();
Serial.print(" Temp = ");
Serial.print(temp);
Serial.println(" *C");
delay(500);
}
UkÅ‚ad MPU6050 z racji posiadania zarówno akcelerometru jak i żyroskopu, pozwala na zastoswanie filtru Kalmana, inaczej mówiÄ…c algorytmu rekurencyjnego. Ponieważ nie jestem wybitnym matematykiem, nie odważę siÄ™ dokÅ‚adnie tÅ‚umaczyć zasady jego dziaÅ‚ania, ale w ogólnym uproszczeniu wykorzystuje on dane żyroskopu do korekcji odczytanych danych z akcelerometru, dziÄ™ki czemu caÅ‚ość dziaÅ‚a wyjÄ…tkowo stabilnie. JeÅ›li masz ochotÄ™, możesz poczytać o nim tutaj: Odczyty Pitch & Roll oraz filtr Kalmana
MPU6050 pozwala na detekcję ruchu i bezruchu całego układu z wykorzystaniem przerwań. Na potrzeby tego przykładu, nie będziemy jednak obsługiwali sprzętowej obsługi przerwania, a jedynie programowej. Zasada działania została opisana w programie poniżej.
#include <Wire.h>
#include <MPU6050.h>
MPU6050 mpu;
void setup()
{
Serial.begin(115200);
Serial.println("Inicjalizacja MPU6050");
while(!mpu.begin(MPU6050_SCALE_2000DPS, MPU6050_RANGE_16G))
{
Serial.println("Nie mozna znalezc MPU6050 - sprawdz polaczenie!");
delay(500);
}
// Dodatkowe opoznienie zasilania akcelerometru 3ms
mpu.setAccelPowerOnDelay(MPU6050_DELAY_3MS);
// Wylaczamy sprzetowe przerwania dla wybranych zdarzen
mpu.setIntFreeFallEnabled(false);
mpu.setIntZeroMotionEnabled(false);
mpu.setIntMotionEnabled(false);
// Ustawiamy filtr gorno-przepustowy
mpu.setDHPFMode(MPU6050_DHPF_5HZ);
// Ustawiamy granice wykrywania ruchu na 4mg (zadana wartosc dzielimy przez 2)
// oraz minimalny czas trwania na 5ms
mpu.setMotionDetectionThreshold(2);
mpu.setMotionDetectionDuration(5);
// Ustawiamy granice wykrywania bezruchu na 8mg (zadana wartosc dzielimy przez 2)
// oraz minimalny czas trwania na 2ms
mpu.setZeroMotionDetectionThreshold(4);
mpu.setZeroMotionDetectionDuration(2);
// Ustawiamy piny 4 i 5 na wyjscia w stanie niskim.
// Diody podlaczone do tych wyjsc beda sygnalizowaly nasze stany
pinMode(4, OUTPUT);
digitalWrite(4, LOW);
pinMode(7, OUTPUT);
digitalWrite(7, LOW);
}
void loop()
{
Vector rawAccel = mpu.readRawAccel();
Activites act = mpu.readActivites();
// Jesli wyrkryto ruch - zapal diode na pinie 4
if (act.isActivity)
{
digitalWrite(4, HIGH);
} else
{
digitalWrite(4, LOW);
}
// Jesli wyrkryto bezruch - zapal diode na pinie 7
if (act.isInactivity)
{
digitalWrite(7, HIGH);
} else
{
digitalWrite(7, LOW);
}
delay(50);
digitalWrite(4, LOW);
}
Poniższy przykÅ‚ad pokazuje w jaki sposób za pomocÄ… MPU6050 można wykryć upadek swobodny. Tym razem wykorzystamy już przerwanie sprzÄ™towe.
#include <Wire.h>
#include <MPU6050.h>
MPU6050 mpu;
boolean ledState = false; // aktualny stan diody LED
boolean freefallDetected = false; // czy wykryto upadek swobodny
boolean freefallBlinkCount = 0; // liczba mrugniec diody LED
void setup()
{
Serial.begin(115200);
Serial.println("Inicjalizacja MPU6050");
while(!mpu.begin(MPU6050_SCALE_2000DPS, MPU6050_RANGE_16G))
{
Serial.println("Nie mozna znalezc MPU6050 - sprawdz polaczenie!");
delay(500);
}
// Dodatkowe opoznienie zasilania akcelerometru 3ms
mpu.setAccelPowerOnDelay(MPU6050_DELAY_3MS);
// Wlaczamy obsluge przerwania sprzetowego dla akcji upadku swobodnego
mpu.setIntFreeFallEnabled(true);
mpu.setIntZeroMotionEnabled(false);
mpu.setIntMotionEnabled(false);
// Ustawiamy filtr gorno-przepustowy
mpu.setDHPFMode(MPU6050_DHPF_5HZ);
// Aby ruch zostal wykryty jako upadek swobodny, musi wystapic przeciazenie minimum 34mg w czasie 3ms.
mpu.setFreeFallDetectionThreshold(17);
mpu.setFreeFallDetectionDuration(2);
// Dioda podlaczona do pinu 4 bedzie sygnalizowac wykryty stan
pinMode(4, OUTPUT);
digitalWrite(4, LOW);
// Aktywujemy obsuge przerwania na pinie 2, aktywne na zbocze narastajace
attachInterrupt(0, doInt, RISING);
}
// Funkcja obslugi przerwania
void doInt()
{
// resetujemy licznik mrugniec i informujemy program, ze akcja zostala rozponana
freefallBlinkCount = 0;
freefallDetected = true;
}
void loop()
{
Vector rawAccel = mpu.readRawAccel();
Activites act = mpu.readActivites();
// Jesli wykryto upadek swobodny, dioda LED bedzie mrugac okreslona liczbe razy
if (freefallDetected)
{
ledState = !ledState;
digitalWrite(4, ledState);
freefallBlinkCount++;
if (freefallBlinkCount == 20)
{
freefallDetected = false;
ledState = false;
digitalWrite(4, ledState);
}
}
delay(100);
}
Biblioteka MPU6050: https://github.com/jarzebski/Arduino-MPU6050
Filtr Kalmana: https://github.com/jarzebski/Arduino-KalmanFilter
MPU6050 rev 3.2: http://www.jarzebski.pl/datasheets/MPU6050_rev32.pdf
MPU6050 rev 3.4: http://www.jarzebski.pl/datasheets/MPU6050_rev34.pdf
MPU6050 rev 4.0: http://www.jarzebski.pl/datasheets/MPU6050_rev40.pdf
MPU6050 rev 4.2: http://www.jarzebski.pl/datasheets/MPU6050_rev42.pdf