Digitaler PID Regler frage zur Formel

OP #4738914
Lesenswert?

Hej

Hätte mal eine Verständnis Frage zur PID Regler.

Hier gibt es eine sehr gute erklärung was regelungstechnik angeht.

http://rn-wissen.de/wiki/index.php/Regelungstechnik

Wenn man runter scrolt kommt man irgend wann zum Digital PID Regler.
Ich habe auch soweit alles verstanden bis auf eine Sache.

Da steht Ta ist die Abtastzeit. Ich bin mir nicht ganz sicher woher ich 
den wert für Ta bekomme.

Ich bin gerade dabei einen DC Bürstenmotor mit Encoder anzusteuern.
Möchte das ganze anhant von PID Regler und PWM machen.

Als Controler kommt ein Atmega328P mit 16MHz zum einsatz der mit Atmel 
Studio in C programmiert wird. Die PWM geht auf eine H Brücke L293. 
Links rechtslauf ist erwünscht. Denke werde erstmal mit drehzahlregelung 
starten. Nächstes ziel ist die positionierung.

Würde mich auf Tipps sehr freuen.


LG
Gast #4738993
Lesenswert?

Hi Tasker,
Tasker schrieb:
> Besser als Interrupt:
> Zieh Dir ne 1ms Task im Hauptprogramm auf.

Ich kenne/lese bisher auch nur die Methode über den Interrupt. Da uC 
eher ein Nebenbereich für mich sind, bin ich mir nicht sicher, was genau 
du mit Task meinst.

Magst du das bitte ein wenig näher ausführen?

Vielen Dank.

Cheers,
Alexander
Gast #4739009
Lesenswert?

eine Zeitscheibe

der vernünftigen Gegenentwurf zum schwachsinnigen delay

z.B erstellt man ein 1ms Task eine 10ms Task  und einen 100ms Task
die immer entsprechend aufgerufen werden un sich alle aus dem selben 1ms 
Takt ableiten.

Temperaturregler funktionen mit Erfassung packt man dann in das 100ms 
Raster
Schalteranfragen in das 10ms Raster
und extrem Zeitkritische Sachen in das 1ms

Der Softwareteil der sich darum kümmert wer jetzt dran kommt nennt man 
Scheduler
und mit allem was man sonst noch dazugehört Betriebssystem.


Das geht auch schon mit weniges kByte Code.
Gast #4739023
Lesenswert?

Alexander schrieb:
> Magst du das bitte ein wenig näher ausführen?

Der meint wohl bestimmt den µC als Statemachine.
(https://www.mikrocontroller.net/articles/Statemachine)

Kann man auch machen, wenn µC nichts anders zu tun hat. Dafür erstellst 
du einen Interrupt mit 1khz Frequenz. Ungefähr so:

1
#define PID_UPDATE_TIME  10
2
uint8_t pid_motor = 0;
3

4
void ISR(){  //1ms
5
static uint32_t cnt_ms=0;
6
cnt_ms++;
7

8
  if (!(pid_motor%PID_UPDATE_TIME))
9
  motor = 1;
10
  if(!lcd....
11
}
12

13
int main{
14
   while(1){
15

16
   if(pid_motor)
17
   pid_motor=0;
18
   //PID Regelung
19
   }
20
   if(lcd)
21
   lcd=0
22
...
23
return 0;
24
}

Man muss hier aber ein paar Dinge beachten:
-Überlauf cnt_ms nach 50 Tagen, besser ist es als uint64_t zu 
deklarieren
-alle Task dürfen nicht zu lang ausgeführt sein
wie hier z.B. wenn der lcd task 20ms braucht, dann wird es mit 1ms 
motor_pid nicht klappen, dann wäre der Interrupt doch besser.
OP #4739080
Lesenswert?

Hallo  jungs,

super danke schonmal für die Antworten.
Wenn ich es richtig verstanden habe ist die abtastzeit die Zeit in der 
eine Regelabweichung stattfindet korrekt?
Das Heißt wenn eine Störgröße mein System stört das eine Regelabweichung 
entsteht, fägt die Abtastzeit an zu zählen? (I Regler e*Ta).


Z.B.


wenn ich folgenden Code verwende:

als PseudoCode betrachten:

int PID()
{

   e = w - x;          //Vergleich
   if (e <> 0)         // Wenn die e kleiner oder auh größer als 0
   {
      Ta++;
      delay_m(1);
   }
   else
   {
      Ta = 1;          // Damit keine Division durch 0 entsteht.
   }

   esum = esum + e;    //Integration I-Anteil

   y = Kp*e + Ki*Ta*esum + Kd/Ta*(e – ealt);  //Reglergleichung
   ealt = e;
}


Bitte den code nicht auf Syntax prüfen ist nur damit ich es auch richtig 
verstehe. Und bitte nicht wegem delay festnageln.

Ich würde oben erst e berechnen, dann gucken ob e größer oder kleiner 0. 
Wenn ja Ta++ ansonsten Ta=1.


Hoffe bin in etwa auf ded richtigen Weg.


LG
Gast #4739106
Lesenswert?

Nee, du mußt die Werte jede ms erfassen und jede ms die Reglergleichung 
aufrufen. Das heisst, im Takt von 1ms wird immer die Veränderung zur 
vorherigen ms berechnet, daraus der Ausgabewert bestimmt und der an das 
Stellglied ausgegeben.
OP #4739319
Lesenswert?

hhhm versthe ich das richtig? Die Abtastzeit ist im Grunde t=1/f, wobei 
f=anzahl der PID Algurithmus Aufrufe ist?.

Z.B. wenn ich 100 mal in der sekunde meine PID Funktion aufrufe, ist 
mein Ta = 1/100Hz. Somit ist Ta = 0,01s -> 10ms. Ist das korrekt?

Dann ist ja Ta in diesem Fall ein fetser Wert korrekt?



LG
#4739352
Lesenswert?

Bei üblichen µC gibst DU die Abtastzeit vor!

Das kann durch das Abfragen eines Timers (interne Uhr) erfolgen oder 
durch eine vom Timer ausgelösten Unterbrechung.

Die Zeit hängt von Deinem Problem ab - und nicht zu vergessen - der 
benötigten Rechenzeit.
Es ist völlig sinnlos eine Temperatur oder eine schwere Masse im 
Mikrosekundenbereich abzutasten.

Hast Du es aber "Eilig", so solltest Du nicht vergessen, dass die oft 
verwendete Fließkommaarithmetik relativ Zeitintensiv ist, so keine 
Fließkommarecheneinheit eingebaut ist.
Auch kann das Bloße einbinden der Fließkommaarithmetikbibliotheken 
Deinen Flash leicht zum Überlaufen bringen.

Mit Ganzzahlarithmetik kann man über den einen oder anderen Stolperstein 
hinwegkommen.
#4739373
Lesenswert?

Volle schrieb:
> Der Softwareteil der sich darum kümmert wer jetzt dran kommt nennt man
> Scheduler
> und mit allem was man sonst noch dazugehört Betriebssystem.

Warum tust Du so als wäre es eine Selbstverständlichkeit daß jeder für 
jeden Kleinkram gleich ein komplettes OS und einen Controller der 4 
Nummern teurer ist als nötig mit einplant? Mach mal nen 
Realitätsabgleich.
#4739393
Lesenswert?

Ein Multitaskingbetriebssystem fällt für mich in die Kategorie: Kanonen 
und Spatzen.

Da ein Multitaskingsystem immer noch langsamer ist, als ein System ohne, 
hält sich der Nutzen in überschaubaren Grenzen. Es gibt natürlich auch 
hier Ausnahmen.

Ist man sich unsicher, ob man konstante Zeiten einhalten kann, so macht 
man einfach: So schnell es geht, also z.B. bei jedem 
Hauptschleifendurchlauf.

Kann es hier zu einer unkalkulierbaren Periode kommen, so muss man sich 
nur die Zeit der letzten Berechnung merken (Takt – Tvor) und dann mit 
einer, minimal, variablen Zykluszeit arbeiten.

Es wird immer wieder vergessen, dass eine konstante Zykluszeit sinnvoll 
und bequem ist, aber nicht notwendig!
(Firma: Potilatormanufaktur) #4739420
Lesenswert?

Digit-22 N. schrieb:
> hhhm versthe ich das richtig? Die Abtastzeit ist im Grunde t=1/f, wobei
> f=anzahl der PID Algurithmus Aufrufe ist?.
>
> Z.B. wenn ich 100 mal in der sekunde meine PID Funktion aufrufe, ist
> mein Ta = 1/100Hz. Somit ist Ta = 0,01s -> 10ms. Ist das korrekt?
>
> Dann ist ja Ta in diesem Fall ein fetser Wert korrekt?

Ja, so ist es. Ta ist sogar immer ein fester Wert, wenn du nicht grade 
deine loop-Frequenz änderst.

Es haben bis dahin in der Tat irgendwie alle an deiner Frage und deinem 
Verständnisproblem vorbei geantwortet.
Gast #4739860
Lesenswert?

Bernd K. schrieb:
> Warum tust Du so als wäre es eine Selbstverständlichkeit daß jeder für
> jeden Kleinkram gleich ein komplettes OS und einen Controller der 4
> Nummern teurer ist als nötig mit einplant? Mach mal nen
> Realitätsabgleich.

solltest du mal machen

Ein funktionierendes Multitasking  Echtzeit OS benötigt wirklich nicht 
viel Code.

Mein erstes habe ich vor 23 Jahren als Teil meiner Diplomarbeit 
geschrieben
hat so 20% der ganzen Arbeit ausbemacht und lief auf einem MC56156 
Signalprozessor.

Seither sind noch ein paar Implementierungen dazugekommen
Andere kommerzielle habe ich recht tief untersucht.

Es muss nicht 100% POSIX kompatibel sein um ein gutes Betriebssystem zu 
sein.
Gast #4739865
Lesenswert?

Sebastian S. schrieb:
> Ist man sich unsicher, ob man konstante Zeiten einhalten kann, so macht
> man einfach: So schnell es geht, also z.B. bei jedem
> Hauptschleifendurchlauf

eine "Lösung" die nur durch Zufall funktionieren kann
irgendwann knallt und man ist dann bei 0 Steht.


Echtzeit ist nicht möglichst schnell
sondern so schnell wie notwendig.


Ein OID Regler mit variablem dt ist eine Herausforderung was Auslegung 
und Stabilität angeht.
OP #4740164
Lesenswert?

Hallo mdma,

Ja irgendwie schon. Aber habs ja jetzt kappiert. :-).

Ich hab das ganze gestern abend getestet. Funkionirt ganz gut soweit.
Hab nen kleinen getriebe dc motor mit einem 360 poti als feedback 
angesteuert.
Ein zweiter poti gibt den soll positionswert vor...

Jetzt kommt der nächste schritt. Dc motor mit encoder regeln. Der 
encoder hat AB und einen nulldurchgang Z. 500 pulse/u...


Mal schauen ob es klappt.
Lg
#4740919
Lesenswert?

Vergiss bitte nicht, dass Du in Deinen PID-Regler ggf. noch weitere 
Funktionen hinzufügen musst, wie das Begrenzen des I-Terms 
(Anti-Windup/-Winddown) oder ein Totbereich (Deadband), in welchem die 
Steuergröße Null wird...

In der Praxis fehlen diese Funktionalitäten oft, was ein unharmonisches 
Regelverhalten, trotz korrekt dimensionierter Terme, zur Folge hat.

Viele Grüße!

Sven
OP #4741074
Lesenswert?

Hallo Jungs,

hier sind die ersten Versuche.
Das einlesen des Encoders funktioniert sehr gut.
Aber irgendwie scheint der Bereich in der While Schleife nicht so 
optimal zu sein. Der motor fährt nicht auf den Sollwert sonder dreht 
sich vibriert und macht was er will. Irgendwie scheint es an den PWMs zu 
liegen. Am oszi sieht man die PWMs nur zappeln.

Im Grunde schaue ich ob y < 0 ist und schalte dem entsprechen den 
ausgang, also zb. OCR1A läuft hoch und OCR0A runter.
Und das gleich für y > 0 nur umgekehrt.
1
#define F_CPU 16000000UL
2

3
#include <avr/io.h>
4
#include <avr/interrupt.h>
5
#include <util/delay.h>
6

7
int count = 0;
8
int state = 0;
9

10

11

12
int main(void)
13
{
14
  // Definition Eingänge für Encoder
15
  //----------------------------------------------------------------------------------------------
16
  PORTD |= (1<<PD2) | (0<<PD1);        // Encoder Eingänge. PD2 = A, PD1 = B.
17
  DDRD &= ~(1 << DDD2);            // Clear the PD2 pin PD2 (PCINT0 pin) is now an input
18
  PORTD |= (1 << PORTD2);            // turn On the Pull-up  PD2 is now an input with pull-up enabled
19
  //----------------------------------------------------------------------------------------------
20
  
21
  // Definition externer Interupt Eingang
22
  //----------------------------------------------------------------------------------------------
23
  EICRA |= (1 << ISC00);            // set INT0 to trigger on ANY logic change
24
  EIMSK |= (1 << INT0);            // Turns on INT0
25
  //----------------------------------------------------------------------------------------------
26
  
27
  // Definition Ausgänge zur Steuerung der H-Brücke
28
  //----------------------------------------------------------------------------------------------
29
  //DDRB = (1<<PB0) | (1<<PB1);          // Motorausgänge zweipunktregelung ohne PWM
30
  DDRB |= _BV(PB1);                // Ausgang PWM
31
  DDRD |= _BV(PD6);                // Ausgang PWM
32
  
33
  TCCR1A |= _BV(COM1A1) | _BV(WGM10);        // PWM PB1 OCR1A 
34
  TCCR1B |= _BV(CS10) | _BV(WGM12);
35
  
36
  TCCR0A |= _BV(COM0A1) | _BV(WGM00) | _BV(WGM01);// PWM PD6 OCR0A 
37
  TCCR0B |= _BV(CS00);
38
  
39
  //----------------------------------------------------------------------------------------------
40
  
41
  //----------------------------------------------------------------------------------------------
42
  sei();                    // turn on interrupts
43
  //----------------------------------------------------------------------------------------------
44
  
45
  // Definition PID Regler Variablen
46
  //----------------------------------------------------------------------------------------------
47
  float e = 0;                // Regelabweichung
48
  float w = 4000;                // Sollwert-Position/Geschwindigkeit
49
  float x = 0;                // Regelgröße aktueller Encoderwert
50
  float y = 0;                // Stellgröße
51
  float esum = 0;                // Summer aller Regelabweichungen
52
  float ealt = 0;                // Letzte Regelabweichung
53
  
54
  float Kp = 1;                // Propotionalbeiwert
55
  float Ki = 1;                // Integralbeiwert
56
  float Kd = 1;                // Differenzialbeiwert
57
  float Ta = 0.01;              // Abtastzeit
58
  //----------------------------------------------------------------------------------------------
59
  
60

61
  while(1)
62
  {
63
    x = count;
64
    e = w - x;
65
    esum = esum + e;
66
    
67
    y = Kp*e + Ki*Ta*esum + Kd/Ta * (e - ealt);
68
    ealt = e;  
69
    
70
    
71
    
72
    if (y < 0)
73
    {
74
      OCR1A = y * (-1);  
75
      OCR0A = 0;
76
      //OCR1A =0;  
77
      //PORTD &= ~(1<<PD6);  //LOW
78
      //PORTB |= 1<<PB1;  //HIGH
79
      //PORTB &= ~(1<<PB1); //LOW
80
    }
81
    if (y > 0)
82
    //_delay_ms(1);
83
    {
84
      
85
      OCR0A = y;
86
      OCR1A = 0;
87
      //PORTB &= ~(1<<PB1); //LOW
88
      //PORTD |= 1<<PD6;    //HIGH
89
      //PORTD &= ~(1<<PD6);  //LOW
90
    }
91
    _delay_ms(1);
92
  }
93
}
94

95

96

97
ISR (INT0_vect)
98
{
99
  
100
  if ( !(PIND & (1<<PIND2)) and !(PIND & (1<<PIND1) ))
101
  {
102
    count++;
103
  }
104
  if ( PIND & (1<<PIND2) and PIND & (1<<PIND1) )
105
  {
106
    count++;
107
  }
108
  
109
  if ( PIND & (1<<PIND2) and !(PIND & (1<<PIND1) ))
110
  {
111
    count--;
112
  }
113
  if ( !(PIND & (1<<PIND2)) and PIND & (1<<PIND1) )
114
  {
115
    count--;
116
  }
117
}

Die parametrierung Kp Ki Kd und Ta sind erstmal nur testweise also bitte 
nicht deswegen festnageln. Im ersten Schritt möchte ich nur einen 
Sollwert Position vor geben und diesen soll der Motor erreichen und 
halten. Pro Umdrehung habe ich 500 Steigende und 500 Fallende Falnke 
worauf mein Interupt reagiert. Somit 1000 Pulse/U.


Vielleicht entdeckt ja jemand den Fehler oder hat einen viel besseren 
Ansatz.
Gast #4741175
Lesenswert?

Hallo Digit-22 N.
ich gehe davon aus, dass Du mit dem Motor positionieren möchtest.
Die Vorgehensweise ist folgende:
1. Den Strom durch den Motor mit eine PI-Regler regeln.
2. Die Drehzahl mit einem PI-Regler regeln.
3. Die Position mit einem P-Regler regeln.

Die ist eine vermaschte Regelung, wobei die Stromregelung die schnellste 
ist (ca. 100uSek), Drehzahlregelung ca. 1 mSek, Positionierung ca. 5 
mSek Abtastzeit.

Nur mit einem PID-Regler habe ich es nie probiert. Industriell werden 
Motoren nach dem oben beschrieben Verfahren Positioniert.
VG Walter
OP #4741188
Lesenswert?

Hallo walter.
Danke für den tipp. Die kaskadierte regelung ist natürlich in planung. 
Steht jedoch als letzten Schritt auf der liste. Für die strom regelung 
würde ich dann kleine stromsensoren einstzen...

Aber erstmal die Kinderkrankheiten ausmerzen. Daher zuerst der PID 
Regler. So lerne ich das ganze am besten...


LG
Gast #4741340
Lesenswert?

Servus,
nehme einen PID-T1 Regler! Der D-Anteil wird sonst schwingen.

Es gibt Faustregeln wie man T1 in Abhängigkeit von KD einstellt 
(Aström).

Man kann schon eine Positionierung mit einen PID-T1 hinkriegen, dann 
braucht man aber schon den Anti-Windup Ansatz nach Aström.

mfg
Angehängte Dateien:
Gast #4741536
Lesenswert?

Digit-22 N. schrieb:
> Kannst du mir auch etwas zum Code sagen?
> Bin etwas am verzweifeln und finde einfach das Problem nicht.

> float y = 0;                // Stellgröße

Benennt man üblichweise mit "u".
Später wäre viellt eine fix-point Arichmetik sinnvoller beim 328P. Ich 
weiß ja nicht welche Ambitionen angepeilt sind.

> float Ta = 0.01;              // Abtastzeit

stimmt nicht mit
>_delay_ms(1);
überein!

Vorausgesetzt die PWM für die Motoransteuerung fkt. (Datenblätter werde 
ich jetzt nicht lesen für dich) ist dieser Regler einfach Murks.

> x = count;
>     e = w - x;
>     esum = esum + e;
>
>     y = Kp*e + Ki*Ta*esum + Kd/Ta * (e - ealt);
>     ealt = e;

Hier fehlt die Saturation:
y=Kp*e... gleich zu anfang macht es: y=1*1000;

Hier musst du die Einheiten umrechnen. Ich vermute mal, dass du mit 12V 
arbeitest.

if(y<=-12)
  y=-12;
if(y>=12
  y=12;

Hier musst du jetzt weiterhin umrechnen: z.B. 12V entspricht 1024 pwm...

Ohne richtige Systemanalyse kriegst du auch keinen guten Regler hin! Das 
ist wie die berühmte Nagel im Heuhaufen. Den Heuhaufen abbrennen kann 
man hier auch nicht.

Du könntest einen Eingangsprung auf den Regler geben und diesen mal in 
Excel ploten lassen. Wie das aussehen los, kannst du mal googeln.

Ich rate dir aber dazu meinen vorgeschlagenen Regler zu nehmen:
1
u=k1* uk_1+ k2* uk_2 + k3* e+ k4* ek_1+ k5* ek_2;
2
uk_2=uk_1;
3
uk_1=u;
4
ek_2=ek_1;
5
ek_1=e;

Die Kontanten Ki gleich am Anfang ausrechnen.
Gast #5973417
Lesenswert?

Hier auch ein code zum Periodischen Ausführen von Funktionen

int Zeitstempel;

//-------- TIMING FUNCTION ------------//
bool tick(unsigned long ms, unsigned long *ptstamp)
{
  unsigned long tAct = millis();
  if(tAct >= *ptstamp + ms) // Zeit fürs Warten
  {
   *ptstamp = tAct; // Zeitpunkt letzter Durchlauf speichern
   return 1;
  }
  else
  {
   return 0;
  }
}

// in der loop:

if(tick(1000, &Zeitstempel))
{
  // wird alle 10 Sekunden ausgeführt
}
Gast #5973429
Lesenswert?

es müsste heißen:

bool tick(unsigned long ms, unsigned long *Zeitstempel)
...
  if(tAct >= *Zeitstempel + ms) // Zeit fürs Warten


Und ich habe eine Frage:
Wofür ist eigentlich die Stromregelung, wie es die Industrie macht 
(siehe oben) zuständig?

Hat jemand ein Codebeispiel für den Anti-Windup? meine bisherigen 
Versuche waren nur ungenügend :-).

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren