Hallo zusammen,
in der Application Note AVR446: Linear speed control of stepper motor
ist eine schöne Beschleunigungsrampenberechnung für Schrittmotoren
erklärt.
Dort werden folgende Formeln verwendet:
und dann weiter umgeformt zu:
wobei
und
Die berechnen also das erste Delay c0, merken sich dieses, und berechnen
alle weiteren Delays basierend auf diesem initialen Delay. Soweit
versteh ich das einigermaßen.
Nun bräuchte ich das etwas anders: Ich möchte gerne c(n) nicht aus c(0)
berechnen, sondern aus c(n-1)
Ich bin mir fast sicher dass ich mal gelernt habe wie das geht, aber ich
fürchte damals hab ich krankheitshalber gefehlt :-)
mathe ist leider nicht gerade meine Stärke.... kann mir da jemand
behilflich sein?
(außerdem bin ich ganz stolz auch mich, weil ich grad zum ersten mal in
meinem leben eine Latex-Formel gesetzt habe!)
Michael Reinelt schrieb:> Nun bräuchte ich das etwas anders: Ich möchte gerne c(n) nicht aus c(0)> berechnen, sondern aus c(n-1)
was erhoffst du dir dadurch ?
Andi ... schrieb:> was erhoffst du dir dadurch ?
ich hab doch gewusst dass diese frage kommt ;-)
Normalerweise will man mit einem Stepper "Positionen" anfahren. Dafür
ist die Rampenberechnung mit c0 sicherlich gut geeignet.
für meine Anwendung interessieren mich aber keine absoluten Positionen,
sondern nur (Winkel-)Geschwindigkeiten bzw. Drehzahlen. Das heisst ich
drehe momentan mit n1 Steps/Second (im einfachsten Fall 0, also
Stillstand) und will n2 Steps/Second erreichen, muss also beschleunigen
oder bremsen. Noch schlimmer wirds wenn ich während des
Beschleunigens/Bremsens eine neue Solldrehzahl bekomme.
Test schrieb:> Hallo,>> das steht doch auch in der App note>>
>
Oh, das hätte ich überlesen....
> oder meinst du etwas anderes?
Triffts fast: mich stört das "4n+1". Ich möchte zu einem beliebigen
Zeitpunkt (sprich: sehr großes bzw. beliebiges n) aus momentaner
Geschwindigkeit (sprich: c(n-1) ) und neuer Sollgeschwindigkeit das
aktuelle Counter-delay errechnen.
> Das hört sich stark nach einem Regler an was du realisieren möchtest.
Nein, hat mit Regler nix zu tun. ich möchte einfach nur
geschwindigkeiten stellen können, unter der berücksichtigung der
aktuellen geschwindigkeit.
Michael Reinelt schrieb:> Triffts fast: mich stört das "4n+1".
Ich würde versuchen, die Formel für c_n-1 aufzustellen -
dann erscheinen natürlich c_n-2 und 4(n-1)+1 auf der
rechten Seite. Diese neue Formal dann nach n umstellen
und n in der Ursprungsformel substituieren.
ich hab das jetzt in der AppNote gefunden: Das ist aber schon die
Approximation mittels Taylor-Reihe...
ich glaub ich habs aber selbst geschafft (mit etwas Hilfe von Wolfram
Alpha):
der term
lässt sich folgendermaßen aus c(n-1) errechnen:
Der Lesbarkeit halber habe ich c(n-1) durch c ersetzt...
Damit bin ich zumindest mathematisch mal am Ziel. Allerdings möchte ich
das so am AVR nciht berechnen :-) wird wohl noch eine Approximation
(oder eine Lookup-Tabelle) werden...
Ich verstehe dein Problem immer noch nicht. Normalerweise legt man sich
die Tabelle vorberechnet in den Speicher. Ob man dann den Motor bei v=0
oder v=v1 loslaufen lässt, ändert nur den Startpunkt in der Tabelle.
Mit freundlichen Grüßen
Thorsten Ostermann
Thorsten Ostermann schrieb:> Ich verstehe dein Problem immer noch nicht. Normalerweise legt man sich> die Tabelle vorberechnet in den Speicher. Ob man dann den Motor bei v=0> oder v=v1 loslaufen lässt, ändert nur den Startpunkt in der Tabelle.
Das stimmt (zumindest meiner Meinung nach) nicht ganz.
Im normalen "fahr eine Position x an" hast du ein einheitliches
Beschleunigungsprofil, welches nur wenige, diskrete Werte annehmen kann.
Und du fährst eigentlich immer aus dem Stillstand los.
Beispiel: der Counter-Wert deiner initialen Beschleunigung wäre 1000,
und deine Maximalgeschwindigkeit wäre 200. Dann hättest du immer
folgendes Profil:
1000 - 414 - 318 - 268 - 236 - 213 - 200 (196)
(die letzte 196 liegt unter deiner Maximalgeschwindigkeit, deshalb
nimmst du 200, oder korrigierst die ganze Rampe nach oben).
Natürlich eignet sich diese Rampe optimal, um sie in einer Tabelle
abzulegen, und stur abzufahren. Du wirst auch nix anderes brauchen, weil
du nie andere Geschwindigkeiten fahren wirst (mal unter der Annahme, du
bremst gleich schnell wie du beschleunigst).
Ich hingegen fahre praktisch beliebige Geschwindigkeiten, aus denen
heraus ich dann bremsen oder beschleunigen muss. Diese Werte find ich
dann aber in obiger Tabelle nicht...
natürlich könnte man interpolieren, eine größere Tabelle ablegen, oder
was auch immer. Die Möglichkeiten stehen mir ja immer noch offen. Ich
möchte aber zuerst die Theorie im Griff haben. Aber da nähere ich mich
eh an, ich kämpfe nur noch etwas mit Wolfram Alpha (leider hab ich kein
Mathematica)
Hallo Michael,
es gibt ja eigentlich keine Geschwindigkeit Null, bereits von Schritt 1
nach Schritt 2 hast du eine Geschwindigkeit. Man kann die Tabelle
tatsächlich universell anlegen. Hier ist das meiner Meinung nach besser
erklärt:
http://www.silabs.com/Support%20Documents/TechnicalDocs/an155.pdf
Mit freundlichen Grüßen
Thorsten Ostermann
Wolfram Alpha hat mir geholfen, den folgenden Ausdruck zu finden:
Excel (igitt!) sagt mir, ich kann das sehr gut mit folgendem Polynom
annähern:
Welches sich dann in eine "rechenzeit-freunliche Version umformen lässt:
(nur mehr drei Multiplikationen und drei Additionen)
Weiss jemand, wie ich Wolfram Alpha dazu kriege, mir so eine
Polynom-Approximation zu liefern? Erstens traue ich dem Excel(igitt!)
nicht, zweitens liefert Excel(igitt!) bei Ordnung 2 schlechte
Ergebnisse, drittens will ich wissen wie das geht :-)
Thorsten Ostermann schrieb:> Hallo Michael,>> es gibt ja eigentlich keine Geschwindigkeit Null, bereits von Schritt 1> nach Schritt 2 hast du eine Geschwindigkeit. Man kann die Tabelle> tatsächlich universell anlegen. Hier ist das meiner Meinung nach besser> erklärt:> http://www.silabs.com/Support%20Documents/TechnicalDocs/an155.pdf
ich muss dich enttäuschen: Die Tabelle ist nicht universell!
Betrachte die ersten beiden Werte: 255 und 105
Und betrachte die zugrundeliegende Formel: c(n) = sqrt(n+1)-sqrt(n)
=> c(0)=1, c(1) = 0.41
Skaliert auf 255 ergibt das: 255, 105
Was wenn ich gerade mit 162 drehe? Wie bremse oder beschleunige ich?
(162 kommt in der Tabelle nicht vor)
Michael Reinelt schrieb:> Wolfram Alpha hat mir geholfen ....
Ich bin mir noch nicht mal sicher, ob die ganze Vorgehensweise überhaupt
irgendwo hinführt oder nicht. Im Moment denke ich eher nicht.
Beispiel.
Deine momentane Drehzahl sei 400 U/min.
Jetzt kommt der Befehl die Drehzahl auf 600 U/min zu erhöhen.
Also beginnst du mit der Beschleunigungsrampe, du verkürzt sukzessive
die Zeitdauer zwischen den Port-Setzungen des Schrittmotors.
Noch während du in dieser Beschleunigungsphase bist, kommt der neue
Befehl die Drehzahl auf 200 U/min abzusenken.
D.h. du musst jetzt die Beschleunigungsphase abbrechen und in eine
Bremsphase eintreten, die dich von der jetzigen Zeitdauer der Port
Aktionen auf die notwendige für 200 U/min bringt.
Ob du das mit dem jetzigen Formalismus mathematisch hinkriegst - aus dem
Bauch raus hätte ich gesagt: schwierig.
Karl Heinz schrieb:> Deine momentane Drehzahl sei 400 U/min.> Jetzt kommt der Befehl die Drehzahl auf 600 U/min zu erhöhen.> Noch während du in dieser Beschleunigungsphase bist, kommt der neue> Befehl die Drehzahl auf 200 U/min abzusenken.
Danke, Karl-Heinz! Endlich einer, der mich versteht! :-)
Karl Heinz schrieb:> Ob du das mit dem jetzigen Formalismus mathematisch hinkriegst - aus dem> Bauch raus hätte ich gesagt: schwierig.
Du wirst lachen: Ich hatte das schon halbwegs im Griff!
Allerdings nur sehr halbwegs: Da ich aufgrund der komplexen Mathematik
schon mal die Nerven weggeschmissen hatte, hab ich das auf die brachiale
gelöst: einen weiteren hochauflösenden Timer mit konstantem Wert
(entsprechend der maximalen Beschleunigung) der kontinuierlich die
Geschwindigkeitswerte erhöht/erniedrigt hatte. Funktioniert in der
Theorie phantastisch: du kriegst absolut lineare Rampen. In der Praxis
leider ganz schlecht: Du erniedrigst den Timer-Endwert, während sich der
eigentliche Stepper-Timer schon zwischen altem und neuem Wert befindet.
Was dazu führt dass der Timer eine "Ehrenrunde" dreht. Und das war nur
einer der vielen unsympatischen Nebeneffekte.
Auch wenn ich momentan noch auf dünnem Eis herumkrebse, ich hab das
Gefühl ich krieg das hin... (und nebenbei wärme ich meine spärlichen
Mathematik-Kenntnisse etwas auf :-)
Man sollte sich vieleicht mal vor Augen führen was eine
Beschleunigungsrampe bedeutet und wozu sie da ist.
Der Stepper kann nicht unendlich beschleunigen, also muss man
kontinuierlich die Drehzahl erhöhen oder verkleinern.
In erster Näherung kann man das linear tun, also die Zeit für eine sich
linear erhöhende/erniedrigende Drehzahl ändern.
Das wäre also eine Konstante (DeltaDrehzahl/Zeit)
Da der Motor aber vieleicht eine drehzahlabhängige Grundlast hat und
evt. auch eine nichtlineare Drehzahl/Drehmoment Kennlinie braucht man
eine Tabelle: max. deltaDrehzahl/Zeit) in Abhängigkeit von der absoluten
Drehzahl.
Das Ganze reicht wenn man es für einige Stützstellen ablegt und
dazwischen interpoliert.
Michael Reinelt schrieb:> Ich hingegen fahre praktisch beliebige Geschwindigkeiten, aus denen> heraus ich dann bremsen oder beschleunigen muss. Diese Werte find ich> dann aber in obiger Tabelle nicht...>
Einfach nächstliegenden Tabellenwert (in der richtigen Richtung) zum
"starten" nehmen, die Beschleunigung dorthin ist kleiner als die
Beschleunigung der Rampe, das sollte der Motor also mitmachen.
rs schrieb:> Einfach nächstliegenden Tabellenwert (in der richtigen Richtung) zum> "starten" nehmen, die Beschleunigung dorthin ist kleiner als die> Beschleunigung der Rampe, das sollte der Motor also mitmachen.
Ja, aber es muss besser gehen: im Einfachsten Fall mach ich mir dieselbe
Lookup-Tabelle (auf mit 256 Einträgen) welche die folgende
Beschleunigung für den aktuellen Geschwindigkeitswert beinhaltet.
Das sind aber fragen der konkreten Implementierung. Mir gehts momentan
darum, die dahinter liegende Mathematik zu durchschauen.
Hallo Karl Heinz,
> Ich bin mir noch nicht mal sicher, ob die ganze Vorgehensweise überhaupt> irgendwo hinführt oder nicht. Im Moment denke ich eher nicht.>> Beispiel.> Deine momentane Drehzahl sei 400 U/min.> Jetzt kommt der Befehl die Drehzahl auf 600 U/min zu erhöhen.
...
> Noch während du in dieser Beschleunigungsphase bist, kommt der neue> Befehl die Drehzahl auf 200 U/min abzusenken.>> D.h. du musst jetzt die Beschleunigungsphase abbrechen und in eine> Bremsphase eintreten, die dich von der jetzigen Zeitdauer der Port> Aktionen auf die notwendige für 200 U/min bringt.>> Ob du das mit dem jetzigen Formalismus mathematisch hinkriegst - aus dem> Bauch raus hätte ich gesagt: schwierig.
Wo ist das Problem? Du läufst einfach rückwärts durch die Tabelle bis du
bei der gewünschten Geschwindigkeit ankommst.
Das das auch in der Praxis geht zeigt unser Schrittmotorcontroller
NC-Pilot USB. Da fahren wir sogar zwei Achsen bahnsynchron bis 50kHz.
Und die Implementierung ist älter als Mach3 und basiert letztlich auf
der o.g. App-Note von Silicon Labs.
http://www.mechapro.de/schrittmotorensteuerung.html
Mit freundlichen Grüßen
Thorsten Ostermann
Michael Reinelt schrieb:> Ja, aber es muss besser gehen: im Einfachsten Fall mach ich mir dieselbe> Lookup-Tabelle (auf mit 256 Einträgen) welche die folgende> Beschleunigung für den aktuellen Geschwindigkeitswert beinhaltet.>> Das sind aber fragen der konkreten Implementierung. Mir gehts momentan> darum, die dahinter liegende Mathematik zu durchschauen.
Interpretiere "n" als index in die Beschleunigungs-Rampe, und nicht als
Schritt-Nummer. Bei konstanter Geschwindigkeit ändert sich n nicht.
Ist die Beschleunigung zeitlich konstant, dann reicht eine Tabelle, die
die Rampe von 0 bis zur maxmimal-Geschwindigkeit abbildet.
Hier gibts das alles nochmal recht kompakt und verständlich (wie ich
finde): http://hwml.com/LeibRamp.pdf
Statt Tabelle kann man natürlich auch einen entsprechenden µC nehmen,
ein ARM mit 100MHz schafft es, bei 100kHz Schrittfequenz jeweils das
nächste Intervall in einer ISR zu berechnen. Und das bei 2-3 Motoren ;-)
Die hohe Timer-Auflösung bedeutet auch sanfte Beschleunigungen auch bei
hohen Schrittfequenzen.
rs schrieb:> Hier gibts das alles nochmal recht kompakt und verständlich (wie ich> finde): http://hwml.com/LeibRamp.pdf
Danke danke danke, der Link ist Gold Wert! Geniale Sache, und tut genau
das wie ich will.
Gibts diesen Algorithmus auch irgendwo in konkreten Code gesetzt? laut
dem Dokument ist der nämlich optimiert für floating point, welches
schneller wäre als integer, weil keine Division, und integer würde
Division brauchen...
Hallo allerseits,
nach langem hin- und herüberlegen, ausprobieren unzähliger Varianten,
glaube ich jetzt eine sehr gute Lösung gefunden zu haben, die ich euch
nicht vorenthalten will.
Ausgangspunkt ist die "exakte" Berechnung eines neuen Counter-Wertes
c_new basierend auf dem aktuellen Counter-Wert c_old, Parameter sind
Timer-Frequenz F und Beschleunigung a:
Das schaut auf den ersten Blick natürlich beängstigend aus, geht aber
überraschend schnell: eine Berechnung dauert auf dem ATmega328 rund 1500
Takte. Das lässt sich mit einer "inversen Quadratwurzel"
http://en.wikipedia.org/wiki/Fast_inverse_square_root noch auf rund 1200
Takte beschleunigen. Ist aber natürlich nicht wirklich
zufriedenstellen...
Dann gibt es den Algorithmus von Aryeh Eiderman, der die Wurzel durch
eine Taylor-Reihe ersetzt. Leider bedingt diese Variante
Fließkomma-Berechnung, kommt aber nur mit Multiplikationen und
Additionen aus. Aber Immerhin eine Steigerung auf rund 600 Takte. Aber
immer noch unbefriedigend... (abgesehen davon dass die "einfachste"
Variante doch stark fehlerbehaftet ist). Meine Versuche, das vernünftig
in integer-Arithemtik zu wandeln, sind gescheitert, das war aber auch
gut so, denn sonst hätt ich vermutlich meine Lösung nicht mehr gesucht
und gefunden...
Ich habe dann versucht, obige Formel durch ein Polynom zweiten Grades
anzunähern, und das funktioniert erstaunlich gut. Die Koeffizienten
lassen sich dabei sogar automatisch ermitteln, etwas kritisch ist nur
die geschickte Wahl der Stützstellen.
Mein Polynom für F=16MHz und a=200.000 Steps/sec^2 sieht folgendermaßen
aus:
mit 0.000016 lässt sich aber schlecht rechnen....
trickreiche Umwandlung in integer-Arithmetik unter Verwendung von
Schiebeoperationen mit ganzzahligem Vielfachen von 8 führen zu folgender
Formel:
1
c_neu=((c*264)>>8)-((((c*274)>>8)*c)>>16)
Ergebnis: 140 Takte, und genauer als das Eiderman-Verfahren!
Im Diagramm sieht man die drei Verfahren gegenübergestellt, und man
erkennt auch deutlich den Fehler der Eiderman-Berechnung.
Fazit: Es lohnt sich, angestrengt nachzudenken :-)
Vielleicht stehe ich ja einfach nur auf dem Schlauch, aber was zeigt der
Graph uns? Also was hast du da auf X- und Y-Achse aufgetragen? Eine
Achsenbeschriftung wäre hilfreich...
Mit freundlichen Grüßen
Thorsten Ostermann
Hallo Michael,
>> http://www.silabs.com/Support%20Documents/TechnicalDocs/an155.pdf>> ich muss dich enttäuschen: Die Tabelle ist nicht universell!>> Betrachte die ersten beiden Werte: 255 und 105>> Und betrachte die zugrundeliegende Formel: c(n) = sqrt(n+1)-sqrt(n)> => c(0)=1, c(1) = 0.41> Skaliert auf 255 ergibt das: 255, 105>> Was wenn ich gerade mit 162 drehe? Wie bremse oder beschleunige ich?> (162 kommt in der Tabelle nicht vor)
In dem man den nächst größeren oder kleineren Tabelleneintrag nimmt. Das
führt natürlich zu einem "Beschleunigungsfehler", der aber in den
meisten Fällen nicht dramatisch ist. Vor allem, wenn man berücksichtigt,
dass man die Tabelle normalerweise so anlegt, dass man nicht mit den
ersten Einträgen arbeiten muss, die eine sehr grobe Verteilung
aufweisen. Man skaliert eben nicht von 0 und 1 nach 255, sondern macht
die Anzahl der Tabelleneinträge ausreichend groß (z.B. 10.000).
Mit freundlichen Grüßen
Thorsten Ostermann
Thorsten Ostermann schrieb:> Eine Achsenbeschriftung wäre hilfreich...
oh sorry...
X: Zeit (in Timer-Ticks)
Y: Counter-Wert (ebenfalls Timer-Ticks)
Würde man die Y-Werte verkehrwerten (als 1/y) ergäbe sich eine lineare
Rampe.
Thorsten Ostermann schrieb:> macht> die Anzahl der Tabelleneinträge ausreichend groß (z.B. 10.000).
Auf einem AVR mit 8kB RAM? :-)
Michael Reinelt schrieb:> Mein Polynom für F=16MHz und a=200.000 Steps/sec^2 sieht folgendermaßen> aus:>> mit 0.000016 lässt sich aber schlecht rechnen....>> trickreiche Umwandlung in integer-Arithmetik unter Verwendung von> Schiebeoperationen mit ganzzahligem Vielfachen von 8 führen zu folgender> Formel:> c_neu = ((c*264)>>8) - ((((c*274)>>8)*c)>>16)
Ich habe das mal in mein Programm eingebaut, aber kein brauchbares
Ergebnis bekommen. Deutlich erfolgreicher (und langsamer) kann ich zur
Beschleunigung die Formel:
c = c * (1-m*c*c);
verwenden, wobei sinnvolle Werte für m im Bereich 1e-11 bis 1e-10
liegen.
Das Abbremsen geht ähnlich mit:
c = c * (1+m*c*c);
Geschickterweise nimmt man dafür einen µC mit FPU :-)
Oder man machts wie bei GRBL:
https://github.com/grbl/grbl
Dort stellt ein Timer etwa alle 1/30Hz die Periodendauer eines zweiten
Timers mittels des Bresenham-Algorithmus nach. Die zweite Timer-ISR
erzeugt die Schrittpulse und stellt bei den (vorher berechneten)
richtigen Schrittzahlen den Bresenham-Algorithmus so nach, dass das
gewünschte Geschwindigkeitsprofil eingehalten wird. (siehe stepper.c)
Falls sich die gewünschte Endgeschwindigkeit mittendrin plötzlich ändert
werden die Parameter entsprechend neu berechnet.
Gruß,
Martin
Martin St. schrieb:> (siehe stepper.c)
Auf den 1. Blick ist dort allerdings ein (grober) Schnitzer zu sehen. In
der T1-Compare-Routine wird per busy-flag, ein wiederholter Aufruf
verhindert, was zur Folge hat, dass sich in diesem Fall die
Schrittfrequenz schlagartig halbiert (doppeltes Intervall).
Besser wäre es, den T1-compare Interrupt kurz abzuschalten und am Ende
der Routine wieder freizugeben. Dann kommt der nächste Interrupt einen
Tick verzögert, wird aber nicht gänzlich verworfen.
Die T1 ISR ist so ausgelegt dass sie auf jeden Fall schneller fertig
wird als nötig und nie blockiert.
Ich denke dass dieser Codeabschnitt während der anfänglichen Entwicklung
entstanden ist umd Fehler abzufangen. Beim frühzeitigem Widereintritt
würde die ganze Programmlogik von GBRL nicht stimmen. Ein while(1); mit
Fehlerausgabe zum anschließendem Debugging wäre an dieser Stelle sicher
besser.
m.n. schrieb:> c = c * (1-m*c*c);> verwenden, wobei sinnvolle Werte für m im Bereich 1e-11 bis 1e-10> liegen.
Das solltest für dich Grund genug sein, mal über die Einheiten
nachdenken, in denen du deine Werte in die Formel einsetzt. Mit
vernünftiger Normierung klappt das auch ohne FPU.
Physiker schrieb:> Das solltest für dich Grund genug sein, mal über die Einheiten> nachdenken, in denen du deine Werte in die Formel einsetzt. Mit> vernünftiger Normierung klappt das auch ohne FPU.
Danke, ehrwürdiger Physiker. Auf dich haben wir gewartet. Du wirst uns
sicher gleich erleuchten, wie wir m=a/F^2 anders normieren, um damit
vernünftige Timerwerte ohne FPU erhalten.
schris schrieb:> oder was ist c
c ist das "counter Delay", also die Anzahl Counter Ticks zwischen zwei
Step-Impulsen (üblicherweise wird das dann ich ein Overflow- oder
OutputCompare-Register geladen)
schris schrieb:> Wie ist eigentlich diese Formel
Dieser Satz kein Verb?
GRBL: das läuft dort ganz anders. zugegeben, recht elegant, aber für
mich so nicht brauchbar.
m.n. schrieb:> Ich habe das mal in mein Programm eingebaut, aber kein brauchbares> Ergebnis bekommen.
Mal ohne Programm, in einer Debug- oder Testroutine probieren. Wichtig
ist auch dass die Variablentypen stimmen (uint16_t)
wenn du das z.B. mit 30.000 aufrufst, solltest du irgendwas in Richtung
20.000 rauskriegen.
Michael Reinelt schrieb:> Danke, ehrwürdiger Physiker. Auf dich haben wir gewartet. Du wirst uns> sicher gleich erleuchten, wie wir m=a/F^2 anders normieren, um damit> vernünftige Timerwerte ohne FPU erhalten.
Solange in deiner Formel ein Änderungsfaktor (1-m*c*c) steht, darfst du
dich natürlich nicht wundern, wenn m größenordnungsmäßig bei 1/c² liegt.
Probiere es mal mit der Substitution c' := c * 10^-6 zu rechnen und erst
danach wieder auf deine Clockanzahl umzurechnen.
Physiker schrieb:> Solange in deiner Formel ein Änderungsfaktor (1-m*c*c) steht, darfst du> dich natürlich nicht wundern, wenn m größenordnungsmäßig bei 1/c² liegt.> Probiere es mal mit der Substitution c' := c * 10^-6 zu rechnen und erst> danach wieder auf deine Clockanzahl umzurechnen.
Das wird nur nichts bringen, weil mir die Rückskalierung genausoviel
kostet wie die etwaige erleichterung in einer andern größenordnung. Die
Berechnung ist auf float ausgelegt, und funktioniert in der Domäne auch
gut. Sie lässt sich schlecht bis gar nicht in eine int-Domain umlegen.
Aber selbstverständlich bin ich offen für gute ideen...
Michael Reinelt schrieb:> Wichtig> ist auch dass die Variablentypen stimmen (uint16_t)
Das kann nicht sein! Andernfalls könnte ich den Ausdruck mit "(...)>>16"
gleich durch 0 ersetzen.
Physiker schrieb:> Solange in deiner Formel ein Änderungsfaktor (1-m*c*c) steht, darfst du> dich natürlich nicht wundern, wenn m größenordnungsmäßig bei 1/c² liegt.
Ich wundere mich nicht und welcher Wert sich für 'm' ergibt ist mir
völlig schnuppe, solange er im float-Format darstellbar ist.
m.n. schrieb:> Ich wundere mich nicht und welcher Wert sich für 'm' ergibt ist mir> völlig schnuppe, solange er im float-Format darstellbar ist.
float ist kein Allheilmittel, du wirst dich spätestens dann wundern wenn
du mal zwei annähernd gleichgroße floats voneinander subtrahierst, da
geht die Genauigkeit nämlich schnell den Bach runter.
Das kann man durch Formelumstellung vermeiden
Michael Reinelt schrieb:> Nun bräuchte ich das etwas anders: Ich möchte gerne c(n) nicht aus c(0)> berechnen, sondern aus c(n-1)
Was genau willst Du eigentlich?
Wenn sich die Ausgangs-Parameter ändern muss auch der erste Teil der
Formel neu berechnet werden - oder?
Ich habe auch schon mal die Bewegung des Mondes relativ zur Erde
"interpoliert" - aber leider nur das und die Wahrheit sah doch etwas
anders aus.
Deine "Formel" passt aus meiner Sicht nur genau (in etwa) auf die
angegebenen Parameter - ändert sich etwas ist auch eine neue Formel
fällig.
Auch aus dem Parallel-Thread bin ich (wahrscheinlich zu doof) nicht so
recht schlau geworden.
Eine Schrittmotor-Rampe kann man berechnen (habe ich experimentell
gemacht) oder vorberechnet hinterlegen und "näherungsweise" ausführen.
Also nochmal - was willst Du erreichen?
Fortschritte... (oder Rückschritte?)
nachdem die Beschleunigungsrampe so gut funktioniert hat, habe ich mich
an der Bremsrampe versucht, und bin gescheitert :-( Das Biest lässt sich
nicht vernünftig durch ein Polynom approximieren, auch nicht wenn man zu
höheren Ordnungen greift (was aber ohnehin kontraproduktiv wäre)
Im Zuge der Bastlereien bin ich dann noch draufgekommen, dass die
Polynom-Approximation sowieso ihre "dunklen Seiten" hat: Mal ist das
Ding nicht monoton steigend, mal schwingt es, mal überschreitet man die
maximale Beschleunigung....
nachdem die Eiderman-Variante eine wirklich sehr saubere Bremsrampe
liefert, bin ich wieder reumütig zu dieser zurückgekehrt.
Und nachdem ich jetzt Erfahrung habe, wie man eine Fließkomma-Berechnung
klug in integer umwandelt, habe ich nun eine recht schöne und schnelle
Form für die Eiderman-Berechnung gefunden:
Die Basis der Eiderman-Berechnung ist ja bekannterweise:
Das Problem ist das sehr kleine m, welches sich zu +/- a/F^2 berechnet
(a = Beschleunigung in Steps/sec, F = Timer-Frequenz). Beim
Beschleunigen ist m negativ, beim bremsen positiv
Durch etwas umformen kommt man auf folgenden Ausdruck:
es reicht also m*p*p*p zu errechnen, beim Beschleunigen erniedrigt man
den momentan Counter-Endwert, beim Bremsen erhöht man entsprechend.
m ist in meinem Fall (a=20.000 Steps/sec^2, F=16 MHz) 7.8E-10, also
wirklich sehr sehr klein
Skaliert man das aber mit2^40, kommt man auf 859. Mit 859 lässt sich
vernünftig rechnen.... problematisch ist natürlich auch noch p^3, bei
16-Bit-Timerwerten sprengt das natürlich alle 32-Bit-Grenzen...
durch vernünftiges Zwischenschieben funktioneirt das aber wunderbar:
Die letzte Zeile sorgt dafür, dass die Funktion niemals 0 zurückliefert,
weil man sonst in einer Rampenposition "gefangen" sein könnte.
und, schlußendlich: ca. 190 Takte (gegenüber 600+ der float-Variante)
Michael Reinelt schrieb:> Das wird nur nichts bringen, weil mir die Rückskalierung genausoviel> kostet wie die etwaige erleichterung
Du kannst mir nicht erzählen, dass ein c >> 20 genausoviel wie eine
Floatingpoint Operation ohne FPU kostet.
Physiker schrieb:> Michael Reinelt schrieb:>> Das wird nur nichts bringen, weil mir die Rückskalierung genausoviel>> kostet wie die etwaige erleichterung>> Du kannst mir nicht erzählen, dass ein c >> 20 genausoviel wie eine> Floatingpoint Operation ohne FPU kostet.
Will ich ja gar nicht :-) Der Trick ist: da ist kein >>20. Alle
Shift-Operationen sind ganzzahlige Vielfache von 8, das ersetzt der GCC
sehr klug durch einzelne Byte-Zugriffe. Im gesamten Asm-Code ist keine
einzige Shift-Operation mehr drinnen:
Michael Reinelt schrieb:> Im gesamten Asm-Code ist keine einzige Shift-Operation mehr drinnen:
Sehr spannend zu lesen, wenn man kryptisches Kopfkino mag. Ohne
sinntragende Kommentare reizt mich der Code-Ausschnitt gerade soetwas
von überhaupt nicht.
Wenn du deine ursprünglichen Korrekturfaktor (1-m*c*c) mit unglücklicher
Normierung berechnest, turnst du mit c*c einfach fürchterlich durch die
Zehnerpotenzen.
Physiker schrieb:> Sehr spannend zu lesen, wenn man kryptisches Kopfkino mag. Ohne> sinntragende Kommentare reizt mich der Code-Ausschnitt gerade soetwas> von überhaupt nicht.
Das ist der Asm-Output des Compilers, und den werd ich für dich jetzt
nciht kommentieren. Es ging mir nur darum zu zeigen dass da keine
Shift-operationen drinnen sind.
> Wenn du deine ursprünglichen Korrekturfaktor (1-m*c*c) mit unglücklicher> Normierung berechnest, turnst du mit c*c einfach fürchterlich durch die> Zehnerpotenzen.
ich fürchte du hast nicht verstanden worum es hier geht: Gewisse
Parameter sind nicht normierbar, weil einfach in der gegebenen Form
vorhanden: Ein 16-bit-Timer hat nun mal einen 16-Bit Wert, den kann und
will ich nicht normieren.
Michael Reinelt schrieb:> ich fürchte du hast nicht verstanden worum es hier geht: Gewisse> Parameter sind nicht normierbar
Es geht auch nicht um die Umnormierung deines Timer, sondern einzig um
alleine um deine quadratische Gleichung. In welchem Bereich bewegt sich
denn dein Faktor (1-m*c*c)?
Physiker schrieb:> Es geht auch nicht um die Umnormierung deines Timer, sondern einzig um> alleine um deine quadratische Gleichung. In welchem Bereich bewegt sich> denn dein Faktor (1-m*c*c)?
zwischen 0 und 1
Edit: 0..1 nur beim Beschleunigen, beim Bremsen sieht der Ausdruck etwas
anders aus: (1+m*c*c) und der kann dann zwischen 1 und 2 liegen
Ich habe jetzt nicht im Detail die ganze Problematik durchgelesen, habe
aber auch schon eine CNC Steuerung für Schrittmotoren entwickelt, dazu
gehören natürlich auch Rampen.
Hier ist meine Rampenberechnung:
//Beschleunigen
if Fovrsoll > Fovr then
A_single = sqr(Fovr^2 + beschlkonst * (1/inkrpermm))
if A_single > Fovrsoll then
Fovr = Fovrsoll
else
Fovr = A_single
end if
end if
//Verzögern
if Fovrsoll < Fovr then
A_single = Fovr^2 - beschlkonst * (1/inkrpermm)
if A_single > 0 then
A_single = sqr(A_single)
end if
if A_single < Fovrsoll then
Fovr = Fovrsoll
else
Fovr = A_single
end if
end if
Fovr = ist der Aktuelle Vorschub (oder auch Geschwindigkeit)
Fovrsoll = ist der Soll Vorschub
beschlkonst = Beschleunigungswert
Die Berechnung wird nach jedem Takt des Schrittmotors gemacht. Während
dem Beschleunigen, wird die Geschwindigkeit nach jedem Takt schrittweise
erhöht, beim Verzögern verringert.
Vielleicht hilft das jemandem weiter
Michael L. schrieb:> A_single = sqr(Fovr^2 + beschlkonst * (1/inkrpermm))
Und genau das will ich adressieren: Die Berechnung verwendet a)
Fließkomma und b) noch komplexe Funktionen wie Quadratwurzel. Auf
schnellen Prozessoren oder welchen mit FPU mag das kein Problem sein,
auf einem AVR braucht dann die ISR viel zu viel Rechenzeit.
Michael Reinelt schrieb:> auf einem AVR braucht dann die ISR viel zu viel Rechenzeit
Gut, bei mir läuft das ganze auf einem INTEL PC mit DOS, ist natürlich
einiges schneller ;-)
Jedoch mache auch ich die Rampenberechnung nicht in der zeitkritischen
ISR in der der Takt erzeugt wird. Ich berechne den Timer im
Hauptprogramm, anschliessend setzte ich ein Start_byte und wenn das
nächste mal die Timer ISR auftritt wird der Output Pin für den Takt
einmal gesetzt und das zweitemal zurückgesetzt. In der Zwischenzeit kann
ich im Hauptprogramm den neuen Timerwert berechnen.
Das ganze läuft für einen Hi/Lo Takt Zyklus grob folgendermaßen ab:
//Hauptprogramm:
Do
-Timer berechnen (Zeit für Hi/Lo Phase des Takts)
-warten bis Start_Byte = 0 (Taktzyklus fertig)
-Start_Byte = 2 (Takt Zyklus starten)
loop until soll = ist
//ISR für Takt Erzeugung:
Takt ISR:
IF Start_Byte = 2 then
set Outputpin
Start_Byte = 1
goto fertig
end if
if Start_Byte = 1 then
reset Outputpin
Start_Byte = 0
goto fertig
end if
fertig:
-Timer mit berechnetem wert laden
end ISR
Hallo Dieter, hallo Michael,
>> Nun bräuchte ich das etwas anders: Ich möchte gerne c(n) nicht aus c(0)>> berechnen, sondern aus c(n-1)>> Was genau willst Du eigentlich?
...
> Eine Schrittmotor-Rampe kann man berechnen (habe ich experimentell> gemacht) oder vorberechnet hinterlegen und "näherungsweise" ausführen.>> Also nochmal - was willst Du erreichen?
Das würde mich langsam auch mal interessieren. Was ist das Ziel? Eine
möglichst gleichförmig beschleunigte Bewegung? Ein möglichst genauer
Bahnverlauf? Wenig Rechenaufwand?
Mit freundlichen Grüßen
Thorsten Ostermann
Soweit ich weiss hat man eine Geschwindigkeit, z.B. 600mm/min =
10mm/sec=
4000 steps/sec (4mm Spindelsteigung, 200 step Schrittmotor, 8
microsteps)
als Maximalgeschwindigkeit.
X = 200mm y = 150mm Z = 10mm
sqrt(X*X+Y*Y+Z*Z+A*A) = 250,2 mm
Auf diese 250,2 mm wird dann die Rampe gefahren, und dann
mittels Bresenham Algorithmus wird diese Rampe auf alle Motoren
aufgeteilt,
sprich interpoliert. Dies bei G1
Bei G0 wird einfach die längste Achse hergenommen, die schnellste
Geschwindigkeit darauf mittels Rampe gefahren, und die restlichen Achsen
mittels bresenham interpoliert.
Thorsten Ostermann schrieb:>> Also nochmal - was willst Du erreichen?>> Das würde mich langsam auch mal interessieren. Was ist das Ziel? Eine> möglichst gleichförmig beschleunigte Bewegung? Ein möglichst genauer> Bahnverlauf? Wenig Rechenaufwand?
Eigentlich dachte ich, das ginge aus ersten paar Posts recht klar
hervor: ich kriege eine (fast beliebige) Solldrehzahl (beachte:
Drehzahl nicht Position) vorgegeben, und möchte den Schrittmotor von
der aktuellen (beliebigen) Drehzahl möglichst schnell (d.h. möglichst
nahe an der maximal möglichen Beschleunigung) auf die neue Drehzahl
bringen. Ein linearer Bahnverlauf ist genauso unwichtig wie eine wie
immer geartete Schrittzahl. Mich interessieren nur Geschwindigkeiten.
Eine lineare Geschwindigkeitsänderung ergibt sich automatisch aus der
Forderung, möglichst nahe an (bzw. im Idealfall mit) der maximalen
Beschleunigung die Geschwingigkeitsänderung durchzuführen. Da die
Berechnung innerhalb der ISR durchgeführt werden soll, soll natürlich
der Rechenaufwand minimal sein.
ich bin aber mit meiner Lösung von oben praktisch am Ziel.