Compare commits

...

2 Commits

Author SHA1 Message Date
linuswck 388e6725ef thermostat: Clear alarm when settings are loaded 2024-10-17 17:36:17 +08:00
linuswck f4c761c5ca pid: Scale PID Parameters with Sampling Rate
- Breaking changes
2024-10-17 17:36:06 +08:00
4 changed files with 51 additions and 18 deletions

View File

@ -85,21 +85,22 @@ async def ld_thermostat_cfg(kirdy: Kirdy):
await kirdy.thermostat.set_temp_mon_upper_limit(70)
await kirdy.thermostat.set_temp_mon_lower_limit(0)
# Configure the thermostat PID parameters.
# You can configure the PID parameter by the included autotune script.
await kirdy.thermostat.set_temperature_setpoint(25)
await kirdy.thermostat.set_pid_kp(0.15668282198105507)
await kirdy.thermostat.set_pid_ki(0.002135962407793784)
await kirdy.thermostat.set_pid_kd(0.829254515277143)
await kirdy.thermostat.set_pid_output_max(1.0)
await kirdy.thermostat.set_pid_output_min(-1.0)
# Configure the thermostat ADC Filter Setting / PID Update Rate / Report Rate.
# The ADC sampling rate determines the report and pid update rate.
# The chosen filter and sampling rate affects the noise of the readings.
# For details, please refer to the AD7172 datasheet.
await kirdy.thermostat.config_temp_adc_filter(FilterConfig.Sinc5Sinc1With50hz60HzRejection.f16sps)
# Configure the thermostat PID parameters.
# You can configure the PID parameter by the included autotune script.
# You should configure the filter sampling rate first before applying pid parameters.
await kirdy.thermostat.set_temperature_setpoint(25)
await kirdy.thermostat.set_pid_kp(0.15668282198105507)
await kirdy.thermostat.set_pid_ki(0.0001281321)
await kirdy.thermostat.set_pid_kd(13.82367)
await kirdy.thermostat.set_pid_output_max(1.0)
await kirdy.thermostat.set_pid_output_min(-1.0)
# Configure thermostat to run in PID control mode
await kirdy.thermostat.set_pid_control_mode()

View File

@ -45,6 +45,7 @@ class PIDAutotune:
raise ValueError('setpoint must be specified')
self._inputs = deque(maxlen=round(lookback / sampletime))
self._sampletime = sampletime
self._setpoint = setpoint
self._outputstep = out_step
self._noiseband = noiseband
@ -69,6 +70,7 @@ class PIDAutotune:
self._out_min = -step
self._noiseband = noiseband
self._inputs = deque(maxlen=round(lookback / sampletime))
self._sampletime = sampletime
def setReady(self):
self._state = PIDAutotuneState.STATE_READY
@ -96,8 +98,8 @@ class PIDAutotune:
def get_tec_pid (self):
divisors = self._tuning_rules["tyreus-luyben"]
kp = self._Ku * divisors[0]
ki = divisors[1] * self._Ku / self._Pu
kd = divisors[2] * self._Ku * self._Pu
ki = divisors[1] * self._Ku / self._Pu / (1 / self._sampletime)
kd = divisors[2] * self._Ku * self._Pu * (1 / self._sampletime)
return kp, ki, kd
def get_pid_parameters(self, tuning_rule='ziegler-nichols'):
@ -109,8 +111,8 @@ class PIDAutotune:
"""
divisors = self._tuning_rules[tuning_rule]
kp = self._Ku * divisors[0]
ki = divisors[1] * self._Ku / self._Pu
kd = divisors[2] * self._Ku * self._Pu
ki = divisors[1] * self._Ku / self._Pu / (1 / self._sampletime)
kd = divisors[2] * self._Ku * self._Pu * (1 / self._sampletime)
return PIDAutotune.PIDParams(kp, ki, kd)
def run(self, input_val, time_input):

View File

@ -142,16 +142,16 @@ impl PidState {
self.controller.parameters = pid_params;
}
pub fn set_pid_params(&mut self, param: PidSettings, val: f32) {
pub fn set_pid_params(&mut self, param: PidSettings, val: f32, curr_rate: f32) {
match param {
PidSettings::Kp => {
self.controller.parameters.kp = val;
}
PidSettings::Ki => {
self.controller.parameters.ki = val;
self.controller.parameters.ki = val * curr_rate;
}
PidSettings::Kd => {
self.controller.parameters.kd = val;
self.controller.parameters.kd = val / curr_rate;
}
PidSettings::Min => {
self.controller.parameters.output_min = val;
@ -162,6 +162,18 @@ impl PidState {
}
}
pub fn update_pid_params_with_new_sampling_rate(&mut self, old_rate: f32, new_rate: f32) {
self.controller.parameters.ki = self.controller.parameters.ki * old_rate / new_rate;
self.controller.parameters.kd = self.controller.parameters.kd * new_rate / old_rate;
}
pub fn get_abs_pid_params(&mut self, curr_rate: f32) -> Parameters {
let mut pid_params = self.controller.parameters.clone();
pid_params.ki = self.controller.parameters.ki / curr_rate;
pid_params.kd = self.controller.parameters.kd * curr_rate;
pid_params
}
pub fn reset_pid_state(&mut self) {
self.controller.u1 = 0.0;
self.controller.x1 = 0.0;

View File

@ -453,7 +453,8 @@ impl Thermostat {
}
pub fn set_pid(&mut self, param: PidSettings, val: f32) {
self.pid_ctrl_ch0.set_pid_params(param, val);
let curr_rate = self.ad7172.get_filter_type_and_rate(0).unwrap().3;
self.pid_ctrl_ch0.set_pid_params(param, val, curr_rate);
}
pub fn set_sh_beta(&mut self, beta: f32) {
@ -492,20 +493,35 @@ impl Thermostat {
}
pub fn set_temp_adc_sinc5_sinc1_filter(&mut self, index: u8, odr: ad7172::SingleChODR) {
let old_rate = self.ad7172.get_filter_type_and_rate(0).unwrap().3;
let new_rate = odr.output_rate().unwrap();
self.pid_ctrl_ch0
.update_pid_params_with_new_sampling_rate(old_rate, new_rate);
self.ad7172.set_sinc5_sinc1_filter(index, odr).unwrap();
}
pub fn set_temp_adc_sinc3_filter(&mut self, index: u8, odr: ad7172::SingleChODR) {
let old_rate = self.ad7172.get_filter_type_and_rate(0).unwrap().3;
let new_rate = odr.output_rate().unwrap();
self.pid_ctrl_ch0
.update_pid_params_with_new_sampling_rate(old_rate, new_rate);
self.ad7172.set_sinc3_filter(index, odr).unwrap();
}
pub fn set_temp_adc_sinc5_sinc1_with_postfilter(&mut self, index: u8, odr: ad7172::PostFilter) {
let old_rate = self.ad7172.get_filter_type_and_rate(0).unwrap().3;
let new_rate = odr.output_rate().unwrap();
self.pid_ctrl_ch0
.update_pid_params_with_new_sampling_rate(old_rate, new_rate);
self.ad7172
.set_sinc5_sinc1_with_50hz_60hz_rejection(index, odr)
.unwrap();
}
pub fn set_temp_adc_sinc3_fine_filter(&mut self, index: u8, rate: f32) {
let old_rate = self.ad7172.get_filter_type_and_rate(0).unwrap().3;
self.pid_ctrl_ch0
.update_pid_params_with_new_sampling_rate(old_rate, rate);
self.ad7172.set_sinc3_fine_filter(index, rate).unwrap();
}
@ -548,7 +564,7 @@ impl Thermostat {
pid_engaged: self.get_pid_engaged(),
temperature_setpoint: self.pid_ctrl_ch0.get_pid_setpoint(),
tec_settings: self.get_tec_settings(),
pid_params: self.get_pid_settings(),
pid_params: self.pid_ctrl_ch0.get_abs_pid_params(temp_adc_settings.rate.unwrap()),
temp_adc_settings: temp_adc_settings,
temp_mon_settings: self.get_temp_mon_settings(),
thermistor_params: self.get_steinhart_hart(),
@ -580,10 +596,12 @@ impl Thermostat {
self.set_pid_engaged(settings.pid_engaged);
self.pid_ctrl_ch0.apply_pid_params(settings.pid_params);
self.pid_ctrl_ch0.update_pid_params_with_new_sampling_rate(settings.temp_adc_settings.rate.unwrap(), 1.0);
self.set_temperature_setpoint(settings.temperature_setpoint);
if !settings.pid_engaged {
self.set_i(settings.tec_settings.i_set.value);
}
self.clear_temp_mon_alarm();
self.set_default_pwr_on(settings.default_pwr_on);
if settings.default_pwr_on {
self.power_up();