Motivation
搞过数字孪生的都知道,在数字孪生当中比较有价值的是代理模型这一块。这是因为有限元仿真往往比较耗时,难以达到根据边界条件的变化实时进行变化。那么用已经仿真好的数据训练代理模型则是一个替代的方案。 在代理模型这一块比较常用的有POD(SVD)矩阵降维和RBF(径向基函数)等,还有各式各样的mlp、cnn等神经网络(其实RBF也可以写成神经网络)。在Unity当中也有Barricuda库能够调用转换成ONNX格式的神经网络模型(TensorFlow、Torch等)。
一旦涉及到神经网络的训练,一方面需要一些深度学习的基础,会涉及到调参,又因为有限元仿真数据具有其特殊性,有些位置可能数值特别小但非常关键,mlp的loss已经降到足够小了,但可能误差很轻易就会达到200%往上,导致这一块往往会消耗大量的时间还不一定能调出能接受的结果。
在实际研究中发现像Python的Scipy库当中其实已经有了RBF的实现方式,而且代码写的非常规范,很轻易就能实现超高精度的插值,精度等级可以达到(e-26),即便有限元仿真数据很小都应该能够满足要要求。然而,在转换Scipy数据库关于RBF插值预测的源代码时发现其中有一部分使用了Cython编写的dll动态链接库,这就使得很难在Unity当中使用C#重新改写Scipy当中的源代码实现过程。如果能用Unity调用Python的Scipy库进行计算并交互也是一个非常巧妙的解决方案。
解决方案
用pythonnet包在unity当中实现Python代码的调用。
Pythonnet是一个Python的扩展模块,允许Python与.NET框架进行交互。它提供了一个双向的桥梁,使得你可以在Python中调用.NET代码,也可以在.NET中调用Python代码。这对于那些希望在Python和.NET之间进行集成的开发人员来说非常有用。Pythonnet允许你在Python中调用.NET类、方法和属性,并且可以在Python中处理.NET对象。使用Pythonnet,你可以利用.NET框架的强大功能,比如.NET库、C#代码等,与Python的灵活性和易用性相结合。这在跨平台开发和混合语言开发方面特别有用,因为它让Python和.NET之间的交互变得非常方便。
放一张调试成功的图片,视频放在B站了,感兴趣可以点击移步过去看看。
操作过程需要注意的点
python当中需要进行的操作
1、在自己的python环境中,pip install pythonnet.
2、确认pythonnet安装的环境目录,并设置其为环境变量
Unity当中需要进行的操作
1、找到pythonnet安装环境当中的动态链接库 Python.Runtime.dll 并将其放在unity当中的asset/plugins路径下.
2、找到Microsoft.CSharp.dll并将其放在Unity的asset/plugiins路径下.
部分源代码放上供参考:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Python.Runtime;
using System;
using UnityEngine.UI;
public class RBFInterpolater : MonoBehaviour
{
public Slider framecontrol;
// Python环境路径和Python DLL路径
private string pythonHome = @"F:\Python\Python39";
private string pythonPath = @"F:\Python\Python39;F:\Python\Python39\Lib\site-packages";
private string pythonDLLPath = @"F:\Python\Python39\python39.dll";
// Python引擎初始化标志
private bool pythonInitialized = false;
Mesh mesh;
// Start is called before the first frame update
void Start()
{
// 检查Python引擎是否已初始化
if (!PythonEngine.IsInitialized)
{
// 设置Python环境变量
Environment.SetEnvironmentVariable("PYTHONHOME", pythonHome);
Environment.SetEnvironmentVariable("PYTHONPATH", pythonPath);
// 设置Python DLL路径
Runtime.PythonDLL = pythonDLLPath;
// 初始化Python引擎
PythonEngine.Initialize();
pythonInitialized = true;
Debug.Log("Python engine has been initialized!");
}
}
modelColorInit(framecontrol.value);
}
private void modelColorInit(float sliderValue)
{
List<float> currentPysicData = new List<float>();
// 使用Python插值器进行计算
using (Py.GIL())
{
dynamic np = Py.Import("numpy");
dynamic sci_in = Py.Import("scipy.interpolate");
// 加载数据
dynamic X = np.array(new float[,] { { 12 }, { 13 }, { 14 }, { 15 }, { 16 }, { 17 }, { 18 } });
dynamic Y = np.load(@"E:\science_research\code repo\VScode\zhongchuan\data\numpy\interpolated_acc.npy", allow_pickle: true);
Debug.Log("Data has been loaded!");
// 创建RBF插值器
dynamic rbf = sci_in.RBFInterpolator(X, Y, kernel: "gaussian", epsilon: 1.0);
Debug.Log("RBF Interpolator has been trained!");
// 使用RBF插值器进行计算
dynamic x = np.array(new float[,] { { sliderValue } });
dynamic result = rbf(x);
Debug.Log("The case of speed = " + sliderValue + " has been calculated!");
Debug.Log(result.ToString());
// 将result转换为List<float>
foreach (var item in result[0])
{
currentPysicData.Add((float)item);
}
}
}
private void OnDestroy()
{
// 在销毁时关闭Python引擎
if (pythonInitialized)
{
PythonEngine.Shutdown();
}
}
}