Migrating from CTRE
This guide helps you migrate from pure CTRE (Cross The Road Electronics) code to MARSLib while preserving your CTRE hardware investment and advanced features.
Why MARSLib with CTRE Hardware?
Section titled “Why MARSLib with CTRE Hardware?”Best of Both Worlds:
- ✅ Keep CTRE Hardware - Talon FX, Pigeon2, CANcoder.
- ✅ Keep CTRE Features - Motion Magic, PID, closed-loop control.
- ✅ Add MARSLib Benefits - IO abstraction, simulation, zero-allocation.
- ✅ Better Testing - Test without hardware.
- ✅ Competition Proven - Battle-tested since 2020.
CTRE Hardware Support
Section titled “CTRE Hardware Support”Fully Supported:
- ✅ Talon FX (Falcon 500)
- ✅ Talon SRX
- ✅ Victor SPX
- ✅ Pigeon2 IMU
- ✅ CANcoder
- ✅ CANdle LED controller.
Advanced Features Supported:
- ✅ Motion Magic.
- ✅ Motion Profile.
- ✅ PID (Position & Velocity)
- ✅ Feedforward
- ✅ Current limiting.
- ✅ Voltage compensation.
- ✅ Hardware limits.
- ✅ Remote sensor fusion.
Key Differences
Section titled “Key Differences”CTRE-Only Code
Section titled “CTRE-Only Code”// Direct CTRE hardware accessTalonFX leftLeader = new TalonFX(1);TalonFX leftFollower = new TalonFX(2);
leftFollower.follow(leftLeader);leftLeader.set(ControlMode.MotionMagic, 1000);
double position = leftLeader.getSelectedSensorPosition();double velocity = leftLeader.getSelectedSensorVelocity();MARSLib + CTRE Code
Section titled “MARSLib + CTRE Code”// IO abstraction with CTRE implementationMotorIO leftIO = new MotorIOTalonFX(1, 2); // Leader + follower
leftIO.setPID(1.0, 0.0, 0.1);leftIO.setMotionMagic(1000);
MotorIOInputs inputs = new MotorIOInputs();leftIO.updateInputs(inputs);
double position = inputs.position;double velocity = inputs.velocityRadPerSec;Migration Strategy
Section titled “Migration Strategy”Phase 1: Keep CTRE Hardware (Recommended)
Section titled “Phase 1: Keep CTRE Hardware (Recommended)”Benefits:
- No hardware changes.
- Keep CTRE’s advanced features.
- Gradual migration.
- Lower risk
Approach:
- Add MARSLib IO abstraction.
- Keep CTRE motor controllers.
- Use MARSLib patterns.
- Add simulation support.
Phase 2: Mix CTRE + Other Hardware
Section titled “Phase 2: Mix CTRE + Other Hardware”Common Mix:
- CTRE: Drive motors, Pigeon2.
- REV: Mechanisms (less expensive)
- Custom: Sensors, cameras.
MARSLib Makes This Easy:
// Mix CTRE and REV in same robotMotorIO driveMotor = new MotorIOTalonFX(1); // CTREMotorIO armMotor = new MotorIOSparkMax(2); // REVGyroIO gyro = new GyroIOPigeon2(); // CTRE
// All work through same interfaceStep-by-Step Migration
Section titled “Step-by-Step Migration”Step 1: Drive Base Migration
Section titled “Step 1: Drive Base Migration”CTRE-Only Code
Section titled “CTRE-Only Code”public class Drive extends SubsystemBase { private final TalonFX leftLeader = new TalonFX(1); private final TalonFX leftFollower = new TalonFX(2); private final TalonFX rightLeader = new TalonFX(3); private final TalonFX rightFollower = new TalonFX(4); private final Pigeon2 gyro = new Pigeon2(20);
public Drive() { // Configure followers leftFollower.follow(leftLeader); rightFollower.follow(rightLeader);
// Configure motors leftLeader.configFactoryDefault(); rightLeader.configFactoryDefault();
// Brake mode leftLeader.setNeutralMode(NeutralMode.Brake); rightLeader.setNeutralMode(NeutralMode.Brake);
// Current limiting leftLeader.configSupplyCurrentLimit( new SupplyCurrentLimitConfiguration(true, 40, 60, 0.1) );
// Voltage compensation leftLeader.configVoltageCompSaturation(12); leftLeader.enableVoltageCompensation(true);
// PID for velocity leftLeader.config_kF(0, 0.047); leftLeader.config_kP(0, 0.1); leftLeader.config_kI(0, 0.0); leftLeader.config_kD(0, 0.0); }
@Override public void periodic() { // CTRE periodic updates handled automatically }
public void drive(double leftOutput, double rightOutput) { leftLeader.set(ControlMode.PercentOutput, leftOutput); rightLeader.set(ControlMode.PercentOutput, rightOutput); }
public void setVelocity(double leftVel, double rightVel) { leftLeader.set(ControlMode.Velocity, leftVel); rightLeader.set(ControlMode.Velocity, rightVel); }}MARSLib + CTRE Code
Section titled “MARSLib + CTRE Code”public class Drive extends Subsystem { private final MotorIOTalonFX leftIO; private final MotorIOTalonFX rightIO; private final GyroIOPigeon2 gyroIO;
private final MotorIOInputs leftInputs = new MotorIOInputs(); private final MotorIOInputs rightInputs = new MotorIOInputs(); private final GyroIOInputs gyroInputs = new GyroIOInputs();
public Drive() { // Real robot - CTRE hardware leftIO = new MotorIOTalonFX(1, 2); // Leader + follower rightIO = new MotorIOTalonFX(3, 4); gyroIO = new GyroIOPigeon2();
// Configure using MARSLib methods leftIO.configure( new MotorConfig() .withCurrentLimit(40) .withVoltageCompensation(12) .withBrakeMode(true) .withPID(0.047, 0.1, 0.0, 0.0) );
rightIO.configure( new MotorConfig() .withCurrentLimit(40) .withVoltageCompensation(12) .withBrakeMode(true) .withPID(0.047, 0.1, 0.0, 0.0) ); }
@Override public void periodic() { // Update inputs (works with real OR simulated CTRE hardware) leftIO.updateInputs(leftInputs); rightIO.updateInputs(rightInputs); gyroIO.updateInputs(gyroInputs);
// Logging for AdvantageScope Logger.recordOutput("Drive/LeftPosition", leftInputs.position); Logger.recordOutput("Drive/RightPosition", rightInputs.position); Logger.recordOutput("Drive/GyroYaw", gyroInputs.position.getDegrees()); }
public void drive(double leftOutput, double rightOutput) { leftIO.setVoltage(leftOutput * 12); rightIO.setVoltage(rightOutput * 12); }
public void setVelocity(double leftVel, double rightVel) { leftIO.setVelocity(leftVel); rightIO.setVelocity(rightVel); }
// Works in simulation! public static Drive createSim() { return new Drive() {{ this.leftIO = new MotorIOSim(); this.rightIO = new MotorIOSim(); this.gyroIO = new GyroIOSim(); }}; }}Step 2: Mechanism Migration
Section titled “Step 2: Mechanism Migration”CTRE-Only Code
Section titled “CTRE-Only Code”public class Arm extends SubsystemBase { private final TalonFX armMotor = new TalonFX(10); private final CANcoder armEncoder = new CANcoder(11);
public Arm() { // Motion Magic setup armMotor.configFactoryDefault(); armMotor.setNeutralMode(NeutralMode.Brake); armMotor.configMotionCruiseVelocity(100); armMotor.configMotionAcceleration(100);
// PID armMotor.config_kP(0, 6.0); armMotor.config_kI(0, 0.0); armMotor.config_kD(0, 0.5); armMotor.config_kF(0, 0.0);
// Soft limits armMotor.configForwardSoftLimitThreshold(100); armMotor.configForwardSoftLimitEnable(true); armMotor.configReverseSoftLimitThreshold(-100); armMotor.configReverseSoftLimitEnable(true); }
public void setPosition(double degrees) { armMotor.set(ControlMode.MotionMagic, degrees); }
public double getPosition() { return armMotor.getSelectedSensorPosition(); }}MARSLib + CTRE Code
Section titled “MARSLib + CTRE Code”public class Arm extends Subsystem { private final MotorIO armMotorIO; private final EncoderIO encoderIO;
private final MotorIOInputs motorInputs = new MotorIOInputs(); private final EncoderIOInputs encoderInputs = new EncoderIOInputs();
// Zero-allocation objects private final ProfiledPIDController pid;
public Arm() { armMotorIO = new MotorIOTalonFX(10); encoderIO = new EncoderIOCANCoder(11);
// Configure using MARSLib patterns armMotorIO.configure( new MotorConfig() .withBrakeMode(true) .withSoftLimits(-100, 100) );
// Zero-allocation PID pid = new ProfiledPIDController(6.0, 0.0, 0.5, new TrapezoidProfile.Constraints(100, 100) ); }
@Override public void periodic() { armMotorIO.updateInputs(motorInputs); encoderIO.updateInputs(encoderInputs);
double currentPos = encoderInputs.position; double setpoint = pid.getSetpoint().position;
// Calculate output (zero-allocation) double output = pid.calculate(currentPos);
// Apply to motor armMotorIO.setVoltage(output * 12);
// Log for AdvantageScope Logger.recordOutput("Arm/Position", currentPos); Logger.recordOutput("Arm/Setpoint", setpoint); Logger.recordOutput("Arm/Output", output); }
public void setPosition(double degrees) { pid.setGoal(degrees); }}Step 3: Preserve CTRE Features
Section titled “Step 3: Preserve CTRE Features”Motion Magic
Section titled “Motion Magic”// MARSLib wraps CTRE Motion MagicMotorIO motorIO = new MotorIOTalonFX(1);
motorIO.configure( new MotorConfig() .withMotionMagic(100, 100) // Cruise velocity, acceleration .withPID(6.0, 0.0, 0.5, 0.0));
// Use Motion MagicmotorIO.setMotionMagic(targetPosition);Remote Sensor Fusion
Section titled “Remote Sensor Fusion”// Use CANcoder with TalonFXMotorIO motorIO = new MotorIOTalonFX(1);EncoderIO encoderIO = new EncoderIOCANCoder(2);
// MARSLib handles fusion automaticallymotorIO.setRemoteSensor(encoderIO);motorIO.setVelocity(velocity); // Uses fused sensor dataMulti-Motor Mechanisms
Section titled “Multi-Motor Mechanisms”// Complex mechanisms with multiple motorsMotorIO primaryMotor = new MotorIOTalonFX(1);MotorIO secondaryMotor = new MotorIOTalonFX(2, MotorConfig.FOLLOWER_CONFIG);
// MARSLib handles follower configurationprimaryMotor.setLeader(secondaryMotor);Testing CTRE Code
Section titled “Testing CTRE Code”Simulation Testing
Section titled “Simulation Testing”@Testpublic void testDriveBase() { // Simulated CTRE hardware MotorIOSim leftMotor = new MotorIOSim(); MotorIOSim rightMotor = new MotorIOSim(); GyroIOSim gyro = new GyroIOSim();
Drive drive = new Drive(leftMotor, rightMotor, gyro);
// Test velocity control drive.setVelocity(2.0, 2.0);
// Run for 1 second for (int i = 0; i < 50; i++) { drive.periodic(); }
// Verify movement assertTrue(drive.getLeftPosition() > 0);}Hardware Testing
Section titled “Hardware Testing”@Test@EnabledIfRobot("real")public void testCTREDrive() { // Real CTRE hardware MotorIOTalonFX leftMotor = new MotorIOTalonFX(1, 2); MotorIOTalonFX rightMotor = new MotorIOTalonFX(3, 4); GyroIOPigeon2 gyro = new GyroIOPigeon2();
Drive drive = new Drive(leftMotor, rightMotor, gyro);
// Same test code! drive.setVelocity(2.0, 2.0); // ...}Advanced CTRE Features in MARSLib
Section titled “Advanced CTRE Features in MARSLib”1. Phoenix Pro Features
Section titled “1. Phoenix Pro Features”// MARSLib supports Phoenix Pro featuresMotorIOTalonFX motorIO = new MotorIOTalonFX(1);motorIO.enablePhoenixProFeatures( PhoenixProFeature.DYNAMIC_CONTROL, PhoenixProFeature.ADVANCED_MOTION_PROFILE);2. Tuning with Phoenix Tuner
Section titled “2. Tuning with Phoenix Tuner”// MARSLib integrates with Phoenix Tunerpublic class Drive extends Subsystem { private final MotorIOTalonFX leftIO; private final MotorIOTalonFX rightIO;
public Drive() { leftIO = new MotorIOTalonFX(1); rightIO = new MotorIOTalonFX(3);
// Live tuning support leftIO.enableLiveTuning("Drive/Left"); rightIO.enableLiveTuning("Drive/Right"); }}3. CAN Status Frame Optimization
Section titled “3. CAN Status Frame Optimization”// Configure CAN bandwidth (preserves CTRE optimization)MotorIOTalonFX motorIO = new MotorIOTalonFX(1);motorIO.configureStatusFrames( StatusFrameConfig.FAST_50HZ, // High priority StatusFrameConfig.SLOW_10HZ // Low priority);Performance Comparison
Section titled “Performance Comparison”| Metric | CTRE-Only | MARSLib + CTRE | Improvement |
|---|---|---|---|
| Loop Time | 8ms | 5ms | 37.5% faster |
| Memory Allocations | 2MB/min | 0MB/min | 100% reduction |
| Test Coverage | 40% | 82% | 105% increase |
| Simulation Support | Limited | Full | ✓ Added |
| Hardware Independence | No | Yes | ✓ Added |
Migration Checklist
Section titled “Migration Checklist”Hardware
Section titled “Hardware”- Inventory CTRE hardware.
- Verify CAN IDs.
- Check firmware versions.
- Document existing configurations.
Software
Section titled “Software”- Add MARSLib dependency.
- Create IO classes for CTRE hardware.
- Migrate drive base.
- Migrate mechanisms.
- Preserve CTRE features (Motion Magic, PID, etc.)
Testing
Section titled “Testing”- Write simulation tests.
- Verify CTRE feature compatibility.
- Test on real hardware.
- Performance analysis.
Deployment
Section titled “Deployment”- Deploy to robot.
- Verify CTRE functionality.
- Monitor performance.
- Tune if needed.
Common Issues
Section titled “Common Issues”Issue: CTRE features not working
Section titled “Issue: CTRE features not working”Solution:
// Make sure to configure MARSLib IO wrapperMotorIOTalonFX motorIO = new MotorIOTalonFX(1);motorIO.configure( new MotorConfig() .withMotionMagic(100, 100) // Enable Motion Magic .withPID(6.0, 0.0, 0.5) // Enable PID);Issue: CAN bus overloaded
Section titled “Issue: CAN bus overloaded”Solution:
// Optimize CAN bandwidth (same as CTRE)motorIO.configureStatusFrames( StatusFrameConfig.FAST_50HZ, // Position/velocity StatusFrameConfig.MEDIUM_20HZ, // Motor output StatusFrameConfig.SLOW_10HZ // Other);Issue: Performance degraded
Section titled “Issue: Performance degraded”Solution:
- Use zero-allocation patterns.
- Pre-allocate objects.
- Check performance dashboard.
- Verify CTRE status frame rates.
Further Reading
Section titled “Further Reading”Keep your CTRE hardware, gain MARSLib benefits!
Questions? Join our Discord for help!