first running version
This commit is contained in:
parent
7128413c7f
commit
44849611a3
6 changed files with 6968 additions and 50 deletions
11
.gitignore
vendored
Normal file
11
.gitignore
vendored
Normal 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
1
.python-version
Normal file
|
|
@ -0,0 +1 @@
|
|||
3.13
|
||||
18
Readme.md
18
Readme.md
|
|
@ -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.
|
||||
---
|
||||
4. Estado Actual del Software
|
||||
`pet_speed.py`: Script de visión artificial que:
|
||||
Inicializa `Picamera2` a 1280x720 a 30 FPS.
|
||||
Carga el pipeline oficial de Hailo (`/usr/share/rpi-camera-assets/hailo_yolov8_inference.json`).
|
||||
Filtra detecciones de COCO para gato (id: 15) y perro (id: 16).
|
||||
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`).
|
||||
Ejecución actual:
|
||||
`pet_speed.py`: Script de visión artificial optimizado con aceleración por hardware que:
|
||||
- Inicializa `Picamera2` con flujo dual sincronizado por hardware (PiSP):
|
||||
- Stream `main` (1280x720) para capturas de alta definición y alertas.
|
||||
- Stream `lores` (640x640) escalado por hardware por el ISP directamente al formato esperado por el modelo YOLOv8 (0% sobrecarga en CPU).
|
||||
- Utiliza la API nativa de aceleración `picamera2.devices.hailo.Hailo` cargando el modelo optimizado (`/usr/share/hailo-models/yolov8s_h8.hef`).
|
||||
- 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
|
||||
uv run python pet_speed.py
|
||||
uv run pet_speed.py
|
||||
```
|
||||
---
|
||||
5. Próximos Pasos (Hoja de Ruta)
|
||||
|
|
|
|||
5862
hailort.1.log
Normal file
5862
hailort.1.log
Normal file
File diff suppressed because it is too large
Load diff
1009
hailort.log
1009
hailort.log
File diff suppressed because it is too large
Load diff
117
pet_speed.py
117
pet_speed.py
|
|
@ -1,85 +1,116 @@
|
|||
import time
|
||||
import math
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
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"}
|
||||
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):
|
||||
if isinstance(box, dict):
|
||||
x = box.get("x", 0.0) + (box.get("width", 0.0) / 2.0)
|
||||
y = box.get("y", 0.0) + (box.get("height", 0.0) / 2.0)
|
||||
elif isinstance(box, (list, tuple)) and len(box) >= 4:
|
||||
x = (box[0] + box[2]) / 2.0
|
||||
y = (box[1] + box[3]) / 2.0
|
||||
else:
|
||||
x, y = 0.5, 0.5
|
||||
return x, y
|
||||
"""
|
||||
Hailo YOLOv8 NMS devuelve [ymin, xmin, ymax, xmax, confidence].
|
||||
Calcula el centroide normalizado (x, y).
|
||||
"""
|
||||
ymin, xmin, ymax, xmax = box[:4]
|
||||
cx = (xmin + xmax) / 2.0
|
||||
cy = (ymin + ymax) / 2.0
|
||||
return cx, cy
|
||||
|
||||
def main():
|
||||
picam2 = Picamera2()
|
||||
|
||||
post_process_file = "/usr/share/rpi-camera-assets/hailo_yolov8_inference.json"
|
||||
model_path = get_hailo_model_path()
|
||||
print(f"[INFO] Cargando modelo Hailo: {model_path} ...")
|
||||
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(
|
||||
main={"size": (1280, 720), "format": "RGB888"},
|
||||
controls={"FrameRate": 30},
|
||||
post_process_file=post_process_file
|
||||
lores={"size": (model_w, model_h), "format": "RGB888"},
|
||||
controls={"FrameRate": 30}
|
||||
)
|
||||
|
||||
picam2.configure(config)
|
||||
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_time = time.time()
|
||||
|
||||
try:
|
||||
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()
|
||||
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
|
||||
for det in detections:
|
||||
class_id = det.get("class_id")
|
||||
confidence = det.get("confidence", 0.0)
|
||||
for class_id, label in TARGET_CLASSES.items():
|
||||
if class_id < len(detections_by_class):
|
||||
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:
|
||||
pet_found = True
|
||||
label = TARGET_CLASSES[class_id]
|
||||
box = det.get("box", [0, 0, 0, 0])
|
||||
|
||||
centroid = calculate_centroid(box)
|
||||
if confidence > 0.45:
|
||||
pet_found = True
|
||||
centroid = calculate_centroid(det)
|
||||
|
||||
if last_centroid and dt > 0:
|
||||
dx = centroid[0] - last_centroid[0]
|
||||
dy = centroid[1] - last_centroid[1]
|
||||
dist = math.hypot(dx, dy)
|
||||
speed = dist / dt
|
||||
if last_centroid and dt > 0:
|
||||
dx = centroid[0] - last_centroid[0]
|
||||
dy = centroid[1] - last_centroid[1]
|
||||
dist = math.hypot(dx, dy)
|
||||
speed = dist / dt
|
||||
|
||||
if speed > SPEED_THRESHOLD:
|
||||
print(f"⚠️ [ALERTA] ¡{label} corriendo! Vel: {speed:.2f} norm/s (Conf: {confidence:.2f})")
|
||||
else:
|
||||
print(f"[OK] {label} detectado (calmado). Vel: {speed:.2f} norm/s")
|
||||
if speed > SPEED_THRESHOLD:
|
||||
print(f"⚠️ [ALERTA] ¡{label} corriendo! Vel: {speed:.2f} norm/s (Conf: {confidence:.2f})")
|
||||
else:
|
||||
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
|
||||
|
||||
if not pet_found:
|
||||
last_centroid = None
|
||||
|
||||
last_time = current_time
|
||||
time.sleep(0.03)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\n[INFO] Deteniendo monitor...")
|
||||
print("\n[INFO] Deteniendo monitor de mascotas...")
|
||||
finally:
|
||||
picam2.stop()
|
||||
hailo.close()
|
||||
print("[INFO] Cámara y acelerador Hailo liberados correctamente.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue