PINN 与 HNN

数字脊髓(Spine 层)的核心挑战:如何在毫秒级实时控制中,既利用神经网络的非线性拟合能力,又保证物理定律的一致性?

纯数据驱动的黑盒神经网络有三个致命缺陷:OOD 物理幻觉、数据效率低、缺乏可证明的安全边界。EICPS 的答案是灰盒建模范式(Grey-box Modeling)

将物理先验(微分方程、守恒律)嵌入神经网络的损失函数结构,而非仅作为数据标签。

PINN 与 HNN:灰盒建模范式

PINN:物理信息神经网络

架构流:输入 (x,u)(x, u) 送入 NN 主干(128→128→n),预测状态导数 x˙^\hat{\dot{x}};物理残差 Lphys\mathcal{L}_{phys} 从输出侧反馈回主干,构成约束闭环。配置点 {(xi,ti)}\{(x_i, t_i)\} 无需对应实际观测数据——这是 PINN 与纯监督学习的根本区别。

损失函数

LPINN=Ldata数据拟合+λLphys物理残差\mathcal{L}_{PINN} = \underbrace{\mathcal{L}_{data}}_{\text{数据拟合}} + \lambda \cdot \underbrace{\mathcal{L}_{phys}}_{\text{物理残差}}

Lphys=1Nci=1NcN[uθ(xi,ti)]f(xi,ti)2\mathcal{L}_{phys} = \frac{1}{N_c} \sum_{i=1}^{N_c} \left\| \mathcal{N}[u_\theta(x_i, t_i)] - f(x_i, t_i) \right\|^2

在 EICPS 脊髓层的应用

对于机器人动力学 M(q)q¨+C(q,q˙)q˙+g(q)=τM(q)\ddot{q} + C(q,\dot{q})\dot{q} + g(q) = \tau,物理残差具体化为:

Lphys=Mθ(q)q¨+Cθ(q,q˙)q˙+gθ(q)τ2\mathcal{L}_{phys} = \left\| M_\theta(q)\ddot{q} + C_\theta(q,\dot{q})\dot{q} + g_\theta(q) - \tau \right\|^2

保证与局限

说明
✅ OOD 不崩溃物理方程约束兜底,远离训练分布时输出仍收敛到已知方程解
✅ 方程一致性训练后满足 Lphys<ε\mathcal{L}_{phys} < \varepsilon,对应定律②
❌ 不保证能量守恒软约束(损失惩罚)无法阻止长时数值积分漂移

典型应用场景:湍流 / 摩擦建模、接触动力学、复杂非线性系统。


HNN:哈密顿神经网络

架构流:输入 z=(q,p)z = (q, p)(广义坐标 + 共轭动量)→ HθH_\theta 网络(2n→128→1,输出标量哈密顿量)→ autograd 计算 zHθ\nabla_z H_\theta → 乘辛矩阵 JJ → 输出 z˙=JHθ(z)\dot{z} = J\nabla H_\theta(z)。整条链中神经网络只学标量,辛结构由架构强制,而非损失惩罚。

哈密顿方程描述相空间辛流:

q˙=Hp,p˙=Hqz˙=JH(z),J=(0II0)\dot{q} = \frac{\partial H}{\partial p}, \qquad \dot{p} = -\frac{\partial H}{\partial q} \qquad \Leftrightarrow \qquad \dot{z} = J \nabla H(z), \quad J = \begin{pmatrix} 0 & I \\ -I & 0 \end{pmatrix}

损失函数(仅需数据拟合,辛结构隐式保证):

LHNN=JHθ(z)z˙obs2\mathcal{L}_{HNN} = \left\| J \nabla H_\theta(z) - \dot{z}_{obs} \right\|^2

保证与局限

说明
✅ 能量守恒dHdt=HJH=0\frac{dH}{dt} = \nabla H \cdot J\nabla H = 0,硬约束,对应定律①
✅ Liouville 定理相空间轨迹保持辛形式 ω=dqdp\omega = dq \wedge dp 不变
❌ 不建模耗散/外力标准 HNN 仅适用保守系统;耗散场景需扩展为 Port-Hamiltonian

典型应用场景:振荡 / 旋转运动、长时稳定性保证、能量健康监测。


灰盒协同:Spine Controller

PINN 遵循流形(切空间向量场)↔ HNN 守护能量(辛梯度向量场)——两者互补,共同构成 EICPS 脊髓层的灰盒控制器:

PINNHNN
约束类型微分方程残差(软约束)辛结构守恒(硬约束)
适用场景湍流、摩擦、接触动力学振荡、旋转、长时稳定性
OOD 行为收敛到已知方程解轨迹保持辛流形上
定律关联定律②方程一致性 + 定律③数据准确性定律①守恒律优先

两者在 Spine Controller 合流,以 1kHz 实时运行:

PINN.predict_next(x, u) — 预测 x˙^\hat{\dot{x}},Euler 积分得下一状态,学习切空间向量场,驱动参考轨迹生成。

HNN.check_energy(z) — 实时计算 HHdHdt\frac{dH}{dt};若 dHdt>εwarn\left|\frac{dH}{dt}\right| > \varepsilon_{warn},立即触发安全反射弧,防止数值积分发散导致的机械失控。


物理 AI 三定律

EICPS 脊髓层的优先级从高到低:

优先级定律保证者违反后果
① 最高守恒律优先dHdt0\frac{dH}{dt} \le 0(耗散)或 =0= 0(保守)HNN 架构强制系统发散(电机过热、机械臂失控)
方程一致性Lphys<ε\mathcal{L}_{phys} < \varepsilonPINN 训练保证物理幻觉(OOD 区域产生不可行命令)
③ 最低数据准确性:最小化 Ldata\mathcal{L}_{data}NN 拟合仅模型精度下降,可数据增广改善

定律优先级决定了回退顺序:能量守恒失效时,无论轨迹多准确都必须触发停机;方程一致性失效时,即使数据拟合精度高也不可信任控制输出。


算法实现

import numpy as np
import torch
import torch.nn as nn
from torch.autograd import grad

# ── PINN:物理信息神经网络 ────────────────────────────────────

class PINNDynamicsModel(nn.Module):
    """
    PINN 动力学模型:学习 ẋ = F(x, u) + physics_residual
    物理残差约束保证 OOD 安全性
    """

    def __init__(self, n_state: int, n_control: int, hidden: int = 128):
        super().__init__()
        self.n_state   = n_state
        self.n_control = n_control

        # 神经网络主干:拟合未建模动力学
        self.net = nn.Sequential(
            nn.Linear(n_state + n_control, hidden),
            nn.Tanh(),
            nn.Linear(hidden, hidden),
            nn.Tanh(),
            nn.Linear(hidden, n_state),
        )

        # 已知物理先验(可选:惯量矩阵等)
        self.M_inv = nn.Parameter(torch.eye(n_state // 2) * 0.1,
                                   requires_grad=True)

    def forward(self, x: torch.Tensor, u: torch.Tensor) -> torch.Tensor:
        """预测状态导数 ẋ"""
        xu = torch.cat([x, u], dim=-1)
        return self.net(xu)

    def physics_residual(self, x: torch.Tensor, u: torch.Tensor,
                          x_dot_obs: torch.Tensor) -> torch.Tensor:
        """物理残差损失 L_phys:M(q)q̈ + C(q,q̇)q̇ + g(q) = τ"""
        n = self.n_state // 2
        ddq_pred = self.forward(x, u)[:, n:]
        residual = (self.M_inv @ ddq_pred.T).T - u[:, :n]
        return (residual ** 2).mean()

    def total_loss(self, x: torch.Tensor, u: torch.Tensor,
                   x_dot_obs: torch.Tensor, lam: float = 1.0) -> torch.Tensor:
        """L_total = L_data + λ · L_phys"""
        L_data = nn.functional.mse_loss(self.forward(x, u), x_dot_obs)
        L_phys = self.physics_residual(x, u, x_dot_obs)
        return L_data + lam * L_phys


# ── HNN:哈密顿神经网络 ───────────────────────────────────────

class HamiltonianNN(nn.Module):
    """
    HNN:强制辛结构守恒
    架构链:z=(q,p) → H_θ 网络 → autograd → ∇H → ×J → ż = J∇H
    """

    def __init__(self, n_dof: int, hidden: int = 128):
        super().__init__()
        self.n = n_dof

        # H(z): R^{2n} → R(标量哈密顿量)
        self.H_net = nn.Sequential(
            nn.Linear(2 * n_dof, hidden),
            nn.Tanh(),
            nn.Linear(hidden, hidden),
            nn.Tanh(),
            nn.Linear(hidden, 1),
        )

    def hamiltonian(self, z: torch.Tensor) -> torch.Tensor:
        return self.H_net(z).squeeze(-1)

    def forward(self, z: torch.Tensor) -> torch.Tensor:
        """辛梯度动力学:ż = J∇H(自动微分实现)"""
        z = z.requires_grad_(True)
        H_sum = self.hamiltonian(z).sum()
        grad_H = grad(H_sum, z, create_graph=True)[0]
        n = self.n
        dq =  grad_H[:, n:]   # ∂H/∂p = q̇
        dp = -grad_H[:, :n]   # -∂H/∂q = ṗ
        return torch.cat([dq, dp], dim=-1)

    def energy_rate(self, z: torch.Tensor) -> torch.Tensor:
        """计算 dH/dt(用于健康监测,应趋近于零)"""
        z_dot = self.forward(z)
        z.requires_grad_(True)
        H_sum = self.hamiltonian(z).sum()
        grad_H = grad(H_sum, z, create_graph=True)[0]
        return (grad_H * z_dot).sum(dim=-1)


# ── 灰盒脊髓控制器:PINN + HNN 合流 ─────────────────────────

class SpineController:
    """
    EICPS 脊髓层:1kHz 实时灰盒控制器
    PINN 预测下一状态;HNN 监测能量健康,超阈触发安全反射弧
    """

    def __init__(self, pinn: PINNDynamicsModel, hnn: HamiltonianNN,
                 energy_warn: float = 0.1):
        self.pinn = pinn
        self.hnn  = hnn
        self.energy_warn = energy_warn

    @torch.no_grad()
    def predict_next(self, x: np.ndarray, u: np.ndarray,
                     dt: float = 0.001) -> np.ndarray:
        """PINN 单步预测(切空间向量场 → Euler 积分)"""
        x_t = torch.tensor(x, dtype=torch.float32).unsqueeze(0)
        u_t = torch.tensor(u, dtype=torch.float32).unsqueeze(0)
        x_dot = self.pinn(x_t, u_t).squeeze(0).numpy()
        return x + dt * x_dot

    def check_energy(self, z: np.ndarray) -> dict:
        """HNN 能量健康监测:|dH/dt| > ε_warn → 触发安全反射弧"""
        z_t = torch.tensor(z, dtype=torch.float32).unsqueeze(0)
        H    = self.hnn.hamiltonian(z_t).item()
        dHdt = self.hnn.energy_rate(z_t).item()
        safe = abs(dHdt) <= self.energy_warn
        return {
            "H": H,
            "dH_dt": dHdt,
            "safe": safe,
            "action": "nominal" if safe else "safety_reflex",
        }

延伸阅读