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