FFT plotting trouble: Concatenated inputs

2 ビュー (過去 30 日間)
Keegan Murphy
Keegan Murphy 2024 年 6 月 10 日
回答済み: Mathieu NOE 2024 年 6 月 11 日
I am currently working on tap testing and am running into an issues with my FFT now that I am trying to Concatenate them to improve the "resolution". The plots max values fit what I expect however i need it to be a line and not have all the points underneath. I'm uncertian if my issue is in my measurements or in my code.
clear all
close all
clc
% each channel is notated with _n where n is the channel
% Hammer is on channel 1
% Accelerometer is on channel 2
% dataset_plot(11,'y')
% dataset_plot(12,'y')
% dataset_plot(13,'y')
%
% dataset_plot(21,'y')
dataset_plot(22,'y')
loading 22_y_1_1.csv loading 22_y_2_1.csv loading 22_y_3_1.csv loading 22_y_4_1.csv loading 22_y_5_1.csv
Warning: Integer operands are required for colon operator when used as index.
Warning: Integer operands are required for colon operator when used as index.
The goal is to have a single clear line so that I can create the transfer function to describe the system.
function dataset_plot(n,direction)
%% data Loading
out_2 = [];
out_1 = [];
for f_n = 1:5
fprintf('loading '+ string(n) + '_' + direction + '_' + string(f_n) + '_1.csv'+'\n')
raw_1 = table2array(readtable(string(n)+'_' + direction + '_' + string(f_n) + '_1.csv'));% read in hammer
raw_cut_1 = raw_1(3:height(raw_1),:); %remove header
if f_n == 1 %if its the first loop find signal length for window
window = hanning(length(raw_cut_1));
sample_rate = mean(diff(raw_cut_1(:,1))); %time between samples
Fs = 1/sample_rate; %Frequency in HZ
end
% Zero correction
Hammer_Zeroing = raw_cut_1(1:(length(raw_cut_1(:,2))*0.2)-1);%fix the offset
Hammer_adj = mean(Hammer_Zeroing);
raw_cut_1(:,2) = raw_cut_1(:,2) - (Hammer_adj);
%concatenate
out_1 = [out_1; raw_cut_1(:,2).*hanning(length(raw_cut_1))];
raw_2 = table2array(readtable(string(n)+'_' + direction + '_' + string(f_n) + '_2.csv'));% read in accel
raw_cut_2 = raw_2(3:height(raw_1),:); %remove header
%Zero correction
Accel_Zeroing = raw_cut_2(1:(length(raw_cut_2(:,2))*0.2)-1);%fix the offset
Accel_adj = mean(Accel_Zeroing);
raw_cut_2(:,2) = raw_cut_2(:,2) - (Accel_adj);
%concatenate
out_2 = [out_2; raw_cut_2(:,2).*hanning(length(raw_cut_2))];
clear raw_cut_2 raw_cut_1 raw_1 raw_2 padding Hammer_adj Hammer_Zeroing Accel_adj Accel_Zeroing
end
%% create X axis
Hammer = out_1;%(:,2);
Accel = out_2;%(:,2);
x_axis = linspace(0,sample_rate*length(Accel),length(Accel));
Accel = vertcat(Accel,ones(length(x_axis)-length(Accel),1).*mean(diff(Accel)));
meas_time_2 = sample_rate*length(x_axis);
%% run FFT functions
[H_f,H_P1] = FFT_ploting(smoothdata(Hammer,"gaussian",20),meas_time_2);
[A_f,A_P1] = FFT_ploting(smoothdata(Accel,"gaussian",20),meas_time_2);
%% fix units
H_P1 = H_P1./0.00225; %convert from V to N @ 2.25 mV/N
A_P1 = A_P1./0.0102; %convert from V to m/s^2 10.2 mV/(m/s²)
%% plot
close all
figure('Name','raw data check')
hold on
plot(Accel)
plot(smoothdata(Accel,"gaussian",10))
figure('Name','FFT Plots')
hold on
yyaxis left
plot(H_f,H_P1);
ylabel 'lbf'
yyaxis right
plot(A_f,A_P1);
ylabel 'm/s^2'
grid on
xlim([5 3000]); %accelerometer limit of 3000Hz
%we are focusing on 5 to 500 Hz
title('Accelerometer & Hammer FFT')
legend('Hammer FFT','Acceleromter FFT')
xlabel('Hz');
end
function [f,P1] = FFT_ploting(fun_data,meas_time)
% count = count + 1; %FFT itteration count
Fs = (meas_time/length(fun_data))^-1; %frequecy in Hz
T = 1/Fs; %meas time between samples (inverse)
L = length(fun_data);
t = (0:L-1)*T;
f = Fs*(0:(L-1)/2)/L;
fft_data = fft(fun_data,L);
P2 = (abs(fft_data/L));
P1 = P2(1:(L+1)/2);
P1(2:end) = 2*P1(2:end);
end

採用された回答

Mathieu NOE
Mathieu NOE 2024 年 6 月 11 日
hello
when you do modal analysis / FRF measurements with a hammer , you should split the record into individual hit input and output signals , then do the averaging of the FRFs (and coherence)
I may not directly give the code for your data , but the process is described here , based on the simulated response of a mass / spring / dash pot system. You can see the measured FRF pretty much overlays with the theoretical Bode plot of the system
%% Modal Analysis / hammer test / FRF acquisition and FRF computation tool
%% FFT processing parameters
Fs = 1024*4;
nfft = 1024*2;
channels = 1;
% dummy hammer test data - or real data if you have
Nhits = 8;
duration_hits = 1; % mean time duration between hits (in seconds)
% Mass / spring / dash pot model
M = 25; % kgs
fn = 100; % Hz (natural frequency)
K = M*(2*pi*fn)^2;
C = 0.05*sqrt(K*M);
% analog model acceleration vs force
num = [1 0 0];
den = [M C K];
% discretization
dt = 1/Fs;
samples_one_hit = duration_hits*Fs;
[numd,dend] = c2dm(num,den,1/Fs,'tustin');
% create some dummy hammer gaussian pulses with variable force amplitude
f0 = Fs/2.5; % pulse frequency
Var_Sq = 1; % pulse variance (squared)
t = dt*(0:samples_one_hit-1)';
t_pulse = max(t)/8;
offset = 0.5*duration_hits*(rand(Nhits,1)); % random time shifts
amplitude = 10*(1 + 1*(rand(Nhits,1))); % random amplitude
force_signal = [];
for k = 1:Nhits
pulse_signal = amplitude(k)*exp(-(((t-t_pulse-offset(k)).^2).*(f0^2))./Var_Sq);
force_signal = [force_signal;pulse_signal];
end
% now all samples are concatenated to make one signal with random delta t
% between pulses
samples = numel(force_signal);
time = dt*(0:samples-1)';
% apply model to force signal (input) to create acceleration signal (output)
accel_signal = filter(numd,dend,force_signal);
accel_signal = accel_signal + 0.001*randn(size(accel_signal));
% now 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; % 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
recommended_max_nfft = floor(min(Fs*diff(Zx)));
%% 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)
% 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)
%% FFT processing
% windowing
windowx = ones(nfft,1); % no window on force signal
windowy = ones(nfft,1); % no window on response sensor signal
% 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);
figure(3), % just to show the individual gaussian pulses with different time offsets
subplot(2,1,1),plot(time_measure,force_signal_measure)
hold on
subplot(2,1,2),plot(time_measure,accel_signal_measure)
hold on
leg_str1{ck} = ['hit # ' num2str(k)];
leg_str2{ck} = ['record # ' num2str(k)];
% 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
subplot(2,1,1),legend(leg_str1); % add legend of valid data
subplot(2,1,2),legend(leg_str2); % add legend of valid data
% 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;
freq = logspace(log10(flow),log10(fhigh),300);
[gm,pm] = bode(num,den,2*pi*freq);
figure(4)
subplot(3,1,1),semilogx(freq,20*log10(gm),'b',f,20*log10(abs(Txy)),'r');
title(['FRF comparison / Valid records : ' num2str(data_valid')])
xlim([flow fhigh]);
ylabel('Magnitude (dB)');
legend('input model','measured');
subplot(3,1,2),semilogx(freq,pm,'b',f,180/pi*(angle(Txy)),'r');
xlim([flow fhigh]);
ylabel('Phase (°)');
legend('input model','measured');
subplot(3,1,3),semilogx(freq,ones(size(freq)),'b',f,Cxy,'r');
xlim([flow fhigh]);
xlabel('Frequency (Hz)');
ylabel('Coh');
legend('input model','measured');
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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

その他の回答 (0 件)

カテゴリ

Help Center および File ExchangeSmoothing and Denoising についてさらに検索

製品


リリース

R2024a

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by