MPU9250 – 9-Achsen Sensormodul – Teil 1

Über den Beitrag

In vorherigen Beiträgen habe ich die Beschleunigungssensoren MMA7361 und ADXL345 vorgestellt. Außerdem habe ich über das Gyroskop- und Beschleunigungssensormodul MPU6050 berichtet. Diesmal möchte ich mich dem 9-Achsen Sensormodul MPU9250 widmen. Dieses vereinigt vier Funktionen:

  • Gyroskop
  • Beschleunigungssensor
  • Magnetometer
  • Thermometer

Ich habe bei meinen Recherchen nach einer Bibliothek gesucht, die zum einen die vielen Funktionen und Einstellparameter des MPU9250 implementiert hat, zum anderen aber auch bedienbar ist, ohne dass man in die Tiefen des Datenblattes einsteigen muss. Da ich nicht wirklich fündig geworden bin, habe ich selbst eine Bibliothek geschrieben, die ich in diesem Beitrag vorstellen werde.

Update 25.03.2022: Die Bibliothek funktioniert jetzt auch mit dem MPU6500, bei dem es sich im Prinzip um einen MPU9250 ohne Magnetometer handelt.  Zudem habe ich die Ansteuerung per SPI implementiert. Beispielsketche sind der Bibliothek beigefügt. 

Update 30.07.2022: Ich bekomme zunehmend Kommentare und Zuschriften, dass das Magnetometer nicht funktioniert und/oder dass die Sketche „MPU9250/MPU6500 does not respond“ zurückmelden. Bitte lest dazu das readme auf GitHub und prüft euer Modul mit dem Beispielsketch MPU9250_who_am_I.

Wegen des großen Umfanges habe ich mich entschieden, den Beitrag aufzuteilen. In diesem ersten Part behandele ich:

  • Eigenschaften / technische Daten des MPU9250
  • Beschaltung
  • Vorstellung meiner Bibliothek MPU9250:
    • Basisdaten ermitteln:
      • Beschleunigung
      • Gyroskop
      • Magnetometer
      • Alle Basisdaten / Temperatur
    • Kalibrierung

In Teil 2 führe ich die Vorstellung meiner Bibliothek fort:

  • Winkel messen
  • Interrupts einrichten
  • Low Power / Cycle Modus
  • Den Fifo nutzen

Eigenschaften und technische Daten des MPU9250

Wie ein Gyroskop und ein Beschleunigungssensor funktioniert, habe ich schon in meinem Beitrag über den MPU6050 beschrieben. Das Magnetometer funktioniert auf Basis des Hall-Effekts. Der ist hier sehr anschaulich erklärt.

Der MPU9250 ist eigentlich ein Auslaufmodell. Auf den Seiten des Herstellers ist er als „EoL“ (End of Life) gekennzeichnet. Ich gehe aber davon aus, dass ihr die Module noch einige Jahre kaufen könnt. Auf Amazon und eBay ist die Auswahl an Shops die ihn führen, riesig. Nachfolger ist der ICM-20948, über den ich auch bald berichten werde. Die Module sind aber noch relativ teuer (~15 Euro). Den MPU9250 hingegen bekommt ihr schon für die Hälfte.

Da der MPU9250 vier Sensoren vereinigt, würde eine vollständige Auflistung der technischen Daten den Rahmen sprengen. Deshalb beschränke ich mich auf wesentliche Eckdaten:

  • Spannungsversorgung: 3.0 – 5.0 Volt (Modul)
    • für den eigentlichen IC sind es 2.4 – 3.6 Volt
  • Kommunikation über I2C, Adressen:
    • AD0 unverbunden oder LOW: 0x68
    • AD0 an HIGH: 0x69
  • Kommunikation über SPI
  • Gyroskop
    • Messbereiche: +/-250, +/-500, +/-1000, +/-2000 °/s
    • Datenrate: 3.91 – 32000 Hz
    • Auflösung: 16 Bit
    • Stromverbrauch: 3.2 mA (normaler Modus) / 8µA (Schlafmodus)
  • Beschleunigungssensor:
    • Messbereiche: +/- 2, +/-4, +/-8, +/-16 g
    • Datenrate: 0.24 Hz (im Cycle Modus) bis 4000 Hz
    • Auflösung: 16 Bit
    • Stromverbrauch: 450 µA (normaler Modus) / 8.4 µA im Low Power Modus (0.98 Hz Cycle) / 19.8 µA im Low Power Modus (31.25 Hz Cycle)
  • Magnetometer:
    • Messbereich: +/- 4800 µT
    • Datenrate: 8 Hz oder 100 Hz
    • Auflösung: 14 oder 16 Bit (ich habe nur 16 Bit implementiert)
    • Stromverbrauch: 280 µA bei 8 Hz Datenrate
  • FiFo (First in, first out) Datenspeicher: 512 Bytes = 256 Einzelwerte = 85 x,y,z-Datentripel
  • Interrupts: Fifo Überlauf, Data Ready und „Wake-on-Motion“ (Beschleunigungsinterrupt)
  • Integriertes Thermometer

Der MPU9250 ermöglicht es, fünf weitere Sensoren per I2C in einem Subnetz steuern, die Daten im MPU9250 zu speichern und auszulesen oder sie im FIFO abzulegen. Das habe ich nur für das Magnetometer implementiert.

Weitere Informationen findet ihr im technischen Datenblatt, in der Register Map und im Datenblatt des Magnetsensors AK8963.

Ansteuerung des MPU9250 mit Arduino UNO und Co

Schaltung

Pull-Up Widerstände für die I2C Leitungen werden nicht benötigt. Eventuell müsst ihr einen Level-Konverter oder Spannungsteiler für die Ein- und Ausgänge verwenden, falls ihr ein 5V Board einsetzt. Auch wenn das MPU9250 Modul einen Spannungsregler für VCC besitzt, heißt das nicht unbedingt, dass auch die anderen Zugänge heruntergeregelt werden.

Der Anschluss des Interrupt Pins wird für einige Beispielsketche benötigt. Da ich ihn active-high einstelle, brauche ich für ihn keinen Spannungsregelung. 

Aber noch ein paar Worte zu den ungenutzten Anschlüssen:

  • EDA/ECL sind die I2C Anschlüsse für externe Sensoren – diese Option habe ich wie erwähnt (noch) nicht implementiert
  • AD0: an HIGH angeschlossen wird die I2C Adresse von 0x68 auf 0x69 geändert
  • NCS: Chip Select für SPI Betrieb (von mir nicht implementiert)
  • FSYNC („Frame Synchronisation“): kann für Eingang für Interrupts weiterer Sensoren genutzt werden – das habe ich (noch) nicht implementiert.
Anschluss des MPU9250 an den Arduino Nano
Anschluss des MPU9250 an den Arduino Nano

Bibliotheken für den MPU9250

Es gibt eine Reihe von Bibliotheken für den MPU9250. Schaut mal in der Arduino Bibliotheksverwaltung oder auf GitHub. Ich war, wie eingangs schon erwähnt, nicht besonders glücklich mit den vorhandenen Bibliotheken, aber die Geschmäcker sind verschieden.

Meine Bibliothek MPU9250_WE findet ihr hier auf GitHub oder ihr installiert sie direkt über die Bibliotheksverwaltung der Arduino IDE.

Das Problem mit dem MPU9250 ist, dass er – bedingt durch seine 4 Sensoren plus I2C Subnetzsteuerung – unglaublich viele Einstellmöglichkeiten hat. Es stellt sich in solchen Fällen die Frage, ob man wirklich alles das, was möglich ist,  auch tatsächlich implementiert. Und obwohl ich das nicht getan habe, umfasst meine Bibliothek stolze neunundfünfzig öffentliche Funktionen. Um die Verwendung dieser Funktionen zu erklären, habe ich elf Beispielsketche verfasst.

Einführung in die Bibliothek MPU9250_WE

Die Basisdaten

Um die Bibliothek bzw. den MPU9250 kennenzulernen, empfehle ich die Sketche in der hier verwendeten Reihenfolge auszuprobieren. Wir starten mit den Basisdaten für Beschleunigung, Gyroskop, Magnetometer und Thermometer. Das ist vergleichsweise trockene Lektüre! An eurer Stelle würde ich mir einen MPU9250 besorgen und die Beispielsketche parallel ausprobieren.

Beispielsketch 1: MPU9250_acceleration_data

Auf diesen ersten Sketch gehe ich am intensivsten ein. Vieles wiederholt sich in anderen Sketchen.

Vorab noch eine Bemerkung zu dem Datenformat „xyzFloat“. Dabei handelt es sich um eine Struktur (struct), die aus drei Floatwerten besteht. Ich benutze xyzFloat für alle Daten, die eine x-, y- und z-Komponente haben.

#include <MPU9250_WE.h>
#include <Wire.h>
#define MPU9250_ADDR 0x68

/* There are several ways to create your MPU9250 object:
 * MPU9250_WE myMPU9250 = MPU9250_WE()              -> uses Wire / I2C Address = 0x68
 * MPU9250_WE myMPU9250 = MPU9250_WE(MPU9250_ADDR)  -> uses Wire / MPU9250_ADDR
 * MPU9250_WE myMPU9250 = MPU9250_WE(&wire2)        -> uses the TwoWire object wire2 / MPU9250_ADDR
 * MPU9250_WE myMPU9250 = MPU9250_WE(&wire2, MPU9250_ADDR) -> all together
 * Successfully tested with two I2C busses on an ESP32
 */
MPU9250_WE myMPU9250 = MPU9250_WE(MPU9250_ADDR);

void setup() {
  Serial.begin(115200);
  Wire.begin();
  if(!myMPU9250.init()){
    Serial.println("MPU9250 does not respond");
  }
  else{
    Serial.println("MPU9250 is connected");
  }
  
  /* The slope of the curve of acceleration vs measured values fits quite well to the theoretical 
   * values, e.g. 16384 units/g in the +/- 2g range. But the starting point, if you position the 
   * MPU9250 flat, is not necessarily 0g/0g/1g for x/y/z. The autoOffset function measures offset 
   * values. It assumes your MPU9250 is positioned flat with its x,y-plane. The more you deviate 
   * from this, the less accurate will be your results.
   * The function also measures the offset of the gyroscope data. The gyroscope offset does not   
   * depend on the positioning.
   * This function needs to be called at the beginning since it can overwrite your settings!
   */
  Serial.println("Position you MPU9250 flat and don't move it - calibrating...");
  delay(1000);
  myMPU9250.autoOffsets();
  Serial.println("Done!");
  
  /*  This is a more accurate method for calibration. You have to determine the minimum and maximum 
   *  raw acceleration values of the axes determined in the range +/- 2 g. 
   *  You call the function as follows: setAccOffsets(xMin,xMax,yMin,yMax,zMin,zMax);
   *  Use either autoOffset or setAccOffsets, not both.
   */
  //myMPU9250.setAccOffsets(-14240.0, 18220.0, -17280.0, 15590.0, -20930.0, 12080.0);

  /*  Sample rate divider divides the output rate of the gyroscope and accelerometer.
   *  Sample rate = Internal sample rate / (1 + divider) 
   *  It can only be applied if the corresponding DLPF is enabled and 0<DLPF<7!
   *  Divider is a number 0...255
   */
  myMPU9250.setSampleRateDivider(5);
  
  /*  MPU9250_ACC_RANGE_2G      2 g   
   *  MPU9250_ACC_RANGE_4G      4 g
   *  MPU9250_ACC_RANGE_8G      8 g   
   *  MPU9250_ACC_RANGE_16G    16 g
   */
  myMPU9250.setAccRange(MPU9250_ACC_RANGE_2G);

  /*  Enable/disable the digital low pass filter for the accelerometer 
   *  If disabled the the bandwidth is 1.13 kHz, delay is 0.75 ms, output rate is 4 kHz
   */
  myMPU9250.enableAccDLPF(true);

  /*  Digital low pass filter (DLPF) for the accelerometer, if enabled 
   *  MPU9250_DPLF_0, MPU9250_DPLF_2, ...... MPU9250_DPLF_7 
   *   DLPF     Bandwidth [Hz]      Delay [ms]    Output rate [kHz]
   *     0           460               1.94           1
   *     1           184               5.80           1
   *     2            92               7.80           1
   *     3            41              11.80           1
   *     4            20              19.80           1
   *     5            10              35.70           1
   *     6             5              66.96           1
   *     7           460               1.94           1
   */
  myMPU9250.setAccDLPF(MPU9250_DLPF_6);

  /*  Set accelerometer output data rate in low power mode (cycle enabled)
   *   MPU9250_LP_ACC_ODR_0_24          0.24 Hz
   *   MPU9250_LP_ACC_ODR_0_49          0.49 Hz
   *   MPU9250_LP_ACC_ODR_0_98          0.98 Hz
   *   MPU9250_LP_ACC_ODR_1_95          1.95 Hz
   *   MPU9250_LP_ACC_ODR_3_91          3.91 Hz
   *   MPU9250_LP_ACC_ODR_7_81          7.81 Hz
   *   MPU9250_LP_ACC_ODR_15_63        15.63 Hz
   *   MPU9250_LP_ACC_ODR_31_25        31.25 Hz
   *   MPU9250_LP_ACC_ODR_62_5         62.5 Hz
   *   MPU9250_LP_ACC_ODR_125         125 Hz
   *   MPU9250_LP_ACC_ODR_250         250 Hz
   *   MPU9250_LP_ACC_ODR_500         500 Hz
   */
  //myMPU9250.setLowPowerAccDataRate(MPU9250_LP_ACC_ODR_500);

  /* sleep() sends the MPU9250 to sleep or wakes it up. 
   * Please note that the gyroscope needs 35 milliseconds to wake up.
   */
  //myMPU9250.sleep(true);

 /* If cycle is set, and standby or sleep are not set, the module will cycle between
   *  sleep and taking a sample at a rate determined by setLowPowerAccDataRate().
   */
  //myMPU9250.enableCycle(true);

  /* You can enable or disable the axes for gyrometer and/or accelerometer measurements.
   * By default all axes are enabled. Parameters are:  
   * MPU9250_ENABLE_XYZ  //all axes are enabled (default)
   * MPU9250_ENABLE_XY0  // X, Y enabled, Z disabled
   * MPU9250_ENABLE_X0Z   
   * MPU9250_ENABLE_X00
   * MPU9250_ENABLE_0YZ
   * MPU9250_ENABLE_0Y0
   * MPU9250_ENABLE_00Z
   * MPU9250_ENABLE_000  // all axes disabled
   */
  //myMPU9250.enableAccAxes(MPU9250_ENABLE_XYZ);
  
}

void loop() {
  xyzFloat accRaw = myMPU9250.getAccRawValues();
  xyzFloat accCorrRaw = myMPU9250.getCorrectedAccRawValues();
  xyzFloat gValue = myMPU9250.getGValues();
  float resultantG = myMPU9250.getResultantG(gValue);
  
  Serial.println("Raw acceleration values (x,y,z):");
  Serial.print(accRaw.x);
  Serial.print("   ");
  Serial.print(accRaw.y);
  Serial.print("   ");
  Serial.println(accRaw.z);

  Serial.println("Corrected ('calibrated') acceleration values (x,y,z):");
  Serial.print(accCorrRaw.x);
  Serial.print("   ");
  Serial.print(accCorrRaw.y);
  Serial.print("   ");
  Serial.println(accCorrRaw.z);

  Serial.println("g values (x,y,z):");
  Serial.print(gValue.x);
  Serial.print("   ");
  Serial.print(gValue.y);
  Serial.print("   ");
  Serial.println(gValue.z);

  Serial.print("Resultant g: ");
  Serial.println(resultantG); // should always be 1 g if only gravity acts on the sensor.
  Serial.println();
  
  delay(1000);
}

 

Initialisierung und Offsets

Die Funktion init() führt zunächst ein Reset des MPU9250 durch und schreibt Defaultwerte in einige Register. init() liefert false zurück, wenn der MPU9250 nicht ansprechbar sein sollte, ansonsten true.

Wenn der MPU9250 flach, also mit seiner x,y-Ebene, aufliegt, wirkt nur die Erdbeschleunigung auf ihn. Entsprechend sollte der g-Wert für die x- und y-Achse null und für die z-Achse eins betragen. Diese Werte sind aber mehr oder weniger weit verschoben. Die Funktion autoOffset() misst die Offsetwerte und zieht sie bei zukünftigen Messungen ab. Die Steigung hingegen stimmte bei den von mir untersuchten Modulen sehr gut. Im 2g Messbereich lag die Differenz der minimalen und maximalen Roh-Beschleunigungswerte ziemlich nah an den erwarteten 2 x 16348 (also 2x 1015). autoOffset() funktioniert nur vernünftig, wenn

  • das Modul flach mit seiner x,y-Ebene aufliegt
  • nicht bewegt wird
  • die Funktion im Setup als erste Funktion aufgerufen wird (da sie bestimmte Einstellungen verändert)

Alternativ könnt ihr die Funktion setAccOffsets() verwenden. Sie führt zu weniger guten Ergebnissen bzgl. des Nullpunkts, ist aber bei größeren Neigungen akkurater und ihr müsst das Modul bei Programmstart nicht ausrichten.

Weitere Einstellungen

Die Funktion setAccRange() legt den Messbereich für die Beschleunigungsmessung fest.

Um die Datenrate zu steuern, verwendet ihr die Funktion setSampleRateDivider(divider).

\text{data rate}=\frac{1}{1+divider}\;[\text{kHz}]

Das funktioniert aber nur, wenn der digitale Tiefpassfilter (digital low pass filter = DLPF) aktiviert ist und dessen Modus 1 bis 6 ist. Die (De-)Aktivierung des DLPF erfolgt mit enableAccDLPF(true/false). Die Stufe wählt ihr mit setAccDLPF(). Je höher die Stufe, desto geringer ist das Rauschen. Nur die Stufe 7 fällt diesbezüglich aus der Reihe. Der Nachteil einer hohen Glättung ist eine gewisse Trägheit. Das heißt, dass es bei Änderung der Beschleunigung eine gewisse Zeit dauert, bis ihr den korrekten Wert erreicht. Details zu den Verzögerungen findet ihr im Sketch.

Mit sleep() versetzt ihr den Beschleunigungssensor und das Gyroskop in den Schlaf. Das gilt aber nicht für das Magnetometer, denn das führt ein gewisses Eigenleben.

Wenn ihr nicht alle Achsen aktivieren wollt, dann könnt ihr diese Einstellung mit enableAccAxes() vornehmen. Die Parameter sind im Sketch erklärt.

Die Funktionen setCycle() und setLowPowerAccDataRate() erläutere ich an anderer Stelle.

Die Ergebnisse

Die Messergebnisse fragt ihr mit den folgenden Funktionen ab:

  • getAccRawValues() liefert die Rohwerte der Beschleunigung, so wie sie im MPU9250 Datenregister stehen
  • getCorrectedAccRawValues() liest die Rohwerte und korrigiert sie um die Offsets
  • getGValues() liefert die Beschleunigungswerte in g (basiert auf den korrigierten Rohdaten)
  • getResultantG(gValue) berechnet die resultierende Beschleunigung aus einem g-Wertetripel, also den Betrag der Summe der drei Vektoren
\text{resultantG}=\sqrt{(gValue.x)^2+(gValue.y)^2+(gValue.z)^2} \;\;\text{[g]}

Wirkt nur die Schwerkraft auf den MPU9250, sollte die Resultierende immer 1 sein. Mit der Resultierenden könnt ihr bequem Beschleunigungen messen, ohne die Bewegung auf eine Achse ausrichten zu müssen. Zieht einfach 1 ab um die Erdbeschleunigung herauszurechnen.

Die Ausgabe zu MPU9250_acceleration_data.ino

Und so sieht dann das Ergebnis aus:

MPU9250 Beispielsketch: Ausgabe von MPU9250_acceleration_data.ino
Ausgabe von MPU9250_acceleration_data.ino

Beispielsketch 2: MPU9250_gyroscope_data

Da ihr nun schon einige Funktionen kennt, kann die Erklärung zu den Gyroskop Funktionen deutlich kürzer ausfallen.

#include <MPU9250_WE.h>
#include <Wire.h>
#define MPU9250_ADDR 0x68

/* There are several ways to create your MPU9250 object:
 * MPU9250_WE myMPU9250 = MPU9250_WE()              -> uses Wire / I2C Address = 0x68
 * MPU9250_WE myMPU9250 = MPU9250_WE(MPU9250_ADDR)  -> uses Wire / MPU9250_ADDR
 * MPU9250_WE myMPU9250 = MPU9250_WE(&wire2)        -> uses the TwoWire object wire2 / MPU9250_ADDR
 * MPU9250_WE myMPU9250 = MPU9250_WE(&wire2, MPU9250_ADDR) -> all together
 * Successfully tested with two I2C busses on an ESP32
 */
MPU9250_WE myMPU9250 = MPU9250_WE(MPU9250_ADDR);

void setup() {
  Serial.begin(115200);
  Wire.begin();
  if(!myMPU9250.init()){
    Serial.println("MPU9250 does not respond");
  }
  else{
    Serial.println("MPU9250 is connected");
  }

  /* The slope of the curve of acceleration vs measured values fits quite well to the theoretical 
   * values, e.g. 16384 units/g in the +/- 2g range. But the starting point, if you position the 
   * MPU9250 flat, is not necessarily 0g/0g/1g for x/y/z. The autoOffset function measures offset 
   * values. It assumes your MPU9250 is positioned flat with its x,y-plane. The more you deviate 
   * from this, the less accurate will be your results.
   * The function also measures the offset of the gyroscope data. The gyroscope offset does not   
   * depend on the positioning.
   * This function needs to be called at the beginning since it can overwrite your settings!
   */
  Serial.println("Position you MPU9250 flat and don't move it - calibrating...");
  delay(1000);
  myMPU9250.autoOffsets();
  Serial.println("Done!");
  
  /*  The gyroscope data is not zero, even if you don't move the MPU9250. 
   *  To start at zero, you can apply offset values. These are the gyroscope raw values you obtain
   *  using the +/- 250 degrees/s range. 
   *  Use either autoOffset or setGyrOffsets, not both.
   */
  //myMPU9250.setGyrOffsets(45.0, 145.0, -105.0);
  
 /*  You can enable or disable the digital low pass filter (DLPF). If you disable the DLPF, you 
   *  need to select the bandwidth, which can be either 8800 or 3600 Hz. 8800 Hz has a shorter delay,
   *  but higher noise level. If DLPF is disabled, the output rate is 32 kHz.
   *  MPU9250_BW_WO_DLPF_3600 
   *  MPU9250_BW_WO_DLPF_8800
   */
  myMPU9250.enableGyrDLPF();
  //myMPU9250.disableGyrDLPF(MPU9250_BW_WO_DLPF_8800); // bandwidth without DLPF
  
  /*  Digital Low Pass Filter for the gyroscope must be enabled to choose the level. 
   *  MPU9250_DPLF_0, MPU9250_DPLF_2, ...... MPU9250_DPLF_7 
   *  
   *  DLPF    Bandwidth [Hz]   Delay [ms]   Output Rate [kHz]
   *    0         250            0.97             8
   *    1         184            2.9              1
   *    2          92            3.9              1
   *    3          41            5.9              1
   *    4          20            9.9              1
   *    5          10           17.85             1
   *    6           5           33.48             1
   *    7        3600            0.17             8
   *    
   *    You achieve lowest noise using level 6  
   */
  myMPU9250.setGyrDLPF(MPU9250_DLPF_6);

  /*  Sample rate divider divides the output rate of the gyroscope and accelerometer.
   *  Sample rate = Internal sample rate / (1 + divider) 
   *  It can only be applied if the corresponding DLPF is enabled and 0<DLPF<7!
   *  Divider is a number 0...255
   */
  myMPU9250.setSampleRateDivider(99);


  /*  MPU9250_GYRO_RANGE_250       250 degrees per second (default)
   *  MPU9250_GYRO_RANGE_500       500 degrees per second
   *  MPU9250_GYRO_RANGE_1000     1000 degrees per second
   *  MPU9250_GYRO_RANGE_2000     2000 degrees per second
   */
  myMPU9250.setGyrRange(MPU9250_GYRO_RANGE_250);
  
  /* sleep() sends the MPU9250 to sleep or wakes it up. 
   * Please note that the gyroscope needs 35 milliseconds to wake up.
   */
  //myMPU9250.sleep(true);

  /* This is a low power standby mode for the gyro function, which allows quick enabling. 
   * (see data sheet for further information)
   */
  //myMPU9250.enableGyrStandby(true);

  
  /* You can enable or disable the axes for gyroscope and/or accelerometer measurements.
   * By default all axes are enabled. Parameters are:  
   * MPU9250_ENABLE_XYZ  //all axes are enabled (default)
   * MPU9250_ENABLE_XY0  // X, Y enabled, Z disabled
   * MPU9250_ENABLE_X0Z   
   * MPU9250_ENABLE_X00
   * MPU9250_ENABLE_0YZ
   * MPU9250_ENABLE_0Y0
   * MPU9250_ENABLE_00Z
   * MPU9250_ENABLE_000  // all axes disabled
   */
  //myMPU9250.enableGyrAxes(MPU9250_ENABLE_000);  
}

void loop() {
  xyzFloat gyrRaw = myMPU9250.getGyrRawValues();
  xyzFloat corrGyrRaw = myMPU9250.getCorrectedGyrRawValues();
  xyzFloat gyr = myMPU9250.getGyrValues();
  
  Serial.println("Gyroscope raw values (x,y,z):");
  Serial.print(gyrRaw.x);
  Serial.print("   ");
  Serial.print(gyrRaw.y);
  Serial.print("   ");
  Serial.println(gyrRaw.z);

  Serial.println("Corrected gyroscope raw values (x,y,z):");
  Serial.print(corrGyrRaw.x);
  Serial.print("   ");
  Serial.print(corrGyrRaw.y);
  Serial.print("   ");
  Serial.println(corrGyrRaw.z);

  Serial.println("Gyroscope Data in degrees/s (x,y,z):");
  Serial.print(gyr.x);
  Serial.print("   ");
  Serial.print(gyr.y);
  Serial.print("   ");
  Serial.println(gyr.z);

  Serial.println("*********************************");

  delay(1000);
}

 

Die Einstellungen für das Gyroskop

Auch hier könnt ihr die autoOffsets() Funktion verwenden. Das Gyroskop sollte im nicht bewegten Zustand für alle Achsen den Messwert null liefern, oder zumindest um null herum schwanken. Ihr werdet aber einen gewissen (lageunabhängigen) Offset feststellen, den diese Funktion ermittelt. Alternativ setzt ihr die im Messbereich +/-250 °/s ermittelten Werte in setGyrOffsets() ein.

Den Tiefpassfilter (DLPF) aktiviert ihr mit enableGyrDLPF(). Wenn ihr den DLPF deaktiviert, könnt ihr trotzdem zwischen den zwei Bandbreiten 8800 und 3600 Hz wählen. Deswegen müsst ihr der Funktion disableGyrDLPF() entsprechende Parameter übergeben. Die Stärke des Tiefpassfilters wählt ihr mit setGyrDLPF(). Auch hier führt ein stärkerer Filter zu größeren Verzögerungen. Ebenso fällt die Stufe 7 wieder aus der Reihe.

Weitere Funktionen:

  • setSampleRateDivider() funktioniert wie beim Beschleunigungssensor.
  • setGyrRange() legt den Messbereich fest.
  • enableGyrStandby() schickt das Gyrometer in eine Art Halbschlaf, der ein schnelleres Aufwachen als aus dem Schlafmodus ermöglicht (leider habe ich keine Zeitangabe gefunden).
  • enableGyrAxes() aktiviert bzw. deaktiviert einzelne Achsen.
  • getGyrRawValues() liefert die aktuell vorhandenen Rohdaten.
  • getCorrectedGyrRawValues() zieht die Offsets von den Rohdaten ab und liefert die korrigierten Daten.
  • getGyrValues() liefert die Gyroskop Daten in Grad/Sekunde, basierend auf den korrigierten Rohdaten.
Die Ausgabe zu MPU9250_gyroscope_data

Die ersten Werte in der Ausgabe unten wurden ohne Bewegung ermittelt. Für den zweiten Satz an Werten habe ich das Modul um seine x-Achse gedreht.

MPU9250 Beispielsketch: Ausgabe von MPU9250_gyroscope_data.ino
Ausgabe von MPU9250_gyroscope_data.ino

Beispielsketch 3: MPU9250_magnetometer_data.ino

Das Magnetometer (AK8963) verhält sich wie ein separates Bauteil. Es hat eine eigene I2C Adresse (0x0C), eigene Register und muss deswegen auch separat initialisiert werden. Wenn ihr einen I2C Scanner auf den MPU9250 anwendet, werdet ihr aber nur seine Adresse sehen. Das Magnetometer liegt nämlich nicht direkt zugänglich hinter dem MPU9250.  Die Ansteuerung läuft über eine Art I2C Subnetz, das über den MPU9250 eingerichtet wird. Davon bekommt ihr allerdings nichts mit, da die Bibliothek diese Aufgaben übernimmt.

#include <MPU9250_WE.h>
#include <Wire.h>
#define MPU9250_ADDR 0x68

/* There are several ways to create your MPU9250 object:
 * MPU9250_WE myMPU9250 = MPU9250_WE()              -> uses Wire / I2C Address = 0x68
 * MPU9250_WE myMPU9250 = MPU9250_WE(MPU9250_ADDR)  -> uses Wire / MPU9250_ADDR
 * MPU9250_WE myMPU9250 = MPU9250_WE(&wire2)        -> uses the TwoWire object wire2 / MPU9250_ADDR
 * MPU9250_WE myMPU9250 = MPU9250_WE(&wire2, MPU9250_ADDR) -> all together
 * Successfully tested with two I2C busses on an ESP32
 */
MPU9250_WE myMPU9250 = MPU9250_WE(MPU9250_ADDR);

void setup() {
  Serial.begin(115200);
  Wire.begin();
  if(!myMPU9250.init()){
    Serial.println("MPU9250 does not respond");
  }
  else{
    Serial.println("MPU9250 is connected");
  }
  if(!myMPU9250.initMagnetometer()){
    Serial.println("Magnetometer does not respond");
  }
  else{
    Serial.println("Magnetometer is connected");
  }

  /* You can choose the following operational modes
   * AK8963_PWR_DOWN            power down (default)
   * AK8963_CONT_MODE_8HZ       continuous at 8Hz sample rate
   * AK8963_CONT_MODE_100HZ     continuous at 100Hz sample rate 
   * 
   * In trigger mode the AK8963 goes into power down after the measurement
   */
  myMPU9250.setMagOpMode(AK8963_CONT_MODE_100HZ);
  
  /* In continuous mode you need to wait for the first data to be available. If you 
   * comment the line below you will probably obtain zero. 
   */
  delay(200);
}

void loop() {
  xyzFloat magValue = myMPU9250.getMagValues(); // returns magnetic flux density [µT] 

  Serial.println("Magnetometer Data in µTesla: ");
  Serial.print(magValue.x);
  Serial.print("   ");
  Serial.print(magValue.y);
  Serial.print("   ");
  Serial.println(magValue.z);

  delay(1000);
}

 

Die Einstellungen für das Magnetometer

Das Magnetometer hat einen Power-Down Modus, einen Trigger Modus und zwei kontinuierliche Modi. Die kontinuierlichen Modi unterscheiden sich in der Datenrate, nämlich 8 oder 100 Hz. Ihr stellt den Modus mit setMagOpMode() ein.

Die Magnetometerwerte erhaltet ihr mit getMagValues(). Das Ergebnis wird in Mikrotesla ausgegeben. Die Funktion liefert die Werte, die sich gerade im Datenspeicher befinden. Im 8 Hz Modus können die Daten also bis zu 125 ms „alt“ sein.  Beim Einschalten messt ihr unter Umständen null. Um das zu verhindern, habe ich ein delay eingefügt.

Ausgabe von MPU9250_magnetometer_data
MPU9250 Beispielsketch: Ausgabe von MPU9250_magnetometer_data.ino
Ausgabe von MPU9250_gyroscope_data.ino

Die Ergebnisse waren zunächst etwas ernüchternd. Hatte ich doch erwartet, ich könnte den MPU9250 ganz einfach als Kompass einsetzen.

In Mitteleuropa liegt die magnetische Flussdichte des Erdmagnetfeldes bei ca. 20 µT in der Horizontalen und um 44 µT in der Vertikalen (Quelle: Wikipedia). Dementsprechend sollte die Differenz zwischen dem maximalen und dem minimalen Wert bei Drehung in der Horizontalen oder in der Vertikalen bei 40 beziehungsweise 88 µT liegen. Das werdet ihr – so wie ich auch – wahrscheinlich so nicht messen, denn:

  1. Es gibt auch hier Offsets.
  2. Ihr messt wahrscheinlich drinnen, und je nach Bauweise des Hauses wird das Erdmagnetfeld abgeschirmt.
  3. Wenn ihr auf dem Breadboard messt, können die Steckbrückenkabel und die Metallleitungen des Breadboards die Messungen beeinflussen.

Ich hatte dann auch noch mit starken Magneten herumgespielt und so mein Breadboard magnetisiert. Erst bei Anschluss mit Steckbrückenkabeln, nur von unten und ohne Breadboard gaben die Ergebnisse dann mehr Sinn.

Ich habe es (noch) nicht weiter verfolgt, das Magnetometer als Kompass einzusetzen. Nach dem, was ich in verschiedenen Anleitungen für andere Magnetometer gelesen habe, scheint das eine Wissenschaft für sich zu sein. Zumindest, wenn man nicht einfach nur Norden und Süden finden will.

Beispielsketch 4: MPU9250_all_data.ino

Mithilfe dieses Sketches bestimmt ihr die Beschleunigung, die Gyroskopwerte, die Magnetometerwerte und die Temperatur. Die Temperatur ruft ihr mit getTemperature() ab, die anderen Funktionen habe ich schon erklärt. Ihr findet den Sketch in den Beispielen.

Das Thermometer dient weniger dazu, die Umgebungstemperatur zu messen, als die Temperaturentwicklung im MPU9250 zu verfolgen. Sie liegt höher als die Raumtemperatur und steigt im Betrieb abhängig von den Messbedingungen.

Offsets dauerhaft einstellen

Beispielsketch 5: MPU9250_calibration

Dieser Sketch soll euch helfen, die Offsets für den Beschleunigungssensor und das Gyroskop zu ermitteln. Dazu wird zunächst der jeweils geringste Messbereich und der maximale Tiefpassfilter eingestellt. Das gewährleistet hohe Auflösung und geringes Rauschen.

Für die Offsetermittlung des Beschleunigungssensors dreht ihr den MPU9250 langsam (!) um seine Achsen und notiert euch die maximalen und minimalen Rohwerte. Am besten stützt ihr dabei die Arme auf, denn jedes Zittern gibt zusätzliche Beschleunigung. Diese Daten benutzt ihr dann für die setAccOffsets() Funktion. Intern werden daraus die Offsets ermittelt. Wenn ihr den Messbereich wechselt, werden die Offsets automatisch angepasst. Mit dieser Methode sei ihr dann nicht mehr auf die ebene Ausrichtung des MPU9250 bei Programmstart angewiesen. Für die Ermittlung präziser kleiner Neigungswinkel würde ich aber weiterhin die autoOffsets() Funktion empfehlen.

Für die Gyroskop Offsets braucht ihr den MPU9250 nicht zu drehen, denn der Offset ist unabhängig von der Neigung. Hier schreibt ihr die Ruhewerte für die x-, y- und z-Achse in die Funktion setGyrOffsets().

Ihr findet den Sketch in den Beispielen. Ich drucke ihn hier nicht noch einmal ab.

Die Ausgabe zu MPU9250_calibration.ino
Beispielsketch MPU9250:
Ausgabe von MPU9250_calibration.ino
Ausgabe von MPU9250_calibration.ino

Update 2024: Wiederverwendung der autoOffsets()-Daten

Ich habe die Funktionen getAccOffsets() und getGyrOffsets() hinzugefügt, mit denen ihr die Kalibrierdaten der autoOffset() Funktion abfragen könnt. Damit müsst ihr autoOffset() nicht bei jedem Start erneut ausführen. Der Beispielsketch MPU9250_reusing_autocalib_data.ino zeigt, wie es geht.

Ausblick

Im zweiten Teil des Beitrages geht es dann um Winkel, Interrupts, den Low Power Modus und den Fifo Puffer. Ihr müsst natürlich nicht warten, sondern könnt die Beispielsketche jetzt schon ausprobieren.

Danksagung

Den MPU9250 als Fritzing Bauteil habe ich hier gefunden – Danke an Randy!

Die Teile für das Beitragsbild habe ich wie üblich auf Pixabay gefunden. Der Kompass, das Space-Shuttle und das Thermometer gab es dort bei OpenClipart-Vectors. Der Kreisel stammt von InspiredImages (Anthony).

9 thoughts on “MPU9250 – 9-Achsen Sensormodul – Teil 1

  1. Hallo, ich wollte fragen ob es möglich ist auch ohne Kallibrieren des Sensors (Flach auflegen), möglich ist korrekte Werte zu erhalten.
    mfg

    1. Hi, kommt darauf an, was du machen willst und welche Abweichungen für dich akzeptabel sind. Wenn du nur Differenzen messen möchtest, musst du nichts kalibrieren. Willst du Winkel absolut und exakt messen, dann solltest du es tun. Probiere es einfach aus, würde ich sagen. Lass die Kalibrierung weg und schau, ob dir die Ausgangswerte passen. Wenn du nicht jedes Mal kalibrieren willst, dann kannst du die Kalibrierwerte auch speichern. Der Sketch MPU9250_reusing_autocalib_data.ino zeigt, wie es geht.
      VG, Wolfgang

  2. Hallo, ich habe gerade 4 sample MPU9250 von unterschiedlichen suppliern bekommen. Gyro und Accel prima, Magnetometerwerte sind immer ‚0‘. AK8963 scheint nicht zu antworten. Init schlägt fehl, Auslesen von Registern ( z.B. WIA) gibt immer ‚0‘. Auskommentieren der init Prozedur für AK8963 bringt keine Änderung.
    Ich habe mal die I2C Adr. bzw. Frequenz des internen I2C Busses geändert – kein Änderung. Ich betreibe die Schaltung mit einem ESP32 und den Bibliotheken aus diesem Bericht. Gibt es da noch einen Tipp was ich machen kann ? VG Dieter

    1. In der neuesten Version der Bibliothek (1.2.4) habe ich den Sketch „MPU9250_who_am_I.ino“ zugefügt, mit dem du testen kannst, welcher MPUxxxx sich auf deinem Modul befindet. Die Version 1.2.4 bzw. der Sketch ist auf GitHub verfügbar. Die Arduino IDE braucht für gewöhnlich bis zu 24 Stunden, bis die neue Version verfügbar ist.

  3. Hallo Wolfgang, wie kann ich die zweite I2c Schnittstelle in deiner Bibliothek aufrufen?
    VG Johannes

    1. Hallo Johannes,

      mit einem ESP32 könntest du es so machen:

      …..
      #define MPU9250_ADDR 0x68
      #define I2C_FREQ 400000

      TwoWire I2C_1 = TwoWire(0);
      TwoWire I2C_2 = TwoWire(1);

      #define SDA_1 15
      #define SCL_1 16
      #define SDA_2 17
      #define SCL_2 18

      TwoWire I2C_1 = TwoWire(0);
      TwoWire I2C_2 = TwoWire(1);

      MPU9250_WE myMPU9250_1 = MPU9250_WE(&I2C_1, MPU9250_ADDR)
      MPU9250_WE myMPU9250_2 = MPU9250_WE(&I2C_2, MPU9250_ADDR)

      void setup() {
      I2C_1.begin(SDA_1, SCL_1, I2C_FREQ);
      I2C_2.begin(SDA_2, SCL_2, I2C_FREQ);
      …..

      Mehr zu dem Thema gibt es hier:
      https://wolles-elektronikkiste.de/i2c-schnittstellen-des-esp32-nutzen

      VG, Wolfgang

  4. Hallo, in welcher Einheit erhalte ich die Beschleunigungsdaten durch folgende Zeilen?
    Serial.println(„Corrected (‚calibrated‘) acceleration values (x,y,z):“);
    Serial.print(accCorrRaw.x);

    1. Hallo,
      das sind dimensionslose Rohdaten. Sie erhalten ihre Bedeutung erst in Verbindung mit dem Messbereich. So wäre z.B. der Rohwert +32767 = 2 g, wenn als Messereich +/-2g gewählt wurde, aber er wäre 4g, wenn +/-4 g der aktuelle Messbereich ist.
      VG, Wolfgang

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert