268 lines
8.4 KiB
C
268 lines
8.4 KiB
C
/*
|
|
* 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 <stdlib.h>
|
|
#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();
|
|
}
|
|
|
|
|