
How to make an FRF with my accelerometer Data and FFT?
    47 visualizaciones (últimos 30 días)
  
       Mostrar comentarios más antiguos
    
    Jo98
 el 4 de Dic. de 2023
  
    
    
    
    
    Comentada: Mathieu NOE
      
 el 24 de Mzo. de 2025
            Dear, Matlab Users.
I have some inquiries about my accelerometer Data, that was processed by a Dewesoft Software.
So, I am gonna share my code and data, and I would like to know if there any suggestion to this resolution.
Sensitivy: 102 mV/g, or 10.40 mV/m/s^2
%Load data
clear all
load("C:\Users\joant\Desktop\Field_Research\Matlab_Code\data_test.mat")
whos
close all
%Plot the array according to the data
%Data of Channel 1
g1= transpose(Data1_352C33OLD)
t1= transpose(Data1_time_352C33OLD); % time
dt=1/(Sample_rate) % period
L=length(g1)
%Iterate
% Figure of Channel 1
figure(1)
plot(t1,g1,'b.-')
set(gca,'Fontsize',14)
xlabel('time(s)')
ylabel('g')
xlim([min(t1) max(t1)]) 
%ylim([-6 6])
grid on
% Calculate Fourier transform of force
points=round((t1(L))/dt)  %Calculate the points
A_F= g1; % This is the Force for setting in the fft.
freq= (0:1/(dt*points):(1-1/(2*points))/dt)'; %Hz
freq=freq(1:round(points/2+1),:); %Hz
length(freq)
length(fft(A_F))
%In this part I need help
A_fft=fft(g1); %without the conversion by 9.8 m/s^2
abs_A=abs(A_fft)
figure(4)
plot(freq,abs_A(1:length(freq)))
xlim([0 6]) % Because 5 Hz is 300 RPM
%ylim([-400 400])
xlabel('Frequency (Hz)')
ylabel('g')
%According to the practice in the laboratory the result is the following
%image.
This was my process to obtain the FFT, I would like to know if this process is correct?. Because when I plot this data is not the same values in y-axis that I had in the Dewesoft software.
Dewesoft fft(g) (attached) vs matlab fft(g) (attached)
 
  
Question #2
According to a Mechanical vibration book, I have the next equation to find the FRF. I applied this but my FRF y axis value is too high so, I don't know If I didn't applied correctly.  That information is in my code and I use the following equation.

w= natural frequency = 31.42 rad/s^2.
I assumpted that A/F is g because according to the book, it says the next information: "Because the acceloremeter produces a voltage that is proportional to acceleration, we obtain the accelerance, A/F(w), from the dynamic signal analyzer".
My graph result:

This graph it doesn't have sense, because I need values aroun of 0.15-0.17 in the first vibration mode. So, I would like to know if there any suggestion how to resolve this.
Best Regards,
Jo98.
0 comentarios
Respuesta aceptada
  Mathieu NOE
      
 el 4 de Dic. de 2023
        hello 
I got the correct result if I assume that your data is in g and not volts (so your analyser has already used the sensor sensivity when storing the data) 
there can be some minor difference in terms of frequency and amplitude as I am not aware of what type of window / nfft is used on your side  

load("data_test.mat")
%Plot the array according to the data
%Data of Channel 1
signal = double(Data1_352C33OLD);
time= Data1_time_352C33OLD; % time
[samples,channels] = size(signal);
dt = mean(diff(time));
Fs = 1/dt;
%% decimate (if needed)
% NB : decim = 1 will do nothing (output = input)
decim = 10;
if decim>1
    Fs = Fs/decim;
    for ck = 1:channels
    newsignal(:,ck) = decimate(signal(:,ck),decim);
    end
   signal = newsignal;
end
samples = length(signal);
time = (0:samples-1)*1/Fs;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% FFT parameters
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
NFFT = 1000;    % for maximal resolution take NFFT = signal length (but then no averaging possible)
OVERLAP = 0.75; % overlap will be used only if NFFT is shorter than signal length
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% display 1 : time domain plot
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
figure(1),
plot(time,signal,'b');grid on
title(['Time plot  / Fs = ' num2str(Fs) ' Hz ']);
xlabel('Time (s)');ylabel('Amplitude');
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% display 2 : averaged FFT spectrum
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
[freq, spectrum_raw] = myfft_peak(signal,Fs,NFFT,OVERLAP);
figure(2),plot(freq,spectrum_raw,'b');grid on
df = freq(2)-freq(1); % frequency resolution 
title(['Averaged FFT Spectrum  / Fs = ' num2str(Fs) ' Hz / Delta f = ' num2str(df,3) ' Hz ']);
xlabel('Frequency (Hz)');ylabel('Amplitude');
function  [freq_vector,fft_spectrum] = myfft_peak(signal, Fs, nfft, Overlap)
% FFT peak spectrum of signal  (example sinus amplitude 1   = 0 dB after fft).
% Linear averaging
%   signal - input signal, 
%   Fs - Sampling frequency (Hz).
%   nfft - FFT window size
%   Overlap - buffer percentage of overlap % (between 0 and 0.95)
[samples,channels] = size(signal);
% fill signal with zeros if its length is lower than nfft
if samples<nfft
    s_tmp = zeros(nfft,channels);
    s_tmp((1:samples),:) = signal;
    signal = s_tmp;
    samples = nfft;
end
% window : hanning
window = hanning(nfft);
window = window(:);
%    compute fft with overlap 
 offset = fix((1-Overlap)*nfft);
 spectnum = 1+ fix((samples-nfft)/offset); % Number of windows
%     % for info is equivalent to : 
%     noverlap = Overlap*nfft;
%     spectnum = fix((samples-noverlap)/(nfft-noverlap));	% Number of windows
    % main loop
    fft_spectrum = 0;
    for i=1:spectnum
        start = (i-1)*offset;
        sw = signal((1+start):(start+nfft),:).*(window*ones(1,channels));
        fft_spectrum = fft_spectrum + (abs(fft(sw))*4/nfft);     % X=fft(x.*hanning(N))*4/N; % hanning only 
    end
    fft_spectrum = fft_spectrum/spectnum; % to do linear averaging scaling
% one sidded fft spectrum  % Select first half 
    if rem(nfft,2)    % nfft odd
        select = (1:(nfft+1)/2)';
    else
        select = (1:nfft/2+1)';
    end
fft_spectrum = fft_spectrum(select,:);
freq_vector = (select - 1)*Fs/nfft;
end
10 comentarios
  oybozkurt
 el 19 de Nov. de 2024
				Thank you for your quick reply. For example, the acceleration and force values obtained as a result of a hit with a hammer are available in the excel file. Can you check this sample data?
  Mathieu NOE
      
 el 20 de Nov. de 2024
				hello 
so I adapted my code for your experimental data - so there is only one hit in your record, I wish you could redo the test with multiple hits in the same record (make 5/6 hits with approx 5 seconds interval).
the benefit of making multiple hits is to reduce the noise effects and average your response curve.
nevertheless , this is the result obtained so far with your single hit record : 
hope it helps ! 
Results
    Mode    Frequency     Damping 
    ____    _________    _________
     1       66.147      0.0081933
     2       307.36       0.031123
Code : 
%% Modal Analysis ( hammer test ) - FRF computation 
%% load data
filename = "Acceleration-Force.xlsx";
headerlines = 10;
% force data
data = readmatrix(filename,"Sheet","Force","NumHeaderLines",headerlines,"ExpectedNumVariables",2);
timef = data(:,1);
force_signal = data(:,2);
force_unit = 'N';
% accel data
data = readmatrix(filename,"Sheet","Acceleration","NumHeaderLines",headerlines,"ExpectedNumVariables",2);
timea = data(:,1);
accel_signal = data(:,2);
accel_unit = 'g';
if sum(abs(timef - timea))>eps
    error('Time vectors don''t match - double check your data')
else
    time = timef;
    clear timef timea
end
dt = mean(diff(time));
Fs = 1/dt;
samples = numel(force_signal);
channels = size(accel_signal,2);
%% FFT processing parameters
nfft = 1024*4;
% let's trigger the data (using "zero crossing" on the force signal) 
% this is basically the same way a FFT/network analyser operates
pre_trig_duration = 10e-3; % (seconds) - add some time to shift the pulse signal to the right
force_signal_level = 1;
[Zx] = find_zc(time,force_signal,force_signal_level);
Zx = Zx - pre_trig_duration;
%recommended max nfft
if numel(Zx)>1
    recommended_max_nfft = floor(min(Fs*diff(Zx)));
else
    recommended_max_nfft = numel(time);
end
%% check valid segments (duration in samples must be above nfft
% pulses durations in samples : 
pd = [Fs*diff(Zx);  samples-Fs*Zx(end)];% add the last pulse duration : 
data_valid = find(pd>=nfft);
figure(2),
subplot(2,1,1),plot(time,force_signal)
title('force signal');
xlabel('Time (s)');
ylabel(force_unit);
% warning message
if nfft>recommended_max_nfft
    text(max(time)*0.1,max(force_signal)*0.9,['Selected nfft = ' num2str(nfft) ' is above recommended max value : ' num2str(recommended_max_nfft)]);
end
subplot(2,1,2),plot(time,accel_signal)
title('accel signal');
xlabel('Time (s)');
ylabel(accel_unit);
%% FFT processing 
% windowing
windowx = ones(nfft,1); % no window on force signal (or use boxcar)
windowy = ones(nfft,1); % no window on response sensor signal (or use a exp window as suggested below if the signal decay rate is too slow).
% windowy = exp(-3*(0:nfft-1)/nfft)'; % 0.1 exp window on response sensor signal 
% initialisation
Pxx = zeros(nfft,1);
Pxy = zeros(nfft,channels);
Pyy = zeros(nfft,channels);
% check if data_valid is not empty (can happen if you choose nfft grossly
% oversized)
if isempty(data_valid)
    error('No data or nfft too large - check data & nfft settings');
else % we have data and nfft is correctly defined
    for ck = 1:numel(data_valid)
        % do it only for valid segments
        k = data_valid(ck);
            % select time data 
            ind_start = find(time>=Zx(k),1,'first');
            % check valid segments (duration in samples must be above nfft
            ind = (ind_start :ind_start + nfft -1);
            time_measure = time(ind);
            time_measure = time_measure - time_measure(1);
            force_signal_measure = force_signal(ind);
            accel_signal_measure = accel_signal(ind);
            % FFT processing 
            x = force_signal_measure;
            y = accel_signal_measure;
            xw = windowx.*x;
            yw = (windowy*ones(1,channels)).*y;
            Xx = fft(xw,nfft);
            Yy = fft(yw,nfft,1);
            Xx2 = abs(Xx).^2;
            Xy2 = Yy.*(conj(Xx)*ones(1,channels));
            Yy2 = abs(Yy).^2;
            Pxx = Pxx + Xx2;
            Pxy = Pxy + Xy2;
            Pyy = Pyy + Yy2;
    end
    % Select first half 
    if ~any(any(imag([x y])~=0))   % if x and y are not complex
        if rem(nfft,2)    % nfft odd
            select = [1:(nfft+1)/2];
        else
            select = [1:nfft/2+1];   % include DC AND Nyquist
        end
        Pxx = Pxx(select);
        Pxy = Pxy(select,:);
        Pyy = Pyy(select,:);
    else
        select = 1:nfft;
    end
    % set up output parameters
    Txy = Pxy ./ (Pxx*ones(1,channels));             % transfer function estimate 
    Cxy = (abs(Pxy).^2)./((Pxx*ones(1,channels)).*Pyy);             % coherence function estimate 
    f = (select - 1)'*Fs/nfft;
end
%% plot model vs identified FRF
flow = 10;
fhigh = Fs/2.56;
figure(4)
subplot(3,1,1),loglog(f,abs(Txy),'r');
title(['FRF estimation based on valid data chuncks # : ' num2str(data_valid')])
xlim([flow fhigh]);
ylabel(['Magnitude (' accel_unit ' / ' force_unit ')']);
subplot(3,1,2),semilogx(f,180/pi*(angle(Txy)),'r');
xlim([flow fhigh]);
ylabel('Phase (°)');
subplot(3,1,3),semilogx(f,Cxy,'r');
xlim([flow fhigh]);
xlabel('Frequency (Hz)');
ylabel('Coh');
ylim([0 1.1]);
%% natural frequencies & damping ratioes
N = 2 ; % number of (dominant) modes to identify
% only use valide data (coherence above 0.9)
ind = Cxy>0.9;
FR = [20 400];
[fn,dr] = modalfit(Txy(ind),f(ind),Fs,N,'FitMethod','lsrf','FreqRange',FR);
T = table((1:N)',fn,dr,'VariableNames',{'Mode','Frequency','Damping'})
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [Zx] = find_zc(x,y,threshold)
% find time values of positive slope zero (or "threshold" value ) y
% crossing points
% put data in rows
x = x(:);
y = y(:);
% positive slope "zero" crossing detection, using linear interpolation
y = y - threshold;
zci = @(data) find(diff(sign(data))>0);  %define function: returns indices of +ZCs
ix=zci(y);                      %find indices of + zero crossings of x
ZeroX = @(x0,y0,x1,y1) x0 - (y0.*(x0 - x1))./(y0 - y1); % Interpolated x value for Zero-Crossing 
Zx = ZeroX(x(ix),y(ix),x(ix+1),y(ix+1));
end
Más respuestas (2)
  oybozkurt
 el 5 de En. de 2025
        Hello Mathieu,
I have been busy trying your programme for a long time, it is very good but I need your help with a few things. Firstly, in some cases it takes points very close to each other as 1st and 2nd mode. Secondly, is there a document you can recommend to understand the technical content of your programme?
7 comentarios
  Mathieu NOE
      
 el 13 de En. de 2025
				oh yes indeed - I didn't see the excel file was different with more sheets :)
so I changed the code the load the correct sheets and also to display the magnitude plot now in dB as in signalexpress , I also did some changes for figure 3 display 
the commented lines are the previous code version (y log scale) - the new code is magnitude converted in dB ref to the declared units.
then I opted for new modalfit parameters : 
N = 1 ; % number of (dominant) modes to identify
FR = [5 300]; %
I see the same values of the dominant mode as in signalexpress (fn = 30 Hz, T = -34 dB ref 1 g/N)
T = 1×4 table
    Mode    Frequency    Damping    Gain (dB)
    ____    _________    _______    _________
     1       30.028      0.12593     -33.616 

Code updated in attachment 
  oybozkurt
 el 13 de En. de 2025
        Hello Mathieu,
First of all, thank you for your efforts. How accurately does the modalfit command work? When the graphs for ‘FitMethod’,‘lsrf’ and ‘FitMethod’,‘pp’ are carefully examined, the natural frequencies do not exactly coincide with the peaks, and there are significant differences between damping evaluated using ‘lsrf’ and ‘pp’ for the data I attached. Again, is the damping calculated by the method in the picture I attached or by another method.

14 comentarios
Ver también
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!






