Skip to content
This repository has been archived by the owner on Jan 6, 2025. It is now read-only.

Commit

Permalink
Merge pull request #8 from team-3482/intake-shooter
Browse files Browse the repository at this point in the history
Intake shooter -> main
  • Loading branch information
Anonymadly authored Oct 24, 2024
2 parents bf9c970 + e669a00 commit 7f13493
Show file tree
Hide file tree
Showing 25 changed files with 2,079 additions and 157 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@
}
},
],
"java.test.defaultConfig": "WPIlibUnitTests"
"java.test.defaultConfig": "WPIlibUnitTests",
"java.jdt.ls.vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx2G -Xms100m -Xlog:disable"
}
13 changes: 10 additions & 3 deletions src/main/java/frc/robot/Robot.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,18 @@

import edu.wpi.first.net.PortForwarder;
import edu.wpi.first.wpilibj.PowerDistribution;
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
import edu.wpi.first.wpilibj2.command.Command;
import edu.wpi.first.wpilibj2.command.CommandScheduler;
import frc.robot.constants.LimelightConstants;

import org.littletonrobotics.junction.LogFileUtil;
import org.littletonrobotics.junction.LoggedRobot;
import org.littletonrobotics.junction.Logger;
import org.littletonrobotics.junction.networktables.NT4Publisher;
import org.littletonrobotics.junction.wpilog.WPILOGReader;
import org.littletonrobotics.junction.wpilog.WPILOGWriter;
import frc.robot.utilities.BuildConstants;
import frc.robot.utilities.BuildConstants; // If this file does not exist, build the robot code.

/**
* The VM is configured to automatically run this class, and to call the
Expand All @@ -33,6 +35,7 @@ public class Robot extends LoggedRobot {
* This function is run when the robot is first started up and should be used
* for any initialization code.
*/
@SuppressWarnings("resource")
@Override
public void robotInit() {
// Port forward all required LL ports. Necessary for robot connections over ethernet.
Expand All @@ -52,17 +55,21 @@ public void robotInit() {
Logger.addDataReceiver(new WPILOGWriter()); // Log to a USB stick ("/U/logs")
Logger.addDataReceiver(new NT4Publisher()); // Publish data to NetworkTables
new PowerDistribution(1, PowerDistribution.ModuleType.kRev); // Enables power distribution logging
} else {
}
else {
setUseTiming(false); // Run as fast as possible
String logPath = LogFileUtil.findReplayLog(); // Pull the replay log from AdvantageScope (or prompt the user)
Logger.setReplaySource(new WPILOGReader(logPath)); // Read replay log
Logger.addDataReceiver(new WPILOGWriter(LogFileUtil.addPathSuffix(logPath, "_sim"))); // Save outputs to a new log
}

Logger.start();
// TODO LOGGING : This is spamming the console "DataLog no file found"
// Logger.start();

// Initialize RobotContainer and all subsystems
RobotContainer.getInstance();

LiveWindow.disableAllTelemetry();
}

/**
Expand Down
223 changes: 124 additions & 99 deletions src/main/java/frc/robot/RobotContainer.java

Large diffs are not rendered by default.

222 changes: 222 additions & 0 deletions src/main/java/frc/robot/auto/AutoShootCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.

package frc.robot.auto;

import java.util.function.Supplier;

import com.ctre.phoenix6.mechanisms.swerve.SwerveDrivetrain.SwerveDriveState;
import com.ctre.phoenix6.mechanisms.swerve.SwerveModule.DriveRequestType;
import com.ctre.phoenix6.mechanisms.swerve.SwerveRequest;

import edu.wpi.first.math.geometry.Rotation2d;
import edu.wpi.first.math.geometry.Translation2d;
import edu.wpi.first.math.geometry.Translation3d;
import edu.wpi.first.math.kinematics.ChassisSpeeds;
import edu.wpi.first.math.util.Units;
import edu.wpi.first.wpilibj.Timer;
import edu.wpi.first.wpilibj2.command.Command;
import frc.robot.constants.Positions;
import frc.robot.constants.Constants.ControllerConstants;
import frc.robot.constants.Constants.ShootingConstants;
import frc.robot.constants.Constants.ShootingFunctions;
import frc.robot.constants.PhysicalConstants.IntakeConstants;
import frc.robot.intake.IntakeSubsystem;
import frc.robot.pivot.PivotSubsystem;
import frc.robot.shooter.ShooterSubsystem;
import frc.robot.swerve.CommandSwerveDrivetrain;
import frc.robot.utilities.ShotVector;

/** Shoots a Note autonomously. */
public class AutoShootCommand extends Command {
// Driving while shooting
private final Supplier<Double> xSupplier;
private final Supplier<Double> ySupplier;
private final double maxSpeed;

private Translation3d speakerTranslation3d;
private ShotVector idealShotVector;
private boolean allowMovement;

private boolean noSpeaker;
private boolean withinDistance;
private Timer timer;

/**
* Creates a new AutoShootCommand.
* @param xSupplier - Supplier for x robot movement from -1.0 to 1.0.
* @param ySupplier - Supplier for y robot movement from -1.0 to 1.0.
* @param allowMovement - Whether to allow driver input while shooting.
*/
private AutoShootCommand(Supplier<Double> xSupplier, Supplier<Double> ySupplier, boolean allowMovement) {
setName("AutoShootCommand");

this.xSupplier = xSupplier;
this.ySupplier = ySupplier;
this.maxSpeed = ShootingConstants.MAX_MOVEMENT_SPEED;

this.allowMovement = allowMovement;
this.idealShotVector = new ShotVector();
this.timer = new Timer();

// Use addRequirements() here to declare subsystem dependencies.
addRequirements(
IntakeSubsystem.getInstance(),
PivotSubsystem.getInstance(),
ShooterSubsystem.getInstance(),
CommandSwerveDrivetrain.getInstance()
);
}

/** Creates a new AutoShootCommand that does not allow movement. */
public AutoShootCommand() {
this(null, null, false);
}

/**
* Creates a new AutoShootCommand that allows movement.
* @param xSupplier - Supplier for x robot movement from -1.0 to 1.0.
* @param ySupplier - Supplier for y robot movement from -1.0 to 1.0.
*/
public AutoShootCommand(Supplier<Double> xSupplier, Supplier<Double> ySupplier) {
this(xSupplier, ySupplier, true);
}

// Called when the command is initially scheduled.
@Override
public void initialize() {
this.noSpeaker = false;
this.withinDistance = false;

try {
this.speakerTranslation3d = Positions.getSpeakerTarget();
}
catch (RuntimeException e) {
System.err.println("Alliance is empty ; cannot target SPEAKER.");
this.noSpeaker = true;
return;
}

this.timer.reset();
}

// Called every time the scheduler runs while the command is scheduled.
@Override
public void execute() {
if (this.noSpeaker) return;

SwerveDriveState botState = CommandSwerveDrivetrain.getInstance().getState();
ChassisSpeeds botSpeeds = ChassisSpeeds.fromRobotRelativeSpeeds(botState.speeds, botState.Pose.getRotation());
double botHeading = botState.Pose.getRotation().getDegrees();

// Ignore rotation because it should be none for accurate shooting either way.
ShotVector botVector = new ShotVector(botSpeeds.vxMetersPerSecond, botSpeeds.vyMetersPerSecond, 0);
this.idealShotVector = calculateInitialShotVector().plus(botVector);


CommandSwerveDrivetrain.getInstance().setControl(
getDrivingControl(Rotation2d.fromDegrees(this.idealShotVector.getYaw()))
);

if (!this.withinDistance) return;

double distance = botState.Pose.getTranslation().getDistance(this.speakerTranslation3d.toTranslation2d());
double adjustedPitch = this.idealShotVector.getAdjustedPitch(distance);

PivotSubsystem.getInstance().motionMagicPosition(adjustedPitch);
ShooterSubsystem.getInstance().motionMagicVelocity(this.idealShotVector.getNormRps());

if (
Math.min(Math.abs(this.idealShotVector.getYaw() - botHeading), 360 - Math.abs(this.idealShotVector.getYaw() - botHeading))
<= ShootingFunctions.CALCULATE_YAW_TOLERANCE.apply(distance)
&& Math.abs(PivotSubsystem.getInstance().getPosition() - adjustedPitch)
<= ShootingFunctions.CALCULATE_PITCH_TOLERANCE.apply(distance)
&& Math.abs(ShooterSubsystem.getInstance().getVelocity() - this.idealShotVector.getNormRps())
<= ShooterSubsystem.metersPerSecondToRotationsPerSecond(
ShootingFunctions.CALCULATE_SPEED_TOLERANCE.apply(distance, this.idealShotVector.getPitch())
)
) {
IntakeSubsystem.getInstance().motionMagicVelocity(IntakeConstants.IDEAL_INTAKE_VELOCITY);
}

if (!IntakeSubsystem.getInstance().frontLaserHasNote()) {
this.timer.start();
}
}

// Called once the command ends or is interrupted.
@Override
public void end(boolean interrupted) {
IntakeSubsystem.getInstance().setSpeed(0);
ShooterSubsystem.getInstance().setSpeed(0);

this.timer.stop();
}

// Returns true when the command should end.
@Override
public boolean isFinished() {
return this.noSpeaker || (!this.allowMovement && !this.withinDistance) || this.timer.hasElapsed(0.25);
}

/**
* Helper that finds the ideal ShotVector from the bot's current position.
* @return The ShotVector.
*/
private ShotVector calculateInitialShotVector() {
SwerveDriveState botState = CommandSwerveDrivetrain.getInstance().getState();
Translation2d botTranslation = botState.Pose.getTranslation();
double distance = botTranslation.getDistance(this.speakerTranslation3d.toTranslation2d());

if (distance > ShootingConstants.MAX_SHOOTING_DISTANCE) {
System.err.println(String.format(
"AutoShootCommand | Too far from SPEAKER. (%.2f > %.2f)",
distance, ShootingConstants.MAX_SHOOTING_DISTANCE
));
this.withinDistance = false;
return new ShotVector();
}
else {
this.withinDistance = true;
}

double yaw = Units.radiansToDegrees(Math.atan2(
this.speakerTranslation3d.getY() - botTranslation.getY(),
this.speakerTranslation3d.getX() - botTranslation.getX()
) + Math.PI);
double velocity = ShooterSubsystem.rotationsPerSecondToMetersPerSecond(
ShootingFunctions.CALCULATE_SHOOTER_VELOCITY.apply(distance)
);
double pitch = ShootingFunctions.CALCULATE_SHOOTER_PITCH.apply(distance, velocity);

return ShotVector.fromYawPitchVelocity(yaw, pitch, velocity);
}

/**
* Helper that gets the control for driving while shooting.
* @param targetRotation - The direction to face as a Rotation2d.
* @return The SwerveRequest.
*/
private SwerveRequest getDrivingControl(Rotation2d targetRotation) {
final SwerveRequest.FieldCentricFacingAngle fieldCentricFacingAngle_withDeadband = new CommandSwerveDrivetrain.FieldCentricFacingAngle_PID_Workaround()
.withDeadband(this.maxSpeed * ControllerConstants.DEADBAND)
.withRotationalDeadband(0)
.withDriveRequestType(DriveRequestType.OpenLoopVoltage);

if (!this.allowMovement) {
return fieldCentricFacingAngle_withDeadband
.withVelocityX(0)
.withVelocityY(0)
.withTargetDirection(targetRotation);
}

double velocityX = this.xSupplier.get() * this.maxSpeed;
double velocityY = this.ySupplier.get() * this.maxSpeed;

return fieldCentricFacingAngle_withDeadband
.withVelocityX(velocityX)
.withVelocityY(velocityY)
.withTargetDirection(targetRotation);
}
}
48 changes: 14 additions & 34 deletions src/main/java/frc/robot/auto/DriveToNoteCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,64 +15,45 @@
import edu.wpi.first.math.util.Units;
import edu.wpi.first.wpilibj.Timer;
import edu.wpi.first.wpilibj2.command.Command;
import edu.wpi.first.wpilibj2.command.CommandScheduler;
import frc.robot.limelights.DetectionSubsystem;
import frc.robot.swerve.CommandSwerveDrivetrain;

/**
* A command that wraps a PathPlanner command that
* paths to the nearest visible note and turns to face it.
* Accounts for increased accuraccy with decreased distance.
* Accounts for increased accuracy with decreased distance.
*/
public class DriveToNoteCommand extends Command {
private final PathConstraints constraints = new PathConstraints(
4.3, 2,
Units.degreesToRadians(720), Units.degreesToRadians(540)
);

private Command pathingCommand;
private Timer timer;
private Command pathingCommand;
private Pose2d currentNotePose;
private boolean finished;

/** Creates a new DriveToNoteCommand. */
public DriveToNoteCommand() {
setName("DriveToNoteCommand");
this.timer = new Timer();
// No requirements, because PathPlanner's Command requires the DriveTrain
// This wrapper is only useful for creating the PathPlanner Path
addRequirements();

// Use addRequirements() here to declare subsystem dependencies.
addRequirements(CommandSwerveDrivetrain.getInstance());
}

// Called when the command is initially scheduled.
@Override
public void initialize() {
this.finished = false;
this.pathingCommand = null;
this.timer.restart();

Pose2d[] notePoses = DetectionSubsystem.getInstance().getRecentNotePoses();
if (notePoses.length == 0) {
this.finished = true;
return;
}

this.currentNotePose = notePoses[0];

this.pathingCommand = generatePath(this.currentNotePose);
CommandScheduler.getInstance().schedule(this.pathingCommand);
}

// Called every time the scheduler runs while the command is scheduled.
@Override
public void execute() {
if (!this.pathingCommand.isScheduled() || this.finished) return;

// Run this check every 5 robot cycles / 3 Detection cycles
if (this.timer.hasElapsed(0.1)) {
this.timer.restart();
}
else {
return;
if (this.pathingCommand != null) {
this.pathingCommand.execute();
}

Pose2d[] notePoses = DetectionSubsystem.getInstance().getRecentNotePoses();
Expand All @@ -90,28 +71,27 @@ public void execute() {
return;
}
// If deviated over 15 cm from the original Note, repath
else if (this.currentNotePose.getTranslation().getDistance(newNotePose.getTranslation()) >= 0.15) {
else if (this.pathingCommand == null || this.currentNotePose.getTranslation().getDistance(newNotePose.getTranslation()) >= 0.15) {
this.currentNotePose = newNotePose;

CommandScheduler.getInstance().cancel(this.pathingCommand);
// NOTE : The first time this runs, it takes up to 250 ms to run this method for some strange reason.
this.pathingCommand = generatePath(this.currentNotePose);
CommandScheduler.getInstance().schedule(this.pathingCommand);
this.pathingCommand.initialize();
}
}

// Called once the command ends or is interrupted.
@Override
public void end(boolean interrupted) {
if (this.pathingCommand != null && this.pathingCommand.isScheduled()) {
CommandScheduler.getInstance().cancel(this.pathingCommand);
if (this.pathingCommand != null) {
this.pathingCommand.end(interrupted);
}
this.timer.stop();
}

// Returns true when the command should end.
@Override
public boolean isFinished() {
return this.finished || !this.pathingCommand.isScheduled();
return this.pathingCommand == null || this.pathingCommand.isFinished();
}

/**
Expand Down
Loading

0 comments on commit 7f13493

Please sign in to comment.