Kostenloser Versand für Bestellungen über 60 €

So messen Sie die Neigung mit dem BNO055

Neben einem Gyroskop und einem Magnetometer integriert der BNO055 einen dreiachsigen Beschleunigungsmesser in einem einzigen Gehäuse. Hier erfahren Sie, wie Beschleunigungsmesser als Neigungsmesser eingesetzt werden und wie Sie mit dem BNO055 die Neigung messen. Mit anderen Worten: Sie lernen, wie Sie den BNO055 als Neigungsmesser verwenden.

Wir beginnen mit dem praktischen Teil, indem wir einige Neigungsmessungen mit dem BNO055 durchführen. Anschließend zeige ich Ihnen den Arduino-Beispielsketch und erkläre die dahinter stehenden mathematischen Formeln. Bitte beachten Sie, dass dieses Tutorial nicht die Integration von Gyroskop- und Beschleunigungsmesserdaten behandelt. Dennoch benötigen Sie für statische Neigungsmessungen nicht unbedingt ein Gyroskop. In diesem Sinne fangen wir an!

Grundlegende Neigungsmessungen

Beschleunigungsmesser nutzen die Schwerkraft, um ihre Neigung zu messen, und sie messen ständig die Schwerkraft der Erde als auf den Boden gerichtete Beschleunigung. Wenn keine anderen Kräfte auf einen Beschleunigungsmesser einwirken, wird sein Ausgang nur durch die Schwerkraft der Erde beeinflusst.

Ein dreiachsiger Beschleunigungsmesser wie der BNO055 gibt eine Beschleunigungsmessung für jede Achse aus. Daher misst der BNO055 im Ruhezustand mit nach unten zeigender Z-Achse keine Beschleunigung entlang der X- und Y-Achse und die Ausgabe von der Z-Achse ist identisch mit der Erdbeschleunigung.

Beginnen Sie nun mit dem Neigen des BNO055 um die x-Achse. Die Erdbeschleunigung wird nun über die z-Achse und die y-Achse gemessen. Wie stark jede Achse die Schwerkraft misst, hängt vom Neigungswinkel ab. Aus diesem Grund können wir den Neigungswinkel berechnen, wenn wir die Beschleunigungen für die Z- und Y-Achse kennen.

Wir können ein einfaches Experiment durchführen, um dies in die Praxis umzusetzen. In dieser groben Anordnung wird die BNO055-Platine an einem Metalllineal befestigt. Indem wir eine Seite des Lineals auf ein Objekt legen, können wir uns leicht ein Dreieck mit bekannten Seiten vorstellen. Wenn wir beispielsweise ein 50 mm dickes Buch an der 300-mm-Marke unter das Lineal legen (die andere Ecke des Lineals beginnt genau bei 0 mm), berechnen wir den Neigungswinkel mit etwa 9,60°, gegeben durch die Gleichung arcsin ( 50/300). Was würde der BNO055 unter diesen Bedingungen messen?

Unter Verwendung der Adafruit-Bibliothek für den BNO055-Sensor habe ich den Beispiel-Sketch read_all_data genommen und ein paar Änderungen vorgenommen. Sie können die Adafruit-Bibliothek aus ihrem GitHub-Repository herunterladen und den Code unten auf der Seite für Ihre eigenen Messungen kopieren.

In dieser Beispielskizze werden 5 Berechnungsmethoden zur Messung der Neigung mit dem BNO055 verwendet:

  1. Non-Fusion Mode ACCONLY mit der Formel arcsin. Die Berechnung verwendet die Ausgabe des dreiachsigen Beschleunigungsmessers. Der Ausgabebereich liegt zwischen +90° und -90° und die Formel ist für Neigungen nahe ±90° ungenauer.
  2. Non-Fusion Mode ACCONLY mit der Formel arccos. Die Berechnung verwendet die Ausgabe des dreiachsigen Beschleunigungsmessers. Der Ausgabebereich liegt zwischen 0° und +180° (d. h. die Ausgabe ist immer positiv). Bei Neigungen nahe 0° und 180° ist die Formel weniger genau.
  3. Non-Fusion Mode ACCONLY mit der Formel arctan. Die Berechnung verwendet die Ausgabe des dreiachsigen Beschleunigungsmessers. Der Ausgabebereich liegt zwischen -90° und +90°. Die Formel weist eine Diskontinuität bei ±90° auf, was bedeutet, dass bei einer Neigung von +90° die Ausgabe plötzlich auf -90° springt und umgekehrt. Diese Formel sollte vermieden werden.
  4. Non-Fusion Mode ACCONLY mit der Formel arctan2. Die Berechnung verwendet die Ausgabe des dreiachsigen Beschleunigungsmessers. Der Ausgabebereich liegt zwischen -180° und +180° und die Formel weist nur eine Unstetigkeit bei ±180° auf. Diese Formel ist genauer als die anderen drei.
  5. Fusion Mode NDOF. Der BNO055 verwendet Daten vom Beschleunigungsmesser, Gyroskop und Magnetometer, um seine Ausrichtung zu berechnen. Die Ausgabe liegt zwischen 0° und 360° und entspricht einem der Euler-Winkel (Nick-, Gier- und Rollwinkel). Im Fusion-Modus unterscheidet der BNO055 auch zwischen Beschleunigungen aufgrund von Bewegung und Schwerkraft. Diese Methode kann verwendet werden, wenn sich der Beschleunigungsmesser nicht im Ruhezustand befindet.
Das Bild unten zeigt die Messwerte für das Neigungs-Theta für jede Berechnungsmethode. Die Messungen wurden durchgeführt, nachdem der Beschleunigungsmesser des BNO055 kalibriert wurde. Die Ergebnisse liegen sehr nahe an der berechneten Neigung von 9,60°.

Wenn der Beschleunigungsmesser noch unkalibriert ist (d. h. direkt nach dem Einschalten des Sensors), liegen die gemessenen Werte etwa 1 Grad höher als berechnet.

Ja, ich weiß, ein Metalllineal auf einem Buch eignet sich nicht gut, um die Genauigkeit von Neigungsmessungen zu bestimmen. Nachdem ich dieses Experiment jedoch für verschiedene Neigungen wiederholt habe, kann ich folgende Beobachtungen machen:

  • Alle 5 Methoden lieferten nahezu identische Ergebnisse für Neigungen zwischen 0° und 90°.
  • Der Unterschied zwischen den Messungen bei kalibriertem und nicht kalibriertem Sensor war gering (ca. 1° bei meinem Setup).
  • Die Werte des Fusion Mode NDOF veränderten sich kaum, wenn der Sensor im Ruhezustand war.
  • Die Werte aus dem Fusion Mode NDOF veränderten sich aufgrund von Vibrationen nicht.
  • Die Werte der 4 Non-Fusion-Modi zeigten aufgrund von Rauschen aus den Rohbeschleunigungsmessungen einige Abweichungen.
  • Die Werte der 4 Non-Fusion-Modi reagierten sehr empfindlich auf Vibrationen (wie leichte Stöße auf dem Tisch).

Zusammenfassend habe ich festgestellt, dass der Fusion Mode NDOF die beste Methode zur Neigungsmessung ist, wenn der Sensor in Ruhe ist. Dies liegt daran, dass der BNO055 die Messungen des Beschleunigungsmessers, des Gyroskops und des Magnetometers integriert, um Störungen durch Sensorbewegungen zu beseitigen. Es ist jedoch unklar, wie die Daten im BNO055 verarbeitet werden, und abhängig von Ihrer Anwendung müssen Sie möglicherweise Ihre Signalverarbeitung direkt aus den Rohdaten entwickeln. In diesem Fall ist die Methode mit der Arctan2-Formel wahrscheinlich der beste Ausgangspunkt.

Wie Beschleunigungsmesser die Neigung berechnen

Auch wenn Sie lieber den Fusion-Modus zum Messen der Neigung verwenden, möchten Sie vielleicht wissen, wie diese Berechnungen im Hintergrund durchgeführt werden.

Nennen wir die gemessenen Beschleunigungen für die z- und y-Achse az bzw. ay. Die Erdbeschleunigung beträgt g und der Neigungswinkel beträgt θ (Theta). Mithilfe der grundlegenden Trigonometrie können wir den Winkel θ mit diesen drei Gleichungen beschreiben:

sin_theta
cos_theta
tan_theta

Einfach genug. Mithilfe der inversen trigonometrischen Funktionen berechnen wir nun den Winkel θ wie folgt:

arcsin_theta
arccos_theta
arctan_theta
arctan2_theta

Wie Sie sehen, stehen uns vier Gleichungen zur Verfügung. Aber was ist der Unterschied zwischen ihnen und welche Gleichung sollten Sie wählen?

Vergleich der inversen trigonometrischen Funktionen

Die folgende Analyse basiert auf dem Dokument von STMicroelectronics DT040 Tilt computation using accelerometer data for inclinometer applications.

Vergleichen wir nun diese inversen trigonometrischen Funktionen. Angenommen, Sie platzieren den BNO055-Sensor so, dass die Neigung -180° beträgt. Ändern Sie nun langsam die Neigung und hören Sie erst auf, wenn Sie +180° erreicht haben. Welche Werte würden Sie erhalten, wenn Sie die Neigung mit diesen vier Funktionen berechnen würden?

Schauen wir uns zunächst das Verhalten der Arcsin-Funktion an. Das folgende Diagramm zeigt die Beziehung zwischen der tatsächlichen Neigung des Sensors (in Grad) und der mit der Arcsin-Funktion berechneten Neigung.

Wenn Sie den Sensor zwischen -90° und +90° neigen, bleibt die Ausgabe der Arcsin-Funktion ebenfalls in diesem Bereich. Sobald aber die Sensorneigung beispielsweise +90° überschreitet, beginnt die Funktion arcsin rückwärts zu zählen. Daher ist die Ausgabe der Arcsin-Funktion auf den Bereich zwischen -90° und +90° beschränkt.

Die aus der Arccos-Funktion berechnete Neigung ist dagegen immer positiv. Der Ausgabebereich liegt zwischen 0° und 180°. Das heißt, wenn Sie den Sensor um eine Neigung von +30° und dann um -30° neigen, gibt die arccos-Funktion den gleichen Wert von +30° aus.

Die Arctan-Funktion begrenzt nicht nur die Ausgabe zwischen ±90°, sondern weist bei diesen Winkeln auch eine Diskontinuität auf. Mit anderen Worten: Wenn Sie beginnen, die Neigung des Sensors zu erhöhen, springt die Ausgabe der Arctan-Funktion von +90° auf -90°, sobald Sie +90° erreichen. Laut dem Dokument DT040 von STMicroelectronics (siehe Link oben) sollte diese Funktion vermieden werden.

Die arctan2-Funktion hingegen hat einen Ausgabebereich von ±180° und weist keine Diskontinuität auf. Das Dokument DT040 von STMicroelectronics verglich auch die Auswirkungen von Rauschen für jede dieser Formeln und kam zu dem Schluss, dass die arctan2-Funktion am genauesten ist. Aus diesen Gründen, so argumentiert das Dokument, sollte die Funktion arctan2 ausgewählt werden.  

Wenn Sie einen Blick auf meine Berechnungen zum Zeichnen dieser Kurven werfen möchten, können Sie die Excel-Datei Comparing Inverse Trigonometric Functions.xlsx herunterladen. Möchten Sie auch mehr über die arctan2-Funktion erfahren? Ich fand die folgenden Websites sehr hilfreich:

Der Code

Abschließend ist hier der Code, den ich aus dem Beispielsketch read_all_data aus der Adafruit Library BNO055 übernommen habe.

				
					#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BNO055.h>
#include <utility/imumaths.h>

/* This driver uses the Adafruit unified sensor library (Adafruit_Sensor),
   which provides a common 'type' for sensor data and some helper functions.

   To use this driver you will also need to download the Adafruit_Sensor
   library and include it in your libraries folder.

   You should also assign a unique ID to this sensor for use with
   the Adafruit Sensor API so that you can identify this particular
   sensor in any data logs, etc.  To assign a unique ID, simply
   provide an appropriate value in the constructor below (12345
   is used by default in this example).

   Connections
   ===========
   Connect SCL to analog 5
   Connect SDA to analog 4
   Connect VDD to 3.3-5V DC
   Connect GROUND to common ground

   History
   =======
   2015/MAR/03  - First release (KTOWN)
*/

/* Set the delay between fresh samples */
uint16_t BNO055_SAMPLERATE_DELAY_MS = 100;

// Check I2C device address and correct line below (by default address is 0x29 or 0x28)
//                                   id, address
Adafruit_BNO055 bno = Adafruit_BNO055(55, 0x28, &Wire);

void setup(void)
{
  Serial.begin(115200);

  while (!Serial) delay(10);  // wait for serial port to open!

  Serial.println("Orientation Sensor Test"); Serial.println("");

  /* Initialise the sensor */
  if (!bno.begin())
  {
    /* There was a problem detecting the BNO055 ... check your connections */
    Serial.print("Ooops, no BNO055 detected ... Check your wiring or I2C ADDR!");
    while (1);
  }

  delay(1000);
}

void loop(void)
{
  //could add VECTOR_ACCELEROMETER, VECTOR_MAGNETOMETER,VECTOR_GRAVITY...
  sensors_event_t orientationData , angVelocityData , linearAccelData, magnetometerData, accelerometerData, gravityData;
  bno.getEvent(&orientationData, Adafruit_BNO055::VECTOR_EULER);
  bno.getEvent(&accelerometerData, Adafruit_BNO055::VECTOR_ACCELEROMETER);

  uint8_t system, gyro, accel, mag = 0;
  bno.getCalibration(&system, &gyro, &accel, &mag);

  Serial.print("Calibration: Sys=");
  Serial.print(system);
  Serial.print(" Gyro=");
  Serial.print(gyro);
  Serial.print(" Accel=");
  Serial.print(accel);
  Serial.print(" Mag=");
  Serial.print(mag);
  Serial.print("  ");

  printEvent(&accelerometerData);
  printEvent(&orientationData);

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

void printEvent(sensors_event_t* event) {
  double x = -1000000, y = -1000000 , z = -1000000; //dumb values, easy to spot problem
  if (event->type == SENSOR_TYPE_ACCELEROMETER) {
    x = event->acceleration.x;
    y = event->acceleration.y;
    z = event->acceleration.z;

    Serial.print("   \tAccx= ");
    Serial.print(x, 2);
    Serial.print("   \tAccy= ");
    Serial.print(y, 2);
    Serial.print("   \tAccz= ");
    Serial.print(z, 2);

    //######### Computation for Inclinometer Applications #########

    //Now let's calculate the magnitude g of the gravity vector
    //This is given by the equation: gravity = sqrt(x²+y²+z²)
    //The unit of gravity is the same as the units of x, y and z.

    float gravity;
    gravity = sqrt(sq(x) + sq(y) + sq(z));
    Serial.print("   \tgravity= ");
    Serial.print(gravity, 2);

    //Let's assume that the z-axis is pointing down. The values of z and gravity are the same.
    //If we incline the sensor on the y-axis, the value of y will increase, z will decrease and x will remain the same.
    //Let's call the angle of inclination theta.
    //We can calculate theta using four different formulas:
    //theta_asin = asin(y / gravity)
    //theta_acos = acos(z / gravity)
    //theta_atan = atan(y / z)
    //theta_atan2 = atan2(y, z) 
    //The unit of theta is radians. We can convert theta to degrees by multiplying by 180° and dividing by pi.

    double theta_asin;
    theta_asin = asin(y / gravity);
    theta_asin = theta_asin * 180/PI;

    double theta_acos;
    theta_acos = acos(z / gravity);
    theta_acos = theta_acos * 180/PI;  

    double theta_atan;
    theta_atan = atan(y / z);
    theta_atan = theta_atan * 180/PI;   

    double theta_atan2;
    theta_atan2 = atan2(y, z);
    theta_atan2 = theta_atan2 * 180/PI;   
    
    Serial.print("=    \ttheta_asin= ");
    Serial.print(theta_asin, 2);
    Serial.print("=    \ttheta_acos= ");
    Serial.print(theta_acos, 2);
    Serial.print("=    \ttheta_atan= ");
    Serial.print(theta_atan, 2);
    Serial.print("=   \ttheta_atan2= ");
    Serial.print(theta_atan2, 2);

  }
  else if (event->type == SENSOR_TYPE_ORIENTATION) {
    x = event->orientation.x;
    y = event->orientation.y;
    z = event->orientation.z;

    Serial.print("=    \tEuler_z= ");
    Serial.print(z, 2);
  }

  else {
    Serial.print("Unk:");
  }
 

}