Skip to content

Commit 262972e

Browse files
committed
adding robustness
1 parent 91337d2 commit 262972e

File tree

5 files changed

+262
-6
lines changed

5 files changed

+262
-6
lines changed

glider/config/graph_params.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
accelerometer_covariance: 0.1
2-
gyroscope_covariance: 0.1
1+
accelerometer_covariance: 0.0001
2+
gyroscope_covariance: 0.0001
33
integration_covariance: 0.001
44
bias_covariance: 0.001
55
use_second_order: 0.0
@@ -16,4 +16,4 @@ odom_scale_noise: 50.0
1616
lag_time: 10.0
1717
imu_frame: "enu"
1818
scale_odom: true
19-
correct_imu: false
19+
correct_imu: true

glider/launch/glider-node.launch.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,9 @@ def generate_launch_description():
5353
'use_odom': use_odom}
5454
],
5555
remappings=[
56-
('/gps', '/ublox_gps_node/fix'),
57-
('/imu', '/zed/zed_node/imu/data'),
58-
('/mag', '/zed/zed_node/imu/mag'),
56+
('/gps', '/ublox/fix'),
57+
('/imu', '/VN100T/imu'),
58+
('/mag', '/VN100T/mag'),
5959
('/odom', '/Odometry'),
6060
]
6161
)

glider/scripts/heading.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
#!/usr/bin/env python3
2+
3+
import rclpy
4+
from rclpy.node import Node
5+
from sensor_msgs.msg import Imu
6+
import math
7+
8+
9+
class RpyPrinter(Node):
10+
def __init__(self):
11+
super().__init__('rpy_printer')
12+
13+
# Create subscriber to IMU topic
14+
self.imu_subscriber = self.create_subscription(
15+
Imu,
16+
'/VN100T/imu', # Subscribe directly to IMU topic
17+
self.imu_callback,
18+
10
19+
)
20+
21+
# Create timer for 2Hz printing (0.5 seconds interval)
22+
self.timer = self.create_timer(0.5, self.print_rpy)
23+
24+
# Store latest orientation data
25+
self.latest_orientation = None
26+
self.latest_timestamp = None
27+
28+
self.get_logger().info('Roll Pitch Yaw Printer node started')
29+
self.get_logger().info('Subscribing to: /imu/data')
30+
self.get_logger().info('Printing RPY at 2Hz')
31+
32+
def imu_callback(self, msg):
33+
"""
34+
Callback function for IMU messages.
35+
Stores the latest orientation data.
36+
"""
37+
self.latest_orientation = msg.orientation
38+
self.latest_timestamp = msg.header.stamp
39+
40+
def quaternion_to_euler(self, quat):
41+
"""
42+
Convert quaternion to roll, pitch, yaw (in radians).
43+
44+
Args:
45+
quat: geometry_msgs.msg.Quaternion
46+
47+
Returns:
48+
tuple: (roll, pitch, yaw) in radians
49+
"""
50+
x, y, z, w = quat.x, quat.y, quat.z, quat.w
51+
52+
# Roll (x-axis rotation)
53+
sinr_cosp = 2 * (w * x + y * z)
54+
cosr_cosp = 1 - 2 * (x * x + y * y)
55+
roll = math.atan2(sinr_cosp, cosr_cosp)
56+
57+
# Pitch (y-axis rotation)
58+
sinp = 2 * (w * y - z * x)
59+
if abs(sinp) >= 1:
60+
pitch = math.copysign(math.pi / 2, sinp) # Use 90 degrees if out of range
61+
else:
62+
pitch = math.asin(sinp)
63+
64+
# Yaw (z-axis rotation)
65+
siny_cosp = 2 * (w * z + x * y)
66+
cosy_cosp = 1 - 2 * (y * y + z * z)
67+
yaw = math.atan2(siny_cosp, cosy_cosp)
68+
69+
return roll, pitch, yaw
70+
71+
def print_rpy(self):
72+
"""
73+
Timer callback function that prints roll, pitch, yaw at 2Hz.
74+
"""
75+
if self.latest_orientation is None:
76+
self.get_logger().warn('No IMU data received yet')
77+
return
78+
79+
# Convert quaternion to Euler angles
80+
roll, pitch, yaw = self.quaternion_to_euler(self.latest_orientation)
81+
82+
# Convert to degrees for easier reading
83+
roll_deg = math.degrees(roll)
84+
pitch_deg = math.degrees(pitch)
85+
yaw_deg = math.degrees(yaw)
86+
87+
# Print with formatting
88+
self.get_logger().info(
89+
f'Roll: {roll_deg:8.2f}° Pitch: {pitch_deg:8.2f}° Yaw: {yaw_deg:8.2f}°'
90+
)
91+
92+
93+
def main(args=None):
94+
rclpy.init(args=args)
95+
96+
node = RpyPrinter()
97+
98+
try:
99+
rclpy.spin(node)
100+
except KeyboardInterrupt:
101+
node.get_logger().info('Shutting down Roll Pitch Yaw Printer node')
102+
finally:
103+
node.destroy_node()
104+
rclpy.shutdown()
105+
106+
107+
if __name__ == '__main__':
108+
main()

glider/scripts/heading_mag.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#!/usr/bin/env python3
2+
3+
import rclpy
4+
from rclpy.node import Node
5+
from sensor_msgs.msg import MagneticField
6+
import math
7+
8+
9+
class MagnetometerHeadingNode(Node):
10+
def __init__(self):
11+
super().__init__('magnetometer_heading_node')
12+
13+
# Create subscriber for magnetometer data
14+
self.subscription = self.create_subscription(
15+
MagneticField,
16+
'/VN100T/mag', # Change this to your magnetometer topic name
17+
self.magnetometer_callback,
18+
10
19+
)
20+
21+
self.get_logger().info('Magnetometer heading node started')
22+
self.get_logger().info('Subscribing to topic: /imu/mag')
23+
24+
def magnetometer_callback(self, msg):
25+
"""
26+
Callback function to process magnetometer data and calculate heading
27+
"""
28+
# Extract magnetic field components
29+
mag_x = msg.magnetic_field.x
30+
mag_y = msg.magnetic_field.y
31+
mag_z = msg.magnetic_field.z
32+
33+
# Calculate heading using atan2 (yaw angle)
34+
# In ENU frame: heading = atan2(mag_y, mag_x)
35+
# This gives heading relative to magnetic north
36+
heading_rad = math.atan2(mag_y, mag_x)
37+
38+
# Convert to degrees
39+
heading_deg = math.degrees(heading_rad)
40+
41+
# Normalize to 0-360 degrees
42+
if heading_deg < 0:
43+
heading_deg += 360.0
44+
45+
# Calculate magnetic field magnitude
46+
magnitude = math.sqrt(mag_x**2 + mag_y**2 + mag_z**2)
47+
48+
# Print the results
49+
self.get_logger().info(
50+
f'Magnetic Field - X: {mag_x:.3f}, Y: {mag_y:.3f}, Z: {mag_z:.3f} µT'
51+
)
52+
self.get_logger().info(
53+
f'Heading: {heading_deg:.2f}° | Magnitude: {magnitude:.3f} µT'
54+
)
55+
self.get_logger().info('---')
56+
57+
58+
def main(args=None):
59+
rclpy.init(args=args)
60+
61+
try:
62+
magnetometer_node = MagnetometerHeadingNode()
63+
rclpy.spin(magnetometer_node)
64+
except KeyboardInterrupt:
65+
pass
66+
finally:
67+
# Clean shutdown
68+
if 'magnetometer_node' in locals():
69+
magnetometer_node.destroy_node()
70+
rclpy.shutdown()
71+
72+
73+
if __name__ == '__main__':
74+
main()

glider/scripts/imu_pose.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#!/usr/bin/env python3
2+
3+
import rclpy
4+
from rclpy.node import Node
5+
from sensor_msgs.msg import Imu
6+
from geometry_msgs.msg import PoseStamped
7+
8+
9+
class ImuOrientationPublisher(Node):
10+
def __init__(self):
11+
super().__init__('imu_orientation_publisher')
12+
13+
# Create subscriber to IMU topic
14+
self.imu_subscriber = self.create_subscription(
15+
Imu,
16+
'/vectornav/imu', # Change this to your IMU topic name
17+
self.imu_callback,
18+
10
19+
)
20+
21+
# Create publisher for pose with orientation only
22+
self.pose_publisher = self.create_publisher(
23+
PoseStamped,
24+
'/orientation_pose',
25+
10
26+
)
27+
28+
self.get_logger().info('IMU Orientation Publisher node started')
29+
self.get_logger().info('Subscribing to: /imu/data')
30+
self.get_logger().info('Publishing to: /orientation_pose')
31+
32+
def imu_callback(self, msg):
33+
"""
34+
Callback function for IMU messages.
35+
Extracts orientation and publishes as PoseStamped with zero position.
36+
"""
37+
# Create PoseStamped message
38+
pose_msg = PoseStamped()
39+
40+
# Set header information
41+
pose_msg.header.stamp = self.get_clock().now().to_msg()
42+
pose_msg.header.frame_id = msg.header.frame_id
43+
44+
# Set position to (0, 0, 0)
45+
pose_msg.pose.position.x = 0.0
46+
pose_msg.pose.position.y = 0.0
47+
pose_msg.pose.position.z = 0.0
48+
49+
# Copy orientation from IMU message
50+
pose_msg.pose.orientation.x = msg.orientation.x
51+
pose_msg.pose.orientation.y = msg.orientation.y
52+
pose_msg.pose.orientation.z = msg.orientation.z
53+
pose_msg.pose.orientation.w = msg.orientation.w
54+
55+
# Publish the pose
56+
self.pose_publisher.publish(pose_msg)
57+
58+
59+
def main(args=None):
60+
rclpy.init(args=args)
61+
62+
node = ImuOrientationPublisher()
63+
64+
try:
65+
rclpy.spin(node)
66+
except KeyboardInterrupt:
67+
node.get_logger().info('Shutting down IMU Orientation Publisher node')
68+
finally:
69+
node.destroy_node()
70+
rclpy.shutdown()
71+
72+
73+
if __name__ == '__main__':
74+
main()

0 commit comments

Comments
 (0)