-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
create PID helper class for running PID loops in a Mechanism's run method #50
Open
dejabot
wants to merge
8
commits into
main
Choose a base branch
from
pid-runner
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
8a2a906
create PID helper class for running PID loops in a Mechanism's run() …
dejabot 628f69e
change logic for when we set the setpoint
dejabot 10c24a4
move shoulder's ffGain config string to ConfigConstants
dejabot b645d96
setpoint and output should be rotations
dejabot 987f1e9
fix a few typos
dejabot a795957
renamed feedForward to arbitraryFeedForward
dejabot e46823d
linting
dejabot 07b509f
addressing PR comment
dejabot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
package com.team766.controllers; | ||
|
||
import com.team766.framework.Mechanism; | ||
import com.team766.hal.MotorController; | ||
import com.team766.library.ValueProvider; | ||
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; | ||
import java.util.function.Supplier; | ||
|
||
/** | ||
* Helper class that can set a setpoint in a PID loop, eg within {@link Mechanism.run}. | ||
* | ||
* Uses functional interfaces for getting the setpoint, output, PID slot, and feedforward. | ||
* Can use with lambdas and/or method references, eg: | ||
* | ||
* PIDRunner pidRunner = new PIDRunner("SHOULDER", leftMotor, ControlMode.Position, | ||
* this::getTargetRotations, this::getRotations); | ||
*/ | ||
public class PIDRunner { | ||
|
||
public static final Supplier<Integer> DEFAULT_SLOT_PICKER = () -> 0; | ||
public static final Supplier<Double> NO_ARBITRARY_FEED_FORWARD = () -> 0.0; | ||
|
||
/** | ||
* Returns a fixed arbitrary FeedForward supplier, simply returning the latest input ffGain from a config file. | ||
* @param arbitraryFFGain Input FeedForward Gain, read from a config file. | ||
* @return Fixed FeedForward Gain from the current value of ffGain. | ||
*/ | ||
public static final Supplier<Double> fixedArbitraryFeedForward( | ||
ValueProvider<Double> arbitraryFFGain) { | ||
return () -> arbitraryFFGain.valueOr(0.0); | ||
} | ||
|
||
/** | ||
* Returns a FeedForward supplier that gets the latest input ffGain from a config file and returns a proportional | ||
* FeedForward based on the cosine of the supplied angle. Useful for arm type mechanisms, where we want to | ||
* counteract gravity proportionally to the arm's current angle, where 0 is parallel to the ground and 90 | ||
* is perpendicular & up. | ||
* | ||
* @param arbitraryFFGain Input FeedForward Gain, read from a config file. | ||
* @param angle Current angle of the mechanism. 0 is parallel to the ground, 90 is perpendicular & up. | ||
* @return Proportional FeedForward Gain based on the angle. | ||
*/ | ||
public static final Supplier<Double> cosineArbitraryFeedForward( | ||
ValueProvider<Double> arbitraryFFGain, Supplier<Double> angle) { | ||
return () -> arbitraryFFGain.valueOr(0.0) * Math.cos(Math.toRadians(angle.get())); | ||
} | ||
|
||
private final String label; | ||
private final MotorController motor; | ||
private final MotorController.ControlMode mode; | ||
private final Supplier<Double> setPoint; | ||
private final Supplier<Double> output; | ||
private final Supplier<Integer> slot; | ||
private final Supplier<Double> arbitraryFeedForward; | ||
private boolean first = true; | ||
private double prevSetPoint = 0.0; | ||
private double prevArbitraryFeedForward = 0.0; | ||
private int prevSlot = 0; | ||
|
||
public PIDRunner( | ||
String label, | ||
MotorController motor, | ||
MotorController.ControlMode mode, | ||
Supplier<Double> setPoint, | ||
Supplier<Double> output) { | ||
this(label, motor, mode, setPoint, output, DEFAULT_SLOT_PICKER, NO_ARBITRARY_FEED_FORWARD); | ||
} | ||
|
||
public PIDRunner( | ||
String label, | ||
MotorController motor, | ||
MotorController.ControlMode mode, | ||
Supplier<Double> setPoint, | ||
Supplier<Double> output, | ||
Supplier<Integer> slot, | ||
Supplier<Double> arbitraryFeedForward) { | ||
this.label = label; | ||
this.motor = motor; | ||
this.mode = mode; | ||
this.setPoint = setPoint; | ||
this.output = output; | ||
this.slot = slot; | ||
this.arbitraryFeedForward = arbitraryFeedForward; | ||
} | ||
|
||
public void run() { | ||
double currentSetPoint = setPoint.get(); | ||
double currentOutput = output.get(); | ||
double currentArbitraryFeedForward = arbitraryFeedForward.get(); | ||
int currentSlot = slot.get(); | ||
|
||
// if we haven't set the setpoint yet, or if the setpoint, feedforward, or slot have | ||
// changed, set the setpoint. | ||
if (first | ||
|| prevSetPoint != currentSetPoint | ||
|| prevArbitraryFeedForward != currentArbitraryFeedForward | ||
|| currentSlot != prevSlot) { | ||
first = false; | ||
prevSetPoint = currentSetPoint; | ||
prevArbitraryFeedForward = currentArbitraryFeedForward; | ||
prevSlot = currentSlot; | ||
|
||
// log to SmartDashboard - useful for PID tuning. | ||
SmartDashboard.putNumber(label + " setpoint", currentSetPoint); | ||
SmartDashboard.putNumber(label + " output", currentOutput); | ||
SmartDashboard.putNumber(label + " PID slot", currentSlot); | ||
|
||
// FIXME: switch to supporting slot, feedForward once that PR is integrated | ||
// motor.set(mode, currentSetPoint, slot.get(), arbitraryFeedForward.get()); | ||
motor.set(mode, currentSetPoint); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI, for arbitraryFeedForward strategies based on sensors (e.g.
cosineArbitraryFeedForward
), the value will likely change on every evaluationThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thx! q - wouldn't it stop once the mechanism stopped moving? or would there be unintended micromovements, eg from vibrations, etc.
in that case, wdyt of using a minimum change threshold?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes. in addition, many sensors will have signal noise that results in different measurements, even if the mechanism is perfectly still.
a change threshold is probably fine. i would think at most 0.01, unless you want to make it configurable