docs(observatory): record realtime plan and measured latency evidence

This commit is contained in:
DCCONSTRUCTIONS
2026-09-02 00:59:48 +03:00
parent bfe6ef4c77
commit ffd6be5606
2 changed files with 782 additions and 0 deletions
@@ -0,0 +1,509 @@
# Stream-first Perception — этап 1, контракты и ограниченный baseline
Дата: 2026-09-01. Experiment ID: `perception-stream-stage1-20260901T1930Z`.
Worker: RTX 4090, 24564 MiB, NVIDIA driver 610.47.
Статус: **частичный результат этапа 1; не полный runtime и не real-time qualification**.
Обновление 20:51 UTC: совместный source-paced пилот выполнен. После оптимизации
128/128 кадров без drops; p95/p99 174.09/190.62 ms — FAIL исходного latency gate.
Подробности и evidence нового experiment `perception-joint-pilot-20260901T2040Z`
находятся ниже. Последнее дополнение — 2026-09-02 00:41 МСК: семь bounded
latency runs; shared-budget CPU overlap дважды дал 128/128 без drops,
но p99 194.25198.22 ms не проходит исходные 125 ms. Подробности — в последнем
разделе. Это не product cutover и не самостоятельный образ.
## Решение и сделанный объём
Первый профиль зафиксирован как `K1 Perception — DDRNet-39 + RF-DETR + TGS`:
одна сегментация, детектор, online LiDAR/расстояния/static obstacles,
temporal/motion, TGS/costmap и advisory policy. EoMT не запускался и не удалялся.
Моторы, автопилот и Synology deployment не затрагивались.
Добавлены:
- [Версионные контракты](../../src/k1link/perception/realtime_contract.py):
bounded start без размера/длительности/полного архива, обязательные каналы,
epoch/lease/profile bindings, шесть выходных слоёв, causal freshness и
отрицательные критерии whole-path real-time проверки.
- [Candidate manifest](../../config/perception/k1-perception-ddrnet39-rfdetr-tgs-prototype-v1.json):
pinned модели/алгоритмы, accepted coarse `hard_surface`, static.unknown,
serial GPU policy, исходные фильтры, пределы памяти/очередей/задержки.
Он не зарегистрирован как готовый executor; image digest остаётся `null`.
- [ADR 0049](../../docs/adr/0049-stream-first-perception-profiles.md): карта
CURRENT → TARGET, reuse существующего raw-first ingress, lifecycle,
no-preload правило и стратегия изоляции зависимостей внутри одного образа.
- [Ограниченные диагностические программы](worker/streaming_profile_stage1/).
**Старый `source_materializer.materialize(job)` не удалён и рабочий LAB-путь
не переключён.** Запрет полного предварительного копирования сейчас закреплён
в новом контракте и тестах. Реального нового сетевого runtime эти изменения
ещё не создают. gRPC остаётся кандидатом, а не проверенным транспортом.
Новый путь должен выдавать текущие результаты до EOF; полное хеширование,
распаковка, PNG-cache и конечная публикация не могут быть условием первого
inference. Текущие запись и raw-first hooks сохраняются; второй acquisition
контур для K1 не создаётся. Защита от перегрузки обязана учитывать суммарные
байты, а не только число сообщений.
## Условия измерений
Все модельные проверки выполнены **последовательно**, только на Worker.
На Mac — лишь ограниченные unit/regression проверки, без моделей и нагрузки.
На время GPU-проб останавливались старый Triton, perception-worker и installed
LAB agent; после проб они восстановлены. До нагрузки GPU utilization был 0%,
память около 529–531 MiB (системный/display остаток). Независимые профили не
запускались вместе. Внутренние CUDA auxiliary streams одного RF-DETR engine
не означают параллельные профили.
Контейнеры проб: `--rm`, фиксированные image IDs, labels `mission-core` /
`observatory`, `--network none`, read-only root/assets, dropped capabilities,
ограниченные CPU/RAM/PIDs/tmpfs. GPU-пробы: 6 CPU / 8 GiB; CPU-пробы:
2 CPU / 2 GiB. Никакие веса не скачивались. Использовались уже имеющиеся на
Worker входы; полная запись не пересылалась и не хешировалась перед пробой.
Это короткий component baseline, **не source-paced replay**. Восемь warmup
итераций DDRNet не входят в 64 измеренных кадра; декодировано 65 исходных
кадров. RF-DETR измерен на синтетическом uint8 tensor. TGS использовал первые
64 уже подготовленных causal rolling clouds: это разрешено для изолированного
замера ядра, но запрещено как замена online preparation в полном профиле.
Local-surface использовал 64 синтетических облака по 4000 точек, stationary
sensor, seed `20260901`; логические timestamps 10 Hz не были pacing-механизмом.
## Компонентные результаты
Время в миллисекундах. Здесь нельзя складывать p95 или обращать отдельные
значения в обещанный FPS полного профиля. Для сохранённых массивов использован
nearest-rank percentile `ceil(q*N)-1`; RF-DETR — percentiles самого trtexec.
При N=64 p99 nearest-rank равен максимуму и не характеризует редкие хвосты.
| Проверка | Среднее | p95 | p99 | Что не включено |
| --- | ---: | ---: | ---: | --- |
| DDRNet, исходный image, FP32, decode/preprocess/H2D/forward/post/D2H | 21.523 | 43.064 | 47.150 | Сеть, остальные слои, публикация |
| RF-DETR TRT11, DMA включён | 2.301 | 2.505 | 2.986 | Реальное изображение/качество, host filtering, сеть |
| TGS C++ на prepared rolling clouds | 1.034 | 1.242 | 1.519 | Подготовка rolling cloud, costmap, транспорт |
| Online local-surface, synthetic, BLAS threads=1 | 63.219 | 66.066 | 73.904 | Реальная траектория, object association, motion/policy |
DDRNet load + first decode + model warmup: 9.160 s, без создания/распаковки
Docker-контейнера и без сетевого старта. Torch allocated peak 276.35 MiB,
reserved 316 MiB — **не** общий VRAM peak профиля. Существующий runner сохраняет
floor-percentiles; таблица пересчитана из его 64 raw rows, не из округлённых
агрегатов. Веса, preprocessing и FP32 не менялись.
RF-DETR: `--warmUp=1000 --duration=0 --iterations=64 --avgRuns=1
--percentile=95,99 --includeDataTransfers --noCudaGraph --noSpinWait`.
TRT11 по умолчанию отключает transfers и включает CUDA graph/spin-wait;
первый диагностический результат без DMA не принят как сравнимый baseline.
В принятом запуске средние H2D / GPU / D2H: 0.120 / 2.173 / 0.009 ms.
Это engine latency, не latency готовых объектов в приложении.
С default numeric-library threading synthetic local-surface занимал в среднем
198.043 ms. При `OPENBLAS_NUM_THREADS=1`, `OMP_NUM_THREADS=1`,
`MKL_NUM_THREADS=1` — 63.219 ms. Пороги геометрии не менялись; bitwise parity
геометрических выходов не проверялась. Все 64 состояния — valid.
Thread limits добавлены в candidate execution policy. CPU-геометрия остаётся
существенным риском бюджета: это уже больше времени, чем оба отдельных
модельных измерения, а online association/motion/fusion ещё не замерены вместе.
## Проверка общей базы образа
Собран **диагностический**, не самостоятельный продуктовый образ:
`ndc-perception-stage1-dependency-probe:20260901`, local image ID
`sha256:664824aa25de1db178f177d67a81b01541a938b479812b9383ddbf03f6b59dbe`.
Создан 2026-09-01 19:49:38 UTC; сборка без network/pull из существующих баз:
| База | Проверенный local image ID |
| --- | --- |
| Installed DDRNet step | `e6c986100613ec804f0e0076ca8695abf43ff88ef9d5d85f6857e0b41db74051` |
| TRAVEL TGS | `7b412020f4d8392d1d1ed1b33beadc44140f0ea8f781e62dd69796042334300f` |
| Triton 26.06 | `58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794` |
В final base скопированы старое `/opt/conda` и C++ TGS binary. Проверены
отдельными последовательными запусками **одного и того же image ID**:
- DDRNet Python 3.9 / Torch 1.13.1 cu117: 64 кадра; среднее 20.781 ms,
p95 39.665 ms, p99 47.251 ms; model-load/first-decode/warmup 8.422 s.
SHA последовательности masks совпал с исходным image:
`7b4cc5e10f0ee7a5cc20ff0679f4b972607975555b7d16670fff19e4ca491adb`.
Это ограниченный parity check 64 кадров, не всего датасета.
- RF-DETR native TRT11: 64 synthetic queries, среднее 2.303 ms, p95 2.356 ms,
p99 2.794 ms, те же явные флаги DMA/graph/spin-wait.
- Python 3.12 online local-surface: 64 valid состояния, mean 63.518 ms.
- Скопированный C++ TGS binary: 64 prepared clouds, mean 1.060 ms.
Таким образом, базовый ABI/import/execution разрыв решаем через изолированные
процессы/interpreters в **одном образе**, без обновления DDRNet checkpoint.
**Совместно resident модели, общий supervisor/IPC/scheduler и полный граф в
одном экземпляре контейнера не проверены.** Пробы используют явные read-only
mounts весов, runner и входов; они не доказывают standalone packaging.
Диагностический image сохранён для воспроизводимости, активного контейнера нет.
Выявленные требования к настоящей сборке:
- Старый DDRNet image не содержит checkpoint в объявленном logical asset path.
Начальная проба image-only завершилась до inference. Веса/runner должны быть
внутри нового образа, а не неявным host-cache.
- Super-gradients требует writable log directory; предоставлялся ограниченный
tmpfs `/root/sg_logs`. Matplotlib использовал temporary cache; будущий runtime
должен явно задавать writable scratch/cache paths и их ограничения.
- В Triton base есть native trtexec/server и Python 3.12/NumPy, но не найдены
Python `tensorrt`, `grpc`, `cv2`, `google.protobuf`. Нельзя считать SDK/codec
окружение готовым. Допустим внутренний supervised native Triton; зависимости
supervisor/transport необходимо явно включить и проверить.
- TensorRT plan связан с runtime/hardware совместимостью. Переносимость Docker
не означает автоматическую совместимость engine с другой GPU/Apple Silicon.
## Ollama / Frigate: изменение по прямому разрешению владельца
Оба сервиса остановлены и **не восстановлены** после измерений:
`sentinel-ollama`, `sentinel-frigate`. Docker restart policy обоих — `no`.
Данные, модели и записи камер не удалялись.
Изменён источник Compose на Worker:
`D:\_PROJ\NODEDC\NODEDC_AEGIS\docker\docker-compose.frigate.yml`.
Frigate получил `profiles: [manual-frigate]`, Ollama — `[manual-ollama]`,
оба `restart: "no"`. Проверка 2026-09-01 19:58:13 UTC: обычный
`compose config --services` не включает ни одного из этих сервисов;
явные manual profiles содержат нужные сервисы с restart=no.
Проверенные task/startup entries по именам сервисов не обнаружены.
Физическая перезагрузка Windows не выполнялась: проверена конфигурация
автозапуска, а не результат реального reboot.
Backup сохранён рядом:
`docker-compose.frigate.yml.before-manual-only-20260901T1915Z`.
SHA-256 до: `2f762ca0361411091a6ca184fbef73fdce7739957476bc25270b9464ad3c205b`.
После: `5e5b1f52e5d328a1fcae8a026a71dec81342734e6770a8fbf3bb8ddd38e7c331`.
Не заменять весь Compose слепо при будущем rollback: сначала проверить drift.
Редкое использование Ollama требует явного ручного запуска; это не повод
возвращать её в постоянную GPU-нагрузку.
Остальной durable Mission Core Worker-контур восстановлен. Triton readiness
проверен HTTP; локальный canonical Mission Core `127.0.0.1:8000` возвращает 200.
У legacy perception-worker до проб наблюдался restart loop (387 restarts);
восстановление контейнера не означает исправления этого прежнего дефекта.
Не связанные с задачей сервисы/данные не менялись.
## Проверки и evidence
78 focused tests прошли: новый contract suite (51) плюс существующие
`test_live_perception`, `test_live_perception_synchronizer`,
`test_live_perception_shadow` (27). Ruff новых contract/test и обеих Python
probe программ — PASS; mypy contract module — PASS. Для DDRNet probe lint
использует Python 3.9 target, не несовместимый с ним `datetime.UTC`.
Это не полный backend/frontend suite и не нагрузочный тест.
Unit tests показывают, среди прочего, что существующий raw-first ingress
отдаёт наблюдение до `end_session`, а новый contract не принимает batch mode,
full-source fields, cross-epoch/future/stale relabeling, неполный scene,
скрытые пропуски, замедленный replay и превышение инженерных бюджетов.
Они не доказывают доставку application ↔ Worker по сети.
Raw timings/logs и replay commands сохранены вне Git:
`.runtime/perception-stream-stage1-20260901/`. UTC/monotonic anchors DDRNet:
baseline `2026-09-01T19:31:47.850689+00:00`, monotonic
`43059737808266``43070282556549`; combined-image
`2026-09-01T19:54:58.292953+00:00`, `44450232680793``44459991768361`.
Эти monotonic значения не вычитаются из часов Mac. trtexec logs содержат UTC
и device-local durations; межмашинный clock mapping ими не измерен.
| Raw artifact | SHA-256 |
| --- | --- |
| ddrnet-baseline.json | `6c33cd98c09bbe91c4a0ef45a75dcdc8426b8b3227988e772225b011167da528` |
| rfdetr-trtexec.log | `078f6a311cb00ad7326d5b9d99799bf45c97546a6f244f5507037b56ebe9917a` |
| tgs-baseline.json | `fe264d31a6a5215edea68c5497eb5c862d5f002a4711190e95dbd0ac952a9430` |
| surface-default-threads.json | `aa7df56e250b8a179f6e61ddd34a312c0594d409122259813e366d75511e066c` |
| surface-single-thread.json | `190794710cd3dbb40313bdbba57fe7e182e2f3dc32c7809b246ae5d257368b19` |
| ddrnet-combined-image.json | `72930d502ce3aab5b146c4c18a8a2b24abfc7bda25b3c6a16425a2e7f8ac6c2e` |
| rfdetr-combined-image.log | `cbff78a8644a6a59132cda64717fd4f206589c52f6fbc5426457445fc451b6a8` |
| surface-combined-image.json | `5d76520bceee57d2f39e2404eb9fa8776d38d2f837a3b116a295538c040463da` |
| tgs-combined-image.csv | `c80faa6cc54860d86af57079954d6654db1a58eecccd432d3992684ba941267a` |
Model/config pins находятся в candidate manifest; существующие raw recordings,
clouds и приватные logs не добавлялись в Git.
## Оставшиеся gates: не объявлять этап 1 или real-time автоматически закрытыми
1. Численные budgets зафиксированы **как инженерный candidate**, не как
измеренное достижение или vehicle safety approval: whole-path p95/p99
≤125 ms, layer age ≤250 ms, release lag ≤25 ms, inflight ≤16 MiB,
camera pending ≤2, zero capacity drops при начальном stride 1.
Их нельзя ослаблять задним числом ради успешного прогона.
2. Совместный causal pilot выполнен 2026-09-01 в 20:51 UTC; его результаты
приведены ниже. Оба модельных процесса resident, GPU inference последователен.
После локальной оптимизации получены 128/128 результатов, но p95/p99
174.09/190.62 ms и backlog growth 51.44 ms не проходят исходные бюджеты.
Измерение выполнено; real-time qualification не получена.
3. Риски detector filters для маленького животного/близкого крупного объекта
установлены по коду (min box 64 px, max fraction 0.5, FOV 0.5), но quality
acceptance на этих случаях не выполнен; пороги сохранены без изменений.
4. Standalone упаковка всех assets, streaming transport/codec/clock mapping,
per-worker fencing, общий supervisor и end-to-end receiver timestamps
остаются следующими реализационными задачами. Нет автоматического GO на
полный recording-run, UI cutover, физический live или автономное движение.
Этапы 2–4 не объявляются начатыми/пройденными за счёт диагностического image.
Существующие исторические результаты и batch acceptance сохранены без
переписывания. Продолжение ведётся в одном
[четырёхэтапном ExecPlan](../../docs/OBSERVATORY_REALTIME_PROFILES_EXECPLAN.md).
## Совместный потоковый пилот — 2026-09-01, 20:51 UTC
**Результат:** полный вычислительный граф действительно исполняется в одном
временном контейнере на RTX 4090. Это уже не сумма отдельных model benchmarks.
Оптимизированный запуск завершил окно 128 кадров без capacity drops, но
**performance gate остаётся FAIL**. Продуктовый batch-путь не переключён.
### Граница и метод
- Один контейнер `ndc-k1-ddrnet-rfdetr-tgs-joint-pilot`, один supervisor,
постоянный DDRNet Python 3.9, native RF-DETR TensorRT/Triton, CPU supervisor
Python 3.12 и C++ TGS. DDRNet завершается до вызова RF-DETR; EoMT отсутствует.
- Рассчитываются DDRNet masks → RF-DETR proposals → online local surface →
LiDAR association/ranges → temporal/motion/rolling obstacles → simulated
threat assessment → TGS/costmap → hard-surface-only advisory policy.
На каждой камере работают обе модели. При отсутствии допустимых текущих
cloud/pose метрические слои возвращают unavailable, а не выдуманный результат.
- Источник RAVNOVES00, session `20260720T065719Z_viewer_live`: существующее
camera video `cadd1696…`, camera index из job
`recorded-camera-602ac89026ed12978619801d`, normalized LiDAR replay v2
`8fc0fb418578b8ee2ac88d502d2acbc63ae533437a9da14f1a9f9d8916f613ce`.
Из архива последовательно читаются original host-arrival timestamps,
point increments и pose. Старый E10 pack используется **только** для трёх
малых статических calibration arrays. Нет готовых masks, surface, TGS,
detections или threat ledgers в качестве входа графа.
- Источник подаётся отдельным producer по исходным временам 1×, не по окончанию
inference. Камерное окно занимает 12.684938 s между первым и последним кадром.
Интервалы: min 2.724 ms, median 99.594 ms, max 215.529 ms; 10 интервалов
короче 50 ms. Поэтому равномерный synthetic 10 Hz не эквивалентен этой записи.
- Прогрев моделей предшествует admission. Начальный sensor preroll ≤500 ms;
raw rolling cloud ≤1 s / 64000 points; pending camera ≤2; aggregate queued +
active observation bytes ≤16 MiB. Один кадр декодируется после его due time.
В финальном запуске прочитано 229/4570 point records, 241/4598 poses,
545590/10751258 XYZ rows, включая отброшенный начальный sensor prefix.
Полная запись не копировалась, не хешировалась и не декодировалась до старта.
- Scene сериализуется и разбирается фактическим **локальным** collector внутри
контейнера. `source_due_to_receiver_ms` включает release/decode/queue/весь граф
и этот collector, **не включает сеть Mission Core ↔ Worker и viewer**.
Сохранение диагностического JSONL происходит после receiver timestamp;
его возможная задержка следующего кадра остаётся видна в queue timings.
- CPU libraries: BLAS/OMP/MKL=1; container 8 CPUs / 8 GiB. CPU quota throttling
в финальном измерении отсутствовал (`nr_throttled=0`). Это не доказывает
отсутствие любого host/driver jitter. Измеренная memory — cgroup memory,
не сумма RSS процессов; sampled VRAM содержит системную/display составляющую.
### Исправления, которые выявил именно совместный запуск
1. Разделены `PYTHONPATH` Python 3.9/3.12: общий путь к Pillow 3.12 ломал DDRNet
при загрузке. Первый canary остановился до admission, без кадров.
2. Threat/motion использует прошлую body pose. Адаптер «только текущая поза»
остановил первое 128-frame окно после 63 результатов. Добавлена bounded
история 64 уже вычисленных body frames; запрос будущего frame запрещён.
Body frame — только camera-forward simulation с прежними height/slope
ограничениями, **не** future-trajectory resolver и не реальная установка рига.
3. Vectorized lookup заменил Python tuple/dict loop по точкам costmap, сохранив
grid membership, holes и индексы. Synthetic exact-parity test прошёл;
DDRNet masks совпали на всех 120 общих кадрах двух запусков. Это не полная
parity всей temporal scene: набор обработанных кадров различался из-за drops.
4. Publication guard учитывает возраст камеры **и** исходных cloud/pose.
Устаревшее evidence остаётся доступным для диагностики, но все его terrain
actions принудительно NO_GO. Только coarse hard_surface с поддержанной
геометрией может быть ALLOW_candidate; rural bare_soil здесь не разрешён.
### Измерения (nearest-rank percentiles, без отбрасывания медленных кадров)
| Запуск | Результаты / вход | Drops | Source→local receiver p95 / p99 | Решение |
| --- | --- | --- | --- | --- |
| Canary 32, рабочий ABI | 32 / 32 | 0 | 232.39 / 253.02 ms | FAIL latency/backlog |
| 128, bounded causal body history | 120 / 128 | 8 | 353.98 / 401.19 ms | FAIL latency/overflow |
| 128, vectorized costmap | 128 / 128 | 0 | 174.09 / 190.62 ms | FAIL latency/backlog |
Финальный запуск `joint-pilot-128-vectorized`, UTC start
`2026-09-01T20:51:35.734802+00:00`, Worker monotonic start `47847791349665`.
Replay mapping: source zero `200950673923333` → Worker zero `47857207269471`.
Последний result `47870540878573`. Часы разных машин не вычитаются напрямую.
Дополнительные результаты финального окна:
- mean source→receiver 87.94 ms; max 213.89 ms. Compute→receiver p95/p99
115.25/122.98 ms, но **этой более узкой метрикой нельзя заменить end-to-end**.
- Queue wait p95/p99 68.68/90.36 ms; прирост средней очереди последних восьми
относительно первых восьми кадров 51.44 ms при лимите 25 ms.
- Source release max 12.78 ms по всем трём каналам — PASS ≤25 ms.
- Первый результат через 546.12 ms от начала sensor-preroll clock, до конца
пилотного окна и до EOF записи. Model/runtime warmup 9.36 s; stop 4.07 s.
- Peak sampled GPU memory 1932 MiB; peak cgroup memory 3342.25 MiB;
observation inflight 7194040 bytes (6.86 MiB), pending peak 2; residual 0.
- TGS/costmap/policy среднее по всем кадрам снизилось с 21.25 до 4.09 ms;
эти средние включают unavailable-кадры. Модельные веса и detector filters
не менялись. Новые бюджеты для получения PASS не вводились.
### Что реально посчитано и чего этот результат не доказывает
75/128 кадров имели допустимую текущую cloud/pose пару; для них рассчитаны
online surface и TGS. На 53 кадрах — unavailable. В записи 43 камеры без нового
point increment и 39 с pose age >100 ms (множества пересекаются). Отдельно первый
кадр отвергнут из-за объединённых preroll increments возрастом до 408 ms —
это ограничение текущего адаптера, не доказательство неисправности сенсора.
Не следует повышать свежесть до «current» повторной маркировкой старого облака.
Опубликованы 398 метрических obstacle observations, из них 275 geometry-only;
детектор на данном отрезке выдавал `person`, `car`, `truck`. Рассчитаны motion
и simulated threats; 62 assessment имели decision=threat, но это **не**
подтверждённые физические угрозы или accuracy score. Cat/dog/близкий крупный
объект и регрессия RAV004 этим отрезком не квалифицированы. Повторяющиеся
наблюдения не считаются уникальными физическими объектами.
21 scene получили stale publication guard, в том числе 3 с допустимым входом,
устаревшим в вычислениях/очереди; ни у одной stale scene нет ALLOW. Body/threat
qualification относится к виртуальному corridor contract, не к полигону.
Scene lineage сохраняет реальные времена точек/pose; старые domain-observation
timestamps всё ещё привязаны к camera anchor. Общий layered freshness ABI
этапа 2 должен учитывать это явно, а не объявлять текущий адаптер финальным.
### Packaging и состояние системы
Native bases проверены по ранее зафиксированным image IDs. Сборка кода,
Pillow и C++ прошла, но экспорт нового полного image остановлен: у Docker
Desktop отсутствует compressed layer `0e5f8475…` исходного Triton, а NVCR
возвращает 403. Настройки/секреты авторизации не менялись. Pilot-assets (33.52 MB)
экспортированы из уже доступных локальных build files и подключены read-only
к работоспособной общей базе `sha256:664824aa25de1db178f177d67a81b01541a938b479812b9383ddbf03f6b59dbe`.
Веса также explicit read-only mounts. Это **не самостоятельный поставляемый
Docker** и не обход отсутствующего image-release gate. Для обычного образа
Dockerfile требует `--target pilot`; `--target pilot-assets` — только диагностика.
До model runs: 533 MiB VRAM, 0% GPU; прежние Mission Core GPU-сервисы временно
остановлены. После проб временных контейнеров нет; Triton и два Mission Core
worker agents восстановлены, Triton ready=200 / healthy, local Mac 8000=200.
Прежние transport/restart проблемы agents зафиксированы до остановки
(perception-worker 33 restarts, installed-agent 3), но не ремонтировались этим
пилотом. Frigate/Ollama остаются stopped/restart=no, данные сохранены.
### Evidence, проверки и следующий gate
Raw reports, scenes и subprocess logs:
`.runtime/perception-joint-pilot-20260901T2040Z/`; такие же run directories
сохранены на Worker под `D:/NDC_MISSIONCORE/runtime/experiments/`.
Команды, pins и ограничения — в `manifest.json` этого evidence directory.
| Артефакт | SHA-256 |
| --- | --- |
| joint-pilot-128-causal-history/report.json | `370bbd97dab1f7881614b18932e0b841dc791b9e5bfebf3e80707c7b437252e8` |
| joint-pilot-128-vectorized/report.json | `80d6843d793dea50a738a4bdf77dc3c6e1bd3a186b8dd58b89ba400611eaacd9` |
| joint-pilot-128-vectorized/scenes.jsonl | `d8cf53dee06cc08bd244d8676e8469a455f610328c44442f0192b2f37ed4afc7` |
| pilot_graph.py, финальная версия | `fd4d5307d4135250791cce73607c04c46f037481860cac691fc50d25e1daa1c4` |
| run_joint_pilot.py, финальная версия | `9ae8d62ea90a5140e90c09f035c29d933887392fe5d2d15635cf912c94d73808` |
| C++ pilot-tgs binary | `1ecb25a1db8dd90609754d4a98aa4ac24f50feb3023cf445a669c6c1cf72aa20` |
Focused verification: 86 tests (8 новых pilot + 51 contract + 27 existing
ingress/synchronizer/shadow); Ruff, включая Python 3.9 child target. Full
backend/frontend suite и field/physical-live не выполнялись.
**Итог gate:** совместный пилот как измерение выполнен; stage-1 performance
приёмка остаётся открытой. Следующая техническая работа — стоимость/джиттер
DDRNet и online surface, расписание единого графа при реальных burst intervals,
отдельные arrivals/age/unknown для модальностей и корректный preroll admission.
Не разрешены скрытый stride, замедление clock, повышение порогов или подмена
метрик. После этого повторить тот же bounded pilot, затем переходить к
standalone/transport этапа 2. Полная запись и автономный выезд не разрешены
самим фактом 128 успешных callback результатов.
## Дополнение 2026-09-02: снижение задержек, не отсечение тяжёлых профилей
Уточнение владельца: Observatory — долгосрочный экспериментариум переносимых
полных профилей для разных совместимых записей. Overload на 4090 допускается
как явно измеренный результат и не блокирует экспериментальную упаковку.
Functional readiness, качество и real-time qualification на конкретной связке
profile/config/hardware/source/transport фиксируются раздельно. Перенос на борт
может убрать внешнюю сеть, но не отменяет decode, IPC, queues и compute.
Это уточнение заменяет прежнюю зависимость «обязательно performance PASS до
развития этапа 2»; gates и запрет actuation при этом не ослаблены.
### Что изменено и проверено
- `pilot_ddrnet_runtime.py`: host/CUDA-event timing отдельно для H2D, model,
sigmoid, layout, argmax и D2H. Измерение модельного GPU-интервала не является
profiler-доказательством compute saturation: в нём возможны scheduling gaps.
- Исследованы перестановка scores перед argmax и локальный CUDA Graph capture
фиксированного FP32 DDRNet. Sigmoid сохранён: его saturated ties могут сделать
`argmax(logits)` неэквивалентным исходной модели. Реальная GPU tie-проверка и
все 128 mask hashes у обоих вариантов совпали с reference.
- `pilot_scheduler.py`: один GPU thread исполняет DDRNet → RF-DETR строго
последовательно, CPU association/surface/motion/TGS/policy обрабатывает
предыдущий кадр в исходном порядке. Второго профиля и GPU concurrency нет.
- Очередь завершённых GPU результатов и ingress делят **два pending места**;
output slot резервируется до следующего GPU вызова, поэтому скрытого
«третьего готового кадра в заблокированном put» нет. Активные входные payloads
остаются в общем 16 MiB учёте до окончания CPU; Docker ограничен 8 GiB.
- Backlog growth в последней версии учитывает ожидание и ingress, и GPU→CPU.
Старые raw reports не переписаны. Запись не замедлялась, кадры не прореживались,
веса, FP32 DDRNet, RF-DETR engine и алгоритмические пороги не изменены.
### Все семь прогонов, включая отрицательные
Одна исходная запись, первые 128 camera frames, original host-arrival clock 1×,
12.685 s camera window, те же raw points/pose/calibration и локальный collector.
Прогоны последовательные, короткие, не рандомизированные: это инженерный
эксперимент, не статистически завершённая performance qualification.
| Run | Results / 128 | Drops | Mean source→collector | p95 | p99 | Mean ingress wait |
| --- | --- | --- | --- | --- | --- | --- |
| reference-128, serial eager | 128 | 0 | 93.75 ms | 157.82 ms | 189.69 ms | 13.65 ms |
| channels-last-128, serial eager | 128 | 0 | 87.68 ms | 190.56 ms | 203.23 ms | 11.95 ms |
| cuda-graph-128, serial | 128 | 0 | 93.38 ms | 175.32 ms | 213.41 ms | 15.22 ms |
| overlap-cpu-128, fixed 1+1 slots | 127 | 1 | 79.57 ms | 121.21 ms | 131.42 ms | 4.74 ms |
| overlap-shared-128, shared 2 slots | 128 | 0 | 81.30 ms | 143.32 ms | 198.22 ms | 7.20 ms |
| serial-repeat-128 | 128 | 0 | 105.08 ms | 232.29 ms | 256.86 ms | 25.48 ms |
| overlap-shared-repeat-128 | 128 | 0 | 83.54 ms | 153.44 ms | 194.25 ms | 8.31 ms |
Вывод: layout и CUDA Graph не дали устойчивого whole-graph выигрыша; defaults
не изменены. Fixed-slot p95 ≤125 ms нельзя выдавать за успех — он потерял кадр
и провалил p99. Shared-budget overlap перспективен по очереди/средней задержке,
но его p99 хуже первого serial reference и лучше serial repeat. Поэтому нет
утверждения о гарантированном ускорении tails. Все варианты пока — явные flags
диагностического стенда; default остаётся serial/eager/reference.
У обоих shared-budget runs все 128 масок, source lineage, proposals,
метрические observations, tracks, threats, costmap states/materials и TGS counts
без `algorithm_ms` совпали с reference. Runtime age и соответствующая policy
естественно различаются и не исключаются из safety guard. В повторе 16 stale
scenes; ни одной с ALLOW. Все actuation/commands=false. Это output parity на
данном окне, не quality/accuracy acceptance.
Последний повтор: fresh cloud/pose 75/128, 398 metric observations; preroll и
layered freshness остаются открыты. Warmup 11.34 s, первый result 549.58 ms
от начала bounded preroll, stop 4.07 s; source-release max 8.72 ms. Pending
peak 2, input payload peak 5,202,376 bytes, residual bytes/slots 0; backlog
growth 0.224 ms. Sampled VRAM peak 1932 MiB; GPU utilization mean 34.88%,
max 83%; cgroup memory peak 6410.07 MiB (не только Python heap).
Таким образом, **«менее 5% загрузки GPU» не подтверждено**: доля VRAM и
GPU utilization — разные метрики. Исчерпание 24 GiB тоже не наблюдалось.
### Evidence и эксплуатационное состояние
Raw reports/scenes/logs, точные команды, SHA и пять code snapshots сохранены
в `.runtime/perception-latency-20260902T0020MSK/manifest.json`; копия evidence
на Worker: `D:/NDC_MISSIONCORE/runtime/experiments/perception-latency-20260902T0020MSK`.
Domain-code assets и model pins прежние; snapshots отражают именно версию
probe каждого прогона. Никакие raw reports не заменены «лучшими» результатами.
| Артефакт | SHA-256 |
| --- | --- |
| reference-128/report.json | `50ed0cb20ecafda6b011f4658a01629816fbd10b11a63f90196ea700738bf2b6` |
| overlap-shared-128/report.json | `76c9309fe6ef2281798b81c40e4f5c97b6289f9babe76f917db3f6bc3d7ffe7e` |
| serial-repeat-128/report.json | `28f0af8dec12db63de5142637d92245e31fac9389f16b9e845e68a8da1ee5acd` |
| overlap-shared-repeat-128/report.json | `e7fea02198c816f82e7f2a54d81ed0f1467867fb84bf5fe3252f06e9905cec2e` |
| overlap-shared-repeat-128/scenes.jsonl | `87b9a14091b37337a2a0df5f80b56b7ea3ec52fc4e0d369d88e77afe45a2dc10` |
| shared-budget-v2-code.tar.gz | `be7058b92352064580bca832e0db2f76c99485ad2f97e28cc4fa894dbf196860` |
91 focused tests PASS: 12 pilot, 52 contract, 27 existing live-ingress/
synchronizer/shadow. Ruff PASS, child lint с Python 3.9 target PASS. Mac не
выполнял модельной нагрузки. Full frontend/backend suite, другие маршруты,
network benchmark, physical-live и actuation в этих пробах не проверены.
Временный pilot-контейнер удалён штатно `--rm`. Три временно остановленных
Mission Core контейнера восстановлены; Triton ready=200, canonical Mac 8000=200.
Frigate/Ollama по решению владельца остаются exited/restart=no; reboot не
выполнялся. Продуктовые defaults, registry и backend/frontend не переключены.
Standalone image и приложение↔Worker transport всё ещё не реализованы этим
стендом. Следующая работа — preroll/layered freshness, общий runtime с
измеряемыми очередями и реальный binary data plane; performance FAIL на 4090
не повод выбросить профиль или остановить развитие экспериментариума.