2189 lines
96 KiB
Org Mode
2189 lines
96 KiB
Org Mode
#+TITLE: Nano Active Stabilization System - Instrumentation
|
||
: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{nass-instrumentation.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:
|
||
|
||
#+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 =detail_instrumentation=
|
||
|
||
Compilation of the following reports:
|
||
- [ ] [[file:~/Cloud/work-projects/ID31-NASS/matlab/test-bench-PD200/test-bench-pd200.org][test-bench-PD200]] and [[file:~/Cloud/work-projects/ID31-NASS/matlab/test-bench-piezo-amplifiers/index.org][test-bench-piezo-amplifiers]] (but less useful)
|
||
- [ ] [[file:~/Cloud/work-projects/ID31-NASS/matlab/test-bench-vionic/test-bench-vionic.org][test-bench-vionic]]
|
||
- [ ] [[file:~/Cloud/work-projects/ID31-NASS/matlab/test-bench-attocube/test-bench-attocube.org][test-bench-attocube]]
|
||
- [ ] [[file:~/Cloud/work-projects/ID31-NASS/matlab/nass-simscape/org/noise_budgeting.org][file:~/Cloud/work-projects/ID31-NASS/matlab/nass-simscape/org/noise_budgeting.org]] for the maximum allowed noise of the relative motion sensors
|
||
- [ ] DAC, ADC, Control system
|
||
- [ ] Reading of the force sensor: ADC + [[file:~/Cloud/work-projects/ID31-NASS/matlab/test-bench-force-sensor/test-bench-force-sensor.org][test-bench-force-sensor]]
|
||
Electrical characteristics of the APA force sensor
|
||
|
||
** DONE [#A] Noise budgeting to have specifications
|
||
CLOSED: [2025-03-17 Mon 14:51]
|
||
|
||
Simplified model (uniaxial?)
|
||
From maximum induced vibration, and estimated bandwidth (S and T), estimate the maximum:
|
||
- Actuator noise (i.e DAC + voltage amplifier)
|
||
- force sensor noise (i.e. ADC) + encoder noise?
|
||
- external metrology noise? (not very relevant, maybe this should be assumed, similar to Attocube noise [[file:~/Cloud/work-projects/ID31-NASS/matlab/test-bench-attocube/test-bench-attocube.org][test-bench-attocube]])
|
||
|
||
=> *Specifications*
|
||
|
||
And specifications in terms of bandwidth, voltage ranges, etc...
|
||
|
||
- Most stringent requirement: vertical vibrations below 15nm RMS
|
||
- Because of system symmetry, only one strut is considered
|
||
It is suppose that the vibrations induced by each strut is uncorrelated
|
||
combination of 6 actuators with uncorrelated noise => x2.5
|
||
- Therefore, each actuator should induce less than 15/2.5=6nm RMS of vibration in the vertical direction
|
||
|
||
3 sources of noise:
|
||
- ADC
|
||
- DAC
|
||
- voltage amplifier
|
||
|
||
Suppose uncorrelated noise: each should be sqrt(3) lower => 3.5 nm RMS max for each source
|
||
|
||
|
||
From noise ASD of elements => easy to compute the effect in nm RMS on the output (in closed-loop).
|
||
|
||
Ideally, we would like to have the specifications in terms of ASD for each element.
|
||
We can suppose the noise ASD to be flat with frequency and find the maximum value to reach x nm RMS => this is our specifications.
|
||
|
||
|
||
*We need inputs and outputs to be in volts for the NASS plant !*
|
||
- use 2DoF model of APA and 3 and 4 DoF model of the flexible joints
|
||
|
||
** DONE [#B] Verify that the specs are correct
|
||
CLOSED: [2025-03-17 Mon 14:51]
|
||
|
||
[[*Output Voltage Noise][Output Voltage Noise]]
|
||
|
||
Are we considering output voltage noise for the specs?
|
||
|
||
** DONE [#A] Make schematic with the plant and all the instrumentation
|
||
CLOSED: [2025-02-27 Thu 14:25]
|
||
|
||
#+begin_src latex :file detail_instrumentation_plant.pdf
|
||
\begin{tikzpicture}
|
||
% Blocs
|
||
\node[block={2.0cm}{2.0cm}, align=center] (plant) {NASS};
|
||
|
||
\coordinate[] (inputVa) at ($(plant.south west)!0.5!(plant.north west)$);
|
||
\coordinate[] (outputVs) at ($(plant.south east)!0.7!(plant.north east)$);
|
||
\coordinate[] (outputde) at ($(plant.south east)!0.3!(plant.north east)$);
|
||
|
||
\node[block={1.0cm}{1.0cm}, left=0.8 of inputVa] (ampl_tf) {$G_{\text{ampl}}$};
|
||
\node[addb={+}{}{}{}{}, left=0.4 of ampl_tf] (ampl_noise) {};
|
||
|
||
\node[addb={+}{}{}{}{}, left=0.8 of ampl_noise] (dac_noise) {};
|
||
\node[DAC, left=0.4 of dac_noise] (dac_tf) {};
|
||
|
||
\node[addb={+}{}{}{}{}, left=1.0 of dac_tf] (iff_sum) {};
|
||
|
||
\node[block={1.0cm}{1.0cm}, above=0.4 of iff_sum] (Kiff) {$\bm{K}_{\text{IFF}}$};
|
||
\node[block={1.0cm}{1.0cm}, left=0.4 of iff_sum] (Khac) {$\bm{K}_{\text{HAC}}$};
|
||
|
||
% \node[block={1.0cm}{1.0cm}, above=0.4 of plant] (Kiff) {$\bm{K}_{\text{IFF}}$};
|
||
% \node[block={1.0cm}{1.0cm}, below=0.4 of plant] (Khac) {$\bm{K}_{\text{HAC}}$};
|
||
|
||
\node[addb={+}{}{}{}{}, right=0.8 of outputVs] (adc_noise) {};
|
||
\node[ADC, right=0.4 of adc_noise] (adc_tf) {};
|
||
|
||
\draw[->] (iff_sum.east) -- node[sloped]{$/$} (dac_tf.west);
|
||
\draw[->] (dac_tf.east) -- (dac_noise.west);
|
||
\draw[->] (dac_noise.east) -- (ampl_noise.west);
|
||
\draw[->] (ampl_noise.east) -- (ampl_tf.west);
|
||
\draw[->] (ampl_tf.east) -- (inputVa)node[above left]{$\bm{V}_a$};
|
||
\draw[->] (outputVs)node[above right]{$\bm{V}_s$} -- (adc_noise.west);
|
||
\draw[->] (adc_noise.east) -- (adc_tf.west);
|
||
\draw[->] (adc_tf.east) -| ++(0.4, 1.8) -| node[near start, sloped]{$/$} (Kiff.north);
|
||
\draw[->] (Kiff.south) -- node[sloped]{$/$} (iff_sum.north);
|
||
\draw[->] (outputde)node[above right]{$\bm{\epsilon}_{\mathcal{L}}$} -| ++(0.6, -1.0) -| node[near start, sloped]{$/$} ($(Khac.west)+(-0.6, 0)$) -- (Khac.west);
|
||
\draw[->] (Khac.east) -- node[sloped]{$/$} (iff_sum.west);
|
||
|
||
\draw[<-] (dac_noise.north) -- ++(0, 0.8)coordinate(dac_noise_input) node[below left]{$n_{\text{ad}}$};
|
||
\draw[<-] (ampl_noise.north) -- ++(0, 0.8)coordinate(ampl_noise_input) node[below right]{$n_{\text{ampl}}$};
|
||
\draw[<-] (adc_noise.north) -- ++(0, 0.8)coordinate(adc_noise_input) node[below right]{$n_{\text{da}}$};
|
||
|
||
\begin{scope}[on background layer]
|
||
\node[fit={(dac_tf.south west) (dac_noise.east|-dac_noise_input)}, fill=colorblue!20!white, draw, dashed, inner sep=4pt] (dac_system) {};
|
||
\node[anchor={north}] at (dac_system.south){$\text{DAC}$};
|
||
\end{scope}
|
||
|
||
\begin{scope}[on background layer]
|
||
\node[fit={(ampl_noise.west|-ampl_tf.south) (ampl_tf.east|-ampl_noise_input)}, fill=colorred!20!white, draw, dashed, inner sep=4pt] (ampl_system) {};
|
||
\node[anchor={north}] at (ampl_system.south){$\text{Amplifier}$};
|
||
\end{scope}
|
||
|
||
\begin{scope}[on background layer]
|
||
\node[fit={(adc_noise.south -| adc_noise.west) (adc_tf.east|-adc_noise_input)}, fill=coloryellow!20!white, draw, dashed, inner sep=4pt] (adc_system) {};
|
||
\node[anchor={north}] at (adc_system.south){$\text{ADC}$};
|
||
\end{scope}
|
||
|
||
\begin{scope}[on background layer]
|
||
\node[fit={(Khac.south west) (Kiff.north east)}, fill=black!20!white, draw, dashed, inner sep=4pt] (control_system) {};
|
||
\node[anchor={north}] at (control_system.south){$\text{RT Controller}$};
|
||
\end{scope}
|
||
\end{tikzpicture}
|
||
#+end_src
|
||
|
||
#+RESULTS:
|
||
[[file:figs/detail_instrumentation_plant.png]]
|
||
|
||
** DONE [#C] Find instrumentation that meet such specification
|
||
CLOSED: [2025-03-17 Mon 14:51]
|
||
|
||
How to read force sensor, etc...
|
||
|
||
** DONE [#C] Characterization of received instruments and compare with specifications
|
||
CLOSED: [2025-03-17 Mon 14:51]
|
||
|
||
Test benches to characterize instruments
|
||
|
||
- [[file:~/Cloud/work-projects/ID31-NASS/matlab/test-bench-PD200/test-bench-pd200.org][test-bench-PD200]]
|
||
- [[file:~/Cloud/work-projects/ID31-NASS/matlab/test-bench-force-sensor/test-bench-force-sensor.org][test-bench-force-sensor]]
|
||
|
||
** DONE [#C] Perform rigorous noise budgeting
|
||
CLOSED: [2025-03-17 Mon 14:51]
|
||
|
||
Sources:
|
||
- ADC: measured
|
||
- PD200: measured
|
||
- Encoder: measured
|
||
- ADC (force sensors): measured
|
||
- Ground motion: estimated
|
||
|
||
By taking into account all those sources, we should be able to do a noise budgeting and compare with the obtain measurements.
|
||
|
||
We need the following transfer functions:
|
||
- from ground motion (x,y,z) to strut motion (1 to 6) => Simscape
|
||
- from DAC voltage to strut motion => identified FRF
|
||
- from PD200 voltage to strut motion => identified FRF
|
||
- from ADC noise to strut motion => identified FRF + Controller (closed-loop)
|
||
|
||
** DONE [#B] Design reasonable IFF and HAC controllers
|
||
CLOSED: [2025-02-27 Thu 16:34]
|
||
|
||
#+begin_src matlab
|
||
%% Identify open-loop plant for IFF
|
||
initializeController('type', 'open-loop');
|
||
% Input/Output definition
|
||
clear io; io_i = 1;
|
||
io(io_i) = linio([mdl, '/Controller'], 1, 'openinput'); io_i = io_i + 1; % Control output [V]
|
||
io(io_i) = linio([mdl, '/NASS'], 3, 'openoutput', [], 'Vs'); io_i = io_i + 1; % Force Sensors Voltages [V]
|
||
|
||
|
||
G_iff = linearize(mdl, io);
|
||
G_iff.InputName = {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'};
|
||
G_iff.OutputName = {'Vs1', 'Vs2', 'Vs3', 'Vs4', 'Vs5', 'Vs6'};
|
||
|
||
%% IFF Controller Design
|
||
% Second order high pass filter
|
||
wz = 2*pi*2;
|
||
xiz = 0.7;
|
||
Ghpf = (s^2/wz^2)/(s^2/wz^2 + 2*xiz*s/wz + 1);
|
||
|
||
Kiff = -200 * ... % Gain
|
||
1/(0.01*2*pi + s) * ... % LPF: provides integral action
|
||
Ghpf * ... % 2nd order HPF (limit low frequency gain)
|
||
eye(6); % Diagonal 6x6 controller (i.e. decentralized)
|
||
|
||
Kiff.InputName = {'fm1', 'fm2', 'fm3', 'fm4', 'fm5', 'fm6'};
|
||
Kiff.OutputName = {'f1', 'f2', 'f3', 'f4', 'f5', 'f6'};
|
||
|
||
%% Root Locus for the Decentralized IFF controller - 1kg Payload
|
||
gains = logspace(-1, 1, 200);
|
||
|
||
figure;
|
||
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
|
||
nexttile();
|
||
hold on;
|
||
|
||
plot(real(pole(G_iff)), imag(pole(G_iff)), 'x', 'color', colors(1,:), ...
|
||
'DisplayName', '$g = 0$');
|
||
plot(real(tzero(G_iff)), imag(tzero(G_iff)), 'o', 'color', colors(1,:), ...
|
||
'HandleVisibility', 'off');
|
||
|
||
for g = gains
|
||
clpoles = pole(feedback(G_iff, g*Kiff, +1));
|
||
plot(real(clpoles), imag(clpoles), '.', 'color', colors(1,:), ...
|
||
'HandleVisibility', 'off');
|
||
end
|
||
|
||
% Optimal gain
|
||
clpoles = pole(feedback(G_iff, Kiff, +1));
|
||
plot(real(clpoles), imag(clpoles), 'kx', ...
|
||
'DisplayName', '$g_{opt}$');
|
||
|
||
xline(0);
|
||
yline(0);
|
||
hold off;
|
||
axis equal;
|
||
xlim([-900, 100]); ylim([-100, 900]);
|
||
xticks([-900:100:0]);
|
||
yticks([0:100:900]);
|
||
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
|
||
xlabel('Real part'); ylabel('Imaginary part');
|
||
|
||
%% Identify Damped plant
|
||
initializeController('type', 'iff');
|
||
|
||
% Input/Output definition
|
||
clear io; io_i = 1;
|
||
io(io_i) = linio([mdl, '/Controller'], 1, 'openinput'); io_i = io_i + 1; % Control output [V]
|
||
io(io_i) = linio([mdl, '/Tracking Error'], 1, 'openoutput', [], 'EdL'); io_i = io_i + 1; % Position Errors [m]
|
||
|
||
|
||
G_hac = linearize(mdl, io);
|
||
G_hac.InputName = {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'};
|
||
G_hac.OutputName = {'eL1', 'eL2', 'eL3', 'eL4', 'eL5', 'eL6'};
|
||
|
||
%% HAC Design
|
||
% Wanted crossover
|
||
wc = 2*pi*10; % [rad/s]
|
||
|
||
% Integrator
|
||
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/80);
|
||
|
||
% Gain to have unitary crossover at wc
|
||
H_gain = 1./abs(evalfr(G_hac(1,1), 1j*wc));
|
||
|
||
% Decentralized HAC
|
||
Khac = -5e4 * ... % Gain
|
||
H_int * ... % Integrator
|
||
H_lead * ... % Low Pass filter
|
||
H_lpf * ... % Low Pass filter
|
||
eye(6); % 6x6 Diagonal
|
||
#+end_src
|
||
|
||
* Introduction :ignore:
|
||
|
||
The goal is to show that each element in the system has been properly chosen based on certain requirements.
|
||
|
||
In order to determine the maximum noise of each instrumentation, a dynamic error budgeting is performed in Section ref:sec:instrumentation_dynamic_error_budgeting.
|
||
|
||
The required instrumentation are then selected based on obtained noise specifications and other requirements summarized in Section ref:sec:detail_instrumentation_choice.
|
||
|
||
The received instrumentation are characterized in Section ref:sec:detail_instrumentation_characterization.
|
||
|
||
- Say the the real time controller is a Speedgoat machine, as it is the standard real time controller used at the ESRF
|
||
|
||
#+begin_src latex :file detail_instrumentation_plant.pdf
|
||
\begin{tikzpicture}
|
||
% Blocs
|
||
\node[block={2.0cm}{2.0cm}, align=center] (plant) {NASS};
|
||
|
||
\coordinate[] (inputVa) at ($(plant.south west)!0.5!(plant.north west)$);
|
||
\coordinate[] (outputVs) at ($(plant.south east)!0.7!(plant.north east)$);
|
||
\coordinate[] (outputde) at ($(plant.south east)!0.3!(plant.north east)$);
|
||
|
||
\node[addb={+}{}{}{}{}, left=0.8 of inputVa] (ampl_noise) {};
|
||
\node[block={1.0cm}{1.0cm}, left=0.4 of ampl_noise] (ampl_tf) {$G_{\text{ampl}}$};
|
||
|
||
\node[addb={+}{}{}{}{}, left=0.8 of ampl_tf] (dac_noise) {};
|
||
\node[DAC, left=0.4 of dac_noise] (dac_tf) {};
|
||
|
||
\node[addb={+}{}{}{}{}, left=1.0 of dac_tf] (iff_sum) {};
|
||
|
||
\node[block={1.0cm}{1.0cm}, above=0.4 of iff_sum] (Kiff) {$\bm{K}_{\text{IFF}}$};
|
||
\node[block={1.0cm}{1.0cm}, left=0.4 of iff_sum] (Khac) {$\bm{K}_{\text{HAC}}$};
|
||
|
||
\node[addb={+}{}{}{}{}, right=0.8 of outputVs] (adc_noise) {};
|
||
\node[ADC, right=0.4 of adc_noise] (adc_tf) {};
|
||
|
||
\draw[->] (iff_sum.east) --node[midway, above]{$\bm{u}$} node[near start, sloped]{$/$} (dac_tf.west);
|
||
\draw[->] (dac_tf.east) -- (dac_noise.west);
|
||
\draw[->] (dac_noise.east) -- (ampl_tf.west);
|
||
\draw[->] (ampl_tf.east) -- (ampl_noise.west);
|
||
\draw[->] (ampl_noise.east) -- (inputVa)node[above left]{$\bm{V}_a$};
|
||
\draw[->] (outputVs)node[above right]{$\bm{V}_s$} -- (adc_noise.west);
|
||
\draw[->] (adc_noise.east) -- (adc_tf.west);
|
||
\draw[->] (adc_tf.east) -| ++(0.4, 1.8) -| node[near start, sloped]{$/$} (Kiff.north);
|
||
\draw[->] (Kiff.south) -- node[sloped]{$/$} (iff_sum.north);
|
||
\draw[->] (outputde)node[above right]{$\bm{\epsilon}_{\mathcal{L}}$} -| ++(0.6, -1.0) -| node[near start, sloped]{$/$} ($(Khac.west)+(-0.6, 0)$) -- (Khac.west);
|
||
\draw[->] (Khac.east) -- node[sloped]{$/$} (iff_sum.west);
|
||
|
||
\draw[<-] (dac_noise.north) -- ++(0, 0.8)coordinate(dac_noise_input) node[below left]{$n_{\text{da}}$};
|
||
\draw[<-] (ampl_noise.north) -- ++(0, 0.8)coordinate(ampl_noise_input) node[below left]{$n_{\text{amp}}$};
|
||
\draw[<-] (adc_noise.north) -- ++(0, 0.8)coordinate(adc_noise_input) node[below right]{$n_{\text{ad}}$};
|
||
|
||
\begin{scope}[on background layer]
|
||
\node[fit={(dac_tf.south west) (dac_noise.east|-dac_noise_input)}, fill=colorblue!20!white, draw, dashed, inner sep=4pt] (dac_system) {};
|
||
\node[anchor={north}] at (dac_system.south){$\text{DAC}$};
|
||
\end{scope}
|
||
|
||
\begin{scope}[on background layer]
|
||
\node[fit={(ampl_tf.south west) (ampl_noise.east|-ampl_noise_input)}, fill=colorred!20!white, draw, dashed, inner sep=4pt] (ampl_system) {};
|
||
\node[anchor={north}] at (ampl_system.south){$\text{Amplifier}$};
|
||
\end{scope}
|
||
|
||
\begin{scope}[on background layer]
|
||
\node[fit={(adc_noise.south -| adc_noise.west) (adc_tf.east|-adc_noise_input)}, fill=coloryellow!20!white, draw, dashed, inner sep=4pt] (adc_system) {};
|
||
\node[anchor={north}] at (adc_system.south){$\text{ADC}$};
|
||
\end{scope}
|
||
|
||
\begin{scope}[on background layer]
|
||
\node[fit={(Khac.south west) (Kiff.north east)}, fill=black!20!white, draw, dashed, inner sep=4pt] (control_system) {};
|
||
\node[anchor={north}] at (control_system.south){$\text{RT Controller}$};
|
||
\end{scope}
|
||
\end{tikzpicture}
|
||
#+end_src
|
||
|
||
#+name: fig:detail_instrumentation_plant
|
||
#+caption: Block diagram of the NASS with considered instrumentation
|
||
#+RESULTS:
|
||
[[file:figs/detail_instrumentation_plant.png]]
|
||
|
||
* Dynamic Error Budgeting
|
||
:PROPERTIES:
|
||
:HEADER-ARGS:matlab+: :tangle matlab/detail_instrumentation_1_dynamic_error_budgeting.m
|
||
:END:
|
||
<<sec:detail_instrumentation_dynamic_error_budgeting>>
|
||
** Introduction :ignore:
|
||
|
||
*Goal*:
|
||
- Obtain specifications regarding the maximum noise of instrumentation (ADC, DAC and voltage amplifier)
|
||
such that it induces acceptable vibrations levels
|
||
|
||
*Procedure*:
|
||
- Get closed-loop transfer functions from noise sources (noise of ADC, DAC and amplifier noise) to positioning error.
|
||
This is done using the multi-body model, with 2DoF APA model (having voltage input and outputs).
|
||
- Focus is made on the vertical direction, as it is the direction with the most stringent requirements.
|
||
If horizontal directions are considered, requirements are just less stringent than for the vertical direction.
|
||
- Deduce the maximum acceptable ASD of the noise sources.
|
||
|
||
As the voltage amplifier gain will impact how the DAC noise is amplified, some assumption are made:
|
||
- we want to apply -20 to 150V to the stacks
|
||
- Typical ADC are +/-10V
|
||
- Assumption of voltage amplifier with gain 20
|
||
|
||
** 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
|
||
%% Linearization options
|
||
opts = linearizeOptions;
|
||
opts.SampleTime = 0;
|
||
|
||
%% Open Simscape Model
|
||
mdl = 'detail_instrumentation_nass'; % Name of the Simulink File
|
||
open(mdl); % Open Simscape Model
|
||
#+END_SRC
|
||
|
||
#+begin_src matlab :noweb yes
|
||
<<m-init-other>>
|
||
#+end_src
|
||
|
||
** Closed-Loop Sensitivity to Instrumentation Disturbances
|
||
|
||
The following noise sources are considered (Figure ref:fig:detail_instrumentation_plant):
|
||
- $n_{da}$: output voltage noise of the DAC
|
||
- $n_{amp}$: output voltage noise of the voltage amplifier
|
||
- $n_{ad}$: voltage noise of the ADC measuring the force sensor stacks
|
||
|
||
Encoder noise, only used to estimate $R_z$ is found to have little impact on the vertical sample error and is therefore ommited from this analysis for clarity.
|
||
|
||
The transfer function from these three noise sources (for one strut) to the vertical error of the sample are estimated from the multi-body model, including the APA300ML and the designed flexible joints (Figure ref:fig:detail_instrumentation_noise_sensitivities).
|
||
|
||
The lateral error was also considered, but the specifications are less stringent than vertical error and the sensitivity to disturbances is smaller.
|
||
|
||
#+begin_src matlab
|
||
%% Identify the transfer functions from disturbance sources to vertical position error
|
||
% Let's initialize all the stages with default parameters.
|
||
initializeGround();
|
||
initializeGranite();
|
||
initializeTy();
|
||
initializeRy();
|
||
initializeRz();
|
||
initializeMicroHexapod();
|
||
initializeSample('m', 1);
|
||
initializeSimplifiedNanoHexapod();
|
||
|
||
initializeSimscapeConfiguration();
|
||
initializeDisturbances('enable', false);
|
||
initializeLoggingConfiguration('log', 'none');
|
||
initializeController('type', 'hac-iff');
|
||
initializeReferences();
|
||
|
||
% Decentralized IFF controller
|
||
wz = 2*pi*2;
|
||
xiz = 0.7;
|
||
Ghpf = (s^2/wz^2)/(s^2/wz^2 + 2*xiz*s/wz + 1);
|
||
|
||
Kiff = -200 * ... % Gain
|
||
1/(0.01*2*pi + s) * ... % LPF: provides integral action
|
||
Ghpf * ... % 2nd order HPF (limit low frequency gain)
|
||
eye(6); % Diagonal 6x6 controller (i.e. decentralized)
|
||
|
||
% Centralized HAC
|
||
wc = 2*pi*10; % Wanted crossover [rad/s]
|
||
H_int = wc/s; % Integrator
|
||
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))); % Lead to increase phase margin
|
||
H_lpf = 1/(1 + s/2/pi/80); % Low Pass filter to increase robustness
|
||
|
||
Khac = -5e4 * ... % Gain
|
||
H_int * ... % Integrator
|
||
H_lead * ... % Low Pass filter
|
||
H_lpf * ... % Low Pass filter
|
||
eye(6); % 6x6 Diagonal
|
||
|
||
% Input/Output definition
|
||
clear io; io_i = 1;
|
||
io(io_i) = linio([mdl, '/dac_noise'], 1, 'input'); io_i = io_i + 1; % DAC noise [V]
|
||
io(io_i) = linio([mdl, '/amp_noise'], 1, 'input'); io_i = io_i + 1; % Voltage Amplifier noise [V]
|
||
io(io_i) = linio([mdl, '/NASS/adc_noise'], 1, 'input'); io_i = io_i + 1; % ADC noise [V]
|
||
io(io_i) = linio([mdl, '/NASS/enc_noise'], 1, 'input'); io_i = io_i + 1; % Encoder noise [m]
|
||
io(io_i) = linio([mdl, '/NASS'], 2, 'output', [], 'z'); io_i = io_i + 1; % Vertical error [m]
|
||
io(io_i) = linio([mdl, '/NASS'], 2, 'output', [], 'y'); io_i = io_i + 1; % Lateral error [m]
|
||
|
||
Gd = linearize(mdl, io);
|
||
Gd.InputName = {...
|
||
'nda1', 'nda2', 'nda3', 'nda4', 'nda5', 'nda6', ... % DAC and Voltage amplifier noise
|
||
'namp1', 'namp2', 'namp3', 'namp4', 'namp5', 'namp6', ... % DAC and Voltage amplifier noise
|
||
'nad1', 'nad2', 'nad3', 'nad4', 'nad5', 'nad6', ... % ADC noise
|
||
'ddL1', 'ddL2', 'ddL3', 'ddL4', 'ddL5', 'ddL6' ... % Encoder noise
|
||
};
|
||
Gd.OutputName = {'y', 'z'}; % Vertical error of the sample
|
||
#+end_src
|
||
|
||
#+begin_src matlab :exports none :tangle no
|
||
%% Save Requirements
|
||
save('./matlab/mat/instrumentation_sensitivity.mat', 'Gd');
|
||
#+end_src
|
||
|
||
#+begin_src matlab :exports none :eval no
|
||
%% Save Requirements
|
||
save('./mat/instrumentation_sensitivity.mat', 'Gd');
|
||
#+end_src
|
||
|
||
#+begin_src matlab :exports none :results none
|
||
%% Transfer function from noise sources to vertical motion errors
|
||
freqs = logspace(0, 3, 1000);
|
||
|
||
figure;
|
||
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
|
||
nexttile();
|
||
hold on;
|
||
plot(freqs, abs(squeeze(freqresp(Gd('z', 'nda1' ), freqs, 'Hz'))), 'DisplayName', '$\epsilon_z/n_{da}$');
|
||
plot(freqs, abs(squeeze(freqresp(Gd('z', 'namp1'), freqs, 'Hz'))), 'DisplayName', '$\epsilon_z/n_{amp}$');
|
||
plot(freqs, abs(squeeze(freqresp(Gd('z', 'nad1' ), freqs, 'Hz'))), 'DisplayName', '$\epsilon_z/n_{ad}$');
|
||
hold off;
|
||
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
|
||
xlabel('Frequency [Hz]'); ylabel('Sensitivity [m/V]');
|
||
ylim([1e-9, 1e-4]);
|
||
leg = legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1);
|
||
leg.ItemTokenSize(1) = 15;
|
||
xlim([1, 1e3]);
|
||
#+end_src
|
||
|
||
#+begin_src matlab :tangle no :exports results :results file replace
|
||
exportFig('figs/detail_instrumentation_noise_sensitivities.pdf', 'width', 'wide', 'height', 'normal');
|
||
#+end_src
|
||
|
||
#+name: fig:detail_instrumentation_noise_sensitivities
|
||
#+caption: Transfer function from noise sources to vertical motion errors
|
||
#+RESULTS:
|
||
[[file:figs/detail_instrumentation_noise_sensitivities.png]]
|
||
|
||
** Estimation of maximum instrumentation noise
|
||
|
||
From previous analysis, we know how the noise of the instrumentation will affect the vertical error of the sample as a function of frequency.
|
||
Now, we want to determine specifications for each instrumentation such that the effect on the vertical error of the sample is within specifications.
|
||
|
||
Most stringent requirement:
|
||
- Vertical vibrations less than the smallest expected beam size of 100nm
|
||
- This corresponds to a maximum allowed vibration of 15nm RMS
|
||
|
||
Assumption on the noise:
|
||
- DAC, DAC and amplifier noise are uncorrelated, which is reasonable.
|
||
Noise corresponding each strut are each uncorrelated.
|
||
This means that the PSD of the different noise sources adds up.
|
||
|
||
Use of system symmetry to simplify the analysis:
|
||
- the effect of all the struts on the vertical errors are identical (verify from the extracted sensitivity curves).
|
||
Therefore only one strut can be considered for this analysis, and the total effect of the six struts is just six times the effect of one strut (in terms of power, but in terms of RMS value it's only sqrt(6)=2.5)
|
||
|
||
In order to have specifications in terms of noise spectral density of each instrumentation, a white noise is assumed, which is quite typical.
|
||
|
||
The noise specification is computed such that if all the instrumentation have this maximum noise, the specification in terms of vertical error is still respected.
|
||
This is a pessimistic choice, but it gives a rough idea of the specifications.
|
||
|
||
#+begin_src matlab
|
||
% Maximum wanted effect of each noise source on the vertical error
|
||
% Specifications: 15nm RMS
|
||
% divide by sqrt(6) because 6 struts
|
||
% divide by sqrt(3) because 3 considered noise sources
|
||
max_asd_z = 15e-9 / sqrt(6) / sqrt(3); % [m/sqrt(Hz)]
|
||
|
||
% Suppose unitary flat noise ASD => compute the effect on vertical noise
|
||
unit_asd = ones(1, length(freqs));
|
||
rms_unit_asd_dac = sqrt(sum((unit_asd.*abs(squeeze(freqresp(Gd('z', 'nda1' ), freqs, 'Hz'))).').^2));
|
||
rms_unit_asd_amp = sqrt(sum((unit_asd.*abs(squeeze(freqresp(Gd('z', 'namp1'), freqs, 'Hz'))).').^2));
|
||
rms_unit_asd_adc = sqrt(sum((unit_asd.*abs(squeeze(freqresp(Gd('z', 'nad1' ), freqs, 'Hz'))).').^2));
|
||
|
||
% Obtained maximum ASD for different instruments
|
||
max_dac_asd = max_asd_z./rms_unit_asd_dac; % [V/sqrt(Hz)]
|
||
max_amp_asd = max_asd_z./rms_unit_asd_amp; % [V/sqrt(Hz)]
|
||
max_adc_asd = max_asd_z./rms_unit_asd_adc; % [V/sqrt(Hz)]
|
||
|
||
% Estimation of the equivalent RMS noise
|
||
max_dac_rms = 1e3*max_dac_asd*sqrt(5e3) % [mV RMS]
|
||
max_amp_rms = 1e3*max_amp_asd*sqrt(5e3) % [mV RMS]
|
||
max_adc_rms = 1e3*max_adc_asd*sqrt(5e3) % [mV RMS]
|
||
#+end_src
|
||
|
||
Obtained maximum noise are:
|
||
- DAC maximum output noise ASD $14\,\mu V/\sqrt{Hz}$.
|
||
- Voltage amplifier maximum output voltage noise ASD $280\,\mu V/\sqrt{Hz}$
|
||
- ADC maximum measurement noise ASD $11\,\mu V/\sqrt{Hz}$.
|
||
|
||
In terms of RMS noise,
|
||
- DAC: <1 mV RMS
|
||
- Voltage amplifier: < 20 mV RMS
|
||
- ADC: < 0.8 mV RMS
|
||
|
||
#+name: tab:detail_instrumentation_specification_noise
|
||
#+caption: Obtained specification in terms of noise
|
||
#+attr_latex: :environment tabularx :width 0.7\linewidth :align Xccc
|
||
#+attr_latex: :center t :booktabs t
|
||
| | ADC | DAC | Amplifier |
|
||
|-------------+-----------------------+-----------------------+------------------------|
|
||
| Maximum ASD | $11\,\mu V/\sqrt{Hz}$ | $14\,\mu V/\sqrt{Hz}$ | $280\,\mu V/\sqrt{Hz}$ |
|
||
| RMS Noise | $0.8\,mV\,\text{RMS}$ | $1\,mV\,\text{RMS}$ | $20\,mV\,\text{RMS}$ |
|
||
|
||
If the Amplitude Spectral Density of the noise of the ADC, DAC and voltage amplifiers are all below the specified maximum noises, then the induced vertical error will be below 15nmRMS.
|
||
|
||
Such specification will guide the choice of instrumentation in Section ref:sec:detail_instrumentation_choice.
|
||
|
||
#+begin_src matlab :exports none :tangle no
|
||
%% Save Requirements
|
||
save('./matlab/mat/instrumentation_requirements.mat', ...
|
||
'max_dac_asd', 'max_amp_asd', 'max_adc_asd', ...
|
||
'max_dac_rms', 'max_amp_rms', 'max_adc_rms');
|
||
#+end_src
|
||
|
||
#+begin_src matlab :exports none :eval no
|
||
%% Save Requirements
|
||
save('./mat/instrumentation_requirements.mat', ...
|
||
'max_dac_asd', 'max_amp_asd', 'max_adc_asd', ...
|
||
'max_dac_rms', 'max_amp_rms', 'max_adc_rms');
|
||
#+end_src
|
||
|
||
* Choice of Instrumentation
|
||
:PROPERTIES:
|
||
:HEADER-ARGS:matlab+: :tangle matlab/detail_instrumentation_2_choice.m
|
||
:END:
|
||
<<sec:detail_instrumentation_choice>>
|
||
** Introduction :ignore:
|
||
|
||
Based on:
|
||
- noise specifications extracted from Section ref:sec:detail_instrumentation_dynamic_error_budgeting
|
||
- other specifications (input/output range, bandwidth, etc...)
|
||
|
||
The most adequate ADC, DAC, Voltage amplifier, and relative positioning sensor are found.
|
||
Different options that were considered are presented, and the choice of instrumentation is explained.
|
||
|
||
** 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 computed requirements
|
||
load('instrumentation_requirements.mat')
|
||
#+end_src
|
||
|
||
** Piezoelectric Voltage Amplifier
|
||
**** Introduction :ignore:
|
||
|
||
There are several characteristics of the piezoelectric voltage amplifiers that should be considered.
|
||
To be able to use the full stroke of the piezoelectric actuator, the voltage output should be between -20 and 150V.
|
||
It should accept an analog input voltage, preferably between -10 and 10V, as it is quite typical for DAC.
|
||
|
||
**** Small signal Bandwidth and Output Impedance
|
||
|
||
There are two bandwidth that should be considered for a piezoelectric voltage amplifier: large signal bandwidth and small signal bandwidth.
|
||
Large signal bandwidth are linked to the output current capacities of the amplifier and will be discussed next.
|
||
|
||
Small signal bandwidth of the voltage amplifier is very important for feedback applications as it can limit the bandwidth of the complete feedback system.
|
||
|
||
A simplified electrical model of a voltage amplifier connected to a piezoelectric stack is shown in Figure ref:fig:detail_instrumentation_amp_output_impedance.
|
||
This model is only valid for small signals, but it is useful to understand the small signal bandwidth limitation [[cite:&fleming14_desig_model_contr_nanop_system, chap. 14]].
|
||
$R_o$ corresponds to the output impedance of the amplifier.
|
||
With the piezoelectric load that corresponds to a capacitance $C_p$, it forms a first order low pass filter eqref:eq:detail_instrumentation_amp_output_impedance.
|
||
|
||
\begin{equation}\label{eq:detail_instrumentation_amp_output_impedance}
|
||
\frac{V_a}{V_i}(s) = \frac{1}{1 + \frac{s}{\omega_0}}, \quad \omega_0 = \frac{1}{R_o C_p}
|
||
\end{equation}
|
||
|
||
#+name: fig:detail_instrumentation_amp_output_impedance
|
||
#+caption: Electrical model of a voltage amplifier with output impedance $R_0$ connected to a piezoelectric stack with capacitance $C_p$
|
||
[[file:figs/detail_instrumentation_amp_output_impedance.png]]
|
||
|
||
Therefore, the small signal bandwidth is load dependent (it decreases as the load capacitance increases).
|
||
The capacitance load of the two piezoelectric stacks in the APA300ML correspond to a capacitance $C_p = 8.8\,\mu F$.
|
||
If a small signal bandwidth of $f_0 = \frac{\omega_0}{2\pi} = 5\,\text{kHz}$ is wanted, the voltage amplifier output impedance should be smaller than $R_0 = 3.6\,\Omega$.
|
||
|
||
#+begin_src matlab
|
||
Cp = 8.8e-6; % Capacitive load of the two piezoelectric actuators
|
||
f0 = 5e3; % Wanted low signal bandwidth [Hz]
|
||
Ro_max = 1/(2*pi*f0 * Cp); % Maximum wanted output impedance [Ohm]
|
||
#+end_src
|
||
|
||
**** Large signal Bandwidth
|
||
|
||
Large signal bandwidth are linked to the maximum output capabilities of the amplifiers in terms of amplitude as a function of frequency [[cite:&spengen16_high_voltag_amplif]].
|
||
|
||
As the primary objective of the NASS is to stabilize the position and not to perform scans, this specification is not as important as the small signal bandwidth.
|
||
|
||
However, let's take into account scanning capabilities, and consider the worst case scenario of a constant velocity scan (i.e. triangular reference signal) with a repetition rate of $f_r = 100\,\text{Hz}$ and using full voltage capabilities of the piezoelectric actuator $V_{pp} = 170\,V$.
|
||
|
||
There are two things to consider:
|
||
- Slew rate that should be above $2 \cdot V_{pp} \cdot f_r = 34\,V/ms$
|
||
This specification is easily achieved by commercial voltage amplifiers.
|
||
- Current output capabilities: as the capacitance impedance decreases the the inverse of the frequency, it can reach very low values at high frequency
|
||
In order to reach high voltage at high frequency, the required current that the voltage amplifier needs to provide may reach very large values.
|
||
$I_{\text{max}} = 2 \cdot V_{pp} \cdot f \cdot C_p = 0.3\,A$
|
||
|
||
So ideally, a voltage amplifier capable of providing $0.3\,A$ of current is wanted.
|
||
|
||
#+begin_src matlab
|
||
%% Slew-rate specifications - Triangular scan
|
||
Vpp = 170; % Full voltage scan [V]
|
||
f0 = 100; % Repetition rate of the triangular scan [Hz]
|
||
|
||
slew_rate = 1e-3*2*Vpp*f0 % Required slew rate [V/ms]
|
||
|
||
%% Maximum Output Current - Triangular scan
|
||
max_current = 2*Vpp*f0*Cp % [A]
|
||
#+end_src
|
||
|
||
**** Output voltage noise
|
||
|
||
As discussed in Section ref:sec:detail_instrumentation_dynamic_error_budgeting, the output noise of the voltage amplifier should be smaller than $20\,mV\,\text{RMS}$.
|
||
|
||
As explained in [[cite:&spengen20_high_voltag_amplif]], the load capacitance of the piezoelectric stack filters the output noise of the amplifier (low pass filter of Figure ref:fig:detail_instrumentation_amp_output_impedance).
|
||
Therefore, when comparing noise indicated in the datasheet of different voltage amplifiers, it is important to check what is the considered capacitance of the load (i.e. the low signal bandwidth considered).
|
||
|
||
Here, the output noise should be smaller than 20mVRMS for a load of 8.8uF and a bandwidth larger than 5kHz.
|
||
|
||
**** Choice of voltage amplifier
|
||
|
||
The specifications are summarized in Table ref:tab:detail_instrumentation_amp_choice.
|
||
The most important characteristics being the (small signal) bandwidth > 5 [kHz] and the output voltage noise (< 20 [mV RMS]).
|
||
|
||
Several voltage amplifiers were considered, with datasheet information summarized in Table ref:tab:detail_instrumentation_amp_choice.
|
||
- Issue for the selection: manufacturers are not specifying the output noise as a function of frequency (i.e. the ASD of the noise), but only the RMS value (i.e. the integrated value over all frequency).
|
||
It does not take into account the frequency dependency of the noise, that is very important to perform error budgets
|
||
Also, the load used to estimate the bandwidth and noise is often not mentioned.
|
||
Most of the time, the bandwidth is indicated with very little load and the noise with high load.
|
||
It renders the comparison between different models more complex.
|
||
- The chosen model is the PD200 from PiezoDrive.
|
||
- It fulfill the specification
|
||
- It has clear documentation, especially about noise and bandwidth
|
||
|
||
#+name: tab:detail_instrumentation_amp_choice
|
||
#+caption: Characteristics of the PD200 compared with the specifications
|
||
#+attr_latex: :environment tabularx :width \linewidth :align Xcccc
|
||
#+attr_latex: :center t :booktabs t :float t
|
||
| *Specification* | *PD200* | WMA-200 | LA75B | E-505 |
|
||
| | PiezoDrive | Falco | Cedrat | PI |
|
||
|--------------------------------------+-----------------------+-------------------------------------------+--------------+-----------|
|
||
| Input Voltage Range: $\pm 10\,V$ | $\pm 10\,V$ | $\pm8.75\,V$ | $-1/7.5\,V$ | |
|
||
| Output Voltage Range: $-20/150\,V$ | $-50/150\,V$ | $\pm 175\,V$ | $-20/150\,V$ | -30/130 |
|
||
| Gain $>15$ | 20 | 20 | 20 | 10 |
|
||
| Output Current $> 300\,mA$ | $900\,mA$ | $150\,mA$ | $360\,mA$ | $215\,mA$ |
|
||
| Slew Rate $> 34\,V/ms$ | $150\,V/\mu s$ | $80\,V/\mu s$ | n/a | n/a |
|
||
| Output noise $< 20\,mV\ \text{RMS}$ | $0.7\,mV\,\text{RMS}$ | $0.05\,mV$ | $3.4\,mV$ | $0.6\,mV$ |
|
||
| (10uF load) | ($10\,\mu F$ load) | ($10\,\mu F$ load) | | |
|
||
| Small Signal Bandwidth $> 5\,kHz$ | $6.4\,kHz$ | $300\,Hz$ | $30\,kHz$ | n/a |
|
||
| ($10\,\mu F$ load) | ($10\,\mu F$ load) | [fn:detail_instrumentation_1] | (unloaded) | |
|
||
| Output Impedance: $< 3.6\,\Omega$ | n/a | $50\,\Omega$[fn:detail_instrumentation_1] | n/a | n/a |
|
||
|
||
# #+name: fig:detail_instrumentation_pd200_specs
|
||
# #+caption: Caption with reference to sub figure (\subref{fig:detail_instrumentation_pd200_specs_bandwidth})
|
||
# #+attr_latex: :options [htbp]
|
||
# #+begin_figure
|
||
# #+attr_latex: :caption \subcaption{\label{fig:detail_instrumentation_pd200_specs_bandwidth}sub caption a}
|
||
# #+attr_latex: :options {0.48\textwidth}
|
||
# #+begin_subfigure
|
||
# #+attr_latex: :width 0.95\linewidth
|
||
# [[file:figs/detail_instrumentation_pd200_specs_bandwidth.png]]
|
||
# #+end_subfigure
|
||
# #+attr_latex: :caption \subcaption{\label{fig:detail_instrumentation_pd200_specs_noise}sub caption b}
|
||
# #+attr_latex: :options {0.48\textwidth}
|
||
# #+begin_subfigure
|
||
# #+attr_latex: :width 0.95\linewidth
|
||
# [[file:figs/detail_instrumentation_pd200_specs_noise.png]]
|
||
# #+end_subfigure
|
||
# #+end_figure
|
||
|
||
** ADC and DAC
|
||
**** Introduction :ignore:
|
||
Analog to digital converters and digital to analog converters are very important to convert signals from the RT controller that only uses digital numbers to the physical plant, which is of course affected by analog signals.
|
||
|
||
**** Synchronicity and Jitter
|
||
|
||
For control systems, it is very important that the inputs and outputs are sampled synchronously with the controller and with low jitter [[cite:&abramovitch22_pract_method_real_world_contr_system;&abramovitch23_tutor_real_time_comput_issues_contr_system]].
|
||
|
||
Therefore, the ADC and DAC needs to be well interfaced with the Speedgoat, and triggered synchronously with the computation of the control signals.
|
||
For that reason, it was decided to first look for ADC and DAC sold by Speedgoat (the RT controller used).
|
||
|
||
**** Sampling Frequency, Bandwidth and delays
|
||
|
||
Several requirements may appear the same but are very different in nature:
|
||
- Sampling frequency: defines the interval between two sampled points, also determines the Nyquist frequency
|
||
- Bandwidth: defines the maximum frequency of a measured signal (typically specified as the -3dB point), usually limited by implemented anti-aliasing filters
|
||
- Delay/latency: delay between the analog signal at the input of the ADC to the digital information transferred to the control system
|
||
|
||
Sigma-Delta ADC can have extremely good noise characteristics, high bandwidth and sampling frequency but very poor latency.
|
||
Typically, the latency can reach 20 times the sampling period [[cite:&schmidt20_desig_high_perfor_mechat_third_revis_edition, chapt. 8.4]].
|
||
|
||
Therefore, Sigma-Delta ADC are very much used for signal acquisition applications, but has limited use for real-time control where latency is critical.
|
||
Therefore, for real time control applications, SAR-ADC (Successive approximation ADCs) is still the mostly applied type because of its single sample latency.
|
||
|
||
**** ADC Noise
|
||
|
||
From the dynamical error budget in Section ref:sec:detail_instrumentation_dynamic_error_budgeting
|
||
Measurement noise ASD should be bellow 11uV/sqrt(Hz), 0.8mV RMS
|
||
|
||
ADC are affected by various noise sources
|
||
One of them is quantization noise, and is linked to the fact that input/output values can only take a finite number of values.
|
||
Let's first find the number of bits such that the quantization noise is fulfilling the requirements.
|
||
|
||
Let's first suppose that the ADC is ideal and the only noise comes from the quantization error.
|
||
Let's note $q = \frac{\Delta V}{2^n}$ the quantization in [V], which is the corresponding value in [V] of the least significant bit.
|
||
$\Delta V$ is the full range of the ADC in [V], $n$ is the number of ADC's bits and $F_s$ is the sample frequency in [Hz].
|
||
|
||
The quantization noise can take a value between $\pm q/2$, and the probability density function is constant in this range (i.e., it’s a uniform distribution).
|
||
Since the integral of the probability density function $p(e)$ is equal to one, its value is $1/q$ for $-q/2 < e < q/2$ as illustrated in Figure ref:fig:detail_instrumentation_adc_quantization.
|
||
|
||
#+begin_src latex :file detail_instrumentation_adc_quantization.pdf
|
||
\begin{tikzpicture}
|
||
\path[fill=black!20!white] (-1, 0) |- (1, 1) |- (-1, 0);
|
||
|
||
\draw[->] (-2, 0) -- (2, 0) node[above left]{$e$};
|
||
\draw[->] (0, -0.5) -- (0, 2) node[below right]{$p(e)$};
|
||
\draw[dashed] (-2, 0) -- (-1, 0) |- (1, 1) |- (2, 0);
|
||
|
||
\node[below] at (1, 0){$\frac{q}{2}$};
|
||
\node[below] at (-1, 0){$-\frac{q}{2}$};
|
||
\node[right] at (1, 1){$\frac{1}{q}$};
|
||
\end{tikzpicture}
|
||
#+end_src
|
||
|
||
#+name: fig:detail_instrumentation_adc_quantization
|
||
#+caption: Probability density function $p(e)$ of the ADC error $e$
|
||
#+RESULTS:
|
||
[[file:figs/detail_instrumentation_adc_quantization.png]]
|
||
|
||
The variance (or time average power) of the quantization noise is eqref:eq:detail_instrumentation_quant_power.
|
||
|
||
\begin{equation}\label{eq:detail_instrumentation_quant_power}
|
||
P_q = \int_{-q/2}^{q/2} e^2 p(e) de = \frac{q^2}{12}
|
||
\end{equation}
|
||
|
||
Now, the goal is to compute the power spectral density of the quantization noise, which is by definition the Fourier transform of the autocorrelation function of the quantization noise.
|
||
Assuming that the noise samples are not correlated with one another, the autocorrelation function can be approximated with a delta function in the time domain.
|
||
Since the Fourier transform of a delta function is equal to one, the power spectral density will be frequency independent (i.e. white noise).
|
||
Therefore, thanks the the Perseval's theorem, the quantization noise is a white noise with total power equal to $P_q = \frac{q^2}{12}$.
|
||
|
||
Thus, the two-sided PSD (from $\frac{-F_s}{2}$ to $\frac{F_s}{2}$), we should divide the noise power $P_q$ by $F_s$:
|
||
\begin{equation}
|
||
P_q = \int_{-F_s/2}^{F_s/2} \Gamma(f) d f = F_s \Gamma = \frac{q^2}{12}
|
||
\end{equation}
|
||
|
||
Finally, the Power Spectral Density of the quantization noise of an ADC is equal to eqref:eq:detail_instrumentation_psd_quant_noise.
|
||
|
||
\begin{equation}\label{eq:detail_instrumentation_psd_quant_noise}
|
||
\Gamma_q = \frac{q^2}{12 F_s} = \frac{\left(\frac{\Delta V}{2^n}\right)^2}{12 F_s} \quad \text{in } \left[ \frac{V^2}{\text{Hz}} \right]
|
||
\end{equation}
|
||
|
||
From a defined noise amplitude spectral density $\Phi_{\text{max}}$, the minimum number of bits so that the quantization noise is below $\Phi_{\text{max}}$ can be computed from eqref:eq:detail_instrumentation_min_n.
|
||
|
||
\begin{equation}\label{eq:detail_instrumentation_min_n}
|
||
n = \text{log}_2 \left( \frac{\Delta V}{\sqrt{12 Fs} \Phi_{\text{max}}} \right)
|
||
\end{equation}
|
||
|
||
With a sampling frequency $F_s = 10\,kHz$, a full range of $\Delta V = 20\,V$ and a maximum allowed ASD $\Phi_{\text{max}} = 11\,\mu V/\sqrt{Hz}$, the minimum number of bits is $n_{\text{min}} = 12.4$, which is easily satisfied by commercial ADCs.
|
||
|
||
#+begin_src matlab
|
||
delta_V = 20; % +/-10 V
|
||
Fs = 10e3; % Sampling Frequency [Hz]
|
||
max_adc_asd = 11e-6; % V/sqrt(Hz)
|
||
|
||
min_n = log2(delta_V/(sqrt(12*Fs)*max_adc_asd))
|
||
#+end_src
|
||
|
||
#+begin_src matlab
|
||
%% Estimate quantization noise of the ADC
|
||
delta_V = 20; % +/-10 V
|
||
n = 16; % number of bits
|
||
Fs = 10e3; % [Hz]
|
||
|
||
q = delta_V/2^n; % Quantization in [V]
|
||
q_psd = q^2/12/Fs; % Quantization noise Power Spectral Density [V^2/Hz]
|
||
q_asd = sqrt(q_psd) % Quantization noise Amplitude Spectral Density [V/sqrt(Hz)]
|
||
#+end_src
|
||
|
||
**** DAC Output voltage noise
|
||
|
||
Similarly to ADC, the DAC output voltage noise ASD should be below $14\,\mu V/\sqrt{Hz}$, 1mV RMS.
|
||
This corresponds to a 13bits +/-10V DAC, which is easily satisfied.
|
||
|
||
**** Choice of the ADC and DAC Board
|
||
|
||
Based on the above analysis, the choice of ADC and DAC is quite straightforward.
|
||
|
||
Integrated in Speedgoat for best synchronicity.
|
||
Chosen model: IO131:
|
||
- 16 analog inputs, based on the AD7609
|
||
- 16 bits, +/- 10V
|
||
- Maximum sampling rate of 200kSPS
|
||
- Simultaneous sampling
|
||
- Differential inputs: can use shielded twisted pairs for high noise immunity
|
||
- 8 analog outputs, based on the AD5754R
|
||
- 16 bits, +/- 10V
|
||
- Conversion time 10us
|
||
- Simultaneous update
|
||
|
||
Noise is not specified, but as it has 16 bits resolution, it should be well below the requirements.
|
||
It will be experimentally measured in Section ref:sec:detail_instrumentation_characterization.
|
||
|
||
** Relative Displacement Sensors
|
||
|
||
Specifications:
|
||
- used for relative positioning
|
||
- Small enough to be integrated in each strut
|
||
- vertical errors of 15nmRMS => 6nmRMS for each strut => maximum 6nmRMS sensor noise
|
||
- Stroke > 100um
|
||
|
||
There are many different sensors that can fulfil the requirements [[cite:&fleming13_review_nanom_resol_posit_sensor]]:
|
||
- Encoders
|
||
- Capacitive Sensors
|
||
- Eddy current sensors
|
||
|
||
#+name: fig:detail_instrumentation_sensor_examples
|
||
#+caption: Measurement of strut flexible modes
|
||
#+attr_latex: :options [htbp]
|
||
#+begin_figure
|
||
#+attr_latex: :caption \subcaption{\label{fig:detail_instrumentation_sensor_encoder}Optical Linear Encoder}
|
||
#+attr_latex: :options {0.33\textwidth}
|
||
#+begin_subfigure
|
||
#+attr_latex: :width 0.9\linewidth
|
||
[[file:figs/detail_instrumentation_sensor_encoder.jpg]]
|
||
#+end_subfigure
|
||
#+attr_latex: :caption \subcaption{\label{fig:detail_instrumentation_sensor_eddy_current}Eddy Current Sensor}
|
||
#+attr_latex: :options {0.33\textwidth}
|
||
#+begin_subfigure
|
||
#+attr_latex: :width 0.9\linewidth
|
||
[[file:figs/detail_instrumentation_sensor_eddy_current.png]]
|
||
#+end_subfigure
|
||
#+attr_latex: :caption \subcaption{\label{fig:detail_instrumentation_sensor_capacitive}Capacitive Sensor}
|
||
#+attr_latex: :options {0.33\textwidth}
|
||
#+begin_subfigure
|
||
#+attr_latex: :width 0.9\linewidth
|
||
[[file:figs/detail_instrumentation_sensor_capacitive.jpg]]
|
||
#+end_subfigure
|
||
#+end_figure
|
||
|
||
The implementation:
|
||
- slight advantage to capacitive or eddy current sensors as they can measure in line with the APA (Figure ref:fig:detail_instrumentation_capacitive_implementation)
|
||
- for the encoder, the measurement has to be "offset" from the strut "action line", and therefore relative rotations between the two ends of the APA induces measurement errors (Figure ref:fig:detail_instrumentation_encoder_implementation).
|
||
|
||
#+name: fig:detail_instrumentation_sensor_implementation
|
||
#+caption: Caption with reference to sub figure
|
||
#+attr_latex: :options [htbp]
|
||
#+begin_figure
|
||
#+attr_latex: :caption \subcaption{\label{fig:detail_instrumentation_encoder_implementation}Optical Encoder}
|
||
#+attr_latex: :options {0.48\textwidth}
|
||
#+begin_subfigure
|
||
#+attr_latex: :scale 1
|
||
[[file:figs/detail_instrumentation_encoder_implementation.png]]
|
||
#+end_subfigure
|
||
#+attr_latex: :caption \subcaption{\label{fig:detail_instrumentation_capacitive_implementation}Capacitive Sensor}
|
||
#+attr_latex: :options {0.48\textwidth}
|
||
#+begin_subfigure
|
||
#+attr_latex: :scale 1
|
||
[[file:figs/detail_instrumentation_capacitive_implementation.png]]
|
||
#+end_subfigure
|
||
#+end_figure
|
||
|
||
One major issue is the fact that the sensor signals have to pass through an electrical slip-ring (because of the continuous spindle rotation).
|
||
Some measurements were performed on the slip-ring integrated in the micro-station, and the cross-talk between different slip-ring channels were found to be quite high.
|
||
It was preferred to use a sensor that transmit the measured displacement digitally, such that it is much less sensitive to noise and cross-talk.
|
||
For that reason, an optical encoder with digital output was preferred (i.e. the interpolation is performed directly in the head).
|
||
|
||
The specifications are summarized in Table ref:tab:detail_instrumentation_sensor_specs.
|
||
|
||
#+name: tab:detail_instrumentation_sensor_specs
|
||
#+caption: Characteristics of the Vionic compared with the specifications
|
||
#+attr_latex: :environment tabularx :width 0.9\linewidth :align Xccc
|
||
#+attr_latex: :center t :booktabs t :float t
|
||
| *Specification* | *Renishaw Vionic* | LION CPL190 | Cedrat ECP500 |
|
||
|-----------------------------+---------------------+-------------+---------------|
|
||
| Technology | Digital Encoder | Capacitive | Eddy Current |
|
||
| Bandwidth $> 5\,\text{kHz}$ | $> 500\,\text{kHz}$ | 10kHz | 20kHz |
|
||
| Noise $< 6\,nm\,\text{RMS}$ | 1.6 nm rms | 4 nm rms | 15 nm rms |
|
||
| Range $> 100\,\mu m$ | Ruler length | 250 um | 500um |
|
||
| In line measurement | | $\times$ | $\times$ |
|
||
| Digital Output | $\times$ | | |
|
||
|
||
* Characterization of Instrumentation
|
||
:PROPERTIES:
|
||
:HEADER-ARGS:matlab+: :tangle matlab/detail_instrumentation_3_characterization.m
|
||
:END:
|
||
<<sec:detail_instrumentation_characterization>>
|
||
** Introduction :ignore:
|
||
|
||
All the instrumentation was then procured and tested individually to verify whether it fulfils the specifications or not.
|
||
|
||
** 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 computed requirements
|
||
load('instrumentation_requirements.mat')
|
||
#+end_src
|
||
|
||
#+begin_src matlab
|
||
%% Sensitivity to disturbances
|
||
load('instrumentation_sensitivity.mat', 'Gd');
|
||
#+end_src
|
||
|
||
** Analog to Digital Converters
|
||
**** Introduction :ignore:
|
||
The ADC of the IO318 cards:
|
||
- have differential inputs
|
||
- internally uses the AD7609 ADC from Analog Devices.
|
||
- capable of 200kSPS, 16 bits, +/-10V
|
||
|
||
**** Measured Noise
|
||
|
||
The measurement of the ADC noise was done by short-circuiting its input with a 50 Ohm resistor and recording the digital value at 10kHz.
|
||
The amplitude spectral density of the recorded values are computed and shown in Figure ref:fig:detail_instrumentation_adc_noise_measured.
|
||
The ADC noise is a white noise with an amplitude spectral density of $5.6\,\mu V/\sqrt{Hz}$ (RMS value of 0.4mV), which fulfills the specifications.
|
||
All ADC channels are measuring the same, so the noise of only one channel is here shown.
|
||
|
||
If required, it is possible to apply some oversampling to lower the obtained noise as explained in cite:lab13_improv_adc.
|
||
To have additional $w$ bits of resolution, the oversampling frequency $f_{os}$ should be $f_{os} = 4^w \cdot f_s$.
|
||
As the ADC can work at 200kSPS, and the real time controller only runs at 10kSPS, an oversampling factor of 16 can be used to have approximately two more bits of resolution (i.e. reducing the noise by a factor 4).
|
||
This works because the noise can be approximated by a white noise and the amplitude is larger than 1 LSB (0.3 mV) [[cite:hauser91_princ_overs_conver]].
|
||
|
||
#+begin_src matlab
|
||
%% ADC noise
|
||
adc = load("2023-08-23_15-42_io131_adc_noise.mat");
|
||
|
||
% Spectral Analysis parameters
|
||
Ts = 1e-4;
|
||
Nfft = floor(1/Ts);
|
||
win = hanning(Nfft);
|
||
Noverlap = floor(Nfft/2);
|
||
% Identification of the transfer function from Va to di
|
||
[pxx, f] = pwelch(detrend(adc.adc_1, 0), win, Noverlap, Nfft, 1/Ts);
|
||
|
||
adc.pxx = pxx;
|
||
adc.f = f;
|
||
|
||
% estimated mean ASD
|
||
sprintf('Mean ASD of the ADC: %.1f uV/sqrt(Hz)', 1e6*sqrt(mean(adc.pxx)))
|
||
sprintf('Specifications: %.1f uV/sqrt(Hz)', 1e6*max_adc_asd)
|
||
|
||
% estimated RMS
|
||
sprintf('RMS of the ADC: %.2f mV RMS', 1e3*rms(detrend(adc.adc_1,0)))
|
||
sprintf('RMS specifications: %.2f mV RMS', max_adc_rms)
|
||
|
||
% Estimate quantization noise of the IO318 ADC
|
||
delta_V = 20; % +/-10 V
|
||
n = 16; % number of bits
|
||
Fs = 10e3; % [Hz]
|
||
|
||
adc.q = delta_V/2^n; % Quantization in [V]
|
||
adc.q_psd = adc.q^2/12/Fs; % Quantization noise Power Spectral Density [V^2/Hz]
|
||
adc.q_asd = sqrt(adc.q_psd); % Quantization noise Amplitude Spectral Density [V/sqrt(Hz)]
|
||
#+end_src
|
||
|
||
#+begin_src matlab :exports none :results none
|
||
%% Measured ADC noise (IO318)
|
||
figure;
|
||
hold on;
|
||
plot(adc.f, sqrt(adc.pxx), 'color', colors(3,:), 'DisplayName', '$\Gamma_{q_{ad}}$')
|
||
plot([adc.f(2), adc.f(end)], [max_adc_asd, max_adc_asd], '--', 'color', colors(3,:), 'DisplayName', 'Specs')
|
||
plot([adc.f(2), adc.f(end)], [adc.q_asd, adc.q_asd], 'k--', 'DisplayName', 'Quantization noise (16 bits, $\pm 10\,V$)')
|
||
hold off;
|
||
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
|
||
xlabel('Frequency [Hz]'); ylabel('ASD [V/$\sqrt{Hz}$]');
|
||
legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1);
|
||
ylim([1e-10, 4e-4]); xlim([1, 5e3]);
|
||
xticks([1e0, 1e1, 1e2, 1e3])
|
||
#+end_src
|
||
|
||
#+begin_src matlab :tangle no :exports results :results file replace
|
||
exportFig('figs/detail_instrumentation_adc_noise_measured.pdf', 'width', 'normal', 'height', 'normal');
|
||
#+end_src
|
||
|
||
#+name: fig:detail_instrumentation_adc_noise_measured
|
||
#+caption: Measured ADC noise (IO318)
|
||
#+RESULTS:
|
||
[[file:figs/detail_instrumentation_adc_noise_measured.png]]
|
||
|
||
**** Reading of piezoelectric force sensor
|
||
|
||
To further verify that the ADC can effectively measure the voltage generated by a piezoelectric stack without issue, a test with the APA95ML was performed.
|
||
The setup is shown in Figure ref:fig:detail_instrumentation_force_sensor_adc_setup where two stacks are used as actuator (in parallel) and one stack is used as a sensor.
|
||
The voltage amplifier used has a gain of 20.
|
||
|
||
#+name: fig:detail_instrumentation_force_sensor_adc_setup
|
||
#+caption: Schematic of the setup to validate the use of the ADC for reading the force sensor volage
|
||
[[file:figs/detail_instrumentation_force_sensor_adc_setup.png]]
|
||
|
||
#+begin_src matlab
|
||
%% Read force sensor voltage with the ADC
|
||
load('force_sensor_steps.mat', 't', 'encoder', 'u', 'v');
|
||
|
||
% Exponential fit to compute the time constant
|
||
% Fit function
|
||
f_exp = @(b,x) b(1).*exp(-b(2).*x) + b(3);
|
||
|
||
% Three steps are performed at the following time intervals:
|
||
t_s = [ 2.5, 23;
|
||
23.8, 35;
|
||
35.8, 50];
|
||
|
||
tau = zeros(size(t_s, 1),1); % Time constant [s]
|
||
V0 = zeros(size(t_s, 1),1); % Offset voltage [V]
|
||
a = zeros(size(t_s, 1),1); %
|
||
|
||
for t_i = 1:size(t_s, 1)
|
||
t_cur = t(t_s(t_i, 1) < t & t < t_s(t_i, 2));
|
||
t_cur = t_cur - t_cur(1);
|
||
y_cur = v(t_s(t_i, 1) < t & t < t_s(t_i, 2));
|
||
|
||
nrmrsd = @(b) norm(y_cur - f_exp(b,t_cur)); % Residual Norm Cost Function
|
||
B0 = [0.5, 0.15, 2.2]; % Choose Appropriate Initial Estimates
|
||
[B,rnrm] = fminsearch(nrmrsd, B0); % Estimate Parameters ‘B’
|
||
|
||
a(t_i) = B(1);
|
||
tau(t_i) = 1/B(2);
|
||
V0(t_i) = B(3);
|
||
end
|
||
|
||
% Data to show the exponential fit
|
||
t_fit_1 = linspace(t_s(1,1), t_s(1,2), 100);
|
||
y_fit_1 = f_exp([a(1),1/tau(1),V0(1)], t_fit_1-t_s(1,1));
|
||
|
||
t_fit_2 = linspace(t_s(2,1), t_s(2,2), 100);
|
||
y_fit_2 = f_exp([a(2),1/tau(2),V0(2)], t_fit_2-t_s(2,1));
|
||
|
||
t_fit_3 = linspace(t_s(3,1), t_s(3,2), 100);
|
||
y_fit_3 = f_exp([a(3),1/tau(3),V0(3)], t_fit_3-t_s(3,1));
|
||
|
||
% Speedgoat ADC input impedance
|
||
Cp = 4.4e-6; % [F]
|
||
Rin = abs(mean(tau))/Cp; % [Ohm]
|
||
|
||
% Estimated input bias current
|
||
in = mean(V0)/Rin; % [A]
|
||
|
||
% Resistor added in parallel to the force sensor
|
||
fc = 0.5; % Wanted corner frequency [Hz]
|
||
Ra = Rin/(2*pi*fc*Cp*Rin - 1); % [Ohm]
|
||
|
||
% New ADC offset voltage
|
||
V_offset = Ra*Rin/(Ra + Rin) * in; % [V]
|
||
#+end_src
|
||
|
||
Steps signals was generated using the DAC with an amplitude of 1V, and the ADC signal was recorded.
|
||
The excitation signal (steps) and measured voltage across the sensor stack are shown in Figure ref:fig:detail_instrumentation_step_response_force_sensor.
|
||
|
||
Two things can be observed:
|
||
- an offset voltage of $2.26\,V$
|
||
- the measured voltage shows an exponential decay response to the step input
|
||
|
||
This can be understood by the electrical schematic shown in Figure ref:fig:detail_instrumentation_force_sensor_adc were the ADC has some input impedance $R_i$ and input bias current $i_n$.
|
||
|
||
|
||
The input impedance $R_i$ of the ADC with the capacitance $C_p$ of the piezoelectric stack sensor forms an RC circuit with a time constant $\tau = R_i C_p$.
|
||
The charge generated by the piezoelectric effects across its capacitance is discharging into the input resistor of the ADC.
|
||
Therefore, the transfer function from the generated voltage $V_p$ to the measured voltage $V_{\text{ADC}}$ is a first order high filter, with time constant $\tau$.
|
||
The an exponential curve was fitted to the experimental data and a time constant $\tau = 6.5\,s$ was obtained.
|
||
With the capacitance of the piezoelectric sensor stack being $C_p = 4.4 \mu F$, the internal impedance of the Speedgoat ADC can be computed as follows $R_i = \frac{\tau}{C_p} = 1.5\,M\Omega$.
|
||
It is close to the specified value of $1\,M\Omega$ found in the datasheet
|
||
|
||
#+begin_src matlab :exports none :results none
|
||
%% Measured voltage accross the sensor stacks - Voltage steps are applied to the actuators
|
||
figure;
|
||
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
|
||
nexttile();
|
||
hold on;
|
||
plot(t, u, 'DisplayName', '$u$');
|
||
plot(t, v, 'DisplayName', '$V_s$');
|
||
plot(t_fit_1, y_fit_1, 'k--', 'DisplayName', 'fit');
|
||
plot(t_fit_2, y_fit_2, 'k--', 'HandleVisibility', 'off');
|
||
plot(t_fit_3, y_fit_3, 'k--', 'HandleVisibility', 'off');
|
||
hold off;
|
||
xlabel('Time [s]'); ylabel('Voltage [V]');
|
||
leg = legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1);
|
||
leg.ItemTokenSize(1) = 15;
|
||
xlim([0, 20]);
|
||
#+end_src
|
||
|
||
#+begin_src matlab :tangle no :exports results :results file none
|
||
exportFig('figs/detail_instrumentation_step_response_force_sensor.pdf', 'width', 'third', 'height', 300);
|
||
#+end_src
|
||
|
||
#+name: fig:detail_instrumentation_force_sensor
|
||
#+caption: Electrical schematic of the ADC measuring the piezoelectric force sensor (\subref{fig:detail_instrumentation_force_sensor_adc}), adapted from cite:reza06_piezoel_trans_vibrat_contr_dampin. Measured voltage $V_s$ while step voltages are generated for the actuator stacks (\subref{fig:detail_instrumentation_step_response_force_sensor}).
|
||
#+attr_latex: :options [htbp]
|
||
#+begin_figure
|
||
#+attr_latex: :caption \subcaption{\label{fig:detail_instrumentation_force_sensor_adc}Electrical Schematic}
|
||
#+attr_latex: :options {0.61\textwidth}
|
||
#+begin_subfigure
|
||
#+attr_latex: :scale 1
|
||
[[file:figs/detail_instrumentation_force_sensor_adc.png]]
|
||
#+end_subfigure
|
||
#+attr_latex: :caption \subcaption{\label{fig:detail_instrumentation_step_response_force_sensor}Measured Signals}
|
||
#+attr_latex: :options {0.35\textwidth}
|
||
#+begin_subfigure
|
||
#+attr_latex: :width 0.95\linewidth
|
||
[[file:figs/detail_instrumentation_step_response_force_sensor.png]]
|
||
#+end_subfigure
|
||
#+end_figure
|
||
|
||
The constant voltage offset can be explained by the input bias current $i_n$ of the ADC, represented in Figure ref:fig:detail_instrumentation_force_sensor_adc
|
||
At DC, the impedance of the piezoelectric stack is much larger that the input impedance of the ADC, and therefore the input bias current $i_n$ passing through the internal resistance $R_i$ produces a constant voltage offset $V_{\text{off}} = R_i i_n$.
|
||
The input bias current $i_n$ is estimated from $i_n = V_{\text{off}}/R_i = 1.5\,\mu A$.
|
||
|
||
In order to reduce the input voltage offset and to increase the corner frequency of the high pass filter, a resistor $R_p$ is added in parallel to the force sensor, as illustrated in Figure ref:fig:detail_instrumentation_force_sensor_adc_R
|
||
|
||
It has two effects:
|
||
- Reduction of input voltage offset:
|
||
\[ V_{\text{off}} = \frac{R_p R_{in}}{R_p + R_{in}} i_n \]
|
||
- Increase the high pass corner frequency $f_c$
|
||
\[ \frac{R_i R_p}{R_i + R_p} C_p = \tau_c = \frac{1}{2 \pi f_c} \]
|
||
\[ R_p = \frac{R_i}{2 \pi f_c C_p R_i - 1} \]
|
||
|
||
The resistor is chosen such that the high pass corner frequency is equal to $0.5\,\text{Hz}$.
|
||
This corresponds to a resistor of $R_p = 76\,k\Omega$.
|
||
With this parallel resistance value, the voltage offset would be $V_{\text{off}} = 0.11\,V$, which is much more acceptable.
|
||
|
||
To validate this, a resistor $R_p \approx 82\,k\Omega$ is then added in parallel with the force sensor as shown in Figure ref:fig:detail_instrumentation_force_sensor_adc_R.
|
||
After the resistor is added, the same steps response were performed (Figure ref:fig:detail_instrumentation_step_response_force_sensor_R).
|
||
And indeed, we obtain a much smaller offset voltage ($V_{\text{off}} = 0.15\,V$) and a much faster time constant ($\tau = 0.45\,s$).
|
||
This validates the model of the ADC and the effectiveness of the added resistor.
|
||
|
||
#+begin_src matlab
|
||
%% Read force sensor voltage with the ADC with added 82.7kOhm resistor
|
||
load('force_sensor_steps_R_82k7.mat', 't', 'encoder', 'u', 'v');
|
||
|
||
% Step times
|
||
t_s = [1.9, 6;
|
||
8.5, 13;
|
||
15.5, 21;
|
||
22.6, 26;
|
||
30.0, 36;
|
||
37.5, 41;
|
||
46.2, 49.5]; % [s]
|
||
|
||
tau = zeros(size(t_s, 1),1); % Time constant [s]
|
||
V0 = zeros(size(t_s, 1),1); % Offset voltage [V]
|
||
a = zeros(size(t_s, 1),1); %
|
||
|
||
for t_i = 1:size(t_s, 1)
|
||
t_cur = t(t_s(t_i, 1) < t & t < t_s(t_i, 2));
|
||
t_cur = t_cur - t_cur(1);
|
||
y_cur = v(t_s(t_i, 1) < t & t < t_s(t_i, 2));
|
||
|
||
nrmrsd = @(b) norm(y_cur - f_exp(b,t_cur)); % Residual Norm Cost Function
|
||
B0 = [0.5, 0.1, 2.2]; % Choose Appropriate Initial Estimates
|
||
[B,rnrm] = fminsearch(nrmrsd, B0); % Estimate Parameters ‘B’
|
||
|
||
a(t_i) = B(1);
|
||
tau(t_i) = 1/B(2);
|
||
V0(t_i) = B(3);
|
||
end
|
||
|
||
% Data to show the exponential fit
|
||
t_fit_1 = linspace(t_s(1,1), t_s(1,2), 100);
|
||
y_fit_1 = f_exp([a(1),1/tau(1),V0(1)], t_fit_1-t_s(1,1));
|
||
|
||
t_fit_2 = linspace(t_s(2,1), t_s(2,2), 100);
|
||
y_fit_2 = f_exp([a(2),1/tau(2),V0(2)], t_fit_2-t_s(2,1));
|
||
|
||
t_fit_3 = linspace(t_s(3,1), t_s(3,2), 100);
|
||
y_fit_3 = f_exp([a(3),1/tau(3),V0(3)], t_fit_3-t_s(3,1));
|
||
#+end_src
|
||
|
||
#+begin_src matlab :exports none :results none
|
||
%% Measured voltage accross the sensor stacks - Voltage steps are applied to the actuators
|
||
figure;
|
||
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
|
||
nexttile();
|
||
hold on;
|
||
plot(t, u, 'DisplayName', '$u$');
|
||
plot(t, v, 'DisplayName', '$V_s$');
|
||
plot(t_fit_1, y_fit_1, 'k--', 'DisplayName', 'fit');
|
||
plot(t_fit_2, y_fit_2, 'k--', 'HandleVisibility', 'off');
|
||
plot(t_fit_3, y_fit_3, 'k--', 'HandleVisibility', 'off');
|
||
hold off;
|
||
xlabel('Time [s]'); ylabel('Voltage [V]');
|
||
leg = legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1);
|
||
leg.ItemTokenSize(1) = 15;
|
||
xlim([0, 20]);
|
||
#+end_src
|
||
|
||
#+begin_src matlab :tangle no :exports results :results file none
|
||
exportFig('figs/detail_instrumentation_step_response_force_sensor_R.pdf', 'width', 'third', 'height', 300);
|
||
#+end_src
|
||
|
||
#+name: fig:detail_instrumentation_force_sensor_R
|
||
#+caption: Effect of an added resistor $R_p$ in parallel to the force sensor. The electrical schematic is shown in (\subref{fig:detail_instrumentation_force_sensor_adc_R}) and the measured signals in (\subref{fig:detail_instrumentation_step_response_force_sensor_R}).
|
||
#+attr_latex: :options [htbp]
|
||
#+begin_figure
|
||
#+attr_latex: :caption \subcaption{\label{fig:detail_instrumentation_force_sensor_adc_R}Electrical Schematic}
|
||
#+attr_latex: :options {0.61\textwidth}
|
||
#+begin_subfigure
|
||
#+attr_latex: :scale 1
|
||
[[file:figs/detail_instrumentation_force_sensor_adc_R.png]]
|
||
#+end_subfigure
|
||
#+attr_latex: :caption \subcaption{\label{fig:detail_instrumentation_step_response_force_sensor_R}Measured Signals}
|
||
#+attr_latex: :options {0.35\textwidth}
|
||
#+begin_subfigure
|
||
#+attr_latex: :width 0.95\linewidth
|
||
[[file:figs/detail_instrumentation_step_response_force_sensor_R.png]]
|
||
#+end_subfigure
|
||
#+end_figure
|
||
|
||
** Instrumentation Amplifier
|
||
|
||
Because the ADC noise may be too large to measure noise of other instruments (anything below $5.6\,\mu V/\sqrt{Hz}$ cannot be distinguish from the noise of the ADC itself), a low noise instrumentation amplifier can be used.
|
||
Here, a Femto DLPVA-101-B-S amplifier, with gains from 20dB up to 80dB, was used.
|
||
|
||
But first, the input[fn:detail_instrumentation_2] noise of the amplifier was characterized.
|
||
To do so, its input was short circuited with a $50\,\Omega$ resistor, and the output voltage was measured by the ADC (Figure ref:fig:detail_instrumentation_femto_meas_setup).
|
||
The maximum amplifier gain of 80dB (i.e. 10000) was used.
|
||
|
||
The measured voltage $n$ was then divided by 10000 to obtain the equivalent noise at the input of the voltage amplifier $n_a$.
|
||
In that case, the noise of the ADC $q_{ad}$ is negligible, thanks to the high gain used.
|
||
The obtained amplifier noise ASD $\Gamma_{n_a}$ and the (negligible) contribution of the ADC noise are shown in Figure ref:fig:detail_instrumentation_femto_input_noise.
|
||
|
||
It was also verified that the bandwidth of the instrumentation amplifier is much larger than 5kHz such that not phase drop are added by the use of the amplifier in the frequency band of interest.
|
||
|
||
#+begin_src latex :file detail_instrumentation_femto_meas_setup.pdf
|
||
\begin{tikzpicture}
|
||
\node[block={0.6cm}{0.6cm}] (const) {$0$};
|
||
% Pre Amp
|
||
\node[addb, right=0.4 of const] (addna) {};
|
||
\node[block, right=0.3 of addna] (Ga) {$G_a(s)$};
|
||
|
||
% ADC
|
||
\node[addb, right=0.8 of Ga] (addqad){};
|
||
\node[ADC, right=0.3 of addqad] (ADC) {ADC};
|
||
|
||
\draw[->] (const.east) -- (addna.west);
|
||
\draw[->] (addna.east) -- (Ga.west);
|
||
\draw[->] (Ga.east) -- (addqad.west);
|
||
\draw[->] (addqad.east) -- (ADC.west);
|
||
\draw[->] (ADC.east) -- node[sloped]{$/$} ++(0.8, 0) node[above left]{$n$};
|
||
|
||
\draw[<-] (addna.north) -- ++(0, 0.6) node[below right](na){$n_{a}$};
|
||
\draw[<-] (addqad.north) -- ++(0, 0.6) node[below right](qad){$q_{ad}$};
|
||
|
||
\coordinate[] (top) at (na.north);
|
||
\coordinate[] (bot) at (Ga.south);
|
||
|
||
% 5113
|
||
\begin{scope}[on background layer]
|
||
\node[fit={(addna.west|-bot) (Ga.east|-top)}, inner sep=4pt, draw, dashed, fill=colorgreen!20!white] (P) {};
|
||
\node[above] at (P.north) {Pre Amp};
|
||
\end{scope}
|
||
|
||
% ADC
|
||
\begin{scope}[on background layer]
|
||
\node[fit={(addqad.west|-bot) (ADC.east|-top)}, inner sep=4pt, draw, dashed, fill=coloryellow!20!white] (P) {};
|
||
\node[above] at (P.north) {ADC};
|
||
\end{scope}
|
||
\end{tikzpicture}
|
||
#+end_src
|
||
|
||
#+begin_src matlab :exports none
|
||
%% Femto Input Voltage Noise
|
||
femto = load('noise_femto.mat', 't', 'Vout', 'notes'); % Load Data
|
||
|
||
% Compute the equivalent voltage at the input of the amplifier
|
||
femto.Vout = femto.Vout/femto.notes.pre_amp.gain;
|
||
femto.Vout = femto.Vout - mean(femto.Vout);
|
||
|
||
Ts = (femto.t(end) - femto.t(1))/(length(femto.t) - 1);
|
||
Nfft = floor(1/Ts);
|
||
win = hanning(Nfft);
|
||
Noverlap = floor(Nfft/2);
|
||
% Power Spectral Density
|
||
[pxx, f] = pwelch(detrend(femto.Vout, 0), win, Noverlap, Nfft, 1/Ts);
|
||
|
||
% Save the results inside the struct
|
||
femto.pxx = pxx(f<=5e3);
|
||
femto.f = f(f<=5e3);
|
||
#+end_src
|
||
|
||
#+begin_src matlab :exports none :results none
|
||
%% Measured input voltage noise of the Femto voltage pre-amplifier
|
||
figure;
|
||
hold on;
|
||
plot(femto.f, sqrt(femto.pxx), 'color', colors(5,:), 'DisplayName', '$\Gamma_{n_a}$');
|
||
plot(adc.f, sqrt(adc.pxx)./femto.notes.pre_amp.gain, 'color', colors(3,:), 'DisplayName', '$\Gamma_{q_{ad}}/|G_a|$')
|
||
hold off;
|
||
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
|
||
xlabel('Frequency [Hz]'); ylabel('ASD [$V/\sqrt{Hz}$]');
|
||
legend('location', 'northeast');
|
||
xlim([1, 5e3]); ylim([2e-10, 1e-7]);
|
||
xticks([1e0, 1e1, 1e2, 1e3]);
|
||
yticks([1e-9, 1e-8]);
|
||
#+end_src
|
||
|
||
#+begin_src matlab :tangle no :exports results :results file none
|
||
exportFig('figs/detail_instrumentation_femto_input_noise.pdf', 'width', 'half', 'height', 350);
|
||
#+end_src
|
||
|
||
#+attr_latex: :options [b]{0.48\linewidth}
|
||
#+begin_minipage
|
||
#+name: fig:detail_instrumentation_femto_meas_setup
|
||
#+caption: Measurement of the instrumentation amplifier input voltage noise
|
||
#+attr_latex: :scale 1 :float nil
|
||
[[file:figs/detail_instrumentation_femto_meas_setup.png]]
|
||
#+end_minipage
|
||
\hfill
|
||
#+attr_latex: :options [b]{0.48\linewidth}
|
||
#+begin_minipage
|
||
#+name: fig:detail_instrumentation_femto_input_noise
|
||
#+caption: Obtained ASD of the instrumentation amplifier input voltage noise
|
||
#+attr_latex: :scale 1 :float nil
|
||
[[file:figs/detail_instrumentation_femto_input_noise.png]]
|
||
#+end_minipage
|
||
|
||
** Digital to Analog Converters
|
||
**** Output Voltage Noise
|
||
In order to measure the output noise of the DAC, the measurement setup schematically represented in Figure ref:fig:detail_instrumentation_dac_setup was used.
|
||
The DAC was instructed to output constant voltage (here zero), and the gain of the pre-amplifier is adjusted such that the measured amplified noise is much larger than the quantization noise of the ADC.
|
||
|
||
The Amplitude Spectral Density $\Gamma_{n_{da}}(\omega)$ of the measured signal was computed and it was verified the contribution of the ADC noise and amplifier noise are negligible.
|
||
|
||
#+begin_src latex :file detail_instrumentation_dac_setup.pdf
|
||
\begin{tikzpicture}
|
||
\node[block={0.6cm}{0.6cm}] (const) {$0$};
|
||
|
||
% DAC
|
||
\node[DAC, right=0.4 of const] (DAC) {DAC};
|
||
\node[addb, right=0.3 of DAC] (addnda){};
|
||
|
||
% Pre Amp
|
||
\node[addb, right=0.8 of addnda] (addna) {};
|
||
\node[block, right=0.3 of addna] (Ga) {$G_a(s)$};
|
||
|
||
% ADC
|
||
\node[addb, right=0.8 of Ga] (addqad){};
|
||
\node[ADC, right=0.3 of addqad] (ADC) {ADC};
|
||
|
||
\draw[->] (const.east) -- node[sloped]{$/$} (DAC.west);
|
||
\draw[->] (DAC.east) -- (addnda.west);
|
||
\draw[->] (addnda.east) -- (addna.west);
|
||
\draw[->] (addna.east) -- (Ga.west);
|
||
\draw[->] (Ga.east) -- (addqad.west);
|
||
\draw[->] (addqad.east) -- (ADC.west);
|
||
\draw[->] (ADC.east) -- node[sloped]{$/$} ++(0.8, 0);
|
||
|
||
\draw[<-] (addnda.north) -- ++(0, 0.6) node[below left](nda){$n_{da}$};
|
||
\draw[<-] (addna.north) -- ++(0, 0.6) node[below right](na){$n_{a}$};
|
||
\draw[<-] (addqad.north) -- ++(0, 0.6) node[below right](qad){$q_{ad}$};
|
||
|
||
\coordinate[] (top) at (na.north);
|
||
\coordinate[] (bot) at (Ga.south);
|
||
|
||
% DAC
|
||
\begin{scope}[on background layer]
|
||
\node[fit={(DAC.west|-bot) (addnda.east|-top)}, inner sep=4pt, draw, dashed, fill=colorblue!20!white] (P) {};
|
||
\node[above] at (P.north) {DAC};
|
||
\end{scope}
|
||
|
||
% 5113
|
||
\begin{scope}[on background layer]
|
||
\node[fit={(addna.west|-bot) (Ga.east|-top)}, inner sep=4pt, draw, dashed, fill=colorgreen!20!white] (P) {};
|
||
\node[above] at (P.north) {Pre Amp};
|
||
\end{scope}
|
||
|
||
% ADC
|
||
\begin{scope}[on background layer]
|
||
\node[fit={(addqad.west|-bot) (ADC.east|-top)}, inner sep=4pt, draw, dashed, fill=coloryellow!20!white] (P) {};
|
||
\node[above] at (P.north) {ADC};
|
||
\end{scope}
|
||
\end{tikzpicture}
|
||
#+end_src
|
||
|
||
#+name: fig:detail_instrumentation_dac_setup
|
||
#+caption: Measurement of the DAC output voltage noise. A pre-amplifier with a gain of 1000 is used before measuring the signal with the ADC.
|
||
#+RESULTS:
|
||
[[file:figs/detail_instrumentation_dac_setup.png]]
|
||
|
||
#+begin_src matlab :exports none
|
||
%% DAC Output Voltage Noise
|
||
dac = load('mat/noise_dac.mat', 't', 'Vn', 'notes');
|
||
|
||
% Take input acount the gain of the pre-amplifier
|
||
dac.Vn = dac.Vn/dac.notes.pre_amp.gain;
|
||
dac.Vn = dac.Vn - mean(dac.Vn);
|
||
|
||
Ts = (dac.t(end) - dac.t(1))/(length(dac.t) - 1);
|
||
Nfft = floor(1/Ts);
|
||
win = hanning(Nfft);
|
||
Noverlap = floor(Nfft/2);
|
||
% Identification of the transfer function from Va to di
|
||
[pxx, f] = pwelch(dac.Vn, win, Noverlap, Nfft, 1/Ts);
|
||
|
||
dac.pxx = pxx(f<=5e3);
|
||
dac.f = f(f<=5e3);
|
||
|
||
% Estimated mean ASD
|
||
sprintf('Mean ASD of the DAC: %.1f uV/sqrt(Hz)', 1e6*sqrt(mean(dac.pxx)))
|
||
sprintf('Specifications: %.1f uV/sqrt(Hz)', 1e6*max_dac_asd)
|
||
|
||
% Estimated RMS
|
||
sprintf('RMS of the DAC: %.2f mV RMS', 1e3*rms(dac.Vn))
|
||
sprintf('RMS specifications: %.2f mV RMS', max_dac_rms)
|
||
#+end_src
|
||
|
||
The obtained Amplitude Spectral Density of the DAC's output voltage is shown in Figure ref:fig:detail_instrumentation_dac_output_noise.
|
||
It is almost white noise with an ASD of 0.6uV/sqrt(Hz).
|
||
There is a little bit of 50Hz, and some low frequency noise (thermal noise?) which are not foreseen to be an issue as it will be inside the bandwidth.
|
||
Note that all channels are measuring the same, so only one channel is shown here.
|
||
|
||
#+begin_src matlab :exports none
|
||
figure;
|
||
tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None');
|
||
|
||
ax1 = nexttile();
|
||
hold on;
|
||
plot(femto.f, sqrt(femto.pxx), 'color', colors(5,:), 'DisplayName', '$\Gamma_{n_a}$');
|
||
plot(dac.f, sqrt(dac.pxx), 'color', colors(1,:), 'DisplayName', '$\Gamma_{n_{da}}$');
|
||
plot([dac.f(2), dac.f(end)], [max_dac_asd, max_dac_asd], '--', 'color', colors(1,:), 'DisplayName', 'DAC specs')
|
||
plot(adc.f, sqrt(adc.pxx)./dac.notes.pre_amp.gain, 'color', colors(3,:), 'DisplayName', '$\Gamma_{q_{ad}}/|G_a|$')
|
||
hold off;
|
||
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
|
||
xlabel('Frequency [Hz]'); ylabel('ASD [$V/\sqrt{Hz}$]');
|
||
leg = legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1);
|
||
leg.ItemTokenSize(1) = 15;
|
||
xlim([1, 5e3]); ylim([2e-10, 4e-4]);
|
||
xticks([1e0, 1e1, 1e2, 1e3]);
|
||
#+end_src
|
||
|
||
#+begin_src matlab :tangle no :exports results :results file none
|
||
exportFig('figs/detail_instrumentation_dac_output_noise.pdf', 'width', 'half', 'height', 'normal');
|
||
#+end_src
|
||
|
||
**** Delay from ADC to DAC
|
||
In order to measure the transfer function from DAC to ADC and verify that the bandwidth of both instrument is high enough, the DAC output was directly wired to the ADC input.
|
||
A white noise signal was generated by the DAC, and the ADC signal was recorded.
|
||
The obtained frequency response function from the digital DAC signal to the digital ADC signal is shown in (Figure ref:fig:detail_instrumentation_dac_adc_tf).
|
||
It corresponds to 1 sample delay, which is corresponding to the specifications.
|
||
|
||
#+begin_src matlab
|
||
%% Measure transfer function from DAC to ADC
|
||
data_dac_adc = load("2023-08-22_15-52_io131_dac_to_adc.mat");
|
||
|
||
% Frequency analysis parameters
|
||
Ts = 1e-4; % Sampling Time [s]
|
||
Nfft = floor(1.0/Ts);
|
||
win = hanning(Nfft);
|
||
Noverlap = floor(Nfft/2);
|
||
|
||
[G_dac_adc, f] = tfestimate(data_dac_adc.dac_1, data_dac_adc.adc_1, win, Noverlap, Nfft, 1/Ts);
|
||
|
||
%
|
||
G_delay = exp(-Ts*s);
|
||
#+end_src
|
||
|
||
#+begin_src matlab :exports none :results none
|
||
%% Measure transfer function from DAC to ADC - It fits a pure "1-sample" delay
|
||
figure;
|
||
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
|
||
|
||
ax1 = nexttile([2,1]);
|
||
hold on;
|
||
plot(f, abs(G_dac_adc), 'color', colors(2,:), 'DisplayName', 'Measurement');
|
||
plot(f, abs(squeeze(freqresp(G_delay, f, 'Hz'))), 'k--', 'DisplayName', 'Pure Delay');
|
||
hold off;
|
||
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
|
||
ylabel('Amplitude [V/V]'); set(gca, 'XTickLabel',[]);
|
||
ylim([1e-1, 1e1]);
|
||
leg = legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1);
|
||
leg.ItemTokenSize(1) = 15;
|
||
|
||
ax2 = nexttile();
|
||
hold on;
|
||
plot(f, 180/pi*unwrap(angle(G_dac_adc)), 'color', colors(2,:));
|
||
plot(f, 180/pi*unwrap(angle(squeeze(freqresp(G_delay, f, 'Hz')))), 'k--', 'DisplayName', 'Pure Delay');
|
||
hold off;
|
||
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
|
||
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
|
||
hold off;
|
||
yticks(-360:90:360);
|
||
ylim([-200, 20])
|
||
|
||
linkaxes([ax1,ax2],'x');
|
||
xlim([1, 5e3]);
|
||
xticks([1e0, 1e1, 1e2, 1e3]);
|
||
#+end_src
|
||
|
||
#+begin_src matlab :tangle no :exports results :results file replace
|
||
exportFig('figs/detail_instrumentation_dac_adc_tf.pdf', 'width', 'half', 'height', 'normal');
|
||
#+end_src
|
||
|
||
#+name: fig:detail_instrumentation_dac
|
||
#+caption: Measurement of the output voltage noise of the ADC (\subref{fig:detail_instrumentation_dac_output_noise}) and measured transfer function from DAC to ADC (\subref{fig:detail_instrumentation_dac_adc_tf}) which corresponds to a "1-sample" delay.
|
||
#+attr_latex: :options [htbp]
|
||
#+begin_figure
|
||
#+attr_latex: :caption \subcaption{\label{fig:detail_instrumentation_dac_output_noise}Output noise of the DAC}
|
||
#+attr_latex: :options {0.48\textwidth}
|
||
#+begin_subfigure
|
||
#+attr_latex: :width 0.95\linewidth
|
||
[[file:figs/detail_instrumentation_dac_output_noise.png]]
|
||
#+end_subfigure
|
||
#+attr_latex: :caption \subcaption{\label{fig:detail_instrumentation_dac_adc_tf}Transfer function from DAC to ADC}
|
||
#+attr_latex: :options {0.48\textwidth}
|
||
#+begin_subfigure
|
||
#+attr_latex: :width 0.95\linewidth
|
||
[[file:figs/detail_instrumentation_dac_adc_tf.png]]
|
||
#+end_subfigure
|
||
#+end_figure
|
||
|
||
** Piezoelectric Voltage Amplifier
|
||
**** Output Voltage Noise
|
||
The measurement setup is shown in Figure ref:fig:detail_instrumentation_pd200_setup.
|
||
The input of the PD200 amplifier is shunted with a $50\,\Omega$ resistor such that only the noise of the amplifier itself is measured.
|
||
The gain of the pre-amplifier was increased in order to measure a signal much larger than the noise of the ADC.
|
||
Two piezoelectric stacks of the APA95ML were connected to the PD200 output to provide an appropriate load.
|
||
|
||
#+begin_src latex :file detail_instrumentation_pd200_setup.pdf
|
||
\begin{tikzpicture}
|
||
\node[block={0.6cm}{0.6cm}] (const) {$0$};
|
||
|
||
% PD200
|
||
\node[block, right=0.4 of const] (Gp){$G_p(s)$};
|
||
\node[addb, right=0.3 of Gp] (addnp){};
|
||
|
||
% Pre Amp
|
||
\node[addb, right=0.8 of addnp] (addna) {};
|
||
\node[block, right=0.3 of addna] (Ga) {$G_a(s)$};
|
||
|
||
% ADC
|
||
\node[addb, right=0.8 of Ga] (addqad){};
|
||
\node[ADC, right=0.3 of addqad] (ADC) {ADC};
|
||
|
||
\draw[->] (const.east) -- (Gp.west);
|
||
\draw[->] (Gp.east) -- (addnp.west);
|
||
\draw[->] (addnp.east) -- (addna.west);
|
||
\draw[->] (addna.east) -- (Ga.west);
|
||
\draw[->] (Ga.east) -- (addqad.west);
|
||
\draw[->] (addqad.east) -- (ADC.west);
|
||
\draw[->] (ADC.east) -- node[sloped]{$/$} ++(0.8, 0) node[above left]{$n$};
|
||
|
||
\draw[<-] (addnp.north) -- ++(0, 0.6) node[below left](np){$n_{p}$};
|
||
\draw[<-] (addna.north) -- ++(0, 0.6) node[below right](na){$n_{a}$};
|
||
\draw[<-] (addqad.north) -- ++(0, 0.6) node[below right](qad){$q_{ad}$};
|
||
|
||
\coordinate[] (top) at (na.north);
|
||
\coordinate[] (bot) at (Ga.south);
|
||
|
||
% PD200
|
||
\begin{scope}[on background layer]
|
||
\node[fit={(addnp.east|-bot) (Gp.west|-top)}, inner sep=4pt, draw, dashed, fill=colorred!20!white] (P) {};
|
||
\node[above] at (P.north) {PD200};
|
||
\end{scope}
|
||
|
||
% 5113
|
||
\begin{scope}[on background layer]
|
||
\node[fit={(addna.west|-bot) (Ga.east|-top)}, inner sep=4pt, draw, dashed, fill=colorgreen!20!white] (P) {};
|
||
\node[above] at (P.north) {Pre Amp};
|
||
\end{scope}
|
||
|
||
% ADC
|
||
\begin{scope}[on background layer]
|
||
\node[fit={(addqad.west|-bot) (ADC.east|-top)}, inner sep=4pt, draw, dashed, fill=coloryellow!20!white] (P) {};
|
||
\node[above] at (P.north) {ADC};
|
||
\end{scope}
|
||
\end{tikzpicture}
|
||
#+end_src
|
||
|
||
#+name: fig:detail_instrumentation_pd200_setup
|
||
#+caption: Setup used to measured the output voltage noise of the PD200 voltage amplifier. A gain $G_a = 1000$ was used for the instrumentation amplifier.
|
||
#+RESULTS:
|
||
[[file:figs/detail_instrumentation_pd200_setup.png]]
|
||
|
||
The Amplitude Spectral Density $\Gamma_n(\omega)$ of the measured signal by the ADC is computed.
|
||
The Amplitude Spectral Density of the output voltage noise of the PD200 amplifier $n_p$ is then computed taking into account the gain of the pre-amplifier:
|
||
\begin{equation}
|
||
\Gamma_{n_p}(\omega) = \frac{\Gamma_n(\omega)}{|G_p(j\omega) G_a(j\omega)|}
|
||
\end{equation}
|
||
|
||
The Amplitude Spectral Density of the measured output noise of the PD200 is computed and shown in Figure ref:fig:detail_instrumentation_pd200_noise.
|
||
It is verified that the contribution of the PD200 noise is much larger than the contribution of the pre-amplifier noise of the quantization noise (i.e. what is measured is indeed the PD200 noise).
|
||
Here, the measured noise of the six received amplifiers are all shown.
|
||
|
||
The Amplitude Spectral Density of the output voltage noise of the PD200 amplifiers present sharp peaks.
|
||
The reason for all these peaks is not clear, but as their amplitude are bellow the specifications, it should not pose any issue.
|
||
|
||
#+begin_src matlab :exports none
|
||
%% PD200 Output Voltage Noise
|
||
% Load all the measurements
|
||
pd200 = {};
|
||
for i = 1:6
|
||
pd200(i) = {load(['mat/noise_PD200_' num2str(i) '_10uF.mat'], 't', 'Vout', 'notes')};
|
||
end
|
||
|
||
% Take into account the pre-amplifier gain
|
||
for i = 1:6
|
||
pd200{i}.Vout = pd200{i}.Vout/pd200{i}.notes.pre_amp.gain;
|
||
end
|
||
|
||
% Sampling time / frequency
|
||
Ts = (pd200{1}.t(end) - pd200{1}.t(1))/(length(pd200{1}.t) - 1);
|
||
|
||
% Compute the PSD of the measured noise
|
||
Nfft = floor(1/Ts);
|
||
win = hanning(Nfft);
|
||
Noverlap = floor(Nfft/2);
|
||
|
||
for i = 1:6
|
||
% Identification of the transfer function from Va to di
|
||
[pxx, f] = pwelch(pd200{i}.Vout, win, Noverlap, Nfft, 1/Ts);
|
||
pd200{i}.pxx = pxx(f<=5e3);
|
||
pd200{i}.f = f(f<=5e3);
|
||
end
|
||
|
||
% Estimated RMS
|
||
sprintf('RMS of the PD200: %.2f mV RMS', 1e3*rms(detrend(pd200{1}.Vout,0)))
|
||
sprintf('RMS specifications: %.2f mV RMS', max_amp_rms)
|
||
#+end_src
|
||
|
||
#+begin_src matlab :exports none :results none
|
||
%% Measured output voltage noise of the PD200 amplifiers
|
||
figure;
|
||
hold on;
|
||
plot([1 Fs/2], [max_amp_asd, max_amp_asd], '--', 'color', colors(2,:), 'DisplayName', 'Specs')
|
||
plot(pd200{1}.f, sqrt(pd200{1}.pxx), 'color', [colors(2, :), 0.5], 'DisplayName', '$\Gamma_{n_p}$');
|
||
for i = 2:6
|
||
plot(pd200{i}.f, sqrt(pd200{i}.pxx), 'color', [colors(2, :), 0.5], 'HandleVisibility', 'off');
|
||
end
|
||
plot(femto.f, sqrt(femto.pxx), 'color', [colors(5, :)], 'DisplayName', '$\Gamma_{n_a}$');
|
||
plot(adc.f, sqrt(adc.pxx)./pd200{1}.notes.pre_amp.gain, 'color', colors(3,:), 'DisplayName', '$\Gamma_{q_{ad}}/|G_a|$')
|
||
hold off;
|
||
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
|
||
xlabel('Frequency [Hz]'); ylabel('ASD [$V/\sqrt{Hz}$]');
|
||
leg = legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1);
|
||
leg.ItemTokenSize(1) = 15;
|
||
ylim([1e-10, 4e-4]); xlim([1, 5e3]);
|
||
xticks([1e0, 1e1, 1e2, 1e3])
|
||
#+end_src
|
||
|
||
#+begin_src matlab :tangle no :exports results :results file replace
|
||
exportFig('figs/detail_instrumentation_pd200_noise.pdf', 'width', 'wide', 'height', 'normal');
|
||
#+end_src
|
||
|
||
#+name: fig:detail_instrumentation_pd200_noise
|
||
#+caption: Measured output voltage noise of the PD200 amplifiers
|
||
#+RESULTS:
|
||
[[file:figs/detail_instrumentation_pd200_noise.png]]
|
||
|
||
**** Small Signal Bandwidth
|
||
|
||
Here the small signal dynamics of all the PD200 amplifiers are identified.
|
||
|
||
A (logarithmic) sweep sine excitation voltage is generated by the Speedgoat DAC with an amplitude of 0.1V and a frequency going from 1Hz up to 5kHz.
|
||
|
||
The output voltage of the PD200 amplifier is measured thanks to the monitor voltage of the PD200 amplifier.
|
||
The input voltage of the PD200 amplifier (the generated voltage by the DAC) is measured with another ADC of the Speedgoat.
|
||
This way, the time delay related to the ADC will not be apparent in the results.
|
||
|
||
All six received amplifiers are measuring the same regarding their transfer functions.
|
||
The amplitude is constant over a wide frequency band and the phase drop is limited to less than 1 degree up to 500Hz, which is well within the specifications.
|
||
|
||
The identified dynamics in Figure ref:fig:detail_instrumentation_pd200_tf can very well be modeled with a first order low pass filter or even a simple constant.
|
||
|
||
#+begin_src matlab :exports none
|
||
%% Load all the measurements
|
||
pd200 = {};
|
||
for i = 1:6
|
||
pd200(i) = {load(['tf_pd200_' num2str(i) '_10uF_small_signal.mat'], 't', 'Vin', 'Vout', 'notes')};
|
||
end
|
||
|
||
% Compute sampling Frequency
|
||
Ts = (pd200{1}.t(end) - pd200{1}.t(1))/(length(pd200{1}.t)-1);
|
||
|
||
% Compute all the transfer functions
|
||
Nfft = floor(1.0/Ts);
|
||
win = hanning(Nfft);
|
||
Noverlap = floor(Nfft/2);
|
||
|
||
for i = 1:length(pd200)
|
||
[tf_est, f] = tfestimate(pd200{i}.Vin, 20*pd200{i}.Vout, win, Noverlap, Nfft, 1/Ts);
|
||
pd200{i}.tf = tf_est(f<=5e3);
|
||
pd200{i}.f = f(f<=5e3);
|
||
end
|
||
|
||
% Amplified model
|
||
Gp = 20/(1 + s/2/pi/25e3);
|
||
#+end_src
|
||
|
||
#+begin_src matlab :exports none
|
||
figure;
|
||
tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None');
|
||
|
||
ax1 = nexttile([2,1]);
|
||
hold on;
|
||
plot(pd200{1}.f, abs(pd200{1}.tf), '-', 'color', [colors(2,:), 0.5], 'linewidth', 2.5, 'DisplayName', 'Measurement')
|
||
plot(pd200{1}.f, abs(squeeze(freqresp(Gp, pd200{1}.f, 'Hz'))), '--', 'color', colors(2,:), 'DisplayName', 'Model')
|
||
hold off;
|
||
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
|
||
ylabel('Magnitude [V/V]'); set(gca, 'XTickLabel',[]);
|
||
hold off;
|
||
ylim([1, 1e2]);
|
||
leg = legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1);
|
||
leg.ItemTokenSize(1) = 15;
|
||
|
||
ax2 = nexttile;
|
||
hold on;
|
||
plot(pd200{1}.f, 180/pi*unwrap(angle(pd200{1}.tf)), '-', 'color', [colors(2,:), 0.5], 'linewidth', 2.5)
|
||
plot(pd200{1}.f, 180/pi*unwrap(angle(squeeze(freqresp(Gp, pd200{1}.f, 'Hz')))), '--', 'color', colors(2,:))
|
||
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
|
||
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
|
||
hold off;
|
||
yticks(-360:2:360);
|
||
ylim([-13, 1]);
|
||
|
||
linkaxes([ax1,ax2],'x');
|
||
xlim([1, 5e3]);
|
||
#+end_src
|
||
|
||
#+begin_src matlab :tangle no :exports results :results file replace
|
||
exportFig('figs/detail_instrumentation_pd200_tf.pdf', 'width', 'wide', 'height', 600);
|
||
#+end_src
|
||
|
||
#+name: fig:detail_instrumentation_pd200_tf
|
||
#+caption: Identified dynamics from input voltage to output voltage
|
||
#+RESULTS:
|
||
[[file:figs/detail_instrumentation_pd200_tf.png]]
|
||
|
||
** Linear Encoders
|
||
|
||
To measure the noise $n$ of the encoder, one can rigidly fix the head and the ruler together such that no motion should be measured.
|
||
Then, the measured signal $y_m$ corresponds to the noise $n$.
|
||
|
||
The measurement bench is shown in Figure ref:fig:detail_instrumentation_vionic_bench.
|
||
Note that the bench is then covered with a "plastic bubble sheet" in order to keep disturbances as small as possible.
|
||
Then, and for all the six encoders, the measured motion during 100s with a sampling frequency of 20kHz.
|
||
|
||
The obtained amplitude spectral density of the measured displacement (i.e. measurement noise) is shown in Figure ref:fig:detail_instrumentation_vionic_asd.
|
||
It corresponds to a white noise, with an amplitude $\approx 1\,nm\,\text{RMS}$.
|
||
|
||
#+begin_src matlab :exports none
|
||
%% Load all the measurements
|
||
enc = {};
|
||
for i = 1:6
|
||
enc(i) = {load(['mat/noise_meas_100s_20kHz_' num2str(i) '.mat'], 't', 'x')};
|
||
end
|
||
|
||
% Compute sampling Frequency
|
||
Ts = (enc{1}.t(end) - enc{1}.t(1))/(length(enc{1}.t)-1);
|
||
Nfft = floor(1.0/Ts);
|
||
win = hanning(Nfft);
|
||
Noverlap = floor(Nfft/2);
|
||
|
||
for i = 1:length(enc)
|
||
[pxx, f] = pwelch(detrend(enc{i}.x, 0), win, Noverlap, Nfft, 1/Ts);
|
||
enc{i}.pxx = pxx(f<=5e3);
|
||
enc{i}.pxx(2) = enc{i}.pxx(3); % Remove first point which corresponds to drifts
|
||
enc{i}.f = f(f<=5e3);
|
||
end
|
||
#+end_src
|
||
|
||
#+begin_src matlab :exports none :results none
|
||
%% Measured Amplitude Spectral Density of the encoder position noise
|
||
figure;
|
||
hold on;
|
||
plot(enc{1}.f, sqrt(enc{1}.pxx), 'color', colors(4,:));
|
||
hold off;
|
||
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
|
||
xlabel('Frequency [Hz]'); ylabel('ASD [$m/\sqrt{Hz}$]');
|
||
xlim([1, 5e3]); ylim([1e-12, 1e-8]);
|
||
#+end_src
|
||
|
||
#+begin_src matlab :tangle no :exports results :results file replace
|
||
exportFig('figs/detail_instrumentation_vionic_asd.pdf', 'width', 'half', 'height', 'normal');
|
||
#+end_src
|
||
|
||
#+attr_latex: :options [b]{0.48\linewidth}
|
||
#+begin_minipage
|
||
#+name: fig:detail_instrumentation_vionic_bench
|
||
#+caption: Test bench used to measured the encoder noise
|
||
#+attr_latex: :width 0.95\linewidth :float nil
|
||
[[file:figs/detail_instrumentation_vionic_bench.jpg]]
|
||
#+end_minipage
|
||
\hfill
|
||
#+attr_latex: :options [b]{0.48\linewidth}
|
||
#+begin_minipage
|
||
#+name: fig:detail_instrumentation_vionic_asd
|
||
#+caption: Measured Amplitude Spectral Density of the encoder noise
|
||
#+attr_latex: :width 0.95\linewidth :float nil
|
||
[[file:figs/detail_instrumentation_vionic_asd.png]]
|
||
#+end_minipage
|
||
|
||
** Noise budgeting from measured instrumentation noise
|
||
|
||
Once all the instrumentation noise were characterized, the effect of the instrumentation noise on the sample's vibration can be assessed using the multi-body model.
|
||
|
||
The obtained vertical motion induced by the ADC noise, DAC noise and voltage amplifier noise is displayed in Figure ref:fig:detail_instrumentation_cl_noise_budget (the effect of encoder noise is negligible).
|
||
|
||
The total motion induced by all the noise sources is around $1.5\,nm$ which is well within the specifications.
|
||
|
||
#+begin_src matlab
|
||
%% Estimate the resulting errors induced by noise of instruments
|
||
f = dac.f;
|
||
|
||
% Vertical direction
|
||
psd_z_dac = 6*(abs(squeeze(freqresp(Gd('z', 'nda1' ), f, 'Hz'))).^2).*dac.pxx;
|
||
psd_z_adc = 6*(abs(squeeze(freqresp(Gd('z', 'nad1' ), f, 'Hz'))).^2).*adc.pxx;
|
||
psd_z_amp = 6*(abs(squeeze(freqresp(Gd('z', 'namp1'), f, 'Hz'))).^2).*pd200{1}.pxx;
|
||
psd_z_enc = 6*(abs(squeeze(freqresp(Gd('z', 'ddL1' ), f, 'Hz'))).^2).*enc{1}.pxx;
|
||
psd_z_tot = psd_z_dac + psd_z_adc + psd_z_amp + psd_z_enc;
|
||
|
||
rms_z_dac = sqrt(trapz(f, psd_z_dac));
|
||
rms_z_adc = sqrt(trapz(f, psd_z_adc));
|
||
rms_z_amp = sqrt(trapz(f, psd_z_amp));
|
||
rms_z_enc = sqrt(trapz(f, psd_z_enc));
|
||
rms_z_tot = sqrt(trapz(f, psd_z_tot));
|
||
|
||
% Lateral direction
|
||
psd_y_dac = 6*(abs(squeeze(freqresp(Gd('y', 'nda1' ), f, 'Hz'))).^2).*dac.pxx;
|
||
psd_y_adc = 6*(abs(squeeze(freqresp(Gd('y', 'nad1' ), f, 'Hz'))).^2).*adc.pxx;
|
||
psd_y_amp = 6*(abs(squeeze(freqresp(Gd('y', 'namp1'), f, 'Hz'))).^2).*pd200{1}.pxx;
|
||
psd_y_enc = 6*(abs(squeeze(freqresp(Gd('y', 'ddL1' ), f, 'Hz'))).^2).*enc{1}.pxx;
|
||
psd_y_tot = psd_y_dac + psd_y_adc + psd_y_amp + psd_y_enc;
|
||
|
||
rms_y_tot = sqrt(trapz(f, psd_y_tot));
|
||
#+end_src
|
||
|
||
#+begin_src matlab :exports none :results none
|
||
%% Closed-loop noise budgeting using measured noise of instrumentation
|
||
figure;
|
||
hold on;
|
||
plot(f, sqrt(psd_z_amp), 'color', [colors(2,:)], 'linewidth', 2.5, 'DisplayName', 'PD200');
|
||
plot(f, sqrt(psd_z_dac), 'color', [colors(1,:)], 'linewidth', 2.5, 'DisplayName', 'DAC')
|
||
plot(f, sqrt(psd_z_adc), 'color', [colors(3,:)], 'linewidth', 2.5, 'DisplayName', 'ADC')
|
||
plot(f, sqrt(psd_z_tot), 'k-', 'DisplayName', sprintf('Total: %.1f nm RMS', 1e9*rms_z_tot));
|
||
hold off;
|
||
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
|
||
xlabel('Frequency [Hz]'); ylabel('ASD [$m/\sqrt{Hz}$]');
|
||
leg = legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1);
|
||
leg.ItemTokenSize(1) = 15;
|
||
xlim([1, 5e3]); ylim([1e-14, 1e-9]);
|
||
xticks([1e0, 1e1, 1e2, 1e3]);
|
||
#+end_src
|
||
|
||
#+begin_src matlab :tangle no :exports results :results file replace
|
||
exportFig('figs/detail_instrumentation_cl_noise_budget.pdf', 'width', 'wide', 'height', 'normal');
|
||
#+end_src
|
||
|
||
#+name: fig:detail_instrumentation_cl_noise_budget
|
||
#+caption: Closed-loop noise budgeting using measured noise of instrumentation
|
||
#+RESULTS:
|
||
[[file:figs/detail_instrumentation_cl_noise_budget.png]]
|
||
|
||
* Conclusion
|
||
:PROPERTIES:
|
||
:UNNUMBERED: t
|
||
:END:
|
||
<<sec:detail_instrumentation_conclusion>>
|
||
|
||
- thanks to multi-body model in which it is easy to include instrumentation and noise sources
|
||
From specification on the sample's vertical motion (most stringent requirement), specification for each noise source was extracted.
|
||
- based on those specifications, adequate instrumentation were chosen.
|
||
for some instrumentation, it was difficult to choose only based on data-sheets are manufacturers often don't share relevant information for noise budgets, such as amplitude spectral densities
|
||
- then, the instrumentation was procured and tested individually.
|
||
All were found to comply with the requirements.
|
||
Finally, based on the measured noise of all instrumentation, the expected sample's vibration induced by all the noise sources was estimated and found to comply with the requirements.
|
||
|
||
* 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/');
|
||
addpath('./matlab/src/'); % Path for scripts
|
||
addpath('./matlab/mat/'); % Path for data
|
||
addpath('./matlab/STEPS/'); % Path for Simscape Model
|
||
addpath('./matlab/subsystems/'); % Path for Subsystems Simulink files
|
||
#+END_SRC
|
||
|
||
#+NAME: m-init-path-tangle
|
||
#+BEGIN_SRC matlab
|
||
%% Path for functions, data and scripts
|
||
addpath('./src/'); % Path for scripts
|
||
addpath('./mat/'); % Path for data
|
||
addpath('./STEPS/'); % Path for Simscape Model
|
||
addpath('./subsystems/'); % Path for Subsystems Simulink files
|
||
#+END_SRC
|
||
|
||
** Initialize other elements
|
||
#+NAME: m-init-other
|
||
#+BEGIN_SRC matlab
|
||
%% Colors for the figures
|
||
colors = colororder;
|
||
freqs = logspace(1,4,1000); % Frequency vector [Hz]
|
||
#+END_SRC
|
||
|
||
* Matlab Functions :noexport:
|
||
*** =initializeSimplifiedNanoHexapod=: Nano Hexapod
|
||
|
||
#+begin_src matlab :tangle matlab/src/initializeSimplifiedNanoHexapod.m :comments none :mkdirp yes :eval no
|
||
function [nano_hexapod] = initializeSimplifiedNanoHexapod(args)
|
||
|
||
arguments
|
||
args.type char {mustBeMember(args.type,{'none', 'stewart'})} = 'stewart'
|
||
end
|
||
|
||
stewart = initializeStewartPlatform();
|
||
|
||
switch args.type
|
||
case 'none'
|
||
stewart.type = 0;
|
||
case 'stewart'
|
||
stewart.type = 1;
|
||
end
|
||
|
||
stewart = initializeFramesPositions(stewart, ...
|
||
'H', 95e-3, ...
|
||
'MO_B', 150e-3);
|
||
|
||
stewart = generateGeneralConfiguration(stewart, ...
|
||
'FH', 15e-3, ...
|
||
'FR', 120e-3, ...
|
||
'FTh', [220, 320, 340, 80, 100, 200]*(pi/180), ...
|
||
'MH', 15e-3, ...
|
||
'MR', 110e-3, ...
|
||
'MTh', [255, 285, 15, 45, 135, 165]*(pi/180));
|
||
|
||
stewart = computeJointsPose(stewart);
|
||
|
||
stewart = initializeStrutDynamics(stewart, ...
|
||
'type', 'apa300ml', ...
|
||
'k', 0.3e6, ...
|
||
'ke', 4.3e6, ...
|
||
'ka', 2.15e6, ...
|
||
'c', 18, ...
|
||
'ce', 0.7, ...
|
||
'ca', 0.35, ...
|
||
'ga', -2.7, ...
|
||
'gs', 0.53e6);
|
||
|
||
stewart = initializeJointDynamics(stewart, ...
|
||
'type_M', '4dof', ...
|
||
'type_F', '2dof_axial', ...
|
||
'Kf_M', 4.83, ...
|
||
'Cf_M', 1e-2, ...
|
||
'Kt_M', 260, ...
|
||
'Ct_M', 1e-2, ...
|
||
'Ka_M', 94e6, ...
|
||
'Ca_M', 1e-2, ...
|
||
'Kf_F', 4.83, ...
|
||
'Cf_F', 1e-2, ...
|
||
'Ka_F', 94e6, ...
|
||
'Ca_F', 1e-2);
|
||
|
||
stewart = initializeCylindricalPlatforms(stewart, ...
|
||
'Fpm', 5, ...
|
||
'Fph', 10e-3, ...
|
||
'Fpr', 150e-3, ...
|
||
'Mpm', 5, ...
|
||
'Mph', 10e-3, ...
|
||
'Mpr', 150e-3);
|
||
|
||
stewart = initializeCylindricalStruts(stewart, ...
|
||
'Fsm', 1e-3, ...
|
||
'Fsh', 60e-3, ...
|
||
'Fsr', 5e-3, ...
|
||
'Msm', 1e-3, ...
|
||
'Msh', 60e-3, ...
|
||
'Msr', 5e-3);
|
||
|
||
stewart = computeJacobian(stewart);
|
||
|
||
stewart = initializeStewartPose(stewart, ...
|
||
'AP', zeros(3,1), ...
|
||
'ARB', eye(3));
|
||
|
||
nano_hexapod = stewart;
|
||
if exist('./mat', 'dir')
|
||
if exist('./mat/nass_model_stages.mat', 'file')
|
||
save('mat/nass_model_stages.mat', 'nano_hexapod', '-append');
|
||
else
|
||
save('mat/nass_model_stages.mat', 'nano_hexapod');
|
||
end
|
||
elseif exist('./matlab', 'dir')
|
||
if exist('./matlab/mat/nass_model_stages.mat', 'file')
|
||
save('matlab/mat/nass_model_stages.mat', 'nano_hexapod', '-append');
|
||
else
|
||
save('matlab/mat/nass_model_stages.mat', 'nano_hexapod');
|
||
end
|
||
end
|
||
end
|
||
#+end_src
|
||
|
||
* Footnotes
|
||
[fn:detail_instrumentation_2] For variable gain amplifiers, it is usual to refer to the input noise rather than the output noise, as the input referred noise is almost independent on the chosen gain.
|
||
[fn:detail_instrumentation_1] The manufacturer proposed to remove the $50\,\Omega$ output resistor to improve to small signal bandwidth above $10\,kHz$
|