Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Created VisionIO template to set up RealVision and SimVision #20

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 6 additions & 4 deletions src/main/java/org/sciborgs1155/robot/Constants.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package org.sciborgs1155.robot;

import static edu.wpi.first.units.Units.*;

import edu.wpi.first.units.Measure;
import edu.wpi.first.units.Time;
import edu.wpi.first.apriltag.AprilTagFieldLayout;
import edu.wpi.first.apriltag.AprilTagFields;
import edu.wpi.first.math.geometry.Rotation3d;
import edu.wpi.first.math.geometry.Transform3d;
import edu.wpi.first.math.geometry.Translation3d;

/**
* Constants is a globally accessible class for storing immutable values. Every value should be
Expand All @@ -17,4 +18,5 @@
public class Constants {
public static final Measure<Time> PERIOD = Seconds.of(0.02); // roborio tickrate (s)
public static final double DEADBAND = 0.1;
public static final int THROUGHBORE_PPR = 2048;
}
139 changes: 139 additions & 0 deletions src/main/java/org/sciborgs1155/robot/vision/Vision.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package org.sciborgs1155.robot.vision;

import java.util.Optional;

import org.photonvision.EstimatedRobotPose;
import org.photonvision.PhotonCamera;
import org.photonvision.PhotonPoseEstimator;
import org.photonvision.PhotonPoseEstimator.PoseStrategy;
import org.photonvision.simulation.PhotonCameraSim;
import org.photonvision.simulation.SimCameraProperties;
import org.photonvision.simulation.VisionSystemSim;
import org.photonvision.targeting.PhotonPipelineResult;
import org.sciborgs1155.robot.Robot;

import edu.wpi.first.math.Matrix;
import edu.wpi.first.math.VecBuilder;
import edu.wpi.first.math.geometry.Pose2d;
import edu.wpi.first.math.geometry.Rotation2d;
import edu.wpi.first.math.numbers.N1;
import edu.wpi.first.math.numbers.N3;
import edu.wpi.first.wpilibj.smartdashboard.Field2d;
import org.sciborgs1155.robot.vision.VisionConstants;
public class Vision {
private final PhotonCamera camera;
private final PhotonPoseEstimator photonEstimator;
private double lastEstTimestamp = 0;

// Simulation
private PhotonCameraSim cameraSim;
private VisionSystemSim visionSim;

public Vision() {
camera = new PhotonCamera(VisionConstants.CAMERA_NAME);

photonEstimator =
new PhotonPoseEstimator(
VisionConstants.TAG_LAYOUT, PoseStrategy.MULTI_TAG_PNP_ON_COPROCESSOR, camera, VisionConstants.ROBOT_TO_CAM);
photonEstimator.setMultiTagFallbackStrategy(PoseStrategy.LOWEST_AMBIGUITY);

// ----- Simulation
if (Robot.isSimulation()) {
// Create the vision system simulation which handles cameras and targets on the field.
visionSim = new VisionSystemSim("main");
// Add all the AprilTags inside the tag layout as visible targets to this simulated field.
visionSim.addAprilTags(VisionConstants.TAG_LAYOUT);
// Create simulated camera properties. These can be set to mimic your actual camera.
var cameraProp = new SimCameraProperties();
cameraProp.setCalibration(960, 720, Rotation2d.fromDegrees(90));
cameraProp.setCalibError(0.35, 0.10);
cameraProp.setFPS(15);
cameraProp.setAvgLatencyMs(50);
cameraProp.setLatencyStdDevMs(15);
// Create a PhotonCameraSim which will update the linked PhotonCamera's values with visible
// targets.
cameraSim = new PhotonCameraSim(camera, cameraProp);
// Add the simulated camera to view the targets on this simulated field.
visionSim.addCamera(cameraSim,VisionConstants.ROBOT_TO_CAM);

cameraSim.enableDrawWireframe(true);
}
}

public PhotonPipelineResult getLatestResult() {
return camera.getLatestResult();
}

/**
* The latest estimated robot pose on the field from vision data. This may be empty. This should
* only be called once per loop.
*
* @return An {@link EstimatedRobotPose} with an estimated pose, estimate timestamp, and targets
* used for estimation.
*/
public Optional<EstimatedRobotPose> getEstimatedGlobalPose() {
var visionEst = photonEstimator.update();
double latestTimestamp = camera.getLatestResult().getTimestampSeconds();
boolean newResult = Math.abs(latestTimestamp - lastEstTimestamp) > 1e-5;
if (Robot.isSimulation()) {
visionEst.ifPresentOrElse(
est ->
getSimDebugField()
.getObject("VisionEstimation")
.setPose(est.estimatedPose.toPose2d()),
() -> {
if (newResult) getSimDebugField().getObject("VisionEstimation").setPoses();
});
}
if (newResult) lastEstTimestamp = latestTimestamp;
return visionEst;
}

/**
* The standard deviations of the estimated pose from {@link #getEstimatedGlobalPose()}, for use
* with {@link edu.wpi.first.math.estimator.SwerveDrivePoseEstimator SwerveDrivePoseEstimator}.
* This should only be used when there are targets visible.
*
* @param estimatedPose The estimated pose to guess standard deviations for.
*/
public Matrix<N3, N1> getEstimationStdDevs(Pose2d estimatedPose) {
var estStdDevs = VisionConstants.SINGLE_TAG_STD_DEVS;
var targets = getLatestResult().getTargets();
int numTags = 0;
double avgDist = 0;
for (var tgt : targets) {
var tagPose = photonEstimator.getFieldTags().getTagPose(tgt.getFiducialId());
if (tagPose.isEmpty()) continue;
numTags++;
avgDist +=
tagPose.get().toPose2d().getTranslation().getDistance(estimatedPose.getTranslation());
}
if (numTags == 0) return estStdDevs;
avgDist /= numTags;
// Decrease std devs if multiple targets are visible
if (numTags > 1) estStdDevs = VisionConstants.MULTIPLE_TAG_STD_DEVS;
// Increase std devs based on (average) distance
if (numTags == 1 && avgDist > 4)
estStdDevs = VecBuilder.fill(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE);
else estStdDevs = estStdDevs.times(1 + (avgDist * avgDist / 30));

return estStdDevs;
}

// ----- Simulation

public void simulationPeriodic(Pose2d robotSimPose) {
visionSim.update(robotSimPose);
}

/** Reset pose history of the robot in the vision system simulation. */
public void resetSimPose(Pose2d pose) {
if (Robot.isSimulation()) visionSim.resetRobotPose(pose);
}

/** A Field2d for visualizing our robot and objects on the field. */
public Field2d getSimDebugField() {
if (!Robot.isSimulation()) return null;
return visionSim.getDebugField();
}
}
30 changes: 30 additions & 0 deletions src/main/java/org/sciborgs1155/robot/vision/VisionConstants.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.sciborgs1155.robot.vision;

import edu.wpi.first.apriltag.AprilTagFieldLayout;
import edu.wpi.first.apriltag.AprilTagFields;
import edu.wpi.first.math.Matrix;
import edu.wpi.first.math.VecBuilder;
import edu.wpi.first.math.geometry.Rotation3d;
import edu.wpi.first.math.geometry.Transform3d;
import edu.wpi.first.math.geometry.Translation3d;
import edu.wpi.first.math.numbers.N1;
import edu.wpi.first.math.numbers.N3;
import edu.wpi.first.math.util.Units;
import org.photonvision.PhotonPoseEstimator.PoseStrategy;

public class VisionConstants {
public static final AprilTagFieldLayout TAG_LAYOUT = null;

public static final String CAMERA_NAME= "placeholder";
public static final Transform3d ROBOT_TO_CAM =
new Transform3d(new Translation3d(0.5, 0.0, 0.5), new Rotation3d(0, 0, 0));

// The layout of the AprilTags on the field
public static final AprilTagFieldLayout kTagLayout =
AprilTagFields.kDefaultField.loadAprilTagLayoutField();

// The standard deviations of
public static final Matrix<N3, N1> SINGLE_TAG_STD_DEVS = VecBuilder.fill(4, 4, 8);
public static final Matrix<N3, N1> MULTIPLE_TAG_STD_DEVS = VecBuilder.fill(4, 4, 8);

}
Loading