Files
gps-denied-onboard/README.md
T
Yuzviak 78dcf7b4e7 fix: post-audit — runtime bugs, functional gaps, docs, hardening
Phase A — Runtime bugs:
  - SSE: add push_event() method to SSEEventStreamer (was missing, masked by mocks)
  - MAVLink: satellites_visible=10 (was 0, triggers ArduPilot failsafe)
  - MAVLink: horiz_accuracy=sqrt(P[0,0]+P[1,1]) per spec (was sqrt(avg))
  - MAVLink: MEDIUM confidence → fix_type=3 per solution.md (was 2)

Phase B — Functional gaps:
  - handle_user_fix() injects operator GPS into ESKF with noise=500m
  - app.py uses create_vo_backend() factory (was hardcoded SequentialVO)
  - ESKF: Mahalanobis gating on satellite updates (rejects outliers >5σ)
  - ESKF: public accessors (position, quaternion, covariance, last_timestamp)
  - Processor: no more private ESKF field access

Phase C — Documentation:
  - README: correct API endpoints, CLI command, 40+ env vars documented
  - Dockerfile: ENV prefixes match pydantic-settings (DB_, SATELLITE_, MAVLINK_)
  - tech_stack.md marked ARCHIVED (contradicts solution.md)

Phase D — Hardening:
  - JWT auth middleware (AUTH_ENABLED=false default, verify_token on /flights)
  - TLS config env vars (AUTH_SSL_CERTFILE, AUTH_SSL_KEYFILE)
  - SHA-256 tile manifest verification in SatelliteDataManager
  - AuthConfig, ESKFSettings, MAVLinkConfig, SatelliteConfig in config.py

Also: conftest.py shared fixtures, download_tiles.py, convert_to_trt.py scripts,
config wiring into app.py lifespan, config-driven ESKF, calculate_precise_angle fix.

Tests: 196 passed / 8 skipped. Ruff clean.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 18:27:35 +03:00

11 KiB
Raw Blame History

GPS-Denied Onboard

Бортова система GPS-denied навігації для фіксованого крила БПЛА на Jetson Orin Nano Super.

Замінює GPS-сигнал власною оцінкою позиції на основі відеопотоку (cuVSLAM), IMU та супутникових знімків. Позиція подається у польотний контролер ArduPilot у форматі GPS_INPUT через MAVLink при 5–10 Гц.


Архітектура

IMU (MAVLink RAW_IMU) ──────────────────────────────────────────▶ ESKF.predict()
                                                                        │
ADTI 20L V1 ──▶ ImageInputPipeline ──▶ ImageRotationManager            │
                                              │                         │
                              ┌───────────────┼───────────────┐         │
                              ▼               ▼               ▼         │
                   cuVSLAM/ORB VO    GlobalPlaceRecog    SatelliteData  │
                       (F07)            (F08/Faiss)         (F04)       │
                              │               │               │         │
                              ▼               ▼               ▼         │
                         ESKF.update_vo()   GSD norm    MetricRefinement│
                              │                              (F09)       │
                              └──────────────────────▶ ESKF.update_sat()│
                                                                        │
                                                         ESKF state ◀──┘
                                                              │
                                              ┌───────────────┼──────────────┐
                                              ▼               ▼              ▼
                                      MAVLinkBridge    FactorGraph     SSE Stream
                                      GPS_INPUT 5-10Hz  (GTSAM ISAM2)  → Ground Station
                                      → ArduPilot FC

State Machine (process_frame):

NORMAL ──(VO fail)──▶ LOST ──▶ RECOVERY ──(GPR+Metric ok)──▶ NORMAL

Стек

Підсистема Dev/CI Jetson (production)
Visual Odometry ORBVisualOdometry (OpenCV) CuVSLAMVisualOdometry (PyCuVSLAM v15)
AI Inference MockInferenceEngine TRTInferenceEngine (TensorRT FP16)
Place Recognition numpy L2 fallback Faiss GPU index
MAVLink MockMAVConnection pymavlink over UART
ESKF numpy (15-state) numpy (15-state)
Factor Graph Mock poses GTSAM 4.3 ISAM2
API FastAPI + Pydantic v2 + SSE FastAPI + Pydantic v2 + SSE
БД SQLite + SQLAlchemy 2 async SQLite
Тести pytest + pytest-asyncio

Швидкий старт

Вимоги

  • Python ≥ 3.11
  • ~500 MB дискового простору (GTSAM wheel)

Встановлення

git clone https://github.com/azaion/gps-denied-onboard.git
cd gps-denied-onboard
git checkout stage1

python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"

Запуск

# Прямий запуск
uvicorn gps_denied.app:app --host 0.0.0.0 --port 8000

# Docker
docker compose up --build

Сервер: http://127.0.0.1:8000

Змінні середовища

# Основні
DB_URL=sqlite+aiosqlite:///./flight_data.db
SATELLITE_TILE_DIR=.satellite_tiles
MAVLINK_CONNECTION=serial:/dev/ttyTHS1:57600  # або tcp:host:port
MAVLINK_OUTPUT_HZ=5.0
MAVLINK_TELEMETRY_HZ=1.0

# ESKF тюнінг (опціонально)
ESKF_VO_POSITION_NOISE=0.3
ESKF_SATELLITE_MAX_AGE=30.0
ESKF_MAHALANOBIS_THRESHOLD=16.27

# API
API_HOST=127.0.0.1
API_PORT=8000

# Моделі
MODEL_WEIGHTS_DIR=weights

Повний список: src/gps_denied/config.py (40+ параметрів з prefix DB_, API_, TILES_, MODEL_, MAVLINK_, SATELLITE_, ESKF_, RECOVERY_, ROTATION_).


API

Endpoint Метод Опис
/health GET Health check
/flights POST Створити політ
/flights/{id} GET Деталі польоту
/flights/{flight_id} DELETE Видалити політ
/flights/{flight_id}/images/batch POST Батч зображень
/flights/{flight_id}/user-fix POST GPS-якір від оператора → ESKF update
/flights/{flight_id}/status GET Статус обробки
/flights/{flight_id}/stream GET SSE стрім (позиція + confidence)
/flights/{flight_id}/frames/{frame_id}/object-to-gps POST Pixel → GPS (ray-ground проекція)
/flights/{flight_id}/waypoints/{waypoint_id} PUT Оновити waypoint
/flights/{flight_id}/waypoints/batch PUT Batch update waypoints

Тести

# Всі тести
python -m pytest -q

# Конкретний модуль
python -m pytest tests/test_eskf.py -v
python -m pytest tests/test_mavlink.py -v
python -m pytest tests/test_accuracy.py -v

# SITL (потребує ArduPilot SITL)
docker compose -f docker-compose.sitl.yml up -d
ARDUPILOT_SITL_HOST=localhost pytest tests/test_sitl_integration.py -v

Покриття тестами (195 passed / 8 skipped)

Файл тесту Компонент К-сть
test_schemas.py Pydantic схеми 12
test_database.py SQLAlchemy CRUD 9
test_api_flights.py REST endpoints 5
test_health.py Health check 1
test_eskf.py ESKF 15-state 17
test_coordinates.py ENU/GPS/pixel 4
test_satellite.py Тайли + Mercator 8
test_pipeline.py Image queue 5
test_rotation.py 360° ротації 4
test_models.py Model Manager + TRT 6
test_vo.py VO (ORB + cuVSLAM) 8
test_gpr.py Place Recognition (Faiss) 7
test_metric.py Metric Refinement + GSD 6
test_graph.py Factor Graph (GTSAM) 4
test_chunk_manager.py Chunk lifecycle 3
test_recovery.py Recovery coordinator 2
test_processor_full.py State Machine 4
test_processor_pipe.py PIPE wiring (Phase 5) 13
test_mavlink.py MAVLink I/O bridge 19
test_acceptance.py AC сценарії + perf 6
test_accuracy.py Accuracy validation 23
test_sitl_integration.py SITL (skip без ArduPilot) 8
Всього 195+8

Benchmark валідації (Phase 7)

python scripts/benchmark_accuracy.py --frames 50

Результати на синтетичній траєкторії (20 м/с, 0.7 fps, шум VO 0.3 м, супутник кожні 5 кадрів):

Критерій Результат Ліміт
80% кадрів ≤ 50 м 100% ≥ 80%
60% кадрів ≤ 20 м 100% ≥ 60%
p95 затримка ~9 мс < 400 мс
VO дрейф за 1 км ~11 м < 100 м

Структура проєкту

gps-denied-onboard/
├── src/gps_denied/
│   ├── app.py                     # FastAPI factory + lifespan
│   ├── config.py                  # Pydantic Settings
│   ├── api/routers/flights.py     # REST + SSE endpoints
│   ├── core/
│   │   ├── eskf.py                # 15-state ESKF (IMU+VO+satellite fusion)
│   │   ├── processor.py           # FlightProcessor + process_frame
│   │   ├── vo.py                  # ORBVisualOdometry / CuVSLAMVisualOdometry
│   │   ├── mavlink.py             # MAVLinkBridge → GPS_INPUT → ArduPilot
│   │   ├── satellite.py           # SatelliteDataManager (local z/x/y tiles)
│   │   ├── gpr.py                 # GlobalPlaceRecognition (Faiss/numpy)
│   │   ├── metric.py              # MetricRefinement (LiteSAM/XFeat + GSD)
│   │   ├── graph.py               # FactorGraphOptimizer (GTSAM ISAM2)
│   │   ├── coordinates.py         # CoordinateTransformer (ENU↔GPS↔pixel)
│   │   ├── models.py              # ModelManager + TRTInferenceEngine
│   │   ├── benchmark.py           # AccuracyBenchmark + SyntheticTrajectory
│   │   ├── pipeline.py            # ImageInputPipeline
│   │   ├── rotation.py            # ImageRotationManager
│   │   ├── recovery.py            # FailureRecoveryCoordinator
│   │   └── chunk_manager.py       # RouteChunkManager
│   ├── schemas/                   # Pydantic схеми (eskf, mavlink, vo, ...)
│   ├── db/                        # SQLAlchemy ORM + async repository
│   └── utils/mercator.py          # Web Mercator tile utilities
├── tests/                         # 22 test модулі
├── scripts/
│   └── benchmark_accuracy.py      # CLI валідація точності
├── Dockerfile                     # Multi-stage Python 3.11 image
├── docker-compose.yml             # Local dev
├── docker-compose.sitl.yml        # ArduPilot SITL harness
├── .github/workflows/
│   ├── ci.yml                     # lint + pytest + docker smoke (кожен push)
│   └── sitl.yml                   # SITL integration (нічний / ручний)
└── pyproject.toml

Компоненти

ID Назва Файл Dev Jetson
F04 Satellite Data Manager core/satellite.py local tiles local tiles
F05 Image Input Pipeline core/pipeline.py
F06 Image Rotation Manager core/rotation.py
F07 Sequential Visual Odometry core/vo.py ORB cuVSLAM
F08 Global Place Recognition core/gpr.py numpy Faiss GPU
F09 Metric Refinement core/metric.py Mock LiteSAM/XFeat TRT
F10 Factor Graph Optimizer core/graph.py Mock GTSAM ISAM2
F11 Failure Recovery core/recovery.py
F12 Route Chunk Manager core/chunk_manager.py
F13 Coordinate Transformer core/coordinates.py
F16 Model Manager core/models.py Mock TRT engines
F17 ESKF Sensor Fusion core/eskf.py
F18 MAVLink I/O Bridge core/mavlink.py Mock pymavlink

Що залишилось (on-device)

  1. Офлайн завантаження тайлів для зони місії → {tile_dir}/z/x/y.png
  2. Конвертація моделей: LiteSAM/XFeat PyTorch → ONNX → TRT FP16
  3. Запуск SITL: docker compose -f docker-compose.sitl.yml up
  4. Польотні дані: записати GPS + відео → порівняти ESKF-траєкторію з ground truth
  5. Калібрування: camera intrinsics + IMU noise density для конкретного апарату

Ліцензія

Приватний репозиторій. Усі права захищено.