Skocz do zawartości

Przeszukaj forum

Pokazywanie wyników dla tagów 'Freestyle'.

  • Szukaj wg tagów

    Wpisz tagi, oddzielając przecinkami.
  • Szukaj wg autora

Typ zawartości


Kategorie forum

  • Elektronika i programowanie
    • Elektronika
    • Arduino i ESP
    • Mikrokontrolery
    • Raspberry Pi
    • Inne komputery jednopłytkowe
    • Układy programowalne
    • Programowanie
    • Zasilanie
  • Artykuły, projekty, DIY
    • Artykuły redakcji (blog)
    • Artykuły użytkowników
    • Projekty - roboty
    • Projekty - DIY
    • Projekty - DIY (początkujący)
    • Projekty - w budowie (worklogi)
    • Wiadomości
  • Pozostałe
    • Oprogramowanie CAD
    • Druk 3D
    • Napędy
    • Mechanika
    • Zawody/Konkursy/Wydarzenia
    • Sprzedam/Kupię/Zamienię/Praca
    • Inne
  • Ogólne
    • Ogłoszenia organizacyjne
    • Dyskusje o FORBOT.pl
    • Na luzie
    • Kosz

Szukaj wyników w...

Znajdź wyniki, które zawierają...


Data utworzenia

  • Rozpocznij

    Koniec


Ostatnia aktualizacja

  • Rozpocznij

    Koniec


Filtruj po ilości...

Data dołączenia

  • Rozpocznij

    Koniec


Grupa


Znaleziono 150 wyników

  1. Mam zaszczyt przedstawić: Roko Kopłapow - czyli Robot Koncepcyjny, Konserwator Płaskich Powierzchni. Co mniej więcej ma oznaczać: mopopodobny twór samojezdny, którego zadaniem jest sprawdzenie wszelkich koncepcji przed zaprojektowaniem i skonstruowaniem prototypu robota do czyszczenia podłogi na mokro. Niestety - nie wszystko poszło po mojej myśli. Niektóre koncepcje okazały się absolutnie nietrafione, inne wymagają dopracowania i ponownego sprawdzenia. Sam robot jednak doskonale się sprawdził pokazując mi, w którą stronę mają iść dalsze prace. Jakie były założenia: robot ma służyć do przetarcia podłogi na mokro; ma być sterowany pojedynczym Arduino Pro Mini; miło by było aby realizował wszystkie funkcje, ale równie dobrze może pracować jako "inteligentne podwozie ze szczotkami", czyli jako nośnik różnej maści przystawek; Robot powinien umieć poruszać się po podłodze bez jej zmywania (czyli podnieść mechanizm szczotek choćby w celu przejechania przez próg); Robot powinien umieć znaleźć stację dokującą lub miejsce parkingowe. Co się nie sprawdziło: Podnoszone szczotki Założenie całkiem fajne, ale zbyt duża siła jest potrzebna do podniesienia całego mechanizmu. W dodatku podniesiony mechanizm zajmuje zbyt dużo miejsca. Teoretycznie dało by się to obejść ale robot musiałby być dużo większy, a tego nie chciałem. Podwozie gąsienicowe O ile na normalnym podłożu zachowywało się bez zarzutu - o tyle na mokrej, śliskiej podłodze to absolutna porażka, gąsienice po prostu nie łapią przyczepności. Podwozie Pololu (testowo z pojedynczą nieruchomą gąbką) w ogóle nie chciało skręcać, jeśli napędzana była tylko jedna gąsienica jechał sobie dalej prosto, przy napędzanych dwóch przeciwbieżnie robił jakieś dziwaczne ewolucje nie mające nic wspólnego ze skręcaniem. Podwozie Tamiya (maksymalna długość gąsienic, szerokość zwiększona do 15 cm) - niewiele lepiej. Co prawda przy podniesionych szczotkach dało się tym manewrować, ale jako że podnoszone szczotki też nie zdały egzaminu - zrezygnowałem z gąsienic i zrobiłem kołowe. Żyroskop Teoretycznie wszystko było w porządku, robot bardzo ładnie jechał prawie po prostej mimo wirujących szczotek które rzucały nim na boki, skręcał dokładnie o tyle ile chciałem... przez pierwsze 30 sekund. Tyle czasu udało mi się utrzymać mojego MPU6050 przy życiu. Niestety - po bardziej wnikliwych poszukiwaniach okazało się, że nie tylko ja mam takie problemy, niektóre egzemplarze działają bezbłędnie, inne się wieszają (zawieszając przy okazji Arduino). Tak więc na razie robot obywa się bez żyroskopu, chociaż w przyszłości na pewno się na niego zdecyduję (może taki? Poradzicie?). Stacja dokująca W związku z brakiem podnoszonych szczotek musiałem z niej zrezygnować - robot nie przejedzie przez próg, a stacja dokująca powinna być w innym pomieszczeniu. Szkoda... Na razie zamiast dokowania robot powinien wjechać w wyznaczony kąt pomieszczenia i się wyłączyć, ale do tego muszę wymyśleć jakiś inteligentny wyłącznik (to już w oddzielnym wątku). Wąska wiązka czujnika laserowego Chciałem przy "rozglądaniu się" robota ograniczyć szerokość matrycy. Niestety - tu już nie pozwoliło mi na to oprogramowanie. Mamy do dyspozycji dwie biblioteki do czujnika VL53L1X: jedną uproszczoną (która nie pozwala na ustawienie regionu) i drugą pełną (adaptacja ST, pozwala na wszystko tyle że zajmuje 20 kB, jak na małe Arduinko to nieco za wiele). Być może któregoś pięknego dnia uda mi się dopisać tę możliwość do tej mniejszej biblioteki, ale na razie niespecjalnie mam na to czas, a z szerokością wiązki 27° muszę nauczyć się żyć... Inna możliwość to postawienie drugiego procesorka tylko do obsługi lasera, mógłbym wtedy zmienić serwo na mały silnik krokowy (nawet taki mi w szufladzie leży), tylko czy jest sens? A co się sprawdziło: Przede wszystkim - napęd szczotek. Obie szczotki to po prostu kuchenne gąbki włożone w uchwyty, napędzane są pojedynczym silnikiem z dwustronnym wałem. Szczotki są obrócone wobec siebie o kąt 90°, aby zmniejszyć szerokość robota i pozbyć się martwego pola pomiędzy szczotkami, obracają się przeciwbieżnie. Gąbki można w każdej chwili wyjąć i wymienić na czyste, w dodatku mogą być włożone zarówno miękką, jak i szorstką stroną do podłogi. Druga rzecz bez której dalsza praca nie miałaby sensu to przedni zderzak. Musi być bardzo lekki, znajdować się dość nisko nad podłogą (progi) a jednocześnie mieć wysokość wystarczającą, aby robot nie usiłował wleźć pod jakąś kuchenną czy łazienkową szafkę. Jednoczęściowy zderzak zapewnia wykrycie przeszkody w promieniu 180°, jest na tyle lekki że nie mam nawet żadnych sprężyn - cztery krańcówki wystarczają. Krańcówki są połączone po dwie równolegle z każdej strony, zwierają przez rezystor do masy co umożliwia bezproblemowy odczyt zderzaków przez pojedyncze wejście analogowe Arduino. Z drobiazgów: materiał. Oprócz elementów drukowanych z PLA mam tu: płytę główną podwozia z 3mm PCW. Łatwe w obróbce, w miarę elastyczne i wystarczająco wytrzymałe. podłogę piętra ze spienionego PCW 4mm. Nie musi wytrzymywać dużych obciążeń, jest bardzo lekkie i możliwe do cięcia zwykłym ostrym nożem. koszyki na akumulatory wydrukowane z PET-G z integralną sprężyną. Początkowo obawiałem się że nie będą kontaktować - okazało się, że sprawują się dużo lepiej niż kupowane podwójne (już nie mówię o porażce pod tytułem "koszyk na jeden akumulator"), a przy okazji mam tu porządny przewód (ucięty od pecetowego zasilacza) a nie jakiś chiński włosek. Dla zainteresowanych podaję link na thingiverse. A z rozwiązań: Enkodery na kołach. No - powiedzmy częściowo, trochę za mała rozdzielczość ale działają. Do bardziej precyzyjnych rzeczy pewnie by się nie nadały, ale tu zdają egzamin. Ponieważ nie wyszedł mi napęd gąsienicowy, zacząłem szukać jakichś fajnych kółek. Kiedyś znalazłem na Allegro za jakieś śmieszne pieniądze silniczki z kątową przekładnią (odpowiedniki Pololu), dokupiłem do tego koła Pololu 42x19, zmontowałem jakieś prowizoryczne podwozie... i na tym się skończyło bo jakoś straciłem zainteresowanie projektem. Czyli silniki z kołami już miałem. W pierwszej wersji robot miał się orientować w przestrzeni za pomocą żyroskopu i laserowego czujnika odległości, ale z braku żyroskopu zostałem skazany na enkodery. Co prawda oficjalnie te silniki nie miały możliwości zamontowania enkoderów, ale na szczęście w to nie uwierzyłem Postanowiłem umieścić enkodery wewnątrz kół. Co prawda miejsca jest dość mało, musiałem wydrukować nowe felgi, ale jakoś cała konstrukcja się zmieściła. Użyłem popularnego TCRT5000L (zawsze mi kilka w szufladzie leży), dokleiłem uchwyty do silników... i ruszyło! Co prawda enkodery czasami fałszują, ale uczciwa bramka Schmitta powinna im w przyszłości pomóc. Na razie podłączenie wygląda tak: Dla zainteresowanych - zdjęcia i pliki STL/SCAD. Czujnik odległości 360° Niedawno ktoś pytał o coś takiego - o ile pamiętam chodziło o ultradźwiękowy czujnik odległości o zakresie ruchu 360°. Ponieważ moje rozwiązanie działa całkiem nieźle (należałoby tylko maksymalnie zmniejszyć luzy, ale to już zależy od możliwości drukarki) mam tu prostą przekładnię, zakładaną bezpośrednio na serwo i mocowaną tymi samymi śrubami co serwo, z tarczą pozwalającą na zamocowanie czujnika (w moim przypadku laser, ale może to być równie dobrze czujnik ultradźwiękowy). Dwie wersje: większa i kompaktowa (jeśli nie starcza miejsca). Jako osi użyłem śruby z podwozia Tamiya oryginalnie służącej do mocowania kół jezdnych z uwagi na fagment bez gwintu, ale w ostateczności można spróbować ze zwykłą śrubą M3. Tym razem nie ma STL-i jako że każdy ma inne potrzeby, zamiast tego plik dla OpenSCAD-a. Potrzebne będą biblioteki: spur_generator oraz parametric_involute_gear_v5.0. Co do programu: Program jest dość prosty, nie ma sensu publikowanie bo pewnie jutro jak mi się coś przyśni to będzie inny (możliwe że lepszy, ale niekoniecznie). Zresztą tak jak wspominałem - to jest egzemplarz koncepcyjny, wystarczy mi stwierdzenie że "da się to zrobić". Mój SMARDZ niesamowicie ułatwia programowanie, musiałem go tylko trochę inaczej zamocować (dodatkowy uchwyt i przedłużacz) bo przy gwałtowniejszych manewrach zsuwał się z pinów - prawdopodobnie bez czegoś takiego rzuciłbym to po paru godzinach. Robot ma cztery tryby pracy: Zdalne sterowanie - wiadomo o co chodzi. Zrobiłem kiedyś takiego uniwersalnego radiopilota, to już czwarte urządzenie które nim steruję; Jazda losowa - taki typowy wszędołaz, potrafi omijać przeszkody, stara się nie jechać prosto za długo aby się poszwendać w ciekawszych miejscach; Plama - analogicznie jak w Roombach, wyciera podłogę w promieniu do ok. 70 cm od miejsca startu i zatrzymuje się po skończeniu pracy; Sprzątanie - tu program nie jest dopracowany, ale w wersji koncepcyjnej nie ma to większego sensu. Ogólnie robot powinien podjechać do najbliższej ściany (to potrafi), ustawić się do niej równolegle (to też potrafi) i krążyć po pomieszczeniu coraz bliżej środka (tu ma problemy związane z niedokładnością enkoderów). Tak wygląda robot w trybie "plama": Ktoś za chwilę powie: zaraz, przecież to robot do sprzątania na mokro, a gdzie woda? Na razie nie ma. Po prostu: ponieważ nie ma stacji dokującej, przed uruchomieniem robota należałoby ręcznie uzupełnić wodę w zbiorniku. Dużo wygodniejsze jest po prostu spryskanie wodą podłogi w pomieszczeniu przed uruchomieniem robota. A czemu "na razie"? Bo jak wspomniałem na początku, ten robot powinien mieć możliwość współpracy z przystawkami, a taką przystawką może być np. urządzenie do spryskiwania płynem do mycia paneli. Ale taka przystawka powinna mieć swój procesor, robot w tym momencie powinien przestawić się w tryb "inteligentnego podwozia", a to wszystko jest kwestią przyszłości Na razie robot spełnił swoje zadanie, popracuję jeszcze trochę nad programem (dopóki się da), i zaczynam zbierać kasę na wersję finalną Obiecane pliki STL i SCAD w załączniku:koplapow.zip Jak zwykle jestem gotów odpowiedzieć na pytania - choć tym razem chyba ich nie będzie...
  2. Witam Od około półtora roku interesuję się sztuczną inteligencją, zwłaszcza zastosowaną w robotyce. Jest to dla mnie niezwykle ciekawy temat Kilka miesięcy temu zacząłem pracować nad moim prywatnym projektem, który cały czas jest w fazie rozwoju. Pewne elementy, takie jak uczenie się poprzez rozmowę z człowiekiem są gotowe, więc postanowiłem nagrać poniższe filmy: AgeBot podłączony jest do internetu. Platforma to gotowa konstrukcja - AlphaBot-PI. Użyłem jej, aby szybciej skupić się na oprogramowaniu. W ostatnim czasie domykałem widoczne na filmach możliwości robota i w międzyczasie zacząłem pracę nad wykorzystaniem kamery (aktualny stan to ~30%). W bliskiej przyszłości platformę mam zamiar wymienić na coś bardziej zaawansowanego lub zbudować swoją, ponieważ ta zaczyna mnie powoli ograniczać (pod względem hardware'u). Jej specyfikacja dostępna jest w internecie (np. na stronie firmy Waveshare), więc nie będę kopiował Jak już zauważyliście - używam Raspberry PI. Oprogramowanie na malinkę napisałem w C++, pozostała część to C++ oraz Python. Systemem jest Raspbian. Do zautomatyzowania instalacji niektórych zależności w systemie (akurat w przypadku samej malinki) używam narzędzia Ansible, a o te krytyczne dla działania aplikacji dba manager pakietów (apt, buduję paczki .deb). Do przetwarzania tekstu użyłem biblioteki Tensorflow, natomiast w procesie uczenia się, robotowi asystuje wnioskowanie. Kamera i przetwarzanie obrazu otworzy wiele nowych drzwi, ułatwi również pracę przy kolejnej domenie - planowaniu zadań. Sam robot jest stosunkowo małą częścią projektu, ale o tym... w przyszłości Każdy feedback mile widziany Pozdrawiam PS: W ciągu kilku dni napiszę posta z trochę bardziej szczegółowymi informacjami odnośnie przetwarzania tekstu - czatowania z robotem
  3. Chciałem zaprezentować mój pierwszy edukacyjny projekt o nazwie kodowej KLOSZARD, który stanowi połączenie pojazdu sterowanego i autonomicznego. Swoją nazwę zawdzięcza temu, że zbudowałem go z najtańszych części ze sklepów wysyłkowych, a jego głównym zadaniem jest pałętanie się po mieszkaniu bez większego celu Początki Pierwsza wersja powstała na gotowym podwoziu robota 2WD. Posiadała jeden moduł mikrokontrolera (Blue Pro Micro) i czujnik odległości (wpięte na breadboardzie). Była zasilana z czterech baterii AA i uwzględniała sterowanie radiowe (moduł + pilot z czterema przyciskami). Niestety wszystkie te elementy potwornie mnie zawiodły, a przede wszystkim to, że pojazd nie potrafił utrzymać prostego kierunku jazdy. Postanowiłem więc podejść do sprawy bardziej profesjonalnie Konstrukcja Obecne czterokołowe podwozie oparte jest na płytach plexi (dociętych na wymiar z portalu aukcyjnego) połączonych kołkami dystansowymi. Posiada cztery silniki z podwójnym wałem (DC 6V z przekładnią 1:48). Na każdym wale umieściłem tarcze z otworami oraz czujniki szczelinowe stanowiące enkodery optyczne. Moduły czujników FC-03 mają wlutowane kondensatory między pin D0 i GND w celu eliminowania błędnych/fałszywych impulsów (rozwiązanie znalezione w sieci). Pierwsze piętro robota wyposażyłem w pięć czujników odległości, koszyk na akumulatory i przełącznik zasilania. Cała konstrukcja zwieńczona jest gustownym zadaszeniem z plexi Zasilanie Maszynę zasilają dwa ogniwa litowo-jonowe (2S 7.4V). Część logiczna otrzymuje napięcie 5V dzięki miniaturowej przetwornicy step-up/step-down (Pololu S7V8A). Silniki sterowane są przez dwa masywne moduły z radiatorami oparte na dwukanałowym układzie L298N. Moduł jezdny Na parterze konstrukcji umieściłem Arduino Pro Micro, które nieustannie oblicza prędkość obrotową każdego wału oraz reguluje napięcia silników w celu uzyskania prędkości zadanej przez moduł główny. Dzięki temu koła mogą niezależnie poruszać się z jednakową prędkością. Komunikacja z modułem głównym odbywa się po magistrali I2C. Komenda sterująca zawiera trzy liczby: 1. prędkość lewej strony, 2. prędkość prawej strony (koło przednie i tylne otrzymują tę samą wartość w przedziale od -100 do +100 RPM) oraz 3. długość zadania mierzona w impulsach enkodera (0 = zadanie ciągłe, nieskończone). [DriveModule.ino] #include <Wire.h> #include <PID_v1.h> /**************************************************************************************************/ class MotorController { static const int8_t SMPL_COUNT = 4; // speed calculation every 4 sensor events static const double RPM_CONST = (60000000 * (SMPL_COUNT / 40.0)); // 20 holes, 40 events bool lastState; byte encPin, fwdPin, bckPin, pwmPin; int8_t signedRpm = 0; double desiredRpm = 0, pwmVal, currRpm = 0; PID *Pid; public: bool setpointReached; uint32_t counter = 0, lastMeasurement = 0, setpoint = 0; MotorController(byte encPin, byte fwdPin, byte bckPin, byte pwmPin) { pinMode(this->encPin = encPin, INPUT); pinMode(this->fwdPin = fwdPin, OUTPUT); pinMode(this->bckPin = bckPin, OUTPUT); pinMode(this->pwmPin = pwmPin, OUTPUT); lastState = !digitalRead(encPin); Pid = new PID(&currRpm, &pwmVal, &desiredRpm, 0.4, 1.5, 0, DIRECT); Pid->SetSampleTime(50); // PID calculation every 50 milliseconds setRpm(0); } int8_t speedMeasurement() { // ISR bool state = digitalRead(encPin); if (state != lastState) { lastState = state; counter++; setpointReached = (setpoint && (counter >= setpoint)); if ((counter % SMPL_COUNT) == 0) { uint32_t currTime = micros(); currRpm = RPM_CONST / (currTime - lastMeasurement); lastMeasurement = currTime; return 2; // RPM calculated } return 1; // sensor event occurred } return 0; // nothing happened } int8_t voltageAdjustment() { if (setpointReached) return 2; // task completed if (!Pid->Compute()) return 0; // nothing happened, no adjustment or PID turned off noInterrupts(); uint32_t eventDuration = micros() - lastMeasurement; if (eventDuration > 400000) currRpm = 0; // no event for 400ms, engine stopped interrupts(); analogWrite(pwmPin, pwmVal); return (eventDuration < 3000000) ? // no event for 3 seconds means danger of power overload 1 /* voltage adjustment */ : 3 /* power overload */; } void setRpm(int8_t rpm, byte initPwm = 0) { if (((rpm ^ signedRpm) < 0) || (rpm && !signedRpm)) { // if new RPM has opposite sign or ... noInterrupts(); currRpm = 0; interrupts(); pwmVal = initPwm; } signedRpm = rpm; desiredRpm = abs(rpm); digitalWrite(fwdPin, rpm > 0 ? HIGH : LOW); // setting direction depending on sign of RPM digitalWrite(bckPin, rpm < 0 ? HIGH : LOW); Pid->SetMode(rpm ? AUTOMATIC : MANUAL); // PID switch ON-OFF } }; /**************************************************************************************************/ class VehicleController { static const byte MAX_PWM = 255; static const byte MID_PWM = 100; uint32_t lastTime = 0; static int ascComp(const void *a, const void *b) { return (*(uint32_t *)a - *(uint32_t *)b); } public: bool powerOverload = false, isRunning = false; int8_t taskCompleted = 0; static const int8_t M_LEN = 4; MotorController motor[M_LEN] = { {/*ENC1*/ 12, /*FIN1*/ 4, /*FIN2*/ 2, /*FENA*/ 3}, {/*ENC2*/ 11, /*FIN3*/ 8, /*FIN4*/ 7, /*FENB*/ 9}, {/*ENC3*/ 10, /*BIN1*/ A1, /*BIN2*/ A0, /*BENA*/ 6}, {/*ENC4*/ 13, /*BIN3*/ A3, /*BIN4*/ A2, /*BENB*/ 5} }; void interruptRoutine() { // ISR for (byte i = 0; i < M_LEN; i++) motor[i].speedMeasurement(); } void loopRoutine() { for (int8_t i = 0; i < M_LEN; i++) { switch (motor[i].voltageAdjustment()) { case 0: // nothing happened break; case 1: // voltage adjustment break; case 2: // task completed taskCompleted++; noInterrupts(); motor[i].setpointReached = motor[i].setpoint = 0; interrupts(); break; case 3: // power overload powerOverload = true; setSpeed(0, 0); return; } } if (taskCompleted >= M_LEN) setSpeed(0, 0); } void setSpeed(int8_t left, int8_t right, uint32_t taskLen = 0) { uint32_t now = micros(); isRunning = left || right; noInterrupts(); if (isRunning) { powerOverload = taskCompleted = 0; for (byte i = 0; i < M_LEN; i++) { motor[i].lastMeasurement = now; motor[i].setpointReached = motor[i].counter = 0; motor[i].setpoint = taskLen; } } else { for (byte i = 0; i < M_LEN; i++) motor[i].setpointReached = motor[i].setpoint = 0; } interrupts(); motor[0].setRpm(left, MID_PWM); motor[1].setRpm(right, MID_PWM); motor[3].setRpm(left, MID_PWM); motor[2].setRpm(right, MID_PWM); } } vehicle; /**************************************************************************************************/ void setChangeInterrupt(byte pin) { pinMode(pin, INPUT); *digitalPinToPCMSK(pin) |= bit(digitalPinToPCMSKbit(pin)); PCIFR |= bit(digitalPinToPCICRbit(pin)); PCICR |= bit(digitalPinToPCICRbit(pin)); } volatile int8_t gblLeftSpeed = 0, gblRightSpeed = 0; volatile uint8_t gblTaskLen = 0; void setup() { Wire.begin(44); Wire.onReceive(i2cReceiveEvent); Wire.onRequest(i2cRequestEvent); for (byte i = 10; i <= 13; i++) setChangeInterrupt(i); // PCINT0 int8_t tmpLSpeed = 0, tmpRSpeed = 0; uint8_t tmpTaskLen = 0; while (true) { vehicle.loopRoutine(); if (gblLeftSpeed != tmpLSpeed || gblRightSpeed != tmpRSpeed || gblTaskLen != tmpTaskLen) { tmpLSpeed = gblLeftSpeed; tmpRSpeed = gblRightSpeed; tmpTaskLen = gblTaskLen; vehicle.setSpeed(tmpLSpeed, tmpRSpeed, tmpTaskLen); } } } void i2cReceiveEvent(int howMany) { if (howMany >= 3) { gblLeftSpeed = Wire.read(); gblRightSpeed = Wire.read(); gblTaskLen = Wire.read(); howMany =- 3; } while (howMany--) Wire.read(); } void i2cRequestEvent() { // task ended Wire.write(gblTaskLen ? vehicle.taskCompleted >= vehicle.M_LEN : 1); } ISR(PCINT0_vect) { vehicle.interruptRoutine(); } Moduł zbliżeniowy Pięcioma czujnikami odległości (4x HC-SR04 i 1x US-015) steruje samotna Atmega328P na płytce uniwersalnej (wgrany bootloader wykorzystuje wewnętrzny oscylator 8MHz). Każde żądanie danych (wysłane po I2C) do tego modułu jest sygnałem do wykonania kolejnego pomiaru ze wszystkich czujników (po kolei). [ProximityModule.ino] #include <Wire.h> #include <NewPing.h> NewPing sensor[] = { NewPing(12, 12, 200), NewPing(5, 5, 200), NewPing(13, 13, 200), NewPing(4, 4, 200), NewPing(A3, A3, 200) }; const byte ORDER[] = {0, 2, 4, 1, 3}; const byte CNT = sizeof(ORDER); volatile byte distance[CNT] = {}; volatile bool takeNext = false; void setup() { Wire.begin(40); Wire.onRequest(requestEvent); while (true) { if (!takeNext) continue; takeNext = false; for (byte i = 0; i < CNT; i++) { distance[ORDER[i]] = sensor[ORDER[i]].ping_cm(); delay(8); } } } void requestEvent() { for (byte i = 0; i < CNT; i++) Wire.write(distance[i]); takeNext = true; } Sterowanie Pojazd może być sterowany za pomocą smartfonu z Androidem. Aplikacja napisana w Java’ie (Android Studio) przełącza się na WiFi rozgłaszane przez robota i przesyła drogą sieciową (na ustalony adres IP i port) krótkie instrukcje sterujące. Przy tej okazji po raz pierwszy w życiu doceniłem prostotę protokołu UDP – każda komenda znajduje się w osobnym pakiecie danych Nie zamieszczam źródeł aplikacji ponieważ zawierają one mnóstwo nadmiarowego kodu związanego bardziej z interfejsem użytkownika. Moduł główny Przypadkowo wpadłem w posiadanie modułu WiFi NodeMCU v3 z rodziny układów ESP8266. Podczas pierwszych testów w Arduino IDE zauważyłem, że najprostsze szkice wygrywają się do niego potwornie długo. Do tej pory nie znalazłem rozwiązania tej niedogodności, ale w trakcie poszukiwań natrafiłem na firmware obsługujący prosty system plików oraz interpreter języka skryptowego Lua. http://nodemcu.readthedocs.io Koncepcja asynchronicznych callbacków wywoływanych przez zdarzenia i timery (znana mi z JavaScriptu) wydała się genialnym rozwiązaniem symulującym pracę wielozadaniową w środowisku jednowątkowym, dlatego postanowiłem przyjrzeć się temu środowisku. NodeMCU pracuje w standardzie 3.3V. Konwerter poziomów logicznych został ukryty na płytce uniwersalnej bezpośrednio pod modułem Pojazd posiada możliwość autonomicznej jazdy aktywowanej z poziomu aplikacji lub wbudowanego w moduł przycisku FLASH. W tym trybie robot ma za zadanie objechać całe mieszkanie tak, aby po jego prawej stronie w odległości do 40 cm zawsze znajdowała się jakaś przeszkoda. W przypadku braku przeszkody robot obraca się w prawo mniej-więcej o 90 stopni i jedzie prosto. Przeszkoda od strony frontowej uruchamia poszukiwanie otwartej przestrzeni przy jednoczesnym obracaniu w lewo. [main.lua] print("Starting Kloszard") local klrd = require "kloszard" local snsr = require "sensor" local autoDrive = { timer = tmr.create() } function autoDrive:onSetup() self.timer:register(100, tmr.ALARM_AUTO, function() local s = klrd.prxRead() for i = 1, 5 do snsr[i]:add(s:byte(i)) end local act = self:action() if act then self.action = act if act == self.main then -- drive forward klrd.drvSend(45, 45) end end end) self:onStop() end function autoDrive:onStart() self.action = function() return self.main end klrd.prxRead() self.timer:start() snsr.sideCheckLock = true end function autoDrive:onStop() self.timer:stop() klrd.drvSend(0, 0) end function autoDrive:onFinish() self:onStop() self.timer:unregister() end function autoDrive:main() -- obstacle ahead if math.min(snsr[2].val, snsr[3].val, snsr[4].val) < 20 then klrd.drvSend(-45, 45) return function() -- finding escape if snsr[2]:gt(20) and snsr[3]:gt(20, 3) and snsr[4].val > 20 then snsr.sideCheckLock = true return self.main end end end -- open space on right side if snsr:rightSideOpen(40) then klrd.drvSend(45, -45, 30) -- turn right return function() if klrd.drvTaskEnded() then return self.main end end end -- right side collision risk if snsr[5]:lt(10) and snsr[5]:get(5) > snsr[5].val then klrd.drvSend(0, 45) -- course correction return function() if snsr[5]:get(5) < snsr[5].val then return self.main end end end end klrd.setup(autoDrive) [sensor.lua] local Queue = { 100, 100, 100, 100, 100, pos = 1, LEN = 5, val = 100 } Queue.__index = Queue Queue.new = function() return setmetatable({}, Queue) end function Queue:add(value) if value == 0 then value = self.val end self[self.pos], self.val = value, value self.pos = self.pos + 1 if self.pos > self.LEN then self.pos = 1 end end function Queue:get(index) if index > self.LEN then index = self.LEN elseif index < 1 then index = 1 end local shift = self.pos - index if shift < 1 then shift = shift + self.LEN end return self[shift] end function Queue:gt(value, len) -- values greater than if not len then len = self.LEN end for i = 1, len do if self:get(i) <= value then return false end end return true end function Queue:lt(value, len) -- values less than if not len then len = self.LEN end for i = 1, len do if self:get(i) >= value then return false end end return true end local M = { sideCheckLock = true } for i = 1, 5 do M[i] = Queue:new() end function M:rightSideOpen(distance) local cnt = 0 for i = 1, self[5].LEN do if self[5][i] > distance then cnt = cnt + 1 end end if self.sideCheckLock then self.sideCheckLock = cnt > 0 return false else self.sideCheckLock = cnt == self[5].LEN return self.sideCheckLock end end return M [kloszard.lua] local M = {} local AUTOBTN_PIN = 3 local CMD_PING = 121 local DRVM_ADDR = 44 -- DriveModule Address local CMD_DRIVE = 122 local PRXM_ADDR = 40 -- ProximityModule Address local CMD_AUTO = 123 local UDP_PORT = 50000 function M.setup(autoDrive) M.autoDrive = autoDrive wifi.setmode(wifi.SOFTAP) wifi.ap.setip({ ip = "192.168.1.1", netmask = "255.255.255.0", gateway = "192.168.1.1" }) wifi.ap.config({ ssid = "KloszardWiFi", pwd = "abcd1234" }) i2c.setup(0, 5, 6, i2c.SLOW) -- SDA pin 5, SCL pin 6 autoDrive:onSetup() M.udpSock = net.createUDPSocket() M.udpSock:listen(UDP_PORT) M.udpSock:on("receive", function(sock, data, port, ip) if data:byte(1) == CMD_PING then sock:send(port, ip, "PONG") elseif data:byte(1) == CMD_DRIVE then M.drvSend(data:byte(2), data:byte(3), data:byte(4)) elseif data:byte(1) == CMD_AUTO then if data:byte(2) ~= 0 then autoDrive:onStart() else autoDrive:onStop() end end end) gpio.trig(AUTOBTN_PIN, "up", function() if autoDrive.timer:state() then autoDrive:onStop() else autoDrive:onStart() end end) M.drvSend(0, 0) end function M.finish() M.autoDrive:onFinish() M.udpSock:close() wifi.setmode(wifi.NULLMODE) end local lastDrvmLeft, lastDrvmRight, lastTaskLen = 0, 0, 0 function M.drvSend(left, right, taskLen) if not taskLen then taskLen = 0 end if lastDrvmLeft == left and lastDrvmRight == right and lastTaskLen == taskLen -- ignore function call if nothing changed then return end lastDrvmLeft, lastDrvmRight, lastTaskLen = left, right, taskLen left = bit.band(left, 0xFF) right = bit.band(right, 0xFF) i2c.start(0) i2c.address(0, DRVM_ADDR, i2c.TRANSMITTER) i2c.write(0, left, right, taskLen) i2c.stop(0) end function M.drvTaskEnded() i2c.start(0) i2c.address(0, DRVM_ADDR, i2c.RECEIVER) local res = i2c.read(0, 1) i2c.stop(0) return res:byte(1) ~= 0 end function M.prxRead() i2c.start(0) i2c.address(0, PRXM_ADDR, i2c.RECEIVER) local res = i2c.read(0, 5) i2c.stop(0) return res end return M WebEspDitor Konieczność ciągłego podłączania kabla USB w celu modyfikacji najdrobniejszych parametrów kodu zmotywowała mnie do napisania dodatkowego skryptu o nazwie WebEspDitor. Jest to prosty serwer/serwis HTTP, który umożliwia edycję plików znajdujących się w pamięci nodeMCU z poziomu przeglądarki internetowej. Skrypt wyświetla również wyniki funkcji print i ostatni błąd aplikacji, w związku z czym powinien być uruchamiany na wstępie jako init.lua (kod aplikacji należy przenieść do pliku main.lua). [init.lua] gpio.mode(3, gpio.INPUT, gpio.PULLUP) -- FLASH BUTTON if gpio.read(3) == 0 then return end -- boot loop protection print("Starting WebEspDitor...") net.createServer():listen(80, function(socket) local htmlTmpl = { -- 1 -- [===[HTTP/1.0 200 OK Content-Type: text/html; charset=UTF-8 Connection: close <!DOCTYPE html><html><head><title>WebEspDitor</title><meta name="viewport" content="width=device-width"><link rel="icon" href="data:;base64,="><style> a{text-decoration:none;color:navy}a:hover{text-decoration:underline}body{ line-height:1.5em;font-family:monospace}</style></head><body> ]===], -- 2 -- [===[<p><a href="/">WebEspDitor</a> [ <a href="/reset">reset</a> ]</p>]===], -- 3 -- [===[<form action="/save/<!FNAME>" method="post" enctype="text/plain"> <textarea name="s" spellcheck="false" style="position:absolute;width:100%; height:100%;margin:0;border:0;padding:4px;resize:none;white-space:pre; box-sizing:border-box">]===], -- 4 -- [===[</textarea><input style="position:absolute;bottom:0;right:0" type="submit" value="Save"></form><style>body{margin:0;overflow:hidden}</style>]===], -- 5 -- [===[<input id="name" type="text"><input type="button" value="create" onclick="location.href='/edit/'+document.getElementById('name').value">]===], -- 6 -- [===[<script>setTimeout(function(){location.href='/'},2000)</script>]===], -- 7 -- [===[<li><a href="/edit/<!FNAME>"><!FNAME></a> (<!FSIZE>, <a href="/delete/<!FNAME>" onclick="return confirm('Are you sure to delete'+ ' file \'<!FNAME>\'?')">del</a>)</li>]===] } local rqst, rspn = { length = 0, totalLen = 0, fname = nil }, { } local function getLine(str, bgnPos) local line, endPos = "", str:find("\r\n", bgnPos, true) if endPos then line = str:sub(bgnPos, endPos - 1) bgnPos = endPos + 2 end return line, bgnPos end local function sendRspn(sck) -- send response table to client if #rspn > 0 then sck:send(table.remove(rspn, 1), sendRspn) else sck:close() end end local function saveRqst(sck, data) -- save incoming content to file if #data > 0 then rqst[#rqst+1] = data end rqst.length = rqst.length + #data if rqst.length >= rqst.totalLen then -- last network frame data = nil rqst[1] = rqst[1]:sub(3) -- enctype text/plain: rqst[#rqst] = rqst[#rqst]:sub(1, -3) -- s=[data]CRLF local fd = file.open(rqst.fname, "w+") if fd then while #rqst > 0 do fd:write(table.remove(rqst, 1)) end fd:close() end collectgarbage("collect") sendRspn(sck) end end socket:on("receive", function(sck, data) rspn[1] = htmlTmpl[1] local line, dataPos = getLine(data, 1) -- 1 2 3 4 5 local parts = {} -- GET /cmd/prm HTTP/1.1 for elm in line:gmatch("[^/ ]+") do parts[#parts+1] = elm end if #parts == 5 then parts[3] = parts[3]:gsub("[^a-zA-Z0-9_.-]", "") end if parts[1] == "POST" then repeat line, dataPos = getLine(data, dataPos) local len = line:match("Content%-Length: (%d+)") if len then rqst.totalLen = tonumber(len) end until #line == 0 end -- EDIT if parts[2] == "edit" and #parts == 5 then local tmp = htmlTmpl[3]:gsub("<!FNAME>", parts[3], 1) rspn[#rspn+1] = tmp tmp = #rspn + 1 local fd = file.open(parts[3], "r") if fd then while true do local chunk = fd:read(512) if chunk then rspn[#rspn+1] = chunk else break end end fd:close() end for i = tmp, #rspn do rspn[i] = rspn[i]:gsub("&", "&amp;"):gsub("<", "&lt;") end rspn[#rspn+1] = htmlTmpl[4] else rspn[#rspn+1] = htmlTmpl[2] -- SAVE if parts[2] == "save" and #parts == 5 then rqst.fname = parts[3] rspn[#rspn+1] = '<p>File "'..parts[3]..'" has been saved.</p>'..htmlTmpl[6] -- DELETE elseif parts[2] == "delete" and #parts == 5 then file.remove(parts[3]) rspn[#rspn+1] = '<p>File "'..parts[3]..'" has been deleted.</p>'..htmlTmpl[6] -- RESET elseif parts[2] == "reset" then local timer = tmr.create() timer:register(500, tmr.ALARM_SINGLE, function() node.restart() end) timer:start() rspn[#rspn+1] = '<p>Waiting for device...</p>'..htmlTmpl[6] -- INDEX else local res = {'<ul>'} for n, s in pairs(file.list()) do if not n:find("init.", 1, true) then res[#res+1] = htmlTmpl[7]:gsub("<!FNAME>", n):gsub("<!FSIZE>", s) end end res[#res+1] = '</ul>' rspn[#rspn+1] = table.concat(res)..htmlTmpl[5] local res = file.getcontents("init.out") if res then rspn[#rspn+1] = '<p>Lua interpreter output:<pre>'..res..'</pre></p>' end end end rspn[#rspn+1] = '</body></html>' socket, htmlTmpl, line, parts = nil collectgarbage("collect") -- FILE UPLOAD if rqst.fname and rqst.totalLen > 0 then sck:on("receive", saveRqst) saveRqst(sck, data:sub(dataPos)) -- NORMAL REQUEST else sendRspn(sck) end end) end) print("Starting main...") do local res, err file.remove("init.out") node.output(function(s) local fd = file.open("init.out", "a+") if fd then fd:write(s) fd:close() end end, 1) if file.exists("main.lc") then res, err = pcall(function() dofile("main.lc") end) else res, err = pcall(function() dofile("main.lua") end) end if not res then print(err) end end Dalszy rozwój Najsłabszymi elementami obecnej wersji KLOSZARDa są plastikowe przekładnie i wały silniczków, które coraz bardziej uginają się pod ciężarem konstrukcji (1,06 kg). Na horyzoncie rozbudowy pojawiła się jednak nadzieja w postaci identycznych silników z przekładnią metalową. Mam nadzieję, że uda się je bezkarnie podmienić Drugie miejsce niedoskonałości zajmuje sam w sobie NodeMCU, który cierpi z powodu niedostatku pamięci RAM oraz tendencji do resetowania w przypadku wszelkich problemów zarówno softwarowych jak i hardwarowych. W tym przypadku rozwiązaniem wydaje się przejście na Raspberry Pi Zero (WiFi+BT), czego dodatkowym atutem byłoby całkowite uwolnienie projektowania i debugowania od kabla USB Dalsze prace będą ukierunkowane na rozwój oprogramowania (bardziej zaawansowane przetwarzanie danych z modułu zbliżeniowego i jezdnego), a może nawet uda się zaimplementować jakąś skromną formę lokalizowania w przestrzeni.
  4. Cześć. Chciałem wam pokazać projekt nad którym pracuję: PENETRATOR Trochę konkretów: aluminiowy kadłub napęd: dwa silniki elektryczne szczotkowe 9V zasilanie 2 x 18650 + 2 x 18650 ukryte pod kadłubem kontroler: Raspberry Pi 3 A+ wizja: moduł kamery 1080p emitery IR umożliwiające kamerze pracę w ciemnościach łączność: WiFi (access point i serwer HTTP) język programowania : Python 3 sterowanie i obraz: autorska aplikacja webowa (w przeglądarce) umożliwiająca wyświetlanie obrazu z kamery i sterowanie robotem W PLANACH: mechaniczne ramię 7DOF oparte na serwomechanizmach MG996R obracanie kamery w 2 płaszczyznach przy pomocy serwomechanizmów mam też ochotę pobawić się sztuczną inteligencją, stąd wybór raspberry a nie np. esp8266 Przy okazji, podziękowania dla Forbot.pl za podstawy które wprowadziły mnie do świata robotyki
  5. Witam, przedstawiam autonomicznego robota balansującego. Robot balansujący na Atmega 1284P 20MHz. Obsługuje komunikację oraz zmianę wsadu za pomocą bluetooth. Ponadto istnieje możliwość sterowania robotem za pomocą pilota IR, oraz zmiany nastawów regulatorów. Posiada system autonomicznej jazdy z wykorzystaniem 3 sensorów ultradźwiękowych. Delikatne ruchy robota w stanie spoczynku spowodowane są dużymi luzami w przekładniach silników. Robot radzi sobie bez problemów ze średniej wielkości nachyleniami podłoża. Sterowanie odbywa się poprzez aplikację na system android, która to wyświetla także podstawowe informacje o robocie (napięcie baterii, wielkość całki w regulatorze pochylenia itp). Tryb autonomicznej jazdy opiera się o trzy ultradźwiękowe czujniki odległości. W oparciu o ich wskazania, robot samoistnie podejmuje decyzje co do dalszej drogi. Jest to ostateczna wersja robota która posiada także prócz trybu autonomicznego, tryb zdalnego sterowania na odległość do 100 metrów. Zaimplementowany moduł auto diagnozy potrafi wykryć 32 ostrzeżenia i błędy, np od niskiego napięcia 12v 5v, po jego niestabilność, uślizg kół, luzy na piastach, opory toczenia i przekładni... itp.... itd... Uruchomienie poszczególnych funkcji robota odbywa się poprzez komendy terminala uart, lub wygodniej pilot ir. Wszystkie parametry robota wyświetlane są na 5 pulpitach 4 wierszowego wyświetlacza lub uproszczone w dedykowanej aplikacji na system android. __________ Komentarz dodany przez: Treker Witam na forum, następnym razem proszę pamiętać o zdjęciu w formie załącznika, które widoczne będzie później w katalogu robotów oraz na stronie głównej. W tym przypadku już poprawiłem
  6. witam (do admina) na wstępie chciałem zaznaczyć, że jedyne co mam z gotowego projektu z neta to modele do wydrukowania (po co tworzyć coś na nowo jak już jest) jeżeli projekt nie zalicza się do akcji rabatowej, mogę usunąć linki i opublikować "po prostu". Był taki pomysł, żeby zbudować robota. Linefollower? jak już to na poważnie, a mi sie nie chce. Jakiś manipulator? nie mam napędów. Autko? to też chcę na poważnie zrobić, coś więcej niż lego. Wybór padł na robota, który miał po prostu zająć czymś kolegów i nauczycieli w szkole - czyli takie cudo na pokazy. Miało to być małe, do plecaka, znalazłem projekt otto diy. Spodobał mi się, więc wydrukowałem, złożyłem na płytce stykowej i ledwie zamknąłem obudowę przez kable 20cm - ale robot działał, wydawał zabawne odgłosy i tańczył. Zadanie swoje spełnił, mogłem go teraz zostawić i się skupić na zvsie (którego opublikuję po dokończeniu obudowy). Ale co to za skończony projekt na płytce stykowej? Nie mogłem tego tak zostawić, to dla mnie wręcz porażka. (Aby zapobiec spamowi, drukowałem na Aflawise U30 - dysza 0.4, chłodzenie petsfang, 60mm/s warstwa 0.3mm. PLA od printme, 200*C/60*C. Ta żółta czapka się źle wydrukowała, to troszkę uzupełniłem ubytki lutownicą i filamentem, co jak widać średnio wyszło.) Myślałem, czego mi w nim brakuje. Oczywiście sztuczki robota robione w kółko szybko się znudziły, to musiałem wymyślić jakieś sterowanie. Mam dużo odbiorników IR, to sprzężyłem taki jeden z robotem, spisałem kody z pilota do ledów i zrobiłem mapowanie przycisków. Następnie wziąłem się za lutowanie płytki pod niego. Kilka godzin lutowania, plątanina kabli z drugiej strony, ale działa. (mistrz painta w akcji): Płytka w procesie projektowania, a bardziej patrzenia czy wszystko jest ok Tutaj podczas wgrywania programu, stabilizator ze starego monitora okazał się być zużyty Gotowa płytka: Na płytce za zasilanie odpowiada stabilizator 7805 (oczywiście z radiatorem i kapką pasty termoprzewodzącej), z kondensatorem 100uF. W środku stara dobra atmega 328, z kwarcem 16Mhz i brzęczykiem z diodą led. Najwięcej jest wyprowadzeń, bo cała listwa 4x3 dla serw, złącze od HC-SR04, VS1838B, interfejsu UART a nawet złącze programatora. Cała płytka ma 60x30mm. Źródło napięcia to 2 akusy lipo na 7.4V (tylko takie miałem) o pojemności 1800mAh. Wyjęte z tableta - tam dużo fajnych rzeczy można znaleźć... Początkowo miałem dać 2 akusy 18650, ale one były za duże pod moją płytkę. Jedno 18650 ze step upem to samo. Więc zostały liposy, które niestety nie są zbyt ciężkie i robot ma dość nisko środek ciężkości (powinien być "na środku", pierwotnie tam miały siedzieć 4 paluszki aa) Zostało napisać kod. Skorzystałem z gotowych bibliotek pod otto, gdzie miałem gotowe sterowanie serwami i czujnikiem. Był za to ogromny kłopot z bibliotekami, bo te gryzły się z timerami których używała biblioteka od otto. Na chińskim githubie znalazłem jakimś cudem bibliotekę od IR która nie używa timerów, biblioteka od dźwięków była łatwiejsza do znalezienia ale musiałem wykonywać modyfikacje w niej, tak czy siak. Sam kod nie jest skomplikowany, po funkcjach startujących peryferia co chwilę sprawdzamy czy odebraliśmy jakiś kod z pilota - jeżeli tak, wykonujemy funkcję z nim związaną. W przeciwnym wypadku wykonujemy kod danego trybu, a jest ich 3: (wiem, na zdjęciu jest ich więcej ale uznałem że są niepotrzebne dla mnie): tryb swobodny, czyli nie robimy nic, tryb omijania przeszkód, tutaj za dużo tłumaczyć nie trzeba, tryb śledzenia: po wykryciu dłoni, robot idzie w jej kierunku. A oto cały kodzik, może kiedyś go dam na githuba bo szkoda by się u mnie marnował: //---------------------------------------------------------------- //-- Otto IR mod //-- Does many things, controlled by an IR remote. //-- (insert github here) //-- CC BY SA (http://ottodiy.com) //-- 02 June 2019 by Leoneq ;3 //----------------------------------------------------------------- // INCLUDES --------------------------- #include <Servo.h> //servo library #include <Oscillator.h> //important library #include <US.h> //ultrasonic library #include <Otto.h> //otto library #include <motoIRremote.h> //timer-free ir library #include <TimerFreeTone.h> //timer-free tone library // PINS ------------------------------- #define PIN_LEFT_HIP 2 #define PIN_RIGHT_HIP 3 #define PIN_LEFT_ANKLE 5 #define PIN_RIGHT_ANKLE 4 #define PIN_IR 6 #define PIN_LED 13 // TRIMS ------------------------------ #define TRIM_LEFT_HIP -9 #define TRIM_RIGHT_HIP -9 #define TRIM_LEFT_ANKLE 0 #define TRIM_RIGHT_ANKLE -20 // OBJECTS ---------------------------- Otto Otto; //This is Otto! IRrecv ir(PIN_IR); decode_results IR_RES; // VARIABLES -------------------------- int WALK_TIME = 1000; int WALK_DIR = 1; int WALK_HEIGHT = 20; int speed = 3; int US_DIST = 0; bool obstacleDetected = false; String IR_CODE = ""; String IR_SYMBOL = ""; #define UP "f700ff" #define DOWN "f7807f" #define OFF "f740bf" #define ON "f7c03f" #define ONE "f720df" #define TWO "f7a05f" #define THREE "f7609f" #define FOUR "f710ef" #define FIVE "f7906f" #define SIX "f750af" #define SEVEN "f730cf" #define EIGHT "f7b04f" #define NINE "f7708f" #define TEN "f708f7" #define ELEVEN "f78877" #define TWELVE "f748b7" #define THIRTEEN "f728d7" #define FOURTEEN "f7a857" #define FIFTEEN "f76897" #define W "f7e01f" #define FLASH "f7d02f" #define STROBE "f7f00f" #define FADE "f7c837" #define SMOOTH "f7e817" /* * There are 5 modes attached to: * W - avoid obstacles (1) * FADE - follow hand! (2) * SMOOTH - free mode. Do anything what you want! (3) */ byte OTTO_MODE = 5; void setup() { Serial.begin(115200); //enable serial for debugging ir.enableIRIn(); //enable ir sensor Otto.init(PIN_LEFT_HIP,PIN_RIGHT_HIP,PIN_LEFT_ANKLE,PIN_RIGHT_ANKLE,true); //init otto! Otto.setTrims(TRIM_LEFT_HIP,TRIM_RIGHT_HIP, TRIM_LEFT_ANKLE, TRIM_RIGHT_ANKLE); //set trims //why not in one line? here, in code it looks better. Serial.println("Otto IR Mod by Leoneq :3"); Serial.println("Summer 2019"); Serial.println("-----------------------------------"); Serial.println("Ready."); Otto.sing(S_connection); //let us know, that otto woke up! Otto.home(); //set initial position delay(100); Otto.sing(S_happy); //when you are happy, otto should be too ;) //end of setup function } void checkIR() { if (ir.decode(&IR_RES)) //if we get something { IR_CODE = String(IR_RES.value, HEX); //we save received data into string Serial.print("Received data: 0x"); Serial.println(IR_CODE); //lets write it on serial checkIRcode(); //we must know what button is pressed delay(250); //little delay ir.resume(); } } void loop() { checkIR(); //check for new ir signals switch(OTTO_MODE) { case 1: //avoid obstacles avoid(); break; case 2: //follow hand mode follow(); break; case 3: //free mode free(); break; } } void free() { //nothing! } void follow() { obstacleDetected = obstacleDetector(); if(obstacleDetected) { Otto.walk(1,1000,FORWARD); } else { Otto.home(); } } void dance() { //soon } void sing() { //soon } void avoid() { obstacleDetected = obstacleDetector(); if(obstacleDetected) { Otto.sing(S_surprise); Otto.walk(5,1000,BACKWARD); delay(1000); Otto.sing(S_happy); Otto.turn(3,1000,RIGHT); delay(2000); } else { Otto.walk(1,1000,FORWARD); } //end of avoid() function } bool obstacleDetector() { int distance = Otto.getDistance(); Serial.print("Distance: "); Serial.println(distance); if(distance<15) { return true; } else { return false; } } void up() { IR_SYMBOL = "UP"; Serial.print("The symbol is: "); //lets write symbol on serial Serial.println(IR_SYMBOL); speed--; Serial.print("Changing speed! The new speed is: "); Serial.print(speed); } void down() { IR_SYMBOL = "DOWN"; Serial.print("The symbol is: "); //lets write symbol on serial Serial.println(IR_SYMBOL); speed++; Serial.print("Changing speed! The new speed is: "); Serial.print(speed); } void off() { IR_SYMBOL = "OFF"; Serial.print("The symbol is: "); //lets write symbol on serial Serial.println(IR_SYMBOL); Serial.print("See you later!"); Otto.home(); Otto.playGesture(OttoSleeping); Otto.sing(S_sleeping); } void on() { IR_SYMBOL = "ON"; Serial.print("The symbol is: "); //lets write symbol on serial Serial.println(IR_SYMBOL); Serial.println("Turning on..."); Otto.sing(S_happy); Otto.playGesture(OttoHappy); Otto.home(); } void _one() { IR_SYMBOL = "1"; Serial.print("The symbol is: "); //lets write symbol on serial Serial.println(IR_SYMBOL); Serial.println("Moonwalking!"); Otto.sing(S_superHappy); Otto.moonwalker(3,speed*300,30,1); Otto.home(); } void _two() { IR_SYMBOL = "2"; Serial.print("The symbol is: "); //lets write symbol on serial Serial.println(IR_SYMBOL); Serial.println("jump!"); Otto.jump(3, speed*300); } void _three() { IR_SYMBOL = "3"; Serial.print("The symbol is: "); //lets write symbol on serial Serial.println(IR_SYMBOL); Serial.println("Am I taller?"); Otto.updown(1, speed*300, 22); } void _four() { IR_SYMBOL = "4"; Serial.print("The symbol is: "); //lets write symbol on serial Serial.println(IR_SYMBOL); Serial.println("I'm happy, because you are with me!"); Otto.sing(S_happy); Otto.playGesture(OttoLove); } void _five() { IR_SYMBOL = "5"; Serial.print("The symbol is: "); //lets write symbol on serial Serial.println(IR_SYMBOL); Serial.println("I'm so sad, help me :("); Otto.sing(S_confused); Otto.playGesture(OttoSad); Otto.sing(S_sad); } void _six() { IR_SYMBOL = "6"; Serial.print("The symbol is: "); //lets write symbol on serial Serial.println(IR_SYMBOL); Serial.println("mmmphm!"); Otto.sing(S_fart1); Otto.playGesture(OttoFart); Otto.sing(S_fart3); Otto.playGesture(OttoWave); } void _eight() { IR_SYMBOL = "8"; Serial.print("The symbol is: "); //lets write symbol on serial Serial.println(IR_SYMBOL); Serial.println("Going forward!"); Otto.walk(3, speed*300, 1); Otto.home(); } void _fourteen() { IR_SYMBOL = "14"; Serial.print("The symbol is: "); //lets write symbol on serial Serial.println(IR_SYMBOL); Serial.println("Going backwards!"); Otto.walk(3, speed*300, -1); Otto.home(); } void _ten() { IR_SYMBOL = "10"; Serial.print("The symbol is: "); //lets write symbol on serial Serial.println(IR_SYMBOL); Serial.println("Rotating left, please wait..."); Otto.turn(3, speed*300, 1); Otto.home(); } void _twelve() { IR_SYMBOL = "12"; Serial.print("The symbol is: "); //lets write symbol on serial Serial.println(IR_SYMBOL); Serial.println("Rotating right, please wait..."); Otto.turn(3, speed*300, -1); Otto.home(); } void _eleven() { IR_SYMBOL = "11"; Serial.print("The symbol is: "); //lets write symbol on serial Serial.println(IR_SYMBOL); Serial.println("I am standing up!"); Otto.home(); } void _w() { IR_SYMBOL = "W"; Serial.print("The symbol is: "); //lets write symbol on serial Serial.println(IR_SYMBOL); Serial.println("Obstacle avoid mode: ACTIVATED"); OTTO_MODE = 1; } void _flash() { IR_SYMBOL = "FLASH"; Serial.print("The symbol is: "); //lets write symbol on serial Serial.println(IR_SYMBOL); Serial.println("Follow mode: ACTIVATED"); OTTO_MODE = 2; } void _strobe() { IR_SYMBOL = "STROBE"; Serial.print("The symbol is: "); //lets write symbol on serial Serial.println(IR_SYMBOL); Serial.println("Free mode: ACTIVATED"); OTTO_MODE = 3; } void _seven() { IR_SYMBOL = "7"; Serial.print("The symbol is: "); //lets write symbol on serial Serial.println(IR_SYMBOL); Serial.println("Standing one foot!"); Otto.bend(1, 2000, 1); } void _nine() { IR_SYMBOL = "7"; Serial.print("The symbol is: "); //lets write symbol on serial Serial.println(IR_SYMBOL); Serial.println("Standing one foot!"); Otto.bend(1, 2000, -1); } void checkIRcode() { if(IR_CODE == UP) up(); else if(IR_CODE == DOWN) down(); else if(IR_CODE == OFF) off(); else if(IR_CODE == ON) on(); else if(IR_CODE == ONE) _one(); else if(IR_CODE == TWO) _two(); else if(IR_CODE == THREE) _three(); else if(IR_CODE == FOUR) _four(); else if(IR_CODE == FIVE) _five(); else if(IR_CODE == SIX) _six(); else if(IR_CODE == SEVEN) _seven(); else if(IR_CODE == EIGHT) _eight(); else if(IR_CODE == NINE) _nine(); else if(IR_CODE == TEN) _ten(); else if(IR_CODE == ELEVEN) _eleven(); else if(IR_CODE == TWELVE) _twelve(); else if(IR_CODE == THIRTEEN) IR_SYMBOL = "13"; else if(IR_CODE == FOURTEEN) _fourteen(); else if(IR_CODE == FIFTEEN) IR_SYMBOL = "15"; else if(IR_CODE == W) _w(); else if(IR_CODE == FLASH) _flash(); else if(IR_CODE == STROBE) _strobe(); //else if(IR_CODE == FADE) _fade(); //else if(IR_CODE == SMOOTH) _fade(); //we cant do switch case with string else IR_SYMBOL = "UNKNOWN"; Serial.print("The symbol is: "); //lets write symbol on serial Serial.println(IR_SYMBOL); } Efekty prac są podane na zdjęciach. Film dorzuce później, jak na yt wrzucę. Robot został nazwany pubga, na pamięć mojego kolegi z 8. klasy, który co chwile mówił do nauczycieli "a pogramy w pubga? pubga, w pubga!" przez chyba cały czerwiec XD W przyszłości chyba nie będę miał na niego za dużo czasu, bo technikum itd. Może na jakiś długi weekend, zamontuję do niego esp, jakieś dodatkowe czujniki albo miotacz ognia. Kto wie. Miłych wakacji, Leoneq ;3
  7. witam witam chciałbym wam przedstawić moja kolejna maszynkę o nazwie gt-07 robocik występował na robotik arena 2008 zdobył 2 miejsce w kategorii freestyle robot porusza się na gąsienicach zrobionych z zużytych pasków rozrządu i współpracującymi z nim elementami( koła z pompy wody napinacze ) napędzany przez dwa mocne silniki prądu stałego z poloneza polecam:) przeniesienie napędu z silnika na gąsienice po przez łańcuch rowerowy na samej platformie znajduje się robot typu arm już wcześniej pokazywany na forum w dziale mechanika na robot arena była uboższa wersja teraz doszło sterowanie z procesora:) wszystko widoczne na zdjęciu oczywiście jak już kiedyś pisałem nie stosuje gotowych serwo mechanizmów nie tylko ze jak dla mnie są za drogie ale mechanika zrobiona z elementów np vhs jest o wiele ciekawsza sami ocenicie:)masa całkowita ponad 25kg maxymalny udźwig robota nieznany ja ważę 80Kg i na nim sobie jezdźe po pokoju jako dodatków wyposażenie ma czujniki pochyłu rtęciowe i automatyczne podświetlanie na fotokomórce. W sumie nie ma się co zagłębiać w dokładny opis bo wszystko jest widoczne na zdjęciu:) mam nadzieje ze się spodoba:) jak na 200zł kosztów całkowitych Robot ten ma wielu przodków z których ma wzięte wiele rozwiązań wystarczy przewertować strony i znaleść roboty z serii gt jeżeli będzie taka potrzeba mogę zamieścić bardziej szczegółowy opis jak się spodobało postaw piwo:) tutaj jedyny filmik z działania robota około pierwszej minuty:)
  8. Cześć. Chciałbym przedstawić wam mój projekt robota samobalansującego. Jest to mój najbardziej zaawansowany jak dotąd projekt, który pochłonął najwięcej pracy. Pomysł na zbudowanie takiego robota zrodził się w mojej głowie w wakacje po obejrzeniu filmiku przedstawiającego podobną konstrukcję. Pracę nad nim rozpocząłem jeszcze w wakacje, a z przerwami zakończyłem w lutym. Działanie robota jest podobne do dostępnych na rynku Hoverboardów, czyli robot ma za zadanie poruszać się w taki sposób, aby utrzymać się w pionie, oczywiście bez podpierania. Konstrukcję robota zaprojektowałem sam i wykonałem z pręta gwintowanego i plexi. Uznałem że taka konstrukcja zapewni robotowi wymaganą sztywność i niską masę, jednocześnie nie rujnując budżetu. Najpierw wykonałem jej model w Fusion 360, a później przystąpiłem do pracy. Roboty z tym trochę było, bo obróbka plexi do łatwych nie należy. Sporo czasu zajęło przycięcie prostokątów, a jeszcze więcej wywiercenie otworów, które musiały być idealnie rozmieszczone, aby później dało się przełożyć przez nie pręt gwintowany. Po zbudowaniu ramy przyszła pora na elektronikę. Ponieważ robot nie zapewnia dużo miejsca, a jednocześnie środek ciężkości powinien być możliwie wysoko, dla ułatwienia balansu, musiałem się postarać ścisnąć wszystko jak to tylko możliwe. Baterię zasilającą robota (ogniwe Li-Po odzyskane z powerbanka o pojemności 3000 mAh) umieściłem na najwyższej półce. Tam również trafiła przetwornica step-up podnosząca napięcie do 7,4 V. Wiem że lepiej byłoby dać dwie baterie połączone szeregowo, ale w momencie budowania budżet na to nie pozwolił. Szybko okazało się że jedna przetwornica to za mało - Arduino restartowało się przy każdym uruchomieniu silnika, nawet jeśli do układu przyłączyłem bardzo duży kondensator. Dodałem więc drugą - teraz silniki były zasilanie niezależnie od elektroniki sterującej. Pozwoliło to stanąć robotowi po raz pierwszy, jednak wciąż jedna przetwornica zasilająca silniki ograniczała ich moc. Dodałem drugą, co nieco poprawiło sytuację, jednak wciąż nieco ogranicza silniki. Póki co szukam lepszego rozwiązania. Schodząc na niższą półkę. Tam trafiła płytka ze sterownikiem i druga z mostkiem H. Pierwszą płytkę lutowałem sam na płytce uniwersalnej - zajęło to kilka godzin i mnóstwo cyny, ale satysfakcja była nie do opisania, zwłaszcza że wszystkie połączenia wykonałem cyną. Po spędzeniu kolejnej godziny na pozbywaniu się zwarć układ był gotowy do testów Robot aby ustalić swoją pozycję korzysta z modułu żyroskopu i akcelerometru MPU6050 oraz wbudowanych w silniki enkoderów. Dane z tych czujników trafiają do Arduino Nano które poprzez układ L298N steruje dwoma silnikami. Dzięki akcelerometrowi i żyroskopowi robot zna swój kąt przechylenia. W pierwszych wersjach kodu szybko okazało się, że to nie wystarczająco, ponieważ o ile robot stał, to cały czas przesuwał się w jedną stronę. Wynika to z tego że konstrukcja nie jest idealnie wyważona, a robot nie zna swojej pozycji, jedynie kąt nachylenia, więc jeżeli ustawiony na sztywno kąt odbiegał od kąta balansu, robot "dryfował". Aby temu zapobiec dodałem drugie sprzężenie zwrotne, tym razem oparte o enkodery przy silnikach. Enkodery zliczają ilość impulsów które silniki wykonały w każdą ze stron, a oprogramowanie dąży, aby ich różnica była równa 0. Pierwsze próby nie były zbyt udane. Kiedy doprowadziłem kod do stanu w którym robot chociaż próbował się utrzymać, udawało mu się to przez maksymalnie kilkanaście sekund. Jednak wraz z poprawianiem kodu robot stał dłużej i dłużej. Obecnie już się nie wywala, jednak mostek H po dłuższym staniu się przegrzewa, więc czas pracy ograniczony jest do kilku minut. Mimo to robot robi duże wrażenie, chociaż z pewnością nie jest idealny. Zastosowałem tanie chińskie silniki, i jak się okazało, jeden obraca się szybciej niż drugi, więc robot obraca się w jedną stronę. Do tego jak wspomniałem przegrzewa się, a silnikom brakuje momentu, więc robot się trzęsie zamiast stać idealnie prosto. Są to jednak problemy które mam w planach poprawić. Planuję dodać również możliwość sterowania po BT (już nawet na płytce jest miejsce na moduł HC-06), ale na razie nie miałem czasu. Oto filmik prezentujący działanie robota:
  9. "Devastator" O zbudowaniu robota myślałem już dawno. Devastator ma też swojego poprzednika, lecz o wykonaniu conajmniej tragicznym. Po podjęciu decyzji o budowie robota zaczęłem rozglądać się za częściami a jednocześnie zajęłem się wakacyjną pracą, żeby jakoś dorobić się na te części. Zbudowanie tego robota kosztowało mnie jak do tej pory trochę ponad 1000 zł dodając jeszcze do tego masę czasu, kawy i nieprzespanych nocek przy budowie. Głównym problemem był dla mnie program. Programistą nie jestem jakimś dobrym a jeszcze dodatkowo projekt ten jest pierwszym z użyciem raspberry pi. Największym problemem był program. Sczególnie dał mi się we znaki, gdy chciałem zaprogramować serva. Jak na złość żaden program znaleziony w internecie nie chciał działać. Więc musiałem przestudiować zasadę działania serv, i na podstawie wyciągnietych wniosków metodą prób i błędów udało mi się dojść do rozwiązania tego problemu. Tak więc mając mniejsze i większe problemy udało mi się dobrnąć do zbudowania robota. Zamysł projektu jest taki, żeby zbudować takiego robota, który dostanie się w takie miejsca niebezpieczne lub miejsca gdzie człowiek się nie dostanie. Czyli np. budynki grożące zawaleniem, szczeliny, oraz wiele innych. Zamiast podawać teoretyczne zastosowania wolę wspomnieć o sytuacjach gdy Devastator znalazł zastosowanie. Przede wszystkim użyłem go do sprawdzenia stanu podwozia auta. następne zastosowanie znalazł gdy zatkały się przepusty na pod wjazdem na moje podwórko. Dzięki temu robotowi szybko odnalazłem owe zatkane przepusty, dzięki czemu oszczędziłem sobię wiele pracy. Sprzydał się również podczas szukania nieszczelności w instalacji hydraulicznej. Zaczynając od początku: Mózgiem robota jest Raspberry PI 3 b+(link na dole). Malinę wybrałem tylko dlatego, że potrzebowałem odskoczni od arduino. Następnie użyłem sterownik silników(link na dole) do sterowania gąsienicami. Do budowy użyłem również baterii li-ion 18650 , powerbanka , kilku serv, modułu kamery z nadajnikiem radiowym oraz własnej płytki pcb. Do budowy użyłem podwozia gąsienicowego "Devastator"(stąd nazwa robota, link na dole), ramienia wydrukowanego na drukarce 3D oraz wieżyczki z kamerką również drukowaną na drukarce. obecnie pracuję nad dopinanym z przodu spychem/elektromagnesem. Program napisany w Pythonie (z małą pomocą wujka google). Sterowany poprzez klawiaturę bluetooth, natomiast obraz przezyłany radiowo przez moduł bezpośrednio do telefonu. Devastator posiada zasilanie modułowe. Znaczy to, że osobno jest zasilana logika, silniki, serva, oraz kamerka. Devastator przeżył już swój debiut na konkursie Geniusz IT(Dotarł do finału). Jak wiadaomo z każdym projektem jest jakieś ograniczenie budżetowe. W moim przypadku ciągle szukam dodatkowych funduszy na ulepszenie tego projektu, np. muszę wymienić serva na o wiele wytrzymalsze bo na ten moment ramię działa jedynie w wersji pokazowej. Muszę również przerzucić się na komunikację radiową, a wiadomo nic za darmo. NIe będę już więcej przynudzał. wrzucam wam więc kilka zdjęć (sorki że obrócone, pogimnastykujecie się trochę ) : A tu kilka przydatnych linków: raspberry: https://botland.com.pl/pl/moduly-i-zestawy-raspberry-pi-3-b-plus/11142-raspberry-pi-3-model-b-wifi-dual-band-bluetooth-1gb-ram-14ghz-7131796402594.html?search_query=raspberry+pi+3+b+&amp;results=937 podwozie: https://botland.com.pl/pl/podwozia-robotow/6610-dfrobot-devastator-gasienicowe-podwozie-robota-metalowe-silniki.html?search_query=devastator&amp;results=2 sterownik silników: https://botland.com.pl/pl/podwozia-robotow/6610-dfrobot-devastator-gasienicowe-podwozie-robota-metalowe-silniki.html?search_query=devastator&amp;results=2
  10. O mnie Witam, Jestem Maciej - pracuje jako software architect (nie mam wykształcenia elektronicznego dlatego proszę o wyrozumiałość jeżeli chodzi o połączenia i rozwiązania - z tego też powodu w projekcie nie daje gotowego przepisu na zasilanie ) i żeby do końca nie zgnuśnieć po godzinach tworzę platformę RemoteMe. W platformie chodzi głównie o ułatwienie sterowaniem urządzeniami IoT poprzez internet - bez konieczności posiadania VPNa, publicznego IP. Po stronie RemoteMe tworzycie strony internetowe ( RemoteMe posiada hosting na Wasze pliki ) + zestaw bilbiotek (Javascript, RasbeprryPi (python), Arduino ESP ) które w łatwy sposób pozwolą na komunikowanie się z Waszymi urządzeniami. Co do protokołu to opracowałem własny (bardziej jest to konwencja przesyłu paczek niż protokół jako taki ) (działa przez sockety, websockety, wywoływania RESTowe) - umożliwia on przesył wiadomości do określonego urządzenia, i coś w rodzaju topic- subscribera ( u mnie się to nazwa"variables" zmienne ) Wasze urządzenia rejestrują się w RemoteMe po zarejestrowaniu, możecie do nich wysyłać komendy przez stronę internetową albo z innych urządzeń. Jednym z ostatnich featerów są "Out of the box projects" - polega to na tym, że jednym kliknięciem importujecie projekt na Wasze konto - oczywiście możecie potem wszytko zmieniać wedle własnych pomysłów - właśnie ostatnim takim projektem jest Samochód RaspberryPi z kamerką o którym w tym kursie. Projekt jest częściowo openSourcowy - bilbioteki Javascript, Python, api Restowe są openSource - sam kod serwera i program na RasbeprryPi jest zamknięty. Platformę tworzę sam - po godzinach i udostępniam za darmo O kursie Ten kurs – przedstawia budowę samochodu sterowanego przez przeglądarkę z wyświetlaniem obrazu z kamerki. Jest to dość specyficzny kurs (jak pisałem wyżej ) – całość oprogramowanie jest już zrobiona, a dodanie plików do własnego konta w RemoteMe sprowadza się do paru kliknięć. Po tak dodanym projekcie można własnoręcznie i dowolnie edytować wszystkie pliki źródłowe (strona WWW skrypt pythonowy) – zwłaszcza, że same pliki projektu nie są skomplikowane, a kod jest dość czytelny i samo opisujący się (mam przynajmniej taką nadzieję ) Na dole kursu opisana jest bardziej szczegółowo zasada działania samochodu. Jeżeli nie jesteście zainteresowani samą platformą RemoteMe to i tak myślę, że w kursie znajdziecie garść pomysłów na podobny projekt Kurs właściwy Na filmie działanie + krok po kroku wszystko z tego kursu. W poprzednim kursie pokazałem jak sterować pozycją kamerki i przechwytywać obraz na przeglądarkę tutaj i sterowanie kamerką tutaj . Teraz rozbudujemy ten projekt i zbudujemy samochód z napędem na 4 koła ( sterowany podobnie jak czołg – żeby skręcić prawa para kół musi się kręcić z inną prędkością niż lewa). Taki efekt otrzymamy na wideo też całość kursu – można zaglądnąć, gdy gdzieś utkniecie Części RaspberryPi z wifi ( np zeroW) link Podwozie z silnikami link Sterownik serwomechanizmów na I2C link Kamera do Rpi + taśma jeżeli potrzebna link Dwa SerwoMechanizmy kompatybilne z uchwytem kamerki kamery link Uchwyt na kamerkę z serwami link Mostek H TB6612FNG link Zasialnie – np akumlatory podłączone do przetwornicy – żeby uzyskać odpowiednie napięcie link Połączenia RaspberryPI steruje serwami poprzez moduł PWM i napędem przez ustawianie stanu pinów na mostku oraz dostarczając sygnału PWM poprzez ten sam moduł, który wysyła sygnał do serwomechanizmów. (Dzięki modułowi do generowanie sygnału PWM nie musimy tych sygnałów generować przez samo RaspberryPi – co jest dość problematyczne, poprostu przez I2C przesyłamy odpowiednie instrukcje do sterownika serw (poprzez bibliotekę pythonową dostarczoną przez adafruit), a ten generuje odpowiedni sygnał PWM dla serwo mechanizmów i do mostka H) Schemat połączeń: Poprzez magistrale I2C RPi steruje kontrolerem serwo mechanizmów, dwa ostanie wyprowadzenia kontrolera są podłączone do mostka H, gdzie wykorzystując sygnał PWM ustawiamy prędkość silników. Do mostka H są również podłączone piny RPi które stanami wysokim i niskim będą określać kierunek obrotu silników napędowych – po szczegóły odsyłam do dokumentacji układu TB6612FNG, równie dobrze można użyć mostka L298N lub podobnego. Zaprojektowałem płytkę PCB, którą możecie użyć pliki eagle,gerber itd tutaj plik pcb.zip Schemat płytki pokrywa się ze schematem powyżej. Wyprowadzenia płytki: Wejście sygnału PWM z kanałów 15 i 14 modułu PWM Wejście zasilania silników do poruszania się Zasilanie układów (PWM, RPi) koniecznie dokładne +5V Wyjście silników napędu, pierwsze dwa wyjścia do jednej pary silników kolejne dwa do drugiej Zasilanie serw i w tym przypadku silników napędu w moim przypadku ~6V (należy sprawdzić w specyfikacji serw i silników, jakie maksymalne napięcie można podłączyć) ZWORKA gdy zepniemy dwa piny napięcie z 5 będzie podawane też do zasilania silników napędu ZWORKA gdy jest podłączona zasilane jest RaspberryPI z połączenia 3 przed podłączeniem zworki należy dokładnie sprawdzić napięcia, żeby nie uszkodzić Najdroższego komponentu czyli właśnie malinki Wlutowujemy kabelki, bo nie będziemy korzystali z konwertera stanów Wlutowujemy kabelki, bo nie będziemy korzystali z konwertera stanów Oczywiście jest jeszcze potrzebne odpowiednie zasilanie, w moim przypadku 6v jako napięcie silników napędu i serw, oraz 5v dla RasbeprryPi i kontrolerów. Poniżej kilka zdjęć poglądowych całości samochodu: Programowanie Przed utworzeniem projektu skonfigurujcie kamerkę i komunikacje I2C używając raspi-config opisane tutaj. Ten projekt jest jednym z gotowych projektów, które możecie prosto zaimplementować kilkoma kliknięciami: KLIK - po zalogowaniu otworzy się projekt. Przejdźcie do zakładki “Build It” i podążajcie krokami, na końcu klik w “Build Project” i otrzymacie: Po kliknięciu “Get QR” pokaże się kod QR, który możecie zeskanować smartfonem, lub po prostu otworzyć stronę przyciskiem “Open” w przeglądarce na komputerze. Narazie nie radzę zmieniać pozycji kamery dopóki nie ustawimy pozycji serw – w niektórych przypadkach możecie uszkodzić swoje serwomechanizmy. Samochód powinien jeździć jak na filmie – może się zdarzyć, że skręca w złą stronę albo serwa są źle skalibrowane, poniżej w omawianiu kodu napisałem jak to naprawić. Omówienie tego, co się stało Jak zauważyliście, tworzenie projektu było mocno zautomatyzowane. Dlatego omówię teraz, co się dokładnie wydarzyło i jak modyfikować Wasz projekt. Przede wszystkim utworzyły się dwie zmienne (zakładka variables): Zmienna cameraPos przesyła pozycje kamery, drive pozycje joysticka. Obie są typem “Small int x2” oznacza to, że wartoscią zmiennej są dwie liczby integer. Strona internetowa zmienia nasze zmienne, a skrypt pythonowy te zmiany rejestruje i odpowiednio reaguje (jak rozwiniemy zmienne zobaczymy, że urządzeniem nasłuchującym na zmiany jest właśnie skrypt pythonowy). Zobaczmy jak wygląda kod Pythonowy (więcej o zmiennych tutaj) Python Skrypt pythonowy został automatycznie wgrany. Oczywiście możemy go podejrzeć i modyfikować ( żeby stworzyć nowy skrypt pythonowy np dla innych projektów zobacz tutaj) . Z informacji jakie teraz są Ci potrzebne to skrypt pythonowy to kolejne urządzenie w remoteMe jest zarządzalne przez skrypt (uruchomiony przez ./runme.sh) , do tego urządzenia możemy wysłać wiadomości, urządzenie reaguje też na zmiany zmiennych, które obserwuje i może te zmienne zmieniać. Żeby otworzyć skrypt pythonowy kliknij python.py i wybierz Edit : Wygląda on następująco. Poniżej omówię ciekawsze fragmenty import logging import socket import math import struct import sys import os os.chdir(sys.argv[1]) sys.path.append('../base') import remoteme import Adafruit_PCA9685 import time import RPi.GPIO as GPIO motorAIn1 = 25 # GPIO25 motorAIn2 = 8 # GPIO8 motorBIn1 = 24 # 24 motorBIn2 = 23 # 23 motors = [[motorAIn1, motorAIn2], [motorBIn1, motorBIn2]] motorsPWM = [14, 15] pwm = None; def motorForward(motorId): GPIO.output(motors[motorId][0], GPIO.LOW) GPIO.output(motors[motorId][1], GPIO.HIGH) def motorBackward(motorId): GPIO.output(motors[motorId][0], GPIO.HIGH) GPIO.output(motors[motorId][1], GPIO.LOW) def motorSoftStop(motorId): GPIO.output(motors[motorId][0], GPIO.LOW) GPIO.output(motors[motorId][1], GPIO.LOW) def setMotor(motorId, speed): if speed == 0: motorSoftStop(motorId) elif speed > 0: motorForward(motorId) elif speed < 0: motorBackward(motorId) speed=-speed logger.info("set speed {} for motor {} ".format(speed,motorId)) pwm.set_pwm(motorsPWM[motorId], 0, int(speed)) def onCameraPosChange(i1, i2): global pwm logger.info("on camera change {} , {}".format(i1, i2)) pwm.set_pwm(1, 0, i1) pwm.set_pwm(0, 0, i2) pass def onDriveChange(x, y): logger.info("on drive change x {} , y {}".format(x, y)) global pwm left=y right=y left+=x right-=x delta=(left+right)/2 left+=delta right+=delta # when your car doesnt drive as suppose try to swich right and left variable below # or remove add minuses next to 2 # another way is to switch cables conencted to motors setMotor(0, 2*left) setMotor(1, 2*right) pass def setupPWM(): global pwm pwm = Adafruit_PCA9685.PCA9685() pwm.set_pwm_freq(80) def setupPins(): global GPIO GPIO.setmode(GPIO.BCM) # Broadcom pin-numbering scheme for motor in motors: for pinId in motor: GPIO.setup(pinId, GPIO.OUT) try: logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s', datefmt='%d.%m %H:%M', filename="logs.log") logger = logging.getLogger('application') setupPWM() setupPins() remoteMe = remoteme.RemoteMe() remoteMe.startRemoteMe(sys.argv) remoteMe.getVariables().observeSmallInteger2("cameraPos" ,onCameraPosChange); remoteMe.getVariables().observeSmallInteger2("drive" ,onDriveChange); remoteMe.wait() finally: pass Sterowanie silnikami: def motorForward(motorId): GPIO.output(motors[motorId][0], GPIO.LOW) GPIO.output(motors[motorId][1], GPIO.HIGH) def motorBackward(motorId): GPIO.output(motors[motorId][0], GPIO.HIGH) GPIO.output(motors[motorId][1], GPIO.LOW) def motorSoftStop(motorId): GPIO.output(motors[motorId][0], GPIO.LOW) GPIO.output(motors[motorId][1], GPIO.LOW) def setMotor(motorId, speed): if speed == 0: motorSoftStop(motorId) elif speed > 0: motorForward(motorId) elif speed < 0: motorBackward(motorId) speed=-speed logger.info("set speed {} for motor {} ".format(speed,motorId)) pwm.set_pwm(motorsPWM[motorId], 0, int(speed)) Funkcja setMotor dla motorId 1 lub 2 ustawia prędkość speed (może być ujemna). poprostu najperw na odpowiednich pinach ustawiamy odpowiednio stany (ruch do przodu do tyłu, hamowanie), a następnie w zależności od prędkości ustawiamy odpowiednio wypełnienie PWM korzystając ze sterownika serw. def onCameraPosChange(x, y): global pwm logger.info("on camera change {} , {}".format(x, y)) pwm.set_pwm(1, 0, x) pwm.set_pwm(0, 0, y) pass Funkcja setMotor dla motorId 1 lub 2 ustawia prędkość speed (może być ujemna). poprostu najperw na odpowiednich pinach ustawiamy odpowiednio stany (ruch do przodu do tyłu, hamowanie), a następnie w zależności od prędkości ustawiamy odpowiednio wypełnienie PWM korzystając ze sterownika serw. def onDriveChange(x, y): logger.info("on drive change x {} , y {}".format(x, y)) global pwm left=y right=y left+=x right-=x delta=(left+right)/2 left+=delta right+=delta # when your car doesnt drive as suppose try to swich right and left variable below # or remove add minuses next to 2 # another way is to switch cables conencted to motors setMotor(0, 2*left) setMotor(1, 2*right) pass Powyższa funkcja zostanie wywołana jak zmieni się zmienna drive – np po zmianie joysticka na stronie. x,y to po prostu współrzędne wychylenia joysticka (1024,1024) oznacza wychylenie maksymalnie do góry i w prawo. Na podstawie tych zmiennych wyliczamy prędkość lewej i prawej strony samochodu. Jeżeli samochód skręca, zamiast jechać do przodu, jedzie do tyłu zamiast do przodu: setMotor(0, Y2*left) setMotor(1, X2*right) Dajcie minusy w różnych kombinacjach (w miejsca X i Y) do czasu, aż samochód jedzie do przodu – gdy joystick jest wychylony do góry. (będzie to odpowiadać zamianą miejscami przewodów lewej strony dla miejsca Y i prawej strony dla miejsca X). Następnie, jeżeli prawa i lewa strony są zamienione (samochód skręca w złą stronę ), w funkcji powyżej zamieńcie left i right miejscami. def setupPWM(): global pwm pwm = Adafruit_PCA9685.PCA9685() pwm.set_pwm_freq(80) def setupPins(): global GPIO GPIO.setmode(GPIO.BCM) # Broadcom pin-numbering scheme for motor in motors: for pinId in motor: GPIO.setup(pinId, GPIO.OUT) Po prostu ustawiamy odpowiednie piny (te do sterowania mostkiem H) na wyjścia. I tworzymy obiekt do kontrolowania sterowania serw. (Uwaga żeby sterownik serw działał prawidłowo musimy włączyć komunikacje I2C używając raspi-config więcej tutaj ) remoteMe.startRemoteMe(sys.argv) remoteMe.getVariables().observeSmallInteger2("cameraPos" ,onCameraPosChange); remoteMe.getVariables().observeSmallInteger2("drive" ,onDriveChange); remoteMe.wait() Ustawienie RemoteMe i ustawienie jakie funkcję mają zostać wywołane, gdy zmienne do sterowania kamery i napędu zostaną zmienione. To tyle w samym skrypcie pythonowym, jak widzicie, nie jest on zbyt skomplikowany, po więcej informacji zapraszam tutaj Strona WWW Tak naprawdę zostały stworzone dwie strony WWW — jedna do kalibracji druga — strona do sterowania samochodem i wyświetlania obrazu z kamerki. Otwórzmy stronę do kalibracji: Otworzy się strona internetowa z dwoma suwakami. Ustawmy górny i dolny na środkową pozycję – kamera się poruszy- górny suwak powinien poruszać kamerę w osi x, dolny y. Jeżeli się tak nie dzieje – zamieńcie miejscami przewody serwomechanizmu. Następnie za pomocą suwaków ustalcie maksymalne wychylenie osi x i osi y dla mnie jest to: x : 298 – 830 i centralna pozycja. Ważne, żeby centralna pozycja byłą dokładnie pomiędzy przedziałem u mnie ((298+830) /2 = 564) y: 223 – 723 i podobnie centralna pozycja kamery w osi y powinna być w środku przedziału Zapiszmy liczby gdzieś w notatniku i otwórzmy do edycji stronę do sterowania samochodem: <camera autoConnect="true" showInfo="true" class="cameraView"></camera> <connectionstatus webSocket="true" directConnection="false" camera="true"></connectionstatus> <variable component="cameraMouseTrack" type="SMALL_INTEGER_2" style="display:block" name="cameraPos" xMin="298" xMax="830" invertX="true" yMin="223" yMax="723" invertY="true" requiredMouseDown="true" reset="true" onlyDirect="true"></variable> <div class="joystickButtons"> <div class="buttons"> <variable class="gyroscope" component="gyroscope" type="SMALL_INTEGER_2" name="cameraPos" label="Gyroscope Camera" orientationSupport="true" xMin="298" xMax="830" xRange="19" invertX="true" yMin="223" yMax="723" yRange="20" invertY="false" onlyDirect="true"></variable> <variable class="gyroscope" component="gyroscope" type="SMALL_INTEGER_2" name="drive" label="Gyroscope Drive" xMin="-512" xMax="512" xRange="19" invertX="false" yMin="-512" yMax="512" yRange="20" invertY="false" onlyDirect="true"></variable> <button class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect gyroscope" onClick="toggleFullScreen()">fullscreen </button> </div> <div class="joystickParent"> <variable class="joystick" component="joystick_simple" type="SMALL_INTEGER_2" name="drive" xRange="1024" yRange="1024" onlyDirect="true"></variable> </div> <div style="clear: both;"/> </div> Są to automatyczne komponenty do sterowania samochodem i wyświetlaniem obrazu wideo. Strona, którą otworzyliście zawiera już wszystkie komponenty potrzebne do sterowania waszym samochodem. Jedyne co trzeba zrobić to zmienić zakres ruchów serwo mechanizmów. xMin,xMax,yMin,yMax, zamieńcie na wartości jakie otrzymaliście na poprzedniej stronie z suwakami. Jeżeli chcecie stworzyć własną stronę ze swoimi komponentami najlepiej utworzyć ją od początku pokazane tutaj – pozwoli Wam to dodawać komponenty w przy pomocy kreatora, gdzie ustalicie potrzebne parametry, albo po prostu edytować źródła tej już utworzonej strony – wcześniej warto utworzyć kopię, gdyby coś poszło nie tak (albo skasować pliki i utworzyć projekt jeszcze raz – wtedy nie zapomnijcie wcześniej wykasować też zmienne) Po zmianie wartości x/y/Min/Max możemy otworzyć naszą stronę np w smartfonie, klikamy na index.html, ale tym razem wybieramy opcje get anymous link Następnie, klikamy ikonkę kodu QR, a kod, który się pojawi skanujemy smarfonem. Oczywiście sterowanie działa również poprzez internet – nie tylko w sieci lokalnej, jednak w niektórych przypadkach jest potrzebna dodatkowa konfiguracja więcej o niej tutaj Trochę szczegółów technicznych ( nie obowiązkowe ) RemoteMe serwuję stronę WWW do sterowania Waszym samochodem ( a dodatkowo Was loguje na Wasze konto – stąd token w linku otwieranym przez smartfon ) i uczestniczy w negocjowaniu tworzenia połączenia WebRTC. Połączenie webRTC jest to połączenie bezpośrednie (RaspberryPi – przeglądarka ( w niektórych przypadkach np za NATem jest potrzebny dodatkowo stun sewer)). Po stworzeniu połączenia webRTC stan zmiennych nie jest w ogóle wysyłany do remoteMe (bo pole “onlyDirect” w komponentach jest ustawione na true). Protokołem webRTC przesyłany jest też obraz, a że webRTC tworzy połaczenie point to point opóźnienie jest znikome. Program na RaspberryPi który odpalacie poleceniem ./runme.sh tworzy połączenie websocketowe z platformą RemoteMe oraz zarządza skryptem pythonowym (wysyła do niego wiadomości, przesyła wiadomości ze skryptu dalej do platformy etc ). Działanie skryptu pythonowego jest możliwe dzięki dodatkowym bilbiotekom RemoteMe (znajdują sie w folderze leafDevices/base/ ). Sama strona internetowa po websocketach łączy się do platformy RemoteMe (pozwalają na to skrypty javascriptowe zaimportowane w headerze pliku index.html ze ścieżek /libs/). Ułatwiają i ustanawiają komunikacje z platformą RemoteMe. Same komponenty wstawione w index.html typu : <variable component="**" W funkcjach pliku remoteMeComponents.js są zastępowane na “standardowe” i “bootstrapowe” komponenty htmlowe, dodatkowo do komponentów przypinane są eventy reagujące na akcje użytkownika i wysyłające odpowiednie komunikaty do skryptu pythonowego. Można podejrzeć jak remoteMeComponents.js zamienia i tworzy nowe komponenty – może to być interesujące gdy macie potrzebę stworzenia własnych komponentów, które RemoteMe nie pozwala dodać z kreatora. W większości przypadków akcje Waszych komponentów będą wykonywać zapis zmiennych w postaci RemoteMe.getInstance().getVariables() .setSmallInteger2("cameraPos",123,456,true); która wyśle do skryptu pythonowego informacje o zmianie zmiennej, podobnie sterujemy silnikami ustawiając odpowiednią zmienną. Podsumowanie Tworząc tej projekt chciałem pokazać Wam jak w łatwy sposób sterować Waszym samochodem z widokiem FPV. Projekt można rozbudowywać, lub zmieniać np, gdy korzystanie z innego mostka H niż ja. Zachęcam zatem do eksperymentowania ze źródłami projektu . Gdy jest to Wasz pierwszy projekt, zachęcam do zrobienia na początek projektu z mrugającą diodą i poczytanie dokumentacji na www.remoteme.org Pozdrawiam, Maciej
  11. Trudne początki Tak naprawdę to jest chyba mój pierwszy projekt w świecie Arduino! Zamarzyłem sobie zbudowanie własnego, terenowego pojazdu zdalnie sterowanego - takiego, na którym można potem zamontować jakiś chwytak, ramię albo kamerkę z przekaźnikiem FPV. Kontroler Tu akurat nie miałem większego wyboru, bo wtedy pod ręką miałem akurat Arduino Leonardo. Zaletą tej płytki jest kompatybilność z popularnymi shieldami do Uno a także złącze microUSB typu B (zamiast mało popularnego złącza "drukarkowego" w Uno). Na potrzeby tego niezbyt skomplikowanego projektu moc obliczeniowa tej płytki jest również całkowicie wystarczająca. Podwozie Moim planem było zbudowanie definitywnego i niepokonanego łazika marsjańskiego, więc zwykłe podwozie nie wchodziło w grę - koniecznie musiało być terenowe. Przegrzebałem naprawdę połowę Internetu w poszukiwaniu tego idealnego podwozia (ale - nie ukrywajmy - mieszczącego się również w moim budżecie) i w końcu mój wybór padł na podwozie Dagu DG012-ATV z napędem na cztery koła. Nieco podwyższony prześwit w stosunku do innych podwozi (dający nadzieję na pokonywanie niewielkich przeszkód), napęd na cztery koła - wszystko to brzmiało bardzo zachęcająco. Czterema silnikami coś oczywiście musi obracać, razem z podwoziem nabyłem więc również czterokanałowy sterownik silników DFRobota (w postaci shieldu dla Arduino). Serwo i czujnik Żeby urozmaicić nieco projekt, dodałem do niego serwo modelarskie, na którym zamontowałem ultradźwiękowy czujnik odległości z założeniem, że spróbuję kiedyś napisać program do autonomicznego poruszania się. RC Od długiego czasu używam do zdalnego sterowania aparatury FrSky Taranis, więc oczywiście musiałem skorzystać z kompatybilnego odbiornika - w tym przypadku X8R. Zasilanie Projekt oczywiście musiał być mobilny, więc zdecydowałem się na zasilenie go dwucelowym akumulatorem Lipo; konieczne okazało się też zastosowanie układu BEC, który obniżył napięcie akumulatora do 5V. Teraz pozostało już tylko zmontować wszystko w całość. Montaż podwozia Tu obyło się bez niespodzianek i problemów, po prostu skręciłem wszystko zgodnie z instrukcją i wyprowadziłem na zewnątrz przewody, którymi zasilane miały być silniki. Potem sprawy nieco się skomplikowały. Wszystko rozbiło się generalnie o to, że jak bym nie ułożył elementów na podwoziu, po prostu nie było takiego ułożenia, żeby wszystko się zmieściło. Sprawy utrudniał również fakt zastosowania Leonardo, które - umówmy się - jest raczej kobylaste i znacznie lepiej sprawdziłoby się tu kompatybilne z tą płytką Arduino Micro. Do tego dochodził BEC, odbiornik, serwo (którego nota bene nie miałem jak zamocować, bo w wersji 4WD podwozia miejsce na serwo zajmowane było przez dwa dodatkowe silniki) no i oczywiście akumulator. Dlatego zdecydowałem się na umieszczenie wszystkiego ponad podwoziem, pozostawiając na dole sam akumulator. Przykleiłem więc na dolnym pokładzie rzep, na którym mocowany jest akumulator - przeciążenia podczas poruszania robota są tak znikome, że jest to naprawdę pewny i sprawdzony sposób montażu (pozwalający też szybko zamontować albo zdemontować akumulator w razie potrzeby). Oprócz tego przykręciłem w narożnikach podwozia długie dystanse (kupione kiedyś w Chinach na potrzeby projektu quadrokoptera) i zabrałem się za przygotowywanie górnej części pojazdu. Górne podwozie wykonałem z fragmentu płytki aluminiowej, którą dociąłem tak, by znalazła się dokładnie ponad górną częścią podwozia - i jednocześnie dzięki temu przykryła koła, z których pył mógł się dostawać do elektroniki. W płytce wyciąłem otwór na serwo; ponieważ nie dysponuję żadnym sprzętem CNC, który pomógłby mi wyciąć równy, parametryczny otwór, rad nierad wziąłem do ręki wiertarkę, najpierw nawierciłem otwory, potem zamontowałem w niej frez i zacząłem ręcznie wycinać aluminium, a na końcu doszlifowałem wszystko pilnikami o zmniejszającej się ziarnistości. Cały proces poniżej: Teraz można było powoli przystąpić do montażu. Na pierwszy ogień poszło serwo, które na szczęście wpasowało się w wycięty przeze mnie otwór po prostu idealnie. Następnym krokiem było zamontowanie BECa, którego umieściłem pod górnym pokładem. Przewód zasilający (zakończony złączem T-Dean) musiałem rozgałęzić, ponieważ jedna para przewodów musiała zostać połączona z BECem, który obniży napięcie do 5V dla części elektroniki, zaś druga para - do sterownika silników, który będzie potem je zasilał. Szczęśliwie silniki akceptują napięcie dwucelowego akumulatora LiPo - trzeba to koniecznie sprawdzić przed zakupem/montażem! Arduino Leonardo można zasilić bezpośrednio z akumulatora, natomiast konieczne było przylutowanie odpowiedniej wtyczki (na zdjęciu po prawej stronie). Na koniec pozostało podłączenie wszystkich komponentów i otrzymujemy następujący efekt: Programowanie Pierwszym programikiem, który napisałem, był tester silników, który uruchamiał każdy z nich na pół sekundy. Kod wygląda następująco: const int E1 = 3; // Motor1 Speed const int E2 = 11; // Motor2 Speed const int E3 = 5; // Motor3 Speed const int E4 = 6; // Motor4 Speed const int M1 = 4; // Motor1 Direction const int M2 = 12; // Motor2 Direction const int M3 = 8; // Motor3 Direction const int M4 = 7; // Motor4 Direction void M1_advance(char Speed) { digitalWrite(M1, HIGH); analogWrite(E1, Speed); } void M2_advance(char Speed) { digitalWrite(M2, LOW); analogWrite(E2, Speed); } void M3_advance(char Speed) { digitalWrite(M3, LOW); analogWrite(E3, Speed); } void M4_advance(char Speed) { digitalWrite(M4, HIGH); analogWrite(E4, Speed); } void M1_back(char Speed) { digitalWrite(M1, LOW); analogWrite(E1, Speed); } void M2_back(char Speed) { digitalWrite(M2, HIGH); analogWrite(E2, Speed); } void M3_back(char Speed) { digitalWrite(M3, HIGH); analogWrite(E3, Speed); } void M4_back(char Speed) { digitalWrite(M4, LOW); analogWrite(E4, Speed); } void setup() { for (int i = 3; i < 9; i++) pinMode(i, OUTPUT); for (int i = 11; i < 13; i++) pinMode(i, OUTPUT); pinMode(LED_BUILTIN, OUTPUT); } void loop() { digitalWrite(LED_BUILTIN, HIGH); M1_advance(100); delay(500); M1_advance(0); M2_advance(100); delay(500); M2_advance(0); M3_advance(100); delay(500); M3_advance(0); M4_advance(100); delay(500); M4_advance(0); digitalWrite(LED_BUILTIN, LOW); delay(2000); // Delay 2S } Jak widać, sterowanie silnikami odbywa się poprzez podawanie kierunku poprzez piny cyfrowe i prędkości poprzez piny analogowe - proste, jak konstrukcja cepa. Teraz przyszła kolej na odbiornik RC i tu zaczęły się schody. Odbiorniki klasycznie podają stan każdego kanału poprzez sygnał PWM. W praktyce jest to seria naprzemiennych zer i jedynek, z których każda para 1+0 trwa 1/55 sekundy. Wartość możemy odczytać mierząc czas trwania sygnału 1: jeżeli jest to 1 milisekunda, przyjmujemy wartość minimalną ("-1"), jeżeli 1.5 milisekundy, to wartość środkową ("0"), zaś jeśli 2 milisekundy, to wartość maksymalną ("1"). Niektóre odbiorniki pozwalają na nieco szerszy zakres - od 0.5ms do 2.5ms, X8R domyślnie podaje wartości z tego pierwszego zakresu. Ponieważ musiałem mierzyć wartości na dwóch różnych kanałach, zdecydowałem się skorzystać z mechanizmu przerwań. Działa on mniej więcej następująco: gdy zajdzie wybrane zdarzenie (na przykład zmiana stanu danego pinu z niskiego na wysoki), wykonanie programu jest przerywane, zostaje wykonana funkcja oznaczona jako tzw. handler przerwania, a po jej zakończeniu program wznawia wykonanie w miejscu, w którym został przerwany. Na Arduino można znaleźć bardzo wygodną bibliotekę EnableInterrupt, która uogólnia sposób dodawania i usuwania handlerów przerwań pomiędzy różnymi wersjami Arduino, a korzysta się z niej w następujący sposób: #include "EnableInterrupt.h" volatile int microsStart = 0; void pin0Rising() { microsStart = micros(); disableInterrupt(0); enableInterrupt(0, pin0Falling, FALLING); } void pin0Falling() { int microsEnd = micros(); int diff = microsEnd - microsStart; if (diff > 1500) { digitalWrite(LED_BUILTIN, HIGH); } else { digitalWrite(LED_BUILTIN, LOW); } disableInterrupt(0); enableInterrupt(0, pin0Rising, RISING); } void setup() { enableInterrupt(0, pin0Rising, RISING); pinMode(LED_BUILTIN, OUTPUT); } void loop() { } Po wpięciu przewodu sygnałowego do pinu 0 i uruchomieniu programiku na kontrolerze, wbudowana dioda powinna się zapalić w momencie, gdy ustawimy drążek w położeniu większym niż połowa. Zwrócę jeszcze uwagę na magiczne słówko "volatile" przy deklaracji zmiennej - informuje ono kompilator, że zmienna ta nie może zostać zoptymalizowana (kompilator, a dokładniej optymalizator w niektórych przypadkach może w locie usunąć zmienną, na przykład jeżeli nie jest ona używana lub przez cały czas trwania programu ma zawsze tę samą wartość). Dodam tu jeszcze jedną ważną informację: program obsługi przerwania powinien być tak krótki, jak to tylko możliwe! Ma to sporo sensu jeżeli się nad tym nieco dłużej zastanowić, ale ja na to nie wpadłem i w pierwotnej wersji programu cała obsługa silników umieszczona była właśnie w programie obsługi przerwania. I ku mojemu zdziwieniu, po uruchomieniu programu, pomimo tego, że drążki były w położeniu zerowym, dwa silniki zaczęły się obracać. Okazało się, że obsługa przerwania obliczającego czas trwania PWM na pierwszym kanale trwała tak długo, że sztucznie opóźniała wywołanie drugiego przerwania wykonującego te same obliczenia dla drugiego kanału, przez co podawało ono zawyżone wartości. Wystarczyło przebudować program w taki sposób, by obsługa silników znalazła się poza programami obsługi przerwań i wszystko wróciło do normy. Pamiętajmy - to może oczywiste, ale mimo wszystko warto to powiedzieć - że mikrokontrolery nie są wielowątkowe, a przerwania nie są wątkami: wykonują się tylko jedno na raz. Drugie przerwanie musi czekać, aż pierwsze zostanie do końca obsłużone. Przed napisaniem końcowego programu pozostało mi już tylko przetestować obsługę czujnika ruchu, programik wygląda następująco: #include <NewPing.h> #define ULTRASONIC_TRIGGER_PIN 9 #define ULTRASONIC_ECHO_PIN 10 #define MAX_DISTANCE 400 NewPing sonar(ULTRASONIC_TRIGGER_PIN, ULTRASONIC_ECHO_PIN, MAX_DISTANCE); void setup() { pinMode(LED_BUILTIN, OUTPUT); } void loop() { digitalWrite(LED_BUILTIN, HIGH); delay(50); digitalWrite(LED_BUILTIN, LOW); delay(50); int ping = sonar.ping_cm(); if (ping < 30) digitalWrite(LED_BUILTIN, HIGH); else digitalWrite(LED_BUILTIN, LOW); delay(200); } Również i tu korzystam z wbudowanej diody, która powinna zapalić się, gdy zmierzona przez czujnik odległość będzie mniejsza niż 30 cm. Wreszcie program obsługujący całego robota: #include <NewPing.h> #include "EnableInterrupt.h" #include "NewPing.h" // Motor constants // Motor1 Speed #define MOTOR_1_SPEED_PIN 3 // Motor2 Speed #define MOTOR_2_SPEED_PIN 11 // Motor3 Speed #define MOTOR_3_SPEED_PIN 5 // Motor4 Speed #define MOTOR_4_SPEED_PIN 6 // Motor1 Direction #define MOTOR_1_DIR_PIN 4 // Motor2 Direction #define MOTOR_2_DIR_PIN 12 // Motor3 Direction #define MOTOR_3_DIR_PIN 8 // Motor4 Direction #define MOTOR_4_DIR_PIN 7 // Ultrasonic constants #define ULTRASONIC_TRIGGER_PIN 9 #define ULTRASONIC_ECHO_PIN 10 #define MAX_DISTANCE 400 // Servo constants #define SERVO_PIN 13 // AI constants #define DISTANCE_THRESHOLD_2 80 #define DISTANCE_THRESHOLD_1 40 // Ultrasonic control NewPing sonar(ULTRASONIC_TRIGGER_PIN, ULTRASONIC_ECHO_PIN, MAX_DISTANCE); // Motor control functions void M1_advance(byte Speed) ///<Motor1 Advance { digitalWrite(MOTOR_1_DIR_PIN, HIGH); analogWrite(MOTOR_1_SPEED_PIN, Speed); } void M2_advance(byte Speed) ///<Motor2 Advance { digitalWrite(MOTOR_2_DIR_PIN, LOW); analogWrite(MOTOR_2_SPEED_PIN, Speed); } void M3_advance(byte Speed) ///<Motor3 Advance { digitalWrite(MOTOR_3_DIR_PIN, LOW); analogWrite(MOTOR_3_SPEED_PIN, Speed); } void M4_advance(byte Speed) ///<Motor4 Advance { digitalWrite(MOTOR_4_DIR_PIN, HIGH); analogWrite(MOTOR_4_SPEED_PIN, Speed); } void M1_back(byte Speed) ///<Motor1 Back off { digitalWrite(MOTOR_1_DIR_PIN, LOW); analogWrite(MOTOR_1_SPEED_PIN, Speed); } void M2_back(byte Speed) ///<Motor2 Back off { digitalWrite(MOTOR_2_DIR_PIN, HIGH); analogWrite(MOTOR_2_SPEED_PIN, Speed); } void M3_back(byte Speed) ///<Motor3 Back off { digitalWrite(MOTOR_3_DIR_PIN, HIGH); analogWrite(MOTOR_3_SPEED_PIN, Speed); } void M4_back(byte Speed) ///<Motor4 Back off { digitalWrite(MOTOR_4_DIR_PIN, LOW); analogWrite(MOTOR_4_SPEED_PIN, Speed); } // PWM control volatile int microsYStart = 0; volatile int microsXStart = 0; volatile int microsZStart = 0; volatile int yMicros = 0; volatile int xMicros = 0; volatile int zMicros = 0; int UScounter = 0; int USlock = 0; // Pin 0 interrupt handling void pin0Rising() { microsYStart = micros(); disableInterrupt(0); enableInterrupt(0, pin0Falling, FALLING); } void pin0Falling() { yMicros = micros() - microsYStart; disableInterrupt(0); enableInterrupt(0, pin0Rising, RISING); } // Pin 1 interrupt handling void pin1Rising() { microsXStart = micros(); disableInterrupt(1); enableInterrupt(1, pin1Falling, FALLING); } void pin1Falling() { xMicros = micros() - microsXStart; disableInterrupt(1); enableInterrupt(1, pin1Rising, RISING); } // Pin2 interrupt handling void pin2Rising() { microsZStart = micros(); disableInterrupt(2); enableInterrupt(2, pin2Falling, FALLING); } void pin2Falling() { zMicros = micros() - microsZStart; disableInterrupt(2); enableInterrupt(2, pin2Rising, RISING); } void setup() { for (int i = 3; i < 9; i++) pinMode(i, OUTPUT); for (int i = 11; i < 13; i++) pinMode(i, OUTPUT); pinMode(ULTRASONIC_TRIGGER_PIN, OUTPUT); pinMode(ULTRASONIC_ECHO_PIN, INPUT); pinMode(SERVO_PIN, OUTPUT); enableInterrupt(0, pin0Rising, RISING); enableInterrupt(1, pin1Rising, RISING); enableInterrupt(2, pin2Rising, RISING); } void loop() { // Eval motor signals int yValue = (yMicros - 1500) / 2; int xValue = (xMicros - 1500) / 2; if (yValue > 260 || yValue < -260) yValue = 0; if (xValue > 260 || xValue < -260) xValue = 0; int leftEngines = constrain(xValue + yValue, -250, 250); int rightEngines = constrain(yValue - xValue, -250, 250); // Check for obstacles every 10th iteration UScounter++; if (UScounter >= 10) { UScounter = 0; int frontDistance = sonar.convert_cm(sonar.ping_median(5)); if (frontDistance != 0 && frontDistance < DISTANCE_THRESHOLD_2) { if (frontDistance > DISTANCE_THRESHOLD_1) USlock = 1; else USlock = 2; } else USlock = 0; } if (USlock == 1) { leftEngines = constrain(leftEngines, -250, 128); rightEngines = constrain(rightEngines, -250, 128); } if (USlock == 2) { leftEngines = constrain(leftEngines, -250, 0); rightEngines = constrain(rightEngines, -250, 0); } if (abs(leftEngines) < 20) { M3_advance(0); M4_advance(0); } else if (leftEngines > 0) { M3_advance(leftEngines); M4_advance(leftEngines); } else { M3_back(-leftEngines); M4_back(-leftEngines); } if (abs(rightEngines) < 20) { M1_advance(0); M2_advance(0); } else if (rightEngines > 0) { M1_advance(rightEngines); M2_advance(rightEngines); } else { M1_back(-rightEngines); M2_back(-rightEngines); } } Pozwala on na kontrolowanie robota przy pomocy jednego drążka aparatury (dwóch kanałów). Oprócz tego program cyklicznie sprawdza odległość przed robotem i zabezpiecza przed wjechaniem w ścianę: w przypadku przeszkody znajdującej się bliżej niż 80 cm od robota, zostanie ograniczona jego maksymalna prędkość, natomiast jeżeli odległość ta będzie mniejsza niż 40cm, robot zatrzyma się całkowicie i niemożliwe będzie ruszenie nim do przodu. Wnioski To była prawdziwa frajda zobaczyć, jak robot rusza i jeździ zgodnie z instrukcjami z aparatury! Pierwszy skończony projekt. Nauczyłem się na nim dużo, bo okazało się, że w trakcie pracy podjąłem bardzo dużo nietrafnych decyzji. Robot tak naprawdę nigdy nie wyjechał z domu, ma bardzo otwartą konstrukcję, która sprzyja dostawaniu się do obudowy pyłu i piachu. W domowych warunkach wystarczyłby natomiast napęd na dwa koła - w ten sposób miałbym też trochę miejsca wewnątrz obudowy. Arduino Leonardo jest świetne, ale wielkie. Znacznie lepiej sprawdziłoby się Arduino Micro albo Teensy. To drugie nawet bardziej, bo shield do sterowania silnikami pożera dużo pinów i niewiele zostaje na odbiór sygnału z odbiornika RC. Udało mi się jeszcze podłączyć czujnik odległości, ale serwo wpiąłem już bezpośrednio w odbiornik, bo po prostu zabrakło mi pinów. Nie ma większego sensu robić żadnych projektów (innych niż wstępne prototypy) na przewodzikach połączeniowych. Lepiej kupić sobie płytkę prototypową, złącza goldpinowe, przewodziki kydexowe i polutować wszystko na płytce - układ zajmuje znacznie mniej miejsce i jest znacznie mniej podatny np. na przypadkowe wyjęcie przewodu. Chodzi mi po głowie wskrzeszenie projektu - właśnie przy pomocy Teensy oraz drukarki 3D, przy pomocy której mogę wydrukować całe nadwozie szyte na miarę. Ale podejrzewam, że zajmę się tym dopiero za jakiś czas...
  12. Cześć! Skończyłem budowę mojego pierwszego robota. Jest to też mój pierwszy większy projekt, więc proszę o wyrozumiałość, komentarze, porady mile widziane. Z racji tego, że posiadam jeszcze dwa komplety lego, postanowiłem je wykorzystać do budowy pojazdu. Początkowo miały być silniki lego, ale stwierdziłem, że lepiej będzie zrobić to na zwykłych, uniwersalnych częściach. Konstrukcja miała być mała, zwinna, sterowana przez WIFI. Przednia oś, jak widać, sterowana za pomocą micro serwa. Nie było większych oporów, serwo bez problemów sobie radziło ze sterowaniem. Zacząłem szukać jakiegoś ciekawego i małego silnika DC. Postanowiłem wybrać silnik z podwójnym wałem. Kolejnym zadaniem było przebudowanie konstrukcji tak, żeby silnik się zmieścił i nie wadził w poruszaniu się robota. Z racji tego, że wolałem zaoszczędzić kilkanaście złotych na przejściówkę lego-wał, rozwierciłem otwory w częściach które miałem. Zacząłem myśleć jak wyglądałoby połączenie iPhone - ESP, ale w międzyczasie na uczelni dziekan zapowiedział nam dodatkowy projekt na zajęciach z mikrokontrolerów ( pierwsze takie zajęcia na drugim roku studiów ) - dowolny robot na platformie Arduino lub STM. Chłopaki na Facebookowej grupie Arduino uświadomili mi, że nie ma sensu robić projektu na ESP, bo musiałbym się łączyć tylko przez sieć i lepszym wyborem byłby bluetooth. Tak też się stało, zakupiłem moduł HM-10, który współpracuje z używanym przeze mnie iOS. Do tego całość przeniosłem na klona płytki Arduino. Jako aplikacji sterującej użyłem ArduinoBlue, wraz z biblioteką. Inne aplikacje nie chciały działać. W dodatku albo na iOS nie ma żadnych popularnych aplikacji do sterowania przez BLE albo nie udało mi się znaleźć. Możecie polecić ciekawe aplikacje na iOS, na pewno sprawdzę! Można zauważyć, że na breadbordzie zainstalowałem jakąś płytkę. Jest to sterownik silników DC, polecany, łatwy w użyciu. Dobra, silnik jest, działa, serwo jest, łączność przez BLE również. Wypadało by odpiąć wszystko od zasilania z gniazdka i zastosować akumulatory, baterie. Z początku była to dla mnie czarna magia, większe napięcie, jakieś ampery, przetwornice. Czytałem różne artykuły, oglądałem poradniki, pytałem na Facebookowej grupie. Ostatecznie, zgodnie z zasadami: Arduino + sekcja logiczna zasilana z powerbanka + wyprowadzenie zasilania bezpośrednio do serwa, silnik DC zasilany przez dwa ogniwa litowo-jonowe połączone szeregowo z BMS, napięcie zmniejszane przez przetwornicę Żeby szybko podpinać ogniwa do robota albo ładować, przylutowałem wtyki/gniazda DC 5.5mm. BTW. Bardzo długo męczyłem się z lutowaniem ogniw na 30-watowej lutownicy kolbowej. Metodą prób i błędów wszystko się złączyło. Przed podłączeniem każdego elementu lutowanego, sprawdzałem miernikiem czy nie ma zwarcia! Dodatkowo pokusiłem się o popularny czujnik odległości oraz mała prowizorka - dwie diody z opornikami schowane w obudowie po serwie (spaliło się), informujące o przeszkodzie w danej odległości. Wersja finalna, kilkukrotnie większa i cięższa niż pierwotna wersja. Wzmocniony układ kierowniczy. Jeździ, skręca, hamuje, cofa. Trzymany pod kocem Wszelkie komentarze mile widziane! A tutaj krótki filmik (musiałem zdemontować czujnik odległości, ponieważ powodował zakłócenia całego układu: Tak wygląda sterowanie za pomocą joysticka:
  13. Witam, jest to mój pierwszy projekt, na którym uczyłem się nieco programowania ... a że w piwnicy leżał mi popsuty odkurzacz to postanowiłem go ożywić po swojemu. Odkurzacz który notabene dostałem jako gratis działał dosłownie 10 minut po czym nie dawał oznak życia. 1.Podwozie jest wykorzystane w 100% z oryginału, nie zmieniałem nic oprócz zrobienia miejsca na elektronikę. 2. Z oryginału wykorzystałem również zasilanie (czyli ogniwa Li-lon) wraz z ładowarką. Zasilanie było oryginalne 7.2 V 3. Elektronika - wykorzystałem Arduino UNO wraz z shield sterownika L293D do 4x silników DC lub 2x krokowych w sumie wykorzystałem taki bo taki miałem, trochę nad wyrost bo zostają nie wykorzystane (a może jeszcze nie wykorzystane) dwa wejścia pod silniki DC. Jako że zaczynałem od podstaw programowanie, przysporzyło mi to trochę problemów. Ale od czego jest forum Na początku udało mi się zrobić aby pojazd sam się poruszał i odbijając od przeszkód (za pomocą dwóch umieszczonych wyłączników krańcowych umieszczonych z przodu odkurzacza) zmieniał kierunek jazdy, nie działało to w 100% tak jak sobie to na początku założyłem, ze względu na ogromny brak wiedzy w programowaniu, ale jednak pierwsze efekty pracy zainspirowały mnie do dalszej pracy nad robotem. #include <AFMotor.h> AF_DCMotor motorP(1); // silnik prawy AF_DCMotor motorL(2); // silnik lewy #define zdL 9 #define zdP 10 void setup() { motorP.setSpeed(255); motorL.setSpeed(255); pinMode(zdP, INPUT_PULLUP); pinMode(zdL, INPUT_PULLUP); } void loop() { bool zderzakL = digitalRead(zdL) == HIGH; bool zderzakP = digitalRead(zdP) == HIGH; // jazda do tyłu if (zderzakL == LOW && zderzakP == LOW) { motorP.run(BACKWARD); motorL.run(BACKWARD); } // // skręt w prawo else if (zderzakL == LOW) { motorP.run(BACKWARD); motorL.run(FORWARD); } // skręt w lewo else if (zderzakP == LOW) { // delay(200); motorP.run(FORWARD); motorL.run(BACKWARD); } // jazda do przodu else { motorP.run(FORWARD); motorL.run(FORWARD); } } Z shelda wykorzystałem piny 9 oraz 10 do serwa jako sterowanie do krańcówek. Kolejną rzecz jaką chciałem zrobić i pobawić się było stworzenie możliwości sterowania odkurzaczem za pomocą bluetooth oraz telefonu z androidem. A więc jak tylko paczuszka ze sklepu pojawiła się w moim domu wraz z modułem bluetooth (2.1 XM-15B 3,3V/5V) postanowiłem działać dalej. Pierwszy problem to połączenie modułu bluetooth z płytką, musiałem dolutować w odpowiednie miejsca przewody aby móc komunikować się z arduino (Rx, Tx) jak również doprowadzenie zasilania. Jednak nie stanowiło to większego wyzwania, udało się za pierwszym razem. Następnie przyszedł czas na połączenie bluetooth z telefonem. Udało mi się to zrealizować również w dość szybkim czasie (wcześniej oczywiście przeglądałem różnego rodzaju poradniki, czy posty kolegów z forum). Przyszedł czas na aplikację do telefonu, jako że gotowe rozwiązania nie podobały się (a może bardziej ambicja wzięła górę) postanowiłem stworzyć coś własnego. Z pomocą przyszła stronka http://ai2.appinventor.mit.edu. Gdzie w sposób prawie że obrazkowy udało mi się zrobił własny interfejs z własna logiką. Przyszedł czas na oprogramowanie w Arduino wszystkich funkcji które chciałem uzyskać tj: możliwość ręcznego sterowania przełączania na tryb automatyczny sterowania prędkością silniczków Udało mi się to za pomocą kodu: #include <AFMotor.h> AF_DCMotor motorP(1); // silnik prawy AF_DCMotor motorL(2); // silnik lewy #define zdL 9 #define zdP 10 String data; void setup() { Serial.begin(9600); Serial.println("Test portu"); pinMode(zdP, INPUT_PULLUP); pinMode(zdL, INPUT_PULLUP); bool zderzakL = digitalRead(zdL) == HIGH; bool zderzakP = digitalRead(zdP) == HIGH; } uint8_t oP = 255; uint8_t oL = 255; int pierwszyStart = 1; void loop() { if (Serial.available() > 0) { delay(1); char znak = Serial.read(); data += znak; pierwszyStart = 2; ///////////// Prędkość silników ///////////// if (data == "X") { oP = oP + 1; } if (data == "x") { oP = oP - 1; } if (data == "Y") { oL = oL + 1; } if (data == "y") { oL = oL - 1; } ///////////// Prędkość silników ///////////// Serial.print("Prędkość silnika prawego: "); Serial.println(oL); Serial.print("Prędkość silnika lewego: "); Serial.println(oP); if (data == "G") { motorP.setSpeed(oP); motorL.setSpeed(oL); motorP.run(FORWARD); motorL.run(FORWARD); } if (data == "B") { motorP.setSpeed(oP); motorL.setSpeed(oL); motorP.run(BACKWARD); motorL.run(BACKWARD); } if (data == "L") { motorP.setSpeed(oP); motorL.setSpeed(oL); motorP.run(BACKWARD); motorL.run(FORWARD); } if (data == "R") { motorP.setSpeed(oP); motorL.setSpeed(oL); motorP.run(FORWARD); motorL.run(BACKWARD); } if (data == "S") { motorP.setSpeed(0); motorL.setSpeed(0); } if (data == "A") { bool zderzakL = digitalRead(zdL) == HIGH; bool zderzakP = digitalRead(zdP) == HIGH; motorP.setSpeed(oP); motorL.setSpeed(oL); // jeżeli oba zderzaki if (zderzakL == LOW && zderzakP == LOW) { motorP.run(BACKWARD); motorL.run(BACKWARD); delay(250); motorP.run(FORWARD); motorL.run(BACKWARD); delay(1150); } // skręt w prawo else if (zderzakL == LOW) { motorP.run(BACKWARD); motorL.run(BACKWARD); delay(100); motorP.run(FORWARD); motorL.run(BACKWARD); delay(100); } // skręt w lewo else if (zderzakP == LOW) { motorP.run(BACKWARD); motorL.run(BACKWARD); delay(100); motorP.run(BACKWARD); motorL.run(FORWARD); delay(100); } // jazda do przodu else { motorP.run(FORWARD); motorL.run(FORWARD); } } data = ""; } else { if (pierwszyStart == 1) { bool zderzakL = digitalRead(zdL) == HIGH; bool zderzakP = digitalRead(zdP) == HIGH; motorP.setSpeed(oP); motorL.setSpeed(oL); // jeżeli oba zderzaki if (zderzakL == LOW && zderzakP == LOW) { motorP.run(BACKWARD); motorL.run(BACKWARD); delay(250); motorP.run(FORWARD); motorL.run(BACKWARD); delay(1150); } // skręt w prawo else if (zderzakL == LOW) { motorP.run(BACKWARD); motorL.run(BACKWARD); delay(100); motorP.run(FORWARD); motorL.run(BACKWARD); delay(100); } // skręt w lewo else if (zderzakP == LOW) { motorP.run(BACKWARD); motorL.run(BACKWARD); delay(100); motorP.run(BACKWARD); motorL.run(FORWARD); delay(100); } // jazda do przodu else { motorP.run(FORWARD); motorL.run(FORWARD); } } } delay(50); } Na pewno nie jest on w 100% zgodny ze sztuką, ale nic dziwnego bo programowania się nie uczyłem. Jest to zlepek funkcji odnalezionych w internecie i przystosowanych do moich potrzeb. Szczerze chciałbym zobaczyć prawidłowy wygląd kodu Na tym chyba zakończę podsumowując że obiekt spełnił moje oczekiwania, a to jest chyba najważniejsze: mogę sterować robotem/odkurzaczem z telefonu przy włączeniu uruchamia się tryb automatyczny bez potrzeby uruchamiania aplikacji w telefonie możliwość włączania/wyłączania na tryb automatyczny z telefonu sterowanie robotem w trybie automatycznym (w przypadku chęci korekty kierunku jazdy) i poza nim odbijanie od przedmiotów Filmik prezentujący funkcjonalność automatycznej jazdy.
  14. Witam, jako, iż jest to pierwszy opis mojego projektu na forum krótko się przedstawię. Mam na imię Marek, niedługo skończę 15 lat a elektroniką interesuję się od około roku. Chciałbym wam dzisiaj przedstawić mojego właściwie drugiego robota, wcześniej był światłolub, ale stwierdziłem, że chciałbym coś czym mógłbym wygodnie sterować używając np telefonu.Nazwa robota oznacza "Bluetooth Pojazd Jeżdżący 1". Na początku użyłem zamówionego z chin Wemos D1 Mini, który potem zmieniłem na połączenie klona Arduino Uno + HC-05 gdyż uznałem to jako lepszą opcję, oraz w moje ręce wpadł ten właśnie moduł bluetooth, następnie po ukazaniu projektu w szkole, zamieniłem Arduino Uno na Arduino Nano, również klona, głównie z powodu rozmiarów. Początkowo zasilanie było zrobione z 4 paluszków AA, co było złym pomysłem, silniki słabo działały, jeżeli w ogóle, chyba, że baterie były nowe. Baterie zostały zamienione na 2 Ogniwa Li-Ion od Panasonica NCR18650B o pojemności 3400mAh od sztuki które są połączone równolegle.Dwoma silnikami steruje sterownik L293D, wszystko znajduje się na podwoziu 2WD RT-5.Umieściłem dodatkowo diodę led RGB, z poziomu aplikacji można wybierać jej kolor, a dodatkowo, jej kolor jest zależny on tego w jakim kierunku porusza się robot. Komunikacja z modułem Bluetooth odbywa się przez aplikacje na telefon. To tak w skrócie, poniżej opiszę poszczególne "sekcje" robota. 1.Zasilanie: Zasilanie, tak jak wyżej napisałem są to 2 Akumulatorki Li-Ion, połączyłem je równolegle, ponieważ chce oba ładować jedną ładowarką, bez potrzeby ich wyjmowania z koszyczka (balancera nie posiadam) . Dlatego iż ogniwa nie mają żadnych zabezpieczeń, ładowarka jest wymagana ponieważ posiada wbudowane już zabezpieczenia. Ogniwa podłączone są do ładowarki TP4056, na wyjściu z niej jest zamontowany Dip Switch, jeden przełącznik do + zasilania, drugi do -. Następnie jest przetwornica step-up MT3608, która podwyższa mi napięcie z 3.6V, na 5V. Następnie poprzez kolejny przełącznik zasilanie idzie do płytki stykowej skąd jest rozprowadzane do wszystkich urządzeń (Silniki, sterownik, arduino, moduł Bluetooth). Praktycznie cały układ zasilania jest polutowany, na uniwersalnej płytce do lutowania 5cm na 7cm. 2.Komunikacja przez Bluetooth: Komunikacja z robotem jest wykonywana poprzez dosyć popularny Bluetooth, Aplikacja na telefon została wykonana przeze mnie, wykorzystując ten poradnik. Wykonałem ją w Mit App Inventor 2. Sama aplikacja wygląda tak: Na samej górze widzimy przycisk by połączyć się z HC-05, niżej widać przyciski do sterowania diodą RGB, a na samym dole są przyciski do sterowania silnikami. 3.Połączenia poszczególnych modułów, silniki, i koła Większość połączeń wykonałem prostymi przewodami jak te albo te, co się dało zamieniłem na ręcznie robione przeze mnie zworki wykonane z skrętki telefonicznej, a reszta jest polutowana. Sterownik silników znajduje się w podstawce na przeciętej na pół płytce do lutowania, ponieważ jest za długa a ja potrzebowałem tylko kawałka na ten sterownik (same zasilanie przez ogniwa było dodane dużo później więc dlatego 2 płytki) Silniki to dosyć tanie, w pakiecie z podwoziem TT D65, zasilane mogą być do 6V, oraz zawierają już wbudowaną przekładnie 1:48. Koła były w pakiecie z silnikami, wiele o nich nie mogę powiedzieć. Oprócz 2 kół na silnikach mam jedno małe z tyłu, dosyć popularne w światłolubach 4.Program w Arduino Program został również przeze mnie, myślę, iż jest raczej na podstawowym poziomie i nie ma czym się chwalić, na początku odczytuje informację otrzymane z modułu Bluetooth, z którym jest połączony przez TX i RX. Zależnie od otrzymanej liczby wykonywany jest konkretny warunek. char t; void setup() { pinMode(9,OUTPUT); //Lewy silnik przód pinMode(10,OUTPUT); //Lewy silnik tył pinMode(11,OUTPUT); //Prawy silnik przód pinMode(12,OUTPUT); //Prawy silnik tył pinMode(5,OUTPUT); //RGB Czerwony pinMode(6,OUTPUT); //RGB Zielony pinMode(7,OUTPUT); //RGB Niebieski Serial.begin(9600); } void loop() { if(Serial.available()){ t = Serial.read(); Serial.println(t); } if(t == '5') { //Do przodu digitalWrite(9,HIGH); digitalWrite(10,LOW); digitalWrite(11,HIGH); digitalWrite(12,LOW); digitalWrite(6,HIGH); //Zielony ON digitalWrite(5,LOW); //Czerwony OFF digitalWrite(7,LOW); //Niebieski OFF } else if(t == '6') { //Do tyłu digitalWrite(9,LOW); digitalWrite(10,HIGH); digitalWrite(11,LOW); digitalWrite(12,HIGH); digitalWrite(5,HIGH); //Czerwony ON digitalWrite(6,LOW); //Zielony OFF digitalWrite(7,LOW); //Niebieski OFF } else if(t == '7') { //W prawo digitalWrite(9,LOW); digitalWrite(10,LOW); digitalWrite(11,HIGH); digitalWrite(12,LOW); digitalWrite(7,HIGH); //Niebieski ON digitalWrite(5,LOW); //Czerwony OFF digitalWrite(6,LOW); //Zielony OFF } else if(t == '8') { //W lewo digitalWrite(9,HIGH); digitalWrite(10,LOW); digitalWrite(11,LOW); digitalWrite(12,LOW); digitalWrite(7,HIGH); //Niebieski ON digitalWrite(5,LOW); //Czerwony OFF digitalWrite(6,LOW); //Zielony OFF } else if(t == '9') { //STOP digitalWrite(9,LOW); digitalWrite(10,LOW); digitalWrite(11,LOW); digitalWrite(12,LOW); digitalWrite(5,HIGH); //Czerwony ON digitalWrite(6,LOW); //Zielony OFF digitalWrite(7,LOW); //Niebieski OFF } else if(t == '1') digitalWrite(5,HIGH); //Czerwony ON else if(t == '2') digitalWrite(6,HIGH); //Zielony ON else if(t == '3') digitalWrite(7,HIGH); //Niebieski ON else if(t == '4') { //RGB OFF digitalWrite(5,LOW); digitalWrite(6,LOW); digitalWrite(7,LOW); } delay(100); } 5.Prezentacja działania Testy wykonałem na profesjonalnej wykładzinie, na panelach jeździ wolniej, bo trochę się ślizga ale problemów większych nie ma, poniżej jest widoczny filmik: Proszę o rady co można zmienić od bardziej doświadczonych kolegów, oraz mam nadzieję, iż ten opis się komuś przyda.
  15. Witam chciałem opisać wam mój szybki i drobny projekt. Spektral - tak został nazwany mały robot do wyścigów na kategorię deathrace. 100x100x80mm oraz 300gr to ograniczenia które narzucił w tej kategorii organizator Robomaticonu 2019. Robot był składany dosłownie dzień przed wyścigami od 10 rano do 2 w nocy następnego dnia. Najmniejsza pomyłka albo uszkodzona część niestety uniemożliwiłaby mi start. Na szczęście części z botlandu i od chińczyka nie zawiodły^^ Robot był napędzany silnikiem 3000rpm z kołem o średnicy 37mm potrafił się naprawdę nieźle rozpędzić ale to było zdecydowanie za dużo. Miał napęd na jedno koło dla uproszczenia konstrukcji, zmniejszenia wagi oraz dla polepszenia zwrotności. Tylne koła mają dość sporą szerokość oraz dużą przyczepność obawiałem się że jak zamontuje je na jednej sztywnej osi auto będzie zbyt podsterowne. Co ciekawe Spektral był jednym startującym pojazdem z układem skrętnym. Niżej zostawiam spis części: Rama oraz felgi zostały wydrukowane na drukarce 3D materiałem PLA Łożyska użyte w 3 kołach Silnik pololu 3000rpm - polecam jednak zamiast niego zastosować 1000rpm Sterownik do silnika szczotkowego 20A (jest idealny, nie grzeje się i nie spalił silnika tylko lekko go podgrzał ) 4 kanałowy odbiornik Kosztuje około 25zł ma naprawdę ładny zasięg i waży tylko 2gr! Serwo 9gr tutaj nie ma co wiele pisać zwykłe serwo sterowane PWM, 180st. Step down obniża napięcia z lipo 2s na 6v To chyba tyle, do tego potrzebujecie garść śrubek M3 oraz jedną M2 do zamocowania cięgna serwa z mechanizmem skrętu. Jakby komuś się bardzo nudziło to radzę dać silnik 1000rpm, w zupełności wystarczy i kamerę FPV - świetna zabawa xD Jeżeli interesuje Cie moja działalność w internecie zapraszam na mojego facebooka wydrukuje oraz koło naukowe politechniki opolskiej SPEKTRUM
  16. Witam, przedstawiam mój pierwszy projekt DIY opisany na Forbocie. Robota nazwałem Maxi-Brzydal, ponieważ podwozie i nadwozie na którym wykonałem robota jest wizualnie zniszczone i nie wygląda ciekawie. Robot może być sterowany za pomocą Bluetooth i posiada funkcję omijania przeszkód, którą można włączyć i wyłączyć z poziomu aplikacji. Robot posiada sterowanie "jak czołg", regulację prędkości, sterowanie chwytakiem (trzema serwami), włącznik lamp i włącznik trybu omijania przeszkód. Panel sterowania zrobiłem w aplikacji Bluetooth Electronic od keuwl. Sama aplikacja nie potrzebuję żadnych dodatkowych bibliotek i wszystko odbywa się za pomocą komend monitora szeregowego, czyli Serial.read itd. MECHANIKA i ZASILANIE: Robot został zbudowany za starym podwoziu od jakiegoś pojazdu RC(Podwozie znalazłem w piwnicy bez baterii silników i z uszkodzoną elektroniką). Podwozie jest bardzo fajne, ponieważ posiada pompowane, gumowe opony, które świetnie sprawują się na powierzchniach wszystkich typów. Całość napędzają dwa silniki 12V, które są w stanie uciągnąć około 2-kilogramową konstrukcję. W przedniej części znajduję się chwytak sterowany za pomocą 3 serw TowerPro MG-995 wykonany z aluminium. Co do zasilania zwykłe "laptopówki" nie dawały wystarczająco mocy więc wybór padł na akumulator żelowy 12V 1.3Ah pozwalający wykorzystać potencjał silników w 100%. Taki akumulator kosztował mnie jakieś 20zł. ELEKTRONIKA: Mózgiem całego robota jest klon bardzo popularnego Arduino NANO z procesorem Atmega328 na pokładzie, który w zupełności wystarcza do tego typu robota. Kod zabiera jakieś 45% pamięci. Do komunikacji z telefonem lub komputerem wykorzystałem moduł BT o nazwie XM-15 . Moduł ten ma większy zasięg od popularnych HC-05 i HC-06, a jest tak samo łatwy w obsłudze. do sterowania silnikami posłużyłem się dwoma sterownikami VNH2SP30 osadzonymi na shildzie do Arduino UNO. Użyłem tych sterowników ponieważ silniki potrzebują aż 5A, a ten sterownik może dać 30A. Płytkę PCB wytrawiłem sam w domu rysując odpowiednie ścieżki i wyszła całkiem dobrze. Na płytce oprócz arduino, sterownika silników i modułu BT znajduję się kilka dodatkowych złączy zasilania, wyjścia do podłączenia serw oraz dość duży kondensator (chodzi o to, że przy maksymalnej mocy silników przy ruszaniu bez niego potrafiło zrestartować arduino i rozłączyć moduł BT, teraz jest Soft Start, ale kondensator został). Na czerwonym nadwoziu zamontowane są trzy czujniki ultradźwiękowe HC-SR04 do wykrywania przeszkód, dwie lampy LED 12V i mały woltomierz do sprawdzania naładowania akumulatora. Jest to mój pierwszy wpis na forum więc proszę o wyrozumiałość dla młodego, dopiero uczącego się, pasjonata robotyki i programowania. Liczę na konstruktywną krytykę od tych bardziej doświadczonych i wskazówki co mogę zrobić lepiej. Na wszystkie pytania postaram się odpowiedzieć. Pozdrawiam wszystkich czytelników, Technowsky!
  17. Witam, Chciałbym przedstawić zbudowany ostatnio pojazd inspekcyjny. Założenia były następujące: dobra mobilność w nierównym terenie, sterowanie za pomocą aplikacji na Android'a oraz podgląd z wbudowanej kamery w czasie rzeczywistym. Mechanika W pojeździe zastosowano uproszczoną, 4-kołową wersję zawieszenia rocker-bogie stosowaną m.in. w łazikach marsjańskich. Główną zaletą tego rozwiązania jest niemal równomierny nacisk wszystkich kół na podłoże oraz możliwość uniesienia koła na przeszkodzie. Zastosowane koła pochodzą od "jakiejś" zabawki (znalezione na strychu ) i są zamocowane adapterem hex 12 mm (z małymi przeróbkami, aby schować przekładnię wewnątrz koła). Każde koło posiada własny silnik DC z przekładnią - prędkość wyjściowa: ok. 120 obr/min przy zasilaniu 12V. Silniki zostały zamocowane do aluminiowego profilu kwadratowego 10 mm za pomocą opasek zaciskowych. Profil ten stanowi część wahacza przymocowanego do osi kadłuba przez łożyska z wiertarek (średnica wewnętrzna 6 mm). Z tyłu pojazdu widoczna jest belka różnicowa łącząca wahacze po obu stronach. Dzięki niej kadłub utrzymywany jest w swojej pozycji a wychylenie jednego wahacza powoduje odchylenie drugiego o taki sam kąt przeciwnie skierowany. Jako kadłub wykorzystano obudowę z ABS. Do jej wieczka przymocowano również maszt z kamerą sterowany w dwóch osiach. Elektronika Komputerem sterującym w pojeździe jest Raspberry Pi Zero W z systemem Raspbian w wersji Jessie. Zastosowano ten model z powodu małych rozmiarów, stosunkowo niskiej ceny i małego poboru mocy. Z racji braku przetwornika ADC, zastosowano również arduino w wersji Pro Mini o napięciu 3.3V (aby było zgodne ze standardem w Raspberry). Są również 2 sterowniki silników na bazie modułu L298N (Sterownik ten ma dwa kanały i można było zastosować tylko jeden sterownik, jednak z powodu niewystarczającej wydajności prądowej zastosowano dwa), 2 przetwornice step-down 5V (osobno dla logiki, i osobno dla serwomechanizmów), dwa serwomechanizmy TowerPro SG90, kamera, oraz pakiet 3S ogniw li-ion w rozmiarze 18650. Z racji tego, że kamera jest podłączana przez taśmę FFC, zastosowano również przejściówki z FFC na goldpin, aby nie uszkodzić taśmy podczas obracania kamerą. Oprogramowanie Arduino w tym pojeździe odpowiedzialne jest za odczyt aktualnego napięcia zasilania oraz generowanie sygnałów PWM dla prędkości silników oraz serwomechanizmów pozycjonowania kamery. To jaki sygnał ma być generowany, jest wiadome dzięki połączeniu z Raspberry poprzez UART. Dodatkową funkcją Arduino jest wyłączenie PWM dla silników w przypadku braku komunikacji z Raspberry co zapobiega niekontrolowanej jeździe pojazdu w przypadku np. zerwania zasięgu. Raspberry komunikuje się z użytkownikiem poprzez sieć WiFi (Malinka działa w trybie hot-spot'u). Program działający na Raspberry został napisany w Python'ie i wykorzystuje również biblioteki Flask, w celu utworzenia serwera. Odpowiednie fragmenty kodu są wykonywane po wywołaniu przypisanego do niego adresu. Do transmisji wideo wykorzystano platformę Gstreamer, która pozwala na strumieniowanie w formacie H.264. Dzięki temu udało się uzyskać płynny obraz przy 30 FPS i rozdzielczości 800x600 cechujący się niewielkim opóźnieniem (ok. 300 ms). Powstała również dedykowana aplikacja na system Android, gdzie widoczny jest podgląd z kamery oraz sterowanie pojazdem. Podsumowanie Powstały pojazd zgodnie z założeniami dobrze radzi sobie w terenie, jednak pozostawia również spore możliwości rozbudowy. Można np. dodać zewnętrzną antenę WiFi aby poprawić zasięg (obecnie jest to ok 50m na otwartej przestrzeni), diodę doświetlającą, czy też różne czujniki. Najprawdopodobniej następnym krokiem będzie dodanie przetwarzania obrazów, aby pojazd był w stanie podążać za danym obiektem i być może omijać przeszkody. Na koniec krótka prezentacja działania:
  18. Witam, chciałbym przedstawić swoją konstrukcję, a mianowicie robota mobilnego o nazwie Bluebot – od połączenia słów bluetooth oraz robot . Jak można się domyślić, konstrukcja jest sterowana przez bluetooth z poziomu aplikacji, co było głównym założeniem. Ponadto chciałem, aby robocik miał jak najbardziej estetyczny wygląd. Pozwolę sobie przejść teraz do szczegółowego opisu. Podwozie Podwozie (a właściwie korpus) zostało wydrukowane na drukarce 3d. Składa się z górnej części – głównej, oraz dolnej – pokrywki przymocowanej do pierwszej części za pomocą trzech wkrętów. Na pierwszej warstwie bardzo mocno są widoczne „paski” nanoszonego filamentu, ale zmniejszając temperaturę głowicy przy kolejnych wydrukach udało mi się trochę zmniejszyć widoczność tego defektu (ktoś ma jakieś pomysły co jeszcze można zrobić?). Zdjęcia pierwsze i drugie przedstawiają model korpusu, natomiast trzecie wspomnianą „wadę” wydruków. Warto jeszcze dopisać, że podwozie jest kołem o średnicy 100mm. Elektronika Elektronika robocika nie jest skomplikowana. „Sektor sterujący” składający się z arduino micro (akurat takie wykorzystałem, równie dobrze można było użyć każdego innego), sterownika silników L293D (nie zdecydowałem się na gotowy moduł TB6612 ze względu na oszczędność miejsca ) i modułu bluetooth HC-05 (zmieściła się jeszcze dioda LED, kondensator ceramiczny i rezystor) znajduje się na płytce PCB, którą sam wykonałem metodą termotransferu. Do narysowania projektu płytki wykorzystałem program Eagle, do trawienia użyłem wytrawiacza B327. Płytka cechuje się nietypowym kształtem widocznym na zdjęciu. Druga część elektroniki, a mianowicie stabilizator L7805 z parą kondensatorów, znajduje się w korpusie. Tam „ułożyłem” także wszystkie przewody, tak, że po zamknięciu obudowy żaden kabelek nie jest widoczny. Zastosowałem taki przełącznik suwakowy, który pełni rolę włącznika. Zastosowany akumulator to pakiet Dualsky 2S 7,4V 550mAh 45C, mieści się idealnie pomiędzy kołami, został przymocowany do podwozia za pomocą rzepu. Mechanika Zastosowałem znane silniczki Pololu z przekładnią 50:1. Jak już wcześniej wspomniałem, są sterowane przez układ L293D. Nie miałem jeszcze problemów z taką kombinacją. Mocowania Pololu, koła także Pololu – 40mm, czerwone (są wciskana na D-kształtny wał). Trzecim punktem podparcia jest dioda Led 10mm, nie świeci. W pierwotnej wersji miały być dwie – z przodu i z tyłu, ale jedna wystarczyła (robot jest delikatnie przechylony do przodu, poza tym dodając czwarty punkt podparcia jedno z kół mogłoby nie dotykać podłoża). Algorytm Sterowanie odbywa się z poziomu RoboRemo Free – aplikacji na urządzenia z systemem Android. Darmowa wersja pozwala na korzystanie z pięciu elementów jednocześnie. Działanie aplikacji zostało przystępnie wyjaśnione w tym filmie. Kod napisałem w Arduino IDE. Sterowanie pojazdem wygląda następująco: przesuwając lewy suwak zwiększa się prędkość lewego silnika, analogicznie prawy suwak. Fragment kodu (gdzie wartość ‘p’ to wartość suwaka, która jest wysyłana do arduino): if (cmd[0] == 'p') { unsigned int prawy = atof(cmd + 2); Serial.println(prawy); analogWrite(PWM_B, prawy);} Dodatkowo podczas przytrzymywania przycisku ‘LED’ zapala się żółta dioda na płytce PCB w robocie. Odpowiada za to ten fragment kodu: if (strcmp(cmd, "led 1")==0) { digitalWrite(ledPin, HIGH);} if (strcmp(cmd, "led 0")==0) { digitalWrite(ledPin, LOW);} Film To byłoby na tyle, dziękuję Wam za przeczytanie mojego opisu, zachęcam do zadawania pytań, na wszystkie postaram się odpowiedzieć .
  19. WSTĘP Mój projekt składa się z dwóch współpracujących układów działających dzięki mikrokontrolerowi Arduino Uno: pojazdu oraz pilota do sterowania. Pojazd posiada silnik DC napędzający wał z tylnymi kołami oraz serwomechanizm odpowiadający za skręcanie. Urządzenie jest wyposażone w akumulator li-pol zasilający mikrokontroler i silniki. Pilot powstał z użyciem modułu joysticka, który umożliwia analogowe sterowanie prędkością i kierunkiem jazdy pojazdu. Urządzenia komunikują się z wykorzystaniem podczerwieni, pilot wyposażony jest w nadajnik IR, a pojazd w odbiornik IR. MECHANIKA Mechaniczna konstrukcja pojazdu została wykonana z plastikowej butelki. Skrętne koło zostało wymontowane z starego krzesła obrotowego. Układ pilota znajduje się w obudowie z odpowiednio wyciętego kartonowego pudełka. ELEKTRONIKA Oba układy są zlutowane na uniwersalnych płytkach. Płytki zostały wyposażone w listwę goldpinów, które wtyka się w Arduino. Dzięki temu rozwiązaniu możemy wyciągnąć Arduino i użyć go w innym projekcie bez potrzeby niszczenia trwałych połączeń. Pojazd jest wyposażony w jednokanałowy sterownik silnika. Schemat układu pojazdu: Schemat układu pilota: Schematy połączeń stworzono z użyciem programu do projektowania i symulowania obwodów elektronicznych EasyEDA. OPROGRAMOWANIE Najważniejsze fragmenty kodu pilota: valueX = analogRead(joyX) / 100; valueY = analogRead(joyY) / 100; isPress = !digitalRead(button); data0 = String(isPress, HEX); data1 = String(valueX, HEX); data2 = String(valueY, HEX); if (isPress == 0) dataSend += "1"; else dataSend += "2"; dataSend += data1 + data2; for (int i = 0; i < 3; i++) { irsend.sendSony(StrToHex(dataSend.c_str()), 12); delay(40); } Program odczytuje pozycję X oraz Y joysticka oraz czy jest on wciśnięty. Następnie pilot wysyła komunikaty zapisane w systemie szesnastkowym. Są to trzy cyfry: pierwsza stan przycisku (0x1 nie wciśnięty, 0x2 wciśnięty), druga kąt ustawienia serwomechanizmu (od 0x0 do 0xA, 0x0 to ustawienie serwomechanizmu maksymalnie w lewo, 0x5 ustawienie serwomechanizmu prosto, 0xA ustawienie serwomechanizmu maksymalnie w prawo) i trzecia prędkość obrotów silnika (od 0x0 do 0xA, 0x0 to maksymalne obroty do tyłu, 0x5 silnik nie pracuje, 0xA maksymalne obroty do przodu). Przykładowy sygnał 0x171 oznacza serwomechanizm ustawiony delikatnie w prawo oraz prawie maksymalna prędkość w tył. Wszystkie dłuższe sygnały są ignorowane, dzięki czemu ograniczony zostaje wpływ zakłóceń na układ. Stan przycisku nie jest używany przez pojazd ale nic nie stoi na przeszkodzie, aby rozbudować projekt o nową funkcjonalność, jak na przykład zapalanie świateł czy sygnał dźwiękowy. Najważniejsze fragmenty kodu pojazdu: if (irrecv.decode(&results)) { IRcommand = String(results.value, HEX); if (IRcommand.length() == 3) { tmp = IRcommand.charAt(1); setTurn(hexChar2Int(tmp)); tmp = IRcommand.charAt(2); setSpeed(hexChar2Int(tmp)); } irrecv.resume(); } Program używa drugiego i trzeciego znaku do ustawienia kolejno odpowiedniej prędkości oraz kąta obrotu serwomechanizmu. Oba programy wykorzystują bibliotekę <IRremote.h> do komunikacji z użyciem podczerwieni. Dodatkowo program pojazdu korzysta z biblioteki <Servo.h>. Program użyty do napisania programowania mikrokontrolerów to Arduino Software.
  20. Cześć wszystkim, chciałbym zaprezentować Wam konstrukcję, która stała się tematem mojej pracy dyplomowej. Działanie: Pojazd opiera się o Raspberry Pi 2 (bo akurat ten model posiadam), a dalmierz wykorzystuje dedykowaną kamerę do Maliny. W projekcie chciałem skupić się na wykrywaniu przeszkód, w związku z czym pominąłem pewne kwestie techniczne, jak np. zasilanie z akumulatorów (robot musi być podłączony do zasilacza kablem) albo kwestie zdalnego sterowania (robot wykonuje z góry zdefiniowane instrukcje ruchu). Najważniejszym elementem tego projektu jest własnoręcznie wykonany dalmierz laserowy, który opisuję dokładniej poniżej. Podstawowym zadaniem robota jest skanowanie przestrzeni wokół niego. Wygenerowana mapa wygląda tak: Jest ona przedstawiona w formie tablicy dwuwymiarowej, w której każda komórka odpowiada kwadratowi o boku 10 cm. Zdecydowałem się na taki sposób zapisania mapy, ponieważ można ją łatwo przetworzyć algorytmami wykrywania drogi. Konstrukcja mechaniczna: Do budowy użyłem gotowego podwozia z dwoma kołami i kółkiem podpierającym z tyłu. Podwozie te wymusza poruszanie się pojazdu analogicznie do poruszania się czołgu, a ma to tę zaletę, że robot może obracać się w miejscu. Do gotowego podwozia przykręciłem drugą płytę, którą ręcznie wyciąłem z płyty MDF. Do tej płyty przymocowana jest cała elektronika wraz z serwomechanizmem, na którym znajduje się głowica dalmierza. Głowica ta również została ręcznie wykonana z płyty pilśniowej. Składają się na nią trzy płytki, z których jedna jest przeznaczona na umocowanie lasera, druga na umocowanie kamery, a trzecia tworzy podstawę i miejsce mocowania do serwomechanizmu. Dwie pierwsze płytki są ułożone względem siebie pod kątem 9,3°. Kąt ten wynika z niedoskonałości ręcznej obróbki materiału, docelowo miał on wynosić 15° (niestety nie dysponuję drukarką 3D). Jego zmniejszenie powoduje, że dalmierz może wykonywać pomiary w większym zakresie (nawet do kilku metrów), jednak pomiary te są mniej precyzyjne, ponieważ na jeden piksel przesunięcia punktu lasera przypada większa różnica odległości. Przestrzeń pomiędzy obydwoma płytami pojazdu ma wysokość około 4 cm i jest zajmowana jedynie przez wiązki przewodów. Przestrzeń ta jest tak duża aby można było, przy przyszłej rozbudowie projektu, umieścić tam 4 cylindryczne akumulatory Li-ion wraz z towarzyszącą im elektroniką. Elektronika: Jak napisałem wcześniej głównym elementem sterującym jest Raspberry Pi, jednak niezbędne są też odpowiednie układy np. do sterowania silnikami. W tym celu stworzyłemukład, który zawiera sterownik silników L293D oraz całą niezbędna elektronikę potrzebną do m.in. sterowania laserem czy serwomechanizmem. Układ ten służy też do rozdzielenia zasilania na wszystkie komponenty. W kwestii zasilania zdecydowałem się na użycie dwóch zasilaczy 5 V, gdzie jeden odpowiada za zasilanie Maliny, a drugi za zasilanie wszystkich silników. Przy jednym zasilaczu zakłócenia powodowane przez silniki były tak duże, że często powodowały resetowanie się Raspberry Pi. Dalmierz: Dalmierz zbudowałem na podstawie poradnika Forbota, w związku z czym pominę tutaj opis metody triangulacji i niezbędne wzory, ponieważ to wszystko możecie przeczytać w wymienionym artykule. Podstawowym problemem przy korzystaniu z tak skonstruowanego dalmierza jest wyznaczenie pozycji plamki lasera na obrazie z kamery. W projekcie zaimplementowałem dwa różne sposoby osiągnięcia tego celu. Pierwszym z nich jest znalezienie najjaśniejszego puntu na obrazie. Przy użyciu biblioteki OpenCV metoda ta jest bardzo łatwa w implementacji. Jednak ja użyłem trochę bardziej rozbudowanej metody używającej maski na obrazie. Maska jest obszarem achromatycznym (1-bitowym), który wyznacza obszar na obrazie, który powinien być wyłączony z obliczeń. W projekcie maskę użyto, aby wyszukiwać najjaśniejszy punkt jedynie w wąskim poziomym pasie obrazu. Innymi słowy, maska obrazu wyłącza z przetwarzania górną i dolną część klatki. Użyłem tej metody ze względu na to, że w trakcie testów często maksimum na obrazie było wykrywane nie w miejscu plamki lasera, a np. w miejscu odbicia światła dziennego od błyszczących przedmiotów w pomieszczeniu. Dzięki ograniczeniu interesującego nas obszaru obrazu udało się znacząco zminimalizować ten efekt. Wysokość tych obszarów dobrałem eksperymentalnie, wiedząc, że ze względu na konstrukcję dalmierza, plamka lasera w oku kamery zawsze „porusza się” w linii poziomej, na stałej wysokości. Wyszukiwanie najjaśniejszego punktu jest metodą bardzo łatwą w implementacji i dobrze sprawdzają się w praktyce. Ma ona jednak jedną poważną wadę. W momencie gdy odległość do przeszkody jest tak duża, że plamka lasera znajduje się poza kadrem, metoda ta w dalszym ciągu odnajdzie najjaśniejszy punkt na obrazie, który błędnie zostanie zinterpretowany jako światło lasera. Używając tej metody nie ma możliwości sprawdzenia czy punkt lasera znajduje się w kadrze. Z tego względu spróbowałem też drugiej metody. Procedura w tym przypadku składa się z kilku kroków: pobranie klatki obrazu przy wyłączonym laserze, włączenie lasera, pobranie drugiej klatki obrazu, odjęcie dwóch pobranych klatek od siebie, wyznaczenie obszaru, w którym obie klatki różnią się od siebie. Jak widać metoda ta już w swoich założeniach jest bardziej skomplikowana niż wcześniejsza. Poza trudniejszą implementacją programową, problemy sprawiają tu też kwestie mechaniczne. Aby odjęte od siebie obrazy w wyniku dały obszar oświetlony laserem cała reszta kadru musi być na obu zdjęciach taka sama. Wymaga to, aby kamera nie poruszała się w trakcie pomiaru. Z tego względu pomiar jest też dłuższy niż przy wyszukiwaniu najjaśniejszego punktu, ponieważ należy poczekać z pobraniem kolejnych klatek aż drgania spowodowane ruchem serwomechanizmu ustaną. W projekcie metoda ta została zaimplementowana w taki sposób: każda z pobranych klatek jest konwertowana na obraz czarno-biały, 8-bitowy, gdzie wartość 255 odpowiada bieli, a 0 odpowiada czerni. Wszystkie wartości pomiędzy to odcienie szarości. Następnie obrazy te są od siebie odejmowane, w wyniku czemu uzyskujemy trzeci czarno-biały obraz. Obraz różnicy jest wtedy konwertowany na mapę 1-bitową z ustaloną wartością progową, tj. wartością, powyżej której piksel jest uznawany za biały, a poniżej za czarny. Do wszystkich tych przekształceń obrazu używane są narzędzia z biblioteki OpenCV. Na tak przetworzonym obrazie są następnie wyszukiwane białe obszary. Obszarów tych zazwyczaj jest więcej niż jeden, często też widoczne są tu krawędzie różnych obiektów, które znajdowały się w kadrze, jednak krawędzie te mają zazwyczaj 1-2 px grubości. Są to niedoskonałości obrazu, których nie udało się wyeliminować nawet przez wyeliminowanie drgań kamery. Aby krawędzie te nie wpływały na wynik, z przetwarzania wyłączane są wszystkie obszary, których wysokość lub szerokość jest mniejsza niż 3 px. W wyniku tego na obrazie różnicy pozostaje obszar światła lasera oraz kilka mniejszych obszarów, które są rożnymi zakłóceniami. Aby ostatecznie znaleźć obszar plamki lasera, szukany jest największy biały obszar na obrazie różnicy i wyznaczany jest jego środek. Obliczony punkt jest pozycją plamki lasera na obrazie. Jak widać, metoda ta jest dużo bardziej skomplikowana i dużo trudniejsza w praktycznej implementacji niż szukanie najjaśniejszego punktu. Nie ma ona jednak wady poprzedniej metody, tj. gdy w kadrze nie znajduje się światło lasera, metoda ta w większości przypadków wykryje to. Wymaga to jednak małych zakłóceń na obrazie różnicy, gdzie po odjęciu zakłóceń do 3 pikseli otrzymamy pusty obraz. Taką sytuację należy zinterpretować jako pomiar poza zakresem.
  21. Projekt powstał jako rozwinięcie prostego jeździka solarnego ( dwa panele słoneczne, dwa silniki rama z dużego klocka ala duplo) który miał problem z pokonywaniem nierówności. Założenia Stworzenie budżetowej platformy jezdnej mogącej poruszać się swobodnie po typowym mieszkaniu - pokonywać przeszkody typu próg czy gruby dywan. Walidacją założenia będzie możliwość pokonywania przeszkód o wysokości około 1 cm. Układ mechaniczny Aby było możliwe pokonywanie nierówności, koła muszą być maksymalnie niezależne - amortyzowane. Próby [teoretyczne] opracowania zawieszenia trwały w sumie kilka miesięcy. Żaden z pomysłów nie był tani a jednocześnie obiecujący (prosty). Natomiast podczas spożywania ptysi z biedronki (francuskie ptysie nadziewane kremem o smaku waniliowym z sosem z czekoladowym) zauważyłem że pudełko jest wytrzymałe ale jego rogi da się dość elastycznie wyginać. Po nawierceniu 8 dziur i przykręceniu silników z kołami okazało się że efekt jest obiecujący. Elektronika Sercem układu jest Arduino Pro Mini 328 3V3 (3V3 bo docelowo będzie zasilane z starej baterii od telefonu) poza tym jest wystarczające. Silnikami steruje DRV8833 - pierwszy kanał na dwa silniki z prawej strony i drugi kanał na dwa silniki z lewej strony. UWAGA trzeba pamiętać o polach lutowniczych pozwalających ograniczyć prąd! Komunikacja odbywa się jednokierunkowo - z telefonu do arduino. Ja użyłem Bluetooth BLE HM-10 bo taki miałem ale może być jakiś inny. Program Jako bazy użyłem przykładu SerialEvent https://www.arduino.cc/en/Tutorial/SerialEvent Dodałem obsługę przycisku włączającego i wyłączającego pojazd (ruch pojazdu). Obsługa sterowania na podstawie akcelerometru: Oś y - prawo lewo ( 123 na wprost [110-136]; 250 max prawo; 0 max lewo) Oś z - speed (0-255) wsteczny max 140; przód max 255 - środek 195, bezład [ 200-230 ] /* akcelerometr: 1 - (x) 2 - (y) prawo lewo ( 123 na wprost [110-136]; 250 max prawo; 0 max lewo ) 3 - (z) speed (0-255) wsteczny max 140; przód max 255 - srodek 195, bezład [ 200-230 ] PWM 9 i 6 */ //PWM int BIN_1 = 9; int BIN_2 = 8; //PWM int AIN_1 = 6; int AIN_2 = 5; int MAX_PWM_VOLTAGE = 240; float MAX_PWM_VOLTAGE_FLOAT = 240.00; float PWM_VOLTAGE_PERCENT = MAX_PWM_VOLTAGE_FLOAT/100; float PWM_VOLTAGE_A; float PWM_VOLTAGE_B; String inputString = ""; // a String to hold incoming data bool stringComplete = false; // whether the string is complete String speed = ""; String direction = ""; int speed_int = 215; int direction_int = 123; int tmp_speed_int = 0; int tmp_direction_int = 0; bool remote = false; void setup() { // initialize serial: Serial.begin(9600); // reserve 200 bytes for the inputString: inputString.reserve(200); // initialize digital pin LED_BUILTIN as an output. pinMode(LED_BUILTIN, OUTPUT); digitalWrite(LED_BUILTIN, HIGH); delay(1000); digitalWrite(LED_BUILTIN, LOW); delay(1000); pinMode(BIN_1, OUTPUT); pinMode(BIN_2, OUTPUT); pinMode(AIN_1, OUTPUT); pinMode(AIN_2, OUTPUT); // delay just in case bluetooth module needs time to "get ready" //delay(100); //Serial.println("setup complete"); //--prevMillis = millis(); digitalWrite(BIN_2, LOW); digitalWrite(AIN_2, LOW); analogWrite(BIN_1, 0); analogWrite(AIN_1, 0); } void loop() { // print the string when a newline arrives: if (stringComplete) { if (inputString.substring(0,6) == "remote"){ if(remote == true){ remote = false; digitalWrite(LED_BUILTIN, LOW); } else { remote = true; digitalWrite(LED_BUILTIN, HIGH); } } if (inputString.substring(0,1) == "2"){ direction = inputString.substring(2); direction_int = direction.toInt(); } if (inputString.substring(0,1) == "3"){ speed = inputString.substring(2); speed_int = speed.toInt(); } ] if((remote == true) && (speed_int > 230)){ //26-50 pomnozone razy dwa daje ponad 50%, przy niższej mocy silniki nie pracują tmp_speed_int = speed_int-205; tmp_speed_int = tmp_speed_int*2; PWM_VOLTAGE_A = PWM_VOLTAGE_PERCENT*tmp_speed_int; PWM_VOLTAGE_B = PWM_VOLTAGE_PERCENT*tmp_speed_int; if((direction_int >= 110 ) && (direction_int <= 136 )){ digitalWrite(BIN_2, LOW); digitalWrite(AIN_2, LOW); } else if (direction_int < 110){ digitalWrite(BIN_2, LOW); digitalWrite(AIN_2, HIGH); } else if (direction_int > 136){ digitalWrite(BIN_2, HIGH); digitalWrite(AIN_2, LOW); } analogWrite(BIN_1, (int)PWM_VOLTAGE_B); analogWrite(AIN_1, (int)PWM_VOLTAGE_A); } else if((remote == true) && (speed_int < 200)){ //26-50 pomnozne razy dwa daje ponad 50%, przy niższej mocy silniki nie pracują tmp_speed_int = speed_int-230; tmp_speed_int = tmp_speed_int*-2; if((direction_int >= 110 ) && (direction_int <= 136 )){ PWM_VOLTAGE_A = PWM_VOLTAGE_PERCENT*tmp_speed_int; PWM_VOLTAGE_B = PWM_VOLTAGE_PERCENT*tmp_speed_int; } else if (direction_int < 110){ PWM_VOLTAGE_A = 0; PWM_VOLTAGE_B = PWM_VOLTAGE_PERCENT*tmp_speed_int; } else if (direction_int > 136){ PWM_VOLTAGE_A = PWM_VOLTAGE_PERCENT*tmp_speed_int; PWM_VOLTAGE_B = 0; } digitalWrite(BIN_2, HIGH); digitalWrite(AIN_2, HIGH); analogWrite(BIN_1, (int)PWM_VOLTAGE_B); analogWrite(AIN_1, (int)PWM_VOLTAGE_A); } else { digitalWrite(BIN_2, LOW); digitalWrite(AIN_2, LOW); analogWrite(BIN_1, 0); analogWrite(AIN_1, 0); } // clear the string: inputString = ""; stringComplete = false; } } /* Serial Event example When new serial data arrives, this sketch adds it to a String. When a newline is received, the loop prints the string and clears it. A good test for this is to try it with a GPS receiver that sends out NMEA 0183 sentences. NOTE: The serialEvent() feature is not available on the Leonardo, Micro, or other ATmega32U4 based boards. created 9 May 2011 by Tom Igoe This example code is in the public domain. http://www.arduino.cc/en/Tutorial/SerialEvent */ /* SerialEvent occurs whenever a new data comes in the hardware serial RX. This routine is run between each time loop() runs, so using delay inside loop can delay response. Multiple bytes of data may be available. */ void serialEvent() { while (Serial.available()) { // get the new byte: char inChar = (char)Serial.read(); //Serial.println(inChar); // add it to the inputString: inputString += inChar; // if the incoming character is a newline, set a flag so the main loop can // do something about it: if (inChar == '\n') { stringComplete = true; } } } Podsumowanie Pojazd pokonuje przeszkody o wysokości ponad 1 centymetra - więc cel został osiągnięty. Natomiast sterowanie nie jest optymalne - docelowo będę zmieniał na skręt realizowany jako obrót wokół własnej osi (naprzemienny kierunek silników z prawej i lewej strony). Do budowy platformy wykorzystano: 4 * silnik DC + koło 1 * sterownik silników DC DRV8833 1 * Arduino Pro Mini 328 3V3 1 * Bluetooth BLE HM-10 (może być każdy BT [slave]) 2 * koszyczek na cztery AA 8 * akumulator AA (najlepsze IMHO enleopy) 1 * Breadboard 170 1 * zestaw kabli 1 * śrubki z nakrętkami z LeroyMerlin 1 * pudełko po francuskich ptysiach nadziewanych kremem o smaku waniliowym z sosem z czekoladowym - na wszelki wypadek zaopatrzyłem się w 3 dodatkowe pudełka Sterowanie odbywa się z smartfonu przy użyciu aplikacji RoboRemoFree. Do programowania Arduino Pro Mini konieczne jest użycie konwertera USB do RS232TTL.
  22. Chciałbym przedstawić wam mój pierwszy projekt na Raspberry Pi – pojazd gąsiennicowy z kamerą i czujnikiem odległości sterowany przez przeglądarkę przy użyciu Bottle. Do tej pory używałem tylko Arduino i postanowiłem spróbować czegoś nowego. Przewagą pojazdu gąsienicowego nad kołowym jest możliwość poruszania się w trudnych warunkach. Podwozie jest amortyzowane co sprawia, że lepiej radzi sobie z nierównościami terenu. Robot bez problemu jeździ w ogrodzie i po śniegu. Podstawowe elementy wykorzystane do budowy robota: podwozie gąsiennicowe Raspberry Pi Zero W (ze względu na wbudowane wifi) sterownik silników L298N servo hat Sparkfun kamera do Raspberry Pi (z adapterem do Pi zero) przetwornica napięcia step down uchwyt do kamery + 2 serwomechanizmy czujnik ultradźwiękowy HC-SR 04 konwenter poziomów logicznych akumulator 7.4 V Budowa: Raspberry, sterownik silników, przetwornica i uchwyt kamery przykręcone są do postawy. Akumulator znajduje się pomiędzy podstawą a podwoziem. Raspberry i servo hat zasilane są kablem USB z przetwornicy podłączonym do hat’a. Sterownik silników i przetwornica podłączone są do akumulatora przez włącznik zasilania. Kamera i czujnik odległości są przymocowane do uchwytu z serwomechanizmami dzięki czemu można sterować ich położeniem. Czujnik odległości wysyła sygnał 5V, więc musi być podłączony do Raspberry przez konwenter stanów logicznych. Oprogramowanie: Do sterowania pojazdem przez przeglądarkę wykorzystałem projekt Bottle przy użyciu portu 8080. Obraz transmitowany jest przy użyciu pakietu Motion. Po wpisaniu w przeglądarkę: http://[ip_Raspberry]:8080/robocik/ pokazuje się panel do sterowania. Sterujemy robotem przy użyciu myszki wciskając odpowiednie przyciski. Możemy sterować kierunkiem jazdy, prędkością, ustawieniem uchwytu, a także włączyć i wyłączyć kamerę i Raspberry oraz zmierzyć odległość od przeszkody. Oprogramowanie składa się z dwóch części – programu w Pythonie i szablonu strony internetowej w html z rozszerzeniem .tpl. W szablonie możemy ustawić wygląd strony i funkcje przycisków, a program w Pythonie odpowiada za sterowanie GPIO. Pojazd_gasiennicowy_Raspberry_Bottle.zip
  23. Cześć, tym razem chciałbym się z Wami podzielić moim pierwszym jeżdżącym robotem - światłolubem. Wybrałem ten typ robota ze względu na jego prostą konstrukcję i łatwy w napisaniu program do sterowania. Do stworzenia podwozia postanowiłem wykorzystać płytę HDF. Jest ona lekka ale wystarczająco sztywna żeby utrzymać robota i co ważne łatwo dostępna i tania. Konstrukcja pod względem mechanicznym jest dosyć duża i niezgrabna, jednak było to zaplanowane. Na dosyć sporym kawałku HDFu zmieściłem 2 koszyki na baterie, jeden na 6 baterii AA, drugi na 2, dzięki czemu mam do dyspozycji 9 i 12V, Arduino Uno oraz płytkę stykową - dzięki takiemu rozwiązaniu mogę w łatwy sposób przerobić robota na innego. Koszyki na baterie przykleiłem do podwozia za pomocą kleju na ciepło, żeby nie mogły się one przemieszczać, Arduino przykręciłem za pomocą 4 długich śrub w taki sposób, że nie przylega ono do powierzchni płyty (jest to spowodowane sposobem montażu tylnego koła) a płytkę stykową przykleiłem na taśmie dwustronnej, która zabezpiecza jej spód. Silniki przymocowałem w taki sposób, jaki został przedstawiony w kursie budowy robotów. Do styków przylutowałem przewody, które następnie wyprowadziłem przez wcześniej nawiercone otwory. Tylne koło (obrotowe) zamocowałem za pomocą 4 śrub, niestety dysponowałem tylko jednym rodzajem więc mocno wystają one ponad powierzchnię podwozia. Kolejnym krokiem było stworzenie elektroniki, która będzie odpowiedzialna za ruch robota i znajdywanie najmocniejszego źródła światła. Do sterowania silnikami wykorzystałem sterownik silników L293D, który podłączyłem tak jak zostało to przedstawione w kursie programowania Arduino. W podwoziu zrobiłem 3 nacięcia (2 z przodu i 1 z tyłu, żeby robot mógł także cofać) do których włożyłem, a następnie zalałem klejem na ciepło fotorezystory 20-30k, które następnie podłączyłem do płytki stykowej i stworzyłem dzielnik z rezystorami 4k7 na których za pomocą pinów analogowych Arduino mierzony jest spadek napięcia. Dzielnik zasiliłem z wyjścia 5V Arduino. Na koniec zostało napisanie programu, który będzie wszystko koordynował. Było to najbardziej pracochłonne zajęcie i wymagało zdecydowanie najwięcej czasu. Program nie jest szczególnie skomplikowany, jednak żeby robot jeździł tak jak trzeba musiałem określić np. wartości różnicy między napięciami na fotorezystorach, żeby nie skręcał on w przypadku, gdy jeden z czujników jest minimalnie mocniej oświetlony. #define L_MOTOR_F 4 //definiowanie pinów #define L_MOTOR_B 5 #define L_MOTOR_PWM 6 #define R_MOTOR_F 2 #define R_MOTOR_B 7 #define R_MOTOR_PWM 3 #define L_O A4 #define R_O A5 #define B_O A3 #define ROZNICA_MIN -200 //okreslenie maksymalnej i minimalnej roznicy miedzy odczytanymi wartosciami napiecia #define ROZNICA_MAX 200 #define ROZNICA_PROG_G 10 //okreslenie roznicy, ktora nie bedzie powodowala skretu #define ROZNICA_PROG_D 10 void setup() { pinMode(L_MOTOR_F, OUTPUT); //deklaracja pinow na wyjscia pinMode(L_MOTOR_B, OUTPUT); pinMode(R_MOTOR_F, OUTPUT); pinMode(R_MOTOR_B, OUTPUT); pinMode(L_MOTOR_PWM, OUTPUT); pinMode(R_MOTOR_PWM, OUTPUT); } void loop() { int lewy=analogRead(L_O); //odczyt wartosci napiec z fotorezystorow int prawy=analogRead(R_O); int tyl=analogRead(B_O); int roznica=lewy-prawy; //obliczenie roznicy miedzy wartosciami przednich fotorezystorow int srednia=(lewy+prawy)/2; //obliczenie sredniej wartosci spadkow napiec na przednich fotorezystorach if (roznica < ROZNICA_MIN) roznica = ROZNICA_MIN; //ograniczenie roznicy else if (roznica > ROZNICA_MAX) roznica = ROZNICA_MAX; else if (roznica<=ROZNICA_PROG_G && roznica>=ROZNICA_PROG_D) //jezeli roznica jest mniejsza od progu, jedzie prosto roznica=0; if(srednia>tyl){ //jezeli natezenie swiatla z przodu jest wieksze niz z tylu - jazda do przodu int zmianaPredkosci = map(roznica, ROZNICA_MIN, ROZNICA_MAX, -40, 40); L_MOTOR(60-zmianaPredkosci); R_MOTOR(60+zmianaPredkosci); } else{ //w przeciwnym wypadku jazda w tyl L_MOTOR(-60); R_MOTOR(-60); } } void L_MOTOR(int V){ //funkcja sterujaca predkoscia lewego silnika if(V>0){ V=map(V,0,100,0,255); digitalWrite(L_MOTOR_F,HIGH); digitalWrite(L_MOTOR_B,LOW); analogWrite(L_MOTOR_PWM,V); } else{ V=abs(V); V=map(V,0,100,0,255); digitalWrite(L_MOTOR_B,HIGH); digitalWrite(L_MOTOR_F,LOW); analogWrite(L_MOTOR_PWM,V); } } void R_MOTOR(int V){ //funkcja sterujaca predkoscia prawego silnika if(V>0){ V=map(V,0,100,0,255); digitalWrite(R_MOTOR_F,HIGH); digitalWrite(R_MOTOR_B,LOW); analogWrite(R_MOTOR_PWM,V); } else{ V=abs(V); V=map(V,0,100,0,255); digitalWrite(R_MOTOR_B,HIGH); digitalWrite(R_MOTOR_F,LOW); analogWrite(R_MOTOR_PWM,V); } } void STOP(){ //zatrzymanie silnikow analogWrite(R_MOTOR_PWM,0); analogWrite(L_MOTOR_PWM,0); } W ten sposób robot został ukończony. Projekt ten bez wątpienia dużo mnie nauczył. Może nie ze strony elektroniki, ale na pewno ze strony programowania. I co najważniejsze miałem dużo zabawy tworząc go Pozdrawiam ~Usohaki
  24. 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.
  25. Wstęp Globalizacja światowej gospodarki oraz gwałtowny rozwój techniczny w XX wieku spowodował bardzo duże udoskonalenie metod produkcji, gdzie przemysłowe innowacje pozwoliłyby na produkcję wysokiej jakości dóbr przy dążeniu do jak najniższej ceny wyrobu końcowego. Jednym z owoców tego postępu jest CNC - computerized numerical control / komputerowe sterowanie urządzeń numerycznych, opracowane w MIT w USA w roku 1949. Podstawową różnicą między obróbką ręczną a CNC jest zastąpienie pracownika sterującego maszyną, komputerem, który z wykorzystaniem czujników (indukcyjne liniały pomiarowe, przełączniki krańcowe, elementy optoelektroniczne) i urządzeń wykonawczych (silniki krokowe / serwomechanizmy / serwonapędy) będzie na bieżąco kontrolował przemieszczanie narzędzia (na przykład frezu, noża tokarskiego czy palnika plazmowego/laserowego) zgodnie z przygotowanym wcześniej na podstawie rysunku technicznego zestawem instrukcji sterujących, pozwalając, w porównaniu do metod konwencjonalnych, na uzyskanie większej wydajności, wyższej dokładności i powtarzalności przy mniejszej ilości pracowników (park maszynowy składający się z kilku obrabiarek może obsługiwać jeden programista) oraz redukcji kosztów. Założenia projektu i dane techniczne Celem projektu było zbudowanie sterowanego numerycznie plotera rysującego, który: jako końcówkę wykonawczą wykorzystywałby długopis o średnicy 10mm z możliwością wykorzystywania wkładów o różnych kolorach; w osiach X i Y, napędzanych przez silniki krokowe, posiadał możliwość przesuwu o 120mm; w osi Z, gdzie długopis poruszany przez serwomechanizm modelarski stanowiłby jednocześnie prowadnicę, posiadałby skok kilku milimetrów; wykorzystywałby w osiach X i Y innowacyjne, bezsmarowne moduły liniowe firmy Igus® DryLin®; byłby niski dzięki konstrukcji opartej o stół krzyżowy z wysięgnikiem; był względnie dokładny, z ostateczną dokładnością pozycjonowania na poziomie +/- 0,5mm. Dane techniczne: konstrukcja stołu krzyżowego z wysięgnikiem, oparta o moduły liniowe wykorzystujące prowadnice o średnicy 10mm i śrubie trapezowej o średnicy 10mm i skoku 2mm; moduły liniowe napędzane silnikami krokowymi NEMA16; śruby modułów liniowych połączone z osiami silników krokowych poprzez sprzęgła elastyczne; pole robocze – kwadrat o boku 120mm; uproszczona budowa osi Z - długopis bez możliwości precyzyjnego przesunięcia – podniesienie / opuszczenie dzięki zastosowaniu serwomechanizmu i gumki recepturki; możliwość zerowania (homing) dzięki wykorzystaniu przełączników krańcowych; własnoręcznie wykonany sterownik oparty o Arduino Nano i open-source’owe oprogramowanie GRBL; całość zasilana zasilaczem 12V o maksymalnej wydajności prądowej 4A. Mechanika i oprogramowanie Mechanika maszyny sterowanej numerycznie, poza koniecznością stosowania napędów, w których możliwe będzie zadawanie ściśle określonego przesunięcia, nie różni się wiele od tej stosowanej w konwencjonalnych maszynach. Pierwszym krokiem w przypadku „Pioneer’a” było wybranie modułów liniowych (SLW o maksymalnej długości przejazdu wózka 150mm) firmy Igus®, które pozwalają zamienić ruch obrotowy pokrętła na ruch liniowy wózka dzięki śrubie trapezowej i odpowiedniej nakrętki, zamocowanej wewnątrz wózka. Standardowo nakrętki są wykonywane ze stali nierdzewnej bądź brązu, dla których koniecznością jest smarowanie, ponadto są stosunkowo ciężkie i z biegiem czasu zużywają się. Igus® opracował innowacyjne tworzywo sztuczne iglidur®, które jest doskonałe dla nakrętek, łożysk ślizgowych i panewek pracujących przy małych i średnich obciążeniach dzięki eliminacji wspomnianych wad. Dwa takie moduły zostały ze sobą połączone w stół krzyżowy za pomocą aluminiowego elementu z blachy o grubości 1,5mm – najpierw wycięto odpowiedni prostokąt na gilotynie, następnie otwory nawiercano wiertarką kolumnową, korzystając z rysunku technicznego stworzonego w programie Solid Edge. Drugim programem CAD wykorzystanym do budowy plotera, był Google SketchUp, w którym zaprojektowane zostały wszystkie elementy drukowane później na drukarce 3D W niskobudżetowych konstrukcjach CNC stosuje się silniki krokowe, ze względu na to, że jest to najtańsza w porównaniu do prostoty sterowania opcja napędu, który pozwalałby na precyzyjne zadawanie kąta, o jaki obrócić ma się wał – wykorzystane silniki NEMA16 firmy MicroStep pobierają prąd rzędu 800mA i posiadają rozdzielczość 200 kroków na obrót – zatem pojedynczy impuls obraca wał o 1,8° (teoretycznie zatem, jeżeli skok śruby wynosi 2mm, to dokładność pozycjonowania wózka wyniesie 0,01mm). Osie silnika krokowego (średnica 5mm) i śrub (średnica 10mm) połączone są aluminiowymi sprzęgłami elastycznymi. Wykorzystane sterowniki silników krokowych to A4988 – aby wysterować silnik krokowy, potrzebne są dwa piny – DIR (kierunek obrotów) i STEP (ilość impulsów podanych na to złącze jest równa ilości kroków, o jakie przesunie się wał silnika). Pozwalają one również na tak zwane mikrokroki – w zamian za mniejszy moment obrotowy pozwalają na nawet 16-krotne zwiększenie rozdzielczości, jednak ze względu na bardzo małą odległość przesuwu wózka w osi X bądź Y po podaniu jednego impulsu na wejście STEP (wspomniane 0,01mm) nie wykorzystano tej opcji w tym projekcie (na zdjęciu sterownik wraz z radiatorem). W początkach CNC wysterowaniem step-stick’ów zajmował się bezpośrednio komputer PC, gdzie do pinów portu LPT podłączone były piny sterowników silników krokowych – nie jest to dobre rozwiązanie, gdyż komputer PC musi jednocześnie obsłużyć wiele procesów, a zatem mogą pojawić się błędy w rysowaniu krzywych (wówczas ruch osi X oraz Y musi ze sobą korelować). Wady te można wyeliminować poprzez zainstalowanie systemu operacyjnego czasu rzeczywistego, na przykład LinuxCNC, jednak w zastosowaniach niskobudżetowych optymalne jest wykorzystanie Arduino – oprogramowanie sterujące (GRBL) przesyła paczkami poprzez USB instrukcje do niego właśnie, a ono, pełniąc funkcję buforującą, wysterowuje silniki w sposób ciągły. GRBL to open-source’owe darmowe oprogramowanie rozwijane przez grupę hobbystów z całego świata, oferujące gotowy do wgrania wsad dla płytek Arduino oraz gotowy program sterujący, dostępny dla systemów Windows, Linux i Mac OS, GRBL Controller, który pozwala na zmianę ustawień sterownika, ręczny przesuw końcówki roboczej, wyzerowanie maszyny (jeżeli takowa posiada taką opcję („Pioneer” posiada), na przykład w postaci czujników krańcowych – po włączeniu plotera program nie wie, gdzie znajduje się końcówka wykonawcza, musi więc przesuwać wózki od poszczególnych osi tak długo, aż zareagują na ich obecność krańcówki – to sygnał, że wózek dojechał do początku osi), podgląd pliku G-Code oraz rzecz jasna przesłanie go do sterownika. G-Code dla prostych kształtów napisać można samodzielnie, bądź poprzez program graficzny (stosuję Inkscape z wtyczką MIGRBL – najpierw z grafiki rastrowej program pozyskuje kontur, który jest wektoryzowany). Następnie wtyczka zbiór taki konwertuje do listy punktów i zapisuje w postaci tekstowego pliku o rozszerzeniu .gcode Do mocowania kartki papieru (format A4) wykorzystano taśmę malarską – sprawdziła się w tej roli bardzo dobrze, kartka jest zamocowana pewnie i nie przesuwa się, a po zakończonym rysowaniu łatwo zdjąć ją ze stołu. Program GRBL jest przeznaczony przede wszystkim dla frezarek CNC, ale nie stanowi większego problemu wykorzystanie jako końcówki roboczej, zamiast wrzeciona ze zamontowanym frezem, długopisu. Przed pierwszym użyciem należy go skonfigurować, wprowadzając ustawienia, które są indywidualne dla każdej konstrukcji. Schemat Kilka zdjęć i rysunków Robot przeszedł lifting związany z zastąpieniem ciężkiej podstawy ze sklejki płytą ze spienionego PCV Podsumowanie Jestem bardzo zadowolony z konstrukcji, mimo niewielkich prędkości stanowi świetny model edukacyjny, doceniony został na I Edycji Konkursu "Elektronika - by żyło się łatwiej" - III miejsce oraz Konkursie Innowacji Technicznych w roku szkolnym 2017/18 - I miejsce na etapie rejonowym i wyróżnienie na ogólnopolskim Pozdrawiam, wn2001
×
×
  • Utwórz nowe...