package frc.robot.subsystems.drivetrain;
import java.util.Arrays;
+import java.util.ArrayList;
import java.util.Optional;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import edu.wpi.first.wpilibj2.command.SubsystemBase;
import frc.robot.constants.Constants;
import frc.robot.constants.FieldConstants;
+import frc.robot.constants.GyroBiasConstants;
import frc.robot.constants.VisionConstants;
import frc.robot.constants.swerve.DriveConstants;
import frc.robot.constants.swerve.ModuleConstants;
import frc.robot.util.SwerveStuff.SwerveSetpoint;
import frc.robot.util.SwerveStuff.SwerveSetpointGenerator;
import frc.robot.util.Vision.Vision;
+import frc.robot.util.Vision.GyroBiasEstimator;
+import org.photonvision.EstimatedRobotPose;
/**
* Represents a swerve drive style drivetrain.
private Rotation2d rawGyroRotation = new Rotation2d();
+ // for vision yaw correction
+ private GyroBiasEstimator gyroBiasEstimator = new GyroBiasEstimator();
+
private final Field2d field = new Field2d();
/**
if (vision.canSeeTag()) {
slipped = false;
modulePoses.reset();
+
+ double currentGyroYaw = gyroInputs.yawPosition.getRadians();
+
+ // to compare bias
+ ArrayList<EstimatedRobotPose> visionPoses = vision.getEstimatedPoses(getPose());
+
+ for (EstimatedRobotPose visionPose : visionPoses) {
+ if (visionPose.estimatedPose != null && visionPose.timestampSeconds > 0) {
+ double visionYaw = visionPose.estimatedPose.getRotation().getZ();
+
+ // gets at vision timestamp, not current gyro yaw
+ double gyroYawAtTimestamp = getGyroYawAtTimestamp(visionPose.timestampSeconds);
+
+ if (!Double.isNaN(gyroYawAtTimestamp)) {
+
+ // use weighted observation
+ gyroBiasEstimator.addObservation(visionYaw, gyroYawAtTimestamp, 1.0);
+ }
+ }
+ }
+
+ // check if we have enough samples
+ if (gyroBiasEstimator.getSampleCount() >= GyroBiasConstants.MIN_SAMPLES) {
+ double fullBias = gyroBiasEstimator.getAndResetBias();
+ double bias = gyroBiasEstimator.applyPartialCorrection(fullBias);
+ if (Math.abs(bias) > GyroBiasConstants.MIN_CORRECTION_RAD) {
+ gyroIO.setYaw(new Rotation2d(currentGyroYaw + bias));
+ resetOdometry(
+ new Pose2d(getPose().getTranslation(), new Rotation2d(currentGyroYaw + bias)));
+ }
+ }
}
}
}
return modules;
}
+ /**
+ * gets gyro yaw at a specific timestamp with interpolation
+ * this is used for timestamp-synchronized gyro/vision comparison.
+ *
+ * @param timestampSeconds the timestamp to get the gyro yaw at
+ * @return the gyro yaw in radians, or Double.NaN if no valid data
+ */
+ private double getGyroYawAtTimestamp(double timestampSeconds) {
+ double[] timestamps = gyroInputs.odometryYawTimestamps;
+ Rotation2d[] positions = gyroInputs.odometryYawPositions;
+
+ if (timestamps == null || positions == null || timestamps.length == 0) {
+ return Double.NaN;
+ }
+
+ // find closest timestamp
+ int closestIndex = 0;
+ double closestDiff = Double.MAX_VALUE;
+
+ for (int i = 0; i < timestamps.length; i++) {
+ double diff = Math.abs(timestamps[i] - timestampSeconds);
+ if (diff < closestDiff) {
+ closestDiff = diff;
+ closestIndex = i;
+ }
+ }
+
+ // only use if within
+ if (closestDiff > 0.1) {
+ return Double.NaN;
+ }
+
+ // if we have adjacent samples, interpolate
+ if (timestamps.length > 1 && closestDiff < 0.05) {
+ // find sample before/after target timestamp
+ int beforeIndex = -1;
+ int afterIndex = -1;
+
+ for (int i = 0; i < timestamps.length; i++) {
+ if (timestamps[i] <= timestampSeconds) {
+ beforeIndex = i;
+ } else {
+ afterIndex = i;
+ break;
+ }
+ }
+
+ // interpolate if we have both before/after
+ if (beforeIndex >= 0 && afterIndex >= 0 && beforeIndex != afterIndex) {
+ double beforeTime = timestamps[beforeIndex];
+ double afterTime = timestamps[afterIndex];
+ double beforeYaw = positions[beforeIndex].getRadians();
+ double afterYaw = positions[afterIndex].getRadians();
+
+ // linear interpolation
+ double t = (timestampSeconds - beforeTime) / (afterTime - beforeTime);
+ double interpolatedYaw = beforeYaw + t * (afterYaw - beforeYaw);
+
+ return interpolatedYaw;
+ }
+ }
+
+ return positions[closestIndex].getRadians();
+ }
+
/**
* Resets the yaw of the robot.
*