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.controller.proto.SimpleMotorFeedforwardProto; 008import edu.wpi.first.math.controller.struct.SimpleMotorFeedforwardStruct; 009import edu.wpi.first.util.protobuf.ProtobufSerializable; 010import edu.wpi.first.util.struct.StructSerializable; 011 012/** A helper class that computes feedforward outputs for a simple permanent-magnet DC motor. */ 013public class SimpleMotorFeedforward implements ProtobufSerializable, StructSerializable { 014 /** The static gain, in volts. */ 015 private double ks; 016 017 /** The velocity gain, in V/(units/s). */ 018 private double kv; 019 020 /** The acceleration gain, in V/(units/s²). */ 021 private double ka; 022 023 /** The period, in seconds. */ 024 private final double m_dt; 025 026 /** 027 * Creates a new SimpleMotorFeedforward with the specified gains and period. 028 * 029 * <p>The units should be radians for angular systems and meters for linear systems. 030 * 031 * @param ks The static gain in volts. 032 * @param kv The velocity gain in V/(units/s). 033 * @param ka The acceleration gain in V/(units/s²). 034 * @param dt The period in seconds. 035 * @throws IllegalArgumentException for kv < zero. 036 * @throws IllegalArgumentException for ka < zero. 037 * @throws IllegalArgumentException for period ≤ zero. 038 */ 039 public SimpleMotorFeedforward(double ks, double kv, double ka, double dt) { 040 this.ks = ks; 041 this.kv = kv; 042 this.ka = ka; 043 if (kv < 0.0) { 044 throw new IllegalArgumentException("kv must be a non-negative number, got " + kv + "!"); 045 } 046 if (ka < 0.0) { 047 throw new IllegalArgumentException("ka must be a non-negative number, got " + ka + "!"); 048 } 049 if (dt <= 0.0) { 050 throw new IllegalArgumentException("period must be a positive number, got " + dt + "!"); 051 } 052 m_dt = dt; 053 } 054 055 /** 056 * Creates a new SimpleMotorFeedforward with the specified gains and period. The period is 057 * defaulted to 20 ms. 058 * 059 * <p>The units should be radians for angular systems and meters for linear systems. 060 * 061 * @param ks The static gain in volts. 062 * @param kv The velocity gain in V/(units/s). 063 * @param ka The acceleration gain in V/(units/s²). 064 * @throws IllegalArgumentException for kv < zero. 065 * @throws IllegalArgumentException for ka < zero. 066 */ 067 public SimpleMotorFeedforward(double ks, double kv, double ka) { 068 this(ks, kv, ka, 0.020); 069 } 070 071 /** 072 * Creates a new SimpleMotorFeedforward with the specified gains. Acceleration gain is defaulted 073 * to zero. The period is defaulted to 20 ms. 074 * 075 * <p>The units should be radians for angular systems and meters for linear systems. 076 * 077 * @param ks The static gain in volts. 078 * @param kv The velocity gain in V/(units/s). 079 * @throws IllegalArgumentException for kv < zero. 080 */ 081 public SimpleMotorFeedforward(double ks, double kv) { 082 this(ks, kv, 0); 083 } 084 085 /** 086 * Sets the static gain. 087 * 088 * @param ks The static gain in volts. 089 */ 090 public void setKs(double ks) { 091 this.ks = ks; 092 } 093 094 /** 095 * Sets the velocity gain. 096 * 097 * @param kv The velocity gain in V/(units/s). 098 */ 099 public void setKv(double kv) { 100 this.kv = kv; 101 } 102 103 /** 104 * Sets the acceleration gain. 105 * 106 * @param ka The acceleration gain in V/(units/s²). 107 */ 108 public void setKa(double ka) { 109 this.ka = ka; 110 } 111 112 /** 113 * Returns the static gain in volts. 114 * 115 * @return The static gain in volts. 116 */ 117 public double getKs() { 118 return ks; 119 } 120 121 /** 122 * Returns the velocity gain in V/(units/s). 123 * 124 * <p>The units should be radians for angular systems and meters for linear systems. 125 * 126 * @return The velocity gain in V/(units/s). 127 */ 128 public double getKv() { 129 return kv; 130 } 131 132 /** 133 * Returns the acceleration gain in V/(units/s²). 134 * 135 * <p>The units should be radians for angular systems and meters for linear systems. 136 * 137 * @return The acceleration gain in V/(units/s²). 138 */ 139 public double getKa() { 140 return ka; 141 } 142 143 /** 144 * Returns the period in seconds. 145 * 146 * @return The period in seconds. 147 */ 148 public double getDt() { 149 return m_dt; 150 } 151 152 /** 153 * Calculates the feedforward from the gains and velocity setpoint assuming continuous control 154 * (acceleration is assumed to be zero). 155 * 156 * @param velocity The velocity setpoint. 157 * @return The computed feedforward. 158 */ 159 public double calculate(double velocity) { 160 return calculate(velocity, velocity); 161 } 162 163 /** 164 * Calculates the feedforward from the gains and setpoints assuming discrete control. 165 * 166 * <p>Note this method is inaccurate when the velocity crosses 0. 167 * 168 * @param currentVelocity The current velocity setpoint. 169 * @param nextVelocity The next velocity setpoint. 170 * @return The computed feedforward. 171 */ 172 public double calculate(double currentVelocity, double nextVelocity) { 173 // See wpimath/algorithms.md#Simple_motor_feedforward for derivation 174 if (ka < 1e-9) { 175 return ks * Math.signum(nextVelocity) + kv * nextVelocity; 176 } else { 177 double A = -kv / ka; 178 double B = 1.0 / ka; 179 double A_d = Math.exp(A * m_dt); 180 double B_d = A > -1e-9 ? B * m_dt : 1.0 / A * (A_d - 1.0) * B; 181 return ks * Math.signum(currentVelocity) + 1.0 / B_d * (nextVelocity - A_d * currentVelocity); 182 } 183 } 184 185 /** 186 * Calculates the maximum achievable velocity given a maximum voltage supply and an acceleration. 187 * Useful for ensuring that velocity and acceleration constraints for a trapezoidal profile are 188 * simultaneously achievable - enter the acceleration constraint, and this will give you a 189 * simultaneously-achievable velocity constraint. 190 * 191 * <p>The units should be radians for angular systems and meters for linear systems. 192 * 193 * @param maxVoltage The maximum voltage that can be supplied to the motor, in volts. 194 * @param acceleration The acceleration of the motor, in (units/s²). 195 * @return The maximum possible velocity in (units/s) at the given acceleration. 196 */ 197 public double maxAchievableVelocity(double maxVoltage, double acceleration) { 198 // Assume max velocity is positive 199 return (maxVoltage - ks - acceleration * ka) / kv; 200 } 201 202 /** 203 * Calculates the minimum achievable velocity given a maximum voltage supply and an acceleration. 204 * Useful for ensuring that velocity and acceleration constraints for a trapezoidal profile are 205 * simultaneously achievable - enter the acceleration constraint, and this will give you a 206 * simultaneously-achievable velocity constraint. 207 * 208 * <p>The units should be radians for angular systems and meters for linear systems. 209 * 210 * @param maxVoltage The maximum voltage that can be supplied to the motor, in volts. 211 * @param acceleration The acceleration of the motor, in (units/s²). 212 * @return The maximum possible velocity in (units/s) at the given acceleration. 213 */ 214 public double minAchievableVelocity(double maxVoltage, double acceleration) { 215 // Assume min velocity is negative, ks flips sign 216 return (-maxVoltage + ks - acceleration * ka) / kv; 217 } 218 219 /** 220 * Calculates the maximum achievable acceleration given a maximum voltage supply and a velocity. 221 * Useful for ensuring that velocity and acceleration constraints for a trapezoidal profile are 222 * simultaneously achievable - enter the velocity constraint, and this will give you a 223 * simultaneously-achievable acceleration constraint. 224 * 225 * <p>The units should be radians for angular systems and meters for linear systems. 226 * 227 * @param maxVoltage The maximum voltage that can be supplied to the motor, in volts. 228 * @param velocity The velocity of the motor, in (units/s). 229 * @return The maximum possible acceleration in (units/s²) at the given velocity. 230 */ 231 public double maxAchievableAcceleration(double maxVoltage, double velocity) { 232 return (maxVoltage - ks * Math.signum(velocity) - velocity * kv) / ka; 233 } 234 235 /** 236 * Calculates the minimum achievable acceleration given a maximum voltage supply and a velocity. 237 * Useful for ensuring that velocity and acceleration constraints for a trapezoidal profile are 238 * simultaneously achievable - enter the velocity constraint, and this will give you a 239 * simultaneously-achievable acceleration constraint. 240 * 241 * <p>The units should be radians for angular systems and meters for linear systems. 242 * 243 * @param maxVoltage The maximum voltage that can be supplied to the motor, in volts. 244 * @param velocity The velocity of the motor, in (units/s). 245 * @return The maximum possible acceleration in (units/s²) at the given velocity. 246 */ 247 public double minAchievableAcceleration(double maxVoltage, double velocity) { 248 return maxAchievableAcceleration(-maxVoltage, velocity); 249 } 250 251 /** SimpleMotorFeedforward struct for serialization. */ 252 public static final SimpleMotorFeedforwardStruct struct = new SimpleMotorFeedforwardStruct(); 253 254 /** SimpleMotorFeedforward protobuf for serialization. */ 255 public static final SimpleMotorFeedforwardProto proto = new SimpleMotorFeedforwardProto(); 256}