pid: Scale PID Parameters with Sampling Rate
- Breaking changes
This commit is contained in:
parent
7e0ec09d07
commit
e922712eb7
|
@ -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_upper_limit(70)
|
||||||
await kirdy.thermostat.set_temp_mon_lower_limit(0)
|
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.
|
# Configure the thermostat ADC Filter Setting / PID Update Rate / Report Rate.
|
||||||
# The ADC sampling rate determines the report and pid update rate.
|
# The ADC sampling rate determines the report and pid update rate.
|
||||||
# The chosen filter and sampling rate affects the noise of the readings.
|
# The chosen filter and sampling rate affects the noise of the readings.
|
||||||
# For details, please refer to the AD7172 datasheet.
|
# For details, please refer to the AD7172 datasheet.
|
||||||
await kirdy.thermostat.config_temp_adc_filter(FilterConfig.Sinc5Sinc1With50hz60HzRejection.f16sps)
|
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
|
# Configure thermostat to run in PID control mode
|
||||||
await kirdy.thermostat.set_pid_control_mode()
|
await kirdy.thermostat.set_pid_control_mode()
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,7 @@ class PIDAutotune:
|
||||||
raise ValueError('setpoint must be specified')
|
raise ValueError('setpoint must be specified')
|
||||||
|
|
||||||
self._inputs = deque(maxlen=round(lookback / sampletime))
|
self._inputs = deque(maxlen=round(lookback / sampletime))
|
||||||
|
self._sampletime = sampletime
|
||||||
self._setpoint = setpoint
|
self._setpoint = setpoint
|
||||||
self._outputstep = out_step
|
self._outputstep = out_step
|
||||||
self._noiseband = noiseband
|
self._noiseband = noiseband
|
||||||
|
@ -69,6 +70,7 @@ class PIDAutotune:
|
||||||
self._out_min = -step
|
self._out_min = -step
|
||||||
self._noiseband = noiseband
|
self._noiseband = noiseband
|
||||||
self._inputs = deque(maxlen=round(lookback / sampletime))
|
self._inputs = deque(maxlen=round(lookback / sampletime))
|
||||||
|
self._sampletime = sampletime
|
||||||
|
|
||||||
def setReady(self):
|
def setReady(self):
|
||||||
self._state = PIDAutotuneState.STATE_READY
|
self._state = PIDAutotuneState.STATE_READY
|
||||||
|
@ -96,8 +98,8 @@ class PIDAutotune:
|
||||||
def get_tec_pid (self):
|
def get_tec_pid (self):
|
||||||
divisors = self._tuning_rules["tyreus-luyben"]
|
divisors = self._tuning_rules["tyreus-luyben"]
|
||||||
kp = self._Ku * divisors[0]
|
kp = self._Ku * divisors[0]
|
||||||
ki = divisors[1] * self._Ku / self._Pu
|
ki = divisors[1] * self._Ku / self._Pu / (1 / self._sampletime)
|
||||||
kd = divisors[2] * self._Ku * self._Pu
|
kd = divisors[2] * self._Ku * self._Pu * (1 / self._sampletime)
|
||||||
return kp, ki, kd
|
return kp, ki, kd
|
||||||
|
|
||||||
def get_pid_parameters(self, tuning_rule='ziegler-nichols'):
|
def get_pid_parameters(self, tuning_rule='ziegler-nichols'):
|
||||||
|
@ -109,8 +111,8 @@ class PIDAutotune:
|
||||||
"""
|
"""
|
||||||
divisors = self._tuning_rules[tuning_rule]
|
divisors = self._tuning_rules[tuning_rule]
|
||||||
kp = self._Ku * divisors[0]
|
kp = self._Ku * divisors[0]
|
||||||
ki = divisors[1] * self._Ku / self._Pu
|
ki = divisors[1] * self._Ku / self._Pu / (1 / self._sampletime)
|
||||||
kd = divisors[2] * self._Ku * self._Pu
|
kd = divisors[2] * self._Ku * self._Pu * (1 / self._sampletime)
|
||||||
return PIDAutotune.PIDParams(kp, ki, kd)
|
return PIDAutotune.PIDParams(kp, ki, kd)
|
||||||
|
|
||||||
def run(self, input_val, time_input):
|
def run(self, input_val, time_input):
|
||||||
|
|
|
@ -142,16 +142,16 @@ impl PidState {
|
||||||
self.controller.parameters = pid_params;
|
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 {
|
match param {
|
||||||
PidSettings::Kp => {
|
PidSettings::Kp => {
|
||||||
self.controller.parameters.kp = val;
|
self.controller.parameters.kp = val;
|
||||||
}
|
}
|
||||||
PidSettings::Ki => {
|
PidSettings::Ki => {
|
||||||
self.controller.parameters.ki = val;
|
self.controller.parameters.ki = val * curr_rate;
|
||||||
}
|
}
|
||||||
PidSettings::Kd => {
|
PidSettings::Kd => {
|
||||||
self.controller.parameters.kd = val;
|
self.controller.parameters.kd = val / curr_rate;
|
||||||
}
|
}
|
||||||
PidSettings::Min => {
|
PidSettings::Min => {
|
||||||
self.controller.parameters.output_min = val;
|
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) {
|
pub fn reset_pid_state(&mut self) {
|
||||||
self.controller.u1 = 0.0;
|
self.controller.u1 = 0.0;
|
||||||
self.controller.x1 = 0.0;
|
self.controller.x1 = 0.0;
|
||||||
|
|
|
@ -453,7 +453,8 @@ impl Thermostat {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_pid(&mut self, param: PidSettings, val: f32) {
|
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) {
|
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) {
|
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();
|
self.ad7172.set_sinc5_sinc1_filter(index, odr).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_temp_adc_sinc3_filter(&mut self, index: u8, odr: ad7172::SingleChODR) {
|
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();
|
self.ad7172.set_sinc3_filter(index, odr).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_temp_adc_sinc5_sinc1_with_postfilter(&mut self, index: u8, odr: ad7172::PostFilter) {
|
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
|
self.ad7172
|
||||||
.set_sinc5_sinc1_with_50hz_60hz_rejection(index, odr)
|
.set_sinc5_sinc1_with_50hz_60hz_rejection(index, odr)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_temp_adc_sinc3_fine_filter(&mut self, index: u8, rate: f32) {
|
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();
|
self.ad7172.set_sinc3_fine_filter(index, rate).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -548,7 +564,7 @@ impl Thermostat {
|
||||||
pid_engaged: self.get_pid_engaged(),
|
pid_engaged: self.get_pid_engaged(),
|
||||||
temperature_setpoint: self.pid_ctrl_ch0.get_pid_setpoint(),
|
temperature_setpoint: self.pid_ctrl_ch0.get_pid_setpoint(),
|
||||||
tec_settings: self.get_tec_settings(),
|
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_adc_settings: temp_adc_settings,
|
||||||
temp_mon_settings: self.get_temp_mon_settings(),
|
temp_mon_settings: self.get_temp_mon_settings(),
|
||||||
thermistor_params: self.get_steinhart_hart(),
|
thermistor_params: self.get_steinhart_hart(),
|
||||||
|
@ -580,6 +596,7 @@ impl Thermostat {
|
||||||
|
|
||||||
self.set_pid_engaged(settings.pid_engaged);
|
self.set_pid_engaged(settings.pid_engaged);
|
||||||
self.pid_ctrl_ch0.apply_pid_params(settings.pid_params);
|
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);
|
self.set_temperature_setpoint(settings.temperature_setpoint);
|
||||||
if !settings.pid_engaged {
|
if !settings.pid_engaged {
|
||||||
self.set_i(settings.tec_settings.i_set.value);
|
self.set_i(settings.tec_settings.i_set.value);
|
||||||
|
|
Loading…
Reference in New Issue