Camera de evenimente GENX320

Modulul Camerei de evenimente GENX320 este un senzor de viziune bazat pe evenimente Prophesee, cu rezoluție de 320x320 și precizie temporală la nivel de microsecundă.

Camera de evenimente GENX320

Pentru fișa tehnică completă, fotografii și comenzi, consultați pagina de produs a Camerei de evenimente GENX320.

Notă

Compatibil cu OpenMV H7 Plus, RT1062 și N6.

Caracteristici principale

  • senzor de viziune bazat pe evenimente, 320x320

  • interval dinamic de 140 dB, fără neclaritate de mișcare

  • rată de ieșire a histogramelor de evenimente de peste 375 Hz

  • Consumul de energie se scalează cu activitatea scenei — pornește de la ~3 mW

  • Funcționează de la <5 lux până la lumina solară puternică, fără expunere automată

  • Generează cadre în tonuri de gri sau fluxuri brute de evenimente

Utilizare

GENX320 este un senzor de viziune bazat pe evenimente — în loc să citească întreaga matrice de 320x320 la un tact de cadru fix, fiecare pixel raportează „evenimente” asincrone în momentul în care detectează o schimbare de luminozitate. Fiecare eveniment poartă o coordonată X/Y, o polaritate ON/OFF (luminos→întunecat sau întunecat→luminos) și o marcă temporală în microsecunde. De aici provin precizia temporală la nivel de microsecundă a senzorului, absența neclarității de mișcare, intervalul dinamic foarte mare și consumul de energie scalat cu activitatea. Scenele statice nu generează date.

Firmware-ul OpenMV expune GENX320 prin csi.CSI cu cid= csi.GENX320. Sunt disponibile două moduri de funcționare:

  • Modul histogramă (implicit) — evenimentele sunt acumulate pe cip în compartimente per pixel și raportate ca un cadru în tonuri de gri de 320x320 la o rată configurabilă (~20-350 FPS). Senzorul se comportă ca o cameră obișnuită, astfel încât toate rutinele standard de procesare a imaginii (Image.find_blobs, palete etc.) funcționează direct.

  • Modul eveniment — evenimentele brute sunt transmise într-un ndarray numpy cu mărci temporale complete în microsecunde, pentru aplicații care au nevoie de detaliul temporal mai degrabă decât de un cadru pre-compartimentat.

Modul histogramă

În modul histogramă, GENX320 generează cadre în tonuri de gri în care fiecare pixel codifică activitatea recentă a evenimentelor din acea locație. Pixelii peste linia de bază a luminozității sunt evenimente ON (luminozitate în creștere), cei sub aceasta sunt evenimente OFF (luminozitate în scădere). Luminozitatea implicită a liniei de bază este 128, iar pasul de contrast per eveniment este 16 — măriți contrastul pentru a face evenimentele să iasă în evidență:

import csi
import time

csi0 = csi.CSI(cid=csi.GENX320)
csi0.reset()
csi0.pixformat(csi.GRAYSCALE)
csi0.framesize((320, 320))
csi0.brightness(128)  # baseline (default 128)
csi0.contrast(16)     # per-event step
csi0.framerate(50)    # 20-350 FPS

clock = time.clock()
while True:
    clock.tick()
    img = csi0.snapshot()
    print(clock.fps())

csi.CSI.brightness, csi.CSI.contrast și csi.CSI.framerate sunt cele trei butoane care modelează ieșirea histogramei.

Ieșire colorizată

Setați csi.CSI.color_palette la image.PALETTE_EVT_LIGHT pentru un fundal deschis sau la image.PALETTE_EVT_DARK pentru unul închis — driverul emite cadre RGB565 folosind paleta direct:

csi0.color_palette(image.PALETTE_EVT_LIGHT)

Calibrarea pixelilor fierbinți

Senzorii de evenimente acumulează „pixeli fierbinți” care se declanșează în mod fals. Rulați csi.IOCTL_GENX320_CALIBRATE pe o scenă statică pentru a-i dezactiva. Driverul construiește un contor de declanșări per pixel de 320x320, calculează media și abaterea standard și dezactivează orice pixel al cărui contor este peste mean + sigma * stddev — apoi pixelii dezactivați încetează să mai emită evenimente la nivelul senzorului.

Doi parametri controlează calibrarea:

  • event_count — câte evenimente să se numere înainte de a calcula statisticile. Bucla captează cadre până când totalul curent de evenimente depășește acest buget. Valori mai mari oferă o estimare mai fiabilă cu prețul unui timp de calibrare mai îndelungat. 10000 este un punct de plecare rezonabil.

  • sigma — multiplicatorul de prag asupra abaterii standard. Valorile mai mici sunt mai agresive (mai mulți pixeli dezactivați); valorile mai mari sunt mai conservatoare. 0.5 este o valoare implicită bună.

Îndreptați mai întâi senzorul către o scenă statică, astfel încât eventualele evenimente generate de mișcare să nu fie contorizate în defavoarea unor pixeli care sunt de fapt în regulă:

csi0.snapshot(time=5000)  # let the user steady the camera
disabled = csi0.ioctl(csi.IOCTL_GENX320_CALIBRATE, 10000, 0.5)
print(f"disabled {disabled} hot pixels")

Filtru anti-pâlpâire (AFK)

Sursele de lumină periodice (fluorescente, afișaje cu LED) generează volume uriașe de evenimente redundante. Filtrul AFK respinge evenimentele al căror pixel comută la o frecvență dintr-o bandă — activați-l prin csi.IOCTL_GENX320_SET_AFK cu marginile benzii în herți:

csi0.ioctl(csi.IOCTL_GENX320_SET_AFK, 1, 130, 160)  # 130-160 Hz
csi0.ioctl(csi.IOCTL_GENX320_SET_AFK, 0)            # disable

Presetări de polarizare (bias)

Fiecare pixel din GenX320 rulează un etaj analogic de intrare cu mai multe polarizări (bias) configurabile. Acestea guvernează împreună sensibilitatea, zgomotul, lățimea de bandă a pixelului și rata de evenimente — combinația potrivită depinde de scenă. Polarizările individuale sunt:

  • DIFF_ON — pragul de contrast al comparatorului pozitiv. Un pixel emite un eveniment ON atunci când log-iluminarea sa a crescut cu această valoare. Mai mic = mai sensibil la tranzițiile luminoase.

  • DIFF_OFF — pragul de contrast al comparatorului negativ (corespondentul simetric pentru evenimentele OFF). Mai mic = mai sensibil la tranzițiile întunecate.

  • FO — frecvența de tăiere trece-jos a pixelului. Mai mare = lățime de bandă a pixelului mai mare (răspuns mai rapid, latență mai mică), dar mai multă activitate de zgomot de fundal.

  • HPF — frecvența de tăiere trece-sus. Mai mare = respingere mai puternică a schimbărilor lente de luminozitate; numai tranzițiile rapide ajung la comparatoare. Utilă pentru ignorarea derivei ambientale.

  • REFR — perioada refractară. După ce un pixel se declanșează, rămâne în resetare pe această durată înainte de a se putea declanșa din nou. Mai mare = timp mort mai lung, util pentru limitarea ratei de evenimente per pixel.

După csi.CSI.reset, driverul aplică csi.GENX320_BIASES_LOW_NOISE, nu csi.GENX320_BIASES_DEFAULT — valorile implicite din fișa tehnică emit o rată de evenimente de fundal mult mai mare, așa că LOW_NOISE este folosit ca punct de plecare pentru a păstra fluxul liniștit. Apelați csi.IOCTL_GENX320_SET_BIASES cu o presetare diferită atunci când aplicația necesită mai multă sensibilitate sau lățime de bandă.

csi.IOCTL_GENX320_SET_BIASES aplică una dintre cele cinci presetări:

  • csi.GENX320_BIASES_DEFAULT — valorile implicite din fișa tehnică GenX320. Sensibilitate, zgomot și lățime de bandă echilibrate pentru scene generale.

  • csi.GENX320_BIASES_LOW_LIGHT — ambele praguri de contrast sunt relaxate pentru o sensibilitate mai mare, FO este coborât pentru a menține zgomotul scăzut, iar HPF este setat la 0 astfel încât schimbările lente de luminozitate să fie încă înregistrate — o scenă cu lumină slabă generează puține evenimente de la sine, așa că dorim ca cât mai multe posibil să treacă.

  • csi.GENX320_BIASES_ACTIVE_MARKER — reglat pentru urmărirea LED-urilor intermitente cu contrast ridicat. Pragurile de contrast sunt ridicate astfel încât numai tranzițiile clare să declanșeze; FO și HPF sunt mărite la maximum pentru a maximiza lățimea de bandă a pixelului și a respinge orice derivă ambientală lentă; REFR este adus la 0 astfel încât fiecare front de pâlpâire să fie captat consecutiv. Rezultatul: un flux care este aproape în întregime fronturi de LED, ușor de urmărit.

  • csi.GENX320_BIASES_LOW_NOISE — valoarea implicită a driverului. Ambele praguri de contrast sunt ridicate față de DEFAULT (mai puțin sensibil), iar FO este coborât (pixel mai lent = pixel mai liniștit). Cel mai bun pentru scene statice sau lente unde evenimentele false ar domina altfel.

  • csi.GENX320_BIASES_HIGH_SPEED — FO este mărit astfel încât fiecare pixel să poată răspunde mai rapid, HPF este ridicat pentru a respinge deriva lentă de luminozitate, iar REFR este ridicat astfel încât un singur front în mișcare rapidă să nu inunde citirea — timpul mort mai lung menține volumul de evenimente limitat sub mișcare intensă.

Suprascrieți polarizările individuale cu csi.IOCTL_GENX320_SET_BIAS plus una dintre csi.GENX320_BIAS_DIFF_ON, csi.GENX320_BIAS_DIFF_OFF, csi.GENX320_BIAS_FO, csi.GENX320_BIAS_HPF sau csi.GENX320_BIAS_REFR și o valoare DAC. Fiecare polarizare este setată independent — alegeți o presetare ca punct de plecare, apoi ajustați polarizările de care are nevoie scena dvs.:

csi0.ioctl(csi.IOCTL_GENX320_SET_BIASES, csi.GENX320_BIASES_LOW_LIGHT)
csi0.ioctl(csi.IOCTL_GENX320_SET_BIAS, csi.GENX320_BIAS_HPF, 20)

Urmărire

Deoarece ieșirea în modul histogramă este doar o imagine în tonuri de gri, urmărirea obișnuită a blob-urilor funcționează direct. Pentru a urmări un LED de tip marker activ, încărcați presetarea de polarizare active-marker și găsiți blob-uri la capătul luminos al histogramei:

import csi
import time

csi0 = csi.CSI(cid=csi.GENX320)
csi0.reset()
csi0.pixformat(csi.GRAYSCALE)
csi0.framesize((320, 320))
csi0.brightness(128)
csi0.contrast(16)
csi0.framerate(200)
csi0.ioctl(csi.IOCTL_GENX320_SET_BIASES, csi.GENX320_BIASES_ACTIVE_MARKER)

clock = time.clock()
while True:
    clock.tick()
    img = csi0.snapshot()
    for blob in img.find_blobs([(120, 140)], invert=True,
                               pixels_threshold=2, area_threshold=4,
                               merge=True):
        img.draw_detection(blob)
    print(clock.fps())

Modul eveniment

Modul eveniment ocolește histograma de pe cip și transmite evenimentele brute într-un ndarray numpy. Fiecare eveniment este un rând de șase coloane uint16:

  • [0] tipul evenimentului — vezi mai jos

  • [1] marca temporală în secunde

  • [2] marca temporală în milisecunde

  • [3] marca temporală în microsecunde

  • [4] coordonata X, 0-319

  • [5] coordonata Y, 0-319

Driverul emite șase tipuri de evenimente în coloana [0]:

  • csi.PIX_OFF_EVENT — un pixel a detectat o scădere a luminozității (pragul comparatorului DIFF_OFF a fost depășit). X/Y indică pixelul care s-a declanșat.

  • csi.PIX_ON_EVENT — un pixel a detectat o creștere a luminozității (pragul DIFF_ON a fost depășit). X/Y indică pixelul.

  • csi.EXT_TRIGGER_FALLING — pinul de declanșare externă al senzorului a văzut un front descendent. X/Y nu sunt utilizate.

  • csi.EXT_TRIGGER_RISING — pinul de declanșare externă al senzorului a văzut un front ascendent. X/Y nu sunt utilizate.

  • csi.RST_TRIGGER_FALLING — declanșare de resetare a pixelilor, front descendent. X/Y nu sunt utilizate. Nu este generat de firmware în acest moment.

  • csi.RST_TRIGGER_RISING — declanșare de resetare a pixelilor, front ascendent. X/Y nu sunt utilizate. Nu este generat de firmware în acest moment.

Intrarea de declanșare externă a GENX320 este conectată la linia de sincronizare a cadrelor a camerei, care este de asemenea direcționată către P10 atât pe procesor, cât și pe conectorul de pini — acționați P10 pentru a injecta fronturi de sincronizare în fluxul de evenimente și pentru a le prelua ca evenimente EXT_TRIGGER_RISING / EXT_TRIGGER_FALLING alături de datele pixelilor.

Majoritatea aplicațiilor se interesează doar de PIX_OFF_EVENT și PIX_ON_EVENT; tipurile de declanșare vă permit să corelați evenimentele cu semnale de sincronizare externe.

Alocați tamponul (buffer) de evenimente cu forma (EVT_res, 6) unde EVT_res este o putere a lui doi între 1024 și 65536, apoi intrați în modul eveniment prin csi.IOCTL_GENX320_SET_MODE cu csi.GENX320_MODE_EVENT și dimensiunea tamponului. Citiți evenimentele cu csi.IOCTL_GENX320_READ_EVENTS, care umple tamponul până la capacitatea sa și returnează numărul de rânduri valide.

Image.draw_event_histogram rasterizează evenimentele într-o imagine în tonuri de gri — pentru fiecare eveniment ON adaugă contrast la compartiment; pentru fiecare eveniment OFF îl scade. clear=True resetează mai întâi imaginea la brightness; clear=False acumulează de-a lungul mai multor apeluri:

import csi
import image
import time
from ulab import numpy as np

img = image.Image(320, 320, image.GRAYSCALE)
events = np.zeros((2048, 6), dtype=np.uint16)

csi0 = csi.CSI(cid=csi.GENX320)
csi0.reset()
csi0.ioctl(csi.IOCTL_GENX320_SET_MODE, csi.GENX320_MODE_EVENT, events.shape[0])

clock = time.clock()
while True:
    clock.tick()
    n = csi0.ioctl(csi.IOCTL_GENX320_READ_EVENTS, events)
    img.draw_event_histogram(events[:n], clear=True, brightness=128, contrast=64)
    img.flush()
    print(n, clock.fps())

Presetările de polarizare din modul histogramă, filtrul AFK și ioctl-urile de calibrare a pixelilor fierbinți funcționează toate la fel în modul eveniment — apelați-le după csi.IOCTL_GENX320_SET_MODE.

Filtrare după polaritate

Feliați tabloul de evenimente cu ulab pentru a păstra numai evenimentele ON (mișcare către o stare mai luminoasă) sau numai evenimentele OFF:

TARGET = csi.PIX_ON_EVENT  # or csi.PIX_OFF_EVENT

events_slice = events[:n]
indices = np.nonzero(events_slice[:, 0] == TARGET)[0]
if len(indices):
    target_events = np.take(events_slice, indices, axis=0)
    img.draw_event_histogram(target_events, clear=True,
                             brightness=128, contrast=64)

Acumulare cu expunere lungă

Setați clear=False pentru a continua să stivuiți evenimentele în aceeași imagine de-a lungul mai multor cadre — rezultatul este o vizualizare a urmelor de mișcare. Resetați periodic pentru a începe o nouă expunere:

EXPOSURE_FRAMES = 30
i = 0
while True:
    n = csi0.ioctl(csi.IOCTL_GENX320_READ_EVENTS, events)
    clear = (i % EXPOSURE_FRAMES) == 0
    img.draw_event_histogram(events[:n], clear=clear, brightness=128, contrast=64)
    img.flush()
    i += 1

Procesare de mare viteză

Renunțați la vizualizare pentru a elibera CPU pentru procesarea evenimentelor. Afișați statisticile numai la fiecare a N-a iterație — afișarea unei linii la fiecare iterație devine blocajul la rate mari de evenimente:

csi0 = csi.CSI(cid=csi.GENX320)
csi0.reset()
csi0.ioctl(csi.IOCTL_GENX320_SET_MODE, csi.GENX320_MODE_EVENT, events.shape[0])

clock = time.clock()
i = 0
while True:
    clock.tick()
    n = csi0.ioctl(csi.IOCTL_GENX320_READ_EVENTS, events)
    i += 1
    if not i % 10:
        print(f"{n} events  {clock.fps()} fps")

Filtru de contrast spațio-temporal (STC)

Un front de contrast real în mișcare tinde să declanșeze o rafală zgomotoasă de evenimente pe același pixel într-o fereastră scurtă de timp — nepotrivirea pixelilor și zgomotul analogic produc evenimente suplimentare în jurul tranziției autentice care nu sunt utile aplicației. Filtrul STC este o post-procesare pe cip care păstrează doar unul (sau câteva) evenimente per rafală și le elimină pe restul.

Implementează trei strategii, selectate prin csi.IOCTL_GENX320_SET_STC și o constantă GENX320_STC_*. Fiecare mod este definit de evenimentele pe care le transmite dintr-o rafală:

Mod

Păstrează

Elimină

csi.GENX320_STC_DISABLE

fiecare eveniment

nimic

csi.GENX320_STC_ONLY

al doilea eveniment al unei rafale

primul + evenimentele ulterioare

csi.GENX320_STC_TRAIL_ONLY

primul eveniment al unei rafale

evenimentele subsecvente

csi.GENX320_STC_TRAIL

primul + fronturile subsecvente

numai zgomotul redundant

În detaliu:

  • csi.GENX320_STC_DISABLE — filtru dezactivat, fiecare eveniment trece (implicit).

  • csi.GENX320_STC_ONLY — păstrează al doilea eveniment al unei rafale. Parametru: stc_threshold (ms). Dacă un eveniment nou pe un pixel sosește în intervalul stc_threshold față de un eveniment anterior, este considerat „al doilea” al unei rafale și este transmis — primul eveniment și orice evenimente ulterioare din aceeași rafală sunt filtrate. Cel mai bun atunci când doriți o tranziție confirmată prin zgomot, mai degrabă decât chiar prima declanșare.

  • csi.GENX320_STC_TRAIL_ONLY — păstrează primul eveniment al unei rafale. Parametru: trail_threshold (ms). După ce un pixel se declanșează, evenimentele ulterioare pe același pixel sunt eliminate până când a trecut trail_threshold. Păstrează sincronizarea precisă a frontului principal — util atunci când momentul comutării polarității contează mai mult decât confirmarea rafalei.

  • csi.GENX320_STC_TRAIL — combină ambele. Parametri: stc_threshold și trail_threshold (ambii în ms). Păstrează frontul principal conform modului Trail plus fronturile subsecvente conform modului STC, astfel încât mai multe evenimente dintr-o rafală tot trec — debit de evenimente mai mare decât filtrele cu un singur mod, dar cel mai bogat semnal.

Cele două praguri trebuie să rămână în limita unui raport de aproximativ 13:1 — senzorul respinge configurațiile în care unul este mai mare de ~13x decât celălalt:

csi0.ioctl(csi.IOCTL_GENX320_SET_STC, csi.GENX320_STC_TRAIL, 1, 2)
csi0.ioctl(csi.IOCTL_GENX320_SET_STC, csi.GENX320_STC_DISABLE)

Adâncimea tamponului

Atunci când ratele de evenimente cresc brusc, pipeline-ul implicit cu triplu tampon favorizează cel mai recent cadru și le elimină pe cele vechi. Măriți adâncimea FIFO prin csi.CSI.framebuffers pentru a pune evenimentele în coadă în schimb — cu prețul procesării unor date ușor mai vechi atunci când gazda rămâne în urmă:

csi0.framebuffers(10)  # FIFO depth, > 3 enables queueing

Streaming și vizualizare pe desktop

Pentru vizualizare GUI în timp real pe un PC gazdă, instrumentul GenX320 Event Streaming din depozitul openmv-projects asociază camera cu un front-end DearPyGui. Interfața GUI de pe PC rulează două vizualizări una lângă alta: o pânză de acumulare a evenimentelor (aceeași idee ca Image.draw_event_histogram, dar cu palete selectabile și moduri de fereastră glisantă vs. ștergere automată) și o hartă de frecvență per pixel acționată de un filtru trece-bandă IIR — utilă pentru depistarea semnalelor periodice (ventilatoare rotative, LED-uri intermitente etc.) direct în fluxul de evenimente.

Vine cu două scripturi de streaming de pe cameră:

  • Modul procesat (genx320_event_mode_streaming_on_cam.py) — camera decodează evenimentele cu csi.IOCTL_GENX320_READ_EVENTS și transmite fiecare rând ca 12 octeți prin USB ([0] tip, [1] sec, [2] ms, [3] us, [4] x, [5] y). Ușor de consumat pe PC deoarece formatul de pe fir corespunde formatului ndarray de pe cameră.

  • Modul brut (genx320_raw_event_mode_streaming_on_cam.py) — camera transmite cuvintele de eveniment împachetate pe 32 de biți native ale cipului prin csi.IOCTL_GENX320_READ_EVENTS_RAW. Aceasta înseamnă 4 octeți per eveniment față de 12 în modul procesat (cu aproximativ 3x mai puține date prin USB), deci o rată de evenimente realizabilă de ~3x mai mare atunci când legătura este blocajul. PC-ul decodează cuvintele împachetate înapoi în aceeași dispunere de eveniment cu 6 coloane folosind numpy vectorizat, astfel încât codul vizualizatorului din aval este identic.

Modul brut este implicit în GUI deoarece debitul USB este constrângerea decisivă la ratele pe care le poate produce GenX320; comutați la modul procesat dacă trebuie să integrați logică de procesare în scriptul de pe cameră.