feat(controls): Autonomous/RC mode switch with 500ms blend ramp (Issue #104) #114

Merged
sl-jetson merged 1 commits from sl-controls/issue-104-mode-switch into main 2026-03-02 08:41:25 -05:00
Collaborator

Summary

  • saltybot_mode_switch package: CH6 CRSF toggle + stick-override + SLAM-interlock state machine with 500 ms blend ramp
  • mode_switch_node: subscribes /rc/joy, /saltybot/balance_state, /slam_toolbox/pose_with_covariance_stamped; publishes /saltybot/control_mode (JSON) + /saltybot/led_pattern
  • cmd_vel_mux_node: scales autonomous /cmd_vel_auto by blend_alpha/cmd_vel, giving the smooth handoff

State machine

RC ──(ch6↑+slam_ok)──> RAMP_TO_AUTO ──(done)──> AUTO
│                            │                    │
│             ←──(ch6↓/slam lost)──────────> RAMP_TO_RC ──> RC
└──────── instant RC on: stick>10% | RC link lost ─────────────┘
  • Sticks neutral ≥ 2 s after stick-override → override cleared → re-enters RAMP_TO_AUTO
  • RC link lost (Joy silent > 0.5 s) → instant RC from any state
  • No AUTO without valid SLAM fix

LED patterns

State Pattern
RC solid_yellow
RAMP_TO_AUTO blink_green_slow
AUTO solid_green
AUTO (SLAM lost) blink_orange_fast
RC link lost blink_red_fast

Test plan

  • 72/72 unit tests pass (no ROS2 runtime)
  • All 4 state transitions covered
  • Safety interlocks: stick override instant, RC link lost instant, no AUTO without SLAM
  • Blend alpha monotonically increasing (RAMP_TO_AUTO) / decreasing (RAMP_TO_RC)
  • Nav2 remapping: launch Nav2/follower with cmd_vel_topic:=/cmd_vel_auto

🤖 Generated with Claude Code

## Summary - **`saltybot_mode_switch` package**: CH6 CRSF toggle + stick-override + SLAM-interlock state machine with 500 ms blend ramp - **`mode_switch_node`**: subscribes `/rc/joy`, `/saltybot/balance_state`, `/slam_toolbox/pose_with_covariance_stamped`; publishes `/saltybot/control_mode` (JSON) + `/saltybot/led_pattern` - **`cmd_vel_mux_node`**: scales autonomous `/cmd_vel_auto` by `blend_alpha` → `/cmd_vel`, giving the smooth handoff ## State machine ``` RC ──(ch6↑+slam_ok)──> RAMP_TO_AUTO ──(done)──> AUTO │ │ │ │ ←──(ch6↓/slam lost)──────────> RAMP_TO_RC ──> RC └──────── instant RC on: stick>10% | RC link lost ─────────────┘ ``` - Sticks neutral ≥ 2 s after stick-override → override cleared → re-enters RAMP_TO_AUTO - RC link lost (Joy silent > 0.5 s) → instant RC from any state - No AUTO without valid SLAM fix ## LED patterns | State | Pattern | |---|---| | RC | `solid_yellow` | | RAMP_TO_AUTO | `blink_green_slow` | | AUTO | `solid_green` | | AUTO (SLAM lost) | `blink_orange_fast` | | RC link lost | `blink_red_fast` | ## Test plan - [x] 72/72 unit tests pass (no ROS2 runtime) - [x] All 4 state transitions covered - [x] Safety interlocks: stick override instant, RC link lost instant, no AUTO without SLAM - [x] Blend alpha monotonically increasing (RAMP_TO_AUTO) / decreasing (RAMP_TO_RC) - [x] Nav2 remapping: launch Nav2/follower with `cmd_vel_topic:=/cmd_vel_auto` 🤖 Generated with [Claude Code](https://claude.com/claude-code)
sl-controls added 1 commit 2026-03-02 08:39:24 -05:00
New package: saltybot_mode_switch

mode_logic.py (pure, no ROS2 dep — 72/72 tests pass):
  State machine: RC → RAMP_TO_AUTO → AUTO → RAMP_TO_RC → RC
  • CH6 (axes[5] > 0.5) requests AUTO; CH6 low → RAMP_TO_RC
  • Stick >10% in AUTO/RAMP_TO_AUTO/RAMP_TO_RC → instant RC (no ramp)
  • Sticks neutral ≥ 2 s after override → override cleared → RAMP_TO_AUTO
  • RC link lost (Joy silent > 0.5 s) → instant RC from any state
  • SLAM fix lost → RAMP_TO_RC (graceful exit from AUTO)
  • No AUTO entry without slam_ok AND rc_link_ok
  blend_alpha: 0.0 (RC) → linear ramp over 500 ms → 1.0 (AUTO)
  led_pattern: solid_yellow(RC) | blink_green_slow(ramp) |
               solid_green(AUTO) | blink_orange_fast(slam lost) |
               blink_red_fast(RC link lost)

mode_switch_node.py (ROS2, 20 Hz):
  Sub: /rc/joy (Joy), /saltybot/balance_state (String),
       /slam_toolbox/pose_with_covariance_stamped (PoseWithCovarianceStamped)
  Pub: /saltybot/control_mode (String JSON: mode+blend_alpha+slam_ok+rc_link_ok+override_active)
       /saltybot/led_pattern (String)

cmd_vel_mux_node.py (ROS2, 20 Hz):
  Sub: /cmd_vel_auto (Twist from Nav2/follower), /saltybot/control_mode
  Pub: /cmd_vel (Twist to bridge, scaled by blend_alpha)
  Note: remap Nav2/follower output to /cmd_vel_auto in launch.

Tests: 72/72 pass (no ROS2 runtime required).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
sl-jetson merged commit b1abdccf03 into main 2026-03-02 08:41:25 -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#114
No description provided.