Neigungskompensierter Kompass (tilt compensated magnetometer/compass)

Hallo Liebe Community,

ich versuche einen neigungskompensierten Kompass mit Hilfe eines
Arduino's, eines Hmc5883l Magnetometers und eines ADXL335 zu bauen.
Leider funktioniert es nicht so wie erwartet und den Fehler kann ich
nicht entdecken...


Zum Beschleunigungssensor ADXL335:

Es ist ein analoger Sensor, weswegen ich zuerst die Volt-Werte lese.

 float xAcc = xRead;
 float yAcc = yRead;
 float zAcc = zRead;

Dann wird ein Offset subtrahiert, damit die X- und Y-Achse auf 0 Volt
liegt, wenn der Sensor HORIZONTAL liegt. Und ein Offset bei zAcc
abgezogen, damit dieser 0 liegt, wenn die Z-Achse horizontal liegt:

 float xAcc = (xRead-327);
 float yAcc = (yRead-333);
 float zAcc = (zRead-334);

Nun noch die Werte skalieren, sodass sie in g (Erdbeschleunigung)
ausgegeben werden:

  float xAcc = (xRead-327)/69.00;
  float yAcc = (yRead-333)/69.00;
  float zAcc = (zRead-334)/69.00;

Liegt der Sensor gerade kommt: X: 0g Y: 0g Z:1g
Liegt die X-Achse oben, kommt: X:1 Y:0 Z:0 usw.

Diese werden nun in Radians umgewandelt:

 float pitch = atan(xAcc/sqrt(pow(yAcc,2) + pow(zAcc,2)));
 float roll = atan(yAcc/sqrt(pow(xAcc,2) + pow(zAcc,2)));

Wobei diese Formel auch funktioniert: (ich weiß nicht warum)

 float rollRadians = asin(yAcc);
 float pitchRadians = asin(xAcc);

Eigentlich müsste das alles stimmen.


Zum Magnetometer HMC5883l:

Diesen steuere ich durch eine Bibliothek an, die ich jetzt nicht genauer
erläutere. Ich bekomme dann die "skalierten" Achsen ausgegeben.

MagnetometerScaled scaled = compass.ReadScaledAxis();
float xMag = scaled.XAxis;
float yMag = scaled.YAxis;
float zMag = scaled.ZAxis;

Hier gibt es noch eine Methode, um die "raw" (Rohdaten) der Achsen
auszugeben. Ich denke aber, dass die nicht zu gebrauchen sind. Oder
kennt sich dort jemand aus?


Zur Neigungskompensation:

Nun benutze ich Formeln, die ich aus dem Internet habe.
Von: 6DOF Arduino | PDF | Compass | Accelerometer

float cosRoll = cos(rollRadians);
float sinRoll = sin(rollRadians);
float cosPitch = cos(pitchRadians);
float sinPitch = sin(pitchRadians);

float Xh = scaled.XAxis * cosPitch + scaled.ZAxis * sinPitch;
float Yh = scaled.XAxis  sinRoll  sinPitch + scaled.YAxis * cosRoll - 
scaled.ZAxis sinRoll  cosPitch;

float heading = atan2(Yh, Xh);

Diese Formel funktioniert komischerweise nicht. Hebe ich beide Sensoren
auf dem Breadboard an der Seite an, bleiben die "Heading"-Werte nicht
gleich, sondern ändern sich.
Ich habe schon pitch und roll getauscht, die Achsen umgedreht, nichts
hat geholfen.

Auch diese Formel, der Webseite Home - TimZaman.com ,
funktioniert nicht:

xh=cx*cos(ayf)+cy*sin(ayf)*sin(axf)-cz*cos(axf)*sin(ayf);
yh=cy*cos(axf)+cz*sin(axf);

Daher frage ich euch: Wo liegt mein Fehler?

Vielen Dank im Voraus! Hoffentlich konnte jemand Nutzen aus meinen
Beitrag ziehen, da es doch leider wenig Dokumentation zur
Neigungskompensation im Internet gibt...

1 Like

RedOneArduino:
Daher frage ich euch: Wo liegt mein Fehler?

Der Fehler liegt darin, dass Du den Sensor nicht individuell kalibriert hast, bevor Du zu rechnen anfängst.

Das, was Du da mit Deiner Library machst:

RedOneArduino:
Diesen steuere ich durch eine Bibliothek an, die ich jetzt nicht genauer
erläutere. Ich bekomme dann die "skalierten" Achsen ausgegeben.

ist die reinste Augenwischerei, wenn es die Library ist, die ich mal kurz angetestet hatte und die ich vermute, dass Du sie verwendest.

Diese Library macht irgendeinen Scheißdreck mit einer statischen Kalibrierung, wobei die statistische Wahrscheinlichkeit, dass die "calibrated" Werte besser zutreffen als die "raw" Werte ungefähr bei 50% liegen. Das ist völlig unbrauchbarer Quatsch, was diese Library als ReadScaledAxis() anbietet. Da kannst Du genau so gut würfeln oder eine Münze werfen und nach dem Ergebnis Deinen Sensor kalibrieren.

Ich habe den HMC5883L selbst mal ein wenig angetestet im Rahmen der von Rabenauge hier angestoßenen Diskussion:
http://forum.arduino.cc/index.php?topic=201453.0

Selbst wenn Du Dich nur auf die waagerechte Ebene beschränkst, und nach besten Kräften Magnetfelder in der Nähe vermeidest: Schon wenn der Sensor auf einer kleinen Platine aufgelötet ist und Du noch eine kleine Headerleiste mit Stahlstifen zum Anlöten von Kabeln dranlötest, ist das Magnetfeld in Sensornähe bereits so verzerrt, dass ohne eine individuelle Kalibrierung des Sensors die Daten kaum irgendwelchen hohen Genauigkeitsanforderungen entsprechen. Erst recht, wenn Du das Sensormodul mit den angelöteten Pin-Header dann noch auf ein Breadboard steckst, das im Innern gespickt ist mit einem Skelett aus Stahl-Klammern.

Wenn Du den Sensor nicht nur in der Ebene kalibrieren möchtest, sondern auch eine Neigungskompensation brauchst, wirst Du eine dynamische Kalibrierung des Sensors in allen drei Raumachsen vornehmen müssen. D.h. Du mußt zunächst am konkreten Sensor eine Messreihe fahren und

  • während einer vollen Dreihung um die x-Achse viele Werte ermitteln
  • während einer vollen Dreihung um die y-Achse viele Werte ermitteln
  • während einer vollen Dreihung um die z-Achse viele Werte ermitteln
    Aus diesen Rohdaten muss dann eine Korrektur-Matrix berechnet werden, die verknüpft mit dem Messwert für jeden Messwert denselben Magnetvektor ermittelt, egal wie die Lage des Sensors im Raum ist.

Mathematische Grundlagen siehe z.B.:
http://www.freescale.com/files/sensors/doc/app_note/AN4246.pdf

Für den Arduino ist z.B. im Multiwii Fluglagecontroller eine Kompass-Kalibrierung enthalten:
http://www.multiwii.com/wiki/index.php?title=Compass_Calibration
Hinweis: Der Kompass muss während der 30-sekündigen Kalibrierungsphase fleißig um alle drei Achsen gedreht werden, so dass Werte aus möglichst viele verschiedenen Raumlagen eingesammelt werden können, oder die Kalibrierung wird vermurkst.

Hier ist auch eine Anleitung, wie Du die Werte der Kalibrierungsmatrix ermitteln kannst, indem Du die Rohdaten-Messwerte erst aufzeichnest und dann mit einem PC-Programm verarbeiten läßt:

Die so ermittelte Matrix muß dann noch invertiert werden. Ich habe mir das aber noch nicht so genau angesehen, mit welchen Formeln dann aus der invertierten Kalibrierungsmatrix die kalibrierten Werte des Magnetsensors errechnet werden.

Und erst wenn Du die korrekten(!) und kalibrierten Sensorwerte hast, kannst Du dann Deine Formeln verwenden, um daraus eine neigungskompensierte Himmelsrichtung zu ermitteln.

Ich bin sprachlos, dass mir jemand geantwortet hat! Allein dafür ein riesen großes Dankeschön!

Dein Ansatz ist sehr gut! Ich habe nicht daran gedacht, dass vielleicht der Kompass einfach nur rumspinnt (-;
Also heißt das, dass meine Formel:

float Xh = scaled.XAxis * cosPitch + scaled.ZAxis * sinPitch;
float Yh = scaled.XAxis  *sinRoll * sinPitch + scaled.YAxis * cosRoll - scaled.ZAxis *sinRoll  *cosPitch;

float heading = atan2(Yh, Xh);

richtig ist?

Ich habe mir nochmal die Library angeguckt und sie ist von loveelectronics.co.uk .
Und die Methode, die die skalierte Achsen berechnet, macht nichts anderes als die "raw" Daten mit 1 zu multiplizieren :stuck_out_tongue:

Gut, dann werde ich gucken, wie man den Magnetometer am Besten kalibriert und es nochmal versuchen!

Vielen, vielen Dank!

So, die Library ist von: http://bildr.org/2012/02/hmc5883l_arduino/

Und die raw-Daten werden doch nicht mit 1 multipliziert, sondern so wie Du sagtest mit irgendwelchen, vordefinierten Werten^^^

Ich versuchs weiter!
RedOneArduino

RedOneArduino:
Also heißt das, dass meine Formel:
...
richtig ist?

Tut mir Leid, aber so gut bin ich in Geometrie nicht, dass ich mir nur die Lösung einer hochgradig komplexen trigonometrischen Aufgabe ansehen brauche und dann verbindlich sagen kann, ob die Lösung richtig oder falsch ist.

Da würde ich schon bemaßte Zeichnungen oder Skizzen sowie die Herleitung der Formeln benötigen, um das nachvollziehen zu können.

RedOneArduino:
Ich habe mir nochmal die Library angeguckt und sie ist von loveelectronics.co.uk .
Und die Methode, die die skalierte Achsen berechnet, macht nichts anderes als die "raw" Daten mit 1 zu multiplizieren :stuck_out_tongue:

Na ja, durch die Multiplikation mit 1 werden die Werte dann ja wenigstens nicht schlechter. Aber auch nicht besser.
Und "kalibriert" sind sie damit noch lange nicht.

RedOneArduino:
Gut, dann werde ich gucken, wie man den Magnetometer am Besten kalibriert und es nochmal versuchen!

Die einfachste Kalibrierung ist wohl die "Hard Iron" Kalibrierung, und wenn ich das recht verstehe, ist das auch in der Hauptsache das, was kalibriert werden müßte. Und diese ist auch recht einfach. Es wird zunächst mal der Mittelwert aller Werte auf einer Achse ermittelt, indem der maximale und der minimale Achsenwert addiert und die Summe durch 2 geteilt wird:
CalibX = 0,5 * (maxXAxis+minXAxis)
CalibY = 0,5 * (maxYAxis+minYAxis)
CalibZ = 0,5 * (maxZAxis+minZAxis)
Dazu muß man den Sensor nacheinander um alle drei Achsen rotieren und die Max/Min-Werte auf den Achsen ermitteln. Das ergibt quasi, wie weit der Nullpunkt des Sensors verschoben ist. Für die skalierten Werte zieht man den so ermittelten Wert einfach ab:
scaledXAxis = XAxis - CalibX
scaledYAxis = YAxis - CalibY
scaledZAxis = ZAxis - CalibZ

Wegen der genauen Berechnungsformeln für die kalibrierten Winkel müßte ich nochmal googeln, oder mir selbst Skizzen machen und selberrechnen.

Zum Experimentieren und verifizieren der Berechnungen wäre es dann wahrscheinlich am günstigsten, ein Kompassmodul zusammen mit einem Beschleunigungsmodul auf einer Platine montiert zu haben, denn nur so kann man vermutlich die Neigung im Raum zuverlässig feststellen. Momentan habe ich nur ein HMC5883L-Modul als Magnetsensor, aber nicht zusammen mit einem Beschleunigungssensor.

Was verwendest Du denn als Sensor, um die Neigung zu ermitteln?

Ich antworte mir mal kurz selbst.

Wie ich festgestellt habe, ist das Ermitteln der maximalen und minimalen Achsen-Rohdatenwerte durch Drehen und Rotieren des Sensors und Messen von Min/Max-Werten der drei Kompassachsen die reinste Sisyphusarbeit. Langweilig und fehlerträchtig und mit hoher Genauigkeit praktisch undurchführbar.

Für die Kompensation von Eisen und fremden Magnetfeldern habe ich daher mal aus dieser Freescale Application-Note:
Calibrating an eCompass in the Presence of Hard and Soft-Iron Interference
http://www.freescale.com/files/sensors/doc/app_note/AN4246.pdf
einen Arduino-Sketch gemacht.

Siehe mein Posting zur eCompass-Kalibrierung von Eisenmagnetfeldern hier:
http://forum.arduino.cc/index.php?topic=201453.msg1738625#msg1738625

Wie gesagt, das ganze macht zwar keinerlei Tilt-Kompensation, aber wenigstens werden Eisenmagnetfelder schon mal auf eine vernünftige Weise kompensiert.

Dann sollte einer Tilt-Kompensation auch nichts im Wege stehen.

1 Like

Erstmal ein riesen Dankeschön, dass Du mir geantwortet hast!

Tut mir leid, dass ich so spät antworte (ca. eine Woche vergangen, denke ich). Ich habe in der Zeit eine library von:
http://krazatchu.ca/2012/02/27/6dof-arduino-compass-accelerometer/
gefunden. Diese Library kalibriert meinen Magnetometer auf Hard-Iron-Fehler :slight_smile:

Und mit dem Programm von: http://davidegironi.blogspot.de/2013/01/magnetometer-calibration-helper-01-for.html#.U4B5JyhvbDQ
,eine Processing-Datei, kann man sich die Werte des Kompasses in einer Kugel angucken.

Letztenendes, habe ich aber aufgeben an meinen Kompass zu arbeiten. Seit knapp 2 Wochen sitze ich dran, und komme kaum zu Ergebnissen. Auch die Kalibrierung hat nicht ausgereicht, um die Fehler bei der Neigungskompensation auszugleichen- Schade eigentlich!
Glücklicherweise bin ich jetzt auf ein Gyroscope umgestiegen: Den MPU 6050! Mit einer genialen Library namens "i2cdevlib" bekomme ich sehr präzise Werte. Besser als mit dem Kompass! Außerdem ist das Gyroscope nicht fehleranfällig für elektrische Komponenten! Und der MPU 6050 hat einen eigenen Chip integriert, der meinem Arduino-Board die Berechnung abnimmt... und und und... Sehr tooles Teil! Nur den Drift muss man ausgleichen, aber das ist nicht so schlimm wie einen Kompass tilt compensated zu machen :wink:

Vielen Dank für Deine Hilfe! Gibt es hier einen Bedanken Button? :smiley:
Ich werden dann nicht mehr auf den Thread antworten.

LG
RedOneArduino

Gibt es hier einen Bedanken Button

Das nennt sich hier "Karma" ...
( Diese Antwort ist gratis, und nach allgemeiner Meinung keinen Karma Punkt wert ) :wink:

Würde mich jetzt mal interessieren, wie du mit der 6050 Drehungen um die Hochachse genau misst....?
DAS ist eigentlich genau das, was mit dem Ding nicht so wirklich geht, drum gibts ja die 9DoF-Ausführung, mit Kompass.

Rabenauge:
Würde mich jetzt mal interessieren, wie du mit der 6050 Drehungen um die Hochachse genau misst....?

Bei der Trägheitsnavigation mußt Du Deine Richtung immer selbst fortschreiben.

Wenn Dein Richtungswinkel zum Zeitpunkt x Sekunden bei 222° ist und Du rotierst während 0,01 s mit einer Gyrorate von 3 Grad pro Sekunde, dann hast Du nach 0,01 Sekunden einen Richtungswinkel von 222,03°, nach 0,02 Sekunden einen von 222,06°, nach 0,03s von 222,09° etc. etc. etc. bis Du nach hundert solchen Schritten nach einer Sekunde bei 225° Richtungswinkel angekommen bist.

Du schreibst anhand der aktuellen Beschleunigungs- und Gyrowerte Deine Richtung immer weiter fort. Dabei entstehen im Laufe der Zeit Driftfehler, Diese Fehler mußt Du von Zeit zu Zeit ausgleichen, z.B. anhand von GPS-Daten.