This repository has been archived on 2025-03-02. You can view files and clone it, but cannot push or open issues or pull requests.
rc2007-soccer/source/ct-Bot/motor.c

269 lines
8.4 KiB
C
Raw Normal View History

/*
* 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();
}