Opis algorytmu PD

Z tego wpisu dowiesz się:

  • Jak płynnie podążać za linią?
  • Jak przetwarzać sygnały z czujników by sterować szybkością poszczególnych silników?
  • Co to jest tzw. „pętla zwrotna”?

A wszystko po to, by oprogramować Line Followera (w skrócie LF)!

Regulator PD to “ucięta” wersja regulatora PID (ang. Proportional-Integral-Derivative). Posłuży nam on do płynnego sterowania linefollower’em. Regulator ten posiada 3 człony:
liniowy (P),
całkowy (I),
różniczkowy (D).

Człon P odpowiada za to, jak szybko silniki LF’a będą się obracać na podstawie położenia linii. Jeżeli linia będzie po prawej stronie czujnika to robot “wie”, że musi skręcić w tym kierunku i zmniejszy szybkość prawego silnika (lub zwiększy prędkość lewego, ale z reguły lepiej zastosować to pierwsze rozwiązanie). Jednak sama zależność liniowa nie wystarcza, żeby płynnie podążać za linią (chyba, że bardzo powoli). Żeby “przewidzieć” zmiany w kierunku ruchu i odpowiednio na nie zareagować przydaje się nam człon D regulatora. Jest to prosta różnica pomiędzy poprzednią a aktualną pozycją linii. W przypadku prostej linii różnica będzie nieznaczna, a podczas nagłej zmiany kierunku robot szybciej zmieni szybkość silników. Człon I pomijamy, bo nie jest on zbyt ważny w przypadku linefollower’ów (z doświadczenia).

Może i jest to na początek trochę skomplikowane, ale implementacja jest bardzo prosta i nie wymaga zbyt wiele pisania:

error_P = pozycja_linii * KP; // zależność liniowa
error_D = (pozycja_linii - poprzednia_pozycja_linii) * KD; // różnica pomiędzy pomiarami
Error = error_P + error_D; // suma członów regulatora
poprzednia_pozycja_linii = pozycja_linii;

Uzyskany w ten sposób Error możemy bezpośrednio wykorzystać do ustawienia prędkości silników. Współczynniki KP i KD określają jaki wpływ mają poszczególne człony na ruch LF’a i będą się znacząco różnić w zależności od algorytmu wykrywania pozycji linii czy samej budowy robota (np. pozycji czujnika). Właśnie w konkretnych wartościach KP, KD leży sekret działania dobrego regulatora PID. Żeby uzyskać płynny ruch trzeba się trochę “pobawić” z tymi współczynnikami i znaleźć najlepsze wartości. Można znaleźć wiele metod strojenia takiego regulatora (np. Metoda Zieglera-Nicholsa), jednak w tym przypadku najprościej jest to zrobić ręcznie czyli metoda prób i błędów, eksperymentalnie zmieniając KP i KD i sprawdzając na torze jak to wpływa na ruch LF.

Na początku ustawiamy KD = 0, i szukamy najmniejszego KP, żeby robot przy małej prędkości w miarę dobrze podążał po prostej linii lub lekkim łuku. Po znalezieniu odpowiedniego KP możemy stopniowo zwiększać współczynnik KD i prędkość, do uzyskania płynnego ruchu. Jako, że różnice w pomiarach są stosunkowo małe w porównaniu do położenia linii, często następuje zależność: KD > KP.

Określanie pozycji linii, wspólne działanie 5-ciu czujników

Jak przy pomocy wielu czujek jednoznacznie określić, gdzie znajduje się linia? Każda czujka zwraca tylko informację o ilości odbitego światła (wartości od 0 do 1023). Można by zastosować algorytm który wybiera jedną pozycję z największą wartością ale prowadzi on tylko do niepłynnego ruchu. Powinniśmy zastosować bardziej precyzyjny algorytm wykrywania linii ze średnią ważoną.

Każdemu czujnikowi przypisujemy wagi, na których postawie będziemy określać pozycję (np. -2000, -1000, 0, 1000, 2000 dla kolejnych czujek). Następnie czytamy wartości z czujników, mnożymy je razy wagę i sumujemy. Na koniec dzielimy otrzymaną wartość przez sumę wartości z czujek. W ten sposób dokładną informację nawet kiedy linia znajduje się na granicy dwóch czujników.

pozycja = (cz1*waga1+cz2*waga2+cz3*waga3+cz4*waga4+cz5*waga5)/(cz1+cz2+cz3+cz4+cz5)

W przypadku zastosowania wag: -2000, -1000, 0, 1000, 2000 dla kolejnych czujników, po przeliczeniu średniej ważonej otrzymamy jedną wartość od -2000 do 2000 (dla -2000 linia znajduje się po lewej stronie, dla 0 – na środku, a dla 2000 – po prawej).

Sterowanie prędkością silników

Jak wykorzystać wcześniej otrzymany Error do sterowania prędkością silników? Do ustawienia prędkości pojedynczego silnika używamy sygnału PWN. Będziemy tą wartość zmieniać w zależności od uzyskanego Error’a.

Jeżeli wartość Error’a jest ujemna to w naszym przypadku pozycja linii znajduje się po lewej stronie LF’a. Aby skorygować ruch będziemy zmniejszać prędkość lewego silnika. Analogicznie postępujemy w przypadku dodatniej wartości:

if(Error > 0){
pwn1=maxSpeed; //maxSpeed to wczesniej ustalona maksymalna wartość PWN LF’a
pwn2=maxSpeed-Error;
} else {
pwn1=maxSpeed+Error;
pwn2=maxSpeed;

Warto pamiętać, że Error może być znacznie większy od wartości maxSpeed. Jeżeli wartość nowego pwn1 lub pwn2 jest mniejsza od 0 to musimy ją ustawić na 0 (pwn powinien wynosić od 0 do 255). Po tych wszystkich operacjach uzyskujemy pwn’y do sterowania silnikami.

Mój LF w akcji:

(c) Bytler, 2019

 

kolorowo – ws2812b

Poznaliśmy bardzo ciekawy układ: 3x LED (kolor czerwony, niebieski i zielony) + sterownik ws2811 – wszystko razem tworzy moduł oświetlenia RGB, który bardzo łatwo się steruje – a uzyskane efekty są bardzo przyjemne ;-)Moduły mogą być sprzedawane osobno (pojedynczo), ale super prezentują się układy połączone w „oczko” lub listwy.

3x RGB

Każdy LED świeci światłem o danej długości (L [nm]), maksymalną jasnością (J [mcd]) i cechuje się też konkretnym spadkiem napięcia (V [V]). Poniżej dane z datasheet.:
Red L=620-625 J=390-420 V=2.0-2.2
Green L=522-525 J=660-720 V=3.0-3.4
Blue L=465-467 J=180-200 V=3.0-3.4

Trzeba uważać, aby zasilając takie paski uważnie dobrze policzyć wymagany MAKSYMALNY prąd pobrany przez układ! Mamy tu 3 LEDy, więc gdy wszystkie będą świecić na 100% to potrzeba ~3x20mA na każdy jeden moduł. Nasze „oczko” ma 12 takich układów, więc daje to około 0.72A prądu –  czyli już uszkodziliśmy Arduino (pamiętamy, że wydajność prądowa płytki Arduino UNO <0.5A ? trzeba na to uważać!). Gdy zasilamy paski/oczka bezpośrednio z Arduino proszę zmniejszyć jasność ledów do 10% albo i niżej (w zależności ile ich mamy).

Przestrzegaj tych reguł (wzięte z /dev/jarzebski)

  • Nie przekraczaj napięcia zasilania powyżej 5V,
  • Dodaj kondensator elektrolityczny o pojemności od 100µF do 1000µF (np.: 6.3V lub wyższy) przy zasilaniu pierwszej diody,
  • Dodaj rezystor o wartości od 300Ω do 1kΩ pomiędzy mikrokontrolerem, a pierwszym pinem DIN. Rezystor ten umieść jak najbliżej diody,
  • Postaraj się możliwie skrócić odległość pomiędzy mikrokontrolerem, a pierwszą diodą,
  • Nie podłączaj diod przy włączonym zasilaniu. Jeśli już musisz, rób to w kolejności: masa, zasilanie, linia sterująca, a odłączaj w odwrotnej kolejności
  • Jeśli diody zasilasz z oddzielnego źródła zasilania, najpierw doprowadź zasilanie do diod, potem do mikrokontrolera,
  • Pamiętaj o zabezpieczeniu antystatycznym 🙂 swetry, polary i inne ubiory łatwo gromadzące ładunek nie są wskazane

Polecam cały wpis o tych układach na /dev/jarzebski.

NeoPixel

Biblioteka do programowania układów WS2812B.

    #include <Adafruit_NeoPixel.h>
     
    #define PIN 7
    #define LICZBADIOD 12
     
    Adafruit_NeoPixel pixels = Adafruit_NeoPixel(LICZBADIOD, PIN, NEO_GRB + NEO_KHZ800);
     
    void setup(){
      pixels.begin(); // najpierw inicjalizacja biblioteki
    }
    
    int nr=0;
    void loop(){
        if (nr > LICZBADIOD) nr=0;
        pixels.setPixelColor(nr, random(0,256), random(0,256), random(0,256)); //programujemy LED-a o numerze nr
        pixels.show(); // konieczne, aby zmiany były widoczne
        delay(30);
        pixels.setPixelColor(nr, 0, 0, 0); //kolor czarny dla  LED-a o numerze nr 
        nr++;
    }

Kompilacja zakończona z komunikatem:

Szkic używa 3046 bajtów (9%) pamięci programu. Maksimum to 32256 bajtów. Zmienne globalne używają 46 bajtów (2%) pamięci dynamicznej, pozostawiając 2002 bajtów dla zmiennych lokalnych.

Zmieniamy LICZBEDIOD z 12 na 59 i… ten sam rozmiar zajmowanej pamięci. Ta uwaga do porównania dla kolejnej (popularnej) biblioteki.

FastLed

Kolejna biblioteka do programowania tych układów WS2812B.

#include <FastLED.h>
#define NUM_LEDS 12
#define DATA_PIN 7

CRGB leds[NUM_LEDS];

void setup() { 
   FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);
   FastLED.setBrightness(10);
}

void loop() { 
      if (nr > NUM_LEDS) nr = 0;
      //leds[nr] = CRGB::Blue; 
      leds[nr].r = random(0, 256);  
      leds[nr].g = random(0, 256);  
      leds[nr].b = random(0, 256);       
      FastLED.show();   //wyswietlamy zaprogramowale kolory LEDow
      leds[nr] = CRGB::Black;
      delay(30);
      nr++;
}

Kompilujemy i widzimy:

Szkic używa 4040 bajtów (12%) pamięci programu. Maksimum to 32256 bajtów. Zmienne globalne używają 139 bajtów (6%) pamięci dynamicznej, pozostawiając 1909 bajtów dla zmiennych lokalnych.

Widzimy, że ten sam kod jest większy z wykorzystaniem FastLed-a. OK. Idziemy krok dalej i zmieniamy NUM_LEDS na 59 i…

Szkic używa 4040 bajtów (12%) pamięci programu. Maksimum to 32256 bajtów. Zmienne globalne używają 280 bajtów (13%) pamięci dynamicznej, pozostawiając 1768 bajtów dla zmiennych lokalnych.

Która biblioteka lepsza – FastLed czy NeoPixel? hmmm… Po pierwsze: obie są proste w użyciu – to zaleta obu. Jednak: FastLed jednak rezerwuje 3 bajty (kolory RGB) dla każdego ws2812b – co jest dość pamięciożerne, tym bardziej, że takie Arduino UNO ma jedynie (aż?) 2kB pamięci. Jednak o wadach i zaletach na razie nie będę się  wypowiadać.

(c) K.G. 2019

Nokia 5510 – wizualizacja 3D

Czy można zmusić Arduino do wyświetlania grafiki 3D? Wiadomo, że do grania w gry potrzebujemy potężnych akceleratorów graficznych (karty nVidii lub ATI) lub dla mniej wymagającej grafiki mocnego CPU. Arduino może i nie ma wielkich zasobów mocy obliczeniowej, ale przy użyciu prostego wyświetlacza i lekkiej imitacji biblioteki 3d możemy uzyskać interesujące efekty. Stworzę “silnik 3D” – który w zasadzie jest tylko prostym rzutowaniem punktów na ekran. Pomimo ograniczonej funkcjonalności, możemy uzyskać ciekawe efekty takie jak obracająca się przestrzenna siatka sześcianu.

Cała sztuczka uzyskania wrażenia 3D polega na dobrym rzutowaniu punków w przestrzeni na piksele ekranu. Wiadomo, że obiekty dalsze są mniejsze, a te bliższe – są większe (perspektywa!). Ta obserwacja prowadzi mnie do banalnego pomysłu – nie będę rzutować trójwymiarowych punktów (x,y,z) na ekranik wyświetlacza (x,y), uwzględniając pozycję kamery w przestrzeni a jedynie… będę zmniejszać/zwiększać obiekty w zależności od ich odległości! Do oddania efektu głębi użyję funkcji wykładniczej a nie liniowej – to kolejna obserwacja, z którą trudno się nie zgodzić (obiekt dwa razy dalej nie jest wcale dwa razy mniejszy!). Dzięki temu obiekty znajdujące się bliżej będą powiększane, a te dalej pomniejszane.

Daje nam to wrażenie, że obiekty znajdujące się dalej zbliżają lub oddalają się wolniej, a natomiast te bliżej – szybciej. Oddalające się punkty powinny się zbiegać do środka ekranu aby otrzymać efekt perspektywy punktowej.

Jako przykład użyjemy wyświetlacza Nokii 5110 oraz bibliotek Adafruit_PCD8544.h oraz Adafruit_GFX.h. Biblioteki posłużą nam do kontrolowania ekranem i wyświetlania linii.

Na początku musimy zaimplementować rzutowanie:

struct point2D{
 int x,y;
};

struct point3D{
 float x,y,z;
 point2D CastTo2D(){
   point2D ret;
   ret.x = szerokosc_ekranu/2 + (x * X_SCALE *  pow(Z_SCALE, z));
   ret.y = wysokosc_ekranu/2 - (y * Y_SCALE * pow(Z_SCALE, z));
   return ret;
 }
};

Tworzę w ten sposób dwie struktury oraz metodę do późniejszego rzutowania na ekran. X_SCALE, Y_SCALE to współczynnki skalowania na poszczególnych osiach ekranu (bardzo przydatne w przypadku gdy piksele wyświetlacza nie są kwadratowe). Z_SCALE natomiast to współczynnik skalowania głębi.

Po podłączeniu ekranu do Arduino używamy bibliotek, żeby coś wyświetlić:

#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>
Adafruit_PCD8544 disp = Adafruit_PCD8544(5, 4, 3);

void setup() {
    disp.begin();
    disp.display(); // wyswietlanie buffera
}

Ten krótki kawałek kodu powinien wyświetlić logo Adafruit na ekranie. Można poświęcić chwilę na przyjrzenie się bliżej bibliotece i postarać się stworzyć własne logo, np. takie:

Gdy już wszystko nam działa możemy przejść do wyświetlenia czegoś przestrzennego. Pomocna będzie funkcja rysująca linię na ekranie pomiędzy punktami w przestrzeni:

void draw3DLine(point3D a, point3D b){
 disp.drawLine(a.CastTo2D().x, a.CastTo2D().y, b.CastTo2D().x, b.CastTo2D().y, BLACK);
}

Musimy jednak mieć na uwadze w jaki sposób punkty przestrzenne są konwertowane na piksele ekranu. Punkt (0,0,0) znajduje się na środku ekranu, a składowa Z określa powiększenie (Z>0) lub pomniejszenie (Z<0) obiektu. Dla Z=0 rozmiar obiektu nie zostanie zmodyfikowany. Znaczenie ma również skala (X_SCALE, Y_SCALE), gdyż to właśnie przez te wartości mnożymy położenie punktu (dla małej skali musimy podać większe współrzędne, bo nasz obiekt na ekranie może okazać się jedynie małą kropką).

Wyświetlenie siatki sześcianu to odpowiednie połączenie ośmiu punktów – dosyć proste. Ale jak sprawić, żeby ten sześcian się obracał? W tym celu sięgamy po parę funkcji trygonometrycznych, a mianowicie sinus i cosinus.

Zamiast podawać konkretne współrzędne x i y, możemy użyć kąta i odległości od środka. Punkt (r*cos(a), r*sin(a)), niezależnie od kąta a, jest zawsze oddalony o odległość r od punktu(0,0). Zwiększając ten kąt, punkt będzie “wędrować” po okręgu. Możemy to wykorzystać właśnie do obracania sześcianem.

Teraz możemy określić pionową parę punktów współrzędnymi:

(cos(a)*R, y, sin(a)*R) i (cos(a)*R, -y, sin(a)*R),

gdzie R to promień okręgu, po którym będą poruszać się punkty i jednocześnie połowa przekątnej sześcianu. Aby uzyskać następną parę punktów musimy je obrócić o 90 stopni, czyli do poprzedniego kąta dodajemy ℼ/2:

(cos(a+PI/2)*R, y, sin(a+PI/2)*R) i (cos(a+PI/2)*R, -y, sin(a+PI/2)*R).

W podobny sposób uzyskujemy pozostałe pary punktów dodając odpowiednio wielokrotność kąta ℼ/2 (dla trzeciej pary: 2*PI/2 i czwartej: 3*PI/2).

Kiedy już zapisaliśmy pozycje punktów w tej postaci, możemy zwiększać kąt a (lub zmniejszać co spowoduje obrót w przeciwnym kierunku). Warto pamiętać, że jest to kąt w radianach i należy go zwiększać o stosunkowo małe wartości.

Efekt jest następujący:

Kod programu:

#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>
#define Z_SCALE 1.2
#define X_SCALE 20
#define Y_SCALE 16
#define ANGLE 0.08
#define DT 60
#define RADIUS 1.3

struct point2D{
  int x,y;
};

struct point3D{
  float x,y,z;
  
  point2D CastTo2D(){
    point2D ret;
    ret.x = round(42. + (x * X_SCALE *  pow(Z_SCALE, z)));
    ret.y = round(24. - (y * Y_SCALE * pow(Z_SCALE, z)));
    return ret;
  }
};
// Software SPI (slower updates, more flexible pin options)
// pin 7 - Serial clock out (SCLK)
// pin 6 - Serial data out (DIN)
// pin 5 - Data/Command select (D/C)
// pin 4 - LCD chip select (CS)
// pin 3 - LCD reset (RST)
Adafruit_PCD8544 disp = Adafruit_PCD8544(7, 6, 5, 4, 3);

void draw3DLine(point3D a, point3D b){
  disp.drawLine(a.CastTo2D().x, a.CastTo2D().y, b.CastTo2D().x, b.CastTo2D().y, BLACK);
}

void setup() {
  disp.begin();
}

float fi = 0.;
void loop() {

  fi+=ANGLE;
  disp.clearDisplay();
  for(int i=0; i<4; i++){
    point3D a= {cos(i*2*M_PI/4 + fi)*RADIUS, 1, sin(i*2*M_PI/4+ fi)*RADIUS};
    point3D b= {cos(i*2*M_PI/4 + fi)*RADIUS, -1, sin(i*2*M_PI/4+ fi)*RADIUS};
    draw3DLine(a,b);
    point3D c= {cos((i+1)*2*M_PI/4 + fi)*RADIUS, 1, sin((i+1)*2*M_PI/4+ fi)*RADIUS};
    point3D d= {cos((i+1)*2*M_PI/4 + fi)*RADIUS, -1, sin((i+1)*2*M_PI/4+ fi)*RADIUS};
    draw3DLine(a,c);
    draw3DLine(b,d);  
  }
  disp.display();
  delay(DT);
}

 

 

(c) Bartosz Bytler 2019

Pomiar ciągu silnika

Pomiar ciągu silnika

Okazuje się, że zaproponowany na zajęciach schemat pomiarowy (wykorzystany do określenia ciągu naszego balonu) nie zadziała w przypadku silniczka:

Dorysowane strzałki tłumaczą sprawę, ale wyjaśniam: waga nie wskazała mniejszej masy, gdyż powietrze z wirujących łopat śmigieł powodowało ciśnienie na wagę… Z balonem ten pomysł „wypalił”, a tutaj nie. Ulepszony zestaw pomiarowy wykorzystujący „dźwignię”  – ważne: oba ramiona tej samej długości, ech ta fizyka 😉

Tym razem nie ma problemu z ciśnieniem powietrza, gdyż to wypychane jest za dźwignię, podnosząc lekko ramię – co rejestruję na wadze. Działa to w dwie strony: przy odwrotnej pracy silnika (lub innych śmigłach: CW zamiast CCW) ramię będzie dociskane do wagi i obserwować będę zwiększenie wskazań masy. Trzeba więc jedynie zanotować odczyt wagi na początku (przed uruchomieniem silniczka) i wykonywać pomiary.

Pomiary najlepiej wykonywać zmieniając napięcie zasilające silniczek – ja tu mam regulowany zasilacz laboratoryjny, który dodatkowo podaje mi informację o prądzie pobieranym przez podłączony układ (ważna informacja).

Pomierzyłem „osiągi” swojego silniczka MT-62 (4 gramy) ze śmigiełkiem 60mm (0.2g) i otrzymałem takie wyniki

napięcie

[V]

prąd

[A]

ciąg

[g]

2 0.06 2
3 0.11 4
3.7 0.14 5.4
4 0.16 6.3
5 0.21 8.8

Oszałamiające one nie są… przy docelowym napięciu z baterii Li-Po 3.7V będę tylko „lekko” na plusie – jeśli chodzi o samą masę silniczka (masa śmigiełka na tyle mała, że pomijam).

Ważymy podzespoły

Arduino Nano (z pinami) 5.7 g
1x pakiet Li-Po 3.7V 800mA 16g
DRV8833 (trzeba 2x) 1g (według producenta)
silniczki + śmigła (trzeba 4) 4g
XM-15B 3.7g
rama? mocowania? x?

Jak widać jest kłopot przy tym silniczku – aby podnieść ~50g (zakładam ramę 10g), to dysponując balonem o sile nośnej ~20g musimy silniczkami dostarczyć brakujące 30g ciągu! MT-62 przy zasilaniu z pojedynczej Li-Po nie wystarcza. Sprawa wygląda lepiej gdy zwiększamy zasilanie (choć nie możemy robić tego do poziomu 2×3.7V, bo te silniczki działają do 6V), ale jeszcze lepiej – gdy zmienimy silniczki (za chwilę).

Rama

Można zaprojektować i wydrukować w 3D samodzielnie, o tak jakoś:

co zrobiłem wzorując się na „klasyku” QX80. Co zyskujemy drukując samemu? możemy ustawić dwa silniczki w innej płaszczyźnie, tak jak nam potrzeba. Tak czy owak fajne jest to, że waga takiej ramy to <10g (zarówno tej kupnej, jak i mojej – wydrukowanej w 3D).

Zapotrzebowanie na prąd

Silniczek MT-62 przy pracy z napięciem 3.7V pobierał 0.14A, czyli 140mA. Cztery takie silniczki będą zjadać 560mA. Dodatkowo trzeba uwzględnić prąd pracy Arduino – zmierzyłem, że to jest 10mA przy działaniu prostego programiku – ale przy bardziej skomplikowanym programie może być więcej. Uwzględnić trzeba sterownik DRV8833 (lub podobny, patrz specyfikacja) oraz moduł Bluetooth (ponownie: patrz specyfikacja). Niech dla prostoty dalszych rachunków zaokrąglę wszystko do 800mA. Bateria Li-Po o pojemności 800mAh umożliwia pobranie właśnie 800mA przez całą godzinę, czyli wychodzi na to, że mamy około godziny latania! To wygląda bardzo optymistycznie.

Można pomyśleć o mniejszej pojemności baterii, np. 250mAh – wówczas będziemy mogli latać tylko ~1/3 godziny (bo taka bateria dostarcza prąd 250mA przez godzinę, a my potrzebujemy 800mA), ale… zyskujemy na wadze! Taki akumulator waży TYLKO 6.6g ! Można też lekko zmniejszyć masę naszej konstrukcji wybierając pakiet 560mAh (waga 11g), co jest niezłym kompromisem. Trzeba to przemyśleć (albo kupić kilka pojemności takich aku). Obowiązkowo zachęcam do poczytania o bateriach Li-Poli przed ich zakupem i używaniem.

Inne silniczki i śmigła

Poniżej przedstawiam zawartość swojej „spóźnionej” paczuchy…

Czyli: kilka (tanich) śmigieł (30mm oraz 60mm), oraz mikro silniczki szczotkowe 716 (po dwa CW i CCW – na wszelki wypadek, gdybym przypadkiem je przepalił czy inaczej uszkodził – są tanie, za to przesyłka droga). Silnik prezentowany na zajęciach MT 62 okazał się słaby w porównaniu do 716-tki (wydaje się odpowiedni do sterowania, ale nie do uzyskiwania ciągu balona).

Uważaj! Pracuja z szybkoobrotowymi silniczkami z zamontowanymi śmigłami jest niebezpieczna – uważaj na palce, oczy i zadbaj o bezpieczeństwo!

716-tka – maleństwo o masie ~3g ze śmigłami (+1 g?) 60mm daje czadu! Przy okazji: śmigła miały za duży otwór (chyba 1mm) więc je zaklinowałem (nóżka od rezystora 1/4W okazała się w sam raz) aby wał mojego 716 siedział sztywno – zadbaj o to i Ty! 

napięcie

[V]

prąd

[A]

ciąg

[g]

1 0.42 15
2 0.8,0.66,1.04 28,20,39
3 1.8,1.63 64,62
3.7 2 69
     

W tabelce zwróć uwagę na DUŻY PRĄD pracy silniczka – pamiętaj, że to tylko jedna sztuka, a są (chyba) potrzebne 2. Przy takich prądach trzeba stosować odpowiedni sterownik. No i uważać na grzanie się układu (sterowniki DRV8833 mają zabezpieczenia termiczne wyłączające układ, gdy temeratura jest zbyt wysoka – trzeba mieć to na uwadze). Dodatkowo: przy napięciach 2 i 3V podałem kilka pomiarów (wartości po przecinkach) – byłem zaskoczony bardzo niestabilną pracą maleństwa 716! To może być problem…

A jak mikro silniczek szczotkowy 716 działa z malutkim śmigłem 30mm (CX-10)? Wydawało mi się, że szkoda na to czasu, a tu jednak miła niespodzianka!

napięcie

[V]

prąd

[A]

ciąg

[g]

1 0.07 2
2 0.16 6
3 0.28 11
3.7 0.42 17
     

I właśnie taki wariant wydaje się bardzo obiecujący – prąd „zjadany” przez silniczek jest akceptowalny do tego projektu, a jego ciąg też sensowny. Sądzę, że warto kupić parę innych zestawów śmigieł i jeszcze poeksperymentować (są tanie, nie ma biedy).

Wyciek helu

Balon gubi gaz… te aluminiowe gubią powoli (szczególnie w porównaniu do gumowych balonów, dla których zalecane jest kupno specjalnego sprayu). Chyba najbardziej gaz ucieka przez „zawór” – warto o to zadbać, dobrze go wiążąc.

Rozpocząłem pomiary (dopiero) 12-go stycznia 2019 (zakup 5-go stycznia) i już wyraźna część helu uciekła – patrz zdjęcie. Przyczepiłem balon do czegoś ciężkiego (u mnie kluczyk, zresztą widoczny na zdjęciu), przywiązałem nitką krawiecką do balona (bo wytrzymała i  lekka!) ale jeszcze nitki nie puściłem – mój układ (kluczyk) ważył 35.7 g. Po puszczeniu nitki balon chciał poderwać kluczyk, ale wyporność była za mała i jedynie zmniejszył wskazania wagi. Rejestrowałem te zmiany w czasie (21 g, 24 g,…,35.2 g), przed każdym pomiarem sprawdzając „kalibracje” – ważyłem kluczyk, trzymając nitkę i sprawdzałem, czy mam 35.7 g. Dopiero potem dokonywałem pomiaru – widziałem, jak kluczyk robił się „coraz cięższy”.

Poniżej wykres przedstawia zależność siły nośnej w funkcji czasu – siła („udźwig”, czyli masa początkowa 35.7g minus wskazania wagi kluczyka) maleje, bo gaz ucieka… 

Ciekawa sprawa z tym wykresem – bo jest liniowy (szkoda, że tak późno rozpocząłem swoją zabawę w ważenie kluczyka, bo może początek był nieliniowy – tego bym się spodziewał, ze względu na większe ciśnienie na początku). Może to też oznaczać, że wyciek helu odbywa się nie przez powierzchnię balona, a głównie przez „zawór” – ten pozostaje cały czas tak samo „ściśnięty” (niedoskonały).

Line Follower — własne podwozie

Byłem zmuszony odwołać dzisiejsze zajęcia (natłok pracy przed końcem roku! przepraszam).

Pan Bartek pobawił się w zaprojektowanie podwozia (blender.org) i oto jego rezultaty:

A co to ten wystający „pypeć”? To trzecie koło 😉 taki ślizgacz (z założenia pojazd ma poruszać się równej nawierzchni, stole, podłodze z namalowaną/przyklejoną czarną linią). Pan Bartek nie lubi kręcić (kołami), dlatego je zakleja lub produkuje „pypcie” 😉

Mamy lekkie kłopoty z naszą drukarką Zoltrax Z200, więc wydruk nie wyszedł jak należy… ale ciągle nadaje się do wykorzystania!

Po Świętach zobaczymy, jak to wszystko działa w praktyce! A skoro o Świętach mowa, to życzę Wszystkiego najlepszego każdemu Fi-BOTowiczowi. Do zobaczenia po przerwie (8 styczeń).

(c) K.G. 2018

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

Pojazd (autonomiczny), refleks (LEDy + przyciski) oraz line follower

Ciągle dwa/trzy niezależne projekty. Wygląda na to, że będą jednak trzy, bo Pan Bartek „atakuje” temat line followera (świetnie!)

1) zespół od pojazdu po sukcesie sterowania pojazdem przez człowieka (bluetooth) poznawał temat czujki odległości HC-SR04. Zamontowali już nawet „trzymak” w pojeździe – bez użycia wkrętaki! zuchy! 😉 Tak, tak – to „oklepany” układ, ale każdy od czegoś zaczyna – prawda? Trzymam więc kciuki za poprawne oprogramowanie go w pojeździe i zrobienie autonomicznego robota (poruszającego się bez ingerencji człowieka, w tym przypadku: wykrywającego kolizje i zmieniającego kierunek ruchu).

2) „refleks” przeszedł w stan rozbudowany i teraz składa się z 3 losowo zapalanych LEDów, i łapaniu reakcji przez 3 przyciski. Układ:

oraz program (+dużo komentarzy):

//piny z podlaczonymi przyciskami
int btn[]={2,4,6};
//piny z podlaczonymi ledami
int led[]={3,5,7};

void setup() {
  Serial.begin(9600);
  for (int i=0; i<3; i++){
    pinMode(btn[i],INPUT_PULLUP);
    pinMode(led[i], OUTPUT);
  }
}

unsigned int i;
unsigned int cr,tstart,tstop;
byte bt1, bt2, bt3, LOS;

void loop() {
  //"dysko-mode" informujace o poczatku rozgrywki
  for(i=0;i<3;i++){
        digitalWrite(led[0], HIGH);
        delay(100);
        digitalWrite(led[1], HIGH);
        digitalWrite(led[0], LOW);
        delay(100);
        digitalWrite(led[2], HIGH);
        digitalWrite(led[1], LOW);
        delay(100);
        digitalWrite(led[2], LOW);
        delay(100);
    }//"dysko-mode"
      
   //(pseudo)losowe czekanie: od 1s do 5s
   delay(random(1000,5000));

   //wybor LEDa do zaswiecenia: 1,2 lub 3
   LOS=random(1,4);
   
   //wersja tylko do testow: wypisywanie na ekran losowania
   Serial.print("LOS=");
   Serial.println(LOS);

   //wlaczenie wylosowanego LEDa     
   //dzieki zapisaniu numerow pin-ow do tablicy jest to teraz bardzo proste!
   digitalWrite(led[LOS-1], HIGH);

   //rozpoczecie mierzenia czasu
   tstart = millis();
       
   //czekanie na rekacje uzytkownika - wcisniecie jednego z trzech pyrzycsikow
   //zapamietujemy w zmiennych, ktore przyciski sa wcisniete
   do{                
       bt1 = digitalRead(btn[0]);
       bt2 = digitalRead(btn[1]);
       bt3 = digitalRead(btn[2]);                
   }while(bt1+bt2+bt3==0); //petla wykonuje sie tak dlugo, az przynajmniej jeden z przyciskow zostanie wcisniety                          

   //zatrzymanie "stopera" skoro cos zostalo wcisniete
   tstop = millis();
   digitalWrite(led[LOS-1], LOW);     

   //okreslenie poprawnosci wcisniecia przycisku:
   //zakladamy, ze zle wcisniety przycisk (tak wygodniej)
   bool ok=false;   //czy wygralem? 

   //sprawdzenie, czy moze jednak zostal wcisniety odpowieni przycisk
   switch(LOS){
      case 1: if ((bt1==1)&&(bt2==0)&&(bt3==0)) ok=true;break;
      case 2: if ((bt2==1)&&(bt1==0)&&(bt3==0)) ok=true;break;
      case 3: if ((bt3==1)&&(bt1==0)&&(bt2==0)) ok=true;break;
    }//swicth

    //jesli wygrales, to stosowny komunikat
    if (ok){    
      cr = tstop-tstart; //cr = "czas rekacji"
      Serial.print("brako! gratulacje! ");    
      Serial.print("czas reakcji:");
      Serial.println(cr);
    }//ok==true
    else{    
      Serial.println("pudło! nie ten przycisk!");    
    }     
    delay(1000);
}//loop
         
        



Można jeszcze popracować nad tym kodem – można dodać kolejny (inny) „dysko-mode” informujące, że się poprawnie wybrało przycisk, albo zaświecić wszystkimi LED-ami, gdy użytkownik „spudłował”. W przyszłości dodamy do układu wyświetlacz LCD aby tam pojawił się stosowny komunikat, a nie na ekranie podłączonego komputera.

3) na bazie TSOP5000 (kiedyś już ją poznawaliśmy) powstanie czujnik do pojazdu typu line follower – praca się właśnie rozpoczyna. Działa już (podwójny) układ czujników, więc teraz rozpoczął się drugi etap: układ 3/4/5? czujek na własnoręcznej płytce prototypowej + pojazd na sterowniku L298.

Dlaczego własnoręcznie robiona czujka, a nie jedna z „kupnych”, jak te ze zdjęć poniżej?

Odpowiedź jest prosta: bo to fajniejsze i mamy więcej zabawy podczas pracy, a o to właśnie chodzi!

 

KG, 2018

Pojazd (Bluetooth) + podstawy (3xLEDy + 3x przycisk)

Rozwija się projekt POJAZDU, a jednocześnie nauczane są podstawy Arduino (i programowania)…

  1. POJAZD
    1. Sterownik Monster vnh2p30 okazał się uszkodzony! Trudno to było wyczuć, bo z jednej strony działał poprawnie kręcąc silniczkiem w jedną stronę, natomiast w stronę przeciwną – albo wcale, albo baaaardzo powoli. Dlatego odsyłamy sterowniki do sklepu i wracamy do L298 i programujemy sterowanie przez Bluetooth. Pamiętamy o tym, aby nie jeździć na maksa bo ten sterownik przeznaczony jest tylko do pracy z prądami <1A. Na kolejnych zajęciach wrócimy do Monstera – bo mamy ich kilka sztuk a nie wszystkie są niesprawne.

  1. Sterowanie Bluetooth przez aplikację  Android RC działa! gratulacje.
  2. Podstawy Arduino/C/C++ pracujemy….

KG, 2018