Arduino – way to stop servos from “shaking”

arduinoservo

Very simply, I am controlling servos (9g micro servos) based on some data read in from elsewhere. Everything works fine except that the servos will constantly "shake." That is, they vibrate back with very subtle movements (with intermittent movements of 1/2 -> 1cm or so).

I tried correcting this issue in software by doing something like:

  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

Where the do-while is necessary initialize the variables that store the mapped servo value (using the arduino servo library.)

The readChange() function is defined as:

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
}

Where xRead is the value that was initialized (the first, mapped servo output.)

This really is not a good approach. It requires that BOTH values must not have changed by a factor of DEG (~10 degrees, or ~0.28V in my case.) If I write the function such that either OR be less than DEG, then what if I was only changing one servo at a time? So there is a delimma.

Is this simply a property of servos (perhaps cheap ones?) or is there a workaround?


It would be much simpler to include a pastie link. Here is the full code.

I have attached two servos together with a laser pointer to allow for two degrees of freedom (X, Y.) There are options, based on the state of several buttons, to control the servos in various ways. The first is "Motion" where I have two photoresistors that, based on the amount of light exposure, affect the position of the servos. I have not yet implemented the code to control the servos by an Xbox controller. The third option is just randomized movement.

enter image description here

Best Answer

When using the Servo library on an Arduino, a common source of servo buzz is that the interrupt-driven servo routines don't actually give a very stable output pulse. Because the AVR takes interrupts for servicing the millis() clock and other things in the Arduino runtime, the jitter in the Servo library is on the order of several microseconds, which translates to a lot of movement in the servo.

The fix for this is to write your own pulse. Something like this:

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

This will turn off other interrupts, and generate a much cleaner PWM pulse. However, it will make the "millis() timer miss some clock ticks. (The "micros()" function may be called something else -- I forget exactly what.)

In general, for timing critical code, you want to get rid of the Arduino runtime entirely, and write your own using the avr-gcc compiler and avr-libc library that powers the Arduino environment. Then you can set up a timer to tick 4 times per microsecond, or even 16 times per microsecond, and get a much better resolution in your PWM.

Another cause of buzz in servos is cheap servos with cheap sensors, where the sensors are noisy, or when the exact position requested with the pulse can't actually be encoded by the sensor. The servo will see "move to position 1822" and try to do it, but ends up with the sensor reading 1823. The servo will then say "move back a little bit" and it ends up with the sensor reading 1821. Repeat! The fix for this is to use high-quality servos. Ideally, not hobby servos at all, but real servos with optical or magnetic absolute encoders.

Finally, if the servos don't get enough power, or if you try to drive their power from the 5V rail on the Arduino, this will generate voltage-sag-induced buzz in the servos, as suggested above. You may be able to fix it with large electrolytic capacitors (which are a good idea for general filtering anyway) but you more likely want to make sure your servo power source can actually deliver several amps of current at the servo voltage.