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®.

Зворотній шаблон — два блокуючих snapshot поспіль — теж працює, але тоді цикл виконується зі швидкістю повільнішого з двох датчиків, а конвеєр швидшого зупиняється між ітераціями. Виберіть ту швидкість, з якою застосунок справді хоче керувати наступною обробкою.

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