New package saltybot_pose_fusion — EKF fusing UWB+IMU absolute pose,
visual odometry velocity, and raw IMU into a single authoritative pose.
pose_fusion_ekf.py (pure Python, no ROS2 deps):
PoseFusionEKF — state [x, y, θ, vx, vy, ω], 6-state EKF.
- predict_imu(ax_body, ay_body, omega, dt): body-frame IMU predict step
with Jacobian F, bias-compensated accel, process noise Q.
- update_uwb_position(x, y, sigma_m): absolute position measurement
(H=[1,0,0,0,0,0; 0,1,0,0,0,0]) from UWB+IMU fused stream.
- update_uwb_heading(heading_rad, sigma_rad): heading measurement.
- update_vo_velocity(vx_body, omega, ...): VO velocity measurement —
body-frame vx rotated to world via cos/sin(θ), updates [vx,vy,ω] state.
- Joseph-form covariance update for numerical stability.
- Dual dropout clocks: uwb_dropout_s, vo_dropout_s (reset on each update).
- Velocity damping when uwb_dropout_s > 2s.
- Sensor weight parameters: sigma_uwb_pos_m, sigma_uwb_head_rad,
sigma_vo_vel_m_s, sigma_vo_omega_r_s, sigma_imu_accel/gyro,
sigma_vel_drift, dropout_vel_damp.
pose_fusion_node.py (ROS2 node 'pose_fusion'):
- Subscribes: /imu/data (Imu, 200Hz → predict), /saltybot/pose/fused_cov
(PoseWithCovarianceStamped, 10Hz → position+heading update, σ extracted
from message covariance when use_uwb_covariance=true), /saltybot/visual_odom
(Odometry, 30Hz → velocity update, σ from twist covariance).
- Publishes: /saltybot/pose/authoritative (PoseWithCovarianceStamped),
/saltybot/pose/status (String JSON, 10Hz).
- TF2: map→base_link broadcast at IMU rate.
- Suppresses output when uwb_dropout_s > uwb_dropout_max_s (10s).
- Warns (throttled) on UWB/VO dropout.
config/pose_fusion_params.yaml: sensor weights + dropout thresholds.
launch/pose_fusion.launch.py: single node launch with params_file arg.
test/test_pose_fusion_ekf.py: 13 unit tests — init, predict, UWB/VO
updates, dropout reset, covariance shape/convergence, sigma override.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
31 lines
931 B
Python
31 lines
931 B
Python
import os
|
|
from glob import glob
|
|
from setuptools import setup
|
|
|
|
package_name = 'saltybot_pose_fusion'
|
|
|
|
setup(
|
|
name=package_name,
|
|
version='0.1.0',
|
|
packages=[package_name],
|
|
data_files=[
|
|
('share/ament_index/resource_index/packages',
|
|
['resource/' + package_name]),
|
|
('share/' + package_name, ['package.xml']),
|
|
(os.path.join('share', package_name, 'launch'), glob('launch/*.py')),
|
|
(os.path.join('share', package_name, 'config'), glob('config/*.yaml')),
|
|
],
|
|
install_requires=['setuptools'],
|
|
zip_safe=True,
|
|
maintainer='sl-perception',
|
|
maintainer_email='sl-perception@saltylab.local',
|
|
description='Multi-sensor EKF pose fusion — UWB + visual odom + IMU (Issue #595)',
|
|
license='MIT',
|
|
tests_require=['pytest'],
|
|
entry_points={
|
|
'console_scripts': [
|
|
'pose_fusion = saltybot_pose_fusion.pose_fusion_node:main',
|
|
],
|
|
},
|
|
)
|