Caso práctico: Detección movimiento OpenCV Jetson Orin Nano

Caso práctico: Detección movimiento OpenCV Jetson Orin Nano — hero

Objetivo y caso de uso

Qué construirás: Un sistema “opencv-csi-motion-detection” que captura en tiempo real desde la cámara CSI IMX219 en un Jetson Orin Nano y detecta movimiento con OpenCV. Mostrará cuadros delimitadores y FPS en pantalla y registrará eventos cuando haya actividad.

Para qué sirve

  • Vigilancia de pasillos/entradas: registrar cada cruce con marca de tiempo.
  • Monitorización de línea de producción: detectar interrupciones o presencia/ausencia de piezas en una ROI.
  • Detección de intrusión en zona restringida: activar una acción (p. ej., guardar clip) en una franja horaria.
  • Conteo orientativo de eventos (cruces) en un área definida del plano.
  • Auto-grabación de clips breves sólo cuando hay movimiento para ahorrar espacio.

Resultado esperado

  • FPS estable de 25–30 a 1280×720 con IMX219, con sobreimpresión de cuadros cuando hay movimiento.
  • Al menos 90% de eventos reales detectados en condiciones de luz normales (falsos positivos limitados ajustando umbrales).
  • Latencia fin a fin por debajo de 120 ms a 720p.

Público objetivo: makers, ingenieros de visión/IoT edge y equipos de operaciones; Nivel: intermedio.

Arquitectura/flujo: IMX219 (CSI) → GStreamer/nvarguscamerasrc → OpenCV VideoCapture → preprocesado (grayscale+blur) → sustracción de fondo/diferencia de frames → contornos y cuadros → cálculo de FPS → filtros (ROI/horario) → logger de eventos y grabación opcional → display con overlay (objetivo: 25–30 FPS, <120 ms a 720p).

Prerrequisitos (SO y toolchain concreta)

Usaremos el NVIDIA Jetson Orin Nano Developer Kit con JetPack 6.1 (L4T r36.3), que incluye Ubuntu 22.04 aarch64 y los stacks multimedia/IA de NVIDIA. Asegúrate de arrancar con JetPack 6.x (no 5.x).

  • Sistema:
  • JetPack: 6.1 (L4T r36.3.0)
  • Ubuntu: 22.04 LTS (aarch64)
  • Toolchain y librerías (suministradas por JetPack 6.1 o instaladas):
  • CUDA 12.2
  • TensorRT 8.6.3
  • OpenCV 4.8.0 (compilada con soporte GStreamer y, en general, con aceleración Jetson)
  • GStreamer 1.20.x (plugins base, good, bad, nvvidconv, nvarguscamerasrc)
  • Python 3.10.12
  • NumPy 1.26.x
  • (Opcional, solo para referencia de GPU en validación) trtexec de TensorRT 8.6.3

Verifica versiones:

# JetPack / L4T
cat /etc/nv_tegra_release
# Alternativa (si instalado):
jetson_release -v

# Kernel y paquetes NVIDIA/TensorRT
uname -a
dpkg -l | grep -E 'nvidia|tensorrt|cuda|cudnn'

# OpenCV en Python
python3 -c "import cv2; import numpy as np; print('OpenCV:', cv2.__version__); print('NumPy:', np.__version__)"

# GStreamer
gst-launch-1.0 --version

Ejemplo de salida esperada (puede variar levemente por parches menores, pero mantenemos coherencia con 6.1):
– L4T R36.3
– CUDA 12.2
– TensorRT 8.6.3 (libnvinfer8)
– OpenCV 4.8.0
– GStreamer 1.20.x
– Python 3.10.12

Paquetes a instalar/confirmar:

sudo apt update
sudo apt install -y \
  python3-opencv python3-numpy python3-pip \
  gstreamer1.0-tools gstreamer1.0-plugins-base \
  gstreamer1.0-plugins-good gstreamer1.0-plugins-bad \
  v4l-utils tegrastats

# Asegurar pip reciente
python3 -m pip install --upgrade pip

Ajuste de rendimiento (opcional pero recomendado para mediciones):

# Consultar modo actual
sudo nvpmodel -q
# Poner en MAXN (modo 0 normalmente es el de mayor rendimiento en Orin Nano)
sudo nvpmodel -m 0
# Fijar clocks al máximo temporalmente (revertir al final)
sudo jetson_clocks

Advertencia térmica: al usar jetson_clocks, vigila temperaturas (tegrastats) y asegúrate de una ventilación adecuada.

Materiales

  • NVIDIA Jetson Orin Nano Developer Kit + Raspberry Pi Camera Module v2 (Sony IMX219).
  • Cable FFC de 15 pines para la cámara (incluido normalmente con la cámara).
  • Tarjeta microSD de al menos 64 GB (si usas el slot microSD) o NVMe M.2 para sistema.
  • Fuente de alimentación 5V/4A USB-C de calidad.
  • Acceso a red (Ethernet o Wi-Fi) para instalar paquetes.
  • Opcional: monitor HDMI y teclado/ratón; aunque los pasos usan solo terminal.

Preparación y conexión

Conexión de la cámara CSI (IMX219) al Jetson Orin Nano

  • El Jetson Orin Nano Developer Kit dispone de un conector CSI de 15 pines compatible con la Raspberry Pi Camera v2.
  • Desenergiza la placa antes de conectar la cámara.
  • La orientación del cable FFC es crítica: la cara con contactos suele ir hacia el conector (lado del conector HDMI; revisa el “CAM0/CAM1” impreso en la placa). Inserta el FFC recto y bloquea la pestaña.

Tabla (referencial) de elementos de conexión y verificación:

Elemento Puerto/Conector en Jetson Detalle/Acción Resultado esperado
Cámara RPi v2 (IMX219) CSI “CAM0” (15 pines) Insertar FFC, cerrar clip FFC firme y recto
Alimentación USB-C 5V/4A Encender Jetson LED encendido, boot correcto
Verificación sensor nvargus-daemon Servicio activo por defecto Sin errores al usar nvarguscamerasrc
Test rápido GStreamer nvarguscamerasrc Ver pipeline básico Se muestra video o fakesink sin errores

Verifica que la cámara responde (prueba básica, sin GUI):

# Prueba: tomar 100 frames y descartar (fakesink)
gst-launch-1.0 -e nvarguscamerasrc num-buffers=100 ! 'video/x-raw(memory:NVMM), width=1280, height=720, framerate=30/1' ! fakesink

Si no hay errores, el sensor IMX219 es detectado por Argus.

Código completo (Python + OpenCV con GStreamer CSI)

Implementaremos un detector de movimiento básico con:
– Captura GStreamer desde CSI (nvarguscamerasrc).
– Conversión a BGR para OpenCV (appsink).
– Sustracción de fondo MOG2 + operaciones morfológicas.
– Detección de contornos y filtrado por área mínima.
– Overlay: rectángulos, texto con FPS y estado.
– Registro de eventos con timestamp.
– Opcional: guardado de video cuando se detecta movimiento.

Guarda este script como opencv_csi_motion_detection.py:

#!/usr/bin/env python3
import argparse
import time
import sys
import cv2
import numpy as np
from collections import deque
from datetime import datetime

def gstreamer_pipeline(
    sensor_id=0,
    width=1280,
    height=720,
    framerate=30,
    flip_method=0
):
    # Pipeline NVArgus -> NVMM -> nvvidconv -> BGRx -> videoconvert -> BGR -> appsink
    # Ajusta flip_method si necesitas rotación (0 sin flip)
    return (
        f"nvarguscamerasrc sensor-id={sensor_id} ! "
        f"video/x-raw(memory:NVMM), width={width}, height={height}, "
        f"format=NV12, framerate={framerate}/1 ! "
        f"nvvidconv flip-method={flip_method} ! "
        f"video/x-raw, format=BGRx ! "
        f"videoconvert ! "
        f"video/x-raw, format=BGR ! "
        f"appsink drop=1 max-buffers=1"
    )

def main():
    parser = argparse.ArgumentParser(description="OpenCV CSI Motion Detection (IMX219 on Jetson Orin Nano)")
    parser.add_argument("--sensor-id", type=int, default=0, help="ID del sensor CSI (0 por defecto)")
    parser.add_argument("--width", type=int, default=1280, help="Ancho de captura")
    parser.add_argument("--height", type=int, default=720, help="Alto de captura")
    parser.add_argument("--fps", type=int, default=30, help="FPS de captura")
    parser.add_argument("--flip", type=int, default=0, help="flip-method nvvidconv (0 sin flip)")
    parser.add_argument("--display", type=int, default=1, help="Mostrar ventana (1 si, 0 no)")
    parser.add_argument("--record", type=int, default=0, help="Grabar cuando haya movimiento (1 si, 0 no)")
    parser.add_argument("--min-area", type=int, default=1500, help="Área mínima del contorno para considerarlo movimiento")
    parser.add_argument("--dilate-iter", type=int, default=2, help="Iteraciones de dilatación")
    parser.add_argument("--erode-iter", type=int, default=1, help="Iteraciones de erosión")
    parser.add_argument("--roi", type=str, default="", help="ROI 'x,y,w,h' (opcional)")
    parser.add_argument("--warmup-sec", type=float, default=1.0, help="Tiempo de calentamiento del sustractor de fondo")
    args = parser.parse_args()

    pipeline = gstreamer_pipeline(
        sensor_id=args.sensor_id,
        width=args.width,
        height=args.height,
        framerate=args.fps,
        flip_method=args.flip
    )

    cap = cv2.VideoCapture(pipeline, cv2.CAP_GSTREAMER)
    if not cap.isOpened():
        print("ERROR: No se pudo abrir la cámara CSI con GStreamer.")
        sys.exit(1)

    # Sustractor de fondo (MOG2)
    backsub = cv2.createBackgroundSubtractorMOG2(history=500, varThreshold=25, detectShadows=True)

    # Opcional: parsear ROI
    roi = None
    if args.roi:
        try:
            x, y, w, h = [int(v) for v in args.roi.split(",")]
            roi = (x, y, w, h)
        except Exception as e:
            print(f"Advertencia: ROI inválida ({args.roi}). Se ignora. Error: {e}")

    # Grabador de video cuando hay movimiento
    fourcc = cv2.VideoWriter_fourcc(*"mp4v")
    out = None
    recording = False
    record_queue = deque(maxlen=30)  # buffer para pre-captura (1 segundo aprox a 30 FPS)

    # Métricas
    prev_time = time.perf_counter()
    fps = 0.0
    frame_count = 0
    motion_events = 0
    warmup_end = time.perf_counter() + args.warmup_sec

    print("Inicializando... Presiona 'q' para salir.")
    while True:
        ret, frame = cap.read()
        if not ret:
            print("ERROR: frame no válido. Saliendo.")
            break

        frame_count += 1
        now = time.perf_counter()
        dt = now - prev_time
        if dt > 0.5:
            fps = frame_count / dt
            frame_count = 0
            prev_time = now

        # Aplicar ROI si existe
        proc = frame
        if roi:
            x, y, w, h = roi
            proc = frame[y:y+h, x:x+w]

        # Sustracción de fondo
        fgmask = backsub.apply(proc)

        # Saltar detección durante warmup para estabilizar el modelo
        in_warmup = (now < warmup_end)

        # Morfología
        kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
        fgmask = cv2.morphologyEx(fgmask, cv2.MORPH_OPEN, kernel, iterations=1)
        if args.erode_iter > 0:
            fgmask = cv2.erode(fgmask, None, iterations=args.erode_iter)
        if args.dilate_iter > 0:
            fgmask = cv2.dilate(fgmask, None, iterations=args.dilate_iter)

        # Umbralización para binario puro
        _, th = cv2.threshold(fgmask, 200, 255, cv2.THRESH_BINARY)

        # Contornos
        contours, _ = cv2.findContours(th, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

        # Detección de movimiento (filtrado por área)
        movement_detected = False
        for c in contours:
            area = cv2.contourArea(c)
            if area < args.min_area:
                continue
            movement_detected = True
            x2, y2, w2, h2 = cv2.boundingRect(c)
            # Ajustar coordenadas si hay ROI
            if roi:
                x2 += roi[0]
                y2 += roi[1]
            cv2.rectangle(frame, (x2, y2), (x2 + w2, y2 + h2), (0, 255, 0), 2)
            cv2.putText(frame, f"Area:{int(area)}", (x2, y2 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 200, 0), 1)

        # Estado y métricas en overlay
        status_text = "WARMUP" if in_warmup else ("MOVIMIENTO" if movement_detected else "OK")
        color = (0, 255, 255) if in_warmup else ((0, 0, 255) if movement_detected else (255, 255, 255))
        cv2.putText(frame, f"{status_text} | FPS:{fps:.1f}", (10, 25),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.7, color, 2)

        # Gestión de grabación
        if args.record:
            # Guardar en buffer circular
            record_queue.append(frame.copy())
            if movement_detected and not in_warmup:
                if not recording:
                    ts = datetime.now().strftime("%Y%m%d_%H%M%S")
                    filename = f"motion_{ts}.mp4"
                    out = cv2.VideoWriter(filename, fourcc, args.fps, (frame.shape[1], frame.shape[0]))
                    # Vuelca el buffer para no perder lo previo al evento
                    for f in record_queue:
                        out.write(f)
                    recording = True
                    motion_events += 1
                    print(f"[{ts}] Movimiento detectado -> Grabando en {filename}")
                # Seguir grabando
                out.write(frame)
            else:
                # Si no hay movimiento, cierra si estaba grabando
                if recording:
                    out.release()
                    out = None
                    recording = False

        # Mostrar ventana si procede
        if args.display:
            cv2.imshow("CSI Motion Detection (IMX219)", frame)
            # También podemos ver la máscara con:
            # cv2.imshow("FG Mask", th)
            key = cv2.waitKey(1) & 0xFF
            if key == ord('q'):
                break

    # Limpieza
    if out is not None:
        out.release()
    cap.release()
    cv2.destroyAllWindows()
    print(f"Eventos de movimiento contados: {motion_events}")

if __name__ == "__main__":
    main()

Puntos clave del código:
– gstreamer_pipeline utiliza nvarguscamerasrc para capturar la IMX219 por CSI y entrega BGR a OpenCV mediante appsink.
– El sustractor MOG2 es suficiente para nivel básico; podría trocarse por KNN.
– ROI opcional para concentrar la detección en un área.
– Grabación basada en eventos y buffer de pre-captura para no perder el inicio del movimiento.
– Overlay con estado y FPS.

Compilación/flash/ejecución

No hay compilación; es Python. Reproduce exactamente:

1) Clona o crea el archivo:

nano opencv_csi_motion_detection.py
# (Pega el código anterior, guarda con Ctrl+O y sal con Ctrl+X)
chmod +x opencv_csi_motion_detection.py

2) Ejecuta (720p a 30 FPS, con ventana y sin grabación):

./opencv_csi_motion_detection.py --width 1280 --height 720 --fps 30 --display 1 --record 0

3) Variantes:
– Desactivar ventana (headless), útil para solo medir:

./opencv_csi_motion_detection.py --display 0
  • Activar grabación de clips al detectar movimiento:
./opencv_csi_motion_detection.py --record 1
  • Definir una ROI (por ejemplo, rectángulo desde x=200,y=120, tamaño 500×300):
./opencv_csi_motion_detection.py --roi 200,120,500,300

4) Ajuste de rendimiento y telemetría (opcional):
– Asegura MAXN y clocks:

sudo nvpmodel -m 0
sudo jetson_clocks
  • Corre tu script y, en otra terminal, observa recursos:
tegrastats
  • Para revertir jetson_clocks cuando termines:
sudo systemctl restart nvfancontrol || true
sudo /usr/sbin/jetson_clocks --restore

Validación paso a paso

1) Validar cámara CSI por GStreamer:
– Comando:
bash
gst-launch-1.0 -e nvarguscamerasrc num-buffers=120 ! 'video/x-raw(memory:NVMM), width=1280, height=720, framerate=30/1' ! nvvidconv ! fakesink

– Esperado: termina sin errores tras procesar 120 buffers. Sin mensajes “no sensor found” ni fallos de nvargus-daemon.

2) Validar OpenCV con pipeline CSI:
– Comando:
bash
python3 - << 'PY'
import cv2
pipeline = "nvarguscamerasrc ! video/x-raw(memory:NVMM), width=640, height=480, framerate=30/1 ! nvvidconv ! video/x-raw, format=BGRx ! videoconvert ! video/x-raw, format=BGR ! appsink drop=1"
cap = cv2.VideoCapture(pipeline, cv2.CAP_GSTREAMER)
print("Opened:", cap.isOpened())
ret, frame = cap.read()
print("Frame:", ret, "shape:" if ret else "", frame.shape if ret else "")
cap.release()
PY

– Esperado: Opened: True y al menos un frame con shape razonable (por ejemplo, (480, 640, 3)).

3) Ejecutar el detector de movimiento y observar overlays:
– Comando:
bash
./opencv_csi_motion_detection.py --width 1280 --height 720 --fps 30 --display 1

– Esperado:
– En los primeros ~1 s, estado “WARMUP”.
– Al mover la mano frente a la cámara, aparecen rectángulos verdes y “MOVIMIENTO”.
– FPS entre 25 y 30 en 720p (según condiciones y carga).
– Si activas –record 1, verás en consola líneas del tipo:
“[YYYYMMDD_HHMMSS] Movimiento detectado -> Grabando en motion_YYYYMMDD_HHMMSS.mp4”.

4) Métricas objetivas:
– FPS: mostrado en overlay y estable >25 FPS a 1280×720. Puedes registrar promedio ejecutando en headless 60 s y midiendo FPS medios por logs si agregas prints, o añadiendo contador simple.
– Latencia: si se requiere, añade marcas de tiempo en overlay (time.perf_counter()) y calcula delta con tegrastats para observar coherencia.
– Área: en overlay “Area:xxxx” por cada contorno. Ajusta –min-area hasta limitar falsos positivos (ruido).
– Porcentaje CPU/GPU/memoria: con tegrastats:
bash
tegrastats

Observa campos como GR3D (GPU), EMC y RAM durante la ejecución.

5) Validación de aceleración GPU (ruta A: TensorRT + ONNX, solo como referencia de stack IA):
– Descarga un modelo ONNX ligero (ResNet50-v2):
bash
wget -O resnet50-v2-7.onnx https://github.com/onnx/models/raw/main/vision/classification/resnet/model/resnet50-v2-7.onnx

– Construye motor FP16 con trtexec (TensorRT 8.6.3):
bash
trtexec --onnx=resnet50-v2-7.onnx \
--saveEngine=resnet50_fp16.plan \
--fp16 \
--workspace=2048 \
--shapes=data:1x3x224x224 \
--timingCacheFile=trt_timing.cache \
--separateProfileRun

– Mide rendimiento:
bash
trtexec --loadEngine=resnet50_fp16.plan \
--shapes=data:1x3x224x224 \
--iterations=200 --avgRuns=100 --streams=1

– Esperado: Latencias medias de 2–5 ms por inferencia (200–500 FPS) en Orin Nano con FP16 (los valores exactos varían). Los logs de trtexec informan “Throughput” y “Mean latency”.

Criterios de éxito del caso:
– El script detecta movimiento de manera fiable en un entorno real, con overlay y FPS visibles.
– Las métricas obtenidas (FPS, latencia aproximada, porcentaje de falso positivo) cumplen las metas enunciadas.
– Se confirma que el stack multimedia (nvarguscamerasrc) y el stack de IA (TensorRT) están funcionales y acelerados por GPU.

Troubleshooting (errores típicos y soluciones)

1) No se abre la cámara CSI en OpenCV (cap.isOpened() == False):
– Causa: pipeline GStreamer incorrecto o falta de nvarguscamerasrc.
– Solución: verifica que gstreamer1.0 está instalado; prueba con gst-launch-1.0 nvarguscamerasrc … y ajusta el pipeline exactamente como en el código. Asegúrate de no instalar opencv-python de pip que sobreescriba la versión de apt (sin soporte GStreamer). Si lo hiciste por error, desinstala con pip y reinstala python3-opencv desde apt.

2) Error “no sensor found” o “nvargus-daemon” falla:
– Causa: cámara mal conectada, cable invertido o conector suelto.
– Solución: apaga, reconecta FFC (cara de contactos orientada correctamente), usa el conector CSI correcto (CAM0), arranca de nuevo. Ejecuta “sudo systemctl restart nvargus-daemon” y prueba otra vez.

3) Ventana negra o latencia muy alta:
– Causa: pipeline sin caps adecuados, conversiones innecesarias o hardware acelerado deshabilitado.
– Solución: usa exactamente la cadena propuesta: nvarguscamerasrc → nvvidconv → BGRx → videoconvert → BGR → appsink. Evita resoluciones/frame rates no soportados por el sensor (IMX219 soporta 1280×720 a 60/30, 1920×1080 a 30, etc., según modos).

4) FPS bajo (<15 FPS en 720p):
– Causa: modo de energía bajo, CPU/GPU limitados o tegrastats muestra throttling.
– Solución: “sudo nvpmodel -m 0” y “sudo jetson_clocks” (temporal). Asegura ventilación. Cierra procesos intensivos. Reduce operaciones morfológicas o aumenta min-area para reducir contornos pequeños.

5) Muchos falsos positivos (ruido):
– Causa: iluminación variable, sombras o reflejos.
– Solución: incrementa –min-area, usa detectShadows=True (ya activado) y eleva varThreshold en MOG2 si modificas el código. Añade ROI para limitar zonas problemáticas. Aplica un blur ligero antes del sustractor de fondo.

6) Error al compilar/run trtexec o ONNX:
– Causa: falta de modelo o shapes incorrectos.
– Solución: verifica el nombre de input del modelo (data:1x3x224x224 en resnet50-v2-7.onnx). Asegura TensorRT instalado (JetPack 6.1) y que trtexec está en PATH (normalmente /usr/src/tensorrt/bin o /usr/bin con enlaces). Revisa dpkg -l | grep tensorrt.

7) “Segmentation fault” al cerrar:
– Causa: recursos no liberados o conflicto de ventanas.
– Solución: asegura cap.release() y cv2.destroyAllWindows() se ejecutan; cierra con ‘q’. Evita cerrar la terminal abruptamente. No mezcles threads sin necesidad.

8) Instalé OpenCV por pip y perdí soporte GStreamer:
– Causa: el wheel de pip suele venir sin GStreamer.
– Solución: “pip3 uninstall opencv-python opencv-contrib-python” y “sudo apt install –reinstall python3-opencv”. Verifica con “cv2.getBuildInformation()” que GStreamer esté “YES” si deseas confirmación (opcional).

Mejoras/variantes

  • Refinar el sustractor:
  • Probar KNN (cv2.createBackgroundSubtractorKNN) para entornos con variación lenta de iluminación.
  • Aplicar un pre-blur (cv2.GaussianBlur) sobre proc antes de apply().

  • Post-procesado:

  • Añadir seguimiento simple (centroid tracking) y conteo de “cruces de línea”.
  • Filtrar contornos por relación de aspecto o perímetro para ignorar ruido.

  • Gestión de eventos:

  • Guardar snapshots (JPEG) solo cuando movimiento inicia o termina, además de los clips mp4.
  • Enviar notificaciones vía MQTT o HTTP al detectar movimiento.

  • Rendimiento:

  • Reducir resolución a 960×540 o 640×480 si se requiere latencia aún menor.
  • Ajustar dilate/erode para balancear ruido y coste de CPU.
  • Utilizar colas y timers para calcular FPS con más precisión a intervalos fijos.

  • IA acelerada (futuras extensiones coherentes con Jetson):

  • Integrar un detector de personas ligero (por ejemplo, PeopleNet con TensorRT) para disparar eventos sólo cuando se detecte una persona, reduciendo falsos positivos.
  • Migrar a un pipeline GStreamer con DeepStream si prefieres una arquitectura más modular para producción.

Checklist de verificación

  • [ ] Confirmé versiones: JetPack 6.1 (L4T r36.3), Ubuntu 22.04, CUDA 12.2, TensorRT 8.6.3, OpenCV 4.8.0, Python 3.10.12, GStreamer 1.20.x.
  • [ ] Instalé paquetes vía apt (python3-opencv, gstreamer, v4l-utils, tegrastats) sin errores.
  • [ ] Conecté la Raspberry Pi Camera Module v2 (IMX219) correctamente al puerto CSI (CAM0) con FFC bien orientado.
  • [ ] Verifiqué la cámara con gst-launch-1.0 nvarguscamerasrc … sin errores.
  • [ ] Ejecuté opencv_csi_motion_detection.py y vi overlay con FPS y rectángulos al mover la mano.
  • [ ] Ajusté –min-area, ROI y morfología hasta reducir falsos positivos en mi entorno.
  • [ ] Medí recursos con tegrastats y, si fue necesario, ajusté nvpmodel/jetson_clocks (recordando revertir al finalizar).
  • [ ] Verifiqué el stack de IA con trtexec y un modelo ONNX, observando throughput esperable.
  • [ ] Guardé clips con –record 1 y comprobé que se crean archivos MP4 con marcas de tiempo.

Notas finales sobre coherencia de hardware/software

  • Este caso práctico está diseñado específicamente para NVIDIA Jetson Orin Nano Developer Kit con Raspberry Pi Camera Module v2 (Sony IMX219), usando su puerto CSI y la cadena nvarguscamerasrc de NVIDIA.
  • Toolchain exacta empleada y validada: JetPack 6.1 (L4T r36.3), CUDA 12.2, TensorRT 8.6.3, OpenCV 4.8.0, GStreamer 1.20.x, Python 3.10.12, NumPy 1.26.x.
  • El objetivo “opencv-csi-motion-detection” se cubre con captura acelerada por hardware (NVMM, nvvidconv) y procesamiento en OpenCV, más una referencia clara de aceleración IA con TensorRT para asegurar el entorno GPU.

Encuentra este producto y/o libros sobre este tema en Amazon

Ir a Amazon

Como afiliado de Amazon, gano con las compras que cumplan los requisitos. Si compras a través de este enlace, ayudas a mantener este proyecto.

Quiz rápido

Pregunta 1: ¿Cuál es el objetivo principal del sistema 'opencv-csi-motion-detection'?




Pregunta 2: ¿Qué tipo de cámara se utiliza en este sistema?




Pregunta 3: ¿Cuál es la tasa de fotogramas por segundo (FPS) esperada para el sistema?




Pregunta 4: ¿Qué tipo de eventos se registran cuando se detecta movimiento?




Pregunta 5: ¿Qué se busca limitar al ajustar los umbrales en el sistema?




Pregunta 6: ¿Qué acción se puede activar en una franja horaria específica?




Pregunta 7: ¿Qué se utiliza para el preprocesado de las imágenes?




Pregunta 8: ¿Cuál es el sistema operativo utilizado en el NVIDIA Jetson Orin Nano?




Pregunta 9: ¿Qué público objetivo se menciona para este sistema?




Pregunta 10: ¿Qué se busca lograr con la auto-grabación de clips breves?




Carlos Núñez Zorrilla
Carlos Núñez Zorrilla
Electronics & Computer Engineer

Ingeniero Superior en Electrónica de Telecomunicaciones e Ingeniero en Informática (titulaciones oficiales en España).

Sígueme:
Scroll to Top