Реализация мечт (нечеткая логика)! Пошаговый рецепт

Есть задача, а для кого-то почти мечта — реализовать нечеткую математику в ВКПа. И здесь отдельное спасибо Вячеславу Петухову за материал, ставший основой похода в нечеткую логику на базе автоматов. Правда, сам Вячеслав высказался отрицательно о реальности подобного «блицкрига», но, ведь, когда есть цель и неистребимое желание ее достичь, то, порой, даже невозможное становится возможным. Мне же, что там скрывать, очень захотелось в ВКПа создавать объекты на основе нечеткой логики, аналогичные объектам в SimInTech. Тем более, что когда-то теория нечетких множеств привлекала мое внимание, а понятие нечеткого регулятора и сейчас, если честно, бередит мои мысли…

Чтобы от чего-то оттолкнуться, был выбран проект из каталога демо-примеров платформы SimInTech из подкаталога «Автоматика и математика\Нечёткая Логика\Система поддержания уровня воды в баке». И на момент начала написания данной статьи мною уже были успешно реализованы некоторые из типовых блоков библиотеки «Нечеткая логика» платформы SimInTech (см. рис. 1). Заметим, что дополнительную информацию о нечеткой логике можно почерпнуть из справки платформы SimInTech, зайдя в раздел «Лабораторные работы по ВУЗам», затем в «Московский Политех» и в завершение — «Разработка системы нечеткого вывода».

Рис. 1. Нечеткая логика платформы SimInTech

Рис. 1. Нечеткая логика платформы SimInTech

Итак, цель поставлена, исходный материал есть и нам ничто уже не помешает в ее достижении. Начнем…

Первым был создан блок «Фазификация Гаусса» из комплекта «Фазификация» библиотеки «Нечеткая логика»(см. рис. 1). В нашем варианте в его основу положен код на внутреннем языке (ЯВУ) платформы SimInTech, предоставленный Вячеславом Петуховым (далее этот факт окажется достаточно важным). Конвертировать его в код на С++ дело не сложное (см. далее Листинг 1). А с точки зрения создания процесса в автоматной форме — совсем проще простого. В данном случае это элементарный автомат из одного состояния, исполняющий в цикле код фазификатора. В нашем случае им будет метод автоматного класса — действие автомата.

Далее были реализованы блоки попроще — операции «Импликация» и «И (конъюнкция)».   Структурно они ровно такие же, как и рассмотренный выше блок Это все та же связка — метод, реализующий функциональность, и автомат с одним состоянием. Более того, это будет наш базовый подход к созданию автоматных аналогов практически любых библиотечных блоков из SimInTech. Здесь основная проблема — создать функциональное наполнение блока, а автомат будет почти всегда типовой и элементарный — циклический с одним состоянием (иногда с двумя), проще которого сложно что-то придумать.

В помощь реализации прикладных блоков (субблоков) исходного проекта был создан аналог стандартного блока «Интегратор с ограничением» (см. библиотека «Динамические» в SimInTech). Структурно это все тот же элементарный автомат, с функциональностью которого, однако,  пришлось повозиться. И так уж случилось, на него суммарно было потрачено времени больше, чем на все рассмотренные и еще даже не упомянутые блоки совместно. Но в программировании это почти стандартная ситуация. Более того, преодоление ее чуть ли не основной кайф, получаемый программистом от своей работы (правда, для кого-то это еще и деньги).

Чтобы все выше перечисленное протестировать, а также реализовать определенную стратегию проектирования, были созданы аналоги блоков SimInTech из других библиотек платформы. Речь о блоках «Меандр» и «Кусочно-линейная» функция из библиотеки «Источники». И они тоже не стали исключением — те же простейшие автоматы. Правда, меандр в целях наглядности имеет два состояния. Одно для высокого состояния выхода, другое — для низкого (сами уровни выходного сигнала задаются в настройках автоматного процесса).

Проект  «Система поддержания уровня воды в баке»

Схема реализуемого далее в ВКПа демо-проекта платформы SimInTech показана на рис. 1. В ней не видно элементов библиотеки «Нечеткая логика», но они сразу проявляются при открытии соответствующих субмоделей — «Нечеткий регулятор», «Клапан» и «Бак». Схемы  данных субмоделей показаны на рис. 2. Именно их нам предстоит реализовать в автоматной форме. А в нашем случае — создать по словам Вячеслава доселе невозможное.

Рис. 1 Проект

Рис. 1 Проект «Система поддержания уровня воды в баке»

Рис. 2 Схемы субмоделей

Рис. 2 Схемы субмоделей «Клапан» (a), «Бак»(b), «Нечеткий регулятор»©

Что же нужно сделать, чтобы субмодели заработали? Для реализации «Клапан» уже все есть. Для субмодели «Бак» нет субблока «Утечка».  Но если его открыть, то перед нами предстанет элементарный код (см. рис. 3). Его функциональность легко реализуется буквально одной строчкой кода С++ и нам не понадобится создавать даже элементарный автоматный процесс для моделирования процесса утечки. Зачем без особого смысла множить число процессов в проекте ВКПа? Но, если это необходимо, то можно решиться и на такой шаг.

Рис. 3. Код на ЯВУ блока

Рис. 3. Код на ЯВУ блока «Утечка»

Сложнее ситуация с блоком «Нечеткий регулятор». По мере реализации проекта, в ВКПа были отработаны самые разные его варианты. От самого сложного, когда все компоненты реализуются автоматами, до самого простого, когда это один автомат. Приведем ту часть кода, самого простого автоматного класса платформы ВКПа с именем FFuzzyController, которая отвечает за функциональность автоматного блока «Нечеткий регулятор».

Листинг 1. Автоматная реализация на С++ блока «Нечеткий регулятор»

double FFuzzyController::FL_And(double inp1, double inp2, double w) { return inp1*inp2*w; }

double FFuzzyController::FL_IfThen(double inp1, double w) { return inp1*w; }

double FFuzzyController::TriangleFM(double x, double a, double b, double c)
{
    double dRet;
    if (x>a && x=b)  dRet = 1 - b/(b-c) + x/(b-c);
    }
    else dRet = 0;
    return dRet;
}

double FFuzzyController::AccProb(double x)
{
    double dRet;
    dRet = (action[0]*TriangleFM(x, a[0], b[0], c[0]));
    for (int i=1; iGetDataSrc(), pVarSigma1->GetDataSrc(), pVarC1->GetDataSrc());
    double y2 = GaussFM(pVarXL->GetDataSrc(), pVarSigma2->GetDataSrc(), pVarC2->GetDataSrc());
    double y3 = GaussFM(pVarXL->GetDataSrc(), pVarSigma3->GetDataSrc(), pVarC3->GetDataSrc());
    if (pVarXL->GetDataSrc()< pVarC1->GetDataSrc()) { y1 = 1.0; }
    if (pVarXL->GetDataSrc() > pVarC3->GetDataSrc()) {y3 = 1.0; }
// фазификатор на сигнал Расход
    double y4 = GaussFM(pVarXV->GetDataSrc(), pVarSigma4->GetDataSrc(), pVarC4->GetDataSrc());
    double y5 = GaussFM(pVarXV->GetDataSrc(), pVarSigma5->GetDataSrc(), pVarC5->GetDataSrc());
    if (pVarXV->GetDataSrc()< pVarC4->GetDataSrc()) { y4 = 1; }
    if (pVarXV->GetDataSrc() > pVarC5->GetDataSrc()) {y5 = 1; }
// схема на базе нечеткой логики
    double f1 = FL_IfThen(y1);
    double f2 = FL_IfThen(y3);
    double f3 = FL_IfThen(y2);
    double f4 = FL_And(y2, y4);
    double f5 = FL_And(y2, y5);
// Аккумуляция и дефазификация
    double y = AccAndDefasication(f2, f5, f3, f4, f1);
// Выход
    pVarY->SetDataSrc(nullptr, y);
}

Для сравнения код фазификатора Гаусса на ЯВУ платформы SimInTech приведен на рис. 4  (ср. его также со схемой на рис. 2с).

Рис. 4. Фазификатор Гаусса на ЯВУ

Рис. 4. Фазификатор Гаусса на ЯВУ

Действие y1() автоматного процесса, порожденного от класса FFuzzyController,  реализует в полном объеме функциональность  блока «Нечеткий регулятор». Оно запускает другие методы класса, которые реализуют отдельные элементы схемы блока в SimInTech. Остальное, как говорится, дело техники и осталось только поблагодарить Вячеслава Петухова за предоставленную весьма полезную и полную информацию для реализации фазификатора Гаусса в ВКПа.

Проблема

Имея на какой-то момент описанные выше блоки и, объединив их в модель, запустив затем ее на исполнение, мы убеждаемся, что проект … не работает! И это обычное дело при проектирования, особенно на начальных его стадиях,  которое не вызывает, конечно, радости, но зато сразу создает преграду, которую нужно преодолеть. А ее преодоление — это ли не смысл работы программиста? Но, однако, ближе к делу. Как понять кто виноват и что нужно сделать, чтобы проект ожил? Проблема в том, что система динамическая, закольцована обратной связью и нащупать в такой ситуации слабое звено весьма непросто.

Но решение и подход есть. У него даже есть название — «метод научного тыка». Следуя ему, необходимо, во-первых, разорвать и взять под контроль обратные связи. В нашем случае это выходные сигналы h и dh  блока «БАК». Их можно смоделировать с помощью блока кусочно-линейной функции SimInTech. Далее — выделить сами субмодели в отдельные проекты и разбираться с каждой из них по отдельности. Так сказать, подход «разделяй и властвуй» в действии. Все это в SimInTech легко сделать, но, правда, если имеешь соответствующие навыки работы в ней…

И вот тут нам пришлось сделать паузу в несколько дней, чтобы заставить правильно работать блок «Интегратор с ограничением». И обойти это никак нельзя, т.к. он основа для двух других субмоделей — «Клапан» и «Бак». Ну, а пока суть да дело … можно протестировать блок «Нечеткий регулятор» в ВКПа, ведь, выше мы уже имеем его автоматную реализацию на С++.

Тестирование блока Нечеткий регулятор

Выделенный из проекта «Нечеткий регулятор» с обвязкой, моделирующей входные сигналы в форме кусочно-линейных функций и блоками визуализации, представлен на рис. 5. Результаты работы блоков, аналогичных друг другу на платформах SimInTech и ВКПа демонстрирует рис. 6. Сравнивать нужно сигналы красного цвета. Видно, что графики настолько похожи, что их вполне можно считать практически идентичными. По крайней мере, отталкиваясь от визуального сравнения. Но как мы увидим далее, по большому счету это, т.е. визуальная идентичность, еще ни чего не означает.

Признаемся, что наблюдаемое совпадение получилось не сразу. На рис. 6b график оранжевого цвета отражает работу автоматного фузификатора Гаусса в самом сложном — параллельном варианте, когда все его блоки представлены автоматными параллельными процессами. Именно в такой форме он был создан поначалу. Процессов в этом случае, исходя из числа элементов схемы, было восемь, исключая процессы блоков демультиплексора и мультиплексора. В нашем случае они не нужны, т.к. вектора представлены отдельными сигналами блоков.   

Рис. 5 Выделенный в отдельный проект

Рис. 5 Выделенный в отдельный проект «Нечеткий регулятор»

Рис. 6. Результаты тестирования блока

Рис. 6. Результаты тестирования блока «Нечеткий регулятор» в SimInTech (a) и ВКПа (b)

Но именно этот график направил наш путь в сторону реализации фазификатора на одном процессе. И, как мы убедимся, это стоило того. А почему параллельный фазификатор выдает сигнал несколько другого вида — тема отдельного разговора и, возможно, отдельного разбирательства. Пока же мы стремимся получить результат как можно ближе к оригиналу. А для оценки возможных «закоулков» по дороге к финишу покажем саму схему фазификатора Гаусса и схему его субмодели из библиотеки SimInTech (ср. с рис. 4).  Лично меня впечатлила не сама основная схема, а схема его субмодели (рис. 7b).

Рис. 7. Фазификатор Гаусса в SimInTech

Рис. 7. Фазификатор Гаусса в SimInTech

Итак, блок «Нечеткий регулятор» в ВКПа создан и, судя по результатам тестирования,  работает достаточно прилично, чтобы можно было утверждать о его аналогичности блоку из SimInTech. Пора переходить к реализации и тестированию остальных блоков — «Клапан» и «Бак».

Тестирование блоков «Клапан» и «Бак»

Реализация субмоделей «Клапан» и «Бак» достаточно проста, чтобы тратить время на ее обсуждение. Нас больше интересуют результаты их тестирования. Для субмодели «Клапан» их демонстрирует рис. 8. И тут все совсем уж просто — диаграммы совпадают один в один.  

Рис. 8. Тестирование блока

Рис. 8. Тестирование блока «Клапан»

Переходим к тестированию субмодели «Бак». Она чуть сложнее, чем предыдущая (ср. рис. 2b и рис. 2a), но несравненно проще субмодели «Нечеткий регулятор». Тем не менее, итоговые результаты тестирования совпадают фактически идеально и в этом случае.

Рис. 8. Тестирование блока

Рис. 8. Тестирование блока «Бак»

Тестирование проекта

По отдельности мы компоненты проверили и уже нет препятствий, чтобы перейти к самому проекту? Результат тестирования проекта в ВКПа показан на рис. 9, а рис. 10 демонстрирует результаты работы «брата-близнеца» в SimInTech. В целом похоже, но…

Рис. 9. Система поддержания уровня воды в баке в ВКПа

Рис. 9. Система поддержания уровня воды в баке в ВКПа

Рис. 10. Система поддержания уровня воды в баке в SimInTech

Рис. 10. Система поддержания уровня воды в баке в SimInTech

Во-первых, проект в ВКПа явно разгоняется прежде чем перейдет в установившийся режим. А, во-вторых, если мы выполним наложение графика, полученного в ВКПа, на график, полученный в SimInTech, совместив прежде всего диаграммы графиков меандра, как наиболее стабильной их части, то увидим задержку заднего фронта выходного сигнала в ВКПа по отношению к сигналу в SimInTech (см. рис. 11). И на это сразу обращаешь внимание, т.к. предыдущее покомпонентное тестирование убедило нас в идентичной работе блоков. Что не так-то? А все просто. Дело в том, что реальная задержка выходного сигнала по отношению к входному, которую учитывает ВКПа по определению, но фактически отрицает SimInTech, сложившись для блоков, соединенных последовательно, проявила себя в полной мере, став заметной при тестировании проекта в целом.

Рис. 11. 2 мсек

Рис. 11. 2 мсек

Рис. 12. 20 мсек

Рис. 12. 20 мсек

Чтобы получить подтверждение высказанной версии, изменим дискретное время проекта, например, в 10 раз, т.е. с 2 мсек до 20 мсек. Результат демонстрирует рис. 12. С изменением быстродействия проекта стала заметны и изменившаяся форма выходного сигнала, и несколько большее смещение заднего фронта выходного сигнала уровня воды в баке. Если мы это же самое проделаем с графиками из ВКПа, то увидим разницу и в этот раз — рис. 13. Здесь бледным цветом представлены диаграммы сигналов при дискретном времени 2 мсек, а более ярким цветом — 20 мсек. Видно, что увеличение дискретного такта влияет на форму сигнала и создает достаточно заметную задержку формирования заднего фронта сигнала уровня воды в баке.

Заметим, что описанные выше манипуляции с дискретным временем соответствуют изменению значения параметров в настройке проекта в SimInTech — Минимальный/Максимальный шаг расчета. При этом задержки, которые могут стать причиной гонок данных,   SimInTech по-прежнему совсем не учитывает.

Рис. 13. Графики сигналов в ВКПа при изменении дискретного времени.

Рис. 13. Графики сигналов в ВКПа при изменении дискретного времени.

Я почти уверен, что мало кто заметил, что выше мы незаметно заменили стандартный фазификатор на его аналог на ЯВУ.Т. е. вместо фазификатора на рис. 7 стали использовать его аналог на рис. 4. Но мы ошиблись, опрометчиво посчитав их эквивалентными не столько в силу их одинакового названия, а сколько из доверия к источнику, который предоставил такой код. На рис. 14 приведена диаграмма выходных сигналов проекта на стандартных фазификаторах (см. рис. 1 и рис. 2) и она отлична от полученной нами.

Рис. 14. Графики сигналов исходного демо-проекта в SimInTech.

Рис. 14. Графики сигналов исходного демо-проекта в SimInTech.

Как показали эксперименты, причина в фазификаторах. Эксперимент, выявляющий это, совсем прост — заменяем один фазификатор на другой и тестируем. Таким образом, появились вопросы к коду на ЯВУ, а точнее к тому, кто его предоставил, т.е. к Вячеславу Петухову. Он об этом знает, но ответа на заданный вопрос на текущий момент еще нет. Надеюсь, что совместными усилиями мы эту загадку разрешим. Все же график, отражающий процесс управления, из дистрибутива SimInTech выглядит, скажем так, аккуратнее, а потому хотелось бы изменить код фазификатора, чтобы получить аналогичный результат. В том числе и в SimInTech.

Выводы

Так чего же мы достигли, кроме того, что реализовали элементы нечеткой логики в автоматной форме?

1. Прежде всего на реально работающем проекте мы доказали, что теория автоматов, автоматное программирование и нечеткая логика не антагонисты. Особенно для тех, кто в это почему-то (хотя причины эти понятны) не верил (а что теперь они скажут?). И, что там скрывать, это было одним из стимулов к работе. Ведь, любая невозможность для кого-то одновременно и недостижимая мечта. А реализация «мечт» — благое дело. Вот такое, надеюсь, хорошее дело мы и совершили.

2. В ВКПа появились блоки, которые можно использовать в любых других проектах. Пусть на это было затрачено время, но оно было потрачено не зря. Кроме того, по ходу работы над проектом были усовершенствованы и расширены визуальные возможности ВКПа, сделавшие работу над проектами более эффективной и приятной.

3. Дополнительно были протестированы, проверены и усовершенствованы возможности платформы ВКПа по созданию и управлению автоматными параллельными процессами. Особенно потребность в этом появилась при создании многокомпонентного  «Нечеткого регулятора». Выше мы рассмотрели, правда, только его однокомпонентную реализацию, но до обсуждения многокомпонентной, я рассчитываю, дело тоже дойдет. В целом это универсальные решения, которые несомненно будут востребованы в других проектах. В текущем же проекте они прошли апробацию.

4. Увлекшись, мы чуть не упустили главное. Ради чего затеяна все эта история с автоматами. То, что стоит всего перечисленного выше. О чем нужно говорить и говорить обязательно. До тех пор и после того, как это не будет усвоено окончательно. О том, что есть в ВКПа и нет ни в SimInTech, ни МАТЛАБ, ни где бы то ни было еще вообще. ВКПа, как реализация концепции автоматного программирования, учитывает инерционность объектов. Ни что не происходит мгновенно. Вычисления — в первую очередь. Вне зависимости от их реализации. Этот факт отразили диаграммы задержек выходных сигналов, поступающих с выхода субмодели «Бак» на входы субмодели «Нечеткий регулятор». И вообще. Любая система управления, содержащая обратную связь, просто обязана учитывать задержку времени, вносимую этой обратной связью. Повторяем медленно и многократно — о-б-я-з-а-н-а!   Поскольку эта задержка, в зависимости от ее величины, влияет на качество управления. А есть ли такие, кто не желал бы качественного управления?   

А теперь, как говорил М.Задорнов, наберите в легкие побольше воздуха! Читаем PS…

Литература

1. Введение в нечёткую логику. https://habr.com/ru/companies/timeweb/articles/713620/

PS

Вспомнив про главное (см. выше п.4), я задался цель, как это показать наглядно. Для этого, открыв проект в ВКПа, увидел в нем лишние процессы, которые решил удалить. Удалив их и перезапустив проект, я вдруг обнаружил, что он не работает. Сразу стало понятно, что удаленные объекты как-то связаны с оставшимися процессами, чего быть не должно. Поскольку они моделировали сигналы обратных связей, то я проверил входы блоков проекта, и, действительно, нижний вход блока «Нечеткий регулятор» был связан с одним из удаленных объектов — сигнал dh от блока «Бак».

Но к чему такое длинное и подробное предисловие? А к тому, что, установив нужную ссылку, я к своему немалому изумлению увидел следующую картинку!

Рис. 15. Графики сигналов исходного демо-проекта в SimInTech.

Рис. 15. Графики сигналов исходного демо-проекта в SimInTech.

Ба! Так, у меня все правильно! А допущенная оплошность «замыливала» правильную работу. Но остался другой вопрос — работа проекта в SimInTech? Ведь, он выдает картинку, приведенную на рис. 10. Но почему? Ведь, я только изменил ссылку и у меня все заработало, а в SimInTech ссылки, судя по схеме, правильные, но, зараза, работает как работает?! Непонятно… Т.е. вопрос к Вячеславу теперь актуален еще в большей степени, чем ранее? Я повторил его код на С++ и он работает, а его код на ЯВУ, правда, набитый мною, нет. Может, я сделал ошибку? Проверил несколько раз, вроде, нет. Так что вопросы, но только к коду в SimInTech, остались.

Но зато теперь стал понятен ход процесса, представленный на рис. 9, рис. 11 и рис. 12. Отрезок, названный нами ранее «разгоном», это нормальный ход, определяемый смоделированным сигналом обратной связи dh. Он покрывал промежуток времени в 50 сек, который мы и приняли за своеобразный «разгон». Далее данный сигнал обратной связи становится постоянным и процесс идет уже зависимый только значения обратной связи h блока «Бак».

Сказанное выше дало следующую подсказку — попробовать установить постоянное значение на нижнем входе субмодели «Нечеткий регулятор» и посмотреть, к чему это приведет. Т.е. теперь схема будет такая:

Рис. 16. Схема управления с одной обратной связью в SimInTech.

Рис. 16. Схема управления с одной обратной связью в SimInTech.

И что мы видим? А мы видим ту же самую картинку, что и на рис. 10. Т.е. система не реагирует на сигнал обратной связи по каналу dh? Проверим, изменим его, увеличив сразу в 1000 раз. Получим такую картинку:

Рис. 16. Схема управления с постоянной обратной связью (dh = 5)

Рис. 16. Схема управления с постоянной обратной связью (dh = 5)

Таким образом, связь в целом «живая», т.к. влияет на результат работы. Но влияем совсем не так, как оно должно быть, чтобы система работала, выдавая результат, как на рис. 14.

Итак, вопросы, вопросы, вопросы… Ждем ответ от Вячеслава. И ждем, кстати, уже довольно долго…

© Habrahabr.ru