Skip to content

PID Configuration and Usage

CoolProGamer edited this page Jan 29, 2025 · 3 revisions

The Basics

A PID, or a Proportional-Integral-Derivative controller is an example of feedback control, where the device reacts to the error in the system in order to approach a destination at maximum precision. In order to fully understand how we use PID and other motion control features of motor controllers, we need to understand these concepts:

Closed-loop control: A control system using feedback and feedforward, wherein error from the system sensor data is used to adjust values in the approach to a target. The device reacts to error to counteract it and get to the destination. Feedback refers to calculations done to automatically offset error found in the system. Feedforward is directly specifying values to be added, multiplied or input in some other calculation to the system to approximate control over error.

Open-loop control: A control system using feedforward, wherein we specify values to directly control device output. We basically guess that it will hopefully get us to a target position/output we want. This control method does NOT use PID, so we don't use it here.

Motor Control Output Types: Motors have a variety of control output types we can use to specify the types of values that control it. The most basic form is known generally as percentage output, where the value is a percentage of voltage to apply to the motor. More types are covered in the linked documentation.

When we want absolute control over a motor's actions on the robot, we want to use a combination of both open-loop and closed-loop control to both account for error, and to manually approximate needed offsets to reach our target. These systems apply to any value we are trying to reach on a motor, whether it be velocity, position, etc.

PID

It’s the special magic sauce of Mustard. It’s like Grey Poupon. Goes nice with ketchup.

PID is the closed-loop control we use for motors on the robot. It contains 3 values that are set as constants for a motor controller:
Proportional- Drives the value towards the target
Integral- Unused in FRC, keep as 0
Derivative- Compensates for error

For actual use cases, PID is run as part of a motion control request that modifies the motor's movement with PID calculations to reduce error and be as accurate as possible. Tuning these values is of utmost importance, and we use the Ziegler-Nichols method to do so.

Different Motor Controllers

Before jumping into using PID or other advanced motion control, we need to clarify what motor type we are using. REVLib and Pheonix use very different systems to assign values and drive motors with them, so make sure to follow the steps listed for your specified motor.

REVLib

Configs and values involved

Really Sparks your imagination to the MAX

The REV library includes support for REV hardware, such as SparkMAXes and SparkFlexes. To configure and use PID, you must create a SparkMaxConfig ; object and config its P, I, and D, along with another optional variable, FF, corresponding to "FeedForward". This extra value is taken into account directly into PID calculations, so it directly affects your motor's movement to some place. This object is directly related to a specific motor, so running methods on it will affect the motor.

SparkMaxConfig sparkMaxConfig = new SparkMaxConfig();

sparkMaxConfig.closedLoop.pidf(sparkConstants.SPARK_KP, sparkConstants.SPARK_KI, sparkConstants.SPARK_KD, sparkConstants.SPARK_KFF);

//once you've set up the configs with SparkMaxConfig, use the below as a example for how to apply it to the motor
sparkMaxMotor.configure(sparkMaxConfig, SparkBase.ResetMode.kResetSafeParameters, SparkBase.PersistMode.kPersistParameters);
  • ResetMode and PersistMode are expanded upon here, but here they are used as placeholders;

Note that the pidf command configures the motor object, and must be paired with the .configure method to apply P, I, D, and FF to the motor. Also, these configs are stored on a virtual "slot" on the motor controller. Each motor controller has several slots where these values can be stored, starting at slot 0;

Running PID-based motion control on a motor

We use the setReference(double value, ControlType ctrl, int pidSlot) method to set both the motor output type and the reference value for PID calculations to reach for the motor, along with the PID slot being used.

The following example sets a reference value of 20 (rotations) in the position control mode, using the values from the 1st pid slot.

pidController.setReference(20, ControlType.kPosition, 1);

For more information on REV control output types with or without PID, see here.

Pheonix (TalonFX)

Configuration of Values

Pheonix 6, the most updated vendordep version, provides a PID value config process integrated into their general configuration process.
You need to create a TalonFXConfiguration object and access its stored instance of SlotXConfigs, where "X" is the pid slot the values are stored in.

TalonFXConfiguration configs = new TalonFXConfiguration();
slot0Configs = configs.Slot0;

The SlotXConfigs object can then be used to access instances of P, I, and D.

slot0configs.kP = 0.01;
slot0configs.kI = 0;
slot0configs.kD = 0.5;

NOTE: the "k" is part of a naming scheme. Don't question it or use it when writing new configurable value code.
Using closed loop control (with PID) also enables us to use some other configurable values with this object: kS, kV, and kA (and kG, but ignore that for now). These are the feedforward values that can be used in conjuction with our feedback PID values to account for more error. See Pheonix 6 Documentation for more information.

Once we have the configuration object, we just have to apply it to a motor of choice, like so:

motor.getConfigurator().apply(configs)

Using PID in Control Requests

In Pheonix 6, PID has to be used in conjuction with a closed-loop control request. In the example below, we create a control request specifying position, with a voltage output (pid calculation to determine what voltage is needed to fulfill position request).

final PositionVoltage controlRequest = new PositionVoltage(0).withSlot(0);

motor.setControl(controlRequest.withPosition(15));

PLEASE refer to the Pheonix 6 documentation for more info regarding their control requests, control output types, and TalonFX in general

Clone this wiki locally