A GUI to Set Simulink Model Parameters
设置Simulink模型参数实例
这个例子介绍了如何制作一个可以设置Simulink模型参数的GUI界面。另外,这个GUI也可以运行仿真以及画出结果。以下这张图显示了GUI在运行三个不同控制增益参数的模型后的界面。
Proportional(Kf)为需要设置的参数之一,Integral(Ki)为所要设置的参数之二。每个参数都使用一组配套的滑条和编辑框来设置。右边的Results list按顺序列出了运行得到的仿真结果标签。
-------------------------------(下文代码复制过来的,仅作参考不保证粘贴后正确性,请参照源文件)
本例所用到的一些技术
本例所用到的GUI构建知识包括:
●∙∙从GUI界面打开Simulink模型并设置其参数;
●∙∙Slider滑条控件和编辑框Edit text控件的联合使用——显示用户已输入的值;
●∙∙根据GUI的状态来决定某些GUI控件是否激活;
●∙∙使用handles结构来管理共享数据;
●∙∙使用隐藏handles进行绘图;
●∙∙增加一个help按钮,在MATLAB Help browser中显示本例的帮助网页;
--------------------------------
如何打开本例程序(跳过)
如何使用GUI(讲解各个按钮的作用,跳过)
运行GUI
GUI Options Settings
●Resize behavior(缩放): Non-resizable不可
●Command-line accessibility(接收命令行): Off关闭
●M-file options selected:
⏹Generate callback function prototypes自动生成回调函数原型
⏹GUI allows only one instance to run只允许同时运行一个实例
这里的是GUI的一些框架设置,如果希望设计出来的GUI界面是可以缩放的,在设计界面的tool->GUI Option。Resize behavior选择Proportional,则控件的大小也跟着窗口自动变化(2007b版本测试)。
Command-line accessibility主要有四种选择:
on(GUI may become Current Figure from command line),
off(GUI never becomes Current Figure),
callback(GUI becomes Current Figure within callbacks),
other(Use settings from property Inspector)。
一般都采用系统默认的callback(2007b版本测试)。而对于M文件的选项设置,对于新手来说均采取默认为佳。其中选中第一项,往GUI添加一个控件后进入M文件,系统才会自动生成该控件的回调函数。第三项,use system color scheme for background(recommended),这个推荐功能的确很不错。
文件所在目录:
\\matlab2007\\help\echdoc\\creating_guis\\examples\\f14ex.fig(f14ex.m)
打开Simulink模块图
本GUI实例关联的Simulink模型是F14。因为GUI要设置模型参数并且运行仿真,所以在GUI界面显示的时候F14应该是打开的。当通过M文件运行GUI时,它会执行model_open子程序。这个子程序的步骤是:
●检测模型是否已经打开了->find_system;
●如果模型没有被打开,则打开模型的模块图以及需要设置参数的子系统->open_system;
●改变控制器Gain模块的大小,使它的值正常显示出来->set_param
●将GUI界面前置使得它可以显示在Simulink模块图的前面->figure
●根据GUI界面的当前值设置模块参数;
--------------
以下是model_open子程序的代码
function model_open(handles)
%函数头,输入参数为handles
if isempty(find_system('Name','f14')),
%判断名为f14的模型是否已经打开
open_system('f14');
%如果没有找到则打开这个模型
open_system('f14/Controller')
%并打开需要设置参数的子系统
set_param('f14/Controller/Gain','Position',[275 14 340 56])
%设置Gain模块的位置和大小
figure(handles.F14ControllerEditor)
%将GUI界面前置使得它可以显示在Simulink模块图的前面
% F14ControllerEditor是GUI界面的Tag(唯一性名字,以下称‘名字’)(见第一张图)
set_param('f14/Controller Gain','Gain',...
get(handles.KfCurrentValue,'String'))
%将GUI中名为KfCurrentValue的控件值赋给f14中Controller Gain模块的Gain参数
set_param(...
'f14/Controller/Proportional plus integral compensator',...
'Numerator',...
get(handles.KiCurrentValue,'String'))
%类似上句
end
%我的附言:斜杠引用的都是simulink模型的对象,设置和获取信息分别是:
set_param(‘模型或者模块的名字路径’,’设置参数’,’设置值’)和get_param();而handles.(一点)来引用的是GUI这边的对象,设置和获取信息分别是:
set(‘GUI或者控件的TAG’,’设置参数’,’设置值’)和get()。
-------------------------------------------------------------------------
设计Slider和Edit Text组件
本GUI的设计中的滑条组件和编辑框组件是相互配合的:
●Edit text显示对应Slider的当前值;
●用户可以在Edit box里输入值,Slider自动更新为此设置值;
●两个组件都可以根据用户的输入来更新为相应值;
滑条的回调函数Slider Callback
GUI使用了两个滑条来指定相应的模块增益,设置滑条使输入值被在一个特定的连续范围内。当用户改变Slider的值时,它的回调函数则会按以下步骤执行:
●调用model_open确定Simulink模型打开,以使得能够设置simulink模型参数;
●获取Slider最新值;
●更新Edit text的当前值;
●将对应的模块参数设置为新值->set_param;
以下是Proportional(Kf)对应滑条的callback
function KfValueSlider_Callback(hObject, eventdata, handles)
% 函数头.
model_open(handles)
%确保模型打开
NewVal = get(hObject, 'Value');
%从slider控件获得Kf增益的新值
set(handles.KfCurrentValue,'String',NewVal)
% 将新值赋予KfCurrentValue(它对应的编辑框的tag)
set_param('f14/Controller/Gain','Gain',num2str(NewVal))
%将Kf增益模块的Gain参数设置为新值
注意,Slider返回数值类型而Edit text要求字符串类型,此时控件会自动将值转换为正确类型。Integral(Ki) slider过程类似。
Current Value编辑框的回调函数Current Value Edit Text Callback
通过编辑框,用户可以直接输入相应的参数值。当用户在编辑框输入值后,在别的控件上单击时,编辑框的回调函数按以下步骤执行:
●调用model_open确定Simulink模型打开,以使得能够设置simulink模型参数;
●将Edit box由String属性返回的字符串转化为double类型->str2double;
●检查用户输入的值是否在Slider设置的范围内;
✧如果输入超出了范围,则将Edit text的String属性置为当前Slider的值(拒绝用户输入的值)
✧如果输入在允许范围内,则将slider的值按此更新
●将对应模块的参数置为新值->set_param;
---------------------
以下是Kf Current value text box的callback
function KfCurrentValue_Callback(hObject, eventdata, handles)
%确保模型打开.
model_open(handles)
% 获取Kf增益的新值,再转换为double类型
NewStrVal = get(hObject, 'String');
NewVal = str2double(NewStrVal);
%检查输入的值是否在范围内.
if isempty(NewVal) || (NewVal< -5) || (NewVal>0),
% 如果超出范围,则恢复为滑竿的当前值
OldVal = get(handles.KfValueSlider,'Value');
set(hObject, 'String',OldVal)
else % 否则,使用编辑框输入的新值
%将slider的值置为新值.
set(handles.KfValueSlider,'Value',NewVal)
%设置KF增益模块的增益参数为新值
set_param('f14/Controller/Gain','Gain',NewStrVal)
end
----------------------------
附:Ki Current value模块也是类似的过程。
---------------------------------------------------------
从GUI界面运行simulink
GUI上的Simulink and store results按钮的回调函数运行仿真模型且将结果储存在handles结构里。将数据存在handles结构里能简化数据在其他子函数的传递过程,因为此结构体能作为一个输入参数来传递。
当用户点击Simulink and store results按钮时,它的回调函数执行步骤如下:
●调用sim命令,运行模型且返回需要绘制的数据;
●定义一个结构体来存放仿真结果、GUI设置的仿真参数当前值、仿真名字、数目;
●将此结构体存在handles结构中;
●更新List box的String参数来列出最近运行的仿真;
Simulate and store results 按钮的callback如下:
function SimulateButton_Callback(hObject, eventdata, handles)
%运行仿真模型f14,并返回结果
[timeVector,stateVector,outputVector] = sim('f14');
%如果已经存在了ResultsData这个变量且它不为空的话
if isfield(handles,'ResultsData') &
~isempty(handles.ResultsData)
%便将handles里的这个结果变量存入过渡变量ResultsData里面
ResultsData = handles.ResultsData;
%最后一个结果数据的RunNumber成员存放了最大运行次数,把它取出
maxNum = ResultsData(length(ResultsData)).RunNumber;
%获得当前结果的排列数
ResultNum = maxNum+1;
else
%如果没有存在ResultsData这个变量(即以前没有运行过模型或者结果数据被删光了)
%则定义一个结构体来存放仿真结果,结构体的内容如下:
ResultsData = struct('RunName',[],'RunNumber',[],...
'KiValue',[],'KfValue',[],'timeVector',[],...
'outputVector',[]);
%将结果数目设置为1(现在只有一个结果)
ResultNum = 1;
end
%如果结果数目为1
if isequal(ResultNum,1),
%将绘图按钮和移除按钮”打开”
set([handles.RemoveButton,handles.PlotButton],'Enable','on')
end
%获取Ki和Kf的值
Ki = get(handles.KiValueSlider,'Value');
Kf = get(handles.KfValueSlider,'Value');
%为当前结果的各个成员变量赋值
ResultsData(ResultNum).RunName = ['Run',num2str(ResultNum)];
ResultsData(ResultNum).RunNumber = ResultNum;
ResultsData(ResultNum).KiValue = Ki;
ResultsData(ResultNum).KfValue = Kf;
ResultsData(ResultNum).timeVector = timeVector;
ResultsData(ResultNum).outputVector = outputVector;
% 为列表框listbox构建新的list字符串;
%获取之前列表框的字符串
ResultsStr = get(handles.ResultsList,'String');
%如果只有一个结果的话
if isequal(ResultNum,1)
ResultsStr = {['Run1',num2str(Kf),' ',num2str(Ki)]};
else
%否则就把表示新结果的字符串添加到原list字符末行
ResultsStr = [ResultsStr;...
{['Run',num2str(ResultNum),' ',num2str(Kf),' ', ...
num2str(Ki)]}];
end
%更新list box的列表显示
set(handles.ResultsList,'String',ResultsStr);
% 储存新的ResultsData
handles.ResultsData = ResultsData;
%保存结果
guidata(hObject, handles)
-------------------------------------------------------
将List Box的结果移除
GUI的Remove按钮的回调函数可以从Results list删除任意选定的项。它同时也从handles结构中删除对应的结果数据。
当用户点击Remove按钮时,它执行的回调函数步骤如下:
●当用户点击Remove按钮时检查list box里的项是否被选取;从List box的String属性中移除这些项,将对应项置为空矩阵;
●从handles结构中移除这些数据;
●如果列表框所有的项目都被删除的话,则显示空矩阵,同时使Remove和Plot按钮处于未激活状态(设置Enable属性);
●保存handles(guidata);
以下使Remove按钮的回调函数:
function RemoveButton_Callback(hObject, eventdata, handles)
%获取当前被选中的项的序号
currentVal = get(handles.ResultsList,'Value');
%获取当前项的列表
resultsStr = get(handles.ResultsList,'String');
%获取所列项的数目
numResults = size(resultsStr,1);
% 将选中的项的列表栏和数据都删掉
resultsStr(currentVal) =[];
handles.ResultsData(currentVal)=[];
%如果没有别的列表项目了,
if isequal(numResults,length(currentVal)),
%将列表字串置为空
resultsStr = {' currentVal = 1; %使Remove 和 Plot按钮处在未激活状态 set([handles.RemoveButton,handles.PlotButton],'Enable','off') end %确保list box的值是有效的,更新它的值和字符串属性 currentVal = min(currentVal,size(resultsStr,1)); set(handles.ResultsList,'Value',currentVal,'String',resultsStr) % 保存新的结果数据ResultData guidata(hObject, handles) ----------------------------------------------------------------------------- 绘制结果图 GUI的Plot按钮将运行数据绘制成图并添加标签。需要绘制的数据是通过handles结构来传递的,它同时也包含了模型运行的增益设置值。当用户点击Plot按钮时,它的回调函数按以下步骤: ●收集Results list中被选中的每一次运行的数据,包括两个变量(时间向量和输出向量),并以不同的颜色为它们作图; ●从储存的数据中生成标签的字符串; ●生成一个图形窗口和绘图的坐标轴,并且在Close按钮的回调函数中保存它的句柄以备使用; ●绘制图形,添加标签,使图形窗口可见; ----------------- 将图绘入隐藏窗口 准备绘制图形的窗口在生成时是不可见的,在添加图形和标签后才设置其为可见。为了防止这个窗口被其他的绘图命令使用,比如命令行或者别的GUI界面,它的HandleVisibility和IntegerHandle属性设置为off。这就意味这它也不响应plot和legend命令。 使用以下步骤向一个隐藏的窗口绘图: ●当创建它时保存这个窗口句柄; ●定一个axes,并把它的Parent属性设置为这个窗口句柄,并保存axes的handle。 ●创建图形(包含一个或多个线条对象),保存这些线条的句柄,并把它们的Parent属性设置为axes的句柄; ●使窗口可见; Plot按钮的callback function PlotButton_Callback(hObject, eventdata, handles) currentVal = get(handles.ResultsList,'Value'); %获取需要绘制的数据的序号; Get data to plot and generate command string with color % specified legendStr = cell(length(currentVal),1); %生成颜色向量 plotColor = {'b','g','r','c','m','y','k'}; %储存每一个需要绘制数据的时间、输出、颜色、标签字符 for ctVal = 1:length(currentVal); PlotData{(ctVal*3)-2} = handles.ResultsData(currentVal(ctVal)).timeVector; PlotData{(ctVal*3)-1} = handles.ResultsData(currentVal(ctVal)).outputVector; numColor = ctVal - 7*( floor((ctVal-1)/7) ); PlotData{ctVal*3} = plotColor{numColor}; legendStr{ctVal} = ... [handles.ResultsData(currentVal(ctVal)).RunName,'; Kf=',... num2str(handles.ResultsData(currentVal(ctVal)).KfValue),... '; Ki=', ... num2str(handles.ResultsData(currentVal(ctVal)).KiValue)]; end % 如果需要的话,生成绘图窗口并保存入handles结构。 if ~isfield(handles,'PlotFigure') ||... ~ishandle(handles.PlotFigure), handles.PlotFigure = ... figure('Name','F14 Simulation Output',... 'Visible','off','NumberTitle','off',... 'HandleVisibility','off','IntegerHandle','off'); handles.PlotAxes = axes('Parent',handles.PlotFigure); guidata(hObject, handles) end % 绘制数据 pHandles = plot(PlotData{:},'Parent',handles.PlotAxes); %添加标签,将图形置前 legend(pHandles(1:2:end),legendStr{:}) % 使窗口可见,并将它置顶 figure(handles.PlotFigure) ----------------------------------------------------- GUI帮助按钮(略跳过) which命令返回文件的全路径名; web命令将此文件显示入Help browser里 function HelpButton_Callback(hObject, eventdata, handles) HelpPath = which('f14ex_help.html'); web(HelpPath); ---------------------------------------------------------- 关闭GUI GUI的Close命令的回调函数关闭绘制的图形窗口,并关闭GUI。图形窗口和GUI窗口的句柄可以从handles结构中获得。它的回调函数执行步骤如下: 检查handles结构体中是否已经存在了PlotFigure,它是否有有效的figure句柄(用户可能手动的关掉了fugure); 关闭GUI的figure。 代码如下: function CloseButton_Callback(hObject, eventdata, handles) %关闭GUI和任何已打开的绘图窗口 if isfield(handles,'PlotFigure') && ... ishandle(handles.PlotFigure), close(handles.PlotFigure); end close(handles.F14ControllerEditor); The List Box Callback and Create Function(略) 1、要求把voicebox.zip展开到某一个目录,并将该目录加入到MATLAB的搜索路径,这样才可以方便的使用其中的函数,但是,我不知道MATLAB的搜索路径是哪一个,该如何加,希望有知道的人告诉我一下~顺便问一下,像这种新增加的工具箱要如何加入到MATLab中?例如我在enyaxp发表的“matlab语音信号处理工具箱”帖子中看到说“解压后剪切至matlab根目录下即可,使用时先将工作目录设为此文件夹,再于命令行中输入"main"即可”,但是……因为我以前没有用过MATLAB,想问问怎么样放到根目录下面?哪个文件夹下叫根目录啊?在指令窗里,运行指令pathtool,选择[File:Set Path]下拉菜单项就可以设置了.你可以放到Matlab安装文件里面的work目录下或者toolbox下都可以的... 2、在主函数中使用下面的语句: cd a; %分别改变当前路径到a、b、c文件夹下,调用其中的函数 afunction cd b bfunction cd c cfunction; cd .. %返回主函数所在的目录,继续执行主函数 3、voicebox中有许多实用的函数。我是从光盘上录下来的。书上说,将压缩包展开到某目录下,并将该目录加到matlab的搜索路径,就可以方便地使用其中的函数了。我不明白的是,如何将这个目录加入到matlab的搜索路径? 先将 VOICEBOX 复制到 MATLAB安装目录下的toolbox 下然后打开MATLAB 然后 FILE ->SET PATH选 ADD FOLDER 选择你刚才复制的那个文件夹就OK了如果你的工具箱中有很多子目录的话,选择 ADD WITH SUBFOLDER之后的操作和上面一样了... 4、GUIDE生成的GUI的M文件控制了你编制的GUI界面的所有属性和行为,或者说外观和对用户操作的响应。比如说按下一个按钮或者选择了一个菜单项之类。M文件包括了运行你整个界面程序所需要的全部代码,包括所有GUI组件的CALLBACKS函数。其实这些callbacks函数算是M文件里的子程序,callback里面就填写你所期望程序做的动作,比如画一个图或者算一个算式。 ------------------------------------------------------- 插入,关于什么是子程序 懂的人跳过^_^ function [avg, med] = newstats(u) % Primary function % NEWSTATS Find mean and median with internal functions. n = length(u); avg = mean(u, n); med = median(u, n); function a = mean(v, n) % Subfunction % Calculate average. a = sum(v)/n; function m = median(v, n) % Subfunction % Calculate median. w = sort(v); if rem(n, 2) == 1 m = w((n+1) / 2); else m = (w(n/2) + w(n/2+1)) / 2; end 以上就是一个大的程序function nestats,它下面另外包含了两个小的function mean和median,这样在大程序的里面就可以以如上的方式调用它们了。子程序的好处在于如果你总是要重复用到一组计算方式的时候,那你就把这组重复计算方式类似以上的方法编写成一个子程序,避免大量重复代码。在M文件里面,会看到最外层,也就是最上面那一一行 function varargout = setfire(varargin) (setfire是我m文件存的名字)就是那个大程序框,它下面有很多小function 比如什么什么creatFcn或者什么callback之类。看上去那个复杂,其实就跟上面这个一样的道理。只不过是M文件的类似avg = mean(u, n);med = median(u, n);这两句话系统隐藏(就当它是隐藏好了)起来了,它会在你点击鼠标(或者响应操作)时候自动去调用执行一次callback函数。所以你只用管把代码写入响应的函数名下就行了。至于系统为什么会自动调用,我们不用管,我们只用知道,我点击鼠标,我拖动滑竿时,系统会执行哪里的代码。就够了。 一 M文件的数据管理模式 ----------------------------------------------------------------- Sharing Data with the Handles Structure 用handles这个东西共享数据 在你运行你的GUI的时候,M文件会自动生成一个叫做handles的东西(准确的说它属于handles 类型的结构体,且取的名字也叫做handles),不用管那么复杂,只用知道你可以从它这里找到GUI的所有数据,比如说控件的信息,菜单信息,axes信息。想象handles就是一个缸了,它里面装载了所有的信息,而且这个缸在各个控件的callback之间传来传去,理所当然那每个控件的callback都可以放入一些想放入的数据,也可以从里面取出任何想要的数据包括别的控件的信息(比如滑竿的当前值,edit text的当前值)和别的控件放进去的数据。 所以,用handles可以达到的目的有两个: -------------- a,各个控件的callback的信息交换 ------ (current_data是随便设置的变量名) handles.current_data = X; 在某控件下的callback写入这一句,就表示你把这个数据放缸里了 guidata(hObject,handles);接着别忘了保存~! 然后在你需要的地方把它从缸里捞出来 X1 = handles.current_data; ------------- b,读取GUI控件的信息,自然也可以设置GUI控件的信息(比如说背景色随着按钮点击而变换之类,或者你想让按钮A点一下,字符B跳一下,也行。) ------ all_choices是随便取的变量名,my_menu是你那个菜单项的TAG名字 all_choices = get(handles.my_menu, 'String'); current_choice = all_choices{get(handles.my_menu, 'Value')}; 这样current_choice就得到了用户界面操作中,目录或者菜单的选择结果。 所以,要什么信息,直接用handles.你的对象 就行了。 存什么信息也直接handles.你的对象 就行了。 如果是自己的数据,就.变量名;如果是控件信息,就用get set待续; 二、M-File里的各个函数代表什么意思 在设计面板设计排列好自己需要的各种按钮或者编辑框之后,下一步任务便是添加自己的响应代码。 Opening function 添加在它名下的代码,在GUI开始运行但是还不可见的时候执行。这里的代码一般都是做一些初始化工作的。 Output function 如果有需要,可以向命令行输出数据。(这个函数我没用过,不多说了^_^) Callbacks 每一次点击按钮或者向输入框输入数据或者拖动滑竿,这些控件名下的callback就会执行一次。 --------------- 函数的输入参数 M-File名下的全部function都会有这两个输入参数 hObject 它代表的是当前的这个控件(也就是你点哪一个按钮或者拖的哪一个滑竿) handles 它代表的是现在这整一个GUI界面 对这两个变量进行修改后 guidata(hObject, handles); 进行保存,否则修改无效; 三、Opnning Function 这个函数名下的代码 在界面可见之前执行。其实你也可以在这个函数名下用handles.什么tag 来获得组件的信息。因为在Opnning函数之前,所有的组件就已经生成了,只不过openning函数是把这些组件‘打开’,让它们显示出来。所以你可以在这个函数下面,添加代码,对界面做一些初始化工作。比如,计算一些数据,显示一幅图或者别的什么工作。 function my_gui_OpeningFcn(hObject, eventdata, handles, varargin) 另外两个输入参数eventdata是matlab的保留参数,为以后开发准备的,我们不用管它。varargin 它里面保存了,传进来的命令行。 varargin怎么理解呢? ------------- my_gui('Position', [71.8 44.9 74.8 19.7]) %GUI也是函数,它只不过是有个界面的函数。它的调用,同样是 函数名(输入参数)。 这里就表示GUI在打开时,位置这个属性被设置成了右边那个值。也就是在这个位置打开GUI。Position是你GUI界面的一个属性。(要想知道各个控件有什么属性,在它上面双击就看到了。)所以同样,也可以用这种方式输入其他的初始化命令。 my_gui('路人甲','年十八') 但如果你输入别的,左边那个根本就不是界面的属性名称。这是输入的参数就保存在varargin里面。也就是 vararging{1}='路人甲' vararging{2}='年十八'。 这样也可以达到向调用的GUI传入数据的目的。 四、Output Function Output function 有输入自然就有输出,顾名思义,这个函数就是用来输出的。 function varargout = my_gui_OutputFcn(hObject, eventdata, handles) % Get default command line output from handles structure varargout{1} = handles.output; 这个函数不是我们自己编写的,也不是我们负责调用。我们只用知道要传出去的数据,其实是放在vararout这个变量里面的。但是我们在别的控件的callback是叫不到vararout这个变量的,因为你去看这些callback的输入参数里并没有vararout这个变量给它用。所以我们只能间接修改handles.output这个变量。(当然在后面别忘了添加guidata(hObject, handles);保存修改) 因为默认的output函数里面varargout{1}=handles.output,也就是修改了vararout。 (所以知道了原理,output只是一个名字而已,你也可以用任意别的名字,或者添加更多的输出变量,只要在outputFcn下面添加 varargout{2}=handles.第二个变量名 。。类似) guidata(hObject, handles)之后不要立即delete窗口命令,因为这时候outputfcn没有再执行一次,也就是你刚刚修改的output并没有更新到varargin里面去。 所以要么单独设计一个关闭按钮;要么跟uiwait(handles.figure1);uiresume合用。 五、Callbacks 回调函数 当你对组件做点击或者别的动作,则自动调用相应的callback。callback的名字取决于你的控件的tag和控件类型以及响应类型。 function print_button_Callback(hObject, eventdata, handles)通用callback 写法