From 5c84b7438bf80fc252b91487d5d2fbda30afacfa Mon Sep 17 00:00:00 2001 From: topquark12 Date: Fri, 8 Jan 2021 11:25:01 +0800 Subject: [PATCH 1/7] Integral rescaling --- src/main.rs | 4 ++-- src/pid.rs | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 571d04f..c745a8f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -332,8 +332,8 @@ fn main() -> ! { pid.target = value, KP => pid.parameters.kp = value as f32, - KI => - pid.parameters.ki = value as f32, + KI => + pid.update_ki(value as f32), KD => pid.parameters.kd = value as f32, OutputMin => diff --git a/src/pid.rs b/src/pid.rs index 6001c54..9da0246 100644 --- a/src/pid.rs +++ b/src/pid.rs @@ -109,6 +109,16 @@ impl Controller { integral: self.integral, } } + + pub fn update_ki(&mut self, new_ki: f32) { + if new_ki == 0.0 { + self.integral = 0.0; + } else { + // Rescale integral with changes to kI, aka "Bumpless operation" + self.integral = f64::from(self.parameters.ki) * self.integral / f64::from(new_ki); + } + self.parameters.ki = new_ki; + } } #[derive(Clone, Serialize, Deserialize)] -- 2.42.0 From 1539b624bd957d7020a051b77c6534cac2553899 Mon Sep 17 00:00:00 2001 From: topquark12 Date: Fri, 8 Jan 2021 11:31:33 +0800 Subject: [PATCH 2/7] pid: more sensible default parameters --- src/pid.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pid.rs b/src/pid.rs index 9da0246..74ba37f 100644 --- a/src/pid.rs +++ b/src/pid.rs @@ -25,10 +25,10 @@ pub struct Parameters { impl Default for Parameters { fn default() -> Self { Parameters { - kp: 1.5, - ki: 1.0, - kd: 1.5, - output_min: 0.0, + kp: 0.0, + ki: 0.0, + kd: 0.0, + output_min: -2.0, output_max: 2.0, integral_min: -10.0, integral_max: 10.0, -- 2.42.0 From 3ba2cc9ddc70908ba3f7e7a3e22d90dcb744ecfd Mon Sep 17 00:00:00 2001 From: topquark12 Date: Fri, 8 Jan 2021 15:52:35 +0800 Subject: [PATCH 3/7] channels: voltage measurement refalect actual voltage across TEC --- src/channels.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/channels.rs b/src/channels.rs index 4ff5984..d5ef47d 100644 --- a/src/channels.rs +++ b/src/channels.rs @@ -438,7 +438,7 @@ impl Channels { dac_feedback: self.read_dac_feedback(channel), i_tec, tec_i, - tec_u_meas: self.read_tec_u_meas(channel), + tec_u_meas: (self.read_tec_u_meas(channel) - ElectricPotential::new::(1.5)) * 4.0, pid_output, } } -- 2.42.0 From 3067b356c541247145daab9f4861658a0659c700 Mon Sep 17 00:00:00 2001 From: topquark12 Date: Fri, 8 Jan 2021 16:18:20 +0800 Subject: [PATCH 4/7] channels: add methods to retrieve actual voltage and current of TEC --- src/channels.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/channels.rs b/src/channels.rs index d5ef47d..ccdf8f7 100644 --- a/src/channels.rs +++ b/src/channels.rs @@ -364,6 +364,16 @@ impl Channels { (duty * max, max) } + // Get actual current passing through TEC + pub fn get_tec_i(&mut self, channel: usize) -> ElectricCurrent { + (self.read_itec(channel) - self.read_vref(channel)) / ElectricalResistance::new::(0.4) + } + + // Get actual voltage across TEC + pub fn get_tec_v(&mut self, channel: usize) -> ElectricPotential { + (self.read_tec_u_meas(channel) - ElectricPotential::new::(1.5)) * 4.0 + } + fn set_pwm(&mut self, channel: usize, pin: PwmPin, duty: f64) -> f64 { fn set>(pin: &mut P, duty: f64) -> f64 { let max = pin.get_max_duty(); @@ -417,7 +427,7 @@ impl Channels { let vref = self.channel_state(channel).vref; let (i_set, _) = self.get_i(channel); let i_tec = self.read_itec(channel); - let tec_i = (i_tec - vref) / ElectricalResistance::new::(0.4); + let tec_i = self.get_tec_i(channel); let (dac_value, _) = self.get_dac(channel); let state = self.channel_state(channel); let pid_output = state.pid.last_output.map(|last_output| @@ -438,7 +448,7 @@ impl Channels { dac_feedback: self.read_dac_feedback(channel), i_tec, tec_i, - tec_u_meas: (self.read_tec_u_meas(channel) - ElectricPotential::new::(1.5)) * 4.0, + tec_u_meas: self.get_tec_v(channel), pid_output, } } -- 2.42.0 From 193d54a0a650b1ec871e9c216c77d00642ffaba8 Mon Sep 17 00:00:00 2001 From: topquark12 Date: Mon, 11 Jan 2021 14:00:52 +0800 Subject: [PATCH 5/7] pid: anti-windup when compliance voltage reached --- src/channel_state.rs | 6 ++++-- src/channels.rs | 4 ++-- src/pid.rs | 16 ++++++++++++---- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/channel_state.rs b/src/channel_state.rs index faa7ae8..b93dd75 100644 --- a/src/channel_state.rs +++ b/src/channel_state.rs @@ -3,11 +3,13 @@ use uom::si::{ f64::{ ElectricPotential, ElectricalResistance, + ElectricCurrent, ThermodynamicTemperature, Time, }, electric_potential::volt, electrical_resistance::ohm, + // electric_current::ampere, thermodynamic_temperature::degree_celsius, time::millisecond, }; @@ -66,10 +68,10 @@ impl ChannelState { } /// Update PID state on ADC input, calculate new DAC output - pub fn update_pid(&mut self) -> Option { + pub fn update_pid(&mut self, current: ElectricCurrent) -> Option { let temperature = self.get_temperature()? .get::(); - let pid_output = self.pid.update(temperature, self.get_adc_interval()); + let pid_output = self.pid.update(temperature, self.get_adc_interval(), current); Some(pid_output) } diff --git a/src/channels.rs b/src/channels.rs index ccdf8f7..037a8b2 100644 --- a/src/channels.rs +++ b/src/channels.rs @@ -73,10 +73,10 @@ impl Channels { pub fn poll_adc(&mut self, instant: Instant) -> Option { self.adc.data_ready().unwrap().map(|channel| { let data = self.adc.read_data().unwrap(); - + let current = self.get_tec_i(channel.into()); let state = self.channel_state(channel); state.update(instant, data); - match state.update_pid() { + match state.update_pid(current) { Some(pid_output) if state.pid_engaged => { // Forward PID output to i_set DAC self.set_i(channel.into(), ElectricCurrent::new::(pid_output)); diff --git a/src/pid.rs b/src/pid.rs index 74ba37f..b2a3f61 100644 --- a/src/pid.rs +++ b/src/pid.rs @@ -1,9 +1,13 @@ use serde::{Serialize, Deserialize}; use uom::si::{ - f64::Time, + f64::{Time, ElectricCurrent}, time::second, + electric_current::ampere, }; +/// Allowable current error for integral accumulation +const CURRENT_ERROR_MAX: f64 = 0.1; + #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Parameters { /// Gain coefficient for proportional term @@ -56,7 +60,7 @@ impl Controller { } } - pub fn update(&mut self, input: f64, time_delta: Time) -> f64 { + pub fn update(&mut self, input: f64, time_delta: Time, current: ElectricCurrent) -> f64 { let time_delta = time_delta.get::(); // error @@ -67,8 +71,12 @@ impl Controller { // integral if let Some(last_output_val) = self.last_output { + let electric_current_error = ElectricCurrent::new::(last_output_val) - current; // anti integral windup - if last_output_val < self.parameters.output_max.into() && last_output_val > self.parameters.output_min.into() { + if last_output_val < self.parameters.output_max.into() && + last_output_val > self.parameters.output_min.into() && + electric_current_error < ElectricCurrent::new::(CURRENT_ERROR_MAX) && + electric_current_error > -ElectricCurrent::new::(CURRENT_ERROR_MAX) { self.integral += error * time_delta; } } @@ -168,7 +176,7 @@ mod test { while !values.iter().all(|value| target.contains(value)) && total_t < CYCLE_LIMIT { let next_t = (t + 1) % DELAY; // Feed the oldest temperature - let output = pid.update(values[next_t], Time::new::(1.0)); + let output = pid.update(values[next_t], Time::new::(1.0), values[next_t]); // Overwrite oldest with previous temperature - output values[next_t] = values[t] + output - (values[t] - DEFAULT) * LOSS; t = next_t; -- 2.42.0 From 3433881d0f26d58fe43d9e720a4bcf68cb074f46 Mon Sep 17 00:00:00 2001 From: topquark12 Date: Mon, 11 Jan 2021 14:10:50 +0800 Subject: [PATCH 6/7] remove dead code --- src/channel_state.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/channel_state.rs b/src/channel_state.rs index b93dd75..4b1c459 100644 --- a/src/channel_state.rs +++ b/src/channel_state.rs @@ -9,7 +9,6 @@ use uom::si::{ }, electric_potential::volt, electrical_resistance::ohm, - // electric_current::ampere, thermodynamic_temperature::degree_celsius, time::millisecond, }; -- 2.42.0 From 9e4d06fdbc46737e7864522630330d0e6a743155 Mon Sep 17 00:00:00 2001 From: topquark12 Date: Mon, 11 Jan 2021 16:24:43 +0800 Subject: [PATCH 7/7] clarify comment --- src/channels.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/channels.rs b/src/channels.rs index 037a8b2..2ddf7d8 100644 --- a/src/channels.rs +++ b/src/channels.rs @@ -364,12 +364,12 @@ impl Channels { (duty * max, max) } - // Get actual current passing through TEC + // Get current passing through TEC pub fn get_tec_i(&mut self, channel: usize) -> ElectricCurrent { (self.read_itec(channel) - self.read_vref(channel)) / ElectricalResistance::new::(0.4) } - // Get actual voltage across TEC + // Get voltage across TEC pub fn get_tec_v(&mut self, channel: usize) -> ElectricPotential { (self.read_tec_u_meas(channel) - ElectricPotential::new::(1.5)) * 4.0 } -- 2.42.0