Żadna to nowość w sieci – dwukołowy robot sterowany platformą Arduino i to jeszcze zbudowany w oparciu o gotową "ramę". A jednak jest szansa, że w każdym takim pojekcie i artykule na blogu znajdzie się coś ciekawego, może nawet unikatowego.
Historia tego robocika zaczęła się od marzenia, którym podzielił się ze mną pewnego wieczoru syn. Otóż powiedział, że chciałby mieć droida. Wiedziałem, o co chodzi, bo mój dziewięcioletni syn jest wielkim fanem Gwiezdnych Wojen (i aktualnie Harry'ego Pottera, choć na bal przebierańców idzie póki co jako Imperator Palpatine). Zadziałał efekt kolejki elektrycznej – zaproponowałem, że zbudujemy robota, który potrafi jeździć. Syn się zgodził.
Poszło więc zamówienie, dotarła paczka i poskładaliśmy robota, którego syn nazwał "Gnomonek", prawdopodobnie bazując na nazwie elementu zegara słonecznego.
Do dziś powstały cztery jego wersje, w zależności głównie od oprogramowania i zastosowanego dodatkowego sprzętu:
Ponieważ wszelkiego rodzaju line followery i beam followery zostały przedstawione na wielu blogach i w różnych odmianach – między innymi właśnie ten, który wykorzystuje komponenty sprzętowe identyczne z zainstalowanymi "Gnomonku" – tutaj skupiłem się na ostatniej jego wersji, czyli zdalnie sterowanym robocie mobilnym z prostym wykrywaniem przeszkód.
Do budowy tego robota wykorzystałem:
Dodatkowo użyłem prostego kodu obsługi czujnika ultradźwiękowego HC-SR04 – kodu zainspirowanego rozwiązaniem znalezionym w serwisie Instructables oraz opisem w dokumentacji technicznej modułu.
Poniżej zamieściłem listing programu – myślę, że nie powinno być problemów ze zrozumieniem poszczególnych jego elementów, starałem się wszak dość szczegółowo i wnikliwie go komentować. Schematu nie załączam - przedstawiony w listingu opis przeznaczenia poszczególnych pinów dla każdego arduinowca będzie prawdopodobnie wyczerpującą informacją na temat połączeń elementów elektronicznych w tym urządzeniu.
Program z oczywistych powodów nie jest doskonały ani optymalny, tym bardziej, że powstawał metodą przyrostową, na podstawie eksperymentów i obserwacji.
Co dalej z robotem? Otóż sterowanie zdalne za pomocą pilota TV jest szalenie niewygodne. Można by pomyśleć o zaadaptowaniu do sterowania nadajnika od biedronkowego śmigłowca – też działa na podczerwień. Byłoby to rozwiązanie optymalne, choć nie dostarczałoby tylu ciekawych opcji, co prezentowane wyżej. Próbowałem nawet "zdjąć" kody emitowane przez taki nadajnik, niestety, jak na razie nie byłem w stanie ogarnąć zastosowanego tam systemu... Co do planów na najbliższe tygodnie (o ile czas pozwoli), to właśnie przygotowałem układ enkodera dla silników – chodzi o to, że motorki zastosowane w robocie, mimo, że takie same pod względem specyfikacji, są szalenie nierówne. Innymi słowy przy takim samym wypełnieniu (czyli zadanej prędkości) robot zaczyna "ściągać" w lewo. Rzecz polega na tym, żeby robot starał się w miarę możliwości jechać równo.
Po rozwiązaniu tego problemu planuję zamontowanie ruchomej, sterowanej serwomechanizmem wieżyczki z czujnikiem HC-SR04 w celu zbudowania robota potrafiącego wykrywać i omijać przeszkody.
Równolegle trwają też prace nad robotem balansującym (nazwa robocza "Black") – moduł sterujący (Arduino Pro Mini, mostek H do sterowania silnikami i akcelerometr z żyroskopem GY-521 plus kilka elementów dyskretnych) oraz szkielet wraz z silnikami już przygotowane, teraz tylko trzeba znaleźć czas na montaż i testy.
Do zobaczenia zatem przy kolejnych wersjach moich (naszych :-)) robotów.
Historia tego robocika zaczęła się od marzenia, którym podzielił się ze mną pewnego wieczoru syn. Otóż powiedział, że chciałby mieć droida. Wiedziałem, o co chodzi, bo mój dziewięcioletni syn jest wielkim fanem Gwiezdnych Wojen (i aktualnie Harry'ego Pottera, choć na bal przebierańców idzie póki co jako Imperator Palpatine). Zadziałał efekt kolejki elektrycznej – zaproponowałem, że zbudujemy robota, który potrafi jeździć. Syn się zgodził.
Poszło więc zamówienie, dotarła paczka i poskładaliśmy robota, którego syn nazwał "Gnomonek", prawdopodobnie bazując na nazwie elementu zegara słonecznego.
Do dziś powstały cztery jego wersje, w zależności głównie od oprogramowania i zastosowanego dodatkowego sprzętu:
- beam follower (czyli ścigający latarkę);
- line follower (czyli jeżdżący po linii; w domu była to biała linia na brązowym tle, w pracy zaś, podczas prezentacji, czarna linia na jasnym tle – kwestia przestawienia paru znaków w programie);
- robot zdalnie sterowany (za pomocą pilota od starego telewizora);
- robot zdalnie sterowany (jak wyżej) z prostym systemem wykrywania przeszkód.
Ponieważ wszelkiego rodzaju line followery i beam followery zostały przedstawione na wielu blogach i w różnych odmianach – między innymi właśnie ten, który wykorzystuje komponenty sprzętowe identyczne z zainstalowanymi "Gnomonku" – tutaj skupiłem się na ostatniej jego wersji, czyli zdalnie sterowanym robocie mobilnym z prostym wykrywaniem przeszkód.
Do budowy tego robota wykorzystałem:
- Magician Chassis z silnikami, kołami i koszykami na baterie (dostępną np. na Nettigo);
- Arduino Leonardo i Arduino Motor Shield (zamiast drogiej płytki Motor Shield można zastosować układ L293D – do użytych przeze mnie silników w zupełności wystarczy, a jakby nie dawał rady, to możemy połączyć kilka takich układów w systemie "na barana", czyli piggy-back);
- RoboBoard 1.0 – jeśli zależy nam na funkcjonalności line followera i beam followera (opis obu rozwiązań z płytką RoboBoard i Arduino znajdziemy tutaj);
- małą płytkę stykową;
- czujnik zbliżeniowy HC-SR04;
- TSOP2236 (lub odpowiednik) jako odbiornik podczerwieni;
- buzzer z generatorem;
- dwie LED-y w różnych kolorach;
- dwa rezystory 220 Ohm;
- paczkę kabelków do płytki stykowej.
- możliwość poruszania się w przód i w tył z jedną z predefiniowanych prędkości ("biegów");
- możliwość płynnej regulacji prędkości;
- płynny skręt po łuku;
- skręt w miejscu (zwrot);
- sygnalizacja dźwiękowa przeszkody w odległości mniejszej niż 20 cm od czoła robota (po wykryciu przeszkody robot cofa się kilka centymetrów i zatrzymuje).
Dodatkowo użyłem prostego kodu obsługi czujnika ultradźwiękowego HC-SR04 – kodu zainspirowanego rozwiązaniem znalezionym w serwisie Instructables oraz opisem w dokumentacji technicznej modułu.
Poniżej zamieściłem listing programu – myślę, że nie powinno być problemów ze zrozumieniem poszczególnych jego elementów, starałem się wszak dość szczegółowo i wnikliwie go komentować. Schematu nie załączam - przedstawiony w listingu opis przeznaczenia poszczególnych pinów dla każdego arduinowca będzie prawdopodobnie wyczerpującą informacją na temat połączeń elementów elektronicznych w tym urządzeniu.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Robot zdalnie sterowany z wykrywaczem przeszkód. | |
"Gnomonek" v 1.0 | |
Opis funkcji pilota: | |
- Power - bazwzględne zatrzymanie silników i wyłączenie fizycznych reakcji na komendy sterujące; | |
- Up - przyspieszanie (od 0 lub wartości aktualnej do 255) | |
- Down - zwalniania (od 255 lub wartości aktualnej do 0) | |
- Mode - skręt w lewo (krótkotrwałe zatrzymanie lewego silnika) | |
- OK - skręt w prawo (krótkotrwałe zatrzymanie prawego silnika) | |
- 1 - 5 - biegi (przyspieszanie lub zwalnianie skokowe) | |
- 0 - bieg wsteczny (amiana kierunku pracy obu silników) | |
- Mute - hamowanie (prędkość do 0) | |
- TV/AV - obrót w lewo | |
- Normal - obrrót w prawo | |
- 3D - wyłączenie/włączenie sonaru | |
*/ | |
#include <MotorShield.h> | |
#include <IRremote.h> | |
// Stałe dla kodów pilota | |
#define R_POWER 0x71F9F1D7 // przycisk "power" | |
#define R_UP 0xEC9A30D9 // przycisk "góra" ("PR ^") | |
#define R_DOWN 0x99247EBA // przycisk "dół" ("PR v") | |
#define R_LEFT 0x2246972E // klawisz "mode" | |
#define R_RIGHT 0xA5B704C1 // klawisz "ok" | |
#define R_0 0xF8E21518 // "0" | |
#define R_1 0x3407DE1F // "1" | |
#define R_2 0x140C2C66 // "2" | |
#define R_3 0xAAB48C8B // "3" | |
#define R_4 0x6F59C2E2 // "4" | |
#define R_5 0x5853F701 // "5" | |
#define R_BRAKE 0x2910BDA4 // przycisk "mute" | |
#define ROTATE_LEFT 0xD2268818 // przycisk TV/AV ("ekran ze strzałką") | |
#define ROTATE_RIGHT 0x5985AF7D // przycisk "normal" | |
#define TOGGLE_PING 0xBBC24EA0 // przycisk "3D" | |
// Biegi (czyli prędkości predefiniowane) | |
#define GEAR_1 150 | |
#define GEAR_2 170 | |
#define GEAR_3 190 | |
#define GEAR_4 220 | |
#define GEAR_5 240 | |
#define IR_PIN 10 // odbiornik podczerwieni | |
#define TRIGGER_PIN 6 // trigger czujnika ultradźwiękowego | |
#define ECHO_PIN 5 // wyjście czujnika ultradźwiękowego | |
#define BUZZER_PIN 4 // buzzer - sygnalizator przeszkody | |
#define MAX_DISTANCE 200 // maksymalna odległość "widziana" przez czujnik przeszkód | |
#define MIN_DISTANCE 10 // minimalna odległość "widziana" przez czujnik przeszkód | |
#define LED 2 // LED sygnalizująca włączenie robota | |
#define LED_BACK 7 // LED sygnalizująca wsteczny kierunek jazdy | |
// Obiekty elementów robota: silników i czujnika zdalengo sterowania | |
IRrecv irrecv(IR_PIN); | |
MS_DCMotor motor_left(MOTOR_B); | |
MS_DCMotor motor_right(MOTOR_A); | |
decode_results results; | |
byte speed_left = 0; | |
byte speed_right = 0; | |
boolean powered_on = false; | |
boolean forward = true; | |
boolean pings = true; | |
/* | |
Wartość zmiennej last_action oznacza: | |
0 - nie powtarzać ostatniej akcji | |
1 - przyspieszenie | |
2 - zwolnienie | |
3 - skręt w lewo | |
4 - skręt w prawo | |
5 - obrót w miejscu w prawo | |
6 - obrót w miejscu w lewo | |
*/ | |
byte last_action = 0; | |
/* | |
Komplet kodów pilota Daewoo R-49C10: | |
0x2910BDA4 - mute | |
0x71F9F1D7 - power | |
0xEC9A30D9 - góra | |
0x99247EBA - dół | |
0x24AE7D4F - lewo | |
0xEB2F0635 - prawo | |
0xF8E21518 - 0 (zero) | |
0x3407DE1F - 1 | |
0x140C2C66 - 2 | |
0xAAB48C8B - 3 | |
0x6F59C2E2 - 4 | |
0x5853F701 - 5 | |
0xBD21F6DA - 6 | |
0x19EC217 - 7 | |
0x1B753386 - 8 | |
0x8C51BBE5 - 9 | |
0xD2268818 - TV/AV | |
0x5985AF7D - "normal" | |
0x2246972E - mode | |
0xA5B704C1 - OK | |
0x43DB40B8 - red | |
0x7AE57839 - green | |
0x9F28D734 - yellow | |
0x572F6EC3 - blue | |
Klawisze telegazety - od lewej: | |
0xA9C60CE4 | |
0xA25D05C4 | |
0xCAD80373 | |
0xCE685077 | |
0xFDAA9C40 | |
0x1C63B32D | |
0xF32F56AD - timer | |
0x5AB58865 - zoom | |
0xBBC24EA0 - 3D | |
Powtórzenie przycisku: | |
0xFFFFFFFF | |
*/ | |
void setSpeedBothMotors(byte speed_val) { | |
speed_left = speed_right = speed_val; | |
motor_right.setSpeed(speed_right); | |
motor_left.setSpeed(speed_left); | |
last_action = 0; | |
} | |
void move_faster() { | |
if (speed_left < 255) { | |
motor_left.setSpeed(++speed_left); | |
} | |
if (speed_right < 255) { | |
motor_right.setSpeed(++speed_right); | |
} | |
last_action = 1; | |
} | |
void move_slower() { | |
if (speed_left > 0) { | |
motor_left.setSpeed(--speed_left); | |
} | |
if (speed_right > 0) { | |
motor_right.setSpeed(--speed_right); | |
} | |
last_action = 2; | |
} | |
void turn_left() { | |
motor_right.setSpeed(speed_right); | |
motor_left.setSpeed(0); | |
delay(50); | |
motor_left.setSpeed(speed_left); | |
last_action = 3; | |
} | |
void turn_right() { | |
motor_left.setSpeed(speed_left); | |
motor_right.setSpeed(0); | |
delay(50); | |
motor_right.setSpeed(speed_right); | |
last_action = 4; | |
} | |
void rotate(boolean r_left) { | |
motor_left.setSpeed(0); | |
motor_right.setSpeed(0); | |
if (r_left) { | |
// Obrót w lewo | |
motor_left.run(BACKWARD); | |
motor_right.run(FORWARD); | |
last_action = 5; | |
} | |
else { | |
// Obrót w prawo | |
motor_left.run(FORWARD); | |
motor_right.run(BACKWARD); | |
last_action = 6; | |
} | |
motor_left.setSpeed(GEAR_4); | |
motor_right.setSpeed(GEAR_4); | |
delay(100); | |
motor_left.setSpeed(0); | |
motor_right.setSpeed(0); | |
if (forward) { | |
motor_left.run(FORWARD); | |
motor_right.run(FORWARD); | |
} | |
else { | |
motor_left.run(BACKWARD); | |
motor_right.run(BACKWARD); | |
} | |
} | |
// Odmierzanie odległości od przeszkody | |
// Protokół oceny odległości | |
// - sensor wysyła jeden "ping" | |
// - badamy jego "długość" | |
// - i konwertujemy na centymetry | |
// Źródło rozwiązania: http://www.instructables.com/id/Simple-Arduino-and-HC-SR04-Example/ | |
// Niestety, biblioteka NewPing "gryzie" się z IRRemote. | |
long getDistance() { | |
long duration; | |
digitalWrite(TRIGGER_PIN, LOW); | |
delayMicroseconds(2); | |
digitalWrite(TRIGGER_PIN, HIGH); | |
delayMicroseconds(10); | |
digitalWrite(TRIGGER_PIN, LOW); | |
duration = pulseIn(ECHO_PIN, HIGH); | |
return duration / 58; | |
} | |
void beep() { | |
// Irytujący sygnał dźwiękowy | |
for (int i = 0; i < 3; i++) { | |
digitalWrite(BUZZER_PIN, HIGH); | |
delay(100); | |
digitalWrite(BUZZER_PIN, LOW); | |
delay(100); | |
} | |
} | |
void alert() { | |
forward = false; | |
digitalWrite(LED_BACK, HIGH); | |
motor_left.run(BACKWARD); | |
motor_right.run(BACKWARD); | |
motor_left.setSpeed(GEAR_1); | |
motor_right.setSpeed(GEAR_1); | |
beep(); | |
motor_left.setSpeed(0); | |
motor_right.setSpeed(0); | |
forward = true; | |
digitalWrite(LED_BACK, LOW); | |
motor_left.run(FORWARD); | |
motor_right.run(FORWARD); | |
} | |
void setup() { | |
pinMode(LED, OUTPUT); | |
digitalWrite(LED, LOW); | |
pinMode(BUZZER_PIN, OUTPUT); | |
digitalWrite(BUZZER_PIN, LOW); | |
pinMode(LED_BACK, OUTPUT); | |
digitalWrite(LED_BACK, LOW); | |
pinMode(TRIGGER_PIN, OUTPUT); | |
pinMode(ECHO_PIN, INPUT); | |
irrecv.enableIRIn(); | |
irrecv.blink13(false); | |
// Domyślna konfiguracja silników | |
motor_right.setSpeed(speed_right); | |
motor_left.setSpeed(speed_left); | |
motor_right.run(BRAKE|FORWARD); | |
motor_left.run(BRAKE|FORWARD); | |
} | |
void loop() { | |
if (pings && powered_on && (MIN_DISTANCE >= getDistance())) { | |
alert(); | |
} | |
// Obsługa pilota zdalnego sterowania | |
if (irrecv.decode(&results)) { | |
switch(results.value) { | |
case R_POWER: | |
// Prędkość do zera i hamowanie lub uruchomienie silników; | |
// Zerując prędkość pozbywamy sie konieczności sprawdzania w przypadku przyspieszania/zwalniania/zmiany biegu czy silnik nie jest zatrzymany. | |
setSpeedBothMotors(0); | |
if (powered_on) { | |
motor_left.run(BRAKE); | |
motor_right.run(BRAKE); | |
digitalWrite(LED, LOW); | |
} | |
else { | |
motor_left.run(RELEASE); | |
motor_right.run(RELEASE); | |
digitalWrite(LED, HIGH); | |
} | |
powered_on = !powered_on; | |
break; | |
case R_UP: | |
// Przyspieszanie | |
move_faster(); | |
break; | |
case R_DOWN: | |
// Zwalnianie | |
move_slower(); | |
break; | |
case R_LEFT: | |
// Skręt w lewo | |
turn_left(); | |
break; | |
case R_RIGHT: | |
// Skręt w prawo | |
turn_right(); | |
break; | |
case ROTATE_LEFT: | |
// Obrót w lewo | |
rotate(true); | |
break; | |
case ROTATE_RIGHT: | |
// Obrót w prawo | |
rotate(false); | |
break; | |
case TOGGLE_PING: | |
// Jesli denerwuje nas wykrywanie przeszkód, to możemy je wyłączyć: | |
pings = !pings; | |
last_action = 0; | |
break; | |
case R_0: | |
// Zmiana kierunku przód-tył | |
if (forward) { | |
motor_left.run(BACKWARD); | |
motor_right.run(BACKWARD); | |
digitalWrite(LED_BACK, HIGH); | |
} | |
else { | |
motor_left.run(FORWARD); | |
motor_right.run(FORWARD); | |
digitalWrite(LED_BACK, LOW); | |
} | |
forward = !forward; | |
last_action = 0; | |
break; | |
case R_1: | |
// Pięć biegów (predefiniowanych prędkości) | |
setSpeedBothMotors(GEAR_1); | |
break; | |
case R_2: | |
setSpeedBothMotors(GEAR_2); | |
break; | |
case R_3: | |
setSpeedBothMotors(GEAR_3); | |
break; | |
case R_4: | |
setSpeedBothMotors(GEAR_4); | |
break; | |
case R_5: | |
setSpeedBothMotors(GEAR_5); | |
break; | |
case R_BRAKE: | |
// Hamowanie | |
motor_right.setSpeed(0); | |
motor_left.setSpeed(0); | |
last_action = 0; | |
break; | |
case REPEAT: | |
// W przypadku zmiany prędkości i kierunku lewo-prawo oraz obrotu - obsługa kodów powtórzeń. | |
switch (last_action) { | |
case 1: | |
move_faster(); | |
break; | |
case 2: | |
move_slower(); | |
break; | |
case 3: | |
turn_left(); | |
break; | |
case 4: | |
turn_right(); | |
break; | |
case 5: | |
rotate(true); | |
break; | |
case 6: | |
rotate(false); | |
break; | |
} | |
break; | |
default: | |
last_action = 0; | |
break; | |
} | |
irrecv.resume(); | |
} | |
} |
Program z oczywistych powodów nie jest doskonały ani optymalny, tym bardziej, że powstawał metodą przyrostową, na podstawie eksperymentów i obserwacji.
Co dalej z robotem? Otóż sterowanie zdalne za pomocą pilota TV jest szalenie niewygodne. Można by pomyśleć o zaadaptowaniu do sterowania nadajnika od biedronkowego śmigłowca – też działa na podczerwień. Byłoby to rozwiązanie optymalne, choć nie dostarczałoby tylu ciekawych opcji, co prezentowane wyżej. Próbowałem nawet "zdjąć" kody emitowane przez taki nadajnik, niestety, jak na razie nie byłem w stanie ogarnąć zastosowanego tam systemu... Co do planów na najbliższe tygodnie (o ile czas pozwoli), to właśnie przygotowałem układ enkodera dla silników – chodzi o to, że motorki zastosowane w robocie, mimo, że takie same pod względem specyfikacji, są szalenie nierówne. Innymi słowy przy takim samym wypełnieniu (czyli zadanej prędkości) robot zaczyna "ściągać" w lewo. Rzecz polega na tym, żeby robot starał się w miarę możliwości jechać równo.
Po rozwiązaniu tego problemu planuję zamontowanie ruchomej, sterowanej serwomechanizmem wieżyczki z czujnikiem HC-SR04 w celu zbudowania robota potrafiącego wykrywać i omijać przeszkody.
Równolegle trwają też prace nad robotem balansującym (nazwa robocza "Black") – moduł sterujący (Arduino Pro Mini, mostek H do sterowania silnikami i akcelerometr z żyroskopem GY-521 plus kilka elementów dyskretnych) oraz szkielet wraz z silnikami już przygotowane, teraz tylko trzeba znaleźć czas na montaż i testy.
Do zobaczenia zatem przy kolejnych wersjach moich (naszych :-)) robotów.
Komentarze
Prześlij komentarz