12.8. CBORChannel ile pencere öğeleri ve telemetri#

Önceki iki sayfadaki arka uçlar ham baytları taşır ve ham bir bayt kanalı, bunları nasıl çözeceğini bilen bir ana bilgisayar programı gerektirir. Çoğu zaman bir kameranın yayımlamak istediği şey bundan daha basittir: birkaç adlandırılmış okuma, birkaç adlandırılmış denetim, bir dalga biçimi, bir derinlik haritası. Bu durum için protocol paketi, adlandırılmış alanlar tutan, bunları SenML düzeninde CBOR kayıtları olarak serileştiren ve ana bilgisayar yazmalarını alan değerlerine geri çözen hazır bir arka uç olan protocol.CBORChannel sınıfını sunar. Getirisi, OpenMV IDE’nin onu zaten anlamasıdır: bir CBORChannel kaydedin, IDE’nin Kanallar görünümü her alanı hiçbir ana bilgisayar kodu olmadan canlı bir pencere öğesi olarak işler – etiketler, anahtarlar, kaydırıcılar, grafikler, haritalar. Aynı kayıtlar düz CBOR’dur, bu yüzden IDE’yi aştığınızda özel bir ana bilgisayar bunları herhangi bir CBOR kitaplığıyla çözer.

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

IDE’nin işlediği hâliyle controls_channel.py örneğinin kanalı: bir FLIR Lepton için göstergeler, anahtarlar ve kaydırıcılar. Her denetim betiğe geri yazar.#

12.8.1. Göstergeler ve denetimler#

Bir CBORChannel, türlendirilmiş alanlardan oluşan bir sözlüktür. add her birini bir ad, bir pencere öğesi type değeri ve o türün gerektirdiği bağımsız değişkenlerle bildirir; ch["name"] öğesine atama yapmak bir alanı günceller; on_write geri çağırması ana bilgisayarın değişikliklerini alır:

import time
import protocol
from protocol import CBORChannel

def on_write(ch, name, value):
    # Called for every control the host changes.
    print(name, "=", value)
    if name == "Reset":
        ch["Count"] = 0

ch = CBORChannel(on_write=on_write)
ch.add("Status", type="label", value="starting")
ch.add("Count", type="label", value=0)
ch.add("Enable", type="toggle", value=True)
ch.add("Threshold", type="slider", min=0, max=100, step=1,
       value=50, unit="%")
ch.add("Gap", type="spinbox", min=0.0, max=15.0, step=0.1,
       value=1.5, unit="mm")
ch.add("Mode", type="radio", options=["Idle", "Track", "Record"],
       value="Idle")
ch.add("Quality", type="select", options=["Low", "Medium", "High"],
       value="Medium")
ch.add("Name", type="lineedit", value="openmv-cam")
ch.add("Reset", type="pushbutton")
protocol.register(name="controls", backend=ch)

count = 0
while True:
    if ch["Enable"]:
        count += 1
        ch["Count"] = count
    ch["Status"] = "threshold %d%%" % ch["Threshold"]
    time.sleep_ms(100)

IDE’de çalıştırın, çerçeve arabelleğinin altındaki bölmeyi Kanallar görünümüne geçirin ve pencere öğeleri belirir: bir label salt okunur bir değerdir (yanında unit değeriyle), text statik bir zengin metin bloğudur, bir toggle bir anahtardır, slider ve spinbox min / max / step içinde bir sayı ayarlar (hassas giriş için döndürme kutusu), radio ve select options arasından birini seçer, lineedit serbest metin alanıdır ve pushbutton, on_write geri çağırmasını True ile çağıran anlık bir eylemdir. Betik, denetimlerini istediği zaman ch["Threshold"] ile geri okur – kanal en son değeri tutar – ve on_write, anında tepki gerektirenler için kancadır. Bir kaydırıcıya ya da döndürme kutusuna atanan bir (min, max, value) demeti, aralığını değeriyle birlikte taşır; bir denetim, sınırları başka bir ayara bağlı olan bir sensörü böyle izler.

İsteğe bağlı on_read(channel) geri çağırması, kanal ana bilgisayar için serileştirilmeden hemen önce çalışır – bir sensörü bir etikete örneklemek için uygun yer; böylece bir okuma yalnızca biri bakarken alınır:

def on_read(ch):
    ch["FPA Temp"] = round(csi0.ioctl(csi.IOCTL_LEPTON_GET_FPA_TEMP), 1)

File → Examples → 12-Protocol altındaki controls_channel.py örneği, kamera akış yaparken bir FLIR Lepton’un ölçüm modunu, sıcaklık aralığını ve ayna / çevirme ayarlarını açığa çıkarmak için tam olarak bu biçimi kullanır.

12.8.2. Dalga biçimleri#

Bir waveform alanı, bir örnekleme hızıyla birlikte bir örnek bloğu taşır. Örnekler, alanın typecode değerindeki (varsayılan olarak işaretsiz 16 bit) bir array nesnesinin ham baytlarıdır; aynı anda birden fazla sinyal için – bir ivmeölçerin üç ekseni – bunları serpiştirin ve kaç series olduğunu ve ne adlandırılacaklarını belirtin:

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

RATE = 1000           # samples per second
CHUNK = 100           # samples per update (100 ms)

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

t = 0
while True:
    mic = array("H", (int(32768 + 20000 * math.sin(2 * math.pi * 50 * (t + i) / RATE))
                      for i in range(CHUNK)))
    imu = array("f")
    for i in range(CHUNK):
        phase = 2 * math.pi * 2 * (t + i) / RATE
        imu.extend((math.sin(phase), math.cos(phase), 1.0))
    ch["Mic"] = bytes(mic)    # the raw sample bytes
    ch["IMU"] = bytes(imu)
    t += CHUNK
    time.sleep_ms(100)

IDE her dalga biçimini seri başına bir izle kayan bir grafik olarak çizer, güncellemeler arasındaki boşlukların boşluk olarak görünmesi için her veri bloğuna zaman damgası vurur ve bir spektrum görünümü, tetikleyiciler, işaretçiler ve CSV, WAV, NumPy ya da Edge Impulse dosyalarına kayıt sunar. Yapay sinüsü audio örnekleriyle ya da bir IMU sürücüsünün okumalarıyla değiştirin, betik bir veri toplama aracı olur.

12.8.3. Derinlik haritaları#

Bir depth alanı, hücre başına bir mesafe olmak üzere 32 bit kayan noktalı sayılardan oluşan bir width x height ızgarasıdır – bir uçuş süresi sensörünün ürettiği şey. min / max, ana bilgisayarın renge eşlemesi gereken aralığı bildirir; IDE renkleri verilerden otomatik olarak da aralıklandırabilir:

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)
reg = 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)
    reg.send_event(0xFFFF)
    time.sleep_ms(50)

sensors_channel.py örneği, böyle bir derinlik kanalını aynı kamerada bir okumalar kanalı ve bir yüz algılayıcıyla eşleştirir. send_event() çağrısı isteğe bağlıdır – IDE kanalları kendi başına yoklar – ancak olay güdümlü bir ana bilgisayara, sormak zorunda kalmadan yeni bir çerçevenin hazır olduğunu bildirir.

12.8.4. Özel bir ana bilgisayarda kod çözme#

Kayıtlar standart CBOR’dur: her kanal okuması, SenML’nin tamsayı anahtarlarıyla (0 ad, 1 birim, 2 sayısal değer, 3 dize değeri, 8 veri değeri ve pencere öğesi türü, seçenekler, aralık ve boyutlar için negatif anahtarlar) anahtarlanmış bir eşlem dizisi döndürür. IDE’yi aşmış bir ana bilgisayar, kanalı channel_read() ile okur ve herhangi bir CBOR kitaplığıyla (pip install cbor2) çözer; bir denetimi geri yazmak ise channel_write() ile gönderilen {0: name, 2: value} eşlemlerinden oluşan bir CBOR dizisidir. protocol.CBORChannel başvurusu her anahtarı listeler. Yine de çoğu proje için IDE’nin Kanallar görünümü ana bilgisayardır ve tek kod yukarıdaki kamera tarafı betiğidir.