Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Ok-landscape
GitHub Repository: Ok-landscape/computational-pipeline
Path: blob/main/latex-templates/templates/mechanical-engineering/vibration_analysis.tex
51 views
unlisted
1
\documentclass[11pt,a4paper]{article}
2
3
% Document Setup
4
\usepackage[utf8]{inputenc}
5
\usepackage[T1]{fontenc}
6
\usepackage{lmodern}
7
\usepackage[margin=1in]{geometry}
8
\usepackage{amsmath,amssymb}
9
\usepackage{siunitx}
10
\usepackage{booktabs}
11
\usepackage{float}
12
\usepackage{caption}
13
\usepackage{hyperref}
14
15
% PythonTeX Setup
16
\usepackage[makestderr]{pythontex}
17
18
\title{Vibration Analysis: SDOF Systems and Modal Analysis}
19
\author{Mechanical Engineering Laboratory}
20
\date{\today}
21
22
\begin{document}
23
\maketitle
24
25
\begin{abstract}
26
This report presents computational analysis of mechanical vibrations including free and forced response of single degree of freedom (SDOF) systems, damping effects, frequency response, and modal analysis. Python-based computations provide quantitative analysis with dynamic visualization.
27
\end{abstract}
28
29
\tableofcontents
30
\newpage
31
32
\section{Introduction to Mechanical Vibrations}
33
34
Vibration analysis is essential for:
35
\begin{itemize}
36
\item Machine design and reliability
37
\item Structural dynamics and earthquake engineering
38
\item Noise and vibration control
39
\item Condition monitoring and diagnostics
40
\end{itemize}
41
42
% Initialize Python environment
43
\begin{pycode}
44
import numpy as np
45
import matplotlib.pyplot as plt
46
from scipy.integrate import odeint
47
from scipy.signal import lti, step, bode
48
49
plt.rcParams['figure.figsize'] = (8, 5)
50
plt.rcParams['font.size'] = 10
51
plt.rcParams['text.usetex'] = True
52
53
def save_fig(filename):
54
plt.savefig(filename, dpi=150, bbox_inches='tight')
55
plt.close()
56
\end{pycode}
57
58
\section{SDOF System Fundamentals}
59
60
\subsection{Equation of Motion}
61
62
For a mass-spring-damper system:
63
\begin{equation}
64
m\ddot{x} + c\dot{x} + kx = F(t)
65
\end{equation}
66
67
Natural frequency and damping ratio:
68
\begin{equation}
69
\omega_n = \sqrt{\frac{k}{m}}, \quad \zeta = \frac{c}{2\sqrt{km}} = \frac{c}{2m\omega_n}
70
\end{equation}
71
72
\begin{pycode}
73
# SDOF system parameters
74
m = 1.0 # kg
75
k = 100 # N/m
76
c = 4.0 # Ns/m
77
78
omega_n = np.sqrt(k/m)
79
zeta = c / (2*np.sqrt(k*m))
80
omega_d = omega_n * np.sqrt(1 - zeta**2)
81
f_n = omega_n / (2*np.pi)
82
83
# Free vibration response
84
def free_vibration(y, t, m, c, k):
85
x, v = y
86
return [v, (-c*v - k*x)/m]
87
88
t = np.linspace(0, 5, 1000)
89
y0 = [0.1, 0] # Initial displacement, zero velocity
90
sol = odeint(free_vibration, y0, t, args=(m, c, k))
91
92
# Analytical solution for underdamped
93
x_analytical = y0[0] * np.exp(-zeta*omega_n*t) * \
94
(np.cos(omega_d*t) + zeta*omega_n/omega_d * np.sin(omega_d*t))
95
96
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
97
98
# Time response
99
ax1.plot(t, sol[:, 0]*1000, 'b-', linewidth=2, label='Numerical')
100
ax1.plot(t, x_analytical*1000, 'r--', linewidth=1, label='Analytical')
101
ax1.plot(t, y0[0]*1000*np.exp(-zeta*omega_n*t), 'k--', linewidth=1, alpha=0.5, label='Envelope')
102
ax1.plot(t, -y0[0]*1000*np.exp(-zeta*omega_n*t), 'k--', linewidth=1, alpha=0.5)
103
ax1.set_xlabel('Time (s)')
104
ax1.set_ylabel('Displacement (mm)')
105
ax1.set_title('Free Vibration Response')
106
ax1.legend()
107
ax1.grid(True, alpha=0.3)
108
109
# Phase portrait
110
ax2.plot(sol[:, 0]*1000, sol[:, 1]*1000, 'b-', linewidth=1)
111
ax2.plot(sol[0, 0]*1000, sol[0, 1]*1000, 'go', markersize=10, label='Start')
112
ax2.plot(sol[-1, 0]*1000, sol[-1, 1]*1000, 'ro', markersize=10, label='End')
113
ax2.set_xlabel('Displacement (mm)')
114
ax2.set_ylabel('Velocity (mm/s)')
115
ax2.set_title('Phase Portrait')
116
ax2.legend()
117
ax2.grid(True, alpha=0.3)
118
119
plt.tight_layout()
120
save_fig('free_vibration.pdf')
121
\end{pycode}
122
123
\begin{figure}[H]
124
\centering
125
\includegraphics[width=\textwidth]{free_vibration.pdf}
126
\caption{Free vibration: time response with envelope decay and phase portrait.}
127
\end{figure}
128
129
\begin{table}[H]
130
\centering
131
\caption{SDOF System Parameters}
132
\begin{tabular}{lcc}
133
\toprule
134
Parameter & Value & Units \\
135
\midrule
136
Natural frequency $\omega_n$ & \py{f"{omega_n:.2f}"} & rad/s \\
137
Natural frequency $f_n$ & \py{f"{f_n:.2f}"} & Hz \\
138
Damping ratio $\zeta$ & \py{f"{zeta:.3f}"} & -- \\
139
Damped frequency $\omega_d$ & \py{f"{omega_d:.2f}"} & rad/s \\
140
\bottomrule
141
\end{tabular}
142
\end{table}
143
144
\section{Effect of Damping}
145
146
\begin{pycode}
147
# Effect of damping ratio on response
148
zeta_values = [0.05, 0.1, 0.2, 0.5, 1.0, 2.0]
149
colors = plt.cm.viridis(np.linspace(0, 1, len(zeta_values)))
150
151
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
152
153
for zeta_i, color in zip(zeta_values, colors):
154
c_i = 2*zeta_i*np.sqrt(k*m)
155
sol_i = odeint(free_vibration, y0, t, args=(m, c_i, k))
156
ax1.plot(t, sol_i[:, 0]*1000, color=color, linewidth=1.5, label=f'$\\zeta$ = {zeta_i}')
157
158
ax1.set_xlabel('Time (s)')
159
ax1.set_ylabel('Displacement (mm)')
160
ax1.set_title('Effect of Damping on Free Response')
161
ax1.legend(fontsize=8)
162
ax1.grid(True, alpha=0.3)
163
164
# Logarithmic decrement
165
# For underdamped systems
166
delta = 2*np.pi*zeta / np.sqrt(1 - zeta**2) if zeta < 1 else 0
167
168
# Peak amplitudes for calculating log decrement
169
peaks_idx = []
170
for i in range(1, len(sol)-1):
171
if sol[i, 0] > sol[i-1, 0] and sol[i, 0] > sol[i+1, 0]:
172
peaks_idx.append(i)
173
174
if len(peaks_idx) >= 2:
175
x1 = sol[peaks_idx[0], 0]
176
x2 = sol[peaks_idx[1], 0]
177
delta_measured = np.log(x1/x2)
178
zeta_measured = delta_measured / np.sqrt(4*np.pi**2 + delta_measured**2)
179
else:
180
delta_measured = 0
181
zeta_measured = 0
182
183
# Show peak decay
184
ax2.semilogy(t[peaks_idx], np.abs(sol[peaks_idx, 0])*1000, 'ro-', linewidth=2, markersize=8)
185
ax2.set_xlabel('Time (s)')
186
ax2.set_ylabel('Peak Amplitude (mm)')
187
ax2.set_title(f'Logarithmic Decrement: $\\delta$ = {delta_measured:.3f}')
188
ax2.grid(True, alpha=0.3)
189
190
plt.tight_layout()
191
save_fig('damping_effect.pdf')
192
\end{pycode}
193
194
\begin{figure}[H]
195
\centering
196
\includegraphics[width=\textwidth]{damping_effect.pdf}
197
\caption{Effect of damping ratio on free vibration and logarithmic decrement measurement.}
198
\end{figure}
199
200
\section{Forced Vibration Response}
201
202
\subsection{Harmonic Excitation}
203
204
For $F(t) = F_0\sin(\omega t)$, the steady-state response is:
205
\begin{equation}
206
X = \frac{F_0/k}{\sqrt{(1-r^2)^2 + (2\zeta r)^2}}, \quad r = \frac{\omega}{\omega_n}
207
\end{equation}
208
209
\begin{pycode}
210
# Frequency response function
211
r = np.linspace(0.01, 3, 500) # Frequency ratio
212
zeta_values = [0.05, 0.1, 0.2, 0.5, 0.7, 1.0]
213
214
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
215
216
# Magnitude
217
for zeta_i in zeta_values:
218
X_ratio = 1 / np.sqrt((1 - r**2)**2 + (2*zeta_i*r)**2)
219
ax1.plot(r, X_ratio, linewidth=1.5, label=f'$\\zeta$ = {zeta_i}')
220
221
ax1.axvline(1, color='k', linestyle='--', alpha=0.5)
222
ax1.set_xlabel('Frequency Ratio $r = \\omega/\\omega_n$')
223
ax1.set_ylabel('Amplitude Ratio $X/(F_0/k)$')
224
ax1.set_title('Frequency Response - Magnitude')
225
ax1.legend(fontsize=8)
226
ax1.grid(True, alpha=0.3)
227
ax1.set_xlim(0, 3)
228
ax1.set_ylim(0, 10)
229
230
# Phase
231
for zeta_i in zeta_values:
232
phi = np.arctan2(2*zeta_i*r, 1 - r**2)
233
ax2.plot(r, np.degrees(phi), linewidth=1.5, label=f'$\\zeta$ = {zeta_i}')
234
235
ax2.axvline(1, color='k', linestyle='--', alpha=0.5)
236
ax2.axhline(90, color='k', linestyle=':', alpha=0.5)
237
ax2.set_xlabel('Frequency Ratio $r = \\omega/\\omega_n$')
238
ax2.set_ylabel('Phase Angle (degrees)')
239
ax2.set_title('Frequency Response - Phase')
240
ax2.legend(fontsize=8)
241
ax2.grid(True, alpha=0.3)
242
243
plt.tight_layout()
244
save_fig('frequency_response.pdf')
245
\end{pycode}
246
247
\begin{figure}[H]
248
\centering
249
\includegraphics[width=\textwidth]{frequency_response.pdf}
250
\caption{Frequency response function showing magnitude and phase for various damping ratios.}
251
\end{figure}
252
253
\subsection{Resonance Analysis}
254
255
\begin{pycode}
256
# Resonance characteristics
257
# Peak frequency and amplitude for underdamped systems
258
zeta_range = np.linspace(0.01, 0.7, 100)
259
r_peak = np.sqrt(1 - 2*zeta_range**2)
260
Q_factor = 1 / (2*zeta_range) # Quality factor
261
262
# Maximum amplitude ratio at resonance
263
X_peak = 1 / (2*zeta_range*np.sqrt(1 - zeta_range**2))
264
265
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
266
267
# Peak frequency ratio
268
axes[0, 0].plot(zeta_range, r_peak, 'b-', linewidth=2)
269
axes[0, 0].set_xlabel('Damping Ratio $\\zeta$')
270
axes[0, 0].set_ylabel('Peak Frequency Ratio $r_{peak}$')
271
axes[0, 0].set_title('Resonance Peak Location')
272
axes[0, 0].grid(True, alpha=0.3)
273
274
# Quality factor
275
axes[0, 1].semilogy(zeta_range, Q_factor, 'r-', linewidth=2)
276
axes[0, 1].set_xlabel('Damping Ratio $\\zeta$')
277
axes[0, 1].set_ylabel('Quality Factor $Q$')
278
axes[0, 1].set_title('Quality Factor vs Damping')
279
axes[0, 1].grid(True, alpha=0.3)
280
281
# Half-power bandwidth
282
bandwidth = 2*zeta_range
283
axes[1, 0].plot(zeta_range, bandwidth, 'g-', linewidth=2)
284
axes[1, 0].set_xlabel('Damping Ratio $\\zeta$')
285
axes[1, 0].set_ylabel('Half-Power Bandwidth $\\Delta r$')
286
axes[1, 0].set_title('Half-Power Bandwidth')
287
axes[1, 0].grid(True, alpha=0.3)
288
289
# Time domain at resonance
290
omega_f = omega_n # Forcing at natural frequency
291
t_res = np.linspace(0, 10, 2000)
292
293
def forced_vibration(y, t, m, c, k, F0, omega):
294
x, v = y
295
F = F0 * np.sin(omega * t)
296
return [v, (F - c*v - k*x)/m]
297
298
F0 = 10 # N
299
sol_res = odeint(forced_vibration, [0, 0], t_res, args=(m, c, k, F0, omega_f))
300
301
axes[1, 1].plot(t_res, sol_res[:, 0]*1000, 'b-', linewidth=1)
302
axes[1, 1].set_xlabel('Time (s)')
303
axes[1, 1].set_ylabel('Displacement (mm)')
304
axes[1, 1].set_title(f'Response at Resonance ($\\omega = \\omega_n$)')
305
axes[1, 1].grid(True, alpha=0.3)
306
307
plt.tight_layout()
308
save_fig('resonance.pdf')
309
310
# Peak amplitude at resonance
311
X_resonance = F0/k / (2*zeta)
312
\end{pycode}
313
314
\begin{figure}[H]
315
\centering
316
\includegraphics[width=\textwidth]{resonance.pdf}
317
\caption{Resonance characteristics: peak location, quality factor, bandwidth, and time response.}
318
\end{figure}
319
320
\section{Transmissibility}
321
322
Force transmitted to foundation:
323
\begin{equation}
324
TR = \frac{F_T}{F_0} = \sqrt{\frac{1 + (2\zeta r)^2}{(1-r^2)^2 + (2\zeta r)^2}}
325
\end{equation}
326
327
\begin{pycode}
328
# Transmissibility
329
r = np.linspace(0.01, 4, 500)
330
zeta_values = [0.05, 0.1, 0.2, 0.5]
331
332
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
333
334
# Force transmissibility
335
for zeta_i in zeta_values:
336
TR = np.sqrt((1 + (2*zeta_i*r)**2) / ((1 - r**2)**2 + (2*zeta_i*r)**2))
337
ax1.plot(r, TR, linewidth=2, label=f'$\\zeta$ = {zeta_i}')
338
339
ax1.axhline(1, color='k', linestyle='--', alpha=0.5)
340
ax1.axvline(np.sqrt(2), color='r', linestyle=':', alpha=0.5, label='$r = \\sqrt{2}$')
341
ax1.set_xlabel('Frequency Ratio $r$')
342
ax1.set_ylabel('Transmissibility $TR$')
343
ax1.set_title('Force Transmissibility')
344
ax1.legend()
345
ax1.grid(True, alpha=0.3)
346
ax1.set_ylim(0, 5)
347
348
# Isolation efficiency
349
isolation = (1 - 1/TR) * 100 # When TR < 1 (r > sqrt(2))
350
for zeta_i in [0.1, 0.2]:
351
TR_i = np.sqrt((1 + (2*zeta_i*r)**2) / ((1 - r**2)**2 + (2*zeta_i*r)**2))
352
isolation_i = np.maximum(0, (1 - TR_i) * 100)
353
ax2.plot(r, isolation_i, linewidth=2, label=f'$\\zeta$ = {zeta_i}')
354
355
ax2.axvline(np.sqrt(2), color='r', linestyle=':', alpha=0.5)
356
ax2.set_xlabel('Frequency Ratio $r$')
357
ax2.set_ylabel('Isolation Efficiency (\\%)')
358
ax2.set_title('Vibration Isolation')
359
ax2.legend()
360
ax2.grid(True, alpha=0.3)
361
362
plt.tight_layout()
363
save_fig('transmissibility.pdf')
364
\end{pycode}
365
366
\begin{figure}[H]
367
\centering
368
\includegraphics[width=\textwidth]{transmissibility.pdf}
369
\caption{Force transmissibility and vibration isolation efficiency.}
370
\end{figure}
371
372
\section{Two-DOF System (Modal Analysis)}
373
374
\begin{pycode}
375
# Two-DOF system
376
m1 = m2 = 1.0 # kg
377
k1 = k2 = k3 = 100 # N/m
378
379
# Mass and stiffness matrices
380
M = np.array([[m1, 0], [0, m2]])
381
K = np.array([[k1+k2, -k2], [-k2, k2+k3]])
382
383
# Eigenvalue problem
384
eigenvalues, eigenvectors = np.linalg.eig(np.linalg.inv(M) @ K)
385
omega_natural = np.sqrt(eigenvalues)
386
omega_sorted = np.sort(omega_natural)
387
388
# Mode shapes
389
idx = np.argsort(eigenvalues)
390
modes = eigenvectors[:, idx]
391
modes = modes / modes[0, :] # Normalize to first component
392
393
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
394
395
# Mode shape visualization
396
x = [0, 1, 2] # Node positions
397
mode1 = [0, modes[0, 0], modes[1, 0]]
398
mode2 = [0, modes[0, 1], modes[1, 1]]
399
400
axes[0, 0].plot(x, mode1, 'bo-', linewidth=2, markersize=10, label=f'Mode 1: $\\omega$ = {omega_sorted[0]:.2f} rad/s')
401
axes[0, 0].axhline(0, color='k', linestyle='-', linewidth=0.5)
402
axes[0, 0].set_xlabel('DOF')
403
axes[0, 0].set_ylabel('Mode Shape Amplitude')
404
axes[0, 0].set_title('First Mode Shape')
405
axes[0, 0].legend()
406
axes[0, 0].grid(True, alpha=0.3)
407
408
axes[0, 1].plot(x, mode2, 'ro-', linewidth=2, markersize=10, label=f'Mode 2: $\\omega$ = {omega_sorted[1]:.2f} rad/s')
409
axes[0, 1].axhline(0, color='k', linestyle='-', linewidth=0.5)
410
axes[0, 1].set_xlabel('DOF')
411
axes[0, 1].set_ylabel('Mode Shape Amplitude')
412
axes[0, 1].set_title('Second Mode Shape')
413
axes[0, 1].legend()
414
axes[0, 1].grid(True, alpha=0.3)
415
416
# Free response of 2-DOF system
417
def two_dof_system(y, t, M, K, C):
418
x = y[:2]
419
v = y[2:]
420
a = np.linalg.inv(M) @ (-C @ v - K @ x)
421
return list(v) + list(a)
422
423
C = 0.1 * K # Proportional damping
424
y0_2dof = [0.1, 0, 0, 0] # Initial conditions
425
t_2dof = np.linspace(0, 5, 1000)
426
sol_2dof = odeint(two_dof_system, y0_2dof, t_2dof, args=(M, K, C))
427
428
axes[1, 0].plot(t_2dof, sol_2dof[:, 0]*1000, 'b-', linewidth=1.5, label='Mass 1')
429
axes[1, 0].plot(t_2dof, sol_2dof[:, 1]*1000, 'r-', linewidth=1.5, label='Mass 2')
430
axes[1, 0].set_xlabel('Time (s)')
431
axes[1, 0].set_ylabel('Displacement (mm)')
432
axes[1, 0].set_title('2-DOF Free Response')
433
axes[1, 0].legend()
434
axes[1, 0].grid(True, alpha=0.3)
435
436
# FFT to identify natural frequencies
437
from scipy.fft import fft, fftfreq
438
N = len(t_2dof)
439
dt = t_2dof[1] - t_2dof[0]
440
yf = fft(sol_2dof[:, 0])
441
xf = fftfreq(N, dt)
442
443
axes[1, 1].plot(xf[:N//2], 2/N * np.abs(yf[:N//2]), 'b-', linewidth=1.5)
444
axes[1, 1].axvline(omega_sorted[0]/(2*np.pi), color='r', linestyle='--', alpha=0.5)
445
axes[1, 1].axvline(omega_sorted[1]/(2*np.pi), color='r', linestyle='--', alpha=0.5)
446
axes[1, 1].set_xlabel('Frequency (Hz)')
447
axes[1, 1].set_ylabel('Amplitude')
448
axes[1, 1].set_title('Frequency Content (FFT)')
449
axes[1, 1].set_xlim(0, 5)
450
axes[1, 1].grid(True, alpha=0.3)
451
452
plt.tight_layout()
453
save_fig('modal_analysis.pdf')
454
\end{pycode}
455
456
\begin{figure}[H]
457
\centering
458
\includegraphics[width=\textwidth]{modal_analysis.pdf}
459
\caption{Modal analysis of 2-DOF system: mode shapes, time response, and FFT.}
460
\end{figure}
461
462
Natural frequencies: $\omega_1 = \py{f"{omega_sorted[0]:.2f}"}$ rad/s, $\omega_2 = \py{f"{omega_sorted[1]:.2f}"}$ rad/s
463
464
\section{Conclusions}
465
466
This analysis demonstrates key aspects of vibration analysis:
467
\begin{enumerate}
468
\item SDOF systems are characterized by natural frequency and damping ratio
469
\item Logarithmic decrement provides experimental damping measurement
470
\item Resonance occurs near natural frequency with phase shift of 90 degrees
471
\item Vibration isolation requires $r > \sqrt{2}$
472
\item MDOF systems have multiple natural frequencies and mode shapes
473
\item FFT analysis identifies frequency content from time domain data
474
\end{enumerate}
475
476
\end{document}
477
478