Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
88b1f69
initial commit
Levercpu Feb 21, 2026
1781c98
initial commit
Levercpu Feb 21, 2026
16ab803
initial commit
Levercpu Feb 21, 2026
f244dae
initial commit
Levercpu Feb 22, 2026
370ea9a
merge main
Levercpu Feb 22, 2026
c0ae709
merge main
Levercpu Feb 22, 2026
2c7560a
most pr comments addressed
Levercpu Mar 1, 2026
4e2456f
Merge branch 'main' of https://github.com/FRC4048/Java_2026 into LS_V…
Levercpu Mar 1, 2026
50d395e
all addressed or comment or question
Levercpu Mar 1, 2026
d449065
Merge branch 'LS_Vision_Simulation' of https://github.com/FRC4048/Jav…
Levercpu Mar 1, 2026
e059a2f
pr comments fixed.
Levercpu Mar 1, 2026
49f822e
fixed pr comments
Levercpu Mar 5, 2026
5754b46
only way to not thrown null pointer
Levercpu Mar 5, 2026
13430c6
Merge branch 'LS_Vision_Simulation' of https://github.com/FRC4048/Jav…
Levercpu Mar 5, 2026
bd2fac0
small logging change
Levercpu Mar 5, 2026
c4a51e7
Merge branch 'main' of https://github.com/FRC4048/Java_2026 into LS_V…
Levercpu Mar 6, 2026
1bb160f
Merge branch 'main' of https://github.com/FRC4048/Java_2026 into LS_V…
Levercpu Mar 6, 2026
382f6b6
Merge branch 'LS_Vision_Simulation' of https://github.com/FRC4048/Jav…
Levercpu Mar 6, 2026
a29d956
Merge branch 'main' of https://github.com/FRC4048/Java_2026 into LS_V…
Levercpu Mar 9, 2026
a47b8f7
fixed
Levercpu Mar 10, 2026
76bf35f
fixed
Levercpu Mar 10, 2026
66f7fd9
fixed
Levercpu Mar 10, 2026
5c60892
wip
Levercpu Mar 10, 2026
cdde152
wip
Levercpu Mar 10, 2026
e11869c
done
Levercpu Mar 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions src/main/java/frc/robot/Robot.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

package frc.robot;


import edu.wpi.first.cameraserver.CameraServer;
import edu.wpi.first.cscore.UsbCamera;
import edu.wpi.first.cscore.VideoSource;
Expand Down Expand Up @@ -138,11 +139,11 @@ public void robotPeriodic() {
// Puts data on the elastic dashboard
SmartDashboard.putString("Alliance Color", Robot.allianceColorString());
SmartDashboard.putBoolean("Hub Active?", hubActive());
if (Constants.currentMode == Constants.Mode.SIM) {
Logger.recordOutput("SimPose", robotContainer.getDriveBase().getSimulationPose().get());
Logger.recordOutput("OdomPose", robotContainer.getDriveBase().getOdom());
}
}

// Puts data on the elastic dashboard
SmartDashboard.putString("Alliance Color", Robot.allianceColorString());
SmartDashboard.putBoolean("Hub Active?", hubActive());
}
SmartDashboard.putString("Selected Action",
robotContainer.getAutoChooser().getCommandDescription());
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/frc/robot/apriltags/SimApriltagIO.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public void simReadings() {
distance = readingPos.getTranslation().getDistance(tag.getPose().toPose2d().getTranslation());
if (BasicVisionFilter.inBounds(readingPos)) {
addReading(new ApriltagReading(readingX, readingY, readingYaw,
distance, 0, tag.number(), Constants.AVERAGE_CAM_LATENCY + random.nextGaussian() * Constants.AVERAGE_CAM_LATENCY_STD_DEV, 0, Logger.getTimestamp() / 1000.0));
distance,0,tag.number(), Constants.AVERAGE_CAM_LATENCY + random.nextGaussian() * Constants.AVERAGE_CAM_LATENCY_STD_DEV, stdDevs.get(0),Logger.getTimestamp() / 1000.0));
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/frc/robot/constants/GameConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public enum Mode {
public static final boolean ENABLE_LOGGING = true;

//Debugs
public static final boolean DEBUG = false;
public static final boolean DEBUG = true;
public static final boolean ARM_DEBUG = true;

//Joystick
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,21 @@
import edu.wpi.first.math.geometry.Translation2d;
import edu.wpi.first.math.kinematics.ChassisSpeeds;
import edu.wpi.first.math.kinematics.SwerveDriveKinematics;
import edu.wpi.first.math.kinematics.SwerveDriveOdometry;
import edu.wpi.first.math.kinematics.SwerveModulePosition;
import edu.wpi.first.math.numbers.N3;
import edu.wpi.first.math.trajectory.Trajectory;
import edu.wpi.first.wpilibj.DriverStation;
import edu.wpi.first.wpilibj.Timer;
import edu.wpi.first.wpilibj.smartdashboard.Field2d;
import edu.wpi.first.wpilibj2.command.Command;
import edu.wpi.first.wpilibj2.command.SubsystemBase;
import edu.wpi.first.wpilibj2.command.sysid.SysIdRoutine.Config;
import frc.robot.constants.Constants;

import frc.robot.constants.GameConstants;
import frc.robot.utils.logging.io.gyro.ThreadedGyro;
import org.littletonrobotics.junction.Logger;
import swervelib.SwerveController;
import swervelib.SwerveDrive;
import swervelib.SwerveDriveTest;
Expand All @@ -38,6 +43,8 @@
import java.io.File;
import java.util.Arrays;
import java.util.Optional;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.Optional;
import java.util.function.DoubleSupplier;
import java.util.function.Supplier;

Expand All @@ -49,6 +56,10 @@ public class SwerveSubsystem extends SubsystemBase {
*/
private final SwerveDrive swerveDrive;
private Vector<N3> variance = VecBuilder.fill(0.1,0.1,0.1);
private SwerveDriveOdometry rawOdometry;
private final ConcurrentLinkedDeque<PoseErrorRecord> poseError = new ConcurrentLinkedDeque<>();
private record PoseErrorRecord(double timestamp, double error) {
}
/**
* Initialize {@link SwerveDrive} with the directory provided.
* The SwerveIMU (which can be null) is the instance of the SwerveIMU to use. If non-null,
Expand Down Expand Up @@ -83,7 +94,14 @@ public SwerveSubsystem(File directory, SwerveIMU swerveIMU) {
swerveDrive.setModuleEncoderAutoSynchronize(Constants.SET_MODULE_ENCODER_AUTO_SYNCHRONIZE,
Constants.SET_MODULE_ENCODER_AUTO_SYNCHRONIZE_DEADBAND); // Enable if you want to resynchronize your absolute encoders and motor encoders periodically when they are not moving.
// swerveDrive.pushOffsetsToEncoders(); // Set the absolute encoder to be used over the internal encoder and push the offsets onto it. Throws warning if not possible

if (Constants.currentMode==Constants.simMode) {
rawOdometry = new SwerveDriveOdometry(
swerveDrive.kinematics,
swerveDrive.getOdometryHeading(),
swerveDrive.getModulePositions(),
startingPose);
}

}

/**
Expand All @@ -105,6 +123,17 @@ public SwerveSubsystem(SwerveDriveConfiguration driveCfg, SwerveControllerConfig
public void periodic() {
//add vision pose here
//addVisionMeasurement(new Pose2d(new Translation2d(16, 2), new Rotation2d()));
if (Constants.currentMode == GameConstants.Mode.SIM) {
double currentTime = Logger.getTimestamp() / 1000000.0;
double oneSecondAgo = currentTime - 1.0;
poseError.removeIf(record -> record.timestamp < oneSecondAgo);
poseError.add(new PoseErrorRecord(currentTime, getError()));
Logger.recordOutput("AveragePoseError", getAverageError());
rawOdometry.update(
swerveDrive.getOdometryHeading(),
swerveDrive.getModulePositions()
);
}
}

@Override
Expand Down Expand Up @@ -282,8 +311,16 @@ public SwerveDriveKinematics getKinematics() {
*
* @param initialHolonomicPose The pose to set the odometry to
*/
// might be broken
public void resetOdometry(Pose2d initialHolonomicPose) {
swerveDrive.resetOdometry(initialHolonomicPose);
if (Constants.currentMode==Constants.simMode) {
SwerveModulePosition[] modules = new SwerveModulePosition[4];
for (int i=0; i<4; i++) {
modules[i] = new SwerveModulePosition();
}
rawOdometry.resetPosition(initialHolonomicPose.getRotation(), modules, initialHolonomicPose);
}
}

/**
Expand All @@ -294,13 +331,23 @@ public void resetOdometry(Pose2d initialHolonomicPose) {
public Pose2d getPose() {
return swerveDrive.getPose();
}
public double getError() {
return getPose().getTranslation().getDistance((getSimulationPose().get().getTranslation()));
}
public double getAverageError(){
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious to see whether this contains any non-zero values - AFAICT, the simulation pose is only applicable in simulation and the real pose is only available in non-simulation, so when would we be able to compare them?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the getpose is the estimated pose from the vision measurements and drift adjusted odometry (trying to model real life performance)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getPose is swerveDrive.getPose(), which is the combined vision and odometry. swerveDrive.getSimulationDriveTrainPose() is the pose during simulation. What does the difference between them tell us?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the getpose has simulated error added on

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

put in wrapper kept for now

return poseError.stream().mapToDouble(record -> record.error).average().orElse(0);
}

public Optional<Pose2d> getSimulationPose() {
return swerveDrive.getSimulationDriveTrainPose();
}
// Todo: fix to only get odomtry
public Pose2d getOdom() {
return swerveDrive.getPose();
if (Constants.currentMode==Constants.simMode) {
return rawOdometry.getPoseMeters();
} else {
return getPose();
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,22 @@

import edu.wpi.first.math.Vector;
import edu.wpi.first.math.geometry.Pose2d;
import edu.wpi.first.math.geometry.Pose3d;
import edu.wpi.first.math.interpolation.TimeInterpolatableBuffer;
import edu.wpi.first.math.kinematics.SwerveDriveKinematics;
import edu.wpi.first.math.numbers.N3;
import frc.robot.constants.GameConstants;
import frc.robot.subsystems.swervedrive.SwerveSubsystem;
import frc.robot.subsystems.swervedrive.vision.truster.FilterResult;
import frc.robot.subsystems.swervedrive.vision.truster.PoseDeviation;
import frc.robot.subsystems.swervedrive.vision.truster.VisionFilter;
import frc.robot.subsystems.swervedrive.vision.truster.VisionMeasurement;
import frc.robot.subsystems.swervedrive.vision.truster.VisionTruster;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedDeque;

import frc.robot.utils.Apriltag;
import org.littletonrobotics.junction.Logger;

/**
Expand Down Expand Up @@ -53,29 +55,32 @@ public FilterablePoseManager(

@Override
public void processQueue() {
List<Pose2d> validMeasurementsPose = new ArrayList<>();
List<Pose2d> invalidMeasurementsPose = new ArrayList<>();

LinkedHashMap<VisionMeasurement, FilterResult> filteredData =
filter.filter(visionMeasurementQueue);
filter.filter(visionMeasurementQueue);
visionMeasurementQueue.clear();
List<Pose2d> validMeasurements = new ArrayList<>();
List<Pose2d> invalidMeasurements = new ArrayList<>();
for (Map.Entry<VisionMeasurement, FilterResult> entry : filteredData.entrySet()) {
VisionMeasurement v = entry.getKey();
FilterResult r = entry.getValue();
for (Map.Entry<VisionMeasurement, FilterResult> filterEntry : filteredData.entrySet()) {
VisionMeasurement v = filterEntry.getKey();
FilterResult r = filterEntry.getValue();
switch (r) {
case ACCEPTED -> {
setVisionSTD(visionTruster.calculateTrust(v));
validMeasurements.add(v.measurement());
validMeasurementsPose.add(v.measurement());
addVisionMeasurement(v);

}
case NOT_PROCESSED -> visionMeasurementQueue.add(v);
case REJECTED -> {
invalidMeasurements.add(v.measurement());
invalidMeasurementsPose.add(v.measurement());
}
}
}
Logger.recordOutput("Apriltag/acceptedMeasurements", validMeasurements.toArray(Pose2d[]::new));
Logger.recordOutput(
"Apriltag/rejectedMeasurements", invalidMeasurements.toArray(Pose2d[]::new));
// Logger.recordOutput("Apriltag/acceptedMeasurementsPose", validMeasurementsPose.toArray(Pose2d[]::new));
// Logger.recordOutput("Apriltag/rejectedMeasurementsPose", invalidMeasurementsPose.toArray(Pose2d[]::new));
Logger.recordOutput("Apriltag/numberAccepted", validMeasurementsPose.size());
Logger.recordOutput("Apriltag/numberRejected", invalidMeasurementsPose.size());
}

public VisionTruster getVisionTruster() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package frc.robot.subsystems.swervedrive.vision.truster;

import edu.wpi.first.math.geometry.Pose2d;
import frc.robot.utils.Apriltag;

/**
* @param measurement estimated robot position (meters) calculated from apriltag tag what tag produced the
* position
* @param distanceFromTag distance (meters) estimated robot pose was from the tag
* @param timeOfMeasurement time when the pose was measured (seconds)
*/
public record VisionMeasurement(
Pose2d measurement, double distanceFromTag, double timeOfMeasurement) {}
public record VisionMeasurement(Pose2d measurement, double distanceFromTag, double timeOfMeasurement) {}
4 changes: 0 additions & 4 deletions src/main/java/frc/robot/utils/Apriltag.java
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ public enum Apriltag {
THIRTY_ONE(0.32, 147.47, 21.75),//Tower, Blue z rotation:0
THIRTY_TWO(0.32, 164.47, 21.75);//Tower, Blue z rotation:0
*/
public record TagPose(Apriltag tag, Pose3d pose) {}
private final double xMeters;
private final double yMeters;
private final double zMeters;
Expand Down Expand Up @@ -133,7 +132,4 @@ public Pose3d getPose() {
public int number() {
return ordinal()+1;
}
public TagPose getTagInfo() {
return new TagPose(this, pose);
}
}