nass-metrology-test-bench/index.org

465 lines
13 KiB
Org Mode

#+TITLE: Cercalo Test Bench
:DRAWER:
#+STARTUP: overview
#+LANGUAGE: en
#+EMAIL: dehaeze.thomas@gmail.com
#+AUTHOR: Dehaeze Thomas
#+HTML_HEAD: <link rel="stylesheet" type="text/css" href="./css/htmlize.css"/>
#+HTML_HEAD: <link rel="stylesheet" type="text/css" href="./css/readtheorg.css"/>
#+HTML_HEAD: <link rel="stylesheet" type="text/css" href="./css/zenburn.css"/>
#+HTML_HEAD: <script type="text/javascript" src="./js/jquery.min.js"></script>
#+HTML_HEAD: <script type="text/javascript" src="./js/bootstrap.min.js"></script>
#+HTML_HEAD: <script type="text/javascript" src="./js/jquery.stickytableheaders.min.js"></script>
#+HTML_HEAD: <script type="text/javascript" src="./js/readtheorg.js"></script>
#+PROPERTY: header-args:matlab :session *MATLAB*
#+PROPERTY: header-args:matlab+ :comments org
#+PROPERTY: header-args:matlab+ :results none
#+PROPERTY: header-args:matlab+ :exports both
#+PROPERTY: header-args:matlab+ :eval no-export
#+PROPERTY: header-args:matlab+ :output-dir figs
#+PROPERTY: header-args:matlab+ :tangle matlab/frf_processing.m
#+PROPERTY: header-args:matlab+ :mkdirp yes
:END:
* Identification
:PROPERTIES:
:header-args:matlab+: :tangle matlab/plant_identification.m
:header-args:matlab+: :comments org :mkdirp yes
:END:
<<sec:plant_identification>>
** ZIP file containing the data and matlab files :ignore:
#+begin_src bash :exports none :results none
if [ matlab/plant_identification.m -nt data/plant_identification.zip ]; then
cp matlab/plant_identification.m plant_identification.m;
zip data/plant_identification \
mat/data_ux.mat \
mat/data_uy.mat \
plant_identification.m
rm plant_identification.m;
fi
#+end_src
#+begin_note
All the files (data and Matlab scripts) are accessible [[file:data/plant_identification.zip][here]].
#+end_note
** Matlab Init :noexport:ignore:
#+begin_src matlab :tangle no :exports none :results silent :noweb yes :var current_dir=(file-name-directory buffer-file-name)
<<matlab-dir>>
#+end_src
#+begin_src matlab :exports none :results silent :noweb yes
<<matlab-init>>
#+end_src
** Excitation Data
#+begin_src matlab
fs = 1e4;
Ts = 1/fs;
#+end_src
We generate white noise with the "random number" simulink block, and we filter that noise.
#+begin_src matlab
Gi = (1)/(1+s/2/pi/100);
#+end_src
#+begin_src matlab :results output replace
c2d(Gi, Ts, 'tustin')
#+end_src
#+RESULTS:
#+begin_example
c2d(Gi, Ts, 'tustin')
ans =
0.030459 (z+1)
--------------
(z-0.9391)
Sample time: 0.0001 seconds
Discrete-time zero/pole/gain model.
#+end_example
** Input / Output data
The identification data is loaded
#+begin_src matlab
ux = load('mat/data_ux.mat', 't', 'ux', 'yx', 'yy');
uy = load('mat/data_uy.mat', 't', 'uy', 'yx', 'yy');
#+end_src
We remove the first seconds where the Cercalo is turned on.
#+begin_src matlab
i0x = 20*fs;
i0y = 10*fs;
ux.t = ux.t( i0x:end) - ux.t(i0x);
ux.ux = ux.ux(i0x:end);
ux.yx = ux.yx(i0x:end);
ux.yy = ux.yy(i0x:end);
uy.t = uy.t( i0y:end) - uy.t(i0x);
uy.uy = uy.uy(i0y:end);
uy.yx = uy.yx(i0y:end);
uy.yy = uy.yy(i0y:end);
#+end_src
#+begin_src matlab
ux.ux = ux.ux-mean(ux.ux);
ux.yx = ux.yx-mean(ux.yx);
ux.yy = ux.yy-mean(ux.yy);
uy.ux = uy.ux-mean(uy.ux);
uy.yx = uy.yx-mean(uy.yx);
uy.yy = uy.yy-mean(uy.yy);
#+end_src
#+begin_src matlab :exports none
figure;
ax1 = subplot(1, 2, 1);
plot(ux.t, ux.ux);
xlabel('Time [s]');
ylabel('Amplitude [V]');
legend({'$u_x$'});
ax2 = subplot(1, 2, 2);
hold on;
plot(ux.t, ux.yx, 'DisplayName', '$y_x$');
plot(ux.t, ux.yy, 'DisplayName', '$y_y$');
hold off;
xlabel('Time [s]');
ylabel('Amplitude [V]');
legend()
linkaxes([ax1,ax2],'x');
xlim([ux.t(1), ux.t(end)])
#+end_src
#+HEADER: :tangle no :exports results :results none :noweb yes
#+begin_src matlab :var filepath="figs/identification_ux.pdf" :var figsize="full-normal" :post pdf2svg(file=*this*, ext="png")
<<plt-matlab>>
#+end_src
#+NAME: fig:identification_ux
#+CAPTION: Identification signals when exciting the $x$ axis ([[./figs/identification_ux.png][png]], [[./figs/identification_ux.pdf][pdf]])
[[file:figs/identification_ux.png]]
#+begin_src matlab :exports none
figure;
ax1 = subplot(1, 2, 1);
plot(uy.t, uy.uy);
xlabel('Time [s]');
ylabel('Amplitude [V]');
legend({'$u_y$'});
ax2 = subplot(1, 2, 2);
hold on;
plot(uy.t, uy.yy, 'DisplayName', '$y_y$');
plot(uy.t, uy.yx, 'DisplayName', '$y_x$');
hold off;
xlabel('Time [s]');
ylabel('Amplitude [V]');
legend()
linkaxes([ax1,ax2],'x');
xlim([uy.t(1), uy.t(end)])
#+end_src
#+HEADER: :tangle no :exports results :results none :noweb yes
#+begin_src matlab :var filepath="figs/identification_uy.pdf" :var figsize="full-normal" :post pdf2svg(file=*this*, ext="png")
<<plt-matlab>>
#+end_src
#+NAME: fig:identification_uy
#+CAPTION: Identification signals when exciting the $y$ axis ([[./figs/identification_uy.png][png]], [[./figs/identification_uy.pdf][pdf]])
[[file:figs/identification_uy.png]]
** Estimation of the Frequency Response Function Matrix
We compute an estimate of the transfer functions.
#+begin_src matlab
[tf_ux_yx, f] = tfestimate(ux.ux, ux.yx, hanning(ceil(1*fs)), [], [], fs);
[tf_ux_yy, ~] = tfestimate(ux.ux, ux.yy, hanning(ceil(1*fs)), [], [], fs);
[tf_uy_yx, ~] = tfestimate(uy.uy, uy.yx, hanning(ceil(1*fs)), [], [], fs);
[tf_uy_yy, ~] = tfestimate(uy.uy, uy.yy, hanning(ceil(1*fs)), [], [], fs);
#+end_src
#+begin_src matlab :exports none
figure;
ax11 = subplot(2, 2, 1);
hold on;
plot(f, abs(tf_ux_yx))
title('Frequency Response Function $\frac{y_x}{u_x}$')
set(gca, 'Xscale', 'log'); set(gca, 'Yscale', 'log');
ylabel('Amplitude')
hold off;
ax12 = subplot(2, 2, 2);
hold on;
plot(f, abs(tf_uy_yx))
title('Frequency Response Function $\frac{y_x}{u_y}$')
set(gca, 'Xscale', 'log'); set(gca, 'Yscale', 'log');
hold off;
ax21 = subplot(2, 2, 3);
hold on;
plot(f, abs(tf_ux_yy))
title('Frequency Response Function $\frac{y_y}{u_x}$')
set(gca, 'Xscale', 'log'); set(gca, 'Yscale', 'log');
ylabel('Amplitude')
xlabel('Frequency [Hz]')
hold off;
ax22 = subplot(2, 2, 4);
hold on;
plot(f, abs(tf_uy_yy))
title('Frequency Response Function $\frac{y_y}{u_y}$')
set(gca, 'Xscale', 'log'); set(gca, 'Yscale', 'log');
xlabel('Frequency [Hz]')
hold off;
linkaxes([ax11,ax12,ax21,ax22],'x');
xlim([10, 1000]);
linkaxes([ax11,ax12,ax21,ax22],'y');
ylim([1e-2, 1e3])
#+end_src
#+HEADER: :tangle no :exports results :results none :noweb yes
#+begin_src matlab :var filepath="figs/frequency_response_matrix.pdf" :var figsize="full-tall" :post pdf2svg(file=*this*, ext="png")
<<plt-matlab>>
#+end_src
#+NAME: fig:frequency_response_matrix
#+CAPTION: Frequency Response Matrix ([[./figs/frequency_response_matrix.png][png]], [[./figs/frequency_response_matrix.pdf][pdf]])
[[file:figs/frequency_response_matrix.png]]
** Coherence
#+begin_src matlab
[coh_ux_yx, f] = mscohere(ux.ux, ux.yx, hanning(ceil(1*fs)), [], [], fs);
[coh_ux_yy, ~] = mscohere(ux.ux, ux.yy, hanning(ceil(1*fs)), [], [], fs);
[coh_uy_yx, ~] = mscohere(uy.uy, uy.yx, hanning(ceil(1*fs)), [], [], fs);
[coh_uy_yy, ~] = mscohere(uy.uy, uy.yy, hanning(ceil(1*fs)), [], [], fs);
#+end_src
#+begin_src matlab :exports none
figure;
ax11 = subplot(2, 2, 1);
hold on;
plot(f, coh_ux_yx)
set(gca, 'Xscale', 'log');
title('Coherence $\frac{y_x}{u_x}$')
ylabel('Coherence')
hold off;
ax12 = subplot(2, 2, 2);
hold on;
plot(f, coh_uy_yx)
set(gca, 'Xscale', 'log');
title('Coherence $\frac{y_x}{u_y}$')
hold off;
ax21 = subplot(2, 2, 3);
hold on;
plot(f, coh_ux_yy)
set(gca, 'Xscale', 'log');
title('Coherence $\frac{y_y}{u_x}$')
ylabel('Coherence')
xlabel('Frequency [Hz]')
hold off;
ax22 = subplot(2, 2, 4);
hold on;
plot(f, coh_uy_yy)
set(gca, 'Xscale', 'log');
title('Coherence $\frac{y_y}{u_y}$')
xlabel('Frequency [Hz]')
hold off;
linkaxes([ax11,ax12,ax21,ax22],'x');
xlim([10, 1000]);
linkaxes([ax11,ax12,ax21,ax22],'y');
ylim([0, 1])
#+end_src
#+HEADER: :tangle no :exports results :results none :noweb yes
#+begin_src matlab :var filepath="figs/identification_coherence.pdf" :var figsize="full-tall" :post pdf2svg(file=*this*, ext="png")
<<plt-matlab>>
#+end_src
#+NAME: fig:identification_coherence
#+CAPTION: Coherence ([[./figs/identification_coherence.png][png]], [[./figs/identification_coherence.pdf][pdf]])
[[file:figs/identification_coherence.png]]
** Extraction of a transfer function matrix
First we define the initial guess for the resonance frequencies and the weights associated.
#+begin_src matlab
freqs_res = [410, 250]; % [Hz]
freqs_res_weights = [10, 10]; % [Hz]
#+end_src
From the number of resonance frequency we want to fit, we define the order =N= of the system we want to obtain.
#+begin_src matlab
N = 2*length(freqs_res);
#+end_src
We then make an initial guess on the complex values of the poles.
#+begin_src matlab
xi = 0.001; % Approximate modal damping
poles = [2*pi*freqs_res*(xi + 1i), 2*pi*freqs_res*(xi - 1i)];
#+end_src
We then define the weight that will be used for the fitting.
Basically, we want more weight around the resonance and at low frequency (below the first resonance).
Also, we want more importance where we have a better coherence.
#+begin_src matlab
weight = ones(1, length(f));
% weight = G_coh';
% alpha = 0.1;
% for freq_i = 1:length(freqs_res)
% weight(f>(1-alpha)*freqs_res(freq_i) & omega<(1 + alpha)*2*pi*freqs_res(freq_i)) = freqs_res_weights(freq_i);
% end
#+end_src
Ignore data above some frequency.
#+begin_src matlab
weight(f>1000) = 0;
#+end_src
#+begin_src matlab :exports none
figure;
hold on;
plot(f, weight);
plot(freqs_res, ones(size(freqs_res)), 'rx');
hold off;
xlabel('Frequency [Hz]');
xlabel('Weight Amplitude');
set(gca, 'xscale', 'log');
xlim([f(1), f(end)]);
#+end_src
#+HEADER: :tangle no :exports results :results none :noweb yes
#+begin_src matlab :var filepath="figs/weights.pdf" :var figsize="wide-normal" :post pdf2svg(file=*this*, ext="png")
<<plt-matlab>>
#+end_src
#+NAME: fig:weights
#+CAPTION: Weights amplitude ([[./figs/weights.png][png]], [[./figs/weights.pdf][pdf]])
[[file:figs/weights.png]]
When we set some options for =vfit3=.
#+begin_src matlab
opts = struct();
opts.stable = 1; % Enforce stable poles
opts.asymp = 1; % Force D matrix to be null
opts.relax = 1; % Use vector fitting with relaxed non-triviality constraint
opts.skip_pole = 0; % Do NOT skip pole identification
opts.skip_res = 0; % Do NOT skip identification of residues (C,D,E)
opts.cmplx_ss = 0; % Create real state space model with block diagonal A
opts.spy1 = 0; % No plotting for first stage of vector fitting
opts.spy2 = 0; % Create magnitude plot for fitting of f(s)
#+end_src
We define the number of iteration.
#+begin_src matlab
Niter = 5;
#+end_src
An we run the =vectfit3= algorithm.
#+begin_src matlab
for iter = 1:Niter
[SER_ux_yx, poles, ~, fit_ux_yx] = vectfit3(tf_ux_yx.', 1i*2*pi*f, poles, weight, opts);
end
for iter = 1:Niter
[SER_uy_yx, poles, ~, fit_uy_yx] = vectfit3(tf_uy_yx.', 1i*2*pi*f, poles, weight, opts);
end
for iter = 1:Niter
[SER_ux_yy, poles, ~, fit_ux_yy] = vectfit3(tf_ux_yy.', 1i*2*pi*f, poles, weight, opts);
end
for iter = 1:Niter
[SER_uy_yy, poles, ~, fit_uy_yy] = vectfit3(tf_uy_yy.', 1i*2*pi*f, poles, weight, opts);
end
#+end_src
#+begin_src matlab :exports none
figure;
ax11 = subplot(2, 2, 1);
hold on;
plot(f, abs(tf_ux_yx))
plot(f, abs(fit_ux_yx))
title('Frequency Response Function $\frac{y_x}{u_x}$')
set(gca, 'Xscale', 'log'); set(gca, 'Yscale', 'log');
ylabel('Amplitude')
hold off;
ax12 = subplot(2, 2, 2);
hold on;
plot(f, abs(tf_uy_yx))
plot(f, abs(fit_uy_yx))
title('Frequency Response Function $\frac{y_x}{u_y}$')
set(gca, 'Xscale', 'log'); set(gca, 'Yscale', 'log');
hold off;
ax21 = subplot(2, 2, 3);
hold on;
plot(f, abs(tf_ux_yy))
plot(f, abs(fit_ux_yy))
title('Frequency Response Function $\frac{y_y}{u_x}$')
set(gca, 'Xscale', 'log'); set(gca, 'Yscale', 'log');
ylabel('Amplitude')
xlabel('Frequency [Hz]')
hold off;
ax22 = subplot(2, 2, 4);
hold on;
plot(f, abs(tf_uy_yy))
plot(f, abs(fit_uy_yy))
title('Frequency Response Function $\frac{y_y}{u_y}$')
set(gca, 'Xscale', 'log'); set(gca, 'Yscale', 'log');
xlabel('Frequency [Hz]')
hold off;
linkaxes([ax11,ax12,ax21,ax22],'x');
xlim([10, 1000]);
linkaxes([ax11,ax12,ax21,ax22],'y');
ylim([1e-2, 1e3])
#+end_src
#+HEADER: :tangle no :exports results :results none :noweb yes
#+begin_src matlab :var filepath="figs/identification_matrix_fit.pdf" :var figsize="full-tall" :post pdf2svg(file=*this*, ext="png")
<<plt-matlab>>
#+end_src
#+NAME: fig:identification_matrix_fit
#+CAPTION: Transfer Function Extraction of the FRF matrix ([[./figs/identification_matrix_fit.png][png]], [[./figs/identification_matrix_fit.pdf][pdf]])
[[file:figs/identification_matrix_fit.png]]
And finally, we create the identified state space model:
#+begin_src matlab
G_ux_yx = minreal(ss(full(SER_ux_yx.A),SER_ux_yx.B,SER_ux_yx.C,SER_ux_yx.D));
G_uy_yx = minreal(ss(full(SER_uy_yx.A),SER_uy_yx.B,SER_uy_yx.C,SER_uy_yx.D));
G_ux_yy = minreal(ss(full(SER_ux_yy.A),SER_ux_yy.B,SER_ux_yy.C,SER_ux_yy.D));
G_uy_yy = minreal(ss(full(SER_uy_yy.A),SER_uy_yy.B,SER_uy_yy.C,SER_uy_yy.D));
G = [G_ux_yx, G_uy_yx;
G_ux_yy, G_uy_yy];
#+end_src
#+begin_src matlab
save('mat/plant.mat', 'G');
#+end_src
* Plant Analysis
* Control