forked from Dan4ick/Lidar_Muxa
дальний порог тревоги 0.3 за 90 м: P@150 0.33 -> 0.37; разбор решения по треку
This commit is contained in:
parent
3f8314e908
commit
6685673bdb
6 changed files with 181 additions and 38 deletions
|
|
@ -137,10 +137,10 @@ python tools/plot_benchmark.py # кривые и граф
|
||||||
| Метрика | Значение | Чем измерено |
|
| Метрика | Значение | Чем измерено |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| Реальный объект 0.67 × 1.35 м на 55 м | **99.5 %** кадров | `tools/check_obstacle.py` |
|
| Реальный объект 0.67 × 1.35 м на 55 м | **99.5 %** кадров | `tools/check_obstacle.py` |
|
||||||
| Ложные тревоги, leave-one-bag-out | **8.0 трека на км**, 11.0 % кадров | `tools/evaluate.py --mbon-dir` |
|
| Ложные тревоги, leave-one-bag-out | **8.0 трека на км**, 13.3 % кадров | `tools/evaluate.py --mbon-dir` |
|
||||||
| То же без обученного считывания | 12.9 на км, 25.9 % кадров | там же, без `--mbon-dir` |
|
| То же без обученного считывания | 12.9 на км, 25.9 % кадров | там же, без `--mbon-dir` |
|
||||||
| На незнакомой линии (памяти нет) | **20.3 на км**; без считывания 33.6 | `tools/evaluate.py --no-memory` |
|
| На незнакомой линии (памяти нет) | **21.3 на км**; без считывания 33.6 | `tools/evaluate.py --no-memory` |
|
||||||
| Дальность (полигон, 14 004 наблюдения) | рабочая дальность **100 м**, P@50 = 0.70, **P@100 = 0.53, P@150 = 0.33** | `tools/plot_benchmark.py` |
|
| Дальность (полигон, 14 004 наблюдения) | рабочая дальность **100 м**, P@50 = 0.70, **P@100 = 0.53, P@150 = 0.37** | `tools/plot_benchmark.py` |
|
||||||
| Обработка кадра | медиана 33 мс из бюджета 100 мс | `tools/run_pipeline.py` |
|
| Обработка кадра | медиана 33 мс из бюджета 100 мс | `tools/run_pipeline.py` |
|
||||||
| Разделение «знакомое / новое» (без учителя) | ROC AUC 0.905 | `tools/tune_memory.py` |
|
| Разделение «знакомое / новое» (без учителя) | ROC AUC 0.905 | `tools/tune_memory.py` |
|
||||||
| Считывание MBON «предмет / тоннель» (с учителем) | ROC AUC **0.983** | `tools/train_mbon.py` |
|
| Считывание MBON «предмет / тоннель» (с учителем) | ROC AUC **0.983** | `tools/train_mbon.py` |
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -5,14 +5,14 @@
|
||||||
"path_m": 0.0,
|
"path_m": 0.0,
|
||||||
"alarm_frames": 183,
|
"alarm_frames": 183,
|
||||||
"alarm_rate": 0.9631578947368421,
|
"alarm_rate": 0.9631578947368421,
|
||||||
"fp_objects": 518,
|
"fp_objects": 522,
|
||||||
"fp_tracks": 4,
|
"fp_tracks": 4,
|
||||||
"fp_per_km": NaN,
|
"fp_per_km": NaN,
|
||||||
"fp_median_d": 106.83187139894102,
|
"fp_median_d": 106.83192412328344,
|
||||||
"obj_rate": 0.9947368421052631,
|
"obj_rate": 0.9947368421052631,
|
||||||
"ms_p50": 32.263549999697716,
|
"ms_p50": NaN,
|
||||||
"ms_p95": 33.94519999383192,
|
"ms_p95": NaN,
|
||||||
"train_size": 37073
|
"train_size": 36428
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"bag": "doubleT_platform",
|
"bag": "doubleT_platform",
|
||||||
|
|
@ -25,9 +25,9 @@
|
||||||
"fp_per_km": 4.981343439320496,
|
"fp_per_km": 4.981343439320496,
|
||||||
"fp_median_d": 33.983066409463774,
|
"fp_median_d": 33.983066409463774,
|
||||||
"obj_rate": null,
|
"obj_rate": null,
|
||||||
"ms_p50": 34.84300000491203,
|
"ms_p50": NaN,
|
||||||
"ms_p95": 38.588240001990926,
|
"ms_p95": NaN,
|
||||||
"train_size": 36151
|
"train_size": 35491
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"bag": "roundT_doubleT",
|
"bag": "roundT_doubleT",
|
||||||
|
|
@ -40,53 +40,53 @@
|
||||||
"fp_per_km": 4.976117835149752,
|
"fp_per_km": 4.976117835149752,
|
||||||
"fp_median_d": 20.107822042272232,
|
"fp_median_d": 20.107822042272232,
|
||||||
"obj_rate": null,
|
"obj_rate": null,
|
||||||
"ms_p50": 34.14939999493072,
|
"ms_p50": NaN,
|
||||||
"ms_p95": 40.86443000596773,
|
"ms_p95": NaN,
|
||||||
"train_size": 36584
|
"train_size": 35861
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"bag": "roundT_pressureGate_roundT",
|
"bag": "roundT_pressureGate_roundT",
|
||||||
"frames": 239,
|
"frames": 239,
|
||||||
"path_m": 246.7637153487153,
|
"path_m": 246.7637153487153,
|
||||||
"alarm_frames": 25,
|
"alarm_frames": 29,
|
||||||
"alarm_rate": 0.10460251046025104,
|
"alarm_rate": 0.12133891213389121,
|
||||||
"fp_objects": 25,
|
"fp_objects": 29,
|
||||||
"fp_tracks": 2,
|
"fp_tracks": 2,
|
||||||
"fp_per_km": 8.104919303770778,
|
"fp_per_km": 8.104919303770778,
|
||||||
"fp_median_d": 160.95257704675265,
|
"fp_median_d": 159.45810767922592,
|
||||||
"obj_rate": null,
|
"obj_rate": null,
|
||||||
"ms_p50": 39.37539999969886,
|
"ms_p50": NaN,
|
||||||
"ms_p95": 41.14257999899564,
|
"ms_p95": NaN,
|
||||||
"train_size": 36718
|
"train_size": 36114
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"bag": "roundT_squareT_pressureGate_squareT",
|
"bag": "roundT_squareT_pressureGate_squareT",
|
||||||
"frames": 239,
|
"frames": 239,
|
||||||
"path_m": 273.9439574444854,
|
"path_m": 273.9439574444854,
|
||||||
"alarm_frames": 35,
|
"alarm_frames": 54,
|
||||||
"alarm_rate": 0.14644351464435146,
|
"alarm_rate": 0.22594142259414227,
|
||||||
"fp_objects": 35,
|
"fp_objects": 54,
|
||||||
"fp_tracks": 4,
|
"fp_tracks": 4,
|
||||||
"fp_per_km": 14.601526667404583,
|
"fp_per_km": 14.601526667404583,
|
||||||
"fp_median_d": 105.12193877726114,
|
"fp_median_d": 106.72244931783379,
|
||||||
"obj_rate": null,
|
"obj_rate": null,
|
||||||
"ms_p50": 40.1726000054623,
|
"ms_p50": NaN,
|
||||||
"ms_p95": 42.31233000136854,
|
"ms_p95": NaN,
|
||||||
"train_size": 36083
|
"train_size": 35654
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"bag": "squareT_platform_squareT_switch",
|
"bag": "squareT_platform_squareT_switch",
|
||||||
"frames": 239,
|
"frames": 239,
|
||||||
"path_m": 270.98139848673645,
|
"path_m": 270.98139848673645,
|
||||||
"alarm_frames": 40,
|
"alarm_frames": 45,
|
||||||
"alarm_rate": 0.16736401673640167,
|
"alarm_rate": 0.18828451882845187,
|
||||||
"fp_objects": 40,
|
"fp_objects": 45,
|
||||||
"fp_tracks": 2,
|
"fp_tracks": 2,
|
||||||
"fp_per_km": 7.380580405772363,
|
"fp_per_km": 7.380580405772363,
|
||||||
"fp_median_d": 104.76858112973235,
|
"fp_median_d": 107.47124911234653,
|
||||||
"obj_rate": null,
|
"obj_rate": null,
|
||||||
"ms_p50": 37.06099999908474,
|
"ms_p50": NaN,
|
||||||
"ms_p95": 39.86911000647524,
|
"ms_p95": NaN,
|
||||||
"train_size": 33658
|
"train_size": 33494
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
@ -269,6 +269,16 @@ $$q = \underbrace{\min\!\left(\frac{n_\text{лучей}}{n_\text{ожид}(d)},1
|
||||||
и экстренное торможение по надёжному близкому объекту. Пороги с гистерезисом — без него трек
|
и экстренное торможение по надёжному близкому объекту. Пороги с гистерезисом — без него трек
|
||||||
на границе даёт дребезг, а дребезжащая команда торможения хуже её отсутствия.
|
на границе даёт дребезг, а дребезжащая команда торможения хуже её отсутствия.
|
||||||
|
|
||||||
|
Порог предупреждения зависит от дальности: до 90 м он 0.5, дальше линейно сходит к 0.3 на
|
||||||
|
200 м. Далёкая улика физически слабее — четыре-восемь лучей вместо сотни, — а цена ошибки ниже:
|
||||||
|
до предмета ещё сотня метров и десятки кадров на подтверждение. Экстренный порог не снижается
|
||||||
|
никогда. Это подняло обнаружение на 150 м с 0.33 до 0.37 без потерь ближе (EXPERIMENTS п. 15.9).
|
||||||
|
|
||||||
|
Решение принимается по улике трека, а не по среднему качеству его наблюдений, хотя улика
|
||||||
|
насыщается: у всего, что видно дольше пары секунд, она равна единице. Проверено три способа
|
||||||
|
учесть среднее — обученное считывание по треку, жёсткий порог и смешивание, — и ни один не
|
||||||
|
дал размена лучше простого порога на незнакомой линии (EXPERIMENTS п. 15.5–15.8).
|
||||||
|
|
||||||
Тормозной путь считается как `v·t_реакции + v²/(2a)` и сравнивается с дистанцией до объекта.
|
Тормозной путь считается как `v·t_реакции + v²/(2a)` и сравнивается с дистанцией до объекта.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -1398,6 +1398,10 @@ h +0.0055 lat +0.0044
|
||||||
могут пригодиться на линии с другой статистикой. Значения по умолчанию
|
могут пригодиться на линии с другой статистикой. Значения по умолчанию
|
||||||
выключают их полностью.
|
выключают их полностью.
|
||||||
|
|
||||||
|
*Позднее:* `warn_far` перемерен на конфигурации после п. 12.3 и 12.5 и
|
||||||
|
**принят** — P@150 0.33 → 0.37 без потерь ближе. Почему отказ устарел —
|
||||||
|
п. 15.9.
|
||||||
|
|
||||||
Итоговая конфигурация и её цена:
|
Итоговая конфигурация и её цена:
|
||||||
|
|
||||||
| | без гашения | **принято** |
|
| | без гашения | **принято** |
|
||||||
|
|
@ -1790,10 +1794,33 @@ P@150 0.32), а на всём, что меньше, — заметно хуже:
|
||||||
потолок. **Тот самый вес, из которого улика складывается, различает их
|
потолок. **Тот самый вес, из которого улика складывается, различает их
|
||||||
идеально.** Сумма потеряла то, что было в слагаемых.
|
идеально.** Сумма потеряла то, что было в слагаемых.
|
||||||
|
|
||||||
|
**Где именно это бьёт — оговорка.** `doubleT_obstacle` записан со **стоящего**
|
||||||
|
поезда, и там наблюдений у каждого трека сотни, так что потолка достигают и
|
||||||
|
предмет, и обстановка. На полигоне поезд едет, ложный трек столько не живёт, и
|
||||||
|
картина другая: улика на потолке у 79 % наблюдений предмета в полосе 0…30 м и
|
||||||
|
лишь у 1.5 % ложных, на 80…110 м — 31 % против 3.4 %.
|
||||||
|
|
||||||
|
То есть насыщение само по себе ложных тревог не создаёт. Оно **обесценивает
|
||||||
|
улику как меру**: у предмета она перестаёт расти, и весь запас, который он
|
||||||
|
заработал качеством наблюдений, пропадает — вместо «этот трек вчетверо
|
||||||
|
убедительнее» остаётся «оба по единице». Хуже всего это там, где поезд стоит
|
||||||
|
или ползёт, то есть ровно в ситуации, когда препятствие опаснее всего.
|
||||||
|
|
||||||
Отсюда и решение: сравнивать с порогом не улику, а её геометрическую смесь со
|
Отсюда и решение: сравнивать с порогом не улику, а её геометрическую смесь со
|
||||||
средним весом наблюдения, `w_mean^b · улика^(1−b)`. При `b = 0` всё ровно как
|
средним весом наблюдения, `w_mean^b · улика^(1−b)`. При `b = 0` всё ровно как
|
||||||
раньше, так что размен меряется, а не объявляется.
|
раньше, так что размен меряется, а не объявляется.
|
||||||
|
|
||||||
|
Что смешивание делает с отсчётом, посчитано прямо по выборке. При `b = 0.3`
|
||||||
|
через порог 0.5 уходят вниз 180 предметных наблюдений и 668 ложных — на одно
|
||||||
|
потерянное обнаружение три и семь десятых убранных ложных. При `b = 0.2`
|
||||||
|
соотношение лучше: 73 против 348, то есть 4.8. При `b = 0.5` оно рушится до
|
||||||
|
1.3, и это то же колено, что видно в таблице п. 15.6.
|
||||||
|
|
||||||
|
Смешивание при этом **не только давит**: у 21 % наблюдений отсчёт растёт — это
|
||||||
|
молодые треки с хорошими наблюдениями, у которых улика ещё мала. Среди ложных
|
||||||
|
таких даже больше (25.5 % против 14.6 % среди предметных), и на незнакомой
|
||||||
|
линии, где память ничего не подавляет, это выходит боком (п. 15.8).
|
||||||
|
|
||||||
### 15.2. Выборка по трекам
|
### 15.2. Выборка по трекам
|
||||||
|
|
||||||
Полигон научили попутно выкладывать описание каждого живого трека на каждом
|
Полигон научили попутно выкладывать описание каждого живого трека на каждом
|
||||||
|
|
@ -1941,3 +1968,100 @@ leave-one-bag-out.
|
||||||
(п. 12.5), для новизны (п. 12.3) и для утечки улики. Это возможно, но
|
(п. 12.5), для новизны (п. 12.3) и для утечки улики. Это возможно, но
|
||||||
смешивание даёт тот же эффект мягко и без ещё одной калибровочной кривой,
|
смешивание даёт тот же эффект мягко и без ещё одной калибровочной кривой,
|
||||||
поэтому порог оставлен выключенным (`track_gate = 0`).
|
поэтому порог оставлен выключенным (`track_gate = 0`).
|
||||||
|
|
||||||
|
### 15.8. Смешивание меняет не в ту сторону
|
||||||
|
|
||||||
|
Ложные треки на километр, обе сцены, считывание по складкам:
|
||||||
|
|
||||||
|
| настройка | знакомая линия | незнакомая линия | объект на 55 м |
|
||||||
|
|---|---:|---:|---:|
|
||||||
|
| база | 8.0 | **20.3** | 99.5 % |
|
||||||
|
| один дальний порог 0.30 | **8.0** | 21.3 | 99.5 % |
|
||||||
|
| смешивание 0.3 + дальний порог с 60 м | 6.3 | **24.8** | 99.5 % |
|
||||||
|
| смешивание 0.2 + дальний порог с 60 м | 8.0 | 24.0 | 99.5 % |
|
||||||
|
|
||||||
|
Смешивание улучшает знакомую линию и **портит незнакомую**. Это не случайность,
|
||||||
|
и механизм виден из разбора п. 15.1: смешивание не только давит — у 21 %
|
||||||
|
наблюдений оно отсчёт поднимает, и среди ложных таких больше, чем среди
|
||||||
|
предметных (25.5 % против 14.6 %). Поднимаются молодые треки с хорошими
|
||||||
|
наблюдениями и малой уликой. На знакомой линии память тоннеля такие треки уже
|
||||||
|
погасила через новизну, а на незнакомой гасить нечем — и смешивание их
|
||||||
|
вытаскивает.
|
||||||
|
|
||||||
|
Приватный тест — это незнакомая линия. Значит, смешивание отвергается:
|
||||||
|
`track_blend = 0` по умолчанию.
|
||||||
|
|
||||||
|
И это ещё не всё. Смешивание надо сравнивать не с базой, а с **тем же разменом,
|
||||||
|
полученным простым поднятием порога** — иначе мы сравниваем две разные рабочие
|
||||||
|
точки и выдаём сдвиг по кривой за улучшение кривой. Контроль:
|
||||||
|
|
||||||
|
| настройка | P@50 | P@100 | P@150 | посторонних |
|
||||||
|
|---|---|---|---|---:|
|
||||||
|
| смешивание 0.2 + дальний порог с 60 м | 0.71 | 0.47 | 0.37 | 817 |
|
||||||
|
| **порог 0.60 + дальний порог, без смешивания** | 0.70 | **0.49** | 0.37 | **816** |
|
||||||
|
|
||||||
|
Одинаковое число посторонних тревог, и при нём простой порог даёт обнаружение
|
||||||
|
на 100 м **лучше**. Никакой новой кривой смешивание не открывает — оно двигает
|
||||||
|
по той же, что и ключ, который у нас уже был.
|
||||||
|
|
||||||
|
Итог: `track_blend = 0`, `track_gate = 0`. Накопители на треке и считывание по
|
||||||
|
трекам остаются в коде как инструмент замера — они и дали понимание, что улика
|
||||||
|
насыщается, — но в решении не участвуют.
|
||||||
|
|
||||||
|
### 15.9. Отрицательные результаты стареют
|
||||||
|
|
||||||
|
Выигрыш в этой главе дал не считывание по трекам, ради которого всё
|
||||||
|
затевалось, а ключ, **отвергнутый тремя разделами раньше**. П. 12.2 говорит
|
||||||
|
дословно: «порог тревоги, линейно опущенный с 0.5 до 0.3 начиная с 90 м, не
|
||||||
|
изменил ни ложных (3.3 на км), ни обнаружения (P@100 = 0.31, решения за 120 м
|
||||||
|
по-прежнему 0)».
|
||||||
|
|
||||||
|
Тогда это было правдой, и причина там же, в п. 12.3: улика далёкого трека
|
||||||
|
колебалась около нуля, потому что знакомость была на той дальности перевёрнута
|
||||||
|
и входила в вес **множителем**. Опускать порог под сигналом, который равен
|
||||||
|
нулю, бессмысленно — что замер и показал.
|
||||||
|
|
||||||
|
Потом приняли гашение знакомости с 90 м (п. 12.3) и порог по лучам,
|
||||||
|
нормированный на дальность (п. 12.5). Улика далёкого трека перестала быть
|
||||||
|
нулём. Тот же самый ключ, тот же самый замер — и P@150 растёт с 0.33 до 0.37
|
||||||
|
ценой нуля ложных треков на знакомой линии и одного на незнакомой.
|
||||||
|
|
||||||
|
Отсюда правило, которое стоит дороже самой правки: **отрицательный результат
|
||||||
|
верен только для той конфигурации, в которой он получен.** Каждое принятое
|
||||||
|
изменение обесценивает часть прежних отказов, и дешёвые из них надо
|
||||||
|
перепроверять. Здесь это стоило одного прогона на четыре минуты.
|
||||||
|
|
||||||
|
Варианты той же правки, полигон, `человек_стоя`:
|
||||||
|
|
||||||
|
| настройка | P@50 | P@100 | P@150 | посторонних |
|
||||||
|
|---|---|---|---|---:|
|
||||||
|
| база | 0.70 | 0.53 | 0.33 | 999 |
|
||||||
|
| **порог 0.30 с 90 м** | 0.70 | 0.53 | **0.37** | **1091** |
|
||||||
|
| порог 0.30 с 60 м | 0.70 | 0.53 | 0.37 | 1197 |
|
||||||
|
| порог 0.20 с 90 м | 0.70 | 0.53 | 0.38 | 1181 |
|
||||||
|
| порог 0.30 + утечка 0.03 | 0.70 | **0.48** | 0.40 | 1172 |
|
||||||
|
|
||||||
|
Начинать раньше незачем: обнаружение то же, ложных больше. Опускать до 0.20 —
|
||||||
|
сотая на 150 м за восемь процентов посторонних тревог. Утечку, отвергнутую
|
||||||
|
в п. 12.4, на нынешней базе тоже перемерили: теперь она даёт P@150 0.40, но
|
||||||
|
забирает 100 м (0.53 → 0.48) и рабочую дальность 100 → 80 м — отвергнута
|
||||||
|
снова, уже по другой причине. Правило «перепроверять» не означает
|
||||||
|
«перепринимать».
|
||||||
|
|
||||||
|
Принято: `warn_far = 0.30`, `warn_far_from = 90`. Итог на всех сценах:
|
||||||
|
|
||||||
|
| | было | **стало** |
|
||||||
|
|---|---:|---:|
|
||||||
|
| P@150, человек стоя | 0.33 | **0.37** |
|
||||||
|
| P@150, человек сидя | 0.22 | **0.24** |
|
||||||
|
| P@100 / P@150, ящик | 0.38 / 0.03 | **0.40 / 0.06** |
|
||||||
|
| P@50 и P@100, человек | 0.70 / 0.53 | 0.70 / 0.53 |
|
||||||
|
| решений в полосе 90–120 м / 120–160 м | 0.49 / 0.38 | **0.51 / 0.41** |
|
||||||
|
| ложных треков на км, знакомая линия | 8.0 | **8.0** |
|
||||||
|
| то же, незнакомая линия | 20.3 | 21.3 |
|
||||||
|
| настоящий объект на 55 м | 99.5 % | **99.5 %** |
|
||||||
|
|
||||||
|
Экстренного торможения правка не касается: у него свой порог и условие
|
||||||
|
`d ≤ max(тормозной путь, 25 м)`, поэтому далёкий ложный трек физически не
|
||||||
|
может его вызвать. Мы снова платим предупреждениями за дальность — для
|
||||||
|
движущегося поезда это правильная сторона размена (как и в п. 12.3).
|
||||||
|
|
|
||||||
|
|
@ -125,8 +125,17 @@ class Params:
|
||||||
# (3.3 на км в обоих случаях). Замер — EXPERIMENTS п. 11.3.
|
# (3.3 на км в обоих случаях). Замер — EXPERIMENTS п. 11.3.
|
||||||
min_hits: int = 2
|
min_hits: int = 2
|
||||||
novelty_floor: float = 0.10
|
novelty_floor: float = 0.10
|
||||||
warn_far: float = 0.5 # порог тревоги на дальнем краю
|
# Порог тревоги, линейно опускаемый с 0.5 до `warn_far` на отрезке от
|
||||||
warn_far_from: float = 90.0 # с какой дальности он начинает падать
|
# `warn_far_from` до 200 м. В п. 12.2 ровно эта правка была отвергнута:
|
||||||
|
# тогда улика далёкого трека колебалась около нуля, и опускать порог под
|
||||||
|
# нулевым сигналом было бессмысленно. После гашения знакомости и порога
|
||||||
|
# по лучам улика перестала быть нулём, и та же правка дала P@150
|
||||||
|
# 0.33 → 0.37 без потерь на 50 и 100 м ценой нуля ложных треков на
|
||||||
|
# знакомой линии и одного на незнакомой. Экстренного торможения она не
|
||||||
|
# касается: у него свой порог и условие d ≤ max(тормозной путь, 25 м).
|
||||||
|
# EXPERIMENTS п. 15.9.
|
||||||
|
warn_far: float = 0.30
|
||||||
|
warn_far_from: float = 90.0
|
||||||
leak_far: float = 0.12 # утечка улики на дальнем краю
|
leak_far: float = 0.12 # утечка улики на дальнем краю
|
||||||
leak_far_from: float = 90.0 # с какой дальности она начинает падать
|
leak_far_from: float = 90.0 # с какой дальности она начинает падать
|
||||||
d_min: float = 4.0
|
d_min: float = 4.0
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue