Skip to content

Commit e786243

Browse files
committed
feat: AprilTagVision subsystem
1 parent 255352c commit e786243

File tree

5 files changed

+127
-11
lines changed

5 files changed

+127
-11
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package org.team1540.robot2024.subsystems.vision;
2+
3+
import edu.wpi.first.math.geometry.Pose2d;
4+
import edu.wpi.first.wpilibj2.command.SubsystemBase;
5+
import org.littletonrobotics.junction.Logger;
6+
import org.team1540.robot2024.Constants;
7+
import org.team1540.robot2024.util.vision.TimestampedVisionPose;
8+
9+
import java.util.Arrays;
10+
11+
public class AprilTagVision extends SubsystemBase {
12+
private final AprilTagVisionIO frontCameraIO;
13+
private final AprilTagVisionIOInputsAutoLogged frontCameraInputs = new AprilTagVisionIOInputsAutoLogged();
14+
15+
private final AprilTagVisionIO rearCameraIO;
16+
private final AprilTagVisionIOInputsAutoLogged rearCameraInputs = new AprilTagVisionIOInputsAutoLogged();
17+
18+
private TimestampedVisionPose frontPose;
19+
private TimestampedVisionPose rearPose;
20+
21+
public AprilTagVision(AprilTagVisionIO frontCameraIO, AprilTagVisionIO rearCameraIO) {
22+
this.frontCameraIO = frontCameraIO;
23+
this.rearCameraIO = rearCameraIO;
24+
}
25+
26+
@Override
27+
public void periodic() {
28+
frontCameraIO.updateInputs(frontCameraInputs);
29+
rearCameraIO.updateInputs(rearCameraInputs);
30+
Logger.processInputs("Vision/FrontCamera", frontCameraInputs);
31+
Logger.processInputs("Vision/RearCamera", rearCameraInputs);
32+
33+
if (frontCameraInputs.lastMeasurementTimestampSecs > frontPose.timestampSecs()) {
34+
frontPose = new TimestampedVisionPose(
35+
frontCameraInputs.lastMeasurementTimestampSecs,
36+
frontCameraInputs.estimatedPoseMeters,
37+
frontCameraInputs.seenTagIDs,
38+
frontCameraInputs.tagPosesMeters);
39+
}
40+
if (rearCameraInputs.lastMeasurementTimestampSecs > rearPose.timestampSecs()) {
41+
rearPose = new TimestampedVisionPose(
42+
rearCameraInputs.lastMeasurementTimestampSecs,
43+
rearCameraInputs.estimatedPoseMeters,
44+
rearCameraInputs.seenTagIDs,
45+
rearPose.tagPosesMeters());
46+
}
47+
}
48+
49+
/**
50+
* Gets the estimated pose by fusing individual computed poses from each camera
51+
*/
52+
public TimestampedVisionPose getEstimatedPose() {
53+
if (Constants.currentMode == Constants.Mode.SIM) return null;
54+
if (frontPose.getNumTagsSeen() < 1 && rearPose.getNumTagsSeen() < 1) return null;
55+
else if (frontPose.getNumTagsSeen() < 1) return rearPose;
56+
else if (rearPose.getNumTagsSeen() < 1) return frontPose;
57+
58+
// This just takes the average of the measurements, we could change this to something more advanced if necessary
59+
int[] allTagIDs = Arrays.copyOf(frontPose.seenTagIDs(), frontPose.getNumTagsSeen() + rearPose.getNumTagsSeen());
60+
System.arraycopy(rearPose.seenTagIDs(), 0, allTagIDs, frontPose.getNumTagsSeen(), rearPose.getNumTagsSeen());
61+
Pose2d[] allTagPoses = Arrays.copyOf(frontPose.tagPosesMeters(), frontPose.getNumTagsSeen() + rearPose.getNumTagsSeen());
62+
System.arraycopy(rearPose.tagPosesMeters(), 0, allTagPoses, frontPose.getNumTagsSeen(), rearPose.getNumTagsSeen());
63+
64+
return new TimestampedVisionPose(
65+
(frontPose.timestampSecs() + rearPose.timestampSecs()) / 2,
66+
frontPose.poseMeters().interpolate(rearPose.poseMeters(), 0.5),
67+
allTagIDs,
68+
allTagPoses);
69+
}
70+
}

src/main/java/org/team1540/robot2024/subsystems/vision/AprilTagVisionIO.java

+3-5
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,9 @@ public interface AprilTagVisionIO {
88
class AprilTagVisionIOInputs {
99
public Pose2d estimatedPoseMeters = new Pose2d();
1010
public boolean hasTarget = false;
11-
public int[] seenTargetIDs = {};
12-
public Pose2d[] targetPosesMeters = {};
13-
14-
public double pipelineLatency = 0.0;
15-
public double captureLatency = 0.0;
11+
public int[] seenTagIDs = {};
12+
public Pose2d[] tagPosesMeters = {};
13+
public double lastMeasurementTimestampSecs = 0.0;
1614
}
1715

1816
default void updateInputs(AprilTagVisionIOInputs inputs) {}

src/main/java/org/team1540/robot2024/subsystems/vision/AprilTagVisionIOLimelight.java

+11-6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import edu.wpi.first.math.geometry.Pose2d;
44
import edu.wpi.first.math.geometry.Pose3d;
55
import edu.wpi.first.math.geometry.Rotation3d;
6+
import edu.wpi.first.wpilibj.Timer;
67
import org.team1540.robot2024.util.vision.LimelightHelpers;
78

89
public class AprilTagVisionIOLimelight implements AprilTagVisionIO {
@@ -36,13 +37,17 @@ public void updateInputs(AprilTagVisionIOInputs inputs) {
3637
)
3738
).toPose2d();
3839
inputs.hasTarget = results.valid;
39-
inputs.seenTargetIDs = new int[fiducialTargets.length];
40-
for (int i = 0; i < fiducialTargets.length; i++) inputs.seenTargetIDs[i] = (int) fiducialTargets[i].fiducialID; // fiducial tag ids had better be ints
41-
inputs.targetPosesMeters = new Pose2d[fiducialTargets.length];
42-
for (int i = 0; i < fiducialTargets.length; i++) inputs.targetPosesMeters[i] = fiducialTargets[i].getTargetPose_RobotSpace2D();
4340

44-
inputs.captureLatency = results.latency_capture;
45-
inputs.pipelineLatency = results.latency_pipeline;
41+
inputs.seenTagIDs = new int[fiducialTargets.length];
42+
for (int i = 0; i < fiducialTargets.length; i++)
43+
inputs.seenTagIDs[i] = (int) fiducialTargets[i].fiducialID; // fiducial tag ids had better be ints
44+
45+
inputs.tagPosesMeters = new Pose2d[fiducialTargets.length];
46+
for (int i = 0; i < fiducialTargets.length; i++)
47+
inputs.tagPosesMeters[i] = fiducialTargets[i].getTargetPose_RobotSpace2D();
48+
49+
inputs.lastMeasurementTimestampSecs =
50+
Timer.getFPGATimestamp() - (results.latency_capture + results.latency_pipeline) / 1000.0;
4651
}
4752

4853
@Override

src/main/java/org/team1540/robot2024/util/vision/LimelightHelpers.java

+3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
import com.fasterxml.jackson.databind.DeserializationFeature;
2525
import com.fasterxml.jackson.databind.ObjectMapper;
2626

27+
// NOTE: This file is available at
28+
// https://github.com/LimelightVision/limelightlib-wpijava/blob/main/LimelightHelpers.java
29+
2730
public class LimelightHelpers {
2831

2932
public static class LimelightTarget_Retro {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package org.team1540.robot2024.util.vision;
2+
3+
import edu.wpi.first.math.geometry.Pose2d;
4+
5+
import java.util.Arrays;
6+
import java.util.Objects;
7+
8+
/**
9+
* @param timestampSecs RIO FPGA timestamp of this vision pose
10+
* @param poseMeters Measured, blue alliance-origin pose from vision
11+
* @param seenTagIDs Array of all tags used to compute this measurement
12+
* @param tagPosesMeters Pose in robot-space of all tags used to compute this measurement
13+
*/
14+
public record TimestampedVisionPose(
15+
double timestampSecs,
16+
Pose2d poseMeters,
17+
int[] seenTagIDs,
18+
Pose2d[] tagPosesMeters) {
19+
20+
public int getNumTagsSeen() {
21+
return seenTagIDs.length;
22+
}
23+
24+
public double getAverageTagDistance() {
25+
double sumDistances = 0.0;
26+
for (Pose2d pose : tagPosesMeters) sumDistances += pose.getTranslation().getNorm();
27+
return sumDistances / getNumTagsSeen();
28+
}
29+
30+
@Override
31+
public boolean equals(Object o) {
32+
if (this == o) return true;
33+
if (o == null || getClass() != o.getClass()) return false;
34+
TimestampedVisionPose that = (TimestampedVisionPose) o;
35+
return Double.compare(timestampSecs, that.timestampSecs) == 0
36+
&& Objects.equals(poseMeters, that.poseMeters)
37+
&& Arrays.equals(seenTagIDs, that.seenTagIDs)
38+
&& Arrays.equals(tagPosesMeters, that.tagPosesMeters);
39+
}
40+
}

0 commit comments

Comments
 (0)