Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
393a1f3
Shooting Testing
rkris28 Oct 9, 2025
9e62d5e
regression line
ShayanHasNo Oct 9, 2025
6ce40c4
fixed regression line, implemented shooting functions
ShayanHasNo Oct 16, 2025
a68fea9
added PID velocity control
nitiTyagii Oct 19, 2025
d4752d0
added tolerance constant
nitiTyagii Oct 19, 2025
31f2803
setpointRPM and velocity control for PID
nitiTyagii Oct 19, 2025
661c835
config for PID (placeholder vals)
nitiTyagii Oct 19, 2025
5326df5
Update inputs with setpointRPM and atSetpoint calculation
nitiTyagii Oct 19, 2025
914bb63
Implement setVelocity and goToSetpoint
nitiTyagii Oct 19, 2025
f2f4439
Initialize SparkClosedLoopController
nitiTyagii Oct 19, 2025
2b4c12f
Added angularVelocity
nitiTyagii Oct 19, 2025
97b9033
convert velocity (rpm) to angular velocity rad/s
nitiTyagii Oct 19, 2025
9196099
Added PID based shootOnce and shootCycle
nitiTyagii Oct 19, 2025
1e59961
removed uneeded angular velocity
nitiTyagii Oct 19, 2025
f6e0743
Added getVelocity method
nitiTyagii Oct 20, 2025
12740aa
Log shooter velocity before indexing
nitiTyagii Oct 20, 2025
7b3b965
x button to use shootCyclePID()
nitiTyagii Oct 20, 2025
f78e0a1
tuned PIDS
ShayanHasNo Oct 27, 2025
ff59ac3
Random change to revert PID
EJainDev Nov 4, 2025
26397eb
added Ks characterization plus feedforward that updates with rpm needed.
ShayanHasNo Oct 27, 2025
08887eb
added Ks characterization plus feedforward that updates with rpm needed.
ShayanHasNo Oct 27, 2025
e0d9fc6
added Ks characterization plus feedforward that updates with rpm needed.
ShayanHasNo Oct 28, 2025
128b5df
switch to ff
ShayanHasNo Oct 28, 2025
ae18ac1
Revert "switch to ff"
EJainDev Nov 4, 2025
e546e31
Revert "added Ks characterization plus feedforward that updates with …
EJainDev Nov 4, 2025
a33a541
Revert "added Ks characterization plus feedforward that updates with …
EJainDev Nov 4, 2025
97e1fe5
Reapply "added Ks characterization plus feedforward that updates with…
EJainDev Nov 4, 2025
f735167
added Ks characterization plus feedforward that updates with rpm needed.
ShayanHasNo Oct 28, 2025
5e54ca2
switch to ff
ShayanHasNo Oct 28, 2025
9b93ec3
Revert "switch to ff"
EJainDev Nov 4, 2025
1688fee
Revert "added Ks characterization plus feedforward that updates with …
EJainDev Nov 4, 2025
d6f7b1a
Revert "added Ks characterization plus feedforward that updates with …
EJainDev Nov 4, 2025
2bca5d6
Reapply "added Ks characterization plus feedforward that updates with…
EJainDev Nov 4, 2025
a0b9e9f
externalized pid values
nitiTyagii Nov 6, 2025
58a9fd0
back to pid
nitiTyagii Nov 6, 2025
7081577
changed setpoint rpm
nitiTyagii Nov 13, 2025
8fd3679
removed duplicate call of goToSetpoint()
nitiTyagii Nov 13, 2025
60ab309
tuned pid values + started chnage to tunableNumbers
nitiTyagii Nov 13, 2025
b428743
Fixed usage of TunableNumber so it can actually be tuned now through …
EJainDev Nov 14, 2025
2ed9385
tuned tolerance
nitiTyagii Nov 18, 2025
20f53d9
Added a goto distance based setpoint method, which calculates the nec…
EJainDev Nov 18, 2025
d26090a
Added vision to get distance instead of manually putting it in.
EJainDev Nov 20, 2025
ad22f2e
corrected camera name
nitiTyagii Dec 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 118 additions & 0 deletions src/main/java/frc/lib/utils/TunableNumber.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@

package frc.lib.utils;

import frc.robot.Constants;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.DoubleSupplier;
import org.littletonrobotics.junction.networktables.LoggedNetworkNumber;

import static frc.robot.Constants.tuningMode;

/**
* Class for a tunable number. Gets value from dashboard in tuning mode, returns default if not or
* value not in dashboard.
*/
public class TunableNumber implements DoubleSupplier {
private static final String tableKey = "/Tuning";

private final String key;
private boolean hasDefault = false;
private double defaultValue;
private LoggedNetworkNumber dashboardNumber;
private Map<Integer, Double> lastHasChangedValues = new HashMap<>();

/**
* Create a new LoggedTunableNumber
*
* @param dashboardKey Key on dashboard
*/
public TunableNumber(String dashboardKey) {
this.key = tableKey + "/" + dashboardKey;
}

/**
* Create a new LoggedTunableNumber with the default value
*
* @param dashboardKey Key on dashboard
* @param defaultValue Default value
*/
public TunableNumber(String dashboardKey, double defaultValue) {
this(dashboardKey);
initDefault(defaultValue);
}

/**
* Set the default value of the number. The default value can only be set once.
*
* @param defaultValue The default value
*/
public void initDefault(double defaultValue) {
if (!hasDefault) {
hasDefault = true;
this.defaultValue = defaultValue;
if (tuningMode) {
dashboardNumber = new LoggedNetworkNumber(key, defaultValue);
}
}
}

/**
* Get the current value, from dashboard if available and in tuning mode.
*
* @return The current value
*/
public double get() {
if (!hasDefault) {
return 0.0;
} else {
return tuningMode ? dashboardNumber.get() : defaultValue;
}
}

/**
* Checks whether the number has changed since our last check
*
* @param id Unique identifier for the caller to avoid conflicts when shared between multiple
* objects. Recommended approach is to pass the result of "hashCode()"
* @return True if the number has changed since the last time this method was called, false
* otherwise.
*/
public boolean hasChanged(int id) {
double currentValue = get();
Double lastValue = lastHasChangedValues.get(id);
if (lastValue == null || currentValue != lastValue) {
lastHasChangedValues.put(id, currentValue);
return true;
}

return false;
}

/**
* Runs action if any of the tunableNumbers have changed
*
* @param id Unique identifier for the caller to avoid conflicts when shared between multiple *
* objects. Recommended approach is to pass the result of "hashCode()"
* @param action Callback to run when any of the tunable numbers have changed. Access tunable
* numbers in order inputted in method
* @param tunableNumbers All tunable numbers to check
*/
public static void ifChanged(int id, Consumer<double[]> action, TunableNumber... tunableNumbers) {
if (Arrays.stream(tunableNumbers).anyMatch(tunableNumber -> tunableNumber.hasChanged(id))) {
action.accept(Arrays.stream(tunableNumbers).mapToDouble(TunableNumber::get).toArray());
}
}

/** Runs action if any of the tunableNumbers have changed */
public static void ifChanged(int id, Runnable action, TunableNumber... tunableNumbers) {
ifChanged(id, values -> action.run(), tunableNumbers);
}

@Override
public double getAsDouble() {
return get();
}
}
4 changes: 3 additions & 1 deletion src/main/java/frc/robot/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@
* constants are needed, to reduce verbosity.
*/
public final class Constants {
}
static public final boolean tuningMode = true;
static public final String camName = "Arucam OV9281(Back)";
}
65 changes: 60 additions & 5 deletions src/main/java/frc/robot/Orchestrator.java
Original file line number Diff line number Diff line change
@@ -1,27 +1,53 @@
package frc.robot;

import java.util.function.BooleanSupplier;

import edu.wpi.first.units.DistanceUnit;
import edu.wpi.first.units.Measure;
import edu.wpi.first.units.Units;
import edu.wpi.first.wpilibj2.command.Command;
import edu.wpi.first.wpilibj2.command.Commands;
import frc.robot.subsystems.indexer.Indexer;
import frc.robot.subsystems.shooter.Shooter;
import frc.robot.subsystems.shooter.ShooterConstants;
import frc.robot.subsystems.vision.Vision;
import org.littletonrobotics.junction.AutoLogOutput;
import org.littletonrobotics.junction.Logger;

import java.util.function.BooleanSupplier;


public class Orchestrator {
private final Indexer indexer;
private final Shooter shooter;
@AutoLogOutput
private double Distance = 300.0;
@AutoLogOutput private final double shootingPower = 0.0;
private final Vision vision;

public Orchestrator(Indexer indexer, Shooter shooter) {
public Orchestrator(Indexer indexer, Shooter shooter, Vision vision) {
this.indexer = indexer;
this.shooter = shooter;
this.vision = vision;
}
public Command spinUpDistance(Double distance) {
Distance = distance;
double percent = (8.331 * Math.pow(10, -5) * Math.pow(distance, 2) + 0.0734 * distance + 15.709) / 100;
Logger.recordOutput("Shooter/Percent", percent);
return shooter.runPercent(percent);
}
public Command shootCycleDistance() {
return Commands.parallel(
spinUpDistance(420.0),
Commands.sequence(
intakeIfNeeded(),
Commands.waitSeconds(ShooterConstants.SPINUP_SECONDS),
indexer.indexIntoShooter())
.repeatedly());
}

public Command spinUp(BooleanSupplier fastMode) {
return Commands.either(
shooter.fullSpeed(),
shooter.speedUp(),
shooter.runPercent(0.8),
fastMode);
}

Expand Down Expand Up @@ -51,11 +77,40 @@ public Command shootCycle(BooleanSupplier fastMode) {
.repeatedly());
}

public Command shootOncePID(double setpointRPM) {
return Commands.parallel(
shooter.toSetpoint(setpointRPM),
Commands.sequence(
intakeIfNeeded(),
Commands.waitUntil(shooter::atSetpoint),
indexer.indexIntoShooter()));
}

public Command shootCyclePID(double setpointRPM) {
return Commands.parallel(
shooter.toSetpoint(setpointRPM),
Commands.sequence(
intakeIfNeeded(),
Commands.waitUntil(shooter::atSetpoint),
indexer.indexIntoShooter())
.repeatedly());
}

public Command shootDistance(Measure<DistanceUnit> distance) {
return Commands.parallel(
shooter.toSetpoint(Units.Meters.of(vision.distanceFromTarget())),
Commands.sequence(
intakeIfNeeded(),
Commands.waitUntil(shooter::atSetpoint),
indexer.indexIntoShooter()).repeatedly());
}


public Command reverseAll() {
return Commands.parallel(shooter.reverse(), indexer.reverse());
}

public Command stopAll() {
return Commands.parallel(shooter.stop(), indexer.stop());
}
}
}
42 changes: 26 additions & 16 deletions src/main/java/frc/robot/RobotContainer.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,22 @@

package frc.robot;

import edu.wpi.first.wpilibj2.command.Commands;
import edu.wpi.first.wpilibj2.command.Commands;
import frc.robot.commands.Autos;
import frc.robot.commands.ExampleCommand;
import frc.robot.subsystems.drive.*;
import edu.wpi.first.units.Units;
import edu.wpi.first.wpilibj2.command.Command;
import edu.wpi.first.wpilibj2.command.Commands;
import edu.wpi.first.wpilibj2.command.button.CommandXboxController;
import edu.wpi.first.wpilibj2.command.button.Trigger;
import frc.robot.subsystems.drive.Drive;
import frc.robot.subsystems.drive.DriveIOSparkMax;
import frc.robot.subsystems.indexer.Indexer;
import frc.robot.subsystems.indexer.IndexerIOSparkMax;
import frc.robot.subsystems.shooter.Shooter;
import frc.robot.subsystems.shooter.ShooterIOSparkMax;
import frc.robot.subsystems.vision.Vision;
import frc.robot.subsystems.vision.VisionIO;
import frc.robot.subsystems.vision.VisionIOPhotonVision;

import static frc.robot.Constants.camName;


/**
* This class is where the bulk of the robot should be declared. Since Command-based is a
Expand All @@ -31,10 +31,10 @@ public class RobotContainer {
// The robot's subsystems and commands are defined here...
private final Drive drive;
// The robot's subsystems and commands are defined here...
private final Vision vision;
private final Indexer indexer = new Indexer(new IndexerIOSparkMax());
private final Shooter shooter = new Shooter(new ShooterIOSparkMax());
private final Orchestrator orchestrator = new Orchestrator(indexer, shooter);
private final Vision vision = new Vision(new VisionIOPhotonVision(camName));
private final Orchestrator orchestrator = new Orchestrator(indexer, shooter, vision);
private final CommandXboxController driverController = new CommandXboxController(0);
private boolean fastMode = false;
private final Trigger fastModeTrigger = new Trigger(() -> fastMode);
Expand All @@ -44,7 +44,6 @@ public class RobotContainer {
public RobotContainer() {
// Configure the trigger bindings
drive = new Drive(new DriveIOSparkMax());
vision = new Vision(new VisionIOPhotonVision("VGA_USB_Camera"){});
configureBindings();
}

Expand Down Expand Up @@ -75,16 +74,27 @@ private void configureBindings() {
.rightTrigger()
.whileTrue(orchestrator.shootCycle(() -> fastMode))
.onFalse(orchestrator.stopAll());
driverController.y().whileTrue(shooter.speedUp()).onFalse(shooter.stop());
driverController
.leftTrigger()
.whileTrue(orchestrator.reverseAll())
.onFalse(orchestrator.stopAll());


driverController
.rightBumper()
.onTrue(orchestrator.shootOnce(() -> fastMode))
.onFalse(orchestrator.stopAll());

//driverController.x().whileTrue(orchestrator.shootCycleDistance()).onFalse(shooter.stop());
driverController.x().whileTrue(orchestrator.shootCyclePID(2000)).onFalse(shooter.stop()); // TODO: change the RPM
driverController.a().whileTrue(orchestrator.shootDistance(Units.Inches.of(168.5))).onFalse(shooter.stop()); // TODO: Test other distances
driverController.leftTrigger().onTrue(indexer.indexUntilSwitch());

driverController.y().onTrue(indexer.indexIntoShooter());


driverController
.leftBumper()
.whileTrue(orchestrator.reverseAll())
.onFalse(orchestrator.stopAll());



}

/**
Expand All @@ -97,4 +107,4 @@ public Command getAutonomousCommand() {
// An example command will be run in autonomous
return Commands.none();
}
}
}
Loading