blob: a4e43e1d0cb802c7c3ef27e6bea28eede8de1ba4 [file] [log] [blame]
James Kuszmaulc68b3502024-10-09 07:56:18 -07001#ifndef FRC971_CONTROL_LOOPS_SWERVE_INVERSE_KINEMATICS_H_
2#define FRC971_CONTROL_LOOPS_SWERVE_INVERSE_KINEMATICS_H_
3#include "aos/util/math.h"
4#include "frc971/control_loops/swerve/simplified_dynamics.h"
5namespace frc971::control_loops::swerve {
6// Class to do straightforwards inverse kinematics of a swerve drivebase. This
7// is meant largely as a sanity-check/initializer for more sophisticated
8// methods. This calculates which directions the modules must be pointed to
9// cause them to be pointed directly along the direction of motion of the
10// drivebase. Accounting for slip angles and the such must be done as part of
11// more sophisticated inverse dynamics.
12template <typename Scalar>
13class InverseKinematics {
14 public:
15 using ModuleParams = SimplifiedDynamics<Scalar>::ModuleParams;
16 using Parameters = SimplifiedDynamics<Scalar>::Parameters;
17 using States = SimplifiedDynamics<Scalar>::States;
18 using State = SimplifiedDynamics<Scalar>::template VelocityState<Scalar>;
19 InverseKinematics(const Parameters &params) : params_(params) {}
20
21 // Uses kVx, kVy, kTheta, and kOmega from the input goal state for the
22 // absolute kinematics. Also uses the specified theta values to bias theta
23 // output values towards the current state (i.e., if the module 0 theta is
24 // currently 0 and we are asked to drive straight backwards, this will prefer
25 // a theta of zero rather than a theta of pi).
26 State Solve(const State &goal) {
27 State result = goal;
28 for (size_t module_index = 0; module_index < params_.modules.size();
29 ++module_index) {
30 SolveModule(goal, params_.modules[module_index].position,
31 &result(States::kThetas0 + 2 * module_index),
32 &result(States::kOmegas0 + 2 * module_index));
33 }
34 return result;
35 }
36
37 void SolveModule(const State &goal,
38 const Eigen::Matrix<Scalar, 2, 1> &module_position,
39 Scalar *module_theta, Scalar *module_omega) {
40 const Scalar vx = goal(States::kVx);
41 const Scalar vy = goal(States::kVy);
42 const Scalar omega = goal(States::kOmega);
43 // module_velocity_in_robot_frame = R(-theta) * robot_vel +
44 // omega.cross(module_position);
45 // module_vel_x = (cos(-theta) * vx - sin(-theta) * vy) - omega * module_y
46 // module_vel_y = (sin(-theta) * vx + cos(-theta) * vy) + omega * module_x
47 // module_theta = atan2(module_vel_y, module_vel_x)
48 // module_omega = datan2(module_vel_y, module_vel_x) / dt
49 // datan2(y, x) / dt = (x * dy/dt - y * dx / dt) / (x^2 + y^2)
50 // robot accelerations are assumed to be zero.
51 // dmodule_vel_x / dt = (sin(-theta) * vx + cos(-theta) * vy) * omega
52 // dmodule_vel_y / dt = (-cos(-theta) * vx + sin(-theta) * vy) * omega
53 const Scalar ctheta = std::cos(-goal(States::kTheta));
54 const Scalar stheta = std::sin(-goal(States::kTheta));
55 const Scalar module_vel_x =
56 (ctheta * vx - stheta * vy) - omega * module_position.y();
57 const Scalar module_vel_y =
58 (stheta * vx + ctheta * vy) + omega * module_position.x();
59 const Scalar nominal_module_theta = atan2(module_vel_y, module_vel_x);
60 // If the current module theta is more than 90 deg from the desired theta,
61 // flip the desired theta by 180 deg.
62 if (std::abs(aos::math::DiffAngle(nominal_module_theta, *module_theta)) >
63 M_PI_2) {
64 *module_theta = aos::math::NormalizeAngle(nominal_module_theta + M_PI);
65 } else {
66 *module_theta = nominal_module_theta;
67 }
68 const Scalar module_accel_x = (stheta * vx + ctheta * vy) * omega;
69 const Scalar module_accel_y = (-ctheta * vx + stheta * vy) * omega;
70 const Scalar module_vel_norm_squared =
71 (module_vel_x * module_vel_x + module_vel_y * module_vel_y);
72 if (module_vel_norm_squared < 1e-5) {
73 // Prevent poor conditioning of module velocities at near-zero speeds.
74 *module_omega = 0.0;
75 } else {
76 *module_omega =
77 (module_vel_x * module_accel_y - module_vel_y * module_accel_x) /
78 module_vel_norm_squared;
79 }
80 }
81
82 private:
83 Parameters params_;
84};
85} // namespace frc971::control_loops::swerve
86#endif // FRC971_CONTROL_LOOPS_SWERVE_INVERSE_KINEMATICS_H_