Stand 04.07.2007 - Dr. Erhard Henkes

c't-Bot


Quelle: http://www.heise.de/ct/projekte/ct-bot/bilder/bot3.jpg

0. Lizenz

Der hier nur zu Demonstrationszwecken auszugsweise gezeigte Code ist frei und unterliegt folgender Lizenz:

http://www.gnu.org/licenses/gpl.html

/*

 * c't-Sim - Robotersimulator fuer den c't-Bot
 *
 * This program is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General
 * Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your
 * option) any later version.
 * This program is distributed in the hope that it will be
 * useful, but WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE. See the GNU General Public License for more details.
 * You should have received a copy of the GNU General Public
 * License along with this program; if not, write to the Free
 * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307, USA.
 *
 */

Den kompletten c't-Bot-Code, der ständig weiter entwickelt wird,  findet man z.B. hier.

1. Einleitung

Ein aktuelles Beispiel bietet der c't-Bot von www.heise.de. Damit kann man die Entwicklung der Programmierung einer solchen Maschine hervorragend studieren. Der c't-Bot wird in C programmiert.

http://www.heise.de/ct/06/02/130/

2. Simulation einrichten

Muss man hierfür löten können? Nein. Der Kern ist die Programmierung. Dafür reicht eine gute Simulation. Wer Lust hat, kann sofort einsteigen. Alles, was man hierfür benötigt findet man hier:

http://www.heise.de/ct/ftp/projekte/ct-bot/installationsanleitung.shtml

Anschließend hat man eine Java-basierte Simulation (c't-Sim) für die Testumgebung und eine C-basierte Simulation für den Roboter namens c't-Bot. Man hat hier insbesondere den Vorteil, dass man mehrere Roboter gleichzeitg austesten kann. Die Kommunikation läuft über TCP/IP.
Als Entwicklungsumgebung verwendet man Eclipse sowohl für Java als auch für C.

3. Bot und Sim in Aktion

Hier einige Bilder, die das Ganze veranschaulichen:

Zunächst wird die Java-Simulation gestartet. Es gibt Räume mit einigen Hinderhissen, hier ein Beispiel.



Abb.: Start des c't-Sim (noch ohne c't-Bots)

Nun startet man die c't-Bot.exe, hier sogar mehrfach. Die Verbindung zur Simulation erfolgt via TCP/IP (IP: 127.0.0.1, Port: 10001)



Abb.: c't-Bot wurde gestartet



Abb.: Die "Bots" bewegen sich munter und versuchen Hindernisse zu umgehen.

Im Kontrollfenster kann man die Daten der einzelnen "Bots" auswählen und in der Grafik die Bewegungen der einzelnen Roboter beobachten.

Nehmen wir das Display des c't-Bot. Folgende Anzeigen sind z.B. programmiert (Display und zugehöriger C-Sourcecode):



display_cursor(1,1);
display_printf("P=%03X %03X D=%03d %03d ",sensLDRL,sensLDRR,sensDistL,sensDistR);
       
display_cursor(2,1);
display_printf("B=%03X %03X L=%03X %03X ",sensBorderL,sensBorderR,sensLineL,sensLineR);
       
display_cursor(3,1);
display_printf("R=%2d %2d F=%d K=%d T=%d ",sensEncL % 10,sensEncR %10,sensError,sensDoor,sensTrans);
       
display_cursor(4,1);
display_printf("I=%04X M=%05d %05d",RC5_Code,sensMouseX,sensMouseY);





display_cursor(1,1);
display_printf("Zeit: %04d:%03d",time_s,time_ms);
       
display_cursor(2,1);
display_printf("TS=%+4d %+4d",target_speed_l,target_speed_r);
       
display_cursor(3,1);
display_printf("RC=%+4d %+4d",sensEncL,sensEncR);
       
display_cursor(4,1);
display_printf("Speed= %04d %04d",v_left,v_right);


Entscheidend für die Funktionsfähigkeit eines Roboters sind seine Sensoren und seine Aktoren.

Die Sensoren sind wie folgt im Code abgebildet:

volatile int16 sensLDRL=0;        /*!< Lichtsensor links */
volatile int16 sensLDRR=0;        /*!< Lichtsensor rechts */

volatile int16 sensDistL=1023;    /*!< Distanz linker  IR-Sensor in [mm], wenn korrekt umgerechnet wird */
volatile int16 sensDistR=1023;    /*!< Distanz rechter IR-Sensor in [mm], wenn korrekt umgerechnet wird */

volatile int16 sensBorderL=0;     /*!< Abgrundsensor links */
volatile int16 sensBorderR=0;     /*!< Abgrundsensor rechts */

volatile int16 sensLineL=0;       /*!< Lininensensor links */
volatile int16 sensLineR=0;       /*!< Lininensensor rechts */

volatile char sensTrans=0;        /*!< Sensor Ueberwachung Transportfach */

volatile char sensDoor=0;         /*!< Sensor Ueberwachung Klappe */

volatile char sensError=0;        /*!< Ueberwachung Motor oder Batteriefehler */

volatile char sensMouseDX;        /*!< Maussensor Delta X, positive Werte zeigen querab der Fahrtrichtung nach rechts */
volatile char sensMouseDY;        /*!< Maussensor Delta Y, positive Werte zeigen in Fahrtrichtung */

volatile int sensMouseX;          /*!< Mausposition X, positive Werte zeigen querab der Fahrtrichtung nach rechts */
volatile int sensMouseY;          /*!< Mausposition Y, positive Werte zeigen in Fahrtrichtung  */

volatile int16 sensEncL=0;        /*!< Encoder linker  Motor */
volatile int16 sensEncR=0;        /*!< Encoder rechter Motor */


Die beiden Motoren (Aktoren) werden z.B. wie folgt mittels High-Level-Routinen abgebildet:

volatile int16 speed_l = 0;    /* Geschwindigkeit linker Motor */
volatile int16 speed_r = 0;    /* Geschwindigkeit rechter Motor */
direction_t direction;         /* Drehrichtung der Motoren */

/* Direkter Zugriff auf den Motor
 * @param left    Geschwindigkeit fuer den linken Motor
 * @param right   Geschwindigkeit fuer den linken Motor
 * Geschwindigkeit liegt zwischen -255 und +255.
 * 0 bedeutet Stillstand, 255 volle Kraft voraus, -255 volle Kraft zurueck.
 * Sinnvoll ist die Verwendung der Konstanten: BOT_SPEED_XXX,
 * also z.B. motor_set(BOT_SPEED_LOW,-BOT_SPEED_LOW);
 * fuer eine langsame Drehung
*/

void motor_set(int16 left, int16 right)
{

    if (left == BOT_SPEED_IGNORE)   
        left    = BOT_SPEED_STOP;
       
    if (abs(left) > BOT_SPEED_MAX)          // Nicht schneller fahren als moeglich
        speed_l = BOT_SPEED_MAX;
    else if (left == 0)                     // Stop wird nicht veraendert
        speed_l = BOT_SPEED_STOP;
    else if (abs(left) < BOT_SPEED_SLOW)    // Nicht langsamer als die
        speed_l = BOT_SPEED_SLOW;           // Motoren koennen
    else                                    // Sonst den Wunsch uebernehmen
        speed_l = abs(left);

          /* ... hier folgt analoger Code für rechts ... */
   
   
    if (left < 0 )
    {

        speed_l = -speed_l;
        direction.left = DIRECTION_BACKWARD;
    }
    else  if (left > 0 )

        direction.left = DIRECTION_FORWARD;
   
   
/* ... hier folgt analoger Code für rechts ... */
           
    bot_motor(speed_l,speed_r);
}

/* Stellt die Servos   Sinnvolle Werte liegen zwischen 8 und 16
 * @param servo Nummer des Servos
 * @param servo Zielwert
  */

void servo_set(char servo, char pos)
{

    if (pos < SERVO_LEFT)
        pos = SERVO_LEFT;
    if (pos > SERVO_RIGHT)
        pos = SERVO_RIGHT;
       
    bot_servo(servo,pos);
}

/ * Initialisiere den Motorkrams */
void motor_init(void)
{

    speed_l = 0;
    speed_r = 0;
    motor_low_init();
}

4. Verhalten (behaviour) steuert den Bot

Das Verhalten des c't-Bot  wird über Verhaltensfunktionen gesteuert, die nach Prioritäten in eine Verhaltensliste eingesetzt werden. Die besten Hinweise erhält man direkt in bot-logik.h.

/*! Verwaltungsstruktur fuer die Verhaltensroutinen */
typedef struct _Behaviour_t
{

   void (*work) (struct _Behaviour_t *data);         /* Zeiger auf die Funktion, die das Verhalten bearbeitet */
   uint8 priority;                                   /* Prioritaet */
   struct _Behaviour_t *caller ;                     /* aufrufendes verhalten */
   uint8 active:1;                                   /* Ist das Verhalten aktiv */
   uint8 subResult:2;                                /* War das aufgerufene unterverhalten erfolgreich (==1)?*/
   struct _Behaviour_t *next;                        /* Naechster Eintrag in der Liste */
} Behaviour_t;

extern volatile int16 target_speed_l;                /* Sollgeschwindigkeit linker Motor */
extern volatile int16 target_speed_r;                /* Sollgeschwindigkeit rechter Motor */

Funktion
Kommentar
extern void bot_behave(void); Kuemmert sich intern um die Ausfuehrung der goto-Kommandos
 @see bot_goto()
extern void bot_behave_init(void); Initilaisert das ganze Verhalten
void activateBehaviour(void *function); Aktiviert eine Regel mit gegebener Funktion
 @param function Die Funktion, die das Verhalten realisiert.
void deactivateBehaviour(void *function); Deaktiviert eine Regel mit gegebener Funktion
@param function Die Funktion, die das Verhalten realisiert.
void bot_drive_square_behaviour(Behaviour_t *data); Beispiel fuer ein Verhalten, das einen Zustand besitzt
es greift auf andere Verhalten zurueck und setzt daher
selbst keine speedWishes
Laesst den Roboter ein Quadrat abfahren
@param *data der Verhaltensdatensatz
void bot_goto_behaviour(Behaviour_t *data); Kuemmert sich intern um die Ausfuehrung der goto-Kommandos,
@param *data der Verhaltensdatensatz
@see bot_goto()
void bot_goto(int16 left, int16 right, Behaviour_t * caller); Drehe die Raeder um die gegebene Zahl an Encoder-Schritten weiter
@param left Schritte links
@param right Schritte rechts
void bot_drive_distance(Behaviour_t* caller,int8 curve, int16 speed, int16 cm); Das Verhalten laesst den Bot eine vorher festgelegte Strecke fahren. Dabei legt die Geschwindigkeit fest, ob der Bot vorwaerts oder rueckwaerts fahren soll.
@param curve Gibt an, ob der Bot eine Kurve fahren soll. Werte von -127 (So scharf wie moeglich links) ueber 0 (gerade aus) bis 127 (so scharf wie moeglich rechts)
@param speed Gibt an, wie schnell der Bot fahren soll. Negative Werte lassen den Bot rueckwaerts fahren.
@param cm Gibt an, wie weit der Bot fahren soll. In cm :-) Die Strecke muss positiv sein, die Fahrtrichtung wird ueber speed geregelt.
void bot_turn(Behaviour_t* caller,int16 degrees); Dreht den Bot im mathematisch positiven Sinn.
@param degrees Grad, um die der Bot gedreht wird. Negative Zahlen drehen im (mathematisch negativen) Uhrzeigersinn.
void bot_olympic_behaviour(Behaviour_t *data); Das Verhalten setzt sich aus 3 Teilverhalten zusammen:
Nach Licht suchen, auf das Licht zufahren, im Licht Slalom fahren.
void bot_drive_distance_behaviour(Behaviour_t* data); Das Verhalten laesst den Bot eine vorher festgelegte Strecke fahren.
@see bot_drive_distance()
void bot_turn_behaviour(Behaviour_t* data); Das Verhalten laesst den Bot eine Punktdrehung durchfuehren.
@see bot_turn()
void bot_explore_behaviour(Behaviour_t *data); Das Verhalten laesst den Roboter den Raum durchsuchen.
Das Verhalten hat mehrere unterschiedlich Zustaende:
1. Zu einer Wand oder einem anderen Hindernis fahren.
2. Zu einer Seite drehen, bis der Bot parallel zur Wand ist.
Es macht vielleicht Sinn, den Maussensor auszulesen, um eine Drehung um einen bestimmten Winkel zu realisieren. Allerdings muesste dafuer auch der Winkel des Bots zur Wand bekannt sein.
3. Eine feste Strecke parallel zur Wand vorwaerts fahren.
Da bot_glance abwechselnd zu beiden Seiten schaut, ist es fuer die Aufgabe, einer Wand auf einer Seite des Bots zu folgen, nur bedingt gewachsen und muss evtl. erweitert werden.
4. Senkrecht zur Wand drehen.
Siehe 2.
5. Einen Bogen fahren, bis der Bot wieder auf ein Hindernis stoesst. Dann das Ganze von vorne beginnen, nur in die andere Richtung und mit einem  weiteren Bogen. So erforscht der Bot einigermassen systematisch den Raum.
 
Da das Verhalten jeweils nach 10ms neu aufgerufen wird, muss der Bot sich 'merken', in welchem Zustand er sich gerade befindet.
void bot_do_slalom_behaviour(Behaviour_t *data); Das Verhalten laesst den Bot einen Slalom fahren.
@see bot_do_slalom()


Die ideale Stelle zum Einstieg in die Programmierung ist die Funktion bot_behave_init
, in der die Liste der Verhalten mit Prioritäten gefüttert und aktiv bzw. inaktiv gesetzt wird. Dort kann man mittels der Funktion activateBehaviour bzw. deactivateBehaviour das für Experimente gewünschte Verhalten einschalten.

#define INACTIVE 0    /* Verhalten ist aus */
#define ACTIVE   1    /* Verhalten ist an  */

void bot_behave_init(void)
/* Initialisert das ganze Verhalten */
{
    // Verhalten zum Schutz des Bots, hohe Prioritaet, Aktiv
    insert_behaviour_to_list(&behaviour, new_behaviour(200, bot_avoid_border, ACTIVE));
    insert_behaviour_to_list(&behaviour, new_behaviour(100, bot_avoid_col,    ACTIVE));
   
    // Verhalten, um Hindernisse besser zu erkennen, relativ hohe Prioritaet, modifiziert nur
    insert_behaviour_to_list(&behaviour, new_behaviour( 60, bot_glance,        ACTIVE));

    // Demo-Verhalten, ganz einfach, inaktiv
    insert_behaviour_to_list(&behaviour, new_behaviour(200, bot_simple_behaviour,        INACTIVE));

    // Demo-Verhalten, etwas komplexer, inaktiv
    insert_behaviour_to_list(&behaviour, new_behaviour( 50, bot_drive_square_behaviour,  INACTIVE));

    // Demo-Verhalten fuer aufwaendiges System, inaktiv
    insert_behaviour_to_list(&behaviour, new_behaviour( 55, bot_olympic_behaviour,       INACTIVE));

    // Hilfsverhalten, die Befehle von Boten-Funktionen ausfuehren, erst inaktiv, werden von Boten aktiviert   
    insert_behaviour_to_list(&behaviour, new_behaviour( 41, bot_drive_distance_behaviour,INACTIVE));
    insert_behaviour_to_list(&behaviour, new_behaviour( 40, bot_turn_behaviour,          INACTIVE));
    insert_behaviour_to_list(&behaviour, new_behaviour( 30, bot_goto_behaviour,          INACTIVE));
    insert_behaviour_to_list(&behaviour, new_behaviour( 51, bot_explore_behaviour,       INACTIVE));
    insert_behaviour_to_list(&behaviour, new_behaviour( 50, bot_do_slalom_behaviour,     INACTIVE));

    // Grundverhalten, setzt aeltere FB-Befehle um, aktiv
    insert_behaviour_to_list(&behaviour, new_behaviour(  2, bot_base,                    ACTIVE));

    // activateBehaviour(bot_simple_behaviour);
    activateBehaviour(bot_olympic_behaviour);

    //...
    return;
}

bot_olympic_behaviour ist mit seinen Unterfunktionen recht komplex, daher hier als einfachstes Beispiel bot_simple-behaviour:

void bot_simple_behaviour(Behaviour_t *data)
{

    static uint8 state=0;
   
    switch (state)
    {

        case 0:
            bot_drive_distance(data,0,BOT_SPEED_MAX,14);
            state++;
            break;
        case 1:
            bot_turn(data,90);
            state=0;
            break;
        default:
            return_from_behaviour(data);
            break;
    }
}

Hier noch die Unterfunktion bot_drive_distance:

/* Das Verhalten laesst den Bot eine vorher festgelegte Strecke fahren. Dabei legt die Geschwindigkeit fest, ob der Bot vorwaerts oder rueckwaerts fahren soll.
 * @param curve Gibt an, ob der Bot eine Kurve fahren soll. Werte von -127 (So scharf wie moeglich links) ueber 0 (gerade aus) bis 127 (so scharf wie moeglich rechts)
 * @param speed Gibt an, wie schnell der Bot fahren soll. Negative Werte lassen den Bot rueckwaerts fahren.
 * @param cm Gibt an, wie weit der Bot fahren soll. In cm :-) Die Strecke muss positiv sein, die Fahrtrichtung wird ueber speed geregelt. */

void bot_drive_distance(Behaviour_t* caller,int8 curve, int16 speed, int16 cm)
{

    int16 marks_to_drive = cm * 10 * ENCODER_MARKS / WHEEL_PERIMETER;
    int16 *encoder;
    drive_distance_curve = curve;
    drive_distance_speed = speed;

    if (curve > 0)
    {

        // Es handelt sich um eine Rechtskurve, daher wird mit dem linken Encoder gerechnet
        encoder = (int16*)&sensEncL;
    }
    else
    {

        encoder = (int16*)&sensEncR;
    }
    if(speed < 0)
    {

        // Es soll rueckwaerts gefahren werden. Der Zielwert ist also kleiner als der aktuelle Encoder-Stand.
        drive_distance_target = *encoder - marks_to_drive;
    }
    else
    {

        drive_distance_target = *encoder + marks_to_drive;   
    }
    switch_to_behaviour(caller, bot_drive_distance_behaviour, NOOVERRIDE);
}

Als Beispiel für den Umgang mit der Steuerung der Verhalten die Gerüst-Funktion
activateBehaviour:

/* Aktiviert eine Regel mit gegebener Funktion  @param function Die Funktion, die das Verhalten realisiert. */
void activateBehaviour(void *function)
{

    Behaviour_t *job; // Zeiger auf ein Verhalten

    // Einmal durch die Liste gehen, bis wir den gewuenschten Eintrag haben
    for (job = behaviour; job; job = job->next)
    {

        if (job->work == function)
        {

            job->active = ACTIVE;
            break;
        }
    }
}

Wie man sieht, sind einige wichtige Grundfunktionen zum Schutz des Roboters sofort aktiv geschaltet, hier am Beispiel der einfachen und hoch priorisierten Funktion bot_avoid_border:

#define BORDER_DANGEROUS    0x360    /* Wert, ab dem wir sicher sind, dass es eine Kante ist */
#define BOT_SPEED_NORMAL    50       /* normale Fahrt */

/* Verhindert, dass der Bot in Graeben faellt  @param *data der Verhaltensdatensatz */
void bot_avoid_border(Behaviour_t *data)
{

    if (sensBorderL > BORDER_DANGEROUS)
        speedWishLeft =- BOT_SPEED_NORMAL;
   
    if (sensBorderR > BORDER_DANGEROUS)
        speedWishRight =- BOT_SPEED_NORMAL;
}

sensBorderL bzw. sensBorderR (s.o.)

volatile int16 sensBorderL=0;     /*!< Abgrundsensor links */

volatile int16 sensBorderR=0;     /*!< Abgrundsensor rechts */



Für erste eigene Versuche bietet sich die Funktion
bot_simple_behaviour
an. Hier ein noch übersichtliches Beispiel für einen eigenen Bewegungsablauf:
Der Bot fährt ohne Hindernisse 50 cm vorwärts, dann dreht er 100° nach links, fährt weitere 50 cm vorwärts, dreht 100° nach rechts und wieder von vorne.

void bot_simple_behaviour(Behaviour_t *data)
{
    static uint8 state = 0;
    static uint8 flag  = 0;
   
    switch (state)
    {
        case 0:
            bot_drive_distance(data, 0, BOT_SPEED_MAX, 50);
            if (flag == 0) state = 1;
            else state = 2;
            break;
        case 1:
            bot_turn(data ,100);
            flag  = 1;
            state = 0;
            break;
        case 2:
            bot_turn(data ,-100);
            flag  = 0;
            state = 0;
            break;
           
        default:
            return_from_behaviour(data);
            break;
    }
}

Realisieren Sie mit den zwei Grundfunktionen
bot_drive_distance und bot_turn eigene Bewegungsmuster und implementieren Sie diese mittels dieser einfachen Funktion bot_simple_behaviour. Verändern Sie hierbei versuchsweise auch die Parameter in bot_drive_distance(Behaviour_t* caller,int8 curve, int16 speed, int16 cm).

5. Das Programmgerüst des c't-Bots

Sie haben nun die für die Programmierung des Verhaltens - sprich der Reaktion der Aktoren auf Informationen durch die Sensoren wichtigen Funktionen und Daten gesehen, aber ein c-Programm startet mit der Hauptfunktion main(). Diese befindet sich in ct-Bot.c und sieht stark vereinfacht wie folgt aus:

/* Hauptprogramm des Bots. Diese Schleife kuemmert sich um seine Steuerung. */
int main (void)
{

      //...

    init(); // enthaelt u.a. bot_behave_init();     

   
//...

    /*! Hauptschleife des Bot */

    for(;;)
    {

          //...
           // Testprogramm, dass den Bot erst links, dann rechtsrum dreht
           #ifdef  TEST_AVAILABLE_MOTOR

            calls++;
            if (calls == 1)           motor_set( BOT_SPEED_SLOW, -BOT_SPEED_SLOW);
            else if (calls == 501)    motor_set(-BOT_SPEED_SLOW,  BOT_SPEED_SLOW);
            else if (calls== 1001)    motor_set( BOT_SPEED_STOP,  BOT_SPEED_STOP);
            else

        #endif
                     
//...
        #ifdef BEHAVIOUR_AVAILABLE
            bot_behave(); // <--- Diese Funktion sorgt fuer das Verhalten des c't-Bot
        #endif
       
//...   
        #ifdef MCU
            #ifdef BOT_2_PC_AVAILABLE
                static int16 lastTimeCom =0;
                bot_2_pc_inform();                // Den PC ueber Sensorern und Aktuatoren informieren
                if (timer_get_s() != lastTimeCom) // sollte genau 1x pro Sekunde zutreffen
               
{
  
                    lastTimeCom = timer_get_s();       

                    bot_2_pc_listen();            // Kommandos vom PC empfangen
                }

            #endif
        #endif
       
//...
    }

    /*! Ende Hauptschleife des Bot */

     //... 
}


In der Startfunktion main() finden sich somit die beiden für das Verhalten wichtigen Funktionen bot_behave_init() und bot_behave(). Die Init-Funktion wird nur einmal aufgerufen, während bot_behave() in einer Endlos-for-Schleife das Verhalten kontrolliert.

Diese beiden Funktionen finden wir bereits im zentralen Modul bot-logik.c:

// Kette:
// main() ---> init() ---> bot_behave_init ---> bot_...

void bot_behave_init(void) /* Initialisert das ganze Verhalten */ (komplett s.o.)
{
    // Verhalten zum Schutz des Bots, hohe Prioritaet, Aktiv
    insert_behaviour_to_list(&behaviour, new_behaviour(200, bot_avoid_border, ACTIVE));
    insert_behaviour_to_list(&behaviour, new_behaviour(100, bot_avoid_col, ACTIVE));
    // Verhalten, um Hindernisse besser zu erkennen, relativ hohe Prioritaet, modifiziert nur
    insert_behaviour_to_list(&behaviour, new_behaviour( 60, bot_glance, ACTIVE));
          //...
    // Grundverhalten, setzt aeltere FB-Befehle um, aktiv
    insert_behaviour_to_list(&behaviour, new_behaviour(  2, bot_base, ACTIVE));
    activateBehaviour(
bot_simple_behaviour); // hier waehlt man das gewuenschte Verhalten aus
    //...
    return;
}


/* Zentrale Verhaltens-Routine, wird regelmaessig aufgerufen.
 * Dies ist der richtige Platz fuer eigene Routinen, um den Bot zu steuern.
 */
void bot_behave(void)
{   
    Behaviour_t *job;          // Zeiger auf ein Verhalten
    float faktorLeft  = 1.0;   // Puffer fuer Modifkatoren
    float faktorRight = 1.0;   // Puffer fuer Modifkatoren
   
    #ifdef RC5_AVAILABLE
        rc5_control();         // Abfrage der IR-Fernbedienung
    #endif

    /* Solange noch Verhalten in der Liste sind. Wir werten die Jobs sortiert nach Prioritaet aus.
       Wichtige zuerst einsortieren!) */
    for (job = behaviour; job; job = job->next)
    {
        if (job->active)
        {
            /* WunschVariablen initialisieren */
            speedWishLeft   = BOT_SPEED_IGNORE;
            speedWishRight  = BOT_SPEED_IGNORE;
            faktorWishLeft  = 1.0;
            faktorWishRight = 1.0;
           
            job->work(job);    /* Verhalten ausfuehren */

            /* Modifikatoren sammeln  */
            faktorLeft  *= faktorWishLeft;
            faktorRight *= faktorWishRight;

            /* Geschwindigkeit aendern? */
            if ((speedWishLeft != BOT_SPEED_IGNORE) || (speedWishRight != BOT_SPEED_IGNORE))
            {
                if (speedWishLeft  != BOT_SPEED_IGNORE)  speedWishLeft  *= faktorLeft;
                if (speedWishRight != BOT_SPEED_IGNORE)  speedWishRight *= faktorRight;
                motor_set(speedWishLeft, speedWishRight);
                break; /* Wenn ein Verhalten Werte direkt setzen will, nicht weitermachen */
            }
        }
        /* Dieser Punkt wird nur erreicht, wenn keine Regel im System die Motoren beeinflusen will */
        if (job->next == NULL)  motor_set(BOT_SPEED_IGNORE, BOT_SPEED_IGNORE);
    }
}

Zentrale Datenstruktur für die Verhaltensfunktionen ist 
_Behaviour_t
in bot-logik.h :

typedef struct _Behaviour_t
{

   void (*work) (struct _Behaviour_t *data); /* Zeiger auf die Funktion, die das Verhalten bearbeitet */
   uint8 priority;                /* Prioritaet */
   struct _Behaviour_t *caller ;  /* aufrufendes Verhalten */
   uint8 active:1;                /* Ist das Verhalten aktiv */
   uint8 subResult:2;             /* War das aufgerufene unterverhalten erfolgreich (==1)? */
   struct _Behaviour_t *next;     /* Naechster Eintrag in der Liste */
} Behaviour_t;

Wem struct, einfach verkettete Liste, Zeiger auf Funktionen noch wenig sagt, dem seien folgende Links empfohlen:

Einfacher C-Kurs
struct
Verkettete Liste
Zeiger

Nun haben wir einen Überblick über die Funktionsweise des Gerüstes und die Steuerung und Programmierung der Verhaltensmuster. Damit kann man im Simulator kostenlos interessante eigene Experimente durchführen.

6. Bot realistisch

Wem die Simulation auf Grund fehlender Störeinflüsse nicht ausreicht oder wer unbedingt praktische Erlebnisse benötigt, kann sich einen Bausatz für den c't-Bot besorgen und den im Simulator getesten Code an den "echten Bot" übermitteln.

Das kann dann so aussehen: Personal Gallery of marvin):

siehe z.B.: http://www.ctbot.de/index.php?page=5&smartor_mode=album_showpage&full=&pic_id=64

7. Bot bauen

Man findet hier zahlreiche Hilfestellungen im Internet:

http://www.heise-medien.de/presseinfo/bilder/ct/06/ct022006.jpg
http://www.heise.de/kiosk/archiv/ct/06/04/208
http://www.heise.de/newsticker/meldung/68088
http://www.heise.de/ct/ftp/projekte/ct-bot/aufbau.shtml

http://www.ctbot.de/
http://wiki.ctbot.de/index.php/Hauptseite
http://www.ctbot.de/index.php?page=5&smartor_mode=album_cat&cat_id=5
http://www.ctbot.de/index.php?page=5&smartor_mode=album_showpage&pic_id=64

http://www.segor.de/L1Bausaetze/ct-robot.shtml

8. Das Herz des c't-Bot

Das Herzstück des c't-Bot ist ein Mikrocontroller ATmega32 (Atmel) der mit einem 16 MHz Quarz getaktet wird. Die CPU ist über 32 I/O-Leitungen (4*8) mit der Außenwelt (Aktoren, Sensoren) verbunden.