Change all indentation

This commit is contained in:
2021-01-08 15:54:58 +01:00
parent f5922ca970
commit ed0c18829b
29 changed files with 8682 additions and 8683 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -40,111 +40,111 @@ In this section, we wish to compare the effect of forces/torques applied by the
** Matlab Init :noexport:ignore:
#+begin_src matlab :tangle no :exports none :results silent :noweb yes :var current_dir=(file-name-directory buffer-file-name)
<<matlab-dir>>
<<matlab-dir>>
#+end_src
#+begin_src matlab :exports none :results silent :noweb yes
<<matlab-init>>
<<matlab-init>>
#+end_src
#+begin_src matlab
simulinkproject('../');
simulinkproject('../');
#+end_src
#+begin_src matlab
open('stewart_platform_model.slx')
open('stewart_platform_model.slx')
#+end_src
** Comparison with fixed support
Let's generate a Stewart platform.
#+begin_src matlab
stewart = initializeStewartPlatform();
stewart = initializeFramesPositions(stewart, 'H', 90e-3, 'MO_B', 45e-3);
stewart = generateGeneralConfiguration(stewart);
stewart = computeJointsPose(stewart);
stewart = initializeStrutDynamics(stewart);
stewart = initializeJointDynamics(stewart, 'type_F', 'universal_p', 'type_M', 'spherical_p');
stewart = initializeCylindricalPlatforms(stewart);
stewart = initializeCylindricalStruts(stewart);
stewart = computeJacobian(stewart);
stewart = initializeStewartPose(stewart);
stewart = initializeInertialSensor(stewart, 'type', 'none');
stewart = initializeStewartPlatform();
stewart = initializeFramesPositions(stewart, 'H', 90e-3, 'MO_B', 45e-3);
stewart = generateGeneralConfiguration(stewart);
stewart = computeJointsPose(stewart);
stewart = initializeStrutDynamics(stewart);
stewart = initializeJointDynamics(stewart, 'type_F', 'universal_p', 'type_M', 'spherical_p');
stewart = initializeCylindricalPlatforms(stewart);
stewart = initializeCylindricalStruts(stewart);
stewart = computeJacobian(stewart);
stewart = initializeStewartPose(stewart);
stewart = initializeInertialSensor(stewart, 'type', 'none');
#+end_src
We don't put any flexibility below the Stewart platform such that *its base is fixed to an inertial frame*.
We also don't put any payload on top of the Stewart platform.
#+begin_src matlab
ground = initializeGround('type', 'none');
payload = initializePayload('type', 'none');
controller = initializeController('type', 'open-loop');
ground = initializeGround('type', 'none');
payload = initializePayload('type', 'none');
controller = initializeController('type', 'open-loop');
#+end_src
The transfer function from actuator forces $\bm{\tau}$ to the relative displacement of the mobile platform $\mathcal{\bm{X}}$ is extracted.
#+begin_src matlab
%% Options for Linearized
options = linearizeOptions;
options.SampleTime = 0;
%% Options for Linearized
options = linearizeOptions;
options.SampleTime = 0;
%% Name of the Simulink File
mdl = 'stewart_platform_model';
%% Name of the Simulink File
mdl = 'stewart_platform_model';
%% Input/Output definition
clear io; io_i = 1;
io(io_i) = linio([mdl, '/Controller'], 1, 'openinput'); io_i = io_i + 1; % Actuator Force Inputs [N]
io(io_i) = linio([mdl, '/Relative Motion Sensor'], 1, 'openoutput'); io_i = io_i + 1; % Position/Orientation of {B} w.r.t. {A}
%% Input/Output definition
clear io; io_i = 1;
io(io_i) = linio([mdl, '/Controller'], 1, 'openinput'); io_i = io_i + 1; % Actuator Force Inputs [N]
io(io_i) = linio([mdl, '/Relative Motion Sensor'], 1, 'openoutput'); io_i = io_i + 1; % Position/Orientation of {B} w.r.t. {A}
%% Run the linearization
G = linearize(mdl, io, options);
G.InputName = {'F1', 'F2', 'F3', 'F4', 'F5', 'F6'};
G.OutputName = {'Edx', 'Edy', 'Edz', 'Erx', 'Ery', 'Erz'};
%% Run the linearization
G = linearize(mdl, io, options);
G.InputName = {'F1', 'F2', 'F3', 'F4', 'F5', 'F6'};
G.OutputName = {'Edx', 'Edy', 'Edz', 'Erx', 'Ery', 'Erz'};
#+end_src
Using the Jacobian matrix, we compute the transfer function from force/torques applied by the actuators on the frame $\{B\}$ fixed to the mobile platform:
#+begin_src matlab
Gc = minreal(G*inv(stewart.kinematics.J'));
Gc.InputName = {'Fnx', 'Fny', 'Fnz', 'Mnx', 'Mny', 'Mnz'};
Gc = minreal(G*inv(stewart.kinematics.J'));
Gc.InputName = {'Fnx', 'Fny', 'Fnz', 'Mnx', 'Mny', 'Mnz'};
#+end_src
We also extract the transfer function from external forces $\bm{\mathcal{F}}_{\text{ext}}$ on the frame $\{B\}$ fixed to the mobile platform to the relative displacement $\mathcal{\bm{X}}$ of $\{B\}$ with respect to frame $\{A\}$:
#+begin_src matlab
%% Input/Output definition
clear io; io_i = 1;
io(io_i) = linio([mdl, '/Disturbances'], 1, 'openinput', [], 'F_ext'); io_i = io_i + 1; % External forces/torques applied on {B}
io(io_i) = linio([mdl, '/Relative Motion Sensor'], 1, 'openoutput'); io_i = io_i + 1; % Position/Orientation of {B} w.r.t. {A}
%% Input/Output definition
clear io; io_i = 1;
io(io_i) = linio([mdl, '/Disturbances'], 1, 'openinput', [], 'F_ext'); io_i = io_i + 1; % External forces/torques applied on {B}
io(io_i) = linio([mdl, '/Relative Motion Sensor'], 1, 'openoutput'); io_i = io_i + 1; % Position/Orientation of {B} w.r.t. {A}
%% Run the linearization
Gd = linearize(mdl, io, options);
Gd.InputName = {'Fex', 'Fey', 'Fez', 'Mex', 'Mey', 'Mez'};
Gd.OutputName = {'Edx', 'Edy', 'Edz', 'Erx', 'Ery', 'Erz'};
%% Run the linearization
Gd = linearize(mdl, io, options);
Gd.InputName = {'Fex', 'Fey', 'Fez', 'Mex', 'Mey', 'Mez'};
Gd.OutputName = {'Edx', 'Edy', 'Edz', 'Erx', 'Ery', 'Erz'};
#+end_src
The comparison of the two transfer functions is shown in Figure [[fig:comparison_Fext_F_fixed_base]].
#+begin_src matlab :exports none
freqs = logspace(1, 4, 1000);
freqs = logspace(1, 4, 1000);
figure;
figure;
ax1 = subplot(2, 1, 1);
hold on;
plot(freqs, abs(squeeze(freqresp(Gc(1,1), freqs, 'Hz'))), '-');
plot(freqs, abs(squeeze(freqresp(Gd(1,1), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Amplitude [m/N]'); set(gca, 'XTickLabel',[]);
ax1 = subplot(2, 1, 1);
hold on;
plot(freqs, abs(squeeze(freqresp(Gc(1,1), freqs, 'Hz'))), '-');
plot(freqs, abs(squeeze(freqresp(Gd(1,1), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Amplitude [m/N]'); set(gca, 'XTickLabel',[]);
ax2 = subplot(2, 1, 2);
hold on;
plot(freqs, 180/pi*angle(squeeze(freqresp(Gc(1,1), freqs, 'Hz'))), '-');
plot(freqs, 180/pi*angle(squeeze(freqresp(Gd(1,1), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
ylabel('Phase [deg]'); xlabel('Frequency [Hz]');
ylim([-180, 180]);
yticks([-180, -90, 0, 90, 180]);
legend({'$\mathcal{X}_{x}/\mathcal{F}_{x}$', '$\mathcal{X}_{x}/\mathcal{F}_{x,ext}$'});
ax2 = subplot(2, 1, 2);
hold on;
plot(freqs, 180/pi*angle(squeeze(freqresp(Gc(1,1), freqs, 'Hz'))), '-');
plot(freqs, 180/pi*angle(squeeze(freqresp(Gd(1,1), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
ylabel('Phase [deg]'); xlabel('Frequency [Hz]');
ylim([-180, 180]);
yticks([-180, -90, 0, 90, 180]);
legend({'$\mathcal{X}_{x}/\mathcal{F}_{x}$', '$\mathcal{X}_{x}/\mathcal{F}_{x,ext}$'});
linkaxes([ax1,ax2],'x');
linkaxes([ax1,ax2],'x');
#+end_src
#+header: :tangle no :exports results :results none :noweb yes
@@ -159,19 +159,19 @@ The comparison of the two transfer functions is shown in Figure [[fig:comparison
This can be understood from figure [[fig:1dof_actuator_external_forces]] where $\mathcal{F}_{x}$ and $\mathcal{F}_{x,\text{ext}}$ have clearly the same effect on $\mathcal{X}_{x}$.
#+begin_src latex :file 1dof_actuator_external_forces.pdf
\begin{tikzpicture}
\draw[ground] (-1, 0) -- (1, 0);
\begin{tikzpicture}
\draw[ground] (-1, 0) -- (1, 0);
\draw[spring] (-0.6, 0) -- (-0.6, 1.5) node[midway, left=0.1]{$k$};
\draw[actuator] ( 0.6, 0) -- ( 0.6, 1.5) node[midway, left=0.1](F){$\mathcal{F}_{x}$};
\draw[spring] (-0.6, 0) -- (-0.6, 1.5) node[midway, left=0.1]{$k$};
\draw[actuator] ( 0.6, 0) -- ( 0.6, 1.5) node[midway, left=0.1](F){$\mathcal{F}_{x}$};
\draw[fill=white] (-1, 1.5) rectangle (1, 2) node[pos=0.5]{$m$};
\draw[fill=white] (-1, 1.5) rectangle (1, 2) node[pos=0.5]{$m$};
\draw[dashed] (1, 2) -- ++(0.5, 0);
\draw[->] (1.5, 2) -- ++(0, 0.5) node[right]{$\mathcal{X}_{x}$};
\draw[dashed] (1, 2) -- ++(0.5, 0);
\draw[->] (1.5, 2) -- ++(0, 0.5) node[right]{$\mathcal{X}_{x}$};
\draw[->] (0, 2) node[]{$\bullet$} -- (0, 2.8) node[below right]{$\mathcal{F}_{x,\text{ext}}$};
\end{tikzpicture}
\draw[->] (0, 2) node[]{$\bullet$} -- (0, 2.8) node[below right]{$\mathcal{F}_{x,\text{ext}}$};
\end{tikzpicture}
#+end_src
#+name: fig:1dof_actuator_external_forces
@@ -182,62 +182,62 @@ This can be understood from figure [[fig:1dof_actuator_external_forces]] where $
** Comparison with a flexible support
We now add a flexible support under the Stewart platform.
#+begin_src matlab
ground = initializeGround('type', 'flexible');
ground = initializeGround('type', 'flexible');
#+end_src
And we perform again the identification.
#+begin_src matlab
%% Input/Output definition
clear io; io_i = 1;
io(io_i) = linio([mdl, '/Controller'], 1, 'openinput'); io_i = io_i + 1; % Actuator Force Inputs [N]
io(io_i) = linio([mdl, '/Relative Motion Sensor'], 1, 'openoutput'); io_i = io_i + 1; % Position/Orientation of {B} w.r.t. {A}
%% Input/Output definition
clear io; io_i = 1;
io(io_i) = linio([mdl, '/Controller'], 1, 'openinput'); io_i = io_i + 1; % Actuator Force Inputs [N]
io(io_i) = linio([mdl, '/Relative Motion Sensor'], 1, 'openoutput'); io_i = io_i + 1; % Position/Orientation of {B} w.r.t. {A}
%% Run the linearization
G = linearize(mdl, io, options);
G.InputName = {'F1', 'F2', 'F3', 'F4', 'F5', 'F6'};
G.OutputName = {'Edx', 'Edy', 'Edz', 'Erx', 'Ery', 'Erz'};
%% Run the linearization
G = linearize(mdl, io, options);
G.InputName = {'F1', 'F2', 'F3', 'F4', 'F5', 'F6'};
G.OutputName = {'Edx', 'Edy', 'Edz', 'Erx', 'Ery', 'Erz'};
Gc = minreal(G*inv(stewart.kinematics.J'));
Gc.InputName = {'Fnx', 'Fny', 'Fnz', 'Mnx', 'Mny', 'Mnz'};
Gc = minreal(G*inv(stewart.kinematics.J'));
Gc.InputName = {'Fnx', 'Fny', 'Fnz', 'Mnx', 'Mny', 'Mnz'};
%% Input/Output definition
clear io; io_i = 1;
io(io_i) = linio([mdl, '/Disturbances'], 1, 'openinput', [], 'F_ext'); io_i = io_i + 1; % External forces/torques applied on {B}
io(io_i) = linio([mdl, '/Relative Motion Sensor'], 1, 'openoutput'); io_i = io_i + 1; % Position/Orientation of {B} w.r.t. {A}
%% Input/Output definition
clear io; io_i = 1;
io(io_i) = linio([mdl, '/Disturbances'], 1, 'openinput', [], 'F_ext'); io_i = io_i + 1; % External forces/torques applied on {B}
io(io_i) = linio([mdl, '/Relative Motion Sensor'], 1, 'openoutput'); io_i = io_i + 1; % Position/Orientation of {B} w.r.t. {A}
%% Run the linearization
Gd = linearize(mdl, io, options);
Gd.InputName = {'Fex', 'Fey', 'Fez', 'Mex', 'Mey', 'Mez'};
Gd.OutputName = {'Edx', 'Edy', 'Edz', 'Erx', 'Ery', 'Erz'};
%% Run the linearization
Gd = linearize(mdl, io, options);
Gd.InputName = {'Fex', 'Fey', 'Fez', 'Mex', 'Mey', 'Mez'};
Gd.OutputName = {'Edx', 'Edy', 'Edz', 'Erx', 'Ery', 'Erz'};
#+end_src
The comparison between the obtained transfer functions is shown in Figure [[fig:comparison_Fext_F_flexible_base]].
#+begin_src matlab :exports none
freqs = logspace(1, 4, 1000);
freqs = logspace(1, 4, 1000);
figure;
figure;
ax1 = subplot(2, 1, 1);
hold on;
plot(freqs, abs(squeeze(freqresp(Gc(1,1), freqs, 'Hz'))), '-');
plot(freqs, abs(squeeze(freqresp(Gd(1,1), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Amplitude [m/N]'); set(gca, 'XTickLabel',[]);
ax1 = subplot(2, 1, 1);
hold on;
plot(freqs, abs(squeeze(freqresp(Gc(1,1), freqs, 'Hz'))), '-');
plot(freqs, abs(squeeze(freqresp(Gd(1,1), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylabel('Amplitude [m/N]'); set(gca, 'XTickLabel',[]);
ax2 = subplot(2, 1, 2);
hold on;
plot(freqs, 180/pi*angle(squeeze(freqresp(Gc(1,1), freqs, 'Hz'))), '-');
plot(freqs, 180/pi*angle(squeeze(freqresp(Gd(1,1), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
ylabel('Phase [deg]'); xlabel('Frequency [Hz]');
ylim([-180, 180]);
yticks([-180, -90, 0, 90, 180]);
legend({'$\mathcal{X}_{x}/\mathcal{F}_{x}$', '$\mathcal{X}_{x}/\mathcal{F}_{x,ext}$'});
ax2 = subplot(2, 1, 2);
hold on;
plot(freqs, 180/pi*angle(squeeze(freqresp(Gc(1,1), freqs, 'Hz'))), '-');
plot(freqs, 180/pi*angle(squeeze(freqresp(Gd(1,1), freqs, 'Hz'))), '--');
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin');
ylabel('Phase [deg]'); xlabel('Frequency [Hz]');
ylim([-180, 180]);
yticks([-180, -90, 0, 90, 180]);
legend({'$\mathcal{X}_{x}/\mathcal{F}_{x}$', '$\mathcal{X}_{x}/\mathcal{F}_{x,ext}$'});
linkaxes([ax1,ax2],'x');
linkaxes([ax1,ax2],'x');
#+end_src
#+header: :tangle no :exports results :results none :noweb yes
@@ -254,22 +254,22 @@ We see that $\mathcal{F}_{x}$ applies a force both on $m$ and $m^{\prime}$ where
And thus $\mathcal{F}_{x}$ and $\mathcal{F}_{x,\text{ext}}$ have clearly *not* the same effect on $\mathcal{X}_{x}$.
#+begin_src latex :file 2dof_actuator_external_forces.pdf
\begin{tikzpicture}
\draw[ground] (-1, 0) -- (1, 0);
\begin{tikzpicture}
\draw[ground] (-1, 0) -- (1, 0);
\draw[spring] (0, 0) -- (0, 1.5) node[midway, left=0.1]{$k^{\prime}$};
\draw[fill=white] (-1, 1.5) rectangle (1, 2) node[pos=0.5]{$m^{\prime}$};
\draw[spring] (0, 0) -- (0, 1.5) node[midway, left=0.1]{$k^{\prime}$};
\draw[fill=white] (-1, 1.5) rectangle (1, 2) node[pos=0.5]{$m^{\prime}$};
\draw[spring] (-0.6, 2) -- (-0.6, 3.5) node[midway, left=0.1]{$k$};
\draw[actuator] ( 0.6, 2) -- ( 0.6, 3.5) node[midway, left=0.1](F){$\mathcal{F}_{x}$};
\draw[spring] (-0.6, 2) -- (-0.6, 3.5) node[midway, left=0.1]{$k$};
\draw[actuator] ( 0.6, 2) -- ( 0.6, 3.5) node[midway, left=0.1](F){$\mathcal{F}_{x}$};
\draw[fill=white] (-1, 3.5) rectangle (1, 4) node[pos=0.5]{$m$};
\draw[fill=white] (-1, 3.5) rectangle (1, 4) node[pos=0.5]{$m$};
\draw[dashed] (1, 4) -- ++(0.5, 0);
\draw[->] (1.5, 4) -- ++(0, 0.5) node[right]{$\mathcal{X}_{x}$};
\draw[dashed] (1, 4) -- ++(0.5, 0);
\draw[->] (1.5, 4) -- ++(0, 0.5) node[right]{$\mathcal{X}_{x}$};
\draw[->] (0, 4) node[]{$\bullet$} -- (0, 4.8) node[below right]{$\mathcal{F}_{x,\text{ext}}$};
\end{tikzpicture}
\draw[->] (0, 4) node[]{$\bullet$} -- (0, 4.8) node[below right]{$\mathcal{F}_{x,\text{ext}}$};
\end{tikzpicture}
#+end_src
#+name: fig:2dof_actuator_external_forces
@@ -289,67 +289,67 @@ In this section, we see how the Compliance matrix of the Stewart platform is lin
** Matlab Init :noexport:ignore:
#+begin_src matlab :tangle no :exports none :results silent :noweb yes :var current_dir=(file-name-directory buffer-file-name)
<<matlab-dir>>
<<matlab-dir>>
#+end_src
#+begin_src matlab :exports none :results silent :noweb yes
<<matlab-init>>
<<matlab-init>>
#+end_src
#+begin_src matlab
simulinkproject('../');
simulinkproject('../');
#+end_src
#+begin_src matlab
open('stewart_platform_model.slx')
open('stewart_platform_model.slx')
#+end_src
** Analysis
Initialization of the Stewart platform.
#+begin_src matlab
stewart = initializeStewartPlatform();
stewart = initializeFramesPositions(stewart, 'H', 90e-3, 'MO_B', 45e-3);
stewart = generateGeneralConfiguration(stewart);
stewart = computeJointsPose(stewart);
stewart = initializeStrutDynamics(stewart);
stewart = initializeJointDynamics(stewart, 'type_F', 'universal_p', 'type_M', 'spherical_p');
stewart = initializeCylindricalPlatforms(stewart);
stewart = initializeCylindricalStruts(stewart);
stewart = computeJacobian(stewart);
stewart = initializeStewartPose(stewart);
stewart = initializeInertialSensor(stewart, 'type', 'none');
stewart = initializeStewartPlatform();
stewart = initializeFramesPositions(stewart, 'H', 90e-3, 'MO_B', 45e-3);
stewart = generateGeneralConfiguration(stewart);
stewart = computeJointsPose(stewart);
stewart = initializeStrutDynamics(stewart);
stewart = initializeJointDynamics(stewart, 'type_F', 'universal_p', 'type_M', 'spherical_p');
stewart = initializeCylindricalPlatforms(stewart);
stewart = initializeCylindricalStruts(stewart);
stewart = computeJacobian(stewart);
stewart = initializeStewartPose(stewart);
stewart = initializeInertialSensor(stewart, 'type', 'none');
#+end_src
No flexibility below the Stewart platform and no payload.
#+begin_src matlab
ground = initializeGround('type', 'none');
payload = initializePayload('type', 'none');
controller = initializeController('type', 'open-loop');
ground = initializeGround('type', 'none');
payload = initializePayload('type', 'none');
controller = initializeController('type', 'open-loop');
#+end_src
Estimation of the transfer function from $\mathcal{\bm{F}}$ to $\mathcal{\bm{X}}$:
#+begin_src matlab
%% Options for Linearized
options = linearizeOptions;
options.SampleTime = 0;
%% Options for Linearized
options = linearizeOptions;
options.SampleTime = 0;
%% Name of the Simulink File
mdl = 'stewart_platform_model';
%% Name of the Simulink File
mdl = 'stewart_platform_model';
%% Input/Output definition
clear io; io_i = 1;
io(io_i) = linio([mdl, '/Controller'], 1, 'openinput'); io_i = io_i + 1; % Actuator Force Inputs [N]
io(io_i) = linio([mdl, '/Relative Motion Sensor'], 1, 'openoutput'); io_i = io_i + 1; % Position/Orientation of {B} w.r.t. {A}
%% Input/Output definition
clear io; io_i = 1;
io(io_i) = linio([mdl, '/Controller'], 1, 'openinput'); io_i = io_i + 1; % Actuator Force Inputs [N]
io(io_i) = linio([mdl, '/Relative Motion Sensor'], 1, 'openoutput'); io_i = io_i + 1; % Position/Orientation of {B} w.r.t. {A}
%% Run the linearization
G = linearize(mdl, io, options);
G.InputName = {'F1', 'F2', 'F3', 'F4', 'F5', 'F6'};
G.OutputName = {'Edx', 'Edy', 'Edz', 'Erx', 'Ery', 'Erz'};
%% Run the linearization
G = linearize(mdl, io, options);
G.InputName = {'F1', 'F2', 'F3', 'F4', 'F5', 'F6'};
G.OutputName = {'Edx', 'Edy', 'Edz', 'Erx', 'Ery', 'Erz'};
#+end_src
#+begin_src matlab
Gc = minreal(G*inv(stewart.kinematics.J'));
Gc.InputName = {'Fnx', 'Fny', 'Fnz', 'Mnx', 'Mny', 'Mnz'};
Gc = minreal(G*inv(stewart.kinematics.J'));
Gc.InputName = {'Fnx', 'Fny', 'Fnz', 'Mnx', 'Mny', 'Mnz'};
#+end_src
Let's first look at the low frequency transfer function matrix from $\mathcal{\bm{F}}$ to $\mathcal{\bm{X}}$.

File diff suppressed because it is too large Load Diff

View File

@@ -46,66 +46,66 @@ In this document, we discuss the various methods to identify the behavior of the
** Introduction :ignore:
** Matlab Init :noexport:ignore:
#+begin_src matlab :tangle no :exports none :results silent :noweb yes :var current_dir=(file-name-directory buffer-file-name)
<<matlab-dir>>
<<matlab-dir>>
#+end_src
#+begin_src matlab :exports none :results silent :noweb yes
<<matlab-init>>
<<matlab-init>>
#+end_src
#+begin_src matlab :results none :exports none
simulinkproject('../');
simulinkproject('../');
#+end_src
#+begin_src matlab
open('stewart_platform_model.slx')
open('stewart_platform_model.slx')
#+end_src
** Initialize the Stewart Platform
#+begin_src matlab
stewart = initializeStewartPlatform();
stewart = initializeFramesPositions(stewart);
stewart = generateGeneralConfiguration(stewart);
stewart = computeJointsPose(stewart);
stewart = initializeStrutDynamics(stewart);
stewart = initializeJointDynamics(stewart, 'type_F', 'universal_p', 'type_M', 'spherical_p');
stewart = initializeCylindricalPlatforms(stewart);
stewart = initializeCylindricalStruts(stewart);
stewart = computeJacobian(stewart);
stewart = initializeStewartPose(stewart);
stewart = initializeInertialSensor(stewart);
stewart = initializeStewartPlatform();
stewart = initializeFramesPositions(stewart);
stewart = generateGeneralConfiguration(stewart);
stewart = computeJointsPose(stewart);
stewart = initializeStrutDynamics(stewart);
stewart = initializeJointDynamics(stewart, 'type_F', 'universal_p', 'type_M', 'spherical_p');
stewart = initializeCylindricalPlatforms(stewart);
stewart = initializeCylindricalStruts(stewart);
stewart = computeJacobian(stewart);
stewart = initializeStewartPose(stewart);
stewart = initializeInertialSensor(stewart);
#+end_src
#+begin_src matlab
ground = initializeGround('type', 'none');
payload = initializePayload('type', 'none');
controller = initializeController('type', 'open-loop');
ground = initializeGround('type', 'none');
payload = initializePayload('type', 'none');
controller = initializeController('type', 'open-loop');
#+end_src
** Identification
#+begin_src matlab
%% Options for Linearized
options = linearizeOptions;
options.SampleTime = 0;
%% Options for Linearized
options = linearizeOptions;
options.SampleTime = 0;
%% Name of the Simulink File
mdl = 'stewart_platform_model';
%% Name of the Simulink File
mdl = 'stewart_platform_model';
%% Input/Output definition
clear io; io_i = 1;
io(io_i) = linio([mdl, '/Controller'], 1, 'openinput'); io_i = io_i + 1; % Actuator Force Inputs [N]
io(io_i) = linio([mdl, '/Relative Motion Sensor'], 1, 'openoutput'); io_i = io_i + 1; % Position/Orientation of {B} w.r.t. {A}
io(io_i) = linio([mdl, '/Relative Motion Sensor'], 2, 'openoutput'); io_i = io_i + 1; % Velocity of {B} w.r.t. {A}
%% Input/Output definition
clear io; io_i = 1;
io(io_i) = linio([mdl, '/Controller'], 1, 'openinput'); io_i = io_i + 1; % Actuator Force Inputs [N]
io(io_i) = linio([mdl, '/Relative Motion Sensor'], 1, 'openoutput'); io_i = io_i + 1; % Position/Orientation of {B} w.r.t. {A}
io(io_i) = linio([mdl, '/Relative Motion Sensor'], 2, 'openoutput'); io_i = io_i + 1; % Velocity of {B} w.r.t. {A}
%% Run the linearization
G = linearize(mdl, io);
% G.InputName = {'tau1', 'tau2', 'tau3', 'tau4', 'tau5', 'tau6'};
% G.OutputName = {'Xdx', 'Xdy', 'Xdz', 'Xrx', 'Xry', 'Xrz', 'Vdx', 'Vdy', 'Vdz', 'Vrx', 'Vry', 'Vrz'};
%% Run the linearization
G = linearize(mdl, io);
% G.InputName = {'tau1', 'tau2', 'tau3', 'tau4', 'tau5', 'tau6'};
% G.OutputName = {'Xdx', 'Xdy', 'Xdz', 'Xrx', 'Xry', 'Xrz', 'Vdx', 'Vdy', 'Vdz', 'Vrx', 'Vry', 'Vrz'};
#+end_src
Let's check the size of =G=:
#+begin_src matlab :results replace output
size(G)
size(G)
#+end_src
#+RESULTS:
@@ -117,7 +117,7 @@ Let's check the size of =G=:
We expect to have only 12 states (corresponding to the 6dof of the mobile platform).
#+begin_src matlab :results replace output
Gm = minreal(G);
Gm = minreal(G);
#+end_src
#+RESULTS:
@@ -129,7 +129,7 @@ And indeed, we obtain 12 states.
** Coordinate transformation
We can perform the following transformation using the =ss2ss= command.
#+begin_src matlab
Gt = ss2ss(Gm, Gm.C);
Gt = ss2ss(Gm, Gm.C);
#+end_src
Then, the =C= matrix of =Gt= is the unity matrix which means that the states of the state space model are equal to the measurements $\bm{Y}$.
@@ -138,29 +138,29 @@ The measurements are the 6 displacement and 6 velocities of mobile platform with
We could perform the transformation by hand:
#+begin_src matlab
At = Gm.C*Gm.A*pinv(Gm.C);
At = Gm.C*Gm.A*pinv(Gm.C);
Bt = Gm.C*Gm.B;
Bt = Gm.C*Gm.B;
Ct = eye(12);
Dt = zeros(12, 6);
Ct = eye(12);
Dt = zeros(12, 6);
Gt = ss(At, Bt, Ct, Dt);
Gt = ss(At, Bt, Ct, Dt);
#+end_src
** Analysis
#+begin_src matlab
[V,D] = eig(Gt.A);
[V,D] = eig(Gt.A);
#+end_src
#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*)
ws = imag(diag(D))/2/pi;
[ws,I] = sort(ws)
ws = imag(diag(D))/2/pi;
[ws,I] = sort(ws)
xi = 100*real(diag(D))./imag(diag(D));
xi = xi(I);
xi = 100*real(diag(D))./imag(diag(D));
xi = xi(I);
data2orgtable([[1:length(ws(ws>0))]', ws(ws>0), xi(xi>0)], {}, {'Mode Number', 'Resonance Frequency [Hz]', 'Damping Ratio [%]'}, ' %.1f ');
data2orgtable([[1:length(ws(ws>0))]', ws(ws>0), xi(xi>0)], {}, {'Mode Number', 'Resonance Frequency [Hz]', 'Damping Ratio [%]'}, ' %.1f ');
#+end_src
#+RESULTS:
@@ -180,54 +180,54 @@ To visualize the i'th mode, we may excite the system using the inputs $U_i$ such
Let's first sort the modes and just take the modes corresponding to a eigenvalue with a positive imaginary part.
#+begin_src matlab
ws = imag(diag(D));
[ws,I] = sort(ws)
ws = ws(7:end); I = I(7:end);
ws = imag(diag(D));
[ws,I] = sort(ws)
ws = ws(7:end); I = I(7:end);
#+end_src
#+begin_src matlab
for i = 1:length(ws)
for i = 1:length(ws)
#+end_src
#+begin_src matlab
i_mode = I(i); % the argument is the i'th mode
i_mode = I(i); % the argument is the i'th mode
#+end_src
#+begin_src matlab
lambda_i = D(i_mode, i_mode);
xi_i = V(:,i_mode);
lambda_i = D(i_mode, i_mode);
xi_i = V(:,i_mode);
a_i = real(lambda_i);
b_i = imag(lambda_i);
a_i = real(lambda_i);
b_i = imag(lambda_i);
#+end_src
Let do 10 periods of the mode.
#+begin_src matlab
t = linspace(0, 10/(imag(lambda_i)/2/pi), 1000);
U_i = pinv(Gt.B) * real(xi_i * lambda_i * (cos(b_i * t) + 1i*sin(b_i * t)));
t = linspace(0, 10/(imag(lambda_i)/2/pi), 1000);
U_i = pinv(Gt.B) * real(xi_i * lambda_i * (cos(b_i * t) + 1i*sin(b_i * t)));
#+end_src
#+begin_src matlab
U = timeseries(U_i, t);
U = timeseries(U_i, t);
#+end_src
Simulation:
#+begin_src matlab
load('mat/conf_simscape.mat');
set_param(conf_simscape, 'StopTime', num2str(t(end)));
sim(mdl);
load('mat/conf_simscape.mat');
set_param(conf_simscape, 'StopTime', num2str(t(end)));
sim(mdl);
#+end_src
Save the movie of the mode shape.
#+begin_src matlab
smwritevideo(mdl, sprintf('figs/mode%i', i), ...
'PlaybackSpeedRatio', 1/(b_i/2/pi), ...
'FrameRate', 30, ...
'FrameSize', [800, 400]);
smwritevideo(mdl, sprintf('figs/mode%i', i), ...
'PlaybackSpeedRatio', 1/(b_i/2/pi), ...
'FrameRate', 30, ...
'FrameSize', [800, 400]);
#+end_src
#+begin_src matlab
end
end
#+end_src
#+name: fig:mode1
@@ -247,83 +247,83 @@ Save the movie of the mode shape.
** Introduction :ignore:
** Matlab Init :noexport:
#+begin_src matlab :tangle no :exports none :results silent :noweb yes :var current_dir=(file-name-directory buffer-file-name)
<<matlab-dir>>
<<matlab-dir>>
#+end_src
#+begin_src matlab :exports none :results silent :noweb yes
<<matlab-init>>
<<matlab-init>>
#+end_src
#+begin_src matlab
simulinkproject('../');
simulinkproject('../');
#+end_src
#+begin_src matlab
open('stewart_platform_model.slx')
open('stewart_platform_model.slx')
#+end_src
** Initialize the Stewart platform
#+begin_src matlab
stewart = initializeStewartPlatform();
stewart = initializeFramesPositions(stewart, 'H', 90e-3, 'MO_B', 45e-3);
stewart = generateGeneralConfiguration(stewart);
stewart = computeJointsPose(stewart);
stewart = initializeStrutDynamics(stewart);
stewart = initializeJointDynamics(stewart, 'type_F', 'universal_p', 'type_M', 'spherical_p');
stewart = initializeCylindricalPlatforms(stewart);
stewart = initializeCylindricalStruts(stewart);
stewart = computeJacobian(stewart);
stewart = initializeStewartPose(stewart);
stewart = initializeInertialSensor(stewart, 'type', 'accelerometer', 'freq', 5e3);
stewart = initializeStewartPlatform();
stewart = initializeFramesPositions(stewart, 'H', 90e-3, 'MO_B', 45e-3);
stewart = generateGeneralConfiguration(stewart);
stewart = computeJointsPose(stewart);
stewart = initializeStrutDynamics(stewart);
stewart = initializeJointDynamics(stewart, 'type_F', 'universal_p', 'type_M', 'spherical_p');
stewart = initializeCylindricalPlatforms(stewart);
stewart = initializeCylindricalStruts(stewart);
stewart = computeJacobian(stewart);
stewart = initializeStewartPose(stewart);
stewart = initializeInertialSensor(stewart, 'type', 'accelerometer', 'freq', 5e3);
#+end_src
We set the rotation point of the ground to be at the same point at frames $\{A\}$ and $\{B\}$.
#+begin_src matlab
ground = initializeGround('type', 'rigid', 'rot_point', stewart.platform_F.FO_A);
payload = initializePayload('type', 'rigid');
controller = initializeController('type', 'open-loop');
ground = initializeGround('type', 'rigid', 'rot_point', stewart.platform_F.FO_A);
payload = initializePayload('type', 'rigid');
controller = initializeController('type', 'open-loop');
#+end_src
** Transmissibility
#+begin_src matlab
%% Options for Linearized
options = linearizeOptions;
options.SampleTime = 0;
%% Options for Linearized
options = linearizeOptions;
options.SampleTime = 0;
%% Name of the Simulink File
mdl = 'stewart_platform_model';
%% Name of the Simulink File
mdl = 'stewart_platform_model';
%% Input/Output definition
clear io; io_i = 1;
io(io_i) = linio([mdl, '/Disturbances/D_w'], 1, 'openinput'); io_i = io_i + 1; % Base Motion [m, rad]
io(io_i) = linio([mdl, '/Absolute Motion Sensor'], 1, 'openoutput'); io_i = io_i + 1; % Absolute Motion [m, rad]
%% Input/Output definition
clear io; io_i = 1;
io(io_i) = linio([mdl, '/Disturbances/D_w'], 1, 'openinput'); io_i = io_i + 1; % Base Motion [m, rad]
io(io_i) = linio([mdl, '/Absolute Motion Sensor'], 1, 'openoutput'); io_i = io_i + 1; % Absolute Motion [m, rad]
%% Run the linearization
T = linearize(mdl, io, options);
T.InputName = {'Wdx', 'Wdy', 'Wdz', 'Wrx', 'Wry', 'Wrz'};
T.OutputName = {'Edx', 'Edy', 'Edz', 'Erx', 'Ery', 'Erz'};
%% Run the linearization
T = linearize(mdl, io, options);
T.InputName = {'Wdx', 'Wdy', 'Wdz', 'Wrx', 'Wry', 'Wrz'};
T.OutputName = {'Edx', 'Edy', 'Edz', 'Erx', 'Ery', 'Erz'};
#+end_src
#+begin_src matlab
freqs = logspace(1, 4, 1000);
freqs = logspace(1, 4, 1000);
figure;
for ix = 1:6
figure;
for ix = 1:6
for iy = 1:6
subplot(6, 6, (ix-1)*6 + iy);
hold on;
plot(freqs, abs(squeeze(freqresp(T(ix, iy), freqs, 'Hz'))), 'k-');
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylim([1e-5, 10]);
xlim([freqs(1), freqs(end)]);
if ix < 6
xticklabels({});
end
if iy > 1
yticklabels({});
end
subplot(6, 6, (ix-1)*6 + iy);
hold on;
plot(freqs, abs(squeeze(freqresp(T(ix, iy), freqs, 'Hz'))), 'k-');
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylim([1e-5, 10]);
xlim([freqs(1), freqs(end)]);
if ix < 6
xticklabels({});
end
if iy > 1
yticklabels({});
end
end
end
end
#+end_src
From cite:preumont07_six_axis_singl_stage_activ, one can use the Frobenius norm of the transmissibility matrix to obtain a scalar indicator of the transmissibility performance of the system:
@@ -333,26 +333,26 @@ From cite:preumont07_six_axis_singl_stage_activ, one can use the Frobenius norm
\end{align*}
#+begin_src matlab
freqs = logspace(1, 4, 1000);
freqs = logspace(1, 4, 1000);
T_norm = zeros(length(freqs), 1);
T_norm = zeros(length(freqs), 1);
for i = 1:length(freqs)
for i = 1:length(freqs)
T_norm(i) = sqrt(trace(freqresp(T, freqs(i), 'Hz')*freqresp(T, freqs(i), 'Hz')'));
end
end
#+end_src
And we normalize by a factor $\sqrt{6}$ to obtain a performance metric comparable to the transmissibility of a one-axis isolator:
\[ \Gamma(\omega) = \|\bm{T}(\omega)\| / \sqrt{6} \]
#+begin_src matlab
Gamma = T_norm/sqrt(6);
Gamma = T_norm/sqrt(6);
#+end_src
#+begin_src matlab
figure;
plot(freqs, Gamma)
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
figure;
plot(freqs, Gamma)
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
#+end_src
* Compliance Analysis
@@ -360,101 +360,101 @@ And we normalize by a factor $\sqrt{6}$ to obtain a performance metric comparabl
** Introduction :ignore:
** Matlab Init :noexport:
#+begin_src matlab :tangle no :exports none :results silent :noweb yes :var current_dir=(file-name-directory buffer-file-name)
<<matlab-dir>>
<<matlab-dir>>
#+end_src
#+begin_src matlab :exports none :results silent :noweb yes
<<matlab-init>>
<<matlab-init>>
#+end_src
#+begin_src matlab
simulinkproject('../');
simulinkproject('../');
#+end_src
#+begin_src matlab
open('stewart_platform_model.slx')
open('stewart_platform_model.slx')
#+end_src
** Initialize the Stewart platform
#+begin_src matlab
stewart = initializeStewartPlatform();
stewart = initializeFramesPositions(stewart, 'H', 90e-3, 'MO_B', 45e-3);
stewart = generateGeneralConfiguration(stewart);
stewart = computeJointsPose(stewart);
stewart = initializeStrutDynamics(stewart);
stewart = initializeJointDynamics(stewart, 'type_F', 'universal_p', 'type_M', 'spherical_p');
stewart = initializeCylindricalPlatforms(stewart);
stewart = initializeCylindricalStruts(stewart);
stewart = computeJacobian(stewart);
stewart = initializeStewartPose(stewart);
stewart = initializeInertialSensor(stewart, 'type', 'accelerometer', 'freq', 5e3);
stewart = initializeStewartPlatform();
stewart = initializeFramesPositions(stewart, 'H', 90e-3, 'MO_B', 45e-3);
stewart = generateGeneralConfiguration(stewart);
stewart = computeJointsPose(stewart);
stewart = initializeStrutDynamics(stewart);
stewart = initializeJointDynamics(stewart, 'type_F', 'universal_p', 'type_M', 'spherical_p');
stewart = initializeCylindricalPlatforms(stewart);
stewart = initializeCylindricalStruts(stewart);
stewart = computeJacobian(stewart);
stewart = initializeStewartPose(stewart);
stewart = initializeInertialSensor(stewart, 'type', 'accelerometer', 'freq', 5e3);
#+end_src
We set the rotation point of the ground to be at the same point at frames $\{A\}$ and $\{B\}$.
#+begin_src matlab
ground = initializeGround('type', 'none');
payload = initializePayload('type', 'rigid');
controller = initializeController('type', 'open-loop');
ground = initializeGround('type', 'none');
payload = initializePayload('type', 'rigid');
controller = initializeController('type', 'open-loop');
#+end_src
** Compliance
#+begin_src matlab
%% Options for Linearized
options = linearizeOptions;
options.SampleTime = 0;
%% Options for Linearized
options = linearizeOptions;
options.SampleTime = 0;
%% Name of the Simulink File
mdl = 'stewart_platform_model';
%% Name of the Simulink File
mdl = 'stewart_platform_model';
%% Input/Output definition
clear io; io_i = 1;
io(io_i) = linio([mdl, '/Disturbances/F_ext'], 1, 'openinput'); io_i = io_i + 1; % Base Motion [m, rad]
io(io_i) = linio([mdl, '/Absolute Motion Sensor'], 1, 'openoutput'); io_i = io_i + 1; % Absolute Motion [m, rad]
%% Input/Output definition
clear io; io_i = 1;
io(io_i) = linio([mdl, '/Disturbances/F_ext'], 1, 'openinput'); io_i = io_i + 1; % Base Motion [m, rad]
io(io_i) = linio([mdl, '/Absolute Motion Sensor'], 1, 'openoutput'); io_i = io_i + 1; % Absolute Motion [m, rad]
%% Run the linearization
C = linearize(mdl, io, options);
C.InputName = {'Fdx', 'Fdy', 'Fdz', 'Mdx', 'Mdy', 'Mdz'};
C.OutputName = {'Edx', 'Edy', 'Edz', 'Erx', 'Ery', 'Erz'};
%% Run the linearization
C = linearize(mdl, io, options);
C.InputName = {'Fdx', 'Fdy', 'Fdz', 'Mdx', 'Mdy', 'Mdz'};
C.OutputName = {'Edx', 'Edy', 'Edz', 'Erx', 'Ery', 'Erz'};
#+end_src
#+begin_src matlab
freqs = logspace(1, 4, 1000);
freqs = logspace(1, 4, 1000);
figure;
for ix = 1:6
figure;
for ix = 1:6
for iy = 1:6
subplot(6, 6, (ix-1)*6 + iy);
hold on;
plot(freqs, abs(squeeze(freqresp(C(ix, iy), freqs, 'Hz'))), 'k-');
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylim([1e-10, 1e-3]);
xlim([freqs(1), freqs(end)]);
if ix < 6
xticklabels({});
end
if iy > 1
yticklabels({});
end
subplot(6, 6, (ix-1)*6 + iy);
hold on;
plot(freqs, abs(squeeze(freqresp(C(ix, iy), freqs, 'Hz'))), 'k-');
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
ylim([1e-10, 1e-3]);
xlim([freqs(1), freqs(end)]);
if ix < 6
xticklabels({});
end
if iy > 1
yticklabels({});
end
end
end
end
#+end_src
We can try to use the Frobenius norm to obtain a scalar value representing the 6-dof compliance of the Stewart platform.
#+begin_src matlab
freqs = logspace(1, 4, 1000);
freqs = logspace(1, 4, 1000);
C_norm = zeros(length(freqs), 1);
C_norm = zeros(length(freqs), 1);
for i = 1:length(freqs)
for i = 1:length(freqs)
C_norm(i) = sqrt(trace(freqresp(C, freqs(i), 'Hz')*freqresp(C, freqs(i), 'Hz')'));
end
end
#+end_src
#+begin_src matlab
figure;
plot(freqs, C_norm)
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
figure;
plot(freqs, C_norm)
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
#+end_src
* Functions
@@ -470,20 +470,20 @@ We can try to use the Frobenius norm to obtain a scalar value representing the 6
:UNNUMBERED: t
:END:
#+begin_src matlab
function [T, T_norm, freqs] = computeTransmissibility(args)
% computeTransmissibility -
%
% Syntax: [T, T_norm, freqs] = computeTransmissibility(args)
%
% Inputs:
% - args - Structure with the following fields:
% - plots [true/false] - Should plot the transmissilibty matrix and its Frobenius norm
% - freqs [] - Frequency vector to estimate the Frobenius norm
%
% Outputs:
% - T [6x6 ss] - Transmissibility matrix
% - T_norm [length(freqs)x1] - Frobenius norm of the Transmissibility matrix
% - freqs [length(freqs)x1] - Frequency vector in [Hz]
function [T, T_norm, freqs] = computeTransmissibility(args)
% computeTransmissibility -
%
% Syntax: [T, T_norm, freqs] = computeTransmissibility(args)
%
% Inputs:
% - args - Structure with the following fields:
% - plots [true/false] - Should plot the transmissilibty matrix and its Frobenius norm
% - freqs [] - Frequency vector to estimate the Frobenius norm
%
% Outputs:
% - T [6x6 ss] - Transmissibility matrix
% - T_norm [length(freqs)x1] - Frobenius norm of the Transmissibility matrix
% - freqs [length(freqs)x1] - Frequency vector in [Hz]
#+end_src
*** Optional Parameters
@@ -491,14 +491,14 @@ We can try to use the Frobenius norm to obtain a scalar value representing the 6
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
args.plots logical {mustBeNumericOrLogical} = false
args.freqs double {mustBeNumeric, mustBeNonnegative} = logspace(1,4,1000)
end
arguments
args.plots logical {mustBeNumericOrLogical} = false
args.freqs double {mustBeNumeric, mustBeNonnegative} = logspace(1,4,1000)
end
#+end_src
#+begin_src matlab
freqs = args.freqs;
freqs = args.freqs;
#+end_src
*** Identification of the Transmissibility Matrix
@@ -506,43 +506,43 @@ We can try to use the Frobenius norm to obtain a scalar value representing the 6
:UNNUMBERED: t
:END:
#+begin_src matlab
%% Options for Linearized
options = linearizeOptions;
options.SampleTime = 0;
%% Options for Linearized
options = linearizeOptions;
options.SampleTime = 0;
%% Name of the Simulink File
mdl = 'stewart_platform_model';
%% Name of the Simulink File
mdl = 'stewart_platform_model';
%% Input/Output definition
clear io; io_i = 1;
io(io_i) = linio([mdl, '/Disturbances/D_w'], 1, 'openinput'); io_i = io_i + 1; % Base Motion [m, rad]
io(io_i) = linio([mdl, '/Absolute Motion Sensor'], 1, 'output'); io_i = io_i + 1; % Absolute Motion [m, rad]
%% Input/Output definition
clear io; io_i = 1;
io(io_i) = linio([mdl, '/Disturbances/D_w'], 1, 'openinput'); io_i = io_i + 1; % Base Motion [m, rad]
io(io_i) = linio([mdl, '/Absolute Motion Sensor'], 1, 'output'); io_i = io_i + 1; % Absolute Motion [m, rad]
%% Run the linearization
T = linearize(mdl, io, options);
T.InputName = {'Wdx', 'Wdy', 'Wdz', 'Wrx', 'Wry', 'Wrz'};
T.OutputName = {'Edx', 'Edy', 'Edz', 'Erx', 'Ery', 'Erz'};
%% Run the linearization
T = linearize(mdl, io, options);
T.InputName = {'Wdx', 'Wdy', 'Wdz', 'Wrx', 'Wry', 'Wrz'};
T.OutputName = {'Edx', 'Edy', 'Edz', 'Erx', 'Ery', 'Erz'};
#+end_src
If wanted, the 6x6 transmissibility matrix is plotted.
#+begin_src matlab
p_handle = zeros(6*6,1);
p_handle = zeros(6*6,1);
if args.plots
if args.plots
fig = figure;
for ix = 1:6
for iy = 1:6
p_handle((ix-1)*6 + iy) = subplot(6, 6, (ix-1)*6 + iy);
hold on;
plot(freqs, abs(squeeze(freqresp(T(ix, iy), freqs, 'Hz'))), 'k-');
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
if ix < 6
xticklabels({});
for iy = 1:6
p_handle((ix-1)*6 + iy) = subplot(6, 6, (ix-1)*6 + iy);
hold on;
plot(freqs, abs(squeeze(freqresp(T(ix, iy), freqs, 'Hz'))), 'k-');
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
if ix < 6
xticklabels({});
end
if iy > 1
yticklabels({});
end
end
if iy > 1
yticklabels({});
end
end
end
linkaxes(p_handle, 'xy')
@@ -554,7 +554,7 @@ If wanted, the 6x6 transmissibility matrix is plotted.
han.YLabel.Visible = 'on';
xlabel(han, 'Frequency [Hz]');
ylabel(han, 'Transmissibility [m/m]');
end
end
#+end_src
*** Computation of the Frobenius norm
@@ -562,25 +562,25 @@ If wanted, the 6x6 transmissibility matrix is plotted.
:UNNUMBERED: t
:END:
#+begin_src matlab
T_norm = zeros(length(freqs), 1);
T_norm = zeros(length(freqs), 1);
for i = 1:length(freqs)
for i = 1:length(freqs)
T_norm(i) = sqrt(trace(freqresp(T, freqs(i), 'Hz')*freqresp(T, freqs(i), 'Hz')'));
end
end
#+end_src
#+begin_src matlab
T_norm = T_norm/sqrt(6);
T_norm = T_norm/sqrt(6);
#+end_src
#+begin_src matlab
if args.plots
if args.plots
figure;
plot(freqs, T_norm)
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]');
ylabel('Transmissibility - Frobenius Norm');
end
end
#+end_src
** Compute the Compliance
@@ -595,20 +595,20 @@ If wanted, the 6x6 transmissibility matrix is plotted.
:UNNUMBERED: t
:END:
#+begin_src matlab
function [C, C_norm, freqs] = computeCompliance(args)
% computeCompliance -
%
% Syntax: [C, C_norm, freqs] = computeCompliance(args)
%
% Inputs:
% - args - Structure with the following fields:
% - plots [true/false] - Should plot the transmissilibty matrix and its Frobenius norm
% - freqs [] - Frequency vector to estimate the Frobenius norm
%
% Outputs:
% - C [6x6 ss] - Compliance matrix
% - C_norm [length(freqs)x1] - Frobenius norm of the Compliance matrix
% - freqs [length(freqs)x1] - Frequency vector in [Hz]
function [C, C_norm, freqs] = computeCompliance(args)
% computeCompliance -
%
% Syntax: [C, C_norm, freqs] = computeCompliance(args)
%
% Inputs:
% - args - Structure with the following fields:
% - plots [true/false] - Should plot the transmissilibty matrix and its Frobenius norm
% - freqs [] - Frequency vector to estimate the Frobenius norm
%
% Outputs:
% - C [6x6 ss] - Compliance matrix
% - C_norm [length(freqs)x1] - Frobenius norm of the Compliance matrix
% - freqs [length(freqs)x1] - Frequency vector in [Hz]
#+end_src
*** Optional Parameters
@@ -616,14 +616,14 @@ If wanted, the 6x6 transmissibility matrix is plotted.
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
args.plots logical {mustBeNumericOrLogical} = false
args.freqs double {mustBeNumeric, mustBeNonnegative} = logspace(1,4,1000)
end
arguments
args.plots logical {mustBeNumericOrLogical} = false
args.freqs double {mustBeNumeric, mustBeNonnegative} = logspace(1,4,1000)
end
#+end_src
#+begin_src matlab
freqs = args.freqs;
freqs = args.freqs;
#+end_src
*** Identification of the Compliance Matrix
@@ -631,43 +631,43 @@ If wanted, the 6x6 transmissibility matrix is plotted.
:UNNUMBERED: t
:END:
#+begin_src matlab
%% Options for Linearized
options = linearizeOptions;
options.SampleTime = 0;
%% Options for Linearized
options = linearizeOptions;
options.SampleTime = 0;
%% Name of the Simulink File
mdl = 'stewart_platform_model';
%% Name of the Simulink File
mdl = 'stewart_platform_model';
%% Input/Output definition
clear io; io_i = 1;
io(io_i) = linio([mdl, '/Disturbances/F_ext'], 1, 'openinput'); io_i = io_i + 1; % External forces [N, N*m]
io(io_i) = linio([mdl, '/Absolute Motion Sensor'], 1, 'output'); io_i = io_i + 1; % Absolute Motion [m, rad]
%% Input/Output definition
clear io; io_i = 1;
io(io_i) = linio([mdl, '/Disturbances/F_ext'], 1, 'openinput'); io_i = io_i + 1; % External forces [N, N*m]
io(io_i) = linio([mdl, '/Absolute Motion Sensor'], 1, 'output'); io_i = io_i + 1; % Absolute Motion [m, rad]
%% Run the linearization
C = linearize(mdl, io, options);
C.InputName = {'Fdx', 'Fdy', 'Fdz', 'Mdx', 'Mdy', 'Mdz'};
C.OutputName = {'Edx', 'Edy', 'Edz', 'Erx', 'Ery', 'Erz'};
%% Run the linearization
C = linearize(mdl, io, options);
C.InputName = {'Fdx', 'Fdy', 'Fdz', 'Mdx', 'Mdy', 'Mdz'};
C.OutputName = {'Edx', 'Edy', 'Edz', 'Erx', 'Ery', 'Erz'};
#+end_src
If wanted, the 6x6 transmissibility matrix is plotted.
#+begin_src matlab
p_handle = zeros(6*6,1);
p_handle = zeros(6*6,1);
if args.plots
if args.plots
fig = figure;
for ix = 1:6
for iy = 1:6
p_handle((ix-1)*6 + iy) = subplot(6, 6, (ix-1)*6 + iy);
hold on;
plot(freqs, abs(squeeze(freqresp(C(ix, iy), freqs, 'Hz'))), 'k-');
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
if ix < 6
xticklabels({});
for iy = 1:6
p_handle((ix-1)*6 + iy) = subplot(6, 6, (ix-1)*6 + iy);
hold on;
plot(freqs, abs(squeeze(freqresp(C(ix, iy), freqs, 'Hz'))), 'k-');
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
if ix < 6
xticklabels({});
end
if iy > 1
yticklabels({});
end
end
if iy > 1
yticklabels({});
end
end
end
linkaxes(p_handle, 'xy')
@@ -678,7 +678,7 @@ If wanted, the 6x6 transmissibility matrix is plotted.
han.YLabel.Visible = 'on';
xlabel(han, 'Frequency [Hz]');
ylabel(han, 'Compliance [m/N, rad/(N*m)]');
end
end
#+end_src
*** Computation of the Frobenius norm
@@ -686,21 +686,21 @@ If wanted, the 6x6 transmissibility matrix is plotted.
:UNNUMBERED: t
:END:
#+begin_src matlab
freqs = args.freqs;
freqs = args.freqs;
C_norm = zeros(length(freqs), 1);
C_norm = zeros(length(freqs), 1);
for i = 1:length(freqs)
for i = 1:length(freqs)
C_norm(i) = sqrt(trace(freqresp(C, freqs(i), 'Hz')*freqresp(C, freqs(i), 'Hz')'));
end
end
#+end_src
#+begin_src matlab
if args.plots
if args.plots
figure;
plot(freqs, C_norm)
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Frequency [Hz]');
ylabel('Compliance - Frobenius Norm');
end
end
#+end_src

View File

@@ -234,30 +234,30 @@ This will also gives us the range for which the approximate forward kinematic is
** Matlab Init :noexport:ignore:
#+begin_src matlab :tangle no :exports none :results silent :noweb yes :var current_dir=(file-name-directory buffer-file-name)
<<matlab-dir>>
<<matlab-dir>>
#+end_src
#+begin_src matlab :exports none :results silent :noweb yes
<<matlab-init>>
<<matlab-init>>
#+end_src
#+begin_src matlab :results none :exports none
simulinkproject('../');
simulinkproject('../');
#+end_src
** Stewart architecture definition
We first define some general Stewart architecture.
#+begin_src matlab
stewart = initializeStewartPlatform();
stewart = initializeFramesPositions(stewart, 'H', 90e-3, 'MO_B', 45e-3);
stewart = generateGeneralConfiguration(stewart);
stewart = computeJointsPose(stewart);
stewart = initializeStewartPose(stewart);
stewart = initializeCylindricalPlatforms(stewart);
stewart = initializeCylindricalStruts(stewart);
stewart = initializeStrutDynamics(stewart);
stewart = initializeJointDynamics(stewart);
stewart = computeJacobian(stewart);
stewart = initializeStewartPlatform();
stewart = initializeFramesPositions(stewart, 'H', 90e-3, 'MO_B', 45e-3);
stewart = generateGeneralConfiguration(stewart);
stewart = computeJointsPose(stewart);
stewart = initializeStewartPose(stewart);
stewart = initializeCylindricalPlatforms(stewart);
stewart = initializeCylindricalStruts(stewart);
stewart = initializeStrutDynamics(stewart);
stewart = initializeJointDynamics(stewart);
stewart = computeJacobian(stewart);
#+end_src
** Comparison for "pure" translations
@@ -267,31 +267,31 @@ We compute the approximate and exact required strut stroke to have the wanted mo
The estimate required strut stroke for both the approximate and exact solutions are shown in Figure [[fig:inverse_kinematics_approx_validity_x_translation]].
The relative strut length displacement is shown in Figure [[fig:inverse_kinematics_approx_validity_x_translation_relative]].
#+begin_src matlab
Xrs = logspace(-6, -1, 100); % Wanted X translation of the mobile platform [m]
Xrs = logspace(-6, -1, 100); % Wanted X translation of the mobile platform [m]
Ls_approx = zeros(6, length(Xrs));
Ls_exact = zeros(6, length(Xrs));
Ls_approx = zeros(6, length(Xrs));
Ls_exact = zeros(6, length(Xrs));
for i = 1:length(Xrs)
for i = 1:length(Xrs)
Xr = Xrs(i);
L_approx(:, i) = stewart.kinematics.J*[Xr; 0; 0; 0; 0; 0;];
[~, L_exact(:, i)] = inverseKinematics(stewart, 'AP', [Xr; 0; 0]);
end
end
#+end_src
#+begin_src matlab :exports none
figure;
hold on;
for i = 1:6
figure;
hold on;
for i = 1:6
set(gca,'ColorOrderIndex',i);
plot(Xrs, abs(L_approx(i, :)));
set(gca,'ColorOrderIndex',i);
plot(Xrs, abs(L_exact(i, :)), '--');
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Wanted $x$ displacement [m]');
ylabel('Estimated required stroke');
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Wanted $x$ displacement [m]');
ylabel('Estimated required stroke');
#+end_src
#+HEADER: :tangle no :exports results :results none :noweb yes
@@ -304,15 +304,15 @@ The relative strut length displacement is shown in Figure [[fig:inverse_kinemati
[[file:figs/inverse_kinematics_approx_validity_x_translation.png]]
#+begin_src matlab :exports none
figure;
hold on;
for i = 1:6
figure;
hold on;
for i = 1:6
plot(Xrs, abs(L_approx(i, :) - L_exact(i, :))./abs(L_approx(i, :) + L_exact(i, :)), 'k-');
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Wanted $x$ displacement [m]');
ylabel('Relative Stroke Error');
end
hold off;
set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log');
xlabel('Wanted $x$ displacement [m]');
ylabel('Relative Stroke Error');
#+end_src
#+HEADER: :tangle no :exports results :results none :noweb yes
@@ -349,41 +349,41 @@ This is what is analyzed in this section.
** Matlab Init :noexport:ignore:
#+begin_src matlab :tangle no :exports none :results silent :noweb yes :var current_dir=(file-name-directory buffer-file-name)
<<matlab-dir>>
<<matlab-dir>>
#+end_src
#+begin_src matlab :exports none :results silent :noweb yes
<<matlab-init>>
<<matlab-init>>
#+end_src
#+begin_src matlab :results none :exports none
simulinkproject('../');
simulinkproject('../');
#+end_src
** Stewart architecture definition
Let's first define the Stewart platform architecture that we want to study.
#+begin_src matlab
stewart = initializeStewartPlatform();
stewart = initializeFramesPositions(stewart, 'H', 90e-3, 'MO_B', 45e-3);
stewart = generateGeneralConfiguration(stewart);
stewart = computeJointsPose(stewart);
stewart = initializeStewartPose(stewart);
stewart = initializeCylindricalPlatforms(stewart);
stewart = initializeCylindricalStruts(stewart);
stewart = initializeStrutDynamics(stewart);
stewart = initializeJointDynamics(stewart);
stewart = computeJacobian(stewart);
stewart = initializeStewartPlatform();
stewart = initializeFramesPositions(stewart, 'H', 90e-3, 'MO_B', 45e-3);
stewart = generateGeneralConfiguration(stewart);
stewart = computeJointsPose(stewart);
stewart = initializeStewartPose(stewart);
stewart = initializeCylindricalPlatforms(stewart);
stewart = initializeCylindricalStruts(stewart);
stewart = initializeStrutDynamics(stewart);
stewart = initializeJointDynamics(stewart);
stewart = computeJacobian(stewart);
#+end_src
** Wanted translations and rotations
Let's now define the wanted extreme translations and rotations.
#+begin_src matlab
Tx_max = 50e-6; % Translation [m]
Ty_max = 50e-6; % Translation [m]
Tz_max = 50e-6; % Translation [m]
Rx_max = 30e-6; % Rotation [rad]
Ry_max = 30e-6; % Rotation [rad]
Rz_max = 0; % Rotation [rad]
Tx_max = 50e-6; % Translation [m]
Ty_max = 50e-6; % Translation [m]
Tz_max = 50e-6; % Translation [m]
Rx_max = 30e-6; % Rotation [rad]
Ry_max = 30e-6; % Rotation [rad]
Rz_max = 0; % Rotation [rad]
#+end_src
** Needed stroke for "pure" rotations or translations
@@ -391,17 +391,17 @@ As a first estimation, we estimate the needed actuator stroke for "pure" rotatio
We do that using either the Inverse Kinematic solution or the Jacobian matrix as an approximation.
#+begin_src matlab
LTx = stewart.kinematics.J*[Tx_max 0 0 0 0 0]';
LTy = stewart.kinematics.J*[0 Ty_max 0 0 0 0]';
LTz = stewart.kinematics.J*[0 0 Tz_max 0 0 0]';
LRx = stewart.kinematics.J*[0 0 0 Rx_max 0 0]';
LRy = stewart.kinematics.J*[0 0 0 0 Ry_max 0]';
LRz = stewart.kinematics.J*[0 0 0 0 0 Rz_max]';
LTx = stewart.kinematics.J*[Tx_max 0 0 0 0 0]';
LTy = stewart.kinematics.J*[0 Ty_max 0 0 0 0]';
LTz = stewart.kinematics.J*[0 0 Tz_max 0 0 0]';
LRx = stewart.kinematics.J*[0 0 0 Rx_max 0 0]';
LRy = stewart.kinematics.J*[0 0 0 0 Ry_max 0]';
LRz = stewart.kinematics.J*[0 0 0 0 0 Rz_max]';
#+end_src
The obtain required stroke is:
#+begin_src matlab :results value replace :exports results
ans = sprintf('From %.2g[m] to %.2g[m]: Total stroke = %.1f[um]', min(min([LTx,LTy,LTz,LRx,LRy])), max(max([LTx,LTy,LTz,LRx,LRy])), 1e6*(max(max([LTx,LTy,LTz,LRx,LRy]))-min(min([LTx,LTy,LTz,LRx,LRy]))))
ans = sprintf('From %.2g[m] to %.2g[m]: Total stroke = %.1f[um]', min(min([LTx,LTy,LTz,LRx,LRy])), max(max([LTx,LTy,LTz,LRx,LRy])), 1e6*(max(max([LTx,LTy,LTz,LRx,LRy]))-min(min([LTx,LTy,LTz,LRx,LRy]))))
#+end_src
#+RESULTS:
@@ -416,11 +416,11 @@ To do so, we may estimate the required actuator stroke for all possible combinat
Let's first generate all the possible combination of maximum translation and rotations.
#+begin_src matlab
Ps = [2*(dec2bin(0:5^2-1,5)-'0')-1, zeros(5^2, 1)].*[Tx_max Ty_max Tz_max Rx_max Ry_max Rz_max];
Ps = [2*(dec2bin(0:5^2-1,5)-'0')-1, zeros(5^2, 1)].*[Tx_max Ty_max Tz_max Rx_max Ry_max Rz_max];
#+end_src
#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*)
data2orgtable(Ps, {}, {'*Tx [m]*', '*Ty [m]*', '*Tz [m]*', '*Rx [rad]*', '*Ry [rad]*', '*Rz [rad]*'}, ' %.1e ');
data2orgtable(Ps, {}, {'*Tx [m]*', '*Ty [m]*', '*Tz [m]*', '*Rx [rad]*', '*Ry [rad]*', '*Rz [rad]*'}, ' %.1e ');
#+end_src
#+RESULTS:
@@ -454,17 +454,17 @@ Let's first generate all the possible combination of maximum translation and rot
For all possible combination, we compute the required actuator stroke using the inverse kinematic solution.
#+begin_src matlab
L_min = 0;
L_max = 0;
L_min = 0;
L_max = 0;
for i = 1:size(Ps,1)
for i = 1:size(Ps,1)
Rx = [1 0 0;
0 cos(Ps(i, 4)) -sin(Ps(i, 4));
0 sin(Ps(i, 4)) cos(Ps(i, 4))];
Ry = [ cos(Ps(i, 5)) 0 sin(Ps(i, 5));
0 1 0;
-sin(Ps(i, 5)) 0 cos(Ps(i, 5))];
0 1 0;
-sin(Ps(i, 5)) 0 cos(Ps(i, 5))];
Rz = [cos(Ps(i, 6)) -sin(Ps(i, 6)) 0;
sin(Ps(i, 6)) cos(Ps(i, 6)) 0;
@@ -474,17 +474,17 @@ For all possible combination, we compute the required actuator stroke using the
[~, Ls] = inverseKinematics(stewart, 'AP', Ps(i, 1:3)', 'ARB', ARB);
if min(Ls) < L_min
L_min = min(Ls)
L_min = min(Ls)
end
if max(Ls) > L_max
L_max = max(Ls)
L_max = max(Ls)
end
end
end
#+end_src
We obtain the required actuator stroke:
#+begin_src matlab :results value replace :exports results
ans = sprintf('From %.2g[m] to %.2g[m]: Total stroke = %.1f[um]', L_min, L_max, 1e6*(L_max-L_min))
ans = sprintf('From %.2g[m] to %.2g[m]: Total stroke = %.1f[um]', L_min, L_max, 1e6*(L_max-L_min))
#+end_src
#+RESULTS:
@@ -513,36 +513,36 @@ However, for small displacements, we can use the Jacobian as an approximate solu
** Matlab Init :noexport:ignore:
#+begin_src matlab :tangle no :exports none :results silent :noweb yes :var current_dir=(file-name-directory buffer-file-name)
<<matlab-dir>>
<<matlab-dir>>
#+end_src
#+begin_src matlab :exports none :results silent :noweb yes
<<matlab-init>>
<<matlab-init>>
#+end_src
#+begin_src matlab :results none :exports none
simulinkproject('../');
simulinkproject('../');
#+end_src
** Stewart architecture definition
Let's first define the Stewart platform architecture that we want to study.
#+begin_src matlab
stewart = initializeStewartPlatform();
stewart = initializeFramesPositions(stewart, 'H', 90e-3, 'MO_B', 45e-3);
stewart = generateGeneralConfiguration(stewart);
stewart = computeJointsPose(stewart);
stewart = initializeStewartPose(stewart);
stewart = initializeCylindricalPlatforms(stewart);
stewart = initializeCylindricalStruts(stewart);
stewart = initializeStrutDynamics(stewart);
stewart = initializeJointDynamics(stewart);
stewart = computeJacobian(stewart);
stewart = initializeStewartPlatform();
stewart = initializeFramesPositions(stewart, 'H', 90e-3, 'MO_B', 45e-3);
stewart = generateGeneralConfiguration(stewart);
stewart = computeJointsPose(stewart);
stewart = initializeStewartPose(stewart);
stewart = initializeCylindricalPlatforms(stewart);
stewart = initializeCylindricalStruts(stewart);
stewart = initializeStrutDynamics(stewart);
stewart = initializeJointDynamics(stewart);
stewart = computeJacobian(stewart);
#+end_src
Let's now define the actuator stroke.
#+begin_src matlab
L_min = -50e-6; % [m]
L_max = 50e-6; % [m]
L_min = -50e-6; % [m]
L_max = 50e-6; % [m]
#+end_src
** Pure translations
@@ -557,21 +557,21 @@ To obtain the mobility "volume" attainable by the Stewart platform when it's ori
For each possible value of $(\theta, \phi)$, we compute the maximum radius $r$ attainable with the constraint that the stroke of each actuator should be between =L_min= and =L_max=.
#+begin_src matlab
thetas = linspace(0, pi, 50);
phis = linspace(0, 2*pi, 50);
rs = zeros(length(thetas), length(phis));
thetas = linspace(0, pi, 50);
phis = linspace(0, 2*pi, 50);
rs = zeros(length(thetas), length(phis));
for i = 1:length(thetas)
for i = 1:length(thetas)
for j = 1:length(phis)
Tx = sin(thetas(i))*cos(phis(j));
Ty = sin(thetas(i))*sin(phis(j));
Tz = cos(thetas(i));
Tx = sin(thetas(i))*cos(phis(j));
Ty = sin(thetas(i))*sin(phis(j));
Tz = cos(thetas(i));
dL = stewart.kinematics.J*[Tx; Ty; Tz; 0; 0; 0;]; % dL required for 1m displacement in theta/phi direction
dL = stewart.kinematics.J*[Tx; Ty; Tz; 0; 0; 0;]; % dL required for 1m displacement in theta/phi direction
rs(i, j) = max([dL(dL<0)*L_min; dL(dL>0)*L_max]);
rs(i, j) = max([dL(dL<0)*L_min; dL(dL>0)*L_max]);
end
end
end
#+end_src
@@ -588,13 +588,13 @@ data2orgtable([1e6*L_min, 1e6*L_max, 1e6*(min(min(rs)))], {}, {'=L_min= [$\mu m$
| -50.0 | 50.0 | 31.5 |
#+begin_src matlab :exports none
figure;
plot3(reshape(rs.*(sin(thetas)'*cos(phis)), [1, length(thetas)*length(phis)]), ...
reshape(rs.*(sin(thetas)'*sin(phis)), [1, length(thetas)*length(phis)]), ...
reshape(rs.*(cos(thetas)'*ones(1, length(phis))), [1, length(thetas)*length(phis)]))
xlabel('X Translation [m]');
ylabel('Y Translation [m]');
zlabel('Z Translation [m]');
figure;
plot3(reshape(rs.*(sin(thetas)'*cos(phis)), [1, length(thetas)*length(phis)]), ...
reshape(rs.*(sin(thetas)'*sin(phis)), [1, length(thetas)*length(phis)]), ...
reshape(rs.*(cos(thetas)'*ones(1, length(phis))), [1, length(thetas)*length(phis)]))
xlabel('X Translation [m]');
ylabel('Y Translation [m]');
zlabel('Z Translation [m]');
#+end_src
#+HEADER: :tangle no :exports results :results none :noweb yes
@@ -613,93 +613,93 @@ using this function https://fr.mathworks.com/help/matlab/ref/contour3.html
** Introduction :ignore:
** Matlab Init :noexport:ignore:
#+begin_src matlab :tangle no :exports none :results silent :noweb yes :var current_dir=(file-name-directory buffer-file-name)
<<matlab-dir>>
<<matlab-dir>>
#+end_src
#+begin_src matlab :exports none :results silent :noweb yes
<<matlab-init>>
<<matlab-init>>
#+end_src
#+begin_src matlab :results none :exports none
simulinkproject('../');
simulinkproject('../');
#+end_src
** Estimation with an analytical model
Let's first define the Stewart Platform Geometry.
#+begin_src matlab
stewart = initializeStewartPlatform();
stewart = initializeFramesPositions(stewart, 'H', 90e-3, 'MO_B', 150e-3);
stewart = generateGeneralConfiguration(stewart);
stewart = computeJointsPose(stewart, 'AP', [0; 0; 0]);
stewart = initializeStewartPlatform();
stewart = initializeFramesPositions(stewart, 'H', 90e-3, 'MO_B', 150e-3);
stewart = generateGeneralConfiguration(stewart);
stewart = computeJointsPose(stewart, 'AP', [0; 0; 0]);
As_init = stewart.geometry.As;
As_init = stewart.geometry.As;
#+end_src
#+begin_src matlab
Tx_max = 60e-6; % Translation [m]
Ty_max = 60e-6; % Translation [m]
Tz_max = 60e-6; % Translation [m]
Rx_max = 30e-6; % Rotation [rad]
Ry_max = 30e-6; % Rotation [rad]
Rz_max = 0; % Rotation [rad]
Tx_max = 60e-6; % Translation [m]
Ty_max = 60e-6; % Translation [m]
Tz_max = 60e-6; % Translation [m]
Rx_max = 30e-6; % Rotation [rad]
Ry_max = 30e-6; % Rotation [rad]
Rz_max = 0; % Rotation [rad]
#+end_src
#+begin_src matlab
Ps = [2*(dec2bin(0:5^2-1,5)-'0')-1, zeros(5^2, 1)].*[Tx_max Ty_max Tz_max Rx_max Ry_max Rz_max];
Ps = [2*(dec2bin(0:5^2-1,5)-'0')-1, zeros(5^2, 1)].*[Tx_max Ty_max Tz_max Rx_max Ry_max Rz_max];
#+end_src
#+begin_src matlab
flex_ang = zeros(size(Ps, 1), 6);
Rxs = zeros(size(Ps, 1), 6)
Rys = zeros(size(Ps, 1), 6)
Rzs = zeros(size(Ps, 1), 6)
flex_ang = zeros(size(Ps, 1), 6);
Rxs = zeros(size(Ps, 1), 6)
Rys = zeros(size(Ps, 1), 6)
Rzs = zeros(size(Ps, 1), 6)
for Ps_i = 1:size(Ps, 1)
Rx = [1 0 0;
0 cos(Ps(Ps_i, 4)) -sin(Ps(Ps_i, 4));
0 sin(Ps(Ps_i, 4)) cos(Ps(Ps_i, 4))];
for Ps_i = 1:size(Ps, 1)
Rx = [1 0 0;
0 cos(Ps(Ps_i, 4)) -sin(Ps(Ps_i, 4));
0 sin(Ps(Ps_i, 4)) cos(Ps(Ps_i, 4))];
Ry = [ cos(Ps(Ps_i, 5)) 0 sin(Ps(Ps_i, 5));
0 1 0;
-sin(Ps(Ps_i, 5)) 0 cos(Ps(Ps_i, 5))];
Ry = [ cos(Ps(Ps_i, 5)) 0 sin(Ps(Ps_i, 5));
0 1 0;
-sin(Ps(Ps_i, 5)) 0 cos(Ps(Ps_i, 5))];
Rz = [cos(Ps(Ps_i, 6)) -sin(Ps(Ps_i, 6)) 0;
sin(Ps(Ps_i, 6)) cos(Ps(Ps_i, 6)) 0;
0 0 1];
Rz = [cos(Ps(Ps_i, 6)) -sin(Ps(Ps_i, 6)) 0;
sin(Ps(Ps_i, 6)) cos(Ps(Ps_i, 6)) 0;
0 0 1];
ARB = Rz*Ry*Rx;
ARB = Rz*Ry*Rx;
stewart = computeJointsPose(stewart, 'AP', Ps(Ps_i, 1:3)', 'ARB', ARB);
stewart = computeJointsPose(stewart, 'AP', Ps(Ps_i, 1:3)', 'ARB', ARB);
flex_ang(Ps_i, :) = acos(sum(As_init.*stewart.geometry.As));
for l_i = 1:6
MRf = stewart.platform_M.MRb(:,:,l_i)*(ARB')*(stewart.platform_F.FRa(:,:,l_i)');
Rys(Ps_i, l_i) = atan2(MRf(1,3), sqrt(MRf(1,1)^2 + MRf(2,2)^2));
Rxs(Ps_i, l_i) = atan2(-MRf(2,3)/cos(Rys(Ps_i, l_i)), MRf(3,3)/cos(Rys(Ps_i, l_i)));
Rzs(Ps_i, l_i) = atan2(-MRf(1,2)/cos(Rys(Ps_i, l_i)), MRf(1,1)/cos(Rys(Ps_i, l_i)));
end
end
flex_ang(Ps_i, :) = acos(sum(As_init.*stewart.geometry.As));
for l_i = 1:6
MRf = stewart.platform_M.MRb(:,:,l_i)*(ARB')*(stewart.platform_F.FRa(:,:,l_i)');
Rys(Ps_i, l_i) = atan2(MRf(1,3), sqrt(MRf(1,1)^2 + MRf(2,2)^2));
Rxs(Ps_i, l_i) = atan2(-MRf(2,3)/cos(Rys(Ps_i, l_i)), MRf(3,3)/cos(Rys(Ps_i, l_i)));
Rzs(Ps_i, l_i) = atan2(-MRf(1,2)/cos(Rys(Ps_i, l_i)), MRf(1,1)/cos(Rys(Ps_i, l_i)));
end
end
#+end_src
And the maximum bending of the flexible joints is: (in [mrad])
#+begin_src matlab :results replace value
1e3*max(max(abs(flex_ang)))
1e3*max(max(abs(flex_ang)))
#+end_src
#+RESULTS:
: 1.1704
#+begin_src matlab :results replace value
1e3*max(max(sqrt(Rxs.^2 + Rys.^2)))
1e3*max(max(sqrt(Rxs.^2 + Rys.^2)))
#+end_src
#+RESULTS:
: 0.075063
#+begin_src matlab :results replace value
1e3*max(max(Rzs))
1e3*max(max(Rzs))
#+end_src
#+RESULTS:
@@ -708,89 +708,89 @@ And the maximum bending of the flexible joints is: (in [mrad])
** Estimation using the Simscape Model
*** Model Init
#+begin_src matlab
open('stewart_platform_model.slx')
open('stewart_platform_model.slx')
#+end_src
#+begin_src matlab
stewart = initializeStewartPlatform();
stewart = initializeFramesPositions(stewart, 'H', 90e-3, 'MO_B', 45e-3);
stewart = generateGeneralConfiguration(stewart);
stewart = computeJointsPose(stewart);
stewart = initializeStrutDynamics(stewart);
stewart = initializeJointDynamics(stewart, 'type_F', 'universal_p', 'type_M', 'spherical_p');
stewart = initializeCylindricalPlatforms(stewart);
stewart = initializeCylindricalStruts(stewart);
stewart = computeJacobian(stewart);
stewart = initializeStewartPose(stewart);
stewart = initializeInertialSensor(stewart, 'type', 'accelerometer', 'freq', 5e3);
stewart = initializeStewartPlatform();
stewart = initializeFramesPositions(stewart, 'H', 90e-3, 'MO_B', 45e-3);
stewart = generateGeneralConfiguration(stewart);
stewart = computeJointsPose(stewart);
stewart = initializeStrutDynamics(stewart);
stewart = initializeJointDynamics(stewart, 'type_F', 'universal_p', 'type_M', 'spherical_p');
stewart = initializeCylindricalPlatforms(stewart);
stewart = initializeCylindricalStruts(stewart);
stewart = computeJacobian(stewart);
stewart = initializeStewartPose(stewart);
stewart = initializeInertialSensor(stewart, 'type', 'accelerometer', 'freq', 5e3);
#+end_src
#+begin_src matlab
ground = initializeGround('type', 'rigid');
payload = initializePayload('type', 'none');
controller = initializeController('type', 'open-loop');
disturbances = initializeDisturbances();
references = initializeReferences(stewart);
ground = initializeGround('type', 'rigid');
payload = initializePayload('type', 'none');
controller = initializeController('type', 'open-loop');
disturbances = initializeDisturbances();
references = initializeReferences(stewart);
#+end_src
*** Controller design to be close to the wanted displacement
#+begin_src matlab
%% Name of the Simulink File
mdl = 'stewart_platform_model';
%% Name of the Simulink File
mdl = 'stewart_platform_model';
%% Input/Output definition
clear io; io_i = 1;
io(io_i) = linio([mdl, '/Controller'], 1, 'openinput'); io_i = io_i + 1; % Actuator Force Inputs [N]
io(io_i) = linio([mdl, '/Stewart Platform'], 1, 'openoutput', [], 'dLm'); io_i = io_i + 1; % Relative Displacement Outputs [m]
%% Input/Output definition
clear io; io_i = 1;
io(io_i) = linio([mdl, '/Controller'], 1, 'openinput'); io_i = io_i + 1; % Actuator Force Inputs [N]
io(io_i) = linio([mdl, '/Stewart Platform'], 1, 'openoutput', [], 'dLm'); io_i = io_i + 1; % Relative Displacement Outputs [m]
%% Run the linearization
G = linearize(mdl, io);
G.InputName = {'F1', 'F2', 'F3', 'F4', 'F5', 'F6'};
G.OutputName = {'L1', 'L2', 'L3', 'L4', 'L5', 'L6'};
%% Run the linearization
G = linearize(mdl, io);
G.InputName = {'F1', 'F2', 'F3', 'F4', 'F5', 'F6'};
G.OutputName = {'L1', 'L2', 'L3', 'L4', 'L5', 'L6'};
#+end_src
#+begin_src matlab
wc = 2*pi*30;
Kl = diag(1./diag(abs(freqresp(G, wc)))) * wc/s * 1/(1 + s/3/wc);
wc = 2*pi*30;
Kl = diag(1./diag(abs(freqresp(G, wc)))) * wc/s * 1/(1 + s/3/wc);
#+end_src
#+begin_src matlab
controller = initializeController('type', 'ref-track-L');
controller = initializeController('type', 'ref-track-L');
#+end_src
*** Simulations
#+begin_src matlab
Tx_max = 60e-6; % Translation [m]
Ty_max = 60e-6; % Translation [m]
Tz_max = 60e-6; % Translation [m]
Rx_max = 30e-6; % Rotation [rad]
Ry_max = 30e-6; % Rotation [rad]
Rz_max = 0; % Rotation [rad]
Tx_max = 60e-6; % Translation [m]
Ty_max = 60e-6; % Translation [m]
Tz_max = 60e-6; % Translation [m]
Rx_max = 30e-6; % Rotation [rad]
Ry_max = 30e-6; % Rotation [rad]
Rz_max = 0; % Rotation [rad]
#+end_src
#+begin_src matlab
Ps = [2*(dec2bin(0:5^2-1,5)-'0')-1, zeros(5^2, 1)].*[Tx_max Ty_max Tz_max Rx_max Ry_max Rz_max];
Ps = [2*(dec2bin(0:5^2-1,5)-'0')-1, zeros(5^2, 1)].*[Tx_max Ty_max Tz_max Rx_max Ry_max Rz_max];
#+end_src
#+begin_src matlab
cl_perf = zeros(size(Ps, 1), 1); % Closed loop performance [percentage]
Rs = zeros(size(Ps, 1), 5); % Max Flexor angles for the 6 legs [mrad]
cl_perf = zeros(size(Ps, 1), 1); % Closed loop performance [percentage]
Rs = zeros(size(Ps, 1), 5); % Max Flexor angles for the 6 legs [mrad]
for Ps_i = 1:size(Ps, 1)
fprintf('Experiment %i over %i', Ps_i, size(Ps, 1));
references = initializeReferences(stewart, 't', 0, 'r', Ps(Ps_i, :)');
set_param('stewart_platform_model','StopTime','0.1');
sim('stewart_platform_model');
for Ps_i = 1:size(Ps, 1)
fprintf('Experiment %i over %i', Ps_i, size(Ps, 1));
cl_perf(Ps_i) = 100*max(abs((simout.y.dLm.Data(end, :) - references.rL.Data(:,1,1)')./simout.y.dLm.Data(end, :)));
Rs(Ps_i, :) = [max(abs(1e3*simout.y.Rf.Data(end, 1:2:end))), max(abs(1e3*simout.y.Rf.Data(end, 2:2:end))), max(abs(1e3*simout.y.Rm.Data(end, 1:3:end))), max(abs(1e3*simout.y.Rm.Data(end, 2:3:end))), max(abs(1e3*simout.y.Rm.Data(end, 3:3:end)))]';
end
references = initializeReferences(stewart, 't', 0, 'r', Ps(Ps_i, :)');
set_param('stewart_platform_model','StopTime','0.1');
sim('stewart_platform_model');
cl_perf(Ps_i) = 100*max(abs((simout.y.dLm.Data(end, :) - references.rL.Data(:,1,1)')./simout.y.dLm.Data(end, :)));
Rs(Ps_i, :) = [max(abs(1e3*simout.y.Rf.Data(end, 1:2:end))), max(abs(1e3*simout.y.Rf.Data(end, 2:2:end))), max(abs(1e3*simout.y.Rm.Data(end, 1:3:end))), max(abs(1e3*simout.y.Rm.Data(end, 2:3:end))), max(abs(1e3*simout.y.Rm.Data(end, 3:3:end)))]';
end
#+end_src
Verify that the simulations are all correct:
#+begin_src matlab :results replace value
max(cl_perf)
max(cl_perf)
#+end_src
#+RESULTS:
@@ -798,7 +798,7 @@ Verify that the simulations are all correct:
*** Results
#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*)
data2orgtable(max(Rs)', {'Rx bot', 'Ry bot', 'Rx top', 'Ry top', 'Rz top'}, {'Stroke [mrad]'}, ' %.2f ');
data2orgtable(max(Rs)', {'Rx bot', 'Ry bot', 'Rx top', 'Ry top', 'Rz top'}, {'Stroke [mrad]'}, ' %.2f ');
#+end_src
#+RESULTS:
@@ -826,22 +826,22 @@ This Matlab function is accessible [[file:../src/computeJacobian.m][here]].
:UNNUMBERED: t
:END:
#+begin_src matlab
function [stewart] = computeJacobian(stewart)
% computeJacobian -
%
% Syntax: [stewart] = computeJacobian(stewart)
%
% Inputs:
% - stewart - With at least the following fields:
% - geometry.As [3x6] - The 6 unit vectors for each strut expressed in {A}
% - geometry.Ab [3x6] - The 6 position of the joints bi expressed in {A}
% - actuators.K [6x1] - Total stiffness of the actuators
%
% Outputs:
% - stewart - With the 3 added field:
% - kinematics.J [6x6] - The Jacobian Matrix
% - kinematics.K [6x6] - The Stiffness Matrix
% - kinematics.C [6x6] - The Compliance Matrix
function [stewart] = computeJacobian(stewart)
% computeJacobian -
%
% Syntax: [stewart] = computeJacobian(stewart)
%
% Inputs:
% - stewart - With at least the following fields:
% - geometry.As [3x6] - The 6 unit vectors for each strut expressed in {A}
% - geometry.Ab [3x6] - The 6 position of the joints bi expressed in {A}
% - actuators.K [6x1] - Total stiffness of the actuators
%
% Outputs:
% - stewart - With the 3 added field:
% - kinematics.J [6x6] - The Jacobian Matrix
% - kinematics.K [6x6] - The Stiffness Matrix
% - kinematics.C [6x6] - The Compliance Matrix
#+end_src
*** Check the =stewart= structure elements
@@ -849,14 +849,14 @@ This Matlab function is accessible [[file:../src/computeJacobian.m][here]].
:UNNUMBERED: t
:END:
#+begin_src matlab
assert(isfield(stewart.geometry, 'As'), 'stewart.geometry should have attribute As')
As = stewart.geometry.As;
assert(isfield(stewart.geometry, 'As'), 'stewart.geometry should have attribute As')
As = stewart.geometry.As;
assert(isfield(stewart.geometry, 'Ab'), 'stewart.geometry should have attribute Ab')
Ab = stewart.geometry.Ab;
assert(isfield(stewart.geometry, 'Ab'), 'stewart.geometry should have attribute Ab')
Ab = stewart.geometry.Ab;
assert(isfield(stewart.actuators, 'K'), 'stewart.actuators should have attribute K')
Ki = stewart.actuators.K;
assert(isfield(stewart.actuators, 'K'), 'stewart.actuators should have attribute K')
Ki = stewart.actuators.K;
#+end_src
@@ -865,7 +865,7 @@ This Matlab function is accessible [[file:../src/computeJacobian.m][here]].
:UNNUMBERED: t
:END:
#+begin_src matlab
J = [As' , cross(Ab, As)'];
J = [As' , cross(Ab, As)'];
#+end_src
*** Compute Stiffness Matrix
@@ -873,7 +873,7 @@ This Matlab function is accessible [[file:../src/computeJacobian.m][here]].
:UNNUMBERED: t
:END:
#+begin_src matlab
K = J'*diag(Ki)*J;
K = J'*diag(Ki)*J;
#+end_src
*** Compute Compliance Matrix
@@ -881,7 +881,7 @@ This Matlab function is accessible [[file:../src/computeJacobian.m][here]].
:UNNUMBERED: t
:END:
#+begin_src matlab
C = inv(K);
C = inv(K);
#+end_src
*** Populate the =stewart= structure
@@ -889,9 +889,9 @@ This Matlab function is accessible [[file:../src/computeJacobian.m][here]].
:UNNUMBERED: t
:END:
#+begin_src matlab
stewart.kinematics.J = J;
stewart.kinematics.K = K;
stewart.kinematics.C = C;
stewart.kinematics.J = J;
stewart.kinematics.K = K;
stewart.kinematics.C = C;
#+end_src
@@ -934,23 +934,23 @@ Otherwise, when the limbs' lengths derived yield complex numbers, then the posit
:UNNUMBERED: t
:END:
#+begin_src matlab
function [Li, dLi] = inverseKinematics(stewart, args)
% inverseKinematics - Compute the needed length of each strut to have the wanted position and orientation of {B} with respect to {A}
%
% Syntax: [stewart] = inverseKinematics(stewart)
%
% Inputs:
% - stewart - A structure with the following fields
% - geometry.Aa [3x6] - The positions ai expressed in {A}
% - geometry.Bb [3x6] - The positions bi expressed in {B}
% - geometry.l [6x1] - Length of each strut
% - args - Can have the following fields:
% - AP [3x1] - The wanted position of {B} with respect to {A}
% - ARB [3x3] - The rotation matrix that gives the wanted orientation of {B} with respect to {A}
%
% Outputs:
% - Li [6x1] - The 6 needed length of the struts in [m] to have the wanted pose of {B} w.r.t. {A}
% - dLi [6x1] - The 6 needed displacement of the struts from the initial position in [m] to have the wanted pose of {B} w.r.t. {A}
function [Li, dLi] = inverseKinematics(stewart, args)
% inverseKinematics - Compute the needed length of each strut to have the wanted position and orientation of {B} with respect to {A}
%
% Syntax: [stewart] = inverseKinematics(stewart)
%
% Inputs:
% - stewart - A structure with the following fields
% - geometry.Aa [3x6] - The positions ai expressed in {A}
% - geometry.Bb [3x6] - The positions bi expressed in {B}
% - geometry.l [6x1] - Length of each strut
% - args - Can have the following fields:
% - AP [3x1] - The wanted position of {B} with respect to {A}
% - ARB [3x3] - The rotation matrix that gives the wanted orientation of {B} with respect to {A}
%
% Outputs:
% - Li [6x1] - The 6 needed length of the struts in [m] to have the wanted pose of {B} w.r.t. {A}
% - dLi [6x1] - The 6 needed displacement of the struts from the initial position in [m] to have the wanted pose of {B} w.r.t. {A}
#+end_src
*** Optional Parameters
@@ -958,11 +958,11 @@ Otherwise, when the limbs' lengths derived yield complex numbers, then the posit
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
stewart
args.AP (3,1) double {mustBeNumeric} = zeros(3,1)
args.ARB (3,3) double {mustBeNumeric} = eye(3)
end
arguments
stewart
args.AP (3,1) double {mustBeNumeric} = zeros(3,1)
args.ARB (3,3) double {mustBeNumeric} = eye(3)
end
#+end_src
*** Check the =stewart= structure elements
@@ -970,14 +970,14 @@ Otherwise, when the limbs' lengths derived yield complex numbers, then the posit
:UNNUMBERED: t
:END:
#+begin_src matlab
assert(isfield(stewart.geometry, 'Aa'), 'stewart.geometry should have attribute Aa')
Aa = stewart.geometry.Aa;
assert(isfield(stewart.geometry, 'Aa'), 'stewart.geometry should have attribute Aa')
Aa = stewart.geometry.Aa;
assert(isfield(stewart.geometry, 'Bb'), 'stewart.geometry should have attribute Bb')
Bb = stewart.geometry.Bb;
assert(isfield(stewart.geometry, 'Bb'), 'stewart.geometry should have attribute Bb')
Bb = stewart.geometry.Bb;
assert(isfield(stewart.geometry, 'l'), 'stewart.geometry should have attribute l')
l = stewart.geometry.l;
assert(isfield(stewart.geometry, 'l'), 'stewart.geometry should have attribute l')
l = stewart.geometry.l;
#+end_src
@@ -986,11 +986,11 @@ Otherwise, when the limbs' lengths derived yield complex numbers, then the posit
:UNNUMBERED: t
:END:
#+begin_src matlab
Li = sqrt(args.AP'*args.AP + diag(Bb'*Bb) + diag(Aa'*Aa) - (2*args.AP'*Aa)' + (2*args.AP'*(args.ARB*Bb))' - diag(2*(args.ARB*Bb)'*Aa));
Li = sqrt(args.AP'*args.AP + diag(Bb'*Bb) + diag(Aa'*Aa) - (2*args.AP'*Aa)' + (2*args.AP'*(args.ARB*Bb))' - diag(2*(args.ARB*Bb)'*Aa));
#+end_src
#+begin_src matlab
dLi = Li-l;
dLi = Li-l;
#+end_src
** =forwardKinematicsApprox=: Compute the Approximate Forward Kinematics
@@ -1007,21 +1007,21 @@ This Matlab function is accessible [[file:../src/forwardKinematicsApprox.m][here
:UNNUMBERED: t
:END:
#+begin_src matlab
function [P, R] = forwardKinematicsApprox(stewart, args)
% forwardKinematicsApprox - Computed the approximate pose of {B} with respect to {A} from the length of each strut and using
% the Jacobian Matrix
%
% Syntax: [P, R] = forwardKinematicsApprox(stewart, args)
%
% Inputs:
% - stewart - A structure with the following fields
% - kinematics.J [6x6] - The Jacobian Matrix
% - args - Can have the following fields:
% - dL [6x1] - Displacement of each strut [m]
%
% Outputs:
% - P [3x1] - The estimated position of {B} with respect to {A}
% - R [3x3] - The estimated rotation matrix that gives the orientation of {B} with respect to {A}
function [P, R] = forwardKinematicsApprox(stewart, args)
% forwardKinematicsApprox - Computed the approximate pose of {B} with respect to {A} from the length of each strut and using
% the Jacobian Matrix
%
% Syntax: [P, R] = forwardKinematicsApprox(stewart, args)
%
% Inputs:
% - stewart - A structure with the following fields
% - kinematics.J [6x6] - The Jacobian Matrix
% - args - Can have the following fields:
% - dL [6x1] - Displacement of each strut [m]
%
% Outputs:
% - P [3x1] - The estimated position of {B} with respect to {A}
% - R [3x3] - The estimated rotation matrix that gives the orientation of {B} with respect to {A}
#+end_src
*** Optional Parameters
@@ -1029,10 +1029,10 @@ This Matlab function is accessible [[file:../src/forwardKinematicsApprox.m][here
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
stewart
args.dL (6,1) double {mustBeNumeric} = zeros(6,1)
end
arguments
stewart
args.dL (6,1) double {mustBeNumeric} = zeros(6,1)
end
#+end_src
*** Check the =stewart= structure elements
@@ -1040,8 +1040,8 @@ This Matlab function is accessible [[file:../src/forwardKinematicsApprox.m][here
:UNNUMBERED: t
:END:
#+begin_src matlab
assert(isfield(stewart.kinematics, 'J'), 'stewart.kinematics should have attribute J')
J = stewart.kinematics.J;
assert(isfield(stewart.kinematics, 'J'), 'stewart.kinematics should have attribute J')
J = stewart.kinematics.J;
#+end_src
*** Computation
@@ -1052,26 +1052,26 @@ From a small displacement of each strut $d\bm{\mathcal{L}}$, we can compute the
position and orientation of {B} with respect to {A} using the following formula:
\[ d \bm{\mathcal{X}} = \bm{J}^{-1} d\bm{\mathcal{L}} \]
#+begin_src matlab
X = J\args.dL;
X = J\args.dL;
#+end_src
The position vector corresponds to the first 3 elements.
#+begin_src matlab
P = X(1:3);
P = X(1:3);
#+end_src
The next 3 elements are the orientation of {B} with respect to {A} expressed
using the screw axis.
#+begin_src matlab
theta = norm(X(4:6));
s = X(4:6)/theta;
theta = norm(X(4:6));
s = X(4:6)/theta;
#+end_src
We then compute the corresponding rotation matrix.
#+begin_src matlab
R = [s(1)^2*(1-cos(theta)) + cos(theta) , s(1)*s(2)*(1-cos(theta)) - s(3)*sin(theta), s(1)*s(3)*(1-cos(theta)) + s(2)*sin(theta);
s(2)*s(1)*(1-cos(theta)) + s(3)*sin(theta), s(2)^2*(1-cos(theta)) + cos(theta), s(2)*s(3)*(1-cos(theta)) - s(1)*sin(theta);
s(3)*s(1)*(1-cos(theta)) - s(2)*sin(theta), s(3)*s(2)*(1-cos(theta)) + s(1)*sin(theta), s(3)^2*(1-cos(theta)) + cos(theta)];
R = [s(1)^2*(1-cos(theta)) + cos(theta) , s(1)*s(2)*(1-cos(theta)) - s(3)*sin(theta), s(1)*s(3)*(1-cos(theta)) + s(2)*sin(theta);
s(2)*s(1)*(1-cos(theta)) + s(3)*sin(theta), s(2)^2*(1-cos(theta)) + cos(theta), s(2)*s(3)*(1-cos(theta)) - s(1)*sin(theta);
s(3)*s(1)*(1-cos(theta)) - s(2)*sin(theta), s(3)*s(2)*(1-cos(theta)) + s(1)*sin(theta), s(3)^2*(1-cos(theta)) + cos(theta)];
#+end_src
* Bibliography :ignore:

View File

@@ -45,40 +45,40 @@
#+end_src
#+begin_src matlab
simulinkproject('../');
simulinkproject('../');
#+end_src
#+begin_src matlab
open('stewart_platform_model.slx')
open('stewart_platform_model.slx')
#+end_src
** Identification of the Dynamics
#+begin_src matlab
apa = load('./mat/APA300ML.mat', 'int_xyz', 'int_i', 'n_xyz', 'n_i', 'nodes', 'M', 'K');
flex_joint = load('./mat/flexor_ID16.mat', 'int_xyz', 'int_i', 'n_xyz', 'n_i', 'nodes', 'M', 'K');
apa = load('./mat/APA300ML.mat', 'int_xyz', 'int_i', 'n_xyz', 'n_i', 'nodes', 'M', 'K');
flex_joint = load('./mat/flexor_ID16.mat', 'int_xyz', 'int_i', 'n_xyz', 'n_i', 'nodes', 'M', 'K');
#+end_src
#+begin_src matlab
stewart = initializeStewartPlatform();
stewart = initializeFramesPositions(stewart, 'H', 90e-3, 'MO_B', 175e-3);
stewart = generateGeneralConfiguration(stewart, 'FH', 20e-3, 'MH', 20e-3, 'FR', 228e-3/2, 'MR', 220e-3/2, 'FTh', [-9, 9, 120-9, 120+9, 240-9, 240+9]*(pi/180), 'MTh', [-60+15, 60-15, 60+15, 180-15, 180+15, -60-15]*(pi/180));
stewart = computeJointsPose(stewart);
stewart = initializeFlexibleStrutDynamics(stewart, 'H', 0.03, 'K', apa.K, 'M', apa.M, 'n_xyz', apa.n_xyz, 'xi', 0.1, 'step_file', 'mat/APA300ML.STEP');
stewart = initializeJointDynamics(stewart, 'type_F', 'flexible', 'K_F', flex_joint.K, 'M_F', flex_joint.M, 'n_xyz_F', flex_joint.n_xyz, 'xi_F', 0.1, 'step_file_F', 'mat/flexor_ID16.STEP', 'type_M', 'flexible', 'K_M', flex_joint.K, 'M_M', flex_joint.M, 'n_xyz_M', flex_joint.n_xyz, 'xi_M', 0.1, 'step_file_M', 'mat/flexor_ID16.STEP');
stewart = initializeCylindricalPlatforms(stewart, 'Fpr', 150e-3, 'Mpr', 125e-3);
stewart = initializeCylindricalStruts(stewart, 'type_F', 'none', 'type_M', 'none');
stewart = computeJacobian(stewart);
stewart = initializeStewartPose(stewart);
stewart = initializeInertialSensor(stewart);
stewart = initializeStewartPlatform();
stewart = initializeFramesPositions(stewart, 'H', 90e-3, 'MO_B', 175e-3);
stewart = generateGeneralConfiguration(stewart, 'FH', 20e-3, 'MH', 20e-3, 'FR', 228e-3/2, 'MR', 220e-3/2, 'FTh', [-9, 9, 120-9, 120+9, 240-9, 240+9]*(pi/180), 'MTh', [-60+15, 60-15, 60+15, 180-15, 180+15, -60-15]*(pi/180));
stewart = computeJointsPose(stewart);
stewart = initializeFlexibleStrutDynamics(stewart, 'H', 0.03, 'K', apa.K, 'M', apa.M, 'n_xyz', apa.n_xyz, 'xi', 0.1, 'step_file', 'mat/APA300ML.STEP');
stewart = initializeJointDynamics(stewart, 'type_F', 'flexible', 'K_F', flex_joint.K, 'M_F', flex_joint.M, 'n_xyz_F', flex_joint.n_xyz, 'xi_F', 0.1, 'step_file_F', 'mat/flexor_ID16.STEP', 'type_M', 'flexible', 'K_M', flex_joint.K, 'M_M', flex_joint.M, 'n_xyz_M', flex_joint.n_xyz, 'xi_M', 0.1, 'step_file_M', 'mat/flexor_ID16.STEP');
stewart = initializeCylindricalPlatforms(stewart, 'Fpr', 150e-3, 'Mpr', 125e-3);
stewart = initializeCylindricalStruts(stewart, 'type_F', 'none', 'type_M', 'none');
stewart = computeJacobian(stewart);
stewart = initializeStewartPose(stewart);
stewart = initializeInertialSensor(stewart);
#+end_src
#+begin_src matlab
ground = initializeGround('type', 'none');
payload = initializePayload('type', 'rigid', 'm', 50);
controller = initializeController('type', 'open-loop');
ground = initializeGround('type', 'none');
payload = initializePayload('type', 'rigid', 'm', 50);
controller = initializeController('type', 'open-loop');
#+end_src
#+begin_src matlab
disturbances = initializeDisturbances();
references = initializeReferences(stewart);
disturbances = initializeDisturbances();
references = initializeReferences(stewart);
#+end_src

View File

@@ -65,12 +65,12 @@ This is done using something called "*Configuration Reference*" ([[https://fr.ma
Basically, the configuration is stored in a mat file =conf_simscape.mat= and then loaded in the workspace for it to be accessible to all the simulink models.
It is automatically loaded when the Simulink project is open. It can be loaded manually with the command:
#+begin_src matlab :eval no
load('mat/conf_simscape.mat');
load('mat/conf_simscape.mat');
#+end_src
It is however possible to modify specific parameters just for one simulation using the =set_param= command:
#+begin_src matlab :eval no
set_param(conf_simscape, 'StopTime', 1);
set_param(conf_simscape, 'StopTime', 1);
#+end_src
* Subsystem Reference
@@ -158,29 +158,29 @@ This Matlab function is accessible [[file:../src/initializePayload.m][here]].
:UNNUMBERED: t
:END:
#+begin_src matlab
function [payload] = initializePayload(args)
% initializePayload - Initialize the Payload that can then be used for simulations and analysis
%
% Syntax: [payload] = initializePayload(args)
%
% Inputs:
% - args - Structure with the following fields:
% - type - 'none', 'rigid', 'flexible', 'cartesian'
% - h [1x1] - Height of the CoM of the payload w.r.t {M} [m]
% This also the position where K and C are defined
% - K [6x1] - Stiffness of the Payload [N/m, N/rad]
% - C [6x1] - Damping of the Payload [N/(m/s), N/(rad/s)]
% - m [1x1] - Mass of the Payload [kg]
% - I [3x3] - Inertia matrix for the Payload [kg*m2]
%
% Outputs:
% - payload - Struture with the following properties:
% - type - 1 (none), 2 (rigid), 3 (flexible)
% - h [1x1] - Height of the CoM of the payload w.r.t {M} [m]
% - K [6x1] - Stiffness of the Payload [N/m, N/rad]
% - C [6x1] - Stiffness of the Payload [N/(m/s), N/(rad/s)]
% - m [1x1] - Mass of the Payload [kg]
% - I [3x3] - Inertia matrix for the Payload [kg*m2]
function [payload] = initializePayload(args)
% initializePayload - Initialize the Payload that can then be used for simulations and analysis
%
% Syntax: [payload] = initializePayload(args)
%
% Inputs:
% - args - Structure with the following fields:
% - type - 'none', 'rigid', 'flexible', 'cartesian'
% - h [1x1] - Height of the CoM of the payload w.r.t {M} [m]
% This also the position where K and C are defined
% - K [6x1] - Stiffness of the Payload [N/m, N/rad]
% - C [6x1] - Damping of the Payload [N/(m/s), N/(rad/s)]
% - m [1x1] - Mass of the Payload [kg]
% - I [3x3] - Inertia matrix for the Payload [kg*m2]
%
% Outputs:
% - payload - Struture with the following properties:
% - type - 1 (none), 2 (rigid), 3 (flexible)
% - h [1x1] - Height of the CoM of the payload w.r.t {M} [m]
% - K [6x1] - Stiffness of the Payload [N/m, N/rad]
% - C [6x1] - Stiffness of the Payload [N/(m/s), N/(rad/s)]
% - m [1x1] - Mass of the Payload [kg]
% - I [3x3] - Inertia matrix for the Payload [kg*m2]
#+end_src
*** Optional Parameters
@@ -188,14 +188,14 @@ This Matlab function is accessible [[file:../src/initializePayload.m][here]].
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
args.type char {mustBeMember(args.type,{'none', 'rigid', 'flexible', 'cartesian'})} = 'none'
args.K (6,1) double {mustBeNumeric, mustBeNonnegative} = 1e8*ones(6,1)
args.C (6,1) double {mustBeNumeric, mustBeNonnegative} = 1e1*ones(6,1)
args.h (1,1) double {mustBeNumeric, mustBeNonnegative} = 100e-3
args.m (1,1) double {mustBeNumeric, mustBeNonnegative} = 10
args.I (3,3) double {mustBeNumeric, mustBeNonnegative} = 1*eye(3)
end
arguments
args.type char {mustBeMember(args.type,{'none', 'rigid', 'flexible', 'cartesian'})} = 'none'
args.K (6,1) double {mustBeNumeric, mustBeNonnegative} = 1e8*ones(6,1)
args.C (6,1) double {mustBeNumeric, mustBeNonnegative} = 1e1*ones(6,1)
args.h (1,1) double {mustBeNumeric, mustBeNonnegative} = 100e-3
args.m (1,1) double {mustBeNumeric, mustBeNonnegative} = 10
args.I (3,3) double {mustBeNumeric, mustBeNonnegative} = 1*eye(3)
end
#+end_src
*** Add Payload Type
@@ -203,16 +203,16 @@ This Matlab function is accessible [[file:../src/initializePayload.m][here]].
:UNNUMBERED: t
:END:
#+begin_src matlab
switch args.type
case 'none'
payload.type = 1;
case 'rigid'
payload.type = 2;
case 'flexible'
payload.type = 3;
case 'cartesian'
payload.type = 4;
end
switch args.type
case 'none'
payload.type = 1;
case 'rigid'
payload.type = 2;
case 'flexible'
payload.type = 3;
case 'cartesian'
payload.type = 4;
end
#+end_src
*** Add Stiffness, Damping and Mass properties of the Payload
@@ -220,12 +220,12 @@ This Matlab function is accessible [[file:../src/initializePayload.m][here]].
:UNNUMBERED: t
:END:
#+begin_src matlab
payload.K = args.K;
payload.C = args.C;
payload.m = args.m;
payload.I = args.I;
payload.K = args.K;
payload.C = args.C;
payload.m = args.m;
payload.I = args.I;
payload.h = args.h;
payload.h = args.h;
#+end_src
** Ground
@@ -242,23 +242,23 @@ This Matlab function is accessible [[file:../src/initializeGround.m][here]].
:UNNUMBERED: t
:END:
#+begin_src matlab
function [ground] = initializeGround(args)
% initializeGround - Initialize the Ground that can then be used for simulations and analysis
%
% Syntax: [ground] = initializeGround(args)
%
% Inputs:
% - args - Structure with the following fields:
% - type - 'none', 'solid', 'flexible'
% - rot_point [3x1] - Rotation point for the ground motion [m]
% - K [3x1] - Translation Stiffness of the Ground [N/m]
% - C [3x1] - Translation Damping of the Ground [N/(m/s)]
%
% Outputs:
% - ground - Struture with the following properties:
% - type - 1 (none), 2 (rigid), 3 (flexible)
% - K [3x1] - Translation Stiffness of the Ground [N/m]
% - C [3x1] - Translation Damping of the Ground [N/(m/s)]
function [ground] = initializeGround(args)
% initializeGround - Initialize the Ground that can then be used for simulations and analysis
%
% Syntax: [ground] = initializeGround(args)
%
% Inputs:
% - args - Structure with the following fields:
% - type - 'none', 'solid', 'flexible'
% - rot_point [3x1] - Rotation point for the ground motion [m]
% - K [3x1] - Translation Stiffness of the Ground [N/m]
% - C [3x1] - Translation Damping of the Ground [N/(m/s)]
%
% Outputs:
% - ground - Struture with the following properties:
% - type - 1 (none), 2 (rigid), 3 (flexible)
% - K [3x1] - Translation Stiffness of the Ground [N/m]
% - C [3x1] - Translation Damping of the Ground [N/(m/s)]
#+end_src
*** Optional Parameters
@@ -266,12 +266,12 @@ This Matlab function is accessible [[file:../src/initializeGround.m][here]].
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
arguments
args.type char {mustBeMember(args.type,{'none', 'rigid', 'flexible'})} = 'none'
args.rot_point (3,1) double {mustBeNumeric} = zeros(3,1)
args.K (3,1) double {mustBeNumeric, mustBeNonnegative} = 1e8*ones(3,1)
args.C (3,1) double {mustBeNumeric, mustBeNonnegative} = 1e1*ones(3,1)
end
end
#+end_src
*** Add Ground Type
@@ -279,14 +279,14 @@ This Matlab function is accessible [[file:../src/initializeGround.m][here]].
:UNNUMBERED: t
:END:
#+begin_src matlab
switch args.type
case 'none'
ground.type = 1;
case 'rigid'
ground.type = 2;
case 'flexible'
ground.type = 3;
end
switch args.type
case 'none'
ground.type = 1;
case 'rigid'
ground.type = 2;
case 'flexible'
ground.type = 3;
end
#+end_src
*** Add Stiffness and Damping properties of the Ground
@@ -294,8 +294,8 @@ This Matlab function is accessible [[file:../src/initializeGround.m][here]].
:UNNUMBERED: t
:END:
#+begin_src matlab
ground.K = args.K;
ground.C = args.C;
ground.K = args.K;
ground.C = args.C;
#+end_src
*** Rotation Point
@@ -303,7 +303,7 @@ This Matlab function is accessible [[file:../src/initializeGround.m][here]].
:UNNUMBERED: t
:END:
#+begin_src matlab
ground.rot_point = args.rot_point;
ground.rot_point = args.rot_point;
#+end_src
* Initialize Disturbances
:PROPERTIES:
@@ -318,13 +318,13 @@ This Matlab function is accessible [[file:../src/initializeGround.m][here]].
:UNNUMBERED: t
:END:
#+begin_src matlab
function [disturbances] = initializeDisturbances(args)
% initializeDisturbances - Initialize the disturbances
%
% Syntax: [disturbances] = initializeDisturbances(args)
%
% Inputs:
% - args -
function [disturbances] = initializeDisturbances(args)
% initializeDisturbances - Initialize the disturbances
%
% Syntax: [disturbances] = initializeDisturbances(args)
%
% Inputs:
% - args -
#+end_src
@@ -333,12 +333,12 @@ This Matlab function is accessible [[file:../src/initializeGround.m][here]].
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
arguments
args.Fd double {mustBeNumeric, mustBeReal} = zeros(6,1)
args.Fd_t double {mustBeNumeric, mustBeReal} = 0
args.Dw double {mustBeNumeric, mustBeReal} = zeros(6,1)
args.Dw_t double {mustBeNumeric, mustBeReal} = 0
end
end
#+end_src
@@ -347,7 +347,7 @@ This Matlab function is accessible [[file:../src/initializeGround.m][here]].
:UNNUMBERED: t
:END:
#+begin_src matlab
disturbances = struct();
disturbances = struct();
#+end_src
** Ground Motion
@@ -355,7 +355,7 @@ This Matlab function is accessible [[file:../src/initializeGround.m][here]].
:UNNUMBERED: t
:END:
#+begin_src matlab
disturbances.Dw = timeseries([args.Dw], args.Dw_t);
disturbances.Dw = timeseries([args.Dw], args.Dw_t);
#+end_src
** Direct Forces
@@ -363,7 +363,7 @@ This Matlab function is accessible [[file:../src/initializeGround.m][here]].
:UNNUMBERED: t
:END:
#+begin_src matlab
disturbances.Fd = timeseries([args.Fd], args.Fd_t);
disturbances.Fd = timeseries([args.Fd], args.Fd_t);
#+end_src
* Initialize References
@@ -379,13 +379,13 @@ This Matlab function is accessible [[file:../src/initializeGround.m][here]].
:UNNUMBERED: t
:END:
#+begin_src matlab
function [references] = initializeReferences(stewart, args)
% initializeReferences - Initialize the references
%
% Syntax: [references] = initializeReferences(args)
%
% Inputs:
% - args -
function [references] = initializeReferences(stewart, args)
% initializeReferences - Initialize the references
%
% Syntax: [references] = initializeReferences(args)
%
% Inputs:
% - args -
#+end_src
@@ -394,31 +394,31 @@ This Matlab function is accessible [[file:../src/initializeGround.m][here]].
:UNNUMBERED: t
:END:
#+begin_src matlab
arguments
arguments
stewart
args.t double {mustBeNumeric, mustBeReal} = 0
args.r double {mustBeNumeric, mustBeReal} = zeros(6, 1)
end
end
#+end_src
** Compute the corresponding strut length
#+begin_src matlab
rL = zeros(6, length(args.t));
rL = zeros(6, length(args.t));
for i = 1:length(args.t)
for i = 1:length(args.t)
R = [cos(args.r(6,i)) -sin(args.r(6,i)) 0;
sin(args.r(6,i)) cos(args.r(6,i)) 0;
0 0 1] * ...
[cos(args.r(5,i)) 0 sin(args.r(5,i));
0 1 0;
-sin(args.r(5,i)) 0 cos(args.r(5,i))] * ...
-sin(args.r(5,i)) 0 cos(args.r(5,i))] * ...
[1 0 0;
0 cos(args.r(4,i)) -sin(args.r(4,i));
0 sin(args.r(4,i)) cos(args.r(4,i))];
[Li, dLi] = inverseKinematics(stewart, 'AP', [args.r(1,i); args.r(2,i); args.r(3,i)], 'ARB', R);
rL(:, i) = dLi;
end
[Li, dLi] = inverseKinematics(stewart, 'AP', [args.r(1,i); args.r(2,i); args.r(3,i)], 'ARB', R);
rL(:, i) = dLi;
end
#+end_src
** References
@@ -426,6 +426,6 @@ This Matlab function is accessible [[file:../src/initializeGround.m][here]].
:UNNUMBERED: t
:END:
#+begin_src matlab
references.r = timeseries(args.r, args.t);
references.rL = timeseries(rL, args.t);
references.r = timeseries(args.r, args.t);
references.rL = timeseries(rL, args.t);
#+end_src

View File

@@ -49,30 +49,30 @@ From the [[https://mathworks.com/products/simulink/projects.html][Simulink proje
The project can be opened using the =simulinkproject= function:
#+begin_src matlab
simulinkproject('../');
simulinkproject('../');
#+end_src
When the project opens, a startup script is ran.
The startup script is defined below and is exported to the =project_startup.m= script.
#+begin_src matlab :eval no :tangle ../src/project_startup.m
project = simulinkproject;
projectRoot = project.RootFolder;
project = simulinkproject;
projectRoot = project.RootFolder;
myCacheFolder = fullfile(projectRoot, '.SimulinkCache');
myCodeFolder = fullfile(projectRoot, '.SimulinkCode');
myCacheFolder = fullfile(projectRoot, '.SimulinkCache');
myCodeFolder = fullfile(projectRoot, '.SimulinkCode');
Simulink.fileGenControl('set',...
'CacheFolder', myCacheFolder,...
'CodeGenFolder', myCodeFolder,...
'createDir', true);
Simulink.fileGenControl('set',...
'CacheFolder', myCacheFolder,...
'CodeGenFolder', myCodeFolder,...
'createDir', true);
%% Load the Simscape Configuration
load('mat/conf_simscape.mat');
%% Load the Simscape Configuration
load('mat/conf_simscape.mat');
#+end_src
When the project closes, it runs the =project_shutdown.m= script defined below.
#+begin_src matlab :eval no :tangle ../src/project_shutdown.m
Simulink.fileGenControl('reset');
Simulink.fileGenControl('reset');
#+end_src
The project also permits to automatically add defined folder to the path when the project is opened.

View File

@@ -38,16 +38,16 @@
What causes the coupling from $F_i$ to $X_i$ ?
#+begin_src latex :file coupling.pdf
\begin{tikzpicture}
\node[block] (Jt) at (0, 0) {$J^{-T}$};
\node[block, right= of Jt] (G) {$G$};
\node[block, right= of G] (J) {$J^{-1}$};
\begin{tikzpicture}
\node[block] (Jt) at (0, 0) {$J^{-T}$};
\node[block, right= of Jt] (G) {$G$};
\node[block, right= of G] (J) {$J^{-1}$};
\draw[->] ($(Jt.west)+(-0.8, 0)$) -- (Jt.west) node[above left]{$F_i$};
\draw[->] (Jt.east) -- (G.west) node[above left]{$\tau_i$};
\draw[->] (G.east) -- (J.west) node[above left]{$q_i$};
\draw[->] (J.east) -- ++(0.8, 0) node[above left]{$X_i$};
\end{tikzpicture}
\draw[->] ($(Jt.west)+(-0.8, 0)$) -- (Jt.west) node[above left]{$F_i$};
\draw[->] (Jt.east) -- (G.west) node[above left]{$\tau_i$};
\draw[->] (G.east) -- (J.west) node[above left]{$q_i$};
\draw[->] (J.east) -- ++(0.8, 0) node[above left]{$X_i$};
\end{tikzpicture}
#+end_src
#+name: fig:block_diag_coupling

File diff suppressed because it is too large Load Diff