Posts mit dem Label MPU6050 werden angezeigt. Alle Posts anzeigen
Posts mit dem Label MPU6050 werden angezeigt. Alle Posts anzeigen

Dienstag, 23. Juli 2013

Kreisgeschwindigkeit aus dem Sensor umrechnen (Die Zweite)

image
Von mir entwickeltes Programm zum darstellen eines künstlichen Horizontes und ein Würfel, das die Bewegungen des Sensors wieder gibt.

Im letzten Post zeigte ich eine relativ ungenaue Lösung für das Umrechnen der Winkellage. Grund hierfür ist, dass der Zeitabstand nicht verwendet wird. Als Provisorium reicht es allerdings aus.

Nun, welche Formel wird stattdessen benötigt?

image
Kreisgeschwindigkeit = Drehwinkel / Zeitspanne
image steht für die Winkelgeschwindigkeit
image steht für den Drehwinkel
image steht für die vergangene Zeit

Im nächsten Schritt muss ich die Formel umstellen, um später an das Ergebnis des Drehwinkels heran zu kommen. Der Sensor gibt pro Abfrage die Winkelgeschwindigkeit, und die Zeit wird zwischen der aktuellen Abfrage und der letzten Abfrage bestimmt.

image

Die verwendeten Werte nehme ich wie zuletzt aus dem Sensor MPU6050 mit den entsprechenden Einstellungen. Der Wert 32768 ist der Maximale Einheiten Wert der in eine Richtung gemessen werden kann. Die vergangene Zeit wird durch 1000ms dividiert, weil die Formel eigentlich für Sekunden ausgelegt ist. Die 2000.0 kommt aus der Einstellung für die Gyroskop Empfindlichkeit, also 2000°/s.

image

Daraus kann nun der Programmcode gebildet werden.

double winkel = ((((measurement - _Offset) / 32768.0) *
                                  ((double)timeelapsed / 1000.0))) * 2000.0;
measurement = Messergebnis
_Offset = Abweichungswert das den Messwert an die eigentliche Lage bringt
timeelapsed = vergangene Zeit zwischen der Abfrage und der letzten

Die Aufstellung ist für den Netduino relativ lang und sollte daher gekürzt werden. Am besten geht das auf einem Blatt Papier.

Formelumstellung
Überflüssiges kürzen

Somit sieht dann der Programmcode schon etwas schlanker aus.

double winkel = ((measurement - _Offset) * (double)timeelapsed) / 16384.0;

Der errechnete Wert zeigt den zurückgelegten Winkel. Damit daraus eine Lagebestimmung wird, muss daraus eine Integralrechnung werden, sowie bereits in meinem letzten Blogeintrag erwähnt. Damit sieht nun die Klasse folgendermaßen aus:

public class GyroscopeAxis
{
    /// Derzeitige Winkellage
    private double _Angle = 0;

    /// Ruft ab, ob der Offset gesetzt wurde oder legt diesen fest.
    private bool _IsOffset_Done = false;

    /// Ruft den Offset ab oder legt diesen fest.
    /// Wird benötigt, um die Abweichung runter zurechnen.
    private double _Offset = 0;

    /// Nimmt den aktuellen Beschleunigungswert und summiert den auf den Winkelwert
    public double UpdateAngle(double measurement, ref int timeelapsed)
    { 
        // Offsetwert festlegen, wenn nicht gesetzt.
        if (!_IsOffset_Done)
        {
            _Offset = measurement;
            _IsOffset_Done = true;
        }

       // Aktuellen Wert summieren und den Offset Wert Abziehen
        _Angle -= ((measurement - _Offset) * (double)timeelapsed) / 16384.0;

        return _Angle;
    }
}

Die Klasse für das Bestimmen der Winkellage

Anders als in meinem vorigen Blog Post habe ich die Abweichung (Offset) mit eingebracht. Dieser Wert wird beim Kalibrieren ermittelt, um die waagerechte Haltung anzugleichen. Was allerdings noch bleibt und auch nicht zu verhindern ist, sind weiterhin die Messfehler, die nur durch das Komplementieren mit der Beschleunigungsdaten gefiltert werden können.

Donnerstag, 18. Juli 2013

Kreisgeschwindigkeit aus dem Sensor umrechnen

image
Von mir entwickeltes Programm zum darstellen eines künstlichen Horizontes.

Neben Beschleunigung existiert die Kreisgeschwindigkeit und kann durch ein Gyroskop gemessen werden. Um nun daraus einen Winkel zu ermitteln, ist eine einfache Rechnung erforderlich.

(Aktueller Winkel) = (Aktueller Winkel) + (Messwert)

Sieht also nicht sonderlich wild aus und genauso wenig schwierig ist dies in ein Programm zu schreiben.

private double _Angle = 0;

public double UpdateAngle(double measurement)
{
    _Angle += measurement;
    return _Angle;
}

Von Gyroskop zu Gyroskop sind die Ergebnisse unterschiedlich. Wie immer nehme ich den MPU6050 Sensor, der einen Messbereich von 0 bis 65535 wiedergibt. Um auch hier negative Werte zu erhalten, ist es nötigt, die Werte um 32768 zu verschieben. Sowie ich das im Blog Post mit dem Beschleunigungssensor Ergebnis erledigt habe. Zudem muss noch der Wert skaliert werden, der davon abhängig ist, auf welche Messgeschwindigkeit eingestellt wurde. Hierzu habe ich eine Tabelle mit Werten zusammengestellt.

250°/s (Messwert) / 8192 (oder 10000)
500°/s (Messwert) / 4096
1000°/s (Messwert) / 2048
2000°/s (Messwert) / 1024
  Leider kann es hier Abweichungen zu meinem Beispiel geben. Hier muss man im nachhinein schauen ob das passt.

Mit dieser Information kann nun die Methode ergänzt werden.

public double UpdateAngle(double measurement)
{
    measurement = measurement / 1024;
    _Angle += measurement;
    return _Angle;
}

Indirekt war das nun der zweite Teil für den künstlichen Horizont. Damit auch dieser Code vollständig funktioniert werden zur Korrektur die Ergebnisse aus dem Beschleunigungssensor benötigt. Der Grund hierfür ist, dass Messfehler entstehen können, die dazu führen, dass sich die Lage nach und nach verschiebt. Das Stichwort an dieser Stelle heißt “Komplementieren”.

Sonntag, 7. Juli 2013

MPU6050 Sensor auslesen mit dem Netduino (Repost)

Quadrocopter_49
Fest verlötet auf meinem Shield.

Anfang des Jahres habe ich bereits darüber geschrieben und dazu auch ein Beispiel Projekt gepostet. Nun möchte ich diesen Artikel neu aufsetzen und etwas mehr auf einzelne Punkte eingehen. Zunächst nochmal der Hinweis, es handelt sich wieder um einen reinen Funktionalen Programm Code und hat daher keine Fehlerbehandlung.

Ein paar Informationen zu dem Sensor

Hersteller InvenSens
Bezeichnung MPU-6000 und MPU-6050
Sensor Achsen 6
Gyroskope Einstellungen +-250°/s, +-500°/s, +-1000°/s, +-2000°/s
Beschleunigungseinstellungen +-2g, +-4g, +-8g, +-16g
Verbindung I²C (der 6000er hat zusätzlich SPI)

Kommen wir nun zum eigentlichen Thema. Ursprünglich gab es von Jeff Rowberg bereits eine fertige Library in C++. Leider ließ sich dieser Code nicht für den Netduino verwenden und daher strebte ich an, eine C# und .NET Micro Framework Variante zu schreiben. Zugegeben, alleine mit dem PDF Dokument hätte ich das nicht verstanden und war froh, die Funktionsweise aus der C++ Variante heraus zu lesen.

Fangen wir mit der Hauptklasse an. Der wichtigste Teil hierbei ist, mit welchen Einstellungen der Sensor Initialisiert wird.

public class MPU6050Sensor
{
    // I²C Klasse für die Verbindung zum Sensor
    private I2CDevice _i2CDevice;

    // Initialisiert den Sensor
    public MPU6050Sensor()
    {
       
// I²C Bus verbindung herstellen
        // Die erste Hex Zahl stellt die Adresse vom Sensor und
        // der Wert 100 steht für die Übertragung in kHz.
        // Wahlweise kann hier auch 400kHz verwendet werden,
        // setzt jedoch eine saubere Verbindung voraus,
        // da Störungen das Ergebnis beeinflussen.
        _i2CDevice = new I2CDevice(new I2CDevice.Configuration(0x68, 100));

        // Sensor Initialisieren
        // Sleep und Reset durchführen
        // Der Hex Wert 0x6B steht für das Power Management 1
        // Der zweite Hex Wert beinhaltet das 'Bit' für einen Reset.
        StatusMessage(Write(new byte[] { 0x6B, 0x80 }));
        Thread.Sleep(10);

        // Sleep beenden und Clock einstelllen
        // Der Hex Wert 0x6B steht für das Power Management 1
        // Der zweite Hex Wert 0x00 legt die 'Clock Select'
        // auf 'Internal 8MHz oscillator'
        // Falls der Temperatur Sensor nicht ausgelesen werden soll,
        // dann kann stattdessen der Wert 0x08 eingetragen werden.
        StatusMessage(Write(new byte[] { 0x6B, 0x00 }));

        // Konfiguration festlegen
        // Diese Einstellungen aktiviert den Tief Pass Filter (DLPF) und
        // wird z.B. verwendet, andere Vibrationen heraus zu filtern.
        // Setting => Acc=5Hz, Delay=19.0ms, Gyro=5Hz, Delay=18.6ms, Fs=1kHz
        StatusMessage(Write(new byte[] { 0x1A, 0x06 }));
    }

    // Sendet das Byte Array zum Modul
   private int Write(byte[] buffer)
    {
        I2CDevice.I2CTransaction[] transactions = new I2CDevice.I2CTransaction[]
        {
            I2CDevice.CreateWriteTransaction(buffer)
        };
        return _i2CDevice.Execute(transactions, 1000);
    }

    // Liest mit den Byte Array die Daten vom Modul
    private int Read(byte[] buffer)
    {
        I2CDevice.I2CTransaction[] transactions = new I2CDevice.I2CTransaction[]
        {
            I2CDevice.CreateReadTransaction(buffer)
        };
        return _i2CDevice.Execute(transactions, 1000);
    }

    // Status Nachrichten wiedergeben
    private void StatusMessage(int result)
    {
        if (result == 0)
        {
            Debug.Print("Status: Fehler beim Senden oder Empfangen");
        }
        else
        {
            Debug.Print("Status: OK");
        }
    }

    // Ruft den Sensor Daten ab.
   public SensorData GetSensorData()
    {
       
// Byte Array für den Lese Vorgang vorbereiten
        byte[] buffer = new byte[14];
        buffer[0] = 0x3B;

        // Der Hex Wert stellt das erste Byte da
        // für die Beschleunigungsachse X
        Write(new byte[] { 0x3B });
       
// Byte Array übergeben zum beschreiben
        Read(buffer);

        // Ergebnis an das Sensor Objekt,
        // in dem die Byte Werte umgewandelt werden
        return new SensorData(buffer);
    }
}

Nun das war vielleicht etwas viel Green Code, dennoch hoffe ich, das die Beschreibungen etwas Aufschluss geben, was diese Byte Werte alle sollen. Weniger aufwendig und kürzer ist das SensorData Objekt, das die Byte Daten in brauchbare Werte umwandelt.

public class SensorData
{
    public uint Acceleration_X = 0;
    public uint Acceleration_Y = 0;
    public uint Acceleration_Z = 0;

    public uint Temperatur = 0;

    public uint Gyroscope_X = 0;
    public uint Gyroscope_Y = 0;
    public uint Gyroscope_Z = 0;

    public SensorData(byte[] buffer)
    {
        // Ergebnis für den Beschleunigungssensors zusammenlegen durch Bitshifting
        Acceleration_X = (((uint)buffer[0]) << 8) | buffer[1];
        Acceleration_Y = (((uint)buffer[2]) << 8) | buffer[3];
        Acceleration_Z = (((uint)buffer[4]) << 8) | buffer[5];

        // Ergebnis für Temperatur
        Temperatur = (((uint)buffer[6]) << 8) | buffer[7];

        // Ergebnis für den Gyroskopsensors zusammenlegen durch Bitshifting
        Gyroscope_X = (((uint)buffer[8]) << 8) | buffer[9];
        Gyroscope_Y = (((uint)buffer[10]) << 8) | buffer[11];
        Gyroscope_Z = (((uint)buffer[12]) << 8) | buffer[13];
    }
}

Nun fehlt noch der Einsatz in der ‘Main’ Methode, dass hier die Klasse für den Sensor initialisiert, den Abruf in das SensorData Objekt kopiert und schließlich über Debug.Print die Daten anzeigt..

public static void Main()
{
    MPU6050Sensor mpu = new MPU6050Sensor();

    SensorData data = mpu.GetSensorData();

    while (true)
    {
        data = mpu.GetSensorData();

        Debug.Print(
            "Acc X: " + data.Acceleration_X + " " +
            "Acc Y: " + data.Acceleration_Y + " " +
            "Acc Z: " + data.Acceleration_Z + " " +
            "Gyro X: " + data.Gyroscope_X + " " +
            "Gyro Y: " + data.Gyroscope_Y + " " +
            "Gyro X: " + data.Gyroscope_Z + " " +
            "Temperatur: " + data.Temperatur);
    }
}

Ist dieser Teil eingeben, so kann es dann auf dem Netduino weiter gehen. Hierbei kann ich den Tipp geben, auf die Verkabelung zu achten. Zwar passiert nichts, wenn ihr etwas nicht richtig angeschlossen habt, jedoch wenn ein Wackelkontakt vorliegt oder das Kabel zu lang ist für die I²C Verbindung, kann es zu erheblichen Störungen kommen.

Netduino_MPU6050

Die Verbindung von Netduino zum Sensor:

  • 3,3V >> VCC
  • GND >> GND
  • AnalogIn 4 >> SDA
  • AnalogIn 5 >> SCL

Ist der Anschluss erfolgt, kann das Programm auf den Netduino geschrieben werden. Sobald der Sensor Initialisiert wurde und der Programmcode in die Schleife geht, wird das Ergebnis über Debug.Print() ausgegeben.

image
In Visual Studio 2012 wird über die ‘Ausgabe’ oder im Englischen ‘Output’ ausgegeben.

Falls Fragen bestehen kann gerne ein Kommentar abgegeben werden oder einen Eintrag im Netduino Forum schreiben. Dort bin ich unter dem Namen Heroduino zu finden.

Natürlich wie immer die Solution für das .NET Micro Framework Version 4.2

Donnerstag, 30. Mai 2013

Beschleunigungswerte aus dem Sensor umrechnen

Quadrocopter_47
Die Sensoren MPU6050 und BMA020

Es gibt zwei Varianten, wie der Winkel errechnet werden kann. Als erstes Beispiel nehmen wir für einen Winkel von 90° einen imaginären Messbereich von 0 bis 100, der die Y Achse darstellt.

image_thumb[2]
Visuelle Darstellung der Achsenausrichtung.

Dann könnte die Formel “(MaxWert / 90°) x Messwert = Winkel” lauten. Die Umsetzung ist relativ naheliegend und kann schnell im Programmcode geschrieben werden.

// Variable mit dem Messwert
int reading = 0;
// Ergebnis = (Maximaler Wert / 90°) * Messwert
double result = (100 / 90) * reading;

Messbereich von 0 bis 100

Je nach Sensor ergeben sich andere maximale Wert. Beim MPU6050 geht der Messbereich von 0 bis 65535 und in Abhängigkeit, welche Einstellung gesetzt wurde, ergibt sich der “MaxWert”. Wenn z.B. der Sensor auf 8G initialisiert wurde, dann muss der Wert durch 8 geteilt werden und erhält somit den MaxWert = 8192. Dieser Wert wird erreicht, wenn die Achse 1G misst.

// Variable mit dem Messwert
int reading = 0;
// Ergebnis = (Maximaler Wert / 90°) * Messwert
double result = (8192 / 90) * reading;

Messbereich von 0 bis 8192

Hier bildet sich allerdings ein konstanter Nachteil. Der “MaxWert” muss immer für verschiedene Messbereiche neu gesetzt werden.

Eine geschicktere Lösung sind die Formellösungen für die Berechnung eines Dreiecks. Da jeder in der Schule im Matheunterricht Geometrie hatte, erspare ich die Erklärung, wie ich zu dieser Formelaufstellung gekommen bin.

image
Verwendete Formel

Das sieht etwas wild aus und benötigt auch im Programmcode einige Zeilen mehr. Im vorigen Beispiel habe ich die restlichen 270° ausgelassen, da eine weitere Erläuterung nicht weiter sinnvoll erschien.

private static int GetAngle(int a, int b)
{
    if (a != 0 & b != 0)
    {
       
// Angewendete Formel zur berechnung des Winkels
        // Winkel = ArkusSin((a² + c² - b² / 2ac) * (360 / Pi))

        double qA = System.Math.Pow(a, 2);
        double qB = System.Math.Pow(b, 2);
        double qC = qA + qB;

        // Das Ergebnis das unter dem Bruch entsteht, führte zu einem Fehler,
        // wenn ich dies alles in ein Zeile geschrieben habe.

       double divided = (2 * a * System.Math.Sqrt(qC));
       double result = (System.Math.Acos((qA + qC - qB) / divided) * (180.0 / System.Math.PI));

       if (a > 0 & b < 0)
        {
    
// Wenn Z nach unten ausgerichtet ist und Y(X) nach links(oder rechts) gekippt ist
            //   |
            // -- --
            // x |
            // -90 bis -180

            return (90 - ((int)result)) * -1;
        }
        else if (a > 0 & b > 0)
        {
    
// Wenn Z nach unten ausgerichtet ist und Y(X) nach rechts(oder links) gekippt ist
            //   |
            // -- --
            //   | x
            // 0 bis -90

            return ((int)result) - 90;
        }
        else if (a < 0 & b < 0)
        {
    
// Wenn Z nach oben ausgerichtet ist und Y(X) nach links(oder rechts) gekippt ist
            // x |
            // -- --
            //   |
            // 90 bis 180

            return ((int)result) - 180;
        }
   
// Wenn Z nach oben ausgerichtet ist und Y(X) nach rechts(oder links) gekippt ist
        //   | x
        // -- --
        //   |
        // 0 bis 90

       return 180 - (int)result;
    }
    else if (a == 0 & b != 0)
    {
        if (b < 0)
        {
            return -90;
        }
       else
        {
           return 90;
        }
    }
    else if (a != 0 & b == 0)
    {
       
// Wenn jedoch Y 0 ist und Z mehr als 1
       if (a > 0)
        {
            return -90;
        }
    }
    return 0;
}

Methode um aus Z und X (oder Y) den Winkel Y (oder X) zu berechnen.

Die Methode nimmt zwei Werte an. Für den Integer ‘a’ wird der Wert von der Beschleunigungssensorachse Z übergeben, der Integer für ‘b’ benötigt den Wert von der Achse X oder Y. Z und X ergeben den Winkel Y. Für Z und Y kommt der Winkel X heraus. Bevor jedoch die Messwerte durch die Methode geschickt werden, muss festgestellt werden, dass ein negativer Wert abgebildet wird. Daher ist es sinnvoll, den Sensor Wert mit der Hälfte des Maximalen Wertes zu verschieben.

private static int GetShiftValue(int value)
{
    if (value > 32768 && value < 65536)
    {
        value = 65536 - value;
    }
    else
    {
        value = 0 - value;
    }
    return value;
}

Mit der Methode wird aus 0 bis 65535, ein Bereich von –32767 bis +32767

Naja, hier muss dann doch am Ende der Maximale Wert gesetzt werden, dabei ist die Einstellung des Sensors wiederum egal. Bei dem Windows Phone 8 ist diese Methode nicht erforderlich.

Quadrocopter_46
Test auf dem Windows Phone 8 (Lumia 820)

Mit dieser Methode können die Daten von verschiedenen Sensoren genutzt werden, ohne dass jedes mal vorher ein Maximal Wert ermittelt und gesetzt werden muss.

Sonntag, 12. Mai 2013

DLPF einstellen für den MPU6050

image
Kleiner Versuchsaufbau mit Sensor und Brushless Motor

Auf einem Quadrocopter sorgen die Motoren für viel Vibrationen, die sich ohne einen Filter auf den Sensor negativ auswirken. Über ein Verlaufsdiagramm über die Beschleunigung und die Winkelgeschwindigkeit kann man deutlich hoch frequentierte Abweichungen sichtbar machen. In einem meiner ersten Tests hatte ich die Funktion DLPF (Digitaler Low-Pass Filter) vom Sensor nicht eingeschaltet und wunderte mich zunächst, warum die Lage nicht reguliert wurde. Also erweiterte ich mein Programm zur Analyse und sah dann das Problem.

Das nächste Bild zeigt den Sensor stillliegend mit laufendem Motor. Die schwarze Linie zeigt die Rohdaten an, die grüne Linie zeigt das Ergebnis nach einer Glättung an.

image
Die Vibrationen vom Motor erschweren es, die eigentlich Lage festzustellen, die der Sensor wirklich hat.

Zuvor kannte ich die Lösung von analogen Tiefpassfiltern. In der Digitaltechnik sieht das etwas anders aus. Es ist ein zeitkritisches Thema, wenn dort der Filter ausgeführt wird. Zu empfehlen ist, einen Microcontroller alleine zu verwenden, wenn der Sensor keinen eigenen hat.

Zum Glück gibt es das DLPF im MPU6050 Sensor, der 7 verschiedene Grenzfrequenzen zur Verfügung stellt und somit die hoch frequentierten Störungen ausfiltern kann.

Nachdem ich nicht ganz so müde an die Sache rangegangen bin, verstand ich auch die Einstellung mit dem DLPF (Digitaler Low-Pass Filter) und legte die Einstellungen in der Initialisierung fest. In meinem Beispiel Projekt vom Januar reicht es, nur diese eine Codezeile hinzuzufügen (besser ist, wenn ihr die neue Solution herunter ladet).

// Konfiguration festlegen
// Einstellung => Acc=5Hz, Delay=19.0ms, Gyro=5Hz, Delay=18.6ms, Fs=1kHz

StatusMessage(Write(new byte[] { 0x1A, 0x06 }));

Wie schon im Kommentar zu sehen ist. Lässt der Sensor nur noch Bewegungen bis 5Hz zu. Alles was darüber steht, wird ausgefiltert.

Der neue Test mit dem DLPF zeigt ein deutlich besseres Ergebnis. Egal wie weit ich nun den Motor ansteuere, die Messdaten zeigen weiter die ruhige Lage des Sensors.

image
Nach der Einstellung des DLPF ist von der Motorenvibration (fast) nichts mehr zu sehen.

Je nach Anwendung könnte die Grenzfrequenz von 5Hz natürlich auch etwas zu niedrig sein. Betrachtet man den Quellcode vom WiiCopter, so ist zu sehen, dass die Grenzfrequenz bei 260Hz liegt.

Wie bereits im Codeausschnitt zu sehen ist, werden zwei Bytes in einem Array übergeben. Das Byte ‘0x1A’ steht für den Aufruf der Konfiguration. Das zweite Byte legt die gewünschte Einstellung fest, die ich in der folgenden Tabelle aufliste:

Byte Inhaltliche Einstellung
0x00 Accelrometer => 260Hz, Gyroscope => 256Hz
0x01 Accelrometer => 184Hz, Gyroscope => 188Hz
0x02 Accelrometer => 94Hz, Gyroscope => 98Hz
0x03 Accelrometer => 44Hz, Gyroscope => 42Hz
0x04 Accelrometer => 21Hz, Gyroscope => 20Hz
0x05 Accelrometer => 10Hz, Gyroscope => 10Hz
0x06 Accelrometer => 5Hz, Gyroscope => 5Hz

Beispiel Solution für Netduino: ExampleMPU6050Sensor.zip

Sonntag, 3. März 2013

Arduino Nano 3.0 und MPU6050 in Quellcode Kurzfassung

image
Verkabelungsbeschreibung ist am ende dieses Blogs.

Bereits Anfang des Jahres habe ich eine Lösung in C#  und .NET Micro Framework geschrieben. Ursprünglich habe ich die Lösung aus der in C++ geschriebenen Library portiert (Library für Arduino). Zuvor suchte ich nach einem kürzeren und funktionalem Beispiel, aber ohne Erfolg. Da ich nun selbst relativ gut mit dem Sensor vertraut bin, konnte ich ein kürzeres Beispiel für den Arduino schreiben.

#include <Wire.h>

#define sensor_address 0x68

void setup()
{
  // 9600 Baut reichen zum Betrachten aus.

  Serial.begin(9600);
  Wire.begin();

  delay(1000);
 
  // Powermanagement aufrufen
  // Sensor schlafen und Reset, Clock wird zunächst von Gyro-Achse Z verwendet
 
  Serial.println("Powermanagement aufrufen - Reset");
  SetConfiguration(0x6B, 0x80);
 
  // Kurz warten
  delay(500);
 
  // Powermanagement aufrufen
  // Sleep beenden und Clock von Gyroskopeachse X verwenden

  Serial.println("Powermanagement aufrufen - Clock festlegen");
  SetConfiguration(0x6B, 0x03);
 
  delay(500);
 
  // Konfigruation  aufrufen
  // Default => Acc=260Hz, Delay=0ms, Gyro=256Hz, Delay=0.98ms, Fs=8kHz

  Serial.println("Konfiguration aufrufen - Default Acc = 260Hz, Delay = 0ms");
  SetConfiguration(0x1A, 0x00);
 
  // Leerzeichen
  Serial.println("");
}

void loop()
{
  byte result[14];
  // Anfangs Adresse von Beschleunigungssensorachse X
  result[0] = 0x3B;
 
   // Aufruf des MPU6050 Sensor
  Wire.beginTransmission(sensor_address);
  // Anfangsadresse verwenden.
  Wire.write(result[0]);
  Wire.endTransmission();
  // 14 Bytes kommen als Antwort
  Wire.requestFrom(sensor_address, 14);
  // Bytes im Array ablegen
  for(int i = 0; i < 14; i++)
  {
    result[i] = Wire.read();
  }
 
  // Zwei Bytes ergeben eine Achsen Wert und könenn

// per Bit Shifting  zusammengelegt werden.
 
  // Beschleunigungssensor
  int acc_X = (((int)result[0]) << 8) | result[1];
  int acc_Y = (((int)result[2]) << 8) | result[3];
  int acc_Z = (((int)result[4]) << 8) | result[5];
 
  // Temperatur sensor
  int temp = (((int)result[6]) << 8) | result[7];
 
  // Gyroskopesensor
  int gyr_X = (((int)result[8]) << 8) | result[9];
  int gyr_Y = (((int)result[10]) << 8) | result[11];
  int gyr_Z = (((int)result[12]) << 8) | result[13];
 
  // Ausgabe
  Serial.print("ACC X:\t");
  Serial.print(acc_X); Serial.print("\t");
  Serial.print("Y:\t");
  Serial.print(acc_Y); Serial.print("\t");
  Serial.print("Z:\t");
  Serial.print(acc_Z); Serial.print("\t");
  Serial.print("Temperatur:\t");
  Serial.print(temp); Serial.print("\t");
  Serial.print("Gyroskope X:\t");
  Serial.print(gyr_X); Serial.print("\t");
  Serial.print("Y:\t");
  Serial.print(gyr_Y); Serial.print("\t");
  Serial.print("Z:\t");
  Serial.print(gyr_Z); Serial.println("\t");
}

void SetConfiguration(byte reg, byte setting)
{
   // Aufruf des MPU6050 Sensor
  Wire.beginTransmission(sensor_address);
  // Register Aufruf
  Wire.write(reg);
  // Einstellungsbyte für das Register senden
  Wire.write(setting);
  Wire.endTransmission();
}

Kurzfassung für den Sensor Aufruf, ohne weitere Library.

Mit dem Quellcode wird der Sensor mit den Standardwerten initialisiert, so dass die Empfindlichkeit des Gyroskop bei 250°/s und der Beschleunigungssensor bei 2g liegt. Steht der Sensor still auf dem Tisch sollten die Werte für das Gyroskop und den Beschleunigungssensor für X und Y Achse nahe bei 65536 oder 0 liegen. Beschleunigung auf der Z-Achse bei ca. 16000.

image
Ausgabe über dem Arduino Monitor

Ich hoffe, ich konnte somit dem einen oder anderen mit diesem kleinen Bespiel weiterhelfen. Bei Fragen kann gerne die Kommentarfunktion verwendet werden.

Anschluss Beschreibung:

  • Arduino Nano 3.0 – Pin4 –> MPU6050 Pin SDA
  • Arduino Nano 3.0 – Pin5 –> MPU6050 Pin SCL
  • Arduino Nano 3.0 – Pin5V –> MPU6050 Pin VCC
  • Arduino Nano 3.0 – PinGND –> MPU6050 Pin GND

 

Zuletzt geändert 20.11.2014

Dienstag, 1. Januar 2013

MPU6050 Sensor auslesen mit dem Netduino

image
Netduino mit Shield - Aufsatz, für Sensor und Reglern
Für mein Quadrocopter Projekt habe ich vor einigen Monaten einen zusätzlichen Sensor benötigt, da ich mit einem Beschleunigungssensor allein Schwierigkeiten hätte, das Gleichgewicht und die Lage festzustellen. Um dies etwas kompakter zu gestalten, fand ich im Internet das MPU6050 Sensor Modul, das einen Beschleunigungssensor und Gyroskopsensor auf einem Chip hat. Über Ebay erhält man das Modul zu einem günstigen Preis, man muss allerdings mit einer gewissen Wartezeit rechnen, so ca.. 3-4 Wochen.
image
MPU6050 (Die Platine selbst, scheint für verschieden Sensor Typen gedacht zu sein, wegen der Aufschrift MPU-x050)
Im Arduino Umfeld scheint der Sensor seine Verbreitung gefunden zu haben. Das dürfte auch an der fertigen Library liegen, womit sich in nur wenigen Codezeilen der Sensor für seine eigenen Projekte einbinden lässt . Eine bereitgestellte Lösung für Netduino gab es dafür nicht, was mich um so mehr dazu bewegt hat, hierfür eine Lösung zu finden.
image
Der Sensor verbunden mit dem Arduino Nano 3.0
Mein Ansatz war, den verfügbaren Code in C++ nach C# und .Net Micro Framework zu portieren. Das hieß viele Stunden Quellcode und Dokumentation zum Sensor lesen. Die Dokumentation befasst sich überwiegend mit den Befehlen des Mikroprozessors vom Sensor, was aber anhand der Beschreibung nur schwer nachvollziehbar ist. Überwiegend hat mir der Quellcode selbst am weitesten geholfen, um die Prozessreihenfolge zu verstehen. Am Anfang funktionierte der Code nur teilweise und ich bekam einfach keine brauchbaren Daten. Mehrmals kontrollierte und überarbeite ich das Initialisieren des Sensors über die Monate hinweg. Dazu muss ich hinzufügen, dass ich nur wenig Zeit hatte.
image
Unregelmäßige Werte vom Sensor. (Ausgabe Wert wird hier verkürzt dargestellt zwischen 0 bis 100)
Bis gestern dachte ich, dass ich mit dem Initialisieren den falschen Byte Wert verwendet habe. Es stellte sich heraus, dass ich bereits beim Einbinden des Sensors über den I²C Bus richtig gelegen hatte. Eine fehlende Codezeile, die ich an einer anderen Stelle gesetzt habe, wirkte Wunder. Also wichtiger Hinweis: “Bevor die Werte gelesen werden können, muss der erste Bytewert zunächst an den Sensor geschrieben werden.” Anschließend bekam ich endlich konstante und brauchbare Daten.
image
Stabile Werte, die sich erst verändern, wenn ich den Sensor bewege.
Nun käme normalweise der Punkt, wo ich nun endlich den Quellcode posten würde. Stattdessen hänge ich einen link an, und biete dieses als Download an, da sich der Blogeintrag zu sehr in die länge ziehen würde.
Beispiel Solution für den Sensor MPU6050

Hinweis zum Beispiel Quellcode:
  • Der Quellcode dient als Beispiel und ist daher keine Lösung
  • Soweit ich konnte, habe ich überall Kommentare eingetragen
  • Ich bin für Fragen und (sinnvolle) Kritik offen

Nachtrag (07.07.2013):

Zu dem Thema habe ich eine neuen Blog Post veröffentlicht

Ameisen Simulation und andere Dinge

Wer lange sich schon mit C# beschäftig, hat sicherlich schon mal was von AntMe gelesen oder gehört. Diese Idee hatte ich aufgegriffen und mi...