W ostatnim czasie zbudowałem robota, którego zadaniem jest poruszanie się i w miarę płynne omijanie przeszkód. Głównym założeniem było wykorzystanie możliwie jak największej liczby „gotowych” podzespołów – tak, aby każdy mógł je kupić i bez zbytnio rozbudowanego zaplecza warsztatowego odtworzyć robota w swoim domu. Skorzystałem głównie ze sklepu KAMAMI.pl, bo jego oferta tego typu podzespołów jest obszerna i nie miałem problemów z wyszukaniem kompletu części.
Robot bazuje na sterowniku Pololu Orangutan SV-328 (od niego pochodzi nazwa), który posiada zintegrowany mikrokontroler ATmega328P, podwójny mostek H TB6612, wyświetlacz LCD 1×8, buzzer, diody, przyciski i zawiera MOSFETowe zabezpieczenie przed odwrotnie spolaryzowanym napięciem zasilania. Sam sterownik jest bardzo drogi, ale w zamian otrzymujemy również „zaplecze” programistyczne Pololu, w tym gotowe biblioteki i przykłady, które można zintegrować m.in. z Atmel Studio 6 i otrzymać środowisko programistyczne bardzo podobne do np. Arduino. Różnica jest taka, że sam sterownik zajmuje znacznie mniej miejsca niż np. Arduino UNO połączone Arduino Motor Shield (zestaw o mniejszych możliwościach i podobnej cenie) i posiada oprócz uC i mostków, wspomniane wcześniej dodatkowe peryferia.
Programowanie może się odbywać w dowolnym języku, a sterownik posiada 6-pinowy interfejs ISP, pod który można podpiąć dowolny programator AVR na ISP.
Podwozie to Zumo Chassis Kit, do którego dokupiłem dwa miniaturowe silniki Pololu z przekładnią 150:1. Podwozie zawiera bardzo przyczepne „gąsienice” (ciężko jest przepchnąć robota po gładkiej powierzchni stołu) oraz koszyk na 4 akumulatory/baterie AA. Zasilanie jest najbardziej niefortunną sprawą, gdyż 4 naładowane na 100% akumulatory NiMh pozwalają na osiągnięcie ok. 5,6V, a przy takim napięciu sterownik nie działa zbyt stabilnie – zwłaszcza, jeśli w trakcie pracy „katuje” silniki, serwa i inne prądożerne podzespoły. Lepszym wyjściem są baterie alkaliczne, a najlepszym – akumulator 2 cell LiPol; odpowiednio płaski spokojnie zmieści się w przegrodzie na akumulatory.
Orangutan wykorzystuje do działania dwa dalmierze optyczne typu SHARP, zamocowane na miniaturowym serwie modelarskim. Dalmierze mają zasięg 4 ÷ 30 cm, co teoretycznie w zupełności wystarcza do wypatrzenia przeszkody, zanim robot w nią uderzy. W praktyce, charakterystyka czujników przy małych odległościach jest nieliniowa (martwa strefa – parabola – w przybliżeniu liniowy spadek), a na dużych przeszkadza już szum i zakłócenia związane z pracą serwa i silników. Efekt końcowy to ograniczony zakres odległości, dla których wskazania na pewno będę prawdziwe, pozostałe odległości zakresu mogą wprowadzać w błąd i trudno jest je wykorzystać do bezpiecznej nawigacji. Zastosowałem dwa czujniki, dzięki czemu możliwe są pomiary techniką triangulacji – robot, oprócz samego zmierzenia odległości od przeszkody, może także w przybliżeniu umiejscowić ją względem siebie prostopadle do płaszczyzny dalmierzy. W praktyce wygląda to tak, że sterownik obraca serwo do momentu, gdy wskazania z obu czujników będą zbliżone. Jednocześnie kąt obrotu orczyka serwa wskazuje umiejscowienie przeszkody względem osi ruchu robota, przez co możliwe jest takie wysterowanie silników, by robot tę przeszkodę ominął lub podążał równolegle do niej. Jeśli przeszkoda znajdzie się zbyt blisko robota i nie będzie już miejsca na płynne ominięcie, robot zatrzyma się, spojrzy w lewo i prawo, porówna pomiary dla obu wychyleń czujników i obróci się w kierunku, w którym jest najwięcej miejsca. Jeśli zarówno prawa, jak i lewa strona jest zabudowana (np. gdy robot wjedzie w narożnik pod kątem 45 stopni), to robot obróci się w miejscu o 180 stopni i pojedzie w przeciwnym kierunku.
Poniżej znajduje się fragment kodu realizujący sam sposób poruszania się robota:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
//wartosc zmierzona przez lewy czujnik lewysharp = analog_read_average(2, 5); //wartosc zmierzona przez prawy czujnik prawysharp = analog_read_average(3, 5); roznica = (prawysharp*2) - (lewysharp*2); obrot = 1400 + roznica; (...) //im wieksza roznica miedzy wartosciami zmierzonymi przez lewy //i prawy czujnik, tym szybciej serwo ma sie obracac predkoscserwa = abs(roznica)/3 +20; (...) set_servo_speed_b(0, (int)(predkoscserwa+0.5)); set_servo_target_b(0,obrot); //brak przeszkody if (lewysharp < UWAGA || prawysharp < UWAGA){ kierunek = (obrot - 1400); kierunek = (int)(kierunek/4); //zadanie predkosci dla lewego i prawego silnika set_motors((kierunek+100), (-kierunek+100)); //czekanie 150ms delay_ms(150); } else { //cofniecie sie set_motors(-100, -100); delay_ms(300); set_motors(1,1); //rozgladanie sie set_servo_speed_b(0,0); //spojrzenie w lewo set_servo_target_b(0,1000); delay_ms(600); lewysharp1 = analog_read_average(2, 5); prawysharp1 = analog_read_average(3, 5); set_servo_speed_b(0,0); //spojrzenie w prawo set_servo_target_b(0,1800); delay_ms(800); lewysharp2 = analog_read_average(2, 5); prawysharp2 = analog_read_average(3, 5); //spojrzenie do przodu set_servo_speed_b(0,0); set_servo_target_b(0,1400); delay_ms(300); //okreslanie kierunku obrotu //bliska przeszkoda po lewej stronie... if (lewysharp1 > UWAGA) { //bliska przeszkoda po prawej stronie... if (prawysharp2 > UWAGA) { set_servo_speed_b(0, 0); //spojrzenie w lewo set_servo_target_b(0,1000); //obrot w lewo w miejscu (zawracanie) set_motors(100, -100); delay_ms(350); //wolne prostowanie "glowy" set_servo_speed_b(0, 300); set_servo_target_b(0,1400); delay_ms(350); set_motors(1,1); //brak lub daleka przeszkoda po prawej stronie } else { set_servo_speed_b(0, 0); //spojrzenie w prawo set_servo_target_b(0,1600); //obrot w prawo set_motors(-100, 100); delay_ms(150); //wolne prostowanie "glowy" servo_speed_b(0, 300); set_servo_target_b(0,1400); delay_ms(150); } //brak lub daleka przeszkoda po lewej stronie } else { //bliska przeszkoda po prawej stronie if (prawysharp2 > UWAGA) { set_servo_speed_b(0, 0); //spojrzenie w lewo set_servo_target_b(0,1200); //obrot w lewo set_motors(100, -100); delay_ms(150); //wolne prostowanie "glowy" set_servo_speed_b(0, 300); set_servo_target_b(0,1400); delay_ms(150); //brak lub daleka przeszkoda po prawej stronie } else { //po lewej stronie wiecej miejsca if ((lewysharp1-prawysharp2)>0) { set_servo_speed_b(0, 0); //spojrzenie w lewo set_servo_target_b(0,1200); //obrot w lewo set_motors(100, -100); delay_ms(150); //wolne prostowanie "glowy" set_servo_speed_b(0, 300); set_servo_target_b(0,1400); delay_ms(150); //po prawej stronie wiecej miejsca } else { set_servo_speed_b(0, 0); //spojrzenie w prawo set_servo_target_b(0,1600); //obrot w prawo set_motors(-100, 100); delay_ms(150); //wolne prostowanie "glowy" set_servo_speed_b(0, 300); set_servo_target_b(0,1400); delay_ms(150); } } } } |
Szczegółowy opis robota oraz jego budowy, można znaleźć na portalu Mikrokontroler.pl