Kursy • Poradniki • Inspirujące DIY • Forum
Na początku wspomnę, że temat line followerów darzę zdecydowanie największą sympatią! Kilka lat temu budowałem takie konstrukcje i startowałem z nimi na licznych zawodach robotów.
Zaczynałem od prostych i nieporadnych pojazdów, aż doszedłem do momentu, w którym moje konstrukcje wygrywały zawody. Jestem więc chyba dobrym przykładem, że budowa takich robotów (nawet najprostszych) pozwala rozwijać swoje umiejętności! Poniżej dwa zdjęcia: po lewej jeden z pierwszych moich line followerów (2010), a po prawej jeden z ostatnich (2014).
- Line follower z 2010 roku.
- Line follower z 2014 roku.
Czym są line followery?
Line followery, jak nazwa wskazuje, są konstrukcjami, które podążają za linią, która wyznacza tor jazdy. Często organizowane są zawody, do których zgłasza się swoje roboty. Wygrywa konstruktor najszybszego pojazdu.
Oczywiście pojazdy nie są zdalnie sterowane,
więc muszą samodzielnie odnaleźć się na trasie.
Przykładowy przejazd jednego z moich robotów:
Budowa toru do line followera
Trasa, na której odbywają się wyścigi najczęściej wyznaczana jest przez czarną linię (o szerokości od 15 do 20 mm), którą umieszcza się na białym podłożu. Możliwa jest też odwrotna wersja, czyli biała linia na czarnym tle - wariant ten jest jednak obecnie rzadko spotykany.
Do budowy domowego toru potrzebne będą:
- podłoże: wariant idealny, to białe płyty meblowe MDF/HDF. Jednak na początku równie dobrze sprawdzi się biały karton/brystol. W ostateczności mogą być nawet panele lub inna twarda, jasna podłoga.
- czarna linia: tutaj, niezależnie od podłoża, należy wykorzystać czarną taśmę izolacyjną, która sprawdza się w takim zastosowaniu idealnie.
Druga, rzadziej spotykana, metoda wykonywania tras dla line followerów, to tzw. "bannery". Chodzi o wydruk toru na materiale, z którego wykonywane są wielkoformatowe reklamy, które często można spotkać np. na płotach.
Na początku trzeba zacząć od prostszych i małych torów. Warto znaleźć kawałek wolnej podłogi (lub wykorzystać brystol) i wykleić tor. Na początek koniecznie zaczynamy od czegoś łatwego - np. od prostokąta z mocno zaokrąglonymi rogami lub elipsy. Oczywiście nie musi być to zrobione idealnie równo - wręcz przeciwnie.
Należy unikać ostrych zakrętów,
na początku najlepiej skupić się na łukach!
Przykład mojego toru widoczny jest poniżej. Specjalnie wybrałem podłogę, która nie ma jednolitego koloru i może sprawiać problemy, oznacza to bowiem, że jeśli u mnie robot zadziała poprawnie, to nie ma opcji, aby u innych osób sprawiał problemy! Jeśli tylko macie taką możliwość, to wybierzcie jednak coś w jednolitym kolorze...
"Tor" został wykonany z jednego kawałka taśmy izolacyjnej. Podczas wyklejania linii taśmę można delikatnie naciągać, co pozwoli formować łuki. Nie należy jednak przesadzać z jej rozciąganiem, ponieważ może to doprowadzić do zwężeń i przebarwień. Trzeba też pamiętać, aby nie zostawiać przyklejonego do podłogi toru na bardzo długo. Po kilkunastu dniach taśma podczas odrywania możesz zostawić trudne do usunięcia ślady!
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!
Zamów w Botland.com.pl »Czujniki i zasada wykrywania linii
Zanim przejdziemy do montażu czujników warto uświadomić sobie jak robot "widzi linię". Pojazdy oświetlają trasę (w świetle widzialnym lub IR), a następnie sprawdzają ile światła odbiło się od trasy. Od białej trasy odbije się zdecydowanie więcej światła, niż od czarnej linii.
Robot poszukuje więc miejsc, w których do czujników wraca mniej światła!
Do tego zadania wykorzystamy sensory, które podczas poprzedniej części kursu pozwoliły na zbudowanie robota Światłoluba. Tym razem konieczne jest założenie zworek, które włączą diody świecące - to właśnie one doświetlą trasę!
Za pomocą wbudowanego w czujnik potencjometru możliwa jest regulacja jasności diody. Bardziej zaawansowanym czytelnikom wyjaśniam dlaczego zdecydowaliśmy się na taką konfigurację:
- Dlaczego kolorowa dioda (zamiast IR)?
Dzięki temu znacznie łatwiej sprawdzić, czy czujnik faktycznie działa oraz gdzie pada światło emitowane przez nadajnik. - Dlaczego regulacja jasności diody, a nie dzielnika napięcia przy fotorezystorze?
Po prostu gołym okiem widać wtedy efekt regulacji. Czujniki miały być proste i przyjazne początkującym tak, aby szybko można było wyłapać problemy.
- Dwa czujniki świecące różną jasnością.
- Różnica w świeceniu z większej wysokości.
Podłączenie czujników do robota
Na początku należy zamocować czujniki. Tym razem muszą "patrzeć w dół", czyli na naszą trasę. Powinny być również blisko siebie (na szerokość taśmy izolacyjnej), która wyznacza trasę. Tutaj idealnie sprawdzą się otwory przygotowane z przodu robota:
Przewody można wygodnie przełożyć przez te same otwory, którymi doprowadzamy sygnały do silników. Oczywiście podłączenie pozostaje bez zmian (złącza ADC_L i ADC_R na shieldzie). Jeśli ktoś obawia się zbyt dużej ilości przewodów w okolicy silników, to można je docisnąć do sklejki za pomocą zwykłej gumki, tak jak jest to widoczne poniżej:
Test i kalibracja czujników
Pora sprawdzić działanie czujników w praktyce - musimy nauczyć robota reagować na linię. Do tego przyda nam się "kawałek trasy", czyli np. kartka z paskiem czarnej taśmy izolacyjnej:
Na początku zacznijmy od prostego testu. Wystarczy wyświetlać informacje na temat wartości odczytanej przez ADC. Proponuję na razie zająć się tylko jednym czujnikiem:
#define L_PWM 5
#define L_DIR 4
#define R_PWM 6
#define R_DIR 9
#define PWM_MAX 165
#define R_LINE_SENSOR A0
#define L_LINE_SENSOR A1
#define BUZZER 10
#define LED 13
void setup() {
//Konfiguracja pinow od mostka H
pinMode(L_DIR, OUTPUT);
pinMode(R_DIR, OUTPUT);
pinMode(L_PWM, OUTPUT);
pinMode(R_PWM, OUTPUT);
//Konfiguracja pozostalych elementow
pinMode(BUZZER, OUTPUT);
digitalWrite(BUZZER, 0); //Wylaczenie buzzera
pinMode(LED, OUTPUT);
digitalWrite(LED, 0); //Wylaczenie diody
Serial.begin(9600);
}
void loop() {
int odczytLewy = analogRead(L_LINE_SENSOR);
Serial.print("Lewy czujnik: ");
Serial.println(odczytLewy);
delay(250);
}
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
}
Praktycznie od razu powinna być zauważalna różnica między odczytami, gdy robot stoi czujnikiem na linii oraz w sytuacji, gdy sensor jej nie widzi. Będzie to wyglądało np. następująco:
- Czujnik nad linią.
- Czujnik obok linii.
W każdym przypadku uzyskane odczyty
mogą się znacznie różnić od prezentowanych powyżej.
Wszystko zależy od ustawienia czujników oraz podłoża, na którym są testowane. Najważniejsze, aby była widoczna różnica między odczytami wykonanymi na czarnej oraz białej powierzchni. Już 50-100 jednostek ADC wystarczy do dalszych prac.
W przypadku problemów z uzyskaniem odpowiednich odczytów powinno zacząć się od regulacji jasności, z jaką świeci dioda. Oczywiście w tym celu korzystamy z potencjometru wbudowanego w czujnik. Dodatkowo warto osłonić fotorezystory kawałkiem rurki termokurczliwej.
Nałożenie jej (i delikatne zaciśnięcie przez podgrzanie) na fotorezystorach powinno zwiększyć różnicę między odczytami. Zabieg ten uodporni również robota na czynniki zewnętrzne (np. światło słoneczne, które wpadając przez okno oświetli dodatkowo kawałek trasy).
Zanim przejdziesz dalej upewnij się, że drugi czujnik również
działa poprawnie! Zapisz wartości, jakie zwraca przetwornik ADC
w przypadku umieszczenia czujnika nad linią oraz poza nią.
W moim przypadku zanotowałem następujące wartości (uśrednione dla obu czujników):
- czujnik nad linią: 930
- czujnik poza linią: 680
Implementacja prostego komparatora
Teraz należy dobrać ręcznie wartość ADC, od której będziemy uznawać, że czujnik wykrył linię. Robot będzie się poruszał, więc nie możemy ustalić sztywnych granic. Odczyty czujników będą cały czas się zmieniać, dlatego najlepiej ustalić wartość graniczną.
Na podstawie wcześniej zanotowanych informacji uznałem, że każdy przypadek powyżej 850 robot ma identyfikować jako czarną linię. Po wybraniu wartości granicznej najlepiej od razu dodać do programu odpowiednią stałą:
#define GRANICA 850
Dla wygody dodajmy teraz dwie funkcje, które będą zwracały informację na temat stanu czujników. Najlepiej przyjąć, że 1 oznacza linię, a 0 jej brak. Funkcje będą więc pełniły rolę komparatorów.
Funkcje zwracają tylko 1 (prawdę) lub 0 (fałsz), więc mogą być typu boolean:
boolean leftSensor() {
if (analogRead(L_LINE_SENSOR) > GRANICA) { //Jesli czujnik widzi linie, to
return 1; //Zwroc 1
} else { //Jesli czujnik nie jest nad linią, to
return 0; //Zwroc 0
}
}
boolean rightSensor() {
if (analogRead(R_LINE_SENSOR) > GRANICA) { //Jesli czujnik widzi linie, to
return 1; //Zwroc 1
} else { //Jesli czujnik nie jest nad linią, to
return 0; //Zwroc 0
}
}
Teraz koniecznie trzeba sprawdzić, czy oba czujniki działają poprawnie. W tym celu w pętli loop() będziemy odpytywać sensory, czy widzą linię. Aby test był "bardziej mobilny" oprócz UARTa wykorzystamy buzzer (dla lewego czujnika) i diodę (dla prawego czujnika).
Cały program może wyglądać następująco:
#define GRANICA 850
#define L_PWM 5
#define L_DIR 4
#define R_PWM 6
#define R_DIR 9
#define PWM_MAX 165
#define R_LINE_SENSOR A0
#define L_LINE_SENSOR A1
#define BUZZER 10
#define LED 13
void setup() {
//Konfiguracja pinow od mostka H
pinMode(L_DIR, OUTPUT);
pinMode(R_DIR, OUTPUT);
pinMode(L_PWM, OUTPUT);
pinMode(R_PWM, OUTPUT);
//Konfiguracja pozostalych elementow
pinMode(BUZZER, OUTPUT);
digitalWrite(BUZZER, 0); //Wylaczenie buzzera
pinMode(LED, OUTPUT);
digitalWrite(LED, 0); //Wylaczenie diody
Serial.begin(9600);
}
void loop() {
if (leftSensor() == 1) { //Jesli lewy czujnik widzi linie
Serial.println("Linia pod lewym czujnikiem!");
digitalWrite(BUZZER, 1); //Wlacz buzzer
} else {
digitalWrite(BUZZER, 0); //Wylacz buzzer
}
if (rightSensor() == 1) { //Jesli prawy czujnik widzi linie
Serial.println("Linia pod prawym czujnikiem!");
digitalWrite(LED, 1); //Wlacz LED
} else {
digitalWrite(LED, 0); //Wylacz LED
}
}
boolean leftSensor() {
if (analogRead(L_LINE_SENSOR) > GRANICA) { //Jesli czujnik widzi linie, to
return 1; //Zwroc 1
} else { //Jesli czujnik nie jest nad linią, to
return 0; //Zwroc 0
}
}
boolean rightSensor() {
if (analogRead(R_LINE_SENSOR) > GRANICA) { //Jesli czujnik widzi linie, to
return 1; //Zwroc 1
} else { //Jesli czujnik nie jest nad linią, to
return 0; //Zwroc 0
}
}
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
}
Zanim przejdziesz dalej upewnij się, że oba czujniki działają poprawnie! Aby uniknąć później wielu problemów trzeba sprawdzić, czy aktualnie ustawiona GRANICA spełnia swoje zadanie.
W tej chwili poprawne działanie programu, to:
- oba czujniki na białej powierzchni - buzzer i dioda wyłączone
- oba czujniki na czarnej powierzchni - buzzer i dioda włączone
- linia pod lewym czujnikiem - buzzer włączony, dioda wyłączona
- linia pod prawym czujnikiem - buzzer wyłączony, dioda włączona.
Teraz przejdziemy do napisania 3 prostych algorytmów,
dzięki którym robot będzie mógł śledzić linię.
Wariant 1: line follower z jednym czujnikiem
Tak wiem, że mamy w robocie założone już 2 czujniki, warto jednak dla ciekawostki sprawdzić jak zachowa się robot śledzący linię jednym czujnikiem. Z takim zadaniem często mierzą się osoby, które budują roboty z klocków LEGO (w zestawach najczęściej jest tylko jeden czujnik linii).
Tak naprawdę, to nie da się śledzić linii
z wykorzystaniem jednego czujnika!
Można jednak śledzić krawędź linii! Jeśli postawimy robota obok lewej krawędzi linii, to program powinien realizować następujące operacje:
- Jeśli czujnik widzi linię: jedź łukiem w lewo,
- Jeśli czujnik nie widzi linii: jedź łukiem w prawo.
Dzięki temu robot będzie "odbijał się" od lewej krawędzi linii!
- Czujnik widzi linię – w lewo.
- Czujnik nie widzi linii – w prawo.
Na potrzeby testu odłączyłem czujnik prawy (wystarczyło odpiąć przewód). Program:
#define GRANICA 850
#define L_PWM 5
#define L_DIR 4
#define R_PWM 6
#define R_DIR 9
#define PWM_MAX 165
#define R_LINE_SENSOR A0
#define L_LINE_SENSOR A1
#define BUZZER 10
#define LED 13
void setup() {
//Konfiguracja pinow od mostka H
pinMode(L_DIR, OUTPUT);
pinMode(R_DIR, OUTPUT);
pinMode(L_PWM, OUTPUT);
pinMode(R_PWM, OUTPUT);
//Konfiguracja pozostalych elementow
pinMode(BUZZER, OUTPUT);
digitalWrite(BUZZER, 0); //Wylaczenie buzzera
pinMode(LED, OUTPUT);
digitalWrite(LED, 0); //Wylaczenie diody
Serial.begin(9600);
}
void loop() {
if (leftSensor() == 1) { //Jesli lewy czujnik widzi linie
leftMotor(0); //Jazda łukiem w lewo
rightMotor(40);
} else {
leftMotor(40); //Jazda łukiem w prawo
rightMotor(0);
}
}
boolean leftSensor() {
if (analogRead(L_LINE_SENSOR) > GRANICA) { //Jesli czujnik widzi linie, to
return 1; //Zwroc 1
} else { //Jesli czujnik nie jest nad linią, to
return 0; //Zwroc 0
}
}
boolean rightSensor() {
if (analogRead(R_LINE_SENSOR) > GRANICA) { //Jesli czujnik widzi linie, to
return 1; //Zwroc 1
} else { //Jesli czujnik nie jest nad linią, to
return 0; //Zwroc 0
}
}
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
}
Działanie programu w praktyce widoczne jest na poniższym filmie:
Może pewnym nadużyciem byłoby powiedzieć, że robot na filmie jedzie płynnie... Jednak na pewno realizuje swoje zadanie, czyli podąża po naklejonej linii. Oczywiście regulując prędkości silników można jeszcze sporo wyregulować i poprawić wyniki robota!
Wariant 2: line follower z dwoma czujnikami
Teraz skorzystamy z dwóch czujników, więc ponownie podłączamy przewód od prawego sensora. Tym razem rozsuniemy czujniki na bok (zgodnie z poniższym zdjęciem) tak, aby w momencie, gdy robot stoi idealnie na linii żaden z sensorów jej nie widział.
Dzięki temu minimalny zjazd z trasy będzie
"alarmował" Arduino, że należy zacząć skręcać!
Tym razem program powinien działać następująco:
- Jeśli żaden z czujników nie widzi linii: jazda do przodu.
- Jeśli czujnik lewy widzi linię: jazda po łuku w lewo.
- Jeśli czujnik prawy widzi linię: jazda po łuku w prawo.
Patrząc na robota od góry, będzie to wyglądało więc następująco:
-
Prawy czujnik widzi linię,
jazda w prawo.
-
Czujniki nie widzą linii,
jazda do przodu.
-
Lewy czujnik widzi linię,
jazda w lewo.
Program realizujący takie zadanie może wyglądać następująco:
#define GRANICA 850
#define L_PWM 5
#define L_DIR 4
#define R_PWM 6
#define R_DIR 9
#define PWM_MAX 165
#define R_LINE_SENSOR A0
#define L_LINE_SENSOR A1
#define BUZZER 10
#define LED 13
void setup() {
//Konfiguracja pinow od mostka H
pinMode(L_DIR, OUTPUT);
pinMode(R_DIR, OUTPUT);
pinMode(L_PWM, OUTPUT);
pinMode(R_PWM, OUTPUT);
//Konfiguracja pozostalych elementow
pinMode(BUZZER, OUTPUT);
digitalWrite(BUZZER, 0); //Wylaczenie buzzera
pinMode(LED, OUTPUT);
digitalWrite(LED, 0); //Wylaczenie diody
Serial.begin(9600);
}
void loop() {
if (leftSensor() == 0 && rightSensor() == 0) { //Jesli czujniki nie widza linii
leftMotor(40); //Jazda prosto
rightMotor(40);
} else if (leftSensor() == 1) { //Jesli lewy czujnik widzi linie
leftMotor(0); //Jazda po łuku w lewo
rightMotor(40);
} else if (rightSensor() == 1) { //Jesli prawy czujnik widzi linie
leftMotor(40); //Jazda po łuku w prawo
rightMotor(0);
}
}
boolean leftSensor() {
if (analogRead(L_LINE_SENSOR) > GRANICA) { //Jesli czujnik widzi linie, to
return 1; //Zwroc 1
} else { //Jesli czujnik nie jest nad linią, to
return 0; //Zwroc 0
}
}
boolean rightSensor() {
if (analogRead(R_LINE_SENSOR) > GRANICA) { //Jesli czujnik widzi linie, to
return 1; //Zwroc 1
} else { //Jesli czujnik nie jest nad linią, to
return 0; //Zwroc 0
}
}
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
}
Działanie programu w praktyce widoczne jest na poniższym filmie:
Robota można lepiej wyregulować zmieniając rozchylenie czujników oraz prędkości z jakimi się porusza. Niestety szybko dojdziemy do sytuacji, w której pojazd na zakręcie "nie wyrobi". Mimo skręcania i tak wyjedzie za trasę. Niestety przy obecnej konfiguracji po wyjechaniu za linię oba czujniki zobaczą "białe podłoże" więc... robot pojedzie prosto!
Podczas budowania line followerów należy zdecydowanie unikać wariantu,
gdy robot będąc idealnie na linii nie widzi jej żadnym czujnikiem!
Rozwiązaniem tego problemu jest kolejna konfiguracja robota!
Wariant 3: line follower z dwoma czujnikami v2
Tym razem również skorzystamy z dwóch czujników, jednak postaramy się zbliżyć je do siebie. Dzięki temu podczas jazdy idealnie "po linii" oba czujniki będą ją widziały. Minimalne odchylenie od poprawnego toru jazdy sprawi, że robot zacznie korygować swój ruch.
Tym razem program powinien działać następująco:
- Jeśli oba czujniki widzą linię: jazda do przodu.
- Jeśli czujnik lewy nie widzi linii: jazda po łuku w prawo.
- Jeśli czujnik prawy nie widzi linii: jazda po łuku w lewo.
Patrząc na robota od góry, będzie to wyglądało więc następująco:
-
Lewy czujnik nie widzi linii,
jazda w prawo.
-
Czujniki widzą linię,
jazda do przodu.
-
Prawy czujnik nie widzi linii,
jazda w lewo.
Przykładowy program realizujący to zadanie widoczny jest poniżej:
#define GRANICA 850
#define L_PWM 5
#define L_DIR 4
#define R_PWM 6
#define R_DIR 9
#define PWM_MAX 165
#define R_LINE_SENSOR A0
#define L_LINE_SENSOR A1
#define BUZZER 10
#define LED 13
void setup() {
//Konfiguracja pinow od mostka H
pinMode(L_DIR, OUTPUT);
pinMode(R_DIR, OUTPUT);
pinMode(L_PWM, OUTPUT);
pinMode(R_PWM, OUTPUT);
//Konfiguracja pozostalych elementow
pinMode(BUZZER, OUTPUT);
digitalWrite(BUZZER, 0); //Wylaczenie buzzera
pinMode(LED, OUTPUT);
digitalWrite(LED, 0); //Wylaczenie diody
Serial.begin(9600);
}
void loop() {
if (leftSensor() == 1 && rightSensor() == 1) { //Jesli oba czujniki widza linii
leftMotor(40); //Jazda prosto
rightMotor(40);
} else if (leftSensor() == 0) { //Jesli lewy czujnik nie widzi linii
leftMotor(40); //Jazda po łuku w prawo
rightMotor(10);
} else if (rightSensor() == 0) { //Jesli prawy czujnik nie widzi linii
leftMotor(10); //Jazda po łuku w lewo
rightMotor(40);
}
}
boolean leftSensor() {
if (analogRead(L_LINE_SENSOR) > GRANICA) { //Jesli czujnik widzi linie, to
return 1; //Zwroc 1
} else { //Jesli czujnik nie jest nad linią, to
return 0; //Zwroc 0
}
}
boolean rightSensor() {
if (analogRead(R_LINE_SENSOR) > GRANICA) { //Jesli czujnik widzi linie, to
return 1; //Zwroc 1
} else { //Jesli czujnik nie jest nad linią, to
return 0; //Zwroc 0
}
}
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
}
Działanie programu w praktyce widoczne jest poniżej:
Inne konfiguracje robota
Podczas budowy line followera duży wpływ na efekt finalny ma rozmieszczenie czujników. W tym robocie bardzo łatwo regulować rozmieszczenie czujników ich rozchylanie na boki (tak jak robiliśmy to podczas opisywanych przykładów).
Równie ciekawe zmiany zachowania można również uzyskać poprzez umieszczenie czujników po drugiej stornie robota (zamiana przodu z tyłem). Jeśli robot będzie jeździł odwrotnie, to sensory będą umieszczone znacznie dalej od osi napędowej. Dzięki temu mniejsze ruchy kół wystarczą do tego, aby nakierować robota na właściwy tor.
Podsumowanie
Mam nadzieję, że po tym artykule każdy będzie już wiedział jak line follower śledzi linię! Zachęcam wszystkich do testów i prób ustawiania jak największej prędkości. Koniecznie pochwalcie się w komentarzach filmami, które pokazują Wasze roboty w akcji!
Omówione zagadnienia, to dopiero wstęp. Do szybszej i płynnej jazdy potrzebna jest większa ilość czujników oraz bardziej rozbudowane algorytmy. W przyszłości pojawi się zestaw dodatkowych sensorów do naszego robota - wtedy zainteresowani tym tematem będę mogli pójść krok dalej!
Nawigacja kursu
Autor kursu: Damian (Treker) Szymański
Ilustracje: Piotr Adamczyk
Powiązane wpisy
arduino, kursBudowyRobotow, linefollower, programowanie, robot
Trwa ładowanie komentarzy...