4.18. Senzori multipli

Câteva camere OpenMV Cam asociază doi senzori de imagine pe aceeași placă – cel mai frecvent o cameră color alături de un senzor termic FLIR® Lepton®, dar aceeași structură se aplică și plăcilor color-plus-event și oricărui viitor hardware cu doi senzori. Fiecare senzor are propriul său grilaj de pixeli, propria sa magistrală de control și rulează propriul flux la propria rată de cadre. API-ul CSI se extinde pentru a le acoperi, permițând aplicației să instanțieze câte un obiect CSI pentru fiecare senzor fizic.

4.18.1. Selectarea senzorului

Constructorul CSI acceptă un argument cid care denumește un senzor specific de pe placă. cid=-1 (valoarea implicită) selectează senzorul principal; constantele cid denumite selectează un senzor secundar după ID-ul cipului:

import csi

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

Fiecare instanță deține propria configurație – format de pixel, framesize, comenzi de expunere / amplificare (gain), rezervă de tampoane de cadre – și este resetată, configurată și citită independent de cealaltă. Constantele pentru senzorii secundari acceptați (LEPTON, GENX320 și celelalte enumerate în referința CSI) denumesc cipul pe care aplicația îl așteaptă pe portul secundar; driverul eșuează construcția dacă cipul efectiv nu corespunde.

4.18.2. Captura de la ambii senzori

Fiecare senzor își rulează fluxul de captură independent de celălalt – senzorul color ar putea livra treizeci de cadre pe secundă, în timp ce Lepton® livrează nouă. Modalitatea simplă de a gestiona această nepotrivire este de a lăsa senzorul mai rapid să conducă bucla și de a citi senzorul mai lent în mod neblocant, preluând orice este disponibil și sărind peste iterație atunci când nu este nimic:

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

Metoda blocantă snapshot() ritmizează bucla; cea neblocantă returnează cel mai recent cadru termic atunci când a sosit unul nou de la apelul anterior, și None în caz contrar. Aplicația continuă să ruleze la rata de cadre a senzorului color și primește un cadru termic ori de câte ori Lepton® produce unul.

Tiparul opus – două instantanee blocante consecutive – funcționează și el, însă bucla rulează atunci la cea mai lentă dintre ratele celor doi senzori, fluxul senzorului mai rapid stagnând între iterații. Alegeți rata pe care procesarea ulterioară a aplicației dorește cu adevărat să o impună.

4.18.3. Resetarea pe linii de alimentare partajate

Unele plăci cu doi senzori alimentează ambele cipuri de pe o singură linie de alimentare sau partajează o linie de resetare. Pe acestea, prima reset() ridică linia și emite un impuls pe semnalul partajat; resetările ulterioare pe celelalte instanțe CSI ar trebui să transmită hard=False astfel încât să își reprogrameze doar propriul cip fără a antrena vecinul printr-o resetare:

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

Un hard=True pe un senzor secundar în această structură ar reseta din nou senzorul principal ca efect secundar, anulând orice configurație pe care aplicația o aplicase deja. Pagina de referință a fiecărei plăci cu doi senzori indică dacă liniile sunt partajate.

4.18.4. Selectarea sursei fluxului

Camerele cu doi senzori au două instanțe CSI, dar tot un singur tampon de cadre pentru flux între ele. Un argument al constructorului alege cadrele cărui senzor alimentează previzualizarea:

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

stream=True face din instanța denumită sursa. Fără argumentul stream=, senzorul principal (cid=-1, valoarea implicită) este sursa; instanțele construite cu cid= al unui senzor secundar rămân tăcute în previzualizare, cu excepția cazului în care stream=True este transmis explicit. Apelurile snapshot() pe senzorul neselectat capturează în continuare cadre în tampoanele de cadre ale acelui senzor în mod normal – doar că nu actualizează previzualizarea.