To build the robot we used the following parts:
![]() |
| Fig 1: Circuit board |
![]() |
| Fig 2: needle-nose-pliers |
![[parts]](fig1_1.gif)
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.
![[hinge]](fig2.gif)
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.
![[solder the 3 body parts]](fig3.gif)
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:
![[the flag]](http://www.linuxfocus.org/common/images/article205/fig4.gif)
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.
![[the bridge]](fig5.gif)
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.
![[music wire for legs]](http://www.linuxfocus.org/common/images/article205/fig6.gif)
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.
![[legs top view]](fig10.gif)
![[legs bottom viewl]](fig11.gif)
Fig 9, 10: legs on the robot
Now you must wire the nitinol wire to the 6 legs.
![[leg and nitinol]](fig7.gif)
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.
![[nitinol must not be loose]](fig8.gif)
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.
![[The gait]](fig9.gif)
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:
![[circuit]](circuit.gif)
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 final drivercircuit2]](drivercircuit2.gif)
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.
*
*/
#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 <[email protected]>
* and Guido Socher <[email protected]>
*
*/
#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 BD
Data 0 is the output of the driver circuit that connects to the parallel port at
pin 2 (D0).![[yes, it walks]](http://www.geocities.com/saturn_doods/robi-0005.gif)
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!
|
|
|
|
|
|