☰
强形式PINN求解固体力学问题:从原理到工程实践
2026/10/21 14:59:23 网站建设 项目流程

PINN 这几年在计算力学圈子里几乎成了“万物可炼”的代名词。你给一个偏微分方程,它给你一套残差损失,训练完就往解析解上靠。但真正落到固体力学问题上,很多人第一步就会被卡住:方程该写成强形式还是弱形式?应力是从网络输出里直接求导拿,还是训练完再做后处理?边界条件怎么加才不会被大梯度振荡带崩?如果你正卡在某一个环节,这篇文章应该能帮你把整条链路理顺。

我下面分享的内容,核心是“强形式 PINN 求解固体力学问题”的完整拆解。所谓强形式,指的是直接用平衡微分方程、几何方程和本构方程构造残差,不去做加权残数法或虚功原理那套弱形式变换。这样做的直接好处是损失函数构建非常干净,想换本构、想加局部高精度约束都很方便。但代价也很明确:你需要对高阶导数、多目标损失权重的平衡、采样点分布等细节很敏感。所以这篇文章除了把原理讲清楚,还会把我在实际操作中踩过的坑、调试过的损失权重数值、以及一些很“体力活”但管用的检查手段都放进来,供真正要复现或改造成自己模型的读者参考。

1. 强形式低层逻辑:为什么固体力学适合直接用残差损失

1.1 先搞清楚网络里的“未知量”是什么

用 PINN 解固体力学问题,第一步不是选网络结构,而是想清楚“网络输出什么”。这个选择直接决定后面所有损失函数的形式。

大多数做法是让网络直接输出位移场。二维弹性问题里,网络的输入是坐标 (x, y),输出是位移分量 (u, v)。训练时的损失由三部分组成:平衡方程的残差、边界条件的残差,如果涉及动态问题还要加初始条件的残差。位移一旦被网络表示出来,应变可以靠自动微分获得,应力再靠本构关系计算,随后再回代到平衡方程里构造内部残差。这一套“位移驱动”的流程和有限元的思路最接近,也因此最容易对照验证。

也有人尝试让网络直接输出应力分量,因为应力解在塑性或接触问题中往往比位移更方便做约束。如果网络输出应力场,就需要额外满足应力平衡方程和本构方程的约束。麻烦在于:应力张量的对称性、各应力分量之间的微分相容条件,都需要额外处理。位移网络在这些方面通常更“安稳”,所以我个人的首选是位移输出。只有当目标问题本身就是应力反演、或者材料本构特别复杂时,才考虑应力网络。

1.2 强形式残差到底在“强迫”网络做什么

先看一个最基础的线弹性静力学问题。忽略体力时,平衡方程可以写为:

σ_ij,j = 0 在 Ω 内,即应力张量的散度为零。

位移—应变关系(几何方程):

ε_ij = (u_i,j + u_j,i) / 2

线弹性本构:

σ_ij = C_ijkl ε_kl

把这些方程全部变成残差形式:

  • 内部残差 R_balance = ∂σ_xx/∂x + ∂σ_xy/∂y(二维 x 方向,另一个方向同理);
  • 边界残差 R_dirichlet = u_pred - u_given(在边界上);
  • 边界残差 R_neumann = σ · n - t_given(自然边界上)。

然后构造总损失:

L = λ_int * ||R_balance||² + λ_dir * ||R_dirichlet||² + λ_neu * ||R_neumann||²

所有残差都做 L2 范数,在大量采样点上求和取平均。

这和有限元有本质区别:有限元把平衡方程变成弱形式,是在“平均意义”上让内力与外力平衡,网格分辨率决定精度;PINN 的强形式直接在采样点上强迫微分方程成立,本质是一个点态的约束。这带来一个好处:你不需要组装全局刚度矩阵,边界形状只要能用坐标函数描述就行,复杂几何的网格划分负担大幅降低。但代价也很直接——网络必须足够光滑,否则取不了高阶导数,而自动微分的导数阶数越高,梯度的数值病态越明显,这为后续调试埋下不少雷。

1.3 什么场景下强形式 PINN 才有真正优势

把话挑明:普通线弹性、简单边界、规则几何的问题,强形式 PINN 和成熟商业有限元软件毫无性价比可言。有限元的理论、收敛性、误差估计都完备,算一个悬臂梁可能连建模加求解十分钟搞定,而 PINN 可能要调半天损失权重。

强形式 PINN 真正值得投入的场景,我总结出三类:

  1. 正问题里几何形状参数化程度高。比如拓扑优化或几何反演,每改一次形状就要重新画网格是很大的成本,而 PINN 可以做几何参数与位移场的联合训练。
  2. 本构关系需要频繁试验。换一个本构方程,强形式 PINN 只需要修改残差函数里那几行应力计算代码,而有限元如果不是商业软件还得动单元子程序。
  3. 需要从稀疏测量数据反演材料参数或全场状态。PINN 天然可以把“已知点位的位移/应变测量值”加进损失项,形成半逆问题求解器。

另一个我在实践中发现的场景是老生常谈的“网格与形状割裂”:当计算域不是标准矩形、圆环这类规则区域时,有限元需要花大量时间做网格划分或网格加密,而 PINN 只需要在采样阶段把边界内的点筛出来,几何表达由坐标判断完成。虽然纯 PINN 对几何高精度表达仍有短板(比如尖锐角点附近的应力奇异需要很特殊处理),但相比几何建模困难的问题,它提供的是一种“少环节”的求解路径。

2. 实操前的关键细节:几何、坐标系与无量纲化

2.1 网络输出位移还是应力:选型判断

我刚开始尝试时,最先掉进的坑是“哪个张量好算就输出哪个”。后来发现应根据“哪个场上的先验约束比较容易表达”来决定。

输出位移场时,边界上给位移的 Dirichlet 条件很好写:直接把边界采样点上的预测位移减去给定位移,取范数即可。输出应力场时,力的边界条件好写,但位移边界条件和应变相容条件都需要额外加微分约束,编程和调权重的复杂度成倍增加。所以除非目标问题本身就是应力反演(已知部分应力测量值),否则我不建议直接输出应力。经验原则:先描述边界约束,再决定输出量,而不是反过来。

2.2 采样点的生成:内部点和边界点的相互独立

PINN 在训练时依赖大量离散点上的残差评估。这些点怎么布,直接影响收敛质量。

对于常见几何(矩形、圆、圆环、多边形围成的域),我的做法是:

  1. 先在包围盒内生成均匀分布的候选点,例如矩形域 [0, Lx] × [0, Ly],直接用 numpy 随机撒 N 个点;
  2. 如果域有开孔或复杂边界,用 SDF(符号距离函数)判断点是否在域内。SDF < 0 表示在材料内部,SDF > 0 表示在外部,|SDF| 小于某个小阈值视为边界点。这一招可以避免逐点做“点是否在多边形内”的几何判断,效率高不少;
  3. 边界点非常重要。不要在内部点和边界点之间搞“共享”,而是单独在每条边界上按弧长参数化采样。曲线边界上用角度参数 θ 均匀采样,直线边界上用线性坐标均匀采样;
  4. 内部点要随机化。固定网格点虽然也能训练,但随机采样在训练中可以用不同批次的点,等效于数据的随机增强,能抑制网络某些区域的过拟合。

还有一个我后来才注意到的细节:强形式残差里的导数项在高应变梯度区域(比如应力集中区)误差最大。你对全场均匀采样时,这些区域占的损失比重太低,网络“看不见”它们。这时需要做一次先验分析,比如线弹性下角点附近应力会剧烈变化,就可以在角点附近加密采样点。加密的方式最简单的是:在角点邻域内按指数分布追加一批采样点。这些点个数不需要多,几百个就足够让损失曲线有明显变化。

2.3 无量纲化:不做这一步,训练八成要崩

这一点我必须单独拎出来强调。我在试一个长 1 m、截面 0.1 m 的悬臂梁时,直接采用国际单位制,训练损失一直在一个较高的平台上慢慢悠悠地降,降到一定程度就不动了。后来检查发现,问题是网络输入 (x, y) 数值范围从 0 到 1 变化,但输出位移却小到毫米甚至微米量级——网络要用有限权重拟合出数值差好几个数量级的映射关系,梯度优化的收敛速度会变得极慢。

标准办法是参考无量纲化:把长度统一除以参考长度,比如梁长 Lref;把位移除以参考位移,把应力除以参考应力,把弹性模量也用参考值归一。最终让输入坐标和输出未知量都落在 O(0.1) 到 O(10) 的范围内。平安经验:网络输入尽量归一化到 [-1, 1],输出量尽量归一化到 [0, 1] 或 [-1, 1]。如果你嫌麻烦,至少也要保证坐标的尺度是“同一个尺度”的,否则等方向几何在损失计算里的各向导数严重不平衡。

无量纲化背后的原因要理解:激活函数 tanh 在输入接近 0 时有近似线性区,输入太大时梯度饱和。如果网络输入 x 范围是 [0, 1],y 范围是 [0, 0.1],那么在面内两个方向求导时,y 方向的导数天然会比 x 方向“看到”更陡的局部变化,梯度更新的平衡就会被打破。无量纲化相当于给不同方向的导数项做了预处理缩放,让网络的重心放在“物理量变化”而不是“坐标量纲”上。

3. 核心实现:网络结构、损失函数与训练配置

3.1 一个可以直接启动的最小骨架

下面这个最小示例用 PyTorch 实现一个二维线性弹性板问题。域为 [0, Lx] × [0, Ly],左边界固定,右边界受均布拉力。这里省略了部分边界采样细节,但核心损失结构是完整的。基于常见实践的思路,网络采用三层 tanh 隐含层,每层 128 神经元;输入是 (x, y),输出是位移 (u, v)。内部残差和边界残差的构造如下。

import torch import torch.nn as nn class MLP(nn.Module): def __init__(self): super().__init__() self.net = nn.Sequential( nn.Linear(2, 128), nn.Tanh(), nn.Linear(128, 128), nn.Tanh(), nn.Linear(128, 128), nn.Tanh(), nn.Linear(128, 2) ) def forward(self, xy): return self.net(xy) def compute_loss(model, pts_int, pts_dir, pts_neu, E, nu): # pts_int: [N_int, 2] # pts_dir: 左边界固定点 # pts_neu: 右边界受拉点 x = pts_int[:, 0:1]; y = pts_int[:, 1:2] pts = torch.cat([x, y], dim=1) pts.requires_grad_(True) uvp = model(pts) u = uvp[:, 0:1]; v = uvp[:, 1:2] def grad(u, x): return torch.autograd.grad(u, x, grad_outputs=torch.ones_like(u), create_graph=True)[0] dux = grad(u, x); duy = grad(u, y) dvx = grad(v, x); dvy = grad(v, y) exx = dux eyy = dvy exy = 0.5 * (duy + dvx) # 平面应力本构(厚度方向自由) C11 = E / (1 - nu**2) C12 = nu * C11 C44 = E / (2 * (1 + nu)) sxx = C11 * exx + C12 * eyy syy = C12 * exx + C11 * eyy sxy = 2 * C44 * exy # 平衡残差(忽略体力) dsxx_dx = grad(sxx, x) dsxy_dy = grad(sxy, y) dsyy_dy = grad(syy, y) dsxy_dx = grad(sxy, x) r_x = dsxx_dx + dsxy_dy r_y = dsxy_dx + dsyy_dy loss_int = torch.mean(r_x**2 + r_y**2) # 左边界 Dirichlet: u=v=0 u_dir = model(pts_dir)[:, 0:1] v_dir = model(pts_dir)[:, 1:2] loss_dir = torch.mean(u_dir**2 + v_dir**2) # 右边界 Neumann: T = sigma_x = p, sigma_xy = 0 u_neu = model(pts_neu)[:, 0:1] v_neu = model(pts_neu)[:, 1:2] x_neu = pts_neu[:, 0:1].requires_grad_(True) y_neu = pts_neu[:, 1:2].requires_grad_(True) ptsn = torch.cat([x_neu, y_neu], dim=1) u_neu = model(ptsn)[:, 0:1] du_neu_x = grad(u_neu, x_neu) dv_neu_x = grad(v_neu, x_neu) exx_n = du_neu_x sxx_n = C11 * exx_n + C12 * (dv_neu_x * 0) # 这里只示意取exx项 # 完整应力分量计算略,思路是计算 sxx_n - p 与 sxy_n 的残差 loss_neu = torch.mean((sxx_n - p)**2) loss = 1.0 * loss_int + 1.0 * loss_dir + 1.0 * loss_neu return loss

这段代码只画了核心逻辑。真正要跑通,还需要单独写好边界点生成函数。我记得第一次跑时,把右边界应力残差的符号写反了,结果网络的解像一个“被拉爆”的曲线,位移场往加载反方向大变形。后来在损失函数里加了应力分量的方向检验,很快定位到是 sxx_n - p 的正负号问题。这个教训也说明:不要只盯总损失数值,还要实时输出边界的局部残差分量。

3.2 激活函数与网络初始化:为什么不是“随便选一个”

在 PINN 社区里,激活函数选择已经积累了不少经验。我自己的观察是:

  • tanh:输出范围 [-1, 1],光滑,导数适中,最适合做二阶微分方程物理场的默认选择。缺点是深层网络容易饱和,所以初始化和归一化要更认真。
  • swish / silu:在某些测试里收敛更快,但从一个解到另一个解的振荡也更明显,物理残差训练中对学习率的敏感度更高。
  • sin:对频域特征捕捉有一定优势,在某些波动问题里效果好。但 sin 激活函数的网络初始化范围很挑剔,一个不合适的缩放会让高频振荡的梯度异常大,训练极不稳定。

经验层面的排序是:默认 tanh,波动问题试 sin,如果训练速度太慢再试 swish。初始化方面,我做了一个小实验:统一采用 Xavier 初始化,训练能稳定跑到一个精度平台;改为随机较大的权重初始化后,损失前期波动明显加剧。对 tanh 网络,Xavier 或 He 初始化都可行,但 X avier 在小规模网络上更平滑。

还有一个必须提的细节:网络的输出层不要加激活函数。位移场理论上可正可负,输出层若带 tanh 会把位移限制在 [-1, 1],如果真实位移超出这个范围,网络永远无法表达。所以输出层就是纯粹的线性层。

3.3 损失权重:它不是超参数,而是“身体状态报告”

强形式 PINN 的损失由内部残差、Dirichlet 边界、Neumann 边界三块组成。多数文章喜欢一开始就手调权重,但我建议先全部设成 1,观察训练中各项的量级和下降趋势,再针对性调整。

原因很简单:网络初始阶段内部残差的量级往往比边界残差小很多。因为网络输出接近零时,导数项也接近零,内部残差天然很小。此时如果不加权重,边界损失占主导,网络会优先满足边界,内部方程却长期欠拟。一个常见调法是:让边界损失稍微高一点,比如 5 到 20,保证边界先“被看见”;当训练进入中期后,再把内部权重加大,逼迫网络向内满足方程。

但这不表示边界权重越高越好。有一次我把边界权重调到 100,发现训练确实快速满足边界位移,但应力场出现明显的振荡,因为网络为了压低边界损失,在边界附近生成了很大的曲率补偿,而内部残差又反过来被拉高。这种多目标博弈没有绝对最优解。我在实践中会用一段 30 行左右的脚本,每隔若干步打印各损失的数值,用它们的比值作为调节依据。例如若 loss_int 比 loss_dir 小两个数量级,那多半不是网络已经学得很好,而是内部残差权重太小,网络根本不关心方程。

3.4 训练过程可视化:不要只看 loss 那条曲线

PINN 训练时,总损失曲线看起来平稳下降,其实未必代表解是正确的。因为各分项损失可能互相抵消,比如内部残差减小但边界残差增大,总损失曲线看上去还是往下走。

我的习惯是每 500 步把以下信息打印一次:

  • 内部残差、Dirichlet 残差、Neumann 残差分别的数值;
  • 在固定验证点上,预测位移与一个粗网格参考解或解析解的相对误差;
  • 应力场云图(如果能快速画出来的话)。

这个习惯在诊断“看似收敛实则无解”时极其重要。有一回损失已经降到 1e-4,但可视化发现位移场的等值线在角落处严重扭曲,原因是边界点采样数量太少,网络把不满足边界条件的区域“平均”掉了。加上边界点密度后,残差才继续下降。PINN 这件事,永远要警惕总损失这个“平均数”骗人。

4. 常见问题与排查技巧实录

4.1 训练不收敛:先分清单点还是全场问题

遇到“loss 降不下去”的局面,我有几个固定排查步骤:

  1. 先不训练,直接把模型初始化后跑一次前向,看看各残差值是否是合理的初始量级。如果 Dirichlet 残差已经非常小,说明网络输出在边界区域竟然自动满足了边界条件,这多半是权重或符号写错了。
  2. 检查坐标是否 requires_grad。如果坐标没有设置 requires_grad=True 或没有用 create_graph=True,高阶导数会是零,内部残差项全是 0。
  3. 检查网络输出层有没有误加激活。如果输出层是 tanh,位移被限制在 [-1, 1],遇到大变形问题会卡在边界上。
  4. 切小网络试跑。128×3 的网络在二维线弹性问题上通常足够,如果切到 32×2 之后反而表现更稳定,那说明原网络参数量过大且训练样本点数不足,拟合产生了震荡。

这里要特别强调 create_graph 的重要性:第一次写 autograd 时,如果内部残差里还要继续对位移求导,必须打开 create_graph=True,否则第二次导数全是 0,但报错不一定明显。

4.2 应力场噪声大:导数阶次越高的地方越敏感

强形式求解里最让人头疼的就是应力场不够光滑。原因很直观:网络输出位移本身拟合得不错,但应力由位移的导数计算而来。一次导数可以接受,如果应变还要继续求导进入平衡方程,二阶导数的误差就被放大,等值线上会出现明显的局部毛刺。

一个非常实用的小技巧是:不要用网络直接输出应力做后处理,而是训练完成后,另外在一个更密的网格上重新求一次位移的导数。这个流程与训练时的计算没有冲突,只是多了点计算成本。另外,可以适度增加内部采样点密度,特别是梯度大的区域。

如果应力噪声仍然严重,还有一个办法是在损失函数里增加对高阶导数的惩罚或平滑项。不过这会引入额外的调参负担。我通常是在“确保解正确”的前提下,用后处理平滑或增加采样点来改善应力云图,而不是过早加正则化项——正则化项若加不好,会把位移场“磨”得过度光滑,应力反而失真。

4.3 边界条件被“淹没”:多目标损失的博弈解决

强形式 PINN 的边界条件不是硬约束,而是软约束。这意味着只要边界损失不够大,网络就可以“牺牲”边界来降低内部残差。一个典型表现是:位移边界上的预测值不是严格等于给定值,而是存在一个小的偏差。

解决思路有两种:

  1. 加边界损失权重。简单直接,但需要反复调;
  2. 构造满足边界条件的特解形式。比如 Dirichlet 边界 u=0 时,在网络输出前面乘一个距离函数 g(x),使 u = g(x) * N(x),g 在边界上为 0。这样边界条件被严格满足,内部残差才是唯一需要优化的对象。这个做法在规则几何里非常好使。

我实际使用中,第二种方法省心很多。比如悬臂梁左边界固定时,可以用 x 坐标的某种函数构造 g,让左边界强制满足 u = v = 0。代价是乘上 g 后,网络需要学习的函数空间发生改变,某些情况下收敛速度反而变慢。所以在“快速验证一个想法”时,我会先用软约束;在“要一个稳定可靠的结果”时,再换成硬约束。

4.4 关键问题速查表

现象可能原因排查手段
各残差初始量级正常但总损失不降学习率过大或过小观察前几十步的变化,尝试 lr=1e-3 到 1e-4
位移场正确但应力云图毛刺多训练阶段应力残差权重太低增大内部残差权重、增加采样点
边界点位移准确但内部位移偏差大内部点太少或分布不均在梯度大的区域加密采样
损失下降很快但最终精度差权重不均衡,边界过度主导打印各分项损失并按比值重调权重
高频振荡激活函数选择或初始化问题换 tanh,使用 Xavier 初始化
几何含孔洞,边界约束总不对SDF 符号判断出错单测 SDF 在边界附近的取值

5. 从静力学向更复杂场景扩展

5.1 动态问题:加上时间维度的注意事项

把强形式 PINN 从静态推广到动态并不困难,网络输入从 (x, y) 变成 (x, y, t),输出仍可以是位移。损失函数里除了平衡方程和边界条件,还需要额外加入 t=0 时刻的位移和速度条件。

动态问题的额外难点在于:时间维度上的采样同样会影响收敛质量。我建议在时间上采用“先粗后密”的调度:前期时间点数量少一些,让网络先学会空间模式,再逐步增加时间点,优化出时间演化。这种课程式训练在波动传播类问题里尤其必要,因为早期如果时间点数太多,网络会试图同时拟合所有时刻,空间特征反而学不动。

另外,动态问题中的应力波传播会有明显的时域高频特征。如果只用 tanh 激活,可能需要更深的网络和高频坐标嵌入,比如将坐标和时间通过 Fourier 特征映射升维。这个做法在波动类问题中提升明显,但要注意嵌入频率的尺度要参考问题的特征波长。

5.2 非线性材料与大变形

强形式 PINN 的一个优势在这里就体现出来了。本构直接写成代码函数,不需要像有限元那样开发新的单元子程序。

以超弹性材料为例,如果采用应变能密度函数 W(ε),那么应力可以写为:

σ = ∂W / ∂ε

在损失函数里只需定义 W 并对应变求导。由于自动微分会自动链式求导,理论上可以支持任意复杂的光滑本构。但要注意:当本构关系包含不光滑点(比如弹塑性加载卸载切换、接触边界摩擦切换)时,神经网络的光滑输出很难精确表达这种非光滑行为,残差会在切换点附近剧烈振荡。这时通常需要引入额外的“塑性乘子”或“内变量”网络,并用增量形式处理,难度会明显上升。

5.3 从正问题到反问题:参数反演

PINN 解决反问题的逻辑很自然:把材料参数(比如弹性模量 E 或者泊松比 ν)设为可训练变量,随网络一起优化。损失函数中除了原有方程残差外,再加上“预测位移与实测位移”之间的误差。这个思路在实验力学中相当有吸引力,因为实验中往往只能测到表面或个别点的位移,而无法获得全场应力。PINN 可以用稀疏测量数据驱动,反推出全场位移、应力和材料参数。

反问题中最常见的陷阱是参数不可辨识。例如只有单轴拉伸的均匀应变数据时,E 和 ν 无法从单一工况中同时确定,因为应力与应变的关系可以等价抵消。我的建议是:做反演前,先做一次简单的可辨识性分析,看损失函数关于参数的导数在最优参数附近是否为零方向较多。如果存在平坦方向,要么增加更多实验工况,要么固定其中一个参数。

5.4 多物理场耦合:换个损失组合而已

固体力学与传热、扩散、渗流耦合时,PINN 的做法仍然是把各子系统的控制方程残差加权求和。比起有限元的耦合弱形式,强形式的 PINN 在损失组装上更直接:把热传导方程的残差加进去,把热膨胀项加进本构方程,再把边界上的热通量条件加进边界损失即可。

但耦合会带来新的权重问题:不同物理场残差的量级可能相差甚远,比如应力以 MPa 量级计数,温度以 ℃ 量级计数,直接把两者残差加起来,数值大的那个会主导训练。所以我建议在构造耦合问题之前,先对每个子系统的物理量做一次无量纲化和标准化,让它们都落在相似的量纲尺度内。这不是锦上添花,而是能否训练达到目标精度的必要条件。

写在最后的实际操作体会

如果你问我“强形式 PINN 解固体力学到底推不推荐”,我的回答是:它不是一个“替代有限元”的通用工具,而是一类更适合“自定义问题与反问题”的求解手段。它最大的价值不在替代,而在补位:当你要在一个需要反复修改几何、本构、参数、边界条件的场景里快速验证想法时,PINN 的代码改动成本非常低。

我建议刚起步的读者不要一上来就挑战三维、弹塑性、非线性几何,而是先用一个二维悬臂梁或平板拉伸问题,把损失结构、权重调节、后处理验证这一整套流程跑通。等这套机制熟练了,再去碰更复杂的场景。做这件事时,尽量保留一份“参考解”用来对照:可以是商业软件的解、解析解或一个非常细网格的有限元结果。没有参考解做锚点,你根本不知道 PINN 的预测是越来越对,还是越来越“自洽地错”。这一点,是用 PINN 做力学仿真和用普通深度学习做图像识别,最大的认知差异。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询