![image]()
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from matplotlib.patches import Circle, FancyArrowPatch
# ====================== 1. 系统参数 ======================
k = 1.0
m = 1.0
omega1 = np.sqrt(k / m)
omega2 = np.sqrt(3 * k / m)
tau_full = np.linspace(0, 12, 1000)
t_full = tau_full / omega1
# 各阶模态位移
u1_mode1 = 0.5 * np.cos(omega1 * t_full)
u2_mode1 = 0.5 * np.cos(omega1 * t_full)
u1_mode2 = 0.5 * np.cos(omega2 * t_full)
u2_mode2 = -0.5 * np.cos(omega2 * t_full)
u1_total = u1_mode1 + u1_mode2
u2_total = u2_mode1 + u2_mode2
# ====================== 2. 画布布局:2行,上2列时程,下3列物理动画 ======================
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
fig = plt.figure(figsize=(18, 9))
gs = fig.add_gridspec(2, 3, height_ratios=[2, 1])
# 上排:u1、u2时程曲线
ax1 = fig.add_subplot(gs[0, 0])
ax2 = fig.add_subplot(gs[0, 1])
# 下排三个物理模型:一阶模态、二阶模态、总振动
ax_mod1 = fig.add_subplot(gs[1, 0])
ax_mod2 = fig.add_subplot(gs[1, 1])
ax_total = fig.add_subplot(gs[1, 2])
# 初始化时程曲线
l1_tot, = ax1.plot([], [], 'k-', lw=2, label='自由振动')
l1_m1, = ax1.plot([], [], 'r-', lw=1, label='一阶模态')
l1_m2, = ax1.plot([], [], 'b--', lw=1.2, label='二阶模态')
l2_tot, = ax2.plot([], [], 'k-', lw=2, label='自由振动')
l2_m1, = ax2.plot([], [], 'r-', lw=1, label='一阶模态')
l2_m2, = ax2.plot([], [], 'b--', lw=1.2, label='二阶模态')
# 时程坐标轴设置
ax1.set_xlim(0, 12)
ax1.set_ylim(-1, 1)
ax1.set_xlabel(r'$\omega_1 t$')
ax1.set_ylabel(r'$u_1$')
ax1.grid(alpha=0.3)
ax1.legend()
ax2.set_xlim(0, 12)
ax2.set_ylim(-1, 1)
ax2.set_xlabel(r'$\omega_1 t$')
ax2.set_ylabel(r'$u_2$')
ax2.grid(alpha=0.3)
ax2.legend()
# 封装创建双质量三弹簧两端固定模型函数
def create_spring_mass_ax(ax, title):
ax.set_xlim(-3, 9)
ax.set_ylim(-1, 1)
ax.set_aspect('equal')
ax.set_title(title)
ax.axis('off')
# 左右固定墙壁
wall_left = plt.Rectangle((-2, -0.5), 1, 1, color='gray')
wall_right = plt.Rectangle((7, -0.5), 1, 1, color='gray')
# 两个质量块初始位置
m1 = plt.Rectangle((1, -0.4), 0.8, 0.8, color='darkblue')
m2 = plt.Rectangle((3, -0.4), 0.8, 0.8, color='darkred')
# 三根弹簧:左墙-m1、m1-m2、m2-右墙
spring_left, = ax.plot([], [], 'k-', lw=1.5)
spring_mid, = ax.plot([], [], 'k-', lw=1.5)
spring_right, = ax.plot([], [], 'k-', lw=1.5)
ax.add_patch(wall_left)
ax.add_patch(wall_right)
ax.add_patch(m1)
ax.add_patch(m2)
return m1, m2, spring_left, spring_mid, spring_right
# 三个子图模型对象
m1_mod1, m2_mod1, sp_left1, sp_mid1, sp_right1 = create_spring_mass_ax(ax_mod1, "第一阶模态(同相振型)")
m1_mod2, m2_mod2, sp_left2, sp_mid2, sp_right2 = create_spring_mass_ax(ax_mod2, "第二阶模态(反相振型)")
m1_tot, m2_tot, sp_left_t, sp_mid_t, sp_right_t = create_spring_mass_ax(ax_total, "模态叠加总振动")
# 弹簧绘制工具函数
def draw_spring(x_start, x_end):
n = 50
x = np.linspace(x_start, x_end, n)
y = 0.2 * np.sin(np.linspace(0, 10 * np.pi, n))
return x, y
# ====================== 3. 动画更新函数 ======================
def update(frame):
# 更新时程曲线
tau_data = tau_full[:frame]
l1_tot.set_data(tau_data, u1_total[:frame])
l1_m1.set_data(tau_data, u1_mode1[:frame])
l1_m2.set_data(tau_data, u1_mode2[:frame])
l2_tot.set_data(tau_data, u2_total[:frame])
l2_m1.set_data(tau_data, u2_mode1[:frame])
l2_m2.set_data(tau_data, u2_mode2[:frame])
# ========== 一阶模态 ==========
um1_1 = u1_mode1[frame]
um2_1 = u2_mode1[frame]
pos_m1 = 1 + um1_1
pos_m2 = 3 + um2_1
m1_mod1.set_x(pos_m1)
m2_mod1.set_x(pos_m2)
sp_left1.set_data(*draw_spring(-1, pos_m1))
sp_mid1.set_data(*draw_spring(pos_m1 + 0.8, pos_m2))
sp_right1.set_data(*draw_spring(pos_m2 + 0.8, 7))
# ========== 二阶模态 ==========
um1_2 = u1_mode2[frame]
um2_2 = u2_mode2[frame]
pos_m1_2 = 1 + um1_2
pos_m2_2 = 3 + um2_2
m1_mod2.set_x(pos_m1_2)
m2_mod2.set_x(pos_m2_2)
sp_left2.set_data(*draw_spring(-1, pos_m1_2))
sp_mid2.set_data(*draw_spring(pos_m1_2 + 0.8, pos_m2_2))
sp_right2.set_data(*draw_spring(pos_m2_2 + 0.8, 7))
# ========== 总振动 ==========
ut1 = u1_total[frame]
ut2 = u2_total[frame]
pos_m1_t = 1 + ut1
pos_m2_t = 3 + ut2
m1_tot.set_x(pos_m1_t)
m2_tot.set_x(pos_m2_t)
sp_left_t.set_data(*draw_spring(-1, pos_m1_t))
sp_mid_t.set_data(*draw_spring(pos_m1_t + 0.8, pos_m2_t))
sp_right_t.set_data(*draw_spring(pos_m2_t + 0.8, 7))
return (l1_tot, l1_m1, l1_m2, l2_tot, l2_m1, l2_m2,
m1_mod1, m2_mod1, sp_left1, sp_mid1, sp_right1,
m1_mod2, m2_mod2, sp_left2, sp_mid2, sp_right2,
m1_tot, m2_tot, sp_left_t, sp_mid_t, sp_right_t)
# 关键修改:repeat=True 开启无限循环播放
ani = FuncAnimation(fig, update, frames=len(tau_full), interval=15, blit=True, repeat=True)
fig.suptitle('二自由度系统:一阶模态、二阶模态、模态叠加同步动态演示(两端固定三弹簧)', fontsize=15)
plt.tight_layout()
# ani.save('sine_waves_and_arrows.gif', writer='pillow', fps=12)
plt.show()