Utilities

FrcCatalyst includes a comprehensive set of utilities commonly needed in FRC programming.

Table of contents

  1. FeedforwardGains
  2. TrapezoidProfileHelper
  3. AlertManager
  4. CharacterizationHelper
  5. MechanismVisualizer
  6. CatalystMath
  7. InterpolatingTable
  8. SlewRateLimiter
  9. MovingAverage
  10. TimedBoolean
  11. Advanced Utilities

FeedforwardGains

Stores and calculates feedforward voltages for different mechanism types. Create them from your SysId results or use manual estimates.

// From SysId results:
FeedforwardGains elevatorFF = FeedforwardGains.elevator(0.12, 2.5, 0.1, 0.35);
FeedforwardGains armFF = FeedforwardGains.arm(0.15, 1.8, 0.05, 0.5);
FeedforwardGains flywheelFF = FeedforwardGains.simple(0.12, 0.11);

// Calculate voltages:
double holdVoltage = elevatorFF.calculateElevator();             // static hold
double moveVoltage = elevatorFF.calculateElevator(1.5);          // at 1.5 m/s
double armHold = armFF.calculateArm(Math.toRadians(45));         // at 45 degrees
double shootVoltage = flywheelFF.calculateSimple(70.0);          // at 70 RPS

TrapezoidProfileHelper

Factory methods for WPILib ProfiledPIDController — saves you from remembering the constraint constructors:

// Linear mechanism profile (meters)
ProfiledPIDController elevatorPID = TrapezoidProfileHelper.createLinear(
    50, 0, 0.5,   // PID gains
    2.0, 4.0      // max velocity (m/s), max accel (m/s^2)
);

// Rotational mechanism profile (degrees)
ProfiledPIDController armPID = TrapezoidProfileHelper.createRotational(
    80, 0, 1.0,   // PID gains
    1.5, 3.0      // max velocity (rot/s), max accel (rot/s^2)
);

// Continuous rotation (turret, 360-degree wrapping)
ProfiledPIDController turretPID = TrapezoidProfileHelper.createContinuousRotational(
    40, 0, 0.5, 2.0, 4.0
);

AlertManager

Centralized fault and warning system. Publishes to NetworkTables under Catalyst/Alerts/ so you can monitor robot health on the dashboard.

AlertManager alerts = AlertManager.getInstance();

// Report faults from anywhere in your code
alerts.error("Elevator", "Motor overtemp cutoff at 85C!");
alerts.warning("Intake", "Stall detected - game piece stuck?");
alerts.info("Vision", "No AprilTags visible");

// Check and clear faults
if (alerts.hasErrors()) {
    // handle errors
}
alerts.clearSubsystem("Elevator");
alerts.clearAll();

CharacterizationHelper

One-line SysId characterization setup. Pass the mechanism and its motor — the helper creates all four SysId routines automatically:

CharacterizationHelper charHelper = new CharacterizationHelper(
    "Elevator",          // name for SysId logging
    elevator,            // subsystem (extends SubsystemBase)
    elevator.getMotor()  // CatalystMotor for voltage control
);

// Four commands for the SysId routine:
SmartDashboard.putData("QS Fwd", charHelper.quasistaticForward());
SmartDashboard.putData("QS Rev", charHelper.quasistaticReverse());
SmartDashboard.putData("Dyn Fwd", charHelper.dynamicForward());
SmartDashboard.putData("Dyn Rev", charHelper.dynamicReverse());

MechanismVisualizer

Dashboard visualization using WPILib’s Mechanism2d. Creates a canvas with elevator and arm visualizations for real-time monitoring:

// Create a canvas (name, width in meters, height in meters)
MechanismVisualizer viz = new MechanismVisualizer("Robot", 1.0, 2.0);

// Add an elevator visualization
// (name, rootX, rootY, maxHeight, color)
var elevatorViz = viz.addElevator("Elevator", 0.5, 0.0, 1.2, Color.kBlue);

// Add an arm on top of the elevator
// (name, rootX, rootY, length, color)
var armViz = viz.addArm("Arm", 0.5, 0.0, 0.5, Color.kRed);

// In periodic: update positions
elevatorViz.setLength(elevator.getPosition());
armViz.setAngle(arm.getAngle());

CatalystMath

Joystick processing, angle math, and physics estimation helpers:

// Complete joystick processing pipeline
double output = CatalystMath.processJoystick(
    joystick.getY(), // raw input
    0.05,            // deadband
    2.0,             // square curve exponent
    0.7              // 70% max speed (slow mode)
);

// Individual operations
double dead = CatalystMath.deadband(0.03, 0.05);   // 0.0 (inside deadband)
double sq = CatalystMath.squareInput(0.5);          // 0.25 (sign preserved)
double cb = CatalystMath.cubeInput(0.5);            // 0.125

// Angle math (all in degrees)
double normalized = CatalystMath.normalizeAngle(370);     // -170 ([-180, 180])
double diff = CatalystMath.angleDifference(90, 270);      // -180
boolean close = CatalystMath.angleWithinTolerance(89, 90, 2); // true

// Physics estimation
double kG = CatalystMath.elevatorGravityFF(5.0, 0.0254, 10.0, 7.09);
double armkG = CatalystMath.armGravityFF(3.0, 0.5, 50.0, 7.09, 45.0);

InterpolatingTable

TreeMap-based linear interpolation for shooter distance tables. Uses method chaining with add():

InterpolatingTable shooterTable = new InterpolatingTable()
    .add(1.0, 3000)   // 1m -> 3000 RPM
    .add(2.0, 3500)   // 2m -> 3500 RPM
    .add(3.0, 4200)   // 3m -> 4200 RPM
    .add(5.0, 5000);  // 5m -> 5000 RPM

double rpm = shooterTable.get(2.5); // interpolates to ~3850 RPM
double clamped = shooterTable.get(0.5); // clamps to 3000 (below min key)

SlewRateLimiter

Asymmetric rate limiter with different acceleration and deceleration profiles. Great for smooth joystick response:

// Different accel vs brake rates
SlewRateLimiter limiter = new SlewRateLimiter(
    3.0,  // max acceleration (units/sec)
    5.0   // max deceleration (units/sec) - brake harder than accelerate
);

// Symmetric rate limit
SlewRateLimiter symmetric = new SlewRateLimiter(4.0);

// In periodic:
double smoothed = limiter.calculate(targetSpeed);
double current = limiter.get(); // read current output

// Reset when needed
limiter.reset(0.0);

MovingAverage

Sliding window average filter for noisy sensor data. Use calculate() to add a sample and get the running average:

MovingAverage filter = new MovingAverage(10); // 10-sample window

// In periodic:
double smooth = filter.calculate(noisySensorValue); // add + get average
double current = filter.get();                       // read current average

// Status checks
boolean full = filter.isFull();     // true when window is filled
int count = filter.getCount();      // number of samples added

// Clear and start over
filter.reset();

TimedBoolean

Debounced boolean with configurable duration threshold. The condition must be sustained for the specified duration before the output goes true:

TimedBoolean stallDetector = new TimedBoolean(0.2); // 200ms threshold

// In periodic:
boolean isStalled = stallDetector.update(current > 30.0);

// Edge detection (great for triggering one-shot actions):
boolean justStalled = stallDetector.risingEdge(current > 30.0);
boolean justCleared = stallDetector.fallingEdge(current > 30.0);

// Manual control
stallDetector.reset();
boolean state = stallDetector.get();

Advanced Utilities

FrcCatalyst also includes advanced utilities used by top FRC teams. See the Advanced section for:

Utility Description
StateSpaceController LQR + Kalman filter for optimal mechanism control
MotionConstraintCalculator Physics-based max velocity/acceleration from motor specs
SignalProcessor EMA, median, low-pass, composite filters for sensor data
PoseHistory Temporal pose tracking with interpolation
DynamicAutoBuilder Runtime path generation with PathPlanner

Back to top

Built for the FRC community. Go build something awesome.

This site uses Just the Docs, a documentation theme for Jekyll.