4.18. Múltiplos sensores#

Algumas OpenMV Cams combinam dois sensores de imagem na mesma placa – mais comumente uma câmera colorida ao lado de um sensor térmico FLIR® Lepton®, mas o mesmo formato se aplica às placas de cor mais eventos e a qualquer hardware futuro de sensor duplo. Cada sensor tem sua própria matriz de pixels, seu próprio barramento de controle e executa seu próprio pipeline em sua própria taxa de quadros. A API CSI se estende para abrangê-los permitindo que a aplicação instancie um objeto CSI por sensor físico.

4.18.1. Selecionando qual sensor#

O construtor de CSI recebe um argumento cid que nomeia um sensor específico na placa. cid=-1 (o padrão) seleciona o sensor primário; as constantes cid nomeadas selecionam um secundário pelo ID do chip:

import csi

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

Cada instância possui sua própria configuração – formato de pixel, framesize, ajustes de exposição / ganho, pool de framebuffer – e é resetada, configurada e lida de forma independente da outra. As constantes para os sensores secundários suportados (LEPTON, GENX320 e as outras listadas na referência de CSI) nomeiam o chip que a aplicação espera na porta secundária; o driver faz a construção falhar se o chip real não corresponder.

4.18.2. Capturando de ambos os sensores#

Cada sensor executa seu pipeline de captura de forma independente do outro – o sensor colorido pode entregar trinta quadros por segundo enquanto o Lepton® entrega nove. A maneira direta de lidar com essa diferença é deixar o sensor mais rápido conduzir o laço e ler o sensor mais lento de forma não bloqueante, pegando o que estiver pronto e pulando a iteração quando não houver 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

O snapshot() bloqueante dita o ritmo do laço; o não bloqueante retorna o quadro térmico mais recente quando um novo chegou desde a chamada anterior, e None caso contrário. A aplicação continua executando na taxa de quadros do sensor colorido e obtém um quadro térmico sempre que o Lepton® produz um.

O padrão oposto – dois snapshots bloqueantes em sequência – também funciona, mas o laço passa a rodar na taxa do mais lento dos dois sensores, com o pipeline do sensor mais rápido travando entre as iterações. Escolha a taxa que o processamento subsequente da aplicação realmente quiser conduzir.

4.18.3. Reset em barramentos de alimentação compartilhados#

Algumas placas de sensor duplo alimentam ambos os chips a partir de um único barramento de alimentação ou compartilham uma linha de reset. Nessas, o primeiro reset() liga o barramento e pulsa o sinal compartilhado; resets subsequentes nas outras instâncias de CSI devem passar hard=False para que reprogramem apenas o próprio chip sem arrastar o vizinho por um reset:

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

Um hard=True em um secundário neste formato resetaria novamente o primário como efeito colateral, desfazendo qualquer configuração que a aplicação já tivesse aplicado. A página de referência de cada placa de sensor duplo indica se os barramentos são compartilhados.

4.18.4. Selecionando a fonte do stream#

Câmeras com dois sensores têm duas instâncias de CSI, mas ainda assim apenas um framebuffer de stream entre elas. Um argumento do construtor escolhe os quadros de qual sensor alimentam a pré-visualização:

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

stream=True torna a instância nomeada a fonte. Sem um argumento stream=, o sensor primário (cid=-1, o padrão) é a fonte; instâncias construídas com cid= de um sensor secundário permanecem silenciosas na pré-visualização a menos que stream=True seja passado explicitamente. Chamadas a snapshot() no sensor não selecionado ainda capturam quadros normalmente para os framebuffers daquele sensor – elas apenas não atualizam a pré-visualização.