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.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. מופעים אינם נבנים ישירות.
ממשק ה-Backend¶
אובייקט backend המועבר ל-protocol.register עשוי לממש כל תת-קבוצה של השיטות הבאות. רק השיטות הקיימות באובייקט מחוברות לשכבת פרוטוקול ה-C; שיטות חסרות משאירות את היכולת המתאימה מושבתת.
- class protocol.backend¶
אובייקט backend של ערוץ המועבר ל-
protocol.register. השיטות שלהלן מתארות את הממשק האופציונלי ש-backend של Python עשוי לממש.- init() object¶
נקראת פעם אחת כאשר הערוץ מאותחל. החזר כל ערך שאינו
Noneבהצלחה; חריגה או היעדר ערך מוחזר מטופלים כשגיאה.
- shape() tuple¶
מחזיר tuple של עד ארבעה מספרים שלמים המתארים את צורת הנתונים (למשל ממדי תמונה). עד ארבעה איברים נצרכים על ידי שכבת הפרוטוקול.
- 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בהצלחה ברירת מחדל.
- class protocol.CBORChannel(on_read: Callable | None = None, on_write: Callable | None = None)¶
backend ברמה גבוהה יותר של Python (מסופק על ידי חבילת
protocolהקפואה) המבצע סריאליזציה של שדות בעלי שם ל-CBOR באמצעות מפתחות שלמים תואמי SenML. תומך ברכיבי תצוגה (label,depth) ובבקרות אינטראקטיביות (toggle,slider,select) עם פונקציות callbackon_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מוחזר חוצץ הנתונים הבינארי, אחרת הערך הסקלרי.
קבועים¶
ביטי דגל ערוץ (משולבים בפעולת bitwise; מועברים ל-protocol.register באמצעות flags או נקבעים אוטומטית על סמך השיטות של ה-backend).
מזהי ערוץ מובנים.