Koncepcję robota opisałem już jakiś czas temu i od razu zabrałem się za realizacje hardware. Soft na razie został zepchnięty na dalszy plan, ale powoli to się zmienia. W ramach przygotowań do implementacji postanowiłem więc określić architekturę systemu. Jako, że to projekt po godzinach, nie ma żadnych narzuconych procedur dotyczących dokumentacji. Nie oznacza to jednak, że ma jej w ogóle nie być. Założenie jest takie, żeby pisać tylko tyle dokumentacji ile jest w danej chwili potrzebne i żeby rosła razem z postępem prac. W tej kwestii projekt potraktuję jako poligon doświadczalny.

Schemat architektury

Celem stworzenia schematu architektury nie było rozpisanie wszystkich szczegółów. To by zajęło dużo czasu, a i tak pewnie bym czegoś nie przewidział. Skupiłem się więc na identyfikacji podstawowych elementów, które będą musiały zostać zaimplementowane. Szczegóły będą dochodzić iteracyjnie w miarę postępów prac nad projektem. Dzięki temu robię tylko tyle dokumentacji ile w danej chwili jest potrzebne.

Bardzo często prace nad projektem wyglądają tak, że implementuje się konkretny moduł ze wszystkimi szczegółami, a potem przechodzi do następnego. Wtedy często okazuje się, że czegoś nie przewidzieliśmy i trzeba przerabiać napisany wcześniej kod. Żeby uniknąć takich problemów, mam zamiar zrobić najpierw szkielet aplikacji z pustymi funkcjami, żeby dobrze określić zależności między modułami i zdefiniować ich publiczne interfejsy.

Koncepcję architektury miałem już wcześniej w głowie. Należało ją tylko przelać na papier. Kiedy już byłem zadowolony z rezultatu, schemat postanowiłem zapisać w formie cyfrowej. Przy okazji nie spodziewałem się, jak trudno znaleźć odpowiednie narzędzie, żeby narysować schemat składający się z prostokątów połączonych strzałkami i do tego trochę tekstu. W końcu udało mi się stworzyć coś takiego:

Opis poszczególnych modułów

W górnej części schematu znajdują się elementy wykonawcze, z którymi procesor współpracuje. Niżej znajdują się moduły realizowane przez software. Pierwsza warstwa to moduły kontrolujące elementy zewnętrzne i zbierające z nich dane. Niżej znajduje się wewnętrzna logika systemu. W kolejnych akapitach opisze do czego służą poszczególne moduły.

Wall detection – moduł wykrywania ścian labiryntu. Jego zadaniem jest sterowanie diodami LED IR i fototranzystorami tak, aby odczytać odległość od ściany labiryntu dla każdego czujnika. Odczytane napięcie na fototranzystorze musi przekonwertować na odległość w milimetrach.

Motor control – moduł sterowania silnikami. Jako wejście otrzymuje zadaną prędkość liniową i kątową robota. Na tej podstawie wyznacza kierunek obrotu silników i wypełnienie sygnałów PWM. Z enkoderów odczytuje dystans pokonany w ostatnim okresie czasu. Wewnątrz modułu najprawdopodobniej znajdą się regulatory PID.

IMU sensor control – moduł obsługi czujników IMU. Realizuje komunikację z czujnikami przez I2C i udostępnia aktualne pomiary z akcelerometru, żyroskopu i magnetometru.

User interface – moduł obsługi przycisków i diod LED. Jego zadaniem jest odczytanie sekwencji wciskania przycisków przez użytkownika i uruchomienie na ich podstawie odpowiedniej komendy. Na razie przewiduję, dwie komendy: start ekspolracji i start speed runa.

Battery monitor – moduł monitorowania napięcia zasilania. Ma za zadanie wykrywać, kiedy napięcie zasilania jest zbyt niskie i zapalać diodę LED.

Debug – moduł debugowy. Informacje debugowe mają być drukowane przez interfejs UART, do którego można podłączyć moduł Bluetooth. Możliwe, że będzie również opcja wydawania robotowi komend przez konsolę debugową np. żeby wyświetlić aktualny rozkład wykrytych ścian w labiryncie.

Position estimation – moduł określający aktualne położenie robota w labiryncie. Do tego celu potrzebuje informacji o pozycji z enkoderów i z IMU. Dodatkowo do korekcji położenia wykorzystane zostaną informacje o odległościach od ścian. Wyjściem będzie aktualna pozycja robota w labiryncie. Moduł najprawdopodobniej będzie wykorzystywał Filtr Kalmana.

Labirynth mapping – moduł zapamiętujący rozkład ścian w labiryncie. Położenie ścian jest określane na podstawie aktualnej pozycji robota i zmierzonej odległości od ściany.

Pathfinding – moduł wyznaczający ścieżkę w labiryncie na podstawie rozłożenia ścian. Efektem jego działania jest wyznaczenie kolejnego pola na które powinien udać się robot. Podczas eksploracji będzie wskazywał pola, które trzeba odkryć, a podczas speed runa – najkrótszą drogę do celu. Do wyszukiwania ścieżek użyję algorytmu Floodfill.

Trajectory generation – moduł generujący trajektorię robota. Na podstawie aktualnej pozycji w labiryncie i punktu docelowego moduł wyznacza prędkość liniową i kątową potrzebną do zrealizowania zadania.

Podsumowanie

W opisach modułów starałem się pokrótce opisać ich zadania. Każdy z tych modułów to tak naprawdę temat na odrębny wpis, pewnie nawet niejeden. W miarę postępu prac każdy z tych tematów postaram się dokładnie omówić. Przy okazji mam zamiar prowadzić dokumentację projektu na githubowym wiki.