myAHRS+ z układem MPU9150
myAHRS+ jest jednym z najwydajniejszych systemów AHRS (Attitude Heading Reference System) z jakim dotychczas przyszło mi pracować. Jego ogromną i niezaprzeczalną zaletą jest wysoka stabilność wyników względem znacznych przyśpieszeń oraz występowania zaburzeń pola magnetycznego.
Komunikacja i konfiguracja modułu myAHRS+ może odbywać się za pomocą interfejsu USB/UART, co czyni go przyjaznym we współpracy z płytkami takimi jak Raspberry Pi czy ODROID. Projektanci i pasjonaci Arduino bez problemu uzyskają dostęp do danych poprzez magistralę I2C.
Dodatkową zaletą jest dostępność GUI myAHRS+ Monitior, który pozwala użytkownikowi na konfigurację wszystkich ustawień pracy modułu oraz podgląd stanu i danych z IMU w czasie rzeczywistym, a także zapis do pliku tekstowego. Za pomocą myAHRS+ Monitior możemy również dokonać stosownej kalibracji magnetometru i czujników.
Najważniejsze parametry modułu myAHRS+ to:
- Trójosiowy, 16-bitowy żyroskop o zakresie pomiaru ±2000 dps
- Trójosiowy, 16-bitowy akcelerometr o zakresie pomiaru ±16 g
- Trójosiowy, 13-bitowy magnetometr o zakresie pomiaru ±1200 μT
- Rozbudowany filtr Kalmana
- Częstotliwość wyników na poziomie 100 Hz
- Możliwość odczytu przeliczonych wyników: kąty Eulera lub Quaternion
- Możliwość odczytu sensorów: przyśpieszenie, prędkość kątowa, pole magnetyczne
- Komunikacja USB (wirtualny port COM), UART (do 460800 bps), I2C (do 1kHz)
Sercem jest 32-bitowy mikrokontroler STM32F103CBU6 taktowany zegarem 72MHz, wykonany w architekturze ARM Cortex-M3. Do pomiarów wartości wykorzystany została jednostka MPU9150.
Pinologia modułu przedstawia się następująco:
Pin | Oznaczenie | Typ pinu | Opis |
J3-1 | INT | O | Przerwanie gotowości danych DATA READY |
J3-2 | SLEEP | I | Wybór trybu pracy (stan niski - tryb uśpienia. stan wysoki - normalna praca) Normalnej pracy odpowiada również pozostawienie pinu nie podłączonego |
J3-3 | I2C_SCL | I | Zegar I2C |
J3-4 | I2C_SDA | I/O | Dane I2C |
J3-5 | USB_DM | I/O | USB D- |
J3-6 | USB_DP | I/O | USB D+ |
J3-7 | NC | Nie używany | |
J3-8 | NC | Nie używany | |
J3-9 | NC | Nie używany | |
J3-10 | NC | Nie używany | |
J4-1 | VDD | PWR | Zasilanie +5V |
J4-2 | nRST | I | Stan niski - Reset, stan wysoki - normalna praca |
J4-3 | NC | Nie używany | |
J4-4 | UART_TX | O | UART TX |
J4-5 | UART_RX | I | UART RX |
J4-6 | NC | Nie używany | |
J4-7 | NC | Nie używany | |
J4-8 | NC | Nie używany | |
J4-9 | NC | Nie używany | |
J4-10 | GND | PWR | Masa |
myAHRS+ Monitor
myAHRS+ Monitor to specjalny program, za pomocą którego możemy monitorować pracę modułu oraz przeprowadzić jego konfigurację. Warto się nad nim pochylić, ponieważ pozwala na dokonanie bardzo ważnej kalibracji magnetometru w celu uwzględnienia deklinacji magnetycznej o której już pisałem w artykule na temat magnetometru HMC5883L.
Kiedy podłączymy moduł do gniazda USB musimy wybrać odpowiedni, wirtualny port komunikacyjny STMicroelectronics Virtual COM Port (ikona lornetki). W przypadku systemu Windows 7 nie są wymagane żadne dodatkowe sterowniki.
Po tym zabiegu będziemy mogli już odczytywać dane bezpośrednio z myAHRS+. Oprócz wykresów i przetworzonych już wartości, mamy również podgląd na wydawane polecenia UART oraz odbierane dane. Na poniższym przykładzie są to dane w formacie binarnym.
Jeśli nie odpowiada nam binarny format, prędkość transmisji, częstotliwość pomiarów lub format zwracanych danych, możemy odpowiednio skonfigurować moduł oraz skalibrować czujniki według własnych potrzeb:
Konfigurator pozwala na nadanie identyfikatora naszemu modułowi, aby był odróżniany w przypadku, gdybyśmy chcieli mieć ich większą ilość. Parametr Divider oznacza dzielnik bazowej częstotliwości 100Hz dla pomiarów - przykładowo dla wartości "2" otrzymamy 50Hz itd.
Ciekawą opcją jest również określenie trybu transmisji jako ciągłej (Continuous) lub na żądanie (Trigger). Mamy również możliwość zdefiniowania, jakie dane chcemy odbierać - czy tylko wartości mierzone czujników, czy już przeliczone kąty. Oczywiście możemy zdecydować się na różne kombinacje.
Należy jednak pamiętać, że zapisanie ustawień nie powoduje przechowanie wszystkich parametrów w pamięci, gdzie ponowne zasilenie modułu będzie wymagało ponownego ich ustawienia. Dlatego projektując urządzenie pamiętajmy, aby wydać stosowne polecenia w trakcie inicjalizacji modułu. Wyjątkiem od tej reguły są parametry kalibracyjne poszczególnych czujników, prędkości magistrali UART oraz ID modułu.
Kalibracja magentometru (deklinacja magnetyczna)
Szalenie ważnym krokiem jest poprawna kalibracja magnetometru uwzględniającego deklinację magnetyczną. Kiedy zobrazujemy sobie wyniki pomiaru pola magnetycznego obracając moduł we wszystkich kierunkach, zauważymy, że powstała sfera nie pokrywa się ze sferą odniesienia.
Do kalibracji magnetometru posłuży nam odpowiednia opcja oznaczona śrubokrętem, kluczem i igły magnetycznej. Wszystko co musimy zrobić, to wykonać jak największą ilość losowych obrotów modułu w możliwie największej ilości płaszczyzn - tak aby zostało pobrane 6000 próbek. Najlepiej jeśli otrzymamy dobrze widoczną sferę:
Kiedy program pobierze 6000 próbek, możemy wyliczyć dane kalibracyjne magnetometru i zapisać je w pamięci EEPROM:
Po takim zabiegu, nowo powstała sfera powinna pokrywać się ze sferą odniesienia:
Komunikacja UART
Za pomocą magistrali UART możemy ustawiać parametry pracy oraz odczytywać przetworzone przez niego dane. Standardowo będziemy posługiwali się nagłówkiem startu ramki komunikacyjnej, zakończenia ramki oraz wyliczoną sumą kontrolną po znaku *
Każde wydawane polecenie poprzedzamy znakiem @, a kończymy znakami powrotu r/n
@polecenie,ewentualny_parametr*suma_kontrolna
Jeśli polecenie zostanie wykonane otrzymamy odpowiedź z nagłówkiem znaku ~
~odpowiedz,ewentualny_parametr*suma_kontrolna
Dane z czujników poprzedzane są natomiast znakiem $
$typ_ramki,dane*suma_kontrolna
Obliczenie sumy kontrolnej odbywa się poprzez obliczenie bitowej różnicy symetrycznej każdego znaku w poleceniu:
Przykładowo tak będzie wyglądało wysłanie polecenia Pythonie:
- def send_command(serial_port, cmd_msg):
- cmd_msg = '@' + cmd_msg.strip()
- crc = 0
- for c in cmd_msg:
- crc = crc^ord(c)
- serial_port.write(cmd_msg + '*%02X'%crc + chr(13) + chr(10))
Pełny przykład znajdziecie tutaj: https://github.com/withrobot/myAHRS_plus/blob/master/common_python/basic_example/python_example.py
Listę komend UART wraz z opcjonalnymi parametrami oraz formatem wyników opublikowano na GitHub: https://github.com/withrobot/myAHRS_plus/tree/master/tutorial
Komunikacja I2C
Pomimo tego, że logika modułu wynosi 3.3V to toleruje on również logikę 5V, dzięki czemu możemy podłączyć go bezpośrednio do Arduino UNO. Jedyne co musimy zrobić, to podciągnąć linie sygnałowe do zasilania rezystorami z przedziału 1kΩ - 10kΩ (typowo 4.7kΩ).
Komunikacja po I2c nie umożliwia konfiguracji modułu - pozwala jedynie na odczytanie wartości rejestrów czujników i obliczonych danych. Adres modułu na magistrali I2c to 0x20.
Lista rejestrów przedstawia się następująco:
Nazwa rejestru | Atrybut | Adres | Domyślna wartość | Opis |
WHO_AM_I | R | 0x01 | 0xB1 | Rejestr identyfikacyjny |
REV_ID_MAJOR | R | 0x02 | - | - |
REV_ID_MINOR | R | 0x03 | - | - |
STATUS | R | 0x04 | 0x80 | - |
I_ACC_X_LOW | R | 0x10 | DATA | Akcelerometr (surowe wartości) |
I_ACC_X_HIGH | R | 0x11 | DATA | Akcelerometr (surowe wartości) |
I_ACC_Y_LOW | R | 0x12 | DATA | Akcelerometr (surowe wartości) |
I_ACC_Y_HIGH | R | 0x13 | DATA | Akcelerometr (surowe wartości) |
I_ACC_Z_LOW | R | 0x14 | DATA | Akcelerometr (surowe wartości) |
I_ACC_Z_HIGH | R | 0x15 | DATA | Akcelerometr (surowe wartości) |
I_GYRO_X_LOW | R | 0x16 | DATA | Żyroskop (surowe wartości) |
I_GYRO_X_HIGH | R | 0x17 | DATA | Żyroskop (surowe wartości) |
I_GYRO_Y_LOW | R | 0x18 | DATA | Żyroskop (surowe wartości) |
I_GYRO_Y_HIGH | R | 0x19 | DATA | Żyroskop (surowe wartości) |
I_GYRO_Z_LOW | R | 0x1A | DATA | Żyroskop (surowe wartości) |
I_GYRO_Z_HIGH | R | 0x1B | DATA | Żyroskop (surowe wartości) |
I_MAGNET_X_LOW | R | 0x1C | DATA | Magnetometr (surowe wartości) |
I_MAGNET_X_HIGH | R | 0x1D | DATA | Magnetometr (surowe wartości) |
I_MAGNET_Y_LOW | R | 0x1E | DATA | Magnetometr (surowe wartości) |
I_MAGNET_Y_HIGH | R | 0x1F | DATA | Magnetometr (surowe wartości) |
I_MAGNET_Z_LOW | R | 0x20 | DATA | Magnetometr (surowe wartości) |
I_MAGNET_Z_HIGH | R | 0x21 | DATA | Magnetometr (surowe wartości) |
C_ACC_X_LOW | R | 0x22 | DATA | Akcelerometr (skalibrowane wartości) |
C_ACC_X_HIGH | R | 0x23 | DATA | Akcelerometr (skalibrowane wartości) |
C_ACC_Y_LOW | R | 0x24 | DATA | Akcelerometr (skalibrowane wartości) |
C_ACC_Y_HIGH | R | 0x25 | DATA | Akcelerometr (skalibrowane wartości) |
C_ACC_Z_LOW | R | 0x26 | DATA | Akcelerometr (skalibrowane wartości) |
C_ACC_Z_HIGH | R | 0x27 | DATA | Akcelerometr (skalibrowane wartości) |
C_GYRO_X_LOW | R | 0x28 | DATA | Żyroskop (skalibrowane wartości) |
C_GYRO_X_HIGH | R | 0x29 | DATA | Żyroskop (skalibrowane wartości) |
C_GYRO_Y_LOW | R | 0x2A | DATA | Żyroskop (skalibrowane wartości) |
C_GYRO_Y_HIGH | R | 0x2B | DATA | Żyroskop (skalibrowane wartości) |
C_GYRO_Z_LOW | R | 0x2C | DATA | Żyroskop (skalibrowane wartości) |
C_GYRO_Z_HIGH | R | 0x2D | DATA | Żyroskop (skalibrowane wartości) |
C_MAGNET_X_LOW | R | 0x2E | DATA | Magnetometr (skalibrowane wartości) |
C_MAGNET_X_HIGH | R | 0x2F | DATA | Magnetometr (skalibrowane wartości) |
C_MAGNET_Y_LOW | R | 0x30 | DATA | Magnetometr (skalibrowane wartości) |
C_MAGNET_Y_HIGH | R | 0x31 | DATA | Magnetometr (skalibrowane wartości) |
C_MAGNET_Z_LOW | R | 0x32 | DATA | Magnetometr (skalibrowane wartości) |
C_MAGNET_Z_HIGH | R | 0x33 | DATA | Magnetometr (skalibrowane wartości) |
TEMPERATURE_LOW | R | 0x34 | DATA | Temperatura |
TEMPERATURE_HIGH | R | 0x35 | DATA | Temperatura |
ROLL_LOW | R | 0x36 | DATA | Kąt Eulera |
ROLL_HIGH | R | 0x37 | DATA | Kąt Eulera |
PITCH_LOW | R | 0x38 | DATA | Kąt Eulera |
PITCH_HIGH | R | 0x39 | DATA | Kąt Eulera |
YAW_LOW | R | 0x3A | DATA | Kąt Eulera |
YAW_HIGH | R | 0x3B | DATA | Kąt Eulera |
QUATERNION_X_LOW | R | 0x3C | DATA | Quaternion |
QUATERNION_X_HIGH | R | 0x3D | DATA | Quaternion |
QUATERNION_Y_LOW | R | 0x3E | DATA | Quaternion |
QUATERNION_Y_HIGH | R | 0x3F | DATA | Quaternion |
QUATERNION_Z_LOW | R | 0x40 | DATA | Quaternion |
QUATERNION_Z_HIGH | R | 0x41 | DATA | Quaternion |
QUATERNION_W_LOW | R | 0x42 | DATA | Quaternion |
QUATERNION_W_HIGH | R | 0x43 | DATA | Quaternion |
Jak nie trudno zauważyć, wszystkie dane czujników są zapisane w formacie 16 bitowym, dlatego w przypadku Arduino, chcąc uzyskać prawidłową wartość, musimy odczytać dwa bajty spod wskazanego adresu.
W przypadku danych surowych I_ACC_X_LOW ~ I_MAGNET_Z_HIGH, wystarczy odczytać tylko młodszy i starszy bajt, gdzie uzskana wartość jest wartością rzeczywistą.
Inaczej sprawa wygląda dla danych skalibrowanych dla czujników, gdzie odczytaną wartość należy przemnożyć przez dany współczynnik skali:
Akcelerometr (g) = OdczytanaWartosc × 16 / 32767
Żyroskop (dps) = OdczytanaWartosc × 2000 / 32767
Magnetometr (uT) = OdczytanaWartosc × 0.3
Temperatura (°C) = OdczytanaWartosc × 200 / 32767
Kąt Eulera (°) = OdczytanaWartosc × 180 / 32767
Quaternion = OdczytanaWartosc / 32767
Przykład dla Arduino
W poniższym przykładzie, przedstawiam sposób dobrania się do rejestrów, zawierających kąty Euelara: Pitch, Roll oraz Yaw.
- #include <Wire.h>
- #define MYAHRS_ADDRESS 0x20
- uint8_t readRegister8(uint8_t reg)
- {
- uint8_t value;
- Wire.beginTransmission(MYAHRS_ADDRESS);
- #if ARDUINO >= 100
- Wire.write(reg);
- #else
- Wire.send(reg);
- #endif
- Wire.endTransmission(false);
- Wire.beginTransmission(MYAHRS_ADDRESS);
- Wire.requestFrom(MYAHRS_ADDRESS, 1);
- while(!Wire.available()) {};
- #if ARDUINO >= 100
- value = Wire.read();
- #else
- value = Wire.receive();
- #endif;
- Wire.endTransmission();
- return value;
- }
- int16_t readRegister16(uint8_t reg)
- {
- int16_t value;
- Wire.beginTransmission(MYAHRS_ADDRESS);
- #if ARDUINO >= 100
- Wire.write(reg);
- #else
- Wire.send(reg);
- #endif
- Wire.endTransmission(false);
- Wire.beginTransmission(MYAHRS_ADDRESS);
- Wire.requestFrom(MYAHRS_ADDRESS, 2);
- #if ARDUINO >= 100
- uint8_t vla = Wire.read();
- uint8_t vha = Wire.read();
- #else
- uint8_t vla = Wire.receive();
- uint8_t vha = Wire.receive();
- #endif;
- Wire.endTransmission();
- value = vha << 8 | vla;
- return value;
- }
- float getRoll()
- {
- int value = readRegister16(0x36);
- return (float)value * 180 / 32767;
- }
- float getPitch()
- {
- int value = readRegister16(0x38);
- return (float)value * 180 / 32767;
- }
- float getYaw()
- {
- int value = readRegister16(0x3A);
- return (float)value * 180 / 32767;
- }
- void setup()
- {
- Serial.begin(115200);
- Wire.begin();
- Serial.print("WHO_I_AM = ");
- Serial.println(readRegister8(0x01), HEX);
- }
- void loop()
- {
- Serial.print(" ROLL = ");
- Serial.print(getRoll());
- Serial.print(" PITCH = ");
- Serial.print(getPitch());
- Serial.print(" YAW = ");
- Serial.println(getYaw());
- }
Porówanie
Poniżej możecie zobaczyć porównanie modułu myAHRS+ z MPU9150 Motion Fit
Podsumowanie
myAHRS+ jest bardzo dobrym, szybkim i szalenie dokładnym modułem - jest niestety też modułem z wyższej półki cenowej. W zależności od sklepu możemy dostać go w cenie od 400zł do 500zł. W sklepach zagranicznych dostępny jest w cenie 75$, co przekłada się na kwotę około 250zł (bez kosztów przesyłki, opłaty celnej, podatku VAT).
Biorąc jednak pod uwagę klasę modułu, braku konieczności samodzielnego programowania jednostki DMP w układach MPUxxx, ogromnej wygody w użytkowaniu i możliwość łatwego podpięcia pod płytki typu Raspberry Pi / Odroid, myAHRS+ jest warty każdej wydanej złotówki.
Na szczególną uwagę zasługuje również pełne wsparcie modułu dla ROS (The Robot Operating System), szalenie popularnego wśród robotyków.
Więcej informacji: https://github.com/withrobot/myAHRS_plus
W Polsce moduł dostępny jest w ofercie sklepów Elty.pl oraz Kamami.pl
Reklama
Komentarze
Ciekawe, czy mając tak dokładny pomiar przyspieszenia (i obrotu z żyroskopu) dało by się w miarę dokładnie wyliczać położenie, bo przecież jeśli prędkość jest pochodną drogi a przyspieszenie pochodną prędkości, to całkując da się to (teoretycznie) policzyć w drugą stronę. Próbowałem kiedyś to zrobić dla telefonu z Androidem (jeśli nie będziemy nim obracać, to wystarczy sam akcelerometr), ale układ za bardzo szumiał i nawet nieruchomy telefon generował narastający w czasie błąd obliczeń wynikający z niedokładnego pomiaru. Próbowałem kulawo implementować jakiś filtr górnoprzepustowy czy właśnie(chyba) filtr kalmana, ale wyniki nie były satysfakcjonujące choć na wykresie przysp. po filtrowaniu wyglądało sensownie. Całkowałem metodą trapezów co przy dobrej rozdzielczości powinno dać sensowne wyniki.
Wiadomo, że w dłuższym czasie taki błąd będzie narastał, ale na krótkich odcinkach można by wyliczać zmianę położenia.
Strzelam, że można :)
To chyba muszę chociaż na nowym smartfonie spróbować odpalić, w sumie akcelerometr o kilka generacji lepszy, może jakoś będzie działać.
Istnieją systemy tzw nawigacji inercyjnej, np na okrętach. W piechocie też się pojawia moda na takie rozwiązania i one właśnie ze względu na wagę wykorzystują czujniki MEMS. To pozwala żołnierzom odnaleźć się w przypadku braku sygnału GPS np. w budynkach, ale jeżeli tylko GPS jest dostępny jest używany do skorygowania błędu nawigacji inercyjnej.
Szukam przykładów połączenia i komunikacji myAHRS+ z Raspberry Pi (Model 2) przez I2C.
Dziękuję z góry.
Hej, Co to jest, te takie coś czym zaczepiasz piny na ahrsie w trakcie komunikacji z arduino? można to gdzieś kupic?
Hej, jest ktoś komu zalega taki czujnik? chetnie odkupie
shadowpl@gmail.com
na elty została chyba jedna sztuka: https://elty.pl/pl/p/AHRS-Attitude-Heading-Reference-System/1266