www.mikrocontroller.net

Forum: GCC Kennlinien-Linearisierung mit pow()

Autor: bounceboy (Gast)
Datum: 14.05.2008 14:29

Guten Tag,

ich versuche mich gerade daran, eine stromgesteuerte Bremse mit einem
AVR zu anzusteuern (funktioniert mit PWM auch ganz gut). Der AVR soll
zudem über ein Display das aufgebrachte Drehoment anzeigen. Da der
Drehmomentverlauf nicht ganz linear ansteigt, habe ich das Drehmoment an
der Welle über den Duty Cycle meiner PWM gemessen und den Verlauf in
Excel mit einer Trendlinie nachvollzogen. Laut Excel lautet die Formel
zur Trendlinie 6. Grades:

y = 1E-10x^6 - 3E-08x^5 + 4E-06x^4 - 0,0003x^3 + 0,0087x^2 - 0,0227x

Mithilfe dieser Formel soll der AVR nun das Drehmoment berechnen:

unsigned int pwm_prozent;               // PWM-Prozentwert 0...100
double moment_nm;

double berechne_moment_nm(void)
{
moment_nm = (((double)(0.0000000001 * (pow(pwm_prozent, 6)))) -
((double)(0.00000003 * (pow(pwm_prozent, 5)))) + ((double)(0.000004 *
(pow(pwm_prozent, 4)))) - ((double)(0.0003 * (pow(pwm_prozent, 3)))) +
((double)(0.0087 * (pow(pwm_prozent, 2)))) - ((double)(0.0227 *
pwm_prozent)));

return moment_nm;

}

Allerdings liefert mir der Controller nicht die erwarteten Ergebnisse.
Als Controller verwende ich einen Mega16 mit dem Pollin-Board, als
Compiler dient CodeVision, die math.h habe ich ebenfalls eingebunden!

Kann das mit meinen Datentypen zusammenhängen? Habe auch schon mal
gehört, dass es Probleme mit der pow-Funktion gibt, kann mir hier jemand
weiter helfen?

Vielen Dank schon mal im Voraus!
Autor: Karl heinz Buchegger (kbuchegg) (Moderator)
Datum: 14.05.2008 14:56

Schon mal was von der Horner Regel gehört?

y = a*x^2 + b*x

ist identisch zu

y = ( ( a * x ) + b ) * x;

(x wird einfach herausgehoben).
Damit wirst du die ganzen pow Aufrufe los, was der Genauigkeit
deiner Rechnerei zugute kommen dürfte (und schneller geht es
nebenbei auch noch)

y = (((((( 1E-10 * x ) - 3E-08 ) *  x + 4E-06 ) * x - 0,0003 ) * x +
      0,0087 ) * x - 0,0227 ) * x;

Wie sinnvoll es ist, die 6., 5. und 4. Potenz mit einzubeziehen
ist eine andere Frage, die man untersuchen müsste. Mit einem
WinAVR double, hast du gerade mal 6 bis 7 signifikante Stellen.
Und das wird dann schon mächtig eng.
Autor: yalu (Gast)
Datum: 14.05.2008 15:32

> Wie sinnvoll es ist, die 6., 5. und 4. Potenz mit einzubeziehen ist
> eine andere Frage,

Der Wertebereich für x geht von 0 bis 100. Für x=100 liegen alle fünf
Summanden des Terms in einer ähnlichen Größenordnung, so dass man
keinen weglassen sollte.
Autor: yalu (Gast)
Datum: 14.05.2008 15:45
Dateianhang: plot.png (12,3 KB, 86 Downloads)
preview image for plot.png

Die Funktion sieht für eine Bremse ja schon recht seltsam aus :)
Oder sollte x vielleicht doch nur von 0 bis 1 gehen?
Autor: I_ H. (i_h)
Datum: 14.05.2008 18:54

pow scheint im AVR-GCC nicht zu funktionieren. Für ganzzahlige
Exponenten ist das aber schnell selber implementiert.
Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum: 15.05.2008 09:34

I_ H. wrote:

> pow scheint im AVR-GCC nicht zu funktionieren.

Was genau funktioniert nicht?
Autor: I_ H. (i_h)
Datum: 15.05.2008 11:41

Garnix funktioniert. Das Problem hatte ich schonmal vor einer Weile.
Spuckt irgendwelche Ergebnisse aus, die mit der Realität nix zu tun
haben.

Beitrag "pow()-Funktion"
Autor: Patrick Neuberger (pneuberger)
Datum: 15.05.2008 12:16

Hallo,

bau die Formel um. Nimm das Horner-Schema von Karl-Heinz. Weiterhin
würde ich darüber nachdenken, die doubles in der Formel mit 1E10 zu
multiplizieren, mit unsigned Ganzzahlen zu rechnen und das Ganze am
Schluß zu dividieren, weil double beim GCC nur float-Genauigkeit hat.


MfG

Patrick
Autor: Patrick Neuberger (pneuberger)
Datum: 15.05.2008 12:58

Waaah... signed meine ich natürlich.
Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum: 15.05.2008 13:26

I_ H. wrote:
> Garnix funktioniert. Das Problem hatte ich schonmal vor einer Weile.
> Spuckt irgendwelche Ergebnisse aus, die mit der Realität nix zu tun
> haben.
>
> Beitrag "pow()-Funktion"

Schnee von gestern, davon abgesehen, dass dort auf Threads von 2004
verwiesen wird.  Kurzes Testprogramm:
Enter x, y: 3 3
pow(3, 3) = 27
Enter x, y: 3 5
pow(3, 5) = 243
Enter x, y: 5 3
pow(5, 3) = 125
Enter x, y: 2.54 3
pow(2.54, 3) = 16.3871
Enter x, y: 1e-5 2
pow(1e-05, 2) = 1e-10
Enter x, y: 1e-5 25
pow(1e-05, 25) = 0
Enter x, y: 2.5 2.5
pow(2.5, 2.5) = 9.88211
Enter x, y: -2.5 3
pow(-2.5, 3) = -15.625
Enter x, y: 10 3
pow(10, 3) = 1000
Enter x, y: 3 10
pow(3, 10) = 59049
Enter x, y: 2 10
pow(2, 10) = 1024
Enter x, y: 9 9
pow(9, 9) = 3.8742e+08
Enter x, y: 

Ziemlich viel für "garnix funktioniert", oder?

Welche Zahlen soll ich dir noch eintippen?
Autor: I_ H. (i_h)
Datum: 15.05.2008 14:54

Also vor ein paar Monaten hatte ich immernoch das Problem, auch mit
einer aktuellen Verion. Btw. eintippen - du hast das aber schon auf 'nem
AVR laufen lassen?
Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum: 15.05.2008 15:19

I_ H. wrote:

> Btw. eintippen - du hast das aber schon auf 'nem
> AVR laufen lassen?

Ja, na klar.  Ich habe immer irgendwo ein Gerippe für eine einfache
stdio-Applikation via UART rumliegen, da habe ich einfach ein
scanf() und ein printf() in einer Endlosschleife reingestopft.

Wie gesagt, wenn du für die aktuelle Version (1.6.x) irgendetwas
findest, wo pow() nicht geht (oder nicht im Rahmen der machbaren
Genauigkeit), dann schreibe einen Bugreport.  Momentan sehe ich
keinen offenen.
Autor: I_ H. (i_h)
Datum: 15.05.2008 16:31

Hab damals das neueste draufgemacht was der gentoo portage tree hergab
(öhm... libc 1.4.6), und damit hat's nicht funktioniert. Hatte damals
auch den passenden Bugreport gefunden, und mir dann pow(10, ...) selber
geschrieben.

Aber gut, wenn's inzwischen gefixt ist. Inzwischen ist 1.6.x auch im
portage tree angekommen.
Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum: 15.05.2008 19:26

Ja, seit dem Weggang von Henrik Brix Andersen ist Gentoo's Support
für die AVR-Toolchain ziemlich hinten runtergefallen, habe ich den
Eindruck.
Autor: andreas (Gast)
Datum: 16.05.2008 18:05

Hallo zusammen!

An bounceboy: Bist du sicher, dass deine Interpolationsfunktion stimmt.
Ich hatte mal den Fall bei einem NTC. Habe Werte aufgenommen und wollte
die von Excel interpoliert haben. Ab dem dritten Grad kam nur noch
Schrott raus. Die Interpolationspolynome haben die Stützstellen nicht
mehr getroffen!

Gib doch einmal die Interpolationsfunktion in Excel ein, und lass sie
über die Messwerte legen.
Autor: bounceboy (Gast)
Datum: 16.05.2008 22:09

Hallo, vielen Dank erst mal für eure Antworten...des Rätsels Lösung lag
doch etwas näher als gedacht. Die Funktion, die mir Excel ausgegeben hat
ist völliger Schwachsinn, warum auch immer?!? Habe das ganze mal in
meinen grafikfähigen Taschenrechner eingedaddelt und komme auf das
gleiche wie Yalu (Danke an dieser Stelle an Yalu und Andreas). Gibt es
irgendwelche andere Möglichkeiten, Kennlinien mit durch Punkte
mathematisch anzunähern?
Autor: yalu (Gast)
Datum: 16.05.2008 22:46

Eine Interpolation bzw. Approximation durch ein Polynom n-ten Grades
ist nicht immer geschickt. Oft ist es so, dass ein niedriger Grad zu
ungenau wird, da die Kurve zu weit an den vorgegebenen Punkte
vorbeiläuft, und ein hoher Grad zwar die Punkte trifft, aber zu
unschönen Wellen zwischen den Punkten führt.

Am besten ist es, wenn man die Physik hinter der ganzen Sache
verstanden hat und eine allgemeine Funktion aufstellen kann, deren
Parameter dann mit Hilfe der Stützpunkte geschätzt werden können.

Oder man erkennt durch scharfes Anschauen der Punkte und ein wenig
Ausprobieren, welcher Funktionstyp gut passen würde und schätzt die
Parameter dann ebenfalls aus den Stützpunkten.

An deiner Stelle würde ich die Messpunkte einfach mal posten,
vielleicht hat ja jemand eine passende Idee.
Autor: bounceboy (Gast)
Datum: 18.05.2008 11:30

Dann stell ich mal hier meine Parameter ein:

PWM[%]   M[Nm]
  0      0
  5      0,10
 10      0,36
 15      0,86
 20      1,32
 25      1,89
 30      2,29
 35      2,74
 40      2,96
 45      3,34
 50      3,59
 55      3,92
 60      4,19
 65      4,36
 70      4,57
 75      4,67
 80      4,86
 85      4,99
 90      5,04
 95      5,14
100      5,19

Wäre nett, wenn mir hier jemand mit einer sinnvollen und hinreichend
genauen Funktion für den Wertebereich von 0 bis 100% aushelfen könnte.
Vielen Dank
Autor: Ernst Bachmann (ernst)
Datum: 18.05.2008 15:49

Nimm die Stützstellen, wie in deinem Post angegeben, und interpolier
linear zwischen denen. Sollte hinreichend genau sein, und ist mit einer
Zeile C erledigt.
Autor: yalu (Gast)
Datum: 18.05.2008 16:42
Dateianhang: approx.png (16 KB, 26 Downloads)
preview image for approx.png

Ich habe gerade mal ein wenig rumgespielt. Mit Polynomen bin ich nicht
sehr weit gekommen, mit

y = a\left(1-\frac 1 {1+bx^2}\right)

bzw.
y = a - a / (1 + b * x*x)

schon eher. Durch "visuelles Probieren" bin ich auf a=6 und b=6.5e-4
gekommen.

Die Funktion ist relativ leicht zu berechnen (auf jeden Fall leichter
als ein Polynom 6ten Grades), und der Approximationsfehler könnte noch
halbwegs in der Größenordnung der Messfehler liegen.

Die Ergebnisse im Vergleich (ym: gemessen, ya: approximiert):

  x   ym    ya   Fehler
  0  0.00  0.00   0.000
  5  0.10  0.10  -0.004
 10  0.36  0.37   0.006
 15  0.86  0.77  -0.094
 20  1.32  1.24  -0.082
 25  1.89  1.73  -0.157
 30  2.29  2.21  -0.075
 35  2.74  2.66  -0.080
 40  2.96  3.06   0.099
 45  3.34  3.41   0.070
 50  3.59  3.71   0.124
 55  3.92  3.98   0.057
 60  4.19  4.20   0.014
 65  4.36  4.40   0.038
 70  4.57  4.57  -0.004
 75  4.67  4.71   0.041
 80  4.86  4.84  -0.023
 85  4.99  4.95  -0.043
 90  5.04  5.04   0.002
 95  5.14  5.13  -0.014
100  5.19  5.20   0.010
Autor: Bernd (Gast)
Datum: 23.05.2008 02:57

Kleiner Tip zu Excel:

y = 1E-10x^6 - 3E-08x^5 + 4E-06x^4 - 0,0003x^3 + 0,0087x^2 - 0,0227x

Das Ergebnis 1E-10x^6 ist zu ungenau. Dazu die errechnet Funktion mit
der rechten Maustaste anklicken und als Zahlenformat Wissenschaftlich
mit 3 oder 4 Nachkommastellen wählen.

Benötigst Du nicht die Umkehrfunktion?

Du möchtest z.B. mit 3 Nm bremsen und benötigst die Pulsweite dazu.
X und Y vertauschen und dann nochmal eine neue Funktion suchen.

Oder y = a - a / (1 + b * x*x) nach x umstellen

x = sqr((a/(a-y)-1)/b)
Autor: Bernd (Gast)
Datum: 23.05.2008 13:48
Dateianhang: Umkehrfunktion.gif (9,9 KB, 19 Downloads)
preview image for Umkehrfunktion.gif

Das Polynom laut Excel funktioniert ganz gut:

y = 5,437E-03x6 + 1,890E-01x5 - 2,976E+00x4
  + 1,503E+01x3 - 3,162E+01x2 + 3,667E+01x

Aber
y = sqr((a/(a-x)-1)/b)
mit a = 5.989, b = 0.00065 kostet weniger Rechenleistung.

Im Wertebereich x >= 0 und x <= 5.19 ist die Funktion stetig.
Aber x muss vor dem Aufruf der Wurzelfunktion überprüft werden.
int nm2pwm(float nm)
{
int pwm;

  if (nm < 0)
    pwm = 0;
  else
  if (nm > 5.19)
    pwm = 100;
  else
    pwm = sqr((5.989/(5.989-nm)-1)/0.00065); 

  return pwm;
}

Eine weitere Möglichkeit wäre, die Funktion aus 3 einfachen Polynomen 2.
Ordung in der Form: y = a0 + a1*x + a2*x*x zusammenzusetzten.
Die 3 Bereiche wären dann z.B. 0 - 1 Nm, 1 - 4 Nm und 4 - 5.19 Nm
int nm2pwm(float nm)
{
  if (nm < 0)
    return 0;
  else
  if (nm < 1)
    return  // hier Polynom 1
  else
  if (nm < 4)
    return  // hier Polynom 2
  else
  if (nm < 5.19)
    return  // hier Polynom 3
  else
    return 100;
}

Etwas Mühe macht es, die Polynome and den Grenzstellen genau
aufeinandertreffen zu lassen. Aber das Ergebnis wäre schnell und genau.

Antwort schreiben

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

Wichtige Regeln - erst lesen, dann posten!

  • Suchfunktion und Betreffsuche benutzen - vielleicht gibt es schon einen ähnlichen Beitrag
  • Aussagekräftigen Betreff wählen
  • Im Betreff angeben um welchen Controllertyp es geht (AVR, PIC, ...)
  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang
  • JPEG-Dateien (.jpg) nur für Fotos und Scans verwenden
  • Schaltpläne, Screenshots usw. als PNG oder GIF anhängen

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [pre]vorformatierter Text (z.B. Code in anderen Sprachen)[/pre]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel






webmaster@mikrocontroller.netImpressumWerbung auf Mikrocontroller.net