Industrielle Fertigung
Industrielles Internet der Dinge | Industrielle Materialien | Gerätewartung und Reparatur | Industrielle Programmierung |
home  MfgRobots >> Industrielle Fertigung >  >> Manufacturing Technology >> Herstellungsprozess

MobBob:DIY-Arduino-Roboter, der von einem Android-Smartphone gesteuert wird

Komponenten und Verbrauchsmaterialien

Arduino Nano R3
× 1
HC-05 Bluetooth-Modul
× 1
SG90 Micro-Servomotor
× 4
4xAA Batteriehalter
× 1
AA-Batterien
× 4

Notwendige Werkzeuge und Maschinen

Lötkolben (generisch)
3D-Drucker (generisch)

Apps und Onlinedienste

Arduino-IDE

Über dieses Projekt

Ich habe diesen Roboter nach den Anweisungen auf der Website hergestellt. Eine tolle Idee für eine Symbiose zwischen Arduino und Android.

MobBob ist ein Smartphone-gesteuerter Roboter. Durch die Nutzung der Leistung Ihres Smartphones ist MobBob ein wandelnder, sprechender Roboter mit Spracherkennung und Computer Vision.

3D-gedruckte Teile:https://www.thingiverse.com/thing:715688

Android.apk-Datei:https://apkpure.com/mobbob/com.cevinius.MobBob

Im Code möchten Sie:

- Aktualisieren Sie die Pin-Variablen, damit sie Ihrem Build entsprechen

- Optimieren Sie das Servozentrum, die Min- und Max-Werte

- Setzen Sie "FRONT_JOINT_HIPS" auf 1 oder -1, je nachdem wie Ihre Hüftservos montiert sind. Ich montiere es mit der Servoachse an der Vorderseite von MobBob. Setzen Sie diesen Wert für diese Konfiguration auf 1.

Code

  • Code
codeArduino
/* * ============================================================* MobBob-Steuerungsprogramm - Software Serielle Bluetooth-Version * von Kevin Chan (alias Cevinius) * ============================================================* * Dieses Programm ermöglicht die Steuerung von MobBob mit seriellen Befehlen. In dieser Version des Codes werden die *-Befehle über einen seriellen Software-Port empfangen, wobei die Pins in #define oben definiert sind. * Dies bedeutet, dass Sie ein beliebiges Arduino-kompatibles Board verwenden und eine Bluetooth-Karte in die Pins für die * Software-Seriell einstecken können. (Im Gegensatz zu der anderen Version, die für das Bluno-Board von DFRobot entwickelt wurde.) * * Dieses Programm ist lang und enthält 2 Hauptkomponenten - ein reibungsloses Servo-Animationsprogramm und ein * serielles Befehls-Parser-Programm. * * Animationssystem * ================* Das Animationsprogramm wurde entwickelt, um Servo-Keyframe-Arrays reibungslos zu animieren. Der Code versucht sein * Bestes zu tun, um einfach zu verwenden. * * Das Animationssystem wird nur 1 Befehl in die Warteschlange stellen. d.h. ein Befehl kann ausgeführt werden, * und ein Befehl kann in die Warteschlange gestellt werden. Wenn Sie weitere Befehle senden, überschreiben diese den Befehl in der Warteschlange. * * Das Animationssystem wartet standardmäßig, bis die aktuelle Animation beendet ist, bevor die nächste gestartet wird. Dies * bedeutet, dass, wenn die Animationsdaten mit dem Roboter in seiner Grundpose enden, die Dinge reibungslos zusammenlaufen. Um dies * zu unterstützen, verfügt das Animationssystem auch über eine Funktion, bei der eine Animation eine "Endsequenz" * haben kann, um den Roboter wieder in die Grundpose zu bringen. Diese Funktion wird für die Vorwärts-/Rückwärts-Animationen verwendet. * Diese Animationen haben eine letzte Sequenz, die den Roboter wieder in die Grundpose bringt. * * Wenn die Wiedergabe einer Animation beendet ist, gibt das Animationssystem eine Antwortzeichenfolge an den seriellen Port aus. * Dadurch können die Anrufer wissen, wann die von ihnen angeforderten Animationen abgespielt wurden. Dies ist nützlich * für Benutzer, um Animationen zu sequenzieren - sie warten darauf, dass eine fertig ist, bevor sie eine andere starten. * * Der Animationscode hat viele Variablen, um Dinge zu optimieren. Z.B. Aktualisierungsfrequenz, Arduino-Pins usw. * * Das Animationsdaten-Array-Format ist auch so konzipiert, dass es einfach von Hand bearbeitet werden kann. * * Befehls-Parser * ==============* Dieses System parst seriell empfangene Befehle und verarbeitet sie. Die Befehle umfassen einen zum direkten * Setzen von Servopositionen, sowie Befehle zum Auslösen vordefinierter Animationen und Walks. * * Benutzer, die sich nicht um die Details des Gehens kümmern möchten, können einfach die vordefinierten Spaziergänge/Animationen verwenden. * Und Benutzer, die die vollständige Kontrolle über die Servos wünschen (um neue Animationen im Handumdrehen zu erstellen), können dies auch tun. * * Wie oben erwähnt, können diese Befehle interaktiv über den Arduino Serial Monitor verwendet werden. Sie können auch * über Bluetooth LE (bei Verwendung eines Bluno) eingesandt werden. Die Telefon-App sendet die Befehle über Bluetooth LE an den * Bluno. * * Allgemeine Befehle:* ----------------- * Bereit/OK-Prüfung: * Statusprüfung. Die Antwort wird sofort zurückgegeben, um zu überprüfen, ob der Controller funktioniert. * * Servo einstellen: * Zeit - Zeit zum Tween zu bestimmten Winkeln, 0 springt sofort zu Winkeln * leftHip - Mikrosekunden von der Mitte. -ve ist angesagt, +ve ist angesagt * leftFoot - Mikrosekunden von flach. -ve ist Fuß nach unten, +ve ist Fuß nach oben * rechte Hüfte - Mikrosekunden von der Mitte. -ve ist angesagt, +ve ist angesagt * rightFoot - Mikrosekunden von flach. -ve ist Fuß unten, +ve ist Fuß oben * Dieser Befehl wird verwendet, um die volle Kontrolle über die Servos zu erhalten. Sie können den Roboter über die angegebene Dauer von seiner * aktuellen Pose in die angegebene Pose tweenen. * * Stop/Reset: * Stoppt den Roboter nach der aktuellen Animation. Kann verwendet werden, um Animationen, die auf Loop * gesetzt sind, auf unbestimmte Zeit zu stoppen. Dies kann auch verwendet werden, um den Roboter in seine Grundpose (aufrecht stehend) zu versetzen * * Sofort stoppen: * Stoppt den Roboter sofort, ohne zu warten, bis die aktuelle Animation abgeschlossen ist. Dieser * unterbricht die aktuelle Animation des Roboters. Möglicherweise befindet sich der Roboter mitten in der Animation * und in einer instabilen Pose. Seien Sie also vorsichtig, wenn Sie dies verwenden. * * Standard-Gehbefehle:* ----------------------- * Vorwärts:, -1 bedeutet kontinuierlich, 0 oder kein Parameter ist das gleiche wie 1 mal. * Rückwärts:, -1 bedeutet kontinuierlich, 0 oder kein Parameter ist gleich 1 Mal. * Links abbiegen:, -1 bedeutet kontinuierlich, 0 oder kein Parameter ist gleich 1 Mal. * Rechts abbiegen:, -1 bedeutet kontinuierlich, 0 oder kein Parameter entspricht 1 Mal. * * Lustige Animationsbefehle:* ----------------------- * Kopfschütteln:, -1 bedeutet kontinuierlich, 0 oder kein Parameter ist das gleiche wie 1 mal. * * Bounce:, -1 bedeutet kontinuierlich, 0 oder kein Parameter ist gleich 1 Mal. * * Wobble:, -1 bedeutet kontinuierlich, 0 oder kein Parameter ist gleich 1 Mal. * Wobble Left:, -1 bedeutet kontinuierlich, 0 oder kein Parameter ist gleich 1 Mal. * Wobble Right:, -1 bedeutet kontinuierlich, 0 oder kein Parameter ist gleich 1 Mal. * * Tap Feet:, -1 bedeutet kontinuierlich, 0 oder kein Parameter entspricht 1 Mal. * Linken Fuß antippen:, -1 bedeutet kontinuierlich, 0 oder kein Parameter entspricht 1 Mal. * Rechten Fuß antippen:, -1 bedeutet kontinuierlich, 0 oder kein Parameter entspricht 1 Mal. * * Shake Legs:, -1 bedeutet kontinuierlich, 0 oder kein Parameter entspricht 1 Mal. * Linkes Bein schütteln:, -1 bedeutet kontinuierlich, 0 oder kein Parameter entspricht 1 Mal. * Rechtes Bein schütteln:, -1 bedeutet kontinuierlich, 0 oder kein Parameter entspricht 1 Mal. * * Außerdem sendet der Code eine Antwortzeichenfolge über Serial zurück, wenn die * Ausführung der Befehle abgeschlossen ist. * * Wenn der Befehl normal beendet wurde, ist die Antwortzeichenfolge der Befehlscode ohne * Parameter. Z.B. Wenn der Vorgang abgeschlossen ist, sendet er die Antwort "". * * Wenn ein Befehl mit  unterbrochen wurde, wurde die aktuelle Animation möglicherweise mittendrin gestoppt. * In diesem Fall könnte sich der Roboter in einer seltsamen Mittelstellung befinden und FinishAnims wurden * möglicherweise nicht gespielt. Um den Benutzer darüber zu informieren, dass dies passiert ist, enthält die Antwortzeichenfolge den Parameter * -1. Beispiel:Wenn ein Spaziergang auf halbem Weg mit  gestoppt wurde, wäre die Antwortzeichenfolge * , um anzuzeigen, dass der Spaziergang gestoppt wurde, aber auf halbem Weg gestoppt wurde. * (Hinweis:Wenn Sie  verwenden, um zu stoppen, wird gewartet, bis der aktuelle Animationszyklus abgeschlossen ist *, bevor er gestoppt wird. In diesem Fall werden Animationen also nicht mittendrin gestoppt.) * * Da die Antworten nach einem gesendet werden Animation abgeschlossen ist, kann der Befehlssender * nach den Antwortzeichenfolgen suchen, um festzustellen, wann der Roboter für einen neuen Befehl bereit ist. * Z.B. Wenn Sie den Befehl  verwenden, wird der Antwortstring erst gesendet, wenn alle 3 Schritte * (und Animation beenden) abgeschlossen sind. Der Befehlssender kann also auf die Antwortzeichenfolge * warten, bevor er dem Roboter sagt, dass er das nächste tun soll. */ #include #include //-------------------------------- -------------------------------------------------- // Geschwindigkeit der seriellen Kommunikation - Stellen Sie dies für Ihre serielle (Bluetooth) Karte ein.//------------------------------------------ -------------------------------------------------- -// Serielle Kommunikationsgeschwindigkeit mit der Bluetooth-Platine.// Einige Platinen sind standardmäßig auf 9600 eingestellt. Die Platine, die ich habe, hat einen Standardwert von 115200. #define SERIAL_SPEED 115200// Richten Sie einen seriellen Software-Port auf diesen Pins ein.const int rxPin =11; // Pin zum Empfangen von Dataconst Int txPin =12; // Pin zum Senden von DatenSoftwareSerial softwareSerial (rxPin, txPin); //--------------------------------- ------------------------------------------------- // Arduino-Pins einrichten - Stellen Sie diese für Ihren speziellen Roboter ein. //------------------------------------- ---------------------------------------------------------const int SERVO_LEFT_HIP =5;const int SERVO_LEFT_FOOT =2;const int SERVO_RIGHT_HIP =3;const int SERVO_RIGHT_FOOT =4;// Ich möchte, dass dieser Code für alle 4-Servo-Zweibeiner verwendbar ist! (wie Bob, MobBob) // Mir ist aufgefallen, dass bei einigen Builds die Hüftservos auf eine andere // Weise montiert werden als bei MobBob. Mit dieser Einstellung können Sie den Code // für jeden Build-Stil konfigurieren. // 1 für MobBob-Stil nach vorne gerichtete Hüften (Gelenk nach vorne) // -1 für Bob-Stil nach hinten gerichtete Hüften (Gelenk nach hinten)#define FRONT_JOINT_HIPS 1//------------------ -------------------------------------------------- --------------// Servo Max/Min/Center Constants - Stellen Sie diese für Ihren speziellen Roboter ein.//-------------------- -------------------------------------------------- --------------const int LEFT_HIP_CENTRE =1580;const int LEFT_HIP_MIN =LEFT_HIP_CENTRE - 500;const int LEFT_HIP_MAX =LEFT_HIP_CENTRE + 500;const int LEFT_FOOT_CENTRE =1410;const int LINKS_FOOT_MIN =LEFT_FOOT_MIN =const int LEFT_FOOT_MAX =LEFT_FOOT_CENTRE + 500;const int RIGHT_HIP_CENTRE =1500;const int RIGHT_HIP_MIN =RIGHT_HIP_CENTRE - 500;const int RIGHT_HIP_MAX =RIGHT_HIP_CENTRE + 500;const int RIGHT_FOOT =1 465;const int RIGHT_FOOT_MIN =RIGHT_FOOT_CENTRE - 500;const int RIGHT_FOOT_MAX =RIGHT_FOOT_CENTRE + 500;//----------------------------------------- ------------------------------------------------- // Hilfsfunktionen zur benutzerfreundlicheren Berechnung der Gelenkwerte. // Hier können Sie die Vorzeichen anpassen, wenn die Servos anders eingestellt sind. // Die Aktualisierung hier bedeutet, dass die Animationsdaten nicht geändert werden müssen, wenn die // Servos sind anders eingestellt. // (zB Die Hüftservos von Original Bob sind rückwärts zu denen von MobBob.) //// (Außerdem fällt es mir schwer, mich an die Zeichen zu erinnern, die für jedes Servo verwendet werden müssen, da sie // für linke / rechte Hüfte und für linke / rechte Füße unterschiedlich sind.) //------------------------------------------------ ------------------------------int LeftHipCentre() { return LEFT_HIP_CENTRE; }int LeftHipIn (int Millisekunden) { return LEFT_HIP_CENTRE + (FRONT_JOINT_HIPS * Millisekunden); }int LeftHipOut (int Millisekunden) { return LEFT_HIP_CENTRE - (FRONT_JOINT_HIPS * Millisekunden); }int RightHipCentre() { return RIGHT_HIP_CENTRE; }int RightHipIn (int Millisekunden) { return RIGHT_HIP_CENTRE - (FRONT_JOINT_HIPS * Millisekunden); }int RightHipOut (int Millisekunden) { return RIGHT_HIP_CENTRE + (FRONT_JOINT_HIPS * Millisekunden); }int LeftFootFlat() { return LEFT_FOOT_CENTRE; }int LeftFootUp(int Millisekunden) { return LEFT_FOOT_CENTRE - Millisekunden; }int LeftFootDown(int Millisekunden) { return LEFT_FOOT_CENTRE + Millisekunden; }int RightFootFlat() { return RIGHT_FOOT_CENTRE; }int RightFootUp(int Millisekunden) { return RIGHT_FOOT_CENTRE + Millisekunden; }int RightFootDown(int Millisekunden) { return RIGHT_FOOT_CENTRE - Millisekunden; }//----------------------------------------------------------- -----------------------------------// Keyframe-Animationsdaten für Standard-Walking und andere Servo-Animationen// // Das Format ist { , ​​, , ,  }// Millisekunden – Zeit zum Tween zu den Positionen dieses Keyframes. Z.B. 500 bedeutet, dass es 500 ms dauert, um von der // Roboterposition am Anfang dieses Frames zu der in diesem Frame angegebenen Position zu gelangen // leftHipMicros - Position der linken Hüfte in Servo-Mikrosekunden. // leftFootMicros - Position der linken Hüfte in Servo Mikrosekunden.// rightHipMicros - Position der linken Hüfte in Servo-Mikrosekunden.// rightFootMicros - Position der linken Hüfte in Servo-Mikrosekunden.// // Die Servo-Mikrowerte unterstützen einen speziellen Wert von -1. Wenn dieser Wert angegeben ist, wird // der Animationscode angewiesen, dieses Servo in diesem Keyframe zu ignorieren. d.h. dieses Servo wird // in der Position bleiben, die es zu Beginn dieses Keyframes hatte. //// Außerdem ist das erste Element in den Animationsdaten etwas Besonderes. Es ist ein Metadatenelement.// Das erste Element ist { , 0, 0, 0, 0 }, das uns die Anzahl der Frames// in der Animation mitteilt. Der erste tatsächliche Keyframe befindet sich also in animData[1] und der letzte Keyframe// befindet sich in animData[]. (Wobei  der Wert in animData[0][0] ist.)//---------------------------------------- -------------------------------------------------- ---// Konstanten, um den Zugriff auf die Keyframe-Arrays besser lesbar zu machen.const int TWEEN_TIME_VALUE =0;const int LEFT_HIP_VALUE =1;const int LEFT_FOOT_VALUE =2;const int RIGHT_HIP_VALUE =3;const int RIGHT_FOOT_VALUE =4;// Constant int RIGHT_FOOT_VALUE =4;// in der Gehgang-Animation data.const int FOOT_DELTA =150;const int HIP_DELTA =FRONT_JOINT_HIPS * 120;// Geht in die standardmäßige gerade stehende Position. Verwendet von stopAnim().int standStraightAnim[][5] ={ // Metadaten. Das erste Element ist die Anzahl der Frames. { 1, 0, 0, 0, 0 }, // Füße flach, Füße gerade { 300, LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat() }}; // Vorher den Roboter zum Feet Flat, Feet Even (dh standStraightAnim).int walkForwardAnim[][5] ={ // Metadaten. Das erste Element ist die Anzahl der Frames. { 8, 0, 0, 0, 0 }, // Nach links neigen, Füße gerade { 300, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipCentre(), RightFootDown(FOOT_DELTA) }, // Nach links neigen, Rechter Fuß vorwärts { 300, LeftHipIn(HIP_DELTA), LeftFootUp(FOOT_DELTA), RightHipOut(HIP_DELTA), RightFootDown(FOOT_DELTA) }, // Füße flach, Rechter Fuß vorwärts { 300, LeftHipIn(HIP_DELTA), LeftFootFlat(), RightHipOut(HIP_DELTA), RightFootFlat() }, // Nach rechts neigen, Rechter Fuß nach vorne { 300, LeftHipIn(HIP_DELTA), LeftFootDown(FOOT_DELTA), RightHipOut(HIP_DELTA), RightFootUp(FOOT_DELTA) }, // Nach rechts neigen, Füße gerade { 300, LeftHipCentre (), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // Nach rechts neigen, Linker Fuß nach vorne { 300, LeftHipOut(HIP_DELTA), LeftFootDown(FOOT_DELTA), RightHipIn(HIP_DELTA), RightFootUp(FOOT_DELTA) } , // Füße flach, Linker Fuß nach vorne { 300, LeftHipOut(HIP_DELTA), LeftFootFlat(), RightHipIn(HIP_DELTA), RightFootFlat() }, // Nach links neigen, Linker Fuß nach vorne { 300, LeftHipOut(HIP_DEL .) TA), LeftFootUp(FOOT_DELTA), RightHipIn(HIP_DELTA), RightFootDown(FOOT_DELTA) }};// Bringen Sie den Roboter zuvor auf Feet Flat, Feet Even (d. h. standStraightAnim).int walkBackwardAnim[][5] ={ // Metadaten. Das erste Element ist die Anzahl der Frames. { 8, 0, 0, 0, 0 }, // Nach links neigen, Füße gerade { 300, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipCentre(), RightFootDown(FOOT_DELTA) }, // Nach links neigen, Linker Fuß vorwärts { 300, LeftHipOut(HIP_DELTA), LeftFootUp(FOOT_DELTA), RightHipIn(HIP_DELTA), RightFootDown(FOOT_DELTA) }, // Füße flach, Linker Fuß vorwärts { 300, LeftHipOut(HIP_DELTA), LeftFootFlat(), RightHipIn(HIP_DELTA), RightFootFlat() }, // Nach rechts neigen, Linker Fuß nach vorne { 300, LeftHipOut(HIP_DELTA), LeftFootDown(FOOT_DELTA), RightHipIn(HIP_DELTA), RightFootUp(FOOT_DELTA) }, // Nach rechts neigen, Füße gerade { 300, LeftHipCentre (), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // Nach rechts neigen, Rechter Fuß nach vorne { 300, LeftHipIn(HIP_DELTA), LeftFootDown(FOOT_DELTA), RightHipOut(HIP_DELTA), RightFootUp(FOOT_DELTA) } , // Füße flach, Rechter Fuß nach vorne { 300, LeftHipIn(HIP_DELTA), LeftFootFlat(), RightHipOut(HIP_DELTA), RightFootFlat() }, // Nach links neigen, Rechter Fuß nach vorne { 300, LeftHipIn(HIP_DELTA .) ) . Das erste Element ist die Anzahl der Frames. { 2, 0, 0, 0, 0 }, // Nach links neigen, Füße gerade { 300, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipCentre(), RightFootDown(FOOT_DELTA) }, // Füße flach, Füße gerade { 300, LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat() }};// Bringen Sie den Roboter zuvor auf Feet Flat, Feet Even (dh standStraightAnim).int turnLeftAnim[][5] ={ / / Metadaten. Das erste Element ist die Anzahl der Frames. { 6, 0, 0, 0, 0 }, // Nach links neigen, Füße gerade { 300, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipCentre(), RightFootDown(FOOT_DELTA) }, // Nach links neigen, Links abbiegen Hüfte, rechte Hüfte drehen { 300, LeftHipIn(HIP_DELTA), LeftFootUp(FOOT_DELTA), RightHipIn(HIP_DELTA), RightFootDown(FOOT_DELTA)}, // Füße flach, Hüfte links drehen, Hüfte rechts drehen { 300, LeftHipIn(HIP_DELTA), LeftFootFlat (), RightHipIn(HIP_DELTA), RightFootFlat() }, // Nach rechts neigen, linke Hüfte drehen, rechte Hüfte drehen { 300, LeftHipIn(HIP_DELTA), LeftFootDown(FOOT_DELTA), RightHipIn(HIP_DELTA), RightFootUp(FOOT_DELTA) }, // Nach rechts neigen, Füße gerade { 300, LeftHipCentre(), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // Füße flach, Füße gerade { 300, LeftHipCentre(), LeftFootFlat(), RightHipCentre( ), RightFootFlat() }};// Bringen Sie den Roboter zuvor zu Feet Flat, Feet Even (dh standStraightAnim).int turnRightAnim[][5] ={ // Metadata. Das erste Element ist die Anzahl der Frames. { 6, 0, 0, 0 }, // Nach rechts neigen, Füße gerade { 300, LeftHipCentre(), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // Nach rechts neigen, Links abbiegen Hüfte, rechte Hüfte drehen { 300, LeftHipIn(HIP_DELTA), LeftFootDown(FOOT_DELTA), RightHipIn(HIP_DELTA), RightFootUp(FOOT_DELTA)}, // Füße flach, Hüfte links drehen, Hüfte rechts drehen { 300, LeftHipIn(HIP_DELTA), LeftFootFlat (), RightHipIn(HIP_DELTA), RightFootFlat() }, // Nach links neigen, linke Hüfte drehen, rechte Hüfte drehen { 300, LeftHipIn(HIP_DELTA), LeftFootUp(FOOT_DELTA), RightHipIn(HIP_DELTA), RightFootDown(FOOT_DELTA)}, // Nach links neigen, Füße gerade { 300, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipCentre(), RightFootDown(FOOT_DELTA) }, // Füße flach, Füße gerade { 300, LeftHipCentre(), LeftFootFlat(), RightHipCentre( ), RightFootFlat() }}; // Kopfschütteln anim. Schnell nach links rechts, um das Schütteln des Kopfes zu emulieren. Int shakeHeadAnim[][5] ={ // Metadata. Das erste Element ist die Anzahl der Frames. { 4, 0, 0, 0, 0 }, // Füße flach, Drehung nach links { 150, LeftHipOut(HIP_DELTA), LeftFootFlat(), RightHipIn(HIP_DELTA), RightFootFlat() }, // Füße flach, Füße gerade { 150 , LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat() }, // Füße flach, Drehung rechts { 150, LeftHipIn(HIP_DELTA), LeftFootFlat(), RightHipOut(HIP_DELTA), RightFootFlat() }, // Füße flach, Füße gerade { 150, LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat() } }; // Wobble anim. Neigen Sie nach links und rechts, um einen Spaß zu machen wobble.int wobbleAnim[][5] ={ // Metadata. Das erste Element ist die Anzahl der Frames. { 4, 0, 0, 0 }, // Neigung nach links, Füße gerade { 300, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipCentre(), RightFootDown(FOOT_DELTA) }, // Füße flach, Füße gerade { 300 , LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat() }, // Nach rechts neigen, Füße gerade { 300, LeftHipCentre(), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // Feet flach, Füße gerade { 300, LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat() } }; // Wobble linke Animation. Neige nach links und zurück.int wobbleLeftAnim[][5] ={ // Metadaten. Das erste Element ist die Anzahl der Frames. { 2, 0, 0, 0 }, // Nach links neigen, Füße gerade { 300, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipCentre(), RightFootDown(FOOT_DELTA) }, // Füße flach, Füße gerade { 300 , LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat() },}; // Rechtsanim wackeln. Nach rechts und zurück neigen.int wobbleRightAnim[][5] ={ // Metadaten. Das erste Element ist die Anzahl der Frames. { 2, 0, 0, 0 }, // Nach rechts neigen, Füße gerade { 300, LeftHipCentre(), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // Füße flach, Füße gerade { 300 , LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat() } }; // Tippen Sie auf die Füße anim. Tippen Sie auf beide feet.int tapFeetAnim[][5] ={ // Metadaten. Das erste Element ist die Anzahl der Frames. { 2, 0, 0, 0 }, // Beide Füße anheben, Füße gerade { 500, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // Füße flach, Füße gerade { 500, LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat() },}; // Tippen Sie mit dem linken Fuß anim.int tapLeftFootAnim[][5] ={ // Metadaten. Das erste Element ist die Anzahl der Frames. { 2, 0, 0, 0, 0 }, // Linken Fuß heben, Füße gerade { 500, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipCentre(), RightFootFlat() }, // Füße flach, Füße gerade { 500 , LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat() },}; // Tippen Sie mit dem rechten Fuß anim.int tapRightFootAnim[][5] ={ // Metadaten. Das erste Element ist die Anzahl der Frames. { 2, 0, 0, 0, 0 }, // Rechten Fuß heben, Füße gerade { 500, LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // Füße flach, Füße gerade { 500 , LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat() },};// Auf und ab springen anim.int bounceAnim[][5] ={ // Metadaten. Das erste Element ist die Anzahl der Frames. { 2, 0, 0, 0 }, // Beide Füße anheben, Füße gerade { 500, LeftHipCentre(), LeftFootDown(300), RightHipCentre(), RightFootDown(300) }, // Füße flach, Füße gerade { 500, LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat() },}; // Shake Legs Animation.int shakeLegsAnim[][5] ={ // Metadaten. Das erste Element ist die Anzahl der Frames. { 14, 0, 0, 0, 0 }, // Nach links neigen, Füße gerade { 300, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipCentre(), RightFootDown(FOOT_DELTA)}, // Nach links neigen, Rechte Hüfte in { 100, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipIn(HIP_DELTA), RightFootDown(FOOT_DELTA)}, // Nach links neigen, Füße gerade { 100, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipCentre(), RightFootDown(FOOT_DELTA) , // Nach links neigen, Rechte Hüfte aus { 100, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipOut(HIP_DELTA), RightFootDown(FOOT_DELTA) }, // Nach links neigen, Füße gerade { 100, LeftHipCentre(), LeftFootUp(FOOT_DELTA) , RightHipCentre(), RightFootDown(FOOT_DELTA) }, // Nach links neigen, Rechte Hüfte in { 100, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipIn(HIP_DELTA), RightFootDown(FOOT_DELTA) }, // Nach links neigen, Füße gerade { 100, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipCentre(), RightFootDown(FOOT_DELTA) }, // Füße flach, Füße gerade { 300, LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat() }, // Tilt-Rig ht, Feet Even { 300, LeftHipCentre(), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // Nach rechts neigen, Linke Hüfte in { 100, LeftHipIn(HIP_DELTA), LeftFootDown(FOOT_DELTA), RightHipCentre() , RightFootUp(FOOT_DELTA) }, // Nach rechts neigen, Füße gerade { 100, LeftHipCentre(), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // Nach rechts neigen, Linke Hüfte aus { 100, LeftHipOut(HIP_DELTA .) ), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // Nach rechts neigen, Füße gerade { 100, LeftHipCentre(), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // Füße flach , Feet Even { 300, LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat() } }; // Shake Left Leg Animation.int shakeLeftLegAnim[][5] ={ // Metadata. Das erste Element ist die Anzahl der Frames. { 12, 0, 0, 0 }, // Nach rechts neigen, Füße gerade { 300, LeftHipCentre(), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // Nach rechts neigen, Linke Hüfte in { 100, LeftHipIn(HIP_DELTA), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // Nach rechts neigen, Füße gerade { 100, LeftHipCentre(), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) , // Nach rechts neigen, Linke Hüfte nach außen { 100, LeftHipOut(HIP_DELTA), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // Nach rechts neigen, Füße gerade { 100, LeftHipCentre(), LeftFootDown(FOOT_DELTA) , RightHipCentre(), RightFootUp(FOOT_DELTA) }, // Nach rechts neigen, Linke Hüfte in { 100, LeftHipIn(HIP_DELTA), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // Nach rechts neigen, Füße gerade { 100, LeftHipCentre(), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA)}, // Nach rechts neigen, Linke Hüfte aus { 100, LeftHipOut(HIP_DELTA), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFo otUp(FOOT_DELTA) }, // Neige rechts, Feet gerade { 100, LeftHipCentre(), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // Neige rechts, Linke Hüfte in { 100, LeftHipIn(HIP_DELTA) , LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // Nach rechts neigen, Füße gerade { 100, LeftHipCentre(), LeftFootDown(FOOT_DELTA), RightHipCentre(), RightFootUp(FOOT_DELTA) }, // Füße flach, Füße gerade { 300, LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat() } }; // Rechtes Bein schütteln Animation.int shakeRightLegAnim[][5] ={ // Metadaten. Das erste Element ist die Anzahl der Frames. { 12, 0, 0, 0 }, // Nach links neigen, Füße gerade { 300, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipCentre(), RightFootDown(FOOT_DELTA) }, // Nach links neigen, Rechte Hüfte in { 100, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipIn(HIP_DELTA), RightFootDown(FOOT_DELTA)}, // Nach links neigen, Füße gerade { 100, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipCentre(), RightFootDown(FOOT_DELTA) , // Nach links neigen, Rechte Hüfte aus { 100, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipOut(HIP_DELTA), RightFootDown(FOOT_DELTA) }, // Nach links neigen, Füße gerade { 100, LeftHipCentre(), LeftFootUp(FOOT_DELTA) , RightHipCentre(), RightFootDown(FOOT_DELTA) }, // Nach links neigen, Rechte Hüfte in { 100, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipIn(HIP_DELTA), RightFootDown(FOOT_DELTA) }, // Nach links neigen, Füße gerade { 100, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipCentre(), RightFootDown(FOOT_DELTA)}, // Nach links neigen, Rechte Hüfte aus { 100, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipOut(HIP_DELTA), RightFootDown (FOOT_DELTA) }, // Nach links neigen, Füße gerade { 100, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipCentre(), RightFootDown(FOOT_DELTA) }, // Nach links neigen, Rechte Hüfte in { 100, LeftHipCentre(), LeftFootUp (FOOT_DELTA), RightHipIn(HIP_DELTA), RightFootDown(FOOT_DELTA) }, // Neige nach links, Füße gerade { 100, LeftHipCentre(), LeftFootUp(FOOT_DELTA), RightHipCentre(), RightFootDown(FOOT_DELTA) }, // Füße flach, Feet gerade { 300, LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat() }, };//------------------------- -------------------------------------------------- -------// Spezielle dynamische Animationsdaten zum Einstellen/Tweening von Servopositionen.//---------------------------- -------------------------------------------------- ----// Dies sind 2 spezielle Animationsdaten, die wir für die Funktion SetServos() verwenden. Sie haben// einen einzigen Rahmen. Diese ändern die Daten in diesen Animationsdaten und spielen sie ab, um // die servos.int zu verschieben setServosAnim1[][5] ={ // Metadata. Das erste Element ist die Anzahl der Frames. { 1, 0, 0, 0, 0 }, // Nach links neigen, Füße gerade { 0, LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat() }};int setServosAnim2[][5] ={ / / Metadaten. Das erste Element ist die Anzahl der Frames. { 1, 0, 0, 0, 0 }, // Tilt left, Feet even { 0, LeftHipCentre(), LeftFootFlat(), RightHipCentre(), RightFootFlat() }};//----------------------------------------------------------------------------------// Servo Variables//----------------------------------------------------------------------------------Servo servoLeftHip;Servo servoLeftFoot;Servo servoRightHip;Servo servoRightFoot;//----------------------------------------------------------------------------------// State variables for playing animations.//----------------------------------------------------------------------------------// Milliseconds between animation updates.const int millisBetweenAnimUpdate =20;// Time when we did the last animation update.long timeAtLastAnimUpdate;// Related to currently playing anim.int (*currAnim)[5]; // Current animation we're playing.int (*finishAnim)[5]; // Animation to play when the currAnim finishes or is stopped.long timeAtStartOfFrame; // millis() at last keyframe - frame we're lerping fromint targetFrame; // Frame we are lerping toint animNumLoops; // Number of times to play the animation. -1 means loop forever.char animCompleteStr[3] ="--"; // This is a 2 character string. When the anim is complete, // we print out the status as "<" + animComplereStr + ">".// Related to anim queue. I.e. Next anim to play.bool animInProgress; // Whether an animation is playingint (*nextAnim)[5]; // This is the next animation to play once the current one is done. // i.e. It's like a queue of size 1! // If curr is non-looping, we play this at the end of the current anim. // If curr is looping, this starts at the end of the current loop, // replacing curr anim. // If nothing is playing, this starts right away. int (*nextFinishAnim)[5]; // This is the finish animation for the queued animation.int nextAnimNumLoops; // Number of times to play the animation. -1 means loop forever.char nextAnimCompleteStr[3] ="--"; // This is a 2 character string. When the anim is complete, // we print out the status as "<" + animComplereStr + ">".bool interruptInProgressAnim; // Whether to change anim immediately, interrupting the current one.// Curr servo positionsint currLeftHip;int currLeftFoot;int currRightHip;int currRightFoot;// Servo positions at start of current keyframeint startLeftHip;int startLeftFoot;int startRightHip;int startRightFoot;//-------------------------------------------------------------------------------// Parser Variables//-------------------------------------------------------------------------------// Constant delimiter tag charsconst char START_CHAR ='<';const char END_CHAR ='>';const char SEP_CHAR =',';// Constants and a variable for the parser state.const int PARSER_WAITING =0; // Waiting for '<' to start parsing.const int PARSER_COMMAND =1; // Reading the command string.const int PARSER_PARAM1 =2; // Reading param 1.const int PARSER_PARAM2 =3; // Reading param 2.const int PARSER_PARAM3 =4; // Reading param 3.const int PARSER_PARAM4 =5; // Reading param 3.const int PARSER_PARAM5 =6; // Reading param 3.const int PARSER_EXECUTE =7; // Finished parsing a command, so execute it.// Current parser state.int currParserState =PARSER_WAITING; // String for storing the command. 2 chars for the command and 1 char for '\0'.// We store the command here as we're parsing.char currCmd[3] ="--";// For tracking which letter we are in the command.int currCmdIndex;// Max command length.const int CMD_LENGTH =2;// Current param values. Store them here after we parse them.int currParam1Val;int currParam2Val;int currParam3Val;int currParam4Val;int currParam5Val;// Variable for tracking which digit we're parsing in a param.// We use this to convert the single digits back into a decimal value.int currParamIndex;// Whether the current param is negative.boolean currParamNegative;// Max parameter length. Stop parsing if it exceeds this.const int MAX_PARAM_LENGTH =6;//===============================================================================// Arduino setup() and loop().//===============================================================================void setup() { // Setup the main serial port softwareSerial.begin(SERIAL_SPEED); // Setup the Servos servoLeftHip.attach( SERVO_LEFT_HIP, LEFT_HIP_MIN, LEFT_HIP_MAX); servoLeftFoot.attach( SERVO_LEFT_FOOT, LEFT_FOOT_MIN, LEFT_FOOT_MAX); servoRightHip.attach( SERVO_RIGHT_HIP, RIGHT_HIP_MIN, RIGHT_HIP_MAX); servoRightFoot.attach(SERVO_RIGHT_FOOT, RIGHT_FOOT_MIN, RIGHT_FOOT_MAX); // Set things up for the parser. setup_Parser(); // Set things up for the animation code. setup_Animation();}void loop() { // Update the parser. loop_Parser(); // Update the animation. loop_Animation();}//===============================================================================// Related to the parser//===============================================================================// Sets up the parser stuff. Called in setup(). Should not be called elsewhere.void setup_Parser(){ // Wait for first command. currParserState =PARSER_WAITING; // Print this response to say we've booted and are ready. softwareSerial.println("");}// Loop() for the parser stuff. Called in loop(). Should not be called elsewhere.void loop_Parser(){ //--------------------------------------------------------- // PARSER // // If there is data, parse it and process it. //--------------------------------------------------------- // Read from pin serial port and write it out on USB port. if (softwareSerial.available()> 0) { char c =softwareSerial.read(); // If we're in WAITING state, look for the START_CHAR. if (currParserState ==PARSER_WAITING) { // If it's the START_CHAR, move out of this state... if (c ==START_CHAR) { // Start parsing the command. currParserState =PARSER_COMMAND; // Reset thing ready for parsing currCmdIndex =0; currCmd[0] ='-'; currCmd[1] ='-'; currParam1Val =0; currParam2Val =0; currParam3Val =0; currParam4Val =0; currParam5Val =0; } // Otherwise, stay in this state. } // In the state to look for the command. else if (currParserState ==PARSER_COMMAND) { // Else if it's a separator, parse parameter 1. But make sure it's not // empty, or else it's a parse error. if (c ==SEP_CHAR) { if (currCmdIndex ==CMD_LENGTH) { currParserState =PARSER_PARAM1; currParamIndex =0; currParamNegative =false; } else { currParserState =PARSER_WAITING; } } // Else if it's the end char, there are no parameters, so we're ready to // process. But make sure it's not empty. Otherwise, it's a parse error. else if (c ==END_CHAR) { if (currCmdIndex ==CMD_LENGTH) { currParserState =PARSER_EXECUTE; } else { currParserState =PARSER_WAITING; } } // If we've got too many letters here, we have a parse error, // so abandon and go back to PARSER_WAITING else if ( (currCmdIndex>=CMD_LENGTH) || (c <'A') || (c> 'Z') ) { currParserState =PARSER_WAITING; } // Store the current character. else { currCmd[currCmdIndex] =c; currCmdIndex++; } } // In the state to parse param 1. else if (currParserState ==PARSER_PARAM1) { // Else if it's a separator, parse parameter 1. if (c ==SEP_CHAR) { if (currParamNegative) { currParam1Val =-1 * currParam1Val; } currParserState =PARSER_PARAM2; currParamIndex =0; currParamNegative =false; } // Else if it's the end char, there are no parameters, so we're ready to // process. else if (c ==END_CHAR) { if (currParamNegative) { currParam1Val =-1 * currParam1Val; } currParserState =PARSER_EXECUTE; } // Check for negative at the start. else if ( (currParamIndex ==0) &&(c =='-') ) { currParamNegative =true; currParamIndex++; } // If it's too long, or the character is not a digit, then it's // a parse error, so abandon and go back to PARSER_WAITING. else if ( (currParamIndex>=MAX_PARAM_LENGTH) || (c <'0') || (c> '9') ) { currParserState =PARSER_WAITING; } // It's a valid character, so process it. else { // Shift existing value across and add new digit at the bottom. int currDigitVal =c - '0'; currParam1Val =(currParam1Val * 10) + currDigitVal; currParamIndex++; } } // In the state to parse param 2. else if (currParserState ==PARSER_PARAM2) { // Else if it's a separator, parse parameter 2. if (c ==SEP_CHAR) { if (currParamNegative) { currParam2Val =-1 * currParam2Val; } currParserState =PARSER_PARAM3; currParamIndex =0; currParamNegative =false; } // Else if it's the end char, there are no parameters, so we're ready to // process. else if (c ==END_CHAR) { if (currParamNegative) { currParam2Val =-1 * currParam2Val; } currParserState =PARSER_EXECUTE; } // Check for negative at the start. else if ( (currParamIndex ==0) &&(c =='-') ) { currParamNegative =true; currParamIndex++; } // If it's too long, or the character is not a digit, then it's // a parse error, so abandon and go back to PARSER_WAITING. else if ( (currParamIndex>=MAX_PARAM_LENGTH) || (c <'0') || (c> '9') ) { currParserState =PARSER_WAITING; } // It's a valid character, so process it. else { // Shift existing value across and add new digit at the bottom. int currDigitVal =c - '0'; currParam2Val =(currParam2Val * 10) + currDigitVal; currParamIndex++; } } // In the state to parse param 3. else if (currParserState ==PARSER_PARAM3) { // Else if it's a separator, parse parameter 2. if (c ==SEP_CHAR) { if (currParamNegative) { currParam3Val =-1 * currParam3Val; } currParserState =PARSER_PARAM4; currParamIndex =0; currParamNegative =false; } // Else if it's the end char, there are no parameters, so we're ready to // process. else if (c ==END_CHAR) { if (currParamNegative) { currParam3Val =-1 * currParam3Val; } currParserState =PARSER_EXECUTE; } // Check for negative at the start. else if ( (currParamIndex ==0) &&(c =='-') ) { currParamNegative =true; currParamIndex++; } // If it's too long, or the character is not a digit, then it's // a parse error, so abandon and go back to PARSER_WAITING. else if ( (currParamIndex>=MAX_PARAM_LENGTH) || (c <'0') || (c> '9') ) { currParserState =PARSER_WAITING; } // It's a valid character, so process it. else { // Shift existing value across and add new digit at the bottom. int currDigitVal =c - '0'; currParam3Val =(currParam3Val * 10) + currDigitVal; currParamIndex++; } } // In the state to parse param 4. else if (currParserState ==PARSER_PARAM4) { // Else if it's a separator, parse parameter 2. if (c ==SEP_CHAR) { if (currParamNegative) { currParam4Val =-1 * currParam4Val; } currParserState =PARSER_PARAM5; currParamIndex =0; currParamNegative =false; } // Else if it's the end char, there are no parameters, so we're ready to // process. else if (c ==END_CHAR) { if (currParamNegative) { currParam4Val =-1 * currParam4Val; } currParserState =PARSER_EXECUTE; } // Check for negative at the start. else if ( (currParamIndex ==0) &&(c =='-') ) { currParamNegative =true; currParamIndex++; } // If it's too long, or the character is not a digit, then it's // a parse error, so abandon and go back to PARSER_WAITING. else if ( (currParamIndex>=MAX_PARAM_LENGTH) || (c <'0') || (c> '9') ) { currParserState =PARSER_WAITING; } // It's a valid character, so process it. else { // Shift existing value across and add new digit at the bottom. int currDigitVal =c - '0'; currParam4Val =(currParam4Val * 10) + currDigitVal; currParamIndex++; } } // In the state to parse param 5. else if (currParserState ==PARSER_PARAM5) { // If it's the end char, there are no parameters, so we're ready to // process. if (c ==END_CHAR) { if (currParamNegative) { currParam5Val =-1 * currParam5Val; } currParserState =PARSER_EXECUTE; } // Check for negative at the start. else if ( (currParamIndex ==0) &&(c =='-') ) { currParamNegative =true; currParamIndex++; } // If it's too long, or the character is not a digit, then it's // a parse error, so abandon and go back to PARSER_WAITING. else if ( (currParamIndex>=MAX_PARAM_LENGTH) || (c <'0') || (c> '9') ) { currParserState =PARSER_WAITING; } // It's a valid character, so process it. else { // Shift existing value across and add new digit at the bottom. int currDigitVal =c - '0'; currParam5Val =(currParam5Val * 10) + currDigitVal; currParamIndex++; } } //--------------------------------------------------------- // PARSER CODE HANDLER (Still part of Parser, but section that // processes completed commands) // // If the most recently read char completes a command, // then process the command, and clear the state to // go back to looking for a new command. // // The parsed items are stored in:// currCmd, currParam1Val, currParam2Val, currParam3Val, // currParam4Val, currParam5Val //--------------------------------------------------------- if (currParserState ==PARSER_EXECUTE) { // Ready/OK Check: if ((currCmd[0] =='O') &&(currCmd[1] =='K')) { softwareSerial.println(""); } // Set Servo: // time - time to tween to specified angles // leftHip - microsecs from centre. -ve is hip in, +ve is hip out // leftFoot - microsecs from flat. -ve is foot down, +ve is foot up // rightHip - microsecs from centre. -ve is hip in, +ve is hip out // rightFoot - microsecs from flat. -ve is foot down, +ve is foot up else if ((currCmd[0] =='S') &&(currCmd[1] =='V')) { int tweenTime =currParam1Val; if (currParam1Val <0) { tweenTime =0; } SetServos(tweenTime, currParam2Val, currParam3Val, currParam4Val, currParam5Val, "SV"); } // Stop/Reset:, Stops current anim. Also can be used to put robot into reset position. else if ((currCmd[0] =='S') &&(currCmd[1] =='T')) { StopAnim("ST"); } // Stop Immediate: else if ((currCmd[0] =='S') &&(currCmd[1] =='I')) { StopAnimImmediate("SI"); } // Forward:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='F') &&(currCmd[1] =='W')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(walkForwardAnim, walkEndAnim, numTimes, "FW"); } // Backward:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='B') &&(currCmd[1] =='W')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(walkBackwardAnim, walkEndAnim, numTimes, "BW"); } // Turn Left:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='L') &&(currCmd[1] =='T')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(turnLeftAnim, NULL, numTimes, "LT"); } // Turn Right:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='R') &&(currCmd[1] =='T')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(turnRightAnim, NULL, numTimes, "RT"); } // Shake Head:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='S') &&(currCmd[1] =='X')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(shakeHeadAnim, NULL, numTimes, "SX"); } // Bounce:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='B') &&(currCmd[1] =='X')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(bounceAnim, NULL, numTimes, "BX"); } // Wobble:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='W') &&(currCmd[1] =='X')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(wobbleAnim, NULL, numTimes, "WX"); } // Wobble Left:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='W') &&(currCmd[1] =='Y')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(wobbleLeftAnim, NULL, numTimes, "WY"); } // Wobble Right:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='W') &&(currCmd[1] =='Z')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(wobbleRightAnim, NULL, numTimes, "WZ"); } // Tap Feet:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='T') &&(currCmd[1] =='X')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(tapFeetAnim, NULL, numTimes, "TX"); } // Tap Left Foot:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='T') &&(currCmd[1] =='Y')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(tapLeftFootAnim, NULL, numTimes, "TY"); } // Tap Right Foot:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='T') &&(currCmd[1] =='Z')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(tapRightFootAnim, NULL, numTimes, "TZ"); } // Shake Legs:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='L') &&(currCmd[1] =='X')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(shakeLegsAnim, NULL, numTimes, "LX"); } // Shake Left Leg:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='L') &&(currCmd[1] =='Y')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(shakeLeftLegAnim, NULL, numTimes, "LY"); } // Shake Right Leg:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='L') &&(currCmd[1] =='Z')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(shakeRightLegAnim, NULL, numTimes, "LZ"); } //-------------------------------------------------- // Clear the state and wait for the next command! // This must be done! //-------------------------------------------------- currParserState =PARSER_WAITING; } }}//===============================================================================// Related to playing servo animations.//===============================================================================// Call this to play the given animation once. Pass in NULL if there is no finishAnim.void PlayAnim(int animToPlay[][5], int finishAnim[][5], const char *completeStr){ // Put this in the queue. PlayAnimNumTimes(animToPlay, finishAnim, 1, completeStr);}// Call this to loop the given animation. Pass in NULL if there is no finishAnim.void LoopAnim(int animToPlay[][5], int finishAnim[][5], const char *completeStr){ // Put this in the queue. PlayAnimNumTimes(animToPlay, finishAnim, -1, completeStr);}// Call this to play the given animation the specified number of times. // -1 number of times will make it loop forever.// Pass in NULL if there is no finishAnim.void PlayAnimNumTimes(int animToPlay[][5], int finishAnim[][5], int numTimes, const char *completeStr){ // Put this in the queue. nextAnim =animToPlay; nextFinishAnim =finishAnim; nextAnimNumLoops =numTimes; // Save the completeStr if (completeStr ==NULL) { nextAnimCompleteStr[0] ='-'; nextAnimCompleteStr[1] ='-'; } else { nextAnimCompleteStr[0] =completeStr[0]; nextAnimCompleteStr[1] =completeStr[1]; }}// Stop after the current animation.void StopAnim(const char *completeStr){ // Put this in the queue. PlayAnimNumTimes(standStraightAnim, NULL, 1, completeStr);}// Stop immediately and lerp robot to zero position, interrupting // any animation that is in progress.void StopAnimImmediate(const char *completeStr){ // Put this in the queue. interruptInProgressAnim =true; PlayAnimNumTimes(standStraightAnim, NULL, 1, completeStr);}// Moves servos to the specified positions. Time 0 will make it immediate. Otherwise,// it'll tween it over a specified time.// For positions, 0 means centered.// For hips, -ve is hip left, +ve is hip right// For feet, -ve is foot down, +ve is foot upvoid SetServos(int tweenTime, int leftHip, int leftFoot, int rightHip, int rightFoot, const char* completeStr){ // Save the completeStr if (completeStr ==NULL) { nextAnimCompleteStr[0] ='-'; nextAnimCompleteStr[1] ='-'; } else { nextAnimCompleteStr[0] =completeStr[0]; nextAnimCompleteStr[1] =completeStr[1]; } // Decide which tween data we use. We don't want to over-write the one that is // in progress. We have and reuse these to keep memory allocation fixed. int (*tweenServoData)[5]; if (currAnim !=setServosAnim1) { tweenServoData =setServosAnim1; } else { tweenServoData =setServosAnim2; } // Set the tween information into the animation data. tweenServoData[1][TWEEN_TIME_VALUE] =tweenTime; tweenServoData[1][LEFT_HIP_VALUE] =LeftHipIn(leftHip); tweenServoData[1][LEFT_FOOT_VALUE] =LeftFootUp(leftFoot); tweenServoData[1][RIGHT_HIP_VALUE] =RightHipIn(rightHip); tweenServoData[1][RIGHT_FOOT_VALUE] =RightFootUp(rightFoot); // Queue this tween to be played next. PlayAnim(tweenServoData, NULL, completeStr);}// Set up variables for animation. This is called in setup(). Should be not called by anywhere else.void setup_Animation(){ // Set the servos to the feet flat, feet even position. currLeftHip =LEFT_HIP_CENTRE; currLeftFoot =LEFT_FOOT_CENTRE; currRightHip =RIGHT_HIP_CENTRE; currRightFoot =RIGHT_FOOT_CENTRE; UpdateServos(); // Set the "start" positions to the current ones. So, when // we pay the next anim, we will tween from the current positions. startLeftHip =currLeftHip; startLeftFoot =currLeftFoot; startRightHip =currRightHip; startRightFoot =currRightFoot; // No animation is playing yet, and nothing in the queue yet. timeAtLastAnimUpdate =millis(); animInProgress =false; interruptInProgressAnim =false; currAnim =NULL; finishAnim =NULL; nextAnim =NULL; nextFinishAnim =NULL;}// Loop function for processing animation. This is called in every loop(). Should be be called by anywhere else.//// NOTE:The way looping animations work is that they basically add themselves back to the queue// when a cycle is done, and if there's nothing already queued up! This way, looping animations// work in a similar way to single-play animations, and fits into the queueing system.void loop_Animation(){ // Get the time at the start of this frame. long currTime =millis(); //-------------------------------------------------------------------------------------- // Decide if we want to perform the animation update. We don't execute this every frame. //-------------------------------------------------------------------------------------- if (timeAtLastAnimUpdate + millisBetweenAnimUpdate> currTime) { // Not yet time to do an anim update, so jump out. Rückkehr; } else { // We reset the timer, and then proceed below to handle the current anim update. timeAtLastAnimUpdate =currTime; } //-------------------------------------------------------------------------------------- // Decide if we need to setup and start a new animation. We do if there's no anim // playing or we've been asked to interrupt the anim. //-------------------------------------------------------------------------------------- if ( (nextAnim !=NULL) &&(!animInProgress || interruptInProgressAnim) ) { // If this was an interrupt, we also set the "start" servo positions // to the current ones. This way, the animation system will tween from the // current positions. if (interruptInProgressAnim) { // This is the place to notify someone of an animation finishing after getting interrupted // Print the command string we just finished. -1 parameter indicates it was interrupted. softwareSerial.print("<"); softwareSerial.print(animCompleteStr); softwareSerial.println(",-1>"); // Set the "start" positions to the current ones. So, when // we pay the next anim, we will tween from the current positions. startLeftHip =currLeftHip; startLeftFoot =currLeftFoot; startRightHip =currRightHip; startRightFoot =currRightFoot; // We've handled any interrupt request, so clear the flag. interruptInProgressAnim =false; } // Store the animation we are now playing. currAnim =nextAnim; finishAnim =nextFinishAnim; animCompleteStr[0] =nextAnimCompleteStr[0]; animCompleteStr[1] =nextAnimCompleteStr[1]; nextAnim =NULL; // Queue is cleared. nextFinishAnim =NULL; nextAnimCompleteStr[0] ='-'; nextAnimCompleteStr[1] ='-'; // Record the number of times to play the animation. animNumLoops =nextAnimNumLoops; // Treat current time as start of frame for the initial lerp to the first frame. timeAtStartOfFrame =currTime; // Set the frame counters. targetFrame =1; // First frame we are lerping to. Index 0 is metadata, so skip. // An animation is now in progress animInProgress =true; } //-------------------------------------------------------------------------------------- // If we are currently playing an animation, then update the animation state and the // servo positions. //-------------------------------------------------------------------------------------- if (animInProgress) { // Determine if we need to switch to the next frame. int timeInCurrFrame =currTime - timeAtStartOfFrame; if (timeInCurrFrame> currAnim[targetFrame][TWEEN_TIME_VALUE]) { // Set the servo positions to the targetFrame's values. // We only set this if the value is> 0. -ve values means that // the current target keyframe did not alter that servos position. if (currAnim[targetFrame][LEFT_HIP_VALUE]>=0) { currLeftHip =currAnim[targetFrame][LEFT_HIP_VALUE]; } if (currAnim[targetFrame][LEFT_FOOT_VALUE]>=0) { currLeftFoot =currAnim[targetFrame][LEFT_FOOT_VALUE]; } if (currAnim[targetFrame][RIGHT_HIP_VALUE]>=0) { currRightHip =currAnim[targetFrame][RIGHT_HIP_VALUE]; } if (currAnim[targetFrame][RIGHT_FOOT_VALUE]>=0) { currRightFoot =currAnim[targetFrame][RIGHT_FOOT_VALUE]; } UpdateServos(); // These current values are now the start of frame values. startLeftHip =currLeftHip; startLeftFoot =currLeftFoot; startRightHip =currRightHip; startRightFoot =currRightFoot; // Now, we try to move to the next frame. // - If there is a next frame, set that as the new target, and proceed. // - If there's no next frame, but it's looping, we re-add this animation // to the queue. // - If there's no next frame, and this is not looping, we stop animating. // (Remember that targetFrame is 1-based since the first element of the animation // data array is metadata) // Increment targetFrame, and reset time in the current frame. targetFrame++; timeAtStartOfFrame =currTime; // If there is no next frame, we stop this current animation. // If it is looping, then we re-queue the current animation if the queue is empty. if (targetFrame> NumOfFrames(currAnim)) { // Stop the current animation. animInProgress =false; // If we're looping forever, and there's no next anim, re-queue the // animation if the queue is empty. if ((animNumLoops <0) &&(nextAnim ==NULL)) { LoopAnim(currAnim, finishAnim, animCompleteStr); } // If we're looping forever, and there is something in the queue, then // finish the animation and proceed. else if ((animNumLoops <0) &&(nextAnim !=NULL)) { if (finishAnim !=NULL) { // Switch to the finish anim. currAnim =finishAnim; finishAnim =NULL; // Record the number of times to play the animation. animNumLoops =1; // Treat current time as start of frame for the initial lerp to the first frame. timeAtStartOfFrame =currTime; // Set the frame counters. targetFrame =1; // First frame we are lerping to. Index 0 is metadata, so skip. // An animation is now in progress animInProgress =true; } else { // We've stopped, so can notify if needed. // Print the command string we just finished. softwareSerial.print("<"); softwareSerial.print(animCompleteStr); softwareSerial.println(">"); } } // If we're looping a limited number of times, and there's no next anim, // re-queue the animation if the queue is empty. else if ((animNumLoops> 1) &&(nextAnim ==NULL)) { PlayAnimNumTimes(currAnim, finishAnim, animNumLoops-1, animCompleteStr); } // In this case, numAnimLoops is 1, this is the last loop through, so // we're done. We play the finishAnim first if needed. else { // If there is a finish animation, switch to that animation. if (finishAnim !=NULL) { // Switch to the finish anim. currAnim =finishAnim; finishAnim =NULL; // Record the number of times to play the animation. animNumLoops =1; // Treat current time as start of frame for the initial lerp to the first frame. timeAtStartOfFrame =currTime; // Set the frame counters. targetFrame =1; // First frame we are lerping to. Index 0 is metadata, so skip. // An animation is now in progress animInProgress =true; } // Otherwise, we're done! We've played the finishAnim if there was one. else { // Print the command string we just finished. softwareSerial.print("<"); softwareSerial.print(animCompleteStr); softwareSerial.println(">"); } } } } // If we're still animating (i.e. the previous check didn't find that // we've finished the current animation), then proceed. if (animInProgress) { // Set the servos per data in the current frame. We only update the servos that have target // microsecond values> 0. This is to support the feature where we leave a servo at its // existing position if an animation data item is -1. float frameTimeFraction =(currTime - timeAtStartOfFrame) / ((float) currAnim[targetFrame][TWEEN_TIME_VALUE]); if (currAnim[targetFrame][LEFT_HIP_VALUE]>=0) { currLeftHip =startLeftHip + ((currAnim[targetFrame][LEFT_HIP_VALUE] - startLeftHip) * frameTimeFraction); } if (currAnim[targetFrame][LEFT_FOOT_VALUE]>=0) { currLeftFoot =startLeftFoot + ((currAnim[targetFrame][LEFT_FOOT_VALUE] - startLeftFoot) * frameTimeFraction); } if (currAnim[targetFrame][RIGHT_HIP_VALUE]>=0) { currRightHip =startRightHip + ((currAnim[targetFrame][RIGHT_HIP_VALUE] - startRightHip) * frameTimeFraction); } if (currAnim[targetFrame][RIGHT_FOOT_VALUE]>=0) { currRightFoot =startRightFoot + ((currAnim[targetFrame][RIGHT_FOOT_VALUE] - startRightFoot) * frameTimeFraction); } UpdateServos(); } }}// Move all the servo to the positions set in the curr... variables.// In the code, we update those variables and then call this to set the servos.void UpdateServos(){ servoLeftHip.writeMicroseconds(currLeftHip); servoLeftFoot.writeMicroseconds(currLeftFoot); servoRightHip.writeMicroseconds(currRightHip); servoRightFoot.writeMicroseconds(currRightFoot);}// Return the number of frames in the given animation data.// Have this helper function to avoid the "magic number" reference of animData[0][0].int NumOfFrames(int animData[][5]){ return animData[0][0];}

Schaltpläne


Herstellungsprozess

  1. Raspberry Pi Roboter über Bluetooth gesteuert
  2. DIY LUMAZOID Arduino Music Visualiser
  3. Arduino Digital Dice
  4. DIY 37 LED-Roulette-Spiel
  5. DIY-Voltmeter mit Arduino und Smartphone
  6. Sprachgesteuerter Roboter
  7. Arduino-gesteuerter Klavierroboter:PiBot
  8. NeoMatrix Arduino Pong
  9. DIY Arduino Roboterarm – gesteuert durch Handgesten
  10. 4-Rad-Roboter mit Arduino-Steuerung mit Dabble