Skocz do zawartości
FiFi98

Pojazd Wall-E i program rysujący mapę

Pomocna odpowiedź

Napisano (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.

IMG_20190125_224054.thumb.jpg.cf738ab53dd2feb0768f6191c8182d14.jpgIMG_20190125_224128.thumb.jpg.9b97710a201f743660fc3bd6871c190a.jpgIMG_20190125_223724.thumb.jpg.93c96c6c31fa0a2b8592b150ab4610ca.jpgIMG_20190125_223706.thumb.jpg.1103162c5b03168bdf6ab59c4f4eaa57.jpg

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.

IMG_20190125_224157.thumb.jpg.7bb3d2885d0fe3a663e978a5db6a8d50.jpg

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.

IMG_20190125_224118.thumb.jpg.d0ae58731e2ce83238cb3672d009595a.jpgIMG_20190125_224106.thumb.jpg.910e5a92ebea7da995c171e27eddf952.jpg

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ł.

1615952904_Beztytuu.thumb.png.a09b45d71f929887305f53fb329b832b.png1180739527_Beztytuu2.thumb.png.177d5bfdcb5e2808c1df78c11aef75a0.png1895605582_Beztytuu3.thumb.png.7986e1e963bdd3505d2d9a7394e2c347.png

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.

IMG_20190125_224145.jpg

Edytowano przez Treker
Poprawiłem formatowanie.
  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

Podoba Ci się ten projekt? Zostaw pozytywny komentarz i daj znać autorowi, że zbudował coś fajnego!

Masz uwagi? Napisz kulturalnie co warto zmienić. Doceń pracę autora nad konstrukcją oraz opisem.

@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 😉

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

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!

Gość
Napisz odpowiedź...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.


×
×
  • Utwórz nowe...