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.