advanced_pid/
i_pd.rs

1//! The `i_pd` module provides a PID controller where the proportional action is based on the process variable (PV).
2//!
3//! `Ipd` 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::{i_pd::Ipd, PidConfig, PidController};
9//!
10//! let config = PidConfig::new(1.0, 0.3, 0.1).with_limits(-1.0, 1.0);
11//! let mut pid = Ipd::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/// `Ipd` is a structure that implements the [`PidController`] trait.
24#[derive(Debug, Clone)]
25pub struct Ipd {
26    config: PidConfig,
27    i_term: FloatType,
28    pre_actual: FloatType,
29}
30
31impl Default for Ipd {
32    /// Creates a new `Ipd` with the default configuration.
33    fn default() -> Self {
34        Self::new(PidConfig::default())
35    }
36}
37
38impl PidController for Ipd {
39    /// Creates a new `Ipd` 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 `Ipd` 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.ki * self.i_term
59            - self.config.gain.kp * actual
60            - self.config.gain.kd * d_term;
61        self.pre_actual = actual;
62        output.clamp(self.config.min, self.config.max)
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69
70    #[test]
71    fn test_i_pd_controller_p() {
72        let gain = crate::PidGain {
73            kp: 1.0,
74            ki: 0.0,
75            kd: 0.0,
76        };
77        let mut pid = Ipd::new(gain.into());
78
79        let output = pid.update(1.0, 0.0, 1.0);
80        assert_eq!(output, 0.0);
81        let output = pid.update(1.0, 1.0, 1.0);
82        assert_eq!(output, -1.0);
83    }
84
85    #[test]
86    fn test_i_pd_controller_i() {
87        let gain = crate::PidGain {
88            kp: 0.0,
89            ki: 1.0,
90            kd: 0.0,
91        };
92        let mut pid = Ipd::new(gain.into());
93
94        let output = pid.update(1.0, 0.0, 1.0);
95        assert_eq!(output, 1.0);
96        let output = pid.update(1.0, 0.0, 1.0);
97        assert_eq!(output, 2.0);
98        let output = pid.update(1.0, 0.0, 1.0);
99        assert_eq!(output, 3.0);
100    }
101
102    #[test]
103    fn test_i_pd_controller_d() {
104        let gain = crate::PidGain {
105            kp: 0.0,
106            ki: 0.0,
107            kd: 1.0,
108        };
109        let mut pid = Ipd::new(gain.into());
110
111        let output = pid.update(0.0, 0.0, 1.0);
112        assert_eq!(output, 0.0);
113        let output = pid.update(1.0, 0.0, 1.0);
114        assert_eq!(output, 0.0);
115        let output = pid.update(1.0, 1.0, 1.0);
116        assert_eq!(output, -1.0);
117    }
118}