diff options
Diffstat (limited to 'source/ct-Bot/bot-logic')
-rw-r--r-- | source/ct-Bot/bot-logic/behaviour_avoid_border.c | 41 | ||||
-rw-r--r-- | source/ct-Bot/bot-logic/behaviour_avoid_col.c | 142 | ||||
-rw-r--r-- | source/ct-Bot/bot-logic/behaviour_catch_pillar.c | 106 | ||||
-rw-r--r-- | source/ct-Bot/bot-logic/behaviour_drive_distance.c | 120 | ||||
-rw-r--r-- | source/ct-Bot/bot-logic/behaviour_drive_square.c | 76 | ||||
-rw-r--r-- | source/ct-Bot/bot-logic/behaviour_follow_line.c | 176 | ||||
-rw-r--r-- | source/ct-Bot/bot-logic/behaviour_goto.c | 141 | ||||
-rw-r--r-- | source/ct-Bot/bot-logic/behaviour_gotoxy.c | 141 | ||||
-rw-r--r-- | source/ct-Bot/bot-logic/behaviour_olympic.c | 428 | ||||
-rw-r--r-- | source/ct-Bot/bot-logic/behaviour_remotecall.c | 407 | ||||
-rw-r--r-- | source/ct-Bot/bot-logic/behaviour_scan.c | 144 | ||||
-rw-r--r-- | source/ct-Bot/bot-logic/behaviour_servo.c | 68 | ||||
-rw-r--r-- | source/ct-Bot/bot-logic/behaviour_simple.c | 139 | ||||
-rw-r--r-- | source/ct-Bot/bot-logic/behaviour_solve_maze.c | 660 | ||||
-rw-r--r-- | source/ct-Bot/bot-logic/behaviour_turn.c | 361 | ||||
-rw-r--r-- | source/ct-Bot/bot-logic/bot-logik.c | 500 |
16 files changed, 3650 insertions, 0 deletions
diff --git a/source/ct-Bot/bot-logic/behaviour_avoid_border.c b/source/ct-Bot/bot-logic/behaviour_avoid_border.c new file mode 100644 index 0000000..9665b8d --- /dev/null +++ b/source/ct-Bot/bot-logic/behaviour_avoid_border.c @@ -0,0 +1,41 @@ +/* + * 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. + * + */ + +/*! @file behaviour_avoid_border.c + * @brief Vermeide Abgruende + * + * @author Benjamin Benz (bbe@heise.de) + * @date 03.11.06 +*/ + +#include "bot-logic/bot-logik.h" + +#ifdef BEHAVIOUR_AVOID_BORDER_AVAILABLE +/*! + * Verhindert, dass der Bot in Graeben faellt + * @param *data der Verhaltensdatensatz + */ +void bot_avoid_border_behaviour(Behaviour_t *data){ + if (sensBorderL > BORDER_DANGEROUS) + speedWishLeft=-BOT_SPEED_NORMAL; + + if (sensBorderR > BORDER_DANGEROUS) + speedWishRight=-BOT_SPEED_NORMAL; +} +#endif diff --git a/source/ct-Bot/bot-logic/behaviour_avoid_col.c b/source/ct-Bot/bot-logic/behaviour_avoid_col.c new file mode 100644 index 0000000..a5c889a --- /dev/null +++ b/source/ct-Bot/bot-logic/behaviour_avoid_col.c @@ -0,0 +1,142 @@ +/* + * 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. + * + */ + +/*! @file behaviour_avoid_col.c + * @brief Vermeide Kollisionen + * + * @author Benjamin Benz (bbe@heise.de) + * @date 03.11.06 +*/ + + +#include "bot-logic/bot-logik.h" + +#ifdef BEHAVIOUR_AVOID_COL_AVAILABLE + +#define ZONE_CLOSEST 0 /*!< Zone fuer extremen Nahbereich */ +#define ZONE_NEAR 1 /*!< Zone fuer Nahbereich */ +#define ZONE_FAR 2 /*!< Zone fuer Fernbereich */ +#define ZONE_CLEAR 3 /*!< Zone fuer Freien Bereich */ + +#define BRAKE_CLOSEST -1.0 /*!< Bremsfaktor fuer extremen Nahbereich ( <1 ==> bremsen <0 ==> rueckwaerts) */ +#define BRAKE_NEAR 0.6 /*!< Bremsfaktor fuer Nahbereich ( <1 ==> bremsen <0 ==> rueckwaerts) */ +#define BRAKE_FAR 0.8 /*!< Bremsfaktor fuer Fernbereich ( <1 ==> bremsen <0 ==> rueckwaerts) */ + +uint8 col_zone_l=ZONE_CLEAR; /*!< Kollisionszone, in der sich der linke Sensor befindet */ +uint8 col_zone_r=ZONE_CLEAR; /*!< Kollisionszone, in der sich der rechte Sensor befindet */ + + +/*! + * Passt auf, dass keine Kollision mit Hindernissen an der Front des Roboters + * geschieht. + * TODO: Diese Funktion ist nur ein Dummy-Beispiel, wie eine Kollisionsvermeidung aussehen + * koennte. Hier ist ein guter Einstiegspunkt fuer eigene Experimente und Algorithmen! + * @param *data der Verhaltensdatensatz + */ +void bot_avoid_col_behaviour(Behaviour_t *data){ + if (sensDistR < COL_CLOSEST) /* sehr nah */ + col_zone_r=ZONE_CLOSEST; /* dann auf jeden Fall CLOSEST Zone */ + else + /* sind wir naeher als NEAR und nicht in der inneren Zone gewesen */ + if ((sensDistR < COL_NEAR) && (col_zone_r > ZONE_CLOSEST)) + col_zone_r=ZONE_NEAR; /* dann auf in die NEAR-Zone */ + else + /* sind wir naeher als FAR und nicht in der NEAR-Zone gewesen */ + if ((sensDistR < COL_FAR) && (col_zone_r > ZONE_NEAR)) + col_zone_r=ZONE_FAR; /* dann auf in die FAR-Zone */ + else + /* wir waren in einer engeren Zone und verlassen sie in Richtung NEAR */ + if (sensDistR < (COL_NEAR * 0.50)) + col_zone_r=ZONE_NEAR; /* dann auf in die NEAR-Zone */ + else + if (sensDistR < (COL_FAR * 0.50)) + col_zone_r=ZONE_FAR; /* dann auf in die NEAR-Zone */ + else + col_zone_r=ZONE_CLEAR; /* dann auf in die CLEAR-Zone */ + + if (sensDistL < COL_CLOSEST) /* sehr nah */ + col_zone_l=ZONE_CLOSEST; /* dann auf jeden Fall CLOSEST-Zone */ + else + /* sind wir naeher als NEAR und nicht in der inneren Zone gewesen */ + if ((sensDistL < COL_NEAR) && (col_zone_l > ZONE_CLOSEST)) + col_zone_l=ZONE_NEAR; /* dann auf in die NEAR-Zone */ + else + /* sind wir naeher als FAR und nicht in der NEAR-Zone gewesen */ + if ((sensDistL < COL_FAR) && (col_zone_l > ZONE_NEAR)) + col_zone_l=ZONE_FAR; /* dann auf in die FAR-Zone */ + else + /* wir waren in einer engeren Zone und verlassen sie in Richtung NEAR */ + if (sensDistL < (COL_NEAR * 0.50)) + col_zone_l=ZONE_NEAR; /* dann auf in die NEAR-Zone */ + else + if (sensDistL < (COL_FAR * 0.50)) + col_zone_l=ZONE_FAR; /* dann auf in die NEAR-Zone */ + else + col_zone_l=ZONE_CLEAR; /* dann auf in die CLEAR-Zone */ + + // Nur reagieren, wenn der Bot vorwaerts faehrt + if ((speed_l > 0) && (speed_r >0)) { + // wenn auf beiden Seiten in der Kollisionszone + if ((col_zone_r == ZONE_CLOSEST)&&(col_zone_l == ZONE_CLOSEST)){ + // Drehe Dich zur Seite, wo mehr Platz ist + if (sensDistR < sensDistL) + bot_turn(data,20); + else + bot_turn(data,-20); + return; + } + + switch (col_zone_l){ + case ZONE_CLOSEST: + faktorWishRight = BRAKE_CLOSEST; + break; + case ZONE_NEAR: + faktorWishRight = BRAKE_NEAR; + break; + case ZONE_FAR: + faktorWishRight = BRAKE_FAR; + break; + case ZONE_CLEAR: + faktorWishRight = 1; + break; + default: + col_zone_l = ZONE_CLEAR; + break; + } + + switch (col_zone_r){ + case ZONE_CLOSEST: + faktorWishLeft = BRAKE_CLOSEST; + break; + case ZONE_NEAR: + faktorWishLeft = BRAKE_NEAR; + break; + case ZONE_FAR: + faktorWishLeft = BRAKE_FAR; + break; + case ZONE_CLEAR: + faktorWishLeft = 1; + break; + default: + col_zone_r = ZONE_CLEAR; + break; + } + } +} +#endif diff --git a/source/ct-Bot/bot-logic/behaviour_catch_pillar.c b/source/ct-Bot/bot-logic/behaviour_catch_pillar.c new file mode 100644 index 0000000..efc0a4e --- /dev/null +++ b/source/ct-Bot/bot-logic/behaviour_catch_pillar.c @@ -0,0 +1,106 @@ +/* + * 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. + * + */ + + +/*! @file behaviour_catch_pillar.c + * @brief sucht nach einer Dose und fängt sie ein + * + * @author Benjamin Benz (bbe@heise.de) + * @date 08.12.06 +*/ + + +#include "bot-logic/bot-logik.h" +#ifdef BEHAVIOUR_CATCH_PILLAR_AVAILABLE +#include <math.h> + +#define MAX_PILLAR_DISTANCE 350 + +#define START 0 +#define LEFT_FOUND 1 +#define TURN_MIDDLE 2 +#define GO 3 + +static uint8 pillar_state=START; +/*! + * Fange eine Dose ein + * @param *data der Verhaltensdatensatz + */ +void bot_catch_pillar_behaviour(Behaviour_t *data){ + static float angle; + + switch (pillar_state){ + case START: + if (sensDistL < MAX_PILLAR_DISTANCE){ // sieht der linke Sensor schon was? + angle=heading; + pillar_state=LEFT_FOUND; + } else + speedWishLeft=-BOT_SPEED_SLOW; + speedWishRight=+BOT_SPEED_SLOW; + //bot_turn(data,5); // Ein Stueck drehen + break; + case LEFT_FOUND: + if (sensDistR < MAX_PILLAR_DISTANCE){ // sieht der rechte Sensor schon was? + angle= heading- angle; + if (angle < 0) + angle+=360; + angle= heading - angle/2; + pillar_state=TURN_MIDDLE; +// bot_turn(data,-angle/2); + }else + speedWishLeft=-BOT_SPEED_SLOW; + speedWishRight=+BOT_SPEED_SLOW; +// bot_turn(data,5); // Eins Stueck drehen + break; + case TURN_MIDDLE: + if (fabs(heading - angle) > 2){ + speedWishLeft=+BOT_SPEED_SLOW; + speedWishRight=-BOT_SPEED_SLOW; + } else { + bot_servo(data,SERVO1,DOOR_OPEN); // Klappe auf + pillar_state=GO; + } + break; + case GO: + if (sensTrans ==0){ + speedWishLeft=+BOT_SPEED_SLOW; + speedWishRight=+BOT_SPEED_SLOW; + } else { + bot_servo(data,SERVO1,DOOR_CLOSE); + pillar_state++; + } + break; + + default: + pillar_state=START; + return_from_behaviour(data); + break; + } +} + +/*! + * Fange ein Objekt ein + * @param caller Der obligatorische Verhaltensdatensatz des Aufrufers + */ +void bot_catch_pillar(Behaviour_t * caller){ + pillar_state=0; + // Zielwerte speichern + switch_to_behaviour(caller,bot_catch_pillar_behaviour,OVERRIDE); +} +#endif diff --git a/source/ct-Bot/bot-logic/behaviour_drive_distance.c b/source/ct-Bot/bot-logic/behaviour_drive_distance.c new file mode 100644 index 0000000..bb29e66 --- /dev/null +++ b/source/ct-Bot/bot-logic/behaviour_drive_distance.c @@ -0,0 +1,120 @@ +/* + * 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. + * + */ + +/*! @file behaviour_drive_distance.c + * @brief Bot faehrt ein Stueck + * + * @author Benjamin Benz (bbe@heise.de) + * @date 03.11.06 +*/ + +#include "bot-logic/bot-logik.h" +#include "log.h" + +#ifdef BEHAVIOUR_DRIVE_DISTANCE_AVAILABLE + + +/* Parameter fuer das bot_drive_distance_behaviour() */ +int16 drive_distance_target; /*!< Zu fahrende Distanz bzw. angepeilter Stand der Radencoder sensEncL bzw. sensEncR */ +int8 drive_distance_curve; /*!< Kruemmung der zu fahrenden Strecke. */ +int16 drive_distance_speed; /*!< Angepeilte Geschwindigkeit. */ + + +/*! + * laesst den Bot in eine Richtung fahren. + * Es handelt sich hierbei nicht im eigentlichen Sinn um ein Verhalten, sondern ist nur eine Abstraktion der Motorkontrollen. + * @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. */ +void bot_drive(int8 curve, int16 speed){ + // Wenn etwas ausgewichen wurde, bricht das Verhalten hier ab, sonst wuerde es evtl. die Handlungsanweisungen von bot_avoid_harm() stoeren. + //if(bot_avoid_harm()) return; + if(curve < 0) { + speedWishLeft = speed * (1.0 + 2.0*((float)curve/127)); + speedWishRight = speed; + } else if (curve > 0) { + speedWishRight = speed * (1.0 - 2.0*((float)curve/127)); + speedWishLeft = speed; + } else { + speedWishLeft = speed; + speedWishRight = speed; + } +} + +/*! + * Das Verhalten laesst den Bot eine vorher festgelegte Strecke fahren. + * @param *data der Verhaltensdatensatz + * @see bot_drive_distance() + */ +void bot_drive_distance_behaviour(Behaviour_t* data){ + int16 *encoder; + int16 to_drive; + + if (drive_distance_curve > 0){ + // Es handelt sich um eine Rechtskurve, daher wird mit dem linken Encoder gerechnet + encoder = (int16*)&sensEncL; + } else { + encoder = (int16*)&sensEncR; + } + + to_drive = drive_distance_target - *encoder; + if(drive_distance_speed < 0) to_drive = -to_drive; + + if(to_drive <= 0){ + return_from_behaviour(data); + } else { + if((drive_distance_speed > BOT_SPEED_SLOW || drive_distance_speed < -BOT_SPEED_SLOW) && to_drive < (0.1 * ENCODER_MARKS)) bot_drive(drive_distance_curve, drive_distance_speed / 2); + else bot_drive(drive_distance_curve, drive_distance_speed); + } +} + + + +/*! + * 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){ +// LOG_DEBUG(("curve= %d, speed= %d, cm =%d",curve, speed, cm)); + + int32 tmp = cm; + tmp*= 10 * ENCODER_MARKS; + tmp /= WHEEL_PERIMETER; + int16 marks_to_drive = (int16) tmp; + + 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); +} +#endif diff --git a/source/ct-Bot/bot-logic/behaviour_drive_square.c b/source/ct-Bot/bot-logic/behaviour_drive_square.c new file mode 100644 index 0000000..7c2a924 --- /dev/null +++ b/source/ct-Bot/bot-logic/behaviour_drive_square.c @@ -0,0 +1,76 @@ +/* + * 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. + * + */ + +/*! @file behaviour_drive_square.c + * @brief Bot faehrt im Quadrat + * + * @author Benjamin Benz (bbe@heise.de) + * @date 03.11.06 +*/ + + +#include "bot-logic/bot-logik.h" + +#ifdef BEHAVIOUR_DRIVE_SQUARE_AVAILABLE +/*! + * Laesst den Roboter ein Quadrat abfahren. + * Einfaches Beispiel fuer ein Verhalten, das einen Zustand besitzt. + * Es greift auf andere Behaviours zurueck und setzt daher + * selbst keine speedWishes. + * @param *data der Verhaltensdatensatz + */ +void bot_drive_square_behaviour(Behaviour_t *data){ + #define STATE_TURN 1 + #define STATE_FORWARD 0 + #define STATE_INTERRUPTED 2 + + static uint8 state = STATE_FORWARD; + + if (data->subResult == SUBFAIL) // letzter Auftrag schlug fehl? + state= STATE_INTERRUPTED; + + switch (state) { + case STATE_FORWARD: // Vorwaerts + bot_goto(data,100,100); + state = STATE_TURN; + break; + case STATE_TURN: // Drehen + bot_goto(data,22,-22); + state=STATE_FORWARD; + break; + case STATE_INTERRUPTED: + return_from_behaviour(data); // Beleidigt sein und sich selbst deaktiviern + break; + + default: /* Sind wir fertig, dann Kontrolle zurueck an Aufrufer */ + return_from_behaviour(data); + break; + } +} + +/*! + * Laesst den Roboter ein Quadrat abfahren. + * @param caller Der obligatorische Verhaltensdatensatz des aufrufers + */ +void bot_drive_square(Behaviour_t* caller){ + switch_to_behaviour(caller, bot_drive_square_behaviour,OVERRIDE); +} + + +#endif diff --git a/source/ct-Bot/bot-logic/behaviour_follow_line.c b/source/ct-Bot/bot-logic/behaviour_follow_line.c new file mode 100644 index 0000000..e35b16f --- /dev/null +++ b/source/ct-Bot/bot-logic/behaviour_follow_line.c @@ -0,0 +1,176 @@ +/* + * 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. + * + */ + +/*! @file behaviour_follow_line.c + * @brief Linienverfolger + * + * @author Benjamin Benz (bbe@heise.de) + * @date 03.11.06 +*/ + +#include "bot-logic/bot-logik.h" + +#ifdef BEHAVIOUR_FOLLOW_LINE_AVAILABLE + +/*! Folgt einer Linie, sobald beide Liniensensoren ausloesen + * Die Linie sollte in etwa die Breite beider CNY70 haben + */ +void bot_follow_line_behaviour(Behaviour_t *data) { + /* Konstanten fuer das Verhalten */ + #define CORNER_LEFT 1 + #define CORNER_RIGHT 2 + /* Zustaende fuer das Verhalten */ + #define CHECK_LINE 0 /* !< Feststellen ob wir ueber einer Linie sind */ + #define FOLLOW_LINE 1 /* !< Folgen einer geraden oder leicht gekruemmten Linie */ + #define CHECK_BORDER 2 /* !< Abgrundsensoren haben Alarm geschlagen. Feststellen ob wirklich Abgrund oder Ecke */ + #define CORNER_TURN 3 /* !< Drehung in Richtun detektiertem Abgrund */ + #define CORRECT_POS 4 /* !< Nach der Drehung 1cm vorfahren zum Ausgleichen */ + #define ADVANCE_CORNER 5 /* !< Auf die Ecke zufahren, bis die Linie verschwindet */ + #define RETREAT_AND_STOP 6 /* !< Zurueckfahren mit voller Geschwindigkeit, dann Stop und Verhalten verlassen */ + + /* Status- und Hilfsvariablen */ + static int8 lineState=CHECK_LINE; + static int8 cornerDetected=False; + + switch(lineState) { + case CHECK_LINE: /* sind beide Sensoren ueber einer Linie? */ + if (sensLineL>=LINE_SENSE && sensLineR>=LINE_SENSE) { + /* zunaechst alle Hilfsverhalten ausschalten, die den Algorithmus stoeren */ + /* Abgrund- und Kollisions-Verhalten ausschalten */ + #ifdef BEHAVIOUR_AVOID_COL_AVAILABLE + deactivateBehaviour(bot_avoid_col_behaviour); + #endif + #ifdef BEHAVIOUR_AVOID_BORDER_AVAILABLE + deactivateBehaviour(bot_avoid_border_behaviour); + #endif + /* bot_glance() stoert bot_turn() */ + //deactivateBehaviour(bot_glance_behaviour); + /* losfahren und nach FOLLOW_LINE wechseln */ + speedWishLeft=BOT_SPEED_FOLLOW; + speedWishRight=BOT_SPEED_FOLLOW; + lineState=FOLLOW_LINE; + } + break; + + case FOLLOW_LINE: + /* Pruefen, ob die Abgrundsensoren einen Abgrund sehen */ + if (sensBorderL>BORDER_DANGEROUS || sensBorderR>BORDER_DANGEROUS) { + /* Abgrund erkannt, das kann jetzt eine Linie sein oder ein richtiger Abgrund.*/ + if (sensBorderL>BORDER_DANGEROUS && sensBorderR>BORDER_DANGEROUS) { + /* Wenn beidseitig erkannt, koennen wir damit nicht umgehen -> + * Ende des Verhaltens */ + speedWishLeft=BOT_SPEED_STOP; + speedWishRight=BOT_SPEED_STOP; + lineState=CHECK_LINE; /* Verhaltensstatus zuruecksetzen */ + //LOG_INFO(("Stopp in FOLLOW_LINE")); + return_from_behaviour(data); + break; + } + /* nachsehen, ob der linke oder rechte Liniensensor ohne Kontakt zur Linie ist + * und ggfs. gegensteuern */ + if (sensBorderL>BORDER_DANGEROUS) { + cornerDetected=CORNER_LEFT; + } else { + cornerDetected=CORNER_RIGHT; + } + /* nun zur vermuteten Ecke vorfahren */ + lineState=CHECK_BORDER; + bot_drive_distance(data,0,BOT_SPEED_FOLLOW,3); + break; + } + if (sensLineL<LINE_SENSE && sensLineR>LINE_SENSE) { + /* links von der Linie abgekommen, daher nach rechts drehen */ + //LOG_DEBUG(("Drehe rechts")); + speedWishLeft=BOT_SPEED_FOLLOW; + speedWishRight=-BOT_SPEED_FOLLOW; + } else if (sensLineL>LINE_SENSE && sensLineR<LINE_SENSE) { + /* andersrum, also links drehen */ + //LOG_DEBUG(("Drehe links")); + speedWishLeft=-BOT_SPEED_FOLLOW; + speedWishRight=BOT_SPEED_FOLLOW; + } else if (sensLineL>LINE_SENSE && sensLineR>LINE_SENSE) { + /* noch ueber der Linie, also einfach geradeaus weiter */ + //LOG_DEBUG(("Fahre geradeaus")); + speedWishLeft=BOT_SPEED_FOLLOW; + speedWishRight=BOT_SPEED_FOLLOW; + } + break; + + case CHECK_BORDER: + /* wir sollten jetzt direkt an der Kante zum Abgrund stehen, wenn es + * denn wirklich eine ist. In dem Fall fahren wir ein Stueck zurueck. + * sonst gehen wir von einer Linie aus, drehen uns in die Richtung, + * in der wir den "Abgrund" entdeckt haben und machen dann weiter mit + * der Linienverfolgung */ + if (sensBorderL>BORDER_DANGEROUS || sensBorderR>BORDER_DANGEROUS) { + /* scheint wirklich ein Abgrund zu sein */ + lineState=RETREAT_AND_STOP; + speedWishLeft=-BOT_SPEED_MAX; + speedWishRight=-BOT_SPEED_MAX; + break; + } + /* war nur eine Ecke, noch weiter darauf zu bis kein Kontakt mehr zur Linie */ + lineState=ADVANCE_CORNER; + speedWishLeft=BOT_SPEED_FOLLOW; + speedWishRight=BOT_SPEED_FOLLOW; + break; + + case ADVANCE_CORNER: + /* auf die Ecke zufahren, bis die Linie verschwindet */ + if (sensLineL<LINE_SENSE && sensLineR<LINE_SENSE) { + /* Linie weg, also Stop, kurz zurueck und drehen */ + lineState=CORNER_TURN; + speedWishLeft=-BOT_SPEED_SLOW; + speedWishRight=-BOT_SPEED_SLOW; + break; + } + speedWishLeft=BOT_SPEED_FOLLOW; + speedWishRight=BOT_SPEED_FOLLOW; + break; + + case CORNER_TURN: + /* 90� in Richtung des detektierten Abgrunds drehen */ + lineState=CORRECT_POS; + bot_turn(data,(cornerDetected==CORNER_LEFT)?90:-90); + cornerDetected=False; + break; + + case CORRECT_POS: + lineState=FOLLOW_LINE; + bot_drive_distance(data,0,BOT_SPEED_SLOW,2); + break; + + case RETREAT_AND_STOP: + /* wir sind an einem Abgrund, Stop und Ende des Verhaltens */ + speedWishLeft=BOT_SPEED_STOP; + speedWishRight=BOT_SPEED_STOP; + cornerDetected=False; + lineState=CHECK_LINE; + return_from_behaviour(data); + break; + } +} + +/*! Folgt einer Linie, sobald beide Liniensensoren ausloesen + * Die Linie sollte in etwa die Breite beider CNY70 haben + */ +void bot_follow_line(Behaviour_t *caller) { + switch_to_behaviour(caller, bot_follow_line_behaviour,NOOVERRIDE); +} +#endif diff --git a/source/ct-Bot/bot-logic/behaviour_goto.c b/source/ct-Bot/bot-logic/behaviour_goto.c new file mode 100644 index 0000000..9b655a4 --- /dev/null +++ b/source/ct-Bot/bot-logic/behaviour_goto.c @@ -0,0 +1,141 @@ +/* + * 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. + * + */ + +/*! @file behaviour_goto.c + * @brief Bot faehrt einen punkt an + * + * @author Benjamin Benz (bbe@heise.de) + * @date 03.11.06 +*/ + + + +#include "bot-logic/bot-logik.h" + +#ifdef BEHAVIOUR_GOTO_AVAILABLE +#include <stdlib.h> + + +#define MOT_GOTO_MAX 4 /*!< Richtungsaenderungen, bis goto erreicht sein muss */ +#define GOTO_REACHED 2 /*!< Wenn Encoder-Distanz <= GOTO_REACHED dann stop */ +#define GOTO_SLOW 4 /*!< Wenn Encoder-Distanz < GOTO_SLOW dann langsame Fahrt */ +#define GOTO_NORMAL 10 /*!< Wenn Encoder-Distanz < GOTO_NORMAL dann normale Fahrt */ +#define GOTO_FAST 40 /*!< Wenn Encoder-Distanz < GOTO_FAST dann schnelle Fahrt, sonst maximale Fahrt */ + + +int16 mot_l_goto=0; /*!< Speichert wie weit der linke Motor drehen soll */ +int16 mot_r_goto=0; /*!< Speichert wie weit der rechte Motor drehen soll */ + + +/*! + * Kuemmert sich intern um die Ausfuehrung der goto-Kommandos, + * @param *data der Verhaltensdatensatz + * @see bot_goto() + */ +void bot_goto_behaviour(Behaviour_t *data){ + static int16 mot_goto_l=0; /*!< Muss der linke Motor noch drehen? */ + static int16 mot_goto_r=0; /*!< Muss der rechte Motor noch drehen? */ + + int16 diff_l; /* Restdistanz links */ + int16 diff_r; /* Restdistanz rechts */ + + /* Sind beide Zaehler Null und ist die Funktion active + * -- sonst waeren wir nicht hier -- + * so ist dies der erste Aufruf ==> initialisieren */ + if (( mot_goto_l ==0) && ( mot_goto_r ==0)){ + /* Zaehler einstellen */ + if (mot_l_goto !=0) + mot_goto_l= MOT_GOTO_MAX; + if (mot_r_goto !=0) + mot_goto_r=MOT_GOTO_MAX; + + /* Encoder zuruecksetzen */ + sensEncL=0; + sensEncR=0; + } + + /* Motor L hat noch keine MOT_GOTO_MAX Nulldurchgaenge gehabt */ + if (mot_goto_l >0){ + diff_l = sensEncL - mot_l_goto; /* Restdistanz links */ + + if (abs(diff_l) <= GOTO_REACHED){ /* 2 Encoderstaende Genauigkeit reicht */ + speedWishLeft = BOT_SPEED_STOP; /* Stop */ + mot_goto_l--; /* wie Nulldurchgang behandeln */ + }else if (abs(diff_l) < GOTO_SLOW) + speedWishLeft= BOT_SPEED_SLOW; + else if (abs(diff_l) < GOTO_NORMAL) + speedWishLeft= BOT_SPEED_NORMAL; + else if (abs(diff_l) < GOTO_FAST) + speedWishLeft= BOT_SPEED_FAST; + else speedWishLeft= BOT_SPEED_MAX; + + // Richtung + if (diff_l>0) { // Wenn uebersteuert, + speedWishLeft= -speedWishLeft; //Richtung umkehren + } + + // Wenn neue Richtung ungleich alter Richtung + if (((speedWishLeft<0)&& (direction.left == DIRECTION_FORWARD))|| ( (speedWishLeft>0) && (direction.left == DIRECTION_BACKWARD) ) ) + mot_goto_l--; // Nulldurchgang merken + } + + // Motor R hat noch keine MOT_GOTO_MAX Nulldurchgaenge gehabt + if (mot_goto_r >0){ + diff_r = sensEncR - mot_r_goto; /* Restdistanz rechts */ + + if (abs(diff_r) <= GOTO_REACHED){ // 2 Encoderstaende Genauigkeit reicht + speedWishRight = BOT_SPEED_STOP; //Stop + mot_goto_r--; // wie Nulldurchgang behandeln + }else if (abs(diff_r) < GOTO_SLOW) + speedWishRight= BOT_SPEED_SLOW; + else if (abs(diff_r) < GOTO_NORMAL) + speedWishRight= BOT_SPEED_NORMAL; + else if (abs(diff_r) < GOTO_FAST) + speedWishRight= BOT_SPEED_FAST; + else speedWishRight= BOT_SPEED_MAX; + + // Richtung + if (diff_r>0) { // Wenn uebersteurt, + speedWishRight= -speedWishRight; //Richtung umkehren + } + + // Wenn neue Richtung ungleich alter Richtung + if (((speedWishRight<0)&& (direction.right == DIRECTION_FORWARD))|| ( (speedWishRight>0) && (direction.right == DIRECTION_BACKWARD) ) ) + mot_goto_r--; // Nulldurchgang merken + } + + /* Sind wir fertig, dann Regel deaktivieren */ + if ((mot_goto_l == 0) && (mot_goto_r == 0)){ + return_from_behaviour(data); + } +} + +/*! + * Drehe die Raeder um die gegebene Zahl an Encoder-Schritten weiter + * @param left Schritte links + * @param right Schritte rechts + */ +void bot_goto(Behaviour_t * caller, int16 left, int16 right){ + // Zielwerte speichern + mot_l_goto=left; + mot_r_goto=right; + + switch_to_behaviour(caller,bot_goto_behaviour,OVERRIDE); +} +#endif diff --git a/source/ct-Bot/bot-logic/behaviour_gotoxy.c b/source/ct-Bot/bot-logic/behaviour_gotoxy.c new file mode 100644 index 0000000..f5190ae --- /dev/null +++ b/source/ct-Bot/bot-logic/behaviour_gotoxy.c @@ -0,0 +1,141 @@ +/* + * 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. + * + */ + +/*! @file behaviour_gotoxy.c + * @brief Bot faehrt eine Position an + * + * @author Benjamin Benz (bbe@heise.de) + * @date 03.11.06 +*/ + +#include "bot-logic/bot-logik.h" + +#ifdef BEHAVIOUR_GOTOXY_AVAILABLE +#include <math.h> + + + + +/* Parameter fuer bot_gotoxy_behaviour-Verhalten */ +float target_x; /*!< Zielkoordinate X */ +float target_y; /*!< Zielkoordinate Y */ +float initialDiffX; /*!< Anfangsdifferenz in X-Richtung */ +float initialDiffY; /*!< Anfangsdifferenz in Y-Richtung */ + + +/*! + * Auslagerung der Berechnung der benoetigten Drehung aus dem gotoxy_behaviour + * @param xDiff + * @param yDiff + */ +int16 bot_gotoxy_calc_turn(float xDiff, float yDiff){ + float newHeading; + if(fabs(yDiff)>0.001 && fabs(xDiff)>0.001){ + newHeading=atan(yDiff/xDiff)*180/(M_PI); + if (xDiff<0) + newHeading+=180; + } else{ + if(fabs(xDiff)<=0.001) + newHeading=(yDiff>0)?90:(-90); + else + newHeading=(xDiff>0)?0:(180); + } + + int16 toTurn=(int16)newHeading-heading; + if (toTurn > 180) toTurn-=360; + if (toTurn < -180) toTurn+=360; + + return toTurn; +} + +/*! + * Das Verhalten faehrt von der aktuellen Position zur angegebenen Position (x/y) + * @param *data der Verhaltensdatensatz + * Verbessert von Thomas Noll, Jens Schoemann, Ben Horst (Philipps-Universitaet Marburg) + */ +void bot_gotoxy_behaviour(Behaviour_t *data){ + #define INITIAL_TURN 0 + #define GOTO_LOOP 1 + #define CORRECT_HEAD 2 + #define REACHED_POS 3 + static int16 speedLeft=BOT_SPEED_FOLLOW; + static int16 speedRight=BOT_SPEED_FOLLOW; + static int8 gotoState=INITIAL_TURN; + /* aus aktueller Position und Ziel neuen Zielwinkel berechnen */ + float xDiff=target_x-x_pos; + float yDiff=target_y-y_pos; + + if(xDiff*initialDiffX <0 || yDiff*initialDiffY <0){ // Hier kann noch verbessert werden + gotoState=REACHED_POS; // z.B. Abfragen statt *-Operation + speedWishLeft=BOT_SPEED_STOP; // bzw. neue Drehung statt Stehenbleiben + speedWishRight=BOT_SPEED_STOP; + } + + + switch(gotoState) { + case INITIAL_TURN: + gotoState=GOTO_LOOP; + bot_turn(data,bot_gotoxy_calc_turn(xDiff,yDiff)); + break; + + case GOTO_LOOP: + /* Position erreicht? */ + if ((xDiff)<10 || (yDiff)<10) { + gotoState=CORRECT_HEAD; + bot_turn(data,bot_gotoxy_calc_turn(xDiff,yDiff)); + break; + } + speedWishLeft=speedLeft; + speedWishRight=speedRight; + break; + + case CORRECT_HEAD: + /* Position erreicht? */ + if ((xDiff)<3 && (yDiff)<3) { + gotoState=REACHED_POS; + speedWishLeft=BOT_SPEED_STOP; + speedWishRight=BOT_SPEED_STOP; + break; + } + speedWishLeft=BOT_SPEED_SLOW; + speedWishRight=BOT_SPEED_SLOW; + break; + + case REACHED_POS: + gotoState=INITIAL_TURN; + return_from_behaviour(data); + break; + } + +} + +/*! + * Botenfunktion: Das Verhalten faehrt von der aktuellen Position zur angegebenen Position (x/y) + * @param caller Aufrufendes Verhalten + * @param x X-Ordinate an die der Bot fahren soll + * @param y Y-Ordinate an die der Bot fahren soll + */ +void bot_gotoxy(Behaviour_t *caller, float x, float y){ + target_x=x; + target_y=y; + initialDiffX=x-x_pos; + initialDiffY=y-y_pos; + switch_to_behaviour(caller, bot_gotoxy_behaviour, NOOVERRIDE); +} +#endif diff --git a/source/ct-Bot/bot-logic/behaviour_olympic.c b/source/ct-Bot/bot-logic/behaviour_olympic.c new file mode 100644 index 0000000..2c6e853 --- /dev/null +++ b/source/ct-Bot/bot-logic/behaviour_olympic.c @@ -0,0 +1,428 @@ +/* + * 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. + * + */ + +/*! @file behaviour_olympic.c + * @brief Bot sucht saeulen und faehrt dann slalom + * + * @author Benjamin Benz (bbe@heise.de) + * @date 03.11.06 +*/ + +#include "bot-logic/bot-logik.h" + +#ifdef BEHAVIOUR_OLYMPIC_AVAILABLE +#include <stdlib.h> + +/* Zustaende des bot_explore-Verhaltens */ + +#define EXPLORATION_STATE_GOTO_WALL 1 /*!< Zustand: Bot sucht eine Wand o.ae. Hinderniss */ +#define EXPLORATION_STATE_TURN_PARALLEL_LEFT 2 /*!< Zustand: Bot dreht sich nach links, bis er parallel zur Wand blickt. */ +#define EXPLORATION_STATE_TURN_PARALLEL_RIGHT 3 /*!< Zustand: Bot dreht sich nach rechts, bis er parallel zur Wand blickt. */ +#define EXPLORATION_STATE_DRIVE_PARALLEL_LEFT 4 /*!< Zustand: Bot faehrt parallel zur Wand links von sich. */ +#define EXPLORATION_STATE_DRIVE_PARALLEL_RIGHT 5 /*!< Zustand: Bot faehrt parallel zur Wand rechts von sich. */ +#define EXPLORATION_STATE_TURN_ORTHOGONAL_LEFT 6 /*!< Zustand: Bot dreht sich nach links, bis er senkrecht zur Wand steht. */ +#define EXPLORATION_STATE_TURN_ORTHOGONAL_RIGHT 7 /*!< Zustand: Bot dreht sich nach rechts, bis er senkrecht zur Wand steht. */ +#define EXPLORATION_STATE_DRIVE_ARC 8 /*!< Zustand: Bot faehrt einen Bogen. Der Winkel des Bogens sollte in einer + *!< weiteren static Variablen (z.B. curve) gespeichert sein. */ + +/* Zustaende des bot_olympic_behaviour-Verhaltens */ + +#define CB_STATE_EXPLORATION 0 /*!< Zustand: Bot erforscht die Umgebung. */ +#define CB_STATE_DOING_SLALOM 1 /*!< Zustand: Bot ist dabei Slalom zu fahren. */ + +/* Zustaende des bot_do_slalom-Verhaltens */ + +#define SLALOM_STATE_START 0 /*!< Zustand: Bot startet eine Slalomlauf und positioniert sich vor der Saeule. */ +#define SLALOM_STATE_TURN_1 1 /*!< Zustand: Bot dreht sich um 90�. */ +#define SLALOM_STATE_DRIVE_ARC 2 /*!< Zustand: Bot faehrt den Bogen um die Saeule. */ +#define SLALOM_STATE_TURN_2 3 /*!< Zustand: Bot dreht sich fuer den Sweep um 45�. */ +#define SLALOM_STATE_SWEEP_RUNNING 4 /*!< Zustand: Bot macht den Sweep. */ +#define SLALOM_STATE_SWEEP_DONE 5 /*!< Zustand: Bot ist fertig mit dem Sweep. */ +#define SLALOM_STATE_CHECK_PILLAR 6 /*!< Zustand: Bot ueberprueft, ob er den Slalom fortsetzen kann. */ + +#define SLALOM_ORIENTATION_LEFT 0 +#define SLALOM_ORIENTATION_RIGHT 1 + +/* Parameter fuer das bot_explore_behaviour() */ +int8 (*exploration_check_function)(void); /*!< Die Funktion, mit der das bot_explore_behaviour() feststellt, ob es etwas gefunden hat. + * Die Funktion muss True (1) zurueck geben, wenn dem so ist, sonst False (0). + * Beispiele fuer eine solche Funktion sind check_for_light, is_good_pillar_ahead etc.*/ + + + + +/*! + * Das Verhalten dreht den Bot so, dass er auf eine Lichtquelle zufaehrt. */ +void bot_goto_light(void){ + int16 speed, curve = (sensLDRL - sensLDRR)/1.5; + + if(curve < -127) curve = -127; + if(curve > 127) curve = 127; + + if(abs(sensLDRL - sensLDRR) < 20){ + speed = BOT_SPEED_MAX; + }else if(abs(sensLDRL - sensLDRR) < 150) { + speed = BOT_SPEED_FAST; + }else { + speed = BOT_SPEED_NORMAL; + } + + bot_drive(curve, speed); +} + + + +/*! + * Gibt aus, ob der Bot Licht sehen kann. + * @return True, wenn der Bot Licht sieht, sonst False. */ +int8 check_for_light(void){ + // Im Simulator kann man den Bot gut auf den kleinsten Lichtschein + // reagieren lassen, in der Realitaet gibt es immer Streulicht, so dass + // hier ein hoeherer Schwellwert besser erscheint. + // Simulator: + if(sensLDRL >= 1023 && sensLDRR >= 1023) return False; + // Beim echten Bot eher: + // if(sensLDRL >= 100 && sensLDRR >= 100) return False; + + else return True; +} + +/*! + * Die Funktion gibt aus, ob sich innerhalb einer gewissen Entfernung ein Objekt-Hindernis befindet. + * @param distance Entfernung in mm, bis zu welcher ein Objekt gesichtet wird. + * @return Gibt False (0) zurueck, wenn kein Objekt innerhalb von distance gesichtet wird. Ansonsten die Differenz + * zwischen dem linken und rechten Sensor. Negative Werte besagen, dass das Objekt naeher am linken, positive, dass + * es naeher am rechten Sensor ist. Sollten beide Sensoren den gleichen Wert haben, gibt die Funktion 1 zurueck, um + * von False unterscheiden zu koennen. */ +int16 is_obstacle_ahead(int16 distance){ + if(sensDistL > distance && sensDistR > distance) return False; + if(sensDistL - sensDistR == 0) return 1; + else return (sensDistL - sensDistR); +} + +/*! + * Gibt aus, ob der Bot eine fuer sein Slalomverhalten geeignete Saeule vor sich hat. + * @return True, wenn er eine solche Saeule vor sich hat, sonst False.*/ +int8 is_good_pillar_ahead(void){ + if(is_obstacle_ahead(COL_NEAR) != False && sensLDRL < 600 && sensLDRR < 600) return True; + else return False; +} + +/*! + * Das Verhalten verhindert, dass dem Bot boese Dinge wie Kollisionen oder Abstuerze widerfahren. + * @return Bestand Handlungsbedarf? True, wenn das Verhalten ausweichen musste, sonst False. + * TODO: Parameter einfuegen, der dem Verhalten vorschlaegt, wie zu reagieren ist. + * */ +int8 bot_avoid_harm(void){ + if(is_obstacle_ahead(COL_CLOSEST) != False || sensBorderL > BORDER_DANGEROUS || sensBorderR > BORDER_DANGEROUS){ + speedWishLeft = -BOT_SPEED_NORMAL; + speedWishRight = -BOT_SPEED_NORMAL; + return True; + } else return False; +} + +/*! + * 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_behaviour 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_explore_behaviour(Behaviour_t *data){ + static int8 curve = 0,state = EXPLORATION_STATE_GOTO_WALL, running_curve = False; + + if((*exploration_check_function)()) return_from_behaviour(data); + + switch(state){ + // Volle Fahrt voraus, bis ein Hindernis erreicht ist. + case EXPLORATION_STATE_GOTO_WALL: + // Der Bot steht jetzt vor einem Hindernis und soll sich nach rechts drehen + if(bot_avoid_harm()) { + state = EXPLORATION_STATE_TURN_PARALLEL_RIGHT; + #ifdef BEHAVIOUR_AVOID_COL_AVAILABLE + deactivateBehaviour(bot_avoid_col_behaviour); + #endif + } + // Es befindet sich kein Hindernis direkt vor dem Bot. + else { + if(sensDistL < COL_NEAR || sensDistR < COL_NEAR){ + bot_drive(0,BOT_SPEED_FAST); + } else { + bot_drive(0,BOT_SPEED_MAX); + } + } + break; + // Nach links drehen, bis der Bot parallel zum Hindernis auf der rechten Seite steht. + /* TODO: Aufgabe: Entwickle ein Verhalten, dass auch bei Loechern funktioniert. + * Tipp dazu: Drehe den Roboter auf das Loch zu, bis beide Bodensensoren das Loch 'sehen'. Anschliessend drehe den Bot um 90 Grad. + * Es ist noetig, neue Zustaende zu definieren, die diese Zwischenschritte beschreiben. + * TODO: Drehung mit dem Maussensor ueberwachen. */ + case EXPLORATION_STATE_TURN_PARALLEL_LEFT: + if(sensDistR < COL_FAR){ + // Volle Drehung nach links mit ca. 3Grad/10ms + bot_drive(-127,BOT_SPEED_FAST); + } else { + // Nachdem das Hindernis nicht mehr in Sicht ist, dreht der Bot noch ca. 3 Grad weiter. + // Im Zweifelsfall dreht das den Bot zu weit, aber das ist besser, als ihn zu kurz zu drehen. + bot_drive(-127,BOT_SPEED_FAST); + state = EXPLORATION_STATE_DRIVE_PARALLEL_RIGHT; + } + break; + // Nach rechts drehen, bis der Bot parallel zum Hindernis auf der linken Seite steht. + /* Aufgabe: siehe EXPLORATION_STATE_TURN_PARALLEL_LEFT */ + case EXPLORATION_STATE_TURN_PARALLEL_RIGHT: + if(sensDistL < COL_FAR){ + // Volle Drehung nach rechts mit ca. 3Grad/10ms + bot_drive(127,BOT_SPEED_FAST); + } else { + /* Nachdem das Hindernis nicht mehr in Sicht ist, dreht der Bot noch ca. 3 Grad weiter. + * Im Zweifelsfall dreht das den Bot zu weit, aber das ist besser, als ihn zu kurz zu drehen. */ + bot_drive(127,BOT_SPEED_FAST); + state = EXPLORATION_STATE_DRIVE_PARALLEL_LEFT; + } + break; + case EXPLORATION_STATE_DRIVE_PARALLEL_LEFT: + bot_drive_distance(data,0,BOT_SPEED_FAST,15); + state = EXPLORATION_STATE_TURN_ORTHOGONAL_RIGHT; + break; + case EXPLORATION_STATE_DRIVE_PARALLEL_RIGHT: + bot_drive_distance(data,0,BOT_SPEED_FAST,15); + state = EXPLORATION_STATE_TURN_ORTHOGONAL_LEFT; + break; + case EXPLORATION_STATE_TURN_ORTHOGONAL_LEFT: + // Drehe den Bot um 90 Grad nach links. + /* Da der Bot sich immer ein bisschen zu weit von der Wand weg dreht, soll er sich + * hier nur um 85 Grad drehen. Nicht schoen, aber klappt.*/ + bot_turn(data,85); + state = EXPLORATION_STATE_DRIVE_ARC; + #ifdef BEHAVIOUR_AVOID_COL_AVAILABLE + activateBehaviour(bot_avoid_col_behaviour); + #endif + break; + case EXPLORATION_STATE_TURN_ORTHOGONAL_RIGHT: + // Drehe den Bot um 90 Grad nach rechts. + /* Da der Bot sich immer ein bisschen zu weit von der Wand weg dreht, soll er sich + * hier nur um 85 Grad drehen. Nicht schoen, aber klappt.*/ + bot_turn(data,-85); + state = EXPLORATION_STATE_DRIVE_ARC; + #ifdef BEHAVIOUR_AVOID_COL_AVAILABLE + activateBehaviour(bot_avoid_col_behaviour); + #endif + break; + case EXPLORATION_STATE_DRIVE_ARC: + /* Fahre einen Bogen. + * Der Bot soll im Wechsel Links- und Rechtsboegen fahren. Daher muss das Vorzeichen von curve wechseln. + * Ausserdem soll der Bogen zunehmend weiter werden, so dass der absolute Wert von curve abnehmen muss. + * Ist der Wert 0, wird er auf den engsten Bogen initialisiert. Da der Bot am Anfang nach rechts abbiegt, + * muss der Wert positiv sein. + * Aufgabe: Manchmal kann es passieren, dass der Bot bei einer kleinen Kurve zu weit weg von der Wand + * startet und dann nur noch im Kreis faehrt. Unterbinde dieses Verhalten. + */ + if(curve == 0){ + curve = 25; + running_curve = True; + } else if (running_curve == False){ + curve *= -0.9; + running_curve = True; + } + /* Sobald der Bot auf ein Hindernis stoesst, wird der naechste Zyklus eingeleitet. + * Auf einen Rechtsbogen (curve > 0) folgt eine Linksdrehung und auf einen Linksbogen eine Rechtsdrehung. + * Wenn der Wert von curve (durch Rundungsfehler bei int) auf 0 faellt, beginnt das Suchverhalten erneut.*/ + if(bot_avoid_harm()) { + state = (curve > 0) ? EXPLORATION_STATE_TURN_PARALLEL_LEFT : EXPLORATION_STATE_TURN_PARALLEL_RIGHT; + running_curve = False; + #ifdef BEHAVIOUR_AVOID_COL_AVAILABLE + deactivateBehaviour(bot_avoid_col_behaviour); + #endif + } else { + bot_drive(curve, BOT_SPEED_MAX); + } + break; + default: + state = EXPLORATION_STATE_GOTO_WALL; + curve = 0; + #ifdef BEHAVIOUR_AVOID_COL_AVAILABLE + activateBehaviour(bot_avoid_col_behaviour); + #endif + } + +} + +/*! + * Aktiviert bot_explore_behaviour. */ +void bot_explore(Behaviour_t *caller, int8 (*check)(void)){ + exploration_check_function = check; + switch_to_behaviour(caller,bot_explore_behaviour,NOOVERRIDE); +} + +/*! + * Das Verhalten laesst den Bot einen Slalom fahren. + * @see bot_do_slalom() + * */ +void bot_do_slalom_behaviour(Behaviour_t *data){ + static int8 state = SLALOM_STATE_CHECK_PILLAR; + static int8 orientation = SLALOM_ORIENTATION_RIGHT; + static int8 sweep_state; + static int8 sweep_steps = 0; + int16 turn; + int8 curve; + + switch(state){ + case SLALOM_STATE_CHECK_PILLAR: + // Der Bot sollte jetzt Licht sehen koennen... + if(check_for_light()){ + // Wenn der Bot direkt vor der Saeule steht, kann der Slalom anfangen, sonst zum Licht fahren + if(bot_avoid_harm()){ + state = SLALOM_STATE_START; + } else bot_goto_light(); + } else {// ... sonst muss er den Slalom-Kurs neu suchen. + #ifdef BEHAVIOUR_AVOID_COL_AVAILABLE + activateBehaviour(bot_avoid_col_behaviour); + #endif + return_from_behaviour(data); + } + break; + case SLALOM_STATE_START: + // Hier ist Platz fuer weitere Vorbereitungen, falls noetig. + #ifdef BEHAVIOUR_AVOID_COL_AVAILABLE + deactivateBehaviour(bot_avoid_col_behaviour); + #endif + state = SLALOM_STATE_TURN_1; + // break; + case SLALOM_STATE_TURN_1: + turn = (orientation == SLALOM_ORIENTATION_LEFT) ? 90 : -90; + bot_turn(data,turn); + state = SLALOM_STATE_DRIVE_ARC; + break; + case SLALOM_STATE_DRIVE_ARC: + // Nicht wundern: Bei einem Links-Slalom faehrt der Bot eine Rechtskurve. + curve = (orientation == SLALOM_ORIENTATION_LEFT) ? 25 : -25; + bot_drive_distance(data,curve,BOT_SPEED_FAST,20); + state = SLALOM_STATE_TURN_2; + break; + case SLALOM_STATE_TURN_2: + turn = (orientation == SLALOM_ORIENTATION_LEFT) ? 45 : -45; + bot_turn(data,turn); + state = SLALOM_STATE_SWEEP_RUNNING; + break; + case SLALOM_STATE_SWEEP_RUNNING: + if(sweep_steps == 0){ + sweep_state = SWEEP_STATE_CHECK; + } + // Insgesamt 6 Schritte drehen + if(sweep_steps < 6) { + if(sweep_state == SWEEP_STATE_CHECK){ + // Phase 1: Pruefen, ob vor dem Bot eine gute Saeule ist + if(is_good_pillar_ahead() == True){ + // Wenn die Saeule gut ist, drauf zu und Slalom anders rum fahren. + state = SLALOM_STATE_CHECK_PILLAR; + orientation = (orientation == SLALOM_ORIENTATION_LEFT) ? SLALOM_ORIENTATION_RIGHT : SLALOM_ORIENTATION_LEFT; + sweep_steps = 0; + } else { + // Sonst drehen. + sweep_state = SWEEP_STATE_TURN; + } + } + if(sweep_state == SWEEP_STATE_TURN) { + // Phase 2: Bot um 15 Grad drehen + turn = (orientation == SLALOM_ORIENTATION_LEFT) ? 15 : -15; + bot_turn(data,turn); + sweep_state = SWEEP_STATE_CHECK; + sweep_steps++; + } + } else { + turn = (orientation == SLALOM_ORIENTATION_LEFT) ? -90 : 90; + bot_turn(data,turn); + state = SLALOM_STATE_SWEEP_DONE; + sweep_steps = 0; + } + break; + case SLALOM_STATE_SWEEP_DONE: + turn = (orientation == SLALOM_ORIENTATION_LEFT) ? -135 : 135; + bot_turn(data,turn); + state = SLALOM_STATE_CHECK_PILLAR; + break; + default: + state = SLALOM_STATE_CHECK_PILLAR; + } + +} + +/*! + * Das Verhalten laesst den Bot zwischen einer Reihe beleuchteter Saeulen Slalom fahren. + * Das Verhalten ist wie bot_explore() in eine Anzahl von Teilschritten unterteilt. + * 1. Vor die aktuelle Saeule stellen, so dass sie zentral vor dem Bot und ungefaehr + * COL_CLOSEST (100 mm) entfernt ist. + * 2. 90 Grad nach rechts drehen. + * 3. In einem relativ engen Bogen 20 cm weit fahren. + * 4. Auf der rechten Seite des Bot nach einem Objekt suchen, dass + * a) im rechten Sektor des Bot liegt, also zwischen -45 Grad und -135 Grad zur Fahrtrichtung liegt, + * b) beleuchtet und + * c) nicht zu weit entfernt ist. + * Wenn es dieses Objekt gibt, wird es zur aktuellen Saeule und der Bot faehrt jetzt Slalom links. + * 5. Sonst zurueck drehen, 90 Grad drehen und Slalom rechts fahren. + * In diesem Schritt kann der Bot das Verhalten auch abbrechen, falls er gar kein Objekt mehr findet. + */ +void bot_do_slalom(Behaviour_t *caller){ + switch_to_behaviour(caller, bot_do_slalom_behaviour,NOOVERRIDE); +} + +/*! + * Das Verhalten setzt sich aus 3 Teilverhalten zusammen: + * Nach Licht suchen, auf das Licht zufahren, im Licht Slalom fahren. */ +void bot_olympic_behaviour(Behaviour_t *data){ + if(check_for_light()){ + /* Sobald der Bot auf ein Objekt-Hinderniss stoesst, versucht er, Slalom zu fahren. + * Aufgabe: Wenn der Bot vor einem Loch steht, hinter welchem sich die Lichtquelle + * befindet, wird er daran haengen bleiben. Schreibe ein Verhalten, dass das verhindert. */ + if(bot_avoid_harm() && is_obstacle_ahead(COL_NEAR)){ + bot_do_slalom(data); + } else bot_goto_light(); + } else bot_explore(data,check_for_light); +} + +/*! + * Initialisiert das Olympische Verhalten + * @param prio_main Prioritaet des Olympischen Verhalten (typ. 100) + * @param prio_helper Prioritaet der Hilfsfunktionen (typ. 52) + * @param active ACTIVE wenn es sofort starten soll, sonst INACTIVE + */ +void bot_olympic_init(int8 prio_main,int8 prio_helper, int8 active){ + // unwichtigere Hilfsverhalten + insert_behaviour_to_list(&behaviour, new_behaviour(prio_helper--, bot_explore_behaviour,INACTIVE)); + insert_behaviour_to_list(&behaviour, new_behaviour( prio_helper, bot_do_slalom_behaviour,INACTIVE)); + // Demo-Verhalten fuer aufwendiges System, inaktiv + insert_behaviour_to_list(&behaviour, new_behaviour(prio_main, bot_olympic_behaviour, active)); +} + +#endif diff --git a/source/ct-Bot/bot-logic/behaviour_remotecall.c b/source/ct-Bot/bot-logic/behaviour_remotecall.c new file mode 100644 index 0000000..6e0125f --- /dev/null +++ b/source/ct-Bot/bot-logic/behaviour_remotecall.c @@ -0,0 +1,407 @@ +/* + * 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. + * + */ + + +/*! @file behaviour_remotecall_behaviour.c + * @brief ruft auf ein Kommando hin andere Verhalten auf und bestaetigt dann ihre Ausfuehrung + * + * @author Benjamin Benz (bbe@heise.de) + * @date 07.12.06 +*/ + +#include "bot-logic/bot-logik.h" +#ifdef BEHAVIOUR_REMOTECALL_AVAILABLE +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "log.h" +#include "command.h" + +#include "bot-logic/remote_calls.h" + +#define REMOTE_CALL_IDLE 0 +#define REMOTE_CALL_SCHEDULED 1 +#define REMOTE_CALL_RUNNING 2 + +/*! Uebergabevariable fuer Remotecall-Verhalten */ +static uint8 running_behaviour =REMOTE_CALL_IDLE; + +static uint8 function_id = 255; +static uint8 parameter_count = 0; /*!< Anzahl der Paramter (ohne Zeiger auf Aufrufer) */ +static uint8 parameter_data[8] = {0}; /*!< Hier liegen die eigentlichen Parameter, derzeit brauchen wir maximal 8 Byte (2 floats, 4 (u)int16 oder 4 (u)int8 */ +#ifdef MCU + static uint8 parameter_length[REMOTE_CALL_MAX_PARAM] = {0}; /*!< Hier speichern wir die Laenge der jeweiligen Parameter */ +#else + static uint8* parameter_length = NULL; /*!< Hier speichern wir die Laenge der jeweiligen Parameter */ +#endif + +#ifdef MCU + #include <avr/pgmspace.h> +#else + #define PROGMEM // Alibideklaration hat keine Funktion, verhindert aber eine Warning + #define strcmp_P strcmp // Auf dem PC gibt es keinen Flash, also auch kein eigenes Compare +#endif + +/*! + * Hier muessen alle Boten-Funktionen rein, die Remote aufgerufen werden sollen + * Diese stoßen dann das zugehoerige Verhalten an + * Ein Eintrag erfolgt so: + * PREPARE_REMOTE_CALL(BOTENFUNKTION,NUMBER_OF_PARAMS, STRING DER DIE PARAMETER BESCHREIBT,laenge der jeweiligen Parameter in Byte) + * + * Alle Botenfunktionen muessen folgendem Schema entsprechen + * void bot_xxx(Behaviour_t * caller, ...); + * + * Erklaerung am Bsp: + * PREPARE_REMOTE_CALL(bot_gotoxy, 2, "float x, float y", 4, 4), + * Name der Botenfunktion --^ ^ ^ ^ ^ + * Anzahl der Parameter --------- | | | + * Beschreibung der Parameter -------- | | + * Anzahl der Bytes Parameter 1 ------------------------ | + * Anzahl der Bytes Parameter 2 --------------------------- + * + * Zur Info: + * 1 Byte brauchen: uint8, int8,char + * 2 Byte brauchen: uint16, int16 + * 4 Byte brauchen: uint32, int32, float + */ +const call_t calls[] PROGMEM = { + #ifdef BEHAVIOUR_DRIVE_DISTANCE_AVAILABLE + PREPARE_REMOTE_CALL(bot_drive_distance,3, "int8 curve, int16 speed, int16 cm", 1,2,2), + #endif + #ifdef BEHAVIOUR_GOTOXY_AVAILABLE + PREPARE_REMOTE_CALL(bot_gotoxy, 2, "float x, float y", 4, 4), + #endif + #ifdef BEHAVIOUR_SOLVE_MAZE_AVAILABLE + PREPARE_REMOTE_CALL(bot_solve_maze,0,""), + #endif + #ifdef BEHAVIOUR_CATCH_PILLAR_AVAILABLE + PREPARE_REMOTE_CALL(bot_catch_pillar,0,""), + #endif + #ifdef BEHAVIOUR_DRIVE_SQUARE_AVAILABLE + PREPARE_REMOTE_CALL(bot_drive_square,0,""), + #endif + #ifdef BEHAVIOUR_FOLLOW_LINE_AVAILABLE + PREPARE_REMOTE_CALL(bot_follow_line,0,""), + #endif + #ifdef BEHAVIOUR_GOTO_AVAILABLE + PREPARE_REMOTE_CALL(bot_goto,2," int16 left, int16 right",2,2), + #endif + #ifdef BEHAVIOUR_OLYMPIC_AVAILABLE + PREPARE_REMOTE_CALL(bot_do_slalom,0,""), + #endif + #ifdef BEHAVIOUR_SCAN_AVAILABLE + PREPARE_REMOTE_CALL(bot_scan,0,""), + #endif + #ifdef BEHAVIOUR_SERVO_AVAILABLE + PREPARE_REMOTE_CALL(bot_servo,2,"uint8 servo, uint8 pos",1,1), + #endif + #ifdef BEHAVIOUR_SIMPLE_AVAILABLE + PREPARE_REMOTE_CALL(bot_simple,0,""), + PREPARE_REMOTE_CALL(bot_simple2,1,"int16 light",2), + #endif + #ifdef BEHAVIOUR_TURN_AVAILABLE + PREPARE_REMOTE_CALL(bot_turn,1,"int16 degrees",2) + #endif +}; + +#define STORED_CALLS (sizeof(calls)/sizeof(call_t)) /*!< Anzahl der Remote calls im Array */ + +/*! + * Sucht den Index des Remote-Calls heraus + * @param call String mit dem namen der gesuchten fkt + * @return Index in das calls-Array. Wenn nicht gefunden, dann 255 + */ +uint8 getRemoteCall(char * call){ +// LOG_DEBUG(("Suche nach Funktion: %s",call)); + + uint8 i; + for (i=0; i< (STORED_CALLS); i++){ + if (!strcmp_P (call, calls[i].name)){ + LOG_DEBUG(("calls[%d].name=%s passt",i,call)); + return i; + } + } + return 255; +} + +#ifdef MCU + /*! + * Hilfsfunktion fuer bot_remotecall() + * Baut einen AVR-kompatiblen Parameterstream aus einem uint32-Parameterarray und einem Infoarray ueber die Parameter + * @param dest Zeiger auf das Ausgabearray (len[0]*2 Byte gross!) + * @param count Anzahl der Parameter + * @param len Zeiger auf ein Array, das die Anzahl der Bytes fuer die jeweiligen Parameter enthaelt + * @param data Zeiger auf die Daten (32 Bit, Laenge 8) + * @author Timo Sandmann (mail@timosandmann.de) + * @date 12.01.2007 + */ + void remotecall_convert_params(uint8* dest, uint8 count, uint8* len, uint8* data){ + uint8 i; + /* jeden Parameter behandeln */ + for (i=0; i<count; i++){ + int8 j; + if (len[i] == 1) *dest++ = 0; // auch (u)int8 beginnen immer in geraden Registern + /* pro Parameter LSB zuerst nach dest kopieren */ + for (j=len[i]-1; j>=0; j--) + *dest++ = data[j]; + data += 4; // data-Array ist immer in 32 Bit + } + } +#endif // MCU + +/*! + * Dieses Verhalten kuemmert sich darum die Verhalten, die von aussen angefragt wurden zu starten und liefert ein feedback zurueck, wenn sie beendet sind. + * @param *data der Verhaltensdatensatz + */ +void bot_remotecall_behaviour(Behaviour_t *data){ + uint8 call_id =255; + +// LOG_DEBUG(("Enter bot_remotecall_behaviour")); + void (* func) (struct _Behaviour_t *data); + + switch (running_behaviour) { + case REMOTE_CALL_SCHEDULED: // Es laueft kein Auftrag, aber es steht ein neuer an +// LOG_DEBUG(("REMOTE_CALL_SCHEDULED")); + + call_id=function_id; + if (call_id >= STORED_CALLS){ +// LOG_DEBUG(("keine Funktion gefunden. Exit")); + running_behaviour=REMOTE_CALL_IDLE; + return; + } + + #ifdef PC + // Auf dem PC liegt die calls-Struktur im RAM + func = (void*) calls[call_id].func; + #else + // Auf dem MCU liegt die calls-Struktur im Flash und muss erst geholt werden + func = (void*) pgm_read_word (& calls[call_id].func); + #endif + + if (parameter_count ==0 ){ // Kommen wir ohne Parameter aus? +// LOG_DEBUG(("call_id=%u",call_id)); + func(data); // Die aufgerufene Botenfunktion starten + running_behaviour=REMOTE_CALL_RUNNING; + } else if (parameter_count <= REMOTE_CALL_MAX_PARAM){ // Es gibt gueltige Parameter + // TODO: Ja hier wird es spannend, denn jetzt muessen die Parameter auf den Stack + LOG_DEBUG(("call_id=%u",call_id)); + LOG_DEBUG(("parameter_count=%u", parameter_count)); + // asm-hacks here ;) + #ifdef PC + /* Prinzip auf dem PC: Wir legen alle Parameter einzeln auf den Stack, springen in die Botenfunktion und raeumen anschliessend den Stack wieder auf */ + uint32 tmp; + uint8 i; + volatile uint8 td=1; // verwenden wir nur, damit der Compiler unsere inline-asm-Bloecke nicht umordnet + for (i=0; i<parameter_count*4 && td>0; i+=4,td++){ // Debug-Info ausgeben +// LOG_DEBUG(("parameter_data[%u-%u] = %lu",i, i+3, *(uint32*)(parameter_data+i))); + } + /* Erster Wert in parameter_length ist die Anzahl der Parameter (ohne Zeiger des Aufrufers) */ + for (i=0; i<parameter_count && td>1; i++,td++){ // Check von td eigentlich sinnlos, aber wir brauchen eine echte Datenabhaengigkeit auf dieses Codestueck + /* cdecl-Aufrufkonvention => Parameter von rechts nach links auf den Stack */ + tmp = *(uint32*)(parameter_data+(parameter_count-i-1)*4); + /* Parameter 2 bis n pushen */ + asm volatile( // IA32-Support only + "pushl %0 # parameter i auf stack " + :: "g" (tmp) + : "memory" + ); + } + /* Parameter 1 (data) und Funktionsaufruf */ + asm volatile( + "pushl %0 # push data \n\t" + "movl %1, %%eax # adresse laden \n\t" + "call *%%eax # jump to callee \n\t" + :: "m" (data), "m" (func) + : "eax", "memory" + ); + /* caller rauemt den Stack wieder auf */ + for (i=0; i<=parameter_count && td>2; i++){ // Check von td erzwingt, dass das Aufraeumen erst jetzt passiert + asm volatile( + "pop %%eax # stack aufraeumen " + ::: "eax", "memory" + ); + } + #else + /* Prinzip auf der MCU: Keine komplizierten Rechnungen, sondern einfach alle Register ueberschreiben. + * Achtung: Derzeit braucht kein Verhalten mehr als 8 Register (2*float oder 4*int16 oder 4*int8), aendert sich das, + * muss man den Code hier erweitern! + * Die AVR-Konvention beim Funktionsaufruf: + * Die Groesse in Byte wird zur naechsten geraden Zahl aufgerundet, falls sie ungerade ist. + * Der Registerort faengt mit 26 an. + * Vom Registerort wird die berechete Groesse abgezogen und das Argument in diesen Registern uebergeben (LSB first). + * In r24/r25 legt der Compiler spaeter den Zeiger des Aufrufers, koennen wir hier also weglassen. */ +// LOG_DEBUG(("r22:r23 = %u:%u", parameter_data[1], parameter_data[0])); +// LOG_DEBUG(("r21:r20 = %u:%u", parameter_data[3], parameter_data[2])); +// LOG_DEBUG(("r18:r19 = %u:%u", parameter_data[5], parameter_data[4])); +// LOG_DEBUG(("r16:r17 = %u:%u", parameter_data[7], parameter_data[6])); + asm volatile( // remotecall_convert_params() hat den Speicher bereits richtig sortiert, nur noch Werte laden + "ld r23, Z+ \n\t" + "ld r22, Z+ \n\t" + "ld r21, Z+ \n\t" + "ld r20, Z+ \n\t" + "ld r19, Z+ \n\t" + "ld r18, Z+ \n\t" + "ld r17, Z+ \n\t" + "ld r16, Z " + :: "z" (parameter_data) + : "r23", "r22", "r21", "r20", "r19", "r18", "r17", "r16" + ); + func(data); // Die aufgerufene Botenfunktion starten + #endif + + running_behaviour=REMOTE_CALL_RUNNING; + return; + } else { +// LOG_DEBUG(("Parameteranzahl unzulaessig!")); + } + break; + + case REMOTE_CALL_RUNNING: // Es lief ein Verhalten und ist nun zuende (sonst waeren wir nicht hier) + { + // Antwort schicken + + char * function_name; + + #ifdef PC + function_name=(char*) &calls[function_id].name; + #else + // Auf dem MCU muessen wir die Daten erstmal aus dem Flash holen + + char tmp[REMOTE_CALL_FUNCTION_NAME_LEN+1]; + function_name=(char*)&tmp; + + uint8* from= (uint8*)& calls[function_id].name; + + uint8 i; + for (i=0; i<REMOTE_CALL_FUNCTION_NAME_LEN+1; i++) + *function_name++ = (uint8) pgm_read_byte ( from++ ); + function_name=(char*)&tmp; + #endif + + #ifdef COMMAND_AVAILABLE + int16 result = data->subResult; + command_write_data(CMD_REMOTE_CALL,SUB_REMOTE_CALL_DONE,&result,&result,function_name); + #endif + +// LOG_DEBUG(("Remote-call %s beendet",function_name)); + + // Aufrauemen + function_id=255; + //parameter_length=NULL; + //parameter_data=NULL; + running_behaviour=REMOTE_CALL_IDLE; + + return_from_behaviour(data); // und Verhalten auch aus + break; + } + default: + return_from_behaviour(data); // und Verhalten auch aus + break; + + } +} + +/*! + * Fuehre einen remote_call aus. Es gibt KEIN aufrufendes Verhalten!! + * @param func Zeiger auf den Namen der Fkt + * @param data Zeiger auf die Daten + */ +void bot_remotecall(char* func, remote_call_data_t* data){ + + function_id= getRemoteCall(func); + if (function_id >= STORED_CALLS){ +// LOG_DEBUG(("Funktion %s nicht gefunden. Exit!",func)); + return; + } + + // parameter_length: Zeiger auf ein Array, das zuerst die Anzahl der Parameter und danach die Anzahl der Bytes fuer die jeweiligen Parameter enthaelt + #ifdef PC + parameter_count = calls[function_id].param_count; + parameter_length = (uint8*)calls[function_id].param_len; + #else + // Auf dem MCU muessen wir die Daten erstmal aus dem Flash holen + uint8* from= (uint8*)& calls[function_id].param_len; + uint8 i; + parameter_count = pgm_read_byte(&calls[function_id].param_count); + for (i=0; i<REMOTE_CALL_MAX_PARAM; i++) + parameter_length[i] = (uint8) pgm_read_byte ( from++ ); + #endif +// LOG_DEBUG(("func=%s param_count=%d Len= %u %u %u %u",func,parameter_count,parameter_length[0],parameter_length[1],parameter_length[2])); +// if (data != NULL){ +// LOG_DEBUG(("data= %u %u %u %u",data[0],data[1],data[2],data[3])); +// } + + #ifdef MCU // Die MCU legt die Parameter nach einem anderen Verfahren ab, diese Funktion konvertiert sie deshalb + remotecall_convert_params(parameter_data, parameter_count, parameter_length, (uint8*)data); + #else // Auf dem PC kopieren wir die Daten einfach + memcpy(parameter_data, data, parameter_count*4); + #endif + + running_behaviour=REMOTE_CALL_SCHEDULED; + activateBehaviour(bot_remotecall_behaviour); +} + +/*! + * Fuehre einen remote_call aus. Es gibt KEIN aufrufendes Verhalten!! + * @param data Zeiger die Payload eines Kommandos. Dort muss zuerst ein String mit dem Fkt-Namen stehen. ihm folgen die Nutzdaten + */ +void bot_remotecall_from_command(uint8 * data){ + char * function_name = (char*)data; + remote_call_data_t * params = (remote_call_data_t *)(data+ strlen(function_name)+1); + bot_remotecall(function_name,params); +} + + +/*! + * Listet alle verfuegbaren Remote-Calls auf und verschickt sie als einzelne Kommanods + */ +void remote_call_list(void){ + #ifdef MCU + call_t call_storage; + uint8* to; + uint8* from; + #endif + call_t* call; + + int16 i; + for (i=0; i< (STORED_CALLS); i++){ + #ifdef MCU + // Auf dem MCU muessen die Daten erstmal aus dem Flash ins RAM + from= (uint8*)&calls[i]; + to= (uint8*)&call_storage; + uint8 j; + for (j=0; j< sizeof(call_t); j++){ + *to = (uint8) pgm_read_byte ( from++ ); + to++; + } + call = &call_storage; + #else + call = (call_t*)&calls[i]; + #endif + + #ifdef COMMAND_AVAILABLE + // und uebertragen + command_write_rawdata(CMD_REMOTE_CALL,SUB_REMOTE_CALL_ENTRY,&i,&i, sizeof(call_t),(uint8*)call); + #endif + +// LOG_DEBUG(("%s(%s)",call->name,call->param_info)); + } +} + +#endif diff --git a/source/ct-Bot/bot-logic/behaviour_scan.c b/source/ct-Bot/bot-logic/behaviour_scan.c new file mode 100644 index 0000000..6f6869c --- /dev/null +++ b/source/ct-Bot/bot-logic/behaviour_scan.c @@ -0,0 +1,144 @@ +/* + * 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. + * + */ + +/*! @file behaviour_scan.c + * @brief Scannt die Umgebung und traegt sie in die Karte ein + * + * @author Benjamin Benz (bbe@heise.de) + * @date 03.11.06 +*/ + +#include "bot-logic/bot-logik.h" +#include "map.h" +#include <math.h> + +#ifdef BEHAVIOUR_SCAN_AVAILABLE +/*! + * Der Roboter faehrt einen Vollkreis und scannt dabei die Umgebung + * @param *data der Verhaltensdatensatz + */ +void bot_scan_onthefly_behaviour(Behaviour_t *data){ + #define ONTHEFLY_DIST_RESOLUTION 0.02 /*!< Alle wieviel gefahrene Strecke [m] soll die Karte aktualisiert werden. Achtung er prueft x und y getrennt, daher ist die tatsaechlich zurueckgelegte Strecke im worst case sqrt(2)*ONTHEFLY_DIST_RESOLUTION */ + #define ONTHEFLY_ANGLE_RESOLUTION 10 /*!< Alle wieviel Gerad Drehung [m] soll die Karte aktualisiert werden */ + + + + #ifdef MAP_AVAILABLE + static float last_x, last_y, last_head; + + float diff_x = fabs(x_pos-last_x); + float diff_y = fabs(y_pos-last_y); + + // Wenn der Bot faehrt, aktualisieren wir alles + if ((diff_x > ONTHEFLY_DIST_RESOLUTION) ||( diff_y > ONTHEFLY_DIST_RESOLUTION)){ + update_map_location(x_pos,y_pos); + update_map(x_pos,y_pos,heading,sensDistL,sensDistR); + + last_x=x_pos; + last_y=y_pos; + last_head=heading; + return; + } + + float diff_head = fabs(last_head-heading); + // Wenn der bot nur dreht, aktualisieren wir nur die Blickstrahlen + if ( diff_head > ONTHEFLY_ANGLE_RESOLUTION ){ + update_map(x_pos,y_pos,heading,sensDistL,sensDistR); + last_head=heading; + } + /* if ((diff_x*diff_x + diff_y*diff_y > ONTHEFLY_DIST_RESOLUTION)||fabs(last_head-heading) > ONTHEFLY_ANGLE_RESOLUTION ){ + last_x=x_pos; + last_y=y_pos; + last_head=heading; + print_map(); + } + */ + #endif +} + + +#define BOT_SCAN_STATE_START 0 +uint8 bot_scan_state = BOT_SCAN_STATE_START; /*!< Zustandsvariable fuer bot_scan_behaviour */ + +/*! + * Der Roboter faehrt einen Vollkreis und scannt dabei die Umgebung + * @param *data der Verhaltensdatensatz + */ +void bot_scan_behaviour(Behaviour_t *data){ + #define BOT_SCAN_STATE_SCAN 1 + + #define ANGLE_RESOLUTION 5 /*!< Aufloesung fuer den Scan in Grad */ + +// static uint16 bot_scan_start_angle; /*!< Winkel, bei dem mit dem Scan begonnen wurde */ + static float turned; /*!< Winkel um den bereits gedreht wurde */ + + static float last_scan_angle; /*!< Winkel bei dem zuletzt gescannt wurde */ + + float diff; + + switch (bot_scan_state){ + case BOT_SCAN_STATE_START: + + turned=0; + last_scan_angle=heading-ANGLE_RESOLUTION; + bot_scan_state=BOT_SCAN_STATE_SCAN; + break; + case BOT_SCAN_STATE_SCAN: + diff = heading - last_scan_angle; + if (diff < -180) + diff+=360; + if (diff*1.15 >= ANGLE_RESOLUTION){ + turned+= diff; + last_scan_angle=heading; + + #ifdef MAP_AVAILABLE + // Eigentlicher Scan hier + update_map(x_pos,y_pos,heading,sensDistL,sensDistR); + //////////// + #endif + + } + + if (turned >= 360-ANGLE_RESOLUTION) // Ende erreicht + bot_scan_state++; + break; + default: + bot_scan_state = BOT_SCAN_STATE_START; + #ifdef MAP_AVAILABLE + print_map(); + #endif + return_from_behaviour(data); + break; + } +} + +/*! + * Der Roboter faehrt einen Vollkreis und scannt dabei die Umgebung + * @param Der aufrufer + */ +void bot_scan(Behaviour_t* caller){ + + bot_scan_state = BOT_SCAN_STATE_START; + bot_turn(caller,360); + switch_to_behaviour(0, bot_scan_behaviour,OVERRIDE); + +// update_map(x_pos,y_pos,heading,sensDistL,sensDistR); +// print_map(); +} +#endif diff --git a/source/ct-Bot/bot-logic/behaviour_servo.c b/source/ct-Bot/bot-logic/behaviour_servo.c new file mode 100644 index 0000000..77583cc --- /dev/null +++ b/source/ct-Bot/bot-logic/behaviour_servo.c @@ -0,0 +1,68 @@ +/* + * 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. + * + */ + + +/*! @file behaviour_servo.c + * @brief kontrolliert die Servos + * + * @author Benjamin Benz (bbe@heise.de) + * @date 07.12.06 +*/ + + +#include "bot-logic/bot-logik.h" +#ifdef BEHAVIOUR_SERVO_AVAILABLE + +#include "motor.h" +#include "timer.h" +/*! Uebergabevariable fuer Servo-Verhalten */ +static uint16 servo_time; +static uint8 servo_nr; +static uint8 servo_pos; +uint8 servo_active = 0; /*!< 0, wenn kein Servo aktiv, sonst Bit der gerade aktiven Servos gesetzt */ + +/*! + * Dieses Verhalten fuehrt ein Servo-Kommando aus und schaltet danach den Servo wieder ab + * + * @param *data der Verhaltensdatensatz + */ +void bot_servo_behaviour(Behaviour_t *data){ + /* Servo ausschalten, falls Klappe zu oder Countdown abgelaufen */ + if ( (servo_pos == DOOR_CLOSE && sensDoor == 0) || (TIMER_GET_TICKCOUNT_16 - servo_time > MS_TO_TICKS(1000L)) ){ + servo_set(servo_nr, SERVO_OFF); // Servo aus + servo_active &= ~servo_nr; + return_from_behaviour(data); // und Verhalten auch aus + } +} + +/*! + * Fahre den Servo an eine Position + * @param servo Nummer des Servos + * @param pos Zielposition des Servos + */ +void bot_servo(Behaviour_t * caller, uint8 servo, uint8 pos){ + servo_active |= servo; + servo_set(servo, pos); // Servo-PWM einstellen + servo_pos = pos; // Zielposition merken + servo_nr = servo; // Servo-Nr speichern + servo_time = TIMER_GET_TICKCOUNT_16; // Der Count down verschafft dem Servo etwas Zeit + + switch_to_behaviour(caller,bot_servo_behaviour,OVERRIDE); // Warte-Verahlten an +} +#endif diff --git a/source/ct-Bot/bot-logic/behaviour_simple.c b/source/ct-Bot/bot-logic/behaviour_simple.c new file mode 100644 index 0000000..5c897e9 --- /dev/null +++ b/source/ct-Bot/bot-logic/behaviour_simple.c @@ -0,0 +1,139 @@ +/* + * 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. + * + */ + + +/*! @file behaviour_simple.c + * @brief ganz einfache Beispielverhalten + * Diese Datei sollte der Einstiegspunkt fuer eigene Experimente sein, + * den Roboter zu steuern. + * + * @author Benjamin Benz (bbe@heise.de) + * @date 03.11.06 +*/ + + +#include "bot-logic/bot-logik.h" +#ifdef BEHAVIOUR_SIMPLE_AVAILABLE + + +/*! + * Ein ganz einfaches Verhalten, es hat maximale Prioritaet + * Hier kann man auf ganz einfache Weise die ersten Schritte wagen. + * Wer die Moeglichkeiten des ganzen Verhaltensframeworks ausschoepfen will, kann diese Funktion getrost auskommentieren + * und findet dann in bot_behave_init() und bot_behave() weitere Hinweise fuer elegante Bot-Programmierung.... + * + * Das Verhalten ist per default abgeschaltet. Daher muss man es erst in bot_behave_init() aktivieren. + * Dort steht aber bereits eine auskommentierte Zeile dazu, von der man nur die zwei Kommentarzeichen wegnehmen muss. + * Achtung, da bot_simple_behaviour() maximale Prioritaet hat, kommt es vor den anderen Regeln, wie dem Schutz vor Abgruenden, etc. zum Zuge + * Das sollte am Anfang nicht stoeren, spaeter sollte man jedoch die Prioritaet herabsetzen. + * + * @param *data der Verhaltensdatensatz + */ +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: + state=0; + return_from_behaviour(data); + break; + } +} + + +/*! + * Rufe das Simple-Verhalten auf + * @param caller Der obligatorische Verhaltensdatensatz des Aufrufers + */ +void bot_simple(Behaviour_t * caller, int16 light){ + switch_to_behaviour(caller,bot_simple_behaviour,OVERRIDE); +} + + +/*! Uebergabevariable fuer SIMPLE2 */ +static int16 simple2_light=0; + +/*! + * Ein ganz einfaches Beispiel fuer ein Hilfsverhalten, + * das selbst SpeedWishes aussert und + * nach getaner Arbeit die aufrufende Funktion wieder aktiviert + * Zudem prueft es, ob eine Uebergabebedingung erfuellt ist. + * + * Zu diesem Verhalten gehoert die Botenfunktion bot_simple2() + * + * Hier kann man auf ganz einfache Weise die ersten Schritte wagen. + * Wer die Moeglichkeiten des ganzen Verhaltensframeworks ausschoepfen will, kann diese Funktion getrost auskommentieren + * und findet dann in bot_behave_init() und bot_behave() weitere Hinweise fuer elegante Bot-Programmierung.... + * + * Das Verhalten ist per default abgeschaltet. Daher muss man es erst in bot_behave_init() aktivieren. + * Dort steht aber bereits eine auskommentierte Zeile dazu, von der man nur die zwei Kommentarzeichen wegnehmen muss. + * Achtung, da bot_simple2_behaviour() maximale Prioritaet hat, kommt es vor den anderen Regeln, wie dem Schutz vor Abgruenden, etc. zum Zuge + * Das sollte am Anfang nicht stoeren, spaeter sollte man jedoch die Prioritaet herabsetzen. + * + * bot_simple2_behaviour faehrt den Bot solange geradeaus, bis es dunkler als im Uebergabeparameter spezifiziert ist wird + * + * @param *data der Verhaltensdatensatz + */ +void bot_simple2_behaviour(Behaviour_t *data){ + #define STATE_SIMPLE2_INIT 0 + #define STATE_SIMPLE2_WORK 1 + #define STATE_SIMPLE2_DONE 2 + static uint8 state = 0; + + switch (state) { + case STATE_SIMPLE2_INIT: + // Nichts zu tun + state=STATE_SIMPLE2_WORK; + break; + case STATE_SIMPLE2_WORK: + // Fahre ganz schnell + speedWishLeft = BOT_SPEED_FAST; + speedWishRight = BOT_SPEED_FAST; + if (sensLDRL< simple2_light) // Beispielbedingung + // Wenn es dunkler als angegeben wird, dann haben wir unserd Ziel erreicht + state=STATE_SIMPLE2_DONE; + break; + + case STATE_SIMPLE2_DONE: /* Sind wir fertig, dann Kontrolle zurueck an Aufrufer */ + state=STATE_SIMPLE2_INIT; + return_from_behaviour(data); + break; + } +} + +/*! + * Rufe das Simple2-Verhalten auf und uebergebe light + * @param caller Der obligatorische Verhaltensdatensatz des Aufrufers + * @param light Uebergabeparameter + */ +void bot_simple2(Behaviour_t * caller, int16 light){ + simple2_light=light; + + // Zielwerte speichern + switch_to_behaviour(caller,bot_simple2_behaviour,OVERRIDE); +} +#endif diff --git a/source/ct-Bot/bot-logic/behaviour_solve_maze.c b/source/ct-Bot/bot-logic/behaviour_solve_maze.c new file mode 100644 index 0000000..5eb2fc4 --- /dev/null +++ b/source/ct-Bot/bot-logic/behaviour_solve_maze.c @@ -0,0 +1,660 @@ +/* + * 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. + * + */ + +/*! @file behaviour_solve_maze.c + * @brief Wandfolger durchs Labyrinth + * + * @author Benjamin Benz (bbe@heise.de) + * @date 03.11.06 +*/ + +#include "bot-logic/bot-logik.h" +#include <math.h> +#include <stdlib.h> +#include "log.h" + +#ifdef BEHAVIOUR_SOLVE_MAZE_AVAILABLE + +/* Parameter fuer das check_wall_behaviour() */ +int8 wall_detected; /*!< enthaelt True oder False, je nach Ergebnis des Verhaltens */ +int8 check_direction; /*!< enthaelt CHECK_WALL_LEFT oder CHECK_WALL_RIGHT */ +int16 wall_distance; /*!< enthaelt gemessene Entfernung */ + +/* Konstanten fuer check_wall_behaviour-Verhalten */ +#define CHECK_WALL_RIGHT 0 +#define CHECK_WALL_LEFT 1 + +/* Parameter fuer das measure_angle_behaviour() */ +int8 measure_direction; /*!< enthaelt MEASURE_RIGHT oder MEASURE_LEFT */ +int16 measure_distance; /*!< enthaelt maximale Messentfernung, enthaelt nach der Messung die Entfernung */ +int16 measured_angle; /*!< enthaelt gedrehten Winkel oder 0, falls nichts entdeckt */ +#ifdef MEASURE_MOUSE_AVAILABLE + int16 start_heading; /*!< Blickwinkel des Bots zu Beginn der Messung */ +#else + int16 startEncL; /*!< enthaelt Encoderstand zu Beginn der Messung */ + int16 startEncR; /*!< enthaelt Encoderstand zu Beginn der Messung */ +#endif +/* Konstanten fuer measure_angle_behaviour-Verhalten */ +#define MEASURE_LEFT 1 +#define MEASURE_RIGHT -1 + + +/*! + * Das Verhalten dreht sich um 45 Grad in die angegebene Richtung (0=rechts, 1=links) + * und prueft, ob auf dem Sensor auf der Seite der angegebenen Richtung eine Wand + * im Abstand von 12cm zu sehen ist. Wenn dem so ist, wird die Variable wall_present + * auf True gesetzt, sonst False */ +void bot_check_wall_behaviour(Behaviour_t *data) { + /* Konstantenfuer check_wall_behaviour-Verhalten */ + #define CORRECT_NEAR 0 + #define CORRECT_NONE 1 + #define CORRECT_FAR 2 + /* Zustaende fuer check_wall_behaviour-Verhalten */ + #define CHECK_WALL_TURN 0 + #define CHECK_WALL_SENS 1 + #define CHECK_WALL_TURN_BACK 2 + #define CHECK_WALL_FINISHED 3 + #define CHECK_WALL_CORRECT 4 + + static int8 checkState=CHECK_WALL_TURN; + /* wenn die Wand noch da ist aber aus dem Blickfeld rueckt, Entfernung und Winkel korrigieren */ + static int8 correctDistance=CORRECT_NONE; + /* letzte, gueltige Distanz fuer Abweichungsberechnung */ + static int16 lastDistance=0; + /* enthaelt anzahl der +/-5 identischen Messungen */ + static int8 measureCount=0; + /* letzter Messwert */ + static int16 lastSensor=0; + + int16 sensor; /* fuer temporaer benutzte Senorwerte */ + + switch(checkState) { + case CHECK_WALL_TURN: + /* haben wir links oder rechts eine Wand? */ + if (check_direction==CHECK_WALL_LEFT) { + checkState=CHECK_WALL_SENS; + bot_turn(data,45); + break; + } else { + checkState=CHECK_WALL_SENS; + bot_turn(data,-45); + break; + } + + case CHECK_WALL_SENS: + if (check_direction==CHECK_WALL_LEFT) { + sensor=sensDistL; + } else { + sensor=sensDistR; + } + /* dafuer sorgen, dass wir nur verlaessliche Werte haben + * dazu muss der wert dreimal nacheinander max. um +/- 5 + * unterschiedlich sein */ + if (measureCount==0) { + lastSensor=sensor; + measureCount++; + break; + } + if (sensor>=lastSensor-5 && sensor<=lastSensor+5 && measureCount<4) { + /* Messwert ist ok */ + measureCount++; + break; + } else if (measureCount<4) { + /* Messwert weicht zu doll ab -> von Neuem messen */ + measureCount=0; + break; + } + /* ok, wir hatten drei Messungen mit nahezu identischen Werten */ + + /* keine wand in eingestellter Maximalentfernung? */ + if (sensor>IGNORE_DISTANCE) { + correctDistance=CORRECT_NONE; /* dann auch keine Korrektur */ + lastDistance=0; /* auch kein Vergleichswert */ + wall_detected=False; /* keine Wand da */ + } else if (sensor<OPTIMAL_DISTANCE-ADJUST_DISTANCE) { + /* bot hat den falschen Abstand zur Wand, zu nah dran */ + wall_detected=True; + correctDistance=CORRECT_NEAR; + } else if (sensor>OPTIMAL_DISTANCE+ADJUST_DISTANCE) { + /* bot hat den falschen Abstand zur Wand, zu weit weg */ + wall_detected=True; + correctDistance=CORRECT_FAR; + } else { + /* perfekter Abstand */ + correctDistance=CORRECT_NONE; + wall_detected=True; + } + wall_distance=sensor; + /* Wenn Korrektur noetig, dann durchfuehren, sonst gleich zurueckdrehen */ + if (correctDistance==CORRECT_NONE) checkState=CHECK_WALL_TURN_BACK; + else checkState=CHECK_WALL_CORRECT; + break; + + + case CHECK_WALL_TURN_BACK: + checkState=CHECK_WALL_FINISHED; + int16 turnAngle=45; + + /* welcher Sensorwert wird gebraucht? */ + if (check_direction==CHECK_WALL_LEFT) { + sensor=sensDistL; + } else { + sensor=sensDistR; + } + /* wenn vorhanden, aus letztem Abstand und aktuellem Abstand + * Korrekturwinkel berechnen, falls vorheriger Abstand da. + * wird durch Abstand>IGNORE_DISTANCE zurueckgesetzt */ + if (lastDistance!=0) { + turnAngle=turnAngle+(lastDistance-wall_distance)/10; + } + if (sensor<IGNORE_DISTANCE) lastDistance=sensor; + if (check_direction==CHECK_WALL_LEFT) { + bot_turn(data,-turnAngle); + } else { + bot_turn(data,turnAngle); + } + break; + + case CHECK_WALL_FINISHED: + /* ok, check beendet -> Verhalten verlassen */ + checkState=CHECK_WALL_TURN; + return_from_behaviour(data); + break; + + case CHECK_WALL_CORRECT: + /* Abhaengig von der Seite, auf der die Wand ist, Entfernung korrigieren */ + if (check_direction==CHECK_WALL_LEFT) { + sensor=sensDistL; + } else { + sensor=sensDistR; + } + if (sensor-OPTIMAL_DISTANCE<0) { + speedWishLeft=-BOT_SPEED_SLOW; + speedWishRight=-BOT_SPEED_SLOW; + } else if (sensor-OPTIMAL_DISTANCE>0) { + speedWishLeft=BOT_SPEED_SLOW; + speedWishRight=BOT_SPEED_SLOW; + } + else { + checkState=CHECK_WALL_TURN_BACK; + speedWishLeft=BOT_SPEED_STOP; + speedWishRight=BOT_SPEED_STOP; + } + break; + + + default: + checkState=CHECK_WALL_TURN; + return_from_behaviour(data); + break; + } +} + + +/*! + * Das Verhalten dreht sich um 45 Grad in die angegebene Richtung (0=rechts, 1=links) + * und prueft, ob auf dem Sensor auf der Seite der angegebenen Richtung eine Wand + * im Abstand von 12-22cm zu sehen ist. Wenn dem so ist, wird die Variable wall_present + * auf True gesetzt, sonst False */ +void bot_check_wall(Behaviour_t *caller,int8 direction) { + check_direction=direction; + wall_detected=False; + switch_to_behaviour(caller, bot_check_wall_behaviour,NOOVERRIDE); +} + +#ifdef MEASURE_MOUSE_AVAILABLE + /*! + * Das Verhalten dreht den Bot in die angegebene Richtung bis ein Hindernis + * im Sichtbereich erscheint, das eine Entfernung bis max. zur angegebenen + * Distanz zum Bot hat. + */ + + void bot_measure_angle_behaviour(Behaviour_t *caller) { + /* Zustaende measure_angle_behaviour-Verhalten */ + #define MEASURE_TURN 0 + #define FOUND_OBSTACLE 1 + #define MEASUREMENT_DONE 2 + + static int8 measureState=MEASURE_TURN; + + /* enthaelt anzahl der +/-5 identischen Messungen */ + static int8 measureCount=0; + /* letzter Messwert */ + static int16 lastSensor=0; + + /* bereits gedrehten Winkel */ + int16 turned_angle=0; + if (measure_direction>0) { + if ((int16)heading_mou<start_heading) { + /* war ein ueberlauf */ + turned_angle=360-start_heading+(int16)heading_mou; + } else { + /* sonst normale differenz berechnen */ + turned_angle=(int16)heading_mou-start_heading; + } + } else { + if ((int16)heading_mou>start_heading) { + /* war ein ueberlauf */ + turned_angle=360-(int16)heading_mou+start_heading; + } else { + turned_angle=start_heading-(int16)heading_mou; + } + + } + + /* sensorwert abhaengig von der Drehrichtung abnehmen */ + int16 sensor=(measure_direction==MEASURE_LEFT)?sensDistL:sensDistR; + /* solange drehen, bis Hindernis innerhalb Messstrecke oder 360 Grad komplett */ + switch(measureState){ + case MEASURE_TURN: + /* nicht mehr als eine komplette Drehung machen! */ + if (turned_angle>=360) { + measure_direction=-measure_direction; + measureState=MEASUREMENT_DONE; + bot_turn(caller,measure_direction*turned_angle); + measured_angle=0; /* kein Hindernis gefunden */ + break; + } + /* Ist ein Objekt in Reichweite? */ + if (sensor<=measure_distance) { + speedWishLeft=BOT_SPEED_STOP; + speedWishRight=BOT_SPEED_STOP; + measureState=FOUND_OBSTACLE; + break; + } + /* Beginnen, zurueckzudrehen */ + speedWishLeft = (measure_direction > 0) ? -BOT_SPEED_FOLLOW : BOT_SPEED_FOLLOW; + speedWishRight = (measure_direction > 0) ? BOT_SPEED_FOLLOW : -BOT_SPEED_FOLLOW; + break; + + case FOUND_OBSTACLE: + /* dafuer sorgen, dass wir nur verlaessliche Werte haben + * dazu muss der wert dreimal nacheinander max. um +/- 5 + * unterschiedlich sein */ + if (measureCount==0) { + lastSensor=sensor; + measureCount++; + break; + } + if (sensor>=lastSensor-5 && sensor<=lastSensor+5 && measureCount<4) { + /* Messwert ist ok */ + measureCount++; + break; + } else if (measureCount<4) { + /* Messwert weicht zu doll ab -> von Neuem messen */ + measureCount=0; + break; + } + /* ok, wir hatten drei Messungen mit nahezu identischen Werten */ + measure_distance=sensor; + /* Hindernis gefunden, nun Bot wieder in Ausgangsstellung drehen */ + measure_direction=-measure_direction; + measured_angle=turned_angle; + measureState=MEASUREMENT_DONE; + bot_turn(caller,measure_direction*turned_angle); + break; + + case MEASUREMENT_DONE: + measureState=MEASURE_TURN; + measureCount=0; + return_from_behaviour(caller); + break; + } + } + + /*! + * Das Verhalten dreht den Bot in die angegebene Richtung bis ein Hindernis + * im Sichtbereich erscheint, das eine Entfernung bis max. zur angegebenen + * Distanz zum Bot hat. + */ + + void bot_measure_angle(Behaviour_t *caller, int8 direction, int16 distance) { + /* maximale Messentfernung und Richtung setzen */ + measure_direction=direction; + measure_distance=distance; + /* Heading zu Anfang des Verhaltens merken */ + start_heading=(int16)heading_mou; + switch_to_behaviour(caller, bot_measure_angle_behaviour,NOOVERRIDE); + } +#else +/*! + * Das Verhalten dreht den Bot in die angegebene Richtung bis ein Hindernis + * im Sichtbereich erscheint, das eine Entfernung bis max. zur angegebenen + * Distanz zum Bot hat. + */ + +void bot_measure_angle_behaviour(Behaviour_t *caller) { + /* Zustaende measure_angle_behaviour-Verhalten */ + #define MEASURE_TURN 0 + #define FOUND_OBSTACLE 1 + #define TURN_COMPLETED 2 + #define TURN_BACK 3 + #define CORRECT_ANGLE 4 + #define MEASUREMENT_DONE 5 + + static int8 measureState=MEASURE_TURN; + + /* bereits gedrehte Strecke errechnen */ + int16 turnedLeft=(measure_direction>0)?-(sensEncL-startEncL):(sensEncL-startEncL); + int16 turnedRight=(measure_direction>0)?(sensEncR-startEncR):-(sensEncR-startEncR); + int16 turnedSteps=(abs(turnedLeft)+abs(turnedRight))/2; + + /* sensorwert abhaengig von der Drehrichtung abnehmen */ + int16 sensor=(measure_direction==MEASURE_LEFT)?sensDistL:sensDistR; + /* solange drehen, bis Hindernis innerhalb Messstrecke oder 360� komplett */ + switch(measureState){ + case MEASURE_TURN: + /* nicht mehr als eine komplette Drehung machen! */ + if (turnedSteps>=ANGLE_CONSTANT) { + speedWishLeft=BOT_SPEED_STOP; + speedWishRight=BOT_SPEED_STOP; + measureState=TURN_COMPLETED; + measured_angle=0; /* kein Hindernis gefunden */ + break; + } + /* Ist ein Objekt in Reichweite? */ + if (sensor<=measure_distance) { + measure_distance=sensor; + speedWishLeft=BOT_SPEED_STOP; + speedWishRight=BOT_SPEED_STOP; + measureState=FOUND_OBSTACLE; + break; + } + /* Beginnen, zurueckzudrehen */ + speedWishLeft = (measure_direction > 0) ? -BOT_SPEED_SLOW : BOT_SPEED_SLOW; + speedWishRight = (measure_direction > 0) ? BOT_SPEED_SLOW : -BOT_SPEED_SLOW; + break; + + case FOUND_OBSTACLE: + /* Hindernis gefunden, nun Bot wieder in Ausgangsstellung drehen */ + measure_direction=-measure_direction; + measured_angle=(int16)((long)(turnedSteps*360)/ANGLE_CONSTANT); + measureState=TURN_BACK; + speedWishLeft = (measure_direction > 0) ? -BOT_SPEED_SLOW : BOT_SPEED_SLOW; + speedWishRight = (measure_direction > 0) ? BOT_SPEED_SLOW : -BOT_SPEED_SLOW; + break; + + case TURN_COMPLETED: + /* bot steht wieder in Ausgangsrichtung, Verhalten beenden */ + speedWishLeft=BOT_SPEED_STOP; + speedWishRight=BOT_SPEED_STOP; + measureState=CORRECT_ANGLE; + break; + + case TURN_BACK: + /* Bot in Ausgangsposition drehen */ + if ((turnedLeft+turnedRight)/2>=0) { + speedWishLeft=BOT_SPEED_STOP; + speedWishRight=BOT_SPEED_STOP; + measureState=TURN_COMPLETED; + break; + } + speedWishLeft = (measure_direction > 0) ? -BOT_SPEED_SLOW : BOT_SPEED_SLOW; + speedWishRight = (measure_direction > 0) ? BOT_SPEED_SLOW : -BOT_SPEED_SLOW; + break; + + case CORRECT_ANGLE: + /* Evtl. etwas zuruecksetzen, falls wir zu weit gefahren sind */ + if (turnedRight>0) { + /* rechts zu weit gefahren..langsam zurueck */ + speedWishRight = (measure_direction > 0) ? -BOT_SPEED_SLOW : BOT_SPEED_SLOW; + } else if (turnedRight<0) { + /* rechts noch nicht weit genug...langsam vor */ + speedWishRight = (measure_direction > 0) ? BOT_SPEED_SLOW : -BOT_SPEED_SLOW; + } else { + /* Endposition erreicht, rechtes Rad anhalten */ + speedWishRight = BOT_SPEED_STOP; + } + if (turnedLeft<0) { + /* links zu weit gefahren..langsam zurueck */ + speedWishLeft = (measure_direction > 0) ? -BOT_SPEED_SLOW : BOT_SPEED_SLOW; + } else if (turnedLeft>0) { + /* links noch nicht weit genug...langsam vor */ + speedWishLeft = (measure_direction > 0) ? BOT_SPEED_SLOW : -BOT_SPEED_SLOW; + } else { + /* Endposition erreicht, linkes Rad anhalten */ + speedWishLeft = BOT_SPEED_STOP; + } + if (speedWishLeft == BOT_SPEED_STOP && speedWishRight == BOT_SPEED_STOP) { + /* beide Raeder haben nun wirklich die Endposition erreicht, daher anhalten */ + measureState=MEASUREMENT_DONE; + } + break; + + case MEASUREMENT_DONE: + measureState=MEASURE_TURN; + return_from_behaviour(caller); + break; + } +} + +/*! + * Das Verhalten dreht den Bot in die angegebene Richtung bis ein Hindernis + * im Sichtbereich erscheint, das eine Entfernung bis max. zur angegebenen + * Distanz zum Bot hat. + */ + +void bot_measure_angle(Behaviour_t *caller, int8 direction, int16 distance) { + /* maximale Messentfernung und Richtung setzen */ + measure_direction=direction; + measure_distance=distance; + /* Encoderwerte zu Anfang des Verhaltens merken */ + startEncL=sensEncL; + startEncR=sensEncR; + switch_to_behaviour(caller, bot_measure_angle_behaviour,NOOVERRIDE); +} +#endif +/*! + * Das Verhalten findet seinen Weg durch ein Labyrinth, das nach gewissen Grundregeln gebaut ist + * in nicht immer optimaler Weise aber in jedem Fall. Es arbeitet nach dem Hoehlenforscher-Algorithmus. + * Einschraenkung: Objekte im Labyrinth, die Endlossschleifen verursachen koennen, z.b. ein einzeln + * stehender Pfeiler im Labyrinth um den der Bot dann immer wieder herum fahren wuerde. + */ +void bot_solve_maze_behaviour(Behaviour_t *data){ + /* Zustaende fuer das bot_solve_maze_behaviour-Verhalten */ + #define CHECK_FOR_STARTPAD 0 + #define CHECK_FOR_WALL_RIGHT 1 + #define CHECK_FOR_WALL_LEFT 2 + #define CHECK_WALL_PRESENT 3 + #define SOLVE_MAZE_LOOP 4 + #define SOLVE_TURN_WALL 5 + #define CHECK_CONDITION 6 + #define TURN_TO_BRANCH 7 + #define DETECTED_CROSS_BRANCH 8 + #define APPROACH_CORNER 9 + #define AVOID_ABYSS 10 + #define REACHED_GOAL 11 + static int8 mazeState=CHECK_FOR_STARTPAD; + static int8 followWall=-1; + static int8 checkedWalls=0; + + int16 distance; + double x; + switch(mazeState) { + case CHECK_FOR_STARTPAD: + /* Wo beginnen wir, nach einer Wand zu suchen? + * Abgrund- und Kollisions-Verhalten ausschalten */ + #ifdef BEHAVIOUR_AVOID_COL_AVAILABLE + deactivateBehaviour(bot_avoid_col_behaviour); + #endif + #ifdef BEHAVIOUR_AVOID_BORDER_AVAILABLE + deactivateBehaviour(bot_avoid_border_behaviour); + #endif + /* sieht nach, ob der Bot auf einem definierten Startpad steht und + * beginnt dann mit der Suche gleich an der richtigen Wand */ + /* Zuserst bei nach Startpad1 gucken */ + checkedWalls=0; + if ((sensLineL>=STARTPAD1-10 && sensLineL<=STARTPAD1+10) || + (sensLineR>=STARTPAD1-10 && sensLineR<=STARTPAD1+10)) { + mazeState=CHECK_FOR_WALL_LEFT; + break; + } + mazeState=CHECK_FOR_WALL_RIGHT; + break; + + case CHECK_FOR_WALL_RIGHT: + + mazeState=CHECK_WALL_PRESENT; + followWall=CHECK_WALL_RIGHT; + bot_check_wall(data,followWall); + break; + + case CHECK_FOR_WALL_LEFT: + + followWall=CHECK_WALL_LEFT; + bot_check_wall(data,followWall); + mazeState=CHECK_WALL_PRESENT; + break; + + + case CHECK_WALL_PRESENT: + /* wenn keine Wand gefunden aber links noch nicht nachgesehen, andere + * Richtung checken, sonst vorfahren */ + checkedWalls++; + if (wall_detected==False){ + /* Wand noch nicht entdeckt...haben wir schon beide gecheckt? */ + if (checkedWalls<2) { + if (followWall==CHECK_WALL_RIGHT) { + mazeState=CHECK_FOR_WALL_LEFT; + break; + } else { + mazeState=CHECK_FOR_WALL_RIGHT; + break; + } + } else { + /* keine wand? dann vorfahren und selbes prozedere nochmal */ + bot_drive_distance(data,0,BOT_SPEED_NORMAL,BOT_DIAMETER); + mazeState=CHECK_FOR_WALL_RIGHT; + checkedWalls=0; + break; + } + } + /* ok, wir haben unsere richtung im labyrinth gefunden jetzt dieser + * nur noch folgen bis Ziel, Abzweig oder Abgrund */ + mazeState=SOLVE_MAZE_LOOP; + break; + + case SOLVE_MAZE_LOOP: + /* Einen Schritt (=halbe BOT-Groesse) vorwaerts */ + mazeState=SOLVE_TURN_WALL; + bot_drive_distance(data,0,BOT_SPEED_NORMAL,BOT_DIAMETER); + break; + + case SOLVE_TURN_WALL: + /* Ziel erreicht? */ + if ((sensLineL>GROUND_GOAL-20 && sensLineL<GROUND_GOAL+20) || + (sensLineR>GROUND_GOAL-20 && sensLineR<GROUND_GOAL+20)) { + /* Bot hat Ziel erreicht...aus Freude einmal um die Achse drehen */ + bot_turn(data,360); + mazeState=REACHED_GOAL; + break; + } + /* checken, ob wand vor uns (Abstand 2.5*Bot-Durchmesser in mm) */ + distance=(sensDistL+sensDistR)/2; + if (distance<=25*BOT_DIAMETER) { // umrechnen 10*BOT_DIAMETER, da letzteres in cm angegeben wird + /* berechnete Entfernung zur Wand abzueglich optimale Distanz fahren */ + mazeState=DETECTED_CROSS_BRANCH; + bot_drive_distance(data,0,BOT_SPEED_NORMAL,(distance-OPTIMAL_DISTANCE)/10); + break; + } + /* Zur Wand drehen....ist die Wand noch da? */ + mazeState=CHECK_CONDITION; + bot_check_wall(data,followWall); + break; + + case CHECK_CONDITION: + /* Solange weiter, wie die Wand zu sehen ist */ + if (wall_detected==True) { + mazeState=SOLVE_MAZE_LOOP; + break; + } + /* messen, wo genau die Ecke ist */ + mazeState=APPROACH_CORNER; + if (followWall==CHECK_WALL_LEFT){ + bot_measure_angle(data,MEASURE_LEFT,300); + } else { + bot_measure_angle(data,MEASURE_RIGHT,300); + } + break; + + case TURN_TO_BRANCH: + /* nun in Richtung Abzweig drehen , dann mit Hauptschleife weiter*/ + mazeState=SOLVE_MAZE_LOOP; + if (followWall==CHECK_WALL_RIGHT) { + bot_turn(data,-90); + } else { + bot_turn(data,90); + } + break; + + case DETECTED_CROSS_BRANCH: + /* Bot faehrt auf eine Ecke zu, in Richtung Gang drehen */ + if (followWall==CHECK_WALL_LEFT) { + mazeState=SOLVE_MAZE_LOOP; + bot_turn(data,-90); + } else { + mazeState=SOLVE_MAZE_LOOP; + bot_turn(data,90); + } + break; + + case APPROACH_CORNER: + /* ok, nun strecke bis zur Kante berechnen */ + x=measure_distance*cos(measured_angle*M_PI/180)/10+BOT_DIAMETER*1.5; + mazeState=TURN_TO_BRANCH; + bot_drive_distance(data,0,BOT_SPEED_NORMAL,(int16)x); + break; + + case REACHED_GOAL: + mazeState=CHECK_WALL_RIGHT; + speedWishLeft=BOT_SPEED_STOP; + speedWishRight=BOT_SPEED_STOP; + return_from_behaviour(data); + break; + } +} + + +/*! + * Das Verhalten findet seinen Weg durch ein Labyrinth, das nach gewissen Grundregeln gebaut ist + * in nicht immer optimaler Weise aber in jedem Fall. Es arbeitet nach dem Hoehlenforscher-Algorithmus. + * Einschraenkung: Objekte im Labyrinth, die Endlossschleifen verursachen koennen, z.b. ein einzeln + * stehender Pfeiler im Labyrinth um den der Bot dann immer wieder herum fahren wuerde. + */ + +void bot_solve_maze(Behaviour_t *caller){ + LOG_DEBUG(("bot_solve_maze()")); + switch_to_behaviour(caller, bot_solve_maze_behaviour,NOOVERRIDE); +} + +/*! + * Initialisiert den Hoelenforscher + * @param prio_main Prioritaet des Hoehlenforschers (typ. 100) + * @param prio_helper Prioritaet der Hilfsfunktionen (typ. 42) + * @param active ACTIVE wenn der hoehlenforcher sofort starten soll, sonst INACTIVE + */ +void bot_solve_maze_init(int8 prio_main,int8 prio_helper, int8 active){ + // Verhalten, um ein Labyrinth nach der Hoehlenforscher-Methode loesen + insert_behaviour_to_list(&behaviour, new_behaviour(prio_main, bot_solve_maze_behaviour,active)); + + insert_behaviour_to_list(&behaviour, new_behaviour(prio_helper--, bot_measure_angle_behaviour,INACTIVE)); + insert_behaviour_to_list(&behaviour, new_behaviour(prio_helper, bot_check_wall_behaviour,INACTIVE)); + +} +#endif diff --git a/source/ct-Bot/bot-logic/behaviour_turn.c b/source/ct-Bot/bot-logic/behaviour_turn.c new file mode 100644 index 0000000..5b53c89 --- /dev/null +++ b/source/ct-Bot/bot-logic/behaviour_turn.c @@ -0,0 +1,361 @@ +/* + * 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. + * + */ + +/*! @file behaviour_turn.c + * @brief Drehe den Bot + * + * @author Benjamin Benz (bbe@heise.de) + * @date 03.11.06 +*/ + + +#include "bot-logic/bot-logik.h" + +#ifdef BEHAVIOUR_TURN_AVAILABLE +#ifdef MCU + #include <avr/eeprom.h> +#endif +#include <stdlib.h> +#include <math.h> + +/* Parameter fuer das bot_turn_behaviour() */ +#ifndef MEASURE_MOUSE_AVAILABLE +int16 turn_targetR; /*!< Zu drehender Winkel bzw. angepeilter Stand des Radencoders sensEncR */ +int16 turn_targetL; /*!< Zu drehender Winkel bzw. angepeilter Stand des Radencoders sensEncL */ +#else +int8 angle_correct=0; /*!< Drehabschnitt 0=0-15Grad, 1=16-45 Grad, 2= >45 Grad */ +int16 to_turn; /*!< Wieviel Grad sind noch zu drehen? */ + #ifdef MCU + uint8 __attribute__ ((section (".eeprom"))) err15=1; /*!< Fehler bei Drehungen unter 15 Grad */ + uint8 __attribute__ ((section (".eeprom"))) err45=2; /*!< Fehler bei Drehungen zwischen 15 und 45 Grad */ + uint8 __attribute__ ((section (".eeprom"))) err_big=4; /*!< Fehler bei groesseren Drehungen */ + #else + uint8 err15=0; + uint8 err45=0; + uint8 err_big=0; + #endif +#endif +int8 turn_direction; /*!< Richtung der Drehung */ + + +/* Hilfsfunktion zur Berechnung einer Winkeldifferenz */ +inline int16 calc_turned_angle(int8 direction, int16 angle1, int16 angle2) { + int16 diff_angle=0; + + if (direction>0){ + // Drehung in mathematisch positivem Sinn + if (angle1>angle2) { + // Es gab einen Ueberlauf + diff_angle=360-angle1+angle2; + } else { + diff_angle=angle2-angle1; + } + } else { + // Drehung in mathematisch negativem Sinn + if (angle1<angle2) { + // Es gab einen Ueberlauf + diff_angle=angle1+360-angle2; + } else { + diff_angle=angle1-angle2; + } + } + return diff_angle; +} + +#ifdef MEASURE_MOUSE_AVAILABLE + /*! + * Das Verhalten laesst den Bot eine Punktdrehung durchfuehren. ++ * Drehen findet in drei Schritten statt. Die Drehung wird dabei ++ * bei Winkeln > 15 Grad zunaechst mit hoeherer Geschwindigkeit ausgefuehrt. Bei kleineren ++ * Winkeln oder wenn nur noch 15 Grad zu drehen sind, nur noch mit geringer Geschwindigkeit + * @param *data der Verhaltensdatensatz + * @see bot_turn() + */ +void bot_turn_behaviour(Behaviour_t *data){ + // Zustaende fuer das bot_turn_behaviour-Verhalten + #define NORMAL_TURN 0 + #define STOP_TURN 1 + #define FULL_STOP 2 + static int8 turnState=NORMAL_TURN; + static int16 old_heading=-1; + static int16 head_count=0; + uint8 e15; + uint8 e45; + uint8 ebig; + + // seit dem letzten mal gedrehte Grad + int16 turned=0; + // aktueller Winkel als int16 + int16 akt_heading=(int16)heading_mou; + + // erster Aufruf? -> alter Winkel==neuer Winkel + if (old_heading==-1) old_heading=akt_heading; + + // berechnen, wieviel Grad seit dem letzten Aufruf gedreht wurden + turned=calc_turned_angle(turn_direction,old_heading,akt_heading); + if (turned > 300) turned -= 360; // hier ging etwas schief + + // aktueller Winkel wird alter Winkel + old_heading=akt_heading; + // aktuelle Drehung von zu drehendem Winkel abziehen + to_turn-=turned; + + switch(turnState) { + case NORMAL_TURN: + // Solange drehen, bis Drehwinkel erreicht ist + // oder gar zu weit gedreht wurde + if (to_turn<1) { + /* Nachlauf abwarten */ + speedWishLeft=BOT_SPEED_STOP; + speedWishRight=BOT_SPEED_STOP; + turnState=STOP_TURN; + break; + } + speedWishLeft = (turn_direction > 0) ? -BOT_SPEED_NORMAL : BOT_SPEED_NORMAL; speedWishLeft = (turn_direction > 0) ? -BOT_SPEED_SLOW : BOT_SPEED_SLOW; + speedWishRight = (turn_direction > 0) ? BOT_SPEED_NORMAL : -BOT_SPEED_NORMAL; speedWishRight = (turn_direction > 0) ? BOT_SPEED_SLOW : -BOT_SPEED_SLOW; + break; + + case STOP_TURN: + // Abwarten, bis Nachlauf beendet + if (akt_heading!=old_heading){ + head_count=0; + speedWishLeft=BOT_SPEED_STOP; + speedWishRight=BOT_SPEED_STOP; + break; + } + if (head_count<10) { + head_count++; + speedWishLeft=BOT_SPEED_STOP; + speedWishRight=BOT_SPEED_STOP; + break; + } + #ifdef MCU + e15=eeprom_read_byte(&err15); + e45=eeprom_read_byte(&err45); + ebig=eeprom_read_byte(&err_big); + #else + e15=err15; + e45=err45; + ebig=err_big; + #endif + + // Neue Abweichung mit alter vergleichen und ggfs neu bestimmen + + switch(angle_correct) { + case 0: + if (abs(to_turn)-e15>1) { + e15=(int8)(abs(to_turn)+e15)/2; + #ifdef MCU + eeprom_write_byte(&err15,e15); + #else + err15=e15; + #endif + } + break; + + case 1: + if (abs(to_turn)-e45>1) { + e45=(int8)(abs(to_turn)+e45)/2; + #ifdef MCU + eeprom_write_byte(&err45,e45); + #else + err45=e45; + #endif + } + break; + + case 2: + if (abs(to_turn)-ebig>1) { + ebig=(int8)(abs(to_turn)+ebig)/2; + #ifdef MCU + eeprom_write_byte(&err_big,ebig); + #else + err_big=ebig; + #endif + } + break; + } + // ok, verhalten beenden + speedWishLeft=BOT_SPEED_STOP; + speedWishRight=BOT_SPEED_STOP; + turnState=NORMAL_TURN; + old_heading=-1; + return_from_behaviour(data); + break; + } +} + +void bot_turn(Behaviour_t *caller, int16 degrees) +{ + // Richtungsgerechte Umrechnung in den Zielwinkel + if(degrees < 0) turn_direction = -1; + else turn_direction = 1; + to_turn=abs(degrees); + #ifdef MCU + if (eeprom_read_byte(&err15)==255 && eeprom_read_byte(&err45)==255 && eeprom_read_byte(&err_big)==255) { + eeprom_write_byte(&err15,1); + eeprom_write_byte(&err45,2); + eeprom_write_byte(&err_big,4); + } + if (to_turn>45) { + to_turn-=eeprom_read_byte(&err_big); + angle_correct=2; + } else if (to_turn<=45 && to_turn>15) { + to_turn-=eeprom_read_byte(&err45); + angle_correct=1; + } else { + to_turn-=eeprom_read_byte(&err15); + angle_correct=0; + } + #else + if (to_turn>45) { + to_turn-=err_big; + angle_correct=2; + } else if (to_turn<=45 && to_turn>15) { + to_turn-=err45; + angle_correct=1; + } else { + to_turn-=err15; + angle_correct=0; + } + #endif + switch_to_behaviour(caller, bot_turn_behaviour,NOOVERRIDE); + } +#else +/*! + * Das Verhalten laesst den Bot eine Punktdrehung durchfuehren. + * @see bot_turn() + * */ +void bot_turn_behaviour(Behaviour_t* data){ + /* Drehen findet in vier Schritten statt. Die Drehung wird dabei + * bei Winkeln > 90 Grad zunaechst mit maximaler Geschwindigkeit ausgefuehrt. Bei kleineren + * Winkeln oder wenn nur noch 90 Grad zu drehen sind, nur noch mit normaler Geschwindigkeit + */ + /* Zustaende fuer das bot_turn_behaviour-Verhalten */ + #define NORMAL_TURN 0 + #define SHORT_REVERSE 1 + #define CORRECT_POSITION 2 + #define FULL_STOP 3 + static int8 turnState=NORMAL_TURN; + /* zu drehende Schritte in die korrekte Drehrichtung korrigieren */ + int16 to_turnR = turn_direction*(turn_targetR - sensEncR); + int16 to_turnL = -turn_direction*(turn_targetL - sensEncL); + + switch(turnState) { + case NORMAL_TURN: + /* Solange drehen, bis beide Encoder nur noch zwei oder weniger Schritte zu fahren haben */ + + if (to_turnL <= 2 && to_turnR<=2){ + /* nur noch 2 Schritte oder weniger, abbremsen einleiten */ + turnState=SHORT_REVERSE; + break; + } + + /* Bis 90 Grad kann mit maximaler Geschwindigkeit gefahren werden, danach auf Normal reduzieren */ + /* Geschwindigkeit fuer beide Raeder getrennt ermitteln */ + if(abs(to_turnL) < ANGLE_CONSTANT*0.25) { + speedWishLeft = (turn_direction > 0) ? -BOT_SPEED_MEDIUM : BOT_SPEED_MEDIUM; + } else { + speedWishLeft = (turn_direction > 0) ? -BOT_SPEED_NORMAL : BOT_SPEED_NORMAL; + } + + if(abs(to_turnR) < ANGLE_CONSTANT*0.25) { + speedWishRight = (turn_direction > 0) ? BOT_SPEED_MEDIUM : -BOT_SPEED_MEDIUM; + } else { + speedWishRight = (turn_direction > 0) ? BOT_SPEED_NORMAL : -BOT_SPEED_NORMAL; + } + + break; + + case SHORT_REVERSE: + /* Ganz kurz durch umpolen anbremsen */ + speedWishLeft = (turn_direction > 0) ? BOT_SPEED_SLOW : -BOT_SPEED_SLOW; + speedWishRight = (turn_direction > 0) ? -BOT_SPEED_SLOW : BOT_SPEED_SLOW; + turnState=CORRECT_POSITION; + break; + + case CORRECT_POSITION: + /* Evtl. etwas zuruecksetzen, falls wir zu weit gefahren sind */ + if (to_turnR<0) { + /* rechts zu weit gefahren..langsam zurueck */ + speedWishRight = (turn_direction > 0) ? -BOT_SPEED_SLOW : BOT_SPEED_SLOW; + } else if (to_turnR>0) { + /* rechts noch nicht weit genug...langsam vor */ + speedWishRight = (turn_direction > 0) ? BOT_SPEED_SLOW : -BOT_SPEED_SLOW; + } else { + /* Endposition erreicht, rechtes Rad anhalten */ + speedWishRight = BOT_SPEED_STOP; + } + + if (to_turnL<0) { + /* links zu weit gefahren..langsam zurueck */ + speedWishLeft = (turn_direction > 0) ? BOT_SPEED_SLOW : -BOT_SPEED_SLOW; + } else if (to_turnL>0) { + /* links noch nicht weit genug...langsam vor */ + speedWishLeft = (turn_direction > 0) ? -BOT_SPEED_SLOW : BOT_SPEED_SLOW; + } else { + /* Endposition erreicht, linkes Rad anhalten */ + speedWishLeft = BOT_SPEED_STOP; + } + + if (speedWishLeft == BOT_SPEED_STOP && speedWishRight == BOT_SPEED_STOP) { + /* beide Raeder haben nun wirklich die Endposition erreicht, daher anhalten */ + turnState=FULL_STOP; + } + break; + + + default: + /* ist gleichzeitig FULL_STOP, da gleiche Aktion + * Stoppen, State zuruecksetzen und Verhalten beenden */ + speedWishLeft = BOT_SPEED_STOP; + speedWishRight = BOT_SPEED_STOP; + turnState=NORMAL_TURN; + return_from_behaviour(data); + break; + } +} + +/*! + * Dreht den Bot im mathematisch positiven Sinn. + * @param degrees Grad, um die der Bot gedreht wird. Negative Zahlen drehen im (mathematisch negativen) Uhrzeigersinn. + * Die Aufloesung betraegt rund 3 Grad + */ +void bot_turn(Behaviour_t* caller,int16 degrees){ + /* Umrechnung von Grad in Encoder-Markierungen. + * Hinweis: Eigentlich muessten der Umfang von Bot und Rad verwendet werden. Die Rechnung wird + * allerdings viel einfacher, wenn man Pi auskuerzt. + * Ist degrees negativ, ist die Drehung negativ und der rechte Encoder muss kleiner werden. + */ + + if(degrees < 0) turn_direction = -1; + else turn_direction = 1; + /* Anzahl zu fahrender Encoderschritte berechnen */ + turn_targetR=(degrees*ANGLE_CONSTANT)/360; + /* linkes Rad dreht entgegengesetzt, daher negativer Wert */ + turn_targetL=-turn_targetR; + + /* aktuellen Sensorwert zu zu drehenden Encoderschritten addieren */ + turn_targetR+=sensEncR; + turn_targetL+=sensEncL; + switch_to_behaviour(caller, bot_turn_behaviour,OVERRIDE); +} +#endif +#endif + diff --git a/source/ct-Bot/bot-logic/bot-logik.c b/source/ct-Bot/bot-logic/bot-logik.c new file mode 100644 index 0000000..3274375 --- /dev/null +++ b/source/ct-Bot/bot-logic/bot-logik.c @@ -0,0 +1,500 @@ +/* + * 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. + * + */ + +/*! @file bot-logik.c + * @brief High-Level Routinen fuer die Steuerung des c't-Bots. + * Diese Datei sollte der Einstiegspunkt fuer eigene Experimente sein, + * den Roboter zu steuern. + * + * bot_behave() arbeitet eine Liste von Verhalten ab. + * Jedes Verhalten kann entweder absolute Werte setzen, dann kommen niedrigerpriorisierte nicht mehr dran. + * Alternativ dazu kann es Modifikatoren aufstellen, die bei niedriger priorisierten angewendet werden. + * bot_behave_init() baut diese Liste auf. + * Jede Verhaltensfunktion bekommt einen Verhaltensdatensatz uebergeben, in den Sie ihre Daten eintraegt + * + * @author Benjamin Benz (bbe@heise.de) + * @author Christoph Grimmer (c.grimmer@futurio.de) + * @date 01.12.05 +*/ + + +#include "bot-logic/bot-logik.h" + +#ifdef BEHAVIOUR_AVAILABLE + +#include "display.h" +#include "rc5.h" + + + +#include <stdlib.h> + +int16 speedWishLeft; /*!< Puffervariablen fuer die Verhaltensfunktionen absolut Geschwindigkeit links*/ +int16 speedWishRight; /*!< Puffervariablen fuer die Verhaltensfunktionen absolut Geschwindigkeit rechts*/ + +float faktorWishLeft; /*!< Puffervariablen fuer die Verhaltensfunktionen Modifikationsfaktor links*/ +float faktorWishRight; /*!< Puffervariablen fuer die Verhaltensfunktionen Modifikationsfaktor rechts */ + +int16 target_speed_l=BOT_SPEED_STOP; /*!< Sollgeschwindigkeit linker Motor - darum kuemmert sich bot_base()*/ +int16 target_speed_r=BOT_SPEED_STOP; /*!< Sollgeschwindigkeit rechter Motor - darum kuemmert sich bot_base() */ + + +/*! Liste mit allen Verhalten */ +Behaviour_t *behaviour = NULL; + +/*! + * Das einfachste Grundverhalten + * @param *data der Verhaltensdatensatz + */ +void bot_base_behaviour(Behaviour_t *data){ + speedWishLeft=target_speed_l; + speedWishRight=target_speed_r; +} + +/*! + * Initialisert das ganze Verhalten + */ +void bot_behave_init(void){ + #ifdef BEHAVIOUR_REMOTECALL_AVAILABLE + // Dieses Verhalten kann andere Starten + insert_behaviour_to_list(&behaviour, new_behaviour(254, bot_remotecall_behaviour,INACTIVE)); + #endif + + #ifdef BEHAVIOUR_SERVO_AVAILABLE + insert_behaviour_to_list(&behaviour, new_behaviour(253, bot_servo_behaviour,INACTIVE)); + #endif + + // Demo-Verhalten, ganz einfach, inaktiv + // Achtung, im Moment hat es eine hoehere Prioritaet als die Gefahrenerkenner!!! + #ifdef BEHAVIOUR_SIMPLE_AVAILABLE + insert_behaviour_to_list(&behaviour, new_behaviour(252, bot_simple_behaviour,INACTIVE)); + insert_behaviour_to_list(&behaviour, new_behaviour(251, bot_simple2_behaviour,INACTIVE)); + #endif + + + // Hoechste Prioritate haben die Notfall Verhalten + + // Verhalten zum Schutz des Bots, hohe Prioritaet, Aktiv + #ifdef BEHAVIOUR_AVOID_BORDER_AVAILABLE + insert_behaviour_to_list(&behaviour, new_behaviour(250, bot_avoid_border_behaviour,ACTIVE)); + #endif + #ifdef BEHAVIOUR_AVOID_COL_AVAILABLE + insert_behaviour_to_list(&behaviour, new_behaviour(249, bot_avoid_col_behaviour,ACTIVE)); + #endif + + #ifdef BEHAVIOUR_SCAN_AVAILABLE + // Verhalten, das die Umgebung des Bots on-the fly beim fahren scannt + insert_behaviour_to_list(&behaviour, new_behaviour(155, bot_scan_onthefly_behaviour,ACTIVE)); + + // Verhalten, das einmal die Umgebung des Bots scannt + insert_behaviour_to_list(&behaviour, new_behaviour(152, bot_scan_behaviour,INACTIVE)); + #endif + + // Alle Hilfsroutinen sind relativ wichtig, da sie auch von den Notverhalten her genutzt werden + // Hilfsverhalten, die Befehle von Boten-Funktionen ausfuehren, erst inaktiv, werden von Boten aktiviert + #ifdef BEHAVIOUR_TURN_AVAILABLE + insert_behaviour_to_list(&behaviour, new_behaviour(150, bot_turn_behaviour,INACTIVE)); + #endif + #ifdef BEHAVIOUR_DRIVE_DISTANCE_AVAILABLE + insert_behaviour_to_list(&behaviour, new_behaviour(149, bot_drive_distance_behaviour,INACTIVE)); + #endif + #ifdef BEHAVIOUR_GOTO_AVAILABLE + insert_behaviour_to_list(&behaviour, new_behaviour(148, bot_goto_behaviour,INACTIVE)); + #endif + + // Hilfsverhalten zum Anfahren von Positionen + #ifdef BEHAVIOUR_GOTOXY_AVAILABLE + insert_behaviour_to_list(&behaviour, new_behaviour(147, bot_gotoxy_behaviour,INACTIVE)); + #endif + + + #ifdef BEHAVIOUR_CATCH_PILLAR_AVAILABLE + insert_behaviour_to_list(&behaviour, new_behaviour(44, bot_catch_pillar_behaviour,INACTIVE)); + #endif + + + #ifdef BEHAVIOUR_OLYMPIC_AVAILABLE + bot_olympic_init(52,100,INACTIVE); + #endif + + #ifdef BEHAVIOUR_FOLLOW_LINE_AVAILABLE + // Verhalten um einer Linie zu folgen + insert_behaviour_to_list(&behaviour, new_behaviour(70, bot_follow_line_behaviour, ACTIVE)); + #endif + + #ifdef BEHAVIOUR_SOLVE_MAZE_AVAILABLE + bot_solve_maze_init(100,43,INACTIVE); + #endif + + #ifdef BEHAVIOUR_DRIVE_SQUARE_AVAILABLE + // Demo-Verhalten, etwas komplexer, inaktiv + insert_behaviour_to_list(&behaviour, new_behaviour(51, bot_drive_square_behaviour,INACTIVE)); + #endif + + + + // Grundverhalten, setzt aeltere FB-Befehle um, aktiv + insert_behaviour_to_list(&behaviour, new_behaviour(2, bot_base_behaviour, ACTIVE)); + + // Um das Simple-Behaviour zu nutzen, die Kommentarzeichen vor der folgenden Zeile entfernen!!! + // activateBehaviour(bot_simple_behaviour); + // activateBehaviour(bot_simple2_behaviour); + + #ifdef PC + #ifdef DISPLAY_AVAILABLE + /* Anzeigen der geladenen Verhalten */ + Behaviour_t *ptr = behaviour; + + display_cursor(5,1); + display_printf("Verhaltensstack:\n"); + while(ptr != NULL) { + display_printf("Prioritaet: %d.\n", ptr->priority); + ptr = ptr->next; + } + #endif + #endif +} + + +/*! + * Aktiviert eine Regel mit gegebener Funktion + * @param function Die Funktion, die das Verhalten realisiert. + */ +void activateBehaviour(BehaviourFunc 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; + } + } +} + + +/*! + * Deaktiviert eine Regel mit gegebener Funktion + * @param function Die Funktion, die das Verhalten realisiert. + */ +void deactivateBehaviour(BehaviourFunc 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 = INACTIVE; + break; + } + } +} + +/*! + * Ruft ein anderes Verhalten auf und merkt sich den Ruecksprung + * return_from_behaviour() kehrt dann spaeter wieder zum aufrufenden Verhalten zurueck + * @param from aufrufendes Verhalten + * @param to aufgerufenes Verhalten + * @param override Hier sind zwei Werte Moeglich: + * 1. OVERRIDE : Das Zielverhalten to wird aktiviert, auch wenn es noch aktiv ist. + * Das Verhalten, das es zuletzt aufgerufen hat wird dadurch automatisch + * wieder aktiv und muss selbst sein eigenes Feld subResult auswerten, um zu pruefen, ob das + * gewuenschte Ziel erreicht wurde, oder vorher ein Abbruch stattgefunden hat. + * 2. NOOVERRIDE : Das Zielverhalten wird nur aktiviert, wenn es gerade nichts zu tun hat. + * In diesem Fall kann der Aufrufer aus seinem eigenen subResult auslesen, + * ob seibem Wunsch Folge geleistet wurde. + */ +void switch_to_behaviour(Behaviour_t * from, void *to, uint8 override ){ + 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 == to) { + break; // Abbruch der Schleife, job zeigt nun auf die Datenstruktur des Zielverhaltens + } + } + + if (job->caller){ // Ist das auzurufende Verhalten noch beschaeftigt? + if (override==NOOVERRIDE){ // nicht ueberschreiben, sofortige Rueckkehr + if (from) + from->subResult=SUBFAIL; + return; + } + // Wir wollen also ueberschreiben, aber nett zum alten Aufrufer sein und ihn darueber benachrichtigen + job->caller->active=ACTIVE; // alten Aufrufer reaktivieren + job->caller->subResult=SUBFAIL; // er bekam aber nicht das gewuenschte Resultat + } + + if (from) { + // laufendes Verhalten abschalten + from->active=INACTIVE; + from->subResult=SUBRUNNING; + } + + // neues Verhalten aktivieren + job->active=ACTIVE; + // Aufrufer sichern + job->caller = from; +} + +/*! + * Kehrt zum aufrufenden Verhalten zurueck + * @param running laufendes Verhalten + */ +void return_from_behaviour(Behaviour_t * data){ + data->active=INACTIVE; // Unterverhalten deaktivieren + if (data->caller){ + data->caller->active=ACTIVE; // aufrufendes Verhalten aktivieren + data->caller->subResult=SUBSUCCESS; // Unterverhalten war erfolgreich + } + data->caller=NULL; // Job erledigt, Verweis loeschen +} + +/*! + * Deaktiviert alle Verhalten bis auf Grundverhalten. Bei Verhaltensauswahl werden die Aktivitaeten vorher + * in die Verhaltens-Auswahlvariable gesichert. + */ +void deactivateAllBehaviours(void){ + Behaviour_t *job; // Zeiger auf ein Verhalten + + #ifdef DISPLAY_BEHAVIOUR_AVAILABLE + // bei Verhaltensanzeige in Aktivitaets-Auswahl-Variable sichern + #ifndef DISPLAY_DYNAMIC_BEHAVIOUR_AVAILABLE + // bei Verhaltensanzeige in Aktivitaets-Auswahl-Variable sichern + // nicht bei dynamischer Anzeige und Selektion + set_behaviours_equal(); + #endif + #endif + + // Einmal durch die Liste gehen und (fast) alle deaktivieren, Grundverhalten nicht + for (job = behaviour; job; job = job->next) { + if ((job->priority >= PRIO_VISIBLE_MIN) &&(job->priority <= PRIO_VISIBLE_MAX)) { + // Verhalten deaktivieren + job->active = INACTIVE; + } + } +} + +/*! + * 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... + (Achtung: 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); + } + } +} + +/*! + * Erzeugt ein neues Verhalten + * @param priority Die Prioritaet + * @param *work Den Namen der Funktion, die sich drum kuemmert + */ +Behaviour_t *new_behaviour(uint8 priority, void (*work) (struct _Behaviour_t *data), int8 active){ + Behaviour_t *newbehaviour = (Behaviour_t *) malloc(sizeof(Behaviour_t)); + + if (newbehaviour == NULL) + return NULL; + + newbehaviour->priority = priority; + newbehaviour->active=active; + newbehaviour->next= NULL; + newbehaviour->work=work; + newbehaviour->caller=NULL; + newbehaviour->subResult=SUBSUCCESS; + return newbehaviour; +} + +/*! + * Fuegt ein Verhalten der Verhaltenliste anhand der Prioritaet ein. + * @param list Die Speicherstelle an der die globale Verhaltensliste anfaengt + * @param behave Einzufuegendes Verhalten + */ +void insert_behaviour_to_list(Behaviour_t **list, Behaviour_t *behave){ + Behaviour_t *ptr = *list; + Behaviour_t *temp = NULL; + + /* Kein Eintrag dabei? */ + if (behave == NULL) + return; + + /* Erster Eintrag in der Liste? */ + if (ptr == NULL){ + ptr = behave; + *list = ptr; + } else { + /* Gleich mit erstem Eintrag tauschen? */ + if (ptr->priority < behave->priority) { + behave->next = ptr; + ptr = behave; + *list = ptr; + } else { + /* Mit dem naechsten Eintrag vergleichen */ + while(NULL != ptr->next) { + if (ptr->next->priority < behave->priority) + break; + + /* Naechster Eintrag */ + ptr = ptr->next; + } + + temp = ptr->next; + ptr->next = behave; + behave->next = temp; + } + } +} + + + +#ifdef DISPLAY_BEHAVIOUR_AVAILABLE + +/*! + * ermittelt ob noch eine weitere Verhaltensseite existiert + */ + int8 another_behaviour_page(void) { + int16 max_behaviours ; + Behaviour_t *ptr ; + + /* dazu muss ich auch gueltige Screenseite sein */ + #ifdef DISPLAY_SCREENS_AVAILABLE + if (display_screen != 2) + return 0; + #endif + + ptr = behaviour; + max_behaviours = 0; + +// zuerst alle Verhalten ermitteln ausser Grundverhalten + while(ptr != NULL) { + if ((ptr->priority >= PRIO_VISIBLE_MIN) &&(ptr->priority <= PRIO_VISIBLE_MAX)) + max_behaviours++; + + ptr = ptr->next; + } + + return (behaviour_page * 6) < max_behaviours; +} + + +/*! + * toggled ein Verhalten der Verhaltensliste an Position pos, + * die Aenderung erfolgt nur auf die Puffervariable + * @param pos Listenposition, entspricht der Taste 1-6 der gewaehlten Verhaltensseite + */ +void toggleNewBehaviourPos(int8 pos){ + Behaviour_t *job; // Zeiger auf ein Verhalten + int8 i; + + // nur aendern, wenn ich richtige Screenseite bin + if (display_screen != 2) + return ; + + // richtigen Index je nach Seite ermitteln + pos = (behaviour_page - 1) * 6 + pos; + i = 0; + + // durch die Liste gehen, bis wir den gewuenschten Index erreicht haben + for (job = behaviour; job; job = job->next) { + if ((job->priority >= PRIO_VISIBLE_MIN) &&(job->priority <= PRIO_VISIBLE_MAX)) { + i++; + if (i == pos) { + // bei dynamischer Wahl wird direkt die Zustandsvariable geaendert + #ifdef DISPLAY_DYNAMIC_BEHAVIOUR_AVAILABLE + job->active = !job->active; + #else + job->active_new = !job->active_new; + #endif + + break; + } + } + } +} + +#ifndef DISPLAY_DYNAMIC_BEHAVIOUR_AVAILABLE +/*! + * Startschuss, die gewaehlten neuen Verhaltensaktivitaeten werden in die + * Verhaltensliste geschrieben und die Verhalten damit scharf geschaltet + */ +void set_behaviours_active_to_new(void) { + + Behaviour_t *job; + for (job = behaviour; job; job = job->next) { + + if ((job->priority >= PRIO_VISIBLE_MIN) &&(job->priority <= PRIO_VISIBLE_MAX)) + job->active = job->active_new; + + } + +} + +/*! + * Die Aktivitaeten der Verhalten werden in die Puffervariable geschrieben, + * welche zur Anzeige und Auswahl verwendet wird + */ +void set_behaviours_equal(void) { + Behaviour_t *job; + for (job = behaviour; job; job = job->next) { + + if ((job->priority >= PRIO_VISIBLE_MIN) &&(job->priority <= PRIO_VISIBLE_MAX)) + job->active_new = job->active; + + } +} +#endif +#endif +#endif |