protocol — ערוצי פרוטוקול OpenMV#

המודול protocol חושף את פרוטוקול המארח של OpenMV ל-Python. הוא מאפשר לאתחל ולהגדיר את מחסנית הפרוטוקול בצד הקושחה, ומאפשר לקוד המשתמש לרשום ערוצים לוגיים מותאמים אישית הנתמכים על ידי אובייקט Python המממש את ממשק הערוץ (read, write, size, poll וכו«). זה מה שכלי הלוויין השולחניים מדברים אליו כאשר הם מזרימים נתוני תמונה או חושפים רכיבים אינטראקטיביים למצלמה מחוברת.

דוגמאות#

הזרמת תמונת RGB565 לכלי מארח באמצעות backend מותאם אישית המממש את ממשק הערוץ הגולמי (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

הסקריפט התואם בצד המארח, המשתמש בחבילת ה-Python openmv (pip install openmv) כדי להתחבר, לדחוף את הסקריפט שעל המצלמה ולמשוך כל פריים:

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

החלף את /dev/ttyACM0 ביציאה הטורית של המצלמה (למשל COM3 ב-Windows). הבנאי openmv.camera.Camera מקבל את אותם פרמטרי פרוטוקול כמו init (crc / seq / ack / events / max_payload / max_retry / timeout) כאשר המחסנית בצד המצלמה הוגדרה מחדש כדי להתאים.

פונקציות#

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#

אתחול (או הגדרה מחדש) של מחסנית הפרוטוקול ורישום ערוצי הנתונים הלוגיים המוגדרים כברירת מחדל (stdin, stdout, stream ואם הודר, profile). מעלה RuntimeError אם האתחול נכשל. הקושחה עולה עם מחסנית פרוטוקול USB ברירת מחדל שכבר פועלת, ולכן קריאה לכך נדרשת רק כדי לשנות את התעבורה או לדרוס את פרמטרי המסגור (framing) ברירת המחדל.

crc מפעיל אימות CRC על מסגרות הפרוטוקול.

seq מפעיל מעקב אחר מספרי רצף.

ack מפעיל אישורי קבלה לכל מסגרת.

events מפעיל התראות אירועי ערוץ.

max_payload הוא גודל ה-payload המרבי בבתים. אם מושמט, נעשה שימוש בברירת המחדל לכל מצלמה שלהלן; היא נגזרת מגודל חוצץ (buffer) הפרוטוקול של כל לוח כ-buffer - 10 (header) - 4 (CRC).

מצלמה

גודל חוצץ (buffer)

Payload מרבי

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 הוא מספר ניסיונות השידור החוזר. ברירת מחדל 3.

rtx_timeout_ms הוא פסק הזמן של השידור החוזר במילישניות (מוכפל לאחר כל פסק זמן). ברירת מחדל 500.

lock_interval_ms הוא מרווח הנעילה המינימלי במילישניות. ברירת מחדל 10.

poll_ms הוא מרווח התשאול במילישניות. 0 (ברירת המחדל) משבית תשאול מבוסס טיימר.

protocol.is_active() bool#

מחזיר True אם מארח מחובר כעת ומחסנית הפרוטוקול פעילה, אחרת False.

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

רישום אובייקט backend של Python כערוץ לוגי חדש והחזרת ידית ProtocolChannel. השיטות הזמינות של אובייקט ה-backend (ראו ממשק ה-Backend להלן) קובעות את יכולות הערוץ; protocol.CHANNEL_FLAG_READ, protocol.CHANNEL_FLAG_WRITE ו-protocol.CHANNEL_FLAG_LOCK מתווספים ל-flags באופן אוטומטי כאשר השיטות המתאימות ממומשות.

name הוא שם הערוץ כמחרוזת. נחתך לגודל חוצץ שם-הערוץ של הקושחה. חובה.

backend הוא אובייקט ה-Python המממש את ממשק ה-backend. חובה. בדרך כלל מועבר באמצעות מילת מפתח (backend=...).

flags הם ביטי דגל ערוץ נוספים (ראו את הקבועים CHANNEL_FLAG_*). אופציונלי; ברירת המחדל היא 0.

מעלה RuntimeError אם לא ניתן לרשום את הערוץ (למשל אין משבצות ערוץ פנויות).

מחלקות#

class protocol.ProtocolChannel#

ידית המוחזרת על ידי protocol.register. מופעים אינם נבנים ישירות.

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

שליחת התראת אירוע ערוץ למארח.

event הוא מזהה האירוע (מספר שלם).

wait_ack אם True חוסם עד שהמארח מאשר את האירוע.

מעלה RuntimeError אם שליחת האירוע נכשלת.

ממשק ה-Backend#

אובייקט backend המועבר ל-protocol.register עשוי לממש כל תת-קבוצה של השיטות הבאות. רק השיטות הקיימות באובייקט מחוברות לשכבת פרוטוקול ה-C; שיטות חסרות משאירות את היכולת המתאימה מושבתת.

class protocol.backend#

אובייקט backend של ערוץ המועבר ל-protocol.register. השיטות שלהלן מתארות את הממשק האופציונלי ש-backend של Python עשוי לממש.

init() object#

נקראת פעם אחת כאשר הערוץ מאותחל. החזר כל ערך שאינו None בהצלחה; חריגה או היעדר ערך מוחזר מטופלים כשגיאה.

poll() bool#

מחזיר True אם בערוץ יש נתונים מוכנים לקריאה על ידי המארח.

lock() bool#

השגת הערוץ עבור העברה. מחזיר True בהצלחה.

unlock() bool#

שחרור הערוץ לאחר העברה. מחזיר True בהצלחה.

size() int#

מחזיר את מספר הבתים הניתנים כעת לקריאה מהערוץ.

shape() tuple#

מחזיר tuple של עד ארבעה מספרים שלמים המתארים את צורת הנתונים (למשל ממדי תמונה). עד ארבעה איברים נצרכים על ידי שכבת הפרוטוקול.

flush() object#

ריקון כל הנתונים הממתינים. החזר כל ערך שאינו None בהצלחה.

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

מחזיר עד size בתים החל מ-offset כאובייקט דמוי bytes התומך בפרוטוקול ה-buffer.

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

וריאנט zero-copy של read. מחזיר חוצץ (buffer) שהזיכרון הבסיסי שלו נקרא ישירות על ידי שכבת הפרוטוקול; החוצץ חייב להישאר תקף למשך כל ההעברה.

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

כתיבת data ב-offset. data הוא bytearray המפנה ישירות לחוצץ ה-C. מחזיר את מספר הבתים שנכתבו, או 0 בהצלחה ברירת מחדל.

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

טיפול ב-ioctl. arg הוא None אם length הוא אפס, אחרת bytearray המפנה לחוצץ ה-C. מחזיר 0 או None בהצלחה, או מספר שלם שלילי בשגיאה.

is_active() bool#

עבור ערוצי תעבורה, מחזיר True אם התעבורה הבסיסית מחוברת כעת.

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

backend ברמה גבוהה יותר של Python (מסופק על ידי חבילת protocol הקפואה) המבצע סריאליזציה של שדות בעלי שם ל-CBOR באמצעות מפתחות שלמים תואמי SenML. תומך ברכיבי תצוגה (label, depth) ובבקרות אינטראקטיביות (toggle, slider, select) עם פונקציות callback on_read/on_write.

on_read הוא callable אופציונלי on_read(channel) הנקרא לפני שהערוץ עובר סריאליזציה עבור המארח. השתמש בו כדי לרענן ערכי שדות.

on_write הוא callable אופציונלי on_write(channel, name, value) הנקרא כאשר המארח כותב ערך חדש עבור שדה בעל שם.

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#

הוספת שדה בעל שם לערוץ.

name הוא שם התצוגה; חייב להיות ייחודי בתוך ערוץ זה.

type הוא סוג הרכיב: "label", "toggle", "slider", "select" או "depth".

value הוא הערך ההתחלתי. ברירת המחדל תלויה ב-type.

unit הוא מחרוזת היחידה עבור label/slider (למשל "Cel", "%RH").

min הוא הערך המינימלי (טווח slider או טווח depth).

max הוא הערך המרבי (טווח slider או טווח depth).

step הוא גודל הצעד (slider).

options היא רשימת מחרוזות האפשרויות (select).

width הוא הרוחב בפיקסלים (depth).

height הוא הגובה בפיקסלים (depth).

__getitem__(name: str) object#

מחזיר את הערך הנוכחי של השדה בעל השם. עבור שדות depth מוחזר חוצץ הנתונים הבינארי, אחרת הערך הסקלרי.

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

הגדרת הערך של השדה בעל השם. עבור שדות slider, tuple מסוג (min, max, value) מעדכן את הטווח ואת הערך הנוכחי בו-זמנית. עבור שדות depth, value הוא חוצץ הנתונים הבינארי.

poll() bool#

שיטת ממשק backend. מחזירה True כאשר נתונים מסוריאליזים זמינים עבור המארח.

size() int#

שיטת ממשק backend. מפעילה את on_read (אם הוגדר) ומחזירה את גודל החוצץ המסוריאליז.

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

שיטת ממשק backend. מחזירה פלח של החוצץ המסוריאליז.

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

שיטת ממשק backend. מפענחת רשימת עדכוני CBOR ומחילה ערכים על שדות בעלי שם תואמים, תוך הפעלת on_write עבור כל אחד.

קבועים#

ביטי דגל ערוץ (משולבים בפעולת bitwise; מועברים ל-protocol.register באמצעות flags או נקבעים אוטומטית על סמך השיטות של ה-backend).

protocol.CHANNEL_FLAG_READ: int#

הערוץ תומך בקריאות.

protocol.CHANNEL_FLAG_WRITE: int#

הערוץ תומך בכתיבות.

protocol.CHANNEL_FLAG_LOCK: int#

הערוץ מממש lock/unlock.

protocol.CHANNEL_FLAG_PHYSICAL: int#

הערוץ מייצג תעבורה פיזית (בניגוד לערוץ נתונים לוגי).

מזהי ערוץ מובנים.

protocol.CHANNEL_ID_TRANSPORT: int#

מזהה ערוץ שמור עבור התעבורה הפעילה.

protocol.CHANNEL_ID_STDIN: int#

מזהה הערוץ של הערוץ stdin המובנה.

protocol.CHANNEL_ID_STDOUT: int#

מזהה הערוץ של הערוץ stdout המובנה.

protocol.CHANNEL_ID_STREAM: int#

מזהה הערוץ של הערוץ stream המובנה.

protocol.CHANNEL_ID_PROFILE: int#

מזהה הערוץ של ערוץ ה-profiler המובנה (קיים רק כאשר הקושחה נבנית עם ה-profiler מופעל).