FrcCatalyst is a plug-and-play Java library for FRC teams using CTRE Phoenix 6 hardware. It provides production-ready mechanism building blocks, hardware wrappers, and utilities so your team can focus on strategy and game-specific logic instead of writing boilerplate.
One import. One builder call. A fully functional mechanism with telemetry, simulation, safety limits, and command factories.
| Feature | Raw WPILib/Phoenix | FrcCatalyst |
|---|---|---|
| Elevator with gravity FF | ~150 lines | 8 lines |
| Swerve + PathPlanner + Vision | ~400 lines | 15 lines |
| Mechanism with sim + telemetry | Build it yourself | Built-in |
| Safe temperature cutoffs | Manual | Automatic |
| Limit switch auto-zeroing | Manual wiring | One builder call |
Add FrcCatalyst to your robot project's build.gradle:
repositories {
maven { url "https://jitpack.io" }
}
dependencies {
implementation "com.github.TomAs-1226:FrcCatalyst:v0.3.0-beta"
}// A full-featured elevator in ~10 lines
LinearMechanism elevator = new LinearMechanism(
LinearMechanism.Config.builder()
.name("Elevator")
.motor(13)
.follower(14, true)
.motorType(MotorType.KRAKEN_X60)
.gearRatio(10.0)
.drumRadius(0.0254) // 1 inch
.stages(2) // 2-stage cascade
.range(0.0, 1.2) // meters
.mass(5.0) // kg
.pid(50, 0, 0.5)
.gravityGain(0.35)
.motionMagic(2.0, 4.0, 20.0)
.currentLimit(40)
.reverseLimitSwitch(0, true) // DIO 0, auto-zero
.maxTemperature(70) // safety cutoff
.position("STOW", 0.0)
.position("INTAKE", 0.3)
.position("AMP", 0.8)
.position("HIGH", 1.1)
.build()
);
// Use it
elevator.setDefaultCommand(elevator.holdPosition());
operatorController.a().onTrue(elevator.goTo("HIGH"));
operatorController.b().onTrue(elevator.goTo("STOW"));- Multi-follower support on
LinearMechanism— chain as many follower TalonFXs as you need. EachwithFollower(...)call is now additive, fixing the v0.2 limitation that capped you at one. - In-house logging core (
frc.lib.catalyst.logging) — every mechanism routes telemetry through a pluggableLogSink. Default sink keeps the v0.2/Catalyst/...NetworkTables layout so dashboards work unchanged. - AdvantageKit interop without bundling — install a ~10-line
LogSinkto forward everything into AK, DataLog, or anything else. Catalyst itself has zero AK dependency. See docs/advanced/logging.md. - IO + Inputs contract (
frc.lib.catalyst.io) — every mechanism now publishes a replay-shaped*MechanismInputsPOJO each loop. Default Phoenix 6 / sim IO swaps land in v0.4. - Three new mechanisms —
ClawMechanism,DifferentialWristMechanism,PneumaticMechanism. - Configurable tolerances —
RotationalMechanismandDifferentialWristMechanismaccepttolerance(...)in their builder;LinearMechanismalready supports it. - Forward-limit auto-zero on
LinearMechanism(mirrors the existing reverse-limit support). - Bug fixes —
RotationalMechanism.atPosition(name)no longer ignores the configured tolerance;LinearMechanismauto-zero now seeds toconfig.minPositioninstead of 0; sim motor count tracks the real follower count.
frc.lib.catalyst
|
+-- hardware/ Motor, encoder, gyro wrappers
| +-- CatalystMotor TalonFX with builder config + telemetry + fault detection
| +-- CatalystEncoder CANcoder wrapper
| +-- CatalystGyro Pigeon2 IMU wrapper
| +-- MotorType Motor specs enum (Kraken, Falcon)
|
+-- mechanisms/ Generic reusable mechanisms
| +-- LinearMechanism Elevator, slide, telescoping arm
| +-- RotationalMechanism Arm, wrist, turret, hood
| +-- FlywheelMechanism Shooter, accelerator wheel
| +-- RollerMechanism Intake, conveyor, indexer (with ramp, pulse, voltage feed)
| +-- WinchMechanism Climber, deployment
| +-- ClawMechanism Motor-driven gripper with stall-based grip detection
| +-- DifferentialWristMechanism Two-motor diffy wrist (pitch + roll)
| +-- PneumaticMechanism Single/double solenoid with pressure-aware safety
| +-- SuperstructureCoordinator State machine + collision zones + timeouts
|
+-- io/ Hardware-abstraction layer (v0.3)
| +-- *MechanismInputs Per-loop input snapshots (replay-friendly)
| +-- *MechanismIO Hardware contracts; default Phoenix 6 impls land in v0.4
|
+-- logging/ In-house logging core (v0.3)
| +-- CatalystLog Static facade — swap sinks at robot init
| +-- LogSink Pluggable sink interface (NT default, AK-bridgeable)
| +-- NetworkTablesSink Default sink — same /Catalyst/... layout as v0.2
| +-- CompoundSink Fan-out to multiple sinks simultaneously
| +-- CatalystInputs Symmetric toLog/fromLog contract for Inputs POJOs
|
+-- subsystems/ Complex subsystems
| +-- SwerveSubsystem Swerve drive with skew correction, snap-to, advanced drive
| +-- VisionSubsystem Multi-camera with innovation tracking + speed rejection
| +-- LEDSubsystem 14 addressable LED patterns
|
+-- util/ Utilities
+-- FeedforwardGains kS/kV/kA/kG storage + calculators
+-- TrapezoidProfileHelper Motion profile factories
+-- AlertManager Centralized fault system
+-- MechanismVisualizer Mechanism2d dashboard helper
+-- CharacterizationHelper SysId routine wrapper
+-- CatalystMath Joystick curves, geometry, physics
+-- InterpolatingTable Shooter lookup tables
+-- SlewRateLimiter Asymmetric rate limiting
+-- MovingAverage Sliding window filter
+-- TimedBoolean Debounced boolean
+-- StateSpaceController LQR + Kalman filter (optimal control)
+-- SignalProcessor EMA, median, low-pass, composite filters
+-- MotionConstraintCalculator Physics-based motion constraints
+-- PoseHistory Temporal pose tracking + interpolation
+-- DynamicAutoBuilder Runtime PathPlanner path generation
+-- TunableNumber Dashboard-editable constants (live PID tuning)
+-- AutoSelector PathPlanner auto chooser with safe defaults
+-- GamePieceTracker Multi-stage piece tracking with Triggers
Every mechanism provides:
- Builder-pattern config with sensible defaults
- Motion Magic position control (on TalonFX)
- WPILib ProfiledPID alternative (on roboRIO)
- Named position presets (
goTo("STOW")) - Gravity compensation (elevator static, arm cosine)
- Built-in simulation (proper DCMotor models)
- Automatic telemetry to NetworkTables
- Safety features (temperature cutoff, limit switches, soft limits)
- Pre-built commands (goTo, hold, jog, zero)
For elevators, linear slides, and telescoping arms.
LinearMechanism elevator = new LinearMechanism(
LinearMechanism.Config.builder()
.name("Elevator")
.motor(13).follower(14, true)
.motorType(MotorType.KRAKEN_X60)
.gearRatio(10.0)
.drumRadius(0.0254)
.stages(2) // cascading stages
.range(0.0, 1.2).mass(5.0)
.pid(50, 0, 0.5).gravityGain(0.35)
.motionMagic(2.0, 4.0, 20.0)
.reverseLimitSwitch(0, true) // auto-zero
.position("STOW", 0.0)
.position("HIGH", 1.1)
.build()
);For arms, wrists, turrets, and hoods.
RotationalMechanism arm = new RotationalMechanism(
RotationalMechanism.Config.builder()
.name("Arm")
.motor(15)
.motorType(MotorType.KRAKEN_X60)
.gearRatio(50.0)
.length(0.5).mass(3.0) // for simulation + FF estimation
.range(-10, 120)
.pid(80, 0, 1.0).gravityGain(0.4)
.motionMagic(200, 400, 2000)
.hardStop(1, true, 0.0) // DIO 1, auto-zero at 0 degrees
.position("STOW", 0).position("SCORE", 100)
.build()
);For shooters with optional dual-motor differential spin.
FlywheelMechanism shooter = new FlywheelMechanism(
FlywheelMechanism.Config.builder()
.name("Shooter")
.motor(20).secondMotor(21) // dual flywheels
.gearRatio(1.5)
.pid(0.3, 0, 0).feedforward(0.12, 0.11)
.velocityTolerance(3.0) // RPS
.build()
);
// Spin with backspin for shot arc control
shooter.spinUp(70, 50); // top 70 RPS, bottom 50 RPSFor intakes with game piece detection.
RollerMechanism intake = new RollerMechanism(
RollerMechanism.Config.builder()
.name("Intake")
.motor(16)
.intakeSpeed(0.8).ejectSpeed(-0.6)
.stallDetection(25, 0.2) // 25A for 0.2s = game piece
.beamBreak(2) // DIO 2
.build()
);
// Auto-stops when game piece detected
intake.intake();Motor-driven gripper. Closes onto a piece, then drops to a low passive hold voltage once stall-current detection trips — so the motor doesn't cook trying to grip harder.
ClawMechanism claw = new ClawMechanism(
ClawMechanism.Config.builder()
.name("Claw")
.motor(30)
.closeVoltage(6.0).openVoltage(-4.0).holdVoltage(1.5)
.stallDetection(25.0, 0.2) // 25A for 0.2s => piece gripped
.currentLimit(40)
.build()
);
operator.a().onTrue(claw.closeUntilGripped());
operator.b().onTrue(claw.open());Two motors coupled through a bevel differential to give pitch + roll control
with a single mechanism. Resolves (pitch, roll) ↔ (leftRotations, rightRotations)
for you and drives both axes via Motion Magic.
DifferentialWristMechanism wrist = new DifferentialWristMechanism(
DifferentialWristMechanism.Config.builder()
.name("Wrist")
.leftMotor(40).rightMotor(41)
.gearRatio(20.0)
.pitchRange(-90, 90).rollRange(-180, 180)
.pid(40, 0, 0.5)
.motionMagic(50, 100, 500)
.position("STOW", 0, 0)
.position("SCORE", 60, 90)
.build()
);
wrist.goTo("SCORE");Single or double solenoid actuator with command factories, pressure-aware safety, and cycle counting. Covers climbers, hatch ejectors, shifters, kickers.
PneumaticMechanism climbHook = new PneumaticMechanism(
PneumaticMechanism.Config.builder()
.name("ClimbHook")
.doubleSolenoid(PneumaticsModuleType.REVPH, 0, 1)
.compressor(PneumaticsModuleType.REVPH)
.requirePressureAbove(40.0) // refuse to fire below 40 psi
.build()
);
driver.x().onTrue(climbHook.extend());
driver.y().onTrue(climbHook.retract());Every mechanism routes telemetry through CatalystLog, a static facade backed
by a pluggable LogSink. By default a NetworkTablesSink keeps the v0.2
/Catalyst/<name>/... layout. To send everything to AdvantageKit instead,
install a thin sink at robot init:
CatalystLog.setSink(new LogSink() {
@Override public void log(String key, double v) { Logger.recordOutput(key, v); }
@Override public void log(String key, boolean v) { Logger.recordOutput(key, v); }
// ... wire the remaining typed overloads similarly
});Catalyst itself has no AdvantageKit dependency — the bridge lives in your code. Full details and a fan-out example in docs/advanced/logging.md.
Wraps CTRE Tuner X generated swerve code with PathPlanner, vision, heading lock, point-at-target, skew correction, and advanced drive features.
SwerveSubsystem drive = new SwerveSubsystem(
TunerConstants.createDrivetrain(),
4.5, // max speed m/s
SwerveSubsystem.PathPlannerConfig.builder()
.translationPID(5.0, 0, 0)
.rotationPID(5.0, 0, 0)
.build()
);
// Enable advanced features
drive.setSkewCorrectionEnabled(true);
drive.enableSlewRateLimiting(2.0, 5.0);
drive.setSnapToAngles(new double[]{0.0, 90.0, 180.0, 270.0}, 5.0);
// Advanced drive: deadband + slew + heading lock + snap + skew correction
drive.setDefaultCommand(drive.advancedDrive(
() -> -controller.getLeftY(),
() -> -controller.getLeftX(),
() -> -controller.getRightX(),
0.05
));
// Slow mode for precision
driver.leftBumper().whileTrue(drive.slowModeWhileHeld(0.3));Multi-camera pose estimation with advanced Kalman filter tuning, innovation tracking, and multi-layer filtering.
VisionSubsystem vision = new VisionSubsystem(VisionConfig.builder()
.addLimelight("limelight-front",
new Transform3d(0.3, 0, 0.5, new Rotation3d(0, Math.toRadians(-15), 0)))
.addPhotonCamera("cam-back",
new Transform3d(-0.3, 0, 0.5, new Rotation3d(0, Math.toRadians(-20), Math.PI)),
fieldLayout)
.driveSubsystem(drive)
.baseXYStdDev(0.3)
.baseRotStdDev(0.7)
.xyDistanceScaling(1.0)
.rejectDuringSpin(2.0)
.rejectDuringHighSpeed(3.0) // reject when > 3 m/s
.maxHeadingDivergence(15.0) // reject if heading disagrees > 15 deg
.fieldDimensions(16.54, 8.21) // custom field bounds
.maxLatency(0.5)
.build());| Utility | Description |
|---|---|
FeedforwardGains |
Store and calculate kS/kV/kA/kG for any mechanism type |
TrapezoidProfileHelper |
Factory methods for WPILib ProfiledPIDController |
AlertManager |
Centralized fault/warning system with NetworkTables publishing |
MechanismVisualizer |
Mechanism2d dashboard builder (elevator + arm visualization) |
CharacterizationHelper |
SysId routine wrapper for one-line characterization setup |
SuperstructureCoordinator |
Multi-mechanism state machine with collision zones + timeouts |
InterpolatingTable |
TreeMap-based linear interpolation (shooter distance tables) |
CatalystMath |
Joystick curves, angle math, geometry helpers, physics |
SlewRateLimiter |
Asymmetric rate limiter (different accel/decel profiles) |
MovingAverage |
Sliding window average filter |
TimedBoolean |
Debounced boolean with rising/falling edge detection |
StateSpaceController |
LQR + Kalman filter for optimal mechanism control |
SignalProcessor |
EMA, median, low-pass, composite sensor filters |
MotionConstraintCalculator |
Physics-based max velocity/acceleration from motor specs |
PoseHistory |
Temporal pose tracking with interpolation for latency compensation |
DynamicAutoBuilder |
Runtime path generation with PathPlanner |
TunableNumber |
Dashboard-editable constants for live PID tuning |
AutoSelector |
PathPlanner auto chooser with safe fallbacks |
GamePieceTracker |
Multi-stage game piece state machine with Triggers |
By default, FrcCatalyst uses the TalonFX internal encoder — no extra hardware needed. For mechanisms requiring absolute positioning (e.g., an arm that must know its angle on startup), you can optionally fuse a CANcoder:
// Default: internal encoder only (simplest, no extra hardware)
CatalystMotor.builder(1)
.sensorToMechanismRatio(10.0) // 10 motor rotations = 1 mechanism rotation
.build();
// FusedCANcoder (requires Phoenix Pro license)
CatalystMotor.builder(1)
.fusedCANcoder(20, 1.0) // CANcoder ID 20, 1:1 rotor-to-sensor
.sensorToMechanismRatio(10.0)
.build();
// SyncCANcoder (no Pro license needed)
CatalystMotor.builder(1)
.syncCANcoder(20, 1.0)
.sensorToMechanismRatio(10.0)
.build();| Mode | Pro Required | Accuracy | Use Case |
|---|---|---|---|
| Internal (default) | No | Good | Elevators, flywheels, most mechanisms |
| FusedCANcoder | Yes | Best | Swerve azimuth, precision arms |
| SyncCANcoder | No | Good+ | Arms that need boot-up absolute position |
| RemoteCANcoder | No | Moderate | Legacy setups |
FrcCatalyst can estimate feedforward gains and max speeds from your mechanism's physical specs:
var config = LinearMechanism.Config.builder()
.motorType(MotorType.KRAKEN_X60)
.gearRatio(10.0)
.drumRadius(0.0254)
.mass(5.0)
.stages(2)
.build();
double gravityFF = config.estimateGravityFF(); // ~0.35V
double maxSpeed = config.estimateMaxSpeed(); // ~1.9 m/sFrcCatalyst includes a comprehensive test project at FrcCatalystTest that validates all library components:
- 68 JUnit tests covering utilities, math helpers, hardware types, and mechanism construction
- Simulation tests for all mechanism types (elevator, arm, flywheel, roller, winch)
- SuperstructureCoordinator state machine integration tests
# Run unit tests (utilities, math, hardware)
./gradlew test
# Run all tests including mechanism simulation
./gradlew testAll| Dependency | Version |
|---|---|
| WPILib | 2026.2.1 |
| CTRE Phoenix 6 | 26.1.1 |
| PhotonVision | v2026.3.1 |
| PathPlanner | 2026.1.2 |
| Java | 17+ |
See CONTRIBUTING.md for guidelines.
This project is available under the MIT License.
Built for the FRC community. Go build something awesome.