6.25. Design optimization and exploration of a multirotor drone (ISAE)#
Written by Marc Budinger (INSA Toulouse), Scott Delbecq (ISAE-SUPAERO) and Félix Pollet (ISAE-SUPAERO), Toulouse, France.
The objective of this notebook is to use and modify the sizing code of the UAV to explore the design space.
6.25.1. Modular sizing code#
Here we use the sizing code but decomposed into several ExplicitComponent
of OpenMDAO. You might notice an improvement in computational cost and robustness compared to the previous sizing code that used a single ExplicitComponent
to wrap all the sizing code.
The models are separated by components and located in different Python files /models/openmdao
.
# Import libraries
import numpy as np
import openmdao.api as om
from models.openmdao.scenarios import Scenarios
from models.openmdao.propeller import Propeller
from models.openmdao.motor import Motor
from models.openmdao.battery import Battery
from models.openmdao.esc import ESC
from models.openmdao.frame import Frame
from models.openmdao.objectives import Objectives
from models.openmdao.constraints import Constraints
The inputs of the whole sizing code are stored in dict
s that we be passed to the problem before the run. Here are the default values seperated by:
specifications
design assumtpions
design variable initialization
However, one of the objective of the notebook is to modify these input values.
# Specifications
specifications_default = {
"M_pay": 50.0, # [kg] load mass
"a_to": 0.25 * 9.81, # [m/s**2] acceleration
"t_hov_spec": 25.0, # [min] time of hover flight
"MTOW": 360.0, # [kg] maximal mass allowed
}
# Design assumptions
design_assumptions_default = {
"N_arm" : 4, # [-] number of arms
"N_pro_arm" : 1, # [-] number of propeller per arm (1 or 2)
"M_bat_ref" : 0.329, # [kg] mass
"E_bat_ref" : 220.0 * 3600.0 * 0.329, # [J]
"C_bat_ref" : 5, # [Ah] Capacity
"I_bat_max_ref" : 50 * 5, # [A] max discharge current
"P_esc_ref" : 3108.0, # [W] Power
"M_esc_ref" : 0.115, # [kg] Mass
"T_nom_mot_ref" : 2.32, # [N*m] rated torque
"T_max_mot_ref" : 85.0 / 70.0 * 2.32, # [N*m] max torque
"R_mot_ref" : 0.03, # [ohm] resistance
"M_mot_ref" : 0.575, # [kg] mass
"K_T_ref" : 0.03, # [N*m/A] torque coefficient
"T_mot_fr_ref" : 0.03, # [N*m] friction torque (zero load, nominal speed)
"sigma_max" : (
280e6 / 4.0
), # [Pa] Composite max stress (2 reduction for dynamic, 2 reduction for stress concentration)
"rho_s" : 1700.0, # [kg/m**3] Volumic mass of aluminum
"rho_air" : 1.18, # [kg/m**3] Air density
"ND_max" : 105000.0 / 60.0 * 0.0254, # [Hz.m] Max speed limit (N.D max) for APC MR propellers
"D_pro_ref" : 11.0 * 0.0254, # [m] Reference propeller diameter
"M_pro_ref" : 0.53 * 0.0283, # [kg] Reference propeller mass
}
# Design variables initial values
design_variables_default = {
"beta_pro": 0.33, # pitch/diameter ratio of the propeller
"k_os": 3.2, # over sizing coefficient on the load mass
"k_ND": 1.2, # slow down propeller coef : ND = NDmax / k_ND
"k_mot": 1.0, # over sizing coefficient on the motor torque
"k_speed_mot": 1.2, # adaption winding coef on the motor speed
"k_mb": 1.0, # ratio battery/load mass
"k_vb": 1.0, # oversizing coefficient for voltage evaluation
"k_D": 0.5, # aspect ratio D_in/D_out for the beam of the frame
}
Similar to the previous notebook, we define the models to include in the problem as well as the optimization formulation.
prob = om.Problem()
group = om.Group()
group.add_subsystem("scenarios", Scenarios(), promotes=["*"])
group.add_subsystem("propeller", Propeller(), promotes=["*"])
group.add_subsystem("motor", Motor(), promotes=["*"])
group.add_subsystem("battery", Battery(), promotes=["*"])
group.add_subsystem("esc", ESC(), promotes=["*"])
group.add_subsystem("frame", Frame(), promotes=["*"])
group.add_subsystem("objectives", Objectives(), promotes=["*"])
group.add_subsystem("constraints", Constraints(), promotes=["*"])
prob.model = group
prob.driver = om.ScipyOptimizeDriver()
prob.driver.options["optimizer"] = "SLSQP"
prob.driver.options["maxiter"] = 100
prob.driver.options["tol"] = 1e-8
prob.model.add_design_var("beta_pro", lower=0.3, upper=0.6)
prob.model.add_design_var("k_os", lower=1.0, upper=10.0)
prob.model.add_design_var("k_ND", lower=1.0, upper=10.0)
prob.model.add_design_var("k_mot", lower=1.0, upper=10.0)
prob.model.add_design_var("k_speed_mot", lower=1.0, upper=10.0)
prob.model.add_design_var("k_mb", lower=0.1, upper=10.0)
prob.model.add_design_var("k_vb", lower=1.0, upper=10.0)
prob.model.add_design_var("k_D", lower=0.01, upper=0.99)
prob.model.add_constraint("cons_1", lower=0.)
prob.model.add_constraint("cons_2", lower=0.)
prob.model.add_constraint("cons_3", lower=0.)
prob.model.add_constraint("cons_4", lower=0.)
prob.model.add_constraint("cons_5", lower=0.)
prob.model.add_constraint("cons_6", lower=0.)
prob.model.add_constraint('cons_7', lower=0.)
# prob.model.add_objective('t_hov', scaler=-0.1)
prob.model.add_objective("M_total_real", scaler=0.1)
prob.setup()
<openmdao.core.problem.Problem at 0x7f4dd9d69d60>
Now we set the inputs based on the values provided by the dictionnaries.
# Setup initial values
for name, value in {**specifications_default, **design_assumptions_default, **design_variables_default}.items():
prob.set_val(name, value)
optimization_unsuccessful = prob.run_driver()
/opt/hostedtoolcache/Python/3.9.21/x64/lib/python3.9/site-packages/scipy/optimize/_slsqp_py.py:437: RuntimeWarning: Values in x were outside bounds during a minimize step, clipping to bounds
fx = wrapped_fun(x)
Optimization terminated successfully (Exit mode 0)
Current function value: 10.377302309050386
Iterations: 11
Function evaluations: 13
Gradient evaluations: 11
Optimization Complete
-----------------------------------
Please note that if the optimization is unsuccessful optimization_unsuccessful = True
. This could be useful.
We can check the details of the optimization results:
results = prob.list_problem_vars(print_arrays=True,
desvar_opts=['lower', 'upper','min', 'max'],
cons_opts=['lower', 'upper', 'equals', 'min', 'max'],
objs_opts=['scaler'])
----------------
Design Variables
----------------
name val size lower upper min max
----------- ------------ ---- ----- ----- ---------- ----------
beta_pro [0.3] 1 0.3 0.6 0.3 0.3
k_os [2.07546046] 1 1.0 10.0 2.07546046 2.07546046
k_ND [1.25998296] 1 1.0 10.0 1.25998296 1.25998296
k_mot [1.02941176] 1 1.0 10.0 1.02941176 1.02941176
k_speed_mot [1.26195253] 1 1.0 10.0 1.26195253 1.26195253
k_mb [0.64317582] 1 0.1 10.0 0.64317582 0.64317582
k_vb [1.] 1 1.0 10.0 1.0 1.0
k_D [0.99] 1 0.01 0.99 0.99 0.99
-----------
Constraints
-----------
name val size lower upper equals min max
------ ----------------- ---- ----- ----- ------ ------------------ ------------------
cons_1 [-7.61446017e-10] 1 0.0 1e+30 None -0.0 -0.0
cons_2 [-3.75628417e-11] 1 0.0 1e+30 None -0.0 -0.0
cons_3 [-5.05906428e-12] 1 0.0 1e+30 None -0.0 -0.0
cons_4 [1.01877113e+09] 1 0.0 1e+30 None 1018771130.9464543 1018771130.9464543
cons_5 [3.17643194] 1 0.0 1e+30 None 3.17643194 3.17643194
cons_6 [-1.57811542e-11] 1 0.0 1e+30 None -0.0 -0.0
cons_7 [256.22697691] 1 0.0 1e+30 None 256.22697691 256.22697691
----------
Objectives
----------
name val size scaler
------------ ------------- ---- ------
M_total_real [10.37730231] 1 0.1
/opt/hostedtoolcache/Python/3.9.21/x64/lib/python3.9/site-packages/openmdao/core/problem.py:2074: OMDeprecationWarning:Method `list_problem_vars` has been renamed `list_driver_vars`.
Please update your code to use list_driver_vars to avoid this warning.
And check the details of the final inputs:
inputs = prob.model.list_inputs(prom_name=True)
70 Input(s) in 'model'
varname val prom_name
--------------- ----------------- -------------
scenarios
k_os [2.07546046] k_os
M_pay [50.] M_pay
N_pro_arm [1.] N_pro_arm
N_arm [4.] N_arm
a_to [2.4525] a_to
propeller
beta_pro [0.3] beta_pro
F_pro_to [318.12917391] F_pro_to
rho_air [1.18] rho_air
ND_max [44.45] ND_max
k_ND [1.25998296] k_ND
M_pro_ref [0.014999] M_pro_ref
D_pro_ref [0.2794] D_pro_ref
F_pro_hov [254.50333913] F_pro_hov
motor
k_mot [1.02941176] k_mot
T_pro_hov [20.72734178] T_pro_hov
k_vb [1.] k_vb
P_pro_to [3616.45872422] P_pro_to
k_speed_mot [1.26195253] k_speed_mot
Omega_pro_to [139.58215244] Omega_pro_to
M_mot_ref [0.575] M_mot_ref
T_nom_mot_ref [2.32] T_nom_mot_ref
R_mot_ref [0.03] R_mot_ref
K_T_ref [0.03] K_T_ref
T_mot_fr_ref [0.03] T_mot_fr_ref
T_max_mot_ref [2.81714286] T_max_mot_ref
Omega_pro_hov [124.84607252] Omega_pro_hov
T_pro_to [25.90917722] T_pro_to
battery
k_mb [0.64317582] k_mb
M_pay [50.] M_pay
E_bat_ref [260568.] E_bat_ref
M_bat_ref [0.329] M_bat_ref
U_bat [35.13984874] U_bat
I_bat_max_ref [250.] I_bat_max_ref
C_bat_ref [5.] C_bat_ref
P_el_mot_hov [3226.16990396] P_el_mot_hov
N_pro [4.] N_pro
esc
P_el_mot_to [4599.19695835] P_el_mot_to
U_bat [35.13984874] U_bat
U_mot_to [35.13984874] U_mot_to
M_esc_ref [0.115] M_esc_ref
P_esc_ref [3108.] P_esc_ref
frame
N_arm [4.] N_arm
D_pro [1.58802403] D_pro
F_pro_to [318.12917391] F_pro_to
N_pro_arm [1.] N_pro_arm
sigma_max [70000000.] sigma_max
k_D [0.99] k_D
rho_s [1700.] rho_s
objectives
C_bat [579849.10715616] C_bat
I_bat_hov [386.56607144] I_bat_hov
M_esc [0.17017621] M_esc
M_pro [0.48453331] M_pro
M_mot [3.8516699] M_mot
N_pro [4.] N_pro
M_pay [50.] M_pay
M_bat [32.15879091] M_bat
M_frame [3.58871449] M_frame
constraints
M_total [103.77302309] M_total
M_total_real [103.77302309] M_total_real
T_max_mot [25.90917722] T_max_mot
T_pro_to [25.90917722] T_pro_to
U_bat [35.13984874] U_bat
U_mot_to [35.13984874] U_mot_to
P_bat_max [1.0187905e+09] P_bat_max
P_el_mot_to [4599.19695835] P_el_mot_to
N_pro [4.] N_pro
U_esc [38.31628069] U_esc
t_hov [25.] t_hov
t_hov_spec [25.] t_hov_spec
MTOW [360.] MTOW
And check the details of the final outputs:
outputs = prob.model.list_outputs()
54 Explicit Output(s) in 'model'
varname val prom_name
--------------- ------------------- -------------
scenarios
M_total [103.77302309] M_total
N_pro [4.] N_pro
F_pro_hov [254.50333913] F_pro_hov
F_pro_to [318.12917391] F_pro_to
propeller
C_t [0.0859] C_t
C_p [0.02768] C_p
D_pro [1.58802403] D_pro
n_pro_to [22.21518953] n_pro_to
Omega_pro_to [139.58215244] Omega_pro_to
M_pro [0.48453331] M_pro
P_pro_to [3616.45872422] P_pro_to
T_pro_to [25.90917722] T_pro_to
n_pro_hov [19.86986957] n_pro_hov
Omega_pro_hov [124.84607252] Omega_pro_hov
P_pro_hov [2587.72721446] P_pro_hov
T_pro_hov [20.72734178] T_pro_hov
motor
T_nom_mot [21.33696947] T_nom_mot
U_bat [35.13984874] U_bat
K_T [0.19949269] K_T
M_mot [3.8516699] M_mot
R_mot [0.05573107] R_mot
T_mot_fr [0.20095669] T_mot_fr
T_max_mot [25.90917722] T_max_mot
I_mot_hov [104.90759761] I_mot_hov
U_mot_hov [30.75249055] U_mot_hov
P_el_mot_hov [3226.16990396] P_el_mot_hov
I_mot_to [130.88266235] I_mot_to
U_mot_to [35.13984874] U_mot_to
P_el_mot_to [4599.19695835] P_el_mot_to
battery
M_bat [32.15879091] M_bat
E_bat [20375809.91972557] E_bat
C_bat [579849.10715616] C_bat
I_bat_max [28992455.35780776] I_bat_max
P_bat_max [1.0187905e+09] P_bat_max
I_bat_hov [386.56607144] I_bat_hov
esc
P_esc [4599.19695835] P_esc
U_esc [38.31628069] U_esc
M_esc [0.17017621] M_esc
frame
alpha_sep [1.57079633] alpha_sep
L_arm [1.12290256] L_arm
D_out_arm [0.1096737] D_out_arm
D_in_arm [0.10857696] D_in_arm
M_arms [1.4354858] M_arms
M_body [2.1532287] M_body
M_frame [3.58871449] M_frame
objectives
t_hov [25.] t_hov
M_total_real [103.77302309] M_total_real
constraints
cons_1 [-7.61446017e-10] cons_1
cons_2 [-3.75628417e-11] cons_2
cons_3 [-5.05906428e-12] cons_3
cons_4 [1.01877113e+09] cons_4
cons_5 [3.17643194] cons_5
cons_6 [-1.57811542e-11] cons_6
cons_7 [256.22697691] cons_7
0 Implicit Output(s) in 'model'
Now feel free to copy/duplicate and modify peaces of the previosu code to answer the different exercices.
Note
Please highlight during the exercice the design space where no physical solutions are found (if relevant).
6.25.2. Determining the physical limits of the design space#
6.25.2.1. Payload#
Exercise 6.24
Determine with the default requirements what is the payload value where the UAV design optimization does not find a solution?
# To be completed
Please comment…
6.25.2.2. Take-off acceleration#
Exercise 6.25
Determine with the default requirements what is the take-off acceleration value where the UAV design optimization does not find a solution?
# To be completed
Please comment…
6.25.2.3. Autonomy#
# To be completed
Please comment…
6.25.3. UAV total mass (\(M_{total,real}\))#
Warning
From now on, deactivate the constraint on the MTOW.
6.25.3.1. \(M_{total,real}\) vs Autonomy#
# To be completed
Please comment…
6.25.3.2. \(M_{total,real}\) vs Payload#
# To be completed
Please comment…
6.25.3.3. Autonomy vs Payload#
# To be completed
Please comment…
6.25.4. Model modifications#
6.25.4.1. Motor reference component#
# To be completed
Please comment…
6.25.4.2. Battery reference component#
# To be completed
Please comment…
6.25.5. Extension to full mission profile#
Exercise 6.26
Modify the code to consider the full mission profile in the design procedure based on the developed simulation model to assess the full energy required. Explore the effects of changing the specifications for the package delivery application.
# To be completed
Please comment…