I bought 10 low-cost hobby servos and want to compensate for their inacuracy in my code (I need to point a pointer at angles from 0° - 140° in steps of 10°). The servos are powered by a separate 5V power supply.
When using the basic servo.write(angle) "out of the box", I obtain the positions marked in green:
From 0° to 50° the angles are "stretched"
From 50° to 90° they are fairly spot on
From 90° to 180° the angles are "compressed"
When using servo.writeMicroseconds(ms), having established that 0° = 610ms/140° = 2040ms, I obtain the positions marked in dark blue:
From 0° to 20° they are fairly spot on
From 20° to 120° they are first "stretched" and then "compressed"
From 120° to 180° they are fairly spot on again
Particularly the servo.writeMicroseconds(ms) results strike me as bizarre, but the servo.write(angle) method is not much better.
I obtain such results more or less with each of these servos. Is there some clever way to compensate via code? Is there a better "servo writing method"? Or must I buy better small servos (which ones should I buy in that case)?
When you do a Servo.write() the library has to convert the angle to a number microseconds in order to determine the width of the pulse.
In my experience with my cheap servos when I have established the appropriate number of microsecs for a position they repeat reliably. To be honest I have never attempted to see if the same numbers of microsecs has the same effect for different examples of the same type of servo.
You may find that there is backlash in the system so that different numbers of microsecs are needed to achieve the same position when moving to the position clockwise or counterclockwise.
Unfortunately you're stuck with the control electronics in the servos. Hobby servos are simply not designed to be precise or particularly linear. Remember, their intended purpose is to be manually controlled via RC using visual feedback.
As Robin2 says cheap servos are usually fairly repeatable so you could always make a calibration table for the few steps you need from 0-140 and run them from that. But it might need recalibrating for each individual servo depending what accuracy you're expecting.
The marks on the image were obtained only by forward rotation from 0° to 180°, in steps of 10°. I have not even checked how things may play out going backwards, or randomly, in steps of 10° as needed.
I am also surprised about the absolute difference between using angles versus microseconds. So, as you say, since there is library conversion, one should just as well use servo.writeMicroseconds (which I like anyway for finer resolution). I used 10.28ms per 1° of rotation.
The stretching and compressing along the 0° to 180° arc means that with servo.writeMicroseconds only 0°, 10°, 20° and 120°, 130°, 140°, ... are more or less spot on. The other nine positions are not and I have no clue how to compensate code-wise for that in-between bit.
Can you recommend a more accurate (yet nearly as small) servo? If I have to spend €50, but get much better accuracy in return, that'd be worthwile for my use case (rotating a negligible weight pointer).
Or should I use a stepper motor with microstepping and just forget about servos altogether?
...and then reading the ms values for angleTarget from that? That's a good idea! I do that next.
In any case, can you recommend a more accurate (yet nearly as small) servo? If I have to spend €50, but get much better accuracy in return, that'd be worthwile for my use case (rotating a negligible weight pointer).
Or should I use a stepper motor with microstepping and just forget about servos altogether?
Try out the idea of the lookup table.
You only need to determine 15 or so values.
It can be a 1d array. The index into the array can be your degrees/10 The value in the array element is the microseconds value.
Lagom:
Can you recommend a more accurate (yet nearly as small) servo?
I can't. Low price has generally been my goal.
Or should I use a stepper motor with microstepping and just forget about servos altogether?
Stepper motors require stepper motor drivers and homing switches and code to make them go to the HOME position when the Arduino is started. Not nearly so convenient. But they definitely have more precision. Stepper motors are also heavy and inefficient users of energy.
vinceherman:
Try out the idea of the lookup table.
You only need to determine 15 or so values.
It can be a 1d array. The index into the array can be your degrees/10 The value in the array element is the microseconds value.
Thanks Vince, 1D array... simpler, yes; I'm determining the 15 microsecond values per servo now. Surprising how much they differ from one servo to the other.
Robin2:
I can't. Low price has generally been my goal.
Stepper motors require stepper motor drivers and homing switches and code to make them go to the HOME position when the Arduino is started. Not nearly so convenient. But they definitely have more precision. Stepper motors are also heavy and inefficient users of energy.
I got several stepper motors and driver PCBs here, but the servo 1D array method via code is most straightforward, so that's what I am doing now; determining the 15 microsecond values per servo. Maybe by digging through this forum for a few hours, I find some higher precision servos later.
The precision of a servo probably depends to some extent on the quality of the internal potentiometer used for feedback. However I would not expect a feedback system based on a potentiometer - even an expensive one - to be perfectly linear and predictable.
Repeatability is a different matter and should be OK if you get the correct values for the LUT for any specific servo.
Yeah, I've measured nearly all of them, letting them run a random angle code now to "break them in" like a good pair of brogues, and later see if they go back to the same positions, depending on the microseconds I noted for each.
If they do reasonably well, like within 1°, the array method is the way to go when one has multiple positions to rotate to.