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...
W celu odłączenia zasilania wystarczy również po prostu zdjąć powyższą zworkę.
Nie trzeba koniecznie przekładać jej na pozycję OFF.
Zworka w pozycji OFF.
Zworka w pozycji ON.
Przy założonej zworce zasilania napędów w pozycji ON (i włączonym zasilaniu)
na płytce powinna się świeci dioda opisana jako MOT.
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.
Producent deklaruje, że układ ma wbudowane zabezpieczenia, które powinny go ochronić. Zawsze jednak istnieje jakieś ryzyko - lepiej unikać takich sytuacji.
Bezpieczne wgrywanie nowego programu
Podczas wgrywania programów staraj się postępować zgodnie z poniższymi krokami:
Zdejmij zworkę zasilania napędów.
Podłącz przewód USB i włącz zasilanie przełącznikiem suwakowym.
Wgraj nowy program.
Odłącz przewód USB.
Wyłącz zasilanie.
Załóż zworkę zasilania napędów.
Postaw robota na ziemi (lub trzymaj w powietrzu za sklejkę).
Włącz zasilanie.
Najniebezpieczniejsza sytuacja, to podłączony przewód USB, założona zworka
zasilania napędów i zasilanie wyłączone przełącznikiem. Wtedy silniki będą zasilane
z portu USB, co może doprowadzić do uszkodzenia robota i komputera!
Gotowe zestawy do kursów Forbota
Komplet elementów 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!
Masz już zestaw? Zarejestruj go wykorzystując dołączony do niego kod. Szczegóły »
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.
Uwaga! Konkretne kierunki (przód/tył) zależą również od sposobu podłączenia
przewodów od silników - więcej o tym w dalszej części artykułu.
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.
Zanim przejdziesz dalej upewnij się, że przewody od lewego silnika (L na podwoziu)
podłączone są do lewego gniazda silników (L na shieldzie). Oczywiście analogicznie
prawy silnik (R), do prawego gniazda (R).
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:
#define L_PWM 5
#define L_DIR 4
#define R_PWM 6
#define R_DIR 9
void setup() {
//Konfiguracja pinow od mostka H
pinMode(L_DIR, OUTPUT);
pinMode(R_DIR, OUTPUT);
pinMode(L_PWM, OUTPUT);
pinMode(R_PWM, OUTPUT);
}
void loop() {
//Obroty silnika przez sekundę do przodu z prędkością 100
digitalWrite(L_DIR, 0); //Ustawienie kierunku
analogWrite(L_PWM, 100); //Ustawienie predkosci
delay(1000); //Opoznienie 1 sekunda
//Zatrzymanie silnika na sekundę
digitalWrite(L_DIR, 0); //Ustawienie kierunku
analogWrite(L_PWM, 0); //Wylaczenie silnika
delay(1000); //Opoznienie 1 sekunda
}
Na razie podczas testów korzystaj jedynie z PWM w zakresie 0-100! Większa wartość może uszkodzić silniki - więcej informacji w dalszej części!
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!
Jeśli silnik się nie kręci - sprawdź koniecznie, czy zielone wtyczki są poprawnie zamontowane na przewodach od silników! Wewnątrz złącza powinien znaleźć się tylko odsłonięty kawałek przewodu bez izolacji!
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:
Możesz zamienić kolejność przewodów we wtyczce od silnika lewego.
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.
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:
Zamień kolejność przewodów we wtyczce od silnika prawego.
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.
Dla wygody dobrze byłoby doprowadzić do sytuacji, gdy po podaniu 0
na piny DIR oba silniki będą kręciły się w tę samą stronę (niezależnie którą).
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.
Najprościej zabezpieczyć więc silniki poprzez ograniczenie maksymalnego wypełnienia sygnału PWM.
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.
Dodajmy od razu na samej górze programu kolejną definicję:
#define PWM_MAX 165
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.
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.
Znak liczby odpowiada za kierunek ("+" do przodu, "-" do tyłu),
natomiast wartość odpowiada za procent maksymalnej prędkości.
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.
void leftMotor(int V) {
}
Oczywiście nazwa zmiennej w argumencie może być dowolna.
Ja wybrałem V, bo jest to popularne oznaczenie prędkości w fizyce.
W kolejnym kroku należy rozróżnić, czy silnik ma się kręcić do przodu, czy do tyłu:
void leftMotor(int V) {
if (V > 0) { //Jesli predkosc jest wieksza od 0 (dodatnia)
} else {
}
}
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:
void leftMotor(int V) {
if (V > 0) { //Jesli predkosc jest wieksza od 0 (dodatnia)
V = map(V, 0, 100, 0, PWM_MAX);
digitalWrite(L_DIR, 0); //Kierunek: do przodu
analogWrite(L_PWM, V); //Ustawienie predkosci
} else {
}
}
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:
void leftMotor(int V) {
if (V > 0) { //Jesli predkosc jest wieksza od 0 (dodatnia)
V = map(V, 0, 100, 0, PWM_MAX);
digitalWrite(L_DIR, 0); //Kierunek: do przodu
analogWrite(L_PWM, V); //Ustawienie predkosci
} else {
V = abs(V); //Funkcja abs() zwroci wartosc V bez znaku
V = map(V, 0, 100, 0, PWM_MAX);
digitalWrite(L_DIR, 1); //Kierunek: do tyłu
analogWrite(L_PWM, V); //Ustawienie predkosci
}
}
Dla testu warto teraz uruchomić program, który powinien wysterować silnik szybko do przodu, a po chwili trochę wolniej do tyłu:
#define L_PWM 5
#define L_DIR 4
#define R_PWM 6
#define R_DIR 9
#define PWM_MAX 165
void setup() {
//Konfiguracja pinow od mostka H
pinMode(L_DIR, OUTPUT);
pinMode(R_DIR, OUTPUT);
pinMode(L_PWM, OUTPUT);
pinMode(R_PWM, OUTPUT);
}
void loop() {
leftMotor(80); //80% predkosci do przodu
delay(2000);
leftMotor(-40); //40% predkosci do tyłu
delay(2000);
}
void leftMotor(int V) {
if (V > 0) { //Jesli predkosc jest wieksza od 0 (dodatnia)
V = map(V, 0, 100, 0, PWM_MAX);
digitalWrite(L_DIR, 0); //Kierunek: do przodu
analogWrite(L_PWM, V); //Ustawienie predkosci
} else {
V = abs(V); //Funkcja abs() zwroci wartosc V bez znaku
V = map(V, 0, 100, 0, PWM_MAX);
digitalWrite(L_DIR, 1); //Kierunek: do tyłu
analogWrite(L_PWM, V); //Ustawienie predkosci
}
}
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:
void rightMotor(int V) {
if (V > 0) { //Jesli predkosc jest wieksza od 0 (dodatnia)
V = map(V, 0, 100, 0, PWM_MAX);
digitalWrite(R_DIR, 0); //Kierunek: do przodu
analogWrite(R_PWM, V); //Ustawienie predkosci
} else {
V = abs(V); //Funkcja abs() zwroci wartosc V bez znaku
V = map(V, 0, 100, 0, PWM_MAX);
digitalWrite(R_DIR, 1); //Kierunek: do tyłu
analogWrite(R_PWM, V); //Ustawienie predkosci
}
}
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:
void stopMotors() {
analogWrite(L_PWM, 0); //Wylaczenie silnika lewego
analogWrite(R_PWM, 0); //Wylaczenie silnika prawego
}
Gotowy szkielet programu z funkcjami
Podstawowy program, który będzie pozwalał na łatwe zabawy z robotem wygląda więc tak:
#define L_PWM 5
#define L_DIR 4
#define R_PWM 6
#define R_DIR 9
#define PWM_MAX 165
void setup() {
//Konfiguracja pinow od mostka H
pinMode(L_DIR, OUTPUT);
pinMode(R_DIR, OUTPUT);
pinMode(L_PWM, OUTPUT);
pinMode(R_PWM, OUTPUT);
}
void loop() {
}
void leftMotor(int V) {
if (V > 0) { //Jesli predkosc jest wieksza od 0 (dodatnia)
V = map(V, 0, 100, 0, PWM_MAX);
digitalWrite(L_DIR, 0); //Kierunek: do przodu
analogWrite(L_PWM, V); //Ustawienie predkosci
} else {
V = abs(V); //Funkcja abs() zwroci wartosc V bez znaku
V = map(V, 0, 100, 0, PWM_MAX);
digitalWrite(L_DIR, 1); //Kierunek: do tyłu
analogWrite(L_PWM, V); //Ustawienie predkosci
}
}
void rightMotor(int V) {
if (V > 0) { //Jesli predkosc jest wieksza od 0 (dodatnia)
V = map(V, 0, 100, 0, PWM_MAX);
digitalWrite(R_DIR, 0); //Kierunek: do przodu
analogWrite(R_PWM, V); //Ustawienie predkosci
} else {
V = abs(V); //Funkcja abs() zwroci wartosc V bez znaku
V = map(V, 0, 100, 0, PWM_MAX);
digitalWrite(R_DIR, 1); //Kierunek: do tyłu
analogWrite(R_PWM, V); //Ustawienie predkosci
}
}
void stopMotors() {
analogWrite(L_PWM, 0); //Wylaczenie silnika lewego
analogWrite(R_PWM, 0); //Wylaczenie silnika prawego
}
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.
Obroty i skręty będzie można wykonywać sterując
prędkościami i kierunkami obrotów poszczególnych silników.
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...
Jazda robota do przodu.
Jazda robota do tyłu.
Aby uzyskać taki efekt należy wydać następujące polecenia. Dla jazdy do przodu:
rightMotor(60); //Prawy do przodu z 60% predkosci
leftMotor(60); //Lewy do przodu z 60% predkosci
Dla jazdy do tyłu:
rightMotor(-60); //Prawy do tyłu z 60% predkosci
leftMotor(-60); //Lewy do tyłu z 60% predkosci
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.
Obrót względem prawego koła.
Obrót względem lewego koła.
W celu wykonania takich manewrów należy wydać następujące polecenia. Dla obrotu względem lewego koła:
rightMotor(60); //Prawy do przodu z 60% predkosci
leftMotor(0); //Lewy wyłączony
Dla obrotu względem prawego napędu:
rightMotor(0); //Prawy wyłączony
leftMotor(60); //Lewy do przodu z 60% predkosci
Ten sposób wykonywania obrotów jest
jednak wykorzystywany dość rzadko.
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:
Obrót robota w lewo.
Obrót robota w prawo.
W celu wykonania takich manewrów należy wydać następujące polecenia. Dla obrotu w lewo:
rightMotor(60); //Prawy do przodu z 60% predkosci
leftMotor(-60); //Lewy do tyłu z 60% predkosci
Dla obrotu w prawo:
rightMotor(-60); //Prawy do tyłu z 60% predkosci
leftMotor(60); //Lewy do przodu z 60% predkosci
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:
#define L_PWM 5
#define L_DIR 4
#define R_PWM 6
#define R_DIR 9
#define PWM_MAX 165
void setup() {
//Konfiguracja pinow od mostka H
pinMode(L_DIR, OUTPUT);
pinMode(R_DIR, OUTPUT);
pinMode(L_PWM, OUTPUT);
pinMode(R_PWM, OUTPUT);
}
void loop() {
leftMotor(60); //Jazda do przodu z predkoscią 60%
rightMotor(60);
delay(1000); //Jazda przez sekunde
leftMotor(-50); //Obrot w miejscu z predkoscią 50%
rightMotor(50);
delay(2000); //Czas trwania obrotu 2 sekundy
leftMotor(-80); //Jazda do tyłu z predkoscią 80%
rightMotor(-80);
delay(1000); //Jazda do tyłu przez sekunde
}
void leftMotor(int V) {
if (V > 0) { //Jesli predkosc jest wieksza od 0 (dodatnia)
V = map(V, 0, 100, 0, PWM_MAX);
digitalWrite(L_DIR, 0); //Kierunek: do przodu
analogWrite(L_PWM, V); //Ustawienie predkosci
} else {
V = abs(V); //Funkcja abs() zwroci wartosc V bez znaku
V = map(V, 0, 100, 0, PWM_MAX);
digitalWrite(L_DIR, 1); //Kierunek: do tyłu
analogWrite(L_PWM, V); //Ustawienie predkosci
}
}
void rightMotor(int V) {
if (V > 0) { //Jesli predkosc jest wieksza od 0 (dodatnia)
V = map(V, 0, 100, 0, PWM_MAX);
digitalWrite(R_DIR, 0); //Kierunek: do przodu
analogWrite(R_PWM, V); //Ustawienie predkosci
} else {
V = abs(V); //Funkcja abs() zwroci wartosc V bez znaku
V = map(V, 0, 100, 0, PWM_MAX);
digitalWrite(R_DIR, 1); //Kierunek: do tyłu
analogWrite(R_PWM, V); //Ustawienie predkosci
}
}
void stopMotors() {
analogWrite(L_PWM, 0); //Wylaczenie silnika lewego
analogWrite(R_PWM, 0); //Wylaczenie silnika prawego
}
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 "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.
Tym tematem nie będziemy zajmować się w kursie. Zainteresowani powinni poczytać na temat regulatorów ze sprzężeniem zwrotnym np. PID.
"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.
Jednak docelowo chcemy zbudować prawdziwego robota, czyli układ, który oprócz
napędów ma czujniki będące "oczami" naszego pojazdu!
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:
Prosto 2 sekundy.
Obrót w miejscu o 360 stopni.
Po łuku 2 sekundy.
Obrót w miejscu o 360 stopni.
Obrót w miejscu o 90 stopni.
Prosto 2 sekundy.
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 wpis był pomocny? Oceń go:
Średnia ocena 5 / 5. Głosów łącznie: 84
Nikt jeszcze nie głosował, bądź pierwszy!
Artykuł nie był pomocny? Jak możemy go poprawić? Wpisz swoje sugestie poniżej. Jeśli masz pytanie to zadaj je w komentarzu - ten formularz jest anonimowy, nie będziemy mogli Ci odpowiedzieć!
Dołącz do 30 tysięcy osób, które otrzymują powiadomienia o nowych artykułach! Zapisz się, a otrzymasz PDF-y ze ściągami (m.in. na temat mocy, tranzystorów, diod i schematów) oraz listę inspirujących DIY na bazie Arduino i Raspberry Pi.
Dołącz do 30 tysięcy osób, które otrzymują powiadomienia o nowych artykułach! Zapisz się, a otrzymasz PDF-y ze ściągami (m.in. na temat mocy, tranzystorów, diod i schematów) oraz listę inspirujących DIY z Arduino i RPi.
Trwa ładowanie komentarzy...