Forum: Mikrocontroller und Digitale Elektronik PI-Drehzahlregler bürstenbehafteter Gleichstrommotor


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Steff G. (steff_123)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Leute,

ich bin momentan dran einen PI-Regler für meinen bürstenbehafteten 
Gleichstrommotor : Drive System DSMP523 ( inkl. Encoder-Scheibe) zu 
implementieren.

Dazu habe ich ein Mega2560-Board und programmiere dieses in C.

Da mein Board via USB mit Matlab kommuniziert lasse ich mir hier die 
Drehzahlen ausgeben.

Zur Überprüfung, ob ich die Flanken der Encoderscheibe richtig einlese, 
habe ich den Motor einfach angesteurt und mir die Drehzahl ausgeben 
lassen - hat soweit gut funktioniert -> Interrupts vom Drehgeber sollten 
richtig empfangen werden.

Ich will meinen Motor auf 10 U/min regeln, jedoch macht meine Regelung 
nicht was sie soll -> siehe Anhang Foto.

Mein Code der Regelung sieht wie folgt aus:
1
...
2
// Reglerparameter
3
  float e = 0;
4
  float esum = 0;
5
  float K_p = 10.0 , K_i = 0;
6
  float u = 0;
7
8
for (int i=0;i<Anzahl;i++)
9
    {
10
      RESET_T4();
11
      RESET_T5();
12
      
13
      esum = 0;
14
    
15
      while (counter4_OVERFLOW < Zeit[i])    // Zeitdauer Zeit[i] des i-ten Prüfzykluses
16
      {
17
        
18
        if(counter5_OVERFLOW == 5) // alle 100ms
19
        {
20
          float y = ((counter_IMPULSE/5.0)/(counter5_OVERFLOW*20.0))* 60000.0;  // Regelgroesse y - Motordrehzahl berechnen
21
            
22
            // Drehzahldaten per serieller Schnittstelle versenden
23
            if(y==0)                  
24
            {  uart_puts("N\n");
25
              uart_puts("0\n");
26
            }
27
            else
28
            {
29
              uart_puts("N\n");
30
              uart_puts( dtostrf( y, 10, 3, s ) );
31
              uart_puts("\n");
32
            }
33
          
34
          e = n_soll[i] - y;              // Regelabweichung e
35
          esum = esum + e;
36
          
37
          u = K_p*e + K_i*0.1*esum;    // Stellgroesse u
38
          
39
          if (u>254.0) {u = 254.0;}          // Stellgroessenbegrenzung um wind-up Effekt zu vermeiden
40
          if (u<0.0) {u = 0.0;}
41
          
42
          counter5_OVERFLOW = 0;            // Zuruecksetzen des counters
43
          counter_IMPULSE = 0;            // Zuruecksetzen des Impulszaehlers
44
        }
45
        OCR1AL = (int)u;                // Berechnete Stellgroesse wird auf Ausgang gelegt
46
      }
47
      
48
      uart_puts("xxx");                  // Befehl um die aktuelle Messreihe abzuschliessen
49
      uart_puts("\n");
50
    }
51
    
52
    uart_puts("End");                    // Befehl um die Kommunikation mit Matlab abzubrechen
53
    uart_puts("\n");
54
    
55
    // Abbremsen des Motors bis zum Stillstand
56
    for (int i=(int)u;i==0;i--)
57
    {
58
      OCR1AL--;
59
      _delay_ms(10);
60
    }
61
    
62
    OCR1AL = 0;
63
64
...

Ich hoffe von euch kann mir jemand helfen, aber ich finde meinen Fehler 
einfach nicht.

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


Bewertung
2 lesenswert
nicht lesenswert
Steff G. schrieb:
> // Drehzahldaten per serieller Schnittstelle versenden
>             if(y==0)
>             {  uart_puts("N\n");
>               uart_puts("0\n");
>             }
>             else
>             {
>               uart_puts("N\n");
>               uart_puts( dtostrf( y, 10, 3, s ) );
>               uart_puts("\n");
>             }

Das ist gar keine gute Idee. Wenn du alle 100ms erstmal die Bibel über 
seriell rausschiebst, wie soll dann die Regelung noch greifen?
Wirf das mal aus dem getimeden Code raus und packs irgendwo in die 
Hauptschleife. Ein Regler möchte regelmässig aufgerufen werden, sonst 
wird er nicht stabil. Dann machst du am besten P und I von aussen 
änderbar und schaust, was bei verschiedenen Werten passiert.
Application Note AVR221 beschreibt übrigens einen PID Regler für die 
Megas und Tinys, denn solltest du dir evtl. mal anschauen.
Noch eines: Es ist evtl. gar nicht nötig, die echte Drehzahl des Motors 
jedesmal wieder zu berechnen, sondern die Zeit zwischen zwei Encoder 
Events zu messen.

: Bearbeitet durch User
von Steff G. (steff_123)


Bewertung
0 lesenswert
nicht lesenswert
Die Zeichenkette werde via der Bibliothek von Peter-Fleury versendet.
Dass läuft ja dann sowieso im "Hintergrund" per Ring-Puffer usw. ab 
oder?

Auch wenn ich das versenden der Zeichenkette auskommentiere läuft mein 
Gleichstrommotor ganz und gar nicht passend.

Matthias S. schrieb:
>Application Note AVR221 beschreibt übrigens einen PID Regler für die
>Megas und Tinys, denn solltest du dir evtl. mal anschauen.

Ich schau mir die Bibliothek mal an, aber grundsätzlich wollte ich den 
PI-Regler selbst implementieren.
Dürfte ja eig. kein großes Hexenwerk sein oder?

Kann in meinem Code evtl. jemand einen Fehler finden?

Der Regler befindet sich aktuell in der Hauptschleife - sollte ich den 
denn wo anders hinpacken?

: Bearbeitet durch User
von Bastian W. (jackfrost)


Bewertung
0 lesenswert
nicht lesenswert
Mit Ki=0 ist der I-Anteil unwirksam. Wie viele Impulse sind eine 
Umdrehung ?


Gruß JackFrost

von Der Andere (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Steff G. schrieb:
> Der Regler befindet sich aktuell in der Hauptschleife - sollte ich den
> denn wo anders hinpacken?

Ein digitaler Regler braucht vor allem eins:
Ein konstantes delta-T (= Abtastrate).
Also sollte der Regler immer von einem Timer abgeleitet werden.
Ob das direkt in einem Timerinterrupt geschieht oder ob man in der 
Hauptschleife schaut ob eine bestimmte Delta Zeit vergangen ist seit 
Beginn des letzten Aufrufs hängt vom Rest des Programms und dem Wert der 
Abtastrate ab.
Den Regleralgorithmus einfach in der Hauptschleife aufzurufen ist ein 
Garant dass es nicht funktioniert.

von Steff G. (steff_123)


Bewertung
0 lesenswert
nicht lesenswert
Bastian Werner schrieb:
> Mit Ki=0 ist der I-Anteil unwirksam.

Ich wollte erst mal nur mit dem P-Anteil ungefähr an den Sollwert kommen 
und dann mit dem I-Anteil die stationäre Genauigkeit abbilden.


Bastian Werner schrieb:
> Wie viele Impulse sind eine Umdrehung ?

5 Impulse entsprechen einer Umdrehung.
Im Motor ist ein Getriebe eingebaut mit einer Untersetzung von 150.
Folglich müsste sich der Motor bei einer vorgegebenen 
Getriebeabtriebsdrehzahl von 10 mit 1500 U/min drehen.

von Steff G. (steff_123)


Bewertung
0 lesenswert
nicht lesenswert
Der Andere schrieb:

> Den Regleralgorithmus einfach in der Hauptschleife aufzurufen ist ein
> Garant dass es nicht funktioniert.

Der Regler wird doch in der Hauptschleife alle 100ms aufgerufen, somit 
habe ich ja mein konstantes delta-T hergestellt oder sehe ich das 
falsch?
1
if(counter5_OVERFLOW == 5) // alle 100ms
2
  ...

von Sumo (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Steff G. schrieb:
> Der Regler wird doch in der Hauptschleife alle 100ms aufgerufen,

Nur musst du dir mal überlegen, dass der Motor mit 1500rpm laufen soll 
und jede Umdrehung bringt 5 Impulse. Also hast du 7500 Impulse/min. Das 
sind 125 Impulse/s und du willst mit 10 Regelvorgängen/s (100ms) regeln?
Ergo: Deine Wiederholrate für den Regler ist VIEL zu langsam...

von Bastian W. (jackfrost)


Bewertung
0 lesenswert
nicht lesenswert
Steff G. schrieb:
> Bastian Werner schrieb:
>> Mit Ki=0 ist der I-Anteil unwirksam.
>
> Ich wollte erst mal nur mit dem P-Anteil ungefähr an den Sollwert kommen
> und dann mit dem I-Anteil die stationäre Genauigkeit abbilden.
>
>
> Bastian Werner schrieb:
>> Wie viele Impulse sind eine Umdrehung ?
>
> 5 Impulse entsprechen einer Umdrehung.
> Im Motor ist ein Getriebe eingebaut mit einer Untersetzung von 150.
> Folglich müsste sich der Motor bei einer vorgegebenen
> Getriebeabtriebsdrehzahl von 10 mit 1500 U/min drehen.


Misst du an der Motorwelle oder am Getriebe ?

Wie lange braucht deine Berechnung in der 100 ms Schleife ?

Wenn er nur mit P so schwingt ist das P schon zu groß.

Schreib das Programm so das die Werte von außen ändern kannst. Dann 
kannst du besser Werte finden.

Gruß JackFrost

von Ralf B. (Firma: Scorptech) (mad_scorp)


Bewertung
0 lesenswert
nicht lesenswert
Steff G. schrieb:
> 5 Impulse entsprechen einer Umdrehung.
> Im Motor ist ein Getriebe eingebaut mit einer Untersetzung von 150.
> Folglich müsste sich der Motor bei einer vorgegebenen
> Getriebeabtriebsdrehzahl von 10 mit 1500 U/min drehen.

Heisst das du kriegst 50 Impulse oder 7500? Entschuldige die Frage, aber 
ich habe den Aufbau noch nicht ganz verstanden.

Falls es 7500 sind (5 Imp * 1500) dann hast du 125 Imp/s und dann sind 
deine 100ms Abtastzeit schon recht knapp gewählt, meiner Meinung nach.

Digitale Regler sind tricky. Die meisten Bastler implementieren das 
quasi-kontinuierlich dh. mit Abtastraten >> Regelzeit.

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


Bewertung
0 lesenswert
nicht lesenswert
Steff G. schrieb:
> aber grundsätzlich wollte ich den
> PI-Regler selbst implementieren.

Die UART Lib benutzt du doch auch schon. Da schadet es sicher nicht, 
sich mal den PID Regler von Atmel anzuschauen.
Im FU benutze ich den auch und schubse in per Timer Interrupt alle 200 
PWM Durchläufe an:
https://www.mikrocontroller.net/articles/3-Phasen_Frequenzumrichter_mit_AVR

Kannst ja mal reinschauen. Klare Skalierung der Eingangswerte schadet 
übrigens nicht. Im Idealfall sind Soll und Ist in der gleichen 
Grössenordnung, dann ist auch der Regler nicht überfordert.

: Bearbeitet durch User
von Steff G. (steff_123)


Bewertung
0 lesenswert
nicht lesenswert
Bastian Werner schrieb:
> Misst du an der Motorwelle oder am Getriebe ?

Ich messe an der Motorwelle.

> Wie lange braucht deine Berechnung in der 100 ms Schleife ?

Habe den Prüfaufbau leider gerade nicht parat - versuche es morgen 
gleich nochmal.
Da brauche ich die time.h Bibliothek um das umzusetzen denke ich...

Ralf B. schrieb:
> Heisst das du kriegst 50 Impulse oder 7500?

7500 der Encoder sitzt auf der Motorwelle.

Ich versuche morgen gleich nochmal am P-Regler zu drehen, ob sich da was 
tut, aber grundsätzlich sollte das C-Programm passen?

Falls ich überhaupt nicht voran komme schau ich mir mal den fertigen 
PID-Regler von Atmel an.

von Ralf B. (Firma: Scorptech) (mad_scorp)


Bewertung
0 lesenswert
nicht lesenswert
Steff G. schrieb:
> Ralf B. schrieb:
>> Heisst das du kriegst 50 Impulse oder 7500?
>
> 7500 der Encoder sitzt auf der Motorwelle.

Dh. du tastest 10 mal pro Sekunde ab und hast 125 Impulse pro Sekunde. 
Ich bin mir nicht sicher, ob du nicht schneller abtasten müsstest.

von Michael M. (michael89)


Bewertung
0 lesenswert
nicht lesenswert
Hi, hier ist mal ein PID-Regler in eine Funktion geschrieben.. kann 
leider nicht sehen wo der Fehler bei dir liegt, aber evtl kannst du 
damit was anfangen.


float PIDRegler(float Drezahl1,float Drehzahl2)    // PID-Regler
{
  // Diese Werte müssen gespeichert werden
static float En=0.0,en1=0.0,en2=0.0;
static float Yn=0.0,yn1=0.0;
static int Yna;

  // Empfindlichkeit des PID-Reglers
float Tn = 1.0;
float Tv = 1.0;
float Kp = 1.0;

  // Variablen für P,I,D
float P,I,D;

  // Zeitkostante

float delta_t=0.1;    // Zykluszeit bei der die Funktion aufgerufen wird 
[s]
float delta_y;

En = Drezahl1 - Drezahl2;  // "Regelgröße" Drezahl2 wird an Drezhal1
                           // angeglichen
// Berechnugn P I D
P = En - en1;
D = (delta_t / Tn) * En;
I = Tv * (En - 2 * en1 + en2);

// Berechnung neuer Stellgröße
delta_y = Kp * ( P + D + I);
Yn = yn1 + delta_y;

// Speichert alte Werte
en2 = en1;
en1 = En;
yn1 = Yn;

// Begrenzt Yn auf 100
if (Yn > 100.0) Yn=99.99;
if (Yn <   0.0) Yn=  0.0;

return Yn;          // Gibt Stellgröße zurück und springt ins 
Hautprogramm zurück
}

Die Funktion muss Zyklisch aufgerufen werden.

MfG
M.V

: Bearbeitet durch User
von Steff G. (steff_123)


Bewertung
0 lesenswert
nicht lesenswert
Vielen Dank für eure Antworten.

Ich hoffe ich hab mein Problem soweit eingrenzen können.

Die Zeile :
1
float y = ((counter_IMPULSE/5.0)/(counter5_OVERFLOW*20.0))* 60000.0;  // Regelgroesse y - Motordrehzahl berechnen
ist das Problem, da mir dieser bei einer Abtastrate von 20 ms nur zu 
bestimmten Motordrehzahlen eine Flanke liefert und ich hier nach Flanken 
die Regelgroesse berechne.

Jetzt versuche ich die Zeit zwischen zwei Interrupts zu messen und somit 
die aktuelle Drehzahl zu berechnen.
Melde mich dann

Darüber hinaus hätte ich grundsätzlich noch eine Frage:

Grundsätzlich wollte ich die Drehzahlen eig. kontinuierlich an den PC 
schicken.
Wo würdet ihr diesen Befehl denn dann einbauen, wenn nicht direkt in die 
Regelschleife?

Oder die auftretenden in ein char Array schreiben und zum Schluss erst 
die kompletten Drehzahlen als Paket versenden?
Es muss nämlich berücksichtigt werden, dass auch noch Drehmomentdaten 
via der seriellen Schnittstelle versendet werden sollen (ca. alle 100 
ms).

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.