13.1.9. عرض Channels#
يعرض مخزن الإطارات ما تراه الكاميرا؛ ويعرض عرض Channels ما يقيسه ويقرره البرنامج النصي. ينشر البرنامج النصي قيمًا مسمّاة عبر بروتوكول التصحيح -- درجة حرارة، أو عدد اكتشافات، أو شكل موجي لميكروفون، أو خريطة عمق بزمن الطيران، أو عنصر تحكم يريد أن يتمكن المستخدم من تغييره -- ويعرض الـ IDE كلًا منها مباشرةً كعنصر واجهة أو رسم بياني أو خريطة في الجزء أسفل مخزن الإطارات. لا شيء يحتاج إلى رسمه على الصورة أو تحليله من الطرفية. على جانب الكاميرا هي بضعة أسطر بـ protocol.CBORChannel؛ وعلى جانب الـ IDE هي مدخل Channels في مُحدِّد الجزء.

عناصر تحكم برنامج نصي لكاميرا حرارية: قراءتا درجة حرارة، ومفاتيح تبديل لأوضاع القياس، وأشرطة تمرير لنطاق درجة الحرارة. وتغيير أي منها يرسل القيمة الجديدة إلى البرنامج النصي.#
ينتظم العرض حسب القناة. تصبح كل قناة يسجّلها البرنامج النصي قسمًا عنوانه اسم القناة، ويصبح كل سجل في القناة صفًا تحته، بالترتيب الذي أضافها به البرنامج النصي. وعندما يسجّل البرنامج النصي قناة أو يغيّر شكل إحداها، يعيد العرض بناء نفسه؛ وعندما يتوقف البرنامج النصي، تبقى آخر القيم على الشاشة، مع تعطيل عناصر التحكم، حتى يبدأ البرنامج النصي التالي.
13.1.9.1. عناصر الواجهة#
تتخذ القراءات وعناصر التحكم الأشكال البديهية. label قيمة للقراءة فقط مع وحدتها؛ والسجل من نوع text كتلة من نص منسّق ثابت للعناوين والشروح. وترسل الأنواع القابلة للكتابة قيمتها الجديدة إلى البرنامج النصي لحظة تغييرك لها، حيث يستقبل رد النداء on_write في البرنامج النصي اسم السجل وقيمته: toggle مفتاح، وslider وspinbox يضبطان رقمًا ضمن نطاق البرنامج النصي وخطوته (مربع الزيادة والنقصان للإدخال الدقيق)، وأزرار radio وselect تختار أحد خيارات البرنامج النصي، وlineedit حقل نص حر، وpushbutton يطلق إجراءً لحظيًا. ويُبقي العرض تعديلك على الشاشة بدلًا من إرجاعه إلى آخر قيمة نشرتها الكاميرا أثناء تنفيذ الكتابة.
على جانب البرنامج النصي، المجموعة كلها هي protocol.CBORChannel واحدة مع add واحد لكل عنصر واجهة ورد نداء للكتابات:
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 هو كتلة من العينات المتداخلة مع معدّل عينات -- صوت، أو محاور مقياس تسارع، أو أثر ADC -- ويرسمه العرض كرسم بياني متمرّر، بسلسلة واحدة لكل محور، يسمّيها البرنامج النصي. وعناصر التحكم في الرسم البياني موجودة في شريط أدواته وعلى الرسم نفسه:
اسحب للتحريك ومرّر للتكبير؛ وانقر نقرًا مزدوجًا للعودة إلى متابعة الحافة الحية. ويضبط History مقدار البيانات السابقة المحتفظ بها حتى تتمكن من التحريك عائدًا عبرها.
Auto Scale يلائم المحور الرأسي مع البيانات؛ وعند إيقافه يكون المحور هو النطاق الذي أعلنه البرنامج النصي.
انقر بالزر الأيمن على الرسم لوضع علامة وقراءة الزمن والقيمة الدقيقين لعينة، أو لضبط مستوى تشغيل. ومع تهيئة Trigger يبقى الرسم البياني ثابتًا حتى تعبر الإشارة المستوى على حافة صاعدة أو هابطة -- طريقة راسم الذبذبات في التقاط العابرات -- وينتظر Re-arm العابرة التالية.
Spectrum يحوّل الرسم البياني إلى رسم ترددي للبيانات نفسها، مع اختيار النافذة (Hann أو Hamming أو Blackman أو المستطيلة)، وAveraging وPeak Hold عبر الإطارات، ومحور Log Frequency. وتسمّي القراءة أسفل الرسم تردد الذروة ومستواه.
Pause يجمّد الرسم البياني دون التأثير على التسجيل؛ وStats يضيف سطرًا للحد الأدنى / الأقصى / RMS؛ وتحفظ قائمة النقر بالزر الأيمن الرسم كصورة.

سجلّا شكل موجي يتمرّران مباشرةً: أثر ميكروفون وأثر 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 هو شبكة من المسافات -- الإطارات التي ينتجها مستشعر زمن الطيران -- ويرسمه العرض كخريطة بألوان زائفة مكبّرة إلى ارتفاع الرسم البياني. يختار شريط الأدوات طريقة العرض: لوحة الألوان (لوحات الكاميرا نفسها Depth وRainbow وIronbow، إضافة إلى لوحتين فاتحة وداكنة لكاميرات الأحداث)، ومرشّح التحجيم الذي يكبّر شبكة المستشعر الصغيرة (ثنائي التكعيب لسطح أملس، أو ثنائي الخطية، أو الأقرب لكتلة واحدة لكل بكسل مستشعر)، وتعيين الدرجات اللونية الذي يحوّل المسافة إلى لون: Script Range يستخدم النطاق الذي أعلنه البرنامج النصي، وAuto Range يمدّ الألوان على المسافات الموجودة فعليًا في كل إطار (متجاهلًا النسبة الشاذة الصغيرة)، وEqualize يوزّعها على توزيع الإطار بحيث يُظهر المشهد المسطّح تفاصيله. وتبلّغ الترويسة عن حجم الخريطة والنطاق المعلَن والحد الأدنى والأقصى والمتوسط للإطار؛ ويقرأ التحويم فوق الخريطة المسافة تحت المؤشر، ويكتب Save Image الخريطة المرسومة إلى ملف PNG.

خريطة عمق بزمن الطيران ليد أمام المستشعر، مع مُحدِّدات لوحة الألوان والتحجيم وتعيين الدرجات اللونية في شريط الأدوات.#
يعلن حقل العمق عن حجم شبكته ونطاقه؛ وكل تحديث هو الشبكة كأعداد عشرية ذات 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 لجانب البرنامج النصي -- أنواع السجلات ووسائطها ورد النداء on_write -- وأمثلة 12-Protocol تحت File → Examples لبرامج نصية عاملة تنشر قراءات وعناصر تحكم وخريطة عمق.