两自由度振动与动力吸振器仿真器 返回
Interactive Tool — Structural Dynamics

两自由度耦合振动 &
动力吸振器仿真器

实时比较有无调谐质量阻尼器(TMD)的频率响应曲线。通过调整质量比和调谐频率,交互式观察反共振的形成。

主系统参数
主质量 m1
kg
主刚度 k1
固有频率 fn1 随之变化
主系统阻尼比 ζ1
%
动力吸振器(TMD)参数
TMD质量 m2
kg
质量比 μ = m₂/m₁
TMD刚度 k2
N/m
TMD阻尼比 ζ2
%
频率响应函数 |X₁/F| vs 频率 fn1 = —
Frf
系统参数汇总
计算结果
fn1 主系统固有频率 [Hz]
fn2 TMD固有频率 [Hz]
μ 质量比 [–]
fopt 最优TMD频率 [Hz]
2质点系动画 — 耦合振动

理论说明 — 动力吸振器(TMD)原理

动力吸振器通过在主系统固有频率附近调谐附加子质量,将主系统的共振峰分裂为两个较小的峰,使原共振频率处的响应趋近于零(反共振)。

$$f_{TMD}^{opt}= \frac{f_{n1}}{1+\mu}, \quad \zeta_{TMD}^{opt}= \sqrt{\frac{3\mu}{8(1+\mu)^3}}$$

Den Hartog(1956)最优调谐准则。质量比 μ 越大减振效果越好,但需权衡设计约束(重量、空间)。超高层建筑的TMD(如台北101)通常 μ ≈ 0.3〜0.5%。

什么是动力吸振器(TMD)?

🙋
“动力吸振器”听起来好厉害,它到底是什么呀?
🎓
简单来说,它就像一个给振动系统请的“保镖”。比如一台大型风机,转起来会嗡嗡共振,很危险。我们给它额外挂上一个小的质量块(TMD),配上弹簧和阻尼器。当主机开始剧烈振动时,这个“保镖”会立刻开始反向振动,把主机的能量“吸”走,从而保护主机。
🙋
诶,真的吗?那这个“保镖”是怎么知道什么时候该干活呢?
🎓
关键就在于“调谐”。我们把吸振器的固有频率调到和主系统共振频率非常接近。这样,当外界激励频率接近这个频率时,吸振器就会“觉醒”并剧烈运动,在主系统的频率响应曲线上形成一个“反共振”的深谷,振幅就降下来了。你可以在模拟器里拖动“TMD频率比”滑块,亲眼看看这个深谷是如何形成和移动的。
🙋
那是不是吸振器的质量越大越好?我直接挂个很重的“保镖”不就行了?
🎓
这是个好问题,但在工程上不现实。质量太大,结构负担不起,成本也高。这里就引入了“质量比”的概念,即吸振器质量与主系统质量的比值。你试试把模拟器里的“质量比”从0.02调到0.2,会发现虽然主共振峰被压得更低,但会分裂出两个新的、靠得更近的共振峰,系统的有效减振频带会变窄。这就是一个典型的设计权衡。

物理模型与关键公式

系统的运动由两个耦合的二阶微分方程描述,其核心是计算系统在简谐激励下的频率响应函数(FRF),它反映了系统输出(位移)与输入(力)的幅值比随频率的变化关系。

$$H(\omega) = \left| \frac{X_1(\omega)}{F(\omega)}\right|$$

其中,$H(\omega)$ 是频率响应函数,$X_1(\omega)$ 是主质量的位移响应,$F(\omega)$ 是作用在主质量上的激励力,$\omega$ 是激励频率。

为了获得最佳的减振效果,Den Hartog提出了最优调谐准则,给出了使主系统共振峰最小的最优调谐频率和最优阻尼比。

$$f_{\mathrm{TMD}}^{\mathrm{opt}}= \frac{f_{n1}}{1+\mu}, \quad \zeta_{\mathrm{TMD}}^{\mathrm{opt}}= \sqrt{\frac{3\mu}{8(1+\mu)^3}}$$

其中,$f_{n1}$ 是主系统的固有频率,$\mu = m_2 / m_1$ 是质量比,$f_{\mathrm{TMD}}^{\mathrm{opt}}$ 和 $\zeta_{\mathrm{TMD}}^{\mathrm{opt}}$ 分别是TMD的最优调谐频率和最优阻尼比。

现实世界中的应用

超高层建筑抗风:最著名的例子就是台北101大楼。它在87至92楼之间悬挂了一个重达660吨的巨型钢球作为TMD,用来抵消强风或地震引起的楼体摆动。这个“调谐质量阻尼器”已成为一个观光景点。

大跨度桥梁减振:像悬索桥、斜拉桥这类柔性桥梁,在风荷载下容易发生有害的涡激振动或颤振。工程师会在桥塔或桥面内部安装多个大型TMD来增加结构阻尼,确保行车安全和舒适。

工业设备与楼板减振:工厂里的大型冲压机、离心机工作时会产生强烈的周期性激振力。通过在设备基座或厂房楼板下安装TMD,可以有效吸收振动能量,防止设备本身损坏,并避免振动通过楼板传递到其他精密区域。

精密仪器隔振:电子显微镜、光刻机等精密设备对微振动极其敏感。为其工作平台配备小型化的TMD系统,可以隔离来自地面或环境的微小振动干扰,保证测量或加工的精度。

常见误解与注意事项

模型假设:本模拟器所用数学模型基于线性、均质、各向同性等简化假设。在将计算结果直接用于设计决策之前,务必确认实际系统是否满足这些假设。

单位与量纲:许多计算错误源于单位换算错误或数量级判断失误。请时刻注意各参数输入框旁标注的单位。

结果验证:始终将模拟器输出结果与物理直觉或手算结果进行核对。若结果出乎意料,请检查输入参数或采用独立方法进行验证。

进阶学习指引

深化理论:在本工具的简化模型基础上,进一步研究非线性效应、三维行为和时间依赖现象。阅读专业教材和学术论文,掌握严格的数学推导,是提升工程解题能力的关键。

数值方法:系统学习有限元法(FEM)、有限差分法(FDM)和有限体积法(FVM),理解商业CAE求解器的内部运行机制,这将显著提升您设置有效仿真的能力。

实验验证:理论和仿真结果必须通过实验数据加以验证。养成将计算结果与测量值进行对比的习惯,这正是V&V(验证与确认)的精髓所在。

CAE工具:准备好后,可进一步探索Ansys、Abaqus、OpenFOAM、COMSOL等业界主流工具。通过本模拟器培养的物理直觉,将帮助您更有效地配置和使用这些工具。

// 2-DOF coupled oscillation animation (function() { const el = document.getElementById('twoDofAnimCanvas'); const ctx = el.getContext('2d'); let phase = 0; function getParams() { const m1 = parseFloat(document.getElementById('sl-m1').value) || 1000; const k1 = parseFloat(document.getElementById('sl-k1').value) || 1e6; const z1 = (parseFloat(document.getElementById('sl-z1').value) || 2) / 100; const m2 = parseFloat(document.getElementById('sl-m2').value) || 50; const k2 = parseFloat(document.getElementById('sl-k2').value) || 50000; const z2 = (parseFloat(document.getElementById('sl-z2').value) || 5) / 100; const f1 = Math.sqrt(k1 / m1) / (2 * Math.PI); const f2 = Math.sqrt(k2 / m2) / (2 * Math.PI); // Mode 1 ≈ f1, Mode 2 ≈ higher freq const mu = m2 / m1; const fL = f1 / (1 + mu); // lower split peak const fU = f1 * (1 + mu); // upper split peak (approximate) return { m1, k1, z1, m2, k2, z2, f1, f2, fL, fU, mu }; } function resize() { const dpr = window.devicePixelRatio || 1; const w = el.parentElement.clientWidth - 48; const H = Math.round(Math.min(w * 0.45, 300)); if (Math.abs(el.width - w * dpr) > 2 || el.height !== H * dpr) { el.width = w * dpr; el.height = H * dpr; el.style.height = H + 'px'; ctx.setTransform(dpr, 0, 0, dpr, 0, 0); } return { W: w, H }; } function drawSpring(x1, y1, x2, y2, n, color) { const dx = x2 - x1, dy = y2 - y1; const len = Math.sqrt(dx*dx + dy*dy); const nx = -dy/len, ny = dx/len; const amp = 7; ctx.beginPath(); ctx.strokeStyle = color; ctx.lineWidth = 1.5; ctx.moveTo(x1, y1); for (let i = 1; i <= n*2; i++) { const t = i / (n*2); const px = x1 + dx * t + nx * amp * (i % 2 === 0 ? 1 : -1); const py = y1 + dy * t + ny * amp * (i % 2 === 0 ? 1 : -1); ctx.lineTo(px, py); } ctx.lineTo(x2, y2); ctx.stroke(); } function drawMass(cx, cy, w, h, label, color) { ctx.fillStyle = color; ctx.beginPath(); ctx.roundRect(cx - w/2, cy - h/2, w, h, 6); ctx.fill(); ctx.strokeStyle = 'rgba(255,255,255,0.3)'; ctx.lineWidth = 1; ctx.stroke(); ctx.fillStyle = '#fff'; ctx.font = 'bold 12px Roboto Mono, monospace'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText(label, cx, cy); } function frame() { const { W, H } = resize(); const { m1, f1, f2, fL, fU, mu } = getParams(); ctx.clearRect(0, 0, W, H); ctx.fillStyle = '#f8f9fa'; ctx.fillRect(0, 0, W, H); const ceilY = 20; const m1W = Math.min(W * 0.35, 160), m1H = 48; const m2W = Math.min(W * 0.18, 80), m2H = 32; const m1CX = W / 2; const baseY = H - 20; // Two-mode superposition: mode 1 at fL, mode 2 at fU const w1 = 2 * Math.PI * fL; const w2 = 2 * Math.PI * fU; const amp1 = 25; const amp2 = 12; const x1disp = amp1 * Math.sin(w1 * phase) + amp2 * Math.sin(w2 * phase * 0.5); const x2disp = amp1 * Math.sin(w1 * phase) * (1 / (1 + mu)) - amp2 * 1.5 * Math.sin(w2 * phase * 0.5); const m1Y = H * 0.52 + x1disp; const m2Y = m1Y - m1H/2 - m2H/2 - 20 + x2disp; // Ceiling ctx.fillStyle = '#001F3F'; ctx.fillRect(0, ceilY, W, 8); ctx.strokeStyle = '#003875'; ctx.lineWidth = 1; for (let i = 0; i < W; i += 16) { ctx.beginPath(); ctx.moveTo(i, ceilY); ctx.lineTo(i - 10, ceilY - 8); ctx.stroke(); } // Ground ctx.fillStyle = '#001F3F'; ctx.fillRect(0, baseY, W, H - baseY); // Main spring (k1): ceiling to m1 drawSpring(m1CX, ceilY + 8, m1CX, m1Y - m1H/2, 8, '#007BFF'); // TMD spring (k2): m1 to m2 drawSpring(m1CX, m1Y - m1H/2, m1CX, m2Y + m2H/2, 5, '#00B4D8'); // Main mass const m1Color = Math.abs(x1disp) > 18 ? '#e17055' : '#007BFF'; drawMass(m1CX, m1Y, m1W, m1H, 'm₁ (主系)', m1Color); // TMD mass const m2Color = '#00B4D8'; drawMass(m1CX, m2Y, m2W, m2H, 'm₂', m2Color); // Labels ctx.fillStyle = '#6c757d'; ctx.font = '10px Roboto Mono, monospace'; ctx.textAlign = 'left'; ctx.fillText(`k₁ = ${(getParams().k1/1000).toFixed(0)} kN/m`, m1CX + m1W/2 + 8, m1Y); ctx.fillText(`k₂ = ${(getParams().k2/1000).toFixed(1)} kN/m`, m1CX + m2W/2 + 8, m2Y); ctx.fillText(`μ = ${(mu*100).toFixed(1)}%`, 10, 50); ctx.fillText(`f₁ = ${f1.toFixed(2)} Hz`, 10, 64); ctx.textAlign = 'center'; phase += 1 / 60; requestAnimationFrame(frame); } frame(); })();