12.8. 使用 CBORChannel 的小工具與遙測#
前兩頁的後端搬運的是原始位元組,而原始位元組通道需要一個知道如何解碼它們的主機程式。多數時候,相機想發布的內容比那簡單得多:幾個具名讀數、幾個具名控制項、一段波形、一張深度圖。針對這種情況,protocol 套件提供了 protocol.CBORChannel,這是一個現成的後端,可保存具名的欄位、將它們序列化為 SenML 版面配置的 CBOR 記錄,並將主機的寫入解碼回欄位值。它的好處在於 OpenMV IDE 已經理解它:註冊一個 CBORChannel,IDE 的 Channels 檢視 就會將每個欄位呈現為即時小工具——標籤、開關、滑桿、圖表、深度圖——完全不需要主機程式碼。同樣的記錄是一般的 CBOR,因此當你的需求超出 IDE 時,自訂主機可用任何 CBOR 函式庫解碼它們。

IDE 所呈現的 controls_channel.py 範例通道:FLIR Lepton 的讀數、切換開關與滑桿。每個控制項都會回寫至指令碼。#
12.8.1. 讀數與控制項#
CBORChannel 是一個由具型別欄位組成的字典。add 以名稱、小工具 type 以及該類型所需的引數宣告每個欄位;對 ch["name"] 指派會更新欄位;on_write 回呼則會收到主機的變更:
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 中執行它,將影格緩衝區下方的窗格切換至 Channels,小工具便會出現:label 是唯讀數值(旁邊附有其 unit),text 是一段靜態的 rich text,toggle 是一個開關,slider 與 spinbox 在 min/max/step 內設定一個數字(微調方塊供精確輸入),radio 與 select 從 options 中擇一,lineedit 是自由文字欄位,而 pushbutton 則是一次性的動作,會以 True 呼叫 on_write。指令碼可隨時以 ch["Threshold"] 讀回其控制項——通道會保存最新的數值——而 on_write 則是給需要立即反應的控制項使用的掛鉤。將 (min, max, value) 元組指派給滑桿或微調方塊會同時移動其範圍與數值,這正是控制項追蹤某個極限取決於其他設定之感測器的方式。
選用的 on_read(channel) 回呼會在通道為主機序列化之前執行——這是將感測器取樣到標籤中的地方,如此只有在有人觀看時才會進行讀取:
def on_read(ch):
ch["FPA Temp"] = round(csi0.ioctl(csi.IOCTL_LEPTON_GET_FPA_TEMP), 1)
File → Examples → 12-Protocol 下的 controls_channel.py 範例正是使用這種形式,在相機串流時公開 FLIR Lepton 的測量模式、溫度範圍與鏡像/翻轉設定。
12.8.2. 波形#
waveform 欄位承載一段具有取樣率的取樣資料。取樣值是以欄位 typecode(預設為無號 16 位元)表示的 array 原始位元組;若要同時傳送多個訊號——例如加速度計的三個軸——請將它們交錯排列,並指明有多少個 series 以及各自的名稱:
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 會將每段波形繪製為捲動圖表,每個序列一條軌跡,並為每個區塊加上時間戳記,使更新之間的間隙顯示為間隙,同時提供頻譜檢視、觸發、標記,以及錄製為 CSV、WAV、NumPy 或 Edge Impulse 檔案。將合成的正弦波換成 audio 取樣或 IMU 驅動程式的讀數,這個指令碼就成了資料收集工具。
12.8.3. 深度圖#
depth 欄位是一個 width x height 的 32 位元浮點數網格,每個格子一個距離——正是飛時測距感測器所產生的資料。min/max 宣告主機應映射為色彩的範圍;IDE 也可以自動依資料決定色彩範圍:
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 範例將這樣的深度通道與一個讀數通道以及同一部相機上的人臉偵測器搭配使用。send_event() 呼叫是選用的——IDE 會自行輪詢通道——但它能告知事件驅動的主機有新影格就緒,而不必由主機詢問。
12.8.4. 在自訂主機上解碼#
這些記錄是標準的 CBOR:每次通道讀取會傳回一個由映射組成的陣列,以 SenML 的整數鍵為鍵(0 名稱、1 單位、2 數值、3 字串值、8 資料值,以及用於小工具類型、選項、範圍與維度的負數鍵)。需求超出 IDE 的主機可用 channel_read() 讀取通道,並以任何 CBOR 函式庫解碼(pip install cbor2);將控制項寫回則是一個由 {0: name, 2: value} 映射組成的 CBOR 陣列,以 channel_write() 傳送。protocol.CBORChannel 參考文件列出了每個鍵。不過對多數專案而言,IDE 的 Channels 檢視就是主機,唯一的程式碼就是上面的相機端指令碼。