Skip to content

Commit c7b1be6

Browse files
Merge pull request #62 from DeepBlueRobotics/2024-control-update
2024 PID update + Introduction to control algorithms
2 parents 9cbf946 + f93b13d commit c7b1be6

6 files changed

Lines changed: 191 additions & 77 deletions

File tree

docs/section-7/control.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
How do you get the robot to move a certain distance? You could try to experimentally find the amount of time and power you need to run to get it to a distance, but that is not going to work for a variable distance or if there is a change in the environmental conditions such as the amount of friction.
2+
3+
How do you get a flywheel to move at a certain velocity? There is no `.setVelocity()` method that allows you to easily control a flywheel.
4+
5+
How do you get an arm to move to a certain position? Again, there is no `.setPosition()` method that can accurately supply the motor with the correct amount of voltage to counteract gravity and other forces.
6+
7+
Or is there?
8+
9+
Control systems are the solutions to the three problems mentioned above. They help programmers manipulate motors to a great deal of control and flexibility. When designing a control algorithm for a robot mechanism, there are a number of different approaches to take. These range from very simple approaches, to advanced and complex ones. Each has tradeoffs. Some will work better than others in different situations, some require more mathematical analysis than others.
10+
11+
There are two fundamental types of mechanism controller that we will cover here:
12+
13+
**Feedforward control (or “open-loop control”)** refers to the class of algorithms which incorporate knowledge of how the mechanism under control is expected to operate. For example, using physics we can account for the force of gravity, friction, and other forces. Using this “model” of operation, the control input is chosen to make the mechanism get close to where it should be.
14+
15+
!!! note
16+
The Feedforward control section is still being worked on, so only Feedback control is discussed.
17+
18+
**Feedback control (or “closed-loop control”)** refers to the class of algorithms which use sensors to measure what a mechanism is doing, and issue corrective commands to move a mechanism from where it actually is, to where you want it to be.
19+
20+
It is usually common and best to use both. In the next few pages, we will discuss both control types extensively.

docs/section-7/feedback-control.md

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
<script
2+
src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"
3+
type="text/javascript">
4+
</script>
5+
6+
## Feedback Control: PID Algorithm
7+
PID (Proportional Integral Derivative) control is a method that issue corrective commands to move a mechanism from where it actually is, to where you want it to be using sensors that detect the difference between the two states. WPILIB and vendor libraries usually have PID implemented with a few additional features. We will first cover the underlying theory, then the implementation.
8+
## Theory
9+
Here is an intuitive explanation of PID:
10+
<iframe width="640" height="360" src="https://www.youtube.com/embed/wkfEZmsQqiA" title="What Is PID Control? | Understanding PID Control, Part 1" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
11+
12+
As a summary, the controller has 3 parts:
13+
14+
- kP (Proportional term): drives the position error to zero by contributing to the control signal proportionally to the current position error
15+
16+
- kI (Integral term): drives the total accumulated error to zero by contributing to the control signal proportionally to the sum of all past errors
17+
18+
- kD (Derivative term): drive the change of the error to zero by contributing to the control signal proportionally to the change of the error
19+
20+
The three quantities are added together to drive the error to zero. The three constants can be "tuned" (by changing around their values) for the error to be driven faster, slower, or to be less volatile.
21+
22+
In code, it looks something like this:
23+
``` Java
24+
25+
// kP, kI, kD can be any value, for example kP can = 0.5
26+
// You "tune" these values by finding the optimal value
27+
double kP = 0.2;
28+
double kI = 0.01;
29+
double kD = 0.05;
30+
31+
double lastTimestamp = Timer.getFPGATimestamp();
32+
double accumulatedError = 0; // for the kI term
33+
double previousError = 0; // for the kD term
34+
35+
// runs every 20 ms
36+
@Override
37+
void periodic() {
38+
double targetAltitude = 100;
39+
drone.fly(calculate(drone.getAltitude(), targetAltitude))
40+
}
41+
42+
/* calculates the output of the PID controller given the error (input) */
43+
double calculate(double currentPosition, double targetPosition) {
44+
double error = targetPosition - currentPosition;
45+
double currentTimestamp = Timer.getFPGATimestamp();
46+
// amount of time that passed since the last time the controller was run
47+
double deltaTime = currentTimestamp - lastTimestamp;
48+
accumulatedError += kI * error * deltaTime
49+
double derivative = (error - previousError) / deltaTime;
50+
51+
// update values for next time this method is called
52+
lastTimestamp = currentTimestamp;
53+
previousError = error;
54+
55+
return kP * error + accumulatedError + kD * derivative;
56+
}
57+
```
58+
59+
For those that know calculus, the formal definition of PID control is:
60+
61+
$$ u(t) = K_{p}e(t) + \int_{0}^{t}e(\tau)d\tau + K_{d}\frac{de}{dt} $$
62+
where u(t) is the control effort (amount of feedback at time t), e(t) is the error at current time t, and \\(\tau\\) is the integration variable. Do not worry about the formula as long as you understand the video and how the code works.
63+
64+
### **PID in a nutshell:**
65+
![PID Shorthand Explanation](pid_shorthand_explanation.png)
66+
(I'm too lazy to draw all the red lines representing kI)
67+
68+
If you want more details, WPIlib has an [article of PID](https://docs.wpilib.org/en/stable/docs/software/advanced-control/introduction/introduction-to-pid.html).
69+
70+
## Tuning
71+
How do you find kP, kI, and kD? What are the optimal values of these constants? What is optimal?
72+
73+
In general, the "optimal" PID controller gets the error to zero as fast as possible, while not overshooting or oscillating. For the following graphs, the red line represents the target/goal while the purple line represents the current position. With the ideal constants, you should get close to the 3rd graph.
74+
75+
![PID Tuning Graphs](pid_tuning_graphs.png)
76+
77+
To get those ideal constants... you kind of just guess and check. WPILIB provides a nice simulation of how it is like to tune these constants. Go to this [page](https://docs.wpilib.org/en/stable/docs/software/advanced-controls/introduction/tuning-flywheel.html) and skip everything until you get to "Pure Feedback Control". Follow the instructions and see if you can get the optimal tuning solution.
78+
79+
## Other constants/terms
80+
81+
### kIz or "I-Zone"
82+
Sometimes, the I term can accumulate error very fast and overshoot the target. In that case, we only want the I term to be accumulating error when the error is smaller than a certain threshold. The "I-Zone", typically denoted as kIz, lets you set that threshold.
83+
84+
!!! warning
85+
Generally, it is discouraged to use the I term because it is easy for the mechanism to overshoot. Feedforward should be used instead of the I term. We will explain why in the Feedforward section.
86+
87+
### kF or "Feed-Forward Gain"
88+
This is a sneak peek to what will be discussed in the Feedforward section, but given the target, what is the estimated motor output necessary to get to the target? Say for example we need get a motor to a certain velocity. It probably needs a set amount of voltage to get to that speed, so we should just add it. Note that this is different from kP since kP contributes proportionally to the error, not the target. kF is usually only used for velocity or current (draw) closed loop control, and even then not really used most of the time. The content discussed in the Feed-forward section essentially does what kF does, but better. However, sometimes you will see this constant in other documentation so it is nice to know what it is.
89+
90+
### kMaxOutput, kMinOutput
91+
Should be self-explanatory, used to make sure you don't give your mechanism too much power
92+
93+
### `setReference()`, `setSetpoint()`, etc
94+
There are many names for "target", such as "reference", "setpoint", and more. When tuned properly, after you set the target the motor should drive to the setpoint.
95+
96+
There are many other methods and configurations that are used to PID control which you can find in the documentation.
97+
98+
## Implementation
99+
You remember the code used to implement PID shown near the beginning of this page? Often, one does not need to implement PID Control from the ground-up. WPILIB and many motor controllers have PID controllers built into the class. All that needs to be done is set the setpoint, kP, kI, and kD (as well as other configurations that you may want).
100+
101+
!!! warning
102+
Ever since the last time this website was updated (2024), the team has not used motors from the CTRE Phoenix API. The information regarding the PID implementation of the CTRE Phoenix motors may be outdated. However, SparkMaxes and other motors should be updated.
103+
104+
For example, all [`BaseMotorControllers`](https://www.ctr-electronics.com/downloads/api/cpp/html classctre_1_1phoenix_1_1motorcontrol_1_1can_1_1_base_motor_controller.html) in the CTRE Phoenix API (Talon_SRX and Victor_SPX) can do PID as follows:
105+
106+
``` Java
107+
// Config
108+
motor.config_kP(0, kP);
109+
motor.config_kI(0, kI);
110+
motor.config_kD(0, kD);
111+
// Tell the PID loop how close we want to get to the setpoint in sensor units
112+
motor.configAllowableClosedloopError(0, closeness);
113+
// Tell the PID loop which sensor to use. In this case use a quadrature encoder
114+
motor.configSelectedFeedbackSensor(FeedbackDevice.QuadEncoder);
115+
// Move to 0 on the selected sensor
116+
motor.set(ControlMode.Position, 0);
117+
```
118+
119+
And for SparkMax, SparkFlex, or other Rev motor controllers, we use the [`SparkPIDController`](https://codedocs.revrobotics.com/java/com/revrobotics/sparkpidcontroller) (Take a look at the javadocs). Notice how there are other methods that let you control the velocity, acceleration, setting target, and more:
120+
``` Java
121+
CANPIDController pidController = motor.getPIDController();
122+
// Config
123+
pidController.setOutputRange(-1.0, 1.0);
124+
pidController.setP(kP, 0);
125+
pidController.setI(kI, 0);
126+
pidController.setD(kD, 0);
127+
// Tell the PID loop how close we want to get to the setpoint in rotations
128+
pidController.setSmartMotionAllowedClosedLoopError​(closeness, 0);
129+
130+
// Move to 0 on the quadrature encoder
131+
pidController.setReference​(0.0, ControlType.kPosition, 0)
132+
```
133+
134+
For general purposes, you can use WPILIB's [`PIDController`](https://github.wpilib.org/allwpilib/docs/release/java/edu/wpi/first/math/controller/PIDController.html) class:
135+
``` Java
136+
PIDController pidController = new PIDController(kP, kI, kD);
137+
pidController.setSetpoint(0);
138+
// Tell the PID loop how close we want to get to the setpoint in rotations
139+
pidController.setTolerance(closeness);
140+
141+
// This code is assumed to be in a method or command called frequently.
142+
double state;
143+
if (!pidController.atSetpoint()) {
144+
// getError() and update() are not actual functions. They are placeholders for your own code.
145+
// Get the current state of what is being controlled
146+
error = getError();
147+
// Do something with the adjustment
148+
update(pidController.calculate(error));
149+
}
150+
// Only use this line once the error is "close enough"
151+
pidController.reset();
152+
```
153+
154+
You need to check out the documentation for each of these classes and familiarize youself with them.
155+
156+
# Conclusion
157+
Thankfully, most of the math is handled by WPIlib or motor controller firmware. However, it is important to understand what is actually happening so that you can properly tune your control loops.
158+
159+
But PID is just the beginning. There are many more complicated and more powerful control methods built upon PID that will be discussed later in this section, including Following Trajectories using PathPlanner and Drivetrain Characterization. In addition, PID is often not enough to properly control a mechanism, feedforward algorithms are also needed...
160+
161+
***
162+
163+
> **xkcd #689: FIRST Design**
164+
>
165+
> ![FIRST](https://imgs.xkcd.com/comics/first_design.png)
166+
>
167+
> You might use PID to control the elevator.
168+
>
169+
> _<https://xkcd.com/689/>_

docs/section-7/pid-control.md

Lines changed: 0 additions & 76 deletions
This file was deleted.
107 KB
Loading
39.6 KB
Loading

mkdocs.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ nav:
6060
- 7. Advanced Programming:
6161
- Introduction: section-7/intro.md
6262
- Common Sensors: section-7/sensors.md
63-
- PID Control: section-7/pid-control.md
63+
- Introduction to Control Algorithms: section-7/control.md
64+
- Feedback Control (PID): section-7/feedback-control.md
6465
- Odometry and Kinematics: section-7/odometry-kinematics.md
6566
- Drivetrain Characterization: section-7/drivetrain-characterization.md
6667
- Creating and Following Paths: section-7/pathweaver.md

0 commit comments

Comments
 (0)