Matlab Functions used for the NASS Project
+Table of Contents
+ +1 Functions
+ +1.1 computePsdDispl
++This Matlab function is accessible here. +
+ +function [psd_object] = computePsdDispl(sys_data, t_init, n_av) + i_init = find(sys_data.time > t_init, 1); + + han_win = hanning(ceil(length(sys_data.Dx(i_init:end, :))/n_av)); + Fs = 1/sys_data.time(2); + + [pdx, f] = pwelch(sys_data.Dx(i_init:end, :), han_win, [], [], Fs); + [pdy, ~] = pwelch(sys_data.Dy(i_init:end, :), han_win, [], [], Fs); + [pdz, ~] = pwelch(sys_data.Dz(i_init:end, :), han_win, [], [], Fs); + + [prx, ~] = pwelch(sys_data.Rx(i_init:end, :), han_win, [], [], Fs); + [pry, ~] = pwelch(sys_data.Ry(i_init:end, :), han_win, [], [], Fs); + [prz, ~] = pwelch(sys_data.Rz(i_init:end, :), han_win, [], [], Fs); + + psd_object = struct(... + 'f', f, ... + 'dx', pdx, ... + 'dy', pdy, ... + 'dz', pdz, ... + 'rx', prx, ... + 'ry', pry, ... + 'rz', prz); +end ++
1.2 computeSetpoint
++This Matlab function is accessible here. +
+ +function setpoint = computeSetpoint(ty, ry, rz) +%% +setpoint = zeros(6, 1); + +%% Ty +Ty = [1 0 0 0 ; + 0 1 0 ty ; + 0 0 1 0 ; + 0 0 0 1 ]; + +% Tyinv = [1 0 0 0 ; +% 0 1 0 -ty ; +% 0 0 1 0 ; +% 0 0 0 1 ]; + +%% Ry +Ry = [ cos(ry) 0 sin(ry) 0 ; + 0 1 0 0 ; + -sin(ry) 0 cos(ry) 0 ; + 0 0 0 1 ]; + +% TMry = Ty*Ry*Tyinv; + +%% Rz +Rz = [cos(rz) -sin(rz) 0 0 ; + sin(rz) cos(rz) 0 0 ; + 0 0 1 0 ; + 0 0 0 1 ]; + +% TMrz = Ty*TMry*Rz*TMry'*Tyinv; + +%% All stages +% TM = TMrz*TMry*Ty; + +TM = Ty*Ry*Rz; + +[thetax, thetay, thetaz] = RM2angle(TM(1:3, 1:3)); + +setpoint(1:3) = TM(1:3, 4); +setpoint(4:6) = [thetax, thetay, thetaz]; + +%% Custom Functions +function [thetax, thetay, thetaz] = RM2angle(R) + if abs(abs(R(3, 1)) - 1) > 1e-6 % R31 != 1 and R31 != -1 + thetay = -asin(R(3, 1)); + thetax = atan2(R(3, 2)/cos(thetay), R(3, 3)/cos(thetay)); + thetaz = atan2(R(2, 1)/cos(thetay), R(1, 1)/cos(thetay)); + else + thetaz = 0; + if abs(R(3, 1)+1) < 1e-6 % R31 = -1 + thetay = pi/2; + thetax = thetaz + atan2(R(1, 2), R(1, 3)); + else + thetay = -pi/2; + thetax = -thetaz + atan2(-R(1, 2), -R(1, 3)); + end + end +end +end ++
1.3 converErrorBasis
++This Matlab function is accessible here. +
+ +function error_nass = convertErrorBasis(pos, setpoint, ty, ry, rz) +% convertErrorBasis - +% +% Syntax: convertErrorBasis(p_error, ty, ry, rz) +% +% Inputs: +% - p_error - Position error of the sample w.r.t. the granite [m, rad] +% - ty - Measured translation of the Ty stage [m] +% - ry - Measured rotation of the Ry stage [rad] +% - rz - Measured rotation of the Rz stage [rad] +% +% Outputs: +% - P_nass - Position error of the sample w.r.t. the NASS base [m] +% - R_nass - Rotation error of the sample w.r.t. the NASS base [rad] +% +% Example: +% + +%% If line vector => column vector +if size(pos, 2) == 6 + pos = pos'; +end + +if size(setpoint, 2) == 6 + setpoint = setpoint'; +end + +%% Position of the sample in the frame fixed to the Granite +P_granite = [pos(1:3); 1]; % Position [m] +R_granite = [setpoint(1:3); 1]; % Reference [m] + +%% Transformation matrices of the stages +% T-y +TMty = [1 0 0 0 ; + 0 1 0 ty ; + 0 0 1 0 ; + 0 0 0 1 ]; + +% R-y +TMry = [ cos(ry) 0 sin(ry) 0 ; + 0 1 0 0 ; + -sin(ry) 0 cos(ry) 0 ; + 0 0 0 1 ]; + +% R-z +TMrz = [cos(rz) -sin(rz) 0 0 ; + sin(rz) cos(rz) 0 0 ; + 0 0 1 0 ; + 0 0 0 1 ]; + +%% Compute Point coordinates in the new reference fixed to the NASS base +% P_nass = TMrz*TMry*TMty*P_granite; +P_nass = TMrz\TMry\TMty\P_granite; +R_nass = TMrz\TMry\TMty\R_granite; + +dx = R_nass(1)-P_nass(1); +dy = R_nass(2)-P_nass(2); +dz = R_nass(3)-P_nass(3); + +%% Compute new basis vectors linked to the NASS base +% ux_nass = TMrz*TMry*TMty*[1; 0; 0; 0]; +% ux_nass = ux_nass(1:3); +% uy_nass = TMrz*TMry*TMty*[0; 1; 0; 0]; +% uy_nass = uy_nass(1:3); +% uz_nass = TMrz*TMry*TMty*[0; 0; 1; 0]; +% uz_nass = uz_nass(1:3); + +ux_nass = TMrz\TMry\TMty\[1; 0; 0; 0]; +ux_nass = ux_nass(1:3); +uy_nass = TMrz\TMry\TMty\[0; 1; 0; 0]; +uy_nass = uy_nass(1:3); +uz_nass = TMrz\TMry\TMty\[0; 0; 1; 0]; +uz_nass = uz_nass(1:3); + +%% Rotations error w.r.t. granite Frame +% Rotations error w.r.t. granite Frame +rx_nass = pos(4); +ry_nass = pos(5); +rz_nass = pos(6); + +% Rotation matrices of the Sample w.r.t. the Granite +Mrx_error = [1 0 0 ; + 0 cos(-rx_nass) -sin(-rx_nass) ; + 0 sin(-rx_nass) cos(-rx_nass)]; + +Mry_error = [ cos(-ry_nass) 0 sin(-ry_nass) ; + 0 1 0 ; + -sin(-ry_nass) 0 cos(-ry_nass)]; + +Mrz_error = [cos(-rz_nass) -sin(-rz_nass) 0 ; + sin(-rz_nass) cos(-rz_nass) 0 ; + 0 0 1]; + +% Rotation matrix of the Sample w.r.t. the Granite +Mr_error = Mrz_error*Mry_error*Mrx_error; + +%% Use matrix to solve +R = Mr_error/[ux_nass, uy_nass, uz_nass]; % Rotation matrix from NASS base to Sample + +[thetax, thetay, thetaz] = RM2angle(R); + +error_nass = [dx; dy; dz; thetax; thetay; thetaz]; + +%% Custom Functions +function [thetax, thetay, thetaz] = RM2angle(R) + if abs(abs(R(3, 1)) - 1) > 1e-6 % R31 != 1 and R31 != -1 + thetay = -asin(R(3, 1)); + % thetaybis = pi-thetay; + thetax = atan2(R(3, 2)/cos(thetay), R(3, 3)/cos(thetay)); + % thetaxbis = atan2(R(3, 2)/cos(thetaybis), R(3, 3)/cos(thetaybis)); + thetaz = atan2(R(2, 1)/cos(thetay), R(1, 1)/cos(thetay)); + % thetazbis = atan2(R(2, 1)/cos(thetaybis), R(1, 1)/cos(thetaybis)); + else + thetaz = 0; + if abs(R(3, 1)+1) < 1e-6 % R31 = -1 + thetay = pi/2; + thetax = thetaz + atan2(R(1, 2), R(1, 3)); + else + thetay = -pi/2; + thetax = -thetaz + atan2(-R(1, 2), -R(1, 3)); + end + end +end + +end ++
1.4 generateDiagPidControl
++This Matlab function is accessible here. +
+ +function [K] = generateDiagPidControl(G, fs) + %% + pid_opts = pidtuneOptions(... + 'PhaseMargin', 50, ... + 'DesignFocus', 'disturbance-rejection'); + + %% + K = tf(zeros(6)); + + for i = 1:6 + input_name = G.InputName(i); + output_name = G.OutputName(i); + K(i, i) = tf(pidtune(minreal(G(output_name, input_name)), 'PIDF', 2*pi*fs, pid_opts)); + end + + K.InputName = G.OutputName; + K.OutputName = G.InputName; +end ++
1.5 identifyPlant
++This Matlab function is accessible here. +
+ +function [sys] = identifyPlant(opts_param) + %% Default values for opts + opts = struct(); + + %% Populate opts with input parameters + if exist('opts_param','var') + for opt = fieldnames(opts_param)' + opts.(opt{1}) = opts_param.(opt{1}); + end + end + + %% Options for Linearized + options = linearizeOptions; + options.SampleTime = 0; + + %% Name of the Simulink File + mdl = 'sim_nano_station_id'; + + %% Input/Output definition + io(1) = linio([mdl, '/Fn'], 1, 'input'); % Cartesian forces applied by NASS + io(2) = linio([mdl, '/Dw'], 1, 'input'); % Ground Motion + io(3) = linio([mdl, '/Fs'], 1, 'input'); % External forces on the sample + io(4) = linio([mdl, '/Fnl'], 1, 'input'); % Forces applied on the NASS's legs + io(5) = linio([mdl, '/Fd'], 1, 'input'); % Disturbance Forces + io(6) = linio([mdl, '/Dsm'], 1, 'output'); % Displacement of the sample + io(7) = linio([mdl, '/Fnlm'], 1, 'output'); % Force sensor in NASS's legs + io(8) = linio([mdl, '/Dnlm'], 1, 'output'); % Displacement of NASS's legs + io(9) = linio([mdl, '/Es'], 1, 'output'); % Position Error w.r.t. NASS base + io(10) = linio([mdl, '/Vlm'], 1, 'output'); % Measured absolute velocity of the legs + + %% Run the linearization + G = linearize(mdl, io, options); + G.InputName = {'Fnx', 'Fny', 'Fnz', 'Mnx', 'Mny', 'Mnz', ... + 'Dgx', 'Dgy', 'Dgz', ... + 'Fsx', 'Fsy', 'Fsz', 'Msx', 'Msy', 'Msz', ... + 'F1', 'F2', 'F3', 'F4', 'F5', 'F6', ... + 'Frzz', 'Ftyx', 'Ftyz'}; + G.OutputName = {'Dx', 'Dy', 'Dz', 'Rx', 'Ry', 'Rz', ... + 'Fm1', 'Fm2', 'Fm3', 'Fm4', 'Fm5', 'Fm6', ... + 'Dm1', 'Dm2', 'Dm3', 'Dm4', 'Dm5', 'Dm6', ... + 'Edx', 'Edy', 'Edz', 'Erx', 'Ery', 'Erz', ... + 'Vm1', 'Vm2', 'Vm3', 'Vm4', 'Vm5', 'Vm6'}; + + %% Create the sub transfer functions + minreal_tol = sqrt(eps); + % From forces applied in the cartesian frame to displacement of the sample in the cartesian frame + sys.G_cart = minreal(G({'Dx', 'Dy', 'Dz', 'Rx', 'Ry', 'Rz'}, {'Fnx', 'Fny', 'Fnz', 'Mnx', 'Mny', 'Mnz'}), minreal_tol, false); + % From ground motion to Sample displacement + sys.G_gm = minreal(G({'Dx', 'Dy', 'Dz', 'Rx', 'Ry', 'Rz'}, {'Dgx', 'Dgy', 'Dgz'}), minreal_tol, false); + % From direct forces applied on the sample to displacement of the sample + sys.G_fs = minreal(G({'Dx', 'Dy', 'Dz', 'Rx', 'Ry', 'Rz'}, {'Fsx', 'Fsy', 'Fsz', 'Msx', 'Msy', 'Msz'}), minreal_tol, false); + % From forces applied on NASS's legs to force sensor in each leg + sys.G_iff = minreal(G({'Fm1', 'Fm2', 'Fm3', 'Fm4', 'Fm5', 'Fm6'}, {'F1', 'F2', 'F3', 'F4', 'F5', 'F6'}), minreal_tol, false); + % From forces applied on NASS's legs to displacement of each leg + sys.G_dleg = minreal(G({'Dm1', 'Dm2', 'Dm3', 'Dm4', 'Dm5', 'Dm6'}, {'F1', 'F2', 'F3', 'F4', 'F5', 'F6'}), minreal_tol, false); + % From forces/torques applied by the NASS to position error + sys.G_plant = minreal(G({'Edx', 'Edy', 'Edz', 'Erx', 'Ery', 'Erz'}, {'Fnx', 'Fny', 'Fnz', 'Mnx', 'Mny', 'Mnz'}), minreal_tol, false); + % From forces/torques applied by the NASS to velocity of the actuator + sys.G_geoph = minreal(G({'Vm1', 'Vm2', 'Vm3', 'Vm4', 'Vm5', 'Vm6'}, {'F1', 'F2', 'F3', 'F4', 'F5', 'F6'}), minreal_tol, false); + % From various disturbance forces to position error + sys.G_dist = minreal(G({'Dx', 'Dy', 'Dz', 'Rx', 'Ry', 'Rz'}, {'Frzz', 'Ftyx', 'Ftyz'}), minreal_tol, false); + + %% We remove low frequency and high frequency dynamics that are usually unstable + % using =freqsep= is risky as it may change the shape of the transfer functions + % f_min = 0.1; % [Hz] + % f_max = 1e4; % [Hz] + + % [~, sys.G_cart] = freqsep(freqsep(sys.G_cart, 2*pi*f_max), 2*pi*f_min); + % [~, sys.G_gm] = freqsep(freqsep(sys.G_gm, 2*pi*f_max), 2*pi*f_min); + % [~, sys.G_fs] = freqsep(freqsep(sys.G_fs, 2*pi*f_max), 2*pi*f_min); + % [~, sys.G_iff] = freqsep(freqsep(sys.G_iff, 2*pi*f_max), 2*pi*f_min); + % [~, sys.G_dleg] = freqsep(freqsep(sys.G_dleg, 2*pi*f_max), 2*pi*f_min); + % [~, sys.G_plant] = freqsep(freqsep(sys.G_plant, 2*pi*f_max), 2*pi*f_min); + + %% We finally verify that the system is stable + if ~isstable(sys.G_cart) || ~isstable(sys.G_gm) || ~isstable(sys.G_fs) || ~isstable(sys.G_iff) || ~isstable(sys.G_dleg) || ~isstable(sys.G_plant) + warning('One of the identified system is unstable'); + end +end ++
1.6 runSimulation
++This Matlab function is accessible here. +
+ +function [] = runSimulation(sys_name, sys_mass, ctrl_type, act_damp) + %% Load the controller and save it for the simulation + if strcmp(ctrl_type, 'cl') && strcmp(act_damp, 'none') + K_obj = load('./mat/K_fb.mat'); + K = K_obj.(sprintf('K_%s_%s', sys_mass, sys_name)); %#ok + save('./mat/controllers.mat', 'K'); + elseif strcmp(ctrl_type, 'cl') && strcmp(act_damp, 'iff') + K_obj = load('./mat/K_fb_iff.mat'); + K = K_obj.(sprintf('K_%s_%s_iff', sys_mass, sys_name)); %#ok + save('./mat/controllers.mat', 'K'); + elseif strcmp(ctrl_type, 'ol') + K = tf(zeros(6)); %#ok + save('./mat/controllers.mat', 'K'); + else + error('ctrl_type should be cl or ol'); + end + + %% Active Damping + if strcmp(act_damp, 'iff') + K_iff_crit = load('./mat/K_iff_crit.mat'); + K_iff = K_iff_crit.(sprintf('K_iff_%s_%s', sys_mass, sys_name)); %#ok + save('./mat/controllers.mat', 'K_iff', '-append'); + elseif strcmp(act_damp, 'none') + K_iff = tf(zeros(6)); %#ok + save('./mat/controllers.mat', 'K_iff', '-append'); + end + + %% + if strcmp(sys_name, 'pz') + initializeNanoHexapod(struct('actuator', 'piezo')); + elseif strcmp(sys_name, 'vc') + initializeNanoHexapod(struct('actuator', 'lorentz')); + else + error('sys_name should be pz or vc'); + end + + if strcmp(sys_mass, 'light') + initializeSample(struct('mass', 1)); + elseif strcmp(sys_mass, 'heavy') + initializeSample(struct('mass', 50)); + else + error('sys_mass should be light or heavy'); + end + + %% Run the simulation + sim('sim_nano_station_ctrl.slx'); + + %% Split the Dsample matrix into vectors + [Dx, Dy, Dz, Rx, Ry, Rz] = matSplit(Es.Data, 1); %#ok + time = Dsample.Time; %#ok + + %% Save the result + filename = sprintf('sim_%s_%s_%s_%s', sys_mass, sys_name, ctrl_type, act_damp); + save(sprintf('./mat/%s.mat', filename), ... + 'time', 'Dx', 'Dy', 'Dz', 'Rx', 'Ry', 'Rz', 'K'); +end ++
1.7 Inverse Kinematics of the Hexapod
++This Matlab function is accessible here. +
+ +function [L] = inverseKinematicsHexapod(hexapod, AP, ARB) +% inverseKinematicsHexapod - Compute the initial position of each leg to have the wanted Hexapod's position +% +% Syntax: inverseKinematicsHexapod(hexapod, AP, ARB) +% +% Inputs: +% - hexapod - Hexapod object containing the geometry of the hexapod +% - AP - Position vector of point OB expressed in frame {A} in [m] +% - ARB - Rotation Matrix expressed in frame {A} + + % Wanted Length of the hexapod's legs [m] + L = zeros(6, 1); + + for i = 1:length(L) + Bbi = hexapod.pos_top_tranform(i, :)' - 1e-3*[0 ; 0 ; hexapod.TP.thickness+hexapod.Leg.sphere.top+hexapod.SP.thickness.top+hexapod.jacobian]; % [m] + Aai = hexapod.pos_base(i, :)' + 1e-3*[0 ; 0 ; hexapod.BP.thickness+hexapod.Leg.sphere.bottom+hexapod.SP.thickness.bottom-hexapod.h-hexapod.jacobian]; % [m] + + L(i) = sqrt(AP'*AP + Bbi'*Bbi + Aai'*Aai - 2*AP'*Aai + 2*AP'*(ARB*Bbi) - 2*(ARB*Bbi)'*Aai); + end +end ++
1.8 computeReferencePose
++This Matlab function is accessible here. +
+ +function [WTr] = computeReferencePose(Dy, Ry, Rz, Dh, Dn) +% computeReferencePose - Compute the homogeneous transformation matrix corresponding to the wanted pose of the sample +% +% Syntax: [WTr] = computeReferencePose(Dy, Ry, Rz, Dh, Dn) +% +% Inputs: +% - Dy - Reference of the Translation Stage [m] +% - Ry - Reference of the Tilt Stage [rad] +% - Rz - Reference of the Spindle [rad] +% - Dh - Reference of the Micro Hexapod (Pitch, Roll, Yaw angles) [m, m, m, rad, rad, rad] +% - Dn - Reference of the Nano Hexapod [m, m, m, rad, rad, rad] +% +% Outputs: +% - WTr - + + %% Translation Stage + Rty = [1 0 0 0; + 0 1 0 Dy; + 0 0 1 0; + 0 0 0 1]; + + %% Tilt Stage - Pure rotating aligned with Ob + Rry = [ cos(Ry) 0 sin(Ry) 0; + 0 1 0 0; + -sin(Ry) 0 cos(Ry) 0; + 0 0 0 1]; + + %% Spindle - Rotation along the Z axis + Rrz = [cos(Rz) -sin(Rz) 0 0 ; + sin(Rz) cos(Rz) 0 0 ; + 0 0 1 0 ; + 0 0 0 1 ]; + + + %% Micro-Hexapod + Rhx = [1 0 0; + 0 cos(Dh(4)) -sin(Dh(4)); + 0 sin(Dh(4)) cos(Dh(4))]; + + Rhy = [ cos(Dh(5)) 0 sin(Dh(5)); + 0 1 0; + -sin(Dh(5)) 0 cos(Dh(5))]; + + Rhz = [cos(Dh(6)) -sin(Dh(6)) 0; + sin(Dh(6)) cos(Dh(6)) 0; + 0 0 1]; + + Rh = [1 0 0 Dh(1) ; + 0 1 0 Dh(2) ; + 0 0 1 Dh(3) ; + 0 0 0 1 ]; + + Rh(1:3, 1:3) = Rhz*Rhy*Rhx; + + %% Nano-Hexapod + Rnx = [1 0 0; + 0 cos(Dn(4)) -sin(Dn(4)); + 0 sin(Dn(4)) cos(Dn(4))]; + + Rny = [ cos(Dn(5)) 0 sin(Dn(5)); + 0 1 0; + -sin(Dn(5)) 0 cos(Dn(5))]; + + Rnz = [cos(Dn(6)) -sin(Dn(6)) 0; + sin(Dn(6)) cos(Dn(6)) 0; + 0 0 1]; + + Rn = [1 0 0 Dn(1) ; + 0 1 0 Dn(2) ; + 0 0 1 Dn(3) ; + 0 0 0 1 ]; + + Rn(1:3, 1:3) = Rnx*Rny*Rnz; + + %% Total Homogeneous transformation + WTr = Rty*Rry*Rrz*Rh*Rn; +end ++