PID-Run-Away-Motorsteuerung

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!

Geben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein

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.

Bitte poste den Code als Text und nicht als unbeschnittenen Screenshot des Codes. Auf diese Weise ist es lesbar und wir können es kopieren und zur Bearbeitung in die Antworten einfügen. Stellen Sie sicher, dass Sie den Code-Tag-Button verwenden und ihn richtig formatieren / formatieren. Sie können auch erwähnen, was die Entwicklungsumgebung und die Programmiersprache ist.
Plötzliche große Sprünge in einem solchen Signal sind oft auf Probleme mit Integer-Überlauf zurückzuführen. Wenn der Signalpfad Ganzzahlen enthält (vielleicht MC_SetMotorSpeed?), Untersuchen Sie sie oder zeigen Sie sie an.
Ihr PID-Code sieht in Ordnung aus, außer dass Sie self->integral mit error * self->Ki inkrementieren sollten (und diese Multiplikation nicht in der self->output-Zeile durchführen sollten), oder Sie sollten self->integral * self- vergleichen. >Ki mit Selbst->Outmin und Selbst->Outmax.
@TimWescott Das Begrenzen des Integrals nach dem Multiplizieren mit Ki scheint das Problem gewesen zu sein. Ich schätze Ihre Hilfe und ich hätte das schon früher merken sollen. Danke schön. Wenn Sie Ihren Kommentar in eine Antwort einfügen könnten, wäre dies vorerst die Lösung.

Antworten (2)

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!

Die Diagramme zeigen nur die Rate, mit der Daten an das Arduino gesendet werden. Ich mache mir bei dieser Lösung keine Sorgen um die Bandbreite, da die Abtastzeit auf der PID angepasst werden kann.
Also, wenn ich die Grafik und dich selbst richtig verstanden habe, steht die Sample-Nummer auf der x-Achse und der PWM-Wert auf der y-Achse?
Entschuldigen Sie. Das hätte ich deutlich machen sollen. Die x-Achse ist die Zeit und die y-Achse ist die Drehzahl des Motors.