@@ -94,11 +94,11 @@ pub struct Pid<T: FloatCore> {
94
94
pub ki : T ,
95
95
/// Derivative gain.
96
96
pub kd : T ,
97
- /// Limiter for the proportional term: `( -p_limit <= P <= p_limit) `.
97
+ /// Limiter for the proportional term: `-p_limit <= P <= p_limit`.
98
98
pub p_limit : T ,
99
- /// Limiter for the integral term: `( -i_limit <= I <= i_limit) `.
99
+ /// Limiter for the integral term: `-i_limit <= I <= i_limit`.
100
100
pub i_limit : T ,
101
- /// Limiter for the derivative term: `( -d_limit <= D <= d_limit) `.
101
+ /// Limiter for the derivative term: `-d_limit <= D <= d_limit`.
102
102
pub d_limit : T ,
103
103
/// Last calculated integral value if [Pid::ki] is used.
104
104
integral_term : T ,
@@ -107,6 +107,22 @@ pub struct Pid<T: FloatCore> {
107
107
}
108
108
109
109
/// Output of [controller iterations](Pid::next_control_output) with weights
110
+ ///
111
+ /// # Example
112
+ ///
113
+ /// This structure is simple to use and features three weights: [p](Self::p), [i](Self::i), and [d](Self::d). These can be used to figure out how much each term from [Pid] contributed to the final [output](Self::output) value which should be taken as the final controller output for this iteration:
114
+ ///
115
+ /// ```rust
116
+ /// use pid::{Pid, ControlOutput};
117
+ ///
118
+ /// // Setup controller
119
+ /// let mut pid = Pid::new(15.0, 100.0);
120
+ /// pid.p(10.0, 100.0).i(1.0, 100.0).d(2.0, 100.0);
121
+ ///
122
+ /// // Input an example value and get a report for an output iteration
123
+ /// let output = pid.next_control_output(26.2456);
124
+ /// println!("P: {}\nI: {}\nD: {}\nFinal Output: {}", output.p, output.i, output.d, output.output);
125
+ /// ```
110
126
#[ derive( Debug , PartialEq , Eq ) ]
111
127
pub struct ControlOutput < T : FloatCore > {
112
128
/// Contribution of the P term to the output.
@@ -173,14 +189,17 @@ where
173
189
self
174
190
}
175
191
176
- /// Given a new measurement, calculates the next control output.
192
+ /// Given a new measurement, calculates the next [ control output](ControlOutput) .
177
193
///
178
194
/// # Panics
179
195
///
180
196
/// - If a setpoint has not been set via `update_setpoint()`.
181
197
pub fn next_control_output ( & mut self , measurement : T ) -> ControlOutput < T > {
198
+ // Calculate the error between the ideal setpoint and the current
199
+ // measurement to compare against
182
200
let error = self . setpoint - measurement;
183
201
202
+ // Calculate the proportional term and limit to it's individual limit
184
203
let p_unbounded = error * self . kp ;
185
204
let p = apply_limit ( self . p_limit , p_unbounded) ;
186
205
@@ -190,6 +209,7 @@ where
190
209
// we store the entire term so that we don't need to remember previous
191
210
// ki values.
192
211
self . integral_term = self . integral_term + error * self . ki ;
212
+
193
213
// Mitigate integral windup: Don't want to keep building up error
194
214
// beyond what i_limit will allow.
195
215
self . integral_term = apply_limit ( self . i_limit , self . integral_term ) ;
@@ -203,9 +223,12 @@ where
203
223
self . prev_measurement = Some ( measurement) ;
204
224
let d = apply_limit ( self . d_limit , d_unbounded) ;
205
225
226
+ // Calculate the final output by adding together the PID terms, then
227
+ // apply the final defined output limit
206
228
let output = p + self . integral_term + d;
207
229
let output = apply_limit ( self . output_limit , output) ;
208
230
231
+ // Return the individual term's contributions and the final output
209
232
ControlOutput {
210
233
p,
211
234
i : self . integral_term ,
0 commit comments