MATLAB.Exponenta
–Û·Ë͇ Matlab&Toolboxes

Приложения с GUI и дескрипторная графика

Связь Simulink-модели и приложения с графическим интерфейсом

Данный раздел посвящен обмену данными Simulink-модели и приложения с графическим интерфейсом пользователя. Мы разберем один из вариантов реализации обмена, в котором значение некоторого параметра Simulink-модели устанавливается в приложении с графическим интерфейсом, а данные, получаемые в Simulink-модели, визуализируются на осях приложения с графическим интерфейсом.

Для чтения этого материала желательны знания

Некоторые необходимые сведения изложены в материале этого раздела.

Описание приложения с графическим интерфейсом и Simulink-модели

Рассмотрим простую задачу. В приложении GUI_Simulink с графическим интерфейсом (окно которого приведено ниже) есть полоса прокрутки и пара осей.


Окно приложения GUI_Simulink

Полоса прокрутки служит для изменения параметра Control в Simulink-модели от 0 до 5. Этот параметр влияет на выходной сигнал, возвращаемый Simulink-моделью, по следующему простому закону:

выходной сигнал = Control * sin(t)

Изменение значения Control приводит к отображению этого значения на верхних осях желтой линией и отображению значения выходного сигнала на нижних осях фиолетовой линией. Пределы по оси времени через каждые 50 сек. сдвигаются (на 50 сек.).

Сама Simulink-модель приведена ниже. Файл GUI_Simulink с моделью здесь.


Simulink-модель

Simulink-модель состоит из следующих блоков:

  • блока с именем Control (это библиотечный блок Constant);
  • блока с именем Sin Wave (библиотечный генератор синусоидального сигнала с параметрами по умолчанию);
  • мультиплексора, т.е. библиотечного блока Mux;
  • блока S-function, который как раз и является S-функцией, предназначенной для реализации обмена между Simulink-моделью и приложением GUI_Simulink (для того, чтобы задать S-функцию блока, достаточно сделать по нему двойной щелчок и в появившемся диалоговом окне Sink Block Parameters: S-function в строке ввода S-function задать имя mysfun (см. рисунок ниже).


Задание имени S-функции для блока S-function

Для солвера Simulink-модели сделаны следующие настройки, которые показаны на рисунке ниже (настройки солвера задаются в окне Configuration Parameters, которое появляется после выбора в меню Simulation окна модели пункта Configuration Parameters…).


Настройки солвера модели GUI_Simulink.mdl

После запуска Simulink-модели появляется окно приложения GUI_Simulink, в котором пользователь управляет значением Control и наблюдает за результатом. При остановке Simulink-модели появляется диалоговое окно с запросом, надо ли оставить окно приложения GUI_Simulink или его можно удалить.

Для того, чтобы блок S-function работал, необходимо запрограммировать функцию с подфункциями для:

  • инициализации блока S-function;
  • действиями на каждом шаге по времени;
  • действиями, выполняемыми перед завершением работы Simulink-модели.

Детально эта функция mysfun и подфункции обсуждаются в следующих разделах.

Совместная работа приложения с графическим интерфейсом и Simulink-модели происходит потому, что при инициализации S-функции создается окно приложения GUI_Simulink с полосой прокрутки и осями с заголовками. С событием Callback полосы прокрутки связывается функция, которая меняет значение константы блока Control. Далее на каждом шаге расчета модели S-функция получает на вход значения с выходов блоков Sine Wave и Control и выводит на оси приложения GUI_Simulink значение Control и Control * sin(t).

Основная проблема состоит в том, что подфункция, выводящая текущие значения Control и Control * sin(t) на оси приложения GUI_Simulink, должна знать указатели на оси. Для решения этой проблемы используется следующий механизм обмена указателями между подфункциями. При создании окна приложения указатель на него сохраняется в качестве значения свойства UserData блока S-function. Далее, при расчете на каждом шаге по времени указатель на окно приложения извлекается из свойства UserData и этот указатель используется для получения указателей на оси приложения.

S-функция mysfun

S-функция mysfun (ее текст приведен в разделе Текст S-функции mysfun с подфункциями) состоит из основной функции mysfun и подфункций, необходимых для работы S-функции и приложения с графическим интерфейсом. Основная функция mysfun вызывается как при инициализации Simulink-модели, так и при ее работе и перед остановкой Simulink-модели.

Аргументы mysfun

Входными аргументами функции mysfun являются:

  • t - текущее время;
  • x - вектор состояний (в нашем случае он не используется);
  • u - вектор значений входных сигналов (в нашем случае его длина равна 2, т.к. на вход блока S-function по шине приходит два сигнала);
  • flag - целое число 0, 1, 2, 3, 4 или 9, в зависимости от которого вызывается одна из подфункций mdlInitializeSizes, mdlUpdate, mdlOutputs или mdlTerminate.

Функция mysfun возвращает следующие величины

  • sys - зависит от того, какая из подфункций работает, т.е. от значения flag. Тогда sys является результатом работы соответствующей подфункции;
  • x0 - начальное состояние, которое определяется в функции mdlInitializeSizes (в нашем примере не используется);
  • str - зарезервировано для будущих расширений, пока значение str должно быть пустым массивом;
  • ts - матрица из двух столбцов, которая содержит шаги дискретизации и смещения по времени.

В зависимости от значения входного аргумента flag функции mysfun вызываются следующие подфункции (это реализовано при помощи условного переключателя switch).

  • Подфункция mdlInitializeSizes (см. раздел Подфункция mdlInitializeSizes), которая служит для инициализации S-функции и создания окна приложения с двумя парами осей и полосой прокрутки.
  • Подфункция mdlUpdate (см. раздел Подфункция mdlUpdate), которая предназначена для выполнения действий на каждом шаге по времени.
  • Подфункция mdlOutputs (см. раздел Подфункция mdlOutputs) возвращает сигналы на выходах блока S-function (в нашем примере и блока S-function нет выходов, поэтому выходным аргументом будет пустой массив).
  • Подфункция mdlTerminate (см. раздел Подфункция mdlTerminate) выполняется при завершении работы Simulink-модели, в ней производится запрос на удаление окна приложения GUI_Simulink.

Наша модель не имеет непрерывных состояний и работает с постоянным шагом, поэтому для работы S-функции достаточно перечисленных функций. В общем случае может понадобится еще и программирование подфункции mdlDerivatives, которая вычисляет производные от непрерывных состояний, и подфункции mdlGetTimeOfNextVarHit, которая находит значение следующего момента времени, при котором должен будет работать блок S-function.

Кроме перечисленных подфункций в функции mysfun есть еще подфункция sldCallback (см. раздел Подфункция sldCallback), которая служит для обработки события Callback полосы прокрутки, возникающего при изменении положения ее бегунка.

Подфункция mdlInitializeSizes

В подфункции mdlInitializeSizes выполняется инициализация параметров S-функции, для чего вызывается функция simsizes, возвращающая структуру sizes со следующими полями, каждое из которых по умолчанию имеет значение 0:

  • NumContStates -число непрерывных состояний блока S-function (в нашем примере их нет).
  • NumDiscStates - число дискретных состояний блока S-function (в нашем примере их так же задавать не надо).
  • NumOutputs - число выходов блока S-function (в нашей модели у блока S-functions нет выходов).
  • NumInputs - число входов блока S-function (в нашем примере их 2).
  • DirFeedthrough - флаг, задающий так называемый прямой проход (Direct Feedthrough), т.е. определяется ли значения на выходах блока S-function только значениями на его входах (в нашем примере он так же не нужен).
  • NumSampleTimes - число дискретизаций по времени, на которых работает блок S-function (в нашем случае он работает на одной частоте, поэтому полагаем NumSampleTimes=1).

После заполнения полей структуры sizes при помощи функции simsizes создается вектор sys с информацией об S-функции.

Далее в подфункции mdlInitializeSizes в качестве значения выходного аргумента x0 с начальными условиями для непрерывных состояний задается пустой массив (поскольку непрерывных состояний нет) и выходному аргументу str (который зарезервирован для будущих расширений) так же задается пустой массив.

После этого задается значение выходного аргумента ts с временем дискретизации блока S-function равным -1 (это значит, что время дискретизации наследуется из того блока, который обращается ко входу блока S-function).

На этом в функции mdlInitializeSizes задание параметров блока S-function завершается и начинается создание окна приложения GUI_Simulink с графическим интерфейсом.

Сначала при помощи функции figure создается окно, указатель на него записывается в переменную Fig. При создании окна используются следующие его свойства.

  • Position - положение окна на экране монитора, значение - вектор [x y width height], где (x,y) - координаты левого нижнего угла окна, width - ширина, а height - высота (все единицы по умолчанию в пикселях).
  • Color - цвет окна, 'k' значит черный.
  • MenuBar - отвечает за наличие стандартных меню и панели инструментов в графическом окне. Нам они не нужны, поэтому присваиваем значение 'none'.
  • Name - имя, выводимое в заголовке окна приложения, в нашем случае это имя приложения GUI_Simulink.
  • NumberTitle - отвечает за наличие в заголовке окна приложения слова Figure и номера окна, в нашем случае это не нужно, поэтому присваиваем свойству NumberTitle значение 'off'.

Далее в переменную InitControl записывается начальное значение параметра Control равное 1 и при помощи функции uicontrol создается полоса прокрутки внизу окна, бегунок которой установлен в значение, соответствующее 1. При создании полосы прокрутки используются следующие ее свойства.

  • Style - определяет тип создаваемого элемента управления, в нашем случае значение 'slider' соответствует полосе прокрутки.
  • Position - положение полосы прокрутки в окне, значение - вектор [x y width height], где (x,y) - координаты левого нижнего угла полосы прокрутки, width - ширина, а height - высота (все единицы по умолчанию в пикселях).
  • Min - минимальное значение свойства Value полосы прокрутки, соответствующее крайнему левому положению бегунка, присваиваем 0, т.к. значение параметра Control должно быть больше или равно 0.
  • Max - максимальное значение свойства Value полосы прокрутки, соответствующее крайнему правому положению бегунка, присваиваем 5, т.к. значение параметра Control должно быть меньше или равно 5.
  • Value - текущее значение, задаем равным InitControl.
  • Callback - указатель на функцию, выполняющуюся при изменении положения бегунка пользователем, в нашем примере это функция sldCallback (см. раздел Подфункция sldCallback).

После создания полосы прокрутки вызывается функция set_param для задания значения InitControl постоянной блока Control. Входными аргументами функции set_param являются:

  • Полное имя блока, которое формируется сцеплением строк из имени модели (его возвращает функция gcs) и имени блока 'Control'. Имя блока отделяется от имени модели косой чертой.
  • Имя свойства 'Value' блока.
  • Значение свойства 'Value', которое должно быть строковым, поэтому используется функция num2str для преобразования числового значения InitControl в строковое представление.

Далее при помощи функции axes создаются нижние и верхние оси. При создании осей задействованы следующие их свойства.

  • Tag - тег, используемый далее в подфункции mdlUpdate (см. раздел Подфункция mdlUpdate) для указания осей, на которые следует выводить график. Для нижней пары осей задается тег 'ax1', а для верхней - 'ax2'.
  • Units - единицы измерения, используемые для задания положения осей в графическом окне, мы будем задавать положение осей в пикселях, поэтому устанавливаем значение 'pixels'.
  • Position - положение осей в окне, значение - вектор [x y width height], где (x,y) - координаты левого нижнего угла осей, width - ширина, а height - высота (все единицы в пикселях).
  • NextPlot - свойство, отвечающее за графический вывод на оси. В нашем случае в подфункции mdlUpdate (см. раздел Подфункция mdlUpdate) мы будем строить графики соединяя текущую точку (соответствующую значению отображаемой величины в данный момент времени) с предыдущей (соответствующей значению отображаемой величины в предыдущий момент времени, в который работал блок S-function). Значение 'add' говорит о том, что новая линия должна добавиться, а старые остаться.
  • XLim и YLim - вектора из двух элементов с пределами осей абсцисс и ординат, соответственно. По оси абсцисс откладывается время, мы задаем пределы от 0 до 50, которые будем затем увеличивать в подфункции mdlUpdate (см. раздел Подфункция mdlUpdate). При задании пределов по оси ординат мы учитываем границы значений отображаемых величин.
  • Color - цвет фона осей, задаем значение 'k' (черный).
  • Xcolor и YColor - цвет осей абсцисс и ординат, соответственно, и их разметки. Задаем значение 'g' (зеленый).
  • Box - прямоугольная рамка вокруг координатных осей, устанавливаем ее, задавая значение 'on'.
  • XGrid и YGrid - сетка по каждой из координатных осей, включаем ее, задавая значение 'on'.

Далее в функции mdlInitializeSizes помощи функции uicontrol создаются два статических текстовых объекта с заголовками Signal (для нижних осей) и Control (для верхних осей). При создании текстовых объектов используются следующие их свойства.

  • Style - определяет тип создаваемого элемента управления, в нашем случае значение 'text' соответствует статическому тексту.
  • String - надпись на объекте.
  • Position - положение текстового объекта в окне, значение - вектор [x y width height], где (x,y) - координаты левого нижнего угла текстового объекта, width - ширина, а height - высота (все единицы по умолчанию в пикселях).
  • FontSize - размер шрифта, по умолчанию он задается в пунктах.
  • FontWeight - жирность шрифта, 'bold' значит полужирный.
  • BackgroundColor - цвет фона, задаем 'k' (черный).
  • ForegroundColor - цвет текста, задаем 'm' (фиолетовый).

Последнее очень важное действие, выполняемое в функции mdlInitializeSizes, состоит в сохранении указателя на графическое окно Fig в свойстве UserData блока S-function. Это делается при помощи функции set_param, первым аргументом которой должен быть указатель на блок S-function. Указатель на текущий блок (т.е. как раз на блок S-function в момент его работы) возвращается функцией gcbh, поэтому запись указателя на окно приложения осуществляется при помощи следующего обращения

set_param(gcbh, 'UserData', Fig)

Подфункция mdlUpdate

Подфункция mdlUpdate вызывается на каждом шаге по времени. Ее входные аргументы содержат:

  • t - текущее время;
  • x - вектор состояний (в нашем примере не используется);
  • u - вектор, содержащий значения на входах блока S-function. В нашем случае элемент u(1) содержит значение Control, а элемент u(2) - текущую величину синусоидального сигнала.

Для вывода нужной информации на оси окна приложения GUI_Simulink необходимо получить указатели на соответствующие объекты. Указатель на окно приложения записан в свойстве UserData блока S-function. Он извлекается в переменную Fig при помощи функции get_param, в качестве первого входного аргумента которой задается функция gcbh, возвращающая указатель на текущий блок, т.е. на блок S-function. Далее указатели на объекты приложения записываются в переменную Handles при помощи функции guihandles, вызванной от указателя на окно. Функция guihandles возвращает структуру, поля которой совпадают с тегами объектов, данными им при создании в функции mdlInitializeSizes. Таким образом в Handles.ax1 содержится указатель на нижние оси, а в Handles.ax2 - указатель на верхние оси окна приложения.

Далее в подфункции mdlUpdate проверяется, нужно ли увеличить пределы оси времени на 50 сек. и производится вывод значения Control и Control * sin(t) на верхние и нижние оси, соответственно. Задача заключается в нанесении точки на оси и соединении ее с предыдущей точкой отрезком. Предыдущее значение времени и выводимой на оси величины хранится в свойстве UserData осей. Перед выводом это значение (т.е. вектор из двух элементов) извлекается при помощи функции get в переменную LastPoint. Если LastPoint содержит пустой массив, то значит предыдущей точки не было и выводимая сейчас точка соответствует значению для первого момента времени. Если LastPoint не пуст, то соединяем текущую точку с предыдущей отрезком.

В конце функции mdlUpdate выходному аргументу sys присваивается пустой массив, поскольку выходов у блока S-function нет.

Подфункция mdlOutputs

Подфункция mdlOutputs вычисляет значения на выходах, но в нашем примере у блока S-function нет выходов, поэтому в подфункции mdlOutputs единственный оператор

sys = [];

Подфункция mdlTerminate

Подфункция mdlTerminate выполняется при завершении работы Simulink-модели. В mdlTerminate при помощи стандартной функции questdlg выводится диалоговое окно с запросом на удаление окна приложения GUI_Simulink.


Окно с запросом на удаление окна приложения

Входными аргументами функции questdlg являются:

  • текст в окне запроса;
  • заголовок окна запроса;
  • названия кнопок;
  • последний аргумент означает кнопку, которой по умолчанию передается фокус ввода.

Функция questdlg возвращает надпись на нажатой пользователем кнопке. Если пользователь нажал кнопку Yes, то при помощи функции get_param указатель на окно приложения записывается в переменную Fig и затем вызывается функция delete для удаления окна. Входным аргументом функции delete должен быть указатель на удаляемый объект.

Подфункция sldCallback

Подфункция sldCallback служит для обработки события Callback, которое возникает при изменении положения бегунка полосы прокрутки. Ее первый входной аргумент src содержит указатель на полосу прокрутки, а второй evt не используется, но нужен при программировании функций обработки событий от элементов интерфейса.

В подфункции sldCallback при помощи функции get значение, соответствующее положению бегунка, (т.е. значение свойства Value) записывается в переменную s. Далее используется функция set_param для задания этого значения блоку Control нашей Simulink-модели. Первый ее аргумент является полным именем блока, которое формируется сцеплением строк. Первая из них - имя модели - возвращается функцией gcs, а вторая является именем блока. (Полное имя блока является иерархическим, оно состоит из имени системы, дальше возможны подсистемы, которым принадлежит блок, и имени самого блока, причем все имена разделяются косой чертой). При задании значения константе в Simulink-модели требуется преобразование числового значения в строковое представление, которое выполняется при помощи функции num2str.

Текст S-функции mysfun с подфункциями

function [sys, x0, str, ts] = mysfun(t, x, u, flag)
% Основная функция для блока S-function

% Определение действия в зависимости от значения flag
switch flag
    case 0
        % Инициализация S-функции
        [sys, x0, str, ts] = mdlInitializeSizes;
    case 2
        % Действия, производимые на каждом шаге по времени
        sys=mdlUpdate(t, x, u);
    case 3
        % Вычисление значений на выходах S-функции
        sys=mdlOutputs(t,x,u);
    case 9
        % Завершение работы
        sys=mdlTerminate(t,x,u);
    otherwise
        % выводим ошибку, т.к. действие не определено
        error(['Unhandled flag = ',num2str(flag)]);
end


function [sys, x0, str, ts] = mdlInitializeSizes
% Инициализация S-функции

% Получение структуры с описанием S-функции
sizes = simsizes;
% задание числа входных портов
sizes.NumInputs = 2;
% число дискретизаций по времени
sizes.NumSampleTimes = 1; 
% создание вектора с информацией об S-функции
sys = simsizes(sizes);

% задание начальных условий
x0  = [];
% параметр str зарезервирован для будущих расширений,
% полагаем его пустым массивом
str = [];
% задание вектора с шагами расчета
ts  = [-1 0];

% создание графического окна GUI_Simulink 
% и запись указателя на него в переменную Fig
Fig=figure('Position',[100 100 400 300],...
    'Color', 'k',...
    'MenuBar', 'none',...
    'Name', 'GUI_Simulink',...
    'NumberTitle', 'off');

% задание начального значения параметра Control
InitControl = 1;

% создание полосы прокрутки, значения которой 
% изменяются от 0 до 5, начальное значение равно InitControl
uicontrol('Style',   'slider',...
    'Position',[10 10 380 15],...
    'Min', 0, 'Max', 5, 'Value', InitControl,...
    'Callback', @sldCallback);

% установка константе Control в Simulink-модели 
% значения InitControl
set_param([gcs '/Control'],'Value',num2str(InitControl));

% создание нижних осей для вывода сигнала
axes('Tag', 'ax1',...
  'Units', 'pixels',...
  'Position',[30 50 350 100],...
  'NextPlot','add',...
  'Xlim',    [0 50],...
  'Ylim',    [-5.2 5.2],...
  'Color','k',...
 'Xcolor', 'g',...
 'YColor', 'g',...
  'Box', 'on',...
 'XGrid', 'on',...
 'YGrid', 'on');
% создание верхних осей для вывода параметра Control
axes('Tag', 'ax2',...
  'Units', 'pixels',...
  'Position',[30 180 350 100],...
  'NextPlot','add',...
  'Xlim',    [0 50],...
  'Ylim',    [-0.2 5.2],...
  'Color','k',...
 'Xcolor', 'g',...
 'YColor', 'g',...
  'Box', 'on',...
 'XGrid', 'on',...
 'YGrid', 'on');

% создание заголовка Signal для нижних осей
uicontrol('Style', 'text',...
    'String', 'Signal',...
    'Position', [180 152 50 16],...
    'FontSize', 10,...
    'FontWeight', 'bold',...
    'BackgroundColor', 'k',...
    'ForegroundColor', 'm')
% создание заголовка Control для верхних осей
uicontrol('Style', 'text',...
    'String', 'Control',...
    'Position', [180 282 50 16],...
    'FontSize', 10,...
    'FontWeight', 'bold',...
    'BackgroundColor', 'k',...
    'ForegroundColor', 'y')

% сохранение указателя на графическое окно в свойстве
% UserData S-функции
set_param(gcbh, 'UserData', Fig);

function sys=mdlUpdate(t, x, u)
% действия, выполняемые на каждом шаге по времени

% получаем указатель на графическое окно GUI_Simulink
Fig = get_param(gcbh,'UserData');
% получаем структуру указателей на объекты окна GUI_Simulink
Handles = guihandles(Fig);
% Выясняем, нужно ли сдвинуть пределы по оси времени на 50 сек.
PassedSampleNum = int32(t*10);
if mod(PassedSampleNum, 500) == 0  
    set(Handles.ax1, 'XLim', [t t+50])
    set(Handles.ax2, 'XLim', [t t+50])
end
% делаем текущими нижние оси
axes(Handles.ax1)
% получаем координаты предыдущей точки
LastPoint = get(Handles.ax1, 'UserData');
if ~isempty(LastPoint)
    % соединяем предыдущую точку линией с текущей
    plot([LastPoint(1) t],[LastPoint(2) u(2)*u(1)],...
        'Color', 'm', 'LineWidth', 2)
    set(Handles.ax1, 'UserData', [t, u(1)*u(2)])
else
    % предыдущей точки не было, т.к. текущая точка - первая
    % выводим текущую точку на оси
    set(Handles.ax1, 'UserData', [t, u(1)*u(2)])
    plot(t, u(1)*u(2),  'Color', 'm', 'LineWidth', 2 )
end
% делаем текущими верхние оси
axes(Handles.ax2)
% получаем координаты предыдущей точки
LastPoint = get(Handles.ax2, 'UserData');
if ~isempty(LastPoint)
    % соединяем предыдущую точку линией с текущей
    plot([LastPoint(1) t],[LastPoint(2) u(1)],...
        'Color', 'y', 'LineWidth', 2)
    set(Handles.ax2, 'UserData', [t, u(1)])
else
    % предыдущей точки не было, т.к. текущая точка - первая
    % выводим текущую точку на оси
    set(Handles.ax2, 'UserData', [t, u(1)])
    plot(t, u(1),  'Color', 'y', 'LineWidth', 2 )
end
sys = [];

function sys=mdlOutputs(t,x,u)
% вычисление значений на выходах блока S-function

%выходов нет, поэтому присваиваем пустой массив.
sys=[];

function sys=mdlTerminate(t,x,u)
% действия, выполняемые при завершении работы 
% Simulink-модели

% выводим диалоговое окно с запросом на удаление окна GUI_Simulink
button = questdlg('Delete the GUI_Simulink window?','GUI_Simulink',...
    'No','Yes','No');
if isequal(button, 'Yes') 
    % если было выбрано удаление, то получаем указатель
    % на окно GUI_Simulink
    Fig = get_param(gcbh,'UserData');
    % удаляем его
    delete(Fig)
end
sys = [];


function sldCallback(src,evt)
% обработка события Callback полосы прокрутки

% получаем значение, установленное полосой прокрутки
s = get(src, 'Value');
% задаем это значение константе Control в Simulink-модели
set_param([gcs '/Control'], 'Value', num2str(s));


Поиск по сайту:

Система Orphus

Яндекс.Метрика