Driving servos with analogWrite

Status
Not open for further replies.

leafario

Member
I am using the Servo library currently, but I want to drive ESCs at 400Hz. Several libraries exist in varying degrees of completion, I could also use the timers myself, but is there something wrong with the following approach:

Code:
static const int OUT_PIN = 23;

const float LOWER_LIMIT = 3276.8; // 2^16 / 100 * 5 = 5 % duty cycle
const float UPPER_LIMIT = 6553.6; // 2^16 / 100 *10 = 10% duty cycle

int main() {
  analogWriteResolution(16);
  pinMode(OUT_PIN, OUTPUT);

  analogWriteFrequency(OUT_PIN, 400);

    while(true) {
        for (float value = LOWER_LIMIT; value < UPPER_LIMIT; value += 1) {
            analogWrite(OUT_PIN, value);
            delay(10);
        }
    }
}

Looking forward to opinions (I mean facts) and thanks for you help.
 
Yes, that should work. It's basically the same thing the PWMServo library does.

Quoting from https://www.pjrc.com/teensy/td_libs_Servo.html:
PWMServo on ARM-based Teensy slows the PWM to only 50 Hz for a group of PWM pins controlled by the same timer. You can still use those other PWM pins with analogWrite(), but the 50 Hz PWM frequency is too low for many applications.

I was under the impression that PMServo also only supports 50Hz PWM frequency. That seems to be wrong, now I think that PWM frequency of the pins controlled by the same timer is always equal. Confusion ensues.
 
Yes the PWM servo library is setup for 50hz as most RC servos are setup to handle being updated 50 times a second with the pulse width between 1-2ms centered at 1.5ms 1500us

Really not sure what you are trying to do at 400hz... i.e. what the actual valid pulse widths and the like. From your code it sounds like the pulse widths you are generating range from
125us to 250us. The question is what are you seeing and what is it that your device is actually expecting?
 
Is there any sort of authoritative list of which servo motors actually accept pulse faster rates? (or is a matter of just trying faster rates with your motor and seeing whether or not it actually works at all?)

Is there any confirmation that they still expect the same ~0.5 to 1.5 ms pulse width, just repeated faster? All the info I can see seems to say the pulse width should be identical, just sent more often.

And to complete my list of questions.... do those motors actually *do* anything mechanically useful in less than the normal 20 ms pulse repeat time?
 
I'm asking partly because this question comes up occasionally....

I honestly don't know about these particular motors, nor can I find any info that looks reliable.

And also because I *might* edit PWMServo to offer this feature. Maybe. Already written & well tested code could make this long-term much easier for everyone.

But first I'd really like to have at least a couple links to solid info about which motors accept this and exactly what they really do.
 
I'm asking partly because this question comes up occasionally....

I honestly don't know about these particular motors, nor can I find any info that looks reliable.

And also because I *might* edit PWMServo to offer this feature. Maybe. Already written & well tested code could make this long-term much easier for everyone.

But first I'd really like to have at least a couple links to solid info about which motors accept this and exactly what they really do.

Hi Paul,

I'm not sure about ESCs, mostly because I've been involved with fixed wing aircraft and UAVs so far. For typical servos, you're correct that most expect a 50 Hz update with a 1-2 ms pulse width. I have not seen an authoritative list of servos accepting faster rates, but for example, the Futaba 9257 is a 1520 ms centered servo with update rates up to 333 Hz. The Futaba 9256 is a 760 ms centered servo with update rates up to 560 Hz. If I recall correctly, 333 Hz is about the fastest you'll get with a 1-2 ms (1520 ms centered) servo. I've seen manufacturers spec up to 800 Hz with a 760 ms centered servo.

We've measured the bandwidths of several of these servos for use in a UAV with flexible wings. While most manufacturers don't take advantage of the higher frequency updates, the Futaba tail rotor servos perform well. For example, the Futaba 9256 has a bandwidth around 25 Hz and the Futaba 471 has a bandwidth around 40 Hz. To achieve those bandwidths, you definitely need to be using the higher frequency update rates.

One last thing to consider, often latency is extremely important as well. You typically get 1 frame of latency at the servo end (1 full frame between the servo receiving the PWM signal and actually applying a torque to its motor). We've also noticed that the PWM generation on the Teensy side isn't directly tied to updating the analogWrite (i.e. the pulse width doesn't update immediately), so you normally have 1 frame of latency on the Teensy end as well. With the higher update rates, at least the latency time is reduced since the frame periods are shorter.

Brian
 
Is there any sort of authoritative list of which servo motors actually accept pulse faster rates? (or is a matter of just trying faster rates with your motor and seeing whether or not it actually works at all?)

Is there any confirmation that they still expect the same ~0.5 to 1.5 ms pulse width, just repeated faster? All the info I can see seems to say the pulse width should be identical, just sent more often.

It is indeed hard to find information on this. However this is not so much about servos, but more about ESCs. In the multicopter world, 50Hz PWM is too slow for most people (after all, if my flight loop runs at 1kHz, which is a good speed for a mini racecopter, then the esc only ever receives new data every 20 loops roughly). All my ESCs understand a PWM signal of 400 Hz. Here is some explanation: Oscar Liangs blog about ESC protocols and more in detail about the oneshot125 protocol. Note that the distinction between PPM and PWM is generally not clear in the RC world. The latter post mentions a duty cycle of 125-250ms, which is (what KurtE mentioned) the duty cycle produced by above code.

I realize now that information about this is really hard to find. 50Hz is definitely a good value for servos. In higher frequency PWM, generally the duty cycle must be still between 5% and 10%. Otherwise, sending the same signal as part of a PPM transmission would not be possible.

EDIT: I inquired in another forum about 'PWM standards' and will do some more testing tonight.
 
Last edited:
UPDATE!

Turns out I was wrong. Paul was right of course. The ESC wants a PWM signal with the same 1ms to 2ms pulse width, but you can send that signal faster.

Here is some code to send a 400Hz signal with a 1ms to 2ms pulse width:

Code:
const float LOWER_LIMIT = 655.36f * 40; // 40% duty cycle of 2.5ms signal is 1 ms
const float UPPER_LIMIT = 655.36f * 80; // 80% duty cycle of 2.5ms signal is 2 ms
const float RANGE = UPPER_LIMIT - LOWER_LIMIT;

#define OUT_PIN 6

void write(float throttle) {
    uint16_t value = LOWER_LIMIT + RANGE * throttle;
    analogWrite(OUT_PIN, value);
}

int main() {
    analogWriteResolution(16);
    pinMode(OUT_PIN, OUTPUT);

    analogWriteFrequency(OUT_PIN, 400);

    while (true) {
        for (float value = 0; value < 1; value += 0.001) {
            write(value);
            delay(10);
        }
}
 
Any links to highly reliable info or specs about these servo motors? Could come in handy next time someone asks....

I have looked for a while, but nothing has cropped up. At this point, this information is anecdotal at best. That is a bad situation on the side of the ESC manufacturers... especially since some older analog servos seem to break when driven with a high frequency pwm signal intended for an ESC.
 
Just for reference in case someone comes along looking for this, some code that accepts arbitrary parameters for frequency and pulse width min/max:
Code:
static const int OUT_PIN = 6;

static const uint8_t RESOLUTION_BITS = 16;
static const float FREQUENCY_HZ = 400.0f;
static const float WAVELENGTH_SEC = 1.0f/FREQUENCY_HZ;

static const float MIN_PULSE_WIDTH_SEC = 0.001f;
static const float MAX_PULSE_WIDTH_SEC = 0.002f;

static const uint8_t MIN_DUTYCYCLE_PERCENT = MIN_PULSE_WIDTH_SEC/WAVELENGTH_SEC * 100;
static const uint8_t MAX_DUTYCYCLE_PERCENT = MAX_PULSE_WIDTH_SEC/WAVELENGTH_SEC * 100;

static const uint16_t LOWER_LIMIT = (1 << RESOLUTION_BITS) / 100 * MIN_DUTYCYCLE_PERCENT;
static const uint16_t UPPER_LIMIT = (1 << RESOLUTION_BITS) / 100 * MAX_DUTYCYCLE_PERCENT;

static const uint16_t RANGE = UPPER_LIMIT - LOWER_LIMIT;

void write(float throttle) {
    uint16_t value = LOWER_LIMIT + RANGE * throttle;
    analogWrite(OUT_PIN, value);
}

void setup() {
    analogWriteResolution(RESOLUTION_BITS);
    pinMode(OUT_PIN, OUTPUT);

    analogWriteFrequency(OUT_PIN, FREQUENCY_HZ);
}

void loop() {
    for (float throttle = 0.0f; throttle < 1.0f; throttle += 0.001f) {
        write(throttle);
        delay(10);
    }
}

It drives my ESC happily at 400Hz.
 
Status
Not open for further replies.
Back
Top