feat: Multi-sensor pose fusion (Issue #595) #606
Loading…
x
Reference in New Issue
Block a user
No description provided.
Delete Branch "sl-perception/issue-595-pose-fusion"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
New package
saltybot_pose_fusion— EKF fusing three sensor streams into a single authoritativePoseWithCovarianceStampedon/saltybot/pose/authoritativewith TF2map→base_link.Sensor fusion architecture
/imu/data/saltybot/pose/fused_cov/saltybot/visual_odompose_fusion_ekf.py(pure Python, no ROS2)predict_imu: body-frame accel+gyro, Jacobian F, process noise Q, running accel-bias estimatorupdate_uwb_position / update_uwb_heading: linear H, σ from message covariance or parameter overrideupdate_vo_velocity: body-frame vx rotated to world via cos/sin(θ), updates [vx, vy, ω] statepose_fusion_node.py/saltybot/pose/statusat 10 Hz (x, y, heading_deg, pos_uncertainty_m, dropout durations, healthy flag)PoseWithCovarianceStamped.covariance(configurable)Tests
13 unit tests in
test/test_pose_fusion_ekf.pycovering init, predict, UWB/VO updates, dropout reset, covariance shape/convergence, and sigma override correctness.Closes #595
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>