- tools/make_benchmark.py: аугментации спавна d_start ∈ [40, 200] м, боковой дрейф v_lat, шум продольной координаты δs (устранение инверсии s_std), сценарии стоянки; - flyguard/lobula.py: зонный пол h_lo_core = 0.16 м в межрельсовой колее, динамическое расширение габарита в кривых W_eff(d) по Corridor.sigma(d), отсечение плоскости настила платформы; - flyguard/central_complex.py: поддержка лежащих препятствий в колее без штрафа за вытянутость формы; - flyguard/synth.py: добавлен класс «человек_лежа» (1.8×0.5×0.3 м); - flyguard/descending.py: дальний мягкий канал предупреждения на дистанциях >90 м; - flyguard/export.py: экспорт детекций в 3D BBox, уровни угрозы, маркеры RViz MarkerArray; - tests/test_pipeline.py, tests/run_tests.py: 38 юнит-тестов и автономный раннер.
24 KiB
Комплексный план развития FlyGuard · Хакатон ЛЦТ-2026 (Кейс 05)
Документ фиксирует согласованную архитектурную стратегию и детальный план доработок ядра FlyGuard для максимизации качества решения на хакатоне ЛЦТ-2026 («Обнаружение посторонних объектов в тоннеле метро по данным 3D-лидара»).
1. Контекст и текущее состояние
Система FlyGuard построена на принципах зрительной системы и грибовидных тел дрозофилы:
облако точек 0.3–0.9 млн, 10 Гц
│
├─ RETINA омматидиальная решётка → дальностный образ 128 × N
├─ HALTERES плоскость рельсов: крен, тангаж, высота сенсора
├─ LAMINA диспаритет 1/R → ON/OFF, центр-окружение на 3 масштабах
├─ MEDULLA / LP T4/T5 → LPTC: скорость поезда без одометрии
├─ LOBULA LC11: кандидаты; разрез компоненты по контрасту
├─ MUSHROOM BODY KC → APL → MBON: новизна формы (без меток)
│ + обученное считывание MBON (с метками из физики)
├─ FAN-SHAPED BODY накопление лучей в координатах пути
├─ CENTRAL COMPLEX накопление улик в координатах пути, треки
└─ DESCENDING два порога с гистерезисом → решение
Текущие измеренные показатели (Ryzen 7600X, Hesai Pandar128):
- Реальный объект на 55 м (
doubleT_obstacle): 99.5 % кадров с детекцией. - Задержка кадра на CPU: 32–33 мс при лимите 100 мс (запас ~3×).
- Ложные тревоги (leave-one-bag-out): 8.0 трека/км (11.0 % кадров).
- Дальность обнаружения стоящего человека: рабочая дальность 100 м, P@50 = 0.70, P@100 = 0.53, P@150 = 0.33.
Главные узкие места, выявленные в ходе анализа:
- Застревание улики трека на 120–160 м:
На 150 м кандидат появляется в ~62 % кадров, но от предмета приходит лишь 4–6 лучей. Покадровое считывание MBON выдаёт малое
p \approx 0.05(из-за априорной редкости), улика прирастает на+gain \cdot w \approx +0.017, а утечка за промах отнимает-leak = -0.12. В итогеevidenceколеблется около нуля, и порог0.5никогда не пробивается. - Артефакты синтетики в
TrackReadout: Признакиs_{std}иu_{std}перевернулись на реальном объекте (0.365м у человека против0.013м у фона) из-за того, что в синтетике предмет спавнился строго неподвижно относительно оценки движения, а в реальности поезд стоял, а человек двигался. Признакиclosedиageзаучили фиксированный спавн с 200 м. В текущей версииtrack_blend = 0.0(выключен). - Неиспользуемая неопределённость коридора
Corridor.sigma: Ось коридора на кривой за горизонтом видимости (62–107 м) продолжается по касательной и расходится с истинным путем до 1.19 м на 150 м. Полуширина габарита жестко зафиксирована на1.6м, поэтому крайние предметы выпадают из маски габарита ещё до кластеризации. - Потеря кандидатов вдали (38 % пропусков на 120–160 м):
Порог
min\_rays\_far = 3отсекает кадры с 1–2 лучами. При этом накопительFanBodyза 15 кадров аккумулирует в вокселе десятки попаданий, но никак не помогает формированию покадрового кандидата. - Скачок ложных тревог на новой линии (
8.0 \to 20.3треков/км): На незнакомом участке без обученной долговременной памяти штатные лотки и кронштейны получают высокую новизну. Габарит сейчас однороден и одинаково доверяет предметам на путях и конструкциям на стенах. - Слипание с платформой на 30–50 м:
При смещении предмета
+0.9м к кромке платформы он объединяется с ней в связную компоненту длиной>15м и выбрасывается правиломmax\_depth. - Отсутствие лежащего человека:
В
catalogueнет класса лежащего человека, а нижняя граница габаритаh_{lo} = 0.28м срезает упавшего на рельсы пассажира.
2. Поэтапный план улучшений
┌─────────────────────────────────────────────────────────────┐
│ ФАЗА 1: Устранение артефактов данных и синтетики │
│ • Аугментация спавна, движения, стоянки в make_benchmark │
│ • Добавление «человек_лежа» (1.8×0.5×0.3 м) │
│ • Честное переобучение и валидация TrackReadout │
└──────────────────────────────┬──────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ ФАЗА 2: Пробитие дальней зоны (100–160 м) │
│ • Двухканальное решение в DescendingNeurons (soft-warning) │
│ • Динамическое расширение габарита по Corridor.sigma(d) │
│ • Обратная связь FanBody → Lobula (порог 2 луча по опоре) │
└──────────────────────────────┬──────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ ФАЗА 3: Обобщаемость на новый тоннель и ближняя зона │
│ • Иерархический габарит (опасная колея vs стены/лотки) │
│ • Вычитание плоскости платформы (h~1.1 м, |u|>1.2 м) │
│ • Понижение h_lo до 0.16 м в межрельсовой зоне │
└──────────────────────────────┬──────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ ФАЗА 4: ROS2 Экспорт, Замеры и Протокол Стенда Жюри │
│ • Модуль экспорта: 3D Bounding Boxes, Threat Level, RViz │
│ • Сквозной Leave-one-bag-out замер и стресс-тест на CPU │
└─────────────────────────────────────────────────────────────┘
Фаза 1: Устранение артефактов данных и синтетики
Цель: сделать выборку для обучения TrackReadout инвариантной к реальным условиям, исключить переобучение на искусственные признаки генератора.
1.1. Аугментация генерации в tools/make_benchmark.py
- Случайная дальность спавна: вместо жесткой фиксации на
d_{start} = 200м задаватьd_{start} \sim \mathcal{U}(40, 200)м. Это разрушает фиктивную корреляцию между истинностью объекта и признакамиclosed = \ln(d_{start}/d)иage. - Поперечное и продольное блуждание объекта:
- Случайная скорость движения человека:
v_{lat} \sim \mathcal{U}(-0.4, 0.4)м/с. - Реалистичный шум положения
\delta s \sim \mathcal{N}(0, \sigma_s^2)с\sigma_s = 0.15м (моделирует погрешность одометрии и скольжение лучей по телу). - Благодаря этому
s_{std}иu_{std}перестают быть нулевыми для синтетических объектов.
- Случайная скорость движения человека:
- Сценарии со стоящим поездом:
- Генерация препятствий в кадры со стоящим поездом (
v = 0, например, кадры бэгаdoubleT_obstacle), чтобы модель не считала нулевую скорость признаком отсутствия опасности.
- Генерация препятствий в кадры со стоящим поездом (
1.2. Добавление класса «человек_лежа» в catalogue
- Добавить геометрию:
"человек_лежа": ObjectModel("человек_лежа", [ Box(length=1.80, width=0.50, height=0.30, h_base=0.0) ], reflectivity=0.35) - В
_qualityучесть, что вытянутость вдоль пути (depth \approx 1.8м) при малой высоте (h \le 0.35м) и расположении в межрельсовой зоне (|u| \le 0.75м) не должна штрафоваться множителемcompact.
1.3. Переобучение TrackReadout
- Собрать обновлённый кэш треков:
python tools/make_benchmark.py --memory artifacts/mushroom_body.npz \ --mbon-dir artifacts/mbon_folds --tracks-out data/cache/track_set_aug.npz - Обучить
TrackReadoutс контролем ROC AUC по всем полосам (целевой AUC>0.90на полосе 110–160 м). - Проверить валидацию на
doubleT_obstacle: оценкаp_{track}для реального объекта должна составлять> 0.95.
Фаза 2: Пробитие дальней зоны (100–160 м)
Цель: гарантировать, что появляющиеся на 120–160 м кандидаты и треки превращаются в надежное раннее предупреждение, не застревая в утечке leak.
2.1. Двухканальное решение в DescendingNeurons
Биологическая аналогия: разделение Giant Fiber (DNp01, жесткий аварийный порог) и DNp02/DNp11 (раннее предупреждение).
# 1. Аварийный канал (Giant Fiber / DNp01) — экстренное торможение
is_emergency = (t.evidence >= self.emergency_evidence) and (d <= stopping_distance)
# 2. Канал раннего предупреждения (DNp02 / DNp11) — ранний soft-warning
is_far_warning = (d > 90.0) and (t.hits >= 2) and (t.p_track >= 0.65)
is_near_warning = (d <= 90.0) and (t.evidence >= self.warn_evidence)
decision.detected = is_emergency or is_far_warning or is_near_warning
decision.emergency = is_emergency
- Безопасность: далёкий ложный трек физически не может вызвать экстренное торможение (
emergency = False), но даёт заблаговременное оповещение на 150 м.
2.2. Динамический габарит по Corridor.sigma(d)
В geometry.py уже заложен расчет неопределенности оси:
\sigma(d) = base + rate \cdot d + 0.02 \cdot \max(d - d_{max\_seen}, 0)
На 150 м \sigma(d) \approx 1.1 м.
В функции gauge_mask в flyguard/lobula.py:
W_{eff}(d) = half\_width + k_\sigma \cdot \sigma(d), \quad k_\sigma = 0.8
- На 50 м:
W_{eff} = 1.6 + 0.8 \cdot 0.45 \approx 1.96м. - На 150 м:
W_{eff} = 1.6 + 0.8 \cdot 1.10 \approx 2.48м. - На кривых радиуса 1300 м объект у внешнего рельса больше не вываливается за границу габарита. Стены тоннеля, попадающие в расширенную зону, отсекаются признаками
containmentи низкимp_{mbon}.
2.3. Обратная связь FanBody \to find_candidates
FanBody аккумулирует лучи в воксельной сетке пути 2.0 \times 0.2 \times 0.25 м.
В find_candidates вводится адаптивный порог числа лучей:
# Если в данной точке пространства накопитель уже видит стабильную опору:
support = fan.support_at(d_comp, u_comp) if fan is not None else 0.0
need_rays = 2 if (d_comp >= 90.0 and support >= 0.6) else (3 if d_comp >= 90.0 else 4)
- Снижает пропуски кандидатов на 120–160 м с 38 % до
< 15% для слабых эхо.
Фаза 3: Обобщаемость на незнакомой линии и ближняя зона
Цель: снизить ложные тревоги на незнакомой линии с 20.3 до <10 треков/км и закрыть потерю объектов возле платформ на 30–50 м.
3.1. Иерархический двухзонный габарит (Core vs Peripheral)
Сечение пути разделяется на две зоны ответственности:
\text{Зона 1 (Колея)}: |u| \le 0.90\,\text{м}, \quad h \in [0.16, 1.90]\,\text{м}
\text{Зона 2 (Периферия)}: 0.90 < |u| \le 1.60\,\text{м} \quad \text{или} \quad h > 1.90\,\text{м}
- Для Зоны 1 (Колея):
- Нижний порог
h_{lo}снижается до0.16м (захватывает лежащего человека). - Множитель компактности в
_qualityне штрафует протяженностьdepthдо2.0м при малой высоте (h \le 0.35м). - Максимальный приоритет подтверждения трека (
gain = 0.40).
- Нижний порог
- Для Зоны 2 (Периферия):
- Пространство кабельных лотков, сигналов и кронштейнов.
- Требование опоры на полотно:
grounded \le 0.3для висящих на стенах объектов подавляет их прирост улики. - Для формирования трека требуется
p_{mbon} \ge 0.50. - Результат: на незнакомой линии ложные срабатывания от кабелей и ниш падают с
20.3до< 10.0на км.
3.2. Геометрическая сегментация платформы на 30–50 м
Платформа в метро имеет стандартизованные параметры: высота 1.10 \pm 0.05 м над рельсом, удаление |u| \ge 1.35 м.
В find_candidates:
# Маска горизонтальной поверхности платформы:
platform_mask = (np.abs(lat) > 1.30) & (tf.h >= 1.05) & (tf.h <= 1.25)
# Исключаем лучи настила платформы из контекста связности:
clustering_context = context & ~platform_mask
- Препятствие у края габарита (
u = +0.9м) больше не сшивается с настилом платформы в длинную 20-метровую стену и не отбрасывается поmax\_depth.
Фаза 4: ROS2-интеграция, API и валидация
Цель: подготовить модуль для интеграции с ребятами, делающими ROS2-узел, сохранив ядро 100 % независимым и легковесным (чистый CPU, NumPy/SciPy).
4.1. Модуль экспорта для ROS2 (flyguard/export.py)
Обеспечивает чистый интерфейс между ядром и ROS2-нодой:
- Преобразование треков
Trackв 3D Bounding Boxes в координатах сенсора(x, y, z, dx, dy, dz, yaw). - Расчет метрик безопасности:
distance_along_track(d, м)lateral_offset(u, м)ttc(Time to Collision, с)threat_level:CLEAR,WARNING,EMERGENCY.
- Генератор структуры для публикации
visualization_msgs/MarkerArray:- Bounding boxes с цветовым кодированием (красный — авария, желтый — предупреждение).
- 3D линия оси коридора тоннеля.
- Текстовые метки с расстоянием и скоростью сближения над объектами.
4.2. Контроль рантайма на CPU
- Зависимости: строго
numpyиscipyв рантайме. - Бюджет времени:
- Новые операции (lookup в FanBody, проверка 2 зон, маска платформы) выполняются на готовых массивах NumPy и добавляют
< 1.2мс. - Ожидаемая медиана на Ryzen 7600X:
\approx 34мс, на стенде жюри (i7-9700E):\approx 70–75мс при лимите 100 мс.
- Новые операции (lookup в FanBody, проверка 2 зон, маска платформы) выполняются на готовых массивах NumPy и добавляют
3. Сводная таблица ожидаемых метрик
| Метрика | До улучшений | После реализации | Ключевой фактор роста |
|---|---|---|---|
Реальный объект на 55 м (doubleT_obstacle) |
99.5 % | > 99.5 % | Сохранение надежности канала Giant Fiber |
| P@150 м (стоящий человек) | 0.33 | > 0.50 | Двухканальное решение по p_{track} + \sigma(d) |
| P@100 м (сидящий человек / чемодан) | 0.34 / 0.21 | > 0.55 / 0.40 | Порог 2 лучей при накоплении в FanBody |
| P@50 м (лежащий человек) | 0.0 % (срезался) | > 0.75 | h_{lo} = 0.16 м в колее + класс в каталоге |
| Ложные тревоги (незнакомая линия) | 20.3 на км | < 10.0 на км | Двухзонный габарит (периферия фильтрует лотки) |
| Обнаружение у платформ (30–50 м) | 66 % (потеря 34 %) | > 90 % | Сегментация настила платформы до кластеризации |
| Время кадра (CPU) | 33 мс | < 36 мс | Векторизованный NumPy/SciPy без оверхеда |
4. Дорожная карта выполнения (Action Checklist)
- Шаг 1 (Данные и синтетика):
- В
flyguard/synth.pyдобавлен классчеловек_лежа. - В
flyguard/central_complex.pyскорректирован расчетcompactв_qualityдля лежащих на путях объектов. - В
tools/make_benchmark.pyдобавить случайныйd_{start}, поперечный дрейфv_{lat}, шум\delta sи статичные кадры. - Пересобрать кэш
track_set.npzи переобучитьTrackReadout.
- В
- Шаг 2 (Дальняя зона):
- В
flyguard/descending.pyимплементирован двухканальный триггер soft-warning (DNp02/DNp11) дляd > 90м. - В
flyguard/lobula.pyприменён динамический габаритW_{eff}(d) = half\_width + k_\sigma \cdot \max(\sigma(d) - 0.25, 0.0). - Оптимизированы параметры
warn_far = 0.35иleak_far = 0.06для предотвращения вымывания улики на дальности.
- В
- Шаг 3 (Зонирование и платформы):
- Двухзонный габарит: колея (
|u| \le 0.85м,h_{lo\_core} = 0.16м) и периферия (h_{lo} = 0.28м). - Геометрическая фильтрация настила платформ (
|u| \ge 1.30м,h \in [1.05, 1.25]м) для устранения слипания объектов со стенкой.
- Двухзонный габарит: колея (
- Шаг 4 (ROS2 и валидация):
- Создан модуль экспорта
flyguard/export.py: расчетBoundingBox3D,ThreatLevel(CLEAR/WARNING/EMERGENCY),TTCи генерация структур дляvisualization_msgs/MarkerArray. - Добавлены и успешно пройдены 35 модульных тестов (
tests/test_pipeline.py). - Запустить сквозную валидацию и зафиксировать метрики в
docs/EXPERIMENTS.md.
- Создан модуль экспорта