Feedforward for TalonFX PID

I want to use a feedforward for the velocity-control on my drive motors, but I am not sure what is the best way to do so. I could use the typical kF constant, or I could use the SimpleMotorFeedforward with the values obtained from characterization. My question is, can I use the kF term and the SimpleMotorFeedforward (passed in as an arbitrary feedforward) at the same time? I would imagine using both at the same time will make the output too high. Perhaps a better approach would be to set kF to 0, set kP to the value provided by sysid, and then pass in the SimpleMotorFeedforward as an arbitrary feedforward.

Are you using these for your steer motors or driving motors?

Are you wanting to run onboard PIDs or wplib PIDs?

This is the best approach if you want to combine the WPILib feedforwards with on-controller feedback.

The kF on the motor controller is equivalent to kV in the WPILib feedforward objects, except in different units.

1 Like

Setting the arbitrary feedforward makes a ton of sense if your velocity setpoint won’t be constantly changing — you get a better model and a more transparent and debuggable process.

If you’re using a trapezoidal motion profile or something else that changes velocity setpoints really often, you probably want to use kF.

For example, we used SimpleMotorFeedforward with on motor P for our swerve drive motor, and a kF term with motion magic and a PD controller for our swerve angle motor.

In what scenario have you found that updating the setpoints and feedforwards at a rate higher than the main loop frequency of 50hz has made an appreciable difference?

With the angle motors on the swerve. The difference was appreciable but not like… incredible.

4 Likes

If I want to use arbitrary feedforward, I should just do
m_driveMotor.set(ControlMode.Velocity, state.speedMetersPerSecond, DemandType.ArbitraryFeedForward, DriveConstants.driveTrainFeedforward.calculate(state.speedMetersPerSecond));?

If I want to use KF, what is the conversion factor?

Unit Conversion

Assume your output is in m/s, and you are driving a motor with an encoder that reports velocity in 100ms instead of seconds (CTRE).
Assuming your positional conversion factor is kEncoderPositionToMeters where encoderValue * kEncoderPositionToMeters will give you the number of meters the wheel spun.

Your position conversion factor will need to take into account your gearbox reduction and also your encoder’s “ticks per rotation” (2048 for falcon integrated, 4096 for mag encoder).

Then your velocity conversion factor will be kEncoderVelocityToMetersPerSecond = kEncoderPositionToMeters * 10 The 10 converts from distance/100ms to distance/second. So encoderVelocity * kEncoderVelocityToMetersPerSecond will give you the velocity of the robot in m/s.

WPILIB FeedForward

In order to use the statement in your question you will first need to convert your velocity to talon units as follows.

double nativeVelocity = speedMetersPerSecond / kEncoderVelocityToMetersPerSecond;
double ffVolts = DriveConstants.driveTrainFeedforward.calculate(speedMetersPerSecond);

m_driveMotor.set(ControlMode.Velocity, nativeVelocity, DemandType.ArbitraryFeedForward, ffVolts/kNominalVoltage);

You will notice that I am dividing the output of the feed forward calculation by kNominalVoltage this is because the output of feed forwards is in volts, while the input of ArbitraryFeedForward is in scaled “units” [-1,1]. This scale is like PercentOutput which means the supplied voltage is actually the current battery voltage multiplied by the percent out value. But the feed forward calculation is precise in the voltage you should give the motor, therefore you need to enable voltage compensation on the motor, and decide on some value to represent 100% output (here called kNominalVoltage).

You can do this somewhere in the initialization sequence in your code

m_driveMotor.configVoltageCompSaturation(kNominalVoltage);
m_driveMotor.enableVoltageCompensation(true);

CTRE kF

If you wanted to use kF (the built in to the CTRE controllers) you still need to characterize your subsystem (using SysId) in the same manner as you would for the WPILIB feed forward, then do some unit conversion to match the CTRE units.

// In Constants.java
public static final double kF = kV / kNominalVoltage * kEncoderVelocityToMetersPerSecond * 1023;
...
// Where you configure your motors
m_driveMotor.config_kF(DriveConstants.kF);

Here is the CTRE page on kF, notice that it makes you do characterization in a very weird way, you should stick to using SysId as that is a lot easier to understand and has sensible units.

Since kF is approximately kV, you are still not compensating for the static friction in the subsystem for which you need kS. So in general, for a drivetrain you should use the WPILIB SimpleMotorFeedforward, but if you are using a subsystem that you want use with CTREs MotionMagic (trapezoidal motion profiling) which has changing velocity setpoints, you can consider using kF.

6 Likes