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!
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.
> 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.
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"
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
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:
1
Enter x, y: 3 3
2
pow(3, 3) = 27
3
Enter x, y: 3 5
4
pow(3, 5) = 243
5
Enter x, y: 5 3
6
pow(5, 3) = 125
7
Enter x, y: 2.54 3
8
pow(2.54, 3) = 16.3871
9
Enter x, y: 1e-5 2
10
pow(1e-05, 2) = 1e-10
11
Enter x, y: 1e-5 25
12
pow(1e-05, 25) = 0
13
Enter x, y: 2.5 2.5
14
pow(2.5, 2.5) = 9.88211
15
Enter x, y: -2.5 3
16
pow(-2.5, 3) = -15.625
17
Enter x, y: 10 3
18
pow(10, 3) = 1000
19
Enter x, y: 3 10
20
pow(3, 10) = 59049
21
Enter x, y: 2 10
22
pow(2, 10) = 1024
23
Enter x, y: 9 9
24
pow(9, 9) = 3.8742e+08
25
Enter x, y:
Ziemlich viel für "garnix funktioniert", oder?
Welche Zahlen soll ich dir noch eintippen?
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?
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.
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.
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.
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?
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.
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.
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)
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.
1
intnm2pwm(floatnm)
2
{
3
intpwm;
4
5
if(nm<0)
6
pwm=0;
7
else
8
if(nm>5.19)
9
pwm=100;
10
else
11
pwm=sqr((5.989/(5.989-nm)-1)/0.00065);
12
13
returnpwm;
14
}
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
1
intnm2pwm(floatnm)
2
{
3
if(nm<0)
4
return0;
5
else
6
if(nm<1)
7
return// hier Polynom 1
8
else
9
if(nm<4)
10
return// hier Polynom 2
11
else
12
if(nm<5.19)
13
return// hier Polynom 3
14
else
15
return100;
16
}
Etwas Mühe macht es, die Polynome and den Grenzstellen genau
aufeinandertreffen zu lassen. Aber das Ergebnis wäre schnell und genau.