Files
Yuzviak 1273ec8eaf docs: rewrite README for Stage 2 Phase 2 (hexagonal arch + AC + structlog)
- Reflects Stage 2 hexagonal layout: components/, hot_types/, obs/, pipeline/
- Updated architecture diagram with ports-and-adapters structure
- Correct test count: 236 passed, 8 skipped (was: 216 from Stage 1)
- New sections: AC traceability, taxonomy markers, per-env config, Roadmap table
- ADR links to 0001-0004
- Component table with Protocol / dev / Jetson adapter columns
- Removed stale Stage 1 next steps and sprint backlog

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 19:06:54 +03:00

14 KiB
Raw Permalink Blame History

GPS-Denied Onboard

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

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

Гілка розробки: stage2 | Фаза: 2/6 (завершено) | Тести: 236 passed, 8 skipped


Архітектура (Stage 2 — Hexagonal / Ports-and-Adapters)

IMU (MAVLink RAW_IMU) ──────────────────────────────────────────▶ ESKF.predict()
                                                                        │
ADTI 20L V1 ──▶ pipeline/image_input ──▶ ImageRotationManager          │
                                              │                         │
                              ┌───────────────┼───────────────┐         │
                              ▼               ▼               ▼         │
                   components/vio    components/gpr    components/      │
                  cuVSLAM (Jetson)   Faiss+DINOv2      satellite_       │
                  ORB-SLAM3 (dev)    numpy (dev)        matcher         │
                              │               │               │         │
                              ▼               ▼               ▼         │
                         ESKF.update_vo()   GPR norm    MetricRefinement│
                              │                              (XFeat TRT) │
                              └──────────────────────▶ ESKF.update_sat()│
                                                                        │
                                                         ESKF state ◀──┘
                                                              │
                                              ┌───────────────┼──────────────┐
                                              ▼               ▼              ▼
                                  components/mavlink_io  core/factor_graph  SSE stream
                                  GPS_INPUT 5-10Hz       (GTSAM ISAM2)      → ground station
                                  → ArduPilot FC

Стейт-машина (FlightProcessor.process_frame):

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

Правило залежностей: pipeline/orchestrator.py імпортує лише Protocols. Тільки pipeline/composition.py (build_pipeline(env)) знає про конкретні адаптери.


Стек

Підсистема Dev / CI Jetson (production)
Visual Odometry ORBVisualOdometry / CuVSLAMMonoDepthVO CuVSLAMMonoDepthVO (PyCuVSLAM v15, barometer як synthetic depth)
AI Inference MockInferenceEngine TRTInferenceEngine (TensorRT FP16)
Place Recognition numpy L2 fallback Faiss GPU + DINOv2-VLAD TRT FP16
MAVLink MockMAVConnection pymavlink over UART
ESKF numpy 15-state numpy 15-state
Factor Graph stub GTSAM 4.3 ISAM2
Логування structlog ConsoleRenderer structlog JSON (orjson)
API FastAPI + Pydantic v2 + SSE FastAPI + Pydantic v2 + SSE
БД SQLite + SQLAlchemy 2 async SQLite

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

Вимоги

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

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

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

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

Запуск

# Прямий запуск (env=x86_dev за замовчуванням)
ENV=x86_dev uvicorn gps_denied.app:app --host 0.0.0.0 --port 8000

# Docker
docker compose up --build

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

Конфігурація

Вибір середовища задається змінною ENV:

ENV=x86_dev   # за замовчуванням — ORB-SLAM3, моки, консольні логи
ENV=jetson    # cuVSLAM + TRT + pymavlink + JSON logs
ENV=ci        # усі моки, без hardware
ENV=sitl      # ArduPilot SITL

Кожне середовище має overlay у config/{env}.yaml. Усі параметри — у src/gps_denied/config.py.

Ключові змінні середовища:

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
ESKF_VO_POSITION_NOISE=0.3
ESKF_SATELLITE_MAX_AGE=30.0
MODEL_WEIGHTS_DIR=weights

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)

Тести

# Всі тести (236 passed, 8 skipped)
python -m pytest tests/ -q --ignore=tests/e2e

# За категорією (taxonomy маркери)
python -m pytest -m unit -q
python -m pytest -m integration -q
python -m pytest -m blackbox -q

# Тести прив'язані до Acceptance Criteria
python -m pytest -m ac -q            # тільки ac-marked тести
python -m pytest -m ac --ac-dump     # + таблиця покриття AC

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

# E2E на публічних UAV-датасетах
pytest tests/e2e/ -q                              # skip якщо датасет відсутній
pytest tests/e2e/ -m "e2e and not e2e_slow" -v   # CI-tier
pytest tests/e2e/ -m e2e_slow -v                  # nightly-tier (VPAIR, MARS-LVIG)

E2E результати (EuRoC MH_0105, indoor):

Датасет Кадри ESKF ATE RMSE Статус
EuRoC MH_01 (easy) 100 0.205 м PASS
EuRoC MH_02 (easy) 100 0.131 м PASS
EuRoC MH_03 (medium) 100 0.008 м PASS
EuRoC MH_04 (difficult) 100 0.009 м PASS
EuRoC MH_05 (difficult) 100 0.007 м PASS
VPAIR sample (fixed-wing) 200 xfail (немає raw IMU)

AC Traceability

# Звіт: 39 AC total — 14 covered, 21 pending-phase-3+, 4 deferred-hardware
python scripts/gen_ac_traceability.py

# CI gate (виходить 0 якщо всі непокриті позначені pending/deferred)
python scripts/gen_ac_traceability.py --check

Матриця: .planning/AC-TRACEABILITY.md


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

gps-denied-onboard/
├── src/gps_denied/
│   ├── app.py                          # FastAPI factory + lifespan
│   ├── config.py                       # AppSettings / RuntimeConfig (pydantic-settings)
│   ├── components/                     # Hexagonal adapters (ports + impls)
│   │   ├── vio/                        # Visual Odometry
│   │   ├── satellite_matcher/          # Tile loading + XFeat metric refinement
│   │   ├── gpr/                        # Global Place Recognition (Faiss/numpy)
│   │   ├── mavlink_io/                 # MAVLink bridge + mock
│   │   ├── anchor_verifier/            # stub — Phase 3
│   │   ├── safety_state/               # stub — Phase 3
│   │   ├── flight_recorder/            # stub — Phase 4
│   │   └── coordinate_transforms/      # stub — Phase 5
│   ├── core/                           # Concentrated math (без DI)
│   │   ├── eskf.py                     # 15-state ESKF (IMU+VO+satellite fusion)
│   │   ├── factor_graph.py             # FactorGraphOptimizer (GTSAM ISAM2)
│   │   ├── coordinates.py              # ENU↔GPS↔pixel transforms
│   │   ├── chunk_manager.py            # RouteChunkManager
│   │   ├── recovery.py                 # FailureRecoveryCoordinator
│   │   ├── rotation.py                 # ImageRotationManager
│   │   └── models.py                   # ModelManager + TRTInferenceEngine
│   ├── hot_types/                      # @dataclass(slots=True, frozen=True) — hot path
│   ├── obs/                            # Observability (Phase 2)
│   │   ├── logging_config.py           # configure_logging(env) — JSON/console
│   │   └── log_schemas.py              # Pydantic v2 boundary log event schemas
│   ├── pipeline/                       # Orchestration layer
│   │   ├── orchestrator.py             # FlightProcessor + process_frame
│   │   ├── composition.py              # build_pipeline(env) — composition root
│   │   ├── image_input.py
│   │   ├── result_manager.py
│   │   └── sse_streamer.py
│   ├── api/routers/flights.py          # REST endpoints + SSE
│   ├── schemas/                        # Pydantic REST schemas + shims до hot_types
│   └── db/                             # SQLAlchemy ORM + async repository
├── tests/                              # 37 test-модулів з pytestmark (236 passed)
│   └── e2e/                            # E2E на публічних UAV-датасетах
├── config/                             # Per-env YAML overlays
│   ├── jetson.yaml
│   ├── x86_dev.yaml
│   ├── ci.yaml
│   └── sitl.yaml
├── scripts/
│   ├── gen_ac_traceability.py          # AC coverage report + CI gate
│   └── benchmark_accuracy.py           # Synthetic trajectory accuracy CLI
├── _docs/
│   ├── 00_problem/
│   │   └── acceptance_criteria.md      # 39 AC (AC-1.1..AC-8.6 + AC-NEW-1..8)
│   └── 01_solution/decisions/          # ADRs 00010004
├── Dockerfile
├── docker-compose.yml
├── docker-compose.sitl.yml
└── .github/workflows/
    ├── ci.yml                          # lint → {unit, integration, blackbox, ac-gate} → docker
    ├── nightly.yml                     # sitl + e2e slow (cron 03:00 UTC)
    └── sitl.yml                        # SITL integration

Компоненти (Stage 2)

Компонент Protocol Dev adapter Jetson adapter
Visual Odometry vio/protocol.py ORBVisualOdometry CuVSLAMMonoDepthVO
Satellite Matcher satellite_matcher/protocol.py LocalTileLoader + MetricRefinement те саме
Place Recognition gpr/protocol.py FaissGPR (numpy fallback) FaissGPR (GPU)
MAVLink I/O mavlink_io/protocol.py MockMAVConnection MAVLinkBridge (pymavlink)
Anchor Verifier anchor_verifier/protocol.py stub stub (Phase 3)
Safety State safety_state/protocol.py stub stub (Phase 3)
Flight Recorder flight_recorder/protocol.py stub stub (Phase 4)
Coord. Transforms coordinate_transforms/protocol.py stub stub (Phase 5)

Acceptance Criteria

39 AC з ідентифікаторами AC-N.M / AC-NEW-N у _docs/00_problem/acceptance_criteria.md.

Ключові порогові значення:

Критерій Поріг
Точність 80% кадрів ≤ 50 м
Точність 60% кадрів ≤ 20 м
End-to-end latency < 400 мс
Пам'ять (shared CPU/GPU) < 8 GB
Сторадж місії < 64 GB
GSD супутникового знімку ≤ 0.5 м/px
Час до першої позиції ≤ 30 с

Архітектурні рішення (ADRs)

ADR Рішення
0001 E2E dataset strategy (EuRoC / VPAIR / MARS-LVIG)
0002 Hexagonal / ports-and-adapters для Stage 2
0003 @dataclass(slots=True, frozen=True) на hot path; Pydantic лише на межах
0004 Stage 2 як незалежна ітерація зі своїми фазами 1–6

Roadmap Stage 2

Фаза Назва Статус
1 Hexagonal Refactor done (2026-05-11)
2 AC Doc + Test Taxonomy + Observability Spine done (2026-05-11)
3 Safety Anchor State Machine + Geometry-Gated Anchor Verifier planned
4 Conditional VPR + Flight Data Recorder planned
5 MAVLink source labels + dual-channel scaffold planned
6 Azaion 10.05.2026 real-flight integration fixture planned

Benchmark

python scripts/benchmark_accuracy.py --frames 50

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

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

Ліцензія

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