基于fmpy实现同状态不同输入的FMU同时间步联合仿真问题咨询
需求与问题
需求
对同一FMU分别施加标称输入和扰动输入,要求在同一时间步运行,且初始状态完全一致。
遇到的问题
- 核心难点为FMU状态复制(保存/恢复所有数值状态)
- 当前使用fmpy工具,但目标FMU不支持其自带的
saveState()/restoreState()功能 - 尝试调用
cancelStep()重置状态时返回error 3(对应FMI2错误码:步骤无法取消,因未处于运行状态)
替代解决方案
方案1:双FMU实例并行仿真(最可靠)
直接实例化两个独立的FMU对象,各自维护独立状态空间。只需确保两者初始化参数、初始条件完全一致,分别施加标称/扰动输入即可。该方案无需依赖FMU的状态保存接口,兼容性拉满。
方案2:手动遍历保存所有状态变量
若必须复用单FMU实例,需手动识别并保存所有状态变量:
- 从ModelDescription中筛选
causality="local"、variability="continuous"/"discrete"的变量(这类是核心状态变量) - 用
getReal()/getInteger()/getBoolean()读取所有状态变量值并缓存 - 重置时重新设置这些变量值,必要时重新执行初始化流程
方案3:重新初始化FMU
放弃cancelStep(),每次仿真前重新执行实例化、初始化流程,强制回到初始状态。缺点是初始化开销较大,适合小模型或低频次仿真场景。
代码实现(方案1:双实例并行仿真)
from fmpy import read_model_description, extract from fmpy.fmi2 import FMU2Slave fmu_filename = 'BouncingBall.fmu' Ts = 0.01 # 加载模型描述,构建变量引用映射 model_description = read_model_description(fmu_filename) vrs = {var.name: var.valueReference for var in model_description.modelVariables} v_ref = vrs['v'] y_ref = vrs['h'] # 提取FMU到临时目录 unzipdir = extract(fmu_filename) # 初始化FMU实例的通用函数 def init_fmu_instance(fmu): fmu.instantiate() fmu.setupExperiment(startTime=0.) fmu.enterInitializationMode() fmu.exitInitializationMode() # 实例化两个独立FMU:标称输入/扰动输入 fmu_nominal = FMU2Slave( guid=model_description.guid, unzipDirectory=unzipdir, modelIdentifier='BouncingBall', instanceName='nominal' ) fmu_disturbed = FMU2Slave( guid=model_description.guid, unzipDirectory=unzipdir, modelIdentifier='BouncingBall', instanceName='disturbed' ) # 初始化两个实例,确保初始环境一致 init_fmu_instance(fmu_nominal) init_fmu_instance(fmu_disturbed) # 设置初始条件 v_t0 = 0 y_t0 = 2 dv = 0.1 # 给两个实例分别设置输入 fmu_nominal.setReal([v_ref, y_ref], [v_t0, y_t0]) fmu_disturbed.setReal([v_ref, y_ref], [v_t0 + dv, y_t0]) # 同一时间步执行仿真 fmu_nominal.doStep(currentCommunicationPoint=0., communicationStepSize=Ts) fmu_disturbed.doStep(currentCommunicationPoint=0., communicationStepSize=Ts) # 输出结果 print("标称输入仿真结果:") print(f" 高度h = {fmu_nominal.getReal([y_ref])[0]}") print(f" 速度v = {fmu_nominal.getReal([v_ref])[0]}") print("\n扰动输入仿真结果:") print(f" 高度h = {fmu_disturbed.getReal([y_ref])[0]}") print(f" 速度v = {fmu_disturbed.getReal([v_ref])[0]}") # 清理资源 fmu_nominal.terminate() fmu_nominal.freeInstance() fmu_disturbed.terminate() fmu_disturbed.freeInstance()
内容的提问来源于stack exchange,提问作者Daan Bortels
相关产品推荐
相关产品推荐

