Skip to content
This repository was archived by the owner on Jan 17, 2026. It is now read-only.

Commit 68ca733

Browse files
committed
Switch to TunableNumbers for gains
Signed-off-by: Jade Turner <spacey-sooty@proton.me>
1 parent d69ba14 commit 68ca733

File tree

7 files changed

+208
-19
lines changed

7 files changed

+208
-19
lines changed

src/main/java/org/curtinfrc/frc2025/Constants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
*/
1818
public final class Constants {
1919
public static final RobotType robotType = RobotType.DEVBOT;
20+
public static final boolean tuningMode = true;
2021
public static final double ROBOT_X = 660; // mm
2122
public static final double ROBOT_Y = 680;
2223

src/main/java/org/curtinfrc/frc2025/subsystems/ejector/Ejector.java

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,54 @@
11
package org.curtinfrc.frc2025.subsystems.ejector;
22

3-
import static org.curtinfrc.frc2025.subsystems.ejector.EjectorConstants.*;
4-
53
import edu.wpi.first.math.controller.PIDController;
64
import edu.wpi.first.math.controller.SimpleMotorFeedforward;
75
import edu.wpi.first.wpilibj2.command.Command;
86
import edu.wpi.first.wpilibj2.command.SubsystemBase;
97
import edu.wpi.first.wpilibj2.command.button.Trigger;
8+
import org.curtinfrc.frc2025.Constants;
9+
import org.curtinfrc.frc2025.util.LoggedTunableNumber;
1010
import org.littletonrobotics.junction.Logger;
1111

1212
public class Ejector extends SubsystemBase {
1313
private final EjectorIO io;
1414
private final EjectorIOInputsAutoLogged inputs = new EjectorIOInputsAutoLogged();
15-
private final PIDController pid = new PIDController(kP, 0, kD);
16-
private final SimpleMotorFeedforward ff = new SimpleMotorFeedforward(kS, kV, kA);
15+
private final PIDController pid = new PIDController(0, 0, 0);
16+
private SimpleMotorFeedforward ff;
17+
18+
// Tunable Numbers
19+
private final LoggedTunableNumber kS = new LoggedTunableNumber("Ejector/kS");
20+
private final LoggedTunableNumber kV = new LoggedTunableNumber("Ejector/kV");
21+
private final LoggedTunableNumber kP = new LoggedTunableNumber("Ejector/kP");
22+
private final LoggedTunableNumber kD = new LoggedTunableNumber("Ejector/kD");
1723

1824
public Ejector(EjectorIO io) {
1925
this.io = io;
26+
switch (Constants.robotType) {
27+
case COMPBOT:
28+
kS.initDefault(0.8);
29+
kV.initDefault(0.11);
30+
ff = new SimpleMotorFeedforward(kS.get(), kV.get());
31+
kP.initDefault(0);
32+
kD.initDefault(0);
33+
pid.setPID(kP.get(), 0, kD.get());
34+
break;
35+
case DEVBOT:
36+
kS.initDefault(0.8);
37+
kV.initDefault(0.11);
38+
ff = new SimpleMotorFeedforward(kS.get(), kV.get());
39+
kP.initDefault(0);
40+
kD.initDefault(0);
41+
pid.setPID(kP.get(), 0, kD.get());
42+
break;
43+
case SIMBOT:
44+
kS.initDefault(0.8);
45+
kV.initDefault(0.11);
46+
ff = new SimpleMotorFeedforward(kS.get(), kV.get());
47+
kP.initDefault(0);
48+
kD.initDefault(0);
49+
pid.setPID(kP.get(), 0, kD.get());
50+
break;
51+
}
2052
}
2153

2254
public final Trigger sensor = new Trigger(() -> inputs.sensor);
@@ -26,6 +58,14 @@ public Ejector(EjectorIO io) {
2658
public void periodic() {
2759
io.updateInputs(inputs);
2860
Logger.processInputs("Ejector", inputs);
61+
62+
if (kS.hasChanged(hashCode()) || kD.hasChanged(hashCode())) {
63+
ff = new SimpleMotorFeedforward(kS.get(), kV.get());
64+
}
65+
66+
if (kP.hasChanged(hashCode()) || kD.hasChanged(hashCode())) {
67+
pid.setPID(kP.get(), 0, kD.get());
68+
}
2969
}
3070

3171
public Command stop() {

src/main/java/org/curtinfrc/frc2025/subsystems/ejector/EjectorConstants.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,4 @@ public class EjectorConstants {
66
public static final int currentLimit = 60;
77
public static final double ejectorMoi = 0.025;
88
public static final double ejectorReduction = 1;
9-
public static final double kP = 0;
10-
public static final double kD = 0;
11-
public static final double kS = 0.8;
12-
public static final double kV = 0.11;
13-
public static final double kA = 0;
149
}

src/main/java/org/curtinfrc/frc2025/subsystems/elevator/Elevator.java

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,60 @@
11
package org.curtinfrc.frc2025.subsystems.elevator;
22

3-
import static org.curtinfrc.frc2025.subsystems.elevator.ElevatorConstants.*;
3+
import static org.curtinfrc.frc2025.subsystems.elevator.ElevatorConstants.pulleyRadiusMeters;
4+
import static org.curtinfrc.frc2025.subsystems.elevator.ElevatorConstants.tolerance;
45

56
import edu.wpi.first.math.controller.PIDController;
67
import edu.wpi.first.math.geometry.Pose3d;
78
import edu.wpi.first.math.geometry.Rotation3d;
89
import edu.wpi.first.wpilibj2.command.Command;
910
import edu.wpi.first.wpilibj2.command.SubsystemBase;
1011
import edu.wpi.first.wpilibj2.command.button.Trigger;
12+
import org.curtinfrc.frc2025.Constants;
1113
import org.curtinfrc.frc2025.Constants.Setpoints;
14+
import org.curtinfrc.frc2025.util.LoggedTunableNumber;
1215
import org.littletonrobotics.junction.AutoLogOutput;
1316
import org.littletonrobotics.junction.Logger;
1417

1518
public class Elevator extends SubsystemBase {
1619
private final ElevatorIO io;
1720
private final ElevatorIOInputsAutoLogged inputs = new ElevatorIOInputsAutoLogged();
18-
private final PIDController pid = new PIDController(kP, 0, kD);
21+
private final PIDController pid = new PIDController(0, 0, 0);
22+
23+
@AutoLogOutput(key = "Elevator/Setpoint")
1924
private Setpoints setpoint = Setpoints.COLLECT;
2025

26+
// Tunable Numbers
27+
LoggedTunableNumber kP = new LoggedTunableNumber("Elevator/kP");
28+
LoggedTunableNumber kD = new LoggedTunableNumber("Elevator/kD");
29+
30+
@AutoLogOutput(key = "Elevator/IsNotAtCollect")
2131
public final Trigger isNotAtCollect = new Trigger(() -> setpoint != Setpoints.COLLECT);
32+
2233
public final Trigger toZero = new Trigger(() -> inputs.touchSensor);
2334

35+
@AutoLogOutput(key = "Elevator/AtSetpoint")
36+
public Trigger atSetpoint = new Trigger(pid::atSetpoint);
37+
2438
public Elevator(ElevatorIO io) {
2539
this.io = io;
2640
pid.setTolerance(tolerance);
41+
switch (Constants.robotType) {
42+
case COMPBOT:
43+
kP.initDefault(16);
44+
kD.initDefault(0);
45+
pid.setPID(kP.get(), 0, kD.get());
46+
break;
47+
case DEVBOT:
48+
kP.initDefault(16);
49+
kD.initDefault(0);
50+
pid.setPID(kP.get(), 0, kD.get());
51+
break;
52+
case SIMBOT:
53+
kP.initDefault(16);
54+
kD.initDefault(0);
55+
pid.setPID(kP.get(), 0, kD.get());
56+
break;
57+
}
2758
}
2859

2960
@Override
@@ -34,9 +65,11 @@ public void periodic() {
3465
Logger.recordOutput("Elevator/setpoint", setpoint);
3566
Logger.recordOutput("Elevator/AtSetpoint", atSetpoint.getAsBoolean());
3667
Logger.recordOutput("Elevator/ActualError", pid.getError());
37-
}
3868

39-
public Trigger atSetpoint = new Trigger(pid::atSetpoint);
69+
if (kP.hasChanged(hashCode()) || kD.hasChanged(hashCode())) {
70+
pid.setPID(kP.get(), 0, kD.get());
71+
}
72+
}
4073

4174
public Command goToSetpoint(Setpoints point) {
4275
return run(

src/main/java/org/curtinfrc/frc2025/subsystems/elevator/ElevatorConstants.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,11 @@ public class ElevatorConstants {
55
public static int motorPort = 31;
66

77
// TODO: TUNE PID and ff
8-
public static double tolerance = 0.05;
9-
public static double kP = 16;
10-
public static double kD = 0;
8+
// TODO: not using tunables for this yet
119
public static double kV = 0;
1210
public static double kA = 0.01;
1311
public static double kG = 0;
12+
public static double tolerance = 1;
1413

1514
public static final double pulleyRadiusMeters = 0.002927789;
1615
public static final double maxHeightMeters = 0.8;

src/main/java/org/curtinfrc/frc2025/subsystems/elevator/ElevatorIOSim.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package org.curtinfrc.frc2025.subsystems.elevator;
22

3-
import static org.curtinfrc.frc2025.subsystems.elevator.ElevatorConstants.kA;
4-
import static org.curtinfrc.frc2025.subsystems.elevator.ElevatorConstants.kV;
53
import static org.curtinfrc.frc2025.subsystems.elevator.ElevatorConstants.maxHeightMeters;
64
import static org.curtinfrc.frc2025.subsystems.elevator.ElevatorConstants.positionMetresToRotations;
75

@@ -10,7 +8,7 @@
108

119
public class ElevatorIOSim implements ElevatorIO {
1210
private final ElevatorSim elevatorSim =
13-
new ElevatorSim(kV, kA, DCMotor.getNEO(1), 0, maxHeightMeters, true, 0);
11+
new ElevatorSim(0, 0.01, DCMotor.getNEO(1), 0, maxHeightMeters, true, 0);
1412
private double volts = 0;
1513

1614
@Override
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// Copyright (c) 2025 FRC 6328
2+
// http://github.com/Mechanical-Advantage
3+
//
4+
// Use of this source code is governed by an MIT-style
5+
// license that can be found in the LICENSE file at
6+
// the root directory of this project.
7+
8+
package org.curtinfrc.frc2025.util;
9+
10+
import java.util.Arrays;
11+
import java.util.HashMap;
12+
import java.util.Map;
13+
import java.util.function.Consumer;
14+
import java.util.function.DoubleSupplier;
15+
import org.curtinfrc.frc2025.Constants;
16+
import org.littletonrobotics.junction.networktables.LoggedNetworkNumber;
17+
18+
/**
19+
* Class for a tunable number. Gets value from dashboard in tuning mode, returns default if not or
20+
* value not in dashboard.
21+
*/
22+
public class LoggedTunableNumber implements DoubleSupplier {
23+
private static final String tableKey = "/Tuning";
24+
25+
private final String key;
26+
private boolean hasDefault = false;
27+
private double defaultValue;
28+
private LoggedNetworkNumber dashboardNumber;
29+
private Map<Integer, Double> lastHasChangedValues = new HashMap<>();
30+
31+
/**
32+
* Create a new LoggedTunableNumber
33+
*
34+
* @param dashboardKey Key on dashboard
35+
*/
36+
public LoggedTunableNumber(String dashboardKey) {
37+
this.key = tableKey + "/" + dashboardKey;
38+
}
39+
40+
/**
41+
* Create a new LoggedTunableNumber with the default value
42+
*
43+
* @param dashboardKey Key on dashboard
44+
* @param defaultValue Default value
45+
*/
46+
public LoggedTunableNumber(String dashboardKey, double defaultValue) {
47+
this(dashboardKey);
48+
initDefault(defaultValue);
49+
}
50+
51+
/**
52+
* Set the default value of the number. The default value can only be set once.
53+
*
54+
* @param defaultValue The default value
55+
*/
56+
public void initDefault(double defaultValue) {
57+
if (!hasDefault) {
58+
hasDefault = true;
59+
this.defaultValue = defaultValue;
60+
if (Constants.tuningMode) {
61+
dashboardNumber = new LoggedNetworkNumber(key, defaultValue);
62+
}
63+
}
64+
}
65+
66+
/**
67+
* Get the current value, from dashboard if available and in tuning mode.
68+
*
69+
* @return The current value
70+
*/
71+
public double get() {
72+
if (!hasDefault) {
73+
return 0.0;
74+
} else {
75+
return Constants.tuningMode ? dashboardNumber.get() : defaultValue;
76+
}
77+
}
78+
79+
/**
80+
* Checks whether the number has changed since our last check
81+
*
82+
* @param id Unique identifier for the caller to avoid conflicts when shared between multiple
83+
* objects. Recommended approach is to pass the result of "hashCode()"
84+
* @return True if the number has changed since the last time this method was called, false
85+
* otherwise.
86+
*/
87+
public boolean hasChanged(int id) {
88+
double currentValue = get();
89+
Double lastValue = lastHasChangedValues.get(id);
90+
if (lastValue == null || currentValue != lastValue) {
91+
lastHasChangedValues.put(id, currentValue);
92+
return true;
93+
}
94+
95+
return false;
96+
}
97+
98+
/**
99+
* Runs action if any of the tunableNumbers have changed
100+
*
101+
* @param id Unique identifier for the caller to avoid conflicts when shared between multiple *
102+
* objects. Recommended approach is to pass the result of "hashCode()"
103+
* @param action Callback to run when any of the tunable numbers have changed. Access tunable
104+
* numbers in order inputted in method
105+
* @param tunableNumbers All tunable numbers to check
106+
*/
107+
public static void ifChanged(
108+
int id, Consumer<double[]> action, LoggedTunableNumber... tunableNumbers) {
109+
if (Arrays.stream(tunableNumbers).anyMatch(tunableNumber -> tunableNumber.hasChanged(id))) {
110+
action.accept(Arrays.stream(tunableNumbers).mapToDouble(LoggedTunableNumber::get).toArray());
111+
}
112+
}
113+
114+
/** Runs action if any of the tunableNumbers have changed */
115+
public static void ifChanged(int id, Runnable action, LoggedTunableNumber... tunableNumbers) {
116+
ifChanged(id, values -> action.run(), tunableNumbers);
117+
}
118+
119+
@Override
120+
public double getAsDouble() {
121+
return get();
122+
}
123+
}

0 commit comments

Comments
 (0)