S函數的相關概念與寫法,直接在幫助文件中搜:【MATLAB S-Functions Create custom blocks defined】、【S-Function Concepts】等
S函數模塊可以從下圖中拖出來:
圖 1
其中S-Function是正宗的S函數模塊,旁邊還有一個S-Function builder是給新手用的,只要學會了S函數模塊,S builder模塊自然一看就懂。
使用S函數模塊的步驟:①寫S函數的.m文件, 並把m文件所在的文件夾加入搜索路徑,如下圖2所示。②在simulink中拖出S函數,並填上m文件的名字,如果有參數(下文會講到),也把參數名填上,如下圖3所示。然後就可以執行仿真了
圖2
圖3
下面是最關鍵的部分,如何寫S函數:
寫S函數的本質就是寫一堆回調函數,C/C++程序員對回調函數應該很熟悉了,既然是回調函數,那寫之前必須要知道形參和返回值的格式,這個格式從哪查?直接在matlab命令行執行: edit sfuntmpl,就直接打開了官方提供的模板,連格式都不用查了,直接仿照官方模板把函數體改改蓋就行了,方便。
(1)S函數的總函數
把官方模板的代碼拷到一個新文件裏,並命名爲自定義名稱,我取的名字是oneOrderModule.m,然後把函數名也改成和文件名相同,我的是這樣的:function [sys,x0,str,ts,simStateCompliance] = oneOrderModule(t,x,u,flag, T)
我與官方版不同的是,自己加了一個參數T,如果你還想添加更多參數,直接在形參表裏添加就行了。添加的形參的實參值,來自於圖3的第二個輸入框,這個輸入框可填常量,也可以填工作區變量名。
形參:①當前的仿真時間t,單位爲秒,該參數可以用來描述變參數系統,例如你想實現【在t>2S時,把系統增益給改掉】這一功能,就可以通過判定t的值來實現;②x爲狀態列向量;③輸入列向量u;④flag爲當前狀態機的第幾步,例如實參送進來的flag=0代表S函數需要初始化,flag=1代表要更新連續狀態
返回值:在不同的狀態步下(也即flag不同時),返回值的意義是不同的,在模板文件的註釋中都講到了:
例如flag=0時,需要返回:輸入輸出狀態變量等的大小SIZES、狀態變量的初值X0等。。。
又如flag=3時,返回值sys代表輸出向量Y,也即狀態空間表達式第2式,Y=CX+DU
不同flag下返回值得寫法,將在下面的各個函數中依次講解
(2)系統初始化
當我麼在函數中檢測到flag=0時,意味着simulnk需要我們返回系統初始化的一些信息。
實際上,我們可以直接把flag=0時需要執行的代碼直接寫在switch-case中,但是爲了使程序更清晰,我們也仿照官方模板,在case中調用初始化函數mdlInitializeSizes,我們把代碼寫在初始化函數中。
function [sys,x0,str,ts,simStateCompliance]=mdlInitializeSizes
sizes = simsizes;
sizes.NumContStates = 1;%連續狀態向量的元素數目
sizes.NumDiscStates = 0;%離散狀態向量的元素數目
sizes.NumOutputs = 2;%輸出向量的元素數目
sizes.NumInputs = 1;%輸入向量的元素數目
sizes.DirFeedthrough = 1;%輸入是否直接饋通到輸出
sizes.NumSampleTimes = 1; % 採樣時間矩陣的行數(必須設爲>=1),另外列數固定爲2,無需設置
sys = simsizes(sizes);%把以上賦值好的結構賦給返回值
x0 = [0];%設置狀態向量的初值
str = [];%保留參數,不用管
ts = [0 0];%採樣時間設置,必須爲mx2維度,其中m爲上面sizes.NumSampleTimes的值
simStateCompliance = 'UnknownSimState';
此函數中,按照回調函數的要求,sys的返回要返回一些成員的尺寸大小,我們可以一個個爲sys(1)、sys(2)。。等依次賦值,但我們不會這麼做,因爲官方模板爲我們提供了一個輔助數據結構的實體simsizes,直接把他複製出來把成員值改好,再賦值給返回值sys即可。simsizes的成員有6個,其中有2個需要單獨講一下:
①直接饋通標誌DirFeedthrough,這個東西實際上就是看看狀態空間表達式第2式Y=CX+DU中的D矩陣是否爲0矩陣,如果不是,那我們必須把饋通標誌設爲1。從回調函數來看,只要我們把DirFeedthrough設成了1,那麼當flag=3時,系統會把t、u兩個參數傳進總函數oneOrderModule中,如果DirFeedthrough設成了0,那麼當flag=3時,我們在oneOrderModule函數或者mdlOutputs函數中中將無法讀到t的值(實際讀出來總是0),也無法讀到u的值(實際讀出來總是一個只含Nan元素的向量)。
②採樣時間矩陣的行數NumSampleTimes
這個東西有些複雜,從直觀上看,它決定了【採樣時間矩陣ts】的行數,ts矩陣是NumSampleTimes行2列的矩陣。ts的每一行均包含一個數據對:[ 採樣時間 偏移量 ],這些數據對不是亂填的,可選就這麼幾種形式:
採樣時間可以理解爲採樣週期,真正的採樣時刻=n*週期+偏移量。
對於連續系統,採樣時間應設爲0,matlab也提供宏CONTINUOUS_SAMPLE_TIME,該宏的值=0。
對於固定步長的離散系統,可以直接設置採樣間隔和偏移,形如:[ 0.1 0.02 ]
對於變步長的離散系統,可以設置爲:[VARIABLE_SAMPLE_TIME, 0.0],這種參數需要同時把simulink求解器solver設置成變步長的。
繼承前一模塊的採樣點,可以設置爲:[INHERITED_SAMPLE_TIME, FIXED_IN_MINOR_STEP_OFFSET或0]。
更多知識點,可以搜索這幾個宏名來學習。
(3)連續系統更新、離散系統更新
這兩個函數分別爲mdlDerivatives和mdlUpdate,如果需要支持自定義的參數,那麼直接修改函數的形參表即可,在我的例子中,我附加了一個T參數,那麼我的連續系統更新函數是這樣的:
function sys=mdlDerivatives(t,x,u, T)
A = [-1/T];
B = [1/T];
sys = A*x+B*u;
原理是這樣的,典型一階系統的微分方程:
如果系統中存在離散狀態,那麼就在mdlUpdate中寫出離散狀態空間表達式即可,如果沒有離散狀態,就直接sys返回空矩陣。
(4)輸出函數
function sys=mdlOutputs(t,x,u)
C = [1
1];
D = [0
1];
sys = C * x + D * u;
%sys = [x t];
這個也沒啥好說的,描述一下輸出向量即可。不過提醒一下,如果使用了 u或t 參數,不要忘記把饋通標誌置1,否則在本函數中收不到t和u的實參值。
這個函數描述了兩個輸出: y(1)=x,y(2)=x+u
下面看一下仿真結果:
和預想的一樣,沒毛病。