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