Files
Oleksandr Bezdieniezhnykh bc40ea7300 [AZ-626] Decompose complete: 47 tasks + docs + module layout
Greenfield Steps 1-6 baseline for the autopilot rewrite from legacy
Qt/C++ to a Rust workspace.

- Remove legacy Qt/C++ tree (ai_controller, drone_controller,
  misc/camera, python_scaffold, root Dockerfile, autopilot.pro,
  legacy main.py / requirements.txt).
- Add _docs/00_problem (problem, restrictions, acceptance criteria,
  security approach, input data + fixtures).
- Add _docs/01_solution/solution_draft01.
- Add _docs/02_document (architecture, system-flows, data_model,
  glossary, decision-rationale, deployment, 13 component descriptions,
  tests/ specs, FINAL_report, module-layout).
- Add _docs/02_tasks/todo with 47 task specs (AZ-640..AZ-686, one
  bootstrap + 46 component tasks) and _dependencies_table.md.
- Add .cursor/rules/artifact-srp.mdc (single-responsibility rule for
  canonical _docs artifacts).
- Track autodev state in _docs/_autodev_state.md (Step 6 completed,
  ready for Step 7 Implement).

Jira: bootstrap AZ-626; component epics AZ-627..AZ-639; tasks
AZ-640..AZ-686. Total complexity 173 points across 12 epics.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-19 11:02:01 +03:00

6.7 KiB
Raw Permalink Blame History

Component — movement_detector

Layer: Perception (data plane in) Status: forward-looking design (Rust + OpenCV bindings; learned-CV fallback per architecture.md §8 Q14)

1. Purpose

Detect small moving point/cluster candidates that are not yet classifiable by Tier 1, in both the zoom-out and zoom-in scan levels, and enqueue them as POIs for confirmation. Compensates for UAV and gimbal motion using synchronised telemetry; naive frame differencing is rejected.

The component is suppressed only during scan_controller's TargetFollow state (the gimbal is dominated by tracking commands during follow).

2. Inputs

Input Source Cadence Notes
Frame frame_ingest up to 30 fps Frames are skipped when ai_locked is set or the system is in TargetFollow.
Gimbal angle (yaw, pitch) gimbal_controller per frame, monotonic-timestamped Telemetry-skew gate: reject samples where frame ↔ gimbal skew exceeds the configured tolerance for the current zoom band.
Zoom state gimbal_controller per frame, monotonic-timestamped Drives zoom-band selection (zoomed_out vs zoomed_in) and per-band thresholds; also used for residual-motion scaling.
UAV motion telemetry mavlink_layer (via mission_executor) 10 Hz target Position + attitude + velocity + monotonic timestamp.
Active-state hint scan_controller event enable_zoomed_out / enable_zoomed_in / disable (the latter is set during TargetFollow).

3. Outputs

Output Consumer Shape
MovementCandidate scan_controller { frame_seq, bbox_normalized, residual_velocity_estimate, telemetry_quality, source_frame_ts, source_zoom_band }
Health metric health aggregator enabled, current_zoom_band, candidates_per_min_zoomed_out, candidates_per_min_zoomed_in, telemetry_skew_drops_total, compensation_quality_per_band.

4. Key Responsibilities

  • Compute per-frame ego-motion using OpenCV optical flow / global motion estimation (e.g. dense Lucas-Kanade or feature-based homography), refined by the synchronised gimbal + UAV telemetry.
  • Subtract estimated ego-motion from per-pixel motion; cluster the residuals.
  • Emit clusters that meet the per-zoom-band minimum size + persistence threshold as MovementCandidates, capped to honour the system-wide ≤5 POIs/min operator-review budget shared with scan_controller.
  • Self-disable in TargetFollow. The component still consumes frames while disabled (to keep its motion-history warm) but emits no candidates.
  • Tag each emitted candidate with source_zoom_band so scan_controller can apply zoom-band-aware queueing logic (described in system-flows.md §F2).

5. Per-zoom-band tuning

The same code path runs at zoom-out and zoom-in, but the configuration differs because the pixel-to-metre ratio differs by ~10×.

Knob Zoom-out (typical) Zoom-in (typical)
Cluster persistence threshold 35 frames 610 frames (gimbal-pan-induced flicker is more frequent at narrow FOV)
Residual-velocity floor low (small physical motion is enough) higher (small physical motion is amplified pixel-wise; raising the floor reduces FP from compensation residuals)
Telemetry-skew tolerance 50 ms frame ↔ gimbal, 100 ms frame ↔ UAV 25 ms frame ↔ gimbal, 50 ms frame ↔ UAV (stricter — gimbal slewing dominates zoomed FOV)
Enqueue-latency budget ≤1 s ≤1.5 s (allows brief gimbal-stability window)
FP cap (per-band) per architecture.md §6 NFR per architecture.md §6 NFR; if exceeded, fallback per Q14

Exact values are mission-tunable; defaults are calibrated during the benchmark gate.

6. Internal State

  • Rolling motion-history buffer (a few seconds of frames + telemetry). One buffer per zoom band; switching bands does not invalidate the buffer for the other.
  • Per-cluster persistence counters (per zoom band).
  • Telemetry-sync state machine.
  • current_zoom_band derived from gimbal_controller's zoom state.

State is in-process only.

7. Failure Modes

Failure Detection Behaviour
Telemetry skew above tolerance (per zoom band) timestamp delta exceeds threshold Drop that frame's compensation; do not emit candidates for the affected window; counter-tagged drop.
Optical-flow degenerate flow magnitudes implausible (e.g. camera failure, full motion blur) Skip emission for that frame; surface as a health signal on sustained occurrence.
Sustained candidate flood at zoom-in (FP cap exceeded) candidates_per_min_zoomed_in over a sliding window Suppress zoom-in emission only; keep zoom-out emission running; surface health → yellow; this is the trigger condition for the Q14 fallback.
Sustained candidate flood at zoom-out (FP cap exceeded) candidates_per_min_zoomed_out over a sliding window Down-rank lowest-confidence candidates; surface health → yellow; never silently drop without counting.
Component disabled by scan_controller active-state hint = disable Emit zero candidates; keep motion history warm.

8. Dependencies

In-process: frame_ingest, gimbal_controller, mavlink_layer, scan_controller.

External: OpenCV (patched, version-pinned). Optional: a learned-CV crate / module (RAFT-derivative or CNN motion-segmentation) behind a build-time feature flag — engaged only when the Q14 fallback is required.

9. Non-Functional Targets

Concern Target
Candidate enqueue latency (zoom-out) ≤1 s from detection to POI in queue
Candidate enqueue latency (zoom-in) ≤1.5 s from detection to POI in queue
False-positive rate at the operator surface bounded by scan_controller's ≤5 POIs/min cap; per-zoom-band internal caps prevent zoom-in starving zoom-out
CPU budget on Jetson configurable; must coexist with Tier 1 (running in ../detections) and Tier 2
Telemetry-skew tolerance per-zoom-band; defaults in §5

10. Open Questions

  • Q14 fallback selection (architecture.md §8): if classical OpenCV fails the per-zoom-band FP cap at zoom-in, the fallback module — learned optical flow vs CNN motion-segmentation vs IMU-tighter-coupled classical — is open. Interface contract is fixed (Frame + telemetry → Vec<MovementCandidate>).
  • Minimum cluster persistence threshold across zoom bands (refined during benchmark gate).
  • Whether to share the motion-history buffer across zoom-band transitions or reset on transition (§6 currently says share).

11. References

  • architecture.md §3, §5 Architectural Principles (ego-motion compensation mandatory; movement runs at both zoom levels), §7.6 Movement detector, §8 Q14.
  • system-flows.md §F2 Movement detection (zoom-out + zoom-in).
  • data_model.md §MovementCandidate.