|
|
This article is available in: English Castellano Deutsch Portugues Russian Turkce Arabic |
by Katja and Guido Socher About the author: Katja is the German editor of LinuxFocus. She likes Tux, film & photography and the sea. Her homepage can be found here. Guido is a long time Linux fan and he likes Linux because it is designed by honest and open people. This is one of the reasons why we call it open source. His homepage is at linuxfocus.org/~guido. Content: |
Abstract:
In this article we build a small six-legged walking robot which we then
control with a Linux PC using the parallel port interface.
Other devices can
be controlled similarily with the parallel port.
Robots have always fascinated us and so we were both excited when we found a book about robots some time ago which already included the robot kit to build a small insectlike robot called Stiquito. Stiquito is a somewhat special robot because it doesn't have a motor but walks because the legs are wired with nitinol and this way it walks totally silently just like a real insect. But when we had built it we noticed that because there wasn't enough friction with the surface where it walked on, its actual movement was very very slow. Fortunately the book also included some descriptions of other robot designs which finally inspired us to build the robot you can read here about.
To build the robot we used the following parts:
Fig 1: Circuit board |
Fig 2: needle-nose-pliers |
For the body you first need three parts of the circuit board, the one with
6x6 holes and the two with 6x7 holes as well as 4cm of 2 mm diameter brass tube
together with 3.7 cm music wire.
Fig 4: backbone and powerbus
Cut the brass tube in
peaces of 8, 17.5 and 8mm as shown in the picture. You can do this by rolling it
back and forth under a sharp kitchen knife and then bending it. The tubes will
break where you made the notch with the knife. It is important that the tube in
the middle is slightly longer than the 6x6 holes circuit board. Cut off about
3.7cm of music wire. The final length must be around 3 mm longer than the 3
tubes together. Put the music wire through the three tubes
The tube in the
middle must be able to rotate while the other two are soldered to the music
wire.
Fig 5: solder boards to backbone
The tube in the
middle is now soldered to the 6x6 holes circuit board. Take care that it can
rotate. The other two tubes are soldered to the other two circuit boards.
Now
take the small, 2x7 holes, circuit board. It should stand up on edge from the
middle brass tube. The circuit board must be notched with a little file or with
the cutter. Solder it to the middle brass tube and the middle circuit board as
shown in the picture:
Fig 6: adding the small circuit board
Sand the 1mm brass tube and cut several 4mm long pieces of the tube. Roll the
tube under the kitchen knife and then bend it. You need 16 of those crimps but
make a few spare crimps.
As a lot of crimping is needed now you should
better test it with a little bit of nitinol before you start: Put the end of the
nitinol wire into the very thin brass tubes (1mm diameter outside) and then
squeeze the brass tube with the needle-nose-pliers. This is called crimping.
Take care to buy good needle-nose-pliers because the forces needed to squeeze
the brass tubes are very high. You can also slide the ends of the nitinol
through 600 grid sand paper to get good electrical connections.
Now we will wire the nitinol wire that is needed to move the legs up and
down.
Fig 7: "the bridge"
You span the nitinol wire so
as if you wanted to build a bridge. You start on one side. There you put the
nitinol wire through the last hole that is possible on the left and straight
side. You tie a knot in the nitinol wire (in order to insure a better
connection) and put a crimp ( a ca. 4mm brass tube) over it and crimp it tight
so that it is tight and the nitinol wire can be put through the second hole from
above on the left side and then through the last possible hole on the left and
straight side. On the bottom again a knot is tied in the nitinol wire and a
crimp is put over and crimped tight (see Fig 7). The nitinol wire must be tight
but not too much. If you tip with the finger on it then it should move 2-4 mm.
If it isn't tight enough or if it is too tight then the robot will not move
properly later on. Solder the crimps to the board.
Do the same on the second
side.
Before continuing try out if it works. Use a 1.5V AA mignon battery and
connect it to one of the nitinol wires. When the wire contracts the middle body
part must rotate by 10-20 degrees. Take care not to connect the battery longer
than 1 second. You damage the wire if you overheat it.
Fig 8: bend the wire
For the legs you cut three
10cm long parts off the music wire. Each is bend 1.5cm on both sides. Then they
are soldered to the three body parts of the robot. They should be parallel to
each other.
Fig 9, 10: legs on the robot
Now you must wire the
nitinol wire to the 6 legs.
Fig 11: add the actuators
Put nitinol wire from
above through a crimp and through a hole in the circuit board. The distance to
the music wire is 3 holes. Crimp it tight (see pictures above).
Then pull a
crimp over the music wire until you reach the knee bending. Put the nitinol wire
through it and crimp it tight. Now comes the most difficult part. Hold the robot
with a little vice and fix and bend the legs with tape or extra cooper wire. The
music wire acts as a counterforce to the nitinol. For this to work the nitinol
must not be loose at all. The music wire must be pulled by 1 circuit board hole
towards the nitinol and then the crimp must be soldered to the leg.
Fig 12: nitinol and music wire on the same
level
Make sure that the music wire and the nitinol are on the same
level. The legs must not move up or down when the nitinol contracts. The leg
must move backwards.
Do the same with the other five legs.
The legs
and the music wire with the brass tubes in the middle of the robot act as a
power bus and therefore there must be an electrical connection between all of
them. As however the middle body part has more freedom because it can rotate and
therefore has no good connection we improved this by taking 3 cm of the 0.1mm
varnished cooper wire and wraping it around the spare brass tube to get a little
coil. Take out the brass tube and then solder this coil in the middle to the
inner leg pair and to one of the outer leg pairs. The coil shape of the wire
ensures maximum flexibility.
When the robot is ready you can solder 0.5m long pieces (or longer if you want) of 0.1 mm varnished cooper wire to the crimps on the board and solder the body crimps themself to the circuit board. We need 9 wires, 6 for the legs, 2 for up/down and one for the common powerbus. You can solder the other ends of the wires to a small connector which you can then plug into a corresponding small socket on the driver circuit.
Our insect is designed to walk in a tripod gait. A tripoid gait means that 3
legs are on the ground (two on one and one on the other side) while the other 3
are up in the air. When the robot walks then the 3 legs on the ground move in
one direction while the legs in the air walk in the opposite direction.
Fig 13: The gait
This circuit board allows you to use your PC to control the actuators on the
robot and plugs into the parallel port.
When we developed our computer
program we first tested it with the LEDs and only pluged the robot control wires
into the socket on the board when it worked correctly and the LEDs showed a
correct working gait. You should do the same when you experiment with the
program.
The robot is quite hungry. You need to run between 200 to 250 mA of
current through the nitinol to contract it. The 3 cm long nitinol wires on the
legs have about 7 Ohms. Always start the software first before you connect the
power to the driver circuit because all data pins are at first set to off by the
software to prevent damaging the nitinol wire. As the bios of the computer sets
the parallel port data pins to random values some of them are maybe in the state
on and the nitinol can be damaged if you run the current for much longer than 1
second through it. The time for the nitinol to cool down should be 1.5 times the
time you heated it.
The circuit diagram:
Fig 14: circuit diagram
As you can see in the
diagram above we use an electronically stabilized power supply. This is to
ensure good and stable power and to protect the parallel port. As external power
supply you can connect any DC power supply between 6 and 24 V. The 7805 is a
standard voltage regulator. The only thing to pay attention to here is that the
2 capacitors (470uF and 0.1uF) are located very closely to the 7805 voltage
regulator because otherwise it could happen that the 7805 chip starts to
oscillate which could destroy the 7805.
The actual driver has to be build
8 times. One for each leg and 2 for twisting the robot (moving the legs up and
down). We use a small NPN Darlington transistor because our robot needs a lot of
current. The BC875 or BC618 can switch about 500mA. The 47K on the input ensures
that an open circuit (e.g the computer is not connected) is always equivalent to
"off". The voltage level on the parallel port is above 4V for "on" and below 1V
for the state "off". The transistor works only as a switch. The 15 Ohm resistors
limit the current and protect both the legs of the robot and the transistor. The
LEDs show the state (on or off).
Below you see pictures of the circuit.
The red LEDs (the ones which are parallel to the robots actuators) are difficult
to see as we used transparent red LEDs. We built the 15 Ohm resistors from
constantan wire coils but this was just because we had plenty of that wire. It
is cheaper to buy ready made 2W resistors.
The parallel port was designed to serve as an output port from a personal
computer and to attach to a printer. Some parallel ports allow both input and
output. Here we only use the port for output. In a later article we will connect
sensors to the robot and then also use the input lines. Although there are 25
pins for the parallel port, we only use nine. Eight of the lines are used as
data output lines and one line serves as the electrical ground.
The pinout
for the parallel port is as follows:
25 PIN D-SUB FEMALE at the PC. Pin Name Dir Description 1 STROBE [-->] Strobe 2 D0 [-->] Data Bit 0 3 D1 [-->] Data Bit 1 4 D2 [-->] Data Bit 2 5 D3 [-->] Data Bit 3 6 D4 [-->] Data Bit 4 7 D5 [-->] Data Bit 5 8 D6 [-->] Data Bit 6 9 D7 [-->] Data Bit 7 10 ACK [<--] Acknowledge 11 BUSY [<--] Busy 12 PE [<--] Paper End 13 SEL [<--] Select 14 AUTOFD [-->] Autofeed 15 ERROR [<--] Error 16 INIT [-->] Initialize 17 SELIN [-->] Select In 18 GND [---] Signal Ground 19 GND [---] Signal Ground 20 GND [---] Signal Ground 21 GND [---] Signal Ground 22 GND [---] Signal Ground 23 GND [---] Signal Ground 24 GND [---] Signal Ground 25 GND [---] Signal GroundYou connect the driver circuit to pin 18 (GND) and to the data pins (2-9).
==== pprobi.c ===== /* vim: set sw=8 ts=8 si : */ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License. * See http://www.gnu.org/copyleft/ for details. * * Written by Katja Socher <katja@linuxfocus.org> * and Guido Socher <guido@linuxfocus.org> * */ #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <stdarg.h> #include <string.h> #include <math.h> #include <signal.h> #include "robi.h" /* ----------- */ static int opt_r=0; static int fd=0; /* ----------- */ /* ----------- */ void help() { printf("pprobi -- control software for a walking robot\n\ USAGE: pprobi [-h] [parport-device]\n\ \n\ OPTIONS:\n\ -h this help\n\ -r reset the parallel port data pins (all zero) and exit\n\ \n\ The default device is /dev/parport0 \n\ "); #ifdef VERINFO puts(VERINFO); #endif exit(0); } /* Signal handler: all off then exit */ void offandexit(int code) { robi_setdata(fd,0); set_terminal(0); exit(0); } /* ----------- */ int main(int argc, char **argv) { int state,bpat,alternate; char *dev; /* The following things are used for getopt: */ int ch; extern char *optarg; extern int optind; extern int opterr; opterr = 0; while ((ch = (char)getopt(argc, argv, "hr")) != -1) { switch (ch) { case 'h': help(); /*no break, help does not return */ case 'r': opt_r=1; break; case '?': fprintf(stderr, "serialtemp ERROR: No such option. -h for help.\n"); exit(1); /*no default action for case */ } } if (argc-optind < 1){ /* less than one argument */ dev="/dev/parport0"; }else{ /* the user has provided one argument */ dev=argv[optind]; } fd=robi_claim(dev); /* robi_claim has its own error checking */ /* catch signals INT and TERM and switch off all data lines before * terminating */ signal(SIGINT, offandexit); signal(SIGTERM, offandexit); /* initialize parpprt data lines to zero: */ robi_setdata(fd,0); set_terminal(1); /* set_terminal has its own error handling */ state=0; alternate=0; if (opt_r){ offandexit(1); } while(1){ ch=getchoice(); if (ch!=0) state=ch; if (ch == ' '){ printf("Stop\n"); robi_setdata(fd,0); usleep(500*1000); } if (ch == 'q'|| ch == 'x'){ printf("Quit\n"); break; } if (state=='l'){ /*right */ printf("walking right\n"); walkright(fd); } if (state=='h'){ /*left */ printf("walking left\n"); walkleft(fd); } if (state=='j'){ printf("walking back\n"); walkback(fd); } if (state=='k'){ if (alternate){ printf("walking straight on a\n"); walkstraight_a(fd); }else{ printf("walking straight on b\n"); walkstraight_b(fd); } alternate=(alternate +1) %2; } } /* we get here if q was typed */ set_terminal(0); return (0); } ==== robi.c ===== /* vim: set sw=8 ts=8 si : */ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License. * See http://www.gnu.org/copyleft/ for details. * * Written by Katja Socher <katja@linuxfocus.org> * and Guido Socher <guido@linuxfocus.org> * */ #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <sys/types.h> #include <sys/time.h> #include <fcntl.h> #include <unistd.h> #include <signal.h> #include <linux/ppdev.h> #include <sys/ioctl.h> #include <termios.h> #include "robi.h" /* like printf but exit the program */ static int die(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); exit(1); } /* get one character from stdin * Returns non zero if char was read otherwise zero * The arrow keys are mapped as follows: * <- = h * -> = l * v = j * ^ = k */ int getchoice() { int c; char s[20]; if (fgets(s,20,stdin)){ c=s[0]; switch (c){ case 0x1b: /* ESC */ if (s[1] == 0x5b){ /* arrow keys are pressed */ switch (s[2]){ case 0x41: /*up arrow*/ c='k'; break; case 0x42: /*down arrow*/ c='j'; break; case 0x44: /*l arrow*/ c='h'; break; case 0x43: /*r arrow*/ c='l'; break; default: c=0; } }else{ c=0; } break; case ' ': case 'h': case 'j': case 'k': case 'l': case 'q': case 'x': break; default: c=0; } return(c); } return(0); } /* Set the Terminal to Non Canonical mode with echo off * or reset the terminal. * USAGE: set_terminal(1) for canonical */ int set_terminal(int canonical) { static struct termios originalsettings; struct termios newsettings; static int origok=0; /* set if originalsettings valid */ if (canonical){ /* save original settings and set canonical mode*/ tcgetattr(fileno(stdin),&originalsettings); newsettings=originalsettings; newsettings.c_lflag &= ~ICANON; newsettings.c_lflag &= ~ECHO; newsettings.c_cc[VMIN]=0; /* do not block */ newsettings.c_cc[VTIME]=1; /* 100 ms */ if (tcsetattr(fileno(stdin),TCSANOW,&newsettings) !=0){ die("ERROR: could not set terminal attributes on stdin\n"); } origok=1; }else{ if (origok){ /* restore settings */ tcsetattr(fileno(stdin),TCSANOW,&originalsettings); } } return(0); } /* open /dev/parportX device and claim it. * USAGE: fd=robi_claim("/dev/parport0"); * The return value is a file descriptor used by other * functions such as robi_setdata */ int robi_claim(char *dev) { int fd,i; fd = open(dev, O_RDWR ); if (fd < 0) { die("ERROR: cannot open device %s\n",dev); } i=0; /* we need exclusive rights as we do not set the control lines*/ /*ioctl(fd, PPEXCL, &i)&& die("ERROR: request for exclusive rights failed\n");*/ ioctl(fd, PPCLAIM, &i)&&die("ERROR: could not claim parport\n"); return(fd); } /* Walk left */ int walkleft(int fd) { /* first B legs to ground */ robi_setdata(fd,LEGBD); usleep(400 *1000); /* all A legs 1 step */ robi_setdata(fd, LEGB1 | LEGB3 ); usleep(1100 *1000); /* first A legs to ground, cool B*/ robi_setdata(fd,LEGAD); usleep(400 *1000); robi_setdata(fd,0); usleep(1000 *1000); return(0); } /* Walk right */ int walkright(int fd) { /* first A legs to ground */ robi_setdata(fd,LEGAD); usleep(500 *1000); robi_setdata(fd, LEGA3 | LEGAD); usleep(300 *1000); /* all A legs 1 step */ robi_setdata(fd, LEGA1 | LEGA3 ); usleep(1100 *1000); /* first B legs to ground, cool A*/ robi_setdata(fd,LEGBD); usleep(400 *1000); robi_setdata(fd,0); usleep(1000 *1000); return(0); } /* Walk with all 3 legs 1 step forward */ int walkstraight_a(int fd) { /* first A legs to ground */ robi_setdata(fd,LEGAD); usleep(800 *1000); /* all A legs 1 step */ robi_setdata(fd, LEGA1 | LEGA2 | LEGA3 ); usleep(1000 *1000); /* first B legs to ground, cool A*/ robi_setdata(fd,LEGBD); usleep(500 *1000); robi_setdata(fd,0); usleep(1200 *1000); return(0); } /* Walk with all 3 legs 1 step forward */ int walkstraight_b(int fd) { /* first B legs to ground */ robi_setdata(fd,LEGBD); usleep(400 *1000); /* all B legs 1 step */ robi_setdata(fd,LEGB1 | LEGB2 | LEGB3); usleep(1000 *1000); /* A down and cool */ robi_setdata(fd,LEGAD); usleep(800 *1000); robi_setdata(fd,0); usleep(1200 *1000); return(0); } /* Walk with all 6 legs 1 step back */ int walkback(int fd) { /* first A legs to ground */ robi_setdata(fd,LEGAD); usleep(800 *1000); /* all B legs 1 step in the air*/ robi_setdata(fd, LEGB1 | LEGB2 | LEGB3 ); usleep(500 *1000); /* first B legs to ground, cool A*/ robi_setdata(fd,LEGBD); usleep(500 *1000); /* all A legs 1 step in the air*/ robi_setdata(fd,LEGA1 | LEGA2 | LEGA3); usleep(500 *1000); /* A down and cool */ robi_setdata(fd,LEGAD); usleep(800 *1000); robi_setdata(fd,0); usleep(1000 *1000); return(0); } /*---------*/ /* Write a bit pattern to the data lines * USAGE: rc=robi_setdata(fd,bitpat); * The return value is 0 on success. */ int robi_setdata(int fd,unsigned char bitpat) { int rc; rc=ioctl(fd, PPWDATA, &bitpat); return(rc); } ==== robi.h ===== /* vim: set sw=8 ts=8 si et: */ #ifndef H_ROBI #define H_ROBI 1 #define VERINFO "version 0.2" /* the first thing you need to do: */ extern int robi_claim(char *dev); /* write a bit pattern to the data lines of the parallel port: */ extern int robi_setdata(int fd,unsigned char bitpat); /* input and terminal functions */ extern int set_terminal(int canonical); extern int getchoice(); extern int walkstraight_a(int fd); extern int walkstraight_b(int fd); extern int walkback(int fd); extern int walkleft(int fd); extern int walkright(int fd); /* data pins to legs: * A1------=------B1 * = * = * B2------=------A2 * = * = * A3------=------B3 * * * Pin to set A-legs to ground= AD * Pin to set B-legs to ground= BD * * parallel port leg name * ------------------------- * data 0 A1 * data 1 A2 * data 2 A3 * data 3 AD * data 4 B1 * data 5 B2 * data 6 B3 * data 7 BD */ #define LEGA1 1 #define LEGA2 2 #define LEGA3 4 #define LEGAD 8 #define LEGB1 16 #define LEGB2 32 #define LEGB3 64 #define LEGBD 128 #endif
The software uses the ppdev programming interface from the 2.4.x Kernel (You need a 2.3.x or 2.4.x Kernel. It will not work with older kernels). This is a clean and convenient interface for writing user space parallel port device drivers. In older kernels we would have had to write a kernel module or use a rather ugly method which would only allow the user root to run the program. The ppdev interface uses the device file /dev/parport0 and by adjusting the owner and permissions of that file you can control who is allowed to use this parallel port interface.
To compile the ppdev as a module into your kernel you need to compile the PARPORT module together with the PPDEV device. This should then look as follows in the .config file:
# # Parallel port support # CONFIG_PARPORT=m CONFIG_PARPORT_PC=m CONFIG_PARPORT_PC_FIFO=y # CONFIG_PARPORT_PC_SUPERIO is not set # CONFIG_PARPORT_AMIGA is not set # CONFIG_PARPORT_MFC3 is not set # CONFIG_PARPORT_ATARI is not set # CONFIG_PARPORT_SUNBPP is not set CONFIG_PARPORT_OTHER=y CONFIG_PARPORT_1284=y # # Character devices # CONFIG_PPDEV=m #
The program first claims (initializes) the parallel port with the ioctl
command PPCLAIM. Then it sets the terminal to non canonical mode. This is to get
the input directly from the keyboard without the user always having to press
return after each input. Next it goes into a loop where it first checks if there
was any user input and then lets the robot walk according to the command. If you
don't do anything the program will just continue with the last command (e.g
continue to walk straight).
The command ioctl(fd, PPWDATA,
&bitpat); is used to set the data lines to a given bit pattern.
The pins from your robot need to be connected to the output lines of the driver circuit as follows:
Legs: A1------=------B1 = = B2------=------A2 = = A3------=------B3 Pin to set A-legs to ground= AD Pin to set B-legs to ground= BD Corresponding output lines of the driver circuit: data 0 A1 data 1 A2 data 2 A3 data 3 AD data 4 B1 data 5 B2 data 6 B3 data 7 BDData 0 is the output of the driver circuit that connects to the parallel port at pin 2 (D0).
We hope that you had a lot of fun building the robot. Just let us know about your robot, especially if yours is built with a different design!
|
Webpages maintained by
the LinuxFocus Editor team © Katja and Guido Socher, FDL LinuxFocus.org Click here to report a fault or send a comment to LinuxFocus |
2001-08-03, generated by lfparser version 2.17