Inhaltsverzeichnis:

Selbstausgleichender Roboter von Magicbit - Gunook
Selbstausgleichender Roboter von Magicbit - Gunook

Video: Selbstausgleichender Roboter von Magicbit - Gunook

Video: Selbstausgleichender Roboter von Magicbit - Gunook
Video: Self Balancing Robot from Magicbit 2024, Juli
Anonim

Dieses Tutorial zeigt, wie man einen selbstbalancierenden Roboter mit dem Magicbit-Entwicklungsboard erstellt. In diesem auf ESP32 basierenden Projekt verwenden wir magicbit als Entwicklungsboard. Daher kann jedes ESP32-Entwicklungsboard in diesem Projekt verwendet werden.

Lieferungen:

  • Zauberbit
  • Dualer H-Brücken-L298-Motortreiber
  • Linearregler (7805)
  • Lipo 7.4V 700mAh Akku
  • Trägheitsmesseinheit (IMU) (6 Freiheitsgrade)
  • Getriebemotoren 3V-6V DC

Schritt 1: Geschichte

Geschichte
Geschichte
Geschichte
Geschichte

Hey Leute, heute werden wir in diesem Tutorial etwas über etwas komplexes lernen. Das ist über einen selbstausgleichenden Roboter mit Magicbit mit Arduino IDE. Also lass uns anfangen.

Schauen wir uns zunächst an, was ein selbstausgleichender Roboter ist. Der selbstausgleichende Roboter ist ein zweirädriger Roboter. Die Besonderheit besteht darin, dass sich der Roboter selbst ohne externe Unterstützung ausbalancieren kann. Wenn die Stromversorgung eingeschaltet ist, steht der Roboter auf und balanciert dann kontinuierlich durch Oszillationsbewegungen. Jetzt haben Sie also nur eine grobe Vorstellung von einem selbstausgleichenden Roboter.

Schritt 2: Theorie und Methodik

Theorie und Methodik
Theorie und Methodik

Um den Roboter auszubalancieren, erhalten wir zuerst Daten von einem Sensor, um den Roboterwinkel zur vertikalen Ebene zu messen. Zu diesem Zweck haben wir MPU6050 verwendet. Nachdem wir die Daten vom Sensor erhalten haben, berechnen wir die Neigung zur vertikalen Ebene. Wenn der Roboter gerade und ausbalanciert ist, ist der Neigungswinkel Null. Wenn nicht, ist der Neigungswinkel ein positiver oder negativer Wert. Wenn der Roboter nach vorne geneigt ist, sollte sich der Roboter nach vorne bewegen. Auch wenn der Roboter zur Rückwärtsseite geneigt ist, sollte sich der Roboter in die Rückwärtsrichtung bewegen. Wenn dieser Neigungswinkel hoch ist, sollte die Reaktionsgeschwindigkeit hoch sein. Umgekehrt ist der Neigungswinkel niedrig, dann sollte die Reaktionsgeschwindigkeit niedrig sein. Um diesen Prozess zu kontrollieren, haben wir ein spezielles Theorem namens PID verwendet. PID ist ein Kontrollsystem, das verwendet wurde, um viele Prozesse zu kontrollieren. PID steht für 3 Prozesse.

  • P-proportional
  • I-Integral
  • D-Derivat

Jedes System hat Input und Output. Auf die gleiche Weise hat auch dieses Kontrollsystem einige Eingaben. In diesem Regelsystem ist dies die Abweichung vom stabilen Zustand. Das haben wir als Fehler bezeichnet. In unserem Roboter ist der Fehler der Neigungswinkel von der vertikalen Ebene. Wenn der Roboter ausbalanciert ist, ist der Neigungswinkel null. Der Fehlerwert ist also null. Daher ist die Ausgabe des PID-Systems Null. Dieses System umfasst drei separate mathematische Prozesse.

Der erste ist der Multiplikationsfehler aus der numerischen Verstärkung. Diese Verstärkung wird normalerweise als Kp bezeichnet

P=Fehler*Kp

Zweitens wird das Integral des Fehlers im Zeitbereich erzeugt und mit einem Teil der Verstärkung multipliziert. Dieser Gewinn wird als Ki. bezeichnet

I= Integral(Fehler)*Ki

Der dritte ist die Ableitung des Fehlers im Zeitbereich und multipliziert ihn mit einer gewissen Verstärkung. Diese Verstärkung wird als Kd. bezeichnet

D=(d(Fehler)/dt)*kd

Nachdem wir die obigen Operationen hinzugefügt haben, erhalten wir unsere endgültige Ausgabe

AUSGANG=P+I+D

Aufgrund des P-Teils kann der Roboter eine stabile Position erhalten, die proportional zur Abweichung ist. I-Teil berechnet die Fehlerfläche im Vergleich zum Zeitdiagramm. Es versucht also, den Roboter immer genau in eine stabile Position zu bringen. Der D-Teil misst die zeitliche Steigung gegenüber dem Fehlerdiagramm. Wenn der Fehler zunimmt, ist dieser Wert positiv. Wenn der Fehler kleiner wird, ist dieser Wert negativ. Aus diesem Grund wird die Reaktionsgeschwindigkeit verringert, wenn sich der Roboter in eine stabile Position bewegt, und dies hilft, unnötige Überschwinger zu vermeiden. Sie können mehr über die PID-Theorie über diesen unten angezeigten Link erfahren.

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

Die Ausgabe der PID-Funktion ist auf den Bereich 0-255 (8-Bit-PWM-Auflösung) begrenzt und wird als PWM-Signal an die Motoren gespeist.

Schritt 3: Hardware-Setup

Hardware-Setup
Hardware-Setup

Dies ist nun der Hardware-Setup-Teil. Das Design des Roboters hängt von Ihnen ab. Wenn Sie den Körper des Roboters entworfen haben, müssen Sie ihn symmetrisch um die vertikale Achse, die in der Motorachse liegt, berücksichtigen. Der Akku befindet sich unten. Dadurch lässt sich der Roboter leicht ausbalancieren. In unserem Design befestigen wir Magicbit Board vertikal am Korpus. Wir haben zwei 12V-Getriebemotoren verwendet. Sie können jedoch jede Art von Getriebemotoren verwenden. Das hängt von Ihren Roboterabmessungen ab.

Wenn wir über die Schaltung sprechen, wird sie von einer 7,4 V Lipo-Batterie gespeist. Magicbit verwendet 5V zur Stromversorgung. Dafür haben wir den Regler 7805 verwendet, um die Batteriespannung auf 5V zu regulieren. In späteren Versionen von Magicbit wird dieser Regler nicht benötigt. Weil es bis zu 12V antreibt. Wir liefern direkt 7,4 V für Motortreiber.

Schließen Sie alle Komponenten gemäß der folgenden Abbildung an.

Schritt 4: Software-Setup

Im Code haben wir die PID-Bibliothek zum Berechnen der PID-Ausgabe verwendet.

Gehen Sie zum folgenden Link, um es herunterzuladen.

www.arduinolibraries.info/libraries/pid

Laden Sie die neueste Version davon herunter.

Um bessere Sensorwerte zu erhalten, haben wir die DMP-Bibliothek verwendet. DMP steht für Digital Motion Process. Dies ist eine eingebaute Funktion von MPU6050. Dieser Chip verfügt über eine integrierte Bewegungsprozesseinheit. Es braucht also Lesen und Analysieren. Danach erzeugt es geräuschlose genaue Ausgaben an den Mikrocontroller (in diesem Fall Magicbit (ESP32)). Aber es gibt viele Arbeiten auf der Seite des Mikrocontrollers, um diese Messwerte zu erfassen und den Winkel zu berechnen. Um es einfach zu sagen, haben wir die MPU6050 DMP-Bibliothek verwendet. Laden Sie es herunter, indem Sie auf den folgenden Link gehen.

github.com/ElectronicCats/mpu6050

Um die Bibliotheken zu installieren, gehen Sie im Arduino-Menü zu Tools-> include library->add.zip library und wählen Sie die heruntergeladene Bibliotheksdatei aus.

Im Code müssen Sie den Sollwinkel richtig ändern. Die PID-Konstantenwerte sind von Roboter zu Roboter unterschiedlich. Setzen Sie also beim Abstimmen zuerst die Ki- und Kd-Werte auf Null und erhöhen Sie dann Kp, bis Sie eine bessere Reaktionsgeschwindigkeit erreichen. Mehr Kp verursacht mehr Überschwinger. Erhöhen Sie dann den Kd-Wert. Erhöhen Sie es immer in sehr geringer Menge. Dieser Wert ist im Allgemeinen niedriger als andere Werte. Erhöhen Sie nun das Ki, bis Sie eine sehr gute Stabilität haben.

Wählen Sie den richtigen COM-Port und den Kartentyp aus. den Code hochladen. Jetzt können Sie mit Ihrem DIY-Roboter spielen.

Schritt 5: Schaltpläne

Schema
Schema

Schritt 6: Code

#enthalten

#include "I2Cdev.h" #include "MPU6050_6Axis_MotionApps20.h" #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE #include "Wire.h" #endif MPU6050 mpu; bool dmpReady = false; // auf true setzen, wenn DMP-Init erfolgreich war uint8_t mpuIntStatus; // hält das aktuelle Interrupt-Statusbyte von der MPU uint8_t devStatus; // Status nach jeder Geräteoperation zurückgeben (0 = Erfolg, !0 = Fehler) uint16_t packetSize; // erwartete DMP-Paketgröße (Standard ist 42 Byte) uint16_t fifoCount; // Anzahl aller Bytes, die sich derzeit im FIFO befinden uint8_t fifoBuffer[64]; // FIFO-Speicherpuffer Quaternion q; // [w, x, y, z] Quaternion-Container VectorFloat Gravitation; // [x, y, z] Schwerkraftvektor float ypr[3]; // [Gieren, Nicken, Rollen] Gieren/Neigen/Rollen Container und Schwerkraftvektor double originalSetpoint = 172,5; doppelter Sollwert = OriginalSollwert; doppelter MovingAngleOffset = 0,1; doppelter Eingang, Ausgang; int moveState=0; double Kp = 23; // setze P first double Kd = 0.8; // dieser Wert im Allgemeinen klein double Ki = 300; // dieser Wert sollte für eine bessere Stabilität hoch sein PID pid(&input, &output, &setpoint, Kp, Ki, Kd, DIREKT); // pid initialisieren int motL1 = 26; / / 4 Pins für Motorantrieb int motL2 = 2; int motR1=27; int motR2=4; volatile bool mpuInterrupt = false; // zeigt an, ob der MPU-Interrupt-Pin hoch gegangen ist void dmpDataReady () { mpuInterrupt = true; aufrechtzuerhalten. Void setup () {ledcSetup (0, 20000, 8); // PWM-Setup ledcSetup (1, 20000, 8); ledcSetup(2, 20000, 8); ledcSetup(3, 20000, 8); ledcAttachPin (motL1, 0); // Pinmode der Motoren ledcAttachPin (motL2, 1); ledcAttachPin(motR1, 2); ledcAttachPin(motR2, 3); // I2C-Bus beitreten (die I2Cdev-Bibliothek macht dies nicht automatisch) #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE Wire.begin(); Wire.setClock(400000); // 400kHz I2C-Takt. Kommentieren Sie diese Zeile, wenn Sie Schwierigkeiten beim Kompilieren haben #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE Fastwire::setup(400, true); #endif Serial.println (F ("Initialisieren von I2C-Geräten …")); pinMode (14, EINGANG); // serielle Kommunikation initialisieren // (115200 ausgewählt, weil es für die Ausgabe von Teapot Demo erforderlich ist, aber es liegt // wirklich an Ihnen, abhängig von Ihrem Projekt) Serial.begin (9600); while (!Seriell); // auf Leonardo-Aufzählung warten, andere fahren sofort fort // Gerät initialisieren Serial.println (F ("Initializing I2C devices …")); mpu.initialize(); // Verbindung überprüfen Serial.println (F ("Geräteverbindungen testen …")); Serial.println (mpu.testConnection () ? F ("MPU6050-Verbindung erfolgreich"): F ("MPU6050-Verbindung fehlgeschlagen")); // Laden und konfigurieren Sie das DMP Serial.println (F ("Initializing DMP …")); devStatus = mpu.dmpInitialize(); // Geben Sie hier Ihre eigenen Gyro-Offsets an, skaliert für die minimale Empfindlichkeit mpu.setXGyroOffset (220); mpu.setYGyroOffset(76); mpu.setZGyroOffset(-85); mpu.setZAccelOffset(1788); // 1688 Werkseinstellung für meinen Testchip // Stellen Sie sicher, dass es funktioniert (gibt 0 zurück, wenn ja) if (devStatus == 0) {// DMP einschalten, jetzt, da es fertig ist Serial.println (F ("DMP aktivieren … ")); mpu.setDMPEnabled(true); // Arduino-Interrupt-Erkennung aktivieren Serial.println (F ("Aktivieren der Interrupt-Erkennung (Arduino externer Interrupt 0)…")); attachInterrupt (14, dmpDataReady, RISING); mpuIntStatus = mpu.getIntStatus(); // Setzen Sie unser DMP-Ready-Flag, damit die Hauptfunktion loop () weiß, dass es in Ordnung ist, sie zu verwenden Serial.println (F ("DMP ready! Warten auf den ersten Interrupt …")); dmpReady = wahr; // Erwartete DMP-Paketgröße für späteren Vergleich abrufen packetSize = mpu.dmpGetFIFOPacketSize(); // PID einrichten pid. SetMode (AUTOMATIC); pid. SetSampleTime(10); pid. SetOutputLimits(-255, 255); } else { // FEHLER! // 1 = anfängliches Laden des Speichers fehlgeschlagen // 2 = DMP-Konfigurationsaktualisierungen fehlgeschlagen // (wenn es kaputt geht, ist der Code normalerweise 1) Serial.print (F ("DMP-Initialisierung fehlgeschlagen (code")); Serial. print(devStatus); Serial.println(F(")")); } } void loop() { // Wenn die Programmierung fehlgeschlagen ist, versuchen Sie nichts zu tun if (!dmpReady) return; // Warte auf MPU-Interrupt oder zusätzliche Pakete, die verfügbar sind, while (!mpuInterrupt && fifoCount < packetSize) { pid. Compute(); // dieser Zeitraum wird zum Laden von Daten verwendet, sodass Sie dies für andere Berechnungen verwenden können motorSpeed (Ausgang); } // Interrupt-Flag zurücksetzen und INT_STATUS-Byte abrufen mpuInterrupt = false; mpuIntStatus = mpu.getIntStatus(); // aktuellen FIFO-Zähler abrufen fifoCount = mpu.getFIFOCount(); // auf Überlauf prüfen (dies sollte niemals passieren, es sei denn, unser Code ist zu ineffizient) if ((mpuIntStatus & 0x10) || fifoCount == 1024) { // zurücksetzen, damit wir sauber weitermachen können mpu.resetFIFO(); Serial.println (F ("FIFO-Überlauf!")); // andernfalls auf DMP-Daten-Ready-Interrupt prüfen (dies sollte häufig vorkommen) } else if (mpuIntStatus & 0x02) { // auf die korrekte verfügbare Datenlänge warten, sollte eine SEHR kurze Wartezeit sein, während (fifoCount 1 Paket verfügbar // (dies.) lässt uns sofort weiterlesen, ohne auf einen Interrupt zu warten) fifoCount -= packetSize; 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); // Eulerwinkel 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){//Vorwärtsrichtung L2=0; L1=abs(PWM); R2=0; R1=abs(PWM); if(L1>=255){ L1 = R1 = 255; } } Else {//Rückwärtsrichtung L1 = 0; L2 = abs (PWM); R1 = 0; R2 = abs (PWM); if (L2> = 255) { L2 = R2 = 255; } } // Motorantrieb ledcWrite (0, L1); ledcWrite (1, L2); ledcWrite (2, R1 * 0,97); // 0,97 ist Geschwindigkeitsfakt oder weil der rechte Motor eine höhere Geschwindigkeit als der linke hat, reduzieren wir sie, bis die Motorgeschwindigkeiten gleich sind ledcWrite(3, R2*0.97);

}

Empfohlen: