Сообщение от
Сергей0308
Подобная задача в ОЛ решается элементарно
И что считает эта схема? Без каких-то личных претензий, но как применить ваш пример для "среднего расхода за последние сутки" как-то неясно.
Если я правильно прочитал вашу схему, то вы считаете "общее количество импульсов/общее время". Верно?
Такой подход будет работать только при постоянной скорости. Если же расход меняется со временем, то ваш алгоритм будет показывать погоду.
Если что, то я выше экспоненциальное сглаживание для ПЛК выложил. Оно адаптируется к изменяющимся скоростям.
И кода там 15 строк. Открываем статью в Wikipedia, сверяем алгоритм. На всякий пожарный проверяем в симуляции.
Наверное, это можно переделать в CFC или даже ПР, но, на мой взгляд, на ST нагляднее (да и сравнить с оригиналом в wikipedia проще).
Код:
PROGRAM averages
(* https://en.wikipedia.org/wiki/Exponential_smoothing#Basic_exponential_smoothing *)
VAR CONSTANT
sampling_interval: REAL := 10; (* seconds, should be equal to interval in task configuration *)
END_VAR
VAR_OUTPUT
last1m: REAL;
last1h: REAL;
last1d: REAL;
END_VAR
VAR
a1m: REAL;
a1h: REAL;
a1d: REAL;
prevCnt: INT;
delta: REAL;
END_VAR
delta := (PLC_PRG.CV - prevCnt)/sampling_interval; (* current flow ITEMS PER SECOND *)
prevCnt := PLC_PRG.CV; (* <-- интересно, тут может возникнуть ошибка из-за того, что мы 2 раза читаем переменную PLC_PRG.CV ? *)
a1m := 1-EXP(-sampling_interval/60); (* можно вычислить заранее и прямо константой забить *)
a1h := 1-EXP(-sampling_interval/3600);
a1d := 1-EXP(-sampling_interval/86400);
last1m := a1m*delta + (1-a1m)*last1m;
last1h := a1h*delta + (1-a1h)*last1h;
last1d := a1d*delta + (1-a1d)*last1d;
Код:
PROGRAM PLC_PRG
VAR_INPUT
inp: BOOL;
END_VAR
VAR_OUTPUT
CV: INT;
END_VAR
VAR
cntr: CTU;
END_VAR
cntr(CU := inp); (* тут просто считаем импульсы *)
CV := cntr.CV;
Если сглаженное значение нужно довольно часто (например, раз в секунду-две-три), то можно сделать без "отдельных программ для усреднения".
Нужно всего несколько строк и одна переменная, в которой будет хранится усреднённое значение -- avg_period:
Код:
period := new-old; (* т.е. время, прошедшее с прошлого раза, в миллисекундах *)
a := 1.0 - EXP(-period / 200.0); (* тут 200.0 мс это как раз период усреднения -- его можно менять *)
avg_period := a*Period + (1.0-a)*avg_period; (* тут накапливается усреднение *)
rate := 1000/avg_period; (* Результат, "оборотов в секунду". Что выводить в случае "деления на ноль" предлагаю рассмотреть отдельно *)
Вот пример, как оно может выглядеть: https://docs.google.com/spreadsheets...it?usp=sharing
Видно, что при увеличении константы результат сглаживания меняется более плавно, но медленнее.
Если характерная частота -- 5-20Гц, то, судя по графику, значение 200 должно неплохо работать. За секунду-полторы оно "выходит на режим", и потом несильно колбасится.