Forum: Compiler & IDEs Motorenreglung: was mache ich denn falsch?


von Luca B. (lucabert)


Lesenswert?

Hallo, Leute!

Endlich habe ich wieder Zeit für meinen Roboter...
Nun, auf dem Tisch habe ich das Problem mit der Reglung der 
Rädermotoren.
Ich habe eine Schaltung gemacht, die mit PWM die Motoren steuert, und 
mit einer optischen Encoder pro Motor eine Rückmeldung kriegt.
Nun soll das Programm geschrieben werden.

Schon vor X Monaten hatte ich das Programm für den "P" (Proportional) 
geschrieben, ich wollte jetzt erweitern mit dem "I" (Integral).

Das ist mein Code:
1
  uint16_t pwm, nSpeed, err, p, l, r;
2
3
  l = getLeftPWM();
4
  r = getRightPWM();
5
  if(getLeftInterval() != 0 && getDirectionLeft() != DIR_STOP)
6
  {
7
    nSpeed = (1000 / getLeftInterval());
8
    if(nSpeed != getSpeed())
9
    {
10
      p = getLeftPWM() * getSpeed();
11
      pwm = p / nSpeed;
12
      err = p % nSpeed;
13
      if(err <= (nSpeed / 2))
14
        errL += err;
15
      else
16
      {
17
        pwm++;
18
        errL -= err;
19
      }
20
      if(errL < 0)
21
      {
22
        while(abs(errL) >= nSpeed)
23
        {
24
          pwm--;
25
          errL += nSpeed;
26
        }
27
      }
28
      else
29
      {
30
        while(errL >= nSpeed)
31
        {
32
          pwm++;
33
          errL -= nSpeed;
34
        }
35
      }
36
      if((pwm >= MINPWM) && (pwm <= MAXPWM))
37
        l = pwm;
38
      else if(pwm < MINPWM)
39
        l = MINPWM;
40
      else
41
        l = MAXPWM;
42
      setLeftCorrectionDone();
43
      turnOnLED(LED2);
44
    }
45
    else
46
    {
47
      turnOffLED(LED2);
48
    }
49
  }
50
// das gleiche für den linken Motor...
51
// dann:
52
  setLeftPWM(l);
53
  setRightPWM(r);
54
  TCCR1A |= _BV(COM1A1) | _BV(COM1B1) | _BV(WGM11);
55
  TCCR1B |= _BV(WGM13) | _BV(CS12) | _BV(CS10);
56
  ICR1 = ICR_LIMIT;

Die Funktionen getLeftPWM() und setLeftPWM() lesen, bzw. setzen den 
neuen PWM-Wert. getLeftInterval() gibt zurück die Zeit, in 
Millisekunden, zwischen zwei Pegel des Encoders. getSpeed() gibt zurück 
den theoretische Geschwindigkeit, die ich erreichen will.

Also, das Programm funktioniert auch, aber ich habe immer eine 
Abweichung zwischen den Motoren...
Manchmal ist es klein, manchmal größer.
Um eine Großordnung zu geben, wenn ich die Motoren 10 Mal drehen lassen, 
habe ich eine Abweichung von 3-5 Schritte. Eine komplette Umdrehung des 
Motors sind 100 Schritte, also ist eine Abweichung von 3-5°...
Es mag sein, daß es klein aussieht, aber ich möchte das beste kriegen, 
was ich kriegen kann...

Eine komische Sache noch: auch wenn ich die Motoren länger drehen lasse, 
ist die Abweichung nicht proportionell größer. Also, nach 30 
Umdrehungen ist es nicht 9-15 Schritte, sondern vielleicht 4-7 
Schritte...

Ideen?

Danke
Luca Bertoncello

von Patrick B. (p51d)


Lesenswert?

Bei deinem Code werde ich nicht ganz schlau: Wie errechnest du den 
PWM-Wert? (hier in der Codesammlung ist sonst ein PID-Beispiel abgelegt, 
welches auch schon sehr gut ist, ansonsten OpenServo, da kannst du dir 
den Code auch ansehen)
OK, Motorregelung ist nicht ganz einfach. Da habe ich mir auch schon die 
Zähne ausgebissen.
Lösungsansatz ist eine Kaskadenregelung. Soll heissen, du hast einen 
Stromregler und einen Geschwindigkeitsregler. Dabei muss der innerste 
Regler immer schneller (z.B alle 1ms) sein als die äusseren (hier z.B > 
2ms, besser 4ms).

Das Problem klingt ganz nach Schrittverlusten..., da du aber anscheinen 
nur die Geschwindigkeit und nicht noch die Position regeln willst, 
sollte das nicht zu einem grossen Problem werden.
Trajektorie ist hier auch hilfreich: Du hast die Anfangsgeschwindigkeit 
und die Endgeschwindigkeit. Hier solltest du mit Zwischenschritten 
arbeiten, um ein überschiessen zu vermeiden.
Wie liest du denn die Encoder ein? Über Interrupts?

Viel Erfolg
Patrick

von Luca B. (lucabert)


Angehängte Dateien:

Lesenswert?

Patrick B. schrieb:
> Bei deinem Code werde ich nicht ganz schlau: Wie errechnest du den
> PWM-Wert? (hier in der Codesammlung ist sonst ein PID-Beispiel abgelegt,
> welches auch schon sehr gut ist, ansonsten OpenServo, da kannst du dir
> den Code auch ansehen)

Naja, der Code habe ich gepostet... :)
Hier rechne ich genau das PWM-Wert...

> OK, Motorregelung ist nicht ganz einfach. Da habe ich mir auch schon die
> Zähne ausgebissen.
> Lösungsansatz ist eine Kaskadenregelung. Soll heissen, du hast einen
> Stromregler und einen Geschwindigkeitsregler. Dabei muss der innerste
> Regler immer schneller (z.B alle 1ms) sein als die äusseren (hier z.B >
> 2ms, besser 4ms).

Mmmm... Wie kann ich den Strom regeln?
Ich befürchte, daß ich nicht kann... Als Anhang den Schaltplan...

> Das Problem klingt ganz nach Schrittverlusten..., da du aber anscheinen
> nur die Geschwindigkeit und nicht noch die Position regeln willst,
> sollte das nicht zu einem grossen Problem werden.
> Trajektorie ist hier auch hilfreich: Du hast die Anfangsgeschwindigkeit
> und die Endgeschwindigkeit. Hier solltest du mit Zwischenschritten
> arbeiten, um ein überschiessen zu vermeiden.

Kannst du mir erklären, wie ich das machen kann?

> Wie liest du denn die Encoder ein? Über Interrupts?

Alle Millisekunden rufe ich folgende Routine auf (eine für jeden Motor):
1
  if((lastLeftStatus == HIGH) && bit_is_clear(ENCODER_PIN, ENCODERL_PIN))
2
  {
3
    lastLeftStatus = LOW;
4
  }
5
  if((lastLeftStatus == LOW) && bit_is_set(ENCODER_PIN, ENCODERL_PIN))
6
  {
7
    nStepLeft++;
8
    lastLeftStatus = HIGH;
9
    intervalL = getUptime() - lastL;
10
    lastL = getUptime();
11
  }

Grüße
Luca Bertoncello

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.