Przejdź do głównej zawartości

Nieskomplikowany robot mobilny

Ż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:
  • 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.
Końcowa wersja została tak przygotowana, żeby można było wrócić do każdej z poprzednich poprzez wgranie odpowiedniego programu.



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.
Zaprogramowane przeze mnie funkcjonalności robota, to:
  • 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).
Jeśli chodzi o komponenty programowe, wykorzystałem standardową bibliotekę dla Arduino Motor Shield oraz bibliotekę IRremote, opisaną na stronie autora i w serwisie Majsterkowo. W przypadku IRremote należy zwrócić uwagę, że w środowisku Arduino IDE w wersji nowszej niż 1.5.4 może wystąpić błąd kompilacji związany z kolizją nazw bibliotek (środowisko dostarcza podobną bibliotekę). Jeśli nie chcemy ostro  modyfikować kodu, zmieńmy po prostu nazwy plików z naszą, lokalną wersją IRremote.
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.

/*
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();
}
}
view raw robot.ino hosted with ❤ by GitHub

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

Popularne posty z tego bloga

Niesamowicie prosty czujnik zmierzchowy.

Tym razem zero programowania, będzie natomiast nostalgiczno-wspomnieniowy układzik, lekko zmodyfikowany. Otóż kilka dni temu rozmawialiśmy w gronie znajomych o różnego rodzaju czujnikach zmierzchowych i czujnikach ruchu. Ponieważ należę do tych wariatów, co to hołdują jeszcze owej przestarzałej i kompletnie odrealnionej dziś zasadzie: "po co kupować, gdy można zrobić", stwierdziłem, że poskładam takie coś (czujnik zmierzchowy; sensor ruchu faktycznie lepiej nabyć, choćby ze względu na rozmiary ;)) i być może podłączę do jakiegoś mikrokontrolera. Przypomniało mi się też przy okazji, że znalazłem ostatnio w elektronicznych śmieciach stary fotorezystor (dla niewtajemniczonych: element zmieniający rezystancję, czyli opór elektryczny, pod wpływem działania strumienia światła) RPP130, jeden z kilku pozostałych po montowanych wieki temu układach tranzystorowych do zdalnego sterowania pracą urządzeń za pomocą latarki... No OK, nie było to specjalnie rozbudowane zdalne sterowanie ;) ...

Aktualizacja oprogramowania układowego w ESP-01 do najnowszej wersji NodeMCU

Oprogramowanie i projekt NodeMCU cieszą się niesłabnącym zainteresowaniem świata konstruktorów urządzeń IoT, zatem co jakiś czas warto odświeżyć sobie firmware w naszych płytkach ESP. Osobiście jestem przeciwnikiem zmienienia czegoś, co dobrze działa, tylko dla zasady czy z chęci cieszenia się świadomością posiadania najnowszej wersji, ale tym razem chodzi jednak o coś innego – zwiększenie funkcjonalności i zapewnienie poprawnego działania oraz kompatybilności z najnowszymi projektami i bibliotekami. W tytule tego artykułu jest mowa o najprostszych płytkach z układem ESP8266 – ESP-01 . To właśnie w oparciu o ten model opracowałem płytkę prototypową, o której pisałem w poprzednim rozdziale. Dotychczas wszystkie moje płytki ESP miały na pokładzie oprogramowanie NodeMCU w wersji 0.9.5 . Zorientowani choć trochę w temacie od razu zauważą (Google? Bing?), że wersja ta ma już co najmniej dwa lata... Najwyższy czas zatem na aktualizację.

Płytka prototypowa na bazie ESP8266 (ESP-01)

To nie jest kolejny artykuł traktujący od początku do... nieco dalej (bo na pewno nie do końca) o płytkach ESP8266 . Żeby się dowiedzieć, co to takiego, odwiedźcie proszę np. tę stronę (oraz wiele innych – poproście o pomoc Waszą ulubioną wyszukiwarkę): http://www.esp8266.com/wiki/doku.php?id=esp8266-module-family . No ale żeby nie było, ESP8266 to układ zawierający na pokładzie wydajny mikrokontroler z rdzeniem RISC-owym, taktowany zegarem 40MHz (wersja, o której jest ten wpis) lub 80MHz, 512KB pamięci flash i podsystem komunikacji przez sieć WiFi . Jest powszechnie wykorzystywany jako swego rodzaju karta sieciowa do połączeń bezprzewodowych naszych urządzeń IoT , które budujemy w zaciszu domowych laboratoriów (i nie tylko). Układ montowany jest na płytkach występujących w kilku wersjach, różniących się przede wszystkim liczbą wyprowadzeń uniwersalnych, czyli GPIO – im większa liczba, tym większe możliwości wykorzystania układu (więcej urządzeń peryferyjnych itp.). Są też pewne ...