生成嵌入Python脚本的FMU:无Ubuntu权限安装Python环境的解决方案咨询
解决无管理员权限Ubuntu系统下嵌入Python脚本的FMU生成问题
方案1:修改PythonFMU的CMake脚本,嵌入独立Python环境
步骤1:编译独立的Python解释器(无管理员权限)
在Ubuntu20.04环境(或同版本Docker容器)中,从源码编译仅用户权限的独立Python版本:
# 下载兼容Ubuntu20.04的Python源码(以3.8为例) wget https://www.python.org/ftp/python/3.8.18/Python-3.8.18.tgz tar xzf Python-3.8.18.tgz cd Python-3.8.18 # 指定安装到用户目录,编译为静态链接版本 ./configure --prefix=$HOME/my_python --enable-shared=no --enable-static=yes make -j$(nproc) make install
编译完成后,$HOME/my_python目录包含完整的独立Python环境,包括解释器、标准库和pip。
步骤2:安装依赖到独立环境
用独立pip安装脚本所需依赖:
$HOME/my_python/bin/pip install numpy pandas # 替换为你的实际依赖
步骤3:修改PythonFMU的CMake配置
找到PythonFMU项目的CMakeLists.txt,做以下调整:
- 指定独立Python路径:替换系统Python查找逻辑,直接指向你的独立环境:
set(Python_ROOT_DIR "$ENV{HOME}/my_python") set(Python_INCLUDE_DIR "${Python_ROOT_DIR}/include/python3.8") set(Python_LIBRARY "${Python_ROOT_DIR}/lib/libpython3.8.a")
- 嵌入Python环境到FMU:添加规则,将独立Python的核心目录复制到FMU的
resources文件夹:
# 复制Python解释器 add_custom_command(TARGET your_fmu_target POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory "${Python_ROOT_DIR}/bin" "${FMU_OUTPUT_DIR}/resources/python/bin" ) # 复制Python标准库与依赖包 add_custom_command(TARGET your_fmu_target POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory "${Python_ROOT_DIR}/lib/python3.8" "${FMU_OUTPUT_DIR}/resources/python/lib/python3.8" )
- 调整FMU初始化逻辑:在PythonFMU的C++ wrapper代码中,设置Python环境变量指向内部路径:
// 获取FMU资源路径 const char* resourcePath = fmi2GetResourcePath(instance); std::string pythonBinPath = std::string(resourcePath) + "/python/bin/python3.8"; std::string pythonLibPath = std::string(resourcePath) + "/python/lib/python3.8"; // 设置PYTHONPATH setenv("PYTHONPATH", pythonLibPath.c_str(), 1); // 初始化Python解释器时指定内部路径 Py_SetProgramName((wchar_t*)pythonBinPath.c_str()); Py_Initialize();
方案2:用PyInstaller打包Python脚本为独立二进制,再封装成FMU
此方案无需修改PythonFMU核心CMake,实现更简单:
步骤1:打包Python脚本为独立二进制
在Ubuntu20.04环境下,用PyInstaller生成不依赖系统Python的单文件二进制:
pip install pyinstaller pyinstaller --onefile --target-platform linux-x86_64 your_script.py
生成的dist/your_script为独立可执行文件。
步骤2:封装成FMU
- 基于PythonFMU编写简单wrapper,在
doStep函数中调用二进制传递数据:
from pythonfmu import FmuBuilder, Fmu2Slave class MySlave(Fmu2Slave): def __init__(self, instanceName, fmuGUID, fmuResourceLocation, mimeType, visible, logLevel): super().__init__(instanceName, fmuGUID, fmuResourceLocation, mimeType, visible, logLevel) self.input_val = 0.0 self.output_val = 0.0 self.script_path = f"{fmuResourceLocation}/your_script" def doStep(self, currentTime, stepSize): import subprocess result = subprocess.run([self.script_path, str(self.input_val)], capture_output=True, text=True) self.output_val = float(result.stdout.strip()) return fmi2Status.ok if __name__ == "__main__": FmuBuilder.build_FMU(MySlave, "MyPythonFMU", "1.0", "me", resources=["dist/your_script"])
- 打包时通过
resources参数将二进制加入FMU的resources目录。
方案3:用Nuitka编译Python脚本为C++代码,静态链接到FMU
此方案将Python代码直接编译为机器码,完全消除对Python解释器的依赖:
步骤1:用Nuitka编译Python脚本
pip install nuitka nuitka --standalone --static-libpython=yes --output-dir=compiled your_script.py
编译后compiled/your_script.dist目录包含静态链接的可执行文件。
步骤2:与FMU wrapper静态编译
用FMU SDK编写C++ wrapper,直接调用编译后的Python代码导出函数,再将wrapper与Nuitka生成的目标文件一起编译为FMU二进制模块,最终打包成FMU。该方式生成的FMU体积更小、运行效率更高。
关键注意事项
- 所有打包操作必须在Ubuntu20.04环境下完成,确保二进制与目标系统兼容。
- 测试时在目标系统直接加载FMU,不要设置任何Python相关环境变量,验证运行状态。
- 对于依赖C扩展的Python库(如numpy),需确认打包工具(PyInstaller/Nuitka)已自动包含对应编译产物,必要时手动补充。
内容的提问来源于stack exchange,提问作者Michele Urbani
相关产品推荐
相关产品推荐

