570 Commits

Author SHA1 Message Date
sl-android
d5246fe3a8 chore: Guard ESC debug output behind compile flag (Issue #521)
- esc_hoverboard.c: huart2 static in production; non-static only under
  #ifdef DEBUG_MOTOR_TEST (needed by R command in jetson_uart.c)
- esc_hoverboard.c: UART5 diagnostic in hoverboard_backend_init() and
  per-packet printf in hoverboard_backend_send() guarded by same flag
- esc_hoverboard.c: #include <stdio.h> also guarded (not needed in production)
- jetson_uart.c: R (baud sweep) and X (GPIO test) commands guarded by
  #ifdef DEBUG_MOTOR_TEST — not compiled into production firmware

Production build: no debug output, static huart2, no R/X commands.
Debug build: define DEBUG_MOTOR_TEST to re-enable all diagnostics.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 23:07:36 -05:00
Salty Bead
3bb4b71cea feat: motor bench test working — UART5 ESC + USART6 Jetson + W command
- Fix UART5 ESC: PC12=TX, PD2=RX @ 115200 baud (was USART2/38400)
- Add jetson_uart.c: USART6 command interface (A/D/E/Z/H/C/W/R/X/? commands)
- Add W command: persistent direct motor test (sets globals, main loop sends at 50Hz)
- Fix power_mgmt: keep UART5 clock active, PC7 EXTI wake source
- Fix main loop: direct_test_speed/steer override disarmed zero-send
- Add boot banner on USART6

Tested: Orin -> FC USART6 -> FC UART5 -> EFeru ESC -> both motors spin
2026-03-06 22:35:24 -05:00
f14ce5c3ba Merge remote-tracking branch 'origin/sl-perception/issue-469-terrain-classification' 2026-03-06 17:37:27 -05:00
2e2ed2d0a7 Merge remote-tracking branch 'origin/sl-controls/issue-506-launch-profiles' 2026-03-06 17:37:27 -05:00
5b9e9dd412 Merge pull request 'feat: Headscale VPN auto-connect (Issue #502)' (#517) from sl-jetson/issue-502-headscale-vpn into main 2026-03-06 17:37:07 -05:00
706a67c0b7 Merge pull request 'feat: Charging dock hardware design (Issue #505)' (#516) from sl-webui/issue-504-integration-tests into main 2026-03-06 17:37:06 -05:00
8d58d5e34c feat: Terrain classification for speed adaptation (Issue #469)
Implement multi-sensor terrain classification using RealSense D435i depth and RPLIDAR A1M8:

- saltybot_terrain_classification: New ROS2 package for terrain classification
- TerrainClassifier: Rule-based classifier matching depth variance + reflectance to terrain type
  (smooth/carpet/grass/gravel) with hysteresis + confidence scoring
- DepthExtractor: Extracts roughness from depth discontinuities and surface gradients
- LidarExtractor: Extracts reflectance from RPLIDAR scan intensities
- terrain_classification_node: 10Hz node fusing both sensors, publishes:
  - /saltybot/terrain_type (JSON with type, confidence, speed_scale)
  - /saltybot/terrain_type_string (human-readable type)
  - /saltybot/terrain_speed_scale (0.0-1.0 speed multiplier for smooth/carpet/grass/gravel)

Speed scales: smooth=1.0, carpet=0.9, grass=0.75, gravel=0.6

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-06 16:43:21 -05:00
d3eca7bebc feat: Integration test suite (Issue #504)
Add comprehensive integration testing for complete ROS2 system stack:

Integration Tests (test_integration_full_stack.py):
  - Verifies all ROS2 nodes launch successfully
  - Checks critical topics are published (sensors, nav, control)
  - Validates system component health and stability
  - Tests launch file validity and configuration
  - Covers indoor/outdoor/follow modes

Launch Testing (test_launch_full_stack.py):
  - Validates launch file syntax and configuration
  - Verifies all required packages are installed
  - Checks launch sequence timing
  - Validates conditional logic for optional components

Test Coverage:
  ✓ SLAM/RTAB-Map (indoor mode)
  ✓ Nav2 navigation stack
  ✓ Perception (YOLOv8n person detection)
  ✓ Control (cmd_vel bridge, STM32 bridge)
  ✓ Audio pipeline and monitoring
  ✓ Sensors (LIDAR, RealSense, UWB, CSI cameras)
  ✓ Battery and temperature monitoring
  ✓ Autonomous docking behavior
  ✓ TF2 tree and odometry

Usage:
  pytest test/test_integration_full_stack.py -v
  pytest test/test_launch_full_stack.py -v

Documentation:
  See test/README_INTEGRATION_TESTS.md for detailed information.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-06 16:42:38 -05:00
8d67d06857 feat: Integration test suite (Issue #504)
Add comprehensive integration testing for complete ROS2 system stack:

Integration Tests (test_integration_full_stack.py):
  - Verifies all ROS2 nodes launch successfully
  - Checks critical topics are published (sensors, nav, control)
  - Validates system component health and stability
  - Tests launch file validity and configuration
  - Covers indoor/outdoor/follow modes

Launch Testing (test_launch_full_stack.py):
  - Validates launch file syntax and configuration
  - Verifies all required packages are installed
  - Checks launch sequence timing
  - Validates conditional logic for optional components

Test Coverage:
  ✓ SLAM/RTAB-Map (indoor mode)
  ✓ Nav2 navigation stack
  ✓ Perception (YOLOv8n person detection)
  ✓ Control (cmd_vel bridge, STM32 bridge)
  ✓ Audio pipeline and monitoring
  ✓ Sensors (LIDAR, RealSense, UWB, CSI cameras)
  ✓ Battery and temperature monitoring
  ✓ Autonomous docking behavior
  ✓ TF2 tree and odometry

Usage:
  pytest test/test_integration_full_stack.py -v
  pytest test/test_launch_full_stack.py -v

Documentation:
  See test/README_INTEGRATION_TESTS.md for detailed information.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-06 16:42:31 -05:00
e5329391bc feat: Add parameter profile YAML files for Nav2 (Issue #506)
- profile_indoor.yaml: Conservative settings (0.4 m/s, 0.35m inflation)
- profile_outdoor.yaml: Moderate settings (0.8 m/s, 0.3m inflation)
- profile_demo.yaml: Agile settings (0.6 m/s, 0.32m inflation)

Each profile customizes velocity limits, costmap inflation, and obstacle detection.
2026-03-06 16:42:31 -05:00
5d17b6c501 feat: Issue #506 — Update nav2.launch.py for profile support
Add profile argument to nav2.launch.py to accept launch profile parameter
and log profile selection for debugging/monitoring.

Changes:
- Add profile_arg declaration with choices (indoor/outdoor/demo)
- Add profile substitution and log output
- Update docstring with profile documentation

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-06 16:42:31 -05:00
b5acb32ee6 feat: Issue #506 — Update full_stack.launch.py for profile support
Add profile argument and documentation to full_stack.launch.py for
Issue #506 launch parameter profiles. Updated to support:
- profile:=indoor (conservative)
- profile:=outdoor (moderate)
- profile:=demo (agile with tricks/social features)

Changes:
- Add profile_arg declaration
- Add profile substitution handle
- Update docstring with profile examples
- Ready for profile-based Nav2 parameter overrides

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-06 16:42:31 -05:00
bbfcd2a9d1 feat: Issue #506 — Launch parameter profiles (indoor/outdoor/demo)
Implement profile-based parameter overrides for Nav2, costmap, and behavior
server configurations. Profiles predefine parameter sets for different
deployment scenarios.

New files:
- config/profiles/indoor.yaml: Conservative (0.2 m/s, tight geofence, no GPS)
- config/profiles/outdoor.yaml: Moderate (0.5 m/s, wide geofence, GPS-enabled)
- config/profiles/demo.yaml: Agile (0.3 m/s, tricks/social features enabled)
- saltybot_bringup/profile_loader.py: YAML loader and parameter merger utility

Supports: ros2 launch saltybot_bringup full_stack.launch.py profile:=<profile>

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-06 16:42:31 -05:00
5add2cab51 Merge remote-tracking branch 'origin/sl-mechanical/issue-505-charging-dock'
# Conflicts:
#	phone/INSTALL_MOTOR_TEST.md
#	phone/MOTOR_TEST_JOYSTICK.md
#	phone/motor_test_joystick.py
2026-03-06 14:59:59 -05:00
892d0a2089 Merge remote-tracking branch 'origin/sl-android/issue-513-phone-joystick'
# Conflicts:
#	phone/MOTOR_TEST_JOYSTICK.md
#	phone/motor_test_joystick.py
2026-03-06 14:00:21 -05:00
678fd221f5 Merge pull request 'feat: Remove ELRS arm requirement (Issue #512)' (#514) from sl-firmware/issue-512-autonomous-arming into main 2026-03-06 12:52:05 -05:00
sl-android
f49e84b8bb feat: Phone-based motor test joystick app (Issue #513)
Implements terminal-based curses UI for interactive bench testing of SaltyBot motors via Termux.

Features:
- Interactive keyboard-based joystick (W/A/S/D or arrow keys)
- Conservative velocity defaults: 0.1 m/s linear, 0.3 rad/s angular
- Real-time velocity feedback with bar graphs
- Spacebar e-stop (instantly zeros velocity)
- 500ms timeout safety (sends zero velocity if idle)
- Dual backend: ROS2 (/cmd_vel) or WebSocket
- Graceful fallback if ROS2 unavailable

Safety Features:
- Conservative defaults (0.1/0.3 m/s)
- E-stop button (spacebar)
- 500ms timeout (sends zero if idle)
- Input clamping and exponential decay
- Status/warning displays

Files:
- motor_test_joystick.py: Main application
- MOTOR_TEST_JOYSTICK.md: User documentation
- INSTALL_MOTOR_TEST.md: Installation guide

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-06 11:47:15 -05:00
48e9af60a9 feat: Remove ELRS arm requirement for autonomous operation (Issue #512)
Enable Jetson autonomous arming while keeping RC as optional override.

Key Changes:
=============

1. RC Kill Switch (CH5 OFF) → Emergency Stop (not disarm)
   - Motors cutoff immediately on kill switch
   - Robot remains armed (allows re-arm without re-initializing)
   - Maintains kill switch safety for emergency situations

2. RC Disarm Only on Explicit CH5 Falling Edge (while RC alive)
   - RC disconnect doesn't disarm Jetson-controlled missions
   - RC failsafe timer (500ms) still handles signal loss

3. Jetson Autonomous Arming (via CDC 'A' command)
   - Works independently of RC state
   - Requires: calibrated IMU, robot level (±10°), no estop active
   - Uses same 500ms arm-hold safety as RC

4. All Safety Preserved
   - Arming hold timer: 500ms
   - Tilt limit: ±10° level
   - IMU calibration required
   - Remote E-stop override
   - RC failsafe: 500ms signal loss = disarm
   - Jetson timeout: 500ms heartbeat = zero motors

Command Protocol (CDC):
   A = arm (Jetson)
   D = disarm (Jetson)
   E/Z = estop / clear estop
   H = heartbeat (keep-alive)
   C<spd>,<str> = drive command

Behavior Matrix:
   RC disconnected      → Jetson-armed stays armed, normal operation
   RC connected + armed → Both Jetson and RC can arm, blended control
   RC kill switch (CH5) → Emergency stop + can re-arm via Jetson 'A'
   RC signal lost       → Disarm after 500ms (failsafe)

See AUTONOMOUS_ARMING.md for complete protocol and testing checklist.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-06 11:46:03 -05:00
e4116dffc0 feat: Remove ELRS arm requirement for autonomous operation (Issue #512)
Enable Jetson autonomous arming while keeping RC as optional override.

Changes:
- RC kill switch (CH5 OFF) now triggers emergency stop instead of disarm
  → Allows Jetson-armed robots to remain armed when RC disconnects
  → Maintains kill switch safety for emergency situations

- RC disarm only triggers on explicit CH5 falling edge (RC still alive)
  → RC disconnect doesn't disarm Jetson-controlled missions
  → RC failsafe timer (500ms) handles signal loss separately

- Jetson arming via CDC 'A' command works independently of RC state
  → Robots can operate fully autonomous without RC transmitter
  → Heartbeat timeout (500ms) prevents runaway if Jetson crashes

Safety maintained:
- Arming hold timer: 500ms (prevents accidental arm)
- Tilt limit: ±10° level required
- IMU calibration: Required before any arm attempt
- Remote E-stop: Blocks all arming
- RC failsafe: 500ms signal loss = disarm
- Jetson timeout: 500ms heartbeat = zero motors

Command protocol (unchanged):
- Jetson: A=arm, D=disarm, E=estop, Z=clear estop
- RC: CH5 switch (optional override)
- Heartbeat: H command every ≤500ms
- Drive: C<speed>,<steer> every ≤200ms

See AUTONOMOUS_ARMING.md for complete protocol and testing checklist.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-06 11:45:12 -05:00
b0c2b5564d feat: Add Issue #505 CAD - 24V Charging Dock OpenSCAD Models
CAD implementation files for Issue #505 (24V charging dock upgrade):

- charging_dock_505.scad: Main dock assembly
  * Base plate: 340×320×12 mm (enlarged for 240W PSU)
  * Back wall: 250×85×10 mm (pogo pin housing, LED bezel recess)
  * V-guide rails: 100mm deep, self-centering funnel (print 2×)
  * ArUco marker frame: ID 42 (DICT_4X4_250), 15cm mast
  * PSU bracket: Sized for Mean Well IRM-240-24 (210×108×56 mm)
  * LED bezel: 4× status indicators (SEARCHING/ALIGNED/CHARGING/FULL)

- charging_dock_receiver_505.scad: Robot-side receiver variants
  * Lab receiver: Stem collar mount (SaltyLab)
  * Rover receiver: Deck flange mount (SaltyRover)
  * Tank receiver: Skid plate mount + extended nose (SaltyTank)
  * Common contact geometry: 20mm CL-to-CL brass pads, V-nose guide
  * Wire bore: 3mm (supports 12 AWG charging wires)

Key changes from Issue #159 (5V):
- PSU dimensions: 63×45×28 mm → 210×108×56 mm
- Base/bracket enlarged accordingly
- ArUco ID: 0 → 42
- Contact geometry unchanged (compatible with Issue #159 receivers)
- Pogo pins, V-guides, LED circuit identical

Files ready for:
- STL export via OpenSCAD render commands
- 3D printing (PETG recommended)
- Assembly integration with docking node (#489)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-06 11:44:28 -05:00
1f4929b68c Merge remote-tracking branch 'origin/sl-jetson/issue-477-urdf'
# Conflicts:
#	jetson/config/RECOVERY_BEHAVIORS.md
2026-03-06 11:43:31 -05:00
d97fa5fab0 Merge remote-tracking branch 'origin/sl-webui/issue-482-behavior-tree'
# Conflicts:
#	jetson/ros2_ws/src/saltybot_bringup/behavior_trees/autonomous_coordinator.xml
#	jetson/ros2_ws/src/saltybot_bringup/launch/autonomous_mode.launch.py
2026-03-06 11:43:26 -05:00
a3d3ea1471 Merge remote-tracking branch 'origin/sl-perception/issue-478-costmaps'
# Conflicts:
#	jetson/ros2_ws/src/saltybot_bringup/config/nav2_params.yaml
2026-03-06 11:43:11 -05:00
e1248f92b9 Merge pull request 'feat: Face display animations on STM32 LCD (Issue #507)' (#508) from sl-android/issue-507-face-animations into main 2026-03-06 10:57:28 -05:00
6f3dd46285 feat: Add Issue #503 - Audio pipeline with Jabra SPEAK 810
Implement full audio pipeline with:
- Jabra SPEAK 810 USB audio I/O (mic + speaker)
- openwakeword 'Hey Salty' wake word detection
- whisper.cpp GPU-accelerated STT (small/base/medium/large models)
- piper TTS synthesis and playback
- Audio state machine: listening → processing → speaking
- MQTT status and state reporting
- Real-time latency metrics tracking

ROS2 Topics Published:
- /saltybot/speech/transcribed_text: STT output for voice router
- /saltybot/audio/state: Current audio state
- /saltybot/audio/status: JSON metrics with latencies

MQTT Topics:
- saltybot/audio/state: Current state (listening/processing/speaking)
- saltybot/audio/status: Complete status JSON

Configuration parameters in yaml:
- device_name: Jabra device pattern
- wake_word_threshold: 0.5 (tunable)
- whisper_model: small/base/medium/large
- mqtt_enabled: true/false with broker config

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-06 10:30:58 -05:00
5cea0812d5 feat: Add Issue #505 - 24V Charging Dock Hardware Design
- Design specification: 24V DC power delivery (upgraded from 5V Issue #159)
- ArUco marker ID 42 (15cm frame) for precision alignment
- Spring-loaded contact pads with V-channel guide rails
- Comprehensive BOM for 24V PSU, wiring, LED status circuit
- Compatible with docking node #489 (ROS2 integration)
- 3D-printable PETG frame (base, back wall, guide rails, brackets)
- Electrical: 240W Mean Well IRM-240-24 PSU, 20A current capacity
- Safety: Fused output, varistor protection, soft-start capable
- Integration: MQTT status reporting, GPIO LED control (Jetson Orin NX)

Files:
- ISSUE_505_CHARGING_DOCK_24V_DESIGN.md: Complete design spec (mechanical, electrical, assembly)
- charging_dock_505_BOM.csv: Procurement list with sourcing info

Next: CAD implementation (charging_dock_505.scad, receiver variant)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-06 10:30:44 -05:00
sl-android
49628bcc61 feat: Add Issue #507 - Face display animations on STM32 LCD
Implements expressive face animations with 5 core emotions (happy/sad/curious/angry/sleeping) and smooth transitions on small LCD displays.

Features:
- State machine with smooth 0.5s emotion transitions (ease-in-out cubic easing)
- Automatic idle blinking (4-6s intervals, 100-150ms duration per blink)
- UART command interface via USART3 @ 115200 (text-based protocol)
- 30Hz target refresh rate via systick integration
- Low-level LCD abstraction supporting monochrome and RGB565
- Rendering primitives: pixel, line (Bresenham), circle (midpoint), filled rect

Architecture:
- face_lcd.h/c: Hardware-agnostic framebuffer & display driver
- face_animation.h/c: Emotion state machine & parameterized face rendering
- face_uart.h/c: UART command parser (HAPPY/SAD/CURIOUS/ANGRY/SLEEP/NEUTRAL/BLINK/STATUS)
- Unit tests (14 test cases): emotion transitions, blinking, rendering, all emotions

Integration:
- main.c: Added includes, initialization (servo_init), systick tick, main loop processing
- Pending: LCD hardware initialization (SPI/I2C config, display controller setup)

Files: 9 new (headers, source, tests, docs), 1 modified (main.c)
Lines: ~1450 total (345 headers, 650 source, 350 tests, 900 docs)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-06 10:27:36 -05:00
062c05cac0 feat: Add Issue #502 - Headscale VPN auto-connect on Orin
Configure Jetson Orin with Tailscale client connecting to Headscale
coordination server at tailscale.vayrette.com:8180. Device registers
as 'saltylab-orin' with persistent auth key for unattended login.

Features:
- systemd auto-start and restart on WiFi drops
- Persistent auth key storage at /opt/saltybot/tailscale-auth.key
- SSH + HTTP access over Tailscale tailnet (encrypted WireGuard)
- IP forwarding enabled for relay/exit node capability
- WiFi resilience with aggressive restart policy
- MQTT reporting of VPN status, IP, and connection type

Components added:
- jetson/scripts/setup-tailscale.sh: Tailscale package installation
- jetson/scripts/headscale-auth-helper.sh: Auth key management utility
- jetson/systemd/tailscale-vpn.service: systemd service unit
- jetson/docs/headscale-vpn-setup.md: Comprehensive setup documentation
- saltybot_cellular/vpn_status_node.py: ROS2 node for MQTT reporting

Updated:
- jetson/systemd/install_systemd.sh: Include tailscale-vpn.service
- jetson/scripts/setup-jetson.sh: Add Tailscale setup steps

Access patterns:
- SSH: ssh user@saltylab-orin.tail12345.ts.net
- HTTP: http://saltylab-orin.tail12345.ts.net:port
- Direct IP: 100.x.x.x (Tailscale allocated address)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-06 10:25:04 -05:00
767f377120 feat: Add Issue #504 - Integration test suite with launch_testing
Create saltybot_tests package with comprehensive automated testing:

Test Coverage:
- Node startup verification (all critical nodes within 30s)
- Topic publishing verification
- TF tree completeness (all transforms present)
- Sensor health checks (RPLIDAR, RealSense, IMU)
- Perception pipeline (person detection availability)
- Navigation stack (odometry, transforms)
- System stability (30-second no-crash test)
- Graceful shutdown verification

Features:
- launch_testing framework for automated startup tests
- NodeChecker: wait for nodes in ROS graph
- TFChecker: verify TF tree completeness
- TopicMonitor: track message rates and counts
- Follow mode tests (minimal hardware deps)
- Subsystem-specific tests for sensor health
- Comprehensive README with troubleshooting

Usage:
  pytest src/saltybot_tests/test/test_launch.py -v -s
  or
  colcon test --packages-select saltybot_tests

Performance Targets:
- Node startup: <30s (follow mode)
- RPLIDAR: 10 Hz scan rate
- RealSense: 30 Hz RGB + depth
- Person detection: 5 Hz
- System stability: 30s no-crash validation

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-06 10:22:38 -05:00
seb
80ee9ece87 Merge pull request 'feat: Voice command router (Issue #491)' (#499) from sl-webui/issue-491-voice-router into main 2026-03-05 19:25:32 -05:00
Sebastien Vayrette
868b453777 fix: resolve merge conflicts for voice router PR #499 (keep both docking + mission logging) 2026-03-05 19:25:23 -05:00
83e3033abe Merge pull request 'feat: OTA firmware update (Issue #492)' (#500) from sl-jetson/issue-492-ota-update into main 2026-03-05 17:24:58 -05:00
5f6a13ccca Merge pull request 'feat: Multi-sensor fusion (Issue #490)' (#498) from sl-perception/issue-490-sensor-fusion into main 2026-03-05 17:24:03 -05:00
4dc18201aa Merge pull request 'feat: Docking station behavior (Issue #489)' (#497) from sl-controls/issue-489-docking into main 2026-03-05 17:16:37 -05:00
2dd03a245d Merge pull request 'feat: ROS2 bag recording for mission logging (Issue #488)' (#496) from sl-firmware/issue-488-bag-recording into main 2026-03-05 17:16:22 -05:00
340248a0d2 feat: Add docking state publisher and update configuration (Issue #489)
- Add /saltybot/docking_state publisher (std_msgs/String) for monitoring
- Update docking_params.yaml battery_low_pct: 15% → 20%
- Add Issue #475 references for conservative servo speeds

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-05 17:08:48 -05:00
7a4930e8d2 feat: ROS2 bag recording for mission logging (Issue #488)
Implement automatic mission logging with bag recorder:
- Auto-records to ~/.saltybot-data/bags/ with 30min rotation
- Records mission-critical topics: /scan, /cmd_vel, /odom, /tf, /camera/color/image_raw/compressed, /saltybot/diagnostics
- MCAP format (preferred) with fallback to sqlite3 with zstd compression
- Services: /saltybot/save_bag, /saltybot/start_recording, /saltybot/stop_recording
- FIFO 20GB disk limit with automatic cleanup of oldest bags
- Auto-starts on launch, auto-saves on graceful shutdown

Changes:
- Updated bag_recorder_node.py with new parameters and services
- Changed default bag_dir to ~/.saltybot-data/bags/
- Set max_storage_gb to 20 (FIFO limit)
- Changed storage_format to MCAP by default
- Added start/stop recording service callbacks
- Updated package.xml description for mission logging

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-05 17:08:21 -05:00
b986702aed feat: Docking station behavior for auto-charging (Issue #489)
- Integrate saltybot_docking package into full_stack.launch.py
- Auto-trigger docking when battery drops to 20% (configurable via battery_low_pct)
- Launch docking at t=7s (after sensors, before Nav2)
- Add /saltybot/docking_state publisher (std_msgs/String) for state monitoring
- Update docking_params.yaml:
  - battery_low_pct: 15% → 20% per Issue #489
  - Add references to Issue #475 for conservative FC+hoverboard speeds
- Docking behavior includes:
  - ArUco marker or IR beacon detection for dock location
  - Nav2-based approach to pre-dock pose (~1m away)
  - Visual servoing final alignment with contact detection
  - Auto-undocking on full charge (80%) or command
  - Integration with power management for mission interruption/resumption

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-05 17:08:21 -05:00
a4285b5ecd feat: Docking station behavior for auto-charging (Issue #489)
- Integrate saltybot_docking package into full_stack.launch.py
- Auto-trigger docking when battery drops to 20% (configurable via battery_low_pct)
- Launch docking at t=7s (after sensors, before Nav2)
- Add /saltybot/docking_state publisher (std_msgs/String) for state monitoring
- Update docking_params.yaml:
  - battery_low_pct: 15% → 20% per Issue #489
  - Add references to Issue #475 for conservative FC+hoverboard speeds
- Docking behavior includes:
  - ArUco marker or IR beacon detection for dock location
  - Nav2-based approach to pre-dock pose (~1m away)
  - Visual servoing final alignment with contact detection
  - Auto-undocking on full charge (80%) or command
  - Integration with power management for mission interruption/resumption

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-05 17:08:15 -05:00
d770cb99a3 feat: Multi-sensor fusion for obstacle avoidance (Issue #490)
- saltybot_sensor_fusion: ROS2 node for LIDAR + depth sensor fusion
- Fuses RPLIDAR A1M8 (360° 2D) + RealSense D435i (front 87° 3D)
- Message filters for time-synchronized sensor inputs
- Smart blind spot handling: rear/sides LIDAR-only, front uses both
- Publishes /scan_fused (unified LaserScan) + PointCloud2 for voxel layer
- Configurable front sector angle (±45°), range multiplier, max range limit
- Parameters: depth_range_multiplier=0.9 (safety margin), max_range=5m

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-05 17:05:25 -05:00
fabfd5e974 feat: TTS personality engine (Issue #494)
Implement context-aware text-to-speech with emotion-driven expression for SaltyBot.

Features:
  ✓ Context-aware greetings (time of day, person names, emotion)
  ✓ Priority queue management (safety > social > idle)
  ✓ Emotion-based rate/pitch modulation (happy: faster+higher, sad: slower+lower)
  ✓ Integration with emotion engine (Issue #429) and TTS service (Issue #421)
  ✓ Configurable personality parameters
  ✓ Person recognition for personalized responses
  ✓ Queue management with 16-item buffer

Architecture:
  Node: tts_personality_node
    - Subscribes: /saltybot/tts_request, /saltybot/emotion_state, /saltybot/person_detected
    - Publishes: /saltybot/tts_command (formatted for TTS service), /saltybot/personality_state
    - Runs worker thread for asynchronous queue processing

Personality Parameters:
  - Name: "Luna" (default, configurable)
  - Speed modulation: happy=1.1x, sad=0.9x, neutral=1.0x
  - Pitch modulation: happy=1.15x, sad=0.85x, neutral=1.0x
  - Time-based greetings for 4 periods (morning, afternoon, evening, night)
  - Known people mapping for personalization

Queue Priority Levels:
  - SAFETY (3): Emergency/safety messages
  - SOCIAL (2): Greetings and interactions
  - IDLE (1): Commentary and chatter
  - NORMAL (0): Default messages

Files Created:
  - saltybot_tts_personality package with main personality node
  - config/tts_personality_params.yaml with configurable parameters
  - launch/tts_personality.launch.py for easy startup
  - Unit tests for personality context and emotion handling
  - Comprehensive README with usage examples

Integration Points:
  - Emotion engine (Issue #429): Listens to emotion updates
  - TTS service (Issue #421): Publishes formatted commands
  - Jabra SPEAK 810: Output audio device
  - Person tracking: Uses detected person names

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-05 17:05:11 -05:00
6d6909d9d9 feat: Voice command router (Issue #491)
Natural language voice command routing with fuzzy matching for speech variations.

Supported Commands:
- Follow me / Come with me
- Stop / Halt / Freeze
- Go home / Return to dock / Charge
- Patrol / Autonomous mode
- Come here / Approach
- Sit / Sit down
- Spin / Rotate / Turn around
- Dance / Groove
- Take photo / Picture / Smile
- What's that / Identify / Recognize
- Battery status / Battery level

Features:
- Fuzzy matching (rapidfuzz token_set_ratio) with 75% threshold
- Multiple pattern support per command for natural variations
- Three routing types: velocity (/cmd_vel), actions (/saltybot/action_command), services
- Command monitoring via /saltybot/voice_command
- Graceful handling of unrecognized speech

Architecture:
- Input: /saltybot/speech/transcribed_text (lowercase text)
- Fuzzy match against 11 command groups with 40+ patterns
- Route to: /cmd_vel (velocity), /saltybot/action_command (actions), or services

Files:
- saltybot_voice_router_node.py: Main router with fuzzy matching
- launch/voice_router.launch.py: Launch configuration
- VOICE_ROUTER_README.md: Usage documentation

Dependencies:
- rapidfuzz: Fuzzy string matching for natural speech handling
- rclpy, std_msgs, geometry_msgs: ROS2 core

Performance: <100ms per command (fuzzy matching + routing)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-05 17:05:02 -05:00
d79e38eb5b feat: OTA firmware update (Issue #492)
Complete over-the-air (OTA) firmware update system with:

Features:
- Downloads releases from Gitea (seb/saltylab-firmware)
- Automatic colcon build in staging directory
- Symlink-based atomic deployment
- ROS2 service restart via systemd
- Automatic rollback on build failure
- Version tracking in ~/.saltybot-data/versions.json
- Update history with timestamps

Safety:
- Blocks updates if robot velocity > 0.05 m/s
- Velocity monitoring via odometry subscription
- Backup before update for recovery

Triggers:
- MQTT /saltybot/ota_command: 'check', 'update:<version>', 'rollback'
- /saltybot/ota_status: JSON status updates
- Dashboard integration ready

Configuration:
- Gitea API base, repo info, directories
- Build timeout: 3600s (1 hour)
- Service restart automation
- Backup retention policy

ROS2 package structure complete with launch files and config.
2026-03-05 17:04:35 -05:00
7b22141142 feat: OTA firmware update (Issue #492)
Complete over-the-air (OTA) firmware update system with:

Features:
- Downloads releases from Gitea (seb/saltylab-firmware)
- Automatic colcon build in staging directory
- Symlink-based atomic deployment
- ROS2 service restart via systemd
- Automatic rollback on build failure
- Version tracking in ~/.saltybot-data/versions.json
- Update history with timestamps

Safety:
- Blocks updates if robot velocity > 0.05 m/s
- Velocity monitoring via odometry subscription
- Backup before update for recovery

Triggers:
- MQTT /saltybot/ota_command: 'check', 'update:<version>', 'rollback'
- /saltybot/ota_status: JSON status updates
- Dashboard integration ready

Configuration:
- Gitea API base, repo info, directories
- Build timeout: 3600s (1 hour)
- Service restart automation
- Backup retention policy

ROS2 package structure complete with launch files and config.
2026-03-05 17:04:27 -05:00
285178b2f9 feat: Configure Nav2 navigation stack conservative speeds (Issue #475)
- Update max_vel_x to 0.3 m/s (conservative for FC + hoverboard ESC)
- Update max_vel_theta to 0.5 rad/s (conservative for FC + hoverboard ESC)
- Set robot_radius to 0.22 m for 0.4m x 0.4m footprint
- Configure velocity smoother with conservative limits
- Both DWB local planner and velocity smoother updated for consistency
- RPLIDAR (/scan) + depth_to_laserscan (/depth_scan) costmap layers enabled
- NavFn global planner, DWB local planner configured

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-05 14:50:41 -05:00
56a48b4e25 Merge PR #486: Issue #480 - Map save/load 2026-03-05 14:48:59 -05:00
8f0215d461 Merge PR #485: Issue #483 - Monitoring dashboard 2026-03-05 14:48:46 -05:00
f5877756d5 feat: Map save/load service for SLAM Toolbox persistence (Issue #480)
Implement automatic map serialization and persistence for slam_toolbox:
- New SlamToolboxPersistenceNode with auto-save every 5 minutes
- Auto-load most recent map on startup
- Services: /saltybot/save_map, /saltybot/load_map, /saltybot/list_maps
- Export to Nav2-compatible YAML + PGM format
- Stores maps in ~/.saltybot-data/maps/ with .posegraph format
- Integrates with slam_toolbox serialize/deserialize services

Changes:
- Created saltybot_mapping/slam_toolbox_persistence.py
- Added slam_toolbox_persistence.launch.py
- Updated slam.launch.py to include persistence service
- Updated CMakeLists.txt to install new executable
- Added slam_toolbox dependency to package.xml

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-05 14:46:33 -05:00
33036e2967 feat: Add URDF robot description (Issue #477) 2026-03-05 14:43:01 -05:00
50d1ebbdcc feat: Behavior tree coordinator for autonomous mode (Issue #482)
State machine: idle → patrol → investigate → interact → return
- IDLE: Waiting for activation or battery recovery
- PATROL: Autonomous patrolling with curiosity-driven exploration (#470)
- INVESTIGATE: Approach and track detected persons
- INTERACT: Social interaction with face recognition and gestures
- RETURN: Navigate back to home/dock with recovery behaviors

Integrations:
- Patrol mode (#446): Waypoint routes with geofence (#441)
- Curiosity behavior (#470): Autonomous exploration
- Person following: Approach detected persons
- Emergency stop cascade (#459): Highest priority safety
- Geofence constraint (#441): Keep patrol within boundaries

Blackboard variables:
- battery_level: Current battery percentage
- person_detected: Person detection state
- current_mode: Current behavior tree state
- home_pose: Home/dock location

Files:
- behavior_trees/autonomous_coordinator.xml: BehaviorTree definition
- launch/autonomous_mode.launch.py: Full launch setup
- saltybot_bringup/bt_nodes.py: Custom BT node plugins
- BEHAVIOR_TREE_README.md: Documentation

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-05 14:42:57 -05:00