@@ -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