Universidade Federal do Rio Grande do Sul (UFRGS)
Programa de Pós-Graduação em Engenharia Civil (PPGEC)
1. Introduction
2. Formal definition
3. Fourier transform of some basic functions
3.1. Constant function
3.2. Sine and cosine functions
4. Transformation of derivatives
5. Convolution and translation theorems
6. The Fast Fourier Transform (FFT)
7. The spectral density and the periodogram
7.1. Definition
7.2. Example with a white noise
7.3. Example with an accelerometer signal
8. Assignment
Prof. Marcelo M. Rocha, Dr.techn. (ORCID)
Porto Alegre, RS, Brazil
# Importing Python modules required for this notebook
# (this cell must be executed with "shift+enter" before any other Python cell)
import numpy as np
import matplotlib.pyplot as plt
from MRPy import MRPy
To understand the Fourier transform we can firstly recall the Fourier series concept, according which any periodic function with period T can be expressed as a sum of sines and cosines:
f(t)=a0+∞∑k=1akcosωkt+bksinωktwith f(t)=f(t+T) and ωk=2kπ/T, where:
a0=1T∫T/2−T/2f(t)dtak=2T∫T/2−T/2cosωktf(t)dtbk=2T∫T/2−T/2sinωktf(t)dtwith k=1,2,… ∞. Recalling that sine and cosine functions have zero mean implies that the series coefficient a0 represents the mean value of function f(t). For example, it can be shown that the Fourier series expansion of a zero mean square wave is:
f(t)=4π∞∑k=1,3,5...1ksinω2k+1twhich can be verified with some few Python lines as follows. Firstly we define a zero mean square wave with periodicity T=1s:
print(9//2, 9%2)
4 1
T = 1 # function periodicity in seconds
N = 1000 # number of discretization points
t = np.linspace(0, T, N)
f1 = np.zeros(N)
f1[:N//2] = 2.0 # first half period is filled
f1 = f1 - 1.0 # f1 has zero mean and amplitude one
plt.plot(t,f1)
plt.grid(True)
Then we reconstitutes this same function from Fourier series as given before, but limiting
the infinite summation to only Nk
terms:
Nk = 11
f2 = np.zeros(N)
for k in range(1, Nk+1, 2):
wk = 2*k*np.pi/T
f2 += np.sin(wk*t)/k
f2 *= 4/np.pi
plt.figure(1, figsize=(8, 6), clear=True)
plt.plot(t, f1, 'b', t, f2, 'r')
plt.xlim( 0, T); plt.xlabel('time (s)')
plt.ylim(-2, 2); plt.ylabel('f(t)')
plt.grid(True)
The approximation accuracy can be explored by changing the value assigned to Nk
in
the calculation above.
It must be emphasized that the series coefficients ak and bk hold information about how to reconstitutes the original function f(t), so they may be seen as transformed functions of variable ωk instead of variable t. We will come back later showing how to numerically estimate the Fourier coefficients for any periodic function.
Once the Fourier series concept has been understood, the Fourier transform is presented in the next section as a extension of the Fourier series concept for T→∞.
The Fourier series expansion can also be expressed for any real or complex function f(t) by replacing the sine and cosine functions in the previous definition with the Euler's formula:
eiωkt=cosωkt+isinωktwhat gives:
f(t)=∞∑k=−∞Fkeiωktwhere:
Fk=1T∫T/2−T/2e−iωktf(t)dtwith k=0,±1,±2,…±∞.
Now the function periodicity is allowed to increase to infinite to yield the following limits:
T→∞ωk=kΔω→ωΔω=2π/T→dωand also the definition:
limT→∞(TFk)=limΔω→0(2πΔωFk)=F(ω)which replaced on the series expansion for f(t) gives:
f(t)=limΔω→0[12π∞∑k=−∞eiωkt(2πΔωFk)Δω]and hence:
f(t)=12π∫∞−∞eiωtF(ω)dω=F−1{F(ω)}with:
F(ω)=∫∞−∞e−iωtf(t)dt=F{f(t)}which are the inverse and the direct Fourier Transform definitions, respectively. Observing that both integral bounds are infinite, the function f(t) must fulfill some special conditions such that the integral convergence can be ensured. The most important condition is that the integral of |f(t)| over the complete domain exists:
∃∫∞−∞|f(ξ)|dξ∈Calthough in some special cases the transformation is possible even if this condition is not fulfilled.
The transform of a given function and its inverse constitutes a so-called transform pair, usually represented as:
f(t)⟺F(ω)While the use of Laplace transforms mostly rely upon lookup tables or CAS (Computer Algebra Systems), the Fourier transform, both direct and inverse, is usually evaluated by means of numerical techniques. Nevertheless, some fundamental functions and theorems must receive special attention as follows.
The constant function, f(t)=c, does not fulfill the condition for the transform existence (its integral over the whole domain is infinite), but by assuming that the inverse transform exists:
c=12π∫∞−∞eiωtF(ω)dωit can be observed that the only function satifying the integral is the Dirac's Delta:
F(ω)=2πcδ(ω)Recalling that the Fourier transform is a linear operator, the function f(t) can be decomposed as its mean value superposed to a fluctuation:
f(t)=ˉf+˜f(t)This implies that, whenever the function has a non-zero mean value, its Fourier transform will also present an impulse function at the origin, ω=0. We will numerically demonstrate this result later on.
The trigonometric functions sin(ω0t) and cos(ω0t) also do not fulfill the condition for transform existence but, in the same way as for the constant function, a meaningful transform can be found. Observe that a given ω0 is being used instead of the independent variable ω.
Let us assume that the inverse transform of the Euler's formula exists:
f(t)=eiω0t=12π∫∞−∞eiωtF(ω)dωthen again the only function satisfying this integral is the Dirac's Delta at ω0.
F(ω)=2πδ(ω−ω0)By going back from Euler's formula it can be easily demonstrated that for the cosine function it gives:
F{cos(ω0t)}=π[δ(ω+ω0)+δ(ω−ω0)]and similarly for the sine function:
F{sin(ω0t)}=iπ[δ(ω+ω0)−δ(ω−ω0)]These two transforms are depicted below.
It can be seen that the cosine transform converges to the constant function transform as the frequency ω0 goes to zero. These results will also be demonstrated after we introduce the numerical approach to the Fourier transform.
The transformation of derivatives is essential for using Fourier transform for solving differential equations. The transform of a derivative is expressed as:
F{˙f(t)}=∫∞−∞e−iωt˙f(t)dtThis can be solved through integration by parts by defining:
u=e−iωtdv=˙f(t)dtdu=−iωe−iωtdtv=f(t)and replacing in:
∫udv=uv−∫vduAssuming that the function f(t) may be zero at the far ends of the integration domain implies that the product uv may be considered to vanish and hence:
∫∞−∞e−iωt˙f(t)dt=0+iω∫∞−∞e−iωtf(t)dtand hence:
F{˙f(t)}=iωF(ω)For solving the dynamic equilibrium equation of linear systems, the second time derivative of f(t) will also be necessary. Applying again the derivation rule results:
F{¨f(t)}=−ω2F(ω)Time derivatives of higher order can be calculated but will not be necessary in the present context.
The convolution and translation theorems previously presented for Laplace transform also apply to the Fourier transform. The convolution reads:
F{f(t)∗h(t)}=F(ω)H(ω)with:
f(t)∗h(t)=∫∞−∞h(t−τ)f(τ)dτwhere we are intentionally changing the notation from ˉg(s) and g(t) (the unit impulse response function in Laplace Transform theory previously presented) to H(ω) and h(t), which will get a particular meaning in the frequency domain analysis with Fourier Transform.
As for the Laplace Transform, the translation in frequency and time domain reads:
F{eω0tf(t)}=F(ω−ω0)These theorems will be of little use in this course.
While there is no explicit form for the inverse Laplace transform, it was shown above that the same does not happen for the Fourier transform. This means that it can be numerically evaluated in both ways by means of a time and frequency domains discretization:
ωk=k(2πT),k=0,1,2,…,N−1tj=j(TN),j=0,1,2,…,N−1where N is the length of a time series fj=f(tj) with total duration T. The numerical calculation of the direct and inverse transforms are then:
Fk=N−1∑j=0fjexp(−iωktj)fj=1NN−1∑k=0Fkexp(iωktj)Observe that all the computation above requires N2 evaluations of the
trigonometric function exp(i2πkj/N).
Recognizing that the multiple permutations of indices k and i could be
accounted to save computational time inspired the famous FFT algorithm by
Cooley and Tukey,
which may reduce the number of exponential function evaluations up to 2N.
This algorithm is now implemented in many digital signal processing programs
and is also available in the numpy
and scipy
modules in Python.
For example, let us calculate the FFT of a constant function, as presented earlier:
N = 8 # length of time series
f1 = np.ones(N) # assigning constant value 1
F = np.fft.fft(f1) # calling FFT numpy function from fft submodule
f2 = np.fft.ifft(F) # inverse transform
np.set_printoptions(precision=3)
print(f1)
print(F.real)
print(F.imag)
print(f2.real)
[1. 1. 1. 1. 1. 1. 1. 1.] [8. 0. 0. 0. 0. 0. 0. 0.] [0. 0. 0. 0. 0. 0. 0. 0.] [1. 1. 1. 1. 1. 1. 1. 1.]
what shows that the result is a real impulse function (discrete Dirac's Delta) at the origin.
Let us now take a look at the cosine function:
T = 1 # series total duration
N = 128 # length of time series
f0 = 2 # sine/cosine frequency (in Hertz)
k = np.arange(0, N+1) # required for f_k calculation
ti = k*T/N # discrete time domain
fk = k/T # discrete frequency domain
ci = np.sin(2*np.pi*f0*ti)
Ck = np.fft.fft(ci)
plt.figure(2, figsize=(8, 4), clear=True)
plt.plot(ti, ci)
plt.xlim( 0, T); plt.xlabel('time (s)')
plt.ylim(-2, 2); plt.ylabel('f(t)')
plt.grid(True)
plt.figure(3, figsize=(8, 6), clear=True)
plt.subplot(2,1,1)
plt.plot(fk, Ck.real)
plt.xlim( 0, N/T); plt.xlabel('frequency (Hz)')
plt.ylim(-N, N ); plt.ylabel('Real(F_k)')
plt.grid(True)
plt.subplot(2,1,2)
plt.plot(fk, Ck.imag)
plt.xlim( 0, N/T); plt.xlabel('frequency (Hz)')
plt.ylim(-N, N ); plt.ylabel('Imag(F_k)')
plt.grid(True)
The imaginary part of the result above, which refers to the sine transform,
is a numerical error caused by the coarse representation of the cosine function.
Try increasing the discretization (larger N
) and observe what happens.
There are some few important issues to better understand the numerical result above:
Given an undamped oscillator with mass m and stifness k=mω2, subjected to force F(t). The dynamic equilibrium equation is:
¨u+ω2u=F(t)/m=f(t)Its internal energy, w(t), is given at any moment by the sum of kinectic and elastic energies as:
w(t)=12m[˙u2+ω2u2]Recalling now the system solution with the convolution integral:
u(t)=1ω∫∞−∞f(τ)sinωn(t−τ)dτand calculating its time derivative with the Leibnitz' rule:
˙u(t)=∫∞−∞f(τ)cosω(t−τ)dτgives that:
w(t)=12m{[∫∞−∞f(τ)cosω(t−τ)dτ]2+[∫∞−∞f(τ)sinω(t−τ)dτ]2}Applying the suitable trigonometric relations to cosω(t−τ) and sinω(t−τ) and doing some algebraic work leads to:
w(t)∝[∫∞−∞f(τ)cosωτdτ]2+[∫∞−∞f(τ)sinωτdτ]2By recalling Euler's formula it can be recognized that the right hand side is the squared absolute value for the inverse Fourier transform of F(ω), what means that |F(ω)|2 is a measure of energy for a physical system subjected to excitation f(t). We call it the Power Spectral Density of f(t), SF(ω):
SF(ω)∝|F(ω)|2The missing proportion constant is chosen such that the total area under the spectral density for ω>0 matches the variance (squared standard deviation) σ2F of f(t):
∫∞0SF(ω)dω=σ2FWhenever a time series fi with length N is available, there are many statistical
estimators for SF(ω).
The most basic of such estimators is the periodogram, directly calculate with the series
FFT.
This estimator is available in the MRPy
module as a class method.
The following example shows how use the MRPy
module to simulate a (almost) perfect white
noise, and to how visualize the simulated signal both in time and frequency domain using
the quick visualization methods.
X = MRPy.white_noise(1, 1024, Td=8) # white noise simulation
#X = MRPy(np.random.normal(0, 1, 1024), Td=8)
f1 = X.plot_time(1, figsize=(8,3), axis_t=(0, 8, -3, 3 )) # plot in time domain
f2 = X.plot_freq(2, figsize=(8,3), axis_f=(0, 64, 0, 0.02)) # plot in frequency domain
SX, fs = X.periodogram()
f = X.f_axis()
plt.figure(10)
plt.plot(f, SX[0]);
plt.grid(True)
sX2 = np.trapz(SX[0], f)
print(np.sqrt(sX2))
0.9993700392857929
This additional example shows how use the MRPy
module to read a file with some
acquired accelerometer signal, and how to visualize the read signal both in time and frequency domain
using manually formatted plots.
# Reads csv file with accelerometer signal and isolates component a_z
data = MRPy.from_file('resources/data/iNVH001', form='invh').zero_mean()
az = MRPy(data[2], data.fs)#extract((2.5,5), by='time')
t = az.t_axis()
plt.figure(4, figsize=(8, 3), clear=True)
plt.plot(t, az[0], lw=0.5)
plt.xlim(0, az.Td); plt.xlabel('time (s)')
plt.ylim(-15, 15); plt.ylabel('a_z (m/s^2)')
plt.grid(True)
# Calls method for periodogram calculation and visualize
Saz, fs = az.periodogram()
f = az.f_axis()
plt.figure(5, figsize=(8, 3), clear=True)
plt.plot(f, Saz[0], lw=0.5)
plt.xlim(0, 20); plt.xlabel('frequency (Hz)')
plt.ylim(0, 5); plt.ylabel('S_az (power)')
plt.grid(True)
# Search for frequency associated to peak of spectrum
kf = Saz[0].argmax()
print('Frequency at spectrum peak: {0:5.2f}Hz'.format(f[kf]))
Frequency at spectrum peak: 8.30Hz
From the plots above it may be concluded that the accelerometer signal has a strong energy density around 8Hz, what may indicate that the mechanical system presents a natural vibration frequency close to this value.
MRPy
e apresentar os periodogramas
(estimativas das densidades espectrais), verificando qual a frequência
natural de vibração livre do objeto instrumentado pelo pico do gráfico.