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

828 lines
35 KiB
Org Mode

#+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: \input{preamble.tex}
#+LATEX_HEADER_EXTRA: \input{preamble_extra.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:
** Notes
Prefix is =test_id31=
*Goals*:
- Short stroke metrology
- Complete validation of the concept + nano-hexapod + instrumentation + control system
- Experimental validation of complementary filter control
*Outline*:
- Short stroke metrology system
- Objective: 5DoF measurement while rotation
- Estimation of angular acceptance
- Do not care too much about accuracy here
- Mounting and alignment
- Jacobian etc...
- Plant identification:
- Comparison with Simscape
- Better Rz alignment: use of the model
- Effect of payload mass
- Robust IFF
- High Authority Control
- Classical Loop Shaping:
- Control in the frame of the struts
- Control in the cartesian frame
- Complementary Filter Control
- S/T, noise budget
- Scientific experiments
- Tomography
- Lateral Ty scans
- Dirty layer scans
- Reflectivity
- Diffraction tomography
** CANC [#C] Find identification where Rz was not taken into account
CLOSED: [2024-11-12 Tue 16:03]
- State "CANC" from "TODO" [2024-11-12 Tue 16:03]
- Much larger coupling than estimated from the model
- In the model, suppose Rz = 0?
- Then how to estimate Rz?
- From voltages
- From encoders
- Results and comparison with model
*Maybe not talk about this here as it is not too interesting*
* Introduction :ignore:
Now that the nano-hexapod is mounted and that a good multi-body model of the nano-hexapod
The system is validated on the ID31 beamline.
At the beginning of the project, it was planned to develop a long stroke 5-DoF metrology system to measure the pose of the sample with respect to the granite.
The development of such system was complex, and was not completed at the time of the experimental tests on ID31.
To still validate the developed nano active platform and the associated instrumentation and control architecture, a 5-DoF short stroke metrology system was developed (Section ref:sec:test_id31_metrology).
The identify dynamics of the nano-hexapod fixed on top of the micro-station was identified for different experimental conditions (payload masses, rotational velocities) and compared with the model (Section ref:sec:test_id31_open_loop_plant).
Decentralized Integral Force Feedback is then applied to actively damp the plant in a robust way (Section ref:sec:test_id31_iff).
High authority control is then applied
#+name: fig:test_id31_micro_station_nano_hexapod
#+caption: Picture of the micro-station without the nano-hexapod (\subref{fig:test_id31_micro_station_cables}) and with the nano-hexapod (\subref{fig:test_id31_fixed_nano_hexapod})
#+attr_latex: :options [htbp]
#+begin_figure
#+attr_latex: :caption \subcaption{\label{fig:test_id31_micro_station_cables}Micro-station and nano-hexapod cables}
#+attr_latex: :options {0.49\textwidth}
#+begin_subfigure
#+attr_latex: :width 0.95\linewidth
[[file:figs/test_id31_micro_station_cables.jpg]]
#+end_subfigure
#+attr_latex: :caption \subcaption{\label{fig:test_id31_fixed_nano_hexapod}Nano-hexapod fixed on top of the micro-station}
#+attr_latex: :options {0.49\textwidth}
#+begin_subfigure
#+attr_latex: :width 0.95\linewidth
[[file:figs/test_id31_fixed_nano_hexapod.jpg]]
#+end_subfigure
#+end_figure
* Short Stroke Metrology System
<<sec:test_id31_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 was not developed yet, a stroke stroke ($> 100\,\mu m^3$) was used instead to validate the nano-hexapod control.
A first considered option was to use the "Spindle error analyzer" shown in Figure ref:fig:test_id31_lion.
This system comprises 5 capacitive sensors which are facing two reference spheres.
As the gap between the capacitive sensors and the spheres is very small[fn:1], the risk of damaging the spheres and the capacitive sensors is high.
#+name: fig:test_id31_short_stroke_metrology
#+caption: Short stroke metrology system used to measure the sample position with respect to the granite in 5DoF. The system is based on a "Spindle error analyzer" (\subref{fig:test_id31_lion}), but the capacitive sensors are replaced with fibered interferometers (\subref{fig:test_id31_interf}). Interferometer heads are shown in (\subref{fig:test_id31_interf_head})
#+attr_latex: :options [htbp]
#+begin_figure
#+attr_latex: :caption \subcaption{\label{fig:test_id31_lion}Capacitive Sensors}
#+attr_latex: :options {0.33\textwidth}
#+begin_subfigure
#+attr_latex: :width 0.9\linewidth
[[file:figs/test_id31_lion.jpg]]
#+end_subfigure
#+attr_latex: :caption \subcaption{\label{fig:test_id31_interf}Short-Stroke metrology}
#+attr_latex: :options {0.33\textwidth}
#+begin_subfigure
#+attr_latex: :width 0.9\linewidth
[[file:figs/test_id31_interf.jpg]]
#+end_subfigure
#+attr_latex: :caption \subcaption{\label{fig:test_id31_interf_head}Interferometer head}
#+attr_latex: :options {0.33\textwidth}
#+begin_subfigure
#+attr_latex: :width 0.9\linewidth
[[file:figs/test_id31_interf_head.jpg]]
#+end_subfigure
#+end_figure
Instead of using capacitive sensors, 5 fibered interferometers were used in a similar way (Figure ref:fig:test_id31_interf).
At the end of each fiber, a sensor head[fn:2] (Figure ref:fig:test_id31_interf_head) is used, which consists of a lens precisely positioned with respect to the fiber's end.
The lens is focusing the light on the surface of the sphere, such that it comes back to the fiber and produces an interference.
This way, the gap between the sensor and the reference sphere is much larger (here around $40\,mm$), removing the risk of collision.
Nevertheless, the metrology system still has limited measurement range, as when the spheres are moving perpendicularly to the beam axis, the reflected light does not coincide with the incident light, and for some perpendicular displacement, the interference is too small to be detected.
** 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
** Metrology Kinematics
<<ssec:test_id31_metrology_kinematics>>
The developed short-stroke metrology system is schematically shown in Figure ref:fig:test_id31_metrology_kinematics.
The point of interest is indicated by the blue frame $\{B\}$, which is located $H = 150\,mm$ above the nano-hexapod's top platform.
The spheres have a diameter $d = 25.4\,mm$, and indicated dimensions are $l_1 = 60\,mm$ and $l_2 = 16.2\,mm$.
In order to compute the pose of the $\{B\}$ frame with respect to the granite (i.e. with respect to the fixed interferometer heads), the measured small displacements $[d_1,\ d_2,\ d_3,\ d_4,\ d_5]$ by the interferometers are first written as a function of the small linear and angular motion of the $\{B\}$ frame $[D_x,\ D_y,\ D_z,\ R_x,\ R_y]$ eqref:eq:test_id31_metrology_kinematics.
\begin{equation}\label{eq:test_id31_metrology_kinematics}
d_1 = D_y - l_2 R_x, \quad d_2 = D_y + l_1 R_x, \quad d_3 = -D_x - l_2 R_y, \quad d_4 = -D_x + l_1 R_y, \quad d_5 = -D_z
\end{equation}
#+attr_latex: :options [b]{0.48\linewidth}
#+begin_minipage
#+name: fig:test_id31_metrology_kinematics
#+caption: Schematic of the measurement system. Measured distances are indicated by red arrows.
#+attr_latex: :scale 1 :float nil
[[file:figs/test_id31_metrology_kinematics.png]]
#+end_minipage
\hfill
#+attr_latex: :options [b]{0.48\linewidth}
#+begin_minipage
#+name: fig:align_top_sphere_comparators
#+attr_latex: :width \linewidth :float nil
#+caption: The top sphere is aligned with the rotation axis of the spindle using two probes.
[[file:figs/test_id31_align_top_sphere_comparators.jpg]]
#+end_minipage
The five equations eqref:eq:test_id31_metrology_kinematics can be written in a matrix form, and then inverted to have the pose of $\{B\}$ frame as a linear combination of the measured five distances by the interferometers eqref:eq:test_id31_metrology_kinematics_inverse.
\begin{equation}\label{eq:test_id31_metrology_kinematics_inverse}
\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
%% Geometrical parameters of the metrology system
H = 150e-3;
l1 = (150-48-42)*1e-3;
l2 = (76.2+48+42-150)*1e-3;
% Computation of the 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
** Rough alignment of the reference spheres
<<ssec:test_id31_metrology_sphere_rought_alignment>>
The two reference spheres are aligned with the rotation axis of the spindle.
To do so, two measuring probes are used as shown in Figure ref:fig:align_top_sphere_comparators.
To not damage the sensitive sphere surface, the probes are instead positioned on the cylinder on which the sphere is mounted.
First, the probes are fixed to the bottom (fixed) cylinder to align its axis with the spindle axis.
Then, the probes are fixed to the top (adjustable) cylinder, and the same alignment is performed.
With this setup, the precision of the alignment of both sphere better with the spindle axis is expected to limited to $\approx 10\,\mu m$.
This is probably limited due to the poor coaxiality between the cylinders and the spheres.
However, the alignment precision should be enough to stay in the acceptance of the interferometers.
** Tip-Tilt adjustment of the interferometers
<<ssec:test_id31_metrology_alignment>>
The short stroke metrology system is placed on top of the main granite using a gantry made of granite blocs to have good vibration and thermal stability (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 0.8\linewidth
[[file:figs/test_id31_short_stroke_metrology_overview.jpg]]
The interferometers need to be aligned with respect to the two reference spheres to approach as much as possible the ideal case shown in Figure ref:fig:test_id31_metrology_kinematics.
The vertical position of the spheres is adjusted using the micro-hexapod to match the height of the interferometers.
Then, the horizontal position of the gantry is adjusted such that the coupling efficiency (i.e. the intensity of the light reflected back in the fiber) of the top interferometer is maximized.
This is equivalent as to optimize the perpendicularity between the interferometer beam and the sphere surface (i.e. the concentricity between the beam and the sphere center).
The lateral sensor heads (i.e. all except the top one), which are each fixed to a custom tip-tilt adjustment mechanism, are individually oriented such that the coupling efficient is maximized.
** Fine Alignment of reference spheres using interferometers
<<ssec:test_id31_metrology_sphere_fine_alignment>>
Thanks to the good alignment of the two reference spheres with the spindle axis and to the fine adjustment of the interferometers orientations, the interferometer measurement is made possible during complete spindle rotation.
This metrology and therefore be used to better align the axis defined by the two spheres' center with the spindle axis.
The alignment process is made by few iterations.
First, the spindle is scanned and the alignment errors are recorded.
From the errors, the motion of the micro-hexapod to better align the spheres is determined and the micro-hexapod is moved.
Then, the spindle is scanned again, and the new alignment errors are recorded.
This iterative process is first perform for angular errors (Figure ref:fig:test_id31_metrology_align_rx_ry) and then for lateral errors (Figure ref:fig:test_id31_metrology_align_dx_dy).
Remaining error after alignment is in the order of $\pm5\,\mu\text{rad}$ for angular errors, $\pm 1\,\mu m$ laterally and less than $0.1\,\mu m$ vertically.
#+begin_src matlab
%% Angular alignment
% 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);
% 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);
% 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('$R_0 = %.0f \\mu$rad', R_it0))
plot(1e6*data_it1.Rx_int_filtered, 1e6*data_it1.Ry_int_filtered, '-', ...
'DisplayName', sprintf('$R_1 = %.0f \\mu$rad', R_it1))
plot(1e6*data_it2.Rx_int_filtered, 1e6*data_it2.Ry_int_filtered, '-', 'color', colors(5,:), ...
'DisplayName', sprintf('$R_2 = %.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);
xlim([-600, 300]);
ylim([-100, 800]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file none
exportFig('figs/test_id31_metrology_align_rx_ry.pdf', 'width', 'half', 'height', 'normal');
#+end_src
#+begin_src matlab
%% Eccentricity alignment
% Load Data
data_it0 = h5scan(data_dir, 'alignment', 'h1rx_h1ry_0002', 5);
data_it1 = h5scan(data_dir, 'alignment', 'h1dx_h1dy', 1);
% 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);
% 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('$R_0 = %.0f \\mu$m', R_it0))
plot(1e6*data_it1.Dx_int_filtered, 1e6*data_it1.Dy_int_filtered, '-', ...
'DisplayName', sprintf('$R_1 = %.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);
xlim([-1, 21]);
ylim([-8, 14]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/test_id31_metrology_align_dx_dy.pdf', 'width', 'half', 'height', 'normal');
#+end_src
#+name: fig:test_id31_metrology_align
#+caption: Measured angular (\subref{fig:test_id31_metrology_align_rx_ry}) and lateral (\subref{fig:test_id31_metrology_align_dx_dy}) errors during a full spindle rotation. Between two rotations, the micro-hexapod is adjusted to better align the two spheres with the rotation axis.
#+attr_latex: :options [htbp]
#+begin_figure
#+attr_latex: :caption \subcaption{\label{fig:test_id31_metrology_align_rx_ry}Angular alignment}
#+attr_latex: :options {0.49\textwidth}
#+begin_subfigure
#+attr_latex: :scale 1
[[file:figs/test_id31_metrology_align_rx_ry.png]]
#+end_subfigure
#+attr_latex: :caption \subcaption{\label{fig:test_id31_metrology_align_dx_dy}Lateral alignment}
#+attr_latex: :options {0.49\textwidth}
#+begin_subfigure
#+attr_latex: :scale 1
[[file:figs/test_id31_metrology_align_dx_dy.png]]
#+end_subfigure
#+end_figure
** Estimated measurement volume
<<ssec:test_id31_metrology_acceptance>>
Because the interferometers are pointing to spheres and not flat surfaces, the lateral acceptance is limited.
In order to estimate the metrology acceptance, the micro-hexapod is used to perform three accurate scans of $\pm 1\,mm$, respectively along the the $x$, $y$ and $z$ axes.
During these scans, the 5 interferometers are recorded, and the ranges in which each interferometer has enough coupling efficiency for measurement are estimated.
Results are summarized in Table ref:tab:test_id31_metrology_acceptance.
The obtained lateral acceptance for pure displacements in any direction is estimated to be around $+/-0.5\,mm$, which is enough for the current application as it is well above the micro-station errors to be actively corrected.
#+begin_src matlab
%% Estimated acceptance of the metrology
% This is estimated by moving the spheres using the micro-hexapod
% Dx
data_dx = h5scan(data_dir, 'metrology_acceptance_new_align', 'dx', 1);
dx_acceptance = zeros(5,1);
for i = [1:size(dx_acceptance, 1)]
% Find range in which the interferometers are measuring displacement
dx_di = diff(data_dx.(sprintf('d%i', i))) == 0;
if sum(dx_di) > 0
dx_acceptance(i) = data_dx.h1tx(find(dx_di(501:end), 1) + 500) - ...
data_dx.h1tx(find(flip(dx_di(1:500)), 1));
else
dx_acceptance(i) = data_dx.h1tx(end) - data_dx.h1tx(1);
end
end
% Dy
data_dy = h5scan(data_dir, 'metrology_acceptance_new_align', 'dy', 1);
dy_acceptance = zeros(5,1);
for i = [1:size(dy_acceptance, 1)]
% Find range in which the interferometers are measuring displacement
dy_di = diff(data_dy.(sprintf('d%i', i))) == 0;
if sum(dy_di) > 0
dy_acceptance(i) = data_dy.h1ty(find(dy_di(501:end), 1) + 500) - ...
data_dy.h1ty(find(flip(dy_di(1:500)), 1));
else
dy_acceptance(i) = data_dy.h1ty(end) - data_dy.h1ty(1);
end
end
% Dz
data_dz = h5scan(data_dir, 'metrology_acceptance_new_align', 'dz', 1);
dz_acceptance = zeros(5,1);
for i = [1:size(dz_acceptance, 1)]
% Find range in which the interferometers are measuring displacement
dz_di = diff(data_dz.(sprintf('d%i', i))) == 0;
if sum(dz_di) > 0
dz_acceptance(i) = data_dz.h1tz(find(dz_di(501:end), 1) + 500) - ...
data_dz.h1tz(find(flip(dz_di(1:500)), 1));
else
dz_acceptance(i) = data_dz.h1tz(end) - data_dz.h1tz(1);
end
end
#+end_src
#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*)
% data2orgtable([dx_acceptance, dy_acceptance, dz_acceptance], {'$d_1$ (y)', '$d_2$ (y)', '$d_3$ (x)', '$d_4$ (x)', '$d_5$ (z)'}, {'$D_x$', '$D_y$', '$D_z$'}, ' %.2f ');
#+end_src
#+name: tab:test_id31_metrology_acceptance
#+caption: Estimated measurement range for each interferometer, and for three different directions.
#+attr_latex: :environment tabularx :width 0.45\linewidth :align Xccc
#+attr_latex: :center t :booktabs t
#+RESULTS:
| | $D_x$ | $D_y$ | $D_z$ |
|-----------+-------------+------------+-------|
| $d_1$ (y) | $1.0\,mm$ | $>2\,mm$ | $1.35\,mm$ |
| $d_2$ (y) | $0.8\,mm$ | $>2\,mm$ | $1.01\,mm$ |
| $d_3$ (x) | $>2\,mm$ | $1.06\,mm$ | $1.38\,mm$ |
| $d_4$ (x) | $>2\,mm$ | $0.99\,mm$ | $0.94\,mm$ |
| $d_5$ (z) | $1.33\, mm$ | $1.06\,mm$ | $>2\,mm$ |
** Estimated measurement errors
<<ssec:test_id31_metrology_errors>>
When using the NASS, the accuracy of the sample's positioning is linked to the accuracy of the external metrology.
However, to validate the nano-hexapod with the associated instrumentation and control architecture, the accuracy of the metrology is not an issue.
Only the bandwidth and noise characteristics of the external metrology are important.
Yet, some elements effecting the accuracy of the metrology are discussed here.
First, the "metrology kinematics" (discussed in Section ref:ssec:test_id31_metrology_kinematics) is only approximate (i.e. valid for very small displacements).
This can be seen when performing lateral $[D_x,\,D_y]$ scans using the micro-hexapod while recording the vertical interferometer (Figure ref:fig:test_id31_xy_map_sphere).
As the interferometer is pointing to a sphere and not to a plane, lateral motion of the sphere is seen as a vertical motion by the top interferometer.
Then, the reference spheres have some deviations with respect to an ideal sphere.
They are meant to be used with capacitive sensors which are integrating the shape errors over large surfaces.
When using interferometers, the size of the "light spot" on the sphere surface is a circle with a diameter $\approx 50\,\mu m$, therefore the system is more sensitive to shape errors with small features.
As the interferometer light is travelling in air, the measured distance is sensitive to any variation in the refractive index of the air.
Therefore, any variation of air temperature, pressure or humidity will induce measurement errors.
For a measurement length of $40\,mm$, a temperature variation of $0.1\,{}^oC$ induces an errors in the distance measurement of $\approx 4\,nm$.
Finally, even in vacuum and in the absence of target motion, the interferometers are affected by noise [[cite:&watchi18_review_compac_inter]].
The effect of the noise on the translation and rotation measurements is estimated in Figure ref:fig:test_id31_interf_noise.
#+begin_src matlab
%% Interferometer noise estimation
data = load("test_id31_interf_noise.mat");
Ts = 1e-4;
Nfft = floor(5/Ts);
win = hanning(Nfft);
Noverlap = floor(Nfft/2);
[pxx_int, f] = pwelch(detrend(data.d, 0), win, Noverlap, Nfft, 1/Ts);
% Uncorrelated noise: square root of the sum of the squares
pxx_cart = pxx_int*sum(inv(Hm).^2, 2)';
rms_dxy = sqrt(trapz(f(f>1), pxx_cart((f>1),1))); % < 0.3 nm RMS
rms_dz = sqrt(trapz(f(f>1), pxx_cart((f>1),3))); % < 0.3 nm RMS
rms_rxy = sqrt(trapz(f(f>1), pxx_cart((f>1),4))); % 5 nrad RMS
#+end_src
#+begin_src matlab
figure;
hold on;
plot(f, sqrt(pxx_cart(:,1)), 'DisplayName', sprintf('$D_{x,y}$, %.1f nmRMS', rms_dxy));
plot(f, sqrt(pxx_cart(:,3)), 'DisplayName', sprintf('$D_{z}$, %.1f nmRMS', rms_dz));
plot(f, sqrt(pxx_cart(:,4)), 'DisplayName', sprintf('$R_{x,y}$, %.1f nradRMS', rms_rxy));
set(gca, 'xscale', 'log'); set(gca, 'yscale', 'log');
xlabel('Frequency [Hz]'); ylabel('ASD $\left[\frac{nm,\ nrad}{\sqrt{Hz}}\right]$')
xlim([1, 1e3]); ylim([1e-3, 1]);
leg = legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1);
leg.ItemTokenSize(1) = 15;
#+end_src
#+begin_src matlab :tangle no :exports results :results file none
exportFig('figs/test_id31_interf_noise.pdf', 'width', 'half', 'height', 'normal');
#+end_src
#+begin_src matlab
%% X-Y scan with the micro-hexapod, and record of the vertical interferometer
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);
% Fit a sphere to the data
[sphere_center,sphere_radius] = sphereFit(1e-3*[x, y, z]);
#+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('$D_x$ [$\mu$m]');
ylabel('$D_y$ [$\mu$m]');
zlabel('$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 none
exportFig('figs/test_id31_xy_map_sphere.pdf', 'width', 'half', 'height', 'normal');
#+end_src
#+name: fig:test_id31_metrology_errors
#+caption: Estimated measurement errors of the metrology. Cross-coupling between lateral motion and vertical measurement is shown in (\subref{fig:test_id31_xy_map_sphere}). Effect of interferometer noise on the measured translations and rotations is shown in (\subref{fig:test_id31_interf_noise}).
#+attr_latex: :options [htbp]
#+begin_figure
#+attr_latex: :caption \subcaption{\label{fig:test_id31_xy_map_sphere}Z measurement during an XY mapping}
#+attr_latex: :options {0.49\textwidth}
#+begin_subfigure
#+attr_latex: :width 0.95\linewidth
[[file:figs/test_id31_xy_map_sphere.png]]
#+end_subfigure
#+attr_latex: :caption \subcaption{\label{fig:test_id31_interf_noise}Interferometer noise}
#+attr_latex: :options {0.49\textwidth}
#+begin_subfigure
#+attr_latex: :width 0.95\linewidth
[[file:figs/test_id31_interf_noise.png]]
#+end_subfigure
#+end_figure
* Bibliography :ignore:
#+latex: \printbibliography[heading=bibintoc,title={Bibliography}]
* Helping Functions :noexport:
** Initialize Path
#+NAME: m-init-path
#+BEGIN_SRC matlab
addpath('./matlab/'); % Path for scripts
%% Path for functions, data and scripts
addpath('./matlab/mat/'); % Path for Computed FRF
addpath('./matlab/src/'); % Path for functions
addpath('./matlab/STEPS/'); % Path for STEPS
addpath('./matlab/subsystems/'); % Path for Subsystems Simulink files
%% Data directory
data_dir = './matlab/mat/'
#+END_SRC
#+NAME: m-init-path-tangle
#+BEGIN_SRC matlab
%% Path for functions, data and scripts
addpath('./mat/'); % Path for Data
addpath('./src/'); % Path for functions
addpath('./STEPS/'); % Path for STEPS
addpath('./subsystems/'); % Path for Subsystems Simulink files
%% Data directory
data_dir = './mat/'
#+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:
#+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
** =sphereFit= - Fit sphere from x,y,z points
:PROPERTIES:
:header-args:matlab+: :tangle matlab/src/sphereFit.m
:header-args:matlab+: :comments none :mkdirp yes :eval no
:END:
#+begin_src matlab
function [Center,Radius] = sphereFit(X)
% this fits a sphere to a collection of data using a closed form for the
% solution (opposed to using an array the size of the data set).
% Minimizes Sum((x-xc)^2+(y-yc)^2+(z-zc)^2-r^2)^2
% x,y,z are the data, xc,yc,zc are the sphere's center, and r is the radius
% Assumes that points are not in a singular configuration, real numbers, ...
% if you have coplanar data, use a circle fit with svd for determining the
% plane, recommended Circle Fit (Pratt method), by Nikolai Chernov
% http://www.mathworks.com/matlabcentral/fileexchange/22643
% Input:
% X: n x 3 matrix of cartesian data
% Outputs:
% Center: Center of sphere
% Radius: Radius of sphere
% Author:
% Alan Jennings, University of Dayton
A=[mean(X(:,1).*(X(:,1)-mean(X(:,1)))), ...
2*mean(X(:,1).*(X(:,2)-mean(X(:,2)))), ...
2*mean(X(:,1).*(X(:,3)-mean(X(:,3)))); ...
0, ...
mean(X(:,2).*(X(:,2)-mean(X(:,2)))), ...
2*mean(X(:,2).*(X(:,3)-mean(X(:,3)))); ...
0, ...
0, ...
mean(X(:,3).*(X(:,3)-mean(X(:,3))))];
A=A+A.';
B=[mean((X(:,1).^2+X(:,2).^2+X(:,3).^2).*(X(:,1)-mean(X(:,1))));...
mean((X(:,1).^2+X(:,2).^2+X(:,3).^2).*(X(:,2)-mean(X(:,2))));...
mean((X(:,1).^2+X(:,2).^2+X(:,3).^2).*(X(:,3)-mean(X(:,3))))];
Center=(A\B).';
Radius=sqrt(mean(sum([X(:,1)-Center(1),X(:,2)-Center(2),X(:,3)-Center(3)].^2,2)));
#+end_src
* Footnotes
[fn:2]M12/F40 model from Attocube
[fn:1]Depending on the measuring range, gap can range from $\approx 1\,\mu m$ to $\approx 100\,\mu m$