Пример проектирования дискретного ПИД-регулятора

         

ЭТАП 4 - Написание программы ПИД-регулятора для ЦВМ


Не затрагивая вопрос выбора ЦВМ, скажем, что это может быть периферийный контроллер (PIC), микроЭВМ (8051, AVR, ...), ЭВМ (x86, ...), промышленный контроллер, DSP (ADSP-21xxx, TMS320, ...) или схема на жесткой логике. Составление программы выполняющей расчет рекурсивного уравнения (*) обычно не вызывает затруднений, если ЦВМ имеет команды деления и умножения чисел (желательно с большой мантиссой и плавающей точкой).

В табл. 1 и 2 представлены программы рекурсивного цифрового фильтра второго порядка. Они написаны на языках C++ и Паскале в средах разработки Borland C++Builder 4 и Borland Delphi 4 в соответствии с технологией создания моделей пользователя для программы VisSim. При трансляции получается файл pid.dll, главная функция которого "zW" может быть включена в модель программы VisSim с помощью блока "userFunction" (см. рис. 7). При записи коэффициентов РУ (*) в параметры блока и стробировании частотой 2000 Гц - это и будет модель искомого дискретного ПИД-регулятора.

Рис. 7

Таблица 1

Файл pid.cpp
#include <math.h>
#include <condefs.h>
#pragma hdrstop
#define EXPORT32 __declspec(dllexport)

//---------------------------------------------------------------------------
struct z_TF_INFO {
  double k;        // коэффициент усиления
  double b0,b1,b2; // коэффициенты полинома числителя
  double a0,a1,a2; // коэффициенты полинома знаменателя
};

//---------------------------------------------------------------------------
extern "C" {
  double buffer_x[2]={0,0}, buffer_y[]={0,0};
  double c, help_y;
//************ Функция размещения параметров ********************************
//************ Вызывается VisSim-ом при создании блока **********************
EXPORT32 long WINAPI zWPA(short FAR *ppCount){
  *ppCount=7; // число записываемых в файл vsm параметров модели пользователя
  return sizeof(z_TF_INFO);
}
//************ Процедура инициализации параметров ***************************
//************ Вызывается VisSim-ом после PA функции ************************
EXPORT32 void WINAPI zWPI(z_TF_INFO *zTF){
  zTF->k=31.9016459416667;
  zTF->b0=1;
  zTF->b1=-1.9894401341982;
  zTF->b2=0.98945592544195;
  zTF->a0=1;
  zTF->a1=-1.3333333333333;
  zTF->a2=0.33333333333333;
}
//************ Функция изменения параметров *********************************
//************ Вызывается VisSim-ом при нажатии правой клавиши мыши *********
EXPORT32 LPSTR WINAPI zWPC(z_TF_INFO *zTF){
  return "k;b0;b1;b2;a0;a1;a2";
}
//************ Процедура Simulation Start ***********************************
//************ Вызывается VisSim-ом на первом шаге моделирования ************
EXPORT32 long WINAPI zWSS(z_TF_INFO *zTF, long *runCount){
  buffer_x[0]=0; buffer_x[1]=0;
  buffer_y[0]=0; buffer_y[1]=0;
  help_y=0; c=0;
  return 0;
}
//************ Процедура Simulation End *************************************
//************ Вызывается VisSim-ом на последнем шаге моделирования *********
EXPORT32 long WINAPI zWSE(z_TF_INFO *zTF, long *runCount){
  return 0;
}
//************ Это базовая процедура в DLL **********************************
//************ Вызывается VisSim-ом на каждом шаге моделирования ************
EXPORT32 void WINAPI zW(z_TF_INFO *zTF, double FAR x[], double FAR y[]){
  if (x[0]==1 && c==0) {
  // Непосредственный алгоритм с двумя буферами
    help_y=(zTF->k*(x[1]*zTF->b0+buffer_x[0]*zTF->b1+buffer_x[1]*zTF->b2)
                -(buffer_y[0]*zTF->a1+buffer_y[1]*zTF->a2)) / zTF->a0;
    buffer_x[1]=buffer_x[0]; buffer_x[0]=x[1];
    buffer_y[1]=buffer_y[0]; buffer_y[0]=help_y;
  // Непосредственный алгоритм с одним буфером
  /* double help;
    help=(x[1]-(buffer_x[0]*zTF->a1+buffer_x[1]*zTF->a2))/zTF->a0;
    help_y=(help*zTF->b0+buffer_x[0]*zTF->b1+buffer_x[1]*zTF->b2)*zTF->k;
    buffer_x[1]=buffer_x[0]; buffer_x[0]=help;*/
  }
  y[0]=help_y;
  c=x[0]; // организованна синхронизация блока по фронту
};
//---------------------------------------------------------------------------
}// end extern "C" {
//---------------------------------------------------------------------------
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*){
  return 1;
}
<
Таблица 2

Файл pid.dpr
library PID;

type
  InVector = array [0..9] of Double;   { тип входной переменной: входов - 10 }
  OutVector = array [0..9] of Double; { тип выходной переменной: выходов - 10 }
  Global = record { запись параметров, и координат "dll"-модели }
    k : double;
    b0, b1, b2 : double;
    a0, a1, a2 : double;
end;

var buffer_x, buffer_y : array [0..1] of double;
    c, help_y : double;
{/************ Это базовая процедура в DLL ****************************/}
{/************ Вызывается VisSim- ом на каждом шаге моделирования ******/}
procedure zW(var zTF:Global; var x:InVector; var y:OutVector); export; stdcall;
var help : double;
begin
  if (x[0]=1)and(c=0) then
  begin
  { Непосредственный алгоритм с двумя буферами }
    help_y:=(zTF.k*(x[1]*zTF.b0+buffer_x[0]*zTF.b1+buffer_x[1]*zTF.b2)
           -(buffer_y[0]*zTF.a1+buffer_y[1]*zTF.a2))/zTF.a0;
    buffer_x[1]:=buffer_x[0]; buffer_x[0]:=x[1];
    buffer_y[1]:=buffer_y[0]; buffer_y[0]:=help_y;{}
  { Непосредственный алгоритм с одним буфером }
  { help:=(x[1]-(buffer_x[0]*zTF.a1+buffer_x[1]*zTF.a2))/zTF.a0;
    help_y:=(help*zTF.b0+buffer_x[0]*zTF.b1+buffer_x[1]*zTF.b2)*zTF.k;
    buffer_x[1]:=buffer_x[0]; buffer_x[0]:=help;{}
  end;
  y[0]:=help_y;
  c:=x[0]; { организованна синхронизация блока по фронту }
end;

{/************ Функция размещения параметров **************************/}
{/************ Вызывается VisSim-ом при создании блока ****************/}
function zWPA( var pCount:integer):Longint; export; stdcall;
begin
  pCount := 7; { число записываемых в файл параметров диалогового окна }
  zWPA := sizeof(Global);     { размер памяти необходимый под параметры }
end;

{/************ Процедура инициализации параметров *********************/}
{/************ Вызывается VisSim-ом после PA функции ******************/}
procedure zWPI( var zTF:Global ); export; stdcall;
begin
  zTF.k:=31.9016459416667;
  zTF.b0:=1;
  zTF.b1:=-1.9894401341982;
  zTF.b2:=0.98945592544195;
  zTF.a0:=1;
  zTF.a1:=-1.3333333333333;
  zTF.a2:=0.33333333333333;
end;

{/************ Функция изменения параметров ***************************/}
{/************ Вызывается VisSim-ом при нажатии правой клавиши мыши ***/}
function zWPC( var zTF:Global ):Pchar; export; stdcall;
begin
  zWPC :='k;b0;b1;b2;a0;a1;a2'; { названия могут быть любые }
end;

{/************ Процедура Simulation Start *****************************/}
{/************ Вызывается VisSim-ом на первом шаге моделирования ******/}
procedure zWSS( var zTF:Global; var runCount:longint); export; stdcall;
begin
  buffer_x[0]:=0; buffer_x[1]:=0;
  buffer_y[0]:=0; buffer_y[1]:=0;
  help_y:=0;      c:=0;
end;

{/************ Процедура Simulation End *******************************/}
{/************ Вызывается VisSim-ом на последнем шаге моделирования ***/}
procedure zWSE( var zTF:Global; var runCount:longint); export; stdcall;
begin
end;

exports
  zW index 1,{ Имя базовой процедуры в DLL Его нужно будет указать в блоке userFunction }
  zWPA index 2,{ Список вспомогательных процедур и функций для экспорта. Они будут }
  zWPI index 3,{ вызываться Vissim-ом по окончаниям PA,PI,PC,SS,SE для базового имени. }
  zWPC index 4,
  zWSS index 5,
  zWSE index 6;

begin
end.

Содержание раздела