pid_loop/lib.rs
1//! A tiny `no_std` PID controller library.
2//!
3//! This crate implements the classic windowed PID loop over abstract data type.
4//!
5//! # Introduction
6//!
7//! A proportional-integral-derivative controller is a control loop mechanism
8//! employing feedback that is widely used in industrial control systems
9//! and a variety of other applications requiring continuously modulated control.
10//!
11//! # Examples
12//!
13//! ```no_run
14//! use pid_loop::PID;
15//!
16//! let target = 42.0;
17//! let mut controller = PID::<f32, 1>::new(2.0, 0.7, 0.3, 0.0, 0.0);
18//! loop {
19//! let correction = controller.next(target, measure());
20//! apply_correction(correction);
21//! sleep();
22//! }
23//!
24//! fn sleep() { todo!() }
25//! fn measure() -> f32 { todo!() }
26//! fn apply_correction(_: f32) { todo!() }
27//! ```
28
29#![no_std]
30#![deny(nonstandard_style, future_incompatible, rust_2018_idioms)]
31use core::ops::*;
32
33/// PID controller
34///
35/// # Examples
36///
37/// ```no_run
38/// use pid_loop::PID;
39///
40/// let target = 42.0;
41/// let mut controller = PID::<f64, 1>::new(2.0, 0.7, 0.3, 0.0, 0.0);
42/// let correction = controller.next(target, measure());
43///
44/// fn measure() -> f64 { todo!() }
45/// ```
46pub struct PID<F, const W: usize> {
47 /// Proportional gain.
48 pub kp: F,
49 /// Integral gain.
50 pub ki: F,
51 /// Derivative gain.
52 pub kd: F,
53 /// Feed forward gain
54 pub kf: F,
55 /// Velocity
56 pub kv: F,
57 last_sp: F,
58 last_error_idx: usize,
59 errors: [F; W],
60 err_history: F,
61}
62
63impl<F, const W: usize> PID<F, W>
64where
65 F: Default + Add<Output = F> + Sub<Output = F> + Mul<Output = F> + PartialOrd + Copy,
66{
67 /// Create a new instance of `PID`.
68 ///
69 /// # Examples
70 ///
71 /// ```
72 /// #![allow(unused_assignments)]
73 /// use pid_loop::PID;
74 ///
75 /// let mut controller = PID::<f32, 1>::new(0.7, 0.034, 0.084, 0.1, 0.0);
76 /// ```
77 pub fn new(
78 kp: impl Into<F>,
79 ki: impl Into<F>,
80 kd: impl Into<F>,
81 kf: impl Into<F>,
82 kv: impl Into<F>,
83 ) -> Self {
84 assert!(W > 0);
85 Self {
86 kp: kp.into(),
87 ki: ki.into(),
88 kd: kd.into(),
89 kf: kf.into(),
90 kv: kv.into(),
91 last_sp: F::default(),
92 errors: [F::default(); W],
93 last_error_idx: usize::default(),
94 err_history: F::default(),
95 }
96 }
97
98 /// Reset controller internal state.
99 ///
100 /// # Examples
101 ///
102 /// ```
103 /// #![allow(unused_assignments)]
104 /// use pid_loop::PID;
105 ///
106 /// let target = 30.0;
107 /// let mut controller = PID::<f32, 1>::new(0.7, 0.034, 0.084, 0.1, 0.0);
108 /// controller.next(target, 42.0);
109 /// controller.reset();
110 ///
111 /// ```
112 pub fn reset(&mut self) {
113 self.last_sp = F::default();
114 self.err_history = F::default();
115 self.errors = [F::default(); W];
116 self.last_error_idx = usize::default();
117 }
118
119 /// Push next measurement into the controller and return correction.
120 ///
121 /// # Examples
122 ///
123 /// ```
124 /// #![allow(unused_assignments)]
125 /// use pid_loop::PID;
126 ///
127 /// let target = 30.0;
128 /// let mut controller = PID::<f64, 1>::new(0.7, 0.034, 0.084, 0.1, 0.1);
129 /// let correction = controller.next(target, 42.0);
130 /// ```
131 pub fn next(&mut self, sp: impl Into<F>, fb: impl Into<F>) -> F {
132 let sp = sp.into();
133 let fb = fb.into();
134 let error = sp - fb;
135
136 let error_delta = error - self.errors[self.last_error_idx];
137 self.push_error(error);
138
139 let sp_delta = sp - self.last_sp;
140 self.last_sp = sp;
141
142 let p = self.kp * error;
143 let i = self.ki * self.err_history;
144 let d = self.kd * error_delta;
145 let f = self.kf * sp_delta;
146 let v = self.kv * fb;
147
148 p + i + d + f + v
149 }
150
151 fn push_error(&mut self, error: F) {
152 self.last_error_idx += 1;
153 if self.last_error_idx >= W {
154 self.last_error_idx = 0;
155 }
156
157 self.err_history = self.err_history - self.errors[self.last_error_idx];
158 self.err_history = self.err_history + error;
159 self.errors[self.last_error_idx] = error;
160 }
161}