Ich habe eine PID, die einen Gleichstrommotor steuert. Ich versuche, die Drehzahl des Motors sehr genau zu steuern. Mein Controller ermöglicht es mir, die Richtung des Motors zu ändern und ihm eine PWM für die Geschwindigkeit zu geben. Daher habe ich eine PID, die ein Plus- und Minus-Maximum und -Minimum hat. Um das Gerät zu beschleunigen und das Gerät schnell genug zu verlangsamen. Der Ausgang des PID ist für die PWM und daher ein absoluter Wert des PID, wobei nur ein Richtungsstift geändert wird, wenn PID < 0 ist. Ich verwende die entgegengesetzte Richtung des Motors nur als Bremssystem. Daher sollte der Motor immer in eine Richtung laufen, sich aber selbst schneller abbremsen, indem er ein umgekehrtes Drehmoment anwendet.
Ich schreibe C-Firmware in MCUXpresso. Die Diagramme stammen vom Senden von Daten über UART an ein Arduino, um Daten einfach darzustellen.
Mein Problem ist, dass manchmal, wenn die Prozessvariable 0 oder nahe daran erreicht, die PID invertiert und negativ werden muss und somit den Motor mit voller Geschwindigkeit in die entgegengesetzte Richtung dreht. Die beiden Bilder unten zeigen die bestimmten Fälle, in denen es passiert ist. Die rote Linie ist der Sollwert und die blaue Linie die Prozessvariable.
Der Code, der das Gerät und die PID steuert, ist unten.
Es fällt mir schwer zu verstehen, warum die PID so davonlaufen würde. Jede Hilfe wäre erstaunlich. Danke schön!
Hauptkontrolle
int dir = FORWARD; //Controls direction of motor
motorPID.setpoint = vehicleSpeed;
motorPID.input = SM_GetRPM();
motorPID.input = motorPID.input * speedConversion;
UART_SendPID((uint8_t)motorPID.input, (uint8_t)motorPID.setpoint);
PID_Compute(&motorPID);
if(motorPID.output < 0){
dir = BACKWARD;
}
if(motorPID.setpoint == 0){
motorPID.output = 0;
}
if(motorPID.input > 60){
MC_SetMotorSpeed(0, dir);
int test = 0;
}
MC_SetMotorSpeed(abs(motorPID.output),dir);
PID-Code
//Find all error variables
self->lastError = self->error;
double input = self->input; //Only so input can't change during compute
self->error = self->setpoint - input;
self->integral += self->error;
double derivative = self->error - self->lastError;
//Anti-integral Windup
if(self->integral > self->Outmax)
self->integral = self->Outmax;
else if(self->integral < self->Outmin)
self->integral = self->Outmin;
//Calculate PID
self->output = (self->Kp*self->error) + (self->Ki * self->integral) + (self->Kd * derivative);
//Set limits
if(self->output > self->Outmax)
self->output = self->Outmax;
else if(self->output < self->Outmin)
self->output = self->Outmin;
BEARBEITEN: Es stellte sich heraus, dass dies ein kombinierter Fehler des beschriebenen Problems und ein Hardwareproblem war.
Versuchen Sie, die Zeile zu ändern, die lautet
self->integral += self->error;
Zu
self->integral += self-> Ki * self->error;
und passen Sie das an, indem Sie die Zeile ändern, die lautet
self->output = (self->Kp*self->error) + (self->Ki * self->integral) + (self->Kd * derivative);
Zu
self->output = (self->Kp*self->error) + self->integral + (self->Kd * derivative);
Dadurch wird der Integralterm für Ihren Integrator-Begrenzungsschritt korrekt skaliert.
Nach Ihren Grafiken zu urteilen, glaube ich nicht, dass Sie mit einer ausreichend hohen Rate abtasten, um Stabilität zu erreichen ... Dies kann jedoch die Einschränkung des Arduino sein, da ich mit dem MCUExpresso nicht vertraut bin.
Idealerweise haben Sie eine mathematische Beschreibung Ihres Systems, so dass Sie eine Übertragungsfunktion haben, dann können Sie Ihre Bandbreite einfach an der Übertragungsfunktion ablesen. Diese Bandbreite ist auch die Nyquist-Frequenz, die die minimale "akzeptable" (bei lockerer Verwendung dieses Wortes) Frequenz ist, mit der ein Signal abgetastet wird. Normalerweise möchten Sie mit etwa dem 30- bis 40-fachen der Nyquist-Frequenz abtasten. Wenn dies nicht möglich ist, schlage ich vor, die Sensoren auszutauschen.
Ich bin letztes Semester an der Uni bei meinem Projekt auf ähnliche Probleme gestoßen, hoffe, das hilft!
Transistor
TimWescott
TimWescott
Zeichnete Fowler