Multiple R/C servo control

Hi folks,
I've been doing lots of tests with controlling R/C servos lately, some good
experiences and some bad experiences. I even wrote an introduction paper on
how to interface the PC with R/C servos using the serial port and a PIC
microcontroller.
The strategy I present in that paper is very basic, and although it works
fine, it is kind of jittery and does not support too many servos. Now I'm
working on a improved version of my servo controller that will theoretically
get rid of the flicker and support unlimited servos (in theory).
I am a firm believer that the best way to learn something is to teach it.
Therefore I will try to make another paper exposing this new method (of
course, if it works). Before doing that, I need to know if someone else
already did such tutorial paper, and if yes, if it is similar to my
strategy.
Here's an outline of my strategy, please express yourself if you find a flaw
in it as I'm going to test it tonight.
1-Assume there is an array of servos controlled by the uP
and this array has the following fields:
servo_idx, pulse_width_uS, servo_no
Whenever the array is updated (through serial commands), it is
sorted in by pulse_width_uS
2-Drive all servo lines high, set TMR0 with the time interval
of the first servo of the array
3-When TMR0 triggers, set the respective servo low.
Also set low all the subsequent servos from that list that
have the same pulse_width_uS. Set new TMR0 interval
as the difference between next servo pulse width and current
one.
4-Repeat this operation for all servos until there are no
more servos to drive low. At that point, set TMR0 with
20ms - current pulse_width_uS
And that goes forever. There are a couple more quirks to it, but that's the
overall outline. If you are controlling 4 servos, TMR0 should trigger 5
times in the worst case, 1 time for driving all servo lines up and 4 times
(less if some of them share the same time interval) to drive them low.
That gives me lots of time in between pulses to do whatever I want (receive
USART bytes for example) without interfering too much with the PWM timing
mechanism.
Do you think it is a good approach to try? If yes, do you think it would be
worthy to write a short paper on that?
Cheers
Padu
Reply to
Padu
Loading thread data ...
Padu... if I spoke Greek I might be able to give you an intelligent answer to your question. Now let me ask you a question: How many people out here in the real world could understand a word you said?
First of all, what kind of servo and what kind of interface are you using? If I buy a sail winch servo from Hitec, how do I connect it to what without going through the radio? How do I power it if not through the existing radio from the vendor?
Should you not set up the experimental parameters first for those of us with interest in this subject to duplicate your work in a scientific manner?
I've met you and recognize you as a brilliant automation engineer and only make this note from my own ignorance on behalf of others who may not be willing to share their own lack of experience but who would like to know more and who could appreciate your insight.
Wayne
Reply to
Wayne Lundberg
Padu:
Controlling servos from a PIC has been done before. My PIC based servo controller can be found at the URL below:

There is a subtle issue with servos -- they are very sensitive to the pulse width. Changing the pulse width by a uSec or two will cause the servo to move a small but noticable amount.
The scheme you propose will have a difficult time producing pulse widths that are close to one another. Changing the pulse width of one servo might cause the pulse widths of the other servos to change a little.
The simpler strategy is to forget all the sorting and just pulse one servo at a time in sequence. Turn on servo 1, set TMR1, turn off servo 1, and repeat for each servo in sequence. Given a maximum pulse width of 2.5uSec, you can visit 8 servos every 20uSec.
Lastly, remember that PIC's are really really cheap. If you find yourself doing all sorts of tricks to get them to do what you want, consider using more PIC's. For example, suppose that you are using a digital servo and need a really fast update rate. Dedicating a PIC to each digital servo is not unthinkable.
Once you get your PIC code working, by all means write it up.
My $.02,
-Wayne
Reply to
Wayne C. Gramlich
"Wayne Lundberg"
It's always good to learn a new language right? ;-) I'm sorry if my post carried too many assumptions, I should have disclaimed that a certain knowledge on the subject was necessary beforehand. I am usually more explicit, but my post was long enough so I decided to summarize my request. I'm sure that some of the habitue PIC gurus will make some sense on what I said. Just to prove that I usually try to be more methodic, please read the paper I mentioned in my email:
formatting link
I fail to convey my point in the above paper, then blame it on me, because its intention is to bean introductory text on how to interface R/C servos with a PIC microcontroller.
R/C servos, the ones you buy in R/C hobby stores (hitec or futaba are good examples)
From my knowledge, it is usual for these types of servos to use a PWM signal of 20ms pulsetrain with a pulse width varying from 1 to 2 ms.
It will probably have 3 wires: VCC, GND and Signal. I don't know about sail servos, but usually R/C servos are powered out of 5VDC (current varies from manufacturer to manufacter). Signal line is the one you have to generate the PWM signal.
As mentioned above, any 5VDC will do. (but take a look at the specs first in order to determine current requirements)
I intend to do that if I write the paper. My previous post was only a feeler if someone else hasn't already written similar material... I don't like to write redundant stuff without adding something substantial to it.
Not a problem. I love to share my knowledge of things I know as well as things I don't know. 8^)
Cheers
Padu
Reply to
Padu
"Wayne C. Gramlich" wrote
That's a very nice write up! I also found some literature about a guy named Scott Edwards who has a similar board. I don't know if he's around here though.
I noticed that in my previous experiments...
Yes, I realize that, but the way I'm planning to do is to do very little computing in the first 2 miliseconds of the pulse train. The sorting procedure (heck, I'm a software engineer, I SHOULD know how to write a very efficient sort procedure) will be done in the remaining 18ms, while the PIC is sitting there doing nothing.
I'm not sure I understood completely this last part. What you are saying is to generate each one of the pulses back to back? If yes, then how do you guarantee the 20ms interval between pulse rises? Are you taking advantage on the fact that pulse train in most rc servos is very forgiving? (I've heard people saying that even 5ms pulse trains worked fine)
I'm aware of that, and that's probably what I'm going end up doing, driving two servos per uC, but I always like to think in more abstract terms. Actually, if I write up this, I think I will completely abstract which processor I am using. (but maybe I'll post the source code as an appendix)
I'll let you know tomorrow what are my findings, I'll pay special attention to your (and mine) concerns about two servos with very close positions.
Thanks for your comments
Padu
Reply to
Padu
A number of us have done PIC based servo controllers over the years. The first one I saw was the one by Dennis Clark.
[snip pulse width sensitivity]
The sort time is a non-issue.
The issue is that there is a delay from the time that you get an interrupt until you stuff the next difference number into the TMR1 register. You have to take that into account. Also, you have to get out of the interrupt routine before TMR1 wants to generate another interrupt. There is a great deal of tricky programming to be done. It is best to avoid all of that tricky stuff.
It is simpler than that. Set TMR0 to interrupt every 2.5ms. It picks the next servo delay from the array and stuffs it into TMR1 and returns from interrupt. When TMR1 fires, clear the servo line. The pulses look as follows:
0 ---XXX-----------------------------XXX-------------------------XXX-- 1 ----------XX-----------------------------XX------------------------- 2 -----------------XXXX--------------------------XXXX----------------- 3 -------------------------XXX---------------------------XXX----------
It should be a piece of cake to keep 8 servos running with 20ms between servo pulses.
I always wind up doing 4 servos, since I put resistors in series with the servo ground return to measure stall current. I simply run out of pins on the smaller PIC's.
[snip PIC's are cheap]
[snip write-up]
-Wayne
Reply to
Wayne C. Gramlich
For this to work your processor would need to be VERY fast.
Say you divided the full travel of your servo into 256 steps so that a value placed in a byte wide veriable mirrors the position of the servo.
The full travel is covered by a 1ms pulse (1ms being one end and 2ms being the other end).
So you would divied your 1ms pulse into 1/256 ms units.
If you had servo A set to position 10 and servo B set to position 11, the amount of time you have available to complete servicing the interrupt that handles servo A and start processing the interrupt to service servo B is 1/256 ms.
this is about 3.9 microseconds. On a fast PIC executing instructions at about 5 MIPS this is about 20 instructions between servicing servo A and servo B. ***ANY*** delay (such as servicing another interrupt - maybe a heartbeat system clock or serial I/O) would give jitter.
You might be able to get away with building groups of servos that have a minimum position difference that is greater than 5. Then you could interleave servicing of the groups say every 10ms. but this would only allow you two groups. if you did this for every 5ms you could interleave 4 groups but this would limit minimum position difference to 4.
Even with this interleaving I still think you would have problems with other interrupts causing jitter.
Regards Sergio Masci
formatting link
- optimising PIC compiler FREE for personal non-commercial use
.
Reply to
Sergio Masci
If you want a really simple and cheap start with controlling servos via the pc, check my page below. I use the servos for a pan/tilt cam, but you could use them for robotic stuff too. I've given up on trying to program my own servo conrol chips, as Kronos robotics sells them for $7 which fits my budget. You can check the bottom link to see how simple it is to control the servos over the net.
formatting link
Reply to
Si Ballenger
I could. All of it.
Anyway, Padu, the problems I see are:
1) You have to make TMR0 tick fast enough to give you the resolution you need. My digital Futaba RC gear gives 1024 steps over the 1-2ms range of pulse widths. To match that you'd need a 1MHz tick on TMR0.
2) you have to make sure everything you do takes
Reply to
Tim Auton
"Wayne C. Gramlich"
The strategy you explained in here plus Sergion Masci's argument have convinced me of that. Perhaps my strategy would work in most of the cases, where values were far apart (or exactly coincident). As you said, the advantage of being able to control an undefined number of servos is quickly lost in an environment with as many restrictions (physical time to compute, number of pins, etc).
And now I know how hard it is to debug this type of application.
0 ---XXX-----------------------------XXX-------------------------XXX--
1 ----------XX-----------------------------XX-------------------------
2 -----------------XXXX--------------------------XXXX-----------------
3 -------------------------XXX---------------------------XXX----------
Very simple idea and I don't see why it wouldn't work. If you don't mind I'll borrow your idea and implement it on my robot... Now looking at my flowchart (yes, I can only program if I flowchart it first), it looks like a nice piece of logic, but I spend a good amount of time to implement it and find out that it wouldn't work as I expected.
Thanks for your help!
Cheers
Padu
Reply to
Padu
[much snippage]
I don't mind at all. Have fun.
-Wayne
Reply to
Wayne C. Gramlich
Thank you Padu.. You have illuminated a few chunks of darkness in my brain. My son Lyle, originator of the RCWindjammer
formatting link
has a few servos left over from some production runs. I'll have to take one apart and play with it. I do have a Basic Stamp X-24, so... I should be in the shop playing instead of here writing!
Wayne
Reply to
Wayne Lundberg
"Wayne C. Gramlich" wrote
I've implemented it on my devboard yesterday, with USART interrupts and all, and works perfectly! No jitter even when sending a constant flow of serial data at 9600baud. Thank you very much.
The only thing I noticed was that when I set TMR1 to 1500uS for example, I would see 1480uS on the oscope. It didn't turn out to be a huge problem, as the error was constant. I did a quick test, doing a program with the only purpose of generating a square wave of fixed width, and even there I had a very small error (about 100uS) between expected (calculated) interval and measured interval.
I'm using a PIC18F452 with an external crystal can at 4MHz. TMR1 is set to 16 bit and no prescaler. The way I'm calculating the interval is pretty simple, at 4MHz each instruction cycle takes 1uS (4/FOSC), therefore a full cycle of TMR1 (0xFFFF) would take 65536uS right? If I want to calculate a TMR1 interval of 1500uS, then it is 65536-1500-2 right? (the 2 is because updating TMR1 takes two instruction cycles), that would give me 64034 or $FA22. Is that correct?
I believe I can compensate for the error and no harm done, but I thought it was weird. The only thing done in TMR1 interrupt is driving servo lines low, nothing else.
Cheers and thanks for all contributions!
Padu
Reply to
Padu
....
....
I also have designed a servo-controller board in the near past. I've used an AVR microcontroller and not a PIC but that shouldn't really change the theory. As other have pointed it out, the approach you're trying to do will have a difficulty generating close, but not equal servo pulses. The other problem, no one seems to have pointed out so far is that servos (at least older/cheaper analog ones) tend to draw a lot of current at the start of the pulse. If you start the pulse for all of your servos at the same time, you can easily overload your power-supply but even if not, you would generate a lot of noise on your power lines. Distributing the servo start-pulses over the full 20ms repetition window like Wayne suggested is a good idea for this reason as well.
Just for reference, you can take a look at my servo controller design at
formatting link
It uses HW de-multiplexing of the signals to achieve even higher precision.
Regards, Andras Tantos
Reply to
Andras Tantos
"Andras Tantos" wrote
I realized that yesterday when I replaced my robot's stock steering servo by a more powerfull digital one. If I input drastic changes (1ms to 2ms in one step), it would draw so much current that my usb port would lock because of the overload current. I know, for now I'm powering out of usb because I'm using a development board that is usb programmable. I'll provide proper current capabilities later when I put everything on a nice protoboard.
On the same subject, I was reading the specs on my digital servo (hitec hsc-5997) and it says that additionally to providing high torque, it is also possible to program it (speed, center, etc) using a "proprietary" hitec programmer. Well, the servo only has 3 wires, whatever the program does, it must be using the standard signal wire (and maybe the VCC line?).
Does anybody know the specs on how to do a home made programmer for those digital servos?
Cheers
Padu
Reply to
Padu
Yes, the AVR ATmega128 has two 16bit timers, each capable of controlling three pins. Set few bits, then update the 6 registers for the 6 servos, no interrupt code required. AVRs rule, and the Linux environment works great with them.
Reply to
Mike Ross
Hi Padu. I hadn't been following this thread previously, but the first thing I did notice with your original scheme is exactly what Andras has pointed out here. Some people have written how it's a good idea to pulse all the servos simultaneously, but in fact then you "will" get a large current surge, from all servos at the same time.
Several months ago on this forum, there was a thread about servo internals, to which Gordon McComb and I made a #posts. I took one of my "analog" servos apart and looked at the signals to the motor on a scope. Basically, current flows into the motor for 2-7 msec or so, every time it receives a pulse-in on the signal line. The length of current output to the motor depends upon how much torque the servo must generate to bring the horn back to the set point. For low torque development, the motor current pulse is only 2-msec. For heavy torque, the current lasts upwards to 7-8 msec or so.
In fact, as an aside, this figure is related to how often you can update an analog servo - probably not faster than every 10-msec, or 100-hz, to be safe. I found that, if you updated the servo too fast, and the torque development was high, the servo internal control loop would crash.
In any case, it's obvious from this that you will generate current surges, which can be upwards to 3-4 Amps if you're running 12+ servos with heavy loads, like on a walker, if you pulse them all simultaneously. Better to string the pulses out, sequentially one after the other, as several poeople have mentioned. In addition, I always put some filter and bypass caps on the servo buss lines for noise filtering, just to be safe.
- dan michaels
formatting link
========================
Reply to
dan
[snippage]
You are welcome.
I think you are witnessing classic interrupt latency. I suspect that you have 20uSec between the time the interrupt occurs until you actually clear the servo pulse. You can verify this by inspecting your interrupt service routine and counting instruction times. Remember, there is "psuedo" CALL instruction required to initiate the interrupt. I'm more familiar with the 16F series, but I think the 18F series CALL takes 3 cycles (I could be wrong.)
Also, if you crank of the beam intensity on your oscilliscope, I suspect that you discover that there is 1-2 uSec of jitter on the pulse. If an interrupt occurs in the middle of a multi-cycle instruction, the interrupt is delayed until the instruction is done. The vast majority of instructions on PIC's are one cycle, but some instructions such as CALL, RETURN, GOTO, etc. take more than one cycle. If the interrupt occurs during one of those instructions, there will be an extra cycle (or two) before the servo pulse is cleared. Given that your servos are not experiencing jitter, I would not recommend worrying about the issue.
-Wayne
Reply to
Wayne C. Gramlich
Is there any open source& design multi channel servo controller board with board 9 or 10 bit accuracy?
Reply to
<Nick>
On Thu, 22 Sep 2005, it was written:
I'm not exactly sure what you are asking for but just in case this is of use:
The XCSB servo library uses 8 bit resolution and it controls multiple RC servos. You can buy the source and change this to 9 or 10 bit resolution yourself very easilly.
There is a simple circuit at
formatting link
labelled "PIC Servo Controller" that shows how to control 4 servos with this library using a VERY low cost PIC.
The software for this circuit is compiled using the LITE edition of the compiler and the servo library that comes with it (not source).
Regards Sergio Masci
formatting link
- optimising PIC compiler FREE for personal non-commercial use
.
Reply to
Sergio Masci

PolyTech Forum website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.