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();
}
}
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.
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.
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?
For a drivetrain, you would just use two PIDControllers to control each side towards some target wheel speed (by outputting voltage or percent output).
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.
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 ?