PDA

View Full Version : Control unlimited servos at the same time



mrx23
- 13th September 2006, 15:00
I have an idea to control lots of (>10) servos from a PIC at the same time.

Normaly everbody would choose this way to control multiple servos:

High Ch1pin 'Or pulseout pin,ch1
Pause ch1
low Ch1pin

High Ch2pin
Pause ch2
low Ch2pin

High Ch3pin
Pause ch3
low Ch3pin

This is uneffecient, and its also limits the maximum servo number
becasuse a servo needs to update its position in every 18ms (19mS) (~50Hz).
When using this technic we must run allover every signal sendout.
Its limited to max ~11 servos (@50Hz)

Here is the normal way to send out control signals:
http://kepfeltoltes.hu/view/060913/Oldway_www.kepfeltoltes.hu_.gif

There is a another solution, when we set High all Ch pins
and give out a pause command, length of the first pause is the lowest ch time ,after the pause, Low the smallest ch pin.
Then we have to figure out wich is the next lowest ch,then subtract the previous ch from this.
We get a delta time. Give pause out for delta time, then Low the second lowest ch pin. Then search for the third lowest ch, subtrack the previous ch, pause delta time,low pin, search for the 4th lowest... and all over for all n servos.

Here is the Faster way:
http://kepfeltoltes.hu/view/060913/More_Fasterway_www.kepfeltoltes.hu_.gif

we have ch1,ch2,ch3,ch4,ch5,ch6,...ch(n) VAR byte 'for 8bit control
ch1 is RB0,ch2 is RB1 ............

1, High all servo pins (RB0,RB1....)
2, find the smallest ch
3, pause for smallest ch time
4, low smallest ch pin

5, find next! smallest ch
6, subtract the previous from it
7, pause for delta time
8, low the ch pin
9, goto 5

it would be very nice when we could put in a loop (for,repeat,while)

The limits of the servo number is really just the pin number of the microcontroller:)

Some idea for the structure, to find the lowest ch:

'---------
For i=0 to (# of the servos)

Select case Ch# 'current servo
case is > ch1
..?
case is > ch2
..?
case is > ch3
..?
.
.
.
End select

Next i
'--------
of course its not so simple, but please help me how to code this.
Thx

blainecf
- 13th September 2006, 15:29
I love to see people analyzing problems, breaking them down into their core components, and discovering that things don't have the limitations previously thought. (i.e. the limitation was really me, not the hardware!)

I'm thinking a gotcha on this is the amount of calculation and decisions being made. And, obviously, things (servo positioning) will change over time.

So, you have calculations, optimizing the data for an algorithm, and allowing for something to signal changes.

The good news: things won't be changing that often (especially when viewed from MCU mhz perspective), or at least you hope that's the case (see below).

Let's say, for the sake of argument, that you were to create three parallel arrays. One array for priority, one for on-time, and one for off-time. Then make an algorithm that will step through the arrays and set the pins accordingly.

The first array functions like a database index, so, rather than (re-)sorting your arrays everytime something changes, you just change the index.

A problem you'll run into is when managing multiple waveforms of different frequencies, you'll have to use something like a least-common-denominator concept to artificially time-slice/time-share. Hard to explain without a bunch of math; but simply draw a few different square waves of different frequencies on a whiteboard, one-above-the-other. Then, draw vertical lines that represent the action points. Conceptually, you'd need infinite resolution, but engineering is trade-offs, so you'll wind up picking (through calculations or trial-and-error) a minimum time increment to deal with.

Why? When will you take time to check for changes and then re-calculate the array values.

------------------

Where's the code!!!!!!!!!

I'll watch this posting to see how things go.

bcf

mrx23
- 13th September 2006, 18:17
I think using an indexing is very good idea instead of sorting.
Its maybe not a problem to recheck the integrity of the index,
because is still much less time than send out pulses in the oldway
(it can take SEVERAL mS-s instead of the integrity check which is not a big
calculation (just comparing) @4Mhz)


Or does enyone knows that how do manufactures solve this problem to
control more than 15 servos @same time, from 1 controller, in single tasking.

Because that is NOT possible to do this with standard servos.

mat janssen
- 13th September 2006, 19:56
I made a system with 8 servo output channels and it is easy to make that for more.
See attachement

mrx23
- 17th September 2006, 15:57
I've finished the whole code.
Its contains of:

1, A Word[] array:
its for storing each channel value between 1000-2000uS-> 1-2mS
Or when using 10bit ADC-> we add ~988 to the adc value

0-1023
+988
988-2011 uS -> 0.98-2mS instead of 1-2mS
engineering is realy a tread of :D
maximum resolution is 16bit because of Word is 16bit.

you can add many channels as your PIC can handle,
simply extend the numbers of elements of the A array.
like A var word[10]

And you also must change the biggest element number
at line 31.

2, B Byte[] array:
it has the same numbers of elements as A.
I use this array as an index of the A array.

On startup I fill it up with [0,1,2,3]
this index is used for "linking" (or pointing) to the A array.

3. sorting
the sorting was do it on the B index array,

4, temp is just a byte because its temporaly holds
the value of a the B elements, which is far less then
255.

5, send out routine
high all servo pins
because the B array is the sorted form of the A
we can easily obtain the lowest channel value.
calculate the delta times
pauseus out the delta times
and low the lowest channel's pin.

You can learn more on delta timing on my previous posts.
and more infos are in the code.
The whole code is just 283 Word
and its MAX runoff time is 3mS! (at a channel config of:1000,1435,1140,2000uS)
when you want to sendout like this: pulseout ch1,ch2,ch3,ch4... itt would took

5,6mS.!!! So my code is really effecient, especially when you use more channels.

array setup: http://kepfeltoltes.eof.hu/gallery.php?pic=files/76_1158505218.gif
-------------------------------------------------------------------------

'startup
counter var BYTE
Temp var byte
A var WORD[4]
B var byte[4]
trisa=0
trisb=0

for counter=0 to 3 'DONT delete!!!!
b[counter]=counter 'its for fill B[] on start up
next counter
'end of startup

a[0]=1000 'CH0 'assign any value between 1000-2000uS->1-2mS
a[1]=1435 'CH1 'in 1uS resolution!
a[2]=1140 'CH2
a[3]=2000 'CH3

'sorting routine (from Melanie)
'B[0] smallest, B[3] biggest
counter=0
SortLoop: 'dont delete this label,its for looping

If a[b[counter]] > a[b[counter+1]] then
Temp=b[counter]
b[counter]=b[counter+1]
b[counter+1]=Temp
If counter>0 then counter=counter-2
endif
counter=counter+1
If counter<3 then goto SortLoop
'end sorting of B index array

'this was 500-725 uS from start,depend a little on
'how much sorting was needed

'next routine will be as long as the biggest ch value
'max 2mS + some uS

'Sendout:
high portb.0 'CH0
high portb.1 'CH1
high portb.2 'CH2
high portb.3 'CH3


for counter=0 to 3
pauseus a[b[counter]]-a[b[counter-1]]
'calc delta times
'if counter=0 -> a[b[0-1]] -> WILL BE 0, TESTED!
'so it'll be: Pauseus a[b[counter]]-0 !
Portb.0(B[counter])=0
'LOW portb# given back from B
next counter

end
---------------------

mrx23
- 18th September 2006, 21:25
Any questions, or ideas?

Darrel Taylor
- 18th September 2006, 22:41
Well, here's one...

What happens with the following array values?

All servo's at same position.
[1000,1000,1000,1000]
<br>

mrx23
- 19th September 2006, 14:14
I've fixed an error in the code.
read comment.

When at least 2 channels have the same value there will
be no problems. Because the sorting cycle just changes the
array when numbers are in wrong place:
example: 1000 1000 1300 1400

current test number:1000
if current number > next number then swap

but 1000 is not > than 1000
so it will not change.

when all ch have the same value:
1000 1000 1000 1000
the B array values will be: [0,1,2,3]

------------------
'startup
counter var BYTE
Temp var byte
A var WORD[4]
B var byte[4]
trisa=0
trisb=0

for counter=0 to 3 'DONT delete!!!!
b[counter]=counter 'its for fill B[] on start up
next counter
'end of startup

a[0]=1000 'CH0 RB0 'assign any value between ~1000-2000uS->~1-2mS
a[1]=1430 'CH1 RB1 'in 1uS resolution!
a[2]=1321 'CH2 RB2
a[3]=1000 'CH3 RB3

'sorting routine (from Melanie)
'B[0] smallest, B[3] biggest
counter=0
SortLoop: 'dont delete this label,its for looping

If a[b[counter]] > a[b[counter+1]] then
Temp=b[counter]
b[counter]=b[counter+1]
b[counter+1]=Temp
If counter>0 then counter=counter-2
endif
counter=counter+1
If counter<3 then goto SortLoop
'end sorting of B index array

'this was 500-725 uS from start,depend a little on
'how much sorting (swapping) was needed

'next routine will be as long as the biggest ch value
'max 2mS + some uS

'Sendout:
high portb.0 'CH0
high portb.1 'CH1
high portb.2 'CH2
high portb.3 'CH3


pauseus a[b[0]] 'for the lowest ch, not for fix ch!
Portb.0(B[0])=0 'dont change!

'the above 2 lines cant be inserted into the For cycle,
'because it will couse for ever loops some times.
'the reason is counter-1 @counter=0, some times will not be 0 (maybe 255).
for counter=1 to 3 'for more channel replace 3

pauseus a[b[counter]]-a[b[counter-1]]
'calc delta times
Portb.0(B[counter])=0
'LOW portb# given back from B
next counter
'you need to redesign this send out routine
'when you reach the max portb port number (1-7)
'simply copy another FOR cycle
'for Porta like: For counter 1 to 7
end

---------------