Ostatnio pisałem relację z NASA Space Apps Challenge. Dzisiaj pora na trochę szczegółów technicznych dotyczących naszej konstrukcji. Wybraliśmy temat „Can you build a …” i podtemat „Make Sense Out of Mars”. Celem było stworzenie sensora, który mógłby pomóc pierwszym kolonizatorom Marsa. Naszym czujnikiem był oczywiście łazik. Jego zadaniem miało być zbieranie informacji o najbliższym otoczeniu obozowiska z uwzględnieniem zróżnicowania terenu i potencjalnych niebezpieczeństw takich jak np. doły i kratery.
Założenia
Naszym głównym założeniem było skonstruowanie robota mobilnego z kamerą. To co miał dokładnie robić było już sprawą drugorzędną. Drugim ważnym założeniem było poznanie technologii, z którymi nie mieliśmy wcześniej styczności. Najważniejszą z nich był ROS – Robot Operating System. Działa on pod Linuxem i zawiera moduły obsługujące praktycznie wszystko co może przydać się w robotyce, a więc między innymi:
- Nawigację
- Mapowanie terenu
- Obsługę czujników
- Komunikację
- Obliczenia fizyczne i matematyczne
- Analizę obrazu
- Planowanie trasy
Do tego chcieliśmy wykorzystać technologie Intela:
- RealSense – kamerę zwracającą poza zwykłym obrazem również informacje o odległości.
- Movidius – jednostkę obliczeniową do sieci neuronowych. Dzięki temu obliczenia mogą odbywać się na pokładzie robota i nie trzeba ich przesyłać do chmury.
Początkowo był plan, żeby użyć Intel Euclid, czyli płytki z Linuxem i postawionym ROSem zintegrowanej z RealSensem i baterią. Niestety nie udało się otrzymać Euclida z próbek. A szkoda, bo – jak się miało później okazać – oszczędziłoby to nam ogromnej ilości problemów.
Poza tym chcieliśmy również wypróbować kilka bibliotek i tooli na mikrokontrolery:
- C++ – programu na pewno nie chcieliśmy pisać w C. Przez myśl przeszły mi jeszcze Rust i Ada, ale tam by było trudniej o gotowe biblioteki, które na hackatonie są gwarancją sukcesu.
- DistoRTOS – system operacyjny na STM32 napisany w C++. Od dłuższego czasu miałem zamiar go wykorzystać, ale jeszcze nie było okazji.
- Kvasir.io – biblioteka do obsługi rejestrów peryferiów bazująca na templatach.
- cmake – system buildowania bardziej przyjazny od pisania wszystkiego bezpośrednio w makefile.
- Ninja – też system buildowania, ale bardziej lowlevelowy. Cmake generuje skrypty dla make albo ninja do późniejszego wykonania właściwego builda.
Koncepcja systemu
Początkowa koncepcja systemu została narysowana na kartce:
Głównym elementem jest Raspberry Pi (korzystaliśmy z wersji 3B+) z działającym ROSem. Podłączamy do niego RealSense, Movidiusa i STM Discovery F4. W planach było również podłączenie do RPi czujnika IMU z akcelerometrem, żyroskopem i magnetometrem, ale na razie tego nie zrobiliśmy. Komunikacja z STMem odbywa się po UART. Głównym zadaniem STMa jest sterowanie czterema silnikami za pomocą mostków H. Jako podwozie użyliśmy gotowego kita Pirate 4WD z czterema kołami i silnikiem podłączonym niezależnie do każdego z kół. Logika zasilana jest z powerbanka, a silniki z LiPola 2S 800mAh. Z zasilaniem logiki trochę przesadziliśmy bo powerbank ma 30Ah, jest ciężki i zajmuje ogromną ilość miejsca, a energii daje tyle, że nigdy go nie rozładujemy. Ale akurat takiego mieliśmy pod ręką. Do tego wykorzystaliśmy jeszcze płytkę stykową, żeby rozprowadzić zasilanie z lipola na mostki H i połączyć z przyciskiem ON/OFF.
Przy okazji taka dygresja – jeżeli kiedykolwiek przyjdzie wam do głowy, żeby robić na hackatonie projekt hardware’owy, nie nastawiajcie się na zrobienie powalającego systemu. Projekty czysto software’owe są dużo prostsze do wykonania, nie trzeba się tyle wcześniej przygotowywać i nie trzeba mieć wszystkich elementów pod ręką. Dodatkowo w trakcie więcej rzeczy się może zepsuć, a realizowalność na hardware bardzo mocno weryfikuje wszystkie pomysły. Dlatego jak mierzycie w sukcesy, albo po prostu chcecie oszczędzić sobie pracy – skupcie się na samym sofcie. To samo tyczy się np. projektów na uczelnie.
Ok, to teraz pora na omówienie problemów, jakie mieliśmy.
ROS na Raspberry Pi
TLDR: Nie udało nam się postawić ROSa na Raspberry.
Przed hackatonem uruchomiliśmy pierwszy znaleziony w necie image na RPi z ROSem i działał. Myśleliśmy, że to wystarczy. Okazało się, że to starsza wersja, gdzie nie było części potrzebnych nam modułów. Głównie chodziło o RealSense. Jest to zastanawiające, ponieważ istnieje projekt TurtleBot wspierany przez duże firmy takie jak Intel, czy Microsoft. W specyfikacji można wyczytać, że wykorzystuje on właśnie Raspberry i RealSense. Nasz RealSense jest co prawda nowszy – wersja R300, a w TurtleBocie jest R200. Ale zgodnie ze specyfikacją oba korzystają z USB 3.0. Może to jest przyczyna problemów.
W każdym razie podczas hackatonu sprawdziliśmy kilkanaście różnych image’ów karty SD. Próbowaliśmy z Raspbianem i UbuntuMate. Każdy z nich trzeba było ściągnąć z neta a WiFi nie było najwyższych lotów i potrafiło się w trakcie zawiesić. Poza tym na niektórych imagach próbowaliśmy kompilować ROS ze źródeł na RPi. Trwało to długo i czasem się wieszała, prawdopodobnie z przegrzania.
Ostatecznie nie wykorzystaliśmy w ogóle ROSa, a RPi miało tylko proste streamowanie obrazu ze zwykłej kamery USB po WiFi.
Po hackatonie prawdopodobnie zrezygnujemy z Raspberry. Pojawiła się opcja, żeby wykorzystać płytkę zawierającą Intel Atom, który obsłuży ROSa i STM32F4, który zastąpi Discovery. Wtedy nie powinno być już żadnych problemów z podłączeniem RealSense.
RealSense
Samo RealSense najpierw próbowaliśmy uruchomić z PCta. Pierwszym problemem było znalezienie odpowiedniego kabla. Mieliśmy moduł RealSense ze specjalnym złączem i drugą płytkę – konwerter na USB 3.0 micro B. Po podłączeniu okazało się, że w naszym RealSense działa tylko czujnik odległości. Kamera RGB nie przesyła obrazu. Nie spodobało się to programowi z oficjalnego SDK, który z tego powodu wywalał exceptiony. Trzeba było odpowiednio zmodyfikować kod. Czujnik jest bardzo delikatny i nie mieliśmy go w wersji z obudową. Możliwe, że się uszkodził. Drugą, mniej prawdopodobną, opcją są jakieś problemy z driverami.
STM32F4 Discovery
Początkowo oczywiście mieliśmy problemy z podłączeniem debugera do PC. Wykorzystywaliśmy nowe Discovery i oczywiście na początku nie działało z OpenOCD. Okazało się, że skrypty dla płytki korzystają z stlink-v2, a powinny z stlink-v2-1. Między tymi wersjami zmieniły się ID device USB i system ich nie rozpoznawał.
Plany wykorzystania bibliotek również się nie udały. Kvasir na razie jest dopiero w wersji eksperymentalnej. Na masterze nie ma naszego procka. Ostatni commit z końca 2017. Ostatni release – i za razem pierwszy – (0.1.0) z 2016. W sumie można było to wcześniej sprawdzić i nie robić sobie nadziei.
DistoRTOS – miałem nadzieję, że łatwo się doda do naszego projektu i uruchomi wszystko od ręki. Są skrypty dla F4 Discovery i komenda do wklejenia w readme. Liczyłem jednak, że dodam go bezproblemowo do istniejącego projektu. Po chwili stwierdziłem, że na DistoRTOS będę potrzebował więcej czasu. Dlatego zapadła decyzja, żeby wszystko klepać w czystym c++ a do DistoRTOSa wrócić po hackatonie.
Pisanie w c++ na czystym projekcie też nie przebiegło bez problemów. Moje skrypty linkera nie radzą sobie z konstruktorami statycznych obiektów, które są inicjalizowane w startupie przed wejściem do funkcji main. To też temat na dłuższą rozkminę, więc zostaje do naprawienia później. Prawdopodobnie użyję skrypty z DistoRTOSa, gdzie ten problem jest rozwiązany. Przy okazji producenci procków np. STM udostępniają tylko proste skrypty linkera, które wspierają jedynie C.
Bez Kvasira trzeba było na bieżąco wymyślić sposób obsługi rejestrów sprzętowych. Początkowo chciałem wzorować się na rozwiązaniu opisanym na blogu Faebhas. Jednak ostatecznie okazało się, że jest to przerost formy nad treścią. Drivery zostały zrealizowane jako klasy, inicjalizacja była ukryta za warstwą abstrakcji. Przerwania były obsługiwane przy wykorzystaniu klas z metodami statycznymi. Opiszę jeszcze dokładniej tą koncepcję. Stworzyłem ją na szybko i docelowo pewnie trzeba będzie ją ulepszyć.
Kolejną rzeczą jakiej nie udało się dodać do STMa była integracja z biblioteką ros_serial_client. Implementuje ona komunikację z ROSem. Dzięki temu silniki mogą być obsługiwane na STMie, a RPi wyśle tylko odpowiednie komendy na topic obsługiwany przez tą bibliotekę. Niestety repo nie jest wystarczające do uruchomienia biblioteki. Folder rosserial_msgs nie zawiera źródeł, trzeba je sobie samemu wygenerować. Są do tego odpowiednie skrypty, które z kolei zależą od Catkina, który służy w ROSie do buildowania i korzysta z cmake i pythona. Kiedy w niedzielę rano doszedłem do tego Catkina i spytałem co to jest chłopaki, którzy męczyli się już cały poprzedni dzień z ROSem tylko mnie wyśmiali. Mieli nawet na RPi z poprzednich kompilacji wygenerowane jakieś rosserial_msgs, ale jak po uruchomieniu Catkina zamiast sourców pojawiły się kolejne skrypty do uruchomienia, daliśmy sobie spokój.
Jedyną nową rzeczą, która faktycznie zdała egzamin był cmake i ninja. Rebuild całego projektu od zera z użyciem ninja zajmuje mniej niż kompilacja inkrementalna z makefile. I robiła się zwykle w mniej niż sekundę. Sam cmake też mocno upraszcza tworzenie skryptów buildowania.
Składanie Hardware’u
Organizatorzy udostępnili nam drukarkę etykiet, dzięki czemu mogliśmy opisać przewody. Część z nich się odkleiła, ale i tak to bardzo fajny wynalazek. Na niewykorzystane goldpiny naklejaliśmy taśmę, żeby je zaizolować. Miałem już wcześniej doświadczenia z ruchomymi evalboardami, które na poruszającym się robocie powodowały zwarcia i spalały całą elektronikę. Podłączanie wszystkiego do kupy pozostało oczywiście na ostatnią chwilę. Na szczęście poszło bardzo sprawnie i udało się wszystko odpalić za pierwszym razem. Korzystałem tu z wcześniejszego doświadczenia i po podłączeniu każdego modułu starałem się na bieżąco sprawdzać, czy wszystko działa. W trakcie podłączania trzeba było tylko przelutowywać przewody od przełącznika ON/OFF. Cały zespół uczestniczył w akcji i przypominało to trochę operację chirurgiczną. Ostatecznie wszystko działało i nic nie spaliliśmy, więc pod tym względem pełny sukces.
Co dalej?
Będziemy na spokojnie rozwijać dalej projekt. Uruchomimy niedziałające elementy, to co się nie da uruchomić zastąpimy nowymi. Części działające na szybko podmienimy na takie od zaprzyjaźnionych firm. Sam łazik będzie wykorzystywany jako atrakcja na różnych eventach.Dodatkowo jest plan, żeby zrobić z niego pomocnika biurowego i może nawet przerodzi się w komercyjny produkt. Na razie rozwijamy go jako open source. Kod można zobaczyć na GitHubie. Na razie za wiele tam nie ma – tylko prosty programik na STM, który steruje silnikami, żeby w kółko wykonywały te same operacje. Z czasem dodamy do projektu też kod na ROSa i pomyślimy o jakimś procesie prowadzenia projektu wykorzystującym toole do CI, statycznej analizy itp.
Na koniec jeszcze filmik z działającym robotem:
11 listopada 2018 at 18:18
Hej!
Szkoda że nie udało wam się uruchomić distortos w waszym projekcie. Jeśli możesz to napisz w czym był problem – feedback jest dla mnie bardzo ważny, w końcu chciałbym aby to nie był projekt tylko dla mnie. Przypuszczam że podeszliście do distortos trochę jak do – np. – FreeRTOSa, że można wziąć pliki RTOSa, wrzucić je gdzieś, skompilować, w projekcie wywołać jakąś funkcję i już. Tymczasem distortos nie do końca jest czymś w stylu „modułu”, to trochę bardziej „framework”, ma swój własny system kompilacji, skrypty linkera, tablice wektorów, startup dla mikrokontrolera, całą gamę niskopoziomowych inicjalizatorów które odpalają się automatycznie, dzięki czemu `main()` jest już (prawie) zwyczajnym wątkiem, nie trzeba nic kombinować. Z tego względu faktycznie bardzo trudno będzie zintegrować distortos z istniejącym już projektem, który ma własny system budowania i podstawowe pliki… Alternatywa – bardzo rozbudowany opis takiej integracji, z wieloma miejscami w których można popełnić jakiś błąd – nie jest bardzo kusząca, więc poszedłem z projektem właśnie w stronę „frameworka”.
11 listopada 2018 at 18:27
Cześć, po hackatonie na spokojnie usiadłem do DistoRTOSa i go zbudowałem. Problem polegał na tym, że na hackatonie próbowałem uruchomić wersję z ostatniego taga. Założyłem, że będzie najbardziej stabilna. I dla niej sposób kompilacji nie pokrywał się z opisem w readme. Po hackatonie wziąłem wersję z mastera i poszło od ręki. Tak więc docelowo w tym projekcie użyjemy DistoRTOSa. Nie wiem jeszcze jak podejdziemy do integracji z aktualnym projektem. Bardzo możliwe, że weźmiemy cały framework.
11 listopada 2018 at 18:35
No ale przecież plik README.md z ostatniego taga (0.6.0) nie zawierał żadnego opisu kompilacji (ten jest na stronie projektu). W pliku tym pojawiły się jakieś opisy dopiero po przejściu na CMake’a. Co do planu integracji, to na pierwszy ogień naprawdę mocno bym polecał użycie distortos jako frameworka, próby zrobienia takiej integracji ręcznie z dużym prawdopodobieństwem będą dosyć problematyczne… Staram się zawsze aby ten ogólny framework był w miarę uniwersalny i niczego specjalnie nie narzucał jeśli nie jest to konieczne, ale wiadomo że moje rozumienie uniwersalności niekoniecznie jest… uniwersalne (; tak więc wszelkie uwagi mile widziane!
11 listopada 2018 at 18:55
Bo ja wtedy miałem readme otwarty na stronie, a repo na kompie cofnięte do taga i nie zajarzyłem, że w tamtej wersji było puste 😀 Komenda nie działała, bo nie było plików toolchaina, znalazłem inne w folderach projektu, ale też nie działały i dałem sobie spokój. W trakice hackatonu już po kilku(nastu) godzinach takie rzeczy naprawdę są dużo trudniejsze, niż się może wydawać 🙂 W każdym razie DistoRTOSa integrujemy jak tylko uporamy się z ROSem przynajmniej w podstawowej konfiguracji na RPi + rosserial.
11 listopada 2018 at 19:11
Keep me posted! Z chęcią pomogę i odpowiem na pytania które z pewnością się pojawią!