12.8. Widgeti i telemetrija s CBORChannel#
Backendi na prethodne dvije stranice prenose sirove bajtove, a kanal sirovih bajtova treba host program koji ih zna dekodirati. Većinu vremena ono što kamera želi objaviti jednostavnije je od toga: nekoliko imenovanih očitanja, nekoliko imenovanih kontrola, valni oblik, karta dubine. Za taj slučaj paket protocol isporučuje protocol.CBORChannel, gotov backend koji drži imenovana polja, serijalizira ih kao CBOR zapise u SenML rasporedu i dekodira zapisivanja s hosta natrag u vrijednosti polja. Njegova je prednost što ga OpenMV IDE već razumije: registrirajte CBORChannel i prikaz Kanali u IDE-u iscrtava svako polje kao widget uživo – oznake, prekidače, klizače, grafove, karte – bez ikakvog host koda. Isti zapisi su običan CBOR, pa ih prilagođeni host dekodira bilo kojom CBOR bibliotekom kad prerastete IDE.

Kanal primjera controls_channel.py kako ga IDE iscrtava: očitanja, prekidači i klizači za FLIR Lepton. Svaka kontrola piše natrag u skriptu.#
12.8.1. Očitanja i kontrole#
CBORChannel je rječnik tipiziranih polja. add deklarira svako od njih s nazivom, type widgeta i argumentima koje ta vrsta treba; dodjela u ch["name"] ažurira polje; on_write povratni poziv prima promjene s hosta:
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)
Pokrenite je u IDE-u, prebacite okno ispod međuspremnika slike na Kanali i widgeti se pojavljuju: oznaka je vrijednost samo za čitanje (sa svojim unit pored), text je blok statičnog obogaćenog teksta, toggle je prekidač, slider i spinbox postavljaju broj unutar min / max / step (brojčani okvir za precizan unos), radio i select biraju jednu od options, lineedit je polje za slobodan unos teksta, a pushbutton je trenutna radnja koja poziva on_write s True. Skripta čita svoje kontrole natrag s ch["Threshold"] kad god želi – kanal drži posljednju vrijednost – a on_write je kuka za one kojima treba trenutna reakcija. N-torka (min, max, value) dodijeljena klizaču ili brojčanom okviru pomiče njegov raspon zajedno s vrijednošću, što je način na koji kontrola prati senzor čije granice ovise o drugoj postavci.
Neobavezni on_read(channel) povratni poziv izvodi se neposredno prije serijalizacije kanala za host – mjesto za uzorkovanje senzora u oznaku tako da se očitanje uzima samo kad netko gleda:
def on_read(ch):
ch["FPA Temp"] = round(csi0.ioctl(csi.IOCTL_LEPTON_GET_FPA_TEMP), 1)
Primjer controls_channel.py pod File → Examples → 12-Protocol koristi upravo ovaj oblik da izloži način mjerenja, raspon temperature i postavke zrcaljenja / okretanja FLIR Leptona dok kamera struji.
12.8.2. Valni oblici#
Polje waveform nosi blok uzoraka s brzinom uzorkovanja. Uzorci su sirovi bajtovi array polja u typecode tog polja (16-bitni bez predznaka prema zadanom); za više signala odjednom – tri osi akcelerometra – ispreplećite ih i navedite koliko series ima i kako ih nazvati:
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 iscrtava svaki valni oblik kao pomični graf s jednim tragom po nizu, vremenski označava svaki dio podataka tako da se praznine između ažuriranja prikazuju kao praznine, i nudi spektralni prikaz, okidače, markere i snimanje u CSV, WAV, NumPy ili Edge Impulse datoteke. Zamijenite sintetički sinus uzorcima audio ili očitanjima IMU upravljačkog programa i skripta je alat za prikupljanje podataka.
12.8.3. Karte dubine#
Polje depth je mreža width x height 32-bitnih floatova, jedna udaljenost po ćeliji – ono što proizvodi time-of-flight senzor. min / max deklariraju raspon koji host treba mapirati u boju; IDE također može automatski odrediti raspon boja iz podataka:
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)
Primjer sensors_channel.py uparuje ovakav kanal dubine s kanalom očitanja i detektorom lica na istoj kameri. Poziv send_event() je neobavezan – IDE sam ispituje kanale – ali govori hostu vođenom događajima da je nova sličica spremna, a da on ne mora pitati.
12.8.4. Dekodiranje na prilagođenom hostu#
Zapisi su standardni CBOR: svako čitanje kanala vraća polje mapa s cjelobrojnim ključevima SenML-a (0 naziv, 1 jedinica, 2 brojčana vrijednost, 3 vrijednost niza, 8 podatkovna vrijednost te negativni ključevi za vrstu widgeta, opcije, raspon i dimenzije). Host koji je prerastao IDE čita kanal s channel_read() i dekodira ga bilo kojom CBOR bibliotekom (pip install cbor2); zapisivanje kontrole natrag je CBOR polje mapa {0: name, 2: value} poslano s channel_write(). Referenca protocol.CBORChannel navodi svaki ključ. Za većinu projekata, ipak, prikaz Kanali u IDE-u je host, a jedini kod je skripta na strani kamere iznad.