Forum: Mikrocontroller und Digitale Elektronik Schrittmotor im Sinus laufen lassen


von Knatalie (Gast)


Lesenswert?

Hallo ihr lieben

Mein Schrittmotor verliert haufenweise schritte und zwar schon im µC 
Programm.

Ich will einen Schrittmotor im Sinus hin und her drehen lassen.
Das ziel ist es mit zwei Schrittmotoren so eine Kreisbewegung zu 
erzeugen, wobei sich dann die beiden Sinus-Bewegungen zu einem Kreis 
überlagern.

aber erst mal geht es nur um die X-Achse.

Bei meinem Projekt ist es wichtig, dass die Zeit exakt eingehalten wird. 
(die Frequenz des Sinus)

Deshalb kann ich nicht wie üblich die Schrittmotoren so ansteuern, dass 
sie einfach ihre Steps abfahren, bis der Job erledigt ist. (das dauert 
nämlich nicht immer gleich lange.


Deshalb habe ich Folgendes Programm geschrieben.
Es läuft auf einem Arduino bestückt mit einem ATMEGA 320
Was da für ein Schrittmotor Treiber dran hängt ist erst mal egal, da 
meine Probleme schon im Programm liegen.

Das Programm generiert eine Sinuskurve, die die anzufahrende Position zu 
einer bestimmten Zeit darstellt.
alle 0.1 Sekunden wird diese Position mit der vorherigen abgeglichen und 
der Motor mit der entsprechenden Geschwindigkeit weiterbewegt, um die 
nächste Position in der richtigen zeit zu erreichen.

Das ganze funktioniert auch auf den ersten Blick, aber leider gehen 
viele Schritte verloren.

So ist eine exakte Ansteuerung nicht möglich.

ich gehe mal davon aus, dass das an der Programmlaufzeit liegt, die den 
Motor Takt durcheinander bringt.

aber wie kann ich das besser programmieren?

ich würde mich über Hilfe sehr freuen.
das kann ja nicht so schwer sein ein Schrittmotor exakt im Sinus laufen 
zu lassen.



hier der code:



1
int stepperXPin =  13;   
2
int stepperXDirPin =  4;   
3
4
5
float i = 0.0000001; 
6
int amplitude = 2000;
7
8
int motor_statusX = 0;   // motor pin HIGH oder LOW
9
float stepsX = 0;        // Anzahl an Schritten die der Motor im naechsten Intervall fahren soll
10
int stepCountX = 0;      // Anzahl der noch zu fahrenden schritte
11
float positionOldX = 0; 
12
float positionX = 0;
13
long previousMillis = 0;   // Timer fuer Interval
14
long zeitaltX = 0;         // Timer fuer Motor-Takt
15
16
17
void setup()   {                
18
  pinMode(stepperXPin, OUTPUT); 
19
  pinMode(stepperXDirPin, OUTPUT);     
20
}
21
22
23
void loop()                     
24
{
25
  
26
  //-----------------Sinus Signal Erzeugung-------------------------//
27
  
28
  
29
  long interval = 100000;  //0.1 sekunden // Aufloesung in der die Sinuskurve in Geraden unterteilt wird.  
30
  if (micros() - previousMillis > interval) {  // takt für das ubergeben des nechsten sinus werts. 
31
    previousMillis = micros();
32
33
    positionX = ((sin(i)*amplitude)); // erzeugen des sinus signals multipliziert mit der Amplitude (Anzal der Schritte)
34
    stepsX = positionOldX - positionX;  // zu gehende schitt anzahl wird ermittet. 
35
    stepCountX = abs(stepsX);
36
    positionOldX = positionX;
37
38
    if(stepsX < 0){ //Motor richtung wird gesetzt
39
      digitalWrite(stepperXDirPin, HIGH); 
40
    }
41
    else {
42
      digitalWrite(stepperXDirPin, LOW); 
43
    } 
44
    
45
    i = i+0.1;  // der zaehler für die Sinuskurve wird um 0.1 erhoeht. das ergibt bei der Sinus laenge von 2 pi eine Aufloesung von 63 pro Sinus durchlauf
46
  }
47
48
49
//-----------------Motor Steuerung-------------------------//
50
51
52
  if(stepCountX > 0){  // solange es noch schritte zu gehen gibt
53
    if (micros() - zeitaltX >= (interval/abs(stepsX*2)) ) { // wird der motor im entsprechenden takt gepulst. (zeit geteilt durch schritte)(schitte mal zwei, da pro Schritt eimal HIGH und LOW geschaktet werden muss
54
55
      zeitaltX= micros();
56
      if (motor_statusX == 1){
57
        digitalWrite(stepperXPin, LOW);
58
        stepCountX--;
59
        motor_statusX = 0;
60
61
      }
62
      else
63
      {
64
        digitalWrite(stepperXPin, HIGH);
65
        motor_statusX = 1;
66
      }
67
    }
68
  }
69
70
}

von MaWin (Gast)


Lesenswert?

previousMillis=micro() // warum milli(sekunden) und micro(sekunden) ?
positionOldX=sin(0)*amplitude;

void loop()
{
  t=micros(); // immer nur 1 Aufruf des Zeitgebers in der Schleife!
  positionX=sin(t/1000000.0)*amplitude; // sonst unterschiedliche
  if(positionX>positionOldX)              // Zeiten möglich
    digitalWrite(stepperXDirPin, HIGH); // (ich verwende sie eh nur 1x)
  else
    digitalWrite(stepperXDirPin, LOW);
  if(positionX!=positionOldX)
  {
    if(abs(positionX-positionOldX)>1) error("CPU zu langsam");
    digitalWrite(stepperXPin, motor_statusX=!motor_statusX); // 
Zuweisung!
    positionOldX=positionX
  }
}

Die Schleife läuft so schnell wie möglich.
Im Normalfall ist positionX immer gleich positionOldX
Nur manchmal ist positionX = positionOldX + 1
oder positionX = positionOldX - 1,
niemals darf die Abweichung mehr als 1 betragen.

Der Vorteil: Nicht nur daß man keine Schritte verliert
sondern die zeitliche Position der Schrittimpulse liegt
auch möglichst exakt dort, wo der Motor sich bewegen muss.

Bei dir läuft der Motor ruckartig, und kann der heftigen
Beschleunigung nicht folgen.

von Knatalie (Gast)


Lesenswert?

danke MaWin

Das ist ja ein ganz anderer Ansatz. nach so etwas habe ich gesucht. 
Bekomme dein Programm zwar noch nicht zum laufen, (der Stepper läuft 
einfach mit konstanter Geschwindigkeit hin und her)
aber ich bin dran.

von MaWin (Gast)


Lesenswert?

> (der Stepper läuft einfach mit konstanter Geschwindigkeit hin und her)

Das passiert, wenn in JEDEM Schleifendurchlauf eine andere Position 
rauskommt, die CPU also zu langsam ist.

Keine Ahnung, wie deine sin-Funktion ist, ob (langsames) floating-Point 
oder (ungenaue) Intergerinterpolation.

von Knatalie (Gast)


Lesenswert?

ok das Programm läuft jetzt ganz gut.

meine Variablen für die Positionen waren Floats, und müssen natürlich 
Ints sein, sonst wird ja nicht auf die ganzen Schritte gerundet...

es läuft bis 4000 schritte, danach wird der Prozessor zu langsam.

da ich ja zwei Motoren ansteuern muss wird das vielleicht etwas knapp, 
je nach dem wie viel das Programm sonst noch machen muss.

mal sehen.

vielen dank auf jeden Fall!!!

Kann man eine Motorsteuerung auch mit Interrups machen? das wäre doch 
wahrscheinlich am besten oder?

hat da jemand eine Beispiel Code?

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


Lesenswert?

Hallo MaWin!

> void loop()
> {
>   t=micros(); // immer nur 1 Aufruf des Zeitgebers in der Schleife!
>   positionX=sin(t/1000000.0)*amplitude; // sonst unterschiedliche
...
>   }
> }
>
> Die Schleife läuft so schnell wie möglich.

Auf einem 8-bit µC dürfte das nicht allzu schnell sein. Nicht umsonst 
gibt es seit Jahrzehnten (!) den Bresenham-Algorithmus, auch für 
Kreisfahrten. Der vermeidet nicht nur die Berechnung der 
Winkelfunktionen, sondern bei vernünftiger Implementierung auch 
Float-Operationen vollständig.

Mit freundlichen Grüßen
Thorsten Ostermann

von Michael (Gast)


Lesenswert?

Knatalie schrieb:
> meine Variablen für die Positionen waren Floats, und müssen natürlich
> Ints sein, sonst wird ja nicht auf die ganzen Schritte gerundet...

Das Runden wird nicht dein Problem sein, sondern die verfügbaren 
MegaFLOPs deines ATmega320

von Uwe (Gast)


Lesenswert?

Die passende Steuerung machts. Vieleicht hat deine ja schon nen 
electronic gear. Ansonsten selber machen ,dafür mußt du aber an die 
Stepclk und Dir inputs des Schrittmotortreiber ran und Mikroschritt wäre 
auch nicht schlecht.

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.