Simulink模型打包發(fā)布:使用MATLAB Engine和PyQt

為實現(xiàn)一些相對復(fù)雜的系統(tǒng)開發(fā)或建模仿真,可以借助MathWorks? Simulink在圖形化的工程界面中編輯和測試模型,或者使用MathWorks提供的現(xiàn)成實例與模塊以零基礎(chǔ)快速搭建所需的仿真環(huán)境。但若要讓他人運行自己搭建的模型,并根據(jù)需要自行修改部分模型參數(shù),或是發(fā)布為可供他人使用的仿真工具,則需要將模型打包為獨立的可執(zhí)行文件,并設(shè)計前端圖形化界面以查看仿真輸出結(jié)果、修改仿真參數(shù)。
模型打包的官方推薦做法是使用MATLAB?自帶的Simulink Compiler工具,將編譯好的 Simulink模型和用于設(shè)置、運行和分析仿真的 MATLAB代碼一起打包,且可以附帶由 MATLAB App設(shè)計工具設(shè)計的UI。該方法理論上實現(xiàn)不難,打包出的程序只需Runtime環(huán)境即可運行。但MATLAB App設(shè)計工具并非主流前端設(shè)計工具,需要學(xué)習(xí)成本,且制作的UI界面較為簡單,不滿足對前端界面要求較高的場景;此外,打包生成的代碼可讀性較差,網(wǎng)上現(xiàn)有的案例資源等又極少,一旦報錯,很難定位錯誤,效率不高。
解決方案提出:
為解決該問題,就需要拋棄官方提供的MATLAB App設(shè)計工具和Simulink Compiler打包工具,自行設(shè)計前端界面、獲取Simulink運行數(shù)據(jù)并打包發(fā)布。一種可行的方案是通過主流前端設(shè)計工具制作前端GUI界面,并通過外部會話訪問MATLAB,以此控制Simulink模型運行并獲取運行時的數(shù)據(jù),如可以采用官方提供的用于Python的MATLAB Engine API(https://ww2.mathworks.cn/help/matlab/matlab_external/install-the-matlab-engine-for-python.html),通過Python會話在后臺調(diào)用MATLAB,將仿真實時狀態(tài)和模型參數(shù)等顯示在以PyQt制作的前端UI界面中,最后通過Pyinstaller打包發(fā)布。
該方案可以實現(xiàn)復(fù)雜的前端軟件設(shè)計與Simulink模型仿真相結(jié)合,且體積不大、兼容性較好,但需安裝有MATLAB環(huán)境而非Runtime,即發(fā)布給他人時除軟件本體外還需附帶完整MATLAB安裝包或?qū)Ψ揭寻惭bMATLAB(若未安裝則Simulink仿真部分不可用,但不影響軟件其他功能)。本質(zhì)上Simulink模型并沒有被編譯發(fā)布,只是調(diào)用了相關(guān)api接口使模型在后臺仿真運行。官方文檔(https://ww2.mathworks.cn/help/matlab/matlab-engine-for-python.html?s_tid=CRUX_lftnav)中也指出了:
“MATLAB Engine API for Python 可提供一個包,供 Python 將 MATLAB 作為計算引擎來調(diào)用。引擎應(yīng)用程序需要已安裝版本的 MATLAB;您無法在只有 MATLAB Runtime 的機器上運行 MATLAB Engine?!?/span>
目前,該方案在網(wǎng)上可供參考的資源不多,文中可能存在錯誤和可優(yōu)化的部分。我使用的Simulink模型為純電動汽車建模(EvReferenceApplication),模型細(xì)節(jié)可參考的我的另一篇文章(leosisland.top/article1)。前端GUI界面使用PyQt5設(shè)計,制作一個小型仿真系統(tǒng)軟件界面來控制電動汽車模型仿真的啟停以及部分模型參數(shù)的修改,同時實時讀取并顯示仿真輸出數(shù)據(jù)。軟件界面設(shè)計需在保證功能完整的前提下盡量高效美觀。
安裝用于 Python 的 MATLAB Engine API
安裝前需要確認(rèn)電腦中的Python和MATLAB版本是否兼容,可以查看官方文檔頁面。(https://ww2.mathworks.cn/support/requirements/python-compatibility.html)
在MATLAB根目錄\extern\engines\python文件夾中找到setup.py,即自動安裝腳本文件;控制臺進入該目錄(若Python安裝在虛擬環(huán)境則要在對應(yīng)虛擬環(huán)境終端進入),輸入指令python setup.py install,安裝會自動執(zhí)行。安裝完成后MATLAB Engine會以Python包的形式存在,可以通過pip list查詢。
除此之外,MATLAB R2022b及以上也可以通過pip安裝,詳情可參考官方文檔。(https://ww2.mathworks.cn/help/matlab/matlab_external/install-the-matlab-engine-for-python.html)
MATLAB Engine需要讀取_arch.txt文件中的地址信息來調(diào)用電腦中的MATLAB,該文件默認(rèn)存放于Python包文件夾\matlab\engine中,且僅在安裝時自動生成,記錄本機MATLAB地址。若本機MATLAB地址改變、_arch.txt文件丟失等都會導(dǎo)致尋址失敗。因此不難想到,若要在打包發(fā)布時將MATLAB Engine以python包的形式包含,移植到他人電腦中運行,則必須手動修改_arch.txt文件中的地址信息為對方電腦中的MATLAB地址,否則無法正常運行。
MATLAB Engine需要讀取的文件地址信息(即_arch.txt文件)如下:

修改python包代碼
?為保證MATLAB Engine在打包移植后仍能正常讀取配置文件,獲取目標(biāo)系統(tǒng)環(huán)境信息及MATLAB地址,需對包中部分代碼進行修改。
在本機Python包文件夾\matlab\engine\__init__.py中找到調(diào)用_arch.txt文件所在代碼:
_arch_filename = os.path.join(_module_folder, "_arch.txt")
將其改為:
_arch_filename = os.path.join(os.path.abspath('.'), "xxxxx.txt")
其中“xxxxx.txt”可以是任意文本文件,需自行編寫代碼,在程序運行時根據(jù)當(dāng)前系統(tǒng)環(huán)境在程序運行根目錄下生成該文件,從而實現(xiàn)MATLAB Engine包尋址。
在python中調(diào)用MATLAB Engine
官方文檔(https://ww2.mathworks.cn/help/matlab/matlab_external/start-the-matlab-engine-for-python.html)中給出了在python中調(diào)用MATLAB Engine的代碼示例和說明可供參考,但大多較為簡略,且實際編程時還需結(jié)合以編程方式運行仿真(https://ww2.mathworks.cn/help/simulink/ug/using-the-sim-command.html),用代碼代替Simulink? UI 與仿真進行交互,如果以前從未接觸過Simulink的代碼形式,可能遇到不少問題。本仿真系統(tǒng)中用到的部分代碼見下表。


其中,本仿真系統(tǒng)用到的部分set_param中的parameter參數(shù)與對應(yīng)的value值見下表。需注意表中所有參數(shù)均為字符串。

另外需要注意的是,在python中調(diào)用MATLAB Engine相當(dāng)于訪問外部會話,屬于耗時操作,若寫在主程序線程中將會導(dǎo)致線程阻塞,影響前端GUI界面運行,需要利用多線程通信來解決該問題。
打包發(fā)布
使用pyinstaller打包發(fā)布python程序,但pyinstaller無法正確識別MATLAB Engine包,此時需要手動復(fù)制該包;若需要打包為單一可執(zhí)行程序(.exe),則需要配置hooks來添加指定包。
在主程序.py目錄下新建文件夾,命名為hooks,其內(nèi)新建py文件,內(nèi)容如下:
from PyInstaller.utils.hooks import collect_all
datas, binaries, hiddenimports = collect_all('matlab')
打包時額外添加指令:--additional-hooks-dir=hooks。如:
pyinstaller -F -w --additional-hooks-dir=hooks 主程序.py
即可正確打包為單一可執(zhí)行程序。
由于B站專欄格式限制,部分內(nèi)容或代碼可能存在格式異常。原文內(nèi)容請前往leosisland.top/article1