Для сборки подобного робота-змеи, автору понадобились: 12 сервомашинок с кронштейнами для крепления, контроллер Arduino Mega и колёсики от конструктора LEGO.
Скетч змееобразного движения:
/* Remote control file for serpentine motion of a snake robot with 12 servos */ #include <Servo.h> // Define servo objects for the snake segments Servo s1; Servo s2; Servo s3; Servo s4; Servo s5; Servo s6; Servo s7; Servo s8; Servo s9; Servo s10; Servo s11; Servo s12; // Define variables int forwardPin = 14; // Remote control movement pins int reversePin = 15; int rightPin = 16; int leftPin = 17; int forwardVal = 0; // Remote control variables int reverseVal = 0; int rightVal = 0; int leftVal = 0; int counter = 0; // Loop counter variable float lag = .5712; // Phase lag between segments int frequency = 1; // Oscillation frequency of segments. int amplitude = 40; // Amplitude of the serpentine motion of the snake int rightOffset = 5; // Right turn offset int leftOffset = -5; // Left turn offset int offset = 6; // Variable to correct servos that are not exactly centered int delayTime = 7; // Delay between limb movements int startPause = 5000; // Delay time to position robot int test = -3; // Test varialble takes values from -6 to +5 void setup() { // Set movement pins as inputs pinMode(forwardPin, INPUT); pinMode(reversePin, INPUT); pinMode(rightPin, INPUT); pinMode(leftPin, INPUT); // Set movement pins to low digitalWrite(forwardPin, LOW); digitalWrite(reversePin, LOW); digitalWrite(rightPin, LOW); digitalWrite(leftPin, LOW); // Attach segments to pins s1.attach(2); s2.attach(3); s3.attach(4); s4.attach(5); s5.attach(6); s6.attach(7); s7.attach(8); s8.attach(9); s9.attach(10); s10.attach(11); s11.attach(12); s12.attach(13); // Put snake in starting position s1.write(90+offset+amplitude*cos(5*lag)); s2.write(90+offset+amplitude*cos(4*lag)); s3.write(90+offset+amplitude*cos(3*lag)); s4.write(90+amplitude*cos(2*lag)); s5.write(90+amplitude*cos(1*lag)); s6.write(90+amplitude*cos(0*lag)); s7.write(90+amplitude*cos(-1*lag)); s8.write(90+amplitude*cos(-2*lag)); s9.write(90+amplitude*cos(-3*lag)); s10.write(90+amplitude*cos(-4*lag)); s11.write(90+amplitude*cos(-5*lag)); s12.write(90+amplitude*cos(-6*lag)); delay(startPause); // Pause to position robot } void loop() { // Read movement pins forwardVal = digitalRead(forwardPin); reverseVal = digitalRead(reversePin); rightVal = digitalRead(rightPin); leftVal = digitalRead(leftPin); // Forward motion if (forwardVal == HIGH){ for(counter = 0; counter < 360; counter += 1) { delay(delayTime); s1.write(90+offset+amplitude*cos(frequency*counter*3.14159/180+5*lag)); s2.write(90+offset+amplitude*cos(frequency*counter*3.14159/180+4*lag)); s3.write(90+offset+amplitude*cos(frequency*counter*3.14159/180+3*lag)); s4.write(90+amplitude*cos(frequency*counter*3.14159/180+2*lag)); s5.write(90+amplitude*cos(frequency*counter*3.14159/180+1*lag)); s6.write(90+amplitude*cos(frequency*counter*3.14159/180+0*lag)); s7.write(90+amplitude*cos(frequency*counter*3.14159/180-1*lag)); s8.write(90+amplitude*cos(frequency*counter*3.14159/180-2*lag)); s9.write(90+amplitude*cos(frequency*counter*3.14159/180-3*lag)); s10.write(90+amplitude*cos(frequency*counter*3.14159/180-4*lag)); s11.write(90+amplitude*cos(frequency*counter*3.14159/180-5*lag)); s12.write(90+amplitude*cos(frequency*counter*3.14159/180-6*lag)); } } // Reverse motion if (reverseVal == HIGH){ for(counter = 360; counter > 0; counter -= 1) { delay(delayTime); s1.write(90+offset+amplitude*cos(frequency*counter*3.14159/180+5*lag)); s2.write(90+offset+amplitude*cos(frequency*counter*3.14159/180+4*lag)); s3.write(90+offset+amplitude*cos(frequency*counter*3.14159/180+3*lag)); s4.write(90+amplitude*cos(frequency*counter*3.14159/180+2*lag)); s5.write(90+amplitude*cos(frequency*counter*3.14159/180+1*lag)); s6.write(90+amplitude*cos(frequency*counter*3.14159/180+0*lag)); s7.write(90+amplitude*cos(frequency*counter*3.14159/180-1*lag)); s8.write(90+amplitude*cos(frequency*counter*3.14159/180-2*lag)); s9.write(90+amplitude*cos(frequency*counter*3.14159/180-3*lag)); s10.write(90+amplitude*cos(frequency*counter*3.14159/180-4*lag)); s11.write(90+amplitude*cos(frequency*counter*3.14159/180-5*lag)); s12.write(90+amplitude*cos(frequency*counter*3.14159/180-6*lag)); } } // Right turn if (rightVal == HIGH){ // Ramp up turn offset for(counter = 0; counter < 10; counter += 1) { delay(delayTime); s1.write(90+offset+.1*counter*rightOffset+amplitude*cos(frequency*counter*3.14159/180+5*lag)); s2.write(90+offset+.1*counter*rightOffset+amplitude*cos(frequency*counter*3.14159/180+4*lag)); s3.write(90+offset+.1*counter*rightOffset+amplitude*cos(frequency*counter*3.14159/180+3*lag)); s4.write(90+.1*counter*rightOffset+amplitude*cos(frequency*counter*3.14159/180+2*lag)); s5.write(90+.1*counter*rightOffset+amplitude*cos(frequency*counter*3.14159/180+1*lag)); s6.write(90+.1*counter*rightOffset+amplitude*cos(frequency*counter*3.14159/180+0*lag)); s7.write(90+.1*counter*rightOffset+amplitude*cos(frequency*counter*3.14159/180-1*lag)); s8.write(90+.1*counter*rightOffset+amplitude*cos(frequency*counter*3.14159/180-2*lag)); s9.write(90+.1*counter*rightOffset+amplitude*cos(frequency*counter*3.14159/180-3*lag)); s10.write(90+.1*counter*rightOffset+amplitude*cos(frequency*counter*3.14159/180-4*lag)); s11.write(90+.1*counter*rightOffset+amplitude*cos(frequency*counter*3.14159/180-5*lag)); s12.write(90+.1*counter*rightOffset+amplitude*cos(frequency*counter*3.14159/180-6*lag)); } // Continue right turn for(counter = 11; counter < 350; counter += 1) { delay(delayTime); s1.write(90+offset+rightOffset+amplitude*cos(frequency*counter*3.14159/180+5*lag)); s2.write(90+offset+rightOffset+amplitude*cos(frequency*counter*3.14159/180+4*lag)); s3.write(90+offset+rightOffset+amplitude*cos(frequency*counter*3.14159/180+3*lag)); s4.write(90+rightOffset+amplitude*cos(frequency*counter*3.14159/180+2*lag)); s5.write(90+rightOffset+amplitude*cos(frequency*counter*3.14159/180+1*lag)); s6.write(90+rightOffset+amplitude*cos(frequency*counter*3.14159/180+0*lag)); s7.write(90+rightOffset+amplitude*cos(frequency*counter*3.14159/180-1*lag)); s8.write(90+rightOffset+amplitude*cos(frequency*counter*3.14159/180-2*lag)); s9.write(90+rightOffset+amplitude*cos(frequency*counter*3.14159/180-3*lag)); s10.write(90+rightOffset+amplitude*cos(frequency*counter*3.14159/180-4*lag)); s11.write(90+rightOffset+amplitude*cos(frequency*counter*3.14159/180-5*lag)); s12.write(90+rightOffset+amplitude*cos(frequency*counter*3.14159/180-6*lag)); } // Ramp down turn offset for(counter = 350; counter < 360; counter += 1) { delay(delayTime); s1.write(90+offset+.1*(360-counter)*rightOffset+amplitude*cos(frequency*counter*3.14159/180+5*lag)); s2.write(90+offset+.1*(360-counter)*rightOffset+amplitude*cos(frequency*counter*3.14159/180+4*lag)); s3.write(90+offset+.1*(360-counter)*rightOffset+amplitude*cos(frequency*counter*3.14159/180+3*lag)); s4.write(90+.1*(360-counter)*rightOffset+amplitude*cos(frequency*counter*3.14159/180+2*lag)); s5.write(90+.1*(360-counter)*rightOffset+amplitude*cos(frequency*counter*3.14159/180+1*lag)); s6.write(90+.1*(360-counter)*rightOffset+amplitude*cos(frequency*counter*3.14159/180+0*lag)); s7.write(90+.1*(360-counter)*rightOffset+amplitude*cos(frequency*counter*3.14159/180-1*lag)); s8.write(90+.1*(360-counter)*rightOffset+amplitude*cos(frequency*counter*3.14159/180-2*lag)); s9.write(90+.1*(360-counter)*rightOffset+amplitude*cos(frequency*counter*3.14159/180-3*lag)); s10.write(90+.1*(360-counter)*rightOffset+amplitude*cos(frequency*counter*3.14159/180-4*lag)); s11.write(90+.1*(360-counter)*rightOffset+amplitude*cos(frequency*counter*3.14159/180-5*lag)); s12.write(90+.1*(360-counter)*rightOffset+amplitude*cos(frequency*counter*3.14159/180-6*lag)); } } // Left turn if (leftVal == HIGH){ // Ramp up turn offset for(counter = 0; counter < 10; counter += 1) { delay(delayTime); s1.write(90+offset+.1*counter*leftOffset+amplitude*cos(frequency*counter*3.14159/180+5*lag)); s2.write(90+offset+.1*counter*leftOffset+amplitude*cos(frequency*counter*3.14159/180+4*lag)); s3.write(90+offset+.1*counter*leftOffset+amplitude*cos(frequency*counter*3.14159/180+3*lag)); s4.write(90+.1*counter*leftOffset+amplitude*cos(frequency*counter*3.14159/180+2*lag)); s5.write(90+.1*counter*leftOffset+amplitude*cos(frequency*counter*3.14159/180+1*lag)); s6.write(90+.1*counter*leftOffset+amplitude*cos(frequency*counter*3.14159/180+0*lag)); s7.write(90+.1*counter*leftOffset+amplitude*cos(frequency*counter*3.14159/180-1*lag)); s8.write(90+.1*counter*leftOffset+amplitude*cos(frequency*counter*3.14159/180-2*lag)); s9.write(90+.1*counter*leftOffset+amplitude*cos(frequency*counter*3.14159/180-3*lag)); s10.write(90+.1*counter*leftOffset+amplitude*cos(frequency*counter*3.14159/180-4*lag)); s11.write(90+.1*counter*leftOffset+amplitude*cos(frequency*counter*3.14159/180-5*lag)); s12.write(90+.1*counter*leftOffset+amplitude*cos(frequency*counter*3.14159/180-6*lag)); } // Continue left turn for(counter = 11; counter < 350; counter += 1) { delay(delayTime); s1.write(90+offset+leftOffset+amplitude*cos(frequency*counter*3.14159/180+5*lag)); s2.write(90+offset+leftOffset+amplitude*cos(frequency*counter*3.14159/180+4*lag)); s3.write(90+offset+leftOffset+amplitude*cos(frequency*counter*3.14159/180+3*lag)); s4.write(90+leftOffset+amplitude*cos(frequency*counter*3.14159/180+2*lag)); s5.write(90+leftOffset+amplitude*cos(frequency*counter*3.14159/180+1*lag)); s6.write(90+leftOffset+amplitude*cos(frequency*counter*3.14159/180+0*lag)); s7.write(90+leftOffset+amplitude*cos(frequency*counter*3.14159/180-1*lag)); s8.write(90+leftOffset+amplitude*cos(frequency*counter*3.14159/180-2*lag)); s9.write(90+leftOffset+amplitude*cos(frequency*counter*3.14159/180-3*lag)); s10.write(90+leftOffset+amplitude*cos(frequency*counter*3.14159/180-4*lag)); s11.write(90+leftOffset+amplitude*cos(frequency*counter*3.14159/180-5*lag)); s12.write(90+leftOffset+amplitude*cos(frequency*counter*3.14159/180-6*lag)); } // Ramp down turn offset for(counter = 350; counter < 360; counter += 1) { delay(delayTime); s1.write(90+offset+.1*(360-counter)*leftOffset+amplitude*cos(frequency*counter*3.14159/180+5*lag)); s2.write(90+offset+.1*(360-counter)*leftOffset+amplitude*cos(frequency*counter*3.14159/180+4*lag)); s3.write(90+offset+.1*(360-counter)*leftOffset+amplitude*cos(frequency*counter*3.14159/180+3*lag)); s4.write(90+.1*(360-counter)*leftOffset+amplitude*cos(frequency*counter*3.14159/180+2*lag)); s5.write(90+.1*(360-counter)*leftOffset+amplitude*cos(frequency*counter*3.14159/180+1*lag)); s6.write(90+.1*(360-counter)*leftOffset+amplitude*cos(frequency*counter*3.14159/180+0*lag)); s7.write(90+.1*(360-counter)*leftOffset+amplitude*cos(frequency*counter*3.14159/180-1*lag)); s8.write(90+.1*(360-counter)*leftOffset+amplitude*cos(frequency*counter*3.14159/180-2*lag)); s9.write(90+.1*(360-counter)*leftOffset+amplitude*cos(frequency*counter*3.14159/180-3*lag)); s10.write(90+.1*(360-counter)*leftOffset+amplitude*cos(frequency*counter*3.14159/180-4*lag)); s11.write(90+.1*(360-counter)*leftOffset+amplitude*cos(frequency*counter*3.14159/180-5*lag)); s12.write(90+.1*(360-counter)*leftOffset+amplitude*cos(frequency*counter*3.14159/180-6*lag)); } } }
Ссылки
Snake Robot
По теме
ChainFORM - роботизированный интерфейс в виде цепочки сервомашинок
Sneaky - интерактивный змееподобный робот
Прототип роботизированной зарядки-змеи от Tesla Motors
Робозмея ACM-R5H
Робот-червяк на базе Arduino
Будущее за бионическими роботами?
Arduino
Arduino, термины, начало работы
КМБ для начинающих ардуинщиков
Состав стартера (точка входа для начинающих ардуинщиков)