Lidar_Muxa/tools/validate_calibration.py
Данил Омелечко 623cbb3843 ML-ядро: конвейер обнаружения на схемах мозга дрозофилы
Ретина, ламина, медулла, лобула, грибовидное тело, веерное тело,
центральный комплекс, нисходящие нейроны. Обучение памяти тоннеля и
считывания MBON, оценка leave-one-bag-out, полигон дальности, 24 теста.

Реальный объект на 55 м — 98.9 % кадров, ложных 7.5 трека на км,
кадр обрабатывается за 33 мс на CPU.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 17:13:55 +03:00

93 lines
4.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Сверка калибровки решётки по данным с паспортной таблицей каналов.
Решение нигде не использует паспортные углы: решётка восстанавливается из самих
облаков точек. Но раз таблица из руководства есть, ею стоит воспользоваться как
**независимой проверкой** — совпадение показывает, что выпрямление образа и
оценка углов сделаны правильно, а не подогнаны.
python tools/validate_calibration.py --bag data/for_hackathon/roundT_doubleT
"""
from __future__ import annotations
import argparse
import csv
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt # noqa: E402
import numpy as np # noqa: E402
import _bootstrap as B # noqa: F401,E402
from flyguard.bag import Bag, find_bags # noqa: E402
from flyguard.retina import ScanLayout # noqa: E402
TABLE = B.PKG / "flyguard" / "data" / "pandar128_channels.csv"
def load_table() -> dict[str, np.ndarray]:
rows = list(csv.DictReader(open(TABLE, encoding="utf-8")))
rows.sort(key=lambda r: int(r["channel"]))
return {
"elevation": np.array([float(r["elevation_deg"]) for r in rows]),
"az_offset": np.array([float(r["az_offset_deg"]) for r in rows]),
"max_range": np.array([float(r["max_range_10pct_m"]) for r in rows]),
"far_field": np.array([int(r["far_field"]) for r in rows], bool),
"high_res": np.array([int(r["high_res"]) for r in rows], bool),
}
def main() -> None:
ap = argparse.ArgumentParser(description=__doc__)
ap.add_argument("--bag", action="append")
ap.add_argument("--root", default=str(B.DATA / "for_hackathon"))
args = ap.parse_args()
tab = load_table()
from pathlib import Path
bags = [Path(b) for b in args.bag] if args.bag else find_bags(args.root)
fig, axes = plt.subplots(1, 3, figsize=(16, 4.6), dpi=110)
print(f"{'бэг':42s} {'ошибка элевации, °':>22s} {'ошибка сдвига, °':>20s}")
print("-" * 88)
for p in bags:
bag = Bag(p)
layout = ScanLayout.calibrate([pc for _, pc in bag.frames(start=2, stop=14)])
# сдвиг канала в градусах = целочисленное выпрямление плюс остаток
meas_off = layout.col_shift * layout.az_step_deg + layout.az_resid_deg
meas_off = meas_off - np.median(meas_off) + np.median(tab["az_offset"])
de = layout.el_deg - tab["elevation"]
da = meas_off - tab["az_offset"]
print(f"{p.name:42s} медиана {np.median(np.abs(de)):7.4f} макс {np.abs(de).max():7.4f}"
f" медиана {np.median(np.abs(da)):6.4f} макс {np.abs(da).max():6.4f}")
axes[0].plot(np.arange(128), de, lw=0.9, alpha=0.8, label=p.name[:18])
axes[1].plot(np.arange(128), da, lw=0.9, alpha=0.8)
axes[0].set_title("элевация: измерено − паспорт")
axes[0].set_xlabel("канал"); axes[0].set_ylabel("°")
axes[0].legend(fontsize=6)
axes[1].set_title("азимутальный сдвиг канала: измерено − паспорт")
axes[1].set_xlabel("канал"); axes[1].set_ylabel("°")
for ax in axes[:2]:
ax.axhline(0, color="k", lw=0.6)
ax.grid(alpha=0.2)
ax = axes[2]
ax.plot(np.arange(128), tab["max_range"], lw=1.2, color="tab:blue")
ff = np.flatnonzero(tab["far_field"])
ax.fill_between(ff, 0, 210, color="tab:orange", alpha=0.18,
label=f"дальнобойные каналы {ff.min()+1}–{ff.max()+1}")
hr = np.flatnonzero(tab["high_res"])
ax.fill_between(hr, 0, 210, color="tab:green", alpha=0.10,
label=f"высокое разрешение {hr.min()+1}–{hr.max()+1}")
ax.set_title("паспортная дальность канала при 10 % отражения")
ax.set_xlabel("канал"); ax.set_ylabel("м")
ax.legend(fontsize=7); ax.grid(alpha=0.2)
B.FIGURES.mkdir(parents=True, exist_ok=True)
out = B.FIGURES / "calibration_vs_datasheet.png"
fig.tight_layout(); fig.savefig(out)
print("\nсохранено:", out)
if __name__ == "__main__":
main()