2020-12-26 14:12:05 -08:00
|
|
|
// Copyright (c) FIRST and other WPILib contributors.
|
|
|
|
|
// Open Source Software; you can modify and/or share it under the terms of
|
|
|
|
|
// the WPILib BSD license file in the root directory of this project.
|
2019-11-09 23:16:42 -05:00
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
2019-11-19 06:47:59 -08:00
|
|
|
#include <wpi/MathExtras.h>
|
2021-08-20 09:02:01 -07:00
|
|
|
#include <wpi/SymbolExports.h>
|
2019-11-09 23:16:42 -05:00
|
|
|
|
2020-08-06 23:57:39 -07:00
|
|
|
#include "units/angle.h"
|
|
|
|
|
#include "units/angular_velocity.h"
|
|
|
|
|
#include "units/math.h"
|
|
|
|
|
#include "units/voltage.h"
|
|
|
|
|
|
2019-11-09 23:16:42 -05:00
|
|
|
namespace frc {
|
|
|
|
|
/**
|
|
|
|
|
* A helper class that computes feedforward outputs for a simple arm (modeled as
|
|
|
|
|
* a motor acting against the force of gravity on a beam suspended at an angle).
|
|
|
|
|
*/
|
2021-08-20 09:02:01 -07:00
|
|
|
class WPILIB_DLLEXPORT ArmFeedforward {
|
2020-01-06 23:30:47 -08:00
|
|
|
public:
|
2019-11-09 23:16:42 -05:00
|
|
|
using Angle = units::radians;
|
|
|
|
|
using Velocity = units::radians_per_second;
|
|
|
|
|
using Acceleration = units::compound_unit<units::radians_per_second,
|
|
|
|
|
units::inverse<units::second>>;
|
|
|
|
|
using kv_unit =
|
|
|
|
|
units::compound_unit<units::volts,
|
|
|
|
|
units::inverse<units::radians_per_second>>;
|
|
|
|
|
using ka_unit =
|
|
|
|
|
units::compound_unit<units::volts, units::inverse<Acceleration>>;
|
|
|
|
|
|
2019-11-19 06:47:59 -08:00
|
|
|
constexpr ArmFeedforward() = default;
|
2019-11-09 23:16:42 -05:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Creates a new ArmFeedforward with the specified gains.
|
|
|
|
|
*
|
|
|
|
|
* @param kS The static gain, in volts.
|
2022-01-31 03:16:26 -05:00
|
|
|
* @param kG The gravity gain, in volts.
|
2019-11-09 23:16:42 -05:00
|
|
|
* @param kV The velocity gain, in volt seconds per radian.
|
2022-05-20 15:16:56 -07:00
|
|
|
* @param kA The acceleration gain, in volt seconds² per radian.
|
2019-11-09 23:16:42 -05:00
|
|
|
*/
|
2019-11-19 06:47:59 -08:00
|
|
|
constexpr ArmFeedforward(
|
2022-01-31 03:16:26 -05:00
|
|
|
units::volt_t kS, units::volt_t kG, units::unit_t<kv_unit> kV,
|
2019-11-19 06:47:59 -08:00
|
|
|
units::unit_t<ka_unit> kA = units::unit_t<ka_unit>(0))
|
2022-01-31 03:16:26 -05:00
|
|
|
: kS(kS), kG(kG), kV(kV), kA(kA) {}
|
2019-11-09 23:16:42 -05:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Calculates the feedforward from the gains and setpoints.
|
|
|
|
|
*
|
2022-06-02 21:22:47 -07:00
|
|
|
* @param angle The angle setpoint, in radians. This angle should be
|
|
|
|
|
* measured from the horizontal (i.e. if the provided
|
|
|
|
|
* angle is 0, the arm should be parallel to the floor).
|
|
|
|
|
* If your encoder does not follow this convention, an
|
|
|
|
|
* offset should be added.
|
2019-11-09 23:16:42 -05:00
|
|
|
* @param velocity The velocity setpoint, in radians per second.
|
2022-05-20 15:16:56 -07:00
|
|
|
* @param acceleration The acceleration setpoint, in radians per second².
|
2019-11-09 23:16:42 -05:00
|
|
|
* @return The computed feedforward, in volts.
|
|
|
|
|
*/
|
|
|
|
|
units::volt_t Calculate(units::unit_t<Angle> angle,
|
|
|
|
|
units::unit_t<Velocity> velocity,
|
|
|
|
|
units::unit_t<Acceleration> acceleration =
|
2019-11-19 06:47:59 -08:00
|
|
|
units::unit_t<Acceleration>(0)) const {
|
2022-01-31 03:16:26 -05:00
|
|
|
return kS * wpi::sgn(velocity) + kG * units::math::cos(angle) +
|
2019-11-22 00:43:02 -05:00
|
|
|
kV * velocity + kA * acceleration;
|
2019-11-19 06:47:59 -08:00
|
|
|
}
|
2019-11-09 23:16:42 -05:00
|
|
|
|
2019-11-22 00:43:02 -05:00
|
|
|
// Rearranging the main equation from the calculate() method yields the
|
|
|
|
|
// formulas for the methods below:
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Calculates the maximum achievable velocity given a maximum voltage supply,
|
|
|
|
|
* a position, and an acceleration. Useful for ensuring that velocity and
|
|
|
|
|
* acceleration constraints for a trapezoidal profile are simultaneously
|
|
|
|
|
* achievable - enter the acceleration constraint, and this will give you
|
|
|
|
|
* a simultaneously-achievable velocity constraint.
|
|
|
|
|
*
|
2022-06-02 21:22:47 -07:00
|
|
|
* @param maxVoltage The maximum voltage that can be supplied to the arm.
|
|
|
|
|
* @param angle The angle of the arm. This angle should be measured
|
|
|
|
|
* from the horizontal (i.e. if the provided angle is 0,
|
|
|
|
|
* the arm should be parallel to the floor). If your
|
|
|
|
|
* encoder does not follow this convention, an offset
|
|
|
|
|
* should be added.
|
2019-11-22 00:43:02 -05:00
|
|
|
* @param acceleration The acceleration of the arm.
|
|
|
|
|
* @return The maximum possible velocity at the given acceleration and angle.
|
|
|
|
|
*/
|
|
|
|
|
units::unit_t<Velocity> MaxAchievableVelocity(
|
2019-11-22 06:31:32 -08:00
|
|
|
units::volt_t maxVoltage, units::unit_t<Angle> angle,
|
2019-11-22 00:43:02 -05:00
|
|
|
units::unit_t<Acceleration> acceleration) {
|
|
|
|
|
// Assume max velocity is positive
|
2022-01-31 03:16:26 -05:00
|
|
|
return (maxVoltage - kS - kG * units::math::cos(angle) -
|
2019-11-22 00:43:02 -05:00
|
|
|
kA * acceleration) /
|
|
|
|
|
kV;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Calculates the minimum achievable velocity given a maximum voltage supply,
|
|
|
|
|
* a position, and an acceleration. Useful for ensuring that velocity and
|
|
|
|
|
* acceleration constraints for a trapezoidal profile are simultaneously
|
|
|
|
|
* achievable - enter the acceleration constraint, and this will give you
|
|
|
|
|
* a simultaneously-achievable velocity constraint.
|
|
|
|
|
*
|
2022-06-02 21:22:47 -07:00
|
|
|
* @param maxVoltage The maximum voltage that can be supplied to the arm.
|
|
|
|
|
* @param angle The angle of the arm. This angle should be measured
|
|
|
|
|
* from the horizontal (i.e. if the provided angle is 0,
|
|
|
|
|
* the arm should be parallel to the floor). If your
|
|
|
|
|
* encoder does not follow this convention, an offset
|
|
|
|
|
* should be added.
|
2019-11-22 00:43:02 -05:00
|
|
|
* @param acceleration The acceleration of the arm.
|
|
|
|
|
* @return The minimum possible velocity at the given acceleration and angle.
|
|
|
|
|
*/
|
|
|
|
|
units::unit_t<Velocity> MinAchievableVelocity(
|
2019-11-22 06:31:32 -08:00
|
|
|
units::volt_t maxVoltage, units::unit_t<Angle> angle,
|
2019-11-22 00:43:02 -05:00
|
|
|
units::unit_t<Acceleration> acceleration) {
|
|
|
|
|
// Assume min velocity is negative, ks flips sign
|
2022-01-31 03:16:26 -05:00
|
|
|
return (-maxVoltage + kS - kG * units::math::cos(angle) -
|
2019-11-22 00:43:02 -05:00
|
|
|
kA * acceleration) /
|
|
|
|
|
kV;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Calculates the maximum achievable acceleration given a maximum voltage
|
|
|
|
|
* supply, a position, and a velocity. Useful for ensuring that velocity and
|
|
|
|
|
* acceleration constraints for a trapezoidal profile are simultaneously
|
|
|
|
|
* achievable - enter the velocity constraint, and this will give you
|
|
|
|
|
* a simultaneously-achievable acceleration constraint.
|
|
|
|
|
*
|
|
|
|
|
* @param maxVoltage The maximum voltage that can be supplied to the arm.
|
2022-06-02 21:22:47 -07:00
|
|
|
* @param angle The angle of the arm. This angle should be measured
|
|
|
|
|
* from the horizontal (i.e. if the provided angle is 0,
|
|
|
|
|
* the arm should be parallel to the floor). If your
|
|
|
|
|
* encoder does not follow this convention, an offset
|
|
|
|
|
* should be added.
|
|
|
|
|
* @param velocity The velocity of the arm.
|
2019-11-22 00:43:02 -05:00
|
|
|
* @return The maximum possible acceleration at the given velocity and angle.
|
|
|
|
|
*/
|
|
|
|
|
units::unit_t<Acceleration> MaxAchievableAcceleration(
|
2019-11-22 06:31:32 -08:00
|
|
|
units::volt_t maxVoltage, units::unit_t<Angle> angle,
|
|
|
|
|
units::unit_t<Velocity> velocity) {
|
2019-11-22 00:43:02 -05:00
|
|
|
return (maxVoltage - kS * wpi::sgn(velocity) -
|
2022-01-31 03:16:26 -05:00
|
|
|
kG * units::math::cos(angle) - kV * velocity) /
|
2019-11-22 00:43:02 -05:00
|
|
|
kA;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Calculates the minimum achievable acceleration given a maximum voltage
|
|
|
|
|
* supply, a position, and a velocity. Useful for ensuring that velocity and
|
|
|
|
|
* acceleration constraints for a trapezoidal profile are simultaneously
|
|
|
|
|
* achievable - enter the velocity constraint, and this will give you
|
|
|
|
|
* a simultaneously-achievable acceleration constraint.
|
|
|
|
|
*
|
|
|
|
|
* @param maxVoltage The maximum voltage that can be supplied to the arm.
|
2022-06-02 21:22:47 -07:00
|
|
|
* @param angle The angle of the arm. This angle should be measured
|
|
|
|
|
* from the horizontal (i.e. if the provided angle is 0,
|
|
|
|
|
* the arm should be parallel to the floor). If your
|
|
|
|
|
* encoder does not follow this convention, an offset
|
|
|
|
|
* should be added.
|
|
|
|
|
* @param velocity The velocity of the arm.
|
2019-11-22 00:43:02 -05:00
|
|
|
* @return The minimum possible acceleration at the given velocity and angle.
|
|
|
|
|
*/
|
|
|
|
|
units::unit_t<Acceleration> MinAchievableAcceleration(
|
2019-11-22 06:31:32 -08:00
|
|
|
units::volt_t maxVoltage, units::unit_t<Angle> angle,
|
|
|
|
|
units::unit_t<Velocity> velocity) {
|
2019-11-22 00:43:02 -05:00
|
|
|
return MaxAchievableAcceleration(-maxVoltage, angle, velocity);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
units::volt_t kS{0};
|
2022-01-31 03:16:26 -05:00
|
|
|
units::volt_t kG{0};
|
2019-11-22 00:43:02 -05:00
|
|
|
units::unit_t<kv_unit> kV{0};
|
|
|
|
|
units::unit_t<ka_unit> kA{0};
|
2019-11-09 23:16:42 -05:00
|
|
|
};
|
|
|
|
|
} // namespace frc
|