Das berühmte zucken der Servos

Hallo zusammen!

Bei der Inbetriebnahme des Arduino werden meine Servos irgendwie positionsmäßig initialisiert. Dies funktioniert auch wunderbar. Nachdem ich jedoch den ersten Servo stelle, fangen danach alle Servos ständig an zu zucken. Stellen lassen sie sich dennoch einwandfrei. Die Servos sind mit einer separaten Stromquelle (5V Netzteil) gespeist und die Masse geht parallel zum Arduino. Muss hier u.U. mit einem Elko gearbeitet werden oder habe ich einen anderen Fehler?

Würde mich sehr über jede Hilfestellung freuen!

Besten Dank und viele Grüße aus Berlin
René

Nachstehend auch noch der Code:

#include <VarSpeedServo.h>

//#include <Servo.h>
#include <Wire.h>
#include <Centipede.h>

Centipede CS;

int taster9 = 0;
int taster8 = 0;
int taster10 = 0;
int taster6 = 0;
int taster5 = 0;
int taster4 = 0;
int taster13 = 0;
int taster14 = 0;

VarSpeedServo weiche1;
VarSpeedServo weiche6;
VarSpeedServo weiche7;

void setup() {

weiche1.attach(12); //12
weiche6.attach(11); //11
weiche7.attach(10); //10

Wire.begin();
CS.initialize();

CS.pinMode(11, OUTPUT);
CS.pinMode(7, OUTPUT);
CS.pinMode(0, OUTPUT);
CS.pinMode(1, OUTPUT);
CS.pinMode(2, OUTPUT);
CS.pinMode(3, OUTPUT);
CS.pinMode(15, OUTPUT);
CS.pinMode(7, OUTPUT);

CS.pinMode(9, INPUT); CS.pinPullup(9, HIGH);
CS.pinMode(8, INPUT); CS.pinPullup(8, HIGH);
CS.pinMode(10, INPUT); CS.pinPullup(10, HIGH);
CS.pinMode(6, INPUT); CS.pinPullup(6, HIGH);
CS.pinMode(5, INPUT); CS.pinPullup(5, HIGH);
CS.pinMode(4, INPUT); CS.pinPullup(4, HIGH);
CS.pinMode(13, INPUT); CS.pinPullup(13, HIGH);
CS.pinMode(14, INPUT); CS.pinPullup(14, HIGH);

}

void loop() {

taster9 = CS.digitalRead(9);
taster8 = CS.digitalRead(8);
taster10 = CS.digitalRead(10);
taster6 = CS.digitalRead(6);
taster5 = CS.digitalRead(5);
taster4 = CS.digitalRead(4);
taster13 = CS.digitalRead(13);
taster14 = CS.digitalRead(14);

// Ab hier werden die Fahrstraßen mit Hilfe von IF-Bedingungen gestellt

// Fahrstraße HB - HBF1

if (taster9 == LOW && taster6 == LOW) {
delay(500);
weiche1.slowmove(55,40); // abzweig
delay(500);
weiche6.slowmove(60,40); // gerade
delay(500);
CS.digitalWrite(11, HIGH);
CS.digitalWrite(2, HIGH);
CS.digitalWrite(0, HIGH);
CS.digitalWrite(3, LOW);
CS.digitalWrite(15, LOW);
}

// Fahstraße HB - HBF2
if (taster9 == LOW && taster5 == LOW) {
delay(500);
weiche1.slowmove(100,40); // gerade
delay(500);
weiche7.slowmove (100, 40); // gerade 100 40
delay(500);
CS.digitalWrite(11, HIGH);
CS.digitalWrite(3, HIGH);
CS.digitalWrite(15, HIGH);
CS.digitalWrite(2, LOW);
CS.digitalWrite(0, LOW);
}

// Fahstraße HBF1 - HBF2 Umsezten
if (taster6 == LOW && taster14 == LOW) {
delay(500);
weiche6.slowmove(100,40); // gerade
delay(500);
weiche7.slowmove(60,40); // gerade 60
delay(500);
CS.digitalWrite(2, HIGH);
CS.digitalWrite(15, HIGH);
//CS.digitalWrite(2, LOW);
//CS.digitalWrite(0, LOW);
}

// Fahstraße HBF2 - HbfEinfahrt
if (taster14 == LOW && taster9 == LOW) {
delay(500);
weiche1.slowmove(100,40); // gerade
delay(500);
weiche7.slowmove(100,40); // gerade
delay(500);
CS.digitalWrite(11, HIGH);
CS.digitalWrite(15, HIGH);
CS.digitalWrite(3, HIGH);
//CS.digitalWrite(2, LOW);
//
}
}

Noch eine recht lustige Entdeckung... Wenn ich (siehe Sketch) Taster9 gedrückt halte, ist das Zucken zwar nicht weg aber deutlich geringer, allerdings auch nur bei Taster9. Wirklich seltsam. Die Taster hängen an einem MCP23017, die Servos sind direkt am Arduino.

Wieviel Strom liefert das NT? Elkos sind in den Servos teils schon eingebaut (die blauen vom Chinesen)

Output 5V, 3,5A... Lustig ist auch, wenn ich das Netzteil ziehe, dann dauert es noch ca. 5 Sekunden bis das Zucken weg ist. Eigentlich müsste doch sofort Ruhe sein...

Das Netzteil wird einen dicken Elko zur Glättung nutzen, somit soweit in Ordnung. Meist kann man das Phaenomen auch sehen, wenn man ein HandyNT mit Led Leuchte hat. Ist nur das Kabel eingesteckt, läuft die Led noch etwas nach. MIt Last direkt verschwunden. Aber aus deinem Zucken werde ich auch noch nicht schlau.

Zucken die Servos auch, wenn du nicht die "VarSpeedServo.h" sonder dir normale "Servo.h" nimmst?

guntherb:
Zucken die Servos auch, wenn du nicht die "VarSpeedServo.h" sonder dir normale "Servo.h" nimmst?

Leider ja.

Grüße
René

Hallo,

ist das PWM Signal stabil? Ist das an allen PWM Ausgängen der Fall?

ich hatte bei mir auch die Servo.h verwendet und hatte mit ständigem Servozucken zu kämpfen.

Da ich nur 2 Servos hatte, habe ich, nach vielen Versuchen die Servo.h rausgeworfen und die Pulse manuell über delayMicroseconds() erzeugt.
Ich vermutete damals eine gegenseitige Störung durch den I2C bus.

Was mir aber auffiel: die 20ms Pulsabstand sind nicht unbedingt nötig. Wenn man die pulse seltener sendet, dann wird das Servo nur langsamer.

Eine andere Alternative: da du ja die Servos zum Weichenstellen verwendest, könntest du auch nur beim Stellen eine gewisse Anzahl von Stellpulsen senden, und dann nichts mehr. Wo nichts gesendet wird, kann auch nichts zittern.

Ich denke, bei Anwendungen,bei denen die Servos ständig in Bewegung sind, fällt das zittern und zucken nicht so auf. Aber für eher statische Anwendungen ist die "von Hand" Lösung nicht so verkehrt.

Hallo Gunther,

deine Ausführungen klingen extrem spannend! Auch ich vermute eine gegenseitige Störung mit dem I2C Bus. Hatte vorher in Experimenten mal Servos einfach so an den Arduino angeschlossen (ohne Erweiterungen via I2C) und da hatte ich solche Probleme nicht.

Ich denke, bei Anwendungen,bei denen die Servos ständig in Bewegung sind, fällt das zittern und zucken nicht so auf. Aber für eher statische Anwendungen ist die "von Hand" Lösung nicht so verkehrt.

Genau daran hatte ich auch gedacht, zumal alle Servos (Weichen) später sowieso bei erster Inbetriebnahme initialisiert werden sollen. Leider scheitert es hier nur an meinem bescheidenen Fachwissen hinsichtlich der Umsetzung. Da werde ich mich noch ordentlich reinlesen müssen um die Servos manuell anzusteuern und vor allem ohne die Servo.h!

@Doc_Arduino: Das Phänomen tritt bei allen Ausgängen auf.

Grüße
René

Hallo,

ohne Servo.h könntest Du Dir eine Funktion schreiben die nichts weiter macht als einen Port ein und auszuschalten und dazwischen Wartezeit einzufügen. Diese rufst Du so lange/so oft auf wie es laut Servo Datenblatt dauert für einen vollne Bewegungsauschlag. Zur Vereinfachung kannste 2 Funktionen erstellen, je für links und rechts Anschlag. Dann mußte innerhalb der Funktion die Werte nicht anpassen. Vielleicht erstmal einfacher. Wenn in Kauf genommen werden kann das während dessen der Code nicht weiterläuft, kannste delay verwenden. Sonst mußte noch das delay durch millis() ersetzen.

Nimm doch sowas:

const int wievieleServos = 1;
int ServoPin[wievieleServos] = {8,9,10};

/*************************************************************************************************
**  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

Das funktioniert bei mir mit 2 Servos, sollte mit 3 auchgehen.
Aufruf in der loop:
ServoRefresh( 0, Winkel0); // Aufruf erstes Servo
ServoRefresh( 1, Winkel1); // Aufruf zweites Servo
ServoRefresh( 2, Winkel2); // Aufruf drittes Servo

edit: Code korrigert, war falsch.

Hallo!
Direkt ansprechen geht auch...Pin auf High und danach Delay (1) für links oder (2) für rechts. dann den Pin auf Low setzen.
Mit millis (EDIT:microseconds) gehts genauer...Servos nicht in ihre mechanische Endlage bringen (Blockierstrom), also rantasten.
..mit einem Delay nach Low kannst die Stellgeschwindigkeit beeinflussen, fur die Position nicht relevant ( wenn > als 2 ms).
Der 20/22 ms Frame wird nur bei der RC Funkstrecke (PPM) benutzt, um bis zu 8 (PWM) Servos anzusprechen.

mfg Martin

guntherb:
Nimm doch sowas:

  _angle constrain(_angle, 20, 160); // Stellwinkel von 20 - 160 Grad erlaubt

Kann es sein das dort bei _angle ein = fehlt ?
Also : _angle = constrain(_angle, 20, 160);

habe den Code gerade getestet und mein Servo mal volle Kanne gegen gefahren :frowning:
Irgend wie funktioniert das aber nicht bei mir.
Ich habe drei aufrufe :

ServoRefresh( 0, 40); 
delay (1000);
ServoRefresh( 0,60); 
delay (1000);
ServoRefresh( 0, 80); 
delay (3000);

Die drei Positionen werden aber nicht richtig angefahren, bzw unterschiedlich. Einmal bewegt sich das Servon nur zwei mal bis zur größeren Pause, oder es bewegt sich manchmal viel weiter. Würde das gerne für meinen Attiny85 benutzen, das die SorftwareServo Library irgend wie zu ungenaue Positionen ausgibt. Dabei rede ich von mind. +/- 5mm bei einer Hebellänge von 7cm.
Aber es sollte dann zuerst mal mit dem Arduion UNO laufen.

Kann es sein das dort bei _angle ein = fehlt ?
Also : _angle = constrain(_angle, 20, 160);

ja, das ist so, da ist wohl was beim kopieren passiert. :blush:

Irgend wie funktioniert das aber nicht bei mir.

Das tut mir leid, ich habe das codeschnipsel aus den Tiefen meiner Festplatte ausgegraben.
Ich denke schon, dass es funktioniert, kanns aber gerade nicht testen.
Ich melde mich nochmal.

Funktionieren tut es, aber nicht richtig, die Positionen ändern sich aber immer etwas.

Nun habe ich das hier gefunden:
http://popovic.info/html/arduino/arduinoUno_1.html#Servomotor_von_Hand_ansteuern

Dort gibt es diesen Code:

const int servoPin = 10;
void setup() {
  pinMode(servoPin, OUTPUT);
}
void loop() {
  for(int winkel=0; winkel<180; winkel++){
    servoAngle(winkel);  
  }  
  for(int winkel=180; winkel>0; winkel--){
    servoAngle(winkel);  
  }  
}
void servoAngle(int angle){
  digitalWrite(servoPin, HIGH);
  angle = map(angle,0,180,500,2500); // Winkel in Zeit umrechnen, siehe Bemerkung!
  delayMicroseconds(angle);
  digitalWrite(servoPin, LOW);
  delayMicroseconds(15000);
}

Ist eigentlich ähnlich, aber es wird einfach mit einem delay gearbeitet und so wie WasJetzt geschrieben hat.
Die Position bleibt nun gleich, auch auf dem Attiny85 !
Problem ist aber, das es vorkommt, das wieder Abstände manchmal (!) in einer anderen Geschwindigkeit angefahren werden !

Da ich aber nicht weiter diesen Beitrag zerpflücken will, werde ich mein Problem in meinem anderen Beitrag weiter beschreiben.
Würde mich freuen wenn mir da jemand weiterhelfen kann.

http://forum.arduino.cc/index.php?topic=213942.0

Der von mir in Reply11 gepostete code hatte tatsächlich einen Fehler. Aber nur der Berechnung des Winkels, als nichts grundsätzliches.

Nur der Aufruf mit delays funktioniert natürlich nicht!

Captain-DJ:
Ich habe drei aufrufe :

ServoRefresh( 0, 40); 

delay (1000);
ServoRefresh( 0,60);
delay (1000);
ServoRefresh( 0, 80);
delay (3000);

Die Funktion lebt davon, dass sie alle 20ms (spätestens) drankommt. Dann funktioniert sie.
Im Anhang eine Datei zum testen. (Servo_refresh.ino)

Und für den Fall, dass das Servo eher statisch betrieben wird, und im Ruhezustand keine Kräfte wirken, noch eine Version, in der nach einer Zeit das Servo nicht mehr angesteuert wird. (Servo_refresh_autoOff.ino)

@Captain-DJ:
Der von dir gepostete Code ist wegen des enthaltenen "delayMicroseconds(15000);" etwas unglücklich.
damit wartet der Arduino nur bis zum nächsten Puls und kann sonst nichts tun. Das ist wie ein delay in der loop: vollständige Blockade!

Servo_refresh.ino (2.12 KB)

Servo_refresh_autoOff.ino (2.52 KB)

@guntherb

Ja habe gemerkt, das diese Stelle mit dem delay so nicht funktionieren kann.
Die Funktion muss immer wieder aufgerufen werden.

Mir ist klar, das dieser Code den Arduino blockiert.
Das macht aber nichts, da ich eine Bewegung des Servo immer erst abarbeiten lasse, bevor ich was neues mache.
Siehe meinen anderen Beitrag, siehe Link Post #15

Wenn ich das Beispiel mit dem Interrupt auf dem Attiny laufen lasse, verschieben sich die Stellungen des Servo.
Bei jedem durschlauf des Programms, ändern sich diese um einige Schritte, was dazu führt, das nichts mehr passt.
Das Problem habe ich mit dem anderen Code nicht.
Nur habe ich eine Stelle, die eben beim ersten abarbeiten richtig, danach meistens zu schnell abgearbeitet wird.
Diese Bewegung des Servo ist genau so in eine Schleife gepackt wie die anderen auch.
Ich vermute, das der Weg zu kurz ist (15 Grad/Schritte oder was die Zahl besagt). Das Problem besteht aber nur wenn ich den Code auf dem Attiny85 laufen lasse.
Nicht das der doch etwas zu "schwach" für solche Sachen ist ?
Habe noch einen zweiten hier, werde es damit mal testen. Das der einen defekt hat, glaube ich zwar nicht, aber ich versuche es mal.

Werde deine zwei Beispiele gleich mal durchtesten. Bedanke mich schon mal dafür !