4.18. Múltiplos sensores

Algumas OpenMV Cams combinam dois sensores de imagem na mesma placa – mais frequentemente uma câmara a cores juntamente com um sensor térmico FLIR® Lepton®, mas o mesmo padrão aplica-se às placas de cor mais eventos e a qualquer hardware futuro com dois sensores. Cada sensor tem o seu próprio array de pixels, o seu próprio barramento de controlo, e executa o seu próprio pipeline à sua própria cadência de fotogramas. A API CSI estende-se para os cobrir, permitindo que a aplicação instancie um objeto CSI por sensor físico.

4.18.1. Selecionar o sensor

O construtor de CSI aceita um argumento cid que identifica um sensor específico na placa. cid=-1 (o padrão) seleciona o sensor primário; as constantes cid com nome identificam um sensor 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 a sua própria configuração – formato de pixel, resolução, controlos de exposição/ganho, pool de framebuffer – e é reiniciada, configurada e lida de forma independente das restantes. As constantes para os sensores secundários suportados (LEPTON, GENX320, e os demais listados na referência de CSI) identificam o chip que a aplicação espera na porta secundária; o driver falha na construção se o chip real não corresponder.

4.18.2. Capturar a partir de ambos os sensores

Cada sensor executa o seu pipeline de captura de forma independente do outro – o sensor de cores pode entregar trinta fotogramas por segundo enquanto o Lepton® entrega nove. A forma mais direta de lidar com essa diferença é deixar o sensor mais rápido conduzir o ciclo e ler o sensor mais lento de forma não bloqueante, tomando o que estiver disponível e saltando a iteração quando não há 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 define o ritmo do ciclo; o não bloqueante devolve o fotograma térmico mais recente quando um novo chegou desde a chamada anterior, ou None caso contrário. A aplicação continua a executar à cadência de fotogramas do sensor de cores e obtém um fotograma térmico sempre que o Lepton® produz um.

O padrão inverso – dois snapshots bloqueantes seguidos – também funciona, mas o ciclo passa então a executar à velocidade do mais lento dos dois sensores, com o pipeline do sensor mais rápido a parar entre iterações. Escolha a cadência que o processamento subsequente da aplicação realmente precisa de conduzir.

4.18.3. Reset em rails de alimentação partilhados

Algumas placas com dois sensores alimentam ambos os chips a partir de um único rail de alimentação ou partilham uma linha de reset. Nesse caso, o primeiro reset() ativa o rail e pulsa o sinal partilhado; os resets subsequentes nas outras instâncias de CSI devem passar hard=False para que apenas reprogramem o seu 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 num sensor secundário nesta configuração re-resetaria o primário como efeito secundário, desfazendo qualquer configuração que a aplicação já tivesse enviado. A página de referência de cada placa com dois sensores indica se os rails são partilhados.

4.18.4. Selecionar a fonte de streaming

As câmaras com dois sensores têm duas instâncias de CSI, mas ainda partilham um único framebuffer de streaming entre elas. Um argumento do construtor escolhe os fotogramas 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 identificada a fonte. Sem 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. As chamadas a snapshot() no sensor não selecionado continuam a capturar fotogramas para os framebuffers desse sensor normalmente – apenas não atualizam a pré-visualização.