Bauen eines Linux-gesteuerten, laufenden Roboters

ArticleCategory: [Es gibt verschiedene Artikel Kategorien]

Hardware

AuthorImage:[Ein Bild von Dir]

[Photo of the Authors]

TranslationInfo:[Autor und Übersetzer]

original in en Katja und Guido Socher 

en to de Katja Socher

AboutTheAuthor:[Eine kleine Biographie über den Autor]

Katja ist die deutsche Redakteurin von LinuxFocus. Sie mag Tux, Film & Fotografie und das Meer. Ihre Homepage findet sich hier.

Guido ist ein langjähriger Linuxfan und er mag Linux, weil es von ehrlichen und offenen Leuten entwickelt wurde. Dies ist einer der Gründe, warum wir es Open Source nennen. Seine Homepage ist auf linuxfocus.org/~guido.

Abstract:[Hier sollte eine kleine Zusammenfassung stehen]

In diesem Artikel bauen wir einen kleinen, sechsbeinigen, laufenden Roboter, den wir dann mit einem Linux PC über den Parallelport ansteuern. Andere Geräte können auf ähnliche Weise über den Parallelport gesteuert werden.

ArticleIllustration:[Das Titelbild des Artikels]

[Illustration]

ArticleBody:[Der eigentliche Artikel. Überschriften innerhalb des Artikels sollten h2 oder h3 sein.]

Einführung

Roboter haben uns schon immer fasziniert, so daß wir beide begeistert waren, als wir vor einiger Zeit ein Buch über Roboter entdeckten, das schon gleich den Roboterbausatz enthielt, um einen kleinen, insektenartigen Roboter namens Stiquito zu bauen. Stiquito ist ein etwas besonderer Roboter, da er keinen Motor hat, sondern dadurch läuft, daß seine Beine mit Nitinol verdrahtet sind. Auf diese Weise läuft er völlig leise, fast wie ein echtes Insekt. Aber als wir ihn gebaut hatten, stellten wir fest, daß seine tatsächliche Bewegung sehr sehr langsam ist, weil er nur sehr wenig Reibung mit der Oberfläche hat, auf der er läuft. Glücklicherweise enthielt das Buch noch einige weitere Beschreibungen von anderen Roboterdesigns, die uns schließlich dazu inspirierten, den Roboter, über den du hier lesen kannst, zu bauen.

Nitinol

Unser Roboter hat keinen Motor und läuft, weil seine Beine mit Nitinol angetrieben werden. Nitinol ist ein Formgedächtnisdraht aus einer Legierung aus Nickel und Titan, der sich wie Muskeln zusammenzieht, wenn er elektrisch erhitzt wird. Wenn ein elektrischer Strom hindurchgeschickt wird, wird der Draht erhitzt und verkürzt sich (er kehrt zu seiner "unausgedehnten Form" zurück). Eine Gegenkraft (Stahldraht in unserem Fall) ist dann erforderlich, um das Nitinol wieder in seine ursprüngliche Länge zurückkehren zu lassen. Wenn man den Draht nur um 3-5 Prozent ausdehnt, ist der Muskeldraht sehr beständig und verläßlich und kann Millionen von Zyklen laufen.


Bauen des Roboters

Um den Roboter zu bauen, haben wir die folgenden Teile benutzt:



[pliers]
Abb. 2: Spitzzange
Die meisten Teile solltest du in einem guten Hobbyladen bekommen können, wo Teile für kleine Flugzeuge, ferngesteuerte Autos, etc.... verkauft werden. Wenn du sie in deiner Heimatstadt nicht bekommen kannst, schau, wo sich die nächste Universität mit einer Architekturfakultät befindet. Da die Studenten normalerweise Modelle von Häusern und anderen Gebäuden bauen müssen, findest du dort wahrscheinlich einen Laden, wo man solche Dinge, wie das sehr dünne Messingröhrchen kaufen kann.
Wenn du die Spitzzange kaufst, stell sicher, daß die Backen flach sind, andernfalls kannst du damit den Nitinoldraht nicht crimpen.


[parts]
Abb. 3: Die Hauptteile für den Roboter

Bauen des Körpers

Für den Körper braucht man zuerst drei der Platinenteile, die mit 6x6 Löchern und die zwei mit 6x7 Löchern sowie das 4cm lange Messingröhrchen mit 2 mm Durchmesser zusammen mit 3.7 cm Stahldraht.

[hinge]
Abb. 4: Rückgrat und Powerbus

Schneide das Messingröhrchen in Stücke von 8, 17.5 und 8mm wie im Bild gezeigt. Man kann das durch Hin- und Herrollen des Röhrchens unter dem scharfen Küchenmesser und durch Biegen erreichen. Die Röhrchen brechen dort, wo du die Kerben mit dem Messer gemacht hast. Es ist wichtig, daß das mittlere Röhrchen etwas länger ist als die 6x6 Löcher Platine. Schneide ca. 3.7cm Stahldraht ab. Die endgültige Länge muß ca. 3mm länger sein als die drei Röhrchen zusammen. Stecke den Stahldraht durch die drei Röhrchen.
Das Röhrchen in der Mitte muß in der Lage sein, zu rotieren, während die anderen beiden an den Stahldraht angelötet werden.

[solder the 3 body parts]
Abb. 5: Löte die Platinen an das Rückgrat

Das mittlere Röhrchen wird jetzt an die 6x6 Löcher Platine gelötet. Paß auf, daß sie rotieren können. Die anderen beiden Röhrchen werden an die anderen beiden Platinen gelötet.
Jetzt nimm die vierte, die kleine 2x7 Löcher Platine. Sie soll hochkant auf dem mittleren Messingröhrchen stehen. Die Platine muß mit einer kleinen Feile oder dem Seitenschneider eingekerbt werden. Löte sie, wie im Bild gezeigt, an das mittlere Messingröhrchen und die mittlere Platine an:
[the flag]
Abb. 6: Hinzufügen der kleinen Platine

Schmiergel das Messingröhrchen mit 1mm Durchmesser und schneide einige 4mm lange Stücke aus dem Röhrchen. Rolle das Rohr unter dem Küchenmesser und bieg es dann. Du brauchst 16 von diesen Crimps, aber mach lieber ein paar mehr.

Da jetzt eine ganze Menge Crimpen gebraucht wird, probierst du es besser mit einem kleinen Stück Nitinol aus, bevor du richtig loslegst: Stecke das Ende des Nitinoldrahtes in das sehr dünne Messingröhrchen (1mm Außendurchmesser) und drücke dann das Messingröhrchen fest mit der Spitzzange. Dies nennt man Crimpen. Paß auf, daß du eine gute Spitzzange verwendest, da die Kraft zum Zusammendrücken der Messingröhrchen sehr hoch ist. Du kannst die Enden des Nitinols auch durch feines 600körniges Schmiergelpapier gleiten lassen, um gute elektrische Verbindungen zu bekommen.

Jetzt verdrahten wir das Nitinol, das benötigt wird, um die Beine hoch und runter zu bewegen.

[the bridge]
Abb. 7: "die Brücke"

Man spannt den Nitinoldraht so, als wollte man eine Brücke bauen. Man fängt auf einer Seite an. Dort zieht man den Nitinoldraht durch das letzte Loch, das auf der linken und geraden Seite möglich ist. Man macht einen Knoten in das Nitinol (um eine bessere Verbindung sicherzustellen) und setzt eine Crimp (ein ca. 4mm langes Messingröhrchen) darüber und crimpt es fest, so daß es fest sitzt und der Nitinoldraht durch das zweite Loch von oben auf der linken Seite und dann durch das letzte mögliche Loch auf der linken und geraden Seite gezogen werden kann (sieh Abb. 7). Der Nitinoldraht muß fest sein, aber nicht zu fest. Wenn du mit dem Finger das Nitinol berührst, sollte es sich 2-4mm bewegen. Wenn es nicht fest genug ist oder zu fest sitzt, wird sich der Roboter später nicht ordentlich bewegen. Löte die Crimpen an die Platine.
Mach dasselbe auf der rechten Seite.
Bevor du weitermachst, probier aus, ob es funktioniert. Benutze eine 1.5 V Batterie und verbinde sie mit dem Nitinoldraht. Wenn der Draht sich zusammenzieht, muß der mittlere Körpterteil um 10-20 Grad rotieren. Paß auf, daß du die Batterie nicht länger als eine Sekunde anschließt. Du beschädigst sonst den Draht, wenn du ihn überhitzt.

Die Beine


[music wire for legs]
Abb. 8: Biegen des Drahts

Für die Beine schneidest du drei 10cm lange Teile aus dem Stahldraht. Jedes Teil wird auf beiden Seiten um 1.5cm gebogen. Dann werden sie an die drei Körperteile des Roboters gelötet. Sie sollten parallel zueinander sein.

[legs top view]

[legs bottom viewl]
Abb. 9, 10: die Roboterbeine

Jetzt mußt du das Nitinol mit den sechs Beinen verdrahten.

[leg and nitinol]
Abb. 11: Hinzufügen des Antriebs

Ziehe Nitinol von oben durch eine Crimpe und durch ein Loch in der Platine. Der Abstand zum Stahldraht beträgt drei Löcher. Crimp es fest (sieh die Bilder oben).
Dann zieh eine Crimp über den Stahldraht bis du das Kniegelenk erreichst. Zieh den Nitinoldraht hindurch und crimp es fest. Jetzt kommt der schwierigste Teil. Halte den Roboter mit einem kleinen Schraubstock, fixiere ihn und spann die Beine mit Klebeband oder extra Kupferdraht an, um spannungsfrei löten zu können. Der Stahldraht wirkt als Gegenkraft zum Nitinol. Damit dies funktioniert, darf das Nitniol überhaupt nicht lose sein. Der Stahldraht muß ein Platinenloch zum Nitinol gezogen werden und dann muß die Crimp an das Bein festgelötet werden.
[nitinol must not be loose]
Abb. 12: Nitinol und Stahldraht auf derselben Ebene


Stell sicher, daß der Stahldraht und das Nitinol auf derselben Ebene sind. Die Beine dürfen sich nicht nach oben oder unten bewegen, wenn sich das Nitinol zusammenzieht, sondern sie müssen sich nach hinten bewegen.

Mach dasselbe mit den anderen fünf Beinen.
Die Beine und der Stahldraht mit den Messingröhrchen in der Mitte des Roboters fungieren als Powerbus und deshalb muß eine elektrische Verbindung zwischen allen von ihnen bestehen. Da jedoch der mittlere Körperteil mehr Freiheit hat, da er rotieren kann und daher keine gute Verbindung besteht, haben wir dies dadurch verbessert, daß wir 3cm des 0.1mm lackierten Kupferdrahtes genommen haben und ihn um ein übriggebliebenes Messingröhrchen gewickelt haben, um eine kleine Spule zu bekommen. Nimm das Messingröhrchen heraus und löte die Spule so, daß die inneren Beinpaare mit den äußeren Beinpaare verbunden sind. Die Wicklungsform des Drahtes stellt die maximale Flexibilität sicher.

Wenn der Roboter fertig ist, kannst du 0.5m lange Stücke (oder länger, wenn du willst) aus 0.1mm lackiertem Kupferdraht an die Crimps auf der Platine löten. Löte dann die Körpercrimps selbst an die Platine. Wir brauchen neun Drähte, sechs für die Beine, zwei für hoch/runter und eins für den allgemeinen Powerbus. Du kannst die anderen Enden des Drahtes an einen kleinen Stecker löten, den du dann in die entsprechende kleine Buchse der Treiberschaltung stecken kannst.

Der Gang

Unser Insekt wurde für einen Dreifußgang (tripod) entwickelt. Ein Dreifußgang meint, daß drei Beine am Boden sind (zwei Füße auf der einen und ein Fuß auf der anderen Seite), während die anderen drei in der Luft sind. Wenn der Roboter geht, dann bewegen sich die drei Beine am Boden in die eine Richtung, während sich die Beine in der Luft in die entgegengesetzte Richtung bewegen.
[The gait]
Abb. 13: Der Gang



Steuern des Roboters mit unserem Linuxcomputer

Die Treiberschaltung

Diese Platine erlaubt es uns, unseren PC zum Steuern des Antriebs unseres Roboters zu benutzen und wird in den Parallelport gesteckt.
Als wir unser Computerprogramm entwickelt haben, haben wir es zuerst mit LEDs getestet und die Robotersteuerungsdrähte erst in den Sockel der Platine gesteckt, als es korrekt funktioniert hat und die LEDs einen korrekten Laufgang anzeigten. Du solltest dasselbe tun, wenn du mit dem Programm experimentierst.
Der Roboter ist recht hungrig. Du mußt zwischen 200 und 250 mA Strom durch den Nitinoldraht schicken, damit er sich zusammenzieht. Die 3cm langen Nitinoldrähte an den Beinen haben ungefähr 7 Ohm. Starte immer zuerst die Software, bevor du den Strom an die Treiberschaltung anschließt, weil alle Datenpins von der Software zuerst auf "aus" gesetzt werden, um ein Beschädigen des Nitinoldrahtes zu verhindern. Das Bios des Computers setzt die Datenpins des Parallelports auf Zufallswerte. Einige davon könnten deshalb dann im Zustand "an" sein. Das Nitinol kann beschädigt werden, wenn du einen Strom viel länger als eine Sekunde hindurchschickst. Die Zeit für das Nitinol, um abzukühlen, sollte 1.5mal so lang sein, wie die Zeit, die du es erhitzt hast.

Das Schaltbild:
[circuit]
Abb. 14: Schaltbild

Wie du im obigen Schaltbild sehen kannst, benutzen wir eine elektronisch stabilisierte Stromquelle, um einen guten und sicheren Strom sicherzustellen und um den Parallelport zu schützen. Als externe Stromquelle kannst du jede DC Stromquelle zwischen 6 und 24V anschließen. Der 7805 ist ein Standardspannungsregler. Das einzige, was du hier beachten mußt, ist, daß die zwei Kondensatoren (470uF und 0.1uF) sich nahe zum 7805 Spannungsregler befinden, weil es ansonsten passieren könnte, daß der 7805 Chip anfängt, zu oszillieren, was den 7805 zerstören könnte.

Der tatsächliche Treiber muß achtmal gebaut werden. Eins für jedes Bein und zwei für das Drehen des Roboters (Hoch und Runter der Beine). Wir benutzen einen kleinen NPN Darlingtontransistor, weil unser Roboter eine Menge Strom verbraucht. Der BC875 oder BC618 können ungefähr 500mA schalten. Die 47K bei der Eingabe stellen sicher, daß ein offener Stromkreis (z.B. der Computer ist nicht angeschlossen) immer mit "aus" äquivalent ist. Das Spannungsniveau des Parallelports liegt über 4V für "an" und unter 1V für den Zustand "aus". Der Transistor arbeitet nur als Schalter. Die 15 Ohm Widerstände begrenzen den Strom und schützen sowohl die Beine des Roboters als auch den Transistor. Die LEDs zeigen den Zustand an (ein oder aus).

Unten siehst du Bilder des Schaltkreises. Die roten LEDs (die, die parallel zu den Roboterantrieben sind), sind schwierig zu sehen, weil wir transparente rote LEDs benutzt haben. Wir haben die 15 Ohm Widerstände aus Konstantandrahtwicklungen gebaut, aber dies war nur der Fall, weil wir sehr viel von diesem Draht zur Verfügung hatten. Es ist billiger, fertige 2W Widerstände zu kaufen.


[the final drivercircuit1] [the final drivercircuit2]
Abb 15: der Schaltkreis



Der Parallelport

Der Parallelport wurde entwickelt, um als Ausgangsport von einem Computer zu dienen und an einen Drucker angeschlossen zu werden. Einige Parallelports erlauben sowohl Ein- als auch Ausgang. Hier benutzen wir nur den Ausgangsport. In einem späteren Artikel werden wir Sensoren an unseren Roboter anschließen und dann auch den Eingangsport benutzen. Obwohl der Parallelport 25 Pins hat, benutzen wir nur neun. Acht werden als Datenausgangsverbindungen benutzt und einer dient als Erdung.
Die Pinbelegung für den Parallelport sieht wie folgt aus:

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 Ground
Du verbindest die Treiberschaltung mit Pin 18 (GND) und mit den Datenpins (2-9).

Der Parallelport arbeitet normalerweise so, daß ein Byte zu den Datenverbindungen geschrieben wird und dann der Computer die Strobeverbindung auf 1 setzt, um dem Drucker anzuzeigen, daß die Daten jetzt gültig sind. Wir benutzen die Datenverbindung direkt, da wir so keine Extralogik für die Strobeverbindung brauchen.

Die Software

Du kannst die Software > hier < herunterladen.
Extrahiere sie mit dem Befehl tar zxvf pprobi*.tar.gz. Die Installationsanweisungen sind enthalten.

Das Programm ist in C geschriebn. Mit den Pfeiltasten und der Leertaste kann man den Roboter steuern und ihn geradeaus, nach rechts, links oder rückwärts gehen lassen. Benutz die Leerzeichentaste, um den Roboter anzuhalten und q (oder x), um das Programm zu beenden. Anstelle der Pfeiltasten kannst du auch die Tasten h,j,k,l (vi key mapping) benutzen, wenn du möchtest. Die Werte, die wir für die Beinbewegung benutzt haben, wurden für unseren Roboter optimiert. Jeder Roboter ist ein bißchen anders, hauptsächlich, weil es schwierig ist, an allen Nitinoldrähten die gleiche mechanische Spannung zu bekommen. Dein Roboter wird mit der Software so wie sie ist, funktionieren, aber nicht alle Beine werden sich gleich gut bewegen und du mußt ein bißchen experimentieren, bis du die Werte gefunden hast, die am besten zu deinem Roboter passen. Paß dabei nur auf, daß keines der Beine jemals überhitzt wird oder nicht genügend Zeit zum Abkühlen erhält.

==== 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


Die Software benutzt die ppdev Programmierschnittstelle des 2.4.x Kernels (du brauchst einen 2.3.x oder 2.4.x Kernel. Es funktioniert nicht mit einem älteren Kernel). Dies ist eine saubere und angenehme Schnittstelle zum Schreiben von user space parallel port Gerätetreibern. In älteren Kerneln hätten wir ein Kernelmodul schreiben oder eine häßliche Methode benutzen müssen, die es nur dem Benutzer root erlaubt hätte, das Programm laufen zu lassen. Die ppdev Schnittstelle benutzt die Gerätedatei und durch Anpassen des Eigentümers und der Rechte der Datei kannst du festlegen, wem es erlaubt ist, diese Parallelportschnittstellle zu benutzen.

Um das ppdev als ein Modul in deinen Kernel zu kompilieren, mußt du das PARPORT Modul zusammen mit dem PPDEV device kompilieren. Dies sollte dann wie folgt in der .config Datei aussehen:

#
# 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
#


Das Programm beansprucht (initialisiert) zuerst den Parallelport mit dem ioctl Befehl PPCLAIM. Dann setzt es das Terminal in non canonical mode. Dies wird gemacht, um die Eingabe direkt von der Tastatur zu erhalten, ohne daß der Benutzer nach jeder Eingabe Return drücken muß. Als nächstes geht es in eine Schleife, wo zuerst überprüft wird, ob es irgendeine Benutzereingabe gab und dann der Roboter entsprechend dem Befehl laufen gelassen wird. Wenn du nichts machst, macht das Programm mit dem letzten Befehl weiter (z.B. fährt es fort, den Roboter geradeaus laufen zu lassen).
Der Befehl ioctl(fd, PPWDATA, &bitpat); wird benutzt, um die Datenverbindung auf ein gegebenes Bitmuster zu setzen.

Die Pins deines Roboters müssen mit den Ausgabeverbindungen der Treiberschaltung wie folgt verbunden sein:

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 ist die Ausgabe der Treiberschaltung, die mit dem Parallelport an Pin 2 (D0) verbunden wird.

Hier ist er, der laufende Roboter:
[yes, it walks]
Er bewegt sich in diesem animierten gif etwas zu schnell. In Wirklichkeit ist er wegen der Abkühlungsphasen, die er braucht, damit das Nitinol in seine Ausgangslänge zurückgeht, etwas langsamer.


Wir hoffen, daß du beim Bauen des Roboters viel Spaß hattest. Laß uns von deinem Roboter wissen, besonders dann, wenn deiner mit einem anderen Design gebaut wurde!

Referenzen