mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-04-23 03:26:38 +00:00
feat(phases 2-7): implement full GPS-denied navigation pipeline
Phase 2 — Visual Odometry: - ORBVisualOdometry (dev/CI), CuVSLAMVisualOdometry (Jetson) - TRTInferenceEngine (TensorRT FP16, conditional import) - create_vo_backend() factory Phase 3 — Satellite Matching + GPR: - SatelliteDataManager: local z/x/y tiles, ESKF ±3σ tile selection - GSD normalization (SAT-03), RANSAC inlier-ratio confidence (SAT-04) - GlobalPlaceRecognition: Faiss index + numpy fallback Phase 4 — MAVLink I/O: - MAVLinkBridge: GPS_INPUT 15+ fields, IMU callback, 1Hz telemetry - 3-consecutive-failure reloc request - MockMAVConnection for CI Phase 5 — Pipeline Wiring: - ESKF wired into process_frame: VO update → satellite update - CoordinateTransformer + SatelliteDataManager via DI - MAVLink state push per frame (PIPE-07) - Real pixel_to_gps via ray-ground projection (PIPE-06) - GTSAM ISAM2 update when available (PIPE-03) Phase 6 — Docker + CI: - Multi-stage Dockerfile (python:3.11-slim) - docker-compose.yml (dev), docker-compose.sitl.yml (ArduPilot SITL) - GitHub Actions: ci.yml (lint+pytest+docker smoke), sitl.yml (nightly) - tests/test_sitl_integration.py (8 tests, skip without SITL) Phase 7 — Accuracy Validation: - AccuracyBenchmark + SyntheticTrajectory - AC-PERF-1: 80% within 50m ✅ - AC-PERF-2: 60% within 20m ✅ - AC-PERF-3: p95 latency < 400ms ✅ - AC-PERF-4: VO drift 1km < 100m ✅ (actual ~11m) - scripts/benchmark_accuracy.py CLI Tests: 195 passed / 8 skipped Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,50 +1,57 @@
|
||||
# GPS-Denied Onboard
|
||||
|
||||
Сервіс геолокалізації знімків БПЛА в умовах відсутності GPS-сигналу.
|
||||
Бортова система GPS-denied навігації для фіксованого крила БПЛА на Jetson Orin Nano Super.
|
||||
|
||||
Система використовує візуальну одометрію (VO), співставлення з супутниковими картами (cross-view matching) та оптимізацію траєкторії через фактор-графи для визначення координат дрона в реальному часі.
|
||||
Замінює GPS-сигнал власною оцінкою позиції на основі відеопотоку (cuVSLAM), IMU та супутникових знімків. Позиція подається у польотний контролер ArduPilot у форматі `GPS_INPUT` через MAVLink при 5–10 Гц.
|
||||
|
||||
---
|
||||
|
||||
## Архітектура
|
||||
|
||||
```
|
||||
UAV Frames ──▷ ImageInputPipeline (F05) ──▷ ImageRotationManager (F06)
|
||||
│
|
||||
┌─────────────────────┼─────────────────────┐
|
||||
▼ ▼ ▼
|
||||
SequentialVO (F07) GlobalPlaceRecog (F08) SatelliteData (F04)
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
FactorGraphOptim (F10) ◂── MetricRefinement (F09) ◂── CoordTransform (F13)
|
||||
│
|
||||
┌─────────┴─────────┐
|
||||
▼ ▼
|
||||
RouteChunkManager (F12) FailureRecovery (F11)
|
||||
│
|
||||
▼
|
||||
SSE Event Streamer ──▷ Ground Station
|
||||
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
|
||||
NORMAL ──(VO fail)──▶ LOST ──▶ RECOVERY ──(GPR+Metric ok)──▶ NORMAL
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Стек
|
||||
|
||||
| Підсистема | Технологія |
|
||||
|-----------|------------|
|
||||
| **API** | FastAPI + Pydantic v2, SSE (sse-starlette) |
|
||||
| **БД** | SQLite + SQLAlchemy 2 (asyncio) |
|
||||
| **CV** | OpenCV (Essential Matrix, RANSAC, recoverPose) |
|
||||
| **Оптимізація** | GTSAM 4.3 (iSAM2, Huber kernel) |
|
||||
| **Моделі** | Mock engines: SuperPoint, LightGlue, DINOv2, LiteSAM |
|
||||
| **Кеш** | diskcache (супутникові тайли) |
|
||||
| **HTTP** | httpx (Google Maps Static Tiles) |
|
||||
| **Тести** | pytest + pytest-asyncio (80 тестів) |
|
||||
| Підсистема | 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 | — |
|
||||
|
||||
---
|
||||
|
||||
@@ -53,7 +60,6 @@ NORMAL ──(VO fail)──▷ LOST ──▷ RECOVERY ──(GPR+Metric ok)─
|
||||
### Вимоги
|
||||
|
||||
- Python ≥ 3.11
|
||||
- pip / venv
|
||||
- ~500 MB дискового простору (GTSAM wheel)
|
||||
|
||||
### Встановлення
|
||||
@@ -65,80 +71,108 @@ git checkout stage1
|
||||
|
||||
python3 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
|
||||
pip install -e ".[dev]"
|
||||
```
|
||||
|
||||
### Конфігурація `.env`
|
||||
|
||||
```env
|
||||
# Опціонально — для реальних супутникових тайлів
|
||||
GOOGLE_MAPS_API_KEY=<your_key>
|
||||
GOOGLE_MAPS_SESSION_TOKEN=<your_token>
|
||||
|
||||
# Налаштування серверу (за замовчуванням)
|
||||
GPS_DENIED_HOST=127.0.0.1
|
||||
GPS_DENIED_PORT=8000
|
||||
GPS_DENIED_DB_URL=sqlite+aiosqlite:///./gps_denied.db
|
||||
```
|
||||
|
||||
### Запуск серверу
|
||||
### Запуск
|
||||
|
||||
```bash
|
||||
# Пряме запуск
|
||||
python -m gps_denied
|
||||
|
||||
# Docker
|
||||
docker compose up --build
|
||||
```
|
||||
|
||||
Сервер стартує на `http://127.0.0.1:8000`.
|
||||
Сервер: `http://127.0.0.1:8000`
|
||||
|
||||
### Змінні середовища
|
||||
|
||||
```env
|
||||
GPS_DENIED_DB_PATH=/data/flights.db
|
||||
GPS_DENIED_TILE_DIR=/data/satellite_tiles # локальні тайли z/x/y.png
|
||||
GPS_DENIED_LOG_LEVEL=INFO
|
||||
MAVLINK_CONNECTION=serial:/dev/ttyTHS1:57600 # UART на Jetson
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API
|
||||
|
||||
| Endpoint | Метод | Опис |
|
||||
|----------|-------|------|
|
||||
| `/health` | GET | Health check |
|
||||
| `/flights` | POST | Створити новий політ |
|
||||
| `/flights` | POST | Створити політ |
|
||||
| `/flights/{id}` | GET | Деталі польоту |
|
||||
| `/flights/{id}` | DELETE | Видалити політ |
|
||||
| `/flights/{id}/images/batch` | POST | Завантажити батч зображень |
|
||||
| `/flights/{id}/fix` | POST | Надати GPS-якір (user fix) |
|
||||
| `/flights/{id}/images/batch` | POST | Батч зображень |
|
||||
| `/flights/{id}/fix` | POST | GPS-якір від оператора |
|
||||
| `/flights/{id}/status` | GET | Статус обробки |
|
||||
| `/flights/{id}/events` | GET | SSE стрім подій |
|
||||
| `/flights/{id}/object-gps` | POST | Pixel → GPS координата |
|
||||
| `/flights/{id}/events` | GET | SSE стрім (позиція + confidence) |
|
||||
| `/flights/{id}/object-gps` | POST | Pixel → GPS (ray-ground проекція) |
|
||||
|
||||
---
|
||||
|
||||
## Тести
|
||||
|
||||
```bash
|
||||
# Усі тести (80 шт, ~23с)
|
||||
python -m pytest tests/ -v
|
||||
# Всі тести
|
||||
python -m pytest -q
|
||||
|
||||
# Тільки acceptance
|
||||
python -m pytest tests/test_acceptance.py -v
|
||||
# Конкретний модуль
|
||||
python -m pytest tests/test_eskf.py -v
|
||||
python -m pytest tests/test_mavlink.py -v
|
||||
python -m pytest tests/test_accuracy.py -v
|
||||
|
||||
# Тільки конкретний модуль
|
||||
python -m pytest tests/test_graph.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_schemas.py` | Pydantic схеми | 12 |
|
||||
| `test_database.py` | SQLAlchemy CRUD | 9 |
|
||||
| `test_api_flights.py` | REST endpoints | 5 |
|
||||
| `test_health.py` | Health check | 1 |
|
||||
| `test_satellite.py` | Тайли + Mercator | 5 |
|
||||
| `test_coordinates.py` | ENU / GPS конвертері | 4 |
|
||||
| `test_pipeline.py` | Image queue | 3 |
|
||||
| `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` | Mock engines | 3 |
|
||||
| `test_vo.py` | Visual Odometry | 5 |
|
||||
| `test_gpr.py` | Place Recognition | 3 |
|
||||
| `test_metric.py` | Metric Refinement | 3 |
|
||||
| `test_graph.py` | Factor Graph | 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 |
|
||||
| | **Всього** | **80** |
|
||||
| `test_accuracy.py` | Accuracy validation | 23 |
|
||||
| `test_sitl_integration.py` | SITL (skip без ArduPilot) | 8 |
|
||||
| | **Всього** | **195+8** |
|
||||
|
||||
---
|
||||
|
||||
## Benchmark валідації (Phase 7)
|
||||
|
||||
```bash
|
||||
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 м |
|
||||
|
||||
---
|
||||
|
||||
@@ -147,70 +181,69 @@ python -m pytest tests/test_graph.py -v
|
||||
```
|
||||
gps-denied-onboard/
|
||||
├── src/gps_denied/
|
||||
│ ├── __init__.py
|
||||
│ ├── __main__.py # Entry point (uvicorn)
|
||||
│ ├── app.py # FastAPI application
|
||||
│ ├── config.py # Pydantic Settings (.env)
|
||||
│ ├── api/
|
||||
│ │ └── flights.py # REST endpoints
|
||||
│ ├── app.py # FastAPI factory + lifespan
|
||||
│ ├── config.py # Pydantic Settings
|
||||
│ ├── api/routers/flights.py # REST + SSE endpoints
|
||||
│ ├── core/
|
||||
│ │ ├── processor.py # FlightProcessor + process_frame (State Machine)
|
||||
│ │ ├── vo.py # Sequential Visual Odometry (F07)
|
||||
│ │ ├── gpr.py # Global Place Recognition (F08)
|
||||
│ │ ├── metric.py # Metric Refinement (F09)
|
||||
│ │ ├── graph.py # Factor Graph Optimizer (F10, GTSAM)
|
||||
│ │ ├── recovery.py # Failure Recovery Coordinator (F11)
|
||||
│ │ ├── chunk_manager.py # Route Chunk Manager (F12)
|
||||
│ │ ├── coordinates.py # Coordinate Transformer (F13)
|
||||
│ │ ├── models.py # Model Manager (F16)
|
||||
│ │ ├── satellite.py # Satellite Data Manager (F04)
|
||||
│ │ ├── pipeline.py # Image Input Pipeline (F05)
|
||||
│ │ ├── rotation.py # Image Rotation Manager (F06)
|
||||
│ │ ├── sse.py # SSE Event Streamer
|
||||
│ │ └── results.py # Result Manager
|
||||
│ ├── db/
|
||||
│ │ ├── database.py # Async engine + session
|
||||
│ │ ├── models.py # SQLAlchemy ORM models
|
||||
│ │ └── repository.py # FlightRepository (CRUD)
|
||||
│ ├── schemas/
|
||||
│ │ ├── __init__.py # Re-exports
|
||||
│ │ ├── flight.py # Flight, Waypoint, GPS, Camera schemas
|
||||
│ │ ├── events.py # SSE event models
|
||||
│ │ ├── image.py # ImageBatch, ProcessingStatus
|
||||
│ │ ├── rotation.py # RotationResult, HeadingHistory
|
||||
│ │ ├── model.py # InferenceEngine, ModelConfig
|
||||
│ │ ├── vo.py # Features, Matches, RelativePose
|
||||
│ │ ├── gpr.py # TileCandidate, DatabaseMatch
|
||||
│ │ ├── metric.py # AlignmentResult, Sim3Transform
|
||||
│ │ ├── graph.py # Pose, OptimizationResult
|
||||
│ │ ├── chunk.py # ChunkHandle, ChunkStatus
|
||||
│ │ └── satellite.py # TileCoords, TileBounds
|
||||
│ └── utils/
|
||||
│ └── mercator.py # Web Mercator utilities
|
||||
├── tests/ # 17 test modules (80 tests)
|
||||
├── docs/ # Архітектурні специфікації
|
||||
├── docs-Lokal/ # Локальний план та рішення
|
||||
├── pyproject.toml # Залежності та конфігурація
|
||||
└── .gitignore
|
||||
│ │ ├── 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
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Компоненти (F-індексація)
|
||||
## Компоненти
|
||||
|
||||
| ID | Назва | Файл | Статус |
|
||||
|----|-------|------|--------|
|
||||
| F04 | Satellite Data Manager | `core/satellite.py` | ✅ Mock |
|
||||
| F05 | Image Input Pipeline | `core/pipeline.py` | ✅ |
|
||||
| F06 | Image Rotation Manager | `core/rotation.py` | ✅ |
|
||||
| F07 | Sequential Visual Odometry | `core/vo.py` | ✅ Mock engines |
|
||||
| F08 | Global Place Recognition | `core/gpr.py` | ✅ Mock Faiss |
|
||||
| F09 | Metric Refinement | `core/metric.py` | ✅ Mock LiteSAM |
|
||||
| F10 | Factor Graph Optimizer | `core/graph.py` | ✅ GTSAM wrapper |
|
||||
| F11 | Failure Recovery Coordinator | `core/recovery.py` | ✅ |
|
||||
| F12 | Route Chunk Manager | `core/chunk_manager.py` | ✅ |
|
||||
| F13 | Coordinate Transformer | `core/coordinates.py` | ✅ |
|
||||
| F16 | Model Manager | `core/models.py` | ✅ Mock/Fallback |
|
||||
| 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 для конкретного апарату
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user