001// Copyright (c) FIRST and other WPILib contributors.
002// Open Source Software; you can modify and/or share it under the terms of
003// the WPILib BSD license file in the root directory of this project.
004
005package edu.wpi.first.math.controller;
006
007import edu.wpi.first.math.MatBuilder;
008import edu.wpi.first.math.Nat;
009import edu.wpi.first.math.system.plant.LinearSystemId;
010
011/** A helper class that computes feedforward outputs for a simple permanent-magnet DC motor. */
012public class SimpleMotorFeedforward {
013  public final double ks;
014  public final double kv;
015  public final double ka;
016
017  /**
018   * Creates a new SimpleMotorFeedforward with the specified gains. Units of the gain values will
019   * dictate units of the computed feedforward.
020   *
021   * @param ks The static gain.
022   * @param kv The velocity gain.
023   * @param ka The acceleration gain.
024   */
025  public SimpleMotorFeedforward(double ks, double kv, double ka) {
026    this.ks = ks;
027    this.kv = kv;
028    this.ka = ka;
029  }
030
031  /**
032   * Creates a new SimpleMotorFeedforward with the specified gains. Acceleration gain is defaulted
033   * to zero. Units of the gain values will dictate units of the computed feedforward.
034   *
035   * @param ks The static gain.
036   * @param kv The velocity gain.
037   */
038  public SimpleMotorFeedforward(double ks, double kv) {
039    this(ks, kv, 0);
040  }
041
042  /**
043   * Calculates the feedforward from the gains and setpoints.
044   *
045   * @param velocity The velocity setpoint.
046   * @param acceleration The acceleration setpoint.
047   * @return The computed feedforward.
048   */
049  public double calculate(double velocity, double acceleration) {
050    return ks * Math.signum(velocity) + kv * velocity + ka * acceleration;
051  }
052
053  /**
054   * Calculates the feedforward from the gains and setpoints.
055   *
056   * @param currentVelocity The current velocity setpoint.
057   * @param nextVelocity The next velocity setpoint.
058   * @param dtSeconds Time between velocity setpoints in seconds.
059   * @return The computed feedforward.
060   */
061  public double calculate(double currentVelocity, double nextVelocity, double dtSeconds) {
062    var plant = LinearSystemId.identifyVelocitySystem(this.kv, this.ka);
063    var feedforward = new LinearPlantInversionFeedforward<>(plant, dtSeconds);
064
065    var r = MatBuilder.fill(Nat.N1(), Nat.N1(), currentVelocity);
066    var nextR = MatBuilder.fill(Nat.N1(), Nat.N1(), nextVelocity);
067
068    return ks * Math.signum(currentVelocity) + feedforward.calculate(r, nextR).get(0, 0);
069  }
070
071  // Rearranging the main equation from the calculate() method yields the
072  // formulas for the methods below:
073
074  /**
075   * Calculates the feedforward from the gains and velocity setpoint (acceleration is assumed to be
076   * zero).
077   *
078   * @param velocity The velocity setpoint.
079   * @return The computed feedforward.
080   */
081  public double calculate(double velocity) {
082    return calculate(velocity, 0);
083  }
084
085  /**
086   * Calculates the maximum achievable velocity given a maximum voltage supply and an acceleration.
087   * Useful for ensuring that velocity and acceleration constraints for a trapezoidal profile are
088   * simultaneously achievable - enter the acceleration constraint, and this will give you a
089   * simultaneously-achievable velocity constraint.
090   *
091   * @param maxVoltage The maximum voltage that can be supplied to the motor.
092   * @param acceleration The acceleration of the motor.
093   * @return The maximum possible velocity at the given acceleration.
094   */
095  public double maxAchievableVelocity(double maxVoltage, double acceleration) {
096    // Assume max velocity is positive
097    return (maxVoltage - ks - acceleration * ka) / kv;
098  }
099
100  /**
101   * Calculates the minimum achievable velocity given a maximum voltage supply and an acceleration.
102   * Useful for ensuring that velocity and acceleration constraints for a trapezoidal profile are
103   * simultaneously achievable - enter the acceleration constraint, and this will give you a
104   * simultaneously-achievable velocity constraint.
105   *
106   * @param maxVoltage The maximum voltage that can be supplied to the motor.
107   * @param acceleration The acceleration of the motor.
108   * @return The minimum possible velocity at the given acceleration.
109   */
110  public double minAchievableVelocity(double maxVoltage, double acceleration) {
111    // Assume min velocity is negative, ks flips sign
112    return (-maxVoltage + ks - acceleration * ka) / kv;
113  }
114
115  /**
116   * Calculates the maximum achievable acceleration given a maximum voltage supply and a velocity.
117   * Useful for ensuring that velocity and acceleration constraints for a trapezoidal profile are
118   * simultaneously achievable - enter the velocity constraint, and this will give you a
119   * simultaneously-achievable acceleration constraint.
120   *
121   * @param maxVoltage The maximum voltage that can be supplied to the motor.
122   * @param velocity The velocity of the motor.
123   * @return The maximum possible acceleration at the given velocity.
124   */
125  public double maxAchievableAcceleration(double maxVoltage, double velocity) {
126    return (maxVoltage - ks * Math.signum(velocity) - velocity * kv) / ka;
127  }
128
129  /**
130   * Calculates the minimum achievable acceleration given a maximum voltage supply and a velocity.
131   * Useful for ensuring that velocity and acceleration constraints for a trapezoidal profile are
132   * simultaneously achievable - enter the velocity constraint, and this will give you a
133   * simultaneously-achievable acceleration constraint.
134   *
135   * @param maxVoltage The maximum voltage that can be supplied to the motor.
136   * @param velocity The velocity of the motor.
137   * @return The minimum possible acceleration at the given velocity.
138   */
139  public double minAchievableAcceleration(double maxVoltage, double velocity) {
140    return maxAchievableAcceleration(-maxVoltage, velocity);
141  }
142}