85 lines
3.0 KiB
Python
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()
|