Skip to content
 
 

Repository files navigation

FrcCatalyst Banner

Build Status Release License Docs

WPILib Phoenix 6 Java 17 PathPlanner PhotonVision


What is FrcCatalyst?

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.

Why FrcCatalyst?

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

Quick Start

1. Add the dependency

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"
}

2. Build a mechanism in seconds

// 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"));

What's New in v0.3.0-beta

  • Multi-follower support on LinearMechanism — chain as many follower TalonFXs as you need. Each withFollower(...) 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 pluggable LogSink. Default sink keeps the v0.2 /Catalyst/... NetworkTables layout so dashboards work unchanged.
  • AdvantageKit interop without bundling — install a ~10-line LogSink to 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 *MechanismInputs POJO each loop. Default Phoenix 6 / sim IO swaps land in v0.4.
  • Three new mechanismsClawMechanism, DifferentialWristMechanism, PneumaticMechanism.
  • Configurable tolerancesRotationalMechanism and DifferentialWristMechanism accept tolerance(...) in their builder; LinearMechanism already supports it.
  • Forward-limit auto-zero on LinearMechanism (mirrors the existing reverse-limit support).
  • Bug fixesRotationalMechanism.atPosition(name) no longer ignores the configured tolerance; LinearMechanism auto-zero now seeds to config.minPosition instead of 0; sim motor count tracks the real follower count.

Architecture

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

Mechanisms

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)

LinearMechanism

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()
);

RotationalMechanism

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()
);

FlywheelMechanism

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 RPS

RollerMechanism

For 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();

ClawMechanism

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());

DifferentialWristMechanism

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");

PneumaticMechanism

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());

Logging & AdvantageKit Bridge

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.


Swerve Drive

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));

Vision

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());

Utilities

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

Encoder Architecture

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

Physics Estimation

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/s

Testing

FrcCatalyst 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

Requirements

Dependency Version
WPILib 2026.2.1
CTRE Phoenix 6 26.1.1
PhotonVision v2026.3.1
PathPlanner 2026.1.2
Java 17+

Contributing

See CONTRIBUTING.md for guidelines.


License

This project is available under the MIT License.


Built for the FRC community. Go build something awesome.

About

2079 ALARM's personal fork of TomAs-1226's FrcCatalyst

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages