phd-test-bench-id31/test-bench-id31.org

14747 lines
493 KiB
Org Mode
Raw Permalink Normal View History

2024-03-19 15:30:11 +01:00
#+TITLE: Nano-Hexapod on the micro-station
:DRAWER:
#+LANGUAGE: en
#+EMAIL: dehaeze.thomas@gmail.com
#+AUTHOR: Dehaeze Thomas
#+HTML_LINK_HOME: ../index.html
#+HTML_LINK_UP: ../index.html
#+HTML_HEAD: <link rel="stylesheet" type="text/css" href="https://research.tdehaeze.xyz/css/style.css"/>
#+HTML_HEAD: <script type="text/javascript" src="https://research.tdehaeze.xyz/js/script.js"></script>
#+BIND: org-latex-image-default-option "scale=1"
#+BIND: org-latex-image-default-width ""
#+LaTeX_CLASS: scrreprt
#+LaTeX_CLASS_OPTIONS: [a4paper, 10pt, DIV=12, parskip=full, bibliography=totoc]
#+LaTeX_HEADER_EXTRA: \input{preamble.tex}
#+LATEX_HEADER_EXTRA: \bibliography{test-bench-id31.bib}
#+BIND: org-latex-bib-compiler "biber"
#+PROPERTY: header-args:matlab :session *MATLAB*
#+PROPERTY: header-args:matlab+ :comments org
#+PROPERTY: header-args:matlab+ :exports none
#+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
#+PROPERTY: header-args:matlab+ :tangle no
#+PROPERTY: header-args:latex :headers '("\\usepackage{tikz}" "\\usepackage{import}" "\\import{$HOME/Cloud/tikz/org/}{config.tex}")
#+PROPERTY: header-args:latex+ :imagemagick t :fit yes
#+PROPERTY: header-args:latex+ :iminoptions -scale 100% -density 150
#+PROPERTY: header-args:latex+ :imoutoptions -quality 100
#+PROPERTY: header-args:latex+ :results file raw replace
#+PROPERTY: header-args:latex+ :buffer no
#+PROPERTY: header-args:latex+ :tangle no
#+PROPERTY: header-args:latex+ :eval no-export
#+PROPERTY: header-args:latex+ :exports results
#+PROPERTY: header-args:latex+ :mkdirp yes
#+PROPERTY: header-args:latex+ :output-dir figs
#+PROPERTY: header-args:latex+ :post pdf2svg(file=*this*, ext="png")
:END:
#+begin_export html
<hr>
<p>This report is also available as a <a href="./test-bench-id31.pdf">pdf</a>.</p>
<hr>
#+end_export
#+latex: \clearpage
* Build :noexport:
#+NAME: startblock
#+BEGIN_SRC emacs-lisp :results none :tangle no
(add-to-list 'org-latex-classes
'("scrreprt"
"\\documentclass{scrreprt}"
("\\chapter{%s}" . "\\chapter*{%s}")
("\\section{%s}" . "\\section*{%s}")
("\\subsection{%s}" . "\\subsection*{%s}")
("\\paragraph{%s}" . "\\paragraph*{%s}")
))
;; Remove automatic org heading labels
(defun my-latex-filter-removeOrgAutoLabels (text backend info)
"Org-mode automatically generates labels for headings despite explicit use of `#+LABEL`. This filter forcibly removes all automatically generated org-labels in headings."
(when (org-export-derived-backend-p backend 'latex)
(replace-regexp-in-string "\\\\label{sec:org[a-f0-9]+}\n" "" text)))
(add-to-list 'org-export-filter-headline-functions
'my-latex-filter-removeOrgAutoLabels)
;; Remove all org comments in the output LaTeX file
(defun delete-org-comments (backend)
(loop for comment in (reverse (org-element-map (org-element-parse-buffer)
'comment 'identity))
do
(setf (buffer-substring (org-element-property :begin comment)
(org-element-property :end comment))
"")))
(add-hook 'org-export-before-processing-hook 'delete-org-comments)
;; Use no package by default
(setq org-latex-packages-alist nil)
(setq org-latex-default-packages-alist nil)
;; Do not include the subtitle inside the title
(setq org-latex-subtitle-separate t)
(setq org-latex-subtitle-format "\\subtitle{%s}")
(setq org-export-before-parsing-hook '(org-ref-glossary-before-parsing
org-ref-acronyms-before-parsing))
#+END_SRC
* Notes :noexport:
* Introduction :ignore:
* Short Stroke Metrology System
<<sec:id31_short_stroke_metrology>>
** Introduction :ignore:
The control of the nano-hexapod requires an external metrology system measuring the relative position of the nano-hexapod top platform with respect to the granite.
As the long-stroke ($\approx 1\,cm^3$) metrology system is not developed yet, a stroke stroke ($> 100\,\mu m^3$) can be used instead to validate the nano-hexapod control.
This short stroke metrology system consists of 5 interferometers pointing at 2 spheres fixed on top of the nano-hexapod (Figure ref:fig:LION_picture_close).
#+name: fig:LION_picture_close
#+caption: Metrology system with LION spheres (1 inch diameter) and 5 interferometers fixed to their individual tip-tilts
#+attr_latex: :width 0.5\linewidth
[[file:figs/LION_picture_close.jpg]]
This short stroke metrology system is fixed to the main granite using a gantry made of granite blocs to have good vibration and thermal stability (see Figure ref:fig:short_stroke_metrology_overview).
#+name: fig:short_stroke_metrology_overview
#+caption: Granite gantry used to fix the short-stroke metrology system
#+attr_latex: :width \linewidth
[[file:figs/short_stroke_metrology_overview.jpg]]
As the metrology system as limited stroke (estimated to be in the order of hundreds of micro-meters in x-y-z), it has to be well aligned in the rest position.
The alignment procedure is as follows:
1. The granite is aligned to be perpendicular to gravity (using inclinometer and adjusting airlocks)
2. The height of micro-hexapod is tuned to be able to position the short stroke metrology without additional shim
3. It is verified that the spindle axis is well perpendicular to the granite using the laser tracker
4. The micro hexapod is then used to align the two spheres with the spindle axis.
** 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 :tangle no :noweb yes
<<m-init-path>>
#+end_src
#+begin_src matlab :eval no :noweb yes
<<m-init-path-tangle>>
#+end_src
#+begin_src matlab :noweb yes
<<m-init-other>>
#+end_src
** Kinematics
#+name: fig:LION_metrology_interferometers
#+caption: Schematic of the measurement system
#+attr_latex: :width 0.5\linewidth
[[file:figs/LION_metrology_interferometers.png]]
We have the following set of equations:
\begin{align}
d_1 &= +D_y - l_2 R_x \\
d_2 &= +D_y + l_1 R_x \\
d_3 &= -D_x - l_2 R_y \\
d_4 &= -D_x + l_1 R_y \\
d_5 &= -D_z
\end{align}
That can be written as a linear transformation:
\begin{equation}
\begin{bmatrix}
d_1 \\ d_2 \\ d_3 \\ d_4 \\ d_5
\end{bmatrix} = \begin{bmatrix}
0 & 1 & 0 & -l_2 & 0 \\
0 & 1 & 0 & l_1 & 0 \\
-1 & 0 & 0 & 0 & -l_2 \\
-1 & 0 & 0 & 0 & l_1 \\
0 & 0 & -1 & 0 & 0
\end{bmatrix} \cdot \begin{bmatrix}
D_x \\ D_y \\ D_z \\ R_x \\ R_y
\end{bmatrix}
\end{equation}
By inverting the matrix, we obtain the Jacobian relation:
\begin{equation}
\begin{bmatrix}
D_x \\ D_y \\ D_z \\ R_x \\ R_y
\end{bmatrix} = \begin{bmatrix}
0 & 1 & 0 & -l_2 & 0 \\
0 & 1 & 0 & l_1 & 0 \\
-1 & 0 & 0 & 0 & -l_2 \\
-1 & 0 & 0 & 0 & l_1 \\
0 & 0 & -1 & 0 & 0
\end{bmatrix}^{-1} \cdot \begin{bmatrix}
d_1 \\ d_2 \\ d_3 \\ d_4 \\ d_5
\end{bmatrix}
\end{equation}
#+begin_src matlab
%% Parameters
H = 150e-3;
l1 = (150-48-42)*1e-3;
l2 = (76.2+48+42-150)*1e-3;
#+end_src
#+begin_src matlab
%% Transformation matrix
Hm = [ 0 1 0 -l2 0;
0 1 0 l1 0;
-1 0 0 0 -l2;
-1 0 0 0 l1;
0 0 -1 0 0];
#+end_src
#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*)
data2orgtable(pinv(Hm), {'*Dx*', '*Dy*', '*Dz*', '*Rx*', '*Ry*'}, {'*d1*', '*d2*', '*d3*', '*d4*', '*d5*'}, ' %.2f ');
#+end_src
#+name: tab:jacobian_metrology
#+caption: Jacobian matrix for the metrology system
#+attr_latex: :environment tabularx :width \linewidth :align cXXXXX
#+attr_latex: :center t :booktabs t
#+RESULTS:
| | *d1* | *d2* | *d3* | *d4* | *d5* |
|------+--------+-------+--------+-------+------|
| *Dx* | 0.0 | 0.0 | -0.79 | -0.21 | 0.0 |
| *Dy* | 0.79 | 0.21 | 0.0 | 0.0 | 0.0 |
| *Dz* | 0.0 | 0.0 | 0.0 | 0.0 | -1.0 |
| *Rx* | -13.12 | 13.12 | -0.0 | 0.0 | 0.0 |
| *Ry* | -0.0 | -0.0 | -13.12 | 13.12 | 0.0 |
** Rough alignment of spheres using comparators
Bottom Sphere, then top sphere.
Alignment better than 10um.
But the coaxiality between the cylinder and the sphere might not be good.
#+name: fig:align_top_sphere_comparators
#+attr_latex: :width \linewidth
#+caption: Two mechanical comparators used to align the top sphere with the rotation axis of the spindle
[[file:figs/align_top_sphere_comparators.jpg]]
** Alignment of spheres using interferometers
*** Angular alignment
#+begin_src matlab
%% Load Data
data_it0 = h5scan(data_dir, 'alignment', 'h1rx_h1ry', 1);
data_it1 = h5scan(data_dir, 'alignment', 'h1rx_h1ry_0002', 3);
data_it2 = h5scan(data_dir, 'alignment', 'h1rx_h1ry_0002', 5);
#+end_src
#+begin_src matlab
%% Offset wrong points
i_it0 = find(abs(data_it0.Rx_int_filtered(2:end)-data_it0.Rx_int_filtered(1:end-1))>1e-5);
data_it0.Rx_int_filtered(i_it0+1:end) = data_it0.Rx_int_filtered(i_it0+1:end) + data_it0.Rx_int_filtered(i_it0) - data_it0.Rx_int_filtered(i_it0+1);
i_it1 = find(abs(data_it1.Rx_int_filtered(2:end)-data_it1.Rx_int_filtered(1:end-1))>1e-5);
data_it1.Rx_int_filtered(i_it1+1:end) = data_it1.Rx_int_filtered(i_it1+1:end) + data_it1.Rx_int_filtered(i_it1) - data_it1.Rx_int_filtered(i_it1+1);
i_it2 = find(abs(data_it2.Rx_int_filtered(2:end)-data_it2.Rx_int_filtered(1:end-1))>1e-5);
data_it2.Rx_int_filtered(i_it2+1:end) = data_it2.Rx_int_filtered(i_it2+1:end) + data_it2.Rx_int_filtered(i_it2) - data_it2.Rx_int_filtered(i_it2+1);
#+end_src
#+begin_src matlab
%% Compute circle fit and get radius
[~, ~, R_it0, ~] = circlefit(1e6*data_it0.Rx_int_filtered, 1e6*data_it0.Ry_int_filtered);
[~, ~, R_it1, ~] = circlefit(1e6*data_it1.Rx_int_filtered, 1e6*data_it1.Ry_int_filtered);
[~, ~, R_it2, ~] = circlefit(1e6*data_it2.Rx_int_filtered, 1e6*data_it2.Ry_int_filtered);
#+end_src
#+begin_src matlab :exports none :results none
%% Rx/Ry alignment of the spheres using the micro-station
figure;
hold on;
plot(1e6*data_it0.Rx_int_filtered, 1e6*data_it0.Ry_int_filtered, '-', ...
'DisplayName', sprintf('It. 0: $R = %.0f \\mu$rad', R_it0))
plot(1e6*data_it1.Rx_int_filtered, 1e6*data_it1.Ry_int_filtered, '-', ...
'DisplayName', sprintf('It. 1: $R = %.0f \\mu$rad', R_it1))
plot(1e6*data_it2.Rx_int_filtered, 1e6*data_it2.Ry_int_filtered, '-', ...
'DisplayName', sprintf('It. 2: $R = %.0f \\mu$rad', R_it2))
hold off;
xlabel('$R_x$ [$\mu$rad]'); ylabel('$R_y$ [$\mu$rad]');
axis equal
legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_metrology_align_rx_ry.pdf', 'width', 'wide', 'height', 'normal');
#+end_src
#+name: fig:id31_metrology_align_rx_ry
#+caption: Rx/Ry alignment of the spheres using the micro-station
#+RESULTS:
[[file:figs/id31_metrology_align_rx_ry.png]]
*** Eccentricity alignment
#+begin_src matlab
%% Load Data
data_it0 = h5scan(data_dir, 'alignment', 'h1rx_h1ry_0002', 5);
data_it1 = h5scan(data_dir, 'alignment', 'h1dx_h1dy', 1);
#+end_src
#+begin_src matlab
%% Offset wrong points
i_it0 = find(abs(data_it0.Dy_int_filtered(2:end)-data_it0.Dy_int_filtered(1:end-1))>1e-5);
data_it0.Dy_int_filtered(i_it0+1:end) = data_it0.Dy_int_filtered(i_it0+1:end) + data_it0.Dy_int_filtered(i_it0) - data_it0.Dy_int_filtered(i_it0+1);
#+end_src
#+begin_src matlab
%% Compute circle fit and get radius
[~, ~, R_it0, ~] = circlefit(1e6*data_it0.Dx_int_filtered, 1e6*data_it0.Dy_int_filtered);
[~, ~, R_it1, ~] = circlefit(1e6*data_it1.Dx_int_filtered, 1e6*data_it1.Dy_int_filtered);
#+end_src
#+begin_src matlab :exports none :results none
%% Dx/Dy alignment of the spheres using the micro-station
figure;
hold on;
plot(1e6*data_it0.Dx_int_filtered, 1e6*data_it0.Dy_int_filtered, '-', ...
'DisplayName', sprintf('It. 0: $R = %.0f \\mu$m', R_it0))
plot(1e6*data_it1.Dx_int_filtered, 1e6*data_it1.Dy_int_filtered, '-', ...
'DisplayName', sprintf('It. 1: $R = %.0f \\mu$m', R_it1))
hold off;
xlabel('$D_x$ [$\mu$m]'); ylabel('$D_y$ [$\mu$m]');
axis equal
legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_metrology_align_dx_dy.pdf', 'width', 'wide', 'height', 'normal');
#+end_src
#+name: fig:id31_metrology_align_dx_dy
#+caption: Dx/Dy alignment of the spheres using the micro-station
#+RESULTS:
[[file:figs/id31_metrology_align_dx_dy.png]]
** Residual error after alignment
#+begin_src matlab
%% Load Data
data = h5scan(data_dir, 'alignment', 'h1dx_h1dy', 1);
#+end_src
- Dx and Dy are less than 1um.
- Dz less than 0.1um.
- Rx and Ry less than 4urad.
#+begin_src matlab :exports none :results none
%% Remaining errors after aligning the metrology using the interferometers
figure;
hold on;
plot(data.nth, 1e6*data.Dx_int_filtered, 'DisplayName', '$D_x$')
plot(data.nth, 1e6*data.Dy_int_filtered, 'DisplayName', '$D_y$')
plot(data.nth, 1e6*data.Dz_int_filtered, 'DisplayName', '$D_z$')
plot(data.nth, 1e6*data.Rx_int_filtered, 'DisplayName', '$R_x$')
plot(data.nth, 1e6*data.Ry_int_filtered, 'DisplayName', '$R_y$')
hold off;
xlabel('$R_z$ [deg]'); ylabel('Remaining Errors [$\mu$m, $\mu$rad]')
xlim([0, 360]); xticks(0:90:360);
ylim([-5, 5]); yticks(-5:1:5);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_align_metorlogy_errors_after_align.pdf', 'width', 'wide', 'height', 'normal');
#+end_src
#+name: fig:id31_align_metorlogy_errors_after_align
#+caption: Remaining errors after aligning the metrology using the interferometers
#+RESULTS:
[[file:figs/id31_align_metorlogy_errors_after_align.png]]
** Metrology acceptance
Because the interferometers are pointing to spheres and not flat surfaces, the lateral acceptance is limited.
#+begin_src matlab
data = h5scan(data_dir, 'metrology_acceptance', 'after_int_align_meshXY', 1);
x = 1e3*detrend(data.h1tx, 0); % [um]
y = 1e3*detrend(data.h1ty, 0); % [um]
z = 1e6*data.Dz_int_filtered - max(data.Dz_int_filtered); % [um]
mdl = scatteredInterpolant(x, y, z);
[xg, yg] = meshgrid(unique(x), unique(y));
zg = mdl(xg, yg);
#+end_src
#+begin_src matlab :exports none :results none
%% XY mapping of the Z measurement by the interferometer
figure;
[~,c] = contour3(xg,yg,zg,30);
c.LineWidth = 3;
xlabel('$\mu$-hexapod $D_x$ [$\mu$m]');
ylabel('$\mu$-hexapod $D_y$ [$\mu$m]');
zlabel('Interf $D_z$ [$\mu$m]');
zlim([-1, 0]);
xticks(-100:50:100);
yticks(-100:50:100);
zticks(-1:0.2:0);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/metrology_alignment_xy_map.pdf', 'width', 'wide', 'height', 'tall');
#+end_src
#+name: fig:metrology_alignment_xy_map
#+caption: XY mapping of the Z measurement by the interferometer
#+RESULTS:
[[file:figs/metrology_alignment_xy_map.png]]
* Simscape Model
<<sec:id31_simscape_model>>
** Introduction :ignore:
** 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 :tangle no :noweb yes
<<m-init-path>>
#+end_src
#+begin_src matlab :eval no :noweb yes
<<m-init-path-tangle>>
#+end_src
#+begin_src matlab :noweb yes
<<m-init-other>>
#+end_src
** Init model
#+begin_src matlab
%% Add directories to path for Simscape Model
addpath('mat')
addpath('matlab/subsystems')
addpath('STEPS/nano_hexapod')
addpath('STEPS/metrology')
addpath('STEPS/png')
#+end_src
#+begin_src matlab
%% Options for Linearized
options = linearizeOptions;
options.SampleTime = 0;
%% Name of the Simulink File
mdl = 'nass_model_id31';
#+end_src
#+begin_src matlab
open(mdl)
#+end_src
#+begin_src matlab
load('mat/conf_simulink.mat');
%% Initialize each Simscape model elements
initializeGround();
initializeGranite();
initializeTy();
initializeRy();
initializeRz();
initializeMicroHexapod();
initializeSimscapeConfiguration();
initializeDisturbances('enable', false);
initializeLoggingConfiguration('log', 'none');
initializeController('type', 'open-loop');
initializeNanoHexapod('flex_bot_type', '2dof', ...
'flex_top_type', '3dof', ...
'motion_sensor_type', 'plates', ...
'actuator_type', '2dof');
initializeSample('type', '0');
initializePosError();
initializeReferences();
initializeRzEstimate('type', 'encoders');
initializeLion();
#+end_src
** Identify Transfer functions
#+begin_src matlab
%% Input/Output definition
clear io; io_i = 1;
io(io_i) = linio([mdl, '/Controller'], 1, 'openinput'); io_i = io_i + 1; % Actuator Inputs
io(io_i) = linio([mdl, '/Micro-Station'], 3, 'openoutput', [], 'Fnlm'); io_i = io_i + 1; % Force Sensors
io(io_i) = linio([mdl, '/Micro-Station'], 3, 'openoutput', [], 'Dnlm'); io_i = io_i + 1; % Encoders
io(io_i) = linio([mdl, '/Tracking Error'], 1, 'openoutput', [], 'EdL'); io_i = io_i + 1; % Position Errors
io(io_i) = linio([mdl, '/Micro-Station/metrology_5dof/Lion_Metrology'], 1, 'output'); io_i = io_i + 1; % Interferometers
#+end_src
#+begin_src matlab :exports none
%% Identify the dynamics for IFF plant
initializeSample('type', '0');
Gm_m0 = linearize(mdl, io);
Gm_m0.InputName = {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'};
Gm_m0.OutputName = {'Fn1', 'Fn2', 'Fn3', 'Fn4', 'Fn5', 'Fn6', ...
'dL1', 'dL2', 'dL3', 'dL4', 'dL5', 'dL6', ...
'eL1', 'eL2', 'eL3', 'eL4', 'eL5', 'eL6', ...
'd1', 'd2', 'd3', 'd4', 'd5'};
initializeSample('type', '1');
Gm_m1 = linearize(mdl, io);
Gm_m1.InputName = {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'};
Gm_m1.OutputName = {'Fn1', 'Fn2', 'Fn3', 'Fn4', 'Fn5', 'Fn6', ...
'dL1', 'dL2', 'dL3', 'dL4', 'dL5', 'dL6', ...
'eL1', 'eL2', 'eL3', 'eL4', 'eL5', 'eL6', ...
'd1', 'd2', 'd3', 'd4', 'd5'};
initializeSample('type', '2');
Gm_m2 = linearize(mdl, io);
Gm_m2.InputName = {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'};
Gm_m2.OutputName = {'Fn1', 'Fn2', 'Fn3', 'Fn4', 'Fn5', 'Fn6', ...
'dL1', 'dL2', 'dL3', 'dL4', 'dL5', 'dL6', ...
'eL1', 'eL2', 'eL3', 'eL4', 'eL5', 'eL6', ...
'd1', 'd2', 'd3', 'd4', 'd5'};
initializeSample('type', '3');
Gm_m3 = linearize(mdl, io);
Gm_m3.InputName = {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'};
Gm_m3.OutputName = {'Fn1', 'Fn2', 'Fn3', 'Fn4', 'Fn5', 'Fn6', ...
'dL1', 'dL2', 'dL3', 'dL4', 'dL5', 'dL6', ...
'eL1', 'eL2', 'eL3', 'eL4', 'eL5', 'eL6', ...
'd1', 'd2', 'd3', 'd4', 'd5'};
#+end_src
#+begin_src matlab :exports none
%% Save Identified Plants
save('./matlab/mat/Gm.mat', 'Gm_m0', 'Gm_m1', 'Gm_m2', 'Gm_m3');
#+end_src
** IFF Plant
#+begin_src matlab :exports none :results none
%% IFF transfer function - Simscape model
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(freqs, abs(squeeze(freqresp(Gm_m0(sprintf('Fn%i', 1), sprintf('u%i', 1)), freqs, 'Hz'))), 'color', [colors(1,:), 0.5], ...
'DisplayName', '$\tau_{m,i}/u_i$ - $m_0$');
for i = 2:6
plot(freqs, abs(squeeze(freqresp(Gm_m0(sprintf('Fn%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(1,:), 0.5], ...
'HandleVisibility', 'off');
end
plot(freqs, abs(squeeze(freqresp(Gm_m1(sprintf('Fn%i', 1), sprintf('u%i', 1)), freqs, 'Hz'))), 'color', [colors(2,:), 0.5], ...
'DisplayName', '$\tau_{m,i}/u_i$ - $m_1$');
for i = 2:6
plot(freqs, abs(squeeze(freqresp(Gm_m1(sprintf('Fn%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(2,:), 0.5], ...
'HandleVisibility', 'off');
end
plot(freqs, abs(squeeze(freqresp(Gm_m2(sprintf('Fn%i', 1), sprintf('u%i', 1)), freqs, 'Hz'))), 'color', [colors(3,:), 0.5], ...
'DisplayName', '$\tau_{m,i}/u_i$ - $m_2$');
for i = 2:6
plot(freqs, abs(squeeze(freqresp(Gm_m2(sprintf('Fn%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(3,:), 0.5], ...
'HandleVisibility', 'off');
end
plot(freqs, abs(squeeze(freqresp(Gm_m3(sprintf('Fn%i', 1), sprintf('u%i', 1)), freqs, 'Hz'))), 'color', [colors(4,:), 0.5], ...
'DisplayName', '$\tau_{m,i}/u_i$ - $m_3$');
for i = 2:6
plot(freqs, abs(squeeze(freqresp(Gm_m3(sprintf('Fn%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(4,:), 0.5], ...
'HandleVisibility', 'off');
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Amplitude [V/V]'); set(gca, 'XTickLabel',[]);
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 3);
ax2 = nexttile;
hold on;
for i =1:6
plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_m0(sprintf('Fn%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(1,:), 0.5]);
end
for i =1:6
plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_m1(sprintf('Fn%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(2,:), 0.5]);
end
for i =1:6
plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_m2(sprintf('Fn%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(3,:), 0.5]);
end
for i =1:6
plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_m3(sprintf('Fn%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(4,:), 0.5]);
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-90, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_simscape_iff_ol_plant.pdf', 'width', 'wide', 'height', 'tall');
#+end_src
#+name: fig:id31_simscape_iff_ol_plant
#+caption: IFF transfer function - Simscape model
#+RESULTS:
[[file:figs/id31_simscape_iff_ol_plant.png]]
** Encoder plant
#+begin_src matlab :exports none :results none
%% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(freqs, abs(squeeze(freqresp(Gm_m0(sprintf('dL%i', 1), sprintf('u%i', 1)), freqs, 'Hz'))), 'color', [colors(1,:), 0.5], ...
'DisplayName', '$\tau_{m,i}/u_i$ - $m_0$');
for i = 2:6
plot(freqs, abs(squeeze(freqresp(Gm_m0(sprintf('dL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(1,:), 0.5], ...
'HandleVisibility', 'off');
end
plot(freqs, abs(squeeze(freqresp(Gm_m1(sprintf('dL%i', 1), sprintf('u%i', 1)), freqs, 'Hz'))), 'color', [colors(2,:), 0.5], ...
'DisplayName', '$\tau_{m,i}/u_i$ - $m_1$');
for i = 2:6
plot(freqs, abs(squeeze(freqresp(Gm_m1(sprintf('dL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(2,:), 0.5], ...
'HandleVisibility', 'off');
end
plot(freqs, abs(squeeze(freqresp(Gm_m2(sprintf('dL%i', 1), sprintf('u%i', 1)), freqs, 'Hz'))), 'color', [colors(3,:), 0.5], ...
'DisplayName', '$\tau_{m,i}/u_i$ - $m_2$');
for i = 2:6
plot(freqs, abs(squeeze(freqresp(Gm_m2(sprintf('dL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(3,:), 0.5], ...
'HandleVisibility', 'off');
end
plot(freqs, abs(squeeze(freqresp(Gm_m3(sprintf('dL%i', 1), sprintf('u%i', 1)), freqs, 'Hz'))), 'color', [colors(4,:), 0.5], ...
'DisplayName', '$\tau_{m,i}/u_i$ - $m_3$');
for i = 2:6
plot(freqs, abs(squeeze(freqresp(Gm_m3(sprintf('dL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(4,:), 0.5], ...
'HandleVisibility', 'off');
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Amplitude [m/V]'); set(gca, 'XTickLabel',[]);
ylim([1e-8, 1e-3]);
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 3);
ax2 = nexttile;
hold on;
for i =1:6
plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_m0(sprintf('dL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(1,:), 0.5]);
end
for i =1:6
plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_m1(sprintf('dL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(2,:), 0.5]);
end
for i =1:6
plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_m2(sprintf('dL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(3,:), 0.5]);
end
for i =1:6
plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_m3(sprintf('dL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(4,:), 0.5]);
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-90, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_simscape_enc_ol_plant.pdf', 'width', 'wide', 'height', 'tall');
#+end_src
#+name: fig:id31_simscape_enc_ol_plant
#+caption: ENC transfer function - Simscape model
#+RESULTS:
[[file:figs/id31_simscape_enc_ol_plant.png]]
** HAC Undamped plant
#+begin_src matlab :exports none :results none
%% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(freqs, abs(squeeze(freqresp(Gm_m0(sprintf('eL%i', 1), sprintf('u%i', 1)), freqs, 'Hz'))), 'color', [colors(1,:), 0.5], ...
'DisplayName', '$\tau_{m,i}/u_i$ - $m_0$');
for i = 2:6
plot(freqs, abs(squeeze(freqresp(Gm_m0(sprintf('eL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(1,:), 0.5], ...
'HandleVisibility', 'off');
end
plot(freqs, abs(squeeze(freqresp(Gm_m1(sprintf('eL%i', 1), sprintf('u%i', 1)), freqs, 'Hz'))), 'color', [colors(2,:), 0.5], ...
'DisplayName', '$\tau_{m,i}/u_i$ - $m_1$');
for i = 2:6
plot(freqs, abs(squeeze(freqresp(Gm_m1(sprintf('eL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(2,:), 0.5], ...
'HandleVisibility', 'off');
end
plot(freqs, abs(squeeze(freqresp(Gm_m2(sprintf('eL%i', 1), sprintf('u%i', 1)), freqs, 'Hz'))), 'color', [colors(3,:), 0.5], ...
'DisplayName', '$\tau_{m,i}/u_i$ - $m_2$');
for i = 2:6
plot(freqs, abs(squeeze(freqresp(Gm_m2(sprintf('eL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(3,:), 0.5], ...
'HandleVisibility', 'off');
end
plot(freqs, abs(squeeze(freqresp(Gm_m3(sprintf('eL%i', 1), sprintf('u%i', 1)), freqs, 'Hz'))), 'color', [colors(4,:), 0.5], ...
'DisplayName', '$\tau_{m,i}/u_i$ - $m_3$');
for i = 2:6
plot(freqs, abs(squeeze(freqresp(Gm_m3(sprintf('eL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(4,:), 0.5], ...
'HandleVisibility', 'off');
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Amplitude [m/V]'); set(gca, 'XTickLabel',[]);
ylim([1e-8, 1e-3]);
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 3);
ax2 = nexttile;
hold on;
for i =1:6
plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_m0(sprintf('eL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(1,:), 0.5]);
end
for i =1:6
plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_m1(sprintf('eL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(2,:), 0.5]);
end
for i =1:6
plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_m2(sprintf('eL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(3,:), 0.5]);
end
for i =1:6
plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_m3(sprintf('eL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(4,:), 0.5]);
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-90, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_simscape_int_ol_plant.pdf', 'width', 'wide', 'height', 'tall');
#+end_src
#+name: fig:id31_simscape_int_ol_plant
#+caption: INT transfer function - Simscape model
#+RESULTS:
[[file:figs/id31_simscape_int_ol_plant.png]]
* Identified Open Loop Plant
<<sec:id31_open_loop_plant>>
** Introduction :ignore:
** 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 :tangle no :noweb yes
<<m-init-path>>
#+end_src
#+begin_src matlab :eval no :noweb yes
<<m-init-path-tangle>>
#+end_src
#+begin_src matlab :noweb yes
<<m-init-other>>
#+end_src
#+begin_src matlab
%% Load Simscape Model
load('Gm.mat', 'Gm_m0', 'Gm_m1', 'Gm_m2', 'Gm_m3');
#+end_src
** Load Data :noexport:
#+begin_src matlab
%% Load identification data
data_m0 = load(sprintf('%s/dynamics/2023-08-08_16-17_ol_plant_m0_Wz0.mat', mat_dir));
data_m1 = load(sprintf('%s/dynamics/2023-08-08_18-57_ol_plant_m1_Wz0.mat', mat_dir));
data_m2 = load(sprintf('%s/dynamics/2023-08-08_17-12_ol_plant_m2_Wz0.mat', mat_dir));
data_m3 = load(sprintf('%s/dynamics/2023-08-08_18-20_ol_plant_m3_Wz0.mat', mat_dir));
#+end_src
#+begin_src matlab :exports none
% Sampling Time [s]
Ts = 1e-4;
% Hannning Windows
Nfft = floor(2.0/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
#+end_src
#+begin_src matlab :exports none
% And we get the frequency vector
[~, f] = tfestimate(data_m0.uL1.id_plant, data_m0.uL1.e_L1, win, Noverlap, Nfft, 1/Ts);
#+end_src
** IFF Plant
#+begin_src matlab :exports none
%% IFF Plant (transfer function from u to taum)
G_iff_m0 = zeros(length(f), 6, 6);
for i_strut = 1:6
eL = [data_m0.(sprintf("uL%i", i_strut)).Vs1 ; data_m0.(sprintf("uL%i", i_strut)).Vs2 ; data_m0.(sprintf("uL%i", i_strut)).Vs3 ; data_m0.(sprintf("uL%i", i_strut)).Vs4 ; data_m0.(sprintf("uL%i", i_strut)).Vs5 ; data_m0.(sprintf("uL%i", i_strut)).Vs6]';
G_iff_m0(:,:,i_strut) = tfestimate(data_m0.(sprintf("uL%i", i_strut)).id_plant, eL, win, Noverlap, Nfft, 1/Ts);
end
#+end_src
#+begin_src matlab :exports none
%% IFF Plant (transfer function from u to taum)
G_iff_m1 = zeros(length(f), 6, 6);
for i_strut = 1:6
eL = [data_m1.(sprintf("uL%i", i_strut)).Vs1 ; data_m1.(sprintf("uL%i", i_strut)).Vs2 ; data_m1.(sprintf("uL%i", i_strut)).Vs3 ; data_m1.(sprintf("uL%i", i_strut)).Vs4 ; data_m1.(sprintf("uL%i", i_strut)).Vs5 ; data_m1.(sprintf("uL%i", i_strut)).Vs6]';
G_iff_m1(:,:,i_strut) = tfestimate(data_m1.(sprintf("uL%i", i_strut)).id_plant, eL, win, Noverlap, Nfft, 1/Ts);
end
#+end_src
#+begin_src matlab :exports none
%% IFF Plant (transfer function from u to taum)
G_iff_m2 = zeros(length(f), 6, 6);
for i_strut = 1:6
eL = [data_m2.(sprintf("uL%i", i_strut)).Vs1 ; data_m2.(sprintf("uL%i", i_strut)).Vs2 ; data_m2.(sprintf("uL%i", i_strut)).Vs3 ; data_m2.(sprintf("uL%i", i_strut)).Vs4 ; data_m2.(sprintf("uL%i", i_strut)).Vs5 ; data_m2.(sprintf("uL%i", i_strut)).Vs6]';
G_iff_m2(:,:,i_strut) = tfestimate(data_m2.(sprintf("uL%i", i_strut)).id_plant, eL, win, Noverlap, Nfft, 1/Ts);
end
#+end_src
#+begin_src matlab :exports none
%% IFF Plant (transfer function from u to taum)
G_iff_m3 = zeros(length(f), 6, 6);
for i_strut = 1:6
eL = [data_m3.(sprintf("uL%i", i_strut)).Vs1 ; data_m3.(sprintf("uL%i", i_strut)).Vs2 ; data_m3.(sprintf("uL%i", i_strut)).Vs3 ; data_m3.(sprintf("uL%i", i_strut)).Vs4 ; data_m3.(sprintf("uL%i", i_strut)).Vs5 ; data_m3.(sprintf("uL%i", i_strut)).Vs6]';
G_iff_m3(:,:,i_strut) = tfestimate(data_m3.(sprintf("uL%i", i_strut)).id_plant, eL, win, Noverlap, Nfft, 1/Ts);
end
#+end_src
#+begin_src matlab :exports none
%% Save Identified Plants
save('./matlab/mat/G_ol.mat', 'G_iff_m0', 'G_iff_m1', 'G_iff_m2', 'G_iff_m3', 'f');
#+end_src
#+begin_src matlab :exports none :results none
%% Measured transfer function from generated voltages to measured voltage on the force sensors
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
for i = 1:5
for j = i+1:6
plot(f, abs(G_iff_m0(:, i, j)), 'color', [0, 0, 0, 0.2], ...
'HandleVisibility', 'off');
end
end
for i = 1:6
plot(f, abs(G_iff_m0(:,i, i)), 'color', colors(i,:), ...
'DisplayName', sprintf('$\\tau_{m,%i}/u_%i$', i, i));
end
plot(f, abs(G_iff_m0(:, 1, 2)), 'color', [0, 0, 0, 0.2], ...
'DisplayName', '$\tau_{m,i}/u_j$');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Amplitude [V/V]'); set(gca, 'XTickLabel',[]);
ylim([1e-4, 1e2]);
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 3);
ax2 = nexttile;
hold on;
for i =1:6
plot(f, 180/pi*angle(G_iff_m0(:,i, i)));
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-90, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_iff_ol_plant_m0.pdf', 'width', 'full', 'height', 'tall');
#+end_src
#+name: fig:id31_iff_ol_plant_m0
#+caption: Measured transfer function from generated voltages to measured voltage on the force sensors
#+RESULTS:
[[file:figs/id31_iff_ol_plant_m0.png]]
The measured frequency response functions from DAC voltages $u_i$ to measured voltages on the force sensors $\tau_{m,i}$ are compared with the simscape model in Figure ref:fig:id31_comp_simscape_frf_ol_iff.
#+begin_src matlab :exports none :results none
%% Comparison of the Simscape model and identified IFF plant
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
for i = 1:5
for j = i+1:6
plot(f, abs(G_iff_m0(:, i, j)), 'color', [colors(1,:), 0.2], ...
'HandleVisibility', 'off');
end
end
for i = 1:5
for j = i+1:6
plot(freqs, abs(squeeze(freqresp(Gm_m0(sprintf('Fn%i', i), sprintf('u%i', j)), freqs, 'Hz'))), 'color', [colors(2,:), 0.2], ...
'HandleVisibility', 'off');
end
end
plot(f, abs(G_iff_m0(:,1, 1)), 'color', [colors(1,:)], ...
'DisplayName', '$\tau_{m,i}/u_i$ - Identified');
for i = 2:6
plot(f, abs(G_iff_m0(:,i, i)), 'color', [colors(1,:)], ...
'HandleVisibility', 'off');
end
plot(freqs, abs(squeeze(freqresp(Gm_m0(sprintf('Fn%i', 1), sprintf('u%i', 1)), freqs, 'Hz'))), 'color', [colors(2,:)], ...
'DisplayName', '$\tau_{m,i}/u_i$ - Simscape');
for i = 2:6
plot(freqs, abs(squeeze(freqresp(Gm_m0(sprintf('Fn%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(2,:)], ...
'HandleVisibility', 'off');
end
plot(f, abs(G_iff_m0(:, 1, 2)), 'color', [colors(1,:), 0.2], ...
'DisplayName', '$\tau_{m,i}/u_j$ - Identified');
plot(freqs, abs(squeeze(freqresp(Gm_m0(sprintf('Fn%i', 1), sprintf('u%i', 2)), freqs, 'Hz'))), 'color', [colors(2,:), 0.2], ...
'DisplayName', '$\tau_{m,i}/u_j$ - Simscape');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Amplitude [V/V]'); set(gca, 'XTickLabel',[]);
ylim([1e-4, 1e2]);
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 2);
ax2 = nexttile;
hold on;
for i = 1:6
plot(f, 180/pi*angle(G_iff_m0(:,i, i)), 'color', [colors(1,:)]);
end
for i = 1:6
plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_m0(sprintf('Fn%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(2,:)]);
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-90, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_comp_simscape_frf_ol_iff.pdf', 'width', 'full', 'height', 'tall');
#+end_src
#+name: fig:id31_comp_simscape_frf_ol_iff
#+caption: Comparison of the Simscape model and identified IFF plant
#+RESULTS:
[[file:figs/id31_comp_simscape_frf_ol_iff.png]]
The effect of the payload mass on the diagonal elements are shown in Figure ref:fig:id31_effect_mass_frf_ol_iff.
#+begin_src matlab :exports none :results none
%% Effect of the payload mass on the IFF plant
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_iff_m0(:,1, 1)), 'color', [colors(1,:), 0.5], ...
'DisplayName', '$\tau_{m,i}/u_i$ - m0');
for i = 2:6
plot(f, abs(G_iff_m0(:,i, i)), 'color', [colors(1,:), 0.5], ...
'HandleVisibility', 'off');
end
plot(f, abs(G_iff_m1(:,1, 1)), 'color', [colors(2,:), 0.5], ...
'DisplayName', '$\tau_{m,i}/u_i$ - m1');
for i = 2:6
plot(f, abs(G_iff_m1(:,i, i)), 'color', [colors(2,:), 0.5], ...
'HandleVisibility', 'off');
end
plot(f, abs(G_iff_m2(:,1, 1)), 'color', [colors(3,:), 0.5], ...
'DisplayName', '$\tau_{m,i}/u_i$ - m2');
for i = 2:6
plot(f, abs(G_iff_m2(:,i, i)), 'color', [colors(3,:), 0.5], ...
'HandleVisibility', 'off');
end
plot(f, abs(G_iff_m3(:,1, 1)), 'color', [colors(4,:), 0.5], ...
'DisplayName', '$\tau_{m,i}/u_i$ - m3');
for i = 2:6
plot(f, abs(G_iff_m3(:,i, i)), 'color', [colors(4,:), 0.5], ...
'HandleVisibility', 'off');
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Amplitude [V/V]'); set(gca, 'XTickLabel',[]);
ylim([1e-2, 1e2]);
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 2);
ax2 = nexttile;
hold on;
for i = 1:6
plot(f, 180/pi*angle(G_iff_m0(:,i, i)), 'color', [colors(1,:), 0.5]);
end
for i = 1:6
plot(f, 180/pi*angle(G_iff_m1(:,i, i)), 'color', [colors(2,:), 0.5]);
end
for i = 1:6
plot(f, 180/pi*angle(G_iff_m2(:,i, i)), 'color', [colors(3,:), 0.5]);
end
for i = 1:6
plot(f, 180/pi*angle(G_iff_m3(:,i, i)), 'color', [colors(4,:), 0.5]);
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-90, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_effect_mass_frf_ol_iff.pdf', 'width', 'full', 'height', 'tall');
#+end_src
#+name: fig:id31_effect_mass_frf_ol_iff
#+caption: Effect of the payload mass on the IFF plant
#+RESULTS:
[[file:figs/id31_effect_mass_frf_ol_iff.png]]
** Encoder plant
#+begin_src matlab :exports none
%% ENC Plant (transfer function from u to taum)
G_enc_m0 = zeros(length(f), 6, 6);
for i_strut = 1:6
eL = [data_m0.(sprintf("uL%i", i_strut)).dL1 ; data_m0.(sprintf("uL%i", i_strut)).dL2 ; data_m0.(sprintf("uL%i", i_strut)).dL3 ; data_m0.(sprintf("uL%i", i_strut)).dL4 ; data_m0.(sprintf("uL%i", i_strut)).dL5 ; data_m0.(sprintf("uL%i", i_strut)).dL6]';
G_enc_m0(:,:,i_strut) = tfestimate(data_m0.(sprintf("uL%i", i_strut)).id_plant, eL, win, Noverlap, Nfft, 1/Ts);
end
#+end_src
#+begin_src matlab :exports none
%% ENC Plant (transfer function from u to taum)
G_enc_m1 = zeros(length(f), 6, 6);
for i_strut = 1:6
eL = [data_m1.(sprintf("uL%i", i_strut)).dL1 ; data_m1.(sprintf("uL%i", i_strut)).dL2 ; data_m1.(sprintf("uL%i", i_strut)).dL3 ; data_m1.(sprintf("uL%i", i_strut)).dL4 ; data_m1.(sprintf("uL%i", i_strut)).dL5 ; data_m1.(sprintf("uL%i", i_strut)).dL6]';
G_enc_m1(:,:,i_strut) = tfestimate(data_m1.(sprintf("uL%i", i_strut)).id_plant, eL, win, Noverlap, Nfft, 1/Ts);
end
#+end_src
#+begin_src matlab :exports none
%% ENC Plant (transfer function from u to taum)
G_enc_m2 = zeros(length(f), 6, 6);
for i_strut = 1:6
eL = [data_m2.(sprintf("uL%i", i_strut)).dL1 ; data_m2.(sprintf("uL%i", i_strut)).dL2 ; data_m2.(sprintf("uL%i", i_strut)).dL3 ; data_m2.(sprintf("uL%i", i_strut)).dL4 ; data_m2.(sprintf("uL%i", i_strut)).dL5 ; data_m2.(sprintf("uL%i", i_strut)).dL6]';
G_enc_m2(:,:,i_strut) = tfestimate(data_m2.(sprintf("uL%i", i_strut)).id_plant, eL, win, Noverlap, Nfft, 1/Ts);
end
#+end_src
#+begin_src matlab :exports none
%% ENC Plant (transfer function from u to taum)
G_enc_m3 = zeros(length(f), 6, 6);
for i_strut = 1:6
eL = [data_m3.(sprintf("uL%i", i_strut)).dL1 ; data_m3.(sprintf("uL%i", i_strut)).dL2 ; data_m3.(sprintf("uL%i", i_strut)).dL3 ; data_m3.(sprintf("uL%i", i_strut)).dL4 ; data_m3.(sprintf("uL%i", i_strut)).dL5 ; data_m3.(sprintf("uL%i", i_strut)).dL6]';
G_enc_m3(:,:,i_strut) = tfestimate(data_m3.(sprintf("uL%i", i_strut)).id_plant, eL, win, Noverlap, Nfft, 1/Ts);
end
#+end_src
#+begin_src matlab :exports none
%% Save Identified Plants
save('./matlab/mat/G_ol.mat', 'G_enc_m0', 'G_enc_m1', 'G_enc_m2', 'G_enc_m3', '-append');
#+end_src
The identified frequency response functions from general voltages $u_i$ to measured displacement of the struts by the encoders $d\mathcal{L}_i$ are compared with the simscape model in Figure ref:fig:id31_comp_simscape_frf_ol_enc.
#+begin_src matlab :exports none :results none
%% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_enc_m0(:, 1, 2)), 'color', [colors(1,:), 0.2], ...
'DisplayName', '$d\mathcal{L}_i/u_j$ - Identified');
for i = 1:5
for j = i+1:6
plot(f, abs(G_enc_m0(:, i, j)), 'color', [colors(1,:), 0.2], ...
'HandleVisibility', 'off');
end
end
plot(freqs, abs(squeeze(freqresp(Gm_m0(sprintf('dL%i', 1), sprintf('u%i', 2)), freqs, 'Hz'))), 'color', [colors(2,:), 0.2], ...
'DisplayName', '$d\mathcal{L}_i/u_j$ - Simscape');
for i = 1:5
for j = i+1:6
plot(freqs, abs(squeeze(freqresp(Gm_m0(sprintf('dL%i', i), sprintf('u%i', j)), freqs, 'Hz'))), 'color', [colors(2,:), 0.2], ...
'HandleVisibility', 'off');
end
end
plot(f, abs(G_enc_m0(:, 1, 1)), 'color', [colors(1,:)], ...
'DisplayName', '$d\mathcal{L}_i/u_i$ - Identified');
for i = 2:6
plot(f, abs(G_enc_m0(:,i, i)), 'color', [colors(1,:)], ...
'HandleVisibility', 'off');
end
plot(freqs, abs(squeeze(freqresp(Gm_m0(sprintf('dL%i', 1), sprintf('u%i', 1)), freqs, 'Hz'))), 'color', [colors(2,:)], ...
'DisplayName', '$d\mathcal{L}_i/u_i$ - Simscape');
for i = 2:6
plot(freqs, abs(squeeze(freqresp(Gm_m0(sprintf('dL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(2,:)], ...
'HandleVisibility', 'off');
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Amplitude [m/V]'); set(gca, 'XTickLabel',[]);
ylim([1e-8, 2e-4]);
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 2);
ax2 = nexttile;
hold on;
for i = 1:6
plot(f, 180/pi*angle(G_enc_m0(:,i, i)), 'color', [colors(1,:)]);
end
for i = 1:6
plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_m0(sprintf('dL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(2,:)]);
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-90, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_comp_simscape_frf_ol_enc.pdf', 'width', 'wide', 'height', 'tall');
#+end_src
#+name: fig:id31_comp_simscape_frf_ol_enc
#+caption: Comparison of the Simscape model and identified plant - Transfer functions from voltages to encoders
#+RESULTS:
[[file:figs/id31_comp_simscape_frf_ol_enc.png]]
#+begin_src matlab :exports none :results none
%% Effect of the payload mass on the transfer function from actuator voltage to encoder displacement
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_enc_m0(:, 1, 1)), 'color', [colors(1,:), 0.5], ...
'DisplayName', '$d\mathcal{L}_i/u_i$ - m0');
for i = 2:6
plot(f, abs(G_enc_m0(:,i, i)), 'color', [colors(1,:), 0.5], ...
'HandleVisibility', 'off');
end
plot(f, abs(G_enc_m1(:, 1, 1)), 'color', [colors(2,:), 0.5], ...
'DisplayName', '$d\mathcal{L}_i/u_i$ - m1');
for i = 2:6
plot(f, abs(G_enc_m1(:,i, i)), 'color', [colors(2,:), 0.5], ...
'HandleVisibility', 'off');
end
plot(f, abs(G_enc_m2(:, 1, 1)), 'color', [colors(3,:), 0.5], ...
'DisplayName', '$d\mathcal{L}_i/u_i$ - m2');
for i = 2:6
plot(f, abs(G_enc_m2(:,i, i)), 'color', [colors(3,:), 0.5], ...
'HandleVisibility', 'off');
end
plot(f, abs(G_enc_m3(:, 1, 1)), 'color', [colors(4,:), 0.5], ...
'DisplayName', '$d\mathcal{L}_i/u_i$ - m3');
for i = 2:6
plot(f, abs(G_enc_m3(:,i, i)), 'color', [colors(4,:), 0.5], ...
'HandleVisibility', 'off');
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Amplitude [m/V]'); set(gca, 'XTickLabel',[]);
ylim([1e-8, 2e-4]);
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 2);
ax2 = nexttile;
hold on;
for i = 1:6
plot(f, 180/pi*angle(G_enc_m0(:,i, i)), 'color', [colors(1,:), 0.5]);
end
for i = 1:6
plot(f, 180/pi*angle(G_enc_m1(:,i, i)), 'color', [colors(2,:), 0.5]);
end
for i = 1:6
plot(f, 180/pi*angle(G_enc_m2(:,i, i)), 'color', [colors(3,:), 0.5]);
end
for i = 1:6
plot(f, 180/pi*angle(G_enc_m3(:,i, i)), 'color', [colors(4,:), 0.5]);
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-90, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_effect_mass_frf_ol_enc.pdf', 'width', 'wide', 'height', 'tall');
#+end_src
#+name: fig:id31_effect_mass_frf_ol_enc
#+caption: Effect of the payload mass on the transfer function from actuator voltage to encoder displacement
#+RESULTS:
[[file:figs/id31_effect_mass_frf_ol_enc.png]]
** HAC Undamped plant
#+begin_src matlab :exports none
%% INT Plant (transfer function from u to taum)
G_int_m0 = zeros(length(f), 6, 6);
for i_strut = 1:6
eL = [data_m0.(sprintf("uL%i", i_strut)).e_L1 ; data_m0.(sprintf("uL%i", i_strut)).e_L2 ; data_m0.(sprintf("uL%i", i_strut)).e_L3 ; data_m0.(sprintf("uL%i", i_strut)).e_L4 ; data_m0.(sprintf("uL%i", i_strut)).e_L5 ; data_m0.(sprintf("uL%i", i_strut)).e_L6]';
G_int_m0(:,:,i_strut) = tfestimate(data_m0.(sprintf("uL%i", i_strut)).id_plant, eL, win, Noverlap, Nfft, 1/Ts);
end
#+end_src
#+begin_src matlab :exports none
%% INT Plant (transfer function from u to taum)
G_int_m1 = zeros(length(f), 6, 6);
for i_strut = 1:6
eL = [data_m1.(sprintf("uL%i", i_strut)).e_L1 ; data_m1.(sprintf("uL%i", i_strut)).e_L2 ; data_m1.(sprintf("uL%i", i_strut)).e_L3 ; data_m1.(sprintf("uL%i", i_strut)).e_L4 ; data_m1.(sprintf("uL%i", i_strut)).e_L5 ; data_m1.(sprintf("uL%i", i_strut)).e_L6]';
G_int_m1(:,:,i_strut) = tfestimate(data_m1.(sprintf("uL%i", i_strut)).id_plant, eL, win, Noverlap, Nfft, 1/Ts);
end
#+end_src
#+begin_src matlab :exports none
%% INT Plant (transfer function from u to taum)
G_int_m2 = zeros(length(f), 6, 6);
for i_strut = 1:6
eL = [data_m2.(sprintf("uL%i", i_strut)).e_L1 ; data_m2.(sprintf("uL%i", i_strut)).e_L2 ; data_m2.(sprintf("uL%i", i_strut)).e_L3 ; data_m2.(sprintf("uL%i", i_strut)).e_L4 ; data_m2.(sprintf("uL%i", i_strut)).e_L5 ; data_m2.(sprintf("uL%i", i_strut)).e_L6]';
G_int_m2(:,:,i_strut) = tfestimate(data_m2.(sprintf("uL%i", i_strut)).id_plant, eL, win, Noverlap, Nfft, 1/Ts);
end
#+end_src
#+begin_src matlab :exports none
%% INT Plant (transfer function from u to taum)
G_int_m3 = zeros(length(f), 6, 6);
for i_strut = 1:6
eL = [data_m3.(sprintf("uL%i", i_strut)).e_L1 ; data_m3.(sprintf("uL%i", i_strut)).e_L2 ; data_m3.(sprintf("uL%i", i_strut)).e_L3 ; data_m3.(sprintf("uL%i", i_strut)).e_L4 ; data_m3.(sprintf("uL%i", i_strut)).e_L5 ; data_m3.(sprintf("uL%i", i_strut)).e_L6]';
G_int_m3(:,:,i_strut) = tfestimate(data_m3.(sprintf("uL%i", i_strut)).id_plant, eL, win, Noverlap, Nfft, 1/Ts);
end
#+end_src
#+begin_src matlab :exports none
%% Save Identified Plants
save('./matlab/mat/G_ol.mat', 'G_int_m0', 'G_int_m1', 'G_int_m2', 'G_int_m3', '-append');
#+end_src
The identified frequency response functions from actuator voltages $u_i$ to measured strut motion from the external metrology (i.e. the interferometers) are compare with the simscape model in Figure ref:fig:id31_comp_simscape_frf_ol_int.
#+begin_src matlab :exports none :results none
%% Measured transfer function from generated voltages to measured voltage on the force sensors
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
for i = 1:5
for j = i+1:6
plot(f, abs(G_int_m0(:, i, j)), 'color', [0, 0, 0, 0.2], ...
'HandleVisibility', 'off');
end
end
for i = 1:6
plot(f, abs(G_int_m0(:,i, i)), 'color', colors(i,:), ...
'DisplayName', sprintf('$e\\mathcal{L}_%i/u_%i$', i, i));
end
plot(f, abs(G_int_m0(:, 1, 2)), 'color', [0, 0, 0, 0.2], ...
'DisplayName', '$-e\mathcal{L}_i/u_j$');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Amplitude [m/V]'); set(gca, 'XTickLabel',[]);
ylim([1e-8, 2e-4]);
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 3);
ax2 = nexttile;
hold on;
for i =1:6
plot(f, 180/pi*angle(G_int_m0(:,i, i)));
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-90, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_int_ol_plant_m0.pdf', 'width', 'full', 'height', 'tall');
#+end_src
#+name: fig:id31_int_ol_plant_m0
#+caption: Measured transfer function from generated voltages to measured voltage on the force sensors
#+RESULTS:
[[file:figs/id31_int_ol_plant_m0.png]]
#+begin_src matlab :exports none :results none
%% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_int_m0(:, 1, 2)), 'color', [colors(1,:), 0.2], ...
'DisplayName', '$-e\mathcal{L}_i/u_j$ - Identified');
for i = 1:5
for j = i+1:6
plot(f, abs(G_int_m0(:, i, j)), 'color', [colors(1,:), 0.2], ...
'HandleVisibility', 'off');
end
end
plot(freqs, abs(squeeze(freqresp(Gm_m0(sprintf('eL%i', 1), sprintf('u%i', 2)), freqs, 'Hz'))), 'color', [colors(2,:), 0.2], ...
'DisplayName', '$-e\mathcal{L}_i/u_j$ - Simscape');
for i = 1:5
for j = i+1:6
plot(freqs, abs(squeeze(freqresp(Gm_m0(sprintf('eL%i', i), sprintf('u%i', j)), freqs, 'Hz'))), 'color', [colors(2,:), 0.2], ...
'HandleVisibility', 'off');
end
end
plot(f, abs(G_int_m0(:, 1, 1)), 'color', [colors(1,:)], ...
'DisplayName', '$-e\mathcal{L}_i/u_i$ - Identified');
for i = 2:6
plot(f, abs(G_int_m0(:,i, i)), 'color', [colors(1,:)], ...
'HandleVisibility', 'off');
end
plot(freqs, abs(squeeze(freqresp(Gm_m0(sprintf('eL%i', 1), sprintf('u%i', 1)), freqs, 'Hz'))), 'color', [colors(2,:)], ...
'DisplayName', '$-e\mathcal{L}_i/u_i$ - Simscape');
for i = 2:6
plot(freqs, abs(squeeze(freqresp(Gm_m0(sprintf('eL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(2,:)], ...
'HandleVisibility', 'off');
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Amplitude [m/V]'); set(gca, 'XTickLabel',[]);
ylim([1e-8, 2e-4]);
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 2);
ax2 = nexttile;
hold on;
for i = 1:6
plot(f, 180/pi*angle(G_int_m0(:,i, i)), 'color', [colors(1,:)]);
end
for i = 1:6
plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_m0(sprintf('eL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(2,:)]);
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-90, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_comp_simscape_frf_ol_int.pdf', 'width', 'full', 'height', 'tall');
#+end_src
#+name: fig:id31_comp_simscape_frf_ol_int
#+caption: Comparison of the Simscape model and identified plant - Transfer functions from voltages to estimated strut motion from interferometers
#+RESULTS:
[[file:figs/id31_comp_simscape_frf_ol_int.png]]
#+begin_src matlab :exports none :results none
%% Effect of the payload mass on the transfer functions from actuator voltage to measured strut motion by the external metrology
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_int_m0(:, 1, 1)), 'color', [colors(1,:), 0.5], ...
'DisplayName', '$-e\mathcal{L}_i/u_i$ - m0');
for i = 2:6
plot(f, abs(G_int_m0(:,i, i)), 'color', [colors(1,:), 0.5], ...
'HandleVisibility', 'off');
end
plot(f, abs(G_int_m1(:, 1, 1)), 'color', [colors(2,:), 0.5], ...
'DisplayName', '$-e\mathcal{L}_i/u_i$ - m1');
for i = 2:6
plot(f, abs(G_int_m1(:,i, i)), 'color', [colors(2,:), 0.5], ...
'HandleVisibility', 'off');
end
plot(f, abs(G_int_m2(:, 1, 1)), 'color', [colors(3,:), 0.5], ...
'DisplayName', '$-e\mathcal{L}_i/u_i$ - m2');
for i = 2:6
plot(f, abs(G_int_m2(:,i, i)), 'color', [colors(3,:), 0.5], ...
'HandleVisibility', 'off');
end
plot(f, abs(G_int_m3(:, 1, 1)), 'color', [colors(4,:), 0.5], ...
'DisplayName', '$-e\mathcal{L}_i/u_i$ - m3');
for i = 2:6
plot(f, abs(G_int_m3(:,i, i)), 'color', [colors(4,:), 0.5], ...
'HandleVisibility', 'off');
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Amplitude [m/V]'); set(gca, 'XTickLabel',[]);
ylim([1e-8, 2e-4]);
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 2);
ax2 = nexttile;
hold on;
for i = 1:6
plot(f, 180/pi*angle(G_int_m0(:,i, i)), 'color', [colors(1,:), 0.5]);
end
for i = 1:6
plot(f, 180/pi*angle(G_int_m1(:,i, i)), 'color', [colors(2,:), 0.5]);
end
for i = 1:6
plot(f, 180/pi*angle(G_int_m2(:,i, i)), 'color', [colors(3,:), 0.5]);
end
for i = 1:6
plot(f, 180/pi*angle(G_int_m3(:,i, i)), 'color', [colors(4,:), 0.5]);
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-90, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_effect_mass_frf_ol_int.pdf', 'width', 'wide', 'height', 'tall');
#+end_src
#+name: fig:id31_effect_mass_frf_ol_int
#+caption: Effect of the payload mass on the transfer functions from actuator voltage to measured strut motion by the external metrology
#+RESULTS:
[[file:figs/id31_effect_mass_frf_ol_int.png]]
** Decoupling improvement thanks to better Rz alignment
*** Alignment procedure
- Control based on encoders
- Slow moving in X and Y
- Compare with X and Y from interf
#+begin_src matlab
%% Load Data
data_1_dx = h5scan(data_dir, 'align_int_enc_Rz', 'tx_first_scan', 2);
data_1_dy = h5scan(data_dir, 'align_int_enc_Rz', 'tx_first_scan', 3);
data_2_dx = h5scan(data_dir, 'align_int_enc_Rz', 'verif-after-correct-offset', 1);
data_2_dy = h5scan(data_dir, 'align_int_enc_Rz', 'verif-after-correct-offset', 2);
#+end_src
#+begin_src matlab
figure;
hold on;
plot(1e6*data_1_dx.Dx_int_filtered, 1e6*data_1_dx.Dy_int_filtered, 'color', colors(1,:), 'DisplayName', 'Measurement')
plot(1e6*data_1_dy.Dx_int_filtered, 1e6*data_1_dy.Dy_int_filtered, 'color', colors(1,:), 'HandleVisibility', 'off')
plot(1e6*[-10:10]*cos(2.7*pi/180), -1e6*[-10:10]*sin(2.7*pi/180), '--', 'color', colors(2,:), 'DisplayName', '$R_z = 2.7$ deg')
plot(1e6*[-10:10]*sin(2.7*pi/180), 1e6*[-10:10]*cos(2.7*pi/180), '--', 'color', colors(2,:), 'HandleVisibility', 'off')
hold off;
xlabel('Interf $D_x$ [$\mu$m]');
ylabel('Interf $D_y$ [$\mu$m]');
legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1);
axis equal
xlim([-10, 10]); ylim([-10, 10]);
xticks([-10:5:10]); yticks([-10:5:10]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_Rz_align_dx_dy_scans_before_calib.pdf', 'width', 'wide', 'height', 'tall');
#+end_src
#+name: fig:id31_Rz_align_dx_dy_scans_before_calib
#+caption: description
#+RESULTS:
[[file:figs/id31_Rz_align_dx_dy_scans_before_calib.png]]
#+begin_src matlab
figure;
hold on;
plot(1e6*data_1_dx.Dx_int_filtered, 1e6*data_1_dx.Dy_int_filtered, 'color', colors(1,:), 'DisplayName', 'Initial')
plot(1e6*data_1_dy.Dx_int_filtered, 1e6*data_1_dy.Dy_int_filtered, 'color', colors(1,:), 'HandleVisibility', 'off')
plot(1e6*data_2_dx.Dx_int_filtered, 1e6*data_2_dx.Dy_int_filtered, 'color', colors(2,:), 'DisplayName', 'After $R_z$ calib')
plot(1e6*data_2_dy.Dx_int_filtered, 1e6*data_2_dy.Dy_int_filtered, 'color', colors(2,:), 'HandleVisibility', 'off')
hold off;
xlabel('Interf $D_x$ [$\mu$m]');
ylabel('Interf $D_y$ [$\mu$m]');
legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1);
axis equal
xlim([-10, 10]); ylim([-10, 10]);
xticks([-10:5:10]); yticks([-10:5:10]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_Rz_align_dx_dy_scans.pdf', 'width', 'wide', 'height', 'tall');
#+end_src
#+name: fig:id31_Rz_align_dx_dy_scans
#+caption: description
#+RESULTS:
[[file:figs/id31_Rz_align_dx_dy_scans.png]]
*** m0
#+begin_src matlab
data = load(sprintf('%s/dynamics/2023-08-08_16-17_ol_plant_m0_Wz0.mat', mat_dir));
data_align = load(sprintf('%s/dynamics/2023-08-17_17-37_ol_plant_m0_Wz0_new_Rz_align.mat', mat_dir));
#+end_src
#+begin_src matlab :exports none
%% INT Plant (transfer function from u to taum)
G_int = zeros(length(f), 6, 6);
for i_strut = 1:6
eL = [data.(sprintf("uL%i", i_strut)).e_L1 ; data.(sprintf("uL%i", i_strut)).e_L2 ; data.(sprintf("uL%i", i_strut)).e_L3 ; data.(sprintf("uL%i", i_strut)).e_L4 ; data.(sprintf("uL%i", i_strut)).e_L5 ; data.(sprintf("uL%i", i_strut)).e_L6]';
G_int(:,:,i_strut) = tfestimate(data.(sprintf("uL%i", i_strut)).id_plant, eL, win, Noverlap, Nfft, 1/Ts);
end
#+end_src
#+begin_src matlab :exports none
%% INT Plant (transfer function from u to taum)
G_int_align = zeros(length(f), 6, 6);
for i_strut = 1:6
eL = [data_align.(sprintf("uL%i", i_strut)).e_L1 ; data_align.(sprintf("uL%i", i_strut)).e_L2 ; data_align.(sprintf("uL%i", i_strut)).e_L3 ; data_align.(sprintf("uL%i", i_strut)).e_L4 ; data_align.(sprintf("uL%i", i_strut)).e_L5 ; data_align.(sprintf("uL%i", i_strut)).e_L6]';
G_int_align(:,:,i_strut) = tfestimate(data_align.(sprintf("uL%i", i_strut)).id_plant, eL, win, Noverlap, Nfft, 1/Ts);
end
#+end_src
#+begin_src matlab :exports none :results none
%% Decrease of the coupling with better Rz alignment
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_int(:, 1, 2)), 'color', [colors(1,:), 0.2], ...
'DisplayName', '$-e\mathcal{L}_i/u_j$');
for i = 1:5
for j = i+1:6
plot(f, abs(G_int(:, i, j)), 'color', [colors(1,:), 0.2], ...
'HandleVisibility', 'off');
end
end
plot(f, abs(G_int_align(:, 1, 2)), 'color', [colors(2,:), 0.2], ...
'DisplayName', '$-e\mathcal{L}_i/u_j$ - $R_z$ align');
for i = 1:5
for j = i+1:6
plot(f, abs(G_int_align(:, i, j)), 'color', [colors(2,:), 0.2], ...
'HandleVisibility', 'off');
end
end
plot(f, abs(G_int(:, 1, 1)), 'color', [colors(1,:)], ...
'DisplayName', '$-e\mathcal{L}_i/u_i$');
for i = 2:6
plot(f, abs(G_int(:,i, i)), 'color', [colors(1,:)], ...
'HandleVisibility', 'off');
end
plot(f, abs(G_int_align(:, 1, 1)), 'color', [colors(2,:)], ...
'DisplayName', '$-e\mathcal{L}_i/u_i$ - $R_z$ align');
for i = 2:6
plot(f, abs(G_int_align(:,i, i)), 'color', [colors(2,:)], ...
'HandleVisibility', 'off');
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Amplitude [m/V]'); set(gca, 'XTickLabel',[]);
ylim([1e-8, 2e-4]);
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 2);
ax2 = nexttile;
hold on;
for i = 1:6
plot(f, 180/pi*angle(G_int(:,i, i)), 'color', [colors(1,:)]);
end
for i = 1:6
plot(f, 180/pi*angle(G_int_align(:,i, i)), 'color', [colors(2,:)]);
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-90, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_coupling_decrease_rz_align.pdf', 'width', 'full', 'height', 'tall');
#+end_src
#+name: fig:id31_coupling_decrease_rz_align
#+caption: Decrease of the coupling with better Rz alignment
#+RESULTS:
[[file:figs/id31_coupling_decrease_rz_align.png]]
*** m3
#+begin_src matlab
data = load(sprintf('%s/dynamics/2023-08-10_18-16_damp_plant_m3_Wz0.mat', mat_dir));
data_align = load(sprintf('%s/dynamics/2023-08-21_15-52_damp_plant_m3_new_Rz.mat', mat_dir));
#+end_src
#+begin_src matlab :exports none
%% INT Plant (transfer function from u to taum)
G_int = zeros(length(f), 6, 6);
for i_strut = 1:6
eL = [data.(sprintf("uL%i", i_strut)).e_L1 ; data.(sprintf("uL%i", i_strut)).e_L2 ; data.(sprintf("uL%i", i_strut)).e_L3 ; data.(sprintf("uL%i", i_strut)).e_L4 ; data.(sprintf("uL%i", i_strut)).e_L5 ; data.(sprintf("uL%i", i_strut)).e_L6]';
G_int(:,:,i_strut) = tfestimate(data.(sprintf("uL%i", i_strut)).id_plant, eL, win, Noverlap, Nfft, 1/Ts);
end
#+end_src
#+begin_src matlab :exports none
%% INT Plant (transfer function from u to taum)
G_int_align = zeros(length(f), 6, 6);
for i_strut = 1:6
eL = [data_align.(sprintf("uL%i", i_strut)).e_L1 ; data_align.(sprintf("uL%i", i_strut)).e_L2 ; data_align.(sprintf("uL%i", i_strut)).e_L3 ; data_align.(sprintf("uL%i", i_strut)).e_L4 ; data_align.(sprintf("uL%i", i_strut)).e_L5 ; data_align.(sprintf("uL%i", i_strut)).e_L6]';
G_int_align(:,:,i_strut) = tfestimate(data_align.(sprintf("uL%i", i_strut)).id_plant, eL, win, Noverlap, Nfft, 1/Ts);
end
#+end_src
#+begin_src matlab :exports none :results none
%% Decrease of the coupling with better Rz alignment
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_int(:, 1, 2)), 'color', [colors(1,:), 0.2], ...
'DisplayName', '$-e\mathcal{L}_i/u_j$');
for i = 1:5
for j = i+1:6
plot(f, abs(G_int(:, i, j)), 'color', [colors(1,:), 0.2], ...
'HandleVisibility', 'off');
end
end
plot(f, abs(G_int_align(:, 1, 2)), 'color', [colors(2,:), 0.2], ...
'DisplayName', '$-e\mathcal{L}_i/u_j$ - $R_z$ align');
for i = 1:5
for j = i+1:6
plot(f, abs(G_int_align(:, i, j)), 'color', [colors(2,:), 0.2], ...
'HandleVisibility', 'off');
end
end
plot(f, abs(G_int(:, 1, 1)), 'color', [colors(1,:)], ...
'DisplayName', '$-e\mathcal{L}_i/u_i$');
for i = 2:6
plot(f, abs(G_int(:,i, i)), 'color', [colors(1,:)], ...
'HandleVisibility', 'off');
end
plot(f, abs(G_int_align(:, 1, 1)), 'color', [colors(2,:)], ...
'DisplayName', '$-e\mathcal{L}_i/u_i$ - $R_z$ align');
for i = 2:6
plot(f, abs(G_int_align(:,i, i)), 'color', [colors(2,:)], ...
'HandleVisibility', 'off');
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Amplitude [m/V]'); set(gca, 'XTickLabel',[]);
ylim([1e-8, 2e-4]);
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 2);
ax2 = nexttile;
hold on;
for i = 1:6
plot(f, 180/pi*angle(G_int(:,i, i)), 'color', [colors(1,:)]);
end
for i = 1:6
plot(f, 180/pi*angle(G_int_align(:,i, i)), 'color', [colors(2,:)]);
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-90, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_coupling_decrease_rz_align_m3.pdf', 'width', 'full', 'height', 'tall');
#+end_src
#+name: fig:id31_coupling_decrease_rz_align_m3
#+caption: Decrease of the coupling with better Rz alignment
#+RESULTS:
[[file:figs/id31_coupling_decrease_rz_align_m3.png]]
** Conclusion
- Good match between the model and experiment
* TODO Noise Budget
<<sec:id31_noise_budget>>
** Introduction :ignore:
In this section, the noise budget is performed.
The vibrations of the sample is measured in different conditions using the external metrology.
** 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 :tangle no :noweb yes
<<m-init-path>>
#+end_src
#+begin_src matlab :eval no :noweb yes
<<m-init-path-tangle>>
#+end_src
#+begin_src matlab :noweb yes
<<m-init-other>>
#+end_src
** Open-Loop Noise Budget
First, the noise is measured while no motion is performed.
#+begin_src matlab
%% Load measured noise
data_m0 = load(sprintf('%s/freq_analysis/2023-08-11_16-51_m0_lac_off.mat', mat_dir));
#+end_src
Noise budget in the cartesian frame
#+begin_src matlab :exports none
%% Coordinate transform
J_int_to_X = [ 0 0 -0.787401574803149 -0.212598425196851 0;
0.78740157480315 0.21259842519685 0 0 0;
0 0 0 0 -1;
-13.1233595800525 13.1233595800525 0 0 0;
0 0 -13.1233595800525 13.1233595800525 0];
a = J_int_to_X*[data_m0.d1; data_m0.d2; data_m0.d3; data_m0.d4; data_m0.d5];
data_m0.t = Ts*[0:length(data_m0.d1)-1];
data_m0.Dx_int = a(1,:);
data_m0.Dy_int = a(2,:);
data_m0.Dz_int = a(3,:);
data_m0.Rx_int = a(4,:);
data_m0.Ry_int = a(5,:);
#+end_src
Data in the time domain
#+begin_src matlab :exports none :results none
%% Measured vibration with the interferometers
figure;
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile();
hold on;
% plot(data_m0.t, 1e9*data_m0.Dx_int, '-', 'DisplayName', '$D_x$');
plot(data_m0.t, 1e9*data_m0.Dy_int, '-', 'DisplayName', '$D_y$');
plot(data_m0.t, 1e9*data_m0.Dz_int, '-', 'DisplayName', '$D_z$');
% plot(data_m0.t, data_m0.Rx_int, 'DisplayName', '$R_x$');
plot(data_m0.t, 1e9*data_m0.Ry_int, '-', 'DisplayName', '$R_y$');
hold off;
set(gca, 'XScale', 'lin'); set(gca, 'YScale', 'lin');
xlabel('Time [s]'); ylabel('Amplitude [nm]');
legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1);
xlim([50, 54])
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_noise_budget_interf_time_domain_m0.pdf', 'width', 'full', 'height', 'normal');
#+end_src
#+name: fig:id31_noise_budget_interf_time_domain_m0
#+caption: Measured vibration with the interferometers
#+RESULTS:
[[file:figs/id31_noise_budget_interf_time_domain_m0.png]]
In the frequency domain
#+begin_src matlab :exports none
% Hannning Windows
Nfft = floor(2.0/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
#+end_src
#+begin_src matlab :exports none
% And we get the frequency vector
[pxx_Dx, f] = pwelch(detrend(data_m0.Dx_int, 0), win, Noverlap, Nfft, 1/Ts);
[pxx_Dy, ~] = pwelch(detrend(data_m0.Dy_int, 0), win, Noverlap, Nfft, 1/Ts);
[pxx_Dz, ~] = pwelch(detrend(data_m0.Dz_int, 0), win, Noverlap, Nfft, 1/Ts);
[pxx_Rx, ~] = pwelch(detrend(data_m0.Rx_int, 0), win, Noverlap, Nfft, 1/Ts);
[pxx_Ry, ~] = pwelch(detrend(data_m0.Ry_int, 0), win, Noverlap, Nfft, 1/Ts);
#+end_src
#+begin_src matlab :exports none :results none
%% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor
figure;
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile();
hold on;
% plot(f, sqrt(pxx_Dx), 'DisplayName', '$D_x$');
plot(f, sqrt(pxx_Dy), 'DisplayName', '$D_y$');
plot(f, sqrt(pxx_Dz), 'DisplayName', '$D_z$');
% plot(f, sqrt(pxx_Rx), 'DisplayName', '$R_x$');
plot(f, sqrt(pxx_Ry), 'DisplayName', '$R_y$');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('Amplitude [$V/\sqrt{Hz}$]');
legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1);
xlim([1, 5e3]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_noise_budget_interf_freq_domain_m0.pdf', 'width', 'wide', 'height', 'normal');
#+end_src
#+name: fig:id31_noise_budget_interf_freq_domain_m0
#+caption: Measured vibration with the interferometers
#+RESULTS:
[[file:figs/id31_noise_budget_interf_freq_domain_m0.png]]
#+begin_src matlab :exports none :results none
%% Cumulative Amplitude Spectrum of the measured Dx and Dy motion
figure;
hold on;
% plot(f, sqrt(flip(-cumtrapz(flip(f), flip(pxx_Dx)))), 'DisplayName', '$D_x$');
plot(f, sqrt(flip(-cumtrapz(flip(f), flip(pxx_Dy)))), 'DisplayName', sprintf('$D_y$ - %.0f nm', rms(1e9*detrend(data_m0.Dy_int, 0))));
plot(f, sqrt(flip(-cumtrapz(flip(f), flip(pxx_Dz)))), 'DisplayName', sprintf('$D_z$ - %.0f nm', rms(1e9*detrend(data_m0.Dz_int, 0))));
% plot(f, sqrt(flip(-cumtrapz(flip(f), flip(pxx_Rx)))), 'DisplayName', '$R_x$');
plot(f, sqrt(flip(-cumtrapz(flip(f), flip(pxx_Ry)))), 'DisplayName', sprintf('$R_y$ - %.0f nrad', rms(1e9*detrend(data_m0.Ry_int, 0))));
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('CAS [$m$]');
legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1);
xlim([1, 5e3]); ylim([1e-10, 1e-7]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_noise_budget_open_loop_cas_m0.pdf', 'width', 'wide', 'height', 'normal');
#+end_src
#+name: fig:id31_noise_budget_open_loop_cas_m0
#+caption: Measured vibration with the interferometers
#+RESULTS:
[[file:figs/id31_noise_budget_open_loop_cas_m0.png]]
** Effect of LAC
#+begin_src matlab
%% Load measured noise
data_ol = load(sprintf('%s/freq_analysis/2023-08-11_16-51_m0_lac_off.mat', mat_dir));
data_lac = load(sprintf('%s/freq_analysis/2023-08-11_16-46_m0_lac_on.mat', mat_dir));
#+end_src
#+begin_src matlab :exports none
%% Coordinate transform
J_int_to_X = [ 0 0 -0.787401574803149 -0.212598425196851 0;
0.78740157480315 0.21259842519685 0 0 0;
0 0 0 0 -1;
-13.1233595800525 13.1233595800525 0 0 0;
0 0 -13.1233595800525 13.1233595800525 0];
a = J_int_to_X*[data_ol.d1; data_ol.d2; data_ol.d3; data_ol.d4; data_ol.d5];
data_ol.Dx_int = a(1,:);
data_ol.Dy_int = a(2,:);
data_ol.Dz_int = a(3,:);
data_ol.Rx_int = a(4,:);
data_ol.Ry_int = a(5,:);
a = J_int_to_X*[data_lac.d1; data_lac.d2; data_lac.d3; data_lac.d4; data_lac.d5];
data_lac.Dx_int = a(1,:);
data_lac.Dy_int = a(2,:);
data_lac.Dz_int = a(3,:);
data_lac.Rx_int = a(4,:);
data_lac.Ry_int = a(5,:);
#+end_src
#+begin_src matlab :exports none
% Hannning Windows
Nfft = floor(2.0/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
[data_ol.pxx_Dx, data_ol.f] = pwelch(detrend(data_ol.Dx_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_ol.pxx_Dy, ~ ] = pwelch(detrend(data_ol.Dy_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_ol.pxx_Dz, ~ ] = pwelch(detrend(data_ol.Dz_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_ol.pxx_Rx, ~ ] = pwelch(detrend(data_ol.Rx_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_ol.pxx_Ry, ~ ] = pwelch(detrend(data_ol.Ry_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_lac.pxx_Dx, data_lac.f] = pwelch(detrend(data_lac.Dx_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_lac.pxx_Dy, ~ ] = pwelch(detrend(data_lac.Dy_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_lac.pxx_Dz, ~ ] = pwelch(detrend(data_lac.Dz_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_lac.pxx_Rx, ~ ] = pwelch(detrend(data_lac.Rx_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_lac.pxx_Ry, ~ ] = pwelch(detrend(data_lac.Ry_int, 0), win, Noverlap, Nfft, 1/Ts);
#+end_src
Effect of LAC (Figure ref:fig:id31_noise_budget_effect_lac_m0):
- reduce amplitude around 80Hz
- Inject some noise between 200 and 700Hz?
#+begin_src matlab :exports none :results none
%% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor
figure;
tiledlayout(1, 3, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile();
hold on;
plot(data_ol.f , sqrt(data_ol.pxx_Dy ), 'DisplayName', '$D_y$ - OL');
plot(data_lac.f, sqrt(data_lac.pxx_Dy), 'DisplayName', '$D_y$ - LAC');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('Amplitude [$m/\sqrt{Hz}$]');
legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1);
title('$D_y$')
ax2 = nexttile();
hold on;
plot(data_ol.f , sqrt(data_ol.pxx_Dz ), 'DisplayName', '$D_z$ - OL');
plot(data_lac.f, sqrt(data_lac.pxx_Dz), 'DisplayName', '$D_z$ - LAC');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1);
title('$D_z$')
ax3 = nexttile();
hold on;
plot(data_ol.f , sqrt(data_ol.pxx_Ry ), 'DisplayName', '$R_y$ - OL');
plot(data_lac.f, sqrt(data_lac.pxx_Ry), 'DisplayName', '$R_y$ - LAC');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1);
title('$R_y$')
linkaxes([ax1,ax2,ax3],'xy');
xlim([1, 5e2]); ylim([1e-11, 2e-8]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_noise_budget_effect_lac_m0.pdf', 'width', 'full', 'height', 'normal');
#+end_src
#+name: fig:id31_noise_budget_effect_lac_m0
#+caption: Measured vibration with the interferometers
#+RESULTS:
[[file:figs/id31_noise_budget_effect_lac_m0.png]]
#+begin_src matlab :exports none :results none
%% Cumulative Amplitude Spectrum of the measured Dx and Dy motion
figure;
tiledlayout(1, 3, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile();
hold on;
plot(data_ol.f, sqrt(flip(-cumtrapz(flip(data_ol.f), flip(data_ol.pxx_Dy)))), 'DisplayName', 'OL');
plot(data_lac.f, sqrt(flip(-cumtrapz(flip(data_lac.f), flip(data_lac.pxx_Dy)))), 'DisplayName', 'LAC');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('CAS [$m$]');
title('$D_y$');
legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1);
ax2 = nexttile();
hold on;
plot(data_ol.f, sqrt(flip(-cumtrapz(flip(data_ol.f), flip(data_ol.pxx_Dz)))), 'DisplayName', 'OL');
plot(data_lac.f, sqrt(flip(-cumtrapz(flip(data_lac.f), flip(data_lac.pxx_Dz)))), 'DisplayName', 'LAC');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
title('$D_z$');
legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1);
ax3 = nexttile();
hold on;
plot(data_ol.f, sqrt(flip(-cumtrapz(flip(data_ol.f), flip(data_ol.pxx_Ry)))), 'DisplayName', 'OL');
plot(data_lac.f, sqrt(flip(-cumtrapz(flip(data_lac.f), flip(data_lac.pxx_Ry)))), 'DisplayName', 'LAC');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
title('$R_y$');
legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1);
linkaxes([ax1,ax2,ax3],'xy');
xlim([1, 1e3]); ylim([1e-10, 1e-7]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_cas_effect_lac_m0.pdf', 'width', 'full', 'height', 'normal');
#+end_src
#+name: fig:id31_cas_effect_lac_m0
#+caption: Measured vibration with the interferometers
#+RESULTS:
[[file:figs/id31_cas_effect_lac_m0.png]]
** Effect of rotation
#+begin_src matlab
data_Wz0 = load(sprintf('%s/freq_analysis/2023-08-11_16-51_m0_lac_off.mat', mat_dir));
data_Wz1 = load(sprintf('%s/freq_analysis/2023-08-11_17-18_m0_lac_off_1rpm.mat', mat_dir));
data_Wz2 = load(sprintf('%s/freq_analysis/2023-08-11_17-39_m0_lac_off_30rpm.mat', mat_dir));
#+end_src
#+begin_src matlab :exports none
%% Coordinate transform
J_int_to_X = [ 0 0 -0.787401574803149 -0.212598425196851 0;
0.78740157480315 0.21259842519685 0 0 0;
0 0 0 0 -1;
-13.1233595800525 13.1233595800525 0 0 0;
0 0 -13.1233595800525 13.1233595800525 0];
a = J_int_to_X*[data_Wz0.d1; data_Wz0.d2; data_Wz0.d3; data_Wz0.d4; data_Wz0.d5];
data_Wz0.Dx_int = a(1,:);
data_Wz0.Dy_int = a(2,:);
data_Wz0.Dz_int = a(3,:);
data_Wz0.Rx_int = a(4,:);
data_Wz0.Ry_int = a(5,:);
a = J_int_to_X*[data_Wz1.d1; data_Wz1.d2; data_Wz1.d3; data_Wz1.d4; data_Wz1.d5];
data_Wz1.Dx_int = a(1,:);
data_Wz1.Dy_int = a(2,:);
data_Wz1.Dz_int = a(3,:);
data_Wz1.Rx_int = a(4,:);
data_Wz1.Ry_int = a(5,:);
a = J_int_to_X*[data_Wz2.d1; data_Wz2.d2; data_Wz2.d3; data_Wz2.d4; data_Wz2.d5];
data_Wz2.Dx_int = a(1,:);
data_Wz2.Dy_int = a(2,:);
data_Wz2.Dz_int = a(3,:);
data_Wz2.Rx_int = a(4,:);
data_Wz2.Ry_int = a(5,:);
#+end_src
#+begin_src matlab :exports none
% Hannning Windows
Nfft = floor(20.0/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
[data_Wz0.pxx_Dx, data_Wz0.f] = pwelch(detrend(data_Wz0.Dx_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_Wz0.pxx_Dy, ~ ] = pwelch(detrend(data_Wz0.Dy_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_Wz0.pxx_Dz, ~ ] = pwelch(detrend(data_Wz0.Dz_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_Wz0.pxx_Rx, ~ ] = pwelch(detrend(data_Wz0.Rx_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_Wz0.pxx_Ry, ~ ] = pwelch(detrend(data_Wz0.Ry_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_Wz1.pxx_Dx, data_Wz1.f] = pwelch(detrend(data_Wz1.Dx_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_Wz1.pxx_Dy, ~ ] = pwelch(detrend(data_Wz1.Dy_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_Wz1.pxx_Dz, ~ ] = pwelch(detrend(data_Wz1.Dz_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_Wz1.pxx_Rx, ~ ] = pwelch(detrend(data_Wz1.Rx_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_Wz1.pxx_Ry, ~ ] = pwelch(detrend(data_Wz1.Ry_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_Wz2.pxx_Dx, data_Wz2.f] = pwelch(detrend(data_Wz2.Dx_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_Wz2.pxx_Dy, ~ ] = pwelch(detrend(data_Wz2.Dy_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_Wz2.pxx_Dz, ~ ] = pwelch(detrend(data_Wz2.Dz_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_Wz2.pxx_Rx, ~ ] = pwelch(detrend(data_Wz2.Rx_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_Wz2.pxx_Ry, ~ ] = pwelch(detrend(data_Wz2.Ry_int, 0), win, Noverlap, Nfft, 1/Ts);
#+end_src
#+begin_src matlab :exports none :results none
%% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor
figure;
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile();
hold on;
plot(data_Wz0.f, sqrt(data_Wz0.pxx_Dy), 'DisplayName', '$D_y$ - 0rpm');
plot(data_Wz1.f, sqrt(data_Wz1.pxx_Dy), 'DisplayName', '$D_y$ - 1rpm');
plot(data_Wz2.f, sqrt(data_Wz2.pxx_Dy), 'DisplayName', '$D_y$ - 30rpm');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('Amplitude [$V/\sqrt{Hz}$]');
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1);
xlim([0.1, 5e3]);
#+end_src
Rotation induces lots of vibrations, especially at high velocity.
#+begin_src matlab :exports none :results none
%% description
figure;
tiledlayout(1, 3, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile();
hold on;
plot(data_Wz0.f, sqrt(flip(-cumtrapz(flip(data_Wz0.f), flip(data_Wz0.pxx_Dy)))), ...
'DisplayName', sprintf('0rpm: $%.0f nm$', 1e9*rms(detrend(data_Wz0.Dy_int, 0))));
plot(data_Wz1.f, sqrt(flip(-cumtrapz(flip(data_Wz1.f), flip(data_Wz1.pxx_Dy)))), ...
'DisplayName', sprintf('6rpm: $%.0f nm$', 1e9*rms(detrend(data_Wz1.Dy_int, 0))));
plot(data_Wz2.f, sqrt(flip(-cumtrapz(flip(data_Wz2.f), flip(data_Wz2.pxx_Dy)))), ...
'DisplayName', sprintf('30rpm: $%.1f \\mu m$', 1e6*rms(detrend(data_Wz2.Dy_int, 0))));
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('CAS [m rms, rad RMS]');
legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1);
xticks([1e0, 1e1, 1e2]);
title('$D_y$')
ax2 = nexttile();
hold on;
plot(data_Wz0.f, sqrt(flip(-cumtrapz(flip(data_Wz0.f), flip(data_Wz0.pxx_Dz)))), ...
'DisplayName', sprintf('0rpm: $%.0f nm$', 1e9*rms(detrend(data_Wz0.Dz_int, 0))));
plot(data_Wz1.f, sqrt(flip(-cumtrapz(flip(data_Wz1.f), flip(data_Wz1.pxx_Dz)))), ...
'DisplayName', sprintf('6rpm: $%.0f nm$', 1e9*rms(detrend(data_Wz1.Dz_int, 0))));
plot(data_Wz2.f, sqrt(flip(-cumtrapz(flip(data_Wz2.f), flip(data_Wz2.pxx_Dz)))), ...
'DisplayName', sprintf('30rpm: $%.0f nm$', 1e9*rms(detrend(data_Wz2.Dz_int, 0))));
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1);
xticks([1e0, 1e1, 1e2]);
title('$D_z$')
ax3 = nexttile();
hold on;
plot(data_Wz0.f, sqrt(flip(-cumtrapz(flip(data_Wz0.f), flip(data_Wz0.pxx_Ry)))), ...
'DisplayName', sprintf('0rpm: $%.1f \\mu$rad', 1e6*rms(detrend(data_Wz0.Ry_int, 0))));
plot(data_Wz1.f, sqrt(flip(-cumtrapz(flip(data_Wz1.f), flip(data_Wz1.pxx_Ry)))), ...
'DisplayName', sprintf('6rpm: $%.1f \\mu$rad', 1e6*rms(detrend(data_Wz1.Ry_int, 0))));
plot(data_Wz2.f, sqrt(flip(-cumtrapz(flip(data_Wz2.f), flip(data_Wz2.pxx_Ry)))), ...
'DisplayName', sprintf('30rpm: $%.0f \\mu$rad', 1e6*rms(detrend(data_Wz2.Ry_int, 0))));
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1);
xticks([1e0, 1e1, 1e2]);
title('$R_y$')
linkaxes([ax1,ax2,ax3],'xy');
xlim([0.1, 5e2]); ylim([1e-10, 3e-5]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_noise_budget_effect_rotation.pdf', 'width', 'full', 'height', 'normal');
#+end_src
#+name: fig:id31_noise_budget_effect_rotation
#+caption: Cumulative Amplitude Spectrum for the three important directions ($D_y$, $D_z$ and $R_y$). Three rotating velocities are shown. Integrated RMS values are shown in the legend.
#+RESULTS:
[[file:figs/id31_noise_budget_effect_rotation.png]]
** Effect of HAC
#+begin_src matlab
%% Load measured noise
data_ol = load(sprintf('%s/freq_analysis/2023-08-11_16-51_m0_lac_off.mat', mat_dir));
data_lac = load(sprintf('%s/freq_analysis/2023-08-11_16-46_m0_lac_on.mat' , mat_dir));
data_hac = load(sprintf('%s/freq_analysis/2023-08-11_16-49_m0_hac_on.mat' , mat_dir));
#+end_src
#+begin_src matlab :exports none
%% Coordinate transform
J_int_to_X = [ 0 0 -0.787401574803149 -0.212598425196851 0;
0.78740157480315 0.21259842519685 0 0 0;
0 0 0 0 -1;
-13.1233595800525 13.1233595800525 0 0 0;
0 0 -13.1233595800525 13.1233595800525 0];
a = J_int_to_X*[data_ol.d1; data_ol.d2; data_ol.d3; data_ol.d4; data_ol.d5];
data_ol.Dx_int = a(1,:);
data_ol.Dy_int = a(2,:);
data_ol.Dz_int = a(3,:);
data_ol.Rx_int = a(4,:);
data_ol.Ry_int = a(5,:);
a = J_int_to_X*[data_lac.d1; data_lac.d2; data_lac.d3; data_lac.d4; data_lac.d5];
data_lac.Dx_int = a(1,:);
data_lac.Dy_int = a(2,:);
data_lac.Dz_int = a(3,:);
data_lac.Rx_int = a(4,:);
data_lac.Ry_int = a(5,:);
a = J_int_to_X*[data_hac.d1; data_hac.d2; data_hac.d3; data_hac.d4; data_hac.d5];
data_hac.Dx_int = a(1,:);
data_hac.Dy_int = a(2,:);
data_hac.Dz_int = a(3,:);
data_hac.Rx_int = a(4,:);
data_hac.Ry_int = a(5,:);
#+end_src
#+begin_src matlab :exports none
% Hannning Windows
Nfft = floor(2.0/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
[data_ol.pxx_Dx, data_ol.f] = pwelch(detrend(data_ol.Dx_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_ol.pxx_Dy, ~ ] = pwelch(detrend(data_ol.Dy_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_ol.pxx_Dz, ~ ] = pwelch(detrend(data_ol.Dz_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_ol.pxx_Rx, ~ ] = pwelch(detrend(data_ol.Rx_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_ol.pxx_Ry, ~ ] = pwelch(detrend(data_ol.Ry_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_lac.pxx_Dx, data_lac.f] = pwelch(detrend(data_lac.Dx_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_lac.pxx_Dy, ~ ] = pwelch(detrend(data_lac.Dy_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_lac.pxx_Dz, ~ ] = pwelch(detrend(data_lac.Dz_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_lac.pxx_Rx, ~ ] = pwelch(detrend(data_lac.Rx_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_lac.pxx_Ry, ~ ] = pwelch(detrend(data_lac.Ry_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_hac.pxx_Dx, data_hac.f] = pwelch(detrend(data_hac.Dx_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_hac.pxx_Dy, ~ ] = pwelch(detrend(data_hac.Dy_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_hac.pxx_Dz, ~ ] = pwelch(detrend(data_hac.Dz_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_hac.pxx_Rx, ~ ] = pwelch(detrend(data_hac.Rx_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_hac.pxx_Ry, ~ ] = pwelch(detrend(data_hac.Ry_int, 0), win, Noverlap, Nfft, 1/Ts);
#+end_src
Bandwidth is approximately 10Hz.
#+begin_src matlab :exports none :results none
%% Cumulative Amplitude Spectrum of the measured Dx and Dy motion
figure;
tiledlayout(1, 3, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile();
hold on;
plot(data_ol.f, sqrt(data_ol.pxx_Dy), 'DisplayName', 'OL');
plot(data_lac.f, sqrt(data_lac.pxx_Dy), 'DisplayName', 'LAC');
plot(data_hac.f, sqrt(data_hac.pxx_Dy), 'DisplayName', 'HAC');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('ASD [$m/\sqrt{Hz}$]');
title('$D_y$');
legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
ax2 = nexttile();
hold on;
plot(data_ol.f, sqrt(data_ol.pxx_Dz), 'DisplayName', 'OL');
plot(data_lac.f, sqrt(data_lac.pxx_Dz), 'DisplayName', 'LAC');
plot(data_hac.f, sqrt(data_hac.pxx_Dz), 'DisplayName', 'HAC');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
title('$D_z$');
legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
ax3 = nexttile();
hold on;
plot(data_ol.f, sqrt(data_ol.pxx_Ry), 'DisplayName', 'OL');
plot(data_lac.f, sqrt(data_lac.pxx_Ry), 'DisplayName', 'LAC');
plot(data_hac.f, sqrt(data_hac.pxx_Ry), 'DisplayName', 'HAC');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
title('$R_y$');
legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1);
linkaxes([ax1,ax2,ax3],'xy');
xlim([1, 1e3]); ylim([1e-12, 1e-7]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_noise_budget_effect_hac_m0.pdf', 'width', 'full', 'height', 'normal');
#+end_src
#+name: fig:id31_noise_budget_effect_hac_m0
#+caption: Measured vibration with the interferometers
#+RESULTS:
[[file:figs/id31_noise_budget_effect_hac_m0.png]]
#+begin_src matlab :exports none :results none
%% Cumulative Amplitude Spectrum of the measured Dx and Dy motion
figure;
tiledlayout(1, 3, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile();
hold on;
plot(data_ol.f, sqrt(flip(-cumtrapz(flip(data_ol.f), flip(data_ol.pxx_Dy)))), 'DisplayName', 'OL');
plot(data_lac.f, sqrt(flip(-cumtrapz(flip(data_lac.f), flip(data_lac.pxx_Dy)))), 'DisplayName', 'LAC');
plot(data_hac.f, sqrt(flip(-cumtrapz(flip(data_hac.f), flip(data_hac.pxx_Dy)))), 'DisplayName', 'HAC');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('CAS [$m$]');
title('$D_y$');
legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1);
ax2 = nexttile();
hold on;
plot(data_ol.f, sqrt(flip(-cumtrapz(flip(data_ol.f), flip(data_ol.pxx_Dz)))), 'DisplayName', 'OL');
plot(data_lac.f, sqrt(flip(-cumtrapz(flip(data_lac.f), flip(data_lac.pxx_Dz)))), 'DisplayName', 'LAC');
plot(data_hac.f, sqrt(flip(-cumtrapz(flip(data_hac.f), flip(data_hac.pxx_Dz)))), 'DisplayName', 'HAC');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
title('$D_z$');
legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1);
ax3 = nexttile();
hold on;
plot(data_ol.f, sqrt(flip(-cumtrapz(flip(data_ol.f), flip(data_ol.pxx_Ry)))), 'DisplayName', 'OL');
plot(data_lac.f, sqrt(flip(-cumtrapz(flip(data_lac.f), flip(data_lac.pxx_Ry)))), 'DisplayName', 'LAC');
plot(data_hac.f, sqrt(flip(-cumtrapz(flip(data_hac.f), flip(data_hac.pxx_Ry)))), 'DisplayName', 'HAC');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
title('$R_y$');
legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1);
linkaxes([ax1,ax2,ax3],'xy');
xlim([1, 1e3]); ylim([1e-10, 1e-6]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_cas_effect_hac_m0.pdf', 'width', 'full', 'height', 'normal');
#+end_src
#+name: fig:id31_cas_effect_hac_m0
#+caption: Measured vibration with the interferometers
#+RESULTS:
[[file:figs/id31_cas_effect_hac_m0.png]]
** TODO Noise coming from force sensor
* Integral Force Feedback
<<sec:id31_iff>>
** Introduction :ignore:
** 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 :tangle no :noweb yes
<<m-init-path>>
#+end_src
#+begin_src matlab :eval no :noweb yes
<<m-init-path-tangle>>
#+end_src
#+begin_src matlab :noweb yes
<<m-init-other>>
#+end_src
** IFF Plants
*** Introduction :ignore:
*** 6x6 Plant
#+begin_src matlab
%% Load identification data
data = load(sprintf('%s/dynamics/2023-08-08_16-17_ol_plant_m0_Wz0.mat', mat_dir));
#+end_src
#+begin_src matlab :exports none
% Hannning Windows
Nfft = floor(1.0/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
#+end_src
#+begin_src matlab :exports none
% And we get the frequency vector
[~, f] = tfestimate(data.uL1.id_plant, data.uL1.e_L1, win, [], [], 1/Ts);
#+end_src
#+begin_src matlab :exports none
%% IFF Plant (transfer function from u to taum)
G_iff = zeros(length(f), 6, 6);
for i_strut = 1:6
eL = [data.(sprintf("uL%i", i_strut)).Vs1 ; data.(sprintf("uL%i", i_strut)).Vs2 ; data.(sprintf("uL%i", i_strut)).Vs3 ; data.(sprintf("uL%i", i_strut)).Vs4 ; data.(sprintf("uL%i", i_strut)).Vs5 ; data.(sprintf("uL%i", i_strut)).Vs6]';
G_iff(:,:,i_strut) = tfestimate(data.(sprintf("uL%i", i_strut)).id_plant, eL, win, [], [], 1/Ts);
end
#+end_src
#+begin_src matlab :exports none :results none
%% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
for i = 1:5
for j = i+1:6
plot(f, abs(G_iff(:, i, j)), 'color', [0, 0, 0, 0.2], ...
'HandleVisibility', 'off');
end
end
for i = 1:6
plot(f, abs(G_iff(:,i, i)), 'color', colors(i,:), ...
'DisplayName', sprintf('$\\tau_{m,%i}/u_%i$', i, i));
end
plot(f, abs(G_iff(:, 1, 2)), 'color', [0, 0, 0, 0.2], ...
'DisplayName', '$\tau_{m,i}/u_j$');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Amplitude [V/V]'); set(gca, 'XTickLabel',[]);
ylim([1e-4, 1e2]);
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 3);
ax2 = nexttile;
hold on;
for i =1:6
plot(f, 180/pi*angle(G_iff(:,i, i)));
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-90, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_Giff_plant.pdf', 'width', 'full', 'height', 'tall');
#+end_src
#+name: fig:id31_Giff_plant
#+caption: Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor
#+RESULTS:
[[file:figs/id31_Giff_plant.png]]
Compare with Model:
#+begin_src matlab
load('Gm_iff.mat');
#+end_src
#+begin_src matlab :exports none :results none
%% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
for i = 1:5
for j = i+1:6
plot(f, abs(G_iff(:, i, j)), 'color', [colors(3,:), 0.2], ...
'HandleVisibility', 'off');
end
end
for i = 1:5
for j = i+1:6
plot(freqs, abs(squeeze(freqresp(Gm_iff_m0(i, j), freqs, 'Hz'))), 'color', [colors(4,:), 0.2], ...
'HandleVisibility', 'off');
end
end
for i = 2:6
plot(f, abs(G_iff(:,i, i)), 'color', [colors(1,:), 0.5], ...
'HandleVisibility', 'off');
end
for i = 2:6
plot(freqs, abs(squeeze(freqresp(Gm_iff_m0(i, i), freqs, 'Hz'))), 'color', [colors(2,:), 0.5], ...
'HandleVisibility', 'off');
end
% plot(f, abs(G_iff(:, 1, 2)), 'color', [0, 0, 0, 0.2], ...
% 'DisplayName', '$\tau_{m,i}/u_j$');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Amplitude [V/V]'); set(gca, 'XTickLabel',[]);
ylim([1e-4, 1e2]);
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 2);
% ax2 = nexttile;
% hold on;
% for i =1:6
% plot(f, 180/pi*angle(G_iff(:,i, i)));
% end
% hold off;
% set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
% xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
% hold off;
% yticks(-360:90:360);
% ylim([-90, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
*** Effect of Rotation
#+begin_src matlab
%% Load identification data
data_Wz0 = load(sprintf('%s/dynamics/2023-08-08_16-17_ol_plant_m0_Wz0.mat', mat_dir));
data_Wz1 = load(sprintf('%s/dynamics/2023-08-08_16-28_ol_plant_m0_Wz36.mat', mat_dir));
data_Wz2 = load(sprintf('%s/dynamics/2023-08-08_16-45_ol_plant_m0_Wz180.mat', mat_dir));
#+end_src
#+begin_src matlab :exports none
% Sampling Time [s]
Ts = 1e-4;
% Hannning Windows
Nfft = floor(1.0/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
#+end_src
#+begin_src matlab :exports none
% And we get the frequency vector
[~, f] = tfestimate(data.uL1.id_plant, data.uL1.e_L1, win, [], [], 1/Ts);
#+end_src
#+begin_src matlab :exports none
%% IFF Plant (transfer function from u to taum)
G_iff_Wz0 = zeros(length(f), 6, 6);
for i_strut = 1:6
eL = [data_Wz0.(sprintf("uL%i", i_strut)).Vs1 ; data_Wz0.(sprintf("uL%i", i_strut)).Vs2 ; data_Wz0.(sprintf("uL%i", i_strut)).Vs3 ; data_Wz0.(sprintf("uL%i", i_strut)).Vs4 ; data_Wz0.(sprintf("uL%i", i_strut)).Vs5 ; data_Wz0.(sprintf("uL%i", i_strut)).Vs6]';
G_iff_Wz0(:,:,i_strut) = tfestimate(data_Wz0.(sprintf("uL%i", i_strut)).id_plant, eL, win, [], [], 1/Ts);
end
#+end_src
#+begin_src matlab :exports none
%% IFF Plant (transfer function from u to taum)
G_iff_Wz1 = zeros(length(f), 6, 6);
for i_strut = 1:6
eL = [data_Wz1.(sprintf("uL%i", i_strut)).Vs1 ; data_Wz1.(sprintf("uL%i", i_strut)).Vs2 ; data_Wz1.(sprintf("uL%i", i_strut)).Vs3 ; data_Wz1.(sprintf("uL%i", i_strut)).Vs4 ; data_Wz1.(sprintf("uL%i", i_strut)).Vs5 ; data_Wz1.(sprintf("uL%i", i_strut)).Vs6]';
G_iff_Wz1(:,:,i_strut) = tfestimate(data_Wz1.(sprintf("uL%i", i_strut)).id_plant, eL, win, [], [], 1/Ts);
end
#+end_src
#+begin_src matlab :exports none
%% IFF Plant (transfer function from u to taum)
G_iff_Wz2 = zeros(length(f), 6, 6);
for i_strut = 1:6
eL = [data_Wz2.(sprintf("uL%i", i_strut)).Vs1 ; data_Wz2.(sprintf("uL%i", i_strut)).Vs2 ; data_Wz2.(sprintf("uL%i", i_strut)).Vs3 ; data_Wz2.(sprintf("uL%i", i_strut)).Vs4 ; data_Wz2.(sprintf("uL%i", i_strut)).Vs5 ; data_Wz2.(sprintf("uL%i", i_strut)).Vs6]';
G_iff_Wz2(:,:,i_strut) = tfestimate(data_Wz2.(sprintf("uL%i", i_strut)).id_plant, eL, win, [], [], 1/Ts);
end
#+end_src
#+begin_src matlab :exports none
%% Save Identified Plants
save('./mat/G_iff.mat', 'G_iff_Wz0', 'G_iff_Wz1', 'G_iff_Wz2', '-append');
#+end_src
#+begin_src matlab :exports none :results none
%% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_iff_Wz0(:, 1, 1)), 'color', [colors(1,:), 0.2], ...
'DisplayName', '$\Omega_z = 0$');
for i = 2:6
plot(f, abs(G_iff_Wz0(:,i, i)), 'color', [colors(1,:), 0.2], ...
'HandleVisibility', 'off')
end
plot(f, abs(G_iff_Wz1(:, 1, 1)), 'color', [colors(2,:), 0.2], ...
'DisplayName', '$\Omega_z = 36$ deg/s');
for i = 2:6
plot(f, abs(G_iff_Wz1(:,i, i)), 'color', [colors(2,:), 0.2], ...
'HandleVisibility', 'off')
end
plot(f, abs(G_iff_Wz2(:, 1, 1)), 'color', [colors(3,:), 0.2], ...
'DisplayName', '$\Omega_z = 180$ deg/s');
for i = 2:6
plot(f, abs(G_iff_Wz2(:,i, i)), 'color', [colors(3,:), 0.2], ...
'HandleVisibility', 'off')
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Amplitude [V/V]'); set(gca, 'XTickLabel',[]);
ylim([1e-2, 1e2]);
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1);
ax2 = nexttile;
hold on;
for i =1:6
plot(f, 180/pi*angle(G_iff_Wz0(:,i, i)), 'color', [colors(1,:), 0.2]);
end
for i =1:6
plot(f, 180/pi*angle(G_iff_Wz1(:,i, i)), 'color', [colors(2,:), 0.2]);
end
for i =1:6
plot(f, 180/pi*angle(G_iff_Wz2(:,i, i)), 'color', [colors(3,:), 0.2]);
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-90, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_Giff_effect_rotation.pdf', 'width', 'full', 'height', 'tall');
#+end_src
#+name: fig:id31_Giff_effect_rotation
#+caption: Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor
#+RESULTS:
[[file:figs/id31_Giff_effect_rotation.png]]
*** Effect of Mass
#+begin_src matlab
load('G_ol.mat', 'G_iff_m0', 'G_iff_m1', 'G_iff_m2', 'G_iff_m3', 'f');
#+end_src
#+begin_src matlab :exports none :results none
%% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_iff_m0(:, 1, 1)), 'color', [colors(1,:), 0.5], ...
'DisplayName', '$m = 0$');
for i = 2:6
plot(f, abs(G_iff_m0(:,i, i)), 'color', [colors(1,:), 0.5], ...
'HandleVisibility', 'off')
end
plot(f, abs(G_iff_m1(:, 1, 1)), 'color', [colors(2,:), 0.5], ...
'DisplayName', '$m = 1$');
for i = 2:6
plot(f, abs(G_iff_m1(:,i, i)), 'color', [colors(2,:), 0.5], ...
'HandleVisibility', 'off')
end
plot(f, abs(G_iff_m2(:, 1, 1)), 'color', [colors(3,:), 0.5], ...
'DisplayName', '$m = 2$');
for i = 2:6
plot(f, abs(G_iff_m2(:,i, i)), 'color', [colors(3,:), 0.5], ...
'HandleVisibility', 'off')
end
plot(f, abs(G_iff_m3(:, 1, 1)), 'color', [colors(4,:), 0.5], ...
'DisplayName', '$m = 3$');
for i = 2:6
plot(f, abs(G_iff_m3(:,i, i)), 'color', [colors(4,:), 0.5], ...
'HandleVisibility', 'off')
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Amplitude [V/V]'); set(gca, 'XTickLabel',[]);
ylim([1e-2, 1e2]);
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1);
ax2 = nexttile;
hold on;
for i =1:6
plot(f, 180/pi*angle(G_iff_m0(:,i, i)), 'color', [colors(1,:), 0.5]);
end
for i =1:6
plot(f, 180/pi*angle(G_iff_m1(:,i, i)), 'color', [colors(2,:), 0.5]);
end
for i =1:6
plot(f, 180/pi*angle(G_iff_m2(:,i, i)), 'color', [colors(3,:), 0.5]);
end
for i =1:6
plot(f, 180/pi*angle(G_iff_m3(:,i, i)), 'color', [colors(4,:), 0.5]);
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-90, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_Giff_effect_mass.pdf', 'width', 'full', 'height', 'tall');
#+end_src
#+name: fig:id31_Giff_effect_mass
#+caption: Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor
#+RESULTS:
[[file:figs/id31_Giff_effect_mass.png]]
*** Compare with the model
#+begin_src matlab
load('Gm.mat')
#+end_src
#+begin_src matlab :exports none :results none
%% Comparison of the identified IFF plant and the IFF plant extracted from the simscape model
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_iff_m0(:, 1, 1)), 'color', [colors(1,:), 0.5], ...
'DisplayName', '$m = 0$');
for i = 2:6
plot(f, abs(G_iff_m0(:,i, i)), 'color', [colors(1,:), 0.5], ...
'HandleVisibility', 'off')
end
plot(f, abs(G_iff_m1(:, 1, 1)), 'color', [colors(2,:), 0.5], ...
'DisplayName', '$m = 1$');
for i = 2:6
plot(f, abs(G_iff_m1(:,i, i)), 'color', [colors(2,:), 0.5], ...
'HandleVisibility', 'off')
end
plot(f, abs(G_iff_m2(:, 1, 1)), 'color', [colors(3,:), 0.5], ...
'DisplayName', '$m = 2$');
for i = 2:6
plot(f, abs(G_iff_m2(:,i, i)), 'color', [colors(3,:), 0.5], ...
'HandleVisibility', 'off')
end
plot(f, abs(G_iff_m3(:, 1, 1)), 'color', [colors(4,:), 0.5], ...
'DisplayName', '$m = 3$');
for i = 2:6
plot(f, abs(G_iff_m3(:,i, i)), 'color', [colors(4,:), 0.5], ...
'HandleVisibility', 'off')
end
plot(freqs, abs(squeeze(freqresp(Gm_m0('Fn1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(1,:), ...
'DisplayName', '$m = 0$ - Model');
plot(freqs, abs(squeeze(freqresp(Gm_m1('Fn1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(2,:), ...
'DisplayName', '$m = 1$ - Model');
plot(freqs, abs(squeeze(freqresp(Gm_m2('Fn1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(3,:), ...
'DisplayName', '$m = 2$ - Model');
plot(freqs, abs(squeeze(freqresp(Gm_m3('Fn1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(4,:), ...
'DisplayName', '$m = 3$ - Model');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Amplitude [V/V]'); set(gca, 'XTickLabel',[]);
ylim([1e-2, 1e2]);
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 2);
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(G_iff_m0(:,1,1)), 'color', [colors(1,:), 0.5]);
for i = 2:6
plot(f, 180/pi*angle(G_iff_m0(:,i, i)), 'color', [colors(1,:), 0.5]);
end
plot(f, 180/pi*angle(G_iff_m1(:,1,1)), 'color', [colors(2,:), 0.5]);
for i = 2:6
plot(f, 180/pi*angle(G_iff_m1(:,i, i)), 'color', [colors(2,:), 0.5]);
end
plot(f, 180/pi*angle(G_iff_m2(:,1,1)), 'color', [colors(3,:), 0.5]);
for i = 2:6
plot(f, 180/pi*angle(G_iff_m2(:,i, i)), 'color', [colors(3,:), 0.5]);
end
plot(f, 180/pi*angle(G_iff_m3(:,1,1)), 'color', [colors(4,:), 0.5]);
for i = 2:6
plot(f, 180/pi*angle(G_iff_m3(:,i, i)), 'color', [colors(4,:), 0.5]);
end
plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_m0('Fn1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(1,:));
plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_m1('Fn1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(2,:));
plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_m2('Fn1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(3,:));
plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_m3('Fn1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(4,:));
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-90, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_Giff_plant_comp_model.pdf', 'width', 'full', 'height', 'tall');
#+end_src
#+name: fig:id31_Giff_plant_comp_model
#+caption: Comparison of the identified IFF plant and the IFF plant extracted from the simscape model
#+RESULTS:
[[file:figs/id31_Giff_plant_comp_model.png]]
** IFF Controller
*** Controller Design
Test second order high pass filter:
#+begin_src matlab
wz = 2*pi*10;
xiz = 0.7;
Ghpf = (s^2/wz^2)/(s^2/wz^2 + 2*xiz*s/wz + 1)
% s/(2*pi*1)/(1 + s/(2*pi*1)) * ... % HPF: reduce gain at low frequency
#+end_src
We want integral action between 20Hz and 200Hz.
#+begin_src matlab
%% IFF Controller
Kiff = -1e2 * ... % Gain
1/(0.01*2*pi + s) * ... % LPF: provides integral action
Ghpf * ...
eye(6); % Diagonal 6x6 controller
Kiff.InputName = {'Fn1', 'Fn2', 'Fn3', 'Fn4', 'Fn5', 'Fn6'};
Kiff.OutputName = {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'};
#+end_src
Loop Gain:
#+begin_src matlab :exports none :results none
%% IFF Loop gain of the diagonal terms
Kiff_frf = squeeze(freqresp(Kiff(1,1), f, 'Hz'));
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_iff_m0(:, 1, 1).*Kiff_frf), 'color', colors(1,:), ...
'DisplayName', '$m = 0$');
plot(f, abs(G_iff_m1(:, 1, 1).*Kiff_frf), 'color', colors(2,:), ...
'DisplayName', '$m = 1$');
plot(f, abs(G_iff_m2(:, 1, 1).*Kiff_frf), 'color', colors(3,:), ...
'DisplayName', '$m = 2$');
plot(f, abs(G_iff_m3(:, 1, 1).*Kiff_frf), 'color', colors(4,:), ...
'DisplayName', '$m = 3$');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Amplitude [V/V]'); set(gca, 'XTickLabel',[]);
ylim([1e-2, 1e1]);
legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1);
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(-G_iff_m0(:,1,1).*Kiff_frf), 'color', colors(1,:));
plot(f, 180/pi*angle(-G_iff_m1(:,1,1).*Kiff_frf), 'color', colors(2,:));
plot(f, 180/pi*angle(-G_iff_m2(:,1,1).*Kiff_frf), 'color', colors(3,:));
plot(f, 180/pi*angle(-G_iff_m3(:,1,1).*Kiff_frf), 'color', colors(4,:));
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_iff_loop_gain_diagonal_terms.pdf', 'width', 'full', 'height', 'tall');
#+end_src
#+name: fig:id31_iff_loop_gain_diagonal_terms
#+caption: IFF Loop gain of the diagonal terms
#+RESULTS:
[[file:figs/id31_iff_loop_gain_diagonal_terms.png]]
Root Locus to obtain optimal gain.
#+begin_src matlab :exports none :results none
%% Root Locus for IFF
gains = logspace(-2, 2, 100);
Gm_iff_m0 = Gm_m0({'Fn1', 'Fn2', 'Fn3', 'Fn4', 'Fn5', 'Fn6'}, {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'});
Gm_iff_m1 = Gm_m1({'Fn1', 'Fn2', 'Fn3', 'Fn4', 'Fn5', 'Fn6'}, {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'});
Gm_iff_m2 = Gm_m2({'Fn1', 'Fn2', 'Fn3', 'Fn4', 'Fn5', 'Fn6'}, {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'});
Gm_iff_m3 = Gm_m3({'Fn1', 'Fn2', 'Fn3', 'Fn4', 'Fn5', 'Fn6'}, {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'});
figure;
tiledlayout(1, 4, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile();
hold on;
plot(real(pole(Gm_iff_m0)), imag(pole(Gm_iff_m0)), 'x', 'color', colors(1,:), ...
'DisplayName', '$g = 0$');
plot(real(tzero(Gm_iff_m0)), imag(tzero(Gm_iff_m0)), 'o', 'color', colors(1,:), ...
'HandleVisibility', 'off');
for g = gains
clpoles = pole(feedback(Gm_iff_m0, g*Kiff, +1));
plot(real(clpoles), imag(clpoles), '.', 'color', colors(1,:), ...
'HandleVisibility', 'off');
end
% Optimal gain
clpoles = pole(feedback(Gm_iff_m0, Kiff, +1));
plot(real(clpoles), imag(clpoles), 'x', 'color', colors(5,:), ...
'DisplayName', '$g_{opt}$');
hold off;
axis equal;
xlim([-640, 0]); ylim([0, 1600]);
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
title('$m_0$');
ax2 = nexttile();
hold on;
plot(real(pole(Gm_iff_m1)), imag(pole(Gm_iff_m1)), 'x', 'color', colors(2,:), ...
'DisplayName', '$g = 0$');
plot(real(tzero(Gm_iff_m1)), imag(tzero(Gm_iff_m1)), 'o', 'color', colors(2,:), ...
'HandleVisibility', 'off');
for g = gains
clpoles = pole(feedback(Gm_iff_m1, g*Kiff, +1));
plot(real(clpoles), imag(clpoles), '.', 'color', colors(2,:), ...
'HandleVisibility', 'off');
end
% Optimal gain
clpoles = pole(feedback(Gm_iff_m1, Kiff, +1));
plot(real(clpoles), imag(clpoles), 'x', 'color', colors(5,:), ...
'DisplayName', '$g_{opt}$');
hold off;
axis equal;
xlim([-320, 0]); ylim([0, 800]);
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
title('$m_1$');
ax3 = nexttile();
hold on;
plot(real(pole(Gm_iff_m2)), imag(pole(Gm_iff_m2)), 'x', 'color', colors(3,:), ...
'DisplayName', '$g = 0$');
plot(real(tzero(Gm_iff_m2)), imag(tzero(Gm_iff_m2)), 'o', 'color', colors(3,:), ...
'HandleVisibility', 'off');
for g = gains
clpoles = pole(feedback(Gm_iff_m2, g*Kiff, +1));
plot(real(clpoles), imag(clpoles), '.', 'color', colors(3,:), ...
'HandleVisibility', 'off');
end
% Optimal gain
clpoles = pole(feedback(Gm_iff_m2, Kiff, +1));
plot(real(clpoles), imag(clpoles), 'x', 'color', colors(5,:), ...
'DisplayName', '$g_{opt}$');
hold off;
axis equal;
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
xlim([-240, 0]); ylim([0, 600]);
title('$m_2$');
ax4 = nexttile();
hold on;
plot(real(pole(Gm_iff_m3)), imag(pole(Gm_iff_m3)), 'x', 'color', colors(4,:), ...
'DisplayName', '$g = 0$');
plot(real(tzero(Gm_iff_m3)), imag(tzero(Gm_iff_m3)), 'o', 'color', colors(4,:), ...
'HandleVisibility', 'off');
for g = gains
clpoles = pole(feedback(Gm_iff_m3, g*Kiff, +1));
plot(real(clpoles), imag(clpoles), '.', 'color', colors(4,:), ...
'HandleVisibility', 'off');
end
% Optimal gain
clpoles = pole(feedback(Gm_iff_m3, Kiff, +1));
plot(real(clpoles), imag(clpoles), 'x', 'color', colors(5,:), ...
'DisplayName', '$g_{opt}$');
hold off;
axis equal;
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
xlim([-160, 0]); ylim([0, 400]);
title('$m_3$');
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_iff_root_locus.pdf', 'width', 'full', 'height', 'tall');
#+end_src
#+name: fig:id31_iff_root_locus
#+caption: Root Locus for IFF. Green crosses are closed-loop poles for the same choosen IFF gain.
#+RESULTS:
[[file:figs/id31_iff_root_locus.png]]
*** TODO Verify Stability
Verify Stability with Nyquist plot:
- Why bad stability margins?
#+begin_src matlab :exports none
%% Compute the Eigenvalues of the loop gain
Ldet = zeros(4, 6, length(f));
% Loop gain
Lmimo = pagemtimes(permute(G_iff_m0, [2,3,1]),squeeze(freqresp(Kiff, f, 'Hz')));
for i_f = 2:length(f)
Ldet(1,:, i_f) = eig(squeeze(Lmimo(:,:,i_f)));
end
Lmimo = pagemtimes(permute(G_iff_m1, [2,3,1]),squeeze(freqresp(Kiff, f, 'Hz')));
for i_f = 2:length(f)
Ldet(2,:, i_f) = eig(squeeze(Lmimo(:,:,i_f)));
end
Lmimo = pagemtimes(permute(G_iff_m2, [2,3,1]),squeeze(freqresp(Kiff, f, 'Hz')));
for i_f = 2:length(f)
Ldet(3,:, i_f) = eig(squeeze(Lmimo(:,:,i_f)));
end
Lmimo = pagemtimes(permute(G_iff_m3, [2,3,1]),squeeze(freqresp(Kiff, f, 'Hz')));
for i_f = 2:length(f)
Ldet(4,:, i_f) = eig(squeeze(Lmimo(:,:,i_f)));
end
#+end_src
#+begin_src matlab :exports none
%% Plot of the eigenvalues of L in the complex plane
figure;
hold on;
for i_mass = 1:4
plot(real(squeeze(Ldet(i_mass, 1,:))), imag(squeeze(Ldet(i_mass, 1,:))), ...
'.', 'color', colors(i_mass, :), ...
'DisplayName', sprintf('%i masses', i_mass));
plot(real(squeeze(Ldet(i_mass, 1,:))), -imag(squeeze(Ldet(i_mass, 1,:))), ...
'.', 'color', colors(i_mass, :), ...
'HandleVisibility', 'off');
for i = 1:6
plot(real(squeeze(Ldet(i_mass, i,:))), imag(squeeze(Ldet(i_mass, i,:))), ...
'.', 'color', colors(i_mass, :), ...
'HandleVisibility', 'off');
plot(real(squeeze(Ldet(i_mass, i,:))), -imag(squeeze(Ldet(i_mass, i,:))), ...
'.', 'color', colors(i_mass, :), ...
'HandleVisibility', 'off');
end
end
plot(-1, 0, 'kx', 'HandleVisibility', 'off');
hold off;
set(gca, 'XScale', 'lin'); set(gca, 'YScale', 'lin');
xlabel('Real'); ylabel('Imag');
legend('location', 'southeast');
xlim([-3, 1]); ylim([-2, 2]);
#+end_src
*** Save Controller
#+begin_src matlab :exports none :tangle no
K_order = order(Kiff(1,1));
Kz = c2d(Kiff(1,1)*(1 + s/2/pi/2e3)^(9-K_order)/(1 + s/2/pi/2e3)^(9-K_order), 1e-4);
[num, den] = tfdata(Kz, 'v');
formatSpec = '%.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e\n';
fileID = fopen('/home/thomas/mnt/data_id31/nass/controllers/K_iff.dat', 'w');
fprintf(fileID, formatSpec, [num; den]');
fclose(fileID);
#+end_src
#+begin_src matlab
save('./matlab/mat/K_iff.mat', 'Kiff')
#+end_src
** Estimated Damped Plant
#+begin_src matlab
%% Damped plant from Simscape model
Gm_hac_m0 = -feedback(Gm_m0, Kiff, 'name', +1);
Gm_hac_m1 = -feedback(Gm_m1, Kiff, 'name', +1);
Gm_hac_m2 = -feedback(Gm_m2, Kiff, 'name', +1);
Gm_hac_m3 = -feedback(Gm_m3, Kiff, 'name', +1);
#+end_src
#+begin_src matlab
%% Verify Stability
isstable(Gm_hac_m0)
isstable(Gm_hac_m1)
isstable(Gm_hac_m2)
isstable(Gm_hac_m3)
#+end_src
#+begin_src matlab
%% Save Damped Plants
save('./matlab/mat/Gm.mat', 'Gm_hac_m0', 'Gm_hac_m1', 'Gm_hac_m2', 'Gm_hac_m3', '-append');
#+end_src
#+begin_src matlab :exports none :results none
%% Estimated damped plant from the Simscape model
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(freqs, abs(squeeze(freqresp(Gm_hac_m0(sprintf('eL%i', 1), sprintf('u%i', 1)), freqs, 'Hz'))), 'color', [colors(1,:), 0.5], ...
'DisplayName', '$\tau_{m,i}/u_i$ - $m_0$');
for i = 2:6
plot(freqs, abs(squeeze(freqresp(Gm_hac_m0(sprintf('eL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(1,:), 0.5], ...
'HandleVisibility', 'off');
end
plot(freqs, abs(squeeze(freqresp(Gm_hac_m1(sprintf('eL%i', 1), sprintf('u%i', 1)), freqs, 'Hz'))), 'color', [colors(2,:), 0.5], ...
'DisplayName', '$\tau_{m,i}/u_i$ - $m_1$');
for i = 2:6
plot(freqs, abs(squeeze(freqresp(Gm_hac_m1(sprintf('eL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(2,:), 0.5], ...
'HandleVisibility', 'off');
end
plot(freqs, abs(squeeze(freqresp(Gm_hac_m2(sprintf('eL%i', 1), sprintf('u%i', 1)), freqs, 'Hz'))), 'color', [colors(3,:), 0.5], ...
'DisplayName', '$\tau_{m,i}/u_i$ - $m_2$');
for i = 2:6
plot(freqs, abs(squeeze(freqresp(Gm_hac_m2(sprintf('eL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(3,:), 0.5], ...
'HandleVisibility', 'off');
end
plot(freqs, abs(squeeze(freqresp(Gm_hac_m3(sprintf('eL%i', 1), sprintf('u%i', 1)), freqs, 'Hz'))), 'color', [colors(4,:), 0.5], ...
'DisplayName', '$\tau_{m,i}/u_i$ - $m_3$');
for i = 2:6
plot(freqs, abs(squeeze(freqresp(Gm_hac_m3(sprintf('eL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(4,:), 0.5], ...
'HandleVisibility', 'off');
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Amplitude [m/V]'); set(gca, 'XTickLabel',[]);
ylim([1e-8, 1e-3]);
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 3);
ax2 = nexttile;
hold on;
for i =1:6
plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_hac_m0(sprintf('eL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(1,:), 0.5]);
end
for i =1:6
plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_hac_m1(sprintf('eL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(2,:), 0.5]);
end
for i =1:6
plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_hac_m2(sprintf('eL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(3,:), 0.5]);
end
for i =1:6
plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_hac_m3(sprintf('eL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(4,:), 0.5]);
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_hac_damped_plant_estimated_simscape.pdf', 'width', 'wide', 'height', 'tall');
#+end_src
#+name: fig:id31_hac_damped_plant_estimated_simscape
#+caption: description
#+RESULTS:
[[file:figs/id31_hac_damped_plant_estimated_simscape.png]]
* High Authority Control
<<sec:id31_iff_hac>>
** Introduction :ignore:
** 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 :tangle no :noweb yes
<<m-init-path>>
#+end_src
#+begin_src matlab :eval no :noweb yes
<<m-init-path-tangle>>
#+end_src
#+begin_src matlab :noweb yes
<<m-init-other>>
#+end_src
** Identify Spurious modes
#+begin_src matlab
%% Load identification data
data = load(sprintf('%s/dynamics/2023-08-10_18-32_identify_spurious_modes.mat', mat_dir));
#+end_src
#+begin_src matlab :exports none
% Hannning Windows
Nfft = floor(1.0/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
#+end_src
#+begin_src matlab :exports none
% And we get the frequency vector
[G1, f] = tfestimate(data.id_plant, data.d1, win, Noverlap, Nfft, 1/Ts);
[G2, ~] = tfestimate(data.id_plant, data.d2, win, Noverlap, Nfft, 1/Ts);
[G3, ~] = tfestimate(data.id_plant, data.d3, win, Noverlap, Nfft, 1/Ts);
[G4, ~] = tfestimate(data.id_plant, data.d4, win, Noverlap, Nfft, 1/Ts);
[G5, ~] = tfestimate(data.id_plant, data.d5, win, Noverlap, Nfft, 1/Ts);
#+end_src
#+begin_src matlab :exports none :results none
%% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor
figure;
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile();
hold on;
plot(f, abs(G1), 'DisplayName', '1 - top');
plot(f, abs(G2), 'DisplayName', '2 - bot');
plot(f, abs(G3), 'DisplayName', '3 - top');
plot(f, abs(G4), 'DisplayName', '4 - bot');
plot(f, abs(G5), 'DisplayName', '5 - vertical');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('Amplitude [m/V]');
xlim([500, 800])
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1);
#+end_src
** HAC Plants
*** Introduction :ignore:
*** 6x6 Plant
#+begin_src matlab
%% Load identification data
data = load(sprintf('%s/dynamics/2023-08-17_17-53_damp_plant_m0_Wz0.mat', mat_dir));
#+end_src
#+begin_src matlab :exports none
% Hannning Windows
Nfft = floor(1.0/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
#+end_src
#+begin_src matlab :exports none
% And we get the frequency vector
[~, f] = tfestimate(data.uL1.id_plant, data.uL1.e_L1, win, [], [], 1/Ts);
#+end_src
#+begin_src matlab :exports none
%% HAC Plant
G_hac = zeros(length(f), 6, 6);
for i_strut = 1:6
eL = [data.(sprintf("uL%i", i_strut)).e_L1 ; data.(sprintf("uL%i", i_strut)).e_L2 ; data.(sprintf("uL%i", i_strut)).e_L3 ; data.(sprintf("uL%i", i_strut)).e_L4 ; data.(sprintf("uL%i", i_strut)).e_L5 ; data.(sprintf("uL%i", i_strut)).e_L6]';
G_hac(:,:,i_strut) = tfestimate(data.(sprintf("uL%i", i_strut)).id_plant, -detrend(eL, 0), win, [], [], 1/Ts);
end
#+end_src
#+begin_src matlab :exports none :results none
%% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
for i = 1:5
for j = i+1:6
plot(f, abs(G_hac(:, i, j)), 'color', [0, 0, 0, 0.2], ...
'HandleVisibility', 'off');
end
end
for i = 1:6
plot(f, abs(G_hac(:,i, i)), 'color', colors(i,:), ...
'DisplayName', sprintf('$\\tau_{m,%i}/u_%i$', i, i));
end
plot(f, abs(G_hac(:, 1, 2)), 'color', [0, 0, 0, 0.2], ...
'DisplayName', '$\tau_{m,i}/u_j$');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Amplitude [m/V]'); set(gca, 'XTickLabel',[]);
ylim([1e-8, 1e-3]);
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 3);
ax2 = nexttile;
hold on;
for i =1:6
plot(f, 180/pi*angle(G_hac(:,i, i)));
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
Compare with Model:
#+begin_src matlab
load('Gm.mat');
#+end_src
#+begin_src matlab :exports none :results none
%% 6x6 plant from generated voltages to displacement of the struts as measured by the external metrology
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_hac(:,1,2)), 'color', [colors(3,:), 0.5], ...
'DisplayName', 'Identified, Off-Diagonal');
for i = 1:5
for j = i+1:6
plot(f, abs(G_hac(:, i, j)), 'color', [colors(3,:), 0.2], ...
'HandleVisibility', 'off');
end
end
plot(freqs, abs(squeeze(freqresp(Gm_hac_m0(sprintf('eL%i', 1), sprintf('u%i', 2)), freqs, 'Hz'))), 'color', [colors(4,:), 0.5], ...
'DisplayName', 'Model, Off-Diagonal');
for i = 1:5
for j = i+1:6
plot(freqs, abs(squeeze(freqresp(Gm_hac_m0(sprintf('eL%i', i), sprintf('u%i', j)), freqs, 'Hz'))), 'color', [colors(4,:), 0.2], ...
'HandleVisibility', 'off');
end
end
plot(f, abs(G_hac(:,1,1)), 'color', [colors(1,:), 0.5], ...
'DisplayName', 'Identified, Diagonal');
for i = 2:6
plot(f, abs(G_hac(:,i, i)), 'color', [colors(1,:), 0.5], ...
'HandleVisibility', 'off');
end
plot(freqs, abs(squeeze(freqresp(Gm_hac_m0(sprintf('eL%i', 1), sprintf('u%i', 1)), freqs, 'Hz'))), 'color', [colors(2,:), 0.5], ...
'DisplayName', 'Model, Diagonal');
for i = 2:6
plot(freqs, abs(squeeze(freqresp(Gm_hac_m0(sprintf('eL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(2,:), 0.5], ...
'HandleVisibility', 'off');
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Amplitude [m/V]'); set(gca, 'XTickLabel',[]);
ylim([1e-8, 1e-3]);
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 2);
ax2 = nexttile;
hold on;
for i = 1:6
plot(f, 180/pi*angle(G_hac(:,i, i)), 'color', [colors(1,:), 0.5])
end
for i = 1:6
plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_hac_m0(sprintf('eL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(2,:), 0.5])
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_Ghac_6x6_plant_comp_model.pdf', 'width', 'full', 'height', 'tall');
#+end_src
#+name: fig:id31_Ghac_6x6_plant_comp_model
#+caption: 6x6 plant from generated voltages to displacement of the struts as measured by the external metrology
#+RESULTS:
[[file:figs/id31_Ghac_6x6_plant_comp_model.png]]
*** Effect of Mass
#+begin_src matlab
%% Load identification data
data_m0 = load(sprintf('%s/dynamics/2023-08-17_17-53_damp_plant_m0_Wz0.mat', mat_dir));
data_m1 = load(sprintf('%s/dynamics/2023-08-10_16-01_damp_plant_m1_Wz0.mat', mat_dir));
data_m2 = load(sprintf('%s/dynamics/2023-08-10_17-28_damp_plant_m2_Wz0.mat', mat_dir));
data_m3 = load(sprintf('%s/dynamics/2023-08-10_18-16_damp_plant_m3_Wz0.mat', mat_dir));
#+end_src
#+begin_src matlab :exports none
% Sampling Time [s]
Ts = 1e-4;
% Hannning Windows
Nfft = floor(2.0/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
#+end_src
#+begin_src matlab :exports none
% And we get the frequency vector
[~, f] = tfestimate(data_m0.uL1.id_plant, data_m0.uL1.e_L1, win, Noverlap, Nfft, 1/Ts);
#+end_src
#+begin_src matlab :exports none
%% HAC Plant (transfer function from u to taum)
G_hac_m0 = zeros(length(f), 6, 6);
for i_strut = 1:6
eL = [data_m0.(sprintf("uL%i", i_strut)).e_L1 ; data_m0.(sprintf("uL%i", i_strut)).e_L2 ; data_m0.(sprintf("uL%i", i_strut)).e_L3 ; data_m0.(sprintf("uL%i", i_strut)).e_L4 ; data_m0.(sprintf("uL%i", i_strut)).e_L5 ; data_m0.(sprintf("uL%i", i_strut)).e_L6]';
G_hac_m0(:,:,i_strut) = tfestimate(data_m0.(sprintf("uL%i", i_strut)).id_plant, -detrend(eL, 0), win, Noverlap, Nfft, 1/Ts);
end
#+end_src
#+begin_src matlab :exports none
%% HAC Plant (transfer function from u to taum)
G_hac_m1 = zeros(length(f), 6, 6);
for i_strut = 1:6
eL = [data_m1.(sprintf("uL%i", i_strut)).e_L1 ; data_m1.(sprintf("uL%i", i_strut)).e_L2 ; data_m1.(sprintf("uL%i", i_strut)).e_L3 ; data_m1.(sprintf("uL%i", i_strut)).e_L4 ; data_m1.(sprintf("uL%i", i_strut)).e_L5 ; data_m1.(sprintf("uL%i", i_strut)).e_L6]';
G_hac_m1(:,:,i_strut) = tfestimate(data_m1.(sprintf("uL%i", i_strut)).id_plant, -detrend(eL, 0), win, Noverlap, Nfft, 1/Ts);
end
#+end_src
#+begin_src matlab :exports none
%% HAC Plant (transfer function from u to taum)
G_hac_m2 = zeros(length(f), 6, 6);
for i_strut = 1:6
eL = [data_m2.(sprintf("uL%i", i_strut)).e_L1 ; data_m2.(sprintf("uL%i", i_strut)).e_L2 ; data_m2.(sprintf("uL%i", i_strut)).e_L3 ; data_m2.(sprintf("uL%i", i_strut)).e_L4 ; data_m2.(sprintf("uL%i", i_strut)).e_L5 ; data_m2.(sprintf("uL%i", i_strut)).e_L6]';
G_hac_m2(:,:,i_strut) = tfestimate(data_m2.(sprintf("uL%i", i_strut)).id_plant, -detrend(eL, 0), win, Noverlap, Nfft, 1/Ts);
end
#+end_src
#+begin_src matlab :exports none
%% HAC Plant (transfer function from u to taum)
G_hac_m3 = zeros(length(f), 6, 6);
for i_strut = 1:6
eL = [data_m3.(sprintf("uL%i", i_strut)).e_L1 ; data_m3.(sprintf("uL%i", i_strut)).e_L2 ; data_m3.(sprintf("uL%i", i_strut)).e_L3 ; data_m3.(sprintf("uL%i", i_strut)).e_L4 ; data_m3.(sprintf("uL%i", i_strut)).e_L5 ; data_m3.(sprintf("uL%i", i_strut)).e_L6]';
G_hac_m3(:,:,i_strut) = tfestimate(data_m3.(sprintf("uL%i", i_strut)).id_plant, -detrend(eL, 0), win, Noverlap, Nfft, 1/Ts);
end
#+end_src
#+begin_src matlab :exports none
%% Save Identified Plants
save('./matlab/mat/G_hac.mat', 'G_hac_m0', 'G_hac_m1', 'G_hac_m2', 'G_hac_m3', 'f');
#+end_src
#+begin_src matlab :exports none :results none
%% Obtained transfer functions
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_hac_m0(:, 1, 1)), 'color', [colors(1,:), 0.5], ...
'DisplayName', '$m = 0$');
for i = 2:6
plot(f, abs(G_hac_m0(:,i, i)), 'color', [colors(1,:), 0.5], ...
'HandleVisibility', 'off')
end
plot(f, abs(G_hac_m1(:, 1, 1)), 'color', [colors(2,:), 0.5], ...
'DisplayName', '$m = 1$');
for i = 2:6
plot(f, abs(G_hac_m1(:,i, i)), 'color', [colors(2,:), 0.5], ...
'HandleVisibility', 'off')
end
plot(f, abs(G_hac_m2(:, 1, 1)), 'color', [colors(3,:), 0.5], ...
'DisplayName', '$m = 2$');
for i = 2:6
plot(f, abs(G_hac_m2(:,i, i)), 'color', [colors(3,:), 0.5], ...
'HandleVisibility', 'off')
end
plot(f, abs(G_hac_m3(:, 1, 1)), 'color', [colors(4,:), 0.5], ...
'DisplayName', '$m = 3$');
for i = 2:6
plot(f, abs(G_hac_m3(:,i, i)), 'color', [colors(4,:), 0.5], ...
'HandleVisibility', 'off')
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Amplitude [m/V]'); set(gca, 'XTickLabel',[]);
ylim([1e-8, 1e-3]);
legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 2);
ax2 = nexttile;
hold on;
for i =1:6
plot(f, 180/pi*angle(G_hac_m0(:,i, i)), 'color', [colors(1,:), 0.5]);
end
for i =1:6
plot(f, 180/pi*angle(G_hac_m1(:,i, i)), 'color', [colors(2,:), 0.5]);
end
for i =1:6
plot(f, 180/pi*angle(G_hac_m2(:,i, i)), 'color', [colors(3,:), 0.5]);
end
for i =1:6
plot(f, 180/pi*angle(G_hac_m3(:,i, i)), 'color', [colors(4,:), 0.5]);
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_Ghac_effect_mass.pdf', 'width', 'full', 'height', 'tall');
#+end_src
#+name: fig:id31_Ghac_effect_mass
#+caption: Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor
#+RESULTS:
[[file:figs/id31_Ghac_effect_mass.png]]
*** Compare with the model
#+begin_src matlab
load('G_hac.mat')
load('Gm.mat')
#+end_src
#+begin_src matlab :exports none :results none
%% Comparison of the identified HAC plant and the HAC plant extracted from the simscape model
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_hac_m0(:, 1, 1)), 'color', [colors(1,:), 0.5], ...
'DisplayName', '$m = 0$');
for i = 2:6
plot(f, abs(G_hac_m0(:,i, i)), 'color', [colors(1,:), 0.5], ...
'HandleVisibility', 'off')
end
plot(f, abs(G_hac_m1(:, 1, 1)), 'color', [colors(2,:), 0.5], ...
'DisplayName', '$m = 1$');
for i = 2:6
plot(f, abs(G_hac_m1(:,i, i)), 'color', [colors(2,:), 0.5], ...
'HandleVisibility', 'off')
end
plot(f, abs(G_hac_m2(:, 1, 1)), 'color', [colors(3,:), 0.5], ...
'DisplayName', '$m = 2$');
for i = 2:6
plot(f, abs(G_hac_m2(:,i, i)), 'color', [colors(3,:), 0.5], ...
'HandleVisibility', 'off')
end
plot(f, abs(G_hac_m3(:, 1, 1)), 'color', [colors(4,:), 0.5], ...
'DisplayName', '$m = 3$');
for i = 2:6
plot(f, abs(G_hac_m3(:,i, i)), 'color', [colors(4,:), 0.5], ...
'HandleVisibility', 'off')
end
plot(freqs, abs(squeeze(freqresp(Gm_hac_m0('eL1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(1,:), ...
'DisplayName', '$m = 0$ - Model');
plot(freqs, abs(squeeze(freqresp(Gm_hac_m1('eL1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(2,:), ...
'DisplayName', '$m = 1$ - Model');
plot(freqs, abs(squeeze(freqresp(Gm_hac_m2('eL1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(3,:), ...
'DisplayName', '$m = 2$ - Model');
plot(freqs, abs(squeeze(freqresp(Gm_hac_m3('eL1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(4,:), ...
'DisplayName', '$m = 3$ - Model');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Amplitude [m/V]'); set(gca, 'XTickLabel',[]);
ylim([1e-8, 1e-3]);
legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 2);
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(G_hac_m0(:,1,1)), 'color', [colors(1,:), 0.5]);
for i = 2:6
plot(f, 180/pi*angle(G_hac_m0(:,i, i)), 'color', [colors(1,:), 0.5]);
end
plot(f, 180/pi*angle(G_hac_m1(:,1,1)), 'color', [colors(2,:), 0.5]);
for i = 2:6
plot(f, 180/pi*angle(G_hac_m1(:,i, i)), 'color', [colors(2,:), 0.5]);
end
plot(f, 180/pi*angle(G_hac_m2(:,1,1)), 'color', [colors(3,:), 0.5]);
for i = 2:6
plot(f, 180/pi*angle(G_hac_m2(:,i, i)), 'color', [colors(3,:), 0.5]);
end
plot(f, 180/pi*angle(G_hac_m3(:,1,1)), 'color', [colors(4,:), 0.5]);
for i = 2:6
plot(f, 180/pi*angle(G_hac_m3(:,i, i)), 'color', [colors(4,:), 0.5]);
end
plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_hac_m0('eL1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(1,:));
plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_hac_m1('eL1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(2,:));
plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_hac_m2('eL1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(3,:));
plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_hac_m3('eL1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(4,:));
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_Ghac_plant_comp_model.pdf', 'width', 'full', 'height', 'tall');
#+end_src
#+name: fig:id31_Ghac_plant_comp_model
#+caption: Comparison of the identified HAC plant and the HAC plant extracted from the simscape model
#+RESULTS:
[[file:figs/id31_Ghac_plant_comp_model.png]]
*** Comparison with Undamped plant
#+begin_src matlab
load('G_ol.mat');
load('G_hac.mat');
#+end_src
#+begin_src matlab :exports none :results none
%% description
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_int_m0(:, 1, 1)), 'color', [colors(1,:), 0.5], ...
'DisplayName', '$m = 0$ - OL');
for i = 2:6
plot(f, abs(G_int_m0(:,i, i)), 'color', [colors(1,:), 0.5], ...
'HandleVisibility', 'off')
end
plot(f, abs(G_hac_m0(:, 1, 1)), 'color', [colors(2,:), 0.5], ...
'DisplayName', '$m = 0$ - Damped');
for i = 2:6
plot(f, abs(G_hac_m0(:,i, i)), 'color', [colors(2,:), 0.5], ...
'HandleVisibility', 'off')
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Amplitude [m/V]'); set(gca, 'XTickLabel',[]);
ylim([1e-8, 1e-3]);
legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 2);
ax2 = nexttile;
hold on;
for i =1:6
plot(f, 180/pi*angle(-G_int_m0(:,i, i)), 'color', [colors(1,:), 0.5]);
end
for i =1:6
plot(f, 180/pi*angle(G_hac_m0(:,i, i)), 'color', [colors(2,:), 0.5]);
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_comp_undamped_damped_plant_m0.pdf', 'width', 'full', 'height', 'tall');
#+end_src
#+name: fig:id31_comp_undamped_damped_plant_m0
#+caption: description
#+RESULTS:
[[file:figs/id31_comp_undamped_damped_plant_m0.png]]
** Robust HAC
#+begin_src matlab
load('G_hac.mat')
load('Gm.mat')
#+end_src
*** Controller design
#+begin_src matlab
%% Wanted crossover
wc = 2*pi*5;
%% Double Integrator
% H_int = (wc^2)/(s + 1e-1*2*pi)^2;
H_int = wc/s;
%% Lead to increase phase margin
a = 2; % Amount of phase lead / width of the phase lead / high frequency gain
H_lead = 1/sqrt(a)*(1 + s/(wc/sqrt(a)))/(1 + s/(wc*sqrt(a)));
%% Low Pass filter to increase robustness
H_lpf = 1/(1 + s/2/pi/30);
%% Notch at the top-plate resonance
% gm = 0.02;
% xi = 0.3;
% wn = 2*pi*665;
% H_notch = (s^2 + 2*gm*xi*wn*s + wn^2)/(s^2 + 2*xi*wn*s + wn^2);
%% Gain to have unitary crossover at 30Hz
[~, i_f] = min(abs(f - wc/2/pi));
H_gain = 1./abs(G_hac_m0(i_f, 1, 1));
%% Decentralized HAC
Khac = H_gain * ... % Gain
H_int * ... % Integrator
H_lpf * ... % Integrator
eye(6); % 6x6 Diagonal
#+end_src
Loop gain
#+begin_src matlab :exports none :results none
%% description
figure;
tiledlayout(3, 1, 'TileSpacing', 'Compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_hac_m0(:,1, 1).*squeeze(freqresp(Khac(1,1), f, 'Hz'))), 'color', colors(1,:), 'DisplayName', '$m_0$');
plot(f, abs(G_hac_m1(:,1, 1).*squeeze(freqresp(Khac(1,1), f, 'Hz'))), 'color', colors(2,:), 'DisplayName', '$m_1$');
plot(f, abs(G_hac_m2(:,1, 1).*squeeze(freqresp(Khac(1,1), f, 'Hz'))), 'color', colors(3,:), 'DisplayName', '$m_2$');
plot(f, abs(G_hac_m3(:,1, 1).*squeeze(freqresp(Khac(1,1), f, 'Hz'))), 'color', colors(4,:), 'DisplayName', '$m_3$');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Loop Gain'); set(gca, 'XTickLabel',[]);
legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 3);
ylim([1e-5, 1e2]);
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(G_hac_m0(:,1, 1).*squeeze(freqresp(Khac(1,1), f, 'Hz'))), 'color', colors(1,:));
plot(f, 180/pi*angle(G_hac_m1(:,1, 1).*squeeze(freqresp(Khac(1,1), f, 'Hz'))), 'color', colors(2,:));
plot(f, 180/pi*angle(G_hac_m2(:,1, 1).*squeeze(freqresp(Khac(1,1), f, 'Hz'))), 'color', colors(3,:));
plot(f, 180/pi*angle(G_hac_m3(:,1, 1).*squeeze(freqresp(Khac(1,1), f, 'Hz'))), 'color', colors(4,:));
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_hac_robust_loop_gain.pdf', 'width', 'full', 'height', 'tall');
#+end_src
#+name: fig:id31_hac_robust_loop_gain
#+caption: description
#+RESULTS:
[[file:figs/id31_hac_robust_loop_gain.png]]
*** Verify Stability
#+begin_src matlab :exports none
%% Compute the Eigenvalues of the loop gain
Ldet = zeros(4, 6, length(f));
% Loop gain
Lmimo = pagemtimes(permute(G_hac_m0, [2,3,1]),squeeze(freqresp(Khac, f, 'Hz')));
for i_f = 2:length(f)
Ldet(1,:, i_f) = eig(squeeze(Lmimo(:,:,i_f)));
end
Lmimo = pagemtimes(permute(G_hac_m1, [2,3,1]),squeeze(freqresp(Khac, f, 'Hz')));
for i_f = 2:length(f)
Ldet(2,:, i_f) = eig(squeeze(Lmimo(:,:,i_f)));
end
Lmimo = pagemtimes(permute(G_hac_m2, [2,3,1]),squeeze(freqresp(Khac, f, 'Hz')));
for i_f = 2:length(f)
Ldet(3,:, i_f) = eig(squeeze(Lmimo(:,:,i_f)));
end
Lmimo = pagemtimes(permute(G_hac_m3, [2,3,1]),squeeze(freqresp(Khac, f, 'Hz')));
for i_f = 2:length(f)
Ldet(4,:, i_f) = eig(squeeze(Lmimo(:,:,i_f)));
end
#+end_src
#+begin_src matlab :exports none
%% Plot of the eigenvalues of L in the complex plane
figure;
hold on;
for i_mass = 1:4
plot(real(squeeze(Ldet(i_mass, 1,:))), imag(squeeze(Ldet(i_mass, 1,:))), ...
'.', 'color', colors(i_mass, :), ...
'DisplayName', sprintf('$m_%i$', i_mass));
plot(real(squeeze(Ldet(i_mass, 1,:))), -imag(squeeze(Ldet(i_mass, 1,:))), ...
'.', 'color', colors(i_mass, :), ...
'HandleVisibility', 'off');
for i = 1:6
plot(real(squeeze(Ldet(i_mass, i,:))), imag(squeeze(Ldet(i_mass, i,:))), ...
'.', 'color', colors(i_mass, :), ...
'HandleVisibility', 'off');
plot(real(squeeze(Ldet(i_mass, i,:))), -imag(squeeze(Ldet(i_mass, i,:))), ...
'.', 'color', colors(i_mass, :), ...
'HandleVisibility', 'off');
end
end
plot(-1, 0, 'kx', 'HandleVisibility', 'off');
hold off;
set(gca, 'XScale', 'lin'); set(gca, 'YScale', 'lin');
xlabel('Real'); ylabel('Imag');
legend('location', 'southeast');
axis square
xlim([-1.5, 0.5]); ylim([-1, 1]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_hac_robust_nyquist.pdf', 'width', 'wide', 'height', 'tall');
#+end_src
#+name: fig:id31_hac_robust_nyquist
#+caption: description
#+RESULTS:
[[file:figs/id31_hac_robust_nyquist.png]]
*** Estimated performances
*** Save Controller
#+begin_src matlab :exports none :tangle no
K_order = order(Khac(1,1));
Kz = c2d(Khac(1,1)*(1 + s/2/pi/2e3)^(9-K_order)/(1 + s/2/pi/2e3)^(9-K_order), 1e-4);
[num, den] = tfdata(Kz, 'v');
formatSpec = '%.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e\n';
fileID = fopen('/home/thomas/mnt/data_id31/nass/controllers/K_hac.dat', 'w');
fprintf(fileID, formatSpec, [num; den]');
fclose(fileID);
#+end_src
#+begin_src matlab
save('./matlab/mat/K_hac.mat', 'Khac')
#+end_src
** High Performance HAC
*** Introduction :ignore:
The goal is to make a controller specific for one mass in order to have high bandwidth.
*** Mass 0
**** Load Plant
#+begin_src matlab
load('G_hac.mat')
load('Gm.mat')
#+end_src
**** Plant
#+begin_src matlab :exports none :results none
%% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
for i = 1:5
for j = i+1:6
plot(f, abs(G_hac_m0(:, i, j)), 'color', [0, 0, 0, 0.2], ...
'HandleVisibility', 'off');
end
end
for i = 1:6
plot(f, abs(G_hac_m0(:,i, i)), 'color', colors(i,:), ...
'DisplayName', sprintf('$\\tau_{m,%i}/u_%i$', i, i));
end
plot(f, abs(G_hac_m0(:, 1, 2)), 'color', [0, 0, 0, 0.2], ...
'DisplayName', '$\tau_{m,i}/u_j$');
plot(freqs, abs(squeeze(freqresp(Gm_hac_m0('eL1', 'u1'), freqs, 'Hz'))), 'k--', ...
'DisplayName', '$\tau_{m,i}/u_i$ - Model');
plot(freqs, abs(squeeze(freqresp(Gm_hac_m0('eL2', 'u1'), freqs, 'Hz'))), 'color', [colors(1,:), 0.2], ...
'DisplayName', '$\tau_{m,i}/u_j$ - Model');
for i = 1:5
for j = i+1:6
plot(freqs, abs(squeeze(freqresp(Gm_hac_m0(sprintf('eL%i', i), sprintf('u%i', j)), freqs, 'Hz'))), 'color', [colors(1,:), 0.2], ...
'HandleVisibility', 'off');
end
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Amplitude [m/V]'); set(gca, 'XTickLabel',[]);
ylim([1e-8, 1e-3]);
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 3);
ax2 = nexttile;
hold on;
for i =1:6
plot(f, 180/pi*angle(G_hac_m0(:,i, i)));
end
plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_hac_m0('eL1', 'u1'), freqs, 'Hz'))), 'k--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
**** Controller design
#+begin_src matlab
%% Wanted crossover
wc = 2*pi*50;
%% Double Integrator
H_int = 1/(s + 0.1*2*pi) * ...
1/(0.1*2*pi + s);
H_int = H_int/abs(evalfr(H_int, 1j*wc));
% H_int = wc/s;
%% Lead to increase phase margin
a = 3; % Amount of phase lead / width of the phase lead / high frequency gain
H_lead = 1/sqrt(a)*(1 + s/(1.5*wc/sqrt(a)))/(1 + s/(1.5*wc*sqrt(a)));
a = 3; % Amount of phase lead / width of the phase lead / high frequency gain
H_lead = H_lead/sqrt(a)*(1 + s/(2.5*wc/sqrt(a)))/(1 + s/(2.5*wc*sqrt(a)));
%% Low Pass filter to increase robustness
H_lpf = 1/(1 + s/2/pi/500);
%% Notch at the top-plate resonance
% gm = 0.02;
% xi = 0.3;
% wn = 2*pi*665;
% H_notch = (s^2 + 2*gm*xi*wn*s + wn^2)/(s^2 + 2*xi*wn*s + wn^2);
%% Gain to have unitary crossover at 30Hz
[~, i_f] = min(abs(f - wc/2/pi));
H_gain = 1./abs(G_hac_m0(i_f, 1, 1));
%% Decentralized HAC
Khac = H_gain * ... % Gain
H_int * ... % Integrator
H_lpf * ... % Low Pass Filter
H_lead * ... % Integrator
eye(6); % 6x6 Diagonal
Khac.InputName = {'eL1', 'eL2', 'eL3', 'eL4', 'eL5', 'eL6'};
Khac.OutputName = {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'};
#+end_src
Loop gain
#+begin_src matlab :exports none :results none
%% Loop gain for the High Authority Control
figure;
tiledlayout(3, 1, 'TileSpacing', 'Compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
for i = 1:6
plot(f, abs(G_hac_m0(:,i,i).*squeeze(freqresp(Khac(i,i), f, 'Hz'))), 'color', [colors(i,:), 0.5]);
end
for i = 1:5
for j = i+1:6
plot(f, abs(G_hac_m0(:,i,j).*squeeze(freqresp(Khac(i,i), f, 'Hz'))), 'color', [0, 0, 0, 0.2]);
end
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Loop Gain'); set(gca, 'XTickLabel',[]);
ylim([1e-4, 1e4]);
ax2 = nexttile;
hold on;
for i = 1:6
plot(f, 180/pi*angle(G_hac_m0(:,i,i).*squeeze(freqresp(Khac(i,i), f, 'Hz'))), 'color', [colors(i,:), 0.5]);
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_high_perf_hac_m0_loop_gain.pdf', 'width', 'wide', 'height', 'tall');
#+end_src
#+name: fig:id31_high_perf_hac_m0_loop_gain
#+caption: Loop gain for the High Authority Control
#+RESULTS:
[[file:figs/id31_high_perf_hac_m0_loop_gain.png]]
**** Verify Stability
#+begin_src matlab :exports none
%% Compute the Eigenvalues of the loop gain
Ldet = zeros(6, length(f));
% Loop gain
Lmimo = pagemtimes(permute(G_hac_m0, [2,3,1]),squeeze(freqresp(Khac, f, 'Hz')));
for i_f = 2:length(f)
Ldet(:, i_f) = eig(squeeze(Lmimo(:,:,i_f)));
end
#+end_src
#+begin_src matlab :exports none
%% Plot of the eigenvalues of L in the complex plane
figure;
hold on;
plot(real(squeeze(Ldet(1,:))), imag(squeeze(Ldet(1,:))), 'k.');
plot(real(squeeze(Ldet(1,:))), -imag(squeeze(Ldet(1,:))), 'k.');
for i = 1:6
plot(real(squeeze(Ldet(i,:))), imag(squeeze(Ldet(i,:))), 'k.');
plot(real(squeeze(Ldet(i,:))), -imag(squeeze(Ldet(i,:))), 'k.');
end
plot(-1, 0, 'kx', 'HandleVisibility', 'off');
hold off;
set(gca, 'XScale', 'lin'); set(gca, 'YScale', 'lin');
xlabel('Real'); ylabel('Imag');
xlim([-3, 1]); ylim([-2, 2]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_high_perf_hac_m0_nyquist.pdf', 'width', 'wide', 'height', 'tall');
#+end_src
#+name: fig:id31_high_perf_hac_m0_nyquist
#+caption: Nyquist plot for the High Authority Control
#+RESULTS:
[[file:figs/id31_high_perf_hac_m0_nyquist.png]]
**** Estimated performances
Loop gain with model
#+begin_src matlab :exports none
%% Loop Gain
figure;
tiledlayout(3, 1, 'TileSpacing', 'Compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
for i = 1:6
plot(freqs, abs(squeeze(freqresp(Gm_hac_m0(sprintf('eL%i', i), sprintf('u%i', i))*Khac(i,i), freqs, 'Hz'))), 'color', [colors(i,:), 0.5]);
end
for i = 1:5
for j = i+1:6
plot(freqs, abs(squeeze(freqresp(Gm_hac_m0(sprintf('eL%i', i), sprintf('u%i', j))*Khac(i,i), freqs, 'Hz'))), 'color', [0, 0, 0, 0.2]);
end
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Loop Gain'); set(gca, 'XTickLabel',[]);
ylim([1e-4, 1e4]);
ax2 = nexttile;
hold on;
for i = 1:6
plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_hac_m0(sprintf('eL%i', i), sprintf('u%i', i))*Khac(i,i), freqs, 'Hz'))), 'color', [colors(i,:), 0.5]);
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
#+begin_src matlab
Gm_cl_m0 = feedback(Gm_hac_m0, 0.8*Khac, 'name', -1);
#+end_src
#+begin_src matlab
isstable(Gm_cl_m0)
#+end_src
**** Save Controller
#+begin_src matlab :exports none :tangle no
K_order = order(Khac(1,1));
Kz = c2d(Khac(1,1)*(1 + s/2/pi/2e3)^(9-K_order)/(1 + s/2/pi/2e3)^(9-K_order), 1e-4);
[num, den] = tfdata(Kz, 'v');
formatSpec = '%.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e\n';
fileID = fopen('/home/thomas/mnt/data_id31/nass/controllers/K_hac_m0.dat', 'w');
fprintf(fileID, formatSpec, [num; den]');
fclose(fileID);
#+end_src
**** Experimental Validation
#+begin_src matlab
data_1rpm = load(sprintf('%s/scans/2023-08-18_10-43_m0_1rpm.mat', mat_dir));
data_30rpm = load(sprintf('%s/scans/2023-08-18_10-45_m0_30rpm.mat', mat_dir));
#+end_src
#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*)
data2orgtable([rms(1e9*data_1rpm.Dy_int), rms(1e9*data_1rpm.Dz_int), rms(1e6*data_1rpm.Ry_int); rms(1e9*data_30rpm.Dy_int), rms(1e9*data_30rpm.Dz_int), rms(1e6*data_30rpm.Ry_int)], {'1rpm', '30rpm'}, {'Dy [nm]', 'Dz [nm]', 'Ry [urad]'}, ' %.1f ');
#+end_src
#+RESULTS:
| | Dy [nm] | Dz [nm] | Ry [urad] |
|-------+---------+---------+-----------|
| 1rpm | 55.3 | 5.9 | 0.1 |
| 30rpm | 85.2 | 12.5 | 0.3 |
**** Closed-Loop identification
#+begin_src matlab
data_cl = load(sprintf('%s/dynamics/2023-08-18_11-03_m0_perf_hac.mat', mat_dir));
#+end_src
#+begin_src matlab :exports none
% Sampling Time [s]
Ts = 1e-4;
% Hannning Windows
Nfft = floor(1.0/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
#+end_src
#+begin_src matlab :exports none
% And we get the frequency vector
[S_dx, f] = tfestimate(data_cl.Dx.id_cl, data_cl.Dx.e_dx, win, Noverlap, Nfft, 1/Ts);
[S_dy, ~] = tfestimate(data_cl.Dy.id_cl, data_cl.Dy.e_dy, win, Noverlap, Nfft, 1/Ts);
[S_dz, ~] = tfestimate(data_cl.Dz.id_cl, data_cl.Dz.e_dz, win, Noverlap, Nfft, 1/Ts);
[S_rx, ~] = tfestimate(data_cl.Rx.id_cl, data_cl.Rx.e_rx, win, Noverlap, Nfft, 1/Ts);
[S_ry, ~] = tfestimate(data_cl.Ry.id_cl, data_cl.Ry.e_ry, win, Noverlap, Nfft, 1/Ts);
[S_rz, ~] = tfestimate(data_cl.Rz.id_cl, data_cl.Rz.e_rz, win, Noverlap, Nfft, 1/Ts);
#+end_src
#+begin_src matlab :exports none :results none
%% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor
figure;
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile();
hold on;
plot(f, abs(S_dy), 'DisplayName', '$D_y$');
plot(f, abs(S_dz), 'DisplayName', '$D_z$');
plot(f, abs(S_ry), 'DisplayName', '$R_y$');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('Amplitude');
ylim([1e-3, 1e1]);
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 3);
xlim([1, 1e3]);
#+end_src
*** Mass 1
**** Load Plant
#+begin_src matlab
load('G_hac.mat')
load('Gm.mat')
#+end_src
**** Plant
#+begin_src matlab :exports none :results none
%% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
for i = 1:5
for j = i+1:6
plot(f, abs(G_hac_m1(:, i, j)), 'color', [0, 0, 0, 0.2], ...
'HandleVisibility', 'off');
end
end
for i = 1:6
plot(f, abs(G_hac_m1(:,i, i)), 'color', colors(i,:), ...
'DisplayName', sprintf('$\\tau_{m,%i}/u_%i$', i, i));
end
plot(f, abs(G_hac_m1(:, 1, 2)), 'color', [0, 0, 0, 0.2], ...
'DisplayName', '$\tau_{m,i}/u_j$');
plot(freqs, abs(squeeze(freqresp(Gm_hac_m1('eL1', 'u1'), freqs, 'Hz'))), 'k--', ...
'DisplayName', '$\tau_{m,i}/u_i$ - Model');
plot(freqs, abs(squeeze(freqresp(Gm_hac_m1('eL2', 'u1'), freqs, 'Hz'))), 'color', [colors(1,:), 0.2], ...
'DisplayName', '$\tau_{m,i}/u_j$ - Model');
for i = 1:5
for j = i+1:6
plot(freqs, abs(squeeze(freqresp(Gm_hac_m1(sprintf('eL%i', i), sprintf('u%i', j)), freqs, 'Hz'))), 'color', [colors(1,:), 0.2], ...
'HandleVisibility', 'off');
end
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Amplitude [m/V]'); set(gca, 'XTickLabel',[]);
ylim([1e-8, 1e-3]);
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 3);
ax2 = nexttile;
hold on;
for i =1:6
plot(f, 180/pi*angle(G_hac_m1(:,i, i)));
end
plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_hac_m1('eL1', 'u1'), freqs, 'Hz'))), 'k--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
**** Plant Inverse
#+begin_src matlab
Gm_hac_red_m1 = flipRphZeros(-balred(Gm_hac_m1('eL1', 'u1'), 6, ...
balredOptions('StateProjection', 'MatchDC', ...
'FreqIntervals', [0, 80])));
#+end_src
#+begin_src matlab :exports none :results none
%% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(freqs, abs(squeeze(freqresp(Gm_hac_m1('eL1', 'u1'), freqs, 'Hz'))));
plot(freqs, abs(squeeze(freqresp(Gm_hac_red_m1, freqs, 'Hz'))));
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Amplitude [m/V]'); set(gca, 'XTickLabel',[]);
ylim([1e-8, 1e-3]);
ax2 = nexttile;
hold on;
plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_hac_m1('eL1', 'u1'), freqs, 'Hz'))));
plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_hac_red_m1, freqs, 'Hz'))));
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
#+begin_src matlab
%% Plant Inverse
Gm_hac_inv_m1 = inv(Gm_hac_red_m1);
#+end_src
#+begin_src matlab :exports none :results none
%% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(freqs, abs(squeeze(freqresp(inv(Gm_hac_m1('eL1', 'u1')), freqs, 'Hz'))));
plot(freqs, abs(squeeze(freqresp(Gm_hac_inv_m1, freqs, 'Hz'))));
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Amplitude [m/V]'); set(gca, 'XTickLabel',[]);
ax2 = nexttile;
hold on;
plot(freqs, 180/pi*angle(squeeze(freqresp(inv(Gm_hac_m1('eL1', 'u1')), freqs, 'Hz'))));
plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_hac_inv_m1, freqs, 'Hz'))));
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
**** Controller design
#+begin_src matlab
%% Wanted crossover
wc = 2*pi*30;
%% Double Integrator
H_int = 1/(s + 0.1*2*pi) * ...
(50*2*pi + s)/(0.01*2*pi + s);
H_int = H_int/abs(evalfr(H_int, 1j*wc));
% H_int = wc/s;
%% Lead to increase phase margin
a = 2; % Amount of phase lead / width of the phase lead / high frequency gain
H_lead = 1/sqrt(a)*(1 + s/(wc/sqrt(a)))/(1 + s/(wc*sqrt(a)));
a = 2; % Amount of phase lead / width of the phase lead / high frequency gain
H_lead = H_lead*1/sqrt(a)*(1 + s/(wc/sqrt(a)))/(1 + s/(wc*sqrt(a)));
%% Low Pass filter to increase robustness
H_lpf = 1/(1 + s/2/pi/200);
%% Notch at the top-plate resonance
% gm = 0.02;
% xi = 0.3;
% wn = 2*pi*665;
% H_notch = (s^2 + 2*gm*xi*wn*s + wn^2)/(s^2 + 2*xi*wn*s + wn^2);
%% Gain to have unitary crossover at 30Hz
[~, i_f] = min(abs(f - wc/2/pi));
H_gain = 1./abs(G_hac_m0(i_f, 1, 1));
%% Decentralized HAC
Khac = 0.8*H_gain * ... % Gain
H_int * ... % Integrator
H_lpf * ... % Low Pass Filter
H_lead * ... % Integrator
eye(6); % 6x6 Diagonal
Khac.InputName = {'eL1', 'eL2', 'eL3', 'eL4', 'eL5', 'eL6'};
Khac.OutputName = {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'};
#+end_src
Loop gain
#+begin_src matlab :exports none
%% Loop Gain
figure;
tiledlayout(3, 1, 'TileSpacing', 'Compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
for i = 1:6
plot(f, abs(G_hac_m0(:,i,i).*squeeze(freqresp(Khac(i,i), f, 'Hz'))), 'color', [colors(i,:), 0.5]);
end
for i = 1:5
for j = i+1:6
plot(f, abs(G_hac_m0(:,i,j).*squeeze(freqresp(Khac(i,i), f, 'Hz'))), 'color', [0, 0, 0, 0.2]);
end
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Loop Gain'); set(gca, 'XTickLabel',[]);
ylim([1e-4, 1e4]);
ax2 = nexttile;
hold on;
for i = 1:6
plot(f, 180/pi*angle(G_hac_m0(:,i,i).*squeeze(freqresp(Khac(i,i), f, 'Hz'))), 'color', [colors(i,:), 0.5]);
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
**** Verify Stability
#+begin_src matlab :exports none
%% Compute the Eigenvalues of the loop gain
Ldet = zeros(6, length(f));
% Loop gain
Lmimo = pagemtimes(permute(G_hac_m0, [2,3,1]),squeeze(freqresp(Khac, f, 'Hz')));
for i_f = 2:length(f)
Ldet(:, i_f) = eig(squeeze(Lmimo(:,:,i_f)));
end
#+end_src
#+begin_src matlab :exports none
%% Plot of the eigenvalues of L in the complex plane
figure;
hold on;
plot(real(squeeze(Ldet(1,:))), imag(squeeze(Ldet(1,:))), 'k.');
plot(real(squeeze(Ldet(1,:))), -imag(squeeze(Ldet(1,:))), 'k.');
for i = 1:6
plot(real(squeeze(Ldet(i,:))), imag(squeeze(Ldet(i,:))), 'k.');
plot(real(squeeze(Ldet(i,:))), -imag(squeeze(Ldet(i,:))), 'k.');
end
plot(-1, 0, 'kx', 'HandleVisibility', 'off');
hold off;
set(gca, 'XScale', 'lin'); set(gca, 'YScale', 'lin');
xlabel('Real'); ylabel('Imag');
xlim([-3, 1]); ylim([-2, 2]);
#+end_src
**** Estimated performances
Loop gain with model
#+begin_src matlab :exports none
%% Loop Gain
figure;
tiledlayout(3, 1, 'TileSpacing', 'Compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
for i = 1:6
plot(freqs, abs(squeeze(freqresp(Gm_hac_m0(sprintf('eL%i', i), sprintf('u%i', i))*Khac(i,i), freqs, 'Hz'))), 'color', [colors(i,:), 0.5]);
end
for i = 1:5
for j = i+1:6
plot(freqs, abs(squeeze(freqresp(Gm_hac_m0(sprintf('eL%i', i), sprintf('u%i', j))*Khac(i,i), freqs, 'Hz'))), 'color', [0, 0, 0, 0.2]);
end
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Loop Gain'); set(gca, 'XTickLabel',[]);
ylim([1e-4, 1e4]);
ax2 = nexttile;
hold on;
for i = 1:6
plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_hac_m0(sprintf('eL%i', i), sprintf('u%i', i))*Khac(i,i), freqs, 'Hz'))), 'color', [colors(i,:), 0.5]);
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
#+begin_src matlab
Gm_cl_m0 = feedback(Gm_hac_m0, Khac, 'name', +1);
#+end_src
#+begin_src matlab
isstable(Gm_hac_m0)
#+end_src
**** Save Controller
#+begin_src matlab :exports none :tangle no
K_order = order(Khac(1,1));
Kz = c2d(Khac(1,1)*(1 + s/2/pi/2e3)^(9-K_order)/(1 + s/2/pi/2e3)^(9-K_order), 1e-4);
[num, den] = tfdata(Kz, 'v');
formatSpec = '%.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e\n';
fileID = fopen('/home/thomas/mnt/data_id31/nass/controllers/K_hac_m0.dat', 'w');
fprintf(fileID, formatSpec, [num; den]');
fclose(fileID);
#+end_src
** Tomography - Performances
*** First scan with closed-loop at middle
#+begin_src matlab
data = load(sprintf('%s/scans/2023-08-17_15-22_tomography_30rpm_m0_robust.mat', mat_dir));
#+end_src
#+begin_src matlab :exports none :results none
writerObj = VideoWriter('test2.avi'); %// initialize the VideoWriter object
open(writerObj) ;
figure;
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile();
di = 500;
hold on;
for i = 1:floor(length(data.Dx_int)/di)-1
if data.hac_status(di*(i+1)-1) == 0
% Only open loop
plot(1e6*data.Dx_int(di*i:di*(i+1)-1), 1e6*data.Dy_int(di*i:di*(i+1)-1), '-', 'color', colors(1,:))
elseif data.hac_status(di*i) == 1
% Only closed loop
plot(1e6*data.Dx_int(di*i:di*(i+1)-1), 1e6*data.Dy_int(di*i:di*(i+1)-1), '-', 'color', colors(2,:))
else
% Both open and closed loop
Dx_int = data.Dx_int(di*i:di*(i+1)-1);
Dy_int = data.Dy_int(di*i:di*(i+1)-1);
plot(1e6*Dx_int(data.hac_status(di*i:di*(i+1)-1) == 0), 1e6*Dy_int(data.hac_status(di*i:di*(i+1)-1) == 0), '-', 'color', colors(1,:))
plot(1e6*Dx_int(data.hac_status(di*i:di*(i+1)-1) == 1), 1e6*Dy_int(data.hac_status(di*i:di*(i+1)-1) == 1), '-', 'color', colors(2,:))
end
axis square
xlim([-3, 3])
ylim([-3, 3])
xlabel("X motion [$\mu m$]");
ylabel("Y motion [$\mu m$]");
F = getframe; %// Capture the frame
writeVideo(writerObj,F) %// add the frame to the movie
end
close(writerObj);
#+end_src
#+begin_src matlab :exports none :results none
%% description
figure;
tiledlayout(1, 2, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile;
hold on;
plot(1e6*data.Dx_int(data.hac_status == 0), 1e6*data.Dy_int(data.hac_status == 0), ...
'DisplayName', 'OL')
plot(1e6*data.Dx_int(data.hac_status == 1), 1e6*data.Dy_int(data.hac_status == 1), ...
'DisplayName', 'CL')
% Get first time where closed-loop ON
[~, i] = find(data.hac_status == 1);
plot(1e6*data.Dx_int(i+50000:end), 1e6*data.Dy_int(i+50000:end), ...
'DisplayName', 'CL, stabilized')
hold off;
axis equal
xlim([-3, 3])
ylim([-3, 3])
xticks([-3:1:3])
yticks([-3:1:3])
xlabel("X motion [$\mu m$]");
ylabel("Y motion [$\mu m$]");
ax2 = nexttile;
hold on;
plot(1e6*data.Dx_int(data.hac_status == 0), 1e6*data.Dy_int(data.hac_status == 0), ...
'DisplayName', 'OL')
plot(1e6*data.Dx_int(data.hac_status == 1), 1e6*data.Dy_int(data.hac_status == 1), ...
'DisplayName', 'CL')
% Get first time where closed-loop ON
[~, i] = find(data.hac_status == 1);
plot(1e6*data.Dx_int(i+50000:end), 1e6*data.Dy_int(i+50000:end), ...
'DisplayName', 'CL, stabilized')
legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1);
hold off;
axis equal
xlabel("X motion [$\mu m$]");
ylabel("Y motion [$\mu m$]");
xlim([-0.3, 0.3])
ylim([-0.3, 0.3])
xticks([-0.3:0.1:0.3])
yticks([-0.3:0.1:0.3])
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_tomography_ol_cl_robust_hac_m0.pdf', 'width', 'full', 'height', 'normal');
#+end_src
#+name: fig:id31_tomography_ol_cl_robust_hac_m0
#+caption: description
#+RESULTS:
[[file:figs/id31_tomography_ol_cl_robust_hac_m0.png]]
*** Slow Rotation - 6RPM
#+begin_src matlab
%% Load measured noise
data_ol = load(sprintf('%s/freq_analysis/2023-08-11_17-18_m0_lac_off_1rpm.mat', mat_dir));
data_lac = load(sprintf('%s/freq_analysis/2023-08-11_17-16_m0_lac_on_1rpm.mat', mat_dir));
data_hac = load(sprintf('%s/freq_analysis/2023-08-11_17-14_m0_hac_on_1rpm.mat', mat_dir));
#+end_src
#+begin_src matlab
%% Coordinate transform
J_int_to_X = [ 0 0 -0.787401574803149 -0.212598425196851 0;
0.78740157480315 0.21259842519685 0 0 0;
0 0 0 0 -1;
-13.1233595800525 13.1233595800525 0 0 0;
0 0 -13.1233595800525 13.1233595800525 0];
a = J_int_to_X*[data_ol.d1; data_ol.d2; data_ol.d3; data_ol.d4; data_ol.d5];
data_ol.Dx_int = a(1,:);
data_ol.Dy_int = a(2,:);
data_ol.Dz_int = a(3,:);
data_ol.Rx_int = a(4,:);
data_ol.Ry_int = a(5,:);
a = J_int_to_X*[data_lac.d1; data_lac.d2; data_lac.d3; data_lac.d4; data_lac.d5];
data_lac.Dx_int = a(1,:);
data_lac.Dy_int = a(2,:);
data_lac.Dz_int = a(3,:);
data_lac.Rx_int = a(4,:);
data_lac.Ry_int = a(5,:);
a = J_int_to_X*[data_hac.d1; data_hac.d2; data_hac.d3; data_hac.d4; data_hac.d5];
data_hac.Dx_int = a(1,:);
data_hac.Dy_int = a(2,:);
data_hac.Dz_int = a(3,:);
data_hac.Rx_int = a(4,:);
data_hac.Ry_int = a(5,:);
#+end_src
#+begin_src matlab :exports none
% Hannning Windows
Nfft = floor(30.0/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
[data_ol.pxx_Dx, data_ol.f] = pwelch(detrend(data_ol.Dx_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_ol.pxx_Dy, ~ ] = pwelch(detrend(data_ol.Dy_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_ol.pxx_Dz, ~ ] = pwelch(detrend(data_ol.Dz_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_ol.pxx_Rx, ~ ] = pwelch(detrend(data_ol.Rx_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_ol.pxx_Ry, ~ ] = pwelch(detrend(data_ol.Ry_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_lac.pxx_Dx, data_lac.f] = pwelch(detrend(data_lac.Dx_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_lac.pxx_Dy, ~ ] = pwelch(detrend(data_lac.Dy_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_lac.pxx_Dz, ~ ] = pwelch(detrend(data_lac.Dz_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_lac.pxx_Rx, ~ ] = pwelch(detrend(data_lac.Rx_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_lac.pxx_Ry, ~ ] = pwelch(detrend(data_lac.Ry_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_hac.pxx_Dx, data_hac.f] = pwelch(detrend(data_hac.Dx_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_hac.pxx_Dy, ~ ] = pwelch(detrend(data_hac.Dy_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_hac.pxx_Dz, ~ ] = pwelch(detrend(data_hac.Dz_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_hac.pxx_Rx, ~ ] = pwelch(detrend(data_hac.Rx_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_hac.pxx_Ry, ~ ] = pwelch(detrend(data_hac.Ry_int, 0), win, Noverlap, Nfft, 1/Ts);
#+end_src
#+begin_src matlab :exports none :results none
%% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor
figure;
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile();
hold on;
plot(data_ol.f , sqrt(data_ol.pxx_Dy ), 'DisplayName', '$D_y$ - OL');
plot(data_lac.f, sqrt(data_lac.pxx_Dy), 'DisplayName', '$D_y$ - LAC');
plot(data_hac.f, sqrt(data_hac.pxx_Dy), 'DisplayName', '$D_y$ - HAC-LAC');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('Amplitude [$V/\sqrt{Hz}$]');
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1);
xlim([1, 5e3]);
#+end_src
#+begin_src matlab :exports none :results none
%% Cumulative Amplitude Spectrum of the measured Dx and Dy motion
figure;
hold on;
plot(data_ol.f, sqrt(flip(-cumtrapz(flip(data_ol.f), flip(data_ol.pxx_Dy)))), 'DisplayName', '$D_y$ - OL');
plot(data_lac.f, sqrt(flip(-cumtrapz(flip(data_lac.f), flip(data_lac.pxx_Dy)))), 'DisplayName', '$D_y$ - LAC');
plot(data_hac.f, sqrt(flip(-cumtrapz(flip(data_hac.f), flip(data_hac.pxx_Dy)))), 'DisplayName', '$D_y$ - HAC-LAC');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('CAS [$m$]');
legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1);
% xlim([0.1, 5e3]); ylim([1e-10, 1e-7]);
#+end_src
*** Rapid Rotation - 30RPM
#+begin_src matlab
%% Load measured noise
data_ol = load(sprintf('%s/freq_analysis/2023-08-11_17-39_m0_lac_off_30rpm.mat', mat_dir));
data_lac = load(sprintf('%s/freq_analysis/2023-08-11_17-36_m0_lac_on_30rpm.mat', mat_dir));
data_hac = load(sprintf('%s/freq_analysis/2023-08-11_17-34_m0_hac_on_30rpm.mat', mat_dir));
#+end_src
#+begin_src matlab
%% Coordinate transform
J_int_to_X = [ 0 0 -0.787401574803149 -0.212598425196851 0;
0.78740157480315 0.21259842519685 0 0 0;
0 0 0 0 -1;
-13.1233595800525 13.1233595800525 0 0 0;
0 0 -13.1233595800525 13.1233595800525 0];
a = J_int_to_X*[data_ol.d1; data_ol.d2; data_ol.d3; data_ol.d4; data_ol.d5];
data_ol.Dx_int = a(1,:);
data_ol.Dy_int = a(2,:);
data_ol.Dz_int = a(3,:);
data_ol.Rx_int = a(4,:);
data_ol.Ry_int = a(5,:);
a = J_int_to_X*[data_lac.d1; data_lac.d2; data_lac.d3; data_lac.d4; data_lac.d5];
data_lac.Dx_int = a(1,:);
data_lac.Dy_int = a(2,:);
data_lac.Dz_int = a(3,:);
data_lac.Rx_int = a(4,:);
data_lac.Ry_int = a(5,:);
a = J_int_to_X*[data_hac.d1; data_hac.d2; data_hac.d3; data_hac.d4; data_hac.d5];
data_hac.Dx_int = a(1,:);
data_hac.Dy_int = a(2,:);
data_hac.Dz_int = a(3,:);
data_hac.Rx_int = a(4,:);
data_hac.Ry_int = a(5,:);
#+end_src
#+begin_src matlab :exports none
% Hannning Windows
Nfft = floor(20.0/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
[data_ol.pxx_Dx, data_ol.f] = pwelch(detrend(data_ol.Dx_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_ol.pxx_Dy, ~ ] = pwelch(detrend(data_ol.Dy_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_ol.pxx_Dz, ~ ] = pwelch(detrend(data_ol.Dz_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_ol.pxx_Rx, ~ ] = pwelch(detrend(data_ol.Rx_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_ol.pxx_Ry, ~ ] = pwelch(detrend(data_ol.Ry_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_lac.pxx_Dx, data_lac.f] = pwelch(detrend(data_lac.Dx_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_lac.pxx_Dy, ~ ] = pwelch(detrend(data_lac.Dy_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_lac.pxx_Dz, ~ ] = pwelch(detrend(data_lac.Dz_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_lac.pxx_Rx, ~ ] = pwelch(detrend(data_lac.Rx_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_lac.pxx_Ry, ~ ] = pwelch(detrend(data_lac.Ry_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_hac.pxx_Dx, data_hac.f] = pwelch(detrend(data_hac.Dx_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_hac.pxx_Dy, ~ ] = pwelch(detrend(data_hac.Dy_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_hac.pxx_Dz, ~ ] = pwelch(detrend(data_hac.Dz_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_hac.pxx_Rx, ~ ] = pwelch(detrend(data_hac.Rx_int, 0), win, Noverlap, Nfft, 1/Ts);
[data_hac.pxx_Ry, ~ ] = pwelch(detrend(data_hac.Ry_int, 0), win, Noverlap, Nfft, 1/Ts);
#+end_src
#+begin_src matlab :exports none :results none
%% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor
figure;
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile();
hold on;
plot(data_ol.f , sqrt(data_ol.pxx_Dy ), 'DisplayName', '$D_y$ - OL');
plot(data_lac.f, sqrt(data_lac.pxx_Dy), 'DisplayName', '$D_y$ - LAC');
plot(data_hac.f, sqrt(data_hac.pxx_Dy), 'DisplayName', '$D_y$ - HAC-LAC');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('Amplitude [$V/\sqrt{Hz}$]');
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1);
xlim([1, 5e3]);
#+end_src
#+begin_src matlab :exports none :results none
%% Cumulative Amplitude Spectrum of the measured Dx and Dy motion
figure;
tiledlayout(1, 3, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile();
hold on;
plot(data_ol.f, sqrt(flip(-cumtrapz(flip(data_ol.f), flip(data_ol.pxx_Dy)))), ...
'DisplayName', sprintf('OL: $%.1f \\mu m$ RMS', 1e6*rms(detrend(data_ol.Dy_int, 0))));
plot(data_lac.f, sqrt(flip(-cumtrapz(flip(data_lac.f), flip(data_lac.pxx_Dy)))), ...
'DisplayName', sprintf('LAC: $%.1f \\mu m$ RMS', 1e6*rms(detrend(data_lac.Dy_int, 0))));
plot(data_hac.f, sqrt(flip(-cumtrapz(flip(data_hac.f), flip(data_hac.pxx_Dy)))), ...
'DisplayName', sprintf('HAC: $%.0f nm$ RMS', 1e9*rms(detrend(data_hac.Dy_int, 0))));
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('CAS [m rms, rad RMS]');
legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1);
xticks([1e0, 1e1, 1e2]);
title('$D_y$')
ax2 = nexttile();
hold on;
plot(data_ol.f, sqrt(flip(-cumtrapz(flip(data_ol.f), flip(data_ol.pxx_Dz)))), ...
'DisplayName', sprintf('OL: $%.0f nm$ RMS', 1e9*rms(detrend(data_ol.Dz_int, 0))));
plot(data_lac.f, sqrt(flip(-cumtrapz(flip(data_lac.f), flip(data_lac.pxx_Dz)))), ...
'DisplayName', sprintf('LAC: $%.0f nm$ RMS', 1e9*rms(detrend(data_lac.Dz_int, 0))));
plot(data_hac.f, sqrt(flip(-cumtrapz(flip(data_hac.f), flip(data_hac.pxx_Dz)))), ...
'DisplayName', sprintf('HAC: $%.0f nm$ RMS', 1e9*rms(detrend(data_hac.Dz_int, 0))));
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1);
xticks([1e0, 1e1, 1e2]);
title('$D_z$')
ax3 = nexttile();
hold on;
plot(data_ol.f, sqrt(flip(-cumtrapz(flip(data_ol.f), flip(data_ol.pxx_Ry)))), ...
'DisplayName', sprintf('OL: $%.1f \\mu$radRMS', 1e6*rms(detrend(data_ol.Ry_int, 0))));
plot(data_lac.f, sqrt(flip(-cumtrapz(flip(data_lac.f), flip(data_lac.pxx_Ry)))), ...
'DisplayName', sprintf('LAC: $%.1f \\mu$radRMS', 1e6*rms(detrend(data_lac.Ry_int, 0))));
plot(data_hac.f, sqrt(flip(-cumtrapz(flip(data_hac.f), flip(data_hac.pxx_Ry)))), ...
'DisplayName', sprintf('HAC: $%.1f \\mu$radRMS', 1e6*rms(detrend(data_hac.Ry_int, 0))));
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1);
xticks([1e0, 1e1, 1e2]);
title('$R_y$')
linkaxes([ax1,ax2,ax3],'xy');
xlim([0.1, 5e2]); ylim([1e-10, 3e-5]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_cas_m0_30rpm_ol_lac_hac.pdf', 'width', 'full', 'height', 'tall');
#+end_src
#+name: fig:id31_cas_m0_30rpm_ol_lac_hac
#+caption: Cumulative Amplitude Spectrum of the errors in $D_y$, $D_z$ and $R_y$ during a tomography scan at 30RPM. Three control configuration are compared: Open-Loop, Low Authority Control, and High Authority Control
#+RESULTS:
[[file:figs/id31_cas_m0_30rpm_ol_lac_hac.png]]
* 6DoF Control in Cartesian plane (rotating with the nano-hexapod)
<<sec:id31_cart_hac_rot>>
** 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 :tangle no :noweb yes
<<m-init-path>>
#+end_src
#+begin_src matlab :eval no :noweb yes
<<m-init-path-tangle>>
#+end_src
#+begin_src matlab :noweb yes
<<m-init-other>>
#+end_src
** Introduction :ignore:
As only Dy, Dz and Ry directions are important, we could only control them.
This lead to a 3x3 plant that may be more decoupled than the 6x6 plant.
** 5x5 plant in Cartesian plane
#+begin_src matlab :exports none
%% Load model
load('Gm.mat')
#+end_src
#+begin_src matlab
%% Jacobian for 3x3 plant
n_hexapod = initializeNanoHexapod('flex_bot_type', '2dof', ...
'flex_top_type', '3dof', ...
'motion_sensor_type', 'plates', ...
'actuator_type', '2dof');
Jt_inv = pinv(n_hexapod.geometry.J');
Jt_inv = Jt_inv(:,[1,2,3,4,5]);
J_int_to_X = [ 0 0 -0.787401574803149 -0.212598425196851 0;
0.78740157480315 0.21259842519685 0 0 0;
0 0 0 0 -1;
-13.1233595800525 13.1233595800525 0 0 0;
0 0 -13.1233595800525 13.1233595800525 0];
#+end_src
Compute identified plant in the Cartesian plane:
#+begin_src matlab
%% Load Data
% data_m0 = load(sprintf('%s/dynamics/2023-08-17_10-44_lac_plant_m0_Wz0_interf.mat', mat_dir));
%% New data after calibrating the Rz-offset
data_m0 = load(sprintf('%s/dynamics/2023-08-17_16-23_lac_plant_m0_Wz0_interf_Rz_align.mat', mat_dir));
#+end_src
#+begin_src matlab :exports none
% Sampling Time [s]
Ts = 1e-4;
% Hannning Windows
Nfft = floor(1.0/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
#+end_src
#+begin_src matlab :exports none
% And we get the frequency vector
[~, f] = tfestimate(data_m0.uL1.id_plant, data_m0.uL1.d1, win, Noverlap, Nfft, 1/Ts);
#+end_src
#+begin_src matlab :exports none
%% HAC Cartesian Plant
G_hac_m0 = zeros(length(f), 5, 6);
for i_strut = 1:6
Di = [data_m0.(sprintf("uL%i", i_strut)).d1 ; data_m0.(sprintf("uL%i", i_strut)).d2 ; data_m0.(sprintf("uL%i", i_strut)).d3 ; data_m0.(sprintf("uL%i", i_strut)).d4 ; data_m0.(sprintf("uL%i", i_strut)).d5]';
G_hac_m0(:,:,i_strut) = tfestimate(data_m0.(sprintf("uL%i", i_strut)).id_plant, detrend(Di, 0), win, Noverlap, Nfft, 1/Ts);
end
G_cart = permute(pagemtimes(J_int_to_X, pagemtimes(permute(G_hac_m0, [2,3,1]), Jt_inv)), [3,1,2]);
#+end_src
Compute plant model in the Cartesian plane:
#+begin_src matlab
Gm_cart = J_int_to_X*Gm_hac_m0({'d1', 'd2', 'd3', 'd4', 'd5'}, {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'})*Jt_inv;
Gm_cart.InputName = {'Fx', 'Fy', 'Fz', 'Mx', 'My'};
Gm_cart.OutputName = {'Dx', 'Dy', 'Dz', 'Rx', 'Ry'};
#+end_src
#+begin_src matlab :exports none :results none
%% 5x5 plant
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_cart(:,1,1)), 'DisplayName', '$D_x$');
plot(f, abs(G_cart(:,2,2)), 'DisplayName', '$D_y$');
plot(f, abs(G_cart(:,3,3)), 'DisplayName', '$D_z$');
plot(f, abs(G_cart(:,4,4)), 'DisplayName', '$R_x$');
plot(f, abs(G_cart(:,5,5)), 'DisplayName', '$R_y$');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude');
legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
% ylim([1e-4, 1e1]);
ax2 = nexttile();
hold on;
plot(f, 180/pi*angle(G_cart(:,1,1)));
plot(f, 180/pi*angle(G_cart(:,2,2)));
plot(f, 180/pi*angle(G_cart(:,3,3)));
plot(f, 180/pi*angle(G_cart(:,4,4)));
plot(f, 180/pi*angle(G_cart(:,5,5)));
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
** Controller Design
#+begin_src matlab
%% Wanted crossover
wc = 2*pi*30;
%% Double Integrator
H_int = 1/(s + 1*2*pi) * ...
1/(s + 0.01*2*pi);
H_int = H_int/abs(evalfr(H_int, 1j*wc));
%% Lead to increase phase margin
a = 2; % Amount of phase lead / width of the phase lead / high frequency gain
H_lead = 1/sqrt(a)*(1 + s/(wc/sqrt(a)))/(1 + s/(wc*sqrt(a)));
a = 2; % Amount of phase lead / width of the phase lead / high frequency gain
H_lead = H_lead*1/sqrt(a)*(1 + s/(1.5*wc/sqrt(a)))/(1 + s/(1.5*wc*sqrt(a)));
%% Low Pass filter to increase robustness
H_lpf = 1/(1 + s/2/pi/300);
%% Notch at the top-plate resonance
% gm = 0.02;
% xi = 0.3;
% wn = 2*pi*665;
% H_notch = (s^2 + 2*gm*xi*wn*s + wn^2)/(s^2 + 2*xi*wn*s + wn^2);
%% Gain to have unitary crossover at 30Hz
[~, i_f] = min(abs(f - wc/2/pi));
H_gain = -1./abs(G_cart(i_f,1,1));
%% Decentralized HAC
Khac_Dx = H_gain * ... % Gain
H_int * ... % Integrator
H_lpf * ... % Low Pass Filter
H_lead; % Integrator
#+end_src
#+begin_src matlab
%% Wanted crossover
wc = 2*pi*30;
%% Double Integrator
H_int = 1/(s + 1*2*pi) * ...
1/(s + 0.01*2*pi);
H_int = H_int/abs(evalfr(H_int, 1j*wc));
%% Lead to increase phase margin
a = 2; % Amount of phase lead / width of the phase lead / high frequency gain
H_lead = 1/sqrt(a)*(1 + s/(wc/sqrt(a)))/(1 + s/(wc*sqrt(a)));
a = 2; % Amount of phase lead / width of the phase lead / high frequency gain
H_lead = H_lead*1/sqrt(a)*(1 + s/(1.5*wc/sqrt(a)))/(1 + s/(1.5*wc*sqrt(a)));
%% Low Pass filter to increase robustness
H_lpf = 1/(1 + s/2/pi/300);
%% Notch at the top-plate resonance
% gm = 0.02;
% xi = 0.3;
% wn = 2*pi*665;
% H_notch = (s^2 + 2*gm*xi*wn*s + wn^2)/(s^2 + 2*xi*wn*s + wn^2);
%% Gain to have unitary crossover at 30Hz
[~, i_f] = min(abs(f - wc/2/pi));
H_gain = -1./abs(G_cart(i_f,2,2));
%% Decentralized HAC
Khac_Dy = H_gain * ... % Gain
H_int * ... % Integrator
H_lpf * ... % Low Pass Filter
H_lead; % Integrator
#+end_src
#+begin_src matlab
%% Wanted crossover
wc = 2*pi*40;
%% Double Integrator
H_int = 1/(s + 2*2*pi) * ...
1/(s + 0.01*2*pi);
H_int = H_int/abs(evalfr(H_int, 1j*wc));
%% Lead to increase phase margin
a = 2; % Amount of phase lead / width of the phase lead / high frequency gain
H_lead = 1/sqrt(a)*(1 + s/(wc/sqrt(a)))/(1 + s/(wc*sqrt(a)));
a = 2; % Amount of phase lead / width of the phase lead / high frequency gain
H_lead = H_lead*1/sqrt(a)*(1 + s/(1.5*wc/sqrt(a)))/(1 + s/(1.5*wc*sqrt(a)));
%% Low Pass filter to increase robustness
H_lpf = 1/(1 + s/2/pi/300);
%% Notch at the top-plate resonance
% gm = 0.02;
% xi = 0.3;
% wn = 2*pi*665;
% H_notch = (s^2 + 2*gm*xi*wn*s + wn^2)/(s^2 + 2*xi*wn*s + wn^2);
%% Gain to have unitary crossover at 30Hz
[~, i_f] = min(abs(f - wc/2/pi));
H_gain = -1./abs(G_cart(i_f,3,3));
%% Decentralized HAC
Khac_Dz = H_gain * ... % Gain
H_int * ... % Integrator
H_lpf * ... % Low Pass Filter
H_lead; % Integrator
#+end_src
#+begin_src matlab
%% Wanted crossover
wc = 2*pi*10;
%% Double Integrator
H_int = 1/(s + 1.5*2*pi) * ...
1/(s + 0.01*2*pi);
H_int = H_int/abs(evalfr(H_int, 1j*wc));
%% Lead to increase phase margin
a = 2; % Amount of phase lead / width of the phase lead / high frequency gain
H_lead = 1/sqrt(a)*(1 + s/(wc/sqrt(a)))/(1 + s/(wc*sqrt(a)));
%% Low Pass filter to increase robustness
H_lpf = 1/(1 + s/2/pi/300);
%% Notch at the top-plate resonance
% gm = 0.02;
% xi = 0.3;
% wn = 2*pi*665;
% H_notch = (s^2 + 2*gm*xi*wn*s + wn^2)/(s^2 + 2*xi*wn*s + wn^2);
%% Gain to have unitary crossover at 30Hz
[~, i_f] = min(abs(f - wc/2/pi));
H_gain = -1./abs(G_cart(i_f,4,4));
%% Decentralized HAC
Khac_Rx = H_gain * ... % Gain
H_int * ... % Integrator
H_lpf * ... % Low Pass Filter
H_lead; % Integrator
#+end_src
#+begin_src matlab
%% Wanted crossover
wc = 2*pi*10;
%% Double Integrator
H_int = 1/(s + 1.5*2*pi) * ...
1/(s + 0.01*2*pi);
H_int = H_int/abs(evalfr(H_int, 1j*wc));
%% Lead to increase phase margin
a = 2; % Amount of phase lead / width of the phase lead / high frequency gain
H_lead = 1/sqrt(a)*(1 + s/(wc/sqrt(a)))/(1 + s/(wc*sqrt(a)));
%% Low Pass filter to increase robustness
H_lpf = 1/(1 + s/2/pi/300);
%% Notch at the top-plate resonance
% gm = 0.02;
% xi = 0.3;
% wn = 2*pi*665;
% H_notch = (s^2 + 2*gm*xi*wn*s + wn^2)/(s^2 + 2*xi*wn*s + wn^2);
%% Gain to have unitary crossover at 30Hz
[~, i_f] = min(abs(f - wc/2/pi));
H_gain = -1./abs(G_cart(i_f,5,5));
%% Decentralized HAC
Khac_Ry = H_gain * ... % Gain
H_int * ... % Integrator
H_lpf * ... % Low Pass Filter
H_lead; % Integrator
#+end_src
#+begin_src matlab
Khac = blkdiag(Khac_Dx, Khac_Dy, Khac_Dz, Khac_Rx, Khac_Ry);
#+end_src
#+begin_src matlab :exports none
%% Loop Gain
figure;
tiledlayout(3, 1, 'TileSpacing', 'Compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_cart(:,1,1).*squeeze(freqresp(Khac(1,1), f, 'Hz'))));
plot(f, abs(G_cart(:,2,2).*squeeze(freqresp(Khac(2,2), f, 'Hz'))));
plot(f, abs(G_cart(:,3,3).*squeeze(freqresp(Khac(3,3), f, 'Hz'))));
plot(f, abs(G_cart(:,4,4).*squeeze(freqresp(Khac(4,4), f, 'Hz'))));
plot(f, abs(G_cart(:,5,5).*squeeze(freqresp(Khac(5,5), f, 'Hz'))));
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Loop Gain'); set(gca, 'XTickLabel',[]);
ylim([1e-4, 1e4]);
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(G_cart(:,1,1).*squeeze(freqresp(Khac(1,1), f, 'Hz'))));
plot(f, 180/pi*angle(G_cart(:,2,2).*squeeze(freqresp(Khac(2,2), f, 'Hz'))));
plot(f, 180/pi*angle(G_cart(:,3,3).*squeeze(freqresp(Khac(3,3), f, 'Hz'))));
plot(f, 180/pi*angle(G_cart(:,4,4).*squeeze(freqresp(Khac(4,4), f, 'Hz'))));
plot(f, 180/pi*angle(G_cart(:,5,5).*squeeze(freqresp(Khac(5,5), f, 'Hz'))));
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
** Check Stability
#+begin_src matlab :exports none
%% Compute the Eigenvalues of the loop gain
Ldet = zeros(5, length(f));
% Loop gain
Lmimo = pagemtimes(permute(G_cart, [2,3,1]),squeeze(freqresp(Khac, f, 'Hz')));
for i_f = 2:length(f)
Ldet(:, i_f) = eig(squeeze(Lmimo(:,:,i_f)));
end
#+end_src
#+begin_src matlab :exports none
%% Plot of the eigenvalues of L in the complex plane
figure;
hold on;
plot(real(squeeze(Ldet(1,:))), imag(squeeze(Ldet(1,:))), 'k.')
plot(real(squeeze(Ldet(1,:))), -imag(squeeze(Ldet(1,:))), 'k.')
for i = 1:size(Ldet,1)
plot(real(squeeze(Ldet(i,:))), imag(squeeze(Ldet(i,:))), 'k.')
plot(real(squeeze(Ldet(i,:))), -imag(squeeze(Ldet(i,:))), 'k.')
end
plot(-1, 0, 'kx');
hold off;
set(gca, 'XScale', 'lin'); set(gca, 'YScale', 'lin');
xlabel('Real'); ylabel('Imag');
xlim([-3, 1]); ylim([-2, 2]);
#+end_src
** Save controllers
#+begin_src matlab :exports none :tangle no
K_order = order(Khac(1,1));
Kz = c2d(-Khac(1,1)*(1 + s/2/pi/2e3)^(9-K_order)/(1 + s/2/pi/2e3)^(9-K_order), 1e-4);
[num, den] = tfdata(Kz, 'v');
formatSpec = '%.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e\n';
fileID = fopen('/home/thomas/mnt/data_id31/nass/controllers/K_hac_Dx.dat', 'w');
fprintf(fileID, formatSpec, [num; den]');
fclose(fileID);
#+end_src
#+begin_src matlab :exports none :tangle no
K_order = order(Khac(2,2));
Kz = c2d(-Khac(2,2)*(1 + s/2/pi/2e3)^(9-K_order)/(1 + s/2/pi/2e3)^(9-K_order), 1e-4);
[num, den] = tfdata(Kz, 'v');
formatSpec = '%.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e\n';
fileID = fopen('/home/thomas/mnt/data_id31/nass/controllers/K_hac_Dy.dat', 'w');
fprintf(fileID, formatSpec, [num; den]');
fclose(fileID);
#+end_src
#+begin_src matlab :exports none :tangle no
K_order = order(Khac(3,3));
Kz = c2d(-Khac(3,3)*(1 + s/2/pi/2e3)^(9-K_order)/(1 + s/2/pi/2e3)^(9-K_order), 1e-4);
[num, den] = tfdata(Kz, 'v');
formatSpec = '%.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e\n';
fileID = fopen('/home/thomas/mnt/data_id31/nass/controllers/K_hac_Dz.dat', 'w');
fprintf(fileID, formatSpec, [num; den]');
fclose(fileID);
#+end_src
#+begin_src matlab :exports none :tangle no
K_order = order(Khac(4,4));
Kz = c2d(-Khac(4,4)*(1 + s/2/pi/2e3)^(9-K_order)/(1 + s/2/pi/2e3)^(9-K_order), 1e-4);
[num, den] = tfdata(Kz, 'v');
formatSpec = '%.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e\n';
fileID = fopen('/home/thomas/mnt/data_id31/nass/controllers/K_hac_Rx.dat', 'w');
fprintf(fileID, formatSpec, [num; den]');
fclose(fileID);
#+end_src
#+begin_src matlab :exports none :tangle no
K_order = order(Khac(5,5));
Kz = c2d(-Khac(5,5)*(1 + s/2/pi/2e3)^(9-K_order)/(1 + s/2/pi/2e3)^(9-K_order), 1e-4);
[num, den] = tfdata(Kz, 'v');
formatSpec = '%.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e\n';
fileID = fopen('/home/thomas/mnt/data_id31/nass/controllers/K_hac_Ry.dat', 'w');
fprintf(fileID, formatSpec, [num; den]');
fclose(fileID);
#+end_src
** Performances
2023-08-18_18-33_m0_1rpm_K_cart.mat
* 3DoF Control in Cartesian plane (fixed)
<<sec:id31_cart_hac_fix>>
** 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 :tangle no :noweb yes
<<m-init-path>>
#+end_src
#+begin_src matlab :eval no :noweb yes
<<m-init-path-tangle>>
#+end_src
#+begin_src matlab :noweb yes
<<m-init-other>>
#+end_src
** Introduction :ignore:
As only Dy, Dz and Ry directions are important, we could only control them.
This lead to a 3x3 plant that may be more decoupled than the 6x6 plant.
** 3x3 plant in Cartesian plane
#+begin_src matlab :exports none
%% Load model
load('Gm.mat')
#+end_src
#+begin_src matlab
%% Jacobian for 3x3 plant
n_hexapod = initializeNanoHexapod('flex_bot_type', '2dof', ...
'flex_top_type', '3dof', ...
'motion_sensor_type', 'plates', ...
'actuator_type', '2dof');
Jt_inv = pinv(n_hexapod.geometry.J');
J_int_to_X = [0.78740157480315 0.21259842519685 0 0 0;
0 0 0 0 -1;
0 0 -13.1233595800525 13.1233595800525 0];
#+end_src
Compute identified plant in the Cartesian plane:
#+begin_src matlab
%% Load Data
% data_m0 = load(sprintf('%s/dynamics/2023-08-17_10-44_lac_plant_m0_Wz0_interf.mat', mat_dir));
%% New data after calibrating the Rz-offset
data_m0 = load(sprintf('%s/dynamics/2023-08-17_16-23_lac_plant_m0_Wz0_interf_Rz_align.mat', mat_dir));
#+end_src
#+begin_src matlab :exports none
% Sampling Time [s]
Ts = 1e-4;
% Hannning Windows
Nfft = floor(1.0/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
#+end_src
#+begin_src matlab :exports none
% And we get the frequency vector
[~, f] = tfestimate(data_m0.uL1.id_plant, data_m0.uL1.d1, win, Noverlap, Nfft, 1/Ts);
#+end_src
#+begin_src matlab :exports none
%% HAC Cartesian Plant
G_hac_m0 = zeros(length(f), 5, 6);
for i_strut = 1:6
Di = [data_m0.(sprintf("uL%i", i_strut)).d1 ; data_m0.(sprintf("uL%i", i_strut)).d2 ; data_m0.(sprintf("uL%i", i_strut)).d3 ; data_m0.(sprintf("uL%i", i_strut)).d4 ; data_m0.(sprintf("uL%i", i_strut)).d5]';
G_hac_m0(:,:,i_strut) = tfestimate(data_m0.(sprintf("uL%i", i_strut)).id_plant, detrend(Di, 0), win, Noverlap, Nfft, 1/Ts);
end
G_cart = permute(pagemtimes(J_int_to_X, pagemtimes(permute(G_hac_m0, [2,3,1]), Jt_inv(:,[2,3,5]))), [3,1,2]);
#+end_src
Compute plant model in the Cartesian plane:
#+begin_src matlab
Gm_cart = J_int_to_X*Gm_hac_m0({'d1', 'd2', 'd3', 'd4', 'd5'}, {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'})*Jt_inv(:,[2,3,5]);
Gm_cart.InputName = {'Fy', 'Fz', 'My'};
Gm_cart.OutputName = {'Dy', 'Dz', 'Ry'};
#+end_src
#+begin_important
Diagonal elements are matching quite well, but off-diagonal elements are very different.
Why so much more coupling than from the model?
- Is it due to the metrology? The spheres could induce coupling as for instance X motion will also be seen as Z motion.
This is especially true if not well centered with the sphere (as seemed to be the case for the lateral interferometers).
#+end_important
#+begin_src matlab :exports none :results none
%% 3x3 cartesian plant
figure;
tiledlayout(3, 3, 'TileSpacing', 'compact', 'Padding', 'None');
ax11 = nexttile();
hold on;
plot(f, abs(G_cart(:,1,1)));
plot(freqs, abs(squeeze(freqresp(Gm_cart('Dy', 'Fy'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('$D_y$'); set(gca, 'XTickLabel',[]);
title('$F_y$')
ax12 = nexttile();
hold on;
plot(f, abs(G_cart(:,1,2)));
plot(freqs, abs(squeeze(freqresp(Gm_cart('Dy', 'Fz'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
title('$F_z$')
ax13 = nexttile();
hold on;
plot(f, abs(G_cart(:,1,3)));
plot(freqs, abs(squeeze(freqresp(Gm_cart('Dy', 'My'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
title('$M_y$')
linkaxes([ax11,ax12,ax13],'y');
ylim([1e-8, 2e-4]);
ax21 = nexttile();
hold on;
plot(f, abs(G_cart(:,2,1)));
plot(freqs, abs(squeeze(freqresp(Gm_cart('Dz', 'Fy'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('$F_z$'); set(gca, 'XTickLabel',[]);
ax22 = nexttile();
hold on;
plot(f, abs(G_cart(:,2,2)));
plot(freqs, abs(squeeze(freqresp(Gm_cart('Dz', 'Fz'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
ax23 = nexttile();
hold on;
plot(f, abs(G_cart(:,2,3)));
plot(freqs, abs(squeeze(freqresp(Gm_cart('Dz', 'My'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
linkaxes([ax21,ax22,ax23],'y');
ylim([1e-8, 2e-4]);
ax31 = nexttile();
hold on;
plot(f, abs(G_cart(:,3,1)));
plot(freqs, abs(squeeze(freqresp(Gm_cart('Ry', 'Fy'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('$M_y$');
ax32 = nexttile();
hold on;
plot(f, abs(G_cart(:,3,2)));
plot(freqs, abs(squeeze(freqresp(Gm_cart('Ry', 'Fz'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
ax33 = nexttile();
hold on;
plot(f, abs(G_cart(:,3,3)));
plot(freqs, abs(squeeze(freqresp(Gm_cart('Ry', 'My'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
linkaxes([ax31,ax32,ax33],'y');
ylim([1e-9, 1e-2]);
linkaxes([ax11,ax12,ax13,ax21,ax22,ax23,ax31,ax32,ax33],'x');
xlim([1, 5e2]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_cart_plant_3x3.pdf', 'width', 'full', 'height', 'tall');
#+end_src
#+name: fig:id31_cart_plant_3x3
#+caption: 3x3 cartesian plant
#+RESULTS:
[[file:figs/id31_cart_plant_3x3.png]]
Normalization of outputs:
#+begin_src matlab
Gm_cart_normalized = -diag(1./diag(dcgain(Gm_cart)))*Gm_cart;
Gm_cart_normalized.InputName = {'Fy', 'Fz', 'My'};
Gm_cart_normalized.OutputName = {'Dy', 'Dz', 'Ry'};
G_cart_normalized = permute(pagemtimes(-diag(1./diag(dcgain(Gm_cart))), permute(G_cart, [2,3,1])), [3,1,2]);
#+end_src
#+begin_src matlab :exports none :results none
%% 3x3 plant
figure;
tiledlayout(3, 3, 'TileSpacing', 'compact', 'Padding', 'None');
ax11 = nexttile();
hold on;
plot(f, abs(G_cart_normalized(:,1,1)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_normalized('Dy', 'Fy'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('$D_y$'); set(gca, 'XTickLabel',[]);
title('$F_y$')
ax12 = nexttile();
hold on;
plot(f, abs(G_cart_normalized(:,1,2)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_normalized('Dy', 'Fz'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
title('$F_z$')
ax13 = nexttile();
hold on;
plot(f, abs(G_cart_normalized(:,1,3)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_normalized('Dy', 'My'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
title('$M_y$')
linkaxes([ax11,ax12,ax13],'y');
ylim([1e-4, 1e1]);
ax21 = nexttile();
hold on;
plot(f, abs(G_cart_normalized(:,2,1)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_normalized('Dz', 'Fy'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('$F_z$'); set(gca, 'XTickLabel',[]);
ax22 = nexttile();
hold on;
plot(f, abs(G_cart_normalized(:,2,2)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_normalized('Dz', 'Fz'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
ax23 = nexttile();
hold on;
plot(f, abs(G_cart_normalized(:,2,3)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_normalized('Dz', 'My'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
linkaxes([ax21,ax22,ax23],'y');
ylim([1e-4, 1e1]);
ax31 = nexttile();
hold on;
plot(f, abs(G_cart_normalized(:,3,1)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_normalized('Ry', 'Fy'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('$M_y$');
ax32 = nexttile();
hold on;
plot(f, abs(G_cart_normalized(:,3,2)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_normalized('Ry', 'Fz'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
ax33 = nexttile();
hold on;
plot(f, abs(G_cart_normalized(:,3,3)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_normalized('Ry', 'My'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
linkaxes([ax31,ax32,ax33],'y');
ylim([1e-4, 1e1]);
linkaxes([ax11,ax12,ax13,ax21,ax22,ax23,ax31,ax32,ax33],'x');
xlim([1, 5e2]);
#+end_src
#+begin_src matlab :exports none :results none
%% 3x3 plant
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_cart_normalized(:,1,1)));
plot(f, abs(G_cart_normalized(:,2,2)));
plot(f, abs(G_cart_normalized(:,3,3)));
plot(f, abs(G_cart_normalized(:,1,2)), 'color', [0,0,0,0.2]);
plot(f, abs(G_cart_normalized(:,1,3)), 'color', [0,0,0,0.2]);
plot(f, abs(G_cart_normalized(:,2,1)), 'color', [0,0,0,0.2]);
plot(f, abs(G_cart_normalized(:,3,1)), 'color', [0,0,0,0.2]);
plot(f, abs(G_cart_normalized(:,2,3)), 'color', [0,0,0,0.2]);
plot(f, abs(G_cart_normalized(:,3,2)), 'color', [0,0,0,0.2]);
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude');
ylim([1e-4, 1e1]);
ax2 = nexttile();
hold on;
plot(f, 180/pi*angle(G_cart_normalized(:,1,1)));
plot(f, 180/pi*angle(G_cart_normalized(:,2,2)));
plot(f, 180/pi*angle(G_cart_normalized(:,3,3)));
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
** Controller Design
*** Dy
#+begin_src matlab
%% Wanted crossover
wc = 2*pi*30;
%% Double Integrator
H_int = 1/(s + 0.1*2*pi) * ...
1/(s + 0.01*2*pi);
H_int = H_int/abs(evalfr(H_int, 1j*wc));
%% Lead to increase phase margin
a = 2; % Amount of phase lead / width of the phase lead / high frequency gain
H_lead = 1/sqrt(a)*(1 + s/(wc/sqrt(a)))/(1 + s/(wc*sqrt(a)));
a = 2; % Amount of phase lead / width of the phase lead / high frequency gain
H_lead = H_lead*1/sqrt(a)*(1 + s/(1.5*wc/sqrt(a)))/(1 + s/(1.5*wc*sqrt(a)));
%% Low Pass filter to increase robustness
H_lpf = 1/(1 + s/2/pi/300);
%% Notch at the top-plate resonance
% gm = 0.02;
% xi = 0.3;
% wn = 2*pi*665;
% H_notch = (s^2 + 2*gm*xi*wn*s + wn^2)/(s^2 + 2*xi*wn*s + wn^2);
%% Gain to have unitary crossover at 30Hz
[~, i_f] = min(abs(f - wc/2/pi));
H_gain = -1./abs(G_cart(i_f,1,1));
%% Decentralized HAC
Khac_Dy = H_gain * ... % Gain
H_int * ... % Integrator
H_lpf * ... % Low Pass Filter
H_lead; % Integrator
#+end_src
#+begin_src matlab :exports none
%% Loop Gain
figure;
tiledlayout(3, 1, 'TileSpacing', 'Compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_cart(:,1,1).*squeeze(freqresp(Khac_Dy, f, 'Hz'))));
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Loop Gain'); set(gca, 'XTickLabel',[]);
ylim([1e-4, 1e4]);
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(G_cart(:,1,1).*squeeze(freqresp(Khac_Dy, f, 'Hz'))));
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
*** Dz
#+begin_src matlab
%% Wanted crossover
wc = 2*pi*50;
%% Double Integrator
H_int = 1/(s + 0.1*2*pi) * ...
1/(s + 0.01*2*pi);
H_int = H_int/abs(evalfr(H_int, 1j*wc));
%% Lead to increase phase margin
a = 2; % Amount of phase lead / width of the phase lead / high frequency gain
H_lead = 1/sqrt(a)*(1 + s/(1.5*wc/sqrt(a)))/(1 + s/(1.5*wc*sqrt(a)));
a = 2; % Amount of phase lead / width of the phase lead / high frequency gain
H_lead = H_lead*1/sqrt(a)*(1 + s/(1.5*wc/sqrt(a)))/(1 + s/(1.5*wc*sqrt(a)));
%% Low Pass filter to increase robustness
H_lpf = 1/(1 + s/2/pi/300);
%% Notch at the top-plate resonance
% gm = 0.02;
% xi = 0.3;
% wn = 2*pi*665;
% H_notch = (s^2 + 2*gm*xi*wn*s + wn^2)/(s^2 + 2*xi*wn*s + wn^2);
%% Gain to have unitary crossover at 30Hz
[~, i_f] = min(abs(f - wc/2/pi));
H_gain = -1./abs(G_cart(i_f,2,2));
%% Decentralized HAC
Khac_Dz = H_gain * ... % Gain
H_int * ... % Integrator
H_lpf * ... % Low Pass Filter
H_lead; % Integrator
#+end_src
#+begin_src matlab :exports none
%% Loop Gain
figure;
tiledlayout(3, 1, 'TileSpacing', 'Compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_cart(:,2,2).*squeeze(freqresp(Khac_Dz, f, 'Hz'))));
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Loop Gain'); set(gca, 'XTickLabel',[]);
ylim([1e-4, 1e4]);
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(G_cart(:,2,2).*squeeze(freqresp(Khac_Dz, f, 'Hz'))));
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
*** Ry
#+begin_src matlab
%% Wanted crossover
wc = 2*pi*30;
%% Double Integrator
H_int = 1/(s + 0.1*2*pi) * ...
1/(s + 0.01*2*pi);
H_int = H_int/abs(evalfr(H_int, 1j*wc));
%% Lead to increase phase margin
a = 2; % Amount of phase lead / width of the phase lead / high frequency gain
H_lead = 1/sqrt(a)*(1 + s/(wc/sqrt(a)))/(1 + s/(wc*sqrt(a)));
a = 2; % Amount of phase lead / width of the phase lead / high frequency gain
H_lead = H_lead*1/sqrt(a)*(1 + s/(1.5*wc/sqrt(a)))/(1 + s/(1.5*wc*sqrt(a)));
%% Low Pass filter to increase robustness
H_lpf = 1/(1 + s/2/pi/300);
%% Notch at the top-plate resonance
% gm = 0.02;
% xi = 0.3;
% wn = 2*pi*665;
% H_notch = (s^2 + 2*gm*xi*wn*s + wn^2)/(s^2 + 2*xi*wn*s + wn^2);
%% Gain to have unitary crossover at 30Hz
[~, i_f] = min(abs(f - wc/2/pi));
H_gain = -1./abs(G_cart(i_f,3,3));
%% Decentralized HAC
Khac_Ry = H_gain * ... % Gain
H_int * ... % Integrator
H_lpf * ... % Low Pass Filter
H_lead; % Integrator
#+end_src
#+begin_src matlab :exports none
%% Loop Gain
figure;
tiledlayout(3, 1, 'TileSpacing', 'Compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_cart(:,3,3).*squeeze(freqresp(Khac_Ry, f, 'Hz'))));
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Loop Gain'); set(gca, 'XTickLabel',[]);
ylim([1e-4, 1e4]);
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(G_cart(:,3,3).*squeeze(freqresp(Khac_Ry, f, 'Hz'))));
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
*** 3x3 controller
#+begin_src matlab
Khac = blkdiag(Khac_Dy, Khac_Dz, Khac_Ry);
#+end_src
#+begin_src matlab :exports none :results none
%% description
figure;
tiledlayout(3, 1, 'TileSpacing', 'Compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_cart(:,1,1).*squeeze(freqresp(Khac_Dy, f, 'Hz'))), 'DisplayName', '$D_y$');
plot(f, abs(G_cart(:,2,2).*squeeze(freqresp(Khac_Dz, f, 'Hz'))), 'DisplayName', '$D_z$');
plot(f, abs(G_cart(:,3,3).*squeeze(freqresp(Khac_Ry, f, 'Hz'))), 'DisplayName', '$R_y$');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Loop Gain'); set(gca, 'XTickLabel',[]);
ylim([1e-4, 1e4]);
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1);
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(G_cart(:,1,1).*squeeze(freqresp(Khac_Dy, f, 'Hz'))));
plot(f, 180/pi*angle(G_cart(:,2,2).*squeeze(freqresp(Khac_Dz, f, 'Hz'))));
plot(f, 180/pi*angle(G_cart(:,3,3).*squeeze(freqresp(Khac_Ry, f, 'Hz'))));
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/G_cart_loop_gain_diagonal_3dof.pdf', 'width', 'full', 'height', 'tall');
#+end_src
#+name: fig:G_cart_loop_gain_diagonal_3dof
#+caption: description
#+RESULTS:
[[file:figs/G_cart_loop_gain_diagonal_3dof.png]]
** Check Stability
#+begin_src matlab :exports none
%% Compute the Eigenvalues of the loop gain
Ldet = zeros(3, length(f));
% Loop gain
Lmimo = pagemtimes(permute(G_cart, [2,3,1]),squeeze(freqresp(Khac, f, 'Hz')));
for i_f = 2:length(f)
Ldet(:, i_f) = eig(squeeze(Lmimo(:,:,i_f)));
end
#+end_src
#+begin_src matlab :exports none :results none
%% description
figure;
hold on;
plot(real(squeeze(Ldet(1,:))), imag(squeeze(Ldet(1,:))), 'k.')
plot(real(squeeze(Ldet(1,:))), -imag(squeeze(Ldet(1,:))), 'k.')
for i = 1:3
plot(real(squeeze(Ldet(i,:))), imag(squeeze(Ldet(i,:))), 'k.')
plot(real(squeeze(Ldet(i,:))), -imag(squeeze(Ldet(i,:))), 'k.')
end
plot(-1, 0, 'kx');
hold off;
set(gca, 'XScale', 'lin'); set(gca, 'YScale', 'lin');
xlabel('Real'); ylabel('Imag');
xlim([-3, 1]); ylim([-2, 2]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/G_cart_nyquist_diagonal_3dof.pdf', 'width', 'wide', 'height', 'tall');
#+end_src
#+name: fig:G_cart_nyquist_diagonal_3dof
#+caption: description
#+RESULTS:
[[file:figs/G_cart_nyquist_diagonal_3dof.png]]
** Save controllers
*** Save Controller
#+begin_src matlab :exports none :tangle no
K_order = order(Khac(1,1));
Kz = c2d(-Khac(1,1)*(1 + s/2/pi/2e3)^(9-K_order)/(1 + s/2/pi/2e3)^(9-K_order), 1e-4);
[num, den] = tfdata(Kz, 'v');
formatSpec = '%.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e\n';
fileID = fopen('/home/thomas/mnt/data_id31/nass/controllers/K_hac_Dy_3x3.dat', 'w');
fprintf(fileID, formatSpec, [num; den]');
fclose(fileID);
#+end_src
#+begin_src matlab :exports none :tangle no
K_order = order(Khac(2,2));
Kz = c2d(-Khac(2,2)*(1 + s/2/pi/2e3)^(9-K_order)/(1 + s/2/pi/2e3)^(9-K_order), 1e-4);
[num, den] = tfdata(Kz, 'v');
formatSpec = '%.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e\n';
fileID = fopen('/home/thomas/mnt/data_id31/nass/controllers/K_hac_Dz_3x3.dat', 'w');
fprintf(fileID, formatSpec, [num; den]');
fclose(fileID);
#+end_src
#+begin_src matlab :exports none :tangle no
K_order = order(Khac(3,3));
Kz = c2d(-Khac(3,3)*(1 + s/2/pi/2e3)^(9-K_order)/(1 + s/2/pi/2e3)^(9-K_order), 1e-4);
[num, den] = tfdata(Kz, 'v');
formatSpec = '%.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e\n';
fileID = fopen('/home/thomas/mnt/data_id31/nass/controllers/K_hac_Ry_3x3.dat', 'w');
fprintf(fileID, formatSpec, [num; den]');
fclose(fileID);
#+end_src
** Controller Design (normalized)
#+begin_src matlab
%% Wanted crossover
wc = 2*pi*30;
%% Double Integrator
H_int = 1/(s + 0.1*2*pi) * ...
(50*2*pi + s)/(0.01*2*pi + s);
H_int = H_int/abs(evalfr(H_int, 1j*wc));
% H_int = wc/s;
%% Lead to increase phase margin
a = 2; % Amount of phase lead / width of the phase lead / high frequency gain
H_lead = 1/sqrt(a)*(1 + s/(wc/sqrt(a)))/(1 + s/(wc*sqrt(a)));
% a = 2; % Amount of phase lead / width of the phase lead / high frequency gain
% H_lead = H_lead*1/sqrt(a)*(1 + s/(wc/sqrt(a)))/(1 + s/(wc*sqrt(a)));
%% Low Pass filter to increase robustness
H_lpf = 1/(1 + s/2/pi/200);
%% Notch at the top-plate resonance
% gm = 0.02;
% xi = 0.3;
% wn = 2*pi*665;
% H_notch = (s^2 + 2*gm*xi*wn*s + wn^2)/(s^2 + 2*xi*wn*s + wn^2);
%% Gain to have unitary crossover at 30Hz
[~, i_f] = min(abs(f - wc/2/pi));
H_gain = 1./abs(G_cart_normalized(i_f, 1, 1));
%% Decentralized HAC
Khac = H_gain * ... % Gain
H_int * ... % Integrator
H_lpf * ... % Low Pass Filter
H_lead * ... % Integrator
eye(3); % 6x6 Diagonal
% Khac.InputName = {'eL1', 'eL2', 'eL3', 'eL4', 'eL5', 'eL6'};
% Khac.OutputName = {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'};
#+end_src
#+begin_src matlab :exports none
%% Loop Gain
figure;
tiledlayout(3, 1, 'TileSpacing', 'Compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
for i = 1:3
plot(f, abs(G_cart_normalized(:,i,i).*squeeze(freqresp(Khac(i,i), f, 'Hz'))), 'color', [colors(i,:), 0.5]);
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Loop Gain'); set(gca, 'XTickLabel',[]);
ylim([1e-4, 1e4]);
ax2 = nexttile;
hold on;
for i = 1:3
plot(f, 180/pi*angle(G_cart_normalized(:,i,i).*squeeze(freqresp(Khac(i,i), f, 'Hz'))), 'color', [colors(i,:), 0.5]);
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
** Verify Stability
#+begin_src matlab :exports none
%% Compute the Eigenvalues of the loop gain
Ldet = zeros(3, length(f));
% Loop gain
Lmimo = pagemtimes(permute(G_cart_normalized, [2,3,1]),squeeze(freqresp(Khac, f, 'Hz')));
for i_f = 2:length(f)
Ldet(:, i_f) = eig(squeeze(Lmimo(:,:,i_f)));
end
#+end_src
#+begin_src matlab :exports none
%% Plot of the eigenvalues of L in the complex plane
figure;
hold on;
plot(real(squeeze(Ldet(1,:))), imag(squeeze(Ldet(1,:))), 'k.');
plot(real(squeeze(Ldet(1,:))), -imag(squeeze(Ldet(1,:))), 'k.');
for i = 1:3
plot(real(squeeze(Ldet(i,:))), imag(squeeze(Ldet(i,:))), 'k.');
plot(real(squeeze(Ldet(i,:))), -imag(squeeze(Ldet(i,:))), 'k.');
end
plot(-1, 0, 'kx', 'HandleVisibility', 'off');
hold off;
set(gca, 'XScale', 'lin'); set(gca, 'YScale', 'lin');
xlabel('Real'); ylabel('Imag');
xlim([-3, 1]); ylim([-2, 2]);
#+end_src
** Control Performances
#+begin_src matlab
data_cl = load(sprintf('%s/dynamics/2023-08-21_10-36_m0_cart_fixed.mat', mat_dir));
#+end_src
#+begin_src matlab :exports none
% Sampling Time [s]
Ts = 1e-4;
% Hannning Windows
Nfft = floor(1.0/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
#+end_src
#+begin_src matlab :exports none
% And we get the frequency vector
[S_dx, f] = tfestimate(data_cl.Dx.id_cl, data_cl.Dx.e_dx, win, Noverlap, Nfft, 1/Ts);
[S_dy, ~] = tfestimate(data_cl.Dy.id_cl, data_cl.Dy.e_dy, win, Noverlap, Nfft, 1/Ts);
[S_dz, ~] = tfestimate(data_cl.Dz.id_cl, data_cl.Dz.e_dz, win, Noverlap, Nfft, 1/Ts);
[S_rx, ~] = tfestimate(data_cl.Rx.id_cl, data_cl.Rx.e_rx, win, Noverlap, Nfft, 1/Ts);
[S_ry, ~] = tfestimate(data_cl.Ry.id_cl, data_cl.Ry.e_ry, win, Noverlap, Nfft, 1/Ts);
[S_rz, ~] = tfestimate(data_cl.Rz.id_cl, data_cl.Rz.e_rz, win, Noverlap, Nfft, 1/Ts);
#+end_src
- [ ] Compare with estimated performances
#+begin_src matlab :exports none :results none
%% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor
figure;
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile();
hold on;
plot(f, abs(S_dy), 'DisplayName', '$D_y$');
plot(f, abs(S_dz), 'DisplayName', '$D_z$');
plot(f, abs(S_ry), 'DisplayName', '$R_y$');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('Amplitude');
ylim([1e-3, 1e1]);
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 3);
xlim([1, 1e3]);
#+end_src
* Complementary Filter Control
<<sec:id31_cart_hac_complementary_filter>>
** 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 :tangle no :noweb yes
<<m-init-path>>
#+end_src
#+begin_src matlab :eval no :noweb yes
<<m-init-path-tangle>>
#+end_src
#+begin_src matlab :noweb yes
<<m-init-other>>
#+end_src
** m0
*** 3x3 plant in Cartesian plane
#+begin_src matlab :exports none
%% Load model
load('Gm.mat')
#+end_src
#+begin_src matlab
%% Jacobian for 3x3 plant
n_hexapod = initializeNanoHexapod('flex_bot_type', '2dof', ...
'flex_top_type', '3dof', ...
'motion_sensor_type', 'plates', ...
'actuator_type', '2dof');
Jt_inv = pinv(n_hexapod.geometry.J');
J_int_to_X = [0.78740157480315 0.21259842519685 0 0 0;
0 0 0 0 -1;
0 0 -13.1233595800525 13.1233595800525 0];
#+end_src
Compute identified plant in the Cartesian plane:
#+begin_src matlab
%% New data after calibrating the Rz-offset
data_m0 = load(sprintf('%s/dynamics/2023-08-21_13-32_damp_plant_m0.mat', mat_dir));
#+end_src
#+begin_src matlab :exports none
% Sampling Time [s]
Ts = 1e-4;
% Hannning Windows
Nfft = floor(1.0/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
#+end_src
#+begin_src matlab :exports none
% And we get the frequency vector
[~, f] = tfestimate(data_m0.uL1.id_plant, data_m0.uL1.d1, win, Noverlap, Nfft, 1/Ts);
#+end_src
#+begin_src matlab :exports none
%% HAC Cartesian Plant
G_hac_m0 = zeros(length(f), 5, 6);
for i_strut = 1:6
Di = [data_m0.(sprintf("uL%i", i_strut)).d1 ; data_m0.(sprintf("uL%i", i_strut)).d2 ; data_m0.(sprintf("uL%i", i_strut)).d3 ; data_m0.(sprintf("uL%i", i_strut)).d4 ; data_m0.(sprintf("uL%i", i_strut)).d5]';
G_hac_m0(:,:,i_strut) = tfestimate(data_m0.(sprintf("uL%i", i_strut)).id_plant, detrend(Di, 0), win, Noverlap, Nfft, 1/Ts);
end
G_cart = permute(pagemtimes(J_int_to_X, pagemtimes(permute(G_hac_m0, [2,3,1]), Jt_inv(:,[2,3,5]))), [3,1,2]);
#+end_src
Compute plant model in the Cartesian plane:
#+begin_src matlab
Gm_cart = J_int_to_X*Gm_hac_m0({'d1', 'd2', 'd3', 'd4', 'd5'}, {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'})*Jt_inv(:,[2,3,5]);
Gm_cart.InputName = {'Fy', 'Fz', 'My'};
Gm_cart.OutputName = {'Dy', 'Dz', 'Ry'};
#+end_src
#+begin_src matlab :exports none :results none
%% 3x3 plant
figure;
tiledlayout(3, 3, 'TileSpacing', 'compact', 'Padding', 'None');
ax11 = nexttile();
hold on;
plot(f, abs(G_cart(:,1,1)));
plot(freqs, abs(squeeze(freqresp(Gm_cart('Dy', 'Fy'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('$D_y$'); set(gca, 'XTickLabel',[]);
title('$F_y$')
ax12 = nexttile();
hold on;
plot(f, abs(G_cart(:,1,2)));
plot(freqs, abs(squeeze(freqresp(Gm_cart('Dy', 'Fz'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
title('$F_z$')
ax13 = nexttile();
hold on;
plot(f, abs(G_cart(:,1,3)));
plot(freqs, abs(squeeze(freqresp(Gm_cart('Dy', 'My'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
title('$M_y$')
linkaxes([ax11,ax12,ax13],'y');
ylim([1e-8, 2e-4]);
ax21 = nexttile();
hold on;
plot(f, abs(G_cart(:,2,1)));
plot(freqs, abs(squeeze(freqresp(Gm_cart('Dz', 'Fy'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('$F_z$'); set(gca, 'XTickLabel',[]);
ax22 = nexttile();
hold on;
plot(f, abs(G_cart(:,2,2)));
plot(freqs, abs(squeeze(freqresp(Gm_cart('Dz', 'Fz'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
ax23 = nexttile();
hold on;
plot(f, abs(G_cart(:,2,3)));
plot(freqs, abs(squeeze(freqresp(Gm_cart('Dz', 'My'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
linkaxes([ax21,ax22,ax23],'y');
ylim([1e-8, 2e-4]);
ax31 = nexttile();
hold on;
plot(f, abs(G_cart(:,3,1)));
plot(freqs, abs(squeeze(freqresp(Gm_cart('Ry', 'Fy'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('$M_y$');
ax32 = nexttile();
hold on;
plot(f, abs(G_cart(:,3,2)));
plot(freqs, abs(squeeze(freqresp(Gm_cart('Ry', 'Fz'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
ax33 = nexttile();
hold on;
plot(f, abs(G_cart(:,3,3)));
plot(freqs, abs(squeeze(freqresp(Gm_cart('Ry', 'My'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
linkaxes([ax31,ax32,ax33],'y');
ylim([1e-9, 1e-2]);
linkaxes([ax11,ax12,ax13,ax21,ax22,ax23,ax31,ax32,ax33],'x');
xlim([1, 5e2]);
#+end_src
#+begin_src matlab :exports none :results none
%% 3x3 plant
figure;
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile();
hold on;
plot(f, abs(G_cart(:,1,1)), 'color', [colors(1,:)]);
plot(freqs, abs(squeeze(freqresp(Gm_cart('Dy', 'Fy'), freqs, 'Hz'))), '--', 'color', [colors(1,:)]);
plot(f, abs(G_cart(:,2,2)), 'color', [colors(2,:)]);
plot(freqs, abs(squeeze(freqresp(Gm_cart('Dz', 'Fz'), freqs, 'Hz'))), '--', 'color', [colors(2,:)]);
plot(f, abs(G_cart(:,3,3)), 'color', [colors(3,:)]);
plot(freqs, abs(squeeze(freqresp(Gm_cart('Ry', 'My'), freqs, 'Hz'))), '--', 'color', [colors(3,:)]);
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('Magnitude');
ylim([1e-9, 1e-2]);
xlim([1, 5e2]);
#+end_src
*** Plant Invert
Reduce model size
#+begin_src matlab
Gm_cart_dy = flipRphZeros(balred(-Gm_cart('Dy', 'Fy'), 10));
Gm_cart_dz = flipRphZeros(balred(-Gm_cart('Dz', 'Fz'), 10));
Gm_cart_ry = flipRphZeros(balred(-Gm_cart('Ry', 'My'), 10));
#+end_src
Add first resonance
#+begin_src matlab :eval no
% gm = 200;
% xi = 0.003;
% wn = 2*pi*670;
% Gm_cart_dy = Gm_cart_dy*(s^2 + 2*gm*xi*wn*s + wn^2)/(s^2 + 2*xi*wn*s + wn^2);
% gm = 200;
% xi = 0.003;
% wn = 2*pi*1086;
% Gm_cart_dz = Gm_cart_dz*(s^2 + 2*gm*xi*wn*s + wn^2)/(s^2 + 2*xi*wn*s + wn^2);
% gm = 200;
% xi = 0.003;
% wn = 2*pi*670;
% Gm_cart_ry = Gm_cart_ry*(s^2 + 2*gm*xi*wn*s + wn^2)/(s^2 + 2*xi*wn*s + wn^2);
#+end_src
#+begin_src matlab :exports none :results none
%% Comparaison of the measured direct terms and the reduced order models
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_cart(:,1,1)), 'color', [colors(1,:)], 'DisplayName', '$D_y$');
plot(f, abs(squeeze(freqresp(Gfit_dy, f, 'Hz'))), '--', 'color', [colors(1,:)], 'HandleVisibility', 'off');
plot(f, abs(G_cart(:,2,2)), 'color', [colors(2,:)], 'DisplayName', '$D_z$');
plot(f, abs(squeeze(freqresp(Gm_cart_dz, f, 'Hz'))), '--', 'color', [colors(2,:)], 'HandleVisibility', 'off');
plot(f, abs(G_cart(:,3,3)), 'color', [colors(3,:)], 'DisplayName', '$R_y$');
plot(f, abs(squeeze(freqresp(Gm_cart_ry, f, 'Hz'))), '--', 'color', [colors(3,:)], 'HandleVisibility', 'off');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude');
legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(G_cart(:,1,1)), 'color', [colors(1,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Gm_cart_dy, f, 'Hz'))), '--', 'color', [colors(1,:)]);
plot(f, 180/pi*angle(G_cart(:,2,2)), 'color', [colors(2,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Gm_cart_dz, f, 'Hz'))), '--', 'color', [colors(2,:)]);
plot(f, 180/pi*angle(G_cart(:,3,3)), 'color', [colors(3,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Gm_cart_ry, f, 'Hz'))), '--', 'color', [colors(3,:)]);
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 2e3]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_comp_cart_plant_reduced_model.pdf', 'width', 'wide', 'height', 'tall');
#+end_src
#+name: fig:id31_comp_cart_plant_reduced_model
#+caption: Comparaison of the measured direct terms and the reduced order models
#+RESULTS:
[[file:figs/id31_comp_cart_plant_reduced_model.png]]
Invert and make realizable
#+begin_src matlab
Gm_cart_dy_inv = inv(Gm_cart_dy);
Gm_cart_dz_inv = inv(Gm_cart_dz);
Gm_cart_ry_inv = inv(Gm_cart_ry);
#+end_src
#+begin_src matlab
isstable(Gm_cart_dy_inv)
isstable(Gm_cart_dz_inv)
isstable(Gm_cart_ry_inv)
#+end_src
*** Save Plant Inverse
#+begin_src matlab :exports none :tangle no
K = -Gm_cart_dy_inv;
K_order = order(K);
Kz = c2d(K*(1 + s/2/pi/2e3)^(9-K_order)/(1 + s/2/pi/2e3)^(9-K_order), 1e-4);
[num, den] = tfdata(Kz, 'v');
formatSpec = '%.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e\n';
fileID = fopen('/home/thomas/mnt/data_id31/nass/controllers/G_dy_inv.dat', 'w');
fprintf(fileID, formatSpec, [num; den]');
fclose(fileID);
#+end_src
#+begin_src matlab :exports none :tangle no
K = -Gm_cart_dz_inv;
K_order = order(K);
Kz = c2d(K*(1 + s/2/pi/2e3)^(9-K_order)/(1 + s/2/pi/2e3)^(9-K_order), 1e-4);
[num, den] = tfdata(Kz, 'v');
formatSpec = '%.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e\n';
fileID = fopen('/home/thomas/mnt/data_id31/nass/controllers/G_dz_inv.dat', 'w');
fprintf(fileID, formatSpec, [num; den]');
fclose(fileID);
#+end_src
#+begin_src matlab :exports none :tangle no
K = -Gm_cart_ry_inv;
K_order = order(K);
Kz = c2d(K*(1 + s/2/pi/2e3)^(9-K_order)/(1 + s/2/pi/2e3)^(9-K_order), 1e-4);
[num, den] = tfdata(Kz, 'v');
formatSpec = '%.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e\n';
fileID = fopen('/home/thomas/mnt/data_id31/nass/controllers/G_ry_inv.dat', 'w');
fprintf(fileID, formatSpec, [num; den]');
fclose(fileID);
#+end_src
*** Control Performances
**** 5Hz
#+begin_src matlab
data_cl = load(sprintf('%s/dynamics/2023-08-21_10-59_m0_cf_5Hz.mat', mat_dir));
#+end_src
#+begin_src matlab :exports none
% Sampling Time [s]
Ts = 1e-4;
% Hannning Windows
Nfft = floor(1.0/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
#+end_src
#+begin_src matlab :exports none
% And we get the frequency vector
[S_dx, f] = tfestimate(data_cl.Dx.id_cl, data_cl.Dx.e_dx, win, Noverlap, Nfft, 1/Ts);
[S_dy, ~] = tfestimate(data_cl.Dy.id_cl, data_cl.Dy.e_dy, win, Noverlap, Nfft, 1/Ts);
[S_dz, ~] = tfestimate(data_cl.Dz.id_cl, data_cl.Dz.e_dz, win, Noverlap, Nfft, 1/Ts);
[S_rx, ~] = tfestimate(data_cl.Rx.id_cl, data_cl.Rx.e_rx, win, Noverlap, Nfft, 1/Ts);
[S_ry, ~] = tfestimate(data_cl.Ry.id_cl, data_cl.Ry.e_ry, win, Noverlap, Nfft, 1/Ts);
[S_rz, ~] = tfestimate(data_cl.Rz.id_cl, data_cl.Rz.e_rz, win, Noverlap, Nfft, 1/Ts);
#+end_src
- [ ] Compare with estimated performances
#+begin_src matlab :exports none :results none
%% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor
figure;
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile();
hold on;
plot(f, abs(S_dy), 'DisplayName', '$D_y$');
plot(f, abs(S_dz), 'DisplayName', '$D_z$');
plot(f, abs(S_ry), 'DisplayName', '$R_y$');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('Amplitude');
ylim([1e-3, 1e1]);
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 3);
xlim([1, 1e3]);
#+end_src
**** 20Hz
#+begin_src matlab
data_cl = load(sprintf('%s/dynamics/2023-08-21_11-04_m0_cf_20Hz.mat', mat_dir));
#+end_src
#+begin_src matlab :exports none
% Sampling Time [s]
Ts = 1e-4;
% Hannning Windows
Nfft = floor(1.0/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
#+end_src
#+begin_src matlab :exports none
% And we get the frequency vector
[S_dx, f] = tfestimate(data_cl.Dx.id_cl, data_cl.Dx.e_dx, win, Noverlap, Nfft, 1/Ts);
[S_dy, ~] = tfestimate(data_cl.Dy.id_cl, data_cl.Dy.e_dy, win, Noverlap, Nfft, 1/Ts);
[S_dz, ~] = tfestimate(data_cl.Dz.id_cl, data_cl.Dz.e_dz, win, Noverlap, Nfft, 1/Ts);
[S_rx, ~] = tfestimate(data_cl.Rx.id_cl, data_cl.Rx.e_rx, win, Noverlap, Nfft, 1/Ts);
[S_ry, ~] = tfestimate(data_cl.Ry.id_cl, data_cl.Ry.e_ry, win, Noverlap, Nfft, 1/Ts);
[S_rz, ~] = tfestimate(data_cl.Rz.id_cl, data_cl.Rz.e_rz, win, Noverlap, Nfft, 1/Ts);
#+end_src
- [ ] Compare with estimated performances
#+begin_src matlab :exports none :results none
%% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor
figure;
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile();
hold on;
plot(f, abs(S_dy), 'DisplayName', '$D_y$');
plot(f, abs(S_dz), 'DisplayName', '$D_z$');
plot(f, abs(S_ry), 'DisplayName', '$R_y$');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('Amplitude');
ylim([1e-3, 1e1]);
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 3);
xlim([1, 1e3]);
#+end_src
**** Different bandwidth for different directions
#+begin_src matlab
data_cl = load(sprintf('%s/dynamics/2023-08-21_11-16_m0_cf_different.mat', mat_dir));
#+end_src
#+begin_src matlab :exports none
% Sampling Time [s]
Ts = 1e-4;
% Hannning Windows
Nfft = floor(1.0/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
#+end_src
#+begin_src matlab :exports none
% And we get the frequency vector
[S_dx, f] = tfestimate(data_cl.Dx.id_cl, data_cl.Dx.e_dx, win, Noverlap, Nfft, 1/Ts);
[S_dy, ~] = tfestimate(data_cl.Dy.id_cl, data_cl.Dy.e_dy, win, Noverlap, Nfft, 1/Ts);
[S_dz, ~] = tfestimate(data_cl.Dz.id_cl, data_cl.Dz.e_dz, win, Noverlap, Nfft, 1/Ts);
[S_rx, ~] = tfestimate(data_cl.Rx.id_cl, data_cl.Rx.e_rx, win, Noverlap, Nfft, 1/Ts);
[S_ry, ~] = tfestimate(data_cl.Ry.id_cl, data_cl.Ry.e_ry, win, Noverlap, Nfft, 1/Ts);
[S_rz, ~] = tfestimate(data_cl.Rz.id_cl, data_cl.Rz.e_rz, win, Noverlap, Nfft, 1/Ts);
#+end_src
- [ ] Compare with estimated performances
#+begin_src matlab :exports none :results none
%% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor
figure;
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile();
hold on;
plot(f, abs(S_dy), 'DisplayName', '$D_y$');
plot(f, abs(S_dz), 'DisplayName', '$D_z$');
plot(f, abs(S_ry), 'DisplayName', '$R_y$');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('Amplitude');
ylim([1e-3, 1e1]);
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 3);
xlim([1, 1e3]);
#+end_src
**** Dz 25Hz
#+begin_src matlab
data_cl = load(sprintf('%s/dynamics/', mat_dir));
#+end_src
#+begin_src matlab :exports none
% Sampling Time [s]
Ts = 1e-4;
% Hannning Windows
Nfft = floor(1.0/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
#+end_src
#+begin_src matlab :exports none
% And we get the frequency vector
[S_dx, f] = tfestimate(data_cl.Dx.id_cl, data_cl.Dx.e_dx, win, Noverlap, Nfft, 1/Ts);
[S_dy, ~] = tfestimate(data_cl.Dy.id_cl, data_cl.Dy.e_dy, win, Noverlap, Nfft, 1/Ts);
[S_dz, ~] = tfestimate(data_cl.Dz.id_cl, data_cl.Dz.e_dz, win, Noverlap, Nfft, 1/Ts);
[S_rx, ~] = tfestimate(data_cl.Rx.id_cl, data_cl.Rx.e_rx, win, Noverlap, Nfft, 1/Ts);
[S_ry, ~] = tfestimate(data_cl.Ry.id_cl, data_cl.Ry.e_ry, win, Noverlap, Nfft, 1/Ts);
[S_rz, ~] = tfestimate(data_cl.Rz.id_cl, data_cl.Rz.e_rz, win, Noverlap, Nfft, 1/Ts);
#+end_src
- [ ] Compare with estimated performances
#+begin_src matlab :exports none :results none
%% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor
figure;
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile();
hold on;
plot(f, abs(S_dy), 'DisplayName', '$D_y$');
plot(f, abs(S_dz), 'DisplayName', '$D_z$');
plot(f, abs(S_ry), 'DisplayName', '$R_y$');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('Amplitude');
ylim([1e-3, 1e1]);
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 3);
xlim([1, 1e3]);
#+end_src
*** Better plant invert
**** Dy
#+begin_src matlab :exports none
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)
%% We define the number of iteration.
Niter = 100;
#+end_src
#+begin_src matlab :exports none
N = 9; %Order of approximation
%Complex conjugate pairs, linearly spaced:
bet=linspace(f(1),f(end),N/2);
poles=[];
for n=1:length(bet)
alf=-bet(n)*1e-1;
poles=[poles (alf-i*bet(n)) (alf+i*bet(n)) ];
end
#+end_src
#+begin_src matlab
%% Estimate resonance frequency and damping
for iter =1:Niter
[G_est, poles, ~, frf_est] = vectfit3(G_cart(f>2&f<800,1,1).', 1i*2*pi*f(f>2&f<800)', poles, 1./(f(f>2&f<800))', opts);
end
Gfit_dy = ss(G_est.A, G_est.B, G_est.C, G_est.D);
#+end_src
#+begin_src matlab :exports none :results none
%% Comparaison of the measured direct terms and the reduced order models
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_cart(:,1,1)), 'color', [colors(1,:)], 'DisplayName', '$D_y$');
plot(f, abs(squeeze(freqresp(Gfit_dy, f, 'Hz'))), '--', 'color', [colors(1,:)], 'HandleVisibility', 'off');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude');
legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(G_cart(:,1,1)), 'color', [colors(1,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Gfit_dy, f, 'Hz'))), '--', 'color', [colors(1,:)]);
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 2e3]);
#+end_src
Stable Inverse
#+begin_src matlab
Ginv_dy = inv(-flipRphZeros(Gfit_dy))/(1 + s/2/pi/1e3);
isstable(Ginv_dy)
#+end_src
**** Dz
#+begin_src matlab :exports none
N = 9; %Order of approximation
%Complex conjugate pairs, linearly spaced:
bet=linspace(f(1),f(end),N/2);
poles=[];
for n=1:length(bet)
alf=-bet(n)*1e-1;
poles=[poles (alf-i*bet(n)) (alf+i*bet(n)) ];
end
#+end_src
#+begin_src matlab
%% Estimate resonance frequency and damping
for iter =1:Niter
[G_est, poles, ~, frf_est] = vectfit3(G_cart(f>2&f<1500,2,2).', 1i*2*pi*f(f>2&f<1500)', poles, 1./(f(f>2&f<1500))', opts);
end
Gfit_dz = ss(G_est.A, G_est.B, G_est.C, G_est.D);
#+end_src
#+begin_src matlab :exports none :results none
%% Comparaison of the measured direct terms and the reduced order models
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_cart(:,2,2)), 'color', [colors(2,:)], 'DisplayName', '$D_z$');
plot(f, abs(squeeze(freqresp(Gfit_dz, f, 'Hz'))), '--', 'color', [colors(2,:)], 'HandleVisibility', 'off');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude');
legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(G_cart(:,2,2)), 'color', [colors(2,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Gfit_dz, f, 'Hz'))), '--', 'color', [colors(2,:)]);
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 2e3]);
#+end_src
Stable Inverse
#+begin_src matlab
Ginv_dz = inv(-flipRphZeros(Gfit_dz))/(1 + s/2/pi/1e3);
isstable(Ginv_dz)
#+end_src
**** Ry
#+begin_src matlab :exports none
N = 9; %Order of approximation
%Complex conjugate pairs, linearly spaced:
bet=linspace(f(1),f(end),N/2);
poles=[];
for n=1:length(bet)
alf=-bet(n)*1e-1;
poles=[poles (alf-i*bet(n)) (alf+i*bet(n)) ];
end
#+end_src
#+begin_src matlab
%% Estimate resonance frequency and damping
for iter =1:Niter
[G_est, poles, ~, frf_est] = vectfit3(G_cart(f>2&f<800,3,3).', 1i*2*pi*f(f>2&f<800)', poles, 1./(f(f>2&f<800))', opts);
end
Gfit_ry = ss(G_est.A, G_est.B, G_est.C, G_est.D);
#+end_src
#+begin_src matlab :exports none :results none
%% Comparaison of the measured direct terms and the reduced order models
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_cart(:,3,3)), 'color', [colors(3,:)], 'DisplayName', '$R_y$');
plot(f, abs(squeeze(freqresp(Gfit_ry, f, 'Hz'))), '--', 'color', [colors(3,:)], 'HandleVisibility', 'off');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude');
legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(G_cart(:,3,3)), 'color', [colors(3,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Gfit_ry, f, 'Hz'))), '--', 'color', [colors(3,:)]);
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 2e3]);
#+end_src
Stable Inverse
#+begin_src matlab
Ginv_ry = inv(flipRphZeros(Gfit_ry))/(1 + s/2/pi/1e3);
isstable(Ginv_ry)
#+end_src
**** Compare Invert plants
#+begin_src matlab :exports none :results none
%% Comparaison of the measured direct terms and the reduced order models
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, 1./abs(G_cart(:,1,1)), 'color', [colors(1,:)], 'DisplayName', '$D_y$');
plot(f, abs(squeeze(freqresp(Ginv_dy, f, 'Hz'))), '--', 'color', [colors(1,:)], 'HandleVisibility', 'off');
plot(f, 1./abs(G_cart(:,2,2)), 'color', [colors(2,:)], 'DisplayName', '$D_z$');
plot(f, abs(squeeze(freqresp(Ginv_dz, f, 'Hz'))), '--', 'color', [colors(2,:)], 'HandleVisibility', 'off');
plot(f, 1./abs(G_cart(:,3,3)), 'color', [colors(3,:)], 'DisplayName', '$R_y$');
plot(f, abs(squeeze(freqresp(Ginv_ry, f, 'Hz'))), '--', 'color', [colors(3,:)], 'HandleVisibility', 'off');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude');
legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(1./G_cart(:,1,1)), 'color', [colors(1,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Ginv_dy, f, 'Hz'))), '--', 'color', [colors(1,:)]);
plot(f, 180/pi*angle(1./G_cart(:,2,2)), 'color', [colors(2,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Ginv_dz, f, 'Hz'))), '--', 'color', [colors(2,:)]);
plot(f, 180/pi*angle(1./G_cart(:,3,3)), 'color', [colors(3,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Ginv_ry, f, 'Hz'))), '--', 'color', [colors(3,:)]);
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 2e3]);
#+end_src
#+begin_src matlab :exports none :results none
%% Comparaison of the measured direct terms and the reduced order models
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(squeeze(freqresp(Ginv_dy, f, 'Hz')).*abs(G_cart(:,1,1))), '-', 'DisplayName', '$D_y$');
plot(f, abs(squeeze(freqresp(Ginv_dz, f, 'Hz')).*abs(G_cart(:,2,2))), '-', 'DisplayName', '$D_z$');
plot(f, abs(squeeze(freqresp(Ginv_ry, f, 'Hz')).*abs(G_cart(:,3,3))), '-', 'DisplayName', '$R_y$');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude');
legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(squeeze(freqresp(Ginv_dy, f, 'Hz')).*G_cart(:,1,1)), '-');
plot(f, 180/pi*angle(squeeze(freqresp(Ginv_dz, f, 'Hz')).*G_cart(:,2,2)), '-');
plot(f, 180/pi*angle(squeeze(freqresp(Ginv_ry, f, 'Hz')).*G_cart(:,3,3)), '-');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 2e3]);
#+end_src
**** Save plant inverse
#+begin_src matlab :exports none :tangle no
K = -Ginv_dy;
K_order = order(K);
Kz_dy = c2d(K*(1 + s/2/pi/2e3)^(9-K_order)/(1 + s/2/pi/2e3)^(9-K_order), 1e-4, 'tustin');
[num, den] = tfdata(Kz_dy, 'v');
formatSpec = '%.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e\n';
fileID = fopen('/home/thomas/mnt/data_id31/nass/controllers/G_dy_inv_fit.dat', 'w');
fprintf(fileID, formatSpec, [num; den]');
fclose(fileID);
#+end_src
#+begin_src matlab :exports none :tangle no
K = -Ginv_dz;
K_order = order(K);
Kz_dz = c2d(K*(1 + s/2/pi/2e3)^(9-K_order)/(1 + s/2/pi/2e3)^(9-K_order), 1e-4, 'tustin');
[num, den] = tfdata(Kz_dz, 'v');
formatSpec = '%.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e\n';
fileID = fopen('/home/thomas/mnt/data_id31/nass/controllers/G_dz_inv_fit.dat', 'w');
fprintf(fileID, formatSpec, [num; den]');
fclose(fileID);
#+end_src
#+begin_src matlab :exports none :tangle no
K = -Ginv_ry;
K_order = order(K);
Kz_ry = c2d(K*(1 + s/2/pi/2e3)^(9-K_order)/(1 + s/2/pi/2e3)^(9-K_order), 1e-4, 'tustin');
[num, den] = tfdata(Kz_ry, 'v');
formatSpec = '%.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e\n';
fileID = fopen('/home/thomas/mnt/data_id31/nass/controllers/G_ry_inv_fit.dat', 'w');
fprintf(fileID, formatSpec, [num; den]');
fclose(fileID);
#+end_src
**** Compare Digital Invert plants
#+begin_src matlab :exports none :results none
%% Comparaison of the measured direct terms and the reduced order models
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, 1./abs(G_cart(:,1,1)), 'color', [colors(1,:)], 'DisplayName', '$D_y$');
plot(f, abs(squeeze(freqresp(Kz_dy, f, 'Hz'))), '--', 'color', [colors(1,:)], 'HandleVisibility', 'off');
plot(f, 1./abs(G_cart(:,2,2)), 'color', [colors(2,:)], 'DisplayName', '$D_z$');
plot(f, abs(squeeze(freqresp(Kz_dz, f, 'Hz'))), '--', 'color', [colors(2,:)], 'HandleVisibility', 'off');
plot(f, 1./abs(G_cart(:,3,3)), 'color', [colors(3,:)], 'DisplayName', '$R_y$');
plot(f, abs(squeeze(freqresp(Kz_ry, f, 'Hz'))), '--', 'color', [colors(3,:)], 'HandleVisibility', 'off');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude');
legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(1./G_cart(:,1,1)), 'color', [colors(1,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Kz_dy, f, 'Hz'))), '--', 'color', [colors(1,:)]);
plot(f, 180/pi*angle(1./G_cart(:,2,2)), 'color', [colors(2,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Kz_dz, f, 'Hz'))), '--', 'color', [colors(2,:)]);
plot(f, 180/pi*angle(1./G_cart(:,3,3)), 'color', [colors(3,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Kz_ry, f, 'Hz'))), '--', 'color', [colors(3,:)]);
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 2e3]);
#+end_src
#+begin_src matlab :exports none :results none
%% Comparaison of the measured direct terms and the reduced order models
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(squeeze(freqresp(Ginv_dy, f, 'Hz')).*abs(G_cart(:,1,1))), '-', 'DisplayName', '$D_y$');
plot(f, abs(squeeze(freqresp(Ginv_dz, f, 'Hz')).*abs(G_cart(:,2,2))), '-', 'DisplayName', '$D_z$');
plot(f, abs(squeeze(freqresp(Ginv_ry, f, 'Hz')).*abs(G_cart(:,3,3))), '-', 'DisplayName', '$R_y$');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude');
legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(squeeze(freqresp(Ginv_dy, f, 'Hz')).*G_cart(:,1,1)), '-');
plot(f, 180/pi*angle(squeeze(freqresp(Ginv_dz, f, 'Hz')).*G_cart(:,2,2)), '-');
plot(f, 180/pi*angle(squeeze(freqresp(Ginv_ry, f, 'Hz')).*G_cart(:,3,3)), '-');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 2e3]);
#+end_src
*** Control Performances
**** Better plant invert
#+begin_src matlab
data_cl = load(sprintf('%s/dynamics/2023-08-21_13-36_m0_cf_inv_fit.mat', mat_dir));
#+end_src
#+begin_src matlab :exports none
% Sampling Time [s]
Ts = 1e-4;
% Hannning Windows
Nfft = floor(1.0/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
#+end_src
#+begin_src matlab :exports none
% And we get the frequency vector
[S_dy, f] = tfestimate(data_cl.Dy.id_cl, data_cl.Dy.e_dy, win, Noverlap, Nfft, 1/Ts);
[S_dz, ~] = tfestimate(data_cl.Dz.id_cl, data_cl.Dz.e_dz, win, Noverlap, Nfft, 1/Ts);
[S_ry, ~] = tfestimate(data_cl.Ry.id_cl, data_cl.Ry.e_ry, win, Noverlap, Nfft, 1/Ts);
#+end_src
#+begin_src matlab :exports none :results none
%% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor
figure;
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile();
hold on;
plot(f, abs(S_dy), 'DisplayName', '$D_y$');
plot(f, abs(S_dz), 'DisplayName', '$D_z$');
plot(f, abs(S_ry), 'DisplayName', '$R_y$');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('Amplitude');
ylim([1e-3, 1e1]);
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 3);
xlim([1, 1e3]);
#+end_src
*** Scans with good controller
**** 1rpm
1RPM scans are performed for all the masses with the same controller.
#+begin_src matlab
data_m0 = load(sprintf("%s/scans/2023-08-21_14-28_m0_1rpm_K_cf.mat", mat_dir));
data_m0.time = Ts*[0:length(data_m0.Rz)-1];
#+end_src
#+begin_src matlab
%% Compute RMS values while in closed-loop
data_m0.Dx_rms_cl = rms(detrend(data_m0.Dx_int, 0));
data_m0.Dy_rms_cl = rms(detrend(data_m0.Dy_int, 0));
data_m0.Dz_rms_cl = rms(detrend(data_m0.Dz_int, 0));
data_m0.Rx_rms_cl = rms(detrend(data_m0.Rx_int, 0));
data_m0.Ry_rms_cl = rms(detrend(data_m0.Ry_int, 0));
#+end_src
#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*)
data2orgtable([1e9*data_m0.Dx_rms_cl, 1e9*data_m0.Dy_rms_cl, 1e9*data_m0.Dz_rms_cl, 1e9*data_m0.Rx_rms_cl, 1e9*data_m0.Ry_rms_cl], ...
{'m0'}, {'Dx [nm]', 'Dy [nm]', 'Dz [nm]', 'Rx [nrad]', 'Ry [nrad]'}, ' %.0f ');
#+end_src
#+RESULTS:
| | Dx [nm] | Dy [nm] | Dz [nm] | Rx [nrad] | Ry [nrad] |
|----+---------+---------+---------+-----------+-----------|
| m0 | 796 | 20 | 8 | 8209 | 73 |
#+begin_src matlab :exports none :results none
%% description
figure;
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile;
hold on;
plot(1e9*data_m0.Dy_int, 1e9*data_m0.Dz_int);
hold off;
axis square
xlabel("Y motion [$nm$]");
ylabel("Z motion [$nm$]");
#+end_src
**** 30rpm
1RPM scans are performed for all the masses with the same controller.
#+begin_src matlab
data_m0 = load(sprintf("%s/scans/2023-08-21_14-33_m0_30rpm_cf_control.mat", mat_dir));
data_m0.time = Ts*[0:length(data_m0.Rz)-1];
#+end_src
#+begin_src matlab
%% Compute RMS values while in closed-loop
data_m0.Dx_rms_cl = rms(detrend(data_m0.Dx_int, 0));
data_m0.Dy_rms_cl = rms(detrend(data_m0.Dy_int, 0));
data_m0.Dz_rms_cl = rms(detrend(data_m0.Dz_int, 0));
data_m0.Rx_rms_cl = rms(detrend(data_m0.Rx_int, 0));
data_m0.Ry_rms_cl = rms(detrend(data_m0.Ry_int, 0));
#+end_src
#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*)
data2orgtable([1e9*data_m0.Dx_rms_cl, 1e9*data_m0.Dy_rms_cl, 1e9*data_m0.Dz_rms_cl, 1e9*data_m0.Rx_rms_cl, 1e9*data_m0.Ry_rms_cl], ...
{'m0'}, {'Dx [nm]', 'Dy [nm]', 'Dz [nm]', 'Rx [nrad]', 'Ry [nrad]'}, ' %.0f ');
#+end_src
#+RESULTS:
| | Dx [nm] | Dy [nm] | Dz [nm] | Rx [nrad] | Ry [nrad] |
|----+---------+---------+---------+-----------+-----------|
| m0 | 820 | 39 | 13 | 7790 | 156 |
#+begin_src matlab :exports none :results none
%% description
figure;
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile;
hold on;
plot(1e9*data_m0.Dy_int, 1e9*data_m0.Dz_int);
hold off;
axis square
xlabel("Y motion [$nm$]");
ylabel("Z motion [$nm$]");
#+end_src
** m1
*** 3x3 plant in Cartesian plane
#+begin_src matlab :exports none
%% Load model
load('Gm.mat')
#+end_src
#+begin_src matlab
%% Jacobian for 3x3 plant
n_hexapod = initializeNanoHexapod('flex_bot_type', '2dof', ...
'flex_top_type', '3dof', ...
'motion_sensor_type', 'plates', ...
'actuator_type', '2dof');
Jt_inv = pinv(n_hexapod.geometry.J');
J_int_to_X = [0.78740157480315 0.21259842519685 0 0 0;
0 0 0 0 -1;
0 0 -13.1233595800525 13.1233595800525 0];
#+end_src
Compute identified plant in the Cartesian plane:
#+begin_src matlab
%% New data after calibrating the Rz-offset
data_m1 = load(sprintf('%s/dynamics/2023-08-21_19-05_damp_plant_m1_new_Rz.mat', mat_dir));
#+end_src
#+begin_src matlab :exports none
% Sampling Time [s]
Ts = 1e-4;
% Hannning Windows
Nfft = floor(1.0/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
#+end_src
#+begin_src matlab :exports none
% And we get the frequency vector
[~, f] = tfestimate(data_m1.uL1.id_plant, data_m1.uL1.d1, win, Noverlap, Nfft, 1/Ts);
#+end_src
#+begin_src matlab :exports none
%% HAC Cartesian Plant
G_hac_m1 = zeros(length(f), 5, 6);
for i_strut = 1:6
Di = [data_m1.(sprintf("uL%i", i_strut)).d1 ; data_m1.(sprintf("uL%i", i_strut)).d2 ; data_m1.(sprintf("uL%i", i_strut)).d3 ; data_m1.(sprintf("uL%i", i_strut)).d4 ; data_m1.(sprintf("uL%i", i_strut)).d5]';
G_hac_m1(:,:,i_strut) = tfestimate(data_m1.(sprintf("uL%i", i_strut)).id_plant, detrend(Di, 0), win, Noverlap, Nfft, 1/Ts);
end
G_cart_m1 = permute(pagemtimes(J_int_to_X, pagemtimes(permute(G_hac_m1, [2,3,1]), Jt_inv(:,[2,3,5]))), [3,1,2]);
#+end_src
Compute plant model in the Cartesian plane:
#+begin_src matlab
Gm_cart_m1 = J_int_to_X*Gm_hac_m1({'d1', 'd2', 'd3', 'd4', 'd5'}, {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'})*Jt_inv(:,[2,3,5]);
Gm_cart_m1.InputName = {'Fy', 'Fz', 'My'};
Gm_cart_m1.OutputName = {'Dy', 'Dz', 'Ry'};
#+end_src
#+begin_src matlab :exports none :results none
%% 3x3 plant
figure;
tiledlayout(3, 3, 'TileSpacing', 'compact', 'Padding', 'None');
ax11 = nexttile();
hold on;
plot(f, abs(G_cart_m1(:,1,1)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m1('Dy', 'Fy'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('$D_y$'); set(gca, 'XTickLabel',[]);
title('$F_y$')
ax12 = nexttile();
hold on;
plot(f, abs(G_cart_m1(:,1,2)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m1('Dy', 'Fz'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
title('$F_z$')
ax13 = nexttile();
hold on;
plot(f, abs(G_cart_m1(:,1,3)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m1('Dy', 'My'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
title('$M_y$')
linkaxes([ax11,ax12,ax13],'y');
ylim([1e-8, 2e-4]);
ax21 = nexttile();
hold on;
plot(f, abs(G_cart_m1(:,2,1)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m1('Dz', 'Fy'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('$F_z$'); set(gca, 'XTickLabel',[]);
ax22 = nexttile();
hold on;
plot(f, abs(G_cart_m1(:,2,2)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m1('Dz', 'Fz'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
ax23 = nexttile();
hold on;
plot(f, abs(G_cart_m1(:,2,3)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m1('Dz', 'My'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
linkaxes([ax21,ax22,ax23],'y');
ylim([1e-8, 2e-4]);
ax31 = nexttile();
hold on;
plot(f, abs(G_cart_m1(:,3,1)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m1('Ry', 'Fy'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('$M_y$');
ax32 = nexttile();
hold on;
plot(f, abs(G_cart_m1(:,3,2)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m1('Ry', 'Fz'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
ax33 = nexttile();
hold on;
plot(f, abs(G_cart_m1(:,3,3)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m1('Ry', 'My'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
linkaxes([ax31,ax32,ax33],'y');
ylim([1e-9, 1e-2]);
linkaxes([ax11,ax12,ax13,ax21,ax22,ax23,ax31,ax32,ax33],'x');
xlim([1, 5e2]);
#+end_src
Normalization of outputs:
#+begin_src matlab
Gm_cart_m1_normalized = -diag(1./diag(dcgain(Gm_cart_m1)))*Gm_cart_m1;
Gm_cart_m1_normalized.InputName = {'Fy', 'Fz', 'My'};
Gm_cart_m1_normalized.OutputName = {'Dy', 'Dz', 'Ry'};
G_cart_m1_normalized = permute(pagemtimes(-diag(1./diag(dcgain(Gm_cart_m1))), permute(G_cart_m1, [2,3,1])), [3,1,2]);
#+end_src
#+begin_src matlab :exports none :results none
%% 3x3 plant
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_cart_m1_normalized(:,1,1)));
plot(f, abs(G_cart_m1_normalized(:,2,2)));
plot(f, abs(G_cart_m1_normalized(:,3,3)));
plot(f, abs(G_cart_m1_normalized(:,1,2)), 'color', [0,0,0,0.2]);
plot(f, abs(G_cart_m1_normalized(:,1,3)), 'color', [0,0,0,0.2]);
plot(f, abs(G_cart_m1_normalized(:,2,1)), 'color', [0,0,0,0.2]);
plot(f, abs(G_cart_m1_normalized(:,3,1)), 'color', [0,0,0,0.2]);
plot(f, abs(G_cart_m1_normalized(:,2,3)), 'color', [0,0,0,0.2]);
plot(f, abs(G_cart_m1_normalized(:,3,2)), 'color', [0,0,0,0.2]);
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude');
ylim([1e-4, 1e1]);
ax2 = nexttile();
hold on;
plot(f, 180/pi*angle(G_cart_m1_normalized(:,1,1)));
plot(f, 180/pi*angle(G_cart_m1_normalized(:,2,2)));
plot(f, 180/pi*angle(G_cart_m1_normalized(:,3,3)));
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
*** Better plant invert
#+begin_src matlab :exports none
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)
%% We define the number of iteration.
Niter = 100;
#+end_src
#+begin_src matlab
N = 9; %Order of approximation
#+end_src
**** Dy
#+begin_src matlab :exports none
%Complex conjugate pairs, linearly spaced:
bet=linspace(f(1),f(end),N/2);
poles=[];
for n=1:length(bet)
alf=-bet(n)*1e-2;
poles=[poles (alf-i*bet(n)) (alf+i*bet(n)) ];
end
#+end_src
#+begin_src matlab
%% Estimate resonance frequency and damping
for iter =1:Niter
[G_est, poles, ~, frf_est] = vectfit3(G_cart_m1(f>1&f<1500,1,1).', 1i*2*pi*f(f>1&f<1500)', poles, 1./sqrt(f(f>1&f<1500))', opts);
end
Gfit_dy = ss(G_est.A, G_est.B, G_est.C, G_est.D);
#+end_src
#+begin_src matlab :exports none :results none
%% Comparaison of the measured direct terms and the reduced order models
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_cart_m1(:,1,1)), 'color', [colors(1,:)], 'DisplayName', '$D_y$');
plot(f, abs(squeeze(freqresp(Gfit_dy, f, 'Hz'))), '--', 'color', [colors(1,:)], 'HandleVisibility', 'off');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude');
legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(G_cart_m1(:,1,1)), 'color', [colors(1,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Gfit_dy, f, 'Hz'))), '--', 'color', [colors(1,:)]);
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 2e3]);
#+end_src
Stable Inverse
#+begin_src matlab
Ginv_dy = inv(flipRphZeros(Gfit_dy))/(1 + s/2/pi/1e3);
isstable(Ginv_dy)
#+end_src
**** Dz
#+begin_src matlab :exports none
%Complex conjugate pairs, linearly spaced:
bet=linspace(f(1),f(end),N/2);
poles=[];
for n=1:length(bet)
alf=-bet(n)*1e-1;
poles=[poles (alf-i*bet(n)) (alf+i*bet(n)) ];
end
#+end_src
#+begin_src matlab
%% Estimate resonance frequency and damping
for iter =1:Niter
[G_est, poles, ~, frf_est] = vectfit3(G_cart_m1(f>2&f<1500,2,2).', 1i*2*pi*f(f>2&f<1500)', poles, 1./sqrt(f(f>2&f<1500))', opts);
end
Gfit_dz = ss(G_est.A, G_est.B, G_est.C, G_est.D);
#+end_src
#+begin_src matlab :exports none :results none
%% Comparaison of the measured direct terms and the reduced order models
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_cart_m1(:,2,2)), 'color', [colors(2,:)], 'DisplayName', '$D_z$');
plot(f, abs(squeeze(freqresp(Gfit_dz, f, 'Hz'))), '--', 'color', [colors(2,:)], 'HandleVisibility', 'off');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude');
legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(G_cart_m1(:,2,2)), 'color', [colors(2,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Gfit_dz, f, 'Hz'))), '--', 'color', [colors(2,:)]);
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 2e3]);
#+end_src
Stable Inverse
#+begin_src matlab
Ginv_dz = inv(-flipRphZeros(Gfit_dz))/(1 + s/2/pi/1e3);
isstable(Ginv_dz)
#+end_src
**** Ry
#+begin_src matlab :exports none
%Complex conjugate pairs, linearly spaced:
bet=linspace(f(1),f(end),N/2);
poles=[];
for n=1:length(bet)
alf=-bet(n)*1e-1;
poles=[poles (alf-i*bet(n)) (alf+i*bet(n)) ];
end
#+end_src
#+begin_src matlab
%% Estimate resonance frequency and damping
for iter =1:Niter
[G_est, poles, ~, frf_est] = vectfit3(G_cart_m1(f>2&f<800,3,3).', 1i*2*pi*f(f>2&f<800)', poles, 1./(f(f>2&f<800))', opts);
end
Gfit_ry = ss(G_est.A, G_est.B, G_est.C, G_est.D);
#+end_src
#+begin_src matlab :exports none :results none
%% Comparaison of the measured direct terms and the reduced order models
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_cart_m1(:,3,3)), 'color', [colors(3,:)], 'DisplayName', '$R_y$');
plot(f, abs(squeeze(freqresp(Gfit_ry, f, 'Hz'))), '--', 'color', [colors(3,:)], 'HandleVisibility', 'off');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude');
legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(G_cart_m1(:,3,3)), 'color', [colors(3,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Gfit_ry, f, 'Hz'))), '--', 'color', [colors(3,:)]);
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 2e3]);
#+end_src
Stable Inverse
#+begin_src matlab
Ginv_ry = inv(flipRphZeros(Gfit_ry))/(1 + s/2/pi/1e3);
isstable(Ginv_ry)
#+end_src
**** Compare Invert plants
#+begin_src matlab :exports none :results none
%% Comparaison of the measured direct terms and the reduced order models
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, 1./abs(G_cart_m1(:,1,1)), 'color', [colors(1,:)], 'DisplayName', '$D_y$');
plot(f, abs(squeeze(freqresp(Ginv_dy, f, 'Hz'))), '--', 'color', [colors(1,:)], 'HandleVisibility', 'off');
plot(f, 1./abs(G_cart_m1(:,2,2)), 'color', [colors(2,:)], 'DisplayName', '$D_z$');
plot(f, abs(squeeze(freqresp(Ginv_dz, f, 'Hz'))), '--', 'color', [colors(2,:)], 'HandleVisibility', 'off');
plot(f, 1./abs(G_cart_m1(:,3,3)), 'color', [colors(3,:)], 'DisplayName', '$R_y$');
plot(f, abs(squeeze(freqresp(Ginv_ry, f, 'Hz'))), '--', 'color', [colors(3,:)], 'HandleVisibility', 'off');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude');
legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(1./G_cart_m1(:,1,1)), 'color', [colors(1,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Ginv_dy, f, 'Hz'))), '--', 'color', [colors(1,:)]);
plot(f, 180/pi*angle(1./G_cart_m1(:,2,2)), 'color', [colors(2,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Ginv_dz, f, 'Hz'))), '--', 'color', [colors(2,:)]);
plot(f, 180/pi*angle(1./G_cart_m1(:,3,3)), 'color', [colors(3,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Ginv_ry, f, 'Hz'))), '--', 'color', [colors(3,:)]);
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 2e3]);
#+end_src
#+begin_src matlab :exports none :results none
%% Comparaison of the measured direct terms and the reduced order models
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(squeeze(freqresp(Ginv_dy, f, 'Hz')).*abs(G_cart_m1(:,1,1))), '-', 'DisplayName', '$D_y$');
plot(f, abs(squeeze(freqresp(Ginv_dz, f, 'Hz')).*abs(G_cart_m1(:,2,2))), '-', 'DisplayName', '$D_z$');
plot(f, abs(squeeze(freqresp(Ginv_ry, f, 'Hz')).*abs(G_cart_m1(:,3,3))), '-', 'DisplayName', '$R_y$');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude');
legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(squeeze(freqresp(Ginv_dy, f, 'Hz')).*G_cart_m1(:,1,1)), '-');
plot(f, 180/pi*angle(squeeze(freqresp(Ginv_dz, f, 'Hz')).*G_cart_m1(:,2,2)), '-');
plot(f, 180/pi*angle(squeeze(freqresp(Ginv_ry, f, 'Hz')).*G_cart_m1(:,3,3)), '-');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 2e3]);
#+end_src
**** Save plant inverse
#+begin_src matlab :exports none :tangle no
K = -Ginv_dy;
K_order = order(K);
Kz_dy = c2d(K*(1 + s/2/pi/2e3)^(9-K_order)/(1 + s/2/pi/2e3)^(9-K_order), 1e-4, 'tustin');
[num, den] = tfdata(Kz_dy, 'v');
formatSpec = '%.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e\n';
fileID = fopen('/home/thomas/mnt/data_id31/nass/controllers/G_dy_inv_m1.dat', 'w');
fprintf(fileID, formatSpec, [num; den]');
fclose(fileID);
#+end_src
#+begin_src matlab :exports none :tangle no
K = -Ginv_dz;
K_order = order(K);
Kz_dz = c2d(K*(1 + s/2/pi/2e3)^(9-K_order)/(1 + s/2/pi/2e3)^(9-K_order), 1e-4, 'tustin');
[num, den] = tfdata(Kz_dz, 'v');
formatSpec = '%.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e\n';
fileID = fopen('/home/thomas/mnt/data_id31/nass/controllers/G_dz_inv_m1.dat', 'w');
fprintf(fileID, formatSpec, [num; den]');
fclose(fileID);
#+end_src
#+begin_src matlab :exports none :tangle no
K = -Ginv_ry;
K_order = order(K);
Kz_ry = c2d(K*(1 + s/2/pi/2e3)^(9-K_order)/(1 + s/2/pi/2e3)^(9-K_order), 1e-4, 'tustin');
[num, den] = tfdata(Kz_ry, 'v');
formatSpec = '%.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e\n';
fileID = fopen('/home/thomas/mnt/data_id31/nass/controllers/G_ry_inv_m1.dat', 'w');
fprintf(fileID, formatSpec, [num; den]');
fclose(fileID);
#+end_src
**** Compare Digital Invert plants
#+begin_src matlab :exports none :results none
%% Comparaison of the measured direct terms and the reduced order models
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, 1./abs(G_cart_m1(:,1,1)), 'color', [colors(1,:)], 'DisplayName', '$D_y$');
plot(f, abs(squeeze(freqresp(Kz_dy, f, 'Hz'))), '--', 'color', [colors(1,:)], 'HandleVisibility', 'off');
plot(f, 1./abs(G_cart_m1(:,2,2)), 'color', [colors(2,:)], 'DisplayName', '$D_z$');
plot(f, abs(squeeze(freqresp(Kz_dz, f, 'Hz'))), '--', 'color', [colors(2,:)], 'HandleVisibility', 'off');
plot(f, 1./abs(G_cart_m1(:,3,3)), 'color', [colors(3,:)], 'DisplayName', '$R_y$');
plot(f, abs(squeeze(freqresp(Kz_ry, f, 'Hz'))), '--', 'color', [colors(3,:)], 'HandleVisibility', 'off');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude');
legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(1./G_cart_m1(:,1,1)), 'color', [colors(1,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Kz_dy, f, 'Hz'))), '--', 'color', [colors(1,:)]);
plot(f, 180/pi*angle(1./G_cart_m1(:,2,2)), 'color', [colors(2,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Kz_dz, f, 'Hz'))), '--', 'color', [colors(2,:)]);
plot(f, 180/pi*angle(1./G_cart_m1(:,3,3)), 'color', [colors(3,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Kz_ry, f, 'Hz'))), '--', 'color', [colors(3,:)]);
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 2e3]);
#+end_src
*** TODO Control Performances
**** Better plant invert
#+begin_src matlab
data_cl = load(sprintf('%s/dynamics/2023-08-21_13-36_m0_cf_inv_fit.mat', mat_dir));
#+end_src
#+begin_src matlab :exports none
% Sampling Time [s]
Ts = 1e-4;
% Hannning Windows
Nfft = floor(1.0/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
#+end_src
#+begin_src matlab :exports none
% And we get the frequency vector
[S_dy, f] = tfestimate(data_cl.Dy.id_cl, data_cl.Dy.e_dy, win, Noverlap, Nfft, 1/Ts);
[S_dz, ~] = tfestimate(data_cl.Dz.id_cl, data_cl.Dz.e_dz, win, Noverlap, Nfft, 1/Ts);
[S_ry, ~] = tfestimate(data_cl.Ry.id_cl, data_cl.Ry.e_ry, win, Noverlap, Nfft, 1/Ts);
#+end_src
#+begin_src matlab :exports none :results none
%% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor
figure;
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile();
hold on;
plot(f, abs(S_dy), 'DisplayName', '$D_y$');
plot(f, abs(S_dz), 'DisplayName', '$D_z$');
plot(f, abs(S_ry), 'DisplayName', '$R_y$');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('Amplitude');
ylim([1e-3, 1e1]);
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 3);
xlim([1, 1e3]);
#+end_src
*** TODO Scans with good controller
**** 1rpm
1RPM scans are performed for all the masses with the same controller.
#+begin_src matlab
data_m0 = load(sprintf("%s/scans/2023-08-21_14-28_m0_1rpm_K_cf.mat", mat_dir));
data_m0.time = Ts*[0:length(data_m0.Rz)-1];
#+end_src
#+begin_src matlab
%% Compute RMS values while in closed-loop
data_m0.Dx_rms_cl = rms(detrend(data_m0.Dx_int, 0));
data_m0.Dy_rms_cl = rms(detrend(data_m0.Dy_int, 0));
data_m0.Dz_rms_cl = rms(detrend(data_m0.Dz_int, 0));
data_m0.Rx_rms_cl = rms(detrend(data_m0.Rx_int, 0));
data_m0.Ry_rms_cl = rms(detrend(data_m0.Ry_int, 0));
#+end_src
#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*)
data2orgtable([1e9*data_m0.Dx_rms_cl, 1e9*data_m0.Dy_rms_cl, 1e9*data_m0.Dz_rms_cl, 1e9*data_m0.Rx_rms_cl, 1e9*data_m0.Ry_rms_cl], ...
{'m0'}, {'Dx [nm]', 'Dy [nm]', 'Dz [nm]', 'Rx [nrad]', 'Ry [nrad]'}, ' %.0f ');
#+end_src
#+RESULTS:
| | Dx [nm] | Dy [nm] | Dz [nm] | Rx [nrad] | Ry [nrad] |
|----+---------+---------+---------+-----------+-----------|
| m0 | 796 | 20 | 8 | 8209 | 73 |
#+begin_src matlab :exports none :results none
%% description
figure;
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile;
hold on;
plot(1e9*data_m0.Dy_int, 1e9*data_m0.Dz_int);
hold off;
axis square
xlabel("Y motion [$nm$]");
ylabel("Z motion [$nm$]");
#+end_src
**** 30rpm
1RPM scans are performed for all the masses with the same controller.
#+begin_src matlab
data_m0 = load(sprintf("%s/scans/2023-08-21_14-33_m0_30rpm_cf_control.mat", mat_dir));
data_m0.time = Ts*[0:length(data_m0.Rz)-1];
#+end_src
#+begin_src matlab
%% Compute RMS values while in closed-loop
data_m0.Dx_rms_cl = rms(detrend(data_m0.Dx_int, 0));
data_m0.Dy_rms_cl = rms(detrend(data_m0.Dy_int, 0));
data_m0.Dz_rms_cl = rms(detrend(data_m0.Dz_int, 0));
data_m0.Rx_rms_cl = rms(detrend(data_m0.Rx_int, 0));
data_m0.Ry_rms_cl = rms(detrend(data_m0.Ry_int, 0));
#+end_src
#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*)
data2orgtable([1e9*data_m0.Dx_rms_cl, 1e9*data_m0.Dy_rms_cl, 1e9*data_m0.Dz_rms_cl, 1e9*data_m0.Rx_rms_cl, 1e9*data_m0.Ry_rms_cl], ...
{'m0'}, {'Dx [nm]', 'Dy [nm]', 'Dz [nm]', 'Rx [nrad]', 'Ry [nrad]'}, ' %.0f ');
#+end_src
#+RESULTS:
| | Dx [nm] | Dy [nm] | Dz [nm] | Rx [nrad] | Ry [nrad] |
|----+---------+---------+---------+-----------+-----------|
| m0 | 820 | 39 | 13 | 7790 | 156 |
#+begin_src matlab :exports none :results none
%% description
figure;
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile;
hold on;
plot(1e9*data_m0.Dy_int, 1e9*data_m0.Dz_int);
hold off;
axis square
xlabel("Y motion [$nm$]");
ylabel("Z motion [$nm$]");
#+end_src
** m2
*** 3x3 plant in Cartesian plane
#+begin_src matlab :exports none
%% Load model
load('Gm.mat')
#+end_src
#+begin_src matlab
%% Jacobian for 3x3 plant
n_hexapod = initializeNanoHexapod('flex_bot_type', '2dof', ...
'flex_top_type', '3dof', ...
'motion_sensor_type', 'plates', ...
'actuator_type', '2dof');
Jt_inv = pinv(n_hexapod.geometry.J');
J_int_to_X = [0.78740157480315 0.21259842519685 0 0 0;
0 0 0 0 -1;
0 0 -13.1233595800525 13.1233595800525 0];
#+end_src
Compute identified plant in the Cartesian plane:
#+begin_src matlab
%% New data after calibrating the Rz-offset
data_m2 = load(sprintf('%s/dynamics/2023-08-21_17-32_damp_plant_m2_new_Rz.mat', mat_dir));
#+end_src
#+begin_src matlab :exports none
% Sampling Time [s]
Ts = 1e-4;
% Hannning Windows
Nfft = floor(1.0/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
#+end_src
#+begin_src matlab :exports none
% And we get the frequency vector
[~, f] = tfestimate(data_m2.uL1.id_plant, data_m2.uL1.d1, win, Noverlap, Nfft, 1/Ts);
#+end_src
#+begin_src matlab :exports none
%% HAC Cartesian Plant
G_hac_m2 = zeros(length(f), 5, 6);
for i_strut = 1:6
Di = [data_m2.(sprintf("uL%i", i_strut)).d1 ; data_m2.(sprintf("uL%i", i_strut)).d2 ; data_m2.(sprintf("uL%i", i_strut)).d3 ; data_m2.(sprintf("uL%i", i_strut)).d4 ; data_m2.(sprintf("uL%i", i_strut)).d5]';
G_hac_m2(:,:,i_strut) = tfestimate(data_m2.(sprintf("uL%i", i_strut)).id_plant, detrend(Di, 0), win, Noverlap, Nfft, 1/Ts);
end
G_cart_m2 = permute(pagemtimes(J_int_to_X, pagemtimes(permute(G_hac_m2, [2,3,1]), Jt_inv(:,[2,3,5]))), [3,1,2]);
#+end_src
Compute plant model in the Cartesian plane:
#+begin_src matlab
Gm_cart_m2 = J_int_to_X*Gm_hac_m2({'d1', 'd2', 'd3', 'd4', 'd5'}, {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'})*Jt_inv(:,[2,3,5]);
Gm_cart_m2.InputName = {'Fy', 'Fz', 'My'};
Gm_cart_m2.OutputName = {'Dy', 'Dz', 'Ry'};
#+end_src
#+begin_src matlab :exports none :results none
%% 3x3 plant
figure;
tiledlayout(3, 3, 'TileSpacing', 'compact', 'Padding', 'None');
ax11 = nexttile();
hold on;
plot(f, abs(G_cart_m2(:,1,1)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m2('Dy', 'Fy'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('$D_y$'); set(gca, 'XTickLabel',[]);
title('$F_y$')
ax12 = nexttile();
hold on;
plot(f, abs(G_cart_m2(:,1,2)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m2('Dy', 'Fz'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
title('$F_z$')
ax13 = nexttile();
hold on;
plot(f, abs(G_cart_m2(:,1,3)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m2('Dy', 'My'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
title('$M_y$')
linkaxes([ax11,ax12,ax13],'y');
ylim([1e-8, 2e-4]);
ax21 = nexttile();
hold on;
plot(f, abs(G_cart_m2(:,2,1)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m2('Dz', 'Fy'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('$F_z$'); set(gca, 'XTickLabel',[]);
ax22 = nexttile();
hold on;
plot(f, abs(G_cart_m2(:,2,2)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m2('Dz', 'Fz'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
ax23 = nexttile();
hold on;
plot(f, abs(G_cart_m2(:,2,3)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m2('Dz', 'My'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
linkaxes([ax21,ax22,ax23],'y');
ylim([1e-8, 2e-4]);
ax31 = nexttile();
hold on;
plot(f, abs(G_cart_m2(:,3,1)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m2('Ry', 'Fy'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('$M_y$');
ax32 = nexttile();
hold on;
plot(f, abs(G_cart_m2(:,3,2)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m2('Ry', 'Fz'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
ax33 = nexttile();
hold on;
plot(f, abs(G_cart_m2(:,3,3)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m2('Ry', 'My'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
linkaxes([ax31,ax32,ax33],'y');
ylim([1e-9, 1e-2]);
linkaxes([ax11,ax12,ax13,ax21,ax22,ax23,ax31,ax32,ax33],'x');
xlim([1, 5e2]);
#+end_src
Normalization of outputs:
#+begin_src matlab
Gm_cart_m2_normalized = -diag(1./diag(dcgain(Gm_cart_m2)))*Gm_cart_m2;
Gm_cart_m2_normalized.InputName = {'Fy', 'Fz', 'My'};
Gm_cart_m2_normalized.OutputName = {'Dy', 'Dz', 'Ry'};
G_cart_m2_normalized = permute(pagemtimes(-diag(1./diag(dcgain(Gm_cart_m2))), permute(G_cart_m2, [2,3,1])), [3,1,2]);
#+end_src
#+begin_src matlab :exports none :results none
%% 3x3 plant
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_cart_m2_normalized(:,1,1)));
plot(f, abs(G_cart_m2_normalized(:,2,2)));
plot(f, abs(G_cart_m2_normalized(:,3,3)));
plot(f, abs(G_cart_m2_normalized(:,1,2)), 'color', [0,0,0,0.2]);
plot(f, abs(G_cart_m2_normalized(:,1,3)), 'color', [0,0,0,0.2]);
plot(f, abs(G_cart_m2_normalized(:,2,1)), 'color', [0,0,0,0.2]);
plot(f, abs(G_cart_m2_normalized(:,3,1)), 'color', [0,0,0,0.2]);
plot(f, abs(G_cart_m2_normalized(:,2,3)), 'color', [0,0,0,0.2]);
plot(f, abs(G_cart_m2_normalized(:,3,2)), 'color', [0,0,0,0.2]);
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude');
ylim([1e-4, 1e1]);
ax2 = nexttile();
hold on;
plot(f, 180/pi*angle(G_cart_m2_normalized(:,1,1)));
plot(f, 180/pi*angle(G_cart_m2_normalized(:,2,2)));
plot(f, 180/pi*angle(G_cart_m2_normalized(:,3,3)));
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
*** Better plant invert
#+begin_src matlab :exports none
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)
%% We define the number of iteration.
Niter = 100;
#+end_src
#+begin_src matlab
N = 9; %Order of approximation
#+end_src
**** Dy
#+begin_src matlab :exports none
%Complex conjugate pairs, linearly spaced:
bet=linspace(f(1),f(end),N/2);
poles=[];
for n=1:length(bet)
alf=-bet(n)*1e-2;
poles=[poles (alf-i*bet(n)) (alf+i*bet(n)) ];
end
#+end_src
#+begin_src matlab
%% Estimate resonance frequency and damping
for iter =1:Niter
[G_est, poles, ~, frf_est] = vectfit3(G_cart_m2(f>1&f<1500,1,1).', 1i*2*pi*f(f>1&f<1500)', poles, 1./sqrt(f(f>1&f<1500))', opts);
end
Gfit_dy = ss(G_est.A, G_est.B, G_est.C, G_est.D);
#+end_src
#+begin_src matlab :exports none :results none
%% Comparaison of the measured direct terms and the reduced order models
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_cart_m2(:,1,1)), 'color', [colors(1,:)], 'DisplayName', '$D_y$');
plot(f, abs(squeeze(freqresp(Gfit_dy, f, 'Hz'))), '--', 'color', [colors(1,:)], 'HandleVisibility', 'off');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude');
legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(G_cart_m2(:,1,1)), 'color', [colors(1,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Gfit_dy, f, 'Hz'))), '--', 'color', [colors(1,:)]);
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 2e3]);
#+end_src
Stable Inverse
#+begin_src matlab
Ginv_dy = inv(-flipRphZeros(Gfit_dy))/(1 + s/2/pi/1e3);
isstable(Ginv_dy)
#+end_src
**** Dz
#+begin_src matlab :exports none
%Complex conjugate pairs, linearly spaced:
bet=linspace(f(1),f(end),N/2);
poles=[];
for n=1:length(bet)
alf=-bet(n)*1e-1;
poles=[poles (alf-i*bet(n)) (alf+i*bet(n)) ];
end
#+end_src
#+begin_src matlab
%% Estimate resonance frequency and damping
for iter =1:Niter
[G_est, poles, ~, frf_est] = vectfit3(G_cart_m2(f>2&f<1500,2,2).', 1i*2*pi*f(f>2&f<1500)', poles, 1./(f(f>2&f<1500))', opts);
end
Gfit_dz = ss(G_est.A, G_est.B, G_est.C, G_est.D);
#+end_src
#+begin_src matlab :exports none :results none
%% Comparaison of the measured direct terms and the reduced order models
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_cart_m2(:,2,2)), 'color', [colors(2,:)], 'DisplayName', '$D_z$');
plot(f, abs(squeeze(freqresp(Gfit_dz, f, 'Hz'))), '--', 'color', [colors(2,:)], 'HandleVisibility', 'off');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude');
legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(G_cart_m2(:,2,2)), 'color', [colors(2,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Gfit_dz, f, 'Hz'))), '--', 'color', [colors(2,:)]);
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 2e3]);
#+end_src
Stable Inverse
#+begin_src matlab
Ginv_dz = inv(-flipRphZeros(Gfit_dz))/(1 + s/2/pi/1e3);
isstable(Ginv_dz)
#+end_src
**** Ry
#+begin_src matlab :exports none
%Complex conjugate pairs, linearly spaced:
bet=linspace(f(1),f(end),N/2);
poles=[];
for n=1:length(bet)
alf=-bet(n)*1e-1;
poles=[poles (alf-i*bet(n)) (alf+i*bet(n)) ];
end
#+end_src
#+begin_src matlab
%% Estimate resonance frequency and damping
for iter =1:Niter
[G_est, poles, ~, frf_est] = vectfit3(G_cart_m2(f>2&f<800,3,3).', 1i*2*pi*f(f>2&f<800)', poles, 1./(f(f>2&f<800))', opts);
end
Gfit_ry = ss(G_est.A, G_est.B, G_est.C, G_est.D);
#+end_src
#+begin_src matlab :exports none :results none
%% Comparaison of the measured direct terms and the reduced order models
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_cart_m2(:,3,3)), 'color', [colors(3,:)], 'DisplayName', '$R_y$');
plot(f, abs(squeeze(freqresp(Gfit_ry, f, 'Hz'))), '--', 'color', [colors(3,:)], 'HandleVisibility', 'off');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude');
legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(G_cart_m2(:,3,3)), 'color', [colors(3,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Gfit_ry, f, 'Hz'))), '--', 'color', [colors(3,:)]);
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 2e3]);
#+end_src
Stable Inverse
#+begin_src matlab
Ginv_ry = inv(-flipRphZeros(Gfit_ry))/(1 + s/2/pi/1e3);
isstable(Ginv_ry)
#+end_src
**** Compare Invert plants
#+begin_src matlab :exports none :results none
%% Comparaison of the measured direct terms and the reduced order models
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, 1./abs(G_cart_m2(:,1,1)), 'color', [colors(1,:)], 'DisplayName', '$D_y$');
plot(f, abs(squeeze(freqresp(Ginv_dy, f, 'Hz'))), '--', 'color', [colors(1,:)], 'HandleVisibility', 'off');
plot(f, 1./abs(G_cart_m2(:,2,2)), 'color', [colors(2,:)], 'DisplayName', '$D_z$');
plot(f, abs(squeeze(freqresp(Ginv_dz, f, 'Hz'))), '--', 'color', [colors(2,:)], 'HandleVisibility', 'off');
plot(f, 1./abs(G_cart_m2(:,3,3)), 'color', [colors(3,:)], 'DisplayName', '$R_y$');
plot(f, abs(squeeze(freqresp(Ginv_ry, f, 'Hz'))), '--', 'color', [colors(3,:)], 'HandleVisibility', 'off');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude');
legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(1./G_cart_m2(:,1,1)), 'color', [colors(1,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Ginv_dy, f, 'Hz'))), '--', 'color', [colors(1,:)]);
plot(f, 180/pi*angle(1./G_cart_m2(:,2,2)), 'color', [colors(2,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Ginv_dz, f, 'Hz'))), '--', 'color', [colors(2,:)]);
plot(f, 180/pi*angle(1./G_cart_m2(:,3,3)), 'color', [colors(3,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Ginv_ry, f, 'Hz'))), '--', 'color', [colors(3,:)]);
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 2e3]);
#+end_src
#+begin_src matlab :exports none :results none
%% Comparaison of the measured direct terms and the reduced order models
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(squeeze(freqresp(Ginv_dy, f, 'Hz')).*abs(G_cart_m2(:,1,1))), '-', 'DisplayName', '$D_y$');
plot(f, abs(squeeze(freqresp(Ginv_dz, f, 'Hz')).*abs(G_cart_m2(:,2,2))), '-', 'DisplayName', '$D_z$');
plot(f, abs(squeeze(freqresp(Ginv_ry, f, 'Hz')).*abs(G_cart_m2(:,3,3))), '-', 'DisplayName', '$R_y$');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude');
legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(squeeze(freqresp(Ginv_dy, f, 'Hz')).*G_cart_m2(:,1,1)), '-');
plot(f, 180/pi*angle(squeeze(freqresp(Ginv_dz, f, 'Hz')).*G_cart_m2(:,2,2)), '-');
plot(f, 180/pi*angle(squeeze(freqresp(Ginv_ry, f, 'Hz')).*G_cart_m2(:,3,3)), '-');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 2e3]);
#+end_src
**** Save plant inverse
#+begin_src matlab :exports none :tangle no
K = -Ginv_dy;
K_order = order(K);
Kz_dy = c2d(K*(1 + s/2/pi/2e3)^(9-K_order)/(1 + s/2/pi/2e3)^(9-K_order), 1e-4, 'tustin');
[num, den] = tfdata(Kz_dy, 'v');
formatSpec = '%.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e\n';
fileID = fopen('/home/thomas/mnt/data_id31/nass/controllers/G_dy_inv_m2.dat', 'w');
fprintf(fileID, formatSpec, [num; den]');
fclose(fileID);
#+end_src
#+begin_src matlab :exports none :tangle no
K = -Ginv_dz;
K_order = order(K);
Kz_dz = c2d(K*(1 + s/2/pi/2e3)^(9-K_order)/(1 + s/2/pi/2e3)^(9-K_order), 1e-4, 'tustin');
[num, den] = tfdata(Kz_dz, 'v');
formatSpec = '%.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e\n';
fileID = fopen('/home/thomas/mnt/data_id31/nass/controllers/G_dz_inv_m2.dat', 'w');
fprintf(fileID, formatSpec, [num; den]');
fclose(fileID);
#+end_src
#+begin_src matlab :exports none :tangle no
K = -Ginv_ry;
K_order = order(K);
Kz_ry = c2d(K*(1 + s/2/pi/2e3)^(9-K_order)/(1 + s/2/pi/2e3)^(9-K_order), 1e-4, 'tustin');
[num, den] = tfdata(Kz_ry, 'v');
formatSpec = '%.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e\n';
fileID = fopen('/home/thomas/mnt/data_id31/nass/controllers/G_ry_inv_m2.dat', 'w');
fprintf(fileID, formatSpec, [num; den]');
fclose(fileID);
#+end_src
**** Compare Digital Invert plants
#+begin_src matlab :exports none :results none
%% Comparaison of the measured direct terms and the reduced order models
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, 1./abs(G_cart_m2(:,1,1)), 'color', [colors(1,:)], 'DisplayName', '$D_y$');
plot(f, abs(squeeze(freqresp(Kz_dy, f, 'Hz'))), '--', 'color', [colors(1,:)], 'HandleVisibility', 'off');
plot(f, 1./abs(G_cart_m2(:,2,2)), 'color', [colors(2,:)], 'DisplayName', '$D_z$');
plot(f, abs(squeeze(freqresp(Kz_dz, f, 'Hz'))), '--', 'color', [colors(2,:)], 'HandleVisibility', 'off');
plot(f, 1./abs(G_cart_m2(:,3,3)), 'color', [colors(3,:)], 'DisplayName', '$R_y$');
plot(f, abs(squeeze(freqresp(Kz_ry, f, 'Hz'))), '--', 'color', [colors(3,:)], 'HandleVisibility', 'off');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude');
legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(1./G_cart_m2(:,1,1)), 'color', [colors(1,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Kz_dy, f, 'Hz'))), '--', 'color', [colors(1,:)]);
plot(f, 180/pi*angle(1./G_cart_m2(:,2,2)), 'color', [colors(2,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Kz_dz, f, 'Hz'))), '--', 'color', [colors(2,:)]);
plot(f, 180/pi*angle(1./G_cart_m2(:,3,3)), 'color', [colors(3,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Kz_ry, f, 'Hz'))), '--', 'color', [colors(3,:)]);
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 2e3]);
#+end_src
*** TODO Control Performances
**** Better plant invert
#+begin_src matlab
data_cl = load(sprintf('%s/dynamics/2023-08-21_13-36_m0_cf_inv_fit.mat', mat_dir));
#+end_src
#+begin_src matlab :exports none
% Sampling Time [s]
Ts = 1e-4;
% Hannning Windows
Nfft = floor(1.0/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
#+end_src
#+begin_src matlab :exports none
% And we get the frequency vector
[S_dy, f] = tfestimate(data_cl.Dy.id_cl, data_cl.Dy.e_dy, win, Noverlap, Nfft, 1/Ts);
[S_dz, ~] = tfestimate(data_cl.Dz.id_cl, data_cl.Dz.e_dz, win, Noverlap, Nfft, 1/Ts);
[S_ry, ~] = tfestimate(data_cl.Ry.id_cl, data_cl.Ry.e_ry, win, Noverlap, Nfft, 1/Ts);
#+end_src
#+begin_src matlab :exports none :results none
%% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor
figure;
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile();
hold on;
plot(f, abs(S_dy), 'DisplayName', '$D_y$');
plot(f, abs(S_dz), 'DisplayName', '$D_z$');
plot(f, abs(S_ry), 'DisplayName', '$R_y$');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('Amplitude');
ylim([1e-3, 1e1]);
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 3);
xlim([1, 1e3]);
#+end_src
*** TODO Scans with good controller
**** 1rpm
1RPM scans are performed for all the masses with the same controller.
#+begin_src matlab
data_m0 = load(sprintf("%s/scans/2023-08-21_14-28_m0_1rpm_K_cf.mat", mat_dir));
data_m0.time = Ts*[0:length(data_m0.Rz)-1];
#+end_src
#+begin_src matlab
%% Compute RMS values while in closed-loop
data_m0.Dx_rms_cl = rms(detrend(data_m0.Dx_int, 0));
data_m0.Dy_rms_cl = rms(detrend(data_m0.Dy_int, 0));
data_m0.Dz_rms_cl = rms(detrend(data_m0.Dz_int, 0));
data_m0.Rx_rms_cl = rms(detrend(data_m0.Rx_int, 0));
data_m0.Ry_rms_cl = rms(detrend(data_m0.Ry_int, 0));
#+end_src
#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*)
data2orgtable([1e9*data_m0.Dx_rms_cl, 1e9*data_m0.Dy_rms_cl, 1e9*data_m0.Dz_rms_cl, 1e9*data_m0.Rx_rms_cl, 1e9*data_m0.Ry_rms_cl], ...
{'m0'}, {'Dx [nm]', 'Dy [nm]', 'Dz [nm]', 'Rx [nrad]', 'Ry [nrad]'}, ' %.0f ');
#+end_src
#+RESULTS:
| | Dx [nm] | Dy [nm] | Dz [nm] | Rx [nrad] | Ry [nrad] |
|----+---------+---------+---------+-----------+-----------|
| m0 | 796 | 20 | 8 | 8209 | 73 |
#+begin_src matlab :exports none :results none
%% description
figure;
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile;
hold on;
plot(1e9*data_m0.Dy_int, 1e9*data_m0.Dz_int);
hold off;
axis square
xlabel("Y motion [$nm$]");
ylabel("Z motion [$nm$]");
#+end_src
**** 30rpm
1RPM scans are performed for all the masses with the same controller.
#+begin_src matlab
data_m0 = load(sprintf("%s/scans/2023-08-21_14-33_m0_30rpm_cf_control.mat", mat_dir));
data_m0.time = Ts*[0:length(data_m0.Rz)-1];
#+end_src
#+begin_src matlab
%% Compute RMS values while in closed-loop
data_m0.Dx_rms_cl = rms(detrend(data_m0.Dx_int, 0));
data_m0.Dy_rms_cl = rms(detrend(data_m0.Dy_int, 0));
data_m0.Dz_rms_cl = rms(detrend(data_m0.Dz_int, 0));
data_m0.Rx_rms_cl = rms(detrend(data_m0.Rx_int, 0));
data_m0.Ry_rms_cl = rms(detrend(data_m0.Ry_int, 0));
#+end_src
#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*)
data2orgtable([1e9*data_m0.Dx_rms_cl, 1e9*data_m0.Dy_rms_cl, 1e9*data_m0.Dz_rms_cl, 1e9*data_m0.Rx_rms_cl, 1e9*data_m0.Ry_rms_cl], ...
{'m0'}, {'Dx [nm]', 'Dy [nm]', 'Dz [nm]', 'Rx [nrad]', 'Ry [nrad]'}, ' %.0f ');
#+end_src
#+RESULTS:
| | Dx [nm] | Dy [nm] | Dz [nm] | Rx [nrad] | Ry [nrad] |
|----+---------+---------+---------+-----------+-----------|
| m0 | 820 | 39 | 13 | 7790 | 156 |
#+begin_src matlab :exports none :results none
%% description
figure;
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile;
hold on;
plot(1e9*data_m0.Dy_int, 1e9*data_m0.Dz_int);
hold off;
axis square
xlabel("Y motion [$nm$]");
ylabel("Z motion [$nm$]");
#+end_src
** m3
*** 3x3 plant in Cartesian plane
#+begin_src matlab :exports none
%% Load model
load('Gm.mat')
#+end_src
#+begin_src matlab
%% Jacobian for 3x3 plant
n_hexapod = initializeNanoHexapod('flex_bot_type', '2dof', ...
'flex_top_type', '3dof', ...
'motion_sensor_type', 'plates', ...
'actuator_type', '2dof');
Jt_inv = pinv(n_hexapod.geometry.J');
J_int_to_X = [0.78740157480315 0.21259842519685 0 0 0;
0 0 0 0 -1;
0 0 -13.1233595800525 13.1233595800525 0];
#+end_src
Compute identified plant in the Cartesian plane:
#+begin_src matlab
%% New data after calibrating the Rz-offset
data_m3 = load(sprintf('%s/dynamics/2023-08-21_16-33_damp_plant_m3_new_Rz_fast.mat', mat_dir));
#+end_src
#+begin_src matlab :exports none
% Sampling Time [s]
Ts = 1e-4;
% Hannning Windows
Nfft = floor(1.0/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
#+end_src
#+begin_src matlab :exports none
% And we get the frequency vector
[~, f] = tfestimate(data_m3.uL1.id_plant, data_m3.uL1.d1, win, Noverlap, Nfft, 1/Ts);
#+end_src
#+begin_src matlab :exports none
%% HAC Cartesian Plant
G_hac_m3 = zeros(length(f), 5, 6);
for i_strut = 1:6
Di = [data_m3.(sprintf("uL%i", i_strut)).d1 ; data_m3.(sprintf("uL%i", i_strut)).d2 ; data_m3.(sprintf("uL%i", i_strut)).d3 ; data_m3.(sprintf("uL%i", i_strut)).d4 ; data_m3.(sprintf("uL%i", i_strut)).d5]';
G_hac_m3(:,:,i_strut) = tfestimate(data_m3.(sprintf("uL%i", i_strut)).id_plant, detrend(Di, 0), win, Noverlap, Nfft, 1/Ts);
end
G_cart_m3 = permute(pagemtimes(J_int_to_X, pagemtimes(permute(G_hac_m3, [2,3,1]), Jt_inv(:,[2,3,5]))), [3,1,2]);
#+end_src
Compute plant model in the Cartesian plane:
#+begin_src matlab
Gm_cart_m3 = J_int_to_X*Gm_hac_m3({'d1', 'd2', 'd3', 'd4', 'd5'}, {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'})*Jt_inv(:,[2,3,5]);
Gm_cart_m3.InputName = {'Fy', 'Fz', 'My'};
Gm_cart_m3.OutputName = {'Dy', 'Dz', 'Ry'};
#+end_src
#+begin_src matlab :exports none :results none
%% 3x3 plant
figure;
tiledlayout(3, 3, 'TileSpacing', 'compact', 'Padding', 'None');
ax11 = nexttile();
hold on;
plot(f, abs(G_cart_m3(:,1,1)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m3('Dy', 'Fy'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('$D_y$'); set(gca, 'XTickLabel',[]);
title('$F_y$')
ax12 = nexttile();
hold on;
plot(f, abs(G_cart_m3(:,1,2)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m3('Dy', 'Fz'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
title('$F_z$')
ax13 = nexttile();
hold on;
plot(f, abs(G_cart_m3(:,1,3)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m3('Dy', 'My'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
title('$M_y$')
linkaxes([ax11,ax12,ax13],'y');
ylim([1e-8, 2e-4]);
ax21 = nexttile();
hold on;
plot(f, abs(G_cart_m3(:,2,1)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m3('Dz', 'Fy'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('$F_z$'); set(gca, 'XTickLabel',[]);
ax22 = nexttile();
hold on;
plot(f, abs(G_cart_m3(:,2,2)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m3('Dz', 'Fz'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
ax23 = nexttile();
hold on;
plot(f, abs(G_cart_m3(:,2,3)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m3('Dz', 'My'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
linkaxes([ax21,ax22,ax23],'y');
ylim([1e-8, 2e-4]);
ax31 = nexttile();
hold on;
plot(f, abs(G_cart_m3(:,3,1)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m3('Ry', 'Fy'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('$M_y$');
ax32 = nexttile();
hold on;
plot(f, abs(G_cart_m3(:,3,2)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m3('Ry', 'Fz'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
ax33 = nexttile();
hold on;
plot(f, abs(G_cart_m3(:,3,3)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m3('Ry', 'My'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
linkaxes([ax31,ax32,ax33],'y');
ylim([1e-9, 1e-2]);
linkaxes([ax11,ax12,ax13,ax21,ax22,ax23,ax31,ax32,ax33],'x');
xlim([1, 5e2]);
#+end_src
Normalization of outputs:
#+begin_src matlab
Gm_cart_m3_normalized = -diag(1./diag(dcgain(Gm_cart_m3)))*Gm_cart_m3;
Gm_cart_m3_normalized.InputName = {'Fy', 'Fz', 'My'};
Gm_cart_m3_normalized.OutputName = {'Dy', 'Dz', 'Ry'};
G_cart_m3_normalized = permute(pagemtimes(-diag(1./diag(dcgain(Gm_cart_m3))), permute(G_cart_m3, [2,3,1])), [3,1,2]);
#+end_src
#+begin_src matlab :exports none :results none
%% 3x3 plant
figure;
tiledlayout(3, 3, 'TileSpacing', 'compact', 'Padding', 'None');
ax11 = nexttile();
hold on;
plot(f, abs(G_cart_m3_normalized(:,1,1)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m3_normalized('Dy', 'Fy'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('$D_y$'); set(gca, 'XTickLabel',[]);
title('$F_y$')
ax12 = nexttile();
hold on;
plot(f, abs(G_cart_m3_normalized(:,1,2)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m3_normalized('Dy', 'Fz'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
title('$F_z$')
ax13 = nexttile();
hold on;
plot(f, abs(G_cart_m3_normalized(:,1,3)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m3_normalized('Dy', 'My'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
title('$M_y$')
linkaxes([ax11,ax12,ax13],'y');
ylim([1e-4, 1e1]);
ax21 = nexttile();
hold on;
plot(f, abs(G_cart_m3_normalized(:,2,1)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m3_normalized('Dz', 'Fy'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('$F_z$'); set(gca, 'XTickLabel',[]);
ax22 = nexttile();
hold on;
plot(f, abs(G_cart_m3_normalized(:,2,2)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m3_normalized('Dz', 'Fz'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
ax23 = nexttile();
hold on;
plot(f, abs(G_cart_m3_normalized(:,2,3)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m3_normalized('Dz', 'My'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
linkaxes([ax21,ax22,ax23],'y');
ylim([1e-4, 1e1]);
ax31 = nexttile();
hold on;
plot(f, abs(G_cart_m3_normalized(:,3,1)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m3_normalized('Ry', 'Fy'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('$M_y$');
ax32 = nexttile();
hold on;
plot(f, abs(G_cart_m3_normalized(:,3,2)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m3_normalized('Ry', 'Fz'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
ax33 = nexttile();
hold on;
plot(f, abs(G_cart_m3_normalized(:,3,3)));
plot(freqs, abs(squeeze(freqresp(Gm_cart_m3_normalized('Ry', 'My'), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
linkaxes([ax31,ax32,ax33],'y');
ylim([1e-4, 1e1]);
linkaxes([ax11,ax12,ax13,ax21,ax22,ax23,ax31,ax32,ax33],'x');
xlim([1, 5e2]);
#+end_src
#+begin_src matlab :exports none :results none
%% 3x3 plant
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_cart_m3_normalized(:,1,1)));
plot(f, abs(G_cart_m3_normalized(:,2,2)));
plot(f, abs(G_cart_m3_normalized(:,3,3)));
plot(f, abs(G_cart_m3_normalized(:,1,2)), 'color', [0,0,0,0.2]);
plot(f, abs(G_cart_m3_normalized(:,1,3)), 'color', [0,0,0,0.2]);
plot(f, abs(G_cart_m3_normalized(:,2,1)), 'color', [0,0,0,0.2]);
plot(f, abs(G_cart_m3_normalized(:,3,1)), 'color', [0,0,0,0.2]);
plot(f, abs(G_cart_m3_normalized(:,2,3)), 'color', [0,0,0,0.2]);
plot(f, abs(G_cart_m3_normalized(:,3,2)), 'color', [0,0,0,0.2]);
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude');
ylim([1e-4, 1e1]);
ax2 = nexttile();
hold on;
plot(f, 180/pi*angle(G_cart_m3_normalized(:,1,1)));
plot(f, 180/pi*angle(G_cart_m3_normalized(:,2,2)));
plot(f, 180/pi*angle(G_cart_m3_normalized(:,3,3)));
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 1e3]);
#+end_src
*** Better plant invert
#+begin_src matlab :exports none
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)
%% We define the number of iteration.
Niter = 100;
#+end_src
#+begin_src matlab
N = 9; %Order of approximation
#+end_src
**** Dy
#+begin_src matlab :exports none
%Complex conjugate pairs, linearly spaced:
bet=linspace(f(1),f(end),N/2);
poles=[];
for n=1:length(bet)
alf=-bet(n)*1e-1;
poles=[poles (alf-i*bet(n)) (alf+i*bet(n)) ];
end
#+end_src
#+begin_src matlab
%% Estimate resonance frequency and damping
for iter =1:Niter
[G_est, poles, ~, frf_est] = vectfit3(G_cart_m3(f>2&f<800,1,1).', 1i*2*pi*f(f>2&f<800)', poles, 1./(f(f>2&f<800))', opts);
end
Gfit_dy = ss(G_est.A, G_est.B, G_est.C, G_est.D);
#+end_src
#+begin_src matlab :exports none :results none
%% Comparaison of the measured direct terms and the reduced order models
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_cart_m3(:,1,1)), 'color', [colors(1,:)], 'DisplayName', '$D_y$');
plot(f, abs(squeeze(freqresp(Gfit_dy, f, 'Hz'))), '--', 'color', [colors(1,:)], 'HandleVisibility', 'off');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude');
legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(G_cart_m3(:,1,1)), 'color', [colors(1,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Gfit_dy, f, 'Hz'))), '--', 'color', [colors(1,:)]);
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 2e3]);
#+end_src
Stable Inverse
#+begin_src matlab
Ginv_dy = inv(flipRphZeros(Gfit_dy))/(1 + s/2/pi/1e3);
isstable(Ginv_dy)
#+end_src
**** Dz
#+begin_src matlab :exports none
%Complex conjugate pairs, linearly spaced:
bet=linspace(f(1),f(end),N/2);
poles=[];
for n=1:length(bet)
alf=-bet(n)*1e-1;
poles=[poles (alf-i*bet(n)) (alf+i*bet(n)) ];
end
#+end_src
#+begin_src matlab
%% Estimate resonance frequency and damping
for iter =1:Niter
[G_est, poles, ~, frf_est] = vectfit3(G_cart_m3(f>2&f<1500,2,2).', 1i*2*pi*f(f>2&f<1500)', poles, 1./(f(f>2&f<1500))', opts);
end
Gfit_dz = ss(G_est.A, G_est.B, G_est.C, G_est.D);
#+end_src
#+begin_src matlab :exports none :results none
%% Comparaison of the measured direct terms and the reduced order models
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_cart_m3(:,2,2)), 'color', [colors(2,:)], 'DisplayName', '$D_z$');
plot(f, abs(squeeze(freqresp(Gfit_dz, f, 'Hz'))), '--', 'color', [colors(2,:)], 'HandleVisibility', 'off');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude');
legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(G_cart_m3(:,2,2)), 'color', [colors(2,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Gfit_dz, f, 'Hz'))), '--', 'color', [colors(2,:)]);
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 2e3]);
#+end_src
Stable Inverse
#+begin_src matlab
Ginv_dz = inv(-flipRphZeros(Gfit_dz))/(1 + s/2/pi/1e3);
isstable(Ginv_dz)
#+end_src
**** Ry
#+begin_src matlab :exports none
%Complex conjugate pairs, linearly spaced:
bet=linspace(f(1),f(end),N/2);
poles=[];
for n=1:length(bet)
alf=-bet(n)*1e-1;
poles=[poles (alf-i*bet(n)) (alf+i*bet(n)) ];
end
#+end_src
#+begin_src matlab
%% Estimate resonance frequency and damping
for iter =1:Niter
[G_est, poles, ~, frf_est] = vectfit3(G_cart_m3(f>2&f<800,3,3).', 1i*2*pi*f(f>2&f<800)', poles, 1./(f(f>2&f<800))', opts);
end
Gfit_ry = ss(G_est.A, G_est.B, G_est.C, G_est.D);
#+end_src
#+begin_src matlab :exports none :results none
%% Comparaison of the measured direct terms and the reduced order models
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(G_cart_m3(:,3,3)), 'color', [colors(3,:)], 'DisplayName', '$R_y$');
plot(f, abs(squeeze(freqresp(Gfit_ry, f, 'Hz'))), '--', 'color', [colors(3,:)], 'HandleVisibility', 'off');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude');
legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(G_cart_m3(:,3,3)), 'color', [colors(3,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Gfit_ry, f, 'Hz'))), '--', 'color', [colors(3,:)]);
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 2e3]);
#+end_src
Stable Inverse
#+begin_src matlab
Ginv_ry = inv(-flipRphZeros(Gfit_ry))/(1 + s/2/pi/1e3);
isstable(Ginv_ry)
#+end_src
**** Compare Invert plants
#+begin_src matlab :exports none :results none
%% Comparaison of the measured direct terms and the reduced order models
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, 1./abs(G_cart_m3(:,1,1)), 'color', [colors(1,:)], 'DisplayName', '$D_y$');
plot(f, abs(squeeze(freqresp(Ginv_dy, f, 'Hz'))), '--', 'color', [colors(1,:)], 'HandleVisibility', 'off');
plot(f, 1./abs(G_cart_m3(:,2,2)), 'color', [colors(2,:)], 'DisplayName', '$D_z$');
plot(f, abs(squeeze(freqresp(Ginv_dz, f, 'Hz'))), '--', 'color', [colors(2,:)], 'HandleVisibility', 'off');
plot(f, 1./abs(G_cart_m3(:,3,3)), 'color', [colors(3,:)], 'DisplayName', '$R_y$');
plot(f, abs(squeeze(freqresp(Ginv_ry, f, 'Hz'))), '--', 'color', [colors(3,:)], 'HandleVisibility', 'off');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude');
legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(1./G_cart_m3(:,1,1)), 'color', [colors(1,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Ginv_dy, f, 'Hz'))), '--', 'color', [colors(1,:)]);
plot(f, 180/pi*angle(1./G_cart_m3(:,2,2)), 'color', [colors(2,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Ginv_dz, f, 'Hz'))), '--', 'color', [colors(2,:)]);
plot(f, 180/pi*angle(1./G_cart_m3(:,3,3)), 'color', [colors(3,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Ginv_ry, f, 'Hz'))), '--', 'color', [colors(3,:)]);
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 2e3]);
#+end_src
#+begin_src matlab :exports none :results none
%% Comparaison of the measured direct terms and the reduced order models
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, abs(squeeze(freqresp(Ginv_dy, f, 'Hz')).*abs(G_cart_m3(:,1,1))), '-', 'DisplayName', '$D_y$');
plot(f, abs(squeeze(freqresp(Ginv_dz, f, 'Hz')).*abs(G_cart_m3(:,2,2))), '-', 'DisplayName', '$D_z$');
plot(f, abs(squeeze(freqresp(Ginv_ry, f, 'Hz')).*abs(G_cart_m3(:,3,3))), '-', 'DisplayName', '$R_y$');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude');
legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(squeeze(freqresp(Ginv_dy, f, 'Hz')).*G_cart_m3(:,1,1)), '-');
plot(f, 180/pi*angle(squeeze(freqresp(Ginv_dz, f, 'Hz')).*G_cart_m3(:,2,2)), '-');
plot(f, 180/pi*angle(squeeze(freqresp(Ginv_ry, f, 'Hz')).*G_cart_m3(:,3,3)), '-');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 2e3]);
#+end_src
**** Save plant inverse
#+begin_src matlab :exports none :tangle no
K = -Ginv_dy;
K_order = order(K);
Kz_dy = c2d(K*(1 + s/2/pi/2e3)^(9-K_order)/(1 + s/2/pi/2e3)^(9-K_order), 1e-4, 'tustin');
[num, den] = tfdata(Kz_dy, 'v');
formatSpec = '%.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e\n';
fileID = fopen('/home/thomas/mnt/data_id31/nass/controllers/G_dy_inv_m3.dat', 'w');
fprintf(fileID, formatSpec, [num; den]');
fclose(fileID);
#+end_src
#+begin_src matlab :exports none :tangle no
K = -Ginv_dz;
K_order = order(K);
Kz_dz = c2d(K*(1 + s/2/pi/2e3)^(9-K_order)/(1 + s/2/pi/2e3)^(9-K_order), 1e-4, 'tustin');
[num, den] = tfdata(Kz_dz, 'v');
formatSpec = '%.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e\n';
fileID = fopen('/home/thomas/mnt/data_id31/nass/controllers/G_dz_inv_m3.dat', 'w');
fprintf(fileID, formatSpec, [num; den]');
fclose(fileID);
#+end_src
#+begin_src matlab :exports none :tangle no
K = -Ginv_ry;
K_order = order(K);
Kz_ry = c2d(K*(1 + s/2/pi/2e3)^(9-K_order)/(1 + s/2/pi/2e3)^(9-K_order), 1e-4, 'tustin');
[num, den] = tfdata(Kz_ry, 'v');
formatSpec = '%.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e\n';
fileID = fopen('/home/thomas/mnt/data_id31/nass/controllers/G_ry_inv_m3.dat', 'w');
fprintf(fileID, formatSpec, [num; den]');
fclose(fileID);
#+end_src
**** Compare Digital Invert plants
#+begin_src matlab :exports none :results none
%% Comparaison of the measured direct terms and the reduced order models
figure;
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(f, 1./abs(G_cart_m3(:,1,1)), 'color', [colors(1,:)], 'DisplayName', '$D_y$');
plot(f, abs(squeeze(freqresp(Kz_dy, f, 'Hz'))), '--', 'color', [colors(1,:)], 'HandleVisibility', 'off');
plot(f, 1./abs(G_cart_m3(:,2,2)), 'color', [colors(2,:)], 'DisplayName', '$D_z$');
plot(f, abs(squeeze(freqresp(Kz_dz, f, 'Hz'))), '--', 'color', [colors(2,:)], 'HandleVisibility', 'off');
plot(f, 1./abs(G_cart_m3(:,3,3)), 'color', [colors(3,:)], 'DisplayName', '$R_y$');
plot(f, abs(squeeze(freqresp(Kz_ry, f, 'Hz'))), '--', 'color', [colors(3,:)], 'HandleVisibility', 'off');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude');
legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(1./G_cart_m3(:,1,1)), 'color', [colors(1,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Kz_dy, f, 'Hz'))), '--', 'color', [colors(1,:)]);
plot(f, 180/pi*angle(1./G_cart_m3(:,2,2)), 'color', [colors(2,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Kz_dz, f, 'Hz'))), '--', 'color', [colors(2,:)]);
plot(f, 180/pi*angle(1./G_cart_m3(:,3,3)), 'color', [colors(3,:)]);
plot(f, 180/pi*angle(squeeze(freqresp(Kz_ry, f, 'Hz'))), '--', 'color', [colors(3,:)]);
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-180, 180])
linkaxes([ax1,ax2],'x');
xlim([1, 2e3]);
#+end_src
*** TODO Control Performances
**** Better plant invert
#+begin_src matlab
data_cl = load(sprintf('%s/dynamics/2023-08-21_13-36_m0_cf_inv_fit.mat', mat_dir));
#+end_src
#+begin_src matlab :exports none
% Sampling Time [s]
Ts = 1e-4;
% Hannning Windows
Nfft = floor(1.0/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
#+end_src
#+begin_src matlab :exports none
% And we get the frequency vector
[S_dy, f] = tfestimate(data_cl.Dy.id_cl, data_cl.Dy.e_dy, win, Noverlap, Nfft, 1/Ts);
[S_dz, ~] = tfestimate(data_cl.Dz.id_cl, data_cl.Dz.e_dz, win, Noverlap, Nfft, 1/Ts);
[S_ry, ~] = tfestimate(data_cl.Ry.id_cl, data_cl.Ry.e_ry, win, Noverlap, Nfft, 1/Ts);
#+end_src
#+begin_src matlab :exports none :results none
%% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor
figure;
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile();
hold on;
plot(f, abs(S_dy), 'DisplayName', '$D_y$');
plot(f, abs(S_dz), 'DisplayName', '$D_z$');
plot(f, abs(S_ry), 'DisplayName', '$R_y$');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('Amplitude');
ylim([1e-3, 1e1]);
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 3);
xlim([1, 1e3]);
#+end_src
*** TODO Scans with good controller
**** 1rpm
1RPM scans are performed for all the masses with the same controller.
#+begin_src matlab
data_m0 = load(sprintf("%s/scans/2023-08-21_14-28_m0_1rpm_K_cf.mat", mat_dir));
data_m0.time = Ts*[0:length(data_m0.Rz)-1];
#+end_src
#+begin_src matlab
%% Compute RMS values while in closed-loop
data_m0.Dx_rms_cl = rms(detrend(data_m0.Dx_int, 0));
data_m0.Dy_rms_cl = rms(detrend(data_m0.Dy_int, 0));
data_m0.Dz_rms_cl = rms(detrend(data_m0.Dz_int, 0));
data_m0.Rx_rms_cl = rms(detrend(data_m0.Rx_int, 0));
data_m0.Ry_rms_cl = rms(detrend(data_m0.Ry_int, 0));
#+end_src
#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*)
data2orgtable([1e9*data_m0.Dx_rms_cl, 1e9*data_m0.Dy_rms_cl, 1e9*data_m0.Dz_rms_cl, 1e9*data_m0.Rx_rms_cl, 1e9*data_m0.Ry_rms_cl], ...
{'m0'}, {'Dx [nm]', 'Dy [nm]', 'Dz [nm]', 'Rx [nrad]', 'Ry [nrad]'}, ' %.0f ');
#+end_src
#+RESULTS:
| | Dx [nm] | Dy [nm] | Dz [nm] | Rx [nrad] | Ry [nrad] |
|----+---------+---------+---------+-----------+-----------|
| m0 | 796 | 20 | 8 | 8209 | 73 |
#+begin_src matlab :exports none :results none
%% description
figure;
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile;
hold on;
plot(1e9*data_m0.Dy_int, 1e9*data_m0.Dz_int);
hold off;
axis square
xlabel("Y motion [$nm$]");
ylabel("Z motion [$nm$]");
#+end_src
**** 30rpm
1RPM scans are performed for all the masses with the same controller.
#+begin_src matlab
data_m0 = load(sprintf("%s/scans/2023-08-21_14-33_m0_30rpm_cf_control.mat", mat_dir));
data_m0.time = Ts*[0:length(data_m0.Rz)-1];
#+end_src
#+begin_src matlab
%% Compute RMS values while in closed-loop
data_m0.Dx_rms_cl = rms(detrend(data_m0.Dx_int, 0));
data_m0.Dy_rms_cl = rms(detrend(data_m0.Dy_int, 0));
data_m0.Dz_rms_cl = rms(detrend(data_m0.Dz_int, 0));
data_m0.Rx_rms_cl = rms(detrend(data_m0.Rx_int, 0));
data_m0.Ry_rms_cl = rms(detrend(data_m0.Ry_int, 0));
#+end_src
#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*)
data2orgtable([1e9*data_m0.Dx_rms_cl, 1e9*data_m0.Dy_rms_cl, 1e9*data_m0.Dz_rms_cl, 1e9*data_m0.Rx_rms_cl, 1e9*data_m0.Ry_rms_cl], ...
{'m0'}, {'Dx [nm]', 'Dy [nm]', 'Dz [nm]', 'Rx [nrad]', 'Ry [nrad]'}, ' %.0f ');
#+end_src
#+RESULTS:
| | Dx [nm] | Dy [nm] | Dz [nm] | Rx [nrad] | Ry [nrad] |
|----+---------+---------+---------+-----------+-----------|
| m0 | 820 | 39 | 13 | 7790 | 156 |
#+begin_src matlab :exports none :results none
%% description
figure;
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile;
hold on;
plot(1e9*data_m0.Dy_int, 1e9*data_m0.Dz_int);
hold off;
axis square
xlabel("Y motion [$nm$]");
ylabel("Z motion [$nm$]");
#+end_src
* Scans
<<sec:id31_scans>>
** Introduction :ignore:
- Section ref:sec:id31_scans_tomography
- Section ref:sec:id31_scans_dz
- Section ref:sec:id31_scans_reflectivity
- Section ref:sec:id31_scans_dy
- Section ref:sec:id31_scans_diffraction_tomo
** 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 :tangle no :noweb yes
<<m-init-path>>
#+end_src
#+begin_src matlab :eval no :noweb yes
<<m-init-path-tangle>>
#+end_src
#+begin_src matlab :noweb yes
<<m-init-other>>
#+end_src
** $R_z$ scans: Tomography
<<sec:id31_scans_tomography>>
*** Introduction :ignore:
m0: 30rpm, 6rpm, 1rpm
m1: 6rpm, 1rpm
m2: 6rpm, 1rpm
m3: 1rpm
*** Robust Control - 1rpm
1RPM scans are performed for all the masses with the same robust controller.
#+begin_src matlab
%% Load Tomography scans with robust controller
data_tomo_1rpm_m0 = load(sprintf("%s/scans/2023-08-11_11-37_tomography_1rpm_m0.mat", mat_dir));
data_tomo_1rpm_m0.time = Ts*[0:length(data_tomo_1rpm_m0.Rz)-1];
data_tomo_1rpm_m1 = load(sprintf("%s/scans/2023-08-11_11-15_tomography_1rpm_m1.mat", mat_dir));
data_tomo_1rpm_m1.time = Ts*[0:length(data_tomo_1rpm_m1.Rz)-1];
data_tomo_1rpm_m2 = load(sprintf("%s/scans/2023-08-11_10-59_tomography_1rpm_m2.mat", mat_dir));
data_tomo_1rpm_m2.time = Ts*[0:length(data_tomo_1rpm_m2.Rz)-1];
data_tomo_1rpm_m3 = load(sprintf("%s/scans/2023-08-11_10-24_tomography_1rpm_m3.mat", mat_dir));
data_tomo_1rpm_m3.time = Ts*[0:length(data_tomo_1rpm_m3.Rz)-1];
#+end_src
The problem for these scans is that the position initialization was not make properly, so the open-loop errors are quite large (see Figure ref:fig:id31_tomo_1rpm_robust_m0).
#+begin_src matlab :exports none :results none
%% $D_x$, $D_y$ and $D_z$ motion during a slow (1RPM) tomography experiment. Open Loop data is shown in blue and closed-loop data in red
figure;
tiledlayout(1, 2, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile;
hold on;
plot(1e6*data_tomo_1rpm_m0.Dx_int(data_tomo_1rpm_m0.hac_status == 0), 1e6*data_tomo_1rpm_m0.Dy_int(data_tomo_1rpm_m0.hac_status == 0), ...
'DisplayName', 'OL')
plot(1e6*data_tomo_1rpm_m0.Dx_int(data_tomo_1rpm_m0.hac_status == 1), 1e6*data_tomo_1rpm_m0.Dy_int(data_tomo_1rpm_m0.hac_status == 1), ...
'DisplayName', 'CL')
hold off;
axis equal
xlabel("X motion [$\mu m$]");
ylabel("Y motion [$\mu m$]");
legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
% xlim([-3, 3])
% ylim([-3, 3])
ax2 = nexttile;
hold on;
plot(1e6*data_tomo_1rpm_m0.Dy_int(data_tomo_1rpm_m0.hac_status == 0), 1e6*data_tomo_1rpm_m0.Dz_int(data_tomo_1rpm_m0.hac_status == 0), ...
'DisplayName', 'OL')
plot(1e6*data_tomo_1rpm_m0.Dy_int(data_tomo_1rpm_m0.hac_status == 1), 1e6*data_tomo_1rpm_m0.Dz_int(data_tomo_1rpm_m0.hac_status == 1), ...
'DisplayName', 'CL')
hold off;
% axis equal
% xlim([-3, 3])
ylim([-0.2, 1.1])
xlabel("Y motion [$\mu m$]");
ylabel("Z motion [$\mu m$]");
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_tomo_1rpm_robust_m0.pdf', 'width', 'full', 'height', 'normal');
#+end_src
#+name: fig:id31_tomo_1rpm_robust_m0
#+caption: $D_x$, $D_y$ and $D_z$ motion during a slow (1RPM) tomography experiment. Open Loop data is shown in blue and closed-loop data in red
#+RESULTS:
[[file:figs/id31_tomo_1rpm_robust_m0.png]]
#+begin_src matlab
yztomographymovie('movies/tomography_1rpm_m0', data_tomo_1rpm_m0, 'xlim_ax1', [-10, 15], 'ylim_ax1', [-0.2, 1.2], 'xlim_ax2', [-300, 300], 'ylim_ax2', [-100, 100])
yztomographymovie('movies/tomography_1rpm_m0_di_5000', data_tomo_1rpm_m0, 'xlim_ax1', [-10, 15], 'ylim_ax1', [-0.2, 1.2], 'xlim_ax2', [-300, 300], 'ylim_ax2', [-100, 100], 'di', 5000)
#+end_src
The obtained open-loop and closed-loop errors are shown in tables ref:tab:id31_tomo_1rpm_robust_ol_errors and ref:tab:id31_tomo_1rpm_robust_cl_errors respectively.
#+begin_src matlab
%% Compute RMS values while in closed-loop and open-loop
[~, i_m0] = find(data_tomo_1rpm_m0.hac_status == 1);
data_tomo_1rpm_m0.Dx_rms_cl = rms(detrend(data_tomo_1rpm_m0.Dx_int(i_m0+50000:end), 0));
data_tomo_1rpm_m0.Dy_rms_cl = rms(detrend(data_tomo_1rpm_m0.Dy_int(i_m0+50000:end), 0));
data_tomo_1rpm_m0.Dz_rms_cl = rms(detrend(data_tomo_1rpm_m0.Dz_int(i_m0+50000:end), 0));
data_tomo_1rpm_m0.Rx_rms_cl = rms(detrend(data_tomo_1rpm_m0.Rx_int(i_m0+50000:end), 0));
data_tomo_1rpm_m0.Ry_rms_cl = rms(detrend(data_tomo_1rpm_m0.Ry_int(i_m0+50000:end), 0));
data_tomo_1rpm_m0.Dx_rms_ol = rms(detrend(data_tomo_1rpm_m0.Dx_int(1:i_m0), 0));
data_tomo_1rpm_m0.Dy_rms_ol = rms(detrend(data_tomo_1rpm_m0.Dy_int(1:i_m0), 0));
data_tomo_1rpm_m0.Dz_rms_ol = rms(detrend(data_tomo_1rpm_m0.Dz_int(1:i_m0), 0));
data_tomo_1rpm_m0.Rx_rms_ol = rms(detrend(data_tomo_1rpm_m0.Rx_int(1:i_m0), 0));
data_tomo_1rpm_m0.Ry_rms_ol = rms(detrend(data_tomo_1rpm_m0.Ry_int(1:i_m0), 0));
%% Compute RMS values while in closed-loop and open-loop
[~, i_m1] = find(data_tomo_1rpm_m1.hac_status == 1);
data_tomo_1rpm_m1.Dx_rms_cl = rms(detrend(data_tomo_1rpm_m1.Dx_int(i_m1+50000:end), 0));
data_tomo_1rpm_m1.Dy_rms_cl = rms(detrend(data_tomo_1rpm_m1.Dy_int(i_m1+50000:end), 0));
data_tomo_1rpm_m1.Dz_rms_cl = rms(detrend(data_tomo_1rpm_m1.Dz_int(i_m1+50000:end), 0));
data_tomo_1rpm_m1.Rx_rms_cl = rms(detrend(data_tomo_1rpm_m1.Rx_int(i_m1+50000:end), 0));
data_tomo_1rpm_m1.Ry_rms_cl = rms(detrend(data_tomo_1rpm_m1.Ry_int(i_m1+50000:end), 0));
data_tomo_1rpm_m1.Dx_rms_ol = rms(detrend(data_tomo_1rpm_m1.Dx_int(1:i_m1), 0));
data_tomo_1rpm_m1.Dy_rms_ol = rms(detrend(data_tomo_1rpm_m1.Dy_int(1:i_m1), 0));
data_tomo_1rpm_m1.Dz_rms_ol = rms(detrend(data_tomo_1rpm_m1.Dz_int(1:i_m1), 0));
data_tomo_1rpm_m1.Rx_rms_ol = rms(detrend(data_tomo_1rpm_m1.Rx_int(1:i_m1), 0));
data_tomo_1rpm_m1.Ry_rms_ol = rms(detrend(data_tomo_1rpm_m1.Ry_int(1:i_m1), 0));
%% Compute RMS values while in closed-loop and open-loop
[~, i_m2] = find(data_tomo_1rpm_m2.hac_status == 1);
data_tomo_1rpm_m2.Dx_rms_cl = rms(detrend(data_tomo_1rpm_m2.Dx_int(i_m2+50000:end), 0));
data_tomo_1rpm_m2.Dy_rms_cl = rms(detrend(data_tomo_1rpm_m2.Dy_int(i_m2+50000:end), 0));
data_tomo_1rpm_m2.Dz_rms_cl = rms(detrend(data_tomo_1rpm_m2.Dz_int(i_m2+50000:end), 0));
data_tomo_1rpm_m2.Rx_rms_cl = rms(detrend(data_tomo_1rpm_m2.Rx_int(i_m2+50000:end), 0));
data_tomo_1rpm_m2.Ry_rms_cl = rms(detrend(data_tomo_1rpm_m2.Ry_int(i_m2+50000:end), 0));
data_tomo_1rpm_m2.Dx_rms_ol = rms(detrend(data_tomo_1rpm_m2.Dx_int(1:i_m2), 0));
data_tomo_1rpm_m2.Dy_rms_ol = rms(detrend(data_tomo_1rpm_m2.Dy_int(1:i_m2), 0));
data_tomo_1rpm_m2.Dz_rms_ol = rms(detrend(data_tomo_1rpm_m2.Dz_int(1:i_m2), 0));
data_tomo_1rpm_m2.Rx_rms_ol = rms(detrend(data_tomo_1rpm_m2.Rx_int(1:i_m2), 0));
data_tomo_1rpm_m2.Ry_rms_ol = rms(detrend(data_tomo_1rpm_m2.Ry_int(1:i_m2), 0));
%% Compute RMS values while in closed-loop and open-loop
[~, i_m3] = find(data_tomo_1rpm_m3.hac_status == 1);
data_tomo_1rpm_m3.Dx_rms_cl = rms(detrend(data_tomo_1rpm_m3.Dx_int(i_m3+50000:end), 0));
data_tomo_1rpm_m3.Dy_rms_cl = rms(detrend(data_tomo_1rpm_m3.Dy_int(i_m3+50000:end), 0));
data_tomo_1rpm_m3.Dz_rms_cl = rms(detrend(data_tomo_1rpm_m3.Dz_int(i_m3+50000:end), 0));
data_tomo_1rpm_m3.Rx_rms_cl = rms(detrend(data_tomo_1rpm_m3.Rx_int(i_m3+50000:end), 0));
data_tomo_1rpm_m3.Ry_rms_cl = rms(detrend(data_tomo_1rpm_m3.Ry_int(i_m3+50000:end), 0));
data_tomo_1rpm_m3.Dx_rms_ol = rms(detrend(data_tomo_1rpm_m3.Dx_int(1:i_m3), 0));
data_tomo_1rpm_m3.Dy_rms_ol = rms(detrend(data_tomo_1rpm_m3.Dy_int(1:i_m3), 0));
data_tomo_1rpm_m3.Dz_rms_ol = rms(detrend(data_tomo_1rpm_m3.Dz_int(1:i_m3), 0));
data_tomo_1rpm_m3.Rx_rms_ol = rms(detrend(data_tomo_1rpm_m3.Rx_int(1:i_m3), 0));
data_tomo_1rpm_m3.Ry_rms_ol = rms(detrend(data_tomo_1rpm_m3.Ry_int(1:i_m3), 0));
#+end_src
#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*)
data2orgtable([1e6*data_tomo_1rpm_m0.Dx_rms_ol, 1e6*data_tomo_1rpm_m0.Dy_rms_ol, 1e9*data_tomo_1rpm_m0.Dz_rms_ol, 1e6*data_tomo_1rpm_m0.Rx_rms_ol, 1e6*data_tomo_1rpm_m0.Ry_rms_ol; ...
1e6*data_tomo_1rpm_m1.Dx_rms_ol, 1e6*data_tomo_1rpm_m1.Dy_rms_ol, 1e9*data_tomo_1rpm_m1.Dz_rms_ol, 1e6*data_tomo_1rpm_m1.Rx_rms_ol, 1e6*data_tomo_1rpm_m1.Ry_rms_ol; ...
1e6*data_tomo_1rpm_m2.Dx_rms_ol, 1e6*data_tomo_1rpm_m2.Dy_rms_ol, 1e9*data_tomo_1rpm_m2.Dz_rms_ol, 1e6*data_tomo_1rpm_m2.Rx_rms_ol, 1e6*data_tomo_1rpm_m2.Ry_rms_ol; ...
1e6*data_tomo_1rpm_m3.Dx_rms_ol, 1e6*data_tomo_1rpm_m3.Dy_rms_ol, 1e9*data_tomo_1rpm_m3.Dz_rms_ol, 1e6*data_tomo_1rpm_m3.Rx_rms_ol, 1e6*data_tomo_1rpm_m3.Ry_rms_ol], ...
{'$m_0$', '$m_1$', '$m_2$', '$m_3$'}, {'$D_x$ [$\mu m$]', '$D_y$ [$\mu m$]', '$D_z$ [$nm$]', '$R_x$ [$\mu\text{rad}$]', '$R_y$ [$\mu\text{rad}$]'}, ' %.0f ');
#+end_src
#+name: tab:id31_tomo_1rpm_robust_ol_errors
#+caption: Measured error during open-loop tomography scans (1rpm)
#+attr_latex: :environment tabularx :width \linewidth :align cXXXXX
#+attr_latex: :center t :booktabs t
#+RESULTS:
| | $D_x$ [$\mu m$] | $D_y$ [$\mu m$] | $D_z$ [$nm$] | $R_x$ [$\mu\text{rad}$] | $R_y$ [$\mu\text{rad}$] |
|-------+-----------------+-----------------+--------------+-------------------------+-------------------------|
| $m_0$ | 6 | 6 | 32 | 34 | 34 |
| $m_1$ | 6 | 7 | 26 | 51 | 55 |
| $m_2$ | 36 | 38 | 36 | 259 | 253 |
| $m_3$ | 31 | 33 | 38 | 214 | 203 |
#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*)
data2orgtable([1e9*data_tomo_1rpm_m0.Dx_rms_cl, 1e9*data_tomo_1rpm_m0.Dy_rms_cl, 1e9*data_tomo_1rpm_m0.Dz_rms_cl, 1e9*data_tomo_1rpm_m0.Rx_rms_cl, 1e9*data_tomo_1rpm_m0.Ry_rms_cl; ...
1e9*data_tomo_1rpm_m1.Dx_rms_cl, 1e9*data_tomo_1rpm_m1.Dy_rms_cl, 1e9*data_tomo_1rpm_m1.Dz_rms_cl, 1e9*data_tomo_1rpm_m1.Rx_rms_cl, 1e9*data_tomo_1rpm_m1.Ry_rms_cl; ...
1e9*data_tomo_1rpm_m2.Dx_rms_cl, 1e9*data_tomo_1rpm_m2.Dy_rms_cl, 1e9*data_tomo_1rpm_m2.Dz_rms_cl, 1e9*data_tomo_1rpm_m2.Rx_rms_cl, 1e9*data_tomo_1rpm_m2.Ry_rms_cl; ...
1e9*data_tomo_1rpm_m3.Dx_rms_cl, 1e9*data_tomo_1rpm_m3.Dy_rms_cl, 1e9*data_tomo_1rpm_m3.Dz_rms_cl, 1e9*data_tomo_1rpm_m3.Rx_rms_cl, 1e9*data_tomo_1rpm_m3.Ry_rms_cl], ...
{'$m_0$', '$m_1$', '$m_2$', '$m_3$'}, {'$D_x$ [nm]', '$D_y$ [nm]', '$D_z$ [nm]', '$R_x$ [nrad]', '$R_y$ [nrad]'}, ' %.0f ');
#+end_src
#+name: tab:id31_tomo_1rpm_robust_cl_errors
#+caption: Measured error during closed-loop tomography scans (1rpm, robust controller)
#+attr_latex: :environment tabularx :width \linewidth :align cXXXXX
#+attr_latex: :center t :booktabs t
#+RESULTS:
| | $D_x$ [nm] | $D_y$ [nm] | $D_z$ [nm] | $R_x$ [nrad] | $R_y$ [nrad] |
|-------+------------+------------+------------+--------------+--------------|
| $m_0$ | 13 | 15 | 5 | 57 | 55 |
| $m_1$ | 16 | 25 | 6 | 102 | 55 |
| $m_2$ | 25 | 25 | 7 | 120 | 103 |
| $m_3$ | 40 | 53 | 9 | 225 | 169 |
*** TODO Slow Tomography Scans Comparison of control performances :noexport:
#+begin_src matlab
% Decentralized in the frame of the struts
data = load(sprintf("%s/scans/2023-08-18_10-43_m0_1rpm.mat", mat_dir));
data.time = Ts*[0:length(data.Rz)-1];
% Rotating cartesian frame
data_cart = load(sprintf("%s/scans/2023-08-18_18-33_m0_1rpm_K_cart.mat", mat_dir));
data_cart.time = Ts*[0:length(data_cart.Rz)-1];
% Fixed cartesian frame
data_cart_fixed = load(sprintf("%s/scans/2023-08-18_19-08_m0_1rpm_K_cart_fixed.mat", mat_dir));
data_cart_fixed.time = Ts*[0:length(data_cart_fixed.Rz)-1];
% Fixed cartesian frame with Complementary Filters
data_cf = load(sprintf("%s/scans/2023-08-21_14-28_m0_1rpm_K_cf.mat", mat_dir));
data_cf.time = Ts*[0:length(data_cf.Rz)-1];
#+end_src
#+begin_src matlab
1e9*rms(data.Dx_int(data.time<45))
1e9*rms(data_cart.Dx_int(data_cart.time<45))
1e9*rms(data_cart_fixed.Dx_int(data_cart_fixed.time<45))
1e9*rms(data_cf.Dx_int(data_cf.time<45))
#+end_src
#+begin_src matlab
1e9*rms(data.Dy_int(data.time<45))
1e9*rms(data_cart.Dy_int(data_cart.time<45))
1e9*rms(data_cart_fixed.Dy_int(data_cart_fixed.time<45))
1e9*rms(data_cf.Dy_int(data_cf.time<45))
#+end_src
#+begin_src matlab
1e9*rms(data.Dz_int(data.time<45))
1e9*rms(data_cart.Dz_int(data_cart.time<45))
1e9*rms(data_cart_fixed.Dz_int(data_cart_fixed.time<45))
1e9*rms(data_cf.Dz_int(data_cf.time<45))
#+end_src
#+begin_src matlab
1e9*rms(data.Rx_int(data.time<45))
1e9*rms(data_cart.Rx_int(data_cart.time<45))
1e9*rms(data_cart_fixed.Rx_int(data_cart_fixed.time<45))
1e9*rms(data_cf.Rx_int(data_cf.time<45))
#+end_src
#+begin_src matlab
1e9*rms(data.Ry_int(data.time<45))
1e9*rms(data_cart.Ry_int(data_cart.time<45))
1e9*rms(data_cart_fixed.Ry_int(data_cart_fixed.time<45))
1e9*rms(data_cf.Ry_int(data_cf.time<45))
#+end_src
#+begin_src matlab
figure;
hold on;
plot(data.time, data.Dy_int)
plot(data_cart.time, data_cart.Dy_int)
plot(data_cart_fixed.time, data_cart_fixed.Dy_int)
plot(data_cf.time, data_cf.Dy_int)
hold off;
#+end_src
#+begin_src matlab :exports none
% Hannning Windows
Nfft = floor(10.0/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
[data.pxx_Dx, data.f] = pwelch(detrend(data.Dx_int(data.time<45), 0), win, Noverlap, Nfft, 1/Ts);
[data.pxx_Dy, ~ ] = pwelch(detrend(data.Dy_int(data.time<45), 0), win, Noverlap, Nfft, 1/Ts);
[data.pxx_Dz, ~ ] = pwelch(detrend(data.Dz_int(data.time<45), 0), win, Noverlap, Nfft, 1/Ts);
[data.pxx_Rx, ~ ] = pwelch(detrend(data.Rx_int(data.time<45), 0), win, Noverlap, Nfft, 1/Ts);
[data.pxx_Ry, ~ ] = pwelch(detrend(data.Ry_int(data.time<45), 0), win, Noverlap, Nfft, 1/Ts);
[data_cart.pxx_Dx, data_cart.f] = pwelch(detrend(data_cart.Dx_int(data_cart.time<45), 0), win, Noverlap, Nfft, 1/Ts);
[data_cart.pxx_Dy, ~ ] = pwelch(detrend(data_cart.Dy_int(data_cart.time<45), 0), win, Noverlap, Nfft, 1/Ts);
[data_cart.pxx_Dz, ~ ] = pwelch(detrend(data_cart.Dz_int(data_cart.time<45), 0), win, Noverlap, Nfft, 1/Ts);
[data_cart.pxx_Rx, ~ ] = pwelch(detrend(data_cart.Rx_int(data_cart.time<45), 0), win, Noverlap, Nfft, 1/Ts);
[data_cart.pxx_Ry, ~ ] = pwelch(detrend(data_cart.Ry_int(data_cart.time<45), 0), win, Noverlap, Nfft, 1/Ts);
[data_cart_fixed.pxx_Dx, data_cart_fixed.f] = pwelch(detrend(data_cart_fixed.Dx_int(data_cart_fixed.time<45), 0), win, Noverlap, Nfft, 1/Ts);
[data_cart_fixed.pxx_Dy, ~ ] = pwelch(detrend(data_cart_fixed.Dy_int(data_cart_fixed.time<45), 0), win, Noverlap, Nfft, 1/Ts);
[data_cart_fixed.pxx_Dz, ~ ] = pwelch(detrend(data_cart_fixed.Dz_int(data_cart_fixed.time<45), 0), win, Noverlap, Nfft, 1/Ts);
[data_cart_fixed.pxx_Rx, ~ ] = pwelch(detrend(data_cart_fixed.Rx_int(data_cart_fixed.time<45), 0), win, Noverlap, Nfft, 1/Ts);
[data_cart_fixed.pxx_Ry, ~ ] = pwelch(detrend(data_cart_fixed.Ry_int(data_cart_fixed.time<45), 0), win, Noverlap, Nfft, 1/Ts);
[data_cf.pxx_Dx, data_cf.f] = pwelch(detrend(data_cf.Dx_int(data_cf.time<45), 0), win, Noverlap, Nfft, 1/Ts);
[data_cf.pxx_Dy, ~ ] = pwelch(detrend(data_cf.Dy_int(data_cf.time<45), 0), win, Noverlap, Nfft, 1/Ts);
[data_cf.pxx_Dz, ~ ] = pwelch(detrend(data_cf.Dz_int(data_cf.time<45), 0), win, Noverlap, Nfft, 1/Ts);
[data_cf.pxx_Rx, ~ ] = pwelch(detrend(data_cf.Rx_int(data_cf.time<45), 0), win, Noverlap, Nfft, 1/Ts);
[data_cf.pxx_Ry, ~ ] = pwelch(detrend(data_cf.Ry_int(data_cf.time<45), 0), win, Noverlap, Nfft, 1/Ts);
#+end_src
#+begin_src matlab
figure;
hold on;
plot(data.f, data.pxx_Dy)
plot(data_cart.f, data_cart.pxx_Dy)
plot(data_cart_fixed.f, data_cart_fixed.pxx_Dy)
plot(data_cf.f, data_cf.pxx_Dy)
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
% legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1);
xlim([0.1, 5e2]);
#+end_src
#+begin_src matlab
figure;
hold on;
plot(data.f, sqrt(flip(-cumtrapz(flip(data.f), flip(data.pxx_Dy)))))
plot(data_cart.f, sqrt(flip(-cumtrapz(flip(data_cart.f), flip(data_cart.pxx_Dy)))))
plot(data_cart_fixed.f, sqrt(flip(-cumtrapz(flip(data_cart_fixed.f), flip(data_cart_fixed.pxx_Dy)))))
plot(data_cf.f, sqrt(flip(-cumtrapz(flip(data_cf.f), flip(data_cf.pxx_Dy)))))
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
% legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1);
xlim([0.1, 5e2]);
#+end_src
*** Robust Control - 6rpm
#+begin_src matlab
data_tomo_6rpm_m0 = load(sprintf("%s/scans/2023-08-11_11-31_tomography_6rpm_m0.mat", mat_dir));
data_tomo_6rpm_m0.time = Ts*[0:length(data_tomo_6rpm_m0.Rz)-1];
#+end_src
#+begin_src matlab
data_tomo_6rpm_m1 = load(sprintf("%s/scans/2023-08-11_11-23_tomography_6rpm_m1.mat", mat_dir));
data_tomo_6rpm_m1.time = Ts*[0:length(data_tomo_6rpm_m1.Rz)-1];
#+end_src
#+begin_src matlab
%% Compute RMS values while in closed-loop
[~, i_m0] = find(data_tomo_6rpm_m0.hac_status == 1);
data_tomo_6rpm_m0.Dx_rms_cl = rms(detrend(data_tomo_6rpm_m0.Dx_int(i_m0+50000:end), 0));
data_tomo_6rpm_m0.Dy_rms_cl = rms(detrend(data_tomo_6rpm_m0.Dy_int(i_m0+50000:end), 0));
data_tomo_6rpm_m0.Dz_rms_cl = rms(detrend(data_tomo_6rpm_m0.Dz_int(i_m0+50000:end), 0));
data_tomo_6rpm_m0.Rx_rms_cl = rms(detrend(data_tomo_6rpm_m0.Rx_int(i_m0+50000:end), 0));
data_tomo_6rpm_m0.Ry_rms_cl = rms(detrend(data_tomo_6rpm_m0.Ry_int(i_m0+50000:end), 0));
data_tomo_6rpm_m0.Dx_rms_ol = rms(detrend(data_tomo_6rpm_m0.Dx_int(1:i_m0), 0));
data_tomo_6rpm_m0.Dy_rms_ol = rms(detrend(data_tomo_6rpm_m0.Dy_int(1:i_m0), 0));
data_tomo_6rpm_m0.Dz_rms_ol = rms(detrend(data_tomo_6rpm_m0.Dz_int(1:i_m0), 0));
data_tomo_6rpm_m0.Rx_rms_ol = rms(detrend(data_tomo_6rpm_m0.Rx_int(1:i_m0), 0));
data_tomo_6rpm_m0.Ry_rms_ol = rms(detrend(data_tomo_6rpm_m0.Ry_int(1:i_m0), 0));
%% Compute RMS values while in closed-loop
[~, i_m1] = find(data_tomo_6rpm_m1.hac_status == 1);
data_tomo_6rpm_m1.Dx_rms_cl = rms(detrend(data_tomo_6rpm_m1.Dx_int(i_m1+50000:end), 0));
data_tomo_6rpm_m1.Dy_rms_cl = rms(detrend(data_tomo_6rpm_m1.Dy_int(i_m1+50000:end), 0));
data_tomo_6rpm_m1.Dz_rms_cl = rms(detrend(data_tomo_6rpm_m1.Dz_int(i_m1+50000:end), 0));
data_tomo_6rpm_m1.Rx_rms_cl = rms(detrend(data_tomo_6rpm_m1.Rx_int(i_m1+50000:end), 0));
data_tomo_6rpm_m1.Ry_rms_cl = rms(detrend(data_tomo_6rpm_m1.Ry_int(i_m1+50000:end), 0));
data_tomo_6rpm_m1.Dx_rms_ol = rms(detrend(data_tomo_6rpm_m1.Dx_int(1:i_m1), 0));
data_tomo_6rpm_m1.Dy_rms_ol = rms(detrend(data_tomo_6rpm_m1.Dy_int(1:i_m1), 0));
data_tomo_6rpm_m1.Dz_rms_ol = rms(detrend(data_tomo_6rpm_m1.Dz_int(1:i_m1), 0));
data_tomo_6rpm_m1.Rx_rms_ol = rms(detrend(data_tomo_6rpm_m1.Rx_int(1:i_m1), 0));
data_tomo_6rpm_m1.Ry_rms_ol = rms(detrend(data_tomo_6rpm_m1.Ry_int(1:i_m1), 0));
#+end_src
#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*)
data2orgtable([1e6*data_tomo_6rpm_m0.Dx_rms_ol, 1e6*data_tomo_6rpm_m0.Dy_rms_ol, 1e9*data_tomo_6rpm_m0.Dz_rms_ol, 1e6*data_tomo_6rpm_m0.Rx_rms_ol, 1e6*data_tomo_6rpm_m0.Ry_rms_ol; ...
1e6*data_tomo_6rpm_m1.Dx_rms_ol, 1e6*data_tomo_6rpm_m1.Dy_rms_ol, 1e9*data_tomo_6rpm_m1.Dz_rms_ol, 1e6*data_tomo_6rpm_m1.Rx_rms_ol, 1e6*data_tomo_6rpm_m1.Ry_rms_ol], ...
{'$m_0$', '$m_1$'}, {'$D_x$ [$\mu m$]', '$D_y$ [$\mu m$]', '$D_z$ [$nm$]', '$R_x$ [$\mu\text{rad}$]', '$R_y$ [$\mu\text{rad}$]'}, ' %.0f ');
#+end_src
#+name: tab:id31_tomo_6rpm_robust_ol_errors
#+caption: Measured error during open-loop tomography scans (6rpm)
#+attr_latex: :environment tabularx :width \linewidth :align cXXXXX
#+attr_latex: :center t :booktabs t
#+RESULTS:
| | $D_x$ [$\mu m$] | $D_y$ [$\mu m$] | $D_z$ [$nm$] | $R_x$ [$\mu\text{rad}$] | $R_y$ [$\mu\text{rad}$] |
|-------+-----------------+-----------------+--------------+-------------------------+-------------------------|
| $m_0$ | 8 | 7 | 20 | 41 | 41 |
| $m_1$ | 4 | 4 | 21 | 39 | 39 |
#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*)
data2orgtable([1e9*data_tomo_6rpm_m0.Dx_rms_cl, 1e9*data_tomo_6rpm_m0.Dy_rms_cl, 1e9*data_tomo_6rpm_m0.Dz_rms_cl, 1e9*data_tomo_6rpm_m0.Rx_rms_cl, 1e9*data_tomo_6rpm_m0.Ry_rms_cl; ...
1e9*data_tomo_6rpm_m1.Dx_rms_cl, 1e9*data_tomo_6rpm_m1.Dy_rms_cl, 1e9*data_tomo_6rpm_m1.Dz_rms_cl, 1e9*data_tomo_6rpm_m1.Rx_rms_cl, 1e9*data_tomo_6rpm_m1.Ry_rms_cl], ...
{'$m_0$', '$m_1$'}, {'$D_x$ [nm]', '$D_y$ [nm]', '$D_z$ [nm]', '$R_x$ [nrad]', '$R_y$ [nrad]'}, ' %.0f ');
#+end_src
#+name: tab:id31_tomo_6rpm_robust_cl_errors
#+caption: Measured error during closed-loop tomography scans (6rpm, robust controller)
#+attr_latex: :environment tabularx :width \linewidth :align cXXXXX
#+attr_latex: :center t :booktabs t
#+RESULTS:
| | $D_x$ [nm] | $D_y$ [nm] | $D_z$ [nm] | $R_x$ [nrad] | $R_y$ [nrad] |
|-------+------------+------------+------------+--------------+--------------|
| $m_0$ | 17 | 19 | 5 | 70 | 73 |
| $m_1$ | 20 | 26 | 7 | 110 | 77 |
*** TODO Medium velocity tomography scans with CF control :noexport:
#+begin_src matlab
data_m1_cf = load(sprintf("%s/scans/2023-08-21_19-18_m1_6rpm_cf_control.mat", mat_dir));
data_m1_cf.time = Ts*[0:length(data_m1_cf.Rz)-1];
#+end_src
#+begin_src matlab
data_m2_cf = load(sprintf("%s/scans/2023-08-21_18-07_m2_6rpm_cf_control.mat", mat_dir));
data_m2_cf.time = Ts*[0:length(data_m2_cf.Rz)-1];
#+end_src
And higher bandwidth:
#+begin_src matlab
data_m1_cf_high_fb = load(sprintf("%s/scans/2023-08-21_19-24_m1_6rpm_cf_control_60Hz.mat", mat_dir));
data_m1_cf_high_fb.time = Ts*[0:length(data_m1_cf_high_fb.Rz)-1];
#+end_src
#+begin_src matlab
figure;
hold on;
plot(data_m1_cf.Dy_int, detrend(data_m1_cf.Dz_int, 0))
plot(data_m2_cf.Dy_int, detrend(data_m2_cf.Dz_int, 0))
plot(data_m1_cf_high_fb.Dy_int, detrend(data_m1_cf_high_fb.Dz_int, 0))
axis equal
#+end_src
#+begin_src matlab
1e9*rms(detrend(data_m1.Dz_int(i_m1+50000:end), 0))
1e9*rms(detrend(data_m1.Dy_int(i_m1+50000:end), 0))
1e9*rms(detrend(data_m1.Ry_int(i_m1+50000:end), 0))
#+end_src
#+begin_src matlab
1e9*rms(detrend(data_m1_cf.Dz_int, 0))
1e9*rms(detrend(data_m1_cf.Dy_int, 0))
1e9*rms(detrend(data_m1_cf.Ry_int, 0))
#+end_src
#+begin_src matlab
1e9*rms(detrend(data_m2.Dz_int, 0))
1e9*rms(detrend(data_m2.Dy_int, 0))
1e9*rms(detrend(data_m2.Ry_int, 0))
#+end_src
#+begin_src matlab
1e9*rms(detrend(data_m1_high_fb.Dz_int, 0))
1e9*rms(detrend(data_m1_high_fb.Dy_int, 0))
1e9*rms(detrend(data_m1_high_fb.Ry_int, 0))
#+end_src
*** Robust Control - 30rpm
#+begin_src matlab
%% Load Data
data_tomo_30rpm_m0 = load(sprintf("%s/scans/2023-08-17_15-26_tomography_30rpm_m0_robust.mat", mat_dir));
data_tomo_30rpm_m0.time = Ts*[0:length(data_tomo_30rpm_m0.Rz)-1];
#+end_src
#+begin_src matlab :exports none :results none
%% Measured motion during tomography scan at 30RPM with a robust controller
figure;
tiledlayout(1, 2, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile;
hold on;
plot(1e6*data_tomo_30rpm_m0.Dx_int(data_tomo_30rpm_m0.hac_status == 0), 1e6*data_tomo_30rpm_m0.Dy_int(data_tomo_30rpm_m0.hac_status == 0), ...
'DisplayName', 'OL')
plot(1e6*data_tomo_30rpm_m0.Dx_int(data_tomo_30rpm_m0.hac_status == 1), 1e6*data_tomo_30rpm_m0.Dy_int(data_tomo_30rpm_m0.hac_status == 1), ...
'DisplayName', 'CL')
hold off;
axis square
xlabel("X motion [$\mu m$]");
ylabel("Y motion [$\mu m$]");
legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
xlim([-3, 3])
ylim([-3, 3])
ax2 = nexttile;
hold on;
plot(1e6*data_tomo_30rpm_m0.Dy_int(data_tomo_30rpm_m0.hac_status == 0), 1e6*data_tomo_30rpm_m0.Dz_int(data_tomo_30rpm_m0.hac_status == 0), ...
'DisplayName', 'OL')
plot(1e6*data_tomo_30rpm_m0.Dy_int(data_tomo_30rpm_m0.hac_status == 1), 1e6*data_tomo_30rpm_m0.Dz_int(data_tomo_30rpm_m0.hac_status == 1), ...
'DisplayName', 'CL')
hold off;
axis equal
xlim([-3, 3])
ylim([-3, 3])
xlabel("Y motion [$\mu m$]");
ylabel("Z motion [$\mu m$]");
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_tomography_m0_30rpm_robust_xyz.pdf', 'width', 'full', 'height', 'tall');
#+end_src
#+name: fig:id31_tomography_m0_30rpm_robust_xyz
#+caption: Measured motion during tomography scan at 30RPM with a robust controller
#+RESULTS:
[[file:figs/id31_tomography_m0_30rpm_robust_xyz.png]]
#+begin_src matlab
%% Compute RMS values while in closed-loop
[~, i_m0] = find(data_tomo_30rpm_m0.hac_status == 1);
data_tomo_30rpm_m0.Dx_rms_cl = rms(detrend(data_tomo_30rpm_m0.Dx_int(i_m0+50000:end), 0));
data_tomo_30rpm_m0.Dy_rms_cl = rms(detrend(data_tomo_30rpm_m0.Dy_int(i_m0+50000:end), 0));
data_tomo_30rpm_m0.Dz_rms_cl = rms(detrend(data_tomo_30rpm_m0.Dz_int(i_m0+50000:end), 0));
data_tomo_30rpm_m0.Rx_rms_cl = rms(detrend(data_tomo_30rpm_m0.Rx_int(i_m0+50000:end), 0));
data_tomo_30rpm_m0.Ry_rms_cl = rms(detrend(data_tomo_30rpm_m0.Ry_int(i_m0+50000:end), 0));
data_tomo_30rpm_m0.Dx_rms_ol = rms(detrend(data_tomo_30rpm_m0.Dx_int(1:i_m0), 0));
data_tomo_30rpm_m0.Dy_rms_ol = rms(detrend(data_tomo_30rpm_m0.Dy_int(1:i_m0), 0));
data_tomo_30rpm_m0.Dz_rms_ol = rms(detrend(data_tomo_30rpm_m0.Dz_int(1:i_m0), 0));
data_tomo_30rpm_m0.Rx_rms_ol = rms(detrend(data_tomo_30rpm_m0.Rx_int(1:i_m0), 0));
data_tomo_30rpm_m0.Ry_rms_ol = rms(detrend(data_tomo_30rpm_m0.Ry_int(1:i_m0), 0));
#+end_src
#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*)
data2orgtable([1e6*data_tomo_30rpm_m0.Dx_rms_ol, 1e6*data_tomo_30rpm_m0.Dy_rms_ol, 1e9*data_tomo_30rpm_m0.Dz_rms_ol, 1e6*data_tomo_30rpm_m0.Rx_rms_ol, 1e6*data_tomo_30rpm_m0.Ry_rms_ol], ...
{'$m_0$'}, {'$D_x$ [$\mu m$]', '$D_y$ [$\mu m$]', '$D_z$ [$nm$]', '$R_x$ [$\mu\text{rad}$]', '$R_y$ [$\mu\text{rad}$]'}, ' %.0f ');
#+end_src
#+name: tab:id31_tomo_30rpm_robust_ol_errors
#+caption: Measured error during open-loop tomography scans (30rpm)
#+attr_latex: :environment tabularx :width \linewidth :align cXXXXX
#+attr_latex: :center t :booktabs t
#+RESULTS:
| | $D_x$ [$\mu m$] | $D_y$ [$\mu m$] | $D_z$ [$nm$] | $R_x$ [$\mu\text{rad}$] | $R_y$ [$\mu\text{rad}$] |
|-------+-----------------+-----------------+--------------+-------------------------+-------------------------|
| $m_0$ | 2 | 2 | 24 | 10 | 10 |
#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*)
data2orgtable([1e9*data_tomo_30rpm_m0.Dx_rms_cl, 1e9*data_tomo_30rpm_m0.Dy_rms_cl, 1e9*data_tomo_30rpm_m0.Dz_rms_cl, 1e9*data_tomo_30rpm_m0.Rx_rms_cl, 1e9*data_tomo_30rpm_m0.Ry_rms_cl], ...
{'$m_0$'}, {'$D_x$ [nm]', '$D_y$ [nm]', '$D_z$ [nm]', '$R_x$ [nrad]', '$R_y$ [nrad]'}, ' %.0f ');
#+end_src
#+name: tab:id31_tomo_30rpm_robust_cl_errors
#+caption: Measured error during closed-loop tomography scans (30rpm, robust controller)
#+attr_latex: :environment tabularx :width \linewidth :align cXXXXX
#+attr_latex: :center t :booktabs t
#+RESULTS:
| | $D_x$ [nm] | $D_y$ [nm] | $D_z$ [nm] | $R_x$ [nrad] | $R_y$ [nrad] |
|-------+------------+------------+------------+--------------+--------------|
| $m_0$ | 34 | 38 | 10 | 127 | 129 |
#+begin_src matlab :exports none
yztomography3dmovie('movies/id31_tomography_m0_30rpm_robust_xyz.avi', data_tomo_30rpm_m0, 'di', 300);
#+end_src
#+begin_src matlab :exports none
yztomographymovie('movies/tomography_30rpm_m0_robust', data_tomo_30rpm_m0, 'xlim_ax1', [-3, 3], 'ylim_ax1', [-3, 3], 'xlim_ax2', [-300, 300], 'ylim_ax2', [-300, 300])
#+end_src
*** TODO Fast Tomography Scan with Complementary Filter Controller :noexport:
#+begin_src matlab
data_cf = load(sprintf("%s/scans/2023-08-21_14-33_m0_30rpm_cf_control.mat", mat_dir));
data_cf.time = Ts*[0:length(data_cf.Rz)-1];
#+end_src
#+begin_src matlab
[~, i0] = find(data.hac_status == 1);
1e9*rms(data.Dy_int(i0(1)+5000:end))
1e9*rms(data.Dz_int(i0(1)+5000:end))
1e9*rms(data_cf.Dy_int)
1e9*rms(data_cf.Dz_int)
#+end_src
Same performance than the robust controller in terms of RMS residual motion.
#+begin_src matlab
figure; plot3(data.Dx_int, data.Dy_int, data.Dz_int)
#+end_src
*** Tomography - Effect of the scanning velocity :noexport:
- [ ] What are the controller used here? Why worst results than with the robust controller?
#+begin_src matlab
data_1rpm = load(sprintf("%s/scans/2023-08-18_10-43_m0_1rpm.mat", mat_dir));
data_1rpm.time = Ts*[0:length(data_1rpm.Rz)-1];
#+end_src
#+begin_src matlab
data_30rpm = load(sprintf("%s/scans/2023-08-18_10-45_m0_30rpm.mat", mat_dir));
data_30rpm.time = Ts*[0:length(data_30rpm.Rz)-1];
#+end_src
#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*)
data2orgtable([1e9*rms(detrend(data_1rpm.Dy_int, 0)), 1e9*rms(detrend(data_30rpm.Dy_int, 0)); 1e9*rms(detrend(data_1rpm.Dz_int, 0)), 1e9*rms(detrend(data_30rpm.Dz_int, 0)); 1e9*rms(detrend(data_1rpm.Ry_int, 0)), 1e9*rms(detrend(data_30rpm.Ry_int, 0))]', {'1RPM', '30RPM'}, {'$D_y$', '$D_z$', '$R_y$'}, ' %.1f ');
#+end_src
#+name: tab:id31_tomography_effect_velocity_rms
#+caption: RMS values of the errors during tomography scan - Effect of $R_z$ velocity
#+attr_latex: :environment tabularx :width 0.5\linewidth :align lXXX
#+attr_latex: :center t :booktabs t
#+RESULTS:
| | $D_y$ | $D_z$ | $R_y$ |
|-------+-------+-------+-------|
| 1RPM | 30.9 | 5.9 | 92.4 |
| 30RPM | 71.7 | 12.5 | 301.3 |
#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*)
data2orgtable([1e9*data_tomo_1rpm_m0.Dx_rms_cl, 1e9*data_tomo_1rpm_m0.Dy_rms_cl, 1e9*data_tomo_1rpm_m0.Dz_rms_cl, 1e9*data_tomo_1rpm_m0.Rx_rms_cl, 1e9*data_tomo_1rpm_m0.Ry_rms_cl; ...
1e9*data_tomo_6rpm_m0.Dx_rms_cl, 1e9*data_tomo_6rpm_m0.Dy_rms_cl, 1e9*data_tomo_6rpm_m0.Dz_rms_cl, 1e9*data_tomo_6rpm_m0.Rx_rms_cl, 1e9*data_tomo_6rpm_m0.Ry_rms_cl; ...
1e9*data_tomo_30rpm_m0.Dx_rms_cl, 1e9*data_tomo_30rpm_m0.Dy_rms_cl, 1e9*data_tomo_30rpm_m0.Dz_rms_cl, 1e9*data_tomo_30rpm_m0.Rx_rms_cl, 1e9*data_tomo_30rpm_m0.Ry_rms_cl], ...
{'1rpm', '6rpm', '30rpm'}, {'$D_x$ [$\mu m$]', '$D_y$ [$\mu m$]', '$D_z$ [$nm$]', '$R_x$ [$\mu\text{rad}$]', '$R_y$ [$\mu\text{rad}$]'}, ' %.0f ');
#+end_src
#+RESULTS:
| | $D_x$ [$\mu m$] | $D_y$ [$\mu m$] | $D_z$ [$nm$] | $R_x$ [$\mu\text{rad}$] | $R_y$ [$\mu\text{rad}$] |
|-------+-----------------+-----------------+--------------+-------------------------+-------------------------|
| 1rpm | 13 | 15 | 5 | 57 | 55 |
| 6rpm | 17 | 19 | 5 | 70 | 73 |
| 30rpm | 34 | 38 | 10 | 127 | 129 |
** $D_z$ scans: Dirty Layer Scans
<<sec:id31_scans_dz>>
*** Step by Step $D_z$ motion
#+begin_src matlab
%% Load Dz MIM data
data_dz_steps_3nm = load(sprintf("%s/scans/2023-08-18_14-57_dz_mim_3_nm.mat", mat_dir));
data_dz_steps_3nm.time = Ts*[0:length(data_dz_steps_3nm.Dz_int)-1];
data_dz_steps_10nm = load(sprintf("%s/scans/2023-08-18_14-57_dz_mim_10_nm.mat", mat_dir));
data_dz_steps_10nm.time = Ts*[0:length(data_dz_steps_10nm.Dz_int)-1];
data_dz_steps_100nm = load(sprintf("%s/scans/2023-08-18_14-57_dz_mim_100_nm.mat", mat_dir));
data_dz_steps_100nm.time = Ts*[0:length(data_dz_steps_100nm.Dz_int)-1];
data_dz_steps_1000nm = load(sprintf("%s/scans/2023-08-18_14-57_dz_mim_1000_nm.mat", mat_dir));
data_dz_steps_1000nm.time = Ts*[0:length(data_dz_steps_1000nm.Dz_int)-1];
#+end_src
Three step sizes are tested:
- $10\,nm$ steps (Figure ref:fig:id31_dz_mim_10nm_steps)
- $100\,nm$ steps (Figure ref:fig:id31_dz_mim_100nm_steps)
- $1\,\mu m$ steps (Figure ref:fig:id31_dz_steps_response)
#+begin_src matlab :exports none :results none
%% Dz MIM test with 10nm steps
figure;
hold on;
plot(data_dz_steps_10nm.time, 1e9*(data_dz_steps_10nm.Dz_int_filtered - mean(data_dz_steps_10nm.Dz_int_filtered(1:1000))))
hold off;
xlabel('Time [s]');
ylabel('$D_z$ Motion [nm]');
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_dz_mim_10nm_steps.pdf', 'width', 'wide', 'height', 'normal');
#+end_src
#+name: fig:id31_dz_mim_10nm_steps
#+caption: Dz MIM test with 10nm steps (low pass filter with cut-off frequency of 10Hz is applied)
#+RESULTS:
[[file:figs/id31_dz_mim_10nm_steps.png]]
#+begin_src matlab :exports none :results none
%% Dz MIM test with 10nm steps
figure;
hold on;
plot(data_dz_steps_100nm.time, 1e9*(data_dz_steps_100nm.Dz_int - mean(data_dz_steps_100nm.Dz_int(1:1000))))
hold off;
xlabel('Time [s]');
ylabel('$D_z$ Motion [nm]');
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_dz_mim_100nm_steps.pdf', 'width', 'wide', 'height', 'normal');
#+end_src
#+name: fig:id31_dz_mim_100nm_steps
#+caption: Dz MIM test with 100nm steps
#+RESULTS:
[[file:figs/id31_dz_mim_100nm_steps.png]]
#+begin_src matlab :exports none :results none
%% Dz step response - Stabilization time is around 70ms
figure;
[~, i] = find(data_dz_steps_1000nm.m_hexa_dz>data_dz_steps_1000nm.m_hexa_dz(1));
i0 = i(1);
figure;
hold on;
plot(data_dz_steps_1000nm.time-data_dz_steps_1000nm.time(i0), 1e9*(data_dz_steps_1000nm.Dz_int - mean(data_dz_steps_1000nm.Dz_int(1:1000))))
plot([-1, 1], [1000-20, 1000-20], 'k--')
plot([-1, 1], [1000+20, 1000+20], 'k--')
xline(0, 'k--', 'LineWidth', 1.5)
xline(0.07, 'k--', 'LineWidth', 1.5)
hold off;
xlabel('Time [s]');
ylabel('$D_z$ Motion [nm]');
xlim([-0.1, 0.2]);
ylim([-100, 1600])
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_dz_steps_response.pdf', 'width', 'wide', 'height', 'normal');
#+end_src
#+name: fig:id31_dz_steps_response
#+caption: $D_z$ step response - Stabilization time is around 70ms
#+RESULTS:
[[file:figs/id31_dz_steps_response.png]]
*** Continuous $D_z$ motion: Dirty Layer Scans
#+begin_src matlab
data_dz_10ums = load(sprintf("%s/scans/2023-08-18_15-33_dirty_layer_m0_small.mat", mat_dir));
data_dz_10ums.time = Ts*[0:length(data_dz_10ums.Dz_int)-1];
#+end_src
#+begin_src matlab
data_dz_100ums = load(sprintf("%s/scans/2023-08-18_15-32_dirty_layer_m0.mat", mat_dir));
data_dz_100ums.time = Ts*[0:length(data_dz_100ums.Dz_int)-1];
#+end_src
Two $D_z$ scans are performed:
- at $10\,\mu m/s$ in Figure ref:fig:id31_dirty_layer_scan_m0
- at $100\,\mu m/s$ in Figure ref:fig:id31_dirty_layer_scan_m0_large
#+begin_src matlab :exports none :results none
%% Dirty layer scan: Dz motion
figure;
hold on;
plot(data_dz_10ums.time, 1e6*data_dz_10ums.Dz_int, ...
'DisplayName', sprintf('$\\epsilon D_z = %.0f$ nm RMS', 1e9*rms(data_dz_10ums.e_dz)))
plot(data_dz_10ums.time, 1e6*data_dz_10ums.e_dy, ...
'DisplayName', sprintf('$\\epsilon D_y = %.0f$ nm RMS', 1e9*rms(data_dz_10ums.e_dy)))
plot(data_dz_10ums.time, 1e6*data_dz_10ums.e_ry, ...
'DisplayName', sprintf('$\\epsilon R_y = %.2f$ $\\mu$rad RMS', 1e6*rms(data_dz_10ums.e_ry)))
hold off;
xlabel('Time [s]');
ylabel('Motion [$\mu$m]');
xlim([0, 2.2])
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_dirty_layer_scan_m0.pdf', 'width', 'wide', 'height', 'normal');
#+end_src
#+name: fig:id31_dirty_layer_scan_m0
#+caption: Dirty layer scan: $D_z$ motion at $10\,\mu m/s$
#+RESULTS:
[[file:figs/id31_dirty_layer_scan_m0.png]]
#+begin_src matlab :exports none :results none
%% Dirty layer scan: Dz motion
figure;
hold on;
plot(data_dz_100ums.time, 1e6*data_dz_100ums.Dz_int, ...
'DisplayName', sprintf('$\\epsilon D_z = %.0f$ nm RMS', 1e9*rms(data_dz_100ums.e_dz)))
plot(data_dz_100ums.time, 1e6*data_dz_100ums.e_dy, ...
'DisplayName', sprintf('$\\epsilon D_y = %.0f$ nm RMS', 1e9*rms(data_dz_100ums.e_dy)))
plot(data_dz_100ums.time, 1e6*data_dz_100ums.e_ry, ...
'DisplayName', sprintf('$\\epsilon R_y = %.2f$ $\\mu$rad RMS', 1e6*rms(data_dz_100ums.e_ry)))
hold off;
xlabel('Time [s]');
ylabel('Motion [$\mu$m]');
xlim([0, 2.2])
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_dirty_layer_scan_m0_large.pdf', 'width', 'wide', 'height', 'normal');
#+end_src
#+name: fig:id31_dirty_layer_scan_m0_large
#+caption: Dirty layer scan: $D_z$ motion at $100\,\mu m/s$
#+RESULTS:
[[file:figs/id31_dirty_layer_scan_m0_large.png]]
#+begin_src matlab
%% Not so good results with the CF controller
data_cf = load(sprintf("%s/scans/2023-08-21_19-20_dirty_layer_m1_cf.mat", mat_dir));
data_cf.time = Ts*[0:length(data_cf.Dz_int)-1];
#+end_src
** $R_y$ scans: Reflectivity
<<sec:id31_scans_reflectivity>>
#+begin_src matlab
%% Load data for the reflectivity scan
data_ry = load(sprintf("%s/scans/2023-08-18_15-24_first_reflectivity_m0.mat", mat_dir));
data_ry.time = Ts*[0:length(data_ry.Ry_int)-1];
#+end_src
An $R_y$ scan is performed at $100\,\mu rad/s$ velocity (Figure ref:fig:id31_reflectivity_scan_Ry_100urads).
During the $R_y$ scan, the errors in $D_y$ are $D_z$ are kept small.
#+begin_src matlab :exports none :results none
%% Ry reflectivity scan
figure;
hold on;
plot(data_ry.time, 1e6*data_ry.Ry_int, 'DisplayName', sprintf('$\\epsilon R_y = %.2f$ $\\mu$rad RMS', 1e6*rms(data_ry.e_ry)))
plot(data_ry.time, 1e6*data_ry.e_dy, 'DisplayName', sprintf('$\\epsilon D_y = %.0f$ nm RMS', 1e9*rms(data_ry.e_dy)))
plot(data_ry.time, 1e6*data_ry.e_dz, 'DisplayName', sprintf('$\\epsilon D_z = %.0f$ nm RMS', 1e9*rms(data_ry.e_dz)))
hold off;
xlabel('Time [s]');
ylabel('$R_y$ motion [$\mu$rad]')
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1);
xlim([0, 6.2]);
ylim([-310, 310])
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_reflectivity_scan_Ry_100urads.pdf', 'width', 'wide', 'height', 'normal');
#+end_src
#+name: fig:id31_reflectivity_scan_Ry_100urads
#+caption: $R_y$ reflecitivity scan at $100\,\mu\text{rad}/s$ velocity
#+RESULTS:
[[file:figs/id31_reflectivity_scan_Ry_100urads.png]]
** $D_y$ Scans
<<sec:id31_scans_dy>>
*** Introduction :ignore:
The steps generated by the IcePAP for the $T_y$ stage are send to the Speedgoat.
Then, we can know in real time what is the wanted position in $D_y$ during $T_y$ scans.
*** Open Loop
#+begin_src matlab
%% Slow Ty scan (10um/s)
data_ty_ol_slow = load(sprintf("%s/scans/2023-08-21_20-05_ty_scan_m1_open_loop_slow.mat", mat_dir));
data_ty_ol_slow.time = Ts*[0:length(data_ty_ol_slow.Dy_int)-1];
#+end_src
We can clearly see micro-stepping errors of the stepper motor used for the $T_y$ stage.
The errors have a period of $10\,\mu m$ with an amplitude of $\pm 100\,nm$.
#+begin_src matlab :exports none :results none
%% Ty scan (at 10um/s) - Dy errors
figure;
plot(1e6*data_ty_ol_slow.Ty, 1e6*detrend(data_ty_ol_slow.e_dy, 0))
xlabel('Ty position [$\mu$m]');
ylabel('$D_y$ error [$\mu$m]');
xlim([-100, 100])
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_ty_scan_10ums_ol_dy_errors.pdf', 'width', 'wide', 'height', 'normal');
#+end_src
#+name: fig:id31_ty_scan_10ums_ol_dy_errors
#+caption: $T_y$ scan (at $10\,\mu m/s$) - $D_y$ errors. The micro-stepping errors can clearly be seen with a period of $10\,\mu m$ and an amplitude of $\pm 100\,nm$
#+RESULTS:
[[file:figs/id31_ty_scan_10ums_ol_dy_errors.png]]
#+begin_src matlab :exports none :results none
%% Ty scan (at 10um/s) - Dz and Ry errors
figure;
tiledlayout(1, 2, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile;
hold on;
plot(1e6*data_ty_ol_slow.Ty, 1e6*detrend(data_ty_ol_slow.e_dz, 0))
hold off;
xlabel('Ty position [$\mu$m]');
ylabel('$D_z$ error [$\mu$m]');
xlim([-100, 100])
ax2 = nexttile;
hold on;
plot(1e6*data_ty_ol_slow.Ty, 1e6*data_ty_ol_slow.e_ry)
hold off;
xlabel('Ty position [$\mu$m]');
ylabel('$R_y$ error [$\mu$rad]');
xlim([-100, 100])
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_ty_scan_10ums_ol_dz_ry_errors.pdf', 'width', 'full', 'height', 'normal');
#+end_src
#+name: fig:id31_ty_scan_10ums_ol_dz_ry_errors
#+caption: $T_y$ scan (at $10\,\mu m/s$) - $D_z$ and $R_y$ errors. The $D_z$ error is most likely due to having the top interferometer pointing to a sphere. The large $R_y$ errors might also be due to the metrology system
#+RESULTS:
[[file:figs/id31_ty_scan_10ums_ol_dz_ry_errors.png]]
*** Closed Loop
#+begin_src matlab
%% Slow Ty scan (10um/s) - CL
data_ty_cl_slow = load(sprintf("%s/scans/2023-08-21_20-07_ty_scan_m1_cf_closed_loop_slow.mat", mat_dir));
data_ty_cl_slow.time = Ts*[0:length(data_ty_cl_slow.Dy_int)-1];
#+end_src
#+begin_src matlab :exports none :results none
%% Ty scan (at 10um/s) - Dy errors
figure;
hold on;
plot(1e6*data_ty_ol_slow.Ty, 1e6*detrend(data_ty_ol_slow.e_dy, 0), ...
'DisplayName', 'OL')
plot(1e6*data_ty_cl_slow.Ty, 1e6*detrend(data_ty_cl_slow.e_dy, 0), ...
'DisplayName', sprintf('CL - $\\epsilon D_y = %.0f$ nmRMS', 1e9*rms(detrend(data_ty_cl_slow.e_dy, 0))))
hold off;
xlabel('Ty position [$\mu$m]');
ylabel('$D_y$ error [$\mu$m]');
xlim([-100, 100])
legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_ty_scan_10ums_cl_dy_errors.pdf', 'width', 'wide', 'height', 'normal');
#+end_src
#+name: fig:id31_ty_scan_10ums_cl_dy_errors
#+caption: $T_y$ scan (at $10\,\mu m/s$) - $D_y$ errors. Open-loop and Closed-loop scans
#+RESULTS:
[[file:figs/id31_ty_scan_10ums_cl_dy_errors.png]]
#+begin_src matlab :exports none :results none
%% Ty scan (at 10um/s) - Dz and Ry errors
figure;
tiledlayout(1, 2, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile;
hold on;
plot(1e6*data_ty_ol_slow.Ty, 1e6*detrend(data_ty_ol_slow.e_dz, 0), ...
'DisplayName', 'OL')
plot(1e6*data_ty_cl_slow.Ty, 1e6*detrend(data_ty_cl_slow.e_dz, 0), ...
'DisplayName', sprintf('Cl - $\\epsilon D_z = %.0f$ nmRMS', 1e9*rms(detrend(data_ty_cl_slow.e_dz, 0))))
hold off;
xlabel('Ty position [$\mu$m]');
ylabel('$D_z$ error [$\mu$m]');
xlim([-100, 100])
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1);
ax2 = nexttile;
hold on;
plot(1e6*data_ty_ol_slow.Ty, 1e6*data_ty_ol_slow.e_ry, ...
'DisplayName', 'OL')
plot(1e6*data_ty_cl_slow.Ty, 1e6*data_ty_cl_slow.e_ry, ...
'DisplayName', sprintf('Cl - $\\epsilon R_y = %.2f$ uradRMS', 1e6*rms(detrend(data_ty_cl_slow.e_ry, 0))))
hold off;
xlabel('Ty position [$\mu$m]');
ylabel('$R_y$ error [$\mu$rad]');
xlim([-100, 100])
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_ty_scan_10ums_cl_dz_ry_errors.pdf', 'width', 'full', 'height', 'normal');
#+end_src
#+name: fig:id31_ty_scan_10ums_cl_dz_ry_errors
#+caption: $T_y$ scan (at $10\,\mu m/s$) - $D_z$ and $R_y$ errors. Open-loop and Closed-loop scans
#+RESULTS:
[[file:figs/id31_ty_scan_10ums_cl_dz_ry_errors.png]]
*** Faster Scan
#+begin_src matlab
%% Fast Ty scan (100um/s) - OL
data_ty_ol_fast = load(sprintf("%s/scans/2023-08-21_20-05_ty_scan_m1_open_loop.mat", mat_dir));
data_ty_ol_fast.time = Ts*[0:length(data_ty_ol_fast.Dy_int)-1];
#+end_src
#+begin_src matlab
%% Fast Ty scan (10um/s) - CL
data_ty_cl_fast = load(sprintf("%s/scans/2023-08-21_20-07_ty_scan_m1_cf_closed_loop.mat", mat_dir));
data_ty_cl_fast.time = Ts*[0:length(data_ty_cl_fast.Dy_int)-1];
#+end_src
Because of micro-stepping errors of the Ty stepper motor, when scanning at high velocity this induce high frequency vibration that are outside the bandwidth of the feedback controller.
At $100\,\mu m/s$, the micro-stepping errors with a period of $10\,\mu m$ (see Figure ref:fig:id31_ty_scan_10ums_ol_dy_errors) are at 10Hz.
These errors are them amplified by some resonances in the system.
This could be easily solved by changing the stepper motor for a torque motor for instance.
#+begin_src matlab :exports none :results none
%% Ty scan (at 100um/s) - Dy errors
figure;
hold on;
plot(1e6*data_ty_ol_fast.Ty, 1e6*detrend(data_ty_ol_fast.e_dy, 0), ...
'DisplayName', 'OL')
plot(1e6*data_ty_cl_fast.Ty, 1e6*detrend(data_ty_cl_fast.e_dy, 0), ...
'DisplayName', sprintf('CL - $\\epsilon D_y = %.0f$ nmRMS', 1e9*rms(detrend(data_ty_cl_fast.e_dy, 0))))
hold off;
xlabel('Ty position [$\mu$m]');
ylabel('$D_y$ error [$\mu$m]');
xlim([-100, 100])
legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_ty_scan_100ums_cl_dy_errors.pdf', 'width', 'wide', 'height', 'normal');
#+end_src
#+name: fig:id31_ty_scan_100ums_cl_dy_errors
#+caption: $T_y$ scan (at $100\,\mu m/s$) - $D_y$ errors. Open-loop and Closed-loop scans
#+RESULTS:
[[file:figs/id31_ty_scan_100ums_cl_dy_errors.png]]
#+begin_src matlab :exports none :results none
%% Ty scan (at 100um/s) - Dz and Ry errors
figure;
tiledlayout(1, 2, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile;
hold on;
plot(1e6*data_ty_ol_fast.Ty, 1e6*detrend(data_ty_ol_fast.e_dz, 0), ...
'DisplayName', 'OL')
plot(1e6*data_ty_cl_fast.Ty, 1e6*detrend(data_ty_cl_fast.e_dz, 0), ...
'DisplayName', sprintf('Cl - $\\epsilon D_z = %.0f$ nmRMS', 1e9*rms(detrend(data_ty_cl_fast.e_dz, 0))))
hold off;
xlabel('Ty position [$\mu$m]');
ylabel('$D_z$ error [$\mu$m]');
xlim([-100, 100])
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1);
ax2 = nexttile;
hold on;
plot(1e6*data_ty_ol_fast.Ty, 1e6*data_ty_ol_fast.e_ry, ...
'DisplayName', 'OL')
plot(1e6*data_ty_cl_fast.Ty, 1e6*data_ty_cl_fast.e_ry, ...
'DisplayName', sprintf('Cl - $\\epsilon R_y = %.2f$ uradRMS', 1e6*rms(detrend(data_ty_cl_fast.e_ry, 0))))
hold off;
xlabel('Ty position [$\mu$m]');
ylabel('$R_y$ error [$\mu$rad]');
xlim([-100, 100])
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_ty_scan_100ums_cl_dz_ry_errors.pdf', 'width', 'full', 'height', 'normal');
#+end_src
#+name: fig:id31_ty_scan_100ums_cl_dz_ry_errors
#+caption: $T_y$ scan (at $100\,\mu m/s$) - $D_z$ and $R_y$ errors. Open-loop and Closed-loop scans
#+RESULTS:
[[file:figs/id31_ty_scan_100ums_cl_dz_ry_errors.png]]
** Combined $R_z$ and $D_y$: Diffraction Tomography
<<sec:id31_scans_diffraction_tomo>>
Instead of doing a fast $R_z$ motion a slow $D_y$, the idea is to perform slow $R_z$ (here 1rpm) and fast $D_y$ scans with the nano-hexapod.
#+begin_src matlab
%% 100um/s - Robust controller
data_dt_100ums = load(sprintf("%s/scans/2023-08-18_17-12_diffraction_tomo_m0.mat", mat_dir));
data_dt_100ums.time = Ts*[0:length(data_dt_100ums.Dy_int)-1];
%% 500um/s - Robust controller (Not used)
% data_dt_500ums = load(sprintf("%s/scans/2023-08-18_17-19_diffraction_tomo_m0_fast.mat", mat_dir));
% data_dt_500ums.time = Ts*[0:length(data_dt_500ums.Dy_int)-1];
%% 500um/s - Complementary filters
data_dt_500ums = load(sprintf("%s/scans/2023-08-21_15-15_diffraction_tomo_m0_fast_cf.mat", mat_dir));
data_dt_500ums.time = Ts*[0:length(data_dt_500ums.Dy_int)-1];
%% 1mm/s - Complementary filters
data_dt_1000ums = load(sprintf("%s/scans/2023-08-21_15-16_diffraction_tomo_m0_fast_cf.mat", mat_dir));
data_dt_1000ums.time = Ts*[0:length(data_dt_1000ums.Dy_int)-1];
%% 5mm/s - Complementary filters
% data_dt_5000ums = load(sprintf("%s/scans/2023-08-21_18-03_diffraction_tomo_m2_fast_cf.mat", mat_dir));
% data_dt_5000ums.time = Ts*[0:length(data_dt_5000ums.Dy_int)-1];
%% 10mm/s - Complementary filters
data_dt_10000ums = load(sprintf("%s/scans/2023-08-21_15-17_diffraction_tomo_m0_fast_cf.mat", mat_dir));
data_dt_10000ums.time = Ts*[0:length(data_dt_10000ums.Dy_int)-1];
#+end_src
Here, the $D_y$ scans are performed only with the nano-hexapod (the Ty stage is not moving), so we are limited to $\pm 100\,\mu m$.
Several $D_y$ velocities are tested: $0.1\,mm/s$, $0.5\,mm/s$, $1\,mm/s$ and $10\,mm/s$ (see Figure ref:fig:id31_diffraction_tomo_velocities).
#+begin_src matlab :exports none :results none
%% Dy motion for several configured velocities
figure;
hold on;
plot(data_dt_10000ums.time, 1e6*data_dt_10000ums.Dy_int, ...
'DisplayName', '$10 mm/s$')
plot(data_dt_1000ums.time, 1e6*data_dt_1000ums.Dy_int, ...
'DisplayName', '$1 mm/s$')
plot(data_dt_500ums.time, 1e6*data_dt_500ums.Dy_int, ...
'DisplayName', '$0.5 mm/s$')
plot(data_dt_100ums.time, 1e6*data_dt_100ums.Dy_int, ...
'DisplayName', '$0.1 mm/s$')
hold off;
xlim([0, 4]);
xlabel('Time [s]');
ylabel('$D_y$ position [$\mu$m]')
legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_diffraction_tomo_velocities.pdf', 'width', 'wide', 'height', 'normal');
#+end_src
#+name: fig:id31_diffraction_tomo_velocities
#+caption: Dy motion for several configured velocities
#+RESULTS:
[[file:figs/id31_diffraction_tomo_velocities.png]]
The corresponding "repetition rate" and $D_y$ scan per spindle turn are shown in Table ref:tab:diffraction_tomo_velocities.
The main issue here is the "waiting" time between two scans that is in the order of 50ms.
By removing this waiting time (fairly easily), we can double the repetition rate at 10mm/s.
#+name: tab:diffraction_tomo_velocities
#+caption: $D_y$ scaning repetition rate
#+attr_latex: :environment tabularx :width 0.6\linewidth :align lXX
#+attr_latex: :center t :booktabs t
| $D_y$ Velocity | Repetition rate | Scans per turn (at 1RPM) |
|----------------+-----------------+--------------------------|
| 0.1 mm/s | 4 s | 15 |
| 0.5 mm/s | 0.9 s | 65 |
| 1 mm/s | 0.5 s | 120 |
| 10 mm/s | 0.18 s | 330 |
The scan results for a velocity of 1mm/s is shown in Figure ref:fig:id31_diffraction_tomo_1mms.
The $D_z$ and $R_y$ errors are quite small during the scan.
The $D_y$ errors are quite large as the velocity is increased.
This type of scan can probably be massively improved by using feed-forward and optimizing the trajectory.
Also, if the detectors are triggered in position (the Speedgoat could generate an encoder signal for instance), we don't care about the $D_y$ errors.
#+begin_src matlab :exports none :results none
%% Diffraction tomography with Dy velocity of 1mm/s and Rz velocity of 1RPM
figure;
hold on;
plot(data_dt_1000ums.time, 1e6*data_dt_1000ums.Dz_int, ...
'DisplayName', sprintf('$\\epsilon D_z = %.0f$ nmRMS', 1e9*rms(data_dt_1000ums.Dz_int)))
plot(data_dt_1000ums.time, 1e6*data_dt_1000ums.Ry_int, ...
'DisplayName', sprintf('$\\epsilon R_y = %.2f$ $\\mu$radRMS', 1e6*rms(data_dt_1000ums.Ry_int)))
plot(data_dt_1000ums.time, 1e6*data_dt_1000ums.Dy_int, ...
'DisplayName', sprintf('$\\epsilon D_y = %.0f$ nmRMS', 1e9*rms(data_dt_1000ums.Dy_int - data_dt_1000ums.m_hexa_dy)))
plot(data_dt_1000ums.time, 1e6*data_dt_1000ums.m_hexa_dy, 'k--', 'HandleVisibility', 'off')
hold off;
xlim([0, 1])
xlabel('Time [s]');
ylabel('Measurement [nm,nrad]')
legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
ylim([-110, 110])
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/id31_diffraction_tomo_1mms.pdf', 'width', 'full', 'height', 'normal');
#+end_src
#+name: fig:id31_diffraction_tomo_1mms
#+caption: Diffraction tomography with Dy velocity of 1mm/s and Rz velocity of 1RPM
#+RESULTS:
[[file:figs/id31_diffraction_tomo_1mms.png]]
#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*)
data2orgtable([1e9*rms(data_dt_100ums.Dy_int - data_dt_100ums.m_hexa_dy), 1e9*rms(data_dt_500ums.Dy_int - data_dt_500ums.m_hexa_dy), 1e9*rms(data_dt_1000ums.Dy_int - data_dt_1000ums.m_hexa_dy), 1e9*rms(data_dt_10000ums.Dy_int - data_dt_10000ums.m_hexa_dy);
1e9*rms(data_dt_100ums.Dz_int), 1e9*rms(data_dt_500ums.Dz_int), 1e9*rms(data_dt_1000ums.Dz_int), 1e9*rms(data_dt_10000ums.Dz_int);
1e6*rms(data_dt_100ums.Ry_int), 1e6*rms(data_dt_500ums.Ry_int), 1e6*rms(data_dt_1000ums.Ry_int), 1e6*rms(data_dt_10000ums.Ry_int)]', {'0.1 mm/s' ,'0.5 mm/s', '1 mm/s', '10 mm/s'}, {'Velocity', '$D_y$ [nmRMS]', '$D_z$ [nmRMS]', '$R_y$ [$\mu\text{radRMS}$]'}, ' %.1f ');
#+end_src
#+name: tab:id31_diffraction_tomo_results
#+caption: Obtained errors for several $D_y$ velocities
#+attr_latex: :environment tabularx :width \linewidth :align lXX
#+attr_latex: :center t :booktabs t
#+RESULTS:
| Velocity | $D_y$ [nmRMS] | $D_z$ [nmRMS] | $R_y$ [$\mu\text{radRMS}$] |
|----------+---------------+---------------+----------------------------|
| 0.1 mm/s | 75.5 | 9.1 | 0.1 |
| 0.5 mm/s | 190.5 | 10.0 | 0.1 |
| 1 mm/s | 428.0 | 11.2 | 0.2 |
| 10 mm/s | 4639.9 | 55.9 | 1.4 |
** Summary of experiments
For each conducted experiments, the $D_y$, $D_z$ and $R_y$ errors are computed and summarized in Table ref:tab:id31_experiments_results_summary.
#+begin_src matlab
%% Summary of results
data_results = [...
1e9*data_tomo_1rpm_m0.Dy_rms_cl, 1e9*data_tomo_1rpm_m0.Dz_rms_cl, 1e9*data_tomo_1rpm_m0.Ry_rms_cl ; ... % Tomo 1rpm
1e9*data_tomo_6rpm_m0.Dy_rms_cl, 1e9*data_tomo_6rpm_m0.Dz_rms_cl, 1e9*data_tomo_6rpm_m0.Ry_rms_cl ; ... % Tomo 6rpm
1e9*data_tomo_30rpm_m0.Dy_rms_cl, 1e9*data_tomo_30rpm_m0.Dz_rms_cl, 1e9*data_tomo_30rpm_m0.Ry_rms_cl ; ... % Tomo 30rpm
1e9*rms(detrend(data_dz_10ums.e_dy, 0)), 1e9*rms(detrend(data_dz_10ums.e_dz, 0)), 1e9*rms(detrend(data_dz_10ums.e_ry, 0)) ; ... % Dz 10um/s
1e9*rms(detrend(data_dz_100ums.e_dy,0)), 1e9*rms(detrend(data_dz_100ums.e_dz,0)), 1e9*rms(detrend(data_dz_100ums.e_ry,0)) ; ... % Dz 100um/s
1e9*rms(detrend(data_ry.e_dy,0)), 1e9*rms(detrend(data_ry.e_dz,0)), 1e9*rms(detrend(data_ry.e_ry,0)) ; ... % Ry 100urad/s
1e9*rms(detrend(data_ty_cl_slow.e_dy, 0)), 1e9*rms(detrend(data_ty_cl_slow.e_dz, 0)), 1e9*rms(detrend(data_ty_cl_slow.e_rz, 0)) ; ... % Dy 10 um/s
1e9*rms(detrend(data_dt_100ums.Dy_int-data_dt_100ums.m_hexa_dy, 0)), 1e9*rms(detrend(data_dt_100ums.Dz_int, 0)), 1e9*rms(detrend(data_dt_100ums.Ry_int, 0)); ... % Diffraction tomo 0.1mm/s
1e9*rms(detrend(data_dt_1000ums.Dy_int-data_dt_1000ums.m_hexa_dy,0)), 1e9*rms(detrend(data_dt_1000ums.Dz_int,0)), 1e9*rms(detrend(data_dt_1000ums.Ry_int,0)) ... % Diffraction tomo 1mm/s
];
#+end_src
#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*)
data2orgtable(data_results, {'Tomography ($R_z$ 1rpm)', 'Tomography ($R_z$ 6rpm)', 'Tomography ($R_z$ 30rpm)', 'Dirty Layer ($D_z$ $10\,\mu m/s$)', 'Dirty Layer ($D_z$ $100\,\mu m/s$)', 'Reflectivity ($R_y$ $100\,\mu\text{rad}/s$)', 'Lateral Scan ($D_y$ $10\,\mu m/s$)', 'Diffraction Tomography ($R_z$ 1rpm, $D_y$ 0.1mm/s)', 'Diffraction Tomography ($R_z$ 1rpm, $D_y$ 1mm/s)'}, {'$D_y$ [nmRMS]', '$D_z$ [nmRMS]', '$R_y$ [nradRMS]'}, ' %.0f ');
#+end_src
#+name: tab:id31_experiments_results_summary
#+caption: Table caption
#+attr_latex: :environment tabularx :width \linewidth :align Xccc
#+attr_latex: :center t :booktabs t
#+RESULTS:
| | $D_y$ [nmRMS] | $D_z$ [nmRMS] | $R_y$ [nradRMS] |
|----------------------------------------------------+---------------+---------------+-----------------|
| Tomography ($R_z$ 1rpm) | 15 | 5 | 55 |
| Tomography ($R_z$ 6rpm) | 19 | 5 | 73 |
| Tomography ($R_z$ 30rpm) | 38 | 10 | 129 |
| Dirty Layer ($D_z$ $10\,\mu m/s$) | 25 | 5 | 114 |
| Dirty Layer ($D_z$ $100\,\mu m/s$) | 34 | 15 | 130 |
| Reflectivity ($R_y$ $100\,\mu\text{rad}/s$) | 28 | 6 | 118 |
| Lateral Scan ($D_y$ $10\,\mu m/s$) | 21 | 10 | 37 |
| Diffraction Tomography ($R_z$ 1rpm, $D_y$ 0.1mm/s) | 75 | 9 | 118 |
| Diffraction Tomography ($R_z$ 1rpm, $D_y$ 1mm/s) | 428 | 11 | 169 |
* Bibliography :ignore:
#+latex: \printbibliography[heading=bibintoc,title={Bibliography}]
* Helping Functions :noexport:
** Initialize Path
#+NAME: m-init-path
#+BEGIN_SRC matlab
%% Path for functions, data and scripts
addpath('./matlab/STEPS/'); % Path for STEPS files
addpath('./matlab/subsystems/'); % Path for Simulink subsystems
addpath('./matlab/mat/'); % Path for data
addpath('./matlab/src/'); % Path for functions
addpath('./matlab/'); % Path for scripts
#+END_SRC
#+NAME: m-init-path-tangle
#+BEGIN_SRC matlab
%% Path for functions, data and scripts
addpath('./STEPS/'); % Path for STEPS files
addpath('./subsystems/'); % Path for Simulink subsystems
addpath('./mat/'); % Path for data
addpath('./src/'); % Path for functions
#+END_SRC
** Initialize other elements
#+NAME: m-init-other
#+BEGIN_SRC matlab
%% Colors for the figures
colors = colororder;
freqs = logspace(0, 3, 1000);
Ts = 1e-4;
data_dir = "/home/thomas/mnt/data_id31/id31nass/id31/20230801/RAW_DATA"
mat_dir = "/home/thomas/mnt/data_id31/nass"
#+END_SRC
** =h5scan= - Easily load h5 files
:PROPERTIES:
:header-args:matlab+: :tangle matlab/src/h5scan.m
:header-args:matlab+: :comments none :mkdirp yes :eval no
:END:
<<sec:h5scan>>
#+begin_src matlab
function [cntrs,tp] = h5scan(pth,smp,ds,sn,varargin)
i = cellfun(@(x) isa(x,'detector'),varargin);
if any(i), det = varargin{i}; varargin = varargin(~i); else, det = []; end;
if ~isstr(ds), ds = sprintf('%.4d',ds); end;
f = sprintf('%s/%s/%s_%s/%s_%s.h5',pth,smp,smp,ds,smp,ds);
h = h5info(f,sprintf('/%d.1/measurement',sn));
fid = H5F.open(f);
for i = 1:length(h.Links),
nm = h.Links(i).Name;
try,
id = H5D.open(fid,h.Links(i).Value{1});
cntrs.(nm) = H5D.read(id);
H5D.close(id);
if ~isempty(det) & strcmp(nm,det.name), cntrs.(nm) = integrate(det,double(cntrs.(nm))); end;
catch,
warning('solving problem with %s\n',nm);
cntrs.(nm) = vrtlds(sprintf('%s/%s/%s_%s/scan%.4d/',pth,smp,smp,ds,sn),nm,det);
end;
[~,tp.(nm)] = fileparts(h.Links(i).Value{1});
end;
try,
h = h5info(f,sprintf('/%d.2/measurement',sn));
catch,
h = [];
end;
if ~isempty(h),
for i = 1:length(h.Links),
nm = h.Links(i).Name;
try,
id = H5D.open(fid,h.Links(i).Value{1});
cntrs.part2.(nm) = H5D.read(id);
H5D.close(id);
catch,
warning('solving problem with %s\n',nm);
cntrs.part2.(nm) = vrtlds(sprintf('%s/%s/%s_%s/scan%.4d/',pth,smp,smp,ds,sn),nm,det);
end;
[~,tp.part2.(nm)] = fileparts(h.Links(i).Value{1});
end;
end;
if length(varargin),
fn = sprintf('/%d.1/instrument/positioners/',sn);
h = h5info(f,fn);
[~,k,m] = intersect({h.Datasets.Name},varargin,'stable');
h.Datasets = h.Datasets(k);
for i = 1:length(h.Datasets),
id = H5D.open(fid,[fn h.Datasets(i).Name]);
cntrs.(h.Datasets(i).Name) = H5D.read(id);
H5D.close(id);
end;
end;
H5F.close(fid);
%%%%%%%%%%%%%%%%%%%%%%%%%%
function A = vrtlds(f,nm,det)
%try,
n = 0; A = [];
fn = sprintf('%s/%s_%.4d.h5',f,nm,n);
while exist(fn) == 2,
fid = H5F.open(fn); n = n+1;
id = H5D.open(fid,sprintf('/entry_0000/ESRF-ID31/%s/data',nm));
if 2 < nargin & strcmp(nm,'p3') & ~isempty(det),
fprintf('integrating %s\n',fn);
if isempty(A),
A = integrate(det,double(H5D.read(id)),1);
else,
tmp = integrate(det,double(H5D.read(id)),1); A.y = cat(2,A.y,tmp.y); A.y0 = cat(2,A.y0,tmp.y0);
end;
else,
fprintf('loading %s\n',fn);
A = cat(3,A,H5D.read(id));
end;
H5D.close(id); H5F.close(fid);
fn = sprintf('%s/%s_%.4d.h5',f,nm,n);
end;
%catch,
% A = [];
%end;
% fid = H5F.open...
% id = H5D.open...
% sid = H5D.get_space(id);
% [ndims,h5_dims]=H5S.get_simple_extent_dims(sid)
% Read a 2x3 hyperslab of data from a dataset, starting in the 4th row and 5th column of the example dataset.
% Create a property list identifier, then open the HDF5 file and the dataset /g1/g1.1/dset1.1.1.
% fid = H5F.open('example.h5');
% id = H5D.open(fid,'/g1/g1.1/dset1.1.1');
% dims = ([500 1679 1475];
% msid = H5S.create_simple(3,dims,[]);
% sid = H5D.get_space(id);
% offset = [n*500 0 0];
% block = dims; % d1: 500 or min(d1tot-n*500,500)
% H5S.select_hyperslab(sid,'H5S_SELECT_SET',offset,[],[],block);
% data = H5D.read(id,'H5ML_DEFAULT',msid,sid,'H5P_DEFAULT');
% H5D.close(id);
% H5F.close(fid);
#+end_src
** =circlefit=
:PROPERTIES:
:header-args:matlab+: :tangle matlab/src/circlefit.m
:header-args:matlab+: :comments none :mkdirp yes :eval no
:END:
<<sec:h5scan>>
#+begin_src matlab
function [xc,yc,R,a] = circlefit(x,y)
%
% [xc yx R] = circfit(x,y)
%
% fits a circle in x,y plane in a more accurate
% (less prone to ill condition )
% procedure than circfit2 but using more memory
% x,y are column vector where (x(i),y(i)) is a measured point
%
% result is center point (yc,xc) and radius R
% an optional output is the vector of coeficient a
% describing the circle's equation
%
% x^2+y^2+a(1)*x+a(2)*y+a(3)=0
%
% By: Izhak bucher 25/oct /1991,
x=x(:); y=y(:);
a=[x y ones(size(x))]\[-(x.^2+y.^2)];
xc = -.5*a(1);
yc = -.5*a(2);
R = sqrt((a(1)^2+a(2)^2)/4-a(3));
#+end_src
** =yztomography3dmovie=
:PROPERTIES:
:header-args:matlab+: :tangle matlab/src/yztomography3dmovie.m
:header-args:matlab+: :comments none :mkdirp yes :eval no
:END:
<<sec:yztomography3dmovie>>
#+begin_src matlab
function [] = yztomography3dmovie(filename, data, args)
#+end_src
#+begin_src matlab
arguments
filename
data
args.di (1,1) double {mustBeNumeric} = 500
args.xlim (2,1) double {mustBeNumeric} = [-3, 3]
args.ylim (2,1) double {mustBeNumeric} = [-3, 3]
args.zlim (2,1) double {mustBeNumeric} = [-0.4, 0.4]
args.view_az (1,1) double {mustBeNumeric} = -70
args.view_el (1,1) double {mustBeNumeric} = 5
end
#+end_src
#+begin_src matlab
colors = colororder;
#+end_src
#+begin_src matlab
writerObj = VideoWriter(filename); % initialize the VideoWriter object
open(writerObj);
#+end_src
#+begin_src matlab
fig = figure;
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile;
#+end_src
#+begin_src matlab
di = args.di;
for i = 1:floor(length(data.Dx_int)/di)-1
if data.hac_status(di*(i+1)-1) == 0
% Only open loop
plot3(ax1, 1e6*data.Dx_int(di*i:di*(i+1)-1), 1e6*data.Dy_int(di*i:di*(i+1)-1), 1e6*data.Dz_int(di*i:di*(i+1)-1), '-', 'color', colors(1,:))
elseif data.hac_status(di*i) == 1
% Only closed loop
plot3(ax1, 1e6*data.Dx_int(di*i:di*(i+1)-1), 1e6*data.Dy_int(di*i:di*(i+1)-1), 1e6*data.Dz_int(di*i:di*(i+1)-1), '-', 'color', colors(2,:))
else
% Both open and closed loop
Dx_int = data.Dx_int(di*i:di*(i+1)-1);
Dy_int = data.Dy_int(di*i:di*(i+1)-1);
Dz_int = data.Dz_int(di*i:di*(i+1)-1);
plot3(ax1, 1e6*Dx_int(data.hac_status(di*i:di*(i+1)-1) == 0), 1e6*Dy_int(data.hac_status(di*i:di*(i+1)-1) == 0), 1e6*Dz_int(data.hac_status(di*i:di*(i+1)-1) == 0), '-', 'color', colors(1,:))
plot3(ax1, 1e6*Dx_int(data.hac_status(di*i:di*(i+1)-1) == 1), 1e6*Dy_int(data.hac_status(di*i:di*(i+1)-1) == 1), 1e6*Dz_int(data.hac_status(di*i:di*(i+1)-1) == 1), '-', 'color', colors(2,:))
end
axis(ax1, 'equal')
xlim(ax1, args.xlim)
ylim(ax1, args.ylim)
zlim(ax1, args.zlim)
view(ax1, args.view_az, args.view_el)
xlabel(ax1, "X motion [$\mu$m]");
ylabel(ax1, "Y motion [$\mu$m]");
zlabel(ax1, "Z motion [$\mu$m]");
drawnow()
writeVideo(writerObj,getframe(fig)) % add the frame to the movie
end
close(writerObj);
#+end_src
** =yztomographymovie=
:PROPERTIES:
:header-args:matlab+: :tangle matlab/src/yztomographymovie.m
:header-args:matlab+: :comments none :mkdirp yes :eval no
:END:
<<sec:h5scan>>
#+begin_src matlab
function [] = yztomographymovie(filename, data, args)
#+end_src
#+begin_src matlab
arguments
filename
data
args.di (1,1) double {mustBeNumeric} = 500
args.xlim_ax1 (2,1) double {mustBeNumeric} = [-3, 3]
args.ylim_ax1 (2,1) double {mustBeNumeric} = [-3, 3]
args.xlim_ax2 (2,1) double {mustBeNumeric} = [-3, 3]
args.ylim_ax2 (2,1) double {mustBeNumeric} = [-3, 3]
end
#+end_src
#+begin_src matlab
colors = colororder;
#+end_src
#+begin_src matlab
writerObj = VideoWriter(filename); % initialize the VideoWriter object
open(writerObj);
#+end_src
#+begin_src matlab
fig = figure;
tiledlayout(1, 2, 'TileSpacing', 'compact', 'Padding', 'None');
ax1 = nexttile;
ax2 = nexttile;
#+end_src
#+begin_src matlab
di = args.di;
for i = 1:floor(length(data.Dx_int)/di)-1
if data.hac_status(di*(i+1)-1) == 0
% Only open loop
plot(ax1, 1e6*data.Dy_int(di*i:di*(i+1)-1), 1e6*data.Dz_int(di*i:di*(i+1)-1), '-', 'color', colors(1,:))
plot(ax2, 1e9*data.Dy_int(di*i:di*(i+1)-1), 1e9*data.Dz_int(di*i:di*(i+1)-1), '-', 'color', colors(1,:))
elseif data.hac_status(di*i) == 1
% Only closed loop
plot(ax1, 1e6*data.Dy_int(di*i:di*(i+1)-1), 1e6*data.Dz_int(di*i:di*(i+1)-1), '-', 'color', colors(2,:))
plot(ax2, 1e9*data.Dy_int(di*i:di*(i+1)-1), 1e9*data.Dz_int(di*i:di*(i+1)-1), '-', 'color', colors(2,:))
else
% Both open and closed loop
Dy_int = data.Dy_int(di*i:di*(i+1)-1);
Dz_int = data.Dz_int(di*i:di*(i+1)-1);
plot(ax1, 1e6*Dy_int(data.hac_status(di*i:di*(i+1)-1) == 0), 1e6*Dz_int(data.hac_status(di*i:di*(i+1)-1) == 0), '-', 'color', colors(1,:))
plot(ax1, 1e6*Dy_int(data.hac_status(di*i:di*(i+1)-1) == 1), 1e6*Dz_int(data.hac_status(di*i:di*(i+1)-1) == 1), '-', 'color', colors(2,:))
plot(ax2, 1e9*Dy_int(data.hac_status(di*i:di*(i+1)-1) == 0), 1e9*Dz_int(data.hac_status(di*i:di*(i+1)-1) == 0), '-', 'color', colors(1,:))
plot(ax2, 1e9*Dy_int(data.hac_status(di*i:di*(i+1)-1) == 1), 1e9*Dz_int(data.hac_status(di*i:di*(i+1)-1) == 1), '-', 'color', colors(2,:))
end
axis(ax1, 'square')
axis(ax2, 'square')
xlim(ax1, args.xlim_ax1)
ylim(ax1, args.ylim_ax1)
xlim(ax2, args.xlim_ax2)
ylim(ax2, args.ylim_ax2)
xlabel(ax1, "Y motion [$\mu m$]");
ylabel(ax1, "Z motion [$\mu m$]");
xlabel(ax2, "Y motion [$nm$]");
ylabel(ax2, "Z motion [$nm$]");
F = getframe(fig); %// Capture the frame
writeVideo(writerObj,F) %// add the frame to the movie
end
close(writerObj);
#+end_src
** Simscape Configuration
:PROPERTIES:
:header-args:matlab+: :tangle ./matlab/src/initializeSimscapeConfiguration.m
:header-args:matlab+: :comments none :mkdirp yes :eval no
:END:
<<sec:initializeSimscapeConfiguration>>
*** Function description
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
function [] = initializeSimscapeConfiguration(args)
#+end_src
*** Optional Parameters
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
args.gravity logical {mustBeNumericOrLogical} = true
end
#+end_src
*** Structure initialization
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
conf_simscape = struct();
#+end_src
*** Add Type
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
if args.gravity
conf_simscape.type = 1;
else
conf_simscape.type = 2;
end
#+end_src
*** Save the Structure
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
save('./mat/conf_simscape.mat', 'conf_simscape');
#+end_src
** Logging Configuration
:PROPERTIES:
:header-args:matlab+: :tangle ./matlab/src/initializeLoggingConfiguration.m
:header-args:matlab+: :comments none :mkdirp yes :eval no
:END:
<<sec:initializeLoggingConfiguration>>
*** Function description
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
function [] = initializeLoggingConfiguration(args)
#+end_src
*** Optional Parameters
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
args.log char {mustBeMember(args.log,{'none', 'all', 'forces'})} = 'none'
args.Ts (1,1) double {mustBeNumeric, mustBePositive} = 1e-3
end
#+end_src
*** Structure initialization
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
conf_log = struct();
#+end_src
*** Add Type
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
switch args.log
case 'none'
conf_log.type = 0;
case 'all'
conf_log.type = 1;
case 'forces'
conf_log.type = 2;
end
#+end_src
*** Sampling Time
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
conf_log.Ts = args.Ts;
#+end_src
*** Save the Structure
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
save('./mat/conf_log.mat', 'conf_log');
#+end_src
** Ground
:PROPERTIES:
:header-args:matlab+: :tangle ./matlab/src/initializeGround.m
:header-args:matlab+: :comments none :mkdirp yes :eval no
:END:
<<sec:initializeGround>>
*** Simscape Model
:PROPERTIES:
:UNNUMBERED: t
:END:
The model of the Ground is composed of:
- A *Cartesian* joint that is used to simulation the ground motion
- A solid that represents the ground on which the granite is located
#+name: fig:simscape_model_ground
#+attr_org: :width 800
#+caption: Simscape model for the Ground
[[file:figs/images/simscape_model_ground.png]]
#+name: fig:simscape_picture_ground
#+attr_org: :width 800
#+caption: Simscape picture for the Ground
[[file:figs/images/simscape_picture_ground.png]]
*** Function description
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
function [ground] = initializeGround(args)
#+end_src
*** Optional Parameters
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
args.type char {mustBeMember(args.type,{'none', 'rigid'})} = 'rigid'
args.rot_point (3,1) double {mustBeNumeric} = zeros(3,1) % Rotation point for the ground motion [m]
end
#+end_src
*** Structure initialization
:PROPERTIES:
:UNNUMBERED: t
:END:
First, we initialize the =granite= structure.
#+begin_src matlab
ground = struct();
#+end_src
*** Add Type
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
switch args.type
case 'none'
ground.type = 0;
case 'rigid'
ground.type = 1;
end
#+end_src
*** Ground Solid properties
:PROPERTIES:
:UNNUMBERED: t
:END:
We set the shape and density of the ground solid element.
#+begin_src matlab
ground.shape = [2, 2, 0.5]; % [m]
ground.density = 2800; % [kg/m3]
#+end_src
*** Rotation Point
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
ground.rot_point = args.rot_point;
#+end_src
*** Save the Structure
:PROPERTIES:
:UNNUMBERED: t
:END:
The =ground= structure is saved.
#+begin_src matlab
save('./mat/stages.mat', 'ground', '-append');
#+end_src
** Granite
:PROPERTIES:
:header-args:matlab+: :tangle ./matlab/src/initializeGranite.m
:header-args:matlab+: :comments none :mkdirp yes :eval no
:END:
<<sec:initializeGranite>>
*** Simscape Model
:PROPERTIES:
:UNNUMBERED: t
:END:
The Simscape model of the granite is composed of:
- A cartesian joint such that the granite can vibrations along x, y and z axis
- A solid
The output =sample_pos= corresponds to the impact point of the X-ray.
#+name: fig:simscape_model_granite
#+attr_org: :width 800
#+caption: Simscape model for the Granite
[[file:figs/images/simscape_model_granite.png]]
#+name: fig:simscape_picture_granite
#+attr_org: :width 800
#+caption: Simscape picture for the Granite
[[file:figs/images/simscape_picture_granite.png]]
*** Function description
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
function [granite] = initializeGranite(args)
#+end_src
*** Optional Parameters
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
args.type char {mustBeMember(args.type,{'rigid', 'flexible', 'none', 'modal-analysis', 'init'})} = 'flexible'
args.Foffset logical {mustBeNumericOrLogical} = false
args.density (1,1) double {mustBeNumeric, mustBeNonnegative} = 2800 % Density [kg/m3]
args.K (3,1) double {mustBeNumeric, mustBeNonnegative} = [4e9; 3e8; 8e8] % [N/m]
args.C (3,1) double {mustBeNumeric, mustBeNonnegative} = [4.0e5; 1.1e5; 9.0e5] % [N/(m/s)]
args.x0 (1,1) double {mustBeNumeric} = 0 % Rest position of the Joint in the X direction [m]
args.y0 (1,1) double {mustBeNumeric} = 0 % Rest position of the Joint in the Y direction [m]
args.z0 (1,1) double {mustBeNumeric} = 0 % Rest position of the Joint in the Z direction [m]
args.sample_pos (1,1) double {mustBeNumeric} = 0.8-25e-3 % Height of the measurment point [m]
end
#+end_src
*** Structure initialization
:PROPERTIES:
:UNNUMBERED: t
:END:
First, we initialize the =granite= structure.
#+begin_src matlab
granite = struct();
#+end_src
*** Add Granite Type
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
switch args.type
case 'none'
granite.type = 0;
case 'rigid'
granite.type = 1;
case 'flexible'
granite.type = 2;
case 'modal-analysis'
granite.type = 3;
case 'init'
granite.type = 4;
end
#+end_src
*** Material and Geometry
:PROPERTIES:
:UNNUMBERED: t
:END:
Properties of the Material and link to the geometry of the granite.
#+begin_src matlab
granite.density = args.density; % [kg/m3]
granite.STEP = './STEPS/granite/granite.STEP';
#+end_src
Z-offset for the initial position of the sample with respect to the granite top surface.
#+begin_src matlab
granite.sample_pos = args.sample_pos; % [m]
#+end_src
*** Stiffness and Damping properties
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
granite.K = args.K; % [N/m]
granite.C = args.C; % [N/(m/s)]
#+end_src
*** Equilibrium position of the each joint.
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
if args.Foffset && ~strcmp(args.type, 'none') && ~strcmp(args.type, 'rigid') && ~strcmp(args.type, 'init')
load('mat/Foffset.mat', 'Fgm');
granite.Deq = -Fgm'./granite.K;
else
granite.Deq = zeros(6,1);
end
#+end_src
*** Save the Structure
:PROPERTIES:
:UNNUMBERED: t
:END:
The =granite= structure is saved.
#+begin_src matlab
save('./mat/stages.mat', 'granite', '-append');
#+end_src
** Translation Stage
:PROPERTIES:
:header-args:matlab+: :tangle ./matlab/src/initializeTy.m
:header-args:matlab+: :comments none :mkdirp yes :eval no
:END:
<<sec:initializeTy>>
*** Simscape Model
:PROPERTIES:
:UNNUMBERED: t
:END:
The Simscape model of the Translation stage consist of:
- One rigid body for the fixed part of the translation stage
- One rigid body for the moving part of the translation stage
- Four 6-DOF Joints that only have some rigidity in the X and Z directions.
The rigidity in rotation comes from the fact that we use multiple joints that are located at different points
- One 6-DOF joint that represent the Actuator.
It is used to impose the motion in the Y direction
- One 6-DOF joint to inject force disturbance in the X and Z directions
#+name: fig:simscape_model_ty
#+ATTR_ORG: :width 800
#+caption: Simscape model for the Translation Stage
[[file:figs/images/simscape_model_ty.png]]
#+name: fig:simscape_picture_ty
#+attr_org: :width 800
#+caption: Simscape picture for the Translation Stage
[[file:figs/images/simscape_picture_ty.png]]
*** Function description
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
function [ty] = initializeTy(args)
#+end_src
*** Optional Parameters
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
args.type char {mustBeMember(args.type,{'none', 'rigid', 'flexible', 'modal-analysis', 'init'})} = 'flexible'
args.Foffset logical {mustBeNumericOrLogical} = false
end
#+end_src
*** Structure initialization
:PROPERTIES:
:UNNUMBERED: t
:END:
First, we initialize the =ty= structure.
#+begin_src matlab
ty = struct();
#+end_src
*** Add Translation Stage Type
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
switch args.type
case 'none'
ty.type = 0;
case 'rigid'
ty.type = 1;
case 'flexible'
ty.type = 2;
case 'modal-analysis'
ty.type = 3;
case 'init'
ty.type = 4;
end
#+end_src
*** Material and Geometry
:PROPERTIES:
:UNNUMBERED: t
:END:
Define the density of the materials as well as the geometry (STEP files).
#+begin_src matlab
% Ty Granite frame
ty.granite_frame.density = 7800; % [kg/m3] => 43kg
ty.granite_frame.STEP = './STEPS/Ty/Ty_Granite_Frame.STEP';
% Guide Translation Ty
ty.guide.density = 7800; % [kg/m3] => 76kg
ty.guide.STEP = './STEPS/ty/Ty_Guide.STEP';
% Ty - Guide_Translation12
ty.guide12.density = 7800; % [kg/m3]
ty.guide12.STEP = './STEPS/Ty/Ty_Guide_12.STEP';
% Ty - Guide_Translation11
ty.guide11.density = 7800; % [kg/m3]
ty.guide11.STEP = './STEPS/ty/Ty_Guide_11.STEP';
% Ty - Guide_Translation22
ty.guide22.density = 7800; % [kg/m3]
ty.guide22.STEP = './STEPS/ty/Ty_Guide_22.STEP';
% Ty - Guide_Translation21
ty.guide21.density = 7800; % [kg/m3]
ty.guide21.STEP = './STEPS/Ty/Ty_Guide_21.STEP';
% Ty - Plateau translation
ty.frame.density = 7800; % [kg/m3]
ty.frame.STEP = './STEPS/ty/Ty_Stage.STEP';
% Ty Stator Part
ty.stator.density = 5400; % [kg/m3]
ty.stator.STEP = './STEPS/ty/Ty_Motor_Stator.STEP';
% Ty Rotor Part
ty.rotor.density = 5400; % [kg/m3]
ty.rotor.STEP = './STEPS/ty/Ty_Motor_Rotor.STEP';
#+end_src
*** Stiffness and Damping properties
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
ty.K = [2e8; 1e8; 2e8; 6e7; 9e7; 6e7]; % [N/m, N*m/rad]
ty.C = [8e4; 5e4; 8e4; 2e4; 3e4; 2e4]; % [N/(m/s), N*m/(rad/s)]
#+end_src
*** Equilibrium position of the each joint.
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
if args.Foffset && ~strcmp(args.type, 'none') && ~strcmp(args.type, 'rigid') && ~strcmp(args.type, 'init')
load('mat/Foffset.mat', 'Ftym');
ty.Deq = -Ftym'./ty.K;
else
ty.Deq = zeros(6,1);
end
#+end_src
*** Save the Structure
:PROPERTIES:
:UNNUMBERED: t
:END:
The =ty= structure is saved.
#+begin_src matlab
save('./mat/stages.mat', 'ty', '-append');
#+end_src
** Tilt Stage
:PROPERTIES:
:header-args:matlab+: :tangle ./matlab/src/initializeRy.m
:header-args:matlab+: :comments none :mkdirp yes :eval no
:END:
<<sec:initializeRy>>
*** Simscape Model
:PROPERTIES:
:UNNUMBERED: t
:END:
The Simscape model of the Tilt stage is composed of:
- Two solid bodies for the two part of the stage
- *Four* 6-DOF joints to model the flexibility of the stage.
These joints are virtually located along the rotation axis and are connecting the two solid bodies.
These joints have some translation stiffness in the u-v-w directions aligned with the joint.
The stiffness in rotation between the two solids is due to the fact that the 4 joints are connecting the two solids are different locations
- A Bushing Joint used for the Actuator.
The Ry motion is imposed by the input.
#+name: fig:simscape_model_ry
#+attr_org: :width 800
#+caption: Simscape model for the Tilt Stage
[[file:figs/images/simscape_model_ry.png]]
#+name: fig:simscape_picture_ry
#+attr_org: :width 800
#+caption: Simscape picture for the Tilt Stage
[[file:figs/images/simscape_picture_ry.png]]
*** Function description
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
function [ry] = initializeRy(args)
#+end_src
*** Optional Parameters
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
args.type char {mustBeMember(args.type,{'none', 'rigid', 'flexible', 'modal-analysis', 'init'})} = 'flexible'
args.Foffset logical {mustBeNumericOrLogical} = false
args.Ry_init (1,1) double {mustBeNumeric} = 0
end
#+end_src
*** Structure initialization
:PROPERTIES:
:UNNUMBERED: t
:END:
First, we initialize the =ry= structure.
#+begin_src matlab
ry = struct();
#+end_src
*** Add Tilt Type
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
switch args.type
case 'none'
ry.type = 0;
case 'rigid'
ry.type = 1;
case 'flexible'
ry.type = 2;
case 'modal-analysis'
ry.type = 3;
case 'init'
ry.type = 4;
end
#+end_src
*** Material and Geometry
:PROPERTIES:
:UNNUMBERED: t
:END:
Properties of the Material and link to the geometry of the Tilt stage.
#+begin_src matlab
% Ry - Guide for the tilt stage
ry.guide.density = 7800; % [kg/m3]
ry.guide.STEP = './STEPS/ry/Tilt_Guide.STEP';
% Ry - Rotor of the motor
ry.rotor.density = 2400; % [kg/m3]
ry.rotor.STEP = './STEPS/ry/Tilt_Motor_Axis.STEP';
% Ry - Motor
ry.motor.density = 3200; % [kg/m3]
ry.motor.STEP = './STEPS/ry/Tilt_Motor.STEP';
% Ry - Plateau Tilt
ry.stage.density = 7800; % [kg/m3]
ry.stage.STEP = './STEPS/ry/Tilt_Stage.STEP';
#+end_src
Z-Offset so that the center of rotation matches the sample center;
#+begin_src matlab
ry.z_offset = 0.58178; % [m]
#+end_src
#+begin_src matlab
ry.Ry_init = args.Ry_init; % [rad]
#+end_src
*** Stiffness and Damping properties
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
ry.K = [3.8e8; 4e8; 3.8e8; 1.2e8; 6e4; 1.2e8];
ry.C = [1e5; 1e5; 1e5; 3e4; 1e3; 3e4];
#+end_src
*** Equilibrium position of the each joint.
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
if args.Foffset && ~strcmp(args.type, 'none') && ~strcmp(args.type, 'rigid') && ~strcmp(args.type, 'init')
load('mat/Foffset.mat', 'Fym');
ry.Deq = -Fym'./ry.K;
else
ry.Deq = zeros(6,1);
end
#+end_src
*** Save the Structure
:PROPERTIES:
:UNNUMBERED: t
:END:
The =ry= structure is saved.
#+begin_src matlab
save('./mat/stages.mat', 'ry', '-append');
#+end_src
** Spindle
:PROPERTIES:
:header-args:matlab+: :tangle ./matlab/src/initializeRz.m
:header-args:matlab+: :comments none :mkdirp yes :eval no
:END:
<<sec:initializeRz>>
*** Simscape Model
:PROPERTIES:
:UNNUMBERED: t
:END:
The Simscape model of the Spindle is composed of:
- Two rigid bodies: the stator and the rotor
- A Bushing Joint that is used both as the actuator (the Rz motion is imposed by the input) and as the force perturbation in the Z direction.
- The Bushing joint has some flexibility in the X-Y-Z directions as well as in Rx and Ry rotations
#+name: fig:simscape_model_rz
#+attr_org: :width 800
#+caption: Simscape model for the Spindle
[[file:figs/images/simscape_model_rz.png]]
#+name: fig:simscape_picture_rz
#+attr_org: :width 800
#+caption: Simscape picture for the Spindle
[[file:figs/images/simscape_picture_rz.png]]
*** Function description
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
function [rz] = initializeRz(args)
#+end_src
*** Optional Parameters
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
args.type char {mustBeMember(args.type,{'none', 'rigid', 'flexible', 'modal-analysis', 'init'})} = 'flexible'
args.Foffset logical {mustBeNumericOrLogical} = false
end
#+end_src
*** Structure initialization
:PROPERTIES:
:UNNUMBERED: t
:END:
First, we initialize the =rz= structure.
#+begin_src matlab
rz = struct();
#+end_src
*** Add Spindle Type
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
switch args.type
case 'none'
rz.type = 0;
case 'rigid'
rz.type = 1;
case 'flexible'
rz.type = 2;
case 'modal-analysis'
rz.type = 3;
case 'init'
rz.type = 4;
end
#+end_src
*** Material and Geometry
:PROPERTIES:
:UNNUMBERED: t
:END:
Properties of the Material and link to the geometry of the spindle.
#+begin_src matlab
% Spindle - Slip Ring
rz.slipring.density = 7800; % [kg/m3]
rz.slipring.STEP = './STEPS/rz/Spindle_Slip_Ring.STEP';
% Spindle - Rotor
rz.rotor.density = 7800; % [kg/m3]
rz.rotor.STEP = './STEPS/rz/Spindle_Rotor.STEP';
% Spindle - Stator
rz.stator.density = 7800; % [kg/m3]
rz.stator.STEP = './STEPS/rz/Spindle_Stator.STEP';
#+end_src
*** Stiffness and Damping properties
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
rz.K = [7e8; 7e8; 2e9; 1e7; 1e7; 1e7];
rz.C = [4e4; 4e4; 7e4; 1e4; 1e4; 1e4];
#+end_src
*** Equilibrium position of the each joint.
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
if args.Foffset && ~strcmp(args.type, 'none') && ~strcmp(args.type, 'rigid') && ~strcmp(args.type, 'init')
load('mat/Foffset.mat', 'Fzm');
rz.Deq = -Fzm'./rz.K;
else
rz.Deq = zeros(6,1);
end
#+end_src
*** Save the Structure
:PROPERTIES:
:UNNUMBERED: t
:END:
The =rz= structure is saved.
#+begin_src matlab
save('./mat/stages.mat', 'rz', '-append');
#+end_src
** Micro Hexapod
:PROPERTIES:
:header-args:matlab+: :tangle ./matlab/src/initializeMicroHexapod.m
:header-args:matlab+: :comments none :mkdirp yes :eval no
:END:
<<sec:initializeMicroHexapod>>
*** Function description
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
function [micro_hexapod] = initializeMicroHexapod(args)
#+end_src
*** Optional Parameters
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
args.type char {mustBeMember(args.type,{'none', 'rigid', 'flexible', 'modal-analysis', 'init', 'compliance'})} = 'flexible'
% initializeFramesPositions
args.H (1,1) double {mustBeNumeric, mustBePositive} = 350e-3
args.MO_B (1,1) double {mustBeNumeric} = 270e-3
% generateGeneralConfiguration
args.FH (1,1) double {mustBeNumeric, mustBePositive} = 50e-3
args.FR (1,1) double {mustBeNumeric, mustBePositive} = 175.5e-3
args.FTh (6,1) double {mustBeNumeric} = [-10, 10, 120-10, 120+10, 240-10, 240+10]*(pi/180)
args.MH (1,1) double {mustBeNumeric, mustBePositive} = 45e-3
args.MR (1,1) double {mustBeNumeric, mustBePositive} = 118e-3
args.MTh (6,1) double {mustBeNumeric} = [-60+10, 60-10, 60+10, 180-10, 180+10, -60-10]*(pi/180)
% initializeStrutDynamics
args.Ki (6,1) double {mustBeNumeric, mustBeNonnegative} = 2e7*ones(6,1)
args.Ci (6,1) double {mustBeNumeric, mustBeNonnegative} = 1.4e3*ones(6,1)
% initializeCylindricalPlatforms
args.Fpm (1,1) double {mustBeNumeric, mustBePositive} = 10
args.Fph (1,1) double {mustBeNumeric, mustBePositive} = 26e-3
args.Fpr (1,1) double {mustBeNumeric, mustBePositive} = 207.5e-3
args.Mpm (1,1) double {mustBeNumeric, mustBePositive} = 10
args.Mph (1,1) double {mustBeNumeric, mustBePositive} = 26e-3
args.Mpr (1,1) double {mustBeNumeric, mustBePositive} = 150e-3
% initializeCylindricalStruts
args.Fsm (1,1) double {mustBeNumeric, mustBePositive} = 1
args.Fsh (1,1) double {mustBeNumeric, mustBePositive} = 100e-3
args.Fsr (1,1) double {mustBeNumeric, mustBePositive} = 25e-3
args.Msm (1,1) double {mustBeNumeric, mustBePositive} = 1
args.Msh (1,1) double {mustBeNumeric, mustBePositive} = 100e-3
args.Msr (1,1) double {mustBeNumeric, mustBePositive} = 25e-3
% inverseKinematics
args.AP (3,1) double {mustBeNumeric} = zeros(3,1)
args.ARB (3,3) double {mustBeNumeric} = eye(3)
% Force that stiffness of each joint should apply at t=0
args.Foffset logical {mustBeNumericOrLogical} = false
end
#+end_src
*** Function content
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
stewart = initializeStewartPlatform();
stewart = initializeFramesPositions(stewart, ...
'H', args.H, ...
'MO_B', args.MO_B);
stewart = generateGeneralConfiguration(stewart, ...
'FH', args.FH, ...
'FR', args.FR, ...
'FTh', args.FTh, ...
'MH', args.MH, ...
'MR', args.MR, ...
'MTh', args.MTh);
stewart = computeJointsPose(stewart);
#+end_src
#+begin_src matlab
stewart = initializeStrutDynamics(stewart, ...
'K', args.Ki, ...
'C', args.Ci);
stewart = initializeJointDynamics(stewart, ...
'type_F', 'universal_p', ...
'type_M', 'spherical_p');
#+end_src
#+begin_src matlab
stewart = initializeCylindricalPlatforms(stewart, ...
'Fpm', args.Fpm, ...
'Fph', args.Fph, ...
'Fpr', args.Fpr, ...
'Mpm', args.Mpm, ...
'Mph', args.Mph, ...
'Mpr', args.Mpr);
stewart = initializeCylindricalStruts(stewart, ...
'Fsm', args.Fsm, ...
'Fsh', args.Fsh, ...
'Fsr', args.Fsr, ...
'Msm', args.Msm, ...
'Msh', args.Msh, ...
'Msr', args.Msr);
stewart = computeJacobian(stewart);
stewart = initializeStewartPose(stewart, ...
'AP', args.AP, ...
'ARB', args.ARB);
#+end_src
#+begin_src matlab
stewart = initializeInertialSensor(stewart, 'type', 'none');
#+end_src
Equilibrium position of the each joint.
#+begin_src matlab
if args.Foffset && ~strcmp(args.type, 'none') && ~strcmp(args.type, 'rigid') && ~strcmp(args.type, 'init')
load('mat/Foffset.mat', 'Fhm');
stewart.actuators.dLeq = -Fhm'./args.Ki;
else
stewart.actuators.dLeq = zeros(6,1);
end
#+end_src
*** Add Type
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
switch args.type
case 'none'
stewart.type = 0;
case 'rigid'
stewart.type = 1;
case 'flexible'
stewart.type = 2;
case 'modal-analysis'
stewart.type = 3;
case 'init'
stewart.type = 4;
case 'compliance'
stewart.type = 5;
end
#+end_src
*** Save the Structure
:PROPERTIES:
:UNNUMBERED: t
:END:
The =micro_hexapod= structure is saved.
#+begin_src matlab
micro_hexapod = stewart;
save('./mat/stages.mat', 'micro_hexapod', '-append');
#+end_src
** Nano Hexapod
:PROPERTIES:
:header-args:matlab+: :tangle ./matlab/src/initializeNanoHexapod.m
:header-args:matlab+: :comments none :mkdirp yes :eval no
:END:
<<sec:initializeNanoHexapod>>
*** Function description
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
function [nano_hexapod] = initializeNanoHexapod(args)
#+end_src
*** Optional Parameters
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
%% Bottom Flexible Joints
args.flex_bot_type char {mustBeMember(args.flex_bot_type,{'2dof', '3dof', '4dof', 'flexible'})} = '4dof'
args.flex_bot_kRx (6,1) double {mustBeNumeric} = ones(6,1)*5 % X bending stiffness [Nm/rad]
args.flex_bot_kRy (6,1) double {mustBeNumeric} = ones(6,1)*5 % Y bending stiffness [Nm/rad]
args.flex_bot_kRz (6,1) double {mustBeNumeric} = ones(6,1)*260 % Torsionnal stiffness [Nm/rad]
args.flex_bot_kz (6,1) double {mustBeNumeric} = ones(6,1)*7e7 % Axial Stiffness [N/m]
args.flex_bot_cRx (6,1) double {mustBeNumeric} = ones(6,1)*0.001 % X bending Damping [Nm/(rad/s)]
args.flex_bot_cRy (6,1) double {mustBeNumeric} = ones(6,1)*0.001 % Y bending Damping [Nm/(rad/s)]
args.flex_bot_cRz (6,1) double {mustBeNumeric} = ones(6,1)*0.001 % Torsionnal Damping [Nm/(rad/s)]
args.flex_bot_cz (6,1) double {mustBeNumeric} = ones(6,1)*0.001 % Axial Damping [N/(m/s)]
%% Top Flexible Joints
args.flex_top_type char {mustBeMember(args.flex_top_type,{'2dof', '3dof', '4dof', 'flexible'})} = '4dof'
args.flex_top_kRx (6,1) double {mustBeNumeric} = ones(6,1)*5 % X bending stiffness [Nm/rad]
args.flex_top_kRy (6,1) double {mustBeNumeric} = ones(6,1)*5 % Y bending stiffness [Nm/rad]
args.flex_top_kRz (6,1) double {mustBeNumeric} = ones(6,1)*260 % Torsionnal stiffness [Nm/rad]
args.flex_top_kz (6,1) double {mustBeNumeric} = ones(6,1)*7e7 % Axial Stiffness [N/m]
args.flex_top_cRx (6,1) double {mustBeNumeric} = ones(6,1)*0.001 % X bending Damping [Nm/(rad/s)]
args.flex_top_cRy (6,1) double {mustBeNumeric} = ones(6,1)*0.001 % Y bending Damping [Nm/(rad/s)]
args.flex_top_cRz (6,1) double {mustBeNumeric} = ones(6,1)*0.001 % Torsionnal Damping [Nm/(rad/s)]
args.flex_top_cz (6,1) double {mustBeNumeric} = ones(6,1)*0.001 % Axial Damping [N/(m/s)]
%% Jacobian - Location of frame {A} and {B}
args.MO_B (1,1) double {mustBeNumeric} = 150e-3 % Height of {B} w.r.t. {M} [m]
%% Relative Motion Sensor
args.motion_sensor_type char {mustBeMember(args.motion_sensor_type,{'struts', 'plates'})} = 'struts'
%% Top Plate
args.top_plate_type char {mustBeMember(args.top_plate_type,{'rigid', 'flexible'})} = 'rigid'
args.top_plate_xi (1,1) double {mustBeNumeric} = 0.01 % Damping Ratio
%% Actuators
args.actuator_type char {mustBeMember(args.actuator_type,{'2dof', 'flexible frame', 'flexible'})} = 'flexible'
args.actuator_Ga (6,1) double {mustBeNumeric} = zeros(6,1) % Actuator gain [N/V]
args.actuator_Gs (6,1) double {mustBeNumeric} = zeros(6,1) % Sensor gain [V/m]
% For 2DoF
args.actuator_k (6,1) double {mustBeNumeric} = ones(6,1)*0.38e6 % [N/m]
args.actuator_ke (6,1) double {mustBeNumeric} = ones(6,1)*1.75e6 % [N/m]
args.actuator_ka (6,1) double {mustBeNumeric} = ones(6,1)*3e7 % [N/m]
args.actuator_c (6,1) double {mustBeNumeric} = ones(6,1)*3e1 % [N/(m/s)]
args.actuator_ce (6,1) double {mustBeNumeric} = ones(6,1)*1e1 % [N/(m/s)]
args.actuator_ca (6,1) double {mustBeNumeric} = ones(6,1)*1e1 % [N/(m/s)]
args.actuator_Leq (6,1) double {mustBeNumeric} = ones(6,1)*0.056 % [m]
% For Flexible Frame
args.actuator_ks (6,1) double {mustBeNumeric} = ones(6,1)*235e6 % Stiffness of one stack [N/m]
args.actuator_cs (6,1) double {mustBeNumeric} = ones(6,1)*1e1 % Stiffness of one stack [N/m]
% Misalignment
args.actuator_d_align (6,3) double {mustBeNumeric} = zeros(6,3) % [m]
args.actuator_xi (1,1) double {mustBeNumeric} = 0.01 % Damping Ratio
end
#+end_src
*** Nano Hexapod Object
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
nano_hexapod = struct();
#+end_src
*** Flexible Joints - Bot
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
nano_hexapod.flex_bot = struct();
switch args.flex_bot_type
case '2dof'
nano_hexapod.flex_bot.type = 1;
case '3dof'
nano_hexapod.flex_bot.type = 2;
case '4dof'
nano_hexapod.flex_bot.type = 3;
case 'flexible'
nano_hexapod.flex_bot.type = 4;
end
nano_hexapod.flex_bot.kRx = args.flex_bot_kRx; % X bending stiffness [Nm/rad]
nano_hexapod.flex_bot.kRy = args.flex_bot_kRy; % Y bending stiffness [Nm/rad]
nano_hexapod.flex_bot.kRz = args.flex_bot_kRz; % Torsionnal stiffness [Nm/rad]
nano_hexapod.flex_bot.kz = args.flex_bot_kz; % Axial stiffness [N/m]
nano_hexapod.flex_bot.cRx = args.flex_bot_cRx; % [Nm/(rad/s)]
nano_hexapod.flex_bot.cRy = args.flex_bot_cRy; % [Nm/(rad/s)]
nano_hexapod.flex_bot.cRz = args.flex_bot_cRz; % [Nm/(rad/s)]
nano_hexapod.flex_bot.cz = args.flex_bot_cz; %[N/(m/s)]
#+end_src
*** Flexible Joints - Top
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
nano_hexapod.flex_top = struct();
switch args.flex_top_type
case '2dof'
nano_hexapod.flex_top.type = 1;
case '3dof'
nano_hexapod.flex_top.type = 2;
case '4dof'
nano_hexapod.flex_top.type = 3;
case 'flexible'
nano_hexapod.flex_top.type = 4;
end
nano_hexapod.flex_top.kRx = args.flex_top_kRx; % X bending stiffness [Nm/rad]
nano_hexapod.flex_top.kRy = args.flex_top_kRy; % Y bending stiffness [Nm/rad]
nano_hexapod.flex_top.kRz = args.flex_top_kRz; % Torsionnal stiffness [Nm/rad]
nano_hexapod.flex_top.kz = args.flex_top_kz; % Axial stiffness [N/m]
nano_hexapod.flex_top.cRx = args.flex_top_cRx; % [Nm/(rad/s)]
nano_hexapod.flex_top.cRy = args.flex_top_cRy; % [Nm/(rad/s)]
nano_hexapod.flex_top.cRz = args.flex_top_cRz; % [Nm/(rad/s)]
nano_hexapod.flex_top.cz = args.flex_top_cz; %[N/(m/s)]
#+end_src
*** Relative Motion Sensor
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
nano_hexapod.motion_sensor = struct();
switch args.motion_sensor_type
case 'struts'
nano_hexapod.motion_sensor.type = 1;
case 'plates'
nano_hexapod.motion_sensor.type = 2;
end
#+end_src
*** Amplified Piezoelectric Actuator
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
nano_hexapod.actuator = struct();
switch args.actuator_type
case '2dof'
nano_hexapod.actuator.type = 1;
case 'flexible frame'
nano_hexapod.actuator.type = 2;
case 'flexible'
nano_hexapod.actuator.type = 3;
end
#+end_src
#+begin_src matlab
%% Actuator gain [N/V]
if all(args.actuator_Ga == 0)
switch args.actuator_type
case '2dof'
nano_hexapod.actuator.Ga = ones(6,1)*(-30.0);
case 'flexible frame'
nano_hexapod.actuator.Ga = ones(6,1); % TODO
case 'flexible'
nano_hexapod.actuator.Ga = ones(6,1)*23.4;
end
else
nano_hexapod.actuator.Ga = args.actuator_Ga; % Actuator gain [N/V]
end
%% Sensor gain [V/m]
if all(args.actuator_Gs == 0)
switch args.actuator_type
case '2dof'
nano_hexapod.actuator.Gs = ones(6,1)*0.098;
case 'flexible frame'
nano_hexapod.actuator.Gs = ones(6,1); % TODO
case 'flexible'
nano_hexapod.actuator.Gs = ones(6,1)*(-4674824);
end
else
nano_hexapod.actuator.Gs = args.actuator_Gs; % Sensor gain [V/m]
end
#+end_src
Mechanical characteristics:
#+begin_src matlab
switch args.actuator_type
case '2dof'
nano_hexapod.actuator.k = args.actuator_k; % [N/m]
nano_hexapod.actuator.ke = args.actuator_ke; % [N/m]
nano_hexapod.actuator.ka = args.actuator_ka; % [N/m]
nano_hexapod.actuator.c = args.actuator_c; % [N/(m/s)]
nano_hexapod.actuator.ce = args.actuator_ce; % [N/(m/s)]
nano_hexapod.actuator.ca = args.actuator_ca; % [N/(m/s)]
nano_hexapod.actuator.Leq = args.actuator_Leq; % [m]
case 'flexible frame'
nano_hexapod.actuator.K = readmatrix('APA300ML_b_mat_K.CSV'); % Stiffness Matrix
nano_hexapod.actuator.M = readmatrix('APA300ML_b_mat_M.CSV'); % Mass Matrix
nano_hexapod.actuator.P = extractNodes('APA300ML_b_out_nodes_3D.txt'); % Node coordinates [m]
nano_hexapod.actuator.ks = args.actuator_ks; % Stiffness of one stack [N/m]
nano_hexapod.actuator.cs = args.actuator_cs; % Damping of one stack [N/m]
nano_hexapod.actuator.xi = args.actuator_xi; % Damping ratio
case 'flexible'
nano_hexapod.actuator.K = readmatrix('full_APA300ML_K.CSV'); % Stiffness Matrix
nano_hexapod.actuator.M = readmatrix('full_APA300ML_M.CSV'); % Mass Matrix
nano_hexapod.actuator.P = extractNodes('full_APA300ML_out_nodes_3D.txt'); % Node coordiantes [m]
nano_hexapod.actuator.d_align = args.actuator_d_align; % Misalignment
nano_hexapod.actuator.xi = args.actuator_xi; % Damping ratio
end
#+end_src
*** Geometry
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
nano_hexapod.geometry = struct();
#+end_src
Center of joints $a_i$ with respect to {F}:
#+begin_src matlab
Fa = [[-21.74, 111.91, 22.49],
[-107.79, -37.13, 22.49],
[-86.05, -74.78, 22.49],
[ 86.05, -74.78, 22.49],
[ 107.79, -37.13, 22.49],
[ 21.74, 111.91, 22.49]]'*1e-3; % Ai w.r.t. {F} [m]
#+end_src
Center of joints $b_i$ with respect to {M}:
#+begin_src matlab
Mb = [[-77.78, 77.78, -22.50],
[-106.25, 28.47, -22.50],
[-28.47, -106.25, -22.50],
[ 28.47, -106.25, -22.50],
[ 106.25, 28.47, -22.50],
[ 77.78, 77.78, -22.50]]'*1e-3; % Bi w.r.t. {M} [m]
#+end_src
Now compute the positions $b_i$ with respect to {F}:
#+begin_src matlab
Fb = Mb + [0; 0; 95e-3]; % Bi w.r.t. {F} [m]
#+end_src
The unit vector representing the orientation of the struts can then be computed:
#+begin_src matlab
si = Fb - Fa;
si = si./vecnorm(si); % Normalize
#+end_src
Location of encoder measurement points when fixed on the plates:
#+begin_src matlab
Fc = [[-76.914, 78.31, 52.605]
[-106.276, 27.454, 52.605]
[-29.362, -105.765, 52.605]
[ 29.362, -105.765, 52.605]
[ 106.276, 27.454, 52.605]
[ 76.914, 78.31, 52.605]]'*1e-3; % Meas pos w.r.t. {F}
Mc = Fc - [0; 0; 95e-3]; % Meas pos w.r.t. {M}
#+end_src
#+begin_src matlab
nano_hexapod.geometry.Fa = Fa;
nano_hexapod.geometry.Fb = Fb;
nano_hexapod.geometry.Fc = Fc;
nano_hexapod.geometry.Mb = Mb;
nano_hexapod.geometry.Mc = Mc;
nano_hexapod.geometry.si = si;
nano_hexapod.geometry.MO_B = args.MO_B;
#+end_src
*** Jacobian for Actuators
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
Bb = Mb - [0; 0; args.MO_B];
nano_hexapod.geometry.J = [nano_hexapod.geometry.si', cross(Bb, nano_hexapod.geometry.si)'];
#+end_src
*** Jacobian for Sensors
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
switch args.motion_sensor_type
case 'struts'
nano_hexapod.geometry.Js = nano_hexapod.geometry.J;
case 'plates'
Bc = Mc - [0; 0; args.MO_B];
nano_hexapod.geometry.Js = [nano_hexapod.geometry.si', cross(Bc, nano_hexapod.geometry.si)'];
end
#+end_src
*** Top Plate
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
nano_hexapod.top_plate = struct();
switch args.top_plate_type
case 'rigid'
nano_hexapod.top_plate.type = 1;
case 'flexible'
nano_hexapod.top_plate.type = 2;
nano_hexapod.top_plate.R_flex = ...
{[-0.51771438594671 0.5201563363052 -0.67927108019211
-0.31530446231304 -0.8540710660135 -0.41369760724945
-0.79533320729697 0 0.60617249143351 ],
[-0.01420448131632 0.9997254079576 0.01863709726679
-0.60600604129104 -0.0234330681729 0.79511481512719
0.79533320729697 0 0.60617249143351 ],
[ 0.53191886726305 0.4795690716524 0.69790817745892
-0.29070157897799 0.8775041341865 -0.38141720787774
-0.79533320729697 0 0.60617249143351 ],
[ 0.53191886726305 -0.4795690716524 -0.69790817745892
0.29070157897799 0.8775041341865 -0.38141720787774
0.79533320729697 0 0.60617249143351 ],
[-0.01420448131633 -0.9997254079576 -0.01863709726680
0.60600604129104 -0.0234330681729 0.79511481512719
-0.79533320729697 0 0.60617249143351 ],
[-0.51771438594672 -0.5201563363051 0.67927108019212
0.31530446231304 -0.8540710660135 -0.41369760724945
0.79533320729697 0 0.60617249143351 ]};
nano_hexapod.top_plate.R_enc = ...
{ [ 0.854071066013574 -0.520156336305191 0
0.520156336305191 0.854071066013574 0
0 0 1 ],
[-0.023433068172958 0.999725407957606 0
-0.999725407957606 -0.023433068172958 0
0 0 1 ],
[-0.877504134186525 -0.479569071652412 0
0.479569071652412 -0.877504134186525 0
0 0 1 ],
[ 0.877504134186525 -0.479569071652413 0
0.479569071652413 0.877504134186525 0
0 0 1 ],
[ 0.023433068172945 0.999725407957606 0
-0.999725407957606 0.023433068172945 0
0 0 1 ],
[-0.854071066013566 -0.520156336305202 0
0.520156336305202 -0.854071066013566 0
0 0 1 ]};
nano_hexapod.top_plate.K = readmatrix('top_plate_K_6.CSV'); % Stiffness Matrix
nano_hexapod.top_plate.M = readmatrix('top_plate_M_6.CSV'); % Mass Matrix
nano_hexapod.top_plate.P = extractNodes('top_plate_out_nodes_3D_qua.txt'); % Node coordiantes [m]
nano_hexapod.top_plate.xi = args.top_plate_xi; % Damping ratio
end
#+end_src
*** Save the Structure
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
if nargout == 0
save('./mat/stages.mat', 'nano_hexapod', '-append');
end
#+end_src
** Sample
:PROPERTIES:
:header-args:matlab+: :tangle ./matlab/src/initializeSample.m
:header-args:matlab+: :comments none :mkdirp yes :eval no
:END:
<<sec:initializeSample>>
*** Function description
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
function [sample] = initializeSample(args)
#+end_src
*** Optional Parameters
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
args.type char {mustBeMember(args.type,{'0', '1', '2', '3'})} = '0'
end
#+end_src
*** Structure initialization
:PROPERTIES:
:UNNUMBERED: t
:END:
First, we initialize the =sample= structure.
#+begin_src matlab
sample = struct();
#+end_src
*** Add Sample Type
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
switch args.type
case '0'
sample.type = 0;
case '1'
sample.type = 1;
case '2'
sample.type = 2;
case '3'
sample.type = 3;
end
#+end_src
*** Save the Structure
:PROPERTIES:
:UNNUMBERED: t
:END:
The =sample= structure is saved.
#+begin_src matlab
save('./mat/stages.mat', 'sample', '-append');
#+end_src
** Initialize Controller
:PROPERTIES:
:header-args:matlab+: :tangle ./matlab/src/initializeController.m
:header-args:matlab+: :comments none :mkdirp yes :eval no
:END:
<<sec:initializeController>>
*** Function Declaration and Documentation
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
function [] = initializeController(args)
#+end_src
*** Optional Parameters
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
args.type char {mustBeMember(args.type,{'open-loop', 'iff', 'dvf', 'hac-dvf', 'ref-track-L', 'ref-track-iff-L', 'cascade-hac-lac', 'hac-iff', 'stabilizing'})} = 'open-loop'
end
#+end_src
*** Structure initialization
:PROPERTIES:
:UNNUMBERED: t
:END:
First, we initialize the =controller= structure.
#+begin_src matlab
controller = struct();
#+end_src
*** Controller Type
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
switch args.type
case 'open-loop'
controller.type = 1;
controller.name = 'Open-Loop';
case 'dvf'
controller.type = 2;
controller.name = 'Decentralized Direct Velocity Feedback';
case 'iff'
controller.type = 3;
controller.name = 'Decentralized Integral Force Feedback';
case 'hac-dvf'
controller.type = 4;
controller.name = 'HAC-DVF';
case 'ref-track-L'
controller.type = 5;
controller.name = 'Reference Tracking in the frame of the legs';
case 'ref-track-iff-L'
controller.type = 6;
controller.name = 'Reference Tracking in the frame of the legs + IFF';
case 'cascade-hac-lac'
controller.type = 7;
controller.name = 'Cascade Control + HAC-LAC';
case 'hac-iff'
controller.type = 8;
controller.name = 'HAC-IFF';
case 'stabilizing'
controller.type = 9;
controller.name = 'Stabilizing Controller';
end
#+end_src
*** Save the Structure
:PROPERTIES:
:UNNUMBERED: t
:END:
The =controller= structure is saved.
#+begin_src matlab
save('./mat/controller.mat', 'controller');
#+end_src
** Generate Reference Signals
:PROPERTIES:
:header-args:matlab+: :tangle ./matlab/src/initializeReferences.m
:header-args:matlab+: :comments none :mkdirp yes :eval no
:END:
<<sec:initializeReferences>>
*** Function Declaration and Documentation
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
function [ref] = initializeReferences(args)
#+end_src
*** Optional Parameters
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
% Sampling Frequency [s]
args.Ts (1,1) double {mustBeNumeric, mustBePositive} = 1e-3
% Maximum simulation time [s]
args.Tmax (1,1) double {mustBeNumeric, mustBePositive} = 100
% Either "constant" / "triangular" / "sinusoidal"
args.Dy_type char {mustBeMember(args.Dy_type,{'constant', 'triangular', 'sinusoidal'})} = 'constant'
% Amplitude of the displacement [m]
args.Dy_amplitude (1,1) double {mustBeNumeric} = 0
% Period of the displacement [s]
args.Dy_period (1,1) double {mustBeNumeric, mustBePositive} = 1
% Either "constant" / "triangular" / "sinusoidal"
args.Ry_type char {mustBeMember(args.Ry_type,{'constant', 'triangular', 'sinusoidal'})} = 'constant'
% Amplitude [rad]
args.Ry_amplitude (1,1) double {mustBeNumeric} = 0
% Period of the displacement [s]
args.Ry_period (1,1) double {mustBeNumeric, mustBePositive} = 1
% Either "constant" / "rotating"
args.Rz_type char {mustBeMember(args.Rz_type,{'constant', 'rotating', 'rotating-not-filtered'})} = 'constant'
% Initial angle [rad]
args.Rz_amplitude (1,1) double {mustBeNumeric} = 0
% Period of the rotating [s]
args.Rz_period (1,1) double {mustBeNumeric, mustBePositive} = 1
% For now, only constant is implemented
args.Dh_type char {mustBeMember(args.Dh_type,{'constant'})} = 'constant'
% Initial position [m,m,m,rad,rad,rad] of the top platform (Pitch-Roll-Yaw Euler angles)
args.Dh_pos (6,1) double {mustBeNumeric} = zeros(6, 1), ...
% For now, only constant is implemented
args.Rm_type char {mustBeMember(args.Rm_type,{'constant'})} = 'constant'
% Initial position of the two masses
args.Rm_pos (2,1) double {mustBeNumeric} = [0; pi]
% For now, only constant is implemented
args.Dn_type char {mustBeMember(args.Dn_type,{'constant'})} = 'constant'
% Initial position [m,m,m,rad,rad,rad] of the top platform
args.Dn_pos (6,1) double {mustBeNumeric} = zeros(6,1)
end
#+end_src
*** Initialize Parameters
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
%% Set Sampling Time
Ts = args.Ts;
Tmax = args.Tmax;
%% Low Pass Filter to filter out the references
s = zpk('s');
w0 = 2*pi*10;
xi = 1;
H_lpf = 1/(1 + 2*xi/w0*s + s^2/w0^2);
#+end_src
*** Translation Stage
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
%% Translation stage - Dy
t = 0:Ts:Tmax; % Time Vector [s]
Dy = zeros(length(t), 1);
Dyd = zeros(length(t), 1);
Dydd = zeros(length(t), 1);
switch args.Dy_type
case 'constant'
Dy(:) = args.Dy_amplitude;
Dyd(:) = 0;
Dydd(:) = 0;
case 'triangular'
% This is done to unsure that we start with no displacement
Dy_raw = args.Dy_amplitude*sawtooth(2*pi*t/args.Dy_period,1/2);
i0 = find(t>=args.Dy_period/4,1);
Dy(1:end-i0+1) = Dy_raw(i0:end);
Dy(end-i0+2:end) = Dy_raw(end); % we fix the last value
% The signal is filtered out
Dy = lsim(H_lpf, Dy, t);
Dyd = lsim(H_lpf*s, Dy, t);
Dydd = lsim(H_lpf*s^2, Dy, t);
case 'sinusoidal'
Dy(:) = args.Dy_amplitude*sin(2*pi/args.Dy_period*t);
Dyd = args.Dy_amplitude*2*pi/args.Dy_period*cos(2*pi/args.Dy_period*t);
Dydd = -args.Dy_amplitude*(2*pi/args.Dy_period)^2*sin(2*pi/args.Dy_period*t);
otherwise
warning('Dy_type is not set correctly');
end
Dy = struct('time', t, 'signals', struct('values', Dy), 'deriv', Dyd, 'dderiv', Dydd);
#+end_src
*** Tilt Stage
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
%% Tilt Stage - Ry
t = 0:Ts:Tmax; % Time Vector [s]
Ry = zeros(length(t), 1);
Ryd = zeros(length(t), 1);
Rydd = zeros(length(t), 1);
switch args.Ry_type
case 'constant'
Ry(:) = args.Ry_amplitude;
Ryd(:) = 0;
Rydd(:) = 0;
case 'triangular'
Ry_raw = args.Ry_amplitude*sawtooth(2*pi*t/args.Ry_period,1/2);
i0 = find(t>=args.Ry_period/4,1);
Ry(1:end-i0+1) = Ry_raw(i0:end);
Ry(end-i0+2:end) = Ry_raw(end); % we fix the last value
% The signal is filtered out
Ry = lsim(H_lpf, Ry, t);
Ryd = lsim(H_lpf*s, Ry, t);
Rydd = lsim(H_lpf*s^2, Ry, t);
case 'sinusoidal'
Ry(:) = args.Ry_amplitude*sin(2*pi/args.Ry_period*t);
Ryd = args.Ry_amplitude*2*pi/args.Ry_period*cos(2*pi/args.Ry_period*t);
Rydd = -args.Ry_amplitude*(2*pi/args.Ry_period)^2*sin(2*pi/args.Ry_period*t);
otherwise
warning('Ry_type is not set correctly');
end
Ry = struct('time', t, 'signals', struct('values', Ry), 'deriv', Ryd, 'dderiv', Rydd);
#+end_src
*** Spindle
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
%% Spindle - Rz
t = 0:Ts:Tmax; % Time Vector [s]
Rz = zeros(length(t), 1);
Rzd = zeros(length(t), 1);
Rzdd = zeros(length(t), 1);
switch args.Rz_type
case 'constant'
Rz(:) = args.Rz_amplitude;
Rzd(:) = 0;
Rzdd(:) = 0;
case 'rotating-not-filtered'
Rz(:) = 2*pi/args.Rz_period*t;
% The signal is filtered out
Rz(:) = 2*pi/args.Rz_period*t;
Rzd(:) = 2*pi/args.Rz_period;
Rzdd(:) = 0;
% We add the angle offset
Rz = Rz + args.Rz_amplitude;
case 'rotating'
Rz(:) = 2*pi/args.Rz_period*t;
% The signal is filtered out
Rz = lsim(H_lpf, Rz, t);
Rzd = lsim(H_lpf*s, Rz, t);
Rzdd = lsim(H_lpf*s^2, Rz, t);
% We add the angle offset
Rz = Rz + args.Rz_amplitude;
otherwise
warning('Rz_type is not set correctly');
end
Rz = struct('time', t, 'signals', struct('values', Rz), 'deriv', Rzd, 'dderiv', Rzdd);
#+end_src
*** Micro Hexapod
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
%% Micro-Hexapod
t = [0, Ts];
Dh = zeros(length(t), 6);
Dhl = zeros(length(t), 6);
switch args.Dh_type
case 'constant'
Dh = [args.Dh_pos, args.Dh_pos];
load('mat/stages.mat', 'micro_hexapod');
AP = [args.Dh_pos(1) ; args.Dh_pos(2) ; args.Dh_pos(3)];
tx = args.Dh_pos(4);
ty = args.Dh_pos(5);
tz = args.Dh_pos(6);
ARB = [cos(tz) -sin(tz) 0;
sin(tz) cos(tz) 0;
0 0 1]*...
[ cos(ty) 0 sin(ty);
0 1 0;
-sin(ty) 0 cos(ty)]*...
[1 0 0;
0 cos(tx) -sin(tx);
0 sin(tx) cos(tx)];
[~, Dhl] = inverseKinematics(micro_hexapod, 'AP', AP, 'ARB', ARB);
Dhl = [Dhl, Dhl];
otherwise
warning('Dh_type is not set correctly');
end
Dh = struct('time', t, 'signals', struct('values', Dh));
Dhl = struct('time', t, 'signals', struct('values', Dhl));
#+end_src
*** Save
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
%% Save
save('./mat/nass_references.mat', 'Dy', 'Ry', 'Rz', 'Dh', 'Dhl', 'args', 'Ts');
end
#+end_src
** Initialize Position Errors
:PROPERTIES:
:header-args:matlab+: :tangle ./matlab/src/initializePosError.m
:header-args:matlab+: :comments none :mkdirp yes
:header-args:matlab+: :eval no :results none
:END:
<<sec:initializePosError>>
*** Function Declaration and Documentation
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
function [] = initializePosError(args)
% initializePosError - Initialize the position errors
%
% Syntax: [] = initializePosError(args)
%
% Inputs:
% - args -
#+end_src
*** Optional Parameters
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
args.error logical {mustBeNumericOrLogical} = false
args.Dy (1,1) double {mustBeNumeric} = 0 % [m]
args.Ry (1,1) double {mustBeNumeric} = 0 % [m]
args.Rz (1,1) double {mustBeNumeric} = 0 % [m]
end
#+end_src
*** Structure initialization
:PROPERTIES:
:UNNUMBERED: t
:END:
First, we initialize the =pos_error= structure.
#+begin_src matlab
pos_error = struct();
#+end_src
*** Type
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
if args.error
pos_error.type = 1;
else
pos_error.type = 0;
end
#+end_src
*** Position Errors
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
pos_error.Dy = args.Dy;
pos_error.Ry = args.Ry;
pos_error.Rz = args.Rz;
#+end_src
*** Save
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
save('./mat/pos_error.mat', 'pos_error');
#+end_src
** Initialize Rz Estimate
:PROPERTIES:
:header-args:matlab+: :tangle ./matlab/src/initializeRzEstimate.m
:header-args:matlab+: :comments none :mkdirp yes
:header-args:matlab+: :eval no :results none
:END:
<<sec:initializeRzEstimate>>
*** Function Declaration and Documentation
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
function [] = initializeRzEstimate(args)
% initializeRzEstimate - Initialize the position errors
%
% Syntax: [] = initializeRzEstimate(args)
%
% Inputs:
% - args -
#+end_src
*** Optional Parameters
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
args.type char {mustBeMember(args.type,{'encoders', 'voltages'})} = 'encoders'
end
#+end_src
*** Structure initialization
:PROPERTIES:
:UNNUMBERED: t
:END:
First, we initialize the =rz_estimate= structure.
#+begin_src matlab
rz_estimate = struct();
#+end_src
*** Type
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
switch args.type
case 'encoders'
rz_estimate.type = 0;
case 'voltages'
rz_estimate.type = 1;
end
#+end_src
*** Position Errors
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
load('mat/stages.mat', 'nano_hexapod');
rz_estimate.J_L_to_X = pinv(nano_hexapod.geometry.J);
rz_estimate.pz_sensitivity = -240e-6/8.5; % [m/V]
#+end_src
*** Save
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
save('./mat/stages.mat', 'rz_estimate', '-append');
#+end_src
** Initialize Lion Metrology
:PROPERTIES:
:header-args:matlab+: :tangle ./matlab/src/initializeLion.m
:header-args:matlab+: :comments none :mkdirp yes
:header-args:matlab+: :eval no :results none
:END:
<<sec:initializeLion>>
*** Function Declaration and Documentation
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
function [] = initializeLion(args)
% initializeLion - Initialize the position errors
%
% Syntax: [] = initializeLion(args)
%
% Inputs:
% - args -
#+end_src
*** Optional Parameters
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
args.type char {mustBeMember(args.type,{'rigid'})} = 'rigid'
end
#+end_src
*** Structure initialization
:PROPERTIES:
:UNNUMBERED: t
:END:
First, we initialize the =rz_estimate= structure.
#+begin_src matlab
lion = struct();
#+end_src
*** Jacobian
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
lion.J_int_to_X = [ 0 0 -0.787401574803149 -0.212598425196851 0;
0.78740157480315 0.21259842519685 0 0 0;
0 0 0 0 -1;
-13.1233595800525 13.1233595800525 0 0 0;
0 0 -13.1233595800525 13.1233595800525 0];
#+end_src
*** Save
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
save('./mat/stages.mat', 'lion', '-append');
#+end_src
** Initialize Disturbances
:PROPERTIES:
:header-args:matlab+: :tangle ./matlab/src/initializeDisturbances.m
:header-args:matlab+: :comments none :mkdirp yes
:header-args:matlab+: :eval no :results none
:END:
<<sec:initializeDisturbances>>
*** Function Declaration and Documentation
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
function [] = initializeDisturbances(args)
% initializeDisturbances - Initialize the disturbances
%
% Syntax: [] = initializeDisturbances(args)
%
% Inputs:
% - args -
#+end_src
*** Optional Parameters
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
% Global parameter to enable or disable the disturbances
args.enable logical {mustBeNumericOrLogical} = true
% Ground Motion - X direction
args.Dwx logical {mustBeNumericOrLogical} = true
% Ground Motion - Y direction
args.Dwy logical {mustBeNumericOrLogical} = true
% Ground Motion - Z direction
args.Dwz logical {mustBeNumericOrLogical} = true
% Translation Stage - X direction
args.Fty_x logical {mustBeNumericOrLogical} = true
% Translation Stage - Z direction
args.Fty_z logical {mustBeNumericOrLogical} = true
% Spindle - Z direction
args.Frz_z logical {mustBeNumericOrLogical} = true
end
#+end_src
*** Load Data
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
load('./mat/dist_psd.mat', 'dist_f');
#+end_src
We remove the first frequency point that usually is very large.
#+begin_src matlab :exports none
dist_f.f = dist_f.f(2:end);
dist_f.psd_gm = dist_f.psd_gm(2:end);
dist_f.psd_ty = dist_f.psd_ty(2:end);
dist_f.psd_rz = dist_f.psd_rz(2:end);
#+end_src
*** Parameters
:PROPERTIES:
:UNNUMBERED: t
:END:
We define some parameters that will be used in the algorithm.
#+begin_src matlab
Fs = 2*dist_f.f(end); % Sampling Frequency of data is twice the maximum frequency of the PSD vector [Hz]
N = 2*length(dist_f.f); % Number of Samples match the one of the wanted PSD
T0 = N/Fs; % Signal Duration [s]
df = 1/T0; % Frequency resolution of the DFT [Hz]
% Also equal to (dist_f.f(2)-dist_f.f(1))
t = linspace(0, T0, N+1)'; % Time Vector [s]
Ts = 1/Fs; % Sampling Time [s]
#+end_src
*** Ground Motion
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
phi = dist_f.psd_gm;
C = zeros(N/2,1);
for i = 1:N/2
C(i) = sqrt(phi(i)*df);
end
#+end_src
#+begin_src matlab
if args.Dwx && args.enable
rng(111);
theta = 2*pi*rand(N/2,1); % Generate random phase [rad]
Cx = [0 ; C.*complex(cos(theta),sin(theta))];
Cx = [Cx; flipud(conj(Cx(2:end)))];;
Dwx = N/sqrt(2)*ifft(Cx); % Ground Motion - x direction [m]
else
Dwx = zeros(length(t), 1);
end
#+end_src
#+begin_src matlab
if args.Dwy && args.enable
rng(112);
theta = 2*pi*rand(N/2,1); % Generate random phase [rad]
Cx = [0 ; C.*complex(cos(theta),sin(theta))];
Cx = [Cx; flipud(conj(Cx(2:end)))];;
Dwy = N/sqrt(2)*ifft(Cx); % Ground Motion - y direction [m]
else
Dwy = zeros(length(t), 1);
end
#+end_src
#+begin_src matlab
if args.Dwy && args.enable
rng(113);
theta = 2*pi*rand(N/2,1); % Generate random phase [rad]
Cx = [0 ; C.*complex(cos(theta),sin(theta))];
Cx = [Cx; flipud(conj(Cx(2:end)))];;
Dwz = N/sqrt(2)*ifft(Cx); % Ground Motion - z direction [m]
else
Dwz = zeros(length(t), 1);
end
#+end_src
*** Translation Stage - X direction
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
if args.Fty_x && args.enable
phi = dist_f.psd_ty; % TODO - we take here the vertical direction which is wrong but approximate
C = zeros(N/2,1);
for i = 1:N/2
C(i) = sqrt(phi(i)*df);
end
rng(121);
theta = 2*pi*rand(N/2,1); % Generate random phase [rad]
Cx = [0 ; C.*complex(cos(theta),sin(theta))];
Cx = [Cx; flipud(conj(Cx(2:end)))];;
u = N/sqrt(2)*ifft(Cx); % Disturbance Force Ty x [N]
Fty_x = u;
else
Fty_x = zeros(length(t), 1);
end
#+end_src
*** Translation Stage - Z direction
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
if args.Fty_z && args.enable
phi = dist_f.psd_ty;
C = zeros(N/2,1);
for i = 1:N/2
C(i) = sqrt(phi(i)*df);
end
rng(122);
theta = 2*pi*rand(N/2,1); % Generate random phase [rad]
Cx = [0 ; C.*complex(cos(theta),sin(theta))];
Cx = [Cx; flipud(conj(Cx(2:end)))];;
u = N/sqrt(2)*ifft(Cx); % Disturbance Force Ty z [N]
Fty_z = u;
else
Fty_z = zeros(length(t), 1);
end
#+end_src
*** Spindle - Z direction
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
if args.Frz_z && args.enable
phi = dist_f.psd_rz;
C = zeros(N/2,1);
for i = 1:N/2
C(i) = sqrt(phi(i)*df);
end
rng(131);
theta = 2*pi*rand(N/2,1); % Generate random phase [rad]
Cx = [0 ; C.*complex(cos(theta),sin(theta))];
Cx = [Cx; flipud(conj(Cx(2:end)))];;
u = N/sqrt(2)*ifft(Cx); % Disturbance Force Rz z [N]
Frz_z = u;
else
Frz_z = zeros(length(t), 1);
end
#+end_src
*** Direct Forces
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
u = zeros(length(t), 6);
Fd = u;
#+end_src
*** Set initial value to zero
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
Dwx = Dwx - Dwx(1);
Dwy = Dwy - Dwy(1);
Dwz = Dwz - Dwz(1);
Fty_x = Fty_x - Fty_x(1);
Fty_z = Fty_z - Fty_z(1);
Frz_z = Frz_z - Frz_z(1);
#+end_src
*** Save
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
save('./mat/nass_disturbances.mat', 'Dwx', 'Dwy', 'Dwz', 'Fty_x', 'Fty_z', 'Frz_z', 'Fd', 'Ts', 't', 'args');
#+end_src
** Initialize Stewart Platform
*** =initializeStewartPlatform=: Initialize the Stewart Platform structure
:PROPERTIES:
:header-args:matlab+: :tangle ./matlab/src/initializeStewartPlatform.m
:header-args:matlab+: :comments none :mkdirp yes :eval no
:END:
<<sec:initializeStewartPlatform>>
This Matlab function is accessible [[file:./matlab/src/initializeStewartPlatform.m][here]].
**** Documentation
:PROPERTIES:
:UNNUMBERED: t
:END:
#+name: fig:stewart-frames-position
#+caption: Definition of the position of the frames
[[file:figs/stewart-frames-position.png]]
**** Function description
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
function [stewart] = initializeStewartPlatform()
% initializeStewartPlatform - Initialize the stewart structure
%
% Syntax: [stewart] = initializeStewartPlatform(args)
%
% Outputs:
% - stewart - A structure with the following sub-structures:
% - platform_F -
% - platform_M -
% - joints_F -
% - joints_M -
% - struts_F -
% - struts_M -
% - actuators -
% - geometry -
% - properties -
#+end_src
**** Initialize the Stewart structure
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
stewart = struct();
stewart.platform_F = struct();
stewart.platform_M = struct();
stewart.joints_F = struct();
stewart.joints_M = struct();
stewart.struts_F = struct();
stewart.struts_M = struct();
stewart.actuators = struct();
stewart.sensors = struct();
stewart.sensors.inertial = struct();
stewart.sensors.force = struct();
stewart.sensors.relative = struct();
stewart.geometry = struct();
stewart.kinematics = struct();
#+end_src
*** =initializeFramesPositions=: Initialize the positions of frames {A}, {B}, {F} and {M}
:PROPERTIES:
:header-args:matlab+: :tangle ./matlab/src/initializeFramesPositions.m
:header-args:matlab+: :comments none :mkdirp yes :eval no
:END:
<<sec:initializeFramesPositions>>
This Matlab function is accessible [[file:./matlab/src/initializeFramesPositions.m][here]].
**** Documentation
:PROPERTIES:
:UNNUMBERED: t
:END:
#+name: fig:stewart-frames-position
#+caption: Definition of the position of the frames
[[file:figs/stewart-frames-position.png]]
**** Function description
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
function [stewart] = initializeFramesPositions(stewart, args)
% initializeFramesPositions - Initialize the positions of frames {A}, {B}, {F} and {M}
%
% Syntax: [stewart] = initializeFramesPositions(stewart, args)
%
% Inputs:
% - args - Can have the following fields:
% - H [1x1] - Total Height of the Stewart Platform (height from {F} to {M}) [m]
% - MO_B [1x1] - Height of the frame {B} with respect to {M} [m]
%
% Outputs:
% - stewart - A structure with the following fields:
% - geometry.H [1x1] - Total Height of the Stewart Platform [m]
% - geometry.FO_M [3x1] - Position of {M} with respect to {F} [m]
% - platform_M.MO_B [3x1] - Position of {B} with respect to {M} [m]
% - platform_F.FO_A [3x1] - Position of {A} with respect to {F} [m]
#+end_src
**** Optional Parameters
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
stewart
args.H (1,1) double {mustBeNumeric, mustBePositive} = 90e-3
args.MO_B (1,1) double {mustBeNumeric} = 50e-3
end
#+end_src
**** Compute the position of each frame
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
H = args.H; % Total Height of the Stewart Platform [m]
FO_M = [0; 0; H]; % Position of {M} with respect to {F} [m]
MO_B = [0; 0; args.MO_B]; % Position of {B} with respect to {M} [m]
FO_A = MO_B + FO_M; % Position of {A} with respect to {F} [m]
#+end_src
**** Populate the =stewart= structure
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
stewart.geometry.H = H;
stewart.geometry.FO_M = FO_M;
stewart.platform_M.MO_B = MO_B;
stewart.platform_F.FO_A = FO_A;
#+end_src
*** =generateGeneralConfiguration=: Generate a Very General Configuration
:PROPERTIES:
:header-args:matlab+: :tangle ./matlab/src/generateGeneralConfiguration.m
:header-args:matlab+: :comments none :mkdirp yes :eval no
:END:
<<sec:generateGeneralConfiguration>>
This Matlab function is accessible [[file:./matlab/src/generateGeneralConfiguration.m][here]].
**** Documentation
:PROPERTIES:
:UNNUMBERED: t
:END:
Joints are positions on a circle centered with the Z axis of {F} and {M} and at a chosen distance from {F} and {M}.
The radius of the circles can be chosen as well as the angles where the joints are located (see Figure [[fig:joint_position_general]]).
#+begin_src latex :file stewart_bottom_plate.pdf :tangle no
\begin{tikzpicture}
% Internal and external limit
\draw[fill=white!80!black] (0, 0) circle [radius=3];
% Circle where the joints are located
\draw[dashed] (0, 0) circle [radius=2.5];
% Bullets for the positions of the joints
\node[] (J1) at ( 80:2.5){$\bullet$};
\node[] (J2) at (100:2.5){$\bullet$};
\node[] (J3) at (200:2.5){$\bullet$};
\node[] (J4) at (220:2.5){$\bullet$};
\node[] (J5) at (320:2.5){$\bullet$};
\node[] (J6) at (340:2.5){$\bullet$};
% Name of the points
\node[above right] at (J1) {$a_{1}$};
\node[above left] at (J2) {$a_{2}$};
\node[above left] at (J3) {$a_{3}$};
\node[right ] at (J4) {$a_{4}$};
\node[left ] at (J5) {$a_{5}$};
\node[above right] at (J6) {$a_{6}$};
% First 2 angles
\draw[dashed, ->] (0:1) arc [start angle=0, end angle=80, radius=1] node[below right]{$\theta_{1}$};
\draw[dashed, ->] (0:1.5) arc [start angle=0, end angle=100, radius=1.5] node[left ]{$\theta_{2}$};
% Division of 360 degrees by 3
\draw[dashed] (0, 0) -- ( 80:3.2);
\draw[dashed] (0, 0) -- (100:3.2);
\draw[dashed] (0, 0) -- (200:3.2);
\draw[dashed] (0, 0) -- (220:3.2);
\draw[dashed] (0, 0) -- (320:3.2);
\draw[dashed] (0, 0) -- (340:3.2);
% Radius for the position of the joints
\draw[<->] (0, 0) --node[near end, above]{$R$} (180:2.5);
\draw[->] (0, 0) -- ++(3.4, 0) node[above]{$x$};
\draw[->] (0, 0) -- ++(0, 3.4) node[left]{$y$};
\end{tikzpicture}
#+end_src
#+name: fig:joint_position_general
#+caption: Position of the joints
#+RESULTS:
[[file:figs/stewart_bottom_plate.png]]
**** Function description
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
function [stewart] = generateGeneralConfiguration(stewart, args)
% generateGeneralConfiguration - Generate a Very General Configuration
%
% Syntax: [stewart] = generateGeneralConfiguration(stewart, args)
%
% Inputs:
% - args - Can have the following fields:
% - FH [1x1] - Height of the position of the fixed joints with respect to the frame {F} [m]
% - FR [1x1] - Radius of the position of the fixed joints in the X-Y [m]
% - FTh [6x1] - Angles of the fixed joints in the X-Y plane with respect to the X axis [rad]
% - MH [1x1] - Height of the position of the mobile joints with respect to the frame {M} [m]
% - FR [1x1] - Radius of the position of the mobile joints in the X-Y [m]
% - MTh [6x1] - Angles of the mobile joints in the X-Y plane with respect to the X axis [rad]
%
% Outputs:
% - stewart - updated Stewart structure with the added fields:
% - platform_F.Fa [3x6] - Its i'th column is the position vector of joint ai with respect to {F}
% - platform_M.Mb [3x6] - Its i'th column is the position vector of joint bi with respect to {M}
#+end_src
**** Optional Parameters
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
stewart
args.FH (1,1) double {mustBeNumeric, mustBePositive} = 15e-3
args.FR (1,1) double {mustBeNumeric, mustBePositive} = 115e-3;
args.FTh (6,1) double {mustBeNumeric} = [-10, 10, 120-10, 120+10, 240-10, 240+10]*(pi/180);
args.MH (1,1) double {mustBeNumeric, mustBePositive} = 15e-3
args.MR (1,1) double {mustBeNumeric, mustBePositive} = 90e-3;
args.MTh (6,1) double {mustBeNumeric} = [-60+10, 60-10, 60+10, 180-10, 180+10, -60-10]*(pi/180);
end
#+end_src
**** Compute the pose
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
Fa = zeros(3,6);
Mb = zeros(3,6);
#+end_src
#+begin_src matlab
for i = 1:6
Fa(:,i) = [args.FR*cos(args.FTh(i)); args.FR*sin(args.FTh(i)); args.FH];
Mb(:,i) = [args.MR*cos(args.MTh(i)); args.MR*sin(args.MTh(i)); -args.MH];
end
#+end_src
**** Populate the =stewart= structure
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
stewart.platform_F.Fa = Fa;
stewart.platform_M.Mb = Mb;
#+end_src
*** =computeJointsPose=: Compute the Pose of the Joints
:PROPERTIES:
:header-args:matlab+: :tangle ./matlab/src/computeJointsPose.m
:header-args:matlab+: :comments none :mkdirp yes :eval no
:END:
<<sec:computeJointsPose>>
This Matlab function is accessible [[file:./matlab/src/computeJointsPose.m][here]].
**** Documentation
:PROPERTIES:
:UNNUMBERED: t
:END:
#+name: fig:stewart-struts
#+caption: Position and orientation of the struts
[[file:figs/stewart-struts.png]]
**** Function description
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
function [stewart] = computeJointsPose(stewart)
% computeJointsPose -
%
% Syntax: [stewart] = computeJointsPose(stewart)
%
% Inputs:
% - stewart - A structure with the following fields
% - platform_F.Fa [3x6] - Its i'th column is the position vector of joint ai with respect to {F}
% - platform_M.Mb [3x6] - Its i'th column is the position vector of joint bi with respect to {M}
% - platform_F.FO_A [3x1] - Position of {A} with respect to {F}
% - platform_M.MO_B [3x1] - Position of {B} with respect to {M}
% - geometry.FO_M [3x1] - Position of {M} with respect to {F}
%
% Outputs:
% - stewart - A structure with the following added fields
% - geometry.Aa [3x6] - The i'th column is the position of ai with respect to {A}
% - geometry.Ab [3x6] - The i'th column is the position of bi with respect to {A}
% - geometry.Ba [3x6] - The i'th column is the position of ai with respect to {B}
% - geometry.Bb [3x6] - The i'th column is the position of bi with respect to {B}
% - geometry.l [6x1] - The i'th element is the initial length of strut i
% - geometry.As [3x6] - The i'th column is the unit vector of strut i expressed in {A}
% - geometry.Bs [3x6] - The i'th column is the unit vector of strut i expressed in {B}
% - struts_F.l [6x1] - Length of the Fixed part of the i'th strut
% - struts_M.l [6x1] - Length of the Mobile part of the i'th strut
% - platform_F.FRa [3x3x6] - The i'th 3x3 array is the rotation matrix to orientate the bottom of the i'th strut from {F}
% - platform_M.MRb [3x3x6] - The i'th 3x3 array is the rotation matrix to orientate the top of the i'th strut from {M}
#+end_src
**** Check the =stewart= structure elements
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
assert(isfield(stewart.platform_F, 'Fa'), 'stewart.platform_F should have attribute Fa')
Fa = stewart.platform_F.Fa;
assert(isfield(stewart.platform_M, 'Mb'), 'stewart.platform_M should have attribute Mb')
Mb = stewart.platform_M.Mb;
assert(isfield(stewart.platform_F, 'FO_A'), 'stewart.platform_F should have attribute FO_A')
FO_A = stewart.platform_F.FO_A;
assert(isfield(stewart.platform_M, 'MO_B'), 'stewart.platform_M should have attribute MO_B')
MO_B = stewart.platform_M.MO_B;
assert(isfield(stewart.geometry, 'FO_M'), 'stewart.geometry should have attribute FO_M')
FO_M = stewart.geometry.FO_M;
#+end_src
**** Compute the position of the Joints
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
Aa = Fa - repmat(FO_A, [1, 6]);
Bb = Mb - repmat(MO_B, [1, 6]);
Ab = Bb - repmat(-MO_B-FO_M+FO_A, [1, 6]);
Ba = Aa - repmat( MO_B+FO_M-FO_A, [1, 6]);
#+end_src
**** Compute the strut length and orientation
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
As = (Ab - Aa)./vecnorm(Ab - Aa); % As_i is the i'th vector of As
l = vecnorm(Ab - Aa)';
#+end_src
#+begin_src matlab
Bs = (Bb - Ba)./vecnorm(Bb - Ba);
#+end_src
**** Compute the orientation of the Joints
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
FRa = zeros(3,3,6);
MRb = zeros(3,3,6);
for i = 1:6
FRa(:,:,i) = [cross([0;1;0], As(:,i)) , cross(As(:,i), cross([0;1;0], As(:,i))) , As(:,i)];
FRa(:,:,i) = FRa(:,:,i)./vecnorm(FRa(:,:,i));
MRb(:,:,i) = [cross([0;1;0], Bs(:,i)) , cross(Bs(:,i), cross([0;1;0], Bs(:,i))) , Bs(:,i)];
MRb(:,:,i) = MRb(:,:,i)./vecnorm(MRb(:,:,i));
end
#+end_src
**** Populate the =stewart= structure
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
stewart.geometry.Aa = Aa;
stewart.geometry.Ab = Ab;
stewart.geometry.Ba = Ba;
stewart.geometry.Bb = Bb;
stewart.geometry.As = As;
stewart.geometry.Bs = Bs;
stewart.geometry.l = l;
stewart.struts_F.l = l/2;
stewart.struts_M.l = l/2;
stewart.platform_F.FRa = FRa;
stewart.platform_M.MRb = MRb;
#+end_src
*** =initializeStewartPose=: Determine the initial stroke in each leg to have the wanted pose
:PROPERTIES:
:header-args:matlab+: :tangle ./matlab/src/initializeStewartPose.m
:header-args:matlab+: :comments none :mkdirp yes :eval no
:END:
<<sec:initializeStewartPose>>
This Matlab function is accessible [[file:./matlab/src/initializeStewartPose.m][here]].
**** Function description
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
function [stewart] = initializeStewartPose(stewart, args)
% initializeStewartPose - Determine the initial stroke in each leg to have the wanted pose
% It uses the inverse kinematic
%
% Syntax: [stewart] = initializeStewartPose(stewart, args)
%
% Inputs:
% - stewart - A structure with the following fields
% - Aa [3x6] - The positions ai expressed in {A}
% - Bb [3x6] - The positions bi expressed in {B}
% - args - Can have the following fields:
% - AP [3x1] - The wanted position of {B} with respect to {A}
% - ARB [3x3] - The rotation matrix that gives the wanted orientation of {B} with respect to {A}
%
% Outputs:
% - stewart - updated Stewart structure with the added fields:
% - actuators.Leq [6x1] - The 6 needed displacement of the struts from the initial position in [m] to have the wanted pose of {B} w.r.t. {A}
#+end_src
**** Optional Parameters
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
stewart
args.AP (3,1) double {mustBeNumeric} = zeros(3,1)
args.ARB (3,3) double {mustBeNumeric} = eye(3)
end
#+end_src
**** Use the Inverse Kinematic function
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
[Li, dLi] = inverseKinematics(stewart, 'AP', args.AP, 'ARB', args.ARB);
#+end_src
**** Populate the =stewart= structure
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
stewart.actuators.Leq = dLi;
#+end_src
*** =initializeCylindricalPlatforms=: Initialize the geometry of the Fixed and Mobile Platforms
:PROPERTIES:
:header-args:matlab+: :tangle ./matlab/src/initializeCylindricalPlatforms.m
:header-args:matlab+: :comments none :mkdirp yes :eval no
:END:
<<sec:initializeCylindricalPlatforms>>
This Matlab function is accessible [[file:./matlab/src/initializeCylindricalPlatforms.m][here]].
**** Function description
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
function [stewart] = initializeCylindricalPlatforms(stewart, args)
% initializeCylindricalPlatforms - Initialize the geometry of the Fixed and Mobile Platforms
%
% Syntax: [stewart] = initializeCylindricalPlatforms(args)
%
% Inputs:
% - args - Structure with the following fields:
% - Fpm [1x1] - Fixed Platform Mass [kg]
% - Fph [1x1] - Fixed Platform Height [m]
% - Fpr [1x1] - Fixed Platform Radius [m]
% - Mpm [1x1] - Mobile Platform Mass [kg]
% - Mph [1x1] - Mobile Platform Height [m]
% - Mpr [1x1] - Mobile Platform Radius [m]
%
% Outputs:
% - stewart - updated Stewart structure with the added fields:
% - platform_F [struct] - structure with the following fields:
% - type = 1
% - M [1x1] - Fixed Platform Mass [kg]
% - I [3x3] - Fixed Platform Inertia matrix [kg*m^2]
% - H [1x1] - Fixed Platform Height [m]
% - R [1x1] - Fixed Platform Radius [m]
% - platform_M [struct] - structure with the following fields:
% - M [1x1] - Mobile Platform Mass [kg]
% - I [3x3] - Mobile Platform Inertia matrix [kg*m^2]
% - H [1x1] - Mobile Platform Height [m]
% - R [1x1] - Mobile Platform Radius [m]
#+end_src
**** Optional Parameters
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
stewart
args.Fpm (1,1) double {mustBeNumeric, mustBePositive} = 1
args.Fph (1,1) double {mustBeNumeric, mustBePositive} = 10e-3
args.Fpr (1,1) double {mustBeNumeric, mustBePositive} = 125e-3
args.Mpm (1,1) double {mustBeNumeric, mustBePositive} = 1
args.Mph (1,1) double {mustBeNumeric, mustBePositive} = 10e-3
args.Mpr (1,1) double {mustBeNumeric, mustBePositive} = 100e-3
end
#+end_src
**** Compute the Inertia matrices of platforms
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
I_F = diag([1/12*args.Fpm * (3*args.Fpr^2 + args.Fph^2), ...
1/12*args.Fpm * (3*args.Fpr^2 + args.Fph^2), ...
1/2 *args.Fpm * args.Fpr^2]);
#+end_src
#+begin_src matlab
I_M = diag([1/12*args.Mpm * (3*args.Mpr^2 + args.Mph^2), ...
1/12*args.Mpm * (3*args.Mpr^2 + args.Mph^2), ...
1/2 *args.Mpm * args.Mpr^2]);
#+end_src
**** Populate the =stewart= structure
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
stewart.platform_F.type = 1;
stewart.platform_F.I = I_F;
stewart.platform_F.M = args.Fpm;
stewart.platform_F.R = args.Fpr;
stewart.platform_F.H = args.Fph;
#+end_src
#+begin_src matlab
stewart.platform_M.type = 1;
stewart.platform_M.I = I_M;
stewart.platform_M.M = args.Mpm;
stewart.platform_M.R = args.Mpr;
stewart.platform_M.H = args.Mph;
#+end_src
*** =initializeCylindricalStruts=: Define the inertia of cylindrical struts
:PROPERTIES:
:header-args:matlab+: :tangle ./matlab/src/initializeCylindricalStruts.m
:header-args:matlab+: :comments none :mkdirp yes :eval no
:END:
<<sec:initializeCylindricalStruts>>
This Matlab function is accessible [[file:./matlab/src/initializeCylindricalStruts.m][here]].
**** Function description
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
function [stewart] = initializeCylindricalStruts(stewart, args)
% initializeCylindricalStruts - Define the mass and moment of inertia of cylindrical struts
%
% Syntax: [stewart] = initializeCylindricalStruts(args)
%
% Inputs:
% - args - Structure with the following fields:
% - Fsm [1x1] - Mass of the Fixed part of the struts [kg]
% - Fsh [1x1] - Height of cylinder for the Fixed part of the struts [m]
% - Fsr [1x1] - Radius of cylinder for the Fixed part of the struts [m]
% - Msm [1x1] - Mass of the Mobile part of the struts [kg]
% - Msh [1x1] - Height of cylinder for the Mobile part of the struts [m]
% - Msr [1x1] - Radius of cylinder for the Mobile part of the struts [m]
%
% Outputs:
% - stewart - updated Stewart structure with the added fields:
% - struts_F [struct] - structure with the following fields:
% - M [6x1] - Mass of the Fixed part of the struts [kg]
% - I [3x3x6] - Moment of Inertia for the Fixed part of the struts [kg*m^2]
% - H [6x1] - Height of cylinder for the Fixed part of the struts [m]
% - R [6x1] - Radius of cylinder for the Fixed part of the struts [m]
% - struts_M [struct] - structure with the following fields:
% - M [6x1] - Mass of the Mobile part of the struts [kg]
% - I [3x3x6] - Moment of Inertia for the Mobile part of the struts [kg*m^2]
% - H [6x1] - Height of cylinder for the Mobile part of the struts [m]
% - R [6x1] - Radius of cylinder for the Mobile part of the struts [m]
#+end_src
**** Optional Parameters
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
stewart
args.Fsm (1,1) double {mustBeNumeric, mustBePositive} = 0.1
args.Fsh (1,1) double {mustBeNumeric, mustBePositive} = 50e-3
args.Fsr (1,1) double {mustBeNumeric, mustBePositive} = 5e-3
args.Msm (1,1) double {mustBeNumeric, mustBePositive} = 0.1
args.Msh (1,1) double {mustBeNumeric, mustBePositive} = 50e-3
args.Msr (1,1) double {mustBeNumeric, mustBePositive} = 5e-3
end
#+end_src
**** Compute the properties of the cylindrical struts
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
Fsm = ones(6,1).*args.Fsm;
Fsh = ones(6,1).*args.Fsh;
Fsr = ones(6,1).*args.Fsr;
Msm = ones(6,1).*args.Msm;
Msh = ones(6,1).*args.Msh;
Msr = ones(6,1).*args.Msr;
#+end_src
#+begin_src matlab
I_F = zeros(3, 3, 6); % Inertia of the "fixed" part of the strut
I_M = zeros(3, 3, 6); % Inertia of the "mobile" part of the strut
for i = 1:6
I_F(:,:,i) = diag([1/12 * Fsm(i) * (3*Fsr(i)^2 + Fsh(i)^2), ...
1/12 * Fsm(i) * (3*Fsr(i)^2 + Fsh(i)^2), ...
1/2 * Fsm(i) * Fsr(i)^2]);
I_M(:,:,i) = diag([1/12 * Msm(i) * (3*Msr(i)^2 + Msh(i)^2), ...
1/12 * Msm(i) * (3*Msr(i)^2 + Msh(i)^2), ...
1/2 * Msm(i) * Msr(i)^2]);
end
#+end_src
**** Populate the =stewart= structure
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
stewart.struts_M.type = 1;
stewart.struts_M.I = I_M;
stewart.struts_M.M = Msm;
stewart.struts_M.R = Msr;
stewart.struts_M.H = Msh;
#+end_src
#+begin_src matlab
stewart.struts_F.type = 1;
stewart.struts_F.I = I_F;
stewart.struts_F.M = Fsm;
stewart.struts_F.R = Fsr;
stewart.struts_F.H = Fsh;
#+end_src
*** =initializeStrutDynamics=: Add Stiffness and Damping properties of each strut
:PROPERTIES:
:header-args:matlab+: :tangle ./matlab/src/initializeStrutDynamics.m
:header-args:matlab+: :comments none :mkdirp yes :eval no
:END:
<<sec:initializeStrutDynamics>>
This Matlab function is accessible [[file:./matlab/src/initializeStrutDynamics.m][here]].
**** Documentation
:PROPERTIES:
:UNNUMBERED: t
:END:
#+name: fig:piezoelectric_stack
#+attr_html: :width 500px
#+caption: Example of a piezoelectric stach actuator (PI)
[[file:figs/piezoelectric_stack.jpg]]
A simplistic model of such amplified actuator is shown in Figure [[fig:actuator_model_simple]] where:
- $K$ represent the vertical stiffness of the actuator
- $C$ represent the vertical damping of the actuator
- $F$ represents the force applied by the actuator
- $F_{m}$ represents the total measured force
- $v_{m}$ represents the absolute velocity of the top part of the actuator
- $d_{m}$ represents the total relative displacement of the actuator
#+begin_src latex :file actuator_model_simple.pdf :tangle no
\begin{tikzpicture}
\draw (-1, 0) -- (1, 0);
% Spring, Damper, and Actuator
\draw[spring] (-1, 0) -- (-1, 1.5) node[midway, left=0.1]{$K$};
\draw[damper] ( 0, 0) -- ( 0, 1.5) node[midway, left=0.2]{$C$};
\draw[actuator] ( 1, 0) -- ( 1, 1.5) node[midway, left=0.1](F){$F$};
\node[forcesensor={2}{0.2}] (fsens) at (0, 1.5){};
\node[left] at (fsens.west) {$F_{m}$};
\draw[dashed] (1, 0) -- ++(0.4, 0);
\draw[dashed] (1, 1.7) -- ++(0.4, 0);
\draw[->] (0, 1.7)node[]{$\bullet$} -- ++(0, 0.5) node[right]{$v_{m}$};
\draw[<->] (1.4, 0) -- ++(0, 1.7) node[midway, right]{$d_{m}$};
\end{tikzpicture}
#+end_src
#+name: fig:actuator_model_simple
#+caption: Simple model of an Actuator
#+RESULTS:
[[file:figs/actuator_model_simple.png]]
**** Function description
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
function [stewart] = initializeStrutDynamics(stewart, args)
% initializeStrutDynamics - Add Stiffness and Damping properties of each strut
%
% Syntax: [stewart] = initializeStrutDynamics(args)
%
% Inputs:
% - args - Structure with the following fields:
% - K [6x1] - Stiffness of each strut [N/m]
% - C [6x1] - Damping of each strut [N/(m/s)]
%
% Outputs:
% - stewart - updated Stewart structure with the added fields:
% - actuators.type = 1
% - actuators.K [6x1] - Stiffness of each strut [N/m]
% - actuators.C [6x1] - Damping of each strut [N/(m/s)]
#+end_src
**** Optional Parameters
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
stewart
args.type char {mustBeMember(args.type,{'classical', 'amplified'})} = 'classical'
args.K (6,1) double {mustBeNumeric, mustBeNonnegative} = 20e6*ones(6,1)
args.C (6,1) double {mustBeNumeric, mustBeNonnegative} = 2e1*ones(6,1)
args.k1 (6,1) double {mustBeNumeric} = 1e6*ones(6,1)
args.ke (6,1) double {mustBeNumeric} = 5e6*ones(6,1)
args.ka (6,1) double {mustBeNumeric} = 60e6*ones(6,1)
args.c1 (6,1) double {mustBeNumeric} = 10*ones(6,1)
args.F_gain (6,1) double {mustBeNumeric} = 1*ones(6,1)
args.me (6,1) double {mustBeNumeric} = 0.01*ones(6,1)
args.ma (6,1) double {mustBeNumeric} = 0.01*ones(6,1)
end
#+end_src
**** Add Stiffness and Damping properties of each strut
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
if strcmp(args.type, 'classical')
stewart.actuators.type = 1;
elseif strcmp(args.type, 'amplified')
stewart.actuators.type = 2;
end
stewart.actuators.K = args.K;
stewart.actuators.C = args.C;
stewart.actuators.k1 = args.k1;
stewart.actuators.c1 = args.c1;
stewart.actuators.ka = args.ka;
stewart.actuators.ke = args.ke;
stewart.actuators.F_gain = args.F_gain;
stewart.actuators.ma = args.ma;
stewart.actuators.me = args.me;
#+end_src
*** =initializeJointDynamics=: Add Stiffness and Damping properties for spherical joints
:PROPERTIES:
:header-args:matlab+: :tangle ./matlab/src/initializeJointDynamics.m
:header-args:matlab+: :comments none :mkdirp yes :eval no
:END:
<<sec:initializeJointDynamics>>
This Matlab function is accessible [[file:./matlab/src/initializeJointDynamics.m][here]].
**** Function description
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
function [stewart] = initializeJointDynamics(stewart, args)
% initializeJointDynamics - Add Stiffness and Damping properties for the spherical joints
%
% Syntax: [stewart] = initializeJointDynamics(args)
%
% Inputs:
% - args - Structure with the following fields:
% - type_F - 'universal', 'spherical', 'universal_p', 'spherical_p'
% - type_M - 'universal', 'spherical', 'universal_p', 'spherical_p'
% - Kf_M [6x1] - Bending (Rx, Ry) Stiffness for each top joints [(N.m)/rad]
% - Kt_M [6x1] - Torsion (Rz) Stiffness for each top joints [(N.m)/rad]
% - Cf_M [6x1] - Bending (Rx, Ry) Damping of each top joint [(N.m)/(rad/s)]
% - Ct_M [6x1] - Torsion (Rz) Damping of each top joint [(N.m)/(rad/s)]
% - Kf_F [6x1] - Bending (Rx, Ry) Stiffness for each bottom joints [(N.m)/rad]
% - Kt_F [6x1] - Torsion (Rz) Stiffness for each bottom joints [(N.m)/rad]
% - Cf_F [6x1] - Bending (Rx, Ry) Damping of each bottom joint [(N.m)/(rad/s)]
% - Cf_F [6x1] - Torsion (Rz) Damping of each bottom joint [(N.m)/(rad/s)]
%
% Outputs:
% - stewart - updated Stewart structure with the added fields:
% - stewart.joints_F and stewart.joints_M:
% - type - 1 (universal), 2 (spherical), 3 (universal perfect), 4 (spherical perfect)
% - Kx, Ky, Kz [6x1] - Translation (Tx, Ty, Tz) Stiffness [N/m]
% - Kf [6x1] - Flexion (Rx, Ry) Stiffness [(N.m)/rad]
% - Kt [6x1] - Torsion (Rz) Stiffness [(N.m)/rad]
% - Cx, Cy, Cz [6x1] - Translation (Rx, Ry) Damping [N/(m/s)]
% - Cf [6x1] - Flexion (Rx, Ry) Damping [(N.m)/(rad/s)]
% - Cb [6x1] - Torsion (Rz) Damping [(N.m)/(rad/s)]
#+end_src
**** Optional Parameters
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
stewart
args.type_F char {mustBeMember(args.type_F,{'universal', 'spherical', 'universal_p', 'spherical_p', 'universal_3dof', 'spherical_3dof', 'flexible'})} = 'universal'
args.type_M char {mustBeMember(args.type_M,{'universal', 'spherical', 'universal_p', 'spherical_p', 'universal_3dof', 'spherical_3dof', 'flexible'})} = 'spherical'
args.Kf_M (6,1) double {mustBeNumeric, mustBeNonnegative} = 33*ones(6,1)
args.Cf_M (6,1) double {mustBeNumeric, mustBeNonnegative} = 1e-4*ones(6,1)
args.Kt_M (6,1) double {mustBeNumeric, mustBeNonnegative} = 236*ones(6,1)
args.Ct_M (6,1) double {mustBeNumeric, mustBeNonnegative} = 1e-3*ones(6,1)
args.Kf_F (6,1) double {mustBeNumeric, mustBeNonnegative} = 33*ones(6,1)
args.Cf_F (6,1) double {mustBeNumeric, mustBeNonnegative} = 1e-4*ones(6,1)
args.Kt_F (6,1) double {mustBeNumeric, mustBeNonnegative} = 236*ones(6,1)
args.Ct_F (6,1) double {mustBeNumeric, mustBeNonnegative} = 1e-3*ones(6,1)
args.Ka_F (6,1) double {mustBeNumeric, mustBeNonnegative} = 1.2e8*ones(6,1)
args.Ca_F (6,1) double {mustBeNumeric, mustBeNonnegative} = 1e1*ones(6,1)
args.Kr_F (6,1) double {mustBeNumeric, mustBeNonnegative} = 1.1e7*ones(6,1)
args.Cr_F (6,1) double {mustBeNumeric, mustBeNonnegative} = 1e1*ones(6,1)
args.Ka_M (6,1) double {mustBeNumeric, mustBeNonnegative} = 1.2e8*ones(6,1)
args.Ca_M (6,1) double {mustBeNumeric, mustBeNonnegative} = 1e1*ones(6,1)
args.Kr_M (6,1) double {mustBeNumeric, mustBeNonnegative} = 1.1e7*ones(6,1)
args.Cr_M (6,1) double {mustBeNumeric, mustBeNonnegative} = 1e1*ones(6,1)
args.K_M double {mustBeNumeric} = zeros(6,6)
args.M_M double {mustBeNumeric} = zeros(6,6)
args.n_xyz_M double {mustBeNumeric} = zeros(2,3)
args.xi_M double {mustBeNumeric} = 0.1
args.step_file_M char {} = ''
args.K_F double {mustBeNumeric} = zeros(6,6)
args.M_F double {mustBeNumeric} = zeros(6,6)
args.n_xyz_F double {mustBeNumeric} = zeros(2,3)
args.xi_F double {mustBeNumeric} = 0.1
args.step_file_F char {} = ''
end
#+end_src
**** Add Actuator Type
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
switch args.type_F
case 'universal'
stewart.joints_F.type = 1;
case 'spherical'
stewart.joints_F.type = 2;
case 'universal_p'
stewart.joints_F.type = 3;
case 'spherical_p'
stewart.joints_F.type = 4;
case 'flexible'
stewart.joints_F.type = 5;
case 'universal_3dof'
stewart.joints_F.type = 6;
case 'spherical_3dof'
stewart.joints_F.type = 7;
end
switch args.type_M
case 'universal'
stewart.joints_M.type = 1;
case 'spherical'
stewart.joints_M.type = 2;
case 'universal_p'
stewart.joints_M.type = 3;
case 'spherical_p'
stewart.joints_M.type = 4;
case 'flexible'
stewart.joints_M.type = 5;
case 'universal_3dof'
stewart.joints_M.type = 6;
case 'spherical_3dof'
stewart.joints_M.type = 7;
end
#+end_src
**** Add Stiffness and Damping in Translation of each strut
:PROPERTIES:
:UNNUMBERED: t
:END:
Axial and Radial (shear) Stiffness
#+begin_src matlab
stewart.joints_M.Ka = args.Ka_M;
stewart.joints_M.Kr = args.Kr_M;
stewart.joints_F.Ka = args.Ka_F;
stewart.joints_F.Kr = args.Kr_F;
#+end_src
Translation Damping
#+begin_src matlab
stewart.joints_M.Ca = args.Ca_M;
stewart.joints_M.Cr = args.Cr_M;
stewart.joints_F.Ca = args.Ca_F;
stewart.joints_F.Cr = args.Cr_F;
#+end_src
**** Add Stiffness and Damping in Rotation of each strut
:PROPERTIES:
:UNNUMBERED: t
:END:
Rotational Stiffness
#+begin_src matlab
stewart.joints_M.Kf = args.Kf_M;
stewart.joints_M.Kt = args.Kt_M;
stewart.joints_F.Kf = args.Kf_F;
stewart.joints_F.Kt = args.Kt_F;
#+end_src
Rotational Damping
#+begin_src matlab
stewart.joints_M.Cf = args.Cf_M;
stewart.joints_M.Ct = args.Ct_M;
stewart.joints_F.Cf = args.Cf_F;
stewart.joints_F.Ct = args.Ct_F;
#+end_src
**** Stiffness and Mass matrices for flexible joint
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
stewart.joints_F.M = args.M_F;
stewart.joints_F.K = args.K_F;
stewart.joints_F.n_xyz = args.n_xyz_F;
stewart.joints_F.xi = args.xi_F;
stewart.joints_F.xi = args.xi_F;
stewart.joints_F.step_file = args.step_file_F;
stewart.joints_M.M = args.M_M;
stewart.joints_M.K = args.K_M;
stewart.joints_M.n_xyz = args.n_xyz_M;
stewart.joints_M.xi = args.xi_M;
stewart.joints_M.step_file = args.step_file_M;
#+end_src
*** =initializeInertialSensor=: Initialize the inertial sensor in each strut
:PROPERTIES:
:header-args:matlab+: :tangle ./matlab/src/initializeInertialSensor.m
:header-args:matlab+: :comments none :mkdirp yes :eval no
:END:
<<sec:initializeInertialSensor>>
This Matlab function is accessible [[file:./matlab/src/initializeInertialSensor.m][here]].
**** Geophone - Working Principle
:PROPERTIES:
:UNNUMBERED: t
:END:
From the schematic of the Z-axis geophone shown in Figure [[fig:z_axis_geophone]], we can write the transfer function from the support velocity $\dot{w}$ to the relative velocity of the inertial mass $\dot{d}$:
\[ \frac{\dot{d}}{\dot{w}} = \frac{-\frac{s^2}{{\omega_0}^2}}{\frac{s^2}{{\omega_0}^2} + 2 \xi \frac{s}{\omega_0} + 1} \]
with:
- $\omega_0 = \sqrt{\frac{k}{m}}$
- $\xi = \frac{1}{2} \sqrt{\frac{m}{k}}$
#+name: fig:z_axis_geophone
#+caption: Schematic of a Z-Axis geophone
[[file:figs/inertial_sensor.png]]
We see that at frequencies above $\omega_0$:
\[ \frac{\dot{d}}{\dot{w}} \approx -1 \]
And thus, the measurement of the relative velocity of the mass with respect to its support gives the absolute velocity of the support.
We generally want to have the smallest resonant frequency $\omega_0$ to measure low frequency absolute velocity, however there is a trade-off between $\omega_0$ and the mass of the inertial mass.
**** Accelerometer - Working Principle
:PROPERTIES:
:UNNUMBERED: t
:END:
From the schematic of the Z-axis accelerometer shown in Figure [[fig:z_axis_accelerometer]], we can write the transfer function from the support acceleration $\ddot{w}$ to the relative position of the inertial mass $d$:
\[ \frac{d}{\ddot{w}} = \frac{-\frac{1}{{\omega_0}^2}}{\frac{s^2}{{\omega_0}^2} + 2 \xi \frac{s}{\omega_0} + 1} \]
with:
- $\omega_0 = \sqrt{\frac{k}{m}}$
- $\xi = \frac{1}{2} \sqrt{\frac{m}{k}}$
#+name: fig:z_axis_accelerometer
#+caption: Schematic of a Z-Axis geophone
[[file:figs/inertial_sensor.png]]
We see that at frequencies below $\omega_0$:
\[ \frac{d}{\ddot{w}} \approx -\frac{1}{{\omega_0}^2} \]
And thus, the measurement of the relative displacement of the mass with respect to its support gives the absolute acceleration of the support.
Note that there is trade-off between:
- the highest measurable acceleration $\omega_0$
- the sensitivity of the accelerometer which is equal to $-\frac{1}{{\omega_0}^2}$
**** Function description
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
function [stewart] = initializeInertialSensor(stewart, args)
% initializeInertialSensor - Initialize the inertial sensor in each strut
%
% Syntax: [stewart] = initializeInertialSensor(args)
%
% Inputs:
% - args - Structure with the following fields:
% - type - 'geophone', 'accelerometer', 'none'
% - mass [1x1] - Weight of the inertial mass [kg]
% - freq [1x1] - Cutoff frequency [Hz]
%
% Outputs:
% - stewart - updated Stewart structure with the added fields:
% - stewart.sensors.inertial
% - type - 1 (geophone), 2 (accelerometer), 3 (none)
% - K [1x1] - Stiffness [N/m]
% - C [1x1] - Damping [N/(m/s)]
% - M [1x1] - Inertial Mass [kg]
% - G [1x1] - Gain
#+end_src
**** Optional Parameters
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
stewart
args.type char {mustBeMember(args.type,{'geophone', 'accelerometer', 'none'})} = 'none'
args.mass (1,1) double {mustBeNumeric, mustBeNonnegative} = 1e-2
args.freq (1,1) double {mustBeNumeric, mustBeNonnegative} = 1e3
end
#+end_src
**** Compute the properties of the sensor
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
sensor = struct();
switch args.type
case 'geophone'
sensor.type = 1;
sensor.M = args.mass;
sensor.K = sensor.M * (2*pi*args.freq)^2;
sensor.C = 2*sqrt(sensor.M * sensor.K);
case 'accelerometer'
sensor.type = 2;
sensor.M = args.mass;
sensor.K = sensor.M * (2*pi*args.freq)^2;
sensor.C = 2*sqrt(sensor.M * sensor.K);
sensor.G = -sensor.K/sensor.M;
case 'none'
sensor.type = 3;
end
#+end_src
**** Populate the =stewart= structure
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
stewart.sensors.inertial = sensor;
#+end_src
*** =displayArchitecture=: 3D plot of the Stewart platform architecture
:PROPERTIES:
:header-args:matlab+: :tangle ./matlab/src/displayArchitecture.m
:header-args:matlab+: :comments none :mkdirp yes :eval no
:END:
<<sec:displayArchitecture>>
This Matlab function is accessible [[file:./matlab/src/displayArchitecture.m][here]].
**** Function description
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
function [] = displayArchitecture(stewart, args)
% displayArchitecture - 3D plot of the Stewart platform architecture
%
% Syntax: [] = displayArchitecture(args)
%
% Inputs:
% - stewart
% - args - Structure with the following fields:
% - AP [3x1] - The wanted position of {B} with respect to {A}
% - ARB [3x3] - The rotation matrix that gives the wanted orientation of {B} with respect to {A}
% - ARB [3x3] - The rotation matrix that gives the wanted orientation of {B} with respect to {A}
% - F_color [color] - Color used for the Fixed elements
% - M_color [color] - Color used for the Mobile elements
% - L_color [color] - Color used for the Legs elements
% - frames [true/false] - Display the Frames
% - legs [true/false] - Display the Legs
% - joints [true/false] - Display the Joints
% - labels [true/false] - Display the Labels
% - platforms [true/false] - Display the Platforms
% - views ['all', 'xy', 'yz', 'xz', 'default'] -
%
% Outputs:
#+end_src
**** Optional Parameters
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
stewart
args.AP (3,1) double {mustBeNumeric} = zeros(3,1)
args.ARB (3,3) double {mustBeNumeric} = eye(3)
args.F_color = [0 0.4470 0.7410]
args.M_color = [0.8500 0.3250 0.0980]
args.L_color = [0 0 0]
args.frames logical {mustBeNumericOrLogical} = true
args.legs logical {mustBeNumericOrLogical} = true
args.joints logical {mustBeNumericOrLogical} = true
args.labels logical {mustBeNumericOrLogical} = true
args.platforms logical {mustBeNumericOrLogical} = true
args.views char {mustBeMember(args.views,{'all', 'xy', 'xz', 'yz', 'default'})} = 'default'
end
#+end_src
**** Check the =stewart= structure elements
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
assert(isfield(stewart.platform_F, 'FO_A'), 'stewart.platform_F should have attribute FO_A')
FO_A = stewart.platform_F.FO_A;
assert(isfield(stewart.platform_M, 'MO_B'), 'stewart.platform_M should have attribute MO_B')
MO_B = stewart.platform_M.MO_B;
assert(isfield(stewart.geometry, 'H'), 'stewart.geometry should have attribute H')
H = stewart.geometry.H;
assert(isfield(stewart.platform_F, 'Fa'), 'stewart.platform_F should have attribute Fa')
Fa = stewart.platform_F.Fa;
assert(isfield(stewart.platform_M, 'Mb'), 'stewart.platform_M should have attribute Mb')
Mb = stewart.platform_M.Mb;
#+end_src
**** Figure Creation, Frames and Homogeneous transformations
:PROPERTIES:
:UNNUMBERED: t
:END:
The reference frame of the 3d plot corresponds to the frame $\{F\}$.
#+begin_src matlab
if ~strcmp(args.views, 'all')
figure;
else
f = figure('visible', 'off');
end
hold on;
#+end_src
We first compute homogeneous matrices that will be useful to position elements on the figure where the reference frame is $\{F\}$.
#+begin_src matlab
FTa = [eye(3), FO_A; ...
zeros(1,3), 1];
ATb = [args.ARB, args.AP; ...
zeros(1,3), 1];
BTm = [eye(3), -MO_B; ...
zeros(1,3), 1];
FTm = FTa*ATb*BTm;
#+end_src
Let's define a parameter that define the length of the unit vectors used to display the frames.
#+begin_src matlab
d_unit_vector = H/4;
#+end_src
Let's define a parameter used to position the labels with respect to the center of the element.
#+begin_src matlab
d_label = H/20;
#+end_src
**** Fixed Base elements
:PROPERTIES:
:UNNUMBERED: t
:END:
Let's first plot the frame $\{F\}$.
#+begin_src matlab
Ff = [0, 0, 0];
if args.frames
quiver3(Ff(1)*ones(1,3), Ff(2)*ones(1,3), Ff(3)*ones(1,3), ...
[d_unit_vector 0 0], [0 d_unit_vector 0], [0 0 d_unit_vector], '-', 'Color', args.F_color)
if args.labels
text(Ff(1) + d_label, ...
Ff(2) + d_label, ...
Ff(3) + d_label, '$\{F\}$', 'Color', args.F_color);
end
end
#+end_src
Now plot the frame $\{A\}$ fixed to the Base.
#+begin_src matlab
if args.frames
quiver3(FO_A(1)*ones(1,3), FO_A(2)*ones(1,3), FO_A(3)*ones(1,3), ...
[d_unit_vector 0 0], [0 d_unit_vector 0], [0 0 d_unit_vector], '-', 'Color', args.F_color)
if args.labels
text(FO_A(1) + d_label, ...
FO_A(2) + d_label, ...
FO_A(3) + d_label, '$\{A\}$', 'Color', args.F_color);
end
end
#+end_src
Let's then plot the circle corresponding to the shape of the Fixed base.
#+begin_src matlab
if args.platforms && stewart.platform_F.type == 1
theta = [0:0.01:2*pi+0.01]; % Angles [rad]
v = null([0; 0; 1]'); % Two vectors that are perpendicular to the circle normal
center = [0; 0; 0]; % Center of the circle
radius = stewart.platform_F.R; % Radius of the circle [m]
points = center*ones(1, length(theta)) + radius*(v(:,1)*cos(theta) + v(:,2)*sin(theta));
plot3(points(1,:), ...
points(2,:), ...
points(3,:), '-', 'Color', args.F_color);
end
#+end_src
Let's now plot the position and labels of the Fixed Joints
#+begin_src matlab
if args.joints
scatter3(Fa(1,:), ...
Fa(2,:), ...
Fa(3,:), 'MarkerEdgeColor', args.F_color);
if args.labels
for i = 1:size(Fa,2)
text(Fa(1,i) + d_label, ...
Fa(2,i), ...
Fa(3,i), sprintf('$a_{%i}$', i), 'Color', args.F_color);
end
end
end
#+end_src
**** Mobile Platform elements
:PROPERTIES:
:UNNUMBERED: t
:END:
Plot the frame $\{M\}$.
#+begin_src matlab
Fm = FTm*[0; 0; 0; 1]; % Get the position of frame {M} w.r.t. {F}
if args.frames
FM_uv = FTm*[d_unit_vector*eye(3); zeros(1,3)]; % Rotated Unit vectors
quiver3(Fm(1)*ones(1,3), Fm(2)*ones(1,3), Fm(3)*ones(1,3), ...
FM_uv(1,1:3), FM_uv(2,1:3), FM_uv(3,1:3), '-', 'Color', args.M_color)
if args.labels
text(Fm(1) + d_label, ...
Fm(2) + d_label, ...
Fm(3) + d_label, '$\{M\}$', 'Color', args.M_color);
end
end
#+end_src
Plot the frame $\{B\}$.
#+begin_src matlab
FB = FO_A + args.AP;
if args.frames
FB_uv = FTm*[d_unit_vector*eye(3); zeros(1,3)]; % Rotated Unit vectors
quiver3(FB(1)*ones(1,3), FB(2)*ones(1,3), FB(3)*ones(1,3), ...
FB_uv(1,1:3), FB_uv(2,1:3), FB_uv(3,1:3), '-', 'Color', args.M_color)
if args.labels
text(FB(1) - d_label, ...
FB(2) + d_label, ...
FB(3) + d_label, '$\{B\}$', 'Color', args.M_color);
end
end
#+end_src
Let's then plot the circle corresponding to the shape of the Mobile platform.
#+begin_src matlab
if args.platforms && stewart.platform_M.type == 1
theta = [0:0.01:2*pi+0.01]; % Angles [rad]
v = null((FTm(1:3,1:3)*[0;0;1])'); % Two vectors that are perpendicular to the circle normal
center = Fm(1:3); % Center of the circle
radius = stewart.platform_M.R; % Radius of the circle [m]
points = center*ones(1, length(theta)) + radius*(v(:,1)*cos(theta) + v(:,2)*sin(theta));
plot3(points(1,:), ...
points(2,:), ...
points(3,:), '-', 'Color', args.M_color);
end
#+end_src
Plot the position and labels of the rotation joints fixed to the mobile platform.
#+begin_src matlab
if args.joints
Fb = FTm*[Mb;ones(1,6)];
scatter3(Fb(1,:), ...
Fb(2,:), ...
Fb(3,:), 'MarkerEdgeColor', args.M_color);
if args.labels
for i = 1:size(Fb,2)
text(Fb(1,i) + d_label, ...
Fb(2,i), ...
Fb(3,i), sprintf('$b_{%i}$', i), 'Color', args.M_color);
end
end
end
#+end_src
**** Legs
:PROPERTIES:
:UNNUMBERED: t
:END:
Plot the legs connecting the joints of the fixed base to the joints of the mobile platform.
#+begin_src matlab
if args.legs
for i = 1:6
plot3([Fa(1,i), Fb(1,i)], ...
[Fa(2,i), Fb(2,i)], ...
[Fa(3,i), Fb(3,i)], '-', 'Color', args.L_color);
if args.labels
text((Fa(1,i)+Fb(1,i))/2 + d_label, ...
(Fa(2,i)+Fb(2,i))/2, ...
(Fa(3,i)+Fb(3,i))/2, sprintf('$%i$', i), 'Color', args.L_color);
end
end
end
#+end_src
**** Figure parameters
#+begin_src matlab
switch args.views
case 'default'
view([1 -0.6 0.4]);
case 'xy'
view([0 0 1]);
case 'xz'
view([0 -1 0]);
case 'yz'
view([1 0 0]);
end
axis equal;
axis off;
#+end_src
**** Subplots
#+begin_src matlab
if strcmp(args.views, 'all')
hAx = findobj('type', 'axes');
figure;
s1 = subplot(2,2,1);
copyobj(get(hAx(1), 'Children'), s1);
view([0 0 1]);
axis equal;
axis off;
title('Top')
s2 = subplot(2,2,2);
copyobj(get(hAx(1), 'Children'), s2);
view([1 -0.6 0.4]);
axis equal;
axis off;
s3 = subplot(2,2,3);
copyobj(get(hAx(1), 'Children'), s3);
view([1 0 0]);
axis equal;
axis off;
title('Front')
s4 = subplot(2,2,4);
copyobj(get(hAx(1), 'Children'), s4);
view([0 -1 0]);
axis equal;
axis off;
title('Side')
close(f);
end
#+end_src
*** =describeStewartPlatform=: Display some text describing the current defined Stewart Platform
:PROPERTIES:
:header-args:matlab+: :tangle ./matlab/src/describeStewartPlatform.m
:header-args:matlab+: :comments none :mkdirp yes :eval no
:END:
<<sec:describeStewartPlatform>>
This Matlab function is accessible [[file:./matlab/src/describeStewartPlatform.m][here]].
**** Function description
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
function [] = describeStewartPlatform(stewart)
% describeStewartPlatform - Display some text describing the current defined Stewart Platform
%
% Syntax: [] = describeStewartPlatform(args)
%
% Inputs:
% - stewart
%
% Outputs:
#+end_src
**** Optional Parameters
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
stewart
end
#+end_src
**** Geometry
#+begin_src matlab
fprintf('GEOMETRY:\n')
fprintf('- The height between the fixed based and the top platform is %.3g [mm].\n', 1e3*stewart.geometry.H)
if stewart.platform_M.MO_B(3) > 0
fprintf('- Frame {A} is located %.3g [mm] above the top platform.\n', 1e3*stewart.platform_M.MO_B(3))
else
fprintf('- Frame {A} is located %.3g [mm] below the top platform.\n', - 1e3*stewart.platform_M.MO_B(3))
end
fprintf('- The initial length of the struts are:\n')
fprintf('\t %.3g, %.3g, %.3g, %.3g, %.3g, %.3g [mm]\n', 1e3*stewart.geometry.l)
fprintf('\n')
#+end_src
**** Actuators
#+begin_src matlab
fprintf('ACTUATORS:\n')
if stewart.actuators.type == 1
fprintf('- The actuators are classical.\n')
fprintf('- The Stiffness and Damping of each actuators is:\n')
fprintf('\t k = %.0e [N/m] \t c = %.0e [N/(m/s)]\n', stewart.actuators.K(1), stewart.actuators.C(1))
elseif stewart.actuators.type == 2
fprintf('- The actuators are mechanicaly amplified.\n')
fprintf('- The vertical stiffness and damping contribution of the piezoelectric stack is:\n')
fprintf('\t ka = %.0e [N/m] \t ca = %.0e [N/(m/s)]\n', stewart.actuators.Ka(1), stewart.actuators.Ca(1))
fprintf('- Vertical stiffness when the piezoelectric stack is removed is:\n')
fprintf('\t kr = %.0e [N/m] \t cr = %.0e [N/(m/s)]\n', stewart.actuators.Kr(1), stewart.actuators.Cr(1))
end
fprintf('\n')
#+end_src
**** Joints
#+begin_src matlab
fprintf('JOINTS:\n')
#+end_src
Type of the joints on the fixed base.
#+begin_src matlab
switch stewart.joints_F.type
case 1
fprintf('- The joints on the fixed based are universal joints\n')
case 2
fprintf('- The joints on the fixed based are spherical joints\n')
case 3
fprintf('- The joints on the fixed based are perfect universal joints\n')
case 4
fprintf('- The joints on the fixed based are perfect spherical joints\n')
end
#+end_src
Type of the joints on the mobile platform.
#+begin_src matlab
switch stewart.joints_M.type
case 1
fprintf('- The joints on the mobile based are universal joints\n')
case 2
fprintf('- The joints on the mobile based are spherical joints\n')
case 3
fprintf('- The joints on the mobile based are perfect universal joints\n')
case 4
fprintf('- The joints on the mobile based are perfect spherical joints\n')
end
#+end_src
Position of the fixed joints
#+begin_src matlab
fprintf('- The position of the joints on the fixed based with respect to {F} are (in [mm]):\n')
fprintf('\t % .3g \t % .3g \t % .3g\n', 1e3*stewart.platform_F.Fa)
#+end_src
Position of the mobile joints
#+begin_src matlab
fprintf('- The position of the joints on the mobile based with respect to {M} are (in [mm]):\n')
fprintf('\t % .3g \t % .3g \t % .3g\n', 1e3*stewart.platform_M.Mb)
fprintf('\n')
#+end_src
**** Kinematics
#+begin_src matlab
fprintf('KINEMATICS:\n')
if isfield(stewart.kinematics, 'K')
fprintf('- The Stiffness matrix K is (in [N/m]):\n')
fprintf('\t % .0e \t % .0e \t % .0e \t % .0e \t % .0e \t % .0e\n', stewart.kinematics.K)
end
if isfield(stewart.kinematics, 'C')
fprintf('- The Damping matrix C is (in [m/N]):\n')
fprintf('\t % .0e \t % .0e \t % .0e \t % .0e \t % .0e \t % .0e\n', stewart.kinematics.C)
end
#+end_src
*** =generateCubicConfiguration=: Generate a Cubic Configuration
:PROPERTIES:
:header-args:matlab+: :tangle ./matlab/src/generateCubicConfiguration.m
:header-args:matlab+: :comments none :mkdirp yes :eval no
:END:
<<sec:generateCubicConfiguration>>
This Matlab function is accessible [[file:./matlab/src/generateCubicConfiguration.m][here]].
**** Function description
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
function [stewart] = generateCubicConfiguration(stewart, args)
% generateCubicConfiguration - Generate a Cubic Configuration
%
% Syntax: [stewart] = generateCubicConfiguration(stewart, args)
%
% Inputs:
% - stewart - A structure with the following fields
% - geometry.H [1x1] - Total height of the platform [m]
% - args - Can have the following fields:
% - Hc [1x1] - Height of the "useful" part of the cube [m]
% - FOc [1x1] - Height of the center of the cube with respect to {F} [m]
% - FHa [1x1] - Height of the plane joining the points ai with respect to the frame {F} [m]
% - MHb [1x1] - Height of the plane joining the points bi with respect to the frame {M} [m]
%
% Outputs:
% - stewart - updated Stewart structure with the added fields:
% - platform_F.Fa [3x6] - Its i'th column is the position vector of joint ai with respect to {F}
% - platform_M.Mb [3x6] - Its i'th column is the position vector of joint bi with respect to {M}
#+end_src
**** Documentation
:PROPERTIES:
:UNNUMBERED: t
:END:
#+name: fig:cubic-configuration-definition
#+caption: Cubic Configuration
[[file:figs/cubic-configuration-definition.png]]
**** Optional Parameters
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
stewart
args.Hc (1,1) double {mustBeNumeric, mustBePositive} = 60e-3
args.FOc (1,1) double {mustBeNumeric} = 50e-3
args.FHa (1,1) double {mustBeNumeric, mustBeNonnegative} = 15e-3
args.MHb (1,1) double {mustBeNumeric, mustBeNonnegative} = 15e-3
end
#+end_src
**** Check the =stewart= structure elements
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
assert(isfield(stewart.geometry, 'H'), 'stewart.geometry should have attribute H')
H = stewart.geometry.H;
#+end_src
**** Position of the Cube
:PROPERTIES:
:UNNUMBERED: t
:END:
We define the useful points of the cube with respect to the Cube's center.
${}^{C}C$ are the 6 vertices of the cubes expressed in a frame {C} which is located at the center of the cube and aligned with {F} and {M}.
#+begin_src matlab
sx = [ 2; -1; -1];
sy = [ 0; 1; -1];
sz = [ 1; 1; 1];
R = [sx, sy, sz]./vecnorm([sx, sy, sz]);
L = args.Hc*sqrt(3);
Cc = R'*[[0;0;L],[L;0;L],[L;0;0],[L;L;0],[0;L;0],[0;L;L]] - [0;0;1.5*args.Hc];
CCf = [Cc(:,1), Cc(:,3), Cc(:,3), Cc(:,5), Cc(:,5), Cc(:,1)]; % CCf(:,i) corresponds to the bottom cube's vertice corresponding to the i'th leg
CCm = [Cc(:,2), Cc(:,2), Cc(:,4), Cc(:,4), Cc(:,6), Cc(:,6)]; % CCm(:,i) corresponds to the top cube's vertice corresponding to the i'th leg
#+end_src
**** Compute the pose
:PROPERTIES:
:UNNUMBERED: t
:END:
We can compute the vector of each leg ${}^{C}\hat{\bm{s}}_{i}$ (unit vector from ${}^{C}C_{f}$ to ${}^{C}C_{m}$).
#+begin_src matlab
CSi = (CCm - CCf)./vecnorm(CCm - CCf);
#+end_src
We now which to compute the position of the joints $a_{i}$ and $b_{i}$.
#+begin_src matlab
Fa = CCf + [0; 0; args.FOc] + ((args.FHa-(args.FOc-args.Hc/2))./CSi(3,:)).*CSi;
Mb = CCf + [0; 0; args.FOc-H] + ((H-args.MHb-(args.FOc-args.Hc/2))./CSi(3,:)).*CSi;
#+end_src
**** Populate the =stewart= structure
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
stewart.platform_F.Fa = Fa;
stewart.platform_M.Mb = Mb;
#+end_src
*** =computeJacobian=: Compute the Jacobian Matrix
:PROPERTIES:
:header-args:matlab+: :tangle ./matlab/src/computeJacobian.m
:header-args:matlab+: :comments none :mkdirp yes :eval no
:END:
<<sec:computeJacobian>>
This Matlab function is accessible [[file:./matlab/src/computeJacobian.m][here]].
**** Function description
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
function [stewart] = computeJacobian(stewart)
% computeJacobian -
%
% Syntax: [stewart] = computeJacobian(stewart)
%
% Inputs:
% - stewart - With at least the following fields:
% - geometry.As [3x6] - The 6 unit vectors for each strut expressed in {A}
% - geometry.Ab [3x6] - The 6 position of the joints bi expressed in {A}
% - actuators.K [6x1] - Total stiffness of the actuators
%
% Outputs:
% - stewart - With the 3 added field:
% - kinematics.J [6x6] - The Jacobian Matrix
% - kinematics.K [6x6] - The Stiffness Matrix
% - kinematics.C [6x6] - The Compliance Matrix
#+end_src
**** Check the =stewart= structure elements
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
assert(isfield(stewart.geometry, 'As'), 'stewart.geometry should have attribute As')
As = stewart.geometry.As;
assert(isfield(stewart.geometry, 'Ab'), 'stewart.geometry should have attribute Ab')
Ab = stewart.geometry.Ab;
assert(isfield(stewart.actuators, 'K'), 'stewart.actuators should have attribute K')
Ki = stewart.actuators.K;
#+end_src
**** Compute Jacobian Matrix
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
J = [As' , cross(Ab, As)'];
#+end_src
**** Compute Stiffness Matrix
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
K = J'*diag(Ki)*J;
#+end_src
**** Compute Compliance Matrix
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
C = inv(K);
#+end_src
**** Populate the =stewart= structure
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
stewart.kinematics.J = J;
stewart.kinematics.K = K;
stewart.kinematics.C = C;
#+end_src
*** =inverseKinematics=: Compute Inverse Kinematics
:PROPERTIES:
:header-args:matlab+: :tangle ./matlab/src/inverseKinematics.m
:header-args:matlab+: :comments none :mkdirp yes :eval no
:END:
<<sec:inverseKinematics>>
This Matlab function is accessible [[file:./matlab/src/inverseKinematics.m][here]].
**** Theory
:PROPERTIES:
:UNNUMBERED: t
:END:
For inverse kinematic analysis, it is assumed that the position ${}^A\bm{P}$ and orientation of the moving platform ${}^A\bm{R}_B$ are given and the problem is to obtain the joint variables, namely, $\bm{L} = [l_1, l_2, \dots, l_6]^T$.
From the geometry of the manipulator, the loop closure for each limb, $i = 1, 2, \dots, 6$ can be written as
\begin{align*}
l_i {}^A\hat{\bm{s}}_i &= {}^A\bm{A} + {}^A\bm{b}_i - {}^A\bm{a}_i \\
&= {}^A\bm{A} + {}^A\bm{R}_b {}^B\bm{b}_i - {}^A\bm{a}_i
\end{align*}
To obtain the length of each actuator and eliminate $\hat{\bm{s}}_i$, it is sufficient to dot multiply each side by itself:
\begin{equation}
l_i^2 \left[ {}^A\hat{\bm{s}}_i^T {}^A\hat{\bm{s}}_i \right] = \left[ {}^A\bm{P} + {}^A\bm{R}_B {}^B\bm{b}_i - {}^A\bm{a}_i \right]^T \left[ {}^A\bm{P} + {}^A\bm{R}_B {}^B\bm{b}_i - {}^A\bm{a}_i \right]
\end{equation}
Hence, for $i = 1, 2, \dots, 6$, each limb length can be uniquely determined by:
\begin{equation}
l_i = \sqrt{{}^A\bm{P}^T {}^A\bm{P} + {}^B\bm{b}_i^T {}^B\bm{b}_i + {}^A\bm{a}_i^T {}^A\bm{a}_i - 2 {}^A\bm{P}^T {}^A\bm{a}_i + 2 {}^A\bm{P}^T \left[{}^A\bm{R}_B {}^B\bm{b}_i\right] - 2 \left[{}^A\bm{R}_B {}^B\bm{b}_i\right]^T {}^A\bm{a}_i}
\end{equation}
If the position and orientation of the moving platform lie in the feasible workspace of the manipulator, one unique solution to the limb length is determined by the above equation.
Otherwise, when the limbs' lengths derived yield complex numbers, then the position or orientation of the moving platform is not reachable.
**** Function description
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
function [Li, dLi] = inverseKinematics(stewart, args)
% inverseKinematics - Compute the needed length of each strut to have the wanted position and orientation of {B} with respect to {A}
%
% Syntax: [stewart] = inverseKinematics(stewart)
%
% Inputs:
% - stewart - A structure with the following fields
% - geometry.Aa [3x6] - The positions ai expressed in {A}
% - geometry.Bb [3x6] - The positions bi expressed in {B}
% - geometry.l [6x1] - Length of each strut
% - args - Can have the following fields:
% - AP [3x1] - The wanted position of {B} with respect to {A}
% - ARB [3x3] - The rotation matrix that gives the wanted orientation of {B} with respect to {A}
%
% Outputs:
% - Li [6x1] - The 6 needed length of the struts in [m] to have the wanted pose of {B} w.r.t. {A}
% - dLi [6x1] - The 6 needed displacement of the struts from the initial position in [m] to have the wanted pose of {B} w.r.t. {A}
#+end_src
**** Optional Parameters
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
stewart
args.AP (3,1) double {mustBeNumeric} = zeros(3,1)
args.ARB (3,3) double {mustBeNumeric} = eye(3)
end
#+end_src
**** Check the =stewart= structure elements
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
assert(isfield(stewart.geometry, 'Aa'), 'stewart.geometry should have attribute Aa')
Aa = stewart.geometry.Aa;
assert(isfield(stewart.geometry, 'Bb'), 'stewart.geometry should have attribute Bb')
Bb = stewart.geometry.Bb;
assert(isfield(stewart.geometry, 'l'), 'stewart.geometry should have attribute l')
l = stewart.geometry.l;
#+end_src
**** Compute
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
Li = sqrt(args.AP'*args.AP + diag(Bb'*Bb) + diag(Aa'*Aa) - (2*args.AP'*Aa)' + (2*args.AP'*(args.ARB*Bb))' - diag(2*(args.ARB*Bb)'*Aa));
#+end_src
#+begin_src matlab
dLi = Li-l;
#+end_src
*** =forwardKinematicsApprox=: Compute the Approximate Forward Kinematics
:PROPERTIES:
:header-args:matlab+: :tangle ./matlab/src/forwardKinematicsApprox.m
:header-args:matlab+: :comments none :mkdirp yes :eval no
:END:
<<sec:forwardKinematicsApprox>>
This Matlab function is accessible [[file:./matlab/src/forwardKinematicsApprox.m][here]].
**** Function description
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
function [P, R] = forwardKinematicsApprox(stewart, args)
% forwardKinematicsApprox - Computed the approximate pose of {B} with respect to {A} from the length of each strut and using
% the Jacobian Matrix
%
% Syntax: [P, R] = forwardKinematicsApprox(stewart, args)
%
% Inputs:
% - stewart - A structure with the following fields
% - kinematics.J [6x6] - The Jacobian Matrix
% - args - Can have the following fields:
% - dL [6x1] - Displacement of each strut [m]
%
% Outputs:
% - P [3x1] - The estimated position of {B} with respect to {A}
% - R [3x3] - The estimated rotation matrix that gives the orientation of {B} with respect to {A}
#+end_src
**** Optional Parameters
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
stewart
args.dL (6,1) double {mustBeNumeric} = zeros(6,1)
end
#+end_src
**** Check the =stewart= structure elements
:PROPERTIES:
:UNNUMBERED: t
:END:
#+begin_src matlab
assert(isfield(stewart.kinematics, 'J'), 'stewart.kinematics should have attribute J')
J = stewart.kinematics.J;
#+end_src
**** Computation
:PROPERTIES:
:UNNUMBERED: t
:END:
From a small displacement of each strut $d\bm{\mathcal{L}}$, we can compute the
position and orientation of {B} with respect to {A} using the following formula:
\[ d \bm{\mathcal{X}} = \bm{J}^{-1} d\bm{\mathcal{L}} \]
#+begin_src matlab
X = J\args.dL;
#+end_src
The position vector corresponds to the first 3 elements.
#+begin_src matlab
P = X(1:3);
#+end_src
The next 3 elements are the orientation of {B} with respect to {A} expressed
using the screw axis.
#+begin_src matlab
theta = norm(X(4:6));
s = X(4:6)/theta;
#+end_src
We then compute the corresponding rotation matrix.
#+begin_src matlab
R = [s(1)^2*(1-cos(theta)) + cos(theta) , s(1)*s(2)*(1-cos(theta)) - s(3)*sin(theta), s(1)*s(3)*(1-cos(theta)) + s(2)*sin(theta);
s(2)*s(1)*(1-cos(theta)) + s(3)*sin(theta), s(2)^2*(1-cos(theta)) + cos(theta), s(2)*s(3)*(1-cos(theta)) - s(1)*sin(theta);
s(3)*s(1)*(1-cos(theta)) - s(2)*sin(theta), s(3)*s(2)*(1-cos(theta)) + s(1)*sin(theta), s(3)^2*(1-cos(theta)) + cos(theta)];
#+end_src