Skip to content

Commit

Permalink
moved vision related things from DriveSubsystem to PhotonPoseEstimati…
Browse files Browse the repository at this point in the history
…onSubsystem
  • Loading branch information
jhhbrown1 committed Jan 19, 2025
1 parent 2c7dfed commit 11c715b
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 68 deletions.
8 changes: 4 additions & 4 deletions src/main/java/frc/robot/Robot.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@
import edu.wpi.first.wpilibj2.command.button.CommandPS4Controller;
import frc.robot.Constants.ControllerConstants;
import frc.robot.subsystems.DriveSubsystem;
import frc.robot.subsystems.Vision;
import frc.robot.subsystems.PhotonPoseEstimationSubsystem;

public class Robot extends TimedRobot {
private Command m_autonomousCommand;
private final Vision m_vision = new Vision();
private final DriveSubsystem m_driveSubsystem = new DriveSubsystem();
PhotonPoseEstimationSubsystem m_photonVisionSubsystem = new PhotonPoseEstimationSubsystem(m_driveSubsystem);
// private final PhotonVisionSubsystem m_PhotonVisionSubsystem = new
// PhotonVisionSubsystem("Cool camera");
private final DriveSubsystem m_driveSubsystem = new DriveSubsystem();
private final CommandPS4Controller m_driverController = new CommandPS4Controller(
ControllerConstants.kDriverControllerPort);
private final PowerDistribution m_pdh = new PowerDistribution();
Expand All @@ -49,7 +49,7 @@ public void bindDriveControls() {
m_driverController.getHID()::getSquareButton));
m_driverController.L1()
.whileTrue(
m_driveSubsystem.tagDriveCommand(
m_photonVisionSubsystem.tagDriveCommand(
() -> -m_driverController.getLeftY(),
() -> -m_driverController.getLeftX(),
() -> m_driverController.getR2Axis() - m_driverController.getL2Axis(),
Expand Down
90 changes: 26 additions & 64 deletions src/main/java/frc/robot/subsystems/DriveSubsystem.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,12 @@
import java.util.function.BooleanSupplier;
import java.util.function.DoubleSupplier;

import org.photonvision.targeting.PhotonTrackedTarget;

import com.studica.frc.AHRS;
import com.studica.frc.AHRS.NavXComType;

import edu.wpi.first.math.MathUtil;
import edu.wpi.first.math.controller.PIDController;
import edu.wpi.first.math.estimator.SwerveDrivePoseEstimator;
import edu.wpi.first.math.geometry.Pose2d;
import edu.wpi.first.math.geometry.Rotation2d;
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;
Expand All @@ -29,7 +24,6 @@
import edu.wpi.first.networktables.NetworkTableInstance;
import edu.wpi.first.networktables.StructArrayPublisher;
import edu.wpi.first.networktables.StructPublisher;
import edu.wpi.first.wpilibj.Timer;
import edu.wpi.first.wpilibj2.command.Command;
import edu.wpi.first.wpilibj2.command.SubsystemBase;
import edu.wpi.first.wpilibj2.command.sysid.SysIdRoutine;
Expand All @@ -47,23 +41,16 @@ public class DriveSubsystem extends SubsystemBase {
private final AHRS m_gyro = new AHRS(NavXComType.kMXP_SPI);
// https://docs.wpilib.org/en/latest/docs/software/advanced-controls/system-identification/index.html
private final SysIdRoutine m_sysidRoutine;
private final SwerveDrivePoseEstimator m_poseEstimator;
private final Vision m_vision;
private final DriveSim m_driveSim;
private final SwerveDriveOdometry m_odometry;

private final StructPublisher<Pose2d> m_posePublisher;
private final StructPublisher<Pose2d> m_visionPosePublisher;
private final StructArrayPublisher<SwerveModuleState> m_targetModuleStatePublisher;
private final StructArrayPublisher<SwerveModuleState> m_currentModuleStatePublisher;

/** Creates a new DriveSubsystem. */
public DriveSubsystem() {
m_posePublisher = NetworkTableInstance.getDefault().getStructTopic("/SmartDashboard/Pose2d", Pose2d.struct)
.publish();
m_visionPosePublisher = NetworkTableInstance.getDefault()
.getStructTopic("/SmartDashboard/VisionPose", Pose2d.struct)
.publish();
m_targetModuleStatePublisher = NetworkTableInstance.getDefault()
.getStructArrayTopic("/SmartDashboard/Target Swerve Modules States", SwerveModuleState.struct)
.publish();
Expand Down Expand Up @@ -92,11 +79,7 @@ public DriveSubsystem() {
} catch (InterruptedException e) {
e.printStackTrace();
}
m_driveSim = new DriveSim(m_kinematics, getHeading(), getModulePositions());
m_odometry = new SwerveDriveOdometry(m_kinematics, getHeading(), getModulePositions());
m_vision = new Vision();
m_poseEstimator = new SwerveDrivePoseEstimator(m_kinematics, getHeading(), getModulePositions(),
m_driveSim.getSimPose());
}

/**
Expand All @@ -118,18 +101,12 @@ private void resetEncoders() {
m_backRight.resetDriveEncoder();
}

public void addVisionMeasurement() {
PhotonTrackedTarget target = m_vision.getBestTarget(); // TODO should this just be integrated into Vision
Pose2d visionPose = m_vision.getVisionPose(target).toPose2d();
m_poseEstimator.addVisionMeasurement(visionPose, Timer.getFPGATimestamp());
}

/**
* Gets the module positions for each swerve module.
*
* @return The module positions, in order of FL, FR, BL, BR
*/
private SwerveModulePosition[] getModulePositions() {
public SwerveModulePosition[] getModulePositions() {
return new SwerveModulePosition[] { m_frontLeft.getModulePosition(), m_frontRight.getModulePosition(),
m_backLeft.getModulePosition(), m_backRight.getModulePosition() };
}
Expand Down Expand Up @@ -199,24 +176,6 @@ public void periodic() {
m_frontRight.getModuleState(),
m_backLeft.getModuleState(), m_backRight.getModuleState() };
m_currentModuleStatePublisher.set(states);
m_poseEstimator.update(getHeading(), getModulePositions());
var estPose = m_vision.getEstimatedGlobalPose();
estPose.ifPresent(
est -> {
m_visionPosePublisher.set(est.estimatedPose.toPose2d());
m_poseEstimator.addVisionMeasurement(
est.estimatedPose.toPose2d(),
est.timestampSeconds,
m_vision.currentSTD);
});
m_posePublisher.set(m_poseEstimator.getEstimatedPosition());
}

@Override
public void simulationPeriodic() {
m_vision.updateVisionSim(m_driveSim.getSimPose());
m_driveSim.updateSimPose(getHeading(), getModulePositions());
// m_posePublisher.set(m_driveSim.getSimPose());
}

/**
Expand Down Expand Up @@ -278,28 +237,32 @@ public Command tagDriveCommand(DoubleSupplier forwardSpeed, DoubleSupplier straf

// Detect nearest April Tag and get translation data from joysticks and tag
// Calculate translation between driver movement and target movement to tag
PhotonTrackedTarget target = m_vision.getBestTarget();
Translation2d jsTranslation = new Translation2d(forwardSpeed.getAsDouble(), strafeSpeed.getAsDouble());
Translation2d targetPos = m_vision.getTranslationToTarget(target);
Translation2d posDiff = jsTranslation.minus(targetPos);
// PhotonTrackedTarget target = m_vision.getBestTarget();
// Translation2d jsTranslation = new Translation2d(forwardSpeed.getAsDouble(),
// strafeSpeed.getAsDouble());
// Translation2d targetPos = m_vision.getTranslationToTarget(target);
// Translation2d posDiff = jsTranslation.minus(targetPos);

// Get rotation data from joysticks and April Tag
// Calculate angle between driver rotation and target rotation to tag
Rotation2d jsRotation = new Rotation2d(forwardSpeed.getAsDouble(), strafeSpeed.getAsDouble());
Rotation2d targetAngle = m_vision.getYawToPose(target);
Rotation2d angleDiff = jsRotation.minus(targetAngle);
// Rotation2d jsRotation = new Rotation2d(forwardSpeed.getAsDouble(),
// strafeSpeed.getAsDouble());
// Rotation2d targetAngle = m_vision.getYawToPose(target);
// Rotation2d angleDiff = jsRotation.minus(targetAngle);

// Use PID controller to calculate needed speeds for movement towards April Tag
PIDController tagController = new PIDController(0.2, 0, 0); // TODO idk what PID constants to use
if (Math.abs(posDiff.getX()) < 3) {
fwdSpeed += tagController.calculate(jsTranslation.getX(), targetPos.getX());
}
if (Math.abs(posDiff.getY()) < 3) {
strSpeed += tagController.calculate(jsTranslation.getY(), targetPos.getY());
}
if (Math.abs(angleDiff.getDegrees()) < 30) { // TODO idk how close "close" is
rotSpeed += tagController.calculate(jsRotation.getRotations(), targetAngle.getRotations());
}
// PIDController tagController = new PIDController(0.2, 0, 0); // TODO idk what
// PID constants to use
// if (Math.abs(posDiff.getX()) < 3) {
// fwdSpeed += tagController.calculate(jsTranslation.getX(), targetPos.getX());
// }
// if (Math.abs(posDiff.getY()) < 3) {
// strSpeed += tagController.calculate(jsTranslation.getY(), targetPos.getY());
// }
// if (Math.abs(angleDiff.getDegrees()) < 30) { // TODO idk how close "close" is
// rotSpeed += tagController.calculate(jsRotation.getRotations(),
// targetAngle.getRotations());
// }

// Convert speeds to volts
rotSpeed *= kTeleopMaxTurnVoltage;
Expand All @@ -324,11 +287,6 @@ public Command resetOdometryCommand(Pose2d pose) {
.withName("ResetOdometryCommand");
}

public Command resetDriveSimCommand(Pose2d pose) {
return runOnce(() -> m_driveSim.resetSimPose(getHeading(), getModulePositions(), pose))
.withName("ResetDriveSimCommand");
}

/**
* Creates a command to run a SysId quasistatic test.
*
Expand All @@ -348,4 +306,8 @@ public Command sysidQuasistatic(SysIdRoutine.Direction direction) {
public Command sysidDynamic(SysIdRoutine.Direction direction) {
return m_sysidRoutine.dynamic(direction);
}

public SwerveDriveKinematics kinematics() {
return m_kinematics;
}
}
144 changes: 144 additions & 0 deletions src/main/java/frc/robot/subsystems/PhotonPoseEstimationSubsystem.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package frc.robot.subsystems;

import static frc.robot.Constants.DriveConstants.*;

import java.util.function.BooleanSupplier;
import java.util.function.DoubleSupplier;

import org.photonvision.targeting.PhotonTrackedTarget;

import edu.wpi.first.math.MathUtil;
import edu.wpi.first.math.controller.PIDController;
import edu.wpi.first.math.estimator.SwerveDrivePoseEstimator;
import edu.wpi.first.math.geometry.Pose2d;
import edu.wpi.first.math.geometry.Rotation2d;
import edu.wpi.first.math.geometry.Translation2d;
import edu.wpi.first.networktables.NetworkTableInstance;
import edu.wpi.first.networktables.StructPublisher;
import edu.wpi.first.wpilibj.Timer;
import edu.wpi.first.wpilibj2.command.Command;
import edu.wpi.first.wpilibj2.command.SubsystemBase;
import frc.robot.Constants.ControllerConstants;

public class PhotonPoseEstimationSubsystem extends SubsystemBase {

private final SwerveDrivePoseEstimator m_poseEstimator;
private final Vision m_vision;
private final DriveSim m_driveSim;

private final StructPublisher<Pose2d> m_visionPosePublisher;
private DriveSubsystem m_driveSubsystem;

public PhotonPoseEstimationSubsystem(DriveSubsystem driveSubsystem) {
m_driveSubsystem = driveSubsystem;
m_visionPosePublisher = NetworkTableInstance.getDefault()
.getStructTopic("/SmartDashboard/VisionPose", Pose2d.struct).publish();
m_driveSim = new DriveSim(driveSubsystem.kinematics(), driveSubsystem.getHeading(),
driveSubsystem.getModulePositions());
m_vision = new Vision();
m_poseEstimator = new SwerveDrivePoseEstimator(driveSubsystem.kinematics(), driveSubsystem.getHeading(),
driveSubsystem.getModulePositions(),
m_driveSim.getSimPose());
}

public void addVisionMeasurement() {
PhotonTrackedTarget target = m_vision.getBestTarget(); // TODO should this just be integrated into Vision
Pose2d visionPose = m_vision.getVisionPose(target).toPose2d();
m_poseEstimator.addVisionMeasurement(visionPose, Timer.getFPGATimestamp());
}

@Override
public void periodic() {
// m_posePublisher.set(m_odometry.update(getHeading(), getModulePositions()));
m_poseEstimator.update(m_driveSubsystem.getHeading(), m_driveSubsystem.getModulePositions());
var estPose = m_vision.getEstimatedGlobalPose();
estPose.ifPresent(
est -> {
m_visionPosePublisher.set(est.estimatedPose.toPose2d());
m_poseEstimator.addVisionMeasurement(
est.estimatedPose.toPose2d(),
est.timestampSeconds,
m_vision.currentSTD);
});
}

@Override
public void simulationPeriodic() {
m_vision.updateVisionSim(m_driveSim.getSimPose());
m_driveSim.updateSimPose(m_driveSubsystem.getHeading(), m_driveSubsystem.getModulePositions());
// m_posePublisher.set(m_driveSim.getSimPose());
}

public Command resetDriveSimCommand(Pose2d pose) {
return runOnce(
() -> m_driveSim
.resetSimPose(m_driveSubsystem.getHeading(), m_driveSubsystem.getModulePositions(), pose))
.withName("ResetDriveSimCommand");
}

/**
* Creates a command to drive the robot with joystick input.
*
* @param forwardSpeed Forward speed supplier. Positive values make the robot
* go forward (+X direction).
* @param strafeSpeed Strafe speed supplier. Positive values make the robot
* go to the left (+Y direction).
* @param rotation Rotation speed supplier. Positive values make the
* robot rotate CCW.
* @param isFieldRelative Supplier for determining if driving should be field
* relative.
* @return A command to drive the robot.
*/
public Command tagDriveCommand(DoubleSupplier forwardSpeed, DoubleSupplier strafeSpeed,
DoubleSupplier rotation, BooleanSupplier isFieldRelative) {
return run(() -> {
// Get the forward, strafe, and rotation speed, using a deadband on the joystick
// input so slight movements don't move the robot
// this can definitely be cleaned up but i didn't feel like it
double rotSpeed = MathUtil.applyDeadband(rotation.getAsDouble(), ControllerConstants.kDeadzone);
double fwdSpeed = MathUtil.applyDeadband(forwardSpeed.getAsDouble(), ControllerConstants.kDeadzone);
double strSpeed = MathUtil.applyDeadband(strafeSpeed.getAsDouble(), ControllerConstants.kDeadzone);
rotSpeed = Math.signum(rotSpeed) * Math.pow(rotSpeed, 2);
fwdSpeed = Math.signum(fwdSpeed) * Math.pow(fwdSpeed, 2);
strSpeed = Math.signum(strSpeed) * Math.pow(strSpeed, 2);

// Detect nearest April Tag and get translation data from joysticks and tag
// Calculate translation between driver movement and target movement to tag
PhotonTrackedTarget target = m_vision.getBestTarget();
Translation2d jsTranslation = new Translation2d(forwardSpeed.getAsDouble(),
strafeSpeed.getAsDouble());
Translation2d targetPos = m_vision.getTranslationToTarget(target);
Translation2d posDiff = jsTranslation.minus(targetPos);

// Get rotation data from joysticks and April Tag
// Calculate angle between driver rotation and target rotation to tag
Rotation2d jsRotation = new Rotation2d(forwardSpeed.getAsDouble(),
strafeSpeed.getAsDouble());
Rotation2d targetAngle = m_vision.getYawToPose(target);
Rotation2d angleDiff = jsRotation.minus(targetAngle);

// Use PID controller to calculate needed speeds for movement towards April Tag
PIDController tagController = new PIDController(0.2, 0, 0); // TODO idk what
// PID constants to use
if (Math.abs(posDiff.getX()) < 3) {
fwdSpeed += tagController.calculate(jsTranslation.getX(), targetPos.getX());
}
if (Math.abs(posDiff.getY()) < 3) {
strSpeed += tagController.calculate(jsTranslation.getY(), targetPos.getY());
}
if (Math.abs(angleDiff.getDegrees()) < 30) { // TODO idk how close "close" is
rotSpeed += tagController.calculate(
jsRotation.getRotations(),
targetAngle.getRotations());
}

// Convert speeds to volts
rotSpeed *= kTeleopMaxTurnVoltage;
fwdSpeed *= kTeleopMaxVoltage;
strSpeed *= kTeleopMaxVoltage;

m_driveSubsystem.drive(fwdSpeed, strSpeed, rotSpeed, isFieldRelative.getAsBoolean());
}).withName("TagDriveCommand");
}

}

0 comments on commit 11c715b

Please sign in to comment.