Dymola通过Python API调用C函数结合time与numpy出现段错误
问题成因
该段错误的核心触发原因是Numpy库与Python C API的重复初始化逻辑不兼容:
- 你当前封装的
pyRunString函数每次被调用时,都会执行一次Py_Initialize()初始化Python解释器、运行代码、再执行Py_Finalize()销毁解释器 - Dymola仿真过程中,每个时间步都会重新调用一次绑定了
time变量的pyString函数,相当于解释器会被重复初始化、销毁数十到数千次 - Numpy底层依赖的部分静态全局变量,在第一次导入并销毁解释器后不会被完全释放,第二次初始化解释器并重新导入Numpy时,就会触发非法内存访问,也就是段错误
- 你之前的两个正常运行的测试用例刚好避开了这个问题:第一个用例不涉及Numpy导入,重复初始化解释器不会出错;第二个用例传入的是固定常量
0.0,Dymola会自动优化为只调用一次外部函数,不会触发重复初始化逻辑
修复方案
只需修改C代码逻辑,让Python解释器在整个仿真生命周期内只初始化一次,不要每次调用都销毁重建即可,修改后的代码如下:
1. 修改Modelica外部函数的C实现
function pyString input String s; output Real o; external "C" o = pyRunString(s) annotation(Include=" #include <Python.h> // 静态标记位,记录Python解释器是否已经初始化 static int py_is_initialized = 0; double pyRunString(const char *str) { if (!py_is_initialized) { Py_Initialize(); // 预导入numpy,避免后续重复导入的开销与风险 PyRun_SimpleString(\"import numpy\"); py_is_initialized = 1; } PyRun_SimpleString(str); // 移除Py_Finalize调用,仿真结束进程退出时系统会自动回收所有资源 return 0.0; } ", Libraries="-lpython3.8", IncludeDirectory="/usr/include/python3.8"); end pyString;
2. 可选优化
如果需要在仿真结束后手动释放Python相关资源,可以额外定义一个终止函数,在仿真手动停止的场景下调用:
void pyCleanup() { if (py_is_initialized) { Py_Finalize(); py_is_initialized = 0; } }
注意事项
请确保annotation里的IncludeDirectory和Libraries参数匹配你本地的Python3.8安装路径,避免编译时找不到Python头文件或动态库的问题。
内容的提问来源于stack exchange,提问作者dr_auerbach
相关产品推荐
相关产品推荐

