first running version

This commit is contained in:
abraham 2026-09-27 23:09:00 +02:00
parent 7128413c7f
commit 44849611a3
6 changed files with 6968 additions and 50 deletions

11
.gitignore vendored Normal file
View file

@ -0,0 +1,11 @@
# Python-generated files
__pycache__/
*.py[oc]
build/
dist/
wheels/
*.egg-info
# Virtual environments
.venv
.lock

1
.python-version Normal file
View file

@ -0,0 +1 @@
3.13

View file

@ -37,14 +37,18 @@ uv pip install sounddevice numpy scipy requests onnxruntime
> **Nota técnica:** Se seleccionó `onnxruntime` en lugar de `tflite-runtime` para garantizar compatibilidad con versiones modernas de Python en ARM64 sin necesidad de compilar desde fuente. > **Nota técnica:** Se seleccionó `onnxruntime` en lugar de `tflite-runtime` para garantizar compatibilidad con versiones modernas de Python en ARM64 sin necesidad de compilar desde fuente.
--- ---
4. Estado Actual del Software 4. Estado Actual del Software
`pet_speed.py`: Script de visión artificial que: `pet_speed.py`: Script de visión artificial optimizado con aceleración por hardware que:
Inicializa `Picamera2` a 1280x720 a 30 FPS. - Inicializa `Picamera2` con flujo dual sincronizado por hardware (PiSP):
Carga el pipeline oficial de Hailo (`/usr/share/rpi-camera-assets/hailo_yolov8_inference.json`). - Stream `main` (1280x720) para capturas de alta definición y alertas.
Filtra detecciones de COCO para gato (id: 15) y perro (id: 16). - Stream `lores` (640x640) escalado por hardware por el ISP directamente al formato esperado por el modelo YOLOv8 (0% sobrecarga en CPU).
Calcula el centroide del animal y evalúa el desplazamiento entre frames consecutivos para alertar si la velocidad supera el umbral de carrera (`SPEED_THRESHOLD = 0.30`). - Utiliza la API nativa de aceleración `picamera2.devices.hailo.Hailo` cargando el modelo optimizado (`/usr/share/hailo-models/yolov8s_h8.hef`).
Ejecución actual: - Filtra detecciones del conjunto COCO para gato (id: 15) y perro (id: 16).
- Calcula el centroide del animal y evalúa el desplazamiento entre fotogramas consecutivos para alertar si la velocidad supera el umbral de carrera (`SPEED_THRESHOLD = 0.30`).
- Libera limpiamente la cámara y el acelerador Hailo al recibir señal de interrupción (`SIGINT`/`Ctrl+C`).
Ejecución:
```bash ```bash
uv run python pet_speed.py uv run pet_speed.py
``` ```
--- ---
5. Próximos Pasos (Hoja de Ruta) 5. Próximos Pasos (Hoja de Ruta)

5862
hailort.1.log Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,85 +1,116 @@
import time
import math import math
import os
import sys
import time
from picamera2 import Picamera2 from picamera2 import Picamera2
from picamera2.devices.hailo import Hailo, hailo_architecture
# Clases COCO: 15 = Cat (Gato), 16 = Dog (Perro)
TARGET_CLASSES = {15: "Gato", 16: "Perro"} TARGET_CLASSES = {15: "Gato", 16: "Perro"}
SPEED_THRESHOLD = 0.30 SPEED_THRESHOLD = 0.30 # Umbral de velocidad normalizada por segundo
def get_hailo_model_path():
arch = hailo_architecture()
candidates = []
if arch == "HAILO8":
candidates = [
"/usr/share/hailo-models/yolov8s_h8.hef",
"/usr/share/hailo-models/yolov8s_h8l.hef"
]
else:
candidates = [
"/usr/share/hailo-models/yolov8s_h8l.hef",
"/usr/share/hailo-models/yolov8s_h8.hef"
]
for path in candidates:
if os.path.exists(path):
return path
raise FileNotFoundError("No se encontró ningún modelo YOLOv8 HEF en /usr/share/hailo-models/")
def calculate_centroid(box): def calculate_centroid(box):
if isinstance(box, dict): """
x = box.get("x", 0.0) + (box.get("width", 0.0) / 2.0) Hailo YOLOv8 NMS devuelve [ymin, xmin, ymax, xmax, confidence].
y = box.get("y", 0.0) + (box.get("height", 0.0) / 2.0) Calcula el centroide normalizado (x, y).
elif isinstance(box, (list, tuple)) and len(box) >= 4: """
x = (box[0] + box[2]) / 2.0 ymin, xmin, ymax, xmax = box[:4]
y = (box[1] + box[3]) / 2.0 cx = (xmin + xmax) / 2.0
else: cy = (ymin + ymax) / 2.0
x, y = 0.5, 0.5 return cx, cy
return x, y
def main(): def main():
picam2 = Picamera2() model_path = get_hailo_model_path()
print(f"[INFO] Cargando modelo Hailo: {model_path} ...")
post_process_file = "/usr/share/rpi-camera-assets/hailo_yolov8_inference.json" hailo = Hailo(model_path)
model_h, model_w, _ = hailo.get_input_shape()
print(f"[INFO] Modelo cargado. Resolución de entrada: {model_w}x{model_h}")
# Se inyecta la etapa Hailo directamente en la configuración de vídeo picam2 = Picamera2()
# Usamos configuración dual:
# - main: 1280x720 para visualización / grabación / alertas nítidas
# - lores: 640x640 directo al tamaño del modelo Hailo sin sobrecarga de CPU (escalado por hardware PiSP)
config = picam2.create_video_configuration( config = picam2.create_video_configuration(
main={"size": (1280, 720), "format": "RGB888"}, main={"size": (1280, 720), "format": "RGB888"},
controls={"FrameRate": 30}, lores={"size": (model_w, model_h), "format": "RGB888"},
post_process_file=post_process_file controls={"FrameRate": 30}
) )
picam2.configure(config) picam2.configure(config)
picam2.start() picam2.start()
print("[INFO] Pipeline Picamera2 + Hailo-8L activo.") print("[INFO] Pipeline Picamera2 + Hailo activo a 30 FPS.")
print("[INFO] Monitorizando mascotas (Gatos y Perros)... Presiona Ctrl+C para salir.\n")
last_centroid = None last_centroid = None
last_time = time.time() last_time = time.time()
try: try:
while True: while True:
metadata = picam2.capture_metadata() # Capturamos el frame 'lores' escalado por hardware por el ISP
lores_frame = picam2.capture_array("lores")
current_time = time.time() current_time = time.time()
dt = current_time - last_time dt = current_time - last_time
detections = metadata.get("hailo_detections", []) # Inferencia por hardware en la NPU Hailo
detections_by_class = hailo.run(lores_frame)
pet_found = False pet_found = False
for det in detections: for class_id, label in TARGET_CLASSES.items():
class_id = det.get("class_id") if class_id < len(detections_by_class):
confidence = det.get("confidence", 0.0) class_detections = detections_by_class[class_id]
for det in class_detections:
# det: [ymin, xmin, ymax, xmax, confidence]
confidence = det[4]
if class_id in TARGET_CLASSES and confidence > 0.45: if confidence > 0.45:
pet_found = True pet_found = True
label = TARGET_CLASSES[class_id] centroid = calculate_centroid(det)
box = det.get("box", [0, 0, 0, 0])
centroid = calculate_centroid(box)
if last_centroid and dt > 0: if last_centroid and dt > 0:
dx = centroid[0] - last_centroid[0] dx = centroid[0] - last_centroid[0]
dy = centroid[1] - last_centroid[1] dy = centroid[1] - last_centroid[1]
dist = math.hypot(dx, dy) dist = math.hypot(dx, dy)
speed = dist / dt speed = dist / dt
if speed > SPEED_THRESHOLD: if speed > SPEED_THRESHOLD:
print(f"⚠️ [ALERTA] ¡{label} corriendo! Vel: {speed:.2f} norm/s (Conf: {confidence:.2f})") print(f"⚠️ [ALERTA] ¡{label} corriendo! Vel: {speed:.2f} norm/s (Conf: {confidence:.2f})")
else: else:
print(f"[OK] {label} detectado (calmado). Vel: {speed:.2f} norm/s") print(f"🐾 [OK] {label} detectado (calmado). Vel: {speed:.2f} norm/s (Conf: {confidence:.2f})")
last_centroid = centroid last_centroid = centroid
break # Evaluamos la mascota principal detectada
if pet_found:
break break
if not pet_found: if not pet_found:
last_centroid = None last_centroid = None
last_time = current_time last_time = current_time
time.sleep(0.03)
except KeyboardInterrupt: except KeyboardInterrupt:
print("\n[INFO] Deteniendo monitor...") print("\n[INFO] Deteniendo monitor de mascotas...")
finally: finally:
picam2.stop() picam2.stop()
hailo.close()
print("[INFO] Cámara y acelerador Hailo liberados correctamente.")
if __name__ == "__main__": if __name__ == "__main__":
main() main()