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:
- 2 x 2 x 1 mm
- 3 magnetic field channels and 3 acceleration channels
- ±16 gauss magnetic full scale
- ±2/±4/±8 g selectable acceleration full scale
- 16-bit data output
- SPI / I2C serial interfaces
- Analog supply voltage 1.9 V to 3.6 V
- Power-down mode / low-power mode
- Programmable interrupt generators for freefall, motion detection and magnetic field detection
- Embedded temperature sensor
- Embedded FIFO
Einsatzbeispiele:
- als Kompass
- als Neigungssensor
- für Kartenrotation, Bildschirmorientierung
- als Schrittzähler
- Erkennung, wenn das Gerät herunterfällt
- Erschütterungserkennung (Click / Doubleclick)
- als Eingabegerät der Richtung für Spiele
- Erkennung, das etwas herunterfällt (Fall-Airbag-Auslösung)
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:
- für 3.3V ausgelegt, keine 5V anlegen
- Beschleunigungssensor: Verbrauch zwischen 50 und 180 µA (10 Hz - 800 Hz)
- Kompass: Verbrauch 270 µA (20 Hz)
- Verbrauch im PowerDown-Modus: 6 µA
- Betriebstemperatur: -40 ... +85 °C
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.- FIFO Mode (FIFO_CTRL (FMODE [2:0]) = 001): Die X, Y, Z-Werte werden gemessen und auf einen FIFO (First in, First out)-Stack gelegt. Der Speicher reicht für 32 Werte-Triple. Ist der Speicher voll, findet keine Aktualisierung mehr statt. Es kann ein Speicher-Voll-Interrupt ausgelöst werden.
- Stream Mode ((FIFO_CTRL (FMODE [2:0]) = 010)): Die Werte werden kontinuierlich aktualisiert. Es stehen immer die aktuellsten Werte-Triples im Stack bereit.
- weitere Modi siehe Datenblatt
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 WerteBeschleunigung 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):
- Der ICE erreicht eine Beschleunigung von etwa 0,5 m/s2, ein moderner S-Bahn-Triebwagen sogar 1,0 m/s2.
- Während der ersten Schritte eines Sprints wirken Beschleunigungen von etwa 4 m/s2 auf den Sportler.
- Die Erdbeschleunigung durch die Erdanziehung ist 9,81 m/s2. Damit beschleunigt man, wenn man fällt.
- Die Kugel beim Kugelstoßen wird in der Abstoßphase mit etwa 10 m/s2 beschleunigt.
- Bei einer Waschmaschine wirken im Schleudergang mehr als 300 g (˜ 3.000 m/s2) auf den Trommelinhalt.
- Ein Tennisball kann Beschleunigungen bis zu 10.000 m/s2 erfahren.
- Bei Nähmaschinen wirken auf die Nadel Beschleunigungen von bis zu 6000 g (˜ 59 km/s2).
- Bei Nesselzellen wird der Stachel mit bis zu 5.410.000 g (˜ 53 Millionen m/s2) beschleunigt.
- Ein Elektro-Rennauto der Studenten der ETH Zürich absolvierte eine Drag-Racing-Strecke von 100 Fuß (ca. 300 m) in 1,513 Sekunden und brachte es damit auf eine durchschnittliche Beschleunigung vom 18,36 m/s
2 (1,87 g). Ein Tesla Model S P90D bräuchte für die Strecke ca. 1,6 Sekunden, ein Top Fuel Dragster typischerweise weniger als 0,85 Sekunden.
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- wenn eben aufliegend (Z zeigt nach unten)
- wo Y am höchsten ist (dann wo Y hinzeigt)
- wo X am höchsten ist (dann wo X hinzeigt)
- Z zeigt keine Auswirkungen
- wenn senkrecht aufgestellt (Y zeigt nach unten)
- wo Z am niedrigsten ist (dann in Richtung von vorne durch den Sensor durch gesehen)
- X und Y zeigen keine Auswirkungen
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:- Temperatur in °C
- Beschleunigung auf der X/Y/Z-Achse in Einheiten (zusaätzlich: Bereich, Abweichung Delta), mg (Tausendstel Erdbeschleunigung) und ms/s2 (Meter pro Sekunde-zum-Quadrat)
- Neigung der X und Y-Achse in Prozent (90° Winkel = 100%)
- Magnetische Flussdichte auf der X/Y/Z-Achse in mGs (Tausendstel Gauß)
- Nachdem der Kompass durch Drehung kalibriert wurde: Kalibirierungsbereiche für X und Y-Achse, Heading (Peilung) und Himmelsrichtung, in die der Pfeil der Y-Achse weist
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.