news 2026/10/16 16:35:59

基于MATLAB的物理信息神经网络求解Burger方程与流动建模实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于MATLAB的物理信息神经网络求解Burger方程与流动建模实践

物理信息神经网络(PINN)这两年几乎成了科学计算领域最热的词之一,尤其在偏微分方程求解、流动建模和反问题方向。以前我做流动计算,遇到强非线性方程,第一反应是加密网格、换格式、调时间推进方案;后来认真把PINN跑通,才发现深度学习和物理定律结合之后,能做的事比想象中多很多。这篇文章我会分享一套基于MATLAB的PINN求解方案,用Burger方程作为第一个算例,再延伸到其他复杂流动模型。如果你手上有MATLAB,但又不想专门去搭一套Python深度学习环境,这篇文章的路子可以直接照着走。

先说清楚这个内容能解决什么问题。传统数值方法在网格生成、边界处理和参数扫描上往往要花大量人力,PDE换个初边值条件,很可能就要重新生成一套流程。PINN的思路刚好相反:把偏微分方程的残差直接写进损失函数,让神经网络在拟合数据的同时遵守物理规律。它尤其适合参数化研究、稀疏数据反演这类场景。文章后面会用Burger方程跑通完整流程,再给出向Navier-Stokes等复杂流动模型扩展的具体思路。

1. 先搞清楚PINN到底在解什么方程

1.1 把物理方程写进损失函数,而不是写进网络结构

很多新手第一次接触PINN,总以为它是在网络结构上做了什么手脚,让网络天生满足物理定律。其实不是。PINN的核心思路非常朴素:你有一个待求函数u(x,t),用一个神经网络u_θ(x,t)去逼近它,然后在损失函数里显式地惩罚它偏离物理方程的程度。

以Burger方程为例:

u_t + u * u_x = ν * u_xx

如果u_θ是精确解,那残差项 r = u_t + u * u_x - ν * u_xx 应该处处等于零。PINN的做法就是对整个时空域采样一堆点,算这个残差,然后让残差的平方均值尽可能小。再加上初始条件的拟合误差和边界条件的拟合误差,一起作为总的损失函数。

比较关键的一点是,网络训练过程中需要的导数并不是用有限差分近似出来的,而是通过自动微分(Automatic Differentiation)精确计算的。MATLAB里的dlarray和dlgradient就是干这个用的。自动微分沿着网络的计算图链式求导,所以u_t、u_x、u_xx都是机器算出来的,不是我们手推的。

这个思路用生活化的说法解释:网络可以自由变形,但每次变形都要交“物理罚金”,偏离方程越远罚金越重。训练的本质就是在有限的数据约束和物理罚金之间找一个平衡点。

1.2 为什么我用MATLAB而不是Python框架

提到PINN,很多人第一反应是Python里的各种开源框架,生态确实丰富。但我最后选择用MATLAB,有几个很现实的原因。

第一,很多课题组和工程师手里已经有大量MATLAB代码,数据预处理、后处理、可视化全在MATLAB里。如果单独为PINN搭一套Python环境,数据要导来导去,光格式转换就能折腾半天。MATLAB深度学习工具箱从R2019b开始支持dlnetwork,后续版本又完善了自定义训练循环,写一个完整的PINN并不困难。

第二,MATLAB调试体验好。PINN训练不收敛的时候,经常需要检查中间某个张量的数值、看某一层的输出分布,Python里你要么print要么上debugger,MATLAB工作区里点开变量一目了然,这对从传统科学计算转过来的研究者特别友好。

第三,PINN这类问题网络规模通常不大,不需要上千层的Transformer,也不需要超大规模分布式训练。一个小型全连接网络,在MATLAB里用CPU跑几千步迭代,耗时也就几分钟到几十分钟。GPU有更好,没有也不会是瓶颈。

当然,MATLAB不是万能的。如果你要做非常复杂的算子库、大规模GPU并行,或者想直接用社区里现成的PINN组件,Python生态确实更丰富。我的建议是:项目数据和团队习惯在MATLAB就用MATLAB,不要为了“时髦”额外引入一套技术栈。

1.3 PINN擅长和不擅长的问题边界

我在实际项目里踩过不少坑,发现PINN不是万能药。先说说它擅长什么。

高维问题是一个典型场景。传统数值方法在高维空间会遇到网格爆炸,比如五维六维的PDE,网格法基本没法做。PINN用点采样代替网格剖分,只要采样点覆盖得住,高维问题也能尝试。还有就是参数化代理模型,这个概念后面会细说——把多个工况打包成一个网络,推理时秒出解。再一个是反问题,已知稀疏的场观测数据,反推方程系数或源项,这是PINN相比传统数值方法的突出优势。

但PINN也有明显的短板。强激波、低粘性这类问题,解里存在近间断的陡峭剖面,网络很难用一组光滑基函数精确表达,容易在激波附近产生抹平或振荡。超高雷诺数湍流直接硬解,目前也不太现实。另外,对于简单的标准PDE,传统有限差分或有限元无论在精度还是计算效率上往往都更胜一筹。

所以我的建议是:不要一上来就想着用PINN替代传统CFD,而是把它当成第二套工具,在参数扫描、反演、数据融合这些传统方法比较吃力的场景里发挥作用。

2. 用MATLAB搭建Burger方程的完整PINN求解器

2.1 方程设定与参考解准备

Burger方程是PINN入门最经典的测试用例,非线性对流项加粘性扩散项,既能体现出流动问题的难度,又不像完整Navier-Stokes方程那样复杂得让人劝退。

我采用的设定是:

x ∈ [-1, 1], t ∈ [0, 1] u_t + u * u_x = ν * u_xx u(0, x) = -sin(πx) u(t, -1) = u(t, 1) = 0

粘性系数ν取0.01。这个参数下有较明显的激波形成过程,对网络有一定挑战性,但对展示PINN能力又足够。如果你第一次跑,我建议先用ν = 0.1试一遍,解会更平滑,容易把整个流程跑通,等确认代码没有问题再切回ν = 0.01。

做PINN训练之前,先准备一套参考解用于事后对比。Burger方程理论上可以通过Cole-Hopf变换求解析解,但实际实现还是要做数值积分。更省事的方式是直接用高分辨率守恒型差分格式算一个基准解。我用的是迎风加中心差分的混合格式,空间网格取2000个点,时间步数取4000步,得到的解足够精细。示意图大致是这样:

% 高分辨率差分求解Burger方程,作为参考解(示意代码) % 注意:这里为了展示思路做了简化,正式使用请采用能达到网格无关的格式 nu = 0.01; Nx = 2000; Nt = 4000; x = linspace(-1, 1, Nx)'; dx = x(2) - x(1); dt = 1 / Nt; u = -sin(pi * x); for n = 1:Nt % 对流项采用迎风思路,粘性项用中心差分 flux = 0.5 * u.^2; % 一阶迎风/中心混合,实际需要CFL判断 u(2:end-1) = u(2:end-1) - dt/dx * (flux(2:end-1) - flux(1:end-2)) ... + nu * dt/dx^2 * (u(3:end) - 2*u(2:end-1) + u(1:end-2)); % 边界条件 u(1) = 0; u(end) = 0; end

这个参考解不要求达到论文级精度,只要网格够细、格式稳定,能给出可靠的对比基准就够了。

2.2 网络定义与自动微分残差

在MATLAB里定义网络使用dlnetwork。我常用的结构是4层隐藏层、每层50个神经元,激活函数用tanh。

layers = [ featureInputLayer(2, 'Normalization', 'none') fullyConnectedLayer(50) tanhLayer fullyConnectedLayer(50) tanhLayer fullyConnectedLayer(50) tanhLayer fullyConnectedLayer(1) ]; net = dlnetwork(layers);

为什么要用tanh而不用ReLU?原因很简单:PINN的损失里要求u对x的二阶导,ReLU是分段线性函数,二阶导恒等于零,网络就失去了表达粘性项的能力。tanh这类光滑激活函数是更合理的选择。

损失函数是PINN的核心。下面的示意代码展示了如何用dlgradient计算一阶导和二阶导,从而形成PDE残差:

function [loss, grads] = modelLoss(net, X, T, X0, T0, U0, nu) % 网络输入是 [X; T],输出是u U = forward(net, [X; T]); % 计算一阶和二阶导数 Ux = dlgradient(U, X); Uxx = dlgradient(Ux, X); Ut = dlgradient(U, T); % PDE残差 f = Ut + U .* Ux - nu * Uxx; pdeLoss = mean(f(:).^2); % 初始条件损失 U0_pred = forward(net, [X0; T0]); icLoss = mean((U0_pred(:) - U0(:)).^2); % 边界条件损失:x=-1 和 x=1 处近似为0 % 实际实现要传入左右边界点,这里示意 U_left = forward(net, [X_left; T_left]); U_right = forward(net, [X_right; T_right]); bcLoss = mean(U_left(:).^2) + mean(U_right(:).^2); % 总损失 loss = pdeLoss + icLoss + bcLoss; grads = dlgradient(loss, net.Learnables); end

注意,这里X、T、X0、T0都必须是dlarray,而且格式要保持列对齐。比如残差点采样时,X和T是同一条样本的横坐标和时间戳,拼接成2×N的输入矩阵,网络输出的列才和样本一一对应。

2.3 采样策略与训练流程

PINN不需要网格,但需要“点”。点怎么采,直接影响训练效果。

残差点:在x∈[-1,1]、t∈[0,1]的整个定义域内随机采样。我用lhsdesign(拉丁超立方)或者简单rand都行,采样量Nf取10000。这些点的作用是让网络在全局范围满足PDE。

初始点:在t=0时刻,x方向上采样200个点,u值是已知的,由初始条件给出。

边界点:在x=-1和x=1的边界上,各采样100~200个时间点,u值等于0。

采样代码大致如下:

rng(0); Nf = 10000; X = dlarray(-1 + 2 * rand(1, Nf), 'CB'); T = dlarray(rand(1, Nf), 'CB'); Nic = 200; X0 = dlarray(-1 + 2 * rand(1, Nic), 'CB'); T0 = dlarray(zeros(1, Nic), 'CB'); U0 = dlarray(-sin(pi * X0), 'CB');

训练的主循环用adamupdate。这里有个很实用的建议:不要迷信单阶段长训练,我推荐两阶段策略。先用一个相对大的学习率(1e-2)跑15000到20000步,让loss快速下降;再降到1e-3精调。两阶段搭配起来,往往比始终用一个学习率收敛得更快、更稳。

3. 训练实操与收敛细节

3.1 拆开损失函数,学会看训练曲线

很多新手看到总loss开始下降,就觉得万事大吉,这是很危险的。总loss只是三个损失项的加权和,同一个数值可能对应完全不同的内部状态。我建议在训练循环里把pdeLoss、icLoss、bcLoss分别打印出来。

numIter = 20000; learnRate = 1e-2; avgGrad = []; avgSqGrad = []; for iter = 1:numIter [loss, grads] = dlfeval(@modelLoss, net, X, T, X0, T0, U0, nu); [net, avgGrad, avgSqGrad] = adamupdate(net, grads, avgGrad, avgSqGrad, iter, learnRate); if mod(iter, 1000) == 0 fprintf('Iter %6d, loss = %.3e, pde = %.3e, ic = %.3e, bc = %.3e\n', ... iter, extractdata(loss), extractdata(pdeLoss), ... extractdata(icLoss), extractdata(bcLoss)); end end

如果你看到pdeLoss一直压在1e-2量级下不去,大概率是残差点不够密,或者网络宽度不够;如果icLoss总是比pdeLoss高一个量级,说明初始点附近的拟合出了问题,需要增加初始点密度,或者对初始条件做硬约束。

3.2 两阶段训练与学习率调整

两阶段训练是PINN实战中很有效的手段。第一轮用1e-2的学习率跑20000步,观察loss曲线。当loss下降变得非常缓慢时,把学习率降到1e-3,再跑10000到20000步。这个过程中不需要改其他参数。

需要注意,PINN的loss曲线不像传统深度学习那样稳定平滑,经常会出现小幅震荡,尤其是PDE残差点重新采样之后。遇到这种情况,先看总体趋势,不要因为单次跳变就急着调参。

如果训练到一半loss突然升高,最常见的元凶是学习率太大,导致参数越过最优区域。这时候不要盲目重启,先把学习率降一个量级,继续从当前模型接着训练,很多时候就能救回来。

3.3 结果对比与误差判断

训练完成后,需要在稠密网格上推理出整个解曲面。方法很简单:

x_eval = linspace(-1, 1, 400); t_eval = linspace(0, 1, 200); [Xm, Tm] = meshgrid(x_eval, t_eval); Xt = dlarray([Xm(:)'; Tm(:)'], 'CB'); U_pred = predict(net, Xt); U_pred = reshape(extractdata(U_pred), size(Xm));

接下来和参考解做三件事:第一,固定几个时间层画剖面线,比如t=0.25、0.5、0.75、1.0,看解的激波位置和厚度是否和参考解基本吻合;第二,画整个时空域的预测热图,直观感受解曲面;第三,算相对L2误差,作为量化指标。

从实际测试来看,ν=0.01的Burger方程,PINN在激波附近通常会有轻微的抹平效应,这是正常现象,不必过度焦虑。如果发现激波附近误差特别大,可以在激波轨迹附近增加采样密度,或者把隐藏层宽度从50加到80。

4. 从Burger方程走向复杂流动模型

4.1 不可压缩Navier-Stokes方程的PINN形式

从Burger方程迁移到Navier-Stokes方程,核心思路完全一样,只是网络输入和输出的维数变了。以二维不可压缩NS方程为例:

u_x + v_y = 0 u_t + u*u_x + v*u_y = -p_x / ρ + ν*(u_xx + u_yy) v_t + u*v_x + v*v_y = -p_y / ρ + ν*(v_xx + v_yy)

网络输入从(x,t)变成(x,y,t),输出从一个标量u变成三个分量[u, v, p]。损失函数里除了动量方程的两个残差项,还要加入连续性方程残差项。

这里有一个非常值得注意的坑:压力p没有独立的进化方程,它整体可以加减一个常数而不影响速度场。如果训练过程中不固定压力参考点,p的均值会漂移,训练时经常会看到一个不断变化的常数偏移。解决方法是固定某一点的p值,或者在网络输出端让p减去当前batch的平均值。

初始条件和边界条件的处理方式与Burger方程一致,都是构造拟合损失。如果几何比较复杂,只需要在边界上采样点,不需要生成贴体网格,这是PINN相对于传统CFD方法的一大优势。

4.2 参数化代理模型与多工况覆盖

当网络结构确定之后,一个很自然的扩展就是把方程里的参数也当成输入。比如想让网络覆盖多个粘性系数ν的工况,可以把网络输入从(x,t)扩展到(x,t,ν),训练时每个batch随机抽取若干个ν值。

这个做法对工程场景特别有意义。传统CFD要覆盖多个雷诺数,需要逐个工况仿真,每个工况都要重新生成网格、迭代求解。PINN参数化代理模型训练一次,推理时输入不同的ν就能秒出对应流场。我第一次跑通这个模式时还挺惊喜的,一个网络顶替掉了原先一堆重复算例。

不过要注意,参数跨度大时网络输入尺度差异会非常明显。建议先把ν这类参数做归一化到0到1,否则网络会因为输入尺度过大而难以收敛。当然,也可以把这类参数做log缩放后归一化,效果往往更好。

4.3 反问题与数据融合

PINN更大的价值在反问题。很多时候我们拿不到完整的初始条件和边界条件,但手里有部分稀疏的观测数据,希望反推方程的未知参数,这时候传统方法很棘手,PINN却能自然地处理。

做法是:把方程中的未知系数(比如粘性ν、某个源项强度)定义成网络的可训练变量,损失函数里除了PDE残差和初边值条件,再加上观测数据的拟合误差。训练过程中,网络权重和这个未知参数同时被Adam更新。训练结束后,未知参数的取值就是反演结果。

这里有个经验之谈:观测数据项的权重通常要设得大一些,比如10或者50,否则物理残差会把数据拟合给“带偏”。如果数据本身有噪声,权重还要根据信噪比适当调整。这种把实测数据和物理方程结合在一起的思路,在工业测量、医疗影像、场重建等场景里非常实用。

5. 常见问题与排查技巧

5.1 训练不收敛时的排查路径

我见过太多人第一步就栽在“loss怎么都降不下去”上。这里给出一个排查路径,基本可以覆盖大多数情况。

第一步,先看网络输出有没有NaN。如果有,八成是学习率太高,或者输入没有归一化,导致梯度爆炸。把学习率降到1e-3以下试试。

第二步,看总loss的内部构成。用前面说的拆解打印,定位是pdeLoss、icLoss还是bcLoss的问题。三个都高,大概率是采样点不够或者网络容量不足;只有某一个高,就针对性地处理。

第三步,看残差点的采样范围。确定有没有把时间和空间范围设错,比如把t采到了[0,1]之外,网络在定义域外学习了一堆没用的信息。

第四步,检查激活函数。如果用了ReLU,别犹豫,换成tanh。ReLU做PINN基本等于自断一臂。

5.2 边界条件失效与激波附近误差过大

边界条件不满足是很常见的问题。软约束方式下,网络有可能为了降低PDE残差而牺牲边界拟合。如果你发现边界附近误差始终压不下来,最简单的办法是加大边界损失的权重,比如把bcLoss乘上一个5到10的系数。

更彻底的办法是硬约束。比如Burger方程的边界条件是u(t,-1)=u(t,1)=0,可以在网络输出后面做一个仿射变换:

u_out = (1 - x)/2 * u_left(t) + (1 + x)/2 * u_right(t) + (1 - x^2) * u_net

这样在x=-1处u_out自动等于u_left(t),在x=1处自动等于u_right(t),边界条件被严格满足,连边界损失项都可以去掉。这个技巧在很多PINN工程里非常实用,尤其适合边界条件比较简单的情况。

至于激波附近误差大,我的习惯是先增大采样点数量,并在激波轨迹附近加密采样。如果还不行,再把网络宽度往上加一档。同时可以把PDE残差中粘性项的权重适当调高,因为激波位置的二阶导数变化非常剧烈,网络需要更强的监督才能捕捉。

5.3 几个值得养成的调参习惯

最后分享几个我自己总结的调参习惯。

第一,每次训练固定随机种子。PINN对采样点和初始化很敏感,不固定随机种子,两次训练曲线完全不同,你会分不清是参数改好了还是运气问题。

第二,把各个损失分量分开打印。总loss不能说明任何问题,拆开看才定位得了瓶颈。

第三,先用简单case验证流程,再上难度。比如先跑ν=0.1的平滑解,确认整套代码没问题,再挑战ν=0.01的激波问题。不要一上来就挑战最难的配置,否则代码有bug你都分不清是bug还是网络能力不足。

第四,训练过程中定期保存模型。我经常遇到一个模型跑了很久,突然学习率没有调好,loss跳上去的情况。如果中途保存了较好的一次结果,至少不用全部重来。

第五,不要迷信网络越深越好。PINN的问题难点往往不在网络容量,而在采样策略、损失函数权重和学习率。加宽一档如果没用,优先检查损失函数的设计。

写到这里,说点我自己的实际体会。第一次在MATLAB里把PINN完整跑通时,我并没有立刻替换掉原来的CFD流程,而是把它当成“第二套工具”来用:传统数值方法继续负责高精度基准解,PINN负责快速预测、参数扫描和反问题。这种混合工作模式,比单纯争论某种方法的输赢要实用得多。如果你也准备在自己的项目里试PINN,我建议先用Burger方程把这条链路彻底走通,再决定要不要往Navier-Stokes方程上迁移。思路一通,后面的路会顺很多。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/16 16:35:59

终端AI编程助手费用失控?free-claude-code的省钱与模型路由实践

我最近把日常开发的一大半搬进了终端,工作流里自然也多了 Claude Code。第一次在项目里跑通它的时候,我确实有点上头:它能自己翻代码、跑测试、改完文件还会回头给你讲思路,几乎是把结对编程塞进了命令行。但第二周查账单的时候我…

作者头像 李华
网站建设 2026/10/16 16:35:48

非对称纳什谈判下多微网电能共享优化策略与实现

干微电网优化的人应该都有印象:一个园区里,A栋屋顶光伏在午间发得多了,消纳不掉,逆变器被迫限功率;隔壁B栋是商业综合体,午间恰恰是负荷高峰,正在按尖峰电价从网上买电。两边物理上只隔几百米&a…

作者头像 李华
网站建设 2026/10/16 16:35:33

APS高级排产系统实施避坑指南:从约束建模到数据治理

简介:APS高级排产系统是一套面向制造业生产计划与调度场景的Java实现资料包,涵盖需求预测、产能规划、多级物料需求计划、约束优化等功能模块,适合企业信息化实施人员、ERP/MES二次开发工程师及有一定Java基础的学习者参考。压缩包内共707个文…

作者头像 李华
网站建设 2026/10/16 16:33:50

电力系统暂态稳定仿真:Matlab与Simulink故障分析实战

还在学校里啃电力系统分析的时候,最头疼的事情之一就是“暂态稳定”这一块。课本上把等面积法则、摇摆曲线、极限切除时间解释得清清楚楚,可一旦让你回答“三相短路到底比两相接地严重多少”“故障发生在线路首端和末端差别有多大”这类问题,…

作者头像 李华
网站建设 2026/10/16 16:30:28

STL源码剖析第六章:C++标准库算法的泛型实现与工程实践

1. 这不是一本“算法导论”,而是一把解剖C标准库的手术刀如果你在书架上见过那本封面泛黄、边角卷起的《STL源码剖析》,大概率它正安静地躺在某位C老手的案头,书页间夹着几根不同颜色的荧光笔标记,页脚密密麻麻写满批注。而第六章…

作者头像 李华
网站建设 2026/10/16 16:29:16

RPGMZ插件列表管理指南:注册机制、顺序与冲突排查

简介:面向 RPG Maker MZ 开发者的功能扩展插件合集,共收录二十九个文件,其中包括二十七个以 Javascript 编写的插件、一份说明文档与一份许可证。插件体系覆盖技能树、炼金合成、虚拟手柄、装备场景扩展、任务系统、战斗状态更新等常用模块&a…

作者头像 李华