diff --git a/figs/test_id31_Kiff_bode_plot.pdf b/figs/test_id31_Kiff_bode_plot.pdf new file mode 100644 index 0000000..94352ec Binary files /dev/null and b/figs/test_id31_Kiff_bode_plot.pdf differ diff --git a/figs/test_id31_Kiff_bode_plot.png b/figs/test_id31_Kiff_bode_plot.png new file mode 100644 index 0000000..d199b15 Binary files /dev/null and b/figs/test_id31_Kiff_bode_plot.png differ diff --git a/figs/test_id31_Kiff_loop_gain.pdf b/figs/test_id31_Kiff_loop_gain.pdf new file mode 100644 index 0000000..3b97691 Binary files /dev/null and b/figs/test_id31_Kiff_loop_gain.pdf differ diff --git a/figs/test_id31_Kiff_loop_gain.png b/figs/test_id31_Kiff_loop_gain.png new file mode 100644 index 0000000..9ea9410 Binary files /dev/null and b/figs/test_id31_Kiff_loop_gain.png differ diff --git a/figs/test_id31_Rz_align_correct.pdf b/figs/test_id31_Rz_align_correct.pdf index eb19536..4416422 100644 Binary files a/figs/test_id31_Rz_align_correct.pdf and b/figs/test_id31_Rz_align_correct.pdf differ diff --git a/figs/test_id31_Rz_align_correct.png b/figs/test_id31_Rz_align_correct.png index 45c5ae7..51ebdcc 100644 Binary files a/figs/test_id31_Rz_align_correct.png and b/figs/test_id31_Rz_align_correct.png differ diff --git a/figs/test_id31_Rz_align_error.pdf b/figs/test_id31_Rz_align_error.pdf index a56c007..825a2d5 100644 Binary files a/figs/test_id31_Rz_align_error.pdf and b/figs/test_id31_Rz_align_error.pdf differ diff --git a/figs/test_id31_Rz_align_error.png b/figs/test_id31_Rz_align_error.png index 772c6a7..66fa4ec 100644 Binary files a/figs/test_id31_Rz_align_error.png and b/figs/test_id31_Rz_align_error.png differ diff --git a/figs/test_id31_block_schematic_plant.pdf b/figs/test_id31_block_schematic_plant.pdf new file mode 100644 index 0000000..634f90c Binary files /dev/null and b/figs/test_id31_block_schematic_plant.pdf differ diff --git a/figs/test_id31_block_schematic_plant.png b/figs/test_id31_block_schematic_plant.png new file mode 100644 index 0000000..635c36e Binary files /dev/null and b/figs/test_id31_block_schematic_plant.png differ diff --git a/figs/test_id31_block_schematic_plant.svg b/figs/test_id31_block_schematic_plant.svg new file mode 100644 index 0000000..4b9696f Binary files /dev/null and b/figs/test_id31_block_schematic_plant.svg differ diff --git a/figs/test_id31_comp_all_undamped_damped_plants.pdf b/figs/test_id31_comp_all_undamped_damped_plants.pdf new file mode 100644 index 0000000..88f2056 Binary files /dev/null and b/figs/test_id31_comp_all_undamped_damped_plants.pdf differ diff --git a/figs/test_id31_comp_all_undamped_damped_plants.png b/figs/test_id31_comp_all_undamped_damped_plants.png new file mode 100644 index 0000000..583fba6 Binary files /dev/null and b/figs/test_id31_comp_all_undamped_damped_plants.png differ diff --git a/figs/test_id31_comp_ol_iff_plant_model.pdf b/figs/test_id31_comp_ol_iff_plant_model.pdf new file mode 100644 index 0000000..c21214c Binary files /dev/null and b/figs/test_id31_comp_ol_iff_plant_model.pdf differ diff --git a/figs/test_id31_comp_ol_iff_plant_model.png b/figs/test_id31_comp_ol_iff_plant_model.png new file mode 100644 index 0000000..356bd2f Binary files /dev/null and b/figs/test_id31_comp_ol_iff_plant_model.png differ diff --git a/figs/test_id31_comp_simscape_Vs.pdf b/figs/test_id31_comp_simscape_Vs.pdf new file mode 100644 index 0000000..c4c936a Binary files /dev/null and b/figs/test_id31_comp_simscape_Vs.pdf differ diff --git a/figs/test_id31_comp_simscape_Vs.png b/figs/test_id31_comp_simscape_Vs.png new file mode 100644 index 0000000..1c70865 Binary files /dev/null and b/figs/test_id31_comp_simscape_Vs.png differ diff --git a/figs/test_id31_comp_simscape_hac.pdf b/figs/test_id31_comp_simscape_hac.pdf new file mode 100644 index 0000000..779fb96 Binary files /dev/null and b/figs/test_id31_comp_simscape_hac.pdf differ diff --git a/figs/test_id31_comp_simscape_hac.png b/figs/test_id31_comp_simscape_hac.png new file mode 100644 index 0000000..18bda46 Binary files /dev/null and b/figs/test_id31_comp_simscape_hac.png differ diff --git a/figs/test_id31_comp_simscape_iff_diag_masses.pdf b/figs/test_id31_comp_simscape_iff_diag_masses.pdf index 6a0815e..962969f 100644 Binary files a/figs/test_id31_comp_simscape_iff_diag_masses.pdf and b/figs/test_id31_comp_simscape_iff_diag_masses.pdf differ diff --git a/figs/test_id31_comp_simscape_iff_diag_masses.png b/figs/test_id31_comp_simscape_iff_diag_masses.png index 4e82bf5..705a88a 100644 Binary files a/figs/test_id31_comp_simscape_iff_diag_masses.png and b/figs/test_id31_comp_simscape_iff_diag_masses.png differ diff --git a/figs/test_id31_first_id_iff.pdf b/figs/test_id31_first_id_iff.pdf index 26a8e11..78e0d98 100644 Binary files a/figs/test_id31_first_id_iff.pdf and b/figs/test_id31_first_id_iff.pdf differ diff --git a/figs/test_id31_first_id_iff.png b/figs/test_id31_first_id_iff.png index e7f1403..672beb6 100644 Binary files a/figs/test_id31_first_id_iff.png and b/figs/test_id31_first_id_iff.png differ diff --git a/figs/test_id31_first_id_int.pdf b/figs/test_id31_first_id_int.pdf index ba84424..6d03ee4 100644 Binary files a/figs/test_id31_first_id_int.pdf and b/figs/test_id31_first_id_int.pdf differ diff --git a/figs/test_id31_first_id_int.png b/figs/test_id31_first_id_int.png index f233c07..098d580 100644 Binary files a/figs/test_id31_first_id_int.png and b/figs/test_id31_first_id_int.png differ diff --git a/figs/test_id31_hac_characteristic_loci.pdf b/figs/test_id31_hac_characteristic_loci.pdf new file mode 100644 index 0000000..f45cc46 Binary files /dev/null and b/figs/test_id31_hac_characteristic_loci.pdf differ diff --git a/figs/test_id31_hac_characteristic_loci.png b/figs/test_id31_hac_characteristic_loci.png new file mode 100644 index 0000000..62ca1e0 Binary files /dev/null and b/figs/test_id31_hac_characteristic_loci.png differ diff --git a/figs/test_id31_hac_loop_gain.pdf b/figs/test_id31_hac_loop_gain.pdf new file mode 100644 index 0000000..1cf8a5d Binary files /dev/null and b/figs/test_id31_hac_loop_gain.pdf differ diff --git a/figs/test_id31_hac_loop_gain.png b/figs/test_id31_hac_loop_gain.png new file mode 100644 index 0000000..fbfc250 Binary files /dev/null and b/figs/test_id31_hac_loop_gain.png differ diff --git a/figs/test_id31_hac_plant_effect_mass.pdf b/figs/test_id31_hac_plant_effect_mass.pdf new file mode 100644 index 0000000..3820177 Binary files /dev/null and b/figs/test_id31_hac_plant_effect_mass.pdf differ diff --git a/figs/test_id31_hac_plant_effect_mass.png b/figs/test_id31_hac_plant_effect_mass.png new file mode 100644 index 0000000..8f831a2 Binary files /dev/null and b/figs/test_id31_hac_plant_effect_mass.png differ diff --git a/figs/test_id31_iff_hac_schematic.pdf b/figs/test_id31_iff_hac_schematic.pdf new file mode 100644 index 0000000..71ba3c5 Binary files /dev/null and b/figs/test_id31_iff_hac_schematic.pdf differ diff --git a/figs/test_id31_iff_hac_schematic.png b/figs/test_id31_iff_hac_schematic.png new file mode 100644 index 0000000..30c6d51 Binary files /dev/null and b/figs/test_id31_iff_hac_schematic.png differ diff --git a/figs/test_id31_iff_hac_schematic.svg b/figs/test_id31_iff_hac_schematic.svg new file mode 100644 index 0000000..c6cb240 Binary files /dev/null and b/figs/test_id31_iff_hac_schematic.svg differ diff --git a/figs/test_id31_iff_root_locus_m0.pdf b/figs/test_id31_iff_root_locus_m0.pdf new file mode 100644 index 0000000..dc9de18 Binary files /dev/null and b/figs/test_id31_iff_root_locus_m0.pdf differ diff --git a/figs/test_id31_iff_root_locus_m0.png b/figs/test_id31_iff_root_locus_m0.png new file mode 100644 index 0000000..aabf369 Binary files /dev/null and b/figs/test_id31_iff_root_locus_m0.png differ diff --git a/figs/test_id31_iff_root_locus_m1.pdf b/figs/test_id31_iff_root_locus_m1.pdf new file mode 100644 index 0000000..a70813d Binary files /dev/null and b/figs/test_id31_iff_root_locus_m1.pdf differ diff --git a/figs/test_id31_iff_root_locus_m1.png b/figs/test_id31_iff_root_locus_m1.png new file mode 100644 index 0000000..d5f81d7 Binary files /dev/null and b/figs/test_id31_iff_root_locus_m1.png differ diff --git a/figs/test_id31_iff_root_locus_m2.pdf b/figs/test_id31_iff_root_locus_m2.pdf new file mode 100644 index 0000000..e084f38 Binary files /dev/null and b/figs/test_id31_iff_root_locus_m2.pdf differ diff --git a/figs/test_id31_iff_root_locus_m2.png b/figs/test_id31_iff_root_locus_m2.png new file mode 100644 index 0000000..e76e283 Binary files /dev/null and b/figs/test_id31_iff_root_locus_m2.png differ diff --git a/figs/test_id31_iff_root_locus_m3.pdf b/figs/test_id31_iff_root_locus_m3.pdf new file mode 100644 index 0000000..1797848 Binary files /dev/null and b/figs/test_id31_iff_root_locus_m3.pdf differ diff --git a/figs/test_id31_iff_root_locus_m3.png b/figs/test_id31_iff_root_locus_m3.png new file mode 100644 index 0000000..dd19f9a Binary files /dev/null and b/figs/test_id31_iff_root_locus_m3.png differ diff --git a/figs/test_id31_iff_schematic.pdf b/figs/test_id31_iff_schematic.pdf new file mode 100644 index 0000000..67b7e4d Binary files /dev/null and b/figs/test_id31_iff_schematic.pdf differ diff --git a/figs/test_id31_iff_schematic.png b/figs/test_id31_iff_schematic.png new file mode 100644 index 0000000..4330ff9 Binary files /dev/null and b/figs/test_id31_iff_schematic.png differ diff --git a/figs/test_id31_iff_schematic.svg b/figs/test_id31_iff_schematic.svg new file mode 100644 index 0000000..880cf6a Binary files /dev/null and b/figs/test_id31_iff_schematic.svg differ diff --git a/figs/test_id31_tomo_m0_30rpm_robust_hac_iff_exp_xy.pdf b/figs/test_id31_tomo_m0_30rpm_robust_hac_iff_exp_xy.pdf new file mode 100644 index 0000000..7171f16 Binary files /dev/null and b/figs/test_id31_tomo_m0_30rpm_robust_hac_iff_exp_xy.pdf differ diff --git a/figs/test_id31_tomo_m0_30rpm_robust_hac_iff_exp_xy.png b/figs/test_id31_tomo_m0_30rpm_robust_hac_iff_exp_xy.png new file mode 100644 index 0000000..30d3c74 Binary files /dev/null and b/figs/test_id31_tomo_m0_30rpm_robust_hac_iff_exp_xy.png differ diff --git a/figs/test_id31_tomo_m0_30rpm_robust_hac_iff_exp_yz.pdf b/figs/test_id31_tomo_m0_30rpm_robust_hac_iff_exp_yz.pdf new file mode 100644 index 0000000..0b48dec Binary files /dev/null and b/figs/test_id31_tomo_m0_30rpm_robust_hac_iff_exp_yz.pdf differ diff --git a/figs/test_id31_tomo_m0_30rpm_robust_hac_iff_exp_yz.png b/figs/test_id31_tomo_m0_30rpm_robust_hac_iff_exp_yz.png new file mode 100644 index 0000000..5f0bfc2 Binary files /dev/null and b/figs/test_id31_tomo_m0_30rpm_robust_hac_iff_exp_yz.png differ diff --git a/figs/test_id31_tomo_m0_30rpm_robust_hac_iff_sim_xy.pdf b/figs/test_id31_tomo_m0_30rpm_robust_hac_iff_sim_xy.pdf new file mode 100644 index 0000000..d268808 Binary files /dev/null and b/figs/test_id31_tomo_m0_30rpm_robust_hac_iff_sim_xy.pdf differ diff --git a/figs/test_id31_tomo_m0_30rpm_robust_hac_iff_sim_xy.png b/figs/test_id31_tomo_m0_30rpm_robust_hac_iff_sim_xy.png new file mode 100644 index 0000000..6018d4e Binary files /dev/null and b/figs/test_id31_tomo_m0_30rpm_robust_hac_iff_sim_xy.png differ diff --git a/figs/test_id31_tomo_m0_30rpm_robust_hac_iff_sim_yz.pdf b/figs/test_id31_tomo_m0_30rpm_robust_hac_iff_sim_yz.pdf new file mode 100644 index 0000000..e6a8934 Binary files /dev/null and b/figs/test_id31_tomo_m0_30rpm_robust_hac_iff_sim_yz.pdf differ diff --git a/figs/test_id31_tomo_m0_30rpm_robust_hac_iff_sim_yz.png b/figs/test_id31_tomo_m0_30rpm_robust_hac_iff_sim_yz.png new file mode 100644 index 0000000..619bae0 Binary files /dev/null and b/figs/test_id31_tomo_m0_30rpm_robust_hac_iff_sim_yz.png differ diff --git a/figs/test_id31_tomo_m2_1rpm_robust_hac_iff_fit.pdf b/figs/test_id31_tomo_m2_1rpm_robust_hac_iff_fit.pdf new file mode 100644 index 0000000..b664fae Binary files /dev/null and b/figs/test_id31_tomo_m2_1rpm_robust_hac_iff_fit.pdf differ diff --git a/figs/test_id31_tomo_m2_1rpm_robust_hac_iff_fit.png b/figs/test_id31_tomo_m2_1rpm_robust_hac_iff_fit.png new file mode 100644 index 0000000..466bb40 Binary files /dev/null and b/figs/test_id31_tomo_m2_1rpm_robust_hac_iff_fit.png differ diff --git a/figs/test_id31_tomo_m2_1rpm_robust_hac_iff_fit_removed.pdf b/figs/test_id31_tomo_m2_1rpm_robust_hac_iff_fit_removed.pdf new file mode 100644 index 0000000..baca2a7 Binary files /dev/null and b/figs/test_id31_tomo_m2_1rpm_robust_hac_iff_fit_removed.pdf differ diff --git a/figs/test_id31_tomo_m2_1rpm_robust_hac_iff_fit_removed.png b/figs/test_id31_tomo_m2_1rpm_robust_hac_iff_fit_removed.png new file mode 100644 index 0000000..f239de7 Binary files /dev/null and b/figs/test_id31_tomo_m2_1rpm_robust_hac_iff_fit_removed.png differ diff --git a/matlab/mat/nass_model_conf_simulink.mat b/matlab/mat/nass_model_conf_simulink.mat deleted file mode 100644 index 6d01d43..0000000 Binary files a/matlab/mat/nass_model_conf_simulink.mat and /dev/null differ diff --git a/matlab/mat/nass_model_controller.mat b/matlab/mat/nass_model_controller.mat index 5f8e547..41f1a28 100644 Binary files a/matlab/mat/nass_model_controller.mat and b/matlab/mat/nass_model_controller.mat differ diff --git a/matlab/mat/nass_model_disturbances.mat b/matlab/mat/nass_model_disturbances.mat index 5937cbb..2aabdae 100644 Binary files a/matlab/mat/nass_model_disturbances.mat and b/matlab/mat/nass_model_disturbances.mat differ diff --git a/matlab/mat/nass_model_references.mat b/matlab/mat/nass_model_references.mat index bb82251..948111f 100644 Binary files a/matlab/mat/nass_model_references.mat and b/matlab/mat/nass_model_references.mat differ diff --git a/matlab/mat/test_id31_simscape_open_loop_plants.mat b/matlab/mat/test_id31_simscape_open_loop_plants.mat index 162767b..0772549 100644 Binary files a/matlab/mat/test_id31_simscape_open_loop_plants.mat and b/matlab/mat/test_id31_simscape_open_loop_plants.mat differ diff --git a/matlab/nass_model_id31.slx b/matlab/nass_model_id31.slx index 6baa747..4c4fe65 100644 Binary files a/matlab/nass_model_id31.slx and b/matlab/nass_model_id31.slx differ diff --git a/matlab/subsystems/apa300ml_2dof.slx b/matlab/subsystems/apa300ml_2dof.slx index 6221417..056c64d 100644 Binary files a/matlab/subsystems/apa300ml_2dof.slx and b/matlab/subsystems/apa300ml_2dof.slx differ diff --git a/matlab/subsystems/metrology_lion.slx b/matlab/subsystems/metrology_lion.slx index d2dd069..dee006a 100644 Binary files a/matlab/subsystems/metrology_lion.slx and b/matlab/subsystems/metrology_lion.slx differ diff --git a/test-bench-id31.org b/test-bench-id31.org index c7fd817..cc1db92 100644 --- a/test-bench-id31.org +++ b/test-bench-id31.org @@ -133,14 +133,196 @@ mat_dir = "/home/thomas/mnt/data_id31/nass" - Reflectivity - Diffraction tomography -** TODO [#A] Make a nice schematic with all the signal names +** TODO [#C] Should I speak about higher bandwidth controllers even though they give lower performances? +[[file:backup.org::*High Performance HAC][High Performance HAC]] -- Force sensors -- Encoders -- Interferometers -- Command signal -- Estimated error from external metrology -- ... +** DONE [#A] Copy all necessary .mat files +CLOSED: [2024-11-13 Wed 21:20] + +** DONE [#B] Change the name of the I/O in the Simscape model to match the notations +CLOSED: [2024-11-14 Thu 11:32] + +Fn => Vs + +** TODO [#B] Coherent notation/description of spindle rotation + +- RPM +- rpm +- Wz (deg/s) + +Make a choice, and then adapt all notations. +Also, change the =initializeReferences= to accept the chosen description instead of =period=. + +** TODO [#A] Check why simulation gives worst performances than reality + +- [X] Check if low pass filtering the disturbances resolves the issue + No but it makes the simulation faster! +- [ ] Maybe because the disturbances where estimated at 60RPM and not at 1RPM ! + +** TODO [#A] Where to discuss the necessity of estimated Rz? + +One big advantage of doing the control in the cartesian plane, is that we don't need the estimation of nano-hexapod Rz, therefore we don't need the encoders anymore! + +Maybe this should be done in A6 (simscape-nass). +Here it can be reminded when doing the control in the cartesian frame. + +** TODO [#B] Explain that RMS values are not filtered + +Maybe say that the detectors are integrating the signals (maybe 10ms?). +Then update some RMS values to show that it can be considered better than the values given. + +** TODO [#A] Find specifications for each experiment + +*Specifications for different directions* (Check this [[file:~/Cloud/work-projects/ID31-NASS/documents/work-package-1/work-package-1.pdf][document]].) +- [ ] Add these specifications in CAS plots (RMS values). + +| | Dx | Dy | Dz | Rx | Ry | Rz | +|-------------+----+-------+-------+----+-------+----------| +| RMS | | 30nm | 15nm | | | 250 nrad | +| peak 2 peak | | 200nm | 100nm | | | 1.7 urad | +|-------------+----+-------+-------+----+-------+----------| +| MIM | | 20nm | 10nm | | 2urad | 1urad | + +- [ ] Also check what was used for the uniaxial model and NASS Simscape model + *20nmRMS* from the uniaxial model + +*Tomography*: +- Beam size: 200nm x 100nm +- Keep the PoI in the beam: peak to peak errors of 200nm in Dy and 100nm in Dz +- RMS errors (/ by 6.6) gives 30nmRMS in Dy and 15nmRMS in Dz. +- Ry error <1.7urad, 250nrad RMS + +** TODO [#B] Analyze the observed modes + +Added mode compared to Nano-Hexapod test bench: +- Modes of the metrology spheres (between 600 and 700Hz) + +- *Sphere modes* + To identify this mode, look at the transfer function from actuator to individual interferometers. + It may be possible that this mode does not appear on the bottom interferometers but only on the top one (because of limited stiffness of the top sphere support). + +Look at =2023-08-10_18-32_identify_spurious_modes.mat= + +** TODO [#A] Explain why position error does not converges to zero during tomography experiments + +In closed-loop, the position does not converges to zeros but to a stable position. + +This is due to the limited loop-gain at a frequency corresponding to the eccentricity. + +Even though there is an integrator in the controller, this integrator is in the frame of the struts, and not in the cartesian frame. + +The eccentricity is seen as DC disturbance in the cartesian frame, but at "Wz" frequency in the frame of the strut, hence the non-convergence to zero position. + +- *Before 2023-08-11_11-41_tomography_30rpm_m0.mat*: Static errors +- *After 2023-08-11_14-16_m0_1rpm.mat*: No more static errors + +Simulation: 30rpm experiment with large off-axis errors (to see if it converges to zero) +#+begin_src matlab +%% Tomography experiment +% Sample is not centered with the rotation axis +% This is done by offsetfing the micro-hexapod by 0.9um +P_micro_hexapod = [10e-6; 0; 0]; % [m] + +set_param(mdl, 'StopTime', '3'); + +initializeMicroHexapod('AP', P_micro_hexapod); +initializeSample('type', '0'); + +initializeDisturbances(... + 'Dw_x', true, ... % Ground Motion - X direction + 'Dw_y', true, ... % Ground Motion - Y direction + 'Dw_z', true, ... % Ground Motion - Z direction + 'Fdy_x', false, ... % Translation Stage - X direction + 'Fdy_z', false, ... % Translation Stage - Z direction + 'Frz_x', true, ... % Spindle - X direction + 'Frz_y', true, ... % Spindle - Y direction + 'Frz_z', true); % Spindle - Z direction + +initializeReferences(... + 'Rz_type', 'rotating', ... + 'Rz_period', 360/180, ... % 180deg/s, 30rpm + 'Dh_pos', [P_micro_hexapod; 0; 0; 0]); + +% Closed-Loop Simulation +load('test_id31_K_iff.mat', 'Kiff'); +load('test_id31_K_hac_robust.mat', 'Khac'); +initializeController('type', 'hac-iff'); +sim(mdl); +exp_tomo_cl_m0_Wz180_offset = simout; +#+end_src + +#+begin_src matlab :exports none :results none +%% Measured radial errors of the Spindle +figure; +hold on; +plot(1e6*exp_tomo_cl_m0_Wz180_offset.y.x.Data, 1e6*exp_tomo_cl_m0_Wz180_offset.y.y.Data, 'DisplayName', 'Simulation') +hold off; +xlabel('X displacement [$\mu$m]'); ylabel('Y displacement [$\mu$m]'); +axis equal +% xlim([-1, 1]); ylim([-1, 1]); +% xticks([-1, -0.5, 0, 0.5, 1]); +% yticks([-1, -0.5, 0, 0.5, 1]); +leg = legend('location', 'none', 'FontSize', 8, 'NumColumns', 1); +leg.ItemTokenSize(1) = 15; +#+end_src + +Yes it converges to zero. + +** TODO [#A] Make detailed outline + +- [ ] Where to put *noise budget*? + - Separated (OL, IFF, HAC-IFF)? + - Or all at once? + - It was made specifically for tomography experiments + - Each time, 3 figures (Dy, Dz, Ry) +- [ ] Separate sections for different control strategies? + +*Outline*: +- Short stroke metrology +- Open-Loop plant + - Effect of poor Rz alignment + - Effect of payload mass, effect of rotation +- IFF + - Controller design + - Check robustness + - Estimated damped plant? +- Robust HAC (frame of the struts) + - Damped plants + - Loop Shaping + - Check stability + - Noise budget + +** TODO [#C] Talk about additional delay observed in the plant from u to d (interf) + +Explain that this is due to digital conversions using additional electronics, but this is inducing additional delays. +This was latter resolved by directly decoding the correct digital protocol in the Speedgoat. + +** TODO [#B] Verify sign of plants +SCHEDULED: <2024-11-15 Fri> + +Should be inverse compare to encoder output + +** DONE [#A] Make a nice schematic with all the signal names +CLOSED: [2024-11-15 Fri 15:03] + +Should probably be similar than the one used in A6 report (Simscape with NASS). + +- [X] Make a first schematic with all the signals going in and out of the Speedgoat. + In this figure, there is nothing about control, kinematics, etc... + But it would be nice to represent the micro-station + metrology +- [X] Make other figures for LAC, HAC-LAC, but this time really bloc diagrams + +- Force sensors: $\bm{V}_s = [V_{s1},\ V_{s2},\ V_{s3},\ V_{s4},\ V_{s5},\ V_{s6}]$ +- Encoders: $\bm{d}_e = [d_{e1},\ d_{e2},\ d_{e3},\ d_{e4},\ d_{e5},\ d_{e6}]$ +- Interferometers: $\bm{d} = [d_{1},\ d_{2},\ d_{3},\ d_{4},\ d_{5}]$ +- Command signal: $\bm{u} = [u_1,\ u_2,\ u_3,\ u_4,\ u_5,\ u_6]$ +- Voltage across the piezoelectric stack actuator: $\bm{V}_a = [V_{a1},\ V_{a2},\ V_{a3},\ V_{a4},\ V_{a5},\ V_{a6}]$ +- Motion of the sample measured by external metrology: $\bm{\mathcal{X}} = [D_x,\,D_y,\,D_z,\,R_x,\,R_y,\,R_z]$ +- Motion of the struts measured by external metrology: $\bm{\mathcal{L}} = [\mathcal{L}_1,\,\mathcal{L}_2,\,\mathcal{L}_3,\,\mathcal{L}_4,\,\mathcal{L}_5,\,\mathcal{L}_6]$ +- Error of the sample measured by external metrology: $\bm{\epsilon}_{\mathcal{X}} = [\epsilon_{D_x},\,\epsilon_{D_y},\,\epsilon_{D_z},\,\epsilon_{R_x},\,\epsilon_{R_y},\,\epsilon_{R_z}]$ +- Error of the struts measured by external metrology: $\bm{\epsilon}_{\mathcal{L}} = [\epsilon_{\mathcal{L}_1},\,\epsilon_{\mathcal{L}_2},\,\epsilon_{\mathcal{L}_3},\,\epsilon_{\mathcal{L}_4},\,\epsilon_{\mathcal{L}_5},\,\epsilon_{\mathcal{L}_6}]$ +- Spindle angle setpoint (or encoder): +- Translation stage setpoint: ** DONE [#A] Resolve height issue in Simscape model CLOSED: [2024-11-13 Wed 11:53] @@ -165,12 +347,15 @@ This means that height of nano-hexapod <=> beam is 800 - 530 - 95 = *175mm and n it seems 150mm was used for the metrology jacobian! - [X] If something is change, update the previous Simscape models -** TODO [#B] Should the micro-hexapod position be adjusted to match the experiment +** CANC [#B] Should the micro-hexapod position be adjusted to match the experiment +CLOSED: [2024-11-13 Wed 18:05] +- State "CANC" from "TODO" [2024-11-13 Wed 18:05] After alignment, the micro-hexapod position was *h1tz = -17.72101mm*. I suppose compared to the initial height of 350mm -** TODO [#A] Maybe just need one mass for the first identification +** DONE [#A] Maybe just need one mass for the first identification +CLOSED: [2024-11-13 Wed 18:05] First identification: - compare with Simscape @@ -180,7 +365,7 @@ First identification: - New identification for all masses - Better match with Simscape model! -** QUES [#A] Why now we have minimum phase zero for IFF Plant? +** QUES [#B] Why now we have minimum phase zero for IFF Plant? ** CANC [#C] Find identification where Rz was not taken into account CLOSED: [2024-11-12 Tue 16:03] @@ -207,7 +392,7 @@ The identify dynamics of the nano-hexapod fixed on top of the micro-station was Decentralized Integral Force Feedback is then applied to actively damp the plant in a robust way (Section ref:sec:test_id31_iff). -High authority control is then applied +High authority control is then applied (Section ref:sec:test_id31_iff_hac). #+name: fig:test_id31_micro_station_nano_hexapod #+caption: Picture of the micro-station without the nano-hexapod (\subref{fig:test_id31_micro_station_cables}) and with the nano-hexapod (\subref{fig:test_id31_fixed_nano_hexapod}) @@ -690,6 +875,76 @@ exportFig('figs/test_id31_xy_map_sphere.pdf', 'width', 'half', 'height', 'normal <> ** Introduction :ignore: +- Force sensors: $\bm{V}_s = [V_{s1},\ V_{s2},\ V_{s3},\ V_{s4},\ V_{s5},\ V_{s6}]$ +- Encoders: $\bm{d}_e = [d_{e1},\ d_{e2},\ d_{e3},\ d_{e4},\ d_{e5},\ d_{e6}]$ +- Interferometers: $\bm{d} = [d_{1},\ d_{2},\ d_{3},\ d_{4},\ d_{5}]$ +- Command signal: $\bm{u} = [u_1,\ u_2,\ u_3,\ u_4,\ u_5,\ u_6]$ +- Voltage across the piezoelectric stack actuator: $\bm{V}_a = [V_{a1},\ V_{a2},\ V_{a3},\ V_{a4},\ V_{a5},\ V_{a6}]$ +- Motion of the sample measured by external metrology: $\bm{y}_\mathcal{X} = [D_x,\,D_y,\,D_z,\,R_x,\,R_y,\,R_z]$ +# - Sample motion expressed in the nano-hexapod frame: $\bm{\mathcal{X}} = [\epsilon_{D_x},\,\epsilon_{D_y},\,\epsilon_{D_z},\,\epsilon_{R_x},\,\epsilon_{R_y},\,\epsilon_{R_z}]$ +# - Motion of the struts measured by external metrology: $\bm{\mathcal{L}} = [\mathcal{L}_1,\,\mathcal{L}_2,\,\mathcal{L}_3,\,\mathcal{L}_4,\,\mathcal{L}_5,\,\mathcal{L}_6]$ +- Error of the sample measured by external metrology: $\bm{\epsilon\mathcal{X}} = [\epsilon_{D_x},\,\epsilon_{D_y},\,\epsilon_{D_z},\,\epsilon_{R_x},\,\epsilon_{R_y},\,\epsilon_{R_z}]$ +- Error of the struts measured by external metrology: $\bm{\epsilon\mathcal{L}} = [\epsilon_{\mathcal{L}_1},\,\epsilon_{\mathcal{L}_2},\,\epsilon_{\mathcal{L}_3},\,\epsilon_{\mathcal{L}_4},\,\epsilon_{\mathcal{L}_5},\,\epsilon_{\mathcal{L}_6}]$ +- Spindle angle setpoint (or encoder): $r_{R_z}$ +- Translation stage setpoint: $r_{D_y}$ +- Tilt stage setpoint: $r_{R_y}$ + +#+begin_src latex :file test_id31_block_schematic_plant.pdf +\begin{tikzpicture} + % Blocs + \node[block={2.0cm}{1.0cm}] (metrology) {Metrology}; + \node[block={2.0cm}{2.0cm}, below=0.1 of metrology, align=center] (nhexa) {Nano\\Hexapod}; + \node[block={4.0cm}{1.5cm}, below=0.1 of nhexa, align=center] (ustation) {Micro\\Station}; + + \coordinate[] (inputVa) at ($(nhexa.south west)!0.5!(nhexa.north west)$); + \coordinate[] (outputVs) at ($(nhexa.south east)!0.3!(nhexa.north east)$); + \coordinate[] (outputde) at ($(nhexa.south east)!0.7!(nhexa.north east)$); + + \coordinate[] (outputDy) at ($(ustation.south east)!0.1!(ustation.north east)$); + \coordinate[] (outputRy) at ($(ustation.south east)!0.5!(ustation.north east)$); + \coordinate[] (outputRz) at ($(ustation.south east)!0.9!(ustation.north east)$); + + \node[block={1.0cm}{1.0cm}, left=0.8 of inputVa] (pd200) {PD200}; + \node[block={1.0cm}{1.0cm}, right=0.8 of outputde] (Rz_kinematics) {$\bm{J}_{R_z}^{-1}$}; + \node[block={2.0cm}{2.0cm}, right=2.2 of ustation, align=center] (ustation_kinematics) {Compute\\Reference\\Position}; + \node[block={2.0cm}{2.0cm}, right=0.8 of ustation_kinematics, align=center] (compute_error) {Compute\\Error\\Position}; + \node[block={2.0cm}{2.0cm}, above=0.8 of compute_error, align=center] (compute_pos) {Compute\\Sample\\Position}; + \node[block={1.0cm}{1.0cm}, right=0.8 of compute_error] (hexa_jacobian) {$\bm{J}$}; + \node[block={1.0cm}{1.0cm}, right=0.8 of metrology] (metrology_kinematics) {$\bm{J}_d^{-1}$}; + + \coordinate[] (inputMetrology) at ($(compute_error.north east)!0.3!(compute_error.north west)$); + \coordinate[] (inputRz) at ($(compute_error.north east)!0.7!(compute_error.north west)$); + + \node[addb={+}{}{}{}{}, right=0.4 of Rz_kinematics] (addRz) {}; + \draw[->] (Rz_kinematics.east) -- (addRz.west); + \draw[->] (outputRz-|addRz)node[branch]{} -- (addRz.south); + + \draw[->] (outputDy) node[above right]{$r_{D_y}$} -- (outputDy-|ustation_kinematics.west); + \draw[->] (outputRy) node[above right]{$r_{R_y}$} -- (outputRy-|ustation_kinematics.west); + \draw[->] (outputRz) node[above right]{$r_{R_z}$} -- (outputRz-|ustation_kinematics.west); + + \draw[->] (outputVs) -- ++(0.8, 0) node[above left]{$\bm{V}_s$}; + + \draw[->] (metrology.east) -- (metrology_kinematics.west) node[above left]{$\bm{d}$}; + + \draw[->] (metrology_kinematics.east)node[above right]{$[D_x,\,D_y,\,D_z,\,R_x,\,R_y]$} -- (compute_pos.west|-metrology_kinematics); + \draw[->] (addRz.east)node[above right]{$R_z$} -- (compute_pos.west|-addRz); + \draw[->] (compute_pos.south)node -- (compute_error.north)node[above right]{$\bm{y}_{\mathcal{X}}$}; + + \draw[->] (outputde) -- (Rz_kinematics.west) node[above left]{$\bm{d}_{e}$}; + \draw[->] (ustation_kinematics.east) -- (compute_error.west) node[above left]{$\bm{r}_{\mathcal{X}}$}; + \draw[->] (compute_error.east) -- (hexa_jacobian.west) node[above left]{$\bm{\epsilon\mathcal{X}}$}; + \draw[->] (hexa_jacobian.east) -- ++(0.8, 0) node[above left]{$\bm{\epsilon\mathcal{L}}$}; + \draw[->] (pd200.east) -- (inputVa) node[above left]{$\bm{V}_a$}; + \draw[<-] (pd200.west) -- ++(-0.8, 0) node[above right]{$\bm{u}$}; +\end{tikzpicture} +#+end_src + +#+name: fig:test_id31_block_schematic_plant +#+caption: Schematic of the +#+RESULTS: +[[file:figs/test_id31_block_schematic_plant.png]] + ** Matlab Init :noexport:ignore: #+begin_src matlab %% test_id31_2_open_loop_plant.m @@ -727,7 +982,11 @@ The model dynamics is also identified in the same conditions. A first comparison between the model and the measured dynamics is done in Figure ref:fig:test_id31_first_id. A good match can be observed for the diagonal dynamics (except the high frequency modes which are not modeled). -However, the coupling for the transfer function from command signals $\mathbf{u}$ to estimated strut motion from the external metrology $e\mathbf{\mathcal{L}}$ is larger than expected (Figure ref:fig:test_id31_first_id_int). +However, the coupling for the transfer function from command signals $\bm{u}$ to estimated strut motion from the external metrology $e\bm{\mathcal{L}}$ is larger than expected (Figure ref:fig:test_id31_first_id_int). + +The experimental time delay estimated from the FRF (Figure ref:fig:test_id31_first_id_int) is larger than expected. +After investigation, it was found that the additional delay was due to digital processing unit[fn:3] that was used to read the interferometers in the Speedgoat. +This issue was later solved. #+begin_src matlab %% Identify the plant dynamics using the Simscape model @@ -753,14 +1012,14 @@ initializeReferences(); % Input/Output definition clear io; io_i = 1; -io(io_i) = linio([mdl, '/Controller'], 1, 'openinput'); io_i = io_i + 1; % Actuator Inputs -io(io_i) = linio([mdl, '/Micro-Station'], 3, 'openoutput', [], 'Fnlm'); io_i = io_i + 1; % Force Sensors -io(io_i) = linio([mdl, '/Tracking Error'], 1, 'openoutput', [], 'EdL'); io_i = io_i + 1; % Position Errors +io(io_i) = linio([mdl, '/Controller'], 1, 'openinput'); io_i = io_i + 1; % Actuator Inputs [V] +io(io_i) = linio([mdl, '/Micro-Station'], 3, 'openoutput', [], 'Vs'); io_i = io_i + 1; % Force Sensors voltages [V] +io(io_i) = linio([mdl, '/Tracking Error'], 1, 'openoutput', [], 'EdL'); io_i = io_i + 1; % Position Errors [m] % With no payload -Gm = linearize(mdl, io); +Gm = exp(-1e-4*s)*linearize(mdl, io); Gm.InputName = {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'}; -Gm.OutputName = {'Fn1', 'Fn2', 'Fn3', 'Fn4', 'Fn5', 'Fn6', ... +Gm.OutputName = {'Vs1', 'Vs2', 'Vs3', 'Vs4', 'Vs5', 'Vs6', ... 'eL1', 'eL2', 'eL3', 'eL4', 'eL5', 'eL6'}; #+end_src @@ -770,30 +1029,21 @@ Gm.OutputName = {'Fn1', 'Fn2', 'Fn3', 'Fn4', 'Fn5', 'Fn6', ... % Load identification data data = load('2023-08-08_16-17_ol_plant_m0_Wz0.mat'); -% Sampling Time [s] -Ts = 1e-4; - -% Hannning Windows +% Frequency analysis parameters +Ts = 1e-4; % Sampling Time [s] Nfft = floor(2.0/Ts); win = hanning(Nfft); Noverlap = floor(Nfft/2); - -% And we get the frequency vector [~, f] = tfestimate(data.uL1.id_plant, data.uL1.e_L1, win, Noverlap, Nfft, 1/Ts); -% IFF Plant (transfer function from u to taum) -G_iff = zeros(length(f), 6, 6); -for i_strut = 1:6 - eL = [data.(sprintf("uL%i", i_strut)).Vs1 ; data.(sprintf("uL%i", i_strut)).Vs2 ; data.(sprintf("uL%i", i_strut)).Vs3 ; data.(sprintf("uL%i", i_strut)).Vs4 ; data.(sprintf("uL%i", i_strut)).Vs5 ; data.(sprintf("uL%i", i_strut)).Vs6]'; - - G_iff(:,:,i_strut) = tfestimate(data.(sprintf("uL%i", i_strut)).id_plant, eL, win, Noverlap, Nfft, 1/Ts); -end - -% INT Plant (transfer function from u to eL) -G_int = zeros(length(f), 6, 6); +G_iff = zeros(length(f), 6, 6); % Force sensor outputs +G_int = zeros(length(f), 6, 6); % Estimated strut errors for i_strut = 1:6 + Vs = [data.(sprintf("uL%i", i_strut)).Vs1 ; data.(sprintf("uL%i", i_strut)).Vs2 ; data.(sprintf("uL%i", i_strut)).Vs3 ; data.(sprintf("uL%i", i_strut)).Vs4 ; data.(sprintf("uL%i", i_strut)).Vs5 ; data.(sprintf("uL%i", i_strut)).Vs6]'; eL = [data.(sprintf("uL%i", i_strut)).e_L1 ; data.(sprintf("uL%i", i_strut)).e_L2 ; data.(sprintf("uL%i", i_strut)).e_L3 ; data.(sprintf("uL%i", i_strut)).e_L4 ; data.(sprintf("uL%i", i_strut)).e_L5 ; data.(sprintf("uL%i", i_strut)).e_L6]'; + dL = [data.(sprintf("uL%i", i_strut)).dL1 ; data.(sprintf("uL%i", i_strut)).dL2 ; data.(sprintf("uL%i", i_strut)).dL3 ; data.(sprintf("uL%i", i_strut)).dL4 ; data.(sprintf("uL%i", i_strut)).dL5 ; data.(sprintf("uL%i", i_strut)).dL6]'; + G_iff(:,:,i_strut) = tfestimate(data.(sprintf("uL%i", i_strut)).id_plant, Vs, win, Noverlap, Nfft, 1/Ts); G_int(:,:,i_strut) = tfestimate(data.(sprintf("uL%i", i_strut)).id_plant, eL, win, Noverlap, Nfft, 1/Ts); end #+end_src @@ -814,9 +1064,9 @@ for i = 1:5 end end plot(f, abs(G_int(:, 1, 1)), 'color', [colors(1,:)], ... - 'DisplayName', '$e\mathcal{L}_i/u_i$ meas'); + 'DisplayName', '$-e\mathcal{L}_i/u_i$ meas'); plot(freqs, abs(squeeze(freqresp(Gm(sprintf('eL%i', 1), sprintf('u%i', 1)), freqs, 'Hz'))), 'color', [colors(2,:)], ... - 'DisplayName', '$e\mathcal{L}_i/u_i$ model'); + 'DisplayName', '$-e\mathcal{L}_i/u_i$ model'); for i = 2:6 plot(f, abs(G_int(:,i, i)), 'color', [colors(1,:)], ... 'HandleVisibility', 'off'); @@ -824,9 +1074,9 @@ for i = 2:6 'HandleVisibility', 'off'); end plot(f, abs(G_int(:, 1, 2)), 'color', [colors(1,:), 0.2], ... - 'DisplayName', '$e\mathcal{L}_i/u_j$ meas'); + 'DisplayName', '$-e\mathcal{L}_i/u_j$ meas'); plot(freqs, abs(squeeze(freqresp(Gm(sprintf('eL%i', 1), sprintf('u%i', 2)), freqs, 'Hz'))), 'color', [colors(2,:), 0.2], ... - 'DisplayName', '$e\mathcal{L}_i/u_j$ model'); + 'DisplayName', '$-e\mathcal{L}_i/u_j$ model'); hold off; set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); ylabel('Amplitude [m/V]'); set(gca, 'XTickLabel',[]); @@ -868,28 +1118,28 @@ for i = 1:5 for j = i+1:6 plot(f, abs(G_iff(:, i, j)), 'color', [colors(1,:), 0.2], ... 'HandleVisibility', 'off'); - plot(freqs, abs(squeeze(freqresp(Gm(sprintf('Fn%i', i), sprintf('u%i', j)), freqs, 'Hz'))), 'color', [colors(2,:), 0.2], ... + plot(freqs, abs(squeeze(freqresp(Gm(sprintf('Vs%i', i), sprintf('u%i', j)), freqs, 'Hz'))), 'color', [colors(2,:), 0.2], ... 'HandleVisibility', 'off'); end end plot(f, abs(G_iff(:,1, 1)), 'color', [colors(1,:)], ... - 'DisplayName', '$\tau_{m,i}/u_i$ meas'); -plot(freqs, abs(squeeze(freqresp(Gm(sprintf('Fn%i', 1), sprintf('u%i', 1)), freqs, 'Hz'))), 'color', [colors(2,:)], ... - 'DisplayName', '$\tau_{m,i}/u_i$ model'); + 'DisplayName', '$V_{s,i}/u_i$ meas'); +plot(freqs, abs(squeeze(freqresp(Gm(sprintf('Vs%i', 1), sprintf('u%i', 1)), freqs, 'Hz'))), 'color', [colors(2,:)], ... + 'DisplayName', '$V_{s,i}/u_i$ model'); for i = 2:6 plot(f, abs(G_iff(:,i, i)), 'color', [colors(1,:)], ... 'HandleVisibility', 'off'); - plot(freqs, abs(squeeze(freqresp(Gm(sprintf('Fn%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(2,:)], ... + plot(freqs, abs(squeeze(freqresp(Gm(sprintf('Vs%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(2,:)], ... 'HandleVisibility', 'off'); end plot(f, abs(G_iff(:, 1, 2)), 'color', [colors(1,:), 0.2], ... - 'DisplayName', '$\tau_{m,i}/u_j$ meas'); -plot(freqs, abs(squeeze(freqresp(Gm(sprintf('Fn%i', 1), sprintf('u%i', 2)), freqs, 'Hz'))), 'color', [colors(2,:), 0.2], ... - 'DisplayName', '$\tau_{m,i}/u_j$ model'); + 'DisplayName', '$V_{s,i}/u_j$ meas'); +plot(freqs, abs(squeeze(freqresp(Gm(sprintf('Vs%i', 1), sprintf('u%i', 2)), freqs, 'Hz'))), 'color', [colors(2,:), 0.2], ... + 'DisplayName', '$V_{s,i}/u_j$ model'); hold off; set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); ylabel('Amplitude [V/V]'); set(gca, 'XTickLabel',[]); -ylim([1e-4, 1e2]); +ylim([5e-5, 4e1]); leg = legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 2); leg.ItemTokenSize(1) = 15; @@ -899,7 +1149,7 @@ for i = 1:6 plot(f, 180/pi*angle(G_iff(:,i, i)), 'color', [colors(1,:)]); end for i = 1:6 - plot(freqs, 180/pi*angle(squeeze(freqresp(Gm(sprintf('Fn%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(2,:)]); + plot(freqs, 180/pi*angle(squeeze(freqresp(Gm(sprintf('Vs%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(2,:)]); end hold off; set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin'); @@ -968,7 +1218,7 @@ figure; hold on; plot(1e6*data_1_dx.Dx_int_filtered, 1e6*data_1_dx.Dy_int_filtered, 'color', colors(2,:), 'DisplayName', 'Measurement') plot(1e6*data_1_dy.Dx_int_filtered, 1e6*data_1_dy.Dy_int_filtered, 'color', colors(2,:), 'HandleVisibility', 'off') -plot( 1e6*[-10:10]*cos(Rz_error), 1e6*[-10:10]*sin(Rz_error), 'k--', 'DisplayName', sprintf('$R_z = %.1f$ deg', Rz_error*180/pi)) +plot( 1e6*[-10:10]*cos(Rz_error), 1e6*[-10:10]*sin(Rz_error), 'k--', 'DisplayName', sprintf('$\\epsilon_{R_z} = %.1f$ deg', Rz_error*180/pi)) plot(-1e6*[-10:10]*sin(Rz_error), 1e6*[-10:10]*cos(Rz_error), 'k--', 'HandleVisibility', 'off') hold off; xlabel('Interf $D_x$ [$\mu$m]'); @@ -998,7 +1248,7 @@ figure; hold on; plot(1e6*data_2_dx.Dx_int_filtered, 1e6*data_2_dx.Dy_int_filtered, 'color', colors(5,:), 'DisplayName', 'Measurement') plot(1e6*data_2_dy.Dx_int_filtered, 1e6*data_2_dy.Dy_int_filtered, 'color', colors(5,:), 'HandleVisibility', 'off') -plot( 1e6*[-10:10]*cos(Rz_error), 1e6*[-10:10]*sin(Rz_error), 'k--', 'DisplayName', sprintf('$R_z = %.1f$ deg', Rz_error*180/pi)) +plot( 1e6*[-10:10]*cos(Rz_error), 1e6*[-10:10]*sin(Rz_error), 'k--', 'DisplayName', sprintf('$\\epsilon_{R_z} = %.1f$ deg', Rz_error*180/pi)) plot(-1e6*[-10:10]*sin(Rz_error), 1e6*[-10:10]*cos(Rz_error), 'k--', 'HandleVisibility', 'off') hold off; xlabel('Interf $D_x$ [$\mu$m]'); @@ -1036,7 +1286,8 @@ exportFig('figs/test_id31_Rz_align_correct.pdf', 'width', 'half', 'height', 'nor <> The plant dynamics is identified after the fine alignment and is compared with the model dynamics in Figure ref:fig:test_id31_first_id_int_better_rz_align. -Compared to the initial identification shown in Figure ref:fig:test_id31_first_id_int, the obtained coupling has decreased and is close to the coupling obtained with the multi-body model. +Compared to the initial identification shown in Figure ref:fig:test_id31_first_id_int, the obtained coupling has decreased and is now close to the coupling obtained with the multi-body model. +At low frequency (below $10\,\text{Hz}$) all the off-diagonal elements have an amplitude $\approx 100$ times lower compared to the diagonal elements, indicating that a low bandwidth feedback controller can be implemented in a decentralized way (i.e. $6$ SISO controllers). #+begin_src matlab %% Identification of the plant after Rz alignment @@ -1050,66 +1301,6 @@ for i_strut = 1:6 end #+end_src -#+begin_src matlab :exports none :results none -%% Decrease of the coupling with better Rz alignment -figure; -tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None'); - -ax1 = nexttile([2,1]); -hold on; -plot(f, abs(G_int(:, 1, 2)), 'color', [colors(1,:), 0.2], ... - 'DisplayName', '$e\mathcal{L}_i/u_j$'); -for i = 1:5 - for j = i+1:6 - plot(f, abs(G_int(:, i, j)), 'color', [colors(1,:), 0.2], ... - 'HandleVisibility', 'off'); - end -end -plot(f, abs(G_int_align(:, 1, 2)), 'color', [colors(2,:), 0.2], ... - 'DisplayName', '$e\mathcal{L}_i/u_j$ - $R_z$ align'); -for i = 1:5 - for j = i+1:6 - plot(f, abs(G_int_align(:, i, j)), 'color', [colors(2,:), 0.2], ... - 'HandleVisibility', 'off'); - end -end -plot(f, abs(G_int(:, 1, 1)), 'color', [colors(1,:)], ... - 'DisplayName', '$e\mathcal{L}_i/u_i$'); -for i = 2:6 - plot(f, abs(G_int(:,i, i)), 'color', [colors(1,:)], ... - 'HandleVisibility', 'off'); -end -plot(f, abs(G_int_align(:, 1, 1)), 'color', [colors(2,:)], ... - 'DisplayName', '$e\mathcal{L}_i/u_i$ - $R_z$ align'); -for i = 2:6 - plot(f, abs(G_int_align(:,i, i)), 'color', [colors(2,:)], ... - 'HandleVisibility', 'off'); -end -hold off; -set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); -ylabel('Amplitude [m/V]'); set(gca, 'XTickLabel',[]); -ylim([1e-8, 2e-4]); -legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 2); - -ax2 = nexttile; -hold on; -for i = 1:6 - plot(f, 180/pi*angle(G_int(:,i, i)), 'color', [colors(1,:)]); -end -for i = 1:6 - plot(f, 180/pi*angle(G_int_align(:,i, i)), 'color', [colors(2,:)]); -end -hold off; -set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin'); -xlabel('Frequency [Hz]'); ylabel('Phase [deg]'); -hold off; -yticks(-360:90:360); -ylim([-90, 180]) - -linkaxes([ax1,ax2],'x'); -xlim([1, 1e3]); -#+end_src - #+begin_src matlab :exports none :results none %% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor figure; @@ -1162,6 +1353,8 @@ exportFig('figs/test_id31_first_id_int_better_rz_align.pdf', 'width', 'wide', 'h The system dynamics was identified with four payload conditions that are shown in Figure ref:fig:test_id31_picture_masses. The obtained direct terms are compared with the model dynamics in Figure ref:fig:test_nhexa_comp_simscape_diag_masses. +It is interesting to note that the anti-resonances in the force sensor plant are now appearing as minimum-phase, as the model predicts (Figure ref:fig:test_id31_comp_simscape_iff_diag_masses). + #+name: fig:test_id31_picture_masses #+caption: The four tested payload conditions. (\subref{fig:test_id31_picture_mass_m0}) without payload. (\subref{fig:test_id31_picture_mass_m1}) with $13\,\text{kg}$ payload. (\subref{fig:test_id31_picture_mass_m2}) with $26\,\text{kg}$ payload. (\subref{fig:test_id31_picture_mass_m3}) with $39\,\text{kg}$ payload. #+attr_latex: :options [htbp] @@ -1192,6 +1385,58 @@ The obtained direct terms are compared with the model dynamics in Figure ref:fig #+end_subfigure #+end_figure +#+begin_src matlab :exports none +%% Identify the model dynamics for all payload conditions +% Initialize each Simscape model elements +initializeGround(); +initializeGranite(); +initializeTy(); +initializeRy(); +initializeRz(); +initializeMicroHexapod(); +initializeNanoHexapod('flex_bot_type', '2dof', ... + 'flex_top_type', '3dof', ... + 'motion_sensor_type', 'plates', ... + 'actuator_type', '2dof'); +initializeSample('type', '0'); + +initializeSimscapeConfiguration('gravity', false); +initializeDisturbances('enable', false); +initializeLoggingConfiguration('log', 'none'); +initializeController('type', 'open-loop'); +initializeReferences(); + +% Input/Output definition +clear io; io_i = 1; +io(io_i) = linio([mdl, '/Controller'], 1, 'openinput'); io_i = io_i + 1; % Actuator Inputs +io(io_i) = linio([mdl, '/Micro-Station'], 3, 'openoutput', [], 'Vs'); io_i = io_i + 1; % Force Sensors +io(io_i) = linio([mdl, '/Tracking Error'], 1, 'openoutput', [], 'EdL'); io_i = io_i + 1; % Position Errors + +initializeSample('type', '0'); +Gm_m0_Wz0 = linearize(mdl, io); +Gm_m0_Wz0.InputName = {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'}; +Gm_m0_Wz0.OutputName = {'Vs1', 'Vs2', 'Vs3', 'Vs4', 'Vs5', 'Vs6', ... + 'eL1', 'eL2', 'eL3', 'eL4', 'eL5', 'eL6'}; + +initializeSample('type', '1'); +Gm_m1_Wz0 = linearize(mdl, io); +Gm_m1_Wz0.InputName = {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'}; +Gm_m1_Wz0.OutputName = {'Vs1', 'Vs2', 'Vs3', 'Vs4', 'Vs5', 'Vs6', ... + 'eL1', 'eL2', 'eL3', 'eL4', 'eL5', 'eL6'}; + +initializeSample('type', '2'); +Gm_m2_Wz0 = linearize(mdl, io); +Gm_m2_Wz0.InputName = {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'}; +Gm_m2_Wz0.OutputName = {'Vs1', 'Vs2', 'Vs3', 'Vs4', 'Vs5', 'Vs6', ... + 'eL1', 'eL2', 'eL3', 'eL4', 'eL5', 'eL6'}; + +initializeSample('type', '3'); +Gm_m3_Wz0 = linearize(mdl, io); +Gm_m3_Wz0.InputName = {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'}; +Gm_m3_Wz0.OutputName = {'Vs1', 'Vs2', 'Vs3', 'Vs4', 'Vs5', 'Vs6', ... + 'eL1', 'eL2', 'eL3', 'eL4', 'eL5', 'eL6'}; +#+end_src + #+begin_src matlab %% Identify the plant from experimental data - All payloads @@ -1273,58 +1518,6 @@ for i_strut = 1:6 end #+end_src -#+begin_src matlab :exports none -%% Identify the model dynamics for all payload conditions -% Initialize each Simscape model elements -initializeGround(); -initializeGranite(); -initializeTy(); -initializeRy(); -initializeRz(); -initializeMicroHexapod(); -initializeNanoHexapod('flex_bot_type', '2dof', ... - 'flex_top_type', '3dof', ... - 'motion_sensor_type', 'plates', ... - 'actuator_type', '2dof'); -initializeSample('type', '0'); - -initializeSimscapeConfiguration('gravity', false); -initializeDisturbances('enable', false); -initializeLoggingConfiguration('log', 'none'); -initializeController('type', 'open-loop'); -initializeReferences(); - -% Input/Output definition -clear io; io_i = 1; -io(io_i) = linio([mdl, '/Controller'], 1, 'openinput'); io_i = io_i + 1; % Actuator Inputs -io(io_i) = linio([mdl, '/Micro-Station'], 3, 'openoutput', [], 'Fnlm'); io_i = io_i + 1; % Force Sensors -io(io_i) = linio([mdl, '/Tracking Error'], 1, 'openoutput', [], 'EdL'); io_i = io_i + 1; % Position Errors - -initializeSample('type', '0'); -Gm_m0_Wz0 = linearize(mdl, io); -Gm_m0_Wz0.InputName = {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'}; -Gm_m0_Wz0.OutputName = {'Fn1', 'Fn2', 'Fn3', 'Fn4', 'Fn5', 'Fn6', ... - 'eL1', 'eL2', 'eL3', 'eL4', 'eL5', 'eL6'}; - -initializeSample('type', '1'); -Gm_m1_Wz0 = linearize(mdl, io); -Gm_m1_Wz0.InputName = {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'}; -Gm_m1_Wz0.OutputName = {'Fn1', 'Fn2', 'Fn3', 'Fn4', 'Fn5', 'Fn6', ... - 'eL1', 'eL2', 'eL3', 'eL4', 'eL5', 'eL6'}; - -initializeSample('type', '2'); -Gm_m2_Wz0 = linearize(mdl, io); -Gm_m2_Wz0.InputName = {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'}; -Gm_m2_Wz0.OutputName = {'Fn1', 'Fn2', 'Fn3', 'Fn4', 'Fn5', 'Fn6', ... - 'eL1', 'eL2', 'eL3', 'eL4', 'eL5', 'eL6'}; - -initializeSample('type', '3'); -Gm_m3_Wz0 = linearize(mdl, io); -Gm_m3_Wz0.InputName = {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'}; -Gm_m3_Wz0.OutputName = {'Fn1', 'Fn2', 'Fn3', 'Fn4', 'Fn5', 'Fn6', ... - 'eL1', 'eL2', 'eL3', 'eL4', 'eL5', 'eL6'}; -#+end_src - #+begin_src matlab :exports none :results none %% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor figure; @@ -1436,18 +1629,18 @@ for i = 2:6 plot(f, abs(G_iff_m3_Wz0(:,i, i)), 'color', [colors(4,:), 0.5], ... 'HandleVisibility', 'off') end -plot(freqs, abs(squeeze(freqresp(Gm_m0_Wz0('Fn1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(1,:), ... +plot(freqs, abs(squeeze(freqresp(Gm_m0_Wz0('Vs1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(1,:), ... 'DisplayName', 'Model (0kg)'); -plot(freqs, abs(squeeze(freqresp(Gm_m1_Wz0('Fn1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(2,:), ... +plot(freqs, abs(squeeze(freqresp(Gm_m1_Wz0('Vs1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(2,:), ... 'DisplayName', 'Model (13kg)'); -plot(freqs, abs(squeeze(freqresp(Gm_m2_Wz0('Fn1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(3,:), ... +plot(freqs, abs(squeeze(freqresp(Gm_m2_Wz0('Vs1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(3,:), ... 'DisplayName', 'Model (26kg)'); -plot(freqs, abs(squeeze(freqresp(Gm_m3_Wz0('Fn1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(4,:), ... +plot(freqs, abs(squeeze(freqresp(Gm_m3_Wz0('Vs1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(4,:), ... 'DisplayName', 'Model (39kg)'); hold off; set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); ylabel('Amplitude [V/V]'); set(gca, 'XTickLabel',[]); -ylim([1e-2, 1e2]); +ylim([1e-2, 4e1]); leg = legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 2); leg.ItemTokenSize(1) = 15; @@ -1465,10 +1658,10 @@ end for i =1:6 plot(f, 180/pi*angle(G_iff_m3_Wz0(:,i, i)), 'color', [colors(4,:), 0.5]); end -plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_m0_Wz0('Fn1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(1,:)) -plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_m1_Wz0('Fn1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(2,:)) -plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_m2_Wz0('Fn1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(3,:)) -plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_m3_Wz0('Fn1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(4,:)) +plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_m0_Wz0('Vs1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(1,:)) +plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_m1_Wz0('Vs1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(2,:)) +plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_m2_Wz0('Vs1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(3,:)) +plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_m3_Wz0('Vs1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(4,:)) hold off; set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin'); xlabel('Frequency [Hz]'); ylabel('Phase [deg]'); @@ -1489,7 +1682,7 @@ exportFig('figs/test_id31_comp_simscape_iff_diag_masses.pdf', 'width', 'half', ' #+caption: Comparison of the diagonal elements (i.e. "direct" terms) of the measured FRF matrix and the dynamics identified from the Simscape model. Both for the dynamics from $u$ to $e\mathcal{L}$ (\subref{fig:test_id31_comp_simscape_int_diag_masses}) and from $u$ to $V_s$ (\subref{fig:test_id31_comp_simscape_iff_diag_masses}) #+attr_latex: :options [htbp] #+begin_figure -#+attr_latex: :caption \subcaption{\label{fig:test_id31_comp_simscape_int_diag_masses}from $u$ to $d_e$} +#+attr_latex: :caption \subcaption{\label{fig:test_id31_comp_simscape_int_diag_masses}from $u$ to $e\mathcal{L}$} #+attr_latex: :options {0.49\textwidth} #+begin_subfigure #+attr_latex: :width 0.95\linewidth @@ -1520,31 +1713,21 @@ This also indicates that the metrology kinematics is correct and is working in r initializeSample('type', '0'); initializeReferences(... 'Rz_type', 'rotating', ... - 'Rz_period', 360/36); + 'Rz_period', 360/36); % 36 deg/s, 6rpm Gm_m0_Wz36 = linearize(mdl, io, 0.1); Gm_m0_Wz36.InputName = {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'}; -Gm_m0_Wz36.OutputName = {'Fn1', 'Fn2', 'Fn3', 'Fn4', 'Fn5', 'Fn6', ... +Gm_m0_Wz36.OutputName = {'Vs1', 'Vs2', 'Vs3', 'Vs4', 'Vs5', 'Vs6', ... 'eL1', 'eL2', 'eL3', 'eL4', 'eL5', 'eL6'}; initializeReferences(... 'Rz_type', 'rotating', ... - 'Rz_period', 360/180); + 'Rz_period', 360/180); % 180 deg/s, 30rpm Gm_m0_Wz180 = linearize(mdl, io, 0.1); Gm_m0_Wz180.InputName = {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'}; -Gm_m0_Wz180.OutputName = {'Fn1', 'Fn2', 'Fn3', 'Fn4', 'Fn5', 'Fn6', ... +Gm_m0_Wz180.OutputName = {'Vs1', 'Vs2', 'Vs3', 'Vs4', 'Vs5', 'Vs6', ... 'eL1', 'eL2', 'eL3', 'eL4', 'eL5', 'eL6'}; #+end_src -#+begin_src matlab :exports none :tangle no -% The identified dynamics are then saved for further use. -save('./matlab/mat/test_id31_simscape_open_loop_plants.mat', 'Gm_m0_Wz0', 'Gm_m0_Wz180', 'Gm_m1_Wz0', 'Gm_m2_Wz0', 'Gm_m3_Wz0'); -#+end_src - -#+begin_src matlab :eval no -% The identified dynamics are then saved for further use. -save('./mat/test_id31_simscape_open_loop_plants.mat', 'Gm_m0_Wz0', 'Gm_m0_Wz180', 'Gm_m1_Wz0', 'Gm_m2_Wz0', 'Gm_m3_Wz0'); -#+end_src - #+begin_src matlab %% Identify the plant from experimental data - Effect of rotation @@ -1583,6 +1766,20 @@ for i_strut = 1:6 end #+end_src +#+begin_src matlab :exports none :tangle no +% The identified dynamics are then saved for further use. +save('./matlab/mat/test_id31_simscape_open_loop_plants.mat', 'Gm_m0_Wz0', 'Gm_m0_Wz36', 'Gm_m0_Wz180', 'Gm_m1_Wz0', 'Gm_m2_Wz0', 'Gm_m3_Wz0'); +save('./matlab/mat/test_id31_identified_open_loop_plants.mat', 'G_int_m0_Wz0', 'G_int_m0_Wz36', 'G_int_m0_Wz180', 'G_int_m1_Wz0', 'G_int_m2_Wz0', 'G_int_m3_Wz0', ... + 'G_iff_m0_Wz0', 'G_iff_m0_Wz36', 'G_iff_m0_Wz180', 'G_iff_m1_Wz0', 'G_iff_m2_Wz0', 'G_iff_m3_Wz0', 'f'); +#+end_src + +#+begin_src matlab :eval no +% The identified dynamics are then saved for further use. +save('./mat/test_id31_simscape_open_loop_plants.mat', 'Gm_m0_Wz0', 'Gm_m0_Wz36', 'Gm_m0_Wz180', 'Gm_m1_Wz0', 'Gm_m2_Wz0', 'Gm_m3_Wz0'); +save('./mat/test_id31_identified_open_loop_plants.mat', 'G_int_m0_Wz0', 'G_int_m0_Wz36', 'G_int_m0_Wz180', 'G_int_m1_Wz0', 'G_int_m2_Wz0', 'G_int_m3_Wz0', ... + 'G_iff_m0_Wz0', 'G_iff_m0_Wz36', 'G_iff_m0_Wz180', 'G_iff_m1_Wz0', 'G_iff_m2_Wz0', 'G_iff_m3_Wz0', 'f'); +#+end_src + #+begin_src matlab :exports none :results none figure; tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None'); @@ -1667,6 +1864,53 @@ exportFig('figs/test_id31_effect_rotation_coupling.pdf', 'width', 'half', 'heigh #+end_subfigure #+end_figure +** TODO Identification of Spurious modes + +- [ ] These are made to identify the modes of the spheres +- [ ] Also discuss other observed modes + +#+begin_src matlab +%% Load identification data +% mat_dir = "/home/thomas/mnt/data_id31/nass"; +data = load(sprintf('%s/dynamics/2023-08-10_18-32_identify_spurious_modes.mat', mat_dir)); +#+end_src + +#+begin_src matlab :exports none +Ts = 1e-4; +% Hannning Windows +Nfft = floor(1.0/Ts); +win = hanning(Nfft); +Noverlap = floor(Nfft/2); +#+end_src + +#+begin_src matlab :exports none +% And we get the frequency vector +[G1, f] = tfestimate(data.id_plant, data.d1, win, Noverlap, Nfft, 1/Ts); +[G2, ~] = tfestimate(data.id_plant, data.d2, win, Noverlap, Nfft, 1/Ts); +[G3, ~] = tfestimate(data.id_plant, data.d3, win, Noverlap, Nfft, 1/Ts); +[G4, ~] = tfestimate(data.id_plant, data.d4, win, Noverlap, Nfft, 1/Ts); +[G5, ~] = tfestimate(data.id_plant, data.d5, win, Noverlap, Nfft, 1/Ts); +#+end_src + +#+begin_src matlab :exports none :results none +%% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor +figure; +tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None'); + +ax1 = nexttile(); +hold on; +plot(f, abs(G1), 'DisplayName', '1 - top'); +plot(f, abs(G2), 'DisplayName', '2 - bot'); +plot(f, abs(G3), 'DisplayName', '3 - top'); +plot(f, abs(G4), 'DisplayName', '4 - bot'); +plot(f, abs(G5), 'DisplayName', '5 - vertical'); +hold off; +set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); +xlabel('Frequency [Hz]'); ylabel('Amplitude [m/V]'); +xlim([500, 800]) +legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1); +#+end_src + ** Conclusion :PROPERTIES: :UNNUMBERED: t @@ -1675,7 +1919,7 @@ exportFig('figs/test_id31_effect_rotation_coupling.pdf', 'width', 'half', 'heigh Thanks to the model, poor alignment between the nano-hexapod axes and the external metrology axes could be identified. After alignment, the identified dynamics is well matching with the multi-body model. -Also, the effects of the payload mass and of the spindle rotation are well capture in the model. +Also, the observed effects of the payload mass and of the spindle rotation on the dynamics are well matching the model predictions. * Decentralized Integral Force Feedback :PROPERTIES: @@ -1684,6 +1928,49 @@ Also, the effects of the payload mass and of the spindle rotation are well captu <> ** Introduction :ignore: +Before implementing a position controller, an active damping controller was first implemented as shown in Figure ref:fig:test_id31_iff_block_diagram. +It consisted of a decentralized Integral Force Feedback controller $\bm{K}_{\text{IFF}}$, with all the diagonal terms being equal eqref:eq:test_id31_Kiff. + +\begin{equation}\label{eq:test_id31_iff_diagonal} +\bm{K}_{\text{IFF}} = K_{\text{IFF}} \cdot \bm{I}_6 = \begin{bmatrix} +K_{\text{IFF}} & & 0 \\ +& \ddots & \\ +0 & & K_{\text{IFF}} +\end{bmatrix} +\end{equation} + +#+begin_src latex :file test_id31_iff_schematic.pdf +\begin{tikzpicture} + % Blocs + \node[block={2.0cm}{2.0cm}] (P) {Plant}; + \coordinate[] (input) at ($(P.south west)!0.5!(P.north west)$); + \coordinate[] (outputH) at ($(P.south east)!0.2!(P.north east)$); + \coordinate[] (outputL) at ($(P.south east)!0.8!(P.north east)$); + + \node[block, above=0.6 of P] (Klac) {$\bm{K}_\text{IFF}$}; + \node[addb, left= of input] (addF) {}; + + % Connections and labels + \draw[->] (outputL) -- ++(0.6, 0) coordinate(eastlac) |- (Klac.east); + \node[above right] at (outputL){$\bm{V}_s$}; + \draw[->] (Klac.west) -| (addF.north); + \draw[->] (addF.east) -- (input) node[above left]{$\bm{u}$}; + + \draw[->] (outputH) -- ++(1.6, 0) node[above left]{$\bm{\epsilon}_{\mathcal{L}}$}; + \draw[<-] (addF.west) -- ++(-1.0, 0) node[above right]{$\bm{u}^{\prime}$}; + + \begin{scope}[on background layer] + \node[fit={(Klac.north-|eastlac) (addF.west|-P.south)}, fill=black!20!white, draw, dashed, inner sep=10pt] (Pi) {}; + \node[anchor={north west}] at (Pi.north west){$\text{Damped Plant}$}; + \end{scope} +\end{tikzpicture} +#+end_src + +#+name: fig:test_id31_iff_block_diagram +#+caption: Block diagram of the implemented decentralized IFF controller. The controller $\bm{K}_{\text{IFF}}$ is a diagonal controller with the same elements on every diagonal term $K_{\text{IFF}}$. +#+RESULTS: +[[file:figs/test_id31_iff_schematic.png]] + ** Matlab Init :noexport:ignore: #+begin_src matlab %% test_id31_3_iff.m @@ -1713,460 +2000,174 @@ Also, the effects of the payload mass and of the spindle rotation are well captu <> #+end_src -** IFF Plants -*** Introduction :ignore: +** IFF Plant +<> + +As the multi-body model is going to be used to estimate the stability of the IFF controller and to optimize achievable damping, it is first checked is this model accurately represents the system dynamics. + +In Figure ref:fig:test_id31_comp_simscape_iff_diag_masses, it was shown that the model well captures the dynamics from each actuator to its collocated force sensor, as that for all considered payloads. +The model is also accurate for the dynamics from an actuator to the force sensors in the other struts (i.e. the off-diagonal dynamics) as shown in Figure ref:fig:test_id31_comp_simscape_Vs. -*** 6x6 Plant #+begin_src matlab -%% Load identification data -data = load(sprintf('%s/dynamics/2023-08-08_16-17_ol_plant_m0_Wz0.mat', mat_dir)); +% Load identified FRF for IFF Plant and Multi-Body Model +load('test_id31_identified_open_loop_plants.mat', 'G_iff_m0_Wz0', 'G_iff_m1_Wz0', 'G_iff_m2_Wz0', 'G_iff_m3_Wz0', 'f'); +load('test_id31_simscape_open_loop_plants.mat', 'Gm_m0_Wz0', 'Gm_m1_Wz0', 'Gm_m2_Wz0', 'Gm_m3_Wz0'); #+end_src #+begin_src matlab :exports none -% Hannning Windows -Nfft = floor(1.0/Ts); -win = hanning(Nfft); -Noverlap = floor(Nfft/2); -#+end_src - -#+begin_src matlab :exports none -% And we get the frequency vector -[~, f] = tfestimate(data.uL1.id_plant, data.uL1.e_L1, win, [], [], 1/Ts); -#+end_src - -#+begin_src matlab :exports none -%% IFF Plant (transfer function from u to taum) -G_iff = zeros(length(f), 6, 6); - -for i_strut = 1:6 - eL = [data.(sprintf("uL%i", i_strut)).Vs1 ; data.(sprintf("uL%i", i_strut)).Vs2 ; data.(sprintf("uL%i", i_strut)).Vs3 ; data.(sprintf("uL%i", i_strut)).Vs4 ; data.(sprintf("uL%i", i_strut)).Vs5 ; data.(sprintf("uL%i", i_strut)).Vs6]'; - - G_iff(:,:,i_strut) = tfestimate(data.(sprintf("uL%i", i_strut)).id_plant, eL, win, [], [], 1/Ts); -end -#+end_src - -#+begin_src matlab :exports none :results none -%% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor figure; -tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None'); +tiledlayout(2, 3, 'TileSpacing', 'tight', 'Padding', 'tight'); -ax1 = nexttile([2,1]); +ax1 = nexttile(); hold on; -for i = 1:5 - for j = i+1:6 - plot(f, abs(G_iff(:, i, j)), 'color', [0, 0, 0, 0.2], ... - 'HandleVisibility', 'off'); - end -end -for i = 1:6 - plot(f, abs(G_iff(:,i, i)), 'color', colors(i,:), ... - 'DisplayName', sprintf('$\\tau_{m,%i}/u_%i$', i, i)); -end -plot(f, abs(G_iff(:, 1, 2)), 'color', [0, 0, 0, 0.2], ... - 'DisplayName', '$\tau_{m,i}/u_j$'); +plot(f, abs(G_iff_m0_Wz0(:, 1, 1))); +plot(freqs, abs(squeeze(freqresp(Gm_m0_Wz0('Vs1', 'u1'), freqs, 'Hz')))); +text(12, 4e1, '$V_{s1}/u_{1}$', 'Horiz','left', 'Vert','top') hold off; set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); -ylabel('Amplitude [V/V]'); set(gca, 'XTickLabel',[]); -ylim([1e-4, 1e2]); -legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 3); +set(gca, 'XTickLabel',[]); ylabel('Amplitude [m/V]'); +yticks([1e-2, 1e-1, 1e0, 1e1]); -ax2 = nexttile; +ax2 = nexttile(); hold on; -for i =1:6 - plot(f, 180/pi*angle(G_iff(:,i, i))); -end +plot(f, abs(G_iff_m0_Wz0(:, 2, 1))); +plot(freqs, abs(squeeze(freqresp(Gm_m0_Wz0('Vs2', 'u1'), freqs, 'Hz')))); +text(12, 4e1, '$V_{s2}/u_{1}$', 'Horiz','left', 'Vert','top') hold off; -set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin'); -xlabel('Frequency [Hz]'); ylabel('Phase [deg]'); -hold off; -yticks(-360:90:360); -ylim([-90, 180]) +set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); +set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]); -linkaxes([ax1,ax2],'x'); -xlim([1, 1e3]); +ax3 = nexttile(); +hold on; +plot(f, abs(G_iff_m0_Wz0(:, 3, 1)), ... + 'DisplayName', 'Measurements'); +plot(freqs, abs(squeeze(freqresp(Gm_m0_Wz0('Vs3', 'u1'), freqs, 'Hz'))), ... + 'DisplayName', 'Model (2-DoF APA)'); +text(12, 4e1, '$V_{s3}/u_{1}$', 'Horiz','left', 'Vert','top') +hold off; +set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); +set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]); +leg = legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1); +leg.ItemTokenSize(1) = 15; + +ax4 = nexttile(); +hold on; +plot(f, abs(G_iff_m0_Wz0(:, 4, 1))); +plot(freqs, abs(squeeze(freqresp(Gm_m0_Wz0('Vs4', 'u1'), freqs, 'Hz')))); +text(12, 4e1, '$V_{s4}/u_{1}$', 'Horiz','left', 'Vert','top') +hold off; +set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); +xlabel('Frequency [Hz]'); ylabel('Amplitude [m/V]'); +xticks([10, 20, 50, 100, 200]) +yticks([1e-2, 1e-1, 1e0, 1e1]); + +ax5 = nexttile(); +hold on; +plot(f, abs(G_iff_m0_Wz0(:, 5, 1))); +plot(freqs, abs(squeeze(freqresp(Gm_m0_Wz0('Vs5', 'u1'), freqs, 'Hz')))); +text(12, 4e1, '$V_{s5}/u_{1}$', 'Horiz','left', 'Vert','top') +hold off; +xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]); +set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); +xticks([10, 20, 50, 100, 200]) + +ax6 = nexttile(); +hold on; +plot(f, abs(G_iff_m0_Wz0(:, 6, 1))); +plot(freqs, abs(squeeze(freqresp(Gm_m0_Wz0('Vs6', 'u1'), freqs, 'Hz')))); +text(12, 4e1, '$V_{s6}/u_{1}$', 'Horiz','left', 'Vert','top') +hold off; +set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); +xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]); +xticks([10, 20, 50, 100, 200]) + +linkaxes([ax1,ax2,ax3,ax4,ax5,ax6],'xy'); +xlim([10, 5e2]); ylim([1e-2, 5e1]); #+end_src #+begin_src matlab :tangle no :exports results :results file replace -exportFig('figs/id31_Giff_plant.pdf', 'width', 'full', 'height', 'tall'); +exportFig('figs/test_id31_comp_simscape_Vs.pdf', 'width', 'full', 'height', 700); #+end_src -#+name: fig:id31_Giff_plant -#+caption: Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor +#+name: fig:test_id31_comp_simscape_Vs +#+caption: Comparison of the measured (in blue) and modeled (in red) frequency transfer functions from the first control signal $u_1$ to the six force sensor voltages $V_{s1}$ to $V_{s6}$ #+RESULTS: -[[file:figs/id31_Giff_plant.png]] - -Compare with Model: -#+begin_src matlab -load('Gm_iff.mat'); -#+end_src - -#+begin_src matlab :exports none :results none -%% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor -figure; -tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None'); - -ax1 = nexttile([2,1]); -hold on; -for i = 1:5 - for j = i+1:6 - plot(f, abs(G_iff(:, i, j)), 'color', [colors(3,:), 0.2], ... - 'HandleVisibility', 'off'); - end -end -for i = 1:5 - for j = i+1:6 - plot(freqs, abs(squeeze(freqresp(Gm_iff_m0(i, j), freqs, 'Hz'))), 'color', [colors(4,:), 0.2], ... - 'HandleVisibility', 'off'); - end -end -for i = 2:6 - plot(f, abs(G_iff(:,i, i)), 'color', [colors(1,:), 0.5], ... - 'HandleVisibility', 'off'); -end -for i = 2:6 - plot(freqs, abs(squeeze(freqresp(Gm_iff_m0(i, i), freqs, 'Hz'))), 'color', [colors(2,:), 0.5], ... - 'HandleVisibility', 'off'); -end -% plot(f, abs(G_iff(:, 1, 2)), 'color', [0, 0, 0, 0.2], ... -% 'DisplayName', '$\tau_{m,i}/u_j$'); -hold off; -set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); -ylabel('Amplitude [V/V]'); set(gca, 'XTickLabel',[]); -ylim([1e-4, 1e2]); -legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 2); - -% ax2 = nexttile; -% hold on; -% for i =1:6 -% plot(f, 180/pi*angle(G_iff(:,i, i))); -% end -% hold off; -% set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin'); -% xlabel('Frequency [Hz]'); ylabel('Phase [deg]'); -% hold off; -% yticks(-360:90:360); -% ylim([-90, 180]) - -linkaxes([ax1,ax2],'x'); -xlim([1, 1e3]); -#+end_src - -*** Effect of Rotation -#+begin_src matlab -%% Load identification data -data_Wz0 = load(sprintf('%s/dynamics/2023-08-08_16-17_ol_plant_m0_Wz0.mat', mat_dir)); -data_Wz1 = load(sprintf('%s/dynamics/2023-08-08_16-28_ol_plant_m0_Wz36.mat', mat_dir)); -data_Wz2 = load(sprintf('%s/dynamics/2023-08-08_16-45_ol_plant_m0_Wz180.mat', mat_dir)); -#+end_src - -#+begin_src matlab :exports none -% Sampling Time [s] -Ts = 1e-4; - -% Hannning Windows -Nfft = floor(1.0/Ts); -win = hanning(Nfft); -Noverlap = floor(Nfft/2); -#+end_src - -#+begin_src matlab :exports none -% And we get the frequency vector -[~, f] = tfestimate(data.uL1.id_plant, data.uL1.e_L1, win, [], [], 1/Ts); -#+end_src - -#+begin_src matlab :exports none -%% IFF Plant (transfer function from u to taum) -G_iff_Wz0 = zeros(length(f), 6, 6); - -for i_strut = 1:6 - eL = [data_Wz0.(sprintf("uL%i", i_strut)).Vs1 ; data_Wz0.(sprintf("uL%i", i_strut)).Vs2 ; data_Wz0.(sprintf("uL%i", i_strut)).Vs3 ; data_Wz0.(sprintf("uL%i", i_strut)).Vs4 ; data_Wz0.(sprintf("uL%i", i_strut)).Vs5 ; data_Wz0.(sprintf("uL%i", i_strut)).Vs6]'; - - G_iff_Wz0(:,:,i_strut) = tfestimate(data_Wz0.(sprintf("uL%i", i_strut)).id_plant, eL, win, [], [], 1/Ts); -end -#+end_src - -#+begin_src matlab :exports none -%% IFF Plant (transfer function from u to taum) -G_iff_Wz1 = zeros(length(f), 6, 6); - -for i_strut = 1:6 - eL = [data_Wz1.(sprintf("uL%i", i_strut)).Vs1 ; data_Wz1.(sprintf("uL%i", i_strut)).Vs2 ; data_Wz1.(sprintf("uL%i", i_strut)).Vs3 ; data_Wz1.(sprintf("uL%i", i_strut)).Vs4 ; data_Wz1.(sprintf("uL%i", i_strut)).Vs5 ; data_Wz1.(sprintf("uL%i", i_strut)).Vs6]'; - - G_iff_Wz1(:,:,i_strut) = tfestimate(data_Wz1.(sprintf("uL%i", i_strut)).id_plant, eL, win, [], [], 1/Ts); -end -#+end_src - -#+begin_src matlab :exports none -%% IFF Plant (transfer function from u to taum) -G_iff_Wz2 = zeros(length(f), 6, 6); - -for i_strut = 1:6 - eL = [data_Wz2.(sprintf("uL%i", i_strut)).Vs1 ; data_Wz2.(sprintf("uL%i", i_strut)).Vs2 ; data_Wz2.(sprintf("uL%i", i_strut)).Vs3 ; data_Wz2.(sprintf("uL%i", i_strut)).Vs4 ; data_Wz2.(sprintf("uL%i", i_strut)).Vs5 ; data_Wz2.(sprintf("uL%i", i_strut)).Vs6]'; - - G_iff_Wz2(:,:,i_strut) = tfestimate(data_Wz2.(sprintf("uL%i", i_strut)).id_plant, eL, win, [], [], 1/Ts); -end -#+end_src - -#+begin_src matlab :exports none -%% Save Identified Plants -save('./mat/G_iff.mat', 'G_iff_Wz0', 'G_iff_Wz1', 'G_iff_Wz2', '-append'); -#+end_src - -#+begin_src matlab :exports none :results none -%% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor -figure; -tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None'); - -ax1 = nexttile([2,1]); -hold on; -plot(f, abs(G_iff_Wz0(:, 1, 1)), 'color', [colors(1,:), 0.2], ... - 'DisplayName', '$\Omega_z = 0$'); -for i = 2:6 - plot(f, abs(G_iff_Wz0(:,i, i)), 'color', [colors(1,:), 0.2], ... - 'HandleVisibility', 'off') -end -plot(f, abs(G_iff_Wz1(:, 1, 1)), 'color', [colors(2,:), 0.2], ... - 'DisplayName', '$\Omega_z = 36$ deg/s'); -for i = 2:6 - plot(f, abs(G_iff_Wz1(:,i, i)), 'color', [colors(2,:), 0.2], ... - 'HandleVisibility', 'off') -end -plot(f, abs(G_iff_Wz2(:, 1, 1)), 'color', [colors(3,:), 0.2], ... - 'DisplayName', '$\Omega_z = 180$ deg/s'); -for i = 2:6 - plot(f, abs(G_iff_Wz2(:,i, i)), 'color', [colors(3,:), 0.2], ... - 'HandleVisibility', 'off') -end -hold off; -set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); -ylabel('Amplitude [V/V]'); set(gca, 'XTickLabel',[]); -ylim([1e-2, 1e2]); -legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1); - -ax2 = nexttile; -hold on; -for i =1:6 - plot(f, 180/pi*angle(G_iff_Wz0(:,i, i)), 'color', [colors(1,:), 0.2]); -end -for i =1:6 - plot(f, 180/pi*angle(G_iff_Wz1(:,i, i)), 'color', [colors(2,:), 0.2]); -end -for i =1:6 - plot(f, 180/pi*angle(G_iff_Wz2(:,i, i)), 'color', [colors(3,:), 0.2]); -end -hold off; -set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin'); -xlabel('Frequency [Hz]'); ylabel('Phase [deg]'); -hold off; -yticks(-360:90:360); -ylim([-90, 180]) - -linkaxes([ax1,ax2],'x'); -xlim([1, 1e3]); -#+end_src - -#+begin_src matlab :tangle no :exports results :results file replace -exportFig('figs/id31_Giff_effect_rotation.pdf', 'width', 'full', 'height', 'tall'); -#+end_src - -#+name: fig:id31_Giff_effect_rotation -#+caption: Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor -#+RESULTS: -[[file:figs/id31_Giff_effect_rotation.png]] - -*** Effect of Mass -#+begin_src matlab -load('G_ol.mat', 'G_iff_m0', 'G_iff_m1', 'G_iff_m2', 'G_iff_m3', 'f'); -#+end_src - -#+begin_src matlab :exports none :results none -%% Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor -figure; -tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None'); - -ax1 = nexttile([2,1]); -hold on; -plot(f, abs(G_iff_m0(:, 1, 1)), 'color', [colors(1,:), 0.5], ... - 'DisplayName', '$m = 0$'); -for i = 2:6 - plot(f, abs(G_iff_m0(:,i, i)), 'color', [colors(1,:), 0.5], ... - 'HandleVisibility', 'off') -end -plot(f, abs(G_iff_m1(:, 1, 1)), 'color', [colors(2,:), 0.5], ... - 'DisplayName', '$m = 1$'); -for i = 2:6 - plot(f, abs(G_iff_m1(:,i, i)), 'color', [colors(2,:), 0.5], ... - 'HandleVisibility', 'off') -end -plot(f, abs(G_iff_m2(:, 1, 1)), 'color', [colors(3,:), 0.5], ... - 'DisplayName', '$m = 2$'); -for i = 2:6 - plot(f, abs(G_iff_m2(:,i, i)), 'color', [colors(3,:), 0.5], ... - 'HandleVisibility', 'off') -end -plot(f, abs(G_iff_m3(:, 1, 1)), 'color', [colors(4,:), 0.5], ... - 'DisplayName', '$m = 3$'); -for i = 2:6 - plot(f, abs(G_iff_m3(:,i, i)), 'color', [colors(4,:), 0.5], ... - 'HandleVisibility', 'off') -end -hold off; -set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); -ylabel('Amplitude [V/V]'); set(gca, 'XTickLabel',[]); -ylim([1e-2, 1e2]); -legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1); - -ax2 = nexttile; -hold on; -for i =1:6 - plot(f, 180/pi*angle(G_iff_m0(:,i, i)), 'color', [colors(1,:), 0.5]); -end -for i =1:6 - plot(f, 180/pi*angle(G_iff_m1(:,i, i)), 'color', [colors(2,:), 0.5]); -end -for i =1:6 - plot(f, 180/pi*angle(G_iff_m2(:,i, i)), 'color', [colors(3,:), 0.5]); -end -for i =1:6 - plot(f, 180/pi*angle(G_iff_m3(:,i, i)), 'color', [colors(4,:), 0.5]); -end -hold off; -set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin'); -xlabel('Frequency [Hz]'); ylabel('Phase [deg]'); -hold off; -yticks(-360:90:360); -ylim([-90, 180]) - -linkaxes([ax1,ax2],'x'); -xlim([1, 1e3]); -#+end_src - -#+begin_src matlab :tangle no :exports results :results file replace -exportFig('figs/id31_Giff_effect_mass.pdf', 'width', 'full', 'height', 'tall'); -#+end_src - -#+name: fig:id31_Giff_effect_mass -#+caption: Obtained transfer function from generated voltages to measured voltages on the piezoelectric force sensor -#+RESULTS: -[[file:figs/id31_Giff_effect_mass.png]] - -*** Compare with the model -#+begin_src matlab -load('Gm.mat') -#+end_src - -#+begin_src matlab :exports none :results none -%% Comparison of the identified IFF plant and the IFF plant extracted from the simscape model -figure; -tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None'); - -ax1 = nexttile([2,1]); -hold on; -plot(f, abs(G_iff_m0(:, 1, 1)), 'color', [colors(1,:), 0.5], ... - 'DisplayName', '$m = 0$'); -for i = 2:6 - plot(f, abs(G_iff_m0(:,i, i)), 'color', [colors(1,:), 0.5], ... - 'HandleVisibility', 'off') -end -plot(f, abs(G_iff_m1(:, 1, 1)), 'color', [colors(2,:), 0.5], ... - 'DisplayName', '$m = 1$'); -for i = 2:6 - plot(f, abs(G_iff_m1(:,i, i)), 'color', [colors(2,:), 0.5], ... - 'HandleVisibility', 'off') -end -plot(f, abs(G_iff_m2(:, 1, 1)), 'color', [colors(3,:), 0.5], ... - 'DisplayName', '$m = 2$'); -for i = 2:6 - plot(f, abs(G_iff_m2(:,i, i)), 'color', [colors(3,:), 0.5], ... - 'HandleVisibility', 'off') -end -plot(f, abs(G_iff_m3(:, 1, 1)), 'color', [colors(4,:), 0.5], ... - 'DisplayName', '$m = 3$'); -for i = 2:6 - plot(f, abs(G_iff_m3(:,i, i)), 'color', [colors(4,:), 0.5], ... - 'HandleVisibility', 'off') -end -plot(freqs, abs(squeeze(freqresp(Gm_m0('Fn1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(1,:), ... - 'DisplayName', '$m = 0$ - Model'); -plot(freqs, abs(squeeze(freqresp(Gm_m1('Fn1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(2,:), ... - 'DisplayName', '$m = 1$ - Model'); -plot(freqs, abs(squeeze(freqresp(Gm_m2('Fn1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(3,:), ... - 'DisplayName', '$m = 2$ - Model'); -plot(freqs, abs(squeeze(freqresp(Gm_m3('Fn1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(4,:), ... - 'DisplayName', '$m = 3$ - Model'); -hold off; -set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); -ylabel('Amplitude [V/V]'); set(gca, 'XTickLabel',[]); -ylim([1e-2, 1e2]); -legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 2); - -ax2 = nexttile; -hold on; -plot(f, 180/pi*angle(G_iff_m0(:,1,1)), 'color', [colors(1,:), 0.5]); -for i = 2:6 - plot(f, 180/pi*angle(G_iff_m0(:,i, i)), 'color', [colors(1,:), 0.5]); -end -plot(f, 180/pi*angle(G_iff_m1(:,1,1)), 'color', [colors(2,:), 0.5]); -for i = 2:6 - plot(f, 180/pi*angle(G_iff_m1(:,i, i)), 'color', [colors(2,:), 0.5]); -end -plot(f, 180/pi*angle(G_iff_m2(:,1,1)), 'color', [colors(3,:), 0.5]); -for i = 2:6 - plot(f, 180/pi*angle(G_iff_m2(:,i, i)), 'color', [colors(3,:), 0.5]); -end -plot(f, 180/pi*angle(G_iff_m3(:,1,1)), 'color', [colors(4,:), 0.5]); -for i = 2:6 - plot(f, 180/pi*angle(G_iff_m3(:,i, i)), 'color', [colors(4,:), 0.5]); -end -plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_m0('Fn1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(1,:)); -plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_m1('Fn1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(2,:)); -plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_m2('Fn1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(3,:)); -plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_m3('Fn1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(4,:)); -hold off; -set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin'); -xlabel('Frequency [Hz]'); ylabel('Phase [deg]'); -hold off; -yticks(-360:90:360); -ylim([-90, 180]) - -linkaxes([ax1,ax2],'x'); -xlim([1, 1e3]); -#+end_src - -#+begin_src matlab :tangle no :exports results :results file replace -exportFig('figs/id31_Giff_plant_comp_model.pdf', 'width', 'full', 'height', 'tall'); -#+end_src - -#+name: fig:id31_Giff_plant_comp_model -#+caption: Comparison of the identified IFF plant and the IFF plant extracted from the simscape model -#+RESULTS: -[[file:figs/id31_Giff_plant_comp_model.png]] - +[[file:figs/test_id31_comp_simscape_Vs.png]] ** IFF Controller -*** Controller Design -Test second order high pass filter: +<> + +A decentralized IFF controller is there designed such that it adds damping to the suspension modes of the nano-hexapod for all considered payloads. +The frequency of the suspension modes are ranging from $\approx 30\,\text{Hz}$ to $\approx 250\,\text{Hz}$ (Figure ref:fig:test_id31_comp_simscape_iff_diag_masses), and therefore the IFF controller should provide integral action in this frequency range. +A second order high pass filter (cut-off frequency of $10\,\text{Hz}$) is added to limit the low frequency gain. + +The bode plot of the decentralized IFF controller is shown in Figure ref:fig:test_id31_Kiff_bode_plot and the "decentralized loop-gains" for all considered payload masses are shown in Figure ref:fig:test_id31_Kiff_loop_gain. +It can be seen that the loop-gain is larger than $1$ around suspension modes indicating that some damping should be added to the suspension modes. + +\begin{equation}\label{eq:test_id31_Kiff} +K_{\text{IFF}} = g_0 \cdot \underbrace{\frac{1}{s}}_{\text{int}} \cdot \underbrace{\frac{s^2/\omega_z^2}{s^2/\omega_z^2 + 2\xi_z s /\omega_z + 1}}_{\text{2nd order LPF}},\quad \left(g_0 = -100,\ \omega_z = 2\pi10\,\text{rad/s},\ \xi_z = 0.7\right) +\end{equation} + #+begin_src matlab +%% IFF Controller Design +% Second order high pass filter wz = 2*pi*10; xiz = 0.7; -Ghpf = (s^2/wz^2)/(s^2/wz^2 + 2*xiz*s/wz + 1) - % s/(2*pi*1)/(1 + s/(2*pi*1)) * ... % HPF: reduce gain at low frequency -#+end_src +Ghpf = (s^2/wz^2)/(s^2/wz^2 + 2*xiz*s/wz + 1); -We want integral action between 20Hz and 200Hz. -#+begin_src matlab -%% IFF Controller -Kiff = -1e2 * ... % Gain - 1/(0.01*2*pi + s) * ... % LPF: provides integral action - Ghpf * ... - eye(6); % Diagonal 6x6 controller -Kiff.InputName = {'Fn1', 'Fn2', 'Fn3', 'Fn4', 'Fn5', 'Fn6'}; +% IFF Controller +Kiff = -1e2 * ... % Gain + 1/(0.01*2*pi + s) * ... % LPF: provides integral action + Ghpf * ... % 2nd order HPF (limit low frequency gain) + eye(6); % Diagonal 6x6 controller (i.e. decentralized) + +Kiff.InputName = {'Vs1', 'Vs2', 'Vs3', 'Vs4', 'Vs5', 'Vs6'}; Kiff.OutputName = {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'}; #+end_src -Loop Gain: +#+begin_src matlab :exports none :tangle no +% The designed IFF controller is saved +save('./matlab/mat/test_id31_K_iff.mat', 'Kiff'); +#+end_src + +#+begin_src matlab :eval no +% The designed IFF controller is saved +save('./mat/test_id31_K_iff.mat', 'Kiff'); +#+end_src + #+begin_src matlab :exports none :results none -%% IFF Loop gain of the diagonal terms +%% Bode plot of the designed decentralized IFF controller +figure; +tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None'); + +ax1 = nexttile([2,1]); +hold on; +plot(f, abs(squeeze(freqresp(Kiff(1,1), f, 'Hz'))), 'color', colors(1,:)); +hold off; +set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); +ylabel('Amplitude'); set(gca, 'XTickLabel',[]); +ylim([1e-2, 1e1]); + +ax2 = nexttile; +hold on; +plot(f, 180/pi*angle(squeeze(freqresp(Kiff(1,1), f, 'Hz'))), 'color', colors(1,:)); +hold off; +set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin'); +xlabel('Frequency [Hz]'); ylabel('Phase [deg]'); +hold off; +yticks(-360:90:360); +ylim([-180, 180]) + +linkaxes([ax1,ax2],'x'); +xlim([1, 1e3]); +#+end_src + +#+begin_src matlab :tangle no :exports results :results file none +exportFig('figs/test_id31_Kiff_bode_plot.pdf', 'width', 'half', 'height', 600); +#+end_src + +#+begin_src matlab :exports none :results none +%% Loop gain for the decentralized IFF controller Kiff_frf = squeeze(freqresp(Kiff(1,1), f, 'Hz')); figure; @@ -2174,26 +2175,27 @@ tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None'); ax1 = nexttile([2,1]); hold on; -plot(f, abs(G_iff_m0(:, 1, 1).*Kiff_frf), 'color', colors(1,:), ... - 'DisplayName', '$m = 0$'); -plot(f, abs(G_iff_m1(:, 1, 1).*Kiff_frf), 'color', colors(2,:), ... - 'DisplayName', '$m = 1$'); -plot(f, abs(G_iff_m2(:, 1, 1).*Kiff_frf), 'color', colors(3,:), ... - 'DisplayName', '$m = 2$'); -plot(f, abs(G_iff_m3(:, 1, 1).*Kiff_frf), 'color', colors(4,:), ... - 'DisplayName', '$m = 3$'); +plot(f, abs(G_iff_m0_Wz0(:, 1, 1).*Kiff_frf), 'color', colors(1,:), ... + 'DisplayName', '$m = 0$ kg'); +plot(f, abs(G_iff_m1_Wz0(:, 1, 1).*Kiff_frf), 'color', colors(2,:), ... + 'DisplayName', '$m = 13$ kg'); +plot(f, abs(G_iff_m2_Wz0(:, 1, 1).*Kiff_frf), 'color', colors(3,:), ... + 'DisplayName', '$m = 26$ kg'); +plot(f, abs(G_iff_m3_Wz0(:, 1, 1).*Kiff_frf), 'color', colors(4,:), ... + 'DisplayName', '$m = 39$ kg'); hold off; set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); -ylabel('Amplitude [V/V]'); set(gca, 'XTickLabel',[]); +ylabel('Loop Gain'); set(gca, 'XTickLabel',[]); ylim([1e-2, 1e1]); -legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1); +leg = legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1); +leg.ItemTokenSize(1) = 15; ax2 = nexttile; hold on; -plot(f, 180/pi*angle(-G_iff_m0(:,1,1).*Kiff_frf), 'color', colors(1,:)); -plot(f, 180/pi*angle(-G_iff_m1(:,1,1).*Kiff_frf), 'color', colors(2,:)); -plot(f, 180/pi*angle(-G_iff_m2(:,1,1).*Kiff_frf), 'color', colors(3,:)); -plot(f, 180/pi*angle(-G_iff_m3(:,1,1).*Kiff_frf), 'color', colors(4,:)); +plot(f, 180/pi*angle(-G_iff_m0_Wz0(:,1,1).*Kiff_frf), 'color', colors(1,:)); +plot(f, 180/pi*angle(-G_iff_m1_Wz0(:,1,1).*Kiff_frf), 'color', colors(2,:)); +plot(f, 180/pi*angle(-G_iff_m2_Wz0(:,1,1).*Kiff_frf), 'color', colors(3,:)); +plot(f, 180/pi*angle(-G_iff_m3_Wz0(:,1,1).*Kiff_frf), 'color', colors(4,:)); hold off; set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin'); xlabel('Frequency [Hz]'); ylabel('Phase [deg]'); @@ -2206,27 +2208,47 @@ xlim([1, 1e3]); #+end_src #+begin_src matlab :tangle no :exports results :results file replace -exportFig('figs/id31_iff_loop_gain_diagonal_terms.pdf', 'width', 'full', 'height', 'tall'); +exportFig('figs/test_id31_Kiff_loop_gain.pdf', 'width', 'half', 'height', 600); #+end_src -#+name: fig:id31_iff_loop_gain_diagonal_terms -#+caption: IFF Loop gain of the diagonal terms -#+RESULTS: -[[file:figs/id31_iff_loop_gain_diagonal_terms.png]] +#+name: fig:test_id31_Kiff +#+caption: Bode plot of the decentralized IFF controller (\subref{fig:test_id31_Kiff_bode_plot}). The decentralized controller $K_{\text{IFF}}$ multiplied by the identified dynamics from $u_1$ to $V_{s1}$ for all payloads are shown in (\subref{fig:test_id31_Kiff_loop_gain}) +#+attr_latex: :options [htbp] +#+begin_figure +#+attr_latex: :caption \subcaption{\label{fig:test_id31_Kiff_bode_plot}Bode plot of $K_{\text{IFF}}$} +#+attr_latex: :options {0.49\textwidth} +#+begin_subfigure +#+attr_latex: :width 0.95\linewidth +[[file:figs/test_id31_Kiff_bode_plot.png]] +#+end_subfigure +#+attr_latex: :caption \subcaption{\label{fig:test_id31_Kiff_loop_gain}Decentralized Loop gains} +#+attr_latex: :options {0.49\textwidth} +#+begin_subfigure +#+attr_latex: :width 0.95\linewidth +[[file:figs/test_id31_Kiff_loop_gain.png]] +#+end_subfigure +#+end_figure + +To estimate the added damping, a root-locus plot is computed using the multi-body model (Figure ref:fig:test_id31_iff_root_locus_m0). +It can be seen that for all considered payloads, the poles are bounded to the "left-half plane" indicating that the decentralized IFF is robust. +The closed-loop poles for the chosen value of the gain are displayed by black crosses. +It can be seen that while damping can be added for all payloads (as compared to the open-loop case), the optimal value of the gain could be tuned separately for each payload. +For instance, for low payload masses, a higher value of the IFF controller gain could lead to better damping. +However, in this study, it was chosen to implement a fix (i.e. non-adaptive) decentralized IFF controller. -Root Locus to obtain optimal gain. #+begin_src matlab :exports none :results none %% Root Locus for IFF gains = logspace(-2, 2, 100); -Gm_iff_m0 = Gm_m0({'Fn1', 'Fn2', 'Fn3', 'Fn4', 'Fn5', 'Fn6'}, {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'}); -Gm_iff_m1 = Gm_m1({'Fn1', 'Fn2', 'Fn3', 'Fn4', 'Fn5', 'Fn6'}, {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'}); -Gm_iff_m2 = Gm_m2({'Fn1', 'Fn2', 'Fn3', 'Fn4', 'Fn5', 'Fn6'}, {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'}); -Gm_iff_m3 = Gm_m3({'Fn1', 'Fn2', 'Fn3', 'Fn4', 'Fn5', 'Fn6'}, {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'}); +Gm_iff_m0 = Gm_m0_Wz0({'Vs1', 'Vs2', 'Vs3', 'Vs4', 'Vs5', 'Vs6'}, {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'}); +Gm_iff_m1 = Gm_m1_Wz0({'Vs1', 'Vs2', 'Vs3', 'Vs4', 'Vs5', 'Vs6'}, {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'}); +Gm_iff_m2 = Gm_m2_Wz0({'Vs1', 'Vs2', 'Vs3', 'Vs4', 'Vs5', 'Vs6'}, {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'}); +Gm_iff_m3 = Gm_m3_Wz0({'Vs1', 'Vs2', 'Vs3', 'Vs4', 'Vs5', 'Vs6'}, {'u1', 'u2', 'u3', 'u4', 'u5', 'u6'}); +#+end_src +#+begin_src matlab :exports none :results none figure; -tiledlayout(1, 4, 'TileSpacing', 'compact', 'Padding', 'None'); - -ax1 = nexttile(); +tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None'); +nexttile(); hold on; plot(real(pole(Gm_iff_m0)), imag(pole(Gm_iff_m0)), 'x', 'color', colors(1,:), ... 'DisplayName', '$g = 0$'); @@ -2241,15 +2263,26 @@ end % Optimal gain clpoles = pole(feedback(Gm_iff_m0, Kiff, +1)); -plot(real(clpoles), imag(clpoles), 'x', 'color', colors(5,:), ... +plot(real(clpoles), imag(clpoles), 'kx', ... 'DisplayName', '$g_{opt}$'); hold off; axis equal; -xlim([-640, 0]); ylim([0, 1600]); +xlim([-600, 0]); ylim([0, 1500]); +xticks([-600:300:0]); +yticks([0:300:1500]); set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]); -title('$m_0$'); +xlabel('Real part'); ylabel('Imaginary part'); +#+end_src -ax2 = nexttile(); +#+begin_src matlab :tangle no :exports results :results file replace +exportFig('figs/test_id31_iff_root_locus_m0.pdf', 'width', 'third', 'height', 'normal'); +#+end_src + +#+begin_src matlab :exports none :results none +%% description +figure; +tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None'); +nexttile(); hold on; plot(real(pole(Gm_iff_m1)), imag(pole(Gm_iff_m1)), 'x', 'color', colors(2,:), ... 'DisplayName', '$g = 0$'); @@ -2264,15 +2297,23 @@ end % Optimal gain clpoles = pole(feedback(Gm_iff_m1, Kiff, +1)); -plot(real(clpoles), imag(clpoles), 'x', 'color', colors(5,:), ... +plot(real(clpoles), imag(clpoles), 'kx', ... 'DisplayName', '$g_{opt}$'); hold off; axis equal; -xlim([-320, 0]); ylim([0, 800]); +xlim([-200, 0]); ylim([0, 500]); set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]); -title('$m_1$'); +xlabel('Real part'); ylabel('Imaginary part'); +#+end_src -ax3 = nexttile(); +#+begin_src matlab :tangle no :exports results :results file replace +exportFig('figs/test_id31_iff_root_locus_m1.pdf', 'width', 'third', 'height', 'normal'); +#+end_src + +#+begin_src matlab :exports none :results none +figure; +tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None'); +nexttile(); hold on; plot(real(pole(Gm_iff_m2)), imag(pole(Gm_iff_m2)), 'x', 'color', colors(3,:), ... 'DisplayName', '$g = 0$'); @@ -2287,15 +2328,23 @@ end % Optimal gain clpoles = pole(feedback(Gm_iff_m2, Kiff, +1)); -plot(real(clpoles), imag(clpoles), 'x', 'color', colors(5,:), ... +plot(real(clpoles), imag(clpoles), 'kx', ... 'DisplayName', '$g_{opt}$'); hold off; axis equal; +xlim([-200, 0]); ylim([0, 500]); set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]); -xlim([-240, 0]); ylim([0, 600]); -title('$m_2$'); +xlabel('Real part'); ylabel('Imaginary part'); +#+end_src -ax4 = nexttile(); +#+begin_src matlab :tangle no :exports results :results file replace +exportFig('figs/test_id31_iff_root_locus_m2.pdf', 'width', 'third', 'height', 'normal'); +#+end_src + +#+begin_src matlab :exports none :results none +figure; +tiledlayout(1, 1, 'TileSpacing', 'compact', 'Padding', 'None'); +nexttile(); hold on; plot(real(pole(Gm_iff_m3)), imag(pole(Gm_iff_m3)), 'x', 'color', colors(4,:), ... 'DisplayName', '$g = 0$'); @@ -2310,50 +2359,658 @@ end % Optimal gain clpoles = pole(feedback(Gm_iff_m3, Kiff, +1)); -plot(real(clpoles), imag(clpoles), 'x', 'color', colors(5,:), ... +plot(real(clpoles), imag(clpoles), 'kx', ... 'DisplayName', '$g_{opt}$'); hold off; axis equal; +xlim([-200, 0]); ylim([0, 500]); set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]); -xlim([-160, 0]); ylim([0, 400]); -title('$m_3$'); +xlabel('Real part'); ylabel('Imaginary part'); #+end_src #+begin_src matlab :tangle no :exports results :results file replace -exportFig('figs/id31_iff_root_locus.pdf', 'width', 'full', 'height', 'tall'); +exportFig('figs/test_id31_iff_root_locus_m3.pdf', 'width', 'third', 'height', 'normal'); #+end_src -#+name: fig:id31_iff_root_locus -#+caption: Root Locus for IFF. Green crosses are closed-loop poles for the same choosen IFF gain. +#+name: fig:test_id31_iff_root_locus_m0 +#+caption: Root Locus plots for the designed decentralized IFF controller and using the multy-body model. Black crosses indicate the closed-loop poles for the choosen value of the gain. +#+attr_latex: :options [htbp] +#+begin_figure +#+attr_latex: :caption \subcaption{\label{fig:test_id31_iff_root_locus_m0}$m = 0\,\text{kg}$} +#+attr_latex: :options {0.24\textwidth} +#+begin_subfigure +#+attr_latex: :width 0.9\linewidth +[[file:figs/test_id31_iff_root_locus_m0.png]] +#+end_subfigure +#+attr_latex: :caption \subcaption{\label{fig:test_id31_iff_root_locus_m1}$m = 13\,\text{kg}$} +#+attr_latex: :options {0.24\textwidth} +#+begin_subfigure +#+attr_latex: :width 0.9\linewidth +[[file:figs/test_id31_iff_root_locus_m1.png]] +#+end_subfigure +#+attr_latex: :caption \subcaption{\label{fig:test_id31_iff_root_locus_m2}$m = 26\,\text{kg}$} +#+attr_latex: :options {0.24\textwidth} +#+begin_subfigure +#+attr_latex: :width 0.9\linewidth +[[file:figs/test_id31_iff_root_locus_m2.png]] +#+end_subfigure +#+attr_latex: :caption \subcaption{\label{fig:test_id31_iff_root_locus_m3}$m = 39\,\text{kg}$} +#+attr_latex: :options {0.24\textwidth} +#+begin_subfigure +#+attr_latex: :width 0.9\linewidth +[[file:figs/test_id31_iff_root_locus_m3.png]] +#+end_subfigure +#+end_figure + +** Estimated Damped Plant +<> + +As the model is accurately modelling the system dynamics, it can be used to estimate the damped plant, i.e. the transfer functions from $\bm{u}^\prime$ to $\bm{\mathcal{L}}$. +The obtained damped plants are compared with the open-loop plants in Figure ref:fig:test_id31_comp_ol_iff_plant_model. +The peak amplitudes corresponding to the suspension modes are approximately reduced by a factor $10$ for all considered payloads, and with the same decentralized IFF controller. + +#+begin_src matlab +%% Estimate damped plant from Multi-Body model +Gm_hac_m0_Wz0 = feedback(Gm_m0_Wz0, Kiff, 'name', +1); +Gm_hac_m1_Wz0 = feedback(Gm_m1_Wz0, Kiff, 'name', +1); +Gm_hac_m2_Wz0 = feedback(Gm_m2_Wz0, Kiff, 'name', +1); +Gm_hac_m3_Wz0 = feedback(Gm_m3_Wz0, Kiff, 'name', +1); + +% Check Stability +isstable(Gm_hac_m0_Wz0) +isstable(Gm_hac_m1_Wz0) +isstable(Gm_hac_m2_Wz0) +isstable(Gm_hac_m3_Wz0) +#+end_src + +#+begin_src matlab :exports none :tangle no +% The estimated damped plants from the multi-body model are saved +save('./matlab/mat/test_id31_simscape_damped_plants.mat', 'Gm_hac_m0_Wz0', 'Gm_hac_m1_Wz0', 'Gm_hac_m2_Wz0', 'Gm_hac_m3_Wz0'); +#+end_src + +#+begin_src matlab :eval no +% The estimated damped plants from the multi-body model are saved +save('./mat/test_id31_simscape_damped_plants.mat', 'Gm_hac_m0_Wz0', 'Gm_hac_m1_Wz0', 'Gm_hac_m2_Wz0', 'Gm_hac_m3_Wz0'); +#+end_src + +#+begin_src matlab :exports none :results none +%% Comparison of the open-loop plants and the estimated damped plant with IFF +figure; +tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None'); + +ax1 = nexttile([2,1]); +hold on; +plot(freqs, abs(squeeze(freqresp(Gm_m0_Wz0('eL1', 'u1'), freqs, 'Hz'))), 'color', [colors(1,:), 0.3], ... + 'DisplayName', '$-e\mathcal{L}_{1}/u_1$ - 0 kg'); +plot(freqs, abs(squeeze(freqresp(Gm_m1_Wz0('eL1', 'u1'), freqs, 'Hz'))), 'color', [colors(2,:), 0.3], ... + 'DisplayName', '$-e\mathcal{L}_{1}/u_1$ - 13 kg'); +plot(freqs, abs(squeeze(freqresp(Gm_m2_Wz0('eL1', 'u1'), freqs, 'Hz'))), 'color', [colors(3,:), 0.3], ... + 'DisplayName', '$-e\mathcal{L}_{1}/u_1$ - 26 kg'); +plot(freqs, abs(squeeze(freqresp(Gm_m3_Wz0('eL1', 'u1'), freqs, 'Hz'))), 'color', [colors(4,:), 0.3], ... + 'DisplayName', '$-e\mathcal{L}_{1}/u_1$ - 39 kg'); +plot(freqs, abs(squeeze(freqresp(Gm_hac_m0_Wz0('eL1', 'u1'), freqs, 'Hz'))), 'color', colors(1,:), ... + 'DisplayName', '$-e\mathcal{L}_{1}/u_1^\prime$ - 0 kg'); +plot(freqs, abs(squeeze(freqresp(Gm_hac_m1_Wz0('eL1', 'u1'), freqs, 'Hz'))), 'color', colors(2,:), ... + 'DisplayName', '$-e\mathcal{L}_{1}/u_1^\prime$ - 13 kg'); +plot(freqs, abs(squeeze(freqresp(Gm_hac_m2_Wz0('eL1', 'u1'), freqs, 'Hz'))), 'color', colors(3,:), ... + 'DisplayName', '$-e\mathcal{L}_{1}/u_1^\prime$ - 26 kg'); +plot(freqs, abs(squeeze(freqresp(Gm_hac_m3_Wz0('eL1', 'u1'), freqs, 'Hz'))), 'color', colors(4,:), ... + 'DisplayName', '$-e\mathcal{L}_{1}/u_1^\prime$ - 39 kg'); +hold off; +set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); +ylabel('Amplitude [m/V]'); set(gca, 'XTickLabel',[]); +ylim([1e-7, 4e-4]); +leg = legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 2); +leg.ItemTokenSize(1) = 15; + +ax2 = nexttile; +hold on; +plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_m0_Wz0('eL1','u1'), freqs, 'Hz'))), 'color', [colors(1,:), 0.3]); +plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_m1_Wz0('eL1','u1'), freqs, 'Hz'))), 'color', [colors(2,:), 0.3]); +plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_m2_Wz0('eL1','u1'), freqs, 'Hz'))), 'color', [colors(3,:), 0.3]); +plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_m3_Wz0('eL1','u1'), freqs, 'Hz'))), 'color', [colors(4,:), 0.3]); +plot(freqs, 180/pi*unwrap(angle(squeeze(freqresp(Gm_hac_m0_Wz0('eL1','u1'), freqs, 'Hz')))), 'color', colors(1,:)); +plot(freqs, 180/pi*unwrap(angle(squeeze(freqresp(Gm_hac_m1_Wz0('eL1','u1'), freqs, 'Hz')))), 'color', colors(2,:)); +plot(freqs, 180/pi*unwrap(angle(squeeze(freqresp(Gm_hac_m2_Wz0('eL1','u1'), freqs, 'Hz')))), 'color', colors(3,:)); +plot(freqs, 180/pi*unwrap(angle(squeeze(freqresp(Gm_hac_m3_Wz0('eL1','u1'), freqs, 'Hz')))), 'color', colors(4,:)); +hold off; +set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin'); +xlabel('Frequency [Hz]'); ylabel('Phase [deg]'); +hold off; +yticks(-360:90:360); +ylim([-20, 200]) + +linkaxes([ax1,ax2],'x'); +xlim([1, 1e3]); +#+end_src + +#+begin_src matlab :tangle no :exports results :results file replace +exportFig('figs/test_id31_comp_ol_iff_plant_model.pdf', 'width', 'wide', 'height', 600); +#+end_src + +#+name: fig:test_id31_comp_ol_iff_plant_model +#+caption: Comparison of the open-loop plants and the estimated damped plant with Decentralized IFF. #+RESULTS: -[[file:figs/id31_iff_root_locus.png]] +[[file:figs/test_id31_comp_ol_iff_plant_model.png]] -*** TODO Verify Stability -Verify Stability with Nyquist plot: +** Conclusion +:PROPERTIES: +:UNNUMBERED: t +:END: +* High Authority Control in the frame of the struts +:PROPERTIES: +:header-args:matlab+: :tangle matlab/test_id31_4_hac.m +:END: +<> +** Introduction :ignore: -- Why bad stability margins? +The position of the sample is actively stabilized by implementing a High-Authority-Controller as shown in Figure ref:fig:test_id31_iff_hac_schematic. + +\begin{equation}\label{eq:eq:test_id31_hac_diagonal} +\bm{K}_{\text{HAC}} = K_{\text{HAC}} \cdot \bm{I}_6 = \begin{bmatrix} +K_{\text{HAC}} & & 0 \\ +& \ddots & \\ +0 & & K_{\text{HAC}} +\end{bmatrix} +\end{equation} + +#+begin_src latex :file test_id31_iff_hac_schematic.pdf +\begin{tikzpicture} + % Blocs + \node[block={2.0cm}{2.0cm}] (P) {Plant}; + \coordinate[] (input) at ($(P.south west)!0.5!(P.north west)$); + \coordinate[] (outputH) at ($(P.south east)!0.2!(P.north east)$); + \coordinate[] (outputL) at ($(P.south east)!0.8!(P.north east)$); + + \node[block, above=0.6 of P] (Klac) {$\bm{K}_\text{IFF}$}; + \node[addb, left= of input] (addF) {}; + + \node[block, left= of addF] (Khac) {$\bm{K}_\text{HAC}$}; + + % Connections and labels + \draw[->] (outputL) -- ++(0.6, 0) coordinate(eastlac) |- (Klac.east); + \node[above right] at (outputL){$\bm{V}_s$}; + \draw[->] (Klac.west) -| (addF.north); + \draw[->] (addF.east) -- (input) node[above left]{$\bm{u}$}; + + \draw[->] (outputH) -- ++(1.6, 0) node[above left]{$\bm{\epsilon\mathcal{L}}$}; + \draw[->] (Khac.east) node[above right]{$\bm{u}^{\prime}$} -- (addF.west); + + \draw[->] ($(outputH) + (1.2, 0)$)node[branch]{} |- ($(Khac.west)+(-0.6, -1.6)$) |- (Khac.west); + + \begin{scope}[on background layer] + \node[fit={(Klac.north-|eastlac) (addF.west|-P.south)}, fill=black!20!white, draw, dashed, inner sep=10pt] (Pi) {}; + \node[anchor={north west}] at (Pi.north west){$\text{Damped Plant}$}; + \end{scope} +\end{tikzpicture} +#+end_src + +#+name: fig:test_id31_iff_hac_schematic +#+caption: Block diagram of the implemented HAC-IFF controllers. The controller $\bm{K}_{\text{HAC}}$ is a diagonal controller with the same elements on every diagonal term $K_{\text{HAC}}$. +#+RESULTS: +[[file:figs/test_id31_iff_hac_schematic.png]] + +** Matlab Init :noexport:ignore: +#+begin_src matlab +%% test_id31_4_hac.m +#+end_src + +#+begin_src matlab :tangle no :exports none :results silent :noweb yes :var current_dir=(file-name-directory buffer-file-name) +<> +#+end_src + +#+begin_src matlab :exports none :results silent :noweb yes +<> +#+end_src + +#+begin_src matlab :tangle no :noweb yes +<> +#+end_src + +#+begin_src matlab :eval no :noweb yes +<> +#+end_src + +#+begin_src matlab :noweb yes +<> +#+end_src + +#+begin_src matlab :noweb yes +<> +#+end_src + +** Damped Plant +<> + +The damped plants (i.e. the transfer function from $\bm{u}^\prime$ to $\bm{\epsilon\mathcal{L}}$) were identified for all payload conditions. +To verify if the model accurately represents the damped plants, both direct terms and coupling terms corresponding to the first actuator are compared in Figure ref:fig:test_id31_comp_simscape_hac. + +#+begin_src matlab +%% Identification of the damped Plant (transfer function from u' to dL) + +% Load identification data +data_m0 = load('2023-08-17_17-53_damp_plant_m0_Wz0.mat'); +data_m1 = load('2023-08-10_16-01_damp_plant_m1_Wz0.mat'); +data_m2 = load('2023-08-10_17-28_damp_plant_m2_Wz0.mat'); +data_m3 = load('2023-08-10_18-16_damp_plant_m3_Wz0.mat'); + +% Hannning Windows +Ts = 1e-4; % Sampling Time [s] +Nfft = floor(2.0/Ts); +win = hanning(Nfft); +Noverlap = floor(Nfft/2); + +% And we get the frequency vector +[~, f] = tfestimate(data_m0.uL1.id_plant, data_m0.uL1.e_L1, win, Noverlap, Nfft, 1/Ts); + +% Identification without any payload +G_hac_m0_Wz0 = zeros(length(f), 6, 6); +for i_strut = 1:6 + eL = [data_m0.(sprintf("uL%i", i_strut)).e_L1 ; data_m0.(sprintf("uL%i", i_strut)).e_L2 ; data_m0.(sprintf("uL%i", i_strut)).e_L3 ; data_m0.(sprintf("uL%i", i_strut)).e_L4 ; data_m0.(sprintf("uL%i", i_strut)).e_L5 ; data_m0.(sprintf("uL%i", i_strut)).e_L6]'; + + G_hac_m0_Wz0(:,:,i_strut) = tfestimate(data_m0.(sprintf("uL%i", i_strut)).id_plant, -detrend(eL, 0), win, Noverlap, Nfft, 1/Ts); +end + +% Identification with 1 "payload layer" +G_hac_m1_Wz0 = zeros(length(f), 6, 6); +for i_strut = 1:6 + eL = [data_m1.(sprintf("uL%i", i_strut)).e_L1 ; data_m1.(sprintf("uL%i", i_strut)).e_L2 ; data_m1.(sprintf("uL%i", i_strut)).e_L3 ; data_m1.(sprintf("uL%i", i_strut)).e_L4 ; data_m1.(sprintf("uL%i", i_strut)).e_L5 ; data_m1.(sprintf("uL%i", i_strut)).e_L6]'; + + G_hac_m1_Wz0(:,:,i_strut) = tfestimate(data_m1.(sprintf("uL%i", i_strut)).id_plant, -detrend(eL, 0), win, Noverlap, Nfft, 1/Ts); +end + +% Identification with 2 "payload layers" +G_hac_m2_Wz0 = zeros(length(f), 6, 6); +for i_strut = 1:6 + eL = [data_m2.(sprintf("uL%i", i_strut)).e_L1 ; data_m2.(sprintf("uL%i", i_strut)).e_L2 ; data_m2.(sprintf("uL%i", i_strut)).e_L3 ; data_m2.(sprintf("uL%i", i_strut)).e_L4 ; data_m2.(sprintf("uL%i", i_strut)).e_L5 ; data_m2.(sprintf("uL%i", i_strut)).e_L6]'; + + G_hac_m2_Wz0(:,:,i_strut) = tfestimate(data_m2.(sprintf("uL%i", i_strut)).id_plant, -detrend(eL, 0), win, Noverlap, Nfft, 1/Ts); +end + +% Identification with 3 "payload layers" +G_hac_m3_Wz0 = zeros(length(f), 6, 6); +for i_strut = 1:6 + eL = [data_m3.(sprintf("uL%i", i_strut)).e_L1 ; data_m3.(sprintf("uL%i", i_strut)).e_L2 ; data_m3.(sprintf("uL%i", i_strut)).e_L3 ; data_m3.(sprintf("uL%i", i_strut)).e_L4 ; data_m3.(sprintf("uL%i", i_strut)).e_L5 ; data_m3.(sprintf("uL%i", i_strut)).e_L6]'; + + G_hac_m3_Wz0(:,:,i_strut) = tfestimate(data_m3.(sprintf("uL%i", i_strut)).id_plant, -detrend(eL, 0), win, Noverlap, Nfft, 1/Ts); +end +#+end_src + +#+begin_src matlab :exports none :tangle no +% The identified dynamics are then saved for further use. +save('./matlab/mat/test_id31_identified_damped_plants.mat', 'G_hac_m0_Wz0', 'G_hac_m1_Wz0', 'G_hac_m2_Wz0', 'G_hac_m3_Wz0', 'f'); +#+end_src + +#+begin_src matlab :eval no +% The identified dynamics are then saved for further use. +save('./mat/test_id31_identified_damped_plants.mat', 'G_hac_m0_Wz0', 'G_hac_m1_Wz0', 'G_hac_m2_Wz0', 'G_hac_m3_Wz0', 'f'); +#+end_src + +#+begin_src matlab +% Load the estimated damped plant from the multi-body model +load('test_id31_simscape_damped_plants.mat', 'Gm_hac_m0_Wz0', 'Gm_hac_m1_Wz0', 'Gm_hac_m2_Wz0', 'Gm_hac_m3_Wz0'); +% Load the undamped plant for comparison +load('test_id31_identified_open_loop_plants.mat', 'G_int_m0_Wz0', 'G_int_m1_Wz0', 'G_int_m2_Wz0', 'G_int_m3_Wz0', 'f'); +#+end_src + +#+begin_src matlab :exports none +figure; +tiledlayout(2, 3, 'TileSpacing', 'tight', 'Padding', 'tight'); + +ax1 = nexttile(); +hold on; +plot(f, abs(G_hac_m0_Wz0(:, 1, 1))); +plot(freqs, abs(squeeze(freqresp(Gm_hac_m0_Wz0('eL1', 'u1'), freqs, 'Hz')))); +text(12, 3e-5, '$\epsilon_{\mathcal{L}1}/u_1^\prime$', 'Horiz','left', 'Vert','top') +hold off; +set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); +set(gca, 'XTickLabel',[]); ylabel('Amplitude [m/V]'); +yticks([1e-7, 1e-6, 1e-5]); + +ax2 = nexttile(); +hold on; +plot(f, abs(G_hac_m0_Wz0(:, 2, 1))); +plot(freqs, abs(squeeze(freqresp(Gm_hac_m0_Wz0('eL2', 'u1'), freqs, 'Hz')))); +text(12, 3e-5, '$\epsilon_{\mathcal{L}2}/u_1^\prime$', 'Horiz','left', 'Vert','top') +hold off; +set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); +set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]); + +ax3 = nexttile(); +hold on; +plot(f, abs(G_hac_m0_Wz0(:, 3, 1))) +plot(freqs, abs(squeeze(freqresp(Gm_hac_m0_Wz0('eL3', 'u1'), freqs, 'Hz')))) +text(12, 3e-5, '$\epsilon_{\mathcal{L}3}/u_1^\prime$', 'Horiz','left', 'Vert','top') +hold off; +set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); +set(gca, 'XTickLabel',[]); set(gca, 'YTickLabel',[]); + +ax4 = nexttile(); +hold on; +plot(f, abs(G_hac_m0_Wz0(:, 4, 1))); +plot(freqs, abs(squeeze(freqresp(Gm_hac_m0_Wz0('eL4', 'u1'), freqs, 'Hz')))); +text(12, 3e-5, '$\epsilon_{\mathcal{L}4}/u_1^\prime$', 'Horiz','left', 'Vert','top') +hold off; +set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); +xlabel('Frequency [Hz]'); ylabel('Amplitude [m/V]'); +xticks([10, 20, 50, 100, 200]) +yticks([1e-7, 1e-6, 1e-5]); + +ax5 = nexttile(); +hold on; +plot(f, abs(G_hac_m0_Wz0(:, 5, 1))); +plot(freqs, abs(squeeze(freqresp(Gm_hac_m0_Wz0('eL5', 'u1'), freqs, 'Hz')))); +text(12, 3e-5, '$\epsilon_{\mathcal{L}5}/u_1^\prime$', 'Horiz','left', 'Vert','top') +hold off; +xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]); +set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); +xticks([10, 20, 50, 100, 200]) + +ax6 = nexttile(); +hold on; +plot(f, abs(G_hac_m0_Wz0(:, 6, 1)), ... + 'DisplayName', 'Measurements'); +plot(freqs, abs(squeeze(freqresp(Gm_hac_m0_Wz0('eL6', 'u1'), freqs, 'Hz'))), ... + 'DisplayName', 'Model (2-DoF APA)'); +text(12, 3e-5, '$\epsilon_{\mathcal{L}6}/u_1^\prime$', 'Horiz','left', 'Vert','top') +hold off; +set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); +xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]); +xticks([10, 20, 50, 100, 200]) +leg = legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1); +leg.ItemTokenSize(1) = 15; + +linkaxes([ax1,ax2,ax3,ax4,ax5,ax6],'xy'); +xlim([10, 5e2]); ylim([1e-7, 4e-5]); +#+end_src + +#+begin_src matlab :tangle no :exports results :results file replace +exportFig('figs/test_id31_comp_simscape_hac.pdf', 'width', 'full', 'height', 700); +#+end_src + +#+name: fig:test_id31_comp_simscape_hac +#+caption: Comparison of the measured (in blue) and modeled (in red) frequency transfer functions from the first control signal ($u_1^\prime$) of the damped plant to the estimated errors ($\epsilon_{\mathcal{L}_i}$) in the frame of the six struts by the external metrology +#+RESULTS: +[[file:figs/test_id31_comp_simscape_hac.png]] + +The six direct terms for all four payload conditions are compared with the model in Figure ref:fig:test_id31_hac_plant_effect_mass. +It is shown that the model accurately represents the dynamics for all payloads. + +In Section ref:sec:test_id31_iff_hac, a High Authority Controller is tuned to be robust to the change of dynamics due to different payloads used. +Without decentralized IFF being applied, the controller would have had to be robust to all the undamped dynamics shown in Figure ref:fig:test_id31_comp_all_undamped_damped_plants, which is a very complex control problem. +With the applied decentralized IFF, the HAC instead had to be be robust to all the damped dynamics shown in Figure ref:fig:test_id31_comp_all_undamped_damped_plants, which is easier from a control perspective. +This is one of the key benefit of using the HAC-LAC strategy. + +#+begin_src matlab :exports none :results none +%% Comparison of the identified HAC plant and the HAC plant extracted from the simscape model +figure; +tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None'); + +ax1 = nexttile([2,1]); +hold on; +plot(f, abs(G_hac_m0_Wz0(:, 1, 1)), 'color', [colors(1,:), 0.2], ... + 'DisplayName', '$m = 0$ kg'); +for i = 2:6 + plot(f, abs(G_hac_m0_Wz0(:,i, i)), 'color', [colors(1,:), 0.2], ... + 'HandleVisibility', 'off') +end +plot(f, abs(G_hac_m1_Wz0(:, 1, 1)), 'color', [colors(2,:), 0.2], ... + 'DisplayName', '$m = 13$ kg'); +for i = 2:6 + plot(f, abs(G_hac_m1_Wz0(:,i, i)), 'color', [colors(2,:), 0.2], ... + 'HandleVisibility', 'off') +end +plot(f, abs(G_hac_m2_Wz0(:, 1, 1)), 'color', [colors(3,:), 0.2], ... + 'DisplayName', '$m = 26$ kg'); +for i = 2:6 + plot(f, abs(G_hac_m2_Wz0(:,i, i)), 'color', [colors(3,:), 0.2], ... + 'HandleVisibility', 'off') +end +plot(f, abs(G_hac_m3_Wz0(:, 1, 1)), 'color', [colors(4,:), 0.2], ... + 'DisplayName', '$m = 39$ kg'); +for i = 2:6 + plot(f, abs(G_hac_m3_Wz0(:,i, i)), 'color', [colors(4,:), 0.2], ... + 'HandleVisibility', 'off') +end +plot(freqs, abs(squeeze(freqresp(Gm_hac_m0_Wz0('eL1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(1,:), ... + 'DisplayName', '$m = 0$ kg (model)'); +plot(freqs, abs(squeeze(freqresp(Gm_hac_m1_Wz0('eL1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(2,:), ... + 'DisplayName', '$m = 13$ kg (model)'); +plot(freqs, abs(squeeze(freqresp(Gm_hac_m2_Wz0('eL1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(3,:), ... + 'DisplayName', '$m = 26$ kg (model)'); +plot(freqs, abs(squeeze(freqresp(Gm_hac_m3_Wz0('eL1', 'u1'), freqs, 'Hz'))), '--', 'color', colors(4,:), ... + 'DisplayName', '$m = 39$ kg (model)'); +hold off; +set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); +ylabel('Amplitude [m/V]'); set(gca, 'XTickLabel',[]); +ylim([2e-7, 3e-5]); +leg = legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 2); +leg.ItemTokenSize(1) = 15; + +ax2 = nexttile; +hold on; +plot(f, 180/pi*unwrapphase(angle(G_hac_m0_Wz0(:,1,1)), f), 'color', [colors(1,:), 0.2]); +for i = 2:6 + plot(f, 180/pi*unwrapphase(angle(G_hac_m0_Wz0(:,i, i)), f), 'color', [colors(1,:), 0.2]); +end +plot(f, 180/pi*unwrapphase(angle(G_hac_m1_Wz0(:,1,1)), f), 'color', [colors(2,:), 0.2]); +for i = 2:6 + plot(f, 180/pi*unwrapphase(angle(G_hac_m1_Wz0(:,i, i)), f), 'color', [colors(2,:), 0.2]); +end +plot(f, 180/pi*unwrapphase(angle(G_hac_m2_Wz0(:,1,1)), f), 'color', [colors(3,:), 0.2]); +for i = 2:6 + plot(f, 180/pi*unwrapphase(angle(G_hac_m2_Wz0(:,i, i)), f), 'color', [colors(3,:), 0.2]); +end +plot(f, 180/pi*unwrapphase(angle(G_hac_m3_Wz0(:,1,1)), f), 'color', [colors(4,:), 0.2]); +for i = 2:6 + plot(f, 180/pi*unwrapphase(angle(G_hac_m3_Wz0(:,i, i)), f), 'color', [colors(4,:), 0.2]); +end +plot(freqs, 180/pi*unwrapphase(angle(squeeze(freqresp(-exp(-3e-4*s)*Gm_hac_m0_Wz0('eL1', 'u1'), freqs, 'Hz'))), f), '--', 'color', colors(1,:)); +plot(freqs, 180/pi*unwrapphase(angle(squeeze(freqresp(-exp(-3e-4*s)*Gm_hac_m1_Wz0('eL1', 'u1'), freqs, 'Hz'))), f), '--', 'color', colors(2,:)); +plot(freqs, 180/pi*unwrapphase(angle(squeeze(freqresp(-exp(-3e-4*s)*Gm_hac_m2_Wz0('eL1', 'u1'), freqs, 'Hz'))), f), '--', 'color', colors(3,:)); +plot(freqs, 180/pi*unwrapphase(angle(squeeze(freqresp(-exp(-3e-4*s)*Gm_hac_m3_Wz0('eL1', 'u1'), freqs, 'Hz'))), f), '--', 'color', colors(4,:)); +hold off; +set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin'); +xlabel('Frequency [Hz]'); ylabel('Phase [deg]'); +hold off; +yticks(-360:90:360); +ylim([-270, 20]) + +linkaxes([ax1,ax2],'x'); +xlim([1, 5e2]); +#+end_src + +#+begin_src matlab :tangle no :exports results :results file none +exportFig('figs/test_id31_hac_plant_effect_mass.pdf', 'width', 'half', 'height', 600); +#+end_src + +#+begin_src matlab :exports none :results none +%% Comparison of all the undamped FRF and all the damped FRF +figure; +tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None'); + +ax1 = nexttile([2,1]); +hold on; +plot(f, abs(G_int_m0_Wz0(:,1,1)), 'color', [colors(1,:), 0.5], 'DisplayName', 'Undamped - $\epsilon\mathcal{L}_i/u_i$'); +plot(f, abs(G_hac_m0_Wz0(:,1,1)), 'color', [colors(2,:), 0.5], 'DisplayName', 'damped - $\epsilon\mathcal{L}_i/u_i^\prime$'); +for i = 1:6 + plot(f, abs(G_int_m0_Wz0(:,i, i)), 'color', [colors(1,:), 0.5], 'HandleVisibility', 'off'); + plot(f, abs(G_int_m1_Wz0(:,i, i)), 'color', [colors(1,:), 0.5], 'HandleVisibility', 'off'); + plot(f, abs(G_int_m2_Wz0(:,i, i)), 'color', [colors(1,:), 0.5], 'HandleVisibility', 'off'); + plot(f, abs(G_int_m3_Wz0(:,i, i)), 'color', [colors(1,:), 0.5], 'HandleVisibility', 'off'); +end +for i = 1:6 + plot(f, abs(G_hac_m0_Wz0(:,i, i)), 'color', [colors(2,:), 0.5], 'HandleVisibility', 'off'); + plot(f, abs(G_hac_m1_Wz0(:,i, i)), 'color', [colors(2,:), 0.5], 'HandleVisibility', 'off'); + plot(f, abs(G_hac_m2_Wz0(:,i, i)), 'color', [colors(2,:), 0.5], 'HandleVisibility', 'off'); + plot(f, abs(G_hac_m3_Wz0(:,i, i)), 'color', [colors(2,:), 0.5], 'HandleVisibility', 'off'); +end +hold off; +set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); +ylabel('Amplitude [m/V]'); set(gca, 'XTickLabel',[]); +leg = legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1); +leg.ItemTokenSize(1) = 15; +ylim([2e-7, 4e-4]); + +ax2 = nexttile; +hold on; +for i =1:6 + plot(f, 180/pi*unwrapphase(angle(-G_int_m0_Wz0(:,i, i)), f), 'color', [colors(1,:), 0.5]); + plot(f, 180/pi*unwrapphase(angle(-G_int_m1_Wz0(:,i, i)), f), 'color', [colors(1,:), 0.5]); + plot(f, 180/pi*unwrapphase(angle(-G_int_m2_Wz0(:,i, i)), f), 'color', [colors(1,:), 0.5]); + plot(f, 180/pi*unwrapphase(angle(-G_int_m3_Wz0(:,i, i)), f), 'color', [colors(1,:), 0.5]); +end +for i = 1:6 + plot(f, 180/pi*unwrapphase(angle(G_hac_m0_Wz0(:,i, i)), f), 'color', [colors(2,:), 0.5]); + plot(f, 180/pi*unwrapphase(angle(G_hac_m1_Wz0(:,i, i)), f), 'color', [colors(2,:), 0.5]); + plot(f, 180/pi*unwrapphase(angle(G_hac_m2_Wz0(:,i, i)), f), 'color', [colors(2,:), 0.5]); + plot(f, 180/pi*unwrapphase(angle(G_hac_m3_Wz0(:,i, i)), f), 'color', [colors(2,:), 0.5]); +end +hold off; +set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin'); +xlabel('Frequency [Hz]'); ylabel('Phase [deg]'); +hold off; +yticks(-360:90:360); +ylim([-270, 20]) + +linkaxes([ax1,ax2],'x'); +xlim([1, 5e2]); +#+end_src + +#+begin_src matlab :tangle no :exports results :results file none +exportFig('figs/test_id31_comp_all_undamped_damped_plants.pdf', 'width', 'half', 'height', 600); +#+end_src + +#+name: fig:test_id31_hac_plant_effect_mass_comp_model +#+caption: Comparison of the measured damped plants and modeled plants for all considered payloads, only "direct" terms ($\epsilon\mathcal{L}_i/u_i^\prime$) are displayed (\subref{fig:test_id31_hac_plant_effect_mass}). Comparison of all undamped $\epsilon\mathcal{L}_i/u_i$ and damped $\epsilon\mathcal{L}_i/u_i^\prime$ measured frequency response functions for all payloads is done in (\subref{fig:test_id31_comp_all_undamped_damped_plants}). +#+attr_latex: :options [htbp] +#+begin_figure +#+attr_latex: :caption \subcaption{\label{fig:test_id31_hac_plant_effect_mass}Effect of payload mass on $\epsilon\mathcal{L}_i/u_i^\prime$} +#+attr_latex: :options {0.49\textwidth} +#+begin_subfigure +#+attr_latex: :width 0.95\linewidth +[[file:figs/test_id31_hac_plant_effect_mass.png]] +#+end_subfigure +#+attr_latex: :caption \subcaption{\label{fig:test_id31_comp_all_undamped_damped_plants}Undamped and damped plants} +#+attr_latex: :options {0.49\textwidth} +#+begin_subfigure +#+attr_latex: :width 0.95\linewidth +[[file:figs/test_id31_comp_all_undamped_damped_plants.png]] +#+end_subfigure +#+end_figure + +** Robust Controller Design +<> + +A first diagonal controller was designed to be robust to change of payloads, which means that every damped plants shown in Figure ref:fig:test_id31_comp_all_undamped_damped_plants should be considered during the controller design. +For a first design, a crossover frequency of $5\,\text{Hz}$ for chosen. +One integrator is added to increase the low frequency gain, a lead is added around $5\,\text{Hz}$ to increase the stability margins and a first order low pass filter with a cut-off frequency of $30\,\text{Hz}$ is added to improve the robustness to dynamical uncertainty at high frequency. +The obtained "decentralized" loop-gains are shown in Figure ref:fig:test_id31_hac_loop_gain. + +\begin{equation}\label{eq:test_id31_robust_hac} +K_{\text{HAC}} = g_0 \cdot \underbrace{\frac{\omega_c}{s}}_{\text{int}} \cdot \underbrace{\frac{1}{\sqrt{\alpha}}\frac{1 + \frac{s}{\omega_c/\sqrt{\alpha}}}{1 + \frac{s}{\omega_c\sqrt{\alpha}}}}_{\text{lead}} \cdot \underbrace{\frac{1}{1 + \frac{s}{\omega_0}}}_{\text{LPF}}, \quad \left( \omega_c = 2\pi5\,\text{rad/s},\ \alpha = 2,\ \omega_0 = 2\pi30\,\text{rad/s} \right) +\end{equation} + +The closed-loop stability is verified by computing the characteristic Loci (Figure ref:fig:test_id31_hac_characteristic_loci). + +#+begin_src matlab +%% HAC Design +% Wanted crossover +wc = 2*pi*5; % [rad/s] + +% Integrator +H_int = wc/s; + +% Lead to increase phase margin +a = 2; % Amount of phase lead / width of the phase lead / high frequency gain +H_lead = 1/sqrt(a)*(1 + s/(wc/sqrt(a)))/(1 + s/(wc*sqrt(a))); + +% Low Pass filter to increase robustness +H_lpf = 1/(1 + s/2/pi/30); + +% Gain to have unitary crossover at 5Hz +[~, i_f] = min(abs(f - wc/2/pi)); +H_gain = 1./abs(G_hac_m0_Wz0(i_f, 1, 1)); + +% Decentralized HAC +Khac = H_gain * ... % Gain + H_int * ... % Integrator + H_lpf * ... % Low Pass filter + eye(6); % 6x6 Diagonal +#+end_src + +#+begin_src matlab :exports none :tangle no +% The designed HAC controller is saved +save('./matlab/mat/test_id31_K_hac_robust.mat', 'Khac'); +#+end_src + +#+begin_src matlab :eval no +% The designed HAC controller is saved +save('./mat/test_id31_K_hac_robust.mat', 'Khac'); +#+end_src + +#+begin_src matlab :exports none :results none +%% Decentralized Loop gain for the High Authority Controller +figure; +tiledlayout(3, 1, 'TileSpacing', 'Compact', 'Padding', 'None'); + +ax1 = nexttile([2,1]); +hold on; +plot(f, abs(G_hac_m0_Wz0(:,1, 1).*squeeze(freqresp(Khac(1,1), f, 'Hz'))), 'color', colors(1,:), 'DisplayName', '$0$ kg'); +plot(f, abs(G_hac_m1_Wz0(:,1, 1).*squeeze(freqresp(Khac(1,1), f, 'Hz'))), 'color', colors(2,:), 'DisplayName', '$13$ kg'); +plot(f, abs(G_hac_m2_Wz0(:,1, 1).*squeeze(freqresp(Khac(1,1), f, 'Hz'))), 'color', colors(3,:), 'DisplayName', '$26$ kg'); +plot(f, abs(G_hac_m3_Wz0(:,1, 1).*squeeze(freqresp(Khac(1,1), f, 'Hz'))), 'color', colors(4,:), 'DisplayName', '$39$ kg'); +xline(5, '--', 'linewidth', 1, 'color', [0,0,0,0.2], 'HandleVisibility', 'off') +hold off; +set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); +ylabel('Loop Gain'); set(gca, 'XTickLabel',[]); +ylim([1e-5, 1e2]); +leg = legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 2); +leg.ItemTokenSize(1) = 15; + +ax2 = nexttile; +hold on; +plot(f, 180/pi*angle(G_hac_m0_Wz0(:,1, 1).*squeeze(freqresp(Khac(1,1), f, 'Hz'))), 'color', colors(1,:)); +plot(f, 180/pi*angle(G_hac_m1_Wz0(:,1, 1).*squeeze(freqresp(Khac(1,1), f, 'Hz'))), 'color', colors(2,:)); +plot(f, 180/pi*angle(G_hac_m2_Wz0(:,1, 1).*squeeze(freqresp(Khac(1,1), f, 'Hz'))), 'color', colors(3,:)); +plot(f, 180/pi*angle(G_hac_m3_Wz0(:,1, 1).*squeeze(freqresp(Khac(1,1), f, 'Hz'))), 'color', colors(4,:)); +xline(5, '--', 'linewidth', 1, 'color', [0,0,0,0.2]) +hold off; +set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin'); +xlabel('Frequency [Hz]'); ylabel('Phase [deg]'); +hold off; +yticks(-360:90:360); +ylim([-180, 180]) + +linkaxes([ax1,ax2],'x'); +xlim([1, 1e3]); +#+end_src + +#+begin_src matlab :tangle no :exports results :results file none +exportFig('figs/test_id31_hac_loop_gain.pdf', 'width', 'half', 'height', 600); +#+end_src #+begin_src matlab :exports none %% Compute the Eigenvalues of the loop gain Ldet = zeros(4, 6, length(f)); % Loop gain -Lmimo = pagemtimes(permute(G_iff_m0, [2,3,1]),squeeze(freqresp(Kiff, f, 'Hz'))); +Lmimo = pagemtimes(permute(G_hac_m0, [2,3,1]),squeeze(freqresp(Khac, f, 'Hz'))); for i_f = 2:length(f) Ldet(1,:, i_f) = eig(squeeze(Lmimo(:,:,i_f))); end -Lmimo = pagemtimes(permute(G_iff_m1, [2,3,1]),squeeze(freqresp(Kiff, f, 'Hz'))); +Lmimo = pagemtimes(permute(G_hac_m1, [2,3,1]),squeeze(freqresp(Khac, f, 'Hz'))); for i_f = 2:length(f) Ldet(2,:, i_f) = eig(squeeze(Lmimo(:,:,i_f))); end -Lmimo = pagemtimes(permute(G_iff_m2, [2,3,1]),squeeze(freqresp(Kiff, f, 'Hz'))); +Lmimo = pagemtimes(permute(G_hac_m2, [2,3,1]),squeeze(freqresp(Khac, f, 'Hz'))); for i_f = 2:length(f) Ldet(3,:, i_f) = eig(squeeze(Lmimo(:,:,i_f))); end -Lmimo = pagemtimes(permute(G_iff_m3, [2,3,1]),squeeze(freqresp(Kiff, f, 'Hz'))); +Lmimo = pagemtimes(permute(G_hac_m3, [2,3,1]),squeeze(freqresp(Khac, f, 'Hz'))); for i_f = 2:length(f) Ldet(4,:, i_f) = eig(squeeze(Lmimo(:,:,i_f))); end @@ -2366,7 +3023,7 @@ hold on; for i_mass = 1:4 plot(real(squeeze(Ldet(i_mass, 1,:))), imag(squeeze(Ldet(i_mass, 1,:))), ... '.', 'color', colors(i_mass, :), ... - 'DisplayName', sprintf('%i masses', i_mass)); + 'DisplayName', sprintf('$m_%i$', i_mass)); plot(real(squeeze(Ldet(i_mass, 1,:))), -imag(squeeze(Ldet(i_mass, 1,:))), ... '.', 'color', colors(i_mass, :), ... 'HandleVisibility', 'off'); @@ -2384,120 +3041,2147 @@ hold off; set(gca, 'XScale', 'lin'); set(gca, 'YScale', 'lin'); xlabel('Real'); ylabel('Imag'); legend('location', 'southeast'); -xlim([-3, 1]); ylim([-2, 2]); +axis square +xlim([-1.5, 0.5]); ylim([-1, 1]); +#+end_src + +#+begin_src matlab :tangle no :exports results :results file none +exportFig('figs/test_id31_hac_characteristic_loci.pdf', 'width', 'half', 'height', 600); +#+end_src + +#+name: fig:test_id31_hac_loop_gain_loci +#+caption: Robust High Authority Controller. "Decentralized loop-gains" are shown in (\subref{fig:test_id31_hac_loop_gain}) and characteristic loci are shown in (\subref{fig:test_id31_hac_characteristic_loci}) +#+attr_latex: :options [htbp] +#+begin_figure +#+attr_latex: :caption \subcaption{\label{fig:test_id31_hac_loop_gain}Loop Gains} +#+attr_latex: :options {0.49\textwidth} +#+begin_subfigure +#+attr_latex: :width 0.95\linewidth +[[file:figs/test_id31_hac_loop_gain.png]] +#+end_subfigure +#+attr_latex: :caption \subcaption{\label{fig:test_id31_hac_characteristic_loci}Characteristic Loci} +#+attr_latex: :options {0.49\textwidth} +#+begin_subfigure +#+attr_latex: :width 0.95\linewidth +[[file:figs/test_id31_hac_characteristic_loci.png]] +#+end_subfigure +#+end_figure + +** Estimation of performances +<> + +To estimate the performances that can be expected with this HAC-LAC architecture and the designed controllers, two simulations of tomography experiments were performed[fn:4]. +The rotational velocity was set to 30rpm, and no payload was added on top of the nano-hexapod. +An open-loop simulation and a closed-loop simulation were performed and compared in Figure ref:fig:test_id31_tomo_m0_30rpm_robust_hac_iff_sim. + +#+begin_src matlab +%% Tomography experiment +% Sample is not centered with the rotation axis +% This is done by offsetfing the micro-hexapod by 0.9um +P_micro_hexapod = [2.5e-6; 0; -0.3e-6]; % [m] + +set_param(mdl, 'StopTime', '3'); % 6 turns at 30rpm + +initializeGround(); +initializeGranite(); +initializeTy(); +initializeRy(); +initializeRz(); +initializeMicroHexapod('AP', P_micro_hexapod); +initializeNanoHexapod('flex_bot_type', '2dof', ... + 'flex_top_type', '3dof', ... + 'motion_sensor_type', 'plates', ... + 'actuator_type', '2dof'); +initializeSample('type', '0'); + +initializeSimscapeConfiguration('gravity', false); +initializeLoggingConfiguration('log', 'all', 'Ts', 1e-4); +initializeController('type', 'open-loop'); + +initializeDisturbances(... + 'Dw_x', true, ... % Ground Motion - X direction + 'Dw_y', true, ... % Ground Motion - Y direction + 'Dw_z', true, ... % Ground Motion - Z direction + 'Fdy_x', false, ... % Translation Stage - X direction + 'Fdy_z', false, ... % Translation Stage - Z direction + 'Frz_x', true, ... % Spindle - X direction + 'Frz_y', true, ... % Spindle - Y direction + 'Frz_z', true); % Spindle - Z direction + +initializeReferences(... + 'Rz_type', 'rotating', ... + 'Rz_period', 360/180, ... % 180deg/s, 30rpm + 'Dh_pos', [P_micro_hexapod; 0; 0; 0]); + +% Open-Loop Simulation +sim(mdl); +exp_tomo_ol_m0_Wz180 = simout; + +% Closed-Loop Simulation +load('test_id31_K_iff.mat', 'Kiff'); +load('test_id31_K_hac_robust.mat', 'Khac'); +initializeController('type', 'hac-iff'); +initializeSample('type', '0'); +sim(mdl); +exp_tomo_cl_m0_Wz180 = simout; #+end_src -*** Save Controller #+begin_src matlab :exports none :tangle no -K_order = order(Kiff(1,1)); - -Kz = c2d(Kiff(1,1)*(1 + s/2/pi/2e3)^(9-K_order)/(1 + s/2/pi/2e3)^(9-K_order), 1e-4); -[num, den] = tfdata(Kz, 'v'); - -formatSpec = '%.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e %.18e\n'; -fileID = fopen('/home/thomas/mnt/data_id31/nass/controllers/K_iff.dat', 'w'); -fprintf(fileID, formatSpec, [num; den]'); -fclose(fileID); +% Save the simulation results +save('./matlab/mat/test_id31_exp_tomo_ol_cl_30rpm_sim.mat', 'exp_tomo_ol_m0_Wz180', 'exp_tomo_cl_m0_Wz180'); #+end_src -#+begin_src matlab -save('./matlab/mat/K_iff.mat', 'Kiff') -#+end_src - -** Estimated Damped Plant -#+begin_src matlab -%% Damped plant from Simscape model -Gm_hac_m0 = -feedback(Gm_m0, Kiff, 'name', +1); -Gm_hac_m1 = -feedback(Gm_m1, Kiff, 'name', +1); -Gm_hac_m2 = -feedback(Gm_m2, Kiff, 'name', +1); -Gm_hac_m3 = -feedback(Gm_m3, Kiff, 'name', +1); -#+end_src - -#+begin_src matlab -%% Verify Stability -isstable(Gm_hac_m0) -isstable(Gm_hac_m1) -isstable(Gm_hac_m2) -isstable(Gm_hac_m3) -#+end_src - -#+begin_src matlab -%% Save Damped Plants -save('./matlab/mat/Gm.mat', 'Gm_hac_m0', 'Gm_hac_m1', 'Gm_hac_m2', 'Gm_hac_m3', '-append'); +#+begin_src matlab :eval no +% Save the simulation results +save('./mat/test_id31_exp_tomo_ol_cl_30rpm_sim.mat', 'exp_tomo_ol_m0_Wz180', 'exp_tomo_cl_m0_Wz180'); #+end_src #+begin_src matlab :exports none :results none -%% Estimated damped plant from the Simscape model +%% Simulation of tomography experiment - no payload, 30rpm - XY errors figure; -tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'None'); - -ax1 = nexttile([2,1]); hold on; -plot(freqs, abs(squeeze(freqresp(Gm_hac_m0(sprintf('eL%i', 1), sprintf('u%i', 1)), freqs, 'Hz'))), 'color', [colors(1,:), 0.5], ... - 'DisplayName', '$\tau_{m,i}/u_i$ - $m_0$'); -for i = 2:6 - plot(freqs, abs(squeeze(freqresp(Gm_hac_m0(sprintf('eL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(1,:), 0.5], ... - 'HandleVisibility', 'off'); -end -plot(freqs, abs(squeeze(freqresp(Gm_hac_m1(sprintf('eL%i', 1), sprintf('u%i', 1)), freqs, 'Hz'))), 'color', [colors(2,:), 0.5], ... - 'DisplayName', '$\tau_{m,i}/u_i$ - $m_1$'); -for i = 2:6 - plot(freqs, abs(squeeze(freqresp(Gm_hac_m1(sprintf('eL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(2,:), 0.5], ... - 'HandleVisibility', 'off'); -end -plot(freqs, abs(squeeze(freqresp(Gm_hac_m2(sprintf('eL%i', 1), sprintf('u%i', 1)), freqs, 'Hz'))), 'color', [colors(3,:), 0.5], ... - 'DisplayName', '$\tau_{m,i}/u_i$ - $m_2$'); -for i = 2:6 - plot(freqs, abs(squeeze(freqresp(Gm_hac_m2(sprintf('eL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(3,:), 0.5], ... - 'HandleVisibility', 'off'); -end -plot(freqs, abs(squeeze(freqresp(Gm_hac_m3(sprintf('eL%i', 1), sprintf('u%i', 1)), freqs, 'Hz'))), 'color', [colors(4,:), 0.5], ... - 'DisplayName', '$\tau_{m,i}/u_i$ - $m_3$'); -for i = 2:6 - plot(freqs, abs(squeeze(freqresp(Gm_hac_m3(sprintf('eL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(4,:), 0.5], ... - 'HandleVisibility', 'off'); -end +plot(1e6*exp_tomo_ol_m0_Wz180.y.x.Data, 1e6*exp_tomo_ol_m0_Wz180.y.y.Data, 'DisplayName', 'OL') +plot(1e6*exp_tomo_cl_m0_Wz180.y.x.Data(1:2e3), 1e6*exp_tomo_cl_m0_Wz180.y.y.Data(1:2e3), 'color', colors(3,:), 'HandleVisibility', 'off') +plot(1e6*exp_tomo_cl_m0_Wz180.y.x.Data(2e3:end), 1e6*exp_tomo_cl_m0_Wz180.y.y.Data(2e3:end), 'color', colors(2,:), 'DisplayName', 'CL') +hold off; +xlabel('$D_x$ [$\mu$m]'); ylabel('$D_y$ [$\mu$m]'); +axis equal +xlim([-3, 3]); ylim([-3, 3]); +xticks([-3:1:3]); +yticks([-3:1:3]); +leg = legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1); +leg.ItemTokenSize(1) = 15; +#+end_src + +#+begin_src matlab :tangle no :exports results :results file none +exportFig('figs/test_id31_tomo_m0_30rpm_robust_hac_iff_sim_xy.pdf', 'width', 'half', 'height', 'normal'); +#+end_src + +#+begin_src matlab :exports none :results none +%% Simulation of tomography experiment - no payload, 30rpm - YZ errors +figure; +hold on; +plot(1e6*exp_tomo_ol_m0_Wz180.y.y.Data, 1e6*exp_tomo_ol_m0_Wz180.y.z.Data, 'DisplayName', 'OL') +plot(1e6*exp_tomo_cl_m0_Wz180.y.y.Data(1:2e3), 1e6*exp_tomo_cl_m0_Wz180.y.z.Data(1:2e3), 'color', colors(3,:), 'HandleVisibility', 'off') +plot(1e6*exp_tomo_cl_m0_Wz180.y.y.Data(2e3:end), 1e6*exp_tomo_cl_m0_Wz180.y.z.Data(2e3:end), 'color', colors(2,:), 'DisplayName', 'CL') +hold off; +xlabel('$D_y$ [$\mu$m]'); ylabel('$D_z$ [$\mu$m]'); +axis equal +xlim([-3, 3]); ylim([-1, 1]); +xticks([-3:1:3]); +yticks([-3:1:3]); +leg = legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1); +leg.ItemTokenSize(1) = 15; +#+end_src + +#+begin_src matlab :tangle no :exports results :results file none +exportFig('figs/test_id31_tomo_m0_30rpm_robust_hac_iff_sim_yz.pdf', 'width', 'half', 'height', 'normal'); +#+end_src + +#+name: fig:test_id31_tomo_m0_30rpm_robust_hac_iff_sim +#+caption: Position error of the sample in the XY (\subref{fig:test_id31_tomo_m0_30rpm_robust_hac_iff_sim_xy}) and YZ (\subref{fig:test_id31_tomo_m0_30rpm_robust_hac_iff_sim_yz}) planes during a simulation of a tomography experiment at 30RPM. No payload is placed on top of the nano-hexapod. +#+attr_latex: :options [htbp] +#+begin_figure +#+attr_latex: :caption \subcaption{\label{fig:test_id31_tomo_m0_30rpm_robust_hac_iff_sim_xy}XY plane} +#+attr_latex: :options {0.49\textwidth} +#+begin_subfigure +#+attr_latex: :scale 0.9 +[[file:figs/test_id31_tomo_m0_30rpm_robust_hac_iff_sim_xy.png]] +#+end_subfigure +#+attr_latex: :caption \subcaption{\label{fig:test_id31_tomo_m0_30rpm_robust_hac_iff_sim_yz}YZ plane} +#+attr_latex: :options {0.49\textwidth} +#+begin_subfigure +#+attr_latex: :scale 0.9 +[[file:figs/test_id31_tomo_m0_30rpm_robust_hac_iff_sim_yz.png]] +#+end_subfigure +#+end_figure + +Then the same tomography experiment (i.e. constant spindle rotation at 30rpm, and no payload) was performed experimentally. +The measured position of the "point of interest" during the experiment are shown in Figure ref:fig:test_id31_tomo_m0_30rpm_robust_hac_iff_exp. + +#+begin_src matlab +%% Experimental Results for Tomography at 30RPM, no payload +% Load measured noise +data_tomo_m0_Wz180 = load('2023-08-17_15-26_tomography_30rpm_m0_robust.mat'); + +[~, i_cl] = find(data_tomo_m0_Wz180.hac_status == 1); +#+end_src + +#+begin_src matlab :exports none :results none +%% Measured radial errors of the Spindle +figure; +hold on; +plot(1e6*data_tomo_m0_Wz180.Dx_int(1:i_cl), 1e6*data_tomo_m0_Wz180.Dy_int(1:i_cl), 'DisplayName', 'OL') +plot(1e6*data_tomo_m0_Wz180.Dx_int(i_cl:i_cl+1e4), 1e6*data_tomo_m0_Wz180.Dy_int(i_cl:i_cl+1e4), 'color', colors(3,:), 'HandleVisibility', 'off') +plot(1e6*data_tomo_m0_Wz180.Dx_int(i_cl+1e4:end), 1e6*data_tomo_m0_Wz180.Dy_int(i_cl+1e4:end), 'color', colors(2,:), 'DisplayName', 'CL') +hold off; +xlabel('$D_x$ [$\mu$m]'); ylabel('$D_y$ [$\mu$m]'); +axis equal +xlim([-3, 3]); ylim([-3, 3]); +xticks([-3:1:3]); +yticks([-3:1:3]); +leg = legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1); +leg.ItemTokenSize(1) = 15; +#+end_src + +#+begin_src matlab :tangle no :exports results :results file none +exportFig('figs/test_id31_tomo_m0_30rpm_robust_hac_iff_exp_xy.pdf', 'width', 'half', 'height', 'normal'); +#+end_src + +#+begin_src matlab :exports none :results none +%% Measured radial errors of the Spindle +figure; +hold on; +plot(1e6*data_tomo_m0_Wz180.Dy_int(1:i_cl), 1e6*data_tomo_m0_Wz180.Dz_int(1:i_cl), 'DisplayName', 'OL') +plot(1e6*data_tomo_m0_Wz180.Dy_int(i_cl:i_cl+1e4), 1e6*data_tomo_m0_Wz180.Dz_int(i_cl:i_cl+1e4), 'color', colors(3,:), 'HandleVisibility', 'off') +plot(1e6*data_tomo_m0_Wz180.Dy_int(i_cl+1e4:end), 1e6*data_tomo_m0_Wz180.Dz_int(i_cl+1e4:end), 'color', colors(2,:), 'DisplayName', 'CL') +hold off; +xlabel('$D_y$ [$\mu$m]'); ylabel('$D_z$ [$\mu$m]'); +axis equal +xlim([-3, 3]); ylim([-1, 1]); +xticks([-3:1:3]); +yticks([-3:1:3]); +leg = legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1); +leg.ItemTokenSize(1) = 15; +#+end_src + +#+begin_src matlab :tangle no :exports results :results file none +exportFig('figs/test_id31_tomo_m0_30rpm_robust_hac_iff_exp_yz.pdf', 'width', 'half', 'height', 'normal'); +#+end_src + +#+name: fig:test_id31_tomo_m0_30rpm_robust_hac_iff_exp +#+caption: Experimental results of a tomography experiment at 30RPM without payload. Position error of the sample is shown in the XY (\subref{fig:test_id31_tomo_m0_30rpm_robust_hac_iff_exp_xy}) and YZ (\subref{fig:test_id31_tomo_m0_30rpm_robust_hac_iff_exp_yz}) planes. +#+attr_latex: :options [htbp] +#+begin_figure +#+attr_latex: :caption \subcaption{\label{fig:test_id31_tomo_m0_30rpm_robust_hac_iff_exp_xy}XY plane} +#+attr_latex: :options {0.49\textwidth} +#+begin_subfigure +#+attr_latex: :scale 0.9 +[[file:figs/test_id31_tomo_m0_30rpm_robust_hac_iff_exp_xy.png]] +#+end_subfigure +#+attr_latex: :caption \subcaption{\label{fig:test_id31_tomo_m0_30rpm_robust_hac_iff_exp_yz}YZ plane} +#+attr_latex: :options {0.49\textwidth} +#+begin_subfigure +#+attr_latex: :scale 0.9 +[[file:figs/test_id31_tomo_m0_30rpm_robust_hac_iff_exp_yz.png]] +#+end_subfigure +#+end_figure + +Even though the simulation (Figure ref:fig:test_id31_tomo_m0_30rpm_robust_hac_iff_sim) and the experimental results (Figure ref:fig:test_id31_tomo_m0_30rpm_robust_hac_iff_exp) are looking similar, the most important metric to compare is the RMS values of the positioning errors in closed-loop. +These are computed for both the simulation and the experimental results and are compared in Table ref:tab:test_id31_tomo_m0_30rpm_robust_hac_iff_rms. +The lateral and vertical errors are similar, however the tilt ($R_y$) errors are underestimated by the model, which is reasonable as disturbances in $R_y$ were not modeled. + +Results obtained with this conservative HAC are already close to the specifications. + +#+begin_src matlab +%% Compute RMS of errors + +% Simulation - OL +rms_Dy_m0_Wz180_ol_sim = rms(detrend(exp_tomo_ol_m0_Wz180.y.y.Data, 0)); +rms_Dz_m0_Wz180_ol_sim = rms(detrend(exp_tomo_ol_m0_Wz180.y.z.Data, 0)); +rms_Ry_m0_Wz180_ol_sim = rms(detrend(squeeze(atan2(exp_tomo_ol_m0_Wz180.y.R.Data(1,3,:), sqrt(exp_tomo_ol_m0_Wz180.y.R.Data(1,1,:).^2+exp_tomo_ol_m0_Wz180.y.R.Data(1,2,:).^2))), 0)); +i_stab = 1000; % Remove the transient that is irrelevant here +% Simulation - CL +rms_Dy_m0_Wz180_cl_sim = rms(detrend(exp_tomo_cl_m0_Wz180.y.y.Data(i_stab:end), 0)); +rms_Dz_m0_Wz180_cl_sim = rms(detrend(exp_tomo_cl_m0_Wz180.y.z.Data(i_stab:end), 0)); +rms_Ry_m0_Wz180_cl_sim = rms(detrend(squeeze(atan2(exp_tomo_cl_m0_Wz180.y.R.Data(1,3,i_stab:end), sqrt(exp_tomo_cl_m0_Wz180.y.R.Data(1,1,i_stab:end).^2+exp_tomo_cl_m0_Wz180.y.R.Data(1,2,i_stab:end).^2))), 0)); + +% Experimental - OL +[~, i_cl] = find(data_tomo_m0_Wz180.hac_status == 1); +rms_Dy_m0_Wz180_ol_exp = rms(detrend(data_tomo_m0_Wz180.Dy_int(1:i_cl), 0)); +rms_Dz_m0_Wz180_ol_exp = rms(detrend(data_tomo_m0_Wz180.Dz_int(1:i_cl), 0)); +rms_Ry_m0_Wz180_ol_exp = rms(detrend(data_tomo_m0_Wz180.Ry_int(1:i_cl), 0)); +% Experimental - CL +rms_Dy_m0_Wz180_cl_exp = rms(detrend(data_tomo_m0_Wz180.Dy_int(i_cl+10000:end), 0)); +rms_Dz_m0_Wz180_cl_exp = rms(detrend(data_tomo_m0_Wz180.Dz_int(i_cl+10000:end), 0)); +rms_Ry_m0_Wz180_cl_exp = rms(detrend(data_tomo_m0_Wz180.Ry_int(i_cl+10000:end), 0)); +#+end_src + +#+name: tab:test_id31_tomo_m0_30rpm_robust_hac_iff_rms +#+caption: RMS values of the errors for a tomography experiment at 30RPM and without payload. Experimental results and simulation are compared. +#+attr_latex: :environment tabularx :width 0.7\linewidth :align Xccc +#+attr_latex: :center t :booktabs t +| | $D_y$ | $D_z$ | $R_y$ | +|---------------------+-----------------------+--------------------+------------------------| +| Experiment (OL) | $1.8\,\mu\text{mRMS}$ | $24\,\text{nmRMS}$ | $10\,\mu\text{radRMS}$ | +|---------------------+-----------------------+--------------------+------------------------| +| Simulation (CL) | $30\,\text{nmRMS}$ | $8\,\text{nmRMS}$ | $73\,\text{nradRMS}$ | +| Experiment (CL) | $39\,\text{nmRMS}$ | $11\,\text{nmRMS}$ | $130\,\text{nradRMS}$ | +|---------------------+-----------------------+--------------------+------------------------| +| Specifications (CL) | $30\,\text{nmRMS}$ | $15\,\text{nmRMS}$ | $250\,\text{nradRMS}$ | + +** Robustness to change of payload +<> + +To verify the robustness to the change of payload mass, four simulations of tomography experiments were performed with payloads as shown Figure ref:fig:test_id31_picture_masses (i.e. $0\,kg$, $13\,kg$, $26\,kg$ and $39\,kg$). +This time, the rotational velocity was set at 1rpm (i.e. 6deg/s), as it is the typical rotational velocity for heavy samples. +The closed-loop systems were stable for all payload conditions, indicating good control robustness. + +#+begin_src matlab +%% Simulation of tomography experiments at 1RPM with all payloads + +% Configuration +set_param(mdl, 'StopTime', '2'); % 30 degrees at 1rpm +initializeLoggingConfiguration('log', 'all', 'Ts', 1e-3); +initializeController('type', 'hac-iff'); +initializeReferences(... + 'Rz_type', 'rotating', ... + 'Rz_period', 360/6, ... % 6deg/s, 1 rpm + 'Dh_pos', [P_micro_hexapod; 0; 0; 0]); + +% Perform the simulations +initializeSample('type', '0'); +sim(mdl); +exp_tomo_cl_m0_1rpm = simout; +initializeSample('type', '1'); +sim(mdl); +exp_tomo_cl_m1_1rpm = simout; +initializeSample('type', '2'); +sim(mdl); +exp_tomo_cl_m2_1rpm = simout; +initializeSample('type', '3'); +sim(mdl); +exp_tomo_cl_m3_1rpm = simout; +#+end_src + +#+begin_src matlab :exports none :tangle no +% Save the simulation results +save('./matlab/mat/test_id31_exp_tomo_cl_1rpm_sim.mat', 'exp_tomo_cl_m0_1rpm', 'exp_tomo_cl_m1_1rpm', 'exp_tomo_cl_m2_1rpm', 'exp_tomo_cl_m3_1rpm'); +#+end_src + +#+begin_src matlab :eval no +% Save the simulation results +save('./mat/test_id31_exp_tomo_cl_1rpm_sim.mat', 'exp_tomo_cl_m0_1rpm', 'exp_tomo_cl_m1_1rpm', 'exp_tomo_cl_m2_1rpm', 'exp_tomo_cl_m3_1rpm'); +#+end_src + +#+begin_src matlab +figure; +hold on; +plot(exp_tomo_cl_m3_1rpm.y.x.Data, exp_tomo_cl_m3_1rpm.y.y.Data) +plot(exp_tomo_cl_m2_1rpm.y.x.Data, exp_tomo_cl_m2_1rpm.y.y.Data) +plot(exp_tomo_cl_m1_1rpm.y.x.Data, exp_tomo_cl_m1_1rpm.y.y.Data) +plot(exp_tomo_cl_m0_1rpm.y.x.Data, exp_tomo_cl_m0_1rpm.y.y.Data) +#+end_src + +#+begin_src matlab +% Estimate the RMS value of the errors +i_stab = 1000; % Remove the transient that is irrelevant here +rms_Dy_m0_1rpm = rms(detrend(exp_tomo_cl_m0_1rpm.y.y.Data(i_stab:end), 0)); +rms_Dz_m0_1rpm = rms(detrend(exp_tomo_cl_m0_1rpm.y.z.Data(i_stab:end), 0)); +rms_Ry_m0_1rpm = rms(detrend(squeeze(atan2(exp_tomo_cl_m0_1rpm.y.R.Data(1,3,:), sqrt(exp_tomo_cl_m0_1rpm.y.R.Data(1,1,:).^2+exp_tomo_cl_m0_1rpm.y.R.Data(1,2,:).^2))), 0)); +rms_Dy_m1_1rpm = rms(detrend(exp_tomo_cl_m1_1rpm.y.y.Data(i_stab:end), 0)); +rms_Dz_m1_1rpm = rms(detrend(exp_tomo_cl_m1_1rpm.y.z.Data(i_stab:end), 0)); +rms_Ry_m1_1rpm = rms(detrend(squeeze(atan2(exp_tomo_cl_m1_1rpm.y.R.Data(1,3,:), sqrt(exp_tomo_cl_m1_1rpm.y.R.Data(1,1,:).^2+exp_tomo_cl_m1_1rpm.y.R.Data(1,2,:).^2))), 0)); +rms_Dy_m2_1rpm = rms(detrend(exp_tomo_cl_m2_1rpm.y.y.Data(i_stab:end), 0)); +rms_Dz_m2_1rpm = rms(detrend(exp_tomo_cl_m2_1rpm.y.z.Data(i_stab:end), 0)); +rms_Ry_m2_1rpm = rms(detrend(squeeze(atan2(exp_tomo_cl_m2_1rpm.y.R.Data(1,3,:), sqrt(exp_tomo_cl_m2_1rpm.y.R.Data(1,1,:).^2+exp_tomo_cl_m2_1rpm.y.R.Data(1,2,:).^2))), 0)); +rms_Dy_m3_1rpm = rms(detrend(exp_tomo_cl_m3_1rpm.y.y.Data(i_stab:end), 0)); +rms_Dz_m3_1rpm = rms(detrend(exp_tomo_cl_m3_1rpm.y.z.Data(i_stab:end), 0)); +rms_Ry_m3_1rpm = rms(detrend(squeeze(atan2(exp_tomo_cl_m3_1rpm.y.R.Data(1,3,:), sqrt(exp_tomo_cl_m3_1rpm.y.R.Data(1,1,:).^2+exp_tomo_cl_m3_1rpm.y.R.Data(1,2,:).^2))), 0)); +#+end_src + +#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*) +data2orgtable(1e9*[rms_Dy_m0_1rpm, rms_Dz_m0_1rpm, rms_Ry_m0_1rpm; rms_Dy_m1_1rpm, rms_Dz_m1_1rpm, rms_Ry_m1_1rpm; rms_Dy_m2_1rpm, rms_Dz_m2_1rpm, rms_Ry_m2_1rpm; rms_Dy_m3_1rpm, rms_Dz_m3_1rpm, rms_Ry_m3_1rpm], {'0 kg', '13 kg', '26 kg', '39 kg'}, {'$D_y$', '$D_z$', '$R_y$'}, ' %.0f '); +#+end_src + +The tomography experiments that were simulated were then experimentally conducted. +For each payload, a spindle rotating was first performed in open-loop, and then the loop was closed during another full spindle rotation. +An example with the $26\,\text{kg}$ payload is shown in Figure ref:fig:test_id31_tomo_m2_1rpm_robust_hac_iff_fit. +The eccentricity between the "point of interest" and the spindle rotation axis is quite large as the added payload mass statically deforms the micro-station stages. +To estimate the open-loop errors, it is supposed that the "point of interest" can be perfectly aligned with the spindle rotation axis. +Therefore, the eccentricity is first estimated by performing a circular fit (dashed black circle in Figure ref:fig:test_id31_tomo_m2_1rpm_robust_hac_iff_fit), and then subtracted from the data in Figure ref:fig:test_id31_tomo_m2_1rpm_robust_hac_iff_fit_removed. +This underestimate the real condition open-loop errors as it is difficult to obtain a perfect alignment in practice. + +#+begin_src matlab +%% Load Tomography scans with robust controller +data_tomo_m0_Wz6 = load("2023-08-11_11-37_tomography_1rpm_m0.mat"); +data_tomo_m0_Wz6.time = Ts*[0:length(data_tomo_m0_Wz6.Rz)-1]; + +data_tomo_m1_Wz6 = load("2023-08-11_11-15_tomography_1rpm_m1.mat"); +data_tomo_m1_Wz6.time = Ts*[0:length(data_tomo_m1_Wz6.Rz)-1]; + +data_tomo_m2_Wz6 = load("2023-08-11_10-59_tomography_1rpm_m2.mat"); +data_tomo_m2_Wz6.time = Ts*[0:length(data_tomo_m2_Wz6.Rz)-1]; + +data_tomo_m3_Wz6 = load("2023-08-11_10-24_tomography_1rpm_m3.mat"); +data_tomo_m3_Wz6.time = Ts*[0:length(data_tomo_m3_Wz6.Rz)-1]; +#+end_src + +#+begin_src matlab :exports none :results none +%% Compute best circle fit for the displayed tomography experiment +[~, i_cl] = find(data_tomo_m0_Wz6.hac_status == 1); + +% Find best circle +[x0, y0, R] = circlefit(data_tomo_m2_Wz6.Dx_int(1:i_m2), data_tomo_m2_Wz6.Dy_int(1:i_m2)); +fun = @(theta)rms((data_tomo_m2_Wz6.Dx_int(1:i_m2) - (x0 + R*cos(data_tomo_m2_Wz6.Rz(1:i_m2)+theta(1)))).^2 + ... + (data_tomo_m2_Wz6.Dy_int(1:i_m2) - (y0 + R*sin(data_tomo_m2_Wz6.Rz(1:i_m2)+theta(1)))).^2); +delta_theta = fminsearch(fun, 0); +#+end_src + +#+begin_src matlab :exports none :results none +%% Tomography experiment at 1rpm with 26kg payload +figure; +hold on; +plot(1e6*data_tomo_m2_Wz6.Dx_int(1:i_cl), 1e6*data_tomo_m2_Wz6.Dy_int(1:i_cl), 'DisplayName', 'OL') +plot(1e6*data_tomo_m2_Wz6.Dx_int(i_cl:i_cl+1e4), 1e6*data_tomo_m2_Wz6.Dy_int(i_cl:i_cl+1e4), 'color', colors(3,:), 'HandleVisibility', 'off') +plot(1e6*data_tomo_m2_Wz6.Dx_int(i_cl+1e4:end), 1e6*data_tomo_m2_Wz6.Dy_int(i_cl+1e4:end), 'color', colors(2,:), 'DisplayName', 'CL') +theta = linspace(0, 2*pi, 500); % Angle to plot the circle [rad] +plot(1e6*(x0 + R*cos(theta)), 1e6*(y0 + R*sin(theta)), 'k--', 'DisplayName', 'Best Fit') +hold off; +xlabel('$D_x$ [$\mu$m]'); ylabel('$D_y$ [$\mu$m]'); +axis equal +xlim([-20, 100]); ylim([-20, 100]); +xticks([-20:20:100]); +yticks([-20:20:100]); +leg = legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1); +leg.ItemTokenSize(1) = 15; +#+end_src + +#+begin_src matlab :tangle no :exports results :results file none +exportFig('figs/test_id31_tomo_m2_1rpm_robust_hac_iff_fit.pdf', 'width', 'half', 'height', 'normal'); +#+end_src + +#+begin_src matlab :exports none :results none +%% Measured radial errors of the Spindle +figure; +hold on; +plot(1e6*(data_tomo_m2_Wz6.Dx_int(1:100:i_m2) - (x0 + R*cos(data_tomo_m2_Wz6.Rz(1:100:i_m2)+delta_theta))), ... + 1e6*(data_tomo_m2_Wz6.Dy_int(1:100:i_m2) - (y0 + R*sin(data_tomo_m2_Wz6.Rz(1:100:i_m2)+delta_theta))), 'color', colors(1,:), 'DisplayName', 'OL') +plot(1e6*detrend(data_tomo_m2_Wz6.Dx_int(i_cl+1e4:100:end), 0), 1e6*detrend(data_tomo_m2_Wz6.Dy_int(i_cl+1e4:100:end), 0), 'color', colors(2,:), 'DisplayName', 'CL') +hold off; +xlabel('$D_x$ [$\mu$m]'); ylabel('$D_y$ [$\mu$m]'); +axis equal +xlim([-0.6, 0.4]); ylim([-0.4, 0.6]); +xticks([-0.6:0.2:0.4]); +yticks([-0.4:0.2:0.6]); +leg = legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1); +leg.ItemTokenSize(1) = 15; +#+end_src + +#+begin_src matlab :tangle no :exports results :results file none +exportFig('figs/test_id31_tomo_m2_1rpm_robust_hac_iff_fit_removed.pdf', 'width', 'half', 'height', 'normal'); +#+end_src + +#+name: fig:test_id31_tomo_m2_1rpm_robust_hac_iff +#+caption: Tomography experiment with rotation velocity of 1rpm, and payload mass of 26kg. Errors in the $(x,y)$ plane are shown in (\subref{fig:test_id31_tomo_m2_1rpm_robust_hac_iff_fit}). The estimated eccentricity is displayed by the black dashed circle. Errors with subtracted eccentricity are shown in (\subref{fig:test_id31_tomo_m2_1rpm_robust_hac_iff_fit_removed}). +#+attr_latex: :options [htbp] +#+begin_figure +#+attr_latex: :caption \subcaption{\label{fig:test_id31_tomo_m2_1rpm_robust_hac_iff_fit}Errors in $(x,y)$ plane} +#+attr_latex: :options {0.49\textwidth} +#+begin_subfigure +#+attr_latex: :scale 0.9 +[[file:figs/test_id31_tomo_m2_1rpm_robust_hac_iff_fit.png]] +#+end_subfigure +#+attr_latex: :caption \subcaption{\label{fig:test_id31_tomo_m2_1rpm_robust_hac_iff_fit_removed}Removed eccentricity} +#+attr_latex: :options {0.49\textwidth} +#+begin_subfigure +#+attr_latex: :scale 0.9 +[[file:figs/test_id31_tomo_m2_1rpm_robust_hac_iff_fit_removed.png]] +#+end_subfigure +#+end_figure + +The RMS values of the open-loop and closed-loop errors for all masses are summarized in Table ref:tab:test_id31_tomo_1rpm_robust_ol_cl_errors. +The obtained closed-loop errors are fulfilling the requirements, except for the $39\,\text{kg}$ payload in the lateral ($D_y$) direction. + +#+begin_src matlab +%% Estimate RMS of the errors while in closed-loop and open-loop +% No mass +[~, i_m0] = find(data_tomo_m0_Wz6.hac_status == 1); +data_tomo_m0_Wz6.Dy_rms_cl = rms(detrend(data_tomo_m0_Wz6.Dy_int(i_m0+50000:end), 0)); +data_tomo_m0_Wz6.Dz_rms_cl = rms(detrend(data_tomo_m0_Wz6.Dz_int(i_m0+50000:end), 0)); +data_tomo_m0_Wz6.Ry_rms_cl = rms(detrend(data_tomo_m0_Wz6.Ry_int(i_m0+50000:end), 0)); + +% Remove eccentricity for OL errors +[x0, y0, R] = circlefit(data_tomo_m0_Wz6.Dx_int(1:i_m0), data_tomo_m0_Wz6.Dy_int(1:i_m0)); +fun = @(theta)rms((data_tomo_m0_Wz6.Dx_int(1:i_m0) - (x0 + R*cos(data_tomo_m0_Wz6.Rz(1:i_m0)+theta(1)))).^2 + ... + (data_tomo_m0_Wz6.Dy_int(1:i_m0) - (y0 + R*sin(data_tomo_m0_Wz6.Rz(1:i_m0)+theta(1)))).^2); +delta_theta = fminsearch(fun, 0); +data_tomo_m0_Wz6.Dy_rms_ol = rms(data_tomo_m0_Wz6.Dy_int(1:i_m0) - (y0 + R*sin(data_tomo_m0_Wz6.Rz(1:i_m0)+delta_theta))); +data_tomo_m0_Wz6.Dz_rms_ol = rms(detrend(data_tomo_m0_Wz6.Dz_int(1:i_m0), 0)); +[x0, y0, R] = circlefit(data_tomo_m0_Wz6.Rx_int(1:i_m0), data_tomo_m0_Wz6.Ry_int(1:i_m0)); +fun = @(theta)rms((data_tomo_m0_Wz6.Rx_int(1:i_m0) - (x0 + R*cos(data_tomo_m0_Wz6.Rz(1:i_m0)+theta(1)))).^2 + ... + (data_tomo_m0_Wz6.Ry_int(1:i_m0) - (y0 + R*sin(data_tomo_m0_Wz6.Rz(1:i_m0)+theta(1)))).^2); +delta_theta = fminsearch(fun, 0); +data_tomo_m0_Wz6.Ry_rms_ol = rms(data_tomo_m0_Wz6.Ry_int(1:i_m0) - (y0 + R*sin(data_tomo_m0_Wz6.Rz(1:i_m0)+delta_theta))); + +% 1 "layer mass" +[~, i_m1] = find(data_tomo_m1_Wz6.hac_status == 1); +data_tomo_m1_Wz6.Dy_rms_cl = rms(detrend(data_tomo_m1_Wz6.Dy_int(i_m1+50000:end), 0)); +data_tomo_m1_Wz6.Dz_rms_cl = rms(detrend(data_tomo_m1_Wz6.Dz_int(i_m1+50000:end), 0)); +data_tomo_m1_Wz6.Ry_rms_cl = rms(detrend(data_tomo_m1_Wz6.Ry_int(i_m1+50000:end), 0)); + +% Remove eccentricity for OL errors +[x0, y0, R] = circlefit(data_tomo_m1_Wz6.Dx_int(1:i_m1), data_tomo_m1_Wz6.Dy_int(1:i_m1)); +fun = @(theta)rms((data_tomo_m1_Wz6.Dx_int(1:i_m1) - (x0 + R*cos(data_tomo_m1_Wz6.Rz(1:i_m1)+theta(1)))).^2 + ... + (data_tomo_m1_Wz6.Dy_int(1:i_m1) - (y0 + R*sin(data_tomo_m1_Wz6.Rz(1:i_m1)+theta(1)))).^2); +delta_theta = fminsearch(fun, 0); +data_tomo_m1_Wz6.Dy_rms_ol = rms(data_tomo_m1_Wz6.Dy_int(1:i_m1) - (y0 + R*sin(data_tomo_m1_Wz6.Rz(1:i_m1)+delta_theta))); +data_tomo_m1_Wz6.Dz_rms_ol = rms(detrend(data_tomo_m1_Wz6.Dz_int(1:i_m1), 0)); +[x0, y0, R] = circlefit(data_tomo_m1_Wz6.Rx_int(1:i_m1), data_tomo_m1_Wz6.Ry_int(1:i_m1)); +fun = @(theta)rms((data_tomo_m1_Wz6.Rx_int(1:i_m1) - (x0 + R*cos(data_tomo_m1_Wz6.Rz(1:i_m1)+theta(1)))).^2 + ... + (data_tomo_m1_Wz6.Ry_int(1:i_m1) - (y0 + R*sin(data_tomo_m1_Wz6.Rz(1:i_m1)+theta(1)))).^2); +delta_theta = fminsearch(fun, 0); +data_tomo_m1_Wz6.Ry_rms_ol = rms(data_tomo_m1_Wz6.Ry_int(1:i_m1) - (y0 + R*sin(data_tomo_m1_Wz6.Rz(1:i_m1)+delta_theta))); + +% 2 "layer masses" +[~, i_m2] = find(data_tomo_m2_Wz6.hac_status == 1); +data_tomo_m2_Wz6.Dy_rms_cl = rms(detrend(data_tomo_m2_Wz6.Dy_int(i_m2+50000:end), 0)); +data_tomo_m2_Wz6.Dz_rms_cl = rms(detrend(data_tomo_m2_Wz6.Dz_int(i_m2+50000:end), 0)); +data_tomo_m2_Wz6.Ry_rms_cl = rms(detrend(data_tomo_m2_Wz6.Ry_int(i_m2+50000:end), 0)); + +% Remove eccentricity for OL errors +[x0, y0, R] = circlefit(data_tomo_m2_Wz6.Dx_int(1:i_m2), data_tomo_m2_Wz6.Dy_int(1:i_m2)); +fun = @(theta)rms((data_tomo_m2_Wz6.Dx_int(1:i_m2) - (x0 + R*cos(data_tomo_m2_Wz6.Rz(1:i_m2)+theta(1)))).^2 + ... + (data_tomo_m2_Wz6.Dy_int(1:i_m2) - (y0 + R*sin(data_tomo_m2_Wz6.Rz(1:i_m2)+theta(1)))).^2); +delta_theta = fminsearch(fun, 0); +data_tomo_m2_Wz6.Dy_rms_ol = rms(data_tomo_m2_Wz6.Dy_int(1:i_m2) - (y0 + R*sin(data_tomo_m2_Wz6.Rz(1:i_m2)+delta_theta))); +data_tomo_m2_Wz6.Dz_rms_ol = rms(detrend(data_tomo_m2_Wz6.Dz_int(1:i_m2), 0)); +[x0, y0, R] = circlefit(data_tomo_m2_Wz6.Rx_int(1:i_m2), data_tomo_m2_Wz6.Ry_int(1:i_m2)); +fun = @(theta)rms((data_tomo_m2_Wz6.Rx_int(1:i_m2) - (x0 + R*cos(data_tomo_m2_Wz6.Rz(1:i_m2)+theta(1)))).^2 + ... + (data_tomo_m2_Wz6.Ry_int(1:i_m2) - (y0 + R*sin(data_tomo_m2_Wz6.Rz(1:i_m2)+theta(1)))).^2); +delta_theta = fminsearch(fun, 0); +data_tomo_m2_Wz6.Ry_rms_ol = rms(data_tomo_m2_Wz6.Ry_int(1:i_m2) - (y0 + R*sin(data_tomo_m2_Wz6.Rz(1:i_m2)+delta_theta))); + +% 3 "layer masses" +[~, i_m3] = find(data_tomo_m3_Wz6.hac_status == 1); +data_tomo_m3_Wz6.Dy_rms_cl = rms(detrend(data_tomo_m3_Wz6.Dy_int(i_m3+50000:end), 0)); +data_tomo_m3_Wz6.Dz_rms_cl = rms(detrend(data_tomo_m3_Wz6.Dz_int(i_m3+50000:end), 0)); +data_tomo_m3_Wz6.Ry_rms_cl = rms(detrend(data_tomo_m3_Wz6.Ry_int(i_m3+50000:end), 0)); + +% Remove eccentricity for OL errors +[x0, y0, R] = circlefit(data_tomo_m3_Wz6.Dx_int(1:i_m3), data_tomo_m3_Wz6.Dy_int(1:i_m3)); +fun = @(theta)rms((data_tomo_m3_Wz6.Dx_int(1:i_m3) - (x0 + R*cos(data_tomo_m3_Wz6.Rz(1:i_m3)+theta(1)))).^2 + ... + (data_tomo_m3_Wz6.Dy_int(1:i_m3) - (y0 + R*sin(data_tomo_m3_Wz6.Rz(1:i_m3)+theta(1)))).^2); +delta_theta = fminsearch(fun, 0); +data_tomo_m3_Wz6.Dy_rms_ol = rms(data_tomo_m3_Wz6.Dy_int(1:i_m3) - (y0 + R*sin(data_tomo_m3_Wz6.Rz(1:i_m3)+delta_theta))); +data_tomo_m3_Wz6.Dz_rms_ol = rms(detrend(data_tomo_m3_Wz6.Dz_int(1:i_m3), 0)); +[x0, y0, R] = circlefit(data_tomo_m3_Wz6.Rx_int(1:i_m3), data_tomo_m3_Wz6.Ry_int(1:i_m3)); +fun = @(theta)rms((data_tomo_m3_Wz6.Rx_int(1:i_m3) - (x0 + R*cos(data_tomo_m3_Wz6.Rz(1:i_m3)+theta(1)))).^2 + ... + (data_tomo_m3_Wz6.Ry_int(1:i_m3) - (y0 + R*sin(data_tomo_m3_Wz6.Rz(1:i_m3)+theta(1)))).^2); +delta_theta = fminsearch(fun, 0); +data_tomo_m3_Wz6.Ry_rms_ol = rms(data_tomo_m3_Wz6.Ry_int(1:i_m3) - (y0 + R*sin(data_tomo_m3_Wz6.Rz(1:i_m3)+delta_theta))); +#+end_src + +#+name: tab:test_id31_tomo_1rpm_robust_ol_cl_errors +#+caption: RMS values of the measured errors during open-loop and closed-loop tomography scans (1rpm) for all considered payloads. Measured closed-Loop errors are indicated by "bold" font. +#+attr_latex: :environment tabularx :width 0.9\linewidth :align Xccc +#+attr_latex: :center t :booktabs t +| | $D_y$ | $D_z$ | $R_y$ | +|------------------+----------------------------------------------+--------------------------------------------+--------------------------------------------------| +| $0\,kg$ | $142 \Longrightarrow \bm{15}\,\text{nm RMS}$ | $32 \Longrightarrow \bm{5}\,\text{nm RMS}$ | $460 \Longrightarrow \bm{55}\,\text{nrad RMS}$ | +| $13\,kg$ | $149 \Longrightarrow \bm{25}\,\text{nm RMS}$ | $26 \Longrightarrow \bm{6}\,\text{nm RMS}$ | $470 \Longrightarrow \bm{55}\,\text{nrad RMS}$ | +| $26\,kg$ | $202 \Longrightarrow \bm{25}\,\text{nm RMS}$ | $36 \Longrightarrow \bm{7}\,\text{nm RMS}$ | $1700 \Longrightarrow \bm{103}\,\text{nrad RMS}$ | +| $39\,kg$ | $297 \Longrightarrow \bm{53}\,\text{nm RMS}$ | $38 \Longrightarrow \bm{9}\,\text{nm RMS}$ | $1700 \Longrightarrow \bm{169}\,\text{nrad RMS}$ | +|------------------+----------------------------------------------+--------------------------------------------+--------------------------------------------------| +| *Specifications* | $30\,\text{nmRMS}$ | $15\,\text{nmRMS}$ | $250\,\text{nradRMS}$ | + +#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*) +data2orgtable([1e9*data_tomo_m0_Wz6.Dy_rms_ol, 1e9*data_tomo_m0_Wz6.Dz_rms_ol, 1e9*data_tomo_m0_Wz6.Ry_rms_ol; ... + 1e9*data_tomo_m1_Wz6.Dy_rms_ol, 1e9*data_tomo_m1_Wz6.Dz_rms_ol, 1e9*data_tomo_m1_Wz6.Ry_rms_ol; ... + 1e9*data_tomo_m2_Wz6.Dy_rms_ol, 1e9*data_tomo_m2_Wz6.Dz_rms_ol, 1e9*data_tomo_m2_Wz6.Ry_rms_ol; ... + 1e9*data_tomo_m3_Wz6.Dy_rms_ol, 1e9*data_tomo_m3_Wz6.Dz_rms_ol, 1e9*data_tomo_m3_Wz6.Ry_rms_ol], ... + {'$m_0$', '$m_1$', '$m_2$', '$m_3$'}, {'$D_y$ [$\mu m$]', '$D_z$ [$nm$]', '$R_y$ [$\mu\text{rad}$]'}, ' %.0f '); +#+end_src + +#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*) +data2orgtable([1e9*data_tomo_m0_Wz6.Dx_rms_cl, 1e9*data_tomo_m0_Wz6.Dy_rms_cl, 1e9*data_tomo_m0_Wz6.Dz_rms_cl, 1e9*data_tomo_m0_Wz6.Rx_rms_cl, 1e9*data_tomo_m0_Wz6.Ry_rms_cl; ... + 1e9*data_tomo_m1_Wz6.Dx_rms_cl, 1e9*data_tomo_m1_Wz6.Dy_rms_cl, 1e9*data_tomo_m1_Wz6.Dz_rms_cl, 1e9*data_tomo_m1_Wz6.Rx_rms_cl, 1e9*data_tomo_m1_Wz6.Ry_rms_cl; ... + 1e9*data_tomo_m2_Wz6.Dx_rms_cl, 1e9*data_tomo_m2_Wz6.Dy_rms_cl, 1e9*data_tomo_m2_Wz6.Dz_rms_cl, 1e9*data_tomo_m2_Wz6.Rx_rms_cl, 1e9*data_tomo_m2_Wz6.Ry_rms_cl; ... + 1e9*data_tomo_m3_Wz6.Dx_rms_cl, 1e9*data_tomo_m3_Wz6.Dy_rms_cl, 1e9*data_tomo_m3_Wz6.Dz_rms_cl, 1e9*data_tomo_m3_Wz6.Rx_rms_cl, 1e9*data_tomo_m3_Wz6.Ry_rms_cl], ... + {'$m_0$', '$m_1$', '$m_2$', '$m_3$'}, {'$D_x$ [nm]', '$D_y$ [nm]', '$D_z$ [nm]', '$R_x$ [nrad]', '$R_y$ [nrad]'}, ' %.0f '); +#+end_src + +** Conclusion +:PROPERTIES: +:UNNUMBERED: t +:END: + +* Dynamic Error Budgeting :noexport: +<> +** Introduction :ignore: + +In this section, the noise budget is performed. +The vibrations of the sample is measured in different conditions using the external metrology. + +** Matlab Init :noexport:ignore: +#+begin_src matlab :tangle no :exports none :results silent :noweb yes :var current_dir=(file-name-directory buffer-file-name) +<> +#+end_src + +#+begin_src matlab :exports none :results silent :noweb yes +<> +#+end_src + +#+begin_src matlab :tangle no :noweb yes +<> +#+end_src + +#+begin_src matlab :eval no :noweb yes +<> +#+end_src + +#+begin_src matlab :noweb yes +<> +#+end_src + +** Open-Loop Noise Budget + +- Effect of rotation. +- Comparison with measurement noise: should be higher + +#+begin_src matlab +%% Effect of rotation +data_ol_Wz0 = load('2023-08-11_16-51_m0_lac_off.mat'); +data_ol_Wz36 = load('2023-08-11_17-18_m0_lac_off_1rpm.mat'); +data_ol_Wz180 = load('2023-08-11_17-39_m0_lac_off_30rpm.mat'); + +% Coordinate transform +J_int_to_X = [ 0 0 -0.787401574803149 -0.212598425196851 0; + 0.78740157480315 0.21259842519685 0 0 0; + 0 0 0 0 -1; + -13.1233595800525 13.1233595800525 0 0 0; + 0 0 -13.1233595800525 13.1233595800525 0]; + +a = J_int_to_X*[data_ol_Wz0.d1; data_ol_Wz0.d2; data_ol_Wz0.d3; data_ol_Wz0.d4; data_ol_Wz0.d5]; +data_ol_Wz0.Dx_int = a(1,:); +data_ol_Wz0.Dy_int = a(2,:); +data_ol_Wz0.Dz_int = a(3,:); +data_ol_Wz0.Rx_int = a(4,:); +data_ol_Wz0.Ry_int = a(5,:); + +a = J_int_to_X*[data_ol_Wz36.d1; data_ol_Wz36.d2; data_ol_Wz36.d3; data_ol_Wz36.d4; data_ol_Wz36.d5]; +data_ol_Wz36.Dx_int = a(1,:); +data_ol_Wz36.Dy_int = a(2,:); +data_ol_Wz36.Dz_int = a(3,:); +data_ol_Wz36.Rx_int = a(4,:); +data_ol_Wz36.Ry_int = a(5,:); + +a = J_int_to_X*[data_ol_Wz180.d1; data_ol_Wz180.d2; data_ol_Wz180.d3; data_ol_Wz180.d4; data_ol_Wz180.d5]; +data_ol_Wz180.Dx_int = a(1,:); +data_ol_Wz180.Dy_int = a(2,:); +data_ol_Wz180.Dz_int = a(3,:); +data_ol_Wz180.Rx_int = a(4,:); +data_ol_Wz180.Ry_int = a(5,:); + +% Hannning Windows +Nfft = floor(20.0/Ts); +win = hanning(Nfft); +Noverlap = floor(Nfft/2); + +[data_ol_Wz0.pxx_Dx, data_ol_Wz0.f] = pwelch(detrend(data_ol_Wz0.Dx_int, 0), win, Noverlap, Nfft, 1/Ts); +[data_ol_Wz0.pxx_Dy, ~ ] = pwelch(detrend(data_ol_Wz0.Dy_int, 0), win, Noverlap, Nfft, 1/Ts); +[data_ol_Wz0.pxx_Dz, ~ ] = pwelch(detrend(data_ol_Wz0.Dz_int, 0), win, Noverlap, Nfft, 1/Ts); +[data_ol_Wz0.pxx_Rx, ~ ] = pwelch(detrend(data_ol_Wz0.Rx_int, 0), win, Noverlap, Nfft, 1/Ts); +[data_ol_Wz0.pxx_Ry, ~ ] = pwelch(detrend(data_ol_Wz0.Ry_int, 0), win, Noverlap, Nfft, 1/Ts); + +[data_ol_Wz36.pxx_Dx, data_ol_Wz36.f] = pwelch(detrend(data_ol_Wz36.Dx_int, 0), win, Noverlap, Nfft, 1/Ts); +[data_ol_Wz36.pxx_Dy, ~ ] = pwelch(detrend(data_ol_Wz36.Dy_int, 0), win, Noverlap, Nfft, 1/Ts); +[data_ol_Wz36.pxx_Dz, ~ ] = pwelch(detrend(data_ol_Wz36.Dz_int, 0), win, Noverlap, Nfft, 1/Ts); +[data_ol_Wz36.pxx_Rx, ~ ] = pwelch(detrend(data_ol_Wz36.Rx_int, 0), win, Noverlap, Nfft, 1/Ts); +[data_ol_Wz36.pxx_Ry, ~ ] = pwelch(detrend(data_ol_Wz36.Ry_int, 0), win, Noverlap, Nfft, 1/Ts); + +[data_ol_Wz180.pxx_Dx, data_ol_Wz180.f] = pwelch(detrend(data_ol_Wz180.Dx_int, 0), win, Noverlap, Nfft, 1/Ts); +[data_ol_Wz180.pxx_Dy, ~ ] = pwelch(detrend(data_ol_Wz180.Dy_int, 0), win, Noverlap, Nfft, 1/Ts); +[data_ol_Wz180.pxx_Dz, ~ ] = pwelch(detrend(data_ol_Wz180.Dz_int, 0), win, Noverlap, Nfft, 1/Ts); +[data_ol_Wz180.pxx_Rx, ~ ] = pwelch(detrend(data_ol_Wz180.Rx_int, 0), win, Noverlap, Nfft, 1/Ts); +[data_ol_Wz180.pxx_Ry, ~ ] = pwelch(detrend(data_ol_Wz180.Ry_int, 0), win, Noverlap, Nfft, 1/Ts); +#+end_src + +#+begin_src matlab :exports none :results none +figure; +tiledlayout(1, 3, 'TileSpacing', 'compact', 'Padding', 'None'); + +ax1 = nexttile(); +hold on; +plot(data_ol_Wz0.f, sqrt(flip(-cumtrapz(flip(data_ol_Wz0.f), flip(data_ol_Wz0.pxx_Dy)))), ... + 'DisplayName', sprintf('0rpm: $%.0f nm$', 1e9*rms(detrend(data_ol_Wz0.Dy_int, 0)))); +plot(data_ol_Wz36.f, sqrt(flip(-cumtrapz(flip(data_ol_Wz36.f), flip(data_ol_Wz36.pxx_Dy)))), ... + 'DisplayName', sprintf('6rpm: $%.0f nm$', 1e9*rms(detrend(data_ol_Wz36.Dy_int, 0)))); +plot(data_ol_Wz180.f, sqrt(flip(-cumtrapz(flip(data_ol_Wz180.f), flip(data_ol_Wz180.pxx_Dy)))), ... + 'DisplayName', sprintf('30rpm: $%.1f \\mu m$', 1e6*rms(detrend(data_ol_Wz180.Dy_int, 0)))); hold off; set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); -ylabel('Amplitude [m/V]'); set(gca, 'XTickLabel',[]); -ylim([1e-8, 1e-3]); -legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 3); +xlabel('Frequency [Hz]'); ylabel('CAS [m rms, rad RMS]'); +legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1); +xticks([1e0, 1e1, 1e2]); +title('$D_y$') + +ax2 = nexttile(); +hold on; +plot(data_ol_Wz0.f, sqrt(flip(-cumtrapz(flip(data_ol_Wz0.f), flip(data_ol_Wz0.pxx_Dz)))), ... + 'DisplayName', sprintf('0rpm: $%.0f nm$', 1e9*rms(detrend(data_ol_Wz0.Dz_int, 0)))); +plot(data_ol_Wz36.f, sqrt(flip(-cumtrapz(flip(data_ol_Wz36.f), flip(data_ol_Wz36.pxx_Dz)))), ... + 'DisplayName', sprintf('6rpm: $%.0f nm$', 1e9*rms(detrend(data_ol_Wz36.Dz_int, 0)))); +plot(data_ol_Wz180.f, sqrt(flip(-cumtrapz(flip(data_ol_Wz180.f), flip(data_ol_Wz180.pxx_Dz)))), ... + 'DisplayName', sprintf('30rpm: $%.0f nm$', 1e9*rms(detrend(data_ol_Wz180.Dz_int, 0)))); +hold off; +set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); +xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]); +legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1); +xticks([1e0, 1e1, 1e2]); +title('$D_z$') + +ax3 = nexttile(); +hold on; +plot(data_ol_Wz0.f, sqrt(flip(-cumtrapz(flip(data_ol_Wz0.f), flip(data_ol_Wz0.pxx_Ry)))), ... + 'DisplayName', sprintf('0rpm: $%.1f \\mu$rad', 1e6*rms(detrend(data_ol_Wz0.Ry_int, 0)))); +plot(data_ol_Wz36.f, sqrt(flip(-cumtrapz(flip(data_ol_Wz36.f), flip(data_ol_Wz36.pxx_Ry)))), ... + 'DisplayName', sprintf('6rpm: $%.1f \\mu$rad', 1e6*rms(detrend(data_ol_Wz36.Ry_int, 0)))); +plot(data_ol_Wz180.f, sqrt(flip(-cumtrapz(flip(data_ol_Wz180.f), flip(data_ol_Wz180.pxx_Ry)))), ... + 'DisplayName', sprintf('30rpm: $%.0f \\mu$rad', 1e6*rms(detrend(data_ol_Wz180.Ry_int, 0)))); +hold off; +set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); +xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]); +legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1); +xticks([1e0, 1e1, 1e2]); +title('$R_y$') + +linkaxes([ax1,ax2,ax3],'xy'); +xlim([0.1, 5e2]); ylim([1e-10, 3e-5]); +#+end_src + +** Effect of LAC + +#+begin_src matlab +%% Effect of LAC + +% Load measured noise +data_lac_Wz180 = load('2023-08-11_17-36_m0_lac_on_30rpm.mat'); + +a = J_int_to_X*[data_lac_Wz180.d1; data_lac_Wz180.d2; data_lac_Wz180.d3; data_lac_Wz180.d4; data_lac_Wz180.d5]; +data_lac_Wz180.Dx_int = a(1,:); +data_lac_Wz180.Dy_int = a(2,:); +data_lac_Wz180.Dz_int = a(3,:); +data_lac_Wz180.Rx_int = a(4,:); +data_lac_Wz180.Ry_int = a(5,:); + +[data_lac_Wz180.pxx_Dx, data_lac_Wz180.f] = pwelch(detrend(data_lac_Wz180.Dx_int, 0), win, Noverlap, Nfft, 1/Ts); +[data_lac_Wz180.pxx_Dy, ~ ] = pwelch(detrend(data_lac_Wz180.Dy_int, 0), win, Noverlap, Nfft, 1/Ts); +[data_lac_Wz180.pxx_Dz, ~ ] = pwelch(detrend(data_lac_Wz180.Dz_int, 0), win, Noverlap, Nfft, 1/Ts); +[data_lac_Wz180.pxx_Rx, ~ ] = pwelch(detrend(data_lac_Wz180.Rx_int, 0), win, Noverlap, Nfft, 1/Ts); +[data_lac_Wz180.pxx_Ry, ~ ] = pwelch(detrend(data_lac_Wz180.Ry_int, 0), win, Noverlap, Nfft, 1/Ts); +#+end_src + +Effect of LAC: +- reduce amplitude around 80Hz +- Inject some noise between 200 and 700Hz? + +#+begin_src matlab :exports none :results none +%% Cumulative Amplitude Spectrum of the measured Dx and Dy motion +figure; +tiledlayout(1, 3, 'TileSpacing', 'compact', 'Padding', 'None'); + +ax1 = nexttile(); +hold on; +plot(data_ol_Wz180.f, sqrt(flip(-cumtrapz(flip(data_ol_Wz180.f), flip(data_ol_Wz180.pxx_Dy)))), 'DisplayName', 'OL'); +plot(data_lac_Wz180.f, sqrt(flip(-cumtrapz(flip(data_lac_Wz180.f), flip(data_lac_Wz180.pxx_Dy)))), 'DisplayName', 'LAC'); +hold off; +set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); +xlabel('Frequency [Hz]'); ylabel('CAS [$m$]'); +title('$D_y$'); +legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1); + +ax2 = nexttile(); +hold on; +plot(data_ol_Wz180.f, sqrt(flip(-cumtrapz(flip(data_ol_Wz180.f), flip(data_ol_Wz180.pxx_Dz)))), 'DisplayName', 'OL'); +plot(data_lac_Wz180.f, sqrt(flip(-cumtrapz(flip(data_lac_Wz180.f), flip(data_lac_Wz180.pxx_Dz)))), 'DisplayName', 'LAC'); +hold off; +set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); +xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]); +title('$D_z$'); +legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1); + +ax3 = nexttile(); +hold on; +plot(data_ol_Wz180.f, sqrt(flip(-cumtrapz(flip(data_ol_Wz180.f), flip(data_ol_Wz180.pxx_Ry)))), 'DisplayName', 'OL'); +plot(data_lac_Wz180.f, sqrt(flip(-cumtrapz(flip(data_lac_Wz180.f), flip(data_lac_Wz180.pxx_Ry)))), 'DisplayName', 'LAC'); +hold off; +set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); +xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]); +title('$R_y$'); +legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1); + +linkaxes([ax1,ax2,ax3],'xy'); +xlim([0.1, 5e2]); ylim([1e-10, 3e-5]); +#+end_src + +** Effect of HAC + +#+begin_src matlab +%% Effect of HAC +% Load measured noise +data_hac_Wz180 = load('2023-08-11_16-49_m0_hac_on.mat'); + +a = J_int_to_X*[data_hac_Wz180.d1; data_hac_Wz180.d2; data_hac_Wz180.d3; data_hac_Wz180.d4; data_hac_Wz180.d5]; +data_hac_Wz180.Dx_int = a(1,:); +data_hac_Wz180.Dy_int = a(2,:); +data_hac_Wz180.Dz_int = a(3,:); +data_hac_Wz180.Rx_int = a(4,:); +data_hac_Wz180.Ry_int = a(5,:); + +[data_hac_Wz180.pxx_Dx, data_hac_Wz180.f] = pwelch(detrend(data_hac_Wz180.Dx_int, 0), win, Noverlap, Nfft, 1/Ts); +[data_hac_Wz180.pxx_Dy, ~ ] = pwelch(detrend(data_hac_Wz180.Dy_int, 0), win, Noverlap, Nfft, 1/Ts); +[data_hac_Wz180.pxx_Dz, ~ ] = pwelch(detrend(data_hac_Wz180.Dz_int, 0), win, Noverlap, Nfft, 1/Ts); +[data_hac_Wz180.pxx_Rx, ~ ] = pwelch(detrend(data_hac_Wz180.Rx_int, 0), win, Noverlap, Nfft, 1/Ts); +[data_hac_Wz180.pxx_Ry, ~ ] = pwelch(detrend(data_hac_Wz180.Ry_int, 0), win, Noverlap, Nfft, 1/Ts); +#+end_src + +Bandwidth is approximately 10Hz. + +#+begin_src matlab :exports none :results none +%% Cumulative Amplitude Spectrum of the measured Dx and Dy motion +figure; +tiledlayout(1, 3, 'TileSpacing', 'compact', 'Padding', 'None'); + +ax1 = nexttile(); +hold on; +plot(data_ol_Wz180.f, sqrt(data_ol_Wz180.pxx_Dy), 'DisplayName', 'OL'); +plot(data_lac_Wz180.f, sqrt(data_lac_Wz180.pxx_Dy), 'DisplayName', 'LAC'); +plot(data_hac_Wz180.f, sqrt(data_hac_Wz180.pxx_Dy), 'DisplayName', 'HAC'); +hold off; +set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); +xlabel('Frequency [Hz]'); ylabel('ASD [$m/\sqrt{Hz}$]'); +title('$D_y$'); +legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1); + +ax2 = nexttile(); +hold on; +plot(data_ol_Wz180.f, sqrt(data_ol_Wz180.pxx_Dz), 'DisplayName', 'OL'); +plot(data_lac_Wz180.f, sqrt(data_lac_Wz180.pxx_Dz), 'DisplayName', 'LAC'); +plot(data_hac_Wz180.f, sqrt(data_hac_Wz180.pxx_Dz), 'DisplayName', 'HAC'); +hold off; +set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); +xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]); +title('$D_z$'); +legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1); + +ax3 = nexttile(); +hold on; +plot(data_ol_Wz180.f, sqrt(data_ol_Wz180.pxx_Ry), 'DisplayName', 'OL'); +plot(data_lac_Wz180.f, sqrt(data_lac_Wz180.pxx_Ry), 'DisplayName', 'LAC'); +plot(data_hac_Wz180.f, sqrt(data_hac_Wz180.pxx_Ry), 'DisplayName', 'HAC'); +hold off; +set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); +xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]); +title('$R_y$'); +legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1); + +linkaxes([ax1,ax2,ax3],'xy'); +xlim([1, 1e3]); ylim([1e-12, 1e-7]); +#+end_src + +#+begin_src matlab :exports none :results none +%% Cumulative Amplitude Spectrum of the measured Dx and Dy motion +figure; +tiledlayout(1, 3, 'TileSpacing', 'compact', 'Padding', 'None'); + +ax1 = nexttile(); +hold on; +plot(data_ol_Wz180.f, sqrt(flip(-cumtrapz(flip(data_ol_Wz180.f), flip(data_ol_Wz180.pxx_Dy)))), 'DisplayName', sprintf('OL %.1f nm RMS' )); +plot(data_lac_Wz180.f, sqrt(flip(-cumtrapz(flip(data_lac_Wz180.f), flip(data_lac_Wz180.pxx_Dy)))), 'DisplayName', 'LAC'); +plot(data_hac_Wz180.f, sqrt(flip(-cumtrapz(flip(data_hac_Wz180.f), flip(data_hac_Wz180.pxx_Dy)))), 'DisplayName', 'HAC'); +hold off; +set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); +xlabel('Frequency [Hz]'); ylabel('CAS [$m$]'); +title('$D_y$'); +legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1); + +ax2 = nexttile(); +hold on; +plot(data_ol_Wz180.f, sqrt(flip(-cumtrapz(flip(data_ol_Wz180.f), flip(data_ol_Wz180.pxx_Dz)))), 'DisplayName', 'OL'); +plot(data_lac_Wz180.f, sqrt(flip(-cumtrapz(flip(data_lac_Wz180.f), flip(data_lac_Wz180.pxx_Dz)))), 'DisplayName', 'LAC'); +plot(data_hac_Wz180.f, sqrt(flip(-cumtrapz(flip(data_hac_Wz180.f), flip(data_hac_Wz180.pxx_Dz)))), 'DisplayName', 'HAC'); +hold off; +set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); +xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]); +title('$D_z$'); +legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1); + +ax3 = nexttile(); +hold on; +plot(data_ol_Wz180.f, sqrt(flip(-cumtrapz(flip(data_ol_Wz180.f), flip(data_ol_Wz180.pxx_Ry)))), 'DisplayName', 'OL'); +plot(data_lac_Wz180.f, sqrt(flip(-cumtrapz(flip(data_lac_Wz180.f), flip(data_lac_Wz180.pxx_Ry)))), 'DisplayName', 'LAC'); +plot(data_hac_Wz180.f, sqrt(flip(-cumtrapz(flip(data_hac_Wz180.f), flip(data_hac_Wz180.pxx_Ry)))), 'DisplayName', 'HAC'); +hold off; +set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); +xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]); +title('$R_y$'); +legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1); + +linkaxes([ax1,ax2,ax3],'xy'); +xlim([0.1, 5e2]); ylim([1e-10, 3e-5]); +#+end_src + +* TODO Scans for scientific experiments :noexport: +:PROPERTIES: +:header-args:matlab+: :tangle matlab/test_id31_x_scans.m +:END: +<> +** Introduction :ignore: + +- Section ref:sec:id31_scans_tomography +- Section ref:sec:id31_scans_dz +- Section ref:sec:id31_scans_reflectivity +- Section ref:sec:id31_scans_dy +- Section ref:sec:id31_scans_diffraction_tomo + +** Matlab Init :noexport:ignore: +#+begin_src matlab :tangle no :exports none :results silent :noweb yes :var current_dir=(file-name-directory buffer-file-name) +<> +#+end_src + +#+begin_src matlab :exports none :results silent :noweb yes +<> +#+end_src + +#+begin_src matlab :tangle no :noweb yes +<> +#+end_src + +#+begin_src matlab :eval no :noweb yes +<> +#+end_src + +#+begin_src matlab :noweb yes +<> +#+end_src + +** $R_z$ scans: Tomography +<> +*** Introduction :ignore: + +- m0: 30rpm, 6rpm, 1rpm +- m1: 6rpm, 1rpm +- m2: 6rpm, 1rpm +- m3: 1rpm + +*Issue with this control architecture (or controller?)*: +- The position is not converging to zero + +*Compare*: +- 1rpm, 6rpm, 30rpm +- at 1rpm: m0, m1, m2, m3 (same robust controller!) + +*** Robust Control - 1rpm +1RPM scans are performed for all the masses with the same robust controller. + +#+begin_src matlab +%% Load Tomography scans with robust controller +data_tomo_1rpm_m0 = load("2023-08-11_11-37_tomography_1rpm_m0.mat"); +data_tomo_1rpm_m0.time = Ts*[0:length(data_tomo_1rpm_m0.Rz)-1]; + +data_tomo_1rpm_m1 = load("2023-08-11_11-15_tomography_1rpm_m1.mat"); +data_tomo_1rpm_m1.time = Ts*[0:length(data_tomo_1rpm_m1.Rz)-1]; + +data_tomo_1rpm_m2 = load("2023-08-11_10-59_tomography_1rpm_m2.mat"); +data_tomo_1rpm_m2.time = Ts*[0:length(data_tomo_1rpm_m2.Rz)-1]; + +data_tomo_1rpm_m3 = load("2023-08-11_10-24_tomography_1rpm_m3.mat"); +data_tomo_1rpm_m3.time = Ts*[0:length(data_tomo_1rpm_m3.Rz)-1]; +#+end_src + +The problem for these scans is that the position initialization was not make properly, so the open-loop errors are quite large (see Figure ref:fig:id31_tomo_1rpm_robust_m0). + +#+begin_src matlab :exports none :results none +%% $D_x$, $D_y$ and $D_z$ motion during a slow (1RPM) tomography experiment. Open Loop data is shown in blue and closed-loop data in red +figure; +tiledlayout(1, 2, 'TileSpacing', 'compact', 'Padding', 'None'); + +ax1 = nexttile; +hold on; +plot(1e6*data_tomo_1rpm_m0.Dx_int(data_tomo_1rpm_m0.hac_status == 0), 1e6*data_tomo_1rpm_m0.Dy_int(data_tomo_1rpm_m0.hac_status == 0), ... + 'DisplayName', 'OL') +plot(1e6*data_tomo_1rpm_m0.Dx_int(data_tomo_1rpm_m0.hac_status == 1), 1e6*data_tomo_1rpm_m0.Dy_int(data_tomo_1rpm_m0.hac_status == 1), ... + 'DisplayName', 'CL') +hold off; +axis equal +xlabel("X motion [$\mu m$]"); +ylabel("Y motion [$\mu m$]"); +legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1); +% xlim([-3, 3]) +% ylim([-3, 3]) ax2 = nexttile; hold on; -for i =1:6 - plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_hac_m0(sprintf('eL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(1,:), 0.5]); -end -for i =1:6 - plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_hac_m1(sprintf('eL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(2,:), 0.5]); -end -for i =1:6 - plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_hac_m2(sprintf('eL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(3,:), 0.5]); -end -for i =1:6 - plot(freqs, 180/pi*angle(squeeze(freqresp(Gm_hac_m3(sprintf('eL%i', i), sprintf('u%i', i)), freqs, 'Hz'))), 'color', [colors(4,:), 0.5]); -end +plot(1e6*data_tomo_1rpm_m0.Dy_int(data_tomo_1rpm_m0.hac_status == 0), 1e6*data_tomo_1rpm_m0.Dz_int(data_tomo_1rpm_m0.hac_status == 0), ... + 'DisplayName', 'OL') +plot(1e6*data_tomo_1rpm_m0.Dy_int(data_tomo_1rpm_m0.hac_status == 1), 1e6*data_tomo_1rpm_m0.Dz_int(data_tomo_1rpm_m0.hac_status == 1), ... + 'DisplayName', 'CL') hold off; -set(gca, 'XScale', 'log'); set(gca, 'YScale', 'lin'); -xlabel('Frequency [Hz]'); ylabel('Phase [deg]'); -hold off; -yticks(-360:90:360); -ylim([-180, 180]) - -linkaxes([ax1,ax2],'x'); -xlim([1, 1e3]); +% axis equal +% xlim([-3, 3]) +ylim([-0.2, 1.1]) +xlabel("Y motion [$\mu m$]"); +ylabel("Z motion [$\mu m$]"); #+end_src #+begin_src matlab :tangle no :exports results :results file replace -exportFig('figs/id31_hac_damped_plant_estimated_simscape.pdf', 'width', 'wide', 'height', 'tall'); +exportFig('figs/id31_tomo_1rpm_robust_m0.pdf', 'width', 'full', 'height', 'normal'); #+end_src -#+name: fig:id31_hac_damped_plant_estimated_simscape -#+caption: description +#+name: fig:id31_tomo_1rpm_robust_m0 +#+caption: $D_x$, $D_y$ and $D_z$ motion during a slow (1RPM) tomography experiment. Open Loop data is shown in blue and closed-loop data in red #+RESULTS: -[[file:figs/id31_hac_damped_plant_estimated_simscape.png]] +[[file:figs/id31_tomo_1rpm_robust_m0.png]] + +The obtained open-loop and closed-loop errors are shown in tables ref:tab:id31_tomo_1rpm_robust_ol_errors and ref:tab:id31_tomo_1rpm_robust_cl_errors respectively. + +#+begin_src matlab +%% Compute RMS values while in closed-loop and open-loop +[~, i_m0] = find(data_tomo_1rpm_m0.hac_status == 1); +data_tomo_1rpm_m0.Dx_rms_cl = rms(detrend(data_tomo_1rpm_m0.Dx_int(i_m0+50000:end), 0)); +data_tomo_1rpm_m0.Dy_rms_cl = rms(detrend(data_tomo_1rpm_m0.Dy_int(i_m0+50000:end), 0)); +data_tomo_1rpm_m0.Dz_rms_cl = rms(detrend(data_tomo_1rpm_m0.Dz_int(i_m0+50000:end), 0)); +data_tomo_1rpm_m0.Rx_rms_cl = rms(detrend(data_tomo_1rpm_m0.Rx_int(i_m0+50000:end), 0)); +data_tomo_1rpm_m0.Ry_rms_cl = rms(detrend(data_tomo_1rpm_m0.Ry_int(i_m0+50000:end), 0)); + +data_tomo_1rpm_m0.Dx_rms_ol = rms(detrend(data_tomo_1rpm_m0.Dx_int(1:i_m0), 0)); +data_tomo_1rpm_m0.Dy_rms_ol = rms(detrend(data_tomo_1rpm_m0.Dy_int(1:i_m0), 0)); +data_tomo_1rpm_m0.Dz_rms_ol = rms(detrend(data_tomo_1rpm_m0.Dz_int(1:i_m0), 0)); +data_tomo_1rpm_m0.Rx_rms_ol = rms(detrend(data_tomo_1rpm_m0.Rx_int(1:i_m0), 0)); +data_tomo_1rpm_m0.Ry_rms_ol = rms(detrend(data_tomo_1rpm_m0.Ry_int(1:i_m0), 0)); + +%% Compute RMS values while in closed-loop and open-loop +[~, i_m1] = find(data_tomo_1rpm_m1.hac_status == 1); +data_tomo_1rpm_m1.Dx_rms_cl = rms(detrend(data_tomo_1rpm_m1.Dx_int(i_m1+50000:end), 0)); +data_tomo_1rpm_m1.Dy_rms_cl = rms(detrend(data_tomo_1rpm_m1.Dy_int(i_m1+50000:end), 0)); +data_tomo_1rpm_m1.Dz_rms_cl = rms(detrend(data_tomo_1rpm_m1.Dz_int(i_m1+50000:end), 0)); +data_tomo_1rpm_m1.Rx_rms_cl = rms(detrend(data_tomo_1rpm_m1.Rx_int(i_m1+50000:end), 0)); +data_tomo_1rpm_m1.Ry_rms_cl = rms(detrend(data_tomo_1rpm_m1.Ry_int(i_m1+50000:end), 0)); + +data_tomo_1rpm_m1.Dx_rms_ol = rms(detrend(data_tomo_1rpm_m1.Dx_int(1:i_m1), 0)); +data_tomo_1rpm_m1.Dy_rms_ol = rms(detrend(data_tomo_1rpm_m1.Dy_int(1:i_m1), 0)); +data_tomo_1rpm_m1.Dz_rms_ol = rms(detrend(data_tomo_1rpm_m1.Dz_int(1:i_m1), 0)); +data_tomo_1rpm_m1.Rx_rms_ol = rms(detrend(data_tomo_1rpm_m1.Rx_int(1:i_m1), 0)); +data_tomo_1rpm_m1.Ry_rms_ol = rms(detrend(data_tomo_1rpm_m1.Ry_int(1:i_m1), 0)); + +%% Compute RMS values while in closed-loop and open-loop +[~, i_m2] = find(data_tomo_1rpm_m2.hac_status == 1); +data_tomo_1rpm_m2.Dx_rms_cl = rms(detrend(data_tomo_1rpm_m2.Dx_int(i_m2+50000:end), 0)); +data_tomo_1rpm_m2.Dy_rms_cl = rms(detrend(data_tomo_1rpm_m2.Dy_int(i_m2+50000:end), 0)); +data_tomo_1rpm_m2.Dz_rms_cl = rms(detrend(data_tomo_1rpm_m2.Dz_int(i_m2+50000:end), 0)); +data_tomo_1rpm_m2.Rx_rms_cl = rms(detrend(data_tomo_1rpm_m2.Rx_int(i_m2+50000:end), 0)); +data_tomo_1rpm_m2.Ry_rms_cl = rms(detrend(data_tomo_1rpm_m2.Ry_int(i_m2+50000:end), 0)); + +data_tomo_1rpm_m2.Dx_rms_ol = rms(detrend(data_tomo_1rpm_m2.Dx_int(1:i_m2), 0)); +data_tomo_1rpm_m2.Dy_rms_ol = rms(detrend(data_tomo_1rpm_m2.Dy_int(1:i_m2), 0)); +data_tomo_1rpm_m2.Dz_rms_ol = rms(detrend(data_tomo_1rpm_m2.Dz_int(1:i_m2), 0)); +data_tomo_1rpm_m2.Rx_rms_ol = rms(detrend(data_tomo_1rpm_m2.Rx_int(1:i_m2), 0)); +data_tomo_1rpm_m2.Ry_rms_ol = rms(detrend(data_tomo_1rpm_m2.Ry_int(1:i_m2), 0)); + +%% Compute RMS values while in closed-loop and open-loop +[~, i_m3] = find(data_tomo_1rpm_m3.hac_status == 1); +data_tomo_1rpm_m3.Dx_rms_cl = rms(detrend(data_tomo_1rpm_m3.Dx_int(i_m3+50000:end), 0)); +data_tomo_1rpm_m3.Dy_rms_cl = rms(detrend(data_tomo_1rpm_m3.Dy_int(i_m3+50000:end), 0)); +data_tomo_1rpm_m3.Dz_rms_cl = rms(detrend(data_tomo_1rpm_m3.Dz_int(i_m3+50000:end), 0)); +data_tomo_1rpm_m3.Rx_rms_cl = rms(detrend(data_tomo_1rpm_m3.Rx_int(i_m3+50000:end), 0)); +data_tomo_1rpm_m3.Ry_rms_cl = rms(detrend(data_tomo_1rpm_m3.Ry_int(i_m3+50000:end), 0)); + +data_tomo_1rpm_m3.Dx_rms_ol = rms(detrend(data_tomo_1rpm_m3.Dx_int(1:i_m3), 0)); +data_tomo_1rpm_m3.Dy_rms_ol = rms(detrend(data_tomo_1rpm_m3.Dy_int(1:i_m3), 0)); +data_tomo_1rpm_m3.Dz_rms_ol = rms(detrend(data_tomo_1rpm_m3.Dz_int(1:i_m3), 0)); +data_tomo_1rpm_m3.Rx_rms_ol = rms(detrend(data_tomo_1rpm_m3.Rx_int(1:i_m3), 0)); +data_tomo_1rpm_m3.Ry_rms_ol = rms(detrend(data_tomo_1rpm_m3.Ry_int(1:i_m3), 0)); +#+end_src + +#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*) +data2orgtable([1e6*data_tomo_1rpm_m0.Dx_rms_ol, 1e6*data_tomo_1rpm_m0.Dy_rms_ol, 1e9*data_tomo_1rpm_m0.Dz_rms_ol, 1e6*data_tomo_1rpm_m0.Rx_rms_ol, 1e6*data_tomo_1rpm_m0.Ry_rms_ol; ... + 1e6*data_tomo_1rpm_m1.Dx_rms_ol, 1e6*data_tomo_1rpm_m1.Dy_rms_ol, 1e9*data_tomo_1rpm_m1.Dz_rms_ol, 1e6*data_tomo_1rpm_m1.Rx_rms_ol, 1e6*data_tomo_1rpm_m1.Ry_rms_ol; ... + 1e6*data_tomo_1rpm_m2.Dx_rms_ol, 1e6*data_tomo_1rpm_m2.Dy_rms_ol, 1e9*data_tomo_1rpm_m2.Dz_rms_ol, 1e6*data_tomo_1rpm_m2.Rx_rms_ol, 1e6*data_tomo_1rpm_m2.Ry_rms_ol; ... + 1e6*data_tomo_1rpm_m3.Dx_rms_ol, 1e6*data_tomo_1rpm_m3.Dy_rms_ol, 1e9*data_tomo_1rpm_m3.Dz_rms_ol, 1e6*data_tomo_1rpm_m3.Rx_rms_ol, 1e6*data_tomo_1rpm_m3.Ry_rms_ol], ... + {'$m_0$', '$m_1$', '$m_2$', '$m_3$'}, {'$D_x$ [$\mu m$]', '$D_y$ [$\mu m$]', '$D_z$ [$nm$]', '$R_x$ [$\mu\text{rad}$]', '$R_y$ [$\mu\text{rad}$]'}, ' %.0f '); +#+end_src + +#+name: tab:id31_tomo_1rpm_robust_ol_errors +#+caption: Measured error during open-loop tomography scans (1rpm) +#+attr_latex: :environment tabularx :width \linewidth :align cXXXXX +#+attr_latex: :center t :booktabs t +#+RESULTS: +| | $D_x$ [$\mu m$] | $D_y$ [$\mu m$] | $D_z$ [$nm$] | $R_x$ [$\mu\text{rad}$] | $R_y$ [$\mu\text{rad}$] | +|-------+-----------------+-----------------+--------------+-------------------------+-------------------------| +| $m_0$ | 6 | 6 | 32 | 34 | 34 | +| $m_1$ | 6 | 7 | 26 | 51 | 55 | +| $m_2$ | 36 | 38 | 36 | 259 | 253 | +| $m_3$ | 31 | 33 | 38 | 214 | 203 | + +#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*) +data2orgtable([1e9*data_tomo_1rpm_m0.Dx_rms_cl, 1e9*data_tomo_1rpm_m0.Dy_rms_cl, 1e9*data_tomo_1rpm_m0.Dz_rms_cl, 1e9*data_tomo_1rpm_m0.Rx_rms_cl, 1e9*data_tomo_1rpm_m0.Ry_rms_cl; ... + 1e9*data_tomo_1rpm_m1.Dx_rms_cl, 1e9*data_tomo_1rpm_m1.Dy_rms_cl, 1e9*data_tomo_1rpm_m1.Dz_rms_cl, 1e9*data_tomo_1rpm_m1.Rx_rms_cl, 1e9*data_tomo_1rpm_m1.Ry_rms_cl; ... + 1e9*data_tomo_1rpm_m2.Dx_rms_cl, 1e9*data_tomo_1rpm_m2.Dy_rms_cl, 1e9*data_tomo_1rpm_m2.Dz_rms_cl, 1e9*data_tomo_1rpm_m2.Rx_rms_cl, 1e9*data_tomo_1rpm_m2.Ry_rms_cl; ... + 1e9*data_tomo_1rpm_m3.Dx_rms_cl, 1e9*data_tomo_1rpm_m3.Dy_rms_cl, 1e9*data_tomo_1rpm_m3.Dz_rms_cl, 1e9*data_tomo_1rpm_m3.Rx_rms_cl, 1e9*data_tomo_1rpm_m3.Ry_rms_cl], ... + {'$m_0$', '$m_1$', '$m_2$', '$m_3$'}, {'$D_x$ [nm]', '$D_y$ [nm]', '$D_z$ [nm]', '$R_x$ [nrad]', '$R_y$ [nrad]'}, ' %.0f '); +#+end_src + +#+name: tab:id31_tomo_1rpm_robust_cl_errors +#+caption: Measured error during closed-loop tomography scans (1rpm, robust controller) +#+attr_latex: :environment tabularx :width \linewidth :align cXXXXX +#+attr_latex: :center t :booktabs t +#+RESULTS: +| | $D_x$ [nm] | $D_y$ [nm] | $D_z$ [nm] | $R_x$ [nrad] | $R_y$ [nrad] | +|-------+------------+------------+------------+--------------+--------------| +| $m_0$ | 13 | 15 | 5 | 57 | 55 | +| $m_1$ | 16 | 25 | 6 | 102 | 55 | +| $m_2$ | 25 | 25 | 7 | 120 | 103 | +| $m_3$ | 40 | 53 | 9 | 225 | 169 | + +*** Robust Control - 6rpm +#+begin_src matlab +data_tomo_6rpm_m0 = load("2023-08-11_11-31_tomography_6rpm_m0.mat"); +data_tomo_6rpm_m0.time = Ts*[0:length(data_tomo_6rpm_m0.Rz)-1]; +#+end_src + +#+begin_src matlab +data_tomo_6rpm_m1 = load("2023-08-11_11-23_tomography_6rpm_m1.mat"); +data_tomo_6rpm_m1.time = Ts*[0:length(data_tomo_6rpm_m1.Rz)-1]; +#+end_src + +#+begin_src matlab :exports none :results none +%% $D_x$, $D_y$ and $D_z$ motion during a slow (6RPM) tomography experiment. Open Loop data is shown in blue and closed-loop data in red +figure; +tiledlayout(1, 2, 'TileSpacing', 'compact', 'Padding', 'None'); + +ax1 = nexttile; +hold on; +plot(1e6*data_tomo_6rpm_m1.Dx_int(data_tomo_6rpm_m1.hac_status == 0), 1e6*data_tomo_6rpm_m1.Dy_int(data_tomo_6rpm_m1.hac_status == 0), ... + 'DisplayName', 'OL') +plot(1e6*data_tomo_6rpm_m1.Dx_int(data_tomo_6rpm_m1.hac_status == 1), 1e6*data_tomo_6rpm_m1.Dy_int(data_tomo_6rpm_m1.hac_status == 1), ... + 'DisplayName', 'CL') +hold off; +axis equal +xlabel("X motion [$\mu m$]"); +ylabel("Y motion [$\mu m$]"); +legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1); +% xlim([-3, 3]) +% ylim([-3, 3]) + +ax2 = nexttile; +hold on; +plot(1e6*data_tomo_6rpm_m1.Dy_int(data_tomo_6rpm_m1.hac_status == 0), 1e6*data_tomo_6rpm_m1.Dz_int(data_tomo_6rpm_m1.hac_status == 0), ... + 'DisplayName', 'OL') +plot(1e6*data_tomo_6rpm_m1.Dy_int(data_tomo_6rpm_m1.hac_status == 1), 1e6*data_tomo_6rpm_m1.Dz_int(data_tomo_6rpm_m1.hac_status == 1), ... + 'DisplayName', 'CL') +hold off; +% axis equal +% xlim([-3, 3]) +ylim([-0.2, 1.1]) +xlabel("Y motion [$\mu m$]"); +ylabel("Z motion [$\mu m$]"); +#+end_src + +#+begin_src matlab +%% Compute RMS values while in closed-loop +[~, i_m0] = find(data_tomo_6rpm_m0.hac_status == 1); +data_tomo_6rpm_m0.Dx_rms_cl = rms(detrend(data_tomo_6rpm_m0.Dx_int(i_m0+50000:end), 0)); +data_tomo_6rpm_m0.Dy_rms_cl = rms(detrend(data_tomo_6rpm_m0.Dy_int(i_m0+50000:end), 0)); +data_tomo_6rpm_m0.Dz_rms_cl = rms(detrend(data_tomo_6rpm_m0.Dz_int(i_m0+50000:end), 0)); +data_tomo_6rpm_m0.Rx_rms_cl = rms(detrend(data_tomo_6rpm_m0.Rx_int(i_m0+50000:end), 0)); +data_tomo_6rpm_m0.Ry_rms_cl = rms(detrend(data_tomo_6rpm_m0.Ry_int(i_m0+50000:end), 0)); + +data_tomo_6rpm_m0.Dx_rms_ol = rms(detrend(data_tomo_6rpm_m0.Dx_int(1:i_m0), 0)); +data_tomo_6rpm_m0.Dy_rms_ol = rms(detrend(data_tomo_6rpm_m0.Dy_int(1:i_m0), 0)); +data_tomo_6rpm_m0.Dz_rms_ol = rms(detrend(data_tomo_6rpm_m0.Dz_int(1:i_m0), 0)); +data_tomo_6rpm_m0.Rx_rms_ol = rms(detrend(data_tomo_6rpm_m0.Rx_int(1:i_m0), 0)); +data_tomo_6rpm_m0.Ry_rms_ol = rms(detrend(data_tomo_6rpm_m0.Ry_int(1:i_m0), 0)); + +%% Compute RMS values while in closed-loop +[~, i_m1] = find(data_tomo_6rpm_m1.hac_status == 1); +data_tomo_6rpm_m1.Dx_rms_cl = rms(detrend(data_tomo_6rpm_m1.Dx_int(i_m1+50000:end), 0)); +data_tomo_6rpm_m1.Dy_rms_cl = rms(detrend(data_tomo_6rpm_m1.Dy_int(i_m1+50000:end), 0)); +data_tomo_6rpm_m1.Dz_rms_cl = rms(detrend(data_tomo_6rpm_m1.Dz_int(i_m1+50000:end), 0)); +data_tomo_6rpm_m1.Rx_rms_cl = rms(detrend(data_tomo_6rpm_m1.Rx_int(i_m1+50000:end), 0)); +data_tomo_6rpm_m1.Ry_rms_cl = rms(detrend(data_tomo_6rpm_m1.Ry_int(i_m1+50000:end), 0)); + +data_tomo_6rpm_m1.Dx_rms_ol = rms(detrend(data_tomo_6rpm_m1.Dx_int(1:i_m1), 0)); +data_tomo_6rpm_m1.Dy_rms_ol = rms(detrend(data_tomo_6rpm_m1.Dy_int(1:i_m1), 0)); +data_tomo_6rpm_m1.Dz_rms_ol = rms(detrend(data_tomo_6rpm_m1.Dz_int(1:i_m1), 0)); +data_tomo_6rpm_m1.Rx_rms_ol = rms(detrend(data_tomo_6rpm_m1.Rx_int(1:i_m1), 0)); +data_tomo_6rpm_m1.Ry_rms_ol = rms(detrend(data_tomo_6rpm_m1.Ry_int(1:i_m1), 0)); +#+end_src + +#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*) +data2orgtable([1e6*data_tomo_6rpm_m0.Dx_rms_ol, 1e6*data_tomo_6rpm_m0.Dy_rms_ol, 1e9*data_tomo_6rpm_m0.Dz_rms_ol, 1e6*data_tomo_6rpm_m0.Rx_rms_ol, 1e6*data_tomo_6rpm_m0.Ry_rms_ol; ... + 1e6*data_tomo_6rpm_m1.Dx_rms_ol, 1e6*data_tomo_6rpm_m1.Dy_rms_ol, 1e9*data_tomo_6rpm_m1.Dz_rms_ol, 1e6*data_tomo_6rpm_m1.Rx_rms_ol, 1e6*data_tomo_6rpm_m1.Ry_rms_ol], ... + {'$m_0$', '$m_1$'}, {'$D_x$ [$\mu m$]', '$D_y$ [$\mu m$]', '$D_z$ [$nm$]', '$R_x$ [$\mu\text{rad}$]', '$R_y$ [$\mu\text{rad}$]'}, ' %.0f '); +#+end_src + +#+name: tab:id31_tomo_6rpm_robust_ol_errors +#+caption: Measured error during open-loop tomography scans (6rpm) +#+attr_latex: :environment tabularx :width \linewidth :align cXXXXX +#+attr_latex: :center t :booktabs t +#+RESULTS: +| | $D_x$ [$\mu m$] | $D_y$ [$\mu m$] | $D_z$ [$nm$] | $R_x$ [$\mu\text{rad}$] | $R_y$ [$\mu\text{rad}$] | +|-------+-----------------+-----------------+--------------+-------------------------+-------------------------| +| $m_0$ | 8 | 7 | 20 | 41 | 41 | +| $m_1$ | 4 | 4 | 21 | 39 | 39 | + +#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*) +data2orgtable([1e9*data_tomo_6rpm_m0.Dx_rms_cl, 1e9*data_tomo_6rpm_m0.Dy_rms_cl, 1e9*data_tomo_6rpm_m0.Dz_rms_cl, 1e9*data_tomo_6rpm_m0.Rx_rms_cl, 1e9*data_tomo_6rpm_m0.Ry_rms_cl; ... + 1e9*data_tomo_6rpm_m1.Dx_rms_cl, 1e9*data_tomo_6rpm_m1.Dy_rms_cl, 1e9*data_tomo_6rpm_m1.Dz_rms_cl, 1e9*data_tomo_6rpm_m1.Rx_rms_cl, 1e9*data_tomo_6rpm_m1.Ry_rms_cl], ... + {'$m_0$', '$m_1$'}, {'$D_x$ [nm]', '$D_y$ [nm]', '$D_z$ [nm]', '$R_x$ [nrad]', '$R_y$ [nrad]'}, ' %.0f '); +#+end_src + +#+name: tab:id31_tomo_6rpm_robust_cl_errors +#+caption: Measured error during closed-loop tomography scans (6rpm, robust controller) +#+attr_latex: :environment tabularx :width \linewidth :align cXXXXX +#+attr_latex: :center t :booktabs t +#+RESULTS: +| | $D_x$ [nm] | $D_y$ [nm] | $D_z$ [nm] | $R_x$ [nrad] | $R_y$ [nrad] | +|-------+------------+------------+------------+--------------+--------------| +| $m_0$ | 17 | 19 | 5 | 70 | 73 | +| $m_1$ | 20 | 26 | 7 | 110 | 77 | + +*** Robust Control - 30rpm +#+begin_src matlab +%% Load Data +data_tomo_30rpm_m0 = load("2023-08-17_15-26_tomography_30rpm_m0_robust.mat"); +data_tomo_30rpm_m0.time = Ts*[0:length(data_tomo_30rpm_m0.Rz)-1]; +#+end_src + +#+begin_src matlab +figure; +hold on; +plot3(1e6*data_tomo_30rpm_m0.Dx_int(data_tomo_30rpm_m0.hac_status == 0), ... + 1e6*data_tomo_30rpm_m0.Dy_int(data_tomo_30rpm_m0.hac_status == 0), ... + 1e6*data_tomo_30rpm_m0.Dz_int(data_tomo_30rpm_m0.hac_status == 0), ... + 'DisplayName', 'OL') +plot3(1e6*data_tomo_30rpm_m0.Dx_int(data_tomo_30rpm_m0.hac_status == 1), ... + 1e6*data_tomo_30rpm_m0.Dy_int(data_tomo_30rpm_m0.hac_status == 1), ... + 1e6*data_tomo_30rpm_m0.Dz_int(data_tomo_30rpm_m0.hac_status == 1), ... + 'DisplayName', 'CL') +hold off; +xlabel('$D_x$ [$\mu$m]'); +ylabel('$D_y$ [$\mu$m]'); +zlabel('$D_z$ [$\mu$m]'); +axis equal +#+end_src + +#+begin_src matlab :exports none :results none +%% Measured motion during tomography scan at 30RPM with a robust controller +figure; +tiledlayout(1, 2, 'TileSpacing', 'compact', 'Padding', 'None'); + +ax1 = nexttile; +hold on; +plot(1e6*data_tomo_30rpm_m0.Dx_int(data_tomo_30rpm_m0.hac_status == 0), 1e6*data_tomo_30rpm_m0.Dy_int(data_tomo_30rpm_m0.hac_status == 0), ... + 'DisplayName', 'OL') +plot(1e6*data_tomo_30rpm_m0.Dx_int(data_tomo_30rpm_m0.hac_status == 1), 1e6*data_tomo_30rpm_m0.Dy_int(data_tomo_30rpm_m0.hac_status == 1), ... + 'DisplayName', 'CL') +hold off; +axis square +xlabel("X motion [$\mu m$]"); +ylabel("Y motion [$\mu m$]"); +legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1); +xlim([-3, 3]) +ylim([-3, 3]) + +ax2 = nexttile; +hold on; +plot(1e6*data_tomo_30rpm_m0.Dy_int(data_tomo_30rpm_m0.hac_status == 0), 1e6*data_tomo_30rpm_m0.Dz_int(data_tomo_30rpm_m0.hac_status == 0), ... + 'DisplayName', 'OL') +plot(1e6*data_tomo_30rpm_m0.Dy_int(data_tomo_30rpm_m0.hac_status == 1), 1e6*data_tomo_30rpm_m0.Dz_int(data_tomo_30rpm_m0.hac_status == 1), ... + 'DisplayName', 'CL') +hold off; +axis equal +xlim([-3, 3]) +ylim([-3, 3]) +xlabel("Y motion [$\mu m$]"); +ylabel("Z motion [$\mu m$]"); +#+end_src + +#+begin_src matlab :tangle no :exports results :results file replace +exportFig('figs/id31_tomography_m0_30rpm_robust_xyz.pdf', 'width', 'full', 'height', 'tall'); +#+end_src + +#+name: fig:id31_tomography_m0_30rpm_robust_xyz +#+caption: Measured motion during tomography scan at 30RPM with a robust controller +#+RESULTS: +[[file:figs/id31_tomography_m0_30rpm_robust_xyz.png]] + +#+begin_src matlab +%% Compute RMS values while in closed-loop +[~, i_m0] = find(data_tomo_30rpm_m0.hac_status == 1); +data_tomo_30rpm_m0.Dx_rms_cl = rms(detrend(data_tomo_30rpm_m0.Dx_int(i_m0+50000:end), 0)); +data_tomo_30rpm_m0.Dy_rms_cl = rms(detrend(data_tomo_30rpm_m0.Dy_int(i_m0+50000:end), 0)); +data_tomo_30rpm_m0.Dz_rms_cl = rms(detrend(data_tomo_30rpm_m0.Dz_int(i_m0+50000:end), 0)); +data_tomo_30rpm_m0.Rx_rms_cl = rms(detrend(data_tomo_30rpm_m0.Rx_int(i_m0+50000:end), 0)); +data_tomo_30rpm_m0.Ry_rms_cl = rms(detrend(data_tomo_30rpm_m0.Ry_int(i_m0+50000:end), 0)); + +data_tomo_30rpm_m0.Dx_rms_ol = rms(detrend(data_tomo_30rpm_m0.Dx_int(1:i_m0), 0)); +data_tomo_30rpm_m0.Dy_rms_ol = rms(detrend(data_tomo_30rpm_m0.Dy_int(1:i_m0), 0)); +data_tomo_30rpm_m0.Dz_rms_ol = rms(detrend(data_tomo_30rpm_m0.Dz_int(1:i_m0), 0)); +data_tomo_30rpm_m0.Rx_rms_ol = rms(detrend(data_tomo_30rpm_m0.Rx_int(1:i_m0), 0)); +data_tomo_30rpm_m0.Ry_rms_ol = rms(detrend(data_tomo_30rpm_m0.Ry_int(1:i_m0), 0)); +#+end_src + +#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*) +data2orgtable([1e6*data_tomo_30rpm_m0.Dx_rms_ol, 1e6*data_tomo_30rpm_m0.Dy_rms_ol, 1e9*data_tomo_30rpm_m0.Dz_rms_ol, 1e6*data_tomo_30rpm_m0.Rx_rms_ol, 1e6*data_tomo_30rpm_m0.Ry_rms_ol], ... + {'$m_0$'}, {'$D_x$ [$\mu m$]', '$D_y$ [$\mu m$]', '$D_z$ [$nm$]', '$R_x$ [$\mu\text{rad}$]', '$R_y$ [$\mu\text{rad}$]'}, ' %.0f '); +#+end_src + +#+name: tab:id31_tomo_30rpm_robust_ol_errors +#+caption: Measured error during open-loop tomography scans (30rpm) +#+attr_latex: :environment tabularx :width \linewidth :align cXXXXX +#+attr_latex: :center t :booktabs t +#+RESULTS: +| | $D_x$ [$\mu m$] | $D_y$ [$\mu m$] | $D_z$ [$nm$] | $R_x$ [$\mu\text{rad}$] | $R_y$ [$\mu\text{rad}$] | +|-------+-----------------+-----------------+--------------+-------------------------+-------------------------| +| $m_0$ | 2 | 2 | 24 | 10 | 10 | + +#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*) +data2orgtable([1e9*data_tomo_30rpm_m0.Dx_rms_cl, 1e9*data_tomo_30rpm_m0.Dy_rms_cl, 1e9*data_tomo_30rpm_m0.Dz_rms_cl, 1e9*data_tomo_30rpm_m0.Rx_rms_cl, 1e9*data_tomo_30rpm_m0.Ry_rms_cl], ... + {'$m_0$'}, {'$D_x$ [nm]', '$D_y$ [nm]', '$D_z$ [nm]', '$R_x$ [nrad]', '$R_y$ [nrad]'}, ' %.0f '); +#+end_src + +#+name: tab:id31_tomo_30rpm_robust_cl_errors +#+caption: Measured error during closed-loop tomography scans (30rpm, robust controller) +#+attr_latex: :environment tabularx :width \linewidth :align cXXXXX +#+attr_latex: :center t :booktabs t +#+RESULTS: +| | $D_x$ [nm] | $D_y$ [nm] | $D_z$ [nm] | $R_x$ [nrad] | $R_y$ [nrad] | +|-------+------------+------------+------------+--------------+--------------| +| $m_0$ | 34 | 38 | 10 | 127 | 129 | + +#+begin_src matlab :exports none +yztomography3dmovie('movies/id31_tomography_m0_30rpm_robust_xyz.avi', data_tomo_30rpm_m0, 'di', 300); +#+end_src + +#+begin_src matlab :exports none +yztomographymovie('movies/tomography_30rpm_m0_robust', data_tomo_30rpm_m0, 'xlim_ax1', [-3, 3], 'ylim_ax1', [-3, 3], 'xlim_ax2', [-300, 300], 'ylim_ax2', [-300, 300]) +#+end_src + +*** TODO Slow Tomography Scans Comparison of control performances :noexport: +#+begin_src matlab +% Decentralized in the frame of the struts +data = load("2023-08-18_10-43_m0_1rpm.mat"); +data.time = Ts*[0:length(data.Rz)-1]; + +% Rotating cartesian frame +data_cart = load("2023-08-18_18-33_m0_1rpm_K_cart.mat"); +data_cart.time = Ts*[0:length(data_cart.Rz)-1]; + +% Fixed cartesian frame +data_cart_fixed = load("2023-08-18_19-08_m0_1rpm_K_cart_fixed.mat"); +data_cart_fixed.time = Ts*[0:length(data_cart_fixed.Rz)-1]; + +% Fixed cartesian frame with Complementary Filters +data_cf = load("2023-08-21_14-28_m0_1rpm_K_cf.mat"); +data_cf.time = Ts*[0:length(data_cf.Rz)-1]; +#+end_src + +#+begin_src matlab +1e9*rms(data.Dx_int(data.time<45)) +1e9*rms(data_cart.Dx_int(data_cart.time<45)) +1e9*rms(data_cart_fixed.Dx_int(data_cart_fixed.time<45)) +1e9*rms(data_cf.Dx_int(data_cf.time<45)) +#+end_src + +#+begin_src matlab +1e9*rms(data.Dy_int(data.time<45)) +1e9*rms(data_cart.Dy_int(data_cart.time<45)) +1e9*rms(data_cart_fixed.Dy_int(data_cart_fixed.time<45)) +1e9*rms(data_cf.Dy_int(data_cf.time<45)) +#+end_src + +#+begin_src matlab +1e9*rms(data.Dz_int(data.time<45)) +1e9*rms(data_cart.Dz_int(data_cart.time<45)) +1e9*rms(data_cart_fixed.Dz_int(data_cart_fixed.time<45)) +1e9*rms(data_cf.Dz_int(data_cf.time<45)) +#+end_src + +#+begin_src matlab +1e9*rms(data.Rx_int(data.time<45)) +1e9*rms(data_cart.Rx_int(data_cart.time<45)) +1e9*rms(data_cart_fixed.Rx_int(data_cart_fixed.time<45)) +1e9*rms(data_cf.Rx_int(data_cf.time<45)) +#+end_src + +#+begin_src matlab +1e9*rms(data.Ry_int(data.time<45)) +1e9*rms(data_cart.Ry_int(data_cart.time<45)) +1e9*rms(data_cart_fixed.Ry_int(data_cart_fixed.time<45)) +1e9*rms(data_cf.Ry_int(data_cf.time<45)) +#+end_src + +#+begin_src matlab +figure; + +hold on; +plot(data.time, data.Dy_int) +plot(data_cart.time, data_cart.Dy_int) +plot(data_cart_fixed.time, data_cart_fixed.Dy_int) +plot(data_cf.time, data_cf.Dy_int) +hold off; +#+end_src + +#+begin_src matlab :exports none +% Hannning Windows +Nfft = floor(10.0/Ts); +win = hanning(Nfft); +Noverlap = floor(Nfft/2); + +[data.pxx_Dx, data.f] = pwelch(detrend(data.Dx_int(data.time<45), 0), win, Noverlap, Nfft, 1/Ts); +[data.pxx_Dy, ~ ] = pwelch(detrend(data.Dy_int(data.time<45), 0), win, Noverlap, Nfft, 1/Ts); +[data.pxx_Dz, ~ ] = pwelch(detrend(data.Dz_int(data.time<45), 0), win, Noverlap, Nfft, 1/Ts); +[data.pxx_Rx, ~ ] = pwelch(detrend(data.Rx_int(data.time<45), 0), win, Noverlap, Nfft, 1/Ts); +[data.pxx_Ry, ~ ] = pwelch(detrend(data.Ry_int(data.time<45), 0), win, Noverlap, Nfft, 1/Ts); + +[data_cart.pxx_Dx, data_cart.f] = pwelch(detrend(data_cart.Dx_int(data_cart.time<45), 0), win, Noverlap, Nfft, 1/Ts); +[data_cart.pxx_Dy, ~ ] = pwelch(detrend(data_cart.Dy_int(data_cart.time<45), 0), win, Noverlap, Nfft, 1/Ts); +[data_cart.pxx_Dz, ~ ] = pwelch(detrend(data_cart.Dz_int(data_cart.time<45), 0), win, Noverlap, Nfft, 1/Ts); +[data_cart.pxx_Rx, ~ ] = pwelch(detrend(data_cart.Rx_int(data_cart.time<45), 0), win, Noverlap, Nfft, 1/Ts); +[data_cart.pxx_Ry, ~ ] = pwelch(detrend(data_cart.Ry_int(data_cart.time<45), 0), win, Noverlap, Nfft, 1/Ts); + +[data_cart_fixed.pxx_Dx, data_cart_fixed.f] = pwelch(detrend(data_cart_fixed.Dx_int(data_cart_fixed.time<45), 0), win, Noverlap, Nfft, 1/Ts); +[data_cart_fixed.pxx_Dy, ~ ] = pwelch(detrend(data_cart_fixed.Dy_int(data_cart_fixed.time<45), 0), win, Noverlap, Nfft, 1/Ts); +[data_cart_fixed.pxx_Dz, ~ ] = pwelch(detrend(data_cart_fixed.Dz_int(data_cart_fixed.time<45), 0), win, Noverlap, Nfft, 1/Ts); +[data_cart_fixed.pxx_Rx, ~ ] = pwelch(detrend(data_cart_fixed.Rx_int(data_cart_fixed.time<45), 0), win, Noverlap, Nfft, 1/Ts); +[data_cart_fixed.pxx_Ry, ~ ] = pwelch(detrend(data_cart_fixed.Ry_int(data_cart_fixed.time<45), 0), win, Noverlap, Nfft, 1/Ts); + +[data_cf.pxx_Dx, data_cf.f] = pwelch(detrend(data_cf.Dx_int(data_cf.time<45), 0), win, Noverlap, Nfft, 1/Ts); +[data_cf.pxx_Dy, ~ ] = pwelch(detrend(data_cf.Dy_int(data_cf.time<45), 0), win, Noverlap, Nfft, 1/Ts); +[data_cf.pxx_Dz, ~ ] = pwelch(detrend(data_cf.Dz_int(data_cf.time<45), 0), win, Noverlap, Nfft, 1/Ts); +[data_cf.pxx_Rx, ~ ] = pwelch(detrend(data_cf.Rx_int(data_cf.time<45), 0), win, Noverlap, Nfft, 1/Ts); +[data_cf.pxx_Ry, ~ ] = pwelch(detrend(data_cf.Ry_int(data_cf.time<45), 0), win, Noverlap, Nfft, 1/Ts); +#+end_src + +#+begin_src matlab +figure; + +hold on; +plot(data.f, data.pxx_Dy) +plot(data_cart.f, data_cart.pxx_Dy) +plot(data_cart_fixed.f, data_cart_fixed.pxx_Dy) +plot(data_cf.f, data_cf.pxx_Dy) +hold off; +set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); +xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]); +% legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1); +xlim([0.1, 5e2]); +#+end_src + +#+begin_src matlab +figure; + +hold on; +plot(data.f, sqrt(flip(-cumtrapz(flip(data.f), flip(data.pxx_Dy))))) +plot(data_cart.f, sqrt(flip(-cumtrapz(flip(data_cart.f), flip(data_cart.pxx_Dy))))) +plot(data_cart_fixed.f, sqrt(flip(-cumtrapz(flip(data_cart_fixed.f), flip(data_cart_fixed.pxx_Dy))))) +plot(data_cf.f, sqrt(flip(-cumtrapz(flip(data_cf.f), flip(data_cf.pxx_Dy))))) +hold off; +set(gca, 'XScale', 'log'); set(gca, 'YScale', 'log'); +xlabel('Frequency [Hz]'); set(gca, 'YTickLabel',[]); +% legend('location', 'southwest', 'FontSize', 8, 'NumColumns', 1); +xlim([0.1, 5e2]); +#+end_src + +*** TODO Medium velocity tomography scans with CF control :noexport: +#+begin_src matlab +data_m1_cf = load("2023-08-21_19-18_m1_6rpm_cf_control.mat"); +data_m1_cf.time = Ts*[0:length(data_m1_cf.Rz)-1]; +#+end_src + +#+begin_src matlab +data_m2_cf = load("2023-08-21_18-07_m2_6rpm_cf_control.mat"); +data_m2_cf.time = Ts*[0:length(data_m2_cf.Rz)-1]; +#+end_src + +And higher bandwidth: +#+begin_src matlab +data_m1_cf_high_fb = load("2023-08-21_19-24_m1_6rpm_cf_control_60Hz.mat"); +data_m1_cf_high_fb.time = Ts*[0:length(data_m1_cf_high_fb.Rz)-1]; +#+end_src + +#+begin_src matlab +figure; +hold on; +plot(data_m1_cf.Dy_int, detrend(data_m1_cf.Dz_int, 0), 'DisplayName', 'm1') +plot(data_m2_cf.Dy_int, detrend(data_m2_cf.Dz_int, 0), 'DisplayName', 'm2') +plot(data_m1_cf_high_fb.Dy_int, detrend(data_m1_cf_high_fb.Dz_int, 0), 'DisplayName', 'm1, high BW') +axis equal +legend +#+end_src + +#+begin_src matlab +1e9*rms(detrend(data_m1.Dz_int(i_m1+50000:end), 0)) +1e9*rms(detrend(data_m1.Dy_int(i_m1+50000:end), 0)) +1e9*rms(detrend(data_m1.Ry_int(i_m1+50000:end), 0)) +#+end_src + +#+begin_src matlab +1e9*rms(detrend(data_m1_cf.Dz_int, 0)) +1e9*rms(detrend(data_m1_cf.Dy_int, 0)) +1e9*rms(detrend(data_m1_cf.Ry_int, 0)) +#+end_src + +#+begin_src matlab +1e9*rms(detrend(data_m2.Dz_int, 0)) +1e9*rms(detrend(data_m2.Dy_int, 0)) +1e9*rms(detrend(data_m2.Ry_int, 0)) +#+end_src + +#+begin_src matlab +1e9*rms(detrend(data_m1_high_fb.Dz_int, 0)) +1e9*rms(detrend(data_m1_high_fb.Dy_int, 0)) +1e9*rms(detrend(data_m1_high_fb.Ry_int, 0)) +#+end_src + +*** TODO Fast Tomography Scan with Complementary Filter Controller :noexport: +#+begin_src matlab +data_cf = load("2023-08-21_14-33_m0_30rpm_cf_control.mat"); +data_cf.time = Ts*[0:length(data_cf.Rz)-1]; +#+end_src + +#+begin_src matlab +[~, i0] = find(data.hac_status == 1); +1e9*rms(data.Dy_int(i0(1)+5000:end)) +1e9*rms(data.Dz_int(i0(1)+5000:end)) + +1e9*rms(data_cf.Dy_int) +1e9*rms(data_cf.Dz_int) +#+end_src + +Same performance than the robust controller in terms of RMS residual motion. + +#+begin_src matlab +figure; plot3(data.Dx_int, data.Dy_int, data.Dz_int) +#+end_src + +*** Tomography - Effect of the scanning velocity :noexport: +- [ ] What are the controller used here? Why worst results than with the robust controller? + +#+begin_src matlab +data_1rpm = load("2023-08-18_10-43_m0_1rpm.mat"); +data_1rpm.time = Ts*[0:length(data_1rpm.Rz)-1]; +#+end_src + +#+begin_src matlab +data_30rpm = load("2023-08-18_10-45_m0_30rpm.mat"); +data_30rpm.time = Ts*[0:length(data_30rpm.Rz)-1]; +#+end_src + +#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*) +data2orgtable([1e9*rms(detrend(data_1rpm.Dy_int, 0)), 1e9*rms(detrend(data_30rpm.Dy_int, 0)); 1e9*rms(detrend(data_1rpm.Dz_int, 0)), 1e9*rms(detrend(data_30rpm.Dz_int, 0)); 1e9*rms(detrend(data_1rpm.Ry_int, 0)), 1e9*rms(detrend(data_30rpm.Ry_int, 0))]', {'1RPM', '30RPM'}, {'$D_y$', '$D_z$', '$R_y$'}, ' %.1f '); +#+end_src + +#+name: tab:id31_tomography_effect_velocity_rms +#+caption: RMS values of the errors during tomography scan - Effect of $R_z$ velocity +#+attr_latex: :environment tabularx :width 0.5\linewidth :align lXXX +#+attr_latex: :center t :booktabs t +#+RESULTS: +| | $D_y$ | $D_z$ | $R_y$ | +|-------+-------+-------+-------| +| 1RPM | 30.9 | 5.9 | 92.4 | +| 30RPM | 71.7 | 12.5 | 301.3 | +#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*) +data2orgtable([1e9*data_tomo_1rpm_m0.Dx_rms_cl, 1e9*data_tomo_1rpm_m0.Dy_rms_cl, 1e9*data_tomo_1rpm_m0.Dz_rms_cl, 1e9*data_tomo_1rpm_m0.Rx_rms_cl, 1e9*data_tomo_1rpm_m0.Ry_rms_cl; ... + 1e9*data_tomo_6rpm_m0.Dx_rms_cl, 1e9*data_tomo_6rpm_m0.Dy_rms_cl, 1e9*data_tomo_6rpm_m0.Dz_rms_cl, 1e9*data_tomo_6rpm_m0.Rx_rms_cl, 1e9*data_tomo_6rpm_m0.Ry_rms_cl; ... + 1e9*data_tomo_30rpm_m0.Dx_rms_cl, 1e9*data_tomo_30rpm_m0.Dy_rms_cl, 1e9*data_tomo_30rpm_m0.Dz_rms_cl, 1e9*data_tomo_30rpm_m0.Rx_rms_cl, 1e9*data_tomo_30rpm_m0.Ry_rms_cl], ... + {'1rpm', '6rpm', '30rpm'}, {'$D_x$ [$\mu m$]', '$D_y$ [$\mu m$]', '$D_z$ [$nm$]', '$R_x$ [$\mu\text{rad}$]', '$R_y$ [$\mu\text{rad}$]'}, ' %.0f '); +#+end_src + +#+RESULTS: +| | $D_x$ [$\mu m$] | $D_y$ [$\mu m$] | $D_z$ [$nm$] | $R_x$ [$\mu\text{rad}$] | $R_y$ [$\mu\text{rad}$] | +|-------+-----------------+-----------------+--------------+-------------------------+-------------------------| +| 1rpm | 13 | 15 | 5 | 57 | 55 | +| 6rpm | 17 | 19 | 5 | 70 | 73 | +| 30rpm | 34 | 38 | 10 | 127 | 129 | + +** $D_z$ scans: Dirty Layer Scans +<> +*** Step by Step $D_z$ motion +#+begin_src matlab +%% Load Dz MIM data +data_dz_steps_3nm = load("2023-08-18_14-57_dz_mim_3_nm.mat"); +data_dz_steps_3nm.time = Ts*[0:length(data_dz_steps_3nm.Dz_int)-1]; + +data_dz_steps_10nm = load("2023-08-18_14-57_dz_mim_10_nm.mat"); +data_dz_steps_10nm.time = Ts*[0:length(data_dz_steps_10nm.Dz_int)-1]; + +data_dz_steps_100nm = load("2023-08-18_14-57_dz_mim_100_nm.mat"); +data_dz_steps_100nm.time = Ts*[0:length(data_dz_steps_100nm.Dz_int)-1]; + +data_dz_steps_1000nm = load("2023-08-18_14-57_dz_mim_1000_nm.mat"); +data_dz_steps_1000nm.time = Ts*[0:length(data_dz_steps_1000nm.Dz_int)-1]; +#+end_src + +Three step sizes are tested: +- $10\,nm$ steps (Figure ref:fig:id31_dz_mim_10nm_steps) +- $100\,nm$ steps (Figure ref:fig:id31_dz_mim_100nm_steps) +- $1\,\mu m$ steps (Figure ref:fig:id31_dz_steps_response) + +#+begin_src matlab :exports none :results none +%% Dz MIM test with 10nm steps +figure; +hold on; +plot(data_dz_steps_10nm.time, 1e9*(data_dz_steps_10nm.Dz_int - mean(data_dz_steps_10nm.Dz_int(1:1000)))) +hold off; +xlabel('Time [s]'); +ylabel('$D_z$ Motion [nm]'); +#+end_src + +#+begin_src matlab :tangle no :exports results :results file replace +exportFig('figs/id31_dz_mim_10nm_steps.pdf', 'width', 'wide', 'height', 'normal'); +#+end_src + +#+name: fig:id31_dz_mim_10nm_steps +#+caption: Dz MIM test with 10nm steps (low pass filter with cut-off frequency of 10Hz is applied) +#+RESULTS: +[[file:figs/id31_dz_mim_10nm_steps.png]] + +#+begin_src matlab :exports none :results none +%% Dz MIM test with 10nm steps +figure; +hold on; +plot(data_dz_steps_100nm.time, 1e9*(data_dz_steps_100nm.Dz_int - mean(data_dz_steps_100nm.Dz_int(1:1000)))) +hold off; +xlabel('Time [s]'); +ylabel('$D_z$ Motion [nm]'); +#+end_src + +#+begin_src matlab :tangle no :exports results :results file replace +exportFig('figs/id31_dz_mim_100nm_steps.pdf', 'width', 'wide', 'height', 'normal'); +#+end_src + +#+name: fig:id31_dz_mim_100nm_steps +#+caption: Dz MIM test with 100nm steps +#+RESULTS: +[[file:figs/id31_dz_mim_100nm_steps.png]] + +#+begin_src matlab :exports none :results none +%% Dz step response - Stabilization time is around 70ms +figure; +[~, i] = find(data_dz_steps_1000nm.m_hexa_dz>data_dz_steps_1000nm.m_hexa_dz(1)); +i0 = i(1); + +figure; +hold on; +plot(data_dz_steps_1000nm.time-data_dz_steps_1000nm.time(i0), 1e9*(data_dz_steps_1000nm.Dz_int - mean(data_dz_steps_1000nm.Dz_int(1:1000)))) +plot([-1, 1], [1000-20, 1000-20], 'k--') +plot([-1, 1], [1000+20, 1000+20], 'k--') +xline(0, 'k--', 'LineWidth', 1.5) +xline(0.07, 'k--', 'LineWidth', 1.5) +hold off; +xlabel('Time [s]'); +ylabel('$D_z$ Motion [nm]'); +xlim([-0.1, 0.2]); +ylim([-100, 1600]) +#+end_src + +#+begin_src matlab :tangle no :exports results :results file replace +exportFig('figs/id31_dz_steps_response.pdf', 'width', 'wide', 'height', 'normal'); +#+end_src + +#+name: fig:id31_dz_steps_response +#+caption: $D_z$ step response - Stabilization time is around 70ms +#+RESULTS: +[[file:figs/id31_dz_steps_response.png]] + + +*** Continuous $D_z$ motion: Dirty Layer Scans +#+begin_src matlab +data_dz_10ums = load("2023-08-18_15-33_dirty_layer_m0_small.mat"); +data_dz_10ums.time = Ts*[0:length(data_dz_10ums.Dz_int)-1]; +#+end_src + +#+begin_src matlab +data_dz_100ums = load("2023-08-18_15-32_dirty_layer_m0.mat"); +data_dz_100ums.time = Ts*[0:length(data_dz_100ums.Dz_int)-1]; +#+end_src + +Two $D_z$ scans are performed: +- at $10\,\mu m/s$ in Figure ref:fig:id31_dirty_layer_scan_m0 +- at $100\,\mu m/s$ in Figure ref:fig:id31_dirty_layer_scan_m0_large + +#+begin_src matlab :exports none :results none +%% Dirty layer scan: Dz motion +figure; +hold on; +plot(data_dz_10ums.time, 1e6*data_dz_10ums.Dz_int, ... + 'DisplayName', sprintf('$\\epsilon D_z = %.0f$ nm RMS', 1e9*rms(data_dz_10ums.e_dz))) +plot(data_dz_10ums.time, 1e6*data_dz_10ums.e_dy, ... + 'DisplayName', sprintf('$\\epsilon D_y = %.0f$ nm RMS', 1e9*rms(data_dz_10ums.e_dy))) +plot(data_dz_10ums.time, 1e6*data_dz_10ums.e_ry, ... + 'DisplayName', sprintf('$\\epsilon R_y = %.2f$ $\\mu$rad RMS', 1e6*rms(data_dz_10ums.e_ry))) +hold off; +xlabel('Time [s]'); +ylabel('Motion [$\mu$m]'); +xlim([0, 2.2]) +legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1); +#+end_src + +#+begin_src matlab :tangle no :exports results :results file replace +exportFig('figs/id31_dirty_layer_scan_m0.pdf', 'width', 'wide', 'height', 'normal'); +#+end_src + +#+name: fig:id31_dirty_layer_scan_m0 +#+caption: Dirty layer scan: $D_z$ motion at $10\,\mu m/s$ +#+RESULTS: +[[file:figs/id31_dirty_layer_scan_m0.png]] + +#+begin_src matlab :exports none :results none +%% Dirty layer scan: Dz motion +figure; +hold on; +plot(data_dz_100ums.time, 1e6*data_dz_100ums.Dz_int, ... + 'DisplayName', sprintf('$\\epsilon D_z = %.0f$ nm RMS', 1e9*rms(data_dz_100ums.e_dz))) +plot(data_dz_100ums.time, 1e6*data_dz_100ums.e_dy, ... + 'DisplayName', sprintf('$\\epsilon D_y = %.0f$ nm RMS', 1e9*rms(data_dz_100ums.e_dy))) +plot(data_dz_100ums.time, 1e6*data_dz_100ums.e_ry, ... + 'DisplayName', sprintf('$\\epsilon R_y = %.2f$ $\\mu$rad RMS', 1e6*rms(data_dz_100ums.e_ry))) +hold off; +xlabel('Time [s]'); +ylabel('Motion [$\mu$m]'); +xlim([0, 2.2]) +legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1); +#+end_src + +#+begin_src matlab :tangle no :exports results :results file replace +exportFig('figs/id31_dirty_layer_scan_m0_large.pdf', 'width', 'wide', 'height', 'normal'); +#+end_src + +#+name: fig:id31_dirty_layer_scan_m0_large +#+caption: Dirty layer scan: $D_z$ motion at $100\,\mu m/s$ +#+RESULTS: +[[file:figs/id31_dirty_layer_scan_m0_large.png]] + +#+begin_src matlab +%% Not so good results with the CF controller +data_cf = load(sprintf("%s/scans/2023-08-21_19-20_dirty_layer_m1_cf.mat", mat_dir)); +data_cf.time = Ts*[0:length(data_cf.Dz_int)-1]; +#+end_src + +** $R_y$ scans: Reflectivity +<> +#+begin_src matlab +%% Load data for the reflectivity scan +data_ry = load("2023-08-18_15-24_first_reflectivity_m0.mat"); +data_ry.time = Ts*[0:length(data_ry.Ry_int)-1]; +#+end_src + +An $R_y$ scan is performed at $100\,\mu rad/s$ velocity (Figure ref:fig:id31_reflectivity_scan_Ry_100urads). +During the $R_y$ scan, the errors in $D_y$ are $D_z$ are kept small. + +#+begin_src matlab :exports none :results none +%% Ry reflectivity scan +figure; +hold on; +plot(data_ry.time, 1e6*data_ry.Ry_int, 'DisplayName', sprintf('$\\epsilon R_y = %.2f$ $\\mu$rad RMS', 1e6*rms(data_ry.e_ry))) +plot(data_ry.time, 1e6*data_ry.e_dy, 'DisplayName', sprintf('$\\epsilon D_y = %.0f$ nm RMS', 1e9*rms(data_ry.e_dy))) +plot(data_ry.time, 1e6*data_ry.e_dz, 'DisplayName', sprintf('$\\epsilon D_z = %.0f$ nm RMS', 1e9*rms(data_ry.e_dz))) +hold off; +xlabel('Time [s]'); +ylabel('$R_y$ motion [$\mu$rad]') +legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1); +xlim([0, 6.2]); +ylim([-310, 310]) +#+end_src + +#+begin_src matlab :tangle no :exports results :results file replace +exportFig('figs/id31_reflectivity_scan_Ry_100urads.pdf', 'width', 'wide', 'height', 'normal'); +#+end_src + +#+name: fig:id31_reflectivity_scan_Ry_100urads +#+caption: $R_y$ reflecitivity scan at $100\,\mu\text{rad}/s$ velocity +#+RESULTS: +[[file:figs/id31_reflectivity_scan_Ry_100urads.png]] + + +** $D_y$ Scans +<> +*** Introduction :ignore: +The steps generated by the IcePAP for the $T_y$ stage are send to the Speedgoat. +Then, we can know in real time what is the wanted position in $D_y$ during $T_y$ scans. + +*** Open Loop +#+begin_src matlab +%% Slow Ty scan (10um/s) +data_ty_ol_slow = load("2023-08-21_20-05_ty_scan_m1_open_loop_slow.mat"); +data_ty_ol_slow.time = Ts*[0:length(data_ty_ol_slow.Dy_int)-1]; +#+end_src + +We can clearly see micro-stepping errors of the stepper motor used for the $T_y$ stage. +The errors have a period of $10\,\mu m$ with an amplitude of $\pm 100\,nm$. + +#+begin_src matlab :exports none :results none +%% Ty scan (at 10um/s) - Dy errors +figure; +plot(1e6*data_ty_ol_slow.Ty, 1e6*detrend(data_ty_ol_slow.e_dy, 0)) +xlabel('Ty position [$\mu$m]'); +ylabel('$D_y$ error [$\mu$m]'); +xlim([-100, 100]) +#+end_src + +#+begin_src matlab :tangle no :exports results :results file replace +exportFig('figs/id31_ty_scan_10ums_ol_dy_errors.pdf', 'width', 'wide', 'height', 'normal'); +#+end_src + +#+name: fig:id31_ty_scan_10ums_ol_dy_errors +#+caption: $T_y$ scan (at $10\,\mu m/s$) - $D_y$ errors. The micro-stepping errors can clearly be seen with a period of $10\,\mu m$ and an amplitude of $\pm 100\,nm$ +#+RESULTS: +[[file:figs/id31_ty_scan_10ums_ol_dy_errors.png]] + +#+begin_src matlab :exports none :results none +%% Ty scan (at 10um/s) - Dz and Ry errors +figure; +tiledlayout(1, 2, 'TileSpacing', 'compact', 'Padding', 'None'); + +ax1 = nexttile; +hold on; +plot(1e6*data_ty_ol_slow.Ty, 1e6*detrend(data_ty_ol_slow.e_dz, 0)) +hold off; +xlabel('Ty position [$\mu$m]'); +ylabel('$D_z$ error [$\mu$m]'); +xlim([-100, 100]) + +ax2 = nexttile; +hold on; +plot(1e6*data_ty_ol_slow.Ty, 1e6*data_ty_ol_slow.e_ry) +hold off; +xlabel('Ty position [$\mu$m]'); +ylabel('$R_y$ error [$\mu$rad]'); +xlim([-100, 100]) +#+end_src + +#+begin_src matlab :tangle no :exports results :results file replace +exportFig('figs/id31_ty_scan_10ums_ol_dz_ry_errors.pdf', 'width', 'full', 'height', 'normal'); +#+end_src + +#+name: fig:id31_ty_scan_10ums_ol_dz_ry_errors +#+caption: $T_y$ scan (at $10\,\mu m/s$) - $D_z$ and $R_y$ errors. The $D_z$ error is most likely due to having the top interferometer pointing to a sphere. The large $R_y$ errors might also be due to the metrology system +#+RESULTS: +[[file:figs/id31_ty_scan_10ums_ol_dz_ry_errors.png]] + +*** Closed Loop +#+begin_src matlab +%% Slow Ty scan (10um/s) - CL +data_ty_cl_slow = load("2023-08-21_20-07_ty_scan_m1_cf_closed_loop_slow.mat"); +data_ty_cl_slow.time = Ts*[0:length(data_ty_cl_slow.Dy_int)-1]; +#+end_src + +#+begin_src matlab :exports none :results none +%% Ty scan (at 10um/s) - Dy errors +figure; +hold on; +plot(1e6*data_ty_ol_slow.Ty, 1e6*detrend(data_ty_ol_slow.e_dy, 0), ... + 'DisplayName', 'OL') +plot(1e6*data_ty_cl_slow.Ty, 1e6*detrend(data_ty_cl_slow.e_dy, 0), ... + 'DisplayName', sprintf('CL - $\\epsilon D_y = %.0f$ nmRMS', 1e9*rms(detrend(data_ty_cl_slow.e_dy, 0)))) +hold off; +xlabel('Ty position [$\mu$m]'); +ylabel('$D_y$ error [$\mu$m]'); +xlim([-100, 100]) +legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1); +#+end_src + +#+begin_src matlab :tangle no :exports results :results file replace +exportFig('figs/id31_ty_scan_10ums_cl_dy_errors.pdf', 'width', 'wide', 'height', 'normal'); +#+end_src + +#+name: fig:id31_ty_scan_10ums_cl_dy_errors +#+caption: $T_y$ scan (at $10\,\mu m/s$) - $D_y$ errors. Open-loop and Closed-loop scans +#+RESULTS: +[[file:figs/id31_ty_scan_10ums_cl_dy_errors.png]] + +#+begin_src matlab :exports none :results none +%% Ty scan (at 10um/s) - Dz and Ry errors +figure; +tiledlayout(1, 2, 'TileSpacing', 'compact', 'Padding', 'None'); + +ax1 = nexttile; +hold on; +plot(1e6*data_ty_ol_slow.Ty, 1e6*detrend(data_ty_ol_slow.e_dz, 0), ... + 'DisplayName', 'OL') +plot(1e6*data_ty_cl_slow.Ty, 1e6*detrend(data_ty_cl_slow.e_dz, 0), ... + 'DisplayName', sprintf('Cl - $\\epsilon D_z = %.0f$ nmRMS', 1e9*rms(detrend(data_ty_cl_slow.e_dz, 0)))) +hold off; +xlabel('Ty position [$\mu$m]'); +ylabel('$D_z$ error [$\mu$m]'); +xlim([-100, 100]) +legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1); + +ax2 = nexttile; +hold on; +plot(1e6*data_ty_ol_slow.Ty, 1e6*data_ty_ol_slow.e_ry, ... + 'DisplayName', 'OL') +plot(1e6*data_ty_cl_slow.Ty, 1e6*data_ty_cl_slow.e_ry, ... + 'DisplayName', sprintf('Cl - $\\epsilon R_y = %.2f$ uradRMS', 1e6*rms(detrend(data_ty_cl_slow.e_ry, 0)))) +hold off; +xlabel('Ty position [$\mu$m]'); +ylabel('$R_y$ error [$\mu$rad]'); +xlim([-100, 100]) +legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1); +#+end_src + +#+begin_src matlab :tangle no :exports results :results file replace +exportFig('figs/id31_ty_scan_10ums_cl_dz_ry_errors.pdf', 'width', 'full', 'height', 'normal'); +#+end_src + +#+name: fig:id31_ty_scan_10ums_cl_dz_ry_errors +#+caption: $T_y$ scan (at $10\,\mu m/s$) - $D_z$ and $R_y$ errors. Open-loop and Closed-loop scans +#+RESULTS: +[[file:figs/id31_ty_scan_10ums_cl_dz_ry_errors.png]] + +*** Faster Scan +#+begin_src matlab +%% Fast Ty scan (100um/s) - OL +data_ty_ol_fast = load("2023-08-21_20-05_ty_scan_m1_open_loop.mat"); +data_ty_ol_fast.time = Ts*[0:length(data_ty_ol_fast.Dy_int)-1]; +#+end_src + +#+begin_src matlab +%% Fast Ty scan (10um/s) - CL +data_ty_cl_fast = load("2023-08-21_20-07_ty_scan_m1_cf_closed_loop.mat"); +data_ty_cl_fast.time = Ts*[0:length(data_ty_cl_fast.Dy_int)-1]; +#+end_src + +Because of micro-stepping errors of the Ty stepper motor, when scanning at high velocity this induce high frequency vibration that are outside the bandwidth of the feedback controller. + +At $100\,\mu m/s$, the micro-stepping errors with a period of $10\,\mu m$ (see Figure ref:fig:id31_ty_scan_10ums_ol_dy_errors) are at 10Hz. +These errors are them amplified by some resonances in the system. + +This could be easily solved by changing the stepper motor for a torque motor for instance. + +#+begin_src matlab :exports none :results none +%% Ty scan (at 100um/s) - Dy errors +figure; +hold on; +plot(1e6*data_ty_ol_fast.Ty, 1e6*detrend(data_ty_ol_fast.e_dy, 0), ... + 'DisplayName', 'OL') +plot(1e6*data_ty_cl_fast.Ty, 1e6*detrend(data_ty_cl_fast.e_dy, 0), ... + 'DisplayName', sprintf('CL - $\\epsilon D_y = %.0f$ nmRMS', 1e9*rms(detrend(data_ty_cl_fast.e_dy, 0)))) +hold off; +xlabel('Ty position [$\mu$m]'); +ylabel('$D_y$ error [$\mu$m]'); +xlim([-100, 100]) +legend('location', 'northeast', 'FontSize', 8, 'NumColumns', 1); +#+end_src + +#+begin_src matlab :tangle no :exports results :results file replace +exportFig('figs/id31_ty_scan_100ums_cl_dy_errors.pdf', 'width', 'wide', 'height', 'normal'); +#+end_src + +#+name: fig:id31_ty_scan_100ums_cl_dy_errors +#+caption: $T_y$ scan (at $100\,\mu m/s$) - $D_y$ errors. Open-loop and Closed-loop scans +#+RESULTS: +[[file:figs/id31_ty_scan_100ums_cl_dy_errors.png]] + +#+begin_src matlab :exports none :results none +%% Ty scan (at 100um/s) - Dz and Ry errors +figure; +tiledlayout(1, 2, 'TileSpacing', 'compact', 'Padding', 'None'); + +ax1 = nexttile; +hold on; +plot(1e6*data_ty_ol_fast.Ty, 1e6*detrend(data_ty_ol_fast.e_dz, 0), ... + 'DisplayName', 'OL') +plot(1e6*data_ty_cl_fast.Ty, 1e6*detrend(data_ty_cl_fast.e_dz, 0), ... + 'DisplayName', sprintf('Cl - $\\epsilon D_z = %.0f$ nmRMS', 1e9*rms(detrend(data_ty_cl_fast.e_dz, 0)))) +hold off; +xlabel('Ty position [$\mu$m]'); +ylabel('$D_z$ error [$\mu$m]'); +xlim([-100, 100]) +legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1); + +ax2 = nexttile; +hold on; +plot(1e6*data_ty_ol_fast.Ty, 1e6*data_ty_ol_fast.e_ry, ... + 'DisplayName', 'OL') +plot(1e6*data_ty_cl_fast.Ty, 1e6*data_ty_cl_fast.e_ry, ... + 'DisplayName', sprintf('Cl - $\\epsilon R_y = %.2f$ uradRMS', 1e6*rms(detrend(data_ty_cl_fast.e_ry, 0)))) +hold off; +xlabel('Ty position [$\mu$m]'); +ylabel('$R_y$ error [$\mu$rad]'); +xlim([-100, 100]) +legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1); +#+end_src + +#+begin_src matlab :tangle no :exports results :results file replace +exportFig('figs/id31_ty_scan_100ums_cl_dz_ry_errors.pdf', 'width', 'full', 'height', 'normal'); +#+end_src + +#+name: fig:id31_ty_scan_100ums_cl_dz_ry_errors +#+caption: $T_y$ scan (at $100\,\mu m/s$) - $D_z$ and $R_y$ errors. Open-loop and Closed-loop scans +#+RESULTS: +[[file:figs/id31_ty_scan_100ums_cl_dz_ry_errors.png]] + +** Combined $R_z$ and $D_y$: Diffraction Tomography +<> +Instead of doing a fast $R_z$ motion a slow $D_y$, the idea is to perform slow $R_z$ (here 1rpm) and fast $D_y$ scans with the nano-hexapod. + +#+begin_src matlab +%% 100um/s - Robust controller +data_dt_100ums = load("2023-08-18_17-12_diffraction_tomo_m0.mat"); +data_dt_100ums.time = Ts*[0:length(data_dt_100ums.Dy_int)-1]; + +%% 500um/s - Robust controller (Not used) +% data_dt_500ums = load(sprintf("%s/scans/2023-08-18_17-19_diffraction_tomo_m0_fast.mat", mat_dir)); +% data_dt_500ums.time = Ts*[0:length(data_dt_500ums.Dy_int)-1]; + +%% 500um/s - Complementary filters +data_dt_500ums = load("2023-08-21_15-15_diffraction_tomo_m0_fast_cf.mat"); +data_dt_500ums.time = Ts*[0:length(data_dt_500ums.Dy_int)-1]; + +%% 1mm/s - Complementary filters +data_dt_1000ums = load("2023-08-21_15-16_diffraction_tomo_m0_fast_cf.mat"); +data_dt_1000ums.time = Ts*[0:length(data_dt_1000ums.Dy_int)-1]; + +%% 5mm/s - Complementary filters +% data_dt_5000ums = load(sprintf("%s/scans/2023-08-21_18-03_diffraction_tomo_m2_fast_cf.mat", mat_dir)); +% data_dt_5000ums.time = Ts*[0:length(data_dt_5000ums.Dy_int)-1]; + +%% 10mm/s - Complementary filters +data_dt_10000ums = load("2023-08-21_15-17_diffraction_tomo_m0_fast_cf.mat"); +data_dt_10000ums.time = Ts*[0:length(data_dt_10000ums.Dy_int)-1]; +#+end_src + +Here, the $D_y$ scans are performed only with the nano-hexapod (the Ty stage is not moving), so we are limited to $\pm 100\,\mu m$. + +Several $D_y$ velocities are tested: $0.1\,mm/s$, $0.5\,mm/s$, $1\,mm/s$ and $10\,mm/s$ (see Figure ref:fig:id31_diffraction_tomo_velocities). + +#+begin_src matlab :exports none :results none +%% Dy motion for several configured velocities +figure; +hold on; +plot(data_dt_10000ums.time, 1e6*data_dt_10000ums.Dy_int, ... + 'DisplayName', '$10 mm/s$') +plot(data_dt_1000ums.time, 1e6*data_dt_1000ums.Dy_int, ... + 'DisplayName', '$1 mm/s$') +plot(data_dt_500ums.time, 1e6*data_dt_500ums.Dy_int, ... + 'DisplayName', '$0.5 mm/s$') +plot(data_dt_100ums.time, 1e6*data_dt_100ums.Dy_int, ... + 'DisplayName', '$0.1 mm/s$') +hold off; +xlim([0, 4]); +xlabel('Time [s]'); +ylabel('$D_y$ position [$\mu$m]') +legend('location', 'southeast', 'FontSize', 8, 'NumColumns', 1); +#+end_src + +#+begin_src matlab :tangle no :exports results :results file replace +exportFig('figs/id31_diffraction_tomo_velocities.pdf', 'width', 'wide', 'height', 'normal'); +#+end_src + +#+name: fig:id31_diffraction_tomo_velocities +#+caption: Dy motion for several configured velocities +#+RESULTS: +[[file:figs/id31_diffraction_tomo_velocities.png]] + +The corresponding "repetition rate" and $D_y$ scan per spindle turn are shown in Table ref:tab:diffraction_tomo_velocities. + +The main issue here is the "waiting" time between two scans that is in the order of 50ms. +By removing this waiting time (fairly easily), we can double the repetition rate at 10mm/s. + +#+name: tab:diffraction_tomo_velocities +#+caption: $D_y$ scaning repetition rate +#+attr_latex: :environment tabularx :width 0.6\linewidth :align lXX +#+attr_latex: :center t :booktabs t +| $D_y$ Velocity | Repetition rate | Scans per turn (at 1RPM) | +|----------------+-----------------+--------------------------| +| 0.1 mm/s | 4 s | 15 | +| 0.5 mm/s | 0.9 s | 65 | +| 1 mm/s | 0.5 s | 120 | +| 10 mm/s | 0.18 s | 330 | + + +The scan results for a velocity of 1mm/s is shown in Figure ref:fig:id31_diffraction_tomo_1mms. +The $D_z$ and $R_y$ errors are quite small during the scan. + +The $D_y$ errors are quite large as the velocity is increased. +This type of scan can probably be massively improved by using feed-forward and optimizing the trajectory. +Also, if the detectors are triggered in position (the Speedgoat could generate an encoder signal for instance), we don't care about the $D_y$ errors. + +#+begin_src matlab :exports none :results none +%% Diffraction tomography with Dy velocity of 1mm/s and Rz velocity of 1RPM +figure; +hold on; +plot(data_dt_1000ums.time, 1e6*data_dt_1000ums.Dz_int, ... + 'DisplayName', sprintf('$\\epsilon D_z = %.0f$ nmRMS', 1e9*rms(data_dt_1000ums.Dz_int))) +plot(data_dt_1000ums.time, 1e6*data_dt_1000ums.Ry_int, ... + 'DisplayName', sprintf('$\\epsilon R_y = %.2f$ $\\mu$radRMS', 1e6*rms(data_dt_1000ums.Ry_int))) +plot(data_dt_1000ums.time, 1e6*data_dt_1000ums.Dy_int, ... + 'DisplayName', sprintf('$\\epsilon D_y = %.0f$ nmRMS', 1e9*rms(data_dt_1000ums.Dy_int - data_dt_1000ums.m_hexa_dy))) +plot(data_dt_1000ums.time, 1e6*data_dt_1000ums.m_hexa_dy, 'k--', 'HandleVisibility', 'off') +hold off; +xlim([0, 1]) +xlabel('Time [s]'); +ylabel('Measurement [nm,nrad]') +legend('location', 'northwest', 'FontSize', 8, 'NumColumns', 1); +ylim([-110, 110]) +#+end_src + +#+begin_src matlab :tangle no :exports results :results file replace +exportFig('figs/id31_diffraction_tomo_1mms.pdf', 'width', 'full', 'height', 'normal'); +#+end_src + +#+name: fig:id31_diffraction_tomo_1mms +#+caption: Diffraction tomography with Dy velocity of 1mm/s and Rz velocity of 1RPM +#+RESULTS: +[[file:figs/id31_diffraction_tomo_1mms.png]] + +#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*) +data2orgtable([1e9*rms(data_dt_100ums.Dy_int - data_dt_100ums.m_hexa_dy), 1e9*rms(data_dt_500ums.Dy_int - data_dt_500ums.m_hexa_dy), 1e9*rms(data_dt_1000ums.Dy_int - data_dt_1000ums.m_hexa_dy), 1e9*rms(data_dt_10000ums.Dy_int - data_dt_10000ums.m_hexa_dy); +1e9*rms(data_dt_100ums.Dz_int), 1e9*rms(data_dt_500ums.Dz_int), 1e9*rms(data_dt_1000ums.Dz_int), 1e9*rms(data_dt_10000ums.Dz_int); +1e6*rms(data_dt_100ums.Ry_int), 1e6*rms(data_dt_500ums.Ry_int), 1e6*rms(data_dt_1000ums.Ry_int), 1e6*rms(data_dt_10000ums.Ry_int)]', {'0.1 mm/s' ,'0.5 mm/s', '1 mm/s', '10 mm/s'}, {'Velocity', '$D_y$ [nmRMS]', '$D_z$ [nmRMS]', '$R_y$ [$\mu\text{radRMS}$]'}, ' %.1f '); +#+end_src + +#+name: tab:id31_diffraction_tomo_results +#+caption: Obtained errors for several $D_y$ velocities +#+attr_latex: :environment tabularx :width \linewidth :align lXX +#+attr_latex: :center t :booktabs t +#+RESULTS: +| Velocity | $D_y$ [nmRMS] | $D_z$ [nmRMS] | $R_y$ [$\mu\text{radRMS}$] | +|----------+---------------+---------------+----------------------------| +| 0.1 mm/s | 75.5 | 9.1 | 0.1 | +| 0.5 mm/s | 190.5 | 10.0 | 0.1 | +| 1 mm/s | 428.0 | 11.2 | 0.2 | +| 10 mm/s | 4639.9 | 55.9 | 1.4 | + +** Conclusion +:PROPERTIES: +:UNNUMBERED: t +:END: +<> + +For each conducted experiments, the $D_y$, $D_z$ and $R_y$ errors are computed and summarized in Table ref:tab:id31_experiments_results_summary. + +#+begin_src matlab +%% Summary of results +data_results = [... + 1e9*data_tomo_1rpm_m0.Dy_rms_cl, 1e9*data_tomo_1rpm_m0.Dz_rms_cl, 1e9*data_tomo_1rpm_m0.Ry_rms_cl ; ... % Tomo 1rpm + 1e9*data_tomo_6rpm_m0.Dy_rms_cl, 1e9*data_tomo_6rpm_m0.Dz_rms_cl, 1e9*data_tomo_6rpm_m0.Ry_rms_cl ; ... % Tomo 6rpm + 1e9*data_tomo_30rpm_m0.Dy_rms_cl, 1e9*data_tomo_30rpm_m0.Dz_rms_cl, 1e9*data_tomo_30rpm_m0.Ry_rms_cl ; ... % Tomo 30rpm + 1e9*rms(detrend(data_dz_10ums.e_dy, 0)), 1e9*rms(detrend(data_dz_10ums.e_dz, 0)), 1e9*rms(detrend(data_dz_10ums.e_ry, 0)) ; ... % Dz 10um/s + 1e9*rms(detrend(data_dz_100ums.e_dy,0)), 1e9*rms(detrend(data_dz_100ums.e_dz,0)), 1e9*rms(detrend(data_dz_100ums.e_ry,0)) ; ... % Dz 100um/s + 1e9*rms(detrend(data_ry.e_dy,0)), 1e9*rms(detrend(data_ry.e_dz,0)), 1e9*rms(detrend(data_ry.e_ry,0)) ; ... % Ry 100urad/s + 1e9*rms(detrend(data_ty_cl_slow.e_dy, 0)), 1e9*rms(detrend(data_ty_cl_slow.e_dz, 0)), 1e9*rms(detrend(data_ty_cl_slow.e_rz, 0)) ; ... % Dy 10 um/s + 1e9*rms(detrend(data_dt_100ums.Dy_int-data_dt_100ums.m_hexa_dy, 0)), 1e9*rms(detrend(data_dt_100ums.Dz_int, 0)), 1e9*rms(detrend(data_dt_100ums.Ry_int, 0)); ... % Diffraction tomo 0.1mm/s + 1e9*rms(detrend(data_dt_1000ums.Dy_int-data_dt_1000ums.m_hexa_dy,0)), 1e9*rms(detrend(data_dt_1000ums.Dz_int,0)), 1e9*rms(detrend(data_dt_1000ums.Ry_int,0)) ... % Diffraction tomo 1mm/s +]; +#+end_src + +#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*) +data2orgtable(data_results, {'Tomography ($R_z$ 1rpm)', 'Tomography ($R_z$ 6rpm)', 'Tomography ($R_z$ 30rpm)', 'Dirty Layer ($D_z$ $10\,\mu m/s$)', 'Dirty Layer ($D_z$ $100\,\mu m/s$)', 'Reflectivity ($R_y$ $100\,\mu\text{rad}/s$)', 'Lateral Scan ($D_y$ $10\,\mu m/s$)', 'Diffraction Tomography ($R_z$ 1rpm, $D_y$ 0.1mm/s)', 'Diffraction Tomography ($R_z$ 1rpm, $D_y$ 1mm/s)'}, {'$D_y$ [nmRMS]', '$D_z$ [nmRMS]', '$R_y$ [nradRMS]'}, ' %.0f '); +#+end_src + +#+name: tab:id31_experiments_results_summary +#+caption: Table caption +#+attr_latex: :environment tabularx :width \linewidth :align Xccc +#+attr_latex: :center t :booktabs t +#+RESULTS: +| | $D_y$ [nmRMS] | $D_z$ [nmRMS] | $R_y$ [nradRMS] | +|----------------------------------------------------+---------------+---------------+-----------------| +| Tomography ($R_z$ 1rpm) | 15 | 5 | 55 | +| Tomography ($R_z$ 6rpm) | 19 | 5 | 73 | +| Tomography ($R_z$ 30rpm) | 38 | 10 | 129 | +| Dirty Layer ($D_z$ $10\,\mu m/s$) | 25 | 5 | 114 | +| Dirty Layer ($D_z$ $100\,\mu m/s$) | 34 | 15 | 130 | +| Reflectivity ($R_y$ $100\,\mu\text{rad}/s$) | 28 | 6 | 118 | +| Lateral Scan ($D_y$ $10\,\mu m/s$) | 21 | 10 | 37 | +| Diffraction Tomography ($R_z$ 1rpm, $D_y$ 0.1mm/s) | 75 | 9 | 118 | +| Diffraction Tomography ($R_z$ 1rpm, $D_y$ 1mm/s) | 428 | 11 | 169 | * Bibliography :ignore: #+latex: \printbibliography[heading=bibintoc,title={Bibliography}] @@ -2535,8 +5219,6 @@ data_dir = './mat/' #+begin_src matlab % Simulink Model name mdl = 'nass_model_id31'; - -load('nass_model_conf_simulink.mat'); #+end_src ** Initialize other elements @@ -2704,6 +5386,60 @@ Center=(A\B).'; Radius=sqrt(mean(sum([X(:,1)-Center(1),X(:,2)-Center(2),X(:,3)-Center(3)].^2,2))); #+end_src +*** =unwrapphase= - Unwrap phase for FRF data +:PROPERTIES: +:header-args:matlab+: :tangle matlab/src/unwrapphase.m +:header-args:matlab+: :comments none :mkdirp yes :eval no +:END: + +#+begin_src matlab +function [unwraped_phase] = unwrapphase(frf, f, args) +#+end_src + +#+begin_src matlab +arguments + frf + f + args.f0 (1,1) double {mustBeNumeric} = 1 +end +#+end_src + +#+begin_src matlab +unwraped_phase = unwrap(frf); +[~,i] = min(abs(f - args.f0)); +unwraped_phase = unwraped_phase - 2*pi*round(unwraped_phase(i)./(2*pi)); +#+end_src + +*** =circlefit= +:PROPERTIES: +:header-args:matlab+: :tangle matlab/src/circlefit.m +:header-args:matlab+: :comments none :mkdirp yes :eval no +:END: + +#+begin_src matlab +function [xc,yc,R,a] = circlefit(x,y) +% +% [xc yx R] = circfit(x,y) +% +% fits a circle in x,y plane in a more accurate +% (less prone to ill condition ) +% procedure than circfit2 but using more memory +% x,y are column vector where (x(i),y(i)) is a measured point +% +% result is center point (yc,xc) and radius R +% an optional output is the vector of coeficient a +% describing the circle's equation +% +% x^2+y^2+a(1)*x+a(2)*y+a(3)=0 +% +% By: Izhak bucher 25/oct /1991, + x=x(:); y=y(:); + a=[x y ones(size(x))]\[-(x.^2+y.^2)]; + xc = -.5*a(1); + yc = -.5*a(2); + R = sqrt((a(1)^2+a(2)^2)/4-a(3)); +#+end_src + ** Initialize Simscape Model *** =initializeSimscapeConfiguration=: Simscape Configuration #+begin_src matlab :tangle matlab/src/initializeSimscapeConfiguration.m :comments none :mkdirp yes :eval no @@ -5161,22 +7897,22 @@ Note that there is trade-off between: :END: **** Theory -For inverse kinematic analysis, it is assumed that the position ${}^A\mathbf{P}$ and orientation of the moving platform ${}^A\mathbf{R}_B$ are given and the problem is to obtain the joint variables, namely, $\mathbf{L} = [l_1, l_2, \dots, l_6]^T$. +For inverse kinematic analysis, it is assumed that the position ${}^A\bm{P}$ and orientation of the moving platform ${}^A\bm{R}_B$ are given and the problem is to obtain the joint variables, namely, $\bm{L} = [l_1, l_2, \dots, l_6]^T$. From the geometry of the manipulator, the loop closure for each limb, $i = 1, 2, \dots, 6$ can be written as \begin{align*} - l_i {}^A\hat{\mathbf{s}}_i &= {}^A\mathbf{A} + {}^A\mathbf{b}_i - {}^A\mathbf{a}_i \\ - &= {}^A\mathbf{A} + {}^A\mathbf{R}_b {}^B\mathbf{b}_i - {}^A\mathbf{a}_i + l_i {}^A\hat{\bm{s}}_i &= {}^A\bm{A} + {}^A\bm{b}_i - {}^A\bm{a}_i \\ + &= {}^A\bm{A} + {}^A\bm{R}_b {}^B\bm{b}_i - {}^A\bm{a}_i \end{align*} -To obtain the length of each actuator and eliminate $\hat{\mathbf{s}}_i$, it is sufficient to dot multiply each side by itself: +To obtain the length of each actuator and eliminate $\hat{\bm{s}}_i$, it is sufficient to dot multiply each side by itself: \begin{equation} - l_i^2 \left[ {}^A\hat{\mathbf{s}}_i^T {}^A\hat{\mathbf{s}}_i \right] = \left[ {}^A\mathbf{P} + {}^A\mathbf{R}_B {}^B\mathbf{b}_i - {}^A\mathbf{a}_i \right]^T \left[ {}^A\mathbf{P} + {}^A\mathbf{R}_B {}^B\mathbf{b}_i - {}^A\mathbf{a}_i \right] + l_i^2 \left[ {}^A\hat{\bm{s}}_i^T {}^A\hat{\bm{s}}_i \right] = \left[ {}^A\bm{P} + {}^A\bm{R}_B {}^B\bm{b}_i - {}^A\bm{a}_i \right]^T \left[ {}^A\bm{P} + {}^A\bm{R}_B {}^B\bm{b}_i - {}^A\bm{a}_i \right] \end{equation} Hence, for $i = 1, 2, \dots, 6$, each limb length can be uniquely determined by: \begin{equation} - l_i = \sqrt{{}^A\mathbf{P}^T {}^A\mathbf{P} + {}^B\mathbf{b}_i^T {}^B\mathbf{b}_i + {}^A\mathbf{a}_i^T {}^A\mathbf{a}_i - 2 {}^A\mathbf{P}^T {}^A\mathbf{a}_i + 2 {}^A\mathbf{P}^T \left[{}^A\mathbf{R}_B {}^B\mathbf{b}_i\right] - 2 \left[{}^A\mathbf{R}_B {}^B\mathbf{b}_i\right]^T {}^A\mathbf{a}_i} + l_i = \sqrt{{}^A\bm{P}^T {}^A\bm{P} + {}^B\bm{b}_i^T {}^B\bm{b}_i + {}^A\bm{a}_i^T {}^A\bm{a}_i - 2 {}^A\bm{P}^T {}^A\bm{a}_i + 2 {}^A\bm{P}^T \left[{}^A\bm{R}_B {}^B\bm{b}_i\right] - 2 \left[{}^A\bm{R}_B {}^B\bm{b}_i\right]^T {}^A\bm{a}_i} \end{equation} If the position and orientation of the moving platform lie in the feasible workspace of the manipulator, one unique solution to the limb length is determined by the above equation. @@ -5235,5 +7971,7 @@ Otherwise, when the limbs' lengths derived yield complex numbers, then the posit #+end_src * Footnotes +[fn:4]Note that the eccentricity of the "point of interest" with respect to the Spindle rotation axis has been tuned from the measurements. +[fn:3]The "PEPU" [[cite:&hino18_posit_encod_proces_unit]] was used for digital protocol conversion between the interferometers and the Speedgoat [fn:2]M12/F40 model from Attocube [fn:1]Depending on the measuring range, gap can range from $\approx 1\,\mu m$ to $\approx 100\,\mu m$ diff --git a/test-bench-id31.pdf b/test-bench-id31.pdf index 3bc8a2b..2a7bbfa 100644 Binary files a/test-bench-id31.pdf and b/test-bench-id31.pdf differ diff --git a/test-bench-id31.tex b/test-bench-id31.tex index 69299b0..57c1e8f 100644 --- a/test-bench-id31.tex +++ b/test-bench-id31.tex @@ -1,4 +1,4 @@ -% Created 2024-11-13 Wed 17:06 +% Created 2024-11-15 Fri 18:12 % Intended LaTeX compiler: pdflatex \documentclass[a4paper, 10pt, DIV=12, parskip=full, bibliography=totoc]{scrreprt} @@ -35,7 +35,7 @@ The identify dynamics of the nano-hexapod fixed on top of the micro-station was Decentralized Integral Force Feedback is then applied to actively damp the plant in a robust way (Section \ref{sec:test_id31_iff}). -High authority control is then applied +High authority control is then applied (Section \ref{sec:test_id31_iff_hac}). \begin{figure}[htbp] \begin{subfigure}{0.49\textwidth} @@ -54,7 +54,6 @@ High authority control is then applied \end{figure} \chapter{Short Stroke Metrology System} -\label{sec:org598c753} \label{sec:test_id31_metrology} The control of the nano-hexapod requires an external metrology system measuring the relative position of the nano-hexapod top platform with respect to the granite. As the long-stroke (\(\approx 1 \,cm^3\)) metrology system was not developed yet, a stroke stroke (\(> 100\,\mu m^3\)) was used instead to validate the nano-hexapod control. @@ -92,7 +91,6 @@ This way, the gap between the sensor and the reference sphere is much larger (he Nevertheless, the metrology system still has limited measurement range, as when the spheres are moving perpendicularly to the beam axis, the reflected light does not coincide with the incident light, and for some perpendicular displacement, the interference is too small to be detected. \section{Metrology Kinematics} -\label{sec:org1981747} \label{ssec:test_id31_metrology_kinematics} The developed short-stroke metrology system is schematically shown in Figure \ref{fig:test_id31_metrology_kinematics}. @@ -135,7 +133,6 @@ The five equations \eqref{eq:test_id31_metrology_kinematics} can be written in a \end{equation} \section{Rough alignment of the reference spheres} -\label{sec:orgd2560a6} \label{ssec:test_id31_metrology_sphere_rought_alignment} The two reference spheres are aligned with the rotation axis of the spindle. @@ -150,7 +147,6 @@ This is probably limited due to the poor coaxiality between the cylinders and th However, the alignment precision should be enough to stay in the acceptance of the interferometers. \section{Tip-Tilt adjustment of the interferometers} -\label{sec:orgb663f66} \label{ssec:test_id31_metrology_alignment} The short stroke metrology system is placed on top of the main granite using a gantry made of granite blocs to have good vibration and thermal stability (Figure \ref{fig:short_stroke_metrology_overview}). @@ -169,7 +165,6 @@ This is equivalent as to optimize the perpendicularity between the interferomete The lateral sensor heads (i.e. all except the top one), which are each fixed to a custom tip-tilt adjustment mechanism, are individually oriented such that the coupling efficient is maximized. \section{Fine Alignment of reference spheres using interferometers} -\label{sec:orgde2bc57} \label{ssec:test_id31_metrology_sphere_fine_alignment} Thanks to the good alignment of the two reference spheres with the spindle axis and to the fine adjustment of the interferometers orientations, the interferometer measurement is made possible during complete spindle rotation. @@ -201,7 +196,6 @@ Remaining error after alignment is in the order of \(\pm5\,\mu\text{rad}\) for a \section{Estimated measurement volume} -\label{sec:org88488f3} \label{ssec:test_id31_metrology_acceptance} Because the interferometers are pointing to spheres and not flat surfaces, the lateral acceptance is limited. @@ -228,7 +222,6 @@ The obtained lateral acceptance for pure displacements in any direction is estim \section{Estimated measurement errors} -\label{sec:org9791179} \label{ssec:test_id31_metrology_errors} When using the NASS, the accuracy of the sample's positioning is linked to the accuracy of the external metrology. @@ -268,10 +261,27 @@ The effect of the noise on the translation and rotation measurements is estimate \end{figure} \chapter{Identified Open Loop Plant} -\label{sec:org7b9ff96} \label{sec:test_id31_open_loop_plant} +\begin{itemize} +\item Force sensors: \(\bm{V}_s = [V_{s1},\ V_{s2},\ V_{s3},\ V_{s4},\ V_{s5},\ V_{s6}]\) +\item Encoders: \(\bm{d}_e = [d_{e1},\ d_{e2},\ d_{e3},\ d_{e4},\ d_{e5},\ d_{e6}]\) +\item Interferometers: \(\bm{d} = [d_{1},\ d_{2},\ d_{3},\ d_{4},\ d_{5}]\) +\item Command signal: \(\bm{u} = [u_1,\ u_2,\ u_3,\ u_4,\ u_5,\ u_6]\) +\item Voltage across the piezoelectric stack actuator: \(\bm{V}_a = [V_{a1},\ V_{a2},\ V_{a3},\ V_{a4},\ V_{a5},\ V_{a6}]\) +\item Motion of the sample measured by external metrology: \(\bm{y}_\mathcal{X} = [D_x,\,D_y,\,D_z,\,R_x,\,R_y,\,R_z]\) +\item Error of the sample measured by external metrology: \(\bm{\epsilon\mathcal{X}} = [\epsilon_{D_x},\,\epsilon_{D_y},\,\epsilon_{D_z},\,\epsilon_{R_x},\,\epsilon_{R_y},\,\epsilon_{R_z}]\) +\item Error of the struts measured by external metrology: \(\bm{\epsilon\mathcal{L}} = [\epsilon_{\mathcal{L}_1},\,\epsilon_{\mathcal{L}_2},\,\epsilon_{\mathcal{L}_3},\,\epsilon_{\mathcal{L}_4},\,\epsilon_{\mathcal{L}_5},\,\epsilon_{\mathcal{L}_6}]\) +\item Spindle angle setpoint (or encoder): \(r_{R_z}\) +\item Translation stage setpoint: \(r_{D_y}\) +\item Tilt stage setpoint: \(r_{R_y}\) +\end{itemize} + +\begin{figure}[htbp] +\centering +\includegraphics[scale=1]{figs/test_id31_block_schematic_plant.png} +\caption{\label{fig:test_id31_block_schematic_plant}Schematic of the} +\end{figure} \section{First Open-Loop Plant Identification} -\label{sec:orgd1bd080} \label{ssec:test_id31_open_loop_plant_first_id} The plant dynamics is first identified for a fixed spindle angle (at \(0\,\text{deg}\)) and without any payload. @@ -279,7 +289,11 @@ The model dynamics is also identified in the same conditions. A first comparison between the model and the measured dynamics is done in Figure \ref{fig:test_id31_first_id}. A good match can be observed for the diagonal dynamics (except the high frequency modes which are not modeled). -However, the coupling for the transfer function from command signals \(\mathbf{u}\) to estimated strut motion from the external metrology \(e\mathbf{\mathcal{L}}\) is larger than expected (Figure \ref{fig:test_id31_first_id_int}). +However, the coupling for the transfer function from command signals \(\bm{u}\) to estimated strut motion from the external metrology \(e\bm{\mathcal{L}}\) is larger than expected (Figure \ref{fig:test_id31_first_id_int}). + +The experimental time delay estimated from the FRF (Figure \ref{fig:test_id31_first_id_int}) is larger than expected. +After investigation, it was found that the additional delay was due to digital processing unit\footnote{The ``PEPU'' \cite{hino18_posit_encod_proces_unit} was used for digital protocol conversion between the interferometers and the Speedgoat} that was used to read the interferometers in the Speedgoat. +This issue was later solved. \begin{figure}[htbp] \begin{subfigure}{0.49\textwidth} @@ -298,7 +312,6 @@ However, the coupling for the transfer function from command signals \(\mathbf{u \end{figure} \section{Better Angular Alignment} -\label{sec:org205ba5e} \label{ssec:test_id31_open_loop_plant_rz_alignment} One possible explanation of the increased coupling observed in Figure \ref{fig:test_id31_first_id_int} is the poor alignment between the external metrology axes (i.e. the interferometer supports) and the nano-hexapod axes. @@ -327,11 +340,11 @@ Results shown in Figure \ref{fig:test_id31_Rz_align_correct} are indeed indicati \end{figure} \section{Open-Loop Identification after alignment} -\label{sec:orgcb4c885} \label{ssec:test_id31_open_loop_plant_after_alignment} The plant dynamics is identified after the fine alignment and is compared with the model dynamics in Figure \ref{fig:test_id31_first_id_int_better_rz_align}. -Compared to the initial identification shown in Figure \ref{fig:test_id31_first_id_int}, the obtained coupling has decreased and is close to the coupling obtained with the multi-body model. +Compared to the initial identification shown in Figure \ref{fig:test_id31_first_id_int}, the obtained coupling has decreased and is now close to the coupling obtained with the multi-body model. +At low frequency (below \(10\,\text{Hz}\)) all the off-diagonal elements have an amplitude \(\approx 100\) times lower compared to the diagonal elements, indicating that a low bandwidth feedback controller can be implemented in a decentralized way (i.e. \(6\) SISO controllers). \begin{figure}[htbp] \centering @@ -340,12 +353,13 @@ Compared to the initial identification shown in Figure \ref{fig:test_id31_first_ \end{figure} \section{Effect of Payload Mass} -\label{sec:org497746b} \label{ssec:test_id31_open_loop_plant_mass} The system dynamics was identified with four payload conditions that are shown in Figure \ref{fig:test_id31_picture_masses}. The obtained direct terms are compared with the model dynamics in Figure \ref{fig:test_nhexa_comp_simscape_diag_masses}. +It is interesting to note that the anti-resonances in the force sensor plant are now appearing as minimum-phase, as the model predicts (Figure \ref{fig:test_id31_comp_simscape_iff_diag_masses}). + \begin{figure}[htbp] \begin{subfigure}{0.24\textwidth} \begin{center} @@ -379,7 +393,7 @@ The obtained direct terms are compared with the model dynamics in Figure \ref{fi \begin{center} \includegraphics[scale=1,width=0.95\linewidth]{figs/test_id31_comp_simscape_int_diag_masses.png} \end{center} -\subcaption{\label{fig:test_id31_comp_simscape_int_diag_masses}from $u$ to $d_e$} +\subcaption{\label{fig:test_id31_comp_simscape_int_diag_masses}from $u$ to $e\mathcal{L}$} \end{subfigure} \begin{subfigure}{0.49\textwidth} \begin{center} @@ -390,8 +404,7 @@ The obtained direct terms are compared with the model dynamics in Figure \ref{fi \caption{\label{fig:test_nhexa_comp_simscape_diag_masses}Comparison of the diagonal elements (i.e. ``direct'' terms) of the measured FRF matrix and the dynamics identified from the Simscape model. Both for the dynamics from \(u\) to \(e\mathcal{L}\) (\subref{fig:test_id31_comp_simscape_int_diag_masses}) and from \(u\) to \(V_s\) (\subref{fig:test_id31_comp_simscape_iff_diag_masses})} \end{figure} -\section{Effect of Rotation} -\label{sec:orgff15d08} +\section{Effect of Spindle Rotation} \label{ssec:test_id31_open_loop_plant_rotation} The dynamics was then identified while the Spindle was rotating at constant velocity. @@ -419,12 +432,337 @@ This also indicates that the metrology kinematics is correct and is working in r \caption{\label{fig:test_id31_effect_rotation}Effect of the spindle rotation on the plant dynamics from \(u\) to \(e\mathcal{L}\). Three rotational velocities are tested (\(0\,\text{deg}/s\), \(36\,\text{deg}/s\) and \(180\,\text{deg}/s\)). Both direct terms (\subref{fig:test_id31_effect_rotation_direct}) and coupling terms (\subref{fig:test_id31_effect_rotation_coupling}) are displayed.} \end{figure} -\section{Conclusion} -\label{sec:orgd703c88} +\section{Identification of Spurious modes} \begin{itemize} -\item Good match between the model and experiment +\item[{$\square$}] These are made to identify the modes of the spheres +\item[{$\square$}] Also discuss other observed modes \end{itemize} +\section*{Conclusion} +Thanks to the model, poor alignment between the nano-hexapod axes and the external metrology axes could be identified. +After alignment, the identified dynamics is well matching with the multi-body model. + +Also, the observed effects of the payload mass and of the spindle rotation on the dynamics are well matching the model predictions. + +\chapter{Decentralized Integral Force Feedback} +\label{sec:test_id31_iff} +Before implementing a position controller, an active damping controller was first implemented as shown in Figure \ref{fig:test_id31_iff_block_diagram}. +It consisted of a decentralized Integral Force Feedback controller \(\bm{K}_{\text{IFF}}\), with all the diagonal terms being equal \eqref{eq:test_id31_Kiff}. + +\begin{equation}\label{eq:test_id31_iff_diagonal} +\bm{K}_{\text{IFF}} = K_{\text{IFF}} \cdot \bm{I}_6 = \begin{bmatrix} +K_{\text{IFF}} & & 0 \\ +& \ddots & \\ +0 & & K_{\text{IFF}} +\end{bmatrix} +\end{equation} + +\begin{figure}[htbp] +\centering +\includegraphics[scale=1]{figs/test_id31_iff_schematic.png} +\caption{\label{fig:test_id31_iff_block_diagram}Block diagram of the implemented decentralized IFF controller. The controller \(\bm{K}_{\text{IFF}}\) is a diagonal controller with the same elements on every diagonal term \(K_{\text{IFF}}\).} +\end{figure} +\section{IFF Plant} +\label{ssec:test_id31_iff_plant} + +As the multi-body model is going to be used to estimate the stability of the IFF controller and to optimize achievable damping, it is first checked is this model accurately represents the system dynamics. + +In Figure \ref{fig:test_id31_comp_simscape_iff_diag_masses}, it was shown that the model well captures the dynamics from each actuator to its collocated force sensor, as that for all considered payloads. +The model is also accurate for the dynamics from an actuator to the force sensors in the other struts (i.e. the off-diagonal dynamics) as shown in Figure \ref{fig:test_id31_comp_simscape_Vs}. + +\begin{figure}[htbp] +\centering +\includegraphics[scale=1]{figs/test_id31_comp_simscape_Vs.png} +\caption{\label{fig:test_id31_comp_simscape_Vs}Comparison of the measured (in blue) and modeled (in red) frequency transfer functions from the first control signal \(u_1\) to the six force sensor voltages \(V_{s1}\) to \(V_{s6}\)} +\end{figure} + +\section{IFF Controller} +\label{ssec:test_id31_iff_controller} + +A decentralized IFF controller is there designed such that it adds damping to the suspension modes of the nano-hexapod for all considered payloads. +The frequency of the suspension modes are ranging from \(\approx 30\,\text{Hz}\) to \(\approx 250\,\text{Hz}\) (Figure \ref{fig:test_id31_comp_simscape_iff_diag_masses}), and therefore the IFF controller should provide integral action in this frequency range. +A second order high pass filter (cut-off frequency of \(10\,\text{Hz}\)) is added to limit the low frequency gain. + +The bode plot of the decentralized IFF controller is shown in Figure \ref{fig:test_id31_Kiff_bode_plot} and the ``decentralized loop-gains'' for all considered payload masses are shown in Figure \ref{fig:test_id31_Kiff_loop_gain}. +It can be seen that the loop-gain is larger than \(1\) around suspension modes indicating that some damping should be added to the suspension modes. + +\begin{equation}\label{eq:test_id31_Kiff} +K_{\text{IFF}} = g_0 \cdot \underbrace{\frac{1}{s}}_{\text{int}} \cdot \underbrace{\frac{s^2/\omega_z^2}{s^2/\omega_z^2 + 2\xi_z s /\omega_z + 1}}_{\text{2nd order LPF}},\quad \left(g_0 = -100,\ \omega_z = 2\pi10\,\text{rad/s},\ \xi_z = 0.7\right) +\end{equation} + +\begin{figure}[htbp] +\begin{subfigure}{0.49\textwidth} +\begin{center} +\includegraphics[scale=1,width=0.95\linewidth]{figs/test_id31_Kiff_bode_plot.png} +\end{center} +\subcaption{\label{fig:test_id31_Kiff_bode_plot}Bode plot of $K_{\text{IFF}}$} +\end{subfigure} +\begin{subfigure}{0.49\textwidth} +\begin{center} +\includegraphics[scale=1,width=0.95\linewidth]{figs/test_id31_Kiff_loop_gain.png} +\end{center} +\subcaption{\label{fig:test_id31_Kiff_loop_gain}Decentralized Loop gains} +\end{subfigure} +\caption{\label{fig:test_id31_Kiff}Bode plot of the decentralized IFF controller (\subref{fig:test_id31_Kiff_bode_plot}). The decentralized controller \(K_{\text{IFF}}\) multiplied by the identified dynamics from \(u_1\) to \(V_{s1}\) for all payloads are shown in (\subref{fig:test_id31_Kiff_loop_gain})} +\end{figure} + +To estimate the added damping, a root-locus plot is computed using the multi-body model (Figure \ref{fig:test_id31_iff_root_locus_m0}). +It can be seen that for all considered payloads, the poles are bounded to the ``left-half plane'' indicating that the decentralized IFF is robust. +The closed-loop poles for the chosen value of the gain are displayed by black crosses. +It can be seen that while damping can be added for all payloads (as compared to the open-loop case), the optimal value of the gain could be tuned separately for each payload. +For instance, for low payload masses, a higher value of the IFF controller gain could lead to better damping. +However, in this study, it was chosen to implement a fix (i.e. non-adaptive) decentralized IFF controller. + +\begin{figure}[htbp] +\begin{subfigure}{0.24\textwidth} +\begin{center} +\includegraphics[scale=1,width=0.9\linewidth]{figs/test_id31_iff_root_locus_m0.png} +\end{center} +\subcaption{\label{fig:test_id31_iff_root_locus_m0}$m = 0\,\text{kg}$} +\end{subfigure} +\begin{subfigure}{0.24\textwidth} +\begin{center} +\includegraphics[scale=1,width=0.9\linewidth]{figs/test_id31_iff_root_locus_m1.png} +\end{center} +\subcaption{\label{fig:test_id31_iff_root_locus_m1}$m = 13\,\text{kg}$} +\end{subfigure} +\begin{subfigure}{0.24\textwidth} +\begin{center} +\includegraphics[scale=1,width=0.9\linewidth]{figs/test_id31_iff_root_locus_m2.png} +\end{center} +\subcaption{\label{fig:test_id31_iff_root_locus_m2}$m = 26\,\text{kg}$} +\end{subfigure} +\begin{subfigure}{0.24\textwidth} +\begin{center} +\includegraphics[scale=1,width=0.9\linewidth]{figs/test_id31_iff_root_locus_m3.png} +\end{center} +\subcaption{\label{fig:test_id31_iff_root_locus_m3}$m = 39\,\text{kg}$} +\end{subfigure} +\caption{\label{fig:test_id31_iff_root_locus_m0}Root Locus plots for the designed decentralized IFF controller and using the multy-body model. Black crosses indicate the closed-loop poles for the choosen value of the gain.} +\end{figure} + +\section{Estimated Damped Plant} +\label{ssec:test_id31_iff_perf} + +As the model is accurately modelling the system dynamics, it can be used to estimate the damped plant, i.e. the transfer functions from \(\bm{u}^\prime\) to \(\bm{\mathcal{L}}\). +The obtained damped plants are compared with the open-loop plants in Figure \ref{fig:test_id31_comp_ol_iff_plant_model}. +The peak amplitudes corresponding to the suspension modes are approximately reduced by a factor \(10\) for all considered payloads, and with the same decentralized IFF controller. + +\begin{figure}[htbp] +\centering +\includegraphics[scale=1]{figs/test_id31_comp_ol_iff_plant_model.png} +\caption{\label{fig:test_id31_comp_ol_iff_plant_model}Comparison of the open-loop plants and the estimated damped plant with Decentralized IFF.} +\end{figure} + +\section*{Conclusion} +\chapter{High Authority Control in the frame of the struts} +\label{sec:test_id31_iff_hac} +The position of the sample is actively stabilized by implementing a High-Authority-Controller as shown in Figure \ref{fig:test_id31_iff_hac_schematic}. + +\begin{equation}\label{eq:eq:test_id31_hac_diagonal} +\bm{K}_{\text{HAC}} = K_{\text{HAC}} \cdot \bm{I}_6 = \begin{bmatrix} +K_{\text{HAC}} & & 0 \\ +& \ddots & \\ +0 & & K_{\text{HAC}} +\end{bmatrix} +\end{equation} + +\begin{figure}[htbp] +\centering +\includegraphics[scale=1]{figs/test_id31_iff_hac_schematic.png} +\caption{\label{fig:test_id31_iff_hac_schematic}Block diagram of the implemented HAC-IFF controllers. The controller \(\bm{K}_{\text{HAC}}\) is a diagonal controller with the same elements on every diagonal term \(K_{\text{HAC}}\).} +\end{figure} +\section{Damped Plant} +\label{ssec:test_id31_iff_hac_plant} + +The damped plants (i.e. the transfer function from \(\bm{u}^\prime\) to \(\bm{\epsilon\mathcal{L}}\)) were identified for all payload conditions. +To verify if the model accurately represents the damped plants, both direct terms and coupling terms corresponding to the first actuator are compared in Figure \ref{fig:test_id31_comp_simscape_hac}. + +\begin{figure}[htbp] +\centering +\includegraphics[scale=1]{figs/test_id31_comp_simscape_hac.png} +\caption{\label{fig:test_id31_comp_simscape_hac}Comparison of the measured (in blue) and modeled (in red) frequency transfer functions from the first control signal (\(u_1^\prime\)) of the damped plant to the estimated errors (\(\epsilon_{\mathcal{L}_i}\)) in the frame of the six struts by the external metrology} +\end{figure} + +The six direct terms for all four payload conditions are compared with the model in Figure \ref{fig:test_id31_hac_plant_effect_mass}. +It is shown that the model accurately represents the dynamics for all payloads. + +In Section \ref{sec:test_id31_iff_hac}, a High Authority Controller is tuned to be robust to the change of dynamics due to different payloads used. +Without decentralized IFF being applied, the controller would have had to be robust to all the undamped dynamics shown in Figure \ref{fig:test_id31_comp_all_undamped_damped_plants}, which is a very complex control problem. +With the applied decentralized IFF, the HAC instead had to be be robust to all the damped dynamics shown in Figure \ref{fig:test_id31_comp_all_undamped_damped_plants}, which is easier from a control perspective. +This is one of the key benefit of using the HAC-LAC strategy. + +\begin{figure}[htbp] +\begin{subfigure}{0.49\textwidth} +\begin{center} +\includegraphics[scale=1,width=0.95\linewidth]{figs/test_id31_hac_plant_effect_mass.png} +\end{center} +\subcaption{\label{fig:test_id31_hac_plant_effect_mass}Effect of payload mass on $\epsilon\mathcal{L}_i/u_i^\prime$} +\end{subfigure} +\begin{subfigure}{0.49\textwidth} +\begin{center} +\includegraphics[scale=1,width=0.95\linewidth]{figs/test_id31_comp_all_undamped_damped_plants.png} +\end{center} +\subcaption{\label{fig:test_id31_comp_all_undamped_damped_plants}Undamped and damped plants} +\end{subfigure} +\caption{\label{fig:test_id31_hac_plant_effect_mass_comp_model}Comparison of the measured damped plants and modeled plants for all considered payloads, only ``direct'' terms (\(\epsilon\mathcal{L}_i/u_i^\prime\)) are displayed (\subref{fig:test_id31_hac_plant_effect_mass}). Comparison of all undamped \(\epsilon\mathcal{L}_i/u_i\) and damped \(\epsilon\mathcal{L}_i/u_i^\prime\) measured frequency response functions for all payloads is done in (\subref{fig:test_id31_comp_all_undamped_damped_plants}).} +\end{figure} + +\section{Robust Controller Design} +\label{ssec:test_id31_iff_hac_controller} + +A first diagonal controller was designed to be robust to change of payloads, which means that every damped plants shown in Figure \ref{fig:test_id31_comp_all_undamped_damped_plants} should be considered during the controller design. +For a first design, a crossover frequency of \(5\,\text{Hz}\) for chosen. +One integrator is added to increase the low frequency gain, a lead is added around \(5\,\text{Hz}\) to increase the stability margins and a first order low pass filter with a cut-off frequency of \(30\,\text{Hz}\) is added to improve the robustness to dynamical uncertainty at high frequency. +The obtained ``decentralized'' loop-gains are shown in Figure \ref{fig:test_id31_hac_loop_gain}. + +\begin{equation}\label{eq:test_id31_robust_hac} +K_{\text{HAC}} = g_0 \cdot \underbrace{\frac{\omega_c}{s}}_{\text{int}} \cdot \underbrace{\frac{1}{\sqrt{\alpha}}\frac{1 + \frac{s}{\omega_c/\sqrt{\alpha}}}{1 + \frac{s}{\omega_c\sqrt{\alpha}}}}_{\text{lead}} \cdot \underbrace{\frac{1}{1 + \frac{s}{\omega_0}}}_{\text{LPF}}, \quad \left( \omega_c = 2\pi5\,\text{rad/s},\ \alpha = 2,\ \omega_0 = 2\pi30\,\text{rad/s} \right) +\end{equation} + +The closed-loop stability is verified by computing the characteristic Loci (Figure \ref{fig:test_id31_hac_characteristic_loci}). + +\begin{figure}[htbp] +\begin{subfigure}{0.49\textwidth} +\begin{center} +\includegraphics[scale=1,width=0.95\linewidth]{figs/test_id31_hac_loop_gain.png} +\end{center} +\subcaption{\label{fig:test_id31_hac_loop_gain}Loop Gains} +\end{subfigure} +\begin{subfigure}{0.49\textwidth} +\begin{center} +\includegraphics[scale=1,width=0.95\linewidth]{figs/test_id31_hac_characteristic_loci.png} +\end{center} +\subcaption{\label{fig:test_id31_hac_characteristic_loci}Characteristic Loci} +\end{subfigure} +\caption{\label{fig:test_id31_hac_loop_gain_loci}Robust High Authority Controller. ``Decentralized loop-gains'' are shown in (\subref{fig:test_id31_hac_loop_gain}) and characteristic loci are shown in (\subref{fig:test_id31_hac_characteristic_loci})} +\end{figure} + +\section{Estimation of performances} +\label{ssec:test_id31_iff_hac_perf} + +To estimate the performances that can be expected with this HAC-LAC architecture and the designed controllers, two simulations of tomography experiments were performed\footnote{Note that the eccentricity of the ``point of interest'' with respect to the Spindle rotation axis has been tuned from the measurements.}. +The rotational velocity was set to 30rpm, and no payload was added on top of the nano-hexapod. +An open-loop simulation and a closed-loop simulation were performed and compared in Figure \ref{fig:test_id31_tomo_m0_30rpm_robust_hac_iff_sim}. + +\begin{figure}[htbp] +\begin{subfigure}{0.49\textwidth} +\begin{center} +\includegraphics[scale=1,scale=0.9]{figs/test_id31_tomo_m0_30rpm_robust_hac_iff_sim_xy.png} +\end{center} +\subcaption{\label{fig:test_id31_tomo_m0_30rpm_robust_hac_iff_sim_xy}XY plane} +\end{subfigure} +\begin{subfigure}{0.49\textwidth} +\begin{center} +\includegraphics[scale=1,scale=0.9]{figs/test_id31_tomo_m0_30rpm_robust_hac_iff_sim_yz.png} +\end{center} +\subcaption{\label{fig:test_id31_tomo_m0_30rpm_robust_hac_iff_sim_yz}YZ plane} +\end{subfigure} +\caption{\label{fig:test_id31_tomo_m0_30rpm_robust_hac_iff_sim}Position error of the sample in the XY (\subref{fig:test_id31_tomo_m0_30rpm_robust_hac_iff_sim_xy}) and YZ (\subref{fig:test_id31_tomo_m0_30rpm_robust_hac_iff_sim_yz}) planes during a simulation of a tomography experiment at 30RPM. No payload is placed on top of the nano-hexapod.} +\end{figure} + +Then the same tomography experiment (i.e. constant spindle rotation at 30rpm, and no payload) was performed experimentally. +The measured position of the ``point of interest'' during the experiment are shown in Figure \ref{fig:test_id31_tomo_m0_30rpm_robust_hac_iff_exp}. + +\begin{figure}[htbp] +\begin{subfigure}{0.49\textwidth} +\begin{center} +\includegraphics[scale=1,scale=0.9]{figs/test_id31_tomo_m0_30rpm_robust_hac_iff_exp_xy.png} +\end{center} +\subcaption{\label{fig:test_id31_tomo_m0_30rpm_robust_hac_iff_exp_xy}XY plane} +\end{subfigure} +\begin{subfigure}{0.49\textwidth} +\begin{center} +\includegraphics[scale=1,scale=0.9]{figs/test_id31_tomo_m0_30rpm_robust_hac_iff_exp_yz.png} +\end{center} +\subcaption{\label{fig:test_id31_tomo_m0_30rpm_robust_hac_iff_exp_yz}YZ plane} +\end{subfigure} +\caption{\label{fig:test_id31_tomo_m0_30rpm_robust_hac_iff_exp}Experimental results of a tomography experiment at 30RPM without payload. Position error of the sample is shown in the XY (\subref{fig:test_id31_tomo_m0_30rpm_robust_hac_iff_exp_xy}) and YZ (\subref{fig:test_id31_tomo_m0_30rpm_robust_hac_iff_exp_yz}) planes.} +\end{figure} + +Even though the simulation (Figure \ref{fig:test_id31_tomo_m0_30rpm_robust_hac_iff_sim}) and the experimental results (Figure \ref{fig:test_id31_tomo_m0_30rpm_robust_hac_iff_exp}) are looking similar, the most important metric to compare is the RMS values of the positioning errors in closed-loop. +These are computed for both the simulation and the experimental results and are compared in Table \ref{tab:test_id31_tomo_m0_30rpm_robust_hac_iff_rms}. +The lateral and vertical errors are similar, however the tilt (\(R_y\)) errors are underestimated by the model, which is reasonable as disturbances in \(R_y\) were not modeled. + +Results obtained with this conservative HAC are already close to the specifications. + +\begin{table}[htbp] +\caption{\label{tab:test_id31_tomo_m0_30rpm_robust_hac_iff_rms}RMS values of the errors for a tomography experiment at 30RPM and without payload. Experimental results and simulation are compared.} +\centering +\begin{tabularx}{0.7\linewidth}{Xccc} +\toprule + & \(D_y\) & \(D_z\) & \(R_y\)\\ +\midrule +Experiment (OL) & \(1.8\,\mu\text{mRMS}\) & \(24\,\text{nmRMS}\) & \(10\,\mu\text{radRMS}\)\\ +\midrule +Simulation (CL) & \(30\,\text{nmRMS}\) & \(8\,\text{nmRMS}\) & \(73\,\text{nradRMS}\)\\ +Experiment (CL) & \(39\,\text{nmRMS}\) & \(11\,\text{nmRMS}\) & \(130\,\text{nradRMS}\)\\ +\midrule +Specifications (CL) & \(30\,\text{nmRMS}\) & \(15\,\text{nmRMS}\) & \(250\,\text{nradRMS}\)\\ +\bottomrule +\end{tabularx} +\end{table} + +\section{Robustness to change of payload} +\label{ssec:test_id31_iff_hac_robustness} + +To verify the robustness to the change of payload mass, four simulations of tomography experiments were performed with payloads as shown Figure \ref{fig:test_id31_picture_masses} (i.e. \(0\,kg\), \(13\,kg\), \(26\,kg\) and \(39\,kg\)). +This time, the rotational velocity was set at 1rpm (i.e. 6deg/s), as it is the typical rotational velocity for heavy samples. +The closed-loop systems were stable for all payload conditions, indicating good control robustness. + +The tomography experiments that were simulated were then experimentally conducted. +For each payload, a spindle rotating was first performed in open-loop, and then the loop was closed during another full spindle rotation. +An example with the \(26\,\text{kg}\) payload is shown in Figure \ref{fig:test_id31_tomo_m2_1rpm_robust_hac_iff_fit}. +The eccentricity between the ``point of interest'' and the spindle rotation axis is quite large as the added payload mass statically deforms the micro-station stages. +To estimate the open-loop errors, it is supposed that the ``point of interest'' can be perfectly aligned with the spindle rotation axis. +Therefore, the eccentricity is first estimated by performing a circular fit (dashed black circle in Figure \ref{fig:test_id31_tomo_m2_1rpm_robust_hac_iff_fit}), and then subtracted from the data in Figure \ref{fig:test_id31_tomo_m2_1rpm_robust_hac_iff_fit_removed}. +This underestimate the real condition open-loop errors as it is difficult to obtain a perfect alignment in practice. + +\begin{figure}[htbp] +\begin{subfigure}{0.49\textwidth} +\begin{center} +\includegraphics[scale=1,scale=0.9]{figs/test_id31_tomo_m2_1rpm_robust_hac_iff_fit.png} +\end{center} +\subcaption{\label{fig:test_id31_tomo_m2_1rpm_robust_hac_iff_fit}Errors in $(x,y)$ plane} +\end{subfigure} +\begin{subfigure}{0.49\textwidth} +\begin{center} +\includegraphics[scale=1,scale=0.9]{figs/test_id31_tomo_m2_1rpm_robust_hac_iff_fit_removed.png} +\end{center} +\subcaption{\label{fig:test_id31_tomo_m2_1rpm_robust_hac_iff_fit_removed}Removed eccentricity} +\end{subfigure} +\caption{\label{fig:test_id31_tomo_m2_1rpm_robust_hac_iff}Tomography experiment with rotation velocity of 1rpm, and payload mass of 26kg. Errors in the \((x,y)\) plane are shown in (\subref{fig:test_id31_tomo_m2_1rpm_robust_hac_iff_fit}). The estimated eccentricity is displayed by the black dashed circle. Errors with subtracted eccentricity are shown in (\subref{fig:test_id31_tomo_m2_1rpm_robust_hac_iff_fit_removed}).} +\end{figure} + +The RMS values of the open-loop and closed-loop errors for all masses are summarized in Table \ref{tab:test_id31_tomo_1rpm_robust_ol_cl_errors}. +The obtained closed-loop errors are fulfilling the requirements, except for the \(39\,\text{kg}\) payload in the lateral (\(D_y\)) direction. + +\begin{table}[htbp] +\caption{\label{tab:test_id31_tomo_1rpm_robust_ol_cl_errors}RMS values of the measured errors during open-loop and closed-loop tomography scans (1rpm) for all considered payloads.} +\centering +\begin{tabularx}{0.9\linewidth}{Xccc} +\toprule + & \(D_y\) & \(D_z\) & \(R_y\)\\ +\midrule +\(0\,kg\) OL & \(142\,\text{nm RMS}\) & \(32\,\text{nm RMS}\) & \(460\,\text{nrad RMS}\)\\ +\(0\,kg\) CL & \(\bm{15}\,\text{nm RMS}\) & \(\bm{5}\,\text{nm RMS}\) & \(\bm{55}\,\text{nrad RMS}\)\\ +\midrule +\(13\,kg\) OL & \(149\,\text{nm RMS}\) & \(26\,\text{nm RMS}\) & \(470\,\text{nrad RMS}\)\\ +\(13\,kg\) CL & \(\bm{25}\,\text{nm RMS}\) & \(\bm{6}\,\text{nm RMS}\) & \(\bm{55}\,\text{nrad RMS}\)\\ +\midrule +\(26\,kg\) OL & \(202\,\text{nm RMS}\) & \(36\,\text{nm RMS}\) & \(1700\,\text{nrad RMS}\)\\ +\(26\,kg\) CL & \(\bm{25}\,\text{nm RMS}\) & \(\bm{7}\,\text{nm RMS}\) & \(\bm{103}\,\text{nrad RMS}\)\\ +\midrule +\(39\,kg\) OL & \(297\,\text{nm RMS}\) & \(38\,\text{nm RMS}\) & \(1700\,\text{nrad RMS}\)\\ +\(39\,kg\) CL & \(\bm{53}\,\text{nm RMS}\) & \(\bm{9}\,\text{nm RMS}\) & \(\bm{169}\,\text{nrad RMS}\)\\ +\midrule +\textbf{Specifications} & \(30\,\text{nmRMS}\) & \(15\,\text{nmRMS}\) & \(250\,\text{nradRMS}\)\\ +\bottomrule +\end{tabularx} +\end{table} + +\section*{Conclusion} + \printbibliography[heading=bibintoc,title={Bibliography}] \end{document}