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() на невыбранном датчике по-прежнему захватывают кадры в буферы кадра этого датчика как обычно – они просто не обновляют предпросмотр.