protocol — Canali del protocollo OpenMV#
Il modulo protocol espone il protocollo host di OpenMV a Python. Consente di inizializzare e configurare lo stack di protocollo lato firmware e permette al codice utente di registrare canali logici personalizzati supportati da un oggetto Python che implementa l’interfaccia del canale (read, write, size, poll, ecc.). È ciò con cui dialogano gli strumenti companion desktop quando trasmettono dati immagine in streaming o espongono widget interattivi a una camera collegata.
Esempi#
Trasmette in streaming un’immagine RGB565 a uno strumento host usando un backend personalizzato che implementa l’interfaccia raw del canale (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
Lo script lato host corrispondente, che usa il pacchetto Python openmv (pip install openmv) per connettersi, inviare lo script sulla camera e recuperare ogni frame:
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()
Sostituisci /dev/ttyACM0 con la porta seriale della camera (ad esempio COM3 su Windows). Il costruttore openmv.camera.Camera accetta gli stessi parametri di protocollo di init (crc / seq / ack / events / max_payload / max_retry / timeout) quando lo stack lato camera è stato riconfigurato in modo corrispondente.
Funzioni#
- 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#
Inizializza (o riconfigura) lo stack di protocollo e registra i canali dati logici predefiniti (
stdin,stdout,streame, se compilato,profile). SollevaRuntimeErrorse l’inizializzazione fallisce. Il firmware si avvia con uno stack di protocollo USB predefinito già in esecuzione, quindi chiamare questa funzione è necessario solo per cambiare il trasporto o sovrascrivere i parametri di framing predefiniti. Chiamarla di nuovo reinizializza lo stack: il timer di polling precedente viene rimosso e i canali vengono registrati da capo.crcabilita la validazione CRC sui frame di protocollo.seqabilita il tracciamento dei numeri di sequenza.ackabilita gli acknowledgement per ogni frame.eventsabilita le notifiche di evento del canale.max_payloadè la dimensione massima del payload in byte. Se omesso viene usato il valore predefinito per camera indicato di seguito; deriva dalla dimensione del buffer di protocollo di ciascuna scheda secondo la formulabuffer - 10 (header) - 4 (CRC).Camera
Dimensione buffer
Payload massimo
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_retriesè il numero di tentativi di ritrasmissione. Predefinito3.rtx_timeout_msè il timeout di ritrasmissione in millisecondi (raddoppiato dopo ogni timeout). Predefinito500.lock_interval_msè l’intervallo di lock minimo in millisecondi. Predefinito10.poll_msè l’intervallo di polling in millisecondi.0(il valore predefinito) disabilita il polling tramite timer.
- protocol.is_active() bool#
Restituisce
Truese un host è attualmente connesso e lo stack di protocollo è attivo, altrimentiFalse.
- protocol.poll() int#
Esegue un’iterazione del task di protocollo: svuota il buffer di ricezione del trasporto, analizza i frame completi, smista i comandi e gestisce gli eventi dei canali. Normalmente il firmware lo fa per te dall’interrupt USB e dal timer
poll_ms, quindi è necessario solo per trasporti personalizzati che non sono pilotati da nessuno dei due – ad esempio quandopoll_ms=0e il trasporto viene interrogato in polling dal loop principale dello script.Restituisce
0in caso di successo e-1se non è registrato alcun trasporto attivo o se la chiamata è stata effettuata da un contesto di interrupt.
- protocol.register(name: str, *, backend: object, flags: int = 0) ProtocolChannel#
Registra un oggetto Python
backendcome nuovo canale logico e restituisce un handleProtocolChannel. I metodi disponibili dell’oggettobackend(vedi Interfaccia del backend di seguito) determinano le capacità del canale;protocol.CHANNEL_FLAG_READ,protocol.CHANNEL_FLAG_WRITEeprotocol.CHANNEL_FLAG_LOCKvengono aggiunti automaticamente aflagsquando i metodi corrispondenti sono implementati.nameè il nome del canale come stringa. Viene troncato alla dimensione del buffer del nome canale del firmware. Obbligatorio.backendè l’oggetto Python che implementa l’interfaccia del backend. Obbligatorio. Tipicamente passato per parola chiave (backend=...).flagssono bit di flag aggiuntivi del canale (vedi le costantiCHANNEL_FLAG_*). Opzionale; il valore predefinito è0.Solleva
RuntimeErrorse il canale non può essere registrato (ad esempio se non ci sono slot di canale liberi).
Classi#
- class protocol.ProtocolChannel#
Handle restituito da
protocol.register. Le istanze non vengono costruite direttamente.
Interfaccia del backend#
Un oggetto backend passato a protocol.register può implementare un qualsiasi sottoinsieme dei metodi seguenti. Solo i metodi presenti nell’oggetto vengono collegati al livello di protocollo C; i metodi mancanti lasciano disabilitata la capacità corrispondente.
- class protocol.backend#
Oggetto backend del canale passato a
protocol.register. I metodi seguenti descrivono l’interfaccia opzionale che un backend Python può implementare.- init() object#
Richiamato una volta sola quando il canale viene inizializzato. Restituisce un qualsiasi valore diverso da
Nonein caso di successo; un’eccezione o un valore di ritorno mancante viene trattato come errore.
- shape() tuple#
Restituisce una tupla di un massimo di quattro interi che descrivono la forma dei dati (ad esempio le dimensioni dell’immagine). Il livello di protocollo consuma fino a quattro elementi.
- flush() object#
Svuota eventuali dati in sospeso. Restituisce un qualsiasi valore diverso da
Nonein caso di successo.
- read(offset: int, size: int) bytes#
Restituisce fino a
sizebyte a partire daoffsetcome oggetto di tipobytesche supporta il protocollo buffer.
- readp(offset: int, size: int) bytes#
Variante zero-copy di
read. Restituisce un buffer la cui memoria sottostante viene letta direttamente dal livello di protocollo; il buffer deve rimanere valido per tutta la durata del trasferimento.
- write(offset: int, data: bytearray) int#
Scrive
dataaoffset.dataè unbytearrayche fa riferimento direttamente al buffer C. Restituisce il numero di byte scritti, oppure0come successo predefinito.
- class protocol.CBORChannel(on_read: Callable | None = None, on_write: Callable | None = None)#
Un backend Python di più alto livello (fornito dal pacchetto frozen
protocol) che serializza campi con nome in CBOR usando chiavi intere compatibili con SenML. Supporta widget di visualizzazione (label,text,depth,waveform) e controlli interattivi (toggle,pushbutton,slider,spinbox,select,radio,lineedit) con callbackon_read/on_write.on_readè un callable opzionaleon_read(channel)invocato prima che il canale venga serializzato per l’host. Usalo per aggiornare i valori dei campi.on_writeè un callable opzionaleon_write(channel, name, value)invocato quando l’host scrive un nuovo valore per un campo denominato.- 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#
Aggiunge un campo denominato al canale.
nameè il nome visualizzato; deve essere univoco all’interno di questo canale.typeè il tipo di widget:"label"– visualizzazione numerica in sola lettura con unaunitopzionale."text"– visualizzazione di stringa in sola lettura."toggle"– interruttore booleano."pushbutton"– azione momentanea (trigger, calibrazione, …). L’host scriveTrueal clic e non visualizza alcuno stato."slider"– controllo numerico conmin/max/step."spinbox"– inserimento numerico preciso a passi; condivide i campi dello slider e la forma di aggiornamento con tupla(min, max, value)."select"– menu a tendina delle stringheoptions."radio"–optionsvisualizzate come un gruppo di pulsanti radio."lineedit"– campo di inserimento di stringa scrivibile."depth"– visualizzazione 2D di una mappa di profondità diwidthxheight."waveform"– grafico 1D di serie temporali di campioni interlacciati.
valueè il valore iniziale. Il valore predefinito dipende datype:0perlabel,""pertext/lineedit/select/radio,Falsepertoggle/pushbutton,minperslider/spinbox.unitè la stringa dell’unità di misura perlabel/slider/spinbox/waveform(ad es."Cel","%RH").minè il valore minimo (intervallo di slider/spinbox, o intervallo di visualizzazione di depth/waveform).maxè il valore massimo (intervallo di slider/spinbox, o intervallo di visualizzazione di depth/waveform).stepè la dimensione del passo (slider/spinbox).optionsè la lista delle stringhe delle opzioni (select/radio), oppure la lista dei nomi delle singole serie (waveform).widthè la larghezza in pixel (depth).heightè l’altezza in pixel (depth).sample_rateè la frequenza di campionamento in campioni al secondo (waveform). Viene inviata all’host come periodo di campionamento.seriesè il numero di serie interlacciate in ogni blocco di campioni (waveform), ad es.3per una traccia XYZ di un accelerometro.typecodeè il typecodearraydei campioni della waveform (ad es."H","h","f"). Il valore predefinito è"H"(16 bit senza segno).
- __getitem__(name: str) object#
Restituisce il valore corrente del campo con il nome indicato. Per i campi
depthewaveformviene restituito il buffer di dati binari, altrimenti il valore scalare.
- __setitem__(name: str, value: Any) None#
Imposta il valore del campo con il nome indicato. Per i campi
sliderespinbox, una tupla(min, max, value)aggiorna contemporaneamente l’intervallo e il valore corrente. Per i campidepth,valueè il buffer di dati binari. Per i campiwaveform,valueè un blocco bytes-like di campioni interlacciati neltypecodedel campo; il numero di campioni per serie viene ricavato dalla sua lunghezza e viene allegato un timestamp monotonico (in microsecondi, accumulato datime.ticks_us()) in modo che l’host possa collocare il blocco sul proprio asse temporale.
- poll() bool#
Metodo dell’interfaccia del backend. Restituisce
Truequando ci sono dati serializzati disponibili per l’host.
- size() int#
Metodo dell’interfaccia del backend. Invoca
on_read(se impostato) e restituisce la dimensione del buffer serializzato.
Costanti#
Bit di flag del canale (combinati bit a bit; passati a protocol.register tramite flags o impostati automaticamente in base ai metodi del backend).
- protocol.CHANNEL_FLAG_PHYSICAL: int#
Il canale rappresenta un trasporto fisico (in contrapposizione a un canale di dati logico).
Identificatori dei canali integrati.