Schritt 19: Code: kriechen
Arduino-Tools und Stiftung wissen
Suchst du einen guten Überblick über Arduinos, dann es gibt eine ganze Reihe von Instructables für Sie, hier ist ein großes.
Ich bin gonna übernehmen Sie heruntergeladen und installieren der Arduino IDE und können Code auf Ihrem Arduino, die zuvor erwähnten instructable hochladen führt Sie durch all das und vieles mehr.
Code-Übersicht
Im vorherigen Schritt entschieden wir uns, dass wir implementieren würde das crawling mit dem Fahrrad durch die "Frames" von Animation, die die Position der einzelnen Servos an einem bestimmten Punkt in der Zeit definieren würde.
Der vollständige Code ist als Datei angehängt, aber ich werde durch die meisten Abschnitte hier gehen, so dass Sie sie verstehen können. Wenn man sie schon verstehen, dann ist das toll, weil Sie wahrscheinlich vorstellen können, einen besseren Weg dazu, die ich habe.
Benötigten Bibliotheken importieren
Die einzige Bibliothek, die für diesen Code benötigt wird "Servo.h", die voll von praktischen Funktionen machen es extrem einfach, Servos aus einem Arduino zu kontrollieren.
#include <Servo.h>
Globale Variablen definieren
Dies sind Variablen, die von überall im Code zugegriffen werden können. Wir nutzen sie, um Informationen zu speichern, die zu einem späteren Zeitpunkt benötigt werden
Timer-Variablen
Der millis() -Befehl liefert die Anzahl der Millisekunden seit das Arduino-Board Ausführen des aktuellen Programms. Wir können speichern das Ergebnis der millis() und dann vergleichen Sie es um das Ergebnis zu einem späteren Zeitpunkt zu bestimmen, wie lange es wurde zwischen den beiden anrufen.
long previousFrameMillis = 0; long previousInterpolationMillis = 0;
Servo-Detail-Variablen
Diese Variablen speichern welche Pin jedes Servo verbunden ist, sowie wie viele Micros entsprechen min (eingefahren/gesenkt) und max (angehoben/erweitert)
//servo pins int LNpin = 7; //left neck muscle int RNpin = 6; //right neck muscle int LSpin = 5; //left shoulder int LEpin = 4; //left elbow int RSpin = 2; //right shoulder int REpin = 3; //right elbow //these are the 'min' and 'max' angles for each servo //can be inverted for servos that are in opposite orientation int LSMin = 1000; int LSMax = 1700; int RSMin = 2000; int RSMax = 1300; int LEMin = 1700; int LEMax = 1300; int REMin = 1300; int REMax = 1700; int LNMin = 1400; int LNMax = 1600; int RNMin = 1600; int RNMax = 1400;
Bilder
Der erste Eindruck ist, dass wir sollten einfach setzen Sie die Servos auf die Positionen, die durch den ersten Frame definiert,, eine bestimmte Verzögerung (FrameDuration warten) und legen Sie dann die Servos auf die nächste Position.
Dadurch wird eine schreckliche Animation aber, da die Servos zu so schnell springen wie sie, um die angegebene können position und warten dort auf ihre nächste Anweisung.
Der Weg um dieses soll zwischen Frames interpolieren. In anderen Worten, wenn meine Frame-Dauer 2 Sekunden beträgt, möchte nach 1,75 Sekunden ich die Servos um drei Viertel (75 %) betragen des Weges zwischen Bild 1 und Bild 2.
Es ist trivial etwas Mathematik, herauszufinden, wo die Servo sein sollte, wenn wir wissen, wie viel des Rahmens als Verhältnis verstrichen ist. In Worten ist es nur (vorherigen Frame) +(the difference between the next and previous frames) * (Prozentsatz der Frame-Dauer abgelaufen ist), dies wird als "lineare Interpolation" bezeichnet.
int frameDuration = 800; //length of a frame in milliseconds int frameElapsed = 0; //counter of milliseconds of this frame that have elapsed int interpolationDuration = 30; //number of milliseconds between interpolation steps
Hier definieren wir die eigentlichen Frames. Beachten Sie, dass ich 0 bis 1000, wobei 0 angibt, zurückgezogen/gesenkt und 1000 zeigt erweitert/angehoben. Ich konnte beliebig viele gewählt haben und es wäre logischere Entscheidungen, aber der Bereich versorgte mich mit einem zufriedenstellenden Kompromiss zwischen Auflösung und Lesbarkeit.
Wir die map() Funktion später verwenden, um LSMin Variable und 1000 LSMax Variablen, die wir zuvor definierten 0 zuordnen (offensichtlich dieses Beispiel bezieht sich auf die linke Schulter, aber es wäre der gleiche Prozess für alle Servos).
Wenn Sie komplexe oder glattere Animationen definieren wollte könnte Sie leicht mehr Frames hinzufügen und auch Zahlen als min/Max. Eine Möglichkeit wäre, etwa 8 Frames zu verwenden, um eine schöne elliptische Bewegung zu machen.
//frames for the crawling gait are stored here int currentFrameIndex = 0; int numFrames = 4; int crawlGaitRS[] = {0,1000,1000,0}; int crawlGaitRE[] = {0,0,1000,1000}; int crawlGaitLE[] = {1000,0,0,1000}; int crawlGaitLS[] = {0,0,1000,1000}; int crawlGaitLN[] = {1000,1000,0,1000}; int crawlGaitRN[] = {0,1000,1000,1000};
Um diese Interpolation Berechnung umzusetzen, die wir brauchen, um den vorherigen Frame und den folgenden Rahmen nachzuverfolgen, also setzen wir einige Variablen zu tun.
//servo last frame micros int LSlast = 0; int RSlast = 0; int LElast = 0; int RElast = 0; int LNlast = 0; int RNlast = 0; //servo next frame micros int LSnext = 0; int RSnext = 0; int LEnext = 0; int REnext = 0; int LNnext = 0; int RNnext = 0; // variable used to store the current frame of animation int currentFrameIndex = 0;
Servo-Objekte
Endlich, wir schaffen einige Servo-Objekte und Variablen zuweisen. Diese sind Instanzen der Servo-Klasse, die wir in Servo.h enthalten und bieten nützliche Funktionen um jedes Servo zu steuern.
// create servo objects to control servos Servo LS; Servo RS; Servo LE; Servo RE; Servo LN; Servo RN;
Funktionen definieren
Arduino-Setup-Funktion
Die Arduino setup() Funktion ist das erste Bit der Code, der ausgeführt wird, nachdem die globalen Variablen definiert wurden. Hier fürs erste genügt ist, legen die Servo-Objekte auf ihre Pins und starten Sie die serielle Schnittstelle, für den Fall, dass wir nichts für Debuggging berichten wollen.
void setup() { Serial.begin(9600); LS.attach(LSpin); RS.attach(RSpin); LE.attach(LEpin); RE.attach(REpin); LN.attach(LNpin); RN.attach(RNpin); }
Nächsten Frame Set
Diese Funktion wird aufgerufen, sobald unsere Servos bis zum Ende eines Frames erhalten. Alles, was es tut, ist:
- Inkrementieren der "CurrentFrameIndex" (es sei denn, wir haben das letzte Bild erreicht haben in diesem Fall es zurück zu Frame 0 Schleifen)
- Speichern der aktuellen Frame-Position als "letzter Frame"
- Der nächste Frame-Position aus dem Animation-Array abrufen
void setNextFrame() { //if this was the last frame, start again if (currentFrameIndex < numFrames - 1) { currentFrameIndex++; } else { currentFrameIndex = 0; } //we have reached the destination frame, so store it as "last frame" LSlast = LSnext; RSlast = RSnext; LElast = LEnext; RElast = REnext; LNlast = LNnext; RNlast = RNnext; //generate new "next frame" LSnext = crawlGaitLS[currentFrameIndex]; RSnext = crawlGaitRS[currentFrameIndex]; LEnext = crawlGaitLE[currentFrameIndex]; REnext = crawlGaitRE[currentFrameIndex]; LNnext = crawlGaitLN[currentFrameIndex]; RNnext = crawlGaitRN[currentFrameIndex]; }
Interpolationsfunktion
Wie bereits beschrieben, nutzen wir eine lineare Interpolation, um genau welche Position bestimmen ein Servo zu einem bestimmten Zeitpunkt zwischen zwei Frames sein sollte.
Mein Computer-Wissenschaft-Dozent sagte immer, dass ein guter Programmierer war alles über faul, wenn Sie vermeiden können, umschreiben von Code mehrmals indem man es in eine Funktion, dann tun Sie es.
Diese Funktion einfach implementiert die Gleichung der linearen Interpolation zwischen zwei Frames eine Servoposition dieser Frame-Position zugeordnet und auf das Servo-Objekt angewendet.
void writeInterpolatMicros(Servo servo, int prevFrame, int nextFrame, int servoMin, int servoMax, float elapsedRatio) { int interpolated = prevFrame + int(float(nextFrame - prevFrame)*elapsedRatio); servo.writeMicroseconds(map(interpolated,0,1000,servoMin,servoMax)); }
Servo-Update-Funktion
Diese Funktion macht den Code übersichtlicher indem Sie einen Teil davon aus der Hauptschleife.
Zunächst das Verhältnis des Rahmens, die bereits abgeschlossen ist berechnet die Anzahl der Millisekunden, die vergangen sind, seit das Gestell, geteilt durch die Anzahl der Millisekunden, die es braucht, um einen Rahmen zu vervollständigen.
Dieses Verhältnis ist die Interpolationsfunktion für jedes Servo, Aktualisierung jede Position weitergegeben.
void updateServos() { float frameElapsedRatio = float(frameElapsed)/float(frameDuration); writeInterpolatMicros(LS,LSlast,LSnext,LSMin,LSMax,frameElapsedRatio); writeInterpolatMicros(LE,LElast,LEnext,LEMin,LEMax,frameElapsedRatio); writeInterpolatMicros(RS,RSlast,RSnext,RSMin,RSMax,frameElapsedRatio); writeInterpolatMicros(RE,RElast,REnext,REMin,REMax,frameElapsedRatio); writeInterpolatMicros(LN,LNlast,LNnext,LNMin,LNMax,frameElapsedRatio); writeInterpolatMicros(RN,RNlast,RNnext,RNMin,RNMax,frameElapsedRatio); }
Hauptschleife
Die wichtigsten loop() ist wo die Action passiert, sobald es beendet, den enthaltene Code ausführen, darin es springt an den Anfang zurück und beginnt wieder von vorne.
Der erste Schritt in der Hauptschleife soll erfassen die aktuelle Anzahl der Millisekunden seit dem Programmstart ausgeführt) damit wir feststellen können, wieviel Zeit seit der letzten Iteration der Schleife verstrichen ist.
Mithilfe dieses Mal können wir feststellen, ob die verstrichene Zeit größer ist als die Zeit, die wir definiert für die Interpolation Schritte, wenn ja, die updateServos()-Funktion zum Generieren von neuer interpolierten Positionen rufen.
Wir prüfen auch, ob die verstrichene Zeit größer als die Frame-Dauer ist in diesem Fall müssen wir die setNextFrame()-Funktion aufrufen.
void loop() { unsigned long currentMillis = millis(); if(currentMillis - previousInterpolationMillis > interpolationDuration) { // save the last time that we updated the servos previousInterpolationMillis = currentMillis; //increment the frame elapsed coutner frameElapsed += interpolationDuration; //update the servos updateServos(); } if(currentMillis - previousFrameMillis > frameDuration) { // save the last time that we updated the servos previousFrameMillis = currentMillis; //reset elapsed frame tiem to 0 frameElapsed = 0; //update the servos setNextFrame(); }