Ретина, ламина, медулла, лобула, грибовидное тело, веерное тело, центральный комплекс, нисходящие нейроны. Обучение памяти тоннеля и считывания MBON, оценка leave-one-bag-out, полигон дальности, 24 теста. Реальный объект на 55 м — 98.9 % кадров, ложных 7.5 трека на км, кадр обрабатывается за 33 мс на CPU. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
91 lines
3.9 KiB
Python
91 lines
3.9 KiB
Python
"""Проверка стабилизации и осевой линии пути на всех бэгах.
|
|
|
|
Печатает устойчивость плоскости рельсов, оценку радиуса кривой и реальную
|
|
дальность прямой видимости; рисует профили осевой линии.
|
|
|
|
python tools/analyze_corridor.py --frames 60
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
|
|
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.geometry import TrackFrame, fit_corridor, fit_rail_plane # noqa: E402
|
|
from flyguard.retina import ScanLayout # noqa: E402
|
|
|
|
|
|
def run(bag_path, n_frames: int, stride: int, fov: float, ax):
|
|
bag = Bag(bag_path)
|
|
layout = ScanLayout.calibrate([pc for _, pc in bag.frames(start=2, stop=14)])
|
|
cols = layout.column_slice(fov)
|
|
lay = layout.sub(cols)
|
|
|
|
plane = corridor = None
|
|
heights, pitches, rolls, rmss, radii, reach = [], [], [], [], [], []
|
|
curves = []
|
|
for k, (_, pc) in enumerate(bag.frames(stop=n_frames * stride, stride=stride)):
|
|
img = layout.project(pc).crop(cols)
|
|
plane = fit_rail_plane(img, lay, prev=plane)
|
|
tf = TrackFrame(img, lay, plane)
|
|
corridor = fit_corridor(tf, prev=corridor)
|
|
|
|
heights.append(plane.height); pitches.append(plane.pitch_deg)
|
|
rolls.append(plane.roll_deg); rmss.append(plane.rms)
|
|
radii.append(corridor.radius)
|
|
# дальность прямой видимости: дальше какой дистанции возвраты иссякают
|
|
d = tf.d[tf.valid]
|
|
reach.append(np.percentile(d, 99.9) if d.size else 0.0)
|
|
if k % max(1, n_frames // 6) == 0:
|
|
dd = np.linspace(0, 200, 100)
|
|
curves.append(corridor.centre(dd))
|
|
|
|
name = bag.path.name
|
|
print(f"=== {name} ({len(heights)} кадров)")
|
|
print(f" высота сенсора: {np.mean(heights):.3f} ± {np.std(heights):.3f} м")
|
|
print(f" тангаж {np.mean(pitches):+.3f}° ± {np.std(pitches):.3f}° "
|
|
f"крен {np.mean(rolls):+.3f}° ± {np.std(rolls):.3f}° "
|
|
f"rms {np.mean(rmss):.3f} м")
|
|
r = np.array(radii)
|
|
fin = np.isfinite(r)
|
|
print(f" радиус кривой: медиана {np.median(r[fin]) if fin.any() else float('inf'):.0f} м, "
|
|
f"прямых кадров {np.mean(~fin):.0%}")
|
|
print(f" дальность прямой видимости p99.9: медиана {np.median(reach):.0f} м, "
|
|
f"макс {np.max(reach):.0f} м")
|
|
|
|
dd = np.linspace(0, 200, 100)
|
|
for c in curves:
|
|
ax.plot(dd, c, lw=1, alpha=0.7)
|
|
ax.axhline(1.7, color="r", ls="--", lw=0.8)
|
|
ax.axhline(-1.7, color="r", ls="--", lw=0.8)
|
|
ax.set_title(f"{name}\nR≈{np.median(r[fin]) if fin.any() else float('inf'):.0f} м, "
|
|
f"видимость {np.median(reach):.0f} м", fontsize=9)
|
|
ax.set_xlabel("вперёд, м"); ax.set_ylabel("ось пути, м")
|
|
ax.set_ylim(-12, 12)
|
|
|
|
|
|
def main() -> None:
|
|
ap = argparse.ArgumentParser(description=__doc__)
|
|
ap.add_argument("--root", default=str(B.DATA / "for_hackathon"))
|
|
ap.add_argument("--frames", type=int, default=60)
|
|
ap.add_argument("--stride", type=int, default=4)
|
|
ap.add_argument("--fov", type=float, default=35.0)
|
|
args = ap.parse_args()
|
|
|
|
bags = find_bags(args.root)
|
|
fig, axes = plt.subplots(1, len(bags), figsize=(3.6 * len(bags), 3.6), dpi=110, squeeze=False)
|
|
for ax, b in zip(axes[0], bags):
|
|
run(b, args.frames, args.stride, args.fov, ax)
|
|
B.FIGURES.mkdir(parents=True, exist_ok=True)
|
|
out = B.FIGURES / "corridor.png"
|
|
fig.tight_layout(); fig.savefig(out)
|
|
print("сохранено:", out)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|