Driving more than 4 servos
Driving more than 4 servos
Hi!
I plan to build a little hexapod, first using cheap 9G servos for the prototype, before switching to the new Dynamixel XL-320 servos (chained serial connection).
Is there a way to drive more than 4 servos (using Servo class)? A 3DOF hexapod will need 24 servos...
Thanks.
I plan to build a little hexapod, first using cheap 9G servos for the prototype, before switching to the new Dynamixel XL-320 servos (chained serial connection).
Is there a way to drive more than 4 servos (using Servo class)? A 3DOF hexapod will need 24 servos...
Thanks.
Frédéric
Re: Driving more than 4 servos
I just realized I wasn't thinking properly when I first read your message. Short version: as such most likely not, however one could try using 595s over SPI (choose some high-speed version and you can run the SPI clock at several MHz) and timer trickery to send out new data every time a pulse needs to end (they can all start at the same time), or put smd-attiny (or similar cheap&small mcu) next to each servo to generate the analog signals and use I2C as the bus. Openservo looks way too sophisticated (and expensive) for this.
Below what I first wrote, just in case someone else needs a bit of background info:
I'm fairly sure the Servo class is for "analog" servos which need 1000us to 2000us pulse every 20ms. To complicate matters there are "digital" servos which take this same analog pulse at up to 200Hz, and some that take shorter pulses at even higher rate. Then there are the Dynamixels and their kind (every name-brand traditional RC-servo maker has their own proprietary bus as well) which use addressable serial bus.
For the serial bus you're not limited by the amount of timers that can be dedicated to generating the pulses, so write your own DynamixelServo class and you're basically limited by RAM if you want an instance for each servo. On the other extreme just change the address on a single instance as needed.
Below what I first wrote, just in case someone else needs a bit of background info:
I'm fairly sure the Servo class is for "analog" servos which need 1000us to 2000us pulse every 20ms. To complicate matters there are "digital" servos which take this same analog pulse at up to 200Hz, and some that take shorter pulses at even higher rate. Then there are the Dynamixels and their kind (every name-brand traditional RC-servo maker has their own proprietary bus as well) which use addressable serial bus.
For the serial bus you're not limited by the amount of timers that can be dedicated to generating the pulses, so write your own DynamixelServo class and you're basically limited by RAM if you want an instance for each servo. On the other extreme just change the address on a single instance as needed.
Re: Driving more than 4 servos
Yes, I mean analog servos. I would like to make a low cost prototype, before switching to Dynamixel.
Do you think it is possible to drive 24 servos using simple GPIO, set in an interrupt routine? The idea is to register a function within a timer interrupt, and there, turn on/off each pin to emulate the Servo class...
Do you think it is possible to drive 24 servos using simple GPIO, set in an interrupt routine? The idea is to register a function within a timer interrupt, and there, turn on/off each pin to emulate the Servo class...
Frédéric
Re: Driving more than 4 servos
I think that you should be able to drive about 12 servos using a single timer. So provided you have the GPIOs, driving 24 servos should just require 2 timers. The teensy code uses a single timer to drive upto 12 servos.
If you add external shift registers then you can dramatically reduce the number of GPIOs required.
Using external shift registers, you should be able to drive 12-16 servos using 2 GPIO lines.
If you're interested, I have C libraries for talking to Dynamexils and a Python one (although it was coded for full python and uses pyserial). You can find my python code here:
https://github.com/dhylands/Bioloid
This includes a command line utility for talking to the servos as well.
The code also implements a command line interface on top of the lower level libraries for talking to the dynamexils. It's buried in a few places in this repository:
The command line stuff is here:
https://github.com/dhylands/projects/tr ... ioloid/cli
and if you look in the Makefile you'll see the names of all of the source files needed, some of which are elsewhere in the same repository.
If you add external shift registers then you can dramatically reduce the number of GPIOs required.
Using external shift registers, you should be able to drive 12-16 servos using 2 GPIO lines.
If you're interested, I have C libraries for talking to Dynamexils and a Python one (although it was coded for full python and uses pyserial). You can find my python code here:
https://github.com/dhylands/Bioloid
This includes a command line utility for talking to the servos as well.
The code also implements a command line interface on top of the lower level libraries for talking to the dynamexils. It's buried in a few places in this repository:
The command line stuff is here:
https://github.com/dhylands/projects/tr ... ioloid/cli
and if you look in the Makefile you'll see the names of all of the source files needed, some of which are elsewhere in the same repository.
Re: Driving more than 4 servos
Could you explain a little bit how it is possible to drive 12 servos with only 1 timer? Do you mean using timer outputs (don't they have only 4 outputs each?), or using a software way?dhylands wrote:I think that you should be able to drive about 12 servos using a single timer. So provided you have the GPIOs, driving 24 servos should just require 2 timers. The teensy code uses a single timer to drive upto 12 servos.
Frédéric
Re: Driving more than 4 servos
Basically, you need to know the required pulse with for each servo.
You use GPIOs to drive the servos, and the timer to drive your code.
Basically, you have a state machine that is either processing a pulse for a particular servo, or you're processing the refresh interval.
So lets say, you have 3 servos, with pulse widths of 1.3, 2.0, and 1.8 msec each. And that the refresh interval is 20 msec.
So you would start things off by bringing servo 1's gpio high, and setting the timer for 1.3 msec.
When the timer expires, you set servo 1's gpio low, set servo 2's gpio high, and set the timer for 2.0 msec
When the timer expires, you set servo 2's gpio low, set servo 3's gpio high and set the timer for 1.8 msec
When the timer expires, you set servo 3's gpio low, and you've finished processing the servos, so now you calculate the refresh interval.
1.3 + 2.0 + 1.8 = 5.1 msec. We've got a refresh interval of 20 msec, and only 5.1 msec has gone by, so we now wait for 14.9 msec.
And then repeat.
By sequencing the servos (and not trying to process them all at once) you get consistent pulse widths.
The big thing is being able to program the timer to go off some time in the future and generate an interrupt when that happens. This interrupt handler is what drives the whole thing. In the teensy code, that ISR is here: https://github.com/dhylands/micropython ... rvo.c#L224 but its using totally different hardware from the pyboard.
However, the logic should be mostly the same.
If you were to use an external shift register, you basically just shift a 1 though the shift register, generating a rising and falling clock edge on each timer interrupt.
You use GPIOs to drive the servos, and the timer to drive your code.
Basically, you have a state machine that is either processing a pulse for a particular servo, or you're processing the refresh interval.
So lets say, you have 3 servos, with pulse widths of 1.3, 2.0, and 1.8 msec each. And that the refresh interval is 20 msec.
So you would start things off by bringing servo 1's gpio high, and setting the timer for 1.3 msec.
When the timer expires, you set servo 1's gpio low, set servo 2's gpio high, and set the timer for 2.0 msec
When the timer expires, you set servo 2's gpio low, set servo 3's gpio high and set the timer for 1.8 msec
When the timer expires, you set servo 3's gpio low, and you've finished processing the servos, so now you calculate the refresh interval.
1.3 + 2.0 + 1.8 = 5.1 msec. We've got a refresh interval of 20 msec, and only 5.1 msec has gone by, so we now wait for 14.9 msec.
And then repeat.
By sequencing the servos (and not trying to process them all at once) you get consistent pulse widths.
The big thing is being able to program the timer to go off some time in the future and generate an interrupt when that happens. This interrupt handler is what drives the whole thing. In the teensy code, that ISR is here: https://github.com/dhylands/micropython ... rvo.c#L224 but its using totally different hardware from the pyboard.
However, the logic should be mostly the same.
If you were to use an external shift register, you basically just shift a 1 though the shift register, generating a rising and falling clock edge on each timer interrupt.
Re: Driving more than 4 servos
I have another question related to servos...
Do you think it is possible to drive a micropython board using radio control (RC) servo pulses signal? The idea would be to plug pyboard inputs to standard RC receiver outputs...
I was thinking to use the ExtInt class, to register a callback, and, in the same time, use a Timer as precise counter (µs) to measure the pulses. But I don't know if it will be fast enough.
All this in // with the software servos control as described above...
Thanks.
Do you think it is possible to drive a micropython board using radio control (RC) servo pulses signal? The idea would be to plug pyboard inputs to standard RC receiver outputs...
I was thinking to use the ExtInt class, to register a callback, and, in the same time, use a Timer as precise counter (µs) to measure the pulses. But I don't know if it will be fast enough.
All this in // with the software servos control as described above...
Thanks.
Frédéric
Re: Driving more than 4 servos
oh yeah - that should definitely be possible, and is on my todo list. I've done it on a 16 MHz AVR. See: http://docwiki.gumstix.org/index.php?ti ... x_RC_input using pin change interrupts.fma wrote:I have another question related to servos...
Do you think it is possible to drive a micropython board using radio control (RC) servo pulses signal? The idea would be to plug pyboard inputs to standard RC receiver outputs...
I was thinking to use the ExtInt class, to register a callback, and, in the same time, use a Timer as precise counter (µs) to measure the pulses. But I don't know if it will be fast enough.
All this in // with the software servos control as described above...
Thanks.
I did a mod to my RC receiver to get all the channels on a single line. The VEX receiver also presents a single signal line. Capturing individual channels should also be possible.
You could use EXTINT, but I wanted to use the HW to capture timestamps on the edges. That makes it more accurate and less susceptible to jitter caused by the EXTINT irq handler being delayed by some other irq. I like using the HW whenever I can.