Maskotka, Line Follower oraz podstawy

Pracujemy w trzech płaszczyznach (3D? hmmmm)

line follower, podstawy

  • podstawy (PWM, serwo silnik)
  • 3x czujka TCRT5000: Pan Bartek zlutował sobie moduł i zbudował PIERWSZEGO line followera (GRATULUJĘ!):
    • powyżej zastosowano nowatorskie rozwiązanie: czujki TCRT działają CYFROWO, ale zapamiętywana jest HISTORIA pomiarów (cyklicznie, 10 odczytów) a DECYZJA o zwrocie robocika podejmowana jest na podstawie UŚREDNIENIA historii – ciekawe rozwiązanie! Wszystko jest raczej tymczasowe, bo jest zamiar przeprogramowania układu – odczytywanie czujek analogowo, co poprawi dynamikę robota. Problemy techniczne – toporne koła i silniki – rozwiązujemy przez wymianę podzespołów na inne (zestaw silniczków i kół Pololu – niebawem w akcji)

(c) K.G. 2018

Sterowanie serwem za pomocą joysticka

Na zajęciach sterowaliśmy serwomechanizmami za pomocą joysticka. Na początku używaliśmy niewielkiego modułu z joystickiem podłączanego bezpośrednio do Arduino. W drugiej części użyliśmy nakładki (shield) na Arduino z joystickiem oraz czterema przyciskami (podobnie jak na gamepadach).

Moduł z joystickiem

Moduł posiada pięć pinów. GND oraz 5V podłączamy do Arduino. VRx i VRy to piny sterujące odpowiednio osią OX (czyli lewo-prawo) oraz OY (czyli góra-dół). Podłączamy je do pinów analogowych. Ostatni pin odpowiada za przycisk, jednak nie używaliśmy go w tym zadaniu.

Prosty program wyświetlający położenie joystika

void setup(){
  Serial.begin(9600);
}
int x,y;
void loop(){
   x = analogRead(A0);
   y = analogRead(A1);
   Serial.print("x=");
   Serial.print(x);
   Serial.print(", y=");
   Serial.println(y);  
  }

Położenie na osi OX (także OY) to liczby z zakresu 0..1023. Położenie spoczynkowe powinno odpowiadać wartości 511 (liczby 0..510 to wychylenie w lewo, liczby 512..1023 to wychylenie w prawo). Użycie tego programu pozwoliło nam sprawdzić, że tak jednak nie jest – u nas joy w położeniu spoczynkowym miał wartości 514, 517 (oś OX i OY).

Podczas działania tego programu możemy postawić sobie następujące zadanie: ustawić pozycję joy-a w takim położeniu, aby odczyty były x=800 y=200. Okazuje się to jednak bardzo trudne! Widzimy więc, że sterowanie joy-em nie jest łatwe i wymaga sporo wprawy.

Wieżyczka

Używana przez nas wieżyczka (do której można przyczepić, np. kamerkę internetową by nią sterować) składała się z dwóch serw na podstawce. Umożliwia to poruszanie się mechanizmu na boki oraz w górę i dół.

Wszystkie potrzebne elementy połączyliśmy za pomocą płytki prototypowej. Zamiast zasilania z Arduino użyliśmy zewnętrznego koszyka na 4 baterie o łącznym napięciu 5V. Dlaczego tak zrobiliśmy? Chodziło nam o oddzielne zasilanie silników aby nie przeciążyć płytki Arduino – pojedyncze serwo może pobrać nawet 200mA prądu (co sprawdzaliśmy na poprzednich zajęciach), a wydajność prądowa Arduino UNO to około 200-400 mA. Dlatego dwa takie serwa mogą uszkodzić naszą płytkę. My użyliśmy oddzielnego zasilania silników aby temu zapobiec. WAŻNE: w przypadku używania kilku źródeł zasilania (u nas Arduino 5V i 4x baterie AAA) musimy uwspólnilić masy (GDN z Arduino, „minus” z bateryjki).

Użyliśmy płytki prototypowej, gdzie na jednej szynie (koloru niebieskiego) wetknęliśmy „-” z koszyka baterii oraz GND Arduino. Do tej „szyny” podłączone były masy serw (przewody koloru brązowego). Druga szyna (czerwona – z drugiej strony płytki, dla naszej wygody) doprowadzone miała przewód „+” z koszyka baterii i tam podłączone były zasilania silników serw (czerwone przewody serw). UWAGA: nie można łączyć pinu 5V Arduino z zewnętrznym zasilaniem baterii – może to spowodować uszkodzenie płytki! Łączymy (=uwspólniamy) jedynie masy. Przewody sterujące serwami (koloru żółtego) podłączyliśmy do pinów PWM Arduino UNO – u nas #3 i #5.

Program sterujący

Na początku standardowo dołączamy do programu bibliotekę umożliwiającą sterowanie serwami. Następnie dodajemy zmienne do obu serw, jedno odpowiedzialne za ruch góra-dół, a drugie lewo-prawo.

#include <Servo.h>
Servo GoraDol;
Servo LewoPrawo;

void setup (){
  Serial.begin(9600);
  GoraDol.attach(3);
  LewoPrawo.attach(5);
}
int x,y;

W kolejnej części deklarujemy zmienne odpowiadające za położenie obu osi joysticka – dokładnie tak, jak w pierwszym programie odczytującym położenia joy-a. Wyświetlamy wartości x oraz y w monitorze szeregowym. Joystick zwykle nie jest idealnie skalibrowany, jednak nie ma to znaczenia przy niewielkiej precyzji.

Funkcja map pozwala na łatwe proporcjonalne przeliczenie wartości. Używamy jej, ponieważ położenia joysticka są z zakresiu 0-1023, natomiast sterowanie serwem chcemy wyrażamy w stopniach 0-180. Wynik przypisujemy od razu do zmiennych x oraz y (używamy tych samych zmiennych, niszcząc ich poprzednie wartości). W argumentach funkcji wpisujemy zmienną do przeliczenia, następnie jej obecny zakres i na końcu zakres po zamianie. Po przeliczeniu wartości możemy je przekazać do serwomechanizmów.

   x=map(x,0,1023,0,180); //funkcja map
   LewoPrawo.write(x);
   y=map(y,0,1023,0,180);
   GoraDol.write(y);
   delay(10);

Nakładka (shield) na Arduino

Fajnym rozwiązaniem jest wykorzystanie specjalnej nakładki (shield), która poszerza możliwości Arduino. Piny obu komponentów pasują do siebie, więc nie da się pomylić przy wpinaniu nakładki. Joystick oraz przyciski są fabrycznie podłączone do pinów, więc nie musimy tego robić w programie. Na nakładce wszystkie piny są podpisane, więc później będziemy tylko operować odpowiednimi oznaczeniami pinów.

Modyfikacja programu – przycisk „zamrażający” położenie wieżyczki

Program sterujący jest bardzo podobny do poprzedniego. Jedyną różnicą jest brak podłączenia pinów cyfrowych. Dodatkową funkcją, którą wprowadzamy do naszego programu, jest zatrzymywanie serwa w ustawionej pozycji. Chcemy aby wciśnięcie wybranego przycisku (np. koloru czerwonego) zablokowywało dalsze sterowanie wieżyczką. Kolejne wciśnięcie tego przycisku powoduje odblokowanie sterowania. W tym celu do kodu wprowadzamy zmienną typu logicznego bool (nazwaną tryb) i ustawiamy ją na wartość true. Następnie wybieramy przycisk (np. ten czerwony), który ma sterować zatrzymaniem serwa i  sprawdzamy, który numer pinu u odpowiada. W naszym programie jest to pin 5.  Tworzymy instrukcję sterującą if, która wykona się po wciśnięciu przycisku. Do wartości zmiennej tryb, przypisujemy jej odwrotność. Czyli jeśli tryb jest true, zostanie zmieniony na false i odwrotnie. Funkcja delay() wprowadzamy aby zarejestrować tylko jedną zmianę ustawienia przycisku (przyciski lubią „drgać” co powoduje nie jeden „klik” a wiele takich „klików” – opóźnienie je zniweluje). Działa to w taki sposób, że jeśli tryb jest wartością false, czyli zostanie raz zmieniony we wcześniejszym if’ie, sterowanie joyem zostaje zablokowane. Natomiast po kolejnym użyciu przycisku, tryb zmieni się z false na true, a sterowanie zostanie odblokowane.

bool tryb=true;
void loop(){
  x=analogRead(A0);
  y=analogRead(A1); 
   if (digitalRead(5)==HIGH){
      tryb=!tryb;
      delay(50);
   }

   if(tryb==true){ 
   x=map(x,0,1023,0,180); //funkcja map
   LewoPrawo.write(x);
   y=map(y,0,1023,0,180);
   GoraDol.write(y);
  }
}

Podsumowanie

Jak można zauważyć programy umożliwiające sterowanie servami nie są skomplikowane ani długie. Można sprytnie wykorzystać komponenty posiadające więcej niż jedną oś ruchu, dzięki czemu nasze możliwości się poszerzają. Jednak precyzyjne sterowanie takimi joystikami nie jest łatwe…

(c) Ewelina, KG 2017

joyshield + stringi + nRF24

Dziś nowy gadget:

czyli joy-shield. Co to ten „shield”? To rozszerzenie, z różnymi modułami, które nadrukowane są na płytkę PCB i nie wymagają płytki stykowej i masy przewodów. Taką płytkę „wtyka” się w piny Arduino, tworząc „kanapkę”. Ten shield ma „gierkowe” przeznaczenie – choć my go wykorzystamy do sterowania pojazdem, ale są też inne (do internetu przez kabel eRJotkę, przez sieć bezprzewodową, z kartą SD do zapisu danych, z obsługą silników i inne). Warto łapać okazję na allegro lub podobnych, bo ja swój shield kupiłem za 25 zł! A co on ma w sobie?

  • Joystick (dwie osie + przycisk)
  • cztery przyciski z kolorowymi klawiszami
  • dwa dodatkowe przyciski microswich
  • złącze dla modułów transmisji radiowych:
    — nRF24xx
    —  xbee
    — APC200
  • stabilizator napięcie 3.3V (do zasilania układów radiowych)

Oczywiście moduł (=shield) przeznaczony do współpracy z płytkami Arduino UNO, MEGA, LEONARDO itp. Warto zwrócić uwagę na jakość wykonania – mój ma fajne opisy w łatwo dostępnych miejscach.

Obsługa joysticka – shielda

Okazuje się banalnie prosta. Czytamy położenie dwóch osi (x i y) – czyli porty analogowe A0 i A1 (cóż, ten shield je sobie „rezerwuje” i nie mamy ich możliwosci wykorzystywania). Wszystko ładnie opisane na płytce. Podobnie z przyciskami.

void setup() {
Serial.begin(9600);
}
int x,y;
void loop() {
  x=analogRead(A0);
  y=analogRead(A1);
  Serial.print(x);
  Serial.print("  ");
  Serial.print(y);
  if (digitalRead(5)==1)
    Serial.print(" niebieski");
  if (digitalRead(4)==1)
    Serial.print(" bialy");
  if (digitalRead(3)==1)
    Serial.print(" czerwony");
//i tak dalej
  Serial.println();
  delay(100);
}

Zwracam uwagę na „sprytne” granie metodą print oraz println wraz ze spacjami tak, aby wszystko mieściło się w jednej linii i nie było „pozlepiane”.

A odczytywanie przycisków – skoro używam funkcji digitalRead(pin) to dlaczego nie ma wcześniej pinMode(pin, INPUT)? Otóż domyślnie piny ustawione są w trybie INPUT, dlatego właśnie nic nie dopisywałem. Jesto to warte zapamiętania, co powino nie być trudne – w końcu dla wejść analogowych nie ustawialiśmy w tryb INPUT, czyli z wejściami cyfrowymi jest tak samo.

Stringi = napisy

Temat rzeka… trzeba zacząć, czym są tablice w C. Zauwazyłem, że godnym polecenia jest artykuł na oficjalnej stronie Arduino. Ja dołączam „zrzut ekranu” z zajęć.

Jak zmienić nasz poprzedni kod, aby działał na napisach?

void setup() {
   Serial.begin(9600);
}

String napis;
void loop() {
  napis=String(analogRead(A0));
  napis = napis + "  ";
  napis = napis + String(analogRead(A1));
  if (digitalRead(5)==1)
    napis = napis + String(" niebieski");
  if (digitalRead(4)==1)
    napis += String(" bialy");
  if (digitalRead(3)==1)
    napis += String(" czerwony");
  //i tak dalej
  Serial.println(napis);
  delay(100);
}

Oczywiście nic tutaj nie zyskaliśmy, a nawet gorzej – obsługa klasy String w Arduino jest zasobożerna – czyli mocno powiększa nasz kod, ale tym się jeszcze nie przejmujemy. Na uwagę zasługuje łatwe dodawanie (łączenie) ze sobą napisów – realizuje to zwykły operator + (plus)

Joyshield + nRF24L01+

Łączymy poznane dziś rzeczy z poprzednimi zajęciami i nadajemy to, co robimy na naszym kontrolerze. Właśnie dlatego użyliśmy wersji Stringowej kodu do joysticka, bo radyjko nadawało właśnie napisy – a nawet napisy z maksymalną długością 32 znaków (bajtów). Poniżej kod:

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <printf.h>

RF24 radio(9, 10);//CE, CS

uint8_t rxAddr[6] = "grzyb";

void setup(){
  Serial.begin(9600);
  Serial.print("nRF24 INIT=");
  bool ok=radio.begin();
  Serial.println(ok);
  printf_begin;
  radio.printDetails();
  radio.setRetries(15, 15);
  radio.openWritingPipe(rxAddr);
  
  radio.stopListening();
}

String napis;

void loop(){
  napis = String("Heniek "); 
  napis += String(analogRead(A0));
  napis += " ";
  napis += String(analogRead(A1));
  if (digitalRead(5)==1)
    napis += String(" niebieski");
  if (digitalRead(4)==1)
    napis += String(" bialy");
  if (digitalRead(3)==1) 
    napis += String(" czerwony");
  //i tak dalej<br>
  radio.write(napis.c_str(), napis.length());
  Serial.println(napis);
  delay(100);
}

Na uwagę zasługuje linia nr 37, gdzie musiałem użyć nowej metody klasy String o nazwie c_str(), której zadaniem jest stworzenie napisu w stylu języka C (nieobiektowego). Wymuszone jest to przez metodę write() klasy radio. Dodatkowo, długość napisu w klasie String uzyskujemy za pomocą metody length() – a nie sizeof(napis), gdyż to by zwróciło tylko rozmiar obiektu (a nie danych wewnątrz tego obiektu).

Z kolei w linii 26 rozpocząłem nasz napis imieniem (fikcyjnym) uczestnika koła Fi-BOT tak, aby uruchomiony przeze mnie program odbiornika nRF mógł łatwo zidentyfikować kto mi nadaje (Tosiek, Heniek czy Ziuta — także fikcyjne imiona).

Co dalej?

Wydawać by się mogło, że trzeba teraz tak przygotowany kod uruchomić do sterowania pojazdem. Nieprawda. Było by to baaaardzo kłopotliwe. Problem polega na tym, że trzeba odczytać napis w odbiorniku nRF umieszczonym w pojeździe, a potem odczytać aktualne wychylenie osi X i Y joya, i przyciski. Ale to trudne! Nawet przy pomocy funkcji toInt() zamieniającej napis na liczbę całkowitą – bo najpier należało by podzielić nasz napis na części – może przecinkami, jakoś tak:

123,512,czerwony,zielony

Czyli wówczas przecinek (kropka, myślnik – cokolwiek) by nam oddzielał jedną informację od drugiej, co by było do wykorzystania w połączeniu z metodą indexOf() z klasy String. Cała procedura mogła by wyglądać jakoś tak:

  • odczytujemy napis w pojeździe, przesłany przez joyshield (z przecinkami)
  • skupiamy się na pierwszej części napisu, aż do pierwszego przecinka – wiemy, że ma to być pozycja na osi X – funkcja StringSubstring() i/lub wspomniana indexOf()
  • zamieniamy ten krótki podciąg (od zera do 4 znaków) na liczbę całkowitą – metoda toInt() – i mamy już oś X
  • kasujemy/wyrzucamy z napisu pierwszą część do przcinka, przechodzimy do pozycji na osi Y
  • robimy podobnie, a potem
  • jeśli napis nie jest jeszcze pusty (bo ciągle kasowaliśmy informację o osi X i Y), to znaczy, że mamy jakieś przyciski
  • jeśli jest przecinek, to znaczy, że są nawet dwa (lub więcej) wciśniętych przycisków
  • tworzymy pierwszy podciąg aż do pierwszego przecinka, porównujemy czy jest to „zielony”, „czerwony” itd…
  • po odczytaniu przycisku kasujemy z napisu tą informację
  • jeśli napis niepusty, to są kolejne przyciski…

Proszę zwrócić uwagę, jakie to zagmatfane! Dlatego na kolejnych zajęciach będziemy wysyłać z nadajnika nie napisy (Stringi), ale bajty z informacjami. Dwa bajty wystarczą do podania osi X, kolejne dwa do osi Y, a jeszcze jeden – tylko jeden – do podania stanu wszystkich 7 przycisków na naszym shieldzie. Stworzymy własny protokół danych – podobny do tego, jakiego używaliśmy przy omawianiu oscyloskopu. Dlatego zachęcam do ponownego wczytania się w tamten wpis i opis protokołu danych. Właśnie to nas czeka na następnych zajęciach! Ekscytujące, nieprawdaż? 😉

Przypominam o spotkaniu w ten piątek 19-maja o 14:15 w sprawie XV Festiwalu, a kolejne zajęcia we wtorek 23 maja o godz. 16:15. Nie wiem, czy będziemy mieć siły na 22 więc od razu uprzedzam, że w poniedziałek 22 maja, podczas imprezy Festiwalowej, ustalimy termin kolejnego spotkania Fi-BOTa. Zapraszam!