protocol — OpenMV-Protokollkanäle#
Das Modul protocol stellt das OpenMV-Host-Protokoll für Python bereit. Es erlaubt die Initialisierung und Konfiguration des firmwareseitigen Protokoll-Stacks und ermöglicht es Benutzercode, eigene logische Kanäle zu registrieren, die von einem Python-Objekt unterstützt werden, das die Kanalschnittstelle implementiert (read, write, size, poll usw.). Mit diesem kommunizieren Desktop-Begleittools, wenn sie Bilddaten streamen oder interaktive Widgets an einer verbundenen Kamera bereitstellen.
Beispiele#
Streamt ein RGB565-Bild an ein Host-Tool über ein eigenes Backend, das die Roh-Kanalschnittstelle implementiert (backend.size(), backend.shape(), backend.poll(), backend.read()):
import csi
import protocol
csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.HD)
img = csi0.snapshot()
img_mv = memoryview(img.bytearray())
frame_ready = True
class FrameChannel:
def size(self):
return len(img_mv)
def shape(self):
return (img.height(), img.width(), len(img_mv))
def poll(self):
return frame_ready
def read(self, offset, size):
global frame_ready
end = offset + size
chunk = img_mv[offset:end]
if end >= len(img_mv):
frame_ready = False
return chunk
protocol.register(name="frame", backend=FrameChannel())
while True:
if not frame_ready:
img = csi0.snapshot()
img_mv = memoryview(img.bytearray())
frame_ready = True
Das passende hostseitige Skript, das das openmv-Python-Paket (pip install openmv) verwendet, um eine Verbindung herzustellen, das kameraseitige Skript zu übertragen und jedes Einzelbild abzurufen:
import cv2
import numpy as np
from openmv.camera import Camera
# The on-cam script above, stored as a string (or read from a file).
SCRIPT = open("frame_streamer_on_cam.py").read()
with Camera("/dev/ttyACM0", baudrate=921600) as cam:
cam.stop()
cam.exec(SCRIPT)
while True:
status = cam.read_status()
if not cam.has_channel("frame") or not status.get("frame"):
continue
h, w, size = cam._channel_shape(cam.get_channel(name="frame"))
if cam.channel_size("frame") < size:
continue
data = cam.channel_read("frame", size)
rgb565 = np.frombuffer(data, dtype="<u2").reshape(h, w)
# Unpack RGB565 to an HxWx3 uint8 RGB image.
r = ((rgb565 >> 11) & 0x1F) << 3
g = ((rgb565 >> 5) & 0x3F) << 2
b = ( rgb565 & 0x1F) << 3
frame = np.dstack([r, g, b]).astype(np.uint8)
# Display with OpenCV (cv2 expects BGR, not RGB).
cv2.imshow("OpenMV", cv2.cvtColor(frame, cv2.COLOR_RGB2BGR))
if cv2.waitKey(1) == ord("q"):
break
cv2.destroyAllWindows()
Ersetzen Sie /dev/ttyACM0 durch den seriellen Port der Kamera (z. B. COM3 unter Windows). Der Konstruktor openmv.camera.Camera akzeptiert dieselben Protokollparameter wie init (crc / seq / ack / events / max_payload / max_retry / timeout), wenn der kameraseitige Stack passend neu konfiguriert wurde.
Funktionen#
- protocol.init(crc: bool = True, seq: bool = True, ack: bool = True, events: bool = True, max_payload: int = ..., rtx_retries: int = 3, rtx_timeout_ms: int = 500, lock_interval_ms: int = 10, poll_ms: int = 0) None#
Initialisiert (oder rekonfiguriert) den Protokoll-Stack und registriert die standardmäßigen logischen Datenkanäle (
stdin,stdout,streamund, falls einkompiliert,profile). LöstRuntimeErroraus, wenn die Initialisierung fehlschlägt. Die Firmware startet bereits mit einem laufenden Standard-USB-Protokoll-Stack, sodass dieser Aufruf nur nötig ist, um den Transport zu wechseln oder die Standard-Framing-Parameter zu überschreiben. Ein erneuter Aufruf initialisiert den Stack neu: Der vorherige Polling-Timer wird entfernt und die Kanäle werden neu registriert.crcaktiviert die CRC-Validierung von Protokoll-Frames.seqaktiviert die Verfolgung von Sequenznummern.ackaktiviert Bestätigungen pro Frame.eventsaktiviert Kanal-Ereignisbenachrichtigungen.max_payloadist die maximale Nutzlastgröße in Bytes. Wird sie weggelassen, wird der untenstehende kameraspezifische Standardwert verwendet; er wird aus der Protokoll-Puffergröße der jeweiligen Platine alsbuffer - 10 (header) - 4 (CRC)abgeleitet.Kamera
Puffergröße
Maximale Nutzlast
OpenMV Cam M4 (
OPENMV2)512
498
OpenMV Cam M7 (
OPENMV3)512
498
OpenMV Cam H7 (
OPENMV4)512
498
OpenMV Cam H7 Plus (
OPENMV4P)4096
4082
OpenMV Pure Thermal (
OPENMVPT)4096
4082
OpenMV Cam RT1062 (
OPENMV_RT1060)4096
4082
OpenMV Cam N6 (
OPENMV_N6)8192
8178
OpenMV AE3 (
OPENMV_AE3)8192
8178
Arduino Portenta H7 (
ARDUINO_PORTENTA_H7)4096
4082
Arduino Giga (
ARDUINO_GIGA)4096
4082
Arduino Nicla Vision (
ARDUINO_NICLA_VISION)4096
4082
rtx_retriesist die Anzahl der erneuten Übertragungsversuche. Standardwert3.rtx_timeout_msist das Zeitlimit für die erneute Übertragung in Millisekunden (verdoppelt sich nach jedem Zeitüberschreiten). Standardwert500.lock_interval_msist das minimale Sperrintervall in Millisekunden. Standardwert10.poll_msist das Abfrageintervall in Millisekunden.0(der Standardwert) deaktiviert das Timer-Polling.
- protocol.is_active() bool#
Gibt
Truezurück, wenn aktuell ein Host verbunden und der Protokoll-Stack aktiv ist, andernfallsFalse.
- protocol.poll() int#
Führt eine Iteration der Protokoll-Task aus: leert den Empfangspuffer des Transports, parst alle vollständigen Frames, verteilt Befehle und bedient Kanalereignisse. Normalerweise erledigt die Firmware dies für Sie aus dem USB-Interrupt und dem
poll_ms-Timer heraus, sodass dies nur für benutzerdefinierte Transporte nötig ist, die von keinem der beiden angetrieben werden – zum Beispiel wennpoll_ms=0gesetzt ist und der Transport aus der Hauptschleife des Skripts abgefragt wird.Gibt
0bei Erfolg zurück und-1, wenn kein aktiver Transport registriert ist oder der Aufruf aus einem Interrupt-Kontext erfolgte.
- protocol.register(name: str, *, backend: object, flags: int = 0) ProtocolChannel#
Registriert ein Python-
backend-Objekt als neuen logischen Kanal und gibt einProtocolChannel-Handle zurück. Die verfügbaren Methoden desbackend-Objekts (siehe Backend-Schnittstelle unten) bestimmen die Fähigkeiten des Kanals;protocol.CHANNEL_FLAG_READ,protocol.CHANNEL_FLAG_WRITEundprotocol.CHANNEL_FLAG_LOCKwerden automatisch zuflagshinzugefügt, wenn die entsprechenden Methoden implementiert sind.nameist der Kanalname als Zeichenkette. Wird auf die Größe des Kanalnamen-Puffers der Firmware gekürzt. Erforderlich.backendist das Python-Objekt, das die Backend-Schnittstelle implementiert. Erforderlich. Wird typischerweise als Schlüsselwort übergeben (backend=...).flagssind zusätzliche Kanal-Flag-Bits (siehe dieCHANNEL_FLAG_*-Konstanten). Optional; Standardwert0.Löst
RuntimeErroraus, wenn der Kanal nicht registriert werden kann (z. B. wenn keine freien Kanalplätze vorhanden sind).
Klassen#
- class protocol.ProtocolChannel#
Handle, das von
protocol.registerzurückgegeben wird. Instanzen werden nicht direkt erstellt.
Backend-Schnittstelle#
Ein an protocol.register übergebenes Backend-Objekt kann eine beliebige Teilmenge der folgenden Methoden implementieren. Nur die auf dem Objekt vorhandenen Methoden werden mit der C-Protokollschicht verdrahtet; fehlende Methoden lassen die entsprechende Fähigkeit deaktiviert.
- class protocol.backend#
Kanal-Backend-Objekt, das an
protocol.registerübergeben wird. Die folgenden Methoden beschreiben die optionale Schnittstelle, die ein Python-Backend implementieren kann.- init() object#
Wird einmal aufgerufen, wenn der Kanal initialisiert wird. Gibt bei Erfolg einen beliebigen Wert ungleich
Nonezurück; eine Ausnahme oder ein fehlender Rückgabewert wird als Fehler behandelt.
- shape() tuple#
Gibt ein Tupel aus bis zu vier Ganzzahlen zurück, das die Datenform beschreibt (z. B. Bildabmessungen). Bis zu vier Elemente werden von der Protokollschicht verbraucht.
- flush() object#
Schreibt alle ausstehenden Daten aus. Gibt bei Erfolg einen beliebigen Wert ungleich
Nonezurück.
- read(offset: int, size: int) bytes#
Gibt bis zu
sizeBytes aboffsetalsbytes-ähnliches Objekt zurück, das das Buffer-Protokoll unterstützt.
- readp(offset: int, size: int) bytes#
Zero-Copy-Variante von
read. Gibt einen Puffer zurück, dessen zugrunde liegender Speicher direkt von der Protokollschicht gelesen wird; der Puffer muss für die Dauer der Übertragung gültig bleiben.
- write(offset: int, data: bytearray) int#
Schreibt
dataanoffset.dataist einbytearray, das direkt auf den C-Puffer verweist. Gibt die Anzahl der geschriebenen Bytes zurück oder0bei standardmäßigem Erfolg.
- class protocol.CBORChannel(on_read: Callable | None = None, on_write: Callable | None = None)#
Ein höherstufiges Python-Backend (bereitgestellt vom eingefrorenen
protocol-Paket), das benannte Felder mit SenML-kompatiblen Integer-Schlüsseln nach CBOR serialisiert. Unterstützt Anzeige-Widgets (label,text,depth,waveform) und interaktive Steuerelemente (toggle,pushbutton,slider,spinbox,select,radio,lineedit) miton_read-/on_write-Callbacks.on_readist ein optionales aufrufbares Objekton_read(channel), das aufgerufen wird, bevor der Kanal für den Host serialisiert wird. Verwenden Sie es, um Feldwerte zu aktualisieren.on_writeist ein optionales aufrufbares Objekton_write(channel, name, value), das aufgerufen wird, wenn der Host einen neuen Wert für ein benanntes Feld schreibt.- add(name: str, type: str, value: Any = None, unit: str | None = None, min: int | float | None = None, max: int | float | None = None, step: int | float | None = None, options: list | None = None, width: int | None = None, height: int | None = None, sample_rate: float | None = None, series: int = 1, typecode: str | None = None) None#
Fügt dem Kanal ein benanntes Feld hinzu.
nameist der Anzeigename; muss innerhalb dieses Kanals eindeutig sein.typeist der Widget-Typ:"label"– schreibgeschützte numerische Anzeige mit optionalerunit."text"– schreibgeschützte String-Anzeige."toggle"– boolescher Schalter."pushbutton"– momentane Aktion (Auslösen, Kalibrieren, …). Der Host schreibt beim KlickTrueund stellt keinen Zustand dar."slider"– numerisches Steuerelement mitmin/max/step."spinbox"– präzise schrittweise Zahleneingabe; teilt sich die Slider-Felder und die Aktualisierungsform per(min, max, value)-Tupel."select"– Drop-down-Liste deroptions-Strings."radio"–optionsdargestellt als Gruppe von Radiobuttons."lineedit"– beschreibbare String-Eingabe."depth"– 2D-Tiefenkartenanzeige mitwidthxheight."waveform"– 1D-Zeitreihendiagramm aus verschachtelten Abtastwerten.
valueist der Anfangswert. Der Standardwert hängt vontypeab:0fürlabel,""fürtext/lineedit/select/radio,Falsefürtoggle/pushbutton,minfürslider/spinbox.unitist der Einheiten-String fürlabel/slider/spinbox/waveform(z. B."Cel","%RH").minist der Minimalwert (Bereich für slider/spinbox oder Anzeigebereich für depth/waveform).maxist der Maximalwert (Bereich für slider/spinbox oder Anzeigebereich für depth/waveform).stepist die Schrittweite (slider/spinbox).optionsist die Liste der Options-Strings (select/radio) oder die Liste der Namen je Serie (waveform).widthist die Breite in Pixeln (Tiefe).heightist die Höhe in Pixeln (Tiefe).sample_rateist die Abtastrate in Abtastwerten pro Sekunde (waveform). Wird dem Host als Abtastperiode gesendet.seriesist die Anzahl der verschachtelten Serien in jedem Abtastblock (waveform), z. B.3für eine XYZ-Aufzeichnung eines Beschleunigungssensors.typecodeist derarray-Typcode der Waveform-Abtastwerte (z. B."H","h","f"). Standardwert"H"(vorzeichenlos, 16 Bit).
- __getitem__(name: str) object#
Gibt den aktuellen Wert des benannten Feldes zurück. Für
depth- undwaveform-Felder wird der binäre Datenpuffer zurückgegeben, andernfalls der Skalarwert.
- __setitem__(name: str, value: Any) None#
Setzt den Wert des benannten Feldes. Für
slider- undspinbox-Felder aktualisiert ein(min, max, value)-Tupel Bereich und aktuellen Wert gleichzeitig. Fürdepth-Felder istvalueder binäre Datenpuffer. Fürwaveform-Felder istvalueein bytes-artiger Block verschachtelter Abtastwerte imtypecodedes Feldes; die Anzahl der Abtastwerte je Serie wird aus seiner Länge abgeleitet, und ein monotoner Zeitstempel (in Mikrosekunden, akkumuliert austime.ticks_us()) wird angehängt, damit der Host den Block auf seiner Zeitachse platzieren kann.
- poll() bool#
Methode der Backend-Schnittstelle. Gibt
Truezurück, wenn serialisierte Daten für den Host verfügbar sind.
- size() int#
Methode der Backend-Schnittstelle. Ruft
on_readauf (falls gesetzt) und gibt die Größe des serialisierten Puffers zurück.
Konstanten#
Kanal-Flag-Bits (bitweise kombiniert; an protocol.register über flags übergeben oder automatisch anhand der Methoden des Backends gesetzt).
- protocol.CHANNEL_FLAG_PHYSICAL: int#
Der Kanal stellt einen physischen Transport dar (im Gegensatz zu einem logischen Datenkanal).
Integrierte Kanalkennungen.