Ретина, ламина, медулла, лобула, грибовидное тело, веерное тело, центральный комплекс, нисходящие нейроны. Обучение памяти тоннеля и считывания MBON, оценка leave-one-bag-out, полигон дальности, 24 теста. Реальный объект на 55 м — 98.9 % кадров, ложных 7.5 трека на км, кадр обрабатывается за 33 мс на CPU.
93 lines
4.3 KiB
Python
93 lines
4.3 KiB
Python
"""Сверка калибровки решётки по данным с паспортной таблицей каналов.
|
||
|
||
Решение нигде не использует паспортные углы: решётка восстанавливается из самих
|
||
облаков точек. Но раз таблица из руководства есть, ею стоит воспользоваться как
|
||
**независимой проверкой** — совпадение показывает, что выпрямление образа и
|
||
оценка углов сделаны правильно, а не подогнаны.
|
||
|
||
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()
|