#+TITLE: Vibration Table
: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 ""
#+BIND: org-latex-bib-compiler "biber"

#+LaTeX_CLASS: scrreprt
#+LaTeX_CLASS_OPTIONS: [a4paper, 10pt, DIV=12, parskip=full]
#+LaTeX_HEADER_EXTRA: \input{preamble.tex}
#+LATEX_HEADER_EXTRA: \addbibresource{ref.bib}

#+PROPERTY: header-args:matlab  :session *MATLAB*
#+PROPERTY: header-args:matlab+ :comments org
#+PROPERTY: header-args:matlab+ :exports both
#+PROPERTY: header-args:matlab+ :results none
#+PROPERTY: header-args:matlab+ :eval no-export
#+PROPERTY: header-args:matlab+ :noweb yes
#+PROPERTY: header-args:matlab+ :tangle no
#+PROPERTY: header-args:matlab+ :mkdirp yes
#+PROPERTY: header-args:matlab+ :output-dir figs

#+PROPERTY: header-args:latex  :headers '("\\usepackage{tikz}" "\\usepackage{import}" "\\import{$HOME/Cloud/tikz/org/}{config.tex}")
#+PROPERTY: header-args:latex+ :imagemagick t :fit yes
#+PROPERTY: header-args:latex+ :iminoptions -scale 100% -density 150
#+PROPERTY: header-args:latex+ :imoutoptions -quality 100
#+PROPERTY: header-args:latex+ :results file raw replace
#+PROPERTY: header-args:latex+ :buffer no
#+PROPERTY: header-args:latex+ :tangle no
#+PROPERTY: header-args:latex+ :eval no-export
#+PROPERTY: header-args:latex+ :exports results
#+PROPERTY: header-args:latex+ :mkdirp yes
#+PROPERTY: header-args:latex+ :output-dir figs
#+PROPERTY: header-args:latex+ :post pdf2svg(file=*this*, ext="png")
:END:

#+begin_export html
  <hr>
  <p>This report is also available as a <a href="./vibration-table.pdf">pdf</a>.</p>
  <hr>
#+end_export

* Introduction

This document is divided as follows:
- Section [[sec:experimental_setup]]: the experimental setup and all the instrumentation are described
- Section [[sec:meas_transformation]]: the mathematics used to compute the 6DoF motion of a solid body from several inertial sensor is derived
- Section [[sec:simscape_model]]: a Simscape model of the vibration table is developed
- Section [[sec:table_dynamics]]: the table dynamics is identified and compared with the Simscape model

* Experimental Setup
<<sec:experimental_setup>>
** Introduction                                                      :ignore:
** CAD Model

#+name: fig:vibration-table-cad-view
#+caption: CAD View of the vibration table
#+attr_latex: :width 0.8\linewidth
[[file:figs/vibration-table-cad-view.png]]

** Instrumentation

#+begin_note
Here are the documentation of the equipment used for this vibration table:
- Modal Shaker: Watson and Gearing
- Inertial Shaker: [[file:doc/inertial_shakers.pdf][IS20]]
- Viscoelastic supports: [[file:doc/810002_doc.pdf][810002]]
- Spring supports: [[file:doc/9129fcb6ec46bb52925bb16155a850f3be01c479.pdf][MV803-12CC]]
- Optical Table: [[https://www.thorlabs.com/thorproduct.cfm?partnumber=B4545A][B4545A]]
- Triaxial Accelerometer: [[https://www.pcb.com/products?model=356b18][356B18]]
- OROS
#+end_note

** Suspended table

- Dimensions :: 450 mm x 450 mm x 60 mm
- Mass :: 21.30 kg

#+name: fig:compliance_optical_table
#+caption: Compliance of the B4545A optical table
#+attr_latex: :width 0.8\linewidth
[[file:figs/B4545A_Compliance_inLb-780.png]]

** Inertial Sensors

| Equipment                        |
|----------------------------------|
| (2x) 1D accelerometer [[https://www.pcbpiezotronics.fr/produit/accelerometre/393b05/][PCB 393B05]] |
| (4x) 3D accelerometer [[https://www.pcbpiezotronics.fr/produit/accelerometres/356b18/][PCB 356B18]] |

* Compute the 6DoF solid body motion from several inertial sensors
:PROPERTIES:
:header-args:matlab+: :tangle matlab/meas_transformation.m
:END:
<<sec:meas_transformation>>
** Introduction                                                      :ignore:
Let's consider a solid body with several accelerometers attached to it (Figure [[fig:local_to_global_coordinates]]).

#+begin_src latex :file local_to_global_coordinates.pdf :post pdf2svg(file=*this*, ext="png") :exports results
\newcommand\irregularcircle[2]{% radius, irregularity
  \pgfextra {\pgfmathsetmacro\len{(#1)+rand*(#2)}}
  +(0:\len pt)
  \foreach \a in {10,20,...,350}{
    \pgfextra {\pgfmathsetmacro\len{(#1)+rand*(#2)}}
    -- +(\a:\len pt)
  } -- cycle
}
\begin{tikzpicture}
  \draw[rounded corners=1mm, fill=blue!30!white] (0, 0) \irregularcircle{3cm}{1mm};

  \node[] (origin) at (0, 0) {$\bullet$};
  \begin{scope}[shift={(origin)}]
    \def\axissize{0.8cm}
    \draw[->] (0, 0) -- ++(\axissize, 0) node[above left]{$x$};
    \draw[->] (0, 0) -- ++(0, \axissize) node[below right]{$y$};
    \draw[fill, color=black] (0, 0) circle (0.05*\axissize);
    \node[draw, circle, inner sep=0pt, minimum size=0.4*\axissize, label=left:$z$] (yaxis) at (0, 0){};
    \node[below right] at (0, 0){$\{O\}$};
  \end{scope}

  \coordinate[] (p1) at (-1.5, -1.5);
  \coordinate[] (p2) at (-1.5, -1.5);
  \coordinate[] (p3) at ( 1.5, -1.5);
  \coordinate[] (p4) at ( 1.5, -1.5);
  \coordinate[] (p5) at ( 1.5,  1.5);
  \coordinate[] (p6) at ( 1.5,  1.5);

  \draw[->] (p1)node[]{$\bullet$} -- ++(0, 1)node[right]{$a_1$};
  \node[draw, circle, inner sep=0pt, minimum size=0.3cm, label=left:$a_2$] at (p2){};
  \draw[fill, color=black] (p2) circle (0.05);
  \draw[->] (p3)node[]{$\bullet$} -- ++(1, 0)node[right]{$a_3$};
  \node[draw, circle, inner sep=0pt, minimum size=0.3cm, label=left:$a_4$] at (p4){};
  \draw[fill, color=black] (p4) circle (0.05);
  \draw[->] (p5)node[]{$\bullet$} -- ++(1, 0)node[right]{$a_5$};
  \node[draw, circle, inner sep=0pt, minimum size=0.3cm, label=left:$a_6$] at (p6){};
  \draw[fill, color=black] (p6) circle (0.04);
\end{tikzpicture}
#+end_src

#+name: fig:local_to_global_coordinates
#+caption: Schematic of the measured motions of a solid body
#+RESULTS:
[[file:figs/local_to_global_coordinates.png]]

The goal of this section is to see how to compute the acceleration/angular acceleration of the solid body from the accelerations $\vec{a}_i$ measured by the accelerometers.

The acceleration/angular acceleration of the solid body is defined as the vector ${}^O\vec{x}$:
\begin{equation}
{}^O\vec{x} = \begin{bmatrix}
  \dot{v}_x \\ \dot{v}_y \\ \dot{v}_z \\ \dot{\omega}_x \\ \dot{\omega}_y \\ \dot{\omega}_z
\end{bmatrix}
\end{equation}

As we want to measure 6dof, we suppose that we have 6 uniaxial acceleremoters (we could use more, but 6 is enough).
The measurement of the individual vectors is defined as the vector $\vec{a}$:
\begin{equation}
\vec{a} = \begin{bmatrix}
  a_1 \\ a_2 \\ a_3 \\ a_4 \\ a_5 \\ a_6
\end{bmatrix}
\end{equation}

From the positions and orientations of the acceleremoters (defined in Section [[sec:accelerometer_pos]]), it is quite straightforward to compute the accelerations measured by the sensors from the acceleration/angular acceleration of the solid body (Section [[sec:transformation_motion_to_acc]]).
From this, we can easily build a transformation matrix $M$, such that:
\begin{equation}
\vec{a} = M \cdot {}^O\vec{x}
\end{equation}

If the matrix is invertible, we can just take the inverse in order to obtain the transformation matrix giving the 6dof acceleration of the solid body from the accelerometer measurements (Section [[sec:transformation_acc_to_motion]]):
\begin{equation}
{}^O\vec{x} = M^{-1} \cdot \vec{a}
\end{equation}

If it is not invertible, then it means that it is not possible to compute all 6dof of the solid body from the measurements.
The solution is then to change the location/orientation of some of the accelerometers.

** 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
addpath('matlab/')
#+end_src

** Define accelerometers positions/orientations
<<sec:accelerometer_pos>>
Let's first define the position and orientation of all measured accelerations with respect to a defined frame $\{O\}$.

#+begin_src matlab
Opm = [-0.1875, -0.1875, -0.245;
       -0.1875, -0.1875, -0.245;
        0.1875, -0.1875, -0.245;
        0.1875, -0.1875, -0.245;
        0.1875,  0.1875, -0.245;
        0.1875,  0.1875, -0.245]';
#+end_src

There are summarized in Table [[tab:accelerometers_table_positions]].

#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*)
data2orgtable(Opm, {'x', 'y', 'z'}, {'$a_1$', '$a_2$', '$a_3$', '$a_4$', '$a_5$', '$a_6$'}, ' %.3f ');
#+end_src

#+name: tab:accelerometers_table_positions
#+caption: Positions of the accelerometers fixed to the vibration table with respect to $\{O\}$
#+attr_latex: :environment tabularx :width 0.55\linewidth :align Xcccccc
#+attr_latex: :center t :booktabs t :float t
#+RESULTS:
|   |  $a_1$ |  $a_2$ |  $a_3$ |  $a_4$ |  $a_5$ |  $a_6$ |
|---+--------+--------+--------+--------+--------+--------|
| x | -0.188 | -0.188 |  0.188 |  0.188 |  0.188 |  0.188 |
| y | -0.188 | -0.188 | -0.188 | -0.188 |  0.188 |  0.188 |
| z | -0.245 | -0.245 | -0.245 | -0.245 | -0.245 | -0.245 |

We then define the direction of the measured accelerations (unit vectors):
#+begin_src matlab
Osm = [0, 1, 0;
       0, 0, 1;
       1, 0, 0;
       0, 0, 1;
       1, 0, 0;
       0, 0, 1;]';
#+end_src

They are summarized in Table [[tab:accelerometers_table_orientations]].

#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*)
data2orgtable(Osm, {'x', 'y', 'z'}, {'$\hat{s}_1$', '$\hat{s}_2$', '$\hat{s}_3$', '$\hat{s}_4$', '$\hat{s}_5$', '$\hat{s}_6$'}, ' %.0f ');
#+end_src

#+name: tab:accelerometers_table_orientations
#+caption: Orientations of the accelerometers fixed to the vibration table expressed in $\{O\}$
#+attr_latex: :environment tabularx :width 0.35\linewidth :align Xcccccc
#+attr_latex: :center t :booktabs t :float t
#+RESULTS:
|   | $\hat{s}_1$ | $\hat{s}_2$ | $\hat{s}_3$ | $\hat{s}_4$ | $\hat{s}_5$ | $\hat{s}_6$ |
|---+-------------+-------------+-------------+-------------+-------------+-------------|
| x |           0 |           0 |           1 |           0 |           1 |           0 |
| y |           1 |           0 |           0 |           0 |           0 |           0 |
| z |           0 |           1 |           0 |           1 |           0 |           1 |

** Transformation matrix from motion of the solid body to accelerometer measurements
<<sec:transformation_motion_to_acc>>

Let's try to estimate the x-y-z acceleration of any point of the solid body from the acceleration/angular acceleration of the solid body expressed in $\{O\}$.
For any point $p_i$ of the solid body (corresponding to an accelerometer), we can write:
\begin{equation}
\begin{bmatrix}
a_{i,x} \\ a_{i,y} \\ a_{i,z}
\end{bmatrix} = \begin{bmatrix}
\dot{v}_x \\ \dot{v}_y \\ \dot{v}_z
\end{bmatrix} + p_i \times \begin{bmatrix}
\dot{\omega}_x \\ \dot{\omega}_y \\ \dot{\omega}_z
\end{bmatrix}
\end{equation}

We can write the cross product as a matrix product using the skew-symmetric transformation:
\begin{equation}
\begin{bmatrix}
a_{i,x} \\ a_{i,y} \\ a_{i,z}
\end{bmatrix} = \begin{bmatrix}
\dot{v}_x \\ \dot{v}_y \\ \dot{v}_z
\end{bmatrix} + \underbrace{\begin{bmatrix}
  0       &  p_{i,z} & -p_{i,y} \\
 -p_{i,z} &  0       &  p_{i,x} \\
  p_{i,y} & -p_{i,x} &  0
\end{bmatrix}}_{P_{i,[\times]}} \cdot \begin{bmatrix}
\dot{\omega}_x \\ \dot{\omega}_y \\ \dot{\omega}_z
\end{bmatrix}
\end{equation}

If we now want to know the (scalar) acceleration $a_i$ of the point $p_i$ in the direction of the accelerometer direction $\hat{s}_i$, we can just project the 3d acceleration on $\hat{s}_i$:
\begin{equation}
a_i = \hat{s}_i^T \cdot \begin{bmatrix}
a_{i,x} \\ a_{i,y} \\ a_{i,z}
\end{bmatrix} = \hat{s}_i^T \cdot \begin{bmatrix}
\dot{v}_x \\ \dot{v}_y \\ \dot{v}_z
\end{bmatrix} + \left( \hat{s}_i^T \cdot P_{i,[\times]} \right) \cdot \begin{bmatrix}
\dot{\omega}_x \\ \dot{\omega}_y \\ \dot{\omega}_z
\end{bmatrix}
\end{equation}

Which is equivalent as a simple vector multiplication:
\begin{equation}
a_i = \begin{bmatrix}
\hat{s}_i^T & \hat{s}_i^T \cdot P_{i,[\times]}
\end{bmatrix}
\begin{bmatrix}
\dot{v}_x \\ \dot{v}_y \\ \dot{v}_z \\ \dot{\omega}_x \\ \dot{\omega}_y \\ \dot{\omega}_z
\end{bmatrix} = \begin{bmatrix}
\hat{s}_i^T & \hat{s}_i^T \cdot P_{i,[\times]}
\end{bmatrix} {}^O\vec{x}
\end{equation}

And finally we can combine the 6 (line) vectors for the 6 accelerometers to write that in a matrix form.
We obtain Eq. eqref:eq:M_matrix.
#+begin_important
The transformation from solid body acceleration ${}^O\vec{x}$ from sensor measured acceleration $\vec{a}$ is:
\begin{equation} \label{eq:M_matrix}
\vec{a} = \underbrace{\begin{bmatrix}
\hat{s}_1^T & \hat{s}_1^T \cdot P_{1,[\times]} \\
\vdots & \vdots \\
\hat{s}_6^T & \hat{s}_6^T \cdot P_{6,[\times]}
\end{bmatrix}}_{M} {}^O\vec{x}
\end{equation}

with $\hat{s}_i$ the unit vector representing the measured direction of the i'th accelerometer expressed in frame $\{O\}$ and $P_{i,[\times]}$ the skew-symmetric matrix representing the cross product of the position of the i'th accelerometer expressed in frame $\{O\}$.
#+end_important

Let's define such matrix using matlab:
#+begin_src matlab
M = zeros(length(Opm), 6);

for i = 1:length(Opm)
    Ri = [0,         Opm(3,i), -Opm(2,i);
         -Opm(3,i),  0,         Opm(1,i);
          Opm(2,i), -Opm(1,i),  0];
    M(i, 1:3) = Osm(:,i)';
    M(i, 4:6) = Osm(:,i)'*Ri;
end
#+end_src

The obtained matrix is shown in Table [[tab:effect_motion_on_meas]].

#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*)
data2orgtable(M, {'$a_1$', '$a_2$', '$a_3$', '$a_4$', '$a_5$', '$a_6$'}, {'$\dot{x}_x$', '$\dot{x}_y$', '$\dot{x}_z$', '$\dot{\omega}_x$', '$\dot{\omega}_y$', '$\dot{\omega}_z$'}, ' %.2f ');
#+end_src

#+name: tab:effect_motion_on_meas
#+caption: Effect of a displacement/rotation on the 6 measurements
#+attr_latex: :environment tabularx :width 0.45\linewidth :align Xcccccc
#+attr_latex: :center t :booktabs t :float t
#+RESULTS:
|       | $\dot{x}_x$ | $\dot{x}_y$ | $\dot{x}_z$ | $\dot{\omega}_x$ | $\dot{\omega}_y$ | $\dot{\omega}_z$ |
|-------+-------------+-------------+-------------+------------------+------------------+------------------|
| $a_1$ |         0.0 |         1.0 |         0.0 |             0.24 |              0.0 |            -0.19 |
| $a_2$ |         0.0 |         0.0 |         1.0 |            -0.19 |             0.19 |              0.0 |
| $a_3$ |         1.0 |         0.0 |         0.0 |              0.0 |            -0.24 |             0.19 |
| $a_4$ |         0.0 |         0.0 |         1.0 |            -0.19 |            -0.19 |              0.0 |
| $a_5$ |         1.0 |         0.0 |         0.0 |              0.0 |            -0.24 |            -0.19 |
| $a_6$ |         0.0 |         0.0 |         1.0 |             0.19 |            -0.19 |              0.0 |

** Compute the transformation matrix from accelerometer measurement to motion of the solid body
<<sec:transformation_acc_to_motion>>

In order to compute the motion of the solid body ${}^O\vec{x}$ with respect to frame $\{O\}$ from the accelerometer measurements $\vec{a}$, we have to inverse the transformation matrix $M$.
\begin{equation}
{}^O\vec{x} = M^{-1} \vec{a}
\end{equation}

We therefore need the determinant of $M$ to be non zero:
#+begin_src matlab :results value replace :exports both :tangle no
det(M)
#+end_src

The obtained inverse of the matrix is shown in Table [[tab:compute_motion_from_meas]].

#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*)
data2orgtable(inv(M), {'$\dot{x}_x$', '$\dot{x}_y$', '$\dot{x}_z$', '$\dot{\omega}_x$', '$\dot{\omega}_y$', '$\dot{\omega}_z$'}, {'$a_1$', '$a_2$', '$a_3$', '$a_4$', '$a_5$', '$a_6$'}, ' %.1f ');
#+end_src

#+name: tab:compute_motion_from_meas
#+caption: Compute the displacement/rotation from the 6 measurements
#+attr_latex: :environment tabularx :width 0.45\linewidth :align Xcccccc
#+attr_latex: :center t :booktabs t :float t
#+RESULTS:
|                  | $a_1$ | $a_2$ | $a_3$ | $a_4$ | $a_5$ | $a_6$ |
|------------------+-------+-------+-------+-------+-------+-------|
| $\dot{x}_x$      |   0.0 |   0.7 |   0.5 |  -0.7 |   0.5 |   0.0 |
| $\dot{x}_y$      |   1.0 |   0.0 |   0.5 |   0.7 |  -0.5 |  -0.7 |
| $\dot{x}_z$      |   0.0 |   0.5 |   0.0 |   0.0 |   0.0 |   0.5 |
| $\dot{\omega}_x$ |   0.0 |   0.0 |   0.0 |  -2.7 |   0.0 |   2.7 |
| $\dot{\omega}_y$ |   0.0 |   2.7 |   0.0 |  -2.7 |   0.0 |   0.0 |
| $\dot{\omega}_z$ |   0.0 |   0.0 |   2.7 |   0.0 |  -2.7 |   0.0 |

* Simscape Model
:PROPERTIES:
:header-args:matlab+: :tangle matlab/simscape_model.m
:END:
<<sec:simscape_model>>
** Introduction                                                      :ignore:
In this section, the Simscape model of the vibration table is described.

#+name: fig:simscape_vibration_table
#+caption: 3D representation of the simscape model
#+attr_latex: :width 0.8\linewidth
[[file:figs/simscape_vibration_table.png]]

** 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
addpath('matlab/')
#+end_src

#+begin_src matlab
% Run meas_transformation.m script in order to get the tranformation (jacobian) matrix
run('meas_transformation.m')
#+end_src

#+begin_src matlab :tangle no :eval no
%% Used to export to another version
open('vibration_table')
sim_versions = {'R2019B'}
for sim_version = sim_versions
    save_system('vibration_table',          ['matlab/' sim_version{1} '/vibration_table.slx'],          'ExportToVersion', sim_version{1});
    save_system('accelerometer_3d',         ['matlab/' sim_version{1} '/accelerometer_3d.slx'],         'ExportToVersion', sim_version{1});
    save_system('accelerometer_3d_perfect', ['matlab/' sim_version{1} '/accelerometer_3d_perfect.slx'], 'ExportToVersion', sim_version{1});
end
#+end_src

#+begin_src matlab
% Open the Simulink File
open('vibration_table')
#+end_src

** Simscape Sub-systems
<<sec:simscape_parameters>>

Parameters for sub-components of the simscape model are defined below.

*** Springs
<<sec:simscape_springs>>

The 4 springs supporting the suspended optical table are modelled with "bushing joints" having stiffness and damping in the x-y-z directions:

#+begin_src matlab
spring.kx = 1e4; % X- Stiffness [N/m]
spring.cx = 1e1; % X- Damping [N/(m/s)]

spring.ky = 1e4; % Y- Stiffness [N/m]
spring.cy = 1e1; % Y- Damping [N/(m/s)]

spring.kz = 1e4; % Z- Stiffness [N/m]
spring.cz = 1e1; % Z- Damping [N/(m/s)]

spring.z0 = 32e-3; % Equilibrium z-length [m]
#+end_src

And we can increase the "equilibrium position" of the vertical springs to take into account the gravity forces and start closer to equilibrium:
#+begin_src matlab
spring.dl = (30.5918/4)*9.80665/spring.kz;
#+end_src

*** Inertial Shaker (IS20)
<<sec:simscape_inertial_shaker>>

The inertial shaker is defined as two solid bodies:
- the "housing" that is fixed to the element that we want to excite
- the "inertial mass" that is suspended inside the housing

The inertial mass is guided inside the housing and an actuator (coil and magnet) can be used to apply a force between the inertial mass and the support.
The "reacting" force on the support is then used as an excitation.

#+name: tab:is20_characteristics
#+caption: Summary of the IS20 datasheet
#+attr_latex: :environment tabularx :width 0.4\linewidth :align lX
#+attr_latex: :center t :booktabs t :float t
| Characteristic  | Value      |
|-----------------+------------|
| Output Force    | 20 N       |
| Frequency Range | 10-3000 Hz |
| Moving Mass     | 0.1 kg     |
| Total Mass      | 0.3 kg     |

From the datasheet in Table [[tab:is20_characteristics]], we can estimate the parameters of the physical shaker.

These parameters are defined below
#+begin_src matlab
shaker.w0 = 2*pi*10; % Resonance frequency of moving mass [rad/s]
shaker.m = 0.1; % Moving mass [m]
shaker.m_tot = 0.3; % Total mass [m]
shaker.k = shaker.m*shaker.w0^2; % Spring constant [N/m]
shaker.c = 0.2*sqrt(shaker.k*shaker.m); % Damping [N/(m/s)]
#+end_src

*** 3D accelerometer (356B18)
<<sec:simscape_accelerometers>>

An accelerometer consists of 2 solids:
- a "housing" rigidly fixed to the measured body
- an "inertial mass" suspended inside the housing by springs and guided in the measured direction

The relative motion between the housing and the inertial mass gives a measurement of the acceleration of the measured body (up to the suspension mode of the inertial mass).

#+name: tab:356b18_characteristics
#+caption: Summary of the 356B18 datasheet
#+attr_latex: :environment tabularx :width 0.5\linewidth :align lX
#+attr_latex: :center t :booktabs t :float t
| Characteristic      | Value               |
|---------------------+---------------------|
| Sensitivity         | 0.102 V/(m/s2)      |
| Frequency Range     | 0.5 to 3000 Hz      |
| Resonance Frequency | > 20 kHz            |
| Resolution          | 0.0005 m/s2 rms     |
| Weight              | 0.025 kg            |
| Size                | 20.3x26.1x20.3 [mm] |

Here are defined the parameters for the triaxial accelerometer:
#+begin_src matlab
acc_3d.m = 0.005; % Inertial mass [kg]
acc_3d.m_tot = 0.025; % Total mass [m]

acc_3d.w0 = 2*pi*20e3; % Resonance frequency [rad/s]

acc_3d.kx = acc_3d.m*acc_3d.w0^2; % Spring constant [N/m]
acc_3d.ky = acc_3d.m*acc_3d.w0^2; % Spring constant [N/m]
acc_3d.kz = acc_3d.m*acc_3d.w0^2; % Spring constant [N/m]

acc_3d.cx = 1e2; % Damping [N/(m/s)]
acc_3d.cy = 1e2; % Damping [N/(m/s)]
acc_3d.cz = 1e2; % Damping [N/(m/s)]
#+end_src

DC gain between support acceleration and inertial mass displacement is $-m/k$:
#+begin_src matlab
acc_3d.g_x = 1/(-acc_3d.m/acc_3d.kx); % [m/s^2/m]
acc_3d.g_y = 1/(-acc_3d.m/acc_3d.ky); % [m/s^2/m]
acc_3d.g_z = 1/(-acc_3d.m/acc_3d.kz); % [m/s^2/m]
#+end_src

We also define the sensitivity in order to have the outputs in volts.
#+begin_src matlab
acc_3d.gV_x = 0.102; % [V/(m/s^2)]
acc_3d.gV_y = 0.102; % [V/(m/s^2)]
acc_3d.gV_z = 0.102; % [V/(m/s^2)]
#+end_src

The problem with using such model for accelerometers is that this adds states to the identified models (2x3 states for each triaxial accelerometer).
These states represents the dynamics of the suspended inertial mass.
In the frequency band of interest (few Hz up to ~1 kHz), the dynamics of the inertial mass can be ignore (its resonance is way above 1kHz).
Therefore, we might as well use idealized "transform sensors" blocks as they will give the same result up to ~20kHz while allowing to reduce the number of identified states.

The accelerometer model can be chosen by setting the =type= property:
#+begin_src matlab
acc_3d.type = 2; % 1: inertial mass, 2: perfect
#+end_src

** Identification
<<sec:simscape_parameters>>

*** Number of states
Let's first use perfect 3d accelerometers:
#+begin_src matlab
acc_3d.type = 2; % 1: inertial mass, 2: perfect
#+end_src

And identify the dynamics from the shaker force to the measured accelerations:
#+begin_src matlab
%% Name of the Simulink File
mdl = 'vibration_table';

%% Input/Output definition
clear io; io_i = 1;
io(io_i) = linio([mdl, '/F'],  1, 'openinput');  io_i = io_i + 1;
io(io_i) = linio([mdl, '/acc'],  1, 'openoutput'); io_i = io_i + 1;

%% Run the linearization
Gp = linearize(mdl, io);
Gp.InputName  = {'F'};
Gp.OutputName  = {'a1', 'a2', 'a3', 'a4', 'a5', 'a6'};
#+end_src

#+begin_src matlab :results output replace :exports results :tangle no
size(Gp)
#+end_src

#+RESULTS:
: size(Gp)
: State-space model with 6 outputs, 1 inputs, and 12 states.

We indeed have the 12 states corresponding to the 6 DoF of the suspended optical table.

Let's now consider the inertial masses for the triaxial accelerometers:
#+begin_src matlab
acc_3d.type = 1; % 1: inertial mass, 2: perfect
#+end_src

#+begin_src matlab
%% Name of the Simulink File
mdl = 'vibration_table';

%% Input/Output definition
clear io; io_i = 1;
io(io_i) = linio([mdl, '/F'],  1, 'openinput');  io_i = io_i + 1;
io(io_i) = linio([mdl, '/acc'],  1, 'openoutput'); io_i = io_i + 1;

%% Run the linearization
Ga = linearize(mdl, io);
Ga.InputName  = {'F'};
Ga.OutputName  = {'a1', 'a2', 'a3', 'a4', 'a5', 'a6'};
#+end_src

#+begin_src matlab :results output replace :exports results :tangle no
size(Ga)
#+end_src

#+RESULTS:
: size(Ga)
: State-space model with 6 outputs, 1 inputs, and 30 states.

And we can see that 18 states have been added.
This corresponds to 6 states for each triaxial accelerometers.

*** Resonance frequencies and mode shapes
Let's now identify the resonance frequency and mode shapes associated with the suspension modes of the optical table.

#+begin_src matlab
acc_3d.type = 2; % 1: inertial mass, 2: perfect

%% Name of the Simulink File
mdl = 'vibration_table';

%% Input/Output definition
clear io; io_i = 1;
io(io_i) = linio([mdl, '/F'],      1, 'openinput');  io_i = io_i + 1;
io(io_i) = linio([mdl, '/acc_O'],  1, 'openoutput'); io_i = io_i + 1;

%% Run the linearization
G = linearize(mdl, io);
G.InputName  = {'F'};
G.OutputName  = {'ax', 'ay', 'az', 'wx', 'wy', 'wz'};
#+end_src

Compute the resonance frequencies
#+begin_src matlab
ws = eig(G.A);
ws = ws(imag(ws) > 0);
#+end_src

And the associated response of the optical table
#+begin_src matlab
x_mod = zeros(6, 6);

for i = 1:length(ws)
    xi = evalfr(G, ws(i));
    x_mod(:,i) = xi./norm(xi);
end
#+end_src

The results are shown in Table [[tab:mode_shapes]].
The motion associated to the mode shapes are just indicative.

#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*)
data2orgtable([flip(imag(ws)/2/pi)'; flip(abs(x_mod),2)], {'$\omega_0$ [Hz]', 'x', 'y', 'z', 'Rx', 'Ry', 'Rz'}, {}, ' %.1f ');
#+end_src

#+name: tab:mode_shapes
#+caption: Resonance frequency and approximation of the mode shapes
#+attr_latex: :environment tabularx :width 0.4\linewidth :align Xcccccc
#+attr_latex: :center t :booktabs t :float t
#+RESULTS:
| $\omega_0$ [Hz] | 5.6 | 5.6 | 5.7 | 8.2 | 8.2 | 8.2 |
|-----------------+-----+-----+-----+-----+-----+-----|
| x               | 0.1 | 0.7 | 0.0 | 0.0 | 0.2 | 0.0 |
| y               | 0.7 | 0.1 | 0.0 | 0.0 | 0.0 | 0.2 |
| z               | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 | 0.0 |
| Rx              | 0.7 | 0.1 | 0.0 | 0.0 | 0.1 | 1.0 |
| Ry              | 0.1 | 0.7 | 0.0 | 0.0 | 1.0 | 0.1 |
| Rz              | 0.0 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 |

** Verify transformation
#+begin_src matlab
%% Options for Linearized
options = linearizeOptions;
options.SampleTime = 0;

%% Name of the Simulink File
mdl = 'vibration_table';

%% Input/Output definition
clear io; io_i = 1;
io(io_i) = linio([mdl, '/F'],  1, 'openinput');  io_i = io_i + 1;
io(io_i) = linio([mdl, '/acc'],  1, 'openoutput'); io_i = io_i + 1;
io(io_i) = linio([mdl, '/acc_O'],  1, 'openoutput'); io_i = io_i + 1;

%% Run the linearization
G = linearize(mdl, io, 0.0, options);
G.InputName  = {'F'};
G.OutputName = {'a1', 'a2', 'a3', 'a4', 'a5', 'a6', ...
                'Dx', 'Dy', 'Dz', 'Rx', 'Ry', 'Rz'};
#+end_src

#+begin_src matlab
G_acc = inv(M)*G(1:6, 1);
G_id = G(7:12, 1);
#+end_src

#+begin_src matlab
bodeFig({G_acc(1), G_id(1)})
bodeFig({G_acc(2), G_id(2)})
bodeFig({G_acc(3), G_id(3)})
bodeFig({G_acc(4), G_id(4)})
bodeFig({G_acc(5), G_id(5)})
bodeFig({G_acc(6), G_id(6)})
#+end_src

* Identification of the table's dynamics
<<sec:table_dynamics>>
** 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
addpath('matlab/')
#+end_src

* Bibliography                                                        :ignore:
#+latex: \printbibliography
