Stap 19: Code: kruipen
Arduino Tools en kennis van de Stichting
Als u op zoek bent naar een goede introductie tot Arduinos, dan er zijn een heleboel Instructables voor u, hier is een grote stap.
Ik ben gonna veronderstellen u hebt gedownload en installeer de Arduino IDE en zijn in staat om code aan uw Arduino, de eerder genoemde instructable uploaden begeleidt u door alle dat en meer.
Code Overview
In de vorige stap besloten hebben we dat we het kruipen uitvoeren zouden door het fietsen door 'frames' van animatie die de positie van elk van de servo's op een bepaald punt in de tijd bepalen zou.
De volledige code is gekoppeld als een bestand, maar ik zal gaan door het grootste deel van de secties hier zodat u hen kunt begrijpen. Als u al ze begrijpt, dan is dat geweldig, want je kunt waarschijnlijk denken aan een betere manier hiervoor dat ik deed.
Vereiste bibliotheken importeren
De enige bibliotheek die nodig is voor deze code is "Servo.h", die is vol met handige functies om te maken het zeer eenvoudig om te controleren van de servo's van een Arduino.
#include <Servo.h>
Globale variabelen definiëren
Dit zijn variabelen die kunnen worden geopend vanuit overal in de code. We ze gebruiken voor het opslaan van informatie die in een later stadium nodig zal zijn
Timer variabelen
De millis() -opdracht geeft als resultaat het aantal milliseconden sinds de Arduino board het huidige programma. We kunnen het resultaat van millis() opslaan en het vervolgens te vergelijken met het resultaat in een later stadium om te bepalen hoe lang het is geweest tussen de twee gesprekken.
long previousFrameMillis = 0; long previousInterpolationMillis = 0;
Servo Detail variabelen
Deze variabelen opslaan welke pin elke servo is, verbonden en hoeveel micros correspondeert met min (ingetrokken/verlaagd) en max (verhoogd/extended)
//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;
Frames
Iemands eerste indruk misschien wel dat we moeten gewoon de servo's naar de posities die zijn gedefinieerd door het eerste frame, een bepaalde vertraging (frameDuration wacht) en stel de servo's op de volgende positie te verplaatsen.
Dit zal resulteren in een verschrikkelijke animatie wel, omdat de servo's zo snel springt als ze naar de opgegeven kunnen positie en wacht er voor hun volgende instructie.
De manier rond dit is om te interpoleren tussen frames. Met andere woorden, als de duur van mijn frame 2 seconden is, wil na 1.75 seconden ik de servo's driekwart (of 75%) van de weg tussen frame 1 en frame 2.
Het is een triviale beetje wiskunde om erachter te komen waar de servo moet staan als we weten hoeveel van het frame is verstreken als verhouding. In woorden het is gewoon (vorig frame) +(the difference between the next and previous frames) * (percentage van de duur van een beeld is verstreken), dit staat bekend als "lineaire interpolatie".
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 definiëren we de werkelijke frames. Opmerking dat ik 0 tot 1000 gebruikte, waar 0 geeft aan dat van de teruggetrokken/verlaagd en 1000 geeft verlengd/verhoogd. Ik kon heb gekozen een aantal, en er zou meer logische keuze, maar het bereik voorzien me van een bevredigend compromis tussen de resolutie en leesbaarheid.
We zullen later de map() functie gebruiken 0 toewijzen aan de variabele LSMin en 1000 aan LSMax-variabele die we eerder gedefinieerd (uiteraard in dit voorbeeld is voor de linker schouder, maar zou het hetzelfde proces voor de andere servo's).
Als je wilde meer complexe of soepeler animaties kun je gemakkelijk bepalen meer frames toevoegen en ook gebruik maken van nummers dan min/max. Een optie zou zijn met ongeveer 8 beelden te maken van een mooie elliptische beweging.
//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};
Oog op de uitvoering van deze berekening van de interpolatie die we moeten bijhouden van het vorige frame en het volgende frame, dus we opgezet sommige variabelen om dat te doen.
//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-objecten
Tot slot, we enkele servo-objecten maken en toewijzen aan variabelen. Dit zijn exemplaren van de servo-klasse die we in de Servo.h opgenomen en zorgt voor nuttige functies om te controleren elke servo.
// create servo objects to control servos Servo LS; Servo RS; Servo LE; Servo RE; Servo LN; Servo RN;
Functies definiëren
Arduino Setup functie
De Arduino Setup functie is de eerste bit van de code die wordt uitgevoerd nadat de globale variabelen zijn gedefinieerd. Alles wat hier nodig is voor nu is de servo-objecten koppelen aan hun pin en opstarten van de seriële poort, voor het geval dat wij een rapport wilt maken om het even wat voor debuggging.
void setup() { Serial.begin(9600); LS.attach(LSpin); RS.attach(RSpin); LE.attach(LEpin); RE.attach(REpin); LN.attach(LNpin); RN.attach(RNpin); }
Set volgend Frame
Deze functie wordt aangeroepen zodra onze servo's aan het einde van een frame. Alles wat het doet is:
- Verhogen van de "currentFrameIndex" (tenzij we hebben bereikt het laatste frame, in welk geval het loops terug naar frame 0)
- Opslaan van de huidige positie van het frame als "laatste frame"
- Ophalen van de volgende frame-posities uit de animatie-matrix
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]; }
Interpolatie functie
Zoals eerder is beschreven, zullen we een lineaire interpolatie gebruiken bepaalt u precies welke positie een servo op een bepaald moment tussen twee frames moet.
Mijn computer wetenschap docent heeft altijd gezegd dat is een goede programmeur was alles over lui, als u dat code herschrijven meerdere malen vermijden kunt door de gegevens in een functie, dan doen.
Deze functie gewoon implementeert de lineaire interpolatie vergelijking tussen twee frames die frame positie wordt toegewezen aan een servo positie en toegepast op het object servo.
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 updatefunctie
Deze functie maakt de code netter door het verwijderen van een stuk van het van de hoofdlus.
Eerst wordt de verhouding van het frame dat is al voltooid berekend volgens het aantal milliseconden dat is verstreken sinds het frame begonnen, gedeeld door het aantal milliseconden duurt om te voltooien van een frame.
Deze verhouding is doorgegeven aan de functie interpolatie voor elke servo, bijwerken van ieders standpunt.
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); }
Hoofdlus
De belangrijkste loop is waar al actie gebeurt, zodra het klaar is met het uitvoeren van alle de programmacode hierin het springt terug naar het begin en begint helemaal opnieuw.
De eerste stap in de hoofdlus is het opnemen van het huidige aantal milliseconden, aangezien het programma begon te lopen) zodat we kunnen bepalen hoeveel tijd is verstreken sinds de laatste iteratie van de lus.
Met behulp van deze tijd kunnen we bepalen of de verstreken tijd groter is dan de periode die we gedefinieerd voor interpolatie stappen, zo ja, de updateServos() functie aanroepen voor het genereren van nieuwe geïnterpoleerde posities.
Ook controleren wij of de verstreken tijd groter dan de duur van een beeld is, in welk geval moeten we de functie setNextFrame() aanroepen.
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(); }