22 votos

Es allí una manera de dejar de servos de "temblor"?

Muy simplemente, yo soy el control de servos(9g Micro Servos) se basa en algunos datos que se leen de fuera. Todo funciona bien, excepto que los servos constantemente "batido". Es decir, hacen vibrar de nuevo con sutiles movimientos (con movimientos oscilantes de 1/2 -> 1cm o así).

He intentado corregir este problema en el software por hacer algo como:

  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

Donde el do-while es necesario inicializar las variables que almacenan el mapeado servo valor(usando el arduino servo de la biblioteca).

El readChange() la función se define como:

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
}

Donde xRead es el valor que se ha inicializado (la primera, que se asignan servo de salida).

Aunque, esto realmente no es un buen enfoque. Se requiere que TANTO los valores no han cambiado por un factor de DEG (~10 grados, o ~0.28 V en mi caso). Si escribo la función de que sea O ser menos de DEG, entonces ¿qué pasa si yo era sólo el cambio de un servo a la vez? Así que hay una delimma ..

Es esto simplemente una propiedad de servos (quizás barato?) o hay alguna solución?


Sería mucho más sencillo para incluir un pastie enlace. Aquí está el código completo: http://pastie.org/8191459

He adjuntado dos servos junto con un puntero láser para permitir dos grados de libertad (X, Y). Hay opciones, basada en el estado de varios botones, para el control de los servos de diversas maneras. La primera es el "Movimiento", donde tengo dos fotoresistores que, basado en la cantidad de exposición a la luz, afecta a la posición de los servos. Aún no he implementado el código de control de los servos por un controlador de Xbox. Y la tercera opción es simplemente aleatorio movimiento.

enter image description here

4 votos

Y

0 votos

H

0 votos

@

29voto

Van Gale Puntos 387

Cuando se utiliza la librería Servo en un Arduino, una fuente común de zumbidos de servo es que las rutinas de servo controladas por interrupción en realidad no dan un pulso de salida muy estable. Debido a que el AVR toma interrupciones para dar servicio al reloj millis() y otras cosas en el tiempo de ejecución de Arduino, el jitter en la librería Servo es del orden de varios microsegundos, lo que se traduce en mucho movimiento en el servo.

La solución es escribir tu propio pulso. Algo como esto:

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

Esto apagará otras interrupciones, y generará un pulso PWM mucho más limpio. Sin embargo, hará que el temporizador "millis() pierda algunos ticks de reloj. (La función "micros()" puede llamarse de otra manera -- no recuerdo exactamente qué).

En general, para el código crítico de temporización, usted quiere deshacerse del tiempo de ejecución de Arduino por completo, y escribir su propia utilizando el compilador avr-gcc y la biblioteca avr-libc que alimenta el entorno Arduino. A continuación, puede configurar un temporizador para que marque 4 veces por microsegundo, o incluso 16 veces por microsegundo, y obtener una resolución mucho mejor en su PWM.

Otra causa de zumbido en los servos son los servos baratos con sensores baratos, donde los sensores son ruidosos, o cuando la posición exacta solicitada con el pulso no puede ser codificada realmente por el sensor. El servo verá "muévete a la posición 1822" y tratará de hacerlo, pero termina con la lectura del sensor 1823. El servo entonces dirá "retrocede un poco" y termina con la lectura del sensor 1821. Repetir. La solución para esto es utilizar servos de alta calidad. Idealmente, no servos de hobby, sino servos reales con encoders absolutos ópticos o magnéticos.

Por último, si los servos no reciben suficiente energía, o si tratas de conducir su energía desde el carril de 5V en el Arduino, esto generará zumbido inducido por caída de tensión en los servos, como se sugirió anteriormente. Usted puede ser capaz de solucionarlo con grandes condensadores electrolíticos (que son una buena idea para el filtrado general de todos modos), pero es más probable que desee asegurarse de que su fuente de alimentación del servo en realidad puede entregar varios amperios de corriente a la tensión del servo.

1 votos

Las señales de control del servo R/C son PWM. El ancho de pulso es nominalmente 1-2 milisegundos, el intervalo de repetición de pulso es de 20 a 50 milisegundos. Yo esperaría más de 10 microsegundos de variación en el ancho de pulso para causar que el servo se ponga nervioso. Jitter en el PRI generalmente no será un problema si el ancho de pulso es estable. (Mi controlador 555 variaba el ancho de pulso y el PRI en la misma cantidad: al servo no le importaba).

0 votos

Todo lo que dices es cierto, excepto el jitter -- los servos jitterarán antes de que el ancho de pulso esté "apagado" por 10 us. ¡Y la fluctuación de interrupción para el Arduino simple (antes de añadir bibliotecas) puede llegar hasta 10 us! El código que pegué está pensado para generar un pulso estable como una roca en el entorno Arduino, que generalmente no es tan bueno en pulsos de servo estables como un circuito 555 dedicado.

7 votos

Acabo de escribir un artículo que muestra cómo generar pulsos precisos en Arduino como el código anterior, excepto que utiliza el hardware del temporizador -y no hay necesidad de desactivar las interrupciones y estropear el tiempo de ejecución de Arduino.

23voto

John R. Strohm Puntos 1559

Esto se llama "zumbido".

Hay un par de cosas que pueden causarlo. La inestabilidad en la alimentación del servo es una causa común. Los servos R/C pueden tener grandes picos cuando ponen el motor en movimiento por primera vez.

Hace muchos años, jugué con un servo Tower Hobbies Royal Titan Standard, controlándolo desde un 555 y un inversor de un transistor. Un circuito de control muy simple. Aprendí que el servo motor consumía 250 mA de la fuente de 5V mientras estaba en movimiento continuo. Zumbando, consumía fácilmente picos de medio amperio. (Tal vez más: Yo sólo estaba monitoreando el medidor de corriente en mi suministro de banco, no el alcance de una derivación de detección de corriente).

Hicieron falta 220 uF directamente a través de mi servo para domarlo.

Intenta poner un condensador electrolítico, de al menos 100 uF, directamente a través de la fuente de alimentación del servo, tan cerca eléctricamente del servo como puedas, y mira si eso ayuda.

Basándome en esos experimentos, nunca consideraría usar servos R/C para NADA sin añadir condensadores. Eso incluye modelos radio-controlados.

Esto también puede ser causado por la suciedad en el servo pot dentro del servo. Prueba primero con el condensador.

6voto

Jeroen Landheer Puntos 3346

¿Sus zumbidos/temblores se producen sólo cuando está en los límites del servo o cerca de ellos (0 grados o 180 grados)? Si es así, puede haber una solución simple para usted. He encontrado que los servos baratos no saben permanecer en los límites de su movimiento muy bien, lo que puede causar el zumbido / temblor que usted está mencionando. Sin embargo, si limitas su rango a 10~170 grados, el problema se solucionará.

Si eso no es suficiente para ti, puedes seguir los arreglos más complejos mencionados en las otras respuestas, como mejor potencia, mejores servosensores, etc.

0 votos

Sí, para mi SG90 estos valores son de 18 a 162. En realidad no hizo 32 grados inalcanzable, tal vez sólo la mitad.

6voto

Alistair Bell Puntos 345

He solucionado mi problema "apagando el servo" después de moverlo. Ejemplo:

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

PIN es el pin PWM conectado a tu servo. cambiandolo a modo Entrada pude apagar la vibración. Esta no es la solución óptima y yo sugeriría probar las otras soluciones en primer lugar.

3voto

C.Slade Puntos 8

Tuve el mismo problema con los servos MG90S (jittering), mis líneas de señal son relativamente largas (60~70cm), colocar un condensador de 103 (10nF) sobre las líneas de señal y tierra me solucionó el problema (coloqué el condensador en algún lugar en el medio, en el punto donde el cable original del servo se conecta a mi cable interno).

Además no podía usar la librería estándar de Servo porque el primer temporizador que coge en el Arduino Mega es Timer-5 y lo necesito para medir la frecuencia. Como sólo uso 10 servos extraje el código clave de la librería Servo y lo cambié para usar Timer-1 (cada temporizador soporta un máximo de 12 servos en el Mega).

El codigo stand-alone esta abajo como referencia, si quieres incluirlo en tu propio proyecto entonces puedes usar la parte de arriba solamente, la parte de abajo es para probar la parte de arriba (escucha en el puerto serie, puedes dar comandos sX y vX, donde sX selecciona un servo, s0 seleccionaria el primer servo, vX fija la posicion del servo en nosotros, asi que v1500 pondria el servo0 en la posicion media, asumiendo que diste un comando s0 primero).

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

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X