Files
uav-search/examples/compare_planners.py
robinson 0d2e580f12 init
2026-09-03 15:50:33 +08:00

85 lines
3.0 KiB
Python

#!/usr/bin/env python3
"""Compare Voronoi+boustrophedon vs Voronoi+mTSP patrol planners."""
from __future__ import annotations
import argparse
from pathlib import Path
import matplotlib.pyplot as plt
from uavsearch.geometry import RectRegion
from uavsearch.partition import generate_uneven_positions
from uavsearch.planner import plan_boustrophedon_patrol, plan_mtsp_patrol
from uavsearch.sim import PatrolSimulator, compare_planners
def main() -> None:
parser = argparse.ArgumentParser(description="Compare patrol planners")
parser.add_argument("--width", type=float, default=1000.0)
parser.add_argument("--height", type=float, default=800.0)
parser.add_argument("--uavs", type=int, default=4)
parser.add_argument("--radius", type=float, default=50.0)
parser.add_argument("--speed", type=float, default=15.0)
parser.add_argument("--seed", type=int, default=42)
parser.add_argument("--duration", type=float, default=600.0)
parser.add_argument("--grid-density", type=float, default=4.0)
parser.add_argument("--save", type=str, default=None)
args = parser.parse_args()
region = RectRegion.from_size(args.width, args.height)
sweep_width = 2 * args.radius
initial_positions = generate_uneven_positions(region, args.uavs, seed=args.seed)
strip_paths, strip_partition = plan_boustrophedon_patrol(
region, args.uavs, sweep_width, initial_positions=initial_positions
)
mtsp_paths, mtsp_partition = plan_mtsp_patrol(
region, args.uavs, args.radius, initial_positions=initial_positions
)
sim_strip = PatrolSimulator(region, strip_paths, args.speed, args.radius, grid_density=args.grid_density)
sim_mtsp = PatrolSimulator(region, mtsp_paths, args.speed, args.radius, grid_density=args.grid_density)
m_strip = sim_strip.run(args.duration)
m_mtsp = sim_mtsp.run(args.duration)
print("Uneven initial positions -> uniform partitions:")
for cell in strip_partition.cells:
ix, iy = cell.initial_position
print(f" UAV {cell.uav_id} at ({ix:.1f}, {iy:.1f}) -> partition {cell.partition_id}")
print()
print("=== Uniform + Boustrophedon ===")
print(m_strip.summary())
print(f"Load balance: {sim_strip.load_balance_ratio():.2f}")
print()
print("=== Uniform + mTSP ===")
print(m_mtsp.summary())
print(f"Load balance: {sim_mtsp.load_balance_ratio():.2f}")
print()
ratio_strip = m_strip.max_revisit_time / max(m_strip.mean_revisit_time, 1e-9)
ratio_mtsp = m_mtsp.max_revisit_time / max(m_mtsp.mean_revisit_time, 1e-9)
print(f"T_max/T_mean — Boustrophedon: {ratio_strip:.2f}, mTSP: {ratio_mtsp:.2f}")
fig = compare_planners(
region,
strip_paths,
mtsp_paths,
args.speed,
args.radius,
args.duration,
strip_partition=strip_partition,
mtsp_partition=mtsp_partition,
)
if args.save:
out = Path(args.save)
out.parent.mkdir(parents=True, exist_ok=True)
fig.savefig(out, dpi=150, bbox_inches="tight")
print(f"Saved comparison to {out}")
else:
plt.show()
if __name__ == "__main__":
main()