Solved: Servo Zittern in Verbindung mit anderen Librarys

jurs: Falls jemand zwei Servos hat und Lust zu testen, anbei mein Code für einen 2-Servo Test-Sketch:

Hallo Jurs, deine Lösung ist wie immer viel eleganter! Trotzdem eine Frage: Warum die beiden Pulse gleichzeitig, warum nicht nacheinander?

Den Code funktioniert (standalone) toll, aber wenn beide Servos den gleichen Winkel stellen sollen, dann geht eines der Servos auf Block. Wenn ich den Ausgabeteil einfach seriell schreibe, dann gehts:

//  noInterrupts();
  digitalWrite(pin1,HIGH); // ersten Refresh-Impuls  starten
  delayMicroseconds(pulselen1);
  digitalWrite(pin1,LOW);
  digitalWrite(pin2,HIGH); // zweiten Refresh-Impuls  starten
  delayMicroseconds(pulselen2);
  digitalWrite(pin2,LOW);
//  interrupts();

Ich habe übrigens auch die Empfindlichkeit meiner (digitalen) Servos auf die Einhaltung der 20ms getestet: kein Problem! Selbst ein "Refresh" mit mehr als 100ms Abstand macht das Servo zwar langsamer, aber es funktioniert.

guntherb: Trotzdem eine Frage: Warum die beiden Pulse gleichzeitig, warum nicht nacheinander?

Geschwindigkeit ist keine Hexerei: Wenn beide Servos Ihren Refresh-Impuls zur selben Zeit starten, und der eine beendet seinen Impuls früher und der andere etwas später, dann brauchen zwei Servos für die Ausführung der Funktion immer genau so lange wie ein Servo: Maximal 2 Millisekunden.

Wenn man die Refresh-Impulse nacheinander sendet, dann brauchen 2 Servos zusammen bis zu 4 Millisekunden, also doppelt so lange.

Die Ansteuerung soll ja auch ein bisschen effektiv sein. Im Endeffekt kann ich eine Ansteuerung für 15 oder 30 Servos schreiben und die Funktion braucht immer nur: maximal 2 Millisekunden für den Refresh-Impuls, egal wie viele Servos. Aber damit das klappt, müssen sie eben gleichzeitig mit dem Refresh-Impuls anfangen, und dann nacheinander abschalten, je weiter nach links der Servo gedreht ist, desto früher beendet er seinen Impuls und die anderen Servos dann nach und nach je nach Drehwinkel, bis der mit dem längsten Refresh-Impuls als letzter fertig wird, aber auch innerhalb von 2 Millisekunden.

Ich weiß, für ein lahmes Temperaturregelungsprogramm mit nur zwei Servos ist das völlig irrelevant. Aber es gibt auch Anwendungen, da sind 2 Millisekunden viel Zeit, die man nicht mal eben alle 20ms verschenken möchte.

guntherb: Den Code funktioniert (standalone) toll, aber wenn beide Servos den gleichen Winkel stellen sollen, dann geht eines der Servos auf Block.

Hm, dann funktioniert entweder die Funktion delayMicroseconds() mit dem Parameter "0" nicht richtig. Oder Deine Servos haben ein deutliches Problem mit der Stromversorgung, wenn beide Servomotoren in exakt derselben Mikrosekunde starten sollen. Bei Gleichstrommotoren ist es ja so: Der Anlaufstrom ist am höchsten. Die Stromaufnahme beim drehenden Motor ist geringer als beim Anlaufen im Moment des Einschaltens.

Vielleicht blockiert ein Motor, weil er zum Anlaufen nicht genug Strom erhält?

Ich habe bei mir auf dem Dachboden inzwischen einen einzelnen Servo wiedergefunden: Einen Graupner C601 von ca. 1980, und mit dem habe ich einige Tests gemacht. Ich teste aber nochmal weiter und schreibe dann nochmal etwas dazu.

Wie gesagt: Prüfe mal die Servo-Stromversorgung! Bei diesen Servos können kleinste Spannungsschwankungen zum Zittern führen und die Servos ziehen kräftig Strom!

Als Fehlerquelle beim Zittern kommen Interrupts und Spannungsschwankungen in Frage. Vielleicht hast Du neben den Servos auch noch andere Bauteile in der Schaltung, die schwankenden Strom ziehen?

Ich habe ehrlich gesagt das I2C-Display im Verdacht, dass das Spnnungsswankungen verursacht, wenn es angesteuert wird. Ich versorge meinen Servo momentan auf dem 5V Pin auf dem Arduino. Selbst wenn ich die Refresh-Impulse zwischen "noInterrupts()" und "interrupts()" packe, scheint das bei der Aktualisierung des Displays einen kleinen und mit der Fingerkuppe spürbaren "Zupp" zu geben. Und das kann eigentlich gar nicht sein.

guntherb: Ich habe übrigens auch die Empfindlichkeit meiner (digitalen) Servos auf die Einhaltung der 20ms getestet: kein Problem! Selbst ein "Refresh" mit mehr als 100ms Abstand macht das Servo zwar langsamer, aber es funktioniert.

Das ist schön. Mein Graupner "Vintage" Servo von 1980 zeigt folgendes Verhalten: Offenbar erfolgt das Nachstellen der Servoposition immer nur eine ganz kurze Zeit nach einem Refresh-Impuls. Möglicherweise immer genau die 20 ms lang, bis der nächste Refresh-Impuls kommen sollte. Wenn ich bei diesem Servo mal eine Refresh-Zeit von 500 ms statt 20 ms einstelle, dann kannst Du beim Verstellen die Refresh-Impulse mitzählen: Der Servo braucht dann von 45 auf 135 Grad insgesamt 7 Refresh "Ticks" und läuft sieben mal ein ganz kleines Stück, bis er den gesamten Verstellweg zurückgelegt hat. D.h. den Verstellweg, den der Servo bei 20ms Refresh in weniger als 0,2s zurücklegt, legt er dann in 3,5s zurück. Zwischendurch ist er dann immer im Zustand "nicht angesteuert" und läßt sich leicht von der Sollposition wegdrücken. Aber im Endeffekt: Der Servo erreicht mit beliebig langen Refresh-Zeiten immer auch seine Sollposition, auch wenn es dann sehr lange dauern kann.

Wie gesagt, ich betreibe wegen des Zitterns und seiner möglichen Gründe nochmal ein bisschen Ursachenforschung.

Wie jurs schrieb; sollte die Spannungsversorgung nicht stabil bzw. stabilsiert sein (vom Board (wegen zuviel Last) oder mit Netzteil) dann Zittern die Servos ebenfalls.
Sollte das Fall sein, einfach einen Spannungsstabilisator z.B. 7807 oder 7809 oder 7812 vorschalten.

Hallo Jurs,

vielen Dank für deine Mühe!

Die Versogungsspannung ist ok. Die Servos werden aus einer externen 6V-Quelle gespeist, die mit kurzem Kabel mit NiMh-Akkus gepuffert ist. Das ist ok. (Hardware kann ich, nur mit der Software haperts!)

Bei den verlängerten Pausen zeigen meine Servos genau das Verhalten das du auch beschrieben hast: bei jedem Puls schiebt sich das Servo in die richtige Richtung, bis es irgendwann da ist. Für Anwendungen die Rückstellkräfte haben untauglich, für mich kein Problem.

Nochmal zum Verhalten bei gleichen Winkeln:
Wenn ich beide Servos mit gleichem Winkel ansteuere, dann wird Servo2 auf den richtigen Winkel gestellt, Servo1 aber geht auf Vollauschlag (>180°) und knurrt.

Deine Vermutung mit dem delayMicroseconds(0) scheint richtig zu sein.
Wenn ich bei Pulslängengleichheit den einen 1µs länger mache, funktionierts:

void twoServoRefresh(byte pin1, byte angle1, byte pin2, byte angle2)
{
  static unsigned long lastRefresh=0;
  if (millis()-lastRefresh<20) return; // noch keine 20 ms vergangen
  lastRefresh=millis(); // Zeit des erfolgten Refresh merken
  
  if (angle1>180) angle1=180; // Stellwinkel bis 180 Grad erlaubt
  if (angle2>180) angle2=180;
  int pulselen1 = MINPULSE + (MAXPULSE-MINPULSE)*(long)angle1/180;
  int pulselen2 = MINPULSE + (MAXPULSE-MINPULSE)*(long)angle2/180;
  int pulslendiff = pulselen1-pulselen2;
  if (pulslendiff == 0) pulslendiff = 1;
//  noInterrupts();
  digitalWrite(pin1,HIGH); // Beide Refresh-Impulse "gleichzeitig" starten
  digitalWrite(pin2,HIGH); // Beide Refresh-Impulse "gleichzeitig" starten
  if (pulselen1<pulselen2) // der erste Refresh-Impuls ist der kürzere
  {
    delayMicroseconds(pulselen1);
    digitalWrite(pin1,LOW);
    delayMicroseconds(pulselen2-pulselen1);
    digitalWrite(pin2,LOW);
  }
  else  // der zweite Refresh-Impuls ist der kürzere oder gleichlang
  {
    delayMicroseconds(pulselen2);
    digitalWrite(pin2,LOW);
    delayMicroseconds(pulslendiff);
    digitalWrite(pin1,LOW);
  }
//  interrupts();  
}

Jetzt werde ich mich mal dranmachen, und die Funkion in meinen gesamtcode mit einzubauen. Bin schon gespannt.

guntherb: Die Versogungsspannung ist ok. Die Servos werden aus einer externen 6V-Quelle gespeist, die mit kurzem Kabel mit NiMh-Akkus gepuffert ist. Das ist ok.

Was ich inzwischen noch herausgefunden habe: Senden auf der seriellen Schnittstelle scheint keine negativen Effekte zu haben (getestet bei 9600 Baud), aber das Empfangen von Zeichen auf Serial erzeugt einen kleinen "Zupp" am Servo, wenn Zeichen eintreffen. Den kann man dann mit der Aktivierung von "noInterrupts()" und "interrupts()" zwar vermeiden (dann herrscht wieder Ruhe), aber dadurch dürften beim Empfang auf Serial Zeichen verlorengehen.

guntherb: Bei den verlängerten Pausen zeigen meine Servos genau das Verhalten das du auch beschrieben hast: bei jedem Puls schiebt sich das Servo in die richtige Richtung, bis es irgendwann da ist. Für Anwendungen die Rückstellkräfte haben untauglich, für mich kein Problem.

Und plötzlich kann man Dinge testen und ausprobieren, mit denen einen die Servo-Library gar nicht experimentieren läßt.

guntherb: Nochmal zum Verhalten bei gleichen Winkeln: Wenn ich beide Servos mit gleichem Winkel ansteuere, dann wird Servo2 auf den richtigen Winkel gestellt, Servo1 aber geht auf Vollauschlag (>180°) und knurrt.

Deine Vermutung mit dem delayMicroseconds(0) scheint richtig zu sein. Wenn ich bei Pulslängengleichheit den einen 1µs länger mache, funktionierts

Wenn delayMicroseconds keinen Null-Parameter mag, würde ich beim zweiten delay vielleicht nur auf Ungleichheit der beiden delays testen und

im "else" Fall einsetzen:
if (pulselen1!=pulselen2) delayMicroseconds(pulselen1-pulselen2);

guntherb: Jetzt werde ich mich mal dranmachen, und die Funkion in meinen gesamtcode mit einzubauen. Bin schon gespannt.

Na dann auf gutes Gelingen! Vielleicht gibt es ja bei Ansteuerung aus der loop einen Unterschied zum Verhalten mit der Servo-Library.

Edit/Nachtrag: delayMicroseconds(0) ist tatsächlich buggy und führt zu einem delay von ca. 1020 Mikrosekunden. Böse Falle!

Oh ihr armen geplagten ... :. Wartet mal bis zum WE, dann bin ich wieder zu Hause und hau den Servos hier was an den Glockenaker. :disappointed_relieved:

Keiner hat Mitleid mit mir ! Ich schwitz mir hier den ganzen Tag auf Malle so was von dermaßen einen ab .... knapp 30° auf der Terrasse im Schatten. ... na ist ja bald zu Ende !

]:D TERWI

Armer TERWI, Sklave seiner Freizeit :P

Applaus

Applaus

Applaus

Dem Meister der Codierung!

Manchmal ist weniger wirklich mehr. Ich habe jetzt Jurs vereinfachten Servoansteuercode (leicht modifiziert mit der delayMicroseconds(0) Unterdrückung) in meinen Code eingebaut. Und, was soll ich sagen: Läuft.

Kein Knurren, kein Zucken, kein einziger Laut von den Servos! (ausser wenn Sie verstellt werden)

Vielen Dank tiefverneigend

Gunther

guntherb:
Manchmal ist weniger wirklich mehr.
Ich habe jetzt Jurs vereinfachten Servoansteuercode (leicht modifiziert mit der delayMicroseconds(0) Unterdrückung) in meinen Code eingebaut. Und, was soll ich sagen:
Läuft.

Kein Knurren, kein Zucken, kein einziger Laut von den Servos! (ausser wenn Sie verstellt werden)

Na Bitte!

Das hört sich ja nach einer erfolgreichen Problemlösung an, wenn das Zittern durch Weglassen der Servo-Library und Einbauen einer einfachen Servo-Refresh-Funktion zur Servoansteuerung beseitigt werden konnte.

So ungefähr hatte ich mir das gedacht. :smiley:

Wenn ich viel Zeit habe, müßte ich wohl nochmal eine “multiServoRefresh()” Funktion machen, falls jemand auf diese Art mehr als zwei Servos ansteuern möchte.

Edit/Nachtrag:
Ich habe gerade auch nochmal die SoftwareServo Library (“alte Servo-Library”) ausprobiert. Die Library funktioniert eigentlich sehr schön, es gibt keine namensgleichen Dateien mit der aktuellen Servo-Library und sie ist auch ganz einfach für Arduino-Version ab 1.0 anpassbar und kompilierbar, wenn man
#include <Arduino.h>
statt WProgram.h einbindet. Diese alte Servo-Lib hat nur ein großes und ein kleines Manko.
Das große Manko: Das einzige Beispiel zur Library ist fehlerhaft programmiert und funktioniert nicht
Das kleine Manko: Das Timing erfolgt in Zeitscheiben von 16 Mikrosekunden, so dass es zwischen 1000 us und 2000 us nicht entsprechend der Ansteuerung in Winkelgraden 181 verschiedene Servopositionen gibt, sondern nur 1000/16= 62 verschiedene Servopositionen, die tatsächlich zwischen 0 und 180 Grad angesteuert werden.

Das sieht nach ziemlichen Kinkerlitzchen aus und falls Bedarf besteht, könnte man natürlich die beiden Mankos beseitigen und auch die alte SoftwareServo Library problemlos unter modernen Arduino-Versionen lauffähig machen.

Hallo zusammen,

bin ganz neu hier also bei Fehlern nicht direkt steinigen.xD

Wie hier im Thread habe ich genau das gleiche Problem. Leider bekomme ich den von jurs gebaute sketch nicht in meinen gebastelt. Es wird wohl an den wieder kehrenden Refresh liegen, Wie bekomme ich den Regelmäßig in meinen Sketch? Zurzeit setze ich den Wert für die Position und führe danach einen Refresh durch. Wie kann ich den alle 20ms aufrufen?

so macht es jurs, dass sein twoServoRefresh beliebig oft ( jedenfalls mehrfach je ms ) aufgerufen werden kann und auch werden sollte

void twoServoRefresh(byte pin1, byte angle1, byte pin2, byte angle2)
{  static unsigned long lastRefresh;
  if (millis()-lastRefresh<20) return; // noch keine 20 ms vergangen
  lastRefresh=millis(); // Zeit des erfolgten Refresh merken
  ...
}

S. Eintrag vom 2013-10-15, 18:49:06

Wie kann ich den alle 20ms aufrufen?

Falscher Anzatz, die Funktion sorgt selbst dafür, nur alle 20 ms je einen Puls auszugeben.

Leider bekomme ich den von jurs gebaute sketch nicht in meinen gebastelt.

In deinem Sketch dürfen natürlich kein delay oder andere blockierenden Wartezeiten oder -Schleifen enthalten sein.

Brauchst du überhaupt 2 Servos?

Ich hatte die Funktion von Jurs etwas modifiziert, um eine (theoretisch) beliebige Anzahl von Servos damit betreiben zu können.

const int wievieleServos = 1;  // Definiert die Anzahl von Servos die betrieben werden
int ServoPin[wievieleServos] = {8};  // Array für die Pins der Servos
int winkel0 = 90;

void setup(){
  Serial.begin(115200);
  for (int i = 0; i < wievieleServos; i++){
    pinMode(ServoPin[i], OUTPUT);
  }    
}

void loop(){
  while (Serial.available() >0){
    winkel0 = Serial.parseInt();
  }
  ServoRefresh(0,winkel0);
 77 ServoRefresh(1,winkel1);
}
  

/*************************************************************************************************
**  ServoRefresh()										**
**************************************************************************************************
** muß in der loop stehen (ohne DELAY!) muß mindestens alle 20ms aufgerufen werden!		**
** gibt >= alle 20ms einen Servopuls am Servopin aus						**
** based on Two-Servo-Test by 'jurs' for German Arduino Forum					**
**  												**
**  											   	**
**  Input:  Servonummer, Winkel (0-140°)							**
**  Output:											**
**  genutzte Globale Variablen: ServoPin[], wievieleServos 					**
**************************************************************************************************/
void ServoRefresh( int _Nr, int _angle)
{
  const int pulseabstand = 20;       // Definition der Kenngrößen
  const int Servo_minpulse = 800;    // der Servoansteuerung
  const int Servo_maxpulse = 2400;
  const int Servo_minW = 20;        // kleinster und größter Winkel
  const int Servo_maxW = 160;
  // berechnen der Geradengleichung des Servos 
  const float m = ((Servo_maxpulse - Servo_minpulse)*1.0/(Servo_maxW - Servo_minW)*1.0);
  const int b = Servo_minpulse - m * Servo_minW;
  
  static unsigned long lastRefresh[wievieleServos];
  if (millis()-lastRefresh[_Nr] < pulseabstand) return; // noch keine 20 ms vergangen
  lastRefresh[_Nr] = millis(); // Zeit des erfolgten Refresh merken
  
  _angle = constrain(_angle, 20, 160); // Stellwinkel von 20 - 160 Grad erlaubt
  int pulselen1 = _angle * m + b;
Serial.print("Winkel = ");Serial.print(_angle);Serial.print("   Puls: ");Serial.println(pulselen1);
  noInterrupts();
  digitalWrite(ServoPin[_Nr],HIGH); // Ersten Refresh-Impuls starten
  delayMicroseconds(pulselen1);
  digitalWrite(ServoPin[_Nr],LOW);
  interrupts(); 
}  // end ServoRefresh

Die Funktionen müssen im loop() stehen.
Es dürfen NIRGENDS delay() verwendet werden!

Okay das mit dem Delay ist schon der Fehler. Hab alles raus geholt und schon rennt alles.

Bin desweiteren gerade am versuchen die analogen Servos durch digital Servos durch digitale zu ersetzen. Leider funktionieren die bei gleichem Sketch nicht wirklich. Sie fahren zwar ganz langsam ruckelnd auf die Position das dauert aber richtig lange. Jemand eine Idee wodran das liegen könnte? Mit analogen alles kein Problem.

Schon mal Danke für eure Hilfe.

Meine digitalen Servos kennen 2 Geschwindigkeitsstufen, je nach dem wie weit ihr "Ziel" entfernt ist. Ist es unmittelbar in der Nähe zum Beispiel von 89 auf 90Grad dann sind sie langsamer unterwegs als wenn ich sie von 70 auf 90Grad fahren lassen. Da fahren sie erst schnell und die letzten 1-2 Grad werden sie langsamer. Vielleicht fahren deine gerade immer die Position an die in einem Umschaltpunkt der Geschwindigkeiten liegen, was sich in ruckeln bemerkbar macht. Hoffe ich hab mch verständlich ausgedrückt...

Fasse es nochmal kurz zusammen: Hohe Differenz zwischen ist und soll = hohe Geschwindigkeit niedrige Diff. zwischen ist und soll = niedrige Geschwindigkeit

zumindest habe ich ich es so bei meinen Hitec Servos bemerkt.