protocol — OpenMV protokollcsatornák#

A protocol modul az OpenMV gazdaprotokollt teszi elérhetővé a Python számára. Lehetővé teszi a firmware-oldali protokollverem inicializálását és konfigurálását, és lehetővé teszi a felhasználói kód számára, hogy egyéni logikai csatornákat regisztráljon, amelyek mögött egy a csatornainterfészt megvalósító Python-objektum áll (read, write, size, poll stb.). Ezzel kommunikálnak az asztali kísérőeszközök, amikor képadatot streamelnek vagy interaktív widgeteket jelenítenek meg egy csatlakoztatott kamerán.

Példák#

RGB565 kép streamelése egy gazdaeszköz felé egyéni háttérprogrammal, amely megvalósítja a nyers csatornainterfészt (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

A hozzá tartozó gazdaoldali szkript, amely az openmv Python-csomagot (pip install openmv) használja a csatlakozáshoz, a kamerán futó szkript feltöltéséhez és az egyes képkockák lekéréséhez:

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()

Cseréld le a /dev/ttyACM0 értéket a kamera soros portjára (pl. COM3 Windows alatt). Az openmv.camera.Camera konstruktor ugyanazokat a protokollparamétereket fogadja el, mint az init (crc / seq / ack / events / max_payload / max_retry / timeout), ha a kameraoldali vermet ehhez illeszkedően újrakonfigurálták.

Függvények#

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#

Inicializálja (vagy újrakonfigurálja) a protokollvermet, és regisztrálja az alapértelmezett logikai adatcsatornákat (stdin, stdout, stream és, ha be van fordítva, profile). RuntimeError hibát vált ki, ha az inicializálás sikertelen. A firmware indulásakor már fut egy alapértelmezett USB protokollverem, így ezt csak akkor kell meghívni, ha meg szeretné változtatni a transzportot vagy felül szeretné bírálni az alapértelmezett keretezési paramétereket. Az ismételt meghívás újrainicializálja a vermet: az előző lekérdezési időzítő eltávolításra kerül, és a csatornák újra regisztrálódnak.

A crc engedélyezi a CRC-ellenőrzést a protokollkereteken.

A seq engedélyezi a sorszámkövetést.

Az ack engedélyezi a keretenkénti nyugtázásokat.

Az events engedélyezi a csatorna-eseményértesítéseket.

A max_payload a maximális hasznosadat-méret bájtban. Ha elhagyod, az alábbi, kameránkénti alapértelmezés érvényesül; ez az egyes lapok protokollpuffer-méretéből származik a buffer - 10 (header) - 4 (CRC) képlet szerint.

Kamera

Pufferméret

Maximális hasznos adat

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

Az rtx_retries az újraküldési kísérletek száma. Alapértelmezés: 3.

Az rtx_timeout_ms az újraküldési időtúllépés ezredmásodpercben (minden időtúllépés után megduplázódik). Alapértelmezés: 500.

A lock_interval_ms a minimális zárolási időköz ezredmásodpercben. Alapértelmezés: 10.

A poll_ms a lekérdezési időköz ezredmásodpercben. A 0 (az alapértelmezés) letiltja az időzítő alapú lekérdezést.

protocol.is_active() → bool#

True értéket ad vissza, ha jelenleg csatlakozik egy gazda, és a protokollverem aktív, egyébként False értéket.

protocol.poll() → int#

Lefuttatja a protokollfeladat egy iterációját: kiüríti a transzport vételi pufferét, feldolgozza a beérkezett teljes kereteket, továbbítja a parancsokat és kiszolgálja a csatornaeseményeket. Ezt a firmware normál esetben elvégzi Ön helyett az USB-megszakításból és a poll_ms időzítőből, így csak olyan egyéni transzportokhoz szükséges, amelyeket egyik sem hajt meg – például amikor poll_ms=0, és a transzportot a szkript fő ciklusa kérdezi le.

Siker esetén 0-t ad vissza, -1-et pedig akkor, ha nincs aktív transzport regisztrálva, vagy a hívás megszakítási kontextusból történt.

protocol.register(name: str, *, backend: object, flags: int = 0) → ProtocolChannel#

Regisztrál egy Python backend objektumot új logikai csatornaként, és visszaad egy ProtocolChannel leírót. A backend objektum elérhető metódusai (lásd alább: Háttérprogram-interfész) határozzák meg a csatorna képességeit; a protocol.CHANNEL_FLAG_READ, a protocol.CHANNEL_FLAG_WRITE és a protocol.CHANNEL_FLAG_LOCK automatikusan hozzáadódik a flags értékhez, amikor a megfelelő metódusok meg vannak valósítva.

A name a csatorna neve karakterláncként. A firmware csatornanév-pufferének méretére csonkolódik. Kötelező.

A backend a háttérprogram-interfészt megvalósító Python-objektum. Kötelező. Jellemzően kulcsszóval adják át (backend=...).

A flags további csatorna-jelzőbitek (lásd a CHANNEL_FLAG_* konstansokat). Opcionális; alapértelmezése 0.

RuntimeError kivételt vált ki, ha a csatorna nem regisztrálható (pl. nincs szabad csatornahely).

Osztályok#

class protocol.ProtocolChannel#

A protocol.register által visszaadott leíró. A példányok nem hozhatók létre közvetlenül.

send_event(event: int, wait_ack: bool = False) → None#

Csatorna-eseményértesítés küldése a gazdának.

Az event az esemény azonosítója (egész szám).

A wait_ack ha True, blokkol, amíg a gazda nyugtázza az eseményt.

RuntimeError kivételt vált ki, ha az esemény küldése meghiúsul.

Háttérprogram-interfész#

A protocol.register függvénynek átadott háttérprogram-objektum az alábbi metódusok bármely részhalmazát megvalósíthatja. Csak az objektumon ténylegesen jelen lévő metódusok kapcsolódnak a C protokollréteghez; a hiányzó metódusok letiltják a megfelelő képességet.

class protocol.backend#

A protocol.register függvénynek átadott csatorna-háttérprogram objektum. Az alábbi metódusok azt az opcionális interfészt írják le, amelyet egy Python-háttérprogram megvalósíthat.

init() → object#

Egyszer hívódik meg, amikor a csatorna inicializálódik. Sikeresség esetén bármely nem None értéket adjon vissza; a kivétel vagy a hiányzó visszatérési érték hibának számít.

poll() → bool#

True értéket ad vissza, ha a csatornán van olvasásra kész adat a gazda számára.

lock() → bool#

Lefoglalja a csatornát egy átvitelhez. Sikeresség esetén True értéket ad vissza.

unlock() → bool#

Felszabadítja a csatornát egy átvitel után. Sikeresség esetén True értéket ad vissza.

size() → int#

Visszaadja a csatornáról jelenleg olvasható bájtok számát.

shape() → tuple#

Visszaad egy legfeljebb négy egész számból álló rekordot, amely az adat alakját írja le (pl. képméreteket). A protokollréteg legfeljebb négy elemet használ fel.

flush() → object#

Kiüríti az összes függőben lévő adatot. Sikeresség esetén bármely nem None értéket adjon vissza.

read(offset: int, size: int) → bytes#

Legfeljebb size bájtot ad vissza az offset pozíciótól kezdve, egy bytes-szerű objektumként, amely támogatja a pufferprotokollt.

readp(offset: int, size: int) → bytes#

A read nullamásolásos (zero-copy) változata. Olyan puffert ad vissza, amelynek mögöttes memóriáját közvetlenül a protokollréteg olvassa; a puffernek érvényesnek kell maradnia az átvitel teljes idejére.

write(offset: int, data: bytearray) → int#

data írása az offset pozícióba. A data egy bytearray, amely közvetlenül a C puffert hivatkozza. Visszaadja a kiírt bájtok számát, vagy 0 értéket alapértelmezett siker esetén.

ioctl(cmd: int, length: int, arg: bytearray | None) → int#

Kezel egy ioctl-t. Az arg None, ha a length nulla, egyébként egy bytearray, amely a C puffert hivatkozza. Sikeresség esetén 0 vagy None értéket ad vissza, hiba esetén pedig negatív egész számot.

is_active() → bool#

Transzportcsatornák esetén True értéket ad vissza, ha a mögöttes transzport jelenleg csatlakozik.

class protocol.CBORChannel(on_read: Callable | None = None, on_write: Callable | None = None)#

Magasabb szintű Python háttérrendszer (a befagyasztott protocol csomag biztosítja), amely a nevesített mezőket SenML-kompatibilis egész kulcsokkal CBOR formátumba sorosítja. Támogatja a megjelenítő widgeteket (label, text, depth, waveform) és az interaktív vezérlőket (toggle, pushbutton, slider, spinbox, select, radio, lineedit) on_read/on_write visszahívásokkal.

Az on_read egy opcionális on_read(channel) hívható objektum, amely a csatorna gazda felé történő sorosítása előtt hívódik meg. Használd a mezőértékek frissítésére.

Az on_write egy opcionális on_write(channel, name, value) hívható objektum, amely akkor hívódik meg, amikor a gazda új értéket ír egy megnevezett mezőbe.

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#

Megnevezett mező hozzáadása a csatornához.

A name a megjelenítendő név; ezen a csatornán belül egyedinek kell lennie.

A type a widget típusa:

  • "label" – csak olvasható numerikus kijelző opcionális unit mértékegységgel.

  • "text" – csak olvasható karakterlánc-kijelző.

  • "toggle" – logikai kapcsoló.

  • "pushbutton" – pillanatnyi művelet (indítás, kalibrálás, …). A host kattintáskor True értéket ír, és nem jelenít meg állapotot.

  • "slider" – numerikus vezérlő min/max/step értékekkel.

  • "spinbox" – pontos, lépésköz szerinti numerikus bevitel; a slider mezőit és a (min, max, value) tuple-es frissítési formát használja.

  • "select" – legördülő lista az options karakterláncokból.

  • "radio" – az options választógomb-csoportként megjelenítve.

  • "lineedit" – írható karakterlánc-bevitel.

  • "depth" – width x height méretű 2D mélységtérkép-kijelző.

  • "waveform" – átlapolt mintákból álló 1D idősor-diagram.

A value a kezdőérték. Az alapértelmezés a type értékétől függ: 0 a label, "" a text/lineedit/select/ radio, False a toggle/pushbutton, min a slider/spinbox esetén.

A unit a mértékegység karakterlánca a label/slider/spinbox/ waveform esetén (pl. "Cel", "%RH").

A min a minimális érték (slider/spinbox tartomány, illetve depth/waveform megjelenítési tartomány).

A max a maximális érték (slider/spinbox tartomány, illetve depth/waveform megjelenítési tartomány).

A step a lépésköz (slider/spinbox).

Az options az opciók karakterláncainak listája (select/radio), vagy a sorozatonkénti nevek listája (waveform).

A width a képpontban megadott szélesség (depth).

A height a képpontban megadott magasság (depth).

A sample_rate a mintavételi frekvencia másodpercenkénti mintában (waveform). A host felé mintavételi periódusként kerül elküldésre.

A series az egyes mintablokkokban átlapolt sorozatok száma (waveform), pl. 3 egy XYZ gyorsulásmérő-görbe esetén.

A typecode a waveform minták array típuskódja (pl. "H", "h", "f"). Alapértelmezése "H" (előjel nélküli 16 bites).

__getitem__(name: str) → object#

Visszaadja a megnevezett mező aktuális értékét. A depth és waveform mezők esetén a bináris adatpuffer, egyébként a skalár érték kerül visszaadásra.

__setitem__(name: str, value: Any) → None#

Beállítja a megnevezett mező értékét. A slider és spinbox mezők esetén egy (min, max, value) tuple egyszerre frissíti a tartományt és az aktuális értéket. A depth mezők esetén a value a bináris adatpuffer. A waveform mezők esetén a value egy bájtszerű blokk, amely a mező typecode típuskódja szerinti átlapolt mintákat tartalmaz; a sorozatonkénti mintaszám a hosszából kerül kiszámításra, és egy monoton időbélyeg (mikroszekundumban, a time.ticks_us() alapján halmozva) kapcsolódik hozzá, hogy a host el tudja helyezni a darabot az időtengelyén.

poll() → bool#

Háttérprogram-interfész metódus. True értéket ad vissza, ha sorosított adat áll rendelkezésre a gazda számára.

size() → int#

Háttérprogram-interfész metódus. Meghívja az on_read függvényt (ha be van állítva), és visszaadja a sorosított puffer méretét.

read(offset: int, size: int) → bytes#

Háttérprogram-interfész metódus. A sorosított puffer egy szeletét adja vissza.

write(offset: int, data: bytearray) → int#

Háttérprogram-interfész metódus. Dekódol egy CBOR frissítési listát, és az értékeket az illeszkedő megnevezett mezőkre alkalmazza, mindegyikhez meghívva az on_write függvényt.

Konstansok#

Csatorna-jelzőbitek (bitenkénti VAGY művelettel kombinálva; a protocol.register függvénynek a flags paraméteren keresztül átadva, vagy a háttérprogram metódusai alapján automatikusan beállítva).

protocol.CHANNEL_FLAG_READ: int#

A csatorna támogatja az olvasást.

protocol.CHANNEL_FLAG_WRITE: int#

A csatorna támogatja az írást.

protocol.CHANNEL_FLAG_LOCK: int#

A csatorna megvalósítja a lock/unlock műveleteket.

protocol.CHANNEL_FLAG_PHYSICAL: int#

A csatorna fizikai transzportot képvisel (szemben a logikai adatcsatornával).

Beépített csatornaazonosítók.

protocol.CHANNEL_ID_TRANSPORT: int#

Az aktív transzporthoz fenntartott csatornaazonosító.

protocol.CHANNEL_ID_STDIN: int#

A beépített stdin csatorna azonosítója.

protocol.CHANNEL_ID_STDOUT: int#

A beépített stdout csatorna azonosítója.

protocol.CHANNEL_ID_STREAM: int#

A beépített stream csatorna azonosítója.

protocol.CHANNEL_ID_PROFILE: int#

A beépített profilozó csatorna azonosítója (csak akkor van jelen, ha a firmware engedélyezett profilozóval készült).