Gibt es eine Möglichkeit, Servos vor dem „Wackeln“ zu bewahren?

20

Ganz einfach, ich steuere Servos (9g Micro Servos) basierend auf Daten, die von woanders eingelesen wurden. Alles funktioniert gut, außer dass die Servos ständig "rütteln". Das heißt, sie vibrieren mit sehr subtilen Bewegungen zurück (mit intermittierenden Bewegungen von 1/2 -> 1 cm oder so).

Ich habe versucht, dieses Problem in der Software zu beheben, indem ich Folgendes ausgeführt habe:

  do{
    delay(DTIME);
    positionServo();
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("X position: ");
    lcd.print(xRead);
    lcd.setCursor(0,1);
    lcd.print("Y position: ");
    lcd.print(yRead);
  }while( readChange() ); //while there has been change

Wo dies erforderlich ist, initialisieren Sie die Variablen, in denen der zugeordnete Servowert gespeichert ist (mithilfe der Arduino-Servobibliothek).

Die Funktion readChange () ist wie folgt definiert:

int readChange(){
  int x_Temp, y_Temp;

  x_Temp = map(analogRead(x_axisReadPin), 0, 1023, 0, 179);
  y_Temp = map(analogRead(y_axisReadPin), 0, 1023, 0, 179);

  if( abs(x_Temp - xRead) < DEG && abs(y_Temp - yRead) < DEG ) return 0; // no change 
  else return 1; //change
}

Dabei ist xRead der Wert, der initialisiert wurde (der erste zugeordnete Servoausgang).

Dies ist jedoch kein guter Ansatz. Es setzt voraus, dass sich BEIDE Werte nicht um einen Faktor von DEG geändert haben (~ 10 Grad, in meinem Fall ~ 0,28 V). Wenn ich die Funktion so schreibe, dass entweder ODER kleiner als DEG ist, was dann, wenn ich jeweils nur einen Servo wechsle? Also gibt es eine delimma ..

Ist das einfach eine Eigenschaft von Servos (vielleicht billige?) Oder gibt es eine Problemumgehung?


Es wäre viel einfacher, einen Pastie-Link einzufügen. Hier ist der vollständige Code: http://pastie.org/8191459

Ich habe zwei Servos zusammen mit einem Laserpointer angebracht, um zwei Freiheitsgrade (X, Y) zu ermöglichen. Abhängig vom Zustand mehrerer Tasten gibt es Optionen, um die Servos auf verschiedene Arten zu steuern. Der erste ist "Motion", bei dem ich zwei Fotowiderstände habe, die auf der Grundlage der Belichtungsmenge die Position der Servos beeinflussen. Ich habe den Code zur Steuerung der Servos durch einen Xbox-Controller noch nicht implementiert. Und die dritte Option ist nur eine zufällige Bewegung.

Bildbeschreibung hier eingeben

sherrellbc
quelle
4
Sie haben anscheinend ein wenig Instabilität oder Rauschen in Ihrem Servocontroller. Sie gehen jedoch auf viele Details ein, die nichts mit dem Servocontroller zu tun zu haben scheinen, abgesehen von der undokumentierten Zeile "positionServo ();", die wir nur schätzen können, wo die Details vergraben sind. Ist der Servoregler im Mikro geschlossen? Äußerlich geschlossen? Analog oder digital? Bei welcher Auflösung wird digital gemessen? Zeigen Sie ein Diagramm des gesamten Systems.
Olin Lathrop
Wie viel Last setzen Sie auf die Servos?
Chris Laplante
4
@OlinLathrop - (S) Er verwendet standardmäßige funkgesteuerte Modellservos, bei denen die gesamte Servoschleife in das Gerät eingebrannt ist. sherrellbc - "Servo" ist ein sehr, sehr allgemeiner Begriff. Leider wählten die Hersteller von RC-Modellkomponenten den am wenigsten aussagekräftigen Begriff für die Geräte, die sie herstellen. Da wir uns hier mit den unterschiedlichsten Servos und Servosystemen befassen, ist es wahrscheinlich eine gute Idee, anzugeben, dass es sich bei Ihren "Servos" um funkgesteuerte Modellservos handelt.
Connor Wolf
1
Ihr System ist zu komplex, als dass wir es für Sie beheben könnten. Vereinfachen Sie es und prüfen Sie, ob das Problem weiterhin besteht. Wenn Sie ein minimales System haben, das das Problem reproduziert, und Sie es trotzdem nicht selbst beheben können, ist es angebracht, um Hilfe zu bitten.
Phil Frost
12
Allgemeiner Hinweis zum Entwerfen von Laserrichtsystemen: Spiegel auf die Servos legen und dann aufeinander richten. Auf diese Weise muss weder ein Servo am anderen noch der Laser am Servo montiert sein, und Sie können alle Servos festschrauben.
PJC50

Antworten:

27

Wenn Sie die Servo-Bibliothek auf einem Arduino verwenden, ist eine häufige Quelle für Servo-Buzz, dass die Interrupt-gesteuerten Servoroutinen tatsächlich keinen sehr stabilen Ausgangsimpuls liefern. Da der AVR Interrupts zum Warten der Millis () - Uhr und anderer Dinge in der Arduino-Laufzeit benötigt, liegt der Jitter in der Servobibliothek in der Größenordnung von mehreren Mikrosekunden, was zu einer großen Bewegung im Servo führt.

Die Lösung dafür ist, Ihren eigenen Puls zu schreiben. Etwas wie das:

cli();
long start = micros();
digitalWrite(PIN, HIGH);
while (micros() - start < duration)
  ;
digitalWrite(PIN, LOW);
sei();

Dies schaltet andere Interrupts aus und erzeugt einen viel saubereren PWM-Impuls. Allerdings wird der "millis () - Timer einige Uhrentakte verfehlen. (Die" micros () "- Funktion kann als etwas anderes bezeichnet werden - ich vergesse genau was.)

Im Allgemeinen möchten Sie für das Timing von kritischem Code die Arduino-Laufzeit vollständig entfernen und Ihre eigene mit dem avr-gcc-Compiler und der avr-libc-Bibliothek schreiben, die die Arduino-Umgebung mit Strom versorgen. Dann können Sie einen Timer einrichten, der 4-mal pro Mikrosekunde oder sogar 16-mal pro Mikrosekunde tickt und eine viel bessere Auflösung in Ihrem PWM erzielt.

Eine andere Ursache für Brummen bei Servos sind billige Servos mit billigen Sensoren, bei denen die Sensoren verrauscht sind oder bei denen die genaue Position, die mit dem Impuls angefordert wurde, vom Sensor nicht wirklich codiert werden kann. Das Servo sieht "Move to Position 1822" und versucht es zu tun, erhält jedoch den Sensorwert 1823. Das Servo sagt dann "Move back a little bit" und erhält den Sensorwert 1821. Wiederholen! Der Fix dafür ist die Verwendung von hochwertigen Servos. Idealerweise gar keine Hobby-Servos, sondern echte Servos mit optischen oder magnetischen Absolutwertgebern.

Wenn die Servos nicht genügend Leistung erhalten oder wenn Sie versuchen, ihre Leistung über die 5-V-Schiene des Arduino zu beziehen, wird ein spannungsabhängiges Summen in den Servos erzeugt, wie oben vorgeschlagen. Möglicherweise können Sie das Problem mit großen Elektrolytkondensatoren beheben (was ohnehin eine gute Idee für die allgemeine Filterung ist), aber Sie möchten mit größerer Wahrscheinlichkeit sicherstellen, dass Ihre Servostromquelle tatsächlich mehrere Ampere Strom bei Servospannung liefert.

Jon Watte
quelle
1
R / C-Servosteuerungssignale sind PWM. Die Impulsbreite beträgt nominell 1-2 Millisekunden, das Impulswiederholungsintervall liegt zwischen 20 und 50 Millisekunden. Ich würde erwarten, dass mehr als etwa 10 Mikrosekunden Variation der Impulsbreite dazu führen, dass der Servo zittert. Jitter im PRI ist im Allgemeinen kein Problem, wenn die Impulsbreite stabil ist. (Mein schmutziger 555-Controller variierte Pulsbreite und PRI um den gleichen Betrag: das war dem Servo egal.)
John R. Strohm
Alles, was Sie sagen, ist wahr, mit Ausnahme der Jitter-Servos, die jittern, bevor die Impulsbreite um 10 us "aus" ist. Und der Interrupt-Jitter für das einfache Arduino (bevor Sie Bibliotheken hinzufügen) kann bis zu 10 us betragen! Der Code, den ich eingefügt habe, soll einen rockstabilen Impuls in der Arduino-Umgebung erzeugen, der bei rockstabilen Servopulsen im Allgemeinen nicht so gut ist wie ein dedizierter 555-Schaltkreis.
Jon Watte
4
Ich habe gerade einen Artikel geschrieben, der zeigt, wie man auf Arduino wie im obigen Code präzise Impulse erzeugt , außer dass die Timer-Hardware verwendet wird - und keine Notwendigkeit besteht, Interrupts auszuschalten und die Arduino-Laufzeit durcheinander zu bringen.
Bigjosh
Beachten Sie, dass der Arduino die Timer-Ausgabe nur an wenigen Pins (den PWM-Pins) unterstützt und Sie die Timer0-Pins für diese Methode nicht verwenden können. Somit gibt es nur 4 Pins, für die dies bei einem normalen Arduino UNO wirklich funktioniert. Wenn Sie 4 oder weniger Servos fahren müssen und die Timer nicht für etwas anderes benötigen, ist dies eine gute Option.
Jon Watte
21

Dies wird als "Buzz" bezeichnet.

Es gibt ein paar Dinge, die es verursachen werden. Instabilität der Stromversorgung des Servos ist eine häufige Ursache. R / C-Servos können einige GROSSE Spitzen zeichnen, wenn sie den Motor zum ersten Mal in Bewegung setzen.

Vor vielen Jahren habe ich mit einem Tower Hobbies Royal Titan Standard-Servo gespielt und es von einem 555 und einem Ein-Transistor-Wechselrichter aus gesteuert. Einfacher Steuerkreis. Ich habe erfahren, dass der Servomotor bei kontinuierlicher Bewegung 250 mA von der 5-V-Versorgung abzog. Es summte und zog leicht Spitzen von einem halben Ampere an. (Vielleicht mehr: Ich habe nur den Strommesser auf meiner Bank überwacht und keinen Shunt mit Strommessung durchgeführt.)

Es dauerte 220 uF direkt über mein Servo, um es zu zähmen.

Versuchen Sie, einen Elektrolytkondensator von mindestens 100 uF direkt über die Stromversorgung des Servos zu legen, so nah wie möglich am Servo, und prüfen Sie, ob dies hilft.

Aufgrund dieser Experimente würde ich niemals in Betracht ziehen, R / C-Servos für ALLES zu verwenden, ohne Kondensatoren hinzuzufügen. Dazu gehören auch funkgesteuerte Modelle.

Dies kann auch durch Schmutz im Servotopf im Servo verursacht werden. Probieren Sie zuerst den Kondensator aus.

John R. Strohm
quelle
6

Brummen / zittern Sie nur, wenn Sie sich den Servolimits nähern (0 Grad oder 180 Grad)? Wenn ja, gibt es möglicherweise eine einfache Lösung für Sie. Ich habe festgestellt, dass billige Servos nicht genau wissen, wie sie an den Grenzen ihrer Bewegung bleiben sollen, was zu dem von Ihnen erwähnten Summen / Zittern führen kann. Wenn Sie den Bereich jedoch auf 10 bis 170 Grad beschränken, wird das Problem behoben.

Wenn das für Sie nicht gut genug ist, können Sie den komplexeren Korrekturen folgen, die in den anderen Antworten erwähnt wurden, wie z. B. bessere Leistung, bessere Servosensoren usw.

Schwachkopf
quelle
Ja, für mein SG90 liegen diese Werte zwischen 18 und 162. Es hat 32 Grad tatsächlich nicht unerreichbar gemacht, vielleicht nur die Hälfte davon.
Maxim Kachurovskiy
5

Ich habe mein Problem behoben, indem ich das Servo ausgeschaltet habe, nachdem ich es bewegt habe. Beispiel:

pinMode(PIN, OUTPUT);
myservo.write(degree);
//give servo time to move
delay(5000);
pinMode(PIN, INPUT);

PINIst der PWM-Pin mit Ihrem Servo verbunden? Durch Umschalten in den Eingabemodus konnte ich die Vibration abschalten. Dies ist keine optimale Lösung und ich würde empfehlen, zuerst die anderen Lösungen auszuprobieren.

Ramast
quelle
Ich habe die anderen Lösungen ausprobiert, dies war die einzige, die funktioniert hat, +1. Tolle Idee, wenn alles andere versagt!
Snappawapa
3

Ich hatte das gleiche Problem mit MG90S-Servos (Jittering), meine Signalleitungen sind relativ lang (60 ~ 70 cm), und ein 103 (10 nF) Kondensator über den Signal- und Masseleitungen hat das Problem für mich behoben (ich habe den Kondensator irgendwo in der in der Mitte an dem Punkt, an dem das ursprüngliche Servokabel mit meinem internen Kabel verbunden ist).

Außerdem konnte ich die Standard-Servobibliothek nicht verwenden, da der erste Timer, den er auf dem Arduino Mega erfasst, Timer-5 ist und ich diesen für die Frequenzmessung benötige. Da ich nur 10 Servos verwende, habe ich den Schlüsselcode aus der Servobibliothek extrahiert und in Timer-1 geändert (jeder Timer unterstützt maximal 12 Servos auf dem Mega).

Der eigenständige Code ist unten als Referenz aufgeführt. Wenn Sie ihn in Ihr eigenes Projekt aufnehmen möchten, können Sie nur den oberen Teil verwenden. Der untere Teil dient zum Testen des oberen Teils. (Er überwacht die serielle Schnittstelle. Sie können sX zuweisen.) und vX-Befehle, bei denen sX ein Servo auswählt, s0 das erste Servo auswählt, vX die Servoposition in uns festlegt, so dass v1500 servo0 auf die mittlere Position setzt, vorausgesetzt, Sie haben zuerst einen s0-Befehl gegeben).

//----------------------------------------------------------------
// This is the actual servo code extracted from the servo library
//----------------------------------------------------------------

#include <avr/pgmspace.h>

//----converts microseconds to tick (assumes prescale of 8)
#define usToTicks(_us)    (( clockCyclesPerMicrosecond()* _us) / 8)

#define MIN_PULSE_WIDTH     544     // the shortest pulse sent to a servo  
#define MAX_PULSE_WIDTH     2400    // the longest pulse sent to a servo 
#define DEFAULT_PULSE_WIDTH 1500    // default pulse width when servo is attached
#define REFRESH_INTERVAL    20000   // minumim time to refresh servos in microseconds

#define TRIM_DURATION       2       // compensation ticks to trim adjust for digitalWrite delays // 12 August 2009

struct s_servar {
    //----counter for the servo being pulsed for each timer (or -1 if refresh interval)
    int8_t  channel;
};
static volatile struct s_servar gl_vars;

//----maximum number of servos controlled by one timer 
#define SERVOS_PER_TIMER    12
//----this can not be higher than SERVOS_PER_TIMER
#define SERVO_AMOUNT        6

struct s_servo {
    volatile unsigned int   ticks;
    unsigned char           pin;
};
struct s_servo  gl_servos[SERVO_AMOUNT] = {
    { usToTicks(DEFAULT_PULSE_WIDTH), 22 },
    { usToTicks(DEFAULT_PULSE_WIDTH), 23 },
    { usToTicks(DEFAULT_PULSE_WIDTH), 24 },
    { usToTicks(DEFAULT_PULSE_WIDTH), 25 },
    { usToTicks(DEFAULT_PULSE_WIDTH), 26 },
    { usToTicks(DEFAULT_PULSE_WIDTH), 27 },
};

ISR(TIMER1_COMPA_vect) {
    unsigned char       servooff;
    if(gl_vars.channel < 0 ) {
        //----channel set to -1 indicated that refresh interval completed so reset the timer
        TCNT1 = 0;
    }
    else{
        servooff = gl_vars.channel;
        if(servooff < SERVO_AMOUNT) {
            //----end the pulse
            digitalWrite(gl_servos[servooff].pin, LOW);
        }
    }
    //----increment to the next channel
    gl_vars.channel++;
    servooff = gl_vars.channel;
    if(servooff < SERVO_AMOUNT) {
        //----set timer interrupt for pulse length
        OCR1A = TCNT1 + gl_servos[servooff].ticks;
        //----start the pulse
        digitalWrite(gl_servos[servooff].pin, HIGH);
    }
    else {
        // finished all channels so wait for the refresh period to expire before starting over
        //----allow a few ticks to ensure the next OCR1A not missed
        if(((unsigned)TCNT1) + 4 < usToTicks(REFRESH_INTERVAL)) {
            OCR1A = (unsigned int)usToTicks(REFRESH_INTERVAL);
        }
        else {
            //----at least REFRESH_INTERVAL has elapsed
            OCR1A = TCNT1 + 4; 
        }
        //----this will get incremented at the end of the refresh period to start again at the first channel
        gl_vars.channel = -1;
    }
}

void InitServoISR() {
    unsigned char   ct;
    gl_vars.channel = -1;
    //----init timer 1
    TCCR1A = 0;             // normal counting mode
    TCCR1B = _BV(CS11);     // set prescaler of 8
    TCNT1 = 0;              // clear the timer count
    TIFR1 |= _BV(OCF1A);    // clear any pending interrupts;
    TIMSK1 |= _BV(OCIE1A);  // enable the output compare interrupt
    //----set all servo pins to output
    for(ct = 0; ct < SERVO_AMOUNT; ct++) {
        pinMode(gl_servos[ct].pin, OUTPUT); 
    }
}

void SetServoMicroSecs(unsigned char servooff, unsigned short value) {
    uint8_t oldSREG;
    if(servooff < SERVO_AMOUNT) {
        //----ensure pulse width is in range
        if(value < MIN_PULSE_WIDTH) { value = MIN_PULSE_WIDTH; }
        else {
            if(value > MAX_PULSE_WIDTH) { value = MAX_PULSE_WIDTH; }
        }
        value -= TRIM_DURATION;
        value = usToTicks(value);
        oldSREG = SREG;
        cli();
        gl_servos[servooff].ticks = value;
        SREG = oldSREG;
    }
}

//------------------------------------------------
// This is code to test the above servo functions
//------------------------------------------------

#define ERR_OK          0
#define ERR_UNKNOWN     1
#define ERR_OUTOFRANGE  2

#define SERDEBUG_CODE
#define MAX_SER_BUF     12

void setup() { 
    InitServoISR();

    #ifdef SERDEBUG_CODE
    Serial.begin(9600);
    Serial.println(F("Start"));
    #endif
}


void loop() {
    #ifdef SERDEBUG_CODE
    uint8_t         ct, chr;
    char            buf[MAX_SER_BUF];
    ct = 0;
    #endif   
    //----main while loop
    while(1) {
        #ifdef SERDEBUG_CODE
        //--------------------
        // Serial Port
        //--------------------
        while (Serial.available() > 0) {
            chr = Serial.read();
            if(chr == '\n') {
                ProcSerCmd(buf, ct);
                ct = 0;
            }
            else {
                //----if for some reason we exceed buffer size we wrap around
                if(ct >= MAX_SER_BUF) { ct = 0; } 
                buf[ct] = chr;
                ct++;
            }
        }
        #endif
    }
}

//------------------------------
// Serial Port Code
//------------------------------

#ifdef SERDEBUG_CODE
uint16_t RetrieveNumber(char *buf, uint8_t size) {
    //--------------------------------------------------------------
    // This function tries to convert a string into a 16 bit number
    // Mainly for test so no strict checking
    //--------------------------------------------------------------
    int8_t  ct;
    uint16_t    out, mult, chr;
    out = 0;
    mult = 1;
    for(ct = size - 1; ct >= 0; ct--) {
        chr = buf[ct];
        if(chr < '0' || chr > '9') { continue; }
        chr -= '0';
        chr *= mult;
        out += chr;
        mult *= 10;
    }
    return(out);
}

void ProcSerCmd(char *buf, uint8_t size) {
    //-----------------------------------------------------------
    // supported test commands
    // sX   X = 0 to SERVO_AMOUNT       Sets the servo for test
    // vX   X = MIN to MAX PULSE WIDTH  Sets the test servo to value X
    //-----------------------------------------------------------
    static unsigned char    lgl_servooff = 0;
    uint8_t                 chr, errcode;
    uint16_t                value;
    errcode = 0;
    while(1) {
        chr = buf[0];
        //----test commands (used during development)
        if(chr == 's') {
            value = RetrieveNumber(buf + 1, size - 1);
            if(value < 0 || value >= SERVO_AMOUNT) { errcode = ERR_OUTOFRANGE; break; }
            lgl_servooff = (unsigned char)value;
            break;
        }
        if(chr == 'v') {
            value = RetrieveNumber(buf + 1, size - 1);
            if(value < MIN_PULSE_WIDTH || value > MAX_PULSE_WIDTH) { errcode = ERR_OUTOFRANGE; break; }
            SetServoMicroSecs(lgl_servooff, value);
            break;
        }
        errcode = ERR_UNKNOWN;
        break;
    }
    if(errcode == 0) {
        Serial.println(F("OK"));
    }
    else {
        Serial.write('E');    
        Serial.println(errcode);
    }
}
#endif
walter k
quelle
2

In diesem Fall bestand meine beste Option darin, die Servos bei jedem Vorgang anzubringen und abzunehmen.

servo1.attach(pinServo1);
for (pos = 0; pos <= servoMax; pos += 1) {
    servo1.write(pos);
    delay(10);
}
servo1.detach(pinServo1);

PS. Das ist wirklich überhaupt keine Qualität, nur eine Problemumgehung.

Shikartoos
quelle
1

Während andere verschiedene Lösungen für dieses Problem mit dem Servosummen vorgeschlagen haben, finden Sie in diesem Thread und in anderen Arduino-Foren folgende Vorschläge:

  • Eigenen Puls erzeugen
  • Versorgen Sie das Gerät separat mit 5V
  • Vermeiden Sie es, an seine Grenzen zu gehen (z. B. 10-170 anstelle von 0-180).
  • Führen Sie einen Kondensator über
  • Nach dem Umzug abnehmen

In meinem Fall stellte ich fest, dass das Summen aufhörte, wenn ein 9V / 2A-Netzteil an die Arduino-Platine angeschlossen wurde. Die einfachste Lösung bestand darin, das Servo langsam zu bewegen:

for (pos = servo.read(); pos < 180; pos += 2) {
  servo.write(pos);
  delay(40);
}

YMMV.

user2105117
quelle
1
#include <Servo.h>             //Servo library
Servo servo_test;        //initialize a servo object for the connected servo  

int angle = 0;
int sw1 = 7;   // pushbutton connected to digital pin 7
int val=0;

void setup()
{
   servo_test.attach(2);     // attach the signal pin of servo to pin2 of arduino
   pinMode(sw1, INPUT_PULLUP);
}

void loop()
{
    val = digitalRead(sw1);
    if (val == LOW)
    {  
        servo_test.attach(2);     // attach the signal pin of servo to pin2 of arduino
        for(angle = 0; angle < 90; angle += 1)   // command to move from 0 degrees to 90 degrees 
        {                                  
            servo_test.write(angle);                 //command to rotate the servo to the specified angle
            delay(5);                     
        } 
    }
    else
    {
        servo_test.detach();// After servo motor stops we need detach commmand to stop vibration
    }
}
Musthafa Kadersha
quelle
0

Für mich sieht das nach Fehlern oder einer falschen Abstimmung der Rückkopplungsschleife aus. High-End-Servosteuerungssysteme kennen die Motoreigenschaften (Induktivität, Drehmoment, Spitzenstrom, Polzahl), die Last (Trägheitsmoment) und die augenblicklichen Bedingungen (Position, Drehzahl, Gegen-EMK, Strom). Mit diesen Informationen kann das Motorsteuerungsprogramm Vorhersagen darüber treffen, was der Servo als Reaktion auf eine bestimmte Eingabe von der Steuerung (dh Strom- / Spannungseingabe) tun wird, und auf dieser Basis die optimale Eingabe erzeugen, um die gewünschte Ausgabe zu erzielen.

Wie Sie sich vorstellen können, ist dies etwas kompliziert, aber eine Internetsuche nach Servofeedback wird Ihnen den Einstieg erleichtern.

EBlake
quelle