justinT21 | 07d41ed | 2024-07-31 21:12:31 -0700 | [diff] [blame] | 1 | #!/usr/bin/python3 |
| 2 | |
| 3 | import numpy, scipy |
| 4 | |
| 5 | from matplotlib import pylab |
| 6 | from frc971.control_loops.swerve import physics_test_utils as utils |
| 7 | from frc971.control_loops.swerve import dynamics |
| 8 | |
| 9 | |
| 10 | class PhysicsDebug(object): |
| 11 | |
| 12 | def wrap(self, python_module): |
justinT21 | 62d9bde | 2024-09-21 17:02:15 -0700 | [diff] [blame] | 13 | self.swerve_physics = utils.wrap(python_module.swerve_full_dynamics) |
justinT21 | 07d41ed | 2024-07-31 21:12:31 -0700 | [diff] [blame] | 14 | self.contact_patch_velocity = [ |
| 15 | utils.wrap_module(python_module.contact_patch_velocity, i) |
| 16 | for i in range(4) |
| 17 | ] |
| 18 | self.wheel_ground_velocity = [ |
| 19 | utils.wrap_module(python_module.wheel_ground_velocity, i) |
| 20 | for i in range(4) |
| 21 | ] |
| 22 | self.wheel_slip_velocity = [ |
| 23 | utils.wrap_module(python_module.wheel_slip_velocity, i) |
| 24 | for i in range(4) |
| 25 | ] |
| 26 | self.wheel_force = [ |
| 27 | utils.wrap_module(python_module.wheel_force, i) for i in range(4) |
| 28 | ] |
| 29 | self.module_angular_accel = [ |
| 30 | utils.wrap_module(python_module.module_angular_accel, i) |
| 31 | for i in range(4) |
| 32 | ] |
| 33 | self.F = [utils.wrap_module(python_module.F, i) for i in range(4)] |
| 34 | self.mounting_location = [ |
| 35 | utils.wrap_module(python_module.mounting_location, i) |
| 36 | for i in range(4) |
| 37 | ] |
| 38 | |
| 39 | self.slip_angle = [ |
| 40 | utils.wrap_module(python_module.slip_angle, i) for i in range(4) |
| 41 | ] |
| 42 | self.slip_ratio = [ |
| 43 | utils.wrap_module(python_module.slip_ratio, i) for i in range(4) |
| 44 | ] |
| 45 | self.Ms = [utils.wrap_module(python_module.Ms, i) for i in range(4)] |
| 46 | |
| 47 | def print_state(self, swerve_physics, I, x): |
| 48 | xdot = swerve_physics(x, I) |
| 49 | |
| 50 | for i in range(4): |
| 51 | print(f" Slip Angle {i} {self.slip_angle[0](x, I)}") |
| 52 | print(f" Slip Ratio {i} {self.slip_ratio[0](x, I)}") |
| 53 | |
| 54 | print(" Steering angle0", x[0]) |
| 55 | print(" Steering angle1", x[4]) |
| 56 | print(" Steering angle2", x[8]) |
| 57 | print(" Steering angle3", x[12]) |
| 58 | print(" Steering velocity0", xdot[0]) |
| 59 | print(" Steering velocity1", xdot[4]) |
| 60 | print(" Steering velocity2", xdot[8]) |
| 61 | print(" Steering velocity3", xdot[12]) |
| 62 | print(" Steering accel0", xdot[2]) |
| 63 | print(" Steering accel1", xdot[6]) |
| 64 | print(" Steering accel2", xdot[10]) |
| 65 | print(" Steering accel3", xdot[14]) |
| 66 | print(" Drive accel0", xdot[3]) |
| 67 | print(" Drive accel1", xdot[7]) |
| 68 | print(" Drive accel2", xdot[11]) |
| 69 | print(" Drive accel3", xdot[15]) |
| 70 | print(" Drive velocity0", x[3] * 2 * 0.0254) |
| 71 | print(" Drive velocity1", x[7] * 2 * 0.0254) |
| 72 | print(" Drive velocity2", x[11] * 2 * 0.0254) |
| 73 | print(" Drive velocity3", x[15] * 2 * 0.0254) |
| 74 | print(" Theta ", x[18]) |
| 75 | print(" Omega ", x[21]) |
| 76 | print(" Alpha", xdot[21]) |
| 77 | print(" vx", xdot[16]) |
| 78 | print(" vy", xdot[17]) |
| 79 | print(" ax", xdot[19]) |
| 80 | print(" ay", xdot[20]) |
| 81 | print(" Fdx ", x[22]) |
| 82 | print(" Fdy ", x[23]) |
| 83 | print(" Moment_d", xdot[24]) |
| 84 | |
| 85 | def plot(self): |
| 86 | velocity = numpy.array([[1.0], [0.0]]) |
| 87 | |
| 88 | flip = False |
| 89 | if flip: |
| 90 | module_angles = [0.01 + numpy.pi] * 4 |
| 91 | else: |
| 92 | module_angles = [-0.1, -0.1, 0.1, 0.1] |
| 93 | |
| 94 | X = utils.state_vector( |
| 95 | velocity=velocity, |
| 96 | drive_wheel_velocity=-1.0 if flip else 1.0, |
| 97 | omega=0.0, |
| 98 | module_angles=module_angles, |
| 99 | ) |
| 100 | |
| 101 | self.I = numpy.array([[40.0], [0.0], [40.0], [0.0], [40.0], [0.0], |
| 102 | [40.0], [0.0]]) |
| 103 | |
| 104 | def calc_I(t, x): |
justinT21 | 62d9bde | 2024-09-21 17:02:15 -0700 | [diff] [blame] | 105 | x_goal = numpy.zeros(16) |
justinT21 | 07d41ed | 2024-07-31 21:12:31 -0700 | [diff] [blame] | 106 | |
| 107 | Kp_steer = 15.0 |
| 108 | Kp_drive = 0.0 |
| 109 | Kd_steer = 7.0 |
| 110 | Kd_drive = 0.0 |
| 111 | Idrive = 5.0 if t < 5.0 else 15.0 |
| 112 | |
| 113 | return numpy.array([ |
| 114 | [ |
| 115 | Kd_steer * (x_goal[2] - x[2]) + Kp_steer * |
| 116 | (x_goal[0] - x[0]) |
| 117 | ], |
| 118 | [Idrive], |
| 119 | [ |
| 120 | Kd_steer * (x_goal[6] - x[6]) + Kp_steer * |
| 121 | (x_goal[4] - x[4]) |
| 122 | ], |
| 123 | [Idrive], |
| 124 | [ |
| 125 | Kd_steer * (x_goal[10] - x[10]) + Kp_steer * |
| 126 | (x_goal[8] - x[8]) |
| 127 | ], |
| 128 | [Idrive], |
| 129 | [ |
| 130 | Kd_steer * (x_goal[14] - x[14]) + Kp_steer * |
| 131 | (x_goal[12] - x[12]) |
| 132 | ], |
| 133 | [Idrive], |
| 134 | ]).flatten() |
| 135 | return numpy.array([ |
| 136 | [ |
| 137 | Kd_steer * (x_goal[2] - x[2]) + Kp_steer * |
| 138 | (x_goal[0] - x[0]) |
| 139 | ], |
| 140 | [ |
| 141 | Kd_drive * (x_goal[3] - x[3]) + Kp_drive * |
| 142 | (x_goal[1] - x[1]) |
| 143 | ], |
| 144 | [ |
| 145 | Kd_steer * (x_goal[6] - x[6]) + Kp_steer * |
| 146 | (x_goal[4] - x[4]) |
| 147 | ], |
| 148 | [ |
| 149 | Kd_drive * (x_goal[7] - x[7]) + Kp_drive * |
| 150 | (x_goal[5] - x[5]) |
| 151 | ], |
| 152 | [ |
| 153 | Kd_steer * (x_goal[10] - x[10]) + Kp_steer * |
| 154 | (x_goal[8] - x[8]) |
| 155 | ], |
| 156 | [ |
| 157 | Kd_drive * (x_goal[11] - x[11]) + Kp_drive * |
| 158 | (x_goal[9] - x[9]) |
| 159 | ], |
| 160 | [ |
| 161 | Kd_steer * (x_goal[14] - x[14]) + Kp_steer * |
| 162 | (x_goal[12] - x[12]) |
| 163 | ], |
| 164 | [ |
| 165 | Kd_drive * (x_goal[15] - x[15]) + Kp_drive * |
| 166 | (x_goal[13] - x[13]) |
| 167 | ], |
| 168 | ]).flatten() |
| 169 | |
| 170 | t_eval = numpy.arange(0, 10.0, 0.005) |
| 171 | self.wrap(dynamics) |
| 172 | result = scipy.integrate.solve_ivp( |
| 173 | lambda t, x: self.swerve_physics(x, calc_I(t, x)).flatten(), |
| 174 | [0, t_eval[-1]], |
| 175 | X.flatten(), |
| 176 | t_eval=t_eval, |
| 177 | ) |
| 178 | print("Function evaluations", result.nfev) |
| 179 | |
| 180 | # continue |
| 181 | print(result.y.shape) |
| 182 | print(result.t.shape) |
| 183 | xdot = numpy.zeros(result.y.shape) |
| 184 | print("shape", xdot.shape) |
| 185 | for i in range(xdot.shape[1]): |
| 186 | xdot[:, i] = self.swerve_physics(result.y[:, i], self.I)[:, 0] |
| 187 | |
| 188 | for i in range(2): |
| 189 | print(f"For t {i * 0.005}") |
| 190 | self.print_state(self.swerve_physics, self.I, result.y[:, i]) |
| 191 | |
| 192 | def ev(fn, Y): |
| 193 | return [fn(Y[:, i], self.I)[0, 0] for i in range(Y.shape[1])] |
| 194 | |
| 195 | fig, axs = pylab.subplots(2) |
| 196 | axs[0].plot(result.t, result.y[0, :], label="steer0") |
| 197 | axs[0].plot(result.t, result.y[4, :], label="steer1") |
| 198 | axs[0].plot(result.t, result.y[8, :], label="steer2") |
| 199 | axs[0].plot(result.t, result.y[12, :], label="steer3") |
| 200 | axs[0].legend() |
| 201 | axs[1].plot(result.t, result.y[2, :], label="steer_velocity0") |
| 202 | axs[1].plot(result.t, result.y[6, :], label="steer_velocity1") |
| 203 | axs[1].plot(result.t, result.y[10, :], label="steer_velocity2") |
| 204 | axs[1].plot(result.t, result.y[14, :], label="steer_velocity3") |
| 205 | axs[1].legend() |
| 206 | |
| 207 | fig, axs = pylab.subplots(2) |
| 208 | for i in range(len(axs)): |
| 209 | axs[i].plot(result.t, |
| 210 | ev(self.slip_angle[i], result.y), |
| 211 | label=f"slip_angle{i}") |
| 212 | axs[i].plot(result.t, |
| 213 | ev(self.slip_ratio[i], result.y), |
| 214 | label=f"slip_ratio{i}") |
| 215 | axs[i].plot(result.t, |
| 216 | xdot[2 + i * 4, :], |
| 217 | label=f'steering_accel{i}') |
| 218 | axs[i].legend() |
| 219 | |
| 220 | fig, axs = pylab.subplots(3) |
| 221 | axs[0].plot(result.t, result.y[3, :], label="drive_velocity0") |
| 222 | axs[0].plot(result.t, result.y[7, :], label="drive_velocity1") |
| 223 | axs[0].plot(result.t, result.y[11, :], label="drive_velocity2") |
| 224 | axs[0].plot(result.t, result.y[15, :], label="drive_velocity3") |
| 225 | axs[0].legend() |
| 226 | |
| 227 | axs[1].plot(result.t, result.y[20, :], label="vy") |
| 228 | axs[1].plot(result.t, result.y[21, :], label="omega") |
| 229 | axs[1].plot(result.t, xdot[20, :], label="ay") |
| 230 | axs[1].plot(result.t, xdot[21, :], label="alpha") |
| 231 | axs[1].legend() |
| 232 | |
| 233 | axs[2].plot(result.t, result.y[19, :], label="vx") |
| 234 | axs[2].plot(result.t, xdot[19, :], label="ax") |
| 235 | |
| 236 | axs[2].plot(result.t, |
| 237 | numpy.hypot(result.y[19, :], result.y[20, :]), |
| 238 | label="speed") |
| 239 | axs[2].legend() |
| 240 | |
| 241 | pylab.figure() |
| 242 | U_control = numpy.zeros((8, 1)) |
| 243 | |
| 244 | for i in range(numpy.shape(result.t)[0]): |
| 245 | U_control = numpy.hstack(( |
| 246 | U_control, |
| 247 | numpy.reshape( |
justinT21 | 62d9bde | 2024-09-21 17:02:15 -0700 | [diff] [blame] | 248 | calc_I(result.t[i], result.y[:, i]), |
justinT21 | 07d41ed | 2024-07-31 21:12:31 -0700 | [diff] [blame] | 249 | (8, 1), |
| 250 | ), |
| 251 | )) |
| 252 | |
| 253 | U_control = numpy.delete(U_control, 0, 1) |
| 254 | |
| 255 | pylab.plot(result.t, U_control[0, :], label="Is0") |
| 256 | pylab.plot(result.t, U_control[1, :], label="Id0") |
| 257 | pylab.legend() |
| 258 | |
| 259 | pylab.show() |
| 260 | |
| 261 | |
| 262 | if __name__ == "__main__": |
| 263 | debug = PhysicsDebug() |
| 264 | debug.plot() |