Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 18 of 18

Thread: BNO080 Observations

  1. #1
    Senior Member
    Join Date
    Oct 2017
    Location
    Houston
    Posts
    443

    BNO080 Observations

    I've been doing some testing on the BNO080 IMU and have found some things that I thought might be interesting to others. I created two perma-boards, one with T4 connected to a BNO080 via I2C, and the other a T4 connected to a BNO080 via SPI (see attached photo).

    First, the BNO080 IMU is fascinating in that it has "self-calibration," meaning can keep a pretty darn good heading without going through a messy magnetometer calibration process. (Note: I do run the Sparkfun calibration sketch when I start up testing just to make sure it's starting out ok, but it's a quick process).

    Would love to hear what others are finding w.r.t. the BNO080, or to get feedback on my thoughts below.

    As far as a few observations:

    1) The calibration process (see the Sparkfun lib for sample sketch) works pretty well. Oddly enough, though, it seems that while it's easy to get the "high" system level calibration, there's always one or two sensors that read medium. And no matter what I do, I don't seem to be able to get ALL the sensors AND the system to go high together. Nonetheless, the IMU seems to be performing really well.

    2) The BNO080 internal quaternion processor seems amazingly smooth. My two sketches (below) compare the internal BNO080-calculated quaternion with one that I do with a Madgwick filter. To really visualize it, open up a Serial Plotter and compare what I'm sending out serially. I'm guessing their internal quaternion processor is running much faster than the Madgwick I'm running.

    3) After running this via I2C for awhile, I thought I'd try the SPI interface. I expected to see a huge speed increase, but don't think I'm seeing that. Maybe someone has a suggestion here. Is there a simple way for me to see how fast the I2C version is running vs. the SPI version? The Sparkfun lib sets the sensor speeds using "myIMU.enableGyro(5);" for example, to set the gyro to 5ms (200Hz) updates. How do I tell what I'm really getting? The sensors should be able to be run at 400Hz, but that'd be an input of "myIMU.enableGyro(2.5);" yet only integers are allowed by the lib it seems.

    Anyway, it's a fascinating sensor to work with...

    Attached are some photos of my perma-boards, along with the ZIP files.
    Attached Thumbnails Attached Thumbnails Click image for larger version. 

Name:	BNO080 Boards.JPG 
Views:	12 
Size:	143.9 KB 
ID:	19429  
    Attached Files Attached Files
    Last edited by Don Kelly; 03-20-2020 at 09:12 PM.

  2. #2
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    5,109
    Hi Don
    Long time. Yeah the BNO080 is kind of a neat little chip. But it was a pain to get working.

    Haven't really played with it much so can't really help you much but as long as the time between reports is fixed not sure you are going to see much difference. As a quick test you can test the time between reports and see what you get or if you have logic analyzer or scope you might be able to measure the timings.

  3. #3
    Senior Member
    Join Date
    Oct 2017
    Location
    Houston
    Posts
    443
    Hey, great to hear from you. I'm using an "if (myIMU.dataAvailable() == true)" line to grab my data, so I'm not really using a fixed time between reports. I guess I need to think about it some more. Maybe I need to try a fixed rate (like 250Hz) and see what happens.

    Interesting stuff. I'll post some things I'm seeing with the ICM-20948 soon too. I also have built a couple of nice self-balancing robots (using the ICM-20948), so will post that as well when I get a chance.

  4. #4
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    5,109
    Cool. I picked up a ICM-20948 as well but haven't really had much time to play with it or the BNO080 except to make sure they worked with the T4. One of these will get back to it.

  5. #5
    Junior Member
    Join Date
    Oct 2013
    Posts
    13
    I have been playing with it recently (use it in my balancing robot). I fixed up SPI a bit and added support for gyro integrated rotation vector. It still needs some testing but, I can get 1 khz updates with gyro integrated rotation vector.
    Try the TareFunction branch from: https://github.com/badVibes--/SparkF...e/TareFunction

  6. #6
    Senior Member
    Join Date
    Oct 2017
    Location
    Houston
    Posts
    443
    That's really cool, are the quat updates also at 1Kz, or is it just gyro 1k updates? Are you connecting the INT pin on the BNO080? I was unsure if that does anything or not.

  7. #7
    Junior Member
    Join Date
    Oct 2013
    Posts
    13
    Quote Originally Posted by Don Kelly View Post
    That's really cool, are the quat updates also at 1Kz, or is it just gyro 1k updates? Are you connecting the INT pin on the BNO080? I was unsure if that does anything or not.
    Yes the quats update at 1khz (and you also get the gyro data). The IMU integrates it from the gyro and corrects it with the underlying rotation vector every 100hz. This period is adjustable and you can also choose which vector corrects it. Either 6 axis game rotation vector (default) or the 9 axis vector rotation vector. There is also an option to enable predictions. I added a method whits lets you write the config to FRS. But that is not yet really tested yet (the prediction part which is disabled by default).

    With SPI you have to connect the INT pin and also the WAKE/P0 pin. I'ts how the ICs let each other know when to communicate.

  8. #8
    Senior Member
    Join Date
    Oct 2017
    Location
    Houston
    Posts
    443
    Great, thanks. I have the INT and WAK connected, just wasn't sure if that was right, so thanks. Will try your 1K updates out...

    By the way, has anyone been able to consistently get the self-calibration to go to HIGH for all sensors? I invariably get one or two that remain at MED no matter what I seem to try.

  9. #9
    Junior Member
    Join Date
    Apr 2020
    Location
    Germany
    Posts
    5
    Quote Originally Posted by Don Kelly View Post
    3) After running this via I2C for awhile, I thought I'd try the SPI interface. I expected to see a huge speed increase, but don't think I'm seeing that. Maybe someone has a suggestion here. Is there a simple way for me to see how fast the I2C version is running vs. the SPI version? The Sparkfun lib sets the sensor speeds using "myIMU.enableGyro(5);" for example, to set the gyro to 5ms (200Hz) updates. How do I tell what I'm really getting? The sensors should be able to be run at 400Hz, but that'd be an input of "myIMU.enableGyro(2.5);" yet only integers are allowed by the lib it seems.
    Hi Don,
    I am also working with the BNO080 and also switched from I2C to SPI due to speed. The main difference you might see is the time it takes for pulling data from the sensor. Therefore you need to measure the time, which it takes to evaluate "myIMU.dataAvailable()".

    The update rates should be similar for I2C and SPI, you can use the following code to check:
    Code:
    #include <SPI.h>
    #include <SparkFun_BNO080_Arduino_Library.h>
    
    BNO080 myIMU;
    
    //These pins can be any GPIO
    byte imuCSPin = 10;
    byte imuWAKPin = 14;
    byte imuINTPin = 16;
    byte imuRSTPin = 15;
    
    unsigned long lastUpdate;  //Used for calc'ing Hz
    
    void setup()
    {
      Serial.begin(115200);
      while (!Serial) {delay(100);}
      Serial.println("BNO080 SPI Read Example");
      
      if(myIMU.beginSPI(imuCSPin, imuWAKPin, imuINTPin, imuRSTPin, 3000000) == false)
      {
        Serial.println("BNO080 over SPI not detected. Are you sure you have all 6 connections? Freezing...");
        while(1);
      }
      //The IMU is now connected over SPI
      //Please see the other examples for library functions that you can call
      myIMU.enableRotationVector(10); //Send data update every 10ms
    
      lastUpdate = micros();
    }
    
    void loop()
    {
      // Look for reports from the IMU
      if (myIMU.dataAvailable() == true)
      {
        Serial.print(1000000.0/(float)(micros()-lastUpdate), 2);
    //    Serial.print(F("Hz"));
        Serial.println();
        lastUpdate = micros();
      }
    }
    To enable the update rate of 400Hz, you need to do some minor changes in the sparkfun library. The sample time is converted to microseconds in the library and only this value is used, hence you can change the enableFunctions input to take microseconds instead of milliseconds. But only sample rates supported by the sensor will work. You need to replace these lines in the library
    Code:
    void BNO080::setFeatureCommand(uint8_t reportID, uint16_t timeBetweenReports, uint32_t specificConfig)
    {
    	 long microsBetweenReports = (long)timeBetweenReports * 1000L;
    
    	shtpData[0] = SHTP_REPORT_SET_FEATURE_COMMAND;	 //Set feature command. Reference page 55
    	shtpData[1] = reportID;							   //Feature Report ID. 0x01 = Accelerometer, 0x05 = Rotation vector
    	shtpData[2] = 0;								   //Feature flags
    	shtpData[3] = 0;								   //Change sensitivity (LSB)
    	shtpData[4] = 0;								   //Change sensitivity (MSB)
    	shtpData[5] = (microsBetweenReports >> 0) & 0xFF;  //Report interval (LSB) in microseconds. 0x7A120 = 500ms
    	shtpData[6] = (microsBetweenReports >> 8) & 0xFF;  //Report interval
    	shtpData[7] = (microsBetweenReports >> 16) & 0xFF; //Report interval
    	shtpData[8] = (microsBetweenReports >> 24) & 0xFF; //Report interval (MSB)
    	shtpData[9] = 0;								   //Batch Interval (LSB)
    	shtpData[10] = 0;								   //Batch Interval
    	shtpData[11] = 0;								   //Batch Interval
    	shtpData[12] = 0;								   //Batch Interval (MSB)
    	shtpData[13] = (specificConfig >> 0) & 0xFF;	   //Sensor-specific config (LSB)
    	shtpData[14] = (specificConfig >> 8) & 0xFF;	   //Sensor-specific config
    	shtpData[15] = (specificConfig >> 16) & 0xFF;	  //Sensor-specific config
    	shtpData[16] = (specificConfig >> 24) & 0xFF;	  //Sensor-specific config (MSB)
    with
    Code:
    void BNO080::setFeatureCommand(uint8_t reportID, uint32_t timeBetweenReports, uint32_t specificConfig)
    {
    
    	shtpData[0] = SHTP_REPORT_SET_FEATURE_COMMAND;	 //Set feature command. Reference page 55
    	shtpData[1] = reportID;							   //Feature Report ID. 0x01 = Accelerometer, 0x05 = Rotation vector
    	shtpData[2] = 0;								   //Feature flags
    	shtpData[3] = 0;								   //Change sensitivity (LSB)
    	shtpData[4] = 0;								   //Change sensitivity (MSB)
    	shtpData[5] = (timeBetweenReports >> 0) & 0xFF;  //Report interval (LSB) in microseconds. 0x7A120 = 500ms
    	shtpData[6] = (timeBetweenReports >> 8) & 0xFF;  //Report interval
    	shtpData[7] = (timeBetweenReports >> 16) & 0xFF; //Report interval
    	shtpData[8] = (timeBetweenReports >> 24) & 0xFF; //Report interval (MSB)
    	shtpData[9] = 0;								   //Batch Interval (LSB)
    	shtpData[10] = 0;								   //Batch Interval
    	shtpData[11] = 0;								   //Batch Interval
    	shtpData[12] = 0;								   //Batch Interval (MSB)
    	shtpData[13] = (specificConfig >> 0) & 0xFF;	   //Sensor-specific config (LSB)
    	shtpData[14] = (specificConfig >> 8) & 0xFF;	   //Sensor-specific config
    	shtpData[15] = (specificConfig >> 16) & 0xFF;	  //Sensor-specific config
    	shtpData[16] = (specificConfig >> 24) & 0xFF;	  //Sensor-specific config (MSB)
    and you also need to change the type of "timeBetweenReports" in all function declarations and definitions from "uint16_t" to "uint32_t"

  10. #10
    Junior Member
    Join Date
    Apr 2020
    Location
    Germany
    Posts
    5
    I made two other observations when using the BNO080:
    1. The update rates are not constant, espacially for the Gyro the fluctuations are quite large
    2. There is a problem with receiving data over SPI when the data is not pulled fast enough. I do not know whether this is a library issue or a sensor issue (see https://forum.sparkfun.com/viewtopic.php?f=83&t=52705 for more details)
      @fsk: I have the same problem with the original Sparkfun library and with your modified version.

  11. #11
    Senior Member
    Join Date
    Oct 2017
    Location
    Houston
    Posts
    443
    DavidG, looks awesome, thx! Will take me a couple days to get time to try this, but looking forward to trying your mods!

  12. #12
    Senior Member
    Join Date
    Oct 2017
    Location
    Houston
    Posts
    443
    DavidG,
    Very cool! Works great. Here's what I get with the T4.0:

    405.84
    369.28
    484.73
    350.39
    427.35
    446.23
    392.16
    384.91
    365.36
    399.84
    484.97
    353.73
    418.94
    478.24
    363.11
    394.94
    364.83
    391.39
    401.28
    411.18

    Awesome!
    Don

  13. #13
    Senior Member
    Join Date
    Oct 2017
    Location
    Houston
    Posts
    443
    See above post for what I see with SPI. It's pretty fast. Here's what I see running same code on I2C:

    227.58 Hz
    228.05 Hz
    375.38 Hz
    376.08 Hz
    376.51 Hz
    376.93 Hz
    193.72 Hz
    373.55 Hz
    197.01 Hz
    371.89 Hz
    376.65 Hz
    219.20 Hz
    225.78 Hz
    375.80 Hz
    226.40 Hz
    377.36 Hz
    377.50 Hz
    376.22 Hz

    Still pretty fast, jumps around more.

  14. #14
    Junior Member
    Join Date
    Apr 2020
    Location
    Germany
    Posts
    5
    Hi Don, would you mind to check something for me? I only have one BNO080 and would like to check, whether the problem I have is a problem with my BNO or if it is a general problem...

    The problem I have is, that if I have my BNO connected over SPI (maybe also with I2C and the interrupt pin connected), I will not be able to receive any sensor updates if I have a delay at the end of the setup part of the code or if I have a process in my main loop, that takes longer than the sample time of the sensor.

    Could you check whether you can reproduce this problem? You just need to add "delay(2000);" at the end of the setup routine or "delay(20);" somewhere in the main loop in code that I have posted above:
    Quote Originally Posted by DavidG View Post
    Code:
    #include <SPI.h>
    #include <SparkFun_BNO080_Arduino_Library.h>
    
    BNO080 myIMU;
    
    //These pins can be any GPIO
    byte imuCSPin = 10;
    byte imuWAKPin = 14;
    byte imuINTPin = 16;
    byte imuRSTPin = 15;
    
    unsigned long lastUpdate;  //Used for calc'ing Hz
    
    void setup()
    {
      Serial.begin(115200);
      while (!Serial) {delay(100);}
      Serial.println("BNO080 SPI Read Example");
      
      if(myIMU.beginSPI(imuCSPin, imuWAKPin, imuINTPin, imuRSTPin, 3000000) == false)
      {
        Serial.println("BNO080 over SPI not detected. Are you sure you have all 6 connections? Freezing...");
        while(1);
      }
      //The IMU is now connected over SPI
      //Please see the other examples for library functions that you can call
      myIMU.enableRotationVector(10); //Send data update every 10ms
    
      lastUpdate = micros();
      delay(2000);       // add this delay
    }
    
    void loop()
    {
      delay(20);       // or this delay
      // Look for reports from the IMU
      if (myIMU.dataAvailable() == true)
      {
        Serial.print(1000000.0/(float)(micros()-lastUpdate), 2);
    //    Serial.print(F("Hz"));
        Serial.println();
        lastUpdate = micros();
      }
    }
    With the delay in the setup function I do not get any sensor readings at all. With the delay in the main loop, I get some updates, but after a few updates it stops.

  15. #15
    Senior Member
    Join Date
    Oct 2017
    Location
    Houston
    Posts
    443
    For SPI, I get the same thing with delay(2000) in the void setup. If I set it to delay(2), delay(20), delay(200), it runs fine, but locks up if set to delay(2000). weird.

    For I2C, it runs fine with the delay(2000).

    Are you getting your board to calibrate all sensors to high? I get that on the I2C, but not on the SPI setup. I'm thinking it's perhaps an issue with the particular board for the SPI setup.

  16. #16
    Junior Member
    Join Date
    Apr 2020
    Location
    Germany
    Posts
    5
    Thanks for the confirmation, maybe I need to switch back to I2C... Do you use I2C with or without interrupt pin?

    I tried to calibrate the sensors and it really difficult, but I managed to calibrate all of them to "high". But after a (very) short time the magnetometer calibration jumped back to "medium" or "low".

    This is the code, that I used for calibration:
    Code:
    #include <SPI.h>
    #include <SparkFun_BNO080_Arduino_Library.h>
    
    BNO080 myIMU;
    
    //These pins can be any GPIO
    byte imuCSPin = 10;
    byte imuWAKPin = 14;
    byte imuINTPin = 16;
    byte imuRSTPin = 15;
    
    void setup()
    {
      Serial.begin(115200);
      while (!Serial) {delay(100);}
      Serial.println("BNO080 SPI Read Example");
      
      if(myIMU.beginSPI(imuCSPin, imuWAKPin, imuINTPin, imuRSTPin, 3000000) == false)
      {
        Serial.println("BNO080 over SPI not detected. Are you sure you have all 6 connections? Freezing...");
        while(1);
      }
    
      //Enable dynamic calibration for accel, gyro, and mag
      myIMU.calibrateAll();                 //Turn on cal for Accel, Gyro, and Mag
    
      myIMU.enableAccelerometer(100);       //Send data update every 100ms
      myIMU.enableGyro(100);                //Send data update every 100ms
      myIMU.enableMagnetometer(100);        //Send data update every 100ms
      myIMU.enableRotationVector(100);      //Send data update every 100ms
    
      //Once magnetic field is 2 or 3, run the Save DCD Now command
      Serial.println(F("Calibrating. Press 's' to save to flash"));
    }
    
    void loop()
    {
      if(Serial.available())
      {
        byte incoming = Serial.read();
    
        if(incoming == 's')
        {
          myIMU.saveCalibration(); //Saves the current dynamic calibration data (DCD) to memory
          myIMU.requestCalibrationStatus(); //Sends command to get the latest calibration status
    
          //Wait for calibration response, timeout if no response
          int counter = 100;
          while(1)
          {
            if(--counter == 0) break;
            if(myIMU.dataAvailable() == true)
            {
              //The IMU can report many different things. We must wait
              //for the ME Calibration Response Status byte to go to zero
              if(myIMU.calibrationComplete() == true)
              {
                Serial.println("Calibration data successfully stored");
                delay(1000);
                break;
              }
            }
    
            delay(1);
          }
          if(counter == 0)
          {
            Serial.println("Calibration data failed to store. Please try again.");
          }
          
          //myIMU.endCalibration(); //Turns off all calibration
          //In general, calibration should be left on at all times. The BNO080
          //auto-calibrates and auto-records cal data roughly every 5 minutes
        }
      }
    
      //Look for reports from the IMU
      if (myIMU.dataAvailable() == true)
      {
        byte accAccuracy = myIMU.getAccelAccuracy();
        byte gyrAccuracy = myIMU.getGyroAccuracy();
        byte magAccuracy = myIMU.getMagAccuracy();
        byte sysAccuracy = myIMU.getQuatAccuracy();
    
        Serial.print(F("Accelerometer: \t"));   printAccuracyLevel(accAccuracy);
        Serial.print(F(",\tGyroscope: \t"));    printAccuracyLevel(gyrAccuracy);
        Serial.print(F(",\tMagnetometer: \t")); printAccuracyLevel(magAccuracy);
        Serial.print(F(",\tSystem: \t"));       printAccuracyLevel(sysAccuracy);
        Serial.println();
      }
    }
    
    //Given a accuracy number, print what it means
    void printAccuracyLevel(byte accuracyNumber)
    {
      if (accuracyNumber == 0) Serial.print(F("Unreliable"));
      else if (accuracyNumber == 1) Serial.print(F("Low"));
      else if (accuracyNumber == 2) Serial.print(F("Medium"));
      else if (accuracyNumber == 3) Serial.print(F("High"));
    }

  17. #17
    Senior Member
    Join Date
    Oct 2017
    Location
    Houston
    Posts
    443
    I use I2C without INT, I just use a qwiic cableClick image for larger version. 

Name:	IMG_3718.JPG 
Views:	1 
Size:	137.7 KB 
ID:	19826.

    I'm getting ready to mount a BNO080 on a larger (SuperDroid Robots model) self balancing robot, and I'll use I2C for that since I'm already using two SPI links to the encoders. I may enable the BNO at a very fast rate, but only grab the sensor data at 100Hz or so.

    I'm using the Sparkfun cal routine, but will give yours a try to see if the BNO behaves any differently. Thx!

  18. #18
    Junior Member
    Join Date
    Apr 2020
    Location
    Germany
    Posts
    5
    My calibration routine is basically a modification of the Sparkfun routine. I doubt that it will give much better results.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •