/* * 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 motor.c * @brief High-Level-Routinen fuer die Motorsteuerung des c't-Bot * @author Benjamin Benz (bbe@heise.de) * @date 15.01.05 */ #include #include "global.h" #include "ct-Bot.h" #include "motor.h" #include "bot-local.h" #include "motor-low.h" #include "timer.h" #include "sensor.h" #include "display.h" int16 speed_l=0; /*!< Geschwindigkeit linker Motor */ int16 speed_r=0; /*!< Geschwindigkeit rechter Motor */ direction_t direction; /*!< Drehrichtung der Motoren */ #define PWMMAX 255 /*!< Maximaler PWM-Wert */ #define PWMMIN 0 /*!< Minimaler PWM-Wert */ #ifdef SPEED_CONTROL_AVAILABLE int8 encoderTargetRateL=0; int8 encoderTargetRateR=0; /*! * @brief Drehzahlregelung fuer die Motoren des c't-Bots * @author Benjamin Benz (bbe@heise.de * @date 01.05.06 * Getrennte Drehzahlregelung fuer linken und rechten Motor sorgt fuer konstante Drehzahl und somit annaehernd * fuer Geradeauslauf * Feintuning von Kp, Ki, Kd verbessert die Genauigkeit und Schnelligkeit der Regelung * Querkopplung der Motoren verbessert Gleichlauf, beispielsweise x*(sensEncL - sensEncR) * in jeden Regler einbauen */ void speed_control(void){ int8 Kp=0; int8 Ki=0; int16 StellwertL=motor_left; /*!< Stellwert links*/ int16 StellwertR=motor_right; /*!< Stellwert rechts*/ static int16 lastEncoderL=0; /*!< vorhergehender Wert von sensEncL */ static int8 lastErrL=0; /*!< letzter Drehzahlfehler links */ static int8 last2ErrL=0; /*!< vorletzter Drehzahlfehler links */ static int16 lastEncoderR=0; /*!< vorhergehender Wert von sensEncR */ static int8 lastErrR=0; /*!< letzter Drehzahlfehler rechts */ static int8 last2ErrR=0; /*!< vorletzter Drehzahlfehler rechts */ int16 err=0; // aktuelle Abweichung vom Soll-Wert int16 encoderRate=0; // IST-Wert [Encoder-ticks/Aufruf] // Wir arbeiten mit verschiedenen PID-Parametern fuer verschiedene Geschwindigkeitsabschnitte if (encoderTargetRateL <= PID_LOW_RATE){ Kp=PID_LOW_Kp; Ki=PID_LOW_Ki; } else { if (encoderTargetRateL >= PID_HIGH_RATE) { Kp=PID_HIGH_Kp; Ki=PID_HIGH_Ki; } else { Kp=(PID_HIGH_Kp+PID_LOW_Kp)/2; Ki=(PID_HIGH_Ki+PID_LOW_Ki)/2; } } //Regler links if (encoderTargetRateL == 0){ StellwertL=0; err=0; lastErrL = 0; } else { encoderRate = sensEncL-lastEncoderL; // aktuelle Ist-Wert berechnen [Encoder-ticks/aufruf] lastEncoderL = sensEncL; // Anzahl der Encoderpulse merken fuer naechsten Aufruf merken err = encoderTargetRateL - encoderRate; // Regelabweichung links // Stellwert Berechnen StellwertL += (Kp * (err - lastErrL)) / 10; // P-Beitrag StellwertL += (Ki * (err + lastErrL)/2 ) /10; // I-Beitrag // StellwertL += Kd * (errL - 2 * lastErrL + last2ErrL); // D-Beitrag //berechneten Stellwert auf zulaessige Groesse begrenzen if (StellwertL > PWMMAX) StellwertL = PWMMAX; if (StellwertL < -PWMMAX) StellwertL = -PWMMAX; #ifdef DISPLAY_REGELUNG_AVAILABLE if (display_screen==DISPLAY_REGELUNG_AVAILABLE){ display_cursor(1,1); display_printf("%03d/%03d ",encoderRate,encoderTargetRateL); display_cursor(2,1); display_printf("e =%03d ",err); display_cursor(3,1); display_printf("L =%04d ", StellwertL); } #endif } last2ErrL = lastErrL; // alten N-2 Fehler merken lastErrL = err; // alten N-1 Fehler merken //Regler rechts if (encoderTargetRateR == 0){ StellwertR=0; err=0; lastErrR=0; } else { encoderRate = sensEncR-lastEncoderR; // aktuelle Ist-Wert berechnen [Encoder-ticks/aufruf] lastEncoderR = sensEncR; // Anzahl der Encoderpulse merken fuer naechsten Aufruf merken err = encoderTargetRateR - encoderRate; // Regelabweichung links // Stellwert Berechnen StellwertR += (Kp * (err - lastErrR))/10; // P-Beitrag StellwertR += (Ki * (err + lastErrR)/2)/10; // I-Beitrag // StellwertR += Kd * (err - 2 * lastErrR + last2ErrR); // D-Beitrag //berechneten Stellwert auf zulaessige Groesse begrenzen if (StellwertR > PWMMAX) StellwertR = PWMMAX; if (StellwertR < -PWMMAX) StellwertR = -PWMMAX; #ifdef DISPLAY_REGELUNG_AVAILABLE if (display_screen==DISPLAY_REGELUNG_AVAILABLE){ display_cursor(1,10); display_printf("%03d/%03d ",encoderRate,encoderTargetRateR); display_cursor(2,10); display_printf("e =%03d ",err); display_cursor(3,10); display_printf("R =%04d ", StellwertR); } #endif } last2ErrR = lastErrR; // alten N-2 Fehler merken lastErrR = err; // alten N-1 Fehler merken #ifdef DISPLAY_REGELUNG_AVAILABLE if (display_screen==DISPLAY_REGELUNG_AVAILABLE){ display_cursor(4,1); display_printf("Kp=%03d Ki=%03d", Kp, Ki); } #endif // Und nun den Wert setzen bot_motor(StellwertL,StellwertR); } #endif /*! * Direkter Zugriff auf den Motor * @param left Geschwindigkeit fuer den linken Motor * @param right Geschwindigkeit fuer den linken Motor * Geschwindigkeit liegt zwischen -255 und +255. * 0 bedeutet Stillstand, 255 volle Kraft voraus, -255 volle Kraft zurueck. * Sinnvoll ist die Verwendung der Konstanten: BOT_SPEED_XXX, * also z.B. motor_set(BOT_SPEED_LOW,-BOT_SPEED_LOW); * fuer eine langsame Drehung */ void motor_set(int16 left, int16 right){ #ifdef SPEED_CONTROL_AVAILABLE static int16 old_mot_ticks=0; #endif if (left == BOT_SPEED_IGNORE) left=BOT_SPEED_STOP; if (right == BOT_SPEED_IGNORE) right=BOT_SPEED_STOP; // Haben wir ueberhaupt etwas zu tun? if ((speed_l == left) && (speed_r == right)){ // Hier sitzt die eigentliche Regelung #ifdef SPEED_CONTROL_AVAILABLE register uint16 ticks = TIMER_GET_TICKCOUNT_16; if (ticks-old_mot_ticks > MS_TO_TICKS((uint16)SPEED_CONTROL_INTERVAL)) { speed_control(); old_mot_ticks = TIMER_GET_TICKCOUNT_16; } #endif return; // Keine Aenderung? Dann zuerueck } if (abs(left) > BOT_SPEED_MAX) // Nicht schneller fahren als moeglich speed_l = BOT_SPEED_MAX; else if (left == 0) // Stop wird nicht veraendert speed_l = BOT_SPEED_STOP; else if (abs(left) < BOT_SPEED_SLOW) // Nicht langsamer als die speed_l = BOT_SPEED_SLOW; // Motoren koennen else // Sonst den Wunsch uebernehmen speed_l = abs(left); if (abs(right) > BOT_SPEED_MAX)// Nicht schneller fahren als moeglich speed_r = BOT_SPEED_MAX; else if (abs(right) == 0) // Stop wird nicht veraendert speed_r = BOT_SPEED_STOP; else if (abs(right) < BOT_SPEED_SLOW) // Nicht langsamer als die speed_r = BOT_SPEED_SLOW; // Motoren koennen else // Sonst den Wunsch uebernehmen speed_r = abs(right); if (left < 0 ) speed_l=-speed_l; if (right < 0 ) speed_r=-speed_r; #ifdef SPEED_CONTROL_AVAILABLE // TODO Hier koennten wir die Motorkennlinie heranziehen um gute Einstiegswerte fuer die Regelung zu haben // Evtl. sogar eine im EEPROM kalibrierbare Tabelle?? encoderTargetRateL = left / SPEED_TO_ENCODER_RATE; // Geschwindigkeit [mm/s] umrechnen in [EncoderTicks/Aufruf] encoderTargetRateR = right / SPEED_TO_ENCODER_RATE; // Geschwindigkeit [mm/s] umrechnen in [EncoderTicks/Aufruf] #endif // Zuerst einmal eine lineare Kennlinie annehmen bot_motor(speed_l/2,speed_r/2); } /*! * Stellt die Servos * Sinnvolle Werte liegen zwischen 8 und 16 * @param servo Nummer des Servos * @param servo Zielwert */ void servo_set(uint8 servo, uint8 pos){ if ((servo== SERVO1) && (pos != SERVO_OFF)) { if (pos< DOOR_CLOSE) pos=DOOR_CLOSE; if (pos> DOOR_OPEN) pos=DOOR_OPEN; } servo_low(servo,pos); } /*! * Initialisiere den Motorkrams */ void motor_init(void){ speed_l=0; speed_r=0; motor_low_init(); }