
Podgląd i sterowanie pozycją kamery RaspberryPi w oknie przeglądarki
Wstęp
W poprzednim kursie (tutaj) pokazałem jak wyświetlić obraz z kamery RasbeprryPi w oknie przeglądarki. W tym kursie pokaże jak sterować pozycją kamery. Będziemy sterować na dwa sposoby
- Przesuwając myszką/ palcem po wyświetlanym obrazie
- Przechylając telefon i używając wbudowanego żyroskopu
Wszystko to w stronie internetowej otwartej w przeglądarce ( ja użyłem Chrome zarówno dla desktopu i smartfona, bo ma najlepsze wsparcie dla protokołu webRTC).
Całość kursu w formie wideo ( po angielsku) :
Jak to będzie działać
Podobnie jak obraz z kamery jest przesyłany protokołem webRTC, będziemy tym samym kanałem przesyłali instrukcje do sterowanie pozycją kamery. Komunikaty z przeglądarki będą trafiały do skryptu Pythonowego na RasbeprryPi, a ten odpowiednio wysteruję sterownik serw. Brzmi prosto i takie też będzie – przesyłanie komunikatów jest całkowicie po stronie RemoteMe, w skrypcie pythonowym należy jedynie zaimplementować funkcję, która jest uruchamiana po zmianie wartości zmiennej – więcej o zmiennych tutaj
Co potrzebujemy:
- Podgląd
- Raspberry PI Zero W
- Dedykowanej kamery
- Sterowanie
- 12-kanał sterownik LED 16-bit PWM I2C zgodny z Adafruit
- Dwa serwo mechanizmy kompatybilne z uchwytem na kamerkę
- Uchwyt na kamerkę (link)
- 12-kanał sterownik LED 16-bit PWM I2C zgodny z Adafruit
- Zasilanie – nie podaje konkretnych zastosować – bo są one uzależnione od samych silników i od tego jakimi akumulatorami akurat dysponujemy. Ja użyłem akumulatorów LiPo 4*1.2V z konwerterem step-down
Podpięcie Serwo Mechanizmów:
Do sterowania servo mechanizmów użyjemy 12-kanałowego sterownika LED 16-bit PWM I2C zgodnego z Adafruit. Komunikuje się on z Rpi poprzez interfejs I2C. Adafruit udostępnia bibliotekę pythona która działa out-of-the-box, Jeżeli w drugim kroku instalacji remoteme na RPi wybraliście opcje [Tak] macie tą bibliotekę już zainstalowaną ;).
Podłączenia:
Pin SDA, SCL, i ground podłączamy do tak samo oznaczonych pinów w RPI:
oznaczenie pinów w RPI wygląda tak:
(czerwone oznaczenia pinów ułatwią Wam liczenie pinów na samym urządzeniu )
Tutaj znajdziecie pełny obrazek z wyprowadeniami Pinów Rpi
Montaż uchwytu kamerki – nie powinien nastręczyć trudności, jedyne na co trzeba zwrócić szczególną uwagę to zmontowanie uchwytu, żeby patrzyła ona na wprost i co najważniejsze serwo-mechanizmy były w pozycji centralnej. Co do samej kamerki bez znaczenia gdzie będzie góra/dół można to łatwo zmienić w ustawieniach programu RemoteMe na RPI
Na zdjęciach i filmie widać zieloną płytkę, do której jest zamontowane RPI i sterownik serw, pliki możecie ściągnąć stąd została ona zaprojektowana w ten sposób, żeby można było łatwo ją wykonać metodą termo transferu. W repozytorium znajdują się też pliki gerber do łatwego zamówienia np z allpcb.com gdzie zamówiłem swoją płytkę.
Płytka przyda się bardziej, gdy w następnym kursie dodamy sterowanie silników do naszego samochodu.
Jak to będzie działać:
- do sterowania kamerą będziemy używać zmiennej typu SMALL_INTEGER_2, która przesyła dwie wartości liczbowe jedną z nich będziemy sterować osią x drugą osią y.
- zmienna będzie zmieniana przez telefon lub komputer gdzie będzie wyświetlany obraz
- przez przesuwanie myszką/ palcem po obszarze gdzie wyświetlane jest wideo
- przy pomocy żyroskopu w smartphonie gdzie odchyl od wyjściowej pozycji będzie sterować odpowiednio osiami kamery
Zaczynamy:
Zaczniemy od utworzenie zmiennej zakładka variables -> add okno uzupełniamy:
Tworzymy klikając “Submit” więcej o zmiennych tutaj
Następnie podpinamy nasze RasbperryPi do RemoteMe.org jak podłączyć tutaj. Następnie na belce podłączonej malinki klikamy burger Menu i “Add External script with wizard”
W pierwszym kroku zaznaczamy zmienna cameraPos
Dzięki temu w wygenerowanym kodzie będziemy mieć funkcje gotowe do zaimplementowania do interakcji z naszą zmienną.
W kroku drugim nadajemy nazwę naszemu urządzeniu “python” i dowolne, nieużywane Id. W ostatnim kroku klikamy “Create device”. Więcej o urządzeniach python tutaj.
W otwartym oknie dodamy kod do obsługi naszych serwo mechanizmów:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
import logging import socket import struct import sys import os os.chdir(sys.argv[1]) sys.path.append('../base') import remoteme import Adafruit_PCA9685 # Added import time # Added import RPi.GPIO as GPIO # Added pwm = None; # Added def setCameraPos(i1, i2): remoteMe.getVariables().setSmallInteger2("cameraPos", i1, i2) def onCameraPosChange(i1, i2): global pwm # Added logger.info("on camera change {} , {}".format(i1, i2)) # Added pwm.set_pwm(1, 0, i1) # Added pwm.set_pwm(0, 0, i2) # Added pass # Added def setupPWM(): # Added global pwm # Added pwm = Adafruit_PCA9685.PCA9685() # Added pwm.set_pwm_freq(80) # Added try: logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s', datefmt='%d.%m %H:%M', filename="logs.log") logger = logging.getLogger('application') setupPWM() # Added remoteMe = remoteme.RemoteMe() remoteMe.startRemoteMe(sys.argv) remoteMe.getVariables().observeSmallInteger2("cameraPos" ,onCameraPosChange); remoteMe.wait() finally: pass |
Dodane linie zaznaczone są komentarzem #Added
Omówienie kodu
1 2 3 4 5 |
import Adafruit_PCA9685 # Added import time # Added import RPi.GPIO as GPIO # Added pwm = None; # Added |
pwm
do kontrolowania kontrolera PWM (Adafruit_PCA9685 )
1 2 3 4 5 6 |
def onCameraPosChange(i1, i2): global pwm logger.info("on camera change {} , {}".format(i1, i2)) pwm.set_pwm(1, 0, i1) pwm.set_pwm(0, 0, i2) pass |
1 2 3 4 |
def setupPWM(): global pwm pwm = Adafruit_PCA9685.PCA9685() pwm.set_pwm_freq(80) |
Po zapisaniu kodu program na RasbperryPi się zresetuję, żeby wczytać nowy skrypt.
Strona internetowa
Teraz dodamy stronę internetową z dwoma suwakami, żeby znaleźć centralną pozycję kamery i maksymalne wychylenia. W świecie idealnym liczby by były np 0,100 dla obu wartkości, czyli wysyłając zmienna o wartości (50,50) ustawilibyśmy naszą kamerę w pozycji centralnej, a wartość (100,100) skierowalibyśmy ją maksymalnie w górę i prawo. Niestety liczby te będą na pewno inne i musimy je znaleźć. Do tego celu stworzymy stronę internetową z dwoma suwakami które będą wysyłać zmienne a my będziemy wiedzieli jakie są wartości zmiennych.
Aby dodać stronę internetową – wybieramy “New Device ” -> “Webpage” i uzupełniamy następująco:
Po dodaniu rozwijamy belkę klikamy index.html i “edit with wizard”. Następnie “Insert component” i uzupełniamy:
Więcej o komponentach tutaj
Z najważniejszych informacji
- będziemy sterować dwoma suwakami zmienna CameraPos
- minimalny i maksymalna liczba jaką wyślemy to 0 – 1000 dla obu osi
Klikamy insert, zamykamy okno wizarda, i otwieramy stronę w nowej zakładce klikając index.html i wybierając “Open in new tab”.
Dostaniemy naszą stronę. Pierwszy suwak odpowiada za oś X- kamery (ruchy poziome – czyli rozglądanie się na boki) a drugi za oś Y (czyli rozglądanie się góra dół). Gdy suwaki sterują inaczej zamieniamy wtyczki serwo mechanizmy miejscami. Gdy serwa nie reagują sprawdźmy, czy zostały wpięte w odpowiednie miejsca.
Poruszając suwakami ustawiamy kamerę w centralnej pozycji dla mnie jest to x=564 i y = 474.
Następnie wychylamy kamerę maksymalnie w minimalne wartości dla mnie to Xmin=298 . Ymin= 223.
Następnie obliczmy wychylenie maksymalne dla kamery, w obliczeniach chodzi o to, żeby centralna pozycja znajdowała się w środku przedziału. Więc prosta matematyka
- Xmax = Xcenter-Xmin + Xcenter = 564 – 298 + 564 = 830
- Ymax = Ycenter-Ymin + Ycenter = 474 – 223+ 474 = 725
Czyli oś X: Xmin = 298, Xcenter = 564, Xmax = 830. ma osi wygląda to następująco:
chodzi o to, żeby odległości pomiędzy wartościami były takie samo a dodatkowo wartość Xcenter wskazywało centralne położenie kamery w płaszczyźnie poziomej.
a oś Y: Ymin=223, Ycenter=474, Ymax=725
Na suwakach sprawdźmy czy maksymalne wartości bez problemu ustawiają kamerę w odpowiedniej pozycji i serwa się nie blokują – jeżeli się blokują zwiększamy Xmin i powtarzamy obliczenia (albo obliczenia robimy od nowa ale bazując na Xmax).
Wracamy do edycji strony index.html i skasujmy suwaki usuwając ze źródeł strony linijkę :
1 |
<variable component="slider" type="SMALL_INTEGER_2" name="cameraPos" label="camera" min="0" max="1000" valuebox="true" ></variable> |
I otwieramy ponownie Wizarda komponentów i wstawiamy komponenty
camera:
Status – w prawym górnym rogu wyświetli nam status połączenia:
Sterowanie palcem/ myszką poruszając po obrazie z kamery:
Zwróć uwagę, że uzupełniłem wartości Xmin itd obliczonymi wartościami.
i Gyroscope – żebyśmy mogli sterować smaprtphonem wychylając go w odpowiednich pozycjach:
Tutaj też wstawiłem obliczone wartości. Więcej o komponentach i konfiguracji tutaj
Warto też zmienić rozmiar wideo w źródłach index.html zamiast width i height wpiszemy style="width: calc(100vmin); height:calc(75vmin) "
– pozwoli to na wypełnienie obrazu kamery, gdy telefon jest w pozycji pionowej
Otwieramy naszą stronę w Smaprtphonie – najłatwiej klikając na index.html -> Get anymous Link -> klik na ikonę kodu QR i skanujemy go smartphonem.
Testujemy przesuwając palcem po ekranie, gdy kamera zamiast do góry wychyla się na dół w komponencie <variable component="cameraMouseTrack"
index.html zmieniamy invertX="false"
na invertX="true"
Gdy podobnie jest z osią Y zmieniamy w tym samym komponencie wartość invertY
Żyroskop włączamy klikając przycisk Gyroscope – zapamiętuje on pozycją smartphone’a i zmieniając wychylenie zmieniamy pozycją kamery – gdy wychylenie w lewo powoduje ruch w prawo postępujemy analogicznie z komponentem do przesuwania palcem ale tym razem edytujemy komponent <variable component="gyroscope".
Gdy chcemy, żeby żyroskop reagował delikatniej zwiększamy wartości zmiennych xRange
i yRange
To tyle na dzisiaj w wideo na początku kursu są przedstawione wszystkie kroki.
Pozdrawiam,
Maciek