Project tutorial

Calibrating My Servos © MIT

All my servos are a little bit different to each other - I've built a rig to measure what PWM values turn my servos to a specific angle.

  • 14,794 views
  • 9 comments
  • 64 respects

Components and supplies

A000066 iso both
Arduino UNO & Genuino UNO
×1
Adafruit PWM Servo Driver Shield
×1
M3 bolt
×8
M3 washer
×8
LM2596 DC-DC Step down voltage converter
×1
Red PLA
×1
White PLA
×1
180 degree clear math protractor
×1
09939 01
Rotary potentiometer (generic)
×1
9g servo motor
×1

Necessary tools and machines

3drag
3D Printer (generic)

Apps and online services

About this project

In a previous post on Hackster, I wrote about how I've created a rig to compare how different servos from the same batch rotate, and showed that sending the same PWM signals to different servos results in some significant differences - the picture below shows servos which all started in their furthest counter clockwise position and were sent the same PWM signal - they all have slightly different behavior.

Why not use existing servo libraries for the Arduino?

I use the Adafruit PWM Servo Driver to drive a bunch of servos, and this allows me to send a different PWM signal to each servo. I could use the Servo library which is built into my Arduino installation - but I've a few reasons for not doing this:

  • With the Adafruit PWM Servo Driver, I can control my servos using just two pins over I2C, rather than having dedicate a pin on my Arduino to every servo.
  • I've sometimes found with the Arduino Servo library that when I attach to a servo and send a signal for it to rotate to the zero degree position that it rotates to the limit of travel and grinds the gears inside the servo.

Of course the 9g servos that I'm using are cheap and not designed for accuracy - but I think I can squeeze some more value from them. I want to use them for a walking robot, and need their behavior to be a bit more precise and predictable.

How can I control my hobby servos more accurately?

Confirming that my servos all behave slightly differently made me want to build a device that would allow me to calibrate a servo - what PWM values do I need to send a servo to move to a 0 degree position? Or the 90 degree center point? How do I avoid sending a PWM value which snaps the servo to an extreme position with lots of crunching and gear grinding?

I decided to combine a couple of simple concepts.

  • Align my servo with the center of a simple protractor, so as the servo's wiper rotates, I can measure its position.
  • Connect a potentiometer to an analog input of the Arduino, so I can control the position of the servo's wiper by adjusting the PWM value as I adjust the position of the potentiometer shaft.

For this version, I was happy to use the Serial Monitor of my Arduino IDE to tell me the value of the PWM signal, which I can record manually.

I used Autodesk 123D to design a simple rig that would hold the potentiometer, the protractor, a single servo, my Arduino, and the Adafruit PWM servo driver along with a power supply.

I want to keep using the Adafruit PWM servo driver for my calibration as that's what I'm likely to use in my final project. If I was going to control the servo directly from a pin on the Arduino, I'd replicate that in the device and calibrate the servo against PWM from the Arduino.

Building the device

I printed out each of the parts, and attached them together using the M3 bolts and washers.

The mounting holes in my device design are adjusted to fit my components - different components may have variances in the hole diameters and positions.

For electronics, I attached the center pin of the potentiometer to the A0 pin of the Arduino, and the other pins to 0v and 5v on the Arduino. I attached the PWM servo motor driver onto the Arduino, and then I connected the DC-DC buck converter (I'd tuned to convert the 9v power supply input to a 5v output). I connected the 9g servo to the first slot on the servo motor driver shield, and then uploaded my sketch to the Arduino.

The end result is shown in the photographs below. By turning the knob in the top left of the device, I can control the position of the servo, and using the Serial Monitor in the Arduino IDE, I can read the PWM signal being sent to the servo motor for any given angle.

So what does the device tell me?

For my servo, I rotated it to its furthest clockwise limit, and placed the wiper on the servo so that it would be positioned just beyond the 180 degree mark on my calibration device's protractor.

I did this because I've found in my hobby servos the range of motion is usually slightly more than 180 degrees.

I connected the power to the device, and then I rotated the potentiometer knob until the servo needle was aligned with the 0 degree mark on the protractor, and noted the PWM value written to my Arduino IDE's Serial Output. Then I rotated the potentiometer until the servo needle pointed at 10 degrees, and noted the PWM value again. I continued this process every 10 degrees until I reached 180 degrees, and I've shared the results in the table below.

I created a graph of the results (shown below), and as you'd expect it's obvious there's a strongly linear relationship between PWM and servo angle.

Now I've got some PWM and angle values that are characteristic of this particular servo - other servos might have similar characteristics, but they won't be identical.

I've a couple of options now for more accurate servo performance:

  • Record these value pairs and hold them in an array in my Arduino sketch, and when I want to move the servo wiper to 90 degrees (for example), just refer to what PWM value relates to this angle and send that PWM value to the servo. This has a drawback that I can only rotate to angles which are multiples of 10.
  • Or if I want complete control of what angle I send to, I can use linear regression on the data set to work out what formula relates angle and PWM value (for this servo, PWM = 2.35 * Angle + 159.4). This way I can pass the angle I want to turn to as a parameter, and programmatically calculate the PWM necessary to rotate this particular servo to that position.

Either way, I'm able to control this servo with more accuracy and confidence than before. It's quite a bit more effort to calibrate each servo precisely, but for my project, it's worth it for the additional accuracy.

Of course, when I remove the servo from the device I have keep the servo wiper attached, as the measurements relate to how the wiper is attached whilst it's being calibrated.

Improvements for the next version

After calibrating a few servos, I've thought of some improvements that I will build into the next version.

  • The servo needs to have a quick release mechanism - maybe securing the servo in place using a clip with neodymium magnets, instead of securing it with M3 bolts.
  • I want to make this smaller - maybe I could re-orient the Arduino and power supply to achieve this.
  • I need a power switch between the 9v battery and the buck converter, so I don't need to fully disconnect the battery every time I want to turn off the machine.
  • I'd like to be able to print the rig in fewer pieces.
  • Instead of manually reading values from the protractor, it would be better if a sensor could detect when the servo is in specific angular positions and feed this back to the Arduino - this could eliminate some parallax error.
  • Finally, an LCD display to show any results would help a lot, as I wouldn't need to have my computer connected to the device.

Wrapping up

So that's it - my hobby servos are cheap and not designed for accuracy - and for a lot of applications, they're perfectly fine. However, I'd like to get a bit more accuracy and repeatable performance from mine, and the rig in this post helps me achieve that.

Code

Sketch for servo calibrator deviceC/C++
#include <Adafruit_PWMServoDriver.h>
#define analogIn A0

Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();

int inputValue = 0;
int tmpPwmValue = 0;

void setup() {
  Serial.begin(9600);
  pwm.begin();
  pwm.setPWMFreq(60);
}

void loop() {
  inputValue = analogRead(analogIn);

  // inputValue will be between 0 and 1024
  // By observation, servo min and max PWM values are usually betwee 100 and 612
  // The formula below allows us to simply convert analog input to PWM
  int pwmValue = (int)(100 + (inputValue / 2));
  pwm.setPWM(0, 0, pwmValue);

  // Check to make sure we're not unnecessarily displaying duplicate data
  if (pwmValue != tmpPwmValue)
  {
    Serial.print("Potentiometer Input = ");
    Serial.print(inputValue);
    Serial.print(", PWM = ");
    Serial.println(pwmValue);
  }

  tmpPwmValue = pwmValue;

  delay(15);
}

Custom parts and enclosures

Comments

Similar projects you might like

Play Polyphonic Tones!

by nathan_ramanathan

  • 3,115 views
  • 3 comments
  • 8 respects

Arduino Tutorial: JARVIS v1 | How to make a Home Automation

Project tutorial by rahulkhanna

  • 5,728 views
  • 10 comments
  • 46 respects

Retro 1980s LED Clock/Count Up Timer

Project in progress by randtekk

  • 1,164 views
  • 4 comments
  • 8 respects

Heart Rate Monitor Using IoT

Project tutorial by Technopaths

  • 3,960 views
  • 2 comments
  • 18 respects

Arduino MEGA Guitar Pedal

Project tutorial by electrosmash

  • 7,065 views
  • 1 comment
  • 35 respects
Add projectSign up / Login