全文約6000字,你將看到以下內(nèi)容:
和大量的模型,、文檔和軟件資源 如何獲取請(qǐng)參考@所有讀者:autoMBD發(fā)布《autoMBD原創(chuàng)技術(shù)文章合集》,。 本篇文章繼續(xù)介紹Simulink生成代碼相關(guān)的內(nèi)容,之前的內(nèi)容可查看上一篇文章《MBD的Simulink使用技巧①:Simulink代碼生成的基本概念》,。文末有福利活動(dòng),。 1 Simulink代碼生成的基本概念(續(xù)) 1.1 上一期補(bǔ)充內(nèi)容 上一篇文章中提到,生成嵌入式代碼,,必須選擇定步長求解器。實(shí)際中,,生成嵌入式代碼幾乎不會(huì)使用Simulink模型庫中的連續(xù)模型,,往往需要通過最簡(jiǎn)單的離散模塊來實(shí)現(xiàn)算法模型。 所以要強(qiáng)調(diào)一點(diǎn):生成嵌入式代碼,,要學(xué)會(huì)使用和適應(yīng)離散模型的搭建方式,。在建模的一開始就要充分考慮離散模型的特點(diǎn)和需求,。 一個(gè)模型是允許多個(gè)離散步長的,這涉及到多任務(wù)相關(guān)的內(nèi)容,,后續(xù)再作詳細(xì)介紹,。建議讀者在建模的時(shí)候,將離散步長(其倒數(shù)即為采樣頻率)顯示出來,,方便辨別不同模塊的實(shí)際步長,。顯示離散步長的方法如下: 1.2 Embedded Coder的使用 Embedded Coder工具專門為嵌入式軟件生成代碼而設(shè)計(jì),集成了MATLAB Coder和Simulink Coder,,可以將m腳本和模型生成C代碼,。Embedded Coder可以在下圖位置找到: Embedded Coder位置 - From autoMBD 單擊“Embedded Coder”便可以進(jìn)入到Code Perspective窗口。在這個(gè)窗口下可以看到四個(gè)主要功能區(qū)域:
如下圖所示: Code Perspective四個(gè)功能區(qū)域 - From autoMBD Tips:MATLAB/Simulink的版本不同,,上述界面可能會(huì)有差異,,截圖為版本為2020b。 在工具欄中,,可以找到大部分關(guān)于代碼生成的選項(xiàng)工具或配置入口,。模型區(qū)域用于編輯和設(shè)計(jì)模型,如果設(shè)置了定步長求解器和ert系統(tǒng)目標(biāo)文件,,單擊工具欄中的“Generate Code”按鈕便可以生成代碼了,。 代碼面板用于查看生成的代碼。在代碼面板中,,點(diǎn)擊生成的代碼,,代碼對(duì)應(yīng)的模型會(huì)高亮顯示。這樣方便用戶追蹤模型生成的代碼,。 代碼映射面板有很多功能,,涉及到的概念和知識(shí)點(diǎn)很多,在后續(xù)的內(nèi)容中會(huì)逐步講解,。 讀者可以自行探索和體驗(yàn)這四個(gè)功能區(qū)域的作用和使用方法,。 2 詳解模型與代碼之間的聯(lián)系 為了便于展示此部分內(nèi)容,我制作了一個(gè)簡(jiǎn)易的PI控制模型作為示例,。讀者可以在autoMBD資源庫的“臨時(shí)資源分享”文件夾中找到該模型(資源序號(hào)tA22),。資源庫鏈接的獲取可以在《autoMBD原創(chuàng)技術(shù)文章合集》中找到(見文章開頭)。 2.1 模型生成的代碼 打開PI控制器示例模型,,可以看到模型如下圖所示: PI控制器示例模型 - From autoMBD 該示例模型已經(jīng)配置好,,點(diǎn)擊“Generate Code”生成代碼??梢园l(fā)現(xiàn)一共生成了六個(gè)文件: PI控制器示例模型生成的代碼 - From autoMBD 生成的文件位于模型同級(jí)目錄下“模型名_ert_rtw”文件夾內(nèi),,這六個(gè)文件的作用分別是:
對(duì)于代碼集成來說,,用戶只需要在主函數(shù)代碼中,,添加下面這個(gè)語句,即可使用模型生成的代碼,,實(shí)現(xiàn)相關(guān)的算法和功能: #inlcude '模型名.h' 2.2 代碼之母——模型 作為MBD的核心,,怎么強(qiáng)調(diào)對(duì)模型的理解的重要性都不為過。 在嵌入式代碼中,,數(shù)據(jù)是代碼的重要組成部分,。代碼中的數(shù)據(jù)和模型中的數(shù)據(jù)是怎么聯(lián)系起來的,便是今天討論的重點(diǎn),。對(duì)于模型,,有四個(gè)與生成代碼緊密關(guān)聯(lián)的模型數(shù)據(jù)形式:
模型中的這四種數(shù)據(jù)形式,生成的代碼各不相同,。通過控制這些模型數(shù)據(jù)的配置,,即可控制生成代碼的數(shù)據(jù)類型、存儲(chǔ)位置和數(shù)據(jù)形式,。 在開始介紹前,,給出模型數(shù)據(jù)形式的思維導(dǎo)圖: 模型數(shù)據(jù)形式的思維導(dǎo)圖 - From autoMBD 2.2.1 信號(hào) 信號(hào)(Signals),即模型中不同模塊之間的數(shù)據(jù)傳遞線,,有兩種:外部信號(hào)和內(nèi)部信號(hào),。其中外部信號(hào)又分為外部輸入信號(hào)和外部輸出信號(hào)。 以我構(gòu)建的PI控制器模型為例,,模型包含的信號(hào)如下圖所示: PI控制器模型的信號(hào) - From autoMBD 上圖中,,Req_Ctrl和Feedback與輸入端口連接,屬于外部輸入信號(hào),;PI_Ctrl與輸出端口連接,,屬于外部輸出信號(hào);Err,、P_value和I_value沒有與任何端口連接,,屬于內(nèi)部信號(hào)。 Tips:上圖并沒有標(biāo)注全部的內(nèi)部信號(hào),,一般只標(biāo)注有重要,、意義的內(nèi)部信號(hào)即可。 保持默認(rèn)設(shè)置情況下生成代碼,,外部信號(hào)會(huì)生成全局變量,,其中輸入變量為“模型名_U”,而輸出變量為“模型名_Y”,。 以本文的示例工程為例,,輸入輸出信號(hào)生成的全局變量以及相關(guān)的數(shù)據(jù)類型聲明和定義如下所示:
/* 外部輸出數(shù)據(jù)類型定義 代碼生成于'模型名.h' */ /* External outputs (root outports fed by signals with default storage) */ typedef struct { real_T PI_Ctrl; /* '<Root>/PI_Ctrl' */ } ExtY_autoMBD_example_PI_noSub_T;
/* 外部輸出變量定義 代碼生成于'模型名.c' */ /* External outputs (root outports fed by signals with default storage) */ ExtY_autoMBD_example_PI_noSub_T autoMBD_example_PI_noSubs_Y; 其他不具備分叉點(diǎn)的內(nèi)部信號(hào),不會(huì)生成任何變量,,而是隱含在算法的運(yùn)算過程當(dāng)中,。 以示例工程的內(nèi)部信號(hào)Err為例,模型中僅該信號(hào)具有分叉點(diǎn),,它生成的局部變量如下所示:
2.2.2 參數(shù) 參數(shù)(Parameters)指的是模塊的參數(shù),,例如:本文PI控制器模型中的PI增益模塊,,它們的參數(shù)分別實(shí)現(xiàn)Kp和Ki,模型中的Discrete-Time Integrator模塊也有兩個(gè)慘數(shù),,具體為采樣時(shí)間參數(shù),、初始值參數(shù)。 保持默認(rèn)設(shè)置情況下,,模塊的參數(shù)會(huì)作為數(shù)值常數(shù),,直接用于算法的運(yùn)算過程。如下所示PI控制器生成的代碼中,,Kp,、Ki和采樣時(shí)間直接使用其數(shù)值2、3和0.001參與算法的運(yùn)算,。 /* 代碼生成于'模型名.c' */ /* Model step function */ void autoMBD_example_PI_noSubs_step(void) { real_T rtb_Err;
/* Sum: '<Root>/Sum2' incorporates: * Inport: '<Root>/Feedback' * Inport: '<Root>/Req_Ctrl' */ rtb_Err = autoMBD_example_PI_noSubs_U.Req_Ctrl - autoMBD_example_PI_noSubs_U.Feedback;
/* Outport: '<Root>/PI_Ctrl' incorporates: * DiscreteIntegrator: '<Root>/Discrete-Time Integrator' * Gain: '<Root>/Kp' * Sum: '<Root>/Sum1' */ /* 直接使用比例增益的數(shù)值常量2.0參與計(jì)算 */ autoMBD_example_PI_noSubs_Y.PI_Ctrl = 2.0 * rtb_Err + autoMBD_example_PI_noSubs_DW.DiscreteTimeIntegrator_DSTATE;
/* Update for DiscreteIntegrator: '<Root>/Discrete-Time Integrator' incorporates: * Gain: '<Root>/Ki' */ /* 直接使用積分增益的數(shù)值常量3.0和離散步長數(shù)值常量0.001參與計(jì)算 */ autoMBD_example_PI_noSubs_DW.DiscreteTimeIntegrator_DSTATE += 3.0 * rtb_Err * 0.001; } 關(guān)于如何修改模塊參數(shù)的代碼生成方式,,使其成為一個(gè)變量而不是數(shù)值常量,,可以按照下圖所示步驟進(jìn)行: 修改模塊參數(shù)的生成方式 - From autoMBD 簡(jiǎn)單來說,就是讓模型參數(shù)是可調(diào)(Tunable)的,,這樣便會(huì)生成一個(gè)新的變量來保存模型中所有模塊的參數(shù),。 修改模塊參數(shù)的生成方式后,重新生成代碼,??梢钥吹剑凇?em>模型名.h”中會(huì)生成一個(gè)新的數(shù)據(jù)結(jié)構(gòu)體,,包含了所有模塊的全部可調(diào)參數(shù):
/* 模塊參數(shù)數(shù)據(jù)類型定義 代碼生成于'模型名_types.h' */ /* Parameters (default storage) */ typedef struct P_autoMBD_example_PI_noSubs_T_ P_autoMBD_example_PI_noSubs_T;
/* 代碼生成于'模型名.c' */ /* Model step function */ void autoMBD_example_PI_noSubs_step(void) { real_T rtb_Err;
/* Sum: '<Root>/Sum2' incorporates: * Inport: '<Root>/Feedback' * Inport: '<Root>/Req_Ctrl' */ rtb_Err = autoMBD_example_PI_noSubs_U.Req_Ctrl - autoMBD_example_PI_noSubs_U.Feedback;
/* Outport: '<Root>/PI_Ctrl' incorporates: * DiscreteIntegrator: '<Root>/Discrete-Time Integrator' * Gain: '<Root>/Kp' * Sum: '<Root>/Sum1' */ /* 使用'模型名_P.Kp_Gain'參與計(jì)算 */ autoMBD_example_PI_noSubs_Y.PI_Ctrl = autoMBD_example_PI_noSubs_P.Kp_Gain * rtb_Err + autoMBD_example_PI_noSubs_DW.DiscreteTimeIntegrator_DSTATE;
/* Update for DiscreteIntegrator: '<Root>/Discrete-Time Integrator' incorporates: * Gain: '<Root>/Ki' */ /* 使用'模型名_P.Ki_Gain'和'模型名_P.DiscreteTimeIntegrator_gainval'參與計(jì)算 */ autoMBD_example_PI_noSubs_DW.DiscreteTimeIntegrator_DSTATE += autoMBD_example_PI_noSubs_P.Ki_Gain * rtb_Err * autoMBD_example_PI_noSubs_P.DiscreteTimeIntegrator_gainval; } 2.2.3 狀態(tài) 狀態(tài)(States)是離散系統(tǒng)運(yùn)算過程中必不可少的元素,。 我們知道,,離散系統(tǒng)是在每一個(gè)離散的時(shí)間點(diǎn)上, 運(yùn)行一次Step函數(shù),。某一時(shí)刻運(yùn)行一次Step函數(shù),,除了需要輸入數(shù)據(jù)(通過外部輸入信號(hào)輸入)以外,往往還需要上一個(gè)時(shí)刻的運(yùn)算結(jié)果,,甚至之前連續(xù)幾個(gè)時(shí)刻的運(yùn)算結(jié)果。 在嵌入式系統(tǒng)中,,這些結(jié)果需要一個(gè)變量來存儲(chǔ),,這個(gè)變量即為狀態(tài)變量。 在Simulink模型庫中,,凡是包含離散因子“z”的模塊,,全部具有狀態(tài)變量。這些模塊在生成代碼時(shí),,都會(huì)生成一個(gè)名為“模型名_DW”的變量來保存狀態(tài)變量,。 具體而已,在本文PI控制器示例工程中,,包含一個(gè)離散積分模塊,,該模塊具有狀態(tài)變量,生成的代碼如下:
/* 狀態(tài)變量定義 位于'模型名.c'中 */ /* Block states (default storage) */ DW_autoMBD_example_PI_noSubs_T autoMBD_example_PI_noSubs_DW; 用戶還可以定義自己的“狀態(tài)變量”,,通過Data Store Memory模塊即可實(shí)現(xiàn),。Data Store Memory模塊位于Simulink庫中的下圖這個(gè)位置: Data Store Memory - From autoMBD Data Store Memory模塊與離散模塊一樣,被當(dāng)作狀態(tài)變量,,生成在變量“模型名_DW”當(dāng)中,。 雖然我們稱它為狀態(tài)變量,但對(duì)于Data Store Memory模塊,,把它當(dāng)作普通的變量來使用也是可以的,。 更多關(guān)于狀態(tài)變量的代碼生成,將在后續(xù)的文章中介紹,。 2.2.4 模型數(shù)據(jù) 模型數(shù)據(jù)是Simulink為模型定義的一個(gè)數(shù)據(jù)類型,,它保存了模型的部分信息。在代碼生成中,,它的數(shù)據(jù)類型和定義如下圖所示:
/* 模型數(shù)據(jù)類型聲明 位于'模型名.h'中 */ /* Forward declaration for rtModel */ typedef struct tag_RTM_autoMBD_example_PI_no_T RT_MODEL_autoMBD_example_PI_n_T;
/* 模型數(shù)據(jù)變量定義 位于'模型名.c'中 */ /* Real-time model */ static RT_MODEL_autoMBD_example_PI_n_T autoMBD_example_PI_noSubs_M_; RT_MODEL_autoMBD_example_PI_n_T *const autoMBD_example_PI_noSubs_M = &autoMBD_example_PI_noSubs_M_;
以上是我的一些建模經(jīng)驗(yàn),規(guī)范建模不是一朝一夕的事情,,是平時(shí)的積累和習(xí)慣養(yǎng)成,。雖然這會(huì)花一些精力,但好處是明顯的,。為了更好的生成可讀性高的代碼,,特別是模型復(fù)雜到一定程度時(shí),我提倡保持一個(gè)好的建模習(xí)慣,。 |
|