|
Цель работы: получить практические навыки в использовании средств межпроцессного взаимодействия в ОС UNIX на примере моделирования цифровой системы управления.
Часть 1.
А. На основании структурной схемы объекта управления получить его математическую модель в стандартной форме, а именно: в виде системы дифференциальных уравнений
Y` = A*Y + B*v (1)
Yout = C*Y (2)
где Y - вектор переменных состояния объекта;
Y` - вектор первых производных переменных состояния объекта;
v - входная величина;
Yout - выходная величина;
A, B и C - матрицы с постоянными коэффициентами;
Б. Переработать прилагаемую программу на языке Си, позволяющую получить кривую разгона объекта при подаче на его вход ступенчатого воздействия:
переопределить глобальные переменные, соответствующие коэффициентам усиления и постоянным времени звеньев структурной схемы, так, чтобы они соответствовали данным вашего варианта;
переопределить текст функции modl, так, чтобы она содержала систему уравнений (1) для вашего варианта;
переопределить выражение для вычисления выходной величины в соответствии с уравнением (2) для вашего варианта;
задать шаг по времени для интегрирования, равным приблизительно 1/100 от минимальной постоянной времени звеньев, а общее время переходного процесса - приблизительно утроенному значению максимальной постоянной времени звеньев.
В. Запустить переработанную программу на исполнение, построить график полученной кривой разгона объекта и включить его в отчет. Для построения графика можно использовать программу gnuplot.
Г. Проверить корректность модели объекта. Для этого получить таблицу кривой разгона объекта при помощи программы myMik - для Linux или myMik.exe - для Windows. Для этого создать текстовый файл, содержащий описание структурной схемы объекта и состоящий из 3 частей:
описание звеньев начинается со строчки, задающей число звеньев, в последующих строчках описываются звенья: буква, определяющая тип звена; целое число, задающее номер звена; одно или несколько чисел с плавающей точкой, определяющих параметры звена. После этого можно поставить значок # и добавить в этой эе строке комментарий произвольного вида.
входное воздействие V N v
пропорциональное звено P N k
интегрирующее звено I N t y0
апериодическое звено A N t k y0
где V,P.I,A - буква, определяющая тип звена;
N - номер звена;
v - входное воздействие;
k - коэффициент усиления звена;
t - постоянная времени звена;
y0 - начальное условие;
после этого добавляется строчка, содержащая значок-разделитель &, указывающий на то, что далее идет описание связей между звеньями. Каждый вход звена описывается строчкой:
Nвх <- Nвых k
где Nвх, Nвых - номера входного и выходного звеньев;
k - коэффициент усиления на входе, обычно 1.0 или -1.0, в соответствии со знаком сумматора, через который проходит связь.
после этого добавляется строчка, содержащая значок-разделитель T, указывающий на то, что далее идет описание времени, а именно: шаг по времени и общее время переходного процесса.
Программа myMik получает данный текстовый файл в качестве параметра командной строки. В ходе работы программа создает несколько текстовых файлов с именами outN.dat, где N - номер звена. Т.е. на выходе мы получаем зависимость выходной величины от времени для каждого из звеньев структурной схемы. Одно из звеньев является выходным и содержит данные, являющиеся кривой разгона всего объекта. Если на выходе объекта имеется сумматор, то после него можно добавить пропорциональное звено с коэффициентом усиления 1.0. Выход этого звена будет выходом объекта.
Д. В отчете представить первые 10 строчек таблиц из файлов, полученных в пунктах В и Г. Совпадение данных должно быть полным. Если этого не наблюдается, то необходимо проверить пункты А и Г и исправить ошибки.
Рассмотрим получение кривой разгона объекта, представленного структурной схемой №:
В качестве переменных состояния выберем значения на выходе апериодических звеньев.
Система дифференциальных уравнений будет иметь вид:
py1[0]=((V-py[1])*K1-py[0])/T1; // y1
py1[1]=(py[0]*K5*K4*K3*K2-py[1])/T2; // y2
py1[2]=(V*K6-py[2])/T6; // y3
py1[3]=(V*K8-py[3])/T8; // y4
а выходная величина будет равна:
Yout =(Y[0]+Y[2])*K7+Y[3];
Шаг по времени будет 60/100 = 0.6; принимаем dT=1.0.
Продолжительность процесса 3*250 = 750.0
Текст программы для получения кривой разгона данного объекта будет следующим:
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
typedef void (*mdl)(float t, float* py, float* py1);
void init_rk(int N, float** py);
void done_rk(float** py);
void rkt4(float t, float dt, float* py, mdl model);
void modl(float t, float* py, float* py1);
float* Y;
int n=4; // порядок системы
float dT,T,Tm;
float V;
float Yout;
float K1=1.8, K2=1.0, K3=2.8, K4=0.4, K5=1.7; // коэффициенты усиления
float K6=0.7, K7=2.9, K8=0.8;
float T1=190.0, T2=60.0, T6=250.0, T8=90.0; //постоянные времени звеньев
FILE *fout;
int main(){
init_rk(n, &Y);
T=0.0;
dT=1.0; // шаг по времени
Tm=750; // продолжительность процесса
V=1.0; // входное воздействие
Y[0]=Y[1]=Y[2]=Y[3]=0.0;
fout = fopen("razgon_ira.dat", "w");
while(T<Tm){
Yout =(Y[0]+Y[2])*K7+Y[3]; // выходная величина
fprintf(stderr,"%5.1f %8.5f\n",T,Yout);
fprintf(fout,"%5.1f %8.5f\n",T,Yout);
rkt4(T,dT,Y, &modl);
T+=dT;
}
fclose(fout);
done_rk(&Y);
return 0;
}
void modl(float t, float* py, float* py1){
py1[0]=((V-py[1])*K1-py[0])/T1; // система дифференциальных уравнений
py1[1]=(py[0]*K5*K4*K3*K2-py[1])/T2;
py1[2]=(V*K6-py[2])/T6;
py1[3]=(V*K8-py[3])/T8;
}
static float* k[4], *py1, *pdy;
static int N_s;
void init_rk(int N, float** py){
int i;
for (i=0; i<4; i++)
k[i]=(float*)calloc(N, sizeof(float));
*py=(float*)calloc(N, sizeof(float));
py1=(float*)calloc(N, sizeof(float));
pdy=(float*)calloc(N, sizeof(float));
N_s=N;
}
void done_rk(float** py){
for (int i=0; i<4; i++) free(k[i]);
free(*py);
free(py1);
free(pdy);
}
void rkt4(float t, float dt, float* py, mdl model){
int i;
for (i=0; i<N_s; i++) pdy[i]=py[i];
model(t,pdy,py1);
for (i=0; i<N_s; i++) k[0][i]=dt*(py1[i]);
for (i=0; i<N_s; i++) pdy[i]=py[i]+k[0][i]/2.0;
model(t+dt/2.0,pdy,py1);
for (i=0; i<N_s; i++) k[1][i]=dt*(py1[i]);
for (i=0; i<N_s; i++) pdy[i]=py[i]+k[1][i]/2.0;
model(t+dt/2.0,pdy,py1);
for (i=0; i<N_s; i++) k[2][i]=dt*(py1[i]);
for (i=0; i<N_s; i++) pdy[i]=py[i]+k[2][i];
model(t+dt,pdy,py1);
for (i=0; i<N_s; i++) k[3][i]=dt*(py1[i]);
for (i=0; i<N_s; i++){
pdy[i]=(k[0][i]+2.0*k[1][i]+2.0*k[2][i]+k[3][i])/6.0;
py[i]+=pdy[i];
}
}
В результате работы программы был получен файл razgon_ira.dat, который был использован для построения графика кривой разгона объекта
Cоздаем текстовый файл obj_ira.dat, содержащий описание структурной схемы объекта. Для этого пронумеруем звенья структурной схемы, как показано на рисунке от 1 до 10. В результате получится:
V 1 1.0
A 2 190 1.8 0.0
A 3 60 1.0 0.0
P 4 2.8
P 5 0.4
P 6 1.7
A 7 250 0.7 0.0
P 8 2.9
A 9 90 0.8 0.0
P 10 1.0
&
2<-1 1.0
2<-3 -1.0
3<-4 1.0
4<-5 1.0
5<-6 1.0
6<-2 1.0
7<-1 1.0
8<-2 1.0
8<-7 1.0
9<-1 1.0
10<-8 1.0
10<-9 1.0
T
1.0
Выход объекта совпадает с выходом звена N10. Сравниваем между собой содержимое файлов razgon_ira.dat и out10.dat:
Как видим, имеется полное совпадение, следовательно, математическая модель объекта в виде системы дифференциальных уравнений соответствует структурной схеме.
Часть 2.
А. Переработать прилагаемые программы диспетчер, регулятор и объект таким образом, чтобы они соответствовали вашему варианту.
Программа-объект, также как и программа для получения кривой разгона должна включать в себя глобальные переменные, соответствующие коэффициентам усиления и постоянным времени звеньев структурной схемы, шаг по времени, текст функции modl, содержащий систему уравнений (1) для вашего варианта, выражение для вычисления выходной величины в соответствии с уравнением (2) для вашего варианта.
Программа регулятор должна при каждом вызове определять текущее значение ошибки, интеграл от ошибки (методом прямоугольников) и первую производную от ошибки (как отношение разностей), а также вычислять управляющее воздействие на объект на основе ПИД-закона регулирования.
Программа диспетчер должна определять общее время процесса - это максимальная постоянная времени объекта, умноженная на 6, а также соотношение шагов по времени регулятора и объекта; на первом этапе это величина равна 10. Диспетчер координирует работу регулятора и объекта при помощи группы из 2-х процессных семафоров.
В разделяемой памяти размещаются следующие переменные:
Yt - текущее значение величины на выходе объекта;
Yn - требуемой значение величины на выходе объекта (во всех вариантах это 1.0);
Ur - управляющее воздействие, вычисленное регулятором;
dt - шаг по времени объекта;
t - текущее время;
final - флажок, свидетельствующий об окончании процесса;
Nreg - соотношение шагов по времени регулятора и объекта;
Б. Запустить на исполнение программы в следующей последовательности: регулятор, объект, диспетчер.
Установить соотношение шагов по времени регулятора и объекта равным 10.
Подобрать параметры настройки ПИД-регулятора, так, чтобы переходный процесс имел более или менее приемлемые показатели качества регулирования.
Записать в два текстовых файла величину на выходе объекта и управляющее воздействие, а также время.
Построить графики по данным этих текстовых файлов и включить их в отчет.
В. Установить соотношение шагов по времени регулятора и объекта равным 100.
Построить графики по данным текстовых файлов (величина на выходе объекта и управляющее воздействие в функции времени) и включить их в отчет.
РЕГУЛЯТОР
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/sem.h>
#define MEM 0x1234 //ключ участка разделяемой памяти
#define SEM 0xABBA //ключ набора семафоров
union semun //объединение для работы с процессными семафорами
{
int val; //начальное значение счетчика
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};
union semun sm;
struct sembuf smb; //описываем переменную для процессного семафора
int dmem, dsem;
char* pmem;
float *Yt, *Yn, *Ur, *Uo, *dt, *t;
int *final;
int *Nreg;
float eps;
float K=0.8, Q=0.005, Td=0.0001; //коэффициенты ПИД-регулятора
float ieps;
float deps;
float peps;
int main(int argc, char *argv[])
{
dmem=shmget(MEM, getpagesize(), IPC_CREAT | S_IRUSR | S_IWUSR); //получаем идентификатор участка разделяемой памяти
pmem=(char*)shmat(dmem,0,0); //подключаемся к разделяемой памяти
dsem=semget(SEM, 2, IPC_CREAT | S_IRUSR | S_IWUSR); //подключаемся к набору семафоров
sm.val=0; //устанавливаем начальное значение счетчика
semctl(dsem,1,SETVAL,sm); //инициализируем семафор
smb.sem_num=1; //работаем с первым семафором из набора
smb.sem_flg=0;
smb.sem_op=-1; //устанавливаем операцию над счетчиком семафора
Yt=(float*)pmem; //инициализируем переменные
Yn=(float*)(pmem+1*sizeof(float));//в разделяемой памяти
Ur=(float*)(pmem+2*sizeof(float));
dt=(float*)(pmem+4*sizeof(float));
t=(float*)(pmem+5*sizeof(float));
final=(int*)(pmem+6*sizeof(float));
Nreg=(int*)(pmem+6*sizeof(float)+sizeof(int));
*final=0; //устанавливаем флаг конца обмена в ноль
*Yn = 1.0; //величина задания
*Ur=0.0;
ieps=0.0;
peps=0.0;
//входим в цикл обмена, условие выхода - установка флага конца обмена в 1
while(!*final){
semop(dsem, &smb, 1); //уменьшаем счетчик семафора первый раз
eps= *Yn - *Yt; //вычисляем ошибку регулирования
ieps+=eps*(*dt)*(*Nreg);
deps = (peps-eps)/(*dt)/(*Nreg);
peps = eps;
*Ur = K*eps + Q*ieps + Td*deps; //вычисляем управляющее воздействие
semop(dsem, &smb, 1); //уменьшаем счетчик семафора второй раз
}
return 0;
}
ОБЪЕКТ
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/sem.h>
#define MEM 0x1234 //ключ участка разделяемой памяти
#define SEM 0xABBA //ключ набора семафоров
typedef void (*mdl)(float t, float* py, float* py1);
void init_rk(int N, float* *py);
void done_rk(float** py);
void rkt4(float t, float dt, float* py, mdl model);
void modl(float t, float* py, float* py1);
union semun //объединение для работы с процессными семафорами
{
int val; //начальное значение счетчика
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};
union semun sm;
struct sembuf smb; //описываем переменную для процессного семафора
int dmem, dsem;
char* pmem;
float* Y;
int n=4;
float V;
float K1=1.8, K2=1.0, K3=2.8, K4=0.4, K5=1.7; //коэффициенты звеньев ОУ
float K6=0.7, K7=2.9, K8=0.8; //коэффициенты звеньев ОУ
float T1=190.0, T2=60.0, T6=250.0, T8=90.0; //постоянные времени звеньев ОУ
int *final;
float *Yt, *Yn, *Ur, *dt, *t;
FILE *fYout, *fYin; //дескрипторы файлов с данными
int main(){
dmem=shmget(MEM, getpagesize(), IPC_CREAT | S_IRUSR | S_IWUSR); //получаем идентификатор участка разделяемой памяти
pmem=(char*)shmat(dmem,0,0); //подключаемся к разделяемой памяти
dsem=semget(SEM, 2, IPC_CREAT | S_IRUSR | S_IWUSR); //подключаемся к набору семафоров
sm.val=0; //устанавливаем начальное значение счетчика
semctl(dsem,0,SETVAL,sm); //инициализируем семафор
smb.sem_num=0; //работаем с нулевым семафором из набора
smb.sem_flg=0;
smb.sem_op=-1; //устанавливаем операцию над счетчиком семафора
Yt=(float*)pmem; //инициализируем переменные
Yn=(float*)(pmem+1*sizeof(float));//в разделяемой памяти
Ur=(float*)(pmem+2*sizeof(float));
dt=(float*)(pmem+4*sizeof(float));
t=(float*)(pmem+5*sizeof(float));
final=(int*)(pmem+6*sizeof(float));
init_rk(n, &Y); //выделяем память под переменные
Y[0]=Y[1]=Y[2]=Y[3]=0.0; //начальные значения переменных на выходе системы
*final=0; //устанавливаем флаг конца обмена в ноль
*t=0.0; //начало отсчета по времени
*dt=1.0; //шаг по времени
fYout=fopen("Yout.dat","w"); //открываем файл Yout на запись
fYin=fopen("Yin.dat","w"); //открываем файл Yin на запись
//входим в цикл обмена, условие выхода - установка флага конца обмена в 1
while(!*final){
semop(dsem, &smb, 1); //уменьшаем счетчик семафора первый раз
*Yt = (Y[0]+Y[2])*K7+Y[3]; //вычисляем выходную величину
fprintf(fYout,"%7.2f %9.5f\n", *t, *Yt); //записываем в файл Yout выходную величину
fprintf(fYin,"%7.2f %12.5g\n", *t, *Ur); //записываем в файл Yin управляющее воздействие
fprintf(stderr,"%7.2f %9.5f\n", *t, *Yt); //выводим на экран выходную величину
rkt4(*t,*dt,Y, &modl); //решаем систему диф. уравнений
*t+=*dt; //увеличиваем время
semop(dsem, &smb, 1); //уменьшаем счетчик семафора второй раз
}
fclose(fYout); //закрываем файлы
fclose(fYin);
return 0;
}
void modl(float t, float* py, float* py1){ //функция моделирует поведение
V = *Ur; //объекта управления
//через систему диф. уравнений
py1[0]=((V-py[1])*K1-py[0])/T1;
py1[1]=(py[0]*K5*K4*K3*K2-py[1])/T2;
py1[2]=(V*K6-py[2])/T6;
py1[3]=(V*K8-py[3])/T8;
}
static float* k[4], *py1, *pdy;
static int N_s;
void init_rk(int N, float** py){ //функция производит выделение
int i; //динамической памяти
for (i=0; i<4; i++) k[i]=(float*)calloc(N, sizeof(float));
*py=(float*)calloc(N, sizeof(float));
py1=(float*)calloc(N, sizeof(float));
pdy=(float*)calloc(N, sizeof(float));
N_s=N;
}
void done_rk(float** py){ //функция освобождает динамическую память
for (int i=0; i<4; i++) free(k[i]);
free(*py);
free(py1);
free(pdy);
}
void rkt4(float t, float dt, float* py, mdl model){ //функция моделирует
int i; //решение системы
for (i=0; i<N_s; i++) pdy[i]=py[i]; //диф. уравнений
model(t,pdy,py1); //методом Рунге-Кутта
for (i=0; i<N_s; i++) k[0][i]=dt*(py1[i]);
for (i=0; i<N_s; i++) pdy[i]=py[i]+k[0][i]/2.0;
model(t+dt/2.0,pdy,py1);
for (i=0; i<N_s; i++) k[1][i]=dt*(py1[i]);
for (i=0; i<N_s; i++) pdy[i]=py[i]+k[1][i]/2.0;
model(t+dt/2.0,pdy,py1);
for (i=0; i<N_s; i++) k[2][i]=dt*(py1[i]);
for (i=0; i<N_s; i++) pdy[i]=py[i]+k[2][i];
model(t+dt,pdy,py1);
for (i=0; i<N_s; i++) k[3][i]=dt*(py1[i]);
for (i=0; i<N_s; i++){
pdy[i]=(k[0][i]+2.0*k[1][i]+2.0*k[2][i]+k[3][i])/6.0;
py[i]+=pdy[i];
}
}
ДИСПЕТЧЕР
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/sem.h>
#define MEM 0x1234 //ключ участка разделяемой памяти
#define SEM 0xABBA //ключ набора семафоров
union semun //объединение для работы с процессными семафорами
{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};
union semun sm;
struct sembuf smb_r, smb_o; //описываем семафоры для взаимодействия с
//регулятором и ОУ
int dmem, dsem;
char* pmem;
float *Yt, *Yn, *Ur, *Uo, *dt, *t;
int *final;
int *Nreg;
float Tm;
int main()
{
dmem=shmget(MEM, getpagesize(), IPC_CREAT | S_IRUSR | S_IWUSR); //получаем идентификатор участка разделяемой памяти
pmem=(char*)shmat(dmem,0,0); //подключаемся к разделяемой памяти
dsem=semget(SEM, 2, IPC_CREAT | S_IRUSR | S_IWUSR); //подключаемся к набору семафоров
smb_o.sem_num=0; //семафор для взаимодействия с ОУ
smb_o.sem_flg=0;
smb_r.sem_num=1; //семафор для взаимодействия с ПИД-регулятором
smb_r.sem_flg=0;
Yt=(float*)pmem; //инициализируем переменные
Yn=(float*)(pmem+1*sizeof(float)); //в разделяемой памяти
Ur=(float*)(pmem+2*sizeof(float));
dt=(float*)(pmem+4*sizeof(float));
t=(float*)(pmem+5*sizeof(float));
final=(int*)(pmem+6*sizeof(float));
Nreg=(int*)(pmem+6*sizeof(float)+sizeof(int));
*Yn = 1.0; //величина задания
Tm = 1500.0; //продолжительность обмена
*Nreg = 10; //отношение шага объекта к шагу регулятора
//входим в цикл обмена, условие выхода - истечение времени, отведенного на процесс регулирования
while(*t < Tm)
{
smb_r.sem_op=2; //операция над счетчиком: +2
semop(dsem, &smb_r,1); //выполняем операцию
smb_r.sem_op=0; //операция над счетчиком: +0
semop(dsem, &smb_r,1); //попытка выполнить операцию
smb_o.sem_op=(*Nreg)*2; //операция над счетчиком: +2*Nreg
semop(dsem, &smb_o,1); //выполняем операцию
smb_o.sem_op=0; //операция над счетчиком: +0
semop(dsem, &smb_o,1); //попытка выполнить операцию
}
*final=1; //устанавливаем флаг конца обмена в единицу
shmdt(pmem); //отключаемся от разделяемой памяти
shmctl(dmem,IPC_RMID,0); //удаляем участок разделяемой памяти
semctl(dsem,0,IPC_RMID,sm); //удаляем набор семафоров
return 0;
}
Nreg = 10
Nreg = 100
Дата добавления: 2015-11-04; просмотров: 24 | Нарушение авторских прав
<== предыдущая лекция | | | следующая лекция ==> |
Министерство образования и науки Российской Федерации | | | Тема работы: Сглаживание эмпирических данных и численное дифференцирование. |