zurück

3-Achsen Beschleunigungs- und Lagesensor

07.05.2018

Hier geht es um den Sensorchip MPU-6050 verbaut auf dem Modul GY-521. Durch Einsatz dieses 3-Achsen-Gyroskops und 3-Achsen-Beschleunigungssensors sind 6 Freiheitsgrade (DOF = "Degrees Of Freedom") gleichzeitig erkennbar. Zusätzlich kann der Sensor noch die Umgebungstemperatur messen.

Modul GY-521
Abb.: Modul GY-521

Kern-Features des GY-521 (laut Hersteller)

Interner Aufbau des GY-521

Das Modul verfügt über einen I²C-Bus, durch den ein Mikrocontroller -wie ein Arduino- einfach angeschlossen werden kann. Möglicherweise sind 3,3V zum korrekten Betrieb des I²C-Bus nicht genug, es sollte daher -falls möglich- immer 5V als Stromversorgung verwendet werden.
Das Modul besitzt für den I²C-Bus Pullup-Widerstände, welche manchmal 10kΩ und manchmal 2,2KΩ sein können. Letzter Wert ist ziemlich niedrig, was zu Problemen führen kann, wenn noch andere Sensor-Module verwendet werden. Hier sollte dann ein zusätzlicher, externer Pullup-Widerstand Abhilfe schaffen.
Manche der GY-521-Module besitzen einen falschen (oder schlechten) Kondensator, was zu hohem Rauschen in den Messungen führen kann.

2,2KΩ-Pullup-Widerstände des Moduls GY-521
Abb.: 2,2KΩ-Pullup-Widerstände des Moduls GY-521

Der Sensor besitzt zusätzliche ein DMP-Einheit ("Digital Motion Processor"), welcher mit Firmware programmiert werden kann und es ermöglicht komplexere Berechnungen direkt auf dem Sensor-Chip durchzuführen. Anscheinend werden aber nicht genug Spezifikationen dafür von InvenSense herausgegeben, so dass durch Reverse Engineering diese Möglichkeiten doch erschlossen werden konnte.

Interner Schaltplan des Moduls GY-521
Abb.: Interner Schaltplan des Moduls GY-521

Anschlüsse des GY-521

Pin Pin-Funktion Arduino Uno
VCC Stromversorgung (3,3V bis 5V) (interner Spannungsregler) 3,3V bzw. 5V
GND Mass/Ground GND
SCL Serial clock (I²C) A5
SDA Serial data (I²C) A4
XDA Auxiliary data (= I²C master serial data, um das Modul an externe Module anzuschließen) (hier nicht benutzt)
XCL Auxiliary clock (= I²C master serial clock, um das Modul an externe Module anzuschließen) (hier nicht benutzt)
AD0 Änderung der I²C-Adresse:
LOW: 0x68
HIGH: 0x69
GND
INT Interrupt digital output (Optionaler Anschluss, um mehrere Module in Reihe zu schalten) (hier nicht benutzt)

Verwendete Bauteile

Optionale Software-Library: I2Cdevlib und MPU6050-Code

Aufbau

Aufbau auf dem Breadboard
Abb.: Aufbau auf dem Breadboard

Ex. 1: Ausgabe der Rohdaten in der seriellen Konsole

Der folgende Sketch steuert das GY-521-Modul an, liest alle verfügbaren Daten in einem festen Zeitintervall (hier: 1s) und gibt sie anschließend in der seriellen Konsole aus. Diese Rohdaten sind leicht zu lesen. Es muss nur der "sleep-mode" deaktiviert werden und nun können die Sensorwerte des Gyroskops, Beschleunigungssensors und Temperaturfühlers gelesen werden. An der Stelle Wire.requestFrom(MPU6050_ADRESS, 7*2, true) werden 14 Byte der Register ausgelesen. Dies liegt daran, dass alle 7 verfügbaren Sensor-Messwerte 2 Byte (=16 Bits) besitzen.

Sketch

#include <Wire.h>

#define MPU6050_ADRESS 0x68
int16_t accX, accY, accZ, gyrX, gyrY, gyrZ, tVal;
double temperature = 0.0;

void setup()
{
  Wire.begin();
  Wire.beginTransmission(MPU6050_ADRESS); // Begins a transmission to the I2C slave (GY-521 board)
  Wire.write(0x6B); // PWR_MGMT_1 register
  Wire.write(0); // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);

  Serial.begin(9600);
  Serial.flush();
}
void loop()
{
  Wire.beginTransmission(MPU6050_ADRESS);
  Wire.write(0x3B); // starting with register 0x3B (ACCEL_XOUT_H) [MPU-6000 and MPU-6050 Register Map and Descriptions Revision 4.2, p.40]
  Wire.endTransmission(false); // the parameter indicates that the Arduino will send a restart. As a result, the connection is kept active.
  Wire.requestFrom(MPU6050_ADRESS, 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)
  tVal = Wire.read()<<8 | Wire.read(); // reading registers: 0x41 (TEMP_OUT_H) and 0x42 (TEMP_OUT_L)
  gyrX = Wire.read()<<8 | Wire.read(); // reading registers: 0x43 (GYRO_XOUT_H) and 0x44 (GYRO_XOUT_L)
  gyrY = Wire.read()<<8 | Wire.read(); // reading registers: 0x45 (GYRO_YOUT_H) and 0x46 (GYRO_YOUT_L)
  gyrZ = Wire.read()<<8 | Wire.read(); // reading registers: 0x47 (GYRO_ZOUT_H) and 0x48 (GYRO_ZOUT_L)

  Serial.print("aX = ");    Serial.print(toStr(accX));
  Serial.print(" | aY = "); Serial.print(toStr(accY));
  Serial.print(" | aZ = "); Serial.print(toStr(accZ));
  Serial.print(" | gX = "); Serial.print(toStr(gyrX));
  Serial.print(" | gY = "); Serial.print(toStr(gyrY));
  Serial.print(" | gZ = "); Serial.print(toStr(gyrZ));

  /**
   * The following parameters are taken from the documentation [MPU-6000/MPU-6050 Product Specification, p.14]:
   *
   * Temperature sensor is -40°C to +85°C (signed integer)
   * 340 per °C
   * Offset = -512 at 35°C
   * At 0°C: -512 - (340 * 35) = -12412
   */
  temperature = (tVal + 12412.0) / 340.0;
  Serial.print(" | T = ");  Serial.print(toStr((int16_t)temperature)); Serial.print("°C");

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

/**
 * Converts int16 to string.
 * Moreover, resulting strings will have the same length
 */
char* toStr(int16_t value)
{
  char result[7];
  sprintf(result, "%6d", value);
  return result;
}

Ergebnis

Ausgabe der Rohdaten in der seriellen Konsole
Abb.: Ausgabe der Rohdaten in der seriellen Konsole

Ex. 3: Winkel messen mit dem MPU-6050

Der folgende Sketch berechnet nach dem Auslesen der Sensor-Daten die richtigen Winkel des Sensors und gibt diese wiederum in der seriellen Konsole aus.

Sketch

#include <Wire.h>

#define MPU6050_ADRESS 0x68

const int ACCEL_OFFSET   = 200;
const int GYRO_OFFSET    = 151;  // 151
const int GYRO_SENSITITY = 131;  // 131 is sensivity of gyro from data sheet
const float GYRO_SCALE   = 0.2; //  0.02 by default - tweak as required
const float LOOP_TIME    = 0.15; // 0.1 = 100ms

int accValue[3], accAngle[3], gyroValue[3], temperature, accCorr;
float gyroAngle[3], gyroCorr;

void setup()
{
  Wire.begin();
  Wire.beginTransmission(MPU6050_ADRESS); // Begins a transmission to the I2C slave (GY-521 board)
  Wire.write(0x6B); // PWR_MGMT_1 register
  Wire.write(0); // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);

  Serial.begin(9600);
  Serial.flush();
}
void loop()
{
  Wire.beginTransmission(MPU6050_ADRESS);
  Wire.write(0x3B); // starting with register 0x3B (ACCEL_XOUT_H) [MPU-6000 and MPU-6050 Register Map and Descriptions Revision 4.2, p.40]
  Wire.endTransmission(false); // the parameter indicates that the Arduino will send a restart. As a result, the connection is kept active.
  Wire.requestFrom(MPU6050_ADRESS, 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
  for(byte i=0; i<3; i++) {
    accValue[i] = Wire.read()<<8 | Wire.read(); // reading registers: ACCEL_XOUT, ACCEL_YOUT, ACCEL_ZOUT
  }
  temperature = Wire.read()<<8 | Wire.read(); // reading registers: 0x41 (TEMP_OUT_H) and 0x42 (TEMP_OUT_L)
  for(byte i=0; i<3; i++) {
    gyroValue[i] = Wire.read()<<8 | Wire.read(); // reading registers: GYRO_XOUT, GYRO_>OUT, GYRO_ZOUT
  }

  Serial.print("Accel: ");
  for(byte i=0; i<3; i++) {
    accCorr = accValue[i] - ACCEL_OFFSET;
    accCorr = map(accCorr, -16800, 16800, -90, 90);
    accAngle[i] = constrain(accCorr, -90, 90);
    Serial.print(accAngle[i]);
    Serial.print("\t");
  }
  Serial.print("| Gyro: \t");

  for(byte i=0; i<3; i++) {
    gyroCorr = (float)((gyroValue[i]/GYRO_SENSITITY) - GYRO_OFFSET);
    gyroAngle[i] = (gyroCorr * GYRO_SCALE) * -LOOP_TIME;
    Serial.print(gyroAngle[i]);
    Serial.print("\t");
  }

  Serial.println(" ");
  delay(LOOP_TIME * 1000);
}

Ergebnis

Ausgabe der Winkel in der seriellen Konsole
Abb.: Ausgabe der Winkel in der seriellen Konsole

Ex. 2: Grafische Ausgabe in "Processing"

Um die Funktionalität des Sensors besser darzustellen, werden die ermittelten Werte nun in Processing übertragen und dort in Echtzeit visualisiert. Die Sketches stammen von der Website "Geek Mom Projects"

Ausgabe der Daten in der seriellen Konsole
Abb.: Ausgabe der Daten in der seriellen Konsole
Video: Live-Demonstration in Processing