Witam,
chciałbym przedstawić Wam mój projekt zaliczeniowy z przedmiotu Systemy Mikroprocesorowe. Przez większość zajęć realizowaliśmy zagadnienia związane z Arduino, więc właśnie na tej platformie zrealizowałem swój projekt. Mnóstwo razy w internecie spotykałem się z pytaniami i problemami dotyczącymi połączenia Arduino i Matlaba, więc zdecydowałem się opublikować moją pracę – może ktoś z niej skorzysta. Na wstępie – Arduino można połączyć z Matlabem na dwa sposoby: za pomocą nakładki (do ściągnięcia w jednym z linków poniżej), dzięki której naszą płytkę możemy zaprogramować pisząc komendy w Matlabie lub za pomocą portu szeregowego. Dzięki pierwszej metodzie mamy od razu dostęp do wszystkich zmiennych, które możemy przedstawiać na wykresie lub realizować na nich różne operacje matematyczne. Ciekawą możliwością jest również napisanie prostego interfejsu graficznego do obsługi naszego programu na Arduino, więcej o tej metodzie: tutaj lub tutaj. Przy pracy z Matlabem polecam szukanie pomocy na oficjalnej stronie Mathworks, wszystkie funkcje są tam dokładnie opisane. Mimo to, samo programowanie mikrokontrolera w Matlabie jest dosyć toporne i przy moim projekcie zdecydowałem się na programowanie klasyczne, a komunikację z Matlabem nawiązałem drugim sposobem.
Założenia projektowe
Chciałem zbudować małe ramię robota zdolne do uniesienia i przemieszczenia małych obiektów. Podczas pracy dotarło do mnie, że samo sterowanie ręczne to trochę mało, ponieważ to tylko ładnie ubrane ruszanie czterema serwami, więc zacząłem pracować nad automatycznym znajdowaniem obiektu za pomocą kamerki internetowej i przenoszeniem go przez robota.
Jak to działa?
Myślę, że nie ma sensu omawiać konstrukcji projektu. Całość to po prostu cztery serwomechanizmy połączone jeden z drugim. Wszystko zmontowane z tego, co miałem pod ręką, ramię między trzecim serwem, a chwytakiem oraz biała podstawka to wycięte spienione pcv.
Wykorzystane elementy, o których warto wspomnieć:
- klon Arduino Uno
- 2 * serwo typu standard
- 2 * serwo typu micro
- chwytak
- ładowarka do PSP z uciętą końcówką, jako zasilacz 5V o wydajności prądowej 2A.
- kamerka internetowa
- mysz komputerowa
Szukając ciekawych spoobów na sterowanie kilkoma serwami wpadłem na wykorzystanie myszy komputerowej. Wykorzystałem tę bibliotekę oraz ten schemat podłączenia. Całość nie powinna przysporzyć większych problemów. Strasznie irytująca okazała się praca nad zdjęciem izolacji i przylutowaniem kabelków od myszki, ponieważ są niesamowicie cienkie i łatwo się urywają.
Schemat układu:
A co z częścią automatyczną?
Niestety – to rozpoznawanie pozycji jest trochę oszukane. System faktycznie rozpoznaje gdzie leży obiekt, ale tylko, jeżeli znajduje się on w jednym z trzech wybranych miejsc.Napisanie programu zdolnego do pracy z obiektem znajdującym się gdziekolwiek w przestrzeni roboczej manipulatora byłoby dużo bardziej złożone, wymagałoby to między innymi rozwiązania odwrotnego zadania kinematyki i prawdopodobnie większość czasu, który poświęciłem na ten projekt pracowałbym nad czymś niezwiązanym z przedmiotem.
Aby Matlab mógł się dogadać z naszą kamerką internetową musimy mu zainstalować odpowiednią nakładkę. W górnym pasku ikon klikamy Add-Ons, wybieramy Get Hardware Support Packages, wybieramy Install from Internet i szukamy USB Webcams. Znowu odsyłam do strony Mathworks, z której sporo można się dowiedzieć.
Poniżej opiszę krok po kroku algorytm obróbki obrazu. Do artykułu dołączę kod źródłowy dla Arduino i dla Matlaba, a także “raport” z obróbki obrazu. Poniżej opiszę tylko najważniejsze funkcje tego skryptu. Jeżeli w poniższym opisie coś będzie niezrozumiałe – zerknij do kodu, może tam to się wyjaśni.
Na początek muszę zapisać sobie zdjęcie, które będzie stanowiło punkt odniesienia. Kamerkę ustawiam dokładnie tak, jak będzie stała przez cały czas działania programu, a następnie robię zdjęcie sceny bez taśmy klejącej (mojego obiektu, który staram się uchwycić):
Takie zdjęcie pobieramy tylko jedno, będziemy z niego korzystać przy każdej iteracji skryptu.
Obraz możemy zapisać następująco:
1 2 3 4 5 |
cam=webcam; %stworzenie połączenia z kamerką (w Matlabie komentarze oznaczamy %, a nie //) a=snapshot(cam); %zrobienie zdjęcia z aktualnego kadru kamerki imshow(a); % wyświetlenie zdjęcia |
Powinniśmy zobaczyć teraz okno z takim zdjęciem, klikamy na odpowiednią ikonkę i zapisujemy obraz w folderze roboczym matlaba jako plik .fig.
Kolejne kroki będą wykonywane co iterację.
Pobieramy aktualny kadr kamerki:
Przenosimy obydwa obrazy na odcienie szarości:
Następnie odejmujemy czarno biały obraz aktualny od wzorcowego. Matlab widzi obrazy jako macierz pikseli, więc jeżeli obrazy są takich samych rozmiarów, może je odjąć:
Na wynikowym obrazie “bardziej białe” są obszary, w których obrazy różnią się najmocniej. W naszym przypadku – to taśma + jakieś zakłócenia.
To był ostatni krok w odcieniach szarości, teraz przechodzimy na zero-jedynkową formę, czyli obraz czarno biały. Idea jest taka: Wystarczająco różne od zdjęcia odniesienia fragmenty będą białe, inne czarne.
Ostatni krok: usuwam wszystkie obszary o polu mniejszym, niż ustawione pole graniczne:
Pozostaje nam wyłuskać z tego obrazu położenie obiektu. Robię to za pomocą funkcji regionprops:
1 |
dane = regionprops(imgclean,'Centroid','MajorAxisLength'); |
imgclean to mój ostateczny obraz, Centroid odpowiada za zwrócenie środka masy obiektu, w tym przypadku będzie to dwuelementowy wektor zawierający współrzędne x oraz y środka białego pola. MajorAxisLenghth zwraca promień obszaru, ostatecznie nie wykorzystałem tego parametru.
Mając już dane dotyczące położenia naszego obiektu, wystarczy napisać warunek: jeżeli x jest taki, a y taki, wyślij do Arduino “1”, jeżeli taki, a taki, wyślij “2”, a jeżeli jeszcze taki i taki, to wyślij “3”.
Jak zrealizować to wysyłanie? Na początku używam funkcji:
1 |
delete (instrfind('PORT','COM10')) |
Zapewnia ona wyczyszczenie portu szeregowego.
Następnie :
1 2 |
s=serial('COM10','BAUD', 9600); fopen(s); |
Te funkcje tworzą i otwierają komunikację szeregową z Arduino.
Pozostaje już tylko wysłać dane:
1 2 3 4 5 6 7 |
if (srodek(1)>ax-30)&&(srodek(1)<ax+30)&&(srodek(2)>ay-30)&&(srodek(2)<ay+30) fprintf(s,1); elseif (srodek(1)>bx-30)&&(srodek(1)<bx+30)&&(srodek(2)>by-30)&&(srodek(2)<by+30) fprintf(s,2); elseif (srodek(1)>cx-30)&&(srodek(1)<cx+30)&&(srodek(2)>cy-30)&&(srodek(2)<cy+30) fprintf(s,3); end |
zmienne ax,ay,bx,by,cx,cy tworzę przed uruchomieniem całego programu, ustawiajac taśmę na miejscach 1,2,3 oraz odczytując wyznaczone przez program współrzędne środka dla tych położeń.
W tym momencie do Arduino zostaje wysłana cyfra 1, 2 lub 3. Robot będzie ignorował przychodzące do niego sygnały dopóki nie wcisnę buttona, wtedy odbierze tę aktualną pozycję i zgarnie taśmę. W kodzie Arduino “łopatologicznie” zapisałem trajektorię ruchu, którą robot musi wykonać, aby chwycić taśmę znajdującą się w każdym z położeń, czyli po prostu wpisałem, w jakiej kolejności muszą się ustawiać kolejne serwa i na jaką pozycję.
Ważna sprawa – kiedy otwieramy komunikację szeregową, Arduino resetuje się. Wystarczy, że klikniemy na “lupę” w kompilatorze Arduino IDE i możemy zaobserwować reset urządzenia. Aby uniknąć takiego ciągłego resetowania mojego robota, przylutowałem kondensator 10uF między pin RESET i GND (+ kondensatora do RESETu), szczegóły tutaj.
Wiem, że ten opis może nie wyjaśniać wszystkiego, jeżeli chcesz się czegoś dowiedzieć lub jest to niejasne – pytaj. Dla zainteresowanych tematem na tyle, żeby przejrzeć załączony kod z Matlaba – zdaję sobie sprawę, że nie jest on optymalny. Kilka operacji jest wykonywanych niepotrzebnie za każdą iteracją, zamiast tylko raz, ale całość jest w miarę prosta w zrozumieniu.
Komunikacja w drugą stronę
Udało nam się ustanowić komunikację w jedną stronę – Arduino odbiera sygnały wysyłane przez Matlaba. Czy da się zrobić coś podobnego, ale tak, aby to Arduino coś wysyłało, a Matlab odbierał i np. tworzył z tego wykresy w czasie rzeczywistym? Oczywiscie, że tak. Pamiętajmy jednak, że komunikacja za pomocą portu szeregowego nie może następować w dwie strony jednocześnie, musimy się zdecydować, czy odbieramy, czy nadajemy sygnał.
Jak wysłać sygnał z Arduino do Matlaba? Dokładnie tak samo, jak wysyłamy go do okienka z podglądem portu szeregowego – Serial.println() w zupełności wystarczy:
1 2 3 4 |
Serial.println(pos1); Serial.println(pos2); Serial.println(pos3); Serial.println(pos4); |
Po stronie Matlaba wygląda to następująco:
1 2 3 4 |
delete (instrfind('PORT','COM6')) %czyszczenie portu szeregowego arduino=serial('COM6','BaudRate',9600); fopen(arduino); x=fscanf(arduino); |
Po wykonaniu takich instrukcji, do zmiennej x przypiszemy zmienną, którą Arduino aktualnie wysyła przez port szeregowy. W moim projekcie wysyłałem 4 zmienne (pozycję każdego serwa) i chciałem obserwować je na czterech różnych wykresach, rozwiązałem to wysyłając je z Arduino do seriala jedno pod drugim, a w Matlabie:
1 2 3 4 |
y1(x)=fscanf(arduino,'%f',[1,1]); y2(x)=fscanf(arduino,'%f',[1,1]); y3(x)=fscanf(arduino,'%f',[1,1]); y4(x)=fscanf(arduino,'%f',[1,1]); |
%f oznacza, że chcę odebrać liczbę, [1,1] definiuje rozmiar, czyli chcę odebrać tylko jeden element.
Używanie nakładki dla Arduino
Aby ją zainstalować, możemy skorzystać z jednego z linków ze wstępu lub z opcji Add-Ons -> Get Hardware Support Packages -> Install from Internet -> Arduino.
Zainstalowanie tego dodatku da nam do dyspozycji szereg funkcji umożliwiających pracę z Arduino, ich listę znajdziecie tutaj.
Jak wspominałem – napisanie całego programu za pomocą Matlaba jest wykonalne, ale strasznie toporne, a w dodatku mamy tutaj do dyspozycji ograniczone możliwości. Już pierwszym problemem jest chociażby wykorzystywanie jakichkolwiek zewnętrznych bibliotek do Arduino.
Mimo tego, że Matlab nie nadaje się do pisania funkcjonalnych programów na Arduino, można go wykorzystać w ciekawy sposób. Wpadłem na to rozwiązanie kiedy szukałem zamiennika dla oscyloskopu przy obserwowaniu sygnałów zmiennych w czasie. Świetnie spisał się następujący kod:
1 2 3 4 5 6 7 8 9 |
a=arduino; i = 1; tic while (toc<10) b(i) = a.readVoltage(0); plot(b); drawnow; i=i+1; end |
Nasz sygnał podłączamy do analogowego wejścia Arduino i już możemy podziwiać wykresy.
Tic rozpoczyna odliczanie czasu, toc przujmuje wartość równą ile sekund minęło od wywołania tic. Nasz program będzie rysował wykres napięcia na pinie A0 przez 10 sekund. Przykładowe wykresy:
Jak widać, na osi poziomej nie mamy czasu, a numer próbki, ale to niewielka strata.
Załączniki i podsumowanie
Połączenie naszego mikrokontrolera z Matlabem jest naprawdę proste. Daje to kilka ciekawych możliwości, które przedstawiłem w tym artykule, ale to tylko te najprostsze. Arsenał tego połączenia jest dużo bardziej imponujący. W razie pytań lub problemów pytajcie – w miarę możliwości chętnie pomogę.
Połączenie Arduino i Matlaba to świetny pomysł. Niewiele jest takich szczegółowych opisów po polsku :( Dzięki za ten wpis!
Projekt zaawansowany. Gratulacje!
Ciekawie opisany sposób rozpoznawania obrazu – nie miałem pojęcia jak takie rzeczy są robione.
Teraz łyżka dziegciu – nie jest to niestety projekt dla domowych majsterkowiczów mn. dlatego że: użyty soft nie jest tani (poza zasięgiem domowego dłubacza), mała szansa na praktyczne zastosowanie w domu i zagrodzie :)
Nie chcę krytykować bo doceniam i wiedzę autora i chęć podzielenia się nią z innymi ale lepszym miejscem na tego typu projekty byłby np. Forbot lub podobne forum hightech :)
Darmowym zamiennikiem Matlaba jest Scilab, wersja niesamowicie zubożona, w której prawdopodobnie będzie brakowało tej wygodnej nakładki do podłączenia kamerki, ale jestem przekonany, że komunikacja z wykorzystaniem Seriala będzie działała tak samo ;)
Hej, super artykuł, zastanawia mnie tylko jedna rzecz: Po co użyłeś Matlaba?
Darmową i nieskomplikowaną alternatywą byłoby użycie pythona + openCV :)
Po prostu nigdy nie miałem styczności z openCV ;) Do tego w miarę często korzystamy na studiach z Matlaba, więc ten projekt to był swego rodzaju trening z tym programem.
Właśnie miałem napisać że te zdjęcia/zrzuty ekranu przypominają mi OpenCV. Znasz jakieś dobre tutoriale?
Niestety tylko po angielsku (ale przynajmniej jest wymówka, żeby go troszkę podszkolić: :) ). Ale ogólnie polecam przejrzeć blog tego gościa: http://www.pyimagesearch.com/
Na przykład to: http://www.pyimagesearch.com/2015/05/25/basic-motion-detection-and-tracking-with-python-and-opencv/
Cały kod jest bardzo ładnie opisany, tak więc dość łatwo można go zrozumieć :)
Dzięki:) Angielski to oczywiście nie problem, jakoś sobie z nim radzę. Oczywiście chętnie też popraktykuję go bardziej. Ten tutorial wygląda całkiem w porządku. Wcześniej patrzyłem tutoriale na oficjalnej stronie OpenCV i nie wyglądają one aż tak zachęcająco.
Na jakiej wersji matlaba pracujesz?
R2014a
Właśnie się zastanawiałem jak skutecznie połączyć Arduino + Matlab, tak żeby możliwe było korzystanie z bibliotek Arduino, a nie tylko najprostsze komendy jak digitalWrite. Dzięki Wielkie – super poradnik!