Skip to content

Commit

Permalink
Implement closed-loop driving, intaking, and shooting
Browse files Browse the repository at this point in the history
  • Loading branch information
harnwalN committed Jan 24, 2024
1 parent 771376c commit be9dd22
Show file tree
Hide file tree
Showing 9 changed files with 237 additions and 49 deletions.
8 changes: 7 additions & 1 deletion src/main/java/frc/robot/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,13 @@
* constants are needed, to reduce verbosity.
*/
public final class Constants {
public static final Mode currentMode = Mode.SIM;
public static final double LAUNCH_SHOOTER_VOLTS = 12.0;
public static final double LAUNCH_HOPPER_VOLTS = 12.0;
public static final double SHOOTER_DELAY = 1;
public static final double INTAKE_SHOOTER_VOLTS = -8.0;
public static final double INTAKE_HOPPER_VOLTS = -4.0;

public static final Mode currentMode = Mode.REAL;

public static enum Mode {
/** Running on a real robot. */
Expand Down
49 changes: 24 additions & 25 deletions src/main/java/frc/robot/RobotContainer.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,11 @@
import edu.wpi.first.wpilibj2.command.Commands;
import edu.wpi.first.wpilibj2.command.button.CommandXboxController;
import frc.robot.commands.FeedForwardCharacterization;
import frc.robot.subsystems.drive.Drive;
import frc.robot.subsystems.drive.DriveIO;
import frc.robot.subsystems.drive.DriveIOSim;
import frc.robot.subsystems.drive.DriveIOSparkMax;
import frc.robot.subsystems.flywheel.Flywheel;
import frc.robot.subsystems.flywheel.FlywheelIO;
import frc.robot.subsystems.flywheel.FlywheelIOSim;
import frc.robot.subsystems.flywheel.FlywheelIOSparkMax;
import frc.robot.commands.IntakeNote;
import frc.robot.commands.LaunchNote;
import frc.robot.commands.PrepareLaunch;
import frc.robot.subsystems.drive.*;
import frc.robot.subsystems.flywheel.*;
import org.littletonrobotics.junction.networktables.LoggedDashboardChooser;
import org.littletonrobotics.junction.networktables.LoggedDashboardNumber;

Expand All @@ -41,48 +38,47 @@
public class RobotContainer {
// Subsystems
private final Drive drive;
private final Flywheel flywheel;
private Flywheel shooter;
private Flywheel hopper;

// Controller
private final CommandXboxController controller = new CommandXboxController(0);

// Dashboard inputs
private final LoggedDashboardChooser<Command> autoChooser;
private final LoggedDashboardNumber flywheelSpeedInput =
new LoggedDashboardNumber("Flywheel Speed", 1500.0);
private final LoggedDashboardNumber flywheelVoltInput =
new LoggedDashboardNumber("Flywheel Speed", 12.0);

/** The container for the robot. Contains subsystems, OI devices, and commands. */
public RobotContainer() {
switch (Constants.currentMode) {
case REAL:
// Real robot, instantiate hardware IO implementations
drive = new Drive(new DriveIOSparkMax());
flywheel = new Flywheel(new FlywheelIOSparkMax());
// drive = new Drive(new DriveIOTalonFX());
// flywheel = new Flywheel(new FlywheelIOTalonFX());
drive = new Drive(new DriveIOTalonSRX());
hopper = new Flywheel(new FlywheelIOTalonSRX(14));
shooter = new Flywheel(new FlywheelIOTalonSRX(15));

break;

case SIM:
// Sim robot, instantiate physics sim IO implementations
drive = new Drive(new DriveIOSim());
flywheel = new Flywheel(new FlywheelIOSim());
shooter = new Flywheel(new FlywheelIOSim());
break;

default:
// Replayed robot, disable IO implementations
drive = new Drive(new DriveIO() {});
flywheel = new Flywheel(new FlywheelIO() {});
shooter = new Flywheel(new FlywheelIO() {});
break;
}

// Set up auto routines
NamedCommands.registerCommand(
"Run Flywheel",
Commands.startEnd(
() -> flywheel.runVelocity(flywheelSpeedInput.get()), flywheel::stop, flywheel)
.withTimeout(5.0));
autoChooser = new LoggedDashboardChooser<>("Auto Choices", AutoBuilder.buildAutoChooser());
Commands.startEnd(() -> shooter.runVolts(12.0), shooter::stop, shooter).withTimeout(5.0));

autoChooser = new LoggedDashboardChooser<>("Auto Choices", AutoBuilder.buildAutoChooser());
// Set up feedforward characterization
autoChooser.addOption(
"Drive FF Characterization",
Expand All @@ -91,7 +87,7 @@ public RobotContainer() {
autoChooser.addOption(
"Flywheel FF Characterization",
new FeedForwardCharacterization(
flywheel, flywheel::runVolts, flywheel::getCharacterizationVelocity));
shooter, shooter::runVolts, shooter::getCharacterizationVelocity));

// Configure the button bindings
configureButtonBindings();
Expand All @@ -106,12 +102,15 @@ public RobotContainer() {
private void configureButtonBindings() {
drive.setDefaultCommand(
Commands.run(
() -> drive.driveArcade(-controller.getLeftY(), controller.getLeftX()), drive));
() -> drive.driveArcade(-controller.getLeftY(), -1 * controller.getRightX()), drive));
controller
.a()
.whileTrue(
Commands.startEnd(
() -> flywheel.runVelocity(flywheelSpeedInput.get()), flywheel::stop, flywheel));
new PrepareLaunch(shooter, hopper)
.withTimeout(Constants.SHOOTER_DELAY)
.andThen(new LaunchNote(shooter, hopper))
.handleInterrupt(() -> shooter.stop()));
controller.b().whileTrue(new IntakeNote(shooter, hopper).handleInterrupt(() -> shooter.stop()));
}

/**
Expand Down
60 changes: 60 additions & 0 deletions src/main/java/frc/robot/commands/IntakeNote.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// 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.commands;

import edu.wpi.first.wpilibj2.command.Command;
import frc.robot.Constants;
import frc.robot.subsystems.flywheel.Flywheel;

// import frc.robot.subsystems.CANLauncher;

/*This is an example of creating a command as a class. The base Command class provides a set of methods that your command
* will override.
*/
public class IntakeNote extends Command {
Flywheel shooter;
Flywheel hopper;

// CANLauncher m_launcher;

/** Creates a new LaunchNote. */
public IntakeNote(Flywheel shooter, Flywheel hopper) {
// save the launcher system internally
this.shooter = shooter;
this.hopper = hopper;
}

// The initialize method is called when the command is initially scheduled.
@Override
public void initialize() {
// Set the wheels to launching speed
shooter.runVolts(Constants.INTAKE_SHOOTER_VOLTS);
hopper.runVolts(Constants.INTAKE_HOPPER_VOLTS);
}

// Called every time the scheduler runs while the command is scheduled.
@Override
public void execute() {
// There is nothing we need this command to do on each iteration. You could remove this method
// and the default blank method
// of the base class will run.
}

// Returns true when the command should end.
@Override
public boolean isFinished() {
// Always return false so the command never ends on it's own. In this project we use the
// scheduler to end the command when the button is released.
return false;
}

// Called once the command ends or is interrupted.
@Override
public void end(boolean interrupted) {
// Stop the wheels when the command ends.
shooter.stop();
hopper.stop();
}
}
62 changes: 62 additions & 0 deletions src/main/java/frc/robot/commands/LaunchNote.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// 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.commands;

import static frc.robot.Constants.*;

import edu.wpi.first.wpilibj2.command.Command;
import frc.robot.Constants;
import frc.robot.subsystems.flywheel.Flywheel;

// import frc.robot.subsystems.CANLauncher;

/*This is an example of creating a command as a class. The base Command class provides a set of methods that your command
* will override.
*/
public class LaunchNote extends Command {
Flywheel shooter;
Flywheel hopper;

// CANLauncher m_launcher;

/** Creates a new LaunchNote. */
public LaunchNote(Flywheel shooter, Flywheel hopper) {
// save the launcher system internally
this.shooter = shooter;
this.hopper = hopper;
}

// The initialize method is called when the command is initially scheduled.
@Override
public void initialize() {
// Set the wheels to launching speed
shooter.runVolts(Constants.LAUNCH_SHOOTER_VOLTS);
hopper.runVolts(Constants.LAUNCH_HOPPER_VOLTS);
}

// Called every time the scheduler runs while the command is scheduled.
@Override
public void execute() {
// There is nothing we need this command to do on each iteration. You could remove this method
// and the default blank method
// of the base class will run.
}

// Returns true when the command should end.
@Override
public boolean isFinished() {
// Always return false so the command never ends on it's own. In this project we use the
// scheduler to end the command when the button is released.
return false;
}

// Called once the command ends or is interrupted.
@Override
public void end(boolean interrupted) {
// Stop the wheels when the command ends.
shooter.stop();
hopper.stop();
}
}
54 changes: 54 additions & 0 deletions src/main/java/frc/robot/commands/PrepareLaunch.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// 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.commands;

import static frc.robot.Constants.*;

import edu.wpi.first.wpilibj2.command.Command;
import frc.robot.subsystems.flywheel.*;

// import frc.robot.subsystems.CANLauncher;

public class PrepareLaunch extends Command {
Flywheel shooter;
Flywheel hopper;

// CANLauncher m_launcher;

/** Creates a new PrepareLaunch. */
public PrepareLaunch(Flywheel shooter, Flywheel hopper) {
this.shooter = shooter;
this.hopper = hopper;
}

// Called when the command is initially scheduled.
@Override
public void initialize() {
// Set launch wheel to speed, keep feed wheel at 0 to let launch wheel spin up.
shooter.runVolts(12.0);
}

// Called every time the scheduler runs while the command is scheduled.
@Override
public void execute() {
// There is nothing we need this command to do on each iteration. You could remove this method
// and the default blank method
// of the base class will run.
}

// Called once the command ends or is interrupted.
@Override
public void end(boolean interrupted) {
// Do nothing when the command ends. The launch wheel needs to keep spinning in order to launch
}

// Returns true when the command should end.
@Override
public boolean isFinished() {
// Always return false so the command never ends on it's own. In this project we use a timeout
// decorator on the command to end it.
return false;
}
}
10 changes: 9 additions & 1 deletion src/main/java/frc/robot/subsystems/drive/Drive.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import com.pathplanner.lib.pathfinding.Pathfinding;
import com.pathplanner.lib.util.PathPlannerLogging;
import com.pathplanner.lib.util.ReplanningConfig;
import edu.wpi.first.math.controller.PIDController;
import edu.wpi.first.math.controller.SimpleMotorFeedforward;
import edu.wpi.first.math.geometry.Pose2d;
import edu.wpi.first.math.geometry.Rotation2d;
Expand All @@ -38,11 +39,15 @@ public class Drive extends SubsystemBase {
public static final double WHEEL_RADIUS = Units.inchesToMeters(3.0);
public static final double TRACK_WIDTH = Units.inchesToMeters(26.0);

public static final double MAX_SPEED_M_PER_S = Units.feetToMeters(10); // TODO find right value

// TODO: NON-SIM FEEDFORWARD GAINS MUST BE TUNED
// Consider using SysId routines defined in RobotContainer
private static final double KS = Constants.currentMode == Mode.SIM ? 0.0 : 0.0;
private static final double KV = Constants.currentMode == Mode.SIM ? 0.227 : 0.0;

private final PIDController pid = new PIDController(0, 0, 0);

private final DriveIO io;
private final DriveIOInputsAutoLogged inputs = new DriveIOInputsAutoLogged();
private final DifferentialDriveOdometry odometry =
Expand Down Expand Up @@ -113,7 +118,10 @@ public void driveVelocity(double leftMetersPerSec, double rightMetersPerSec) {
/** Run open loop based on stick positions. */
public void driveArcade(double xSpeed, double zRotation) {
var speeds = DifferentialDrive.arcadeDriveIK(xSpeed, zRotation, true);
io.setVoltage(speeds.left * 12.0, speeds.right * 12.0);
io.setVoltage(
pid.calculate(inputs.leftVelocityRadPerSec * WHEEL_RADIUS, speeds.left * MAX_SPEED_M_PER_S),
pid.calculate(
inputs.rightVelocityRadPerSec * WHEEL_RADIUS, speeds.right * MAX_SPEED_M_PER_S));
}

/** Stops the drive. */
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/frc/robot/subsystems/drive/DriveIOTalonSRX.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import com.ctre.phoenix.motorcontrol.can.SlotConfiguration;
import com.ctre.phoenix.motorcontrol.can.TalonSRX;
import com.ctre.phoenix.motorcontrol.can.TalonSRXConfiguration;
import edu.wpi.first.math.MathUtil;
import edu.wpi.first.math.controller.PIDController;
import edu.wpi.first.math.util.Units;

Expand Down Expand Up @@ -115,7 +114,8 @@ public void setVoltage(double leftVolts, double rightVolts) {
@Override
public void setVelocity(
double leftRadPerSec, double rightRadPerSec, double leftFFVolts, double rightFFVolts) {
setVoltage(MathUtil.clamp((pid.calculate(leftRadPerSec)+leftFFVolts), -12.0, 12.0),
MathUtil.clamp(pid.calculate(rightRadPerSec)+rightFFVolts, -12.0, 12.0));
setVoltage(
pid.calculate(leftLeader.getSelectedSensorVelocity(), leftRadPerSec) + leftFFVolts,
pid.calculate(rightLeader.getSelectedSensorVelocity(), rightRadPerSec) + rightFFVolts);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public static class FlywheelIOInputs {
public double positionRad = 0.0;
public double velocityRadPerSec = 0.0;
public double appliedVolts = 0.0;
public double shooterPercent = 0.0;
public double[] currentAmps = new double[] {};
}

Expand Down
Loading

0 comments on commit be9dd22

Please sign in to comment.