12.8. Widgetit ja telemetria CBORChannelilla#
Kahden edellisen sivun taustaosat siirtävät raakatavuja, ja raakatavukanava tarvitsee isäntäohjelman, joka osaa dekoodata ne. Useimmiten se, mitä kamera haluaa julkaista, on tätä yksinkertaisempaa: muutama nimetty lukema, muutama nimetty säädin, aaltomuoto, syvyyskartta. Tätä tapausta varten protocol-paketti sisältää luokan protocol.CBORChannel, valmiin taustaosan, joka säilyttää nimettyjä kenttiä, sarjallistaa ne CBOR-tietueiksi SenML-asettelussa ja dekoodaa isännän kirjoitukset takaisin kenttäarvoiksi. Sen etu on, että OpenMV IDE ymmärtää sen jo valmiiksi: rekisteröi CBORChannel, niin IDE:n Kanavat-näkymä piirtää jokaisen kentän reaaliaikaisena widgettinä – tunnisteina, kytkiminä, liukusäätiminä, kuvaajina, karttoina – ilman lainkaan isäntäkoodia. Samat tietueet ovat tavallista CBOR:ia, joten mukautettu isäntä dekoodaa ne millä tahansa CBOR-kirjastolla, kun kasvat ulos IDE:stä.

controls_channel.py-esimerkin kanava IDE:n piirtämänä: lukemat, kytkimet ja liukusäätimet FLIR Leptonille. Jokainen säädin kirjoittaa takaisin skriptille.#
12.8.1. Lukemat ja säätimet#
CBORChannel on sanakirja tyypitettyjä kenttiä. add ilmoittaa kunkin nimellä, widgetin type-tyypillä ja tyypin tarvitsemilla argumenteilla; sijoitus kohteeseen ch["name"] päivittää kentän; on_write-takaisinkutsu vastaanottaa isännän muutokset:
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)
Suorita se IDE:ssä, vaihda kehyspuskurin alapuolinen ruutu Kanavat-näkymään, niin widgetit ilmestyvät: label on vain luettava arvo (unit sen vieressä), text on lohko staattista muotoiltua tekstiä, toggle on kytkin, slider ja spinbox asettavat luvun min / max / step -rajoissa (askelluskenttä tarkkaa syöttöä varten), radio ja select valitsevat yhden options-vaihtoehdoista, lineedit on vapaa tekstikenttä, ja pushbutton on hetkellinen toiminto, joka kutsuu on_write-takaisinkutsua arvolla True. Skripti lukee säätimensä takaisin lausekkeella ch["Threshold"] milloin tahansa – kanava säilyttää viimeisimmän arvon – ja on_write on koukku niille, jotka tarvitsevat välittömän reaktion. Liukusäätimeen tai askelluskenttään sijoitettu (min, max, value)-monikko siirtää sen aluetta arvon mukana, millä säädin seuraa anturia, jonka rajat riippuvat toisesta asetuksesta.
Valinnainen on_read(channel)-takaisinkutsu ajetaan juuri ennen kuin kanava sarjallistetaan isännälle – paikka, jossa anturi näytteistetään tunnisteeseen, jotta lukema otetaan vain, kun joku katsoo:
def on_read(ch):
ch["FPA Temp"] = round(csi0.ioctl(csi.IOCTL_LEPTON_GET_FPA_TEMP), 1)
controls_channel.py-esimerkki kohdassa File → Examples → 12-Protocol käyttää täsmälleen tätä muotoa paljastaakseen FLIR Leptonin mittaustilan, lämpötila-alueen ja peilaus- / kääntöasetukset kameran suoratoistaessa.
12.8.2. Aaltomuodot#
waveform-kenttä kuljettaa lohkon näytteitä näytteenottotaajuuksineen. Näytteet ovat array-taulukon raakatavut kentän typecode-tyyppikoodissa (oletuksena etumerkitön 16-bittinen); useaa signaalia varten kerralla – kiihtyvyysanturin kolme akselia – lomita ne ja kerro, kuinka monta series-sarjaa on ja miksi niitä kutsutaan:
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 piirtää jokaisen aaltomuodon vierivänä kuvaajana, jossa on yksi jälki sarjaa kohti, aikaleimaa jokaisen lohkon, jotta päivitysten väliset aukot näkyvät aukkoina, ja tarjoaa spektrinäkymän, liipaisut, merkit ja tallennuksen CSV-, WAV-, NumPy- tai Edge Impulse -tiedostoihin. Korvaa synteettinen siniaalto audio-näytteillä tai IMU-ajurin lukemilla, niin skripti on datankeruutyökalu.
12.8.3. Syvyyskartat#
depth-kenttä on width x height -ruudukko 32-bittisiä liukulukuja, yksi etäisyys solua kohti – sitä, mitä lentoaika-anturi tuottaa. min / max ilmoittavat alueen, jonka isännän tulisi kuvata väreiksi; IDE voi myös määrittää värialueen automaattisesti datasta:
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-esimerkki yhdistää tällaisen syvyyskanavan lukemakanavaan ja kasvontunnistimeen samassa kamerassa. send_event()-kutsu on valinnainen – IDE kyselee kanavia itse – mutta se kertoo tapahtumapohjaiselle isännälle, että uusi kehys on valmis, ilman että sen tarvitsee kysyä.
12.8.4. Dekoodaus mukautetussa isännässä#
Tietueet ovat standardia CBOR:ia: jokainen kanavan luku palauttaa taulukon karttoja, joiden avaimina ovat SenML:n kokonaislukuavaimet (0 nimi, 1 yksikkö, 2 numeerinen arvo, 3 merkkijonoarvo, 8 data-arvo sekä negatiiviset avaimet widgetin tyypille, vaihtoehdoille, alueelle ja mitoille). Isäntä, joka on kasvanut ulos IDE:stä, lukee kanavan metodilla channel_read() ja dekoodaa sen millä tahansa CBOR-kirjastolla (pip install cbor2); säätimen kirjoittaminen takaisin on CBOR-taulukko {0: name, 2: value}-karttoja, joka lähetetään metodilla channel_write(). protocol.CBORChannel-viite luettelee jokaisen avaimen. Useimmissa projekteissa IDE:n Kanavat-näkymä on kuitenkin isäntä, ja ainoa koodi on yllä oleva kameran puolen skripti.