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>
22 KiB
autopilot — Data Model
Status: forward-looking design (Rust). This is the canonical entity catalogue.
The autopilot binary itself has one persistent store: the on-device mapobjects_store (engine TBD — architecture.md §8 Q3). Everything else is in-memory only. Mission state and the central MapObjects state are pulled from the external missions API on start; there is no in-process mission database. The on-device mapobjects_store is a working copy of the central MapObjects state for the active mission's bounding box; the central observation log is the source of truth across missions (per architecture.md §7.13).
1. Entity Map
erDiagram
Frame ||--o{ Detection : "produced by detection_client"
Frame ||--o{ MovementCandidate : "produced by movement_detector (zoom-out + zoom-in)"
Detection ||--|| BoundingBox : "bbox_normalized"
DetectionBatch ||--o{ Detection : "contains"
POI ||--o| Tier2Evidence : "zoom-in Tier 2"
POI ||--o| VlmAssessment : "Tier 3 (optional, zoom-in)"
POI }o--o| MapObject : "lookup by H3 + class"
POI }o--o| IgnoredItem : "decline_suppressed"
MapObject ||--o{ MapObjectObservation : "history (central append-only log)"
MapObjectsBundle ||--o{ MapObject : "pre-flight pull"
MapObjectsBundle ||--o{ MapObjectObservation : "post-flight push"
MapObjectsBundle ||--o{ IgnoredItem : "ignored items round-trip"
OperatorCommand ||--o| POI : "confirm/decline target"
MissionItem ||--o{ MissionWaypoint : "translates to"
MissionItem ||--o{ Geofence : "carries"
Geofence }o--o{ Coordinate : "polygon"
MissionWaypoint ||--|| Coordinate : "at"
2. Perception entities
Frame
A decoded video frame. Produced by frame_ingest; consumed by detection_client, movement_detector, telemetry_stream.
| Field | Type | Notes |
|---|---|---|
seq |
u64 | Monotonic sequence number; primary key for cross-component correlation. |
capture_ts_monotonic_ns |
u64 | Wall-clock-independent timestamp at the earliest practical point in the pipeline. |
decode_ts_monotonic_ns |
u64 | When frame_ingest finished decoding. |
pixels |
Arc<Bytes> |
Raw pixel data; consumers do not copy. |
width, height |
u32 | |
pix_fmt |
enum | NV12 | YUV420P | RGB24 (decoder dependent). |
ai_locked |
bool | If set, downstream consumers skip detection (operator-side or supervisor gating). |
In-memory only.
BoundingBox
| Field | Type | Notes |
|---|---|---|
x_min, y_min, x_max, y_max |
f32 | Normalised to [0.0, 1.0] in image coordinates. |
Detection
One Tier-1 detection. Mirrors the ../detections contract; carries through to operator overlay unchanged.
| Field | Type | Notes |
|---|---|---|
class_id |
u32 | |
class_name |
string | Human-readable label. |
confidence |
f32 | 0.0–1.0. |
bbox_normalized |
BoundingBox |
|
mask_or_polyline |
optional bytes | For polyline classes (e.g. footpaths). |
source_frame_seq |
u64 | Foreign key into Frame. |
DetectionBatch
| Field | Type | Notes |
|---|---|---|
frame_seq |
u64 | |
detections |
Vec<Detection> |
|
latency_ms |
u32 | Tier-1 round-trip; observed for budget compliance. |
model_version |
string | Reported by ../detections; logged on change. |
MovementCandidate
A residual-motion cluster surviving ego-motion compensation in movement_detector.
| Field | Type | Notes |
|---|---|---|
frame_seq |
u64 | |
bbox_normalized |
BoundingBox |
|
residual_velocity_estimate |
optional struct | Direction + magnitude in image coords; used for prioritisation. |
telemetry_quality |
enum | synced | degraded | unsynced (drives whether the candidate may be surfaced at all). |
source_frame_ts_monotonic_ns |
u64 | |
source_zoom_band |
enum | zoomed_out | zoomed_in. Drives scan_controller's queueing logic (per system-flows.md §F2): zoom-out candidates enter the POI queue normally; zoom-in candidates may bump current-ROI confidence or enter the queue with their own priority. |
Tier2Evidence
Output of semantic_analyzer for a single zoom-in ROI hold.
| Field | Type | Notes |
|---|---|---|
roi_id |
uuid | Stable identifier within a zoom-in hold. |
path_freshness |
f32 | null | 0.0 = no path / not applicable; 1.0 = fresh. |
endpoint_score |
f32 | null | Concealed-position likelihood at an endpoint (branch pile / dark entrance). |
concealment_score |
f32 | null | General concealment-POI score. |
recommended_next_action |
enum | PanFollowFootpath | HoldEndpoint | PanBroad | ReturnToZoomOut. |
source_detections |
Vec<DetectionId> |
For audit / replay. |
status |
enum | ok | timeout | oversize | error. |
VlmAssessment
Validated, structured response from vlm_client. Free-form VLM text is not a downstream API.
| Field | Type | Notes |
|---|---|---|
label |
enum | confirmed_concealed_position | rejected | inconclusive | error. |
confidence |
f32 | 0.0–1.0; VLM-reported or derived. |
evidence_spans |
Vec<string> |
Short justifications, bounded length. |
reason |
string | One-line rationale; bounded length. |
status |
enum | ok | timeout | schema_invalid | ipc_error | disabled. |
latency_ms |
u32 | Round-trip including IPC. |
model_version |
string | Reported by the NanoLLM process for the loaded weights; logged on change for forensic correlation. |
status semantics: any value other than ok MUST result in label = inconclusive (or error for a critical failure). scan_controller MUST NOT promote a POI to a confirmed target on a non-ok VlmAssessment.
3. Decision entities
POI
A Point-of-Interest enqueued by scan_controller. Source: a Tier-1 detection, a movement candidate from movement_detector, or a Tier-2 semantic finding.
| Field | Type | Notes |
|---|---|---|
id |
uuid | Stable for the POI's lifetime. |
confidence |
f32 (0.0–1.0) | Composite of detection / motion / Tier-2 score. |
mgrs |
string | MGRS coordinate from the GPS-Denied service or autopilot GPS. |
class |
string | Concrete class. |
class_group |
enum | Per mapobjects_store config (e.g. military_vehicle_group, concealed_position_group, movement_candidate). |
source_detection_ids |
Vec<DetectionId> |
For audit / replay. |
enqueued_at |
timestamp | For queue ageing. |
priority |
f32 | confidence × proximity_to_current_camera × age_factor. |
decline_suppressed |
bool | True if (MGRS, class_group) matches an existing IgnoredItem. |
vlm_status |
enum | Mirrors VlmAssessment.status (or not_requested / pending). |
tier2_evidence |
optional Tier2Evidence |
|
deadline |
timestamp | Per the confidence-scaled operator-decision window. |
Field queue_position is not stored; it is computed at read time from priority + enqueued_at.
MapObject
A persisted map entry, indexed by H3 cell. Owned by mapobjects_store; written on each NEW / MOVED classification, read on each new detection. The on-device MapObject is a working copy of the central state for the active mission.
| Field | Type | Notes |
|---|---|---|
h3_cell |
u64 | H3 cell index at the configured resolution (default res 10, ~15 m edge). |
mgrs_key |
string | MGRS coordinate; together with class forms the hashtable composite key. |
class |
string | Concrete class (not the group). |
class_group |
string | Group used for matching during EXISTING / MOVED / NEW classification. |
gps_lat, gps_lon |
f64 | For distance calculation against incoming detections. |
size_width_m, size_length_m |
f32 | Bounding area on the ground. |
confidence |
f32 | Latest observation confidence (or running average, per implementation). |
first_seen, last_seen |
timestamp | Earliest and most recent observation; last_seen drives the REMOVED candidate diff at region-end. |
mission_id |
string | For the DELETE /missions/{id} cascade. |
source |
enum | central_pulled (came from pre-flight pull) | local_observed (added during this mission). On post-flight push only local_observed records become new observations centrally. |
pending_upload |
bool | True for any local_observed entry not yet pushed centrally. Cleared on successful POST /missions/{id}/mapobjects ack. |
Persisted in mapobjects_store (engine TBD per architecture.md §8 Q3).
MapObjectObservation
A single per-detection record. The on-device store appends one of these per NEW / MOVED / EXISTING / REMOVED-CANDIDATE classification; the post-flight push uploads the unflushed list to the central missions API. The central side stores all observations append-only as the source of truth (per architecture.md §7.13).
| Field | Type | Notes |
|---|---|---|
id |
uuid | Locally generated; stable across the mission. |
h3_cell |
u64 | |
class |
string | Concrete class. |
class_group |
string | Group used for the diff. |
mission_id |
string | |
uav_id |
string | Identifies the airframe; assigned at provisioning. |
observed_at_monotonic_ns |
u64 | Local monotonic at observation. |
observed_at_wallclock |
timestamp | Bound from GPS or NTP per the wall-clock policy. |
gps_lat, gps_lon |
f64 | |
mgrs |
string | |
size_width_m, size_length_m |
f32 | |
confidence |
f32 | |
diff_kind |
enum | NEW | MOVED | EXISTING | REMOVED_CANDIDATE. |
photo_ref |
string | null | URL or compact reference; uploaded out-of-band per the central API contract (Q7). |
raw_evidence |
json | null | Audit payload; size-capped. |
In-memory; durably persisted in mapobjects_store until the post-flight push acknowledges. On the central side, map_object_observations is the corresponding table (see architecture.md §7.13).
MapObjectsBundle
The wire shape for both the pre-flight pull (response body) and the post-flight push (request body) on /missions/{id}/mapobjects.
| Field | Type | Notes |
|---|---|---|
schema_version |
string | Semver; mismatched versions are rejected. |
mission_id |
string | |
bbox |
Coordinate[2] (NW + SE) |
The mission area; used by the central API to scope the response. |
map_objects |
Vec<MapObject> |
Pre-flight: current view from the central store. Post-flight push uses MapObjectObservation instead (see below). |
observations |
Vec<MapObjectObservation> |
Post-flight: the full pass diff. |
ignored_items |
Vec<IgnoredItem> |
Pre-flight: union-merged from the central store. Post-flight: only items appended during this mission. |
as_of |
timestamp | Pre-flight: when the central store snapshot was computed. Post-flight: when the on-device flush started. |
freshness |
enum (pre-flight only) | fresh (≤ configured staleness window) | stale (operator must acknowledge to use). |
IgnoredItem
A scene the operator declined; consulted by scan_controller before promoting any future detection to a POI. Union-merged across missions on the central side (per architecture.md §7.13 conflict resolution).
| Field | Type | Notes |
|---|---|---|
id |
uuid | Locally generated. |
mgrs |
string | Decline location. |
h3_cell |
u64 | For central-side indexing. |
class_group |
string | Class group of the declined detection. |
decline_time |
timestamp | Wall-clock at decline (operator-side). |
operator_id |
string | null | If known from the Ground Station session. |
mission_id |
string | The mission during which the decline happened. |
retention_scope |
enum | mission (cleared at mission end on-device, retained centrally indexed by mission) | session (cleared at session end on-device) | until_expiry (carries expires_at). |
expires_at |
timestamp | null | Required when retention_scope = until_expiry. |
source |
enum | central_pulled (pre-flight pull) | local_appended (during this mission). Only local_appended is uploaded to central in the post-flight push. |
pending_upload |
bool | True for any local_appended entry not yet pushed centrally. |
Lookup key: (MGRS, class_group) exact match (subject to the same H3 k-ring widening as MapObject lookups, when configured).
Persisted in mapobjects_store. Central-side table: map_object_ignored per architecture.md §7.13.
4. Action / piloting entities
Coordinate
| Field | Type | Notes |
|---|---|---|
latitude |
f64 | Geographic; degrees. |
longitude |
f64 | Geographic; degrees. |
altitude_m |
f32 | Above ground or above home, depending on usage; the carrying entity defines the frame. |
Geofence
A polygon on the mission. Both INCLUSION and EXCLUSION are honoured by mission_executor.
| Field | Type | Notes |
|---|---|---|
kind |
enum | INCLUSION | EXCLUSION. |
vertices |
Vec<Coordinate> |
Polygon vertices in order. |
MissionItem
The business-level mission item: what the missions API delivers and what the operator authored. Owned by mission-schema, the artefact shared with the missions repo (extraction location TBD — architecture.md §8 Q5).
| Field | Type | Notes |
|---|---|---|
id |
uuid | |
kind |
enum | waypoint | search | region_search | return | target_follow_breakpoint. |
at |
optional Coordinate |
For waypoint / return. |
region |
optional polygon | For region_search. |
cruise_speed_mps |
optional f32 | If set, mission_executor emits a MAV_CMD_DO_CHANGE_SPEED waypoint before the affected items. |
target_classes |
optional Vec<string> |
Per-item search hint (e.g. tank, artillery). |
MissionWaypoint
The MAVLink-level wire item: what mavlink_layer sends to ArduPilot / PX4. Owned by mavlink_layer.
| Field | Type | Notes |
|---|---|---|
seq |
u16 | MAVLink mission item sequence number. |
frame |
enum | MAV_FRAME_GLOBAL_RELATIVE_ALT (system default; no terrain-following). |
command |
enum | One of: MAV_CMD_NAV_TAKEOFF, MAV_CMD_NAV_WAYPOINT, MAV_CMD_NAV_LAND, MAV_CMD_DO_CHANGE_SPEED, MAV_CMD_NAV_RETURN_TO_LAUNCH, MAV_CMD_DO_SET_MODE. |
current |
bool | True only for the very first item in a fresh upload. |
auto_continue |
bool | True for everything except the final item. |
param_1..param_4 |
f32 | Command-specific. |
lat_deg_e7, lon_deg_e7 |
i32 | Scaled-integer geographic coordinates. |
alt_m |
f32 | Above home (relative). |
Translation contract — MissionItem → MissionWaypoint
Owner: mission_executor, variant-aware (multirotor / fixed-wing).
Source MissionItem.kind |
Resulting MissionWaypoint(s) |
|---|---|
waypoint |
exactly one MAV_CMD_NAV_WAYPOINT |
region_search |
sequence of MAV_CMD_NAV_WAYPOINTs computed per the sweep pattern (architecture.md §8 Q1) |
return |
one MAV_CMD_NAV_RETURN_TO_LAUNCH (or MAV_CMD_NAV_LAND at the explicit return point) |
target_follow_breakpoint |
(none) — used only as a structural marker for re-upload; not sent to MAVLink |
(cruise speed carried by a MissionItem) |
one MAV_CMD_DO_CHANGE_SPEED placed before the affected MAV_CMD_NAV_WAYPOINTs |
Multirotor variants prepend MAV_CMD_NAV_TAKEOFF and append MAV_CMD_NAV_LAND. Fixed-wing variants do neither (the airframe is RC-launched and put into AUTO by the operator); they only upload + start the mission.
The cruise-speed translation is required to reach the autopilot. If a MissionItem declares a cruise speed, the corresponding MAV_CMD_DO_CHANGE_SPEED MUST be present in the uploaded sequence with the speed in param_1. Conformance test in deployment/ci_cd_pipeline.md §5.
OperatorCommand
Every command from the Ground Station to autopilot is wrapped in this authenticated envelope. The principle is committed (architecture.md §5); the exact signature scheme is open per Q9. operator_bridge rejects any command that fails signature validation, replay-protection check, or session validation.
| Field | Type | Notes |
|---|---|---|
command_id |
uuid | Idempotency key; cached for 60 s by operator_bridge. |
session_token |
string | Opaque session token issued by the Ground Station at operator login; bound to operator_id. |
sequence_number |
u64 | Monotonically increasing per-session; replay-protection. Lower-or-equal numbers per session are rejected. |
issued_at_wallclock |
timestamp | Operator-side wall-clock. Used for forensic audit; not used for trust decisions. |
kind |
enum | confirm_poi | decline_poi | start_target_follow | release_target_follow | acknowledge_bit_degraded | safety_override | mission_abort. |
payload |
json | Action-specific body. |
signature |
bytes | Signature over (session_token, sequence_number, kind, payload). Scheme TBD per Q9. |
scan_controller and mission_executor see only the validated payload; the auth envelope is opaque to them. Audit logs record command_id, operator_id (resolved from session token), kind, and result.
GimbalState
| Field | Type | Notes |
|---|---|---|
yaw, pitch |
f32 | Degrees. |
zoom |
f32 | Effective focal length or zoom factor (vendor-specific). |
ts_monotonic_ns |
u64 | Stamp at the moment the gimbal feedback was received. |
command_in_flight |
bool | True between command issuance and feedback that motion completed. |
In-memory only; consumed by frame_ingest and movement_detector for telemetry-skew compensation.
5. Sync / wire formats
MGRS sync message — wire format
The operator round trip (telemetry_stream ⇄ Ground Station) uses MGRS-encoded payloads in both directions. Field separator is ::.
Drone → Operator (detection report):
| Position | Field | Type | Notes |
|---|---|---|---|
| 1 | missionId |
string | Server-assigned mission UUID. |
| 2 | MGRS(encoded) |
string | MGRS coordinate (compact, military-grid). |
| 3 | class |
string | Concrete detection class. |
| 4 | confidence |
f32 | 0.0–1.0. |
| 5 | size_width_m |
f32 | Ground-projected width. |
| 6 | size_length_m |
f32 | Ground-projected length. |
| 7 | photo_metadata |
string | URL or compact reference to the snapshot frame. |
| 8 | flags |
bitmask | Reserved (e.g. target_follow_active, vlm_used, movement_origin). |
Operator → Drone (command / acknowledgment):
| Position | Field | Type | Notes |
|---|---|---|---|
| 1 | missionId |
string | Must match the drone-side mission. |
| 2 | Encoded(GroundMGRS :: Time) |
string | Operator's ground location + decision timestamp. |
| 3 | (variable) | … | Action-specific payload (POI ID, action enum, follow-toggle, etc.). |
| N | missionId2 |
string | Echo of missionId for stream-multiplexing safety. |
The exact serialisation of position 3 (action payload) is left to the Ground Station API contract (open question; see architecture.md §8 Q2).
6. Persistence and lifecycle
| Entity | Persisted? | Where | Lifecycle |
|---|---|---|---|
Frame, Detection, DetectionBatch, MovementCandidate, Tier2Evidence, VlmAssessment, GimbalState |
no | in-memory | per frame / per ROI / per command — dropped on state change. |
POI |
no | in-memory inside scan_controller |
enqueued, surfaced, decided (confirm / decline / timeout), then dropped. |
MapObject |
yes | mapobjects_store (working copy of central state) |
mission-scoped on-device; appended to central observation log via post-flight push (F8); cleared on DELETE /missions/{id} cascade. |
MapObjectObservation |
yes | mapobjects_store until acknowledged centrally |
per-detection append-log; durable across in-flight crash; cleared per record on POST /missions/{id}/mapobjects ack. |
IgnoredItem |
yes | mapobjects_store (working copy + post-flight upload of locally-appended items) |
per retention_scope; central side union-merged. |
MissionItem |
no in autopilot | source of truth is the missions API |
pulled on start; refreshed on middle-waypoint POST. |
MissionWaypoint |
no | in-memory inside mavlink_layer |
re-derived from MissionItems on each upload / re-upload. |
OperatorCommand |
partial | command-id cache (60 s) for idempotency; full audit log persisted on disk | per-command; audit-retained per configured policy. |
7. Versioning and contracts
| Contract | Owner | Versioning |
|---|---|---|
mission-schema (the MissionItem shape) |
shared between autopilot and missions repos; extraction location TBD (architecture.md §8 Q5) |
semantic versioning; mission_client validates schema_version on fetch. |
MapObjects bundle schema (MapObjectsBundle for pull/push, MapObjectObservation for the central observation log) |
shared between autopilot and missions repos as part of the §7.13 endpoint extension |
semantic versioning; mission_client validates schema_version; central side rejects mismatches with 4xx (architecture.md §8 Q7). |
../detections gRPC contract |
../detections repo (per ../_docs/03_detections.md) |
versioned; detection_client rejects schema mismatches (architecture.md §8 Q4). |
VlmAssessment schema |
autopilot-internal (this document is the source of truth) | versioned; vlm_client rejects schema-invalid responses. The model_version field correlates assessments with VLM weights. |
| MGRS sync wire format | autopilot-internal (this document is the source of truth) | versioned; field-position changes are breaking. |
| MAVLink command surface | per architecture.md §7.7 |
adding messages requires explicit design review. |
OperatorCommand envelope (signature scheme) |
open per architecture.md §8 Q9 |
once chosen, versioned; both Ground Station and operator_bridge must agree. |