Desktop app for analyzing FRC robot shooting performance. Combines WPILib DataLog (.wpilog) files with match broadcast video to detect shots, track balls, and determine shot accuracy.
Built for the 2026 FRC game REBUILT, where robots launch yellow Fuel into alliance-specific Hubs.
- Shot detection from logs -- Detects shots from shooter wheel RPM drops, gated by controller input (right bumper + right trigger)
- Robot tracking -- SAM 2 video segmentation tracks your robot across the entire match from a single bounding box annotation
- Ball detection -- HSV yellow blob detection finds balls departing the robot, tracks their flight path, and determines if they score
- Goal scoring -- Define red/blue hub regions on the broadcast frame; balls are classified as made/missed based on trajectory
- Video sync -- Manual sync workflow aligns log timestamps to video time with fine-tune nudge controls
- Project persistence -- Save/load full session state (video, log, sync, tracking, ball results) as
.saprojJSON files
# Install dependencies
uv sync
# CLI: analyze a single log
uv run python main.py logs/some_match.wpilog
# CLI: batch analyze all logs
uv run python main.py --all
# GUI: launch the full app
uv run --extra gui python gui_main.py
# Run tests
uv run python -m pytest tests/ -vuv pip install "sam-2 @ git+https://github.com/facebookresearch/sam2.git" torch torchvision huggingface_hubRequires CUDA GPU with ~12GB VRAM. Falls back to CPU (slow) if CUDA is unavailable. CSRT tracking is available as a legacy fallback without GPU.
- File > Open Video -- Load a match broadcast video (
.mp4) - File > Open Log -- Load the corresponding
.wpilogfile; shots are detected automatically - Sync -- Click "Sync to Auto" at the moment autonomous starts in the video to align log and video time
- Tracking > SAM 2: Click & Track -- Pause on a frame, draw a box around your robot, SAM 2 propagates tracking through the whole video
- Ball Detection > Set Hub Regions -- Draw red and blue hub rectangles on a frame (fixed for all matches from the same broadcast camera)
- Ball Detection > Detect Balls -- Scans the full video for yellow balls departing the robot's bounding box, classifies each as made/missed
- File > Save Project -- Saves everything to a
.saprojfile for later
- Ball Detection > Show Launch Zone -- Visualizes the detection zone above the robot
- Ball Detection > Debug HSV Overlay -- Shows live HSV detection results on the video (cyan = valid candidate in launch zone, orange = outside, red = too small, magenta = too large)
# Dump annotated frames showing HSV detection + launch zone
uv run python debug_ball_detect.py saves/project.saproj --start-s 81 --duration-s 10
# Override HSV parameters
uv run python debug_ball_detect.py saves/project.saproj --h-low 15 --s-low 50 --min-area 30# Single log analysis
uv run python main.py logs/match.wpilog
# All logs in a directory
uv run python main.py --all --logs-dir logs/
# Skip button gating (detect all RPM drops)
uv run python main.py --all --no-buttons
# List all entry names in a log
uv run python main.py match.wpilog --list-entries
# Custom detection parameters
uv run python main.py match.wpilog --threshold 300 --window 0.3main.py CLI entry point
gui_main.py GUI entry point (PyQt6)
gui/
main_window.py Main window, menus, signal wiring
video_player.py QVideoSink-based player with tracking + ball overlays
timeline_widget.py Custom-painted RPM trace, shot markers, scrub bar
shot_table.py Shot list with outcome column
sync_panel.py Video-log sync controls
sam2_tracker.py SAM 2 robot tracking (primary)
tracker.py CSRT robot tracking (legacy)
ball_detector.py HSV ball detection, trajectory tracking, goal classification
hub_dialog.py Hub region selection dialog
click_dialog.py SAM 2 box prompt dialog
log_loader.py Log parsing bridge for GUI
project.py .saproj save/load
log_parser/
reader.py WPILogFile: .wpilog parsing via robotpy-wpiutil
shot_detector.py RPM-based shot detection + summaries
utils/
config.py Dataclass configs for all tunable parameters
tests/
test_shot_detector.py Shot detection tests
test_project.py Project save/load tests
Reads NT:/Shooter/RollerVelocityRPM from .wpilog files and detects RPM drops exceeding a threshold (default 500 RPM) within a time window (default 200ms). Shots are gated on controller input: right bumper (DS:joystick0/buttons[5]) AND right trigger (DS:joystick0/axes[3]) must be held.
User draws a bounding box around their robot on any frame. SAM 2 propagates the segmentation mask through the entire video at ~10fps. The mask is converted to bounding boxes for overlay rendering. Frames are subsampled and offloaded to CPU to fit within GPU memory.
Scans every video frame using HSV color filtering for yellow (H: 20-35, S: 100-255, V: 100-255). A launch zone is computed above the robot's tracked bounding box. When a valid yellow blob appears in the launch zone, it starts being tracked frame-by-frame. Multiple balls can be tracked simultaneously. Tracking stops after 1.5 seconds or when the blob is lost.
Each ball trajectory is checked against the correct alliance's hub region (determined from NT:/GameState/OnBlue in the log, with NT:/FMSInfo/IsRedAlliance as fallback). If the ball enters the hub region or was heading toward it when tracking ended, it's classified as "made". Otherwise "missed".