feat(bringup): visual odometry drift detector (Issue #260) #265

Merged
sl-jetson merged 1 commits from sl-perception/issue-260-vo-drift into main 2026-03-02 14:13:35 -05:00
Collaborator

Summary

  • Pure-Python _vo_drift.py: OdomSample(t,x,y), OdomBuffer (rolling deque with max_age_s eviction + window() query), compute_drift() — compares cumulative path lengths of VO vs wheel odom over the last window_s seconds; drift_m = |vo_path − wheel_path|; uses path length (sum of inter-sample steps) rather than straight-line displacement so circular motion is handled correctly
  • vo_drift_node.py: subscribes /camera/odom + /odom (BEST_EFFORT, remappable to /saltybot/visual_odom + /saltybot/rover_odom); 2 Hz timer publishes Bool on /saltybot/vo_drift_detected and Float32 on /saltybot/vo_drift_magnitude; throttled warn log when drifting
  • Default parameters: drift_threshold_m=0.5, window_s=10.0, publish_hz=2.0

Test plan

  • test/test_vo_drift.py — 27/27 pure-Python tests pass (no ROS2 required)
  • Covers: OdomBuffer push/eviction/window, path length (straight, diagonal, L-shape), drift detection at/above/below threshold, window boundary, samples outside window ignored
  • Deploy on Jetson: remap to /saltybot/visual_odom + /saltybot/rover_odom; slide robot sideways to trigger VO slip; confirm vo_drift_detected=True

🤖 Generated with Claude Code

## Summary - Pure-Python `_vo_drift.py`: `OdomSample(t,x,y)`, `OdomBuffer` (rolling deque with `max_age_s` eviction + `window()` query), `compute_drift()` — compares cumulative path lengths of VO vs wheel odom over the last `window_s` seconds; `drift_m = |vo_path − wheel_path|`; uses path length (sum of inter-sample steps) rather than straight-line displacement so circular motion is handled correctly - `vo_drift_node.py`: subscribes `/camera/odom` + `/odom` (BEST_EFFORT, remappable to `/saltybot/visual_odom` + `/saltybot/rover_odom`); 2 Hz timer publishes `Bool` on `/saltybot/vo_drift_detected` and `Float32` on `/saltybot/vo_drift_magnitude`; throttled warn log when drifting - Default parameters: `drift_threshold_m=0.5`, `window_s=10.0`, `publish_hz=2.0` ## Test plan - [x] `test/test_vo_drift.py` — 27/27 pure-Python tests pass (no ROS2 required) - Covers: OdomBuffer push/eviction/window, path length (straight, diagonal, L-shape), drift detection at/above/below threshold, window boundary, samples outside window ignored - [ ] Deploy on Jetson: remap to `/saltybot/visual_odom` + `/saltybot/rover_odom`; slide robot sideways to trigger VO slip; confirm `vo_drift_detected=True` 🤖 Generated with [Claude Code](https://claude.com/claude-code)
sl-perception added 1 commit 2026-03-02 13:26:28 -05:00
Adds sliding-window drift detector that compares cumulative path lengths
of visual odom and wheel odom over a configurable window (default 10 s).
Drift = |vo_path − wheel_path|; flagged when ≥ 0.5 m (configurable).
OdomBuffer handles per-source rolling storage with automatic age eviction.
Publishes Bool on /saltybot/vo_drift_detected and Float32 on
/saltybot/vo_drift_magnitude at 2 Hz.  27/27 pure-Python tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
sl-jetson merged commit 94a6f0787e into main 2026-03-02 14:13:35 -05:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: seb/saltylab-firmware#265
No description provided.