In the quad app, we are using a legacy unit of 1e-8 seconds to represent the PWM pulse width. This is somewhat platform dependent and just not very helpful at the application layer. We should replace this unit with something like Duty cycle, a float from 0 to 1.
The following places will likely need to be updated
the hardware/application interface
the control algorithm uses this unit, so all control parameters will need to be updated to use the new unit
Edit
Instead of duty cycle, use a normalized value from [0,1] to represent the active region of the ESCs (1ms-2ms pulse width). In our specific case, we are operating at a frequency of 450 Hz, so our duty cycle would range from 40%-80% in the hardware layer.
Designs
Child items
...
Show closed items
Linked items
0
Link issues together to show that they're related.
Learn more.
brendan [5:06 PM]
In the process of fixing a unit in the quad code, a piece of me would like to just fix the unit to something we all like. From the model perspective, would you guys prefer to work with duty cycle, or does it really not matter? FYI @david
andy [6:36 PM]
I don't know what is best honestly. As far as I know (and feel free to correct me if I am wrong), ESCs typically operate between the 1ms to 2ms pulse range. This corresponds to a duty cycle of 40% to 80% given the frequency we are running at. To me it makes more sense to have work with the pulse range, as that doesn't typically change even if the ESCs get upgraded. Working with duty cycles means that if sometime in the future they want to run at a different frequency, some values in the model will have to be changed...
brendan [7:12 PM]
That makes sense to me. The issue is that we’d like to keep the quad application decoupled from a specific hardware implementation. We can keep hardware specific stuff in a relatively thin driver layer. Keeping that level of platform agnosticism for our app keeps it maximally compatible with any system it might find itself running on. So I guess I’m trying to shoot for what the most inter-platform constant variable would be. At first, I thought that would be duty cycle (isn’t pulse width frequency dependent?)
andy [7:25 PM]
If you want the quad application decoupled from hardware then duty cycle is probably the best way to do that. I am confused partially though, as I thought the goal of this "quad" application was to be specific to quad-rotors. Most quad-rotor applications utilize an ESC to drive their motors, and most ESCs as I said before operate between the 1ms to 2ms pulse range. I don't know how generalized you want this application to be, but if you truly want it to be decoupled from hardware, duty cycle is the best route.
With regards to pulse width being frequency dependent, you are correct it is, however the ESCs only run between that specific pulse range and so it's a baseline I can use within the model. However, I just realized that either way something will have to change if future teams want to change the frequency we run at, whether it be on the model side or quad side.
[7:28]
Ultimately, whatever you decide is just a couple of values in our "modelParameters.m" file, which should be documented well enough that future teams can use it, so don't worry to much about how it effects the model.
brendan [7:40 PM]
We’re not shooting for generalization, just the most inter-platform constant variable. Judging by your comments, it sounds like that variable might actually be the pulse width? So for instance, if I changed to a faster frequency, then my duty cycle range would necessary increase (say from 35%-85%), so that the pulse width is still 1ms-2ms?
emceerich [7:50 PM]
Won't you always need (1) frequency / period, (2) duty cycle % / pulse width (3) minimum maximum for either ?
[7:52]
I think the pulse width time, and min max pulse are probably more absolute and need no conversion when trying to verify on a scope or implement in hardware***
[7:52]
*** get imciner2 to confirm
brendan [8:06 PM]
Yep, your listed requirements are correct, but now that we’ve separated the application code from the platform code, we can be much more abstract at the application layer, and keep hardware specific details at the platform layer. So if there’s a more general variable that works across many types of platforms, I would chose that variable.
[8:09]
Doing a quick online, it seems @andy’s 1ms-2ms range for ESCs is pretty standard, so I’m of the impression that duty cycle would not be a good variable.
brendan [8:18 PM]
Here’s one more consideration, how does a normalized unit sound for the application layer? So 0 for min and 1 for max. And then, of course, we let the hardware layer be concerned with converting it to the appropriate pulse width. (edited)
emceerich [8:19 PM]
That may actually be best
andy [8:20 PM]
Yeah, if you are just targeting the application layer, then making it as simple as that makes most sense.
brendan [8:26 PM]
@david It seems there is consensus on a [0,1] normalized PWM value. Any objections/comments?
@phjones We are considering moving away from using the pulse width value inside our quad application, and using a normalized [0,1] pwm value throughout the application (which includes the control algorithm computation). We would incorporate the appropriate conversions to pulse width inside the hardware layer.
bbartelschanged title from Use Duty Cycle over PWM ticks in Application to Use a normalized PWM value
changed title from Use Duty Cycle over PWM ticks in Application to Use a normalized PWM value
structPWMInputDriver{void*state;int(*read)(structPWMInputDriver*self,intchannel,long*pulse_width_ns);// pulse with ranges from 1-2ms}structPWMOutputDriver{void*state;int(*write)(structPWMOutputDriver*self,intchannel,longpulse_width_ns);// pulse width ranges from 1-2ms};
Proposed:
structRCInputDriver{void*state;int(*read)(structRCInputDriver*self,intchannel,float*val);// val ranges from 0 to 1}structMotorDriver{void*state;int(*write)(structMotorDriver*self,intchannel,floatval);// val ranges from 0 to 1};
@dawehr@snawerdt@tymina I'm struggling to come up with something more meaningful than val. Anything come to mind?
@dawehr@snawerdt@tymina@bbartels : I would call val, duty_cycle, as from what I've read this appears to be a normalized duty cycle. So if I'm under standing correctly, the platform specific MotorDriver software will be responsible for converting this normalized duty cycle into timer ticks. It should be noted that the platform specific software needs to know at what clock frequency the hardware PWM generator is running. I'm guessing such information will be (is) being provided has something like #define PWM_TIMER_FREQ or PWM_TIMER_TICK_PERIOD_NS. Then the commands sent to the PWM hardware will be something like
// convert PWM period in ns to number of PWM timer ticks.configure_PWM_period=PWM_PERIOD_NS/PWM_TIMER_TICK_PERIOD_NS;// convert normalized duty cycle to number of PWM timer ticksconfigure_PWM_duty_cycle=(PWM_PERIOD_NS*duty_cycle)/PWM_TIMER_TICK_PERIOD_NS;
Example defines, assuming a PWM timer frequency of 100 MHz, which gives a PWM_TIMER_TICK_PERIOD_NS of 10 ns.
#define PWM_TIMER_TICK_PERIOD_NS 10#define PWM_PERIOD_NS 2,000,000 // 2ms is 2 million nanoseconds
@dawehr@snawerdt@tymina@bbartels : Hmmm... now that I think a little more about it, calling val something like norm_cmd may make more sense as it is the normalized value of the command that one wants to send and not necessary a duty cycle. For example, having 0 to 1 map to 1ms to 2ms is not exactly specifying a duty cycle. Updated example:
// convert PWM period in ns to number of PWM timer ticks.configure_PWM_period=PWM_PERIOD_NS/PWM_TIMER_TICK_PERIOD_NS;// convert normalized command to number of PWM timer ticksconfigure_PWM_duty_cycle=((PWM_DUTY_CYCLE_RANGE_NS*norm_cmd)+PWM_DUTY_CYCLE_OFFSET_NS)/PWM_TIMER_TICK_PERIOD_NS;
Example defines, assuming a PWM timer frequency of 100 MHz, which gives a PWM_TIMER_TICK_PERIOD_NS of 10 ns.
#define PWM_TIMER_TICK_PERIOD_NS 10 // 10 ns PWM tick period#define PWM_PERIOD_NS 2,000,000 // 2ms is 2 million nanoseconds#define PWM_DUTY_CYCLE_RANGE_NS 1,000,000 // 2ms - 1ms = 1ms is 1,000,000 ns: Duty cycle (pulse width) for our ESCs need to be between 1ms to 2ms. Which gives a range of 1ms.#define PWM_DUTY_CYCLE_OFFSET_NS 1,000,000 // 1ms offset as the minimum PWM pulse width is 1ms, which is 1,000,000 ns.
I'm think norm_cmd makes sense for motors, since we could say the application issues commands as outputs to the motors, but it doesn't feel as appropriate for rc receiver, since those are more inputs, and semantically mixing "command" as both an input and output feels a little confusing to me.
I'm currently considering norm_magnitude, which seems to achieve what I'm shooting for.
Using norm_magnitude, I think would be OK. With respect to norm_cmd, the PWM signals coming in from the RC controller are also commands. They are commands for pitch/roll/yaw, or in some configuration of the controller x-velocity, y-velocity commands. It is pretty common to view PWM signals as commands as they are typically used a way to communicate actions to peripherals. In terms of input/output with respect to the quad, you could call motor commands norm_output_cmd, and for the RC controller norm_input_cmd.