/*Es wird getestet, ob sich der Segway in Balance haelt. Akku ausreichend geladen ? Min 8V notwendig fuer die A4988, bei Betrieb der Motoren. Siehe Voltmeter Anzeige am Segway. Noch keine Fahrbefehle ueber Bluetooth vom Smartphone (siehe Sketch 88). Der Segway wird etwa in Balance-Lage gehalten. Nach Hochladen kommt Ausschrift am SerialMonitor: ======================================== Calculating gyro offsets DO NOT MOVE MPU6050... Done! X : -1.54 Y : 0.27 Z : -0.65 Program will start after 3 seconds ======================================== WinkelY bei Start= -105 (in Grad ; wenn Segway in dieser Lage gehalten wird) Das Bibliotheksprogramm MPU6050_tockn.h muss aus dem Bibliotheksmanager geladen werden. Man findet den Code (.cpp und .h)auch auf der Webseite: https://github.com/Tockn/MPU6050_tockn Der Balancewinkel ist abhaengig von der Anordnung des MPU-6050, wir befestigen das Modul so, dass der Winkel Y relevant ist. Er kann mit dem sketch_83_MPU6050_tockn_test ermittelt werden. In die Variable BALANCEWINKEL muss moeglichst genau dieser Wert, sonst bewegt sich der Segway in eine Richtung auch ohne Fahrbefehl. Takt des I2C-Bus erhoehen von standardmaessig 100kHz auf 400kHz, mit Wire.setClock(400000). Das Schrittmotor-Treiber-Modul mit A4988 ist auf 1/8 Schritt-Betrieb eingestellt: Eingaenge MS1 und MS2 an +5V sowie MS3 an GND. Eingang /RST an +5V. Diese Verbindungen sollten geprueft werden, sonst unerklaerliche Fehlfunktion. Am Potentiometer Einstellung Phasenstrom so, dass Funktion erfuellt wird. Beachte: Je nach Ausfuhrung des Nano evtl "ATmega328P(Old Bootloader)" einstellen*/ /* Pins am Nano fuer Verbindung zu den Schrittmotor-Treibern: */ #define MotLinksD 5 // Drehrichtung Motor links von Pin D5 #define MotLinksS 9 // Schritt Motor links von Pin D9 #define MotRechtsD 3 // Drehrichtung Motor rechts von Pin D3 #define MotRechtsS 2 // Schritt Motor rechts von Pin D2 #define MotEnable 4 // Enable beide Motoren (Treiber A4988 aktiviert wenn LOW) /* Vorbereiten Kommunikation mit Gyrosensor MPU-6050: */ #include "Wire.h" // Bibliotheksprogramm fuer I2C-Verbindung (zum MPU-6050) #include "MPU6050_tockn.h" // Bibliotheksprogramm fuer Funktion des MPU-6050 MPU6050 mpu6050(Wire); /* Einige Parameter festlegen: */ #define BALANCEWINKEL -105 // Neigungswinkel in Grad, welcher nah an der Balance liegt, abhaengig von der Anordnung /* Parameter für PID-Neigungsregelung:*/ int kp= 80; // P-Anteil des Reglers (experimentell ermittelt) int ki= 50; // I-Anteil des Reglers (experimentell ermittelt) int kd= 200; // D-Anteil des Reglers (experimentell ermittelt) #define GRENZWERT 4000 // Maximalwert fuer die Stellgroesse Neigungswinkel (pid), das ergibt ... // ...500000/4000 MicrosProSchritt, 8000 1/8 Schritte a 0,225 Grad/Sek = 5 Rad-Umdr/Sek /* Initialwerte vorgeben: */ int MotLinksGeschwind = 0; // Initialwert 0, waere ein 1/8 Schritt a 0,225 Grad pro Sekunde, Wert wird aber dann... int MotRechtsGeschwind = 0; // ...durch Stellgroesse pid veraendert long Looptimer = 0; // In dieser Variablen wird die Zeit gespeichert, die mit millis verglichen wird int Loopzeit = 10; // Zeitabstand zwischen zwei Messungen am MPU6050 und Neuberechnung der Stellgroesse... long LetzteZeit = 0; // ...dabei erfolgt aber keine Ausgabe von Schritten - optimale Loopzeit austesten long Zeit = 0; float Abweich = 0; // Zeit und Abweichung wird benoetigt fuer den D-Anteil der Regelung float LetzteAbweich = 0; float WinkelBalance; float WinkelY; // Aktueller Neigungswinkel gemaess Messung durch MPU-6050 float p=0.0; // Stellgroesse Proportional-Anteil, am Ausgang vom PID-Regler float i=0.0; // Stellgroesse Integral-Anteil, am Ausgang vom PID-Regler float d=0.0; // Stellgroesse Differenzial-Anteil, am Ausgang vom PID-Regler float pid=0.0; // Gesamt-Stellgroesse, am Ausgang vom PID-Regler void setup() { Serial.begin(115200); pinMode(MotLinksD, OUTPUT); pinMode(MotLinksS, OUTPUT); pinMode(MotRechtsD, OUTPUT); pinMode(MotRechtsS, OUTPUT); pinMode(MotEnable, OUTPUT); digitalWrite(MotEnable, LOW); // Beide Motortreiber aktiv setzen delay(1000); Wire.begin(); // Initialisierung I2C Bus fuer Kommunikation mit MPU6050(Arduino als Master) Wire.setClock(400000); // I2C Takt erhoehen von standardmaessig 100kHz auf 400kHz// Etwas warten, bis eventuelle Erschuetterungen abgeklungen sind mpu6050.begin(); // Bibliotheksprogramm fuer den MPU startet, Segway nahe Balance-Winkel halten mpu6050.calcGyroOffsets(true); // Waehrend der Kalibrierung nicht bewegen! float WinkelY = mpu6050.getAngleY(); // Bei anderer Ausrichtung des MPU-6050 muss andere Achse gewaehlt werden WinkelBalance = WinkelY; // Wenn beim Start der Segway nicht genau auf BALANCEWINKEL steht, wird die Regelabweichung... // ...sofort hoch, die Raeder drehen schnell. Der Wert wird vorerst dem tatsaechl Winkel angepasst Serial.println(); Serial.print("WinkelY bei Start= "); Serial.println(WinkelY); } void loop() { if(millis() > Looptimer) // Der folgende Programmabschnitt erst wieder wenn Loopzeit vorbei ist... { // ...siehe Looptimer aktualisieren weiter unten mpu6050.update(); // MPU-6050 auslesen float WinkelY = mpu6050.getAngleY(); // Bei anderer Ausrichtung des MPU-6050 muss andere Achse gewaehlt werden Looptimer = millis() + Loopzeit; // Looptimer aktualisieren Zeit = millis(); if (WinkelBalance < BALANCEWINKEL) WinkelBalance +=0.1; // Allmaehliche Angleichung des Werts an BALANCEWINKEL nach dem Start... if (WinkelBalance > BALANCEWINKEL) WinkelBalance -=0.1; // ... siehe oben /* Berechnung PID-Regler: */ Abweich = WinkelBalance - WinkelY; // Regelabweichung bestimmen p = kp * Abweich; // P-Anteil berechnen i = i+(ki * Abweich); // I-Anteil berechnen if(i > GRENZWERT)i = GRENZWERT; // Der I-Anteil wird begrenzt (ebenso wie der gesamte pid-Wert, siehe unten) if(i < -GRENZWERT)i = -GRENZWERT; d = kd * ((Abweich-LetzteAbweich)/(Zeit-LetzteZeit)); // D-Anteil berechnen pid = p + d + i; // Stellgroesse (fuer MotLinksGeschwind und MotRechtsGeschwind) berechnen LetzteAbweich = Abweich; LetzteZeit = Zeit; if(pid < 10 && pid > -10)pid = 0; // Bei geringer Abweichung von der Balance eine "Totzone" (vorerst nur gering) /* Weiterverarbeiten der Stellgroesse: */ if(abs(pid) < GRENZWERT) // Wenn die Stellgroesse innerhalb des erlaubten Bereiches liegt { MotLinksGeschwind = pid ; MotRechtsGeschwind = pid ; } // Wenn sie ausserhalb liegt, bleibt der bisherige Wert unveraendert if(WinkelY > -60 || WinkelY < -160) PORTD =PORTD | B00010000; // Falls der Roboter umgefallen ist, Motoren abschalten...*/ } // ...entspricht digitalWrite(MotorEN,HIGH) /* Aufruf der Unterprogramme zur Ansteuerung der Motoren: */ MotLinks(); MotRechts(); } void MotLinks() { if(MotLinksGeschwind == 0) return; // Wenn Null - zurueck zu Beginn von void loop // Wenn ungleich null: PORTB = PORTB & B11111101; // D9 bzw A4988-Eingang STEP auf LOW setzen, durch Portmanipulation mit bitweise UND // Wesentlich schneller als mit Befehl digitalWrite(Motor1S, LOW), aber notwendig noch... delayMicroseconds(3); // ...delay, weil der A4988-Eingang STEP eine min Impulslaenge 1,9 Mikrosek verlangt static unsigned long LetzterSchritt = 0; // Variable mit "static": Wert 0 wird nur beim ersten Durchlauf zugewiesen static boolean Status = false; // false ist gleichbedeutend mit 0 bzw LOW long MicrosProSchritt = 500000ul / abs(MotLinksGeschwind); // Berechnung der Zeit zwischen zwei Schritten (1/8 Schritten 0,225 Grad) digitalWrite(MotLinksD, MotLinksGeschwind > 0 ? LOW:HIGH); // Richtungsangabe setzen, Ausgabe an Pin Motor1D // Ergebnis ist HIGH, wenn Motor1Geschwind > 0 ist. Sonst LOW. if(micros() - LetzterSchritt > MicrosProSchritt) { // Wenn die Zeit ueberschritten ist erfolgt ein Wechsel des Status PORTB = PORTB | B00000010; // D9 und damit A4988-Eingang STEP auf HIGH setzen, ein 1/8 Schritt 0,225 Grad wird ausgefuehrt // Portmanipulation mit bitweise ODER LetzterSchritt = micros(); // Variable wird wieder aktualisiert, fuer erneuten Vgl mit micros } } void MotRechts() { if(MotRechtsGeschwind == 0) return; // Wenn Null - zurueck zu Beginn von void loop // Wenn ungleich null: PORTD = PORTD & B11111011; // D2 bzw A4988-Eingang STEP auf LOW setzen, durch Portmanipulation mit bitweise UND // Wesentlich schneller als mit Befehl digitalWrite(Motor1S, LOW), aber notwendig noch... delayMicroseconds(3); // ...delay, weil der A4988-Eingang STEP eine min Impulslaenge 1,9 Mikrosek verlangt static unsigned long LetzterSchritt = 0; // Variable mit "static": Wert 0 wird nur beim ersten Durchlauf zugewiesen static boolean Status = false; // false ist gleichbedeutend mit 0 bzw LOW long MicrosProSchritt = 500000ul / abs(MotRechtsGeschwind); // Berechn der Zeit zwischen zwei Schritten (1/8 Schritten a 0,225 Grad) //Serial.print ("MicrosProSchritt= "); //Serial.println (MicrosProSchritt); digitalWrite(MotRechtsD, MotRechtsGeschwind > 0 ? HIGH:LOW); // Anders als bei void MotLinks hier HIGH:LOW, da Drehrichtung invers sein muss... // ...weil die Achse in entgegensetzte Richt zeigt if(micros() - LetzterSchritt > MicrosProSchritt) { // Wenn die Zeit ueberschritten ist erfolgt ein Wechsel des Status PORTD = PORTD | B00000100; // D2 und damit A4988-Eingang STEP auf HIGH setzen - ein 1/8 Schritt 0,225 Grad wird ausgefuehrt // Portmanipulation mit bitweise ODER LetzterSchritt = micros(); // Variable wird wieder aktualisiert, fuer erneuten Vgl mit micros } }