You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于FMI 2.0.3的C语言FMU Slave创建、初始化及核心函数实现疑问

FMI 2.0.3 模拟器初始化与FMU实现问题解决

一、FMU初始化(fmi2Instantiate)相关问题

1. GUID 传入值

每个FMU的modelDescription.xml文件中,<fmi2ModelDescription>标签下的guid属性是该FMU的唯一标识,直接复制该字符串作为参数传入即可。例如:"a1b2c3d4-5678-90ef-ghij-klmnopqrstuv"。

2. 资源路径传入值

资源路径指FMU解压后包含modelDescription.xml及相关资源文件的目录路径。若你的FMU是打包的.fmu文件,需先解压到指定目录,再将该目录的绝对路径(或模拟器可识别的相对路径)传入。比如Windows下用"./fmu_system_unpacked",Linux下用"./fmu_system_unpacked"。

3. 回调函数实现与传入

回调函数是FMU与模拟器交互的桥梁,需实现fmi2CallbackFunctions结构体中的4个核心函数:

  • fmi2Logger:处理FMU的日志输出,直接打印到控制台即可
  • fmi2AllocateMemory:FMU申请内存时调用,封装标准malloc
  • fmi2FreeMemory:释放FMU内存,封装标准free
  • fmi2StepFinished:异步步结束通知,同步仿真模式下可留空实现

示例代码:

void fmi2Logger(fmi2ComponentEnvironment env, fmi2String instanceName, fmi2Status status, fmi2String category, fmi2String message, ...) {
    va_list args;
    va_start(args, message);
    printf("[%s] %s: ", category, instanceName);
    vprintf(message, args);
    va_end(args);
}

void* fmi2AllocateMemory(size_t nobj, size_t size) {
    return malloc(nobj * size);
}

void fmi2FreeMemory(void* obj) {
    free(obj);
}

void fmi2StepFinished(fmi2ComponentEnvironment env, fmi2Status status) {
    // 同步仿真无需处理,留空即可
}

// 组装回调结构体
fmi2CallbackFunctions callbacks = {
    .logger = fmi2Logger,
    .allocateMemory = fmi2AllocateMemory,
    .freeMemory = fmi2FreeMemory,
    .stepFinished = fmi2StepFinished,
    .componentEnvironment = NULL // 可传入自定义上下文指针,比如模拟器状态
};

调用fmi2Instantiate时,将上述callbacks结构体传入,instanceName可自定义(如"FMU-System"),fmuType选择fmi2CoSimulation(协同仿真模式)。

二、FMU核心函数实现逻辑

1. fmi2Component 的作用

fmi2Component是FMU实例的句柄,本质是指向自定义FMU内部状态结构体的指针。比如针对FMU-system,可定义:

typedef struct {
    double system_state; // 系统状态变量(如温度、流量)
    double control_param; // 来自FMU-control的控制参数
    fmi2Real current_time; // 当前仿真时间
} MySystemFMU;

在fmi2Instantiate中,用fmi2AllocateMemory分配该结构体内存,返回的指针即为fmi2Component,后续所有FMU函数均通过该句柄访问内部状态。

2. fmi2SetReal / fmi2GetReal 实现

这两个函数负责读写FMU变量,需提前在modelDescription.xml中为每个变量定义唯一的valueReference(VR):

  • fmi2SetReal:根据传入的VR找到对应内部变量,完成赋值
  • fmi2GetReal:根据VR读取内部变量值,返回给模拟器

FMU-system的示例实现:

fmi2Status fmi2SetReal(fmi2Component c, const fmi2ValueReference vr[], size_t nvr, const fmi2Real value[]) {
    MySystemFMU* fmu = (MySystemFMU*)c;
    for (size_t i = 0; i < nvr; i++) {
        switch (vr[i]) {
            case CONTROL_PARAM_VR: // 提前定义的控制参数VR
                fmu->control_param = value[i];
                break;
            default:
                return fmi2Error;
        }
    }
    return fmi2OK;
}

fmi2Status fmi2GetReal(fmi2Component c, const fmi2ValueReference vr[], size_t nvr, fmi2Real value[]) {
    MySystemFMU* fmu = (MySystemFMU*)c;
    for (size_t i = 0; i < nvr; i++) {
        switch (vr[i]) {
            case SYSTEM_STATE_VR: // 提前定义的系统状态VR
                value[i] = fmu->system_state;
                break;
            default:
                return fmi2Error;
        }
    }
    return fmi2OK;
}

3. fmi2DoStep 实现(FMU-system状态计算)

该函数是单次仿真步的核心,流程为:

  1. 校验当前时间与步长的合法性
  2. 利用当前控制参数,通过闭式方程计算新的系统状态
  3. 更新FMU内部的当前时间与状态

示例代码(假设闭式方程为system_state(t+dt) = system_state(t) + control_param * dt):

fmi2Status fmi2DoStep(fmi2Component c, fmi2Real currentCommunicationPoint, fmi2Real communicationStepSize, fmi2Boolean noSetFMUStatePriorToCurrentPoint) {
    MySystemFMU* fmu = (MySystemFMU*)c;
    
    // 执行闭式方程计算
    fmu->system_state += fmu->control_param * communicationStepSize;
    // 更新当前仿真时间
    fmu->current_time = currentCommunicationPoint + communicationStepSize;
    
    return fmi2OK;
}

针对FMU-control,fmi2DoStep的逻辑则是:通过fmi2SetReal获取系统状态后,用闭式方程计算新的控制参数,等待模拟器通过fmi2GetReal读取。

内容的提问来源于stack exchange,提问作者user14018421

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 11:45:45