MPU6050 Beschleunigungssensor und Gyroskop

Über den Beitrag

Der MPU6050, oder genauer gesagt das darauf basierende Modul, ist ein 3-Achsen Beschleunigungssensor und 3-Achsen Gyroskop. Darüber hinaus misst der MPU6050 die Temperatur. Es ist schon ziemlich viel über diesen Sensor geschrieben worden, aber vielleicht kann ich euch mit diesem Beitrag den einen oder anderen neuen Aspekt vermitteln, wie z.B. die Interrupt Programmierung.

Der Beitrag ist folgendermaßen gegliedert:

Wer einfach nur schnell „losmessen“ will, der kann direkt zum Teil mit den Bibliotheken gehen.

Grundlagen

Das Messprinzip des MPU6050

Im Zusammenhang mit Beschleunigungssensoren stoßt ihr häufig auf die Begriffe „3-Achsen“, „6-Achsen“ oder gar „9-Achsen Sensoren“. Wir verlassen dabei nicht die dreidimensionale Welt. Vielmehr kommen ein oder mehrere, in drei Dimensionen wirksame Sensoren parallel zum Einsatz. Und das sind im Falle des MPU6050 ein Beschleunigungs- und ein Gyroskop. 9-Achsen Sensoren haben meistens noch einen Erdmagnetsensor.

Gyroskope

Das klassische Gyroskop wird auch Kreiselinstrument genannt. Einen Kreisel kennt jeder und auch seine Wirkung. Die Drehung stabilisiert ihn und der Versuch, seine Drehachse zu kippen, erfordert Kraft. Und genau das kann man sich zunutze machen, um die Lage eines Gegenstandes (z.B. eines Flugzeuges oder Schiffes) im Raum zu bestimmen. Gegenüber klassischen Kreiseln ist bei Gyroskopen der Kreisel in einem Rahmen angebracht. Hier als Spielzeug:

Ein Spielzeug Gyroskop
Ein Spielzeug Gyroskop

Im MPU6050 gibt es natürlich keinen Kreisel (und glücklicherweise müsst ihr ihn auch nicht aufziehen ;-)). In solchen Sensoren befinden sich „Micro-Electric-Mechanical Systems“ (MEMS). Im Fall von Gyroskopen sind das beweglich aufgehängte Körper, die bei Beschleunigung ihre Lage gegenüber einem festen Rahmen verändern. Die Abstandsänderung führt zu einer Änderung der Kapazität. Im Prinzip sieht das so aus:

Das Messprinzip des MPU6050
Das Messprinzip des MPU6050

Eine ältere Technik beruht auf dem Piezoeffekt, nutzt also Druckänderungen als Messprinzip.

Beschleunigungssensor

Ein Beschleunigungssensor funktioniert nach demselben Prinzip. Der Unterschied ist lediglich, dass der Beschleunigungssensor die Beschleunigung in Richtung der x-, y- und z-Achse detektiert, wohingegen das Gyroskop die Bewegung um die Achsen detektiert. Wenn das Modul in Ruhe ist, liefert das Gyroskop für x, y und z den Wert Null liefern. Der Beschleunigungssensor hingegen detektiert die Erdbeschleunigung auch im Ruhezustand (in z-Richtung, wenn das Modul flach liegt).

Die Module haben die Lage der x- und y-Achse aufgedruckt. Die z-Achse steht senkrecht auf dem Modul.

MPU6050 Modul
MPU6050 Modul

Technische Daten / Eigenschaften

Die wichtigsten technischen Daten des MPU6050 sind:

  • Spannungsversorgung (VDD/GND): das Modul besitzt einen LDO Spannungskonverter (siehe auch hier), kann also sowohl mit 3,3 V wie auch mit 5 V versorgt werden. Der „nackte“ IC sollte mit 2,375 – 3,46 V versorgt werden.
  • Stromverbrauch (Modul): ca. 5,1 mA, im Sleep-Mode ca. 1,4 mA (eigene Messungen). Wenn ihr die LED entfernt, reduziert sich der Verbrauch auf 3,7 mA bzw. 33 µA im Sleep-Mode.
  • Beschleunigungssensor: 16-Bit Auflösung, Messbereiche +/- 2, 4, 8 oder 16 g; mit g = Erdbeschleunigung, nicht Gramm!
  • Gyroskop: 16-Bit Auflösung, Messbereiche: +/- 250, 500, 1000 oder 2000°/s.
  • Kommunikation (SDA/SCL): I2C, Adresse 0x68 (AD0 = GND oder unverbunden) oder 0x69 (AD0 = HIGH)
  • Interruptfunktion: „Data Ready“, „Free Fall“, Beschleunigungslimit;
  • Anschluss weiterer Sensoren (XDA / XCL): darauf werde ich nicht eingehen

Weitere Informationen findet ihr im Datenblatt und der Register Map.

Ansteuerung des MPU6050 ohne Bibliothek

Der Minimalsketch

Wenn ihr mit den Voreinstellungen (+/-2 g, +/-250°/s) leben könnt, dann kommt ihr vielleicht mit dem folgenden Sketch (Basis: Arduino Playground) aus. 

Ich werde nicht alle Details des Sketches durchgehen, nur so viel:

  • zunächst wird der MPU6050 durch den Eintrag einer Null in das „Power Management 1“ Register geweckt
  • der MPU6050 erzeugt kontinuierlich Ergebnisse für Beschleunigung, Gyroskop und Temperatur
  • da die Ergebnisregister direkt aufeinanderfolgen, lassen sie sich bequem in einem Rutsch auslesen
  • es werden nur Rohdaten ausgegeben (-32767 bis + 32767)
#include "Wire.h" 

#define MPU6050_ADDR 0x68 // Alternatively set AD0 to HIGH  --> Address = 0x69

int16_t accX, accY, accZ, gyroX, gyroY, gyroZ, tRaw; // Raw register values (accelaration, gyroscope, temperature)
char result[7]; // temporary variable used in convert function

void setup() {
  Serial.begin(9600);
  Wire.begin();
  Wire.beginTransmission(MPU6050_ADDR);
  Wire.write(0x6B); // PWR_MGMT_1 register
  Wire.write(0); // wake up!
  Wire.endTransmission(true);
}
void loop() {
  Wire.beginTransmission(MPU6050_ADDR);
  Wire.write(0x3B); // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false); // the parameter indicates that the Arduino will send a restart. 
                               // As a result, the connection is kept active.
  Wire.requestFrom(MPU6050_ADDR, 14, true); // request a total of 7*2=14 registers
  
  // "Wire.read()<<8 | Wire.read();" means two registers are read and stored in the same int16_t variable
  accX = Wire.read()<<8 | Wire.read(); // reading registers: 0x3B (ACCEL_XOUT_H) and 0x3C (ACCEL_XOUT_L)
  accY = Wire.read()<<8 | Wire.read(); // reading registers: 0x3D (ACCEL_YOUT_H) and 0x3E (ACCEL_YOUT_L)
  accZ = Wire.read()<<8 | Wire.read(); // reading registers: 0x3F (ACCEL_ZOUT_H) and 0x40 (ACCEL_ZOUT_L)
  tRaw = Wire.read()<<8 | Wire.read(); // reading registers: 0x41 (TEMP_OUT_H) and 0x42 (TEMP_OUT_L)
  gyroX = Wire.read()<<8 | Wire.read(); // reading registers: 0x43 (GYRO_XOUT_H) and 0x44 (GYRO_XOUT_L)
  gyroY = Wire.read()<<8 | Wire.read(); // reading registers: 0x45 (GYRO_YOUT_H) and 0x46 (GYRO_YOUT_L)
  gyroZ = Wire.read()<<8 | Wire.read(); // reading registers: 0x47 (GYRO_ZOUT_H) and 0x48 (GYRO_ZOUT_L)
  
  Serial.print("AcX = "); Serial.print(toStr(accX));
  Serial.print(" | AcY = "); Serial.print(toStr(accY));
  Serial.print(" | AcZ = "); Serial.print(toStr(accZ));
  // from data sheet:
  Serial.print(" | tmp = "); Serial.print((tRaw + 12412.0) / 340.0);
  Serial.print(" | GyX = "); Serial.print(toStr(gyroX));
  Serial.print(" | GyY = "); Serial.print(toStr(gyroY));
  Serial.print(" | GyZ = "); Serial.print(toStr(gyroZ));
  Serial.println();
  
  delay(1000);
}

char* toStr(int16_t character) { // converts int16 to string and formatting
  sprintf(result, "%6d", character);
  return result;
}

 

Unten seht ihr die Ausgabe. Da der MPU6050 flach auf in seiner x/y-Ebene lag, sollten eigentlich alle Ergebnisse mit Ausnahme der Beschleunigung in z-Richtung (Erdbeschleunigung) Null ergeben. Das ist nicht der Fall, aber qualitativ ist das Ergebnis schon nicht schlecht.

Ihr könnt die Ausgangswerte anpassen, indem ihr Offset Werte in dafür vorgesehene Register eintragt. Einige Bibliotheken haben das implementiert, andere nicht.

Ausgabe des Minimalsketches für den MPU6050
Ausgabe des Minimalsketches für den MPU6050

Der Vollständigkeit halber habe ich hier noch die Beschaltung abgebildet. Die LED und die Leitung zum Interrupt brauchen wir später noch. Pull-ups für die I2C Leitungen (3.3 V) sind auf dem Modul integriert.

Verkabelung des MPU6050 am Arduino
Verkabelung des MPU6050 am Arduino

Der erweiterte Minimalsketch

Ich habe dem Minimalsketch dann ein paar Extras spendiert:

  • setAccRange() legt den Messbereich für die Beschleunigung fest
  • setGyrRange() legt den Messbereich für das Gyroskop fest
  • MCP6050_wakeUp() weckt den MPU6050
    • Er braucht etwas Zeit zum Aufwachen, erst nach 30 ms habe ich sinnvolle Gyroskopwerte ermitteln können.
  • MCP6050_sleep schickt den MPU6050 in den Schlaf

Durch zwischenzeitlichen Schlaf habe ich den Stromverbrauch mit dem folgenden Sketch von 5,2 auf 1,4 mA drücken können. Wenn ihr dann noch die LED entfernt, geht der Verbrauch auf unter 0,1 mA. 

Wer den Sketch hinsichtlich der Registereinstellungen nachvollziehen möchte, der schaue in die Register Map.

#include "Wire.h" 

#define MPU6050_ADDR              0x68 // Alternatively set AD0 to HIGH  --> Address = 0x69
#define MPU6050_GYRO_CONFIG       0x1B ///< Gyro specfic configuration register
#define MPU6050_ACCEL_CONFIG      0x1C
#define MPU6050_ACCEL_XOUT_H      0x3B
#define MPU6050_PWR_MGT_1         0x6B
#define MPU6050_SLEEP             0x06

typedef enum {
  MPU6050_ACC_RANGE_2G,  // +/- 2g (default)
  MPU6050_ACC_RANGE_4G,  // +/- 4g
  MPU6050_ACC_RANGE_8G,  // +/- 8g
  MPU6050_ACC_RANGE_16G // +/- 16g
} mpu6050_acc_range;

typedef enum {
  MPU6050_GYR_RANGE_250,  // +/- 250 deg/s (default)
  MPU6050_GYR_RANGE_500,  // +/- 500 deg/s
  MPU6050_GYR_RANGE_1000, // +/- 1000 deg/s
  MPU6050_GYR_RANGE_2000  // +/- 2000 deg/s
} mpu6050_gyr_range;

int16_t accX, accY, accZ, gyroX, gyroY, gyroZ, tRaw; // Raw register values (accelaration, gyroscope, temperature)
char result[7];

void setup() {
  Serial.begin(9600);
  Wire.begin();
  MPU6050_wakeUp();
  setAccRange(MPU6050_ACC_RANGE_16G);
  setGyrRange(MPU6050_GYR_RANGE_250);
}

void loop() {
  MPU6050_wakeUp();
  Wire.beginTransmission(MPU6050_ADDR);
  Wire.write(MPU6050_ACCEL_XOUT_H); // starting with register ACCEL_XOUT_H
  Wire.endTransmission(false); // the parameter indicates that the Arduino will send a restart. 
                               // As a result, the connection is kept active.
  Wire.requestFrom(MPU6050_ADDR, 7*2, true); // request a total of 7*2=14 registers
  
  // "Wire.read()<<8 | Wire.read();" means two registers are read and stored in the same variable
  accX = Wire.read()<<8 | Wire.read(); // reading registers: 0x3B (ACCEL_XOUT_H) and 0x3C (ACCEL_XOUT_L)
  accY = Wire.read()<<8 | Wire.read(); // reading registers: 0x3D (ACCEL_YOUT_H) and 0x3E (ACCEL_YOUT_L)
  accZ = Wire.read()<<8 | Wire.read(); // reading registers: 0x3F (ACCEL_ZOUT_H) and 0x40 (ACCEL_ZOUT_L)
  tRaw = Wire.read()<<8 | Wire.read(); // reading registers: 0x41 (TEMP_OUT_H) and 0x42 (TEMP_OUT_L)
  gyroX = Wire.read()<<8 | Wire.read(); // reading registers: 0x43 (GYRO_XOUT_H) and 0x44 (GYRO_XOUT_L)
  gyroY = Wire.read()<<8 | Wire.read(); // reading registers: 0x45 (GYRO_YOUT_H) and 0x46 (GYRO_YOUT_L)
  gyroZ = Wire.read()<<8 | Wire.read(); // reading registers: 0x47 (GYRO_ZOUT_H) and 0x48 (GYRO_ZOUT_L)
  MPU6050_sleep();
  
  // print out data
  Serial.print("AcX = "); Serial.print(toStr(accX));
  Serial.print(" | AcY = "); Serial.print(toStr(accY));
  Serial.print(" | AcZ = "); Serial.print(toStr(accZ));
  // the following equation was taken from the documentation [MPU-6000/MPU-6050 Register Map and Description, p.30]
  Serial.print(" | tmp = "); Serial.print((tRaw + 12412.0) / 340.0);
  Serial.print(" | GyX = "); Serial.print(toStr(gyroX));
  Serial.print(" | GyY = "); Serial.print(toStr(gyroY));
  Serial.print(" | GyZ = "); Serial.print(toStr(gyroZ));
  Serial.println();
  
  delay(1000);
}

char* toStr(int16_t i) { // int16 to string plus output format
  sprintf(result, "%6d", i);
  return result;
}

void setAccRange(mpu6050_acc_range range){
  writeRegister(MPU6050_ACCEL_CONFIG, range<<3);
}

void setGyrRange(mpu6050_gyr_range range){
  writeRegister(MPU6050_GYRO_CONFIG, range<<3);
}

void MPU6050_wakeUp(){
  writeRegister(MPU6050_PWR_MGT_1, 0); 
  delay(30); // give him time to wake up, gyro needs quite a while to stabilize;
}

void MPU6050_sleep(){
  writeRegister(MPU6050_PWR_MGT_1, 1<<MPU6050_SLEEP); 
}

void writeRegister(uint16_t reg, byte value){
  Wire.beginTransmission(MPU6050_ADDR);
  Wire.write(reg); 
  Wire.write(value); 
  Wire.endTransmission(true);
}

 

Da ich in dem Beispiel die Range +/- 16 g gewählt habe, gehen die Messwerte für die Beschleunigung in z-Richtung auf ein Achtel zurück:

Ausgabe des erweiterten Minimalsketches
Ausgabe des erweiterten Minimalsketches

Ich hätte noch weitere Funktionen einfügen können, aber irgendwann wird es unübersichtlich und dann sind Bibliotheken die bessere Wahl.

Umrechnung der Rohdaten

Für die Umrechnung der Beschleunigungsrohdaten (rawValue) in g gilt:

\text{g-value}\;[\text{g}]=\frac{\vert range \vert\cdot rawV\!alue}{32768}

Die Umrechnung der Gyroskop Daten in Winkelgeschwindigkeiten erfolgt nach folgender Formel (aus dem Datenblatt abgeleitet):

\omega\;[\text{°\!/s}] = \frac{\vert range \vert\ \cdot rawV\!alue}{131\cdot 250}

Interrupts

Vielleicht macht ja das folgende, ganz kurze Video des „Magic Push Buttons“ Appetit auf die Interrupt Funktion, die übrigens in keiner Bibliothek, die ich mir angeschaut habe, implementiert war.

Wie ihr seht, bringt ein Druck auf den Taster die LED zum Leuchten – obwohl der Taster gar nicht verbunden ist. Die Lösung des Rätsels ist: die Erschütterung beim Drücken des Tasters reicht aus, um einen entsprechend empfindlich eingestellten „wake on motion“ Interrupt auszulösen. Ich hätte also genauso gut auf den Tisch klopfen können. Der Arduino Sketch sorgt dafür, dass bei Registrierung des Interrupts die LED für eine Sekunde leuchtet.

Ihr könnt auch andere tolle Sachen mit diesem Interrupt anstellen. Zum Beispiel könnt ihr ein Gerät realisieren, dass sich anschaltet, wenn ihr es in die Hand nehmt. Wenn es herumliegt, soll es schlafen. Kein Problem, ihr müsst dann nur das Interrupt Signal als Wecksignal für den Microcontroller nutzen. Siehe dazu auch mein Beitrag über Sleep Modes.

Das Problem mit nicht dokumentierten Registern

Im Datenblatt des MPU6050 ist eine Prozedur beschrieben, wie Interrupts einzurichten sind. Das Problem ist nur, dass sie nicht funktioniert. Beim Nachforschen stieß ich auf diese Seite, die erklärt, wie man es richtig macht. Dabei werden sowohl im Datenblatt nicht dokumentierte Register benutzt, wie auch nicht dokumentierte Bits dokumentierter Register.

Interessanterweise gibt es fleißige Leute, die im „reverse engineering“ Verfahren die Dokumentationsarbeit nachgeholt haben. Das Ergebnis, also die vollständige Register Map, gibt es hier.

Der Interrupt Sketch

Ich habe also die Anleitung genommen und darauf basierend den gleich folgenden Sketch geschrieben. Ein paar der in der Anleitung vorgeschlagenen Schritte, braucht man meiner Meinung nach nicht. Ich habe sie eingefügt aber auskommentiert.

Den Sketch im Detail zu erklären, würde den Rahmen sprengen. Nur ein paar Anmerkungen:

  • setInterrupt(x) aktiviert den Interrupt; x bestimmt die Sensitivität mit einem Wert von 1 (4 mg = „4 milli-g“, nicht Milligramm) bis 255 (1024 mg)
    • bei 255 muss man den MPU6050 schon kräftig schütteln, um den Interrupt auszulösen
  • wenn ihr den Interrupt Pin active-low einstellen wollt, dann entkommentiert Zeile 41
  • mit der Latch Funktion könnt ihr einstellen, dass der Interrupt Pin bis zum nächsten Lesevorgang aktiv bleibt. Ändert dazu Zeile 41 in: writeRegister(MPU6050_INT_PIN_CFG, 1<<MPU6050_LATCH_INT_EN)
  • Kombi von active-low und latch: writeRegister(MPU6050_INT_PIN_CFG, ((1<<MPU6050_ACTL) | (1<<MPU6050_LATCH_IN_EN))

Hier nun der Sketch:

#include "Wire.h" 

#define MPU6050_ADDR              0x68 // Alternatively set AD0 to HIGH  --> Address = 0x69
#define MPU6050_ACCEL_CONFIG      0x1C // Accelerometer Configuration Register
#define MPU6050_PWR_MGT_1         0x6B // Power Management 1 Register
#define MPU6050_INT_PIN_CFG       0x37 // Interrupt Pin / Bypass Enable Configuration Register
#define MPU6050_INT_ENABLE        0x38 // Interrupt Enable Register
#define MPU6050_LATCH_INT_EN      0x05 // Latch Enable Bit for Interrupt 
#define MPU6050_ACTL              0x07 // Active-Low Enable Bit
#define MPU6050_WOM_EN            0x06 // Wake on Motion Enable bit
#define MPU6050_WOM_THR           0x1F // Wake on Motion Threshold Register
#define MPU6050_MOT_DUR           0x20 // Motion Detection Duration Register
#define MPU6050_ACCEL_INTEL_CTRL  0x69 // Accelaration Interrupt Control Register
#define MPU6050_SIGNAL_PATH_RESET 0x68 // Signal Path Reset Register

byte interruptPin=2;
byte ledPin=10;
volatile bool accEvent = false;

void setup() {
  Wire.begin();
  writeRegister(MPU6050_PWR_MGT_1, 0);
  setInterrupt(1); // set Wake on Motion Interrupt / Sensitivity; 1(highest sensitivity) - 255
  pinMode(ledPin, OUTPUT);
  pinMode(interruptPin, INPUT);
  attachInterrupt(digitalPinToInterrupt(interruptPin), motion, RISING);
}

void loop() {
   if(accEvent){
    digitalWrite(ledPin, HIGH);
    delay(1000);
    digitalWrite(ledPin, LOW);
    accEvent = false;
    attachInterrupt(digitalPinToInterrupt(interruptPin), motion, RISING);   
  }
}

void setInterrupt(byte threshold){
//writeRegister(MPU6050_SIGNAL_PATH_RESET, 0b00000111);  // not(?) needed
//writeRegister(MPU6050_INT_PIN_CFG, 1<<MPU6050_ACTL); // 1<<MPU6050_LATCH_INT_EN
  writeRegister(MPU6050_ACCEL_CONFIG, 0b00000001);
  writeRegister(MPU6050_WOM_THR, threshold); 
  writeRegister(MPU6050_MOT_DUR, 0b00000001);  // set duration (LSB = 1 ms)
//writeRegister(MPU6050_ACCEL_INTEL_CTRL, 0x15);  // not needed (?)
  writeRegister(MPU6050_INT_ENABLE, 1<<MPU6050_WOM_EN);
}

void writeRegister(uint16_t reg, byte value){
  Wire.beginTransmission(MPU6050_ADDR);
  Wire.write(reg);
  Wire.write(value);
  Wire.endTransmission();
}

void motion(){
  accEvent = true;
  detachInterrupt(2);
}

 

Bibliotheken für den MPU6050

Keine Bibliothek, die ich gefunden habe, nutzt die Möglichkeiten des MPU6050 wirklich umfassend aus. Die Interrupts sind dabei nur Beispiel. Es reizt mich schon, eine vollständigere Bibliothek zu erstellen. Der Aufwand ist allerdings erheblich und die meisten von euch werden wahrscheinlich mit den Basisfunktionen auskommen. Vielleicht komme ich noch mal darauf zurück.

Adafruit MPU6050

Die vollständigste Bibliothek, die ich gefunden habe, ist die von Adafruit. Ihr könnt sie hier direkt von Github herunterladen oder über die Bibliotheksverwaltung in der Arduino IDE installieren.

Basic reading Sketch

Am besten startet ihr mit dem mitgelieferten Beispielsketch basic_readings.ino. Er liest die Beschleunigungswerte, die Temperatur und die Gyroskop Werte. Ihr könnt die Messbereiche und einen Low-Pass Filter (setFilterBandwith()) für die Sensoren einstellen. Auf den Filter komme ich weiter unten noch zu sprechen. Hier erst einmal der unveränderte Sketch:

// Basic demo for accelerometer readings from Adafruit MPU6050

#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>

Adafruit_MPU6050 mpu;

void setup(void) {
  Serial.begin(115200);
  while (!Serial)
    delay(10); // will pause Zero, Leonardo, etc until serial console opens

  Serial.println("Adafruit MPU6050 test!");

  // Try to initialize!
  if (!mpu.begin()) {
    Serial.println("Failed to find MPU6050 chip");
    while (1) {
      delay(10);
    }
  }
  Serial.println("MPU6050 Found!");

  mpu.setAccelerometerRange(MPU6050_RANGE_8_G);
  Serial.print("Accelerometer range set to: ");
  switch (mpu.getAccelerometerRange()) {
  case MPU6050_RANGE_2_G:
    Serial.println("+-2G");
    break;
  case MPU6050_RANGE_4_G:
    Serial.println("+-4G");
    break;
  case MPU6050_RANGE_8_G:
    Serial.println("+-8G");
    break;
  case MPU6050_RANGE_16_G:
    Serial.println("+-16G");
    break;
  }
  mpu.setGyroRange(MPU6050_RANGE_500_DEG);
  Serial.print("Gyro range set to: ");
  switch (mpu.getGyroRange()) {
  case MPU6050_RANGE_250_DEG:
    Serial.println("+- 250 deg/s");
    break;
  case MPU6050_RANGE_500_DEG:
    Serial.println("+- 500 deg/s");
    break;
  case MPU6050_RANGE_1000_DEG:
    Serial.println("+- 1000 deg/s");
    break;
  case MPU6050_RANGE_2000_DEG:
    Serial.println("+- 2000 deg/s");
    break;
  }

  mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);
  Serial.print("Filter bandwidth set to: ");
  switch (mpu.getFilterBandwidth()) {
  case MPU6050_BAND_260_HZ:
    Serial.println("260 Hz");
    break;
  case MPU6050_BAND_184_HZ:
    Serial.println("184 Hz");
    break;
  case MPU6050_BAND_94_HZ:
    Serial.println("94 Hz");
    break;
  case MPU6050_BAND_44_HZ:
    Serial.println("44 Hz");
    break;
  case MPU6050_BAND_21_HZ:
    Serial.println("21 Hz");
    break;
  case MPU6050_BAND_10_HZ:
    Serial.println("10 Hz");
    break;
  case MPU6050_BAND_5_HZ:
    Serial.println("5 Hz");
    break;
  }

  Serial.println("");
  delay(100);
}

void loop() {

  /* Get new sensor events with the readings */
  sensors_event_t a, g, temp;
  mpu.getEvent(&a, &g, &temp);

  /* Print out the values */
  Serial.print("Acceleration X: ");
  Serial.print(a.acceleration.x);
  Serial.print(", Y: ");
  Serial.print(a.acceleration.y);
  Serial.print(", Z: ");
  Serial.print(a.acceleration.z);
  Serial.println(" m/s^2");

  Serial.print("Rotation X: ");
  Serial.print(g.gyro.x);
  Serial.print(", Y: ");
  Serial.print(g.gyro.y);
  Serial.print(", Z: ");
  Serial.print(g.gyro.z);
  Serial.println(" rad/s");

  Serial.print("Temperature: ");
  Serial.print(temp.temperature);
  Serial.println(" degC");

  Serial.println("");
  delay(500);
}

 

Beachtet, dass die Ergebnisse in m/s2 bzw. rad/s ausgegeben werden. Hier habe ich das Modul einmal mit seiner z-Achse, einmal mit seiner x-Achse und schließlich mit seiner y-Achse senkrecht ausgerichtet:

Ausgabe von basic_readings.ino
Ausgabe von basic_readings.ino

Falls ihr Winkelgeschwindigkeiten in Grad/s umrechnen wollt:

2\pi\cdot rad = 360° \;\; \Rightarrow\;\;1°=\frac{2\pi\cdot rad}{360}

Für die Umrechnung der Beschleunigungswerte in g, teilt die Werte durch 9,81.

Plotter Sketch

Gerade bei Winkelgeschwindigkeiten möchtet Ihr vielleicht nicht nur Einzelwerte ermitteln, sondern den Verlauf verfolgen. Dafür eignet sich der Sketch plotter.ino, den ich hier auch unverändert abdrucke:

// Basic demo for accelerometer readings from Adafruit MPU6050

#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>

Adafruit_MPU6050 mpu;

void setup(void) {
  Serial.begin(115200);
  while (!Serial) {
    delay(10); // will pause Zero, Leonardo, etc until serial console opens
  }

  // Try to initialize!
  if (!mpu.begin()) {
    Serial.println("Failed to find MPU6050 chip");
    while (1) {
      delay(10);
    }
  }

  mpu.setAccelerometerRange(MPU6050_RANGE_16_G);
  mpu.setGyroRange(MPU6050_RANGE_250_DEG);
  mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);
  Serial.println("");
  delay(100);
}

void loop() {

  /* Get new sensor events with the readings */
  sensors_event_t a, g, temp;
  mpu.getEvent(&a, &g, &temp);

  /* Print out the values */
  Serial.print(a.acceleration.x);
  Serial.print(",");
  Serial.print(a.acceleration.y);
  Serial.print(",");
  Serial.print(a.acceleration.z);
  Serial.print(", ");
  Serial.print(g.gyro.x);
  Serial.print(",");
  Serial.print(g.gyro.y);
  Serial.print(",");
  Serial.print(g.gyro.z);
  Serial.println("");

  delay(10);
}

 

Ladet den Sketch hoch und wählt dann anstelle des seriellen Monitors den seriellen Plotter. Hier habe ich den MPU6050 mehrfach um 90° gedreht:

Plotterausgabe
Plotterausgabe

Cycle und Filter Bandwith

Ich möchte noch auf zwei Parameter zu sprechen kommen, deren Wirkung der folgende Sketch illustriert. Dieser Sketch stammt von mir, ist also nicht Teil der Beispielsketche der Bibliothek.

Die Funktion setCycleRate(frequency) bewirkt, dass der MPU6050 in den Schlaf geht und in der vorgegebenen Frequenz (frequency) aufwacht, um einen Satz Messwerte aufzunehmen. Das spart erheblich Strom. Genau genommen legt die Funktion nur die Frequenz fest. Aktiv geschaltet wird sie mit enableCycle(true).  Übergebt ihr „false“, wird sie inaktiv.

Die Funktion setFilterBandwith(bandwith) dämpft die Messwerteschwankung. Wenn ihr den Sketch mit den von mir gewählten Parametern (siehe Zeile 58/84) hochladet und euren MPU6050 kippt, werdet ihr sehen, dass die Messwerte sehr träge reagieren. In Kombination mit der Cycle Funktion solltet ihr die Bandbreite also hoch setzen. Ändert den Wert zum Beispiel auf 260 Hz und schaut, was passiert.

// Basic demo for cycle and filter bandwith from Adafruit MPU6050

#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>

Adafruit_MPU6050 mpu;

void setup(void) {
  Serial.begin(115200);
  while (!Serial)
    delay(10); // will pause Zero, Leonardo, etc until serial console opens

  Serial.println("Adafruit MPU6050 test!");

  // Try to initialize!
  if (!mpu.begin()) {
    Serial.println("Failed to find MPU6050 chip");
    while (1) {
      delay(10);
    }
  }
  Serial.println("MPU6050 Found!");

  mpu.setAccelerometerRange(MPU6050_RANGE_8_G);
  Serial.print("Accelerometer range set to: ");
  switch (mpu.getAccelerometerRange()) {
  case MPU6050_RANGE_2_G:
    Serial.println("+-2G");
    break;
  case MPU6050_RANGE_4_G:
    Serial.println("+-4G");
    break;
  case MPU6050_RANGE_8_G:
    Serial.println("+-8G");
    break;
  case MPU6050_RANGE_16_G:
    Serial.println("+-16G");
    break;
  }
  mpu.setGyroRange(MPU6050_RANGE_500_DEG);
  Serial.print("Gyro range set to: ");
  switch (mpu.getGyroRange()) {
  case MPU6050_RANGE_250_DEG:
    Serial.println("+- 250 deg/s");
    break;
  case MPU6050_RANGE_500_DEG:
    Serial.println("+- 500 deg/s");
    break;
  case MPU6050_RANGE_1000_DEG:
    Serial.println("+- 1000 deg/s");
    break;
  case MPU6050_RANGE_2000_DEG:
    Serial.println("+- 2000 deg/s");
    break;
  }

  mpu.setFilterBandwidth(MPU6050_BAND_10_HZ);
  Serial.print("Filter bandwidth set to: ");
  switch (mpu.getFilterBandwidth()) {
  case MPU6050_BAND_260_HZ:
    Serial.println("260 Hz");
    break;
  case MPU6050_BAND_184_HZ:
    Serial.println("184 Hz");
    break;
  case MPU6050_BAND_94_HZ:
    Serial.println("94 Hz");
    break;
  case MPU6050_BAND_44_HZ:
    Serial.println("44 Hz");
    break;
  case MPU6050_BAND_21_HZ:
    Serial.println("21 Hz");
    break;
  case MPU6050_BAND_10_HZ:
    Serial.println("10 Hz");
    break;
  case MPU6050_BAND_5_HZ:
    Serial.println("5 Hz");
    break;
  }

  mpu.setCycleRate(MPU6050_CYCLE_5_HZ);
  Serial.print("Cycle rate set to: ");
  switch (mpu.getCycleRate()) {
  case MPU6050_CYCLE_1_25_HZ:
    Serial.println("1.25 Hz");
    break;
  case MPU6050_CYCLE_5_HZ:
    Serial.println("5 Hz");
    break;
  case MPU6050_CYCLE_20_HZ:
    Serial.println("20 Hz");
    break;
  case MPU6050_CYCLE_40_HZ:
    Serial.println("40 Hz");
    break;
  }
  
  mpu.enableCycle(true);
 
  Serial.println("");
  delay(100);
}

void loop() {

  /* Get new sensor events with the readings */
  sensors_event_t a, g, temp;
  mpu.getEvent(&a, &g, &temp);

  /* Print out the values */
  Serial.print("Acceleration X: ");
  Serial.print(a.acceleration.x);
  Serial.print(", Y: ");
  Serial.print(a.acceleration.y);
  Serial.print(", Z: ");
  Serial.print(a.acceleration.z);
  Serial.println(" m/s^2");

  Serial.print("Rotation X: ");
  Serial.print(g.gyro.x);
  Serial.print(", Y: ");
  Serial.print(g.gyro.y);
  Serial.print(", Z: ");
  Serial.print(g.gyro.z);
  Serial.println(" rad/s");

  Serial.print("Temperature: ");
  Serial.print(temp.temperature);
  Serial.println(" degC");

  Serial.println("");
  delay(500);
}

Noch mehr Informationen findet ihr in diesem Adafruit Tutorial zum MPU6050. Außerdem gibt es eine Reihe weiterer Beispielsketche.  

MPU6050 light

Ganz interessant ist auch die Bibliothek MPU6050_light, die ihr hier auf Github findet oder auch über die Arduino IDE installieren könnt. Sie ist sehr einfach gehalten (eben „light“). Dafür aber bietet sie die Möglichkeit, Neigungswinkel auszugeben, die sie aus den (Erd-)Beschleunigungsdaten berechnet. Voraussetzung ist dabei, dass die Bewegungen langsam erfolgen, damit die Erdbeschleunigung die bestimmende Größe ist. Hier eine Ausgabe des Beispielsketches getAngle.ino. Ich habe dabei die xy-Ebene hin- und hergekippt. Das klappt ziemlich gut, wobei ich meine Wasserwaage aber noch nicht entsorgen würde.

Ausgabe von GetAngle.ino der MPU6050 light Library
Ausgabe von GetAngle.ino der MPU6050 light Library

16 thoughts on “MPU6050 Beschleunigungssensor und Gyroskop

  1. Hallo Wolfgang,
    danke für die schnelle Antwort. Ist ein langsamer Drift, so eine Stunde ein Grad. kan bis 3 oder 6 Grad gehen, kann auch schon beim Einschalten bei 2 Grad sein. alles etwas diffus. Ich stell dann mechanisch auf Null, driftet aber wieder weg. Ich versuch es mal mit dem Light und neuem 6050.
    Gebe dann Bescheid wie es läuft.

  2. Hallo Wolfgang,
    hänge nun schon seit 2 Stunden auf deiner Seite und bin begeistert!
    Habe mit dem MPU6050 eine Wohnkabinenausrichtung realisiert, klappt auch sehr gut. Einziges Problem ist, das mir die Winkel über den Tag bis zu 6 Grad wegdriften. Ich hoffe das ich das eventuell mit der Light Version wegkriege. Hast Du da Erfahrungen?

    1. Hallo Rudolf,
      ich habe keinen Drift festgestellt, ich habe aber auch nie über den ganzen Tag gemessen. Ist das ein langsamer, kontinuierlicher Drift oder erreichst du irgendwann einen Wert, der sich nicht mehr ändert? Oder schwankt der Wert hin und her? Ob das mit der Light Bibliothek besser wird, kann ich dir nicht sagen. Wenn ich wetten sollte, würde ich aber eher auf nein tippen.

      1. Hallo Wolfgang,
        danke für die schnelle Antwort. Ist ein langsamer Drift, so eine Stunde ein Grad. kan bis 3 oder 6 Grad gehen, kann auch schon beim Einschalten bei 2 Grad sein. alles etwas diffus. Ich stell dann mechanisch auf Null, driftet aber wieder weg. Ich versuch es mal mit dem Light und neuem 6050.
        Gebe dann Bescheid wie es läuft.

  3. hallo,
    wie immer sind die erklärungen 1A!
    Frage:
    ich habe zwei MPU6050, jeweils mit einem ESP8266 verbunden. Gleicher aufbau auf jeweils einer platine, sensor ist mechanisch fest mit der platine verbunden. Verwende eines der mustersketche und lese das signal mit dieser zeile aus, wobei ich den sensor in der X/Z ebene jeweils um ca. 90° drehe (0°, 90°, 180°, 270°), der sensor volführt quasi einen looping:

    Ax = (double)AccelX / AccelScaleFactor;

    die ergebnisse sehen so aus:
    sensor-1 : 1,5 0,5 -0,5 0,5
    sensor-2 : 1 0 -1 0

    wie kann ich mir die unterschiede erklären? Wobei die sind schon enorm! Es wäre mir wichtig, dass die ergebnisse im rahmen vernünftiger toleranzen vergleichbar gross wären. Wie mache ich das?

    danke…
    gruss georg

    1. Also, dass die Module ein bisschen variieren, ist normal, aber solche Unterschiede sind merkwürdig. Da ist irgendetwas im Argen. Ich würde erst einmal mal den Adafruit basic_readings sketch laufen lassen. Dann kannst du ja zumindest schon mal sehen, ob die Module im Prinzip vernünftige Werte liefern. Die Achsen, die senkrecht stehen sollten ja ca. 10 m/s2 liefern. Wenn etwas Richtiges herauskommt, dann muss der Fehler irgendwo im Programm liegen. Wenn du nicht weiterkommst dann schicke es mir mal an wolfgang.ewald@wolles-elektronikkiste.de..

      1. hallo Wolfgang,
        der sketch von Adafruit meldet nur „Failed to find MPU6050 chip“ – es sind ja auch clone…
        gruss georg

        1. ich hatte aber noch ein exemplar des MPU6050 clones auf meinem breadboard, das habe ich auch getestet, die werte entsprechen ziemlich genau dem modell 2. Also ist modell 1- sensor schrott…

        2. Der Adafruit Sketch sollte auch mit Klonen funktionieren. Das klingt nach einem Problem mit der I2C Verbindung. Du kannst das mit einem I2C Scanner Sketch prüfen:

          https://wolles-elektronikkiste.de/i2c-scanner

          Findest du damit die beiden Adressen 0x68 und 0x69? Falls nein: Wenn du nur einen MPU6050 anschließt, findest du die Adresse 0x68? Falls ja, hast du beim zweiten Modul den Pin AD0 auf High gesetzt?

          1. ich habe jetzt einen eigenen i2c scanner probiert, weder der noch der von Deiner webseite hat bei keinem der dreien MPU module eine adresse gefunden. Was meinst Du mit AD0 High setzen?

            1. jetzt habe ich das mit dem „zweiten“ AD0 verstanden. Es sind jeweils ein esp8266 und eine mpu6050 auf zwei getrennten platinen… Identische schaltung, aber jede für sich… sollten aber ähnliche werte je nach lage liefern…

              1. Wenn der Scanner keine Adresse findet, dann ist irgendetwas mit der I2C Verbindung im Argen. Pull-Ups sind eigentlich auf dem Modul. Hast du die richtigen I2C Pins am esp8266 gewählt? Welches Board / welche Pins nutzt du?

                1. SCL zu D7 / SDA zu D6 sind verbunden, dann nur noch VCC und GND. Ich konnte da nicht mehr viel probieren, die MPU war ja in beiden fällen fest verlötet. Bin dann den einfachsten weg gegangen:
                  Aus einem bereits eingemotteten roboter habe ich noch eine MPU „kanibalisieren“ können, die mit den falschen werten durch die neue ersetzt, alles läuft, ich habe jetzt zwei module die beide die gleichen wert liefern…
                  gruss und danke für die hilfe…georg

  4. Hallo Hr. Ewald
    Ich finde ihre Ausführungen sehr interesant.Ich habe da gleich auch ein paar Fragen.
    Kann man mit dem MPU6050 auch Schalbefehle, (arduino uno) also in der X achse 2x und in der Y achse 2x die sich im messbereich 2g befinden.Ich möchte ein Wohnmobil automatisch in Waage bringen.Da muss ein “ Neigungssensor“ sehr empfindich sein,meine ich.Ich bauch natürlich noch mehr,an Bauteilen und Schaltungen
    um die ganze Sache zum Laufen zu bringen.Im ganzen Internet ist nichts zu finden (sketche,usw.)
    Ich selber bin ganz neu beim Arduino unterwegs,finde es aber sehr interesant.Bin auch fleißig beim Üben.
    Vom Beruf her bin ich Elektromeister und Hobbyelektroniker.Würde mich sehr freuen wenn sie was für mich haben.Besten Dank. Erhard Schiewe.

  5. Danke wieder mal für den tollen Blog-Beitrag!
    Ich habe für eine elektronische Wasserwaage zur Ausrichtung einer Astronomie-Teleskopplattform einen ADXL345 (GY291 Baustein) genommen. Wußte gar nicht, dass es da auch andere Typen gibt. Hast Du Dich bewußt für den verwendeten Typ entschieden oder war das Zufall? Gibts da ggf. eklatante Unterschiede?

    Viele Grüße

    1. Das ist nicht der einzige Beschleunigungssensor, den ich ausprobiere. Ich habe schlicht mit diesem Modell angefangen, weil es doch recht stark verbreitet zu sein scheint. Der ADXL345 ist auch auf meiner Liste – liegt aber noch unausgepackt auf meinem Schreibtisch. .

Schreibe einen Kommentar

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