Tangle + use online CSS and JS

This commit is contained in:
Thomas Dehaeze 2020-11-12 10:25:34 +01:00
parent beb037bb95
commit 50dab7913d
13 changed files with 1036 additions and 1346 deletions

View File

@ -1,145 +0,0 @@
.org-bold { /* bold */ font-weight: bold; }
.org-bold-italic { /* bold-italic */ font-weight: bold; font-style: italic; }
.org-buffer-menu-buffer { /* buffer-menu-buffer */ font-weight: bold; }
.org-builtin { /* font-lock-builtin-face */ color: #7a378b; }
.org-button { /* button */ text-decoration: underline; }
.org-calendar-today { /* calendar-today */ text-decoration: underline; }
.org-change-log-acknowledgement { /* change-log-acknowledgement */ color: #b22222; }
.org-change-log-conditionals { /* change-log-conditionals */ color: #a0522d; }
.org-change-log-date { /* change-log-date */ color: #8b2252; }
.org-change-log-email { /* change-log-email */ color: #a0522d; }
.org-change-log-file { /* change-log-file */ color: #0000ff; }
.org-change-log-function { /* change-log-function */ color: #a0522d; }
.org-change-log-list { /* change-log-list */ color: #a020f0; }
.org-change-log-name { /* change-log-name */ color: #008b8b; }
.org-comint-highlight-input { /* comint-highlight-input */ font-weight: bold; }
.org-comint-highlight-prompt { /* comint-highlight-prompt */ color: #00008b; }
.org-comment { /* font-lock-comment-face */ color: #999988; font-style: italic; }
.org-comment-delimiter { /* font-lock-comment-delimiter-face */ color: #999988; font-style: italic; }
.org-completions-annotations { /* completions-annotations */ font-style: italic; }
.org-completions-common-part { /* completions-common-part */ color: #000000; background-color: #ffffff; }
.org-completions-first-difference { /* completions-first-difference */ font-weight: bold; }
.org-constant { /* font-lock-constant-face */ color: #008b8b; }
.org-diary { /* diary */ color: #ff0000; }
.org-diff-context { /* diff-context */ color: #7f7f7f; }
.org-diff-file-header { /* diff-file-header */ background-color: #b3b3b3; font-weight: bold; }
.org-diff-function { /* diff-function */ background-color: #cccccc; }
.org-diff-header { /* diff-header */ background-color: #cccccc; }
.org-diff-hunk-header { /* diff-hunk-header */ background-color: #cccccc; }
.org-diff-index { /* diff-index */ background-color: #b3b3b3; font-weight: bold; }
.org-diff-nonexistent { /* diff-nonexistent */ background-color: #b3b3b3; font-weight: bold; }
.org-diff-refine-change { /* diff-refine-change */ background-color: #d9d9d9; }
.org-dired-directory { /* dired-directory */ color: #0000ff; }
.org-dired-flagged { /* dired-flagged */ color: #ff0000; font-weight: bold; }
.org-dired-header { /* dired-header */ color: #228b22; }
.org-dired-ignored { /* dired-ignored */ color: #7f7f7f; }
.org-dired-mark { /* dired-mark */ color: #008b8b; }
.org-dired-marked { /* dired-marked */ color: #ff0000; font-weight: bold; }
.org-dired-perm-write { /* dired-perm-write */ color: #b22222; }
.org-dired-symlink { /* dired-symlink */ color: #a020f0; }
.org-dired-warning { /* dired-warning */ color: #ff0000; font-weight: bold; }
.org-doc { /* font-lock-doc-face */ color: #8b2252; }
.org-escape-glyph { /* escape-glyph */ color: #a52a2a; }
.org-file-name-shadow { /* file-name-shadow */ color: #7f7f7f; }
.org-flyspell-duplicate { /* flyspell-duplicate */ color: #cdad00; font-weight: bold; text-decoration: underline; }
.org-flyspell-incorrect { /* flyspell-incorrect */ color: #ff4500; font-weight: bold; text-decoration: underline; }
.org-fringe { /* fringe */ background-color: #f2f2f2; }
.org-function-name { /* font-lock-function-name-face */ color: teal; }
.org-header-line { /* header-line */ color: #333333; background-color: #e5e5e5; }
.org-help-argument-name { /* help-argument-name */ font-style: italic; }
.org-highlight { /* highlight */ background-color: #b4eeb4; }
.org-holiday { /* holiday */ background-color: #ffc0cb; }
.org-isearch { /* isearch */ color: #b0e2ff; background-color: #cd00cd; }
.org-isearch-fail { /* isearch-fail */ background-color: #ffc1c1; }
.org-italic { /* italic */ font-style: italic; }
.org-keyword { /* font-lock-keyword-face */ color: #0086b3; }
.org-lazy-highlight { /* lazy-highlight */ background-color: #afeeee; }
.org-link { /* link */ color: #0000ff; text-decoration: underline; }
.org-link-visited { /* link-visited */ color: #8b008b; text-decoration: underline; }
.org-log-edit-header { /* log-edit-header */ color: #a020f0; }
.org-log-edit-summary { /* log-edit-summary */ color: #0000ff; }
.org-log-edit-unknown-header { /* log-edit-unknown-header */ color: #b22222; }
.org-match { /* match */ background-color: #ffff00; }
.org-next-error { /* next-error */ background-color: #eedc82; }
.org-nobreak-space { /* nobreak-space */ color: #a52a2a; text-decoration: underline; }
.org-org-archived { /* org-archived */ color: #7f7f7f; }
.org-org-block { /* org-block */ color: #7f7f7f; }
.org-org-block-begin-line { /* org-block-begin-line */ color: #b22222; }
.org-org-block-end-line { /* org-block-end-line */ color: #b22222; }
.org-org-checkbox { /* org-checkbox */ font-weight: bold; }
.org-org-checkbox-statistics-done { /* org-checkbox-statistics-done */ color: #228b22; font-weight: bold; }
.org-org-checkbox-statistics-todo { /* org-checkbox-statistics-todo */ color: #ff0000; font-weight: bold; }
.org-org-clock-overlay { /* org-clock-overlay */ background-color: #ffff00; }
.org-org-code { /* org-code */ color: #7f7f7f; }
.org-org-column { /* org-column */ background-color: #e5e5e5; }
.org-org-column-title { /* org-column-title */ background-color: #e5e5e5; font-weight: bold; text-decoration: underline; }
.org-org-date { /* org-date */ color: #a020f0; text-decoration: underline; }
.org-org-document-info { /* org-document-info */ color: #191970; }
.org-org-document-info-keyword { /* org-document-info-keyword */ color: #7f7f7f; }
.org-org-document-title { /* org-document-title */ color: #191970; font-size: 144%; font-weight: bold; }
.org-org-done { /* org-done */ color: #228b22; font-weight: bold; }
.org-org-drawer { /* org-drawer */ color: #0000ff; }
.org-org-ellipsis { /* org-ellipsis */ color: #b8860b; text-decoration: underline; }
.org-org-footnote { /* org-footnote */ color: #a020f0; text-decoration: underline; }
.org-org-formula { /* org-formula */ color: #b22222; }
.org-org-headline-done { /* org-headline-done */ color: #bc8f8f; }
.org-org-hide { /* org-hide */ color: #ffffff; }
.org-org-latex-and-export-specials { /* org-latex-and-export-specials */ color: #8b4513; }
.org-org-level-1 { /* org-level-1 */ color: #0000ff; }
.org-org-level-2 { /* org-level-2 */ color: #a0522d; }
.org-org-level-3 { /* org-level-3 */ color: #a020f0; }
.org-org-level-4 { /* org-level-4 */ color: #b22222; }
.org-org-level-5 { /* org-level-5 */ color: #228b22; }
.org-org-level-6 { /* org-level-6 */ color: #008b8b; }
.org-org-level-7 { /* org-level-7 */ color: #7a378b; }
.org-org-level-8 { /* org-level-8 */ color: #8b2252; }
.org-org-link { /* org-link */ color: #0000ff; text-decoration: underline; }
.org-org-meta-line { /* org-meta-line */ color: #b22222; }
.org-org-mode-line-clock { /* org-mode-line-clock */ color: #000000; background-color: #bfbfbf; }
.org-org-mode-line-clock-overrun { /* org-mode-line-clock-overrun */ color: #000000; background-color: #ff0000; }
.org-org-quote { /* org-quote */ color: #7f7f7f; }
.org-org-scheduled { /* org-scheduled */ color: #006400; }
.org-org-scheduled-previously { /* org-scheduled-previously */ color: #b22222; }
.org-org-scheduled-today { /* org-scheduled-today */ color: #006400; }
.org-org-sexp-date { /* org-sexp-date */ color: #a020f0; }
.org-org-special-keyword { /* org-special-keyword */ color: #a020f0; }
.org-org-table { /* org-table */ color: #0000ff; }
.org-org-tag { /* org-tag */ font-weight: bold; }
.org-org-target { /* org-target */ text-decoration: underline; }
.org-org-time-grid { /* org-time-grid */ color: #b8860b; }
.org-org-todo { /* org-todo */ color: #ff0000; font-weight: bold; }
.org-org-upcoming-deadline { /* org-upcoming-deadline */ color: #b22222; }
.org-org-verbatim { /* org-verbatim */ color: #7f7f7f; }
.org-org-verse { /* org-verse */ color: #7f7f7f; }
.org-org-warning { /* org-warning */ color: #ff0000; font-weight: bold; }
.org-outline-1 { /* outline-1 */ color: #0000ff; }
.org-outline-2 { /* outline-2 */ color: #a0522d; }
.org-outline-3 { /* outline-3 */ color: #a020f0; }
.org-outline-4 { /* outline-4 */ color: #b22222; }
.org-outline-5 { /* outline-5 */ color: #228b22; }
.org-outline-6 { /* outline-6 */ color: #008b8b; }
.org-outline-7 { /* outline-7 */ color: #7a378b; }
.org-outline-8 { /* outline-8 */ color: #8b2252; }
.org-preprocessor { /* font-lock-preprocessor-face */ color: #7a378b; }
.org-query-replace { /* query-replace */ color: #b0e2ff; background-color: #cd00cd; }
.org-regexp-grouping-backslash { /* font-lock-regexp-grouping-backslash */ font-weight: bold; }
.org-regexp-grouping-construct { /* font-lock-regexp-grouping-construct */ font-weight: bold; }
.org-region { /* region */ background-color: #eedc82; }
.org-secondary-selection { /* secondary-selection */ background-color: #ffff00; }
.org-shadow { /* shadow */ color: #7f7f7f; }
.org-show-paren-match { /* show-paren-match */ background-color: #40e0d0; }
.org-show-paren-mismatch { /* show-paren-mismatch */ color: #ffffff; background-color: #a020f0; }
.org-string { /* font-lock-string-face */ color: #dd1144; }
.org-tool-bar { /* tool-bar */ color: #000000; background-color: #bfbfbf; }
.org-tooltip { /* tooltip */ color: #000000; background-color: #ffffe0; }
.org-trailing-whitespace { /* trailing-whitespace */ background-color: #ff0000; }
.org-type { /* font-lock-type-face */ color: #228b22; }
.org-underline { /* underline */ text-decoration: underline; }
.org-variable-name { /* font-lock-variable-name-face */ color: teal; }
.org-warning { /* font-lock-warning-face */ color: #ff0000; font-weight: bold; }
.org-widget-button { /* widget-button */ font-weight: bold; }
.org-widget-button-pressed { /* widget-button-pressed */ color: #ff0000; }
.org-widget-documentation { /* widget-documentation */ color: #006400; }
.org-widget-field { /* widget-field */ background-color: #d9d9d9; }
.org-widget-inactive { /* widget-inactive */ color: #7f7f7f; }
.org-widget-single-line-field { /* widget-single-line-field */ background-color: #d9d9d9; }

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -6,12 +6,11 @@
#+EMAIL: dehaeze.thomas@gmail.com
#+AUTHOR: Dehaeze Thomas
#+HTML_HEAD: <link rel="stylesheet" type="text/css" href="./css/htmlize.css"/>
#+HTML_HEAD: <link rel="stylesheet" type="text/css" href="./css/readtheorg.css"/>
#+HTML_HEAD: <script type="text/javascript" src="./js/jquery.min.js"></script>
#+HTML_HEAD: <script type="text/javascript" src="./js/bootstrap.min.js"></script>
#+HTML_HEAD: <script type="text/javascript" src="./js/jquery.stickytableheaders.min.js"></script>
#+HTML_HEAD: <script type="text/javascript" src="./js/readtheorg.js"></script>
#+HTML_LINK_HOME: ../index.html
#+HTML_LINK_UP: ../index.html
#+HTML_HEAD: <link rel="stylesheet" type="text/css" href="https://research.tdehaeze.xyz/css/style.css"/>
#+HTML_HEAD: <script type="text/javascript" src="https://research.tdehaeze.xyz/js/script.js"></script>
#+PROPERTY: header-args:matlab :session *MATLAB*
#+PROPERTY: header-args:matlab+ :comments org
@ -19,6 +18,7 @@
#+PROPERTY: header-args:matlab+ :exports both
#+PROPERTY: header-args:matlab+ :eval no-export
#+PROPERTY: header-args:matlab+ :output-dir figs
#+PROPERTY: header-args:matlab+ :tangle no
#+PROPERTY: header-args:shell :eval no-export
:END:
@ -1296,11 +1296,20 @@ The loop gain is $L = G K$.
<<matlab-init>>
#+end_src
#+begin_src matlab :tangle no
addpath('./matlab/');
addpath('./matlab/mat/');
#+end_src
#+begin_src matlab :eval no
addpath('./mat/');
#+end_src
** Initialization
Let's load the previously defined parameters for the model.
#+begin_src matlab :exports none :noweb yes
load('./mat/parameters.mat');
load('parameters.mat');
#+end_src
#+begin_src matlab :results none

7
js/bootstrap.min.js vendored

File diff suppressed because one or more lines are too long

4
js/jquery.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
!function(a,b){"use strict";function c(c,g){var h=this;h.$el=a(c),h.el=c,h.id=e++,h.$window=a(b),h.$document=a(document),h.$el.bind("destroyed",a.proxy(h.teardown,h)),h.$clonedHeader=null,h.$originalHeader=null,h.isSticky=!1,h.hasBeenSticky=!1,h.leftOffset=null,h.topOffset=null,h.init=function(){h.$el.each(function(){var b=a(this);b.css("padding",0),h.$originalHeader=a("thead:first",this),h.$clonedHeader=h.$originalHeader.clone(),b.trigger("clonedHeader."+d,[h.$clonedHeader]),h.$clonedHeader.addClass("tableFloatingHeader"),h.$clonedHeader.css("display","none"),h.$originalHeader.addClass("tableFloatingHeaderOriginal"),h.$originalHeader.after(h.$clonedHeader),h.$printStyle=a('<style type="text/css" media="print">.tableFloatingHeader{display:none !important;}.tableFloatingHeaderOriginal{position:static !important;}</style>'),a("head").append(h.$printStyle)}),h.setOptions(g),h.updateWidth(),h.toggleHeaders(),h.bind()},h.destroy=function(){h.$el.unbind("destroyed",h.teardown),h.teardown()},h.teardown=function(){h.isSticky&&h.$originalHeader.css("position","static"),a.removeData(h.el,"plugin_"+d),h.unbind(),h.$clonedHeader.remove(),h.$originalHeader.removeClass("tableFloatingHeaderOriginal"),h.$originalHeader.css("visibility","visible"),h.$printStyle.remove(),h.el=null,h.$el=null},h.bind=function(){h.$scrollableArea.on("scroll."+d,h.toggleHeaders),h.isWindowScrolling||(h.$window.on("scroll."+d+h.id,h.setPositionValues),h.$window.on("resize."+d+h.id,h.toggleHeaders)),h.$scrollableArea.on("resize."+d,h.toggleHeaders),h.$scrollableArea.on("resize."+d,h.updateWidth)},h.unbind=function(){h.$scrollableArea.off("."+d,h.toggleHeaders),h.isWindowScrolling||(h.$window.off("."+d+h.id,h.setPositionValues),h.$window.off("."+d+h.id,h.toggleHeaders)),h.$scrollableArea.off("."+d,h.updateWidth)},h.toggleHeaders=function(){h.$el&&h.$el.each(function(){var b,c=a(this),d=h.isWindowScrolling?isNaN(h.options.fixedOffset)?h.options.fixedOffset.outerHeight():h.options.fixedOffset:h.$scrollableArea.offset().top+(isNaN(h.options.fixedOffset)?0:h.options.fixedOffset),e=c.offset(),f=h.$scrollableArea.scrollTop()+d,g=h.$scrollableArea.scrollLeft(),i=h.isWindowScrolling?f>e.top:d>e.top,j=(h.isWindowScrolling?f:0)<e.top+c.height()-h.$clonedHeader.height()-(h.isWindowScrolling?0:d);i&&j?(b=e.left-g+h.options.leftOffset,h.$originalHeader.css({position:"fixed","margin-top":h.options.marginTop,left:b,"z-index":3}),h.leftOffset=b,h.topOffset=d,h.$clonedHeader.css("display",""),h.isSticky||(h.isSticky=!0,h.updateWidth()),h.setPositionValues()):h.isSticky&&(h.$originalHeader.css("position","static"),h.$clonedHeader.css("display","none"),h.isSticky=!1,h.resetWidth(a("td,th",h.$clonedHeader),a("td,th",h.$originalHeader)))})},h.setPositionValues=function(){var a=h.$window.scrollTop(),b=h.$window.scrollLeft();!h.isSticky||0>a||a+h.$window.height()>h.$document.height()||0>b||b+h.$window.width()>h.$document.width()||h.$originalHeader.css({top:h.topOffset-(h.isWindowScrolling?0:a),left:h.leftOffset-(h.isWindowScrolling?0:b)})},h.updateWidth=function(){if(h.isSticky){h.$originalHeaderCells||(h.$originalHeaderCells=a("th,td",h.$originalHeader)),h.$clonedHeaderCells||(h.$clonedHeaderCells=a("th,td",h.$clonedHeader));var b=h.getWidth(h.$clonedHeaderCells);h.setWidth(b,h.$clonedHeaderCells,h.$originalHeaderCells),h.$originalHeader.css("width",h.$clonedHeader.width())}},h.getWidth=function(c){var d=[];return c.each(function(c){var e,f=a(this);if("border-box"===f.css("box-sizing"))e=f[0].getBoundingClientRect().width;else{var g=a("th",h.$originalHeader);if("collapse"===g.css("border-collapse"))if(b.getComputedStyle)e=parseFloat(b.getComputedStyle(this,null).width);else{var i=parseFloat(f.css("padding-left")),j=parseFloat(f.css("padding-right")),k=parseFloat(f.css("border-width"));e=f.outerWidth()-i-j-k}else e=f.width()}d[c]=e}),d},h.setWidth=function(a,b,c){b.each(function(b){var d=a[b];c.eq(b).css({"min-width":d,"max-width":d})})},h.resetWidth=function(b,c){b.each(function(b){var d=a(this);c.eq(b).css({"min-width":d.css("min-width"),"max-width":d.css("max-width")})})},h.setOptions=function(c){h.options=a.extend({},f,c),h.$scrollableArea=a(h.options.scrollableArea),h.isWindowScrolling=h.$scrollableArea[0]===b},h.updateOptions=function(a){h.setOptions(a),h.unbind(),h.bind(),h.updateWidth(),h.toggleHeaders()},h.init()}var d="stickyTableHeaders",e=0,f={fixedOffset:0,leftOffset:0,marginTop:0,scrollableArea:b};a.fn[d]=function(b){return this.each(function(){var e=a.data(this,"plugin_"+d);e?"string"==typeof b?e[b].apply(e):e.updateOptions(b):"destroy"!==b&&a.data(this,"plugin_"+d,new c(this,b))})}}(jQuery,window);

View File

@ -1,85 +0,0 @@
$(function() {
$('.note').before("<p class='admonition-title note'>Note</p>");
$('.seealso').before("<p class='admonition-title seealso'>See also</p>");
$('.warning').before("<p class='admonition-title warning'>Warning</p>");
$('.caution').before("<p class='admonition-title caution'>Caution</p>");
$('.attention').before("<p class='admonition-title attention'>Attention</p>");
$('.tip').before("<p class='admonition-title tip'>Tip</p>");
$('.important').before("<p class='admonition-title important'>Important</p>");
$('.hint').before("<p class='admonition-title hint'>Hint</p>");
$('.error').before("<p class='admonition-title error'>Error</p>");
$('.danger').before("<p class='admonition-title danger'>Danger</p>");
});
$( document ).ready(function() {
// Shift nav in mobile when clicking the menu.
$(document).on('click', "[data-toggle='wy-nav-top']", function() {
$("[data-toggle='wy-nav-shift']").toggleClass("shift");
$("[data-toggle='rst-versions']").toggleClass("shift");
});
// Close menu when you click a link.
$(document).on('click', ".wy-menu-vertical .current ul li a", function() {
$("[data-toggle='wy-nav-shift']").removeClass("shift");
$("[data-toggle='rst-versions']").toggleClass("shift");
});
$(document).on('click', "[data-toggle='rst-current-version']", function() {
$("[data-toggle='rst-versions']").toggleClass("shift-up");
});
// Make tables responsive
$("table.docutils:not(.field-list)").wrap("<div class='wy-table-responsive'></div>");
});
$( document ).ready(function() {
$('#text-table-of-contents ul').first().addClass('nav');
// ScrollSpy also requires that we use
// a Bootstrap nav component.
$('body').scrollspy({target: '#text-table-of-contents'});
// add sticky table headers
$('table').stickyTableHeaders();
// set the height of tableOfContents
var $postamble = $('#postamble');
var $tableOfContents = $('#table-of-contents');
$tableOfContents.css({paddingBottom: $postamble.outerHeight()});
// add TOC button
var toggleSidebar = $('<div id="toggle-sidebar"><a href="#table-of-contents"><h2>Table of Contents</h2></a></div>');
$('#content').prepend(toggleSidebar);
// add close button when sidebar showed in mobile screen
var closeBtn = $('<a class="close-sidebar" href="#">Close</a>');
var tocTitle = $('#table-of-contents').find('h2');
tocTitle.append(closeBtn);
});
window.SphinxRtdTheme = (function (jquery) {
var stickyNav = (function () {
var navBar,
win,
stickyNavCssClass = 'stickynav',
applyStickNav = function () {
if (navBar.height() <= win.height()) {
navBar.addClass(stickyNavCssClass);
} else {
navBar.removeClass(stickyNavCssClass);
}
},
enable = function () {
applyStickNav();
win.on('resize', applyStickNav);
},
init = function () {
navBar = jquery('nav.wy-nav-side:first');
win = jquery(window);
};
jquery(init);
return {
enable : enable
};
}());
return {
StickyNav : stickyNav
};
}($));

File diff suppressed because one or more lines are too long

623
matlab/simscape_analysis.m Normal file
View File

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

View File

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