4.18. Múltiples sensores

Unas pocas OpenMV Cam combinan dos sensores de imagen en la misma placa – lo más habitual es una cámara de color junto a un sensor térmico FLIR® Lepton®, pero el mismo esquema se aplica a las placas de color más eventos y a cualquier futuro hardware de doble sensor. Cada sensor tiene su propia matriz de píxeles, su propio bus de control y ejecuta su propia canalización a su propia frecuencia de fotogramas. La API CSI se amplía para cubrirlos permitiendo que la aplicación instancie un objeto CSI por cada sensor físico.

4.18.1. Seleccionar qué sensor

El constructor CSI recibe un argumento cid que nombra un sensor específico de la placa. cid=-1 (el valor predeterminado) selecciona el sensor principal; las constantes cid con nombre seleccionan un secundario por ID de chip:

import csi

csi_rgb     = csi.CSI()                    # primary colour sensor
csi_thermal = csi.CSI(cid=csi.LEPTON)      # FLIR® Lepton®

Cada instancia es propietaria de su propia configuración – formato de píxel, tamaño de fotograma, controles de exposición / ganancia, grupo de búferes de fotogramas – y se reinicia, configura y lee independientemente de la otra. Las constantes para los sensores secundarios admitidos (LEPTON, GENX320 y las demás que figuran en la referencia de CSI) nombran el chip que la aplicación espera en el puerto secundario; el controlador falla la construcción si el chip real no coincide.

4.18.2. Capturar desde ambos sensores

Cada sensor ejecuta su canalización de captura independientemente del otro – el sensor de color podría entregar treinta fotogramas por segundo mientras que el Lepton® entrega nueve. La forma directa de gestionar ese desajuste es dejar que el sensor más rápido gobierne el bucle y leer el sensor más lento de manera no bloqueante, tomando lo que esté listo y omitiendo la iteración cuando no haya nada:

import csi

csi_rgb     = csi.CSI()
csi_thermal = csi.CSI(cid=csi.LEPTON)

csi_rgb.reset()                        # powers the rail, pulses RESET
csi_rgb.pixformat(csi.RGB565)
csi_rgb.framesize(csi.QVGA)

csi_thermal.reset(hard=False)          # I2C reconfigure only
csi_thermal.pixformat(csi.GRAYSCALE)
csi_thermal.framesize(csi.QQVGA)

while True:
    rgb_img     = csi_rgb.snapshot()                  # blocks for next colour frame
    thermal_img = csi_thermal.snapshot(blocking=False)  # returns None if not ready
    if thermal_img is not None:
        # process aligned colour + thermal pair
        pass
    else:
        # process colour only on this iteration
        pass

La snapshot() bloqueante marca el ritmo del bucle; la no bloqueante devuelve el fotograma térmico más reciente cuando ha llegado uno nuevo desde la llamada anterior, y None en caso contrario. La aplicación sigue ejecutándose a la frecuencia de fotogramas del sensor de color y obtiene un fotograma térmico cada vez que el Lepton® produce uno.

El patrón opuesto – dos capturas bloqueantes una tras otra – también funciona, pero entonces el bucle se ejecuta a la frecuencia del más lento de los dos sensores, y la canalización del sensor más rápido se detiene entre iteraciones. Elija la frecuencia que el procesamiento posterior de la aplicación realmente quiera gobernar.

4.18.3. Reinicio en rieles de alimentación compartidos

Algunas placas de doble sensor alimentan ambos chips desde un único riel de alimentación o comparten una línea de reinicio. En esas, el primer reset() activa el riel y pulsa la señal compartida; los reinicios posteriores en las otras instancias de CSI deben pasar hard=False para que reprogramen solo su propio chip sin arrastrar al vecino a un reinicio:

csi_rgb.reset()                        # primary -- powers the rail, pulses RESET
csi_thermal.reset(hard=False)          # secondary -- I2C reconfigure only

Un hard=True en un secundario con este esquema volvería a reiniciar el principal como efecto secundario, deshaciendo cualquier configuración que la aplicación ya hubiera aplicado. La página de referencia de cada placa de doble sensor indica si los rieles son compartidos.

4.18.4. Seleccionar la fuente del flujo

Las cámaras con dos sensores tienen dos instancias de CSI pero aun así un solo búfer de fotogramas de flujo entre ambas. Un argumento del constructor elige qué fotogramas de sensor alimentan la vista previa:

csi_rgb     = csi.CSI()                    # primary
csi_thermal = csi.CSI(cid=csi.LEPTON,
                      stream=True)         # preview source

stream=True convierte a la instancia nombrada en la fuente. Sin el argumento stream=, el sensor principal (cid=-1, el valor predeterminado) es la fuente; las instancias construidas con cid= de un sensor secundario permanecen silenciosas en la vista previa a menos que se pase explícitamente stream=True. Las llamadas a snapshot() en el sensor no seleccionado siguen capturando fotogramas en los búferes de fotogramas de ese sensor con normalidad – simplemente no actualizan la vista previa.