Forum: Mikrocontroller und Digitale Elektronik PID Regler für DC Motor mit linearem Gray-Code Inkremantalgeber: Grundlegende Fragen


von Niels J. (niels)


Angehängte Dateien:

Lesenswert?

Mahlzeit,

ich versuche mich gerade daran, vorerst einmal nur die Geschwindigkeit 
eines Druckerschlittens (Spender: Epson Stylus) halbwegs unter Kontrolle 
zu bringen.
Vorab einmal zum System an sich, wie es hier auf dem Schreibtisch steht:
Mechanik: Wie gesagt, es handelt sich um einen Druckerschlitten aus 
einem Epson Stylus. Was ich übrig gelassen habe, ist die Führung aus 
Metall, den Schlitten aus Plastik, den DC-Motor als Antrieb mit 
Zahnriemen und den Gray-Code Plastikstreifen mit entsprechendem Sensor.
Achja, das Teil steht hochkant, die Bewegungsrichtungen sind also hoch 
und runter.
Elektronik:
Besagter Motor wird von einem halben LM298 und 12 V angetrieben. Über 
zwei Pins lässt sich die Drehrichtung des Motors bestimmen und über den 
Enable Pin per PWM die Geschwindigkeit.
Als Controller kommt ist ein AtMega328P vorhanden. Den Gray-Code lese 
ich über zwei Pins am Controller mittels PinChange-Interrupts und dem 
Code aus dem Artikel über Drehgeber aus.

Den Artikel über Regler von RN-Wissen habe ich mir auch schon zu Gemüte 
geführt, aber ein paar Sachen kann ich noch nicht ganz nachvollziehen:

Eingang/Ausgang eines Reglers:
Die Abweichung von Soll zu Ist, bzw. dessen Ableitung und Integral gehen 
in den Regler rein. Soweit so gut, ich will die Geschwindigkeit regeln, 
also kommt die Differenz der Positionen zum Zeitpunkt t und t-1 rein.
Als Ausgang habe ich dann eine 8 bit PWM, bzw. durch Richtungsänderung 
eine "9-bit signed PWM", zur Verfügung.
Aber wie bekomme ich diese beiden unterschiedlichen "Formate" unter 
einen Hut?
Die PWM geht von -255 .. 255, die Geschwindigkeit von ~ -23 d/ms bis 15 
d/ms.
Sollte ich versuchen das auf %-te gleich zu setzen, oder ist das dem 
Regler mit den richtigen Einstellungen sogar egal? Ein weiteres Problem 
ist, dass der Schlitten sich unterhalb eines PWM-Wertes von 190 gar 
nicht mehr bewegt, also habe ich im Ausgabebereich sogar noch sowas wie 
einen "blinden Fleck", in dem gar nichts passiert.

Ki/Kp/Kd eines Reglers:
Ja, ich habe gelesen, dass man hier ordentlich spielen kann, bestenfalls 
erstmal Ki und Kd auf 0 setzen und dann mit Kp loslegen. Aber wie sind 
denn die ganz grob ungefähren Bereiche, in denen man sich hier bewegt? 
Kd = 1,00 - 1,99, oder 0 - 200 oder, oder, oder? Ich will mich halt 
nicht in Nachkommaschritten an etwas herantasten, dass erst ab 100 oder 
so irgendwas macht.

Ich hoffe ich konnte meine Unklarheiten halbswegs greifbar 
formulieren...

Achja, damit das System von dem ich hier rede nicht ein vollkommen 
unklares verbal beschriebenes Gebilde bleibt, anbei noch ein paar 
Diagramme, wie es sich denn so bei konstanter PWM verhält.
200d in der Legende steht dann für PWM: 200, Richtung down
220u entsprechend für PWM 220, nach oben fahrend, usw.
Zu den Einheiten: das "d" in "d/10ms" steht einfach für "dots" aus dem 
Encoder. Die gesamte Strecke ist ~9000 von diesen "dots" lang.

Beste Grüße

Niels

von Pandur S. (jetztnicht)


Lesenswert?

Ich wuerd den blinden Fleck wegmachen. Also zu einem Stellwert von +1 
oder mehr wird 190 addiert, bei einem Stellwert von -1 oder tiefer wird 
190 subtrahiert.

Bei mir laufen die Regelungen ueblicherweise mit Longint Zahlen. Am 
Schluss wird fuer das Stellglied dann runterskaliert. zB auf 0-255. So 
kann man alle Rechnungen mit Ganzzahlen durchfuehren.

von Karl H. (kbuchegg)


Lesenswert?

Niels J. schrieb:

> Ki/Kp/Kd eines Reglers:
> Ja, ich habe gelesen, dass man hier ordentlich spielen kann, bestenfalls
> erstmal Ki und Kd auf 0 setzen und dann mit Kp loslegen. Aber wie sind
> denn die ganz grob ungefähren Bereiche, in denen man sich hier bewegt?

Kann man nicht sagen.

> nicht in Nachkommaschritten an etwas herantasten, dass erst ab 100 oder
> so irgendwas macht.

NIemand hindert dich, da ordentliche Schritte zu machen.
Probierst du 0.1 und nichts tut sich, dann probierst du eben 0.2
Tut sich dann immer noch nichts, dann probierst du 100.
Jetzt kann es sein, dass dein Schlitten einen ordentlichen Satz macht, 
dann probierst du eben mal 50.
Oder es tut sich nichts, dann probierst du 200.

Erst mal musst du eine unegfähre Vorstellung davon kriegen, in welchem 
Zahlenbereich dein Reglerwert zirka liegen wird. Klar kann man das auch 
ausrechnen, insbesondere wenn man die Fehlerwerte kennt, denn mehr als 
Vollgas kann auch dein Regler nicht geben und daraus kann man 
zurückrechnen, welcher Kp Wert bei bekanntem Fehlerwert zu Vollgas 
führt. Aber meistens muss man nicht päpstlicher als der Papst sein. Es 
ist ja nicht so, dass bei dir dann der Kernreaktor durchgeht, wenn dein 
Reglerwert vollkommen daneben liegt.
SIehs mal so: Als du das erste mal selber Auto gefahren bist, hattest du 
auch keine Ahnung, wie stark du aufs Gaspedal treten musst. War das ein 
Problem? Nicht wirklich: in ein paar Sekunden hattest du das raus, 
wieviel es ungefähr sein muss. Ruckelt es eben beim ersten mal ein 
wenig, aber nach dem 10ten mal anfahren hattest du den Dreh raus.

Eine Hand am Schalter der Stromversorgung hat noch nie geschadet.

von Niels J. (niels)


Angehängte Dateien:

Lesenswert?

Da ich so vom TE unbeantwortete Threads selbst nicht ab kann, hier 
einmal meine Résumes:

Oder D. schrieb:
> Ich wuerd den blinden Fleck wegmachen. Also zu einem Stellwert von +1
> oder mehr wird 190 addiert, bei einem Stellwert von -1 oder tiefer wird
> 190 subtrahiert.

Danke für den Rat, werde ich wahrscheinlich auch noch machen, wenn ich 
die "Nullwerte" genauer ermittelt habe. Bis dahin tut's auch ein kleiner 
I Anteil, dauert dann ungefähr 10 ms länger, bis sich was bewegt.


Karl H. schrieb:
> SIehs mal so: Als du das erste mal selber Auto gefahren bist, hattest du
> auch keine Ahnung, wie stark du aufs Gaspedal treten musst. War das ein
> Problem? Nicht wirklich: in ein paar Sekunden hattest du das raus,
> wieviel es ungefähr sein muss. Ruckelt es eben beim ersten mal ein
> wenig, aber nach dem 10ten mal anfahren hattest du den Dreh raus.

Danke für das anschauliche Sinnbild :D Nachdem ich mich dann mal dran 
gemacht habe, noch mehr Variablen meines Programms von außen 
veränderlich zu machen, und ich mir noch ein kleines Hilfstool zum 
Einstellen geschrieben hatte, bekam ich doch tatsächlich schnell ein 
Gefühl für das Gaspedal ;)

Anbei noch ein Screenshot des Hilfstools mit grafischer Darstellung des 
Geschwindigkeits- und Reglerausgangsverlaufs.

Beste Grüße

Niels

von c-hater (Gast)


Lesenswert?

Niels J. schrieb:

> ich versuche mich gerade daran, vorerst einmal nur die Geschwindigkeit
> eines Druckerschlittens (Spender: Epson Stylus) halbwegs unter Kontrolle
> zu bringen.
[...]

Dazu wäre als erstes zu überprüfen, ob deine Lösung für die 
Eingangsgröße auch bei maximaler Verfahrgeschwindigkeit noch zuverlässig 
zählt (was ich ernsthaft bezweifeln würde).

Weil: wenn aus der Istwert-Gewinnung schon nur Mist rauskommt, kannst du 
an den Reglerkoeffizienten rumspielen, bis du in der Kiste liegst, da 
wird niemals ein vernünftiges Ergebnis bei rauskommen können...

von Niels J. (niels)


Lesenswert?

c-hater schrieb:
> Dazu wäre als erstes zu überprüfen, ob deine Lösung für die
> Eingangsgröße auch bei maximaler Verfahrgeschwindigkeit noch zuverlässig
> zählt (was ich ernsthaft bezweifeln würde).

Ich wüsste jetzt nicht, warum das anzuzweifeln wäre. Die Strecke ist 
9000 "Ticks" lang und die maximale Geschwindigkeit ungefähr 30 Ticks/ms. 
Somit wird das Pin Change Interrupt mit maximal 33 kHz aufgerufen, ich 
denke mal, dabei langweilt sich der Controller noch mehr als genug. Auch 
sonst konnte ich bei "so schnell mit der Hand verfahren wie möglich" 
noch keinen Drift feststellen.

Beste Grüße

Niels

von Wolfgang A. (Gast)


Lesenswert?

Niels J. schrieb:
> ich denke mal, dabei langweilt sich der Controller noch mehr als genug.

Da würde ich mal ganz kritisch im Auge behalten, wieviel Zeit er noch 
außerhalb der ISR "frei" hat - nur so zur Vorsicht, bevor es komische 
Effekte gibt. Ich weiß ja nicht, wieviel Rechnerei du ihm sonst noch so 
aufgetragen hast.

von c-hater (Gast)


Lesenswert?

Niels J. schrieb:

> Ich wüsste jetzt nicht, warum das anzuzweifeln wäre. Die Strecke ist
> 9000 "Ticks" lang und die maximale Geschwindigkeit ungefähr 30 Ticks/ms.
> Somit wird das Pin Change Interrupt mit maximal 33 kHz aufgerufen

Hmm. 33kHz ISR-Frequenz ist schon nicht unbedingt wenig, Es hängt aber 
natürlich auch vom verfügbaren Systemtakt ab, was genau damit 
tatsächlich möglich ist.

Ich kann in deinen bisherigen Äußerungen nur leider keinerlei Angaben 
dazu finden. Habe ich aber möglicherweise auch im dritten Anlauf einfach 
bloß überlesen...

Und während du das rauskramst: bitte auch gleich noch den Code der 
entsprechenden ISR hinzufügen. Auch der glänzt im Thread bisher nur 
durch eins: vollständige Absenz.

von Niels J. (niels)


Lesenswert?

Immer mit der Ruhe zu so später Stunde ;)
Meine eigentliche Frage wurde inzwischen ja schon längst geklärt. Damit, 
dass meine mir selbst auferlegte Aufgabenstellung noch solche Tücken 
aufweisen könnte, habe ich nicht gerechnet.
Den Code reiche ich trotzfen erst morgen nach, dafür ist es mir jetzt zu 
spät.

Bis dahin noch einen entspannten Samstag Abend

von Conny P. (conny_phi)


Lesenswert?

Hallo Niels,
Deine Beschreibungen über den Druckerschlitten, der sich auf und ab 
bewegt, haben mich neugierig gemacht. Magst Du verraten, wie das Projekt 
am Ende aussehen soll?

von Niels J. (niels)


Angehängte Dateien:

Lesenswert?

Sodele..

c-hater schrieb:
> Hmm. 33kHz ISR-Frequenz ist schon nicht unbedingt wenig, Es hängt aber
> natürlich auch vom verfügbaren Systemtakt ab, was genau damit
> tatsächlich möglich ist.
>
> Ich kann in deinen bisherigen Äußerungen nur leider keinerlei Angaben
> dazu finden. Habe ich aber möglicherweise auch im dritten Anlauf einfach
> bloß überlesen...

Ich kann dich beruhigen, der Code war tatsächlich noch nicht zu finden 
;)
Die Funktion wird jedesmal aufgerufen, wenn sich an den Pins des Encoder 
Ausgangs was ändert. Ich hatte mich bei meiner Geschwindigkeitsangabe 
allerdings noch geirrt, die beträgt maximal ~15 Ticks/ms daraus 
resultiert also eine Frequenz von ungefähr 16.7 kHz
1
//Encode the Gray Code to an absolute Position
2
void encode_gray(void)
3
{
4
  int8_t new, diff;
5
 
6
  new = 0;
7
  if( PHASE_A )
8
    new = 3;
9
  if( PHASE_B )
10
    new ^= 1;                   // convert gray to binary
11
  diff = last - new;                // difference last - new
12
  if( diff & 1 ){               // bit 0 = value (1)
13
    last = new;                 // store new as next last
14
    position_current += (diff & 2) - 1;        // bit 1 = direction (+/-)
15
  }
16
}

In der Main Schleife wird dann "nur" noch einmal pro ms der PID 
Algorithmus ausgeführt:
1
if(pid_tick)
2
    {  
3
      pid_tick = 0;
4
      w = speed;
5
      x = position_current - position_old;
6
      position_old = position_current;
7
      esum += e;
8
      e = w - x;
9
      y = Kp * e + Ki * Ta * esum + Kd * (e-ealt)/Ta;
10
      ealt = e;
11
      if (y>255)
12
        y = 255;
13
      if (y<-255)
14
        y = -255;
15
      uart_putc(x>>8);
16
      uart_putc(x);
17
      uart_putc(y>>8);
18
      uart_putc(y);
19
      setSpeed(y);
20
    }

Der ATMega328P läuft aktuell mit 11.0592 MHz, ich muss mal noch nach 
meinen anderen Quarzen suchen, das war halt gerade auf dem STK500.

Anbei ist noch die gesamte main.c. Die ist allerdings noch sehr spärlich 
kommentiert, etliche Variablen lassen sich sicher noch optimieren etc. 
Hauptsächlich ist er darauf ausgelegt erstmal zu funktionieren, was er 
auch tut (Zumindest unterliege ich bisher noch diesem Glauben)


Conny P. schrieb:
> Hallo Niels,
> Deine Beschreibungen über den Druckerschlitten, der sich auf und ab
> bewegt, haben mich neugierig gemacht. Magst Du verraten, wie das Projekt
> am Ende aussehen soll?

Wenn ich mich mal wirklich soweit zusammenreiße, das bis zum Ende durch 
zu ziehen, soll es ein Kossel 3D Drucker werden. Mehr dazu hier: 
http://reprap.org/wiki/Kossel

Aber anstatt der Schrittmotoren und den teuren Führungen halt mit "2D 
Drucker Innereien". 3 * 2D macht dann 3D ;)

So, soviel erstmal dazu, ich widme mich dann mal wieder dem unsonnigen 
Sonntag

Niels

von c-hater (Gast)


Lesenswert?

Niels J. schrieb:

> Die Funktion wird jedesmal aufgerufen, wenn sich an den Pins des Encoder
> Ausgangs was ändert.
1
//Encode the Gray Code to an absolute Position
2
void encode_gray(void)
3
{
4
  int8_t new, diff;
5
 
6
  new = 0;
7
  if( PHASE_A )
8
    new = 3;
9
  if( PHASE_B )
10
    new ^= 1;                   // convert gray to binary
11
  diff = last - new;                // difference last - new
12
  if( diff & 1 ){               // bit 0 = value (1)
13
    last = new;                 // store new as next last
14
    position_current += (diff & 2) - 1;        // bit 1 = direction (+/-)
15
  }
16
}
17
18
ISR(PCINT1_vect)
19
{
20
  encode_gray();  
21
}

Das sieht schonmal garnicht gut aus. Es würde mich nicht wundern, wenn 
die Laufzeit des erzeugten Codes über 100 Takte ist. Du hast wirklich 
alles unternommen, um dem Compiler eine wenigstens auch nur halbwegs 
effiziente Umsetzung nicht zu ermöglichen. Wie wär's mal mit einem 
"inline"?

Bei 33kHz ISR-Frequenz bräuchtest du also ca. 3MHz Takt, um auch nur die 
Positionserfassung zu machen. Und da dürfte dann nix anderes stören. Tut 
es aber, denn es gibt ja noch die genauso bescheuert gestrickte zweite 
ISR...

Das zusammen könnte sogar bei 11MHz schon eng werden. Hoffentlich bist 
du wenigstens sicher, daß das Teil wirklich mit den angegeben 11Mhz 
läuft. Fuses?

von Niels J. (niels)


Lesenswert?

c-hater schrieb:
> Das sieht schonmal garnicht gut aus. Es würde mich nicht wundern, wenn
> die Laufzeit des erzeugten Codes über 100 Takte ist. Du hast wirklich
> alles unternommen, um dem Compiler eine wenigstens auch nur halbwegs
> effiziente Umsetzung nicht zu ermöglichen. Wie wär's mal mit einem
> "inline"?
>
> Bei 33kHz ISR-Frequenz bräuchtest du also ca. 3MHz Takt, um auch nur die
> Positionserfassung zu machen. Und da dürfte dann nix anderes stören. Tut
> es aber, denn es gibt ja noch die genauso bescheuert gestrickte zweite
> ISR...

Charmant.. wenn gleich auch absolut richtig. Deine Vermutung mit den 100 
Takten hat mich dann doch mal etwas stutzig gemacht, also mal in die 
main.lss geguckt und siehe da: In der ISR Routine werden erstmal ALLE 
Register zwischengespeichert. Also die main.c editiert und neu 
kompiliert. Jetzt fallen acht mal push und acht mal pull weg, alleine 
das spart ja schon mal 32 Takte. Danke dafür :)
Da die andere Funktion, die ich in der Timer ISR aufgerufen habe, 
inwzwischen ja auch nur noch eine Zeile lang war habe ich den Code 
direkt in die ISR verfrachtet, da sollte jetzt auch wesentlich weniger 
unnötiger Overhead produziert werden.

Hast du sonst noch andere derartig grobe Patzer gefunden, oder ist's 
soweit erstmal erträglich?

Beste Grüße

Niels

PS.: Bei den ~11 MHz bin ich mir sicher, anfänglich hatte ich noch eine 
LED im Sekundentakt am blinken, einfach als sichtbares Lebenszeichen.

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.