BNO055 Quaternion to Euler

I'm using a BNO055 to detect if a device is falling over and bring it into safe mode.
I thought this would be a simple task using the nice features of the BNO055... I was wrong.
I have a problem with the Euler Y (and Z) values the BNO055 when using the standard library examples.

X-behaviour : BNO055 flat on the table, turn it clockwise. X increases from 0 to 360 during one complete revolution and then rolls over to 0 again. (That would be a behaviour I'd appreciate for Y)
Note to be more precise: once magnetic calibration has been established, values jump one-time and now the output value gives delta with respect to magnetic north instead of power-on position.
Y-behaviour : Y starts at 0. As you lift up one side of the BNO055, the Y value increases to 90 when perfectly vertical, then decreases again to 0 until perfectly upside-down. Lifting up the other side gives you 0 to -90 to 0.
Z-behaviour: Outputs similar values as Y.
(In examples above I've used integers to simplify. In reality the output values are floats that are only zero upon power-on and never go back to be completely zero. )

My project needs to detect Y angles close to 90° before coming into action and I'm concerned that I could be missing a "falling-over-detection" when the device turns over very fast.
The device could be upside-down with Y values close to 0 as follows:
From a Y sequence of 70 - 74 - 78 - 83 - 87 - 86 - 82 -.. and testing for (Y > 88) you cannot tell if it passed 90 and flipped over or straightened up again.
The exact Y (and maybe Z) values at which the device goes into safety mode will need some experimentation in a next phase.

The Adafruit documentation states that Euler angles should not be used when Y or Z are over 45° and one should be using quaternions.

I've been doing quite a bit of study but I found a lot of code snippets but no firm complete code that does the quaternions to (-179° - 0° - 180°) Euler conversion. From what I understand, it seems a complex task to write and debug.
I wonder if somebody would already have gone through all this and could post working example code of quaternions to (0° - 180°) Euler conversion. Or has solved this problem in another way.

Side remarks:

  • Since I'm concerned about Y angle, (and not navigation) I don't think that I could be running into gimball trouble.
  • I don't want to use mercury switches to detect an upside-down situation given the risk, there are aluminium parts on the device and I would not be happy to be needing mercury.
  • any solution without using quaternions would work for me.
  • I used the unchanged Euler example code from the standard Adafruit library. I don't think it adds value to the question to repost it here.

Please describe the project that you are working on, not the issues with attempting to solve a problem using the BNO055. It is the wrong tool for the job.

It is much easier, faster and more accurate to measure tilt using an accelerometer (either the one in the BNO055, or a dedicated accelerometer sensor), rather than rely on the slow, inaccurate BNO055 3D orientation calculation. See this tutorial for how to do that. How_to_Use_a_Three-Axis_Accelerometer_for_Tilt_Sensing-DFRobot

Note: Euler angles are particularly nasty because there are around 12 different conventions for defining them, and the order in which you apply the rotations is a critical part of the definition. So it is not easy to interpret them.

Accelerometers repond to acceleration as well as orientation, which is why a full IMU is needed to get meaningful orientation when movement is rapid.

An IMU is neither required nor desired for this application, as an object just starting to fall over is not moving rapidly.

Also, the BNO055 is hopelessly inaccurate and unstable for such use.

I don’t have a BNO055 on my desk, so I can’t confirm my code. But I have worked extensively with the BNO055 and it’s adafruit library. I may have a solution for you.

The “Euler Angles” returned by the BNO0555 are not true euler angles, because they are not tied to any of the 12 unique rotation sequences mentioned by jremington. They are “instantaneous orientation angles” and aren’t very useful IMO.

However, the Adafruit BNO055 library returns quaternions from the imu::Quaternion Adafruit_BNO055::getQuat() method. (just in case you want to look through the library source files to see how it works).

This stuff returned by this method is an object of class Quaternion, which is defined within the library folder under utility/quaternion.h

If you look at this file, you’ll see that class Quaternion has a public method, toEuler() that returns a 3-vector set of true euler angles. Which are described below.

From quaternion.h:

    // Returns euler angles that represent the quaternion.  Angles are
    // returned in rotation order and right-handed about the specified
    // axes:
    //
    //   v[0] is applied 1st about z (ie, roll)
    //   v[1] is applied 2nd about y (ie, pitch)
    //   v[2] is applied 3rd about x (ie, yaw)
    //
    // Note that this means result.x() is not a rotation about x;
    // similarly for result.z().

So if you have an Adafruit_BNO055 object in your code (for this example, we’ll call it “bno”), then you should be able to get the true Eulers by doing something like:

imu::Quaternion quat = bno.getQuat();

imu::Vector<3> eul = quat.toEuler();

double roll = eul.x();
double pitch = eul.y();
double yaw = eul.z();

Where quat is your quaternion and eul is the 3-vector containing your euler angles.

I hope this helps.

1 Like

Thanks for all answers !!
I’ve got the application working in a very acceptable way and have been able to test it in real life today. So far, it does what it needs to do. I’m not getting any false positives either.
What I’m not yet sure of is how it will react to a very fast falling over situation.
(Note to JRemington: the device is moving all the time and banking all the time. I just need safe mode when it banks too far, so this is not about ‘starting to fall over’ but rather ‘almost on its side’)

  • My observations are that the BNO055 is working accurately and that Euler angles don’t "creep"over time even after hours of movements within allowed range. (That was completely different with the very-low-cost GY521/MPU6050 devices I started testing with: showed even creep when stationary on a table.)

For now, I worked with orientation and Euler angles, testing on Y>60 or Y<-60 and that works fine.
I’ve added a LED that lights up when Y>48 or Y<-48 and that allows for good testing.

I’ve also increased sampling rate (the rate by which the Arduino is collecting BNO055 output) to once per 100ms in order for a quick transition to safe mode.

So in the coming days and weeks I’ll be trying the suggested solutions one by one, accompanied by LEDs that light up when a piece of logic has triggered. If false positives show up, I’ll know what triggered them.