Kurs budowy robotów – #4 – pierwsze programowanie

Kurs budowy robotów – #4 – pierwsze programowanie

Jeśli nasz robot stoi już na 3 kołach, to możemy spokojnie zabrać za główną część kursu, czyli programowanie. Nie użyjemy gotowej biblioteki, wszystko napiszemy wspólnie od pustego szkicu. Dzięki temu opisane zagadnienia będzie można łatwo zastosować przy innych projektach.

Zajmiemy się również częstym problemem, który pojawia się przy budowie pierwszych robotów.

Na tym etapie zakładam, że każdy zapoznał się już z wykorzystywanym shieldem, który opisałem w 2 części tego kursu. Co więcej, obok Waszego komputera powinien stać już złożony robot, który jest gotowy do programowania.

W kursie skupimy się na budowie i programowaniu robotów, nie ma więc tutaj miejsca na opis podstaw Arduino. Osoby, które będą miały problemy z podstawami programowania będę starał się odsyłać do odpowiednich miejsc kursu Arduino.

Aby nie uszkodzić robota i napędów...

Zanim robot ruszy warto wprowadzić w życie kilka nawyków, które znacznie zmniejszą szansą na uszkodzenia konstrukcji. Podczas programowania odłączaj zasilanie dopływające do napędów (za pomocą odpowiedniej zworki na shieldzie). Dzięki temu robot m.in. nie spadnie z biurka...

Staraj się unikać sytuacji, gdy koła robota będą zablokowane. Konkretnie chodzi o sytuacje, w których dostarczamy prąd do silników, ale przez zablokowanie kół nic się nie dzieje. Może tak się zdarzyć, gdy trzymamy robota za koła i blokujemy ich ruch ręką lub np. gdy podczas jazdy wjedzie on w jakąś przeszkodę i będzie cały czas próbował "wjechać w ścianę".

Sytuacja taka jest groźna z dwóch powodów. Po pierwsze możliwe są uszkodzenia mechaniczne kół, silników oraz przekładni. Po drugie, podczas zatrzymania silników znacznie rośnie pobierany przez nie prąd, może to doprowadzić do przegrzania układu sterującego napędami.

Bezpieczne wgrywanie nowego programu

Podczas wgrywania programów staraj się postępować zgodnie z poniższymi krokami:

  1. Zdejmij zworkę zasilania napędów.
  2. Podłącz przewód USB i włącz zasilanie przełącznikiem suwakowym.
  3. Wgraj nowy program.
  4. Odłącz przewód USB.
  5. Wyłącz zasilanie.
  6. Załóż zworkę zasilania napędów.
  7. Postaw robota na ziemi (lub trzymaj w powietrzu za sklejkę).
  8. Włącz zasilanie.
Zestaw elementów do kursu

 999+ pozytywnych opinii  Gwarancja pomocy  Wysyłka w 24h

Części pozwalające wykonać ćwiczenia z kursu budowy robotów dostępne są w formie gotowych zestawów! W komplecie znajdują się elementy mechaniczne (w tym koła i silniki), baterie oraz shield!

Zamów w Botland.com.pl »

Sterowanie silnikami z użyciem mostka H (DRV8835)

Nasze Arduino może sterować silnikami korzystając z pośredniczącego układu - mostka H. Silniki pobierają zbyt duży prąd, aby można je było bezpiecznie podłączyć do mikrokontrolera.

Podstawy opisane w tamtym artykule są nadal aktualne, jednak wykorzystany tutaj mostek (DRV8835) wymaga odrobinę innego, prostszego sterowania. Nasz shield łączy mostek H z Arduino tylko za pomocą 4 sygnałów. Dwa z nich odpowiadają za kierunek obrotów, a pozostałe za prędkość silników:

  • Pin 4 odpowiada za kierunek obrotów silnika lewego.
  • Pin 5 (PWM) odpowiada za prędkość silnika lewego.
  • Pin 6 (PWM) odpowiada za prędkość silnika prawego.
  • Pin 9 odpowiada za kierunek obrotów silnika prawego.

Zasada działania dla poszczególnych silników jest następująca:

Przykładowo kręcenie lewym silnikiem do przodu z prędkością XX wymaga:

  • ustawienia na wyjściu nr 4 sygnału 0,
  • ustawienia  na pinie nr 5 sygnału PWM o wypełnieniu XX.

Kręcenie tym samym silnikiem do tyłu wymagałoby następującego sterowania:

  • ustawienia na wyjściu nr 4 sygnału 1,
  • ustawienia  na pinie nr 5 sygnału PWM o wypełnieniu XX.

Natomiast zatrzymanie silnika wymaga:

  • ustawienia na wyjściu nr 4 sygnału o dowolnej wartości,
  • ustawienia  na pinie nr 5 sygnału PWM o wypełnieniu 0.

Pierwszy test silników w praktyce

Pora na pierwszy, prosty program. Na razie sprawdzimy tylko, czy oba silniki działają poprawnie. Zarówno na shieldzie, jak i na podwoziu ze sklejki oznaczone są silniki robota lewy (L - left) i prawy (R - right). Oczywiście strony te są umowne, bo konstrukcja nie ma jednego przodu i równie dobrze może jeździć odwrotnie... Załóżmy teraz, że miejsce z zamontowanym Arduino jest tyłem.

Zacznijmy od wprowadzenia definicji pinów oraz ich konfiguracji. Będziemy stosować skróty, które pozwolą na szybką identyfikację wyprowadzeń:

  • L (left) - lewy,
  • R (right) - prawy,
  • DIR (direction) - kierunek,
  • PWM - pin, na który podajemy sygnał PWM ustawiający prędkość danego silnika.

Przykładowo L_DIR oznacza pin Arduino, którym wybieramy kierunek obrotów lewego silnika.

Teraz pora uruchomić lewy silnik. Zacznijmy od prostego włączania i wyłączania. Cały program może wyglądać następująco:

W powyższym programie podświetliłem jedną linię. Jest ona umieszczona tam nadmiarowo. Jeśli na pinie L_PWM ustawiamy 0, to stan L_DIR nie zrobi nam już różnicy - silnik będzie wyłączony. Linia ta jednak nie zaszkodzi (można ją tam zostawić) - dodałem ją dla lepszego zobrazowania sytuacji.

Efekt działania tego programu widoczny jest na filmie, oczywiście uniosłem dla testu przód robota, aby nie uciekł mi z kadru!

Zanim przejdziesz dalej upewnij się, że w Twoim robocie kręci się ten sam, lewy silnik. Jeśli tak nie jest to należy zamienić miejsca wtyczek w shieldzie (prawy z lewym silnikiem). Jeśli kręci się lewy silnik, ale w przeciwną stronę, to są dwa wyjścia:

  1. Możesz zamienić kolejność przewodów we wtyczce od silnika lewego.
  2. Nie rób teraz nic - "poprawimy" to później programowo.

Niezależnie od wybranej metody zanotuj sobie teraz, w którą stronę kręci się lewy silnik, gdy na pinie L_DIR ustawiona była wartość 0. W moim wypadku zapisuję, że silnik kręci się "do przodu", a poprawniej mówiąc - przy takich obrotach robot porusza się do przodu.

Test ponawiamy dla drugiego silnika:

Po uruchomieniu tego programu musi kręcić się silnik prawy. Dobrze by było, aby kierunek obrotów zgadzał się z wcześniej testowanym silnikiem lewym. Jeśli tak nie jest, to znów masz dwa wyjścia:

  1. Zamień kolejność przewodów we wtyczce od silnika prawego.
  2. Nie rób teraz nic - "poprawimy" to później programowo.

Tak samo jak poprzednio zanotuj sobie, w którą stronę kręci się silnik prawy, gdy na R_DIR jest 0. W moim przypadku będzie to "do przodu", czyli tak samo jak w przypadku silnika lewego.

Zadanie dodatkowe 4.1

Sprawdź jak zachowują się silniki dla innych wartości PWM. Trzymaj się nadal zakresu 0-100. Czyli przykładowo zobacz, co dzieje się, gdy ustawisz prędkość "50": analogWrite(R_PWM, 50); Sprawdź, czy silniki kręcą się tak samo dobrze dla niskich wartości PWM itd.


Kolejnym etapem będzie napisanie kilku funkcji, które pozwolą na łatwe sterowanie silnikami (bez pamiętania o wartościach DIR itd). Jednak zanim do tego przejdziemy, to wyjaśnię skąd wcześniej wzięło się ograniczenie maksymalnej wartości PWM do 100...

Ograniczenie prędkości (bezpieczne napięcie maks.)

Jak powinieneś pamiętać z kursu podstaw Arduino (część 5) podczas korzystania z PWM możemy regulować wypełnienie od 0 do 255, gdzie 0 to 0% wypełnienia, a 255, to 100% wypełnienia. Przed testami poprosiłem wstępnie, aby operować wypełnieniem z zakresu 0-100, co przekładało się na regulację od 0% do około 40% wypełnienia. Skąd takie ograniczenie?

Cały robot zasilany jest z 6 baterii AA, co daje maksymalnie 6*1,5V = 9V. Było to niezbędne do poprawnej pracy całej konstrukcji i właśnie takie napięcie możemy dostarczyć do silników - jednak byłoby to dla nich zabójcze. Zastosowane napędy pracują najlepiej, gdy są zasilane ~5V.

Wcześniej narzucona wartość 100 była jedynie przykładowa, nie chciałem od razu tłumaczyć skąd wzięła się dziwna wartość maksymalna 165 - bo właśnie takie największe wypełnienia można ustawiać. Wartość ta stanowi około 65% wypełnienia PWM.

Skąd wzięła się ta wartość? Została dobrana eksperymentalnie zamiast silników podłączyłem do robota woltomierz i sprawdzałem napięcie, które pojawia się tam w zależności od wybranego PWM. Dla 165 zmierzyłem lekko ponad 5V, co będzie bezpieczną wartością - szczególnie, że z czasem baterie będą się trochę rozładowywać.

Pomiar napięcia dopływającego do silników przy wybranym PWM.

Pomiar napięcia dopływającego do silników przy wybranym PWM.

Dodajmy od razu na samej górze programu kolejną definicję:

Od razu wyjaśnia się też sprawa dlaczego przy zbyt małym wypełnieniu PWM silniki nie ruszają, tylko wydają piski. Poniższej pewnego napięcia zasilającego napędy te są zwyczajnie za słabe, aby poruszyć naszego robota.

Łatwe funkcje sterujące napędami

Pora zabrać się za napisanie funkcji, które usystematyzują powyższe informacje i pozwolą na łatwe programowanie robota. Do napisania są 3 funkcje. Dwie, to osobne sterowanie lewym/prawym silnikiem. Trzecia będzie zatrzymywała robota.

Każdy może zbudować ten mechanizm zgodnie ze swoimi upodobaniami. Ja w swoich robotach najczęściej korzystam z mechanizmu, który pozwala sterować kierunkiem i prędkością silników za pomocą 1 liczby. Wywołanie funkcji leftMotor(95); sprawi, że silnik będzie kręcił się do przodu z 95% maksymalnej prędkości. Natomiast leftMotor(-95); będzie odpowiadało za obroty z taką samą prędkością, ale do tyłu.

Zaczynamy od pustej funkcji typu void, ponieważ nie będzie ona zwracała żadnej wartości. Jako argument przyjmować będziemy jedną liczbę typu int. Typ ten może przechowywać liczby dodatnie i ujemne, więc sprawdzi się tu idealnie.

W kolejnym kroku należy rozróżnić, czy silnik ma się kręcić do przodu, czy do tyłu:

Nie musimy dokładnie zastanawiam się, co w przypadku, gdy prędkość będzie 0, ponieważ tak, czy inaczej będzie to oznaczało zatrzymanie silnika (kierunek nie będzie miał znaczenia). W kolejnym kroku uzupełnijmy część dla silnika kręcącego się do przodu:

Funkcja map() pomaga nam przeskalować prędkość. Tutaj używamy jej do przeliczenia wartości procentowej prędkości 0-100%, na wypełnienie sygnału PWM 0-165 (PWM_MAX). Oczywiście w zaznaczonej linijce musimy wpisać odpowiednią wartość L_DIR, dla której silnik kręcił się do przodu. W moim przypadku zgodnie z wcześniejszymi testami wpisałem 0.

Ostatni etap, to wysterowanie silnika do tyłu. Należy jednak pamiętać, że w tym przypadku nie da się od razu ustawić wypełnienia PWM o wartości V, ponieważ zmienna ta będzie przechowywała wartość ujemną! Tutaj pomocna będzie funkcje wbudowana w Arduino, która zwraca nam wartość bezwzględną liczby (czyli bez znaku).

Mowa o abs(). Dla jasności, przykłady:

  • Wywołanie abs(10) zwraca 10
  • Wywołanie abs (-10) również zwraca 10, itd.

Wracając do przykładu z silnikiem, cała funkcja sterująca lewym silnikiem powinna wyglądać tak:

Dla testu warto teraz uruchomić program, który powinien wysterować silnik szybko do przodu, a po chwili trochę wolniej do tyłu:

Gdy test ten przebiegnie pozytywnie można dodać analogiczną funkcję dla silnika prawego. Opis kolejnych kroków byłby identyczny, więc ograniczę się do wklejenia gotowej funkcji:

Funkcja zatrzymująca robota

Oczywiście korzystając z powyższych dwóch funkcji można również zatrzymywać robota, ale czasami wygodna będzie osobna, krótka funkcja, która zrobi wszystko "sama". W celu zatrzymania robota wystarczy na pinach od PWM ustawić dowolne wypełnienie PWM równe 0.

Tym razem funkcja nie musi przyjmować żadnych argumentów:

Gotowy szkielet programu z funkcjami

Podstawowy program, który będzie pozwalał na łatwe zabawy z robotem wygląda więc tak:

Możliwe ruchy robota

Teraz warto zastanowić się nad ruchami, które może wykonywać robot. Mamy tutaj napęd, który składa się z dwóch niezależnie sterowanych silników. Brak kół skrętnych, więc nie możemy tutaj mówić o sterowaniu jak w samochodzie. Bliżej nam tutaj do czołgu, który napędzany jest dwiema gąsienicami.

Jazda do przodu i do tyłu

Pierwsza, podstawowa możliwość robota to jazda do przodu lub do tyłu. Tutaj sprawa jest całkiem oczywista. Jeśli oba koła kręcą się w tym samym kierunku i z tą samą prędkością to robot pojedzie w daną stronę. Są od tego pewne wyjątki, ale o tym później...

Aby uzyskać taki efekt należy wydać następujące polecenia. Dla jazdy do przodu:

Dla jazdy do tyłu:

Obrót względem koła

Jeśli robot będzie kręcił tylko jednym z kół, to nasza konstrukcja będzie jechała po małym łuku. Dokładniej mówiąc będzie się obracała dookoła tego nieruchomego napędu.

W celu wykonania takich manewrów należy wydać następujące polecenia. Dla obrotu względem lewego koła:

Dla obrotu względem prawego napędu:

Obroty w miejscu

Jedną z ciekawszych możliwości czołgów jest obrót wykonywany dosłownie w miejscu. Tak będzie mógł również zachować się nasz robot. Wystarczy, że koła będą kręciły się w przeciwne strony:

W celu wykonania takich manewrów należy wydać następujące polecenia. Dla obrotu w lewo:

Dla obrotu w prawo:


Oczywiście możliwe są również inne opcje. Sterując silnikami z różną prędkością będziemy mogli poruszać się po łukach itd. Zachęcam do sprawdzenia opisywanych wyżej metod w praktyce. Dajcie znać w komentarzach, jeśli uzyskaliście jakieś ciekawe efekty! Szczególnie mile widziane wszystkie materiały wideo!

Testy możliwych ruchów w praktyce

Teraz możesz już samodzielnie napisać programy, które sprawdzą jak robot radzi sobie z jazdą. Sposób realizacji przykładowej trasy (jazda do przodu przez sekundę, obrót w miejscu przez dwie sekundy, jazda do tyłu przez sekundę) wygląda następująco:


Poniżej widoczne są moje próby z innymi programami i ustawieniami, zachęcam do takich testów:

Dlaczego robot "nie potrafi" jechać prosto?

Wielu początkujących jest bardzo zasmucona, gdy po wydaniu komendy "jazda do przodu" widzi, że robot nie zachowuje się zgodnie z oczekiwaniami. Często mimo zadania takich samych obrotów na oba silniki ich faktyczna prędkość będzie różna.

Spowoduje to jazdę po łuku. Tak niestety będzie, ale nie jest to nic złego. Powodów jest wiele. Należy pamiętać, że silniki, przekładnie i koła, to elementy mechaniczne... Nic nie jest idealne. Zawsze jeden silnik będzie trochę szybszy, jedna przekładnia będzie stawiała trochę większy opór, a jedno koło będzie mniej przyczepne.

Jazda

Jazda "prosto" często okazuje się łukiem...

Suma takich błędów będzie powodowała, że robot zawsze będzie skręcał, w którąś ze stron. Jazda po prostej to dość "skomplikowane zadanie". Konstrukcje, które muszą jechać prosto wyposaża się w droższe napędy, często dodaje się również czujniki (enkodery), które mierzą faktyczną prędkość obrotów silnika. Zastosowanie bardziej zaawansowanych programów pozwala wtedy jechać prosto.

"Krzywa" jazda nie jest wcale problemem!

Jazda po prostej jest potrzebna w nielicznych sytuacjach. Teraz testujemy zachowanie robota ze sztywno zaprogramowanymi operacjami, więc jazda po łuku będzie widoczna.

W kolejnej części kursu dodamy mechaniczne czujniki przeszkód, które uchronią robota przed zderzeniem ze ścianą. Zamiast jechać ślepo prosto w przeszkodę będzie on mógł reagować na otoczenie i odpowiednio zmieniać prędkości silników.


Jeśli różnica w obrotach silników jest stosunkowo duża lub przeszkadza komuś bardzo mocno, to można pokusić się o prostą korektę błędu w programie. Wystarczy zmniejszyć trochę PWM_MAX dla szybszego silnika. Rozwiązanie to jednak może okazać się zgubne. Polecam je jedynie w formie testów. Podczas dalszych części kursu naprawdę nie trzeba się tym przejmować.

Zadanie dodatkowe 4.2

Napisz program realizujący jazdę po następującej trasie:

Zadanie dodatkowe 4.3

Napisz program realizujący zadanie płynnego zwiększania oraz zmniejszania prędkości:

Podsumowanie

W tej części kursu ożywiliśmy nasz pojazd, który może teraz poruszać się po zaprogramowanych trasach. W ramach ćwiczenia polecam napisać kilka programów, dzięki którym robot będzie umiał pokonać wymyślone przez Was trajektorie np. jazda po prostokącie, elipsie, kręcenie ósemek itd.

W kolejnym artykule zajmiemy się mechanicznymi czujnikami przeszkód, co pozwoli na pierwszą autonomiczną jazdę naszego pojazdu. Tym razem będzie umiał samodzielnie omijać przeszkody!

Czy artykuł był pomocny? Oceń go:

Średnia ocena / 5. Głosów łącznie:

Nawigacja kursu

Autor kursu: Damian (Treker) Szymański
Grafiki, ilustracje: Piotr Adamczyk

drv, kursBudowyRobotow, mostek, robot, silniki, sterowanie

Trwa ładowanie komentarzy...