Merge "Compensate steer coupling torque from velocity cost" into main
diff --git a/frc971/control_loops/swerve/casadi_velocity_mpc.py b/frc971/control_loops/swerve/casadi_velocity_mpc.py
index 8b97933..239f49a 100644
--- a/frc971/control_loops/swerve/casadi_velocity_mpc.py
+++ b/frc971/control_loops/swerve/casadi_velocity_mpc.py
@@ -233,10 +233,13 @@
 
         # TODO(austin): Don't penalize torque steering current.
         for i in range(4):
+            Is = U[2 * i + 0]
+            Id = U[2 * i + 1]
             # Steer
-            J += U[2 * i + 0] * U[2 * i + 0] / 100000.0
+            J += ((Is + dynamics.STEER_CURRENT_COUPLING_FACTOR * Id)**
+                  2.0) / 100000.0
             # Drive
-            J += U[2 * i + 1] * U[2 * i + 1] / 1000.0
+            J += Id * Id / 1000.0
 
         return casadi.Function("Jn", [X, U, R], [J])
 
diff --git a/frc971/control_loops/swerve/generate_physics.cc b/frc971/control_loops/swerve/generate_physics.cc
index e223b74..3c4eaf6 100644
--- a/frc971/control_loops/swerve/generate_physics.cc
+++ b/frc971/control_loops/swerve/generate_physics.cc
@@ -617,6 +617,16 @@
                                               sub(integer(1), div(rb1_, rp_)))),
                             div(rw_, rb2_))))))))));
     result_py.emplace_back("");
+    result_py.emplace_back("# Is = STEER_CURRENT_COUPLING_FACTOR * Id");
+    result_py.emplace_back(absl::Substitute(
+        "STEER_CURRENT_COUPLING_FACTOR = $0",
+        ccode(*(neg(
+            mul(div(Gs_, Kts_),
+                mul(div(Ktd_, mul(Gd_, rw_)),
+                    neg(mul(add(neg(wb_), mul(add(rs_, rp_),
+                                              sub(integer(1), div(rb1_, rp_)))),
+                            div(rw_, rb2_))))))))));
+    result_py.emplace_back("");
 
     result_py.emplace_back("# Returns the derivative of our state vector");
     result_py.emplace_back("# [thetas0, thetad0, omegas0, omegad0,");
diff --git a/frc971/control_loops/swerve/physics_test.py b/frc971/control_loops/swerve/physics_test.py
index 9aa6457..0a3fdb6 100644
--- a/frc971/control_loops/swerve/physics_test.py
+++ b/frc971/control_loops/swerve/physics_test.py
@@ -582,6 +582,23 @@
             self.assertLess(xdot[dynamics.STATE_OMEGAD2, 0], -100.0)
             self.assertLess(xdot[dynamics.STATE_OMEGAD3, 0], -100.0)
 
+    def test_steer_coupling(self):
+        """Tests that the steer coupling factor cancels out steer coupling torque."""
+        steer_I = numpy.array(
+            [[dynamics.STEER_CURRENT_COUPLING_FACTOR * 10.0], [10.0]] * 4)
+
+        X = utils.state_vector(
+            velocity=numpy.array([[0.0], [0.0]]),
+            omega=0.0,
+        )
+        X_velocity = self.to_velocity_state(X)
+        Xdot = self.velocity_swerve_physics(X_velocity, steer_I)
+
+        self.assertAlmostEqual(Xdot[dynamics.VELOCITY_STATE_OMEGAS0, 0], 0.0)
+        self.assertAlmostEqual(Xdot[dynamics.VELOCITY_STATE_OMEGAS1, 0], 0.0)
+        self.assertAlmostEqual(Xdot[dynamics.VELOCITY_STATE_OMEGAS2, 0], 0.0)
+        self.assertAlmostEqual(Xdot[dynamics.VELOCITY_STATE_OMEGAS3, 0], 0.0)
+
 
 if __name__ == "__main__":
     unittest.main()