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
:
Bearbeitet durch User
Servus, ein digitaler Regler basiert immer auf eine feste äquidistante Abtastzeit. Dafür eignet sich wunderbar ein Interrupt mit 1khz Frequenz.
>Dafür eignet sich wunderbar ein Interrupt mit 1khz Frequenz
Besser als Interrupt:
Zieh Dir ne 1ms Task im Hauptprogramm auf.
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
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.
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.
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
:
Bearbeitet durch User
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.
Digit-22 N. schrieb: > Da steht Ta ist die Abtastzeit. Ich bin mir nicht ganz sicher woher ich > den wert für Ta bekomme. Das wurde schon hier im Forum behandelt. Beitrag "Abtastzeit bei PID Regler"
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
:
Bearbeitet durch User
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.
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.
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!
:
Bearbeitet durch User
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.
:
Bearbeitet durch User
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.
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.
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
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
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.
:
Bearbeitet durch User
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
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
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
Ok danke für den Tipp. Kannst du mir auch etwas zum Code sagen? Bin etwas am verzweifeln und finde einfach das Problem nicht. Könnte es sein das mein externer Interut und die PWM Erzeugung sich nicht vertragen? LG Digit-22
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.
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 }
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 :-).
Ferdinand schrieb: > es müsste heißen: > > bool tick(unsigned long ms, unsigned long *Zeitstempel) > ... > if(tAct >= *Zeitstempel + ms) // Zeit fürs Warten Warum?
Wächter schrieb: > Und das nach 3 Jahren! Wär/ist doch in Ordnung, solange man inhaltlich was beiträgt.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.