悬臂梁模态程序

image


import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

# ====================== 1. 悬臂梁模态基础参数 ======================
# 悬臂梁:x∈[0, L],x=0固支,x=L自由端
L = 4.0  # 梁总长
num_node = 100  # 梁离散节点数
x_beam = np.linspace(0, L, num_node)

# 悬臂梁无量纲频率系数(前两阶)
lambda1 = 1.8751
lambda2 = 4.6941

# 模态振型函数 悬臂梁解析振型
def cantilever_mode_shape(x, lam):
    """
    悬臂梁归一化振型函数
    w(x) = cosh(lam x/L) - cos(lam x/L) - sigma*(sinh(lam x/L)-sin(lam x/L))
    sigma=(coshλ+cosλ)/(sinhλ+sinλ)
    """
    xi = x / L
    sigma = (np.cosh(lam) + np.cos(lam)) / (np.sinh(lam) + np.sin(lam))
    w = np.cosh(lam * xi) - np.cos(lam * xi) - sigma * (np.sinh(lam * xi) - np.sin(lam * xi))
    return w

# 两阶振型空间分布
phi1 = cantilever_mode_shape(x_beam, lambda1)
phi2 = cantilever_mode_shape(x_beam, lambda2)
# 归一化,自由端幅值统一为1
phi1 = phi1 / phi1[-1]
phi2 = phi2 / phi2[-1]

# 固有角频率
omega1 = lambda1**2
omega2 = lambda2**2

# 时间轴
tau_full = np.linspace(0, 15, 800)

# 各阶模态振动时程(自由端位移)
amp = 0.6
disp_mode1 = amp * np.cos(omega1 * tau_full)
disp_mode2 = amp * np.cos(omega2 * tau_full)
disp_total = disp_mode1 + disp_mode2

# 全梁任意时刻挠度:模态叠加 w(x,t)=phi1(x)*q1(t)+phi2(x)*q2(t)
def get_beam_deflection(t):
    q1 = amp * np.cos(omega1 * t)
    q2 = amp * np.cos(omega2 * t)
    w1 = phi1 * q1
    w2 = phi2 * q2
    w_tot = w1 + w2
    return w1, w2, w_tot

# ====================== 2. 画布布局 2行3列 ======================
fig = plt.figure(figsize=(18, 9))
gs = fig.add_gridspec(2, 3, height_ratios=[2, 1])

# 上排:自由端位移时程曲线
ax_time = fig.add_subplot(gs[0, :])  # 整行放时程
ax_mod1 = fig.add_subplot(gs[1, 0])
ax_mod2 = fig.add_subplot(gs[1, 1])
ax_total = fig.add_subplot(gs[1, 2])

# 时程曲线初始化
l_tot, = ax_time.plot([], [], "k-", lw=2, label="总振动(模态叠加)")
l_m1, = ax_time.plot([], [], "r-", lw=1.2, label="一阶弯曲模态")
l_m2, = ax_time.plot([], [], "b--", lw=1.2, label="二阶弯曲模态")

ax_time.set_xlim(0, 15)
ax_time.set_ylim(-amp*2.2, amp*2.2)
ax_time.set_xlabel(r"无量纲时间 $\tau$")
ax_time.set_ylabel("梁自由端挠度 w")
ax_time.grid(alpha=0.3)
ax_time.legend(loc="upper right")
ax_time.set_title("悬臂梁自由端振动时程曲线")

# 统一绘制悬臂梁画布函数
def setup_beam_axes(ax, title):
    ax.set_xlim(-0.5, L+0.5)
    ax.set_ylim(-1.0, 1.0)
    ax.set_aspect("equal")
    ax.set_title(title)
    ax.axis("off")
    # 左侧固支墙体
    wall = plt.Rectangle((-0.5, -1), 0.5, 2, color="dimgray", hatch="//")
    ax.add_patch(wall)
    # 梁主体线
    beam_line, = ax.plot([], [], color="#222222", lw=3)
    # 节点散点
    beam_scat = ax.scatter([], [], c="darkorange", s=12)
    return beam_line, beam_scat

# 三个视图初始化
line_m1, scat_m1 = setup_beam_axes(ax_mod1, "悬臂梁 第一阶模态")
line_m2, scat_m2 = setup_beam_axes(ax_mod2, "悬臂梁 第二阶模态")
line_tot, scat_tot = setup_beam_axes(ax_total, "两阶模态叠加总振动")

# ====================== 3. 动画更新逻辑 ======================
def update(frame):
    t_now = tau_full[frame]
    tau_data = tau_full[:frame]

    # 更新时程图
    l_tot.set_data(tau_data, disp_total[:frame])
    l_m1.set_data(tau_data, disp_mode1[:frame])
    l_m2.set_data(tau_data, disp_mode2[:frame])

    # 获取当前时刻全梁挠度
    w1, w2, wt = get_beam_deflection(t_now)

    # 一阶模态画面
    line_m1.set_data(x_beam, w1)
    scat_m1.set_offsets(np.column_stack([x_beam, w1]))

    # 二阶模态画面
    line_m2.set_data(x_beam, w2)
    scat_m2.set_offsets(np.column_stack([x_beam, w2]))

    # 叠加总振动画面
    line_tot.set_data(x_beam, wt)
    scat_tot.set_offsets(np.column_stack([x_beam, wt]))

    return l_tot, l_m1, l_m2, line_m1, scat_m1, line_m2, scat_m2, line_tot, scat_tot

# ========== 关键修改:interval从18改为9,帧率翻倍,速度快一倍 ==========
ani = FuncAnimation(
    fig, update, frames=len(tau_full),
    interval=9, blit=True, repeat=True
)

fig.suptitle("悬臂梁一阶、二阶弯曲模态与模态叠加同步动态仿真", fontsize=16)
plt.tight_layout()

# 导出GIF配套修改:fps改成60(原30),保证导出视频同样快一倍
# ani.save("cantilever_2modes_fast.gif", writer="pillow", fps=60)
plt.show()
posted @ 2026-07-27 16:04  redufa  阅读(7)  评论(0)    收藏  举报