# Evaluate PINN
t_test = torch.linspace(t_start, t_end, 500, device=device).view(-1, 1)
t_test_norm = normalise_t(t_test)
with torch.no_grad():
thetas_pred_norm = pinn(t_test_norm)
thetas_pred = denormalise_theta(thetas_pred_norm)
t_plot = t_test[:, 0].detach().cpu().numpy()
th1_pinn = thetas_pred[:, 0].detach().cpu().numpy()
th2_pinn = thetas_pred[:, 1].detach().cpu().numpy()
# ----------------------------------------------------------------------------
# Plot 1: Displacement — both arms
fig, axes = plt.subplots(
2, 1,
figsize=(9, 10))
axes[0].plot(
t_plot,
th1_pinn,
color='black',
linewidth=2,
label=r'PINN $\hat\theta_1$')
axes[0].plot(
t,
theta1,
color='blue',
linestyle='--',
alpha=0.7,
label=r'Numerical $\theta_1$')
axes[0].scatter(
t_exp_np,
theta1_exp_np,
s=15,
color='blue',
alpha=0.8,
label='Exp Data')
axes[0].set_xlabel(
'Time (s)',
fontsize=12)
axes[0].set_ylabel(
'Angle (rad)',
fontsize=12)
axes[0].set_title(
r'Top Pendulum Dynamics ($\theta_1$)',
fontsize=13)
axes[0].legend(fontsize=10)
axes[0].grid(True, alpha=0.3)
axes[1].plot(
t_plot,
th2_pinn,
color='black',
linewidth=2,
label=r'PINN $\hat\theta_2$')
axes[1].plot(
t,
theta2,
color='red',
linestyle='--',
alpha=0.7,
label=r'Numerical $\theta_2$')
axes[1].scatter(
t_exp_np,
theta2_exp_np,
s=15,
color='red',
alpha=0.8,
label='Exp Data')
axes[1].set_xlabel(
'Time (s)', fontsize=12)
axes[1].set_ylabel('Angle (rad)', fontsize=12)
axes[1].set_title(r'Bottom Pendulum Dynamics ($\theta_2$)', fontsize=13)
axes[1].legend(fontsize=10); axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# ----------------------------------------------------------------------------
# Plot 2: Training loss
fig, ax = plt.subplots(figsize=(9, 5))
ax.plot(
total_loss_history,
marker='o',
markersize=2,
color='black',
linewidth=3,
label="Total Loss")
ax.plot(
ic_loss_history,
color='orange',
linewidth=0.8,
label="IC Loss")
ax.plot(
physics_loss_history,
color='red',
linewidth=1.2,
alpha=0.8,
label="Physics Loss")
ax.plot(
data_loss_history,
color='blue',
linewidth=1.2,
alpha=0.8,
label="Data Loss")
for ep in [5000, 10000, 15000]:
plt.axvline(
x=ep,
color='orange',
linestyle='--',
alpha=0.5)
ax.axvline(
x=5000,
color='orange',
linestyle='--',
alpha=0.5,
label='LR reductions')
ax.set_yscale('log')
ax.set_xlabel('Epoch', fontsize=12)
ax.set_ylabel('Total Loss (log scale)', fontsize=12)
ax.set_title('2-DOF Hybrid PINN Training Loss', fontsize=13)
ax.legend(fontsize=11)
ax.grid(True, which='both', linestyle='-', alpha=0.2)
plt.tight_layout()
plt.show()