SW Orientation Sensor - Weber-State-Submarine-Project/Submarine GitHub Wiki
Introduction
This section documents the software implementation of the Orientation package. The package is responsible for capturing data from the BNO08X orientation sensor and converting it into standard ROS 2 IMU messages for integration with other ROS 2 components.
Overview of the Package
The Orientation package consists of two main components:
- Orientation Publisher:
A ROS 2 node that captures data from the BNO08X orientation sensor and publishes it to theorientation_topic
. - Orientation to IMU Converter:
A ROS 2 node that subscribes to theorientation_topic
, processes the data, and publishes it to the/imu
topic in the standardsensor_msgs/Imu
format.
Key functionalities include:
- Data Capture: Collecting acceleration, angular velocity, and orientation data from the BNO08X sensor.
- Custom Message Publication: Publishing orientation data in a custom ROS 2 message format (
orientation_msg/Orientation
). - Data Conversion: Converting custom orientation data to the standardized
sensor_msgs/Imu
message format for use in navigation and localization.
Code Walkthrough
1. Orientation Publisher
The OrientationPublisher
node initializes the BNO08X orientation sensor and publishes raw sensor data to the orientation_topic
. The node:
- Uses the I2C protocol to communicate with the sensor.
- Enables multiple sensor features, including accelerometer, gyroscope, magnetometer, linear acceleration, and rotation vector.
- Publishes data at a fixed interval using a timer.
- Reads in data at 10hz to stabilize imu readings and reduce computational load on the Raspberry Pi
Key Features:
- Error Handling: Includes runtime error handling to manage sensor issues gracefully.
- ROS 2 Publisher: Uses the custom
orientation_msg/Orientation
message type to transmit data.
2. Orientation to IMU Converter
The OrientationToImu
node subscribes to the orientation_topic
and converts the data into a standard ROS 2 Imu
message. It:
- Maps orientation, angular velocity, and linear acceleration data to the appropriate fields in the
sensor_msgs/Imu
message format. - Publishes the converted message to the
/imu
topic for use in localization and navigation.
Challenges
-
Data Synchronization:
Ensuring that orientation updates are synchronized with other sensor data to maintain an accurate representation of the robot’s state. -
Error Management:
Handling exceptions during sensor data retrieval to prevent interruptions in data flow. -
Standardization:
Mapping custom orientation data to the standardizedImu
format required careful alignment of data fields.
Code Listings
Orientation Publisher
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from orientation_msg.msg import Orientation
import board
import busio
from adafruit_bno08x.i2c import BNO08X_I2C
import adafruit_bno08x
import time
class OrientationPublisher(Node):
def __init__(self):
super().__init__('orientation_publisher')
# Initialize I2C bus and BNO08x sensor
self.i2c = busio.I2C(board.SCL, board.SDA)
self.bno = BNO08X_I2C(self.i2c)
# Enable features
self.bno.enable_feature(adafruit_bno08x.BNO_REPORT_ACCELEROMETER)
self.bno.enable_feature(adafruit_bno08x.BNO_REPORT_GYROSCOPE)
self.bno.enable_feature(adafruit_bno08x.BNO_REPORT_MAGNETOMETER)
self.bno.enable_feature(adafruit_bno08x.BNO_REPORT_LINEAR_ACCELERATION)
self.bno.enable_feature(adafruit_bno08x.BNO_REPORT_ROTATION_VECTOR)
self.publisher_ = self.create_publisher(Orientation, 'orientation_topic', 1)
self.timer = self.create_timer(.1, self.timer_callback)
def timer_callback(self):
try:
# Read sensor data
accel_x, accel_y, accel_z = self.bno.acceleration
gyro_x, gyro_y, gyro_z = self.bno.gyro
mag_x, mag_y, mag_z = self.bno.magnetic
linear_accel_x, linear_accel_y, linear_accel_z = self.bno.linear_acceleration
quat_x, quat_y, quat_z, quat_w = self.bno.quaternion
# Create and populate the ROS 2 message
msg = Orientation()
msg.acc_x = accel_x
msg.acc_y = accel_y
msg.acc_z = accel_z
msg.gyro_x = gyro_x
msg.gyro_y = gyro_y
msg.gyro_z = gyro_z
msg.mag_x = mag_x
msg.mag_y = mag_y
msg.mag_z = mag_z
msg.lin_acc_x = linear_accel_x
msg.lin_acc_y = linear_accel_y
msg.lin_acc_z = linear_accel_z
msg.quat_x = quat_x
msg.quat_y = quat_y
msg.quat_z = quat_z
msg.quat_w = quat_w
# Publish the message
self.publisher_.publish(msg)
#self.get_logger().info(f'Publishing: {msg}')
except RuntimeError as e:
pass
self.get_logger().error(f"Runtime error: {e}")
except Exception as e:
pass
self.get_logger().error(f"Error: {e}")
def main(args=None):
rclpy.init(args=args)
node = OrientationPublisher()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
Orientation to IMU Converter
import rclpy
from rclpy.node import Node
from orientation_msg.msg import Orientation
from sensor_msgs.msg import Imu
import numpy as np
class OrientationToImu(Node):
def __init__(self):
super().__init__('orientation_to_imu')
self.subscription = self.create_subscription(
Orientation,
'orientation_topic',
self.listener_callback,
10)
self.publisher_ = self.create_publisher(Imu, '/imu', 10)
self.last_time = self.get_clock().now()
def listener_callback(self, msg):
current_time = self.get_clock().now()
dt = (current_time - self.last_time).nanoseconds / 1e9
self.last_time = current_time
imu_msg = Imu()
imu_msg.header.stamp = current_time.to_msg()
imu_msg.header.frame_id = 'imu'
# Populate IMU message with orientation data
imu_msg.linear_acceleration.x = msg.lin_acc_x
imu_msg.linear_acceleration.y = msg.lin_acc_y
imu_msg.linear_acceleration.z = msg.lin_acc_z
imu_msg.angular_velocity.x = msg.gyro_x
imu_msg.angular_velocity.y = msg.gyro_y
imu_msg.angular_velocity.z = msg.gyro_z
imu_msg.orientation.x = msg.quat_x
imu_msg.orientation.y = msg.quat_y
imu_msg.orientation.z = msg.quat_z
imu_msg.orientation.w = msg.quat_w
self.publisher_.publish(imu_msg)
def main(args=None):
rclpy.init(args=args)
node = OrientationToImu()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()