feat: RC/Autonomous mode switch (Phase 2) #33

Merged
seb merged 1 commits from sl-controls/mode-switch into main 2026-02-28 21:43:01 -05:00
Collaborator

Summary

  • Mode enum: RC_MANUAL, RC_ASSISTED, AUTONOMOUS selected by RC CH6 (3-pos switch)
  • Smooth 500ms blend ramp between modes — single blend float (0=RC, 1=auto) drives all interpolation
  • CH5 always has kill authority regardless of mode
  • Telemetry: "md" field added to JSON stream
  • Safe on USB-only build: CRSF stub keeps rc_alive=false → RC inputs zero, CH5 kill never fires

Mode Behaviour

Mode CH6 Steer source Speed source
RC_MANUAL (0) ≤600 RC CH4 PID + RC CH3 bias
RC_ASSISTED (1) 601–1199 50/50 blend RC+auto PID + blended bias
AUTONOMOUS (2) ≥1200 Jetson via mode_manager_set_auto_cmd() PID + auto bias

Key Design Decisions

  • Balance PID never bypassed — speed channel always includes PID output; RC/auto only adds a bias (±300 counts max) to avoid destabilising
  • Steer-only ramped at MOTOR_STEER_RAMP_RATE inside motor_driver; mode blend is a separate higher-level ramp
  • Deadband ±30 CRSF counts (~2%) on CH3/CH4 to mask stick center noise
  • Jetson integration: mode_manager_set_auto_cmd() called every loop when jetson_cmd_is_active(); steer is blended per mode

Files

  • include/mode_manager.h — API + robot_mode_t enum
  • src/mode_manager.c — full implementation
  • include/config.h — CRSF CH indices, deadband, speed-bias max, blend timing
  • src/main.c — init, mode update, CH5 kill, ESC blended steer/speed, "md" telemetry

Test plan

  • Build passes (no warnings)
  • USB-only: "md":0 in telemetry, no steer applied
  • CH6 low → "md":0; CH6 mid → "md":1; CH6 high → "md":2
  • Mode transition: blend advances smoothly, no step change at ESC
  • CH5 disarm from any mode → immediate disarm
  • AUTONOMOUS mode with mode_manager_set_auto_cmd(steer=500): steer ≈500
  • RC_ASSISTED: steer is mean of RC and auto values
  • RC CH3 pushed forward in MANUAL: bal.motor_cmd + bias sent to ESC

🤖 Generated with Claude Code

## Summary - Mode enum: `RC_MANUAL`, `RC_ASSISTED`, `AUTONOMOUS` selected by RC CH6 (3-pos switch) - Smooth 500ms blend ramp between modes — single `blend` float (0=RC, 1=auto) drives all interpolation - CH5 always has kill authority regardless of mode - Telemetry: `"md"` field added to JSON stream - Safe on USB-only build: CRSF stub keeps `rc_alive=false` → RC inputs zero, CH5 kill never fires ## Mode Behaviour | Mode | CH6 | Steer source | Speed source | |---|---|---|---| | `RC_MANUAL` (0) | ≤600 | RC CH4 | PID + RC CH3 bias | | `RC_ASSISTED` (1) | 601–1199 | 50/50 blend RC+auto | PID + blended bias | | `AUTONOMOUS` (2) | ≥1200 | Jetson via `mode_manager_set_auto_cmd()` | PID + auto bias | ## Key Design Decisions - **Balance PID never bypassed** — speed channel always includes PID output; RC/auto only adds a bias (±300 counts max) to avoid destabilising - **Steer-only ramped** at `MOTOR_STEER_RAMP_RATE` inside `motor_driver`; mode blend is a separate higher-level ramp - **Deadband** ±30 CRSF counts (~2%) on CH3/CH4 to mask stick center noise - **Jetson integration**: `mode_manager_set_auto_cmd()` called every loop when `jetson_cmd_is_active()`; steer is blended per mode ## Files - `include/mode_manager.h` — API + `robot_mode_t` enum - `src/mode_manager.c` — full implementation - `include/config.h` — CRSF CH indices, deadband, speed-bias max, blend timing - `src/main.c` — init, mode update, CH5 kill, ESC blended steer/speed, `"md"` telemetry ## Test plan - [ ] Build passes (no warnings) - [ ] USB-only: `"md":0` in telemetry, no steer applied - [ ] CH6 low → `"md":0`; CH6 mid → `"md":1`; CH6 high → `"md":2` - [ ] Mode transition: blend advances smoothly, no step change at ESC - [ ] CH5 disarm from any mode → immediate disarm - [ ] AUTONOMOUS mode with `mode_manager_set_auto_cmd(steer=500)`: steer ≈500 - [ ] RC_ASSISTED: steer is mean of RC and auto values - [ ] RC CH3 pushed forward in MANUAL: `bal.motor_cmd + bias` sent to ESC 🤖 Generated with [Claude Code](https://claude.com/claude-code)
sl-controls added 1 commit 2026-02-28 21:06:52 -05:00
Adds mode_manager.c/h: three operating modes selected by RC CH6 (3-pos
switch), smoothly interpolated over ~500ms to prevent jerky transitions.

Modes:
  RC_MANUAL   (blend=0.0) — RC CH4 steer + CH3 speed bias; balance PID active
  RC_ASSISTED (blend=0.5) — 50/50 blend of RC and Jetson autonomous commands
  AUTONOMOUS  (blend=1.0) — Jetson steer only; RC CH5 still kills motors

Key design:
- Single `blend` float (0=RC, 1=auto) drives all lerp; MANUAL→AUTO takes
  500ms, adjacent steps take ~250ms
- CH6 thresholds: <=600=MANUAL, >=1200=AUTONOMOUS, else ASSISTED
- CH4/CH3 read with ±30-count deadband around CRSF center (992)
- RC speed bias (CH3, ±300 counts) added to bal.motor_cmd AFTER PID
- RC CH5 kill: if rc_alive && !crsf_state.armed → disarm, regardless of mode
- Jetson steer fed via mode_manager_set_auto_cmd() → blended in get_steer()
- Telemetry: new "md" field (0/1/2) in USB JSON stream
- mode_manager_set_auto_cmd() API ready for Jetson serial bridge integration

config.h: CRSF channel indices, deadband, speed-bias max, blend timing.
Safe on USB-only build: CRSF stub keeps last_rx_ms=0 → rc_alive=false
→ RC inputs = 0, mode stays RC_MANUAL, CH5 kill never fires.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
seb approved these changes 2026-02-28 21:42:57 -05:00
seb left a comment
Owner

Flash-tested, builds and streams clean.

Flash-tested, builds and streams clean.
seb merged commit 14ac85bf57 into main 2026-02-28 21:43:01 -05:00
Sign in to join this conversation.
No Reviewers
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

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