thermostat/src/pid.rs

158 lines
4.0 KiB
Rust
Raw Normal View History

2020-09-24 07:18:33 +08:00
use serde::{Serialize, Deserialize};
use uom::si::{
f64::Time,
time::second,
};
2020-09-24 07:18:33 +08:00
2020-09-25 03:35:15 +08:00
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
2020-03-19 04:51:30 +08:00
pub struct Parameters {
pub kp: f32,
pub ki: f32,
pub kd: f32,
pub output_min: f32,
pub output_max: f32,
pub integral_min: f32,
pub integral_max: f32
2020-03-19 04:51:30 +08:00
}
2020-03-20 01:34:57 +08:00
impl Default for Parameters {
fn default() -> Self {
Parameters {
2020-09-18 06:41:32 +08:00
kp: 1.5,
ki: 0.1,
kd: 150.0,
2020-03-20 01:34:57 +08:00
output_min: 0.0,
2020-09-18 06:41:32 +08:00
output_max: 2.0,
integral_min: -10.0,
integral_max: 10.0,
2020-03-20 01:34:57 +08:00
}
}
}
2020-03-19 04:51:30 +08:00
#[derive(Clone)]
pub struct Controller {
2020-03-20 05:00:22 +08:00
pub parameters: Parameters,
pub target: f64,
integral: f64,
2020-03-20 05:00:22 +08:00
last_input: Option<f64>,
pub last_output: Option<f64>,
2020-03-19 04:51:30 +08:00
}
impl Controller {
pub const fn new(parameters: Parameters) -> Controller {
Controller {
parameters: parameters,
target: 0.0,
last_input: None,
2020-03-20 05:00:22 +08:00
integral: 0.0,
last_output: None,
2020-03-19 04:51:30 +08:00
}
}
pub fn update(&mut self, input: f64, time_delta: Time) -> f64 {
let time_delta = time_delta.get::<second>();
2020-09-18 06:09:30 +08:00
// error
let error = input - self.target;
2020-03-19 04:51:30 +08:00
2020-09-18 06:09:30 +08:00
// partial
let p = f64::from(self.parameters.kp) * error;
2020-03-19 04:51:30 +08:00
// integral
self.integral += f64::from(self.parameters.ki) * error * time_delta;
if self.integral < self.parameters.integral_min.into() {
self.integral = self.parameters.integral_min.into();
2020-03-19 04:51:30 +08:00
}
if self.integral > self.parameters.integral_max.into() {
self.integral = self.parameters.integral_max.into();
2020-03-19 04:51:30 +08:00
}
let i = self.integral;
2020-03-19 04:51:30 +08:00
2020-09-18 06:09:30 +08:00
// derivative
2020-03-19 04:51:30 +08:00
let d = match self.last_input {
None =>
0.0,
Some(last_input) =>
f64::from(self.parameters.kd) * (input - last_input) / time_delta,
2020-03-19 04:51:30 +08:00
};
self.last_input = Some(input);
2020-09-18 06:09:30 +08:00
// output
2020-03-19 04:51:30 +08:00
let mut output = p + i + d;
if output < self.parameters.output_min.into() {
output = self.parameters.output_min.into();
2020-03-19 04:51:30 +08:00
}
if output > self.parameters.output_max.into() {
output = self.parameters.output_max.into();
2020-03-19 04:51:30 +08:00
}
2020-03-20 05:00:22 +08:00
self.last_output = Some(output);
2020-03-19 04:51:30 +08:00
output
}
2020-10-01 02:06:47 +08:00
pub fn summary(&self, channel: usize) -> Summary {
Summary {
channel,
parameters: self.parameters.clone(),
target: self.target,
integral: self.integral,
}
}
}
#[derive(Clone, Serialize, Deserialize)]
pub struct Summary {
channel: usize,
parameters: Parameters,
target: f64,
integral: f64,
}
2020-03-19 04:51:30 +08:00
#[cfg(test)]
mod test {
use super::*;
const PARAMETERS: Parameters = Parameters {
kp: 0.055,
ki: 0.005,
kd: 0.04,
output_min: -10.0,
output_max: 10.0,
integral_min: -100.0,
integral_max: 100.0,
};
#[test]
fn test_controller() {
const DEFAULT: f64 = 0.0;
const TARGET: f64 = -1234.56;
const ERROR: f64 = 0.01;
2020-03-19 04:51:30 +08:00
const DELAY: usize = 10;
let mut pid = Controller::new(PARAMETERS.clone());
pid.target = TARGET;
2020-03-19 04:51:30 +08:00
let mut values = [DEFAULT; DELAY];
let mut t = 0;
let mut total_t = 0;
let target = (TARGET - ERROR)..=(TARGET + ERROR);
while !values.iter().all(|value| target.contains(value)) {
let next_t = (t + 1) % DELAY;
// Feed the oldest temperature
let output = pid.update(values[next_t]);
// Overwrite oldest with previous temperature - output
values[next_t] = values[t] - output;
2020-03-19 04:51:30 +08:00
t = next_t;
total_t += 1;
}
}
2020-10-01 02:06:47 +08:00
#[test]
fn summary_to_json() {
let mut pid = Controller::new(PARAMETERS.clone());
pid.target = 30.0 / 1.1;
let buf = pid.summary(0).to_json().unwrap();
assert_eq!(buf[0], b'{');
assert_eq!(buf[buf.len() - 1], b'}');
}
2020-03-19 04:51:30 +08:00
}