Often when people design multirotors, they find it tricky to figure out what components would be a good choice for their desired mission. Typically after a few hours of scrolling through Hobbyking or another online store they come to what they think is a good fit, where everything has enough power to fly and is not too heavy. Though let's be honest, this process usually just involves copying what other people have done, picking something that “looks right”, or just plain guessing. The problem is that this can leave your designs inflexible and suboptimal. This is a tutorial about using optimization techniques to come up with smarter designs that are flexible and squeeze the most out of your componentry choices. It’s a coding tutorial, so you need to have basic skills in Python to follow along, but if you don’t, just read through and use the finished script I provide at the end.
Software that you'll need:

GPkit (this is a Python package for performing optimization)
Make sure GPkit has installed correctly and is working well by using some of the examples they have provided. It can be a tricky install, but it's worth it once you have it all working.
To get started, open up your favourite text editor and make a file named opt.py. The first thing you want to do is import the package that we are using to do the optimization, called GPkit.
from gpkit import Variable, Model, units
Now we can set up a simple physicsbased model for a multirotor. This model can be used for any basic design objective such as maneuverability or payload capacity. The first characteristic of a multirotor is its number of rotors, i.e. is it a quad, tricopter, octocopter etc. We’ll go with a quadrotor here.
# Configuration n_rotors = Variable('n_rotors',4,'')
We can then take a guess at the payload and structure mass.
payload_mass = Variable('payload_mass',0.1,'kg') structures_mass = Variable('structures_mass',0.2,'kg')
Next we need to model the battery, which is usually a large fraction of the mass. To get a reasonable value for the battery specific energy, or amount of energy it can hold per unit mass, I took the average of some random batteries off Hobbyking. The eX notation just means *10^X, it’s another way of saying scientific notation that’s much easier to code.
# Battery battery_mass = Variable('battery_mass','kg') battery_energy = Variable('battery_energy','J') battery_specificEnergy = Variable('battery_specificEnergy',2.98e5,'J/kg')
The next step in the power chain between the battery and thrust is the electronic speed controller or ESC, which we model in the same way as the battery though instead of specific energy we use specific power.
# ESC esc_mass = Variable('esc_mass','kg') esc_power = Variable('esc_power','W') esc_specificPower = Variable('esc_specificPower',500,'W/kg')
Same thing for the motor.
# Motor motor_mass = Variable('motor_mass','kg') motor_power = Variable('motor_power','W') motor_specificPower = Variable('motor_specificPower',3007,'W/kg')
Ignoring things like RPM matching, a simple model of a propeller is just its diameter and efficiency. We’ll fix the diameter, for reasons that’ll become obvious later.
# Propeller propeller_d = Variable('propeller_d',0.1,'m') propeller_efficiency = Variable('propeller_efficiency',0.9,'')
Now we need to ensure that our design will actually fly, as well as perform well, so we make some variables to track totals and performance.
# Performance variables t_hover = Variable('t_hover','s') T_total = Variable('T_total','N') P_total = Variable('P_total','W') m_total = Variable('m_total','kg')
Add in the air density and gravity at sea level for planet Earth, where I assume you’ll be flying.
# Environment rho = Variable('rho',1.225,'kg/m^3') g = Variable('g',9.8,'m/s/s')
So that’s all of our variables for the model. Now we can use constraints to add in the physics. We’re going to put all of these constraints in an array to be used by the solver, so I’ll just explain them one by one and then give a code block at the end that has them in an array. Let’s look at our first constraint.
battery_energy <= battery_specificEnergy*battery_mass
Here we’re saying that the battery energy can’t be more than how much we can put into a battery of some mass according to the specific energy. We can do the same with the power for the ESC and the motor, using their specific power values we set earlier.
esc_power <= esc_specificPower*esc_mass, motor_power <= motor_specificPower*motor_mass
One thing that we want to make sure of is that the ESC has enough power to run the motor.
esc_power >= motor_power
Now we can add a thrust model, which looks scary, but it’s really just this model multiplied by the number of propellers we have.
T_total <= 3.141/2 * rho* units('m^3/kg')* (propeller_efficiency*n_rotors*motor_power**(0.6)*propeller_d**(0.6)*units('(N/(W^0.6 * m^0.6))')),
Next we add up all the masses.
m_total >= n_rotors*(esc_mass+motor_mass) + battery_mass+payload_mass+structures_mass
We then add up the power, using the ESC power as it is the true consumption from the battery.
P_total >= n_rotors*esc_power
We also need to make sure our design flies.
T_total >= m_total*g,
The maximum hover time is the amount of time it takes to drain the batteries.
t_hover <= battery_energy/P_total
Here’s the complete set of constraints.
# Constraints constraints = [battery_energy <= battery_specificEnergy*battery_mass, esc_power <= esc_specificPower*esc_mass, motor_power <= motor_specificPower*motor_mass, esc_power >= motor_power, T_total <= 3.141/2 * rho* units('m^3/kg')* (propeller_efficiency*n_rotors*motor_power**(0.6)*propeller_d**(0.6)*units('(N/(W^0.6 * m^0.6))')), m_total >= n_rotors*(esc_mass+motor_mass) + battery_mass+payload_mass+structures_mass, P_total >= n_rotors*esc_power, T_total >= m_total*g, t_hover <= battery_energy/P_total]
We'll set up our objective, or design goal, to maximize hover time.
# Objective (to minimize) objective = 1/t_hover
Now we can put everything together and solve the model.
# Formulate the Model m = Model(objective, constraints) # Solve the Model sol = m.solve(verbosity=1)
We also want to see what our result was, so we’ll take the hover time in seconds and display it in minutes.
print '\nMax hover time is ' + str((sol(t_hover)*1/60).magnitude) + ' minutes'
If you run the Python file, either from IDLE or from your command line using ‘python opt.py’, you should see something like this.
Cost  0.0004271 [1/s] Free Variables  P_total : 44.16 [W] T_total : 7.35 [N] battery_energy : 1.034e+05 [J] battery_mass : 0.347 [kg] esc_mass : 0.02208 [kg] esc_power : 11.04 [W] m_total : 0.75 [kg] motor_mass : 0.003672 [kg] motor_power : 11.04 [W] t_hover : 2341 [s] Constants  battery_specificEnergy : 2.98e+05 [J/kg] esc_specificPower : 500 [W/kg] g : 9.8 [m/s**2] motor_specificPower : 3007 [W/kg] n_rotors : 4 payload_mass : 0.1 [kg] propeller_d : 0.1 [m] propeller_efficiency : 0.9 rho : 1.225 [kg/m**3] structures_mass : 0.2 [kg] Sensitivities  g : 2.161 structures_mass : 0.5764 payload_mass : 0.2882 motor_specificPower : 0.04233 esc_specificPower : 0.2546 n_rotors : 0.8646 battery_specificEnergy : 1 propeller_d : 1.297 rho : 2.161 propeller_efficiency : 2.161 Max hover time is 39.0206870556 minutes
If you didn't get the above, check your file against the one I provide at the end, or write your errors in the comments below.
What does all of this mean? Well, the cost is the inverse of the result for the objective we set, that’s why it has units of 1/s. The free variables are parameters that we left up to the optimizer, and the constants are values that we set. You can use the results of these free variables to help you pick out the right components from a retailer or your parts bin. Note that you can also fix the free variables according to what you have, to find what you need for the missing component. For example, we can fix the battery mass at some value.
battery_mass = Variable('battery_mass',0.2,'kg')
If you ran this you'd then know what the best ESC and motor choice would be, given this battery. Sensitivities are the interesting one, they tell you how much a given design variable affects your objective. For this example the variables that make our flight time worse if they increase are gravity and mass, and things that make it better are better batteries, motors, ESCs and bigger/more efficient propellers. Note that this design is the global optimum, it’s impossible to do better than the answer given if you’re using the same inputs and model.
We now have this program that spits out magical designs, so what can we do with it? Well, you can play around with things such as different propeller diameter values, payloads or structures and see how they scale. Or you can pick out specific energy or power values for expensive batteries or motors for and see what that does. One thing that I did was take the environmental values (rho=0.02 and g=3.71) for Mars and trying to make that work. I found that you need really large values of propeller diameter to make this work. NASA has run into the same problem.
After a while you'll get bored of endurance alone, so let’s extend the model to include another performance parameter, the thrust to weight ratio of the multirotor. We add this to our objective with multiplication, and we tone down the hover time using its exponent as we don’t care that much about it anymore. So change the objective line with the following code.
objective = (1/t_hover)**0.0001 * ((m_total*g)/T_total)**5
We also want to add a print out for the thrust to weight ratio, so we don't have to calculate it ourselves. Add this after the printout for the hover time.
print 'Thrust to weight ratio is ' + str(sol((T_total/(m_total*g))))
If we run this we get an insanely short hover time, because we are just making a rocket.
Max hover time is 0.000386251718637 minutes Thrust to weight ratio is 2.42199152139 dimensionless
Let’s put a lower constraint on the hover time and now try and see what thrust to weight ratio we can get.
t_hover >= Variable('t_hoverLimit',120,'s')
Result:
Max hover time is 2.00000007343 minutes Thrust to weight ratio is 2.20132038373 dimensionless
That’s pretty good, and you can see in the table that the design tries to keep the battery just light enough to fly with a monster powertrain. Other metrics for performance might be the payload mass fraction, total size or total mass. If you can describe something that you like in a multirotor using math, you can optimize it and make the best design possible. By tuning the exponents for each part of the objective, we can make proritize characteristics important according to our mission.
This is a pretty basic model, it doesn’t even include things like motor or ESC efficiency, so there’s definitely improvements that you can make. Also, the structures mass doesn’t change no matter how big the propellers are, and the mass of the propellers are not modelled at all. So there’s a lot you could improve on if you wanted to use this for your own designs.
The finished script is here: https://github.com/lochieferrier/gptut/blob/master/opt.py
If you’re interested in learning more about the optimization method used in this tutorial, read this (full disclosure: I work with the GPkit team as an undergraduate researcher).
Let me know in the comments if you found this useful and if you would like to see tutorials on setting up optimization for other types of UAVs (planes, helis etc). Also let me know if there are problems with the script.
I like the idea of this model though, have you used it in practice? How did the predicted results hold up?
Log In to reply