Servo Vlinder Prototype Voor Malenia Cosplay
by EmmaVv in Circuits > Arduino
83 Views, 1 Favorites, 0 Comments
Servo Vlinder Prototype Voor Malenia Cosplay
Voor dit project heb ik een prototype gemaakt van vlinders die ik uiteindelijk ga gebruiken voor mijn Malenia, goddess of rot cosplay. Malenia komt uit de game Elden ring, één van mijn favoriete games. Malenia heeft grote vleugels waarop veel levende vlinders zitten. In de tweede cutscene van haar bossfight zie je dat er veel vlinders van haar af vliegen. Met dit project wou ik die levenden vlinders namaken met behulp van Arduino. Hiermee geef ik mijn cosplay extra dimensie en kan ik de cutscene nabootsen in het echt
Supplies
Elektronisch
16 stokjes uit 3D printer
16 Servo's.
Arduino UNO r3.
2 ORTONIC 16 kanaals 12-bit PWM Servo Driver I2C.
Voeding
USB-kabel
Voedingskabel
Materialen afwerking
MDF plaat (22,5 cm x 75 cm x 0,6 cm)
3 Houten pootjes (11 cm x 2 cm x 6 cm)
Dik mat papier
Stickerpapier
Tie wraps
Blokje voor de Tie wraps
Dubbelzijdige tape
Talens Expert Acrylic verf
kleine kwast
Gereedschap
Hamer
Spijkers
Zaag
Workmate
Boormachine
Printer
3D printer
Hot glue
Secondelijm
Schaar
Schroevendraaier
Soldeerbout
Soldeertin
Wacom tekentablet
Software
Autodesk fusion 360
Arduino IDE 2.3.0
Photoshop
Hoorcollege Servo
Tijdens een hoorcollege over Arduino's kregen we de opdracht om te spelen met een servo motor. Daarvoor kregen we deze code. Terwijl ik met de waardes aan het spelen was, had ik het idee om met servo's een project te maken. Ik heb altijd al een cosplay willen maken, dus ik was op zoek naar karakters waarbij ik makkelijk een bewegend component kon nabootsen. Ik kwam al snel op Elden ring, maar na wat onderzoek had ik bedacht dat ik Malenia's vleugels wou maken. Eerst wou ik dat de vleugels zelf bewogen, maar dat bleek niet zo realistisch te zijn vanwege de grootte van de motors en accu die ik daarvoor nodig zou hebben. Dus had ik bedacht dat ik met servo's de vlinders wou nabootsen die op de vleugels van Malenia zitten. En met de kennis die ik deze hoorcollege had opgelopen ben ik aan de slag gegaan met het codere van de vlinders.
#include <Servo.h>
Servo servo1;
void setup() {
Serial.begin(9600);
pinMode(A0, INPUT);
servo1.attach(6);
}
void loop() {
int sensorValue = analogRead(A0);
int servoPos = map(sensorValue, 0, 1023, 0, 180);
servo1.write(servoPos);
delay(50);
}
Beginnen Code
Dit is de code voor het eerste vlinder concept. Met deze code wou ik ervoor zorgen dat de vlinders op een zo natuurlijk mogelijke manier bewogen. Hieronder staat de code met notities.
Animaties = De mogelijke animaties die een vlinder kan afpelen.
Iteraties = Hoe vaak een vlinder bepaalde animaties achter elkaar afspeelt.
Steps = Animatieframes
// Servo motor library importeren
#include <Servo.h>
// Servo motor aanmaken
Servo myservo;
// Type definiëren voor servodata
typedef struct servoData {
uint8_t currentAnimationId; // Animatietype
uint8_t currentAnimationStep; // Huidige animatieframe
int currentStepWaitTime; // Wachttijd van de huidige animatieframe
uint8_t currentIteration; // Huidige iteratie
int currentIterationWaitTime; // Huidige wachttijd tijdens iteratie
uint8_t iterationCount; // Aantal iteraties
int iterationWaitTime; // Wachttijd tussen iteraties
uint8_t animationSpeedup; // Animatie versneller
int currentAnimationWaitTime; // Wachttijd tussen animaties
} servoData;
// Animaties
const uint8_t animations[][2] =
{
{2,50},{4,50},{6,50},{8,50},{10,50},{12,50},{14,50},{16,15},{18,15},{20,15},{22,15},{24,15},{26,15},{28,15},{30,15}, // animation 1, {positie, wachttijd}
{32,15},{34,10},{36,10},{38,10},{40,10},{42,15},{44,15},{46,15},{48,15},{50,15},{52,50},{54,50},{56,50},{58,50},{60,50}, // animation 1
{60,50},{58,50},{56,50},{54,50},{52,50},{50,15},{48,15},{46,15},{44,15},{42,15},{40,10},{38,10},{36,10},{34,10},{32,15}, // animation 1
{30,15},{28,15},{26,15},{24,15},{22,15},{20,15},{18,15},{16,15},{14,50},{12,50},{10,50},{8,50},{6,50},{4,50},{2,50}, // animation 1
{2,50},{4,50},{6,50},{8,50},{10,50},{12,50},{14,50},{16,15},{18,15},{20,15},{22,15},{24,15},{26,15},{28,15},{30,15}, // animation 2
{32,15},{34,10},{36,10},{38,10},{40,10}, // animation 2
{40,10},{38,10},{36,10},{34,10},{32,15},{30,15},{28,15},{26,15},{24,15},{22,15},{20,15},{18,15},{16,15},{14,50},{12,50}, // animation 2
{10,50},{8,50},{6,50},{4,50},{2,50} // animation 2
};
// Frames per animatie
const uint8_t animationSettings[] = { 60, 40 };
// Startwaarden servo
servoData servosData[] = {
{ .currentAnimationId = 0, .currentAnimationStep = 0, .currentStepWaitTime = 0, .currentIteration = 0, .currentIterationWaitTime = 0, .iterationCount = 0, .iterationWaitTime = 0, .animationSpeedup = 1, .currentAnimationWaitTime = 0 }
};
// Berekent aantal aanwezige servo's
uint8_t numberOfServosConfigured = 1;
void setup()
{
Serial.begin(115200);
// Pin servo
myservo.attach(9);
// Lees analoge waarde van ingang 1 (deze is niet aangesloten en levert dus een willekeurige waarde op)
int analogInputValue = analogRead(0);
// Randomnummer genorator configureren
randomSeed(analogInputValue);
}
void loop()
{
// Loop over alle servo's heen
for (uint8_t servoIndex = 0; servoIndex < numberOfServosConfigured; servoIndex++)
{
// Is de pauze tussen 2 animaties voorbij?
if (servosData[servoIndex].currentAnimationWaitTime > 0)
{
servosData[servoIndex].currentAnimationWaitTime--; // Nee, tel wachttijd af met 1
}
else
{
// Is de wachttijd tussen frames voorbij?
if (servosData[servoIndex].currentStepWaitTime > 0)
{
servosData[servoIndex].currentStepWaitTime--; // Nee, tel wachttijd af met 1
}
else
{
// Is de wachttijd tussen iteraties voorbij?
if (servosData[servoIndex].currentIterationWaitTime > 0)
{
servosData[servoIndex].currentIterationWaitTime--; // Nee, tel wachttijd af met 1
}
else
{
// Checkt of alle frames voorbij zijn van de huidige animatie
if (servosData[servoIndex].currentAnimationStep == animationSettings[servosData[servoIndex].currentAnimationId])
{
// Zo ja, zijn alle iteraties voorbij?
if (servosData[servoIndex].currentIteration != servosData[servoIndex].iterationCount)
{
//Nee, selecteer de volgende iteratie
servosData[servoIndex].currentIteration++;
servosData[servoIndex].currentIterationWaitTime = servosData[servoIndex].iterationWaitTime; // Laad currentIterationWaittime met bewaarde iterationWaitTime
servosData[servoIndex].currentAnimationStep = 0; // Animatie start bij eerste frame
}
else
{
// Animatie afgerond, maak nieuwe animatie aan
// Bepaal wachttijd tussen 2 animaties
servosData[servoIndex].currentAnimationWaitTime = random(4000, 8000);
// Selecteer de voglende animatie
servosData[servoIndex].currentAnimationId = random(0, 1);
// Selecteer versnelling animatie
servosData[servoIndex].animationSpeedup = random(1, 3);
// Reset iteratie teller
servosData[servoIndex].currentIteration = 0;
// Bepaal hoeveelheid iteraties
servosData[servoIndex].iterationCount = random(2, 4);
// Bepaal wachttijd tussen 2 iteraties
servosData[servoIndex].iterationWaitTime = random(100, 200);
// Laad currentIterationWaittime met bewaarde iterationWaitTime
servosData[servoIndex].currentIterationWaitTime = servosData[servoIndex].iterationWaitTime;
// Animatie start bij eerste frame
servosData[servoIndex].currentAnimationStep = 0;
}
}
else
{
// Selecteer volgende frame
// Bepaal positie animatieframe in lijst met animatie informatie
int animationsPointer = 0;
for(uint8_t animation = 0; animation < servosData[servoIndex].currentAnimationId; animation++)
{
// Skip stappen voorliggende animaties
animationsPointer += animationSettings[animation];
}
// Selecteer volgende frame
animationsPointer += servosData[servoIndex].currentAnimationStep;
// Bepaal positie servo
uint8_t stepSize = animations[animationsPointer][0];
// Stuur servo aan
myservo.write(stepSize);
// Bepaal wachttijd tussen frames en deel deze door versnelling
servosData[servoIndex].currentStepWaitTime = (animations[animationsPointer][1] / servosData[servoIndex].animationSpeedup);
// Selecteer volgende frame
servosData[servoIndex].currentAnimationStep++;
}
}
}
}
}
// Wacht 1 miliseconde per stap
delay(1);
}
Meerdere Servo's
Nadat ik de code klaar had om één servo te laten bewegen, heb ik de code aangepast zodat ik meerdere servo's tegelijkertijd kan aansturen. Ook heb ik een servodriver (Adadruit_PWMServoDriver) toegevoegd, omdat ik uiteindelijk meer servo's nodig had dan dat er passen in een arduino uno. Ook zit er in deze iteratie van de code een extra animatie.
// Importeren driver voor ondersteunen meerdere servo's
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
#define SERVOBASEOFFSET 300
// Ondersteuning meerdere servo's
Adafruit_PWMServoDriver servoDrivers[] = { Adafruit_PWMServoDriver(0x40) };
typedef struct servoData {
uint8_t servoControllerId;
uint8_t servoId;
uint8_t currentAnimationId;
uint8_t currentAnimationStep;
int currentStepWaitTime;
uint8_t currentIteration;
int currentIterationWaitTime;
uint8_t iterationCount;
int iterationWaitTime;
uint8_t animationSpeedup;
int currentAnimationWaitTime;
} servoData;
const uint8_t animations[][2] =
{
{2,40},{4,40},{6,40},{8,40},{10,40},{12,40},{14,40},{16,15},{18,15},{20,15},{22,15},{24,15},{26,15},{28,15},{30,15}, // animation 1
{32,15},{34,10},{36,10},{38,10},{40,10},{42,15},{44,15},{46,15},{48,15},{50,15},{52,40},{54,40},{56,40},{58,40},{60,40}, // animation 1
{60,40},{58,40},{56,40},{54,40},{52,40},{50,15},{48,15},{46,15},{44,15},{42,15},{40,10},{38,10},{36,10},{34,10},{32,15}, // animation 1
{30,15},{28,15},{26,15},{24,15},{22,15},{20,15},{18,15},{16,15},{14,40},{12,40},{10,40},{8,40},{6,40},{4,40},{2,40}, // animation 1
{2,40},{4,40},{6,40},{8,40},{10,15},{12,15},{14,15},{16,15},{18,10},{20,10},{22,10},{24,10},{26,15},{28,15},{30,15}, // animation 2
{32,15},{34,40},{36,40},{38,40},{40,40}, // animation 2
{40,40},{38,40},{36,40},{34,40},{32,15},{30,15},{28,15},{26,15},{24,10},{22,10},{20,10},{18,10},{16,15},{14,15},{12,15}, // animation 2
{10,15},{8,40},{6,40},{4,40},{2,40}, // animation 2
{2,40},{4,40},{6,40},{8,40},{10,40},{12,40},{14,40},{16,40},{18,40},{20,40},{22,15},{24,15},{26,15},{28,15},{30,15}, // animation 3
{32,15},{34,10},{36,10},{38,10},{40,10},{42,15},{44,15},{46,15},{48,15},{50,15},{52,15},{54,15},{56,15},{58,15},{60,15}, // animation 3
{62,15},{64,15},{66,15},{68,15},{70,15},{72,15},{74,15},{76,15},{78,15},{80,15},{82,15},{84,15},{86,15},{88,15},{90,15}, // animation 3
{92,15},{94,15},{96,15},{98,15},{100,15},{102,40},{104,40},{106,40},{108,40},{110,40},{112,40},{114,40},{116,40},{118,40},{120,40}, // animation 3
{120,40},{118,40},{116,40},{114,40},{112,40},{110,40},{108,40},{106,40},{104,40},{102,40},{100,15},{98,15},{96,15},{94,15},{92,15}, // animation 3
{90,15},{88,15},{86,15},{84,15},{82,15},{80,15},{78,15},{76,15},{74,15},{72,15},{70,15},{68,15},{66,15},{64,15},{62,15}, // animation 3
{60,15},{58,15},{56,15},{54,15},{52,15},{50,15},{48,15},{46,15},{44,15},{42,15},{40,15},{38,15},{36,15},{34,10},{32,15}, // animation 3
{30,15},{28,15},{26,15},{24,15},{22,15},{20,40},{18,40},{16,40},{14,40},{12,40},{10,40},{8,40},{6,40},{4,40},{2,40} // animation 3
};
// Extra animatie
const uint8_t animationSettings[] = { 60, 40, 120 };
// Meerdere servo's
servoData servosData[] = {
{ .servoControllerId = 0, .servoId = 0, .currentAnimationId = 0, .currentAnimationStep = 0, .currentStepWaitTime = 0, .currentIteration = 0, .currentIterationWaitTime = 0, .iterationCount = 0, .iterationWaitTime = 0, .animationSpeedup = 1, .currentAnimationWaitTime = 0 },
{ .servoControllerId = 0, .servoId = 1, .currentAnimationId = 0, .currentAnimationStep = 0, .currentStepWaitTime = 0, .currentIteration = 0, .currentIterationWaitTime = 0, .iterationCount = 0, .iterationWaitTime = 0, .animationSpeedup = 1, .currentAnimationWaitTime = 0 },
{ .servoControllerId = 0, .servoId = 2, .currentAnimationId = 0, .currentAnimationStep = 0, .currentStepWaitTime = 0, .currentIteration = 0, .currentIterationWaitTime = 0, .iterationCount = 0, .iterationWaitTime = 0, .animationSpeedup = 1, .currentAnimationWaitTime = 0 }
};
// Aantal servodrivers
uint8_t numberOfServoControllersConfigured = 1;
// Aantal servo's
uint8_t numberOfServosConfigured = 3;
// Aantal animaties
uint8_t numberOfAnimationsAvailable = 3;
void setup()
{
Serial.begin(115200);
int analogInputValue = analogRead(0);
randomSeed(analogInputValue);
// Klaarzetten servo's
for (uint8_t servoControllerIndex = 0; servoControllerIndex < numberOfServoControllersConfigured; servoControllerIndex++)
{
servoDrivers[servoControllerIndex].begin();
servoDrivers[servoControllerIndex].setOscillatorFrequency(27000000);
servoDrivers[servoControllerIndex].setPWMFreq(50);
}
}
void loop()
{
for (uint8_t servoIndex = 0; servoIndex < numberOfServosConfigured; servoIndex++)
{
if (servosData[servoIndex].currentAnimationWaitTime > 0)
{
servosData[servoIndex].currentAnimationWaitTime--;
}
else
{
if (servosData[servoIndex].currentStepWaitTime > 0)
{
servosData[servoIndex].currentStepWaitTime--;
}
else
{
if (servosData[servoIndex].currentIterationWaitTime > 0)
{
servosData[servoIndex].currentIterationWaitTime--;
}
else
{
if (servosData[servoIndex].currentAnimationStep == animationSettings[servosData[servoIndex].currentAnimationId])
{
if (servosData[servoIndex].currentIteration != servosData[servoIndex].iterationCount)
{
servosData[servoIndex].currentIteration++;
servosData[servoIndex].currentIterationWaitTime = servosData[servoIndex].iterationWaitTime;
servosData[servoIndex].currentAnimationStep = 0;
}
else
{
servosData[servoIndex].currentAnimationWaitTime = random(4000, 8000);
servosData[servoIndex].currentAnimationId = random(0, numberOfAnimationsAvailable);
servosData[servoIndex].animationSpeedup = random(1, 3);
servosData[servoIndex].currentIteration = 0;
servosData[servoIndex].iterationCount = random(1, 4);
servosData[servoIndex].iterationWaitTime = random(100, 200);
servosData[servoIndex].currentIterationWaitTime = servosData[servoIndex].iterationWaitTime;
servosData[servoIndex].currentAnimationStep = 0;
}
}
else
{
int animationsPointer = 0;
for(uint8_t animation = 0; animation < servosData[servoIndex].currentAnimationId; animation++)
animationsPointer += animationSettings[animation];
animationsPointer += servosData[servoIndex].currentAnimationStep;
uint8_t stepSize = animations[animationsPointer][0];
// Stuur servo aan via servodriver
servoDrivers[servosData[servoIndex].servoControllerId].setPWM(servosData[servoIndex].servoId, 0, SERVOBASEOFFSET + stepSize);
servosData[servoIndex].currentStepWaitTime = (animations[animationsPointer][1] / servosData[servoIndex].animationSpeedup);
servosData[servoIndex].currentAnimationStep++;
}
}
}
}
}
// Wacht 1 miliseconde per stap
delay(1);
}
Eerste Concept Vlinder
Eerst had ik bedacht door middel van een touwtje tussen de servo en vleugel toe te voegen, dat ik met één servo twee vleugels kon besturen. De reden waarom ik het met één servo per vlinder wou maken, is dat het dan veel minder stroom per vlinder gebruikt. In de praktijk viel dit concept tegen. Het touwtje bleek niet stevig genoeg om de vleugels op en neer te laten bewegen. Vanwege de kleine schaal van de vlinders was het ook erg moeilijk om in elkaar te zetten. Ik moest dus een nieuwe manier verzinnen waarop ik de vleugels kon laten bewegen.
Tweede Concept Vlinder
Uiteindelijk ben ik op het idee gekomen om twee servo's per vlinder te gebruiken. Minder optimaal, maar het bracht wel het resultaat die ik nodig had. Verder heb ik nog stokjes ge-3D-print en aan de servo gelijmd, zodat ik daar straks de vlinders overheen kan schuiven.
Monteren Vlinders Op MDF
Nu dat de vlinders werkten ben ik ze gaan monteren op een MDF plaat. het idee was om alle hardware op de achterkant te plakken en de voorkant mooi af te werken. Door met een boormachine gaten in het MDF de boren kunnen de draadjes van de servo erin zodat ze uit het zicht zijn. Ook heb ik uit een lat hout 3 houten pootjes (11 cm x 2 cm x 6 cm) gesneden zodat de onderkant vrij is voor de andere onderdelen.
Vlinders Aansluiten
Nadat alle servo's op de voorkant van het MDF waren gemonteerd ben ik aan de slag gegaan met alle onderdelen aansluiten. Alle onderdelen aan de onderkant zijn door middel van tie wraps om plakzetels aangebracht. Ook heb ik een pushbutton aangebracht zodat ik straks de vlinders op pauze kan zetten. (De tweede servodriver is adres 41.)
Interactie En Twee Servo's Per Vlinder
Dit is de code voor één vlinder die bestaat uit 2 servo's. De pushbutton functionaliteit zit in deze iteratie van de code ook verwerkt. Door op de pushbutton te klikken stoppen de servo's. Deze code is ook de uiteindelijke code die ik gebruik voor mijn project.
#include <Wire.h>
// Importeer pushbutton functionaliteit
#include <Pushbutton.h>
#include <Adafruit_PWMServoDriver.h>
#define SERVOBASEOFFSET 325
// Maak pusbutton aan en koppel deze aan A0
Pushbutton button(A0);
// Op de ene 10 en op de andere 6 servo's
Adafruit_PWMServoDriver servoDrivers[] = { Adafruit_PWMServoDriver(0x40), Adafruit_PWMServoDriver(0x41) };
// Staan de animaties aan?
bool sequenceOn = true;
typedef struct servoData {
uint8_t servoControllerId;
uint8_t servoIds[2];
uint8_t currentAnimationId;
uint8_t currentAnimationStep;
int currentStepWaitTime;
uint8_t currentIteration;
int currentIterationWaitTime;
uint8_t iterationCount;
int iterationWaitTime;
uint8_t animationSpeedup;
int currentAnimationWaitTime;
} servoData;
const uint8_t animations[][2] =
{
{2,40},{4,40},{6,40},{8,40},{10,40},{12,40},{14,40},{16,15},{18,15},{20,15},{22,15},{24,15},{26,15},{28,15},{30,15}, // animation 1
{32,15},{34,10},{36,10},{38,10},{40,10},{42,15},{44,15},{46,15},{48,15},{50,15},{52,40},{54,40},{56,40},{58,40},{60,40}, // animation 1
{60,40},{58,40},{56,40},{54,40},{52,40},{50,15},{48,15},{46,15},{44,15},{42,15},{40,10},{38,10},{36,10},{34,10},{32,15}, // animation 1
{30,15},{28,15},{26,15},{24,15},{22,15},{20,15},{18,15},{16,15},{14,40},{12,40},{10,40},{8,40},{6,40},{4,40},{2,40}, // animation 1
{2,40},{4,40},{6,40},{8,40},{10,15},{12,15},{14,15},{16,15},{18,10},{20,10},{22,10},{24,10},{26,15},{28,15},{30,15}, // animation 2
{32,15},{34,40},{36,40},{38,40},{40,40}, // animation 2
{40,40},{38,40},{36,40},{34,40},{32,15},{30,15},{28,15},{26,15},{24,10},{22,10},{20,10},{18,10},{16,15},{14,15},{12,15}, // animation 2
{10,15},{8,40},{6,40},{4,40},{2,40}, // animation 2
{2,40},{4,40},{6,40},{8,40},{10,40},{12,40},{14,40},{16,40},{18,40},{20,40},{22,15},{24,15},{26,15},{28,15},{30,15}, // animation 3
{32,15},{34,10},{36,10},{38,10},{40,10},{42,15},{44,15},{46,15},{48,15},{50,15},{52,15},{54,15},{56,15},{58,15},{60,15}, // animation 3
{62,15},{64,15},{66,15},{68,15},{70,15},{72,15},{74,15},{76,15},{78,15},{80,15},{82,15},{84,15},{86,15},{88,15},{90,15}, // animation 3
{90,15},{88,15},{86,15},{84,15},{82,15},{80,15},{78,15},{76,15},{74,15},{72,15},{70,15},{68,15},{66,15},{64,15},{62,15}, // animation 3
{60,15},{58,15},{56,15},{54,15},{52,15},{50,15},{48,15},{46,15},{44,15},{42,15},{40,15},{38,15},{36,15},{34,10},{32,15}, // animation 3
{30,15},{28,15},{26,15},{24,15},{22,15},{20,40},{18,40},{16,40},{14,40},{12,40},{10,40},{8,40},{6,40},{4,40},{2,40} // animation 3
};
const uint8_t animationSettings[] = { 60, 40, 90 };
// 2 servo's per servodata
servoData servosData[] = {
{ .servoControllerId = 0, .servoIds = { 0, 1 }, .currentAnimationId = 0, .currentAnimationStep = 0, .currentStepWaitTime = 0, .currentIteration = 0, .currentIterationWaitTime = 0, .iterationCount = 0, .iterationWaitTime = 0, .animationSpeedup = 1, .currentAnimationWaitTime = 0 },
{ .servoControllerId = 0, .servoIds = { 2, 3 }, .currentAnimationId = 0, .currentAnimationStep = 0, .currentStepWaitTime = 0, .currentIteration = 0, .currentIterationWaitTime = 0, .iterationCount = 0, .iterationWaitTime = 0, .animationSpeedup = 1, .currentAnimationWaitTime = 0 },
{ .servoControllerId = 0, .servoIds = { 4, 5 }, .currentAnimationId = 0, .currentAnimationStep = 0, .currentStepWaitTime = 0, .currentIteration = 0, .currentIterationWaitTime = 0, .iterationCount = 0, .iterationWaitTime = 0, .animationSpeedup = 1, .currentAnimationWaitTime = 0 },
{ .servoControllerId = 0, .servoIds = { 6, 7 }, .currentAnimationId = 0, .currentAnimationStep = 0, .currentStepWaitTime = 0, .currentIteration = 0, .currentIterationWaitTime = 0, .iterationCount = 0, .iterationWaitTime = 0, .animationSpeedup = 1, .currentAnimationWaitTime = 0 },
{ .servoControllerId = 0, .servoIds = { 8, 9 }, .currentAnimationId = 0, .currentAnimationStep = 0, .currentStepWaitTime = 0, .currentIteration = 0, .currentIterationWaitTime = 0, .iterationCount = 0, .iterationWaitTime = 0, .animationSpeedup = 1, .currentAnimationWaitTime = 0 },
{ .servoControllerId = 1, .servoIds = { 0, 1 }, .currentAnimationId = 0, .currentAnimationStep = 0, .currentStepWaitTime = 0, .currentIteration = 0, .currentIterationWaitTime = 0, .iterationCount = 0, .iterationWaitTime = 0, .animationSpeedup = 1, .currentAnimationWaitTime = 0 },
{ .servoControllerId = 1, .servoIds = { 2, 3 }, .currentAnimationId = 0, .currentAnimationStep = 0, .currentStepWaitTime = 0, .currentIteration = 0, .currentIterationWaitTime = 0, .iterationCount = 0, .iterationWaitTime = 0, .animationSpeedup = 1, .currentAnimationWaitTime = 0 },
{ .servoControllerId = 1, .servoIds = { 4, 5 }, .currentAnimationId = 0, .currentAnimationStep = 0, .currentStepWaitTime = 0, .currentIteration = 0, .currentIterationWaitTime = 0, .iterationCount = 0, .iterationWaitTime = 0, .animationSpeedup = 1, .currentAnimationWaitTime = 0 },
};
uint8_t numberOfServoControllersConfigured = 2;
uint8_t numberOfServosConfigured = 8;
uint8_t numberOfAnimationsAvailable = 3;
void setup()
{
Serial.begin(115200);
int analogInputValue = analogRead(0);
randomSeed(analogInputValue);
for (uint8_t servoControllerIndex = 0; servoControllerIndex < numberOfServoControllersConfigured; servoControllerIndex++)
{
servoDrivers[servoControllerIndex].begin();
servoDrivers[servoControllerIndex].setOscillatorFrequency(27000000);
servoDrivers[servoControllerIndex].setPWMFreq(50);
}
}
void loop()
{
// Haal pushbutton status op en verander of animaties aan of uit staan
if (button.getSingleDebouncedRelease()) sequenceOn = !sequenceOn;
// Animaties staan aan?
if (sequenceOn)
{
// Behandel animaties
for (uint8_t servoIndex = 0; servoIndex < numberOfServosConfigured; servoIndex++)
{
if (servosData[servoIndex].currentAnimationWaitTime > 0)
{
servosData[servoIndex].currentAnimationWaitTime--;
}
else
{
if (servosData[servoIndex].currentStepWaitTime > 0)
{
servosData[servoIndex].currentStepWaitTime--;
}
else
{
if (servosData[servoIndex].currentIterationWaitTime > 0)
{
servosData[servoIndex].currentIterationWaitTime--;
}
else
{
if (servosData[servoIndex].currentAnimationStep == animationSettings[servosData[servoIndex].currentAnimationId])
// handle iteration ?
if (servosData[servoIndex].currentIteration != servosData[servoIndex].iterationCount)
{
servosData[servoIndex].currentIteration++;
servosData[servoIndex].currentIterationWaitTime = servosData[servoIndex].iterationWaitTime;
servosData[servoIndex].currentAnimationStep = 0;
}
else
{
servosData[servoIndex].currentAnimationWaitTime = random(4000, 8000);
// select next animation randomly
servosData[servoIndex].currentAnimationId = random(0, numberOfAnimationsAvailable);
servosData[servoIndex].animationSpeedup = random(1, 3);
servosData[servoIndex].currentIteration = 0;
servosData[servoIndex].iterationCount = random(1, 4);
servosData[servoIndex].iterationWaitTime = random(100, 200);
servosData[servoIndex].currentIterationWaitTime = servosData[servoIndex].iterationWaitTime;
// prepare servoData for new animation
servosData[servoIndex].currentAnimationStep = 0;
}
}
else
{
int animationsPointer = 0;
for(uint8_t animation = 0; animation < servosData[servoIndex].currentAnimationId; animation++)
animationsPointer += animationSettings[animation];
animationsPointer += servosData[servoIndex].currentAnimationStep;
uint8_t stepSize = animations[animationsPointer][0];
// Voor linker en rechter vleugel
for (uint8_t serviceIdIndex = 0; serviceIdIndex < 2; serviceIdIndex++)
{
servoDrivers[servosData[servoIndex].servoControllerId].setPWM(servosData[servoIndex].servoIds[serviceIdIndex], 0, SERVOBASEOFFSET + stepSize);
}
servosData[servoIndex].currentStepWaitTime = (animations[animationsPointer][1] / servosData[servoIndex].animationSpeedup);
servosData[servoIndex].currentAnimationStep++;
}
}
}
}
}
}
delay(1);
}
Afwerking
Nadat alle onderdelen op hun plaats zaten, begon ik met het tekenen van vlinders in Photoshop. Tijdens het tekenen had ik een referentie van Malenia's vleugels zodat de kleuren en vorm zo accuraat mogelijk zijn. Daarna maakte ik een bestand aan per kleur en achterkant en begon ik met het uitprinten van de vlinders op dik A3 papier. Met hot glue plakte ik de voor- en achterkanten van de vlinders tegen elkaar en schoof ze over het ge-3D-printen stokjes aan de servo's. Voor het bedekken van de plaat zelf had ik de bruine en donkergroene vlinders op stickerpapier geprint zodat ik ze gelijk op het MDF kon plakken. Nadat alle vlinders aan de servo's en op de plaat zaten, ben ik met acrylverf over de randen van de vlinders gegaan met zwarte verf zodat het witte van het papier niet meer te zien is. Ook heb ik wat kleuren verf verdunt met water en op de vlinders geschilderd zodat er meer dimensie in de vleugels zit. Soms zag je nog wat servo onderdelen door de vlinder heen en die heb ik ook geschilderd.
Compleet
En toen was het af! In deze video's laat ik zien hoe ze bewegen.
Downloads
Conclusie
Tijdens dit project heb ik erg veel geleerd over arduino's en wat je er allemaal mee kan doen. Het monteren en afwerken van het project vond ik het leukst, omdat dan echt alles mooi samen komt. Het coderen vind ik altijd wel minder leuk, maar doordat je snel feedback krijgt vanuit je onderdelen vond ik het wel leuker dan normaal. Ik ga verder dit prototype uitwerken zodat ik straks een volledige Malenia, goddess of rot cosplay heb.