feat(bringup): floor surface type classifier on D435i RGB (Issue #249) #256

Merged
sl-jetson merged 1 commits from sl-perception/issue-249-floor-classifier into main 2026-03-02 13:22:18 -05:00
Collaborator

Summary

  • Pure-Python _floor_classifier.py: extract_features(bgr, roi_frac=0.40) → 6-dim feature vector [hue_mean, sat_mean, val_mean, sat_std, texture_var, edge_density] from bottom 40% of frame (floor ROI); classify_floor_patch() → nearest-centroid in weighted L2 space, returns ClassifyResult(label, confidence, features, distances); LabelSmoother — majority-vote deque over last N frames
  • Six classes with hand-calibrated centroids: carpet (mid-sat, smooth), tile (low-sat, structured edges), wood (warm hue, moderate grain), concrete (grey, uniform), grass (green, high-sat), gravel (low-sat, high-edge)
  • floor_classifier_node.py: subscribes /camera/color/image_raw (BEST_EFFORT), publishes std_msgs/String on /saltybot/floor_type at 2 Hz with distance_threshold guard that outputs "unknown" when no class matches well

Test plan

  • test/test_floor_classifier.py — 34/34 pure-Python tests pass (no ROS2 required)
  • Synthetic validation: green → grass ✓ grey → concrete ✓ orange → wood ✓
  • Deploy on Jetson, walk across carpet/tile/outdoor grass — confirm label changes
  • Tune centroids via class_centroids ROS parameter if needed

🤖 Generated with Claude Code

## Summary - Pure-Python `_floor_classifier.py`: `extract_features(bgr, roi_frac=0.40)` → 6-dim feature vector `[hue_mean, sat_mean, val_mean, sat_std, texture_var, edge_density]` from bottom 40% of frame (floor ROI); `classify_floor_patch()` → nearest-centroid in weighted L2 space, returns `ClassifyResult(label, confidence, features, distances)`; `LabelSmoother` — majority-vote deque over last N frames - Six classes with hand-calibrated centroids: **carpet** (mid-sat, smooth), **tile** (low-sat, structured edges), **wood** (warm hue, moderate grain), **concrete** (grey, uniform), **grass** (green, high-sat), **gravel** (low-sat, high-edge) - `floor_classifier_node.py`: subscribes `/camera/color/image_raw` (BEST_EFFORT), publishes `std_msgs/String` on `/saltybot/floor_type` at 2 Hz with `distance_threshold` guard that outputs `"unknown"` when no class matches well ## Test plan - [x] `test/test_floor_classifier.py` — 34/34 pure-Python tests pass (no ROS2 required) - Synthetic validation: green → grass ✓ grey → concrete ✓ orange → wood ✓ - [ ] Deploy on Jetson, walk across carpet/tile/outdoor grass — confirm label changes - [ ] Tune centroids via `class_centroids` ROS parameter if needed 🤖 Generated with [Claude Code](https://claude.com/claude-code)
sl-perception added 1 commit 2026-03-02 12:51:35 -05:00
Adds multi-feature nearest-centroid classifier for 6 surface types:
carpet, tile, wood, concrete, grass, gravel.  Features: circular hue mean,
saturation mean/std, brightness, Laplacian texture variance, Sobel edge
density — all extracted from the bottom 40% of each frame (floor ROI).
Majority-vote temporal smoother (window=5) suppresses single-frame noise.
Publishes std_msgs/String on /saltybot/floor_type at 2 Hz.
34/34 pure-Python tests pass (no ROS2 required).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
sl-jetson merged commit a55cd9c97f into main 2026-03-02 13:22:18 -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#256
No description provided.