Forum: Compiler & IDEs PID läuft in falsche Richtung


von Mark P. (pisy)


Lesenswert?

Hallo zusammen,
folgendes Problem, die Variable die in den OCR1A geschrieben wird, zählt 
fortlaufend hoch und läuft in die Begrenzung rein.
Ich erläutere jetzt die Hardware und die Software.

Ich habe ein Netzteil mit dessen V+ ein Widerstandsdraht verbunden ist, 
dieser hat ca 1 Ohm. An dem Widerstand ist dann ein Spannungsteiler 
angeschlossen, an diesem Spannungsteiler ist parallel eine Lastsenke. 
Ende des Teilers und der Senke sind mit der Masse des Netzgeräts 
verbunden. Auch die Massen der Controller liegen alle auf dem gleichen 0 
Potential.
Ich will über den Spannungsteiler, der 24 in Reihe liegende Widerstände 
hat, die Spannung messen und diese einlesen, manch einer hat mir ja 
schon hierbei geholfen.
Die Erfassung läuft einwandfrei und die Spannungswerte werden in einem 
Array gesammelt.
Alle 24 Schleifendurchläufe wenn alle Spannungen gesammelt sind, soll 
geregelt werden.
Dazu werden alle Spannungen im Array mit einem Sollwert verglichen und 
die niedrigste Spannung wird dann als Istwert verwendet.
Liegen alle Spannung größer gleich dem Sollwert soll nichts getan 
werden.
Mein Problem ist, dass der Wert "Senke" den ich in den OCR1A schreibe 
fortlaufend hochgezählt wird und somit der Strom steigt. Wenn der Strom 
aber steigt, dann steigt auch der Spannungsabfall an dem 1 Ohm 
Widerstand und die Spannung am Spannungsteiler wird noch geringer.
Jedoch soll das ganze umgekehrt laufen. Wenn Die Spannung fällt, soll 
Strom reduziert werden.
Die Regelung lief einwandfrei als ich den Strom als Istwert genommen 
habe, bzw über den Strom alles geregelt habe.

Der Regler ist von einem niederländischen Kollegen.
Gemeten=Istwert
Gewenst=Sollwert


1
volatile int8_t gewenst=0; //Sollwert
2
volatile int8_t gemeten=0; //Istwert
3
volatile uint16_t senke=0; //Wert für OCR1C
4
volatile int16_t i_hold_value=0; //Variable für Spannungsvergleich
5
6
7
int PID(void)
8
{
9
  static int _first=1;
10
  static int sec=0;
11
  static double Ts,Kc,Ti,Td;
12
  static double gemeten_1=0;
13
  static double K_LPF,c1,c2,lpf=1,lpf_1=1,lpf_2=1;
14
  static double ek,ek1;
15
  static uint16_t output;
16
  static double P,I,D;
17
  Kc=0.5;
18
  Ti=1;
19
  Td=1;
20
  Ts=1;
21
  
22
23
  K_LPF = (double)Td * 0.1;  
24
  c1 = (1.0*K_LPF-(double)Ts)/(1.0*K_LPF+(double)Ts); 
25
  c2 = (double)Ts / (1.0*K_LPF + (double)Ts); 
26
  lpf = c1 * lpf_1 + c2 * (gemeten+gemeten_1);
27
28
  ek=gewenst-gemeten;  //error berekenen
29
30
  P=-Kc * (gemeten-gemeten_1);//  vor Kc
31
  I=Kc * ((double) Ts/(double)Ti * ek);
32
  D=((-Kc * (double)Td/(double)Ts) * (lpf-2*lpf_1+lpf_2));
33
  output=output+P+I+D;
34
35
  ek1=ek;
36
  gemeten_1=gemeten;
37
  lpf_2 = lpf_1;
38
  lpf_1 = lpf;
39
  
40
41
  return output;
42
}
43
44
45
//---------------------------------------------------------------------------
46
// Main-Funktion
47
//---------------------------------------------------------------------------
48
49
main ()
50
{
51
52
  init();
53
54
  //----------------------------------------------------------------------    
55
56
  // Timer 1 initialisieren
57
  TCCR1A=(1<<WGM11)|(1<<WGM10)|(1<<COM1A1);
58
  TCCR1B=(1<<DDB1);
59
  //----------------------------------------------------------------------  
60
61
  // Variablen deklarieren
62
  uint16_t result=0;
63
  char c_buffer[20];
64
  char c_zeichen;
65
  int16_t i_temp;      //Sollwert Strom oder anzahl_zellen
66
  int i_anzahl_zellen;
67
  uint16_t i_zellspannungen[23];
68
  int i_volt_setpoint=0;
69
  int i_low_volt=0;  
70
  int counter;
71
  float f_help=0;
72
  char standby=0;
73
  //----------------------------------------------------------------------  
74
75
  uint8_t i_index=0;
76
  int count=0;
77
  int checksum=0;
78
79
  count=1;
80
  PORTC&=~(1<<DDC5);
81
  while (true) // Mainloop
82
83
  {
84
    
85
    init_timer_2 ();    //aktivieren des Timers 
86
    i_abbruch=1;      //Setzt Abbruchkriterium
87
    standby=0; //Um Senke abzuschalten
88
89
    
90
    i_zellspannungen[count]=einlesen(0);  //Sammeln der Spannungen
91
92
    
93
94
    if (count==24)  //Regle erst nachdem alle Zellen abgetastet sind
95
    {
96
97
    gewenst=10;  //Sollwert
98
    i_hold_value=gewenst;
99
100
    for (i_index=1;i_index<25;i_index++)
101
      {
102
        //Beginn der 25 For Schleife
103
104
        if(i_zellspannungen[i_index]<i_hold_value) //mit i_hold_value als SOLLWERT
105
        {
106
107
          i_hold_value=i_zellspannungen[i_index];//suche geringste Zellspannung und benutze die als Istwert
108
109
        }
110
111
      }
112
      //Ende der 25er For Schleife
113
114
      //Regelkriterium
115
116
      if (gewenst>i_hold_value) //ist Sollwert größer als Istwert dann regel
117
      {
118
119
        gemeten=i_hold_value;//Istwert=Wert der niedrigsten Zelle
120
        senke=PID(); //Speicher PWM Compare in Zwischenvariable
121
122
        //Schränke PWM ein
123
        if (senke>1023)
124
        {
125
          senke=1023;
126
        }
127
      else if (senke<0)
128
        {
129
          senke=1; //hier kommt später ein Highpegel hin, der über einen Thyristor die Senke in standby schaltet
130
        }
131
      else
132
        {
133
          ;
134
        }
135
136
        OCR1A=senke;
137
138
      }
139
140
    else //andersfalls mache gar nichts
141
142
      ;
143
144
    }
145
146
    do              //wartet so lange bis Timer übergelaufen
147
    {              //springt in Timer 2 
148
    }while(i_abbruch!=0);      
149
150
    count++;
151
152
  }
153
  //end mainloop
154
}


Entweder ich muss ein Vorzeichen im PID ändern, ich weiss aber nicht 
welches, ich habe die allgemeine Konstante K_C schon negiert und die 
Differenz zwischen Sollt und Ist umgekehrt, aber es läuft immer wieder 
hoch.
Vielleicht kann mir einer helfen bzw mit Fehlersuchen.

Danke im Voraus

von Klaus W. (mfgkw)


Lesenswert?

Also die Richtung der Regelung könntest du einfach ändern mit:
1
   return -output;
anstatt:
1
   return output;
am Ende von PID().
Andere mögliche Stellen gibt es natürlich auch...

von Mark P. (pisy)


Lesenswert?

Wenn ich da ein minus Vorsetze, dreht dann erhalte ich ja einen 
negativen Wert. versteht das ORC1A negative Werte?
Und wenn ja, negiert er dann die Spannung? Habe jetzt kein Board zum 
ausprobieren hier, aber mich würde es wundern wenn es so einfach wäre, 
weil die Lastsenke ja dann auch ein negatives PWM erhält und PWM ist ja 
nichts anderes als ein digitaler Code.

von Karl H. (kbuchegg)


Lesenswert?

Mark Pisani schrieb:
> Wenn ich da ein minus Vorsetze, dreht dann erhalte ich ja einen
> negativen Wert. versteht das ORC1A negative Werte?

Da OCR1A unsigned ist: Nein
Aber diese Negierung kommt praktisch aufs gleiche raus, als wie wenn du 
sagen würdest

   return MAXIMAL_WERT - output;

> Und wenn ja, negiert er dann die Spannung?

Nein

von brrrrrrrrrrrrrhhhhmmmmmmmmmm (Gast)


Lesenswert?

Wie wäre es von 100% deinen Stellwert abzuziehen ???
Invertiern ???

von Mark P. (pisy)


Lesenswert?

Mit den 100% das habe ich versucht, ich habe gesagt 1023-output, er hat 
trotzdem hochgerechnt. Ganz so dämlich bin ich auch nicht;)

von B e r n d W. (smiley46)


Lesenswert?

ek = gemeten-gewenst;  //error berekenen

MfG, Bernd

von Mark P. (pisy)


Lesenswert?

Hallo Bernd, danke aber das war auch mein erster Ansatz, leider hat das 
nicht funktioniert.

von Stefan (Gast)


Lesenswert?

Ich habe Deine Anwendung zwar nicht wirklich verstanden, aber meiner 
Meinung nach machst Du zwei entscheidende Fehler:

1.) Eine Regelung macht nur dann Sinn, wenn immer geregelt wird,
    nicht nur wenn Dein Ist-Wert < Soll-Wert ist

2.) Du aktualisierst die PWM nur wenn Du auch regelst,
    also Ist < Soll. Dann muss der Ist-Wert ja auch größer werden.
    Sobald aber Ist > Soll aktualisierst Du die PWM nicht mehr,
    wie soll Ist dann jemals wieder kleiner werden?

von Mark P. (pisy)


Lesenswert?

Ja ich verstehe,
das habe ich mich auch gefragt. Mein Arbeitskollege meinte, die Senke 
soll keinen Strom mehr ziehen, wenn Ist=Soll ist oder Ist>Soll.
Mein Problem ist, ich weiss nicht welchen Wert ich dann übergeben soll 
als PWM.
Oder ich mache eine Schleife, das er in jedem Fall regelt und sonst den 
letzten PWM Wert wieder übergibt, dieser ist ja relativ nah an dem 
Sollwert wenn dieser wieder erreicht wird.

von Stefan (Gast)


Lesenswert?

Da ist jetzt nicht böse gemeint, aber ich habe den Eindruck, Du fummelst 
an einem bestehenden Programm herum, von dem Du keinen blassen Dunst 
hast.
Das ist immer gefährlich, wenn man nicht weiß, was man tut oder tun 
soll.

Das tolle an einer Regelung ist ja, dass man sich eigentlich überhaupt 
keine Gedanken mehr darüber machen muss, welchen PWM-Wert man übergeben 
soll, denn das "Denken" übernimmt ja gerade der Regler... vorausgesetzt, 
man hat die Regelparameter "vernünftig" eingestellt.

Sich drumherum winden und Schleifen programmieren, falls nicht ... oder 
vielleicht doch... oder was denn nun...?!

Mach Dich mit der Regelungstechnik vertraut und dem zu regelnden System 
vertraut und programmier den Regler neu, so dass Du ihn auch 
verstehst... sonst wird das nix... mein guter Rat zur Nacht ;-)

von kurz (Gast)


Lesenswert?

Zustimmung!

von B e r n d W. (smiley46)


Lesenswert?

Bedingung ist, dass ein Ausregeln überhaupt möglich ist.

Gruss, Bernd

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.