13.1.10. Samodzielne okna terminala¶
Tools → Open Terminal otwiera niezależne okna terminala – każde z nich to miniaturowa sesja OpenMV IDE we własnym oknie, z podglądem bufora ramki, histogramem i interaktywnym terminalem, połączona poprzez wybrany przez Ciebie transport. Nie wpływa to na połączenie głównego okna, dzięki czemu samodzielny terminal pozwala obserwować drugą kamerę, podczas gdy pierwsza pozostaje połączona, albo debugować kamerę po drugiej stronie sieci.
Samodzielne okno terminala przez port szeregowy: interaktywny terminal po lewej wraz z paskiem narzędzi run / stop / reset, bufor ramki i histogram po prawej – ożywają, gdy kamera przesyła ramki strumieniowo w paśmie.¶
New Terminal prosi o wybranie jednego z trzech transportów:
Serial port – dowolny port szeregowy o dowolnej szybkości transmisji (baud) (domyślnie 115 200). Obejmuje to port USB drugiej kamery, kamerę podłączoną przez mostek USB-to-UART, szeregowe łącze Bluetooth (które pojawia się jako zwykły port szeregowy) albo dowolne urządzenie szeregowe spoza OpenMV, które wymaga terminala. Na porcie USB szybkość transmisji (baud) nie ogranicza prędkości – dane zawsze przemieszczają się z prędkością łącza USB – ale na porcie USB kamery unikaj wartości 921 600 i 12 000 000, które przełączają kamerę z REPL na protokół debugowania IDE.
TCP – połącz się z serwerem pod wybranym hostem i portem albo nasłuchuj jako serwer na wybranym porcie.
UDP – ta sama para ról, ale przez UDP.
IDE zapamiętuje ostatnie dziesięć konfiguracji i wyświetla je w podmenu Open Terminal w celu otwarcia jednym kliknięciem; Clear Menu je zapomina.
W przeciwieństwie do panelu głównego okna przeznaczonego wyłącznie do wyświetlania danych wyjściowych, samodzielny terminal jest w pełni interaktywny: to REPL. Wpisuj polecenia w wierszu poleceń, a Python wykonuje je na podłączonej kamerze wiersz po wierszu, z historią i uzupełnianiem tabulatorem dostarczanymi przez sam MicroPython. Pasek narzędzi dodaje wykonywane jednym kliknięciem odpowiedniki typowych sekwencji sterujących – uruchom bieżący skrypt z edytora, zatrzymaj działający skrypt oraz miękki reset – a także te same elementy sterujące czyszczeniem, zapisem i zawijaniem co główny panel terminala.
Bufor ramki nad terminalem również działa na żywo. Gdy podłączona kamera przesyła strumieniowo skompresowane ramki w paśmie – osadzone w tym samym strumieniu co jej wydruki – terminal dekoduje je i wyświetla, a przyciski Record i Zoom działają dokładnie tak samo jak w głównym oknie. To połączenie stanowi koncepcję debugowania sieciowego IDE: kamera, która udostępnia swój REPL przez Wi-Fi, otrzymuje pełną pętlę edycja-uruchomienie-podgląd bez udziału kabla USB.
13.1.10.1. Strumieniowanie ramek w paśmie¶
Kodowanie rozumiane przez terminal jest proste: bajt 0xFE otwiera ramkę, drugi 0xFE ją zamyka, a każdy bajt ładunku pomiędzy nimi ma ustawiony najwyższy bit i niesie sześć bitów skompresowanego obrazu – trzy bajty obrazu stają się czterema bajtami ładunku. Zwykły tekst nigdy nie używa tych wartości bajtów, więc ramki i dane wyjściowe print() współdzielą strumień bez kolizji: terminal wyświetla tekst i pokazuje ramki.
Poniższy skrypt przechwytuje, konwertuje każdą ramkę na JPEG i drukuje ją w tej postaci. Pakowanie bitów odbywa się za pośrednictwem ulab (który nie ma operatorów przesunięcia, więc przesunięcia są zapisane jako mnożenia i dzielenia) i jest na tyle szybkie, że nadąża za kamerą:
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())
Działa to w samodzielnym terminalu, ponieważ drukowanie w REPL czeka: kamera blokuje się, dopóki terminal nie odbierze danych, więc każdy bajt ramki dociera, a JPEG szybko przemieszcza się przez łącze USB full-speed lub high-speed. Ten sam skrypt działa bez zmian przez terminal TCP, czyli opisaną powyżej ścieżkę debugowania sieciowego. Jedynym miejscem, gdzie nie zadziała, jest główne połączenie debugowania IDE, którego kanał wyjściowy w razie przepełnienia resetuje się zamiast czekać – tam podgląd bufora ramki i tak natywnie pokazuje ramki kamery, więc nic nie zostaje pominięte.