MATLAB的S文件編寫

Part I:

所謂s函數是system Function的簡稱, 用它來寫自己的simulink模塊. s函數可以用matlab、C、C++、Fortran、Ada等語言來寫, 這兒我只介紹怎樣用matlab語言來寫吧(主要是它比較簡單) < xmlnamespace prefix =”o” ns =”urn:schemas-microsoft-com:office:office” />

先講講爲什麼要用s函數,我覺得用s函數可以利用matlab的豐富資源,而不僅僅侷限於simulink提供的模塊,而用c或c++等語言寫的s函數還可以實現對硬件端口的操作, 還可以操作windows API等

先介紹一下simulink的仿真過程(以便理解s函數),simulink的仿真有兩個階段: 一個爲初始化,這個階段主要是設置一些參數,像系統的輸入輸出個數、狀態初值、 採樣時間等;第二個階段就是運行階段,這個階段裏要進行計算輸出、更新離散狀態、 計算連續狀態等等,這個階段需要反覆運行,直至結束.

在matlab的workspace裏輸入edit sfuntmpl(這是matlab自己提供的s函數模板),我們看它來具體分析s函數的結構.

  1. 函數的函數頭
    函數的第一行:function [sys,x0,str,ts]=sfuntmpl(t,x,u,flag) , 先講輸入與輸出變量的含義:
    t是採樣時間, x是狀態變量, u是輸入(是做成simulink模塊的輸入), flag是仿真過程中的狀態標誌(以它來判斷當前是初始化還是運行等)
    sys輸出根據flag的不同而不同(下面將結合flag來講sys的含義), x0是狀態變量的初始值, str是保留參數(mathworks公司還沒想好該怎麼用它, 一般在初始化中將它置空就可以了, str=[]), ts是一個1×2的向量, ts(1)是採樣週期, ts(2)是偏移量

  2. 函數分析
    下面結合sfuntmpl.m中的代碼來講具體的結構:
    switch flag, %判斷flag,看當前處於哪個狀態

case 0,
[sys,x0,str,ts]=mdlInitializeSizes;
// 解釋說明
flag=0表示當前處於初始化狀態,此時調用函數mdlInitializeSizes進行初始化,此函數在該文件的第149行定義. 其中的參數sys是一個結構體,它用來設置模塊的一些參數,各個參數詳細說明如下
size = simsizes;%用於設置模塊參數的結構體用simsizes來生成
sizes.NumContStates = 0; %模塊連續狀態變量的個數
sizes.NumDiscStates = 0; %模塊離散狀態變量的個數
sizes.NumOutputs = 0; %模塊輸出變量的個數
sizes.NumInputs = 0; %模塊輸入變量的個數
sizes.DirFeedthrough = 1; %模塊是否存在直接貫通
sizes.NumSampleTimes = 1; %模塊的採樣時間個數, 至少是一個
sys = simsizes(sizes); %設置完後賦給sys輸出
舉個例子,考慮如下模型:
dx/dt=fc(t,x,u) 也可以用連續狀態方程描述:dx/dt=A*x+B*u
x(k+1)=fd(t,x,u) 也可以用離散狀態方程描述:x(k+1)=H*x(k)+G*u(k)
y=fo(t,x,u) 也可以用輸出狀態方程描述:y=C*x+D*u
設上述模型連續狀態變量、離散狀態變量、輸入變量、輸出變量均爲1個,我們就只需改上面那一段代碼爲(一般連續狀態與離散狀態不會一塊用, 我這兒是爲了方便說明):
sizes.NumContStates=1;sizes.NumDiscStates=1;sizes.NumOutputs=1;sizes.NumInputs=1;
其他的可以不變, 繼續在mdlInitializeSizes函數中往下看:
x0 = []; %狀態變量設置爲空,表示沒有狀態變量,以我們上面的假設,可改爲x0=0,0
str = []; %保留參數, 置[]就可以了, 沒什麼用
ts = [0 0]; %採樣週期設爲0表示是連續系統, 如果是離散系統在下面的mdlGetTimeOfNextVarHit函數中具體介紹

case 1,
sys=mdlDerivatives(t,x,u);
//
flag=1表示此時要計算連續狀態的微分, 即上面提到的dx/dt=fc(t,x,u)中的dx/dt, 找到193行的函數mdlDerivatives, 如果設置連續狀態變量個數爲0, 此處只需sys=[]就可以了, 按我們上述討論的那個模型, 此處改成 sys=fc(t,x(1),u)或sys=A*x(1)+B*u, 我們這兒x(1)是連續狀態變量, 而x(2)是離散的, 這兒只用到連續的, 此時的輸出sys就是微分

case 2,
sys=mdlUpdate(t,x,u);
//
flag=2表示此時要計算下一個離散狀態, 即上面提到的x(k+1)=fd(t,x,u), 找到mdlUpdate函數, 它這兒sys=[]表示沒有離散狀態, 我們這兒可以改成sys=fd(t,x(2),u)或sys=H*x(2)+G*u;%sys即爲x(k+1)

case 3,
sys=mdlOutputs(t,x,u);
//
flag=3表示此時要計算輸出, 即y=fo(t,x,u), 找到218行的mdlOutputs函數. 如果sys=[]表示沒有輸出, 我們改成sys=fo(t,x,u)或sys=C*x+D*u %sys此時爲輸出y

case 4,
sys=mdlGetTimeOfNextVarHit(t,x,u);
//
flag=4表示此時要計算下一次採樣的時間, 只在離散採樣系統中有用(即上文的mdlInitializeSizes中提到的ts設置ts(1)不爲0), 連續系統中只需在mdlGetTimeOfNextVarHit函數中寫上sys=[]. 這個函數主要用於變步長的設置, 具體實現大家可以用edit vsfunc看vsfunc.m這個例子

case 9,
sys=mdlTerminate(t,x,u);
//
flag=9表示此時系統要結束,一般來說寫上在mdlTerminate函數中寫上sys=[]就可, 如果你在結束時還要設置什麼,就在此函數中寫完了.
Part II
此外, s函數還可以帶用戶參數, 下面給個例子, 它和simulink下的gain模塊功能一樣
function [sys,x0,str,ts] = sfungain(t,x,u,flag,gain)
switch flag,
case 0,
sizes = simsizes;
sizes.NumContStates = 0;
sizes.NumDiscStates = 0;
sizes.NumOutputs = 1;
sizes.NumInputs = 1;
sizes.DirFeedthrough = 1;
sizes.NumSampleTimes = 1;
sys = simsizes(sizes);
x0=[];
str=[];
ts=[0,0];
case 3,
sys=gain*u;
case {1,2,4,9},
sys = [];
end
做好了s函數後, simulink –> user-defined function下拖一個S-Function到你的模型, 就可以用了. 在simulink –> user-defined function還有個s-Function Builder, 他可以生成用c語言寫的s函數. 或者在matlab的workspace下打sfundemos, 可以看到很多演示s函數的程序

Part III
SIMULINK s-function的設計
Simulink爲用戶提供了許多內置的基本庫模塊, 通過這些模塊進行連接而構成系統的模型. 對於那些經常使用的模塊進行組合並封裝可以構建出重複使用的新模塊, 但它依然是基於Simulink原來提供的內置模塊.
而Simulink s-function是一種強大的對模塊庫進行擴展的新工具.
(一) s-function的概念
s-function是一個動態系統的計算機語言描述, 在MATLAB裏, 用戶可以選擇用m文件編寫, 也可以用c或mex文件編寫, 在這裏只給大家介紹如何用m文件編寫s-function.
S-function提供了擴展Simulink模塊庫的有力工具, 它採用一種特定的調用語法, 使函數和Simulink解法器進行交互.
S-function最廣泛的用途是定製用戶自己的Simulink模塊. 它的形式十分通用, 能夠支持連續系統、離散系統和混合系統.
(二) 建立m文件s-function
1. 使用模板文件:sfuntmp1.m, 其格式爲[sys,x0]=function(t,x,u,flag). 該該模板文件位於MATLAB根目錄下toolbox/simulink/blocks目錄下
模板文件裏s-function的結構十分簡單, 它只爲不同的flag的值指定要相應調用的m文件子函數. 比如當flag=3時, 即模塊處於計算輸出這個仿真階段時, 相應調用的子函數爲sys=mdloutputs(t,x,u). 模板文件使用switch語句來完成這種指定, 當然這種結構並不唯一, 用戶也可以使用if語句來完成同樣的功能. 而且在實際運用時, 可以根據實際需要來去掉某些值, 因爲並不是每個模塊都需要經過所有的子函數調用.
模板文件只是Simulink爲方便用戶而提供的一種參考格式, 並不是編寫s-function的語法要求, 用戶完全可以改變子函數的名稱, 或者直接把代碼寫在主函數裏, 但使用模板文件的好處是, 比較方便, 而且條理清晰.
使用模板編寫s-function, 用戶只需把s-函數名換成期望的函數名稱, 如果需要額外的輸入參量, 還需在輸入參數列表的後面增加這些參數, 因爲前面的4個參數是simulink調用s-function時自動傳入的. 對於輸出參數, 最好不做修改. 接下去的工作就是根據所編s-function要完成的任務, 用相應的代碼去替代模板裏各個子函數的代碼即可.
Simulink在每個仿真階段都會對s-function進行調用. 在調用時, Simulink會根據所處的仿真階段爲flag傳入不同的值, 而且還會爲sys這個返回參數指定不同的角色. 也就是說盡管是相同的sys變量, 但在不同的仿真階段其意義卻不相同, 這種變化由simulink自動完成.
m文件s-function可用的子函數說明如下:
mdlInitializeSizes(flag=0) – 定義s-function模塊的基本特性, 包括採樣時間、連續或者離散狀態的初始條件和sizes數組
mdlDerivatives(flag=1) – 計算連續狀態變量的微分方程
mdlUpdate(flag=2) – 更新離散狀態、採樣時間和主時間步的要求
mdlOutputs(flag=3) – 計算s-function的輸出
mdlGetTimeOfNextVarHit(flag=4) – 計算下一個採樣點的絕對時間, 這個方法僅僅是在用戶在mdlInitializeSizes 裏說明了一個可變的離散採樣時間
概括說來, 建立s-function可以分成兩個分離的任務: 第一, 初始化模塊特性包括輸入輸出信號的寬度, 離散連續狀態的初始條件和採樣時間. 第二, 將算法放到合適的s-function子函數中去。
2. 定義s-function的初始信息
爲了讓Simulink識別出一個m文件是s-function, 用戶必須在s-函數裏提供有關s-函數的說明信息, 包括採樣時間、連續或者離散狀態個數等初始條件. 這一部分主要是在mdlInitializeSizes子函數裏完成.
Sizes數組是s-function函數信息的載體, 它內部的字段意義爲:
NumContStates(sys(1)):連續狀態的個數(狀態向量連續部分的寬度)
NumDiscStates(sys(2)):離散狀態的個數(狀態向量離散部分的寬度)
NumOutputs(sys(3)): 輸出變量的個數(輸出向量的寬度)
NumInputs(sys(4)):輸入變量的個數(輸入向量的寬度)
DirFeedthrough(sys(5)):有不連續根的數量
NumSampleTimes(sys(6)):採樣時間的個數,有無代數循環標誌
如果字段代表的向量寬度爲動態可變, 則可以將它們賦值爲-1. 注意DirFeedthrough是一個布爾變量, 它的取值只有0和1兩種, 0表示沒有直接饋入, 此時用戶在編寫mdlOutputs子函數時就要確保子函數的代碼裏不出現輸入變量u; 1表示有直接饋入. NumSampleTimes表示採樣時間的個數, 也就是ts變量的行數, 與用戶對ts的定義有關.
需要指出的是, 由於s-function會忽略端口, 所以當有多個輸入變量或多個輸出變量時, 必須用mux模塊或demux模塊將多個單一輸入合成一個複合輸入向量或將一個複合輸出向量分解爲多個單一輸出.
3. 輸入和輸出參量說明
S-function默認的4個輸入參數爲t、x、u和flag, 它們的次序不能變動, 代表的意義分別爲:
t – 代表當前的仿真時間, 這個輸入參數通常用於決定下一個採樣時刻, 或者在多采樣速率系統中, 用來區分不同的採樣時刻點, 並據此進行不同的處理
x – 表示狀態向量, 這個參數是必須的, 甚至在系統中不存在狀態時也是如此. 它具有很靈活的運用
u – 表示輸入向量
flag – 是一個控制在每一個仿真階段調用哪一個子函數的參數, 由Simulink在調用時自動取值.
S-function默認的4個返回參數爲sys、x0、它們的次序不能變動,代表的意義分別爲:
sys – 是一個通用的返回參數, 它所返回值的意義取決於flag的值
x0 – 是初始的狀態值(沒有狀態時是一個空矩陣[]), 這個返回參數只在flag值爲0時纔有效, 其他時候都會被忽略.

假設, 有一系統如下:
dx1=x2
dx2=9.81*sin(x(1))-2*x(2)+u
求出系統在單位階躍輸入下的x1的狀態變化曲線, 假設x1,x2初值爲0, 則:
function [sys,x0]=dong(t,x,u,flag)
if flag==0
sys=[2;0;2;1;0;0];
x0=[0;0];
elseif flag==1
sys=[x(2);9.81*sin(x(1))-2*x(2)+u];
elseif flag==3
sys=[x(1);x(2)];
else
sys=[];
end

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章