Problem mit PID-Regelung

Hallo zusammen,

ich bin ein totaler Arduino Neuling und nutze dieses Board aber nun für eine Studienarbeit.
Ich möchte die Geschwindigkeit eines Motor über einen Poti regeln aber gleichzeitig soll sich die Geschwindigkeit in Abhängigkeit der Werte, die ich von einem Ultraschallsensor einlese, ändern.

PID vPID(&vist, &vger,&vsoll, Kp, Ki, Kd, DIRECT);
void setup(){
Serial.begin(9600);
pinMode(MotorA, OUTPUT);
pinMode(MotorB, OUTPUT);
pinMode (PWM, OUTPUT);
digitalWrite(CSr, HIGH);
vPID.SetOutputLimits(0 , 255); //falls Variablen double!!
vPID.SetMode(AUTOMATIC);
vPID.SetSampleTime(SampleTime);
}

void loop(){
vsoll=analogRead(Poti)/4.02;

digitalWrite(MotorA, HIGH);
digitalWrite(MotorB, LOW);

vist=vsoll- Abstandsmessung()/5;

vSchiebPID.Compute();
analogWrite(PWMlvger);
}
int Abstandsmessung()
{
// // Turn on the display:
lcd.display();
pinMode(pingPin, OUTPUT);
digitalWrite(pingPin, LOW);
delayMicroseconds(2);
digitalWrite(pingPin, HIGH);
delayMicroseconds(5);
digitalWrite(pingPin, LOW);
pinMode(pingPin, INPUT);
duration = pulseIn(pingPin, HIGH);
Abstand = microsecondsToCentimeters(duration);
return Abstand;
}

long microsecondsToCentimeters(long microseconds)
{
return microseconds / 29 / 2;
}

Der Motor gibt nun immer sehr stockend gas. Und oft wenn er soll aber auch wenn man nichts ändert beschleunigt der Motor stockend.

Kann mir jemand helfen was ich falsch mache?

Vielen Danke
Grüße

Pack mal bitte das ganze Programm (in "Code"-Tags) rein.
Hier stimmt so einiges nicht.

Z.B. muss Sampletime einen Wert enthalten-man kann die Zeile auch ganz weg lassen, dann wird die Voreinstellung benutzt.
Glaub, 200ms...

Dann heisst dein Regler PID vPID, später aber lässt du den gar nicht berechnen, sondern irgendwas, vorher nie intialisiertes namens vSchiebPID...
Auch der Kommentar da ist Quatsch: vPID.SetOutputLimits(0 , 255); //falls Variablen double!!
Hat damit absolut nix zu tun, OutputLlimits beschreibt lediglich den Bereich, in dem sich die Ausgangsgrösse des Reglers bewegen soll!
Wenn du also den Motor über ne PWM ansteuerst, die 256 Stufen hat, ist deine Programmzeile richtig, aber der Kommentar dahinter zeigt, dass du nicht verstanden hast, wozu die gut ist.

Übrigens: für eine Regelung braucht man immer ein Feedback- woher soll der Regler sonst wissen, wann, und wie weit er zu regeln hat?

Mit deinen Vorgaben (bzw. dem, was du hier erklärt hast) brauchst du lediglich nen Drehzahlsteller.
Ohne Feedback (was der Motor wirklich macht) kann eine Regelung nicht funktionieren.
Hab da ja nen Verdacht, aber-> beschreib das Problem mal bitte richtig und genau.

Hallo Danke
schon einmal für die Antwort.
Ich versuche das Problem nun genauer zu beschreiben. Ich versuche einen Wagen über zwei Elektromotoren anzutreiben. Die Geschwindigkeit der Motoren sollen mit Hilfe eines Drehpotentiometer geregelt werden. Damit man nicht zu Nah oder zu weit weg vom Wagen ist will ich die Geschwindigkeit über einen Ultraschallsensor anpassen. D.h. ist die Person zu weit weg wird der Wagen langsamer, ist die zu nah dran wird der Wagen schneller. Ich hoffe das kann man soweit verstehen.
Die gewünschte Geschwindigkeit soll über ein PWM an die Motoren weitergegeben werden. Da das ganze ohne Regelung immer sehr unsanft war, wollte ich es mit einer PID Reglung optimieren.

#include <LiquidCrystal.h>
#include <PID_v1.h>

const int MotorAr = 43;
const int DIAGAr = 47; 
const int PWMr=45; 
const int DIAGBr =51; 
const int MotorBr = 53;
const int CSr =49; 
const int MotorAl = 42;
const int DIAGAl = 46; 
const int PWMl=44; 
const int DIAGBl =50; 
const int MotorBl = 52;
const int CSl =48;
const int Poti=A12;
LiquidCrystal lcd(16, 17, 18, 19, 20, 21);
const int pingPin = 2;
int Abstand, optAbstand=40; 
int WertPoti;
long duration;
double vsoll, vist, Kp=2, Ki=5, Kd=1, v, SampleTime=100; 
PID vSchiebPID(&vist, &v,&vsoll, Kp, Ki, Kd, DIRECT); 



void setup(){
    Serial.begin(9600);
    lcd.begin(16, 2);
    pinMode(MotorAr, OUTPUT);
    pinMode(MotorBr, OUTPUT);
    pinMode(CSr, OUTPUT); 
    pinMode(DIAGAr, INPUT);
    pinMode(DIAGBr, INPUT); 
    pinMode (PWMr, OUTPUT);
    pinMode(MotorAl, OUTPUT);
    pinMode(MotorBl, OUTPUT);
    pinMode(CSl, OUTPUT); 
    pinMode(DIAGAl, INPUT);
    pinMode(DIAGBl, INPUT); 
    pinMode (PWMl, OUTPUT);
   digitalWrite(CSr, HIGH); 
   digitalWrite(CSl, HIGH); 
   lcd.print("Start!");
   vSchiebPID.SetOutputLimits(0 , 255); //PWM ansteuern
   vSchiebPID.SetMode(AUTOMATIC); 
   vSchiebPID.SetSampleTime(SampleTime);
}

void loop(){
 Abstand = Abstandsmessung(); //Abstand über den Ultraschallsensor einlesen
 Serial.print("Abstand"); 
 Serial.print(Abstand); 
 Serial.println();
   digitalWrite(CSr, LOW); 
   digitalWrite(CSl, LOW); 
   digitalWrite(MotorAr, HIGH);
  digitalWrite(MotorBr, LOW); 
  digitalWrite(MotorAl, HIGH);
  digitalWrite(MotorBl, LOW);
  WertPoti=analogRead(Poti);
  lcd.print("Poti:");
  lcd.print(WertPoti);
  lcd.clear();
  Serial.print("Poti:");
  Serial.print(WertPoti);
  Serial.println();
 
  
  vsoll=analogRead(Poti)/4.02; //die gewünschte Geschwingikeit vom Poti
  lcd.print("v:"); 
  lcd.print(vsoll);
  Serial.print("VSoll:");
  Serial.print(vsoll); 
    Serial.println();
  
  
digitalWrite (CSr, LOW);
digitalWrite (CSl, LOW);
 //Geschwindigkeitsänderung über Ultraschallsensor
 if (Abstand <= optAbstand+5 || Abstand >= optAbstand-5)
{ 
 vist=vsoll;
}  
if (Abstand < optAbstand-5);
{
  
  vist=vsoll+(optAbstand-Abstand)*2;
  
  }

if (Abstand > optAbstand+5)
{
  
  vist=vsoll-(Abstand-optAbstand)*2;

}

 analogWrite(PWMl,v); 
 analogWrite(PWMr, v);
  
 Serial.print("vist:");
 Serial.print(vist); 
 Serial.println();
 
vSchiebPID.Compute(); 
    
   analogWrite(PWMl,v); 
 analogWrite(PWMr, v);
 
 Serial.print("v:"); 
 Serial.print(v); 
   Serial.println();

}

int Abstandsmessung()
{
      lcd.display();
   pinMode(pingPin, OUTPUT);
    digitalWrite(pingPin, LOW);
    delayMicroseconds(2);
    digitalWrite(pingPin, HIGH);
    delayMicroseconds(5);
    digitalWrite(pingPin, LOW);
    pinMode(pingPin, INPUT);
    duration = pulseIn(pingPin, HIGH);
    Abstand = microsecondsToCentimeters(duration);
    lcd.print("Abstand");
    lcd.setCursor(0, 1);
    lcd.print(Abstand);
    lcd.clear();
    return Abstand;
   
}



long microsecondsToCentimeters(long microseconds)
{
  return microseconds / 29 / 2;
}

Vielen Dank
Gruß Karo

Hallo,

der Wagen soll möglichst mit konstanten Abstand vor einer Person vorausfahren?
Mit dem Poti stellst Du nur die Grundgeschwindigkeit ein? Denn beim fahren kann man ja schlecht am Poti drehen?

Deine PinModes in der Abstandsmessung gehören in setup() und das lcd Init und clear sollten auch raus. Das lcd haste ja schon in setup initialisiert. Damit wird die Messung an sich schon schneller. lcd clear ist eher langsam, besser cursor zurück an eine position setzen und dann die alten Werten mit neuen einfach überschreiben lassen.

Zur Regelung selbst kann ich leider nichts sagen.

danke
doch die Geschwindigkeit soll über den Poti auch während der Fahrt geregelt werden können

So ganz verstehe ich das noch nicht.

Du willst den Abstand Regeln, in dem der Wagen vor der Person herfährt. Korrekt?

Das bedeutet für mich:
Person steht: Wagen steht.
Person läuft langsam los => Wagen fährt langsam los.
Person läuft schnell => Wagen fährt schnell.

Wozu das Poti?

Das ist letztlich eine Unterart der elektronischen Deichsel, bzw ein Abstandsregler wie er ja auch in manchen Autos verbaut ist.
Wahrscheinlich ist ein PID hier garnicht nötig. Wenn, dann würde ich erstmal die Parameter Ki und Kp zu Null setzen, und dann langsam den Kp erhöhen. Da wahrscheinlich der hinterhergehende Mensch instinktiv "mitregelt" sollte das reichen. Erst wenn das halbwegs stabil läuft, dann kannst du Anfangen mit Ki zu experimentieren. Kd wirst du nicht brauchen.

Was aber am wichtigsten ist: Deine Regelgröße ist nicht Geschwindigkeit, sondern der Abstand!
Dein Ausgangswert ist dann die Geschwindigkeit.
Deine PID deklaration sollte also so aussehen:

PID vSchiebPID(&Abstand, &v,&optAbstand, Kp, Ki, Kd, DIRECT);

Dafür brauhts echt keinen PID-Regler.
Die Regelstrecke hast du auch so:

Sensor misst Abstand-> das Sensorergebnis wird ein wenig aufbereitet->als PWM wieder auf den Motor gegeben.
Mit aufbereiten meine ich hier, evtl. den Wert "umdrehen", oder passend skalieren, und gut.
Wenn du beispielsweise den Sensor per AnalogRead() einlesen würdest, kämen Werte zwischen 0 und 1024 heraus- die teilst du einfach durch vier und gibst sie direkt als PWM zum Motor (vorausgesetzt, 1024 ist Abstand.max und 0 ist Abstand.min)- schon haste ne Regelung.

Poti ist sinnlos: während der Fahrt kann ich den nicht bedienen, wenn die Karre vor mir herfährt (oder mir hinterher).
Falls der aber aus irgendeinem Grund unbedingt mit rein muss, auch kein Ding: damit würde ich der PWM einfach nur einen "Startwert" vorgeben. Macht aber wirklich kaum Sinn: Verstelle ich den Offset nach oben, fährt mir das Ding in den Allerwertesten, wenn ich zu langsam laufe.

Und: wenn wir von nem echten, normalen Fussgänger reden, brauchst du eine Sampletime von 100ms im Leben nicht, dort würd ich entweder den Grundwert nehmen oder sogar noch höher gehn. Der PID regelt auch in den Zeiten dazwischen (immer, wenn regler.compute() aufgerufen wird), die Sampletime legt lediglich fest, wie oft Input und Output verglichen werden (und darauf basierend neu gerechnet wird). Nen Regler funktioniert nicht in Echtzeit-nen bisschen Zeit musst du ihm schon geben (der kann regeln was er will, wenn der Wagen mechanisch gar nicht so schnell reagieren kann..).

Danke
ich werde mich nocheinmal dran probieren.
Der Drehpoti soll später durch gas drehgriffe zum Beschleunigen ersetzt werden

Hallo,

ich glaube uns ist allen immer noch nicht klar wofür das Poti ist wenn der Abstand geregelt werden soll im Bezug zu einer Person?

Oder wird das eine Art Segway, Du stehst drauf, gibts Handgas und überwachts gleichzeitig den Abstand zu irgendwas? Dann nimmt er automatisch Gas weg, greift also in Dein Gasbefehl ein? Oder wie?

Ich stells mir so vor: eine Art Motorschubkarre: der Vorarbeiter dackelt vorneweg, Paulchen (der am Gasgriff... ]:smiley: ) mit der Karre hinterher.
Damit er seinem Chef nicht in den A...llerwertesten fahren kann, gibts den Abstandssensor. Wenn der Vorarbeiter stehen bleibt (und Paulchen nicht rechtzeitig das Gas zurückreisst) liegt er selber auf der Karre-und lernt was. Wenns Vorarbeiter aber eilig hat, flattert Paule als Wimpel an den Handgriffen der Geschichte hinterher... 8)

Hallo,

schöne Vorstellung, im Kopfkino läuft gerade der Trickfilm dazu ... :smiley: :smiley: :smiley:

Ah, nun wird es klarer.

Du willst also nicht auf einen Abstand regeln, sondern nur dafür sorgen, dass ein minimaler Abstand nicht unterschritten wird!

Ändert aber nichts an meiner Aussage:

guntherb:
Was aber am wichtigsten ist: Deine Regelgröße ist nicht Geschwindigkeit, sondern der Abstand!
Dein Ausgangswert ist dann die Geschwindigkeit.
Deine PID deklaration sollte also so aussehen:

PID vSchiebPID(&Abstand, &v,&optAbstand, Kp, Ki, Kd, DIRECT);

Der einzige Unterschied ist die Verarbeitung der Ausgangsgröße:
Du gibst über Poti eine Geschwindigkeit (eigentlich ist es ja eine Leistung) vor. (Sicherheitstip: nimm ein Stereo-poti und werte beide Schleifer getrennt aus. Wenn ein Draht abreißt, hast du ein Kriterium für Notaus)
Diese Poti-Vorgabe multipliziest du mit dem Ausgangsignal des Reglers. Der Regler muß so gepolt sein: Abstand zu groß: Ausgang 1, Abstand zu klein: Ausgang = 0.

Dann kannst du, solange der Abstand groß genug ist, alles mit dem Gasgriff regeln, und wenn er zu nahe kommt, dreht der Regler den Saft ab.
Aber es reicht ein P-Regler, vielleicht brauchts einen PD. Aber keinesfalls ein I-Anteil, der ist hier tödlich, weil er sich aufzieht, solange der Regler im Anschlag ist.

danke