Adding some more AprilTag stuff, comments for interfaces, and a mess of TODOs
This commit is contained in:
parent
da6df8b490
commit
477f066585
@ -4,11 +4,14 @@
|
|||||||
|
|
||||||
package frc.robot;
|
package frc.robot;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import com.pathplanner.lib.auto.AutoBuilder;
|
import com.pathplanner.lib.auto.AutoBuilder;
|
||||||
import com.pathplanner.lib.auto.NamedCommands;
|
import com.pathplanner.lib.auto.NamedCommands;
|
||||||
|
|
||||||
import edu.wpi.first.math.MathUtil;
|
import edu.wpi.first.math.MathUtil;
|
||||||
import edu.wpi.first.math.geometry.Pose2d;
|
import edu.wpi.first.math.geometry.Pose2d;
|
||||||
|
import edu.wpi.first.wpilibj.DriverStation;
|
||||||
import edu.wpi.first.wpilibj.smartdashboard.SendableChooser;
|
import edu.wpi.first.wpilibj.smartdashboard.SendableChooser;
|
||||||
import edu.wpi.first.wpilibj2.command.Command;
|
import edu.wpi.first.wpilibj2.command.Command;
|
||||||
import edu.wpi.first.wpilibj2.command.button.CommandXboxController;
|
import edu.wpi.first.wpilibj2.command.button.CommandXboxController;
|
||||||
@ -23,10 +26,19 @@ public class RobotContainer {
|
|||||||
|
|
||||||
private SendableChooser<Command> autoChooser;
|
private SendableChooser<Command> autoChooser;
|
||||||
|
|
||||||
|
private int ampTagID;
|
||||||
|
|
||||||
|
// TODO There's more than one source tag, how do we want to handle this?
|
||||||
|
private int sourceTagID;
|
||||||
|
|
||||||
|
// TODO There's more than one speaker tag, how do we want to handle this?
|
||||||
|
private int speakerTag;
|
||||||
|
|
||||||
public RobotContainer() {
|
public RobotContainer() {
|
||||||
// TODO Need to provide a real initial pose
|
// TODO Need to provide a real initial pose
|
||||||
|
// TODO Need to provide a real IAprilTagProvider, null means we're not using one at all
|
||||||
// TODO Need to provide a real VisualPoseProvider, null means we're not using one at all
|
// TODO Need to provide a real VisualPoseProvider, null means we're not using one at all
|
||||||
drivetrain = new Drivetrain(new Pose2d(), null);
|
drivetrain = new Drivetrain(new Pose2d(), null, null);
|
||||||
|
|
||||||
// An example Named Command, doesn't need to remain once we start actually adding real things
|
// An example Named Command, doesn't need to remain once we start actually adding real things
|
||||||
// ALL Named Commands need to be defined AFTER subsystem initialization and BEFORE auto/controller configuration
|
// ALL Named Commands need to be defined AFTER subsystem initialization and BEFORE auto/controller configuration
|
||||||
@ -38,6 +50,18 @@ public class RobotContainer {
|
|||||||
primary = new CommandXboxController(OIConstants.kPrimaryXboxUSB);
|
primary = new CommandXboxController(OIConstants.kPrimaryXboxUSB);
|
||||||
secondary = new CommandXboxController(OIConstants.kSecondaryXboxUSB);
|
secondary = new CommandXboxController(OIConstants.kSecondaryXboxUSB);
|
||||||
|
|
||||||
|
Optional<DriverStation.Alliance> alliance = DriverStation.getAlliance();
|
||||||
|
|
||||||
|
if (alliance.isEmpty() || alliance.get() == DriverStation.Alliance.Red) {
|
||||||
|
ampTagID = 5;
|
||||||
|
sourceTagID = 9;
|
||||||
|
speakerTag = 4;
|
||||||
|
} else {
|
||||||
|
ampTagID = 6;
|
||||||
|
sourceTagID = 1;
|
||||||
|
speakerTag = 8;
|
||||||
|
}
|
||||||
|
|
||||||
configureBindings();
|
configureBindings();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,10 +90,49 @@ public class RobotContainer {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// This was originally a run while held, not sure that's really necessary
|
// Lock on to the appropriate Amp AprilTag while still being able to strafe and drive forward and back
|
||||||
|
primary.a().onTrue(
|
||||||
|
drivetrain.driveAprilTagLock(
|
||||||
|
primary::getLeftX,
|
||||||
|
primary::getLeftY,
|
||||||
|
OIConstants.kTeleopDriveDeadband,
|
||||||
|
ampTagID,
|
||||||
|
960 // TODO This is a guess, the vision system should be an ICamera and this should be vision.getWidth()
|
||||||
|
).until(
|
||||||
|
() -> MathUtil.applyDeadband(primary.getRightX(), OIConstants.kTeleopDriveDeadband) != 0
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Lock on to the appropriate Source AprilTag while still being able to strafe and drive forward and back
|
||||||
|
primary.b().onTrue(
|
||||||
|
drivetrain.driveAprilTagLock(
|
||||||
|
primary::getLeftX,
|
||||||
|
primary::getLeftY,
|
||||||
|
OIConstants.kTeleopDriveDeadband,
|
||||||
|
sourceTagID,
|
||||||
|
960 // TODO This is a guess, the vision system should be an ICamera and this should be vision.getWidth()
|
||||||
|
).until(
|
||||||
|
() -> MathUtil.applyDeadband(primary.getRightX(), OIConstants.kTeleopDriveDeadband) != 0
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Lock on to the appropriate Speaker AprilTag while still being able to strafe and drive forward and back
|
||||||
|
primary.x().onTrue(
|
||||||
|
drivetrain.driveAprilTagLock(
|
||||||
|
primary::getLeftX,
|
||||||
|
primary::getLeftY,
|
||||||
|
OIConstants.kTeleopDriveDeadband,
|
||||||
|
speakerTag,
|
||||||
|
960 // TODO This is a guess, the vision system should be an ICamera and this should be vision.getWidth()
|
||||||
|
).until(
|
||||||
|
() -> MathUtil.applyDeadband(primary.getRightX(), OIConstants.kTeleopDriveDeadband) != 0
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// This was originally a run while held, not sure that's really necessary, change it if need be
|
||||||
primary.y().onTrue(drivetrain.zeroHeadingCommand());
|
primary.y().onTrue(drivetrain.zeroHeadingCommand());
|
||||||
|
|
||||||
// This was originally a run while held, not sure that's really necessary
|
// This was originally a run while held, not sure that's really necessary, change it if need be
|
||||||
primary.rightBumper().onTrue(drivetrain.setXCommand());
|
primary.rightBumper().onTrue(drivetrain.setXCommand());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,8 +15,10 @@ public final class DrivetrainConstants {
|
|||||||
public static final double kRotationalSlewRate = 4.0; // percent per second (1 = 100%)
|
public static final double kRotationalSlewRate = 4.0; // percent per second (1 = 100%)
|
||||||
|
|
||||||
// Chassis configuration
|
// Chassis configuration
|
||||||
|
// TODO Update track width
|
||||||
public static final double kTrackWidth = Units.inchesToMeters(26.5-1.75*2);
|
public static final double kTrackWidth = Units.inchesToMeters(26.5-1.75*2);
|
||||||
// Distance between centers of right and left wheels on robot
|
// Distance between centers of right and left wheels on robot
|
||||||
|
// TODO Update wheel base
|
||||||
public static final double kWheelBase = Units.inchesToMeters(32.3-1.75*2);
|
public static final double kWheelBase = Units.inchesToMeters(32.3-1.75*2);
|
||||||
// Distance between front and back wheels on robot
|
// Distance between front and back wheels on robot
|
||||||
public static final SwerveDriveKinematics kDriveKinematics = new SwerveDriveKinematics(
|
public static final SwerveDriveKinematics kDriveKinematics = new SwerveDriveKinematics(
|
||||||
|
@ -2,9 +2,47 @@ package frc.robot.interfaces;
|
|||||||
|
|
||||||
import java.util.OptionalDouble;
|
import java.util.OptionalDouble;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface which ensures a class can provide common AprilTag oriented
|
||||||
|
* information from various sources in a consistent way.
|
||||||
|
*/
|
||||||
public interface IAprilTagProvider {
|
public interface IAprilTagProvider {
|
||||||
|
/**
|
||||||
|
* A method to get the AprilTag offset from the center of the image. This is intended to be used with
|
||||||
|
* a PIDController, the PIDController keeps the robot rotated such that the AprilTag stays in the horizontal
|
||||||
|
* center of the image.
|
||||||
|
*
|
||||||
|
* TODO Is there some other way of ensuring an AprilTag is in the center of the image frame without knowing the horizontal resolution?
|
||||||
|
* TODO Certain providers don't give resolution information natively. Which makes me think there's some other way to do this.
|
||||||
|
*
|
||||||
|
* @param id The ID of the AprilTag to give an offset from
|
||||||
|
* @param cameraHalfXRes Half of the horizontal resolution of the camera sensor
|
||||||
|
* @return A double with the number of pixels offset from center the tag is, or OptionalDouble.empty() if the tag is not present in the camera's view
|
||||||
|
*/
|
||||||
public OptionalDouble getTagOffsetFromCameraCenterByID(int id, double cameraHalfXRes);
|
public OptionalDouble getTagOffsetFromCameraCenterByID(int id, double cameraHalfXRes);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A method to get the distance from <i>the camera</i> to the AprilTag specified
|
||||||
|
*
|
||||||
|
* @param id The ID of the AprilTag to give a distance to
|
||||||
|
* @param targetHeightMeters The height of the AprilTag off the ground, in meters
|
||||||
|
* @return The distance, in meters, to the target, or OptionalDouble.empty() if the tag is not present in the camera's view
|
||||||
|
*/
|
||||||
public OptionalDouble getTagDistanceFromCameraByID(int id, double targetHeightMeters);
|
public OptionalDouble getTagDistanceFromCameraByID(int id, double targetHeightMeters);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A method to get the pitch of a particular AprilTag
|
||||||
|
*
|
||||||
|
* @param id The ID of the AprilTag to get the pitch of
|
||||||
|
* @return The pitch, in radians, of the target, or OptionalDouble.empty() if the tag is not present in the camera's view
|
||||||
|
*/
|
||||||
public OptionalDouble getTagPitchByID(int id);
|
public OptionalDouble getTagPitchByID(int id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A method to get the yaw of a particular AprilTag
|
||||||
|
*
|
||||||
|
* @param id The ID of the AprilTag to get the yaw of
|
||||||
|
* @return The yaw, in radians, of the target, or OptionalDouble.empty() if the tag is not present in the camera's view
|
||||||
|
*/
|
||||||
public OptionalDouble getTagYawByID(int id);
|
public OptionalDouble getTagYawByID(int id);
|
||||||
}
|
}
|
||||||
|
21
src/main/java/frc/robot/interfaces/ICamera.java
Normal file
21
src/main/java/frc/robot/interfaces/ICamera.java
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package frc.robot.interfaces;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface which ensures a class can provide common camera
|
||||||
|
* properties in a consistent way
|
||||||
|
*/
|
||||||
|
public interface ICamera {
|
||||||
|
/**
|
||||||
|
* Retrieves the width of the camera's resolution
|
||||||
|
*
|
||||||
|
* @return The width of the resolution, in pixels
|
||||||
|
*/
|
||||||
|
public double getWidth();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the height of the camera's resolution
|
||||||
|
*
|
||||||
|
* @return The height of the resolution, in pixels
|
||||||
|
*/
|
||||||
|
public double getHeight();
|
||||||
|
}
|
@ -4,8 +4,24 @@ import java.util.Optional;
|
|||||||
|
|
||||||
import edu.wpi.first.math.geometry.Pose2d;
|
import edu.wpi.first.math.geometry.Pose2d;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface which ensures a class' ability to provide visual pose information
|
||||||
|
* in a consistent way
|
||||||
|
*/
|
||||||
public interface IVisualPoseProvider {
|
public interface IVisualPoseProvider {
|
||||||
|
/**
|
||||||
|
* A record that can contain the two elements necessary for a WPILIB
|
||||||
|
* pose estimator to use the information from a vision system as part of a full
|
||||||
|
* robot pose estimation
|
||||||
|
*/
|
||||||
public record VisualPose(Pose2d visualPose, double timestamp) {}
|
public record VisualPose(Pose2d visualPose, double timestamp) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a VisualPose or null if an empty Optional if none is available.
|
||||||
|
* Implementation should provide an empty response if it's unable to provide
|
||||||
|
* a reliable pose, or any pose at all.
|
||||||
|
*
|
||||||
|
* @return An Optional containing a VisualPose, or empty if no VisualPose can reliably be provided
|
||||||
|
*/
|
||||||
public Optional<VisualPose> getVisualPose();
|
public Optional<VisualPose> getVisualPose();
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import edu.wpi.first.math.kinematics.SwerveModuleState;
|
|||||||
import edu.wpi.first.util.WPIUtilJNI;
|
import edu.wpi.first.util.WPIUtilJNI;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.OptionalDouble;
|
||||||
import java.util.function.DoubleSupplier;
|
import java.util.function.DoubleSupplier;
|
||||||
|
|
||||||
import com.kauailabs.navx.frc.AHRS;
|
import com.kauailabs.navx.frc.AHRS;
|
||||||
@ -24,10 +25,12 @@ import frc.robot.constants.AutoConstants;
|
|||||||
import frc.robot.constants.DrivetrainConstants;
|
import frc.robot.constants.DrivetrainConstants;
|
||||||
import frc.robot.utilities.MAXSwerveModule;
|
import frc.robot.utilities.MAXSwerveModule;
|
||||||
import frc.robot.utilities.SwerveUtils;
|
import frc.robot.utilities.SwerveUtils;
|
||||||
|
import frc.robot.interfaces.IAprilTagProvider;
|
||||||
import frc.robot.interfaces.IVisualPoseProvider;
|
import frc.robot.interfaces.IVisualPoseProvider;
|
||||||
import frc.robot.interfaces.IVisualPoseProvider.VisualPose;
|
import frc.robot.interfaces.IVisualPoseProvider.VisualPose;
|
||||||
import edu.wpi.first.wpilibj2.command.Command;
|
import edu.wpi.first.wpilibj2.command.Command;
|
||||||
import edu.wpi.first.wpilibj2.command.PIDCommand;
|
import edu.wpi.first.wpilibj2.command.PIDCommand;
|
||||||
|
import edu.wpi.first.wpilibj2.command.PrintCommand;
|
||||||
import edu.wpi.first.wpilibj2.command.SubsystemBase;
|
import edu.wpi.first.wpilibj2.command.SubsystemBase;
|
||||||
|
|
||||||
public class Drivetrain extends SubsystemBase {
|
public class Drivetrain extends SubsystemBase {
|
||||||
@ -43,6 +46,8 @@ public class Drivetrain extends SubsystemBase {
|
|||||||
private final SlewRateLimiter m_magLimiter;
|
private final SlewRateLimiter m_magLimiter;
|
||||||
private final SlewRateLimiter m_rotLimiter;
|
private final SlewRateLimiter m_rotLimiter;
|
||||||
|
|
||||||
|
private final IAprilTagProvider m_aprilTagProvider;
|
||||||
|
|
||||||
private final IVisualPoseProvider m_visualPoseProvider;
|
private final IVisualPoseProvider m_visualPoseProvider;
|
||||||
|
|
||||||
// Odometry class for tracking robot pose
|
// Odometry class for tracking robot pose
|
||||||
@ -56,7 +61,7 @@ public class Drivetrain extends SubsystemBase {
|
|||||||
private double m_prevTime;
|
private double m_prevTime;
|
||||||
|
|
||||||
/** Creates a new DriveSubsystem. */
|
/** Creates a new DriveSubsystem. */
|
||||||
public Drivetrain(Pose2d initialPose, IVisualPoseProvider visualPoseProvider) {
|
public Drivetrain(Pose2d initialPose, IAprilTagProvider aprilTagProvider, IVisualPoseProvider visualPoseProvider) {
|
||||||
m_frontLeft = new MAXSwerveModule(
|
m_frontLeft = new MAXSwerveModule(
|
||||||
DrivetrainConstants.kFrontLeftDrivingCanId,
|
DrivetrainConstants.kFrontLeftDrivingCanId,
|
||||||
DrivetrainConstants.kFrontLeftTurningCanId,
|
DrivetrainConstants.kFrontLeftTurningCanId,
|
||||||
@ -80,6 +85,7 @@ public class Drivetrain extends SubsystemBase {
|
|||||||
DrivetrainConstants.kBackRightChassisAngularOffset
|
DrivetrainConstants.kBackRightChassisAngularOffset
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// TODO The NavX is not centered in the robot, it's output probably needs to be translated
|
||||||
m_gyro = new AHRS(SPI.Port.kMXP);
|
m_gyro = new AHRS(SPI.Port.kMXP);
|
||||||
|
|
||||||
m_magLimiter = new SlewRateLimiter(DrivetrainConstants.kMagnitudeSlewRate);
|
m_magLimiter = new SlewRateLimiter(DrivetrainConstants.kMagnitudeSlewRate);
|
||||||
@ -97,6 +103,8 @@ public class Drivetrain extends SubsystemBase {
|
|||||||
initialPose
|
initialPose
|
||||||
);
|
);
|
||||||
|
|
||||||
|
m_aprilTagProvider = aprilTagProvider;
|
||||||
|
|
||||||
m_visualPoseProvider = visualPoseProvider;
|
m_visualPoseProvider = visualPoseProvider;
|
||||||
|
|
||||||
m_currentRotation = 0.0;
|
m_currentRotation = 0.0;
|
||||||
@ -307,6 +315,44 @@ public class Drivetrain extends SubsystemBase {
|
|||||||
this);
|
this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Command driveAprilTagLock(DoubleSupplier xSpeed, DoubleSupplier ySpeed, double deadband, int tagID, double cameraHorizontalResolution) {
|
||||||
|
if (m_aprilTagProvider == null) {
|
||||||
|
return new PrintCommand("No AprilTag Provider Available");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO The process variable is different here than what these constants are used for, may need to use something different
|
||||||
|
PIDController controller = new PIDController(
|
||||||
|
AutoConstants.kPHeadingController,
|
||||||
|
0,
|
||||||
|
AutoConstants.kDHeadingController
|
||||||
|
);
|
||||||
|
|
||||||
|
return new PIDCommand(
|
||||||
|
controller,
|
||||||
|
() -> {
|
||||||
|
OptionalDouble tagOffset = m_aprilTagProvider.getTagOffsetFromCameraCenterByID(tagID, cameraHorizontalResolution);
|
||||||
|
|
||||||
|
if (tagOffset.isEmpty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tagOffset.getAsDouble();
|
||||||
|
},
|
||||||
|
() -> { return 0; },
|
||||||
|
(output) -> {
|
||||||
|
// TODO Field relative or no? What makes sense here, since the intent is to orient to the tag, not the field.
|
||||||
|
this.drive(
|
||||||
|
-MathUtil.applyDeadband(xSpeed.getAsDouble(), deadband),
|
||||||
|
-MathUtil.applyDeadband(ySpeed.getAsDouble(), deadband),
|
||||||
|
output,
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
},
|
||||||
|
this
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the wheels into an X formation to prevent movement.
|
* Sets the wheels into an X formation to prevent movement.
|
||||||
*/
|
*/
|
||||||
|
@ -19,9 +19,10 @@ import edu.wpi.first.apriltag.AprilTagFields;
|
|||||||
import edu.wpi.first.math.geometry.Transform3d;
|
import edu.wpi.first.math.geometry.Transform3d;
|
||||||
import edu.wpi.first.math.util.Units;
|
import edu.wpi.first.math.util.Units;
|
||||||
import frc.robot.interfaces.IAprilTagProvider;
|
import frc.robot.interfaces.IAprilTagProvider;
|
||||||
|
import frc.robot.interfaces.ICamera;
|
||||||
import frc.robot.interfaces.IVisualPoseProvider;
|
import frc.robot.interfaces.IVisualPoseProvider;
|
||||||
|
|
||||||
public class PhotonVision implements IAprilTagProvider,IVisualPoseProvider {
|
public class PhotonVision implements ICamera,IAprilTagProvider,IVisualPoseProvider {
|
||||||
|
|
||||||
private final PhotonCamera camera;
|
private final PhotonCamera camera;
|
||||||
|
|
||||||
@ -29,8 +30,11 @@ public class PhotonVision implements IAprilTagProvider,IVisualPoseProvider {
|
|||||||
|
|
||||||
private final double cameraHeightMeters;
|
private final double cameraHeightMeters;
|
||||||
private final double cameraPitchRadians;
|
private final double cameraPitchRadians;
|
||||||
|
private final double cameraWidth;
|
||||||
|
private final double cameraHeight;
|
||||||
|
|
||||||
public PhotonVision(String cameraName, Transform3d robotToCam, double cameraHeightMeters, double cameraPitchRadians) throws IOException {
|
public PhotonVision(String cameraName, Transform3d robotToCam, double cameraHeightMeters, double cameraPitchRadians,
|
||||||
|
double cameraWidth, double cameraHeight) throws IOException {
|
||||||
camera = new PhotonCamera(cameraName);
|
camera = new PhotonCamera(cameraName);
|
||||||
|
|
||||||
photonPoseEstimator = new PhotonPoseEstimator(
|
photonPoseEstimator = new PhotonPoseEstimator(
|
||||||
@ -44,6 +48,18 @@ public class PhotonVision implements IAprilTagProvider,IVisualPoseProvider {
|
|||||||
|
|
||||||
this.cameraHeightMeters = cameraHeightMeters;
|
this.cameraHeightMeters = cameraHeightMeters;
|
||||||
this.cameraPitchRadians = cameraPitchRadians;
|
this.cameraPitchRadians = cameraPitchRadians;
|
||||||
|
this.cameraWidth = cameraWidth;
|
||||||
|
this.cameraHeight = cameraHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getWidth() {
|
||||||
|
return cameraWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getHeight() {
|
||||||
|
return cameraHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
Loading…
Reference in New Issue
Block a user