advanced_pid/
pi_d.rs

1//! The `pi_d` module provides a PID controller where the derivative action is based on the process variable (PV).
2//!
3//! `PiD` is a structure that implements the [`PidController`] trait, which provides methods for creating a new controller and updating the controller.
4//!
5//! # Examples
6//!
7//! ```rust
8//! use advanced_pid::{prelude::*, PiD, PidConfig};
9//!
10//! let config = PidConfig::new(1.0, 0.3, 0.1).with_limits(-1.0, 1.0);
11//! let mut pid = PiD::new(config);
12//!
13//! let target = 1.0;
14//! let actual = 0.0;
15//! let dt = 1.0;
16//!
17//! println!("{}", pid.update(target, actual, dt));
18//! ```
19use super::FloatType;
20use super::PidConfig;
21use super::PidController;
22
23/// `PiD` is a structure that implements the [`PidController`] trait.
24#[derive(Debug, Clone)]
25pub struct PiD {
26    config: PidConfig,
27    i_term: FloatType,
28    pre_actual: FloatType,
29}
30
31impl Default for PiD {
32    /// Creates a new `PiD` with the default configuration.
33    fn default() -> Self {
34        Self::new(PidConfig::default())
35    }
36}
37
38impl PidController for PiD {
39    /// Creates a new `PiD` with the specified configuration.
40    fn new(config: PidConfig) -> Self {
41        Self {
42            config,
43            i_term: 0.0,
44            pre_actual: FloatType::NAN,
45        }
46    }
47
48    /// Updates the `PiD` controller with the specified set point, actual value, and time delta.
49    /// Returns the controller output.
50    fn update(&mut self, set_point: FloatType, actual: FloatType, dt: FloatType) -> FloatType {
51        let error = set_point - actual;
52        self.i_term += error * dt;
53        let d_term = if self.pre_actual.is_nan() {
54            0.0
55        } else {
56            (actual - self.pre_actual) / dt
57        };
58        let output = self.config.gain.kp * error + self.config.gain.ki * self.i_term
59            - self.config.gain.kd * d_term;
60        self.pre_actual = actual;
61        output.clamp(self.config.min, self.config.max)
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68
69    #[test]
70    fn test_pi_d_controller_p() {
71        let gain = crate::PidGain {
72            kp: 1.0,
73            ki: 0.0,
74            kd: 0.0,
75        };
76        let mut pid = PiD::new(gain.into());
77
78        let output = pid.update(1.0, 0.0, 1.0);
79        assert_eq!(output, 1.0);
80    }
81
82    #[test]
83    fn test_pi_d_controller_i() {
84        let gain = crate::PidGain {
85            kp: 0.0,
86            ki: 1.0,
87            kd: 0.0,
88        };
89        let mut pid = PiD::new(gain.into());
90
91        let output = pid.update(1.0, 0.0, 1.0);
92        assert_eq!(output, 1.0);
93        let output = pid.update(1.0, 0.0, 1.0);
94        assert_eq!(output, 2.0);
95        let output = pid.update(1.0, 0.0, 1.0);
96        assert_eq!(output, 3.0);
97    }
98
99    #[test]
100    fn test_pi_d_controller_d() {
101        let gain = crate::PidGain {
102            kp: 0.0,
103            ki: 0.0,
104            kd: 1.0,
105        };
106        let mut pid = PiD::new(gain.into());
107
108        let output = pid.update(0.0, 0.0, 1.0);
109        assert_eq!(output, 0.0);
110        let output = pid.update(1.0, 0.0, 1.0);
111        assert_eq!(output, 0.0);
112        let output = pid.update(1.0, 1.0, 1.0);
113        assert_eq!(output, -1.0);
114    }
115}