-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Brownout!
- Loading branch information
Showing
5 changed files
with
185 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package frc.robot; | ||
|
||
import edu.wpi.first.wpilibj2.command.SubsystemBase; | ||
|
||
/** | ||
* A convenience class wrapping SubsystemBase for brownout and other shared subsystem logic. | ||
*/ | ||
public abstract class GRTSubsystem extends SubsystemBase { | ||
protected int currentLimit; | ||
protected final double minCurrent; | ||
|
||
/** | ||
* Creates a GRTSubsystem from a given initial current limit and minimum current. | ||
* @param currentLimit The initial (maximum) current limit. | ||
* @param minCurrent The minimum current. | ||
*/ | ||
public GRTSubsystem(int currentLimit, double minCurrent) { | ||
this.currentLimit = currentLimit; | ||
this.minCurrent = minCurrent; | ||
} | ||
|
||
/** | ||
* Gets the subsystem's minimum current. | ||
* @return The minimum current. | ||
*/ | ||
public double getMinCurrent() { | ||
return minCurrent; | ||
} | ||
|
||
/** | ||
* Gets the subsystem's current current limit. | ||
* @return The current limit. | ||
*/ | ||
public int getCurrentLimit() { | ||
return currentLimit; | ||
} | ||
|
||
/** | ||
* Gets the total current being drawn by the subsystem. Implementers should use `PowerController.getCurrentDrawnFromPDH` | ||
* with all the subsystem's motor controller ports. | ||
* @return The total current being drawn by the subsystem. | ||
*/ | ||
abstract public double getTotalCurrentDrawn(); | ||
|
||
/** | ||
* Sets the subsystem's current limit. | ||
* @param limit The current limit (in amps) for the subsystem. | ||
*/ | ||
abstract public void setCurrentLimit(double limit); | ||
|
||
/** | ||
* Contains logic for cleaning up the subsystem for climb. | ||
* TODO: this might not be strictly necessary; perhaps it would be useful to call `climbInit()` on all subsystems before climb | ||
* and leave the cleanup logic to be implemented (or left blank) in each subsystem, but it's also probably fine to call | ||
* `climbInit()` on only the subsystems that need to clean up (and not include it in this class). I guess it depends on how | ||
* many subsystems need to implement climb cleanup logic. | ||
*/ | ||
public void climbInit() { } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package frc.robot.brownout; | ||
|
||
import java.util.HashSet; | ||
import java.util.Set; | ||
|
||
import edu.wpi.first.wpilibj.PowerDistribution; | ||
import frc.robot.GRTSubsystem; | ||
|
||
/** | ||
* A power controller which dynamically sets current limits for subsystems to avoid brownout situations. | ||
*/ | ||
public class PowerController { | ||
private static PowerDistribution PDH = new PowerDistribution(); | ||
|
||
private GRTSubsystem[] subsystems; | ||
|
||
// TODO: Calculate total sustainable current | ||
private static final double totalSustainableCurrent = 200.0; | ||
|
||
public PowerController(GRTSubsystem... subsystems) { | ||
this.subsystems = subsystems; | ||
} | ||
|
||
/** | ||
* Calculates the current limits for each subsystem based on their usage. | ||
* This method ensures that all subsystem limits sum to `totalSustainableCurrent`, with dynamic limit distribution | ||
* based on usage ratios and minimum current values. | ||
*/ | ||
public void calculateLimits() { | ||
double totalDraw = PDH.getTotalCurrent(); | ||
if (totalDraw == 0) return; | ||
calculateLimits(totalSustainableCurrent, totalDraw, Set.of(subsystems)); | ||
} | ||
|
||
/** | ||
* Calculates the current limits for each subsystem based on their usage. | ||
* This method ensures that all subsystem limits sum to `totalSustainableCurrent`, with dynamic limit distribution | ||
* based on usage ratios and minimum current values. | ||
* | ||
* @param totalCurrent The total current limit. | ||
* @param totalDraw The total current draw. | ||
* @param remaining The remaining (non-scaled) subsystems. | ||
*/ | ||
private void calculateLimits(double totalCurrent, double totalDraw, Set<GRTSubsystem> remaining) { | ||
// Calculate the "ideal ratio" from the total drawn current | ||
double idealRatio = totalCurrent / totalDraw; | ||
|
||
// Check each subsystem to see if they cannot be scaled (if the resultant limit would be below their minimum current). | ||
// If this is the case, limit the system by its minimum and adjust the ideal ratio accordingly. | ||
for (GRTSubsystem subsystem : remaining) { | ||
double drawn = subsystem.getTotalCurrentDrawn(); | ||
double min = subsystem.getMinCurrent(); | ||
|
||
if (drawn * idealRatio < min) { | ||
subsystem.setCurrentLimit(min); | ||
|
||
// Adjust the ratio to as if the below-minimum subsystem were excluded entirely from the calculation | ||
HashSet<GRTSubsystem> cloned = new HashSet<GRTSubsystem>(remaining); | ||
cloned.remove(subsystem); | ||
calculateLimits(totalCurrent - min, totalDraw - drawn, cloned); | ||
return; | ||
} | ||
} | ||
|
||
// For all other subsystems, limit each subsystem by scaling their current draw by the ideal ratio. | ||
// This acts to distribute unused limit to subsystems which are approaching their limits while maintaining that the | ||
// sum of all the current limits adds up to the total sustainable threshold. | ||
for (GRTSubsystem subsystem : remaining) { | ||
subsystem.setCurrentLimit(subsystem.getTotalCurrentDrawn() * idealRatio); | ||
} | ||
} | ||
|
||
/** | ||
* Gets the total current drawn from the PDH by the specified channels. | ||
* @param channels The channels to sum. | ||
* @return The total current drawn by the provided channels. | ||
*/ | ||
public static double getCurrentDrawnFromPDH(int... channels) { | ||
double sum = 0; | ||
for (int channel : channels) { | ||
sum += PDH.getCurrent(channel); | ||
} | ||
return sum; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters