Control Feedforward en WPILib
Nota
This article focuses on in-code implementation of feedforward control in WPILib. For a conceptual explanation of the feedforward equations used by WPILib, see Introduction to DC Motor Feedforward
You may have used feedback control (such as PID) for reference tracking (making a system’s output follow a desired reference signal). While this is effective, it’s a reactionary measure; the system won’t start applying control effort until the system is already behind. If we could tell the controller about the desired movement and required input beforehand, the system could react quicker and the feedback controller could do less work. A controller that feeds information forward into the plant like this is called a feedforward controller.
A feedforward controller injects information about the system’s dynamics (like a mathematical model does) or the intended movement. Feedforward handles parts of the control actions we already know must be applied to make a system track a reference, then feedback compensates for what we do not or cannot know about the system’s behavior at runtime.
The WPILib Feedforward Classes
WPILib proporciona una serie de clases para ayudar a los usuarios a implementar un control de retroalimentación preciso para sus mecanismos. En muchos sentidos, una retroalimentación precisa es más importante que la retroalimentación para el control efectivo de un mecanismo. Dado que la mayoría de los mecanismos de FRC® obedecen de cerca a las ecuaciones del sistema bien entendidas, comenzar con un feedforward preciso es fácil y enormemente beneficioso para un control de mecanismo preciso y robusto.
The WPILib feedforward classes closely match the available mechanism characterization tools available in the SysId toolsuite. The system identification toolsuite can be used to quickly and effectively determine the correct gains for each type of feedforward. If you are unable to empirically characterize your mechanism (due to space and/or time constraints), reasonable estimates of kG
, kV
, and kA
can be obtained by fairly simple computation, and are also available from ReCalc. kS
is nearly impossible to model, and must be measured empirically.
WPILib actualmente proporciona las siguientes tres clases auxiliares para el control anticipado:
SimpleMotorFeedforward (Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/math/controller/SimpleMotorFeedforward.html), C++,
Python
)ArmFeedforward (Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/math/controller/ArmFeedforward.html), C++,
Python
)ElevatorFeedforward (Java](https://github.wpilib.org/allwpilib/docs/development/java/edu/wpi/first/math/controller/ElevatorFeedforward.html), C++,
Python
)
SimpleMotorFeedforward
Nota
En C ++, la clase SimpleMotorFeedforward
se basa en el tipo de unidad utilizada para las mediciones de distancia, que puede ser angular o lineal. Las ganancias pasadas deben tener unidades consistentes con las unidades de distancia, o se arrojará un error de tiempo de compilación. kS
debe tener unidades de voltios
, kV
debe tener unidades de voltios*segundos / distancia
y kA
debe tener unidades de ``voltios*segundos^2 / distancia ``. Para obtener más información sobre las unidades C++, consulte docs/software/basic-planning/cpp-units:The C ++ Units Library.
Nota
Los componentes de feedforward de Java calcularán los resultados en unidades determinadas por las unidades de las ganancias de feedforward proporcionadas por el usuario. Los usuarios deben tener cuidado de mantener las unidades consistentes, ya que WPILibJ no tiene un sistema de unidad de tipo seguro.
Nota
The API documentation for Python feedforward components indicate which unit is being used as wpimath.units.NAME
. Users must take care to use correct units, as Python does not have a type-safe unit system.
La clase SimpleMotorFeedforward
calcula el feedforward para mecanismos que constan de motores DC de imanes permanentes sin carga externa más que fricción e inercia, como volantes y accionamientos de robots.
Para crear un SimpleMotorFeedforward
, simplemente constrúyalo con las ganancias requeridas:
Nota
La ganancia de kA
se puede omitir y, si es así, tendrá un valor predeterminado de cero. Para muchos mecanismos, especialmente aquellos con poca inercia, no es necesario.
// Create a new SimpleMotorFeedforward with gains kS, kV, and kA
SimpleMotorFeedforward feedforward = new SimpleMotorFeedforward(kS, kV, kA);
// Create a new SimpleMotorFeedforward with gains kS, kV, and kA
// Distance is measured in meters
frc::SimpleMotorFeedforward<units::meters> feedforward(kS, kV, kA);
from wpimath.controller import SimpleMotorFeedforwardMeters
# Create a new SimpleMotorFeedforward with gains kS, kV, and kA
# Distance is measured in meters
feedforward = SimpleMotorFeedforwardMeters(kS, kV, kA)
Para calcular el feedforward, simplemente llame al método calculate()
con la velocidad y aceleración deseadas del motor:
Nota
El argumento de aceleración se puede omitir de la llamada calculate()
y, si es así, se tomará por defecto un valor de cero. Esto debe hacerse siempre que no haya un punto de ajuste de aceleración claramente definido.
// Calculates the feedforward for a velocity of 10 units/second and an acceleration of 20 units/second^2
// Units are determined by the units of the gains passed in at construction.
feedforward.calculate(10, 20);
// Calculates the feedforward for a velocity of 10 meters/second and an acceleration of 20 meters/second^2
// Output is in volts
feedforward.Calculate(10_mps, 20_mps_sq);
# Calculates the feedforward for a velocity of 10 meters/second and an acceleration of 20 meters/second^2
# Output is in volts
feedforward.calculate(10, 20)
ArmFeedforward
Nota
In C++, the ArmFeedforward
class assumes distances are angular, not linear. The passed-in gains must have units consistent with the angular unit, or a compile-time error will be thrown. kS
and kG
should have units of volts
, kV
should have units of volts * seconds / radians
, and kA
should have units of volts * seconds^2 / radians
. For more information on C++ units, see Biblioteca de unidades de C++.
Nota
Los componentes de feedforward de Java calcularán los resultados en unidades determinadas por las unidades de las ganancias de feedforward proporcionadas por el usuario. Los usuarios deben tener cuidado de mantener las unidades consistentes, ya que WPILibJ no tiene un sistema de unidad de tipo seguro.
Nota
The API documentation for Python feedforward components indicate which unit is being used as wpimath.units.NAME
. Users must take care to use correct units, as Python does not have a type-safe unit system.
La clase ArmFeedforward
calcula el feedforwards para brazos que están controlados directamente por un motor DC de imán permanente, con carga externa de fricción, inercia y masa del brazo. Este es un modelo preciso de la mayoría de los brazos en FRC.
Para crear un ArmFeedforward
, simplemente constrúyalo con las ganancias requeridas:
Nota
La ganancia de kA
se puede omitir y, si es así, tendrá un valor predeterminado de cero. Para muchos mecanismos, especialmente aquellos con poca inercia, no es necesario.
// Create a new ArmFeedforward with gains kS, kG, kV, and kA
ArmFeedforward feedforward = new ArmFeedforward(kS, kG, kV, kA);
// Create a new ArmFeedforward with gains kS, kG, kV, and kA
frc::ArmFeedforward feedforward(kS, kG, kV, kA);
from wpimath.controller import ArmFeedforward
# Create a new ArmFeedforward with gains kS, kG, kV, and kA
feedforward = ArmFeedforward(kS, kG, kV, kA)
Para calcular el feedforward, simplemente llame al método `` calculate () `` con la posición, velocidad y aceleración deseadas del brazo:
Nota
El argumento de aceleración se puede omitir de la llamada calculate()
y, si es así, se tomará por defecto un valor de cero. Esto debe hacerse siempre que no haya un punto de ajuste de aceleración claramente definido.
// Calculates the feedforward for a position of 1 units, a velocity of 2 units/second, and
// an acceleration of 3 units/second^2
// Units are determined by the units of the gains passed in at construction.
feedforward.calculate(1, 2, 3);
// Calculates the feedforward for a position of 1 radians, a velocity of 2 radians/second, and
// an acceleration of 3 radians/second^2
// Output is in volts
feedforward.Calculate(1_rad, 2_rad_per_s, 3_rad/(1_s * 1_s));
# Calculates the feedforward for a position of 1 radians, a velocity of 2 radians/second, and
# an acceleration of 3 radians/second^2
# Output is in volts
feedforward.calculate(1, 2, 3)
ElevatorFeedforward
Nota
In C++, the passed-in gains must have units consistent with the distance units, or a compile-time error will be thrown. kS
and kG
should have units of volts
, kV
should have units of volts * seconds / distance
, and kA
should have units of volts * seconds^2 / distance
. For more information on C++ units, see Biblioteca de unidades de C++.
Nota
Los componentes de feedforward de Java calcularán los resultados en unidades determinadas por las unidades de las ganancias de feedforward proporcionadas por el usuario. Los usuarios deben tener cuidado de mantener las unidades consistentes, ya que WPILibJ no tiene un sistema de unidad de tipo seguro.
Nota
The API documentation for Python feedforward components indicate which unit is being used as wpimath.units.NAME
. Users must take care to use correct units, as Python does not have a type-safe unit system.
La clase ElevatorFeedforward
calcula el feedforwards para ascensores que consisten en motores DC de imán permanente cargados por fricción, inercia y la masa del ascensor. Este es un modelo preciso de la mayoría de los ascensores en FRC.
Para crear un ElevatorFeedforward
, simplemente constrúyalo con las ganancias requeridas:
Nota
La ganancia de kA
se puede omitir y, si es así, tendrá un valor predeterminado de cero. Para muchos mecanismos, especialmente aquellos con poca inercia, no es necesario.
// Create a new ElevatorFeedforward with gains kS, kG, kV, and kA
ElevatorFeedforward feedforward = new ElevatorFeedforward(kS, kG, kV, kA);
// Create a new ElevatorFeedforward with gains kS, kV, and kA
// Distance is measured in meters
frc::ElevatorFeedforward feedforward(kS, kG, kV, kA);
from wpimath.controller import ElevatorFeedforward
# Create a new ElevatorFeedforward with gains kS, kV, and kA
# Distance is measured in meters
feedforward = ElevatorFeedforward(kS, kG, kV, kA)
Para calcular el feedforward, simplemente llame al método calculate()
con la velocidad y aceleración deseadas del motor:
Nota
El argumento de aceleración se puede omitir de la llamada calculate()
y, si es así, se tomará por defecto un valor de cero. Esto debe hacerse siempre que no haya un punto de ajuste de aceleración claramente definido.
// Calculates the feedforward for a velocity of 20 units/second
// and an acceleration of 30 units/second^2
// Units are determined by the units of the gains passed in at construction.
feedforward.calculate(20, 30);
// Calculates the feedforward for a velocity of 20 meters/second
// and an acceleration of 30 meters/second^2
// Output is in volts
feedforward.Calculate(20_mps, 30_mps_sq);
# Calculates the feedforward for a velocity of 20 meters/second
# and an acceleration of 30 meters/second^2
# Output is in volts
feedforward.calculate(20, 30)
Uso de Feedforward para controlar los mecanismos
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.
El control Feedforward se puede utilizar completamente por sí solo, sin un controlador de retroalimentación. Esto se conoce como control de «bucle abierto» y para muchos mecanismos (especialmente los accionamientos de robots) puede resultar perfectamente satisfactorio. Se puede emplear un SimpleMotorFeedforward
para controlar el accionamiento de un robot de la siguiente manera:
public void tankDriveWithFeedforward(double leftVelocity, double rightVelocity) {
leftMotor.setVoltage(feedforward.calculate(leftVelocity));
rightMotor.setVoltage(feedForward.calculate(rightVelocity));
}
void TankDriveWithFeedforward(units::meters_per_second_t leftVelocity,
units::meters_per_second_t rightVelocity) {
leftMotor.SetVoltage(feedforward.Calculate(leftVelocity));
rightMotor.SetVoltage(feedforward.Calculate(rightVelocity));
}
def tankDriveWithFeedforward(self, leftVelocity: float, rightVelocity: float):
self.leftMotor.setVoltage(feedForward.calculate(leftVelocity))
self.rightMotor.setVoltage(feedForward.calculate(rightVelocity))