Forum: PC-Programmierung Lineare Rampe f. Schrittmotor nach David Austin


von Helge P. (duese1990)


Lesenswert?

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
const long cpufreq = 16000000; // CPU Takt [Hz]
3
const long f = 1000000; // Frequenz des Timers [Hz]
4
const int prescaler = 1; // Timervorteiler
5
int overf1; // Overflowvaribale Timer1 (Legt die Timerfrequenz fest)
6
7
// Maschinendaten
8
int p = 10; // Steigung der Spindel [mm]
9
int res = 1600; // Steps pro Umdrehung, 1600 = 1/8 Betrieb
10
11
// Bewegungsdaten
12
double v_max = 60; // Endgeschwindigkeit [mm/s]
13
double t = 2; // Beschleunigungszeit [s]
14
double omega; // Winkelgeschwindigkeit [rad/s]
15
double omega_d; // Winkelbeschleunigung [rad/s^2]
16
double alpha; // Schrittwinkel pro Step [rad]
17
18
// weitere Variablen
19
const double pi = 3.141593; // pi
20
long tn; // absoluter Zeitpunkt des Steps
21
long i = 0; // Timeraufrufe seit letzten Step
22
long n = 1; // aktueller Beschleunigungsstep
23
long cnt0; // Zeit bis zum ersten Step
24
long cntn; // Zeit zwischen letztem und nächstem Step
25
long n_end; // zur Beschleunigung erforderliche Schritte
26
27
void setup()
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
void Timer1(int overf1)
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
void loop()
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

von KPL (Gast)


Lesenswert?

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?

von Helge P. (duese1990)


Lesenswert?

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!

von KPL (Gast)


Lesenswert?

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

von KPL (Gast)


Lesenswert?

Sorry für die Rechtschreibung, ich schäm mich gerade selber. So spät 
sollte ich nichts mehr schreiben :-)

von Helge P. (duese1990)


Lesenswert?

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^^.

von Kai G. (kpl)


Lesenswert?

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

von chloro (Gast)


Lesenswert?

float und insbesonders double sind murks. long muessen reichen.

von Helge P. (duese1990)


Lesenswert?

ok! ich bau heut abend mal wieder auf die approximierte formel um ...

von Thorsten O. (Firma: mechapro GmbH) (ostermann) Benutzerseite


Lesenswert?

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

von Helge P. (duese1990)


Lesenswert?

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.

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.