FiFi98 Napisano Styczeń 25, 2019 Udostępnij Napisano Styczeń 25, 2019 (edytowany) Przedstawiam mój pierwszy projekt DIY na forum Forbota, jest to pojazd o nazwie Wall-E kontrolowany przez bluetooth, którego zadaniem jest tworzenie mapy przeszkód w pomieszczeniu, w którym się znajduje. MECHANIKA Wall-E został zbudowany na ładnej zielonej sklejce, która niestety utraciła część swego uroku wskutek wiercenia kiedyś używanych otworów na kable i śrubki, które obecnie nie pełnią żadnej funkcji. Napęd robota został przymocowany do podwozia przy pomocy drewnianych klocków. Silniki krokowe 28BYJ-48 z przekładnią pracują pod napięciem 5V, posiadają wystarczający moment obrotowy 0,03Nm, lecz ich wadą jest dość niska prędkość. Do silników przymocowane są koła. Z tyłu znajduje się kółko obrotowe. Do mechaniki należy też doliczyć Serwo TowerPro SG-90, które pozwala regulować kąt widzenia przyklejonego do niego czujnika odległości bez konieczności obracania pojazdu. HARDWARE Sercem pojazdu jest klon Arduino Uno. Kontroluje ono dwa silniki za pośrednictwem dedykowanych sterowników, które zakupiłem razem z silnikami. Wyświetlacz tekstowy JHD162A-B-W w kolorze niebieskim wyświetla nazwę robota, jego prędkość, kierunek ruchu i kąt patrzenia. Czujnik odległości HC-SR04 zapewnia dane o przeszkodach w otoczeniu i odległości od nich. Posiada zadowalającą dokładność przy niskiej cenie, choć doświadczenie nauczyło mnie, aby kierować go zawsze na przeszkodę pod kątem prostym. Komunikację bluetooth umożliwia moduł bluetooth HC-06. Jest on chyba najpopularniejszy obok modułu 05, interfejs UART pozwala na proste podłączenie, go do pinów Arduino. Od oferty z linku różni się tylko tym, że nie posiada wyprowadzeń STATE i EN, co jednak nie ogranicza jego możliwości. Na pokładzie Wall-Ego umieściłem także płytkę wylutowaną ze starej zabawki RC. Stanowi ona węzeł, który łączy pin zasilania 5V Arduno z zasilaniem dodatnim układów peryferyjnych. Wychodzą z niego przewody męskie, a za nimi tam, gdzie zaszła taka potrzeba również żeńskie (przyznam, jedna wielka plątanina kabli), taką samą funkcje pełni drugi węzeł GND. Na płytce ulokowałem też podstawkę pod układ scalony, która stanowi gniazdo na moduł bluetooth, gdyż nie chciałem go wlutowywać na stałe (przy wgrywaniu programu na płytkę Arduino należy go wyciągnąć). Znajduje się tam też dzielnik napięcia zbudowany z rezystorów 1kOhm i 2kOhm, który podaje na pin RX modułu napięcie 3,3V. Zapobiega to uszkodzeniu układu, który pracuje w logice 3,3V, a Arduino w 5V. Napięcia wychodzącego z pinu TX nie trzeba podnosić, co byłoby bardziej kłopotliwe, gdyż Arduino rozpoznaje 3,3V jako logiczne 1. Na płytce umieściłem także potencjometr 10kOhm, który służy do ustawienia kontrastu wyświetlacza. Aby umożliwić komunikację z notebookiem, który nie posiada wbudowanego modułu BT potrzebny jest adapter podłączany do niego przez USB. SOFTWARE Program Arduino napisany został w środowisku Arduino IDE, czyli standardowym środowisku. Program na PC powstał w środowisku Processing. Przy wyborze zdecydowało kryterium języka - operuję tylko C++ i Java - oraz interfejs graficzny. Zrealizowałem też identyczną komunikację korzystając z C++ i CodeBlocks, lecz program konsolowy mnie nie satysfakcjonował. Na screenie przedstawiłem, gdzie znaleźć numer portu, do którego przypisany jest moduł HC-06 oraz interfejs programu wraz z przykładowym fragmentem mapy, którą Wall-E stworzył. Kod Arduino: /* 1 - stop 2 - do przodu 3 - do tyłu 4 - lewo 5 - prawo 6 - żądanie trasy 7 - żądanie pomiaru 81...86 - zmiana prędkośćci 101...118 - pozycja serwa */ #include <SoftwareSerial.h> //dodanie bibliotek #include <Servo.h> #include <Stepper.h> #include <LiquidCrystal.h> #define echo 11 //podłączenie czujnika odległości #define trig 12 LiquidCrystal lcd(14, 15, 16, 17, 18, 19); //tworzenie obiektów SoftwareSerial bt (0,1); Stepper left(64, 2, 4, 3, 5); Stepper right(64, 6, 8, 7, 9); Servo serwo; char state; //zmienna przechowująca odczyt z bluetooth long czas; //zmienne używane przy pomiarze odległości int dystans; int prawy=0, lewy=0; //koła: do tyłu/stop/do przódu int pom; //zmienna pomocnicza przy nadawaniu wyniku pomiaru int steps; //przebyte kroki void setup() { pinMode(echo, INPUT); //deklaracje OUTPUT/INPUT czujnika odległości pinMode(trig, OUTPUT); Serial.begin(9600); //ustanowienie komunikacji przez bluetooth serwo.attach(10); //podłączenie serwa right.setSpeed(300); //domyślna prędkość silników left.setSpeed(300); lcd.begin(16, 2); //ustannowienie komunikacji z wyświetlaczem lcd.setCursor(0, 0); //początkowe dane do wyświetlenia lcd.print("STOP"); lcd.setCursor(8, 0); lcd.print("SPEED: 6"); lcd.setCursor(0, 1); lcd.print("Wall-E"); lcd.setCursor(8, 1); lcd.print("SERVO: 9"); } void loop() { if(Serial.available() > 0){ //jeżeli istnieją dane do odebrania state = Serial.read(); //odbierz switch (state) { case '1': //stop lewy = 0; prawy = 0; lcd.setCursor(0, 0); lcd.print("STOP "); break; case '2': //do przodu lewy = 1; prawy = 1; lcd.setCursor(0, 0); lcd.print("FORW. "); break; case '3': //do tyłu lewy = -1; prawy = -1; lcd.setCursor(0, 0); lcd.print("BACKW."); break; case '4': //wokół własnej osi w lewo lewy=-1; prawy=1; lcd.setCursor(0, 0); lcd.print("LEFT "); break; case '5': //wokół własnej osi w prawo lewy=1; prawy=-1; lcd.setCursor(0, 0); lcd.print("RIGHT "); break; case '6': //żądanie przebytych kroków Serial.print(char(steps/10+115)); steps=0; break; case '7': //żądanie pomiaru z czujnika odległości delay(200); pom = pomiar(); if(pom < 115) Serial.print(char(pom)); else Serial.print(char(115)); break; case 'Q': //zmiana prędkości case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': left.setSpeed(int(state-80)*50); right.setSpeed(int(state-80)*50); lcd.setCursor(15, 0); lcd.print(int(state-80)); break; default: //zmiana pozycji serwa serwo.write(int(state-100)*10); if(int(state-100) < 10){ if(int(state-100) == 9){ lcd.setCursor(14, 1); lcd.print(" ");} lcd.setCursor(15, 1); lcd.print(int(state-100));} else {lcd.setCursor(14, 1); lcd.print(int(state-100));} break; } } if(lewy == 1) {left.step(-1); steps++;} //ruch i odnotowanie do zmiennej steps else if(lewy == -1) {left.step(1); steps++;} if(prawy == 1) right.step(-1); else if(prawy == -1) right.step(1); if(steps == 100){ //automatyczne wysłanie ilości kroków po wykonaniu 100 Serial.print(char(steps/10+115)); steps=0; } } int pomiar() //funkcja pomiar z czujnika odległości { digitalWrite(trig, LOW); delayMicroseconds(2); digitalWrite(trig, HIGH); delayMicroseconds(10); digitalWrite(trig, LOW); czas = pulseIn(echo, HIGH); dystans = czas / 58; return dystans; } Kod Processing: import processing.serial.*; //dodanie biblioteki Serial bt; //obiekt - port szeregowy int odleglosc = 0; //zmienna przechowująca odczyt z czujnika odległości int pozycja=9; //pozycja serwa boolean flag_up=false, flag_down=false, flag_lewa=false, flag_prawa=false; //flagi kierunku boolean pomiar = false; //flaga mówiąca czy robot ma wykonywać pomiar int speed = 6; //prędkość int licznik = 0; //wartość używana przy żądaniu przebytej odleglości float ang = 0; //odchylenie robota od położenia wyjściowego float x_co, y_co; //koordynaty x, y int steps=0; //odebrana ilośc przebytych kroków float[][] dots = new float[1000][2]; //tablica na przeszkody int odczyt; //dane odebrane z bluetooth int i; //zmienna pomocnicza przy rysowaniu przeszkód void setup(){ size(1000,500); //rozmiar okna bt=new Serial(this, "COM13", 9600); //tworzenie obiektu bt.buffer(1); //ilośc bajtów do buforowania } void serialEvent (Serial bt){ //SerialEvent odczyt = bt.read(); if(odczyt >= 115) {steps = (odczyt-115)*10; //informacja o przebytych krokach pozycja(); //odświeżenie pozycji } else {odleglosc = odczyt; //informacja o nowej przeszkodzie dots[i][0]=x_co+odleglosc*2.5*sin(ang+((float)(9-pozycja)/18)*PI); //OX dots[i][1]=-y_co-odleglosc*2.5*cos(ang+((float)(9-pozycja)/18)*PI); //OY i++;} } void draw(){ background(100); fill(255, 255, 255); rect(500, 0, 500, 500); for(int i = 0; i<21; i++) //tworzenie siatki line(500, i*25+y_co%25, 1000, i*25+y_co%25); for(int i = 0; i<21; i++){ if(i*25-x_co%25>0) line(500+i*25-x_co%25, 0, 500+i*25-x_co%25, 500);} textSize(10); fill(255, 255, 255); text("10cm", 470, 50+y_co%25); //jedna kratka 10x10cm fill(1); for(int j=0; j<=i; j++){ //wyswietlenie przeszkód if(dots[j][0] < 250-x_co && dots[j][1] < 250-y_co && dots[j][0] > -250+x_co && dots[j][1] > -250+x_co) rect(750+dots[j][0]-x_co, 250+dots[j][1]+y_co, 5, 5);} fill(0, 200, 0); //wyswietlenie robota triangle(750+10*sin(ang), 250-10*cos(ang), 750+10*sin(ang+PI*3/4), 250-10*cos(ang+PI*3/4),750+10*sin(ang+PI*5/4), 250-10*cos(ang+PI*5/4)); fill(150); //napis serwo i wskaźnik pozycji rect(50, 50, 200, 200); textSize(30); fill(255, 255, 255); text("Serwo", 60, 40); line(150, 200, 150-90*sin((float)(pozycja*10-90)/57), 200-90*cos((float)(pozycja*10-90)/57)); fill(150); //napis odległość i odczyt z czujnika odległości rect(300,50,150,50); fill(255, 255, 255); textSize(30); text("Odległość", 310, 40); text(odleglosc, 310, 90); fill(150); //przyciski do sterowania serwem rect(190,400,50,50); rect(260,400,50,50); fill(255, 0, 0); triangle(230, 440, 230, 410, 200, 425); triangle(270, 440, 270, 410, 300, 425); if(flag_up) fill(120); //sterowanie kołami else fill(150); rect(300, 150, 150, 50); if(flag_down) fill(120); else fill(150); rect(300, 300, 150, 50); if(flag_lewa) fill(120); else fill(150); rect(300, 225, 50, 50); if(flag_prawa) fill(120); else fill(150); rect(400, 225, 50, 50); if(flag_up) fill(150); else fill(255, 0, 0); triangle(350, 190, 375, 160, 400, 190); if(flag_down) fill(150); else fill(255, 0, 0); triangle(350, 310, 375, 340, 400, 310); if(flag_lewa) fill(150); else fill(255, 0, 0); triangle(340, 230, 310, 250, 340, 270); if(flag_prawa) fill(150); else fill(255, 0, 0); triangle(410, 230, 440, 250, 410, 270); fill(150); //przyciski do regulowania prędkości rect(50, 300, 50, 50); rect(50, 400, 50, 50); fill(255, 0, 0); triangle(60, 340, 75, 310, 90, 340); triangle(60, 410, 75, 440, 90, 410); fill(5); line(60, 390 - (speed*5), 90, 390 - (speed*5)); fill(150); //przycisk włącz/wyłącz autopomiar rect(150, 300, 100, 50); fill(255, 255, 255); textSize(20); if(pomiar) text("Pom. ON", 157, 340); else text("Pom. OFF", 155, 340); sterowanie(); } void sterowanie(){ //funkcja obsługi przycisków if(mousePressed && mouseX<250 && mouseX>150 && mouseY<350 && mouseY>300){ pomiar=!pomiar; //przycisk pomiar ON/OFF if(!pomiar) odleglosc=0; delay(100);} if(pomiar) licznik++; //automatyczne żądanie informacji o przeszkodach if(licznik == 50) { bt.write('7'); i++; licznik = 0;} if(mousePressed && mouseX<100 && mouseX>50 && mouseY<350 && mouseY>300 && speed<6){ speed++; //zwiększ prędkość bt.write(char(speed+80)); delay(100);} if(mousePressed && mouseX<100 && mouseX>50 && mouseY<450 && mouseY>400 && speed>1){ speed--; //zmniejsz prędkość bt.write(char(speed+80)); delay(100);} if(mousePressed && mouseX<240 && mouseX>190 && mouseY<450 && mouseY>400 && pozycja<18){ pozycja++; //obsługa strzałek do serwa bt.write(char(pozycja+100)); delay(100);} else if(mousePressed && mouseX<310 && mouseX>260 && mouseY<450 && mouseY>400 && pozycja>0){ pozycja--; bt.write(char(pozycja+100)); delay(100);} if(mousePressed && mouseX<450 && mouseX>300 && mouseY<200 && mouseY>150){ flag_up=!flag_up; //do przodu flag_down=false; flag_prawa=false; flag_lewa=false; if(flag_up) bt.write('2'); else bt.write('1'); delay(100);} if(mousePressed && mouseX<450 && mouseX>300 && mouseY<350 && mouseY>300){ flag_down=!flag_down; //do tyłu flag_up=false; flag_prawa=false; flag_lewa=false; if(flag_down) bt.write('3'); else bt.write('1'); delay(100);} if(mousePressed && mouseX<350 && mouseX>300 && mouseY<275 && mouseY>225) {flag_lewa=!flag_lewa; //obrót wokół własnej osi w lewo if(flag_lewa) bt.write('4'); else bt.write('1'); flag_up=false; flag_down=false; flag_prawa=false; delay(100); bt.write('6'); delay(100);} if(mousePressed && mouseX<450 && mouseX>400 && mouseY<275 && mouseY>225){ flag_prawa=!flag_prawa; //obrót wokół własnej osi w prawo if(flag_prawa) bt.write('5'); else bt.write('1'); flag_up=false; flag_down=false; flag_lewa=false; delay(100); bt.write('6'); delay(100);} } void pozycja() //funkcja odświeżania pozycji robota { if(flag_lewa) ang-=(float)(steps*TWO_PI)/5600; else if(flag_prawa) ang+=(float)(steps*TWO_PI)/5600; else if(flag_up){ x_co+=((float)steps*sin(ang))/40; y_co+=((float)steps*cos(ang))/40;} else if(flag_down){ x_co-=((float)steps*sin(ang))/40; y_co-=((float)steps*cos(ang))/40;} } ZASILANIE Robot zasilany jest z powerbanka umieszczonego pod sklejką. Planowałem wykorzystanie zwykłej baterii, lecz silniki krokowy pobierają razem prąd 600mA, co powodowało przerwy w zasilaniu z czterech paluszków AA. Mam nadzieję, że Wall-E spodoba się forumowiczom. Z przyjemnością publikuję jego opis na Forbocie. Mam 17 lat i rok temu zacząłem swoją przygodę z elektroniką i programowaniem, a mój pojazd jest jej efektem. W przyszłości planuję rozszerzyć jego możliwości dodając chociażby układ z rodziny ESP w miejsce Arduino, co otworzy go na IoT i zwiększy jego autonomię. Zachęcam do komentowania, jest to bezcenna możliwość do wymiany doświadczeń i opinii. Edytowano Styczeń 26, 2019 przez Treker Poprawiłem formatowanie. 1 Cytuj Link do komentarza Share on other sites More sharing options...
Treker (Damian Szymański) Styczeń 26, 2019 Udostępnij Styczeń 26, 2019 @FiFi98, witam na forum 😉 Widzę, że to Twoje pierwsze kroki na Forbocie, oto najważniejsze informacje na start: Chcesz przywitać się z innymi członkami naszej społeczności? Skorzystaj z tematu powitania użytkowników. Opis najciekawszych funkcji, które ułatwiają korzystanie z forum znajdziesz w temacie instrukcja korzystania z forum - co warto wiedzieć? Poszczególne posty możesz oceniać (pozytywnie i negatywnie) za pomocą reakcji - ikona serca w prawym dolnym rogu każdej wiadomości. Właśnie zaakceptowałem opis. Dziękuję za przedstawienie ciekawego projektu, zachęcam do prezentowania kolejnych DIY oraz aktywności na naszym forum 😉 1 Cytuj Link do komentarza Share on other sites More sharing options...
Pomocna odpowiedź
Dołącz do dyskusji, napisz odpowiedź!
Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!