Версия для печати

Непрерывный регулятор CONT_C (FB41) стандартный блок ПИД регулятора Siemens

Дата: 2009-04-28

Добавлено: komatic

Тема: PIDcontrol

В этом материале рассмотрим стандартный блок ПИД регулятора Siemens (CONT_C)
Лучшая документация к программному блоку - ее исходный текст.

Скомпилированный блок идентичен оригинальному.

Документация по функции:
english - download .pdf 0.3Мб
русская - скачать .pdf 0.3Мб

pid

FUNCTION_BLOCK FB41
TITLE ="Непрерывный PID регулятор"
AUTHOR : SIMATIC
FAMILY : ICONT
NAME : CONT_C
VERSION : " 1.5"
//reversed by komatic
VAR_INPUT
  COM_RST               : BOOL ;                    //Полный рестарт
  MAN_ON                : BOOL  := TRUE;            //Ручной режим включить
  PVPER_ON              : BOOL ;                    //Чтение входной переменной с периферии включить
  P_SEL                 : BOOL  := TRUE;            //Пропорциональную составляющую включить
  I_SEL                 : BOOL  := TRUE;            //Интегральную составляющую включить
  INT_HOLD              : BOOL ;                    //Удержание интегральной составляющей
  I_ITL_ON              : BOOL ;                    //Инициализировать интегральную составляющую
  D_SEL                 : BOOL ;                    //Дифференциальную составляющую включить
  CYCLE                 : TIME  := T#1S;            //Время выполнения блока
  SP_INT                : REAL ;                    //Внутреннее задание
  PV_IN                 : REAL ;                    //Входная переменная
  PV_PER                : WORD ;                    //Входная переменная (периферия)
  MAN                   : REAL ;                    //Ручной выход
  GAIN                  : REAL  := 2.000000e+000;   //Коэффициент пропорциональности
  TI                    : TIME  := T#20S;           //Время интегрирования
  TD                    : TIME  := T#10S;           //Время дифференцирования
  TM_LAG                : TIME  := T#2S;            //Время действия дифференциальной составляющей
  DEADB_W               : REAL ;                    //Ширина зоны нечувствительности
  LMN_HLM               : REAL  := 1.000000e+002;   //Верхний предел выходного сигнала
  LMN_LLM               : REAL ;                    //Нижний предел выходного сигнала
  PV_FAC                : REAL  := 1.000000e+000;   //Коэффициент для корректировки входной переменной (умножение)
  PV_OFF                : REAL ;                    //Коэффициент для корректировки входной переменной (сложение)
  LMN_FAC               : REAL  := 1.000000e+000;   //Коэффициент для корректировки выходной переменной (умножение)
  LMN_OFF               : REAL ;                    //Коэффициент для корректировки входной переменной (сложение)
  I_ITLVAL              : REAL ;                    //Начальное значение интегральной составляющей
  DISV                  : REAL ;                    //Возмущающая переменная
END_VAR
VAR_OUTPUT
  LMN                   : REAL ;                    //Выходное значение
  LMN_PER               : WORD ;                    //Выходное значение (переферия)
  QLMN_HLM              : BOOL ;                    //Верхний предел выхода достигнут
  QLMN_LLM              : BOOL ;                    //Нижний предел выхода достигнут
  LMN_P                 : REAL ;                    //Пропорциональная составляющая
  LMN_I                 : REAL ;                    //Интегральная составляющая
  LMN_D                 : REAL ;                    //Дифференциальная составляющая
  PV                    : REAL ;                    //Входная переменная
  ER                    : REAL ;                    //Сигнал рассогласования
END_VAR
VAR
  sInvAlt               : REAL ;   
  sIanteilAlt           : REAL ;   
  sRestInt              : REAL ;   
  sRestDif              : REAL ;   
  sRueck                : REAL ;   
  sLmn                  : REAL ;   
  sbArwHLmOn            : BOOL ;                    //Выход достиг максимального значения 
  sbArwLLmOn            : BOOL ;                    //Выход достиг минимального значения
  sbILimOn              : BOOL  := TRUE;   
END_VAR
VAR_TEMP
  Hvar                  : REAL ;                    //Hilfsvariable
  rCycle                : REAL ;                    //Abtastzeit in real
  Diff                  : REAL ;                    //Дnderungswert
  Istwert               : REAL ;                    //Istwert
  ErKp                  : REAL ;                    //Вспомогательная переменная
  rTi                   : REAL ;                    //Integrationszeit in real
  rTd                   : REAL ;                    //Differentiationszeit in real
  rTmLag                : REAL ;                    //Verzцgerungszeit in real
  Panteil               : REAL ;                    //P-Anteil
  Ianteil               : REAL ;                    //I-Anteil
  Danteil               : REAL ;                    //D-Anteil
  Verstaerk             : REAL ;                    //Verstдrkung
  RueckDiff             : REAL ;                    //Differenz des Rьckkopplungswertes
  RueckAlt              : REAL ;                    //Alter Rьckkopplungswert
  dLmn                  : REAL ;                    //Stellwert
  gf                    : REAL ;                    //Hilfwert
  rVal                  : REAL ;                    //Real Hilfsvariable
END_VAR
BEGIN
IF COM_RST                                          //Полный рестарт
THEN
    // Обнуление переменных
    sIanteilAlt:=I_ITLVAL;
    LMN:=0.0;                                       //Выходное значение
    QLMN_HLM:=0;                                    //Верхний предел выхода достигнут
    QLMN_LLM:=0;                                    //Нижний предел выхода достигнут
    LMN_P:=0.0;                                     //Пропорциональная составляющая
    LMN_I:=0.0;                                     //Интегральная составляющая
    LMN_D:=0.0;                                     //Дифференциальная составляющая
    LMN_PER:=0;        //Выходное значение (периферия)
    PV:=0.0;                                        //Входная переменная
    ER:=0.0;                                        //Сигнал рассогласования
    sInvAlt:=0.0;
    sRestInt:=0.0;
    sRestDif:=0.0;
    sRueck:=0.0;
    sLmn:=0.0;
    sbArwHLmOn:=0;
    sbArwLLmOn:=0;
ELSE
    rCycle:= DINT_TO_REAL(TIME_TO_DINT(CYCLE)) / 1000.0;            //Время выполнения блока в секундах
    Istwert:=INT_TO_REAL(WORD_TO_INT(PV_PER)) * 100.0 / 27648.0;    // Получили входную переменную от периферии (0-2768 в 0-100)
    Istwert:=Istwert * PV_FAC + PV_OFF;                             // Скорректировали входную переменную
    IF NOT PVPER_ON THEN Istwert:=PV_IN; END_IF;                    // Если периферийный вход отключен берем переменную из PV_IN
    PV:= Istwert;                                                   // Входная переменная
    ErKp:=SP_INT - PV;                                              // Получили рассогласование между заданием и входом
    IF     ErKp < (-DEADB_W)     THEN ER:=ErKp+DEADB_W;             // Если рассогласование больше зоны нечувствительности
    ELSIF  ErKp >   DEADB_W      THEN ER:=ErKp-DEADB_W;             // уменьшаем рассогласование на величину зоны нечуств.
    ELSE                              ER:=0.0;                      // иначе принимаем рассогласование равным нулю.
    END_IF;
    ErKp:=ER * GAIN;                                                // Рассогласование, умноженное на кфт пропорциональности
    rTi:=DINT_TO_REAL(TIME_TO_DINT(TI)) / 1000.0;                   // Время интегрирования в секундах
    rTd:=DINT_TO_REAL(TIME_TO_DINT(TD)) / 1000.0;                   // Время дифференцирования в секундах
    rTmLag:=DINT_TO_REAL(TIME_TO_DINT(TM_LAG)) / 1000.0;            // Время действия диф.составляющей в секундах
    // Проверка на допустимость временных настроек регулятора
    IF rTi < (rCycle * 0.5)     THEN    rTi:=rCycle * 0.5;      END_IF; // Минимальное время интегрирования - Cycle / 2
    IF rTd < rCycle             THEN    rTd:=rCycle;            END_IF; // Минимальное время дифференцирования - Cycle
    IF rTmLag < rCycle * 0.5    THEN    rTmLag:=rCycle * 0.5;   END_IF; // Минимальное время действия диф.сост.- Cycle / 2
    // Вычисление пропорциональной составляющей
    IF P_SEL
    THEN
        Panteil:=ErKp;                                              // Если выбрана пропорциональная составляющая
    ELSE
        Panteil:=0.0;
    END_IF;
    //---------------------------------------------------------------------------------------------
    // Вычисление интегральной составляющей
    //---------------------------------------------------------------------------------------------   
    IF I_SEL                    //Интегральную составляющую включить
    THEN
        IF I_ITL_ON             //Инициализировать интегральную составляющую
        THEN
            Ianteil:=I_ITLVAL;
            sRestInt:=0.0;
        ELSE
            IF MAN_ON           //Ручной режим
            THEN
                Ianteil:=sLmn - Panteil - DISV;
                sRestInt:=0.0;
            ELSE                //Автоматический режим
                Diff:=rCycle / rTi *(ErKp + sInvAlt) *  0.5 + sRestInt;
                IF ((Diff>0.0) AND sbArwHLmOn OR INT_HOLD)OR ((Diff<0.0) AND sbArwLLmOn) THEN Diff:=0.0; END_IF;       
                Ianteil:=sIanteilAlt + Diff;
                sRestInt:=sIanteilAlt - Ianteil + Diff;
            END_IF;
        END_IF;
    ELSE
        Ianteil:=0.0;
        sRestInt:=0.0;
    END_IF;
    //---------------------------------------------------------------------------------------------
    //Формирование дифференциальной составляющей
    //---------------------------------------------------------------------------------------------   
    Diff:=ErKp;
    IF (NOT MAN_ON) AND D_SEL
    THEN
        Verstaerk:= rTd / (rCycle * 0.5 + rTmLag);
        Danteil:= (Diff - sRueck) * Verstaerk;
        RueckAlt:= sRueck;
        RueckDiff:= rCycle / rTd * Danteil + sRestDif;
        sRueck:= RueckDiff + RueckAlt;
        sRestDif:=RueckAlt - sRueck + RueckDiff;
    ELSE
        Danteil:=0.0;   
        sRestDif:=0.0;
        sRueck:=Diff;
    END_IF;
    //---------------------------------------------------------------------------------------------
    //Формирование выхода
    //---------------------------------------------------------------------------------------------
    dLmn:=Panteil + Ianteil + Danteil + DISV;
    IF MAN_ON           // Если ручной режим
    THEN
        dLmn:=MAN;      // Ручной выход
    ELSE
        IF (NOT I_ITL_ON) AND I_SEL
        THEN
            IF (Ianteil > (LMN_HLM - DISV)) AND (dLmn > LMN_HLM) AND ((dLmn - LMN_D)> LMN_HLM)
            THEN
                rVal:=LMN_HLM - DISV;
                gf:=  dLmn - LMN_HLM;
                rVal:=Ianteil - rVal;
                IF  rVal > gf THEN rVal:=gf; END_IF;
                Ianteil:=Ianteil - rVal;
            ELSE
                IF (Ianteil < (LMN_LLM - DISV)) AND (dLmn < LMN_LLM) AND ((dLmn - LMN_D) < LMN_LLM)
                THEN
                    rVal:=LMN_LLM - DISV;
                    gf:=dLmn - LMN_LLM;
                    rVal:=Ianteil - rVal;
                    IF rVal < gf  THEN rVal:=gf; END_IF;
                    Ianteil:=Ianteil - rVal;
                END_IF;   
             END_IF;      
        END_IF;
    END_IF;
    LMN_P:=Panteil;
    LMN_I:=Ianteil;
    LMN_D:=Danteil;
    sInvAlt:=ErKp;
    sIanteilAlt:=Ianteil;
    sbArwHLmOn:=0;
    sbArwLLmOn:=0;
    IF (dLmn >= LMN_HLM)
    THEN
        QLMN_HLM:=1;
        QLMN_LLM:=0;
        dLmn:=LMN_HLM;
        sbArwHLmOn:=1;
    ELSE
        QLMN_HLM:=0;
        IF (dLmn <= LMN_LLM)
        THEN
            QLMN_LLM:=1;
            dLmn:=LMN_LLM;
            sbArwLLmOn:=1;
        ELSE
            QLMN_LLM:=0
        END_IF;
    END_IF;
    sLmn:=dLmn;
    dLmn:=sLmn * LMN_FAC + LMN_OFF;  
    LMN:=dLmn;
    dLmn:=LMN * 2.764800e+002;
    IF dLmn >= 3.251100e+004
    THEN
        dLmn:=3.251100e+004;
    ELSE
        IF dLmn <= -3.251200e+004
        THEN
            dLmn:=-3.251200e+004;
        END_IF;
    END_IF;
    LMN_PER:= INT_TO_WORD(REAL_TO_INT(dLmn));
END_IF;   
END_FUNCTION_BLOCK





Просмотров: 75274

Комментарии к материалу

Добавлен: KruFFT    Дата: 2009-11-23

Спасибо! Вот именно сейчас этот код понадобился. Как оказалось, в S7-412H нет SFB41. :(

Добавлен: Миша    Дата: 2013-03-10

Спасибо огромное! Очень выручили! :)

Добавлен: Николай    Дата: 2014-04-02

Круто, к 1200 прикрутить :)

Добавлен: Юрий    Дата: 2014-04-02

А какой смысл этих 2-х строк:
Ianteil:=sIanteilAlt + Diff;
sRestInt:=sIanteilAlt - Ianteil + Diff;
При каких обстоятедьствах sRestInt будет не равно нулю?

Добавлен: Колян    Дата: 2014-04-03

sRestInt:=sIanteilAlt-sIanteilAlt- Diff+ Diff=0

Добавлен: komatic    Дата: 2014-04-03

>При каких обстоятедьствах sRestInt будет не равно нулю?
Самое смешное - что при любых обстоятельствах, в реальном контроллере оно нулю равно не будет. легко проверить промониторив работающий регулятор.

Добавлен: komatic    Дата: 2014-04-04

почему не получается 0?
скорее всего из-за ограниченной точности операций с числами в real формате
http://plc4good.org.ua/view_post.php?id=165

Добавлен: sergio green    Дата: 2014-04-21

Подобная конструкция и по диф. составляющей. Это борьба с погрешностью?
И есть ли смысл оставлять эти строчки?

Добавлен: пид    Дата: 2014-08-22

А что нужно исправить, что бы пропорциональный канал был параллелен -И и -Д каналу?

Добавлен: ПИД    Дата: 2014-08-22

ErKp:=ER * GAIN; -> ErKp:=ER * 1.0;

Panteil:=ErKp; ->
Panteil:=ErKp*GAIN;

Добавлен: Сергей    Дата: 2014-12-10

Огромное спасибо!
На работе обслуживаю\программирую PLC (в основном Siemens S7-200..S7-400), дома (хобби) AVR.. Для проекта терморегулятора (DS18B20 + ATTiny85) искал Сяшную библиотеку PID. Пробовал использовать готовую от Atmel (AVR221) и другие по форумам искал. Не подходят. В симуляторе видна огромная разница между решениями PID Control, применёнными в промышленных контроллерах и тем, что я смог накопать в интернете.
В итоге, взял отсюда исходник FB41 и с SCL перевёл на Си. На ATTiny85 отлично (идеально!) работает. Убрал лишнее, немного оптимизировал для быстродейсвия. На 8Мгц время обработки FB41 занимает 0.8-1.2 мсек. На порядок дольше чем алгоритмы с целочисленной арифметикой, но зато имеем понятные коэффициенты, безудароное переключение ручн\автомат, правильную реализацию D составляющей...
Если кому нужно, могу выложить сяшные исходники на файлообменник QIP, ссылку здесь кину.

Добавлен: Сергей    Дата: 2014-12-10

Выложил исходники + проект на IAR + проект Proteus h**p://file.qip.ru/arch/6yfGw-Lm/PID_FB41_IAR_project.html
Картинка с протеуса h**p://shot.qip.ru/00CQYB-595nqPfJV
Всем удачи и спасибо komatic

Добавлен: komatic    Дата: 2014-12-10

спасибо за добрые слова и полезный материал, обязательно оформлю и выложу

Добавлен: Сергей    Дата: 2014-12-10

Не зачто ;) Странно, что в интернете нет подобной темы. Готовый, хорошо документированный, проверенный в промышленности алгоритм. Для 32-bit ARM вообще самое то.
>При каких обстоятедьствах sRestInt будет не равно нулю?
Всё верно, борьба с погрешностью REAL вычислений. Смотрел в отладчике, там постоянно "мельтешат" микро-значения в районе 10Е-8...10Е-10. Для "облегчения жизни" AVR "убрал" sRestInt и sRestDif, проверил - разницы в поведении регулятора не заметил. Зато выиграл ~220uS (~1700 тактов CPU) + место. Также не заметил разницы при переходе с float на double float.
Если бы портировал под STM32, то оставил бы как есть. Всё таки программисты Siemens не зря это сделали.

Добавлен: Old Bell    Дата: 2015-04-19

LMN_HLM : REAL := 1.000000e+002; //Верхний предел выходного сигнала
----------------
у оригинала выход не привязан к 100 процентам?

Добавлен: komatic    Дата: 2015-04-19

1.000000e+002 это и есть 100.0 или я не понял вопроса

Добавлен: Old    Дата: 2015-04-19

да, в этом варианте привязано к 100. у оригинала, насколько я понимаю, нет. Значит вы доработали регулятор. входные переменные так же жестко прописаны у вас.

Или уже настроенный PID взяли для этого кода.
мне выход нужен от нуля до HEX 4000.
Или второй вопрос, чем ориганал отличается от этоого варианта. кроме того, что я заметил.
спасибо

Добавлен: komatic    Дата: 2015-04-19

>Old
нет, я ничего не добавлял от себя, такой код инициализаций получается после команды "Generate source" на оригинальном FB.
Вы можете это с легкостью проверить.

Добавлен: komatic    Дата: 2015-04-19

если у вас другой результат, обратите внимание, что версия этого регулятора - 1.5 (указано в шапке свойств блока)

Добавлен: Old Bell    Дата: 2015-04-19

Спасибо, Komatic, все верно говорите. Я немного не понял работу ограничения верхнего и нижнего уровня.
Ограничение в процентах от выходного сигнала ю
Не мог понять, отчего ограничение 100, а на выходе LMN_PER максимальная величина 27648.
Посмотрел еще раз в симуляторе и прояснилось.

еще вопрос один, если открываю 41 блок канопеном, то код в IT (AWL)ассемблер.
И в шапке открытого блока вижу "generiert vom SCL Übersetzer Version: SCL K5.1.5.1 (C5.1.9.17)"

чир код был сгенерирован "переводчиком" кода SCL и далее версия.

Это такая версия с ассемлером от сименса или при открытии блока (в редакторе степа) инсталлированный модуль языка SCL автоматически переводит в ассемблер.
Полагаю, что первое, но хотелось бы, что бы второе )))
если второе, где и что нужно отключить, что бы оригинальный код увидеть?

Добавлен: Old Bell    Дата: 2016-12-16

переведу пару коментариев
Hilfsvariable - вспомогательная переменная
Abtastzeit in real-время обработки в REAL
Дnderungswert -Änderungswert-величина изменения
Istwert-действительное значение

Integrationszeit in real-время интегрирования в REAL
Differentiationszeit in real-время дифференцирования в REAL
 VerzцgerungszeitVerzögerungszeit in real- время задержки в REAL
 P-Anteil- P-составляющая
 I-Anteil- I-составляющая
 D-Anteil- D-составляющая
  Verstдrkung-Verstäkung-усиление
  Differenz des Rьckkopplungswertes-разница величины обратной связи
  Alter Rьckkopplungswert-старое значение величины обратной связи
  Stellwert-
  Hilfwert- вспомогательная величина
  Real Hilfsvariable - истинная текущая вспомогательная переменная

Добавлен: Old Bell    Дата: 2016-12-16

Stellwert-значение управляющего или регулирующего воздействия

Добавлен: CORSAR    Дата: 2016-12-29

Добрый день!
К вопросу о назначении строки: sRestInt:=sIanteilAlt - Ianteil + Diff;
Это метод суммирования Кохена. Описан здесь: http://www.machinelearning.ru/wiki/index.php?title=%D0%A1%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%BE%D0%B3%D0%BE_%D0%BC%D0%BD%D0%BE%D0%B6%D0%B5%D1%81%D1%82%D0%B2%D0%B0_%D1%87%D0%B8%D1%81%D0%B5%D0%BB%2C_%D1%81%D1%83%D1%89%D0%B5%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D0%BE_%D0%BE%D1%82%D0%BB%D0%B8%D1%87%D0%B0%D1%8E%D1%89%D0%B8%D1%85%D1%81%D1%8F_%D0%BF%D0%BE_%D0%B2%D0%B5%D0%BB%D0%B8%D1%87%D0%B8%D0%BD%D0%B5

Добавлен: CORSAR    Дата: 2016-12-29

Для S7-1200/1500 возможен перевод математических вычислений в формат LREAL. В этом случае строки коррекции вычислений, о которых писал выше, можно убрать.

Добавлен: dmw    Дата: 2017-11-06

в строках
IF (Ianteil > (LMN_HLM - DISV)) AND (dLmn > LMN_HLM) AND ((dLmn - LMN_D)> LMN_HLM)
и
IF (Ianteil < (LMN_LLM - DISV)) AND (dLmn < LMN_LLM) AND ((dLmn - LMN_D) < LMN_LLM)
выходная переменная LMN_D появляется прежде, чем ей присвоено значение.
Может быть строку
LMN_D:=Danteil;
перенести сразу после этой
dLmn:=Panteil + Ianteil + Danteil + DISV;

Добавлен: dmw    Дата: 2017-11-07

Или LMN_D в приведенных строках - это предыдущее значение, рассчитанное на предыдущем цикле, тогда логичнее LMN_D сделать переменной IN_OUT?

Добавить комментарий

Ваше имя:

Текст комментария (4000 max):

Введите сумму с картинки: