protocol — Canales del protocolo OpenMV¶
El módulo protocol expone el protocolo de host de OpenMV a Python. Permite inicializar y configurar la pila de protocolo del lado del firmware, y deja que el código de usuario registre canales lógicos personalizados respaldados por un objeto Python que implementa la interfaz del canal (read, write, size, poll, etc.). Esto es con lo que se comunican las herramientas complementarias de escritorio cuando transmiten datos de imagen o exponen widgets interactivos a una cámara conectada.
Ejemplos¶
Transmite una imagen RGB565 a una herramienta de host usando un backend personalizado que implementa la interfaz del canal en bruto (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
El script del lado del host correspondiente, que usa el paquete Python openmv (pip install openmv) para conectarse, enviar el script de la cámara y recuperar cada fotograma:
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()
Reemplace /dev/ttyACM0 con el puerto serie de la cámara (por ejemplo COM3 en Windows). El constructor openmv.camera.Camera acepta los mismos parámetros de protocolo que init (crc / seq / ack / events / max_payload / max_retry / timeout) cuando la pila del lado de la cámara se ha reconfigurado para que coincida.
Funciones¶
- 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¶
Inicializa (o reconfigura) la pila de protocolo y registra los canales de datos lógicos predeterminados (
stdin,stdout,streamy, si está compilado,profile). LanzaRuntimeErrorsi la inicialización falla. El firmware arranca con una pila de protocolo USB predeterminada ya en ejecución, por lo que llamar a esto solo es necesario para cambiar el transporte o anular los parámetros de tramado predeterminados.crchabilita la validación CRC en las tramas del protocolo.seqhabilita el seguimiento de números de secuencia.ackhabilita los acuses de recibo por trama.eventshabilita las notificaciones de eventos de canal.max_payloades el tamaño máximo de la carga útil en bytes. Si se omite, se usa el valor predeterminado por cámara que se indica a continuación; se deriva del tamaño del búfer de protocolo de cada placa comobuffer - 10 (header) - 4 (CRC).Cámara
Tamaño del búfer
Carga útil máxima
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_retrieses el número de intentos de retransmisión. Predeterminado3.rtx_timeout_mses el tiempo de espera de retransmisión en milisegundos (se duplica tras cada tiempo de espera agotado). Predeterminado500.lock_interval_mses el intervalo mínimo de bloqueo en milisegundos. Predeterminado10.poll_mses el intervalo de sondeo en milisegundos.0(el valor predeterminado) deshabilita el sondeo por temporizador.
- protocol.is_active() bool¶
Devuelve
Truesi actualmente hay un host conectado y la pila de protocolo está activa; de lo contrario,False.
- protocol.register(name: str, *, backend: object, flags: int = 0) ProtocolChannel¶
Registra un objeto Python
backendcomo un nuevo canal lógico y devuelve un manejadorProtocolChannel. Los métodos disponibles del objetobackend(consulte Interfaz del backend más abajo) determinan las capacidades del canal;protocol.CHANNEL_FLAG_READ,protocol.CHANNEL_FLAG_WRITEyprotocol.CHANNEL_FLAG_LOCKse añaden aflagsautomáticamente cuando se implementan los métodos correspondientes.namees el nombre del canal como cadena. Se trunca al tamaño del búfer de nombres de canal del firmware. Obligatorio.backendes el objeto Python que implementa la interfaz del backend. Obligatorio. Normalmente se pasa por palabra clave (backend=...).flagsson bits de indicador de canal adicionales (consulte las constantesCHANNEL_FLAG_*). Opcional; el valor predeterminado es0.Lanza
RuntimeErrorsi el canal no se puede registrar (por ejemplo, no hay ranuras de canal libres).
Clases¶
- class protocol.ProtocolChannel¶
Manejador devuelto por
protocol.register. Las instancias no se construyen directamente.
Interfaz del backend¶
Un objeto backend pasado a protocol.register puede implementar cualquier subconjunto de los siguientes métodos. Solo los métodos presentes en el objeto se conectan a la capa de protocolo en C; los métodos ausentes dejan deshabilitada la capacidad correspondiente.
- class protocol.backend¶
Objeto backend de canal pasado a
protocol.register. Los métodos siguientes describen la interfaz opcional que puede implementar un backend Python.- init() object¶
Se llama una vez cuando se inicializa el canal. Devuelve cualquier valor distinto de
Noneen caso de éxito; una excepción o la ausencia de valor de retorno se tratan como un error.
- shape() tuple¶
Devuelve una tupla de hasta cuatro enteros que describen la forma de los datos (por ejemplo, las dimensiones de la imagen). La capa de protocolo consume hasta cuatro elementos.
- flush() object¶
Vacía cualquier dato pendiente. Devuelve cualquier valor distinto de
Noneen caso de éxito.
- read(offset: int, size: int) bytes¶
Devuelve hasta
sizebytes a partir deoffsetcomo un objeto similar abytesque admite el protocolo de búfer.
- readp(offset: int, size: int) bytes¶
Variante de
readsin copia (zero-copy). Devuelve un búfer cuya memoria subyacente lee directamente la capa de protocolo; el búfer debe permanecer válido durante toda la transferencia.
- write(offset: int, data: bytearray) int¶
Escribe
dataenoffset.dataes unbytearrayque referencia directamente el búfer de C. Devuelve el número de bytes escritos, o0en caso de éxito predeterminado.
- class protocol.CBORChannel(on_read: Callable | None = None, on_write: Callable | None = None)¶
Un backend Python de nivel superior (proporcionado por el paquete congelado
protocol) que serializa campos con nombre a CBOR usando claves enteras compatibles con SenML. Admite widgets de visualización (label,depth) y controles interactivos (toggle,slider,select) con funciones de retorno (callbacks)on_read/on_write.on_reades un invocable opcionalon_read(channel)que se invoca antes de serializar el canal para el host. Úselo para actualizar los valores de los campos.on_writees un invocable opcionalon_write(channel, name, value)que se invoca cuando el host escribe un nuevo valor para un campo con nombre.- 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) None¶
Añade un campo con nombre al canal.
namees el nombre para mostrar; debe ser único dentro de este canal.typees el tipo de widget:"label","toggle","slider","select"o"depth".valuees el valor inicial. El valor predeterminado depende detype.unites la cadena de unidad paralabel/slider(por ejemplo,"Cel","%RH").mines el valor mínimo (rango del slider o rango de profundidad).maxes el valor máximo (rango del slider o rango de profundidad).stepes el tamaño del paso (slider).optionses la lista de cadenas de opciones (select).widthes el ancho en píxeles (depth).heightes la altura en píxeles (depth).
- __getitem__(name: str) object¶
Devuelve el valor actual del campo con nombre. Para los campos
depthse devuelve el búfer de datos binarios; de lo contrario, el valor escalar.
- __setitem__(name: str, value: Any) None¶
Establece el valor del campo con nombre. Para los campos
slider, una tupla(min, max, value)actualiza el rango y el valor actual simultáneamente. Para los camposdepth,valuees el búfer de datos binarios.
- poll() bool¶
Método de la interfaz del backend. Devuelve
Truecuando hay datos serializados disponibles para el host.
- size() int¶
Método de la interfaz del backend. Invoca
on_read(si está definido) y devuelve el tamaño del búfer serializado.
Constantes¶
Bits de indicador de canal (combinados a nivel de bits; pasados a protocol.register mediante flags o establecidos automáticamente en función de los métodos del backend).
- protocol.CHANNEL_FLAG_PHYSICAL: int¶
El canal representa un transporte físico (en contraposición a un canal de datos lógico).
Identificadores de canal integrados.