Servo "buzz"

I am experiencing servo buzz related to a millis() timer in the same function

Basically a I’m using writeMicroseconds and a miilis() timer to step a servo very slowly up and down. Steadiness is important for my project and the timer is making my servos very jittery.

I read that it is possible to generate your own PWM for the Servo pins but I do not understand the code, and I do not recognize the functions from the example I was looking at. I am hoping someone here can help me make sense of the example, or make me an example I could use. Either way I would appreciate the knowledge

Heres the page I was looking at - http://electronics.stackexchange.com/questions/77502/is-there-a-way-to-stop-servos-from-shaking

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.)

Hope you can help

I don't understand what the posted code will achieve. I made a servo run recently from the hardware timer. Here is example code:

/*
Servo motor controlled by Timer 1

Author: Nick Gammon
Date: 1 February 2015

Note: Servo motors expect a pulse every 20 mS (50 Hz).

The pulse width should be 1.5 mS for a neutral position,
1 mS for -45 degrees and 2 mS for +45 degrees.

Some motors might expect different amounts, eg. 0.5 mS for -180
degrees and 2.5 mS for +180 degrees.

*/

const byte potpin = A0;  // analog pin used to connect the potentiometer

const unsigned long PRESCALER = 8;         // Timer 1 prescaler
const float PULSE_PERIOD = 0.020;          // 20 mS
const float ZERO_POSITION_WIDTH = 0.0005;  // 0.5 mS
const float FULL_POSITION_WIDTH = 0.0024;  // 2.5 mS

// how far apart the pulses are
const unsigned long PULSE_WIDTH_COUNT = F_CPU / PRESCALER * PULSE_PERIOD;
// minimum pulse width (-45 degrees)
const unsigned long ZERO_POSITION_COUNT = F_CPU / PRESCALER * ZERO_POSITION_WIDTH;
// minimum pulse width (+45 degrees)
const unsigned long FULL_POSITION_COUNT = F_CPU / PRESCALER * FULL_POSITION_WIDTH;

void setup() 
  { 
  TCCR1A = 0;          // disable all PWM on Timer1 whilst we set it up
  ICR1 = PULSE_WIDTH_COUNT - 1;   // frequency is every 20ms (zero-relative)
  
  // Configure timer 1 for Fast PWM mode using ICR1, with 8x  prescaling
  TCCR1A = bit (WGM11);
  TCCR1B = bit (WGM13) | bit (WGM12) |  bit (CS11);  // fast PWM top at ICR1
  TCCR1A |= bit (COM1A1);  // Clear OC1A/OC1B on Compare Match,
  pinMode (9, OUTPUT);
  }   // end of setup

void loop() 
{ 
  int val = analogRead(potpin);  // reads the value of the potentiometer (value between 0 and 1023) 
  OCR1A = ZERO_POSITION_COUNT + (val * (FULL_POSITION_COUNT - ZERO_POSITION_COUNT) / 1024) - 1;
  delay(15);                     // wait for the servo to get there 
} // end of loop

Basically by changing OCR1A to some different value as per the calculation near the end, you can change the servo position. That particular line assumes that the limits of travel (val) are 0 to 1023.

This should reduce or eliminate buzz because it does not rely on interrupts. If that doesn't help (and maybe try my test code first with a potentiometer, or even just a fixed value) then you must have a hardware issue.

[quote author=Nick Gammon link=msg=2081237 date=1423360154] I don't understand what the posted code will achieve. I made a servo run recently from the hardware timer. Here is example code:

/*
Servo motor controlled by Timer 1

Author: Nick Gammon
Date: 1 February 2015

Note: Servo motors expect a pulse every 20 mS (50 Hz).

The pulse width should be 1.5 mS for a neutral position,
1 mS for -45 degrees and 2 mS for +45 degrees.

Some motors might expect different amounts, eg. 0.5 mS for -180
degrees and 2.5 mS for +180 degrees.

*/

const byte potpin = A0;  // analog pin used to connect the potentiometer

const unsigned long PRESCALER = 8;         // Timer 1 prescaler
const float PULSE_PERIOD = 0.020;          // 20 mS
const float ZERO_POSITION_WIDTH = 0.0005;  // 0.5 mS
const float FULL_POSITION_WIDTH = 0.0024;  // 2.5 mS

// how far apart the pulses are
const unsigned long PULSE_WIDTH_COUNT = F_CPU / PRESCALER * PULSE_PERIOD;
// minimum pulse width (-45 degrees)
const unsigned long ZERO_POSITION_COUNT = F_CPU / PRESCALER * ZERO_POSITION_WIDTH;
// minimum pulse width (+45 degrees)
const unsigned long FULL_POSITION_COUNT = F_CPU / PRESCALER * FULL_POSITION_WIDTH;

void setup() 
  { 
  TCCR1A = 0;          // disable all PWM on Timer1 whilst we set it up
  ICR1 = PULSE_WIDTH_COUNT - 1;   // frequency is every 20ms (zero-relative)
  
  // Configure timer 1 for Fast PWM mode using ICR1, with 8x  prescaling
  TCCR1A = bit (WGM11);
  TCCR1B = bit (WGM13) | bit (WGM12) |  bit (CS11);  // fast PWM top at ICR1
  TCCR1A |= bit (COM1A1);  // Clear OC1A/OC1B on Compare Match,
  pinMode (9, OUTPUT);
  }   // end of setup

void loop() 
{ 
  int val = analogRead(potpin);  // reads the value of the potentiometer (value between 0 and 1023) 
  OCR1A = ZERO_POSITION_COUNT + (val * (FULL_POSITION_COUNT - ZERO_POSITION_COUNT) / 1024) - 1;
  delay(15);                     // wait for the servo to get there 
} // end of loop

Basically by changing OCR1A to some different value as per the calculation near the end, you can change the servo position. That particular line assumes that the limits of travel (val) are 0 to 1023.

This should reduce or eliminate buzz because it does not rely on interrupts. If that doesn't help (and maybe try my test code first with a potentiometer, or even just a fixed value) then you must have a hardware issue. [/quote]

Nick, thanks so much for your input. I have tested the servos and all the hardware involved individually, and have narrowed it down to definitely be some sort of programming issue. I originally saw heavy buzz when my LCD screen updated, and consistent buzz throughout the program. I took all the elements out but the millis() and the servos and they still jitter so it MUST be the millis() function (in my opinion)

I will attach the code that I have written to control my project (it is a camera slider +pan +tilt) it uses case/switch for the menus - but the final case statement is used to actually run the slider program (once the user has input all the parameters) Could you take a look at the bottom of the code and tell me if there is something you could help me with to eliminate the noise??

#include <Wire.h>
#include <Adafruit_MCP23017.h>
#include <Adafruit_RGBLCDShield.h>
#include <Adafruit_MotorShield.h> //LIBRARY FOR MOTOR DRIVER SHIELD
#include "utility/Adafruit_PWMServoDriver.h"
#include <Servo.h> //LIBRARY FOR SERVOS

Adafruit_MotorShield AFMS = Adafruit_MotorShield(0x61); 
Adafruit_StepperMotor *myStepper = AFMS.getStepper(200, 2);
Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();
#define WHITE 0x7


Servo pan;
Servo tilt;//DECLARE SERVOS
int yy = 0; //STEP COUNTER
//declares ints
uint8_t i=0; //BUTTON INPUTS
int x = 1; //case statement movement
int input_stage = 0;
int time = millis(); //time from when the thing turned on

//user inputs
int total_hour = 0;
int total_minute = 0;
int slide_min = 0;
int slide_max = 0;
int pan_min = 800 ;
int pan_max = 2200;
int tilt_min = 800;
int tilt_max = 2200;


//Integers used for calculations for later
int total_steps = yy;
long total_time = 0;
long step_duration = 0;
int total_pan = 0;
int total_tilt = 0;
long pan_duration = 0;
long tilt_duration = 0;
long step_millis = 0;
long pan_millis = 0;
long tilt_millis = 0;
int step_percent = 0;

// Integers for real time program sliding action
int cur_step = 0;
int pan_pos = 0;
int tilt_pos = 0;
long run_dur = 0;
long start_time = 0;
int total_slide = 0;
long display_refresh = millis();
long display_dur = 1000;
int d = 1;
long end_time = millis();
int m = 0;

void setup(){  
  lcd.begin(16, 2);
  lcd.setBacklight(WHITE);
  lcd.print("Welcome to LapsR");//initialize
  delay(2000);
  
  AFMS.begin();  //initialize motor shield
  pan.attach(10); //attach servos to pins
  tilt.attach(9);
   
  myStepper->setSpeed(30); // set speed
  
  lcd.setCursor(0, 0);
  lcd.print("By Doug E FresH!");
  delay(2500);
  lcd.setCursor(0, 1);
  lcd.print("Thanks to: Dave");
  delay(2000);
  lcd.setCursor(0, 1);
  lcd.print("Thanks to:  Max");
  delay(2000);
  pan.writeMicroseconds(1500); //set pan and tilt servos to centre ish
  tilt.writeMicroseconds(1500); 
  delay(5000);
  pan.writeMicroseconds(1000); //Jitter Test
  tilt.writeMicroseconds(1000); 
  delay(5000);
  pan.writeMicroseconds(2000); //Jitter Test
  tilt.writeMicroseconds(2000); 
  delay(5000);
}

void loop(){  
  uint8_t buttons = lcd.readButtons();  //read lcd buttons
  lcd.blink(); //set cursor to blink
  int limit1 = 4;  //declare limit switch
  pinMode(limit1, INPUT_PULLUP);  // sets pin mode to button
  
  
  
  //begin case statement
  switch(x){
  case 1:
  
   if(m==0){ //move slider backwards until it finds a limit switch
    if((digitalRead(limit1) == HIGH)){ // digital read reads the limit switch pin - HIGH means the switch is open 
      myStepper->step(2,BACKWARD,DOUBLE);
      lcd.clear();
      lcd.print("stage 1");
      delay(10);   
    }
    else{ //move to the next step
      myStepper->release(); //release motor
      m = 1; //set program to 2nd stage
    }
  }
  if(m==1){ // moving ahead jus a bit so there is some pad for innacuracy
    yy = 50; //step counter is initialized
    lcd.clear();
    lcd.print("stage 2");
    delay(1000); 
    myStepper->step(50,FORWARD,SINGLE); //move forward 50 steps
    m = 2;//move to the next step
  } 
  if(m==2){ //stage 3 dawg
    if((digitalRead(limit1) == HIGH)){//count steps til limit is hit
      myStepper->step(1,FORWARD,SINGLE);//move forward 1 step
      lcd.clear();
      lcd.print("stage 3");   
      yy++; //step counter move up one
      delay(10); 
    }
    else{  //display step percentage and total steps
      x=2;
      lcd.clear();
      lcd.print("step %");lcd.print((yy-100)/100);
      delay(5000);
      lcd.clear();
      lcd.print("total: ");lcd.print(yy-100);
      delay(5000);
  
      myStepper->step((yy - 50),BACKWARD,SINGLE); // reset to start position (minus 50 steps for pad)
      myStepper->release(); // release motor
      step_percent = (yy-100)/100; //make calculations
      total_steps = yy-100;
    }
  }  
  

break;

  case 2:
    
    if(input_stage == 0){ //enter duration
      if(d==1){
        lcd.clear();
        lcd.print("Total Duration?");//start asking for inputs - start with total overall duration hours
        lcd.setCursor(0, 1);
        lcd.print("H: ");
        lcd.print(total_hour);
        lcd.print("  M: ");
        lcd.print(total_minute);
        lcd.setCursor(3,1);
        display_refresh = millis();
        d = 0;
      }
      if (buttons & BUTTON_UP) {  //sets hour up - slight debounce delay
        total_hour = total_hour++;
        delay(250);
        d=1;
      }  
      if (buttons & BUTTON_DOWN) { //sets hour down 
        total_hour = total_hour--;
        delay(250);
        d = 1;
      }   
      if (buttons & BUTTON_SELECT){
        input_stage++;delay(250); //select button moves to next stage
        lcd.clear();
        d=1;
      }
    }  
    if(input_stage == 1){
      uint8_t buttons = lcd.readButtons();
      if(d==1){  //when d == 1 update display
        lcd.clear();
        lcd.print("Total Duration?");//ask for minutes
        lcd.setCursor(0, 1);
        lcd.print("H: ");
        lcd.print(total_hour);
        lcd.print("  M: ");
        lcd.print(total_minute);
        lcd.setCursor(9,1);
        d=0;
      }
      if (buttons & BUTTON_UP) {
        total_minute = total_minute++;  //set minutes
        delay(100);
        d=1;
 
  
break;

    case 3:
    
    //calculate moving time durations
    total_time = total_hour*60;
    total_time = total_time + total_minute;
    total_time = total_time*60000;
    total_slide = slide_max - slide_min;
    step_duration = total_time/(total_slide*step_percent);
    total_pan = pan_max - pan_min;
    total_tilt = tilt_max - tilt_min;
    pan_duration = total_time/total_pan;
    tilt_duration = total_time/total_tilt;

 
    
    //sets pedestal to user input slide min position
    myStepper->step((step_percent*slide_min), FORWARD, DOUBLE);
    x = 4;
    start_time = millis();
    end_time = millis() + total_time;

break;

    case 4:
    long remaining = end_time - millis(); //make a couple of run time calculations
    run_dur = millis() - start_time;
   
    if(millis() - step_millis > step_duration){ //take one step after step duration
      myStepper->step(1, FORWARD, SINGLE);
      cur_step++;
      step_millis = millis();  //resets step duration
      myStepper->release(); 
    }
    if(millis() - pan_millis > pan_duration){ //same as slide
      pan_pos++;
      pan.writeMicroseconds(pan_pos);
      pan_millis = millis();
    }
    if(millis() - tilt_millis > tilt_duration){//same as slide
      tilt_pos++;
      tilt.writeMicroseconds(tilt_pos);
      tilt_millis = millis();
    }
   if(run_dur > total_time){   //resets to top of user input once total duration runs out
     x = 0;
     input_stage = 0;
     myStepper->step(cur_step, BACKWARD, DOUBLE); //sets motor back to home position
     myStepper->release(); //release motor
    }
  }
}

I had to delete a bunch of the menu steps because of the character cap in the forum... but it is the final case statement that is important i think

I originally saw heavy buzz when my LCD screen updated, and consistent buzz throughout the program.

Well, anything that stops interrupts will affect servos. I suggest you consider using my code (adapted) which uses the hardware PWM.

thanks Nick, I am going to try and adapt it, but there are so many functions I do not recognize in there. I was hoping you might be able to help me apply it to the servo movement in ‘case 4’. I’m sorry for being so needy! I am a big time novice, but I am dedicated to learning about robotics and engineering. If you have instagram you can actually look at some pictures and videos of the project in question if you look up @freshanator

servo jitter <------

There is so much stuff there I can't be confident any change I make will work without testing, and right now I have to strip my office because of work being done in it tomorrow. I can't be sure if Timer 1 is being used for something else or not.

Hi, Can I ask the obvious question? How are you powering the servos and do you have enough power to supply them.

Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png or pdf?

Tom...... :)

TomGeorge:
Hi,
Can I ask the obvious question?
How are you powering the servos and do you have enough power to supply them.

Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png or pdf?

Tom… :slight_smile:

Thanks Nick. I will attempt using your code

Yes, I can show you, but I have been very thorough in troubleshooting to narrow the jitter down to the millis() timer.

I am using a 12v 2amp source

IMG_0429.JPG

Hi, good, what value are the two blue electros and where are they connected? What spec is the servo and how many? Have you written a simple sketch using a Servo library?

Tom...... :) Why so many questions, I can't help it, its my job, I fix stuff.