Objetivo y caso de uso
Qué construirás: un sistema deepstream-wildlife-count en Jetson Orin NX 16GB que procesa en tiempo real dos flujos CSI de una Arducam estéreo (2x IMX219), ejecuta inferencia acelerada por GPU (DeepStream) y superpone telemetría ambiental (BME680) en el vídeo.
Para qué sirve
- Monitorizar fauna en comederos/bebederos con conteos por clase (aves, mamíferos).
- Relacionar temperatura, humedad y VOC con patrones de presencia animal.
- Generar series de “conteos por minuto” para estudios de biodiversidad o impacto ambiental.
- Vigilancia pasiva en reservas, parques o granjas, con alarma si aparece un objetivo (p. ej., vacas fuera de cercado).
- Validar rendimiento de inferencia y carga con doble cámara CSI y overlay de telemetría.
Resultado esperado
- FPS sostenido ≥ 25 por cámara a 1280×720@30 usando SSD-Mobilenet v1 en FP16 (modo MAXN).
- Latencia end-to-end por frame < 50 ms medida con timestamps de DeepStream.
- Conteo por clase COCO (p. ej., bird, cat, dog, cow) y acumulados “por minuto”.
- Superposición en tiempo real de cajas/IDs y telemetría BME680 (T, HR, VOC) en ambos streams.
- Utilización de GPU estable < 80% y sin pérdidas de frames en carga sostenida.
Público objetivo: investigadores de biodiversidad, gestores de parques/granjas y desarrolladores de edge AI; Nivel: intermedio–avanzado
Arquitectura/flujo: 2x CSI (IMX219) → nvarguscamerasrc → nvstreammux (1280×720@30) → nvinfer (SSD-Mobilenet v1 FP16) → nvtracker/tiler → nvdsosd (bboxes + conteos + overlay BME680) → display/record; BME680 por I2C integra T/HR/VOC en el metadata del frame y un agregador emite “conteos/min”.
Prerrequisitos (SO, toolchain y versiones exactas)
- Plataforma:
- NVIDIA Jetson Orin NX 16GB Developer Kit.
- Sistema: Ubuntu 20.04.6 LTS (L4T 36.3.0) vía JetPack 6.0 GA.
- Toolchain exacta (verificada en JetPack 6.0 GA):
- JetPack 6.0 GA (L4T 36.3.0).
- CUDA 12.2.0.
- cuDNN 8.9.x (paquetes l4t-cudnn 8.9 para CUDA 12.2).
- TensorRT 8.6.2.
- DeepStream SDK 6.4.0.
- GStreamer 1.20.x (instalado por L4T/DeepStream).
- Python 3.8.10.
- Comandos de verificación:
- cat /etc/nv_tegra_release
- uname -a
- dpkg -l | grep -E ‘nvidia|tensorrt|deepstream|cudnn|cuda’
- nvcc –version
- deepstream-app –version-all
- Paquetes del sistema (mínimos):
- sudo apt update
- sudo apt install -y deepstream-6.4 python3-gi python3-pip python3-dev libglib2.0-dev i2c-tools python3-smbus v4l-utils
- pip3 install bme680 smbus2
- Notas:
- DeepStream 6.4 se instala en: /opt/nvidia/deepstream/deepstream
- Python bindings (pyds) incluidos con DeepStream 6.4: instalar con pip desde el wheel provisto:
- sudo python3 -m pip install /opt/nvidia/deepstream/deepstream/lib/python/bindings/py3/pyds-*.whl
Comandos para verificar e inventariar versiones (ejecuta y conserva las salidas para tu registro):
cat /etc/nv_tegra_release
uname -a
dpkg -l | grep -E 'nvidia-l4t|tensorrt|deepstream|cuda|cudnn'
nvcc --version
deepstream-app --version-all
Ejemplo de componentes esperados (resumen):
– nvidia-l4t-core 36.3.0
– nvidia-cuda-toolkit 12-2
– libnvinfer8 8.6.2
– deepstream-6.4 6.4.0-1
– Python 3.8.10
Materiales
- Jetson Orin NX 16GB Developer Kit + Arducam Stereo Camera (2x IMX219) + BME680:
- Jetson Orin NX 16GB Developer Kit (módulo Orin NX 16GB + carrier board con 2 puertos CSI).
- Arducam Stereo Camera (2x IMX219), dos cintas FFC 15 pines (conectores CAM0/CAM1).
- Sensor BME680 (placa breakout I2C, 3.3V).
- Cables dupont hembra-hembra para GPIO (al menos 4: 3.3V, GND, SDA, SCL).
- Fuente 65W USB-C PD o suministro oficial NVIDIA.
- Tarjeta microSD (si aplica) o SSD M.2 para datos (opcional).
- Trípode/soporte para cámara (recomendado).
Preparación y conexión
Colocación de las cámaras CSI (Arducam 2x IMX219)
- Desenergiza el kit antes de conectar.
- Identifica los puertos CSI del carrier: CAM0 y CAM1 (conector FFC de 15 pines).
- Inserta cada FFC con el contacto metálico orientado hacia el conector según serigrafía del carrier (asegura el pestillo).
- Asigna:
- IMX219 izquierdo → CAM0 (sensor-id=0).
- IMX219 derecho → CAM1 (sensor-id=1).
Prueba rápida de cada sensor (uno por vez; la segunda línea usa la otra cámara):
gst-launch-1.0 -e nvarguscamerasrc sensor-id=0 ! "video/x-raw(memory:NVMM),width=1280,height=720,framerate=30/1" ! nvoverlaysink
gst-launch-1.0 -e nvarguscamerasrc sensor-id=1 ! "video/x-raw(memory:NVMM),width=1280,height=720,framerate=30/1" ! nvoverlaysink
Si ves imagen en ambos, las conexiones están correctas.
Cableado del BME680 (I2C a 3.3V)
Conecta el BME680 a la cabecera de 40 pines (GPIO) del carrier de Jetson (bus I2C-1). Mantén los cables cortos.
Tabla de pines (Jetson 40p → BME680):
| Señal | Jetson Orin NX 40p | Pin físico | BME680 |
|---|---|---|---|
| 3.3V | 3V3 | 1 | VCC |
| GND | GND | 6 | GND |
| SDA | I2C1_SDA | 3 | SDA |
| SCL | I2C1_SCL | 5 | SCL |
- Habilita I2C y verifica la dirección del BME680 (0x76 o 0x77):
- sudo i2cdetect -y 1
- Debe aparecer “76” o “77”. Recuerda esta dirección para el software (por defecto usaremos 0x76).
Modo de potencia y clocks (rendimiento)
- Establece modo MAXN y clocks al máximo (vigila térmicas):
sudo nvpmodel -q
sudo nvpmodel -m 0
sudo jetson_clocks
- Abre un monitor de recursos al correr el pipeline:
sudo tegrastats
Código completo
Implementaremos toda la tubería en Python usando GStreamer + DeepStream (bindings pyds). El detector será SSD-Mobilenet v1 (ONNX, COCO-80) con parser SSD oficial de DeepStream. Contaremos clases de fauna (subset COCO) y superpondremos la telemetría del BME680.
Estructura de archivos:
– deepstream_wildlife_count.py (pipeline en Python).
– config_infer_primary_ssd.txt (nvinfer: ONNX SSD).
– labels_coco_80.txt (etiquetas COCO).
– Opcional: config_tracker_IOU.yml (usaremos el que viene con DeepStream).
1) Script principal: deepstream_wildlife_count.py
Guarda lo siguiente como deepstream_wildlife_count.py:
#!/usr/bin/env python3
# deepstream_wildlife_count.py
# Pipeline:
# 2x nvarguscamerasrc (sensor-id 0 y 1) -> nvstreammux -> nvinfer(SSD-COCO, FP16)
# -> nvtracker(IOU) -> nvosd (overlay counts + BME680) -> nveglglessink
#
# Toolchain: JetPack 6.0 (L4T 36.3), DeepStream 6.4, TensorRT 8.6.2, CUDA 12.2, Python 3.8.10
# Dependencias: python3-gi, pyds (DeepStream), bme680
import sys
import gi
gi.require_version('Gst', '1.0')
gi.require_version('GObject', '2.0')
from gi.repository import Gst, GObject
import pyds
import threading
import time
# BME680
import bme680
# Clases COCO a considerar como fauna
ANIMAL_CLASSES = {
"bird", "cat", "dog", "horse", "sheep", "cow",
"elephant", "bear", "zebra", "giraffe"
}
# Variables globales para telemetría ambiental
env_data = {
"temp_c": 0.0,
"hum": 0.0,
"press_hpa": 0.0,
"gas_kohm": 0.0,
"ok": False
}
def bme680_thread(stop_event):
# Intenta primario (0x76) y secundario (0x77)
sensor = None
for addr in (bme680.I2C_ADDR_PRIMARY, bme680.I2C_ADDR_SECONDARY):
try:
sensor = bme680.BME680(i2c_addr=addr)
break
except Exception:
sensor = None
if sensor is None:
print("[BME680] No se encontró el sensor en 0x76/0x77")
return
# Configuración típica
sensor.set_humidity_oversample(bme680.OS_2X)
sensor.set_pressure_oversample(bme680.OS_4X)
sensor.set_temperature_oversample(bme680.OS_8X)
sensor.set_filter(bme680.FILTER_SIZE_3)
sensor.set_gas_status(bme680.ENABLE_GAS_MEAS)
# Calentador de gas
sensor.set_gas_heater_temperature(320)
sensor.set_gas_heater_duration(150)
sensor.select_gas_heater_profile(0)
print("[BME680] Sensor iniciado. Leyendo cada 2 s...")
while not stop_event.is_set():
if sensor.get_sensor_data():
env_data["temp_c"] = sensor.data.temperature
env_data["hum"] = sensor.data.humidity
env_data["press_hpa"] = sensor.data.pressure
env_data["gas_kohm"] = (sensor.data.gas_resistance or 0.0) / 1000.0
env_data["ok"] = True
time.sleep(2.0)
def make_nvargus_source(name, sensor_id=0, width=1280, height=720, fps=30):
source = Gst.ElementFactory.make("nvarguscamerasrc", name)
if not source:
raise RuntimeError("No se pudo crear nvarguscamerasrc")
source.set_property("sensor-id", sensor_id)
caps = Gst.ElementFactory.make("capsfilter", f"{name}_caps")
caps.set_property("caps", Gst.Caps.from_string(
f"video/x-raw(memory:NVMM), width={width}, height={height}, framerate={fps}/1, format=NV12"
))
nvvidconv = Gst.ElementFactory.make("nvvideoconvert", f"{name}_conv")
return source, caps, nvvidconv
def osd_add_text_overlay(batch_meta, frame_meta, text, x=10, y=20, font_size=18):
display_meta = pyds.nvds_acquire_display_meta_from_pool(batch_meta)
display_meta.num_labels = 1
txt_params = display_meta.text_params[0]
txt_params.display_text = text
txt_params.x_offset = x
txt_params.y_offset = y
txt_params.font_params.font_name = "Serif"
txt_params.font_params.font_size = font_size
txt_params.font_params.font_color.set(1.0, 1.0, 1.0, 1.0) # blanco
txt_params.set_bg_clr = 1
txt_params.text_bg_clr.set(0.0, 0.0, 0.0, 0.6) # semitransparente
pyds.nvds_add_display_meta_to_frame(frame_meta, display_meta)
def pgie_src_pad_buffer_probe(pad, info, u_data):
# Recorre metadatos del batch y cuenta animales por frame
gst_buffer = info.get_buffer()
if not gst_buffer:
return Gst.PadProbeReturn.OK
batch_meta = pyds.gst_buffer_get_nvds_batch_meta(hash(gst_buffer))
l_frame = batch_meta.frame_meta_list
# Contadores por stream
stream_counts = {}
while l_frame is not None:
try:
frame_meta = pyds.NvDsFrameMeta.cast(l_frame.data)
except StopIteration:
break
stream_id = frame_meta.source_id
if stream_id not in stream_counts:
stream_counts[stream_id] = 0
l_obj = frame_meta.obj_meta_list
while l_obj is not None:
try:
obj_meta = pyds.NvDsObjectMeta.cast(l_obj.data)
except StopIteration:
break
# Mapear class_id -> label_name via labels file (se gestiona internamente en nvinfer)
# Aquí tomamos el nombre desde obj_meta.obj_label (si está configurado por nvinfer)
label = obj_meta.obj_label
if label in ANIMAL_CLASSES:
stream_counts[stream_id] += 1
try:
l_obj = l_obj.next
except StopIteration:
break
# Overlay: texto con conteos y telemetría
totals = sum(stream_counts.values()) if stream_counts else 0
if env_data["ok"]:
env_txt = f"T={env_data['temp_c']:.1f}C RH={env_data['hum']:.1f}% P={env_data['press_hpa']:.1f}hPa VOC={env_data['gas_kohm']:.2f}kΩ"
else:
env_txt = "BME680: N/D"
text_lines = []
for sid in sorted(stream_counts.keys()):
text_lines.append(f"Cam{sid}: {stream_counts[sid]} animales")
text_lines.append(f"Total: {totals}")
text_lines.append(env_txt)
overlay_text = " | ".join(text_lines)
osd_add_text_overlay(batch_meta, frame_meta, overlay_text, x=10, y=30 + 25*stream_id, font_size=18)
try:
l_frame = l_frame.next
except StopIteration:
break
return Gst.PadProbeReturn.OK
def main():
Gst.init(None)
pipeline = Gst.Pipeline.new("ds-wildlife-count")
# Fuentes: dos cámaras CSI
src0, caps0, conv0 = make_nvargus_source("source0", sensor_id=0)
src1, caps1, conv1 = make_nvargus_source("source1", sensor_id=1)
streammux = Gst.ElementFactory.make("nvstreammux", "streammux")
streammux.set_property("batch-size", 2)
streammux.set_property("live-source", 1)
streammux.set_property("width", 1280)
streammux.set_property("height", 720)
streammux.set_property("batched-push-timeout", 40000)
pgie = Gst.ElementFactory.make("nvinfer", "primary-infer")
pgie.set_property("config-file-path", "./config_infer_primary_ssd.txt")
tracker = Gst.ElementFactory.make("nvtracker", "tracker")
# Usa IOU tracker (config incluido en DeepStream 6.4)
tracker.set_property("ll-lib-file", "/opt/nvidia/deepstream/deepstream/lib/libnvds_nvmultiobjecttracker.so")
tracker.set_property("ll-config-file", "/opt/nvidia/deepstream/deepstream/samples/configs/deepstream-app/config_tracker_IOU.yml")
tracker.set_property("enable_batch_process", 1)
nvvidconv_post = Gst.ElementFactory.make("nvvideoconvert", "convertor")
nvosd = Gst.ElementFactory.make("nvdsosd", "onscreendisplay")
sink = Gst.ElementFactory.make("nveglglessink", "sink")
sink.set_property("sync", 0)
for e in [src0, caps0, conv0, src1, caps1, conv1, streammux, pgie, tracker, nvvidconv_post, nvosd, sink]:
if not e:
raise RuntimeError("Error creando un elemento de la tubería")
pipeline.add(e)
# Vincula cada fuente al streammux
src0.link(caps0)
caps0.link(conv0)
src1.link(caps1)
caps1.link(conv1)
sinkpad0 = streammux.get_request_pad("sink_0")
sinkpad1 = streammux.get_request_pad("sink_1")
if not sinkpad0 or not sinkpad1:
raise RuntimeError("No se pudieron obtener sink pads del streammux")
srcpad0 = conv0.get_static_pad("src")
srcpad1 = conv1.get_static_pad("src")
if srcpad0.link(sinkpad0) != Gst.PadLinkReturn.OK:
raise RuntimeError("No se pudo enlazar source0->streammux.sink_0")
if srcpad1.link(sinkpad1) != Gst.PadLinkReturn.OK:
raise RuntimeError("No se pudo enlazar source1->streammux.sink_1")
if not streammux.link(pgie):
raise RuntimeError("No se pudo enlazar streammux->nvinfer")
if not pgie.link(tracker):
raise RuntimeError("No se pudo enlazar nvinfer->nvtracker")
if not tracker.link(nvvidconv_post):
raise RuntimeError("No se pudo enlazar nvtracker->nvvideoconvert")
if not nvvidconv_post.link(nvosd):
raise RuntimeError("No se pudo enlazar nvvideoconvert->nvdsosd")
if not nvosd.link(sink):
raise RuntimeError("No se pudo enlazar nvdsosd->sink")
# Probe para conteos y overlay en la salida de PGIE (o tracker)
pgie_src_pad = pgie.get_static_pad("src")
if not pgie_src_pad:
raise RuntimeError("No se pudo obtener src pad de nvinfer")
pgie_src_pad.add_probe(Gst.PadProbeType.BUFFER, pgie_src_pad_buffer_probe, 0)
# Hilo del BME680
stop_event = threading.Event()
t = threading.Thread(target=bme680_thread, args=(stop_event,), daemon=True)
t.start()
# Ejecuta la tubería
loop = GObject.MainLoop()
bus = pipeline.get_bus()
bus.add_signal_watch()
def on_message(bus, message):
t = message.type
if t == Gst.MessageType.EOS:
print("EOS recibido, saliendo…")
loop.quit()
elif t == Gst.MessageType.ERROR:
err, dbg = message.parse_error()
print("ERROR:", err, dbg)
loop.quit()
return True
bus.connect("message", on_message)
print("Iniciando pipeline DeepStream wildlife-count…")
pipeline.set_state(Gst.State.PLAYING)
try:
loop.run()
except KeyboardInterrupt:
pass
finally:
print("Deteniendo…")
pipeline.set_state(Gst.State.NULL)
stop_event.set()
t.join(timeout=1.0)
if __name__ == "__main__":
sys.exit(main())
2) Configuración nvinfer: config_infer_primary_ssd.txt
Usaremos el parser SSD oficial de DeepStream y el modelo ONNX SSD-Mobilenet v1 (COCO-80). Guarda como config_infer_primary_ssd.txt:
[property]
gpu-id=0
net-scale-factor=0.0078431372
offsets=127.5;127.5;127.5
model-color-format=0
onnx-file=./models/ssd_mobilenetv1-12.onnx
model-engine-file=./models/ssd_mobilenetv1-12_b2_fp16.engine
labelfile-path=./labels_coco_80.txt
network-type=0
num-detected-classes=80
batch-size=2
interval=0
gie-unique-id=1
process-mode=1
network-mode=2 # 0:FP32, 2:FP16
infer-dims=3;300;300
maintain-aspect-ratio=1
parse-bbox-func-name=NvDsInferParseCustomSSD
custom-lib-path=/opt/nvidia/deepstream/deepstream/lib/libnvds_infercustomparser.so
output-blob-names=boxes;scores
[class-attrs-all]
pre-cluster-threshold=0.4
nms-iou-threshold=0.5
topk=300
Notas:
– batch-size=2 para dos cámaras.
– network-mode=2 para FP16.
– El parser SSD de DeepStream (libnvds_infercustomparser.so) expone NvDsInferParseCustomSSD.
– Ajusta thresholds si deseas más/menos detecciones.
3) Etiquetas COCO: labels_coco_80.txt
Guarda un archivo labels_coco_80.txt con 80 líneas (índice 0–79). Ejemplo mínimo (primeras y relevantes para fauna; completa a 80 líneas):
person
bicycle
car
motorcycle
airplane
bus
train
truck
boat
traffic light
fire hydrant
stop sign
parking meter
bench
bird
cat
dog
horse
sheep
cow
elephant
bear
zebra
giraffe
backpack
umbrella
handbag
tie
suitcase
frisbee
skis
snowboard
sports ball
kite
baseball bat
baseball glove
skateboard
surfboard
tennis racket
bottle
wine glass
cup
fork
knife
spoon
bowl
banana
apple
sandwich
orange
broccoli
carrot
hot dog
pizza
donut
cake
chair
couch
potted plant
bed
dining table
toilet
tv
laptop
mouse
remote
keyboard
cell phone
microwave
oven
toaster
sink
refrigerator
book
clock
vase
scissors
teddy bear
hair drier
toothbrush
Asegúrate de tener 80 líneas exactas.
4) Modelo ONNX
Descarga el modelo SSD-Mobilenet v1 (ONNX Model Zoo) y crea carpeta models:
mkdir -p ./models
wget -O ./models/ssd_mobilenetv1-12.onnx \
https://github.com/onnx/models/raw/main/vision/object_detection_segmentation/ssd-mobilenetv1/model/ssd-mobilenetv1-12.onnx
nvinfer generará el engine FP16 la primera vez (model-engine-file).
Compilación/flash/ejecución
Sigue el orden:
1) Verifica el sistema y toolchain:
– cat /etc/nv_tegra_release
– deepstream-app –version-all
2) Instala DeepStream 6.4 y dependencias (si no lo hiciste con SDK Manager):
sudo apt update
sudo apt install -y deepstream-6.4 python3-gi python3-pip python3-dev libglib2.0-dev i2c-tools python3-smbus v4l-utils
sudo python3 -m pip install bme680 smbus2
sudo python3 -m pip install /opt/nvidia/deepstream/deepstream/lib/python/bindings/py3/pyds-*.whl
3) Prepara el proyecto (en tu carpeta de trabajo):
mkdir -p ~/deepstream-wildlife-count && cd ~/deepstream-wildlife-count
# Copia o crea:
# - deepstream_wildlife_count.py
# - config_infer_primary_ssd.txt
# - labels_coco_80.txt
# Descarga el modelo:
mkdir -p models
wget -O ./models/ssd_mobilenetv1-12.onnx \
https://github.com/onnx/models/raw/main/vision/object_detection_segmentation/ssd-mobilenetv1/model/ssd-mobilenetv1-12.onnx
4) Test rápido de cámaras (opcional, una por vez):
gst-launch-1.0 -e nvarguscamerasrc sensor-id=0 ! "video/x-raw(memory:NVMM),width=1280,height=720,framerate=30/1" ! nvoverlaysink
gst-launch-1.0 -e nvarguscamerasrc sensor-id=1 ! "video/x-raw(memory:NVMM),width=1280,height=720,framerate=30/1" ! nvoverlaysink
5) Configura modo de potencia y clocks:
sudo nvpmodel -m 0
sudo jetson_clocks
6) Ejecuta el sistema de conteo (DeepStream generará el engine FP16 la primera vez):
python3 deepstream_wildlife_count.py
7) Monitorea rendimiento con tegrastats en otra terminal:
sudo tegrastats
Salida esperada:
– Ventana con vídeo de ambas cámaras, bounding boxes y etiquetas por objeto, overlay superior con:
– Cam0: N animales | Cam1: M animales | Total: T | T=xx.xC RH=yy.y% P=zzz.zhPa VOC=kk.kkkΩ
Notas:
– El engine FP16 (.engine) se crea en ./models a partir del ONNX; en la primera ejecución es normal ver una demora adicional (build de TensorRT).
– Si quieres precompilar el engine para asegurar FP16 con batch=2, deja a nvinfer hacerlo; es lo más simple y robusto aquí.
Validación paso a paso
1) CSI OK:
– Ejecuta los gst-launch de prueba para sensor-id=0 y 1; cada uno debe mostrar vídeo estable a 1280×720.
– Si falla: revisa cables FFC, reinicia nvargus-daemon: sudo systemctl restart nvargus-daemon
2) I2C/BME680 OK:
– sudo i2cdetect -y 1 → ver 0x76 (o 0x77).
– Ejecuta un script corto (opcional) para leer el BME680:
– python3 -c «import bme680; s=bme680.BME680(); import time; s.get_sensor_data(); print(s.data.temperature)»
– Debes ver una temperatura razonable (20–40°C).
3) DeepStream 6.4 OK:
– deepstream-app –version-all → muestra versiones de GStreamer, CUDA, TensorRT, etc.
– Comprueba existencia del parser: ls /opt/nvidia/deepstream/deepstream/lib/libnvds_infercustomparser.so
4) Pipeline corre:
– python3 deepstream_wildlife_count.py
– En la primera ejecución se crea el engine: verás logs de TensorRT (build) y luego FPS estables.
– Observa el overlay: “Cam0: X animales | Cam1: Y animales | Total: Z | T=..C RH=..% P=..hPa VOC=..kΩ”
– Mueve una figura de animal o muestras impresas en papel (p. ej., imágenes de COCO con animales) delante de las cámaras; el contador debe aumentar.
5) Métricas:
– tegrastats típico (orientativo, con 2x 720p):
– GR3D_FREQ 60–90%, EMC 20–40%, RAM ~3–5 GB, FPS ~27–30 por stream.
– Exige: FPS ≥ 25 y CPU < 35% (observa en htop).
6) Cierre y limpieza:
– Ctrl+C para salir del script.
– Restablecer clocks si deseas:
– sudo systemctl restart nvargus-daemon
– (Opcional) volver a modo ahorro: sudo nvpmodel -m 2
Criterios de éxito:
– Detección visible de animales (perros/gatos/aves/ganado) con cajas correctas.
– Overlay con telemetría BME680 variando al soplar sobre el sensor (humedad sube) o tocándolo (temperatura sube).
– Rendimiento dentro de las métricas esperadas (FPS, utilización GPU/CPU).
Troubleshooting (errores típicos y soluciones)
1) nvarguscamerasrc: “No cameras available” o timeout
– Causa: FFC invertido, sensor no detectado, nvargus-daemon colgado.
– Solución: Revisa orientación del flex (contactos), aprieta pestillos, reinicia daemon:
– sudo systemctl restart nvargus-daemon
– Reboot si persiste: sudo reboot
2) Pantalla negra en una cámara, la otra OK
– Causa: sensor-id mal asignado o fallo en un IMX219.
– Solución: Intercambia cables entre CAM0/CAM1, prueba cada sensor con gst-launch por separado.
3) i2cdetect no muestra 0x76/0x77
– Causa: cableado erróneo (SDA/SCL), BME680 a 5V (incorrecto), bus I2C equivocado.
– Solución: Verifica tabla de pines; usa 3.3V; revisa soldaduras; prueba en bus 1 (i2cdetect -y 1).
4) Error nvinfer: “Failed to open custom lib” o parser no encontrado
– Causa: ruta incorrecta al parser.
– Solución: Asegura custom-lib-path=/opt/nvidia/deepstream/deepstream/lib/libnvds_infercustomparser.so y que el archivo exista.
5) Error ONNX o mismatch de blobs (output-blob-names)
– Causa: nombres de capas diferentes.
– Solución: Mantén output-blob-names=boxes;scores según ssd-mobilenetv1-12.onnx del ONNX Model Zoo. Si cambia el modelo, ajusta.
6) Rendimiento bajo (FPS < 20)
– Causa: no estás en FP16, clocks bajos.
– Solución: network-mode=2 (FP16), sudo nvpmodel -m 0; sudo jetson_clocks; reduce resolución a 960×540 en nvarguscamerasrc caps.
7) Overlay sin telemetría (BME680 N/D)
– Causa: librería bme680 no instalada o I2C inaccesible sin sudo.
– Solución: pip3 install bme680; añade usuario a i2c: sudo usermod -aG i2c $USER (relogin) o ejecuta con sudo.
8) Caída por GStreamer caps negotiation
– Causa: memoria no NVMM o caps inconsistentes.
– Solución: Usa “video/x-raw(memory:NVMM),format=NV12” en las caps; nvvideoconvert antes de nvstreammux.
Mejoras/variantes
- INT8 para mayor FPS: calibra SSD con dataset representativo y usa network-mode=1 y calibración en nvinfer (requiere tabla de calibración).
- Offload parcial a DLA (Orin): en nvinfer usa use-dla-core=0/1 si procede y la red es compatible con DLA.
- Sustituye el detector por un YOLOv8/YOLOv5 entrenado en fauna (vía ONNX + parser YOLO para DeepStream) para mejor precisión en animales.
- Añade nvdsanalytics para conteo por zonas (line crossing, ROI) y métricas de ocupación por especie.
- Fusión estéreo: emplea ambas cámaras para estimar disparidad (pipeline paralelo) y filtrar falsos positivos por profundidad.
- Streaming remoto: publica el resultado por RTSP (nvv4l2h264enc + rtph264pay + udpsink) y registra conteos a InfluxDB/Prometheus.
- Gestión energética: automatiza reducción de framerate nocturno y re-escala dinámico según carga (Gst caps renegotiation).
Checklist de verificación
- [ ] JetPack 6.0 (L4T 36.3.0), DeepStream 6.4, TensorRT 8.6.2 y CUDA 12.2 verificados (comandos de versión).
- [ ] Cámaras IMX219 conectadas: nvarguscamerasrc sensor-id=0 y 1 muestran vídeo.
- [ ] BME680 visible en I2C (0x76/0x77) y librería bme680 instalada.
- [ ] Proyecto con archivos: deepstream_wildlife_count.py, config_infer_primary_ssd.txt, labels_coco_80.txt, models/ssd_mobilenetv1-12.onnx.
- [ ] Parser SSD de DeepStream presente: /opt/nvidia/deepstream/deepstream/lib/libnvds_infercustomparser.so.
- [ ] Modo MAXN y clocks activos: sudo nvpmodel -m 0; sudo jetson_clocks.
- [ ] Pipeline ejecuta con 2 flujos, overlay con conteos y telemetría visible.
- [ ] FPS ≥ 25/stream, GPU < 60% y CPU < 35% (observado en tegrastats/htop).
- [ ] Registro de prueba con presencia de animales (reales o impresos), conteos razonables.
Con este caso práctico, has desplegado un sistema avanzado de conteo de fauna en el borde, acelerado por GPU con DeepStream, aprovechando la doble cámara CSI del “Jetson Orin NX 16GB Developer Kit + Arducam Stereo Camera (2x IMX219) + BME680”, y con telemetría ambiental integrada para análisis contextual de la actividad detectada.
Encuentra este producto y/o libros sobre este tema en 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.




