CANTalon PIDF close loop error is offset by a wide margin

Hey everyone!

My team is trying to tune our robot’s shooter velocity with TalonSRX’s built-in PIDF closed loop. However, when running the shooter motors the closed loop error is significantly below its expected value. This makes the motor controller think that the shooter motor is always below the target speed, even when that isn’t the case.

We’re using Talon SRX’s with a Versaplanetary encoder stage from Vexpro. They are running two 775 motors in parallel at the same time. We’re using the two motors in a master-follower setup.

Here are two graphs we recorded on SmartDashboard during testing. The graph on the left is the recorded motor speed while the graph on the right is the closed loop error calculated by the Talon motor controller.

The recorded speed values are accurate, and every number input in the picture is used in our closed loop.

We’ve tried many different approaches to fixing the problem, including reversing the motor sensors, updating the Java SRX libraries and the firmware on each Talon (both currently at the latest firmware version), and have searched through the Talon SRX software reference manual

Here are screenshots from the Rio Motor Controller Web Server:

Master Motor:

Follower/Slave Motor:

(Note, These motors were updated with a Closed Loop P value of 0.1, not 0.6 as seen in the graphs.)

Here is the Java source code we used for our tests:

public class Robot extends IterativeRobot {

    CANTalon shooterMotorA, shooterMotorB;

    public void robotInit() {
        shooterMotorA = new CANTalon(0);
        shooterMotorB = new CANTalon(5);








        // Initialize Smart Dashboard values
        SmartDashboard.putNumber("Shooter Target Speed", 0.0);
        SmartDashboard.putNumber("P ShooterAccelerate", 0.0);
        SmartDashboard.putNumber("I ShooterAccelerate", 0.0);
        SmartDashboard.putNumber("D ShooterAccelerate", 0.0);
        SmartDashboard.putNumber("F ShooterAccelerate", 0.0);

    public void teleopPeriodic() {
                SmartDashboard.getNumber("P ShooterAccelerate", 0.0),
                SmartDashboard.getNumber("I ShooterAccelerate", 0.0),
                SmartDashboard.getNumber("D ShooterAccelerate", 0.0)
        shooterMotorA.setF(SmartDashboard.getNumber("F ShooterAccelerate", 0.0));

        shooterMotorA.set(SmartDashboard.getNumber("Shooter Target Speed", 0.0));

        // Logs and Feedback
        SmartDashboard.putNumber("PID ShooterAccelerate OUTPUT", 
        SmartDashboard.putNumber("PID ShooterAccelerate ERROR",