blob: d8c1321f53e28b6700a41e50ba8a199cc42216d9 [file] [log] [blame]
justinT21446e4f62024-06-16 22:36:10 -07001#include <symengine/add.h>
2#include <symengine/matrix.h>
3#include <symengine/number.h>
4#include <symengine/printers.h>
5#include <symengine/real_double.h>
6#include <symengine/simplify.h>
7#include <symengine/solve.h>
8#include <symengine/symbol.h>
9
10#include <array>
11#include <cmath>
12#include <numbers>
13#include <utility>
14
15#include "absl/strings/str_format.h"
16#include "absl/strings/str_join.h"
17#include "absl/strings/str_replace.h"
18#include "absl/strings/substitute.h"
19#include "gflags/gflags.h"
20#include "glog/logging.h"
21
22#include "aos/init.h"
23#include "aos/util/file.h"
24#include "frc971/control_loops/swerve/motors.h"
25
26DEFINE_string(output_base, "",
27 "Path to strip off the front of the output paths.");
28DEFINE_string(cc_output_path, "", "Path to write generated header code to");
29DEFINE_string(h_output_path, "", "Path to write generated cc code to");
justinT21942892b2024-07-02 22:33:50 -070030DEFINE_string(py_output_path, "", "Path to write generated py code to");
justinT21446e4f62024-06-16 22:36:10 -070031
32DEFINE_bool(symbolic, false, "If true, write everything out symbolically.");
33
justinT21942892b2024-07-02 22:33:50 -070034using SymEngine::abs;
justinT21446e4f62024-06-16 22:36:10 -070035using SymEngine::add;
36using SymEngine::atan2;
37using SymEngine::Basic;
38using SymEngine::ccode;
39using SymEngine::cos;
40using SymEngine::DenseMatrix;
41using SymEngine::div;
42using SymEngine::Inf;
43using SymEngine::integer;
44using SymEngine::map_basic_basic;
45using SymEngine::minus_one;
46using SymEngine::neg;
47using SymEngine::NegInf;
48using SymEngine::pow;
49using SymEngine::RCP;
50using SymEngine::real_double;
51using SymEngine::RealDouble;
52using SymEngine::Set;
53using SymEngine::simplify;
54using SymEngine::sin;
55using SymEngine::solve;
56using SymEngine::symbol;
57using SymEngine::Symbol;
58
59namespace frc971::control_loops::swerve {
60
61// State per module.
62struct Module {
63 RCP<const Symbol> Is;
64
65 RCP<const Symbol> Id;
66
67 RCP<const Symbol> thetas;
68 RCP<const Symbol> omegas;
69 RCP<const Symbol> alphas;
70 RCP<const Basic> alphas_eqn;
71
72 RCP<const Symbol> thetad;
73 RCP<const Symbol> omegad;
74 RCP<const Symbol> alphad;
75 RCP<const Basic> alphad_eqn;
76
77 // Acceleration contribution from this module.
78 DenseMatrix accel;
79 RCP<const Basic> angular_accel;
80};
81
82class SwerveSimulation {
83 public:
84 SwerveSimulation() : drive_motor_(KrakenFOC()), steer_motor_(KrakenFOC()) {
85 auto fx = symbol("fx");
86 auto fy = symbol("fy");
87 auto moment = symbol("moment");
88
89 if (FLAGS_symbolic) {
90 Cx_ = symbol("Cx");
91 Cy_ = symbol("Cy");
92
93 r_w_ = symbol("r_w_");
94
95 m_ = symbol("m");
96 J_ = symbol("J");
97
98 Gd1_ = symbol("Gd1");
99 rs_ = symbol("rs");
100 rp_ = symbol("rp");
101 Gd2_ = symbol("Gd2");
102
103 rb1_ = symbol("rb1");
104 rb2_ = symbol("rb2");
105
106 Gd2_ = symbol("Gd3");
107 Gd_ = symbol("Gd");
108
109 Js_ = symbol("Js");
110
111 Gs_ = symbol("Gs");
112 wb_ = symbol("wb");
113
114 Jdm_ = symbol("Jdm");
115 Jsm_ = symbol("Jsm");
116 Kts_ = symbol("Kts");
117 Ktd_ = symbol("Ktd");
118
119 robot_width_ = symbol("robot_width");
120
121 caster_ = symbol("caster");
122 contact_patch_length_ = symbol("Lcp");
123 } else {
124 Cx_ = real_double(5 * 9.8 / 0.05 / 4.0);
125 Cy_ = real_double(5 * 9.8 / 0.05 / 4.0);
126
127 r_w_ = real_double(2 * 0.0254);
128
129 m_ = real_double(25.0); // base is 20 kg without battery
130 J_ = real_double(6.0);
131
132 Gd1_ = real_double(12.0 / 42.0);
133 rs_ = real_double(28.0 / 20.0 / 2.0);
134 rp_ = real_double(18.0 / 20.0 / 2.0);
135 Gd2_ = div(rs_, rp_);
136
137 // 15 / 45 bevel ratio, calculated using python script ported over to
138 // GetBevelPitchRadius(double
139 // TODO(Justin): Use the function instead of computed constantss
140 rb1_ = real_double(0.3805473);
141 rb2_ = real_double(1.14164);
142
143 Gd3_ = div(rb1_, rb2_);
144 Gd_ = mul(mul(Gd1_, Gd2_), Gd3_);
145
146 Js_ = real_double(0.1);
147
148 Gs_ = real_double(35.0 / 468.0);
149 wb_ = real_double(0.725);
150
151 Jdm_ = real_double(drive_motor_.motor_inertia);
152 Jsm_ = real_double(steer_motor_.motor_inertia);
153 Kts_ = real_double(steer_motor_.Kt);
154 Ktd_ = real_double(drive_motor_.Kt);
155
156 robot_width_ = real_double(24.75 * 0.0254);
157
158 caster_ = real_double(0.01);
159 contact_patch_length_ = real_double(0.02);
160 }
161
162 x_ = symbol("x");
163 y_ = symbol("y");
164 theta_ = symbol("theta");
165
166 vx_ = symbol("vx");
167 vy_ = symbol("vy");
168 omega_ = symbol("omega");
169
170 ax_ = symbol("ax");
171 ay_ = symbol("ay");
172 atheta_ = symbol("atheta");
173
174 // Now, compute the accelerations due to the disturbance forces.
175 angular_accel_ = div(moment, J_);
176 DenseMatrix external_accel = DenseMatrix(2, 1, {div(fx, m_), div(fy, m_)});
177
178 // And compute the physics contributions from each module.
179 modules_[0] = ModulePhysics(
180 0, DenseMatrix(
181 2, 1,
182 {div(robot_width_, integer(2)), div(robot_width_, integer(2))}));
183 modules_[1] =
184 ModulePhysics(1, DenseMatrix(2, 1,
185 {div(robot_width_, integer(-2)),
186 div(robot_width_, integer(2))}));
187 modules_[2] =
188 ModulePhysics(2, DenseMatrix(2, 1,
189 {div(robot_width_, integer(-2)),
190 div(robot_width_, integer(-2))}));
191 modules_[3] =
192 ModulePhysics(3, DenseMatrix(2, 1,
193 {div(robot_width_, integer(2)),
194 div(robot_width_, integer(-2))}));
195
196 // And convert them into the overall robot contribution.
197 DenseMatrix temp0 = DenseMatrix(2, 1);
198 DenseMatrix temp1 = DenseMatrix(2, 1);
199 DenseMatrix temp2 = DenseMatrix(2, 1);
200 accel_ = DenseMatrix(2, 1);
201
202 add_dense_dense(modules_[0].accel, external_accel, temp0);
203 add_dense_dense(temp0, modules_[1].accel, temp1);
204 add_dense_dense(temp1, modules_[2].accel, temp2);
205 add_dense_dense(temp2, modules_[3].accel, accel_);
206
207 angular_accel_ = add(angular_accel_, modules_[0].angular_accel);
208 angular_accel_ = add(angular_accel_, modules_[1].angular_accel);
209 angular_accel_ = add(angular_accel_, modules_[2].angular_accel);
210 angular_accel_ = simplify(add(angular_accel_, modules_[3].angular_accel));
211
212 VLOG(1) << "accel(0, 0) = " << ccode(*accel_.get(0, 0));
213 VLOG(1) << "accel(1, 0) = " << ccode(*accel_.get(1, 0));
214 VLOG(1) << "angular_accel = " << ccode(*angular_accel_);
215 }
216
justinT21942892b2024-07-02 22:33:50 -0700217 // Writes the physics out to the provided .py path.
218 void WritePy(std::string_view py_path) {
219 std::vector<std::string> result_py;
220
221 result_py.emplace_back("#!/usr/bin/python3");
222 result_py.emplace_back("");
223 result_py.emplace_back("import numpy");
224 result_py.emplace_back("import math");
225 result_py.emplace_back("from math import sin, cos, fabs");
226 result_py.emplace_back("");
227
228 result_py.emplace_back("def atan2(y, x):");
229 result_py.emplace_back(" if x < 0:");
230 result_py.emplace_back(" return -math.atan2(y, x)");
231 result_py.emplace_back(" else:");
232 result_py.emplace_back(" return math.atan2(y, x)");
233
234 result_py.emplace_back("def swerve_physics(t, X, U_func):");
235 // result_py.emplace_back(" print(X)");
236 result_py.emplace_back(" result = numpy.empty([25, 1])");
237 result_py.emplace_back(" X = X.reshape(25, 1)");
238 result_py.emplace_back(" U = U_func(X)");
239 result_py.emplace_back("");
240
241 // Start by writing out variables matching each of the symbol names we use
242 // so we don't have to modify the computed equations too much.
243 for (size_t m = 0; m < kNumModules; ++m) {
244 result_py.emplace_back(
245 absl::Substitute(" thetas$0 = X[$1, 0]", m, m * 4));
246 result_py.emplace_back(
247 absl::Substitute(" omegas$0 = X[$1, 0]", m, m * 4 + 2));
248 result_py.emplace_back(
249 absl::Substitute(" omegad$0 = X[$1, 0]", m, m * 4 + 3));
250 }
251
252 result_py.emplace_back(
253 absl::Substitute(" theta = X[$0, 0]", kNumModules * 4 + 2));
254 result_py.emplace_back(
255 absl::Substitute(" vx = X[$0, 0]", kNumModules * 4 + 3));
256 result_py.emplace_back(
257 absl::Substitute(" vy = X[$0, 0]", kNumModules * 4 + 4));
258 result_py.emplace_back(
259 absl::Substitute(" omega = X[$0, 0]", kNumModules * 4 + 5));
260
261 result_py.emplace_back(
262 absl::Substitute(" fx = X[$0, 0]", kNumModules * 4 + 6));
263 result_py.emplace_back(
264 absl::Substitute(" fy = X[$0, 0]", kNumModules * 4 + 7));
265 result_py.emplace_back(
266 absl::Substitute(" moment = X[$0, 0]", kNumModules * 4 + 8));
267
268 // Now do the same for the inputs.
269 for (size_t m = 0; m < kNumModules; ++m) {
270 result_py.emplace_back(absl::Substitute(" Is$0 = U[$1, 0]", m, m * 2));
271 result_py.emplace_back(
272 absl::Substitute(" Id$0 = U[$1, 0]", m, m * 2 + 1));
273 }
274
275 result_py.emplace_back("");
276
277 // And then write out the derivative of each state.
278 for (size_t m = 0; m < kNumModules; ++m) {
279 result_py.emplace_back(
280 absl::Substitute(" result[$0, 0] = omegas$1", m * 4, m));
281 result_py.emplace_back(
282 absl::Substitute(" result[$0, 0] = omegad$1", m * 4 + 1, m));
283
284 result_py.emplace_back(absl::Substitute(
285 " result[$0, 0] = $1", m * 4 + 2, ccode(*modules_[m].alphas_eqn)));
286 result_py.emplace_back(absl::Substitute(
287 " result[$0, 0] = $1", m * 4 + 3, ccode(*modules_[m].alphad_eqn)));
288 }
289
290 result_py.emplace_back(
291 absl::Substitute(" result[$0, 0] = vx", kNumModules * 4));
292 result_py.emplace_back(
293 absl::Substitute(" result[$0, 0] = vy", kNumModules * 4 + 1));
294 result_py.emplace_back(
295 absl::Substitute(" result[$0, 0] = omega", kNumModules * 4 + 2));
296
297 result_py.emplace_back(absl::Substitute(" result[$0, 0] = $1",
298 kNumModules * 4 + 3,
299 ccode(*accel_.get(0, 0))));
300 result_py.emplace_back(absl::Substitute(" result[$0, 0] = $1",
301 kNumModules * 4 + 4,
302 ccode(*accel_.get(1, 0))));
303 result_py.emplace_back(absl::Substitute(
304 " result[$0, 0] = $1", kNumModules * 4 + 5, ccode(*angular_accel_)));
305
306 result_py.emplace_back(
307 absl::Substitute(" result[$0, 0] = 0.0", kNumModules * 4 + 6));
308 result_py.emplace_back(
309 absl::Substitute(" result[$0, 0] = 0.0", kNumModules * 4 + 7));
310 result_py.emplace_back(
311 absl::Substitute(" result[$0, 0] = 0.0", kNumModules * 4 + 8));
312
313 result_py.emplace_back("");
314 result_py.emplace_back(" return result.reshape(25,)\n");
315
316 aos::util::WriteStringToFileOrDie(py_path, absl::StrJoin(result_py, "\n"));
317 }
318
justinT21446e4f62024-06-16 22:36:10 -0700319 // Writes the physics out to the provided .cc and .h path.
320 void Write(std::string_view cc_path, std::string_view h_path) {
321 std::vector<std::string> result_cc;
322 std::vector<std::string> result_h;
323
324 std::string_view include_guard_stripped = FLAGS_h_output_path;
325 CHECK(absl::ConsumePrefix(&include_guard_stripped, FLAGS_output_base));
326 std::string include_guard =
327 absl::StrReplaceAll(absl::AsciiStrToUpper(include_guard_stripped),
328 {{"/", "_"}, {".", "_"}});
329
330 // Write out the header.
331 result_h.emplace_back(absl::Substitute("#ifndef $0_", include_guard));
332 result_h.emplace_back(absl::Substitute("#define $0_", include_guard));
333 result_h.emplace_back("");
334 result_h.emplace_back("#include <Eigen/Dense>");
335 result_h.emplace_back("");
336 result_h.emplace_back("namespace frc971::control_loops::swerve {");
337 result_h.emplace_back("");
338 result_h.emplace_back("// Returns the derivative of our state vector");
339 result_h.emplace_back("// [thetas0, thetad0, omegas0, omegad0,");
340 result_h.emplace_back("// thetas1, thetad1, omegas1, omegad1,");
341 result_h.emplace_back("// thetas2, thetad2, omegas2, omegad2,");
342 result_h.emplace_back("// thetas3, thetad3, omegas3, omegad3,");
343 result_h.emplace_back("// x, y, theta, vx, vy, omega,");
344 result_h.emplace_back("// Fx, Fy, Moment]");
345 result_h.emplace_back("Eigen::Matrix<double, 25, 1> SwervePhysics(");
346 result_h.emplace_back(
347 " Eigen::Map<const Eigen::Matrix<double, 25, 1>> X,");
348 result_h.emplace_back(
349 " Eigen::Map<const Eigen::Matrix<double, 8, 1>> U);");
350 result_h.emplace_back("");
351 result_h.emplace_back("} // namespace frc971::control_loops::swerve");
352 result_h.emplace_back("");
353 result_h.emplace_back(absl::Substitute("#endif // $0_", include_guard));
354
355 // Write out the .cc
356 result_cc.emplace_back(
357 absl::Substitute("#include \"$0\"", include_guard_stripped));
358 result_cc.emplace_back("");
359 result_cc.emplace_back("#include <cmath>");
360 result_cc.emplace_back("");
361 result_cc.emplace_back("namespace frc971::control_loops::swerve {");
362 result_cc.emplace_back("");
363 result_cc.emplace_back("Eigen::Matrix<double, 25, 1> SwervePhysics(");
364 result_cc.emplace_back(
365 " Eigen::Map<const Eigen::Matrix<double, 25, 1>> X,");
366 result_cc.emplace_back(
367 " Eigen::Map<const Eigen::Matrix<double, 8, 1>> U) {");
368 result_cc.emplace_back(" Eigen::Matrix<double, 25, 1> result;");
369
370 // Start by writing out variables matching each of the symbol names we use
371 // so we don't have to modify the computed equations too much.
372 for (size_t m = 0; m < kNumModules; ++m) {
373 result_cc.emplace_back(
374 absl::Substitute(" const double thetas$0 = X($1, 0);", m, m * 4));
375 result_cc.emplace_back(absl::Substitute(
376 " const double omegas$0 = X($1, 0);", m, m * 4 + 2));
377 result_cc.emplace_back(absl::Substitute(
378 " const double omegad$0 = X($1, 0);", m, m * 4 + 3));
379 }
380
381 result_cc.emplace_back(absl::Substitute(" const double theta = X($0, 0);",
382 kNumModules * 4 + 2));
383 result_cc.emplace_back(
384 absl::Substitute(" const double vx = X($0, 0);", kNumModules * 4 + 3));
385 result_cc.emplace_back(
386 absl::Substitute(" const double vy = X($0, 0);", kNumModules * 4 + 4));
387 result_cc.emplace_back(absl::Substitute(" const double omega = X($0, 0);",
388 kNumModules * 4 + 5));
389
390 result_cc.emplace_back(
391 absl::Substitute(" const double fx = X($0, 0);", kNumModules * 4 + 6));
392 result_cc.emplace_back(
393 absl::Substitute(" const double fy = X($0, 0);", kNumModules * 4 + 7));
394 result_cc.emplace_back(absl::Substitute(" const double moment = X($0, 0);",
395 kNumModules * 4 + 8));
396
397 // Now do the same for the inputs.
398 for (size_t m = 0; m < kNumModules; ++m) {
399 result_cc.emplace_back(
400 absl::Substitute(" const double Is$0 = U($1, 0);", m, m * 2));
401 result_cc.emplace_back(
402 absl::Substitute(" const double Id$0 = U($1, 0);", m, m * 2 + 1));
403 }
404
405 result_cc.emplace_back("");
406
407 // And then write out the derivative of each state.
408 for (size_t m = 0; m < kNumModules; ++m) {
409 result_cc.emplace_back(
410 absl::Substitute(" result($0, 0) = omegas$1;", m * 4, m));
411 result_cc.emplace_back(
412 absl::Substitute(" result($0, 0) = omegad$1;", m * 4 + 1, m));
413
414 result_cc.emplace_back(absl::Substitute(
415 " result($0, 0) = $1;", m * 4 + 2, ccode(*modules_[m].alphas_eqn)));
416 result_cc.emplace_back(absl::Substitute(
417 " result($0, 0) = $1;", m * 4 + 3, ccode(*modules_[m].alphad_eqn)));
418 }
419
420 result_cc.emplace_back(
421 absl::Substitute(" result($0, 0) = omega;", kNumModules * 4));
422 result_cc.emplace_back(
423 absl::Substitute(" result($0, 0) = vx;", kNumModules * 4 + 1));
424 result_cc.emplace_back(
425 absl::Substitute(" result($0, 0) = vy;", kNumModules * 4 + 2));
426
427 result_cc.emplace_back(absl::Substitute(
428 " result($0, 0) = $1;", kNumModules * 4 + 3, ccode(*angular_accel_)));
429 result_cc.emplace_back(absl::Substitute(" result($0, 0) = $1;",
430 kNumModules * 4 + 4,
431 ccode(*accel_.get(0, 0))));
432 result_cc.emplace_back(absl::Substitute(" result($0, 0) = $1;",
433 kNumModules * 4 + 5,
434 ccode(*accel_.get(1, 0))));
435
436 result_cc.emplace_back(
437 absl::Substitute(" result($0, 0) = 0.0;", kNumModules * 4 + 6));
438 result_cc.emplace_back(
439 absl::Substitute(" result($0, 0) = 0.0;", kNumModules * 4 + 7));
440 result_cc.emplace_back(
441 absl::Substitute(" result($0, 0) = 0.0;", kNumModules * 4 + 8));
442
443 result_cc.emplace_back("");
444 result_cc.emplace_back(" return result;");
445 result_cc.emplace_back("}");
446 result_cc.emplace_back("");
447 result_cc.emplace_back("} // namespace frc971::control_loops::swerve");
448
449 aos::util::WriteStringToFileOrDie(cc_path, absl::StrJoin(result_cc, "\n"));
450 aos::util::WriteStringToFileOrDie(h_path, absl::StrJoin(result_h, "\n"));
451 }
452
453 private:
454 static constexpr uint8_t kNumModules = 4;
455
456 Module ModulePhysics(const int m, DenseMatrix mounting_location) {
457 VLOG(1) << "Solving module " << m;
458
459 Module result;
460
461 result.Is = symbol(absl::StrFormat("Is%u", m));
462 result.Id = symbol(absl::StrFormat("Id%u", m));
463
464 RCP<const Symbol> thetamd = symbol(absl::StrFormat("theta_md%u", m));
465 RCP<const Symbol> omegamd = symbol(absl::StrFormat("omega_md%u", m));
466 RCP<const Symbol> alphamd = symbol(absl::StrFormat("alpha_md%u", m));
467
468 result.thetas = symbol(absl::StrFormat("thetas%u", m));
469 result.omegas = symbol(absl::StrFormat("omegas%u", m));
470 result.alphas = symbol(absl::StrFormat("alphas%u", m));
471
472 result.thetad = symbol(absl::StrFormat("thetad%u", m));
473 result.omegad = symbol(absl::StrFormat("omegad%u", m));
474 result.alphad = symbol(absl::StrFormat("alphad%u", m));
475
476 // Velocity of the module in field coordinates
justinT21942892b2024-07-02 22:33:50 -0700477 DenseMatrix robot_velocity = DenseMatrix(2, 1);
478 mul_dense_dense(R(theta_), DenseMatrix(2, 1, {vx_, vy_}), robot_velocity);
justinT21446e4f62024-06-16 22:36:10 -0700479 VLOG(1) << "robot velocity: " << robot_velocity.__str__();
480
481 // Velocity of the contact patch in field coordinates
482 DenseMatrix temp_matrix = DenseMatrix(2, 1);
483 DenseMatrix temp_matrix2 = DenseMatrix(2, 1);
484 DenseMatrix contact_patch_velocity = DenseMatrix(2, 1);
485
486 mul_dense_dense(R(theta_), mounting_location, temp_matrix);
487 add_dense_dense(angle_cross(temp_matrix, omega_), robot_velocity,
488 temp_matrix2);
489 mul_dense_dense(R(add(theta_, result.thetas)),
490 DenseMatrix(2, 1, {caster_, integer(0)}), temp_matrix);
491 add_dense_dense(temp_matrix2,
492 angle_cross(temp_matrix, add(omega_, result.omegas)),
493 contact_patch_velocity);
494
495 VLOG(1);
496 VLOG(1) << "contact patch velocity: " << contact_patch_velocity.__str__();
497
498 // Relative velocity of the surface of the wheel to the ground.
499 DenseMatrix wheel_ground_velocity = DenseMatrix(2, 1);
500 mul_dense_dense(R(neg(add(result.thetas, theta_))), contact_patch_velocity,
501 wheel_ground_velocity);
502
503 VLOG(1);
504 VLOG(1) << "wheel ground velocity: " << wheel_ground_velocity.__str__();
505
justinT21942892b2024-07-02 22:33:50 -0700506 RCP<const Basic> slip_angle = neg(atan2(wheel_ground_velocity.get(1, 0),
507 wheel_ground_velocity.get(0, 0)));
justinT21446e4f62024-06-16 22:36:10 -0700508
509 VLOG(1);
510 VLOG(1) << "slip angle: " << slip_angle->__str__();
511
512 RCP<const Basic> slip_ratio =
513 div(sub(mul(r_w_, result.omegad), wheel_ground_velocity.get(0, 0)),
justinT21942892b2024-07-02 22:33:50 -0700514 abs(wheel_ground_velocity.get(0, 0)));
justinT21446e4f62024-06-16 22:36:10 -0700515 VLOG(1);
516 VLOG(1) << "Slip ratio " << slip_ratio->__str__();
517
518 RCP<const Basic> Fwx = simplify(mul(Cx_, slip_ratio));
519 RCP<const Basic> Fwy = simplify(mul(Cy_, slip_angle));
520
521 RCP<const Basic> Ms =
522 mul(Fwy, add(div(contact_patch_length_, integer(3)), caster_));
523 VLOG(1);
524 VLOG(1) << "Ms " << Ms->__str__();
525 VLOG(1);
526 VLOG(1) << "Fwx " << Fwx->__str__();
527 VLOG(1);
528 VLOG(1) << "Fwy " << Fwy->__str__();
529
530 // alphas = ...
531 RCP<const Basic> lhms =
532 mul(add(neg(wb_), mul(add(rs_, rp_), sub(integer(1), div(rb1_, rp_)))),
justinT21942892b2024-07-02 22:33:50 -0700533 mul(div(r_w_, rb2_), neg(Fwx)));
justinT21446e4f62024-06-16 22:36:10 -0700534 RCP<const Basic> lhs = add(add(Ms, div(mul(Jsm_, result.Is), Gs_)), lhms);
535 RCP<const Basic> rhs = add(Jsm_, div(div(Js_, Gs_), Gs_));
536 RCP<const Basic> accel_steer_eqn = simplify(div(lhs, rhs));
537
538 VLOG(1);
539 VLOG(1) << result.alphas->__str__() << " = " << accel_steer_eqn->__str__();
540
541 lhs = sub(mul(sub(div(add(rp_, rs_), rp_), integer(1)), result.omegas),
542 mul(Gd1_, mul(Gd2_, omegamd)));
543 RCP<const Basic> dplanitary_eqn = sub(mul(Gd3_, lhs), result.omegad);
544
545 lhs = sub(mul(sub(div(add(rp_, rs_), rp_), integer(1)), result.alphas),
546 mul(Gd1_, mul(Gd2_, alphamd)));
547 RCP<const Basic> ddplanitary_eqn = sub(mul(Gd3_, lhs), result.alphad);
548
549 RCP<const Basic> drive_eqn = sub(
550 add(mul(neg(Jdm_), div(alphamd, Gd_)), mul(Ktd_, div(result.Id, Gd_))),
justinT21942892b2024-07-02 22:33:50 -0700551 mul(neg(Fwx), r_w_));
justinT21446e4f62024-06-16 22:36:10 -0700552
553 VLOG(1) << "drive_eqn: " << drive_eqn->__str__();
554
555 // Substitute in ddplanitary_eqn so we get rid of alphamd
556 map_basic_basic map;
557 RCP<const Set> reals = interval(NegInf, Inf, true, true);
558 RCP<const Set> solve_solution = solve(ddplanitary_eqn, alphamd, reals);
559 map[alphamd] = solve_solution->get_args()[1]->get_args()[0];
560 VLOG(1) << "temp: " << solve_solution->__str__();
561 RCP<const Basic> drive_eqn_subs = drive_eqn->subs(map);
562
563 map.clear();
564 map[result.alphas] = accel_steer_eqn;
565 RCP<const Basic> drive_eqn_subs2 = drive_eqn_subs->subs(map);
566 RCP<const Basic> drive_eqn_subs3 = simplify(drive_eqn_subs2);
567 VLOG(1) << "drive_eqn simplified: " << drive_eqn_subs3->__str__();
568
569 solve_solution = solve(drive_eqn_subs3, result.alphad, reals);
570
571 RCP<const Basic> drive_accel =
572 simplify(solve_solution->get_args()[1]->get_args()[0]);
573 VLOG(1) << "drive_accel: " << drive_accel->__str__();
574
575 DenseMatrix mat_output = DenseMatrix(2, 1);
576 mul_dense_dense(R(add(theta_, result.thetas)),
577 DenseMatrix(2, 1, {Fwx, Fwy}), mat_output);
578
579 // Comput the resulting force from the module.
580 DenseMatrix F = mat_output;
581
582 RCP<const Basic> torque = simplify(force_cross(mounting_location, F));
583 result.accel = DenseMatrix(2, 1);
584 mul_dense_scalar(F, pow(m_, minus_one), result.accel);
585 result.angular_accel = div(torque, J_);
586 VLOG(1);
587 VLOG(1) << "angular_accel = " << result.angular_accel->__str__();
588
589 VLOG(1);
590 VLOG(1) << "accel(0, 0) = " << result.accel.get(0, 0)->__str__();
591 VLOG(1);
592 VLOG(1) << "accel(1, 0) = " << result.accel.get(1, 0)->__str__();
593
594 result.alphad_eqn = drive_accel;
595 result.alphas_eqn = accel_steer_eqn;
596 return result;
597 }
598
599 DenseMatrix R(const RCP<const Basic> theta) {
600 return DenseMatrix(2, 2,
601 {cos(theta), neg(sin(theta)), sin(theta), cos(theta)});
602 }
603
604 DenseMatrix angle_cross(DenseMatrix a, RCP<const Basic> b) {
605 return DenseMatrix(2, 1, {mul(a.get(1, 0), b), mul(neg(a.get(0, 0)), b)});
606 }
607
608 RCP<const Basic> force_cross(DenseMatrix r, DenseMatrix f) {
609 return sub(mul(r.get(0, 0), f.get(1, 0)), mul(r.get(1, 0), f.get(0, 0)));
610 }
611
612 // z represents the number of teeth per gear, theta is the angle between
613 // shafts(in degrees), D_02 is the pitch diameter of gear 2 and b_2 is the
614 // length of the tooth of gear 2
615 // returns std::pair(r_01, r_02)
616 std::pair<double, double> GetBevelPitchRadius(double z1, double z2,
617 double theta, double D_02,
618 double b_2) {
619 double gamma_1 = std::atan2(z1, z2);
620 double gamma_2 = theta / 180.0 * std::numbers::pi - gamma_1;
621 double R_m = D_02 / 2 / std::sin(gamma_2) - b_2 / 2;
622 return std::pair(R_m * std::cos(gamma_2), R_m * std::sin(gamma_2));
623 }
624
625 Motor drive_motor_;
626 Motor steer_motor_;
627
628 RCP<const Basic> Cx_;
629 RCP<const Basic> Cy_;
630 RCP<const Basic> r_w_;
631 RCP<const Basic> m_;
632 RCP<const Basic> J_;
633 RCP<const Basic> Gd1_;
634 RCP<const Basic> rs_;
635 RCP<const Basic> rp_;
636 RCP<const Basic> Gd2_;
637 RCP<const Basic> rb1_;
638 RCP<const Basic> rb2_;
639 RCP<const Basic> Gd3_;
640 RCP<const Basic> Gd_;
641 RCP<const Basic> Js_;
642 RCP<const Basic> Gs_;
643 RCP<const Basic> wb_;
644 RCP<const Basic> Jdm_;
645 RCP<const Basic> Jsm_;
646 RCP<const Basic> Kts_;
647 RCP<const Basic> Ktd_;
648 RCP<const Basic> robot_width_;
649 RCP<const Basic> caster_;
650 RCP<const Basic> contact_patch_length_;
651 RCP<const Basic> x_;
652 RCP<const Basic> y_;
653 RCP<const Basic> theta_;
654 RCP<const Basic> vx_;
655 RCP<const Basic> vy_;
656 RCP<const Basic> omega_;
657 RCP<const Basic> ax_;
658 RCP<const Basic> ay_;
659 RCP<const Basic> atheta_;
660
661 std::array<Module, kNumModules> modules_;
662
663 DenseMatrix accel_;
664 RCP<const Basic> angular_accel_;
665};
666
667} // namespace frc971::control_loops::swerve
668
669int main(int argc, char **argv) {
670 aos::InitGoogle(&argc, &argv);
671
672 frc971::control_loops::swerve::SwerveSimulation sim;
673
justinT21942892b2024-07-02 22:33:50 -0700674 if (!FLAGS_cc_output_path.empty() && !FLAGS_h_output_path.empty() &&
675 !FLAGS_py_output_path.empty()) {
justinT21446e4f62024-06-16 22:36:10 -0700676 sim.Write(FLAGS_cc_output_path, FLAGS_h_output_path);
justinT21942892b2024-07-02 22:33:50 -0700677 sim.WritePy(FLAGS_py_output_path);
justinT21446e4f62024-06-16 22:36:10 -0700678 }
679
680 return 0;
681}