BNO085 yaw values varies each startup

General project help for Adafruit customers

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
paddy123
 
Posts: 6
Joined: Fri Sep 23, 2022 7:28 am

BNO085 yaw values varies each startup

Post by paddy123 »

Hello there,

I'm using a BNO085 with a Jetson Nano and need to get the orientation as quaternion. Therefore, I use this code (see below). What I need is an absolute vector but each time I run my code, it starts with 0 at the actual orientation. So when I face to east and start, it reports 0 for east. When I shut down and move my robot facing north and start again, my program reports 0 for north. So I get a relative yaw value but I need absolute value

What do I need to change to get a 0 on east always?

To test this, I use this ROS code to get quaternion. I feed this into another node, which translates the quaternion into euler angles (see second code), which returns the yaw value in radians.


EDIT: Instead of reading quaternion from rotation vector, I changed the code to use geomagnetic vector. now I get the same values each time. But to my understanding, geomagnetic vector should be less accurate than rotation vector and rotation vector takes magnetometer into account as well. So I wonder, why rotation vector acts like game vector.

EDIT EDIT: but the geomagnetic_quaternion is not normalized, so it is invalid and looks different to the other

Code: Select all

#!/usr/bin/env python3
# Driver: SPDX-FileCopyrightText: 2020 Bryan Siepert, written for Adafruit Industries

import rospy
from sensor_msgs.msg import MagneticField,Imu
from std_msgs.msg import Float64
from diagnostic_msgs.msg import DiagnosticStatus
import time
import sys
import board
import busio
from adafruit_bno08x import (
    BNO_REPORT_ACCELEROMETER,
    BNO_REPORT_GYROSCOPE,
    BNO_REPORT_MAGNETOMETER,
    BNO_REPORT_ROTATION_VECTOR,
    BNO_REPORT_GYRO_INTEGRATED_ROTATION_VECTOR,
    BNO_REPORT_GEOMAGNETIC_ROTATION_VECTOR
)
from adafruit_bno08x.i2c import BNO08X_I2C

def bno08x_node():

    # Initialize ROS node
    raw_pub = rospy.Publisher('bno08x/raw', Imu, queue_size=10)
    mag_pub = rospy.Publisher('bno08x/mag', MagneticField, queue_size=10)
    status_pub = rospy.Publisher('bno08x/status', DiagnosticStatus, queue_size=10)
    rospy.init_node('bno08x')
    rate = rospy.Rate(100) # frequency in Hz
    rospy.loginfo(rospy.get_caller_id() + "  bno08x node launched.")
    frame_id = rospy.get_param('~frame_id', 'imu')
    # load covariance from parameter
    cov_linear = rospy.get_param('~cov_linear', -1)
    cov_angular = rospy.get_param('~cov_angular', -1)
    cov_orientation = rospy.get_param('~cov_orientation', -1)
    cov_magnetic = rospy.get_param('~cov_magnetic', -1)

    i2c = busio.I2C(board.SCL, board.SDA, frequency=800000)
    bno = BNO08X_I2C(i2c,address=0x4a) # BNO080 (0x4b) BNO085 (0x4a)

    bno.enable_feature(BNO_REPORT_ACCELEROMETER)
    bno.enable_feature(BNO_REPORT_GYROSCOPE)
    bno.enable_feature(BNO_REPORT_MAGNETOMETER)
    bno.enable_feature(BNO_REPORT_ROTATION_VECTOR)
    #bno.enable_feature(BNO_REPORT_GEOMAGNETIC_ROTATION_VECTOR)

    time.sleep(0.5) # ensure IMU is initialized

    while not rospy.is_shutdown():
        raw_msg = Imu()
        raw_msg.header.stamp = rospy.Time.now()
        raw_msg.header.frame_id = frame_id

        accel_x, accel_y, accel_z = bno.acceleration
        raw_msg.linear_acceleration.x = accel_x
        raw_msg.linear_acceleration.y = accel_y
        raw_msg.linear_acceleration.z = accel_z

        gyro_x, gyro_y, gyro_z = bno.gyro
        raw_msg.angular_velocity.x = gyro_x
        raw_msg.angular_velocity.y = gyro_y
        raw_msg.angular_velocity.z = gyro_z
        
        quat_i, quat_j, quat_k, quat_real = bno.quaternion
        raw_msg.orientation.w = quat_real
        raw_msg.orientation.x = quat_i
        raw_msg.orientation.y = quat_j
        raw_msg.orientation.z = quat_k

        raw_msg.orientation_covariance[0] = -1
        raw_msg.linear_acceleration_covariance[0] = -1
        raw_msg.angular_velocity_covariance[0] = -1

        if cov_orientation != -1:
            raw_msg.orientation_covariance[0] = cov_orientation
            raw_msg.orientation_covariance[4] = cov_orientation
            raw_msg.orientation_covariance[8] = cov_orientation


        if cov_linear != -1:
            raw_msg.linear_acceleration_covariance[0] = cov_linear
            raw_msg.linear_acceleration_covariance[4] = cov_linear
            raw_msg.linear_acceleration_covariance[8] = cov_linear

        if cov_angular != -1:
            raw_msg.angular_velocity_covariance[0] = cov_angular
            raw_msg.angular_velocity_covariance[4] = cov_angular
            raw_msg.angular_velocity_covariance[8] = cov_angular

        raw_pub.publish(raw_msg)

        mag_msg = MagneticField()
        mag_x, mag_y, mag_z = bno.magnetic
        mag_msg.header.stamp = rospy.Time.now()
        mag_msg.magnetic_field.x = mag_x
        mag_msg.magnetic_field.y = mag_y
        mag_msg.magnetic_field.z = mag_z

        mag_msg.magnetic_field_covariance[0] = -1
        if cov_magnetic != -1:
            mag_msg.magnetic_field_covariance[0] = cov_magnetic
            mag_msg.magnetic_field_covariance[4] = cov_magnetic
            mag_msg.magnetic_field_covariance[8] = cov_magnetic

        mag_pub.publish(mag_msg)
        
        status_msg = DiagnosticStatus()
        status_msg.level = 0
        status_msg.name = "bno08x IMU"
        status_msg.message = ""
        status_pub.publish(status_msg)

        rate.sleep()   
    
    rospy.loginfo(rospy.get_caller_id() + "  bno08x node finished")

if __name__ == '__main__':
    try:
        bno08x_node()
    except rospy.ROSInterruptException:
        rospy.loginfo(rospy.get_caller_id() + "  bno08x node exited with exception.")

Code: Select all

#!/usr/bin/env python
import rospy
from sensor_msgs.msg import Imu
from tf.transformations import euler_from_quaternion, quaternion_from_euler

def get_rotation (msg):
    global roll, pitch, yaw
    orientation_q = msg.orientation
    orientation_list = [orientation_q.x, orientation_q.y, orientation_q.z, orientation_q.w]
    (roll, pitch, yaw) = euler_from_quaternion (orientation_list)
    print yaw

rospy.init_node('my_quaternion_to_euler')

sub = rospy.Subscriber ('/bno08x/raw', Imu, get_rotation)

r = rospy.Rate(1)
while not rospy.is_shutdown():
    r.sleep()

User avatar
sj_remington
 
Posts: 994
Joined: Mon Jul 27, 2020 4:51 pm

Re: BNO085 yaw values varies each startup

Post by sj_remington »

I don't use python, but it sounds like the sensor is not calibrated.

You do have to go through a calibration procedure at startup, and check the device calibration status from time to time, to make sure that calibration was successful and the orientation data are valid.

User avatar
paddy123
 
Posts: 6
Joined: Fri Sep 23, 2022 7:28 am

Re: BNO085 yaw values varies each startup

Post by paddy123 »

Hi there,
I tried to get calibration status by using python method calibration_status. Because it always return 0, I assume it is not calibrated.
So I added some code which starts calibration in this case by calling begin_calibration method. This returns false in all cases. So i placed a while loop after begin_calibration to check the status. It is always 0 even if I rotate the robot on all axis.

I wasn't able to find any documentation about how to calibrate when using CircuitPython. It would be great if someone can guide me. Until now, I'm not able to use this device for any serious task.

Code: Select all

    while not rospy.is_shutdown():
        if bno.calibration_status == 0:
            rospy.loginfo("bno08x begin calibration")
            bno.begin_calibration
            
            while bno.calibration_status == 0:
                rospy.loginfo("bno08x calibration failed, retry") 
                time.sleep(1.0)

User avatar
gammaburst
 
Posts: 1013
Joined: Thu Dec 31, 2015 12:06 pm

Re: BNO085 yaw values varies each startup

Post by gammaburst »

I'm unfamiliar with Jetson Nano and some parts of your code, but maybe this observation will help you.

I run this code on a FeatherS2 (Adafruit 4769) with CircuitPython 7.3.3 and a BNO085.
After power-up it immediately outputs absolute orientation with correct compass heading.
I'm not doing any calibration steps.

Code: Select all

import time
import board
import busio
import adafruit_bno08x.i2c

i2c = busio.I2C(board.SCL, board.SDA, frequency=100000, timeout=10000)
bno = adafruit_bno08x.i2c.BNO08X_I2C(i2c)
bno.enable_feature(adafruit_bno08x.BNO_REPORT_ROTATION_VECTOR)

while True:
  time.sleep(0.05)
  quat_i, quat_j, quat_k, quat_real = bno.quaternion
  print("%9.5f%9.5f%9.5f%9.5f" % (quat_i, quat_j, quat_k, quat_real))

User avatar
paddy123
 
Posts: 6
Joined: Fri Sep 23, 2022 7:28 am

Re: BNO085 yaw values varies each startup

Post by paddy123 »

Hello,

thanks for your example. Did you confirm that this is really absolute orientation and not relative to startup? I feed the quaternion into a function to get euler angle and see, that geomagnetic vector returns always the real absolute heading, whereas the BNO_REPORT_ROTATION_VECTOR returns a heading of 0 right after start.

So there is always an offset between geomagnetic vector and rotation vector. When moving the device, both vectors follows the real movement, but the offset keeps the same.

User avatar
gammaburst
 
Posts: 1013
Joined: Thu Dec 31, 2015 12:06 pm

Re: BNO085 yaw values varies each startup

Post by gammaburst »

Yes:
Power-down, aim Y-axis north, power-up, quaternions say: 0 0 0 +1
Power-down, aim Y-axis east, power-up, quaternions say: 0 0 -.7 +.7
Power-down, aim Y-axis south, power-up, quaternions say: 0 0 +1 0
Power-down, aim Y-axis west, power-up, quaternions say: 0 0 +.7 +.7

I'm looking at the Y-axis symbol printed on the Adafruit 4754 breakout's silkscreen.
I'm running the example code from my previous message (BNO_REPORT_ROTATION_VECTOR).
I'm slightly rounding-off the quaternion values for clarity.

It's possible my first few quaternions after power-up are incorrect. I can't see them because it takes a couple seconds for the USB to reconnect after power-up.

I tried three different Adafruit 4754 breakouts. They all behave similarly. They are not brand-new out-of-the-bag BNO085 breakouts - I've used them before in various projects. Perhaps one of those projects reconfigured my BNO085s so they behave this way?? Just a wild guess.

Try degaussing/demagnetizing your project. I'm not sure that would help because you're seeing different behavior with different report types, but who knows. I think the BNO085 has several persistent settings such as tare, reorientation, calibration. I don't know much about them.

User avatar
paddy123
 
Posts: 6
Joined: Fri Sep 23, 2022 7:28 am

Re: BNO085 yaw values varies each startup

Post by paddy123 »

SOLVED

I came here to let you know the issue is solved and I'm really impressed of this little neat IMU now. Just for reference to others fighting with similar issues.

When enabling rotation vector, my IMU device didn't report absolute heading but starts from a relative position at each startup. The output was the same as the game rotation vector provides. After several minutes, things starts to improve and heading becomes better but not reliable. This is not useful for my use-case, it has to be accurate right after start.

What I didn't before was to run the calibration procedure. I used the device with factory calibration data and I thought the output with these calibration settings must be better as without any (status reports told me everything is ok and valid). In fact, that's not true on my device.
After running calibration example from Adafruit library and saving the data to the IMU, rotation vector works as expected and described. Meaning it now shows correct absolute heading right after startup. Orientation of my robot is really acurate now.

So if you want to get absolute heading, you must run calibration first. Otherwise it will report relative heading instead. Don't trust status report in this case. It shows good magnetometer calibration but this isn't true.

User avatar
sj_remington
 
Posts: 994
Joined: Mon Jul 27, 2020 4:51 pm

Re: BNO085 yaw values varies each startup

Post by sj_remington »

The magnetometer calibration must be done after installing the sensor in the robot, as the robot components almost always add significant distortions to the Earth's magnetic field.

"Factory calibration" will actually work against you in such a situation.

User avatar
jps2000
 
Posts: 811
Joined: Fri Jun 02, 2017 4:12 pm

Re: BNO085 yaw values varies each startup

Post by jps2000 »

That is correct
You have to mount the sensor in the robot and then do the cal movements like figure of eight with the robot .
Here you see the problem in case the sensor is mounted on a bike or car

Locked
Please be positive and constructive with your questions and comments.

Return to “General Project help”