Finish report

This commit is contained in:
2023-02-28 14:09:18 +01:00
parent 50dab7913d
commit 2413fc641e
212 changed files with 61262 additions and 16238 deletions

View File

@@ -1,21 +0,0 @@
%% Controller
s = tf('s');
K = 4.5724e11*(s+44.46)/((s+8.127e-08)*(s+0.0002334)*(s+384.9));
%% Open Simulink for Control
open rotating_frame_ctrl.slx
sim rotating_frame_ctrl.slx
%%
figure;
hold on;
plot(sim_out.Time, sim_out.Data(:, 1));
plot(sim_out.Time, sim_out.Data(:, 2));
hold off;
xlabel('Time (s)');
ylabel('Displacement (m)');
legend({'Dx', 'Dy'});
exportFig('D_x_y', 'wide-tall');

View File

@@ -1,52 +0,0 @@
%% Open Simulink file
open rotating_frame.slx
%% Options for Linearized
options = linearizeOptions;
options.SampleTime = 0;
%% Name of the Simulink File
mdl = 'rotating_frame';
%% Input/Output definition
io(1) = linio([mdl, '/fx'], 1, 'input');
io(2) = linio([mdl, '/fy'], 1, 'input');
io(3) = linio([mdl, '/dx'], 1, 'output');
io(4) = linio([mdl, '/dy'], 1, 'output');
%% Run the linearization
angle_e = 0; rot_speed = 0;
G = linearize(mdl, io, 0);
%% Input/Output names
G.InputName = {'Fx', 'Fy'};
G.OutputName = {'Dx', 'Dy'};
%% Run the linearization
angle_e = 0; rot_speed = 2*pi;
Gr = linearize(mdl, io, 0);
%% Input/Output names
Gr.InputName = {'Fx', 'Fy'};
Gr.OutputName = {'Dx', 'Dy'};
%% Run the linearization
angle_e = 1*2*pi/180; rot_speed = 2*pi;
Ge = linearize(mdl, io, 0);
%% Input/Output names
Ge.InputName = {'Fx', 'Fy'};
Ge.OutputName = {'Dx', 'Dy'};
%%
f = figure('visible', 'off');
bode(G);
exportFig('G_x_y', 'wide-tall');
close(f);
figure;
bode(Ge);
% exportFig('G_x_y_e', 'normal-normal');

View File

@@ -1,44 +0,0 @@
%% Open Simulink file
open rotating_frame.slx
%% Options for Linearized
options = linearizeOptions;
options.SampleTime = 0;
%% Name of the Simulink File
mdl = 'rotating_frame';
%% Input/Output definition
io(1) = linio([mdl, '/fu'], 1, 'input');
io(2) = linio([mdl, '/fv'], 1, 'input');
io(3) = linio([mdl, '/dx'], 1, 'output');
io(4) = linio([mdl, '/dy'], 1, 'output');
%% Run the linearization
rot_speed = 2*pi;
G = linearize(mdl, io, 0.0);
%% Input/Output names
G.InputName = {'Fu', 'Fv'};
G.OutputName = {'Dx', 'Dy'};
%% Run the linearization
G1 = linearize(mdl, io, 0.4);
%% Input/Output names
G1.InputName = {'Fu', 'Fv'};
G1.OutputName = {'Dx', 'Dy'};
%% Run the linearization
G2 = linearize(mdl, io, 0.8);
%% Input/Output names
G2.InputName = {'Fu', 'Fv'};
G2.OutputName = {'Dx', 'Dy'};
figure;
bode(G, G1, G2);
exportFig('G_u_v_to_x_y', 'wide-tall');

View File

@@ -1,46 +0,0 @@
%% Open Simulink file
open rotating_frame.slx
%% Options for Linearized
options = linearizeOptions;
options.SampleTime = 0;
%% Name of the Simulink File
mdl = 'rotating_frame';
%% Input/Output definition
io(1) = linio([mdl, '/fu'], 1, 'input');
io(2) = linio([mdl, '/fv'], 1, 'input');
io(3) = linio([mdl, '/du'], 1, 'output');
io(4) = linio([mdl, '/dv'], 1, 'output');
%% Run the linearization
rot_speed = 0;
G = linearize(mdl, io, 0.0);
%% Input/Output names
G.InputName = {'Fu', 'Fv'};
G.OutputName = {'Du', 'Dv'};
%% Run the linearization
rot_speed = 2*pi/60;
G1 = linearize(mdl, io, 0.0);
%% Input/Output names
G1.InputName = {'Fu', 'Fv'};
G1.OutputName = {'Du', 'Dv'};
%% Run the linearization
rot_speed = 2*pi;
G2 = linearize(mdl, io, 0.0);
%% Input/Output names
G2.InputName = {'Fu', 'Fv'};
G2.OutputName = {'Du', 'Dv'};
figure;
bode(G, G1, G2);
legend({'$\omega = 0$', '$\omega = 2pi/60$', '$\omega = 2pi$'})
exportFig('G_u_v', 'normal-normal');

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,220 @@
%% 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
%% Colors for the figures
colors = colororder;
%% Simscape model name
mdl = 'rotating_model';
% System Poles: Campbell Diagram
% The poles of $\mathbf{G}_d$ are the complex solutions $p$ of equation eqref:eq:poles.
% #+name: eq:poles
% \begin{equation}
% \left( \frac{p^2}{{\omega_0}^2} + 2 \xi \frac{p}{\omega_0} + 1 - \frac{{\Omega}^2}{{\omega_0}^2} \right)^2 + \left( 2 \frac{\Omega}{\omega_0} \frac{p}{\omega_0} \right)^2 = 0
% \end{equation}
% Supposing small damping ($\xi \ll 1$), two pairs of complex conjugate poles are obtained as shown in equation eqref:eq:pole_values.
% #+name: eq:pole_values
% \begin{subequations}
% \begin{align}
% p_{+} &= - \xi \omega_0 \left( 1 + \frac{\Omega}{\omega_0} \right) \pm j \omega_0 \left( 1 + \frac{\Omega}{\omega_0} \right) \\
% p_{-} &= - \xi \omega_0 \left( 1 - \frac{\Omega}{\omega_0} \right) \pm j \omega_0 \left( 1 - \frac{\Omega}{\omega_0} \right)
% \end{align}
% \end{subequations}
%% Model parameters for first analysis
kn = 1; % Actuator Stiffness [N/m]
mn = 1; % Payload Mass [kg]
cn = 0.05; % Actuator Damping [N/(m/s)]
xin = cn/(2*sqrt(kn*mn)); % Modal Damping [-]
w0n = sqrt(kn/mn); % Natural Frequency [rad/s]
%% Computation of the poles as a function of the rotating velocity
Wzs = linspace(0, 2, 51); % Vector of rotation speeds [rad/s]
p_ws = zeros(4, length(Wzs));
for i = 1:length(Wzs)
Wz = Wzs(i);
pole_G = pole(1/(((s^2)/(w0n^2) + 2*xin*s/w0n + 1 - (Wz^2)/(w0n^2))^2 + (2*Wz*s/(w0n^2))^2));
[~, i_sort] = sort(imag(pole_G));
p_ws(:, i) = pole_G(i_sort);
end
clear pole_G;
% The real and complex parts of these two pairs of complex conjugate poles are represented in Figure ref:fig:rotating_campbell_diagram as a function of the rotational speed $\Omega$.
% As the rotational speed increases, $p_{+}$ goes to higher frequencies and $p_{-}$ goes to lower frequencies.
% The system becomes unstable for $\Omega > \omega_0$ as the real part of $p_{-}$ is positive.
% Physically, the negative stiffness term $-m\Omega^2$ induced by centrifugal forces exceeds the spring stiffness $k$.
%% Campbell diagram - Real and Imaginary parts of the poles as a function of the rotating velocity
figure;
tiledlayout(1, 2, 'TileSpacing', 'Compact', 'Padding', 'None');
ax1 = nexttile();
hold on;
plot(Wzs, real(p_ws(1, :)), '-', 'color', colors(1,:), 'DisplayName', '$p_{+}$')
plot(Wzs, real(p_ws(4, :)), '-', 'color', colors(1,:), 'HandleVisibility', 'off')
plot(Wzs, real(p_ws(2, :)), '-', 'color', colors(2,:), 'DisplayName', '$p_{-}$')
plot(Wzs, real(p_ws(3, :)), '-', 'color', colors(2,:), 'HandleVisibility', 'off')
plot(Wzs, zeros(size(Wzs)), 'k--', 'HandleVisibility', 'off')
hold off;
xlabel('Rotational Speed $\Omega$'); ylabel('Real Part');
leg = legend('location', 'northwest', 'FontSize', 8);
leg.ItemTokenSize(1) = 8;
xlim([0, 2*w0n]);
xticks([0,w0n/2,w0n,3/2*w0n,2*w0n])
xticklabels({'$0$', '', '$\omega_0$', '', '$2 \omega_0$'})
ylim([-3*xin, 3*xin]);
yticks([-3*xin, -2*xin, -xin, 0, xin, 2*xin, 3*xin])
yticklabels({'', '', '$-\xi\omega_0$', '$0$', ''})
ax2 = nexttile();
hold on;
plot(Wzs, imag(p_ws(1, :)), '-', 'color', colors(1,:))
plot(Wzs, imag(p_ws(4, :)), '-', 'color', colors(1,:))
plot(Wzs, imag(p_ws(2, :)), '-', 'color', colors(2,:))
plot(Wzs, imag(p_ws(3, :)), '-', 'color', colors(2,:))
plot(Wzs, zeros(size(Wzs)), 'k--')
hold off;
xlabel('Rotational Speed $\Omega$'); ylabel('Imaginary Part');
xlim([0, 2*w0n]);
xticks([0,w0n/2,w0n,3/2*w0n,2*w0n])
xticklabels({'$0$', '', '$\omega_0$', '', '$2 \omega_0$'})
ylim([-3*w0n, 3*w0n]);
yticks([-3*w0n, -2*w0n, -w0n, 0, w0n, 2*w0n, 3*w0n])
yticklabels({'', '', '$-\omega_0$', '$0$', '$\omega_0$', '', ''})
% Identify Generic Dynamics :noexport:
%% Sample
ms = 0.5; % Sample mass [kg]
%% Tuv Stage
kn = 1; % Stiffness [N/m]
mn = 0.5; % Tuv mass [kg]
cn = 0.01*2*sqrt(kn*(mn+ms)); % Damping [N/(m/s)]
%% General Configuration
model_config = struct();
model_config.controller = "open_loop"; % Default: Open-Loop
model_config.Tuv_type = "normal"; % Default: 2DoF stage
%% Input/Output definition
clear io; io_i = 1;
io(io_i) = linio([mdl, '/controller'], 1, 'openinput'); io_i = io_i + 1; % [Fu, Fv]
io(io_i) = linio([mdl, '/fd'], 1, 'openinput'); io_i = io_i + 1; % [Fdu, Fdv]
io(io_i) = linio([mdl, '/xf'], 1, 'openinput'); io_i = io_i + 1; % [Dfx, Dfy]
io(io_i) = linio([mdl, '/translation_stage'], 1, 'openoutput'); io_i = io_i + 1; % [Fmu, Fmv]
io(io_i) = linio([mdl, '/translation_stage'], 2, 'openoutput'); io_i = io_i + 1; % [Du, Dv]
io(io_i) = linio([mdl, '/ext_metrology'], 1, 'openoutput'); io_i = io_i + 1; % [Dx, Dy]
%% Tested rotating velocities [rad/s]
Wzs = [0, 0.1, 0.2, 0.7, 1.2]; % Vector of rotation speeds [rad/s]
Gs = {zeros(2, 2, length(Wzs))}; % Direct terms
for i = 1:length(Wzs)
Wz = Wzs(i);
%% Linearize the model
G = linearize(mdl, io, 0);
%% Input/Output definition
G.InputName = {'Fu', 'Fv', 'Fdx', 'Fdy', 'Dfx', 'Dfy'};
G.OutputName = {'fu', 'fv', 'Du', 'Dv', 'Dx', 'Dy'};
Gs{:,:,i} = G;
end
%% Save All Identified Plants
save('./mat/rotating_generic_plants.mat', 'Gs', 'Wzs');
% System Dynamics: Effect of rotation
% The system dynamics from actuator forces $[F_u, F_v]$ to the relative motion $[d_u, d_v]$ is identified for several rotating velocities.
% Looking at the transfer function matrix $\mathbf{G}_d$ in equation eqref:eq:Gd_w0_xi_k, one can see that the two diagonal (direct) terms are equal and that the two off-diagonal (coupling) terms are opposite.
% The bode plot of these two terms are shown in Figure ref:fig:rotating_direct_coupling_bode_plot for several rotational speeds $\Omega$.
% These plots confirm the expected behavior: the frequency of the two pairs of complex conjugate poles are further separated as $\Omega$ increases.
% For $\Omega > \omega_0$, the low frequency pair of complex conjugate poles $p_{-}$ becomes unstable.
%% Bode plot of the direct and coupling terms for several rotating velocities
freqs = logspace(-1, 1, 1000);
figure;
tiledlayout(3, 2, 'TileSpacing', 'Compact', 'Padding', 'None');
% Magnitude
ax1 = nexttile([2, 1]);
hold on;
for i = 1:length(Wzs)
plot(freqs, abs(squeeze(freqresp(Gs{i}('du', 'Fu'), freqs, 'rad/s'))), '-', 'color', colors(i,:))
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude [m/N]');
title('Direct terms: $d_u/F_u$, $d_v/F_v$');
ylim([1e-2, 1e2]);
yticks([1e-2,1e-1,1,1e1,1e2])
yticklabels({'$0.01/k$', '$0.1/k$', '$1/k$', '$10/k$', '$100/k$'})
ax2 = nexttile([2, 1]);
hold on;
for i = 1:length(Wzs)
plot(freqs, abs(squeeze(freqresp(Gs{i}('dv', 'Fu'), freqs, 'rad/s'))), '-', 'color', colors(i,:), ...
'DisplayName', sprintf('$\\Omega = %.1f \\omega_0$', Wzs(i)))
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
title('Coupling Terms: $d_u/F_v$, $d_v/F_u$');
ldg = legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1);
ldg.ItemTokenSize = [10, 1];
ylim([1e-2, 1e2]);
ax3 = nexttile;
hold on;
for i = 1:length(Wzs)
plot(freqs, 180/pi*angle(squeeze(freqresp(Gs{i}('du', 'Fu'), freqs, 'rad/s'))), '-', 'color', colors(i,:))
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [rad/s]'); ylabel('Phase [deg]');
yticks(-180:90:180);
ylim([-180 180]);
xticks([1e-1,1,1e1])
xticklabels({'$0.1 \omega_0$', '$\omega_0$', '$10 \omega_0$'})
ax4 = nexttile;
hold on;
for i = 1:length(Wzs)
plot(freqs, 180/pi*angle(squeeze(freqresp(Gs{i}('dv', 'Fu'), freqs, 'rad/s'))), '-', 'color', colors(i,:));
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [rad/s]'); set(gca, 'YTickLabel',[]);
xticks([1e-1,1,1e1])
xticklabels({'$0.1 \omega_0$', '$\omega_0$', '$10 \omega_0$'})
linkaxes([ax1,ax2,ax3,ax4],'x');
xlim([freqs(1), freqs(end)]);
linkaxes([ax1,ax2],'y');

View File

@@ -0,0 +1,152 @@
%% 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
%% Colors for the figures
colors = colororder;
%% Simscape model name
mdl = 'rotating_model';
%% Load "Generic" system dynamics
load('rotating_generic_plants.mat', 'Gs', 'Wzs');
% Effect of the rotation speed on the IFF plant dynamics
% The transfer functions from actuator forces $[F_u,\ F_v]$ to the measured force sensors $[f_u,\ f_v]$ are identified for several rotating velocities and shown in Figure ref:fig:rotating_iff_bode_plot_effect_rot.
% As was expected from the derived equations of motion:
% - when $0 < \Omega < \omega_0$: the low frequency gain is no longer zero and two (non-minimum phase) real zero appears at low frequency.
% The low frequency gain increases with $\Omega$.
% A pair of (minimum phase) complex conjugate zeros appears between the two complex conjugate poles that are split further apart as $\Omega$ increases.
% - when $\omega_0 < \Omega$: the low frequency pole becomes unstable.
%% Bode plot of the direct and coupling term for Integral Force Feedback - Effect of rotation
figure;
freqs = logspace(-2, 1, 1000);
figure;
tiledlayout(3, 2, 'TileSpacing', 'Compact', 'Padding', 'None');
% Magnitude
ax1 = nexttile([2, 1]);
hold on;
for i = 1:length(Wzs)
plot(freqs, abs(squeeze(freqresp(Gs{i}('fu', 'Fu'), freqs, 'rad/s'))), '-', 'color', colors(i,:))
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude [N/N]');
title('Direct terms: $f_u/F_u$, $f_v/F_v$');
ylim([1e-3, 1e2]);
ax2 = nexttile([2, 1]);
hold on;
for i = 1:length(Wzs)
plot(freqs, abs(squeeze(freqresp(Gs{i}('fv', 'Fu'), freqs, 'rad/s'))), '-', 'color', colors(i,:), ...
'DisplayName', sprintf('$\\Omega = %.1f \\omega_0$', Wzs(i)))
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
title('Coupling Terms: $f_u/F_v$, $f_v/F_u$');
ldg = legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
ldg.ItemTokenSize = [10, 1];
ylim([1e-3, 1e2]);
ax3 = nexttile;
hold on;
for i = 1:length(Wzs)
plot(freqs, 180/pi*angle(squeeze(freqresp(Gs{i}('fu', 'Fu'), freqs, 'rad/s'))), '-', 'color', colors(i,:))
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [rad/s]'); ylabel('Phase [deg]');
yticks(-180:90:180);
ylim([-180 180]);
xticks([1e-2,1e-1,1,1e1])
xticklabels({'$0.01 \omega_0$', '$0.1 \omega_0$', '$\omega_0$', '$10 \omega_0$'})
ax4 = nexttile;
hold on;
for i = 1:length(Wzs)
plot(freqs, 180/pi*angle(squeeze(freqresp(Gs{i}('fv', 'Fu'), freqs, 'rad/s'))), '-', 'color', colors(i,:));
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [rad/s]'); set(gca, 'YTickLabel',[]);
yticks(-180:90:180);
ylim([-180 180]);
xticks([1e-2,1e-1,1,1e1])
xticklabels({'$0.01 \omega_0$', '$0.1 \omega_0$', '$\omega_0$', '$10 \omega_0$'})
linkaxes([ax1,ax2,ax3,ax4],'x');
xlim([freqs(1), freqs(end)]);
linkaxes([ax1,ax2],'y');
% #+name: fig:rotating_iff_diagram
% #+caption: Control diagram for decentralized Integral Force Feedback
% #+RESULTS:
% [[file:figs/rotating_iff_diagram.png]]
% The decentralized IFF controller $\bm{K}_F$ corresponds to a diagonal controller with integrators:
% #+name: eq:Kf_pure_int
% \begin{equation}
% \begin{aligned}
% \mathbf{K}_{F}(s) &= \begin{bmatrix} K_{F}(s) & 0 \\ 0 & K_{F}(s) \end{bmatrix} \\
% K_{F}(s) &= g \cdot \frac{1}{s}
% \end{aligned}
% \end{equation}
% In order to see how the IFF controller affects the poles of the closed loop system, a Root Locus plot (Figure ref:fig:rotating_root_locus_iff_pure_int) is constructed as follows: the poles of the closed-loop system are drawn in the complex plane as the controller gain $g$ varies from $0$ to $\infty$ for the two controllers $K_{F}$ simultaneously.
% As explained in cite:preumont08_trans_zeros_struc_contr_with,skogestad07_multiv_feedb_contr, the closed-loop poles start at the open-loop poles (shown by $\tikz[baseline=-0.6ex] \node[cross out, draw=black, minimum size=1ex, line width=2pt, inner sep=0pt, outer sep=0pt] at (0, 0){};$) for $g = 0$ and coincide with the transmission zeros (shown by $\tikz[baseline=-0.6ex] \draw[line width=2pt, inner sep=0pt, outer sep=0pt] (0,0) circle[radius=3pt];$) as $g \to \infty$.
% #+begin_important
% Whereas collocated IFF is usually associated with unconditional stability cite:preumont91_activ, this property is lost due to gyroscopic effects as soon as the rotation velocity in non-null.
% This can be seen in the Root Locus plot (Figure ref:fig:rotating_root_locus_iff_pure_int) where poles corresponding to the controller are bound to the right half plane implying closed-loop system instability.
% #+end_important
% Physically, this can be explained like so: at low frequency, the loop gain is very large due to the pure integrator in $K_{F}$ and the finite gain of the plant (Figure ref:fig:rotating_iff_bode_plot_effect_rot).
% The control system is thus canceling the spring forces which makes the suspended platform no able to hold the payload against centrifugal forces, hence the instability.
%% Root Locus for the Decentralized Integral Force Feedback controller
figure;
Kiff = 1/s*eye(2);
gains = logspace(-2, 4, 300);
Wz_i = [1,3,4];
hold on;
for i = 1:length(Wz_i)
plot(real(pole(Gs{Wz_i(i)}({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff)), imag(pole(Gs{Wz_i(i)}({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff)), 'x', 'color', colors(i,:), ...
'DisplayName', sprintf('$\\Omega = %.1f \\omega_0 $', Wzs(Wz_i(i))),'MarkerSize',8);
plot(real(tzero(Gs{Wz_i(i)}({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff)), imag(tzero(Gs{Wz_i(i)}({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff)), 'o', 'color', colors(i,:), ...
'HandleVisibility', 'off','MarkerSize',8);
for g = gains
cl_poles = pole(feedback(Gs{Wz_i(i)}({'fu', 'fv'}, {'Fu', 'Fv'}), g*Kiff, -1));
plot(real(cl_poles), imag(cl_poles), '.', 'color', colors(i,:), ...
'HandleVisibility', 'off','MarkerSize',4);
end
end
hold off;
axis square;
xlim([-1.8, 0.2]); ylim([0, 2]);
xticks([-1, 0])
xticklabels({'-$\omega_0$', '$0$'})
yticks([0, 1, 2])
yticklabels({'$0$', '$\omega_0$', '$2 \omega_0$'})
xlabel('Real Part'); ylabel('Imaginary Part');
leg = legend('location', 'northwest', 'FontSize', 8);
leg.ItemTokenSize(1) = 8;

474
matlab/rotating_3_iff_hpf.m Normal file
View File

@@ -0,0 +1,474 @@
%% 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
%% Colors for the figures
colors = colororder;
%% Simscape model name
mdl = 'rotating_model';
%% Load "Generic" system dynamics
load('rotating_generic_plants.mat', 'Gs', 'Wzs');
% Modified Integral Force Feedback Controller
% The Integral Force Feedback Controller is modified such that instead of using pure integrators, pseudo integrators (i.e. low pass filters) are used:
% \begin{equation}
% K_{\text{IFF}}(s) = g\frac{1}{\omega_i + s} \begin{bmatrix}
% 1 & 0 \\
% 0 & 1
% \end{bmatrix}
% \end{equation}
% where $\omega_i$ characterize down to which frequency the signal is integrated.
% Let's arbitrary choose the following control parameters:
%% Modified IFF - parameters
g = 2; % Controller gain
wi = 0.1; % HPF Cut-Off frequency [rad/s]
Kiff = (g/s)*eye(2); % Pure IFF
Kiff_hpf = (g/(wi+s))*eye(2); % IFF with added HPF
% The loop gains ($K_F(s)$ times the direct dynamics $f_u/F_u$) with and without the added HPF are shown in Figure ref:fig:rotating_iff_modified_loop_gain.
% The effect of the added HPF limits the low frequency gain to finite values as expected.
%% Loop gain for the IFF with pure integrator and modified IFF with added high pass filter
freqs = logspace(-2, 1, 1000);
Wz_i = 2;
figure;
tiledlayout(3, 1, 'TileSpacing', 'Compact', 'Padding', 'None');
% Magnitude
ax1 = nexttile([2, 1]);
hold on;
plot(freqs, abs(squeeze(freqresp(Gs{Wz_i}('fu', 'Fu')*Kiff(1,1), freqs, 'rad/s'))))
plot(freqs, abs(squeeze(freqresp(Gs{Wz_i}('fu', 'Fu')*Kiff_hpf(1,1), freqs, 'rad/s'))))
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Loop Gain');
% Phase
ax2 = nexttile;
hold on;
plot(freqs, 180/pi*angle(squeeze(freqresp(Gs{Wz_i}('fu', 'Fu')*Kiff(1,1), freqs, 'rad/s'))), 'DisplayName', 'IFF')
plot(freqs, 180/pi*angle(squeeze(freqresp(Gs{Wz_i}('fu', 'Fu')*Kiff_hpf(1,1), freqs, 'rad/s'))), 'DisplayName', 'IFF + HPF')
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [rad/s]'); ylabel('Phase [deg]');
yticks(-180:90:180);
ylim([-180 180]);
xticks([1e-2,1e-1,1,1e1])
xticklabels({'$0.01 \omega_0$', '$0.1 \omega_0$', '$\omega_0$', '$10 \omega_0$'})
leg = legend('location', 'southwest', 'FontSize', 8);
leg.ItemTokenSize(1) = 20;
linkaxes([ax1,ax2],'x');
xlim([freqs(1), freqs(end)]);
% #+name: fig:rotating_iff_modified_loop_gain
% #+caption: Loop gain for the IFF with pure integrator and modified IFF with added high pass filter ($\Omega = 0.1\omega_0$)
% #+RESULTS:
% [[file:figs/rotating_iff_modified_loop_gain.png]]
% The Root Locus plots for the decentralized IFF with and without the HPF are displayed in Figure ref:fig:rotating_iff_root_locus_hpf.
% With the added HPF, the poles of the closed loop system are shown to be *stable up to some value of the gain* $g_\text{max}$ given by equation eqref:eq:gmax_iff_hpf.
% #+name: eq:gmax_iff_hpf
% \begin{equation}
% \boxed{g_{\text{max}} = \omega_i \left( \frac{{\omega_0}^2}{\Omega^2} - 1 \right)}
% \end{equation}
% It is interesting to note that $g_{\text{max}}$ also corresponds to the controller gain at which the low frequency loop gain (Figure ref:fig:rotating_iff_modified_loop_gain) reaches one.
%% Root Locus for the initial IFF and the modified IFF
gains = logspace(-2, 4, 200);
figure;
tiledlayout(1, 3, 'TileSpacing', 'Compact', 'Padding', 'None');
ax1 = nexttile([1,2]);
hold on;
for g = gains
clpoles = pole(feedback(Gs{Wz_i}({'fu', 'fv'}, {'Fu', 'Fv'}), g*Kiff));
plot(real(clpoles), imag(clpoles), '.', 'color', colors(1,:), 'HandleVisibility', 'off','MarkerSize',4);
end
for g = gains
clpoles = pole(feedback(Gs{Wz_i}({'fu', 'fv'}, {'Fu', 'Fv'}), g*Kiff_hpf));
plot(real(clpoles), imag(clpoles), '.', 'color', colors(2,:), 'HandleVisibility', 'off','MarkerSize',4);
end
% Pure Integrator
plot(real(pole(Gs{Wz_i}({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff)), ...
imag(pole(Gs{Wz_i}({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff)), ...
'x', 'color', colors(1,:), 'DisplayName', 'IFF','MarkerSize',8);
plot(real(tzero(Gs{Wz_i}({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff)), ...
imag(tzero(Gs{Wz_i}({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff)), ...
'o', 'color', colors(1,:), 'HandleVisibility', 'off','MarkerSize',8);
% Modified IFF
plot(real(pole(Gs{Wz_i}({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff_hpf)), ...
imag(pole(Gs{Wz_i}({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff_hpf)), ...
'x', 'color', colors(2,:), 'DisplayName', 'IFF + HPF','MarkerSize',8);
plot(real(tzero(Gs{Wz_i}({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff_hpf)), ...
imag(tzero(Gs{Wz_i}({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff_hpf)), ...
'o', 'color', colors(2,:), 'HandleVisibility', 'off','MarkerSize',8);
hold off;
axis square;
leg = legend('location', 'northwest', 'FontSize', 8);
leg.ItemTokenSize(1) = 8;
xlabel('Real Part'); ylabel('Imaginary Part');
xlim([-2.25, 0.25]); ylim([-1.25, 1.25]);
xticks([-2, -1, 0])
xticklabels({'$-2\omega_0$', '$-\omega_0$', '$0$'})
yticks([-1, 0, 1])
yticklabels({'$-\omega_0$', '$0$', '$\omega_0$'})
ax2 = nexttile();
hold on;
for g = gains
clpoles = pole(feedback(Gs{Wz_i}({'fu', 'fv'}, {'Fu', 'Fv'}), g*Kiff));
plot(real(clpoles), imag(clpoles), '.', 'color', colors(1,:),'MarkerSize',4);
end
for g = gains
clpoles = pole(feedback(Gs{Wz_i}({'fu', 'fv'}, {'Fu', 'Fv'}), g*Kiff_hpf));
plot(real(clpoles), imag(clpoles), '.', 'color', colors(2,:),'MarkerSize',4);
end
% Pure Integrator
plot(real(pole(Gs{Wz_i}({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff)), ...
imag(pole(Gs{Wz_i}({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff)), ...
'x', 'color', colors(1,:),'MarkerSize',8);
plot(real(tzero(Gs{Wz_i}({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff)), ...
imag(tzero(Gs{Wz_i}({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff)), ...
'o', 'color', colors(1,:),'MarkerSize',8);
% Modified IFF
plot(real(pole(Gs{Wz_i}({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff_hpf)), ...
imag(pole(Gs{Wz_i}({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff_hpf)), ...
'x', 'color', colors(2,:),'MarkerSize',8);
plot(real(tzero(Gs{Wz_i}({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff_hpf)), ...
imag(tzero(Gs{Wz_i}({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff_hpf)), ...
'o', 'color', colors(2,:),'MarkerSize',8);
hold off;
axis square;
xlabel('Real Part'); ylabel('Imaginary Part');
title('Zoom near the origin');
xlim([-0.15, 0.1]); ylim([-0.15, 0.15]);
xticks([-0.1, 0, 0.1])
xticklabels({'$-0.1\omega_0$', '$0$', '$0.1\omega_0$'})
yticks([-0.1, 0, 0.1])
yticklabels({'$-0.1\omega_0$', '$0$', '$0.1\omega_0$'})
% Optimal IFF with HPF parameters $\omega_i$ and $g$
% Two parameters can be tuned for the modified controller in equation eqref:eq:iff_lhf: the gain $g$ and the pole's location $\omega_i$.
% The optimal values of $\omega_i$ and $g$ are here considered as the values for which the damping of all the closed-loop poles are simultaneously maximized.
% In order to visualize how $\omega_i$ does affect the attainable damping, the Root Locus plots for several $\omega_i$ are displayed in Figure ref:fig:rotating_root_locus_iff_modified_effect_wi.
% It is shown that even though small $\omega_i$ seem to allow more damping to be added to the suspension modes, the control gain $g$ may be limited to small values due to equation eqref:eq:gmax_iff_hpf.
%% High Pass Filter Cut-Off Frequency
wis = [0.01, 0.1, 0.5, 1]*Wzs(2); % [rad/s]
%% Root Locus for the initial IFF and the modified IFF
gains = logspace(-2, 4, 200);
figure;
tiledlayout(1, 2, 'TileSpacing', 'Compact', 'Padding', 'None');
ax1 = nexttile();
hold on;
for wi_i = 1:length(wis)
wi = wis(wi_i);
Kiff = (1/(wi+s))*eye(2);
L(wi_i) = plot(nan, nan, '.', 'color', colors(wi_i,:), 'DisplayName', sprintf('$\\omega_i = %.2f \\omega_0$', wi./Wzs(2)));
for g = gains
clpoles = pole(feedback(Gs{2}({'fu', 'fv'}, {'Fu', 'Fv'}), g*Kiff));
plot(real(clpoles), imag(clpoles), '.', 'color', colors(wi_i,:),'MarkerSize',4);
end
plot(real(pole(Gs{2}({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff)), ...
imag(pole(Gs{2}({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff)), ...
'x', 'color', colors(wi_i,:),'MarkerSize',8);
plot(real(tzero(Gs{2}({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff)), ...
imag(tzero(Gs{2}({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff)), ...
'o', 'color', colors(wi_i,:),'MarkerSize',8);
end
hold off;
axis square;
xlim([-2.3, 0.1]); ylim([-1.2, 1.2]);
xticks([-2:1:2]); yticks([-2:1:2]);
leg = legend(L, 'location', 'northwest', 'FontSize', 8);
leg.ItemTokenSize(1) = 8;
xlabel('Real Part'); ylabel('Imaginary Part');
clear L
xlim([-2.25, 0.25]); ylim([-1.25, 1.25]);
xticks([-2, -1, 0])
xticklabels({'$-2\omega_0$', '$-\omega_0$', '$0$'})
yticks([-1, 0, 1])
yticklabels({'$-\omega_0$', '$0$', '$\omega_0$'})
ax2 = nexttile();
hold on;
for wi_i = 1:length(wis)
wi = wis(wi_i);
Kiff = (1/(wi+s))*eye(2);
L(wi_i) = plot(nan, nan, '.', 'color', colors(wi_i,:));
for g = gains
clpoles = pole(feedback(Gs{2}({'fu', 'fv'}, {'Fu', 'Fv'}), g*Kiff));
plot(real(clpoles), imag(clpoles), '.', 'color', colors(wi_i,:),'MarkerSize',4);
end
plot(real(pole(Gs{2}({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff)), ...
imag(pole(Gs{2}({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff)), ...
'x', 'color', colors(wi_i,:),'MarkerSize',8);
plot(real(tzero(Gs{2}({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff)), ...
imag(tzero(Gs{2}({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff)), ...
'o', 'color', colors(wi_i,:),'MarkerSize',8);
end
hold off;
axis square;
xlim([-2.3, 0.1]); ylim([-1.2, 1.2]);
xticks([-2:1:2]); yticks([-2:1:2]);
xlabel('Real Part'); ylabel('Imaginary Part');
title('Zoom near the origin');
clear L
xlim([-0.15, 0.1]); ylim([-0.15, 0.15]);
xticks([-0.1, 0, 0.1])
xticklabels({'$-0.1\omega_0$', '$0$', '$0.1\omega_0$'})
yticks([-0.1, 0, 0.1])
yticklabels({'$-0.1\omega_0$', '$0$', '$0.1\omega_0$'})
% #+name: fig:rotating_root_locus_iff_modified_effect_wi
% #+caption: Root Locus for several high pass filter cut-off frequency
% #+RESULTS:
% [[file:figs/rotating_root_locus_iff_modified_effect_wi.png]]
% In order to study this trade off, the attainable closed-loop damping ratio $\xi_{\text{cl}}$ is computed as a function of $\omega_i/\omega_0$.
% The gain $g_{\text{opt}}$ at which this maximum damping is obtained is also displayed and compared with the gain $g_{\text{max}}$ at which the system becomes unstable (Figure ref:fig:rotating_iff_hpf_optimal_gain).
% Three regions can be observed:
% - $\omega_i/\omega_0 < 0.02$: the added damping is limited by the maximum allowed control gain $g_{\text{max}}$
% - $0.02 < \omega_i/\omega_0 < 0.2$: the attainable damping ratio is maximized and is reached for $g \approx 2$
% - $0.2 < \omega_i/\omega_0$: the added damping decreases as $\omega_i/\omega_0$ increases.
%% Compute the optimal control gain
wis = logspace(-2, 1, 100); % [rad/s]
opt_xi = zeros(1, length(wis)); % Optimal simultaneous damping
opt_gain = zeros(1, length(wis)); % Corresponding optimal gain
for wi_i = 1:length(wis)
wi = wis(wi_i);
Kiff = 1/(s + wi)*eye(2);
fun = @(g)computeSimultaneousDamping(g, Gs{2}({'fu', 'fv'}, {'Fu', 'Fv'}), Kiff);
[g_opt, xi_opt] = fminsearch(fun, 0.5*wi*((1/Wzs(2))^2 - 1));
opt_xi(wi_i) = 1/xi_opt;
opt_gain(wi_i) = g_opt;
end
%% Attainable damping ratio as a function of wi/w0. Corresponding control gain g_opt and g_max are also shown
figure;
yyaxis left
plot(wis, opt_xi, '-', 'DisplayName', '$\xi_{cl}$');
set(gca, 'YScale', 'lin');
ylim([0,1]);
ylabel('Damping Ratio $\xi$');
yyaxis right
hold on;
plot(wis, opt_gain, '-', 'DisplayName', '$g_{opt}$');
plot(wis, wis*((1/Wzs(2))^2 - 1), '--', 'DisplayName', '$g_{max}$');
set(gca, 'YScale', 'lin');
ylim([0,10]);
ylabel('Controller gain $g$');
xlabel('$\omega_i/\omega_0$');
set(gca, 'XScale', 'log');
legend('location', 'northeast', 'FontSize', 8);
% Obtained Damped Plant
% Let's choose $\omega_i = 0.1 \cdot \omega_0$ and compute the damped plant.
% The undamped and damped plants are compared in Figure ref:fig:rotating_iff_hpf_damped_plant in blue and red respectively.
% A well damped plant is indeed obtained.
% However, the magnitude of the coupling term ($d_v/F_u$) is larger then IFF is applied.
%% Compute damped plant
wi = 0.1; % [rad/s]
g = 2; % Gain
Kiff_hpf = (g/(wi+s))*eye(2); % IFF with added HPF
Kiff_hpf.InputName = {'fu', 'fv'};
Kiff_hpf.OutputName = {'Fu', 'Fv'};
G_iff_hpf = feedback(Gs{2}, Kiff_hpf, 'name');
isstable(G_iff_hpf) % Verify stability
%% Damped plant with IFF and added HPF - Transfer function from $F_u$ to $d_u$
freqs = logspace(-2, 1, 1000);
figure;
tiledlayout(3, 1, 'TileSpacing', 'Compact', 'Padding', 'None');
% Magnitude
ax1 = nexttile([2, 1]);
hold on;
plot(freqs, abs(squeeze(freqresp(Gs{2}('Du', 'Fu'), freqs, 'rad/s'))), '-', 'color', [zeros(1,3)], ...
'DisplayName', '$d_u/F_u$, OL')
plot(freqs, abs(squeeze(freqresp(G_iff_hpf('Du', 'Fu'), freqs, 'rad/s'))), '-', 'color', [colors(1,:)], ...
'DisplayName', '$d_u/F_u$, IFF')
plot(freqs, abs(squeeze(freqresp(Gs{2}('Dv', 'Fu'), freqs, 'rad/s'))), '-', 'color', [zeros(1,3), 0.5], ...
'DisplayName', '$d_v/F_u$, OL')
plot(freqs, abs(squeeze(freqresp(G_iff_hpf('Dv', 'Fu'), freqs, 'rad/s'))), '-', 'color', [colors(1,:), 0.5], ...
'DisplayName', '$d_v/F_u$, IFF')
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude [m/N]');
leg = legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 2);
leg.ItemTokenSize(1) = 20;
ax2 = nexttile;
hold on;
plot(freqs, 180/pi*angle(squeeze(freqresp(Gs{2}('Du', 'Fu'), freqs, 'rad/s'))), '-', 'color', zeros(1,3))
plot(freqs, 180/pi*angle(squeeze(freqresp(G_iff_hpf('Du', 'Fu'), freqs, 'rad/s'))), '-', 'color', colors(1,:))
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [rad/s]'); ylabel('Phase [deg]');
yticks(-180:90:180);
ylim([-180 180]);
xticks([1e-2,1e-1,1,1e1])
xticklabels({'$0.01 \omega_0$', '$0.1 \omega_0$', '$\omega_0$', '$10 \omega_0$'})
linkaxes([ax1,ax2],'x');
xlim([freqs(1), freqs(end)]);
% #+name: fig:rotating_iff_hpf_damped_plant
% #+caption: Damped plant with IFF and added HPF - Transfer function from $F_u$ to $d_u$, $\omega_i = 0.1 \cdot \omega_0$, $\Omega = 0.1 \cdot \omega_0$
% #+RESULTS:
% [[file:figs/rotating_iff_hpf_damped_plant.png]]
% In order to study how $\omega_i$ affects the coupling of the damped plant, the closed-loop plant is identified for several $\omega_i$.
% The direct and coupling terms of the plants are shown in Figure ref:fig:rotating_iff_hpf_damped_plant_effect_wi_coupling (left) and the ratio between the two (i.e. the coupling ratio) is shown in Figure ref:fig:rotating_iff_hpf_damped_plant_effect_wi_coupling (right).
% The coupling ratio is decreasing as $\omega_i$ increases.
% There is therefore a *trade-off between achievable damping and coupling ratio* for the choice of $\omega_i$.
% The same trade-off can be seen between achievable damping and loss of compliance at low frequency (see Figure ref:fig:rotating_iff_hpf_effect_wi_compliance).
%% Compute damped plant
wis = [0.03, 0.1, 0.5]; % [rad/s]
g = 2; % Gain
Gs_iff_hpf = {};
for i = 1:length(wis)
Kiff_hpf = (g/(wis(i)+s))*eye(2); % IFF with added HPF
Kiff_hpf.InputName = {'fu', 'fv'};
Kiff_hpf.OutputName = {'Fu', 'Fv'};
Gs_iff_hpf(i) = {feedback(Gs{2}, Kiff_hpf, 'name')};
end
%% Effect of $\omega_i$ on the damped plant coupling
freqs = logspace(-2, 1, 1000);
figure;
tiledlayout(3, 2, 'TileSpacing', 'Compact', 'Padding', 'None');
% Magnitude
ax1 = nexttile([2, 1]);
hold on;
for i = 1:length(wis)
plot(freqs, abs(squeeze(freqresp(Gs_iff_hpf{i}('Du', 'Fu'), freqs, 'rad/s'))), '-', 'color', [colors(i,:)], ...
'DisplayName', sprintf('$d_u/F_u$, $\\omega_i = %.2f \\omega_0$', wis(i)))
plot(freqs, abs(squeeze(freqresp(Gs_iff_hpf{i}('Dv', 'Fu'), freqs, 'rad/s'))), '-', 'color', [colors(i,:), 0.5], ...
'DisplayName', sprintf('$d_v/F_u$, $\\omega_i = %.2f \\omega_0$', wis(i)))
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude [m/N]');
leg = legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1);
leg.ItemTokenSize(1) = 20;
ax3 = nexttile([3,1]);
hold on;
for i = 1:length(wis)
plot(freqs, abs(squeeze(freqresp(Gs_iff_hpf{i}('Dv', 'Fu')/Gs_iff_hpf{i}('Du', 'Fu'), freqs, 'rad/s'))), '-', 'color', [colors(i,:)], ...
'DisplayName', sprintf('$\\omega_i = %.2f \\omega_0$', wis(i)))
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [rad/s]'); ylabel('Coupling Ratio');
leg = legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1);
leg.ItemTokenSize(1) = 20;
xticks([1e-2,1e-1,1,1e1])
xticklabels({'$0.01 \omega_0$', '$0.1 \omega_0$', '$\omega_0$', '$10 \omega_0$'})
ax2 = nexttile;
hold on;
for i = 1:length(wis)
plot(freqs, 180/pi*angle(squeeze(freqresp(Gs_iff_hpf{i}('Du', 'Fu'), freqs, 'rad/s'))), '-')
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [rad/s]'); ylabel('Phase [deg]');
yticks(-180:90:180);
ylim([-180 180]);
xticks([1e-2,1e-1,1,1e1])
xticklabels({'$0.01 \omega_0$', '$0.1 \omega_0$', '$\omega_0$', '$10 \omega_0$'})
linkaxes([ax1,ax2],'x');
xlim([freqs(1), freqs(end)]);
% #+name: fig:rotating_iff_hpf_damped_plant_effect_wi_coupling
% #+caption: Effect of $\omega_i$ on the damped plant coupling
% #+RESULTS:
% [[file:figs/rotating_iff_hpf_damped_plant_effect_wi_coupling.png]]
%% Effect of $\omega_i$ on the obtained compliance
freqs = logspace(-2, 1, 1000);
figure;
tiledlayout(1, 1, 'TileSpacing', 'Compact', 'Padding', 'None');
% Magnitude
ax1 = nexttile();
hold on;
for i = 1:length(wis)
plot(freqs, abs(squeeze(freqresp(Gs_iff_hpf{i}('Du', 'Fdx'), freqs, 'rad/s'))), '-', 'color', [colors(i,:)], ...
'DisplayName', sprintf('$d_{x}/F_{dx}$, $\\omega_i = %.2f \\omega_0$', wis(i)))
end
plot(freqs, abs(squeeze(freqresp(Gs{2}('Du', 'Fdx'), freqs, 'rad/s'))), 'k--', ...
'DisplayName', '$d_{x}/F_{dx}$, OL')
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [rad/s]'); ylabel('Compliance [m/N]');
leg = legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1);
leg.ItemTokenSize(1) = 20;
xticks([1e-2,1e-1,1,1e1])
xticklabels({'$0.01 \omega_0$', '$0.1 \omega_0$', '$\omega_0$', '$10 \omega_0$'})

500
matlab/rotating_4_iff_kp.m Normal file
View File

@@ -0,0 +1,500 @@
%% 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
%% Colors for the figures
colors = colororder;
%% Simscape model name
mdl = 'rotating_model';
%% Load "Generic" system dynamics
load('rotating_generic_plants.mat', 'Gs', 'Wzs');
% Identify plant with parallel stiffnesses :noexport:
%% Tuv Stage
mn = 0.5; % Tuv mass [kg]
%% Sample
ms = 0.5; % Sample mass [kg]
%% General Configuration
model_config = struct();
model_config.controller = "open_loop"; % Default: Open-Loop
model_config.Tuv_type = "parallel_k"; % Default: 2DoF stage
%% Input/Output definition
clear io; io_i = 1;
io(io_i) = linio([mdl, '/controller'], 1, 'openinput'); io_i = io_i + 1; % [Fu, Fv]
io(io_i) = linio([mdl, '/fd'], 1, 'openinput'); io_i = io_i + 1; % [Fdu, Fdv]
io(io_i) = linio([mdl, '/translation_stage'], 1, 'openoutput'); io_i = io_i + 1; % [Fmu, Fmv]
io(io_i) = linio([mdl, '/translation_stage'], 2, 'openoutput'); io_i = io_i + 1; % [Du, Dv]
io(io_i) = linio([mdl, '/ext_metrology'], 1, 'openoutput'); io_i = io_i + 1; % [Dx, Dy]
% Effect of the parallel stiffness on the IFF plant
% The IFF plant (transfer function from $[F_u, F_v]$ to $[f_u, f_v]$) is identified in three different cases:
% - without parallel stiffness $k_p = 0$
% - with a small parallel stiffness $k_p < m \Omega^2$
% - with a large parallel stiffness $k_p > m \Omega^2$
Wz = 0.1; % The rotation speed [rad/s]
%% No parallel Stiffness
kp = 0; % Parallel Stiffness [N/m]
cp = 0.001*2*sqrt(kp*(mn+ms)); % Small parallel damping [N/(m/s)]
kn = 1 - kp; % Stiffness [N/m]
cn = 0.01*2*sqrt(kn*(mn+ms)); % Damping [N/(m/s)]
G_no_kp = linearize(mdl, io, 0);
G_no_kp.InputName = {'Fu', 'Fv', 'Fdx', 'Fdy'};
G_no_kp.OutputName = {'fu', 'fv', 'Du', 'Dv', 'Dx', 'Dy'};
%% Small parallel Stiffness
kp = 0.5*(mn+ms)*Wz^2; % Parallel Stiffness [N/m]
cp = 0.001*2*sqrt(kp*(mn+ms)); % Small parallel damping [N/(m/s)]
kn = 1 - kp; % Stiffness [N/m]
cn = 0.01*2*sqrt(kn*(mn+ms)); % Damping [N/(m/s)]
G_low_kp = linearize(mdl, io, 0);
G_low_kp.InputName = {'Fu', 'Fv', 'Fdx', 'Fdy'};
G_low_kp.OutputName = {'fu', 'fv', 'Du', 'Dv', 'Dx', 'Dy'};
%% Large parallel Stiffness
kp = 1.5*(mn+ms)*Wz^2; % Parallel Stiffness [N/m]
cp = 0.001*2*sqrt(kp*(mn+ms)); % Small parallel damping [N/(m/s)]
kn = 1 - kp; % Stiffness [N/m]
cn = 0.01*2*sqrt(kn*(mn+ms)); % Damping [N/(m/s)]
G_high_kp = linearize(mdl, io, 0);
G_high_kp.InputName = {'Fu', 'Fv', 'Fdx', 'Fdy'};
G_high_kp.OutputName = {'fu', 'fv', 'Du', 'Dv', 'Dx', 'Dy'};
% The Bode plots of the obtained dynamics are shown in Figure ref:fig:rotating_iff_effect_kp.
% One can see that for $k_p > m \Omega^2$, the two real zeros with $k_p < m \Omega^2$ are transformed into two complex conjugate zeros and the systems shows alternating complex conjugate poles and zeros.
%% Effect of the parallel stiffness on the IFF plant
freqs = logspace(-2, 1, 1000);
figure;
tiledlayout(3, 1, 'TileSpacing', 'Compact', 'Padding', 'None');
% Magnitude
ax1 = nexttile([2, 1]);
hold on;
plot(freqs, abs(squeeze(freqresp(G_no_kp( 'fu', 'Fu'), freqs, 'rad/s'))), '-', ...
'DisplayName', '$k_p = 0$')
plot(freqs, abs(squeeze(freqresp(G_low_kp( 'fu', 'Fu'), freqs, 'rad/s'))), '-', ...
'DisplayName', '$k_p < m\Omega^2$')
plot(freqs, abs(squeeze(freqresp(G_high_kp('fu', 'Fu'), freqs, 'rad/s'))), '-', ...
'DisplayName', '$k_p > m\Omega^2$')
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude [N/N]');
ylim([1e-5, 5e1]);
legend('location', 'southeast', 'FontSize', 8);
% Phase
ax2 = nexttile;
hold on;
plot(freqs, 180/pi*angle(squeeze(freqresp(G_no_kp( 'fu', 'Fu'), freqs, 'rad/s'))), '-')
plot(freqs, 180/pi*angle(squeeze(freqresp(G_low_kp( 'fu', 'Fu'), freqs, 'rad/s'))), '-')
plot(freqs, 180/pi*angle(squeeze(freqresp(G_high_kp('fu', 'Fu'), freqs, 'rad/s'))), '-')
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [rad/s]'); ylabel('Phase [deg]');
yticks(-180:90:180);
ylim([-180 180]);
hold off;
xticks([1e-2,1e-1,1,1e1])
xticklabels({'$0.01 \omega_0$', '$0.1 \omega_0$', '$\omega_0$', '$10 \omega_0$'})
linkaxes([ax1,ax2],'x');
xlim([freqs(1), freqs(end)]);
% #+name: fig:rotating_iff_effect_kp
% #+caption: Effect of the parallel stiffness on the IFF plant: Bode plot of $G_{k}(1,1) = f_u/F_u$ without parallel spring, with parallel spring stiffness $k_p < m \Omega^2$ and $k_p > m \Omega^2$, $\Omega = 0.1 \omega_0$
% #+RESULTS:
% [[file:figs/rotating_iff_effect_kp.png]]
% Figure ref:fig:rotating_iff_kp_root_locus shows the Root Locus plots for $k_p = 0$, $k_p < m \Omega^2$ and $k_p > m \Omega^2$ when $K_F$ is a pure integrator as in Eq. eqref:eq:Kf_pure_int.
% It is shown that if the added stiffness is higher than the maximum negative stiffness, the poles of the closed-loop system are bounded on the (stable) left half-plane, and hence the *unconditional stability of IFF is recovered*.
%% Root Locus for IFF without parallel spring, with small parallel spring and with large parallel spring
gains = logspace(-2, 2, 200);
figure;
tiledlayout(1, 3, 'TileSpacing', 'Compact', 'Padding', 'None');
ax1 = nexttile([1,2]);
hold on;
plot(real(pole(G_no_kp({'fu','fv'},{'Fu','Fv'})*(1/s))), imag(pole(G_no_kp({'fu','fv'},{'Fu','Fv'})*(1/s))), 'x', 'color', colors(1,:), ...
'DisplayName', '$k_p = 0$','MarkerSize',8);
plot(real(tzero(G_no_kp({'fu','fv'},{'Fu','Fv'})*(1/s))), imag(tzero(G_no_kp({'fu','fv'},{'Fu','Fv'})*(1/s))), 'o', 'color', colors(1,:), ...
'HandleVisibility', 'off','MarkerSize',8);
for g = gains
cl_poles = pole(feedback(G_no_kp({'fu','fv'},{'Fu','Fv'}), (g/s)*eye(2)));
plot(real(cl_poles), imag(cl_poles), '.', 'color', colors(1,:), ...
'HandleVisibility', 'off','MarkerSize',4);
end
plot(real(pole(G_low_kp({'fu','fv'},{'Fu','Fv'})*(1/s))), imag(pole(G_low_kp({'fu','fv'},{'Fu','Fv'})*(1/s))), 'x', 'color', colors(2,:), ...
'DisplayName', '$k_p < m\Omega^2$','MarkerSize',8);
plot(real(tzero(G_low_kp({'fu','fv'},{'Fu','Fv'})*(1/s))), imag(tzero(G_low_kp({'fu','fv'},{'Fu','Fv'})*(1/s))), 'o', 'color', colors(2,:), ...
'HandleVisibility', 'off','MarkerSize',8);
for g = gains
cl_poles = pole(feedback(G_low_kp({'fu','fv'},{'Fu','Fv'}), (g/s)*eye(2)));
plot(real(cl_poles), imag(cl_poles), '.', 'color', colors(2,:), ...
'HandleVisibility', 'off','MarkerSize',4);
end
plot(real(pole(G_high_kp({'fu','fv'},{'Fu','Fv'})*(1/s))), imag(pole(G_high_kp({'fu','fv'},{'Fu','Fv'})*(1/s))), 'x', 'color', colors(3,:), ...
'DisplayName', '$k_p > m\Omega^2$','MarkerSize',8);
plot(real(tzero(G_high_kp({'fu','fv'},{'Fu','Fv'})*(1/s))), imag(tzero(G_high_kp({'fu','fv'},{'Fu','Fv'})*(1/s))), 'o', 'color', colors(3,:), ...
'HandleVisibility', 'off','MarkerSize',8);
for g = gains
cl_poles = pole(feedback(G_high_kp({'fu','fv'},{'Fu','Fv'}), (g/s)*eye(2)));
plot(real(cl_poles), imag(cl_poles), '.', 'color', colors(3,:), ...
'HandleVisibility', 'off','MarkerSize',4);
end
hold off;
axis square;
xlim([-2.25, 0.25]); ylim([-1.25, 1.25]);
xticks([-2, -1, 0])
xticklabels({'$-2\omega_0$', '$-\omega_0$', '$0$'})
yticks([-1, 0, 1])
yticklabels({'$-\omega_0$', '$0$', '$\omega_0$'})
xlabel('Real Part'); ylabel('Imaginary Part');
leg = legend('location', 'northwest', 'FontSize', 8);
leg.ItemTokenSize(1) = 8;
ax2 = nexttile();
hold on;
plot(real(pole(G_no_kp( {'fu','fv'},{'Fu','Fv'})*(1/s))), imag(pole(G_no_kp( {'fu','fv'},{'Fu','Fv'})*(1/s))), 'x', 'color', colors(1,:), 'MarkerSize',8);
plot(real(tzero(G_no_kp({'fu','fv'},{'Fu','Fv'})*(1/s))), imag(tzero(G_no_kp({'fu','fv'},{'Fu','Fv'})*(1/s))), 'o', 'color', colors(1,:), 'MarkerSize',8);
for g = gains
cl_poles = pole(feedback(G_no_kp({'fu','fv'},{'Fu','Fv'}), (g/s)*eye(2)));
plot(real(cl_poles), imag(cl_poles), '.', 'color', colors(1,:), 'MarkerSize', 4);
end
plot(real(pole(G_low_kp( {'fu','fv'},{'Fu','Fv'})*(1/s))), imag(pole(G_low_kp( {'fu','fv'},{'Fu','Fv'})*(1/s))), 'x', 'color', colors(2,:), 'MarkerSize',8);
plot(real(tzero(G_low_kp({'fu','fv'},{'Fu','Fv'})*(1/s))), imag(tzero(G_low_kp({'fu','fv'},{'Fu','Fv'})*(1/s))), 'o', 'color', colors(2,:), 'MarkerSize',8);
for g = gains
cl_poles = pole(feedback(G_low_kp({'fu','fv'},{'Fu','Fv'}), (g/s)*eye(2)));
plot(real(cl_poles), imag(cl_poles), '.', 'color', colors(2,:), 'MarkerSize', 4);
end
plot(real(pole(G_high_kp( {'fu','fv'},{'Fu','Fv'})*(1/s))), imag(pole(G_high_kp( {'fu','fv'},{'Fu','Fv'})*(1/s))), 'x', 'color', colors(3,:), 'MarkerSize',8);
plot(real(tzero(G_high_kp({'fu','fv'},{'Fu','Fv'})*(1/s))), imag(tzero(G_high_kp({'fu','fv'},{'Fu','Fv'})*(1/s))), 'o', 'color', colors(3,:), 'MarkerSize',8);
for g = gains
cl_poles = pole(feedback(G_high_kp({'fu','fv'},{'Fu','Fv'}), (g/s)*eye(2)));
plot(real(cl_poles), imag(cl_poles), '.', 'color', colors(3,:), 'MarkerSize',4);
end
hold off;
axis equal;
xlim([-0.04, 0.04]); ylim([-0.08, 0.08]);
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
xlabel('Real Part'); ylabel('Imaginary Part');
title('Zoom on controller pole')
% Effect of $k_p$ on the attainable damping
% Even though the parallel stiffness $k_p$ has no impact on the open-loop poles (as the overall stiffness $k$ is kept constant), it has a large impact on the transmission zeros.
% Moreover, as the attainable damping is generally proportional to the distance between poles and zeros cite:preumont18_vibrat_contr_activ_struc_fourt_edition, the parallel stiffness $k_p$ is foreseen to have some impact on the attainable damping.
% To study this effect, Root Locus plots for several parallel stiffnesses $k_p > m \Omega^2$ are shown in Figure ref:fig:rotating_iff_kp_root_locus_effect_kp.
% The frequencies of the transmission zeros of the system are increasing with an increase of the parallel stiffness $k_p$ (thus getting closer to the poles) and the associated attainable damping is reduced.
% #+begin_important
% Therefore, even though the parallel stiffness $k_p$ should be larger than $m \Omega^2$ for stability reasons, it should not be taken too large as this would limit the attainable damping.
% #+end_important
%% Tested parallel stiffnesses
kps = [2, 20, 40]*(mn + ms)*Wz^2;
%% Root Locus: Effect of the parallel stiffness on the attainable damping
figure;
gains = logspace(-2, 4, 500);
hold on;
for kp_i = 1:length(kps)
kp = kps(kp_i); % Parallel Stiffness [N/m]
cp = 0.001*2*sqrt(kp*(mn+ms)); % Small parallel damping [N/(m/s)]
kn = 1 - kp; % Stiffness [N/m]
cn = 0.01*2*sqrt(kn*(mn+ms)); % Damping [N/(m/s)]
% Identify dynamics
G = linearize(mdl, io, 0);
G.InputName = {'Fu', 'Fv', 'Fdx', 'Fdy'};
G.OutputName = {'fu', 'fv', 'Du', 'Dv', 'Dx', 'Dy'};
plot(real(pole(G({'fu', 'fv'}, {'Fu', 'Fv'})*(1/s*eye(2)))), imag(pole(G({'fu', 'fv'}, {'Fu', 'Fv'})*(1/s*eye(2)))), 'x', 'color', colors(kp_i,:), ...
'DisplayName', sprintf('$k_p = %.1f m \\Omega^2$', kp/((mn+ms)*Wz^2)),'MarkerSize',8);
plot(real(tzero(G({'fu', 'fv'}, {'Fu', 'Fv'})*(1/s*eye(2)))), imag(tzero(G({'fu', 'fv'}, {'Fu', 'Fv'})*(1/s*eye(2)))), 'o', 'color', colors(kp_i,:), ...
'HandleVisibility', 'off','MarkerSize',8);
for g = gains
cl_poles = pole(feedback(G({'fu', 'fv'}, {'Fu', 'Fv'}), (g/s)*eye(2)));
plot(real(cl_poles), imag(cl_poles), '.', 'color', colors(kp_i,:),'MarkerSize',4, ...
'HandleVisibility', 'off');
end
end
hold off;
axis square;
% xlim([-1.15, 0.05]); ylim([0, 1.2]);
xlim([-2.25, 0.25]); ylim([-1.25, 1.25]);
xticks([-2, -1, 0])
xticklabels({'$-2\omega_0$', '$-\omega_0$', '$0$'})
yticks([-1, 0, 1])
yticklabels({'$-\omega_0$', '$0$', '$\omega_0$'})
xlabel('Real Part'); ylabel('Imaginary Part');
leg = legend('location', 'northwest', 'FontSize', 8);
leg.ItemTokenSize(1) = 12;
% #+name: fig:rotating_iff_kp_root_locus_effect_kp
% #+caption: Root Locus: Effect of the parallel stiffness on the attainable damping, $\Omega = 0.1 \omega_0$
% #+RESULTS:
% [[file:figs/rotating_iff_kp_root_locus_effect_kp.png]]
% This is confirmed by the Figure ref:fig:rotating_iff_kp_optimal_gain where the attainable closed-loop damping ratio $\xi_{\text{cl}}$ and the associated optimal control gain $g_\text{opt}$ are computed as a function of the parallel stiffness.
%% Computes the optimal parameters and attainable simultaneous damping
alphas = logspace(-2, 0, 100);
alphas(end) = []; % Remove last point
opt_xi = zeros(1, length(alphas)); % Optimal simultaneous damping
opt_gain = zeros(1, length(alphas)); % Corresponding optimal gain
Kiff = 1/s*eye(2);
for alpha_i = 1:length(alphas)
kp = alphas(alpha_i);
cp = 0.001*2*sqrt(kp*(mn+ms)); % Small parallel damping [N/(m/s)]
kn = 1 - kp; % Stiffness [N/m]
cn = 0.01*2*sqrt(kn*(mn+ms)); % Damping [N/(m/s)]
% Identify dynamics
G = linearize(mdl, io, 0);
G.InputName = {'Fu', 'Fv', 'Fdx', 'Fdy'};
G.OutputName = {'fu', 'fv', 'Du', 'Dv', 'Dx', 'Dy'};
fun = @(g)computeSimultaneousDamping(g, G({'fu', 'fv'}, {'Fu', 'Fv'}), Kiff);
[g_opt, xi_opt] = fminsearch(fun, 2);
opt_xi(alpha_i) = 1/xi_opt;
opt_gain(alpha_i) = g_opt;
end
%% Attainable damping as a function of the stiffness ratio
figure;
yyaxis left
plot(alphas, opt_xi, '-', 'DisplayName', '$\xi_{cl}$');
set(gca, 'YScale', 'lin');
ylim([0,1]);
ylabel('Damping Ratio $\xi$');
yyaxis right
hold on;
plot(alphas, opt_gain, '-', 'DisplayName', '$g_{opt}$');
set(gca, 'YScale', 'lin');
ylim([0,2.5]);
ylabel('Controller gain $g$');
set(gca, 'XScale', 'log');
legend('location', 'northeast', 'FontSize', 8);
xlabel('$k_p$');
xlim([0.01, 1]);
xticks([0.01, 0.1, 1])
xticklabels({'$m\Omega^2$', '$10m\Omega^2$', '$100m\Omega^2$'})
% Damped plant
% Let's choose a parallel stiffness equal to $k_p = 2 m \Omega^2$ and compute the damped plant.
% The damped and undamped transfer functions from $F_u$ to $d_u$ are compared in Figure ref:fig:rotating_iff_kp_damped_plant.
% Even though the two resonances are well damped, the IFF changes the low frequency behavior of the plant which is usually not wanted.
% This is due to the fact that "pure" integrators are used, and that the low frequency loop gains becomes large below some frequency.
%% Identify dynamics with parallel stiffness = 2mW^2
Wz = 0.1; % [rad/s]
kp = 2*(mn + ms)*Wz^2; % Parallel Stiffness [N/m]
cp = 0.001*2*sqrt(kp*(mn+ms)); % Small parallel damping [N/(m/s)]
kn = 1 - kp; % Stiffness [N/m]
cn = 0.01*2*sqrt(kn*(mn+ms)); % Damping [N/(m/s)]
% Identify dynamics
G = linearize(mdl, io, 0);
G.InputName = {'Fu', 'Fv', 'Fdx', 'Fdy'};
G.OutputName = {'fu', 'fv', 'Du', 'Dv', 'Dx', 'Dy'};
%% IFF controller with pure integrator
Kiff_kp = (2.2/s)*eye(2);
Kiff_kp.InputName = {'fu', 'fv'};
Kiff_kp.OutputName = {'Fu', 'Fv'};
%% Compute the damped plant
G_cl_iff_kp = feedback(G, Kiff_kp, 'name');
%% Damped plant with IFF - Transfer function from $F_u$ to $d_u$
freqs = logspace(-3, 1, 1000);
figure;
tiledlayout(3, 1, 'TileSpacing', 'Compact', 'Padding', 'None');
% Magnitude
ax1 = nexttile([2, 1]);
hold on;
plot(freqs, abs(squeeze(freqresp(G('Du', 'Fu'), freqs, 'rad/s'))), '-', 'color', zeros(1,3), ...
'DisplayName', '$d_u/F_u$ - OL')
plot(freqs, abs(squeeze(freqresp(G_cl_iff_kp('Du', 'Fu'), freqs, 'rad/s'))), '-', 'color', colors(1,:), ...
'DisplayName', '$d_u/F_u$ - IFF + $k_p$')
plot(freqs, abs(squeeze(freqresp(G('Dv', 'Fu'), freqs, 'rad/s'))), '-', 'color', [zeros(1,3), 0.5], ...
'DisplayName', '$d_v/F_u$ - OL')
plot(freqs, abs(squeeze(freqresp(G_cl_iff_kp('Dv', 'Fu'), freqs, 'rad/s'))), '-', 'color', [colors(1,:), 0.5], ...
'DisplayName', '$d_v/F_u$ - IFF + $k_p$')
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude [m/N]');
ldg = legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 2);
ldg.ItemTokenSize(1) = 20;
ylim([1e-6, 1e2]);
ax2 = nexttile;
hold on;
plot(freqs, 180/pi*angle(squeeze(freqresp(G('Du', 'Fu'), freqs, 'rad/s'))), '-', 'color', zeros(1,3))
plot(freqs, 180/pi*angle(squeeze(freqresp(G_cl_iff_kp('Du', 'Fu'), freqs, 'rad/s'))), '-', 'color', colors(1,:))
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [rad/s]'); ylabel('Phase [deg]');
yticks(-180:90:180);
ylim([-180 180]);
xticks([1e-3,1e-2,1e-1,1,1e1])
xticklabels({'$0.001 \omega_0$', '$0.01 \omega_0$', '$0.1 \omega_0$', '$\omega_0$', '$10 \omega_0$'})
linkaxes([ax1,ax2],'x');
xlim([freqs(1), freqs(end)]);
% #+name: fig:rotating_iff_kp_damped_plant
% #+caption: Damped plant with IFF - Transfer function from $F_u$ to $d_u$
% #+RESULTS:
% [[file:figs/rotating_iff_kp_damped_plant.png]]
% In order to lower the low frequency gain, an high pass filter is added to the IFF controller (which is equivalent as shifting the controller pole to the left in the complex plane):
% \begin{equation}
% K_{\text{IFF}}(s) = g\frac{1}{\omega_i + s} \begin{bmatrix}
% 1 & 0 \\
% 0 & 1
% \end{bmatrix}
% \end{equation}
% Let's see how the high pass filter impacts the attainable damping.
% The controller gain $g$ is kept constant while $\omega_i$ is changed, and the minimum damping ratio of the damped plant is computed.
% The obtained damping ratio as a function of $\omega_i/\omega_0$ (where $\omega_0$ is the resonance of the system without rotation) is shown in Figure ref:fig:rotating_iff_kp_added_hpf_effect_damping.
w0 = sqrt((kn+kp)/(mn+ms)); % Resonance frequency [rad/s]
wis = w0*logspace(-2, 0, 100); % LPF cut-off [rad/s]
%% Computes the obtained damping as a function of the HPF cut-off frequency
opt_xi = zeros(1, length(wis)); % Optimal simultaneous damping
for wi_i = 1:length(wis)
Kiff_kp_hpf = (2.2/(s + wis(wi_i)))*eye(2);
Kiff_kp_hpf.InputName = {'fu', 'fv'};
Kiff_kp_hpf.OutputName = {'Fu', 'Fv'};
[~, xi] = damp(feedback(G, Kiff_kp_hpf, 'name'));
opt_xi(wi_i) = min(xi);
end
%% Effect of the high pass filter cut-off frequency on the obtained damping
figure;
plot(wis, opt_xi, '-');
set(gca, 'XScale', 'log');
set(gca, 'YScale', 'lin');
ylim([0,1]);
ylabel('Damping Ratio $\xi$');
xlabel('$\omega_i/\omega_0$');
% #+name: fig:rotating_iff_kp_added_hpf_effect_damping
% #+caption: Effect of the high pass filter cut-off frequency on the obtained damping
% #+RESULTS:
% [[file:figs/rotating_iff_kp_added_hpf_effect_damping.png]]
% Let's choose $\omega_i = 0.1 \cdot \omega_0$ and compute the damped plant again.
% The Bode plots of the undamped, damped with "pure" IFF, and with added high pass filters are shown in Figure ref:fig:rotating_iff_kp_added_hpf_damped_plant.
% The added high pass filter gives almost the same damping properties while giving acceptable low frequency behavior.
%% Compute the damped plant with added High Pass Filter
Kiff_kp_hpf = (2.2/(s + 0.1*w0))*eye(2);
Kiff_kp_hpf.InputName = {'fu', 'fv'};
Kiff_kp_hpf.OutputName = {'Fu', 'Fv'};
G_cl_iff_hpf_kp = feedback(G, Kiff_kp_hpf, 'name');
%% Bode plot of the direct and coupling terms for several rotating velocities
freqs = logspace(-3, 1, 1000);
figure;
tiledlayout(3, 1, 'TileSpacing', 'Compact', 'Padding', 'None');
% Magnitude
ax1 = nexttile([2, 1]);
hold on;
plot(freqs, abs(squeeze(freqresp(G( 'Du', 'Fu'), freqs, 'rad/s'))), '-', 'color', zeros( 1,3), ...
'DisplayName', '$d_u/F_u$ - OL')
plot(freqs, abs(squeeze(freqresp(G_cl_iff_kp( 'Du', 'Fu'), freqs, 'rad/s'))), '-', 'color', colors(1,:), ...
'DisplayName', '$d_u/F_u$ - IFF + $k_p$')
plot(freqs, abs(squeeze(freqresp(G_cl_iff_hpf_kp('Du', 'Fu'), freqs, 'rad/s'))), '-', 'color', colors(2,:), ...
'DisplayName', '$d_u/F_u$ - IFF + $k_p$ + HPF')
plot(freqs, abs(squeeze(freqresp(G( 'Dv', 'Fu'), freqs, 'rad/s'))), '-', 'color', [zeros( 1,3), 0.5], ...
'DisplayName', '$d_v/F_u$ - OL')
plot(freqs, abs(squeeze(freqresp(G_cl_iff_kp( 'Dv', 'Fu'), freqs, 'rad/s'))), '-', 'color', [colors(1,:), 0.5], ...
'DisplayName', '$d_v/F_u$ - IFF + $k_p$')
plot(freqs, abs(squeeze(freqresp(G_cl_iff_hpf_kp('Dv', 'Fu'), freqs, 'rad/s'))), '-', 'color', [colors(2,:), 0.5], ...
'DisplayName', '$d_v/F_u$ - IFF + $k_p$ + HPF')
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude [m/N]');
ldg = legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 2);
ldg.ItemTokenSize(1) = 20;
ax2 = nexttile;
hold on;
plot(freqs, 180/pi*angle(squeeze(freqresp(G( 'Du', 'Fu'), freqs, 'rad/s'))), '-', 'color', zeros( 1,3))
plot(freqs, 180/pi*angle(squeeze(freqresp(G_cl_iff_kp( 'Du', 'Fu'), freqs, 'rad/s'))), '-', 'color', colors(1,:))
plot(freqs, 180/pi*angle(squeeze(freqresp(G_cl_iff_hpf_kp('Du', 'Fu'), freqs, 'rad/s'))), '-', 'color', colors(2,:))
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [rad/s]'); ylabel('Phase [deg]');
yticks(-180:90:180);
ylim([-180 180]);
xticks([1e-3,1e-2,1e-1,1,1e1])
xticklabels({'$0.001 \omega_0$', '$0.01 \omega_0$', '$0.1 \omega_0$', '$\omega_0$', '$10 \omega_0$'})
linkaxes([ax1,ax2],'x');
xlim([freqs(1), freqs(end)]);

185
matlab/rotating_5_rdc.m Normal file
View File

@@ -0,0 +1,185 @@
%% 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
%% Colors for the figures
colors = colororder;
%% Simscape model name
mdl = 'rotating_model';
%% Load "Generic" system dynamics
load('rotating_generic_plants.mat', 'Gs', 'Wzs');
% Decentralized Relative Damping Control
% The transfer functions from $[F_u,\ F_v]$ to $[d_u,\ d_v]$ is identified and shown in Figure ref:fig:rotating_rdc_plant_effect_rot for several rotating velocities.
%% Bode plot of the direct and coupling term for the "relative damping control" plant - Effect of rotation
figure;
freqs = logspace(-2, 1, 1000);
figure;
tiledlayout(3, 2, 'TileSpacing', 'Compact', 'Padding', 'None');
% Magnitude
ax1 = nexttile([2, 1]);
hold on;
for i = 1:length(Wzs)
plot(freqs, abs(squeeze(freqresp(Gs{i}('du', 'Fu'), freqs, 'rad/s'))), '-', 'color', colors(i,:))
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude [N/N]');
title('Direct terms: $d_u/F_u$, $d_v/F_v$');
ylim([1e-3, 1e2]);
ax2 = nexttile([2, 1]);
hold on;
for i = 1:length(Wzs)
plot(freqs, abs(squeeze(freqresp(Gs{i}('dv', 'Fu'), freqs, 'rad/s'))), '-', 'color', colors(i,:), ...
'DisplayName', sprintf('$\\Omega = %.1f \\omega_0$', Wzs(i)))
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
title('Coupling Terms: $d_u/F_v$, $d_v/F_u$');
ldg = legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
ldg.ItemTokenSize = [10, 1];
ylim([1e-3, 1e2]);
ax3 = nexttile;
hold on;
for i = 1:length(Wzs)
plot(freqs, 180/pi*angle(squeeze(freqresp(Gs{i}('du', 'Fu'), freqs, 'rad/s'))), '-', 'color', colors(i,:))
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [rad/s]'); ylabel('Phase [deg]');
yticks(-180:90:180);
ylim([-180 180]);
xticks([1e-2,1e-1,1,1e1])
xticklabels({'$0.01 \omega_0$', '$0.1 \omega_0$', '$\omega_0$', '$10 \omega_0$'})
ax4 = nexttile;
hold on;
for i = 1:length(Wzs)
plot(freqs, 180/pi*angle(squeeze(freqresp(Gs{i}('dv', 'Fu'), freqs, 'rad/s'))), '-', 'color', colors(i,:));
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [rad/s]'); set(gca, 'YTickLabel',[]);
yticks(-180:90:180);
ylim([-180 180]);
xticks([1e-2,1e-1,1,1e1])
xticklabels({'$0.01 \omega_0$', '$0.1 \omega_0$', '$\omega_0$', '$10 \omega_0$'})
linkaxes([ax1,ax2,ax3,ax4],'x');
xlim([freqs(1), freqs(end)]);
linkaxes([ax1,ax2],'y');
% #+name: fig:rotating_rdc_plant_effect_rot
% #+caption: Bode plot of the direct and coupling term for the "relative damping control" plant - Effect of rotation
% #+RESULTS:
% [[file:figs/rotating_rdc_plant_effect_rot.png]]
% In order to see if large damping can be added with Relative Damping Control, the root locus is computed (Figure ref:fig:rotating_rdc_root_locus).
% The closed-loop system is unconditionally stable and the poles can be damped as much as wanted.
%% Root Locus for Relative Damping Control
figure;
Krdc = s*eye(2); % Relative damping controller
gains = logspace(-2, 2, 300); % Tested gains
Wz_i = [1,3,4];
hold on;
for i = 1:length(Wz_i)
plot(real(pole(Gs{Wz_i(i)}({'du', 'dv'}, {'Fu', 'Fv'})*Krdc)), imag(pole(Gs{Wz_i(i)}({'du', 'dv'}, {'Fu', 'Fv'})*Krdc)), 'x', 'color', colors(i,:), ...
'DisplayName', sprintf('$\\Omega = %.1f \\omega_0 $', Wzs(Wz_i(i))),'MarkerSize',8);
plot(real(tzero(Gs{Wz_i(i)}({'du', 'dv'}, {'Fu', 'Fv'})*Krdc)), imag(tzero(Gs{Wz_i(i)}({'du', 'dv'}, {'Fu', 'Fv'})*Krdc)), 'o', 'color', colors(i,:), ...
'HandleVisibility', 'off','MarkerSize',8);
for g = gains
cl_poles = pole(feedback(Gs{Wz_i(i)}({'du', 'dv'}, {'Fu', 'Fv'}), g*Krdc, -1));
plot(real(cl_poles), imag(cl_poles), '.', 'color', colors(i,:), ...
'HandleVisibility', 'off','MarkerSize',4);
end
end
hold off;
axis square;
xlim([-1.8, 0.2]); ylim([0, 2]);
xticks([-1, 0])
xticklabels({'-$\omega_0$', '$0$'})
yticks([0, 1, 2])
yticklabels({'$0$', '$\omega_0$', '$2 \omega_0$'})
xlabel('Real Part'); ylabel('Imaginary Part');
leg = legend('location', 'northwest', 'FontSize', 8);
leg.ItemTokenSize(1) = 8;
% Damped Plant
% Let's select a reasonable "Relative Damping Control" gain, and compute the closed-loop damped system.
% The open-loop and damped plants are compared in Figure ref:fig:rotating_rdc_damped_plant.
% The rotating aspect does not add any complexity for the use of Relative Damping Control.
% It does not increase the low frequency coupling as compared to Integral Force Feedback.
i = 2;
%% Relative Damping Controller
Krdc = 2*s*eye(2);
Krdc.InputName = {'Du', 'Dv'};
Krdc.OutputName = {'Fu', 'Fv'};
%% Compute the damped plant
G_cl_rdc = feedback(Gs{i}, Krdc, 'name');
%% Damped plant using Relative Damping Control
figure;
freqs = logspace(-3, 2, 1000);
figure;
tiledlayout(3, 1, 'TileSpacing', 'Compact', 'Padding', 'None');
% Magnitude
ax1 = nexttile([2, 1]);
hold on;
plot(freqs, abs(squeeze(freqresp(Gs{i}( 'Du', 'Fu'), freqs, 'rad/s'))), '-', 'color', zeros(1,3), ...
'DisplayName', 'OL - $G_d(1,1)$')
plot(freqs, abs(squeeze(freqresp(G_cl_rdc('Du', 'Fu'), freqs, 'rad/s'))), '-', 'color', colors(1,:), ...
'DisplayName', 'RDC - $G_d(1,1)$')
plot(freqs, abs(squeeze(freqresp(Gs{i}( 'Dv', 'Fu'), freqs, 'rad/s'))), '-', 'color', [zeros(1,3), 0.5], ...
'DisplayName', 'OL - $G_d(2,1)$')
plot(freqs, abs(squeeze(freqresp(G_cl_rdc('Dv', 'Fu'), freqs, 'rad/s'))), '-', 'color', [colors(1,:), 0.5], ...
'DisplayName', 'RDC - $G_d(2,1)$')
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude [m/N]');
ldg = legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 2);
ldg.ItemTokenSize(1) = 20;
ylim([1e-6, 1e2]);
ax2 = nexttile;
hold on;
plot(freqs, 180/pi*angle(squeeze(freqresp(Gs{i}('Du', 'Fu'), freqs, 'rad/s'))), '-', 'color', zeros(1,3))
plot(freqs, 180/pi*angle(squeeze(freqresp(G_cl_rdc('Du', 'Fu'), freqs, 'rad/s'))), '-', 'color', colors(1,:))
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [rad/s]'); ylabel('Phase [deg]');
yticks(-180:90:180);
ylim([-180 180]);
linkaxes([ax1,ax2],'x');
xlim([freqs(1), freqs(end)]);

View File

@@ -0,0 +1,261 @@
%% 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
%% Colors for the figures
colors = colororder;
%% Simscape model name
mdl = 'rotating_model';
%% Load "Generic" system dynamics
load('rotating_generic_plants.mat', 'Gs', 'Wzs');
% Identify plants :noexport:
%% The rotating speed is set to $\Omega = 0.1 \omega_0$.
Wz = 0.1; % [rad/s]
%% Masses
ms = 0.5; % Sample mass [kg]
mn = 0.5; % Tuv mass [kg]
%% General Configuration
model_config = struct();
model_config.controller = "open_loop"; % Default: Open-Loop
%% Input/Output definition
clear io; io_i = 1;
io(io_i) = linio([mdl, '/controller'], 1, 'openinput'); io_i = io_i + 1; % [Fu, Fv]
io(io_i) = linio([mdl, '/fd'], 1, 'openinput'); io_i = io_i + 1; % [Fdu, Fdv]
io(io_i) = linio([mdl, '/xf'], 1, 'openinput'); io_i = io_i + 1; % [Dfx, Dfy]
io(io_i) = linio([mdl, '/translation_stage'], 1, 'openoutput'); io_i = io_i + 1; % [Fmu, Fmv]
io(io_i) = linio([mdl, '/translation_stage'], 2, 'openoutput'); io_i = io_i + 1; % [Du, Dv]
io(io_i) = linio([mdl, '/ext_metrology'], 1, 'openoutput'); io_i = io_i + 1; % [Dx, Dy]
%% Identifying plant with parallel stiffness
model_config.Tuv_type = "parallel_k";
% Parallel stiffness
kp = 2*(mn+ms)*Wz^2; % Parallel Stiffness [N/m]
cp = 0.001*2*sqrt(kp*(mn+ms)); % Small parallel damping [N/(m/s)]
% Tuv Stage
kn = 1 - kp; % Stiffness [N/m]
cn = 0.01*2*sqrt(kn*(mn+ms)); % Damping [N/(m/s)]
% Linearize
G_kp = linearize(mdl, io, 0);
G_kp.InputName = {'Fu', 'Fv', 'Fdx', 'Fdy', 'Dfx', 'Dfy'};
G_kp.OutputName = {'fu', 'fv', 'Du', 'Dv', 'Dx', 'Dy'};
%% Identifying plant with no parallel stiffness
model_config.Tuv_type = "normal";
% Tuv Stage
kn = 1; % Stiffness [N/m]
cn = 0.01*2*sqrt(kn*(mn+ms)); % Damping [N/(m/s)]
% Linearize
G = linearize(mdl, io, 0);
G.InputName = {'Fu', 'Fv', 'Fdx', 'Fdy', 'Dfx', 'Dfy'};
G.OutputName = {'fu', 'fv', 'Du', 'Dv', 'Dx', 'Dy'};
%% IFF Controller
Kiff = (2.2/(s + 0.1))*eye(2);
Kiff.InputName = {'fu', 'fv'};
Kiff.OutputName = {'Fu', 'Fv'};
%% IFF Controller with added stiffness
Kiff_kp = (2.2/(s + 0.1))*eye(2);
Kiff_kp.InputName = {'fu', 'fv'};
Kiff_kp.OutputName = {'Fu', 'Fv'};
%% Relative Damping Controller
Krdc = 2*s*eye(2);
Krdc.InputName = {'Du', 'Dv'};
Krdc.OutputName = {'Fu', 'Fv'};
% Root Locus
% Figure ref:fig:rotating_comp_techniques_root_locus shows the Root Locus plots for the two proposed IFF modifications as well as for relative damping control.
% While the two pairs of complex conjugate open-loop poles are identical for both IFF modifications, the transmission zeros are not.
% This means that the closed-loop behavior of both systems will differ when large control gains are used.
% One can observe that the closed loop poles corresponding to the system with added springs (in red) are bounded to the left half plane implying unconditional stability.
% This is not the case for the system where the controller is augmented with an HPF (in blue).
% It is interesting to note that the maximum added damping is very similar for both techniques.
%% Comparison of active damping techniques for rotating platform - Root Locus
figure;
gains = logspace(-2, 2, 500);
hold on;
% IFF
plot(real(pole(G({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff)), imag(pole(G({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff)), 'x', 'color', colors(1,:), ...
'DisplayName', 'IFF with HPF', 'MarkerSize', 8);
plot(real(tzero(G({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff)), imag(tzero(G({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff)), 'o', 'color', colors(1,:), ...
'HandleVisibility', 'off', 'MarkerSize', 8);
for g = gains
cl_poles = pole(feedback(G({'fu', 'fv'}, {'Fu', 'Fv'}), g*Kiff));
plot(real(cl_poles), imag(cl_poles), '.', 'color', colors(1,:),'MarkerSize',4, ...
'HandleVisibility', 'off');
end
% IFF with parallel stiffness
plot(real(pole(G_kp({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff_kp)), imag(pole(G_kp({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff_kp)), 'x', 'color', colors(2,:), ...
'DisplayName', 'IFF with $k_p$', 'MarkerSize', 8);
plot(real(tzero(G_kp({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff_kp)), imag(tzero(G_kp({'fu', 'fv'}, {'Fu', 'Fv'})*Kiff_kp)), 'o', 'color', colors(2,:), ...
'HandleVisibility', 'off', 'MarkerSize', 8);
for g = gains
cl_poles = pole(feedback(G_kp({'fu', 'fv'}, {'Fu', 'Fv'}), g*Kiff_kp));
plot(real(cl_poles), imag(cl_poles), '.', 'color', colors(2,:),'MarkerSize',4, ...
'HandleVisibility', 'off');
end
% RDC
plot(real(pole(G({'Du', 'Dv'}, {'Fu', 'Fv'})*Krdc)), imag(pole(G({'Du', 'Dv'}, {'Fu', 'Fv'})*Krdc)), 'x', 'color', colors(3,:), ...
'DisplayName', 'RDC', 'MarkerSize', 8);
plot(real(tzero(G({'Du', 'Dv'}, {'Fu', 'Fv'})*Krdc)), imag(tzero(G({'Du', 'Dv'}, {'Fu', 'Fv'})*Krdc)), 'o', 'color', colors(3,:), ...
'HandleVisibility', 'off', 'MarkerSize', 8);
for g = gains
cl_poles = pole(feedback(G({'Du', 'Dv'}, {'Fu', 'Fv'}), g*Krdc));
plot(real(cl_poles), imag(cl_poles), '.', 'color', colors(3,:),'MarkerSize',4, ...
'HandleVisibility', 'off');
end
hold off;
axis square;
xlim([-1.15, 0.05]); ylim([0, 1.2]);
xlabel('Real Part'); ylabel('Imaginary Part');
leg = legend('location', 'northwest', 'FontSize', 8);
leg.ItemTokenSize(1) = 12;
% Obtained Damped Plant
% The actively damped plants are computed for the three techniques and compared in Figure ref:fig:rotating_comp_techniques_dampled_plants.
% #+begin_important
% It is shown that while the diagonal (direct) terms of the damped plants are similar for the three active damping techniques, of off-diagonal (coupling) terms are not.
% Integral Force Feedback strategy is adding some coupling at low frequency which may negatively impact the positioning performances.
% #+end_important
%% Compute Damped plants
G_cl_iff = feedback(G, Kiff, 'name');
G_cl_iff_kp = feedback(G_kp, Kiff_kp, 'name');
G_cl_rdc = feedback(G, Krdc, 'name');
%% Comparison of the damped plants obtained with the three active damping techniques
figure;
freqs = logspace(-3, 2, 1000);
figure;
tiledlayout(3, 1, 'TileSpacing', 'Compact', 'Padding', 'None');
% Magnitude
ax1 = nexttile([2, 1]);
hold on;
plot(freqs, abs(squeeze(freqresp(G( 'Du', 'Fu'), freqs, 'rad/s'))), '-', 'color', zeros(1,3), ...
'DisplayName', 'OL')
plot(freqs, abs(squeeze(freqresp(G_cl_iff( 'Du', 'Fu'), freqs, 'rad/s'))), '-', 'color', colors(1,:), ...
'DisplayName', 'IFF + HPF')
plot(freqs, abs(squeeze(freqresp(G_cl_iff_kp('Du', 'Fu'), freqs, 'rad/s'))), '-', 'color', colors(2,:), ...
'DisplayName', 'IFF + $k_p$')
plot(freqs, abs(squeeze(freqresp(G_cl_rdc( 'Du', 'Fu'), freqs, 'rad/s'))), '-', 'color', colors(3,:), ...
'DisplayName', 'RDC')
plot(freqs, abs(squeeze(freqresp(G( 'Dv', 'Fu'), freqs, 'rad/s'))), '-', 'color', [zeros(1,3), 0.5], ...
'DisplayName', 'Coupling')
plot(freqs, abs(squeeze(freqresp(G_cl_iff( 'Dv', 'Fu'), freqs, 'rad/s'))), '-', 'color', [colors(1,:), 0.5], ...
'DisplayName', 'Coupling')
plot(freqs, abs(squeeze(freqresp(G_cl_iff_kp('Dv', 'Fu'), freqs, 'rad/s'))), '-', 'color', [colors(2,:), 0.5], ...
'DisplayName', 'Coupling')
plot(freqs, abs(squeeze(freqresp(G_cl_rdc( 'Dv', 'Fu'), freqs, 'rad/s'))), '-', 'color', [colors(3,:), 0.5], ...
'DisplayName', 'Coupling')
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); ylabel('Magnitude [m/N]');
ldg = legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 2);
ldg.ItemTokenSize = [10, 1];
ylim([1e-6, 1e2])
ax2 = nexttile;
hold on;
plot(freqs, 180/pi*angle(squeeze(freqresp(G( 'Du', 'Fu'), freqs, 'rad/s'))), '-', 'color', zeros(1,3))
plot(freqs, 180/pi*angle(squeeze(freqresp(G_cl_iff( 'Du', 'Fu'), freqs, 'rad/s'))), '-', 'color', colors(1,:))
plot(freqs, 180/pi*angle(squeeze(freqresp(G_cl_iff_kp('Du', 'Fu'), freqs, 'rad/s'))), '-', 'color', colors(2,:))
plot(freqs, 180/pi*angle(squeeze(freqresp(G_cl_rdc( 'Du', 'Fu'), freqs, 'rad/s'))), '-', 'color', colors(3,:))
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [rad/s]'); ylabel('Phase [deg]');
yticks(-180:90:180);
ylim([-180 180]);
linkaxes([ax1,ax2],'x');
xlim([freqs(1), freqs(end)]);
% Transmissibility And Compliance
% The proposed active damping techniques are now compared in terms of closed-loop transmissibility and compliance.
% The transmissibility is here defined as the transfer function from a displacement of the rotating stage along $\vec{i}_x$ to the displacement of the payload along the same direction.
% It is used to characterize how much vibration is transmitted through the suspended platform to the payload.
% The compliance describes the displacement response of the payload to external forces applied to it.
% This is a useful metric when disturbances are directly applied to the payload.
% It is here defined as the transfer function from external forces applied on the payload along $\vec{i}_x$ to the displacement of the payload along the same direction.
% Very similar results are obtained for the two proposed IFF modifications in terms of transmissibility and compliance (Figure ref:fig:rotating_comp_techniques_transmissibility_compliance).
% #+begin_important
% Using IFF degrades the compliance at low frequency while using relative damping control degrades the transmissibility at high frequency.
% This is very well known characteristics of these common active damping techniques that holds when applied to rotating platforms.
% #+end_important
%% Comparison of the obtained transmissibilty and compliance for the three tested active damping techniques
freqs = logspace(-2, 2, 1000);
figure;
tiledlayout(1, 2, 'TileSpacing', 'Compact', 'Padding', 'None');
% Transmissibility
ax1 = nexttile();
hold on;
plot(freqs, abs(squeeze(freqresp(G( 'Dx', 'Dfx'), freqs, 'rad/s'))), '-', 'color', zeros(1,3), ...
'DisplayName', 'OL')
plot(freqs, abs(squeeze(freqresp(G_cl_iff( 'Dx', 'Dfx'), freqs, 'rad/s'))), '-', 'color', colors(1,:), ...
'DisplayName', 'IFF + HPF')
plot(freqs, abs(squeeze(freqresp(G_cl_iff_kp('Dx', 'Dfx'), freqs, 'rad/s'))), '-', 'color', colors(2,:), ...
'DisplayName', 'IFF + $k_p$')
plot(freqs, abs(squeeze(freqresp(G_cl_rdc( 'Dx', 'Dfx'), freqs, 'rad/s'))), '-', 'color', colors(3,:), ...
'DisplayName', 'RDC')
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [rad/s]'); ylabel('Transmissibility [m/m]');
xlim([freqs(1), freqs(end)]);
% Compliance
ax1 = nexttile();
hold on;
plot(freqs, abs(squeeze(freqresp(G( 'Dx', 'Fdx'), freqs, 'rad/s'))), '-', 'color', zeros(1,3), ...
'DisplayName', 'OL')
plot(freqs, abs(squeeze(freqresp(G_cl_iff( 'Dx', 'Fdx'), freqs, 'rad/s'))), '-', 'color', colors(1,:), ...
'DisplayName', 'IFF + HPF')
plot(freqs, abs(squeeze(freqresp(G_cl_iff_kp('Dx', 'Fdx'), freqs, 'rad/s'))), '-', 'color', colors(2,:), ...
'DisplayName', 'IFF + $k_p$')
plot(freqs, abs(squeeze(freqresp(G_cl_rdc( 'Dx', 'Fdx'), freqs, 'rad/s'))), '-', 'color', colors(3,:), ...
'DisplayName', 'RDC')
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [rad/s]'); ylabel('Compliance [m/N]');
ldg = legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 2);
ldg.ItemTokenSize = [10, 1];
xlim([freqs(1), freqs(end)]);

File diff suppressed because it is too large Load Diff

602
matlab/rotating_8_nass.m Normal file
View File

@@ -0,0 +1,602 @@
%% 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
%% Colors for the figures
colors = colororder;
%% Nano-Hexapod on top of Micro-Station model
mdl = 'nass_rotating_model';
%% Load micro-station parameters
load('uniaxial_micro_station_parameters.mat')
%% Load controllers
load('nass_controllers.mat');
% System dynamics
%% Nano-Hexapod
mn = 15; % Nano-Hexapod mass [kg]
%% Light Sample
ms = 1; % Sample Mass [kg]
%% General Configuration
model_config = struct();
model_config.controller = "open_loop"; % Default: Open-Loop
model_config.Tuv_type = "normal"; % Default: 2DoF stage
%% Input/Output definition
clear io; io_i = 1;
io(io_i) = linio([mdl, '/controller'], 1, 'openinput'); io_i = io_i + 1; % Actuator Forces [Fu, Fv]
io(io_i) = linio([mdl, '/fd'], 1, 'openinput'); io_i = io_i + 1; % Direct Forces on Sample [Fdx, Fdy]
io(io_i) = linio([mdl, '/xf'], 1, 'openinput'); io_i = io_i + 1; % Floor Motion [Dfx, Dfy]
io(io_i) = linio([mdl, '/ft'], 1, 'openinput'); io_i = io_i + 1; % Micro-Station Disturbances [Ftx, Fty]
io(io_i) = linio([mdl, '/nano_hexapod'], 1, 'openoutput'); io_i = io_i + 1; % [Fmu, Fmv]
io(io_i) = linio([mdl, '/nano_hexapod'], 2, 'openoutput'); io_i = io_i + 1; % [Du, Dv]
io(io_i) = linio([mdl, '/ext_metrology'],1, 'openoutput'); io_i = io_i + 1; % [Dx, Dy]
% The dynamics of the undamped and damped plants are identified.
% The active damping parameters used are the optimal ones previously identified (i.e. for the rotating nano-hexapod fixed on a rigid platform).
%% Voice Coil (i.e. soft) Nano-Hexapod
kn = 1e4; % Nano-Hexapod Stiffness [N/m]
cn = 2*0.005*sqrt((ms + mn)*kn); % Nano-Hexapod Damping [N/(m/s)]
Wz = 0; % Rotating Velocity [rad/s]
G_vc_norot = linearize(mdl, io, 0.0);
G_vc_norot.InputName = {'Fu', 'Fv', 'Fdx', 'Fdy', 'Dfx', 'Dfy', 'Ftx', 'Fty'};
G_vc_norot.OutputName = {'fu', 'fv', 'Du', 'Dv', 'Dx', 'Dy'};
Wz = 2*pi; % Rotating Velocity [rad/s]
G_vc_fast = linearize(mdl, io, 0.0);
G_vc_fast.InputName = {'Fu', 'Fv', 'Fdx', 'Fdy', 'Dfx', 'Dfy', 'Ftx', 'Fty'};
G_vc_fast.OutputName = {'fu', 'fv', 'Du', 'Dv', 'Dx', 'Dy'};
%% APA (i.e. relatively stiff) Nano-Hexapod
kn = 1e6; % Nano-Hexapod Stiffness [N/m]
cn = 2*0.005*sqrt((ms + mn)*kn); % Nano-Hexapod Damping [N/(m/s)]
Wz = 0; % Rotating Velocity [rad/s]
G_md_norot = linearize(mdl, io, 0.0);
G_md_norot.InputName = {'Fu', 'Fv', 'Fdx', 'Fdy', 'Dfx', 'Dfy', 'Ftx', 'Fty'};
G_md_norot.OutputName = {'fu', 'fv', 'Du', 'Dv', 'Dx', 'Dy'};
Wz = 2*pi; % Rotating Velocity [rad/s]
G_md_fast = linearize(mdl, io, 0.0);
G_md_fast.InputName = {'Fu', 'Fv', 'Fdx', 'Fdy', 'Dfx', 'Dfy', 'Ftx', 'Fty'};
G_md_fast.OutputName = {'fu', 'fv', 'Du', 'Dv', 'Dx', 'Dy'};
%% Piezoelectric (i.e. stiff) Nano-Hexapod
kn = 1e8; % Nano-Hexapod Stiffness [N/m]
cn = 2*0.005*sqrt((ms + mn)*kn); % Nano-Hexapod Damping [N/(m/s)]
Wz = 0; % Rotating Velocity [rad/s]
G_pz_norot = linearize(mdl, io, 0.0);
G_pz_norot.InputName = {'Fu', 'Fv', 'Fdx', 'Fdy', 'Dfx', 'Dfy', 'Ftx', 'Fty'};
G_pz_norot.OutputName = {'fu', 'fv', 'Du', 'Dv', 'Dx', 'Dy'};
Wz = 2*pi; % Rotating Velocity [rad/s]
G_pz_fast = linearize(mdl, io, 0.0);
G_pz_fast.InputName = {'Fu', 'Fv', 'Fdx', 'Fdy', 'Dfx', 'Dfy', 'Ftx', 'Fty'};
G_pz_fast.OutputName = {'fu', 'fv', 'Du', 'Dv', 'Dx', 'Dy'};
%% Identify plants with Parallel stiffnesses
model_config.Tuv_type = "parallel_k"; % Default: 2DoF stage
% Voice Coil
kp = 1e3;
cp = 2*0.001*sqrt((ms + mn)*kp);
kn = 1e4-kp; % Nano-Hexapod Stiffness [N/m]
cn = 2*0.01*sqrt((ms + mn)*kn); % Nano-Hexapod Damping [N/(m/s)]
% Identify dynamics
Wz = 2*pi; % [rad/s]
G_vc_kp_fast = linearize(mdl, io, 0);
G_vc_kp_fast.InputName = {'Fu', 'Fv', 'Fdx', 'Fdy', 'Dfx', 'Dfy', 'Ftx', 'Fty'};
G_vc_kp_fast.OutputName = {'fu', 'fv', 'Du', 'Dv', 'Dx', 'Dy'};
Wz = 0; % [rad/s]
G_vc_kp_norot = linearize(mdl, io, 0);
G_vc_kp_norot.InputName = {'Fu', 'Fv', 'Fdx', 'Fdy', 'Dfx', 'Dfy', 'Ftx', 'Fty'};
G_vc_kp_norot.OutputName = {'fu', 'fv', 'Du', 'Dv', 'Dx', 'Dy'};
% APA
kp = 1e4;
cp = 2*0.001*sqrt((ms + mn)*kp);
kn = 1e6 - kp; % Nano-Hexapod Stiffness [N/m]
cn = 2*0.01*sqrt((ms + mn)*kn); % Nano-Hexapod Damping [N/(m/s)]
% Identify dynamics
Wz = 2*pi; % [rad/s]
G_md_kp_fast = linearize(mdl, io, 0);
G_md_kp_fast.InputName = {'Fu', 'Fv', 'Fdx', 'Fdy', 'Dfx', 'Dfy', 'Ftx', 'Fty'};
G_md_kp_fast.OutputName = {'fu', 'fv', 'Du', 'Dv', 'Dx', 'Dy'};
Wz = 0; % [rad/s]
G_md_kp_norot = linearize(mdl, io, 0);
G_md_kp_norot.InputName = {'Fu', 'Fv', 'Fdx', 'Fdy', 'Dfx', 'Dfy', 'Ftx', 'Fty'};
G_md_kp_norot.OutputName = {'fu', 'fv', 'Du', 'Dv', 'Dx', 'Dy'};
% Piezo
kp = 1e5;
cp = 2*0.001*sqrt((ms + mn)*kp);
kn = 1e8 - kp; % Nano-Hexapod Stiffness [N/m]
cn = 2*0.01*sqrt((ms + mn)*kn); % Nano-Hexapod Damping [N/(m/s)]
% Identify dynamics
Wz = 2*pi; % [rad/s]
G_pz_kp_fast = linearize(mdl, io, 0);
G_pz_kp_fast.InputName = {'Fu', 'Fv', 'Fdx', 'Fdy', 'Dfx', 'Dfy', 'Ftx', 'Fty'};
G_pz_kp_fast.OutputName = {'fu', 'fv', 'Du', 'Dv', 'Dx', 'Dy'};
Wz = 0; % [rad/s]
G_pz_kp_norot = linearize(mdl, io, 0);
G_pz_kp_norot.InputName = {'Fu', 'Fv', 'Fdx', 'Fdy', 'Dfx', 'Dfy', 'Ftx', 'Fty'};
G_pz_kp_norot.OutputName = {'fu', 'fv', 'Du', 'Dv', 'Dx', 'Dy'};
%% Closed Loop Plants - IFF with HPF
G_vc_norot_iff_hpf = feedback(G_vc_norot, Kiff_hpf_vc, 'name');
G_vc_fast_iff_hpf = feedback(G_vc_fast, Kiff_hpf_vc, 'name');
G_md_norot_iff_hpf = feedback(G_md_norot, Kiff_hpf_md, 'name');
G_md_fast_iff_hpf = feedback(G_md_fast, Kiff_hpf_md, 'name');
G_pz_norot_iff_hpf = feedback(G_pz_norot, Kiff_hpf_pz, 'name');
G_pz_fast_iff_hpf = feedback(G_pz_fast, Kiff_hpf_pz, 'name');
%% Closed Loop Plants - IFF with Parallel Stiffness
G_vc_norot_iff_kp = feedback(G_vc_kp_norot, Kiff_kp_vc, 'name');
G_vc_fast_iff_kp = feedback(G_vc_kp_fast, Kiff_kp_vc, 'name');
G_md_norot_iff_kp = feedback(G_md_kp_norot, Kiff_kp_md, 'name');
G_md_fast_iff_kp = feedback(G_md_kp_fast, Kiff_kp_md, 'name');
G_pz_norot_iff_kp = feedback(G_pz_kp_norot, Kiff_kp_pz, 'name');
G_pz_fast_iff_kp = feedback(G_pz_kp_fast, Kiff_kp_pz, 'name');
%% Closed Loop Plants - RDC
G_vc_norot_rdc = feedback(G_vc_norot, Krdc_vc, 'name');
G_vc_fast_rdc = feedback(G_vc_fast, Krdc_vc, 'name');
G_md_norot_rdc = feedback(G_md_norot, Krdc_md, 'name');
G_md_fast_rdc = feedback(G_md_fast, Krdc_md, 'name');
G_pz_norot_rdc = feedback(G_pz_norot, Krdc_pz, 'name');
G_pz_fast_rdc = feedback(G_pz_fast, Krdc_pz, 'name');
% The undamped and damped plants are shown in Figure ref:fig:rotating_nass_plant_comp_stiffness.
% Three nano-hexapod velocities are shown (from left to right): $k_n = \SI{0.01}{\N\per\mu\m}$, $k_n = \SI{1}{\N\per\mu\m}$ and $k_n = \SI{100}{\N\per\mu\m}$.
% The direct terms are shown by the solid curves while the coupling terms are shown by the shaded ones.
% #+begin_important
% It can be observed on Figure ref:fig:rotating_nass_plant_comp_stiffness that:
% - Coupling (ratio between the off-diagonal and direct terms) is larger for the soft nano-hexapod
% - Damping added by the three proposed techniques is quite high and the obtained plant is rather easy to control
% - There is some coupling between nano-hexapod and micro-station dynamics for the stiff nano-hexapod (mode at 200Hz)
% - The two proposed IFF modification yields similar results
% #+end_important
%% Bode plot of the transfer function from nano-hexapod actuator to measured motion by the external metrology
freqs_vc = logspace(-1, 2, 1000);
freqs_md = logspace(0, 3, 1000);
freqs_pz = logspace(0, 3, 1000);
figure;
tiledlayout(3, 3, 'TileSpacing', 'Compact', 'Padding', 'None');
ax1 = nexttile([2,1]);
hold on;
plot(freqs_vc, abs(squeeze(freqresp(G_vc_fast('Dx', 'Fu'), freqs_vc, 'Hz'))), 'color', zeros(1,3), ...
'DisplayName', 'OL');
plot(freqs_vc, abs(squeeze(freqresp(G_vc_fast('Dy', 'Fu'), freqs_vc, 'Hz'))), 'color', [zeros(1,3), 0.5], ...
'DisplayName', 'Coupling');
plot(freqs_vc, abs(squeeze(freqresp(G_vc_fast_iff_hpf('Dx', 'Fu'), freqs_vc, 'Hz'))), 'color', colors(1,:), ...
'DisplayName', 'IFF + $k_p$');
plot(freqs_vc, abs(squeeze(freqresp(G_vc_fast_iff_hpf('Dy', 'Fu'), freqs_vc, 'Hz'))), 'color', [colors(1,:), 0.5], ...
'HandleVisibility', 'off');
plot(freqs_vc, abs(squeeze(freqresp(G_vc_fast_iff_kp('Dx', 'Fu'), freqs_vc, 'Hz'))), 'color', colors(2,:), ...
'DisplayName', 'IFF + HPF');
plot(freqs_vc, abs(squeeze(freqresp(G_vc_fast_iff_kp('Dy', 'Fu'), freqs_vc, 'Hz'))), 'color', [colors(2,:), 0.5], ...
'HandleVisibility', 'off');
plot(freqs_vc, abs(squeeze(freqresp(G_vc_fast_rdc('Dx', 'Fu'), freqs_vc, 'Hz'))), 'color', colors(3,:), ...
'DisplayName', 'RDC');
plot(freqs_vc, abs(squeeze(freqresp(G_vc_fast_rdc('Dy', 'Fu'), freqs_vc, 'Hz'))), 'color', [colors(3,:), 0.5], ...
'HandleVisibility', 'off');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Magnitude [m/N]'); set(gca, 'XTickLabel',[]);
ylim([1e-12, 1e-2])
title('$k_n = 0.01\,N/\mu m$');
ax2 = nexttile([2,1]);
hold on;
plot(freqs_md, abs(squeeze(freqresp(G_md_fast('Dx', 'Fu'), freqs_md, 'Hz'))), 'color', zeros(1,3), ...
'DisplayName', 'OL');
plot(freqs_md, abs(squeeze(freqresp(G_md_fast('Dy', 'Fu'), freqs_md, 'Hz'))), 'color', [zeros(1,3), 0.5], ...
'DisplayName', 'Coupling');
plot(freqs_md, abs(squeeze(freqresp(G_md_fast_iff_hpf('Dx', 'Fu'), freqs_md, 'Hz'))), 'color', colors(1,:), ...
'DisplayName', 'IFF + $k_p$');
plot(freqs_md, abs(squeeze(freqresp(G_md_fast_iff_hpf('Dy', 'Fu'), freqs_md, 'Hz'))), 'color', [colors(1,:), 0.5], ...
'HandleVisibility', 'off');
plot(freqs_md, abs(squeeze(freqresp(G_md_fast_iff_kp('Dx', 'Fu'), freqs_md, 'Hz'))), 'color', colors(2,:), ...
'DisplayName', 'IFF + HPF');
plot(freqs_md, abs(squeeze(freqresp(G_md_fast_iff_kp('Dy', 'Fu'), freqs_md, 'Hz'))), 'color', [colors(2,:), 0.5], ...
'HandleVisibility', 'off');
plot(freqs_md, abs(squeeze(freqresp(G_md_fast_rdc('Dx', 'Fu'), freqs_md, 'Hz'))), 'color', colors(3,:), ...
'DisplayName', 'RDC');
plot(freqs_md, abs(squeeze(freqresp(G_md_fast_rdc('Dy', 'Fu'), freqs_md, 'Hz'))), 'color', [colors(3,:), 0.5], ...
'HandleVisibility', 'off');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
ylim([1e-12, 1e-2])
title('$k_n = 1\,N/\mu m$');
ax3 = nexttile([2,1]);
hold on;
plot(freqs_pz, abs(squeeze(freqresp(G_pz_fast('Dx', 'Fu'), freqs_pz, 'Hz'))), 'color', zeros(1,3), ...
'DisplayName', 'OL');
plot(freqs_pz, abs(squeeze(freqresp(G_pz_fast('Dy', 'Fu'), freqs_pz, 'Hz'))), 'color', [zeros(1,3), 0.5], ...
'DisplayName', 'Coupling');
plot(freqs_pz, abs(squeeze(freqresp(G_pz_fast_iff_hpf('Dx', 'Fu'), freqs_pz, 'Hz'))), 'color', colors(1,:), ...
'DisplayName', 'IFF + $k_p$');
plot(freqs_pz, abs(squeeze(freqresp(G_pz_fast_iff_hpf('Dy', 'Fu'), freqs_pz, 'Hz'))), 'color', [colors(1,:), 0.5], ...
'HandleVisibility', 'off');
plot(freqs_pz, abs(squeeze(freqresp(G_pz_fast_iff_kp('Dx', 'Fu'), freqs_pz, 'Hz'))), 'color', colors(2,:), ...
'DisplayName', 'IFF + HPF');
plot(freqs_pz, abs(squeeze(freqresp(G_pz_fast_iff_kp('Dy', 'Fu'), freqs_pz, 'Hz'))), 'color', [colors(2,:), 0.5], ...
'HandleVisibility', 'off');
plot(freqs_pz, abs(squeeze(freqresp(G_pz_fast_rdc('Dx', 'Fu'), freqs_pz, 'Hz'))), 'color', colors(3,:), ...
'DisplayName', 'RDC');
plot(freqs_pz, abs(squeeze(freqresp(G_pz_fast_rdc('Dy', 'Fu'), freqs_pz, 'Hz'))), 'color', [colors(3,:), 0.5], ...
'HandleVisibility', 'off');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]);
ylim([1e-12, 1e-2])
ldg = legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1);
ldg.ItemTokenSize = [20, 1];
title('$k_n = 100\,N/\mu m$');
ax1b = nexttile;
hold on;
plot(freqs_vc, 180/pi*unwrap(angle(squeeze(freqresp(G_vc_fast('Dx', 'Fu'), freqs_vc, 'Hz')))), 'color', zeros(1,3));
plot(freqs_vc, 180/pi*unwrap(angle(squeeze(freqresp(G_vc_fast_iff_hpf('Dx', 'Fu'), freqs_vc, 'Hz')))), 'color', colors(1,:));
plot(freqs_vc, 180/pi*unwrap(angle(squeeze(freqresp(G_vc_fast_iff_kp('Dx', 'Fu'), freqs_vc, 'Hz')))), 'color', colors(2,:));
plot(freqs_vc, 180/pi*unwrap(angle(squeeze(freqresp(G_vc_fast_rdc('Dx', 'Fu'), freqs_vc, 'Hz')))), 'color', colors(3,:));
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
yticks(-360:90:360);
ylim([-270, 90]);
ax2b = nexttile;
hold on;
plot(freqs_md, 180/pi*unwrap(angle(squeeze(freqresp(G_md_fast('Dx', 'Fu'), freqs_md, 'Hz')))), 'color', zeros(1,3));
plot(freqs_md, 180/pi*unwrap(angle(squeeze(freqresp(G_md_fast_iff_hpf('Dx', 'Fu'), freqs_md, 'Hz')))), 'color', colors(1,:));
plot(freqs_md, 180/pi*unwrap(angle(squeeze(freqresp(G_md_fast_iff_kp('Dx', 'Fu'), freqs_md, 'Hz')))), 'color', colors(2,:));
plot(freqs_md, 180/pi*unwrap(angle(squeeze(freqresp(G_md_fast_rdc('Dx', 'Fu'), freqs_md, 'Hz')))), 'color', colors(3,:));
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
hold off;
yticks(-360:90:360);
ylim([-270, 90]);
ax3b = nexttile;
hold on;
plot(freqs_pz, 180/pi*unwrap(angle(squeeze(freqresp(G_pz_fast('Dx', 'Fu'), freqs_pz, 'Hz')))), 'color', zeros(1,3));
plot(freqs_pz, 180/pi*unwrap(angle(squeeze(freqresp(G_pz_fast_iff_hpf('Dx', 'Fu'), freqs_pz, 'Hz')))), 'color', colors(1,:));
plot(freqs_pz, 180/pi*unwrap(angle(squeeze(freqresp(G_pz_fast_iff_kp('Dx', 'Fu'), freqs_pz, 'Hz')))), 'color', colors(2,:));
plot(freqs_pz, 180/pi*unwrap(angle(squeeze(freqresp(G_pz_fast_rdc('Dx', 'Fu'), freqs_pz, 'Hz')))), 'color', colors(3,:));
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
hold off;
yticks(-360:90:360);
ylim([-270, 90]);
linkaxes([ax1,ax1b],'x');
xlim([freqs_vc(1), freqs_vc(end)]);
linkaxes([ax2,ax2b],'x');
xlim([freqs_md(1), freqs_md(end)]);
linkaxes([ax3,ax3b],'x');
xlim([freqs_pz(1), freqs_pz(end)]);
% #+name: fig:rotating_nass_plant_comp_stiffness
% #+caption: Bode plot of the transfer function from nano-hexapod actuator to measured motion by the external metrology
% #+RESULTS:
% [[file:figs/rotating_nass_plant_comp_stiffness.png]]
% To confirm that the coupling is smaller when the stiffness of the nano-hexapod is increase, the /coupling ratio/ for the three nano-hexapod stiffnesses are shown in Figure ref:fig:rotating_nass_plant_coupling_comp.
%% Coupling ratio for the proposed active damping techniques evaluated for the three nano-hexapod stiffnesses
figure;
freqs_vc = logspace(-1, 2, 1000);
freqs_md = logspace(0, 3, 1000);
freqs_pz = logspace(0, 3, 1000);
figure;
tiledlayout(1, 3, 'TileSpacing', 'Compact', 'Padding', 'None');
ax1 = nexttile();
hold on;
plot(freqs_vc, abs(squeeze(freqresp(G_vc_fast('Dy', 'Fu')/G_vc_fast('Dx', 'Fu'), freqs_vc, 'Hz'))), 'color', zeros(1,3), ...
'DisplayName', 'OL');
plot(freqs_vc, abs(squeeze(freqresp(G_vc_fast_iff_hpf('Dy', 'Fu')/G_vc_fast_iff_hpf('Dx', 'Fu'), freqs_vc, 'Hz'))), 'color', colors(1,:), ...
'DisplayName', 'IFF + $k_p$');
plot(freqs_vc, abs(squeeze(freqresp(G_vc_fast_iff_kp('Dy', 'Fu')/G_vc_fast_iff_kp('Dx', 'Fu'), freqs_vc, 'Hz'))), 'color', colors(2,:), ...
'DisplayName', 'IFF + HPF');
plot(freqs_vc, abs(squeeze(freqresp(G_vc_fast_rdc('Dy', 'Fu')/G_vc_fast_rdc('Dx', 'Fu'), freqs_vc, 'Hz'))), 'color', colors(3,:), ...
'DisplayName', 'RDC');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('Coupling Ratio');
title('$k_n = 0.01\,N/\mu m$');
ax2 = nexttile();
hold on;
plot(freqs_md, abs(squeeze(freqresp(G_md_fast('Dy', 'Fu')/G_md_fast('Dx', 'Fu'), freqs_md, 'Hz'))), 'color', zeros(1,3), ...
'DisplayName', 'OL');
plot(freqs_md, abs(squeeze(freqresp(G_md_fast_iff_hpf('Dy', 'Fu')/G_md_fast_iff_hpf('Dx', 'Fu'), freqs_md, 'Hz'))), 'color', colors(1,:), ...
'DisplayName', 'IFF + $k_p$');
plot(freqs_md, abs(squeeze(freqresp(G_md_fast_iff_kp('Dy', 'Fu')/G_md_fast_iff_kp('Dx', 'Fu'), freqs_md, 'Hz'))), 'color', colors(2,:), ...
'DisplayName', 'IFF + HPF');
plot(freqs_md, abs(squeeze(freqresp(G_md_fast_rdc('Dy', 'Fu')/G_md_fast_rdc('Dx', 'Fu'), freqs_md, 'Hz'))), 'color', colors(3,:), ...
'DisplayName', 'RDC');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
title('$k_n = 1\,N/\mu m$');
ax3 = nexttile();
hold on;
plot(freqs_pz, abs(squeeze(freqresp(G_pz_fast('Dy', 'Fu')/G_pz_fast('Dx', 'Fu'), freqs_pz, 'Hz'))), 'color', zeros(1,3), ...
'DisplayName', 'OL');
plot(freqs_pz, abs(squeeze(freqresp(G_pz_fast_iff_hpf('Dy', 'Fu')/G_pz_fast_iff_hpf('Dx', 'Fu'), freqs_pz, 'Hz'))), 'color', colors(1,:), ...
'DisplayName', 'IFF + $k_p$');
plot(freqs_pz, abs(squeeze(freqresp(G_pz_fast_iff_kp('Dy', 'Fu')/G_pz_fast_iff_kp('Dx', 'Fu'), freqs_pz, 'Hz'))), 'color', colors(2,:), ...
'DisplayName', 'IFF + HPF');
plot(freqs_pz, abs(squeeze(freqresp(G_pz_fast_rdc('Dy', 'Fu')/G_pz_fast_rdc('Dx', 'Fu'), freqs_pz, 'Hz'))), 'color', colors(3,:), ...
'DisplayName', 'RDC');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
title('$k_n = 100\,N/\mu m$');
ldg = legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
ldg.ItemTokenSize = [20, 1];
linkaxes([ax1,ax2,ax3], 'y')
% Effect of disturbances
% The effect of three disturbances are considered:
% - Floor motion (Figure ref:fig:rotating_nass_effect_floor_motion)
% - Micro-Station vibrations (Figure ref:fig:rotating_nass_effect_stage_vibration)
% - Direct force applied on the payload (Figure ref:fig:rotating_nass_effect_direct_forces)
% #+begin_important
% Conclusions are similar than with the uniaxial (non-rotating) model:
% - Regarding the effect of floor motion and forces applied on the payload:
% - The stiffer, the better (magnitudes are lower for the right curves, Figures ref:fig:rotating_nass_effect_floor_motion and ref:fig:rotating_nass_effect_direct_forces)
% - Integral Force Feedback degrades the performances at low frequency compared to relative damping control
% - Regarding the effect of micro-station vibrations:
% - Having a soft nano-hexapod allows to filter these vibrations between the suspensions modes of the nano-hexapod and some flexible modes of the micro-station. Using relative damping control reduce this filtering (Figure ref:fig:rotating_nass_effect_stage_vibration, left).
% #+end_important
%% Effect of Floor motion on the position error - Comparison of active damping techniques for the three nano-hexapod stiffnesses
freqs = logspace(-1, 3, 1000);
figure;
tiledlayout(1, 3, 'TileSpacing', 'Compact', 'Padding', 'None');
ax1 = nexttile();
hold on;
plot(freqs, abs(squeeze(freqresp(G_vc_fast('Dx', 'Dfx'), freqs, 'Hz'))), 'color', zeros(1,3), ...
'DisplayName', 'OL');
plot(freqs, abs(squeeze(freqresp(G_vc_fast_iff_hpf('Dx', 'Dfx'), freqs, 'Hz'))), 'color', colors(1,:), ...
'DisplayName', 'IFF + $k_p$');
plot(freqs, abs(squeeze(freqresp(G_vc_fast_iff_kp('Dx', 'Dfx'), freqs, 'Hz'))), 'color', colors(2,:), ...
'DisplayName', 'IFF + HPF');
plot(freqs, abs(squeeze(freqresp(G_vc_fast_rdc('Dx', 'Dfx'), freqs, 'Hz'))), 'color', colors(3,:), ...
'DisplayName', 'RDC');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('Magnitude $d_x/D_{f,x}$ [m/N]');
xticks([1e-1, 1e0, 1e1, 1e2, 1e3]);
xtickangle(0)
title('$k_n = 0.01\,N/\mu m$');
ax2 = nexttile();
hold on;
plot(freqs, abs(squeeze(freqresp(G_md_fast('Dx', 'Dfx'), freqs, 'Hz'))), 'color', zeros(1,3), ...
'DisplayName', 'OL');
plot(freqs, abs(squeeze(freqresp(G_md_fast_iff_hpf('Dx', 'Dfx'), freqs, 'Hz'))), 'color', colors(1,:), ...
'DisplayName', 'IFF + $k_p$');
plot(freqs, abs(squeeze(freqresp(G_md_fast_iff_kp('Dx', 'Dfx'), freqs, 'Hz'))), 'color', colors(2,:), ...
'DisplayName', 'IFF + HPF');
plot(freqs, abs(squeeze(freqresp(G_md_fast_rdc('Dx', 'Dfx'), freqs, 'Hz'))), 'color', colors(3,:), ...
'DisplayName', 'RDC');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
xticks([1e-1, 1e0, 1e1, 1e2, 1e3]);
xtickangle(0)
title('$k_n = 1\,N/\mu m$');
ax3 = nexttile();
hold on;
plot(freqs, abs(squeeze(freqresp(G_pz_fast('Dx', 'Dfx'), freqs, 'Hz'))), 'color', zeros(1,3), ...
'DisplayName', 'OL');
plot(freqs, abs(squeeze(freqresp(G_pz_fast_iff_hpf('Dx', 'Dfx'), freqs, 'Hz'))), 'color', colors(1,:), ...
'DisplayName', 'IFF + $k_p$');
plot(freqs, abs(squeeze(freqresp(G_pz_fast_iff_kp('Dx', 'Dfx'), freqs, 'Hz'))), 'color', colors(2,:), ...
'DisplayName', 'IFF + HPF');
plot(freqs, abs(squeeze(freqresp(G_pz_fast_rdc('Dx', 'Dfx'), freqs, 'Hz'))), 'color', colors(3,:), ...
'DisplayName', 'RDC');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
xticks([1e-1, 1e0, 1e1, 1e2, 1e3]);
xtickangle(0)
title('$k_n = 100\,N/\mu m$');
ldg = legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
ldg.ItemTokenSize = [20, 1];
linkaxes([ax1,ax2,ax3], 'y')
% #+name: fig:rotating_nass_effect_floor_motion
% #+caption: Effect of Floor motion on the position error - Comparison of active damping techniques for the three nano-hexapod stiffnesses
% #+RESULTS:
% [[file:figs/rotating_nass_effect_floor_motion.png]]
%% Effect of micro-station vibrations on the position error - Comparison of active damping techniques for the three nano-hexapod stiffnesses
figure;
tiledlayout(1, 3, 'TileSpacing', 'Compact', 'Padding', 'None');
ax1 = nexttile();
hold on;
plot(freqs, abs(squeeze(freqresp(G_vc_fast('Dx', 'Ftx'), freqs, 'Hz'))), 'color', zeros(1,3), ...
'DisplayName', 'OL');
plot(freqs, abs(squeeze(freqresp(G_vc_fast_iff_hpf('Dx', 'Ftx'), freqs, 'Hz'))), 'color', colors(1,:), ...
'DisplayName', 'IFF + $k_p$');
plot(freqs, abs(squeeze(freqresp(G_vc_fast_iff_kp('Dx', 'Ftx'), freqs, 'Hz'))), 'color', colors(2,:), ...
'DisplayName', 'IFF + HPF');
plot(freqs, abs(squeeze(freqresp(G_vc_fast_rdc('Dx', 'Ftx'), freqs, 'Hz'))), 'color', colors(3,:), ...
'DisplayName', 'RDC');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('Magnitude $d_x/f_{t,x}$ [m/N]');
xticks([1e-1, 1e0, 1e1, 1e2, 1e3]);
xtickangle(0)
title('$k_n = 0.01\,N/\mu m$');
ax2 = nexttile();
hold on;
plot(freqs, abs(squeeze(freqresp(G_md_fast('Dx', 'Ftx'), freqs, 'Hz'))), 'color', zeros(1,3), ...
'DisplayName', 'OL');
plot(freqs, abs(squeeze(freqresp(G_md_fast_iff_hpf('Dx', 'Ftx'), freqs, 'Hz'))), 'color', colors(1,:), ...
'DisplayName', 'IFF + $k_p$');
plot(freqs, abs(squeeze(freqresp(G_md_fast_iff_kp('Dx', 'Ftx'), freqs, 'Hz'))), 'color', colors(2,:), ...
'DisplayName', 'IFF + HPF');
plot(freqs, abs(squeeze(freqresp(G_md_fast_rdc('Dx', 'Ftx'), freqs, 'Hz'))), 'color', colors(3,:), ...
'DisplayName', 'RDC');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
xticks([1e-1, 1e0, 1e1, 1e2, 1e3]);
xtickangle(0)
title('$k_n = 1\,N/\mu m$');
ax3 = nexttile();
hold on;
plot(freqs, abs(squeeze(freqresp(G_pz_fast('Dx', 'Ftx'), freqs, 'Hz'))), 'color', zeros(1,3), ...
'DisplayName', 'OL');
plot(freqs, abs(squeeze(freqresp(G_pz_fast_iff_hpf('Dx', 'Ftx'), freqs, 'Hz'))), 'color', colors(1,:), ...
'DisplayName', 'IFF + $k_p$');
plot(freqs, abs(squeeze(freqresp(G_pz_fast_iff_kp('Dx', 'Ftx'), freqs, 'Hz'))), 'color', colors(2,:), ...
'DisplayName', 'IFF + HPF');
plot(freqs, abs(squeeze(freqresp(G_pz_fast_rdc('Dx', 'Ftx'), freqs, 'Hz'))), 'color', colors(3,:), ...
'DisplayName', 'RDC');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
xticks([1e-1, 1e0, 1e1, 1e2, 1e3]);
xtickangle(0)
title('$k_n = 100\,N/\mu m$');
ldg = legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
ldg.ItemTokenSize = [20, 1];
linkaxes([ax1,ax2,ax3], 'y')
% #+name: fig:rotating_nass_effect_stage_vibration
% #+caption: Effect of micro-station vibrations on the position error - Comparison of active damping techniques for the three nano-hexapod stiffnesses
% #+RESULTS:
% [[file:figs/rotating_nass_effect_stage_vibration.png]]
%% Effect of sample forces on the position error - Comparison of active damping techniques for the three nano-hexapod stiffnesses
figure;
tiledlayout(1, 3, 'TileSpacing', 'Compact', 'Padding', 'None');
ax1 = nexttile();
hold on;
plot(freqs, abs(squeeze(freqresp(G_vc_fast('Dx', 'Fdx'), freqs, 'Hz'))), 'color', zeros(1,3), ...
'DisplayName', 'OL');
plot(freqs, abs(squeeze(freqresp(G_vc_fast_iff_hpf('Dx', 'Fdx'), freqs, 'Hz'))), 'color', colors(1,:), ...
'DisplayName', 'IFF + $k_p$');
plot(freqs, abs(squeeze(freqresp(G_vc_fast_iff_kp('Dx', 'Fdx'), freqs, 'Hz'))), 'color', colors(2,:), ...
'DisplayName', 'IFF + HPF');
plot(freqs, abs(squeeze(freqresp(G_vc_fast_rdc('Dx', 'Fdx'), freqs, 'Hz'))), 'color', colors(3,:), ...
'DisplayName', 'RDC');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('Magnitude $d_x/f_{s,x}$ [m/N]');
xticks([1e-1, 1e0, 1e1, 1e2, 1e3]);
xtickangle(0)
title('$k_n = 0.01\,N/\mu m$');
ax2 = nexttile();
hold on;
plot(freqs, abs(squeeze(freqresp(G_md_fast('Dx', 'Fdx'), freqs, 'Hz'))), 'color', zeros(1,3), ...
'DisplayName', 'OL');
plot(freqs, abs(squeeze(freqresp(G_md_fast_iff_hpf('Dx', 'Fdx'), freqs, 'Hz'))), 'color', colors(1,:), ...
'DisplayName', 'IFF + $k_p$');
plot(freqs, abs(squeeze(freqresp(G_md_fast_iff_kp('Dx', 'Fdx'), freqs, 'Hz'))), 'color', colors(2,:), ...
'DisplayName', 'IFF + HPF');
plot(freqs, abs(squeeze(freqresp(G_md_fast_rdc('Dx', 'Fdx'), freqs, 'Hz'))), 'color', colors(3,:), ...
'DisplayName', 'RDC');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
xticks([1e-1, 1e0, 1e1, 1e2, 1e3]);
xtickangle(0)
title('$k_n = 1\,N/\mu m$');
ax3 = nexttile();
hold on;
plot(freqs, abs(squeeze(freqresp(G_pz_fast('Dx', 'Fdx'), freqs, 'Hz'))), 'color', zeros(1,3), ...
'DisplayName', 'OL');
plot(freqs, abs(squeeze(freqresp(G_pz_fast_iff_hpf('Dx', 'Fdx'), freqs, 'Hz'))), 'color', colors(1,:), ...
'DisplayName', 'IFF + $k_p$');
plot(freqs, abs(squeeze(freqresp(G_pz_fast_iff_kp('Dx', 'Fdx'), freqs, 'Hz'))), 'color', colors(2,:), ...
'DisplayName', 'IFF + HPF');
plot(freqs, abs(squeeze(freqresp(G_pz_fast_rdc('Dx', 'Fdx'), freqs, 'Hz'))), 'color', colors(3,:), ...
'DisplayName', 'RDC');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]);
xticks([1e-1, 1e0, 1e1, 1e2, 1e3]);
xtickangle(0)
title('$k_n = 100\,N/\mu m$');
ldg = legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1);
ldg.ItemTokenSize = [20, 1];
linkaxes([ax1,ax2,ax3], 'y')

Binary file not shown.

BIN
matlab/rotating_model.slx Normal file

Binary file not shown.

View File

@@ -1,623 +0,0 @@
%% Clear Workspace and Close figures
clear; close all; clc;
%% Intialize Laplace variable
s = zpk('s');
addpath('./mat/');
% Initialization
% Let's load the previously defined parameters for the model.
load('parameters.mat');
bode_opts = bodeoptions;
bode_opts.FreqUnits = 'Hz';
bode_opts.MagUnits = 'abs';
bode_opts.MagScale = 'log';
bode_opts.Grid = 'on';
bode_opts.PhaseVisible = 'off';
bode_opts.Title.FontSize = 10;
bode_opts.XLabel.FontSize = 10;
bode_opts.YLabel.FontSize = 10;
bode_opts.TickLabel.FontSize = 10;
open rotating_frame.slx
% First we define the parameters that must be defined in order to run the Simscape simulation.
w = 2*pi; % Rotation speed [rad/s]
theta_e = 0; % Static measurement error on the angle theta [rad]
m = 5; % mass of the sample [kg]
mTuv = 30;% Mass of the moving part of the Tuv stage [kg]
kTuv = 1e8; % Stiffness of the Tuv stage [N/m]
cTuv = 0; % Damping of the Tuv stage [N/(m/s)]
% Then, we defined parameters that will be used in the following analysis.
mlight = 5; % Mass for light sample [kg]
mheavy = 55; % Mass for heavy sample [kg]
wlight = 2*pi; % Max rot. speed for light sample [rad/s]
wheavy = 2*pi/60; % Max rot. speed for heavy sample [rad/s]
kvc = 1e3; % Voice Coil Stiffness [N/m]
kpz = 1e8; % Piezo Stiffness [N/m]
d = 0.01; % Maximum excentricity from rotational axis [m]
freqs = logspace(-2, 3, 1000); % Frequency vector for analysis [Hz]
% Identification in the rotating referenced frame
% We initialize the inputs and outputs of the system to identify:
% - Inputs: $f_u$ and $f_v$
% - Outputs: $d_u$ and $d_v$
%% Options for Linearized
options = linearizeOptions;
options.SampleTime = 0;
%% Name of the Simulink File
mdl = 'rotating_frame';
%% Input/Output definition
io(1) = linio([mdl, '/fu'], 1, 'input');
io(2) = linio([mdl, '/fv'], 1, 'input');
io(3) = linio([mdl, '/du'], 1, 'output');
io(4) = linio([mdl, '/dv'], 1, 'output');
% We start we identify the transfer functions at high speed with the light sample.
w = wlight; % Rotation speed [rad/s]
m = mlight; % mass of the sample [kg]
kTuv = kpz;
Gpz_light = linearize(mdl, io, 0.1);
Gpz_light.InputName = {'Fu', 'Fv'};
Gpz_light.OutputName = {'Du', 'Dv'};
kTuv = kvc;
Gvc_light = linearize(mdl, io, 0.1);
Gvc_light.InputName = {'Fu', 'Fv'};
Gvc_light.OutputName = {'Du', 'Dv'};
% Then we identify the system with an heavy mass and low speed.
w = wheavy; % Rotation speed [rad/s]
m = mheavy; % mass of the sample [kg]
kTuv = kpz;
Gpz_heavy = linearize(mdl, io, 0.1);
Gpz_heavy.InputName = {'Fu', 'Fv'};
Gpz_heavy.OutputName = {'Du', 'Dv'};
kTuv = kvc;
Gvc_heavy = linearize(mdl, io, 0.1);
Gvc_heavy.InputName = {'Fu', 'Fv'};
Gvc_heavy.OutputName = {'Du', 'Dv'};
% Coupling ratio between $f_{uv}$ and $d_{uv}$
% In order to validate the equations written, we can compute the coupling ratio using the simscape model and compare with the equations.
% From the previous identification, we plot the coupling ratio in both case (figure [[fig:coupling_ratio_light_heavy]]).
% We obtain the same result than the analytical case (figures [[fig:coupling_light]] and [[fig:coupling_heavy]]).
figure;
hold on;
plot(freqs, abs(squeeze(freqresp(Gvc_light('Du', 'Fu'), freqs, 'Hz')))./abs(squeeze(freqresp(Gvc_light('Dv', 'Fu'), freqs, 'Hz'))));
plot(freqs, abs(squeeze(freqresp(Gpz_light('Du', 'Fu'), freqs, 'Hz')))./abs(squeeze(freqresp(Gpz_light('Dv', 'Fu'), freqs, 'Hz'))));
set(gca,'ColorOrderIndex',1);
plot(freqs, abs(squeeze(freqresp(Gvc_heavy('Du', 'Fu'), freqs, 'Hz')))./abs(squeeze(freqresp(Gvc_heavy('Dv', 'Fu'), freqs, 'Hz'))), '--');
plot(freqs, abs(squeeze(freqresp(Gpz_heavy('Du', 'Fu'), freqs, 'Hz')))./abs(squeeze(freqresp(Gpz_heavy('Dv', 'Fu'), freqs, 'Hz'))), '--');
hold off;
xlim([freqs(1), freqs(end)]);
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('Coupling ratio');
legend({'light - VC', 'light - PZ', 'heavy - VC', 'heavy - PZ'}, 'Location', 'northeast')
% Transfer function from force to force sensor (IFF plant)
%% Name of the Simulink File
mdl = 'rotating_frame';
%% Input/Output definition
io(1) = linio([mdl, '/fu'], 1, 'input');
io(2) = linio([mdl, '/fv'], 1, 'input');
io(3) = linio([mdl, '/fum'], 1, 'output');
io(4) = linio([mdl, '/fvm'], 1, 'output');
% We start we identify the transfer functions at high speed with the light sample.
w = wlight; % Rotation speed [rad/s]
m = mlight; % mass of the sample [kg]
kTuv = kpz;
Gpz_light = linearize(mdl, io, 0.1);
Gpz_light.InputName = {'Fu', 'Fv'};
Gpz_light.OutputName = {'Fum', 'Fvm'};
kTuv = kvc;
Gvc_light = linearize(mdl, io, 0.1);
Gvc_light.InputName = {'Fu', 'Fv'};
Gvc_light.OutputName = {'Fum', 'Fvm'};
% Then we identify the system with an heavy mass and low speed.
w = wheavy; % Rotation speed [rad/s]
m = mheavy; % mass of the sample [kg]
kTuv = kpz;
Gpz_heavy = linearize(mdl, io, 0.1);
Gpz_heavy.InputName = {'Fu', 'Fv'};
Gpz_heavy.OutputName = {'Fum', 'Fvm'};
kTuv = kvc;
Gvc_heavy = linearize(mdl, io, 0.1);
Gvc_heavy.InputName = {'Fu', 'Fv'};
Gvc_heavy.OutputName = {'Fum', 'Fvm'};
figure;
hold on;
plot(freqs, abs(squeeze(freqresp(Gvc_heavy('Fum', 'Fu'), freqs, 'Hz'))));
hold off;
xlim([freqs(1), freqs(end)]);
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('Coupling ratio');
legend({'light - VC', 'light - PZ', 'heavy - VC', 'heavy - PZ'}, 'Location', 'northeast')
% Plant identification
% The goal is to study the control problems due to the coupling that appears because of the rotation.
%% Options for Linearized
options = linearizeOptions;
options.SampleTime = 0;
%% Name of the Simulink File
mdl = 'rotating_frame';
%% Input/Output definition
io(1) = linio([mdl, '/fu'], 1, 'input');
io(2) = linio([mdl, '/fv'], 1, 'input');
io(3) = linio([mdl, '/du'], 1, 'output');
io(4) = linio([mdl, '/dv'], 1, 'output');
% First, we identify the system when the rotation speed is null and then when the rotation speed is equal to 60rpm.
% The actuators are voice coil with some damping added.
% The bode plot of the system not rotating and rotating at 60rpm is shown figure [[fig:Gvc_speed]].
w = 0; % Rotation speed [rad/s]
m = mlight; % mass of the sample [kg]
kTuv = kvc;
cTuv = 0.1*sqrt(kTuv*m);
Gvc = linearize(mdl, io, 0.1);
Gvc.InputName = {'Fu', 'Fv'};
Gvc.OutputName = {'Du', 'Dv'};
w = wlight; % Rotation speed [rad/s]
m = mlight; % mass of the sample [kg]
kTuv = kvc;
cTuv = 0.1*sqrt(kTuv*m);
Gtvc = linearize(mdl, io, 0.1);
Gtvc.InputName = {'Fu', 'Fv'};
Gtvc.OutputName = {'Du', 'Dv'};
figure;
ax1 = subplot(2,2,1);
title('From $F_u$ to $D_u$');
hold on;
plot(freqs, abs(squeeze(freqresp(Gvc(1, 1), freqs, 'Hz'))));
plot(freqs, abs(squeeze(freqresp(Gtvc(1, 1), freqs, 'Hz'))));
hold off;
xlim([freqs(1), freqs(end)]);
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]);
ylabel('Magnitude [m/N]');
ax2 = subplot(2,2,3);
hold on;
plot(freqs, 180/pi*angle(squeeze(freqresp(Gvc(1, 1), freqs, 'Hz'))), 'DisplayName', 'Gvc - $\omega = 0$');
plot(freqs, 180/pi*angle(squeeze(freqresp(Gtvc(1, 1), freqs, 'Hz'))), 'DisplayName', 'Gvc - $\omega = 60$rpm');
hold off;
yticks(-180:90:180);
ylim([-180 180]);
xlim([freqs(1), freqs(end)]);
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
legend('Location', 'northeast');
linkaxes([ax1,ax2],'x');
ax1 = subplot(2,2,2);
title('From $F_u$ to $D_v$');
hold on;
plot(freqs, abs(squeeze(freqresp(Gvc(1, 2), freqs, 'Hz'))));
plot(freqs, abs(squeeze(freqresp(Gtvc(1, 2), freqs, 'Hz'))));
hold off;
xlim([freqs(1), freqs(end)]);
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]);
ax2 = subplot(2,2,4);
hold on;
plot(freqs, 180/pi*angle(squeeze(freqresp(Gvc(1, 2), freqs, 'Hz'))), 'DisplayName', 'Gvc - $\omega = 0$');
plot(freqs, 180/pi*angle(squeeze(freqresp(Gtvc(1, 2), freqs, 'Hz'))), 'DisplayName', 'Gvc - $\omega = 60$rpm');
hold off;
yticks(-180:90:180);
ylim([-180 180]);
xlim([freqs(1), freqs(end)]);
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]');
legend('Location', 'northeast');
linkaxes([ax1,ax2],'x');
% Effect of rotation speed
% We first identify the system (voice coil and light mass) for multiple rotation speed.
% Then we compute the bode plot of the system (figure [[fig:Guu_uv_ws]]).
% As the rotation frequency increases:
% - one pole goes to lower frequencies while the other goes to higher frequencies
% - one zero appears between the two poles
% - the zero disappears when $\omega > \sqrt{\frac{k}{m}}$ and the low frequency pole becomes unstable (positive real part)
% To stabilize the unstable pole, we need a control bandwidth of at least twice of frequency of the unstable pole.
ws = linspace(0, 2*pi, 5); % Rotation speed vector [rad/s]
m = mlight; % mass of the sample [kg]
kTuv = kvc;
cTuv = 0.1*sqrt(kTuv*m);
Gs_vc = {zeros(1, length(ws))};
for i = 1:length(ws)
w = ws(i);
Gs_vc{i} = linearize(mdl, io, 0.1);
Gs_vc{i}.InputName = {'Fu', 'Fv'};
Gs_vc{i}.OutputName = {'Du', 'Dv'};
end
freqs = logspace(-2, 2, 1000);
figure;
ax1 = subplot(2,2,1);
title('$D_u/F_u$');
hold on;
for i = 1:length(ws)
plot(freqs, abs(squeeze(freqresp(Gs_vc{i}(1, 1), freqs, 'Hz'))));
end
hold off;
xlim([freqs(1), freqs(end)]);
ylim([1e-8, 1]);
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]);
ylabel('Magnitude [m/N]');
ax2 = subplot(2,2,3);
hold on;
for i = 1:length(ws)
plot(freqs, 180/pi*angle(squeeze(freqresp(Gs_vc{i}(1, 1), freqs, 'Hz'))));
end
hold off;
yticks(-180:90:180);
ylim([-180 180]);
xlim([freqs(1), freqs(end)]);
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
linkaxes([ax1,ax2],'x');
ax1 = subplot(2,2,2);
title('$D_v/F_u$');
hold on;
for i = 1:length(ws)
plot(freqs, abs(squeeze(freqresp(Gs_vc{i}(1, 2), freqs, 'Hz'))));
end
hold off;
xlim([freqs(1), freqs(end)]);
ylim([1e-8, 1]);
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]);
set(gca, 'YTickLabel',[]);
ax2 = subplot(2,2,4);
hold on;
for i = 1:length(ws)
plot(freqs, 180/pi*angle(squeeze(freqresp(Gs_vc{i}(1, 2), freqs, 'Hz'))), 'DisplayName', sprintf('w = %.0f [rpm]', ws(i)*60/2/pi));
end
hold off;
yticks(-180:90:180);
ylim([-180 180]);
xlim([freqs(1), freqs(end)]);
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
set(gca, 'YTickLabel',[]);
xlabel('Frequency [Hz]');
legend('Location', 'northeast');
linkaxes([ax1,ax2],'x');
% #+LABEL: fig:Guu_uv_ws
% #+CAPTION: Diagonal term as a function of the rotation frequency
% [[file:figs/Guu_uv_ws.png]]
% Then, we can look at the same plots for the piezoelectric actuator (figure [[fig:Guu_ws_pz]]). The effect of the rotation frequency has very little effect on the dynamics of the system to control.
ws = linspace(0, 2*pi, 5); % Rotation speed vector [rad/s]
m = mlight; % mass of the sample [kg]
kTuv = kpz;
cTuv = 0.1*sqrt(kTuv*m);
Gs_pz = {zeros(1, length(ws))};
for i = 1:length(ws)
w = ws(i);
Gs_pz{i} = linearize(mdl, io, 0.1);
Gs_pz{i}.InputName = {'Fu', 'Fv'};
Gs_pz{i}.OutputName = {'Du', 'Dv'};
end
freqs = logspace(2, 3, 1000);
figure;
ax1 = subplot(2,1,1);
hold on;
for i = 1:length(ws)
plot(freqs, abs(squeeze(freqresp(Gs_pz{i}(1, 1), freqs, 'Hz'))));
end
hold off;
xlim([freqs(1), freqs(end)]);
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]);
ylabel('Magnitude [m/N]');
ax2 = subplot(2,1,2);
hold on;
for i = 1:length(ws)
plot(freqs, 180/pi*angle(squeeze(freqresp(Gs_pz{i}(1, 1), freqs, 'Hz'))), 'DisplayName', sprintf('w = %.0f [rpm]', ws(i)*60/2/pi));
end
hold off;
yticks(-180:90:180);
ylim([-180 180]);
xlim([freqs(1), freqs(end)]);
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
legend('Location', 'northeast');
linkaxes([ax1,ax2],'x');
% Controller design
% We design a controller based on the identification when the system is not rotating.
% The obtained controller is a lead-lag controller with the following transfer function.
Kll = 2.0698e09*(s+40.45)*(s+1.181)/((s+0.01)*(s+198.4)*(s+2790));
K = [Kll 0;
0 Kll];
K.InputName = {'Du', 'Dv'};
K.OutputName = {'Fu', 'Fv'};
% The loop gain is displayed figure [[fig:Gvc_loop_gain]].
freqs = logspace(-2, 2, 1000);
figure;
% Amplitude
ax1 = subplot(2,1,1);
hold on;
plot(freqs, abs(squeeze(freqresp(Gvc('Du', 'fu')*Kll, freqs, 'Hz'))), '-');
set(gca,'xscale','log'); set(gca,'yscale','log');
ylabel('Amplitude [m/N]');
set(gca, 'XTickLabel',[]);
hold off;
% Phase
ax2 = subplot(2,1,2);
hold on;
plot(freqs, 180/pi*unwrap(angle(squeeze(freqresp(Gvc('Du', 'fu')*Kll, freqs, 'Hz')))), '-');
set(gca,'xscale','log');
yticks(-180:180:180);
ylim([-180 180]);
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
linkaxes([ax1,ax2],'x');
% Controlling the rotating system
% We here want to see if the system is robust with respect to the rotation speed.
% We use the controller that was designed based on the dynamics of the non-rotating system.
% Let's first plot the SISO loop gain.
freqs = logspace(-2, 2, 1000);
figure;
% Amplitude
ax1 = subplot(2,1,1);
hold on;
plot(freqs, abs(squeeze(freqresp(Gs_vc{1}(1, 1)*Kll, freqs, 'Hz'))), '-');
plot(freqs, abs(squeeze(freqresp(Gs_vc{2}(1, 1)*Kll, freqs, 'Hz'))), '-');
plot(freqs, abs(squeeze(freqresp(Gs_vc{5}(1, 1)*Kll, freqs, 'Hz'))), '-');
set(gca,'xscale','log'); set(gca,'yscale','log');
ylabel('Amplitude [m/N]');
set(gca, 'XTickLabel',[]);
hold off;
% Phase
ax2 = subplot(2,1,2);
hold on;
plot(freqs, 180/pi*angle(squeeze(freqresp(Gs_vc{1}(1, 1)*Kll, freqs, 'Hz'))), 'DisplayName', sprintf('%.0f rpm', ws(1)./(2*pi).*60));
plot(freqs, 180/pi*angle(squeeze(freqresp(Gs_vc{2}(1, 1)*Kll, freqs, 'Hz'))), 'DisplayName', sprintf('%.0f rpm', ws(2)./(2*pi).*60));
plot(freqs, 180/pi*angle(squeeze(freqresp(Gs_vc{5}(1, 1)*Kll, freqs, 'Hz'))), 'DisplayName', sprintf('%.0f rpm', ws(5)./(2*pi).*60));
set(gca,'xscale','log');
yticks(-180:180:180);
ylim([-180 180]);
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
hold off;
legend('Location', 'northeast');
linkaxes([ax1,ax2],'x');
% #+NAME: fig:loop_gain_turning
% #+CAPTION: Loop Gain $G_u * K$
% [[file:figs/loop_gain_turning.png]]
% We can now compute the close-loop systems.
Gvc_cl = {zeros(1, length(ws))};
for i = 1:length(ws)
Gvc_cl{i} = feedback(Gs_vc{i}, K, 'name');
end
% Let's now look on figure [[fig:evolution_poles_u]] at the evolution of the poles of the system when closing only one loop (controlling only one direction). We see that two complex conjugate poles are approaching the real axis and then they separate: one goes to positive real part and the other goes to negative real part.
% The system then goes unstable at some point (here for $\omega=60rpm$).
figure;
hold on;
for i = 1:length(ws)
sys = feedback(Gs_vc{i}(1, 1), K(1, 1), 'name');
plot(real(pole(sys)), imag(pole(sys)), 'x', 'DisplayName', sprintf('$\\omega = %.0f rpm$', ws(i)/(2*pi)*60));
end
hold off;
xlim([-80, 10]);
xlabel('Real Axis'); ylabel('Imaginary Axis');
legend('Location', 'northeast');
% #+NAME: fig:evolution_poles_u
% #+CAPTION: Evolution of the poles of the closed-loop system when closing just one loop
% [[file:figs/evolution_poles_u.png]]
% If we look at the poles of the closed loop-system for multiple rotating speed (figure [[fig:poles_cl_system]]) when closing the two loops (MIMO system), we see that they all have a negative real part (stable system), and their evolution on the complex plane is rather small compare to the open loop evolution.
figure;
hold on;
for i = 1:length(ws)
plot(real(pole(Gvc_cl{i})), imag(pole(Gvc_cl{i})), 'x', 'DisplayName', sprintf('$\\omega = %.0f rpm$', ws(i)/(2*pi)*60));
end
hold off;
xlim([-80, 0]);
xlabel('Real Axis'); ylabel('Imaginary Axis');
legend('Location', 'northeast');
% Close loop performance
% First, we create the closed loop systems. Then, we plot the transfer function from the reference signals $[r_u, r_v]$ to the output $[d_u, d_v]$ (figure [[fig:perfcomp]]).
freqs = logspace(-2, 3, 1000);
figure;
ax1 = subplot(1,2,1);
title('$G_{r_u \to d_u}$')
hold on;
for i = 1:length(ws)
sys = Gvc_cl{i}*K;
plot(freqs, abs(squeeze(freqresp(sys(1, 1), freqs, 'Hz'))));
end
hold off;
xlim([freqs(1), freqs(end)]);
ylim([1e-4, 10]);
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]'); ylabel('Magnitude [m/N]');
ax2 = subplot(1,2,2);
title('$G_{r_u \to d_v}$')
hold on;
for i = 1:length(ws)
sys = Gvc_cl{i}*K;
plot(freqs, abs(squeeze(freqresp(sys(1, 2), freqs, 'Hz'))), 'DisplayName', sprintf('$\\omega = %.0f rpm$', ws(i)/(2*pi)*60));
end
hold off;
xlim([freqs(1), freqs(end)]);
ylim([1e-4, 10]);
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]');
legend('Location', 'northeast')
linkaxes([ax1,ax2],'x');
% Campbell Diagram for the close loop system
m = mlight;
k = kvc;
c = 0.1*sqrt(k*m);
wsvc = linspace(0, 10, 100); % [rad/s]
polesvc = zeros(8, length(wsvc));
for i = 1:length(wsvc)
Gs = (m*s^2 + (k-m*w^2))/((m*s^2 + (k - m*w^2))^2 + (2*m*w*s)^2);
Gcs = (2*m*w*s)/((m*s^2 + (k - m*w^2))^2 + (2*m*w*s)^2);
G = [Gs, Gcs; Gcs, Gs];
G.InputName = {'Fu', 'Fv'};
G.OutputName = {'Du', 'Dv'};
polei = pole(feedback(G, K, 'name'));
polesvc(:, i) = sort(polei(imag(polei) > 0));
polei = pole(feedback(G, 10*K, 'name'));
polesvcb(:, i) = sort(polei(imag(polei) > 0));
end
figure;
% Amplitude
ax1 = subplot(1,2,1);
hold on;
for i = 1:8
plot(wsvc, real(polesvc(i, :)), 'b')
plot(wsvc, real(polesvcb(i, :)), 'r')
end
plot(wsvc, zeros(size(wsvc)), 'k--')
hold off;
xlabel('Rotation Frequency [rad/s]');
ylabel('Pole Real Part');
ax2 = subplot(1,2,2);
hold on;
for i = 1:8
plot(wsvc, imag(polesvc(i, :)), 'b')
plot(wsvc, imag(polesvcb(i, :)), 'r')
end
hold off;
xlabel('Rotation Frequency [rad/s]');
ylabel('Pole Imaginary Part');

View File

@@ -0,0 +1,10 @@
% =computeSimultaneousDamping=
function [xi_min] = computeSimultaneousDamping(g, G, K)
[~, xi] = damp(minreal(feedback(G, g*K), [], false));
xi_min = 1/min(xi);
if xi_min < 0
xi_min = 1e8;
end
end

View File

@@ -1,397 +0,0 @@
%% Clear Workspace and Close figures
clear; close all; clc;
%% Intialize Laplace variable
s = zpk('s');
% Numerical Values for the NASS
% Let's define the parameters for the NASS.
mlight = 35; % Mass for light sample [kg]
mheavy = 85; % Mass for heavy sample [kg]
wlight = 2*pi; % Max rot. speed for light sample [rad/s]
wheavy = 2*pi/60; % Max rot. speed for heavy sample [rad/s]
kvc = 1e3; % Voice Coil Stiffness [N/m]
kpz = 1e8; % Piezo Stiffness [N/m]
wdot = 1; % Maximum rotation acceleration [rad/s2]
d = 0.01; % Maximum excentricity from rotational axis [m]
ddot = 0.2; % Maximum Horizontal speed [m/s]
save('./mat/parameters.mat');
labels = {'Light sample mass [kg]', ...
'Heavy sample mass [kg]', ...
'Max rot. speed - light [rpm]', ...
'Max rot. speed - heavy [rpm]', ...
'Voice Coil Stiffness [N/m]', ...
'Piezo Stiffness [N/m]', ...
'Max rot. acceleration [rad/s2]', ...
'Max mass excentricity [m]', ...
'Max Horizontal speed [m/s]'};
data = [mlight, mheavy, 60*wlight/2/pi, 60*wheavy/2/pi, kvc, kpz, wdot, d, ddot];
data2orgtable(data', labels, {}, ' %.1e ')
% Euler and Coriolis forces - Numerical Result
% First we will determine the value for Euler and Coriolis forces during regular experiment.
% - *Euler forces*: $m d_v \ddot{\theta}$
% - *Coriolis forces*: $2 m \dot{d_v} \dot{\theta}$
Felight = mlight*d*wdot;
Feheavy = mheavy*d*wdot;
Fclight = 2*mlight*ddot*wlight;
Fcheavy = 2*mheavy*ddot*wheavy;
% The obtained values are displayed in table [[tab:euler_coriolis]].
data = [Fclight, Fcheavy ;
Felight, Feheavy];
data2orgtable(data, {'Coriolis', 'Euler'}, {'Light', 'Heavy'}, ' %.1fN ')
% Negative Spring Effect - Numerical Result
% The negative stiffness due to the rotation is equal to $-m{\omega_0}^2$.
Klight = mlight*wlight^2;
Kheavy = mheavy*wheavy^2;
% The values for the negative spring effect are displayed in table [[tab:negative_spring]].
% This is definitely negligible when using piezoelectric actuators. It may not be the case when using voice coil actuators.
data = [Klight, Kheavy];
data2orgtable(data, {'Neg. Spring'}, {'Light', 'Heavy'}, ' %.1f[N/m] ')
% Numerical Analysis
% We plot on the same graph $\frac{|-m \omega^2 + (k - m {\omega_0}^2)|}{|2 m \omega_0 \omega|}$ for the voice coil and the piezo:
% - with the light sample (figure [[fig:coupling_light]]).
% - with the heavy sample (figure [[fig:coupling_heavy]]).
f = logspace(-1, 3, 1000);
figure;
hold on;
plot(f, abs(-mlight*(2*pi*f).^2 + kvc - mlight * wlight^2)./abs(2*mlight*wlight*2*pi*f), 'DisplayName', 'Voice Coil')
plot(f, abs(-mlight*(2*pi*f).^2 + kpz - mlight * wlight^2)./abs(2*mlight*wlight*2*pi*f), 'DisplayName', 'Piezo')
plot(f, ones(1, length(f)), 'k--', 'HandleVisibility', 'off')
hold off;
xlim([f(1), f(end)]);
set(gca, 'xscale', 'log'); set(gca, 'yscale', 'log');
xlabel('Frequency [Hz]');
legend('Location', 'northeast');
% #+LABEL: fig:coupling_light
% #+CAPTION: Relative Coupling for light mass and high rotation speed
% [[file:./figs/coupling_light.png]]
figure;
hold on;
plot(f, abs(-mheavy*(2*pi*f).^2 + kvc - mheavy * wheavy^2)./abs(2*mheavy*wheavy*2*pi*f), 'DisplayName', 'Voice Coil')
plot(f, abs(-mheavy*(2*pi*f).^2 + kpz - mheavy * wheavy^2)./abs(2*mheavy*wheavy*2*pi*f), 'DisplayName', 'Piezo')
plot(f, ones(1, length(f)), 'k--', 'HandleVisibility', 'off')
hold off;
xlim([f(1), f(end)]);
set(gca, 'xscale', 'log'); set(gca, 'yscale', 'log');
xlabel('Frequency [Hz]');
legend('Location', 'northeast');
% Limitations due to negative stiffness effect
% If $\max{\dot{\theta}} \ll \sqrt{\frac{k}{m}}$, then the negative spring effect is negligible and $k - m\dot{\theta}^2 \approx k$.
% Let's estimate what is the maximum rotation speed for which the negative stiffness effect is still negligible ($\omega_\text{max} = 0.1 \sqrt{\frac{k}{m}}$). Results are shown table [[tab:negative_stiffness]].
data = 0.1*60*(1/2/pi)*[sqrt(kvc/mlight), sqrt(kpz/mlight);
sqrt(kvc/mheavy), sqrt(kpz/mheavy)];
data2orgtable(data, {'Light', 'Heavy'}, {'Voice Coil', 'Piezo'}, ' %.0f[rpm] ')
% #+NAME: tab:negative_stiffness
% #+CAPTION: Maximum rotation speed at which negative stiffness is negligible ($0.1\sqrt{\frac{k}{m}}$)
% #+RESULTS:
% | | Voice Coil | Piezo |
% |-------+------------+-----------|
% | Light | 5[rpm] | 1614[rpm] |
% | Heavy | 3[rpm] | 1036[rpm] |
% The negative spring effect is proportional to the rotational speed $\omega$.
% The system dynamics will be much more affected when using soft actuator.
% #+begin_important
% Negative stiffness effect has very important effect when using soft actuators.
% #+end_important
% The system can even goes unstable when $m \omega^2 > k$, that is when the centrifugal forces are higher than the forces due to stiffness.
% From this analysis, we can determine the lowest practical stiffness that is possible to use: $k_\text{min} = 10 m \omega^2$ (table sec:tab:min_k)
data = 10*[mlight*2*pi, mheavy*2*pi/60]
data2orgtable(data, {'k min [N/m]'}, {'Light', 'Heavy'}, ' %.0f ')
% Voice coil actuator
m = mlight;
k = kvc;
ws = linspace(0, 2*pi, 5); % Rotation speed vector [rad/s]
Gs = {zeros(1, length(ws))};
Gcs = {zeros(1, length(ws))};
for i = 1:length(ws)
w = ws(i);
Gs(i) = {(m*s^2 + (k-m*w^2))/((m*s^2 + (k - m*w^2))^2 + (2*m*w*s)^2)};
Gcs(i) = {(2*m*w*s)/((m*s^2 + (k - m*w^2))^2 + (2*m*w*s)^2)};
end
freqs = logspace(-2, 1, 1000);
figure;
ax1 = subplot(2,1,1);
hold on;
for i = 1:length(ws)
plot(freqs, abs(squeeze(freqresp(Gs{i}, freqs, 'Hz'))));
end
hold off;
xlim([freqs(1), freqs(end)]);
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]);
ylabel('Magnitude [m/N]');
ax2 = subplot(2,1,2);
hold on;
for i = 1:length(ws)
plot(freqs, 180/pi*angle(squeeze(freqresp(Gs{i}, freqs, 'Hz'))), 'DisplayName', sprintf('w = %.0f [rpm]', ws(i)*60/2/pi));
end
hold off;
yticks(-180:90:180);
ylim([-180 180]);
xlim([freqs(1), freqs(end)]);
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
legend('Location', 'southwest');
linkaxes([ax1,ax2],'x');
% #+LABEL: fig:G_ws_vc
% #+CAPTION: Bode plot of the direct transfer function term (from $F_u$ to $D_u$) for multiple rotation speed - Voice coil
% [[file:figs/G_ws_vc.png]]
freqs = logspace(-2, 1, 1000);
figure;
ax1 = subplot(2,1,1);
hold on;
for i = 1:length(ws)
plot(freqs, abs(squeeze(freqresp(Gcs{i}, freqs, 'Hz'))));
end
hold off;
xlim([freqs(1), freqs(end)]);
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]);
ylabel('Magnitude [m/N]');
ax2 = subplot(2,1,2);
hold on;
for i = 1:length(ws)
plot(freqs, 180/pi*angle(squeeze(freqresp(Gcs{i}, freqs, 'Hz'))), 'DisplayName', sprintf('w = %.0f [rpm]', ws(i)*60/2/pi));
end
hold off;
yticks(-180:90:180);
ylim([-180 180]);
xlim([freqs(1), freqs(end)]);
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
legend('Location', 'southwest');
linkaxes([ax1,ax2],'x');
% Piezoelectric actuator
m = mlight;
k = kpz;
ws = linspace(0, 2*pi, 5); % Rotation speed vector [rad/s]
Gs = {zeros(1, length(ws))};
Gcs = {zeros(1, length(ws))};
for i = 1:length(ws)
w = ws(i);
Gs(i) = {(m*s^2 + (k-m*w^2))/((m*s^2 + (k - m*w^2))^2 + (2*m*w*s)^2)};
Gcs(i) = {(2*m*w*s)/((m*s^2 + (k - m*w^2))^2 + (2*m*w*s)^2)};
end
freqs = logspace(2, 3, 1000);
figure;
ax1 = subplot(2,1,1);
hold on;
for i = 1:length(ws)
plot(freqs, abs(squeeze(freqresp(Gs{i}, freqs, 'Hz'))));
end
hold off;
xlim([freqs(1), freqs(end)]);
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]);
ylabel('Magnitude [m/N]');
ax2 = subplot(2,1,2);
hold on;
for i = 1:length(ws)
plot(freqs, 180/pi*angle(squeeze(freqresp(Gs{i}, freqs, 'Hz'))), 'DisplayName', sprintf('w = %.0f [rpm]', ws(i)*60/2/pi));
end
hold off;
yticks(-180:90:180);
ylim([-180 180]);
xlim([freqs(1), freqs(end)]);
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
legend('Location', 'southwest');
linkaxes([ax1,ax2],'x');
% #+LABEL: fig:G_ws_pz
% #+CAPTION: Bode plot of the direct transfer function term (from $F_u$ to $D_u$) for multiple rotation speed - Piezoelectric actuator
% [[file:figs/G_ws_pz.png]]
figure;
ax1 = subplot(2,1,1);
hold on;
for i = 1:length(ws)
plot(freqs, abs(squeeze(freqresp(Gcs{i}, freqs, 'Hz'))));
end
hold off;
xlim([freqs(1), freqs(end)]);
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
set(gca, 'XTickLabel',[]);
ylabel('Magnitude [m/N]');
ax2 = subplot(2,1,2);
hold on;
for i = 1:length(ws)
plot(freqs, 180/pi*angle(squeeze(freqresp(Gcs{i}, freqs, 'Hz'))), 'DisplayName', sprintf('w = %.0f [rpm]', ws(i)*60/2/pi));
end
hold off;
yticks(-180:90:180);
ylim([-180 180]);
xlim([freqs(1), freqs(end)]);
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
xlabel('Frequency [Hz]'); ylabel('Phase [deg]');
legend('Location', 'southwest');
linkaxes([ax1,ax2],'x');
% Campbell diagram
% The poles of the system are computed for multiple values of the rotation frequency. To simplify the computation of the poles, we add some damping to the system.
m = mlight;
k = kvc;
c = 0.1*sqrt(k*m);
wsvc = linspace(0, 10, 100); % [rad/s]
polesvc = zeros(2, length(wsvc));
for i = 1:length(wsvc)
polei = pole(1/((m*s^2 + c*s + (k - m*wsvc(i)^2))^2 + (2*m*wsvc(i)*s)^2));
polesvc(:, i) = sort(polei(imag(polei) > 0));
end
m = mlight;
k = kpz;
c = 0.1*sqrt(k*m);
wspz = linspace(0, 1000, 100); % [rad/s]
polespz = zeros(2, length(wspz));
for i = 1:length(wspz)
polei = pole(1/((m*s^2 + c*s + (k - m*wspz(i)^2))^2 + (2*m*wspz(i)*s)^2));
polespz(:, i) = sort(polei(imag(polei) > 0));
end
% We then plot the real and imaginary part of the poles as a function of the rotation frequency (figures [[fig:poles_w_vc]] and [[fig:poles_w_pz]]).
% When the real part of one pole becomes positive, the system goes unstable.
% For the voice coil (figure [[fig:poles_w_vc]]), the system is unstable when the rotation speed is above 5 rad/s. The real and imaginary part of the poles of the system with piezoelectric actuators are changing much less (figure [[fig:poles_w_pz]]).
figure;
% Amplitude
ax1 = subplot(1,2,1);
hold on;
plot(wsvc, real(polesvc(1, :)))
set(gca,'ColorOrderIndex',1)
plot(wsvc, real(polesvc(2, :)))
plot(wsvc, zeros(size(wsvc)), 'k--')
hold off;
xlabel('Rotation Frequency [rad/s]');
ylabel('Pole Real Part');
ax2 = subplot(1,2,2);
hold on;
plot(wsvc, imag(polesvc(1, :)))
set(gca,'ColorOrderIndex',1)
plot(wsvc, -imag(polesvc(1, :)))
set(gca,'ColorOrderIndex',1)
plot(wsvc, imag(polesvc(2, :)))
set(gca,'ColorOrderIndex',1)
plot(wsvc, -imag(polesvc(2, :)))
hold off;
xlabel('Rotation Frequency [rad/s]');
ylabel('Pole Imaginary Part');
% #+LABEL: fig:poles_w_vc
% #+CAPTION: Real and Imaginary part of the poles of the system as a function of the rotation speed - Voice Coil and light sample
% [[file:figs/poles_w_vc.png]]
figure;
% Amplitude
ax1 = subplot(1,2,1);
hold on;
plot(wspz, real(polespz(1, :)))
set(gca,'ColorOrderIndex',1)
plot(wspz, real(polespz(2, :)))
plot(wspz, zeros(size(wspz)), 'k--')
hold off;
xlabel('Rotation Frequency [rad/s]');
ylabel('Pole Real Part');
ax2 = subplot(1,2,2);
hold on;
plot(wspz, imag(polespz(1, :)))
set(gca,'ColorOrderIndex',1)
plot(wspz, -imag(polespz(1, :)))
set(gca,'ColorOrderIndex',1)
plot(wspz, imag(polespz(2, :)))
set(gca,'ColorOrderIndex',1)
plot(wspz, -imag(polespz(2, :)))
hold off;
xlabel('Rotation Frequency [rad/s]');
ylabel('Pole Imaginary Part');