502 lines
22 KiB
Matlab
502 lines
22 KiB
Matlab
% Matlab Init :noexport:ignore:
|
|
|
|
%% ustation_3_disturbances.m
|
|
|
|
%% Clear Workspace and Close figures
|
|
clear; close all; clc;
|
|
|
|
%% Intialize Laplace variable
|
|
s = zpk('s');
|
|
|
|
%% Path for functions, data and scripts
|
|
addpath('./mat/'); % Path for Data
|
|
addpath('./src/'); % Path for functions
|
|
addpath('./STEPS/'); % Path for STEPS
|
|
addpath('./subsystems/'); % Path for Subsystems Simulink files
|
|
|
|
% Simulink Model name
|
|
mdl = 'ustation_simscape';
|
|
|
|
load('nass_model_conf_simulink.mat');
|
|
|
|
%% Colors for the figures
|
|
colors = colororder;
|
|
|
|
%% Frequency Vector
|
|
freqs = logspace(log10(10), log10(2e3), 1000);
|
|
|
|
% Ground Motion
|
|
|
|
% The ground motion was measured by using a sensitive 3-axis geophone[fn:11] placed on the ground.
|
|
% The generated voltages were recorded with a high resolution DAC, and converted to displacement using the Geophone sensitivity transfer function.
|
|
% The obtained ground motion displacement is shown in Figure ref:fig:ustation_ground_disturbance.
|
|
|
|
|
|
%% Compute Floor Motion Spectral Density
|
|
% Load floor motion data
|
|
% velocity in [m/s] is measured in X, Y and Z directions
|
|
load('ustation_ground_motion.mat', 'Ts', 'Fs', 'vel_x', 'vel_y', 'vel_z', 't');
|
|
|
|
% Estimate ground displacement from measured velocity
|
|
% This is done by integrating the motion
|
|
gm_x = lsim(1/(s+0.1*2*pi), vel_x, t);
|
|
gm_y = lsim(1/(s+0.1*2*pi), vel_y, t);
|
|
gm_z = lsim(1/(s+0.1*2*pi), vel_z, t);
|
|
|
|
Nfft = floor(2/Ts);
|
|
win = hanning(Nfft);
|
|
Noverlap = floor(Nfft/2);
|
|
|
|
[pxx_gm_vx, f_gm] = pwelch(vel_x, win, Noverlap, Nfft, 1/Ts);
|
|
[pxx_gm_vy, ~] = pwelch(vel_y, win, Noverlap, Nfft, 1/Ts);
|
|
[pxx_gm_vz, ~] = pwelch(vel_z, win, Noverlap, Nfft, 1/Ts);
|
|
|
|
% Convert PSD in velocity to PSD in displacement
|
|
pxx_gm_x = pxx_gm_vx./((2*pi*f_gm).^2);
|
|
pxx_gm_y = pxx_gm_vy./((2*pi*f_gm).^2);
|
|
pxx_gm_z = pxx_gm_vz./((2*pi*f_gm).^2);
|
|
|
|
%% Measured ground motion
|
|
figure;
|
|
hold on;
|
|
plot(t, 1e6*gm_x, 'DisplayName', '$D_{xf}$')
|
|
plot(t, 1e6*gm_y, 'DisplayName', '$D_{yf}$')
|
|
plot(t, 1e6*gm_z, 'DisplayName', '$D_{zf}$')
|
|
hold off;
|
|
xlabel('Time [s]');
|
|
ylabel('Ground motion [$\mu$m]')
|
|
xlim([0, 5]); ylim([-2, 2])
|
|
leg = legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1);
|
|
leg.ItemTokenSize(1) = 15;
|
|
|
|
% Ty Stage
|
|
|
|
% To measure the positioning errors of the translation stage, the setup shown in Figure ref:fig:ustation_errors_ty_setup is used.
|
|
% A special optical element (called a "straightness interferometer"[fn:9]) is fixed on top of the micro-station, while a laser source[fn:10] and a straightness reflector are fixed on the ground.
|
|
% A similar setup was used to measure the horizontal deviation (i.e. in the $x$ direction), as well as the pitch and yaw errors of the translation stage.
|
|
|
|
% #+name: fig:ustation_errors_ty_setup
|
|
% #+caption: Experimental setup to measure the flatness (vertical deviation) of the translation stage
|
|
% [[file:figs/ustation_errors_ty_setup.png]]
|
|
|
|
% Six scans were performed between $-4.5\,mm$ and $4.5\,mm$.
|
|
% The results for each individual scan are shown in Figure ref:fig:ustation_errors_dy_vertical.
|
|
% The measurement axis may not be perfectly aligned with the translation stage axis; this, a linear fit is removed from the measurement.
|
|
% The remaining vertical displacement is shown in Figure ref:fig:ustation_errors_dy_vertical_remove_mean.
|
|
% A vertical error of $\pm300\,nm$ induced by the translation stage is expected.
|
|
% Similar result is obtained for the $x$ lateral direction.
|
|
|
|
|
|
%% Ty errors
|
|
% Setpoint is in [mm]
|
|
% Vertical error is in [um]
|
|
ty_errors = load('ustation_errors_ty.mat');
|
|
|
|
% Compute best straight line to remove it from data
|
|
average_error = mean(ty_errors.ty_z')';
|
|
straight_line = average_error - detrend(average_error, 1);
|
|
|
|
%% Measurement of the linear (vertical) deviation of the Translation stage
|
|
figure;
|
|
hold on;
|
|
plot(ty_errors.setpoint, ty_errors.ty_z(:,1), '-', 'color', colors(1,:), 'DisplayName', 'Raw data')
|
|
plot(ty_errors.setpoint, ty_errors.ty_z(:,2:end), '-', 'color', colors(1,:), 'HandleVisibility', 'off')
|
|
plot(ty_errors.setpoint, straight_line, '--', 'color', colors(2,:), 'DisplayName', 'Linear fit')
|
|
hold off;
|
|
xlabel('$D_y$ position [mm]'); ylabel('Vertical error [$\mu$m]');
|
|
xlim([-5, 5]); ylim([-8, 8]);
|
|
legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1);
|
|
|
|
%% Measurement of the linear (vertical) deviation of the Translation stage - Remove best linear fit
|
|
figure;
|
|
plot(ty_errors.setpoint, ty_errors.ty_z - straight_line, 'k-')
|
|
xlabel('$D_y$ position [mm]'); ylabel('Vertical error [$\mu$m]');
|
|
xlim([-5, 5]); ylim([-0.4, 0.4]);
|
|
|
|
|
|
|
|
% #+name: fig:ustation_errors_dy
|
|
% #+caption: Measurement of the linear (vertical) deviation of the Translation stage (\subref{fig:ustation_errors_dy_vertical}). A linear fit is then removed from the data (\subref{fig:ustation_errors_dy_vertical_remove_mean}).
|
|
% #+attr_latex: :options [htbp]
|
|
% #+begin_figure
|
|
% #+attr_latex: :caption \subcaption{\label{fig:ustation_errors_dy_vertical}Measured vertical error}
|
|
% #+attr_latex: :options {0.49\textwidth}
|
|
% #+begin_subfigure
|
|
% #+attr_latex: :width 0.95\linewidth
|
|
% [[file:figs/ustation_errors_dy_vertical.png]]
|
|
% #+end_subfigure
|
|
% #+attr_latex: :caption \subcaption{\label{fig:ustation_errors_dy_vertical_remove_mean}Error after removing linear fit}
|
|
% #+attr_latex: :options {0.49\textwidth}
|
|
% #+begin_subfigure
|
|
% #+attr_latex: :width 0.95\linewidth
|
|
% [[file:figs/ustation_errors_dy_vertical_remove_mean.png]]
|
|
% #+end_subfigure
|
|
% #+end_figure
|
|
|
|
|
|
%% Convert the data to frequency domain
|
|
% Suppose a certain constant velocity scan
|
|
delta_ty = (ty_errors.setpoint(end) - ty_errors.setpoint(1))/(length(ty_errors.setpoint) - 1); % [mm]
|
|
ty_vel = 8*1.125; % [mm/s]
|
|
Ts = delta_ty/ty_vel;
|
|
Fs = 1/Ts;
|
|
|
|
% Frequency Analysis
|
|
Nfft = floor(length(ty_errors.setpoint)); % Number of frequency points
|
|
win = hanning(Nfft); % Windowing
|
|
Noverlap = floor(Nfft/2); % Overlap for frequency analysis
|
|
|
|
% Comnpute the power spectral density
|
|
[pxx_dy_dz, f_dy] = pwelch(1e-6*detrend(ty_errors.ty_z, 1), win, Noverlap, Nfft, Fs);
|
|
pxx_dy_dz = mean(pxx_dy_dz')';
|
|
|
|
% Having the PSD of the lateral error equal to the PSD of the vertical error
|
|
% is a reasonable assumption (and verified in practice)
|
|
pxx_dy_dx = pxx_dy_dz;
|
|
|
|
% Spindle
|
|
|
|
% To measure the positioning errors induced by the Spindle, a "Spindle error analyzer"[fn:7] is used as shown in Figure ref:fig:ustation_rz_meas_lion_setup.
|
|
% A specific target is fixed on top of the micro-station, which consists of two sphere with 1 inch diameter precisely aligned with the spindle rotation axis.
|
|
% Five capacitive sensors[fn:8] are pointing at the two spheres, as shown in Figure ref:fig:ustation_rz_meas_lion_zoom.
|
|
% From the 5 measured displacements $[d_1,\,d_2,\,d_3,\,d_4,\,d_5]$, the translations and rotations $[D_x,\,D_y,\,D_z,\,R_x,\,R_y]$ of the target can be estimated.
|
|
|
|
% #+name: fig:ustation_rz_meas_lion_setup
|
|
% #+caption: Experimental setup used to estimate the errors induced by the Spindle rotation (\subref{fig:ustation_rz_meas_lion}). The motion of the two reference spheres is measured using 5 capacitive sensors (\subref{fig:ustation_rz_meas_lion_zoom})
|
|
% #+attr_latex: :options [htbp]
|
|
% #+begin_figure
|
|
% #+attr_latex: :caption \subcaption{\label{fig:ustation_rz_meas_lion}Micro-station and 5-DoF metrology}
|
|
% #+attr_latex: :options {0.49\textwidth}
|
|
% #+begin_subfigure
|
|
% #+attr_latex: :width 0.9\linewidth
|
|
% [[file:figs/ustation_rz_meas_lion.jpg]]
|
|
% #+end_subfigure
|
|
% #+attr_latex: :caption \subcaption{\label{fig:ustation_rz_meas_lion_zoom}Zoom on the metrology system}
|
|
% #+attr_latex: :options {0.49\textwidth}
|
|
% #+begin_subfigure
|
|
% #+attr_latex: :width 0.9\linewidth
|
|
% [[file:figs/ustation_rz_meas_lion_zoom.jpg]]
|
|
% #+end_subfigure
|
|
% #+end_figure
|
|
|
|
% A measurement was performed during a constant rotational velocity of the spindle of 60rpm and during 10 turns.
|
|
% The obtained results are shown in Figure ref:fig:ustation_errors_spindle.
|
|
% A large fraction of the radial (Figure ref:fig:ustation_errors_spindle_radial) and tilt (Figure ref:fig:ustation_errors_spindle_tilt) errors is linked to the fact that the two spheres are not perfectly aligned with the rotation axis of the Spindle.
|
|
% This is displayed by the dashed circle.
|
|
% After removing the best circular fit from the data, the vibrations induced by the Spindle may be viewed as stochastic disturbances.
|
|
% However, some misalignment between the "point-of-interest" of the sample and the rotation axis will be considered because the alignment is not perfect in practice.
|
|
% The vertical motion induced by scanning the spindle is in the order of $\pm 30\,nm$ (Figure ref:fig:ustation_errors_spindle_axial).
|
|
|
|
|
|
%% Spindle Errors
|
|
% Errors are already converted to x,y,z,Rx,Ry
|
|
% Units are in [m] and [rad]
|
|
spindle_errors = load('ustation_errors_spindle.mat');
|
|
|
|
%% Measured radial errors of the Spindle
|
|
figure;
|
|
hold on;
|
|
plot(1e6*spindle_errors.Dx(1:100:end), 1e6*spindle_errors.Dy(1:100:end), 'DisplayName', 'Raw data')
|
|
% Compute best fitting circle
|
|
[x0, y0, R] = circlefit(spindle_errors.Dx, spindle_errors.Dy);
|
|
theta = linspace(0, 2*pi, 500); % Angle to plot the circle [rad]
|
|
plot(1e6*(x0 + R*cos(theta)), 1e6*(y0 + R*sin(theta)), '--', 'DisplayName', 'Best Fit')
|
|
hold off;
|
|
xlabel('X displacement [$\mu$m]'); ylabel('Y displacement [$\mu$m]');
|
|
axis equal
|
|
xlim([-1, 1]); ylim([-1, 1]);
|
|
xticks([-1, -0.5, 0, 0.5, 1]);
|
|
yticks([-1, -0.5, 0, 0.5, 1]);
|
|
leg = legend('location', 'none', 'FontSize', 8, 'NumColumns', 1);
|
|
leg.ItemTokenSize(1) = 15;
|
|
|
|
%% Measured axial errors of the Spindle
|
|
figure;
|
|
plot(spindle_errors.deg(1:100:end)/360, 1e9*spindle_errors.Dz(1:100:end))
|
|
xlabel('Rotation [turn]'); ylabel('Z displacement [nm]');
|
|
axis square
|
|
xlim([0,2]); ylim([-40, 40]);
|
|
|
|
%% Measured tilt errors of the Spindle
|
|
figure;
|
|
plot(1e6*spindle_errors.Rx(1:100:end), 1e6*spindle_errors.Ry(1:100:end))
|
|
xlabel('X angle [$\mu$rad]'); ylabel('Y angle [$\mu$rad]');
|
|
axis equal
|
|
xlim([-35, 35]); ylim([-35, 35]);
|
|
xticks([-30, -15, 0, 15, 30]);
|
|
yticks([-30, -15, 0, 15, 30]);
|
|
|
|
|
|
|
|
% #+name: fig:ustation_errors_spindle
|
|
% #+caption: Measurement of the radial (\subref{fig:ustation_errors_spindle_radial}), axial (\subref{fig:ustation_errors_spindle_axial}) and tilt (\subref{fig:ustation_errors_spindle_tilt}) Spindle errors during a 60rpm spindle rotation. The circular best fit is shown by the dashed circle. It represents the misalignment of the spheres with the rotation axis.
|
|
% #+attr_latex: :options [htbp]
|
|
% #+begin_figure
|
|
% #+attr_latex: :caption \subcaption{\label{fig:ustation_errors_spindle_radial}Radial errors}
|
|
% #+attr_latex: :options {0.33\textwidth}
|
|
% #+begin_subfigure
|
|
% #+attr_latex: :width 0.9\linewidth
|
|
% [[file:figs/ustation_errors_spindle_radial.png]]
|
|
% #+end_subfigure
|
|
% #+attr_latex: :caption \subcaption{\label{fig:ustation_errors_spindle_axial}Axial error}
|
|
% #+attr_latex: :options {0.33\textwidth}
|
|
% #+begin_subfigure
|
|
% #+attr_latex: :width 0.9\linewidth
|
|
% [[file:figs/ustation_errors_spindle_axial.png]]
|
|
% #+end_subfigure
|
|
% #+attr_latex: :caption \subcaption{\label{fig:ustation_errors_spindle_tilt}Tilt errors}
|
|
% #+attr_latex: :options {0.33\textwidth}
|
|
% #+begin_subfigure
|
|
% #+attr_latex: :width 0.9\linewidth
|
|
% [[file:figs/ustation_errors_spindle_tilt.png]]
|
|
% #+end_subfigure
|
|
% #+end_figure
|
|
|
|
|
|
%% Remove the circular fit from the data
|
|
[x0, y0, R] = circlefit(spindle_errors.Dx, spindle_errors.Dy);
|
|
|
|
% Search the best angular match
|
|
fun = @(theta)rms((spindle_errors.Dx - (x0 + R*cos(pi/180*spindle_errors.deg+theta(1)))).^2 + (spindle_errors.Dy - (y0 - R*sin(pi/180*spindle_errors.deg+theta(1)))).^2);
|
|
x0 = [0];
|
|
delta_theta = fminsearch(fun, 0);
|
|
|
|
% Compute the remaining error after removing the best circular fit
|
|
spindle_errors.Dx_err = spindle_errors.Dx - (x0 + R*cos(pi/180*spindle_errors.deg+delta_theta));
|
|
spindle_errors.Dy_err = spindle_errors.Dy - (y0 - R*sin(pi/180*spindle_errors.deg+delta_theta));
|
|
|
|
%% Convert the data to frequency domain
|
|
Ts = (spindle_errors.t(end) - spindle_errors.t(1))/(length(spindle_errors.t)-1); % [s]
|
|
Fs = 1/Ts; % [Hz]
|
|
|
|
% Frequency Analysis
|
|
Nfft = floor(2.0*Fs); % Number of frequency points
|
|
win = hanning(Nfft); % Windowing
|
|
Noverlap = floor(Nfft/2); % Overlap for frequency analysis
|
|
|
|
% Comnpute the power spectral density
|
|
[pxx_rz_dz, f_rz] = pwelch(spindle_errors.Dz, win, Noverlap, Nfft, Fs);
|
|
[pxx_rz_dx, ~ ] = pwelch(spindle_errors.Dx_err, win, Noverlap, Nfft, Fs);
|
|
[pxx_rz_dy, ~ ] = pwelch(spindle_errors.Dy_err, win, Noverlap, Nfft, Fs);
|
|
|
|
% Sensitivity to disturbances
|
|
% <<ssec:ustation_disturbances_sensitivity>>
|
|
|
|
% To compute the disturbance source (i.e. forces) that induced the measured vibrations in Section ref:ssec:ustation_disturbances_meas, the transfer function from the disturbance sources to the stage vibration (i.e. the "sensitivity to disturbances") needs to be estimated.
|
|
% This is achieved using the multi-body model presented in Section ref:sec:ustation_modeling.
|
|
% The obtained transfer functions are shown in Figure ref:fig:ustation_model_sensitivity.
|
|
|
|
|
|
%% Extract sensitivity to disturbances from the Simscape model
|
|
% Initialize stages
|
|
initializeGround();
|
|
initializeGranite();
|
|
initializeTy();
|
|
initializeRy();
|
|
initializeRz();
|
|
initializeMicroHexapod();
|
|
|
|
initializeDisturbances('enable', false);
|
|
initializeSimscapeConfiguration('gravity', false);
|
|
initializeLoggingConfiguration();
|
|
|
|
% Configure inputs and outputs
|
|
clear io; io_i = 1;
|
|
io(io_i) = linio([mdl, '/Disturbances/Dwx'], 1, 'openinput'); io_i = io_i + 1; % Vertical Ground Motion
|
|
io(io_i) = linio([mdl, '/Disturbances/Dwy'], 1, 'openinput'); io_i = io_i + 1; % Vertical Ground Motion
|
|
io(io_i) = linio([mdl, '/Disturbances/Dwz'], 1, 'openinput'); io_i = io_i + 1; % Vertical Ground Motion
|
|
io(io_i) = linio([mdl, '/Disturbances/Fdy_x'], 1, 'openinput'); io_i = io_i + 1; % Parasitic force Ty
|
|
io(io_i) = linio([mdl, '/Disturbances/Fdy_z'], 1, 'openinput'); io_i = io_i + 1; % Parasitic force Ty
|
|
io(io_i) = linio([mdl, '/Disturbances/Frz_x'], 1, 'openinput'); io_i = io_i + 1; % Parasitic force Rz
|
|
io(io_i) = linio([mdl, '/Disturbances/Frz_y'], 1, 'openinput'); io_i = io_i + 1; % Parasitic force Rz
|
|
io(io_i) = linio([mdl, '/Disturbances/Frz_z'], 1, 'openinput'); io_i = io_i + 1; % Parasitic force Rz
|
|
io(io_i) = linio([mdl, '/Micro-Station/metrology_6dof/x'], 1, 'openoutput'); io_i = io_i + 1; % Relative motion - Hexapod/Granite
|
|
io(io_i) = linio([mdl, '/Micro-Station/metrology_6dof/y'], 1, 'openoutput'); io_i = io_i + 1; % Relative motion - Hexapod/Granite
|
|
io(io_i) = linio([mdl, '/Micro-Station/metrology_6dof/z'], 1, 'openoutput'); io_i = io_i + 1; % Relative motion - Hexapod/Granite
|
|
|
|
% Run the linearization
|
|
Gd = linearize(mdl, io, 0);
|
|
Gd.InputName = {'Dwx', 'Dwy', 'Dwz', 'Fdy_x', 'Fdy_z', 'Frz_x', 'Frz_y', 'Frz_z'};
|
|
Gd.OutputName = {'Dx', 'Dy', 'Dz'};
|
|
|
|
% The identified dynamics are then saved for further use.
|
|
save('./mat/ustation_disturbance_sensitivity.mat', 'Gd')
|
|
|
|
%% Sensitivity to Ground motion
|
|
freqs = logspace(log10(10), log10(2e2), 1000);
|
|
|
|
figure;
|
|
hold on;
|
|
plot(freqs, abs(squeeze(freqresp(Gd('Dx', 'Dwx'), freqs, 'Hz'))), 'DisplayName', '$D_x/D_{xf}$');
|
|
plot(freqs, abs(squeeze(freqresp(Gd('Dy', 'Dwy'), freqs, 'Hz'))), 'DisplayName', '$D_y/D_{yf}$');
|
|
plot(freqs, abs(squeeze(freqresp(Gd('Dz', 'Dwz'), freqs, 'Hz'))), 'DisplayName', '$D_z/D_{zf}$');
|
|
hold off;
|
|
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
|
|
ylabel('Amplitude [m/m]'); xlabel('Frequency [Hz]');
|
|
leg = legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1);
|
|
leg.ItemTokenSize(1) = 15;
|
|
|
|
%% Sensitivity to Translation stage disturbance forces
|
|
figure;
|
|
hold on;
|
|
plot(freqs, abs(squeeze(freqresp(Gd('Dx', 'Fdy_x'), freqs, 'Hz'))), 'color', colors(1,:), 'DisplayName', '$D_x/F_{xD_y}$');
|
|
plot(freqs, abs(squeeze(freqresp(Gd('Dz', 'Fdy_z'), freqs, 'Hz'))), 'color', colors(3,:), 'DisplayName', '$D_z/F_{zD_y}$');
|
|
hold off;
|
|
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
|
|
ylabel('Amplitude [m/N]'); xlabel('Frequency [Hz]');
|
|
ylim([1e-10, 1e-7]);
|
|
leg = legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1);
|
|
leg.ItemTokenSize(1) = 15;
|
|
|
|
%% Sensitivity to Spindle disturbance forces
|
|
figure;
|
|
hold on;
|
|
plot(freqs, abs(squeeze(freqresp(Gd('Dx', 'Frz_x'), freqs, 'Hz'))), 'DisplayName', '$D_x/F_{xR_z}$');
|
|
plot(freqs, abs(squeeze(freqresp(Gd('Dy', 'Frz_y'), freqs, 'Hz'))), 'DisplayName', '$D_y/F_{yR_z}$');
|
|
plot(freqs, abs(squeeze(freqresp(Gd('Dz', 'Frz_z'), freqs, 'Hz'))), 'DisplayName', '$D_z/F_{zR_z}$');
|
|
hold off;
|
|
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
|
|
ylabel('Amplitude [m/N]'); xlabel('Frequency [Hz]');
|
|
ylim([1e-10, 1e-7]);
|
|
leg = legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
|
|
leg.ItemTokenSize(1) = 15;
|
|
|
|
% Obtained disturbance sources
|
|
% <<ssec:ustation_disturbances_results>>
|
|
|
|
% From the measured effect of disturbances in Section ref:ssec:ustation_disturbances_meas and the sensitivity to disturbances extracted from the Simscape model in Section ref:ssec:ustation_disturbances_sensitivity, the power spectral density of the disturbance sources (i.e. forces applied in the stage's joint) can be estimated.
|
|
% The obtained power spectral density of the disturbances are shown in Figure ref:fig:ustation_dist_sources.
|
|
|
|
|
|
%% Compute the PSD of the equivalent disturbance sources
|
|
pxx_rz_fx = pxx_rz_dx./abs(squeeze(freqresp(Gd('Dx', 'Frz_x'), f_rz, 'Hz'))).^2;
|
|
pxx_rz_fy = pxx_rz_dy./abs(squeeze(freqresp(Gd('Dy', 'Frz_y'), f_rz, 'Hz'))).^2;
|
|
pxx_rz_fz = pxx_rz_dz./abs(squeeze(freqresp(Gd('Dz', 'Frz_z'), f_rz, 'Hz'))).^2;
|
|
|
|
pxx_dy_fx = pxx_dy_dx./abs(squeeze(freqresp(Gd('Dx', 'Fdy_x'), f_dy, 'Hz'))).^2;
|
|
pxx_dy_fz = pxx_dy_dz./abs(squeeze(freqresp(Gd('Dz', 'Fdy_z'), f_dy, 'Hz'))).^2;
|
|
|
|
%% Save the PSD of the disturbance sources for further used
|
|
% in the Simscape model
|
|
|
|
% Ground motion
|
|
min_f = 1; max_f = 100;
|
|
gm_dist.f = f_gm(f_gm < max_f & f_gm > min_f);
|
|
gm_dist.pxx_x = pxx_gm_x(f_gm < max_f & f_gm > min_f);
|
|
gm_dist.pxx_y = pxx_gm_y(f_gm < max_f & f_gm > min_f);
|
|
gm_dist.pxx_z = pxx_gm_z(f_gm < max_f & f_gm > min_f);
|
|
|
|
% Translation stage
|
|
min_f = 0.5; max_f = 500;
|
|
dy_dist.f = f_dy(f_dy < max_f & f_dy > min_f);
|
|
dy_dist.pxx_fx = pxx_dy_fx(f_dy < max_f & f_dy > min_f);
|
|
dy_dist.pxx_fz = pxx_dy_fz(f_dy < max_f & f_dy > min_f);
|
|
|
|
% Spindle
|
|
min_f = 1; max_f = 500;
|
|
rz_dist.f = f_rz(f_rz < max_f & f_rz > min_f);
|
|
rz_dist.pxx_fx = pxx_rz_fx(f_rz < max_f & f_rz > min_f);
|
|
rz_dist.pxx_fy = pxx_rz_fy(f_rz < max_f & f_rz > min_f);
|
|
rz_dist.pxx_fz = pxx_rz_fz(f_rz < max_f & f_rz > min_f);
|
|
|
|
% The identified dynamics are then saved for further use.
|
|
save('./mat/ustation_disturbance_psd.mat', 'rz_dist', 'dy_dist', 'gm_dist')
|
|
|
|
%% Ground Motion
|
|
figure;
|
|
hold on;
|
|
plot(f_gm, sqrt(pxx_gm_x), 'DisplayName', '$D_{wx}$');
|
|
plot(f_gm, sqrt(pxx_gm_y), 'DisplayName', '$D_{wy}$');
|
|
plot(f_gm, sqrt(pxx_gm_z), 'DisplayName', '$D_{wz}$');
|
|
set(gca, 'xscale', 'log'); set(gca, 'yscale', 'log');
|
|
xlabel('Frequency [Hz]'); ylabel('Spectral Density $\left[\frac{m}{\sqrt{Hz}}\right]$')
|
|
xlim([1, 200]);
|
|
leg = legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1);
|
|
leg.ItemTokenSize(1) = 15;
|
|
|
|
figure;
|
|
hold on;
|
|
plot(f_dy, sqrt(pxx_dy_fx), 'DisplayName', '$F_{xD_y}$');
|
|
plot(f_dy, sqrt(pxx_dy_fz), 'DisplayName', '$F_{zD_y}$');
|
|
set(gca, 'xscale', 'log'); set(gca, 'yscale', 'log');
|
|
xlabel('Frequency [Hz]'); ylabel('Spectral Density $\left[\frac{N}{\sqrt{Hz}}\right]$')
|
|
xlim([1, 200]); ylim([1e-3, 1e3]);
|
|
leg = legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1);
|
|
leg.ItemTokenSize(1) = 15;
|
|
|
|
figure;
|
|
hold on;
|
|
plot(f_rz, sqrt(pxx_rz_fx), 'DisplayName', '$F_{xR_z}$');
|
|
plot(f_rz, sqrt(pxx_rz_fy), 'DisplayName', '$F_{yR_z}$');
|
|
plot(f_rz, sqrt(pxx_rz_fz), 'DisplayName', '$F_{zR_z}$');
|
|
set(gca, 'xscale', 'log'); set(gca, 'yscale', 'log');
|
|
xlabel('Frequency [Hz]'); ylabel('Spectral Density $\left[\frac{N}{\sqrt{Hz}}\right]$')
|
|
xlim([1, 200]); ylim([1e-3, 1e3]);
|
|
leg = legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1);
|
|
leg.ItemTokenSize(1) = 15;
|
|
|
|
|
|
|
|
% #+name: fig:ustation_dist_sources
|
|
% #+caption: Measured spectral density of the micro-station disturbance sources. Ground motion (\subref{fig:ustation_dist_source_ground_motion}), translation stage (\subref{fig:ustation_dist_source_translation_stage}) and spindle (\subref{fig:ustation_dist_source_spindle}).
|
|
% #+attr_latex: :options [htbp]
|
|
% #+begin_figure
|
|
% #+attr_latex: :caption \subcaption{\label{fig:ustation_dist_source_ground_motion}Ground Motion}
|
|
% #+attr_latex: :options {0.33\textwidth}
|
|
% #+begin_subfigure
|
|
% #+attr_latex: :width 0.9\linewidth
|
|
% [[file:figs/ustation_dist_source_ground_motion.png]]
|
|
% #+end_subfigure
|
|
% #+attr_latex: :caption \subcaption{\label{fig:ustation_dist_source_translation_stage}Translation Stage}
|
|
% #+attr_latex: :options {0.33\textwidth}
|
|
% #+begin_subfigure
|
|
% #+attr_latex: :width 0.9\linewidth
|
|
% [[file:figs/ustation_dist_source_translation_stage.png]]
|
|
% #+end_subfigure
|
|
% #+attr_latex: :caption \subcaption{\label{fig:ustation_dist_source_spindle}Spindle}
|
|
% #+attr_latex: :options {0.33\textwidth}
|
|
% #+begin_subfigure
|
|
% #+attr_latex: :width 0.9\linewidth
|
|
% [[file:figs/ustation_dist_source_spindle.png]]
|
|
% #+end_subfigure
|
|
% #+end_figure
|
|
|
|
% The disturbances are characterized by their power spectral densities, as shown in Figure ref:fig:ustation_dist_sources.
|
|
% However, to perform time domain simulations, disturbances must be represented by a time domain signal.
|
|
% To generate stochastic time-domain signals with a specific power spectral densities, the discrete inverse Fourier transform is used, as explained in [[cite:&preumont94_random_vibrat_spect_analy chap. 12.11]].
|
|
% Examples of the obtained time-domain disturbance signals are shown in Figure ref:fig:ustation_dist_sources_time.
|
|
|
|
|
|
%% Compute time domain disturbance signals
|
|
initializeDisturbances();
|
|
load('nass_model_disturbances.mat');
|
|
|
|
figure;
|
|
hold on;
|
|
plot(Rz.t, Rz.x, 'DisplayName', '$F_{xR_z}$');
|
|
plot(Rz.t, Rz.y, 'DisplayName', '$F_{yR_z}$');
|
|
plot(Rz.t, Rz.z, 'DisplayName', '$F_{zR_z}$');
|
|
xlabel('Time [s]'); ylabel('Amplitude [N]')
|
|
xlim([0, 1]); ylim([-100, 100]);
|
|
leg = legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
|
|
leg.ItemTokenSize(1) = 15;
|
|
|
|
figure;
|
|
hold on;
|
|
plot(Dy.t, Dy.x, 'DisplayName', '$F_{xD_y}$');
|
|
plot(Dy.t, Dy.z, 'DisplayName', '$F_{zD_y}$');
|
|
xlabel('Time [s]'); ylabel('Amplitude [N]')
|
|
xlim([0, 1]); ylim([-60, 60])
|
|
leg = legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1);
|
|
leg.ItemTokenSize(1) = 15;
|
|
|
|
figure;
|
|
hold on;
|
|
plot(Dw.t, 1e6*Dw.x, 'DisplayName', '$D_{xf}$');
|
|
plot(Dw.t, 1e6*Dw.y, 'DisplayName', '$D_{yf}$');
|
|
plot(Dw.t, 1e6*Dw.z, 'DisplayName', '$D_{zf}$');
|
|
xlabel('Time [s]'); ylabel('Amplitude [$\mu$m]')
|
|
xlim([0, 1]); ylim([-0.6, 0.6])
|
|
leg = legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1);
|
|
leg.ItemTokenSize(1) = 15;
|