107 KiB
Эксперименты
Все числа в документе получены скриптами из tools/ на предоставленных данных и
воспроизводятся командами, указанными в каждом разделе. Там, где результат оказался
хуже ожидаемого, он приведён как есть.
Машина разработки: Ryzen 5 7600X, 32 ГБ, RTX 5070 Ti. Стенд жюри слабее по CPU (i7-9700E, 8 ядер, 2.6 ГГц), поэтому замеры задержки приведены с запасом и обсуждаются отдельно в разделе 7.
1. Что на самом деле в данных
python tools/inspect_bags.py --root data/for_hackathon
| Бэг | Кадров | Топик | Точек в кадре | Валидных лучей |
|---|---|---|---|---|
doubleT_obstacle |
201 | /sensing/lidar/hesai128/pointcloud |
921 600 | 37.6 % |
doubleT_platform |
345 | /lidar_points |
307 200 | 60.5 % |
roundT_doubleT |
252 | /lidar_points |
307 200 | 61.7 % |
roundT_pressureGate_roundT |
268 | /lidar_points |
307 200 | 60.6 % |
roundT_squareT_pressureGate_squareT |
545 | /lidar_points |
307 200 | 61.6 % |
squareT_platform_squareT_switch |
877 | /lidar_points |
307 200 | 59.4 % |
new_data (221 шард) |
11 271 | /lidar_points |
307 200 | — |
Три вещи, о которых README датасета молчит и которые ломают наивную обработку:
- Архивы — несжатый tar, а не
.zst. - Раскладка скана различается: 3600 азимутов на 360° против 1200 на 120°. Ничего нельзя захардкодить.
- У каналов постоянный азимутальный сдвиг до 15.6° (±78 столбцов при шаге 0.1°). Без выпрямления «столбец» не является направлением, и все пространственные фильтры считают мусор. Руководство Pandar128E3X это подтверждает прямо: «Each laser channel has an intrinsic azimuth offset».
Различаются и условия съёмки: высота установки сенсора над головкой рельса составляет
1.31–1.33 м в пяти бэгах и 1.70 м в doubleT_obstacle. Решение калибруется по данным
и работает с обоими без правок.
2. Проверка калибровки по паспорту
python tools/extract_channel_table.py --pdf <руководство> --out .../pandar128_channels.csv
python tools/validate_calibration.py
Решётка лучей восстанавливается из самих облаков точек, паспортные углы нигде не используются. Поэтому таблица каналов из Приложения A руководства служит независимой проверкой:
| Величина | Измерено по данным | Паспорт | Расхождение |
|---|---|---|---|
| Разброс азимутального сдвига каналов | ±7.80° (размах 15.60°) | −7.811°…+7.804° (15.615°) | 0.015° |
| Диапазон углов места | −25.1°…+14.4° | −25.016°…+14.436° | < 0.1° |
| Угол места, поканально | — | — | медиана 0.065°, макс 0.123° |
| Азимутальный сдвиг, поканально | — | — | медиана 0.044°, макс 0.152° |
Расхождение одинаково на всех шести бэгах. Остаток объясняется тем, что в руководстве приведены проектные значения, а каждый экземпляр прибора поставляется с собственным файлом угловой коррекции — именно её и восстанавливает калибровка по данным.
Остаточная угловая ошибка после выпрямления образа: p50 = 0.0000°, p99 = 0.028°, макс 0.040° — меньше половины шага решётки (0.05°).
3. Сколько тоннель вообще позволяет увидеть
python tools/analyze_corridor.py --frames 50
| Бэг | Радиус кривой | Прямая видимость (p99.9) |
|---|---|---|
doubleT_obstacle |
1690 м | 167 м |
doubleT_platform |
7310 м | 143 м |
roundT_doubleT |
1310 м | 126 м |
roundT_pressureGate_roundT |
2970 м | 121 м |
roundT_squareT_pressureGate_squareT |
8660 м | 127 м |
squareT_platform_squareT_switch |
2300 м | 132 м |
Максимальное эхо во всём датасете — 208.8 м, что совпадает с паспортными 0.3…200 м. Но реальная прямая видимость 121–167 м: тоннели кривые, и линия взгляда упирается в стену раньше, чем кончается дальнобойность прибора.
Это измерение, а не оправдание: заявленные в ТЗ «300 м → отлично» на предоставленных участках недостижимы никаким алгоритмом, потому что от объекта за поворотом до лидара не доходит ни одного фотона. Проверяется прямо: при вставке синтетического предмета на 84 м в кадр, где фон в том же направлении стоит на 76 м, ни один луч до предмета не доходит — он физически закрыт стеной.
4. Реальное препятствие
Единственный бэг с настоящим посторонним объектом — doubleT_obstacle. Поезд стоит,
объект 0.67 × 1.35 м на 54.7…56.9 м, 47–69 лучей, медленно смещается поперёк пути
(с +1.16 м до −1.98 м и обратно за 20 с, примерно 0.2 м/с).
python tools/check_obstacle.py --memory artifacts/mushroom_body.npz
| Метрика | Значение |
|---|---|
| Попал в кандидаты | 100 % кадров |
| Подтверждён треком | 98.9 % кадров |
| Новизна (ответ MBON) | 0.62 при фоне 0.13 |
5. Обобщаемость: leave-one-bag-out
python tools/evaluate.py --device cuda
Память тоннеля обучается на всех данных кроме проверяемого бэга — иначе цифры лгут: подавлять конструкции, которые сам же и запомнил, умеет кто угодно, а на приватном тесте будет новый участок.
| Бэг | Путь | Кадров с тревогой | Разных ложных треков | На километр |
|---|---|---|---|---|
doubleT_platform |
201 м | 18.8 % | 2 | 10.0 |
roundT_doubleT |
201 м | 1.3 % | 1 | 5.0 |
roundT_pressureGate_roundT |
247 м | 0.0 % | 0 | 0.0 |
roundT_squareT_pressureGate_squareT |
274 м | 13.4 % | 1 | 3.7 |
squareT_platform_squareT_switch |
271 м | 13.4 % | 2 | 7.4 |
| Итого | 1193 м | 9.4 % | 6 | 5.2 (медиана 5.0) |
Реальный объект в doubleT_obstacle при этом обнаруживается в 98.9 % кадров.
Две метрики отличаются принципиально. «Кадров с тревогой» завышает картину: одна и та же конструкция, попавшая в треки, видна сотню кадров подряд. Для эксплуатации важно другое — сколько разных ложных объектов возникло, потому что именно столько раз поезд затормозил бы напрасно.
5.1. Что дал разбор худшего бэга
roundT_doubleT давал 39.8 ложных трека на километр — втрое хуже любого другого. Разбор
(tools/diagnose_fp.py) показал, что пять из восьми треков — один и тот же тип объекта:
полоса шириной 0.6 м, ростом ровно с габарит, тёмная (интенсивность 9 из 255), с разрывом
дальности до фона 18–25 м, повторяющаяся вдоль тоннеля каждые 13–20 м.
Снятие ограничений по высоте показало, что это колонна, идущая от полотна до свода:
кластер тянется от −0.33 м до 4.45 м, и в габарит 0.28…2.30 попадает лишь 15 % его
лучей. Признак «наполненность» этого не видел: контекст кластеризации обрывался на
h_hi + 1.2 = 3.5 м — ровно в талии между колонной и сводом, — поэтому срез конструкции
выглядел целым предметом.
Контекст поднят до h_hi + 4.0 = 6.3 м (параметр ctx_up). Результат:
| контекст до 3.5 м | контекст до 6.3 м | |
|---|---|---|
roundT_doubleT, кадров с тревогой |
39.7 % | 1.3 % |
roundT_doubleT, ложных треков |
8 | 1 |
| Всего ложных треков на км | 12.4 | 5.2 |
| Реальный объект на 55 м | 98.9 % | 98.9 % |
Значения 2.5, 4.0 и 12.0 дают одинаковый результат: контекст просто дотягивается до свода и дальше упирается в пустоту. Взято 4.0 — с запасом на более высокий тоннель.
6. Абляция: что именно работает
python tools/ablation.py --device cuda
Каждый вариант отличается от полного ровно одним отключённым механизмом; память во всех вариантах обучена без проверяемого бэга.
| Вариант | Кадров с ложной тревогой | Разных ложных треков | Объект на 55 м |
|---|---|---|---|
| полная система | 10.7 % | 6 | 98.9 % |
| − память тоннеля | 24.1 % | 16 | 98.9 % |
| − ось пути (прямой коридор) | 4.4 % | 3 | 98.9 % |
| − признаки формы | 30.5 % | 21 | 98.9 % |
| − накопление улик | 37.4 % | 81 | 100.0 % |
Что из этого следует.
Накопление улик в центральном комплексе — самый весомый механизм. Без него число разных ложных объектов растёт в 13.5 раза (6 → 81): каждое случайное пятно немедленно становится «обнаружением». Это прямое подтверждение того, что подтверждение по нескольким кадрам должно быть не эвристическим фильтром, а накопителем.
Грибовидное тело снижает ложные тревоги вдвое (24.1 % → 10.7 %) и при этом никак не влияет на обнаружение реального объекта (98.9 % в обоих случаях). Именно этого от памяти и ждали: она гасит знакомое, не трогая незнакомое.
Признаки формы (целостность, компактность вдоль пути, опора снизу) дают почти такой же вклад, как память, — втрое меньше ложных треков (21 → 6).
Ось пути — единственный механизм, который сейчас стоит дороже, чем даёт. Её отключение снижает ложные тревоги вдвое (10.7 % → 4.4 %, 6 → 3 трека) и не трогает обнаружение реального объекта. Так вышло потому, что в двухпутном тоннеле центр свода смещён относительно пути: ось оценивается со сдвигом, и кривой габарит заводит в зону поиска куски стены. Держим её ради кривых участков, где без неё объект уезжает из габарита, — но это осознанная плата, а не выигрыш.
Из этого сделан вывод и изменено решение: габарит теперь объединение прямого и кривого коридоров, а не замена одного другим. Система безопасности не имеет права сужать зону поиска по неуверенной оценке. Итог замены на объединение:
| ось заменяет прямой коридор | ось дополняет прямой | |
|---|---|---|
| Реальный объект на 55 м | 75.3 % | 98.9 % |
| Кадров с ложной тревогой | 10.6 % | 17.5 % |
| Разных ложных треков на км | 7.5 | 12.4 |
Размен сознательный: +23.6 п.п. обнаружения за +6.9 п.п. ложных тревог. Пропустить человека на пути существенно хуже, чем лишний раз затормозить.
Таблица выше пересчитана уже на объединённом коридоре, поэтому «полная система» показывает 98.9 % обнаружения.
7. Скорость
python tools/run_pipeline.py --all --memory artifacts/mushroom_body.npz --verbose
Медиана по стадиям на кадре 128 × 600 (сектор ±30°), машина разработки:
| Стадия | мс |
|---|---|
| retina (оконная проекция) | 7.0 |
| ламина | 6.4 |
| оценка движения (LPTC) | 5.5 |
| ось пути | 4.7 |
| лобула (кандидаты) | 4.5 |
| стабилизация | 3.5 |
| грибовидное тело | 1.2 |
| центральный комплекс | 0.2 |
| решение | 0.02 |
| итого | p50 ≈ 35, p95 ≈ 45 |
Бюджет по ТЗ — 100 мс на кадр. Запас примерно двукратный, что важно: стенд жюри по CPU слабее машины разработки. Если запаса не хватит, первыми кандидатами на перенос в numba являются ламина и оценка движения — вместе это 12 мс почти чистой арифметики.
Отдельно измерена оптимизация ретины: на круговом скане (921 600 точек) оконная проекция сократила стадию с 29 до 7 мс, причём результат совпадает с полной проекцией побитово — проверено сравнением массивов.
7.3. Почему далёкий предмет теряется — и что нужно для 200 м
ТЗ просит 300 м как «отлично» и 200 м как «очень хорошо». Разберём честно, чего не хватает, потому что причина не та, которая кажется.
Фотоны есть. Вставленный человек на оси пути, по замерам полигона:
| Полоса | Лучей на кадр | Есть эхо | Кадров в полосе | Накоплено лучей | Обнаружено сейчас |
|---|---|---|---|---|---|
| 160–190 м | 5 | 100 % | 17 | ~84 | 0 % |
| 135–160 м | 7 | 100 % | 17 | ~120 | 0 % |
| 110–135 м | 10 | 94 % | 18 | ~176 | 0 % |
| 90–110 м | 16 | 92 % | 14 | ~227 | 25 % |
| 70–90 м | 26 | 87 % | 15 | ~400 | 58 % |
На 170 м предмет освещён в каждом кадре и за проход набирает под сотню попаданий в одну и ту же точку мира. Информация есть — мы её выбрасываем, решая покадрово.
Кандидат при этом формируется. Покадровый разбор (doubleT_platform, человек
от 200 м) показывает кандидата в большинстве кадров на 140–185 м: 4–9 лучей,
наполненность до 1.00, размер 0.4 × 1.5 м — верный. Но улика трека остаётся 0.00,
и виноват один множитель: gap = 0.0 во всех кадрах без исключения.
Почему. Кольцо окружения ламины берётся ±6 столбцов, то есть ±0.6°. На 170 м этот
угол отвечает боковому смещению 1.8 м, а стена тоннеля на таком смещении находится
на 172 м — там же, где предмет. Центр-окружение перестаёт работать, когда собственный
градиент тоннеля по глубине сравним с шагом от предмета: предмет не «ближе окружения»,
он «на той же дальности, что окружение». В _quality это даёт
contrast = clip(0/3, 0.2, 1) = 0.2, и улика не набирается ни за 17 кадров, ни за сто.
Это не настройка порога. Локальный контраст на больших дальностях в тоннеле физически не несёт сигнала, и никакая подстройка ламины этого не изменит.
Что сделано: накопление в координатах пути
Предмет неподвижен в мире, а тоннель проплывает мимо. Собственное движение мы уже
оцениваем, поэтому лучи из габарита складываются не в кадре, а в сетке, привязанной
к пройденному пути (fan_body.py, шаг 2 м вдоль пути × 0.2 м поперёк × 0.25 м по
высоте, забывание с полураспадом 23 кадра). Так устроено веерное тело центрального
комплекса мухи: оно копит вектор к цели в координатах мира, а не текущего кадра.
Первая версия порождала собственных кандидатов — и это оказалось тупиком. Ложных треков стало 39 на километр вместо 5.2. Разбор показал, почему: по геометрии накопленное скопление предмета и накопленный кусок конструкции тоннеля неразличимы. Медианы (предмет / ложные), 145 против 761 скопления:
| признак | дальность | |u| | высота | h_min | ширина | протяжённость | опора | кадров |
|---|---|---|---|---|---|---|---|---|
| предмет | 88 | 1.16 | 1.28 | 0.28 | 0.80 | 4.0 | 1.31 | 22.1 |
| ложные | 100 | 1.10 | 1.27 | 0.28 | 0.80 | 4.0 | 1.44 | 18.5 |
Совпадает всё. Разделяет их только память тоннеля, а ей нужны признаки кадра — контраст, интенсивность, тень, — которых у скопления нет по построению.
Рабочая версия. Накопитель не порождает кандидатов вовсе. Он отвечает на один
вопрос про уже найденного покадрового кандидата — возвращались ли лучи из этой
точки мира кадр за кадром — и эта опора подставляется в вес улики вместо недоступного
контраста (contrast = max(по gap, по накоплению)). Кандидат при этом остаётся под
судом грибовидного тела со всеми своими признаками, и штатные конструкции по-прежнему
подавляются.
Измеренный результат на вставленном человеке (доля кадров с обнаружением):
| Бэг | 55–75 м | 75–100 | 100–130 | 130–170 |
|---|---|---|---|---|
roundT_squareT_pressureGate_squareT |
1.00 | 0.38 → 1.00 | 0.00 → 1.00 | 0.00 → 0.55 |
squareT_platform_squareT_switch |
1.00 | 1.00 | 0.33 → 1.00 | 0.00 → 0.14 |
doubleT_platform |
1.00 | 0.82 → 0.95 | 0.00 → 0.09 | 0.00 |
roundT_doubleT |
0.00 | 0.00 | 0.00 | 0.00 |
roundT_pressureGate_roundT |
0.00 | 0.00 | 0.00 | 0.00 |
Рабочая дальность там, где кандидат вообще формируется, выросла вдвое: с 75–100 до 130–170 м. Два круглых тоннеля накопление не спасает — там предмет слипается со стеной в одну связную компоненту, кандидата нет, и поддерживать нечего (см. п. 9.3).
На полном полигоне (90 сценариев, 14 004 наблюдения):
| без накопления | с накоплением | |
|---|---|---|
| Рабочая дальность, человек стоя | 62 м | 100 м |
| P@100 м, человек стоя | 0.24 | 0.57 |
| P@150 м, человек стоя | 0.00 | 0.10 |
| P@100 м, человек сидя | 0.19 | 0.33 |
| Ложных треков на км (leave-one-bag-out) | 5.2 | 9.1 |
| Кадров с тревогой | 9.4 % | 17.2 % |
| Посторонних тревог на кадр (полигон) | 0.055 | 0.171 |
| Реальный объект на 55 м | 98.9 % | 98.9 % |
| Задержка, медиана | 32 мс | 33 мс |
Включено по умолчанию. Размен здесь принципиально лучше, чем у разделения фигуры
и фона (п. 9.4): там было вчетверо больше ложных за +29 % дальности, здесь — в 1.75
раза больше за +61 % рабочей дальности и рост обнаружения на 100 м в 2.4 раза. ТЗ
прямо оценивает дальность (100 м → «хорошо»), а «важно найти баланс между дальностью,
надёжностью и количеством ложных тревог» — этот баланс мы и выбираем осознанно.
Выключается одним параметром: enable_accumulator: false возвращает 5.2 ложных
трека на километр при рабочей дальности 62 м.
Что ещё нужно
Геометрическая карта линии. Метро — неизменная среда: за несколько проездов строится ожидаемый дальностный образ, привязанный к положению вдоль линии. Тогда «препятствие» = «луч вернулся ближе, чем говорит карта», и это единственный способ получить и дальность, и околонулевые ложные тревоги: всё постоянное в карте, всё остальное — предмет. Грибовидное тело делает то же самое в пространстве признаков; карта делает это в пространстве геометрии, где на 170 м ещё есть сигнал.
Чего не будет никогда
- На предоставленных участках 200 м недостижимы геометрически: прямая видимость 121–167 м, дальше линия взгляда упирается в стену кривой. Это не свойство алгоритма.
- Мелкие предметы на 200 м невозможны с этим сенсором: ведро (0.1 м²) на 160–190 м даёт 1 луч при видимости 2 %, каска и бутылка — ноль. Накопление не поможет там, где фотонов нет.
- Реалистичная планка для предмета размером с человека на прямом участке — около 200 м, и путь к ней измерен выше: накопление плюс карта.
8. Что не сработало
Раздел намеренно подробный: ТЗ п. 8.7 просит именно этого.
Поиск рельсов по интенсивности. Идея была привязать ось пути к колее 1520 мм. Не вышло: медианная интенсивность на уровне головок рельсов равна 8 из 255, рельсы ничем не выделяются на фоне полотна, и пара пиков на расстоянии 1.52 м находится где попало — оценки прыгали от −1.18 до +1.37 м в соседних срезах одного кадра. Отказались, ось пути оценивается по дрейфу центра свода.
Обычная связность при кластеризации. Соседние лучи объединялись без учёта глубины, и предмет на 55 м слипался со стеной на 150 м в одно пятно размером 5 × 4 м. Реальный объект обнаруживался в 16 кадрах из 20. После введения допуска по глубине, растущего с расстоянием, — 20 из 20 и правильные габариты 0.67 × 1.35 м.
Грибовидное тело с мушиными параметрами. 2000 клеток Кеньона и 5 % активных насыщаются после нескольких тысяч примеров: подавлено 98 % синапсов, новизна реального препятствия падает до 0.001, и оно перестаёт обнаруживаться совсем. Потребовалось увеличить популяцию до 50 000 и снизить разрежённость до 0.1 %, а темп депрессии согласовать с размером обучающей выборки.
Корреляция продольного профиля «в лоб». Первая версия оценки скорости залипала на нулевом сдвиге: в профиль входила ближняя зона, где на метр пути приходятся тысячи лучей, и её вклад подавлял всё остальное. Помогло исключение ближней зоны и вычитание скользящего среднего. Отдельно обнаружилась ошибка в перепроекции — использовалась высота над рельсом вместо z сенсора, из-за чего согласие держалось на 0.12 вместо 0.9.
Взвешивание срезов по числу точек при оценке оси пути. У ближних срезов точек в сотни раз больше, и подгонка полностью игнорировала дальние, где как раз содержится кривизна. Кривые расходились от +10 до −10 м на 200 м в соседних кадрах. Равные веса по срезам и линейная (а не квадратичная) экстраполяция за горизонт видимости решили проблему.
Оценка оси пути на станции. Платформа делает сечение резко несимметричным, центр свода «уезжает», и габарит заезжает прямо на платформу: радиус кривой падал с 999 до 225 м за 4.5 с. Это давало 54 % кадров с ложной тревогой на бэге с платформой и стрелкой. Помогли два физических ограничения — минимальный радиус 300 м и предел скорости изменения оси. Стало 5.4 %.
Срыв оценки скорости на смене типа тоннеля. На переходе круглого тоннеля в двухпутный сопоставление кадров теряло опору и выдавало попеременно 0 и 70 км/ч. Помог фильтр с физическим пределом ускорения 3 м/с²: поезд за 0.1 с так не разгоняется.
Полигон без учёта кривизны. Первая версия синтетических сценариев ставила предмет в поперечных координатах сенсора, а не на ось пути. В кривой это уносило его в стену, и «рабочая дальность» выходила 32 м вместо реальных 55+. Исправлено привязкой к оси.
9. Размеченный полигон: дальность обнаружения
python tools/make_benchmark.py --memory artifacts/mushroom_body.npz
python tools/plot_benchmark.py
Разметки в датасете нет, а организаторы предупредили, что приватный тест собран добавлением синтезированных препятствий. Полигон строится тем же способом: в реальные кадры пустого тоннеля трассировкой лучей вставляется предмет, стоящий на оси пути в фиксированной точке тоннеля, поезд к нему подъезжает, и на каждом кадре известна истинная дистанция.
Модель сенсора опирается на руководство: поканальная дальность из Приложения A (каналы 34–65 берут 200 м, каналы 98–128 смотрят в землю и рассчитаны только на ближнее поле), вероятность обнаружения на паспортной дальности PoD = 70 %, шум дальности ±2 см, заполнение пятна луча для мелких предметов.
Проверка модели: настоящий объект 0.67 × 1.35 м на 55 м даёт 47–69 лучей; синтетический человек 0.44 × 1.71 м на 60 м даёт 50 лучей. Совпадает.
Результаты приводятся в трёх разрезах, потому что смешивать их нельзя:
- видимость — доля кадров, в которых до предмета дошёл хотя бы один луч; за поворотом она падает до нуля независимо от алгоритма;
- обнаружение при условии видимости — собственно качество алгоритма;
- обнаружение как есть — произведение первых двух, эксплуатационная величина.
9.1. Результат
90 сценариев: 9 предметов × 2 поперечных смещения × 5 бэгов, 14 004 наблюдения с известной истинной дистанцией.
| Предмет | Площадь | Рабочая дальность | P@50 м | P@100 м | P@150 м | Видимость |
|---|---|---|---|---|---|---|
| человек стоя | 0.75 м² | 100 м | 0.70 | 0.53 | 0.12 | 92.5 % |
| человек сидя | 0.42 м² | 20 м | 0.62 | 0.34 | 0.00 | 89.3 % |
| ящик | 0.36 м² | 20 м | 0.49 | 0.00 | 0.00 | 88.1 % |
| чемодан | 0.25 м² | 62 м | 0.53 | 0.00 | 0.00 | 81.9 % |
| ведро | 0.10 м² | 8 м | 0.00 | 0.00 | 0.00 | 61.2 % |
| каска | 0.07 м² | — | 0.00 | 0.00 | 0.00 | 51.1 % |
| камень | 0.05 м² | — | 0.00 | 0.00 | 0.00 | 45.1 % |
| бутылка | 0.03 м² | — | 0.00 | 0.00 | 0.00 | 41.2 % |
| кабель | ~0 м² | — | 0.00 | 0.00 | 0.00 | 25.7 % |
Предыдущие замеры для сравнения. Без накопления в координатах пути (п. 7.3): человек — рабочая дальность 62 м, P@50 = 0.56, P@100 = 0.24. С накоплением, но без разреза по контрасту (п. 9.5): 100 м, P@50 = 0.56, P@100 = 0.57.
«Рабочая дальность» у предметов около порога неустойчива и её не надо читать как физическую величину: метрика идёт от ближнего пояса и обрывается на первом, где доля падает ниже 0.5, усредняя при этом два поперечных положения — на оси и со смещением 0.9 м к краю габарита. У сидящего человека и ящика пояс 25–40 м даёт 0.48 против порога 0.50, и число падает с 62 до 20 м, хотя P@50 при этом не ухудшилось. Содержательны таблицы по поясам (п. 9.2 и 9.5), а не это одно число.
Граница проходит по числу лучей: на 40–55 м человек даёт 68 лучей, чемодан 25, ведро 11, каска 6, бутылка 3. Ниже примерно десяти лучей предмет перестаёт отличаться от шума решётки, и никакая обработка этого не исправит — нужен либо более плотный сенсор, либо подъезд ближе.
9.2. Почему рабочая дальность 62 м, а реальный объект виден на 98.9 %
Разброс по бэгам огромный, и он объясняет расхождение:
| Бэг (человек стоя, на оси) | 25–40 м | 40–55 м | 55–70 м | 70–90 м | лучей на 50 м |
|---|---|---|---|---|---|
doubleT_platform |
1.00 | 1.00 | 1.00 | 1.00 | 68 |
roundT_squareT_pressureGate_squareT |
1.00 | 1.00 | 1.00 | 0.67 | 68 |
squareT_platform_squareT_switch |
0.00 | 0.18 | 1.00 | 1.00 | 56 |
roundT_pressureGate_roundT |
0.73 | 0.00 | 0.00 | 0.00 | 65 |
roundT_doubleT |
0.27 | 0.00 | 0.00 | 0.00 | 32 |
Не «плохо везде понемногу», а идеально на одних участках и слепо на других, причём при 65 лучах на предмете. То есть дело не в видимости и не в размере.
Таблица снята до накопления в координатах пути (п. 7.3) и до разреза по контрасту (п. 9.5). Актуальные цифры по тем же бэгам — в п. 9.5:
roundT_pressureGate_roundTиз полностью слепого за 40 м стал 0.45 / 1.00 / 0.57 на 40–90 м,roundT_doubleTза 40 м слепым остался.
9.3. Найденная причина слепоты: связная компонента течёт вдоль стены
Покадровый разбор провала (roundT_pressureGate_roundT, человек на 50 м, 65 лучей
вставлено) показал: кандидата нет вообще. В кадре всего 5 компонент, и одна из них —
42 059 лучей, протянувшиеся по дальности от 4 до 99 м, наполненность 0.05.
Кластеризация с разрывом по глубине объединяет соседние лучи, если их дальности
отличаются меньше чем на 0.06·R + 0.35 м. Вдоль гладкой стены тоннеля соседние лучи
отличаются на сантиметры, поэтому стена связна от ближнего поля до горизонта. Предмет,
стоящий у такой стены, попадает в ту же компоненту и вместе с ней отбрасывается
правилом «ни один предмет не тянется на 15 м вдоль пути».
Это не регрессия: мерж одинаков при любой верхней границе контекста, включая исходную.
Попытка первая: разрезать по дальности. Переглубокая компонента не выбрасывается,
а пересобирается с более строгим допуском. Предмет при этом действительно выделяется —
57 лучей, наполненность 1.00. Измеренный размен на roundT_pressureGate_roundT
(человек на 25…90 м) и глобально:
| Допуск разреза | Обнаружение | Ложных кадров (бэг) | Ложных треков (бэг) |
|---|---|---|---|
| выключен | 28.2 % | 0.0 % | 0 |
| 0.045 | 28.2 % | 27.2 % | 1 |
| 0.040 | 61.5 % | 21.5 % | 2 |
| 0.030 | 94.9 % | 57.5 % | 5 |
| 0.015 | 94.9 % | 73.7 % | 16 |
| 0.006 | 94.9 % | 96.5 % | 47 |
Глобально при 0.030: ложных треков 25.8 на км против 5.2, кадров с тревогой 52 % против 9.4 %. Половина кадров с тревогой — это непрерывное торможение, поэтому разрез по умолчанию выключен.
Проверялось и то, можно ли отделить осколок стены от предмета по признакам: ни один не разделяет их. Медианы (предмет / стена): ширина 0.21 / 0.20 м, наполненность 1.00 / 1.00, лучей 12 / 8, новизна 0.50 / 0.41. Строгий разрез делает стену геометрически неотличимой от предметов — потому и цена такая. Этот вариант убран.
9.4. Разделение фигуры и фона по движению
Разрезать по дальности нельзя: предмет и стена рядом с ним стоят на одной дальности. Зато они по-разному приближаются, и это чистая геометрия. Вдоль фиксированного луча стена, параллельная движению, не приближается вовсе: поезд едет, точка пересечения скользит по стене, дальность не меняется. Предмет, обращённый к поезду, приближается ровно на пройденный путь.
Отсюда признак: advance = (r_прошлый − r_текущий) / ds. Ноль у фона, единица у фигуры.
Ничего перепроецировать не нужно — столбец решётки отвечает фиксированному азимуту, а
рысканье в кривой за кадр (0.06° при радиусе 1300 м) меньше шага решётки. Это тот самый
канал T4/T5 → LPTC: широкопольный поток задаёт ожидание, а что движется иначе — фигура.
Замер на вставленном человеке подтверждает физику: у предмета advance = 0.99…1.01,
у стены на той же дальности — 0.39…0.82 и падает по мере приближения.
Разрез по этому признаку (Params.split_adv, lobula.split_by_figure) не крошит стену:
из склеенной компоненты в 42 000 лучей остаётся 2.6–5.5 тысяч и 6–15 кандидатов
вместо 1259 у разреза по дальности.
Важно для честности замера. Первая проверка дала 70 % ложных кадров, но она была
некорректной: память тоннеля обучена на кандидатах старого генератора, а разрез
порождает формы, которых она никогда не видела, — всё выглядит новым. После пересбора
кэша и переобучения памяти на тех же настройках (tune_candidates_adv.npz,
new_data_candidates_adv.npz, 70 273 кандидата) картина такая:
Что это даёт — полигон:
| без разделения | с разделением | |
|---|---|---|
| Рабочая дальность, человек стоя | 62 м | 80 м |
| P@50 м | 0.56 | 0.71 |
| P@100 м | 0.24 | 0.36 |
| Чемодан, P@50 м | 0.57 | 0.67 |
По бэгам (человек на оси, доля кадров с обнаружением):
| Бэг | 25–40 м | 40–55 | 55–70 | 70–90 | 90–110 |
|---|---|---|---|---|---|
squareT_platform_squareT_switch |
0.00 → 1.00 | 0.18 → 1.00 | 1.00 → 1.00 | 1.00 | 1.00 |
roundT_doubleT |
0.27 → 0.64 | 0.00 | 0.00 | 0.00 | 0.00 |
roundT_pressureGate_roundT |
0.73 → 0.80 | 0.00 | 0.00 | 0.00 | 0.00 |
roundT_squareT_pressureGate_squareT |
1.00 | 1.00 | 1.00 | 0.67 → 0.89 | 0.00 |
doubleT_platform |
1.00 | 1.00 | 1.00 | 1.00 | 0.25 |
Что это стоит:
| без разделения | с разделением | |
|---|---|---|
| Кадров с ложной тревогой | 9.4 % | 30.3 % |
| Разных ложных треков на км | 5.2 | 21.5 |
| Ложных тревог на полигоне, на кадр | 0.055 | 0.348 |
| Задержка, медиана | 31 мс | 39 мс |
| Реальный объект на 55 м | 98.9 % | 98.9 % |
Порогом это не лечится. Проверены значения 0.6 / 0.8 / 0.9: ложных треков 21.5 / 21.6 / 21.6 на километр, а доля кадров с тревогой только растёт (30 → 35 → 40 %). Причина в том, что ложные срабатывания здесь — не шум, а настоящие поверхности, обращённые к поезду: рамы гермозатворов, торцы, порталы. По одному признаку движения они от предмета неотличимы, потому что физически ведут себя так же.
Решение: разделение по умолчанию выключено (split_adv: 0.0). 21.5 ложного трека
на километр — это напрасное торможение каждые 47 метров, система в таком виде
неработоспособна, и +18 м рабочей дальности этого не окупают. Два бэга из пяти
разделение к тому же не спасает: за 40 м они остаются слепыми.
Чем это лечится по-настоящему. Не порогом, а памятью: приближающиеся конструкции тоннеля постоянны и повторяются от проезда к проезду. Здесь память обучена на пяти бэгах и 20 минутах записи; на реальной линии с многими проездами грибовидное тело подавило бы их так же, как подавляет всё остальное штатное. Это проверяемое предсказание, а не надежда: переобучение памяти уже снизило ложные кадры с 70 % до 30 % — просто за счёт того, что она увидела эти формы один раз.
9.5. Разделение фигуры и фона по контрасту — третья попытка, и она работает
Разрез по допуску (п. 9.3) и разрез по движению (п. 9.4) вернули зрение и оба оказались слишком дороги. Оставался третий признак фигуры, и он всё это время уже вычислялся — просто не участвовал в сегментации.
Физика. Гладкая стена даёт нулевой центр-окружение по построению. Как бы
сильно дальность ни менялась вдоль стены, она меняется плавно: центр равен своему
окружению, и контраст равен нулю. Предмет на стене — это ступенька, и она видна.
Именно этим занята ламина, и её выход gap («насколько ближе окружения», м) наш
конвейер считал с самого начала — но использовал только как признак кандидата.
Сегментация же шла по связности с допуском по глубине, и переглубокая компонента
отбрасывалась целиком ещё до того, как признак кому-то пригождался.
Замер разделимости (вставленный человек, два круглых тоннеля, 55 кадров):
Порог gap |
Лучей предмета | Лучей фона | Компонент фона в кадре |
|---|---|---|---|
| 2 м | 90.8 % / 87.5 % | 4.15 % / 3.10 % | 24.6 / 16.3 |
| 4 м | 89.2 % / 83.4 % | 0.68 % / 0.62 % | 13.7 / 13.5 |
| 6 м | 85.7 % / 75.1 % | 0.13 % / 0.22 % | 5.0 / 8.9 |
| 9 м | 80.7 % / 61.7 % | 0.02 % / 0.05 % | 0.7 / 2.6 |
Для сравнения: разрез по допуску давал 1259 кандидатов в кадре. Здесь фон распадается на 5–9 компонент — на два порядка меньше, и это настоящие выступы, которые память способна выучить, а не произвольные осколки гладкой стены.
Разделение держится до 75 м и разваливается к 90 м: доля лучей предмета, прошедших порог 6 м, по полосам — 88 / 96 / 97 / 89 / 61 / 14 / 0 % на 15 / 30 / 45 / 60 / 75 / 90 / 105 м. Дальше 90 м кольцо окружения снова упирается в стену на той же дальности (п. 7.3), и контраста нет.
Почему первая версия всё равно была дорогой
Без ограничений разрез дал 9.4 ложных трека на км против 7.4 — и причина
нашлась замером, а не рассуждением. Медианы кандидатов на roundT_doubleT:
| признак | без разреза | с разрезом |
|---|---|---|
| протяжённость вдоль пути | 2.82 м | 1.00 м |
| то же, дальше 55 м | 6.87 м | 0.81 м |
| наполненность | 0.47 | 0.57 |
контраст gap |
1.24 м | 3.29 м |
Разрез отрезает фон, и вместе с фоном исчезает протяжённость. В весе улики
(central_complex._quality) за неё отвечает множитель компактности
clip(1.5 − depth/(3·span)): при depth 6.9 м и размере 0.65 м он равен 0.1, при
depth 0.8 м — 1.0. То есть каждое наблюдение вырезанной фигуры стало весить
вдесятеро больше, и подавление протяжённых конструкций, работавшее до разреза,
выключилось.
Порог этого не лечит — проверено сквозным замером:
| Порог разреза | Ложных треков на км |
|---|---|
| выключен | 7.4 |
| 6 м | 9.4 |
| 9 м | 10.4 |
| 12 м | 9.4 |
Что решило: одна фигура на компоненту
Раз каждая лишняя фигура стоит вдесятеро дороже прежнего, их число надо ограничить. Из переглубокой компоненты выносится только сильнейшая фигура по сумме превышения порога. Это тот же приём глобального торможения, которым APL оставляет активными считанные проценты клеток Кеньона, а широкопольное торможение — считанные колонки LC11.
| Фигур на компоненту | Ложных треков на км | Кадров с тревогой |
|---|---|---|
| без ограничения | 9.4 | 16.9 % |
| 2 | 7.4 | 15.5 % |
| 1 | 6.4 | 15.0 % |
Дальность обнаружения при этом не меняется вовсе: и при одной фигуре, и при двух, и без ограничения все три конфигурации дают одинаковые доли по полосам. Лишние фигуры не добавляли зрения — только ложные тревоги.
Разрез вдобавок не применяется ближе 55 м (split_near): там покадровый тракт
видит предмет и без него (100 % на всех пяти бэгах до 70 м), а обстановки,
дающей контраст, в ближнем поле на порядок больше. Без этого ограничения 75 %
добавленных ложных треков приходили именно оттуда.
Итог
Цифры ниже — leave-one-bag-out с пересобранным кэшем кандидатов и переобученной памятью: без этого замер лжёт (п. 9.4).
| Конфигурация | Ложных треков на км | Кадров с тревогой | Объект 55 м |
|---|---|---|---|
| как было | 9.1 | 17.2 % | 98.9 % |
| с разрезом по контрасту | 7.5 | 16.7 % | 98.9 % |
То есть разрез не только вернул зрение там, где его не было, но и снизил ложные тревоги — за счёт того, что переглубокая компонента перестала выбрасываться целиком и вместо неё в память попадает одна осмысленная фигура, которую есть чему выучить.
Обнаружение вставленного человека на оси, доля кадров. Протокол полигона: предмет стоит в точке тоннеля, до которой от начала записи 200 м, поезд подъезжает. Память обучена и на этом бэге — как и во всём полигоне.
| Бэг | 15–25 м | 25–40 | 40–55 | 55–70 | 70–90 |
|---|---|---|---|---|---|
roundT_pressureGate_roundT |
1.00 | 0.73 | 0.00 → 0.45 | 0.00 → 1.00 | 0.00 → 0.57 |
squareT_platform_squareT_switch |
0.00 | 0.00 | 0.18 → 0.45 | 1.00 | 1.00 |
roundT_doubleT |
1.00 | 0.27 | 0.00 | 0.00 | 0.00 |
Это ровно тот бэг, который разбирался в п. 9.3. roundT_pressureGate_roundT —
запись, где компонента из 42 000 лучей течёт вдоль стены от 4 до 99 м и уносит
предмет с собой. Разрез по контрасту превращает полностью слепую полосу 40–90 м в
0.45 / 1.00 / 0.57. Разрез по допуску (п. 9.3) и по движению (п. 9.4) добивались
там же 0.94 и 0.80 ценой 25.8 и 21.5 ложного трека на километр; здесь ложных треков
стало меньше, чем было до разреза.
Второй слепой бэг разрез не спасает, и причины там две, обе разобраны
покадрово в п. 9.6: за 50 м до предмета не доходит линия взгляда (98 % лучей
упираются в преграду ближе него), а на 35–52 м мешает уже наш собственный порог
split_near, снижать который оказалось слишком дорого.
Осторожно с «рабочей дальностью». Метрика в plot_benchmark.py идёт от ближнего
пояса и останавливается на первом, где доля падает ниже 0.5, а усредняет она два
поперечных положения сразу — на оси и со смещением 0.9 м к краю габарита. У предметов,
стоящих около порога, она поэтому скачет: у сидящего человека пояс 25–40 м даёт 0.48
против 0.50, и «рабочая дальность» падает с 62 до 20 м при том, что P@50 м
выросло с 0.58 до 0.62. Смотреть надо на таблицу по поясам, а не на одно число.
Где разрез стоит денег — 1: холодный старт. Всё сказанное верно, когда память
обучена. На совершенно новой линии, где памяти нет вовсе, разрез, наоборот, дорог:
21.8 → 31.8 ложных трека на километр. Это логично — он порождает кандидатов из
настоящих выступов тоннеля, и без памяти отличить их не от чего. Реальный сценарий —
именно с обученной памятью, она поставляется в образе; но если участок настолько
новый, что память к нему неприменима, split_gap: 0.0 возвращает прежнее поведение.
Где разрез стоит денег — 2: стоянка. На записи со стоящим поездом (doubleT_obstacle)
он добавляет один устойчивый трек на 76.6 м, и доля кадров с посторонней тревогой
растёт с 63 % до 96 %. Это не случайность: при нулевом пройденном пути не работают
ни накопитель, ни привыкание — оба живут в координатах пути. Число разных ложных
треков на этом бэге растёт всего с 4 до 5; раздувается именно доля кадров, потому
что на стоянке ничто не уходит из поля зрения.
9.6. Почему roundT_doubleT остаётся слепым за 40 м
Разрез по контрасту (п. 9.5) открыл roundT_pressureGate_roundT, но второй слепой
бэг не тронул. Покадровый разбор цепочки «лучи → кандидат → улика → тревога» на
штатной постановке полигона (предмет в точке, до которой от начала записи 200 м)
показал две разные причины в двух разных полосах дальности. Одна физическая,
вторая — наша.
Дальше 50 м: смотреть не на что
Считаем, сколько лучей доходит до предмета, по этапам:
| Дальность до предмета | Геометрически попали | Пережили модель сенсора | Не заслонены |
|---|---|---|---|
| 55–105 м | 29 | 29 | 1 |
| 30–55 м | 109 | 109 | 86 |
Модель сенсора не теряет ни одного луча: предмет крупный, дальность паспортная. Но за 55 м 98 % лучей упираются в эхо, которое ближе предмета. Дело не в отражательной способности и не в числе каналов — до предмета просто не доходит линия взгляда.
Это не артефакт постановки. Предмет пробовали ставить четырьмя способами — на оценённую ось коридора, прямо по оси сенсора, на половину смещения и со сдвигом +0.8 м; заслонение одинаково во всех четырёх, и дальность преграды тоже одна и та же:
| Дальность до предмета | 104 | 94 | 84 | 74 | 64 | 54 | 45 |
|---|---|---|---|---|---|---|---|
| Преграда, м | 90 | 84 | 76 | 66 | 58 | 52 | 49 |
| Лучей видно (ось коридора) | 3/17 | 1/19 | 0/25 | 0/37 | 1/41 | 8/61 | 63/89 |
Преграда приближается вместе с поездом и в какой-то момент обгоняет предмет: на 45 м обзор доходит до 49 м, предмет оказывается ближе преграды — и 63 луча из 89 приходят разом. Ровно с этого места и начинается обнаружение.
Для сравнения, на том же замере roundT_pressureGate_roundT преграда всегда за
предметом (предмет на 104 м — преграда на 113 м; предмет на 56 м — преграда на 70 м),
поэтому там предмет виден, и разрез по контрасту может ему помочь.
Почему видимость на этом перегоне такая короткая. По бэгу в целом вдоль оси пути видно на 106 м (медиана), но полигон ставит предмет в точку за 200 м от начала записи, а это самое начало проезда — как раз худший его участок. Плюс общий для всех бэгов эффект: луч, идущий вдоль пути, скользит по полотну, и с какой-то дальности прирельсовая зона перестаёт наблюдаться вовсе. Нижняя наблюдаемая точка у оси пути (5-й процентиль высоты над головкой рельса):
| Дальность | 20 м | 40 | 60 | 80 | 90 | 100 |
|---|---|---|---|---|---|---|
roundT_doubleT |
−0.35 | −0.32 | −0.21 | −0.07 | +0.36 | +0.64 |
roundT_pressureGate_roundT |
−0.32 | −0.32 | −0.06 | +0.18 | +0.10 | −0.11 |
doubleT_platform |
−0.35 | −0.36 | −0.34 | −0.06 | +0.08 | +0.35 |
За 80–90 м самое низкое, что видно у оси, находится уже выше рельса на 0.1–0.6 м. Это и есть предел скользящего луча, и он объясняет, почему у мелких лежащих предметов видимость в полигоне 25–61 %: их просто нечем осветить.
Никакой обработкой это не лечится. Лечится геометрической картой линии (п. 7.3) — или вторым сенсором, поднятым выше.
35–52 м: лучи есть, кандидата нет — и это наш порог
Здесь картина обратная: лучей 83…146, а кандидата нет.
| Дальность | 41.6 | 38.7 | 35.9 | 33.3 | 30.7 | 28.3 |
|---|---|---|---|---|---|---|
| Лучей на предмете | 83 | 101 | 126 | 146 | 168 | 200 |
Кандидат при split_near = 55 |
нет | нет | нет | нет | есть | есть |
Кандидат при split_near = 20 |
есть | есть | есть | есть | есть | есть |
Причина — тот же мерж со стеной, что в п. 9.3, и наш собственный порог: разрез по контрасту по умолчанию не применяется ближе 55 м. Порог был введён из соображения «ближе покадровый тракт видит предмет и сам» — на этом бэге это неверно.
Со сниженным порогом первая тревога наступает на 37.3 м вместо 28.3 м, а доля кадров без кандидата в окне 20–42 м падает с 44 % до 6 %.
Порог всё равно оставлен 55 м. Цена снижения измерена честно — с пересбором кэша
кандидатов при split_near = 20 и переобучением памяти на нём:
split_near |
Ложных треков на км | Кадров с тревогой |
|---|---|---|
| 55 м | 7.4 | 16.7 % |
| 30 м | 14.1 | 25.6 % |
| 20 м (память не пересобрана) | 18.1 | 28.2 % |
| 20 м (честно, память переобучена) | 17.1 | 27.9 % |
Переобучение на этот раз почти ничего не вернуло (18.1 → 17.1): ближние вырезанные фигуры от препятствий действительно неотличимы. А выигрыш — девять метров на дальности, где он ничего не меняет: ТЗ оценивает 100 / 200 / 300 м, и поезд на 50 км/ч не останавливается ни за 28, ни за 37 м. Платить за это 2.3-кратным ростом ложных тревог нельзя.
split_near: 20.0 в конфиге доступен для линии, где обстановка беднее и ложные
тревоги дешевле.
10. Новый участок: привыкание внутри проезда — отрицательный результат
Обученная память отвечает на вопрос «этот тоннель я уже видел». На новом участке она бесполезна по определению, и это измеренная величина, а не абстрактный риск:
| Ложных треков на км | Кадров с тревогой | |
|---|---|---|
| память обучена на других бэгах (leave-one-bag-out) | 9.1 | 17.2 % |
| памяти нет вовсе (новая линия) | 21.8 | 33.1 % |
Приватный тест — это как раз новый участок, поэтому вопрос важный. Механизм был сделан, доведён до работы и отвергнут по результатам замера. Ниже — почему, потому что отрицательный результат здесь содержательнее положительного.
Замысел
Тоннельная обстановка повторяется вдоль пути, а посторонний предмет — нет. Кабельный кронштейн, рама крепи, стык тюбингов встречаются каждые несколько метров в одном и том же виде; разбор худшего бэга (п. 5.1) прямо это показал — пять из восьми ложных треков были колоннами, повторяющимися каждые 13–20 м. Упавший же предмет лежит в одном месте.
Отсюда привыкание, считающее не по времени и не по числу кадров, а по числу разных точек пути, где встретилась эта форма. Настоящий предмет виден сто кадров подряд, но всё это время стоит в одной точке мира: его код депрессируется один раз и остаётся новым до конца подъезда. Биологически это familiarity suppression MBON-α′3, а момент депрессии разрешает координата пути из центрального комплекса — роль, которую у мухи играют дофаминергические PPL1/PAM.
Что пришлось починить, чтобы механизм вообще заработал
Обе ошибки найдены замером и сами по себе поучительны.
Полный набор признаков не годится. Первая версия кодировала кандидата тем же дескриптором, что и долговременная память. За двенадцать разных мест новизна упала с 1.000 до 0.981 — то есть ни на что. В дескрипторе есть дальность, число лучей, угловой размер, интенсивность: код одного и того же кронштейна с 90 и с 40 м разъезжается, повторы не узнаются, а подъезжающий предмет каждый кадр выглядит новой формой и привыкает сам к себе. Переведено на десять признаков формы, не зависящих от дальности.
Нормировка обязана замирать. Пока среднее и разброс пересчитываются, вместе с
ними плывёт код. У неподвижного предмета, чьи признаки формы не меняются вовсе,
набор активных клеток обновлялся настолько, что предмет засчитывался как
двадцать шесть разных мест и гасил сам себя. При пороге заморозки 4000
кандидатов эффект оставался живуч: в roundT_doubleT (около 570 кандидатов за
проезд) нормировка не замирала никогда, привыкание стояло на 0.20, медианная
новизна кандидата 1.00, и ложных треков было ровно столько же, сколько без него.
Порог снижен до 300.
Почему всё равно отвергнуто
После починок механизм начал давать цифры: leave-one-bag-out 7.5 → 5.9 ложных трека на км, новая линия 31.8 → 26.8. Выглядело как успех — до проверки обратной стороны.
Привыкание глушило сам предмет. Замер на вставленных предметах
(doubleT_platform, подъезд с 200 м):
| Новизна кандидата, 20–60 м | Обнаружение, 70–90 м | |
|---|---|---|
| привыкание выключено | 0.75 | 95 % |
| привыкание включено | 0.24 | 74 % |
И это не «предмет заодно с обстановкой»: медианная новизна обычного кандидата в тех же прогонах — 0.43…0.71, то есть предмет подавлялся сильнее, чем тоннельная обстановка. Избирательность не просто мала, она отрицательна.
Причина — насыщение популяции, а не узнавание повторов. За проезд набирается около 270 событий депрессии по 80 клеток каждое; при популяции 4000 это 5.4 попадания на клетку, то есть подавлено всё. Ровно та же ошибка, что описана в п. 8 для долговременной памяти с мушиными параметрами, — и проверяется она так же, поднятием ёмкости:
| Ёмкость короткой памяти | Ложных треков на км (LOO) | Новизна предмета | Обнаружение 70–90 м |
|---|---|---|---|
| привыкание выключено | 7.5 | 0.75 | 95 % |
| 4 000 клеток | 5.9 | 0.24 | 74 % |
| 20 000 клеток | 7.5 | 0.47 | 95 % |
| 50 000 клеток | 7.5 | 0.64 | 95 % |
При ёмкости, достаточной чтобы не насыщаться, привыкание не меняет ничего: 7.5 против 7.5 на обученной памяти и 31.8 против 31.8 на новой линии. Весь видимый выигрыш был глобальным глушением — а его и так даёт порог решения, который у нас уже есть отдельным параметром.
Пробовались два способа сделать отсчёт избирательным, оба измерены и оба ничего не дали:
- резкий отсчёт MBON: вместо среднего по активным клеткам — квантиль 0.75 или 0.90, то есть «знакомо, только если подавлено не меньше трёх четвертей кода». Новизну предмета это вернуло (0.75), но и весь эффект тоже (7.5 → 7.5);
- огрубление признаков (coarse coding) до половины и до целого разброса, чтобы два похожих кронштейна давали буквально один и тот же код: 7.5 / 7.5 / 7.5.
Что это на самом деле означает
Коды двух разных экземпляров одной и той же конструкции не перекрываются настолько, чтобы второй узнавал первого, даже после огрубления до целого стандартного отклонения. Проще говоря: в этих данных штатная обстановка повторяется не так буквально, как предполагалось. Разброс между экземплярами одной конструкции — по ракурсу, по числу лучей, по тому, какая часть попала в габарит — больше, чем разрешение любого разумного кода формы.
Поэтому на новом участке работает то, что и работало: долговременная память, обученная на других участках, снижает ложные тревоги с 21.8 до 9.1 трека на километр. Признаки в дескрипторе намеренно смешаны — форма и угловой размер переносятся на любой тоннель, положение в сечении запоминает конкретную обстановку, и именно первая половина даёт этот перенос.
Механизм оставлен в коде и выключен (enable_habituation: false) со всеми
параметрами: ёмкость, квантиль отсчёта, огрубление, шаг «разных мест», путь
полузабывания. На линии, где обстановка стандартизована сильнее, чем в этих записях,
он может заработать — но проверять это надо замером на той линии, а не надеждой.
11. Обученное считывание MBON: метки из физики
До сих пор грибовидное тело училось без учителя: оно запоминало частоту
обстановки и отвечало «знакомо/ново». Вставка предметов трассировкой лучей даёт
то, чего в датасете нет, — метку. synth.inject возвращает индексы лучей,
которые он переписал, поэтому кандидат размечается точным пересечением: предмет,
если не меньше половины его лучей пришли от вставки (tools/make_training_set.py,
MIN_OVERLAP = 0.5). Слои остаются те же — разрежённый код клеток Кеньона,
торможение APL, один выход MBON, — меняется только учитель: вместо частоты
обстановки различение «предмет / тоннель» (flyguard/mbon_readout.py).
11.1. Сначала — найденный артефакт: интенсивность выдавала вставку
Первое же обучение дало подозрительно красивые цифры: leave-one-bag-out AUC 0.9935, по полосам дальности от 0.998 вблизи до 0.941 на 160–230 м. Разбор важности перестановкой поставил наверх не форму, а интенсивность:
inten +0.0187 ← втрое больше следующего
elongation +0.0051
fill +0.0048
log_rays +0.0030
Проверка показала, откуда это взялось. Интенсивность вставки считалась по
ламбертовой модели ρ·cosθ/r², и произведение intensity · r² у вставленных
предметов держалось в пределах 1.8× между p10 и p90, тогда как у настоящих
кандидатов разброс 64×. Иначе говоря, вставка — и только она — точно
подчинялась учебниковому закону обратных квадратов, и это распознаётся надёжнее
любой формы.
Хуже того, у нижнего среза шкалы признак переворачивается. Разделяющая способность одной интенсивности по полосам:
| полоса | intensity вставки | intensity обстановки | AUC по одной интенсивности |
|---|---|---|---|
| 0–30 м | 37.2 | 5.0 | 0.956 |
| 30–55 м | 4.6 | 4.5 | 0.513 |
| 55–80 м | 1.7 | 4.0 | 0.287 |
| 80–110 м | 1.0 | 3.0 | 0.197 |
| 110–160 м | 1.0 | 5.0 | 0.049 |
| 160–230 м | 1.0 | 7.5 | 0.010 |
За 110 м расчётная яркость вставки падала ниже единицы, упиралась в срез шкалы — и «тускло» становилось почти безошибочным признаком предмета. AUC 0.010 означает почти идеальное разделение с обратным знаком.
В реальных данных всё наоборот. Медиана интенсивности по всему облаку, 20 кадров на бэг:
| бэг | 5–15 м | 15–30 | 30–50 | 50–80 | 80–120 | 120–170 | 170–230 |
|---|---|---|---|---|---|---|---|
doubleT_obstacle |
11 | 9 | 8 | 9 | 6 | 6 | 6 |
roundT_doubleT |
10 | 8 | 8 | 4 | 2 | 3 | 4 |
squareT_platform_squareT_switch |
8 | 8 | 7 | 3 | 2 | 2 | 4 |
Никакого 1/r². Прибор отдаёт не принятую энергию, а отражательную способность
с компенсацией дальности — то, что в руководстве Hesai называется
reflectivity. Ламбертова модель описывала физику фотона, а не то число, которое
лежит в поле intensity.
Настоящий предмет даёт точку привязки. В doubleT_obstacle он стоит на 55.9 м,
57 лучей, и его медианная интенсивность — 34.5 (p10…p90 = 31…39) при
медиане окружающих кандидатов 23.5. Старая формула на этой дальности дала бы
1.45, то есть в 24 раза тусклее настоящего.
Первая попытка починки была неправильной. Раз прибор компенсирует
дальность, яркость вставки сделали постоянной: 100 · ρ · √cosθ со шкалой,
привязанной к настоящему предмету (ρ = 0.35 → около 34). Выборка пересобрана,
модель переобучена — и артефакт просто перевернулся:
| полоса | вставка | обстановка | AUC по одной интенсивности |
|---|---|---|---|
| 0–30 м | 35.0 | 5.0 | 0.966 |
| 30–55 м | 35.2 | 4.5 | 0.960 |
| 55–80 м | 34.9 | 4.0 | 0.959 |
| 80–110 м | 35.1 | 3.0 | 0.970 |
| 110–160 м | 34.8 | 5.0 | 0.832 |
| 160–230 м | 34.2 | 7.5 | 0.798 |
Важность интенсивности при этом выросла втрое (+0.0533 против +0.0187), то есть модель стала опираться на подсказку ещё сильнее.
Настоящая причина: абсолютной шкалы в этих данных нет. Медиана яркости
кандидатов обстановки по бэгам — 3.0, 4.0, 4.0, 4.0, 7.0, а в
doubleT_obstacle, где лежит настоящий предмет, 23.5 при 34.5 у самого
предмета. Разброс между записями впятеро больше, чем контраст предмета к фону
внутри записи. Любое абсолютное число, назначенное вставке, оказывается
подарком детектору — в ту или в другую сторону.
Как сделано в итоге (synth.local_intensity и synth.IntensityEnv):
вставка берёт яркость реальных возвратов с тех же самых лучей — того, что
предмет заслонил. Лучи, ушедшие в пустоту (за 100 м таких большинство),
получают образец из реальных возвратов той же полосы дальности в этом же
кадре; разложение кадра по дальности считается один раз и переиспользуется
всеми 135 сценариями. Ни отражательной способности, ни закона яркости от
дальности в функции нет; ρ осталась там, где ей место, — в вероятности, что
эхо вообще вернётся. Проверка вставками в реальные бэги:
| дальность | 30 м | 60 м | 100 м | 150 м | 200 м |
|---|---|---|---|---|---|
| вставка | 8.8 | 6.9 | 4.3 | 4.3 | 10.1 |
| реальные возвраты рядом | 10.0 | 4.0 | 2.0 | 3.0 | 3.0 |
Это заниженная оценка: настоящий предмет в записи был в 1.47 раза ярче
окружения, и этот запас мы себе не засчитываем. На линии с откалиброванной
яркостью его можно вернуть — замером, а не верой. Тест
test_injected_intensity_copies_the_surroundings держит свойство: среди
аргументов нет отражения, а без разложения окружения дальность ничего не меняет.
Замечание, важное для оценки ущерба: складка, не видевшая doubleT_obstacle,
и с артефактом ставила настоящему предмету 1.000 (p10 = 0.998) против 0.393
у обстановки — на ближней дальности модель опиралась не только на подсказку.
Но полигонная дальность, измеренная на таких вставках, мерила бы признак,
которого в реальности нет. Поэтому все цифры ниже получены на выборке, где
яркость взята из самой записи.
Отдельный урок про метрику: таблицы выше потребовали починить подсчёт AUC.
Ранги раздавались по порядку в массиве, а не усреднялись на совпадениях, и
признак, у которого все значения равны, получал 0 или 1 вместо 0.5. Первым
ложным сигналом такого рода оказался контраст к фону за 160 м — там он
структурно равен нулю у всех кандидатов сразу. Теперь ранги средние, а сторож
«что делит выборку само по себе» встроен в tools/train_mbon.py и печатается
перед каждым обучением.
11.2. Что даёт обученное считывание
Обучение — полнопакетный логистический спуск по разрежённому коду: 4 000 клеток Кеньона, 100 активных после торможения APL, 23 признака кандидата, 185 252 примера, 19 445 из них предметы. Ёмкость выбрана развёрткой: 8 000 и 20 000 клеток дают ту же AUC (0.9859 и 0.9850 против 0.9860), а в худшем кадре стоят 2.8 и 7.3 мс вместо 1.5 — платить не за что.
Проверка — leave-one-bag-out, отдельная модель на каждую складку. Без этого сквозные цифры были бы ложными: считывание увидело бы проверяемый бэг ровно так же, как память тоннеля, обученная на всём подряд.
| бэг | AUC | обстановки при 95 % предметов | бустинг по сырым признакам |
|---|---|---|---|
doubleT_platform |
0.9923 | 3.54 % | 0.9951 |
roundT_doubleT |
0.9939 | 2.79 % | 0.9987 |
roundT_pressureGate_roundT |
0.9759 | 16.30 % | 0.9866 |
roundT_squareT_pressureGate_squareT |
0.9828 | 11.65 % | 0.9930 |
squareT_platform_squareT_switch |
0.9851 | 5.87 % | 0.9902 |
| среднее | 0.9860 | 8.03 % | 0.9927 |
AUC по полосам дальности: 0.995 / 0.996 / 0.992 / 0.973 / 0.955 / 0.738 — качество честно падает с дальностью, как и должно быть, когда от предмета остаётся четыре луча.
Градиентный бустинг по тем же сырым признакам стабильно лучше на 0.005…0.01 AUC. Это цена разрежённого кода, и мы её знаем: взамен получаем схему, которая считается за 1.5 мс на худшем кадре и держит непрерывное дообучение.
Наверху важности — форма, а не положение и не яркость:
fill +0.0079 abs_lat +0.0051
elongation +0.0076 acc_support +0.0046
h +0.0055 lat +0.0044
Положение в сечении (abs_lat, lat) стоит на 4–6 месте. Формально это
законный признак — опасны именно предметы в габарите, — но границу
«положительной» области задали наши же расстановки (0, ±0.6, ±1.2 м от оси),
и на защите это надо называть вслух, а не прятать.
Сквозной результат, leave-one-bag-out:
| без считывания | со считыванием | |
|---|---|---|
| разных ложных треков на километр | 7.5 | 3.3 |
| кадров с ложной тревогой | 16.7 % | 4.4 % |
| реальный объект на 55 м | 98.9 % | 98.9 % |
| задержка кадра, медиана | 33 мс | 35–41 мс |
На размеченном полигоне (модель по складкам, то есть для каждого бэга та, что его не видела):
| человек стоя | без считывания | со считыванием |
|---|---|---|
| рабочая дальность «как есть» | 62 м | 80 м |
| P@50 м | 0.70 | 0.70 |
| P@100 м | 0.19 | 0.31 |
| посторонних тревог на кадр | 0.169 | 0.030 |
Прибавка не только на человеке: ящик P@100 0.02 → 0.24, человек сидя 0.25 → 0.39, чемодан P@50 0.55 → 0.60, ведро впервые поднялось над нулём (рабочая дальность 8 → 20 м).
Отдельно: прежние заявленные 100 м рабочей дальности и P@100 = 0.53 получены на полигоне с артефактом яркости и завышены. Честные числа без считывания — 80 м и 0.19, со считыванием — 80 м и 0.31.
11.3. Сколько дальности покупается за ложные тревоги
Смысл обученного считывания не в том, чтобы просто показать меньшее число
ложных. Оно даёт запас, а запас надо тратить: поезд движется, и дальность
обнаружения — это время на торможение. Поэтому пороги решения вынесены в
Params и развёрнуты замером. Все строки — leave-one-bag-out по ложным и
полигон по дальности, модель по складкам:
| конфигурация | ложных треков/км | кадров с тревогой | реальный объект | P@50 | P@100 | тревог на кадр, полигон |
|---|---|---|---|---|---|---|
| без считывания | 7.4 | 16.65 % | 98.9 % | 0.70 | 0.19 | 0.169 |
| считывание, три подтверждения | 3.3 | 4.44 % | 98.9 % | 0.70 | 0.31 | 0.030 |
| по умолчанию: считывание, два подтверждения | 3.3 | 4.52 % | 99.5 % | 0.70 | 0.31 | 0.030 |
| тише: смешивание 0.7 | 2.5 | 4.85 % | 98.9 % | 0.70 | 0.23 | 0.016 |
| дальше: порог улики 0.40 | 5.3 | 6.19 % | 98.9 % | 0.71 | 0.34 | 0.039 |
| смешивание 0.7 + порог 0.32 | 4.3 | 7.28 % | 98.9 % | 0.71 | 0.31 | 0.055 |
| порог 0.30 | 7.8 | 8.37 % | — | — | — | — |
| порог 0.22 | 9.8 | 10.04 % | — | — | — | — |
Три вывода, каждый замером.
Смешивание с ручной формулой — не способ купить дальность. Строка «смешивание 0.7 + порог 0.32» даёт ровно ту же P@100 = 0.31, что и чистая модель при штатном пороге, но стоит 4.3 ложных трека вместо 3.3 и вдвое больше посторонних тревог на полигоне. Смешивание полезно в другом качестве — как тихий режим: при штатном пороге оно опускает ложные до 2.5 на км и 0.016 на кадр, отдавая дальность (P@100 0.31 → 0.23). Оно же — страховка на случай, когда предмет не похож ни на что из каталога: ручная формула продолжает работать по геометрии.
Понижение порога работает, но дорого. 0.50 → 0.40 добавляет 0.03 к P@100 человека и 0.07 ящику, а платит 3.3 → 5.3 ложных трека. Дальше кривая только круче: 0.30 стоит 7.8, 0.22 — 9.8, то есть хуже, чем было без считывания вообще. Оставлено пресетом, а не умолчанием.
Два подтверждения вместо трёх оказались бесплатными. Ложные не изменились (3.3 на км, 4.44 → 4.52 % кадров), посторонние тревоги на полигоне не изменились тоже (0.030 на кадр), а обнаружение выросло: реальный объект 98.9 → 99.5 %, рабочая дальность человека сидя 20 → 80 м, чемодана «как есть» 20 → 62 м. Причина видна из данных: на 60…100 м предмет среднего размера виден через кадр, и третьего подтверждения приходится ждать дольше, чем он успевает приблизиться. Это стало умолчанием.
Строки пресетов измерены при трёх подтверждениях — прежнем умолчании. В базовой точке переход на два ложных не изменил, но для пресетов это отдельно не перемерялось.
Чего порогами не изменить. P@150 равна нулю в любой конфигурации. Это не алгоритм: прямая видимость в этих тоннелях 121–167 м, а прирельсовая зона за 80–90 м не наблюдается вовсе — луч скользит по полотну (п. 3 и п. 7.3). Потолок здесь геометрический.
12. Воронка потерь: где именно пропадает видимый предмет
Кривая дальности говорит, сколько мы теряем, но не где. Поэтому полигон инструментирован тремя ступенями: попали ли лучи в предмет (видимость), появился ли кандидат, собрался ли трек, прошло ли решение. Считается доля от видимых наблюдений, то есть от тех, где предмет физически освещён лучами и претензии к алгоритму осмысленны.
Человек стоя, конфигурация по умолчанию:
| полоса | видимых | кандидат | трек | решение | нет кандидата | трек без решения |
|---|---|---|---|---|---|---|
| 6–30 м | 396 | 0.82 | 0.89 | 0.82 | 0.18 | 0.06 |
| 30–50 м | 184 | 0.55 | 0.64 | 0.59 | 0.45 | 0.05 |
| 50–70 м | 147 | 0.75 | 0.82 | 0.81 | 0.25 | 0.01 |
| 70–90 м | 131 | 0.73 | 0.68 | 0.62 | 0.27 | 0.06 |
| 90–120 м | 177 | 0.66 | 0.54 | 0.24 | 0.34 | 0.31 |
| 120–160 м | 251 | 0.62 | 0.37 | 0.00 | 0.38 | 0.37 |
Столбец «трек» бывает выше «кандидата»: трек живёт между кадрами и переживает кадр без кандидата — это не ошибка таблицы, а работа накопителя.
Видны две разные болезни, и лечатся они по-разному.
12.1. Ближняя: 30–50 м, кандидат не рождается
Худшая полоса во всём диапазоне — хуже, чем 120–160 м. Причина известна:
разрез компоненты по контрасту запрещён ближе 55 м (split_near), и предмет,
слипшийся со стеной, уходит вместе с ней. Запрет стоял потому, что снятие
стоило слишком дорого по ложным. Обученное считывание удвоило бюджет, и
проверку повторили:
| не резать ближе | ложных треков/км | кадров с тревогой |
|---|---|---|
| 55 м (принято) | 3.3 | 4.5 % |
| 40 м | 6.3 | 7.5 % |
| 30 м | 7.3 | 9.6 % |
| 20 м | 8.3 | 12.1 % |
| резать везде | 8.3 | 12.7 % |
Весь выигрыш считывания съедается уже к 30 м, а на 20 м мы хуже, чем были без модели вообще. Запрет оставлен. Дополнительный довод: при служебном торможении с 60 км/ч тормозной путь с реакцией около 160 м, то есть полоса 30–50 м для полной остановки поздняя в любом случае — платить за неё ложными тревогами на всей дистанции невыгодно.
12.2. Дальняя: за 90 м трек есть, решения нет
Здесь теряется 0.31 и 0.37 видимых наблюдений уже после того, как трек собран. Первая догадка — порог. Проверена и отвергнута: порог тревоги, линейно опущенный с 0.5 до 0.3 начиная с 90 м, не изменил ни ложных (3.3 на км), ни обнаружения (P@100 = 0.31, решения за 120 м по-прежнему 0). Порог новизны тоже ни при чём: с нулём вместо 0.10 всё то же самое.
Настоящая причина считается на бумаге. Улика растёт на gain · качество за
попадание и падает на leak за промах, при gain 0.34 и leak 0.12. При
доле попаданий p улика не убывает только если p·gain·w > (1-p)·leak. На
120–160 м кандидат появляется в 62 % наблюдений, откуда порог по качеству
w > 0.22 — столько далёкое наблюдение из четырёх лучей не даёт никогда.
Поэтому улика далёкого трека не «чуть ниже порога», а колеблется около нуля,
и снижать порог бессмысленно.
Промах на 150 м, однако, не свидетельство отсутствия предмета, а свойство
решётки: там предмет виден через кадр. Ровно по этой причине число лучей уже
нормируется на ожидаемое для этой дальности (_quality), и промах надо
нормировать так же. Утечка сделана зависящей от дальности (leak_far).
Цена замерена сразу на двух сценах:
| утечка на дальнем краю | ложных/км, память обучена | то же на новой линии |
|---|---|---|
| 0.12 (было) | 3.3 | 20.6 |
| 0.06 | 3.3 | 21.6 |
| 0.03 | 3.3 | 21.6 |
| 0.01 | 3.3 | 21.6 |
Цена плоская: появление долгоживущих далёких треков стоит примерно одного лишнего трека на километр на незнакомой линии и ничего — на знакомой.
12.3. Из чего на самом деле состоит улика далёкого предмета
Три ручки подряд ничего не дали, и это был знак, что ограничитель другой. Поэтому вместо четвёртой развёртки — прямая трассировка: человек стоя вставлен на 150 м, 178 кадров, и на каждом выписаны все множители улики.
| величина | медиана | p90 |
|---|---|---|
| лучей в предмете | 6 | 8 |
| кандидат найден | 164 из 178 кадров | |
| оценка MBON | 0.078 | 0.493 |
| новизна | 0.153 | 0.221 |
| качество улики (произведение) | 0.005 | 0.034 |
Новый трек выживает, только если gain · качество > 0.02, то есть при
качестве выше 0.059. Таких наблюдений 5.5 %. Материал, стало быть, есть
— кандидат находится в 92 % кадров, — а улику копить нечем. Ни порогом, ни
утечкой это не лечится, потому что ноль на любой множитель остаётся нулём.
Дальше видно, какой из двух множителей виноват. На 120–185 м, 299 кандидатов, из них 231 — предмет:
| множитель | предмет | обстановка | AUC |
|---|---|---|---|
| новизна | 0.150 | 0.199 | 0.293 |
| оценка MBON | 0.051 | 0.003 | 0.743 |
Новизна на дальности перевёрнута. Настоящий предмет выглядит более знакомым, чем окружающая обстановка: на шести лучах дескриптор вырождается, и память узнаёт в предмете любую далёкую конструкцию. Мы этим множителем гасили собственный сигнал.
Модель различает, но её ответ раздавлен редкостью. Доля предметов среди кандидатов падает с 42 % на 0–30 м до 3.1 % за 160 м, и калиброванная апостериорная вероятность обязана падать вместе с ней — даже когда улика по существу та же. Различие при этом сохраняется: 0.051 против 0.003, в семнадцать раз.
Отсюда две правки, обе — нормировка на то, что достижимо на этой дальности,
ровно как уже сделано для числа лучей в _quality:
- гашение знакомости с дальностью (
nov_fade_from): за 90 м вклад новизны плавно сходит к нулю, вблизи остаётся как был; - поправка на распространённость (
mbon_prior_from): из логита модели вычитается типичный для этой полосы, то есть апостериорная вероятность превращается в отношение правдоподобий.
Поправка на распространённость сама по себе не дала ничего: ни ложных (3.3 на км), ни обнаружения (P@100 = 0.31, за 120 м ноль), воронка не сдвинулась ни на сотую. Это ожидаемо и поучительно: пока второй множитель обнуляет произведение, правильная правка первого бесполезна.
Гашение знакомости дало главный результат дня:
| человек стоя | было | стало |
|---|---|---|
| P@50 | 0.70 | 0.70 |
| P@100 | 0.31 | 0.48 |
| P@150 | 0.00 | 0.32 |
| решений в полосе 120–160 м | 0.00 | 0.40 |
| ложных треков/км, память обучена | 3.3 | 5.6 |
| то же на новой линии | 21.6 | 21.6 |
| посторонних тревог на кадр, полигон | 0.030 | 0.047 |
Впервые предмет вообще обнаруживается на 150 м. Цена — полтора лишних ложных трека на километр на знакомой линии; на незнакомой правка бесплатна, потому что отказываться там не от чего. Приватный тест — это именно незнакомая линия.
Отдельно про характер цены: лишние далёкие треки дают предупреждения, а не
торможение. Экстренный уровень требует d ≤ max(тормозной путь, 25 м)
(descending.py), поэтому далёкий ложный трек физически не может вызвать
экстренное торможение. Мы платим предупреждениями за дальность обнаружения —
для движущегося поезда это правильная сторона размена.
12.4. Что из этого принято
Обе правки и зависящая от дальности утечка проверены порознь и вместе, каждая на двух сценах — линия с обученной памятью и первый проезд по незнакомой:
| конфигурация | ложных/км, память | то же, новая линия | P@100 | P@150 | тревог на кадр |
|---|---|---|---|---|---|
| как было | 3.3 | 20.6 | 0.31 | 0.00 | 0.030 |
| утечка 0.03 | 3.3 | 21.6 | 0.33 | 0.00 | 0.030 |
| поправка на распространённость | 3.3 | 20.6 | 0.31 | 0.00 | 0.030 |
| гашение знакомости с 90 м | 5.6 | 20.6 | 0.48 | 0.32 | 0.046 |
| гашение + утечка | 5.6 | 21.6 | 0.48 | 0.32 | 0.047 |
| гашение + утечка + поправка | 5.6 | 21.6 | 0.48 | 0.32 | 0.048 |
Принято одно гашение знакомости. Утечка добавляет к нему сотые доли (человек сидя P@100 0.44 → 0.48, решений в полосе 120–160 м 0.37 → 0.40) и стоит одного лишнего ложного трека на километр ровно на той сцене, которая соответствует приватному тесту. Поправка на распространённость не даёт ничего ни отдельно, ни в связке — таблицы обнаружения совпадают до сотой.
Обе отвергнутые правки оставлены в коде выключенными (leak_far,
mbon_prior_from, warn_far): они верны по сути, измеримы одним ключом и
могут пригодиться на линии с другой статистикой. Значения по умолчанию
выключают их полностью.
Итоговая конфигурация и её цена:
| без гашения | принято | |
|---|---|---|
| реальный объект на 55 м | 99.5 % | 99.5 % |
| P@50, человек стоя | 0.70 | 0.70 |
| P@100 | 0.31 | 0.48 |
| P@150 | 0.00 | 0.32 |
| ложных треков/км, знакомая линия | 3.3 | 5.6 |
| кадров с тревогой, знакомая линия | 4.5 % | 9.4 % |
| ложных треков/км, новая линия | 20.6 | 20.6 |
| задержка кадра, медиана | 33 мс | 32 мс |
Размен честный и его надо называть вслух: на знакомой линии доля кадров
с тревогой удваивается. Взамен появляется обнаружение на 150 м, которого не
было вовсе, а на незнакомой линии — там, где будет приватный тест, —
плата нулевая. Кому дороже тишина, чем дальность, ставит nov_fade_from: 0
и возвращается к 3.3 трека на километр.