Skip to content

Commit 9d9f5a1

Browse files
committed
Fix symbolic models
1 parent 44faf9d commit 9d9f5a1

9 files changed

Lines changed: 174 additions & 191 deletions

File tree

drone_models/first_principles/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
TODO: Add description.
44
"""
55

6-
from drone_models.first_principles.model import dynamics, dynamics_symbolic
6+
from drone_models.first_principles.model import dynamics, symbolic_dynamics
77

8-
__all__ = ["dynamics", "dynamics_symbolic"]
8+
__all__ = ["dynamics", "symbolic_dynamics"]

drone_models/first_principles/model.py

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -114,34 +114,35 @@ def dynamics(
114114
return pos_dot, quat_dot, vel_dot, ang_vel_dot, rotor_vel_dot
115115

116116

117-
def dynamics_symbolic(
118-
calc_rotor_vel: bool = True,
119-
calc_dist_f: bool = False,
120-
calc_dist_t: bool = False,
117+
@register_model_parameters(FirstPrinciplesParams)
118+
def symbolic_dynamics(
119+
model_rotor_vel: bool = True,
120+
model_dist_f: bool = False,
121+
model_dist_t: bool = False,
121122
*,
122-
thrust_tau: float,
123-
KF: float,
124-
KM: float,
125-
L: float,
126-
sign_matrix: Array,
127-
gravity_vec: Array,
128123
mass: float,
124+
gravity_vec: Array,
129125
J: Array,
130126
J_inv: Array,
127+
KF: float,
128+
KM: float,
129+
L: float,
130+
mixing_matrix: Array,
131+
thrust_tau: float,
131132
) -> tuple[cs.MX, cs.MX, cs.MX, cs.MX]:
132133
"""TODO take from numeric."""
133134
# States and Inputs
134135
X = cs.vertcat(symbols.pos, symbols.quat, symbols.vel, symbols.ang_vel)
135-
if calc_rotor_vel:
136+
if model_rotor_vel:
136137
X = cs.vertcat(X, symbols.rotor_vel)
137-
if calc_dist_f:
138+
if model_dist_f:
138139
X = cs.vertcat(X, symbols.dist_f)
139-
if calc_dist_t:
140+
if model_dist_t:
140141
X = cs.vertcat(X, symbols.dist_t)
141142
U = symbols.cmd_rotor_vel
142143

143144
# Defining the dynamics function
144-
if calc_rotor_vel:
145+
if model_rotor_vel:
145146
# Thrust dynamics
146147
rotor_vel_dot = 1 / thrust_tau * (U - symbols.rotor_vel) - 1 / KM * symbols.rotor_vel**2
147148
forces_motor = KF * symbols.rotor_vel**2
@@ -150,12 +151,12 @@ def dynamics_symbolic(
150151

151152
# Creating force and torque vector
152153
forces_motor_vec = cs.vertcat(0, 0, cs.sum1(forces_motor))
153-
torques_motor_vec = forces_motor @ sign_matrix * cs.vertcat(L, L, KM / KF)
154+
torques_motor_vec = (forces_motor.T @ mixing_matrix).T * cs.vertcat(L, L, KM / KF)
154155

155156
# Linear equation of motion
156157
forces_motor_vec_world = symbols.rot @ forces_motor_vec
157158
forces_sum = forces_motor_vec_world + gravity_vec * mass
158-
if calc_dist_f is True:
159+
if model_dist_f:
159160
forces_sum = forces_sum + symbols.dist_f
160161

161162
pos_dot = symbols.vel
@@ -167,11 +168,11 @@ def dynamics_symbolic(
167168
)
168169
quat_dot = 0.5 * (xi @ symbols.quat)
169170
torques_sum = torques_motor_vec
170-
if calc_dist_t:
171+
if model_dist_t:
171172
torques_sum = torques_sum + symbols.rot.T @ symbols.dist_t
172173
ang_vel_dot = J_inv @ (torques_sum - cs.cross(symbols.ang_vel, J @ symbols.ang_vel))
173174

174-
if calc_rotor_vel:
175+
if model_rotor_vel:
175176
X_dot = cs.vertcat(pos_dot, quat_dot, vel_dot, ang_vel_dot, rotor_vel_dot)
176177
else:
177178
X_dot = cs.vertcat(pos_dot, quat_dot, vel_dot, ang_vel_dot)

drone_models/so_rpy/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
TODO: Add description.
44
"""
55

6-
from drone_models.so_rpy.model import dynamics, dynamics_symbolic
6+
from drone_models.so_rpy.model import dynamics, symbolic_dynamics
77

8-
__all__ = ["dynamics", "dynamics_symbolic"]
8+
__all__ = ["dynamics", "symbolic_dynamics"]

drone_models/so_rpy/model.py

Lines changed: 32 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616
if TYPE_CHECKING:
1717
from array_api_typing import Array
1818

19-
from drone_models.utils.constants import Constants
20-
2119

2220
@register_model_parameters(SoRpyParams)
2321
@supports(rotor_dynamics=False)
@@ -98,8 +96,10 @@ def dynamics(
9896
if dist_t is not None:
9997
# adding torque disturbances to the state
10098
# angular acceleration can be converted to total torque given the inertia matrix
101-
torque = ang_vel_dot @ J.mT + xp.linalg.cross(ang_vel, ang_vel @ J.mT)
102-
# adding torque
99+
torque = (J @ ang_vel_dot[..., None])[..., 0]
100+
torque = torque + xp.linalg.cross(ang_vel, (J @ ang_vel[..., None])[..., 0])
101+
# adding torque. TODO: This should be a linear transformation. Can't we just transform the
102+
# disturbance torque to an ang_vel_dot summand directly?
103103
torque = torque + rot.apply(dist_t, inverse=True)
104104
# back to angular acceleration
105105
torque = torque - xp.linalg.cross(ang_vel, (J @ ang_vel[..., None])[..., 0])
@@ -108,38 +108,46 @@ def dynamics(
108108
return pos_dot, quat_dot, vel_dot, ang_vel_dot, rotor_vel_dot
109109

110110

111-
def dynamics_symbolic(
112-
constants: Constants,
113-
calc_rotor_vel: bool = False,
114-
calc_dist_f: bool = False,
115-
calc_dist_t: bool = False,
111+
@register_model_parameters(SoRpyParams)
112+
def symbolic_dynamics(
113+
model_rotor_vel: bool = False,
114+
model_dist_f: bool = False,
115+
model_dist_t: bool = False,
116+
*,
117+
mass: float,
118+
gravity_vec: Array,
119+
J: Array,
120+
J_inv: Array,
121+
acc_coef: Array,
122+
cmd_f_coef: Array,
123+
rpy_coef: Array,
124+
rpy_rates_coef: Array,
125+
cmd_rpy_coef: Array,
116126
) -> tuple[cs.MX, cs.MX, cs.MX, cs.MX]:
117127
"""The fitted double integrator (DI) model with optional motor delay (D).
118128
119129
TODO.
120130
"""
121131
# States and Inputs
122132
X = cs.vertcat(symbols.pos, symbols.quat, symbols.vel, symbols.ang_vel)
123-
if calc_rotor_vel:
133+
if model_rotor_vel:
124134
X = cs.vertcat(X, symbols.rotor_vel)
125-
if calc_dist_f:
135+
if model_dist_f:
126136
X = cs.vertcat(X, symbols.dist_f)
127-
if calc_dist_t:
137+
if model_dist_t:
128138
X = cs.vertcat(X, symbols.dist_t)
129139
U = cs.vertcat(symbols.cmd_roll, symbols.cmd_pitch, symbols.cmd_yaw, symbols.cmd_thrust)
130140

131141
# Defining the dynamics function
132142
# Creating force vector
133-
forces_motor_vec = cs.vertcat(
134-
0, 0, constants.DI_ACC[0] + constants.DI_ACC[1] * symbols.cmd_thrust
135-
)
143+
forces_motor_vec = cs.vertcat(0, 0, acc_coef + cmd_f_coef * symbols.cmd_thrust)
136144

137145
# Linear equation of motion
138146
pos_dot = symbols.vel
139-
vel_dot = symbols.rot @ forces_motor_vec / constants.MASS + constants.GRAVITY_VEC
140-
if calc_dist_f:
147+
vel_dot = symbols.rot @ forces_motor_vec / mass + gravity_vec
148+
if model_dist_f:
141149
# Adding force disturbances to the state
142-
vel_dot = vel_dot + symbols.dist_f / constants.MASS
150+
vel_dot = vel_dot + symbols.dist_f / mass
143151

144152
# Rotational equation of motion
145153
euler_angles = rotation.cs_quat2euler(symbols.quat)
@@ -150,24 +158,19 @@ def dynamics_symbolic(
150158
quat_dot = 0.5 * (xi @ symbols.quat)
151159
rpy_rates = rotation.cs_ang_vel2rpy_rates(symbols.quat, symbols.ang_vel)
152160
rpy_rates_dot = (
153-
constants.DI_PARAMS[:, 0] * euler_angles
154-
+ constants.DI_PARAMS[:, 1] * rpy_rates
155-
+ constants.DI_PARAMS[:, 2]
156-
* cs.vertcat(symbols.cmd_roll, symbols.cmd_pitch, symbols.cmd_yaw)
161+
rpy_coef * euler_angles
162+
+ rpy_rates_coef * rpy_rates
163+
+ cmd_rpy_coef * cs.vertcat(symbols.cmd_roll, symbols.cmd_pitch, symbols.cmd_yaw)
157164
)
158165
ang_vel_dot = rotation.cs_rpy_rates_deriv2ang_vel_deriv(symbols.quat, rpy_rates, rpy_rates_dot)
159-
if calc_dist_t:
166+
if model_dist_t:
160167
# adding torque disturbances to the state
161168
# angular acceleration can be converted to total torque
162-
torque = constants.J @ ang_vel_dot + cs.cross(
163-
symbols.ang_vel, constants.J @ symbols.ang_vel
164-
)
169+
torque = J @ ang_vel_dot + cs.cross(symbols.ang_vel, J @ symbols.ang_vel)
165170
# adding torque
166171
torque = torque + symbols.rot.T @ symbols.dist_t
167172
# back to angular acceleration
168-
ang_vel_dot = constants.J_INV @ (
169-
torque - cs.cross(symbols.ang_vel, constants.J @ symbols.ang_vel)
170-
)
173+
ang_vel_dot = J_inv @ (torque - cs.cross(symbols.ang_vel, J @ symbols.ang_vel))
171174

172175
X_dot = cs.vertcat(pos_dot, quat_dot, vel_dot, ang_vel_dot)
173176
Y = cs.vertcat(symbols.pos, symbols.quat)

drone_models/so_rpy_rotor/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
TODO: Add description.
44
"""
55

6-
from drone_models.so_rpy_rotor.model import dynamics, dynamics_symbolic
6+
from drone_models.so_rpy_rotor.model import dynamics, symbolic_dynamics
77

8-
__all__ = ["dynamics", "dynamics_symbolic"]
8+
__all__ = ["dynamics", "symbolic_dynamics"]

drone_models/so_rpy_rotor/model.py

Lines changed: 46 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@
1818
if TYPE_CHECKING:
1919
from array_api_typing import Array
2020

21-
from drone_models.utils.constants import Constants
22-
2321

2422
@register_model_parameters(SoRpyRotorParams)
2523
@supports(rotor_dynamics=True)
@@ -35,10 +33,10 @@ def dynamics(
3533
*,
3634
mass: float,
3735
gravity_vec: Array,
38-
KF: Array,
39-
KM: Array,
4036
J: Array,
4137
J_inv: Array,
38+
KF: Array,
39+
KM: Array,
4240
rotor_coef: Array,
4341
acc_coef: Array,
4442
cmd_f_coef: Array,
@@ -62,10 +60,10 @@ def dynamics(
6260
mass: Mass of the drone (kg).
6361
gravity_vec: Gravity vector (m/s^2). We assume the gravity vector points downwards, e.g.
6462
[0, 0, -9.81].
65-
KF: Motor force constant (N/rad^2).
66-
KM: Motor torque constant (Nm/rad^2).
6763
J: Inertia matrix (kg m^2).
6864
J_inv: Inverse inertia matrix (1/kg m^2).
65+
KF: Motor force constant (N/rad^2).
66+
KM: Motor torque constant (Nm/rad^2).
6967
rotor_coef: Coefficient for the rotor dynamics (1/s).
7068
acc_coef: Coefficient for the acceleration (1/s^2).
7169
cmd_f_coef: Coefficient for the collective thrust (N/rad^2).
@@ -118,54 +116,71 @@ def dynamics(
118116
if dist_t is not None:
119117
# adding torque disturbances to the state
120118
# angular acceleration can be converted to total torque given the inertia matrix
121-
torque = ang_vel_dot @ J.mT + xp.linalg.cross(ang_vel, ang_vel @ J.mT)
122-
# adding torque
123-
torque = torque + rot.apply(dist_t, inverse=True) # TODO rotation into body frame
119+
torque = (J @ ang_vel_dot[..., None])[..., 0]
120+
torque = torque + xp.linalg.cross(ang_vel, (J @ ang_vel[..., None])[..., 0])
121+
# adding torque. TODO: This should be a linear transformation. Can't we just transform the
122+
# disturbance torque to an ang_vel_dot summand directly?
123+
torque = torque + rot.apply(dist_t, inverse=True)
124124
# back to angular acceleration
125125
torque = torque - xp.linalg.cross(ang_vel, (J @ ang_vel[..., None])[..., 0])
126126
ang_vel_dot = (J_inv @ torque[..., None])[..., 0]
127127

128128
return pos_dot, quat_dot, vel_dot, ang_vel_dot, rotor_vel_dot
129129

130130

131-
def dynamics_symbolic(
132-
constants: Constants,
133-
calc_rotor_vel: bool = False,
134-
calc_dist_f: bool = False,
135-
calc_dist_t: bool = False,
131+
@register_model_parameters(SoRpyRotorParams)
132+
def symbolic_dynamics(
133+
model_rotor_vel: bool = False,
134+
model_dist_f: bool = False,
135+
model_dist_t: bool = False,
136+
*,
137+
mass: float,
138+
gravity_vec: Array,
139+
J: Array,
140+
J_inv: Array,
141+
KF: Array,
142+
KM: Array,
143+
rotor_coef: Array,
144+
acc_coef: Array,
145+
cmd_f_coef: Array,
146+
rpy_coef: Array,
147+
rpy_rates_coef: Array,
148+
cmd_rpy_coef: Array,
136149
) -> tuple[cs.MX, cs.MX, cs.MX, cs.MX]:
137150
"""The fitted double integrator (DI) model with optional motor delay (D).
138151
139152
TODO.
140153
"""
141154
# States and Inputs
142155
X = cs.vertcat(symbols.pos, symbols.quat, symbols.vel, symbols.ang_vel)
143-
if calc_rotor_vel:
156+
if model_rotor_vel:
144157
X = cs.vertcat(X, symbols.rotor_vel)
145-
if calc_dist_f:
158+
if model_dist_f:
146159
X = cs.vertcat(X, symbols.dist_f)
147-
if calc_dist_t:
160+
if model_dist_t:
148161
X = cs.vertcat(X, symbols.dist_t)
149162
U = cs.vertcat(symbols.cmd_roll, symbols.cmd_pitch, symbols.cmd_yaw, symbols.cmd_thrust)
150163
cmd_rpy = cs.vertcat(symbols.cmd_roll, symbols.cmd_pitch, symbols.cmd_yaw)
151164

152165
# Defining the dynamics function
153-
if calc_rotor_vel:
166+
if model_rotor_vel:
154167
# motor_force2rotor_vel
155-
cmd_rotor_vel = cs.sqrt(symbols.cmd_thrust / 4 / constants.KF)
156-
rotor_vel_dot = 1 / constants.DI_D_ACC[2] * (cmd_rotor_vel - symbols.rotor_vel)
157-
thrust = constants.KF * cs.sum1(symbols.rotor_vel**2)
168+
cmd_rotor_vel = cs.sqrt(symbols.cmd_thrust / 4 / KF)
169+
rotor_vel_dot = (
170+
1 / rotor_coef * (cmd_rotor_vel - symbols.rotor_vel) - KM * symbols.rotor_vel**2
171+
)
172+
forces_motor = KF * cs.sum1(symbols.rotor_vel**2)
158173
else:
159-
thrust = symbols.cmd_thrust
174+
forces_motor = symbols.cmd_thrust
160175
# Creating force vector
161-
forces_motor_vec = cs.vertcat(0, 0, constants.DI_D_ACC[0] + constants.DI_D_ACC[1] * thrust)
176+
forces_motor_vec = cs.vertcat(0, 0, acc_coef + cmd_f_coef * forces_motor)
162177

163178
# Linear equation of motion
164179
pos_dot = symbols.vel
165-
vel_dot = symbols.rot @ forces_motor_vec / constants.MASS + constants.GRAVITY_VEC
166-
if calc_dist_f:
180+
vel_dot = symbols.rot @ forces_motor_vec / mass + gravity_vec
181+
if model_dist_f:
167182
# Adding force disturbances to the state
168-
vel_dot = vel_dot + symbols.dist_f / constants.MASS
183+
vel_dot = vel_dot + symbols.dist_f / mass
169184

170185
# Rotational equation of motion
171186
euler_angles = rotation.cs_quat2euler(symbols.quat)
@@ -175,26 +190,18 @@ def dynamics_symbolic(
175190
)
176191
quat_dot = 0.5 * (xi @ symbols.quat)
177192
rpy_rates = rotation.cs_ang_vel2rpy_rates(symbols.quat, symbols.ang_vel)
178-
rpy_rates_dot = (
179-
constants.DI_D_PARAMS[:, 0] * euler_angles
180-
+ constants.DI_D_PARAMS[:, 1] * rpy_rates
181-
+ constants.DI_D_PARAMS[:, 2] * cmd_rpy
182-
)
193+
rpy_rates_dot = rpy_coef * euler_angles + rpy_rates_coef * rpy_rates + cmd_rpy_coef * cmd_rpy
183194
ang_vel_dot = rotation.cs_rpy_rates_deriv2ang_vel_deriv(symbols.quat, rpy_rates, rpy_rates_dot)
184-
if calc_dist_t:
195+
if model_dist_t:
185196
# adding torque disturbances to the state
186197
# angular acceleration can be converted to total torque
187-
torque = constants.J @ ang_vel_dot + cs.cross(
188-
symbols.ang_vel, constants.J @ symbols.ang_vel
189-
)
198+
torque = J @ ang_vel_dot + cs.cross(symbols.ang_vel, J @ symbols.ang_vel)
190199
# adding torque
191200
torque = torque + symbols.rot.T @ symbols.dist_t
192201
# back to angular acceleration
193-
ang_vel_dot = constants.J_INV @ (
194-
torque - cs.cross(symbols.ang_vel, constants.J @ symbols.ang_vel)
195-
)
202+
ang_vel_dot = J_inv @ (torque - cs.cross(symbols.ang_vel, J @ symbols.ang_vel))
196203

197-
if calc_rotor_vel:
204+
if model_rotor_vel:
198205
X_dot = cs.vertcat(pos_dot, quat_dot, vel_dot, ang_vel_dot, rotor_vel_dot)
199206
else:
200207
X_dot = cs.vertcat(pos_dot, quat_dot, vel_dot, ang_vel_dot)

drone_models/so_rpy_rotor_drag/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
TODO: Add description.
44
"""
55

6-
from drone_models.so_rpy_rotor_drag.model import dynamics, dynamics_symbolic
6+
from drone_models.so_rpy_rotor_drag.model import dynamics, symbolic_dynamics
77

8-
__all__ = ["dynamics", "dynamics_symbolic"]
8+
__all__ = ["dynamics", "symbolic_dynamics"]

0 commit comments

Comments
 (0)