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 007/** 008 * A helper class that computes feedforward outputs for a simple arm (modeled as a motor acting 009 * against the force of gravity on a beam suspended at an angle). 010 */ 011@SuppressWarnings("MemberName") 012public class ArmFeedforward { 013 public final double ks; 014 public final double kcos; 015 public final double kv; 016 public final double ka; 017 018 /** 019 * Creates a new ArmFeedforward with the specified gains. Units of the gain values will dictate 020 * units of the computed feedforward. 021 * 022 * @param ks The static gain. 023 * @param kcos The gravity gain. 024 * @param kv The velocity gain. 025 * @param ka The acceleration gain. 026 */ 027 public ArmFeedforward(double ks, double kcos, double kv, double ka) { 028 this.ks = ks; 029 this.kcos = kcos; 030 this.kv = kv; 031 this.ka = ka; 032 } 033 034 /** 035 * Creates a new ArmFeedforward with the specified gains. Acceleration gain is defaulted to zero. 036 * Units of the gain values will dictate units of the computed feedforward. 037 * 038 * @param ks The static gain. 039 * @param kcos The gravity gain. 040 * @param kv The velocity gain. 041 */ 042 public ArmFeedforward(double ks, double kcos, double kv) { 043 this(ks, kcos, kv, 0); 044 } 045 046 /** 047 * Calculates the feedforward from the gains and setpoints. 048 * 049 * @param positionRadians The position (angle) setpoint. 050 * @param velocityRadPerSec The velocity setpoint. 051 * @param accelRadPerSecSquared The acceleration setpoint. 052 * @return The computed feedforward. 053 */ 054 public double calculate( 055 double positionRadians, double velocityRadPerSec, double accelRadPerSecSquared) { 056 return ks * Math.signum(velocityRadPerSec) 057 + kcos * Math.cos(positionRadians) 058 + kv * velocityRadPerSec 059 + ka * accelRadPerSecSquared; 060 } 061 062 /** 063 * Calculates the feedforward from the gains and velocity setpoint (acceleration is assumed to be 064 * zero). 065 * 066 * @param positionRadians The position (angle) setpoint. 067 * @param velocity The velocity setpoint. 068 * @return The computed feedforward. 069 */ 070 public double calculate(double positionRadians, double velocity) { 071 return calculate(positionRadians, velocity, 0); 072 } 073 074 // Rearranging the main equation from the calculate() method yields the 075 // formulas for the methods below: 076 077 /** 078 * Calculates the maximum achievable velocity given a maximum voltage supply, a position, and an 079 * acceleration. Useful for ensuring that velocity and acceleration constraints for a trapezoidal 080 * profile are simultaneously achievable - enter the acceleration constraint, and this will give 081 * you a simultaneously-achievable velocity constraint. 082 * 083 * @param maxVoltage The maximum voltage that can be supplied to the arm. 084 * @param angle The angle of the arm. 085 * @param acceleration The acceleration of the arm. 086 * @return The maximum possible velocity at the given acceleration and angle. 087 */ 088 public double maxAchievableVelocity(double maxVoltage, double angle, double acceleration) { 089 // Assume max velocity is positive 090 return (maxVoltage - ks - Math.cos(angle) * kcos - acceleration * ka) / kv; 091 } 092 093 /** 094 * Calculates the minimum achievable velocity given a maximum voltage supply, a position, and an 095 * acceleration. Useful for ensuring that velocity and acceleration constraints for a trapezoidal 096 * profile are simultaneously achievable - enter the acceleration constraint, and this will give 097 * you a simultaneously-achievable velocity constraint. 098 * 099 * @param maxVoltage The maximum voltage that can be supplied to the arm. 100 * @param angle The angle of the arm. 101 * @param acceleration The acceleration of the arm. 102 * @return The minimum possible velocity at the given acceleration and angle. 103 */ 104 public double minAchievableVelocity(double maxVoltage, double angle, double acceleration) { 105 // Assume min velocity is negative, ks flips sign 106 return (-maxVoltage + ks - Math.cos(angle) * kcos - acceleration * ka) / kv; 107 } 108 109 /** 110 * Calculates the maximum achievable acceleration given a maximum voltage supply, a position, and 111 * a velocity. Useful for ensuring that velocity and acceleration constraints for a trapezoidal 112 * profile are simultaneously achievable - enter the velocity constraint, and this will give you a 113 * simultaneously-achievable acceleration constraint. 114 * 115 * @param maxVoltage The maximum voltage that can be supplied to the arm. 116 * @param angle The angle of the arm. 117 * @param velocity The velocity of the arm. 118 * @return The maximum possible acceleration at the given velocity. 119 */ 120 public double maxAchievableAcceleration(double maxVoltage, double angle, double velocity) { 121 return (maxVoltage - ks * Math.signum(velocity) - Math.cos(angle) * kcos - velocity * kv) / ka; 122 } 123 124 /** 125 * Calculates the minimum achievable acceleration given a maximum voltage supply, a position, and 126 * a velocity. Useful for ensuring that velocity and acceleration constraints for a trapezoidal 127 * profile are simultaneously achievable - enter the velocity constraint, and this will give you a 128 * simultaneously-achievable acceleration constraint. 129 * 130 * @param maxVoltage The maximum voltage that can be supplied to the arm. 131 * @param angle The angle of the arm. 132 * @param velocity The velocity of the arm. 133 * @return The minimum possible acceleration at the given velocity. 134 */ 135 public double minAchievableAcceleration(double maxVoltage, double angle, double velocity) { 136 return maxAchievableAcceleration(-maxVoltage, angle, velocity); 137 } 138}