13.1.10. Отдельные окна терминала¶
Tools → Open Terminal открывает независимые окна терминала – каждое из них представляет собой миниатюрный сеанс OpenMV IDE в собственном окне, со средством просмотра буфера кадра, гистограммой и интерактивным терминалом, подключённым через выбранный вами транспорт. Подключение главного окна при этом не затрагивается, поэтому отдельный терминал позволяет наблюдать за второй камерой, пока первая остаётся подключённой, или отлаживать камеру на другом конце сети.
Отдельное окно терминала по последовательному порту: интерактивный терминал слева с панелью инструментов запуска / остановки / сброса, буфер кадра и гистограмма справа – они оживают, когда камера передаёт кадры по тому же каналу.¶
New Terminal предлагает один из трёх транспортов:
Serial port – любой последовательный порт на любой скорости передачи (по умолчанию 115 200). Это охватывает USB-порт второй камеры, камеру, подключённую через мост USB-to-UART, последовательное соединение по Bluetooth (которое отображается как обычный последовательный порт) или любое не-OpenMV последовательное устройство, которому нужен терминал. На USB-порту скорость передачи не ограничивает быстродействие – данные всегда движутся со скоростью USB-канала – но на USB-порту камеры избегайте значений 921 600 и 12 000 000, которые переключают камеру из REPL в отладочный протокол IDE.
TCP – подключение к серверу на выбранном узле и порту или прослушивание в качестве сервера на выбранном порту.
UDP – та же пара ролей, но по UDP.
IDE запоминает последние десять конфигураций и перечисляет их в подменю Open Terminal для повторного открытия одним щелчком; Clear Menu забывает их.
В отличие от панели главного окна, работающей только на вывод, отдельный терминал полностью интерактивен: это REPL. Вводите команды в приглашении, и Python выполняет их на подключённой камере строка за строкой, с историей и автодополнением по табуляции, которые предоставляет сам MicroPython. Панель инструментов добавляет эквиваленты распространённых управляющих последовательностей в виде кнопок – запуск текущего скрипта из редактора, остановка работающего скрипта и программный сброс – а также те же элементы управления очисткой, сохранением и переносом строк, что и в панели главного терминала.
Буфер кадра над терминалом тоже живой. Когда подключённая камера передаёт сжатые кадры по тому же каналу – встроенными в тот же поток, что и её вывод print – терминал декодирует и отображает их, а кнопки Record и Zoom работают точно так же, как в главном окне. Эта комбинация и есть подход IDE к сетевой отладке: камера, предоставляющая свой REPL по Wi-Fi, получает полный цикл правки-запуска-просмотра без единого USB-кабеля.
13.1.10.1. Передача кадров по тому же каналу¶
Кодировка, которую понимает терминал, проста: байт 0xFE открывает кадр, второй байт 0xFE закрывает его, а у каждого байта полезной нагрузки между ними установлен старший бит, и он несёт шесть бит сжатого изображения – три байта изображения превращаются в четыре байта полезной нагрузки. Обычный текст никогда не использует эти значения байтов, поэтому кадры и вывод print() делят поток без конфликтов: терминал показывает текст и отображает кадры.
Приведённый ниже скрипт делает захват, преобразует каждый кадр в JPEG и выводит его в таком виде. Упаковка битов выполняется через ulab (в котором нет операторов сдвига, поэтому сдвиги записаны как умножения и деления) и достаточно быстра, чтобы успевать за камерой:
import csi
import sys
import time
from ulab import numpy as np
csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.QVGA)
clock = time.clock()
def encode_for_ide(data):
n = len(data)
n3 = n - (n % 3)
m = (n3 // 3) * 4
out = bytearray(((n * 8) + 5) // 6 + 2)
out[0] = 0xFE
out[-1] = 0xFE
if n3:
src = np.frombuffer(data, dtype=np.uint8)
dst = np.frombuffer(out, dtype=np.uint8)
b0 = src[0:n3:3]
b1 = src[1:n3:3]
b2 = src[2:n3:3]
dst[1:m + 1:4] = (b0 & 0x3F) | 0x80
dst[2:m + 2:4] = (b0 // 64) | ((b1 & 0x0F) * 4) | 0x80
dst[3:m + 3:4] = (b1 // 16) | ((b2 & 0x03) * 16) | 0x80
dst[4:m + 4:4] = (b2 // 4) | 0x80
if n % 3 == 2:
x = data[n - 2] | (data[n - 1] << 8)
out[m + 1] = 0x80 | (x & 0x3F)
out[m + 2] = 0x80 | ((x >> 6) & 0x3F)
out[m + 3] = 0x80 | ((x >> 12) & 0x3F)
elif n % 3 == 1:
out[m + 1] = 0x80 | (data[n - 1] & 0x3F)
out[m + 2] = 0x80 | (data[n - 1] >> 6)
return out
while True:
clock.tick()
img = csi0.snapshot().to_jpeg(quality=80)
sys.stdout.write(encode_for_ide(img.bytearray()))
print(clock.fps())
Это работает в отдельном терминале, потому что вывод REPL ожидает: камера блокируется, пока терминал не заберёт данные, поэтому до места назначения доходит каждый байт кадра, а JPEG быстро передаётся по каналу USB full-speed или high-speed. Тот же скрипт без изменений работает по TCP-терминалу – это и есть путь сетевой отладки, описанный выше. Единственное место, где это не работает, – основное отладочное подключение IDE, канал вывода которого при переполнении сбрасывается сам вместо ожидания – там средство просмотра буфера кадра уже показывает кадры камеры нативно, так что ничего не теряется.