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}