Алгоритмы релейного управления
Релейное управление – самый простой алгоритм из возможных, ведь у нас есть только два состояния – вкл и выкл. В этом уроке рассмотрим алгоритмы, которые сделают релейное управление более правильным, позволят сохранить "здоровье" реле и повысят точность регулирования. Начнём с самого простого и очевидного:
if (temp < 50.0) digitalWrite(relayPin, 1); else digitalWrite(relayPin, 0);
Данный код не нуждается в комментариях, он просто включает реле, когда условная температура ниже 50 градусов, и выключает, когда она выше. Если вызывать данный код без задержки или таймера - мы получим жуткий дребезг в момент включения и выключения реле, так как шумы измерений будут постоянно менять результат условия: Зелёный график - как раз состояние реле. Ужас! Из этого графика также следует неутешительный вывод: значения надо фильтровать, это сильно увеличит стабильность системы. Фильтры мы подробно разбирали вот в этом уроке. Первым шагом к созданию нормального релейного регулятора является период работы регулятора, его можно реализовать как задержкой (не рекомендуется, сами понимаете), так и таймером на миллис:
if (temp < 50.0) digitalWrite(relayPin, 1); else digitalWrite(relayPin, 0); delay(1000);
Период 1 секунда
static uint32_t tmr; if (millis() - tmr >= 1000) { tmr = millis(); if (temp < 50.0) digitalWrite(relayPin, 1); else digitalWrite(relayPin, 0); }
И ситуация в корне изменится, ведь даже при всём желании реле не сможет переключаться чаще, чем раз в секунду!
Гистерезис
Второй способ – гистерезис, позволяет ещё сильнее уменьшить количество переключений реле и даже избавиться от опроса по таймеру, что повышает реакцию системы на изменения, сохранив при этом хорошую устойчивость к помехам. Гистерезис разделяет установку на две, чуть меньше и чуть больше, на размер окна гистерезиса. Логика работы такова, что мы включаем реле на нагрев ниже нижней линии, и выключаем только выше верхней. То есть образуется область, внутри которой система грубо говоря движется по инерции от последнего переключения и переходит в новое состояние только при выходе из этой области. Понятное дело, что добавление гистерезиса сильно уменьшает не только количество переключений реле, но и точность, потому что мы своими собственными руками задаём область, точность внутри которой нам фактически безразлична, как и шумы измерения. В коде гистерезис можно реализовать так:
#define RELAY_PIN 2 float setpoint = 50.0; // установка float hyster = 2; // ширина окна гистерезиса // ............... static bool relayState = false; // обязательно глобальная или статическая! if (temp < (setpoint - hyster )) relayState = true; else if (temp > (setpoint + hyster )) relayState = false; digitalWrite(RELAY_PIN, relayState);
Для примера ниже используется библиотека thermistorMinim.h, скачать можно здесь.
Работает оно следующим образом: Отлично! Теперь нам не страшны шумы и износ реле, но мы фактически "раскачали" систему, заставляя её включаться чуть ниже заданной температуры, а выключаться - чуть выше. Колебания температуры стали сильнее, и это не очень приятно. Есть ли способ их избежать? Да!
Хитрый алгоритм с опережением
Рассмотренный далее алгоритм позволяет выключать и включать реле заранее, анализируя скорость изменения температуры. Если система чувствует, что температура растёт и может подняться выше установки - она выключает реле, и наоборот. Такой способ называется управлением с обратной связью по скорости изменения величины. Сама скорость изменения вводится в алгоритм как производная - изменение величины, делённое на время, за которое произошло изменение. Далее это изменение умножается на некий коэффициент, который играет роль коэффициента усиления и уникален для каждой системы, подбирается вручную в диапазоне от 0.001 до 1000, зависит от инертности системы и выбранного периода работы регулятора. Сам алгоритм можно представить в виде функции:
boolean relayGet() { float signal; if (k > 0) { float rate = (input - prevInput) / _dt_s; // производная от величины (величина/секунду) prevInput = input; signal = input + rate * k; } else { signal = input; } int8_t F = (sign(signal - setpoint - hysteresis / 2) + sign(signal - setpoint + hysteresis / 2)) / 2; if (F == 1) output = _direction; else if (F == -1) output = !_direction; return output; }
Данный алгоритм реализован у меня в библиотеке GyverRelay, вот тут на неё есть вся документация, примеры и прочее. Рассмотрим простой пример:
#define THERM_PIN 0 #define RELAY_PIN 2 #define SETPOINT 50.0 #define HYSTER 2 #include "thermistorMinim.h" // GND --- термистор --- A0 --- 10к --- 5V thermistor therm(THERM_PIN, 10000, 3950); // пин, сопротивление, бета-коэффициент #include "GyverRelay.h" // установка, гистерезис, направление регулирования GyverRelay regulator; // либо GyverRelay regulator(); без указания направления (будет REVERSE) void setup() { Serial.begin(9600); pinMode(RELAY_PIN, OUTPUT); // пин реле regulator.k = 8.5; // коэффициент обратной связи (подбирается по факту) regulator.setpoint = SETPOINT; // установка (ставим на SETPOINT градусов) regulator.hysteresis = HYSTER; // ширина гистерезиса } void loop() { regul(); debug(); } void regul() { static uint32_t tmr; if (millis() - tmr > 500) { tmr = millis(); regulator.input = therm.getTempAverage(); // сообщаем регулятору текущую температуру digitalWrite(RELAY_PIN, regulator.getResult()); // отправляем на реле (ОС работает по своему таймеру) } } void debug() { static uint32_t tmr; if (millis() - tmr > 50) { tmr = millis(); Serial.print(regulator.input); // фактическая Serial.print(','); Serial.print(SETPOINT); // гистерезис Serial.print(','); Serial.print(SETPOINT + HYSTER); // гистерезис Serial.print(','); Serial.print(SETPOINT - HYSTER); // гистерезис Serial.print(','); Serial.println(regulator.output * 2 + 30); // сост. реле } }
Как можно видеть, библиотека очень простая: настраиваем установку и гистерезис - система будет стараться удержать установку внутри него, то есть он играет больше роль окна точности. Далее передаём в регулятор значение с датчика, а он нам выдаёт 1 или 0 - включать или выключать реле. И всё! График на той же системе выглядит вот так, регулятор работает просто потрясающе! Такая точность даже и не снилась классическим схемам с гистерезисом. Как настроить: для быстрой системы, как у меня (обмотанный нихромом термистор), нужно выбирать время опроса датчика поменьше, то есть опрашивать датчик почаще. У меня хороший результат получился на 2 опросах в секунду. Для больших инерционных систем можно брать период в несколько секунд или даже минут. Алгоритм измеряет скорость изменения температуры за это время и умножает его на коэффициент. Если во время работы система перелетает через гистерезис, нужно увеличить коэффициент, чтобы реле выключалось и включалось раньше.
Полезные страницы
- Набор GyverKIT – большой стартовый набор Arduino моей разработки, продаётся в России
- Каталог ссылок на дешёвые Ардуины, датчики, модули и прочие железки с AliExpress у проверенных продавцов
- Подборка библиотек для Arduino, самых интересных и полезных, официальных и не очень
- Полная документация по языку Ардуино, все встроенные функции и макросы, все доступные типы данных
- Сборник полезных алгоритмов для написания скетчей: структура кода, таймеры, фильтры, парсинг данных
- Видео уроки по программированию Arduino с канала “Заметки Ардуинщика” – одни из самых подробных в рунете
- Поддержать автора за работу над уроками
- Обратная связь – сообщить об ошибке в уроке или предложить дополнение по тексту ([email protected])