LSM303C Kompass/Beschleunigungssensor-Chip über I2C-Bus ansteuern

Ein weiterer Sensor, mit dem ich letztens meinen Zoo angereichert habe, ist der LSM303C Accelerometer / Magnetometer. Also ein Sensor, mit dem man Beschleunigungen und Magnetstärken messen kann. Die Magnetstärken kann man so genau messen, dass man das Erdmagnetfeld erkennen kann und somit einen Kompass hat.

Dies geschieht in 3 Ebenen, den sogenannten X, Y und Z-Achsen für jeweils eine räumliche Dimension, wobei die Z-Achse für auf / nieder steht.


Praktische Anwendung für den Beschleunigungsmesser wäre z. B. ein Schrittzähler, der erkennt, wwann nach einem abwärts wieder ein aufwärts und danach wieder ein abwärts kommt, um dabei je einen Schritt zu zählen.

Der LSM303C hat folgende Eigenschaften:
Einsatzbeispiele:

LSM303C


Pin Name Funktion 1 SCL I2C serial clock SPC SPI port clock 2 CS_XL Accelerometer: SPI enable and mode 3 CS_MAG Magnetometer: SPI enable and mode 4 SDA I2C serial data (SDA) SDI SPI serial data input n(SDI) SDO 3-wire interface (SDO) 5 C1 Capacitor Pin (C1=100 nF) 6 GND GND 7 INT_MAG Magnetometer interrupt signal 8 GND GND 9 Vdd Power supply 1.9V ... 3.6V (abs. max. 4.8V), keine 5V! 10 Vdd_IO Power supply for IO pins (1.71V ... Vdd+0.1) 11 DRDY_MAG Magnetometer data ready 12 INT_XL Accelerometer interrupt signal Technische Daten:

Die Schaltung



Die Schaltung ist denkbar einfach: es müssen nur SDA (BCM 2) und SCL (BCM 3) des I2C-Busses an das Modul angeschlossen werden und dort noch 3.3V und GND. VIN am Modul steht wohl für eine zusätzliche Versorgungsspannung, es reichte bei mir aber, nur 3V3 zu versorgen. Dran denken: keine 5V an diesem Modul! Dafür ist es nicht ausgelegt.

Die restlichen Anschlüsse des Moduls sind, falls man den SPI-Bus benutzen will, für Interrupts und für Clients am I2C-Bus, bei dem das Modul den Server macht. Also für uns nicht von Interesse.

Ich habe auf die Seite mit den interessanten 5 Pins einen 5 Pin-Header an die Unterseite der Platine gelötet, damit ich sie direkt ins Breadboard stecken kann. Schön wäre es zwar gewesen, die Anschlussbezeichnungen oben zu haben, aber wichtiger ist hierbei natürlich noch, dass die Orientierung der Platine mit den aufgedruckten Achsen übereinstimmt und man diese sehen kann.

Der Beschleunigungssensor

Der Beschleunigungssensor kann in einem FIFO-Stack-Modus betrieben werden. Dieser kann benutzt werden, wenn genaue und schnelle Bewegungsabläufe aufgenommen werden sollen, aber der Hostcontroller oder die Schnittstelle dafür zu langsam ist. Suchen wir einmal mit den IC2Tools nach der Neuerrungenschaft: pi@raspberrypi:~ $ i2cdetect -y 1 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- 1d 1e -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- -- Gleich zwei Adresse. Soviel sei verraten: die 0x1d ist für den Beschleunigungssensor und die 01e für den Kompass zuständig. Schauen wir einmal was der Beschleunigungssensor zu sagen hat. pi@raspberrypi:~ $ i2cdump -y 1 0x1d b 0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef 00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 ...............A 10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 20: 07 00 00 04 00 00 00 00 00 00 00 00 00 00 02 20 ?..?..........? 30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 50: b4 88 17 44 01 21 88 e6 2c 20 82 73 00 00 00 47 ???D?!??, ?s...G 60: 47 07 40 40 00 00 00 00 00 80 00 00 00 00 00 00 G?@@.....?...... 70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 ...............A 90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ a0: 07 00 00 04 00 00 00 00 00 00 00 00 00 00 02 20 ?..?..........? b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ d0: b4 88 17 44 01 21 88 e6 2c 20 82 73 00 00 00 47 ???D?!??, ?s...G e0: 47 07 40 40 00 00 00 00 00 80 00 00 00 00 00 00 G?@@.....?...... f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ Laut Datenblatt sollen die Messwerte ab 0x28 stehen. Dort steht aber noch nichts. Denn wir müssen den Chip erst konfugurieren. Wie, das verrät uns ein weiterer Blick ins Datenblatt.

Wichtige Register-Adressen Beschleunigungssensor (Hauptadresse 0x1d): Adresse r/w Name Bedeutung 0x0f r Who Am I 0x41 = 'A' = Beschleunigungssensor, 0x3d = Kompass 0x20-0x26 r/w CTRL_REG Control Register 1-7 0x27 r STATUS_REG Status register 0x28-0x29 r OUT_X X-Beschleunigung (LowByte HighByte) 0x2a-0x2b r OUT_Y Y-Beschleunigung (LowByte HighByte) 0x2c-0x2d r OUT_Z Z-Beschleunigung (LowByte HighByte) 0x2e r/w FIFO_CTRL FIFO Register 0x2f r FIFO_SRC FIFO Source 0x3a-0x3b r/w X_REF X-Referenz (LowByte HighByte) 0x3c-0x3d r/w Y_REF Y-Referenz (LowByte HighByte) 0x3e-0x3f r/w Z_REF Z-Referenz (LowByte HighByte) Wichtige Register-Werte Beschleunigungssensor (Hauptadresse 0x1d): 0x20 CTRL_REG1_A Bit 7 6 5 4 3 2 1 0 Name HR ODR2 ODR1 ODR0 BDU ZEN YEN XEN HR: High resolution (wenn 1) ODR: Output data rate & power mode selection, Default value: 000 Bit 6 5 4 Name ODR2 ODR1 ODR0 0 0 0 Power Down (default) 0 0 1 10 Hz 0 1 0 50 Hz 0 1 1 100 Hz 1 0 0 200 Hz 1 0 1 400 Hz 1 1 0 800 Hz 1 1 1 nicht verwendet BDU: Block Data Update. Default value:0 = continues update ZEN: Z-Achse aktiv. Default value: 1 = aktiv YEN: Y-Achse aktiv. Default value: 1 = aktiv XEN: X-Achse aktiv. Default value: 1 = aktiv 50 Aktualisierungen pro Sekunde sollten ausreichen. Wir wollen weiterhin alle 3 Achsen benutzen. Daraus ergibt sich folgendes Bitmuster: Bit 7 6 5 4 3 2 1 0 Name HR ODR2 ODR1 ODR0 BDU ZEN YEN XEN 0 0 1 0 0 1 1 1 oder 0x27 in hex. Wir setzen also den Befehl i2cset -y 1 0x1d 0x20 0x27 auf der Kommandozeile ab, um den Chip entsprechend zu konfigurieren und siehe da: nun werden auch Messwerte angezeigt: pi@raspberrypi:~ $ i2cdump -y 1 0x1d b 0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef 00: 00 00 00 00 00 00 00 00 00 00 00 e0 fb 00 00 41 ...........??..A 10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 20: 27 00 00 04 00 00 00 ff 3b ff 0f 02 e0 3f 02 20 '..?....;.????? 30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 50: b4 88 17 44 01 21 88 e6 2c 20 82 73 00 00 00 47 ???D?!??, ?s...G 60: 47 07 40 40 00 00 00 00 00 80 00 00 00 00 00 00 G?@@.....?...... 70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 80: 00 00 00 00 00 00 00 00 00 00 00 00 fc 00 00 41 ............?..A 90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ a0: 27 00 00 04 00 00 00 ff 85 ff db 01 de 3e 02 20 '..?....?.???>? b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ d0: b4 88 17 44 01 21 88 e6 2c 20 82 73 00 00 00 47 ???D?!??, ?s...G e0: 47 07 40 40 00 00 00 00 00 80 00 00 00 00 00 00 G?@@.....?...... f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ Hier sind natürlich 0x28 bis 0x2d interessant, die die Rohdaten für die Beschleunigung sind: 0x28-0x29 = 0xff3b = 65339 X-Beschleunigung (LowByte HighByte) 0x2a-0x2b = 0x020f = 527 Y-Beschleunigung (LowByte HighByte) 0x2c-0x2d = 0x3fe0 = 16352 Z-Beschleunigung (LowByte HighByte) Ein wiederholtes Auslesen des X-Wertes können wir also durch i2cget -y 1 0x1d 0x28 w für die X-Achse erreichen. Dabei fällt auf: je stärker ich die Platine beschleunige, desto kleiner wird dieser Wert. 0xffff wird vielleicht für absoluten Ruhezustand stehen. Aber noch handelt es sich hier um Rohdaten.

Magnetkompass und Temperatur

pi@raspberrypi:~ $ i2cdump -y 1 0x1e b 0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef 00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3d ...............= 10: d5 03 70 67 55 4a 31 46 2a b2 86 ac 00 00 00 00 ??pgUJ1F*???.... 20: 18 00 03 00 00 00 00 00 2b e5 47 29 01 eb 00 00 ?.?.....+?G)??.. 30: e8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ?............... 40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3d ...............= 90: d5 03 70 67 55 4a 31 46 2a b2 86 ac 00 00 00 00 ??pgUJ1F*???.... a0: 18 00 03 00 00 00 00 00 2b e5 47 29 01 eb 00 00 ?.?.....+?G)??.. b0: e8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ?............... c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ Wichtige Register-Adressen Magnetkompass (Hauptadresse 0x1e): Adresse r/w Name Bedeutung 0x0f r Who Am I 0x41 = Beschleunigungssensor, 0x3d = Kompass 0x20-0x24 r/w CTRL_REG Control Register 1-5 0x27 r STATUS_REG Status register 0x28-0x29 r OUT_X X-Ausrichtung (LowByte HighByte) 0x2a-0x2b r OUT_Y Y-Ausrichtung (LowByte HighByte) 0x2c-0x2d r OUT_Z Z-Ausrichtung (LowByte HighByte) 0x2e-0x2f r OUT_TEMP Temperatur (LowByte HighByte) Wichtige Register-Werte Magnetkompass (Hauptadresse 0x1e): 0x20 CTRL_REG1_M Bit 7 6 5 4 3 2 1 0 Name TMP_EN OM1 OM0 DO2 DO1 DO0 0 ST TMP_EN: Temperature sensor enable. Default value: 0 (0: disabled; 1: enabled) OM: X and Y axes operation mode (00=low power, 01=medium power, 10=high power, 11=Ultrahigh power mode) DO: Output data rate. Default value: 100 (10 Hz) 000=0.625, 001=1.25, 010=2.5, 011=5, 100=10, 101=20, 110=40, 111=80 Hz Bit 7 6 5 4 3 2 1 0 Name TMP_EN OM1 OM0 DO2 DO1 DO0 0 ST 1 0 0 1 0 0 0 0 = 0x90 Mit i2cset -y 1 0x1e 0x20 0x90 setzen wir diese Werte: 10 Aktualisierungen pro Sekunde, Temperatursensor an. 0x22 CTRL_REG3_M Bit 7 6 5 4 3 2 1 0 Name I2CDIS 0 LP 0 0 SIM MD! MD0 I2CDIS: Disable I2C interface. Default value 0. (0=enable; 1=disable) LP: Low Power Mode (wenn 1, dann 0.625 Hz und minimale Berechnungsarbeit) SIM: SPI Serial Interface mode selection. Default value: 0 0=SPI write only, 1=SPI read and write MD: System operation mode selection. Default value: 11 00=Continuous-conversion mode 01=Single-conversion mode (von 0.625 bis 80Hz) 10=Power Down 11=Power Down Wenn wir Werte wollen, müssen wir den Magnet-Kompass hier einschalten: Bit 7 6 5 4 3 2 1 0 Name I2CDIS 0 LP 0 0 SIM MD1 MD0 0 0 0 0 0 0 0 0 = 0x00 Mit i2cset -y 1 0x1e 0x22 0x00 setzen wir auch diese Werte. Nun sollten sich Werte auslesen lassen: pi@raspberrypi:~ $ i2cdump -y 1 0x1e b 0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef 00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3d ...............= 10: d5 03 70 67 55 4a 31 46 2a b2 86 ac 00 00 00 00 ??pgUJ1F*???.... 20: 90 00 00 00 00 00 00 ff 4d e6 0f 2c da e7 f7 ff ?.......M??,???. 30: e8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ?............... 40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3d ...............= 90: d5 03 70 67 55 4a 31 46 2a b2 86 ac 00 00 00 00 ??pgUJ1F*???.... a0: 90 00 00 00 00 00 00 0f 27 e6 fb 2b 40 e7 f6 ff ?......?'??+@??. b0: e8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ?............... c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ Die Messwerte finden sich ab 0x28: 0x28-0x29 = 0xe64d = 58957 X-Ausrichtung (LowByte HighByte) 0x2a-0x2b = 0x2c0f = 11279 Y-Ausrichtung (LowByte HighByte) 0x2c-0x2d = 0xe7da = 59354 Z-Ausrichtung (LowByte HighByte) 0x2e-0x2f = 0xfff7 = 65527 Temperatur (LowByte HighByte)

Berechnung der Realweltdaten aus den Sensor-Rohdaten

Nun haben wir die Roh-Messwerte. Die müssen aber erst noch umgerechnet werden, damit etwas gut les- und verstehbares rauskommt.

Die wichtigen Eckdaten zur Berechnung beziehen wir wieder aus dem Datenblatt: Sensor characteristics @ Vdd = 2.5V and T=25 °C LA_So Linear acceleration sensitivity FS = ±2 g 0.061 mg/LSB FS = ±4 g 0.122 mg/LSB FS = ±8 g 0.244 mg/LSB LA_TyOff Typical zero-g level offset accuracy ±40 mg LA_TCSo Linear acceleration sensitivity change vs. temperature 0.01 % / °C M_GN Magnetic sensitivity FS = ±16 gauss 0.58 mgauss/LSB M_TyOff Typical zero-gauss level offset accuracy ±1 gauss Konfiguriert haben wir den Beschleunigunssensor auf ±2 g (Default in FS in CTRL_REG4_A (0x23).

1 g (auch "G-Kräfte" oder "G-Force" genannt) entspricht der Erdbeschleunigung von 9.81 m/s2. Wenn etwas zu Boden fällt, so tut es dies mit einer Beschleunigung von 1 g.

Der Sensor zeigt Beschleunigungen mit Werten von -32768 bis +32767 an, je nach Richtung. Ein Vollausschlag (32767) bedeutet bei der Konfiguration FS = ±2 g eine Beschleunigung von 2 g, doppelte Fallgeschwindigkeit. Im Ruhezustand zeigt der Sensor ca. ein g für die Fallgeschwindkeit, also einen Rohwert von etwa +16384 an, was der Erdbeschleunigung entgegengesetzt zur Fallrichtung entspricht, weil - vereinfacht gesagt - er durch den Tisch am Fallen gehindert wird. Fällt das Board mit Erdbeschleunigung herunter, so befindet sich es im "freien Fall" und würde (Luftwiderstand mal nicht berücksichtigt) 0 g messen.

Alle 32768 Einheiten, die pro Richtung zur Verfügung stehen, zusammen entsprechen also 2 g. Demnach entspricht eine Einheit g/(32768)/2 = 0,061 mg (Tausendstel g), was mit dem Datenblatt korrespondiert.

Berechnung Temperatur

Lassen wir uns einmal die Werte ausgeben... Temperatur: -14 (-17 ... -8) Dazu habe ich eine kleine Schleife gebaut, die die Werte fortwährend misst und die Minimal- und Maximalwerte ausgibt.

Leider schweigt sich das Datenblatt über den einfachsten Wert aus. Da aber allgemein eine Referenztemperatur von 25 °C bei Thermometern gilt, wird sich der Wert hierauf beziehen. Steht eine Messwerteinheit für 1/4 °C, dann kommt die Temperatur ganz gut hin. Wir rechnen also temp = 25 + word / 4. und können dann die Tempeartur in Grad Celsius anzeigen: Temperatur: 21.25 °C (21.0 ... 21.75)

Berechnung Beschleunigung

Ein Blick auf unsere Werte Beschleunigung X: 2577 (2557 ... 2647) Beschleunigung Y: 806 (762 ... 836) Beschleunigung Z: 16013 (15767 ... 16039) zeigt uns: Unsere Werte stehen nicht auf Null. Für die Z-Achse hätten wir eigentlich 16384 erwartet. Wir haben also Abweichungen.

Das kann allein schon daraus resultieren, dass der Sensor nicht 100%ig gerade aufliegt, also im rechten Winkel zum Erdmittelpunkt, aus dem die Erdbeschleunigung wirkt. Denn der Gravitationsausschlag von 16384 macht hier einen sehr großen Anteil aus. Fällt der Sensor nun um 90° gedreht, mit der X-Achse nach unten herunter, dann werden wir den Ausschlag auf eben dieser X-Achse und nicht auf der Z-Achse haben. Da wir nie genau wissen, wie der Sensor gerade liegt und die Lage einen großen Anteil ausmacht, können wir die Beschleunigung nur über alle 3 Achsen gesamt betrachtet berechnen.

Jetzt kann man es sich schwer machen und komplizierte Berechnungen anstellen, die Temperatur, Standorthöhe über Normal Null und andere physikalische Messwerte einbezieht. Das hätte den Vorteil, dass der Beschleunigungssensor aus dem Stand heraus funktioniert, also z. B. wenn man ihn während der Autofahrt einschaltet.

Oder man macht es sich einfach. Wir messen einfach die ersten ein, zwei Sekunden die Werte im Ruhezustand, leiten daraus den Mittelwert als Delta ab und ziehen dieses Delta bei den weiteren Ausgaben ab. So haben wir die relative Beschleunigung zum Ruhezustand. Einzige Voraussetzung: Der Sensor muss anfangs ruhig liegen.

So bekommen wir eine übersichtlichere Rohwert-Anzeige: Beschleunigung X: 9 (-33 ... 39, Delta=2582) Beschleunigung Y: 14 (-51 ... 42, Delta=790) Beschleunigung Z: 5 (-35 ... 51, Delta=16013) Wichtig wird jetzt die Summe aller Deltas, dass der Gesamtabweichung im Ruhezustand entspricht. Die aktuelle Beschleunigung ist dann die Summe der Abweichungen zum Ruhezustand.

Nun müssen wir die Messwerteinheiten noch umrechnen, um auf Realwelt-Messwerte zu kommen. Hier kann man m/s2 oder auch g wählen. Da man nur selten im wirklichen Leben die Beschleunigung in Einheiten misst, sondern eher die damit verbundene Trägheitskraft auf den eigenen Körper wahrnimmt, hier zur Anschauung ein paar Beispiele (Quelle Wikipedia): Man kann natürlich auch beide Werte berechnen beschlXG = round((beschlX-bXDelta)*0.061,3) beschlXMS2 = round(beschlXG/1000.*9.81,3) und anzeigen: Beschleunigung X: 5 (-51 ... 39, Delta=1817) = 0.305 mg = 0.003 ms**2 Beschleunigung Y: -1 (-19 ... 43, Delta=193) = 0.305 mg = 0.003 ms**2 Beschleunigung Z: -18 (-46 ... 35, Delta=16076) Das funktioniert aber nur gut, wenn der Sensor plan liegt. Wird er geneigt, wirkt sich die Erdbeschleunigung auch auf die anderen Achsen aus. Dann ist die Erdbeschleunigung nicht mehr ohne erneute Kalibrierung aus der X- / Y- Beschleunigung herausrechenbar.

Berechnung Neigungswinkel

Interessant und praktisch einsetzbar wird die Sache, wenn man die Erdbeschleunigung, die ja immer wirkt, berücksichtigt. Denn je nachdem, wie der Sensor geneigt ist, wirkt die Erdbeschleunigung nicht nur auf die Z-Achse (wenn absolut eben), sondern auf X- und Y-Achse.

Durch das Neigen in alle vier Richtungen - nach vorne, nach hinten, nach links und nach rechts, jeweils um 90° - kann man die Werte für die Maximalneigungen festlegen. Entweder indem man den Benutzer das Gerät kalibrieren lässt, indem er dieses kippt und wir die Maximalmesswerte protokollieren. Oder indem man selbst die Maximalwerte notiert und diese hart kodiert, weil man annimmt, dass der Benutzer evtl. Fehler macht (zu weit kippt, nicht genau plan auflegt etc.).

Unser Kippbereich soll 90° in jede Richtung sein und auf den Kopf stellen soll nicht gelten. Dann komme ich auf folgende Maximalwerte (Delta bereits eingerechnet): X-Achse: -16384 bis +16384, Mitte 0 Y-Achse: -16384 bis +16384, Mitte 0 Bei einem von Haus aus kalibrierten Sensor sollten die Werte bei einer FS = ±2 g so auch sein, denn maximal gekippt befindet sich die betreffende Achse nun in Fallrichtung. Ein bisschen Abweichung ist aber trotzdem, aber diese Werte haben wir ja bereits gemessen und in den Delta-Werten gespeichert. Mit diesem Wissen kann man den Kippwinkel von 0 bis 100% errechnen: kippX = (beschlX-bXDelta) / (16384.-bXDelta) if kippX < -1: kippX= -1 if kippX > 1: kippX= 1 kippY = (beschlY-bYDelta) / (16384.-bYDelta) if kippY < -1: kippY= -1 if kippY > 1: kippY= 1 Kippe ich das Breadboard samt Sensor um 45° (also die Hälfte des rechten Winkels) nach vorne, so müsste ich eine Neigung von 50% erhalten. Das Vorzeichen gibt an, zu welcher Seite gekippt wurde. Beschleunigung X: 7182 (-284 ... 11037, Delta=1821) = 438.102 mg = 4.298 ms**2 Beschleunigung Y: -12 (-173 ... 220, Delta=206) = 438.102 mg = 4.298 ms**2 Beschleunigung Z: -2257 (-5887 ... 535, Delta=16104) Neigung X-Achse: 49.3% Neigung Y-Achse: -0.1%

Interpretation der Magnetometer-Messdaten, Einsatz als Gaußmeter

Der Magnetometer reagiert stark auf magnetische Felder. So kann man alleine schon an den Werte ablesen, wenn sich ein Magnet in der Nähe befindet. Bringe ich einen kleinen, aber starken Neodym-Magneten in 10 cm Nähe, dann hat das schon Ausschläge bis zu 20000 zur Folge. Je nachdem, mit welchem Pol des Dauermagneten ich mich nähere habe ich Werte von -32768 bis +32767. Danach dauert es, dass der Sensor die Magnetisierung wieder abbaut.

Ich rate nicht dazu, einen starken Magneten direkt an den Sensor zu halten. Das könnte ihn zerstören. Das Datenblatt führt unter Absolute Maximum Ratings "MEF - Maximum exposed field - 1000 gauss" auf. Darüber sollte man den Sensor nicht belasten. Doch wieviel sind eigentlich 1000 Gauss und wieviel 1 Gauss?

Gauß (oder auch international Gauss) ist die nach Carl Friedruch Gauß benannte Einheit für die magnetische Flußdichte, oder vereinfacht gesagt: je stärker der Magnet, desto mehr Gauß. Aber eigentlich ist Gauß veraltet. Seit 1970 verwendet man Tesla, wobei 1 Gs = 0,1 mT und 1 Tesla = 10000 Gauß.

Hier ein paar Beispiel-Werte aus der Natur (Quelle Wikipedia und eigene Berechnungen): Tesla Gauß Beispiel 0,00005 T 0,5 Gs Erdmagnetfeld in Deutschland 0,002 T 20 Gs in 1 cm Abstand von einem 100-A-Strom, z. B. Batteriestrom beim Anlassen eines Pkw 0,1 T 1000 Gs handelsüblicher Hufeisenmagnet 0,25 T 2500 Gs ein typischer Sonnenfleck 1,61 T 16100 Gs maximale Flussdichte eines NdFeB-Magneten (Neodym-Eisen-Bor). Typischerweise 1 T bis 1,5 T. NdFeB-Magnete sind derzeit die stärksten Dauermagnete bis 3,0 T 30000 Gs Kernspintomograph für die Anwendung am Menschen. 8,6 T 86000 Gs supraleitende Dipolmagnete des Large Hadron Collider des CERN in Betrieb 23,5 T 235000 Gs derzeit stärkster supraleitender Magnet in der NMR-Spektroskopie 32 T 320000 Gs Stärkster Magnet auf Basis von (hochtemperatur) Supraleitern 100 T 1 Mio. Gs Pulsspule – höchste Flussdichte ohne Zerstörung der Kupferspule, erzeugt für wenige Millisekunden 1-100 10 Mrd - Magnetfeld auf einem Neutronenstern Mio T 1 Bio. Gs Ein Neodym-Magnet ist also 10fache Überdosis für den Sensor.

Zur Empfindlichkeit des Magnetometers meint das Datenblatt: M_GN Magnetic sensitivity FS = ±16 gauss 0.58 mgauss/LSB M_TyOff Typical zero-gauss level offset accuracy ±1 gauss Eine andere Einstellung als FS = ±16 gauss kann der Sensor nicht. Es gilt also immer: Ein Werteschritt ist 0.58 mGs groß. Damit lässt sich leicht der MilliGauß-Wert errechnen: mgaussX = kompassX*0.58 und natürlich auch anzeigen: Kompass X: -5256 = -3048.48 mGs Kompass Y: 6592 = 3823.36 mGs Kompass Z: -2884 = -1672.72 mGs Ich habe mal eine aufgebogene Büroklammer magnetisiert und bekomme damit Ausschläge von immerhin 15'000 mGs, also 15 Gs, wenn ich deren Spitze an den Sensor halte. Die Nadel an einem Analog-Kompass bekomme ich damit nur ein paar Grad abgelenkt, dann schwingt der Kompass wieder zurück. Der Sensor ist also ziemlich empfindlich.

Auch wenn Gegenstände aus magnetisierbarem Metall (etwa eine 2 Euro-Münze) an den Sensor führt, so zeigt er erhöhte Werte aus. Man könnte ihn also evtl. auch zur Erkennung von Rohren und Kabeln in Wänden benutzen, wobei das wohl am besten funktioniert, wenn die Kabel stromführend sind, weil sie dann ein elektromagnetisches Feld aufbauen.

Berechnung des Heading / der Peilung, Bestimmung der Himmelsrichtung

Norden ist weil dann die Feldlinien des Erdmagnetfelds parallel zum Sensor stehen.

Süden ist dementsprechend dort, wo das Gegenteil der Nord-Sensorwerte vorliegt und die anderen Himmelsrichtungen befinden sich dazwischen, wobei Ost und West die gleichen Werte haben und die zweite Achse zur Bestimmung hinzugezogen werden muss.

Um eine Himmelsrichtung anzeigen zu können, muss der Kompass kalibiert werden: Liegt er plan auf, indem er einmal langsam horizontal um die Z-Achse gedreht wird. Die höchsten und niedrigsten Werte der X- und Y-Achse werden mitprotokolliert und das Programm kann danach die Abweichung zum Maximalwert für Norden (auf der X und der Y-Achse) berechnen und damit die Himmelsrichtung.

Will man den Kompass auch schräg halten wollen, dann muss man den Sensor jeweils einmal langsam um alle drei Achsen drehen und dafür die Min- und Max-Werte speichern. Daraus könnte man dann auch bei Schräglage die Himmelsrichtung errechnen.

Genauer und einfacher ist allerdings die Methode, bei der der Kompass eben gehalten wird. Evtl. kann man einmal mit Wasserwaage den Kompass planeben kalibrieren und den Wert der Z-Achse speichern, um dann den Nutzer warnen, wenn der Kompass schräg gehalten wird.

Zur Berechnung werden die Werte der X und Y Achse in ein gedachtes Koordinatensystem eingetragen und danach wird daraus mittels der mathematische Funktion arctan2 der Polarwinkel errechnet, und zwar bezogen auf die Y-Achse. Zeigt der Pfeil der Y-Achse genau Richtung Norden, so ist der Winkel 0° bzw. 360°. 90° entspricht Osten, 180° Süden und 270° Westen. Der Sensor sollte in einem Gerät also so eingebaut werden, dass die Pfeilrichtung Y nach vorne zeigt. Dann kann sich der Nutzer z. B. solange im Kreis drehen, bis das Gerätevordere und er Richtung Norden schaut.

Aus Gründen der Lesbarkeit wird auch gleich die Himmelsrichtung in 45° Genauigkeit errechnet: # Anzahl Einheiten auf den Achsen kXBer = abs(abs(kXMax) - abs(kXMin)) kYBer = abs(abs(kYMax) - abs(kYMin)) # Mitte der Achse kXMit = kXMin + (kXBer / 2.) kYMit = kYMin + (kYBer / 2.) # gedachte Koordinaten X = (kompassX - kXMit) / (kXBer) Y = (kompassY - kYMit) / (kYBer) # Berechnung der Polarpeilung heading = (math.atan2(X, Y)) * 180. / math.pi if heading < 0: heading += 360 # "Umrechnung" in Richtungsangabe if heading >= 315+22.5 or heading < 0+22.5: richt="N" elif heading >= 0+22.5 and heading < 45+22.5: richt="NO" elif heading >= 45+22.5 and heading < 90+22.5: richt="O" elif heading >= 90+22.5 and heading < 135+22.5: richt="SO" elif heading >= 135+22.5 and heading < 180+22.5: richt="S" elif heading >= 180+22.5 and heading < 225+22.5: richt="SW" elif heading >= 225+22.5 and heading < 270+22.5: richt="W" elif heading >= 270+22.5 and heading < 315+22.5: richt="NW" Die Ausgabe des Programmes sieht dann z. B. so aus: Kompass X: -4217 = -2445.86 mGs Kompass Y: 6746 = 3912.68 mGs Kompass Z: -3205 = -1858.9 mGs Kompass X Bereich: -5757 ... -2239 Kompass Y Bereich: 3176 ... 6904 Kompass Richtung: 352.3° (N)

Sourcecode

Der folgende Code zeigt fortwährend auf der Kommandozeile folgende Werte an: Es wird ein Wert alle Zehntelsekunde genommen. Die erstenb 20 Messungen (2 Sekunden) muss der Sensor eben und still liegen (Ruheposition); diese Werte werden zur Kalibrierung hergenommen. Danach erfolgt die Anzeige der Beschleunigungs- und Neigungswerte relativ zur Ruheposition.

Danach kann der Kompass kalibriert werden, indem er horizontal (am besten auf einer ebenen Fläche gedreht wird). Das sollte langsam geschehen und gesamt etwa 2 Sekunden dauern. Nach Kalibrierung erfolgt dann auch die Anzeige der Himmelsrichtung.

# -*- encoding: utf-8 -*- # (C) 2018 by Oliver Kuhlemann # Bei Verwendung freue ich mich um Namensnennung, # Quellenangabe und Verlinkung # Quelle: http://cool-web.de/raspberry/ import RPi.GPIO as GPIO # Funktionen für die GPIO-Ansteuerung laden from time import sleep # damit müssen wir nur noch sleep() statt time.sleep schreiben import time import datetime from sys import exit # um das Programm ggf. vorzeitg zu beenden import os from subprocess import PIPE, Popen import smbus # für den I2C-Bus import math GPIO.setmode(GPIO.BCM) # die GPIO-Pins im BCM-Modus ansprechen # Definitionen für den Sensor - bleiben gleich, darum fest im Code # hier nur zur Dokumentation #i2cBeschl = 0x1d # I2C-Adresse #i2cKompass = 0x1e # I2C-Adresse # jeweils 2 Bytes LoHi #adBeschlX = 0x28 #adBeschlY = 0x2a #adBeschlZ = 02c #adKompassX = 0x28 #adKompassY = 0x2a #adKompassZ = 0x2c #adTemperatur = 0x2e # mit im Kompass drin i2c = smbus.SMBus(1) # Erzeugen und Öffnen einer I2C-Instanz def initBeschl(): i2c.write_byte_data (0x1d, 0x20, 0x27) def initKompass(): i2c.write_byte_data (0x1e, 0x20, 0x90) i2c.write_byte_data (0x1e, 0x22, 0x00) def getTemp(): word=i2c.read_word_data(0x1e,0x2e) if word > 32767: word -= 65536 temp = 25 + word / 4. # nur durch rumprobieren geschätzt, dürfte aber stimmen return temp def getBeschlX(): # Beschleunigungssensor X-Achse word=i2c.read_word_data(0x1d,0x28) if word > 32767: word -= 65536 return word def getBeschlY(): word=i2c.read_word_data(0x1d,0x2a) if word > 32767: word -= 65536 return word def getBeschlZ(): word=i2c.read_word_data(0x1d,0x2c) if word > 32767: word -= 65536 return word def getKompassX(): # Magnetometer X-Achse word=i2c.read_word_data(0x1e,0x28) if word > 32767: word -= 65536 return word def getKompassY(): word=i2c.read_word_data(0x1e,0x2a) if word > 32767: word -= 65536 return word def getKompassZ(): word=i2c.read_word_data(0x1e,0x2c) if word > 32767: word -= 65536 return word # --- Ende Funktionen --- Beginn Hauptprogramm ------------------------------------------- try: initBeschl() initKompass() bXMin=32767 bXMax=-32768 bYMin=32767 bYMax=-32768 bZMin=32767 bZMax=-32768 kXMin=32767 kXMax=-32768 kYMin=32767 kYMax=-32768 kZMin=32767 kZMax=-32768 bXDelta=0 bYDelta=0 bZDelta=0 tMin=32767 tMax=-32768 za = 0 # zählt Durchläufe while 1: za+=1 ########################## Temperatur temp = getTemp() if temp < tMin: tMin=temp if temp > tMax: tMax=temp print "Temperatur: " +str(temp)+" °C ("+str(tMin)+" ... "+str(tMax)+")\n" ########################## Beschleunigung beschlX = getBeschlX() if beschlX < bXMin: bXMin=beschlX if beschlX > bXMax: bXMax=beschlX beschlXG = round((beschlX-bXDelta)*0.061,3) beschlXMS2 = round(beschlXG/1000.*9.81,3) print "Beschleunigung X: " +str(beschlX-bXDelta)+" ("+str(bXMin-bXDelta)+" ... "+str(bXMax-bXDelta)+", Delta="+str(bXDelta)+") = "+ str(beschlXG) +" mg = " + str(beschlXMS2) +" ms**2" beschlY = getBeschlY() if beschlY < bYMin: bYMin=beschlY if beschlY > bYMax: bYMax=beschlY beschlYG = round((beschlY-bYDelta)*0.061,3) beschlYMS2 = round(beschlYG/1000.*9.81,3) print "Beschleunigung Y: " +str(beschlY-bYDelta)+" ("+str(bYMin-bYDelta)+" ... "+str(bYMax-bYDelta)+", Delta="+str(bYDelta)+") = "+ str(beschlXG) +" mg = " + str(beschlXMS2) +" ms**2" beschlZ = getBeschlZ() if beschlZ < bZMin: bZMin=beschlZ if beschlZ > bZMax: bZMax=beschlZ print "Beschleunigung Z: " +str(beschlZ-bZDelta)+" ("+str(bZMin-bZDelta)+" ... "+str(bZMax-bZDelta)+", Delta="+str(bZDelta)+")\n" ########################## Neigungswinkel kippX = (beschlX-bXDelta) / (16384.-bXDelta) if kippX < -1: kippX= -1 if kippX > 1: kippX= 1 kippY = (beschlY-bYDelta) / (16384.-bYDelta) if kippY < -1: kippY= -1 if kippY > 1: kippY= 1 print "Neigung X-Achse: " +str(round(kippX*100,1))+"%" print "Neigung Y-Achse: " +str(round(kippY*100,1))+"%\n" ########################## Gaußmeter kompassX = getKompassX() if kompassX < kXMin: kXMin=kompassX if kompassX > kXMax: kXMax=kompassX mgaussX = kompassX*0.58 print "Kompass X: " +str(kompassX) + " = " + str(mgaussX) + " mGs" kompassY = getKompassY() if kompassY < kYMin: kYMin=kompassY if kompassY > kYMax: kYMax=kompassY mgaussY = kompassY*0.58 print "Kompass Y: " +str(kompassY) + " = " + str(mgaussY) + " mGs" kompassZ = getKompassZ() if kompassZ < kZMin: kZMin=kompassZ if kompassZ > kZMax: kZMax=kompassZ mgaussZ = kompassZ*0.58 print "Kompass Z: " +str(kompassZ) + " = " + str(mgaussZ) + " mGs\n" # Bei der 20. Messung werden die bisher protokollierten Daten zur Kalibrierung für # den Beschleunigungssensor genutzt if za == 20: # Mittelwert als Delta bXDelta=(bXMin+bXMax)/2 bYDelta=(bYMin+bYMax)/2 bZDelta=(bZMin+bZMax)/2 ########################## Kompass if kompassX > kXMax: kXMax = kompassX if kompassX < kXMin: kXMin = kompassX if kompassY > kYMax: kYMax = kompassY if kompassY < kYMin: kYMin = kompassY print "Kompass X Bereich: " +str(kXMin) + " ... " + str(kXMax) print "Kompass Y Bereich: " +str(kYMin) + " ... " + str(kYMax) if abs(kXMax - kXMin) < 3000 or abs(kYMax - kYMin) < 3000: print "Kompass Richtung: " + "[Kompass 1x horizontal um die Z-Achse drehen]\n" else: # Anzahl Einheiten auf den Achsen kXBer = abs(abs(kXMax) - abs(kXMin)) kYBer = abs(abs(kYMax) - abs(kYMin)) # Mitte der Achse kXMit = kXMin + (kXBer / 2.) kYMit = kYMin + (kYBer / 2.) # gedachte Koordinaten X = (kompassX - kXMit) / (kXBer) Y = (kompassY - kYMit) / (kYBer) # Berechnung der Polarpeilung heading = (math.atan2(X, Y)) * 180. / math.pi if heading < 0: heading += 360 # "Umrechnung" in Richtungsangabe if heading >= 315+22.5 or heading < 0+22.5: richt="N" elif heading >= 0+22.5 and heading < 45+22.5: richt="NO" elif heading >= 45+22.5 and heading < 90+22.5: richt="O" elif heading >= 90+22.5 and heading < 135+22.5: richt="SO" elif heading >= 135+22.5 and heading < 180+22.5: richt="S" elif heading >= 180+22.5 and heading < 225+22.5: richt="SW" elif heading >= 225+22.5 and heading < 270+22.5: richt="W" elif heading >= 270+22.5 and heading < 315+22.5: richt="NW" print "Kompass Richtung: " +str(round(heading,1)) + "° ("+ richt+")\n" print "-----------\n" sleep (.1) except KeyboardInterrupt: pass i2c.close() Die komplexeren Dinge wurden ja schon erklärt, der Rest des Codes sollte leicht zu verstehen sein, die Kommentare helfen.

Als nächsten Schritt werde ich meine Arduino 8x8-LED-Matrix-Platine um den Sensor erweitern, um so eine tragbare Lösung zu haben, die mit Batterien läuft. Denn ein Kompass macht zuhause wenig Sinn. Und auch ein Beschleunigungsmesser ist z. B. im Auto oder auf der Zugfahrt wesentlich nützlicher. Die 8x8-LED-Matrix dürfte sich zur Anzeige eignen.