docs(tech-audit): OSS stack audit and sprint-1 technology decisions

Records architectural gap (cuVSLAM Mono has no metric scale),
chosen path (Mono-Depth + barometer), and per-layer decisions
for VO, ESKF, GTSAM, Place Recognition, and MAVLink.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Yuzviak
2026-04-18 15:29:02 +03:00
parent 81ec7c317c
commit 352d5e59ed
@@ -0,0 +1,179 @@
# Tech Audit — Open-Source Stack для GPS-Denied Navigation
**Дата:** 2026-04-18
**Горизонт:** 23 тижні (sprint 1)
**Контекст:** Fixed-wing БПЛА, Jetson Orin Nano Super, nadir камера ADTI 20L V1, ArduPilot FC
---
## 1. Архітектурний розрив — головна знахідка
### Проблема з cuVSLAM Inertial mode
Поточна архітектура планує cuVSLAM у режимі "Inertial" (з IMU). Але:
- cuVSLAM Inertial/Visual-Inertial mode потребує **стерео камеру** (дві камери поряд, як очі)
- У нас одна nadir камера — Mono режим
- cuVSLAM Mono дає лише кут повороту, **без metric scale** (без відстані)
- Без scale неможливо побудувати GPS_INPUT — це useless для ArduPilot
**Важливо:** камера пілота (вперед) + nadir камера (вниз) — це не stereo. Stereo = дві камери в одному напрямку на відстані 10–20 см.
### Чому це не критично для sprint 1
Scale для nadir камери над рівною місцевістю — **детермінований**:
```
scale = altitude / focal_length
```
Барометр дає висоту → ESKF рахує scale → VO потребує лише точний 2D planar tracking. Це і є перший шлях.
---
## 2. Вибрана архітектура (sprint 1)
### VO: cuVSLAM Mono-Depth + барометр
**Рішення:** cuVSLAM Mono-Depth режим, де барометрична висота подається як synthetic depth для відновлення metric scale.
**Точність між GPR корекціями:** ~0.30.5 м
**Drift на маршруті:** обмежений — GPR скидає накопичену похибку
### Чому drift не критичний
Архітектура має три рівні корекції:
```
VO (drift між кадрами)
ESKF ← IMU (короткострокова стабілізація, ~ms)
Place Recognition ← satellite tiles (глобальна корекція, ~сотні метрів)
GTSAM loop closure (довгострокова консистентність)
```
Якщо GPR впізнає місцевість кожні ~500 м → загальна похибка на 10 км маршруту: **15 м**. Це прийнятно для GPS-denied.
**Ризик GPR:** не спрацює при хмарах, однорідній місцевості (поле без орієнтирів), застарілих тайлах. В цих випадках drift накопичується до наступного успішного match.
---
## 3. Технологічний стек — рішення по шарах
### VO Layer
| Компонент | Dev/CI | Production (Jetson) | Статус |
|---|---|---|---|
| **Visual Odometry** | ORBVisualOdometry (OpenCV) | cuVSLAM Mono-Depth | Залишаємо, фіксуємо Mono-Depth |
| **XFeat** | — | Satellite tile matching (майбутнє) | Не VO fallback, окремий трек |
| **SuperPoint+LightGlue** | — | — | Відхилено: 15–33× повільніше за cuVSLAM |
**Відхилені альтернативи:**
- ORB-SLAM3 Mono-Inertial (~0.08 м) — потребує IMU ≥100 Hz по MAVLink. Типово ArduPilot шле 50 Hz (SR*_RAW_SENS). Можна підняти до 200 Hz зміною одного параметра, але не пріоритет зараз.
- cuVSLAM Stereo-Inertial (0.054 м) — потребує hardware зміни (друга камера). Довгостроково.
### ESKF
| Рішення | Обґрунтування |
|---|---|
| Залишаємо numpy 15-state ESKF | IMU preintegration не критичний на 5–10 Hz VO для повільного fixed-wing |
| **Пінити `numpy==1.26.4`** | NumPy 2.0 ламає GTSAM Python bindings (issue #2264) — критично |
| `manifpy` — опційно | pip-installable Lie group math, додати тільки якщо знайдемо баги в quaternion коді |
### Factor Graph (GTSAM)
**Рішення sprint 1: пропустити GTSAM, використовувати ESKF.**
ESKF достатній для Gaussian GPS_INPUT 510 Hz. Factor graph дає перевагу (~15 vs 34 см) лише при non-Gaussian noise з outliers — не наш сценарій зараз.
Коли повернутись: **GTSAM 4.2 stable** (не 4.3a1 alpha). miniSAM — стейл з 2019, g2o Python — experimental. Єдиний живий варіант.
### Place Recognition
| Компонент | Рішення | Обґрунтування |
|---|---|---|
| **Descriptor** | DINOv2-VLAD (AnyLoc) | 1012 мс TRT FP16 на Jetson, offline-capable |
| **Index** | Faiss GPU | Залишаємо |
| **INT8 квантизація** | ❌ Не робити | Broken для ViT на Jetson (NVIDIA/TRT#4348, dinov2#489) |
| **Довгостроково** | EigenPlaces (ICCV 2023) | Кращий ONNX export, viewpoint-robust |
| **NetVLAD** | ❌ Deprecated | DINOv2-VLAD краще на 2.4% R@1 (MSLS 2024) |
| **Satellite tiles** | MapTiler offline MBTiles | Україна zoom 013, JPEG, offline |
**Перевірити:** [aero-vloc](https://github.com/prime-slam/aero-vloc) — benchmark на aerial nadir imagery до того як фіксувати Faiss index дизайн.
### MAVLink / ArduPilot
| Рішення | Обґрунтування |
|---|---|
| **pymavlink** залишаємо | MAVSDK-Python не підтримує GPS_INPUT (PX4-first) |
| Reference impl | `MAVProxy/modules/mavproxy_GPSInput.py` — точне кодування GPS_INPUT (#232) |
| Частота injection | 510 Hz (не 20 Hz — timing jitter при вищих rate) |
| Yaw extension field | Не використовувати — ArduPilot 4.x ігнорує |
---
## 4. Критичні перевірки перед кодом
Три речі, які треба знати до того як писати будь-який код:
### 4.1 Процесор Flight Controller
- **H743** — підтримує GPS_INPUT over serial ✅
- **F405** — мовчки ігнорує GPS_INPUT ❌ (втрата днів на дебаг)
- Перевірити: Mission Planner → Help → About (показує назву FC), або питати у постачальника дрона
### 4.2 IMU rate по MAVLink
- За замовчуванням ArduPilot: **50 Hz**
- Для ORB-SLAM3 Mono-Inertial потрібно: **≥100 Hz**
- Параметр для зміни: `SR2_RAW_SENS` (або SR0/SR1 залежно від порту)
- Для sprint 1 (cuVSLAM Mono-Depth) — не критично
### 4.3 numpy версія
- Зафіксувати `numpy==1.26.4` в `pyproject.toml` негайно
- NumPy 2.0 ламає GTSAM silently — важко дебажити
---
## 5. Пріоритети на 2–3 тижні
### Тиждень 1
- [ ] Зафіксувати `numpy==1.26.4` в pyproject.toml
- [ ] Перевірити FC processor (H743 vs F405)
- [ ] Перевірити поточний IMU rate по MAVLink
- [ ] Prototype cuVSLAM Mono-Depth з барометром як synthetic depth
- [ ] Запустити aero-vloc на наших nadir кадрах
### Тиждень 2
- [ ] cuVSLAM Mono-Depth інтеграція в E2EHarness
- [ ] Порівняти ATE: ORB (поточний baseline) vs cuVSLAM Mono-Depth
- [ ] AnyLoc offline setup + перший тест на satellite tiles
### Тиждень 3
- [ ] XFeat TRT export для satellite matching (окремий трек від VO)
- [ ] MAVLink GPS_INPUT injection тест на SITL (Software-in-the-Loop)
- [ ] Визначити чи варто піднімати IMU rate для майбутнього Mono-Inertial
---
## 6. Аналоги нашої системи
Для розуміння де ми знаходимось відносно SOTA:
| Система | VO | Fusion | Scale | Статус |
|---|---|---|---|---|
| **Наша** | cuVSLAM Mono | ESKF | Барометр | Sprint 1 |
| **VINS-Fusion** | Mono-Inertial | Factor graph | IMU | Open-source, ROS |
| **OpenVINS** | MSCKF Mono | EKF | IMU | Open-source, ~0.08м EuRoC |
| **PX4 EKF2** | — | UKF | GPS | Вбудований в PX4 |
| **ArduPilot EKF3** | — | EKF | GPS | Вбудований в ArduPilot |
Наша архітектура унікальна тим що **замінює GPS через Place Recognition** (satellite matching) — не просто VIO, а VIO + global anchor. Це правильний підхід для тривалих маршрутів.
---
## 7. Відкриті питання
1. Якість cuVSLAM Mono-Depth на feature-poor nadir terrain (рівне поле, ліс) — потребує льотних тестів
2. Частота та надійність GPR match в реальних умовах України
3. Чи достатньо барометра для scale recovery при різких змінах висоти (маневри, вітер)
4. Перехід на Stereo-Inertial (hardware) — коли і чи потрібен взагалі