13.1.9. תצוגת Channels#

חוצץ הפריימים (frame buffer) מראה את מה שהמצלמה רואה; תצוגת Channels מראה את מה שהסקריפט מודד ומחליט. סקריפט מפרסם ערכים בעלי שם דרך פרוטוקול הניפוי – טמפרטורה, ספירת זיהויים, צורת גל של מיקרופון, מפת עומק זמן-מעוף (time-of-flight), או פקד שהוא רוצה שהמשתמש יוכל לשנות – וה-IDE מציג כל אחד מהם בזמן אמת כווידג’ט, גרף או מפה בחלונית שמתחת לחוצץ הפריימים. שום דבר לא צריך להיות מצויר על התמונה או מפוענח מתוך הטרמינל. בצד המצלמה אלה כמה שורות עם protocol.CBORChannel; בצד ה-IDE זו הרשומה Channels של בורר החלונית.

The Channels view showing a thermal camera script's controls channel: FPA and AUX temperature readouts with their unit, Measurement and High Temp toggles, Temp Min and Temp Max sliders, and H-Mirror and V-Flip toggles

הפקדים של סקריפט מצלמה תרמית: שתי קריאות טמפרטורה, מתגים למצבי המדידה, ומחוונים לטווח הטמפרטורה. שינוי כל אחד מהם שולח את הערך החדש לסקריפט.#

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

13.1.9.1. ווידג’טים#

קריאות ופקדים לובשים את הצורות המתבקשות. label הוא ערך לקריאה בלבד עם היחידה שלו; רשומת text היא גוש של טקסט עשיר סטטי לכותרות ולהסברים. הסוגים הניתנים לכתיבה שולחים את ערכם החדש בחזרה לסקריפט ברגע שאתה משנה אותם, ושם ה-callback on_write של הסקריפט מקבל את שם הרשומה והערך: toggle הוא מתג, slider ו-spinbox קובעים מספר בתוך הטווח והצעד של הסקריפט (תיבת הספין להזנה מדויקת), לחצני radio ו-select בוחרים אחת מאפשרויות הסקריפט, lineedit הוא שדה טקסט חופשי, ו-pushbutton מפעיל פעולה רגעית. התצוגה משאירה את העריכה שלך על המסך במקום להחזיר אותה לערך האחרון שהמצלמה פרסמה בזמן שהכתיבה בדרך.

בצד הסקריפט כל הסט הוא protocol.CBORChannel עם add אחד לכל ווידג’ט ו-callback לכתיבות:

import time
import protocol
from protocol import CBORChannel

def on_write(ch, name, value):
    print(name, "=", value)      # every change lands here

ch = CBORChannel(on_write=on_write)
ch.add("Status", type="label", value="starting")
ch.add("Enable", type="toggle", value=True)
ch.add("Threshold", type="slider", min=0, max=100,
       step=1, value=50, unit="%")
ch.add("Mode", type="select",
       options=["Idle", "Track", "Record"], value="Idle")
ch.add("Calibrate", type="pushbutton")
protocol.register(name="controls", backend=ch)

while True:
    if ch["Enable"]:                   # read a control back
        ch["Status"] = "threshold %d%%" % ch["Threshold"]
    time.sleep_ms(100)

הרץ אותו והחלף את החלונית ל-Channels: מופיע מקטע בשם controls עם חמשת הווידג’טים, והזזת המחוון מדפיסה בטרמינל. סוגי הווידג’טים האחרים – spinbox, radio, lineedit, text – הם add אחד נוסף כל אחד; פרק פרוטוקול המארח עובר על כולם.

13.1.9.2. גרפי צורת גל#

רשומת waveform היא בלוק של דגימות משולבות (interleaved) עם קצב דגימה – שמע, צירי מד-תאוצה, עקבת ADC – והתצוגה מציירת אותה כגרף נגלל, סדרה אחת לכל ציר, בשמות שהסקריפט נתן. פקדי הגרף נמצאים בסרגל הכלים שלו ועל התרשים עצמו:

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

  • Auto Scale מתאים את הציר האנכי לנתונים; כשהוא כבוי, הציר הוא הטווח שהסקריפט הצהיר עליו.

  • לחץ לחיצה ימנית על התרשים כדי להניח marker ולקרוא את הזמן והערך המדויקים של דגימה, או כדי לקבוע trigger level. כאשר Trigger דרוך הגרף עומד במקומו עד שהאות חוצה את הרמה בקצה עולה או יורד – דרכו של האוסצילוסקופ לתפוס מעבר חולף – ו-Re-arm ממתין לבא.

  • Spectrum מחליף את הגרף לתרשים תדר של אותם נתונים, עם בחירת חלון (Hann, Hamming, Blackman, מלבני), Averaging ו-Peak Hold על פני פריימים, וציר Log Frequency. הקריאה מתחת לתרשים נוקבת בתדר השיא וברמתו.

  • Pause מקפיא את הגרף מבלי להשפיע על הקלטה; Stats מוסיף שורת מינימום / מקסימום / RMS; ותפריט הלחיצה הימנית שומר את התרשים כתמונה.

The Channels view with two waveform records scrolling live: a 16 kHz microphone trace and a three-series accelerometer trace with its X, Y, and Z legend, each with its Record, Spectrum, Pause, and Stats buttons

שתי רשומות צורת גל נגללות בזמן אמת: עקבת מיקרופון ועקבת IMU בת שלוש סדרות.#

שדה צורת גל מקבל את קצב הדגימה, ולכמה אותות בבת אחת, את מספר ה-series המשולבות ואת שמותיהן; כל עדכון הוא הבייטים הגולמיים של array של דגימות ב-typecode של השדה:

import math
import time
import protocol
from array import array
from protocol import CBORChannel

RATE, CHUNK = 1000, 100            # 1 kHz, 100 ms per update

ch = CBORChannel()
ch.add("IMU", type="waveform", sample_rate=RATE, series=3,
       typecode="f", min=-2.0, max=2.0,
       options=["X", "Y", "Z"], unit="g")
protocol.register(name="signals", backend=ch)

t = 0
while True:
    block = array("f")
    for i in range(CHUNK):
        phase = 2 * math.pi * 2 * (t + i) / RATE
        block.extend((math.sin(phase), math.cos(phase), 1.0))
    ch["IMU"] = bytes(block)        # interleaved X, Y, Z
    t += CHUNK
    time.sleep_ms(100)

13.1.9.3. מפות עומק#

רשומת depth היא רשת של מרחקים – הפריימים שחיישן זמן-מעוף (time-of-flight) מפיק – והתצוגה מציירת אותה כמפת צבע-מדומה המוגדלת לגובה הגרף. סרגל הכלים בוחר את הרינדור: ה-palette (פלטות Depth, Rainbow ו-Ironbow של המצלמה עצמה, בתוספת פלטות בהירה וכהה למצלמות אירועים), ה-scaling filter שמגדיל את רשת החיישן הקטנה (bicubic למשטח חלק, bilinear, או nearest לבלוק אחד לכל פיקסל חיישן), וה-tone mapping שהופך מרחק לצבע: Script Range משתמש בטווח שהסקריפט הצהיר עליו, Auto Range מותח את הצבעים על פני המרחקים הקיימים בפועל בכל פריים (תוך התעלמות מהאחוזים הבודדים החריגים), ו-Equalize פורש אותם על פני התפלגות הפריים כך שסצנה שטוחה עדיין מראה את פרטיה. הכותרת מדווחת על גודל המפה, הטווח המוצהר, והמינימום, המקסימום והממוצע של הפריים; ריחוף מעל המפה קורא את המרחק שמתחת לסמן, ו-Save Image כותב את המפה המרונדרת ל-PNG.

The Channels view showing an 8 x 8 time-of-flight depth map scaled up smoothly with the Depth palette, the palette, scaling, and tone-mapping selectors in its toolbar, and the header reporting the size, declared range, and the frame's minimum, maximum, and mean

מפת עומק זמן-מעוף (time-of-flight) של יד מול החיישן, עם בוררי הפלטה, שינוי קנה המידה ומיפוי הגוונים בסרגל הכלים.#

שדה עומק מצהיר על גודל הרשת והטווח שלו; כל עדכון הוא הרשת כ-floats ארוזים בני 32 סיביות, שזה מה שהקורא של מודול tof מפיק:

import struct
import time
import tof
import protocol
from protocol import CBORChannel

tof.init()
ch = CBORChannel()
ch.add("depth", type="depth", width=tof.width(),
       height=tof.height(), min=0, max=1000)
protocol.register(name="ToF", backend=ch)

while True:
    try:
        d, dmin, dmax = tof.read_depth(vflip=True, hmirror=True)
    except RuntimeError:
        continue
    ch["depth"] = struct.pack("<%df" % len(d), *d)
    time.sleep_ms(50)

13.1.9.4. הקלטה#

לכל גרף ומפה יש לחצן Record, ולערוץ עם יותר מאחד מהם יש פס Record All שמפעיל את כולם יחד. ההקלטה כותבת את הנתונים עם הגעתם – לא את הרינדור – לקובץ שהפורמט שלו נקבע לפי הסיומת שאתה בוחר בחלון השמירה: CSV לגיליונות אלקטרוניים ולכלי ציור, WAV לצורות גל של שמע חד-ערוצי, מערכי NumPy .npy ל-Python, ו-Edge Impulse JSON או CBOR להעלאה ישירה לפרויקט Edge Impulse כנתוני אימון, עם תווית אופציונלית שנותנת שם לקבצים. מוצעים רק הפורמטים שיכולים להכיל את נתוני הרשומה; מפת עומק, למשל, אינה יכולה להיות קובץ WAV. לכידה ארוכה מפוצלת לחלקים ממוספרים כך שאף קובץ בודד אינו גדל יתר על המידה, והקלטה שורדת רישום מחדש של הערוצים על ידי הסקריפט. הקריאה לצד הלחצן עוקבת אחר מספר הדגימות שנכתבו וגודל הקובץ.

מרווח ה-Channel Polling של החלונית, במקטע הפרוטוקול של ההעדפות, קובע באיזו תדירות ה-IDE מבקש מהמצלמה נתוני ערוצים חדשים; ברירת המחדל מתאימה לסקריפטים שמפרסמים כמה פעמים בשנייה, וסקריפט שמזרים שמע נהנה ממרווח קצר יותר.

ראה גם

protocol.CBORChannel עבור צד הסקריפט – סוגי הרשומות, הארגומנטים שלהם וה-callback on_write – והדוגמאות 12-Protocol תחת File → Examples לסקריפטים עובדים שמפרסמים קריאות, פקדים ומפת עומק.