Combinando prealimentación y Control PID

Nota

Este artículo cubre la implementación en código del control PID / prealimentación combinado con las clases de biblioteca proporcionadas por WPILib. Próximamente se publicará documentación que describe los conceptos involucrados con más detalle.

Los controladores de realimentación y retroalimentación se pueden usar de forma aislada, pero son más efectivos cuando se combinan. Afortunadamente, combinar estos dos métodos de control es extremadamente sencillo - uno simplemente suma sus resultados.

Usando prealimentación con un PIDController

Users may add any feedforward they like to the output of the controller before sending it to their motors:

// Adds a feedforward to the loop output before sending it to the motor
motor.setVoltage(pid.calculate(encoder.getDistance(), setpoint) + feedforward);
// Adds a feedforward to the loop output before sending it to the motor
motor.SetVoltage(pid.Calculate(encoder.GetDistance(), setpoint) + feedforward);
# Adds a feedforward to the loop output before sending it to the motor
motor.setVoltage(pid.calculate(encoder.getDistance(), setpoint) + feedforward)

Además, feedforward es una característica completamente separada de la retroalimentación y, por lo tanto, no tiene ninguna razón para manejarse en el mismo objeto de controlador, ya que esto viola la separación de preocupaciones. WPILib viene con varias clases auxiliares para calcular voltajes de retroalimentación precisos para mecanismos FRC|reg| - para obtener más información, consulte Control Feedforward en WPILib.

Uso de componentes Feedforward con PID

Nota

Since feedforward voltages are physically meaningful, it is best to use the setVoltage() (Java, C++, Python) method when applying them to motors to compensate for «voltage sag» from the battery.

¿Cómo sería un ejemplo más completo de control PID / prealimentación combinado? Considera el drive example de la página de prealimentación. Podemos modificar esto fácilmente para incluir control de retroalimentación (con un componente SimpleMotorFeedforward):

public void tankDriveWithFeedforwardPID(double leftVelocitySetpoint, double rightVelocitySetpoint) {
  leftMotor.setVoltage(feedforward.calculate(leftVelocitySetpoint)
      + leftPID.calculate(leftEncoder.getRate(), leftVelocitySetpoint));
  rightMotor.setVoltage(feedForward.calculate(rightVelocitySetpoint)
      + rightPID.calculate(rightEncoder.getRate(), rightVelocitySetpoint));
}
void TankDriveWithFeedforwardPID(units::meters_per_second_t leftVelocitySetpoint,
                                 units::meters_per_second_t rightVelocitySetpoint) {
  leftMotor.SetVoltage(feedforward.Calculate(leftVelocitySetpoint)
      + leftPID.Calculate(leftEncoder.getRate(), leftVelocitySetpoint.value()));
  rightMotor.SetVoltage(feedforward.Calculate(rightVelocitySetpoint)
      + rightPID.Calculate(rightEncoder.getRate(), rightVelocitySetpoint.value()));
}
def tank_drive_with_feedforward_PID(
    left_velocity_setpoint: float,
    right_velocity_setpoint: float,
) -> None:
    leftMotor.setVoltage(
        feedforward.calculate(left_velocity_setpoint)
        + leftPID.calculate(leftEncoder.getRate(), left_velocity_setpoint)
    )
    rightMotor.setVoltage(
        feedforward.calculate(right_velocity_setpoint)
        + rightPID.calculate(rightEncoder.getRate(), right_velocity_setpoint)
    )

Otros tipos de mecanismos se pueden manejar de manera similar.