How are pwm values set after controllers outputs are calculated for quadrotors?

Hi, I am trying to understand what happens after ‘mc_att_control_main.cpp’ is executed to set pwm values for the motors. I see that controller output values are published

I am familiar with the general modeling and control of quadrotors and I know the need to use the mixer matrix. But I am not sure how this is done in the PX4 Firmware. For the moment I think that in ‘fmu.cpp’ the mixer is called in line 1260 and then the function ‘pwm_limit_calc’ is called in line 1287.

But if this is correct, I also have some questions about the different scales and limits that are implemented.

For exemple, it seem that in pwm_limit_calc the outputs (control_value = output) are expected to be from -1 to 1

But then in ‘mixer_multirotor.cpp’ output is limited between and ‘_idle_speed’ and 1, but I could not find where this idle speed is set and if it can be set to -1, (before this line of the code, it seems that output values are limited from 0 to 1).

I would appreciate if someone could clarify this process, confirm or correct what I’ve said.


1 Like

As far as i know idle_speed is set in the mixer file. See -> Multirotor Mixer -> deadband
For example the quad_x mixer file:

And _idle_speed is calculated in “mixer_multirotor.cpp”. When the deadband is set 0 in the mixer file _idle_speed is -1.0f:

You’re right, until line 364 in mixer_multirotor.cpp the output values are limited from 0 to 1. But in line 364 they get limited between _idle_speed (most of the time -1) and 1.


Where do we see the transition from _actuators to outputs?

Is there an explicit statement somewhere in the codebase that says: outputs = _actuators?

Hi, Jose, the process from _actuators to the final PWM outputs is very complicated and not to easy to understand.

So it is not easy to describe it as a simple statements for that. To understand it several hours may be needed or several days or months.

Here is some information for the journey:

The mc_att_control() outputs the target values (-1 to 1) for the torques and thrust of body.

These values are transferred to fmu.cpp if Pixhawk has only FMU board or to px4iofirmware/mixer.cpp if Pixhawk has IO board with FMU board. Normally Pixhawk has a IO board. In this normal Pixhawk, FMU board is responsible for AUX PWM output pins and IO board is for MAIN PWM pins.

But this transfer is not easy to understand. It is related driver software and registers. And between FMU board and IO board they seems to communicate via I2C.

The mix() function transforms the values of _actuators to throttle values of motors (-1 to 1). The mixer classes and functions are somewhat hard to understand it.

The pwm_limit_calc() function transforms the throttle values of motors to the PWM values (normally 1000 to 2000 micro seconds) of ESC.


Thanks bigbellmercy! The logic paths you have described are basically what I have tracked down. But I am seeking a in-depth working level understanding. this code base is used to control flying machines, and it is important (and a very responsible thing to do so) for all designers to understand its most fundamental (not just top-level) workings. Taking hours/months/days to understand does not seem to inspire confidence in using this system (we have developed full GNC systems from scratch within that frame, so that defeats the purpose of using px4),

Welcome Jose, it is great that you developed it on your own!

What I was getting at is that the detailed logics should not be very hard to parse and understand. Do you know in detail how this command to pwm works? To the extent that you are able to simulate it in Matlab for the purpose of algorithm evaluation and Algorithm-in-the-Loop 6DOF analysis?

1 Like

This was a highly amusing thread. I’ve bookmarked it for future reference.

I am glad you found it amusing larry_c, any insight into this?

I think the phrase “The code always reveals the truth” may apply here.

Affirmative, that is where I got stuck, to track down the location in which the _actuators gets translated into PWM duty cycle command. Do you know which code contains that?

1 Like

Refer to the @bigbellmercy thread above - that’s a very complete description. The last step of sending the PWM to the ESC is in the IO Module processor.

If you were to implement an entire FCS in MATLAB or Simulink, sending the values to the IOModule would be the step to send that data to the ESCs.

And between FMU board and IO board they seems to communicate via I2C.

The mix() function transforms the values of _actuators to throttle values of motors (-1 to 1). The mixer classes and functions are somewhat hard to understand it.

The pwm_limit_calc() function transforms the throttle values of motors to the PWM values (normally 1000 to 2000 micro seconds) of ESC.

We’ve hacked the code to completely remove the ESC’s and add in an alternative BLDC system via CAN.

1 Like

Sir how about if I need to add a DC motor to the system just like you add BLDC to the system. I need to control that DC motor speed via a change in the duty cycle ratio of the PWM signal. It seems like you have told us that we if we change the throttle value of motors from the function pwm_limit_calc() (-1 to 1) we will be able to change the PWM values. Does this mean this can vary the duty cycle of the PWM because you know that for DC motor to increase decrease speed we need to manipulate the duty cycle of PWM?
If changing values from -1 to 1 increase decrease the speed of a motor. Then I am confused I one more part please help me to get out of this confusion too. The confusing part is in which function that I need to put values from -1 to 1 wether in pwm_limit_calc() or in Mix() function.
My target is to control the DC motor speed and run the DC motor instead of using ESC and other BLDC’s. As I have been working on this from months now and more it gets into the more trouble and confusion I found which decrease my confidence even more.

There are many parts to what we did, but essentially we removed the PWM calcs and replace it with a torque calculation that’s used to control a BLDC motor via CANOpen in Torque mode.
Im sure you could scale the PWM value to a torque as well, but we derived torque directly and sent it to the motor controller.

@larry_c I am sorry i didn’t got your point. what do you mean by removed the PWM calculations and replace it with torque calculation?
I didn’t know about these things yet. As a starter after reading code for like 3 months now i came to know that actuator_output[ ] function control the output of the pixhawk 4 board. But the when i publish the value to this module it shows the Vp-p 3.3 and with the frequency of 50Hz. But by looking at the signal through oscilloscope the duty cycle of the signal is small. Due to that when i publish the value in (actuator_ourput[1]= 1.0f ; ) it will activate the port 2 in pixhawk hardware and with the value -1.0f to 1.0f the value of the on the oscilloscope remains the same. There is no change on the duty cycle. Due to which my external Dc motor controller neither increase nor decrease the speed of the motor.
Now i want to find a way to through which i can able to change my speed by changing the PWM. one other way i have an idea is, what if i change the value from the RC controller send the throttle signal to the pixhawk 4 and the px4 translate that from -1 to 1 to vary the speed. If i directly change the value and run the code the mavlink simple app example. Then what is that module or topic which i need to feed the value from -1 to 1 and where to feed the value. I am so confused about it. where to find.

@HammadT Hi. I don’t know if you ever got an answer to your questions. If not, this may shed some light on it. I’m working on a quad copter problem and this is what I’ve learned. The method MixingOutput::setAndPublishActuatorOutputs is responsible for publishing the actuator output. This method references a global variable (an array of integers) which is the vector of PWM lengths representing the microseconds of the pulse. These are the PWM lengths generated on the Pixhawk output ports. The global variable is called _current_output_values and this is packed into an actuator_outputs_s structure and published as a uORB message. Change the content of _current_output_values and you will change what PWM signals get generated. What happens next depends on the hardware you are working on. In src/drivers either PX4IO.cpp or fmu.cpp will be used to interact with the hardware a generated the actual PWM signal.

I’ve tried to change in Pikhawk firmware using Qt but I didn’t understand how to do.
Now Im working with simulink. In Mathworks there are non response to quastions about this topic.

My problem is that rotors haven’t the same speed. so I think that I have to change PID parameters in the first part. I don’t know where to find original PID paramaters in order to change them.

In the second part , I didn’t understand the command calculation formula:
idle_PWM = uint16(1000);
% Quad X
M1 = uint16(((Roll + Pitch - Yaw) / 2 * Thrust + Thrust) * single(1000)) + idle_PWM;
M2 = uint16(((-Roll + Pitch + Yaw) / 2 * Thrust + Thrust) * single(1000)) + idle_PWM;
M3 = uint16(((-Roll - Pitch - Yaw) / 2 * Thrust + Thrust) * single(1000)) + idle_PWM;
M4 = uint16(((Roll - Pitch + Yaw) / 2 * Thrust + Thrust) * single(1000)) + idle_PWM;

Thank you in advance.

Hello all,

Where is the fmu.cpp in the current master version of the code? Has it been renamed to PWMOutput.cpp ?