Skip to content

Commit

Permalink
Create Shooter subsystem
Browse files Browse the repository at this point in the history
  • Loading branch information
JackJ09 committed Jan 20, 2024
1 parent d784c38 commit 91a139f
Show file tree
Hide file tree
Showing 3 changed files with 314 additions and 4 deletions.
11 changes: 9 additions & 2 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "java",
"name": "Main",
"request": "launch",
"mainClass": "org.team340.robot.Main",
"projectName": "Crescendo2024-340"
},
{
"type": "wpilib",
"name": "WPILib Desktop Debug",
"request": "launch",
"desktop": true,
"desktop": true
},
{
"type": "wpilib",
"name": "WPILib roboRIO Debug",
"request": "launch",
"desktop": false,
"desktop": false
}
]
}
45 changes: 43 additions & 2 deletions src/main/java/org/team340/robot/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,14 @@ public static final class RobotMap {
public static final int SHOOTER_FEED_MOTOR = 31;
public static final int SHOOTER_LEFT_SHOOT_MOTOR = 32;
public static final int SHOOTER_RIGHT_SHOOT_MOTOR = 33;

public static final int SHOOTER_NOTE_DETECTOR = 0;
}

public static final class IntakeConstants {

public static final PIDConfig INTAKE_DEPLOY_STRONG_PID = new PIDConfig(0.0, 0.0, 0.0);
public static final PIDConfig INTAKE_DEPLOY_WEAK_PID = new PIDConfig(0.0, 0.0, 0.0);
public static final PIDConfig INTAKE_DEPLOY_STRONG_PID = new PIDConfig(0.0, 0.0, 0.0, 0.0);
public static final PIDConfig INTAKE_DEPLOY_WEAK_PID = new PIDConfig(0.0, 0.0, 0.0, 0.0);

public static final double INTAKE_DEPLOY_POSITION = 0.0;
public static final double INTAKE_DEPLOY_POSITION_TOLERANCE = 0.0;
Expand All @@ -92,6 +94,45 @@ public static final class IntakeConstants {
public static final double INTAKE_SPIT_ROLLER_SPEED = -0.0;
}

public static final class ShooterConstants {

public static final double FEED_INTAKE_SPEED = 0.0;

/*All linear pivot distances in inches.*/
public static final double SHOOTER_PIVOT_REL_ENC_CONVERSION = 0.2;

public static final double DISTANCE_BETWEEN_SHOOTER_PIVOT_AND_DART_PIVOT_ON_THE_SHOOTER = 0.0;
public static final double DISTANCE_BETWEEN_SHOOTER_PIVOT_AND_DART_PIVOT_ON_THE_MOTOR = 0.0;
public static final double MINIMUM_LENGTH_OF_DART = 0.0;
public static final double SUM_OF_SQUARES_OF_LENGTHS =
DISTANCE_BETWEEN_SHOOTER_PIVOT_AND_DART_PIVOT_ON_THE_SHOOTER *
DISTANCE_BETWEEN_SHOOTER_PIVOT_AND_DART_PIVOT_ON_THE_SHOOTER +
DISTANCE_BETWEEN_SHOOTER_PIVOT_AND_DART_PIVOT_ON_THE_MOTOR *
DISTANCE_BETWEEN_SHOOTER_PIVOT_AND_DART_PIVOT_ON_THE_MOTOR;

public static final double TWICE_THE_LENGTH_OF_THE_PRODUCTS_OF_LENGTHS =
2 * DISTANCE_BETWEEN_SHOOTER_PIVOT_AND_DART_PIVOT_ON_THE_SHOOTER * DISTANCE_BETWEEN_SHOOTER_PIVOT_AND_DART_PIVOT_ON_THE_MOTOR;

public static final double OFFSET_ANGLE = Math.acos(
(SUM_OF_SQUARES_OF_LENGTHS - MINIMUM_LENGTH_OF_DART * MINIMUM_LENGTH_OF_DART) / TWICE_THE_LENGTH_OF_THE_PRODUCTS_OF_LENGTHS
);

public static final double MINIMUM_ANGLE = 0.0;
public static final double MAXIMUM_ANGLE = 0.0;

public static final PIDConfig SHOOTER_PIVOT_PID = new PIDConfig(0.0, 0.0, 0.0, 0.0);
public static final PIDConfig SHOOTER_FEED_PID = new PIDConfig(0.0, 0.0, 0.0, 0.0);
public static final PIDConfig SHOOTER_LEFT_SHOOT_PID = new PIDConfig(0.0, 0.0, 0.0, 0.0);
public static final PIDConfig SHOOTER_RIGHT_SHOOT_PID = new PIDConfig(0.0, 0.0, 0.0, 0.0);

public static final double SHOOTER_PIVOT_PID_MIN_OUTPUT = 0.0;
public static final double SHOOTER_PIVOT_PID_MAX_OUTPUT = 0.0;
public static final double SHOOTER_PIVOT_MIN_VEL = 0.0;
public static final double SHOOTER_PIVOT_MAX_VEL = 0.0;
public static final double SHOOTER_PIVOT_MAX_ACCEL = 0.0;
public static final double SHOOTER_PIVOT_CLOSED_LOOP_ERR = 0.0;
}

/**
* Constants for the swerve subsystem.
*/
Expand Down
262 changes: 262 additions & 0 deletions src/main/java/org/team340/robot/subsystems/Shooter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
package org.team340.robot.subsystems;

import com.revrobotics.AbsoluteEncoder;
import com.revrobotics.CANSparkBase.ControlType;
import com.revrobotics.CANSparkFlex;
import com.revrobotics.CANSparkLowLevel.MotorType;
import com.revrobotics.CANSparkMax;
import com.revrobotics.RelativeEncoder;
import com.revrobotics.SparkAbsoluteEncoder.Type;
import com.revrobotics.SparkPIDController;
import edu.wpi.first.math.geometry.Pose2d;
import edu.wpi.first.wpilibj.DigitalInput;
import edu.wpi.first.wpilibj.DriverStation;
import edu.wpi.first.wpilibj2.command.Command;
import java.util.function.Supplier;
import org.team340.lib.GRRSubsystem;
import org.team340.lib.util.Math2;
import org.team340.lib.util.Mutable;
import org.team340.lib.util.config.rev.SparkPIDControllerConfig;
import org.team340.robot.Constants.RobotMap;
import org.team340.robot.Constants.ShooterConstants;

public class Shooter extends GRRSubsystem {

private final CANSparkMax pivotMotor = createSparkMax("Pivot Motor", RobotMap.SHOOTER_PIVOT_MOTOR, MotorType.kBrushless);
private final CANSparkMax feedMotor = createSparkMax("Feeder Motor", RobotMap.SHOOTER_FEED_MOTOR, MotorType.kBrushless);
private final CANSparkFlex leftShootMotor = createSparkFlex(
"Shooter Motor Left",
RobotMap.SHOOTER_LEFT_SHOOT_MOTOR,
MotorType.kBrushless
);
private final CANSparkFlex rightShootMotor = createSparkFlex(
"Shooter Motor Right",
RobotMap.SHOOTER_RIGHT_SHOOT_MOTOR,
MotorType.kBrushless
);

private final SparkPIDController pivotPID = pivotMotor.getPIDController();
private final SparkPIDController feedPID = feedMotor.getPIDController();
private final SparkPIDController leftShootPID = leftShootMotor.getPIDController();
private final SparkPIDController rightShootPID = rightShootMotor.getPIDController();

private final AbsoluteEncoder pivotAbsoluteEncoder = createSparkMaxAbsoluteEncoder(
"Pivot Absolute Encoder",
pivotMotor,
Type.kDutyCycle
);
//TODO: correct this when create relative encoder exists.
private final RelativeEncoder pivotRelativeEncoder = pivotMotor.getEncoder();

private final DigitalInput noteDetector = createDigitalInput("Note Detector", RobotMap.SHOOTER_NOTE_DETECTOR);

public Shooter() {
super("Shooter");
pivotRelativeEncoder.setPositionConversionFactor(ShooterConstants.SHOOTER_PIVOT_REL_ENC_CONVERSION);
pivotAbsoluteEncoder.setPositionConversionFactor(Math2.TWO_PI);

new SparkPIDControllerConfig()
.setPID(ShooterConstants.SHOOTER_PIVOT_PID.p(), ShooterConstants.SHOOTER_PIVOT_PID.i(), ShooterConstants.SHOOTER_PIVOT_PID.d())
.setIZone(ShooterConstants.SHOOTER_PIVOT_PID.iZone())
.setOutputRange(ShooterConstants.SHOOTER_PIVOT_PID_MIN_OUTPUT, ShooterConstants.SHOOTER_PIVOT_PID_MAX_OUTPUT)
.apply(pivotMotor, pivotPID);
pivotPID.setSmartMotionMaxVelocity(ShooterConstants.SHOOTER_PIVOT_MAX_VEL, 0);
pivotPID.setSmartMotionMaxVelocity(ShooterConstants.SHOOTER_PIVOT_MAX_VEL, 0);
pivotPID.setSmartMotionMinOutputVelocity(ShooterConstants.SHOOTER_PIVOT_MAX_VEL, 0);
pivotPID.setSmartMotionMaxAccel(ShooterConstants.SHOOTER_PIVOT_MAX_ACCEL, 0);
pivotPID.setSmartMotionAllowedClosedLoopError(ShooterConstants.SHOOTER_PIVOT_CLOSED_LOOP_ERR, 0);

new SparkPIDControllerConfig()
.setPID(ShooterConstants.SHOOTER_FEED_PID.p(), ShooterConstants.SHOOTER_FEED_PID.i(), ShooterConstants.SHOOTER_FEED_PID.d())
.setIZone(ShooterConstants.SHOOTER_FEED_PID.iZone())
.apply(feedMotor, feedPID);

new SparkPIDControllerConfig()
.setPID(
ShooterConstants.SHOOTER_LEFT_SHOOT_PID.p(),
ShooterConstants.SHOOTER_LEFT_SHOOT_PID.i(),
ShooterConstants.SHOOTER_LEFT_SHOOT_PID.d()
)
.setIZone(ShooterConstants.SHOOTER_LEFT_SHOOT_PID.iZone())
.apply(leftShootMotor, leftShootPID);

new SparkPIDControllerConfig()
.setPID(
ShooterConstants.SHOOTER_RIGHT_SHOOT_PID.p(),
ShooterConstants.SHOOTER_RIGHT_SHOOT_PID.i(),
ShooterConstants.SHOOTER_RIGHT_SHOOT_PID.d()
)
.setIZone(ShooterConstants.SHOOTER_RIGHT_SHOOT_PID.iZone())
.apply(rightShootMotor, rightShootPID);
}

/**
* This starts running the pivot motor to an angle. This must run regularly until the shooter reaches the angle.
* @param angleToShootAt This is the angle that will be used.
*/
private void setShooterToAngle(double angleToShootAt) {
if (angleToShootAt < ShooterConstants.MINIMUM_ANGLE || angleToShootAt > ShooterConstants.MAXIMUM_ANGLE) {
DriverStation.reportWarning(
"Invalid shooter pivot angle. " +
angleToShootAt +
" is not between " +
ShooterConstants.MINIMUM_ANGLE +
" and " +
ShooterConstants.MAXIMUM_ANGLE,
false
);
return;
}
pivotPID.setReference(distanceToMoveDart(angleToShootAt), ControlType.kSmartMotion);
}

/**
* This starts running the shooter motors to there respective speeds..
* @param leftSpeed This is speed the left motor will be set to.
* @param rightSpeed This is speed the right motor will be set to.
*/
private void setShooterToSpeed(double leftSpeed, double rightSpeed) {
leftShootPID.setReference(leftSpeed, ControlType.kVelocity);
rightShootPID.setReference(rightSpeed, ControlType.kVelocity);
}

/**
* this method shoots the note by spinning the feedMotor. Make sure to stop the feed motor after the note has completely cleared the shooter.
* @param feederWheelSpeed This is the speed the shooter wheels will attempt to reach. Make this big so you do not rip the note.
*/
private void shoot(double feederWheelSpeed) {
feedPID.setReference(feederWheelSpeed, ControlType.kVelocity);
}

private boolean hasShooterReachedAngle() {
return true;
}

private boolean hasShooterReachedSpeed() {
return true;
}

/**
* This command receives a note from the intake.
* @return A Command for receiving a note.
*/
public Command receiveNote() {
return commandBuilder()
.onInitialize(() -> {
feedPID.setReference(ShooterConstants.FEED_INTAKE_SPEED, ControlType.kDutyCycle);
})
.isFinished(() -> {
return noteDetector.get();
})
.onEnd(() -> {
feedMotor.stopMotor();
});
}

public Command shooterToAngle(double angleToShootAt) {
return shooterToAngle(() -> angleToShootAt);
}

/**
* drives the shooter to the specified angle. Since the angle is a supplier it may change while this command is executing.
* @param angleToShootAt This is the angle the shooter will go to, check {@link #hasShooterReachedAngle()} to see if it has finished.
* @return
*/
public Command shooterToAngle(Supplier<Double> angleToShootAt) {
return commandBuilder().onExecute(() -> setShooterToAngle(angleToShootAt.get())).onEnd(() -> pivotMotor.stopMotor());
}

/**
* Runs the shooter up to a specified speed. This speed can change while this command is running.
* @param shooterSpeed This is the speed to drive the shooter at.
* @return Returns this command.
*/
public Command setShootSpeed(Supplier<Double> shooterSpeed) {
return commandBuilder()
.onExecute(() -> {
setShooterToSpeed(shooterSpeed.get(), shooterSpeed.get());
})
.onEnd(() -> {
leftShootMotor.stopMotor();
rightShootMotor.stopMotor();
});
}

/**
* This command takes the current position, uses it to decide the angle to shoot at, then shoots the note.
* Note that this will not correct the robot's yaw or position.
* @param robotPosition This is the position used for the math, note that it must be a supplier.
* @return This command.
*/
public Command shootSpeaker(Supplier<Pose2d> robotPosition) {
Mutable<Integer> counter = new Mutable<>(0);

return commandBuilder()
.onInitialize(() -> {
//TODO: find a value that works well
counter.set(40);
//TODO: do math!!!
setShooterToSpeed(0, 0);
setShooterToAngle(0);
})
.onExecute(() -> {
if (hasShooterReachedAngle() && hasShooterReachedSpeed()) {
//TODO: make a value for this
shoot(0);
counter.set(Math.max(0, counter.get() - 1));
}
})
.isFinished(() -> counter.get() == 0)
.onEnd(() -> {
pivotMotor.stopMotor();
feedMotor.stopMotor();
leftShootMotor.stopMotor();
rightShootMotor.stopMotor();
});
}

/**
* DANGER use at your own risk!
* @param sensorAngle
* @return
*/
private double angleToDartExtension(double sensorAngle) {
return (
Math.sqrt(
ShooterConstants.SUM_OF_SQUARES_OF_LENGTHS -
ShooterConstants.TWICE_THE_LENGTH_OF_THE_PRODUCTS_OF_LENGTHS *
Math.cos(sensorAngle + ShooterConstants.OFFSET_ANGLE)
) -
ShooterConstants.MINIMUM_LENGTH_OF_DART
);
}

private double distanceToMoveDart(double targetAngle) {
double currentAngle = pivotAbsoluteEncoder.getPosition();
double currentExtensionLength = angleToDartExtension(currentAngle);
double targetExtensionLength = angleToDartExtension(targetAngle);
double deltaExtensionLength = targetExtensionLength - currentExtensionLength;
double currentRelativeEncoderPosition = pivotRelativeEncoder.getPosition();

return deltaExtensionLength + currentRelativeEncoderPosition;
}

public Command shootSpeaker(Pose2d robotPosition) {
return shootSpeaker(() -> robotPosition);
}

public Command shootAmp() {
//TODO: write this
return null;
}

public Command shootTrap() {
//TODO: write this
return null;
}

public Command spit() {
//TODO: write this
return null;
}
}

0 comments on commit 91a139f

Please sign in to comment.