Make a Crawling Robot Zombie With Severed Legs

by ossum in Circuits > Robots

22748 Views, 163 Favorites, 0 Comments

Make a Crawling Robot Zombie With Severed Legs

montage.jpg
glamour_shots_0001.JPG
glamour_shots_0003.JPG

We all love zombies and robots, two of the things that are most likely to be our undoing one day. Lets help things along by building a creepy little robot zombie.

My goal with this Instructable is to take a doll and (re)animate it with servos and mechanical linkages, its braaaaiiins will be an Arduino microcontroller.

I like to use whatever I can scrounge up for my projects, since it keeps the cost down, but it does mean that maybe you won't have exactly the same items on hand, I will try and give alternatives. Besides, you are a maker, you'll figure it out.

In the same vein, my design has been planned around limited tools, you could make it with anything from hand-tools to CNC cutters or anything in between.

This project could be a pretty good introductory robot for someone who has never built one before.

A word of warning: There was a point in the middle of this project when I was struggling to fit an undead robot into a dress and had to take a step back to ask myself what the heck I was doing with my time... I guess this could happen to you too, don't worry though, I got over it ;-)

Shameless Plug

I started this whole project for an Instructables contest, so if you think it's cool, and you don't want to risk being eaten by spurned zombots, I'd appreciate your vote!

Update: Thanks Guys!

So I won a prize in the Halloween contest, thanks for your votes!

Update 22-Oct-2014: Added Remote Control (step 21) and a more in-depth video

Parts Required

before_2.JPG
before_0.JPG
before_1.JPG
ArduinoNanoFront_3_lg.jpg
power_source.jpg
2014-09-24 10.57.50.jpg
CAM02059.jpg
CAM02061.jpg

Electronics

4x High Torque Servos

The servos for the arms need to be relatively high torque since they have to lift the weight of the robot. I would also recommend going for dual-bearing servos if possible. These have a bearing on either end of the output shaft in order to better handle side loads.

I used TrackStar TS-600MG from HobbyKing since I liked the sound of metal gears and dual ball bearings. The ball bearings turned out to be a fiction though, the shafts are supported by brass bushings.

2x Slim Servos

The servos for the neck (if you choose to animate it) don't have to lift much weight, so small low-profile servos will do. I imagine 9g servos would not be up to the task though.

I used Corona DS-239MG from HobbyKing, since I had them on hand.

Arduino Nano

Use any micro controller that you like, but I am partial to the Arduino Nano, which I buy by the boatload from DealExtreme.

Batteries

I used 6x 4.2V Li-ion batteries that I got for free (they expired 10 years ago, but heck, this is a zombie right?). You can use whatever is available to you, your choice will affect your DC-DC converter requirements.

DC-DC converter/UBEC (Universal Battery Elimination Circuit)

What you need here is dependant on your batteries, read electronics section for more details).

I used an obsolete Volgen FDC15-48S05 which I got out of a scrapped piece of telecoms equipment, so you are unlikely to find one exactly the same.

If you are using RC batteries, you could use a UBEC from the radio control world (HobbyKing has quite the selection, I have read good things about the TURNIGY 8-15A UBEC for robots, but have never tried it myself)

Assorted Bits and Bobs

Connectors (for batteries)

Perfboard/Stripboard (I got from DealExtreme)

Insulated Wire

2.54mm (0.1") header pins (to plug servos into)

Solder

Insulating Tape

Heat Shrink

Mechanical

Torso/Shoulder assembly

6mm MDF (could use acrylic, plywood, aluminium, whatever you have tools to cut)

Wood Glue (if using wood)

Matchsticks/Toothpicks (used as dowels)

Arm Linkages

12mm dowel (I used about 800mm of it)

Aluminium Servo Horns (DX or HK)

Neck Linkage

Bicycle Brake Cable

Brake Cable tube

EZ Connectors for servo cable (DealExtreme equivalent)

Spring/Servo linkage ball joint (could use some rubber hose)

Fasteners

I used M3 (3mm) fasteners for pretty much everything. If you take apart old electronics you will find loads of them (of course they are cheap as chips to buy too).

M3x12mm screws

M3x30mm screws

M3 nuts (Nylocs are handy if you can get them)

M3 washers (split and flat)

Aesthetics

Doll

Bigger is probably better in this case. I found the local Chinese shop and bought one of those cheapies with a cloth body and plastic limbs, which ended up being perfect.

Zombification

Acrylic Paint

Plastic Packets (white preferably)

Popcorn Kernels (for teeth)

Silicone (or other flexible glue)

Superglue/Double Sided Tape

Tools Required

tools.jpg

Mechanical Work

  • CNC router / scroll saw/ laser cutter / jigsaw / handsaw
  • Drill press / Hand drill / Homemade Drill Press
  • Screwdrivers
  • Socket/Spanner

Electrical Work

  • Soldering Iron
  • Pliers
  • Side-cutters

Software

Electronics: Power Supply

power_source.jpg
battery_connector_2.jpg
battery_connector_1.jpg
battery_connector_3.jpg

Power Supply Requirements

Servos

Most servos are rated to run off a supply of 5V or 6V although one will find a handful of HV (High Voltage) ones that will accept 7.4V. Mine are only rated up to 6V, so I needed a way to regulate the voltage.

Arduino

The Ardunio requires a 7-12V input voltage which it regulates down to 5V (or you can directly supply a regulated 5V). The current required will be negligible compared to the servos. Take a look at the datasheet for all the info.

Voltage Conversion

I also didn't have access to any high drain batteries (there is more to a battery than it's capacity and voltage, some batteries are better at delivering all of their power quickly than others, i.e. supplying a lot of current, which is something motors/servos require), so I decide to stack six 4.2V batteries in series, resulting in an output voltage of 25.2V at maximum charge.

Remember that POWER=CURRENT*VOLTAGE (P=IV), so if we assume that the power drawn on the output of the DC-DC converter is equal to the power supplied by the batteries, then the current that the batteries have to supply is only (Vout/Vin)*(Iout), which in my case means an input current about 5 times lower than output current.

You could use a UBEC from the radio control world (Hobby King has quite the selection, I have read good things about the TURNIGY 8-15A UBEC for robots, but have never tried it myself)

I decided to supply power to the Ardunio via a 12V linear regulator instead of the DC-DC converter, just in case that converter browns out or goes into current limiting when the servos exceed its current capability(which would otherwise cause the Arduino to restart). The 12V regulator drops all of the extra voltage as heat, but this is not a big deal since the current draw of the Arduino is so low. The Arduino's on-board regulator drops the 12V down to 5V.

Wiring

The wiring for the power supply is incredibly simple. I made a little piece of stripboard with 2.54mm header pins on it to plug the batteries into, connecting the positive of one to the negative of the next. I have shown it for completeness, but if you are using an RC battery you won't need such a contraption.

I also included and on/off toggle switch.

Read the documentation that comes with you DC-DC converter carefully and you will see which pins/plugs get the +/- from the battery and which pins/plugs provide the output.

  • If you buy from HobbyKing it is always wise to read people's comments on the product, it seems like markings/documentation is not to be trusted.
  • If you are using scavenged components like I did, then Google is your friend. I was able to find a datasheet for my converter without much trouble by searching for numbers that were printed on it.

Electronics: Build the Circuit

wiring_diagram.jpg
2014-08-03 22.16.35.jpg
2014-08-04 14.51.00.jpg
2014-09-20 14.26.57.jpg
TO-220AB.jpg

The circuit is actually extremely simple; all we are doing is providing power to the Ardunio and the servos.

  • Provide 12V power to Ardunio (Arduino will regulate this down to 5V)
  • Provide 5V power to the servos
  • Connect the Arduino's output pins to the servo's data lines.

Arduino and Servo Pins

I started by soldering the Arduino into a piece of perfboard and putting in 3 rows of 2.54mm header pins next to digital pins 2,3,4,5,6 and 7.

Connect each of the pins in the row closest to the Arduino to the adjacent digital pin.

The 2nd row from the Arduino is the 5V power supply rail for the servos, connect them all together.

The 3rd row from the Arduino is the negative (ground) power supply rail for the servos, connect them all together.

Power

In the previous step we planned out our power requirements and made connectors for the batteries.

I soldered a corresponding connector onto the perfboard and ran the positive to the positive inputs of the DC-DC converter and the 12V regulator.

Run the negative of the battery connector to the negative inputs/ground pins of the DC-DC converter and the 12V regulator.

Take the 5V output of the DC-DC converter to the center row of servo pins and the ground/negative output to the outermost row of servo pins.

Take the 12V regulator's output to the "Vin" pin on the Ardunio and make sure that at least one of the Ardunio's "GND" pins is connected to ground (battery negative).

Capacitors

The capacitors are there to help the regulator out by supplying some of the current to the servos under peak conditions. They act like tiny batteries that can discharge and recharge incredibly quickly.

I would start with two 560uF caps and see how you go, it was plenty for me.

Put your chosen capacitors between the Ground and 5V servo rails. If you are using electrolytic capacitors, make sure to get the polarity right (the side with the stripe is negative)

Planning, CAD Work

Stayin-alive.gif
rendered_down_back_small.gif
crawl_01_small.gif

At this stage I just had a concept that I was going to make a "half-a-zombie" crawler, so I started sketching things to figure out how to make the robot actually move. I find pen and paper planning to be incredibly helpful. I normally end up with pages and pages of doodled drawings before I break out the CAD tools. For this partiular project it meant a lot of sitting sketching, waving my arms around like a half-zombie, while my wife tried to watch a TV show.

The design i went with in the end has what robotocists call "2 degrees of freedom" (DOF) per arm.

  1. The shoulder, which can move up and down,
  2. The "elbow" which moves forwards and backwards linearly. The servo's rotational movement is converted in (mostly) linear movement by adding a 2nd linkage, turning the arm into a parallelogram

Now that I had a design in mind I started to draw things up in CAD. There are a number of tools to use for this (AutoDesk inventor looks like a good free option if you are a student), but I used SolidWorks, since I have access via work. The whole topic of 3D cad is too much to cover here, but these are the things I focused on

  • Although I had access to a CNC router, didn't want to end up with a design was impossible to fabricate without one. All of the parts are 2D, which means they can easily be cut with a laser cutter, scroll saw or even hand saw.
  • I had 6mm MDF available, so I designed around that. The design could easily be adjusted for other thicknesses by adjusting the depth of the cutouts.
  • The smallest bit available on the CNC router I used was 6mm, hence why there are no inside diameters smaller than that.

I have attached my DXF files if you intend to use a CNC tool and also PDFs which you could print to scale and stick on to your material before cutting by hand.

If you use my designs you will want to confirm that the hole placings work for your servos.

Torso: Cut and Glue

mechanical_construction_002.JPG
mechanical_construction_006.JPG
mechanical_construction_003.JPG
mechanical_construction_004.JPG
mechanical_construction_007.JPG
mechanical_construction_008.JPG
mechanical_construction_012.JPG

Cutting

The first step here was to cut out the pieces. There is not much to say about this.

  • If you are using a CNC machine (laser cutter or router) then you will use the DXF files you created in the CAD step (mine can be found in the planning step if you want to use them).
  • If you are doing it by hand and want to use my design, print out the PDF (found in the planning step) of the parts and stick it to your material, then cut out using a scroll saw, hacksaw or whatever other tools you can get your grubby paws on. One could definitely simplify some of the shapes if cutting by hand.

Fastening

Next up is fastening the pieces together. Since I had my pieces cut out of 6mm MDF I used PVA (Alcolin Cold Glue in particular), if you cut your pieces out of acrylic or other material you will need to choose a suitable glue. This is where it comes in handy to have a lot of clamps,. As you can see from my pictures, I do not have enough clamps, so I used my vise too.

The parts were very rigid once glued, but I decide to glue in some dowels for extra strength. Once the pieces were glued together I drilled some small holes perpendicularly through adjoining pieces and inserted matchsticks covered in glue (matchsticks or toothpicks are ideal, drill your holes accordingly). It is worth noting though, MDF does not like having its edges drilled into and likes to split; I got around this by drilling with successively larger bits, from 2mm to 2.5mm to 3mm.

Once all the glue is set, use a bit of sandpaper to clean up any dowels that are sticking out.

Torso: Shoulder Assembly

mechanical_construction_013.JPG
mechanical_construction_017.JPG
elbow servo.jpg
shoulder_support_screw.jpg

Now that your pieces are glued into 3 distinct assemblies, torso, left shoulder bracket and right shoulder bracket, you can begin installing the servos.

IMPORTANT: You must center the servos before you attach the horns. The easiest way to do this is with a servo tester (like in my picture) but if you don't have you may as well use your Arduino. See the "Servos: A Refresher" step if you need more help.

I installed the shoulder servos with some M3x12mm pozi screws and M3 nuts, along with spring washers to prevent things vibrating loose (you could also use Loctite).

Attach the servo horn to the shoulder servo. I made mine horizontal, but in retrospect I wish I had installed them with about 20 degrees down-tilt. Your zombie has no reason to lift its arms in the air like it just don't care, but being able to tilt them lower down would increase "ground clearance" when crawling.

Attach the shoulder brackets to the servo horns. I had to use hex socket screws here, since it is impossible to get a screwdriver in there.

Now you can install the elbow servos, with the same size screws and nuts as the shoulders.

Finally, insert a long screw through the hole that is opposite the shoulder servo's pivot point, this helps prevent too much side-load being placed on the servo's bearings. There is probably a neater solution, but I coldn't think of a cheaper one. Since the arms are not moving at high speed the friction really shouldn't be an issue. I used an M3 Nyloc nut to hold the screw in place, but made sure not to tighten it too much (we don't want it to cause friction, just to stop the screw falling out).

If you don't have access to any Nylock nuts, just use two regular nuts and tighten them towards each other.

Making Arm Linkages

parallel_arms.jpg
arms_0.jpg
arms_bottom.jpg
arms_1.jpg
arms_2.jpg
arms_for_access.jpg
arm_fasteners_0.jpg
arm_fasteners_1.jpg

Design Theory

As discussed in the planning stage, the arms need to form a parallelogram, this means the "forearm" bar will always stay parallel to an imaginary line drawn between the servo shaft and the pivot point of the parallel bar. The parallel linkage effectively turns the rotation of our "elbow" servo into a forwards/backwards motion on the forearm bar.

For clarity see the annotated image: If A and B are the same length and C and D are the same length, A will always be parallel to B.

So long as your arm pieces form parallelograms they can be any length that you like. Choose something that suits the scale of you doll. Keep in mind that there is a compromise when increasing the length of your robot's upper arms. Longer upper arms mean that you can lift the torso higher off the ground, but they also mean that your servos need to generate more torque.

Building an Army

You will have seen arms made of aluminium bars in some of my photos. These were my first concept, but proved too difficult to work with my limited tools. After a bit of thinking I chose to use 12mm hardwood dowels instead, these were great, easy to work with hand tools and plenty strong enough.

You will need to decide exactly where to put your holes, based on your servo horns and arm lengths. The only suggestion I have is to trim the arm on the servo so that you can remove it from the horn without removing the horn from the servo, if you look at the pictures you will see that I did this towards the end.

I also chose to plane the sides flat on some of the arms, this was mostly because the screws I had were either a touch too short or far too long.

In order to tie the parallel bars to the forearm I used long screws (about 30mm, but will depend on your dowels) with Nyloc nuts tightened until just before they started to cause friction.

Mounting for the Doll Arms

While you are at it, you probably want to test fit the doll arms and make some method of mounting them.

The way I did it was to drill a hole though the arm that was a fraction smaller than an M3x16mm threaded hex circuit board spacer, then carefully tap the spacer into the hole. Since the wood deformed around it it was very solid, but I squeezed a bit of super glue around it anyway to keep it in place. I also put a very short screw with a washer in the back side, which meant it couldn't be pulled back through the hole, even if the glue failed. I now had a threaded hole to use to fasten the plastic doll arms to the forearm bar.

Add an Electronics Tray

electronics_tray_1.jpg
electronics_tray_0.jpg
electronics_tray_populated.jpg
electronics_tray_2.jpg

You will need to make a small tray to carry the electronics as well as the neck servos if you choose to use them. Besides carrying the electronics, this tray serves the important function of giving the arms something to leverage against. You will find that the robot struggles to move without it.

This tray could easily be built into the original design as an additional piece, but I was designing this thing on the fly, so mine is separate. I simply grabbed a scrap bit of 4mm plastic and used a some discarded right-angled aluminium extrusion to bolt it to the torso.

The size of your tray will depend very much on what kind of electronics/power system you go with. I have attached some pictures of my final setup for an idea of how I arranged things. More info on the individual items can be found in the relevant steps.

Note: If you are planning to use neck servos you will probably want to drill the holes through the torso before attaching the electronics tray, since it will get in the way

Neck Linkage

neck_0.jpg
neck_1.jpg
neck_2.jpg
neck_4.jpg
neck_5.jpg
neck_3.jpg
2014-09-11 12.09.06.jpg
neck_fail_0.jpg
neck_joint_failure.jpg

You don't need to animate the head if you want to save on servos, but thought it would be cool, so I did. If you decide not to, then perhaps try mount it on a spring, that should give it a bit of a zombie-ish wiggle.

Head mounting plate

You need something to mount the head to. I chose to use my holesaw that was closest in diameter to the doll's neck to cut out a disk of MDF. If I was doing it again I might use wood (because MDF dislikes having things screwed into its edges) or find a different way altogether.

I then drilled three holes into the edges of of the disk, with successively larger bits to prevent splitting, and inserted some M3 threaded hex circuit board spacers and glued them in place. I drilled matching holes in the doll's neck so that I could now easily attach the head to the board with short machine screws.

Linkage

My original plan was to use an RC ball-joint on a swiveling screw as the neck joint, but this didn't give me the fluid movement I had hoped. I have included photos of it anyway, since it shows how many ways there are to achieve things.

In the end I used a spring instead, which gave much smoother movement. I believe the spring came from a desk lamp of sorts, but it is hard to tell, since more recently it just came out of my box-o-springs.

Neck Muscles and Tendons (servos and Cables)

neck_motion_optomized_diff.gif
tendons_0.jpg
tendons_1.jpg
tendons_3.jpg
tendons_4.jpg
tendons_5.jpg
tendons_6.jpg
zombaby_head_4_small_optimized.gif

In order to make the head move I decided to go with servos mounted at the back of the robot, with their force applied to the head via push-rods, similar to how most RC planes control their flaps.

Choose Push-Rods

My first attempt was to use stiff wire push-rods from an old RC plane I found in the scrap, but they were too rigid to go through the bends in the tubes without encountering huge friction. I then discovered some flexible stranded cable from bicycle brakes/gears which worked much better. It has a perfect compromise between flexibility (needed to go around bends in the tube) and rigidity (needed to prevent too much bending where the wire is outside the tube) for this application.

Here is a website that explains all sorts of connectors and things that the RC plane folk use.

Mount the Servos

I chose to use low-profile wing servos, since the head really doesn't require much force to move and the space savings were attractive. Mounting these was easy too, since the mounting tabs are parallel to the electronics tray. I drilled some 3mm holes and attached the servo with M3 screws and bolts.

One of my servos was pretty beat-up and missing a mounting tab, so I just glued it in place with silicone, which worked well, so that is an option too.

Locate and Drill the Holes for Push-Rods/Cables

Look at my photos for guidance, but you will need to determine where to put the holes for your push-rods. Take the following into account:

  • The tubes should have as few bends/kinks as possible
  • The cables should exit the tubes as close to the servos and neck as possible, without requiring the cable to flex too much.

Zombification: Skin/Painting (arms)

doll_arms_0.jpg
doll_arms_1.jpg
doll_arms_2.jpg
doll_arms_3.jpg
head_0.jpg
head_1.jpg
head_2.jpg
head_3.jpg
head_4.jpg
2014-09-19 23.32.39.jpg

Rot that Skin

I made use of a great technique that I found here on instructables to zombify the doll's skin.

I started by wrapping the limbs and face tightly with plastic from shopping packets, securing the ends with a few dabs of Cyanoacrylate (Super Glue) or thin double-sided tape. Don't use silicone here, because the paint won't stick to it (or at least, Acrylic wouldn't stick to the marine sealant I used in places)

One the limbs are wrapped up, use a heat gun on them, the heat causes the plastic to shrink and deform, tightening around the doll parts and creating a great basis for zombie skin.

Paint that Skin

One you are satisfied with your skin effect it is time to start painting. I used acrylics and was very pleased with how it turned out. I only used 3 colours in the end, white, black and a sort of reddish brown.

You could choose to make your zombie browner, greener, juicier or fresher, depending what flavour of undead you prefer. I went with a pale-dead kind of look this time.

I started off by painting pretty much everything white because I didn't want the difference in the fleshy doll skin and white plastic to mess up my paint job.

Then I did various layers of slightly watered-down grey/pink/brown.

Don't be afraid to use your fingers as well as the brushes or anything else you can get your hands on. I smeared a pinkish mix on with my hands in places and stuck darker colours in the crevices with a toothpick.

To bring detail out in the long folds I put blobs of darker paint on the fold then drag it down with the crease, this gets paint stuck in the deeper bits and wiped off the higher bits, which helps bring out the texture.

Don't be afraid to just go bananas, after all, it's a zombie so it is very forgiving.

Zombification: Teeth

teeth_0.jpg
teeth_1.jpg
teeth_2.jpg
teeth_3.JPG

Most dolls have very un-zombie little mouths, so I decided to correct that.

The First Cut is the Creepest

I considered a gaping mouth, or even one that could be controlled by servos, but in the end decided to go a little more subtle. I used a Dremel cut-off wheel and some side-cutters to enlarge the mouth on one side into a bit of a grimace.

Make the Teeth

I then got some popcorn kernels an used wood glue to stick them to a piece of thin card (I would recommend black card, not blue like I used) . I also built up the "gums" with some contact adhesive glue (the wood glue was too fluid to do it).

Paint

The paint was done in multiple layers, starting with light washes, followed by blacks and browns. I wiped most of the paint off with a rag on each pass, leaving darker areas in the gaps between teeth. Finally I put some thick reddish brown on the gums.

Glue in Teeth

Once the paint was dry I used some marine silicone to stick the whole assembly into the head (any glue will work, so long as it is flexible and sticks to the plastic). Choose a glue that is nice and tacky, or set very quickly, because it is very awkward to hold the teeth in place via the neck.

Zombification: Flexible Skin

2014-09-20 15.27.09.jpg
2014-09-24 11.44.14.jpg
2014-09-24 11.44.46.jpg
2014-09-24 12.29.26.jpg

There were a few places where the doll's dress did not cover the mechanics, in particular, the elbows and the neck. I corrected this by creating "skin" with some more white plastic.

Neck

  1. Wrap a bit of thin double sided tape or some super glue around the neck.
  2. Make a ring of fencing wire, just narrower than the shoulders
  3. Make a cone/ tube of white plastic (from a shopping packet) from the neck to the shoulders.
  4. Paint to match the other skin

I was going to glue magnets onto the top of the torso to hold the wire ring in place, but it turned out not to be needed, since the ring was larger than the collar of the dress. Your mileage may vary.

Arms

The pictures describe this step better than words will. I extended the arms with tubes of plastic which was then painted to match the rest of the skin. You may have to take more or less care here depending on the dol's clothes. I just needed to cover the elbows, so I just tucked the plastic into the sleeves.

Zombification: Severed Leg

leg_0.jpg
leg_1.jpg
leg_3.jpg
leg_4.jpg
CAM02045.jpg

Bone

Take a left-over piece of dowel from the arm assemblies and carve a bone shape out of it, making sure to leave a bit of extra length on the end.

I did all of the carving with a Dremel sanding band, but you could just as easily achieve it with a file and some sand paper.

Leg

Chop a piece of one of the unused doll legs and wrap it in plastic, then hit it with the heat gun, just like the previous steps.

Use a paintable caulk to fill up the end of the leg. I stuck some torn-up bits of plastic bag in there to make it extra fleshy.

Once you have filled the top of the leg with caulk you can stick the bone in (before it sets). Make sure that it is inserted to a depth that makes sense. Since I included a head on my bone I made sure it lined up with where the knee would be.

Paint

Paint it.

I used exactly the same methods as described in the earlier steps.

Attach

I drilled a hole through the top of the leg and use a bit of fencing wire to attach the leg to the electronics tray.

A loose attachment is preferable so that the leg flops about nicely when the doll crawls.

Zombification: Weathered Clothes

dress_before.jpg
dress_weathered.jpg

Zombies are not known for their dress sense, so we need to mess up the clothes a little bit.

The doll I bought was wearing a rather fancy dress, made of the cheapest, most synthetic, material known to mankind.

I tried everything I could think of to weather it, but nothing really gave me the effect I was looking for.

Bleach

The bleach made no difference... I had read that it would yellow and damage synthetics, but apparently not this stuff.

Coffee

Adam Savage always talks of spritzing clothes with coffee to weather them. Either he drinks way stronger coffee than me (unlikely) or they should make baristas' uniforms out of this stuff.

Tea

Because, why not? No real effect.

Acrylic Paint

In the end this was the most effective, and still only barely. I rubbed in watered down white paint to the dark areas to try and wash them out.

I also grubbed up the white areas with some green/brown paint and added some reddish brown around the collar.

Servos: a Refresher

CAM01831-MOTION.gif
2014-06-23 14.08.55.jpg

Before we can get started on writing code, let's just refresh on how and what servos do.

Your standard hobby servo consists of the following major parts

  • DC Motor
  • Gearbox (LOTs of reduction)
  • Potentionmeter (variable resistor)
  • Control Circuit

The potentiometer is connected to the output shaft of the gearbox. The control circuit uses this potentiometer to determine what angle the output shaft is at and therefore, which way and how much it needs to turn the DC motor in order to achieve the angle requested by the input signal. The potentiometer itself can generally not turn more than 180 degrees and as such there is a mechanical limit to how far a servo can turn (although one does get special servos that can turn further, or even continuously).

Control Signal

The control signal is actually a 5V pulse of 1000 to 2000 microseconds, where 1000 indicates minimum rotation and 2000 indicates maximum rotation, and all the values in between. These control pulses are sent every 20 milliseconds.

What all of this means is that we can use a microcontroller to generate the pulses which will set the servo shaft to a specified angle.

Connector

The standard servo connector has 3 sockets and fits onto a 2.54mm (0.1") row of male header pins. The connectors can have a variety of colour schemes, but they are usually:

  • Ground: Black/Brown
  • +5v: Red
  • Signal: Orange/White

Code: Planning

rendered_down_back_small.gif
crawl_01_annotated_slow.gif
crawl_01_small.gif

Translate Movement into Servo Positions

It is easy to describe how the arms need to move to make the zombot move forwards, but how do we convert that into servo movements?

First, lets describe how we would move forwards if we were lying on the ground and could only use our arms.

  1. Raise arm off the ground
  2. Extend arm as far forward as we can
  3. Lower arm and grab the ground
  4. Pull ourselves forwards (pull arm back)

We could do this with both arms synchronised (like swimming butterfly) or with alternate arms (like swimming crawl).

I will work the example with the crawl option, you can easily use the same procedure to generateother patterns of movement.

Left Arm Right Arm
Raised, Pulled Back Lowered, Extended Forward
Raised,Extended Forward Lowered,Pulled Back
Lowered,Extended Forward Raised, Pulled Back
Down Pulled Back Raised, Extended Forward

The most logical way I could think of to implement this in code was to define a series of "frames" which contained the position of all the servos at a given moment. Looping through the frames at a given rate will give us a movement animation.

Here I am considering raising/extending as "maximum" and lowering/retracting as "minimum".

Frame Left ShoulderLeft ElbowRight ShoulderRight Elbow
1 MaxMinMinMax
2MaxMaxMinMin
3MinMaxMaxMin
4MinMinMaxMax

Determine Servo Limits

Before we can write code to use our fancy new frame-by-frame animation, we need to determine the minimum and maximum for each servo. There are two major factors to consider

  • There could be a physical obstruction. if your mechanical assembly doesn't allow your servo to turn as far as your software requests it could damage the servo.
  • We need to translate "min" and "max" into milliseconds, and these are opposite on either side of the body. For example: the shoulder servo (looking from the front) on the right hand side needs to turn clockwise to raise the arm, but on the left hand side clockwise will lower the arm.

I wrote the following tiny piece of code to determine the range of motion of a servo. Simply upload it to your arduino and connect a servo to the specified pin (pin 3 in the example).

  • Use a serial terminal (I prefer putty) to connect to the Arduino (9600 Baud).
  • Press 'q' to send the servo to min (1000 microseconds)
  • Press 'w' to center the servo
  • Press 'e' to send the servo to max (2000 microseconds)
  • Use 'o' and 'p' to incrememnt or decrement the current position by 5 microseconds
  • Make note of how many microseconds correspond to retracted/lowered
  • Make note of how many microseconds correspond to extended/raised

Once you have determined how many microseconds correspond to retracted/lowered and extended/raised, do the same for all the other servos.

// By Jason Suter 2014
// This example code is in the public domain.
#include <Servo.h> 


//pin details
int servoPin = 3;

static int minMicros = 1000;
static int midMicros = 1500;
static int maxMicros = 2000;
 
Servo servoUnderTest;  // create servo object to control a servo 
 
int posMicros = 1500;    // variable to store the servo position 
 
void setup() 
{ 
  servoUnderTest.attach(servoPin);
  
  //configure serial port  
  Serial.begin(9600);
} 
 
 
void loop() 
{ 

  if (Serial.available() > 0) {
    char inByte = Serial.read();; //incoming serial byte
    if (inByte == 'q') {
      posMicros = minMicros;
    }
    else if (inByte == 'w') {
      posMicros = midMicros;
    }
    else if (inByte == 'e') {
      posMicros = maxMicros;
    }        
    else if (inByte == 'o') {
      posMicros = max(posMicros-5,minMicros);
    }        
    else if (inByte == 'p') {
      posMicros = min(posMicros+5,maxMicros);
    }
}
    //report current position
    Serial.print(posMicros);
    servoUnderTest.write(posMicros);

} 

Downloads

Code: Crawl

coding2.gif

Arduino Tools and Foundation Knowledge

If you are looking for a good introduction to Arduinos, then there are a whole lot of Instructables for you, here is a great one.

I am going to assume you have downloaded and install the Arduino IDE and are able to upload code to your Arduino, the previously mentioned instructable will guide you through all of that and more.

Code Overview

In the previous step we decided that we would implement the crawling by cycling through "frames" of animation which would define the position of each of the servos at a given point in time.

The full code is attached as a file, but I will go through most of the sections here so that you can understand them. If you already do understand them, then that's great, because you can probably think of a better way of doing this that I did.

Import Required Libraries

The only library that is required for this code is "Servo.h" which is full of handy functions to make it extremely easy to control servos from an Arduino.

#include <Servo.h>

Define Global Variables

These are variables that can be accessed from anywhere in the code. We use them to store information that will be required at a later stage

Timer Variables

The millis() command returns the number of milliseconds since the Arduino board began running the current program. We can store the result of millis() and then compare it to the result at a later stage to determine how long it has been between the two calls.

long previousFrameMillis = 0;
long previousInterpolationMillis = 0;

Servo Detail Variables

These variables store which pin each servo is connected to, as well as how many micros correspond to min (retracted/lowered) and max (raised/extended)

//servo pins
int LNpin = 7; //left neck muscle
int RNpin = 6; //right neck muscle
int LSpin = 5; //left shoulder
int LEpin = 4; //left elbow
int RSpin = 2; //right shoulder
int REpin = 3; //right elbow

//these are the 'min' and 'max' angles for each servo
//can be inverted for servos that are in opposite orientation
int LSMin = 1000;
int LSMax = 1700;

int RSMin = 2000;
int RSMax = 1300;

int LEMin = 1700;
int LEMax = 1300;

int REMin = 1300;
int REMax = 1700;

int LNMin = 1400;
int LNMax = 1600;

int RNMin = 1600;
int RNMax = 1400;

Frames

One's first impression might be that we should just set the servos to the positions defined by the first frame, wait a given delay (frameDuration) and then set the servos to the next position.

This will result in a horrible animation though, since the servos will jump as fast as they can to the specified position and then wait there for their next instruction.

The way around this is to interpolate between frames. In other words, If my frame duration is 2 seconds, after 1.75 seconds I want the servos to be three quarters (or 75%) of the way between frame 1 and frame 2.

It is a trivial bit of maths to figure out where the servo should be if we know how much of the frame has elapsed as ratio. In words it is just (previous frame)+(the difference between the next and previous frames)*(percentage of frame duration elapsed), this is known as "linear interpolation".

int frameDuration = 800; //length of a frame in milliseconds
int frameElapsed = 0; //counter of milliseconds of this frame that have elapsed
int interpolationDuration = 30; //number of milliseconds between interpolation steps  

Here we define the actual frames. Note that I used 0 to 1000, where 0 indicates retracted/lowered and 1000 indicates extended/raised. I could have chosen any number, and there could well be more logical choices, but the range provided me with a satisfactory compromise between resolution and readability.

We will use the map() function later to map 0 to LSMin variable and 1000 to LSMax variable that we defined earlier (obviously this example is for the left shoulder, but it would be the same process for all the other servos).

If you wanted to define more complex or smoother animations you could easily add more frames and also use numbers other than min/max. One option would be to use about 8 frames to make a nice elliptical motion.

//frames for the crawling gait are stored here
int currentFrameIndex = 0;
int numFrames = 4;
int crawlGaitRS[] = {0,1000,1000,0};
int crawlGaitRE[] = {0,0,1000,1000};
int crawlGaitLE[] = {1000,0,0,1000};
int crawlGaitLS[] = {0,0,1000,1000};
int crawlGaitLN[] = {1000,1000,0,1000};
int crawlGaitRN[] = {0,1000,1000,1000};
In order to implement this interpolation calculation we need to keep track of the previous frame and the following frame, so we set up some variables to do that.
//servo last frame micros
int LSlast = 0;
int RSlast = 0;
int LElast = 0;
int RElast = 0;
int LNlast = 0;
int RNlast = 0;

//servo next frame micros
int LSnext = 0;
int RSnext = 0;
int LEnext = 0;
int REnext = 0; 
int LNnext = 0;
int RNnext = 0;

// variable used to store the current frame of animation
int currentFrameIndex = 0;


Servo Objects

Finally, we create some servo objects and assign them to variables. These are instances of the servo class that we included in Servo.h and will provide useful functions to control each servo.

// create servo objects to control servos
Servo LS;
Servo RS;
Servo LE;
Servo RE;
Servo LN;
Servo RN;

Define Functions

Arduino Setup Function

The Arduino setup() function is the first bit of code that gets run after the global variables have been defined. All that is needed here for now is to attach the servo objects to their pins and start up the Serial port, in case we want to report anything for debuggging.

void setup() 
{ 
  Serial.begin(9600);
  LS.attach(LSpin);
  RS.attach(RSpin);
  LE.attach(LEpin);
  RE.attach(REpin);
  LN.attach(LNpin);
  RN.attach(RNpin);
}

Set Next Frame

This function is called once our servos get to the end of a frame. All that it is doing is:

  • Incrementing the "currentFrameIndex" (unless we have have reached the last frame, in which case it loops back to frame 0)
  • Store the current frame position as "last frame"
  • Retrieve the next frame position from the animation array
void setNextFrame()
{
  //if this was the last frame, start again
  if (currentFrameIndex < numFrames - 1) { 
    currentFrameIndex++;
  }
  else {
    currentFrameIndex = 0;    
  }

  //we have reached the destination frame, so store it as "last frame"
  LSlast = LSnext;
  RSlast = RSnext;
  LElast = LEnext;
  RElast = REnext;
  LNlast = LNnext;
  RNlast = RNnext;

  
  //generate new "next frame"
  LSnext = crawlGaitLS[currentFrameIndex];
  RSnext = crawlGaitRS[currentFrameIndex];
  LEnext = crawlGaitLE[currentFrameIndex];
  REnext = crawlGaitRE[currentFrameIndex];
  LNnext = crawlGaitLN[currentFrameIndex];
  RNnext = crawlGaitRN[currentFrameIndex];

}

Interpolation Function

As described previously, we will use a linear interpolation to determine exactly what position a servo should be in at a given time between two frames.

My computer science lecturer always said that being a good programmer was all about being lazy, if you can avoid rewriting code multiple times by putting it in a function, then do it.

This function simply implements the linear interpolation equation between two frames and then maps that frame position to a servo position and applies it to the servo object.

void writeInterpolatMicros(Servo servo, int prevFrame, int nextFrame, int servoMin, int servoMax, float elapsedRatio) {
    int interpolated = prevFrame + int(float(nextFrame - prevFrame)*elapsedRatio);
    servo.writeMicroseconds(map(interpolated,0,1000,servoMin,servoMax));

}

Servo Update Function

This function makes the code neater by removing a chunk of it from the main loop.

First the ratio of the frame that is already completed is calculated using the number of milliseconds that have elapsed since the frame started, divided by the number of milliseconds it takes to complete a frame.

This ratio is passed along to the interpolation function for each servo, updating each one's position.

void updateServos()
{
    
    float frameElapsedRatio = float(frameElapsed)/float(frameDuration);

    writeInterpolatMicros(LS,LSlast,LSnext,LSMin,LSMax,frameElapsedRatio);
    writeInterpolatMicros(LE,LElast,LEnext,LEMin,LEMax,frameElapsedRatio);
    writeInterpolatMicros(RS,RSlast,RSnext,RSMin,RSMax,frameElapsedRatio);
    writeInterpolatMicros(RE,RElast,REnext,REMin,REMax,frameElapsedRatio);
    writeInterpolatMicros(LN,LNlast,LNnext,LNMin,LNMax,frameElapsedRatio);
    writeInterpolatMicros(RN,RNlast,RNnext,RNMin,RNMax,frameElapsedRatio);

}

Main Loop

The main loop() is where all the action happens, as soon as it finishes executing all the code contained inside it it jumps back to the beginning and starts all over again.

The first step in the main loop is to record the current number of milliseconds since the program started running) so that we can determine how much time has elapsed since the last iteration of the loop.

Using this time we can determine whether the elapsed time is greater than the period we defined for interpolation steps, if so, call the updateServos() function to generate new interpolated positions.

We also check whether the elapsed time is greater than the frame duration, in which case we need to call the setNextFrame() function.

void loop() 
{ 
  unsigned long currentMillis = millis();
    
  if(currentMillis - previousInterpolationMillis > interpolationDuration) {
    // save the last time that we updated the servos
    previousInterpolationMillis = currentMillis;   
    //increment the frame elapsed coutner
    frameElapsed += interpolationDuration;
    //update the servos
    updateServos();

  }

  if(currentMillis - previousFrameMillis > frameDuration) {
    // save the last time that we updated the servos
    previousFrameMillis = currentMillis;
   //reset elapsed frame tiem to 0
   frameElapsed = 0;
    //update the servos
    setNextFrame();
  }
 

Downloads

Code: Adding Control

code_pic.jpg

In the previous step we defined a basic crawl routine and discussed how to use interpolation to move the servos smoothly. Now, what if you want to control your Zombot?

There are oodles of ways to implement this, both in the hardware and the software. I chose to handle it using the Arduino's serial connection, which means it will be really trivial to control the robot wirelessly via Bluetooth, or as I am doing at the moment for debugging, simply via the USB cable.

This is by no means the most elegant way of achieving these results, but it is hopefully easy to read and understand, as well as flexible.

As before I will attach the full code below, but I will go through the key points here.

Communication Protocol

I have decided to set my communication protocol up in the following way.

  • Each data packet (instruction) from the controller to the robot is a string of characters consisting of two parts, separated by a ":" character.
    • The "command" comes first and tells the robot what to do (for example: begin moving)
    • The "argument" comes second and provides extra information
  • Each data packet begins with a "[" and ends with a "]"

Define Global Variables

In addition to the previously defined variables, we now need to to setup some variables which will be used in our communication protocol via serial.

char inDataCommand[10]; //array to store command
char inDataArg[10]; //array to store command argument
int inDataIndex = 0; //used when stepping through characters of the packet
boolean packetStarted = false; //have we started receiving a data packet (got a "["
boolean argStarted = false; //have we received a ":" and started receiving the arg
boolean packetComplete = false; //did we receive a "]"
boolean packetArg = false; //are we reading the arg yet, or still the command

Define Functions

Read a Command From Serial

This function can be called to check the serial interface for received commands.

It checks the incoming serial bytes (characters) one by one, throwing them away unless they are a "[" which indicates the beginning of a command.

Once a command has been started, each byte is stored into the "command" variable until a ":" or "]" is received. If a ":" is received, we begin storing the following bytes into the "argument" variable until a "]" is received.

If at any point a "[" is received during the reading of another instruction, that previous instruction is discarded. This prevents us getting stuck if someone never transmitted a "]" end-of-command character and we wanted to send a new command.

Once a full command has been received the "processCommand" function is called, which will actually interperet and action the command.

void SerialReadCommand() {
  /*
  This function checks the serial interface for incoming commands.
  It expects the commands to be of the form "[command:arg]" 
  where 'command' and 'arg' are strings of bytes separated by the 
  character ':' and encapsulated by the chars '[' and ']'
  */
  if (Serial.available() > 0) {
    char inByte = Serial.read();; //incoming byte from serial
    if (inByte == '[') {
      packetStarted = true;
      packetArg = false;
      inDataIndex = 0;
      inDataCommand[inDataIndex] = '\0'; //last character in a string must be a null terminator
      inDataArg[inDataIndex] = '\0'; //last character in a string must be a null terminator
    }
    else if (inByte == ']') {
      packetComplete = true;
    }
    else if (inByte == ':') {
      argStarted = true;
      inDataIndex = 0;
    }
    else if (packetStarted && !argStarted) {
      inDataCommand[inDataIndex] = inByte;
      inDataIndex++;
      inDataCommand[inDataIndex] = '\0';
    }
    else if (packetStarted && argStarted) {
      inDataArg[inDataIndex] = inByte;
      inDataIndex++;
      inDataArg[inDataIndex] = '\0';
    }
    
    if (packetStarted && packetComplete) {
      //try and split the packet into command and arg
      Serial.print("command received: ");
      Serial.println(inDataCommand);
      Serial.print("arg received: ");
      Serial.println(inDataArg);
      
      //apply input
      processUserInput();
      
      packetStarted = false;
      packetComplete = false;
      argStarted = false;
      packetArg = false;
      
    }
    else if (packetComplete) {
      //this packet was never started
      packetStarted = false;
      packetComplete = false;
      argStarted = false;
      packetArg = false;
    }
    
      
  }  
}

Process a Command

Once a valid command (and optionally an argument) have been received, they need to be procesed, so that we can take the appropriate action.

For the moment I have not needed more than one byte of information to define a command, so we just look at the first byte of command. Using this byte as the argument for a switch() statement allows us to perform a function defined by the command byte.

In this example we are looking for the character "w" , "s" or "c".

If "w" is received, then the animation frames are overwritten with a new animation that defines a "butterfly" movement.

If "s" is received, then the animation frames are overwritten with a new animation that defines a "crawl" movement.

If "c" is received, then the animation frames are all set to the same position, effectively stopping all movement.

Since one cannot re-assign all of the values in an array at once, we first define a new temporary array for each servo, containing the new frames, then use the "memcpy" to copy those values over the actual frame array's location in memory.

void processUserInput()  {
  /*for now all commands are single chars (one byte), can expand later if required
  char commandByte = inDataCommand[0];
  if (commandByte != '\0') {
    switch (commandByte) {
    case 'w': {
      //synchronised forwards (butterfly)
      numFrames = 4;
      int newRS[] = {0,1000,1000,0};
      int newRE[] = {0,0,1000,1000};
      int newLE[] = {0,0,1000,1000};
      int newLS[] = {0,1000,1000,0};
      int newLN[] = {1000,1000,0,1000};
      int newRN[] = {0,1000,1000,1000};
    
      memcpy(crawlGaitRS, newRS,numFrames);
      memcpy(crawlGaitRE, newRE,numFrames);
      memcpy(crawlGaitLE, newLE,numFrames);      
      memcpy(crawlGaitLS, newLS,numFrames);
      memcpy(crawlGaitLN, newLN,numFrames);
      memcpy(crawlGaitRN, newRN,numFrames);
      break;
    }
    case 's': {
      //crawl stroke, 180 degrees out of phase
      numFrames = 4;      
      int newRS[] = {0,1000,1000,0};
      int newRE[] = {0,0,1000,1000};
      int newLE[] = {1000,0,0,1000};
      int newLS[] = {0,0,1000,1000};
      int newLN[] = {1000,1000,0,1000};
      int newRN[] = {0,1000,1000,1000};
      
      memcpy(crawlGaitRS, newRS,numFrames);
      memcpy(crawlGaitRE, newRE,numFrames);
      memcpy(crawlGaitLE, newLE,numFrames);      
      memcpy(crawlGaitLS, newLS,numFrames);
      memcpy(crawlGaitLN, newLN,numFrames);
      memcpy(crawlGaitRN, newRN,numFrames);
      
      break;
    }
      case 'c': {
     //turn left
    
    crawlGaitRS[] = {250,250,250,250};
    crawlGaitRE[] = {250,250,250,250};
    crawlGaitLE[] = {250,250,250,250};
    crawlGaitLS[] = {250,250,250,250};
    crawlGaitLN[] = {250,250,250,250};
    crawlGaitRN[] = {250,250,250,250};
    
    break;
   }

    }
  }
}

Main Loop

The only addition required in the main loop is a call to check for user input on each iteration of the loop.

  //get user input using the SerialReadCommand function we wrote
  SerialReadCommand();

Downloads

Remote Control: Bluetooth

ardblue.jpeg
bluetoothcommport.png

Buy a Device

There are all kinds of of ways in which one could add remote control, but the simplest to me is via a Bluetooth serial module. These Bluetooth-Serial modules allow you to connect you phone or computer to the device as though it is connected via a cable and send/receive serial commands from the micro-controller.

These JY-MCU modules are available cheaply from various Chinese stores, I got mine from the purveyors of the most Extreme Deals for about $7.50.

Update the Code

Choose your Serial Pins

You can use the module on the standard Ardunio pins SERIAL0 and SERIAL1, but then you have to disconnect it every time that you want to upload a new version of you firmware.

Using the Arduino Library Software Serial we are able to define a second serial port and use that instead.

First import the library

#include <SoftwareSerial.h>

Then, during the global variable declarations, we initialise an instance of the SoftwareSerial class and define which pins will be used. I chose digital pin 11 as Receive (Rx) and 10 as Transmit (Tx).

SoftwareSerial BTSerial(11, 10); // RX, TX

Modify Read Procedure

The only differences now to using the regular serial port is that during setup() we startup the software serial instance instead and when callign functions we refer to the SoftwareSerial instance that we created. Your device may be running at 9600 baud rate, which would be more than sufficient, but mine has been set to 115200 in the past, so I see no reason to change it. Check this if you are receiving nonsense characters.

BTSerial.begin(115200);

When checking for available data we would call:

BTSerial.available()

and when reading a character we'd call:

BTSerial.read()

Connect the Hardware

Wire the Blutooth Module to the Arduino

If you are using the same JY-MCU module as I am, then:

  • connect the Vcc to the 5V pin of the Arduino for power (therefore using the Arduino's onboard regulator)
  • connect GND to a ground pin on the Arduino
  • Connect Tx to Rx on Arduino (pin 11 in my case)
  • Connect Rx to Tx on Arduino (pin 10 in my case)

WARNING: 3.3V logic

The receive pin on the JY-MCU is rated as 3.3V logic. In my case I just used the 5V output from the Arduino and it worked without a hitch , but you may want to drop your Arduino's Tx output voltage with a pair of voltage divider resistors.

User Your Fancy New Wireless Link

Before you can talk to the Arduino from your computer over the air (assuming it has Bluetooth built in or you have installed a dongle) or you phone (assuming you have a Bluetooth terminal app that works or have written your own) you need to pair the devices.

This process varies with operating system, but in general:

  • Find the Bluetooth icon in the quick launch bar and click on it
  • Select the option to add a device
  • Choose you module from the list (it may show up as "linvor") and click connect
  • Enter the pairing code when requested (usually 1234 with these modules)

Once the devices are paired, look in your control panel's device manager (if on windows) and see what com port number the Bluetooth module has been assigned under the "Ports (Com & LPT)" section. Use a serial terminal, such as putty, to connect to this port as you would any wired serial link.

More Information

There is a great in depth Instructable on this module if you need more help

Downloads

Conclusions and Competitions

montage.jpg

Conclusions and Comments

I hope you have enjoyed my Instructable. I fully intend to do some more development on this robot, a project like this is never finished!

I would love to see your versions of it, hear your questions and listen to what you think about it all, so please leave a comment. I will try my best to help with any troubles you might have.

Competitions

This is the step that I would like you to collaborate with me on, you click the "vote" button and I don't unleash Zombots on the populace. Or, you know, you just vote for me because you think that I made a cool diverse instructable and hopefully taught you something along the way :-D

In particular I am excited about the RadioShack Microcontroller contest and the Form1+ 3D printer contest, because I cannot imagine anything cooler than having that ability to use 3D printing to bring more crazy robots to life, among other things.

It fits pretty well in Halloween Props as well as as seeming like something that Super Villain might make though, so... don't be shy.