Moin Moin,
vorne weg, das ist mein erster Beitrag und ich hoffe, ich bin im
richtigen Unterforum gelandet. Bevor ich mich entschlossen habe dieses
Thema zu erstellen habe ich mir 10 Tage den Kopf zerbrochen und habe
sämtliche Suchmaschinen durchforstet ...
Kurz zu mir: Ihr dürft mich Helge nennen ;), ich bin 23 Jahre "alt",
komme aus dem schönen Ostfriesland und studiere Maschinenbau - nun im 5.
Semester, welches bei uns das Praxissemester ist - darum verbringe ich
ein halbes Jahr in München.
Zu meinem Projekt:
Ich bin zur Zeit dabei einen G-Code Interpreter zu programmieren (mit
den mitteln die mir zur Verfügung stehen) - darum darf mein
Programmierstil gerne kritisiert werden.
Achja, das ganze wird auf einem Arduino Mega 2560 umgesetzt.
Aktueller Stand ist, dass das ein- und auslesen des G-Codes von SD-Card
funktioniert, aufeinander treffende Geraden werden "übergeschliffen".
Die unterprogramme zum Verfahren von Geraden (Bresenham) und Kreisen
laufen ebenfalls.
Nun zum Problem:
Das beschleunigen der Achsantriebe. Als Motoren nutze ich aktuell noch
einfache Stepper - später sollen DC Servomotoren mit UHU Servocontroller
zum Einsatz kommen.
Nach ein wenig Recherche bin ich immer wieder auf das Prinzip von David
Austin gestoßen. Hier einer der vielen Links dazu:
http://www.embedded.com/design/mcus-processors-and-socs/4006438/Generate-stepper-motor-speed-profiles-in-real-time
Erstmal geht es mir nur um das anfahren des Motors. Dies funktioniert
auch, allerdings hat die erreichte Endgeschwindigkeit sowie die
Beschleunigungsdauer nichts mit den Werten zu tun, die ich vorgebe.
Leider weiss ich mittlerweile nicht mehr wo vorne und hinten ist.
Ich hänge nun mal einen knappen Code an - der eben ein schönes anlaufen
erzeugt, aber nicht so wie es die Solldaten vorgeben.
Am besten wäre wahrscheinlich, wenn sich jemand meldet, der genau dieses
vorgehen schon genutzt hat.
1
// Timer variablen
2
constlongcpufreq=16000000;// CPU Takt [Hz]
3
constlongf=1000000;// Frequenz des Timers [Hz]
4
constintprescaler=1;// Timervorteiler
5
intoverf1;// Overflowvaribale Timer1 (Legt die Timerfrequenz fest)
6
7
// Maschinendaten
8
intp=10;// Steigung der Spindel [mm]
9
intres=1600;// Steps pro Umdrehung, 1600 = 1/8 Betrieb
10
11
// Bewegungsdaten
12
doublev_max=60;// Endgeschwindigkeit [mm/s]
13
doublet=2;// Beschleunigungszeit [s]
14
doubleomega;// Winkelgeschwindigkeit [rad/s]
15
doubleomega_d;// Winkelbeschleunigung [rad/s^2]
16
doublealpha;// Schrittwinkel pro Step [rad]
17
18
// weitere Variablen
19
constdoublepi=3.141593;// pi
20
longtn;// absoluter Zeitpunkt des Steps
21
longi=0;// Timeraufrufe seit letzten Step
22
longn=1;// aktueller Beschleunigungsstep
23
longcnt0;// Zeit bis zum ersten Step
24
longcntn;// Zeit zwischen letztem und nächstem Step
25
longn_end;// zur Beschleunigung erforderliche Schritte
26
27
voidsetup()
28
{
29
Serial.begin(115200);
30
pinMode(13,OUTPUT);
31
32
// maximale Winkelgeschwinigkeit
33
omega=(v_max/p)*2*pi;
34
// Winkelbeschleunigung
35
omega_d=(omega/t);
36
// Schrittwinkel pro Step
37
alpha=(2*pi)/res;
38
// erforderliche Steps bis maximale Geschwindigkeit
39
n_end=((omega*omega)/(2*alpha*omega_d));
40
41
// Zeit bis zum ersten Step
42
cnt0=f*sqrt((2*alpha)/omega_d)*0.676;
43
cntn=cnt0;
44
45
// absoluter Zeitpunkt des Steps
46
// tn = sqrt((2*n*alpha)/omega_d);
47
48
// Berechne Overflowvariable für Timer1
49
overf1=(cpufreq/(prescaler*f))-1;
50
// Timer1 starten
51
Timer1(overf1);
52
}
53
54
voidTimer1(intoverf1)
55
{
56
// initialize Timer1
57
cli();// disable global interrupts
58
TCCR1A=0;// set entire TCCR1A register to 0
59
TCCR1B=0;// same for TCCR1B
60
61
// set compare match register to desired timer count:
62
OCR1A=overf1;
63
// turn on CTC mode:
64
TCCR1B|=(1<<WGM12);
65
// prescaler einstellen
66
TCCR1B|=(1<<CS10);
67
TCCR1B|=(0<<CS11);
68
TCCR1B|=(0<<CS12);
69
// enable timer compare interrupt:
70
TIMSK1|=(1<<OCIE1A);
71
72
sei();// enable global interrupts
73
}
74
75
ISR(TIMER1_COMPA_vect)
76
{
77
i++;
78
79
if(i==cntn)
80
{
81
digitalWrite(13,HIGH);
82
83
if(n<n_end)
84
n++;
85
86
//cntn = cntn - ((2*cntn)/(4*n+1));
87
cntn=cnt0*(sqrt(n+1)-sqrt(n));
88
89
i=0;
90
91
digitalWrite(13,LOW);
92
}
93
}
94
95
voidloop()
96
{}
Es wäre klasse, wenn jemand Rat weiss oder andere Ideen ans Tageslicht
kommen. Weiterhin hoffe ich niemanden "erschlagen" zu haben :D
vielen Dank!
Helge
Hmmm... Das Rampe hoch und runterfahren auf einer einzelnen Geraden habe
ich nie als grosses Implementierungsproblem gesehen.
Ein grösseres Problem war für mich immer die Tatsache, wie man rampen
z.B. bei Übergängen zwischen Geradensegmenten hinbekommt. Und Geraden
können z.b. auch nur aus 1-2 Schritten bestehen und nicht wie im
Idealfall immer gesehen eine Länge besitzen, die es erlaubt die Rampe
komplett abzufahren.
Bin bisher auch immer ohne Bresenham ausgekommen. Ich hab bisher immer
folgendes Implementiert:
Die Schrittmotorenansteuerung erfolgt innerhalb eines Timer interrupts
mit fester Frequenz (z.B. 10 kHz). Die Ansteuerung erfolgt dann mit
Hilfe des DDS Prinzips. Der Geschwindigkeitswert wird zu dem
Phasenakkumulator hinzuadiert und bei einem Überlauf an der
signifikanten Bitstelle wird jeweils ein Schritt weitergesprungen.
Das schöne daran: Jeder Motor hat dann eine unabhängigen Geschwindkeits-
und Schrittanzahlwert. Wenn man eine 2D oder 3D Gerade abfahren will
berechnet man die Geschwindigkeit der 3 Motoren einfach so, dass die
Motoren nach dem Starten sich alle genausolang bewegen. Dank der hohen
Auflösung der Phasenakkumulatoren ist das machbar.
Den Aspekt "Rampen" habe ich dann immer durch ändern der Timer interrupt
frequenz realisiert.
Das anpassen der Frequenz ist völlig unabhängig von der eigentlichen
Bewegungssteuerung und erlaubt es die Rampen auch über mehrere
Geradensegmente zu verschleifen, bzw. abhängig von der Richtungsänderung
auch dynamisch anzupassen.
Klappt wunderbar, auch mit 8051 Controllern und ist auch rein fixed
point und man muss nicht so umständlich den Bresenham für Linien
implementieren :-)
Rein interessehalber:
Weisst Du, wo man günstige DC Servomotoren bekommt? Also einfach "nur"
einen DC Motor mit einem Encoder der eine ausreichende Auflösung besitzt
um einen Steppermotor zu ersetzen?
Der Bresenham ist ja nicht wild. Ich kann damit aktuell beliebig im Raum
liegende Geraden Verfahren und nach Bedarf das Kreis bzw. Helix
Verfahren dazwischen schalten.
Nun wollte ich ja nach dem Prinzip von David Austin das anfahren regeln,
in dem ich später den Bresenham in notwendigem Maß ausbremse.
Und es muss ja auch funktionieren - dieser Artikel ist zieg fach
"kopiert" also nochmal wiedergegeben zu finden.
Aber was ich "reinstecke" kommt an der Motorwelle nicht raus^^.
Echt zum verrückt werden, dass einen soetwas solange aufhält.
zu den Servos:
Ich stehe momentan mit China in Kontakt.
Schreib mir deine eMail Adresse oder so. Wenn sich dort was bezahlbares
ergibt melde ich mich!
Nur so eine Idee:
Deine CPU läuft mit 16 MHz.
Die Timer ISR wird, wenn ich es richtig interpretiere jede 1us
getriggert (prescaler=1, 16 Zyklen max).
Die ISR muss also in <16 Zyklen abgearbeitet sein. Das wird nicht
gelingen und dadurch die Timer Intterruptserviceroutine effektiv
deutlich niederfrequenter aufgerufen werden, wass evtl. zu dem
beschriebenen Problemen führt.
Dann befinden sich eine extrem langsame floating point Berechnungen in
der ISR, die zusätzlich, aufgrund Ihrer Bearbeitungsdauer dafür sorgen,
dass Timerevents "verschluckt" werden.
Du kannst ja testweise mal einen GPIO pin in der Timer ISR toggeln und
auf dem Scope anschauen und mit dem erwarteten Wert vergleichen.
Evtl. überseh ich ja was. Ist immer schwerer fremden Code zu lesen als
eigenen.
Du solltest mal schauen, ob Du nicht die ein oder andere Variable, die
in der Timer ISR benutzt wird nicht noch als volatile deklarieren musst.
VG,
Kai
ok, vollkommen richtig...
Die hatte ich auch irgendwann mal auf 200000Hz.
Aber es wird schonmal schneller ;).
Hab f nun auf 50000 runtergedreht und bin bei 4,1 U/s von 6,5
erwarteten.
Weiter runter sollte das aber nicht mehr ...
Morgen gehts weiter...
Ich nehme auch gerne ganz andere Ideen an. Müssen nur in das bisherige
Konzept passen^^.
Super!
Kein Problem, ich fand das Prinzip mit den DDS halt nur echt praktisch
weil es sowas wie Bresenham unnötig macht.
Wenn ich was funktionierendes anders hätte, würd ich auch nichts mehr
ändern :-)
Bei 50000 ist es auch noch wahrscheinlich, dass Du timer events
verlierst. Bleiben ja nur 320 Zyklen Zeit in der die
Floatingpointemulation die Wurzel berechnen muss.
Keine Ahnung, ob das wirklich kritisch ist, es würde aber nur in dem
Fall passieren, wo der THEN-zweig ausgeführt wird.
VG,
Kai
Hallo "KPL"
> Bin bisher auch immer ohne Bresenham ausgekommen. Ich hab bisher immer> folgendes Implementiert:> Die Schrittmotorenansteuerung erfolgt innerhalb eines Timer interrupts> mit fester Frequenz (z.B. 10 kHz). Die Ansteuerung erfolgt dann mit> Hilfe des DDS Prinzips. Der Geschwindigkeitswert wird zu dem> Phasenakkumulator hinzuadiert und bei einem Überlauf an der> signifikanten Bitstelle wird jeweils ein Schritt weitergesprungen.
Was verstehst du unter "DDS"? Und was ist ein Phasenakkumulator? Einfach
ein Register, was hochgezählt wird? Wenn der Schritt abhängig von einer
Bitstelle im Phasenakku ausgelöst wird können aber die Geschwindigkeiten
der Achsen immer nur im Verhältnis 2^n/2^m stehen? Und jeweils ein
Bruchteil deiner 10kHz sein? Das wäre bei Mikroschritt-Ansteuerung ja
schnell sehr knapp. Oder habe ich deine Erklärung nicht richtig
verstanden?
Mit freundlichen Grüßen
Thorsten Ostermann
Jihaaaaa!
es funktioniert! Allerdings auf ganz eigene Art und Weise - da fährt man
doch noch häufig am besten mit.
Sobald das ganze vernünftig dokumentiert ist, werde ich es hier zum
besten geben ...
Ich bin mir noch nicht 100%tig sicher ob die Anlaufzeit exakt
eingehalten wird, aber er läuft schonmal sauber bis zur gesetzten
Geschwindigkeit hoch und in der Anlaufdauer steckt auf jedenfall ein zu
erkenndes Muster.