[wpimath] Make swerve and differential kinematics functions immutable (#8274)

Originally started with just swerve, but expanded to diff and mecanum
(docs only) for parity across the drivetrains. Return value checks are
applied when possible to make migration easier and to error loudly if
people forget.

---------

Co-authored-by: Joseph Eng <91924258+KangarooKoala@users.noreply.github.com>
This commit is contained in:
Gold856
2026-04-17 00:23:32 -04:00
committed by GitHub
parent 8c80cdcf28
commit 056d7bcbbe
34 changed files with 396 additions and 394 deletions

View File

@@ -9,119 +9,119 @@ kEpsilon = 1e-9
def test_optimize():
angle_a = Rotation2d.fromDegrees(45)
ref_a = SwerveModuleVelocity(-2, Rotation2d.fromDegrees(180))
ref_a.optimize(angle_a)
optimized_a = ref_a.optimize(angle_a)
assert ref_a.velocity == pytest.approx(2.0, abs=kEpsilon)
assert ref_a.angle.degrees() == pytest.approx(0.0, abs=kEpsilon)
assert optimized_a.velocity == pytest.approx(2.0, abs=kEpsilon)
assert optimized_a.angle.degrees() == pytest.approx(0.0, abs=kEpsilon)
angle_b = Rotation2d.fromDegrees(-50)
ref_b = SwerveModuleVelocity(4.7, Rotation2d.fromDegrees(41))
ref_b.optimize(angle_b)
optimized_b = ref_b.optimize(angle_b)
assert ref_b.velocity == pytest.approx(-4.7, abs=kEpsilon)
assert ref_b.angle.degrees() == pytest.approx(-139.0, abs=kEpsilon)
assert optimized_b.velocity == pytest.approx(-4.7, abs=kEpsilon)
assert optimized_b.angle.degrees() == pytest.approx(-139.0, abs=kEpsilon)
def test_no_optimize():
angle_a = Rotation2d.fromDegrees(0)
ref_a = SwerveModuleVelocity(2, Rotation2d.fromDegrees(89))
ref_a.optimize(angle_a)
optimized_a = ref_a.optimize(angle_a)
assert ref_a.velocity == pytest.approx(2.0, abs=kEpsilon)
assert ref_a.angle.degrees() == pytest.approx(89.0, abs=kEpsilon)
assert optimized_a.velocity == pytest.approx(2.0, abs=kEpsilon)
assert optimized_a.angle.degrees() == pytest.approx(89.0, abs=kEpsilon)
angle_b = Rotation2d.fromDegrees(0)
ref_b = SwerveModuleVelocity(-2, Rotation2d.fromDegrees(-2))
ref_b.optimize(angle_b)
optimized_b = ref_b.optimize(angle_b)
assert ref_b.velocity == pytest.approx(-2.0, abs=kEpsilon)
assert ref_b.angle.degrees() == pytest.approx(-2.0, abs=kEpsilon)
assert optimized_b.velocity == pytest.approx(-2.0, abs=kEpsilon)
assert optimized_b.angle.degrees() == pytest.approx(-2.0, abs=kEpsilon)
def test_cosine_scaling():
angle_a = Rotation2d.fromDegrees(0)
ref_a = SwerveModuleVelocity(2, Rotation2d.fromDegrees(45))
ref_a.cosineScale(angle_a)
optimized_a = ref_a.cosineScale(angle_a)
assert ref_a.velocity == pytest.approx(math.sqrt(2.0), abs=kEpsilon)
assert ref_a.angle.degrees() == pytest.approx(45.0, abs=kEpsilon)
assert optimized_a.velocity == pytest.approx(math.sqrt(2.0), abs=kEpsilon)
assert optimized_a.angle.degrees() == pytest.approx(45.0, abs=kEpsilon)
angle_b = Rotation2d.fromDegrees(45)
ref_b = SwerveModuleVelocity(2, Rotation2d.fromDegrees(45))
ref_b.cosineScale(angle_b)
optimized_b = ref_b.cosineScale(angle_b)
assert ref_b.velocity == pytest.approx(2.0, abs=kEpsilon)
assert ref_b.angle.degrees() == pytest.approx(45.0, abs=kEpsilon)
assert optimized_b.velocity == pytest.approx(2.0, abs=kEpsilon)
assert optimized_b.angle.degrees() == pytest.approx(45.0, abs=kEpsilon)
angle_c = Rotation2d.fromDegrees(-45)
ref_c = SwerveModuleVelocity(2, Rotation2d.fromDegrees(45))
ref_c.cosineScale(angle_c)
optimized_c = ref_c.cosineScale(angle_c)
assert ref_c.velocity == pytest.approx(0.0, abs=kEpsilon)
assert ref_c.angle.degrees() == pytest.approx(45.0, abs=kEpsilon)
assert optimized_c.velocity == pytest.approx(0.0, abs=kEpsilon)
assert optimized_c.angle.degrees() == pytest.approx(45.0, abs=kEpsilon)
angle_d = Rotation2d.fromDegrees(135)
ref_d = SwerveModuleVelocity(2, Rotation2d.fromDegrees(45))
ref_d.cosineScale(angle_d)
optimized_d = ref_d.cosineScale(angle_d)
assert ref_d.velocity == pytest.approx(0.0, abs=kEpsilon)
assert ref_d.angle.degrees() == pytest.approx(45.0, abs=kEpsilon)
assert optimized_d.velocity == pytest.approx(0.0, abs=kEpsilon)
assert optimized_d.angle.degrees() == pytest.approx(45.0, abs=kEpsilon)
angle_e = Rotation2d.fromDegrees(-135)
ref_e = SwerveModuleVelocity(2, Rotation2d.fromDegrees(45))
ref_e.cosineScale(angle_e)
optimized_e = ref_e.cosineScale(angle_e)
assert ref_e.velocity == pytest.approx(-2.0, abs=kEpsilon)
assert ref_e.angle.degrees() == pytest.approx(45.0, abs=kEpsilon)
assert optimized_e.velocity == pytest.approx(-2.0, abs=kEpsilon)
assert optimized_e.angle.degrees() == pytest.approx(45.0, abs=kEpsilon)
angle_f = Rotation2d.fromDegrees(180)
ref_f = SwerveModuleVelocity(2, Rotation2d.fromDegrees(45))
ref_f.cosineScale(angle_f)
optimized_f = ref_f.cosineScale(angle_f)
assert ref_f.velocity == pytest.approx(-math.sqrt(2.0), abs=kEpsilon)
assert ref_f.angle.degrees() == pytest.approx(45.0, abs=kEpsilon)
assert optimized_f.velocity == pytest.approx(-math.sqrt(2.0), abs=kEpsilon)
assert optimized_f.angle.degrees() == pytest.approx(45.0, abs=kEpsilon)
angle_g = Rotation2d.fromDegrees(0)
ref_g = SwerveModuleVelocity(-2, Rotation2d.fromDegrees(45))
ref_g.cosineScale(angle_g)
optimized_g = ref_g.cosineScale(angle_g)
assert ref_g.velocity == pytest.approx(-math.sqrt(2.0), abs=kEpsilon)
assert ref_g.angle.degrees() == pytest.approx(45.0, abs=kEpsilon)
assert optimized_g.velocity == pytest.approx(-math.sqrt(2.0), abs=kEpsilon)
assert optimized_g.angle.degrees() == pytest.approx(45.0, abs=kEpsilon)
angle_h = Rotation2d.fromDegrees(45)
ref_h = SwerveModuleVelocity(-2, Rotation2d.fromDegrees(45))
ref_h.cosineScale(angle_h)
optimized_h = ref_h.cosineScale(angle_h)
assert ref_h.velocity == pytest.approx(-2.0, abs=kEpsilon)
assert ref_h.angle.degrees() == pytest.approx(45.0, abs=kEpsilon)
assert optimized_h.velocity == pytest.approx(-2.0, abs=kEpsilon)
assert optimized_h.angle.degrees() == pytest.approx(45.0, abs=kEpsilon)
angle_i = Rotation2d.fromDegrees(-45)
ref_i = SwerveModuleVelocity(-2, Rotation2d.fromDegrees(45))
ref_i.cosineScale(angle_i)
optimized_i = ref_i.cosineScale(angle_i)
assert ref_i.velocity == pytest.approx(0.0, abs=kEpsilon)
assert ref_i.angle.degrees() == pytest.approx(45.0, abs=kEpsilon)
assert optimized_i.velocity == pytest.approx(0.0, abs=kEpsilon)
assert optimized_i.angle.degrees() == pytest.approx(45.0, abs=kEpsilon)
angle_j = Rotation2d.fromDegrees(135)
ref_j = SwerveModuleVelocity(-2, Rotation2d.fromDegrees(45))
ref_j.cosineScale(angle_j)
optimized_j = ref_j.cosineScale(angle_j)
assert ref_j.velocity == pytest.approx(0.0, abs=kEpsilon)
assert ref_j.angle.degrees() == pytest.approx(45.0, abs=kEpsilon)
assert optimized_j.velocity == pytest.approx(0.0, abs=kEpsilon)
assert optimized_j.angle.degrees() == pytest.approx(45.0, abs=kEpsilon)
angle_k = Rotation2d.fromDegrees(-135)
ref_k = SwerveModuleVelocity(-2, Rotation2d.fromDegrees(45))
ref_k.cosineScale(angle_k)
optimized_k = ref_k.cosineScale(angle_k)
assert ref_k.velocity == pytest.approx(2.0, abs=kEpsilon)
assert ref_k.angle.degrees() == pytest.approx(45.0, abs=kEpsilon)
assert optimized_k.velocity == pytest.approx(2.0, abs=kEpsilon)
assert optimized_k.angle.degrees() == pytest.approx(45.0, abs=kEpsilon)
angle_l = Rotation2d.fromDegrees(180)
ref_l = SwerveModuleVelocity(-2, Rotation2d.fromDegrees(45))
ref_l.cosineScale(angle_l)
optimized_l = ref_l.cosineScale(angle_l)
assert ref_l.velocity == pytest.approx(math.sqrt(2.0), abs=kEpsilon)
assert ref_l.angle.degrees() == pytest.approx(45.0, abs=kEpsilon)
assert optimized_l.velocity == pytest.approx(math.sqrt(2.0), abs=kEpsilon)
assert optimized_l.angle.degrees() == pytest.approx(45.0, abs=kEpsilon)
def test_equality():