Servogeschwindigkeit über Programm ändern?!?

Hallo Leute,

ich habe einen Roboterarm gebaut, welcher über 6 Servomotoren über 6 Slide Potentiometer angesteuert werden soll. Das Programm dafür war realtiv leicht, allerdings ist mir aufgefallen, dass die Motoren sehr schnell auslenken.

Gibt es eine Möglichkeit den Servos eine gewisse Geschwindigkeit für einen Winkel vorzugeben?
Ich bin damit gerade ein bisschen überfordert und habe schon ein bisschen gegoogelt → eine Möglichkeit wäre den Winkel der Servos in mehrere Stufen zu unterteilen → ich tue mir schwer das umzusetzen, da ich ein Anfänger bin.

Könntet Ihr mir vielleicht helfen, dafür wäre ich euch sehr dankbar!!!

Damit ihr wisst, was ich bisher gemacht habe, folgt hier der Code:

#include <Servo.h>

// Servos

Servo servo1;
Servo servo2;
Servo servo3;
Servo servo4;
Servo servo5;
Servo servo6;

//Potentiometers

int pot1 = A0;
int pot2 = A1;
int pot3 = A2;
int pot4 = A3;
int pot5 = A4;
int pot6 = A5;

//Variable zum Werte auslesen

int valPot1;
int valPot2;
int valPot3;
int valPot4;
int valPot5;
int valPot6;

void setup()
{
//PWM Pins der Servos

servo1.attach(3);
servo1.write(90);  //Start Position Servo 1
servo2.attach(5);
servo2.write(90); // Start Position Servo 2
servo3.attach(6);
servo3.write(90); // Start Position Servo 3
servo4.attach(9);
servo4.write(70); // Start Position Servo 4
servo5.attach(10);
servo5.write(90);  // Start Position Servo 5
servo6.attach(11);
servo6.write(90); // Start Position Servo 6
}
void loop()
{
//Wert des Potentiometers auslesen
valPot1 = analogRead(pot1);
valPot1 = map (valPot1, 0, 1023, 0, 180); 
servo1.write(valPot1); 

valPot2 = analogRead(pot2);
valPot2 = map (valPot2, 0, 1023, 0, 175);
servo2.write(valPot2);

valPot3 = analogRead(pot3);
valPot3 = map (valPot3, 0, 1023, 0, 175);
servo3.write(valPot3);

valPot4 = analogRead(pot4);
valPot4 = map (valPot4, 0, 1023, 70, 175);
servo4.write(valPot4);

valPot5 = analogRead(pot5);
valPot5 = map (valPot5, 0, 1023, 0, 175);
servo5.write(valPot5);

valPot6 = analogRead(pot6);
valPot6 = map (valPot6, 0, 1023, 70, 175);
servo6.write(valPot6);

}

Vielen Dank schon mal im Vorraus!

Schönen Sonntag :slight_smile:

Hallo,

setze doch bitte denen Sketch in Code-Tags, dann können wir alle den besser lesen, auch am Mobilgerät.
Dazu die Schaltfläche </> oben links im Editorfenster verwenden. Das kannst du auch nachträglich mit deinem Sketch machen.

Gibt es eine Möglichkeit den Servos eine gewisse Geschwindigkeit für einen Winkel vorzugeben?

NEIN

Du kannst das Poti langsam bewegen: der Servo wird versuchen, so schnell wie möglich die gerade gegebene Position zu erreichen um insgesamt so langsam/schnell wie das Poti zu sein.

Mit einem Controller musst du nicht unbedingt die Poti-Stellung direkt auf den Servo geben.

Das kannst du wahlweise auch langsamer machen und zeitabhängig eine Wunsch-Servostellung errechnen, die sich beliebig langsam der Poti-Stellung annähert.
Also ist die Antwort auf deine Frage doch: JA

Im einfachsten Fall ergibt sich die Geschwindigkeit aus einem kleinen delay();  

int pwert;
void loop() {
  int rohwert = analogRead(A0);
  if ( rohwert > pwert) pwert++;
  else if (rohwert < pwert) pwert--;
  int swert = map (pwert, 0, 1023, 0, 180);
  servo1.write(swert);
  // andere Servos genauso
  delay(5); // 5 ms/Step, also 5.1 sek von 0 bis 1023 
}

Brauchst natürlich für mehrere Potis auch mehrere pwert Variable. rohwert und der swert könnten, da nur kurz gebraucht, für alle Potis/Servos die gleichen sein.

Hi

‘Von Haus aus’ kann Das die Servo-Lib nicht.
Hier geht Es im Normalfall auch nur darum, daß das Servo möglichst schnell am Zielpunkt ankommt.

Bei Dir könnte man eine Verzögerung künstlich einbauen, quasi eine State-Maschine pro Servo.

  1. int valPot1 ist unglücklich - Du wirst keine negativen Werte vom ADC erhalten oder zum Servo schicken
    Da Du aber Werte bis 1023 einlesen willst, kommst Du um int oder uint16_t nicht herum - kostet Beides den gleichen Platz - ist halt ‘unschön’.
  2. int pot1=A0 ist ebenfalls unglücklich - Dein Arduino hat nur Pin-Nummern im positiven Bereich und unter 256 Beinchen - hier spart Dir byte ein Byte Platz - wenn Du die Definition als const byte pot1=A1; schreibst, werden die ‘Zahlen’ (A0 ist auch nur ein Platzhalter für die Pin-Nummer des Analog-Input A0) direkt im Code eingetragen - dürfte unterm Strich sogar gar kein Platz brauchen.
  3. Da Du 6x ‘genau das Gleiche’ machst, kann man Das in eine Funktion auslagern oder zumindest die Einzelwerte in Arrays aufnehmen - Das vll. für Später.

So, Deine Zeitverzögerung in Pseudo-Code

unsigned long wartezeit=20;  //alle 20 ms wird das Servo um 1 Grad verdreht
unsigned long lastmillis=millis();

setup(){
aktPosi=90;        // Start-Position auf 90°
Servo(aktPosi);
}

loop(){
if (millis()-lastmillis>=wartezeit){
  if (sollPosi!=aktPosi){
    if (sollPosi<aktPosi){
       aktPosi--;
    }else{
      aktPosi++;
    }
    lastmillis+=wartezeit;
    Servo(aktPosi);
  }
}

Wenn Das für EIN Servo funktioniert, können wir Das auf 6 Servos aufblasen.

MfG

PS: (fast) gleicher Ansatz

Hallo

Ich habe da Mal was gemacht

Das ging mit zwei Servo s sollte aber auch mit mehreren gehen
Heinz

Oder man nimmt gleich eine Servo-Lib, die sowas kann. Z.B. meine ‘MobaTools’ können das. Dann muss man sich im Sketch überhaupt nicht darum kümmern.
Es gibt aber auch noch andere Servo-Libs, die das können. Die mitgelieferte Servo-Lib ist da etwas simpel gestrickt.

Hallo,

ein Servo fährt wie Micha schon schrieb direkt und so schnell wie es kann auf die neue Position. Alle Positionen sind “nur” bestimmte Pulsbreiten. Die Kunst besteht nun darin die Pulsbreite von der aktuellen zur neuen nachzuführen also sanft zu ändern was Zwischenwerte erfordert. Es erfordert etwas Anpassung und Geduld damit es softig aussieht. Ist die Pulsänderung zu klein, ist das Servo zu schnell auf der neuen Zwischenposition und wartet auf die nächsten Pulsbreite. Ist die Pulsänderung zu groß fährt es quasi durch.

Ich mach das rein mit Änderung der Pulsbreite. Ohne jedes delay. Kannste auf bis zu 8 Servos anpassen ohne die Standard 20ms zu strecken. Zum erweitern
ab Zeile 17 neue Servos dazu,
ab Zeile 40 Inits dazu,
ab Zeile 54 falls notwendig individuelle Schrittweiten dazu, ansonsten gilt Standardwert Zeile 46 der dann nicht überschrieben wird,
ab Zeile 87 erweitern, Prinzip sollte erkennbar sein

Du brauchst jetzt nur noch ein Mapping von deinen analog Read auf Werte zwischen 1999 … 3999 counts. Bei mir basiert das alles auf Pulsbreiten in µs und zwar das doppelte. Entspricht 1ms … 2ms Pulsbreite. Hängt mit der Timereinstellung zusammen.

/*
  Doc_Arduino - german Arduino Forum
  IDE 1.8.5
  Arduino Mega2560

  Servosteuerung nach Vorbild RC Anlage (Danke an Falk Brunner für die Unterstützung)
  combie Pin Definition angewendet
  11.04.2018
*/

#include <util/atomic.h>
#include <CombiePinDefinition.h>

using namespace Combie::PinDefinition;
Servos<23> Servo_0;    // Pin und Name 
Servos<24> Servo_1;
Servos<25> Servo_2;

const byte SUM_SERVOS = 3;              // Anzahl der Servos

typedef enum {MOVE, DELAY, OFF} state_servos;          // Steuerzustände

typedef struct servo_data_t {           // Datentyp ... Eigenschaften eines Servos
    byte stepWidth;                     // Schrittweite > Stellgeschwindigkeit 
    unsigned int pulscount;             // aktuelle Position (2 counts = 1µs)
    unsigned int pulscount_New;         // neue Position     
}servo_data;

servo_data servo[SUM_SERVOS];           // alle Servos zusammengefasst

const unsigned int PULSCOUNT_MIN = 1999;        // Standard 1ms
const unsigned int PULSCOUNT_MAX = 3999;        // Standard 2ms
const unsigned int PULSPAUSENCOUNT = 39999;     // Standard 20ms
unsigned int servo_pausecount = PULSPAUSENCOUNT;     
volatile bool flag_servos_updated = false;

void setup()  { 
  Servo_0.init();
  Servo_1.init();
  Servo_2.init();
  
  // Servos default settings
  for (byte i=0; i<SUM_SERVOS; i++) { 
    servo[i].pulscount = 2999;            // alle Servos auf Mitte (2 count = 1µs) 
    servo[i].pulscount_New = 2999;        // alle Servos auf Mitte  
    servo[i].stepWidth = 255;             // max. Stellgeschwindigkeit pauschal für alle     
  }
   
  preSet_Timer1();
  run_Timer1();                           // entweder das oder turn_on_off_servos()

  // Bsp. individuelle Stellgeschwindigkeit
  servo[0].stepWidth = 20;                
  servo[1].stepWidth = 30;                 
}


void loop() {

  // Funktion zur Pulsabschaltung nach x [ms]
  //turn_on_off_servos(2500);              // entweder das oder run_Timer1() in setup
  
  if (flag_servos_updated == true) {
    calc_ServoPositions();
    flag_servos_updated = false;
  }

  change_Servoposition();               // Demo
    
} // loop Ende


// ****** Funktionen ******* //

ISR(TIMER1_COMPB_vect)    // wird aller >1ms aufgerufen
{  
   static byte servoNr = 0;
   static unsigned int tcount = 0;
    
   switch(servoNr) {    
      // Count  1999 =  1ms
      // Count  3999 =  2ms
      // Count 39999 = 20ms
      case 0:                Servo_0.ein(); tcount = servo[servoNr].pulscount; break;
      case 1: Servo_0.aus(); Servo_1.ein(); tcount = servo[servoNr].pulscount; break;  
      case 2: Servo_1.aus(); Servo_2.ein(); tcount = servo[servoNr].pulscount; break;  
      case 3: Servo_2.aus();                tcount = servo_pausecount;         break;  // restliche Pulspausenzeit
   }
   
   OCR1B += tcount;  
   servoNr++;
   if (servoNr > SUM_SERVOS) {
    flag_servos_updated = true;
    servoNr = 0;                  
   }                  
}


void calc_ServoPositions ()      
{          
   // *** alle Pulszeiten aktualisieren ***
   for (byte i=0; i<SUM_SERVOS; i++) {
      if (PULSCOUNT_MIN <= servo[i].pulscount_New && servo[i].pulscount_New <= PULSCOUNT_MAX) {
         
         if (servo[i].pulscount_New > servo[i].pulscount) {           // Bsp.: 3800 > 3700
            servo[i].pulscount += servo[i].stepWidth;                 // Bsp.: 3800 + 250 = 4050
            if (servo[i].pulscount > servo[i].pulscount_New) {        // Bsp.: 4050 > 3800
               servo[i].pulscount = servo[i].pulscount_New;           // Maximalwertbegrenzung = 3800   
            }
         }
         else if (servo[i].pulscount_New < servo[i].pulscount) {
            servo[i].pulscount -= servo[i].stepWidth;
            if (servo[i].pulscount < servo[i].pulscount_New) {
               servo[i].pulscount = servo[i].pulscount_New;
            }
         }
      }      
   }
   
   // *** restliche Pulspausenzeit berechnen nach Abzug aller Pulszeiten ***
   servo_pausecount = PULSPAUSENCOUNT;        // entspricht 20ms
   for (byte i=0; i<SUM_SERVOS; i++) {
      servo_pausecount = servo_pausecount - servo[i].pulscount;
   }
}


void turn_on_off_servos (unsigned int off_delay)     
{
   static state_servos state_SERVO_MODE = MOVE;  
   static unsigned long last_ms = 0;
       
   // *** prüfen ob irgendein Servo eine neue Position anfahren soll
   for (byte i=0; i<SUM_SERVOS; i++) {
      if (servo[i].pulscount != servo[i].pulscount_New) {
         state_SERVO_MODE = MOVE;
      }
   }
   
   switch (state_SERVO_MODE) {
      case MOVE:  // Pulserzeugung erneut einschalten
                  run_Timer1();
                  state_SERVO_MODE = DELAY;
                  last_ms = millis();
                  break;

      case DELAY: // wenn alle Servopulse fertig geformt und Delaytime vorbei, dann Puls abschalten
                  if (millis()-last_ms > off_delay)  {
                    if (flag_servos_updated == true) {    
                      stop_Timer1(); 
                      state_SERVO_MODE = OFF;
                    }
                  }
                  break;
                  
      case OFF:   // warten bis Pulserzeugung wieder benötigt wird
                  break;
      
      default:    state_SERVO_MODE = OFF;
                  break;
   }
}


void preSet_Timer1 ()     // Normal Mode, Servo Pulserzeugung
{
  cli();                  // Interrupts ausschalten
  TCCR1A = 0;             // Reset 
  TCCR1B = 0;             // 
  TIMSK1 = 0;             // 
  TCNT1  = 0;             // 
  OCR1B  = 0;             // 
  TIMSK1 = (1<<OCIE1B);   // enable Compare Match B ISR
  sei();                  // Interrupts einschalten
}  


void run_Timer1 ()        // Servo Pulserzeugung      
{
  ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {
    TCCR1B |= (1 << CS11);  // Prescaler 8
  }
}


void stop_Timer1 ()       // Servo Pulserzeugung
{
  ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {
    TCCR1B &= ~( (1<<CS12)|(1<<CS11)|(1<<CS10) ); 
  }
}

 
void change_Servoposition()                     // Demo
{
  static unsigned long last_ms = 0;
  const unsigned int intervall = 7000;          // aller 7s bewegen
  
  if ( millis() - last_ms < intervall)  return;
  last_ms += intervall;

  for (byte i=0; i<SUM_SERVOS; i++) { 
    servo[i].pulscount_New += 2000;             // etwas bewegen
    if (servo[i].pulscount_New > PULSCOUNT_MAX) {
      servo[i].pulscount_New = PULSCOUNT_MIN;    
    }
  }
}

CombiePinDefinition.zip (3.39 KB)