Spisu treści:

Samobalansujący robot firmy Magicbit: 6 kroków
Samobalansujący robot firmy Magicbit: 6 kroków

Wideo: Samobalansujący robot firmy Magicbit: 6 kroków

Wideo: Samobalansujący robot firmy Magicbit: 6 kroków
Wideo: Diy self balancing robot arduino based. 2024, Lipiec
Anonim

Ten samouczek pokazuje, jak zrobić samobalansującego robota za pomocą płytki deweloperskiej Magicbit. Używamy magicbit jako płytki rozwojowej w tym projekcie, który jest oparty na ESP32. Dlatego w tym projekcie można zastosować dowolną płytkę rozwojową ESP32.

Kieszonkowe dzieci:

  • magicbit
  • Sterownik silnika z podwójnym mostkiem H L298
  • Regulator liniowy (7805)
  • Akumulator Lipo 7,4 V 700 mAh
  • Inercyjna jednostka pomiarowa (IMU) (6 stopni swobody)
  • motoreduktory 3V-6V DC

Krok 1: Historia

Fabuła
Fabuła
Fabuła
Fabuła

Hej, dzisiaj w tym samouczku nauczymy się trochę skomplikowanej rzeczy. Chodzi o samobalansującego robota używającego Magicbit z Arduino IDE. Więc zacznijmy.

Przede wszystkim spójrzmy, czym jest samobalansujący robot. Robot samobalansujący jest robotem dwukołowym. Cechą szczególną jest to, że robot może się balansować bez użycia zewnętrznego wsparcia. Gdy zasilanie jest włączone, robot wstaje, a następnie stale balansuje za pomocą ruchów oscylacyjnych. Więc teraz masz już jakieś ogólne pojęcie o samobalansującym robocie.

Krok 2: Teoria i metodologia

Teoria i metodologia
Teoria i metodologia

Aby zrównoważyć robota, najpierw otrzymujemy dane z jakiegoś czujnika, aby zmierzyć kąt robota do płaszczyzny pionowej. Do tego celu wykorzystaliśmy MPU6050. Po uzyskaniu danych z czujnika obliczamy nachylenie do płaszczyzny pionowej. Jeśli robot jest w pozycji prostej i zrównoważonej, kąt pochylenia wynosi zero. Jeśli nie, to kąt nachylenia ma wartość dodatnią lub ujemną. Jeśli robot jest przechylony do przodu, robot powinien przesunąć się do przodu. Również jeśli robot jest przechylony do tyłu, robot powinien poruszać się w odwrotnym kierunku. Jeśli ten kąt nachylenia jest duży, szybkość reakcji powinna być wysoka. I odwrotnie, kąt pochylenia jest niski, więc szybkość reakcji powinna być niska. Do sterowania tym procesem posłużyliśmy się specyficznym twierdzeniem zwanym PID. PID to system sterowania, który służył do sterowania wieloma procesami. PID oznacza 3 procesy.

  • P-proporcjonalny
  • Ja-całka
  • D-pochodna

Każdy system ma wejście i wyjście. W ten sam sposób ten system sterowania również ma pewne dane wejściowe. W tym systemie sterowania jest to odchylenie od stanu stabilnego. Nazwaliśmy to błędem. W naszym robocie błędem jest kąt nachylenia od płaszczyzny pionowej. Jeśli robot jest wyważony, kąt pochylenia wynosi zero. Więc wartość błędu będzie wynosić zero. Dlatego wyjście systemu PID wynosi zero. System ten obejmuje trzy oddzielne procesy matematyczne.

Pierwszym z nich jest pomnożenie błędu od wzmocnienia numerycznego. Ten zysk jest zwykle nazywany Kp

P=błąd*Kp

Drugi polega na wygenerowaniu całki błędu w dziedzinie czasu i pomnożeniu jej z pewnego zysku. Ten zysk zwany Ki

I= Całka (błąd)*Ki

Trzecia to pochodna błędu w dziedzinie czasu i pomnożenie go przez pewną wielkość zysku. Ten zysk nazywa się Kd

D=(d(błąd)/dt)*kd

Po dodaniu powyższych operacji otrzymujemy ostateczny wynik

WYJŚCIE=P+I+D

Dzięki części P robot może uzyskać stabilną pozycję, która jest proporcjonalna do odchylenia. I część oblicza wykres obszaru błędu w funkcji czasu. Dlatego stara się zawsze dokładnie ustawić robota w stabilnej pozycji. Część D mierzy nachylenie wykresu w czasie w funkcji błędu. Jeśli błąd się zwiększa, ta wartość jest dodatnia. Jeśli błąd się zmniejsza, ta wartość jest ujemna. Z tego powodu, gdy robot znajdzie się w stabilnej pozycji, szybkość reakcji zostanie zmniejszona, co pomoże usunąć niepotrzebne przeregulowania. Możesz dowiedzieć się więcej o teorii PID z tego linku pokazanego poniżej.

www.arrow.com/en/research-and-events/articles/pid-controller-basics-and-tutorial-pid-implementation-in-arduino

Wyjście funkcji PID jest ograniczone do zakresu 0-255 (8-bitowa rozdzielczość PWM) i będzie podawane do silników jako sygnał PWM.

Krok 3: Konfiguracja sprzętu

Konfiguracja sprzętu
Konfiguracja sprzętu

Teraz to jest część konfiguracji sprzętu. Projekt robota zależy od Ciebie. Projektując korpus robota należy uwzględnić jego symetrię względem osi pionowej leżącej w osi silnika. Akumulator znajduje się poniżej. Dzięki temu robot jest łatwy do wyważenia. W naszym projekcie płytkę Magicbit mocujemy pionowo do korpusu. Użyliśmy dwóch motoreduktorów 12V. Ale możesz użyć dowolnego rodzaju motoreduktorów. to zależy od wymiarów twojego robota.

Kiedy mówimy o układzie, jest zasilany z baterii Lipo 7.4V. Magicbit używał 5V do zasilania. Dlatego zastosowaliśmy regulator 7805 do regulacji napięcia akumulatora do 5V. W późniejszych wersjach Magicbit ten regulator nie jest potrzebny. Bo to zasilanie do 12V. Dostarczamy bezpośrednio 7.4V do sterownika silnika.

Połącz wszystkie komponenty zgodnie z poniższym schematem.

Krok 4: Konfiguracja oprogramowania

W kodzie użyliśmy biblioteki PID do obliczenia wyjścia PID.

Przejdź do poniższego linku, aby go pobrać.

www.arduinolibraries.info/libraries/pid

Pobierz najnowszą wersję.

Aby uzyskać lepsze odczyty czujników, wykorzystaliśmy bibliotekę DMP. DMP oznacza cyfrowy proces ruchu. Jest to wbudowana funkcja MPU6050. Ten chip ma zintegrowaną jednostkę przetwarzania ruchu. Więc to wymaga czytania i analizy. Następnie generuje bezszumowe, dokładne dane wyjściowe do mikrokontrolera (w tym przypadku Magicbit(ESP32)). Ale jest wiele prac po stronie mikrokontrolera, aby wziąć te odczyty i obliczyć kąt. A więc po prostu użyliśmy biblioteki MPU6050 DMP. Pobierz go, przechodząc do poniższego linku.

github.com/ElectronicCats/mpu6050

Aby zainstalować biblioteki, w menu Arduino przejdź do narzędzi->załącz bibliotekę->dodaj bibliotekę.zip i wybierz plik biblioteki, który pobrałeś.

W kodzie musisz poprawnie zmienić zadany kąt. Wartości stałych PID różnią się w zależności od robota. Więc dostrajając to, najpierw ustaw wartości Ki i Kd na zero, a następnie zwiększaj Kp, aż uzyskasz lepszą szybkość reakcji. Więcej Kp powoduje więcej przeregulowań. Następnie zwiększ wartość Kd. Zwiększaj go zawsze w bardzo małej ilości. Ta wartość jest ogólnie niska niż inne wartości. Teraz zwiększaj Ki, aż uzyskasz bardzo dobrą stabilność.

Wybierz odpowiedni port COM i wpisz kartę. prześlij kod. Teraz możesz bawić się swoim robotem DIY.

Krok 5: Schematy

Schematy
Schematy

Krok 6: Kod

#włączać

#include "I2Cdev.h" #include "MPU6050_6Axis_MotionApps20.h" #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE #include "Wire.h" #endif MPU6050 mpu; bool dmpReady = fałsz; // ustaw true jeśli inicjowanie DMP powiodło się uint8_t mpuIntStatus; // przechowuje aktualny bajt stanu przerwania z MPU uint8_t devStatus; // zwróć status po każdej operacji urządzenia (0 = sukces, !0 = błąd) uint16_t packetSize; // oczekiwany rozmiar pakietu DMP (domyślnie 42 bajty) uint16_t fifoCount; // liczba wszystkich bajtów znajdujących się aktualnie w FIFO uint8_t fifoBuffer[64]; // bufor pamięci FIFO Quaternion q; // [w, x, y, z] kontener kwaternionowy VectorFloat grawitacja; // [x, y, z] wektor grawitacji float ypr[3]; // [odchylenie, pochylenie, przechylenie] kontener odchylenie/pochylenie/przechylenie i wektor grawitacji double originalSetpoint = 172,5; podwójna nastawa = oryginalna nastawa; podwójne PrzesunięcieAngleOffset = 0,1; podwójne wejście, wyjście; int stan ruchu=0; double Kp = 23;//set P pierwszy double Kd = 0.8;//ta wartość generalnie mała double Ki = 300;//ta wartość powinna być wysoka dla lepszej stabilności PID pid(&input, &output, &setpoint, Kp, Ki, Kd, DIRECT);//pid inicjalizuj int motL1=26;//4 piny do napędu silnikowego int motL2=2; int moR1=27; int moR2=4; volatile bool mpuInterrupt = false; // wskazuje, czy pin przerwania MPU osiągnął stan wysoki void dmpDataReady() { mpuInterrupt = true; } void setup() { ledcSetup(0, 20000, 8);//pwm setup ledcSetup(1, 20000, 8); ledcSetup(2, 20000, 8); ledcSetup(3, 20000, 8); ledcAttachPin(motL1, 0);//tryb pin silników ledcAttachPin(motL2, 1); ledcAttachPin(motR1, 2); ledcAttachPin(motR2, 3); // dołącz do magistrali I2C (biblioteka I2Cdev nie robi tego automatycznie) #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE Wire.begin(); Wire.setClock(400000); // Zegar I2C 400kHz. Skomentuj tę linię, jeśli masz problemy z kompilacją #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE Fastwire::setup(400, true); #endif Serial.println(F("Inicjowanie urządzeń I2C…")); pinMode(14, WEJŚCIE); // zainicjuj komunikację szeregową // (wybrano 115200, ponieważ jest to wymagane dla wyjścia demonstracyjnego Teapot, // ale zależy to od ciebie // w zależności od twojego projektu) Serial.begin(9600); podczas (!Serial); // czekaj na wyliczenie Leonardo, inni kontynuują natychmiast // zainicjuj urządzenie Serial.println(F("Inicjowanie urządzeń I2C…")); mpu.initialize(); // sprawdź połączenie Serial.println(F("Testowanie połączeń urządzeń…")); Serial.println(mpu.testConnection() ? F("Połączenie MPU6050 powiodło się"): F("Połączenie MPU6050 nie powiodło się")); // załaduj i skonfiguruj DMP Serial.println(F("Inicjowanie DMP…")); devStatus = mpu.dmpInitialize(); // podaj tutaj swoje własne przesunięcia żyroskopu, przeskalowane dla minimalnej czułości mpu.setXGyroOffset(220); mpu.setYGyroOffset(76); mpu.setZGyroOffset(-85); mpu.setZAccelOffset(1788); // ustawienie fabryczne 1688 dla mojego testowego układu // upewnij się, że działa (zwraca 0, jeśli tak) if (devStatus == 0) { // włącz DMP, teraz, gdy jest gotowy Serial.println(F("Włączanie DMP… ")); mpu.setDMPEwłączony(prawda); // włącz wykrywanie przerwań Arduino Serial.println(F("Włączanie wykrywania przerwań (zewnętrzne przerwanie Arduino 0)…")); attachInterrupt(14, dmpDataReady, ROŚNIE); mpuIntStatus = mpu.getIntStatus(); // ustaw naszą flagę DMP Ready, aby funkcja main loop() wiedziała, że można jej użyć Serial.println(F("DMP gotowy! Czekam na pierwsze przerwanie…")); dmpReady = prawda; // uzyskaj oczekiwany rozmiar pakietu DMP do późniejszego porównania packSize = mpu.dmpGetFIFOPacketSize(); //ustaw PID pid. SetMode(AUTOMATYCZNY); pid. SetSampleTime(10); pid. SetOutputLimits(-255, 255); } else { // BŁĄD! // 1 = ładowanie pamięci początkowej nie powiodło się // 2 = aktualizacja konfiguracji DMP nie powiodła się // (jeśli ma się zepsuć, zwykle kod to 1) Serial.print(F("Inicjalizacja DMP nie powiodła się (kod")); Serial. print(devStatus); Serial.println(F(")")); } } void loop() { // jeśli programowanie nie powiodło się, nie próbuj nic robić if (!dmpReady) return; // czekaj na przerwanie MPU lub dodatkowe pakiety dostępne while (!mpuInterrupt && fifoCount < pakietRozmiar) { pid. Compute();//ten okres czasu jest używany do ładowania danych, więc możesz użyć tego do innych obliczeń motorSpeed(wyjście); } // zresetuj flagę przerwania i uzyskaj INT_STATUS byte mpuInterrupt = false; mpuIntStatus = mpu.getIntStatus(); // pobierz aktualną liczbę FIFO fifoCount = mpu.getFIFOCount(); // sprawdź, czy nie ma przepełnienia (to nie powinno się zdarzyć, chyba że nasz kod jest zbyt nieefektywny) if ((mpuIntStatus & 0x10) || fifoCount == 1024) { // zresetuj, abyśmy mogli kontynuować czysto mpu.resetFIFO(); Serial.println(F("Przepełnienie FIFO!")); // w przeciwnym razie sprawdź przerwanie gotowości danych DMP (powinno się to zdarzać często) } else if (mpuIntStatus & 0x02) { // czekaj na poprawną dostępną długość danych, powinno być BARDZO krótkie oczekiwanie (dostępny pakiet fifoCount 1 // (to pozwala nam natychmiast przeczytać więcej bez czekania na przerwanie) fifoCount -= wielkość pakietu; mpu.dmpGetQuaternion(&q, fifoBuffer);mpu.dmpGetGravity(&gravity, &q);mpu.dmpGetYawPitchRoll(ypr, &q, &gravity); #if LOG_INPUT Serial. print("ypr\t"); Serial.print(ypr[0] * 180/M_PI);//kąty eulera Serial.print("\t"); Serial.print(ypr[1] * 180/M_PI); Serial.print("\t"); Serial.println(ypr[2] * 180/M_PI); #endif input = ypr[1] * 180/M_PI + 180; } } void motorSpeed(int PWM){ float L1, L2, R1, R2; if(PWM>=0){//kierunek do przodu L2=0; L1=abs(PWM); R2=0; R1=abs(PWM); if(L1>=255){ L1=R1=255; } } else {//kierunek wstecz L1=0; L2=abs(PWM); R1=0; R2=abs(PWM); if(L2>=255){ L2=R2=255; } } //ledcWrite(0, L1) napędu silnikowego; ledcWrite(1, L2); ledcWrite(2, R1*0.97);//0.97 to fakt prędkości lub, ponieważ prawy silnik ma większą prędkość niż lewy, więc zmniejszamy ją, aż prędkości silnika będą równe ledcWrite(3, R2*0.97);

}

Zalecana: