Гистерезис - алгоритм управления увлажнителем по показаниям датчика влажности
- Войдите на сайт для отправки комментариев
И снова горячо привествую всех пользователей форума. Есть задача: нужно управлять переключением скоростей мотора вентилятора в зависимости от показаний датчика влажности. Алгоритм работы следующий
Если влажность выше заданной на более чем 4% - увлажнитель не работает, если влажность отличается на 3% от заданной в обе стороны, то включаем в режим малой производительности, если влажность ниже заданной на 4-6%, то включаем увлажнитель в режим средней производительности, и если влажность ниже заданной более чем на 7%, включаем максимальную производительность увлажнителя. Тупо в лоб, решил задачу таким кодом:
case 0: //автоматический режим выбора скорости, основанный на показаниях датчика { digitalWrite(LedAutoSpeed, HIGH); digitalWrite(LedLowSpeed, LOW); digitalWrite(LedMedSpeed, LOW); digitalWrite(LedHighSpeed, LOW); // вот тут автоматика началась HumCalc = HumTargetNew - HumCurrent; //вычисляем разницу между измеренной и заданной влажностью if (HumCalc <= -4) { //если влажность на 4% выше зааданной, то... digitalWrite(MotorFan, LOW); //...отключаем мотор вентилятора... digitalWrite(PumpWater, LOW); //...отключаем помпу... digitalWrite(MotorOsc, LOW); // ...отключаем OSC. } else if (HumCalc >= -3 && HumCalc <= 3) { //если влажность отличается на 3% от зааданной в обе стороны, то... analogWrite(MotorFan, FanLowSpeed); //...включаем мотор вентилятора на малую скорость... digitalWrite(PumpWater, HIGH); //...включаем помпу... if (StateOsc == true) { // проверяем был ли включен OSC, если да, то... digitalWrite(MotorOsc, HIGH); // ...включаем OSC } } else if (HumCalc >= 4 && HumCalc <= 6) { //если влажность ниже заданной на 4-6%, то... analogWrite(MotorFan, FanMedSpeed); //...включаем мотор вентилятора на среднюю скорость... digitalWrite(PumpWater, HIGH); //...включаем помпу... if (StateOsc == true) { // проверяем был ли включен OSC, если да, то... digitalWrite(MotorOsc, HIGH); // ...включаем OSC } } else if (HumCalc >= 7) { //если влажность ниже заданной более чем на 7%, то... analogWrite(MotorFan, FanHighSpeed); //...включаем мотор вентилятора на высокую скорость... digitalWrite(PumpWater, HIGH); //...включаем помпу... if (StateOsc == true) { // проверяем был ли включен OSC, если да, то... digitalWrite(MotorOsc, HIGH); // ...включаем OSC } } // вот тут автоматика закончилась break;
Но уже на этапе тестирования наступил на грабли - когда влажность колеблется на границе диапазона на 1-2% туда-сюда и соответствено дергаются туда-сюда скорости мотора, что не есть гуд. Гугленье дало несколько варианто решения проблемы:
1 - увеличение интервала опроса датчика - С этим поигрался, в принципе если поставить интервал опроса датчика раз в 5 минут, то все работает, но решение не слишком красивое на мой взгляд.
2 - считывание нескольких показаний и усреднение значения - В принципе это разновидность предыдущего варианта, каких либо плюсов данного решения не увидел.
3 - учет гистерезиса - вроде как и самое правильное решение, но алгоритм никак не рождается. Если бы была одна скорость, то решение просто как 5 копеек, создаем переменную HumHysteresis и присваиваем ей значение например 2, и:
// кошки on case 0: //автоматический режим выбора скорости, основанный на показаниях датчика - тест гистерезиса { digitalWrite(LedAutoSpeed, HIGH); digitalWrite(LedLowSpeed, LOW); digitalWrite(LedMedSpeed, LOW); digitalWrite(LedHighSpeed, LOW); if (HumCurrent >= HumTarget + HumHysteresis) { // если измеренная влажность больше целевой на велицину гистерезиса digitalWrite(MotorFan, LOW); // выключаем вентилятор digitalWrite(PumpWater, LOW); //... и помпу } else if (HumCurrent <= HumTarget - HumHysteresis) { // если измеренная влажность меньше целевой на велицину гистерезиса digitalWrite(MotorFan, HIGH); // включаем вентилятор digitalWrite(PumpWater, HIGH); //... и помпу } break; } // кошки off
А вот с тремя скоростями ну никак схема в эту конструкцию не укладывается.
Информируйте нас о новых идеях и продвижени работ.
Когда (и если) у Вас появятся вопросы, не стесняйтесь спрашивать.
Пока же позволю себе побрюзжать на строки 16, 22 и 28. Нафига проверять флаг, если потом не изменять его значения?
Пока же позволю себе побрюзжать на строки 16, 22 и 28. Нафига проверять флаг, если потом не изменять его значения?
Выскажу предположение, что этот флаг должны менять залетевшие извне нейтрино. Т.е. такой датчик из МК получается.
Можно после смены режима не ранее (например) 2 мин разрешать следующее, что в принципе сравнимо с более редким опросом датчика.
Ох, прошу прощения, мозги совсем вскипели к моменту отправки поста. Вопрос - люди добрые, подскажите как можно уложить гистересис в первый фрагмент кода или направьте в нужную сторону, если полет мыслей завел меня на тупиковый путь.
Этот флаг переворачивается в другом куске кода:
Т.е. моя логика такая - когда влажность выше заданной, а увлажнитель в режиме работы АВТО, исполнительные устройства (помпу, мотор вентилятора, мотор OSC) отключаются. Мотор OSC на момент отключения всех устройств может быть как включен, так и выключен. Соответственно в момент нажатия кнопки OSC мы включаем мотор и запоминаем его состояние, а в строках 16, 22 и 28 проверяем его состояние, и если мотор OSC работал до момента откючения исполнительных устройсв, то включаем его, иначе - не включаем. Считаете что ректальная реализация?
а в строках 16, 22 и 28 проверяем его состояние, и если мотор OSC работал до момента откючения исполнительных устройсв, то включаем его
Вот, включаете. Состояние мотора поменялось? Он ведь теперь включён! А кто будет это запоминать? То бишь флаг менять?
А зачем мне запоминать состояние мотора? Этот флаг говорит что то вроде "включать мотор когда увлажнитель работает", и от того что в автоматическом режиме мы этот мотор переодически отключаем (ну достигли мы заданной влажности и перевели увлажнитель в режим мониторинга влажности до достижении порогового значения, зачем нам этим мотором вхолостую маслать), указание "включить этот мотор" при старте вентилятора не теряет силы, просто мы его на время простоя временно отключаем. т.е. в этих строках мы проверяем "было ли дано задание на включение мотора", если да, то включаем, если нет - то не включаем. Возможно сумбурно изложил, но в этом месте код работает так, как я от него и жду.
UPD: Или имеете ввиду что не есть гуд дергать пин, к которому подключен мотор, при переходе со скорости на скорость? Я так понимаю что дерганья и не происходит, если PIN в состоянии HIGH, то при следующем digitalWrite HIGH для него ничего не поменяется, т.е. переключения фактически не будет. Или я не прав?
А зачем мне запоминать состояние мотора?
Ну, не надо, значит - не надо.
После включения мотора значение флага можно учитывать в формуле сравнения. Прибавляя или вычитая значение флага из померенной влажности можно автоматически поиметь гестирезис.
Не совсем уловил Вашу идею. Измеренная мощность заносится в пересенную HumCurrent, значение флага включения мотора будет 1 (я так понимаю речь идет про мотор вентилятора). Формула сравнения будет (HumTarget - (HumCurrent - 1)) <= -4 - и мы получим только сдвиг включения/выключения на 1%, а никак не гистерезис.
Dinosaur, гистерезитс - частный случай при наличии единственной границы: при движении вверх подвигаем границу вверх, а при движении вниз, соответственно, - вниз. Если границ несколько, то и подход стандартный - переменная состояния.
Но Вам бы пока с гистерезисом разобраться. Судя по предыдущему посту, Вы так и не поняли, как использовать флаг включения мотора для организации гистерезиса. Вы вот, вроде, привели формулу, но флага в ней как не было, так и нет.
Флаг может иметь любое отличное от нуля значение. Соответственно и сдвиг уровня может быть не на 1% а на столько,сколько нужно.
Добрый вечер, корифеи
В общем вопрос поднятый в первом посте так и не дает мне покоя - на данный момент функция (в упрощенном виде) выглядит так:
В части включения LOW скорости/выключения все работает корректно: при превышении порогового значения плюс гистерезис мотор отключается, при уходе ниже порогового значения минус гистерезис, мотор включается на LOW скорость, внутри петли гистерезиса его состояние не меняется (за исключением попадания в петлю путем перескока "через скорость" - в этом случае принудительно ставится скорость LOW).
Но на стыках скоростей LOW-MED и MED-HIGH возникает "болтанка", когда humCurrentAverage колеблется возле порогового значения, соответственно скорости мотора переключаются туда-сюда. В голову приходят только "индийские" варианты решения проблемы: вызывать функцию реже (например раз в минуту), тогда переключения скорости мотора будут происходить не так часто и вообще есть надежда что значение humCurrentAverage успеет "убежать" за это время из пограничного диапазопа; прикрутить таймер и запретить изменение скоростей мотора чаще чем заданное время таймера (собственно разновидность первого варианта). Но на мой взгляд это костыли, и должно быть решение проблемы пограничных состояний, а не борьба со следствием. Подскажите в какую сторону думу думать?
Нужно отслеживать направление перехода и значение гистерезиса именно для этого перехода (для этой границы) устанавливать в обратную сторону. То есть, если у тебя есть три границы переходов, то нужен массив из трех гистерезисов, в который при каждом пересечении границы нужно установить плюс/минус гистерезис. Опционально, для остальных границ в этот момент записыввать нулевой гистерезис.
Как-то так.
Ну здесь работает высказывание -Теория без практики мертва, практика без теории слепа. У Вас второй вариант. Пока не разберетесь с теорией, будете тыкаться как слепой котенок.
Это Вы мне? У меня уже года три работает выытяжка на кухне именно по этому принцципу: 4 уровня загрязнения воздуха, переключение с гистерезисом на каждом уровне. Практика с теорией слились в экстазе.
IgorR, скорее всего не Вам. Теория это не то что работает, а то что позволяет объяснить другому как это работает. И как только вы сможете объяснить Dinosaur-у и он поймет, то можно сказать что у Вас работает не только практика, но и теория. А так это все слова.
Мужики, спасибо конечно за крылатые фразы, но хотелось бы по существу - это начало моего пути и я прекрасно понимаю что и теория, и практика хромают у меня на обе ноги, в том числе с помощью этого форума пытаюсь разобраться.
к IgorR - определить направление движения проблем нет, но дальше не вполне уловил Вашу мысль. Например целевое значение стоит 50, границы переходов 53 (выкл), 47 (скор 1), 44 (скор 2), 41 (скор 3). НО!!!! текущее значение например 37 (т.е. включаем третью скорость), а следующее прилетает например.... 52. если взять его за новую границу перехода, и от него построить гистерезис в обе стороны, то сущая ерунда получится и еще ошибка накапливаться будет. Можете свою мысль кодом проиллюстрировать?
Привет. Я тут подумал на досуге. Можно немного по другому и проще. Направление определить несложно. А для переключчения использовать два массива границ: для движения вверх и для движения вниз.
Например, желаемые уровни 20, 40 и 60. Абсолютное значение гистерезиса пусть будет 5.
Тогда Levels_UP = [25, 45, 65], а Levels_DOWN = [15, 35, 55].
И никаких лишних телодвижений :)
к IgorR - определить направление движения проблем нет, но дальше не вполне уловил Вашу мысль. Например целевое значение стоит 50, границы переходов 53 (выкл), 47 (скор 1), 44 (скор 2), 41 (скор 3). НО!!!! текущее значение например 37 (т.е. включаем третью скорость), а следующее прилетает например.... 52. если взять его за новую границу перехода, и от него построить гистерезис в обе стороны, то сущая ерунда получится и еще ошибка накапливаться будет. Можете свою мысль кодом проиллюстрировать?
Имеелось ввиду, что, для Вашего примера, значениее гистерезиса будет применено к уровню 47. Например, для гистерезиса = 2, новое значение станет 45 (сместится вниз). Остальные останутся неизменными. В общем случае - изменяется на значение гистерезиса в обратную движению сторону ближайшая пересеченная граница, остальные приводятся к "стандартному" уровню. Но это немного гиморно. См. предыдущий пост.
PS Как-то у Вас уж очень близко значения расположены, если это из реальных данных. Тут как-то с гистерезисом уже сложно становится :(
Опять не ухвачу Вашу мысль - уже на листочке нарисовал цветными фломастерами. Рассмотрим уроверь 40 (пороги соответсвенно при понижении - 35, при увеличении - 45), значение переменной уменьшается до уровня 36 (не пересекаем уровень 35, работает первая скорость), значение переменной стабилизирутеся и начинает увеличиваться (опа, вроде как все нормально, производительности хватает, но пес с ним - врубаем вторую скорость) до 37, где опять стабилизируется и идет вниз (твоюмать, опять врубаем первую скорость) к 36, ну и так по кругу.
Постойте, а зачем врубать 2-ю передачу при значении 37? При движении вверх граница включения - 45. Вот и движемся, пока 45 не достигнем. Достигли, врубили посильнее - показания стали уменьшаться. Но, пока не достигли границы переключения на ниспадающейй ветви (а это - 35) - мотор у Вас крутится на второй скорости. Наступило 35 - бац, и вентилятор зашуршал еле-еле, пытаясь поддержать достигнутое значение. Но кто-то сильно задышал и показания поползли вверх. Достигли 45 - и все по новой. Так и будет болтаться вокруг границы 40. И это нормально. Три значения скорости - это довольно грубая регулировка. А гистерезис нужен, чтобы при достижении показаний уровня перключения в 40 единиц не происходила "болтанка" из-за того, что датчик будет в течение какого-то времени хаотично выдавать 40 +- 1 единицу, пока воздух стабильно не уйдет в какое-то из состояний.
!!! Это не вытяжка, а увлажнитель, поэтому логика работы противоположная ))) Сегодня уже голова не варит - завтра с утра еще раз перечитаю Ваше сообщение и постараюсь осмыслить.
Ведь как только изменится направление изменения переменной (от падения к росту), мы попадаем в левую сторону рисунка и получается должны отработать это событие? или имеете ввиду никак не отрабатывать а ждать выход за пределы верхней границы (45)? Тогда как быть с "перескоком" между диапазонами?
Перескок. А какая разница системе, последовательно вы проходите диапазоны или резкое изменение вызовет пропуск какого-то из них?
Ну а граница по любому будет размыта. При желаемом уровне переключения, предположим 50 единиц, в одну сторону переключение произойдет при 50 + Gist, а в другую при 50 - Gist. При этом Gist должен быть больше некого "эпсилон", характеризующего а) нестабильность показания датчика (например, он дает показания плюс-минус 0.5) б) инерционность системы (при изменении оборотов движка система отреагирует очень далеко не сразу, поэтому какое-то время показания датчика так и будут болтаться в районе 50 +/- эпсилон).
Поэтому желательно произвести ряд замеров при стационарном состоянии системы и определить максимальное значение эпсилона и Gist взять равным двум-трем эпсилонам.
Ведь как только изменится направление изменения переменной (от падения к росту), мы попадаем в левую сторону рисунка и получается должны отработать это событие? или имеете ввиду никак не отрабатывать а ждать выход за пределы верхней границы (45)?
Изменение направления движения фиксировать при переключении скорости движка.
Покопайте в сторону PID
Еще как варийант засекать время посоледнего перехода. И запретить смену скорости пока не пройдет допустим минута от последнего переключения.
Да, этот этап я понимаю как работает, тут ничего сложного:
ммм... как то так?
Не будет ли любезен многоуважаемый Джинн... :) Где можно посмотреть на этот автомат?
Начинаем с основы
Где используются эти переменные в Вашем примере?
В общем спасибо всем за подсказки, этот алгоритм оказался вполне работоспособен, для тех кто столкнется с подобной задачей, выкладываю код (вероятно задумку можно оформить изящнее, но конструкции <if> <else> мне на данном этапе освоения языка понятнее):
Здесь
Ну там ясно. Как только "ступил" на чужую белую зону, то переходи к "врагу". А на граничных своих серых зонах можно "гулять сколько хочешь".
Да, спасибо Вам за картинки, до меня не доходил момент, что нужно сделать несколько диапазонов и связать их через "нейтральные полосы", все пытался диапазоны слепить друг у другу.
Пух, это что? О_О
07
void
stand(state_t s) {
08
state = s;
09
switch
(state) {
10
case
speed0:
11
break
;
12
case
speed1:
13
break
;
14
case
speed2:
15
break
;
16
case
speed3:
17
break
;
18
}
19
}
Пух, это что? О_О
Автомат Пуха.