4.18. Несколько датчиков

Небольшое число камер OpenMV Cam объединяют два датчика изображения на одной плате – чаще всего цветную камеру вместе с тепловым датчиком FLIR® Lepton®, но та же схема применима к платам «цвет плюс события» и любому будущему оборудованию с двумя датчиками. Каждый датчик имеет собственную пиксельную матрицу, собственную шину управления и работает в собственном конвейере с собственной частотой кадров. API CSI расширяется для их поддержки, позволяя приложению создавать по одному объекту CSI на каждый физический датчик.

4.18.1. Выбор датчика

Конструктор CSI принимает аргумент cid, который называет конкретный датчик на плате. cid=-1 (значение по умолчанию) выбирает основной датчик; именованные константы cid выбирают вторичный по идентификатору микросхемы:

import csi

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

Каждый экземпляр владеет собственной конфигурацией – пиксельный формат, размер кадра, регуляторы экспозиции / усиления, пул буфера кадра – и сбрасывается, настраивается и читается независимо от других. Константы для поддерживаемых вторичных датчиков (LEPTON, GENX320 и другие, перечисленные в справочнике CSI) называют микросхему, которую приложение ожидает на вторичном порту; драйвер прерывает создание объекта, если фактическая микросхема не совпадает.

4.18.2. Захват с обоих датчиков

Каждый датчик работает в своём конвейере захвата независимо от другого – цветной датчик может выдавать тридцать кадров в секунду, в то время как Lepton® выдаёт девять. Самый прямой способ справиться с этим несоответствием – позволить более быстрому датчику управлять циклом и читать более медленный датчик неблокирующе, забирая то, что готово, и пропуская итерацию, когда ничего нет:

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

Блокирующий snapshot() задаёт темп цикла; неблокирующий возвращает самый свежий тепловой кадр, если новый поступил с момента предыдущего вызова, и None в противном случае. Приложение продолжает работать с частотой кадров цветного датчика и получает тепловой кадр всякий раз, когда Lepton® его выдаёт.

Противоположная схема – два блокирующих снимка подряд – тоже работает, но тогда цикл выполняется с частотой более медленного из двух датчиков, при этом конвейер более быстрого датчика простаивает между итерациями. Выбирайте ту частоту, которую действительно должна задавать последующая обработка в приложении.

4.18.3. Сброс при общих линиях питания

Некоторые платы с двумя датчиками питают обе микросхемы от одной линии питания или используют общую линию сброса. На таких первый reset() поднимает линию питания и подаёт импульс на общий сигнал; последующие сбросы на других экземплярах CSI должны передавать hard=False, чтобы они перепрограммировали только свою собственную микросхему, не протаскивая соседа через сброс:

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

hard=True на вторичном датчике в такой схеме как побочный эффект заново сбросил бы основной, отменив любую настройку, которую приложение уже отправило. На справочной странице каждой платы с двумя датчиками указано, являются ли линии питания общими.

4.18.4. Выбор источника потока

Камеры с двумя датчиками имеют два экземпляра CSI, но при этом всего один буфер кадра потока на двоих. Аргумент конструктора выбирает, кадры какого датчика подаются в предпросмотр:

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

stream=True делает именованный экземпляр источником. Без аргумента stream= источником является основной датчик (cid=-1, значение по умолчанию); экземпляры, построенные с cid= вторичного датчика, остаются «молчащими» в предпросмотре, если только явно не передано stream=True. Вызовы snapshot() на невыбранном датчике по-прежнему захватывают кадры в буферы кадра этого датчика как обычно – они просто не обновляют предпросмотр.