Ta strona używa ciasteczek (plików cookies), dzięki którym może działać lepiej. Dowiedz się więcejRozumiem i akceptuję

Jak wykorzystać Raspberry Pi do budowy robota z kamerą?

Programowanie 15.12.2015 Elvis

RaspberryPiNiektórzy uważają, że wykorzystanie Linuxa do budowy robotów nie ma większego sensu.

W tym artykule postaram się pokazać, że jest to bardzo łatwe i ma swoje zalety. Szczególnie, gdy jednostką sterującą będzie Raspberry Pi. Pora na robota z kamerą!

Opisywany robot powstał tylko jako prezentacja możliwego podejścia – nie jest ukończoną konstrukcją. W tej chwili nie posiada możliwości autonomicznego sterowania. Jest więc raczej zdalnie sterowaną platformą, którą można rozwijać poprzez dodanie czujników otoczenia lub analizę obrazu z zamontowanej kamery.

Robot z kamerą – lista elementów

Na początek wymienię elementy składowe omawianego robota.

Podstawa robota została wydrukowana na drukarce 3D. To nadal wstępny prototyp:

RPi-bot

Projekt podwozia dla robota na Raspberry Pi.

Po zmontowaniu robot prezentuje się następująco:

Prototyp robota z kamerą.

Oprogramowanie

Jako system operacyjny wybrałem Raspbiana. Jest to popularna dystrybucja Linuxa dedykowana dla Raspberry Pi, oparta o Debiana. Więcej informacji można znaleźć na stronie projektu.

Pierwszym etapem po instalacji była konfiguracja Wi-Fi – wykonywałem ją z poziomu konsoli, wykorzystując bezprzewodową klawiaturę (na zdjęciu widoczny jeszcze moduł USB od klawiatury). Po skonfigurowaniu WiFi, dalsze prace można było prowadzić przez ssh, więc klawiatura nie była już więcej potrzebna.

Wstępna konfiguracja.

Wykorzystanie kamery w robocie

W budowanym robocie wykorzystałem kamerkę dedykowaną, ale na wszelki wypadek testowałem również kamerkę podłączaną przez USB. Obie działały bez problemu, chociaż dedykowana była dużo lżejsza oraz nie zajmowała portu USB.

Po podłączeniu kamery musimy sprawdzić, czy system ją widzi. Najprościej wykonać polecenie:

Jeśli zobaczymy urządzenie /dev/video0 to znaczy, że nasza kamerka została wykryta prawidłowo. Moja kamerka USB została wykryta automatycznie, natomiast w przypadku kamerki dedykowanej, konieczne okazało się zainstalowanie modułu jądra bcm2835-v4l2:

Mając kamerę, musimy zastanowić się nad wyborem programu do jej obsługi. Możemy oczywiście napisać własny np. wykorzystując bibliotekę OpenCV, ale na początek najłatwiej jest po prostu udostępnić obraz z kamery przez WiFi.

Na szczęście nie musimy pisać odpowiedniego programu – znajdziemy w internecie gotowe rozwiązania. W pakietach dostarczanych z Raspbianem odnajdziemy program Motion. Pozwala on na udostępnienie obrazu z kamerki przez www, czyli spełnia nasze oczekiwania, jednak nie polecam go – jest strasznie zasobożerny, użycie CPU sięga 100%, a nawet na RPi 2 liczba klatek na sekundę jest zaskakująco niska – 2-3 klatki.

Jeśli zainstalowaliśmy pakiet motion najlepiej jest go odinstalować. Jeśli będzie uruchamiany w tle, może powodować błędy w dalszej konfiguracji.

Instalacja mjpg-streamer

Znacznie lepiej jest wykorzystać program mjpg-streamer. Program działa znacznie szybciej na Raspberry i nie powoduje problemów z obciążeniem procesora, a liczba klatek na sekundę jest znacznie większa.

Niestety nie jest on dostępny jako gotowy pakiet (albo ja takiego nie znalazłem), konieczne jest pobranie go ręcznie. Gotową wersję, dostosowaną do Raspberry pobrać można poleceniem:

Gdy mamy już pogram musimy go skompilować. Najpierw zainstalujemy pakiety, które są do tego niezbędne:

Teraz już możemy rozpakować pobrane archiwum oraz wykonać kompilację:

Nie jestem entuzjastą kompilacji na Raspberry, ale ten projekt jest na tyle mały, że zajmuje to tylko chwilkę. Teraz możemy uruchomić nasz serwer udostępniający obraz z kamery:

Aby sprawdzić, czy wszystko działa otwieramy przeglądarkę www i wpisujemy adres IP Raspberry. Port na którym działa serwer to 8090, podaliśmy taki w linii poleceń.

W moim przypadku Raspberry Pi miało adres 192.168.1.10, więc otworzyłem w przeglądarce http://192.168.1.10:8090 zobaczyłem wtedy następującą stronę:

Zrzut ekranu 2015-10-31 o 18.07.59

Mjpg-streamer w praktyce.

Dalej musimy wybrać zakładkę Stream, ponieważ później będziemy zainteresowani samym obrazem. Oczywiście warto popatrzeć na pozostałe możliwości jakie daje mjpg-streamer.

Opcje dostępne w mjpg-streamer.

Jak widzimy w podpowiedzi, obraz jest dostępny pod adresem /?action=stream. Możemy to sprawdzić wpisując w przeglądarce adres: http://192.168.1.10:8090/?action=stream

Dostęp do strumienia z obrazem.

Sterownik silników

Kolejnym elementem bez którego nie zbudujemy robota jest układ sterowania. Do budowy prostego robota wręcz idealnie nadaje się moduł RPi Motor HAT produkowany przez firmę MSX Elektronika.

MSX_moduly_RPI_6

Moduł sterownika silników od MSX Elektronika.

Podstawowe cechy modułu to:

  • dwa popularne mostki TB6612 – datasheet
  • układ PCA9685 jako 16-kanałowy PWM – datasheet
  • 4 wolne wyjścia PWM np. do sterowania serwomechanizmami
  • przetwornica impulsowa do zasilania modułu oraz RPi

Jak widzimy, moduł pozwala nie tylko sterować silnikami, daje też możliwość zasilania całego robota. Dzięki wbudowanej przetwornicy wystarczy podłączyć akumulator, a moduł będzie zasilał własną logikę oraz płytkę Raspberry Pi.

Moduł może być też zasilany z 5V, czyli z płytki RPi – dzięki temu możemy również robota zasilać z USB. Jest to bardzo wygodne rozwiązanie podczas programowania.

Na początek powinniśmy zapoznać się ze schematem płytki oraz sterowaniem silnikami. Poniżej możemy zobaczyć jak mostek H jest połączony ze źródłem sygnału PWM:

Schemat modułu RPi HAT.

Schemat modułu RPi HAT.

Układ PCA9685 jest wyposażony w 16 wyjść PWM oznaczonych CH0-CH15. Jak widzimy wyjścia CH0, CH1, CH14 i CH15 są dostępne na złączu, więc możemy je wykorzystać do rozszerzania możliwości naszego robota, np. do sterowania serwomechanizmami.

Pozostałe wyjścia PWM są podłączone do mostków TB6612. Ciekawostką jest wykorzystanie PWM również do sterowania kierunkiem pracy mostka, czyli jako zwykłe wyjście GPIO. Jest to o tyle dobre rozwiązanie, że RPi ma mało wolnych wyprowadzeń, a w ten sposób wykorzystując i2c można sterować wszystkimi wejściami TB6612. Jako przykład zobaczmy kanał A mostka. Do jego sterowania wykorzystywane są 3 wejścia: PWM_A, IN_A1, IN_A2:

  • PWM_A – jest podłączone do wyjścia CH2
  • IN_A1 – CH4
  • IN_A2 – CH3

PWM_A odpowiada za wypełnienie PWM wyjściwowego sygnału (a więc prędkość obrotową silnika). Linie IN_A1 oraz IN_A2 ustalają kierunek obrotów silnika, są zwykłymi liniami GPIO.

Program w Pythonie

Wykorzystamy Pythona. Programowanie w nim jest bardzo łatwe, a programy nie muszą być kompilowane, co ułatwia tworzenie programu, pozwala też na łatwe eksperymentowanie. Programować można zdalnie logując się przez ssh do Raspberry.

Nie bez znaczenia jest również gotowa biblioteka obsługująca PCA9685. Jej kod znajdziemy tutaj. Sterownik PCA9685 znajdziemy w pliku Adafruit_PWM_Servo_Driver.py.

Pierwszy krok to sprawdzenie, czy i2c działa. Wykorzystamy do tego program i2cdetect, który znajdziemy w pakiecie i2c-tools (jeśli go nie zainstalowaliśmy, najwyższy czas to zrobić).

Aby sprawdzić, czy mamy działający interfejs i2c piszemy:

Jeśli zobaczymy coś w rodzaju: /dev/i2c-1, oznacza to, że mamy interfejs i2c gotowy do działania. Brak interfejsu może wynikać z braku odpowiedniego modułu. Aby go załadować musimy wydać komendę:

Teraz powinniśmy i2c odnaleźć bez problemu. Wygodniej jest automatycznie ładować odpowiedni moduł, bez konieczności wydawania polecenia modprobe. Aby moduł był ładowany podczas startu systemu, możemy dodać odpowiedni wpis do pliku /etc/modules:

Interfejs jest gotowy, możemy sprawdzić, czy Raspberry jest w stanie wykryć płytkę sterownika. Wydajemy polecenie i2cdetect 1

Jak widzimy, moduł jest dostępny pod adresem 60 (hexadecymalnie). Czas napisać prosty skrypt w Pythonie, który uruchomi silnik. Napierw zobaczmy kod, później omówimy jego treść:

Pierwsze, co rzuca się w oczy, to mała ilość linijek programu. Tylko 5 + jedna od importu biblioteki.

W pierwszej linijce importujemy klasę PWM z biblioteki Adafruit_PWM_Servo_Driver. Klasa ta odpowiada za obsługę układu PCA9685, w który jest wyposażony nasz moduł. Kolejna linijka to utworzenie obiektu klasy PWM. Jako parametr podajemy adres układu – odczytaliśmy go wcześniej, więc podajemy 0x60. Następnie ustalamy częstotliwość pracy – 1kHz.

Nie jest to najlepsza wartość, może powodować piszczenie silników, ale na początek w zupełności wystarczy.

Ostatnie 3 linijki to właściwe sterowanie silnikiem. Najpierw ustawiamy wypełnienie PWM. Podajemy 1000 jako przykładową wartość – zakres jest od 0 do 4095, powinniśmy więc uzyskać ok. 25% wypełnienia. Następnie ustalamy kierunek pracy silnika. Podanie jako wypełnienia wartości (0, 0) daje wypełnienie 0%, więc na linii CH4 uzyskamy stan niski.

Aby uzyskać ciągły stan wysoki musimy zamienić kolejność parametrów i podajemy (4096, 0). W ten sposób na wyjściu CH3 uzyskamy stały stan wysoki.

Rozwijanie programu

Gdy wiemy jak sterować silnikiem możemy napisać skrypt, za pomocą którego będziemy mogli sterować całym robotem. Skrypt będzie uruchamiany z dwoma parametrami, każdy z zakresu od -4095, do 4095. Pierwszy parametr będzie ustalał kierunek i prędkość lewego silnika, a drugi prawego. Kod programu:

Program zawiera sporo powtórzeń, można więc go trochę udoskonalić. Po pierwsze wypadałoby zebrać definicje kanałów PWM do stałych (albo chociaż zmiennych). Po drugie sterowanie lewym i prawym silnikiem jest prawie identyczne, lepiej byłoby więc nie duplikować kodu.

Poniżej znacznie krótsza wersja (chociaż nie wiem czy czytelniejsza):

Mając ten program możemy zdalnie sterować robotem z poziomu konsoli:

Sterowanie silnikami z konsoli.

W tym momencie moglibyśmy podłączyć czujniki i rozwinąć skrypt o autonomiczną pracę naszego robota. Możemy też popracować nad zdalnym sterowaniem obecnej platformy.

Sterowanie robotem z przeglądarki

Używanie konsoli nie jest najwygodniejszą opcją do sterowania robotem. Może też być uciążliwe, gdybyśmy chcieli sterować robotem przykładowo przez smartfon lub tablet. Zamiast tego spróbujemy sterować robotem z poziomu przeglądarki.

Skoro zaczęliśmy programować w Pythonie, możemy wykorzystać serwer www oparty o ten język. Ja wybrałem projekt bottle – na stronie projektu znajdziemy dokumentację oraz poradnik użycia.

Postaram się pokazać jak łatwo jest w Pytonie stworzyć prostą aplikację webową (sterującą robotem). Najpierw piszemy bardzo prosty skrypt (podobny do przykładu ze strony Bottle):

Ostatnia linijka to uruchomienie serwera na podanym adresie i porcie. Znacznik @route określa ścieżkę, pod którą będzie dostępna nasza aplikacja. Fragment w nawiasach oznacza parametr (speed, typu int). Nasza aplikacja będzie więc dostępna pod adresem:

http://192.168.1.10:8080/robocik/left/1000

Gdzie 1000 to przykładowa wartość parametru. Program jako odpowiedź wyśle napis przekazany do funkcji template. Rezultat wygląda następująco:

Sterowanie robotem z poziomu przeglądarki.

Funkcja index(speed) jest wywoływana za każdym razem, kiedy otwieramy podany wcześniej adres. Możemy w niej wysterować silnik – wcześniej już napisaliśmy odpowiednie funkcje. Nowy skrypt wygląda więc następująco:

Tak krótki skrypt wystarczy do sterowania silnikiem przez przeglądarkę internetową. Pozostaje jeszcze dodać sterowanie prawym silnikiem oraz dodać stronę główną, tak żeby nie było konieczności wchodzenia ręcznie na strony /left i /right.

Nasz skrypt jest już gotowy, jednak jeśli spróbujemy otworzyć adres strony głównej sterowania robotem, czyli http://192.168.1.10:8080/robocik/ pojawi się błąd – brakuje szablonu index.

Okazuje się, że funkcja template() może przyjmować dwa rodzaje parametrów: kod HTML, który zostanie bezpośrednio przekazany do przeglądarki, albo nazwę pliku z szablonem strony. Pliki szablonów powinny być przechowywane w podkatalogu views i mieć rozszerzenie .tpl. Jednak są to w rzeczywistości zwykłe pliki HTML. Możemy taki plik utworzyć, a w nim umieścić kod, który będziemy chcieli wyświetlać w przeglądarce:

Końcowy efekt – przesuwając suwaczki zmieniamy prędkość poszczególnych silników:

Sterowanie robotem z poziomu przeglądarki.

Podsumowanie

Opisywany robot powstał przy okazji recenzowania modułów firmy MSX. Nie jest to w pełni ukończony projekt, raczej wstępny opis który można dalej rozbudować. Bez odpowiednich czujników nazywanie tej konstrukcji robotem jest niewątpliwie pewnym nadużyciem – w sumie jest to zdalnie sterowany pojazd wykorzystujący Raspberry Pi jako kontroler.

Aby zmienić go w prawdziwego robota, należałoby podłączyć czujniki lub dodać przetwarzanie obrazu. Jednak powyższy opis miał na celu pokazanie jak łatwo można przygotować zdalnie sterowaną platformę przy wykorzystaniu modułu RPi Motor HAT, Raspberry Pi oraz języka Python. Mam nadzieję, że pomysły w nim opisane przydadzą się innym osobom w ich konstrukcjach.

Dajcie znać w komentarzach, jeśli jesteście zainteresowani podobną tematyką!

Autor: Piotr (Elvis) Bugalski
Redakcja: Damian (Treker) Szymański

Powiadomienia o nowych, darmowych artykułach!

Komentarze

hob_bit

11:05, 15.12.2015

#1

Bardzo fajny i edukacyjny artykuł - Zawsze czekam i takich szukam w sieci, więc jak najbardziej kolejne tego typu mile widziane.

qwerty

21:36, 15.12.2015

#2

Taki sterownik silników do RPi w wersji pierwszej też jest dostępny?

Treker
Administrator

22:49, 15.12.2015

#3

qwerty, testowany model przeznaczony jest do RPI 2, niestety nie ma analogicznego modułu do RPI 1. Może znajdziesz coś u innych producentów?

betepok

9:45, 20.12.2015

#4

Bardzo ciekawy artykuł. Dzięki!

OldSkull

21:43, 20.12.2015

#5

Czy jesteś w stanie podać parametry transmisji? Na którym RPi ile fps, jaka rozdzielczość, jaki bitrate i jaka kompresja?

Poza tym bardzo fajny opis.

Elvis
Autor wpisu

22:21, 20.12.2015

#6

Niestety robot stał się "dawca organów" do dalszych prac... Ale jak chodzi o RPi to można uzyskać maksymalną rozdzielczość 2592x1944, przy 15 fps. Przy niższej rozdzielczości, liczba klatek na sekundę może wzrosnąć nawet do 90.

Sterownik obsługuje większość popularnych formatów, w tym MJPG, H.264, ale pełną prędkość oferuje tylko w przypadku I420.

betepok

10:15, 23.12.2015

#7

Czy do Raspberry Pi można zastosować sposób programowania z Arduino? Chodzi o pisanie kodu na na silniki. Wole pozostać przy C zamiast studiować kolejny język w tym przypadku Python.

Elvis
Autor wpisu

10:44, 23.12.2015

#8

Jeśli masz na myśli programowanie w C lub C++, to oczywiście Raspberry Pi można w tych językach programować. Natomiast o ile wiem nie ma Arduino IDE w wersji, która generowałaby kod dla malinki. Więc programować w C można, ale niestety tak samo jak Arduino.

Istnieje Arduino IDE działające na Raspberry, które tworzy kod dla AVR - ale to raczej nie pomoże.

betepok

11:05, 23.12.2015

#9

Czyli jeśli się biorę za Raspberry i programowanie peryferiów silniki, czujniki... krótko mówiąc budowę robota na Raspberry to lepiej zostać przy Pythonie?

Chumanista

11:18, 23.12.2015

#10

Nie no, możesz normalnie używać WiringPi z C, C++, co tylko chcesz właściwie.

http://wiringpi.com/

Ja bym używał C++, ale jeśli chcesz się nauczyć nowego języka to możesz.

Marooned

12:45, 23.12.2015

#11

betepok napisał/a:

Czy do Raspberry Pi można zastosować sposób programowania z Arduino?

Sposób, nie. Język możesz użyć, ale programy nie będą wyglądać tak samo. AVR programujesz niskopoziomowo, grzebiesz w rejestrach uC, ustawiasz timery, dzielniki, etc. Na RPi masz już system operacyjny i programowanie jest bliższe programowaniu na PC niż na uC. To, że można wykorzystać ten sam język nie sprawi, że programy będą wyglądać podobnie.

Chumanista

13:04, 23.12.2015

#12

Marooned, Zdziwiłbyś się:

#include <wiringPi.h>

int main (void)

{

wiringPiSetup () ;

pinMode (0, OUTPUT) ;

for (;;)

{

digitalWrite (0, HIGH) ; delay (500) ;

digitalWrite (0, LOW) ; delay (500) ;

}

return 0 ;

}

static void waitForEnter (void)

{

printf ("Press SELECT to continue: ") ; fflush (stdout) ;

while (digitalRead (AF_SELECT) == HIGH) // Wait for push

delay (1) ;

while (digitalRead (AF_SELECT) == LOW) // Wait for release

delay (1) ;

printf ("OK\n") ;

}

To z przykładów na stronie.

WiringPi bardzo przypomina biblioteki Arduino.

Elvis
Autor wpisu

13:13, 23.12.2015

#13

O tym na jak wiele sposobów można na RPi pobawić się pinami więcej jest na stronie: http://elinux.org/RPi_GPIO_Code_Samples

Jak widać WiringPi to tylko jedna z bardzo wielu opcji. Istnieje nawet możliwość bezpośredniej modyfikacji rejestrów - zupełnie jak na AVR.

Marooned

13:53, 23.12.2015

#14

Hm, no to widać niepotrzebnie zabrałem głos wprowadzając zamieszanie. Dzięki za sprostowanie.

Elvis
Autor wpisu

14:34, 23.12.2015

#15

Marooned, w tym co napisałeś jest bardzo dużo racji. Przede wszystkim na Raspberry nie mamy bibliotek z Arduino. Są oczywiście inne, nawet lepsze, ale na ogół trudniejsze do użycia. Największą zaletą Arduino jest prostota - bardzo łatwo jest napisać pierwszy program, dodawanie bibliotek nie stanowi problemu, a same biblioteki mają bardzo przyjemne w użyciu interfejsy.

Podobnie jest w przypadku Python-a. Sam język jest, jaki jest - można go lubić lub nienawidzieć. Ale każdy kto się nim bawił, doceni jakość bibliotek. Są po prostu łatwe, miłe i przyjemne.

Natomiast C/C++ na malince jest jak... C/C++ na PC. Mamy więc znane z Linuxa biblioteki i funkcje systemowe. Są one super wydajne, ogólne i przez lata dopracowywane. Jednak napisanie nawet prostego programu może okazać się nie lada przedsięwzięciem. Prosty przykład - napisz serwer www w Pythonie (gotowa biblioteka + kilka linijek kodu), a następnie zrealizuj to samo w C... Nawet wykorzystując gotowy framework będzie to duuużo trudniejsze (chociaż rezultat niewątpliwie może działać wydajniej).

betepok

13:45, 24.12.2015

#16

http://wiringpi.com/ - wygląda przyjaźnie (na pierwszy rzut oka).

"Jak wygląda sprawa bibliotek do czujników, urządzeń?" - Głupie pytanie odszczekuje:)

Po instalacji i2c-tools i podłączeniu płytki z jakiś powodów nie widzę interfejsu i2c. Pewnie czegoś nie doinstalowałem lub z braku doświadczenia pominąłem:( Z opisu winiak, że rozbiłem wszystko jak należy.

Zobacz wszystkie komentarze (22) na forum

FORBOT Damian Szymański © 2006 - 2017 Zakaz kopiowania treści oraz grafik bez zgody autora. vPRsLH.