IMU jetzt sauber als ROS Node eingebunden

This commit is contained in:
Eskimue
2026-05-28 15:18:11 +02:00
parent 580029730a
commit 2ed835d679
5 changed files with 483 additions and 2 deletions
+209
View File
@@ -0,0 +1,209 @@
#!/usr/bin/env python3
import os
import signal
import sys
import time
import warnings
import roslibpy
import adafruit_bno08x
import board
import busio
from adafruit_bno08x import (
BNO_REPORT_ACCELEROMETER,
BNO_REPORT_GYROSCOPE,
BNO_REPORT_MAGNETOMETER,
BNO_REPORT_ROTATION_VECTOR,
)
from adafruit_bno08x.i2c import BNO08X_I2C
RATE_HZ = float(os.environ.get('IMU_RATE_HZ', '15'))
FRAME_ID = os.environ.get('IMU_FRAME_ID', 'imu_link')
ADDRESS = os.environ.get('IMU_I2C_ADDRESS', '0x4A')
ROSBRIDGE_HOST = os.environ.get('IMU_ROSBRIDGE_HOST', '127.0.0.1')
ROSBRIDGE_PORT = int(os.environ.get('IMU_ROSBRIDGE_PORT', '9090'))
RUNNING = True
WORKAROUND_APPLIED = False
def handle_shutdown(signum, frame):
del signum, frame
global RUNNING
RUNNING = False
def apply_bno08x_workaround():
global WORKAROUND_APPLIED
if WORKAROUND_APPLIED:
return
original_handle_packet = adafruit_bno08x.BNO08X._handle_packet
def safe_handle_packet(self, packet):
try:
return original_handle_packet(self, packet)
except KeyError as exc:
if len(exc.args) == 1 and isinstance(exc.args[0], int):
return
raise
adafruit_bno08x.BNO08X._handle_packet = safe_handle_packet
WORKAROUND_APPLIED = True
def connect_ros():
client = roslibpy.Ros(host=ROSBRIDGE_HOST, port=ROSBRIDGE_PORT)
client.run()
start = time.time()
while RUNNING and not client.is_connected:
if time.time() - start > 10.0:
raise RuntimeError('rosbridge connection timeout')
time.sleep(0.1)
return client
def create_topics(client):
imu_topic = roslibpy.Topic(client, '/imu/data', 'sensor_msgs/Imu')
mag_topic = roslibpy.Topic(client, '/imu/mag', 'sensor_msgs/MagneticField')
status_topic = roslibpy.Topic(client, '/imu/status', 'std_msgs/String')
imu_topic.advertise()
mag_topic.advertise()
status_topic.advertise()
return imu_topic, mag_topic, status_topic
def create_sensor():
with warnings.catch_warnings():
warnings.filterwarnings(
'ignore',
message='I2C frequency is not settable in python, ignoring!',
category=RuntimeWarning,
)
apply_bno08x_workaround()
i2c = busio.I2C(board.SCL, board.SDA, frequency=400000)
sensor = BNO08X_I2C(i2c)
for feature in (
BNO_REPORT_ACCELEROMETER,
BNO_REPORT_GYROSCOPE,
BNO_REPORT_MAGNETOMETER,
BNO_REPORT_ROTATION_VECTOR,
):
sensor.enable_feature(feature)
time.sleep(0.2)
return sensor
def ros_time_now():
current = time.time()
secs = int(current)
nsecs = int((current - secs) * 1000000000)
return {'secs': secs, 'nsecs': nsecs}
def publish_status(topic, text):
topic.publish(roslibpy.Message({'data': text}))
def publish_measurements(imu_topic, mag_topic, status_topic, sensor):
stamp = ros_time_now()
acceleration = sensor.acceleration
gyro = sensor.gyro
magnetic = sensor.magnetic
quaternion = sensor.quaternion
imu_topic.publish(roslibpy.Message({
'header': {'stamp': stamp, 'frame_id': FRAME_ID},
'orientation': {
'x': quaternion[0],
'y': quaternion[1],
'z': quaternion[2],
'w': quaternion[3],
},
'orientation_covariance': [0.0] * 9,
'angular_velocity': {
'x': gyro[0],
'y': gyro[1],
'z': gyro[2],
},
'angular_velocity_covariance': [0.0] * 9,
'linear_acceleration': {
'x': acceleration[0],
'y': acceleration[1],
'z': acceleration[2],
},
'linear_acceleration_covariance': [0.0] * 9,
}))
mag_topic.publish(roslibpy.Message({
'header': {'stamp': stamp, 'frame_id': FRAME_ID},
'magnetic_field': {
'x': magnetic[0] * 1e-6,
'y': magnetic[1] * 1e-6,
'z': magnetic[2] * 1e-6,
},
'magnetic_field_covariance': [0.0] * 9,
}))
publish_status(status_topic, 'BNO085 verbunden auf {0} mit {1:.0f} Hz'.format(ADDRESS, RATE_HZ))
def main():
signal.signal(signal.SIGINT, handle_shutdown)
signal.signal(signal.SIGTERM, handle_shutdown)
print('imu_node.py startet mit {0:.0f} Hz'.format(RATE_HZ))
sys.stdout.flush()
ros_client = None
imu_topic = None
mag_topic = None
status_topic = None
sensor = None
sleep_seconds = 1.0 / max(1.0, RATE_HZ)
while RUNNING:
try:
if ros_client is None or not ros_client.is_connected:
ros_client = connect_ros()
imu_topic, mag_topic, status_topic = create_topics(ros_client)
print('rosbridge verbunden')
sys.stdout.flush()
if sensor is None:
sensor = create_sensor()
publish_status(status_topic, 'BNO085 initialisiert auf {0}'.format(ADDRESS))
print('BNO085 initialisiert')
sys.stdout.flush()
publish_measurements(imu_topic, mag_topic, status_topic, sensor)
time.sleep(sleep_seconds)
except Exception as exc:
message = 'IMU Fehler: {0}'.format(exc)
print(message)
sys.stdout.flush()
if status_topic is not None and ros_client is not None and ros_client.is_connected:
try:
publish_status(status_topic, message)
except Exception:
pass
sensor = None
time.sleep(1.0)
if ros_client is not None:
try:
if imu_topic is not None:
imu_topic.unadvertise()
if mag_topic is not None:
mag_topic.unadvertise()
if status_topic is not None:
status_topic.unadvertise()
ros_client.terminate()
except Exception:
pass
if __name__ == '__main__':
main()