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.ArmFeedforwardProto; 008import edu.wpi.first.math.controller.struct.ArmFeedforwardStruct; 009import edu.wpi.first.util.protobuf.ProtobufSerializable; 010import edu.wpi.first.util.struct.StructSerializable; 011 012/** 013 * A helper class that computes feedforward outputs for a simple arm (modeled as a motor acting 014 * against the force of gravity on a beam suspended at an angle). 015 */ 016public class ArmFeedforward implements ProtobufSerializable, StructSerializable { 017 /** The static gain, in volts. */ 018 public final double ks; 019 020 /** The gravity gain, in volts. */ 021 public final double kg; 022 023 /** The velocity gain, in volt seconds per radian. */ 024 public final double kv; 025 026 /** The acceleration gain, in volt secondsĀ² per radian. */ 027 public final double ka; 028 029 /** Arm feedforward protobuf for serialization. */ 030 public static final ArmFeedforwardProto proto = new ArmFeedforwardProto(); 031 032 /** Arm feedforward struct for serialization. */ 033 public static final ArmFeedforwardStruct struct = new ArmFeedforwardStruct(); 034 035 /** 036 * Creates a new ArmFeedforward with the specified gains. Units of the gain values will dictate 037 * units of the computed feedforward. 038 * 039 * @param ks The static gain. 040 * @param kg The gravity gain. 041 * @param kv The velocity gain. 042 * @param ka The acceleration gain. 043 * @throws IllegalArgumentException for kv < zero. 044 * @throws IllegalArgumentException for ka < zero. 045 */ 046 public ArmFeedforward(double ks, double kg, double kv, double ka) { 047 this.ks = ks; 048 this.kg = kg; 049 this.kv = kv; 050 this.ka = ka; 051 if (kv < 0.0) { 052 throw new IllegalArgumentException("kv must be a non-negative number, got " + kv + "!"); 053 } 054 if (ka < 0.0) { 055 throw new IllegalArgumentException("ka must be a non-negative number, got " + ka + "!"); 056 } 057 } 058 059 /** 060 * Creates a new ArmFeedforward with the specified gains. Acceleration gain is defaulted to zero. 061 * Units of the gain values will dictate units of the computed feedforward. 062 * 063 * @param ks The static gain. 064 * @param kg The gravity gain. 065 * @param kv The velocity gain. 066 */ 067 public ArmFeedforward(double ks, double kg, double kv) { 068 this(ks, kg, kv, 0); 069 } 070 071 /** 072 * Calculates the feedforward from the gains and setpoints. 073 * 074 * @param positionRadians The position (angle) setpoint. This angle should be measured from the 075 * horizontal (i.e. if the provided angle is 0, the arm should be parallel with the floor). If 076 * your encoder does not follow this convention, an offset should be added. 077 * @param velocityRadPerSec The velocity setpoint. 078 * @param accelRadPerSecSquared The acceleration setpoint. 079 * @return The computed feedforward. 080 */ 081 public double calculate( 082 double positionRadians, double velocityRadPerSec, double accelRadPerSecSquared) { 083 return ks * Math.signum(velocityRadPerSec) 084 + kg * Math.cos(positionRadians) 085 + kv * velocityRadPerSec 086 + ka * accelRadPerSecSquared; 087 } 088 089 /** 090 * Calculates the feedforward from the gains and velocity setpoint (acceleration is assumed to be 091 * zero). 092 * 093 * @param positionRadians The position (angle) setpoint. This angle should be measured from the 094 * horizontal (i.e. if the provided angle is 0, the arm should be parallel with the floor). If 095 * your encoder does not follow this convention, an offset should be added. 096 * @param velocity The velocity setpoint. 097 * @return The computed feedforward. 098 */ 099 public double calculate(double positionRadians, double velocity) { 100 return calculate(positionRadians, velocity, 0); 101 } 102 103 // Rearranging the main equation from the calculate() method yields the 104 // formulas for the methods below: 105 106 /** 107 * Calculates the maximum achievable velocity given a maximum voltage supply, a position, and an 108 * acceleration. Useful for ensuring that velocity and acceleration constraints for a trapezoidal 109 * profile are simultaneously achievable - enter the acceleration constraint, and this will give 110 * you a simultaneously-achievable velocity constraint. 111 * 112 * @param maxVoltage The maximum voltage that can be supplied to the arm. 113 * @param angle The angle of the arm. This angle should be measured from the horizontal (i.e. if 114 * the provided angle is 0, the arm should be parallel with the floor). If your encoder does 115 * not follow this convention, an offset should be added. 116 * @param acceleration The acceleration of the arm. 117 * @return The maximum possible velocity at the given acceleration and angle. 118 */ 119 public double maxAchievableVelocity(double maxVoltage, double angle, double acceleration) { 120 // Assume max velocity is positive 121 return (maxVoltage - ks - Math.cos(angle) * kg - acceleration * ka) / kv; 122 } 123 124 /** 125 * Calculates the minimum achievable velocity given a maximum voltage supply, a position, and an 126 * acceleration. Useful for ensuring that velocity and acceleration constraints for a trapezoidal 127 * profile are simultaneously achievable - enter the acceleration constraint, and this will give 128 * you a simultaneously-achievable velocity constraint. 129 * 130 * @param maxVoltage The maximum voltage that can be supplied to the arm. 131 * @param angle The angle of the arm. This angle should be measured from the horizontal (i.e. if 132 * the provided angle is 0, the arm should be parallel with the floor). If your encoder does 133 * not follow this convention, an offset should be added. 134 * @param acceleration The acceleration of the arm. 135 * @return The minimum possible velocity at the given acceleration and angle. 136 */ 137 public double minAchievableVelocity(double maxVoltage, double angle, double acceleration) { 138 // Assume min velocity is negative, ks flips sign 139 return (-maxVoltage + ks - Math.cos(angle) * kg - acceleration * ka) / kv; 140 } 141 142 /** 143 * Calculates the maximum achievable acceleration given a maximum voltage supply, a position, and 144 * a velocity. Useful for ensuring that velocity and acceleration constraints for a trapezoidal 145 * profile are 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 arm. 149 * @param angle The angle of the arm. This angle should be measured from the horizontal (i.e. if 150 * the provided angle is 0, the arm should be parallel with the floor). If your encoder does 151 * not follow this convention, an offset should be added. 152 * @param velocity The velocity of the arm. 153 * @return The maximum possible acceleration at the given velocity. 154 */ 155 public double maxAchievableAcceleration(double maxVoltage, double angle, double velocity) { 156 return (maxVoltage - ks * Math.signum(velocity) - Math.cos(angle) * kg - velocity * kv) / ka; 157 } 158 159 /** 160 * Calculates the minimum achievable acceleration given a maximum voltage supply, a position, and 161 * a velocity. Useful for ensuring that velocity and acceleration constraints for a trapezoidal 162 * profile are simultaneously achievable - enter the velocity constraint, and this will give you a 163 * simultaneously-achievable acceleration constraint. 164 * 165 * @param maxVoltage The maximum voltage that can be supplied to the arm. 166 * @param angle The angle of the arm. This angle should be measured from the horizontal (i.e. if 167 * the provided angle is 0, the arm should be parallel with the floor). If your encoder does 168 * not follow this convention, an offset should be added. 169 * @param velocity The velocity of the arm. 170 * @return The minimum possible acceleration at the given velocity. 171 */ 172 public double minAchievableAcceleration(double maxVoltage, double angle, double velocity) { 173 return maxAchievableAcceleration(-maxVoltage, angle, velocity); 174 } 175}