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 – это блок чередующихся выборок с частотой дискретизации – звук, оси акселерометра, трасса АЦП, – и представление строит его как прокручивающийся график, по одной серии на ось, с именами от скрипта. Элементы управления графиком находятся на его панели инструментов и на самом графике:
Перетаскивайте для панорамирования и прокручивайте для масштабирования; двойной щелчок возвращает к слежению за живым краем. History задаёт, сколько прошлых данных хранится, чтобы можно было прокрутить их назад.
Auto Scale подгоняет вертикальную ось под данные; если выключено, ось – это диапазон, объявленный скриптом.
Щёлкните по графику правой кнопкой мыши, чтобы поставить маркер и прочитать точное время и значение выборки или задать уровень триггера. При взведённом Trigger график стоит на месте, пока сигнал не пересечёт уровень по переднему или заднему фронту – осциллографический способ поймать переходный процесс, – а Re-arm ждёт следующего.
Spectrum переключает график на частотное представление тех же данных, с выбором окна (Ханна, Хэмминга, Блэкмана, прямоугольное), 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 как рабочие скрипты, публикующие показания, элементы управления и карту глубины.