1212//! pid.p(10.0, 100.0);
1313//!
1414//! // Input a measurement with an error of 5.0 from our setpoint
15+ //!
16+ //! #[cfg(feature = "dt")]
1517//! let output = pid.next_control_output(10.0, 1.0);
18+ //! #[cfg(not(feature = "dt"))]
19+ //! let output = pid.next_control_output(10.0);
1620//!
1721//! // Show that the error is correct by multiplying by our kp
1822//! assert_eq!(output.output, 50.0); // <--
1923//! assert_eq!(output.p, 50.0);
2024//!
2125//! // It won't change on repeat; the controller is proportional-only
26+ //!
27+ //! #[cfg(feature = "dt")]
2228//! let output = pid.next_control_output(10.0, 1.0);
29+ //! #[cfg(not(feature = "dt"))]
30+ //! let output = pid.next_control_output(10.0);
31+ //!
2332//! assert_eq!(output.output, 50.0); // <--
2433//! assert_eq!(output.p, 50.0);
2534//!
2635//! // Add a new integral term to the controller and input again
2736//! pid.i(1.0, 100.0);
37+ //!
38+ //! #[cfg(feature = "dt")]
2839//! let output = pid.next_control_output(10.0, 1.0);
40+ //! #[cfg(not(feature = "dt"))]
41+ //! let output = pid.next_control_output(10.0);
2942//!
3043//! // Now that the integral makes the controller stateful, it will change
3144//! assert_eq!(output.output, 55.0); // <--
3447//!
3548//! // Add our final derivative term and match our setpoint target
3649//! pid.d(2.0, 100.0);
50+ //! #[cfg(feature = "dt")]
3751//! let output = pid.next_control_output(15.0, 1.0);
52+ //! #[cfg(not(feature = "dt"))]
53+ //! let output = pid.next_control_output(15.0);
3854//!
3955//! // The output will now say to go down due to the derivative
4056//! assert_eq!(output.output, -5.0); // <--
@@ -79,7 +95,10 @@ impl<T: PartialOrd + num_traits::Signed + Copy> Number for T {}
7995/// p_controller.p(10.0, 100.0);
8096///
8197/// // Get first output
98+ /// #[cfg(feature = "dt")]
8299/// let p_output = p_controller.next_control_output(400.0, 1.0);
100+ /// #[cfg(not(feature = "dt"))]
101+ /// let p_output = p_controller.next_control_output(400.0);
83102/// ```
84103///
85104/// This controller would give you set a proportional controller to `10.0` with a target of `15.0` and an output limit of `100.0` per [output](Self::next_control_output) iteration. The same controller with a full PID system built in looks like:
@@ -92,7 +111,10 @@ impl<T: PartialOrd + num_traits::Signed + Copy> Number for T {}
92111/// full_controller.p(10.0, 100.0).i(4.5, 100.0).d(0.25, 100.0);
93112///
94113/// // Get first output
114+ /// #[cfg(feature = "dt")]
95115/// let full_output = full_controller.next_control_output(400.0, 1.0);
116+ /// #[cfg(not(feature = "dt"))]
117+ /// let full_output = full_controller.next_control_output(400.0);
96118/// ```
97119///
98120/// This [`next_control_output`](Self::next_control_output) method is what's used to input new values into the controller to tell it what the current state of the system is. In the examples above it's only being used once, but realistically this will be a hot method. Please see [ControlOutput] for examples of how to handle these outputs; it's quite straight forward and mirrors the values of this structure in some ways.
@@ -141,7 +163,11 @@ pub struct Pid<T: Number> {
141163/// pid.p(10.0, 100.0).i(1.0, 100.0).d(2.0, 100.0);
142164///
143165/// // Input an example value and get a report for an output iteration
166+ /// #[cfg(feature = "dt")]
144167/// let output = pid.next_control_output(26.2456, 1.0);
168+ /// #[cfg(not(feature = "dt"))]
169+ /// let output = pid.next_control_output(26.2456);
170+ ///
145171/// println!("P: {}\nI: {}\nD: {}\nFinal Output: {}", output.p, output.i, output.d, output.output);
146172/// ```
147173#[ derive( Debug , PartialEq , Eq ) ]
@@ -210,6 +236,7 @@ where
210236 self
211237 }
212238
239+ #[ cfg( feature = "dt" ) ]
213240 /// Given a new measurement and dt, calculates the next [control output](ControlOutput).
214241 ///
215242 /// # Panics
@@ -259,6 +286,55 @@ where
259286 }
260287 }
261288
289+ #[ cfg( not( feature = "dt" ) ) ]
290+ /// Given a new measurement, calculates the next [control output](ControlOutput).
291+ ///
292+ /// # Panics
293+ ///
294+ /// - If a setpoint has not been set via `update_setpoint()`.
295+ pub fn next_control_output ( & mut self , measurement : T ) -> ControlOutput < T > {
296+ // Calculate the error between the ideal setpoint and the current
297+ // measurement to compare against
298+ let error = self . setpoint - measurement;
299+
300+ // Calculate the proportional term and limit to it's individual limit
301+ let p_unbounded = error * self . kp ;
302+ let p = apply_limit ( self . p_limit , p_unbounded) ;
303+
304+ // Mitigate output jumps when ki(t) != ki(t-1).
305+ // While it's standard to use an error_integral that's a running sum of
306+ // just the error (no ki), because we support ki changing dynamically,
307+ // we store the entire term so that we don't need to remember previous
308+ // ki values.
309+ self . integral_term = self . integral_term + error * self . ki ;
310+
311+ // Mitigate integral windup: Don't want to keep building up error
312+ // beyond what i_limit will allow.
313+ self . integral_term = apply_limit ( self . i_limit , self . integral_term ) ;
314+
315+ // Mitigate derivative kick: Use the derivative of the measurement
316+ // rather than the derivative of the error.
317+ let d_unbounded = -match self . prev_measurement . as_ref ( ) {
318+ Some ( prev_measurement) => measurement - * prev_measurement,
319+ None => T :: zero ( ) ,
320+ } * self . kd ;
321+ self . prev_measurement = Some ( measurement) ;
322+ let d = apply_limit ( self . d_limit , d_unbounded) ;
323+
324+ // Calculate the final output by adding together the PID terms, then
325+ // apply the final defined output limit
326+ let output = p + self . integral_term + d;
327+ let output = apply_limit ( self . output_limit , output) ;
328+
329+ // Return the individual term's contributions and the final output
330+ ControlOutput {
331+ p,
332+ i : self . integral_term ,
333+ d,
334+ output,
335+ }
336+ }
337+
262338 /// Resets the integral term back to zero, this may drastically change the
263339 /// control output.
264340 pub fn reset_integral_term ( & mut self ) {
@@ -283,6 +359,30 @@ mod tests {
283359 use super :: Pid ;
284360 use crate :: ControlOutput ;
285361
362+ /// Macro to call Pid::next_control_output() with the correct signature
363+ #[ cfg( feature = "dt" ) ]
364+ macro_rules! pid_next_control_output {
365+ ( $self: ident, $measurement: expr, $dt: expr) => {
366+ $self. next_control_output( $measurement, $dt)
367+ } ;
368+ ( $self: ident, $measurement: expr) => {
369+ // If the dt feature is enabled, we call the function with dt = 1.0
370+ $self. next_control_output( $measurement, 1.0 )
371+ } ;
372+ }
373+
374+ /// Macro to call Pid::next_control_output() with the correct signature
375+ #[ cfg( not( feature = "dt" ) ) ]
376+ macro_rules! pid_next_control_output {
377+ ( $self: ident, $measurement: expr, $dt: expr) => {
378+ // If the dt feature is not enabled, we ignore the dt argument
379+ $self. next_control_output( $measurement)
380+ } ;
381+ ( $self: ident, $measurement: expr) => {
382+ $self. next_control_output( $measurement)
383+ } ;
384+ }
385+
286386 /// Proportional-only controller operation and limits
287387 #[ test]
288388 fn proportional ( ) {
@@ -291,11 +391,11 @@ mod tests {
291391 assert_eq ! ( pid. setpoint, 10.0 ) ;
292392
293393 // Test simple proportional
294- assert_eq ! ( pid . next_control_output ( 0.0 , 1.0 ) . output, 20.0 ) ;
394+ assert_eq ! ( pid_next_control_output! ( pid , 0.0 , 1.0 ) . output, 20.0 ) ;
295395
296396 // Test proportional limit
297397 pid. p_limit = 10.0 ;
298- assert_eq ! ( pid . next_control_output ( 0.0 , 1.0 ) . output, 10.0 ) ;
398+ assert_eq ! ( pid_next_control_output! ( pid , 0.0 , 1.0 ) . output, 10.0 ) ;
299399 }
300400
301401 /// Derivative-only controller operation and limits
@@ -305,14 +405,14 @@ mod tests {
305405 pid. p ( 0.0 , 100.0 ) . i ( 0.0 , 100.0 ) . d ( 2.0 , 100.0 ) ;
306406
307407 // Test that there's no derivative since it's the first measurement
308- assert_eq ! ( pid . next_control_output ( 0.0 , 1.0 ) . output, 0.0 ) ;
408+ assert_eq ! ( pid_next_control_output! ( pid , 0.0 , 1.0 ) . output, 0.0 ) ;
309409
310410 // Test that there's now a derivative
311- assert_eq ! ( pid . next_control_output ( 5.0 , 1.0 ) . output, -10.0 ) ;
411+ assert_eq ! ( pid_next_control_output! ( pid , 5.0 , 1.0 ) . output, -10.0 ) ;
312412
313413 // Test derivative limit
314414 pid. d_limit = 5.0 ;
315- assert_eq ! ( pid . next_control_output ( 10.0 , 1.0 ) . output, -5.0 ) ;
415+ assert_eq ! ( pid_next_control_output! ( pid , 10.0 , 1.0 ) . output, -5.0 ) ;
316416 }
317417
318418 /// Integral-only controller operation and limits
@@ -322,26 +422,26 @@ mod tests {
322422 pid. p ( 0.0 , 100.0 ) . i ( 2.0 , 100.0 ) . d ( 0.0 , 100.0 ) ;
323423
324424 // Test basic integration
325- assert_eq ! ( pid . next_control_output ( 0.0 , 1.0 ) . output, 20.0 ) ;
326- assert_eq ! ( pid . next_control_output ( 0.0 , 1.0 ) . output, 40.0 ) ;
327- assert_eq ! ( pid . next_control_output ( 5.0 , 1.0 ) . output, 50.0 ) ;
425+ assert_eq ! ( pid_next_control_output! ( pid , 0.0 , 1.0 ) . output, 20.0 ) ;
426+ assert_eq ! ( pid_next_control_output! ( pid , 0.0 , 1.0 ) . output, 40.0 ) ;
427+ assert_eq ! ( pid_next_control_output! ( pid , 5.0 , 1.0 ) . output, 50.0 ) ;
328428
329429 // Test limit
330430 pid. i_limit = 50.0 ;
331- assert_eq ! ( pid . next_control_output ( 5.0 , 1.0 ) . output, 50.0 ) ;
431+ assert_eq ! ( pid_next_control_output! ( pid , 5.0 , 1.0 ) . output, 50.0 ) ;
332432 // Test that limit doesn't impede reversal of error integral
333- assert_eq ! ( pid . next_control_output ( 15.0 , 1.0 ) . output, 40.0 ) ;
433+ assert_eq ! ( pid_next_control_output! ( pid , 15.0 , 1.0 ) . output, 40.0 ) ;
334434
335435 // Test that error integral accumulates negative values
336436 let mut pid2 = Pid :: new ( -10.0 , 100.0 ) ;
337437 pid2. p ( 0.0 , 100.0 ) . i ( 2.0 , 100.0 ) . d ( 0.0 , 100.0 ) ;
338- assert_eq ! ( pid2 . next_control_output ( 0.0 , 1.0 ) . output, -20.0 ) ;
339- assert_eq ! ( pid2 . next_control_output ( 0.0 , 1.0 ) . output, -40.0 ) ;
438+ assert_eq ! ( pid_next_control_output! ( pid2 , 0.0 , 1.0 ) . output, -20.0 ) ;
439+ assert_eq ! ( pid_next_control_output! ( pid2 , 0.0 , 1.0 ) . output, -40.0 ) ;
340440
341441 pid2. i_limit = 50.0 ;
342- assert_eq ! ( pid2 . next_control_output ( -5.0 , 1.0 ) . output, -50.0 ) ;
442+ assert_eq ! ( pid_next_control_output! ( pid2 , -5.0 , 1.0 ) . output, -50.0 ) ;
343443 // Test that limit doesn't impede reversal of error integral
344- assert_eq ! ( pid2 . next_control_output ( -15.0 , 1.0 ) . output, -40.0 ) ;
444+ assert_eq ! ( pid_next_control_output! ( pid2 , -15.0 , 1.0 ) . output, -40.0 ) ;
345445 }
346446
347447 /// Checks that a full PID controller's limits work properly through multiple output iterations
@@ -350,11 +450,11 @@ mod tests {
350450 let mut pid = Pid :: new ( 10.0 , 1.0 ) ;
351451 pid. p ( 1.0 , 100.0 ) . i ( 0.0 , 100.0 ) . d ( 0.0 , 100.0 ) ;
352452
353- let out = pid . next_control_output ( 0.0 , 1.0 ) ;
453+ let out = pid_next_control_output ! ( pid , 0.0 , 1.0 ) ;
354454 assert_eq ! ( out. p, 10.0 ) ; // 1.0 * 10.0
355455 assert_eq ! ( out. output, 1.0 ) ;
356456
357- let out = pid . next_control_output ( 20.0 , 1.0 ) ;
457+ let out = pid_next_control_output ! ( pid , 20.0 , 1.0 ) ;
358458 assert_eq ! ( out. p, -10.0 ) ; // 1.0 * (10.0 - 20.0)
359459 assert_eq ! ( out. output, -1.0 ) ;
360460 }
@@ -365,25 +465,25 @@ mod tests {
365465 let mut pid = Pid :: new ( 10.0 , 100.0 ) ;
366466 pid. p ( 1.0 , 100.0 ) . i ( 0.1 , 100.0 ) . d ( 1.0 , 100.0 ) ;
367467
368- let out = pid . next_control_output ( 0.0 , 1.0 ) ;
468+ let out = pid_next_control_output ! ( pid , 0.0 , 1.0 ) ;
369469 assert_eq ! ( out. p, 10.0 ) ; // 1.0 * 10.0
370470 assert_eq ! ( out. i, 1.0 ) ; // 0.1 * 10.0
371471 assert_eq ! ( out. d, 0.0 ) ; // -(1.0 * 0.0)
372472 assert_eq ! ( out. output, 11.0 ) ;
373473
374- let out = pid . next_control_output ( 5.0 , 1.0 ) ;
474+ let out = pid_next_control_output ! ( pid , 5.0 , 1.0 ) ;
375475 assert_eq ! ( out. p, 5.0 ) ; // 1.0 * 5.0
376476 assert_eq ! ( out. i, 1.5 ) ; // 0.1 * (10.0 + 5.0)
377477 assert_eq ! ( out. d, -5.0 ) ; // -(1.0 * 5.0)
378478 assert_eq ! ( out. output, 1.5 ) ;
379479
380- let out = pid . next_control_output ( 11.0 , 1.0 ) ;
480+ let out = pid_next_control_output ! ( pid , 11.0 , 1.0 ) ;
381481 assert_eq ! ( out. p, -1.0 ) ; // 1.0 * -1.0
382482 assert_eq ! ( out. i, 1.4 ) ; // 0.1 * (10.0 + 5.0 - 1)
383483 assert_eq ! ( out. d, -6.0 ) ; // -(1.0 * 6.0)
384484 assert_eq ! ( out. output, -5.6 ) ;
385485
386- let out = pid . next_control_output ( 10.0 , 1.0 ) ;
486+ let out = pid_next_control_output ! ( pid , 10.0 , 1.0 ) ;
387487 assert_eq ! ( out. p, 0.0 ) ; // 1.0 * 0.0
388488 assert_eq ! ( out. i, 1.4 ) ; // 0.1 * (10.0 + 5.0 - 1.0 + 0.0)
389489 assert_eq ! ( out. d, 1.0 ) ; // -(1.0 * -1.0)
@@ -402,8 +502,8 @@ mod tests {
402502
403503 for _ in 0 ..5 {
404504 assert_eq ! (
405- pid_f32 . next_control_output ( 0.0 , 1.0 ) . output,
406- pid_f64 . next_control_output ( 0.0 , 1.0 ) . output as f32
505+ pid_next_control_output! ( pid_f32 , 0.0 , 1.0 ) . output,
506+ pid_next_control_output! ( pid_f64 , 0.0 , 1.0 ) . output as f32
407507 ) ;
408508 }
409509 }
@@ -420,8 +520,8 @@ mod tests {
420520
421521 for _ in 0 ..5 {
422522 assert_eq ! (
423- pid_i32 . next_control_output ( 0 , 1 ) . output,
424- pid_i8 . next_control_output ( 0i8 , 1i8 ) . output as i32
523+ pid_next_control_output! ( pid_i32 , 0 , 1 ) . output,
524+ pid_next_control_output! ( pid_i8 , 0i8 , 1i8 ) . output as i32
425525 ) ;
426526 }
427527 }
@@ -432,7 +532,7 @@ mod tests {
432532 let mut pid = Pid :: new ( 10.0 , 100.0 ) ;
433533 pid. p ( 1.0 , 100.0 ) . i ( 0.1 , 100.0 ) . d ( 1.0 , 100.0 ) ;
434534
435- let out = pid . next_control_output ( 0.0 , 1.0 ) ;
535+ let out = pid_next_control_output ! ( pid , 0.0 , 1.0 ) ;
436536 assert_eq ! ( out. p, 10.0 ) ; // 1.0 * 10.0
437537 assert_eq ! ( out. i, 1.0 ) ; // 0.1 * 10.0
438538 assert_eq ! ( out. d, 0.0 ) ; // -(1.0 * 0.0)
@@ -441,7 +541,7 @@ mod tests {
441541 pid. setpoint ( 0.0 ) ;
442542
443543 assert_eq ! (
444- pid . next_control_output ( 0.0 , 1.0 ) ,
544+ pid_next_control_output! ( pid , 0.0 , 1.0 ) ,
445545 ControlOutput {
446546 p: 0.0 ,
447547 i: 1.0 ,
@@ -457,7 +557,7 @@ mod tests {
457557 let mut pid = Pid :: new ( 10.0f32 , -10.0 ) ;
458558 pid. p ( 1.0 , -50.0 ) . i ( 1.0 , -50.0 ) . d ( 1.0 , -50.0 ) ;
459559
460- let out = pid . next_control_output ( 0.0 , 1.0 ) ;
560+ let out = pid_next_control_output ! ( pid , 0.0 , 1.0 ) ;
461561 assert_eq ! ( out. p, 10.0 ) ;
462562 assert_eq ! ( out. i, 10.0 ) ;
463563 assert_eq ! ( out. d, 0.0 ) ;
0 commit comments