Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into offseason-base
Browse files Browse the repository at this point in the history
  • Loading branch information
krypto-bot-2539[bot] committed Jan 9, 2025
2 parents 9ea4f1f + 3a3ef5f commit 07ffb61
Show file tree
Hide file tree
Showing 4 changed files with 417 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/main/java/frc/robot/RobotContainer.java
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ private void configureBindings() {
.onTrue(drivetrain.runOnce(() -> drivetrain.seedFieldCentric()));
operatorController
.getRightBumper()
.onTrue(drivetrain.runOnce(() -> drivetrain.resetPose(new Pose2d())));
.onTrue(drivetrain.runOnce(() -> drivetrain.resetPose(Pose2d.kZero)));
}

private double deadband(double value, double deadband) {
Expand Down
23 changes: 23 additions & 0 deletions src/main/java/frc/robot/subsystems/CommandSwerveDrivetrain.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
* be used in command-based projects.
*/
public class CommandSwerveDrivetrain extends SwerveDrivetrain implements Subsystem {
private final CustomOdometry m_odometry_custom;

private static final double kSimLoopPeriod = 0.005; // 5 ms
private Notifier m_simNotifier = null;
private double m_lastSimTime;
Expand Down Expand Up @@ -162,6 +164,9 @@ public CommandSwerveDrivetrain(
startSimThread();
}
m_applyFieldSpeedsOrbit = generateSwerveSetpointConfig();
m_odometry_custom =
new CustomOdometry(new CustomInverseKinematics(getModuleLocations()), getPigeon2());
registerTelemetry(m_odometry_custom::odometryFunction);
}

/**
Expand All @@ -185,6 +190,9 @@ public CommandSwerveDrivetrain(
}

m_applyFieldSpeedsOrbit = generateSwerveSetpointConfig();
m_odometry_custom =
new CustomOdometry(new CustomInverseKinematics(getModuleLocations()), getPigeon2());
registerTelemetry(m_odometry_custom::odometryFunction);
}

/**
Expand Down Expand Up @@ -217,6 +225,9 @@ public CommandSwerveDrivetrain(
}

m_applyFieldSpeedsOrbit = generateSwerveSetpointConfig();
m_odometry_custom =
new CustomOdometry(new CustomInverseKinematics(getModuleLocations()), getPigeon2());
registerTelemetry(m_odometry_custom::odometryFunction);
}

private FieldOrientedOrbitSwerveRequest generateSwerveSetpointConfig() {
Expand Down Expand Up @@ -401,6 +412,18 @@ public void periodic() {
Logger.recordOutput("Drive/actualChassisSpeeds", getState().Speeds);

Logger.recordOutput("Drive/pose", getState().Pose);

Logger.recordOutput("Drive/customPose", m_odometry_custom.m_currentPose);

Logger.recordOutput("Drive/slippingModule", m_odometry_custom.m_maxSlippingWheelIndex);

Logger.recordOutput("Drive/slippingModuleAmount", m_odometry_custom.m_maxSlippingAmount);
Logger.recordOutput("Drive/slippingModuleRatio", m_odometry_custom.m_maxSlippingRatio);

Logger.recordOutput("Drive/customOdometryTime", m_odometry_custom.m_lastOdometryTime);

Logger.recordOutput("Drive/isSlipping", m_odometry_custom.m_isSlipping);
Logger.recordOutput("Drive/isMultiSlipping", m_odometry_custom.m_isMultiwheelSlipping);
}

private void startSimThread() {
Expand Down
222 changes: 222 additions & 0 deletions src/main/java/frc/robot/subsystems/CustomInverseKinematics.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
package frc.robot.subsystems;

import edu.wpi.first.math.MathSharedStore;
import edu.wpi.first.math.MathUsageId;
import edu.wpi.first.math.geometry.Rotation2d;
import edu.wpi.first.math.geometry.Translation2d;
import edu.wpi.first.math.geometry.Twist2d;
import edu.wpi.first.math.kinematics.ChassisSpeeds;
import edu.wpi.first.math.kinematics.SwerveModulePosition;
import edu.wpi.first.math.kinematics.SwerveModuleState;
import java.util.Arrays;
import org.ejml.simple.SimpleMatrix;

public class CustomInverseKinematics {
public class CustomKinematicsSolution {
public Twist2d estimatedTwist;
}

private final SimpleMatrix m_inverseKinematics;
private final SimpleMatrix[] m_inverseKinematicsMissing;
private final SimpleMatrix m_forwardKinematics;
private final SimpleMatrix[] m_forwardKinematicsMissing;

private final int m_numModules;
private final Translation2d[] m_modules;
private Rotation2d[] m_moduleHeadings;
private Translation2d m_prevCoR = Translation2d.kZero;

/**
* Constructs a swerve drive kinematics object. This takes in a variable number of module
* locations as Translation2d objects. The order in which you pass in the module locations is
* the same order that you will receive the module states when performing inverse kinematics. It
* is also expected that you pass in the module states in the same order when calling the
* forward kinematics methods.
*
* @param moduleTranslationsMeters The locations of the modules relative to the physical center
* of the robot.
*/
public CustomInverseKinematics(Translation2d... moduleTranslationsMeters) {
if (moduleTranslationsMeters.length < 2) {
throw new IllegalArgumentException("A swerve drive requires at least two modules");
}
m_numModules = moduleTranslationsMeters.length;
m_modules = Arrays.copyOf(moduleTranslationsMeters, m_numModules);
m_moduleHeadings = new Rotation2d[m_numModules];
Arrays.fill(m_moduleHeadings, Rotation2d.kZero);
m_inverseKinematics = new SimpleMatrix(m_numModules * 2, 3);

for (int i = 0; i < m_numModules; i++) {
m_inverseKinematics.setRow(i * 2 + 0, 0, /* Start Data */ 1, 0, -m_modules[i].getY());
m_inverseKinematics.setRow(i * 2 + 1, 0, /* Start Data */ 0, 1, +m_modules[i].getX());
}

m_forwardKinematics = m_inverseKinematics.pseudoInverse();

m_inverseKinematicsMissing = new SimpleMatrix[m_numModules];
m_forwardKinematicsMissing = new SimpleMatrix[m_numModules];

for (int i = 0; i < m_numModules; i++) {
m_inverseKinematicsMissing[i] =
m_inverseKinematics
.rows(0, i * 2)
.concatRows(m_inverseKinematics.rows(i * 2 + 2, m_numModules * 2));
var x = m_inverseKinematicsMissing[i].getNumRows();
var y = m_inverseKinematicsMissing[i].getNumCols();
m_forwardKinematicsMissing[i] = m_inverseKinematicsMissing[i].pseudoInverse();
}

MathSharedStore.reportUsage(MathUsageId.kKinematics_SwerveDrive, 1);
}

public SimpleMatrix toModuleVelocities(SwerveModuleState... moduleStates) {
var moduleStatesMatrix = new SimpleMatrix(moduleStates.length * 2, 1);

for (int i = 0; i < m_numModules; i++) {
var module = moduleStates[i];
moduleStatesMatrix.set(i * 2, 0, module.speedMetersPerSecond * module.angle.getCos());
moduleStatesMatrix.set(i * 2 + 1, module.speedMetersPerSecond * module.angle.getSin());
}

return moduleStatesMatrix;
}

public SimpleMatrix toModuleVelocities(ChassisSpeeds chassisSpeeds) {
var chassisSpeedsVector = new SimpleMatrix(3, 1);
chassisSpeedsVector.set(0, 0, chassisSpeeds.vxMetersPerSecond);
chassisSpeedsVector.set(1, 0, chassisSpeeds.vyMetersPerSecond);
chassisSpeedsVector.set(2, 0, chassisSpeeds.omegaRadiansPerSecond);

return m_inverseKinematics.mult(chassisSpeedsVector);
}

public ChassisSpeeds toChassisSpeeds(int missingModule, SwerveModuleState... moduleStates) {
if (moduleStates.length != m_numModules) {
throw new IllegalArgumentException(
"Number of modules is not consistent with number of module locations provided in "
+ "constructor");
}
var moduleStatesMatrix = new SimpleMatrix((m_numModules - 1) * 2, 1);

for (int i = 0; i < m_numModules - 1; i++) {
int j = i;
if (i >= missingModule) {
j++;
}
var module = moduleStates[j];
moduleStatesMatrix.set(i * 2, 0, module.speedMetersPerSecond * module.angle.getCos());
moduleStatesMatrix.set(i * 2 + 1, module.speedMetersPerSecond * module.angle.getSin());
}

var chassisSpeedsVector =
m_forwardKinematicsMissing[missingModule].mult(moduleStatesMatrix);
return new ChassisSpeeds(
chassisSpeedsVector.get(0, 0),
chassisSpeedsVector.get(1, 0),
chassisSpeedsVector.get(2, 0));
}

public ChassisSpeeds toChassisSpeeds(SwerveModuleState... moduleStates) {
if (moduleStates.length != m_numModules) {
throw new IllegalArgumentException(
"Number of modules is not consistent with number of module locations provided in "
+ "constructor");
}
var moduleStatesMatrix = new SimpleMatrix(m_numModules * 2, 1);

for (int i = 0; i < m_numModules; i++) {
var module = moduleStates[i];
moduleStatesMatrix.set(i * 2, 0, module.speedMetersPerSecond * module.angle.getCos());
moduleStatesMatrix.set(i * 2 + 1, module.speedMetersPerSecond * module.angle.getSin());
}

var chassisSpeedsVector = m_forwardKinematics.mult(moduleStatesMatrix);
return new ChassisSpeeds(
chassisSpeedsVector.get(0, 0),
chassisSpeedsVector.get(1, 0),
chassisSpeedsVector.get(2, 0));
}

/**
* Performs forward kinematics to return the resulting chassis state from the given module
* states. This method is often used for odometry -- determining the robot's position on the
* field using data from the real-world speed and angle of each module on the robot.
*
* @param moduleDeltas The latest change in position of the modules (as a SwerveModulePosition
* type) as measured from respective encoders and gyros. The order of the swerve module
* states should be same as passed into the constructor of this class.
* @return The resulting Twist2d.
*/
public Twist2d toTwist2d(SwerveModulePosition... moduleDeltas) {
if (moduleDeltas.length != m_numModules) {
throw new IllegalArgumentException(
"Number of modules is not consistent with number of module locations provided in "
+ "constructor");
}
var moduleDeltaMatrix = new SimpleMatrix(m_numModules * 2, 1);

for (int i = 0; i < m_numModules; i++) {
var module = moduleDeltas[i];
moduleDeltaMatrix.set(i * 2, 0, module.distanceMeters * module.angle.getCos());
moduleDeltaMatrix.set(i * 2 + 1, module.distanceMeters * module.angle.getSin());
}

var chassisDeltaVector = m_forwardKinematics.mult(moduleDeltaMatrix);
return new Twist2d(
chassisDeltaVector.get(0, 0),
chassisDeltaVector.get(1, 0),
chassisDeltaVector.get(2, 0));
}

public Twist2d toTwist2d(int missingModule, SwerveModulePosition... moduleDeltas) {
if (moduleDeltas.length != m_numModules) {
throw new IllegalArgumentException(
"Number of modules is not consistent with number of module locations provided in "
+ "constructor");
}
var moduleDeltaMatrix = new SimpleMatrix((m_numModules - 1) * 2, 1);

for (int i = 0; i < (m_numModules - 1); i++) {
int j = i;
if (i >= missingModule) {
j++;
}
var module = moduleDeltas[j];
moduleDeltaMatrix.set(i * 2, 0, module.distanceMeters * module.angle.getCos());
moduleDeltaMatrix.set(i * 2 + 1, module.distanceMeters * module.angle.getSin());
}

var chassisDeltaVector = m_forwardKinematicsMissing[missingModule].mult(moduleDeltaMatrix);
return new Twist2d(
chassisDeltaVector.get(0, 0),
chassisDeltaVector.get(1, 0),
chassisDeltaVector.get(2, 0));
}

public Twist2d toTwist2d(SwerveModulePosition[] start, SwerveModulePosition[] end) {
if (start.length != end.length) {
throw new IllegalArgumentException("Inconsistent number of modules!");
}
var newPositions = new SwerveModulePosition[start.length];
for (int i = 0; i < start.length; i++) {
newPositions[i] =
new SwerveModulePosition(
end[i].distanceMeters - start[i].distanceMeters, end[i].angle);
}
return toTwist2d(newPositions);
}

public Twist2d toTwist2d(
int missingModule, SwerveModulePosition[] start, SwerveModulePosition[] end) {
if (start.length != end.length) {
throw new IllegalArgumentException("Inconsistent number of modules!");
}
var newPositions = new SwerveModulePosition[start.length];
for (int i = 0; i < start.length; i++) {
newPositions[i] =
new SwerveModulePosition(
end[i].distanceMeters - start[i].distanceMeters, end[i].angle);
}
return toTwist2d(missingModule, newPositions);
}
}
Loading

0 comments on commit 07ffb61

Please sign in to comment.