Solved Servo control with frequencies other than 50hz and M280
-
I have a KST DS216MG hobby servo I am trying to control. It has the following basic PWM control specifications:
- Frequency: 333Hz
- Pulse Width Range: 800μs - 2200μs
Other common hobby servos like the SG90 use 50Hz and as stated in the Duet docs various pulse width ranges, usually between 1-2ms. Based on this I setup my servo on my Duet 2 Wifi with Duex 5 connected to PWM 5 and running RRF 3.3 as follows:
M950 S0 C"duex.pwm5" Q333
Using this setup I have not had any luck getting the servo to do anything other that a few random movements using the M280 command regardless of whether I try using the angle or the pulse width variant of the command. To diagnose this further I have put a scope on the PWM signal output to see what happening (excuse my ignorance, I am not an EE).
So with the standard configuration setup to the default 50Hz like so:
M950 S0 C"duex.pwm5"
Then sending the following command to set an explicit pulse width:
M280 P0 S2200
I get the following PWM signal.
So 50Hz is as expected and the pulse width is at 2.2ms/2200μs exactly as expected. If I now change the configuration to include the frequency and send the same command.
M950 S0 C"duex.pwm5" Q333 M280 P0 S2200
I instead get this PWM signal.
So the frequency has definitely come out correctly, however the pulse width is no longer correct. It looks like the pulse width has been divided by the increase in frequency 2200/(333/50) which is what I would expect if RRF isn't factoring in the frequency for the passed in pulse width.
This would be fine if it were possible to multiply the pulse width in the M280 command artificially to compensate, however it looks like the S parameter in the M280 command is limited (correctly, EDIT: looks like it's just hard coded to a limit of 2400 actually) by the configured frequency (i.e., a pulse width of greater than 3000μs makes no sense when using 333Hz of course). So with M280 I don't seem to have any means of producing the correct range of pulse widths for this servo.
However I can get the correct pulse widths if I use the M42 command instead and set a duty cycle corresponding to the pulse width that I want to see. So for example if I have the PWM output configured at 333Hz and send the following command:
M42 P0 S0.73
I then get the following PWM signal.
So now with the correct frequency and pulse width I need. Using that I was then able to determine the duty cycle for the minimum and maximum servo range and move the servo predicably with M42 between those extremes. So I imagine I can use M42 as a work around, however based on these experiments it looks to me like M280 doesn't work as intended for servos running at something other than 50Hz.
I haven't filed a bug since I am not sure if my interpretation of what is happening here is correct given my fairly basic electronics knowledge. Am I missing something obvious or is this an issue with M280?
-
And in what should have been my first step looking at the code for M280 we have the following:
if (angleOrWidth < MinServoPulseWidth) { // User gave an angle so convert it to a pulse width in microseconds angleOrWidth = (min<float>(angleOrWidth, 180.0) * ((MaxServoPulseWidth - MinServoPulseWidth) / 180.0)) + MinServoPulseWidth; } else if (angleOrWidth > MaxServoPulseWidth) { angleOrWidth = MaxServoPulseWidth; } float pwm = angleOrWidth * (ServoRefreshFrequency/1e6); if (invert) { pwm = 1.0 - pwm; } IoPort::WriteAnalog(servoPin, pwm, ServoRefreshFrequency); ...
And
ServoRefreshFrequency
is hard coded to50
insrc/Configuration.h
andMaxServoPulseWidth
is set to2400
insrc/GCodes/GCodes.h
. So this explains why I see no effect setting the pulse width above2400
. So in the above code it looks like the frequency is backed out of thepwm
value to account for it being passed in to theWriteAnalog
call.Nowhere in this code is the pins configured PWM frequency taken into account, not sure if it happens deeper in
IoPort::WriteAnalog
or not, that seems to have two variants, one for expansion and one for regular and the expansion one ignores the frequency (so not sure if my issue is specific to using the Duex5). -
Doesn’t look like anyone else is having this issue. I’m trying to work out whether to report a bug or not since reading several other issues on Github people are directed to seek support here on the forum first to establish if it is an issue or just not understanding the way something is intended to work.
-
-
@fcwilt no it does not seem to. It just makes some random movements with various commands, presumably when the signal aligns to something it understands.
I may try again now that I know more about the actual signal that is being output since it does seem to work on an Arduino with the normal Servo library which is strange since that presumably also assumes 50Hz.
-
@ardenpm are you sure that 333Hz isn't the maximum frequency accepted by that servo?
I'll look into supporting different PWM frequencies in M280.
-
@dc42 definitely not sure given how sketchy the spec sheets are for these types of servos. However it is sold as a ‘digital’ servo rather than an ‘analog’ servo.
I wasn’t aware of the distinction previously but from what I understand the digital servos typically do use a higher frequency signal and update their position more frequently.
That’s going on chatter on FPV and model plane forums which seems to be where they get the most use. Their controllers seem to require you to specify the frequency.
I’ll do some more experiments and see if I can get it to do anything useful at 50Hz, however it definitely worked well at 333Hz once I got it sending the pulse widths in the spec sheet with M42 where as at 50Hz I was only able to get random motions.
Frequency support for M280 would be great though.
-
@ardenpm said in Servo control with frequencies other than 50hz and M280:
Frequency support for M280 would be great though.
I have now implemented and tested this.
-
@dc42 Wow, fast work! Just took a quick look at the code in the 3.4-dev branch, looking forward to testing it out when ready in a build.
-
Just for prosperity if others come across this thread in the future, I finally got around to updating to RRF 3.4.x and the implemented change works great. I can now correctly define the servo with the needed PWM frequency like so:
M950 S0 C”duex.pwm5” Q333
And then move it with the servo commands like so:
M280 P0 S45 ; set servo position to 45 degrees
And it moves a sensible amount as expected.
-
-