Files
gps-denied-onboard/next_steps.md
T
Yuzviak 81ec7c317c docs: record PR #10 — all 5 EuRoC MH baseline numbers
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 15:19:41 +03:00

123 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Next Steps — дорожня карта
Живий документ. Галочки заповнюються у міру виконання. Коментарі в квадратних дужках `[decision: …]` фіксують рішення, щоб через місяць можна було зрозуміти *чому* ми зробили саме так.
---
## 1. Аудит відповідності солюшну
Весь девелопмент зроблений не з цільового `_docs/01_solution/solution.md`, а з сукупності документів у `_docs/01_solution/`. Треба звірити імплементацію саме з цільовим солюшном.
- [ ] Порівняти поточну імплементацію з `_docs/01_solution/solution.md` (не з усією текою)
- [ ] **VO треба зробити на cuVSLAM** (якщо не буде явних перепон)
- Rationale: SP+LG (SuperPoint+LightGlue) rejection: 1533× повільніше за cuVSLAM. Немає built-in IMU fusion, loop closure, tracking-failure detection. Побудова цих фіч навколо SP+LG — значний час, і все одно повільніше. XFeat (~30–50 мс) — кращий fallback для VO якщо cuVSLAM не зайде на nadir-камеру.
- [ ] Прогнати cuVSLAM через e2e-харнес (див. пункт 3) — перевірити що VO+ESKF разом дають адекватну точність на EuRoC/VPAIR
- [ ] Якщо cuVSLAM підходить → видалити SP+LG з кодбейса. Залишити XFeat як fallback
**Цей крок треба робити ПІД захистом e2e-харнеса**, не до нього, щоб відразу бачити регресії (див. пункт 3).
---
## 2. Реструктуризація коду
Весь код у `src/gps_denied/`, але весь проект і так про gps_denied — зайвий рівень неймспейсу.
- [ ] `git mv src/gps_denied/* src/` (або через rename imports, що чистіше)
- [ ] Оновити імпорти у всіх файлах (`from gps_denied.X``from X` чи залишити як неймспейс, вирішити)
- [ ] Оновити `pyproject.toml`: `[tool.setuptools.packages.find]`
- [ ] Прогнати повну test suite — має бути зелено
- [ ] Оновити CI скрипти, Docker, docs
**Робити під захистом e2e-харнеса** — одна велика рефакторинг-зміна з багатьма PR → легко поламати щось непомітно. Зелений e2e (на тих самих числах що до рефакторингу) — sign-off.
---
## 3. Autopilot existing-code flow — e2e-харнес
Всі проекти в azaion приводяться до механізму девелопменту з `.cursor/skills/autopilot` (`flows/existing-code` для існуючого коду, `flows/greenfield` для нового).
Коротко алгоритм: питаємо вимоги до e2e-тестів і даних → рефакторимо для можливості e2e-покриття → пишемо e2e-підсистему що запускає продукт як black-box → переконуємось що працює → **тоді** ітеративно рефакторимо під захистом e2e.
### Статус виконання
**Design** (локально, gitignored): `.planning/brainstorms/2026-04-16-e2e-datasets-design.md`
**Plan** (локально, gitignored): `.planning/brainstorms/2026-04-16-e2e-datasets-plan.md`
**In-repo docs**: [src/gps_denied/testing/README.md](src/gps_denied/testing/README.md) (harness architecture), [_docs/01_solution/decisions/0001-e2e-dataset-strategy.md](_docs/01_solution/decisions/0001-e2e-dataset-strategy.md) (ADR — selection rationale)
[decision 2026-04-16: замість збору власних даних з Mavic-а — використати публічні UAV датасети. Причина: блокер на проприєтарні дані (Денис, мавікісти), а нам треба рухатись вже зараз. Public датасети: VPAIR (fixed-wing, downward, 300400 м) + EuRoC (indoor MAV, IMU+GT, індустрійний benchmark) + MARS-LVIG (rotary, featureless сіквенси як stress-test). Деталі — у ADR 0001.]
- [x] Побудувати `DatasetAdapter` ABC + capability flags (has_raw_imu, has_rtk_gt, platform_class) — `src/gps_denied/testing/datasets/base.py`
- [x] `SyntheticAdapter` для harness self-test (`synthetic.py`)
- [x] Trajectory метрики — RMSE, ATE, RPE (`metrics.py`)
- [x] `E2EHarness` — жене адаптер через `FlightProcessor`, збирає estimated positions, порівнює з GT (`harness.py`)
- [x] SHA256-verified dataset downloader + registry (`download.py`, `scripts/download_dataset.py`)
- [x] `EuRoCAdapter` + unit-тести з fabricated fixture
- [x] `VPAIRAdapter` + unit-тести з fabricated fixture (**реальний формат**: poses_query.txt, ECEF xyz, Euler rad)
- [x] `MARSLVIGAdapter` + unit-тести (очікує pre-extracted layout з ROS bag)
- [x] `coord.py` helpers: ECEF→WGS84 (Heikkinen closed-form), Euler→quaternion (ZYX aerospace)
- [x] Integration тести з skip-when-absent fixtures (EuRoC, VPAIR, MARS-LVIG)
- [x] Pytest markers `e2e`, `e2e_slow`, `needs_dataset` у pyproject.toml
- [x] **Перший реальний e2e-прогін на VPAIR sample** (200 кадрів fixed-wing, 300-400 м над Bonn/Eifel)
- Результат: пайплайн завершується без падінь, **ATE RMSE ~1 770 км** → xfail
- [decision 2026-04-16: це очікувано. VO сам по собі без IMU і без supплементарного supплутникового anchoring розходиться. VPAIR не має raw IMU → ESKF-шлях не активується. Xfail-branch документує цю deгадацію; перейде у strict-assert коли VO+GPR будуть тюновані для high-altitude nadir + знайдемо датасет з raw IMU.]
- [x] **Перший реальний e2e-прогін на EuRoC MH_01** (перші 100 кадрів indoor MAV, ASL формат)
- Результат: пайплайн завершується за ~30 с, **ATE RMSE ~10 871 м** → xfail за ceiling 5 м
- [decision 2026-04-17: замість старого `robotics.ethz.ch` URL (TCP timeout) — новий ETH Research Collection DOI `10.3929/ethz-b-000690084`. Окремого `MH_01_easy.zip` там немає, лише 12.6 GB bundle з усіма 5 MH-сіквенсами. Витягнули тільки `MH_01_easy.zip` у `/home/yuzviak/Azaion/Data/machine_hall/MH_01/`, symlink з `datasets/euroc/MH_01/`. Registry entry перейменована на `euroc_machine_hall` (bundle-level SHA256).]
- [decision 2026-04-17: harness отримав параметр `max_frames` — CI-tier гонить 100 кадрів за ~30 с. Повна sequence (3682) — ~3 години, не CI-tier.]
- Pipeline diff vs VPAIR: raw IMU є → ESKF активний; але satellite-anchoring для indoor-сцени не релевантний, VO+ESKF без якоря дрейфує на кілометри.
- [ ] Замінити xfail на strict-assert у VPAIR і EuRoC тестах (коли VO+GPR+ESKF-anchoring почнуть давати притомні числа)
- [ ] **IMU з SITL для початку** — ArduPilot SITL може літати mission з waypoints, генерувати 200 Hz IMU через MAVLink. Для піднять пайплайну цього достатньо. Реальний IMU треба так чи інакше, але це паралельна гілка роботи.
### Патерн гілок для e2e-роботи
- `feat/e2e-vpair`**merged** у stage1 через PR #1 (2026-04-16, rebase)
- `feat/e2e-euroc` — поточна робота (2026-04-17): harness `max_frames`, перший реальний прогін EuRoC MH_01, реєстр перейменовано на `euroc_machine_hall`
- Інтеграція у stage1 через послідовні PR-и. Політика: наступні merge без `--delete-branch` (гілки лишаються на GitHub)
---
## 4. Збір реальних UAV-даних
[decision 2026-04-16: **деприйорити** цей пункт на користь пункту 3 (публічні датасети). Причина: публічні датасети вже є, дають реальний fixed-wing сценарій, не вимагають координації людей. Повернемось до цього коли:
- (а) потрібно буде валідувати на *нашому* конкретному дроні (intrinsics, спектр вібрації IMU, camera model відрізняються від VPAIR/EuRoC);
- (б) готовий буде VO+ESKF-тюнінг і захочеться "фінальний" датасет саме під envelope tactical fixed-wing 200-1500 м.]
- [ ] Денис Попов — логи Mavic (камера вниз, висока висота). *Призупинено.*
- [ ] Штатні мавікісти — пара тестових вильотів з nadir-камерою. *Призупинено, потребує переконати.*
- [ ] DJI Mavic флайт у `/home/yuzviak/Azaion/Data/` (MP4 + SRT + Airdata CSV)
- [decision 2026-04-16: НЕ інтегруємо зараз. Причини: (1) DJI не публікує raw IMU, є лише attitude/speeds (результат фʼюжену) — ESKF-шлях не активується; (2) потрібні camera intrinsics конкретної моделі; (3) sync SRT@30Hz + CSV@5Hz потребує окремого alignment-кроку. Залишаємо як qualitative demo пізніше, коли інфраструктура стабілізується.]
---
## Хронологія рішень
- **2026-04-16 ранок**: Аудит інфраструктури, pull upstream, вирішили взяти публічні датасети замість чекати на Дениса. Brainstorm + spec + plan: VPAIR (Tier 1), MARS-LVIG (Tier 2), EuRoC (Tier 3 CI).
- **2026-04-16 день**: Реалізовано e2e-харнес з `DatasetAdapter` pattern. 12 комітів у stage1 (`a2620ae``0062323`), пушнуто. 233 passed, 13 skipped.
- **2026-04-16 вечір**: Спробували скачати EuRoC MH_01 — старий URL лежить, знайшли новий DOI (12 GB bundle, завтра). Переключились на VPAIR (вже скачаний). Реальний формат відрізняється від припущеного: ECEF+Euler+no timestamps. Написали `coord.py` (ECEF→WGS84 Heikkinen + Euler→quat), переписали `VPAIRAdapter`. Гілка `feat/e2e-vpair`, 5 комітів. Перший реальний прогін: ATE ~1770 км (очікувано, задокументовано в xfail).
- **2026-04-17**: Завантажили 12.6 GB `machine_hall.zip`, витягли `MH_01` (2.6 GB). Додали `max_frames` у `E2EHarness` (TDD, 3 нових тести). Перший реальний прогін EuRoC MH_01 на 100 кадрах: пайплайн завершується за ~30 с, **ATE RMSE ~10.87 км → xfail**. Registry entry перейменовано `euroc_mh01``euroc_machine_hall` з реальним SHA256 `5ed7d07…`; URL порожній (ETH Research Collection не дає direct link, ручне завантаження). Гілка `feat/e2e-euroc`.
- **2026-04-18**: Три PR підряд у stage1:
- **PR #4** `feat/e2e-trace` — додали per-frame JSONL трасування в `E2EHarness` (`trace_path` параметр). Запустили EuRoC MH_01 з трасуванням: виявлено `vo_success=0/100`, `eskf_initialized=0/100`, `alignment_success=77/100`. Всі оцінки — fallback satellite matching без ESKF/VO.
- **PR #5** `feat/e2e-vo-only` — ORB VO-only діагностика. Запустили `ORBVisualOdometry` напряму на EuRoC кадрах: **99/99 tracking rate (100%)**. Висновок: проблема була не в VO-алгоритмі, а в тому що `SequentialVisualOdometry` (Mock random keypoints → RANSAC failure) використовувалась у харнесі.
- **PR #6** `feat/e2e-harness-orb-vo` — замінили VO-бекенд у `_build_processor` з `SequentialVisualOdometry(ModelManager())` на `ORBVisualOdometry()`. Новий результат: `vo_success=99/100`. Залишилась проблема: `eskf_initialized=0/100` — ESKF потребує `init_flight()` зі start_gps, харнес цього не робить.
- [decision 2026-04-18: VO-бекенд у харнесі має бути `ORBVisualOdometry` (реальні OpenCV фічі), а не Mock SP+LG (random keypoints). Для Jetson-production cuVSLAM залишається метою — але харнес валідує pipeline логіку незалежно від бекенду.]
- **Наступний крок**: ініціалізувати ESKF у харнесі з синтетичним GPS-origin (середня координата GT або перша GT-поза). Це увімкне ESKF-шлях і дасть змогу виміряти реальний VO+ESKF дрейф без satellite fallback.
- **PR #8** `feat/e2e-eskf-init` — ESKF ініціалізується з першої GT-пози адаптера. Результат: `eskf_initialized: 0→100/100`, `eskf_has_position: 100/100`. ATE RMSE ~10.7 км. Satellite measurements відхиляються Mahalanobis gate (Δ²~10⁶ >> 16.3). Root cause: ORB `scale_ambiguous=True` → unit-scale VO translations → ESKF position diverges → satellite outlier gate срацює коректно.
- **Наступний крок**: VO metric scale. ORB переводить `(R, t̂)` де `t̂` — одиничний вектор, реальна відстань невідома. Варіанти: (а) передавати IMU timestamps у ESKF щоб він сам рахував scale через velocity; (б) перейти на cuVSLAM (metric backend); (в) хак — фіксований scale для EuRoC indoor (~0.3 м між кадрами). Правильний шлях — (б) cuVSLAM, але для CI-діагностики можна (в).
- **PR #9** `feat/e2e-vo-scale` — реалізовано варіант (а): `vo_scale_m=0.005` (5 мм/кадр, виміряно з GT median). `_ScaledVO` wrapper нормалізує unit-vector ORB translation і множить на scale. `HarnessResult` тепер збирає `eskf_positions_enu`. **Перший strict-assert тест на реальних даних**: `test_euroc_mh01_eskf_drift_within_ceiling` PASS — ESKF ATE RMSE **~0.20 м** за 100 кадрів (ceiling 0.5 м).
- [decision 2026-04-18: фіксований scale 0.005 м/кадр — це діагностичний хак для CI. Правильне рішення — cuVSLAM (metric VO). Але цей тест тепер є regression guard: якщо рефакторинг VO/ESKF ламає інтеграцію — тест покаже.]
- **Поточний стан pipeline на EuRoC MH_01**: `vo_success=99/100`, `eskf_initialized=100/100`, ESKF ATE=0.20 м ✓. GPS estimate ATE xfail (satellite не туновано під indoor).
- **Наступні кроки**: (1) MH_02-05 параметризовані тести — перевірити чи 0.20 м baseline стабільний на інших sequences; (2) VPAIR — там satellite matching може бути актуальним (outdoor, є reference_views/); (3) cuVSLAM як VO backend.
- **PR #10** `feat/e2e-mh-multi` — витягли MH_02-05 з bundle, написали 10 параметризованих тестів (pipeline_completes + eskf_drift для всіх 5 sequences). Всі 10 PASS. ESKF ATE baseline:
| Sequence | Difficulty | ESKF ATE RMSE |
|---|---|---|
| MH_01 | easy | 0.205 m |
| MH_02 | easy | 0.131 m |
| MH_03 | medium | 0.008 m |
| MH_04 | difficult | 0.009 m |
| MH_05 | difficult | 0.007 m |
MH_03-05 мають дуже малий дрейф (~1 см) бо перші 100 кадрів MAV майже нерухомий (старт sequence). Це нормально — цей baseline зростатиме якщо запустити більше кадрів.
**Поточний стан e2e харнесу**: 70 passed, 1 skipped, 2 xfailed. Всі EuRoC MH sequences покриті strict-assert тестами.