1. 程式人生 > >matlab中s-function函式的使用

matlab中s-function函式的使用

   在matlab的workspace裡打edit sfuntmpl(這是matlab自己提供的s函式模板),我們看它來具體分析s函式的結構。 它的第一行是這樣的: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)是偏移量。

下面結合sfuntmpl.m中的程式碼來講具體的結構:
switch flag, %判斷flag,看當前處於哪個狀態
case 0,
 [sys,x0,str,ts]=mdlInitializeSizes;
flag=0表示處於初始化狀態,此時用函式mdlInitializeSizes進行初始化,此函式在 sfuntmpl.m的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.NumInpu
ts=1;
其他的可以不變。繼續在mdlInitializeSizes函式中往下看:
     x0 = [];    %狀態變數設定為空,表示沒有狀態變數,以我們上面的假設,可改 %為x0=[0,0](離散和連續的狀態變數我們都設它初值為0)
     str = [];    %這個就不用說了,保留引數嘛,置[]就可以了,反正沒什麼用,可 %能7.0會給它一些意義
     ts = [0 0]; %取樣週期設為0表示是連續系統,如果是離散系統在下面的mdlGet %TimeOfNextVarHit函式中具體介紹
嘻嘻,總算講完了初始化,後面的應該快了
在sfuntmpl的106行繼續往下看:
    case 1,
      sys=mdlDerivatives(t,x,u);
flag=1表示此時要計算連續狀態的微分,即上面提到的dx/dt=fc(t,x,u)中的dx/dt,找到 mdlDerivatives函式(在193行)如果設定連續狀態變數個數為0,此處只需sys=[]; 就可以了(如sfuntmpl中一樣),按我們上述討論的那個模型,此處改成 sys=fc(t,x(1),u)或sys=A*x(1)+B*u %我們這兒x(1)是連續狀態變數,而x(2)是離散的,這兒只用到連續的,此時的輸出sys就是微分
繼續,在sfuntmpl的112行:
  
case 2,
       sys=mdlUpdate(t,x,u);
flag=2表示此時要計算下一個離散狀態,即上面提到的x(k+1)=fd(t,x,u),找到mdlUpd ate函式(在206行)它這兒sys=[];表示沒有離散狀態,我們這而可以改成 sys=fd(t,x(2),u)或sys=H*x(2)+G*u;%sys即為x(k+1)

看來後面幾個一兩句話就可了,呵呵,在sfuntmpl的118行
case 3,
       sys=mdlOutputs(t,x,u);
flag=3表示此時要計算輸出,即y=fo(t,x,u),找到mdlOutputs函式(在218行),如上,如果sys=[]表示沒有輸出,我們改成sys=fo(t,x,u)或sys=C*x+D*u %sys此時為輸出y
好像快完了,嘻嘻,在sfuntmpl的124行
 case 4,
sys=mdlGetTimeOfNextVarHit(t,x,u);
flag=4表示此時要計算下一次取樣的時間,只在離散取樣系統中有用(即上文的mdlInit ializeSizes中提到的ts設定ts(1)不為0)
連續系統中只需在mdlGetTimeOfNextVarHit函式中寫上sys=[];這個函式主要用於變步長的設定,具體實現大家可以用edit vsfunc看vsfunc.m這個例子
最後一個,在sfuntmpl的130行
 case 9,
 sys=mdlTerminate(t,x,u);
flag=9表示此時系統要結束,一般來說寫上在mdlTerminate函式中寫上sys=[]就可,如果你在結束時還要設定什麼,就在此函式中寫
關於sfuntmpl這個s函式的模板講完了。
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
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