""" patrol.py — Autonomous patrol loop with YOLO PPE detection + LLaVA scene assessment """ import time from API.zmq_api import gradual_stop from API.camera_api import get_frame from API.yolo_api import yolo_ppe_violations, yolo_person_too_close, yolo_summary from API.llava_api import ask_patrol from API.memory_api import mem, log_detection from Brain.executor import move_step from Core.config_loader import load_config _cfg = load_config("Patrol") DEFAULT_DURATION = _cfg["default_duration_minutes"] PROXIMITY_THRESH = _cfg["proximity_threshold"] PROXIMITY_PAUSE = _cfg["proximity_pause_s"] def patrol(duration_minutes: float = 0.0, alert_callback=None): """ Timed patrol loop. Each iteration: 1. YOLO PPE violation check -> log + alert 2. LLaVA scene assessment -> decide next move 3. Proximity safety stop -> pause if someone is too close 4. Execute move_step Parameters ---------- duration_minutes : float How long to patrol (default from config). alert_callback : callable, optional Called with (alert_text: str) whenever an alert fires. """ if duration_minutes <= 0: duration_minutes = DEFAULT_DURATION end_time = time.time() + duration_minutes * 60.0 step = 0 print(f" [Patrol] Starting {duration_minutes:.1f}-minute patrol") try: while time.time() < end_time: step += 1 # ----- 1. YOLO PPE violations ----- violations = yolo_ppe_violations() if violations: for v in violations: alert_text = f"PPE violation: {v}" print(f" [Patrol] {alert_text}") log_detection(v, position="patrol", distance="") if alert_callback: alert_callback(alert_text) # ----- 2. LLaVA scene assessment ----- img_b64 = get_frame() if img_b64: d = ask_patrol(img_b64) else: d = {"observation": "no frame", "alert": None, "next_move": "forward", "duration": 1.0} observation = d.get("observation", "") alert = d.get("alert") next_move = d.get("next_move", "forward") duration = float(d.get("duration", 1.0)) if observation: print(f" [Patrol] step {step}: {observation}") if alert: alert_text = f"Alert: {alert}" print(f" [Patrol] {alert_text}") if alert_callback: alert_callback(alert_text) # Log interesting detections to memory if mem and observation and "person" in observation.lower(): log_detection("person", position="patrol", distance="") # ----- 3. Proximity safety stop (YOLO + LiDAR) ----- lidar_blocked = False try: from API.lidar_api import obstacle_ahead lidar_blocked = obstacle_ahead() except ImportError: pass if yolo_person_too_close(threshold=PROXIMITY_THRESH) or lidar_blocked: reason = "LiDAR obstacle" if lidar_blocked else "Person too close" print(f" [Patrol] {reason} — pausing {PROXIMITY_PAUSE}s") gradual_stop() time.sleep(PROXIMITY_PAUSE) continue # ----- 4. Execute movement ----- if next_move and next_move != "stop": move_step(next_move, duration) else: gradual_stop() time.sleep(0.5) except KeyboardInterrupt: print(" [Patrol] Interrupted") finally: gradual_stop() print(f" [Patrol] Finished after {step} steps") return step