Forum: Mikrocontroller und Digitale Elektronik pid Positionsteuerung mit dc motor und poti arduino


von Philipp D. (dreyer)


Lesenswert?

Moin,
ich bin Neuling und ungelehrter aber höchst interessiert an 
mikrocontrollern.
Habe mir für den anfang ein arduino duemillanove mit motorshield rev 
zugelegt bereu aber auch schon den kauf des motorshields, aber egal zum 
Thema. ich habe hier als set up Duelmillanove sainsmart motorshiel einen 
getriebe dc motor 1 zu 17 übersetzung 12v travo mehrere linera potis 
gewindestange mit einenr mutter an der ein Teleskopierbare Hebel 
angebaut ist. Dieser führt zum poti und betätigt diesen. Die 
gewindestange wird vom dc motor gedreht und schwenk den hebel von links 
nach rechts. Das ist mein Versuchsaufbau, das spätere ziel das der motor 
mit hilfe eines moosgummirades am lenkrad und das an der vorderachse ein 
ein poti den lenkwinkel bestimmt.


ich hab schon geübt und gespielt mit motor und poti aber bei der pid 
positions regelung finde ich momentan meine Grenzen.
Ich hab mir mehrere codes und librarys durchgelesen muss aber sagen aus 
den librarys werde ich nicht klug! darum meine bitte an euch könnt ihr 
mir bitte weiterhelfen
Brett beguards pid example aus dem tutorial wurde meist als sehr rubust 
gelobt aber irgendwie fehlt da doch die würze für meon vorhaben ich 
dachte daran die aggressiv KP,KI,KD und konstant KD,KI,KP von der 
adaptive tuning pid library einzubauen um schnell und sicher zur 
gewünschten position zu kommen.

so ich hab mich mal probiert die beiden zusammen zu bringen weis aber 
nicht mehr weiter wie ich die parameter KP,KI,KD tausche und verdammte 
axt wie krieg da die drehrichtung raus das kann doch nicht so einfach 
wie zum beispiel mit einer if funktion. :D
1
/*working variables*/
2
unsigned long lastTime;
3
double Input, Output, Setpoint;
4
double ITerm, lastInput;
5
double kp, ki, kd;
6
int SampleTime = 1000; //1 sec
7
double outMin, outMax;
8
bool inAuto = false;
9
double aggKp=4, aggKi=0.2, aggKd=1;
10
double consKp=1, consKi=0.05, consKd=0.25;
11
 
12
#define MANUAL 0
13
#define AUTOMATIC 1
14
 
15
#define DIRECT 0
16
#define REVERSE 1
17
18
#include <MSMotorShield.h>
19
20
MS_DCMotor motor(4);
21
22
23
24
int controllerDirection=DIRECT;
25
 
26
 void setup() {
27
   Serial.begin(9600);
28
   motor.run(RELEASE);
29
   Input = analogRead(5);
30
   Setpoint = 500;
31
   
32
   
33
 
34
 }
35
 
36
 void loop()  
37
 {  
38
   Input = analogRead(5);
39
   
40
   double gap = abs(setoint-input);
41
   if(grap<15);
42
   SetTunings (consKp, consKi, consKd);
43
   else
44
   SetTunings(aggKp, aggKi, aggKd);
45
 }
46
void Compute()
47
{
48
   if(!inAuto) return;
49
   unsigned long now = millis();
50
   int timeChange = (now - lastTime);
51
   if(timeChange>=SampleTime)
52
   Input=analogRead(5);
53
   {
54
      /*Compute all the working error variables*/
55
      double error = Setpoint - Input;
56
      ITerm+= (ki * error);
57
      if(ITerm > outMax) ITerm= outMax;
58
      else if(ITerm < outMin) ITerm= outMin;
59
      double dInput = (Input - lastInput);
60
 
61
      /*Compute PID Output*/
62
      Output = kp * error + ITerm- kd * dInput;
63
      if(Output > outMax) Output = outMax;
64
      else if(Output < outMin) Output = outMin;
65
 
66
      /*Remember some variables for next time*/
67
      lastInput = Input;
68
      lastTime = now;
69
   }
70
}
71
 
72
void SetTunings(double Kp, double Ki, double Kd)
73
{
74
   if (Kp<0 || Ki<0|| Kd<0) return;
75
 
76
  double SampleTimeInSec = ((double)SampleTime)/1000;
77
   kp = Kp;
78
   ki = Ki * SampleTimeInSec;
79
   kd = Kd / SampleTimeInSec;
80
 
81
  if(controllerDirection ==REVERSE)
82
   {
83
      kp = (0 - kp);
84
      ki = (0 - ki);
85
      kd = (0 - kd);
86
   }
87
}
88
 
89
void SetSampleTime(int NewSampleTime)
90
{
91
   if (NewSampleTime > 0)
92
   {
93
      double ratio  = (double)NewSampleTime
94
                      / (double)SampleTime;
95
      ki *= ratio;
96
      kd /= ratio;
97
      SampleTime = (unsigned long)NewSampleTime;
98
   }
99
}
100
 
101
void SetOutputLimits(double Min, double Max)
102
{
103
   if(Min > Max) return;
104
   outMin = Min;
105
   outMax = Max;
106
 
107
   if(Output > outMax) Output = outMax;
108
   else if(Output < outMin) Output = outMin;
109
 
110
   if(ITerm > outMax) ITerm= outMax;
111
   else if(ITerm < outMin) ITerm= outMin;
112
}
113
 
114
void SetMode(int Mode)
115
{
116
    bool newAuto = (Mode == AUTOMATIC);
117
    if(newAuto == !inAuto)
118
    {  /*we just went from manual to auto*/
119
        Initialize();
120
    }
121
    inAuto = newAuto;
122
}
123
 
124
void Initialize()
125
{
126
   lastInput = Input;
127
   ITerm = Output;
128
   if(ITerm > outMax) ITerm= outMax;
129
   else if(ITerm < outMin) ITerm= outMin;
130
}
131
 
132
void SetControllerDirection(int Direction)
133
{
134
   controllerDirection = Direction;
135
}



oder sollte man ein autotune regler nehmen auf jeden fall darf er nie 
aufhören zu regeln da es fremdeinflüsse auf die lenkung gibt die den 
schonmal ne bewegung am lenkrad ausmachen, aber das dauerhafte regeln 
dürfte ja ein positiver nebeneffekt sein.

ich hoffe ihr könnt mir helfen

Vielen dank im vorraus

dreyer

: Bearbeitet durch User
von tommy (Gast)


Lesenswert?

Philipp Dreyer schrieb:
> if(grap<15);

wo haste denn grap deklariert?

ausserdem: nach if (irgendeine Bedingung) hätte ich jetzt
kein Semikolon (;) erwartet.

von Mike (Gast)


Lesenswert?

tommy schrieb:
> ausserdem: nach if (irgendeine Bedingung) hätte ich jetzt
> kein Semikolon (;) erwartet.

Nur mit Semikolon dahinter kann der Compiler die Zeile wegoptimieren und 
über das nicht deklarierte grap hinwegsehen. ;-)

von Philipp D. (dreyer)


Lesenswert?

Muss man zum drehrichtungswechsel die pid paramter mit einem - versehen 
?
               if (grap<0) (contollerDirection=REVERSE);



               if (Output<0) motor.run(BACKWARD);
               if (Output>0) motor.run(FORWARD);
               double Min=110, Max=250;
               set.MotorSpeed(Output);

was meint ihr dazu?

danke im vorraus

dreyer

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Mir scheint, du stocherst da etwas im Dunklen. Lass dir doch mal per 
seriell einige Parameter, wie z.B. outMin und outMax, sowie den Output 
des PID Reglers ausgeben und schau erstmal, wo die Werte überhaupt sind, 
wenn du manuell das Rückmeldepoti drehst (Motor abgeklemmt). Je näher du 
dann dem Setpoint kommst, umso kleiner sollte Output werden. Übrigens 
ist double hier wirklich übertrieben und kostet sehr viel Zeit. Wenn du 
schon mit Gleitkommazahlen agierst, reicht float hier dicke aus. Eine 
flinke PID Berechnung wäre besser mit int realisiert.
Gut, wenn Output das Vorzeichen wechselt, müsstest du, wenn alles o.k. 
ist, den Setpoint überschritten haben und das Servo will in die andere 
Richtung drehen.
Dein Ziel ist es nun, Output in ein passendes Format zu bringen und 
damit die PWM anzusteuern.

Dein nächstes Ziel müsste sein, die Parameter P,I und D per Konsole 
ändern zu können, um dein Servogebilde stabil und reaktionsschnell zu 
bekommen, wenn du den Setpoint per Sollwert-Poti änderst.

Als kleiner Tipp: Wenn du den einen Parameter auf null drehst, ist er 
ausser Gefecht. Also D auf null und es bleibt ein PI Regler übrig.
Fang mit P alleine an und dann füge die I und D Komponente zu.

: Bearbeitet durch User
von Philipp D. (dreyer)


Lesenswert?

Warum krieg ich da kein Output raus?
1
/*working variables*/
2
unsigned long lastTime;
3
float Input, Output, Setpoint;
4
float ITerm, lastInput;
5
float kp, ki, kd;
6
int SampleTime = 1000; //1 sec
7
float outMin, outMax;
8
bool inAuto = false;
9
float aggKp=4, aggKi=0.2, aggKd=1;
10
float consKp=1, consKi=0.05, consKd=0.25;
11
float Min=110, Max=250;
12
#define MANUAL 0
13
#define AUTOMATIC 1
14
 
15
#define DIRECT 0
16
#define REVERSE 1
17
18
#include <MSMotorShield.h>
19
20
MS_DCMotor motor(4);
21
22
23
int controllerDirection=DIRECT;
24
 
25
 void setup() {
26
   Serial.begin(9600);
27
   motor.run(RELEASE);
28
   Input = analogRead(5);
29
   Setpoint = 500;
30
   
31
32
 
33
 }
34
 
35
 void loop()  
36
 {  
37
   Input = analogRead(5);
38
   
39
   float grap = (Setpoint-Input);
40
   if(grap<10);
41
   SetTunings (consKp, consKi, consKd);
42
  
43
   if(grap>10)
44
   SetTunings(aggKp, aggKi, aggKd);
45
   
46
     Serial.print("setpoint: ");Serial.print(Setpoint); Serial.print(" ");
47
  Serial.print("input: ");Serial.print(Input); Serial.print(" ");
48
  Serial.print("output: ");Serial.print(Output); Serial.print(" ");
49
  delay(1000);
50
  
51
 }
52
void Compute()
53
{
54
   if(!inAuto) return;
55
   unsigned long now = millis();
56
   int timeChange = (now - lastTime);
57
   if(timeChange>=SampleTime)
58
   Input=analogRead(5);
59
   {
60
      /*Compute all the working error variables*/
61
      float error = Setpoint - Input;
62
      ITerm+= (ki * error);
63
      if(ITerm > outMax) ITerm= outMax;
64
      else if(ITerm < outMin) ITerm= outMin;
65
      float dInput = (Input - lastInput);
66
 
67
      /*Compute PID Output*/
68
      Output = kp * error + ITerm- kd * dInput;
69
      if(Output > outMax) Output = outMax;
70
      else if(Output < outMin) Output = outMin;
71
 
72
      /*Remember some variables for next time*/
73
      lastInput = Input;
74
      lastTime = now;
75
   }
76
}
77
 
78
void SetTunings(double Kp, double Ki, double Kd)
79
{
80
   if (Kp<0 || Ki<0|| Kd<0) return;
81
 
82
  float SampleTimeInSec = ((float)SampleTime)/1000;
83
   kp = Kp;
84
   ki = Ki * SampleTimeInSec;
85
   kd = Kd / SampleTimeInSec;
86
 
87
  if(controllerDirection ==REVERSE)
88
   {
89
      kp = (0 - kp);
90
      ki = (0 - ki);
91
      kd = (0 - kd);
92
   }
93
}
94
 
95
void SetSampleTime(int NewSampleTime)
96
{
97
   if (NewSampleTime > 0)
98
   {
99
      float ratio  = (float)NewSampleTime
100
                      / (float)SampleTime;
101
      ki *= ratio;
102
      kd /= ratio;
103
      SampleTime = (unsigned long)NewSampleTime;
104
   }
105
}
106
 
107
void SetOutputLimits(float Min, float Max)
108
{ 
109
  
110
   if(Min > Max) return;
111
   outMin = Min;
112
   outMax = Max;
113
 
114
   if(Output > outMax) Output = outMax;
115
   else if(Output < outMin) Output = outMin;
116
 
117
   if(ITerm > outMax) ITerm= outMax;
118
   else if(ITerm < outMin) ITerm= outMin;
119
}
120
 
121
void SetMode(int Mode)
122
{
123
    bool newAuto = (Mode == AUTOMATIC);
124
    if(newAuto == !inAuto)
125
    {  /*we just went from manual to auto*/
126
        Initialize();
127
    }
128
    inAuto = newAuto;
129
}
130
 
131
void Initialize()
132
{
133
   lastInput = Input;
134
   ITerm = Output;
135
   if(ITerm > outMax) ITerm= outMax;
136
   else if(ITerm < outMin) ITerm= outMin;
137
}
138
 
139
void SetControllerDirection(int Direction)
140
{
141
   controllerDirection = Direction;
142
}

: Bearbeitet durch User
von Philipp D. (dreyer)


Lesenswert?

Sagen wir mal so wie kriegt man in arduino rein??

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Philipp Dreyer schrieb:
> Sagen wir mal so wie kriegt man in arduino rein??

Dieser Satz kein Verb :-P

Evtl. fehlt bei deinem Programm die Hauptschleife? Du hast nichts über 
das gesagt, was am seriellen Port rauskommt?
Geh doch mal bitte schrittweise vor und fang nicht gleich mit dem 
Komplettprogramm an. Prüfe jedes Unterprogramm für sich. Und am besten 
liest du dir auch mal die Application Note 221 von Atmel durch, da wird 
ein prima PID Regler beschrieben, den du leicht auf deine Bedürfnisse 
ändern kannst. Ausserdem steht da eine ganze Menge zur Theorie eines 
PID.

: Bearbeitet durch User
von Mike (Gast)


Lesenswert?

Matthias Sch. schrieb:
> Evtl. fehlt bei deinem Programm die Hauptschleife?

Da ist sie doch (Arduino OS)

Philipp Dreyer schrieb:
> void loop()

von Philipp D. (dreyer)


Lesenswert?

Matthias Sch. schrieb:
> Philipp Dreyer schrieb:
>> Sagen wir mal so wie kriegt man in arduino rein??
>
> Dieser Satz kein Verb :-P
>
> Evtl. fehlt bei deinem Programm die Hauptschleife? Du hast nichts über
> das gesagt, was am seriellen Port rauskommt?
> Geh doch mal bitte schrittweise vor und fang nicht gleich mit dem
> Komplettprogramm an. Prüfe jedes Unterprogramm für sich. Und am besten
> liest du dir auch mal die Application Note 221 von Atmel durch, da wird
> ein prima PID Regler beschrieben, den du leicht auf deine Bedürfnisse
> ändern kannst. Ausserdem steht da eine ganze Menge zur Theorie eines
> PID.

moin,
 ich war frustriert :D
hab mich aber zusammengerissen und ein bisschen gebastellt und bin bis 
jetzte bei folgendem code angekommen. Meon problem ist momentan, dass 
ich die Pid outputs begränzen will für eine pwm funktion. Also im 
positiven bereich von 110bis250 und im negativen bereich von 
-110bis-250. Dann hab ich hoffentlich die möglichkeit über eine if 
abfrage ob output < als 0 ist die möglichkeit die drehrrichtung des 
motors zu bestimmen und kann den output direkt als pwm zahl einsetzen. 
Das ist mein nächstes ziel! ich hab die Output begrenzung mit den beiden 
Kommentar Strichen "//" aus den code raus genommen. So wie der code ist 
lauft er und regelt auch, aber wenn ich die begrenzung einsetzen will 
setzt er sich auf -10 beim ITerm fest
und beim Output auf -110.

Noch mal geschirr habe ein poti auf A5 und lasse mir die Zahlen Serial 
ausgeben

unsigned long lastTime;
float Input, Output, Setpoint;
float fehler, ITerm, lastInput;
float kp, ki, kd;
int SampleTime = 1000; //1 sec
float NewSampleTime=0;
float outMin, outMax;
bool inAuto = true;
float aggKp=4, aggKi=0.2, aggKd=1;
float consKp=1, consKi=0.05, consKd=0.25;
int Min=(110), Max=(250);
int minusMin(-110), minusMax=(-250);
float Kp=2,Ki=0.5,Kd=2;

float ratio;

 float outmax = (255);
   int outmin = (10);
   int minusoutmax = (-255);
   int minusoutmin = (-10);
   int minusoutMax;
   int minusoutMin;
void setup() {

 Serial.begin(9600);

   Input=analogRead(5);
   Setpoint=(500);




    // put your setup code here, to run once:

}

void loop() {

  Input=analogRead(5);

 if(!inAuto) return;
   unsigned long now = millis();
   int timeChange = (now - lastTime);
   if(timeChange>=SampleTime)

  Input=analogRead(5);
  /*Compute all the working error variables*/
      float fehler = Setpoint - Input;
      ITerm = (ki * fehler);



   // {  if (ITerm > outmax) ITerm = (255);
    //  if (ITerm < outmin) ITerm = (10);}



    // { if  (ITerm > minusoutmax) ITerm = (-255);
     //  if   (ITerm < minusoutmin) ITerm = (-10) ;
     // }
     float dInput = (Input - lastInput);

      /*Compute PID Output*/
      Output = kp * fehler + ITerm- kd * dInput;

      /*Remember some variables for next time*/
      lastInput = Input;
      lastTime = now;


      //if (Kp<0 || Ki<0|| Kd<0) return;


  SampleTime=1000;
  float SampleTimeInSec = ((int) SampleTime/1000);
   kp = Kp;
   ki = Ki * SampleTimeInSec;
   kd = Kd / SampleTimeInSec;


    if (NewSampleTime > 0)

     {  float ratio  = (NewSampleTime / SampleTime);
      ki *= ratio;
      kd /= ratio;
      SampleTime = (unsigned long)NewSampleTime;  }


      if(Min > Max) return;
   outMin = Min;
   outMax = Max;
   minusoutMin = minusMin;
   minusoutMax = minusMax;

   //if(Output > outMax) Output = outMax;
   //else if(Output < outMin) Output = outMin;

   //if(Output > minusoutMax) Output = outMax;
   //else if(Output < minusoutMin) Output = outMin;
  //if(ITerm > outMax) ITerm= outMax;
  //else if(ITerm < outMin) ITerm= outMin;





 //Serial.print("setpoint: ");Serial.print(Setpoint); Serial.print(" ");
  Serial.print("input: ");Serial.print(Input); Serial.print(" ");
  Serial.print("output: ");Serial.print(Output); Serial.print(" ");
  //Serial.print("fehler");Serial.print(fehler);Serial.print(" ");
  Serial.print("iterm");Serial.print(ITerm);Serial.print(" ");
  Serial.print("ki");Serial.print(ki);Serial.print(" ");



  Serial.print("dinput");Serial.print(dInput);Serial.print(" ");
  Serial.print("ratio");Serial.print(ratio);Serial.print(" ");
  Serial.print("sampletimeinsec");Serial.print(SampleTimeInSec);Serial.pri 
nt("  ");
  Serial.print("timechange");Serial.print(timeChange);Serial.print(" ");


  Serial.flush();
  delay(500);


  // put your main code here, to run repeatedly:







für irgendwelche verbesserungen in meiner code grammatik und Aufbau? 
Immer her damit habe ein offenes ohr für alles.
vielen dank im vorraus Dreyer

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Philipp Dreyer schrieb:
> outMin = Min;
>    outMax = Max;
>    minusoutMin = minusMin;
>    minusoutMax = minusMax;

Das ist mir nicht klar. Warum setzt du den deine eigentlich festen 
Grenzwerte hier auf neue Werte? Lass das mal weg und schau dir dann 
nochmal die Werte an.
Ausserdem solltest du dir wirklich mal die AVR221 ansehen, dein ganzes 
float-zu-int-und-wieder-zurück hin und her ist nämlich noch eine 
Fehlerquelle.
Noch ein Tipp: Bewege dich nur im positiven Zahlenbereich und definiere 
einfach z.B. 128 als Grenze. Ist der Output darunter, dreht sich der 
Motor in eine Richtung, dadrüber in die andere.

von Philipp D. (dreyer)


Lesenswert?

Guten
so hab float int aufgeräumt



unsigned long lastTime;
float Input, Output, Setpoint;
float fehler, ITerm, lastInput;
float kp, ki, kd;
int SampleTime = 1000; //1 sec
float NewSampleTime=0;
float outMin, outMax;
bool inAuto = true;
float aggKp=4, aggKi=0.2, aggKd=1;
float consKp=1, consKi=0.05, consKd=0.25;
float Min=(110), Max=(250);
float Kp=2,Ki=0.5,Kd=2;
float minusMin=(-255), minusMax=(-110);
float ratio;



und so krieg ich nu auch die pwm signale eingeschränkt

     //  if(Min > Max) return;
   if (Output > 0)

   outMin = Min,  outMax = Max;

   else if (Output < 0)

   outMin = minusMin, outMax = minusMax;

   if(Output > outMax) Output = outMax;
   else if(Output < outMin) Output = outMin;

   //if (ITerm > outMax) ITerm= outMax;
   //else if(ITerm < outMin) ITerm= outMin;


mal eine frage zum iterm meint ihr ich sollte noch mehrer iterms 
zusammen addieren ?

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.