Skip to content

Commit 35982d3

Browse files
committed
Update thrust dynamics model and add intertia/gyro torque
1 parent b0ac79c commit 35982d3

2 files changed

Lines changed: 120 additions & 62 deletions

File tree

drone_models/data/params.toml

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,29 @@
22

33
[cf2x_L250]
44
gravity_vec = [0.0, 0.0, -9.81]
5-
mass = 0.03454
5+
mass = 0.0319
66
L = 0.03253
7-
J = [ # TODO
7+
J = [ # TODO
88
[16.8e-6, 0.0, 0.0],
99
[0.0, 16.8e-6, 0.0],
1010
[0.0, 0.0, 29.8e-6]
1111
]
1212
rpm2thrust = [0.0, -5.382196214637237e-7, 2.4582929831265485e-10]
1313
rpm2torque = [0.0, 1.410454111996297e-9, 1.4592584373980652e-12]
1414
thrust2torque = 0.007350862856566459
15-
# TODO: This is an arbitrary number that leads to a working simulation. It MUST be fitted and
16-
# updated as soon as possible.
17-
thrust_tau = 0.03 # TODO value?
18-
rotor_tau = 0.03 # TODO value?
15+
rotor_dyn_coef = [ 7.355623702172756, 0.0, 0.0, 0.00024443862952110715,] # [ 7.355623702172756, 0.0, 0.0, 0.00024443862952110715,]
16+
rotor_dyn_coef_simple = 6.886705423469015
17+
thrust_dyn_coef = 6.8932095506763345
1918
mixing_matrix = [
2019
[-1.0, -1.0, 1.0, 1.0],
2120
[-1.0, 1.0, 1.0, -1.0],
2221
[-1.0, 1.0, -1.0, 1.0]
2322
]
23+
drag_matrix = [ # This term is from the so_rpy_rotor_drag model
24+
[-0.01471782, 0.0, 0.0 ],
25+
[0.0, -0.01471782, 0.0 ],
26+
[0.0, 0.0, -0.01277641 ]
27+
]
2428
# The following parameters are for the platform, but are maybe not actually used by the models. However,
2529
# we still keep them here in one place, since some other things (sim, estimator, firmware) might need them.
2630
pwm_min = 7000
@@ -36,23 +40,29 @@ prop_inertia = 34.52e-9 # TODO seems off
3640

3741
[cf2x_P250]
3842
gravity_vec = [0.0, 0.0, -9.81]
39-
mass = 0.03454
43+
mass = 0.0318
4044
L = 0.03253
41-
J = [ # TODO
42-
[14e-6, 0.0, 0.0],
43-
[0.0, 14e-6, 0.0],
44-
[0.0, 0.0, 21.7e-6]
45+
J = [ # TODO from L250
46+
[16.8e-6, 0.0, 0.0],
47+
[0.0, 16.8e-6, 0.0],
48+
[0.0, 0.0, 29.8e-6]
4549
]
4650
rpm2thrust = [0.0, -3.6200226530383495e-7, 1.6060924304100328e-10] # Index is order
4751
rpm2torque = [0.0, -2.2665265829562245e-9, 1.1149485566919186e-12] # Index is order
4852
thrust2torque = 0.0069928948992470565
49-
thrust_tau = 0.138 # TODO value?
50-
rotor_tau = 0.138 # TODO value?
53+
rotor_dyn_coef = [ 5.172596691828673, 8.14774234381285e-5, 0.0, 0.0002095253491455741,]
54+
rotor_dyn_coef_simple = 7.709730027690284
55+
thrust_dyn_coef = 7.9435775497736785
5156
mixing_matrix = [
5257
[-1.0, -1.0, 1.0, 1.0],
5358
[-1.0, 1.0, 1.0, -1.0],
5459
[-1.0, 1.0, -1.0, 1.0]
5560
]
61+
drag_matrix = [ # This term is from the so_rpy_rotor_drag model
62+
[-0.01351483, 0.0, 0.0 ],
63+
[0.0, -0.01351483, 0.0 ],
64+
[0.0, 0.0, -0.01677452 ]
65+
]
5666
# The following parameters are for the platform, but are maybe not actually used by the models. However,
5767
# we still keep them here in one place, since some other things (sim, estimator, firmware) might need them.
5868
pwm_min = 7000
@@ -68,23 +78,29 @@ prop_inertia = 26.97e-9
6878

6979
[cf2x_T350]
7080
gravity_vec = [0.0, 0.0, -9.81]
71-
mass = 0.037 # TODO
81+
mass = 0.0379
7282
L = 0.03253
73-
J = [ # TODO
74-
[14e-6, 0.0, 0.0 ],
75-
[0.0, 14e-6, 0.0 ],
76-
[0.0, 0.0, 21.7e-6]
83+
J = [ # TODO from L250
84+
[16.8e-6, 0.0, 0.0],
85+
[0.0, 16.8e-6, 0.0],
86+
[0.0, 0.0, 29.8e-6]
7787
]
7888
rpm2thrust = [0.0, -7.167227176573658e-7, 2.9401303690194613e-10] # Index is order
7989
rpm2torque = [0.0, 5.815894847811497e-10, 1.331813874166509e-12] # Index is order
8090
thrust2torque = 0.005355990836477486
81-
thrust_tau = 0.09625268508006131
82-
rotor_tau = 0.0984319234122739
91+
rotor_dyn_coef = [ 11.374753209400291, 0.0, 0.0, 0.00037867688499079635,]
92+
rotor_dyn_coef_simple = 11.352970450445243
93+
thrust_dyn_coef = 11.12424272978587
8394
mixing_matrix = [
8495
[-1.0, -1.0, 1.0, 1.0],
8596
[-1.0, 1.0, 1.0, -1.0],
8697
[-1.0, 1.0, -1.0, 1.0]
8798
]
99+
drag_matrix = [ # This term is from the so_rpy_rotor_drag model
100+
[-0.01556697, 0.0, 0.0 ],
101+
[0.0, -0.01556697, 0.0 ],
102+
[0.0, 0.0, -0.02191672 ]
103+
]
88104
# The following parameters are for the platform, but are maybe not actually used by the models. However,
89105
# we still keep them here in one place, since some other things (sim, estimator, firmware) might need them.
90106
pwm_min = 7000
@@ -110,13 +126,19 @@ J = [
110126
rpm2thrust = [0.0, -3.133427287299859e-7, 4.407354891648379e-10] # TODO , Index is order
111127
rpm2torque = [0.0, 1.65886356219615e-9, 2.4693477924534137e-12] # TODO , Index is order
112128
thrust2torque = 0.00593893393599368
113-
thrust_tau = 0.06286705869479182
114-
rotor_tau = 0.06082197967926632
129+
rotor_dyn_coef = [ 13.996001897562685, 0.00011093207920685363, 5.933168530682111, 0.00031951312393561264,]
130+
rotor_dyn_coef_simple = 15.416891997523813
131+
thrust_dyn_coef = 15.09965949800411
115132
mixing_matrix = [
116133
[-1.0, -1.0, 1.0, 1.0],
117134
[-1.0, 1.0, 1.0, -1.0],
118135
[-1.0, 1.0, -1.0, 1.0]
119136
]
137+
drag_matrix = [ # This term is from the so_rpy_rotor_drag model
138+
[-0.02149163, 0.0, 0.0 ],
139+
[0.0, -0.02149163, 0.0 ],
140+
[0.0, 0.0, -0.02359736 ]
141+
]
120142
# The following parameters are for the platform, but are maybe not actually used by the models. However,
121143
# we still keep them here in one place, since some other things (sim, estimator, firmware) might need them.
122144
pwm_min = 7000

drone_models/first_principles/model.py

Lines changed: 76 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,16 @@ def dynamics(
2828
dist_t: Array | None = None,
2929
*,
3030
mass: float,
31+
L: float,
32+
prop_inertia: float,
3133
gravity_vec: Array,
3234
J: Array,
3335
J_inv: Array,
3436
rpm2thrust: Array,
3537
rpm2torque: Array,
36-
L: float,
3738
mixing_matrix: Array,
38-
rotor_tau: float,
39+
rotor_dyn_coef: Array,
40+
drag_matrix: Array,
3941
) -> tuple[Array, Array, Array, Array, Array | None]:
4042
r"""First principles model for a quatrotor.
4143
@@ -63,7 +65,7 @@ def dynamics(
6365
rpm2torque: Propeller torque constant (Nm min^2).
6466
L: Distance from the CoM to the motor (m).
6567
mixing_matrix: Mixing matrix denoting the turn direction of the motors (4x3).
66-
rotor_tau: Thrust time constant (s).
68+
rotor_dyn_coef: Rotor dynamics coefficients.
6769
6870
.. math::
6971
\sum_{i=1}^{\\infty} x_{i} TODO
@@ -74,41 +76,57 @@ def dynamics(
7476
More information https://ahrs.readthedocs.io/en/latest/filters/angular.html
7577
"""
7678
xp = array_namespace(pos)
77-
mass, gravity_vec, J, J_inv, rpm2thrust, rpm2torque, L, mixing_matrix, rotor_tau = to_xp(
78-
mass,
79-
gravity_vec,
80-
J,
81-
J_inv,
82-
rpm2thrust,
83-
rpm2torque,
84-
L,
85-
mixing_matrix,
86-
rotor_tau,
87-
xp=xp,
88-
device=device(pos),
79+
mass, L, prop_inertia, gravity_vec, rpm2thrust, rpm2torque = to_xp(
80+
mass, L, prop_inertia, gravity_vec, rpm2thrust, rpm2torque, xp=xp, device=device(pos)
81+
)
82+
J, J_inv, mixing_matrix, rotor_dyn_coef, drag_matrix = to_xp(
83+
J, J_inv, mixing_matrix, rotor_dyn_coef, drag_matrix, xp=xp, device=device(pos)
8984
)
90-
rot = R.from_quat(quat)
85+
rot = R.from_quat(quat) # from body to world
86+
rot_mat = rot.inv().as_matrix() # from world to body
9187
# Rotor dynamics
9288
if rotor_vel is None:
9389
rotor_vel, rotor_vel_dot = cmd, None
9490
else:
95-
rotor_vel_dot = 1 / rotor_tau * (cmd - rotor_vel) # - 1 / KM * rotor_vel**2
91+
rotor_vel_dot = xp.where(
92+
cmd > rotor_vel,
93+
rotor_dyn_coef[0] * (cmd - rotor_vel) + rotor_dyn_coef[1] * (cmd**2 - rotor_vel**2),
94+
rotor_dyn_coef[2] * (cmd - rotor_vel) + rotor_dyn_coef[3] * (cmd**2 - rotor_vel**2),
95+
)
9696
# Creating force and torque vector
9797
forces_motor = rpm2thrust[0] + rpm2thrust[1] * rotor_vel + rpm2thrust[2] * rotor_vel**2
98-
torques_motor = rpm2torque[0] + rpm2torque[1] * rotor_vel + rpm2torque[2] * rotor_vel**2
9998
forces_motor_tot = xp.sum(forces_motor, axis=-1)
10099
zeros = xp.zeros_like(forces_motor_tot)
101100
forces_motor_vec = xp.stack((zeros, zeros, forces_motor_tot), axis=-1)
101+
forces_motor_vec_world = rot.apply(forces_motor_vec)
102+
force_gravity = gravity_vec * mass
103+
force_drag = (rot_mat.mT @ (drag_matrix @ (rot_mat @ vel[..., None])))[..., 0]
102104

103-
torque_vec = (mixing_matrix @ (forces_motor)[..., None])[..., 0] * xp.stack(
105+
torques_motor = rpm2torque[0] + rpm2torque[1] * rotor_vel + rpm2torque[2] * rotor_vel**2
106+
torque_thrust = (mixing_matrix @ (forces_motor)[..., None])[..., 0] * xp.stack(
104107
[L, L, xp.asarray(0.0)]
105-
) + (mixing_matrix @ (torques_motor)[..., None])[..., 0] * xp.stack(
108+
)
109+
torque_drag = (mixing_matrix @ (torques_motor)[..., None])[..., 0] * xp.stack(
106110
[xp.asarray(0.0), xp.asarray(0.0), xp.asarray(1.0)]
107-
) # TODO add gyro etc
111+
)
112+
# convert rotor speed from RPM to rad/s for physical calculations
113+
rpm_to_rad = 2 * xp.pi / 60
114+
rotor_vel_rads = rotor_vel * rpm_to_rad
115+
rotor_vel_dot_rads = (
116+
rotor_vel_dot * rpm_to_rad if rotor_vel_dot is not None else xp.zeros_like(rotor_vel)
117+
)
118+
torque_inertia = prop_inertia * xp.stack(
119+
[
120+
-ang_vel[..., 1] * xp.sum(mixing_matrix[..., -1, :] * rotor_vel_rads, axis=-1),
121+
-ang_vel[..., 0] * xp.sum(mixing_matrix[..., -1, :] * rotor_vel_rads, axis=-1),
122+
xp.sum(mixing_matrix[..., -1, :] * rotor_vel_dot_rads, axis=-1),
123+
],
124+
axis=-1,
125+
)
126+
torque_vec = torque_thrust + torque_drag + torque_inertia
108127

109128
# Linear equation of motion
110-
forces_motor_vec_world = rot.apply(forces_motor_vec)
111-
forces_sum = forces_motor_vec_world + gravity_vec * mass # TODO add drag etc
129+
forces_sum = forces_motor_vec_world + force_gravity + force_drag
112130
if dist_f is not None:
113131
forces_sum = forces_sum + dist_f
114132

@@ -130,14 +148,16 @@ def symbolic_dynamics(
130148
model_dist_t: bool = False,
131149
*,
132150
mass: float,
151+
L: float,
152+
prop_inertia: float,
133153
gravity_vec: Array,
134154
J: Array,
135155
J_inv: Array,
136156
rpm2thrust: Array,
137157
rpm2torque: Array,
138-
L: float,
139158
mixing_matrix: Array,
140-
rotor_tau: float,
159+
rotor_dyn_coef: Array,
160+
drag_matrix: Array,
141161
) -> tuple[cs.MX, cs.MX, cs.MX, cs.MX]:
142162
"""TODO take from numeric."""
143163
# States and Inputs
@@ -152,27 +172,43 @@ def symbolic_dynamics(
152172

153173
# Defining the dynamics function
154174
if model_rotor_vel:
155-
# Thrust dynamics
156-
rotor_vel_dot = 1 / rotor_tau * (U - symbols.rotor_vel) # - 1 / KM * symbols.rotor_vel**2
157-
forces_motor = (
158-
rpm2thrust[0] + rpm2thrust[1] * symbols.rotor_vel + rpm2thrust[2] * symbols.rotor_vel**2
159-
)
160-
torques_motor = (
161-
rpm2torque[0] + rpm2torque[1] * symbols.rotor_vel + rpm2torque[2] * symbols.rotor_vel**2
175+
# Rotor dynamics
176+
rotor_vel_dot = cs.if_else(
177+
U > symbols.rotor_vel,
178+
rotor_dyn_coef[0] * (U - symbols.rotor_vel)
179+
+ rotor_dyn_coef[1] * (U**2 - symbols.rotor_vel**2),
180+
rotor_dyn_coef[2] * (U - symbols.rotor_vel)
181+
+ rotor_dyn_coef[3] * (U**2 - symbols.rotor_vel**2),
162182
)
163183
else:
164-
forces_motor = rpm2thrust[0] + rpm2thrust[1] * U + rpm2thrust[2] * U**2
165-
torques_motor = rpm2torque[0] + rpm2torque[1] * U + rpm2torque[2] * U**2
166-
184+
symbols.rotor_vel = U
167185
# Creating force and torque vector
186+
forces_motor = (
187+
rpm2thrust[0] + rpm2thrust[1] * symbols.rotor_vel + rpm2thrust[2] * symbols.rotor_vel**2
188+
)
168189
forces_motor_vec = cs.vertcat(0.0, 0.0, cs.sum1(forces_motor))
169-
torques_motor_vec = mixing_matrix @ forces_motor * cs.vertcat(
170-
L, L, 0.0
171-
) + mixing_matrix @ torques_motor * cs.vertcat(0.0, 0.0, 1.0)
190+
forces_motor_vec_world = symbols.rot @ forces_motor_vec
191+
force_gravity = gravity_vec * mass
192+
force_drag = symbols.rot @ (drag_matrix @ (symbols.rot.T @ symbols.vel))
193+
194+
torques_motor = (
195+
rpm2torque[0] + rpm2torque[1] * symbols.rotor_vel + rpm2torque[2] * symbols.rotor_vel**2
196+
)
197+
torques_thrust = mixing_matrix @ forces_motor * cs.vertcat(L, L, 0.0)
198+
torques_drag = mixing_matrix @ torques_motor * cs.vertcat(0.0, 0.0, 1.0)
199+
# convert rotor speed from RPM to rad/s for physical calculations
200+
rpm_to_rad = 2 * cs.pi / 60
201+
rotor_vel_rads = symbols.rotor_vel * rpm_to_rad
202+
rotor_vel_dot_rads = rotor_vel_dot * rpm_to_rad if model_rotor_vel else symbols.rotor_vel * 0.0
203+
torque_inertia = prop_inertia * cs.vertcat(
204+
-symbols.ang_vel[1] * cs.sum(mixing_matrix[-1, :] * rotor_vel_rads),
205+
-symbols.ang_vel[0] * cs.sum(mixing_matrix[-1, :] * rotor_vel_rads),
206+
cs.sum(mixing_matrix[-1, :] * rotor_vel_dot_rads),
207+
)
208+
torques_motor_vec = torques_thrust + torques_drag + torque_inertia
172209

173210
# Linear equation of motion
174-
forces_motor_vec_world = symbols.rot @ forces_motor_vec
175-
forces_sum = forces_motor_vec_world + gravity_vec * mass
211+
forces_sum = forces_motor_vec_world + force_gravity + force_drag
176212
if model_dist_f:
177213
forces_sum = forces_sum + symbols.dist_f
178214

0 commit comments

Comments
 (0)