mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-04-22 22:56:36 +00:00
492 lines
34 KiB
Markdown
492 lines
34 KiB
Markdown
# Solution Draft
|
||
|
||
## Assessment Findings
|
||
|
||
| Old Component Solution | Weak Point (functional/security/performance) | New Solution |
|
||
|------------------------|----------------------------------------------|-------------|
|
||
| FastAPI + SSE as primary output | **Functional**: New AC requires MAVLink GPS_INPUT to flight controller, not REST/SSE. The system must act as a GPS replacement module. SSE is wrong output channel. | **Replace with pymavlink GPS_INPUT sender**. Send GPS_INPUT at 5-10Hz to flight controller via UART. Retain minimal FastAPI only for local IPC (object localization API). |
|
||
| No ground station integration | **Functional**: New AC requires streaming position+confidence to ground station and receiving re-localization commands via telemetry. Draft02 had no telemetry. | **MAVLink telemetry integration**: GPS data forwarded automatically by flight controller. Custom data via NAMED_VALUE_FLOAT (confidence, drift). Re-localization hints via COMMAND_LONG listener. |
|
||
| MAVSDK library (per restriction) | **Functional**: MAVSDK-Python v3.15.3 cannot send GPS_INPUT messages. Feature requested since 2021, still unresolved. This is a blocking limitation for the core output function. | **Use pymavlink** for all MAVLink communication. pymavlink provides `gps_input_send()` and full MAVLink v2 access. Note conflict with restriction — pymavlink is the only viable option. |
|
||
| 3fps camera → ~3Hz output | **Performance**: ArduPilot GPS_RATE_MS minimum is 5Hz (200ms). 3Hz camera output is below minimum. Flight controller EKF may not fuse properly. | **IMU-interpolated 5-10Hz GPS_INPUT**: ESKF prediction runs at 100+Hz internally. Emit predicted state as GPS_INPUT at 5-10Hz. Camera corrections arrive at 3Hz within this stream. |
|
||
| No startup/failsafe procedures | **Functional**: New AC requires init from last GPS, reboot recovery, IMU-only fallback. Draft02 assumed position was already known. | **Full lifecycle management**: (1) Boot → read GPS from flight controller → init ESKF. (2) Reboot → read IMU-extrapolated position → re-init. (3) N-second failure → stop GPS_INPUT → autopilot falls back to IMU. |
|
||
| Basic object localization (nadir only) | **Functional**: New AC adds AI camera with configurable angle and zoom. Nadir pixel-to-GPS is insufficient. | **Trigonometric projection for oblique camera**: ground_distance = alt × tan(tilt), bearing = heading + pan + pixel offset. Local API for AI system requests. |
|
||
| No thermal management | **Performance**: Jetson Orin Nano Super throttles at 80°C (GPU drops 1GHz→300MHz = 3x slowdown). Could blow 400ms budget. | **Thermal monitoring + adaptive pipeline**: Use 25W mode. Monitor via tegrastats. If temp >75°C → reduce satellite matching frequency. If >80°C → VO+IMU only. |
|
||
| ESKF covariance without explicit drift budget | **Functional**: New AC requires max 100m cumulative VO drift between satellite anchors. Draft02 uses covariance for keyframe selection but no explicit budget. | **Drift budget tracker**: √(σ_x² + σ_y²) from ESKF as drift estimate. When approaching 100m → force every-frame satellite matching. Report via horiz_accuracy in GPS_INPUT. |
|
||
| No satellite imagery validation | **Functional**: New AC requires ≥0.5 m/pixel, <2 years old. Draft02 didn't validate. | **Preprocessing validation step**: Check zoom 19 availability (0.3 m/pixel). Fall back to zoom 18 (0.6 m/pixel). Flag stale tiles. |
|
||
| "Ask user via API" for re-localization | **Functional**: New AC says send re-localization request to ground station via telemetry link, not REST API. Operator sends hint via telemetry. | **MAVLink re-localization protocol**: On 3 consecutive failures → send STATUSTEXT alert to ground station. Operator sends COMMAND_LONG with approximate lat/lon. System uses hint to constrain tile search. |
|
||
|
||
## Product Solution Description
|
||
|
||
A real-time GPS-denied visual navigation system for fixed-wing UAVs, running on a Jetson Orin Nano Super (8GB). The system replaces the GPS module for the flight controller by sending MAVLink GPS_INPUT messages via pymavlink over UART. Position is determined by fusing: (1) CUDA-accelerated visual odometry (cuVSLAM), (2) absolute position corrections from satellite image matching, and (3) IMU data from the flight controller. GPS_INPUT is sent at 5-10Hz, with camera-based corrections at 3Hz and IMU prediction filling the gaps.
|
||
|
||
**Hard constraint**: Camera shoots at ~3fps (333ms interval). The full VO+ESKF pipeline must complete within 400ms per frame. GPS_INPUT output rate: 5-10Hz minimum (ArduPilot EKF requirement).
|
||
|
||
**Output architecture**:
|
||
- **Primary**: pymavlink → GPS_INPUT to flight controller via UART (replaces GPS module)
|
||
- **Telemetry**: Flight controller auto-forwards GPS data to ground station. Custom NAMED_VALUE_FLOAT for confidence/drift at 1Hz
|
||
- **Commands**: Ground station → COMMAND_LONG → flight controller → pymavlink listener on companion computer
|
||
- **Local IPC**: Minimal FastAPI on localhost for object localization requests from AI systems
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────────┐
|
||
│ OFFLINE (Before Flight) │
|
||
│ Satellite Tiles → Download & Validate → Pre-resize → Store │
|
||
│ (Google Maps) (≥0.5m/px, <2yr) (matcher res) (GeoHash) │
|
||
│ Copy to Jetson storage │
|
||
└─────────────────────────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────────────┐
|
||
│ ONLINE (During Flight) │
|
||
│ │
|
||
│ STARTUP: │
|
||
│ pymavlink → read GLOBAL_POSITION_INT → init ESKF → start cuVSLAM │
|
||
│ │
|
||
│ EVERY FRAME (3fps, 333ms interval): │
|
||
│ ┌──────────────────────────────────────┐ │
|
||
│ │ Nav Camera → Downsample (CUDA ~2ms) │ │
|
||
│ │ → cuVSLAM VO+IMU (~9ms) │ │
|
||
│ │ → ESKF measurement update │ │
|
||
│ └──────────────────────────────────────┘ │
|
||
│ │
|
||
│ 5-10Hz CONTINUOUS (between camera frames): │
|
||
│ ┌──────────────────────────────────────┐ │
|
||
│ │ ESKF IMU prediction → GPS_INPUT send │──→ Flight Controller │
|
||
│ │ (pymavlink, every 100-200ms) │ (GPS1_TYPE=14) │
|
||
│ └──────────────────────────────────────┘ │
|
||
│ │
|
||
│ KEYFRAMES (every 3-10 frames, async): │
|
||
│ ┌──────────────────────────────────────┐ │
|
||
│ │ Satellite match (CUDA stream B) │──→ ESKF correction │
|
||
│ │ LiteSAM TRT FP16 or XFeat │ │
|
||
│ └──────────────────────────────────────┘ │
|
||
│ │
|
||
│ TELEMETRY (1Hz): │
|
||
│ ┌──────────────────────────────────────┐ │
|
||
│ │ NAMED_VALUE_FLOAT: confidence, drift │──→ Ground Station │
|
||
│ │ STATUSTEXT: alerts, re-loc requests │ (via telemetry radio) │
|
||
│ └──────────────────────────────────────┘ │
|
||
│ │
|
||
│ COMMANDS (from ground station): │
|
||
│ ┌──────────────────────────────────────┐ │
|
||
│ │ Listen COMMAND_LONG: re-loc hint │←── Ground Station │
|
||
│ │ (lat/lon from operator) │ (via telemetry radio) │
|
||
│ └──────────────────────────────────────┘ │
|
||
│ │
|
||
│ LOCAL IPC: │
|
||
│ ┌──────────────────────────────────────┐ │
|
||
│ │ FastAPI localhost:8000 │←── AI Detection System │
|
||
│ │ POST /localize (object GPS calc) │ │
|
||
│ │ GET /status (system health) │ │
|
||
│ └──────────────────────────────────────┘ │
|
||
│ │
|
||
│ IMU: 100+Hz from flight controller → ESKF prediction │
|
||
│ TILES: ±2km preloaded in RAM from flight plan │
|
||
│ THERMAL: Monitor via tegrastats, adaptive pipeline throttling │
|
||
└─────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
## Speed Optimization Techniques
|
||
|
||
### 1. cuVSLAM for Visual Odometry (~9ms/frame)
|
||
NVIDIA's CUDA-accelerated VO library (PyCuVSLAM v15.0.0, March 2026) achieves 116fps on Jetson Orin Nano 8GB at 720p. Supports monocular camera + IMU natively. Auto-fallback to IMU when visual tracking fails, loop closure, Python and C++ APIs.
|
||
|
||
**CRITICAL: cuVSLAM on low-texture terrain (agricultural fields, water)**:
|
||
cuVSLAM uses Shi-Tomasi corners + Lucas-Kanade optical flow (classical features). On uniform agricultural terrain:
|
||
- Few corners detected → sparse/unreliable tracking
|
||
- Frequent keyframe creation → heavier compute
|
||
- Tracking loss → IMU fallback (~1s) → constant-velocity integrator (~0.5s)
|
||
- cuVSLAM does NOT guarantee pose recovery after tracking loss
|
||
|
||
**Mitigation**:
|
||
1. Increase satellite matching frequency when cuVSLAM keypoint count drops
|
||
2. IMU dead-reckoning bridge via ESKF (continues GPS_INPUT output during tracking loss)
|
||
3. Accept higher drift in featureless segments — report via horiz_accuracy
|
||
4. Keypoint density monitoring triggers adaptive satellite matching
|
||
|
||
### 2. Keyframe-Based Satellite Matching
|
||
Not every frame needs satellite matching:
|
||
- cuVSLAM provides VO at every frame (~9ms)
|
||
- Satellite matching triggers on keyframes selected by:
|
||
- Fixed interval: every 3-10 frames
|
||
- ESKF covariance exceeds threshold (drift approaching budget)
|
||
- VO failure: cuVSLAM reports tracking loss
|
||
- Thermal: reduce frequency if temperature high
|
||
|
||
### 3. Satellite Matcher Selection (Benchmark-Driven)
|
||
|
||
**Context**: Our UAV-to-satellite matching is nadir-to-nadir (both top-down). Challenges are season/lighting differences and temporal changes, not extreme viewpoint gaps.
|
||
|
||
**Candidate A: LiteSAM (opt) TRT FP16 @ 1280px** — Best satellite-aerial accuracy (RMSE@30 = 17.86m on UAV-VisLoc). 6.31M params. TensorRT FP16 with reparameterized MobileOne. Estimated ~165-330ms on Orin Nano Super with TRT FP16.
|
||
|
||
**Candidate B: XFeat semi-dense** — ~50-100ms on Orin Nano Super. Fastest option. General-purpose but our nadir-nadir gap is small.
|
||
|
||
**Decision rule** (day-one on Orin Nano Super):
|
||
1. Export LiteSAM (opt) to TensorRT FP16
|
||
2. Benchmark at 1280px
|
||
3. If ≤200ms → LiteSAM at 1280px
|
||
4. If >200ms → XFeat
|
||
|
||
### 4. TensorRT FP16 Optimization
|
||
LiteSAM's MobileOne backbone is reparameterizable — multi-branch collapses to single feed-forward at inference. INT8 safe only for MobileOne CNN layers, NOT for TAIFormer transformer components.
|
||
|
||
### 5. CUDA Stream Pipelining
|
||
- Stream A: cuVSLAM VO for current frame (~9ms) + ESKF fusion (~1ms)
|
||
- Stream B: Satellite matching for previous keyframe (async, does not block VO)
|
||
- CPU: GPS_INPUT output loop, NAMED_VALUE_FLOAT, command listener, tile management
|
||
|
||
### 6. Proactive Tile Loading
|
||
Preload tiles within ±2km of flight plan into RAM at startup. For a 50km route, ~2000 tiles at zoom 19 ≈ ~200MB. Eliminates disk I/O during flight.
|
||
|
||
On VO failure / expanded search:
|
||
1. Compute IMU dead-reckoning position
|
||
2. Rank preloaded tiles by distance to predicted position
|
||
3. Try top 3 tiles, then expand
|
||
|
||
### 7. 5-10Hz GPS_INPUT Output Loop
|
||
Dedicated thread/coroutine sends GPS_INPUT at fixed rate (5-10Hz):
|
||
1. Read current ESKF state (position, velocity, covariance)
|
||
2. Compute horiz_accuracy from √(σ_x² + σ_y²)
|
||
3. Set fix_type based on last correction type (3=satellite-corrected, 2=VO-only, 1=IMU-only)
|
||
4. Send via `mav.gps_input_send()`
|
||
5. Sleep until next interval
|
||
|
||
This decouples camera frame rate (3fps) from GPS_INPUT rate (5-10Hz).
|
||
|
||
## Existing/Competitor Solutions Analysis
|
||
|
||
| Solution | Approach | Accuracy | Hardware | Limitations |
|
||
|----------|----------|----------|----------|-------------|
|
||
| Mateos-Ramirez et al. (2024) | VO (ORB) + satellite keypoint correction + Kalman | 142m mean / 17km (0.83%) | Orange Pi class | No re-localization; ORB only; 1000m+ altitude |
|
||
| SatLoc (2025) | DinoV2 + XFeat + optical flow + adaptive fusion | <15m, >90% coverage | Edge (unspecified) | Paper not fully accessible |
|
||
| LiteSAM (2025) | MobileOne + TAIFormer + MinGRU subpixel refinement | RMSE@30 = 17.86m on UAV-VisLoc | RTX 3090 (62ms), AGX Orin (497ms@1184px) | Not tested on Orin Nano |
|
||
| cuVSLAM (NVIDIA, 2025-2026) | CUDA-accelerated VO+SLAM, mono/stereo/IMU | <1% trajectory error (KITTI) | Jetson Orin Nano (116fps) | VO only, no satellite matching |
|
||
| EfficientLoFTR (CVPR 2024) | Aggregated attention + adaptive token selection | Competitive with LiteSAM | TRT available | 15.05M params, heavier |
|
||
| STHN (IEEE RA-L 2024) | Deep homography estimation | 4.24m at 50m range | Lightweight | Needs RGB retraining |
|
||
| JointLoc (IROS 2024) | Retrieval + VO fusion, adaptive weighting | 0.237m RMSE over 1km | Open-source | Planetary, needs adaptation |
|
||
|
||
## Architecture
|
||
|
||
### Component: Flight Controller Integration (NEW)
|
||
|
||
| Solution | Tools | Advantages | Limitations | Performance | Fit |
|
||
|----------|-------|-----------|-------------|------------|-----|
|
||
| pymavlink GPS_INPUT | pymavlink | Full MAVLink v2 access, GPS_INPUT support, pure Python, aarch64 compatible | Lower-level API, manual message handling | ~1ms per send | ✅ Best |
|
||
| MAVSDK-Python TelemetryServer | MAVSDK v3.15.3 | Higher-level API, aarch64 wheels | NO GPS_INPUT support, no custom messages | N/A — missing feature | ❌ Blocked |
|
||
| MAVSDK C++ MavlinkDirect | MAVSDK v4 (future) | Custom message support planned | Not available in Python wrapper yet | N/A — not released | ❌ Not available |
|
||
| MAVROS (ROS) | ROS + MAVROS | Full GPS_INPUT support, ROS ecosystem | Heavy ROS dependency, complex setup, unnecessary overhead | ~5ms overhead | ⚠️ Overkill |
|
||
|
||
**Selected**: **pymavlink** — only viable Python library for GPS_INPUT. Pure Python, works on aarch64, full MAVLink v2 message set.
|
||
|
||
**Restriction note**: restrictions.md specifies "MAVSDK library" but MAVSDK-Python cannot send GPS_INPUT (confirmed: Issue #320, open since 2021). pymavlink is the necessary alternative.
|
||
|
||
Configuration:
|
||
- Connection: UART (`/dev/ttyTHS0` or `/dev/ttyTHS1` on Jetson, 115200-921600 baud)
|
||
- Flight controller: GPS1_TYPE=14, SERIAL2_PROTOCOL=2 (MAVLink2)
|
||
- GPS_INPUT rate: 5-10Hz (dedicated output thread)
|
||
- Heartbeat: 1Hz to maintain connection
|
||
|
||
### Component: Visual Odometry
|
||
|
||
| Solution | Tools | Advantages | Limitations | Performance | Fit |
|
||
|----------|-------|-----------|-------------|------------|-----|
|
||
| cuVSLAM (mono+IMU) | PyCuVSLAM v15.0.0 | 116fps on Orin Nano, NVIDIA-optimized, loop closure, IMU fallback | Closed-source, low-texture terrain risk | ~9ms/frame | ✅ Best |
|
||
| XFeat frame-to-frame | XFeatTensorRT | Open-source, learned features | No IMU integration, ~30-50ms | ~30-50ms/frame | ⚠️ Fallback |
|
||
| ORB-SLAM3 | OpenCV + custom | Well-understood, open-source | CPU-heavy, ~30fps | ~33ms/frame | ⚠️ Slower |
|
||
|
||
**Selected**: **cuVSLAM (mono+IMU mode)** — 116fps, purpose-built for Jetson.
|
||
|
||
### Component: Satellite Image Matching
|
||
|
||
| Solution | Tools | Advantages | Limitations | Performance | Fit |
|
||
|----------|-------|-----------|-------------|------------|-----|
|
||
| LiteSAM (opt) TRT FP16 @ 1280px | TensorRT | Best satellite-aerial accuracy, 6.31M params | Untested on Orin Nano Super TRT | Est. ~165-330ms TRT FP16 | ✅ If ≤200ms |
|
||
| XFeat semi-dense | XFeatTensorRT | ~50-100ms, Jetson-proven, fastest | General-purpose | ~50-100ms | ✅ Fallback |
|
||
|
||
**Selection**: Day-one benchmark. LiteSAM TRT FP16 at 1280px → if ≤200ms → LiteSAM. If >200ms → XFeat.
|
||
|
||
### Component: Sensor Fusion
|
||
|
||
| Solution | Tools | Advantages | Limitations | Performance | Fit |
|
||
|----------|-------|-----------|-------------|------------|-----|
|
||
| ESKF (custom) | Python/C++ | Lightweight, multi-rate, well-understood | Linear approximation | <1ms/step | ✅ Best |
|
||
| Hybrid ESKF/UKF | Custom | 49% better accuracy | More complex | ~2-3ms/step | ⚠️ Upgrade path |
|
||
|
||
**Selected**: **ESKF** with adaptive measurement noise. State vector: [position(3), velocity(3), orientation_quat(4), accel_bias(3), gyro_bias(3)] = 16 states.
|
||
|
||
**Output rates**:
|
||
- IMU prediction: 100+Hz (from flight controller IMU via pymavlink)
|
||
- cuVSLAM VO update: ~3Hz
|
||
- Satellite update: ~0.3-1Hz (keyframes, async)
|
||
- GPS_INPUT output: 5-10Hz (ESKF predicted state)
|
||
|
||
**Drift budget**: Track √(σ_x² + σ_y²) from ESKF covariance. When approaching 100m → force every-frame satellite matching.
|
||
|
||
### Component: Ground Station Telemetry (NEW)
|
||
|
||
| Solution | Tools | Advantages | Limitations | Performance | Fit |
|
||
|----------|-------|-----------|-------------|------------|-----|
|
||
| MAVLink auto-forwarding + NAMED_VALUE_FLOAT | pymavlink | Standard MAVLink, no custom protocol, works with all GCS (Mission Planner, QGC) | Limited bandwidth (~12kbit/s), NAMED_VALUE_FLOAT name limited to 10 chars | ~50 bytes/msg | ✅ Best |
|
||
| Custom MAVLink dialect messages | pymavlink + custom XML | Full flexibility | Requires custom GCS plugin, non-standard | ~50 bytes/msg | ⚠️ Complex |
|
||
| Separate telemetry channel | TCP/UDP over separate radio | Full bandwidth | Extra hardware, extra radio | N/A | ❌ Not available |
|
||
|
||
**Selected**: **Standard MAVLink forwarding + NAMED_VALUE_FLOAT**
|
||
|
||
Telemetry data sent to ground station:
|
||
- GPS position: auto-forwarded by flight controller from GPS_INPUT data
|
||
- Confidence score: NAMED_VALUE_FLOAT `"gps_conf"` at 1Hz (values: 1=HIGH, 2=MEDIUM, 3=LOW, 4=VERY_LOW)
|
||
- Drift estimate: NAMED_VALUE_FLOAT `"gps_drift"` at 1Hz (meters)
|
||
- Matching status: NAMED_VALUE_FLOAT `"sat_match"` at 1Hz (0=inactive, 1=matching, 2=failed)
|
||
- Alerts: STATUSTEXT for critical events (re-localization request, system failure)
|
||
|
||
Re-localization from ground station:
|
||
- Operator sees drift/failure alert in GCS
|
||
- Sends COMMAND_LONG (MAV_CMD_USER_1) with lat/lon in param5/param6
|
||
- Companion computer listens for COMMAND_LONG with target component ID
|
||
- Receives hint → constrains tile search → attempts satellite matching near hint coordinates
|
||
|
||
### Component: Startup & Lifecycle (NEW)
|
||
|
||
**Startup sequence**:
|
||
1. Boot Jetson → start GPS-Denied service (systemd)
|
||
2. Connect to flight controller via pymavlink on UART
|
||
3. Wait for heartbeat from flight controller
|
||
4. Read GLOBAL_POSITION_INT → extract lat, lon, alt
|
||
5. Initialize ESKF state with this position (high confidence if real GPS available)
|
||
6. Start cuVSLAM with first camera frames
|
||
7. Begin GPS_INPUT output loop at 5-10Hz
|
||
8. Preload satellite tiles within ±2km of flight plan into RAM
|
||
9. System ready — GPS-Denied active
|
||
|
||
**GPS denial detection**:
|
||
Not required — the system always outputs GPS_INPUT. If real GPS is available, the flight controller uses whichever GPS source has better accuracy (configurable GPS blending or priority). When real GPS degrades/lost, flight controller seamlessly uses our GPS_INPUT.
|
||
|
||
**Failsafe**:
|
||
- If no valid position estimate for N seconds (configurable, e.g., 10s): stop sending GPS_INPUT
|
||
- Flight controller detects GPS timeout → falls back to IMU-only dead reckoning
|
||
- System logs failure, continues attempting recovery (VO + satellite matching)
|
||
- When recovery succeeds: resume GPS_INPUT output
|
||
|
||
**Reboot recovery**:
|
||
1. Jetson reboots → re-establish pymavlink connection
|
||
2. Read GPS_RAW_INT (now IMU-extrapolated by flight controller since GPS_INPUT stopped)
|
||
3. Initialize ESKF with this position (low confidence, horiz_accuracy=100m+)
|
||
4. Resume cuVSLAM + satellite matching → accuracy improves over time
|
||
5. Resume GPS_INPUT output
|
||
|
||
### Component: Object Localization (UPDATED)
|
||
|
||
**Two modes**:
|
||
|
||
**Mode 1: Navigation camera (nadir)**
|
||
Frame-center GPS from ESKF. Any object in navigation camera frame:
|
||
1. Pixel offset from center: (dx_px, dy_px)
|
||
2. Convert to meters: dx_m = dx_px × GSD, dy_m = dy_px × GSD
|
||
3. Rotate by heading (yaw from IMU)
|
||
4. Convert meter offset to lat/lon delta, add to frame-center GPS
|
||
|
||
**Mode 2: AI camera (configurable angle and zoom)**
|
||
1. Get current UAV position from ESKF
|
||
2. Get AI camera params: tilt_angle (from vertical), pan_angle (from heading), zoom (effective focal length)
|
||
3. Get pixel coordinates of detected object in AI camera frame
|
||
4. Compute bearing: bearing = heading + pan_angle + atan2(dx_px × sensor_width / focal_eff, focal_eff)
|
||
5. Compute ground distance: for flat terrain, slant_range = altitude / cos(tilt_angle + dy_angle), ground_range = slant_range × sin(tilt_angle + dy_angle)
|
||
6. Convert bearing + ground_range to lat/lon offset
|
||
7. Return GPS coordinates with accuracy estimate
|
||
|
||
**Local API** (FastAPI on localhost:8000):
|
||
- `POST /localize` — accepts: pixel_x, pixel_y, camera_id ("nav" or "ai"), ai_camera_params (tilt, pan, zoom) → returns: lat, lon, accuracy_m
|
||
- `GET /status` — returns: system state, confidence, drift, uptime
|
||
|
||
### Component: Satellite Tile Preprocessing (Offline)
|
||
|
||
**Selected**: GeoHash-indexed tile pairs on disk + RAM preloading.
|
||
|
||
Pipeline:
|
||
1. Define operational area from flight plan
|
||
2. Download satellite tiles from Google Maps Tile API at zoom 19 (0.3 m/pixel)
|
||
3. If zoom 19 unavailable: fall back to zoom 18 (0.6 m/pixel — meets ≥0.5 m/pixel requirement)
|
||
4. Validate: resolution ≥0.5 m/pixel, check imagery staleness where possible
|
||
5. Pre-resize each tile to matcher input resolution
|
||
6. Store: original + resized + metadata (GPS bounds, zoom, GSD, download date) in GeoHash-indexed structure
|
||
7. Copy to Jetson storage before flight
|
||
8. At startup: preload tiles within ±2km of flight plan into RAM
|
||
|
||
### Component: Re-localization (Disconnected Segments)
|
||
|
||
When cuVSLAM reports tracking loss (sharp turn, no features):
|
||
1. Flag next frame as keyframe → trigger satellite matching
|
||
2. Compute IMU dead-reckoning position since last known position
|
||
3. Rank preloaded tiles by distance to dead-reckoning position
|
||
4. Try top 3 tiles sequentially
|
||
5. If match found: position recovered, new segment begins
|
||
6. If 3 consecutive keyframe failures: send STATUSTEXT alert to ground station ("RE-LOC REQUEST: position uncertain, drift Xm")
|
||
7. While waiting for operator hint: continue VO/IMU dead reckoning, report low confidence via horiz_accuracy
|
||
8. If operator sends COMMAND_LONG with lat/lon hint: constrain tile search to ±500m of hint
|
||
9. If still no match after operator hint: continue dead reckoning, log failure
|
||
|
||
### Component: Thermal Management (NEW)
|
||
|
||
**Power mode**: 25W (stable sustained performance)
|
||
|
||
**Monitoring**: Read GPU/CPU temperature via tegrastats or sysfs thermal zones at 1Hz.
|
||
|
||
**Adaptive pipeline**:
|
||
- Normal (<70°C): Full pipeline — cuVSLAM every frame + satellite match every 3-10 frames
|
||
- Warm (70-75°C): Reduce satellite matching to every 5-10 frames
|
||
- Hot (75-80°C): Reduce satellite matching to every 10-15 frames
|
||
- Throttling (>80°C): Disable satellite matching entirely, VO+IMU only (cuVSLAM ~9ms is very light). Report LOW confidence. Resume satellite matching when temp drops below 75°C
|
||
|
||
**Hardware requirement**: Active cooling fan (5V) mandatory for UAV companion computer enclosure.
|
||
|
||
## Processing Time Budget (per frame, 333ms interval)
|
||
|
||
### Normal Frame (non-keyframe, ~60-80% of frames)
|
||
|
||
| Step | Time | Notes |
|
||
|------|------|-------|
|
||
| Image capture + transfer | ~10ms | CSI/USB3 |
|
||
| Downsample (for cuVSLAM) | ~2ms | OpenCV CUDA |
|
||
| cuVSLAM VO+IMU | ~9ms | NVIDIA CUDA-optimized, 116fps |
|
||
| ESKF measurement update | ~1ms | NumPy |
|
||
| **Total per camera frame** | **~22ms** | Well within 333ms |
|
||
|
||
GPS_INPUT output runs independently at 5-10Hz (every 100-200ms):
|
||
| Step | Time | Notes |
|
||
|------|------|-------|
|
||
| Read ESKF state | <0.1ms | Shared state |
|
||
| Compute horiz_accuracy | <0.1ms | √(σ²) |
|
||
| pymavlink gps_input_send | ~1ms | UART write |
|
||
| **Total per GPS_INPUT** | **~1ms** | Negligible overhead |
|
||
|
||
### Keyframe Satellite Matching (async, every 3-10 frames)
|
||
|
||
Runs on separate CUDA stream — does NOT block VO or GPS_INPUT.
|
||
|
||
**Path A — LiteSAM TRT FP16 at 1280px (if ≤200ms benchmark)**:
|
||
|
||
| Step | Time | Notes |
|
||
|------|------|-------|
|
||
| Downsample to 1280px | ~1ms | OpenCV CUDA |
|
||
| Load satellite tile | ~1ms | Pre-loaded in RAM |
|
||
| LiteSAM (opt) TRT FP16 | ≤200ms | Go/no-go threshold |
|
||
| Geometric pose (RANSAC) | ~5ms | Homography |
|
||
| ESKF satellite update | ~1ms | Delayed measurement |
|
||
| **Total** | **≤210ms** | Async |
|
||
|
||
**Path B — XFeat (if LiteSAM >200ms)**:
|
||
|
||
| Step | Time | Notes |
|
||
|------|------|-------|
|
||
| XFeat extraction + matching | ~50-80ms | TensorRT FP16 |
|
||
| Geometric verification (RANSAC) | ~5ms | |
|
||
| ESKF satellite update | ~1ms | |
|
||
| **Total** | **~60-90ms** | Async |
|
||
|
||
## Memory Budget (Jetson Orin Nano Super, 8GB shared)
|
||
|
||
| Component | Memory | Notes |
|
||
|-----------|--------|-------|
|
||
| OS + runtime | ~1.5GB | JetPack 6.2 + Python |
|
||
| cuVSLAM | ~200-500MB | CUDA library + map state (configure pruning for 3000 frames) |
|
||
| Satellite matcher TensorRT | ~50-100MB | LiteSAM FP16 or XFeat FP16 |
|
||
| Preloaded satellite tiles | ~200MB | ±2km of flight plan |
|
||
| pymavlink + MAVLink runtime | ~20MB | Lightweight |
|
||
| FastAPI (local IPC) | ~50MB | Minimal, localhost only |
|
||
| Current frame buffer | ~2MB | |
|
||
| ESKF state + buffers | ~10MB | |
|
||
| **Total** | **~2.1-2.9GB** | ~26-36% of 8GB — comfortable |
|
||
|
||
## Confidence Scoring → GPS_INPUT Mapping
|
||
|
||
| Level | Condition | horiz_accuracy (m) | fix_type | GPS_INPUT satellites_visible |
|
||
|-------|-----------|---------------------|----------|------------------------------|
|
||
| HIGH | Satellite match succeeded + cuVSLAM consistent | 10-20 | 3 (3D) | 12 |
|
||
| MEDIUM | cuVSLAM VO only, recent satellite correction (<500m travel) | 20-50 | 3 (3D) | 8 |
|
||
| LOW | cuVSLAM VO only, no recent correction, OR high thermal throttling | 50-100 | 2 (2D) | 4 |
|
||
| VERY LOW | IMU dead-reckoning only | 100-500 | 1 (no fix) | 1 |
|
||
| MANUAL | Operator-provided re-localization hint | 200 | 3 (3D) | 6 |
|
||
|
||
Note: `satellites_visible` is synthetic — used to influence EKF weighting. ArduPilot gives more weight to GPS with higher satellite count and lower horiz_accuracy.
|
||
|
||
## Key Risks and Mitigations
|
||
|
||
| Risk | Likelihood | Impact | Mitigation |
|
||
|------|-----------|--------|------------|
|
||
| **MAVSDK cannot send GPS_INPUT** | CONFIRMED | Must use pymavlink (conflicts with restriction) | Use pymavlink. Document restriction conflict. No alternative in Python. |
|
||
| **cuVSLAM fails on low-texture agricultural terrain** | HIGH | Frequent tracking loss, degraded VO | Increase satellite matching frequency. IMU dead-reckoning bridge. Accept higher drift. |
|
||
| **Jetson UART instability with ArduPilot** | MEDIUM | MAVLink connection drops | Test thoroughly. Use USB serial adapter if UART unreliable. Add watchdog reconnect. |
|
||
| **Thermal throttling blows satellite matching budget** | MEDIUM | Miss keyframe windows | Adaptive pipeline: reduce/skip satellite matching at high temp. Active cooling mandatory. |
|
||
| LiteSAM TRT FP16 >200ms at 1280px | MEDIUM | Must use XFeat | Day-one benchmark. XFeat fallback. |
|
||
| XFeat cross-view accuracy insufficient | MEDIUM | Satellite corrections less accurate | Multi-tile consensus, strict RANSAC, increase keyframe frequency. |
|
||
| cuVSLAM map memory growth on long flights | MEDIUM | Memory pressure | Configure map pruning, max keyframes. |
|
||
| Google Maps satellite quality in conflict zone | HIGH | Satellite matching fails | Accept VO+IMU with higher drift. Alternative providers. |
|
||
| GPS_INPUT at 3Hz too slow for ArduPilot EKF | HIGH | Poor EKF fusion, position jumps | 5-10Hz output with IMU interpolation between camera frames. |
|
||
| Companion computer reboot mid-flight | LOW | ~30-60s GPS gap | Flight controller IMU fallback. Automatic recovery on restart. |
|
||
| Telemetry bandwidth saturation | LOW | Custom messages compete with autopilot telemetry | Limit NAMED_VALUE_FLOAT to 1Hz. Keep messages compact. |
|
||
|
||
## Testing Strategy
|
||
|
||
### Integration / Functional Tests
|
||
- End-to-end: camera → cuVSLAM → ESKF → GPS_INPUT → verify flight controller receives valid position
|
||
- Compare computed positions against ground truth GPS from coordinates.csv
|
||
- Measure: percentage within 50m (target: 80%), percentage within 20m (target: 60%)
|
||
- Test GPS_INPUT rate: verify 5-10Hz output to flight controller
|
||
- Test sharp-turn handling: verify satellite re-localization after 90-degree heading change
|
||
- Test disconnected segments: simulate 3+ route breaks, verify all segments connected
|
||
- Test re-localization: simulate 3 consecutive failures → verify STATUSTEXT sent → inject COMMAND_LONG hint → verify recovery
|
||
- Test object localization: send POST /localize with known AI camera params → verify GPS accuracy
|
||
- Test startup: verify ESKF initializes from flight controller GPS
|
||
- Test reboot recovery: kill process → restart → verify reconnection and position recovery
|
||
- Test failsafe: simulate total failure → verify GPS_INPUT stops → verify flight controller IMU fallback
|
||
- Test cuVSLAM map memory: run 3000-frame session, monitor memory growth
|
||
|
||
### Non-Functional Tests
|
||
- **Day-one satellite matcher benchmark**: LiteSAM TRT FP16 at 1280px on Orin Nano Super
|
||
- cuVSLAM benchmark: verify 116fps monocular+IMU on Orin Nano Super
|
||
- cuVSLAM terrain stress test: urban, agricultural, water, forest
|
||
- **UART reliability test**: sustained pymavlink communication over 1+ hour
|
||
- **Thermal endurance test**: run full pipeline for 30+ minutes, measure GPU temp, verify no throttling with active cooling
|
||
- Per-frame latency: must be <400ms for VO pipeline
|
||
- GPS_INPUT latency: measure time from camera capture to GPS_INPUT send
|
||
- Memory: peak usage during 3000-frame session (must stay <8GB)
|
||
- Drift budget: verify ESKF covariance tracks cumulative drift, triggers satellite matching before 100m
|
||
- Telemetry bandwidth: measure total MAVLink bandwidth used by companion computer
|
||
|
||
## References
|
||
- pymavlink GPS_INPUT example: https://webperso.ensta.fr/lebars/Share/GPS_INPUT_pymavlink.py
|
||
- pymavlink mavgps.py: https://github.com/ArduPilot/pymavlink/blob/master/examples/mavgps.py
|
||
- ArduPilot GPS Input module: https://ardupilot.org/mavproxy/docs/modules/GPSInput.html
|
||
- MAVLink GPS_INPUT message spec: https://mavlink.io/en/messages/common.html#GPS_INPUT
|
||
- MAVSDK-Python GPS_INPUT limitation: https://github.com/mavlink/MAVSDK-Python/issues/320
|
||
- MAVSDK-Python custom message limitation: https://github.com/mavlink/MAVSDK-Python/issues/739
|
||
- ArduPilot companion computer setup: https://ardupilot.org/dev/docs/raspberry-pi-via-mavlink.html
|
||
- Jetson Orin UART with ArduPilot: https://forums.developer.nvidia.com/t/uart-connection-between-jetson-nano-orin-and-ardupilot/325416
|
||
- MAVLink NAMED_VALUE_FLOAT: https://mavlink.io/en/messages/common.html#NAMED_VALUE_FLOAT
|
||
- MAVLink STATUSTEXT: https://mavlink.io/en/messages/common.html#STATUSTEXT
|
||
- MAVLink telemetry bandwidth: https://github.com/mavlink/mavlink/issues/1605
|
||
- JetPack 6.2 Super Mode: https://developer.nvidia.com/blog/nvidia-jetpack-6-2-brings-super-mode-to-nvidia-jetson-orin-nano-and-jetson-orin-nx-modules/
|
||
- Jetson Orin Nano power consumption: https://edgeaistack.app/blog/jetson-orin-nano-power-consumption/
|
||
- UAV target geolocation: https://www.mdpi.com/1424-8220/22/5/1903
|
||
- LiteSAM (2025): https://www.mdpi.com/2072-4292/17/19/3349
|
||
- LiteSAM code: https://github.com/boyagesmile/LiteSAM
|
||
- cuVSLAM (2025-2026): https://github.com/NVlabs/PyCuVSLAM
|
||
- PyCuVSLAM API: https://nvlabs.github.io/PyCuVSLAM/api.html
|
||
- Intermodalics cuVSLAM benchmark: https://www.intermodalics.ai/blog/nvidia-isaac-ros-in-depth-cuvslam-and-the-dp3-1-release
|
||
- XFeat (CVPR 2024): https://arxiv.org/abs/2404.19174
|
||
- XFeat TensorRT for Jetson: https://github.com/PranavNedunghat/XFeatTensorRT
|
||
- EfficientLoFTR (CVPR 2024): https://github.com/zju3dv/EfficientLoFTR
|
||
- STHN (IEEE RA-L 2024): https://github.com/arplaboratory/STHN
|
||
- JointLoc (IROS 2024): https://github.com/LuoXubo/JointLoc
|
||
- Hybrid ESKF/UKF: https://arxiv.org/abs/2512.17505
|
||
- Google Maps Tile API: https://developers.google.com/maps/documentation/tile/satellite
|
||
- ArduPilot EKF Source Selection: https://ardupilot.org/copter/docs/common-ekf-sources.html
|
||
- Mateos-Ramirez et al. (2024): https://www.mdpi.com/2076-3417/14/16/7420
|
||
- SatLoc (2025): https://www.scilit.com/publications/e5cafaf875a49297a62b298a89d5572f
|
||
|
||
## Related Artifacts
|
||
- AC Assessment: `_docs/00_research/gps_denied_nav/00_ac_assessment.md`
|
||
- Research artifacts: `_docs/00_research/gps_denied_nav_v3/`
|
||
- Tech stack evaluation: `_docs/01_solution/tech_stack.md`
|
||
- Security analysis: `_docs/01_solution/security_analysis.md`
|