Introduction: Bioinspired Robotic Snake

About: YouTube: https://www.youtube.com/channel/WillDonaldson

I was inspired to begin this project after seeing research videos of both tree climbing robotic snakes and robotic eels. This is my first attempt and building robots using serpentine locomotion, but it won't be my last! Subscribe on YouTube if you would like to see future developments.

Below I outline the construction of 2 different snakes along with the files for 3D printing and a discussion about the code and algorithms to achieve snake-like motion. If you wish to continue learning more, after reading this instructable I would suggest reading the links in the references section at the bottom of the page.

This instructable is technically a 2-in-1, in that I explain how to make 2 different versions of a robotic snake. If you are only interested in building one of the snakes ignore the instructions for the other snake. These 2 different snakes will be from here on referred to using the following phrases interchangeably:

  1. Single axis snake, 1D snake, or yellow and black snake
  2. Double axis snake, 2D snake, or white snake

Of course you can print the snakes in any colour filament you want. The only difference between the two snakes is that in the 2D snake each motor is rotated 90 degrees relative to the previous, whereas in the 1D snake all the motors are aligned in a single axis.

A final foreword is that while each of my snakes have only 10 servos it is possible to make the snakes with more or less servos. One thing to consider is that with less servos you will achieve less successful motion, and with more servos you will probably be more successful with the serpentine motion but you will need to consider cost, current draw (see later remarks) and the number of pins available on the Arduino. Feel free to alter the length of the snake, however keep in mind you will also need to alter the code to account for this change.

Step 1: Components

This is a parts list for a single snake, if you want to make both snakes you will need to double the volume of components.

  • 10 MG996R servos*
  • 1.75mm 3D printing filament
  • 10 ball bearings, part number 608 (I salvaged mine from the outer rim of Jitterspin fidget spinners)
  • 20 small ball bearings, part number r188, for the wheels** (I salvaged mine from the inner part of Jitterspin fidget spinners)
  • 40 philips head screws 6-32 x 1/2" (or similar)
  • 8 longer screws (I don't have a part number but they are the same diameter as the screws above)
  • At least 20 pieces of 4 inch zipties (its up to you how many you want to use)
  • 5m each of red and black 20 gauge wire or thicker***
  • Standard 22 gauge wire
  • 30 male header pins (split into 10 lots of 3)
  • Arduino Nano
  • 3D printed parts (see next section)
  • Some form of power (see the section: "Powering the snake" for more info), I personally used a modified ATX power supply
  • 1000uF 25V electrolytic capacitor
  • Heat shrink tube of various sizes, solder, glue and other miscellaneous tools

*you can use other types but you will need to redesign the 3D files to fit with your servos. Also if you try using smaller servos like the sg90, you may find they are not strong enough (I have not tested this and it will be up to you to experiment).

**you don't need to use small ball bearings for the wheels, I just had a lot laying around. Alternatively you could use LEGO wheels or other toy wheels.

***This wire can have up to 10 amps going through it, too thin and the current will melt it. See this page for more info.

Step 2: 3D Printing Components

If you are making the 1D snake print these pieces.

If you are making the 2D snake print these pieces.

Important note: The scale might be wrong! I design my components in Fusion 360 (in mm units), exported the design as an .stl file into MakerBot software and then printed it on a Qidi Tech printer (a clone version of the MakerBot Replicator 2X). Somewhere along this workflow there is a bug and all of my prints come out too small. I have been unable to identify the location of the bug but have a temporary fix of scaling each print to 106% size in the MakerBot software, this fixes the problem.

Given this, be warned that if you print off the files above they may be incorrectly scaled. I suggest printing just one piece and checking if it fits with your MG996R servo before printing them all.

If you do print any of the files please let me know what the outcome is: if the print is too small, just right, too large and by how much percent. By working together as a community we can troubleshoot the location of the bug using different 3D printers and .stl slicers. Once the issue is resolved I will update this section and the links above.

Step 3: Assembly of the Snakes

The assembly process is mostly the same for both versions of the snake. The only difference is the in the 2D snake each motor is rotated 90 degrees relative to the previous, whereas in the 1D snake all the motors are aligned in a single axis.

Begin by unscrewing the servo, save the screws and remove the top and bottom pieces of the black plastic frame, and be careful not to lose any of the gears! Slide the servo into the 3D printed frame, orientated as in the pictures above. Replace the top of the servo case, and screw it in place with four 6-32 1/2" screws. Save the bottom of the servo frame (in case you want to use it again in later projects) and replace it with the 3D printed case, the only difference being the additional knob for a ball bearing to slip over. Screw the servo back together, repeat 10 times.

IMPORTANT: Before continuing you must upload code to the Arduino and move each servo to 90 degrees. Failure to do this could result in you breaking one or more servo and/or the 3D printed frames. If you are unsure how to move a servo to 90 degrees see this page. Basically connect the red wire of the servo to 5V on the Arduino, the brown wire to GND and the yellow wire to digital pin 9, then upload the code in the link.

Now that each servo is at 90 degrees, continue:

Connect the 10 segments by inserting the 3D printed knob from one servo case into the hole of a second segment piece, then with a little bit of force push the axle of the servo into its hole (see pictures above and video for clarity). If you are making the 1D snake, all the segments should be aligned, if you are making the 2D snake, each segment should be rotated 90 degrees to the previous segment. Note that the tail and head frame are only half the length of the other segments, connect them but don't comment the pyramid shaped pieces until after we have finished the wiring.

Attach the x-shaped servo arm and screw it into position. Slip the ball bearing over the 3D printed knob, this will require gently squeezing the 2 semi-circle posts together. Depending on what brand of filament you use and the infill density, the posts may be too brittle and snap, I don't think this will be the case but nonetheless don't use excessive force. I personally used PLA filament with 10% infill. Once the ball bearing is on, it should stay locked on by the overhangs on the knob.

Step 4: Circuit

The circuit is the same for both robotic snakes. During the wiring process make sure there is enough wiring space for each segment to rotate completely, especially in the 2D snake.

Above is a circuit diagram for the wiring with only 2 servos. I tried doing a circuit drawing with 10 servos but it got way too overcrowded. The only difference between this picture and real life is that you need to wire 8 more servos in parallel and connect the PWM signal wires to pins on the Arduino Nano.

When wiring the power lines I used a single piece of 18 gauge wire (thick enough to withstand 10amps) as the main 5V line running down the length of the snake. Using wire strippers I removed a small section of insulator at 10 regular intervals, and soldered a short piece of wire from each of these intervals a group of 3 male header pins. Repeat this a second time for the black 18 gauge GND wire and a second male header pin. Finally solder a longer wire to the 3rd male header pin, this pin will carry the PWM signal to the servo from the Arduino Nano in the head of the snake (the wire must be long enough to reach, even when the segments bend). Attach heat shrink tube as required. Connect the 3 male header pins the the 3 female header pins of the servo wires. Repeat 10 times for each of the 10 servos. Ultimately what this achieves is wiring the servos in parallel and running PWM signal wires to the Nano. The reason for the male/female header pins was so you can easily take segments apart and replace servos if they break without unsoldering everything.

Solder the GND and 5V wires to a 3x7 hole perf board in the tail with a capacitor and screw terminals. The purpose of the capacitor is to remove any current draw spikes caused when starting up the servos, that can reset the Arduino Nano (if you don't have a capacitor you can probably get away without it, but it is better to be safe). Remember that the long prong of electrolytic capacitors need to be connected to the 5V line and the shorter prong to the GND line. Solder the the GND wire to the GND pin of the Nano and the 5V wire to the 5V pin. Note if you are using a different voltage, (see next section), say a Lipo battery with 7.4V, then wire the red wire to the Vin pin, NOT the 5V pin, doing so will destroy the pin.

Solder the 10 PWM signal wires to pins on the Arduino Nano. I wired mine in the following order, you can choose to wire yours differently but just remember that you will then need to change the servo.attach() lines in the code. If you are unsure what I am talking about just wire it the same way as I did and you won't have issues. In order from the servo at the tail of the snake to the head of the snake, I wired both my snakes in the following order. Connecting the signal pins to: A0, A1, A2, A3, A4, A5, D4, D3, D8, D7.

Use zipties to clean up the wiring. Before continuing check that all the segments can move with sufficient room for the wires to move without being pulled apart. Now that the wiring is done we can screw on the head and tail pyramid-shaped caps. Note that the tail has a hole for the tether to come out of and the head has a hole for the Arduino programming cable.

Step 5: Powering the Snake

Because the servos are wired in parallel, they all get the same voltage, but the current must be added up. Looking at the datasheet for MG996r servos they can draw up to 900mA each while running (assuming no stalling). Thus the total current draw if all 10 servos move at the same time is 0.9A*10=9A. As such a normal 5v, 2A wall socket adapter won't work. I decided to modify an ATX power supply, capable of 5v at 20A. I am not going to explain how to do this, as it has been discussed a lot on Instructables and YouTube already. A quick search online will show you how to modify one of these power supplies.

Assuming you have modified the power supply, it is simply a case of connecting a long tether between the power supply and the screw terminals on the snake.

Another option is to use an onboard lipo battery pack. I haven't tried this so it will be up to you to design a mount for the batteries and wire them in. Keep in mind the operating voltages, current draw of the servos and the Arduino (don't solder anything other than 5v to the 5v pin on the Arduino, go to the Vin pin if you have a higher voltage).

Step 6: Test Everything Is Working

Before continuing lets just test everything is working. Upload this code. Your snake should move each servo individually between 0-180 and then finish by laying in a straight line. If it doesn't then something is wrong, most likely the wiring is incorrect or the servos were not initially centred at 90 degrees as mentioned in "Assembly of the snakes" section.

Step 7: Code

There is currently no remote controller for the snake, all the motion is preprogrammed and you can choose what you want. I will develop a remote control in version 2, but if you want to control it remotely I would suggest looking into other tutorials on Instructables and adapting the snake to be bluetooth compatible.

If you are making the 1D snake upload this code.

If you are making the 2D snake upload this code.

I encourage you to play around with the code, make your own changes, and create new algorithms. Read the next several sections for a detailled explanation of each type of locomotion and how the code for it works.

Step 8: Scales Vs Wheels

One of the main ways snakes are able to move forward is through the shape of their scales. The scales allow for easier forward motion. For a further explanation watch this video from 3:04 onwards to see how scales help the snake move forward. Looking at 3:14 in the same video shows the effect when the snakes are in a sleeve, removing the friction of the scales. As shown in my YouTube video when the robotic 1D snake tries slithering on grass without scales, it neither moves forward or backwards since the forces sum to a net zero. As such we need to add some of artificial scales to the underbelly of the robot.

Research in recreating locomotion via scales was done at Harvard University and demonstrated in this video. I was unable to devise a similar method to move the scales up and down on my robot and instead settled for attaching passive 3D printed scales to the underbelly.

Unfortunately this proved ineffective (see in my YouTube video at 3:38) as the scales still skimmed over the surface of the carpet instead of catching on the fibres and increasing the friction.

If you wish to experiment with the scales I made you can 3D print the files from my GitHub. If you make your own successfully let me know in the comments down below!

Using a different approach I tried using wheels made from r188 ball bearings with heat shrink tubing over the outside as a 'tires'. You can 3D print the plastic wheel axles from the .stl files on my GitHub. While wheels aren't biologically accurate, they are analogous to scales in that forward rotation is easy but side to side motion is significantly harder. You can see the successful result of the wheels in my YouTube video.

Step 9: Slithering Motion (single Axis Snake)

We've discussed how scales help the snake moves forward, but how do we program the snake? The short answer is we pump a sine wave through the servo chain. But for a more detailed breakdown of how exactly that works lets look at the code. Please note in the code block below that I have omitted most of the minor details and only focused on the mathematical side. Do NOT put this code into your Arduino, it won't work, it is merely for explanation purposes. Instead use this complete code.

This line writes to each of the 10 servos a sine wave. The base line angle is 90 degrees, the offset variable will control if the snake goes forward (offset=0) or turns left or right (offset=10 or -10), see GIF above. The sine wave outputs a value between [-1,1], this value can be scaled up by multiplying by the amplitude.

for(int j=0; j<10; j++){  
  myServos[j].write(90+offset+Amplitude*sin(Speed*rads+j*Wavelengths*Shift));
}

Since the servo has a range of [0,180] degrees we must ensure that the above values don't give an output below 0 or above 180. The following while loop is used to constrain the amplitude to with in these bounds. Mathematically we must satisfy this condition: |offset|+|amplitude|<=90

while(MaxAngleDisplacement>90){
  Amplitude=abs(Amplitude)-1;
  MaxAngleDisplacement=abs(offset)+Amplitude;
}

To get the desired output from the sine wave we must use radians instead of degrees. If you are unsure what radians are here is a brief intro. Essentially 2*pi radians = 360 degrees. The following line makes this conversion.

for(int i=0; i<360; i++){
  rads=i*pi/180.0;
}

Because each servo is a little further along the sine wave than the previous servo we must shift each successive motor in the sine wave code. This is done using the following line. The shift variable can then be seen in action in the for-loop above.

float pi=3.14159;
int TotalNumberofServos=10;    //change as required
float Shift = 2*pi/TotalNumberofServos;  

We have now set the foundational work. I encourage you to play around with changing the values of the following variables: Amplitude, Speed and Wavelengths to see the effect they have on the sine wave output.

Step 10: Inch Worm (single Axis Snake)

This motion can be intuitively understood from watching an Inch Worm move. The code propagates a simple bump through the snakes body, moving itself slightly forward each time. I don't want to insert another block of code here so instead I will direct you to my GitHub to look at the code in the function called 'InchWorm()'.

One thing to note is that the efficiency could be increased by adding scales. If you watch my YouTube video at 7:24 looking specifically at the tail segment only you will see that it jitters back and forwards. This indicates the snake isn't just moving forwards, it is also slightly slipping back with each movement, decreasing efficiency.

Step 11: Other Geometries (single Axis Snake)

The straightline(), Cshape(), ubend(), and ring() functions are all fairly self explanatory in that they creating c-shaped bends, u-shaped bends etc, although I will remark if you want to carry the snake around I found that the ubend() is the best shape to do it in, otherwise the snake flips around everywhere as the servos move under their own weight even when not powered.

Step 12: Sidewinding Motion (double Axis Snake)

Before looking at the code lets look at the real sidewinder snake. In the previous link the motion of the sidewinder is described through a process of periodically lifting up parts of the snakes body and thrusting itself to the side. To achieve this we will send a sine wave down both the horizontal and vertical planes of the 2D snake. In the code this motion is written in the function 'sidewind()'.

You may notice that the sidewinding motion in the GIF (running in real time, not sped up) above is of better quality than the sidewinding clip in my YouTube video (running at 2x speed). This demonstrates the importance of experimentation and finding the right values for the variables.The variables used in the GIF result in more successful sidewinding than in my YouTube video. In the GIF the variables are:

Speed=2

Wavelengths=1.5

Whereas in the YouTube video the variables are:

Speed=1

Wavelengths=1.0

To achieve sidewinding motion a sine wave must be sent through one plane and a cosine through the other. To do this I have modified the code from the 1D case by separating the 5 motors in the vertical plane and the 5 in the horizontal using an even (2*j) and odd (2*j+1) numbering system respectively. We want one of these lines to move as a sine wave and the other as a cosine wave. In order to control the direction of movement I added an expression with a Multiplier variable (equal to 1 or -1). Depending on the value of the Multiplier variable the algebraic expressions: -(Multiplier-1)*pi/4 and +(Multiplier+1)*pi/4, will either be 0 or +pi/2. Notice that sin(x+pi/2)=cos(x). Hence we have achieved what we were aiming for: a sine wave in one plane and a cosine wave in the other, reversing the order of the waves will change the direction of movement.

for(int j=0; j<5; j++){
myServos[2*j].write(90+offset+amplitude*sin(Speed*rads+j*Wavelengths*shift-(Multiplier-1)*pi/4)); //moves servos in vertical plane myServos[2*j+1].write(90+offset+amplitude*sin(Speed*rads+j*Wavelengths*shift+(Multiplier+1)*pi/4)); //moves servos in horizontal plane }

To achieve a turning motion I just modified the code above slightly so that the front half of the snake (motors on pins A5, 4, 3, 8, 7) sidewinds to the right, and the back half of the snake (motors on pins A0, A1, A2, A3, A4) sidewinds to the left, resulting in a turning motion to the right (a similar approach can result in turning left).

for(int j=0; j<3; j++){
myServos[2*j].write(90+offset+amplitude*sin(Speed*rads+j*Wavelengths*shift+(Multiplier+1)*pi/4)); myServos[2*j+1].write(90+offset+amplitude*sin(Speed*rads+j*Wavelengths*shift-(Multiplier-1)*pi/4)); } for(int j=3; j<5; j++){ myServos[2*j].write(90+offset+amplitude*sin(Speed*rads+j*Wavelengths*shift-(Multiplier-1)*pi/4)); myServos[2*j+1].write(90+offset+amplitude*sin(Speed*rads+j*Wavelengths*shift+(Multiplier+1)*pi/4)); }

Step 13: Striking (double Axis Snake)

Inspired by a cobra, with this code the snake sits waiting to strike. The code is fairly straight forward in that the snake stays stationary until it is time to strike, at which time it throws servos on pins 3 and 7 forward to attack, then returning to its resting position. Future improvements will include a proximity sensor.

Step 14: Comment on Tree and Pipe Climbing (double Axis Snake)

One of the movements I would have liked to worked on is a tree climbing algorithm which involves a helical spiral rolling over itself like a smoke ring. Unfortunately my 2D snake was not long enough to wrap around a tree. For now I will leave you with this video from Carnegie Mellon university for inspiration. If you want to try working on this yourself I would direct you to read some of the links in the references section for an understanding of the math.

Step 15: Future Developments

The following are a list of improvements I want to make, however reading this Instructable might encourage you to have a go yourself and beat me to it! If you do build your own snake with any of these features I would love to know!

  • Onboard battery, get rid of that tether cable!
  • More servos, longer snake length, but more compact design
  • Remove loose wires and instead have pin contacts between the segments
  • Proximity sensor/and or camera in head
  • Mouth/tongue/fangs/eyes/hissing sound
  • Autonomous movement
  • Tree climbing ability
  • Tunnelling in pipes ability
  • Remote control operated
  • Adapt technology into a robotic underwater eel
  • And perhaps most importantly: redesign the 3D printed segments so that the segments can be rotated 90 degrees easily allowing for you to swap between the 1D snake and 2D snake quickly without needing to print an entirely new snake

Step 16: References

Make it Move Contest

First Prize in the
Make it Move Contest