2020-10-08 10:52:20 +02:00
#+TITLE : Sensor Fusion Paper - Matlab Computation
:DRAWER:
#+HTML_LINK_HOME : ../index.html
#+HTML_LINK_UP : ../index.html
#+LATEX_CLASS : cleanreport
#+LATEX_CLASS_OPTIONS : [tocnp, secbreak, minted]
#+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 : <script src="../js/jquery.min.js"></script>
#+HTML_HEAD : <script src="../js/bootstrap.min.js"></script>
#+HTML_HEAD : <script src="../js/jquery.stickytableheaders.min.js"></script>
#+HTML_HEAD : <script src="../js/readtheorg.js"></script>
#+PROPERTY : header-args:matlab :session *MATLAB*
#+PROPERTY : header-args:matlab+ :tangle matlab/comp_filters_design.m
#+PROPERTY : header-args:matlab+ :comments org
#+PROPERTY : header-args:matlab+ :exports both
#+PROPERTY : header-args:matlab+ :results none
#+PROPERTY : header-args:matlab+ :eval no-export
#+PROPERTY : header-args:matlab+ :noweb yes
#+PROPERTY : header-args:matlab+ :mkdirp yes
#+PROPERTY : header-args:matlab+ :output-dir figs
:END:
* Introduction :ignore:
The control architecture studied here is shown on figure [[fig:sf_arch_class_prefilter ]] where:
- $G^{\prime}$ is the plant to control
- $K = G^{-1} H_L^{-1}$ is the controller used with $G$ a model of the plant
- $H_L$ and $H_H$ are complementary filters ($H_L + H_H = 1$)
- $K_r$ is a pre-filter that can be added
#+name : fig:sf_arch_class_prefilter
#+caption : Control Architecture
2020-10-26 17:52:18 +01:00
[[file:figs-paper/sf_arch_class_prefilter.png ]]
2020-10-08 10:52:20 +02:00
Here is the outline of the =matlab= analysis for this control architecture:
2020-10-26 17:52:18 +01:00
- Section [[sec:plant ]]:
The plant model $G$ is defined
- Section [[sec:uncertainty ]]:
The plant uncertainty set $\Pi_I$ is defined using the multiplicative input uncertainty: $\Pi_I: \ G^\prime = G (1 + w_I \Delta)$.
Thus the weight $w_I$ is defined such that the true system dynamics is included in the set $\Pi_I$
- Section [[sec:specifications ]]:
From the specifications on performance that are expressed in terms of upper bounds of $S$ and $T$, performance weights $w_S$ and $w_T$ are derived such that the goal is to obtain $|S| < \frac{1}{|w_S|}$ and $|T| < \frac{1}{|w_T|}, \ \forall \omega$
- Section [[sec:upper_bounds_filters ]]:
Upper bounds on the magnitude of the complementary filters $|H_L|$ and $|H_H|$ are defined in order to ensure Nominal Performance (NP), Robust Stability (RS) and Robust Performance (RP)
- Section [[sec:h_infinity ]]:
$H_L$ and $H_H$ are synthesize using the $\mathcal{H}_ \infty$ synthesis such that $|H_L|$ and $|H_H|$ are within the specified bounds and such that $H_L + H_H = 1$ (complementary property)
- Section [[sec:analytical_formula ]]:
Analytical formulas of complementary filters are used
- Section [[sec:comp_filters ]]:
The obtained complementary filters using the $\mathcal{H}_\infty$ synthesis and the analytical formula are compared
- Section [[sec:controller_analysis ]]:
The obtain controller $K = G^{-1} H_H^{-1}$ is analyzed
- Section [[sec:nominal_stability_performance ]]:
The Nominal Stability (NS) and Nominal Performance conditions are verified
- Section [[sec:robustness_analysis ]]:
Robust Stability and Robust Performance conditions are studied
- Section [[sec:pre_filter ]]:
A pre-filter that is used to limit the input usage due to the change of the reference is added
2020-10-08 10:52:20 +02:00
* ZIP file containing the data and matlab files :ignore:
#+begin_src bash :exports none :results none
if [ matlab/sensor_fusion.m -nt data/sensor_fusion.zip ]; then
cp matlab/sensor_fusion.m sensor_fusion.m;
zip data/sensor_fusion \
sensor_fusion.m
rm sensor_fusion.m;
fi
#+end_src
#+begin_note
All the files (data and Matlab scripts) are accessible [[file:data/sensor_fusion.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
#+begin_src matlab
2020-10-26 17:52:18 +01:00
addpath('src');
freqs = 0.5*logspace(0, 3, 1000);
2020-10-08 10:52:20 +02:00
#+end_src
* Definition of the plant
2020-10-26 17:52:18 +01:00
<<sec:plant >>
2020-10-08 10:52:20 +02:00
The studied system consists of a solid positioned on top of a motorized uni-axial soft suspension.
The absolute position $x$ of the solid is measured using an inertial sensor and a force $F$ can be applied to the mass using a voice coil actuator.
2020-10-26 17:52:18 +01:00
The model of the system is represented on figure [[fig:mech_sys_alone ]] where the mass of the solid is $m = 10\ [kg]$, the stiffness of the suspension is $k = 10^4\ [N/m]$ and the damping of the system is $c = 10^2\ [N/ (m/s)]$.
2020-10-08 10:52:20 +02:00
#+name : fig:mech_sys_alone
#+caption : One degree of freedom system
2020-10-26 17:52:18 +01:00
[[file:figs-paper/mech_sys_alone.png ]]
2020-10-08 10:52:20 +02:00
The plant $G$ is defined on matlab and its bode plot is shown on figure [[fig:bode_plot_mech_sys ]].
#+begin_src matlab
2020-10-26 17:52:18 +01:00
m = 10; % mass [kg]
k = 1e4; % stiffness [N/m]
c = 1e2; % damping [N/(m/s)]
2020-10-08 10:52:20 +02:00
G = 1/(m*s^2 + c*s + k);
#+end_src
#+begin_src matlab :exports none
figure;
2020-10-26 17:52:18 +01:00
tiledlayout(3, 1, 'TileSpacing', 'None', 'Padding', 'None');
% Magnitude
ax1 = nexttile([2, 1]);
2020-10-08 10:52:20 +02:00
hold on;
plot(freqs, abs(squeeze(freqresp(G, freqs, 'Hz'))), 'k-');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]);
ylabel('Magnitude [m/N]');
2020-10-26 17:52:18 +01:00
% Phase
ax2 = nexttile;
2020-10-08 10:52:20 +02:00
hold on;
plot(freqs, 180/pi*angle(squeeze(freqresp(G, freqs, 'Hz'))), 'k-');
hold off;
2020-10-26 17:52:18 +01:00
yticks(-180:90:0);
ylim([-180 0]);
2020-10-08 10:52:20 +02:00
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
2020-10-26 17:52:18 +01:00
2020-10-08 10:52:20 +02:00
linkaxes([ax1,ax2],'x');
2020-10-26 17:52:18 +01:00
xlim([freqs(1), freqs(end)])
2020-10-08 10:52:20 +02:00
#+end_src
2020-10-26 17:52:18 +01:00
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/bode_plot_mech_sys.pdf', 'width', 'half', 'height', 'normal');
2020-10-08 10:52:20 +02:00
#+end_src
2020-10-26 17:52:18 +01:00
#+name : fig:bode_plot_mech_sys
#+caption : Bode plot of $G$
#+RESULTS :
2020-10-08 10:52:20 +02:00
[[file:figs/bode_plot_mech_sys.png ]]
* Multiplicative input uncertainty
2020-10-26 17:52:18 +01:00
<<sec:uncertainty >>
2020-10-08 10:52:20 +02:00
We choose to use the multiplicative input uncertainty to model the plant uncertainty:
2020-10-26 17:52:18 +01:00
\begin{equation}
\Pi_I: \ G^\prime(s) = G(s) (1 + w_I(s) \Delta(s)),\text{ with } |\Delta(j\omega)| < 1 \ \forall \omega
\end{equation}
2020-10-08 10:52:20 +02:00
2020-10-26 17:52:18 +01:00
The uncertainty weight is shown in figure [[fig:bode_wi ]].
2020-10-08 10:52:20 +02:00
#+begin_src matlab
2020-10-26 17:52:18 +01:00
wI = createWeight('n', 2, 'w0', 2*pi*80, 'G0', 0.1, 'G1', 10, 'Gc', 1);
2020-10-08 10:52:20 +02:00
#+end_src
#+begin_src matlab :exports none
figure;
2020-10-26 17:52:18 +01:00
tiledlayout(1, 1, 'TileSpacing', 'None', 'Padding', 'None');
2020-10-08 10:52:20 +02:00
hold on;
plot(freqs, abs(squeeze(freqresp(wI, freqs, 'Hz'))), 'k-');
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('Magnitude');
hold off;
xlim([freqs(1), freqs(end)]);
ylim([1e-1, 10]);
#+end_src
2020-10-26 17:52:18 +01:00
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/bode_wi.pdf', 'width', 'half', 'height', 'small');
2020-10-08 10:52:20 +02:00
#+end_src
2020-10-26 17:52:18 +01:00
#+name : fig:bode_wi
#+CAPTION : Bode plot of $w_I$
#+RESULTS :
2020-10-08 10:52:20 +02:00
[[file:figs/bode_wi.png ]]
2020-10-26 17:52:18 +01:00
Elements in the uncertainty set $\Pi_I$ are computed and their bode plot is shown on figure [[fig:plant_uncertainty_bode_plot ]].
2020-10-08 10:52:20 +02:00
2020-10-26 17:52:18 +01:00
#+begin_src matlab :exports none
% Delta = ultidyn('Delta', [1 1]);
2020-10-08 10:52:20 +02:00
2020-10-26 17:52:18 +01:00
% Gd = G*(1+wI*Delta);
% Gds = usample(Gd, 20);
2020-10-08 10:52:20 +02:00
#+end_src
#+begin_src matlab :exports none
figure;
2020-10-26 17:52:18 +01:00
tiledlayout(2, 1, 'TileSpacing', 'None', 'Padding', 'None');
% Magnitude
ax1 = nexttile;
2020-10-08 10:52:20 +02:00
hold on;
2020-10-26 17:52:18 +01:00
plotMagUncertainty(wI, freqs, 'G', G, 'DisplayName', '$G$');
plot(freqs, abs(squeeze(freqresp(G, freqs, 'Hz'))), 'k-');
2020-10-08 10:52:20 +02:00
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
2020-10-26 17:52:18 +01:00
ylabel('Magnitude [m/N]'); set(gca, 'XTickLabel',[]);
ylim([1e-8, 1e-3]);
2020-10-08 10:52:20 +02:00
hold off;
2020-10-26 17:52:18 +01:00
2020-10-08 10:52:20 +02:00
% Phase
2020-10-26 17:52:18 +01:00
ax2 = nexttile;
2020-10-08 10:52:20 +02:00
hold on;
2020-10-26 17:52:18 +01:00
plotPhaseUncertainty(wI, freqs, 'G', G);
2020-10-08 10:52:20 +02:00
plot(freqs, 180/pi*unwrap(angle(squeeze(freqresp(G, freqs, 'Hz')))), 'k-');
set(gca,'xscale','log');
2020-10-26 17:52:18 +01:00
yticks(-360:90:90);
ylim([-360 90]);
2020-10-08 10:52:20 +02:00
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
2020-10-26 17:52:18 +01:00
2020-10-08 10:52:20 +02:00
linkaxes([ax1,ax2],'x');
xlim([freqs(1), freqs(end)]);
#+end_src
2020-10-26 17:52:18 +01:00
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/plant_uncertainty_bode_plot.pdf', 'width', 'half', 'height', 'tall');
2020-10-08 10:52:20 +02:00
#+end_src
2020-10-26 17:52:18 +01:00
#+name : fig:plant_uncertainty_bode_plot
#+caption : Some elements in the uncertainty set $\Pi_I$
#+RESULTS :
2020-10-08 10:52:20 +02:00
[[file:figs/plant_uncertainty_bode_plot.png ]]
* Specifications and performance weights
2020-10-26 17:52:18 +01:00
<<sec:specifications >>
2020-10-08 10:52:20 +02:00
The control objective is to isolate the displacement $x$ of the mass from the ground motion $w$.
The specifications are described below:
- at least a factor $10$ of disturbance rejection at $2\ \text{Hz}$ and with a slope of $2$ below $2\ \text{Hz}$ until a rejection of $10^3$
- the noise attenuation should be at least $10$ above $100\ \text{Hz}$ and with a slope of $-2$ above
These specifications can be represented as upper bounds on the closed loop transfer functions $S$ and $T$ (see figure [[fig:bode_requirements ]]).
#+begin_src matlab :exports none
figure;
2020-10-26 17:52:18 +01:00
2020-10-08 10:52:20 +02:00
hold on;
set(gca,'ColorOrderIndex',1)
plot([100, 1000], [0.1, 0.001], ':', 'DisplayName', '$|T|$ - Upper bound');
set(gca,'ColorOrderIndex',2)
plot([0.1, 0.2, 2], [0.001, 0.001, 0.1], ':', 'DisplayName', '$|S|$ - Upper bound');
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('Magnitude');
hold off;
xlim([freqs(1), freqs(end)]);
ylim([1e-3, 10]);
xticks([0.1, 1, 10, 100, 1000]);
2020-10-26 17:52:18 +01:00
legend('location', 'northeast', 'FontSize', 8);
2020-10-08 10:52:20 +02:00
#+end_src
2020-10-26 17:52:18 +01:00
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/bode_requirements.pdf', 'width', 'half', 'height', 'normal');
2020-10-08 10:52:20 +02:00
#+end_src
2020-10-26 17:52:18 +01:00
#+name : fig:bode_requirements
#+caption : Upper bounds on $S$ and $T$
#+RESULTS :
2020-10-08 10:52:20 +02:00
[[file:figs/bode_requirements.png ]]
We now define two weights, $w_S(s)$ and $w_T(s)$ such that $1/|w_S|$ and $1/ |w_T|$ are lower than the previously defined upper bounds.
Then, the performance specifications are satisfied if the following condition is valid:
\[ \big|S(j\omega)\big| < \frac{1}{|w_S(j\omega)|} ; \quad \big|T(j\omega)\big| < \frac{1}{|w_T(j\omega)|}, \quad \forall \omega \]
The weights are defined as follow. They magnitude is compared with the upper bounds on $S$ and $T$ on figure [[fig:compare_weights_upper_bounds_S_T ]].
#+begin_src matlab
wS = 1600/(s+0.13)^2;
wT = 1000*((s/(2*pi*1000)))^2;
#+end_src
#+begin_src matlab :exports none
figure;
hold on;
set(gca,'ColorOrderIndex',1)
plot(freqs, 1./abs(squeeze(freqresp(wT, freqs, 'Hz'))), '--', 'DisplayName', '$1/ |w_T|$');
set(gca,'ColorOrderIndex',1)
plot([100, 1000], [0.1, 0.001], ':', 'DisplayName', '$|T|$ - Upper bound');
set(gca,'ColorOrderIndex',2)
plot(freqs, 1./abs(squeeze(freqresp(wS, freqs, 'Hz'))), '--', 'DisplayName', '$1/ |w_S|$');
set(gca,'ColorOrderIndex',2)
plot([0.1, 0.2, 2], [0.001, 0.001, 0.1], ':', 'DisplayName', '$|S|$ - Upper bound');
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('Magnitude');
hold off;
xlim([freqs(1), freqs(end)]);
ylim([1e-4, 10]);
xticks([0.1, 1, 10, 100, 1000]);
2020-10-26 17:52:18 +01:00
legend('location', 'southeast', 'FontSize', 8);
2020-10-08 10:52:20 +02:00
#+end_src
2020-10-26 17:52:18 +01:00
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/compare_weights_upper_bounds_S_T.pdf', 'width', 'half', 'height', 'normal');
2020-10-08 10:52:20 +02:00
#+end_src
2020-10-26 17:52:18 +01:00
#+name : fig:compare_weights_upper_bounds_S_T
#+caption : Weights $w_S$ and $w_T$ with the upper bounds on $S$ and $T$ obtained from the specifications
#+RESULTS :
2020-10-08 10:52:20 +02:00
[[file:figs/compare_weights_upper_bounds_S_T.png ]]
* Upper bounds on the norm of the complementary filters for NP, RS and RP
2020-10-26 17:52:18 +01:00
<<sec:upper_bounds_filters >>
2020-10-08 10:52:20 +02:00
Now that we have defined $w_I$, $w_S$ and $w_T$, we can derive conditions for Nominal Performance, Robust Stability and Robust Performance ($j\omega$ is omitted here for readability):
\begin{align*}
\text{NP} &\Leftrightarrow |H_H| < \frac{1}{|w_S|} \text{ and } |H_L| < \frac{1}{|w_T|} \quad \forall \omega \\
\text{RS} &\Leftrightarrow |H_L| < \frac{1}{|w_I| (2 + |w_I|)} \quad \forall \omega \\
\text{RP for } S &\Leftarrow |H_H| < \frac{1 + |w_I|}{|w_S| (2 + |w_I|)} \quad \forall \omega \\
\text{RP for } T &\Leftrightarrow |H_L| < \frac{1}{|w_T| (1 + |w_I|) + |w_I|} \quad \forall \omega
\end{align*}
These conditions are upper bounds on the complementary filters used for control.
We plot these conditions on figure [[fig:weights_NP_RS_RP ]].
#+begin_src matlab :exports none
wT_resp = abs(squeeze(freqresp(wT, freqs, 'Hz')));
wI_resp = abs(squeeze(freqresp(wI, freqs, 'Hz')));
wS_resp = abs(squeeze(freqresp(wS, freqs, 'Hz')));
Hh_resp = wT_resp.*(1 + wI_resp)./(wS_resp.* (wT_resp .* (1 + wI_resp) + wI_resp));
Hl_resp = 1./(wT_resp .* (1 + wI_resp) + wI_resp);
figure;
hold on;
plot(freqs, wS_resp .* Hh_resp + wI_resp .* Hl_resp, '--', 'DisplayName', 'NP - $H_L$');
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('Magnitude');
hold off;
xlim([freqs(1), freqs(end)]);
ylim([1e-3, 10]);
xticks([0.1, 1, 10, 100, 1000]);
2020-10-26 17:52:18 +01:00
legend('location', 'northeast', 'FontSize', 8);
2020-10-08 10:52:20 +02:00
#+end_src
#+begin_src matlab :exports none
wT_resp = abs(squeeze(freqresp(wT, freqs, 'Hz')));
wI_resp = abs(squeeze(freqresp(wI, freqs, 'Hz')));
wS_resp = abs(squeeze(freqresp(wS, freqs, 'Hz')));
figure;
hold on;
set(gca,'ColorOrderIndex',1)
plot(freqs, 1./wT_resp, '--', 'DisplayName', 'NP - $H_L$');
set(gca,'ColorOrderIndex',1)
2020-10-26 17:52:18 +01:00
plot(freqs, 1./(wT_resp .* (1 + wI_resp) + wI_resp), ':', 'DisplayName', 'RP - $H_L$ (T)');
2020-10-08 10:52:20 +02:00
set(gca,'ColorOrderIndex',1)
plot(freqs, 1./(wI_resp .* (2 + wI_resp)), '-.', 'DisplayName', 'RS - $H_L$');
set(gca,'ColorOrderIndex',2)
plot(freqs, 1./wS_resp, '--', 'DisplayName', 'NP - $H_H$');
set(gca,'ColorOrderIndex',2)
2020-10-26 17:52:18 +01:00
plot(freqs, (1 + wI_resp)./(wS_resp .* (2 + wI_resp)), ':', 'DisplayName', 'RP - $H_H$ (S)');
2020-10-08 10:52:20 +02:00
set(gca,'ColorOrderIndex',2)
2020-10-26 17:52:18 +01:00
plot(freqs, wT_resp.*(1 + wI_resp)./(wS_resp.* (wT_resp .* (1 + wI_resp) + wI_resp)), '-', 'DisplayName', 'RP - $H_H$ (S)');
2020-10-08 10:52:20 +02:00
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('Magnitude');
hold off;
xlim([freqs(1), freqs(end)]);
2020-10-26 17:52:18 +01:00
ylim([1e-4, 10]);
2020-10-08 10:52:20 +02:00
xticks([0.1, 1, 10, 100, 1000]);
2020-10-26 17:52:18 +01:00
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 2);
2020-10-08 10:52:20 +02:00
#+end_src
2020-10-26 17:52:18 +01:00
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/weights_NP_RS_RP.pdf', 'width', 'half', 'height', 'normal');
2020-10-08 10:52:20 +02:00
#+end_src
2020-10-26 17:52:18 +01:00
#+name : fig:weights_NP_RS_RP
#+caption : Upper bounds on the norm of the complementary filters for NP, RS and RP
#+RESULTS :
2020-10-08 10:52:20 +02:00
[[file:figs/weights_NP_RS_RP.png ]]
* H-Infinity synthesis of complementary filters
2020-10-26 17:52:18 +01:00
<<sec:h_infinity >>
2020-10-08 10:52:20 +02:00
We here synthesize the complementary filters using the $\mathcal{H}_\infty$ synthesis.
The goal is to specify upper bounds on the norms of $H_L$ and $H_H$ while ensuring their complementary property ($H_L + H_H = 1$).
In order to do so, we use the generalized plant shown on figure [[fig:sf_hinf_filters_plant_b ]] where $w_L$ and $w_H$ weighting transfer functions that will be used to shape $H_L$ and $H_H$ respectively.
#+name : fig:sf_hinf_filters_plant_b
#+caption : Generalized plant used for the $\mathcal{H}_\infty$ synthesis of the complementary filters
2020-10-26 17:52:18 +01:00
[[file:figs-paper/sf_hinf_filters_plant_b.png ]]
2020-10-08 10:52:20 +02:00
The $\mathcal{H}_\infty$ synthesis applied on this generalized plant will give a transfer function $H_L$ (figure [[fig:sf_hinf_filters_b ]]) such that the $\mathcal{H}_\infty$ norm of the transfer function from $w$ to $[z_H,\ z_L]$ is less than one:
\[ \left\| \begin{array}{c} H_L w_L \\ (1 - H_L) w_H \end{array} \right\|_ \infty < 1 \]
Thus, if the above condition is verified, we can define $H_H = 1 - H_L$ and we have that:
\[ \left\| \begin{array}{c} H_L w_L \\ H_H w_H \end{array} \right\|_ \infty < 1 \]
Which is almost (with an maximum error of $\sqrt{2}$) equivalent to:
\begin{align*}
|H_L| &< \frac{1}{|w_L|}, \quad \forall \omega \\
|H_H| &< \frac{1}{|w_H|}, \quad \forall \omega
\end{align*}
We then see that $w_L$ and $w_H$ can be used to shape both $H_L$ and $H_H$ while ensuring (by definition of $H_H = 1 - H_L$) their complementary property.
#+name : fig:sf_hinf_filters_b
#+caption : $\mathcal{H}_\infty$ synthesis of the complementary filters
2020-10-26 17:52:18 +01:00
[[file:figs-paper/sf_hinf_filters_b.png ]]
2020-10-08 10:52:20 +02:00
Thus, if we choose $w_L$ and $w_H$ such that $1/|w_L|$ and $1/ |w_H|$ lie below the upper bounds of figure [[fig:weights_NP_RS_RP ]], we will ensure the NP, RS and RP of the controlled system.
Depending if we are interested only in NP, RS or RP, we can adjust the weights $w_L$ and $w_H$.
#+begin_src matlab
omegab = 2*pi*9;
wH = (omegab)^2/(s + omegab*sqrt(1e-5))^2;
omegab = 2*pi*28;
wL = (s + omegab/(4.5)^(1/3))^3/(s*(1e-4)^(1/3) + omegab)^3;
#+end_src
#+begin_src matlab :exports none
figure;
hold on;
set(gca,'ColorOrderIndex',1)
plot(freqs, 1./abs(squeeze(freqresp(wT, freqs, 'Hz'))), '--', 'DisplayName', 'NP - $H_L$');
set(gca,'ColorOrderIndex',1)
plot(freqs, 1./(abs(squeeze(freqresp(wT, freqs, 'Hz'))) .* (1 + abs(squeeze(freqresp(wI, freqs, 'Hz')))) + abs(squeeze(freqresp(wI, freqs, 'Hz')))), ':', 'DisplayName', 'RP for T - $H_L$');
set(gca,'ColorOrderIndex',1)
plot(freqs, 1./(abs(squeeze(freqresp(wI, freqs, 'Hz'))) .* (2 + abs(squeeze(freqresp(wI, freqs, 'Hz'))))), '-.', 'DisplayName', 'RS - $H_L$');
set(gca,'ColorOrderIndex',2)
plot(freqs, 1./abs(squeeze(freqresp(wS, freqs, 'Hz'))), '--', 'DisplayName', 'NP - $H_H$');
set(gca,'ColorOrderIndex',2)
plot(freqs, (1 + abs(squeeze(freqresp(wI, freqs, 'Hz'))))./(abs(squeeze(freqresp(wS, freqs, 'Hz'))) .* (2 + abs(squeeze(freqresp(wI, freqs, 'Hz'))))), ':', 'DisplayName', 'RP for S - $H_H$');
set(gca,'ColorOrderIndex',1)
plot(freqs, 1./abs(squeeze(freqresp(wL, freqs, 'Hz'))), '-', 'DisplayName', '$w_L$');
set(gca,'ColorOrderIndex',2)
plot(freqs, 1./abs(squeeze(freqresp(wH, freqs, 'Hz'))), '-', 'DisplayName', '$w_H$');
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('Magnitude');
hold off;
xlim([freqs(1), freqs(end)]);
ylim([1e-3, 10]);
2020-10-26 17:52:18 +01:00
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 2);
2020-10-08 10:52:20 +02:00
#+end_src
2020-10-26 17:52:18 +01:00
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/weights_wl_wh.pdf', 'width', 'half', 'height', 'normal');
2020-10-08 10:52:20 +02:00
#+end_src
2020-10-26 17:52:18 +01:00
#+name : fig:weights_wl_wh
#+caption : Weights on the complementary filters $w_L$ and $w_H$ and the associated performance weights
#+RESULTS :
2020-10-08 10:52:20 +02:00
[[file:figs/weights_wl_wh.png ]]
We define the generalized plant $P$ on matlab.
#+begin_src matlab
P = [0 wL;
wH -wH;
1 0];
#+end_src
And we do the $\mathcal{H}_\infty$ synthesis using the =hinfsyn= command.
#+begin_src matlab :results output replace :exports both
[Hl_hinf, ~, gamma, ~ ] = hinfsyn(P, 1, 1,'TOLGAM', 0.001, 'METHOD', 'ric', 'DISPLAY', 'on');
#+end_src
#+RESULTS :
#+begin_example
[Hl_hinf, ~, gamma, ~ ] = hinfsyn(P, 1, 1,'TOLGAM', 0.001, 'METHOD', 'ric', 'DISPLAY', 'on');
Test bounds: 0.0000 < gamma <= 1.7285
gamma hamx_eig xinf_eig hamy_eig yinf_eig nrho_xy p/f
1.729 4.1e+01 8.4e-12 1.8e-01 0.0e+00 0.0000 p
0.864 3.9e+01 -5.8e-02# 1.8e-01 0.0e+00 0.0000 f
1.296 4.0e+01 8.4e-12 1.8e-01 0.0e+00 0.0000 p
1.080 4.0e+01 8.5e-12 1.8e-01 0.0e+00 0.0000 p
0.972 3.9e+01 -4.2e-01# 1.8e-01 0.0e+00 0.0000 f
1.026 4.0e+01 8.5e-12 1.8e-01 0.0e+00 0.0000 p
0.999 3.9e+01 8.5e-12 1.8e-01 0.0e+00 0.0000 p
0.986 3.9e+01 -1.2e+00# 1.8e-01 0.0e+00 0.0000 f
0.993 3.9e+01 -8.2e+00# 1.8e-01 0.0e+00 0.0000 f
0.996 3.9e+01 8.5e-12 1.8e-01 0.0e+00 0.0000 p
0.994 3.9e+01 8.5e-12 1.8e-01 0.0e+00 0.0000 p
0.993 3.9e+01 -3.2e+01# 1.8e-01 0.0e+00 0.0000 f
Gamma value achieved: 0.9942
#+end_example
We then define the high pass filter $H_H = 1 - H_L$. The bode plot of both $H_L$ and $H_H$ is shown on figure [[fig:hinf_filters_results ]].
#+begin_src matlab
Hh_hinf = 1 - Hl_hinf;
#+end_src
#+begin_src matlab :exports none
figure;
2020-10-26 17:52:18 +01:00
tiledlayout(3, 1, 'TileSpacing', 'None', 'Padding', 'None');
% Magnitude
ax1 = nexttile([2, 1]);
2020-10-08 10:52:20 +02:00
hold on;
set(gca,'ColorOrderIndex',1)
plot(freqs, 1./abs(squeeze(freqresp(wL, freqs, 'Hz'))), '--', 'DisplayName', '$w_L$');
set(gca,'ColorOrderIndex',2)
plot(freqs, 1./abs(squeeze(freqresp(wH, freqs, 'Hz'))), '--', 'DisplayName', '$w_H$');
set(gca,'ColorOrderIndex',1)
plot(freqs, abs(squeeze(freqresp(Hl_hinf, freqs, 'Hz'))), '-', 'DisplayName', '$H_L$ - $\mathcal{H}_ \infty$');
set(gca,'ColorOrderIndex',2)
plot(freqs, abs(squeeze(freqresp(Hh_hinf, freqs, 'Hz'))), '-', 'DisplayName', '$H_H$ - $\mathcal{H}_ \infty$');
2020-10-26 17:52:18 +01:00
hold off;
2020-10-08 10:52:20 +02:00
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
2020-10-26 17:52:18 +01:00
ylabel('Magnitude'); set(gca, 'XTickLabel',[]);
ylim([1e-3, 10]);
legend('location', 'southeast', 'FontSize', 8);
% Phase
ax2 = nexttile;
hold on;
plot(freqs, 180/pi*phase(squeeze(freqresp(Hl_hinf, freqs, 'Hz'))), '-');
plot(freqs, 180/pi*phase(squeeze(freqresp(Hh_hinf, freqs, 'Hz'))), '-');
2020-10-08 10:52:20 +02:00
hold off;
2020-10-26 17:52:18 +01:00
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
set(gca, 'XScale', 'log');
yticks([-360:90:360]);
axis padded
linkaxes([ax1,ax2],'x');
2020-10-08 10:52:20 +02:00
xlim([freqs(1), freqs(end)]);
#+end_src
2020-10-26 17:52:18 +01:00
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/hinf_filters_results.pdf', 'width', 'half', 'height', 'normal');
2020-10-08 10:52:20 +02:00
#+end_src
2020-10-26 17:52:18 +01:00
#+name : fig:hinf_filters_results
#+caption : Obtained complementary filters using $\mathcal{H}_\infty$ synthesis
#+RESULTS :
2020-10-08 10:52:20 +02:00
[[file:figs/hinf_filters_results.png ]]
2020-10-26 17:52:18 +01:00
* TODO Complementary filters using analytical formula
<<sec:analytical_formula >>
- [ ] The problem here is that the High pass filter gain goes to zero at high frequency, and when inverted, it will go to infinity.
2020-10-08 10:52:20 +02:00
We here use analytical formula for the complementary filters $H_L$ and $H_H$.
The first two formulas that are used to generate complementary filters are:
\begin{align*}
H_L(s) &= \frac{(1+\alpha) (\frac{s}{\omega_0})+1}{\left((\frac{s}{\omega_0})+1\right) \left((\frac{s}{\omega_0})^2 + \alpha (\frac{s}{\omega_0}) + 1\right)}\\
H_H(s) &= \frac{(\frac{s}{\omega_0})^2 \left((\frac{s}{\omega_0})+1+ \alpha\right)}{\left((\frac{s}{\omega_0})+1\right) \left((\frac{s}{\omega_0})^2 + \alpha (\frac{s}{\omega_0}) + 1\right)}
\end{align*}
where:
- $\omega_0$ is the blending frequency in rad/s.
- $\alpha$ is used to change the shape of the filters:
- Small values for $\alpha$ will produce high magnitude of the filters $|H_L(j\omega)|$ and $|H_H(j\omega)|$ near $\omega_0$ but smaller value for $|H_L(j\omega)|$ above $\approx 1.5 \omega_0$ and for $|H_H(j\omega)|$ below $\approx 0.7 \omega_0$
- A large $\alpha$ will do the opposite
This is illustrated on figure [[fig:comp_filters_param_alpha ]].
As it is usually wanted to have the $\| S \|_\infty < 2$, $\alpha$ between $0.5$ and $1$ gives a good trade-off between the performance and the robustness.
The slope of those filters at high and low frequencies is $-2$ and $2$ respectively for $H_L$ and $H_H$.
#+begin_src matlab :exports none
freqs_study = logspace(-2, 2, 10000);
alphas = [0.1, 1, 10];
w0 = 2*pi*1;
figure;
ax1 = subaxis(2,1,1);
hold on;
for i = 1:length(alphas)
alpha = alphas(i);
Hh2 = (s/w0)^2*((s/w0)+1+alpha)/(((s/w0)+1)* ((s/w0)^2 + alpha*(s/w0) + 1));
Hl2 = ((1+alpha)*(s/w0)+1)/(((s/w0)+1)* ((s/w0)^2 + alpha*(s/w0) + 1));
set(gca,'ColorOrderIndex',i);
plot(freqs_study, abs(squeeze(freqresp(Hh2, freqs_study, 'Hz'))));
set(gca,'ColorOrderIndex',i);
plot(freqs_study, abs(squeeze(freqresp(Hl2, freqs_study, 'Hz'))));
end
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
2020-10-26 17:52:18 +01:00
ylabel('Magnitude'); set(gca, 'XTickLabel',[]);
2020-10-08 10:52:20 +02:00
hold off;
ylim([1e-3, 20]);
% Phase
ax2 = subaxis(2,1,2);
hold on;
for i = 1:length(alphas)
alpha = alphas(i);
Hh2 = (s/w0)^2*((s/w0)+1+alpha)/(((s/w0)+1)* ((s/w0)^2 + alpha*(s/w0) + 1));
Hl2 = ((1+alpha)*(s/w0)+1)/(((s/w0)+1)* ((s/w0)^2 + alpha*(s/w0) + 1));
set(gca,'ColorOrderIndex',i);
plot(freqs_study, 180/pi*angle(squeeze(freqresp(Hh2, freqs_study, 'Hz'))), 'DisplayName', sprintf('$\\alpha = %g$', alpha));
set(gca,'ColorOrderIndex',i);
plot(freqs_study, 180/pi*angle(squeeze(freqresp(Hl2, freqs_study, 'Hz'))), 'HandleVisibility', 'off');
end
set(gca,'xscale','log');
yticks(-180:90:180);
ylim([-180 180]);
xlabel('Relative Frequency $\frac{\omega}{\omega_0}$'); ylabel('Phase [deg]');
2020-10-26 17:52:18 +01:00
legend('Location', 'northeast', 'FontSize', 8);
2020-10-08 10:52:20 +02:00
hold off;
linkaxes([ax1,ax2],'x');
xlim([freqs_study(1), freqs_study(end)]);
#+end_src
2020-10-26 17:52:18 +01:00
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/comp_filters_param_alpha.pdf', 'width', 'half', 'height', 'tall');
2020-10-08 10:52:20 +02:00
#+end_src
2020-10-26 17:52:18 +01:00
#+name : fig:comp_filters_param_alpha
#+caption : Effect of the parameter $\alpha$ on the shape of the generated second order complementary filters
#+RESULTS :
2020-10-08 10:52:20 +02:00
[[file:figs/comp_filters_param_alpha.png ]]
The parameters $\alpha$ and $\omega_0$ are chosen in order to have that the complementary filters stay below the defined upper bounds.
The obtained complementary filters are shown on figure [[fig:complementary_filters_second_order ]].
The Robust Performance is not fulfilled for $T$, and we see that the RP condition as a slop of $-3$. We thus have to use different formula for the complementary filters here.
#+begin_src matlab
w0 = 2*pi*13;
alpha = 0.8;
Hh2_ana = (s/w0)^2*((s/w0)+1+alpha)/(((s/w0)+1)* ((s/w0)^2 + alpha*(s/w0) + 1));
Hl2_ana = ((1+alpha)*(s/w0)+1)/(((s/w0)+1)* ((s/w0)^2 + alpha*(s/w0) + 1));
#+end_src
#+begin_src matlab :exports none
figure;
hold on;
set(gca,'ColorOrderIndex',1)
plot(freqs, 1./abs(squeeze(freqresp(wT, freqs, 'Hz'))), '--', 'DisplayName', 'NP - $H_L$');
set(gca,'ColorOrderIndex',1)
plot(freqs, 1./(abs(squeeze(freqresp(wT, freqs, 'Hz'))) .* (1 + abs(squeeze(freqresp(wI, freqs, 'Hz')))) + abs(squeeze(freqresp(wI, freqs, 'Hz')))), ':', 'DisplayName', 'RP for T - $H_L$');
set(gca,'ColorOrderIndex',1)
plot(freqs, 1./(abs(squeeze(freqresp(wI, freqs, 'Hz'))) .* (2 + abs(squeeze(freqresp(wI, freqs, 'Hz'))))), '-.', 'DisplayName', 'RS - $H_L$');
set(gca,'ColorOrderIndex',2)
plot(freqs, 1./abs(squeeze(freqresp(wS, freqs, 'Hz'))), '--', 'DisplayName', 'NP - $H_H$');
set(gca,'ColorOrderIndex',2)
plot(freqs, (1 + abs(squeeze(freqresp(wI, freqs, 'Hz'))))./(abs(squeeze(freqresp(wS, freqs, 'Hz'))) .* (2 + abs(squeeze(freqresp(wI, freqs, 'Hz'))))), ':', 'DisplayName', 'RP for S - $H_H$');
set(gca,'ColorOrderIndex',1)
plot(freqs, abs(squeeze(freqresp(Hl2_ana, freqs, 'Hz'))), '-', 'DisplayName', '$H_L$ - Analytical');
set(gca,'ColorOrderIndex',2)
plot(freqs, abs(squeeze(freqresp(Hh2_ana, freqs, 'Hz'))), '-', 'DisplayName', '$H_H$ - Analytical');
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('Magnitude');
hold off;
xlim([freqs(1), freqs(end)]);
ylim([1e-3, 10]);
xticks([0.1, 1, 10, 100, 1000]);
2020-10-26 17:52:18 +01:00
legend('location', 'southeast', 'FontSize', 8);
2020-10-08 10:52:20 +02:00
#+end_src
2020-10-26 17:52:18 +01:00
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/complementary_filters_second_order.pdf', 'width', 'half', 'height', 'normal');
2020-10-08 10:52:20 +02:00
#+end_src
2020-10-26 17:52:18 +01:00
#+name : fig:complementary_filters_second_order
#+caption : Second order complementary filters using the analytical formula
#+RESULTS :
2020-10-08 10:52:20 +02:00
[[file:figs/complementary_filters_second_order.png ]]
The following formula gives complementary filters with slopes of $-3$ and $3$:
\begin{align*}
H_L(s) &= \frac{\left(1+(\alpha+1)(\beta+1)\right) (\frac{s}{\omega_0})^2 + (1+\alpha+ \beta)(\frac{s}{\omega_0}) + 1}{\left(\frac{s}{\omega_0} + 1\right) \left( (\frac{s}{\omega_0})^2 + \alpha (\frac{s}{\omega_0}) + 1 \right) \left( (\frac{s}{\omega_0})^2 + \beta (\frac{s}{\omega_0}) + 1 \right)}\\
H_H(s) &= \frac{(\frac{s}{\omega_0})^3 \left( (\frac{s}{\omega_0})^2 + (1+ \alpha+\beta) (\frac{s}{\omega_0}) + (1+(\alpha+1)(\beta+1)) \right)}{\left(\frac{s}{\omega_0} + 1\right) \left( (\frac{s}{\omega_0})^2 + \alpha (\frac{s}{\omega_0}) + 1 \right) \left( (\frac{s}{\omega_0})^2 + \beta (\frac{s}{\omega_0}) + 1 \right)}
\end{align*}
The parameters are:
- $\omega_0$ is the blending frequency in rad/s
- $\alpha$ and $\beta$ that are used to change the shape of the filters similarly to the parameter $\alpha$ for the second order complementary filters
The filters are defined below and the result is shown on figure [[fig:complementary_filters_third_order ]] where we can see that the complementary filters are below the defined upper bounds.
#+begin_src matlab
alpha = 1;
beta = 10;
w0 = 2*pi*14;
Hh3_ana = (s/w0)^3 * ((s/w0)^2 + (1+alpha+beta)*(s/w0) + (1+(alpha+1)*(beta+1)))/((s/w0 + 1)* ((s/w0)^2+alpha*(s/w0)+1)* ((s/w0)^2+beta*(s/w0)+1));
Hl3_ana = ((1+(alpha+1)*(beta+1))* (s/w0)^2 + (1+alpha+beta)*(s/w0) + 1)/((s/w0 + 1)*((s/w0)^2+alpha* (s/w0)+1)*((s/w0)^2+beta* (s/w0)+1));
#+end_src
#+begin_src matlab :exports none
figure;
hold on;
set(gca,'ColorOrderIndex',1)
plot(freqs, 1./abs(squeeze(freqresp(wT, freqs, 'Hz'))), '--', 'DisplayName', 'NP - $H_L$');
set(gca,'ColorOrderIndex',1)
plot(freqs, 1./(abs(squeeze(freqresp(wT, freqs, 'Hz'))) .* (1 + abs(squeeze(freqresp(wI, freqs, 'Hz')))) + abs(squeeze(freqresp(wI, freqs, 'Hz')))), ':', 'DisplayName', 'RP for T - $H_L$');
set(gca,'ColorOrderIndex',1)
plot(freqs, 1./(abs(squeeze(freqresp(wI, freqs, 'Hz'))) .* (2 + abs(squeeze(freqresp(wI, freqs, 'Hz'))))), '-.', 'DisplayName', 'RS - $H_L$');
set(gca,'ColorOrderIndex',2)
plot(freqs, 1./abs(squeeze(freqresp(wS, freqs, 'Hz'))), '--', 'DisplayName', 'NP - $H_H$');
set(gca,'ColorOrderIndex',2)
plot(freqs, (1 + abs(squeeze(freqresp(wI, freqs, 'Hz'))))./(abs(squeeze(freqresp(wS, freqs, 'Hz'))) .* (2 + abs(squeeze(freqresp(wI, freqs, 'Hz'))))), ':', 'DisplayName', 'RP for S - $H_H$');
set(gca,'ColorOrderIndex',1)
plot(freqs, abs(squeeze(freqresp(Hl3_ana, freqs, 'Hz'))), '-', 'DisplayName', '$H_L$ - Analytical');
set(gca,'ColorOrderIndex',2)
plot(freqs, abs(squeeze(freqresp(Hh3_ana, freqs, 'Hz'))), '-', 'DisplayName', '$H_H$ - Analytical');
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('Magnitude');
hold off;
xlim([freqs(1), freqs(end)]);
ylim([1e-3, 10]);
xticks([0.1, 1, 10, 100, 1000]);
2020-10-26 17:52:18 +01:00
legend('location', 'southeast', 'FontSize', 8);
2020-10-08 10:52:20 +02:00
#+end_src
2020-10-26 17:52:18 +01:00
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/complementary_filters_third_order.pdf', 'width', 'half', 'height', 'normal');
2020-10-08 10:52:20 +02:00
#+end_src
2020-10-26 17:52:18 +01:00
#+name : fig:complementary_filters_third_order
#+caption : Third order complementary filters using the analytical formula
#+RESULTS :
2020-10-08 10:52:20 +02:00
[[file:figs/complementary_filters_third_order.png ]]
* Comparison of complementary filters
2020-10-26 17:52:18 +01:00
<<sec:comp_filters >>
2020-10-08 10:52:20 +02:00
The generated complementary filters using $\mathcal{H}_\infty$ and the analytical formulas are compared on figure [[fig:comp_hinf_analytical ]].
Although they are very close to each other, there is some difference to note here:
- the analytical formula provides a very simple way to generate the complementary filters (and thus the controller), they could even be used to tune the controller online using the parameters $\alpha$ and $\omega_0$. However, these formula have the property that $|H_H|$ and $|H_L|$ are symmetrical with the frequency $\omega_0$ which may not be desirable.
- while the $\mathcal{H}_\infty$ synthesis of the complementary filters is not as straightforward as using the analytical formula, it provides a more optimized procedure to obtain the complementary filters
The complementary filters obtained with the $\mathcal{H}_\infty$ will be used for further analysis.
#+begin_src matlab :exports none
figure;
2020-10-26 17:52:18 +01:00
tiledlayout(2, 1, 'TileSpacing', 'None', 'Padding', 'None');
2020-10-08 10:52:20 +02:00
2020-10-26 17:52:18 +01:00
% Magnitude
ax1 = nexttile;
2020-10-08 10:52:20 +02:00
hold on;
set(gca,'ColorOrderIndex',1)
plot(freqs, abs(squeeze(freqresp(Hl_hinf, freqs, 'Hz'))), '--');
set(gca,'ColorOrderIndex',2)
plot(freqs, abs(squeeze(freqresp(Hh_hinf, freqs, 'Hz'))), '--');
set(gca,'ColorOrderIndex',1)
plot(freqs, abs(squeeze(freqresp(Hl2_ana, freqs, 'Hz'))), '-');
set(gca,'ColorOrderIndex',2)
plot(freqs, abs(squeeze(freqresp(Hh2_ana, freqs, 'Hz'))), '-');
set(gca,'ColorOrderIndex',1)
plot(freqs, abs(squeeze(freqresp(Hl3_ana, freqs, 'Hz'))), ':');
set(gca,'ColorOrderIndex',2)
plot(freqs, abs(squeeze(freqresp(Hh3_ana, freqs, 'Hz'))), ':');
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
2020-10-26 17:52:18 +01:00
ylabel('Magnitude'); set(gca, 'XTickLabel',[]);
2020-10-08 10:52:20 +02:00
hold off;
ylim([1e-4, 10]);
2020-10-26 17:52:18 +01:00
% Phase
ax2 = nexttile;
2020-10-08 10:52:20 +02:00
hold on;
set(gca,'ColorOrderIndex',1)
plot(freqs, 180/pi*phase(squeeze(freqresp(Hl_hinf, freqs, 'Hz'))), '--', 'DisplayName', '$H_L$ - $\mathcal{H}_ \infty$');
set(gca,'ColorOrderIndex',2)
plot(freqs, 180/pi*phase(squeeze(freqresp(Hh_hinf, freqs, 'Hz'))), '--', 'DisplayName', '$H_H$ - $\mathcal{H}_ \infty$');
set(gca,'ColorOrderIndex',1)
plot(freqs, 180/pi*phase(squeeze(freqresp(Hl2_ana, freqs, 'Hz'))), '-', 'DisplayName', '$H_L$ - $2$');
set(gca,'ColorOrderIndex',2)
plot(freqs, 180/pi*phase(squeeze(freqresp(Hh2_ana, freqs, 'Hz'))), '-', 'DisplayName', '$H_H$ - $2$');
set(gca,'ColorOrderIndex',1)
plot(freqs, 180/pi*phase(squeeze(freqresp(Hl3_ana, freqs, 'Hz'))), ':', 'DisplayName', '$H_L$ - $3$');
set(gca,'ColorOrderIndex',2)
plot(freqs, 180/pi*phase(squeeze(freqresp(Hh3_ana, freqs, 'Hz')))+360, ':', 'DisplayName', '$H_H$ - $3$');
set(gca, 'XScale', 'log');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks([-360:90:360]);
2020-10-26 17:52:18 +01:00
legend('location', 'northeast', 'FontSize', 8);
2020-10-08 10:52:20 +02:00
linkaxes([ax1,ax2],'x');
xlim([freqs(1), freqs(end)]);
xticks([0.1, 1, 10, 100, 1000]);
#+end_src
2020-10-26 17:52:18 +01:00
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/comp_hinf_analytical.pdf', 'width', 'half', 'height', 'tall');
2020-10-08 10:52:20 +02:00
#+end_src
2020-10-26 17:52:18 +01:00
#+name : fig:comp_hinf_analytical
#+caption : Comparison of the complementary filters obtained with $\mathcal{H}_\infty$ synthesis and with the analytical formula
#+RESULTS :
2020-10-08 10:52:20 +02:00
[[file:figs/comp_hinf_analytical.png ]]
* Controller Analysis
2020-10-26 17:52:18 +01:00
<<sec:controller_analysis >>
2020-10-08 10:52:20 +02:00
The controller $K$ is computed from the plant model $G$ and the low pass filter $H_H$:
\[ K = G^{-1} H_H^{-1} \]
As this is not proper and thus realizable, a second order low pass filter is added with a crossover frequency much larger than the control bandwidth.
#+begin_src matlab
2020-10-26 17:52:18 +01:00
omega = 2*pi*500;
2020-10-08 10:52:20 +02:00
K = 1/(Hh_hinf*G) * 1/ ((1+s/omega)*(1+s/omega+ (s/omega)^2));
#+end_src
#+begin_src matlab :exports none
K = zpk(minreal(K));
#+end_src
#+begin_src matlab :results output replace :exports results :wrap example
zpk(K)
#+end_src
#+RESULTS :
#+begin_example
zpk(K)
ans =
4.961e12 (s+9.915e04) (s^2 + 5s + 500) (s^2 + 284.6s + 2.135e04) (s^2 + 130.5s + 9887)
--------------------------------------------------------------------------------------------------
(s+9.914e04) (s+6283) (s^2 + 0.3576s + 0.03198) (s^2 + 413.8s + 6.398e04) (s^2 + 6283s + 3.948e07)
Continuous-time zero/pole/gain model.
#+end_example
The bode plot of the controller is shown on figure [[fig:bode_plot_controller ]]:
- two integrator are present at low frequency
- the resonance of the plant at $3.5\ \text{Hz}$ is inverted (notched)
- a lead is added at $10\ \text{Hz}$
#+begin_src matlab :exports none
figure;
2020-10-26 17:52:18 +01:00
tiledlayout(3, 1, 'TileSpacing', 'None', 'Padding', 'None');
2020-10-08 10:52:20 +02:00
% Magnitude
2020-10-26 17:52:18 +01:00
ax1 = nexttile([2, 1]);
2020-10-08 10:52:20 +02:00
hold on;
plot(freqs, abs(squeeze(freqresp(K, freqs, 'Hz'))), 'k-');
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
2020-10-26 17:52:18 +01:00
ylabel('Magnitude'); set(gca, 'XTickLabel',[]);
ylim([8e3, 1e8])
2020-10-08 10:52:20 +02:00
hold off;
% Phase
2020-10-26 17:52:18 +01:00
ax2 = nexttile;
2020-10-08 10:52:20 +02:00
hold on;
plot(freqs, 180/pi*angle(squeeze(freqresp(K, freqs, 'Hz'))), 'k-');
set(gca,'xscale','log');
yticks(-180:90:180);
ylim([-180 180]);
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
2020-10-26 17:52:18 +01:00
2020-10-08 10:52:20 +02:00
linkaxes([ax1,ax2],'x');
xlim([freqs(1), freqs(end)]);
#+end_src
2020-10-26 17:52:18 +01:00
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/bode_plot_controller.pdf', 'width', 'half', 'height', 'normal');
2020-10-08 10:52:20 +02:00
#+end_src
2020-10-26 17:52:18 +01:00
#+name : fig:bode_plot_controller
#+caption : Bode plot of the obtained controller $K$
#+RESULTS :
2020-10-08 10:52:20 +02:00
[[file:figs/bode_plot_controller.png ]]
* Nominal Stability and Nominal Performance
2020-10-26 17:52:18 +01:00
<<sec:nominal_stability_performance >>
2020-10-08 10:52:20 +02:00
The nominal stability of the system is first checked with the =allmargin= matlab command.
#+begin_src matlab :results output replace
allmargin(K*G*Hl_hinf)
#+end_src
#+RESULTS :
#+begin_example
allmargin(K*G*Hl_hinf)
ans =
struct with fields:
2020-10-26 17:52:18 +01:00
GainMargin: 3.86196691340257
GMFrequency: 225.992831144248
PhaseMargin: 34.0890006269983
PMFrequency: 88.3632737129296
DelayMargin: 0.0067331740287082
DMFrequency: 88.3632737129296
2020-10-08 10:52:20 +02:00
Stable: 1
#+end_example
The system is stable and the stability margins are good.
The bode plot of the loop gain $L = K*G*H_L$ is shown on figure [[fig:bode_plot_loop_gain ]].
#+begin_src matlab :exports none
figure;
2020-10-26 17:52:18 +01:00
tiledlayout(3, 1, 'TileSpacing', 'None', 'Padding', 'None');
2020-10-08 10:52:20 +02:00
% Magnitude
2020-10-26 17:52:18 +01:00
ax1 = nexttile([2, 1]);
2020-10-08 10:52:20 +02:00
hold on;
plot(freqs, abs(squeeze(freqresp(K*G*Hl_hinf, freqs, 'Hz'))), 'k-');
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]);
2020-10-26 17:52:18 +01:00
ylabel('Loop Gain');
2020-10-08 10:52:20 +02:00
hold off;
2020-10-26 17:52:18 +01:00
ylim([1e-3, 1e2])
2020-10-08 10:52:20 +02:00
% Phase
2020-10-26 17:52:18 +01:00
ax2 = nexttile;
2020-10-08 10:52:20 +02:00
hold on;
plot(freqs, 180/pi*unwrap(angle(squeeze(freqresp(K*G*Hl_hinf, freqs, 'Hz')))), 'k-');
set(gca,'xscale','log');
yticks(-270:90:0);
ylim([-270 0]);
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
2020-10-26 17:52:18 +01:00
2020-10-08 10:52:20 +02:00
linkaxes([ax1,ax2],'x');
xlim([freqs(1), freqs(end)]);
#+end_src
2020-10-26 17:52:18 +01:00
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/bode_plot_loop_gain.pdf', 'width', 'half', 'height', 'normal');
2020-10-08 10:52:20 +02:00
#+end_src
2020-10-26 17:52:18 +01:00
#+name : fig:bode_plot_loop_gain
#+caption : Bode Plot of the Loop Gain $L = K G H_L$
#+RESULTS :
2020-10-08 10:52:20 +02:00
[[file:figs/bode_plot_loop_gain.png ]]
In order to check the Nominal Performance of the system, we compute the sensibility and the complementary sensibility transfer functions.
#+begin_src matlab
S = 1/(K*G*Hl_hinf + 1);
T = K*G*Hl_hinf/(K*G*Hl_hinf + 1);
#+end_src
We then compare their norms with the upper bounds on the performance of the system (figure [[fig:verification_NP ]]).
As expected, we guarantee the Nominal Performance of the system.
#+begin_src matlab :exports none
figure;
hold on;
set(gca,'ColorOrderIndex',1)
plot([100, 1000], [0.1, 0.001], ':', 'DisplayName', 'Upper bound on $|T|$');
set(gca,'ColorOrderIndex',2)
plot([0.1, 0.2, 2], [0.001, 0.001, 0.1], ':', 'DisplayName', 'Upper bound on $|S|$');
set(gca,'ColorOrderIndex',1)
plot(freqs, abs(squeeze(freqresp(T, freqs, 'Hz'))), '-', 'DisplayName', '$T$');
set(gca,'ColorOrderIndex',2)
plot(freqs, abs(squeeze(freqresp(S, freqs, 'Hz'))), '-', 'DisplayName', '$S$');
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('Magnitude');
hold off;
xlim([freqs(1), freqs(end)]);
2020-10-26 17:52:18 +01:00
ylim([1e-4, 5]);
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 2);
2020-10-08 10:52:20 +02:00
#+end_src
2020-10-26 17:52:18 +01:00
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/verification_NP.pdf', 'width', 'half', 'height', 'normal');
2020-10-08 10:52:20 +02:00
#+end_src
2020-10-26 17:52:18 +01:00
#+name : fig:verification_NP
#+caption : Bode plot of $S$ and $T$ in order to verify the nominal performance of the system
#+RESULTS :
2020-10-08 10:52:20 +02:00
[[file:figs/verification_NP.png ]]
* Robust Stability and Robust Performance
2020-10-26 17:52:18 +01:00
<<sec:robustness_analysis >>
2020-10-08 10:52:20 +02:00
In order to verify the Robust stability of the system, we can use the following equivalence:
\[ \text{RS} \Leftrightarrow \left| w_I T \right| < 1 \quad \forall \omega \]
This is shown on figure [[fig:robust_stability ]].
#+begin_src matlab :exports none
Ts = Gds*K*Hl_hinf/(Gds*K*Hl_hinf + 1);
Ss = 1/(Gds*K*Hl_hinf + 1);
#+end_src
#+begin_src matlab :exports none
figure;
hold on;
plot(freqs, abs(squeeze(freqresp(wI*T, freqs, 'Hz'))), 'k-');
plot([freqs(1) freqs(end)], [1 1], 'k--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
2020-10-26 17:52:18 +01:00
xlabel('Frequency [Hz]'); ylabel('Magnitude');
xlim([freqs(1), freqs(end)]);
ylim([0.02, 2]);
2020-10-08 10:52:20 +02:00
#+end_src
2020-10-26 17:52:18 +01:00
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/robust_stability.pdf', 'width', 'half', 'height', 'small');
2020-10-08 10:52:20 +02:00
#+end_src
2020-10-26 17:52:18 +01:00
#+name : fig:robust_stability
#+caption : Robust Stability Check: $|w_I T| < 1, \quad \forall \omega$
#+RESULTS :
[[file:figs/robust_stability.png ]]
2020-10-08 10:52:20 +02:00
To check Robust Stability, we can also look at the loop gain of the uncertain system (figure [[fig:loop_gain_robustness ]]) or the Nyquist plot (figure [[fig:nyquist_robustness ]]).
2020-10-26 17:52:18 +01:00
#+begin_src matlab :exports none
2020-10-08 10:52:20 +02:00
figure;
2020-10-26 17:52:18 +01:00
tiledlayout(3, 1, 'TileSpacing', 'None', 'Padding', 'None');
% Magnitude
ax1 = nexttile([2, 1]);
2020-10-08 10:52:20 +02:00
hold on;
for i=1:length(Gds)
plot(freqs, abs(squeeze(freqresp(Gds(:, :, i)*K*Hl_hinf, freqs, 'Hz'))), '-', 'color', [0, 0, 0, 0.1]);
end
plot(freqs, abs(squeeze(freqresp(G*K*Hl_hinf, freqs, 'Hz'))), 'k-');
hold off;
2020-10-26 17:52:18 +01:00
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Loop Gain'); set(gca, 'XTickLabel',[]);
ylim([1e-3, 1e2])
2020-10-08 10:52:20 +02:00
% Phase
2020-10-26 17:52:18 +01:00
ax2 = nexttile;
2020-10-08 10:52:20 +02:00
hold on;
for i=1:length(Gds)
plot(freqs, 180/pi*unwrap(angle(squeeze(freqresp(Gds(:, :, i)*K*Hl_hinf, freqs, 'Hz')))), '-', 'color', [0, 0, 0, 0.1]);
end
plot(freqs, 180/pi*unwrap(angle(squeeze(freqresp(G*K*Hl_hinf, freqs, 'Hz')))), 'k-');
2020-10-26 17:52:18 +01:00
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
2020-10-08 10:52:20 +02:00
yticks(-360:90:180);
ylim([-270 0]);
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
2020-10-26 17:52:18 +01:00
2020-10-08 10:52:20 +02:00
linkaxes([ax1,ax2],'x');
xlim([freqs(1), freqs(end)]);
#+end_src
2020-10-26 17:52:18 +01:00
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/loop_gain_robustness.pdf', 'width', 'half', 'height', 'normal');
2020-10-08 10:52:20 +02:00
#+end_src
2020-10-26 17:52:18 +01:00
#+name : fig:loop_gain_robustness
#+caption : Loop Gain of the uncertain system
#+RESULTS :
2020-10-08 10:52:20 +02:00
[[file:figs/loop_gain_robustness.png ]]
#+begin_src matlab :exports none
freqs_nyquist = logspace(0, 4, 100);
figure;
hold on;
for i=1:length(Gds)
plot(real(squeeze(freqresp(Gds(:, :, i)*K*Hl_hinf, freqs_nyquist, 'Hz'))), imag(squeeze(freqresp(Gds(:, :, i)*K*Hl_hinf, freqs_nyquist, 'Hz'))), 'color', [0, 0, 0, 0.1]);
end
plot(real(squeeze(freqresp(G*K*Hl_hinf, freqs_nyquist, 'Hz'))), imag(squeeze(freqresp(G*K*Hl_hinf, freqs_nyquist, 'Hz'))), 'k');
hold off;
xlim([-1.4, 0.2]); ylim([-1.4, 0.2]);
xticks(-1.4:0.2:0.2); yticks(-1.4:0.2:0.2);
xlabel('Real Part'); ylabel('Imaginary Part');
#+end_src
2020-10-26 17:52:18 +01:00
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/nyquist_robustness.pdf', 'width', 'half', 'height', 'small');
2020-10-08 10:52:20 +02:00
#+end_src
2020-10-26 17:52:18 +01:00
#+name : fig:nyquist_robustness
#+caption : Nyquist plot of the uncertain system
#+RESULTS :
2020-10-08 10:52:20 +02:00
[[file:figs/nyquist_robustness.png ]]
The Robust Performance is verified by plotting $|S|$ and $|T|$ for the uncertain system along side the upper bounds defined for performance.
This is shown on figure [[fig:robust_performance_result ]] and we can indeed confirmed that the robust performance property of the system is valid.
#+begin_src matlab :exports none
figure;
hold on;
2020-10-26 17:52:18 +01:00
for i=1:length(Gds)
2020-10-08 10:52:20 +02:00
plot(freqs, abs(squeeze(freqresp(Ts(:, :, i), freqs, 'Hz'))), 'color', [0, 0.4470, 0.7410, 0.1] , 'HandleVisibility', 'off');
plot(freqs, abs(squeeze(freqresp(Ss(:, :, i), freqs, 'Hz'))), 'color', [0.8500, 0.3250, 0.0980, 0.1], 'HandleVisibility', 'off');
end
set(gca,'ColorOrderIndex',1)
2020-10-26 17:52:18 +01:00
plot(freqs, abs(squeeze(freqresp(G*K*Hl_hinf/(1+G*K*Hl_hinf), freqs, 'Hz'))), 'DisplayName', '$|T|$');
2020-10-08 10:52:20 +02:00
set(gca,'ColorOrderIndex',2)
2020-10-26 17:52:18 +01:00
plot(freqs, abs(squeeze(freqresp(1/(1+G*K*Hl_hinf), freqs, 'Hz'))), 'DisplayName', '$|S|$');
2020-10-08 10:52:20 +02:00
set(gca,'ColorOrderIndex',1)
2020-10-26 17:52:18 +01:00
plot([100, 1000], [0.1, 0.001], ':', 'DisplayName', '$|T|$ - Spec.');
2020-10-08 10:52:20 +02:00
set(gca,'ColorOrderIndex',2)
2020-10-26 17:52:18 +01:00
plot([0.1, 0.2, 2], [0.001, 0.001, 0.1], ':', 'DisplayName', '$|S|$ - Spec.');
2020-10-08 10:52:20 +02:00
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
hold off;
2020-10-26 17:52:18 +01:00
xlabel('Frequency [Hz]'); ylabel('Magnitude');
2020-10-08 10:52:20 +02:00
xlim([freqs(1), freqs(end)]);
ylim([1e-4, 5]);
xticks([0.1, 1, 10, 100, 1000]);
2020-10-26 17:52:18 +01:00
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 2);
2020-10-08 10:52:20 +02:00
#+end_src
2020-10-26 17:52:18 +01:00
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/robust_performance_result.pdf', 'width', 'half', 'height', 'normal');
2020-10-08 10:52:20 +02:00
#+end_src
2020-10-26 17:52:18 +01:00
#+name : fig:robust_performance_result
#+caption : Verification of the Robust Performance
#+RESULTS :
2020-10-08 10:52:20 +02:00
[[file:figs/robust_performance_result.png ]]
* Pre-filter
2020-10-26 17:52:18 +01:00
<<sec:pre_filter >>
2020-10-08 10:52:20 +02:00
For now, we have not specified any performance requirements on the input usage due to change of reference.
Do limit the input usage due to change of reference, we can use a pre-filter $K_r$ as shown on figure [[fig:sf_arch_class_prefilter ]].
If we want a response time of 0.01 second, we can choose a first order low pass filter with a crossover frequency at $1/0.01 = 100\ \text{Hz}$ as defined below.
#+begin_src matlab
Kr = 1/(1+s/2/pi/100);
#+end_src
We then run a simulation for a step of $1\mu m$ for the system without and with the pre-filter $K_r$ (figure [[fig:u_and_y_with_Kr ]]).
This confirms that a pre-filter can be used to limit the input usage due to change of reference using this architecture.
#+begin_src matlab
2020-10-26 17:52:18 +01:00
t = linspace(0, 0.01, 500);
2020-10-08 10:52:20 +02:00
opts = stepDataOptions;
opts.StepAmplitude = 1e-6;
u = step((K)/(1+G*K*Hl_hinf), t, opts);
uf = step((Kr*K)/(1+G*K*Hl_hinf), t, opts);
y = step((K*G)/(1+G*K*Hl_hinf), t, opts);
yf = step((Kr*G*K)/(1+G*K*Hl_hinf), t, opts);
#+end_src
#+begin_src matlab :exports none
figure;
2020-10-26 17:52:18 +01:00
tiledlayout(2, 1, 'TileSpacing', 'None', 'Padding', 'None');
2020-10-08 10:52:20 +02:00
2020-10-26 17:52:18 +01:00
ax1 = nexttile;
2020-10-08 10:52:20 +02:00
hold on;
2020-10-26 17:52:18 +01:00
plot(t, u, 'DisplayName', 'Without Pre-filter');
plot(t, uf, 'DisplayName', 'With Pre-Filter');
2020-10-08 10:52:20 +02:00
hold off;
2020-10-26 17:52:18 +01:00
ylabel('Input $u$ [N]'); set(gca, 'XTickLabel',[]);
legend('location', 'northeast', 'FontSize', 8);
2020-10-08 10:52:20 +02:00
2020-10-26 17:52:18 +01:00
ax2 = nexttile;
2020-10-08 10:52:20 +02:00
hold on;
2020-10-26 17:52:18 +01:00
plot(t, y);
plot(t, yf);
2020-10-08 10:52:20 +02:00
hold off;
2020-10-26 17:52:18 +01:00
xlabel('Time [s]'); ylabel('Output [m]');
2020-10-08 10:52:20 +02:00
#+end_src
2020-10-26 17:52:18 +01:00
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/u_and_y_with_Kr.pdf', 'width', 'half', 'height', 'normal');
2020-10-08 10:52:20 +02:00
#+end_src
2020-10-26 17:52:18 +01:00
#+name : fig:u_and_y_with_Kr
#+caption : Input usage and response due to a step change of reference when using a pre-filter $K_r$
#+RESULTS :
[[file:figs/u_and_y_with_Kr.png ]]
2020-10-08 10:52:20 +02:00
2020-10-26 17:52:18 +01:00
* Matlab Functions
<<sec:matlab_functions >>
:PROPERTIES:
:header-args:matlab+: :comments none :mkdirp yes :eval no
:END:
2020-10-08 10:52:20 +02:00
2020-10-26 17:52:18 +01:00
** =createWeight=
<<sec:createWeight >>
This Matlab function is accessible [[file:src/createWeight.m ][here ]].
#+begin_src matlab :tangle src/createWeight.m
function [W] = createWeight(args)
% createWeight -
%
% Syntax: [in_data] = createWeight(in_data)
%
% Inputs:
% - n - Weight Order
% - G0 - Low frequency Gain
% - G1 - High frequency Gain
% - Gc - Gain of W at frequency w0
% - w0 - Frequency at which |W(j w0)| = Gc
%
% Outputs:
% - W - Generated Weight
arguments
args.n (1,1) double {mustBeInteger, mustBePositive} = 1
args.G0 (1,1) double {mustBeNumeric, mustBePositive} = 0.1
args.G1 (1,1) double {mustBeNumeric, mustBePositive} = 10
args.Gc (1,1) double {mustBeNumeric, mustBePositive} = 1
args.w0 (1,1) double {mustBeNumeric, mustBePositive} = 1
end
mustBeBetween(args.G0, args.Gc, args.G1);
s = tf('s');
W = (((1/args.w0)*sqrt((1-(args.G0/args.Gc)^(2/args.n))/ (1-(args.Gc/args.G1)^(2/args.n)))*s + (args.G0/args.Gc)^(1/args.n))/((1/args.G1)^(1/args.n)* (1/args.w0)*sqrt((1-(args.G0/args.Gc)^(2/args.n))/ (1-(args.Gc/args.G1)^(2/args.n)))*s + (1/args.Gc)^(1/args.n)))^args.n;
end
% Custom validation function
function mustBeBetween(a,b,c)
if ~((a > b && b > c) || (c > b && b > a))
eid = 'createWeight:inputError';
msg = 'Gc should be between G0 and G1.';
throwAsCaller(MException(eid,msg))
end
end
#+end_src
** =plotMagUncertainty=
<<sec:plotMagUncertainty >>
This Matlab function is accessible [[file:src/plotMagUncertainty.m ][here ]].
#+begin_src matlab :tangle src/plotMagUncertainty.m
function [p] = plotMagUncertainty(W, freqs, args)
% plotMagUncertainty -
%
% Syntax: [p] = plotMagUncertainty(W, freqs, args)
%
% Inputs:
% - W - Multiplicative Uncertainty Weight
% - freqs - Frequency Vector [Hz]
% - args - Optional Arguments:
% - G
% - color_i
% - opacity
%
% Outputs:
% - p - Plot Handle
arguments
W
freqs double {mustBeNumeric, mustBeNonnegative}
args.G = tf(1)
args.color_i (1,1) double {mustBeInteger, mustBeNonnegative} = 0
args.opacity (1,1) double {mustBeNumeric, mustBeNonnegative} = 0.3
args.DisplayName char = ''
2020-10-08 10:52:20 +02:00
end
2020-10-26 17:52:18 +01:00
% Get defaults colors
colors = get(groot, 'defaultAxesColorOrder');
2020-10-08 10:52:20 +02:00
2020-10-26 17:52:18 +01:00
p = patch([freqs flip(freqs)], ...
[abs(squeeze(freqresp(args.G, freqs, 'Hz'))).*(1 + abs(squeeze(freqresp(W, freqs, 'Hz')))); ...
flip(abs(squeeze(freqresp(args.G, freqs, 'Hz'))).*max(1 - abs(squeeze(freqresp(W, freqs, 'Hz'))), 1e-6))], 'w', ...
'DisplayName', args.DisplayName);
2020-10-08 10:52:20 +02:00
2020-10-26 17:52:18 +01:00
if args.color_i == 0
p.FaceColor = [0; 0; 0];
else
p.FaceColor = colors(args.color_i, :);
end
p.EdgeColor = 'none';
p.FaceAlpha = args.opacity;
end
#+end_src
** =plotPhaseUncertainty=
<<sec:plotPhaseUncertainty >>
This Matlab function is accessible [[file:src/plotPhaseUncertainty.m ][here ]].
#+begin_src matlab :tangle src/plotPhaseUncertainty.m
function [p] = plotPhaseUncertainty(W, freqs, args)
% plotPhaseUncertainty -
%
% Syntax: [p] = plotPhaseUncertainty(W, freqs, args)
%
% Inputs:
% - W - Multiplicative Uncertainty Weight
% - freqs - Frequency Vector [Hz]
% - args - Optional Arguments:
% - G
% - color_i
% - opacity
%
% Outputs:
% - p - Plot Handle
arguments
W
freqs double {mustBeNumeric, mustBeNonnegative}
args.G = tf(1)
args.color_i (1,1) double {mustBeInteger, mustBeNonnegative} = 0
args.opacity (1,1) double {mustBeNumeric, mustBePositive} = 0.3
args.DisplayName char = ''
2020-10-08 10:52:20 +02:00
end
2020-10-26 17:52:18 +01:00
% Get defaults colors
colors = get(groot, 'defaultAxesColorOrder');
2020-10-08 10:52:20 +02:00
2020-10-26 17:52:18 +01:00
% Compute Phase Uncertainty
Dphi = 180/pi*asin(abs(squeeze(freqresp(W, freqs, 'Hz'))));
Dphi(abs(squeeze(freqresp(W, freqs, 'Hz'))) > 1) = 360;
2020-10-08 10:52:20 +02:00
2020-10-26 17:52:18 +01:00
% Compute Plant Phase
G_ang = 180/pi*angle(squeeze(freqresp(args.G, freqs, 'Hz')));
2020-10-08 10:52:20 +02:00
2020-10-26 17:52:18 +01:00
p = patch([freqs flip(freqs)], [G_ang+Dphi; flip(G_ang-Dphi)], 'w', ...
'DisplayName', args.DisplayName);
2020-10-08 10:52:20 +02:00
2020-10-26 17:52:18 +01:00
if args.color_i == 0
p.FaceColor = [0; 0; 0];
else
p.FaceColor = colors(args.color_i, :);
2020-10-08 10:52:20 +02:00
end
2020-10-26 17:52:18 +01:00
p.EdgeColor = 'none';
p.FaceAlpha = args.opacity;
2020-10-08 10:52:20 +02:00
2020-10-26 17:52:18 +01:00
end
2020-10-08 10:52:20 +02:00
#+end_src