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  /** The static gain. */
014  public final double ks;
015
016  /** The velocity gain. */
017  public final double kv;
018
019  /** The acceleration gain. */
020  public final double ka;
021
022  /**
023   * Creates a new SimpleMotorFeedforward with the specified gains. Units of the gain values will
024   * dictate units of the computed feedforward.
025   *
026   * @param ks The static gain.
027   * @param kv The velocity gain.
028   * @param ka The acceleration gain.
029   * @throws IllegalArgumentException for kv < zero.
030   * @throws IllegalArgumentException for ka < zero.
031   */
032  public SimpleMotorFeedforward(double ks, double kv, double ka) {
033    this.ks = ks;
034    this.kv = kv;
035    this.ka = ka;
036    if (kv < 0.0) {
037      throw new IllegalArgumentException("kv must be a non-negative number, got " + kv + "!");
038    }
039    if (ka < 0.0) {
040      throw new IllegalArgumentException("ka must be a non-negative number, got " + ka + "!");
041    }
042  }
043
044  /**
045   * Creates a new SimpleMotorFeedforward with the specified gains. Acceleration gain is defaulted
046   * to zero. Units of the gain values will dictate units of the computed feedforward.
047   *
048   * @param ks The static gain.
049   * @param kv The velocity gain.
050   */
051  public SimpleMotorFeedforward(double ks, double kv) {
052    this(ks, kv, 0);
053  }
054
055  /**
056   * Calculates the feedforward from the gains and setpoints.
057   *
058   * @param velocity The velocity setpoint.
059   * @param acceleration The acceleration setpoint.
060   * @return The computed feedforward.
061   */
062  public double calculate(double velocity, double acceleration) {
063    return ks * Math.signum(velocity) + kv * velocity + ka * acceleration;
064  }
065
066  /**
067   * Calculates the feedforward from the gains and setpoints.
068   *
069   * @param currentVelocity The current velocity setpoint.
070   * @param nextVelocity The next velocity setpoint.
071   * @param dtSeconds Time between velocity setpoints in seconds.
072   * @return The computed feedforward.
073   */
074  public double calculate(double currentVelocity, double nextVelocity, double dtSeconds) {
075    var plant = LinearSystemId.identifyVelocitySystem(this.kv, this.ka);
076    var feedforward = new LinearPlantInversionFeedforward<>(plant, dtSeconds);
077
078    var r = MatBuilder.fill(Nat.N1(), Nat.N1(), currentVelocity);
079    var nextR = MatBuilder.fill(Nat.N1(), Nat.N1(), nextVelocity);
080
081    return ks * Math.signum(currentVelocity) + feedforward.calculate(r, nextR).get(0, 0);
082  }
083
084  // Rearranging the main equation from the calculate() method yields the
085  // formulas for the methods below:
086
087  /**
088   * Calculates the feedforward from the gains and velocity setpoint (acceleration is assumed to be
089   * zero).
090   *
091   * @param velocity The velocity setpoint.
092   * @return The computed feedforward.
093   */
094  public double calculate(double velocity) {
095    return calculate(velocity, 0);
096  }
097
098  /**
099   * Calculates the maximum achievable velocity given a maximum voltage supply and an acceleration.
100   * Useful for ensuring that velocity and acceleration constraints for a trapezoidal profile are
101   * simultaneously achievable - enter the acceleration constraint, and this will give you a
102   * simultaneously-achievable velocity constraint.
103   *
104   * @param maxVoltage The maximum voltage that can be supplied to the motor.
105   * @param acceleration The acceleration of the motor.
106   * @return The maximum possible velocity at the given acceleration.
107   */
108  public double maxAchievableVelocity(double maxVoltage, double acceleration) {
109    // Assume max velocity is positive
110    return (maxVoltage - ks - acceleration * ka) / kv;
111  }
112
113  /**
114   * Calculates the minimum achievable velocity given a maximum voltage supply and an acceleration.
115   * Useful for ensuring that velocity and acceleration constraints for a trapezoidal profile are
116   * simultaneously achievable - enter the acceleration constraint, and this will give you a
117   * simultaneously-achievable velocity constraint.
118   *
119   * @param maxVoltage The maximum voltage that can be supplied to the motor.
120   * @param acceleration The acceleration of the motor.
121   * @return The minimum possible velocity at the given acceleration.
122   */
123  public double minAchievableVelocity(double maxVoltage, double acceleration) {
124    // Assume min velocity is negative, ks flips sign
125    return (-maxVoltage + ks - acceleration * ka) / kv;
126  }
127
128  /**
129   * Calculates the maximum achievable acceleration given a maximum voltage supply and a velocity.
130   * Useful for ensuring that velocity and acceleration constraints for a trapezoidal profile are
131   * simultaneously achievable - enter the velocity constraint, and this will give you a
132   * simultaneously-achievable acceleration constraint.
133   *
134   * @param maxVoltage The maximum voltage that can be supplied to the motor.
135   * @param velocity The velocity of the motor.
136   * @return The maximum possible acceleration at the given velocity.
137   */
138  public double maxAchievableAcceleration(double maxVoltage, double velocity) {
139    return (maxVoltage - ks * Math.signum(velocity) - velocity * kv) / ka;
140  }
141
142  /**
143   * Calculates the minimum achievable acceleration given a maximum voltage supply and a velocity.
144   * Useful for ensuring that velocity and acceleration constraints for a trapezoidal profile are
145   * simultaneously achievable - enter the velocity constraint, and this will give you a
146   * simultaneously-achievable acceleration constraint.
147   *
148   * @param maxVoltage The maximum voltage that can be supplied to the motor.
149   * @param velocity The velocity of the motor.
150   * @return The minimum possible acceleration at the given velocity.
151   */
152  public double minAchievableAcceleration(double maxVoltage, double velocity) {
153    return maxAchievableAcceleration(-maxVoltage, velocity);
154  }
155}