|
5 | 5 | import warnings |
6 | 6 | from typing import TYPE_CHECKING |
7 | 7 |
|
| 8 | +import casadi as cs |
8 | 9 | from array_api_compat import array_namespace |
9 | 10 | from scipy.spatial.transform import Rotation as R |
10 | 11 |
|
| 12 | +import drone_models.models.symbols as symbols |
11 | 13 | from drone_models.models.utils import supports |
12 | 14 | from drone_models.transform import motor_force2rotor_speed |
13 | 15 | from drone_models.utils import rotation |
@@ -61,7 +63,8 @@ def dynamics( |
61 | 63 | warnings.warn("Rotor velocity is not provided, using commanded rotor velocity directly.") |
62 | 64 | else: |
63 | 65 | rotor_vel_dot = ( |
64 | | - 1 / constants.DI_DD_ACC[2] * (cmd_rotor_vel - rotor_vel) - constants.KM * rotor_vel**2 |
| 66 | + 1 / constants.DI_DD_ACC[2] * (cmd_rotor_vel[..., None] - rotor_vel) |
| 67 | + - constants.KM * rotor_vel**2 |
65 | 68 | ) |
66 | 69 | forces_motor = xp.sum(constants.KF * rotor_vel**2, axis=-1) |
67 | 70 | forces_sum = xp.sum(forces_motor, axis=-1) |
@@ -102,10 +105,85 @@ def dynamics( |
102 | 105 | return pos_dot, quat_dot, vel_dot, ang_vel_dot, rotor_vel_dot |
103 | 106 |
|
104 | 107 |
|
105 | | -def symbolic_dynamics(): |
106 | | - """Symbolic dynamics of the fitted double integrator (DI) model with optional motor delay (D). |
| 108 | +def dynamics_symbolic( |
| 109 | + constants: Constants, |
| 110 | + calc_rotor_vel: bool = False, |
| 111 | + calc_dist_f: bool = False, |
| 112 | + calc_dist_t: bool = False, |
| 113 | +) -> tuple[cs.MX, cs.MX, cs.MX, cs.MX]: |
| 114 | + """The fitted double integrator (DI) model with optional motor delay (D). |
107 | 115 |
|
108 | | - Returns: |
109 | | - The symbolic derivatives of all state variables. |
| 116 | + TODO. |
110 | 117 | """ |
111 | | - ... |
| 118 | + # States and Inputs |
| 119 | + X = cs.vertcat(symbols.pos, symbols.quat, symbols.vel, symbols.ang_vel) |
| 120 | + if calc_rotor_vel: |
| 121 | + X = cs.vertcat(X, symbols.rotor_vel) |
| 122 | + if calc_dist_f: |
| 123 | + X = cs.vertcat(X, symbols.dist_f) |
| 124 | + if calc_dist_t: |
| 125 | + X = cs.vertcat(X, symbols.dist_t) |
| 126 | + U = cs.vertcat(symbols.cmd_roll, symbols.cmd_pitch, symbols.cmd_yaw, symbols.cmd_thrust) |
| 127 | + |
| 128 | + # Defining the dynamics function |
| 129 | + if calc_rotor_vel: |
| 130 | + # Note: Due to the structure of the integrator, we split the commanded thrust into |
| 131 | + # four equal parts and later apply the sum as total thrust again. Those four forces |
| 132 | + # are not the true forces of the motors, but the sum is the true total thrust. |
| 133 | + forces_motor_dot = 1 / constants.DI_D_ACC[2] * (symbols.cmd_thrust / 4 - symbols.rotor_vel) |
| 134 | + thrust = cs.sum1(symbols.rotor_vel) |
| 135 | + # Creating force vector |
| 136 | + forces_motor_vec = cs.vertcat(0, 0, constants.DI_D_ACC[0] + constants.DI_D_ACC[1] * thrust) |
| 137 | + else: |
| 138 | + thrust = symbols.cmd_thrust |
| 139 | + # Creating force vector |
| 140 | + forces_motor_vec = cs.vertcat(0, 0, constants.DI_ACC[0] + constants.DI_ACC[1] * thrust) |
| 141 | + |
| 142 | + # Linear equation of motion |
| 143 | + pos_dot = symbols.vel |
| 144 | + vel_dot = symbols.rot @ forces_motor_vec / constants.MASS + constants.GRAVITY_VEC |
| 145 | + if calc_dist_f: |
| 146 | + # Adding force disturbances to the state |
| 147 | + vel_dot = vel_dot + symbols.dist_f / constants.MASS |
| 148 | + |
| 149 | + # Rotational equation of motion |
| 150 | + euler_angles = rotation.cs_quat2euler(symbols.quat) |
| 151 | + |
| 152 | + xi = cs.vertcat( |
| 153 | + cs.horzcat(0, -symbols.ang_vel.T), cs.horzcat(symbols.ang_vel, -cs.skew(symbols.ang_vel)) |
| 154 | + ) |
| 155 | + quat_dot = 0.5 * (xi @ symbols.quat) |
| 156 | + rpy_rates = rotation.cs_ang_vel2rpy_rates(symbols.quat, symbols.ang_vel) |
| 157 | + if calc_rotor_vel: |
| 158 | + rpy_rates_dot = ( |
| 159 | + constants.DI_D_PARAMS[:, 0] * euler_angles |
| 160 | + + constants.DI_D_PARAMS[:, 1] * rpy_rates |
| 161 | + + constants.DI_D_PARAMS[:, 2] |
| 162 | + * cs.vertcat(symbols.cmd_roll, symbols.cmd_pitch, symbols.cmd_yaw) |
| 163 | + ) |
| 164 | + else: |
| 165 | + rpy_rates_dot = ( |
| 166 | + constants.DI_PARAMS[:, 0] * euler_angles |
| 167 | + + constants.DI_PARAMS[:, 1] * rpy_rates |
| 168 | + + constants.DI_PARAMS[:, 2] |
| 169 | + * cs.vertcat(symbols.cmd_roll, symbols.cmd_pitch, symbols.cmd_yaw) |
| 170 | + ) |
| 171 | + ang_vel_dot = rotation.cs_rpy_rates_deriv2ang_vel_deriv(symbols.quat, rpy_rates, rpy_rates_dot) |
| 172 | + if calc_dist_t: |
| 173 | + # adding torque disturbances to the state |
| 174 | + # angular acceleration can be converted to total torque |
| 175 | + torque = constants.J @ ang_vel_dot - cs.cross( |
| 176 | + symbols.ang_vel, constants.J @ symbols.ang_vel |
| 177 | + ) |
| 178 | + # adding torque |
| 179 | + torque = torque + symbols.dist_t |
| 180 | + # back to angular acceleration |
| 181 | + ang_vel_dot = constants.J_INV @ torque |
| 182 | + |
| 183 | + if calc_rotor_vel: |
| 184 | + X_dot = cs.vertcat(pos_dot, quat_dot, vel_dot, ang_vel_dot, forces_motor_dot) |
| 185 | + else: |
| 186 | + X_dot = cs.vertcat(pos_dot, quat_dot, vel_dot, ang_vel_dot) |
| 187 | + Y = cs.vertcat(symbols.pos, symbols.quat) |
| 188 | + |
| 189 | + return X_dot, X, U, Y |
0 commit comments