PIDSubsystem and PIDCommand Classes

So I’ve been working on PID control and I found the PIDSubsystem class. I’ve been doing some research onto how this would integrate into a drive train and I think I have programmed a subsystem. My only issue is that I don’t understand what the double output is used for in useOutput() method. The WPIlib example uses it for a feedforward but it’s part of the default method so I can’t change it. Does that output need to be used in a specific way?

I’m also a little confused about the PIDCommand class. Do you have to use a PIDCommand with a PIDSubsystem or can you use a regular command? If I do use PIDCommand, how can I make it work with my PIDSubsystem?

here’s my subsystem

package frc.robot.subsystems;

import com.revrobotics.CANSparkMax;
import com.revrobotics.CANSparkMaxLowLevel;

import edu.wpi.first.wpilibj.CounterBase.EncodingType;
import edu.wpi.first.wpilibj.controller.PIDController;
import edu.wpi.first.wpilibj.Encoder;
import edu.wpi.first.wpilibj2.command.PIDSubsystem;
import frc.robot.Constants;

public class AutoDriveTrain extends PIDSubsystem {

    CANSparkMax leftLeader = new CANSparkMax(1, CANSparkMaxLowLevel.MotorType.kBrushless);
    CANSparkMax rightLeader = new CANSparkMax(3, CANSparkMaxLowLevel.MotorType.kBrushless);

    CANSparkMax leftFollower = new CANSparkMax(2, CANSparkMaxLowLevel.MotorType.kBrushless);
    CANSparkMax rightFollower = new CANSparkMax(4, CANSparkMaxLowLevel.MotorType.kBrushless);

    Encoder driveEncoder = new Encoder(0, 1, false, EncodingType.k4X);

    public AutoDriveTrain() {
        super(new PIDController(Constants.kP, Constants.kI, Constants.kD));

        leftFollower.follow(leftLeader, false);
        rightFollower.follow(rightLeader, false);

        driveEncoder.reset();
    }
        
    @Override
    public void periodic() {
        
    }

    @Override
    public void useOutput(double output, double setpoint) {
        leftLeader.set(output + getController().calculate(getMeasurement(), setpoint));
        rightLeader.set(output + getController().calculate(getMeasurement(), setpoint));
    }

    @Override
    public double getMeasurement() {
        return driveEncoder.get();
    }
}

Thanks!!

So, is your design goal a positional PID closed loop drivetrain? That appears to be what you have created here.

I fundamentally can’t wrap my brain around how that is going to work, but to answer your other questions

You would use it to perform your PID, whatever that type is. The WPILib example is for a shooter, so they are using the voltage and the pid output as a feedforward.

You can use a regular command to work with a PIDSubsystem, for example you may use an InstantCommand to set the setpoint of your PIDSubsystem.

You can use a PIDCommand with a regular subsystem as well, which is what the WPILib example is showing your with the TurnToAngle command.

1 Like

I don’t like using PIDSubsystem because I find it more confusing than simply creating a PIDController-- this is basically what you are getting from it.
image
The useOutput() method is literally how you define what the PID output is used for; it’s called automatically whenever the subsystem’s controller is enabled (enable()) using the current setpoint (setSetpoint()) and measurement (getMeasurement()).
If this is for a drivetrain, you want two PIDControllers (one for each side) instead of a PIDSubsystem.

PIDCommand is not necessary to control your subsystem using PID. Your command only has to control the subsystem setpoint to use the controller already there. PIDCommand is when you want a new, separate PID controller specifically for that command(i.e controlling drivetrain yaw position, which might then output to your two drivetrain PID controllers).

How would you go about just creating a pid controller rather than using the class? Do you know if there are any examples i could look at or maybe a github repo?

The docs article on PIDController should tell you everything you need to use with the object.

There are also plenty of WPILib example projects, specifically this potentiometer example which shows a simple PIDController object.

For a drivetrain, you would just use two PIDControllers to control each side towards some target wheel speed (by outputting voltage or percent output).

1 Like

leftLeader.set(driveController.calculate(driveEncoder.getDistance(), setpoint)); this is the set method for the leader motor. should I use a different method for setting the motors? you mentioned outputting voltage earlier. should I use that?

This should work.
Controlling voltage is better than percent, because percent output is reliant on the battery. If your battery voltage varies or sags in a match you will have inconsistent responses through percent output; voltage control tries to compensate for this.

@Gandagorn , chiming in here because we ran into PIDSubsytem issues and I had stumbled on your thread. We finally figured out our particular implementation bug so sharing this here in case it helps you:

The issue

Be cautious when using the PIDSubsystem class the way you may have used something like SubsystemBase. PIDSubsystem provides its own periodic() method implementation and you should try to avoid overriding it.

From the docs

The PIDSubsystem will automatically call [getMeasurement and useOutput] from its periodic() block, and pass its value to the control loop.

Overriding periodic() will stop this from happening, which is required for it to work.

What to try

Remove this entire block of code

@Override 
public void periodic() {
}

If you want to add something to the periodic block of a PIDSubsystem, you have to be sure to call super().

I have also been fighting this. I am trying to convert to the new 2020 commands. get Measurement and useOutput are not running? I just tried removing periodic but it is still not working. Could you share how you setup the PIDSubsystem ?