feat(#142): terrain adaptation — surface detection + dynamic speed/PID/bias #154

Merged
sl-jetson merged 1 commits from sl-controls/issue-142-terrain into main 2026-03-02 10:07:39 -05:00
Collaborator

Summary

Implements Issue #142 — surface detection and dynamic terrain adaptation.

Two new ROS2 packages:

saltybot_terrain_msgs (ament_cmake)

  • Terrain.msg: stamp, surface_type, roughness, incline_rad, grip, slip_ratio, speed_scale

saltybot_terrain_adaptation (ament_python, 50 Hz)

  • terrain_analyzer.py: IMU vibration RMS → normalised roughness + surface classification (smooth/tile/carpet/grass/gravel) with hysteresis-protected switching (5-sample gate)
  • incline_detector.py: quaternion pitch → first-order LPF (τ=1.5s) → incline_rad; motor_bias() = k_bias × sin(incline)
  • slip_detector.py: |v_cmd − v_actual| / |v_cmd| with EMA smoothing; gated below min_speed_ms
  • terrain_adapter.py: TerrainState, TerrainAdapter (speed_scale = roughness_scale × incline_scale × slip_scale; pid_scales from grip; motor_bias), TerrainHistory (rolling deque + JSON type-change log)
  • terrain_adaptation_node.py: 50 Hz; IMU watchdog (0.5s timeout → safe defaults); publishes /saltybot/terrain JSON, /saltybot/terrain_speed_scale, /saltybot/terrain_pid_scales, /saltybot/terrain_motor_bias, /saltybot/terrain_history; optional typed /saltybot/terrain_typed (TerrainMsg) if saltybot_terrain_msgs is built
  • config/terrain_params.yaml, launch/terrain_adaptation.launch.py

Test plan

  • 60/60 pytest passing: TerrainAnalyzer (buffer, roughness, classification), SlipDetector (ratio, gate, EMA, reset), InclineDetector (LPF, seeding, clamp, motor_bias), TerrainAdapter (speed_scale, pid_scales, motor_bias, apply), TerrainHistory (change detection, JSON log)
## Summary Implements Issue #142 — surface detection and dynamic terrain adaptation. Two new ROS2 packages: ### `saltybot_terrain_msgs` (ament_cmake) - `Terrain.msg`: stamp, surface_type, roughness, incline_rad, grip, slip_ratio, speed_scale ### `saltybot_terrain_adaptation` (ament_python, 50 Hz) - **`terrain_analyzer.py`**: IMU vibration RMS → normalised roughness + surface classification (smooth/tile/carpet/grass/gravel) with hysteresis-protected switching (5-sample gate) - **`incline_detector.py`**: quaternion pitch → first-order LPF (τ=1.5s) → incline_rad; `motor_bias()` = k_bias × sin(incline) - **`slip_detector.py`**: |v_cmd − v_actual| / |v_cmd| with EMA smoothing; gated below `min_speed_ms` - **`terrain_adapter.py`**: `TerrainState`, `TerrainAdapter` (speed_scale = roughness_scale × incline_scale × slip_scale; pid_scales from grip; motor_bias), `TerrainHistory` (rolling deque + JSON type-change log) - **`terrain_adaptation_node.py`**: 50 Hz; IMU watchdog (0.5s timeout → safe defaults); publishes `/saltybot/terrain` JSON, `/saltybot/terrain_speed_scale`, `/saltybot/terrain_pid_scales`, `/saltybot/terrain_motor_bias`, `/saltybot/terrain_history`; optional typed `/saltybot/terrain_typed` (TerrainMsg) if `saltybot_terrain_msgs` is built - `config/terrain_params.yaml`, `launch/terrain_adaptation.launch.py` ## Test plan - [x] 60/60 pytest passing: TerrainAnalyzer (buffer, roughness, classification), SlipDetector (ratio, gate, EMA, reset), InclineDetector (LPF, seeding, clamp, motor_bias), TerrainAdapter (speed_scale, pid_scales, motor_bias, apply), TerrainHistory (change detection, JSON log)
sl-controls added 1 commit 2026-03-02 10:04:59 -05:00
Two new ROS2 packages implementing Issue #142:

saltybot_terrain_msgs (ament_cmake)
- Terrain.msg: stamp, surface_type, roughness, incline_rad, grip, slip_ratio, speed_scale

saltybot_terrain_adaptation (ament_python, 50 Hz)
- terrain_analyzer.py: IMU vibration RMS → roughness [0,1] + surface classification
  (smooth/tile/carpet/grass/gravel) with hysteresis-protected switching
- incline_detector.py: quaternion pitch → first-order LPF → incline_rad; motor_bias()
- slip_detector.py: cmd_vel vs odom → slip_ratio with EMA smoothing + speed gate
- terrain_adapter.py: TerrainState, TerrainAdapter (speed_scale, pid_scales, motor_bias),
  TerrainHistory (rolling deque, JSON type-change log)
- terrain_adaptation_node.py: 50 Hz node; IMU watchdog; publishes terrain JSON + typed msg,
  speed_scale, pid_scales (JSON), motor_bias, terrain_history (on type change)
- config/terrain_params.yaml, launch/terrain_adaptation.launch.py

Tests: 60/60 passing (TerrainAnalyzer, SlipDetector, InclineDetector, TerrainAdapter,
TerrainHistory all covered)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
sl-jetson merged commit 33007fb5ed into main 2026-03-02 10:07:39 -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#154
No description provided.