diff --git a/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveDriveKinematics.java b/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveDriveKinematics.java index 1732c9912c..fa07880210 100644 --- a/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveDriveKinematics.java +++ b/wpimath/src/main/java/edu/wpi/first/math/kinematics/SwerveDriveKinematics.java @@ -10,7 +10,6 @@ import edu.wpi.first.math.geometry.Rotation2d; import edu.wpi.first.math.geometry.Translation2d; import edu.wpi.first.math.geometry.Twist2d; import java.util.Arrays; -import java.util.Collections; import org.ejml.simple.SimpleMatrix; /** @@ -233,7 +232,10 @@ public class SwerveDriveKinematics { */ public static void desaturateWheelSpeeds( SwerveModuleState[] moduleStates, double attainableMaxSpeedMetersPerSecond) { - double realMaxSpeed = Collections.max(Arrays.asList(moduleStates)).speedMetersPerSecond; + double realMaxSpeed = 0; + for (SwerveModuleState moduleState : moduleStates) { + realMaxSpeed = Math.max(realMaxSpeed, Math.abs(moduleState.speedMetersPerSecond)); + } if (realMaxSpeed > attainableMaxSpeedMetersPerSecond) { for (SwerveModuleState moduleState : moduleStates) { moduleState.speedMetersPerSecond = @@ -266,7 +268,10 @@ public class SwerveDriveKinematics { double attainableMaxModuleSpeedMetersPerSecond, double attainableMaxTranslationalSpeedMetersPerSecond, double attainableMaxRotationalVelocityRadiansPerSecond) { - double realMaxSpeed = Collections.max(Arrays.asList(moduleStates)).speedMetersPerSecond; + double realMaxSpeed = 0; + for (SwerveModuleState moduleState : moduleStates) { + realMaxSpeed = Math.max(realMaxSpeed, Math.abs(moduleState.speedMetersPerSecond)); + } if (attainableMaxTranslationalSpeedMetersPerSecond == 0 || attainableMaxRotationalVelocityRadiansPerSecond == 0 diff --git a/wpimath/src/main/native/include/frc/kinematics/SwerveDriveKinematics.inc b/wpimath/src/main/native/include/frc/kinematics/SwerveDriveKinematics.inc index c7f26e0da4..9a2b5abeeb 100644 --- a/wpimath/src/main/native/include/frc/kinematics/SwerveDriveKinematics.inc +++ b/wpimath/src/main/native/include/frc/kinematics/SwerveDriveKinematics.inc @@ -134,12 +134,13 @@ void SwerveDriveKinematics::DesaturateWheelSpeeds( wpi::array* moduleStates, units::meters_per_second_t attainableMaxSpeed) { auto& states = *moduleStates; - auto realMaxSpeed = std::max_element(states.begin(), states.end(), - [](const auto& a, const auto& b) { - return units::math::abs(a.speed) < - units::math::abs(b.speed); - }) - ->speed; + auto realMaxSpeed = + units::math::abs(std::max_element(states.begin(), states.end(), + [](const auto& a, const auto& b) { + return units::math::abs(a.speed) < + units::math::abs(b.speed); + }) + ->speed); if (realMaxSpeed > attainableMaxSpeed) { for (auto& module : states) { @@ -157,12 +158,13 @@ void SwerveDriveKinematics::DesaturateWheelSpeeds( units::radians_per_second_t attainableMaxRobotRotationSpeed) { auto& states = *moduleStates; - auto realMaxSpeed = std::max_element(states.begin(), states.end(), - [](const auto& a, const auto& b) { - return units::math::abs(a.speed) < - units::math::abs(b.speed); - }) - ->speed; + auto realMaxSpeed = + units::math::abs(std::max_element(states.begin(), states.end(), + [](const auto& a, const auto& b) { + return units::math::abs(a.speed) < + units::math::abs(b.speed); + }) + ->speed); if (attainableMaxRobotTranslationSpeed == 0_mps || attainableMaxRobotRotationSpeed == 0_rad_per_s || realMaxSpeed == 0_mps) { diff --git a/wpimath/src/test/java/edu/wpi/first/math/kinematics/SwerveDriveKinematicsTest.java b/wpimath/src/test/java/edu/wpi/first/math/kinematics/SwerveDriveKinematicsTest.java index 43dd02ad9b..2f8b216a0a 100644 --- a/wpimath/src/test/java/edu/wpi/first/math/kinematics/SwerveDriveKinematicsTest.java +++ b/wpimath/src/test/java/edu/wpi/first/math/kinematics/SwerveDriveKinematicsTest.java @@ -371,4 +371,21 @@ class SwerveDriveKinematicsTest { () -> assertEquals(4.0 * factor, arr[2].speedMetersPerSecond, kEpsilon), () -> assertEquals(7.0 * factor, arr[3].speedMetersPerSecond, kEpsilon)); } + + @Test + void testDesaturateNegativeSpeed() { + SwerveModuleState fl = new SwerveModuleState(1, new Rotation2d()); + SwerveModuleState fr = new SwerveModuleState(1, new Rotation2d()); + SwerveModuleState bl = new SwerveModuleState(-2, new Rotation2d()); + SwerveModuleState br = new SwerveModuleState(-2, new Rotation2d()); + + SwerveModuleState[] arr = {fl, fr, bl, br}; + SwerveDriveKinematics.desaturateWheelSpeeds(arr, 1); + + assertAll( + () -> assertEquals(0.5, arr[0].speedMetersPerSecond, kEpsilon), + () -> assertEquals(0.5, arr[1].speedMetersPerSecond, kEpsilon), + () -> assertEquals(-1.0, arr[2].speedMetersPerSecond, kEpsilon), + () -> assertEquals(-1.0, arr[3].speedMetersPerSecond, kEpsilon)); + } } diff --git a/wpimath/src/test/native/cpp/kinematics/SwerveDriveKinematicsTest.cpp b/wpimath/src/test/native/cpp/kinematics/SwerveDriveKinematicsTest.cpp index 8e0dc8f351..4e549b7c85 100644 --- a/wpimath/src/test/native/cpp/kinematics/SwerveDriveKinematicsTest.cpp +++ b/wpimath/src/test/native/cpp/kinematics/SwerveDriveKinematicsTest.cpp @@ -274,3 +274,18 @@ TEST_F(SwerveDriveKinematicsTest, DesaturateSmooth) { EXPECT_NEAR(arr[2].speed.value(), 4.0 * kFactor, kEpsilon); EXPECT_NEAR(arr[3].speed.value(), 7.0 * kFactor, kEpsilon); } + +TEST_F(SwerveDriveKinematicsTest, DesaturateNegativeSpeed) { + SwerveModuleState state1{1.0_mps, 0_deg}; + SwerveModuleState state2{1.0_mps, 0_deg}; + SwerveModuleState state3{-2.0_mps, 0_deg}; + SwerveModuleState state4{-2.0_mps, 0_deg}; + + wpi::array arr{state1, state2, state3, state4}; + SwerveDriveKinematics<4>::DesaturateWheelSpeeds(&arr, 1.0_mps); + + EXPECT_NEAR(arr[0].speed.value(), 0.5, kEpsilon); + EXPECT_NEAR(arr[1].speed.value(), 0.5, kEpsilon); + EXPECT_NEAR(arr[2].speed.value(), -1.0, kEpsilon); + EXPECT_NEAR(arr[3].speed.value(), -1.0, kEpsilon); +}