目录
—— 前言
—— 技术篇
1)Python与Matlab交互
2)Matlab与Simulink交互
3)Pyhton与Matlab/Simulink交互
—- 小结
前言
一年前,还没接触过Pyhton和Simulink,Matlab也只会写一些简单的计算脚本。如今使用Python的AI库和Matlab/Simulink快一年了,期间用Simulink做微电网的暂态仿真,搭建环境,然后在Python中做强化学习的训练。为了实现训练的自主运行,需要打通Python和Matlab/Simulink的接口,经过一段时间的探索,终于找到了一个可行的方案。
20年初的时候,2019版的Matalb还没有Reinforcement learning (RL)库,只有Deep learning库,直到2020a版本,Mathwork官方才建立化强化学习库。并且由于刚引入,其支持的RL算法较少,且很多个性化的训练需求无法完成。另外,我的训练需要和Simulink交互,且动态修改Simulink内部参数,并实时分析Simulink的仿真数据,这使得Matlab的RL包无法直接满足需求。于是最终选择了Pyhton和Matlab/Simulink混合仿真的路线,期间踩了不少坑,也解决了不少麻烦。现在把它们记录下来,算是一个小结,也希望给像我一样遇到类似困难的同学提供一点帮助。
技术篇
Python最终与Simulink交互,是通过与Maltlab的交互实现的,整个过程有两个关键点:一是Python调用并运行Maltab的m文件,同时直接读取工作区中的参数数据;二是Matlab通过代码控制Simulink启停,重置参数,并读取Simlink的仿真结果。这两点实现后,将Simulink作为环境,在Python中进行AI训练就不再是难事。下面分别介绍这两个关键点。
一. Python与Matlab交互
Python与Matlab交互有两种方式:1)Python作为主控程序,调用Matlab;2)Matlab作为主控程序,调用Python。
- Python call Matlab
Python调用Matlab是逻辑是,在Matlab中安装Python 包形式的引擎API,然后再py文件中 improt matlab engine,即Matlab进程,通过这个engine可以直接调用Matlab内置函数或者自己编写的m文件。具体实现分为以下4步。
1)检查Python和Matlab的版本适配情况。
目前Mathwork官方发布的版本适配情况如下图所示。
其中,Matlab的版本可以从安装文件名直接看出来;python版本可以在Matlab中通过以下代码查找:
pe = pyenv; pe.Version
更详细的版本适配内容可参考Mathwork官方文档:
https://link.zhihu.com/?target=https%3A//ww2.mathworks.cn/help/matlab/matlab_external/install-supported-python-implementation.html%3Flang%3Den
2)在Matalb根目录中安装Python包形式的引擎API
Windows中,在管理员模式下按“win+R”,键入cmd,回车打开进入系统命令行,键入:
cd "matlabroot\extern\engines\python"
python setup.py install
或者在MATLAB 命令提示符下键入以下命令:
cd (fullfile(matlabroot,'extern','engines','python'))
system('python setup.py install')
更详细教程可参考Mathwork官方文档:
官方文档(中文版)
https://link.zhihu.com/?target=https%3A//ww2.mathworks.cn/help/matlab/matlab_external/install-the-matlab-engine-for-python.html
3)在Python中import matlab engine 并调用
import matlab.engine # 在Python中导入Matlab引擎
eng = matlab.engine.start_matlab() # 启动Matlab引擎,命名为eng
eng.ini_operating_point(nargout=0) # 通过eng运行写好的m文件 “ini_operating_point.m”,nargout=0表示不返回输出。
# Note: 此m文件需要在ptyhon的启动界面下。
通过上述方法,相当于用Python开启了一个虚拟Matlab进程,没有UI界面,但是工作区及其变量依然存在。运行完m文件后,可以通过Python直接读取虚拟进程中的变量,例如:
out = eng.eval('out') # 用 matlab的eval 函数读取变量 ‘out’
另外,在Python中将变量赋值到Matlab工作区的指令也很常用,例如:
eng.workspace['Q_ref'] = Q_ref.tolist() # 在Matlab工作区生成变量‘Q_ref’,大小等于Python中的变量‘Q_ref’
上述通过Python启动Matlab engine的过程大致要花30秒,比直接打开Matlab要稍微慢点。
4)退出Matlab engine
eng.quit() # quit Matlab engine
使用完Matlab代码后,需要在Python中手动代码退出,否则这个进程会一直挂在后台,占用系统资源。
- Matlab call Python
Matlab里可以很方便地调用Python。在Matlab Command window里键入 pyversion ,就能看到链接的Python解释器,下图是我通过Anaconda配置的写强化学习训练的环境,命名成了“DQN”…. Python版本是3.6。
这里插个题外话,关于Python的版本和环境管理。早期Tenserflow代码使用的是早版本Python,所以需要通过Python环境管理来支持之前版本的代码。目前,最新的Tensorflow和Pytorch库都需要较新版本的Python支持。
具体的例子和更详细的使用方法,可参考知乎上的其它帖子和Mathwork官方文档,这里不做赘述,传送门如下:
https://zhuanlan.zhihu.com/p/40992247
https://ww2.mathworks.cn/help/matlab/call-python-libraries.html?s_tid=CRUX_lftnav
二. Matlab与Simulink交互
常见的Simulink使用方法是拖动模块,搭建系统,然后点击控制栏中的 ‘Run’来运行,并通过Scope观测输出。
这种方式在少工作量的场景下比较方便,但如果碰到以下场景,手动启停就不太现实了。比如:1)需要频繁地调参,甚至在仿真过程中动态修改参数;2)需要批量仿真,反复运行成千上万次;3)初始化时间很长,实际需要观察时间段在仿真末尾几秒。这时候就需要代码来控制Simulink仿真。
实际上,鼠标点击能实现的控制操作都可以通过代码来实现。主要涉及到两个函数:一是仿真运行函数 sim( ),用于控制simulink仿真的启停;二是参数设置函数 set_param( ),用于参数设置,包括仿真控制参数,和仿真模型参数。
下面贴一段我写的代码,使用了上面提到了命令。这段代码的功能是将一个Simulink模型仿真到4秒,并存成工作点,以后的仿真读取工作点直接从4秒时刻继续。
% -------------------------------------------------------------------------
% This manuscripte is to set the initial operaton point of simulink file
% The environment is Banshee microgrid
% Last modfied: 3/17/2021 by Buxin She
% -------------------------------------------------------------------------
%%
% parameter initialization
file_name = 'Banshee_grid_connected_PQ';
start_time = '4';
normal_stop_time = '11';
% load sim model
load_system(file_name);
% set loading initial operation point off
set_param(file_name,'LoadInitialState','off');
set_param(file_name,'SimscapeUseOperatingPoints','off');
% set getting final state on and save it as IniOperPoint
set_param(file_name,'SaveCompleteFinalSimState','on');
set_param(file_name,'SaveFinalState','on');
set_param(file_name,'FinalStateName','myOperPoint');
simOut = sim(file_name,'StopTime',start_time);
myOperPoint = simOut.myOperPoint;
save('IniOperPoint.mat', 'myOperPoint');
set_param(file_name,'SaveCompleteFinalSimState','off');
% set loading initial operation point on
set_param(file_name,'LoadInitialState','on');
set_param(file_name,'InitialState','myOperPoint');
set_param(file_name,'SimscapeUseOperatingPoints','on'); % enable Operation point initialization
set_param(file_name,'SimscapeOperatingPoint','myOperPoint');
% restore the stop time
set_param(file_name,'StopTime',normal_stop_time);
One more thing,怎么通过代码控制Simulink更新内部仿真参数,做到动态仿真。方法是使用Simulink Assertion 模块。
在Assertion中设置一个触发逻辑,Simulink会自动运行Assertion下预先写好的代码。我的一个具体使用方式是写一句暂停代码(set_param(bdroot, ‘SimulationCommand’, ‘pause’)),触发后使Simulink仿真暂停,然后在m文件中用代码更新仿真参数和设置,最后通过set_param()控制Simulink继续运行。
关于Matlab与Simulink交互更详细的例子和使用方法,可参考下面教程:
1)Sim() 函数使用教程
https://zhuanlan.zhihu.com/p/57487483
https://link.zhihu.com/?target=https%3A//www.mathworks.com/help/simulink/slref/sim.html
2)Set_param()函数使用教程
https://link.zhihu.com/?target=https%3A//www.mathworks.com/help/simulink/slref/set_param.html
三. Pyhton与Matlab/Simulink交互
搞定了Python/Matlab交互,Matlab/Simulink交互之后,通过Python控制Simulink就容易很多了。整理交互逻辑是:Python启动matlab engine,然后通过代码控制Simulink启停,读取、更新仿真参数,最后直接读取、存储Matlab工作区的变量结果。
贴一段以Simulink作为环境,Python作为主控程序,调用Tensorflow进行强化学习的代码(代码有所简化),运用到了上面提到的大部分技巧。
import numpy as np
import tensorflow as tf
import matlab.engine
# ------------------------- Training loop ------------------------------
# Start Matlab engine and initialize simulink model
eng = matlab.engine.start_matlab()
eng.ini_sim_ddpg(nargout=0)
while True:
# set action
action_temp = policy(state_temp, action_noise, lower_action, upper_action)
print(f'action_temp is {action_temp}')
# sim and get next state
eng.set_param('simple_case_ddpg/Control/kp', 'value', str(kp), nargout=0)
eng.set_param('simple_case_ddpg/Control/ki', 'value', str(ki), nargout=0)
eng.set_param('simple_case_ddpg/Control/pause_time', 'value', str(pause_time), nargout=0)
eng.set_param('simple_case_ddpg', 'SimulationCommand', 'continue', nargout=0)
# update next state and reward
next_state_temp = np.array([-0.0116, eng.eval('out.t(end)'), eng.eval('out.Vt(end)')])
next_state_temp = next_state_temp[None, :] # expand dimension to (1, 3)
reward_temp = get_reward(next_state_temp) # get reward based on t and Vt
episode_reward += reward_temp
# record buffer
buffer_temp = merge_buffer(state_temp, action_temp, reward_temp, next_state_temp)
buffer = np.append(buffer, buffer_temp[None, :], axis=0)
buffer_counter += 1
# learn and update target network
learn()
update_target(target_actor.variables, actor.variables, tau)
update_target(target_critic.variables, critic.variables, tau)
# episode done
pause_time += step_time
if pause_time > (stop_time + step_time):
eng.set_param('simple_case_ddpg', 'SimulationCommand', 'stop', nargout=0)
break
# print episodic reward
print("Episode * {} * Avg Reward is ==> {}".format(episodes + ep + 1, episode_reward))
eng.quit() # quit Matlab engine
小结
本文主要讲述了Python,Matlab,Simulink三者交互的逻辑,小结了一些交互技巧。
现在来看,Matlab在2020、2021的版本中加强了对强化学习的支持,基本涵盖了目前主流的RL算法,使用Simulink作为环境时,不必大费周章地与Python进行交互了。
但是,如果想跟踪最新的AI研究成果,并用到科研中,上述交互还是很有价值的。因为最新的AI算法大都是Python开源,并且Github也有很多优质的资源。所以Mark一下本文,以做防身用。
共勉。