GENX320 Event-Kamera¶
Das GENX320 Event-Kameramodul ist ein ereignisbasierter Vision-Sensor von Prophesee mit einer Auflösung von 320x320 und Mikrosekunden-Zeitpräzision.
Vollständiges Datenblatt, Fotos und Bestellinformationen finden Sie auf der Produktseite der GENX320 Event-Kamera.
Bemerkung
Unterstützt auf der OpenMV H7 Plus, RT1062 und N6.
Highlights¶
Ereignisbasierter Vision-Sensor mit 320x320
140 dB Dynamikbereich, keine Bewegungsunschärfe
Event-Histogramm-Ausgaberate von über 375 Hz
Die Leistungsaufnahme skaliert mit der Szenenaktivität — beginnt bei ~3 mW
Arbeitet von <5 Lux bis hin zu hellem Sonnenlicht ohne automatische Belichtung
Gibt Graustufen-Einzelbilder oder rohe Ereignisströme aus
Verwendung¶
Der GENX320 ist ein ereignisbasierter Vision-Sensor — anstatt das gesamte 320x320-Array mit einem festen Einzelbildtakt auszulesen, meldet jeder Pixel asynchrone „Ereignisse“ in dem Moment, in dem er eine Helligkeitsänderung erkennt. Jedes Ereignis trägt eine X/Y-Koordinate, eine ON/OFF-Polarität (hell→dunkel oder dunkel→hell) und einen Mikrosekunden-Zeitstempel. Daher rühren die Mikrosekunden-Zeitpräzision des Sensors, das Fehlen von Bewegungsunschärfe, der sehr hohe Dynamikbereich und die mit der Aktivität skalierende Leistungsaufnahme. Statische Szenen erzeugen keine Daten.
Die OpenMV-Firmware stellt den GENX320 über csi.CSI mit cid= csi.GENX320 bereit. Zwei Betriebsmodi sind verfügbar:
Histogramm-Modus (Standard) — Ereignisse werden auf dem Chip in Bins pro Pixel akkumuliert und als 320x320-Graustufen-Einzelbild mit einer konfigurierbaren Rate (~20-350 FPS) gemeldet. Der Sensor verhält sich wie eine normale Kamera, sodass alle standardmäßigen Bildverarbeitungsroutinen (
Image.find_blobs, Paletten usw.) direkt funktionieren.Event-Modus — rohe Ereignisse strömen mit vollständigen Mikrosekunden-Zeitstempeln in ein numpy-
ndarray, für Anwendungen, die die zeitliche Detailtiefe statt eines vorab gebinnten Einzelbildes benötigen.
Histogramm-Modus¶
Im Histogramm-Modus gibt der GENX320 Graustufen-Einzelbilder aus, bei denen jeder Pixel die jüngste Ereignisaktivität an dieser Stelle kodiert. Pixel oberhalb der Helligkeits-Basislinie sind ON-Ereignisse (Helligkeit steigt), darunter sind OFF-Ereignisse (Helligkeit fällt). Die standardmäßige Basislinienhelligkeit ist 128 und der Kontrastschritt pro Ereignis ist 16 — erhöhen Sie den Kontrast, um Ereignisse hervorzuheben:
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 und csi.CSI.framerate sind die drei Stellschrauben, die die Histogramm-Ausgabe formen.
Kolorierte Ausgabe¶
Setzen Sie csi.CSI.color_palette auf image.PALETTE_EVT_LIGHT für einen hellen Hintergrund oder image.PALETTE_EVT_DARK für einen dunklen — der Treiber gibt RGB565-Einzelbilder aus, die die Palette direkt verwenden:
csi0.color_palette(image.PALETTE_EVT_LIGHT)
Hot-Pixel-Kalibrierung¶
Event-Sensoren sammeln „Hot Pixels“ an, die fälschlicherweise auslösen. Führen Sie csi.IOCTL_GENX320_CALIBRATE gegen eine statische Szene aus, um sie zu deaktivieren. Der Treiber erstellt eine 320x320-Trefferzählung pro Pixel, berechnet Mittelwert und Standardabweichung und deaktiviert jeden Pixel, dessen Zählwert über mean + sigma * stddev liegt — danach senden die deaktivierten Pixel keine Ereignisse mehr auf Sensorebene.
Zwei Parameter steuern die Kalibrierung:
event_count— wie viele Ereignisse vor der Berechnung der Statistik gezählt werden. Die Schleife erfasst Einzelbilder, bis die laufende Ereignissumme dieses Budget überschreitet. Höhere Zählwerte liefern eine zuverlässigere Schätzung, allerdings auf Kosten einer längeren Kalibrierungszeit.10000ist ein sinnvoller Ausgangspunkt.sigma— Schwellenwert-Multiplikator für die Standardabweichung. Niedrigere Werte sind aggressiver (mehr Pixel deaktiviert); höhere Werte sind konservativer.0.5ist ein guter Standardwert.
Richten Sie den Sensor zuerst auf eine statische Szene, damit bewegungsbedingte Ereignisse nicht gegen Pixel gezählt werden, die eigentlich in Ordnung sind:
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")
Anti-Flacker-Filter (AFK)¶
Periodische Lichtquellen (Leuchtstoffröhren, LED-betriebene Displays) erzeugen riesige Mengen redundanter Ereignisse. Der AFK-Filter verwirft Ereignisse, deren Pixel mit einer Frequenz innerhalb eines Bandes umschaltet — aktivieren Sie ihn über csi.IOCTL_GENX320_SET_AFK mit den Bandgrenzen in Hertz:
csi0.ioctl(csi.IOCTL_GENX320_SET_AFK, 1, 130, 160) # 130-160 Hz
csi0.ioctl(csi.IOCTL_GENX320_SET_AFK, 0) # disable
Bias-Voreinstellungen¶
Jeder Pixel im GenX320 betreibt ein analoges Frontend mit mehreren konfigurierbaren Biases. Sie steuern gemeinsam Empfindlichkeit, Rauschen, Pixelbandbreite und Ereignisrate — die richtige Kombination hängt von der Szene ab. Die einzelnen Biases sind:
DIFF_ON — der positive Komparator-Kontrastschwellenwert. Ein Pixel sendet ein ON-Ereignis, wenn seine logarithmische Beleuchtung um diesen Betrag gestiegen ist. Niedriger = empfindlicher für helle Übergänge.
DIFF_OFF — der negative Komparator-Kontrastschwellenwert (das symmetrische Gegenstück für OFF-Ereignisse). Niedriger = empfindlicher für dunkle Übergänge.
FO — die Tiefpass-Grenzfrequenz des Pixels. Höher = breitere Pixelbandbreite (schnellere Reaktion, geringere Latenz), aber mehr Hintergrundrausch-Aktivität.
HPF — die Hochpass-Grenzfrequenz. Höher = stärkere Unterdrückung langsamer Helligkeitsänderungen; nur schnelle Übergänge erreichen die Komparatoren. Nützlich, um Umgebungsdrift zu ignorieren.
REFR — die Refraktärzeit. Nachdem ein Pixel ausgelöst hat, bleibt er für diese Dauer im Reset-Zustand, bevor er erneut auslösen kann. Höher = längere Totzeit, nützlich zur Begrenzung der Ereignisrate pro Pixel.
Nach csi.CSI.reset wendet der Treiber csi.GENX320_BIASES_LOW_NOISE an, nicht csi.GENX320_BIASES_DEFAULT — die Datenblatt-Standardwerte erzeugen eine deutlich höhere Hintergrund-Ereignisrate, daher wird LOW_NOISE als Ausgangspunkt verwendet, um den Strom ruhig zu halten. Rufen Sie csi.IOCTL_GENX320_SET_BIASES mit einer anderen Voreinstellung auf, wenn die Anwendung mehr Empfindlichkeit oder Bandbreite benötigt.
csi.IOCTL_GENX320_SET_BIASES wendet eine von fünf Voreinstellungen an:
csi.GENX320_BIASES_DEFAULT— GenX320-Datenblatt-Standardwerte. Ausgewogene Empfindlichkeit, Rauschen und Bandbreite für allgemeine Szenen.csi.GENX320_BIASES_LOW_LIGHT— beide Kontrastschwellenwerte gelockert für höhere Empfindlichkeit, FO gesenkt, um das Rauschen niedrig zu halten, und HPF auf 0 gesetzt, sodass langsame Helligkeitsänderungen weiterhin registriert werden — eine Szene mit wenig Licht erzeugt von sich aus nur wenige Ereignisse, daher möchten wir, dass möglichst viele durchkommen.csi.GENX320_BIASES_ACTIVE_MARKER— abgestimmt auf die Verfolgung kontrastreicher blinkender LEDs. Kontrastschwellenwerte angehoben, sodass nur scharfe Übergänge auslösen; FO und HPF hochgedreht, um die Pixelbandbreite zu maximieren und jegliche langsame Umgebungsdrift zu unterdrücken; REFR auf 0 gezogen, sodass jede Blinkflanke unmittelbar nacheinander erfasst wird. Das Ergebnis: ein Strom, der fast ausschließlich aus LED-Flanken besteht und leicht zu verfolgen ist.csi.GENX320_BIASES_LOW_NOISE— Treiberstandard. Beide Kontrastschwellenwerte gegenüberDEFAULTangehoben (weniger empfindlich) und FO gesenkt (langsamerer Pixel = ruhigerer Pixel). Am besten für statische oder langsame Szenen, in denen falsche Ereignisse andernfalls dominieren würden.csi.GENX320_BIASES_HIGH_SPEED— FO angehoben, sodass jeder Pixel schneller reagieren kann, HPF erhöht, um langsame Helligkeitsdrift zu unterdrücken, und REFR erhöht, damit eine einzelne schnell bewegte Flanke das Auslesen nicht überflutet — die längere Totzeit hält das Ereignisvolumen bei starker Bewegung begrenzt.
Überschreiben Sie einzelne Biases mit csi.IOCTL_GENX320_SET_BIAS zusammen mit einem von csi.GENX320_BIAS_DIFF_ON, csi.GENX320_BIAS_DIFF_OFF, csi.GENX320_BIAS_FO, csi.GENX320_BIAS_HPF oder csi.GENX320_BIAS_REFR und einem DAC-Wert. Jeder Bias wird unabhängig gesetzt — wählen Sie eine Voreinstellung als Ausgangspunkt und passen Sie dann die Biases an, die Ihre Szene benötigt:
csi0.ioctl(csi.IOCTL_GENX320_SET_BIASES, csi.GENX320_BIASES_LOW_LIGHT)
csi0.ioctl(csi.IOCTL_GENX320_SET_BIAS, csi.GENX320_BIAS_HPF, 20)
Verfolgung¶
Da die Ausgabe im Histogramm-Modus einfach ein Graustufenbild ist, funktioniert die normale Blob-Verfolgung direkt. Um eine Active-Marker-LED zu verfolgen, laden Sie die Active-Marker-Bias-Voreinstellung und suchen Sie Blobs am hellen Ende des Histogramms:
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())
Event-Modus¶
Der Event-Modus umgeht das On-Chip-Histogramm und streamt rohe Ereignisse in ein numpy-ndarray. Jedes Ereignis ist eine Zeile aus sechs uint16-Spalten:
[0]Ereignistyp — siehe unten[1]Sekunden-Zeitstempel[2]Millisekunden-Zeitstempel[3]Mikrosekunden-Zeitstempel[4]X-Koordinate, 0-319[5]Y-Koordinate, 0-319
Der Treiber gibt sechs Ereignistypen in Spalte [0] aus:
csi.PIX_OFF_EVENT— ein Pixel hat eine Helligkeitsabnahme erkannt (derDIFF_OFF-Komparatorschwellenwert wurde überschritten). X/Y zeigen auf den Pixel, der ausgelöst hat.csi.PIX_ON_EVENT— ein Pixel hat eine Helligkeitszunahme erkannt (derDIFF_ON-Schwellenwert wurde überschritten). X/Y zeigen auf den Pixel.csi.EXT_TRIGGER_FALLING— der externe Trigger-Pin des Sensors hat eine fallende Flanke erkannt. X/Y werden nicht verwendet.csi.EXT_TRIGGER_RISING— der externe Trigger-Pin des Sensors hat eine steigende Flanke erkannt. X/Y werden nicht verwendet.csi.RST_TRIGGER_FALLING— Pixel-Reset-Trigger, fallende Flanke. X/Y werden nicht verwendet. Wird derzeit nicht von der Firmware erzeugt.csi.RST_TRIGGER_RISING— Pixel-Reset-Trigger, steigende Flanke. X/Y werden nicht verwendet. Wird derzeit nicht von der Firmware erzeugt.
Der externe Trigger-Eingang des GENX320 ist mit der Frame-Sync-Leitung der Kamera verbunden, die auch zu P10 sowohl am Prozessor als auch an der Pin-Stiftleiste geführt wird — treiben Sie P10 an, um Sync-Flanken in den Ereignisstrom einzuspeisen und sie als EXT_TRIGGER_RISING / EXT_TRIGGER_FALLING-Ereignisse neben den Pixeldaten aufzunehmen.
Die meisten Anwendungen interessieren sich nur für PIX_OFF_EVENT und PIX_ON_EVENT; mit den Trigger-Typen können Sie Ereignisse mit externen Zeitsignalen korrelieren.
Allokieren Sie den Ereignispuffer mit der Form (EVT_res, 6), wobei EVT_res eine Zweierpotenz zwischen 1024 und 65536 ist, und wechseln Sie dann über csi.IOCTL_GENX320_SET_MODE mit csi.GENX320_MODE_EVENT und der Puffergröße in den Event-Modus. Lesen Sie Ereignisse mit csi.IOCTL_GENX320_READ_EVENTS, das den Puffer bis zu seiner Kapazität füllt und die Anzahl der gültigen Zeilen zurückgibt.
Image.draw_event_histogram rastert Ereignisse in ein Graustufenbild — für jedes ON-Ereignis addiert es contrast zum Bin; für jedes OFF-Ereignis subtrahiert es. clear=True setzt das Bild zuerst auf brightness zurück; clear=False akkumuliert über viele Aufrufe hinweg:
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())
Die Bias-Voreinstellungen des Histogramm-Modus, der AFK-Filter und die Hot-Pixel-Kalibrierungs-Ioctls funktionieren im Event-Modus alle auf die gleiche Weise — rufen Sie sie nach csi.IOCTL_GENX320_SET_MODE auf.
Filtern nach Polarität¶
Schneiden Sie das Ereignis-Array mit ulab, um nur ON-Ereignisse (Bewegung in einen helleren Zustand) oder nur OFF-Ereignisse zu behalten:
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)
Langzeitbelichtungs-Akkumulation¶
Setzen Sie clear=False, um Ereignisse über viele Einzelbilder hinweg im selben Bild zu stapeln — das Ergebnis ist eine Bewegungsspur-Visualisierung. Setzen Sie regelmäßig zurück, um eine neue Belichtung zu beginnen:
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
Hochgeschwindigkeitsverarbeitung¶
Lassen Sie die Visualisierung weg, um CPU für die Ereignisverarbeitung freizugeben. Geben Sie Statistiken nur bei jeder N-ten Iteration aus — eine Print-Zeile bei jeder Iteration wird bei hohen Ereignisraten zum Flaschenhals:
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")
Räumlich-zeitlicher Kontrastfilter (STC)¶
Eine echte bewegte Kontrastkante neigt dazu, innerhalb eines kurzen Zeitfensters einen verrauschten Burst von Ereignissen auf demselben Pixel auszulösen — Pixel-Mismatch und analoges Rauschen erzeugen rund um den echten Übergang zusätzliche Ereignisse, die für die Anwendung nicht nützlich sind. Der STC-Filter ist eine On-Chip-Nachverarbeitung, die nur eines (oder wenige) Ereignisse pro Burst behält und den Rest verwirft.
Er implementiert drei Strategien, ausgewählt über csi.IOCTL_GENX320_SET_STC und eine GENX320_STC_*-Konstante. Jeder Modus ist dadurch definiert, welche Ereignisse er aus einem Burst weiterleitet:
Modus |
Behält |
Verwirft |
|---|---|---|
jedes Ereignis |
nichts |
|
zweites Ereignis eines Bursts |
erstes + spätere Ereignisse |
|
erstes Ereignis eines Bursts |
nachfolgende Ereignisse |
|
erste + nachfolgende Flanken |
nur redundantes Rauschen |
Im Detail:
csi.GENX320_STC_DISABLE— Filter aus, jedes Ereignis passiert (Standard).csi.GENX320_STC_ONLY— behält das zweite Ereignis eines Bursts. Parameter:stc_threshold(ms). Wenn ein neues Ereignis auf einem Pixel innerhalb vonstc_thresholdnach einem vorherigen Ereignis eintrifft, wird es als das „zweite“ eines Bursts betrachtet und weitergeleitet — das erste Ereignis und alle nachfolgenden Ereignisse im selben Burst werden herausgefiltert. Am besten, wenn Sie einen rauschbestätigten Übergang statt des allerersten Treffers wünschen.csi.GENX320_STC_TRAIL_ONLY— behält das erste Ereignis eines Bursts. Parameter:trail_threshold(ms). Nachdem ein Pixel ausgelöst hat, werden nachfolgende Ereignisse auf demselben Pixel verworfen, bistrail_thresholdverstrichen ist. Bewahrt das präzise Timing der Vorderflanke — nützlich, wenn der Moment des Polaritätswechsels wichtiger ist als die Burst-Bestätigung.csi.GENX320_STC_TRAIL— kombiniert beides. Parameter:stc_thresholdundtrail_threshold(beide ms). Behält die Vorderflanke gemäß dem Trail-Modus plus nachfolgende Flanken gemäß dem STC-Modus, sodass mehrere Ereignisse aus einem Burst weiterhin durchkommen — höherer Ereignisdurchsatz als die Einzelmodus-Filter, aber das reichhaltigste Signal.
Die beiden Schwellenwerte müssen ungefähr innerhalb eines Verhältnisses von 13:1 bleiben — der Sensor lehnt Konfigurationen ab, bei denen einer mehr als das ~13-fache des anderen beträgt:
csi0.ioctl(csi.IOCTL_GENX320_SET_STC, csi.GENX320_STC_TRAIL, 1, 2)
csi0.ioctl(csi.IOCTL_GENX320_SET_STC, csi.GENX320_STC_DISABLE)
Puffertiefe¶
Wenn die Ereignisraten in die Höhe schnellen, bevorzugt die standardmäßige Triple-Buffer-Pipeline das neueste Einzelbild und verwirft die alten. Erhöhen Sie die FIFO-Tiefe über csi.CSI.framebuffers, um Ereignisse stattdessen in die Warteschlange zu stellen — auf Kosten der Verarbeitung etwas älterer Daten, wenn der Host ins Hintertreffen gerät:
csi0.framebuffers(10) # FIFO depth, > 3 enables queueing
Desktop-Streaming und Visualisierung¶
Für die Echtzeit-GUI-Visualisierung auf einem Host-PC verbindet das GenX320 Event Streaming-Tool im openmv-projects-Repository die Kamera mit einem DearPyGui-Frontend. Die PC-GUI führt zwei Visualisierungen nebeneinander aus: eine Ereignis-Akkumulations-Leinwand (gleiche Idee wie Image.draw_event_histogram, aber mit auswählbaren Paletten und Schiebefenster- vs. Auto-Clear-Modi) und eine Frequenzkarte pro Pixel, die von einem IIR-Bandpassfilter angetrieben wird — nützlich, um periodische Signale (rotierende Lüfter, blinkende LEDs usw.) direkt im Ereignisstrom zu erkennen.
Es werden zwei On-Cam-Streaming-Skripte mitgeliefert:
Verarbeiteter Modus (
genx320_event_mode_streaming_on_cam.py) — die Kamera dekodiert Ereignisse mitcsi.IOCTL_GENX320_READ_EVENTSund streamt jede Zeile als 12 Bytes über USB ([0]Typ,[1]Sek,[2]ms,[3]us,[4]x,[5]y). Auf dem PC leicht zu verarbeiten, da das Übertragungsformat dem On-Cam-ndarray-Format entspricht.Roh-Modus (
genx320_raw_event_mode_streaming_on_cam.py) — die Kamera streamt die nativen 32-Bit-gepackten Ereigniswörter des Chips übercsi.IOCTL_GENX320_READ_EVENTS_RAW. Das sind 4 Bytes pro Ereignis gegenüber 12 im verarbeiteten Modus (etwa 3-mal weniger Daten über USB), also eine ~3-mal höhere erreichbare Ereignisrate, wenn die Verbindung der Flaschenhals ist. Der PC dekodiert die gepackten Wörter mit vektorisiertem numpy zurück in dasselbe 6-Spalten-Ereignislayout, sodass der nachgelagerte Visualisierungscode identisch ist.
Der Roh-Modus ist in der GUI die Standardeinstellung, da der USB-Durchsatz bei den Raten, die der GenX320 erzeugen kann, die bindende Einschränkung ist; wechseln Sie in den verarbeiteten Modus, wenn Sie Verarbeitungslogik in das On-Cam-Skript einbinden müssen.