6 #define M_PI 3.1415926535897932384626433 12 static const char *
const TAG =
"pid.autotune";
81 ESP_LOGW(TAG,
"%s: Setpoint changed during autotune! The result will not be accurate!", this->
id_.c_str());
85 float error = setpoint - process_variable;
86 const uint32_t now =
millis();
95 ESP_LOGV(TAG,
"%s: Not enough data yet for autotuner", this->
id_.c_str());
101 if (!zc_symmetrical || !amplitude_convergent) {
104 if (zc_symmetrical) {
105 ESP_LOGVV(TAG,
"%s: ZC is not symmetrical", this->
id_.c_str());
107 if (amplitude_convergent) {
108 ESP_LOGVV(TAG,
"%s: Amplitude is not convergent", this->
id_.c_str());
111 ESP_LOGVV(TAG,
"%s: >", this->
id_.c_str());
125 ESP_LOGI(TAG,
"%s: PID Autotune finished!", this->
id_.c_str());
129 ESP_LOGVV(TAG,
" Relay magnitude: %f", d);
130 this->
ku_ = 4.0f * d / float(M_PI * osc_ampl);
141 ESP_LOGI(TAG,
"%s: PID Autotune:", this->
id_.c_str());
142 ESP_LOGI(TAG,
" State: Succeeded!");
143 bool has_issue =
false;
145 ESP_LOGW(TAG,
" Could not reliably determine oscillation amplitude, PID parameters may be inaccurate!");
146 ESP_LOGW(TAG,
" Please make sure you eliminate all outside influences on the measured temperature.");
150 ESP_LOGW(TAG,
" Oscillation Frequency is not symmetrical. PID parameters may be inaccurate!");
153 " This is usually because the heat and cool processes do not change the temperature at the same rate.");
155 " Please try reducing the positive_output value (or increase negative_output in case of a cooler)");
159 ESP_LOGI(TAG,
" All checks passed!");
163 ESP_LOGI(TAG,
" Calculated PID parameters (\"Ziegler-Nichols PID\" rule):");
165 ESP_LOGI(TAG,
" control_parameters:");
166 ESP_LOGI(TAG,
" kp: %.5f", fac.kp);
167 ESP_LOGI(TAG,
" ki: %.5f", fac.ki);
168 ESP_LOGI(TAG,
" kd: %.5f", fac.kd);
170 ESP_LOGI(TAG,
" Please copy these values into your YAML configuration! They will reset on the next reboot.");
174 ESP_LOGV(TAG,
" Ku: %f, Pu: %f", this->
ku_, this->
pu_);
176 ESP_LOGD(TAG,
" Alternative Rules:");
178 print_rule_(
"Ziegler-Nichols PI", 0.45f, 0.54f, 0.0f);
179 print_rule_(
"Pessen Integral PID", 0.7f, 1.75f, 0.105f);
180 print_rule_(
"Some Overshoot PID", 0.333f, 0.667f, 0.111f);
181 print_rule_(
"No Overshoot PID", 0.2f, 0.4f, 0.0625f);
182 ESP_LOGI(TAG,
"%s: Autotune completed", this->
id_.c_str());
186 ESP_LOGD(TAG,
"%s: PID Autotune:", this->
id_.c_str());
187 ESP_LOGD(TAG,
" Autotune is still running!");
189 ESP_LOGD(TAG,
" Stats so far:");
197 float kp = kp_factor *
ku_;
198 float ki = ki_factor * ku_ /
pu_;
199 float kd = kd_factor * ku_ *
pu_;
208 ESP_LOGD(TAG,
" Rule '%s':", name);
209 ESP_LOGD(TAG,
" kp: %.5f, ki: %.5f, kd: %.5f", fac.kp, fac.ki, fac.kd);
214 if (this->
state == RELAY_FUNCTION_INIT) {
215 bool pos = error > this->noiseband;
216 state = pos ? RELAY_FUNCTION_POSITIVE : RELAY_FUNCTION_NEGATIVE;
219 if (this->
state == RELAY_FUNCTION_POSITIVE && error < -this->noiseband) {
221 this->
state = RELAY_FUNCTION_NEGATIVE;
223 }
else if (this->
state == RELAY_FUNCTION_NEGATIVE && error > this->noiseband) {
225 this->
state = RELAY_FUNCTION_POSITIVE;
229 float output =
state == RELAY_FUNCTION_POSITIVE ? output_positive : output_negative;
239 if (this->
state == FREQUENCY_DETECTOR_INIT) {
240 bool pos = error > this->noiseband;
241 state = pos ? FREQUENCY_DETECTOR_POSITIVE : FREQUENCY_DETECTOR_NEGATIVE;
244 bool had_crossing =
false;
245 if (this->
state == FREQUENCY_DETECTOR_POSITIVE && error < -this->noiseband) {
246 this->
state = FREQUENCY_DETECTOR_NEGATIVE;
248 }
else if (this->
state == FREQUENCY_DETECTOR_NEGATIVE && error > this->noiseband) {
249 this->
state = FREQUENCY_DETECTOR_POSITIVE;
255 if (this->last_zerocross != 0) {
256 uint32_t dt = now - this->last_zerocross;
257 this->zerocrossing_intervals.push_back(dt);
259 this->last_zerocross = now;
264 return this->zerocrossing_intervals.size() >= 2;
270 for (uint32_t v : this->zerocrossing_intervals)
273 float mean_value = sum / this->zerocrossing_intervals.size();
275 float mean_period = mean_value / 1000 * 2;
284 if (zerocrossing_intervals.empty())
286 uint32_t max_interval = zerocrossing_intervals[0];
287 uint32_t min_interval = zerocrossing_intervals[0];
288 for (uint32_t interval : zerocrossing_intervals) {
289 max_interval = std::max(max_interval, interval);
290 min_interval = std::min(min_interval, interval);
292 float ratio = min_interval / float(max_interval);
293 return ratio >= 0.66;
299 if (relay_state != last_relay_state) {
304 this->phase_maxs.push_back(phase_max);
309 this->phase_mins.push_back(phase_min);
312 this->phase_min = error;
313 this->phase_max = error;
315 this->last_relay_state = relay_state;
317 this->phase_min = std::min(this->phase_min, error);
318 this->phase_max = std::max(this->phase_max, error);
322 if (this->phase_maxs.size() > 7)
323 this->phase_maxs.erase(this->phase_maxs.begin());
324 if (this->phase_mins.size() > 7)
325 this->phase_mins.erase(this->phase_mins.begin());
331 return std::min(phase_mins.size(), phase_maxs.size()) >= 3;
334 float total_amplitudes = 0;
335 size_t total_amplitudes_n = 0;
336 for (
size_t i = 1; i < std::min(phase_mins.size(), phase_maxs.size()) - 1; i++) {
337 total_amplitudes += std::abs(phase_maxs[i] - phase_mins[i + 1]);
338 total_amplitudes_n++;
340 float mean_amplitude = total_amplitudes / total_amplitudes_n;
342 return mean_amplitude / 2.0f;
347 if (this->phase_mins.empty() || this->phase_maxs.empty())
350 float global_max = phase_maxs[0], global_min = phase_mins[0];
351 for (
auto v : this->phase_mins)
352 global_min = std::min(global_min, v);
353 for (
auto v : this->phase_maxs)
354 global_max = std::min(global_max, v);
355 float global_amplitude = (global_max - global_min) / 2.0f;
356 float mean_amplitude = this->get_mean_oscillation_amplitude();
357 return (mean_amplitude - global_amplitude) / (global_amplitude) < 0.05f;
struct esphome::pid::PIDAutotuner::OscillationAmplitudeDetector amplitude_detector_
struct esphome::pid::PIDAutotuner::OscillationFrequencyDetector frequency_detector_
float update(float error)
enum esphome::pid::PIDAutotuner::RelayFunction::RelayFunctionState state
optional< PIDResult > result_params
struct esphome::pid::PIDAutotuner::RelayFunction relay_function_
float get_mean_oscillation_period() const
PIDResult calculate_pid_(float kp_factor, float ki_factor, float kd_factor)
bool has_enough_data() const
uint32_t IRAM_ATTR HOT millis()
bool is_amplitude_convergent() const
PIDAutotuneResult update(float setpoint, float process_variable)
bool is_increase_decrease_symmetrical() const
uint32_t enough_data_phase_
std::vector< uint32_t > zerocrossing_intervals
float current_target_error() const
void update(float error, RelayFunction::RelayFunctionState relay_state)
void print_rule_(const char *name, float kp_factor, float ki_factor, float kd_factor)
Implementation of SPI Controller mode.
bool has_enough_data() const
enum esphome::pid::PIDAutotuner::State state_
float get_mean_oscillation_amplitude() const
void update(uint32_t now, float error)
PIDResult get_ziegler_nichols_pid_()