Schrittmotor-Servo

Hallo Leute,

vorab, ich habe noch nicht die beste Erfahrung mit C++ deswegen bitte ich dies zu berücksichtigen.
Mein Ziel ist es aus einen Schrittmotor (Nema 17) und einen 270-Grad-Poti einen Servo-Motor zu bauen.
Ich habe ein Programm entworfen das ein PWM-Signal empfängt und auswertet.. Dabei findet ein Soll/Ist-Vergleich zwischen PWM und den aktuellen Standes des Potis statt, befindet sich der Analog-Wert (Ist) unter oder über dem PWM-Wert (Soll), Bekommt der Schrittmotorendriver(Easy-Driver) den Auftrag den Motor in die entsprechende Richtung zu bewegen, um auf das Soll zu kommen. Bis dahin funktioniert es eigentlich reibungslos. Nun habe ich das Problem das man das PWM und den Poti nie ganz sauber bekommt sodass sie immer so um ein paar Inkremente schwanken. Das bedeutet, dass der Motor an seinem Soll-Punkt anfängt zu sehr stark zu zittern.
Meine Idee wäre es ja gewesen an dem Soll-Punkt einen Bereich von so ca. +/- 10 Inkremente festzulegen, sodass wenn der Poti in diesen Bereich kommt der Step-Pin des Easy-Drivers auf LOW setzt... Wäre das möglich ? wenn ja wie ?

Ich wäre dankbar für jeden Lösungsansatz!

Grüße

Robert


int pwm_period;
volatile boolean done;
unsigned long start;
 

unsigned long currentMillis;
long previousMillis = 0;
long interval = 20;
#define poti  A9

int potival;
// Servo PWM input pin
#define  PWM_IN 7
const byte step_pin = 1; 
const byte dir_pin = 3;
const byte MS1 = 4; 
const byte MS2 =  5 ;
const byte SLEEP = 6;

int sollpot;
int istpot;

void setup() {
// stepper-Driver 
pinMode(MS1,OUTPUT);
pinMode(MS2,OUTPUT);
pinMode(dir_pin,OUTPUT);
pinMode(step_pin,OUTPUT);
pinMode(SLEEP,OUTPUT);


 
digitalWrite(MS1,HIGH);
digitalWrite(MS2, HIGH);

/* Configure type of Steps on EasyDriver:
// MS1 MS2
//
// LOW LOW = Full Step //
// HIGH LOW = Half Step //
// LOW HIGH = A quarter of Step //
// HIGH HIGH = An eighth of Step //
*/
digitalWrite(SLEEP,HIGH);
delay(5);
 
  
  pinMode(PWM_IN, INPUT);
 
  
  attachInterrupt(digitalPinToInterrupt(PWM_IN), measurePulse, CHANGE);
 

  SerialUSB.begin(9600);
 
}
 
void measurePulse() {
  if (digitalRead(PWM_IN) == HIGH) {
    // Beginning of pulse, mark time in start variable
    start = micros();
  }
  else {
    // End of pulse, determine pulse width
    pwm_period = micros() - start;
    done = true;
  }
}
 
void loop() {

 
  currentMillis = millis();
 

  if (currentMillis - previousMillis >= interval) {
    // Reset current timestamp
    previousMillis = currentMillis;
 
    // Break if not finished measuring pulse width
    if (!done)
      return;
 
    // Print pulse width measurement to serial monitor
    SerialUSB.println (pwm_period);
 
    // Reset done flag
    done = false;
 
  }

potival = analogRead(poti);

potival =map(potival, 0, 1023, 564,2401);


SerialUSB.println (potival);

potival = istpot;
pwm_period= sollpot;

// Soll/Ist-Vergleich 
  
    if (potival > pwm_period) 

     {  
      
        digitalWrite(dir_pin, HIGH);  // (HIGH = anti-clockwise / LOW = clockwise)
        digitalWrite(step_pin, HIGH);
        delay(1);
        digitalWrite(step_pin, LOW);
        delay(1);

       
     }
   
   if (potival < pwm_period) 
   
{
  
        digitalWrite(dir_pin, LOW);  // (HIGH = anti-clockwise / LOW = clockwise)
        digitalWrite(step_pin, HIGH);
        delay(1);
        digitalWrite(step_pin, LOW);
        delay(1);

}

}

Auf was für einem MC soll das laufen?
Davon ist u.a. die Auflösung des ADC abhängig.
Auf nem Atmega kannst Du mit einem Pendeln von 2-3 digit rechnen.

Warum nimmst Du nicht gleich einen Servo, anstelle dieses Umwegs?

Gruß Tommy

Etwa so:
if (potival > pwm_period + 10)
oder für leichtes Ändern eine Konstante statt 10. Für < entsprechend.

Oder mit Hysterese, dann muß man sich merken, in welche Richtung der Motor zuletzt gefahren ist.

Hallo,

gesteuert wird das ganze mit einem Seeeduiono Xiao , über einen Pegelwandler bekommt er die Signale.. aber es spielt ja erstmal keine Rolle meine Frage wahr wie man am besten diesen Bereich festlegen kann.. das Fein-Tuning kann ich dann machen.

ja das stimmt schon aber es hat zum einen einen baulichen Hintergrund und einen persönlichen Aspekt weil ich das ganze eigentlich sehr interessant finde : D

okay, aber das bedeutet ja, dass ich nur in (+) eine Toleranz von 10 habe wie kann ich das Gleichzeitig auch in (-) machen ?
if (potival > pwm_period + 10) & if (potival > pwm_period - 10) ?

Ok.
Ich bin nur aus Deinem Code nicht schlau geworden.
An dieser Stelle:

ist potival immer auf 0.
Das map soll doch sicher istPot werden.
Ich würde mehrere Werte einlesen und daraus eine Mittelwert bilden. Das klappt meist ganz gut.

int pwm_period;
volatile boolean done;
unsigned long start;


unsigned long currentMillis;
long previousMillis = 0;
long interval = 20;
#define poti  A9

int potival;
// Servo PWM input pin
#define  PWM_IN 7
const byte step_pin = 1;
const byte dir_pin = 3;
const byte MS1 = 4;
const byte MS2 =  5 ;
const byte SLEEP = 6;

int sollpot;
int istpot;
uint8_t zaehler;
const byte anzahl = 12;
uint16_t potiVal[anzahl];

void setup()
{
  // stepper-Driver
  pinMode(MS1, OUTPUT);
  pinMode(MS2, OUTPUT);
  pinMode(dir_pin, OUTPUT);
  pinMode(step_pin, OUTPUT);
  pinMode(SLEEP, OUTPUT);
  digitalWrite(MS1, HIGH);
  digitalWrite(MS2, HIGH);
  /* Configure type of Steps on EasyDriver:
    // MS1 MS2
    //
    // LOW LOW = Full Step //
    // HIGH LOW = Half Step //
    // LOW HIGH = A quarter of Step //
    // HIGH HIGH = An eighth of Step //
  */
  digitalWrite(SLEEP, HIGH);
  delay(5);
  pinMode(PWM_IN, INPUT);
  attachInterrupt(digitalPinToInterrupt(PWM_IN), measurePulse, CHANGE);
  SerialUSB.begin(9600);
}

void measurePulse()
{
  if (digitalRead(PWM_IN) == HIGH)
  {
    // Beginning of pulse, mark time in start variable
    start = micros();
  }
  else
  {
    // End of pulse, determine pulse width
    pwm_period = micros() - start;
    done = true;
  }
}

uint16_t readAnalog()
{
  static uint16_t returnWert = 564;
  potiVal[zaehler] = analogRead(poti);
  zaehler++;
  if (zaehler == anzahl)
  {
    zaehler = 0;
    uint16_t maxWert = 0;
    uint16_t minWert = 1023;
    uint32_t summeWert = 0;
    for (byte b = 0; b < anzahlWerte; b++)
    {
      if (potiVal[b] > maxWert)
      {
        maxWert = potiVal[b];
      }
      if (potiVal[b] < minWert)
      {
        minWert = potiVal[b];
      }
      summeWert += potiVal[b];
    }
    summeWert -= maxWert;
    summeWert -= minWert;
    summeWert /= anzahl - 2;
    {
      returnWert = map(summeWert, 0, 1023, 564, 2401);
    }
    return returnwert;
  }
}

void loop()
{
  currentMillis = millis();
  if (currentMillis - previousMillis >= interval)
  {
    // Reset current timestamp
    previousMillis = currentMillis;
    // Break if not finished measuring pulse width
    if (!done)
    { return; }
    // Print pulse width measurement to serial monitor
    SerialUSB.println (pwm_period);
    // Reset done flag
    done = false;
  }
  istPot = readAnalog();
  SerialUSB.println (istpot);
  pwm_period = sollpot;
  // Soll/Ist-Vergleich
  if (istpot > pwm_period)
  {
    digitalWrite(dir_pin, HIGH);  // (HIGH = anti-clockwise / LOW = clockwise)
    digitalWrite(step_pin, HIGH);
    delay(1);
    digitalWrite(step_pin, LOW);
    delay(1);
  }
  if (istpot < pwm_period)
  {
    digitalWrite(dir_pin, LOW);  // (HIGH = anti-clockwise / LOW = clockwise)
    digitalWrite(step_pin, HIGH);
    delay(1);
    digitalWrite(step_pin, LOW);
    delay(1);
  }
}

Hallo,

nicht getestet, sowas in der Art

if( sollwert > istwert + Hyst){
fahrePlus;
}
if(sollwert < istwert- Hyst){
fahreMinus;
}

Du könntest aber auch auf den Istwert ganz verzichten und das Poti beim Start nur als Referenz einlesen. Wenn der Stepper richtig ausgelegt ist und mit einer Rampe betrieben wird geht dem normal nichts verloren. Dann kannst Du ihn mit einer Lib absolut verfahren.
Heinz

Abend,
vielen Dank für deine Idee aber leider bekomme ich deinen Code nicht kompiliert... ich bekomme es leider nicht raus. ich glaube er meckert die eckigen Klammern an.

Grüße Robert

Wie wäre es denn damit, uns die konkrete Fehlermeldung zukommen zu lassen?

Gruß Tommy

Okay wie ist das mit der Hysterese gemeint? soll ich an diesem Punkt meine Toleranz angeben ?

Grüße

Robert

Ok, ich hab das mal korrigiert - da Du mit dem serial.print anders umgehst, musste ich das auskommentieren.
Ich erklär mal kurz was passiert.
Es werden 12 Werte aufgenommen und wenn die alle aufgenommen sind, wird der höchste und der niedrigste Wert ermittelt und verworfen.
Es bleiben übrig 10 Werte, aus denen ein Mittelwert gebildet wird.

In dieser Version werden nach dem auswerten alle Werte verworfen und das Array mit 12 neuen Werten gefüllt.
Wenn Du das nicht willst, sondern die vorherigen Werte erhalten bleiben sollen "gleitend rechnen", dann musst Du die Klammer aus Zeile 96 deaktivieren und Zeile 74 setzen.
Ich habs kommentiert. (Dann kann auch das static vor dem returnwert entfallen)

Deine Warnung für Deine intervallgeschcihte auch bereinigt.

int pwm_period;
volatile boolean done;
unsigned long start;


unsigned long currentMillis;
uint32_t previousMillis = 0;
uint32_t interval = 20;
#define poti  A9

int potival;
// Servo PWM input pin
#define  PWM_IN 7
const byte step_pin = 1;
const byte dir_pin = 3;
const byte MS1 = 4;
const byte MS2 =  5 ;
const byte SLEEP = 6;

int sollpot;
int istpot;
uint8_t zaehler;
const byte anzahl = 12;
uint16_t potiVal[anzahl];

void setup()
{
  // stepper-Driver
  pinMode(MS1, OUTPUT);
  pinMode(MS2, OUTPUT);
  pinMode(dir_pin, OUTPUT);
  pinMode(step_pin, OUTPUT);
  pinMode(SLEEP, OUTPUT);
  digitalWrite(MS1, HIGH);
  digitalWrite(MS2, HIGH);
  /* Configure type of Steps on EasyDriver:
    // MS1 MS2
    //
    // LOW LOW = Full Step //
    // HIGH LOW = Half Step //
    // LOW HIGH = A quarter of Step //
    // HIGH HIGH = An eighth of Step //
  */
  digitalWrite(SLEEP, HIGH);
  delay(5);
  pinMode(PWM_IN, INPUT);
  attachInterrupt(digitalPinToInterrupt(PWM_IN), measurePulse, CHANGE);
  //SerialUSB.begin(9600);
}

void measurePulse()
{
  if (digitalRead(PWM_IN) == HIGH)
  {
    // Beginning of pulse, mark time in start variable
    start = micros();
  }
  else
  {
    // End of pulse, determine pulse width
    pwm_period = micros() - start;
    done = true;
  }
}

uint16_t readAnalog()
{
  static uint16_t returnWert = 564;
  potiVal[zaehler] = analogRead(poti);
  zaehler++;
  if (zaehler == anzahl)
  {
    zaehler = 0;
    // } Diese Klammer einkommentieren, damit gleitend gerechnet wird
    uint16_t maxWert = 0;
    uint16_t minWert = 1023;
    uint32_t summeWert = 0;
    for (byte b = 0; b < anzahl; b++)
    {
      if (potiVal[b] > maxWert)
      {
        maxWert = potiVal[b];
      }
      if (potiVal[b] < minWert)
      {
        minWert = potiVal[b];
      }
      summeWert += potiVal[b];
    }
    summeWert -= maxWert;
    summeWert -= minWert;
    summeWert /= anzahl - 2;
    {
      returnWert = map(summeWert, 0, 1023, 564, 2401);
    }
  } // Diese Klammer auskommentieren, wenn die Klammer in Zeile 74 aktiv ist
  return returnWert;
}

void loop()
{
  currentMillis = millis();
  if (currentMillis - previousMillis >= interval)
  {
    // Reset current timestamp
    previousMillis = currentMillis;
    // Break if not finished measuring pulse width
    if (!done)
    { return; }
    // Print pulse width measurement to serial monitor
    //SerialUSB.println (pwm_period);
    // Reset done flag
    done = false;
  }
  istpot = readAnalog();
  //SerialUSB.println (istpot);
  pwm_period = sollpot;
  // Soll/Ist-Vergleich
  if (istpot > pwm_period)
  {
    digitalWrite(dir_pin, HIGH);  // (HIGH = anti-clockwise / LOW = clockwise)
    digitalWrite(step_pin, HIGH);
    delay(1);
    digitalWrite(step_pin, LOW);
    delay(1);
  }
  if (istpot < pwm_period)
  {
    digitalWrite(dir_pin, LOW);  // (HIGH = anti-clockwise / LOW = clockwise)
    digitalWrite(step_pin, HIGH);
    delay(1);
    digitalWrite(step_pin, LOW);
    delay(1);
  }
}

Sorry, ich denke ich habe es.. es haben Deklarationen gefehlt..

Ne.
Nur Rechtschreibfehler. :wink:

Alles klaro, vielen vielen Dank..
Ich werde es ausprobieren, Danke für detaillierte Beschreibung das hat sehr geholfen!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.