-
Notifications
You must be signed in to change notification settings - Fork 282
Controls Tutorial for Motion Profiling #2589
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
Merged
jasondaming
merged 32 commits into
wpilibsuite:main
from
gerth2:gerth2_elevator_motion_profiling
Jan 5, 2025
Merged
Changes from 20 commits
Commits
Show all changes
32 commits
Select commit
Hold shift + click to select a range
db5426b
first pass new page
gerth2 5e569f0
wip2
gerth2 b42d94d
wip wip wip now I have to go make dinner
gerth2 1bd3d16
moar wip
gerth2 a4cb2dd
many things but it be movin
gerth2 8318e26
WIP getting profiling done
gerth2 09bfc71
WIP adding profiling
gerth2 82d3f95
basic profiling working. Including AI-assisted code translation.
gerth2 54fe002
Further tweaks for tunability and clerer explanation
gerth2 261cb8d
lint and spelling
gerth2 05759ed
Fixing setpoint nonresponsive bug
gerth2 963760d
Furhter typos and bugfixes
gerth2 8139418
reworded setpoint
gerth2 c90cb53
reworked naming of signals
gerth2 8d0cefc
bonk
gerth2 7e16a4c
Merge remote-tracking branch 'upstream/main' into gerth2_elevator_mot…
gerth2 cff4af3
Merge remote-tracking branch 'upstream/main' into gerth2_elevator_mot…
gerth2 403fad8
Merge remote-tracking branch 'origin/gerth2_elevator_motion_profiling…
gerth2 641a312
Merge branch 'wpilibsuite:main' into gerth2_elevator_motion_profiling
gerth2 f1c2556
rewrote trapezoid profile
gerth2 3a3adf9
Air resistance is now less arbitrary
gerth2 f7352ef
Update source/docs/software/advanced-controls/introduction/tuning-ele…
gerth2 f879c2f
Update source/docs/software/advanced-controls/introduction/tuning-ele…
gerth2 6f6757c
Update source/docs/software/advanced-controls/introduction/tuning-ele…
gerth2 9178c61
Update source/docs/software/advanced-controls/introduction/tuning-ele…
gerth2 3802f13
Update source/docs/software/advanced-controls/introduction/tuning-ele…
gerth2 3777398
Update source/docs/software/advanced-controls/introduction/tuning-ele…
gerth2 0b78585
Cleanups from Jason's requests
gerth2 0b6f7e9
lint
gerth2 b5b0823
Update source/docs/software/advanced-controls/introduction/tuning-ele…
gerth2 816e710
Added list of links for all tutorials
gerth2 5739ca0
oops
gerth2 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 hidden or 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 hidden or 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
80 changes: 80 additions & 0 deletions
80
source/_extensions/controls_js_sim/plant/vertical-elevator-plant.js
This file contains hidden or 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,80 @@ | ||
class VerticalElevatorPlant { | ||
constructor(TimestepS, heightM) { | ||
this.TimestepS = TimestepS; | ||
|
||
// Gains estimated by ReCalc (http://reca.lc) with the specs below | ||
// motor: 1x REV Neo | ||
// gearing: 3:1 | ||
// Pulley Diameter: 1 in | ||
// efficiency: 75 | ||
// moving mass: 20 kg | ||
this.kGVolts = 2.28; | ||
this.kVVoltSecondPerM = 3.07; | ||
this.kAVoltSecondSquaredPerM = 0.41; | ||
|
||
//Maximum height the elevator travels to | ||
this.heightM = heightM; | ||
|
||
this.state = [0, 0]; | ||
|
||
this.systemNoise = false; | ||
// Simulate half volt std dev system noise at sim loop update frequency | ||
this.gaussianNoise = gaussian(0, 0.5); | ||
} | ||
|
||
acceleration([posM, velMPerS], inputVolts) { | ||
if (this.systemNoise) { | ||
inputVolts += this.gaussianNoise(); | ||
} | ||
|
||
const gravityAcceleration = -this.kGVolts / this.kAVoltSecondSquaredPerM; | ||
const EMFAcceleration = -this.kVVoltSecondPerM * velMPerS / this.kAVoltSecondSquaredPerM; | ||
const controlEffortAcceleration = inputVolts / this.kAVoltSecondSquaredPerM; | ||
const airResistanceAccel = -1.0 * Math.sign(velMPerS) * Math.pow(velMPerS, 2) * 0.2; | ||
|
||
var springAccel = 0.0; | ||
var dashpotAccel = 0.0; | ||
|
||
//Apply hard-stops as a combo spring + dashpot | ||
if(posM > this.heightM){ | ||
//Top limit | ||
springAccel = (posM - this.heightM) * -100000.0; | ||
dashpotAccel = -100.0 * velMPerS; | ||
} else if(posM < 0.0){ | ||
//Bottom limit | ||
springAccel = (posM) * -100000.0; | ||
dashpotAccel = -100.0 * velMPerS; | ||
} | ||
|
||
const accelMPerSSquared = gravityAcceleration + EMFAcceleration + controlEffortAcceleration + airResistanceAccel + springAccel + dashpotAccel; | ||
|
||
|
||
return [velMPerS, accelMPerSSquared] | ||
} | ||
|
||
reset() { | ||
this.state = [0, 0]; | ||
} | ||
|
||
update(inputVolts) { | ||
// Simulate system noise | ||
if (this.systemNoise && inputVolts > 0) { | ||
// apply system noise | ||
inputVolts += this.gaussianNoise(); | ||
} | ||
|
||
this.state = | ||
secondOrderRK4((state, inputVolts) => this.acceleration(state, inputVolts), | ||
this.state, | ||
inputVolts, | ||
this.TimestepS); | ||
} | ||
|
||
getPositionM() { | ||
return this.state[0]; | ||
} | ||
|
||
setSystemNoise(enabled) { | ||
this.systemNoise = enabled; | ||
} | ||
} |
154 changes: 154 additions & 0 deletions
154
source/_extensions/controls_js_sim/sim/vertical-elevator-sim.js
This file contains hidden or 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,154 @@ | ||
class VerticalElevatorSim extends BaseSim { | ||
constructor(divIdPrefix) { | ||
super(divIdPrefix, "M", -0.5, 2.5); | ||
|
||
this.simDurationS = 5.0; | ||
this.simulationTimestepS = 0.005; | ||
this.controllerTimestepS = 0.02; | ||
|
||
this.elevHeightM = 1.0; | ||
|
||
this.plant = new VerticalElevatorPlant(this.simulationTimestepS, this.elevHeightM); | ||
|
||
this.visualization = new VerticalElevatorVisualization( | ||
this.visualizationDrawDiv, | ||
this.simulationTimestepS, | ||
this.elevHeightM, | ||
() => this.iterationCount - 1, | ||
setpoint => this.setSetpointM(setpoint), | ||
() => this.begin() | ||
); | ||
this.visualization.drawStatic(); | ||
|
||
this.timeSinceLastControllerIteration = 0.0; | ||
|
||
this.accumulatedError = 0.0; | ||
this.previousPositionError = 0.0; | ||
|
||
//User-configured feedback | ||
this.kP = 0.0; | ||
this.kI = 0.0; | ||
this.kD = 0.0; | ||
|
||
//User-configured Feed-Forward | ||
this.kG = 0.0; | ||
this.kV = 0.0; | ||
this.kA = 0.0; | ||
|
||
//User-configured Profiling | ||
this.maxVelMps = 0.0; | ||
this.maxAccelMps2 = 0.0; | ||
|
||
this.inputVolts = 0.0; | ||
|
||
//Default starting setpoint | ||
this.goal = new ProfileState(0.0, 0.0, 0.0); | ||
|
||
// Reset at least once right at the start | ||
this.resetCustom(); | ||
} | ||
|
||
setSetpointM(setpoint) { | ||
this.goal = new ProfileState(setpoint, 0.0, 0.0); | ||
document.getElementById(this.divIdPrefix + "_setpoint").value = setpoint; | ||
} | ||
|
||
resetCustom() { | ||
this.plant.reset(); | ||
this.timeS = Array(this.simDurationS / this.simulationTimestepS) | ||
.fill() | ||
.map((_, index) => { | ||
return index * this.simulationTimestepS; | ||
}); | ||
|
||
this.visualization.setCurPos(0.0); | ||
this.visualization.setCurTime(0.0); | ||
this.visualization.setCurSetpoint(0.0); | ||
this.visualization.setCurControlEffort(0.0); | ||
|
||
this.accumulatedError = 0.0; | ||
this.previousPositionError = 0.0; | ||
this.inputvolts = 0.0; | ||
this.iterationCount = 0; | ||
|
||
this.makeProfile() | ||
|
||
this.positionDelayLine = new DelayLine(13); //models sensor lag | ||
|
||
} | ||
|
||
makeProfile(){ | ||
var constraints = new ProfileConstraints(this.maxVelMps, this.maxAccelMps2); | ||
this.profile = new TrapezoidProfile(constraints) | ||
this.start = new ProfileState(0.0,0.0,0.0); | ||
this.setpoint = this.start; | ||
this.profile.init(this.start, this.goal) | ||
} | ||
|
||
iterateCustom() { | ||
|
||
this.curSimTimeS = this.timeS[this.iterationCount]; | ||
|
||
let measuredPositionM = this.positionDelayLine.getSample(); | ||
|
||
// Update controller at controller freq | ||
if (this.timeSinceLastControllerIteration >= this.controllerTimestepS) { | ||
this.setpoint = this.profile.calculate(this.curSimTimeS, this.setpoint) | ||
this.inputVolts = this.updateController(this.setpoint, measuredPositionM); | ||
this.timeSinceLastControllerIteration = this.controllerTimestepS; | ||
} else { | ||
this.timeSinceLastControllerIteration = this.timeSinceLastControllerIteration + this.simulationTimestepS; | ||
} | ||
|
||
this.plant.update(this.inputVolts); | ||
|
||
this.positionDelayLine.addSample(this.plant.getPositionM()); | ||
|
||
this.visualization.setCurPos(this.plant.getPositionM()); | ||
this.visualization.setCurTime(this.curSimTimeS); | ||
this.visualization.setCurSetpoint(this.setpoint.pos); | ||
this.visualization.setCurControlEffort(this.inputVolts); | ||
|
||
this.procVarActualSignal.addSample(new Sample(this.curSimTimeS, this.plant.getPositionM())); | ||
this.procVarDesiredSignal.addSample(new Sample(this.curSimTimeS, this.setpoint.pos)); | ||
this.voltsSignal.addSample(new Sample(this.curSimTimeS, this.inputVolts)); | ||
|
||
this.iterationCount++; | ||
|
||
if (this.iterationCount >= this.timeS.length) { | ||
this.end(); | ||
} | ||
} | ||
|
||
updateController(setpoint, measurement) { | ||
|
||
// Calculate error, error derivative, and error integral | ||
let positionError = setpoint.pos - measurement; | ||
|
||
this.accumulatedError += positionError * this.controllerTimestepS; | ||
|
||
const derivativeError = | ||
(positionError - this.previousPositionError) / this.controllerTimestepS; | ||
|
||
// PID + gravity/Profiled feed-forward control law | ||
let controlEffortVolts = | ||
this.kG + | ||
this.kV * setpoint.vel + | ||
this.kA * setpoint.accel + | ||
this.kP * positionError + | ||
this.kI * this.accumulatedError + | ||
this.kD * derivativeError; | ||
|
||
// Cap voltage at max/min of the physically possible command | ||
if (controlEffortVolts > 12) { | ||
controlEffortVolts = 12; | ||
} else if (controlEffortVolts < -12) { | ||
controlEffortVolts = -12; | ||
} | ||
|
||
this.previousPositionError = positionError; | ||
|
||
return controlEffortVolts; | ||
} | ||
|
||
} |
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.
Uh oh!
There was an error while loading. Please reload this page.