#+TITLE: Cercalo Test Bench :DRAWER: #+STARTUP: overview #+LANGUAGE: en #+EMAIL: dehaeze.thomas@gmail.com #+AUTHOR: Dehaeze Thomas #+HTML_HEAD: #+HTML_HEAD: #+HTML_HEAD: #+HTML_HEAD: #+HTML_HEAD: #+HTML_HEAD: #+HTML_HEAD: #+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: Notes: 145 994 8 * Identification :PROPERTIES: :header-args:matlab+: :tangle matlab/plant_identification.m :header-args:matlab+: :comments org :mkdirp yes :END: <> ** 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) <> #+end_src #+begin_src matlab :exports none :results silent :noweb yes <> #+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") <> #+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") <> #+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") <> #+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") <> #+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") <> #+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") <> #+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