Using the BNO086 with SPI configuration along with the ESP Thing Plus C microcontroller, and the sparkfun bno08x arduino library. I am executing a fairly simple program. After the setup of the BNO08x myIMU object for SPI and the GameRotationVector (4 millisec interval) the IMU provides the correct roll, pitch and yaw values as viewed using the Arduino IDE ‘serial plotter’. The IMU is running without resetting. Around 15 minutes to 30 minutes, the IMU ‘appears’ to have the axis values incorrect, such has roll movements provide pitch values (viewing on the serial plotter as well as the logged text values), pitch movements provide yaw values, yaw movements provide roll values. My test monitoring consists of the IMU test program continuously running, about every 15 minutes I manually move the IMU to visually see how the R,P,Y movements correspond to the R,P,Y values determined via the IMU. When I see the incorrect values,I reset the microcontroller test program which re-initialises the IMU, then the IMU R, P, Y values are connect for a while. I have run this scenario many times with the longest duration for 5 hours (checking every 15 minutes). The IMU axis values could be changing more often than I am checking, yet the axis appear to be accurate at least for several minutes duration.
I’ll probably test a couple more BNO086 IMUs on a different setup to see how that does. My current BNO086/ESP32 Thing Plus C assembly (destined to be a quadcopter flight controller) is ‘solidly soldered in’, thus the BNO086 is not ‘easily’ swappable.
Possibly someone out there in the ‘community aether’, has also found this problem and can provide some insight.
More information is needed. Please post time series plots of the three angles, showing some examples of the transition.
Since the serial plotter has limited memory, a more productive approach would be to capture the three angles as a .csv file, identify and plot the transition(s) from those data.
You can use a terminal program on a PC to log Serial.print values for an arbitrary period of time.
Hint: the plural of “axis” is “axes”.
Please post the entire program, using code tags.
Here is the code for the BNO086 IMU test with the ESP32 thingPlus C microcontroller using SPI.
/*
Program SF_ESP_ThingPlusC_SPI testing.ino
Test program for BNO086 IMU with ESP32 Thing Plus C microcontroller.
Save for reference:
Using the BNO08x IMU
This example shows how to communicate with the sensor over SPI.
It requires a few more connections than the qwiic cable.
In addition to your usual SPI lines (CS/PICO/POCI/SCK),
This also requires INT and RST. These are crucial for timing when talking to
this sensor.
It outputs the i/j/k/real parts of the rotation vector.
https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation
By: Nathan Seidle
SparkFun Electronics
Date: December 21st, 2017
SparkFun code, firmware, and software is released under the MIT License.
Please see LICENSE.md for further details.
Originally written by Nathan Seidle @ SparkFun Electronics, December 28th, 2017
Adjusted by Pete Lewis @ SparkFun Electronics, June 2023 to incorporate the
CEVA Sensor Hub Driver, found here:
https://github.com/ceva-dsp/sh2
Also, utilizing code from the Adafruit BNO08x Arduino Library by Bryan Siepert
for Adafruit Industries. Found here:
https://github.com/adafruit/Adafruit_BNO08x
Also, utilizing I2C and SPI read/write functions and code from the Adafruit
BusIO library found here:
https://github.com/adafruit/Adafruit_BusIO
Reference: IoT RedBoard --> BNO08x
D5 --> CS
PICO --> SI
POCI --> SO
SCK --> SCK
A4 --> INT
A5 --> RST
3V3 --> 3V3
GND --> GND
BNO08x "mode" pins set for SPI:
PSO --> 3V3
PS1 --> 3V3
Serial.print it out at 115200 baud to serial monitor.
Feel like supporting our work? Buy a board from SparkFun!
https://www.sparkfun.com/products/22857
Yep, I bought 4 so far. :)
*/
#include "WiFi.h"
#include "SparkFun_BNO08x_Arduino_Library.h" // CTRL+Click here to get the library: http://librarymanager/All#SparkFun_BNO08x
BNO08x myIMU;
//#define BNO08X_ADDR 0x4B // Not needed with SPI.
#define BNO08X_CS 32
#define BNO08X_INT 4
#define BNO08X_RST 33
#define SCOPE_PROBE 13
float yaw = 0;
float pitch = 0;
float roll = 0;
float rollCalibration = 0;
float pitchCalibration = 0;
float yawCalibration = 0;
int calibrationCount = 500;
unsigned long currentTime = 0;
unsigned long displayTimesync = 0;
unsigned long eventTimesync = 0;
void setup() {
pinMode(SCOPE_PROBE, OUTPUT);
digitalWrite(SCOPE_PROBE,LOW);
Serial.begin(115200);
while(!Serial) delay(10); // Wait for Serial to become available.
// Necessary for boards with native USB (like the SAMD51 Thing+).
// For a final version of a project that does not need serial debug (or a USB cable plugged in),
// Comment out this while loop, or it will prevent the remaining code from running.
WiFi.mode(WIFI_OFF);
Serial.println(">>> WiFi turned off <<<");
Serial.println();
Serial.println("BNO08x initialization.");
if (myIMU.beginSPI(BNO08X_CS, BNO08X_INT, BNO08X_RST, 3000000) == false) { //3M maximun.
Serial.println("BNO08x not detected. Check your jumpers and the hookup guide. Freezing...");
while (1)
;
}
Serial.println("BNO08x found!");
delay(100); //***
if (myIMU.wasReset()) { //***
setReports();
}
Serial.println("IMU reading events");
delay(100); //This delay seems to be critical. Without it the ESP32 uC continuously executes a hardware reset apparently not recognizing
// the incoming interrupt from the BNO086 IMU.
}
void setReports(void) {
Serial.println("Setting desired reports.");
if(myIMU.enableGameRotationVector(4.00) == true) {
Serial.println(F("Rotation vector enabled"));
Serial.println(F("Output in form i, j, k, real, accuracy"));
}
else {
Serial.println("Could not enable rotation vector");
}
delay(100); // This delay allows enough time for the BNO086 to accept the new
// configuration and clear its reset status.
}
void loop() {
currentTime = millis();
if (myIMU.wasReset()) {
Serial.print("sensor was reset ");
setReports();
}
if(currentTime > (eventTimesync + 4.00)) {
if (myIMU.getSensorEvent() == true) {
if (myIMU.getSensorEventID() == SENSOR_REPORTID_GAME_ROTATION_VECTOR) {
eventTimesync = currentTime;
digitalWrite(SCOPE_PROBE, HIGH);
// IMU Yaw, pitch, roll.
roll = (myIMU.getRoll()) * 180.0 / PI; // Convert roll to degrees
pitch = (myIMU.getPitch()) * 180.0 / PI; // Convert pitch to degrees
yaw = (myIMU.getYaw()) * 180.0 / PI; // Convert yaw / heading to degrees
digitalWrite(SCOPE_PROBE,LOW);
//calibrationCount = 0; //for debug.
if(calibrationCount > 0) {
calibrationCount--;
rollCalibration = (0.90 * rollCalibration) + (0.10 * roll);
pitchCalibration = (0.90 * pitchCalibration) + (0.10 * pitch);
yawCalibration = (0.90 * yawCalibration) + (0.10 * yaw);
if(calibrationCount == 0) {
Serial.print("roll: "); Serial.print(roll); Serial.print(", "); Serial.print("rollCalib: "); Serial.println(rollCalibration);
Serial.print("pitch: "); Serial.print(pitch); Serial.print(", "); Serial.print("pitchCalib: "); Serial.println(pitchCalibration);
Serial.print("yaw: "); Serial.print(yaw); Serial.print(", "); Serial.print("yawCalib: "); Serial.println(yawCalibration);
delay(5000);
}
}
roll -= rollCalibration;
pitch -= pitchCalibration;
yaw -= yawCalibration;
if(calibrationCount == 0) {
if(currentTime > (displayTimesync + 50)) {
displayTimesync = currentTime;
Serial.print("R:"); Serial.print(roll, 2); Serial.print(", ");
Serial.print("P:"); Serial.print(pitch, 2); Serial.print(", ");
Serial.print("Y:"); Serial.println(yaw, 2);
}
}
}
}
}
}
I also tested this same scenario using another BNO086 IMU with a ESP32 IOT Redboard microcontroller using SPI, this exhibited the same issue. Then I configured the hardware and software to run the same test using the I2C interface, which so far in the duration of about 15 hours has not exhibited this issue.
This looks like nonsense to me, but it might be useful for very short term orientation of a game headset. It is not at all useful for long term navigation. Where did you find this code?
if(calibrationCount > 0) {
calibrationCount--;
rollCalibration = (0.90 * rollCalibration) + (0.10 * roll);
pitchCalibration = (0.90 * pitchCalibration) + (0.10 * pitch);
yawCalibration = (0.90 * yawCalibration) + (0.10 * yaw);
To be honest, I and many other people gave up on Bosch IMUs years ago, because the built in sensor calibration works so poorly. I don’t recommend them at all.
A manually calibrated non-Bosch 9DOF sensor running the Mahony or Madgwick filter does not need ad hoc corrections like the above, and will produce superior orientation measurements.
>>>
this looks like nonsense to me, but …
Perhaps, yet the intent was a manual calibration on the first 500 data values (I wrote that code clause) because I could not get the ‘auto-calibration’ to be less than 8.5 degrees (when level) on one of the axes (and thanks for the spelling tip); it was just a test, and yes agreed not optimal.
>>>
To be honest, I and many other people gave up on Bosch IMUs years ago, because the built in sensor calibration works so poorly. I don’t recommend them at all.
Great to know this. Which IMUs do you recommend for a DIY 5 inch quadcopter flight controller? My first step is to get the quadcopter hovering and stable. I have few other posts related on this if you are interested where using the BNO086 IMU with the I2C interface ( and velox 2306 motors) did actually stay in the air (barely?) I know I’m at least 5 years late to this ‘party’, yet it is what it is and I now have more time to dedicate to learning.
>>>
A manually calibrated non-Bosch 9DOF sensor running the Mahony or Madgwick filter does not need ad hoc corrections like the above, and will produce superior orientation measurements.
Thanks, this is ‘now’ my next step to gain a better understanding of these filtering techniques. Also from my reading, it looks like the Madgwick filter is a good starting point.
Any of the latest 9DOF sensors will outperform the BNO086, especially for your purposes. Sparkfun sells the ICM-20948, for example.
The Mahony filter is faster and simpler, and performs as well as the Madgwick. Example code for the ICM-20948 can be found here.
It is absolutely essential that the magnetometer is properly calibrated in place. Best overview tutorial.
Aside: Euler angles do not behave at all like Cartesian coordinates: you cannot simply add angles from successive rotations, and the axial order of applying the rotations matters a great deal. There are 12 different definitions for them.