第十五篇:结构动力响应分析方法

摘要

结构动力响应分析是结构动力学的核心内容,旨在计算结构在各种动力荷载作用下的响应。本教程系统地介绍结构动力响应分析的各种方法,包括时域分析、频域分析、模态分析和随机响应分析等。首先阐述动力响应分析的基本概念和数学模型;然后详细介绍各种分析方法的原理和应用范围;接着讲解数值计算方法,如直接积分法、模态叠加法等;最后通过Python实现多个典型案例,包括时程分析、频率响应分析、模态分析和随机振动分析等。通过本教程的学习,读者将掌握结构动力响应分析的核心方法和实用技术。

关键词

动力响应分析,时域分析,频域分析,模态分析,随机振动,直接积分法,模态叠加法,Python仿真


在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1. 引言

1.1 动力响应分析的重要性

结构在使用过程中会受到各种动力荷载的作用,如风荷载、地震作用、车辆荷载、设备振动等。这些动力荷载会引起结构的振动响应,可能导致结构损坏、影响使用功能或降低舒适性。因此,结构动力响应分析是结构设计、安全评估和性能预测的重要手段。

动力响应分析的主要目的

  1. 安全性评估:验证结构在动力荷载作用下是否满足强度和稳定性要求
  2. 适用性评估:确保结构在正常使用条件下的振动水平满足舒适度要求
  3. 优化设计:通过分析不同设计方案的动力响应,选择最优设计
  4. 损伤识别:通过响应分析识别结构的损伤位置和程度
  5. 振动控制:为振动控制策略的设计提供依据

1.2 动力响应分析方法分类

结构动力响应分析方法可以从不同角度进行分类:

按分析域分类

  • 时域分析:直接在时间域内求解运动方程,适用于任意荷载和非线性系统
  • 频域分析:通过傅里叶变换将问题转换到频率域,适用于线性系统和周期性荷载

按求解方法分类

  • 直接积分法:直接数值积分求解运动方程,如Newmark-β法、Wilson-θ法等
  • 模态叠加法:利用结构的模态特性,将多自由度问题转化为单自由度问题
  • 频率响应法:通过频率域分析求解系统的频率响应函数

按荷载特性分类

  • 确定性分析:针对确定性荷载(如正弦荷载、脉冲荷载)的响应分析
  • 随机分析:针对随机荷载(如随机风荷载、地震动)的响应分析

按系统特性分类

  • 线性分析:假设结构为线性系统,适用于小变形情况
  • 非线性分析:考虑结构的非线性特性,适用于大变形或材料非线性情况

1.3 动力响应分析的数学模型

结构动力响应分析的基础是建立结构的运动方程。对于多自由度线性系统,运动方程为:

Mx¨+Cx˙+Kx=F(t)\mathbf{M} \ddot{\mathbf{x}} + \mathbf{C} \dot{\mathbf{x}} + \mathbf{K} \mathbf{x} = \mathbf{F}(t)Mx¨+Cx˙+Kx=F(t)

其中:

  • M\mathbf{M}M 为质量矩阵
  • C\mathbf{C}C 为阻尼矩阵
  • K\mathbf{K}K 为刚度矩阵
  • x\mathbf{x}x 为位移向量
  • F(t)\mathbf{F}(t)F(t) 为荷载向量

对于非线性系统,运动方程为:

Mx¨+C(x,x˙)x˙+K(x)x=F(t)\mathbf{M} \ddot{\mathbf{x}} + \mathbf{C}(\mathbf{x}, \dot{\mathbf{x}}) \dot{\mathbf{x}} + \mathbf{K}(\mathbf{x}) \mathbf{x} = \mathbf{F}(t)Mx¨+C(x,x˙)x˙+K(x)x=F(t)


2. 时域分析方法

2.1 直接积分法

直接积分法是通过数值积分直接求解运动方程的方法,适用于任意荷载和非线性系统。常用的直接积分法包括:

中心差分法

  • 基于泰勒展开,使用前一时间步的位移和速度计算当前时间步的加速度
  • 显式方法,不需要解线性方程组
  • 时间步长受稳定性限制

Newmark-β法

  • 隐式方法,使用当前时间步的位移和速度计算加速度
  • 无条件稳定(当 β≥1/4\beta \geq 1/4β1/4γ≥1/2\gamma \geq 1/2γ1/2
  • 广泛应用于工程分析

Wilson-θ法

  • 基于Newmark法,引入θ参数
  • 无条件稳定(当 θ≥1.37\theta \geq 1.37θ1.37
  • 具有良好的数值稳定性

HHT-α法

  • 引入α参数控制算法的数值耗散
  • 适用于高频内容丰富的问题

2.2 Newmark-β法的数学推导

Newmark-β法的基本假设为:

x˙t+Δt=x˙t+Δt[(1−γ)x¨t+γx¨t+Δt]\dot{\mathbf{x}}_{t+\Delta t} = \dot{\mathbf{x}}_t + \Delta t \left[ (1-\gamma) \ddot{\mathbf{x}}_t + \gamma \ddot{\mathbf{x}}_{t+\Delta t} \right]x˙t+Δt=x˙t+Δt[(1γ)x¨t+γx¨t+Δt]

xt+Δt=xt+Δtx˙t+Δt2[(1/2−β)x¨t+βx¨t+Δt]\mathbf{x}_{t+\Delta t} = \mathbf{x}_t + \Delta t \dot{\mathbf{x}}_t + \Delta t^2 \left[ (1/2-\beta) \ddot{\mathbf{x}}_t + \beta \ddot{\mathbf{x}}_{t+\Delta t} \right]xt+Δt=xt+Δtx˙t+Δt2[(1/2β)x¨t+βx¨t+Δt]

其中,γ\gammaγβ\betaβ 为参数,通常取 γ=1/2\gamma = 1/2γ=1/2β=1/4\beta = 1/4β=1/4(平均加速度法)。

将上述表达式代入运动方程,得到:

(M+γβΔt2K+γΔtC)x¨t+Δt=Ft+Δt−K(xt+Δtx˙t+Δt2(1/2−β)x¨t)−C(x˙t+Δt(1−γ)x¨t)−Mx¨t\left( \mathbf{M} + \gamma \beta \Delta t^2 \mathbf{K} + \gamma \Delta t \mathbf{C} \right) \ddot{\mathbf{x}}_{t+\Delta t} = \mathbf{F}_{t+\Delta t} - \mathbf{K} \left( \mathbf{x}_t + \Delta t \dot{\mathbf{x}}_t + \Delta t^2 (1/2-\beta) \ddot{\mathbf{x}}_t \right) - \mathbf{C} \left( \dot{\mathbf{x}}_t + \Delta t (1-\gamma) \ddot{\mathbf{x}}_t \right) - \mathbf{M} \ddot{\mathbf{x}}_t(M+γβΔt2K+γΔtC)x¨t+Δt=Ft+ΔtK(xt+Δtx˙t+Δt2(1/2β)x¨t)C(x˙t+Δt(1γ)x¨t)Mx¨t

2.3 直接积分法的稳定性分析

数值稳定性

  • 显式方法(如中心差分法):有条件稳定,时间步长必须小于临界时间步长
  • 隐式方法(如Newmark-β法):可无条件稳定,时间步长仅受精度影响

临界时间步长
对于中心差分法,临界时间步长为:

Δtcr=2ωmax\Delta t_{cr} = \frac{2}{\omega_{max}}Δtcr=ωmax2

其中,ωmax\omega_{max}ωmax 为系统的最大固有频率。


3. 频域分析方法

3.1 傅里叶变换

频域分析的基础是傅里叶变换,将时域信号转换为频域表示:

F(ω)=∫−∞∞f(t)e−iωtdtF(\omega) = \int_{-\infty}^{\infty} f(t) e^{-i\omega t} dtF(ω)=f(t)etdt

逆变换为:

f(t)=12π∫−∞∞F(ω)eiωtdωf(t) = \frac{1}{2\pi} \int_{-\infty}^{\infty} F(\omega) e^{i\omega t} d\omegaf(t)=2π1F(ω)etdω

3.2 频率响应函数

对于线性系统,输入和输出之间的关系可以用频率响应函数(FRF)表示:

H(ω)=X(ω)F(ω)H(\omega) = \frac{X(\omega)}{F(\omega)}H(ω)=F(ω)X(ω)

其中,X(ω)X(\omega)X(ω) 为输出的傅里叶变换,F(ω)F(\omega)F(ω) 为输入的傅里叶变换。

对于单自由度系统,频率响应函数为:

H(ω)=1−mω2+icω+kH(\omega) = \frac{1}{-m\omega^2 + ic\omega + k}H(ω)=mω2+icω+k1

对于多自由度系统,频率响应函数矩阵为:

H(ω)=(−ω2M+iωC+K)−1\mathbf{H}(\omega) = \left( -\omega^2 \mathbf{M} + i\omega \mathbf{C} + \mathbf{K} \right)^{-1}H(ω)=(ω2M+C+K)1

3.3 频域分析的应用

频率响应分析

  • 计算系统在不同频率下的响应幅值和相位
  • 识别系统的共振频率和阻尼特性
  • 评估系统对谐波荷载的响应

随机振动分析

  • 通过功率谱密度(PSD)分析随机荷载的响应
  • 计算响应的统计特性(均值、方差、概率分布)

4. 模态分析方法

4.1 模态叠加法

模态叠加法利用结构的模态特性,将多自由度问题转化为单自由度问题:

  1. 模态分析:求解结构的固有频率和模态振型
  2. 坐标变换:将物理坐标转换为模态坐标
  3. 单自由度求解:在模态坐标下求解每个模态的响应
  4. 叠加:将各模态的响应转换回物理坐标并叠加

4.2 模态分析的数学基础

结构的自由振动方程为:

Mx¨+Kx=0\mathbf{M} \ddot{\mathbf{x}} + \mathbf{K} \mathbf{x} = 0Mx¨+Kx=0

假设解的形式为 x=ϕeiωt\mathbf{x} = \boldsymbol{\phi} e^{i\omega t}x=ϕet,代入得到特征值问题:

(K−ω2M)ϕ=0\left( \mathbf{K} - \omega^2 \mathbf{M} \right) \boldsymbol{\phi} = 0(Kω2M)ϕ=0

求解得到固有频率 ω1,ω2,…,ωn\omega_1, \omega_2, \dots, \omega_nω1,ω2,,ωn 和对应的模态振型 ϕ1,ϕ2,…,ϕn\boldsymbol{\phi}_1, \boldsymbol{\phi}_2, \dots, \boldsymbol{\phi}_nϕ1,ϕ2,,ϕn

4.3 模态叠加法的应用

线性系统

  • 适用于线性系统的动力响应分析
  • 计算效率高,特别是对于大型系统
  • 可以只考虑贡献较大的前几阶模态

非线性系统

  • 模态叠加法不适用于强非线性系统
  • 对于弱非线性系统,可以采用非线性模态叠加法

5. 随机响应分析

5.1 随机过程基础

随机振动分析处理的是随机荷载作用下的结构响应。随机过程的基本特性包括:

  • 均值:随机过程的平均水平
  • 自相关函数:描述不同时刻随机变量之间的相关性
  • 功率谱密度(PSD):描述随机过程在频率域的能量分布

5.2 随机响应的计算方法

频域方法

  • 利用功率谱密度和频率响应函数计算响应的功率谱密度
  • 适用于线性系统
  • 计算效率高

时域方法

  • 生成随机荷载的时间历程样本
  • 对每个样本进行时程分析
  • 统计分析响应的统计特性
  • 适用于非线性系统

5.3 响应统计特性的计算

对于线性系统,响应的统计特性可以通过以下公式计算:

  • 响应功率谱密度SX(ω)=∣H(ω)∣2SF(ω)S_X(\omega) = |H(\omega)|^2 S_F(\omega)SX(ω)=H(ω)2SF(ω)
  • 响应方差σX2=∫0∞SX(ω)dω\sigma_X^2 = \int_{0}^{\infty} S_X(\omega) d\omegaσX2=0SX(ω)dω
  • 响应均方根RMSX=σX2\text{RMS}_X = \sqrt{\sigma_X^2}RMSX=σX2

6. Python实现案例

6.1 案例1:直接积分法(Newmark-β法)

本案例实现Newmark-β法求解结构的动力响应。

# -*- coding: utf-8 -*-
"""
主题015:结构动力响应分析方法 - 案例1
直接积分法(Newmark-β法)
"""
import matplotlib
matplotlib.use('Agg')
import numpy as np
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings('ignore')
import os

# 创建输出目录
output_dir = r'd:\文档\500仿真领域\工程仿真\结构动力学仿真\主题015'
os.makedirs(output_dir, exist_ok=True)

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

print("=" * 70)
print("主题015:结构动力响应分析方法")
print("案例1:直接积分法(Newmark-β法)")
print("=" * 70)

# ============================================================
# 多自由度结构模型
# ============================================================
print("\n多自由度结构模型:")
print("-" * 70)

# 质量矩阵
M = np.array([[1000, 0, 0],
              [0, 1000, 0],
              [0, 0, 1000]])

# 刚度矩阵
K = np.array([[40000, -20000, 0],
              [-20000, 40000, -20000],
              [0, -20000, 20000]])

# 阻尼矩阵(瑞利阻尼)
alpha = 0.1
beta = 0.005
C = alpha * M + beta * K

print("  质量矩阵 M (kg):")
print(M)
print("\n  刚度矩阵 K (N/m):")
print(K)
print("\n  阻尼矩阵 C (N·s/m):")
print(C)

# 计算固有频率
eigenvalues, eigenvectors = np.linalg.eig(np.linalg.inv(M) @ K)
idx = np.argsort(eigenvalues)
omega_n = np.sqrt(eigenvalues[idx])
f_n = omega_n / (2 * np.pi)

print(f"\n固有频率:")
for i, (wn, fn) in enumerate(zip(omega_n, f_n)):
    print(f"  第{i+1}阶: ω = {wn:.4f} rad/s, f = {fn:.4f} Hz")

# ============================================================
# Newmark-β法
# ============================================================
def newmark_beta(M, C, K, F, x0, v0, a0, dt, t_end, gamma=0.5, beta=0.25):
    """
    Newmark-β法求解运动方程
    
    参数:
    M: 质量矩阵
    C: 阻尼矩阵
    K: 刚度矩阵
    F: 荷载时程函数
    x0: 初始位移
    v0: 初始速度
    a0: 初始加速度
    dt: 时间步长
    t_end: 结束时间
    gamma: Newmark参数
    beta: Newmark参数
    
    返回:
    t: 时间数组
    x: 位移时程
    v: 速度时程
    a: 加速度时程
    """
    n_dof = len(x0)
    n_steps = int(t_end / dt)
    
    t = np.linspace(0, t_end, n_steps + 1)
    x = np.zeros((n_steps + 1, n_dof))
    v = np.zeros((n_steps + 1, n_dof))
    a = np.zeros((n_steps + 1, n_dof))
    
    # 初始条件
    x[0] = x0
    v[0] = v0
    a[0] = a0
    
    # 构建有效刚度矩阵
    K_eff = K + gamma / (beta * dt) * C + 1 / (beta * dt**2) * M
    
    # 预计算常数
    a1 = 1 / (beta * dt**2)
    a2 = gamma / (beta * dt)
    a3 = 1 / (beta * dt)
    a4 = 1 / (2 * beta) - 1
    a5 = gamma / beta - 1
    a6 = dt * (gamma / (2 * beta) - 1)
    
    for i in range(n_steps):
        # 计算有效荷载
        F_t = F(t[i+1])
        F_eff = F_t + M @ (a1 * x[i] + a3 * v[i] + a4 * a[i]) + C @ (a2 * x[i] + a5 * v[i] + a6 * a[i])
        
        # 求解位移
        x[i+1] = np.linalg.solve(K_eff, F_eff)
        
        # 计算速度和加速度
        a[i+1] = a1 * (x[i+1] - x[i]) - a3 * v[i] - a4 * a[i]
        v[i+1] = v[i] + dt * ((1 - gamma) * a[i] + gamma * a[i+1])
    
    return t, x, v, a

# ============================================================
# 荷载函数
# ============================================================
def seismic_load(t):
    """地震荷载时程"""
    # 生成人工地震波
    if t < 0:
        return np.array([0, 0, 0])
    
    # 模拟El Centro地震波
    omega = 2 * np.pi * 1.0  # 主频1Hz
    envelope = np.exp(-0.1 * t) * np.sin(np.pi * t / 10)**2
    if t > 10:
        envelope = 0
    
    acc = 0.1 * np.sin(omega * t) + 0.05 * np.sin(3 * omega * t) + 0.02 * np.sin(5 * omega * t)
    acc *= envelope
    
    # 转换为地震力
    return -M @ np.array([acc, acc, acc])

# ============================================================
# 求解动力响应
# ============================================================
print("\n求解动力响应:")
print("-" * 70)

# 初始条件
x0 = np.zeros(3)
v0 = np.zeros(3)
a0 = np.linalg.solve(M, seismic_load(0) - C @ v0 - K @ x0)

# 时间参数
dt = 0.01
t_end = 20.0

print(f"  时间步长: {dt} s")
print(f"  计算时间: {t_end} s")
print(f"  总步数: {int(t_end/dt)} 步")

# 使用Newmark-β法求解
t, x, v, a = newmark_beta(M, C, K, seismic_load, x0, v0, a0, dt, t_end)

# 计算加速度响应(转换为m/s²)
a_accel = np.zeros_like(a)
for i in range(len(a)):
    a_accel[i] = a[i] + np.array([seismic_load(t[i])[0]/M[0,0], 
                                   seismic_load(t[i])[1]/M[1,1], 
                                   seismic_load(t[i])[2]/M[2,2]])

# ============================================================
# 结果分析
# ============================================================
print("\n响应结果分析:")
print("-" * 70)

for i in range(3):
    peak_disp = np.max(np.abs(x[:, i]))
    peak_vel = np.max(np.abs(v[:, i]))
    peak_acc = np.max(np.abs(a_accel[:, i]))
    
    print(f"\n第{i+1}层:")
    print(f"  峰值位移: {peak_disp*1000:.2f} mm")
    print(f"  峰值速度: {peak_vel:.2f} m/s")
    print(f"  峰值加速度: {peak_acc:.2f} m/s²")

# ============================================================
# 绘制结果
# ============================================================
print("\n正在生成可视化图表...")

fig1, axes = plt.subplots(3, 3, figsize=(16, 12))

for i in range(3):
    # 位移时程
    axes[i, 0].plot(t, x[:, i]*1000, 'b-', linewidth=1.5)
    axes[i, 0].set_xlabel('Time (s)', fontsize=11)
    axes[i, 0].set_ylabel('Displacement (mm)', fontsize=11)
    axes[i, 0].set_title(f'Floor {i+1} Displacement', fontsize=12, fontweight='bold')
    axes[i, 0].grid(True, alpha=0.3)
    
    # 速度时程
    axes[i, 1].plot(t, v[:, i], 'g-', linewidth=1.5)
    axes[i, 1].set_xlabel('Time (s)', fontsize=11)
    axes[i, 1].set_ylabel('Velocity (m/s)', fontsize=11)
    axes[i, 1].set_title(f'Floor {i+1} Velocity', fontsize=12, fontweight='bold')
    axes[i, 1].grid(True, alpha=0.3)
    
    # 加速度时程
    axes[i, 2].plot(t, a_accel[:, i], 'r-', linewidth=1.5)
    axes[i, 2].set_xlabel('Time (s)', fontsize=11)
    axes[i, 2].set_ylabel('Acceleration (m/s²)', fontsize=11)
    axes[i, 2].set_title(f'Floor {i+1} Acceleration', fontsize=12, fontweight='bold')
    axes[i, 2].grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig(f'{output_dir}/newmark_analysis.png', dpi=150, bbox_inches='tight')
plt.close()
print("  Newmark分析图已保存")

# 绘制层间位移
interstory_disp = np.zeros((len(t), 2))
for i in range(len(t)):
    interstory_disp[i, 0] = x[i, 1] - x[i, 0]  # 1-2层间
    interstory_disp[i, 1] = x[i, 2] - x[i, 1]  # 2-3层间

fig2, axes = plt.subplots(1, 2, figsize=(14, 5))

axes[0].plot(t, interstory_disp[:, 0]*1000, 'b-', linewidth=1.5)
axes[0].set_xlabel('Time (s)', fontsize=11)
axes[0].set_ylabel('Interstory Displacement (mm)', fontsize=11)
axes[0].set_title('1-2 Interstory Displacement', fontsize=12, fontweight='bold')
axes[0].grid(True, alpha=0.3)

axes[1].plot(t, interstory_disp[:, 1]*1000, 'r-', linewidth=1.5)
axes[1].set_xlabel('Time (s)', fontsize=11)
axes[1].set_ylabel('Interstory Displacement (mm)', fontsize=11)
axes[1].set_title('2-3 Interstory Displacement', fontsize=12, fontweight='bold')
axes[1].grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig(f'{output_dir}/interstory_disp.png', dpi=150, bbox_inches='tight')
plt.close()
print("  层间位移图已保存")

# ============================================================
# 不同时间步长的影响
# ============================================================
print("\n不同时间步长的影响分析:")
print("-" * 70)

dt_values = [0.005, 0.01, 0.02, 0.05]
peak_disps = []

for dt_test in dt_values:
    t_test, x_test, v_test, a_test = newmark_beta(M, C, K, seismic_load, x0, v0, a0, dt_test, t_end)
    peak_disp = np.max(np.abs(x_test[:, 2]))  # 顶层峰值位移
    peak_disps.append(peak_disp)
    print(f"  dt = {dt_test:.3f} s: 顶层峰值位移 = {peak_disp*1000:.2f} mm")

# 绘制时间步长影响
fig3, ax = plt.subplots(figsize=(10, 6))

ax.plot(dt_values, np.array(peak_disps)*1000, 'b-o', linewidth=2, markersize=8)
ax.set_xlabel('Time Step (s)', fontsize=11)
ax.set_ylabel('Peak Displacement (mm)', fontsize=11)
ax.set_title('Effect of Time Step on Peak Displacement', fontsize=12, fontweight='bold')
ax.grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig(f'{output_dir}/time_step_effect.png', dpi=150, bbox_inches='tight')
plt.close()
print("  时间步长影响图已保存")

print("\n" + "=" * 70)
print("Newmark-β法动力响应分析完成!")
print("=" * 70)

6.2 案例2:频率响应分析

本案例实现频率响应分析,计算结构在谐波荷载作用下的响应。

# -*- coding: utf-8 -*-
"""
主题015:结构动力响应分析方法 - 案例2
频率响应分析
"""
import matplotlib
matplotlib.use('Agg')
import numpy as np
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings('ignore')
import os

output_dir = r'd:\文档\500仿真领域\工程仿真\结构动力学仿真\主题015'
os.makedirs(output_dir, exist_ok=True)

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

print("\n" + "=" * 70)
print("案例2:频率响应分析")
print("=" * 70)

# ============================================================
# 多自由度结构模型
# ============================================================
print("\n多自由度结构模型:")
print("-" * 70)

# 质量矩阵
M = np.array([[1000, 0, 0],
              [0, 1000, 0],
              [0, 0, 1000]])

# 刚度矩阵
K = np.array([[40000, -20000, 0],
              [-20000, 40000, -20000],
              [0, -20000, 20000]])

# 阻尼矩阵(瑞利阻尼)
alpha = 0.1
beta = 0.005
C = alpha * M + beta * K

print("  质量矩阵 M (kg):")
print(M)
print("\n  刚度矩阵 K (N/m):")
print(K)
print("\n  阻尼矩阵 C (N·s/m):")
print(C)

# 计算固有频率
eigenvalues, eigenvectors = np.linalg.eig(np.linalg.inv(M) @ K)
idx = np.argsort(eigenvalues)
omega_n = np.sqrt(eigenvalues[idx])
f_n = omega_n / (2 * np.pi)

print(f"\n固有频率:")
for i, (wn, fn) in enumerate(zip(omega_n, f_n)):
    print(f"  第{i+1}阶: ω = {wn:.4f} rad/s, f = {fn:.4f} Hz")

# ============================================================
# 频率响应函数计算
# ============================================================
def frequency_response_function(M, C, K, omega_range):
    """
    计算频率响应函数
    
    参数:
    M: 质量矩阵
    C: 阻尼矩阵
    K: 刚度矩阵
    omega_range: 频率范围
    
    返回:
    H: 频率响应函数矩阵
    """
    n_dof = M.shape[0]
    n_freq = len(omega_range)
    H = np.zeros((n_dof, n_dof, n_freq), dtype=complex)
    
    for i, omega in enumerate(omega_range):
        # 构建动力刚度矩阵
        K_dyn = -omega**2 * M + 1j * omega * C + K
        # 计算频率响应函数(逆矩阵)
        H[:, :, i] = np.linalg.inv(K_dyn)
    
    return H

# ============================================================
# 频率范围
# ============================================================
print("\n频率响应分析:")
print("-" * 70)

# 频率范围(覆盖前3阶固有频率)
f_min = 0.1
top_freq = f_n[-1] * 2
f_range = np.logspace(np.log10(f_min), np.log10(top_freq), 200)
omega_range = 2 * np.pi * f_range

print(f"  频率范围: {f_min:.1f} ~ {top_freq:.1f} Hz")
print(f"  频率点数: {len(f_range)}")

# 计算频率响应函数
H = frequency_response_function(M, C, K, omega_range)

# 荷载分布(作用于顶层)
F0 = 1000.0  # 荷载幅值
load_distribution = np.array([0, 0, 1])  # 仅顶层受荷

# 计算响应
response_amp = np.zeros((3, len(f_range)))
response_phase = np.zeros((3, len(f_range)))

for i in range(len(f_range)):
    # 计算响应
    X = H[:, :, i] @ (F0 * load_distribution)
    response_amp[:, i] = np.abs(X)
    response_phase[:, i] = np.angle(X) * 180 / np.pi  # 转换为度

# ============================================================
# 结果分析
# ============================================================
print("\n频率响应分析结果:")
print("-" * 70)

for i in range(3):
    # 找到峰值响应频率
    peak_idx = np.argmax(response_amp[i])
    peak_freq = f_range[peak_idx]
    peak_amp = response_amp[i, peak_idx]
    
    print(f"\n第{i+1}层:")
    print(f"  峰值响应频率: {peak_freq:.2f} Hz")
    print(f"  峰值响应幅值: {peak_amp*1000:.2f} mm")
    print(f"  对应固有频率: {f_n[i]:.2f} Hz")

# ============================================================
# 绘制频率响应曲线
# ============================================================
print("\n正在生成可视化图表...")

fig1, axes = plt.subplots(3, 2, figsize=(16, 12))

for i in range(3):
    # 幅值响应
    axes[i, 0].semilogx(f_range, response_amp[i]*1000, 'b-', linewidth=2)
    axes[i, 0].axvline(f_n[i], color='r', linestyle='--', alpha=0.7, label=f'f{i+1} = {f_n[i]:.2f} Hz')
    axes[i, 0].set_xlabel('Frequency (Hz)', fontsize=11)
    axes[i, 0].set_ylabel('Amplitude (mm)', fontsize=11)
    axes[i, 0].set_title(f'Floor {i+1} Amplitude Response', fontsize=12, fontweight='bold')
    axes[i, 0].legend()
    axes[i, 0].grid(True, alpha=0.3)
    
    # 相位响应
    axes[i, 1].semilogx(f_range, response_phase[i], 'g-', linewidth=2)
    axes[i, 1].axvline(f_n[i], color='r', linestyle='--', alpha=0.7, label=f'f{i+1} = {f_n[i]:.2f} Hz')
    axes[i, 1].set_xlabel('Frequency (Hz)', fontsize=11)
    axes[i, 1].set_ylabel('Phase (deg)', fontsize=11)
    axes[i, 1].set_title(f'Floor {i+1} Phase Response', fontsize=12, fontweight='bold')
    axes[i, 1].legend()
    axes[i, 1].grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig(f'{output_dir}/frequency_response.png', dpi=150, bbox_inches='tight')
plt.close()
print("  频率响应图已保存")

# ============================================================
# 三维频率响应图
# ============================================================
fig2 = plt.figure(figsize=(12, 8))
ax = fig2.add_subplot(111, projection='3d')

# 网格
X, Y = np.meshgrid(f_range, np.arange(1, 4))
Z = response_amp.T * 1000  # 转换为mm

# 绘制表面
surf = ax.plot_surface(X, Y, Z, cmap='viridis', alpha=0.8)

ax.set_xlabel('Frequency (Hz)', fontsize=11)
ax.set_ylabel('Floor', fontsize=11)
ax.set_zlabel('Amplitude (mm)', fontsize=11)
ax.set_title('3D Frequency Response Surface', fontsize=12, fontweight='bold')

# 添加颜色条
fig2.colorbar(surf, ax=ax, shrink=0.5, aspect=5)

plt.tight_layout()
plt.savefig(f'{output_dir}/frequency_response_3d.png', dpi=150, bbox_inches='tight')
plt.close()
print("  3D频率响应图已保存")

# ============================================================
# 不同阻尼比的影响
# ============================================================
print("\n不同阻尼比的影响分析:")
print("-" * 70)

damping_ratios = [0.02, 0.05, 0.10, 0.20]
response_amp_damping = []

for zeta in damping_ratios:
    # 重新计算阻尼矩阵
    C_zeta = 2 * zeta * np.sqrt(M[0,0] * K[0,0]) * np.eye(3)
    # 计算频率响应
    H_zeta = frequency_response_function(M, C_zeta, K, omega_range)
    # 计算顶层响应
    X_zeta = np.abs(H_zeta[2, 2, :] * F0)
    response_amp_damping.append(X_zeta)
    
    print(f"  阻尼比 ζ = {zeta:.2f}: 顶层峰值响应 = {np.max(X_zeta)*1000:.2f} mm")

# 绘制阻尼比影响
fig3, ax = plt.subplots(figsize=(10, 6))

for i, zeta in enumerate(damping_ratios):
    ax.semilogx(f_range, response_amp_damping[i]*1000, linewidth=2, label=f'ζ = {zeta:.2f}')

ax.axvline(f_n[2], color='r', linestyle='--', alpha=0.7, label=f'f3 = {f_n[2]:.2f} Hz')
ax.set_xlabel('Frequency (Hz)', fontsize=11)
ax.set_ylabel('Amplitude (mm)', fontsize=11)
ax.set_title('Effect of Damping Ratio on Frequency Response', fontsize=12, fontweight='bold')
ax.legend()
ax.grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig(f'{output_dir}/damping_effect.png', dpi=150, bbox_inches='tight')
plt.close()
print("  阻尼比影响图已保存")

print("\n" + "=" * 70)
print("频率响应分析完成!")
print("=" * 70)

6.3 案例3:模态叠加法

本案例实现模态叠加法求解结构的动力响应。

# -*- coding: utf-8 -*-
"""
主题015:结构动力响应分析方法 - 案例3
模态叠加法
"""
import matplotlib
matplotlib.use('Agg')
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
import warnings
warnings.filterwarnings('ignore')
import os

output_dir = r'd:\文档\500仿真领域\工程仿真\结构动力学仿真\主题015'
os.makedirs(output_dir, exist_ok=True)

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

print("\n" + "=" * 70)
print("案例3:模态叠加法")
print("=" * 70)

# ============================================================
# 多自由度结构模型
# ============================================================
print("\n多自由度结构模型:")
print("-" * 70)

# 质量矩阵
M = np.array([[1000, 0, 0],
              [0, 1000, 0],
              [0, 0, 1000]])

# 刚度矩阵
K = np.array([[40000, -20000, 0],
              [-20000, 40000, -20000],
              [0, -20000, 20000]])

# 阻尼矩阵(瑞利阻尼)
alpha = 0.1
beta = 0.005
C = alpha * M + beta * K

print("  质量矩阵 M (kg):")
print(M)
print("\n  刚度矩阵 K (N/m):")
print(K)
print("\n  阻尼矩阵 C (N·s/m):")
print(C)

# ============================================================
# 模态分析
# ============================================================
print("\n模态分析:")
print("-" * 70)

# 求解特征值问题
eigenvalues, eigenvectors = np.linalg.eig(np.linalg.inv(M) @ K)

# 排序(从小到大)
idx = np.argsort(eigenvalues)
eigenvalues = eigenvalues[idx]
eigenvectors = eigenvectors[:, idx]

# 计算固有频率和阻尼比
omega_n = np.sqrt(np.real(eigenvalues))
f_n = omega_n / (2 * np.pi)

# 模态阻尼比(假设各阶模态阻尼比相同)
zeta_n = np.zeros(3)
for i in range(3):
    # 计算模态阻尼
    phi = eigenvectors[:, i].reshape(-1, 1)
    c_modal = phi.T @ C @ phi
    m_modal = phi.T @ M @ phi
    zeta_n[i] = c_modal[0, 0] / (2 * np.sqrt(m_modal[0, 0] * eigenvalues[i]))

print("  模态振型矩阵 Φ:")
print(eigenvectors)

print(f"\n固有频率和阻尼比:")
for i in range(3):
    print(f"  第{i+1}阶: ω = {omega_n[i]:.4f} rad/s, f = {f_n[i]:.4f} Hz, ζ = {zeta_n[i]:.4f}")

# 模态质量矩阵
M_modal = eigenvectors.T @ M @ eigenvectors
print("\n  模态质量矩阵 M_modal:")
print(M_modal)

# 模态刚度矩阵
K_modal = eigenvectors.T @ K @ eigenvectors
print("\n  模态刚度矩阵 K_modal:")
print(K_modal)

# 模态阻尼矩阵
C_modal = eigenvectors.T @ C @ eigenvectors
print("\n  模态阻尼矩阵 C_modal:")
print(C_modal)

# ============================================================
# 模态叠加法
# ============================================================
def modal_superposition(M, C, K, phi, omega_n, zeta_n, F_func, t):
    """
    模态叠加法求解动力响应
    
    参数:
    M: 质量矩阵
    C: 阻尼矩阵
    K: 刚度矩阵
    phi: 模态振型矩阵
    omega_n: 固有频率
    zeta_n: 模态阻尼比
    F_func: 荷载时程函数
    t: 时间数组
    
    返回:
    x: 位移时程
    """
    n_dof = M.shape[0]
    n_modes = phi.shape[1]
    n_steps = len(t)
    
    x = np.zeros((n_steps, n_dof))
    
    # 对每个模态求解
    for i in range(n_modes):
        print(f"  求解第{i+1}阶模态...")
        
        # 模态质量、刚度、阻尼
        m_i = phi[:, i].T @ M @ phi[:, i]
        k_i = phi[:, i].T @ K @ phi[:, i]
        c_i = phi[:, i].T @ C @ phi[:, i]
        
        # 模态荷载
        def modal_force(t_val):
            F = F_func(t_val)
            return phi[:, i].T @ F
        
        # 单自由度运动方程
        def sdof_system(y, t_val):
            q, q_dot = y
            F_modal = modal_force(t_val)
            q_ddot = (F_modal - c_i * q_dot - k_i * q) / m_i
            return [q_dot, q_ddot]
        
        # 初始条件
        y0 = [0, 0]
        
        # 求解单自由度方程
        solution = odeint(sdof_system, y0, t)
        q = solution[:, 0]
        
        # 转换为物理坐标并叠加
        x_i = np.outer(q, phi[:, i])
        x += x_i
    
    return x

# ============================================================
# 荷载函数
# ============================================================
def pulse_load(t):
    """脉冲荷载"""
    if t < 0.5:
        return np.array([0, 0, 5000 * np.sin(np.pi * t / 0.5)])
    else:
        return np.array([0, 0, 0])

def harmonic_load(t):
    """谐波荷载"""
    omega = 2 * np.pi * 2.0  # 2Hz
    return np.array([0, 0, 1000 * np.sin(omega * t)])

# ============================================================
# 求解动力响应
# ============================================================
print("\n求解动力响应:")
print("-" * 70)

# 时间参数
t = np.linspace(0, 10, 500)

# 脉冲荷载响应
print("  求解脉冲荷载响应...")
x_pulse = modal_superposition(M, C, K, eigenvectors, omega_n, zeta_n, pulse_load, t)

# 谐波荷载响应
print("  求解谐波荷载响应...")
x_harmonic = modal_superposition(M, C, K, eigenvectors, omega_n, zeta_n, harmonic_load, t)

# ============================================================
# 结果分析
# ============================================================
print("\n响应结果分析:")
print("-" * 70)

print("脉冲荷载响应:")
for i in range(3):
    peak_disp = np.max(np.abs(x_pulse[:, i]))
    print(f"  第{i+1}层峰值位移: {peak_disp*1000:.2f} mm")

print("\n谐波荷载响应:")
for i in range(3):
    peak_disp = np.max(np.abs(x_harmonic[:, i]))
    print(f"  第{i+1}层峰值位移: {peak_disp*1000:.2f} mm")

# ============================================================
# 绘制结果
# ============================================================
print("\n正在生成可视化图表...")

fig1, axes = plt.subplots(3, 2, figsize=(16, 12))

for i in range(3):
    # 脉冲荷载响应
    axes[i, 0].plot(t, x_pulse[:, i]*1000, 'b-', linewidth=1.5)
    axes[i, 0].set_xlabel('Time (s)', fontsize=11)
    axes[i, 0].set_ylabel('Displacement (mm)', fontsize=11)
    axes[i, 0].set_title(f'Floor {i+1} - Pulse Load Response', fontsize=12, fontweight='bold')
    axes[i, 0].grid(True, alpha=0.3)
    
    # 谐波荷载响应
    axes[i, 1].plot(t, x_harmonic[:, i]*1000, 'r-', linewidth=1.5)
    axes[i, 1].set_xlabel('Time (s)', fontsize=11)
    axes[i, 1].set_ylabel('Displacement (mm)', fontsize=11)
    axes[i, 1].set_title(f'Floor {i+1} - Harmonic Load Response', fontsize=12, fontweight='bold')
    axes[i, 1].grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig(f'{output_dir}/modal_superposition.png', dpi=150, bbox_inches='tight')
plt.close()
print("  模态叠加法响应图已保存")

# ============================================================
# 模态参与分析
# ============================================================
print("\n模态参与分析:")
print("-" * 70)

# 计算模态参与因子
modal_participation = np.zeros(3)
for i in range(3):
    modal_participation[i] = np.sum(eigenvectors[:, i])
    print(f"  第{i+1}阶模态参与因子: {modal_participation[i]:.4f}")

# 计算有效质量
modal_mass = np.zeros(3)
for i in range(3):
    modal_mass[i] = (np.sum(eigenvectors[:, i]))**2 / M_modal[i, i]
    print(f"  第{i+1}阶有效质量: {modal_mass[i]:.2f} kg")

# 绘制模态参与和有效质量
fig2, axes = plt.subplots(1, 2, figsize=(14, 5))

modes = ['Mode 1', 'Mode 2', 'Mode 3']

axes[0].bar(modes, modal_participation, color='steelblue', alpha=0.8)
axes[0].set_xlabel('Mode', fontsize=11)
axes[0].set_ylabel('Participation Factor', fontsize=11)
axes[0].set_title('Modal Participation Factors', fontsize=12, fontweight='bold')
axes[0].grid(True, alpha=0.3, axis='y')

axes[1].bar(modes, modal_mass, color='coral', alpha=0.8)
axes[1].set_xlabel('Mode', fontsize=11)
axes[1].set_ylabel('Effective Mass (kg)', fontsize=11)
axes[1].set_title('Modal Effective Mass', fontsize=12, fontweight='bold')
axes[1].grid(True, alpha=0.3, axis='y')

plt.tight_layout()
plt.savefig(f'{output_dir}/modal_participation.png', dpi=150, bbox_inches='tight')
plt.close()
print("  模态参与分析图已保存")

# ============================================================
# 不同模态数量的影响
# ============================================================
print("\n不同模态数量的影响分析:")
print("-" * 70)

n_modes_list = [1, 2, 3]
peak_disps = []

for n_modes in n_modes_list:
    # 使用前n_modes阶模态
    phi_truncated = eigenvectors[:, :n_modes]
    omega_truncated = omega_n[:n_modes]
    zeta_truncated = zeta_n[:n_modes]
    
    # 求解响应
    x_truncated = modal_superposition(M, C, K, phi_truncated, omega_truncated, zeta_truncated, pulse_load, t)
    
    # 计算顶层峰值位移
    peak_disp = np.max(np.abs(x_truncated[:, 2]))
    peak_disps.append(peak_disp)
    
    print(f"  前{n_modes}阶模态: 顶层峰值位移 = {peak_disp*1000:.2f} mm")

# 绘制模态数量影响
fig3, ax = plt.subplots(figsize=(10, 6))

ax.plot(n_modes_list, np.array(peak_disps)*1000, 'b-o', linewidth=2, markersize=8)
ax.set_xlabel('Number of Modes', fontsize=11)
ax.set_ylabel('Peak Displacement (mm)', fontsize=11)
ax.set_title('Effect of Number of Modes on Response', fontsize=12, fontweight='bold')
ax.grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig(f'{output_dir}/modal_number_effect.png', dpi=150, bbox_inches='tight')
plt.close()
print("  模态数量影响图已保存")

print("\n" + "=" * 70)
print("模态叠加法分析完成!")
print("=" * 70)

6.4 案例4:随机振动分析

本案例实现随机振动分析,计算结构在随机荷载作用下的响应。

# -*- coding: utf-8 -*-
"""
主题015:结构动力响应分析方法 - 案例4
随机振动分析
"""
import matplotlib
matplotlib.use('Agg')
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal
import warnings
warnings.filterwarnings('ignore')
import os

output_dir = r'd:\文档\500仿真领域\工程仿真\结构动力学仿真\主题015'
os.makedirs(output_dir, exist_ok=True)

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

print("\n" + "=" * 70)
print("案例4:随机振动分析")
print("=" * 70)

# ============================================================
# 单自由度结构模型
# ============================================================
print("\n单自由度结构模型:")
print("-" * 70)

m = 1000.0  # 质量 (kg)
k = 40000.0  # 刚度 (N/m)
zeta = 0.05  # 阻尼比

omega_n = np.sqrt(k / m)  # 固有频率
c = 2 * zeta * m * omega_n  # 阻尼系数

print(f"  质量 m = {m} kg")
print(f"  刚度 k = {k} N/m")
print(f"  阻尼比 ζ = {zeta}")
print(f"  固有频率 ω_n = {omega_n:.4f} rad/s")
print(f"  阻尼系数 c = {c:.2f} N·s/m")

# ============================================================
# 随机荷载生成
# ============================================================
def generate_random_load(t, dt, seed=42):
    """生成随机荷载时程"""
    np.random.seed(seed)
    n = len(t)
    
    # 生成白噪声
    white_noise = np.random.randn(n)
    
    # 带通滤波(模拟风荷载或地震动)
    sos = signal.butter(4, [0.1, 10], 'bandpass', fs=1/dt, output='sos')
    filtered = signal.sosfilt(sos, white_noise)
    
    # 调整功率谱密度
    freq = np.fft.fftfreq(n, dt)
    psd = np.zeros(n)
    
    # 模拟风荷载PSD(随频率增加而减小)
    for i in range(n):
        f = abs(freq[i])
        if f > 0:
            psd[i] = 1.0 / (1 + f**2)
    
    # 应用PSD
    filtered_fft = np.fft.fft(filtered)
    filtered_fft *= np.sqrt(psd)
    random_load = np.real(np.fft.ifft(filtered_fft))
    
    # 标准化
    random_load = random_load / np.std(random_load) * 1000  # 标准差为1000N
    
    return random_load

# ============================================================
# 时间参数
# ============================================================
dt = 0.01
t_end = 60.0
t = np.arange(0, t_end, dt)

print(f"\n时间参数:")
print(f"  时间步长: {dt} s")
print(f"  持续时间: {t_end} s")
print(f"  总点数: {len(t)}")

# 生成随机荷载
F = generate_random_load(t, dt)

# ============================================================
# 求解随机响应(时域方法)
# ============================================================
def sdof_random_response(m, c, k, F, dt):
    """求解单自由度系统的随机响应"""
    n = len(F)
    x = np.zeros(n)
    v = np.zeros(n)
    a = np.zeros(n)
    
    # 初始条件
    x[0] = 0
    v[0] = 0
    a[0] = (F[0] - c * v[0] - k * x[0]) / m
    
    # 使用中心差分法
    for i in range(1, n):
        # 预测加速度
        a[i] = (F[i] - c * v[i-1] - k * x[i-1]) / m
        
        # 更新速度和位移
        v[i] = v[i-1] + a[i] * dt
        x[i] = x[i-1] + v[i-1] * dt + 0.5 * a[i-1] * dt**2
    
    return x, v, a

print("\n求解随机响应:")
print("-" * 70)

x, v, a = sdof_random_response(m, c, k, F, dt)

# ============================================================
# 统计分析
# ============================================================
print("\n响应统计分析:")
print("-" * 70)

# 位移统计
x_mean = np.mean(x)
x_std = np.std(x)
x_rms = np.sqrt(np.mean(x**2))
x_max = np.max(np.abs(x))

# 速度统计
v_mean = np.mean(v)
v_std = np.std(v)
v_rms = np.sqrt(np.mean(v**2))
v_max = np.max(np.abs(v))

# 加速度统计
a_mean = np.mean(a)
a_std = np.std(a)
a_rms = np.sqrt(np.mean(a**2))
a_max = np.max(np.abs(a))

print(f"位移统计:")
print(f"  均值: {x_mean*1000:.2f} mm")
print(f"  标准差: {x_std*1000:.2f} mm")
print(f"  RMS: {x_rms*1000:.2f} mm")
print(f"  最大值: {x_max*1000:.2f} mm")

print(f"\n速度统计:")
print(f"  均值: {v_mean:.2f} m/s")
print(f"  标准差: {v_std:.2f} m/s")
print(f"  RMS: {v_rms:.2f} m/s")
print(f"  最大值: {v_max:.2f} m/s")

print(f"\n加速度统计:")
print(f"  均值: {a_mean:.2f} m/s²")
print(f"  标准差: {a_std:.2f} m/s²")
print(f"  RMS: {a_rms:.2f} m/s²")
print(f"  最大值: {a_max:.2f} m/s²")

# ============================================================
# 功率谱密度分析
# ============================================================
print("\n功率谱密度分析:")
print("-" * 70)

# 计算荷载和响应的功率谱密度
freq_F, Pxx_F = signal.welch(F, fs=1/dt, nperseg=1024)
freq_x, Pxx_x = signal.welch(x, fs=1/dt, nperseg=1024)
freq_v, Pxx_v = signal.welch(v, fs=1/dt, nperseg=1024)
freq_a, Pxx_a = signal.welch(a, fs=1/dt, nperseg=1024)

# 计算理论频率响应函数
omega = 2 * np.pi * freq_F
H = 1 / (-m * omega**2 + 1j * c * omega + k)
H_mag = np.abs(H)

# 理论响应PSD
Pxx_x_theory = H_mag**2 * Pxx_F

print(f"  荷载PSD峰值频率: {freq_F[np.argmax(Pxx_F)]:.2f} Hz")
print(f"  位移PSD峰值频率: {freq_x[np.argmax(Pxx_x)]:.2f} Hz")
print(f"  理论峰值频率: {omega_n/(2*np.pi):.2f} Hz")

# ============================================================
# 绘制结果
# ============================================================
print("\n正在生成可视化图表...")

fig1, axes = plt.subplots(2, 2, figsize=(14, 12))

# 1. 荷载时程
axes[0, 0].plot(t, F, 'k-', linewidth=1)
axes[0, 0].set_xlabel('Time (s)', fontsize=11)
axes[0, 0].set_ylabel('Force (N)', fontsize=11)
axes[0, 0].set_title('Random Load Time History', fontsize=12, fontweight='bold')
axes[0, 0].grid(True, alpha=0.3)

# 2. 位移时程
axes[0, 1].plot(t, x*1000, 'b-', linewidth=1)
axes[0, 1].set_xlabel('Time (s)', fontsize=11)
axes[0, 1].set_ylabel('Displacement (mm)', fontsize=11)
axes[0, 1].set_title('Displacement Time History', fontsize=12, fontweight='bold')
axes[0, 1].grid(True, alpha=0.3)

# 3. 速度时程
axes[1, 0].plot(t, v, 'g-', linewidth=1)
axes[1, 0].set_xlabel('Time (s)', fontsize=11)
axes[1, 0].set_ylabel('Velocity (m/s)', fontsize=11)
axes[1, 0].set_title('Velocity Time History', fontsize=12, fontweight='bold')
axes[1, 0].grid(True, alpha=0.3)

# 4. 加速度时程
axes[1, 1].plot(t, a, 'r-', linewidth=1)
axes[1, 1].set_xlabel('Time (s)', fontsize=11)
axes[1, 1].set_ylabel('Acceleration (m/s²)', fontsize=11)
axes[1, 1].set_title('Acceleration Time History', fontsize=12, fontweight='bold')
axes[1, 1].grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig(f'{output_dir}/random_time_history.png', dpi=150, bbox_inches='tight')
plt.close()
print("  随机时程图已保存")

# 功率谱密度图
fig2, axes = plt.subplots(2, 2, figsize=(14, 12))

# 1. 荷载PSD
axes[0, 0].semilogx(freq_F, Pxx_F, 'k-', linewidth=2)
axes[0, 0].set_xlabel('Frequency (Hz)', fontsize=11)
axes[0, 0].set_ylabel('PSD (N²/Hz)', fontsize=11)
axes[0, 0].set_title('Load Power Spectral Density', fontsize=12, fontweight='bold')
axes[0, 0].grid(True, alpha=0.3)

# 2. 位移PSD
axes[0, 1].semilogx(freq_x, Pxx_x, 'b-', linewidth=2, label='Simulation')
axes[0, 1].semilogx(freq_F, Pxx_x_theory, 'r--', linewidth=2, label='Theory')
axes[0, 1].axvline(omega_n/(2*np.pi), color='g', linestyle='--', alpha=0.7, label=f'f_n = {omega_n/(2*np.pi):.2f} Hz')
axes[0, 1].set_xlabel('Frequency (Hz)', fontsize=11)
axes[0, 1].set_ylabel('PSD (m²/Hz)', fontsize=11)
axes[0, 1].set_title('Displacement Power Spectral Density', fontsize=12, fontweight='bold')
axes[0, 1].legend()
axes[0, 1].grid(True, alpha=0.3)

# 3. 速度PSD
axes[1, 0].semilogx(freq_v, Pxx_v, 'g-', linewidth=2)
axes[1, 0].axvline(omega_n/(2*np.pi), color='r', linestyle='--', alpha=0.7, label=f'f_n = {omega_n/(2*np.pi):.2f} Hz')
axes[1, 0].set_xlabel('Frequency (Hz)', fontsize=11)
axes[1, 0].set_ylabel('PSD (m²/s²/Hz)', fontsize=11)
axes[1, 0].set_title('Velocity Power Spectral Density', fontsize=12, fontweight='bold')
axes[1, 0].legend()
axes[1, 0].grid(True, alpha=0.3)

# 4. 加速度PSD
axes[1, 1].semilogx(freq_a, Pxx_a, 'r-', linewidth=2)
axes[1, 1].axvline(omega_n/(2*np.pi), color='g', linestyle='--', alpha=0.7, label=f'f_n = {omega_n/(2*np.pi):.2f} Hz')
axes[1, 1].set_xlabel('Frequency (Hz)', fontsize=11)
axes[1, 1].set_ylabel('PSD (m²/s⁴/Hz)', fontsize=11)
axes[1, 1].set_title('Acceleration Power Spectral Density', fontsize=12, fontweight='bold')
axes[1, 1].legend()
axes[1, 1].grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig(f'{output_dir}/random_psd.png', dpi=150, bbox_inches='tight')
plt.close()
print("  功率谱密度图已保存")

# ============================================================
# 概率分布分析
# ============================================================
print("\n概率分布分析:")
print("-" * 70)

# 计算位移的概率分布
hist_x, bins_x = np.histogram(x, bins=50, density=True)
mid_bins_x = (bins_x[:-1] + bins_x[1:]) / 2

# 理论正态分布
x_normal = np.linspace(np.min(x), np.max(x), 100)
y_normal = (1 / (x_std * np.sqrt(2 * np.pi))) * np.exp(-0.5 * ((x_normal - x_mean) / x_std)**2)

print(f"  位移分布均值: {x_mean*1000:.2f} mm")
print(f"  位移分布标准差: {x_std*1000:.2f} mm")

# 绘制概率分布图
fig3, ax = plt.subplots(figsize=(10, 6))

ax.hist(x*1000, bins=50, density=True, alpha=0.7, color='steelblue', label='Histogram')
ax.plot(x_normal*1000, y_normal*1000, 'r-', linewidth=2, label='Normal Distribution')
ax.set_xlabel('Displacement (mm)', fontsize=11)
ax.set_ylabel('Probability Density', fontsize=11)
ax.set_title('Displacement Probability Distribution', fontsize=12, fontweight='bold')
ax.legend()
ax.grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig(f'{output_dir}/displacement_distribution.png', dpi=150, bbox_inches='tight')
plt.close()
print("  概率分布图已保存")

print("\n" + "=" * 70)
print("随机振动分析完成!")
print("=" * 70)

7. 总结与展望

7.1 本教程总结

本教程系统地介绍了结构动力响应分析的各种方法,包括:

  1. 时域分析方法:详细讲解了直接积分法(如Newmark-β法)的原理和实现,适用于任意荷载和非线性系统。

  2. 频域分析方法:介绍了傅里叶变换、频率响应函数等概念,适用于线性系统和周期性荷载。

  3. 模态分析方法:讲解了模态叠加法的原理和应用,利用结构的模态特性提高计算效率。

  4. 随机振动分析:介绍了随机过程基础、功率谱密度分析和响应统计特性计算。

通过四个Python案例实现,读者可以掌握:

  • Newmark-β法求解动力响应
  • 频率响应分析识别结构共振特性
  • 模态叠加法高效计算多自由度系统响应
  • 随机振动分析评估结构在随机荷载下的性能

7.2 应用前景

结构动力响应分析在工程实践中具有广泛的应用前景:

  • 结构设计:优化结构参数,提高结构的动力性能
  • 抗震设计:评估结构在地震作用下的响应
  • 风振分析:预测结构在风荷载作用下的振动水平
  • 设备振动:分析设备振动对结构的影响
  • 振动控制:为振动控制策略的设计提供依据

7.3 未来发展方向

结构动力响应分析的未来发展方向包括:

  • 非线性动力分析:考虑材料非线性、几何非线性等因素
  • 多物理场耦合分析:如结构-流体耦合、结构-土壤耦合等
  • 不确定性分析:考虑参数不确定性对响应的影响
  • 智能算法应用:如机器学习在动力响应预测中的应用
  • 实时监测与分析:结合传感器技术实现结构响应的实时监测和分析
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐