test-bench-force-sensor/index.org
2020-11-10 13:00:07 +01:00

567 lines
16 KiB
Org Mode
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#+TITLE: Piezoelectric Force Sensor - Test Bench
:DRAWER:
#+LANGUAGE: en
#+EMAIL: dehaeze.thomas@gmail.com
#+AUTHOR: Dehaeze Thomas
#+HTML_LINK_HOME: ../index.html
#+HTML_LINK_UP: ../index.html
#+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: <link rel="stylesheet" type="text/css" href="./css/custom.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/readtheorg.js"></script>
#+PROPERTY: header-args:latex :headers '("\\usepackage{tikz}" "\\usepackage{import}" "\\import{$HOME/Cloud/tikz/org/}{config.tex}")
#+PROPERTY: header-args:latex+ :imagemagick t :fit yes
#+PROPERTY: header-args:latex+ :iminoptions -scale 100% -density 150
#+PROPERTY: header-args:latex+ :imoutoptions -quality 100
#+PROPERTY: header-args:latex+ :results raw replace :buffer no
#+PROPERTY: header-args:latex+ :eval no-export
#+PROPERTY: header-args:latex+ :exports both
#+PROPERTY: header-args:latex+ :mkdirp yes
#+PROPERTY: header-args:latex+ :output-dir figs
#+PROPERTY: header-args:latex+ :post pdf2svg(file=*this*, ext="png")
#+PROPERTY: header-args:matlab :session *MATLAB*
#+PROPERTY: header-args:matlab+ :comments org
#+PROPERTY: header-args:matlab+ :exports both
#+PROPERTY: header-args:matlab+ :results none
#+PROPERTY: header-args:matlab+ :eval no-export
#+PROPERTY: header-args:matlab+ :noweb yes
#+PROPERTY: header-args:matlab+ :mkdirp yes
#+PROPERTY: header-args:matlab+ :output-dir figs
:END:
* Introduction :ignore:
In this document is studied how a piezoelectric stack can be used to measured the force.
- Section [[sec:open_closed_circuit]]: the effect of the input impedance of the electronics connected to the force sensor stack on the stiffness of the stack is studied
- Section [[sec:charge_voltage_estimation]]:
* Change of Stiffness due to Sensors stack being open/closed circuit
:PROPERTIES:
:header-args:matlab+: :tangle matlab/open_closed_circuit.m
:END:
<<sec:open_closed_circuit>>
** Introduction :ignore:
** Matlab Init :noexport:ignore:
#+begin_src matlab :tangle no :exports none :results silent :noweb yes :var current_dir=(file-name-directory buffer-file-name)
<<matlab-dir>>
#+end_src
#+begin_src matlab :exports none :results silent :noweb yes
<<matlab-init>>
#+end_src
#+begin_src matlab :tangle no
addpath('./matlab/mat/');
#+end_src
#+begin_src matlab :eval no
addpath('./mat/');
#+end_src
** Load Data
#+begin_src matlab
oc = load('identification_open_circuit.mat', 't', 'encoder', 'u');
sc = load('identification_short_circuit.mat', 't', 'encoder', 'u');
#+end_src
** Transfer Functions
#+begin_src matlab
Ts = 1e-4; % Sampling Time [s]
win = hann(ceil(10/Ts));
#+end_src
#+begin_src matlab
[tf_oc_est, f] = tfestimate(oc.u, oc.encoder, win, [], [], 1/Ts);
[co_oc_est, ~] = mscohere( oc.u, oc.encoder, win, [], [], 1/Ts);
[tf_sc_est, ~] = tfestimate(sc.u, sc.encoder, win, [], [], 1/Ts);
[co_sc_est, ~] = mscohere( sc.u, sc.encoder, win, [], [], 1/Ts);
#+end_src
#+begin_src matlab :exports none
figure;
hold on;
plot(f, co_oc_est, '-')
plot(f, co_sc_est, '-')
set(gca, 'Xscale', 'log'); set(gca, 'Yscale', 'lin');
ylabel('Coherence'); xlabel('Frequency [Hz]');
hold off;
xlim([0.5, 5e3]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/stiffness_force_sensor_coherence.pdf', 'width', 'wide', 'height', 'normal');
#+end_src
#+name: fig:stiffness_force_sensor_coherence
#+caption:
#+RESULTS:
[[file:figs/stiffness_force_sensor_coherence.png]]
#+begin_src matlab :exports none
figure;
tiledlayout(2, 1, 'TileSpacing', 'None', 'Padding', 'None');
ax1 = nexttile;
hold on;
plot(f, abs(tf_oc_est), '-', 'DisplayName', 'Open-Circuit')
plot(f, abs(tf_sc_est), '-', 'DisplayName', 'Short-Circuit')
set(gca, 'Xscale', 'log'); set(gca, 'Yscale', 'log');
ylabel('Amplitude'); set(gca, 'XTickLabel',[]);
hold off;
ylim([1e-7, 3e-4]);
legend('location', 'southwest');
ax2 = nexttile;
hold on;
plot(f, 180/pi*angle(tf_oc_est), '-')
plot(f, 180/pi*angle(tf_sc_est), '-')
set(gca, 'Xscale', 'log'); set(gca, 'Yscale', 'lin');
ylabel('Phase'); xlabel('Frequency [Hz]');
hold off;
yticks(-360:90:360);
axis padded 'auto x'
linkaxes([ax1,ax2], 'x');
xlim([0.5, 5e3]);
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/stiffness_force_sensor_bode.pdf', 'width', 'wide', 'height', 'tall');
#+end_src
#+name: fig:stiffness_force_sensor_bode
#+caption:
#+RESULTS:
[[file:figs/stiffness_force_sensor_bode.png]]
#+begin_src matlab :tangle no :exports results :results file replace
xlim([180, 280]);
exportFig('figs/stiffness_force_sensor_bode_zoom.pdf', 'width', 'small', 'height', 'tall');
#+end_src
#+name: fig:stiffness_force_sensor_bode_zoom
#+caption: Zoom on the change of resonance
#+RESULTS:
[[file:figs/stiffness_force_sensor_bode_zoom.png]]
#+begin_important
The change of resonance frequency / stiffness is very small and is not important here.
#+end_important
* Generated Number of Charge / Voltage
:PROPERTIES:
:header-args:matlab+: :tangle matlab/charge_voltage_estimation.m
:END:
<<sec:charge_voltage_estimation>>
** Introduction :ignore:
Two stacks are used as actuator (in parallel) and one stack is used as sensor.
The amplifier gain is 20V/V (Cedrat LA75B).
** Matlab Init :noexport:ignore:
#+begin_src matlab :tangle no :exports none :results silent :noweb yes :var current_dir=(file-name-directory buffer-file-name)
<<matlab-dir>>
#+end_src
#+begin_src matlab :exports none :results silent :noweb yes
<<matlab-init>>
#+end_src
#+begin_src matlab :tangle no
addpath('./matlab/mat/');
#+end_src
#+begin_src matlab :eval no
addpath('./mat/');
#+end_src
** Steps
#+begin_src matlab
load('force_sensor_steps.mat', 't', 'encoder', 'u', 'v');
#+end_src
#+begin_src matlab
figure;
tiledlayout(2, 1, 'TileSpacing', 'None', 'Padding', 'None');
nexttile;
plot(t, v);
xlabel('Time [s]'); ylabel('Measured voltage [V]');
nexttile;
plot(t, u);
xlabel('Time [s]'); ylabel('Actuator Voltage [V]');
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/force_sen_steps_time_domain.pdf', 'width', 'wide', 'height', 'tall');
#+end_src
#+name: fig:force_sen_steps_time_domain
#+caption: Time domain signal during the 3 actuator voltage steps
#+RESULTS:
[[file:figs/force_sen_steps_time_domain.png]]
Three steps are performed at the following time intervals:
#+begin_src matlab
t_s = [ 2.5, 23;
23.8, 35;
35.8, 50];
#+end_src
Fit function:
#+begin_src matlab
f = @(b,x) b(1).*exp(b(2).*x) + b(3);
#+end_src
We are interested by the =b(2)= term, which is the time constant of the exponential.
#+begin_src matlab
tau = zeros(size(t_s, 1),1);
V0 = zeros(size(t_s, 1),1);
#+end_src
#+begin_src matlab
for t_i = 1:size(t_s, 1)
t_cur = t(t_s(t_i, 1) < t & t < t_s(t_i, 2));
t_cur = t_cur - t_cur(1);
y_cur = v(t_s(t_i, 1) < t & t < t_s(t_i, 2));
nrmrsd = @(b) norm(y_cur - f(b,t_cur)); % Residual Norm Cost Function
B0 = [0.5, -0.15, 2.2]; % Choose Appropriate Initial Estimates
[B,rnrm] = fminsearch(nrmrsd, B0); % Estimate Parameters B
tau(t_i) = 1/B(2);
V0(t_i) = B(3);
end
#+end_src
#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*)
data2orgtable([abs(tau), V0], {}, {'$tau$ [s]', '$V_0$ [V]'}, ' %.2f ');
#+end_src
#+RESULTS:
| $tau$ [s] | $V_0$ [V] |
|-----------+-----------|
| 6.47 | 2.26 |
| 6.76 | 2.26 |
| 6.49 | 2.25 |
With the capacitance being $C = 4.4 \mu F$, the internal impedance of the Speedgoat ADC can be computed as follows:
#+begin_src matlab
Cp = 4.4e-6; % [F]
Rin = abs(mean(tau))/Cp;
#+end_src
#+begin_src matlab :results value replace :exports results
ans = Rin
#+end_src
#+RESULTS:
: 1494100.0
The input impedance of the Speedgoat's ADC should then be close to $1.5\,M\Omega$ (specified at $1\,M\Omega$).
#+begin_important
How can we explain the voltage offset?
#+end_important
As shown in Figure [[fig:force_sensor_model_electronics_without_R]] (taken from cite:reza06_piezoel_trans_vibrat_contr_dampin), an input voltage offset is due to the input bias current $i_n$.
#+name: fig:force_sensor_model_electronics_without_R
#+caption: Model of a piezoelectric transducer (left) and instrumentation amplifier (right)
[[file:figs/force_sensor_model_electronics_without_R.png]]
The estimated input bias current is then:
#+begin_src matlab
in = mean(V0)/Rin;
#+end_src
#+begin_src matlab :results value replace :exports results
ans = in
#+end_src
#+RESULTS:
: 1.5119e-06
An additional resistor in parallel with $R_{in}$ would have two effects:
- reduce the input voltage offset
\[ V_{off} = \frac{R_a R_{in}}{R_a + R_{in}} i_n \]
- increase the high pass corner frequency $f_c$
\[ C_p \frac{R_{in}R_a}{R_{in} + R_a} = \tau_c = \frac{1}{f_c} \]
\[ R_a = \frac{R_i}{f_c C_p R_i - 1} \]
If we allow the high pass corner frequency to be equals to 3Hz:
#+begin_src matlab
fc = 3;
Ra = Rin/(fc*Cp*Rin - 1);
#+end_src
#+begin_src matlab :results value replace :exports results
ans = Ra
#+end_src
#+RESULTS:
: 79804
With this parallel resistance value, the voltage offset would be:
#+begin_src matlab
V_offset = Ra*Rin/(Ra + Rin) * in;
#+end_src
#+begin_src matlab :results value replace :exports results
ans = V_offset
#+end_src
#+RESULTS:
: 0.11454
Which is much more acceptable.
** Add Parallel Resistor
A resistor $R_p \approx 100\,k\Omega$ is added in parallel with the force sensor as shown in Figure [[fig:force_sensor_model_electronics]].
#+name: fig:force_sensor_model_electronics
#+caption: Model of a piezoelectric transducer (left) and instrumentation amplifier (right) with the additional resistor $R_p$
[[file:figs/force_sensor_model_electronics.png]]
#+begin_src matlab
load('force_sensor_steps_R_82k7.mat', 't', 'encoder', 'u', 'v');
#+end_src
#+begin_src matlab
figure;
tiledlayout(2, 1, 'TileSpacing', 'None', 'Padding', 'None');
nexttile;
plot(t, v);
xlabel('Time [s]'); ylabel('Measured voltage [V]');
nexttile;
plot(t, u);
xlabel('Time [s]'); ylabel('Actuator Voltage [V]');
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/force_sen_steps_time_domain_par_R.pdf', 'width', 'wide', 'height', 'tall');
#+end_src
#+name: fig:force_sen_steps_time_domain_par_R
#+caption: Time domain signal during the actuator voltage steps
#+RESULTS:
[[file:figs/force_sen_steps_time_domain_par_R.png]]
Three steps are performed at the following time intervals:
#+begin_src matlab
t_s = [1.9, 6;
8.5, 13;
15.5, 21;
22.6, 26;
30.0, 36;
37.5, 41;
46.2, 49.5]
#+end_src
Fit function:
#+begin_src matlab
f = @(b,x) b(1).*exp(b(2).*x) + b(3);
#+end_src
We are interested by the =b(2)= term, which is the time constant of the exponential.
#+begin_src matlab
tau = zeros(size(t_s, 1),1);
V0 = zeros(size(t_s, 1),1);
#+end_src
#+begin_src matlab
for t_i = 1:size(t_s, 1)
t_cur = t(t_s(t_i, 1) < t & t < t_s(t_i, 2));
t_cur = t_cur - t_cur(1);
y_cur = v(t_s(t_i, 1) < t & t < t_s(t_i, 2));
nrmrsd = @(b) norm(y_cur - f(b,t_cur)); % Residual Norm Cost Function
B0 = [0.5, -0.2, 0.2]; % Choose Appropriate Initial Estimates
[B,rnrm] = fminsearch(nrmrsd, B0); % Estimate Parameters B
tau(t_i) = 1/B(2);
V0(t_i) = B(3);
end
#+end_src
And indeed, we obtain a much smaller offset voltage and a much faster time constant.
#+begin_src matlab :exports results :results value table replace :tangle no :post addhdr(*this*)
data2orgtable([abs(tau), V0], {}, {'$tau$ [s]', '$V_0$ [V]'}, ' %.2f ');
#+end_src
#+RESULTS:
| $tau$ [s] | $V_0$ [V] |
|-----------+-----------|
| 0.43 | 0.15 |
| 0.45 | 0.16 |
| 0.43 | 0.15 |
| 0.43 | 0.15 |
| 0.45 | 0.15 |
| 0.46 | 0.16 |
| 0.48 | 0.16 |
Knowing the capacitance value, we can estimate the value of the added resistor (neglecting the input impedance of $\approx 1\,M\Omega$):
#+begin_src matlab
Cp = 4.4e-6; % [F]
Rin = abs(mean(tau))/Cp;
#+end_src
#+begin_src matlab :results value replace :exports results
ans = Rin
#+end_src
#+RESULTS:
: 101200.0
And we can verify that the bias current estimation stays the same:
#+begin_src matlab
in = mean(V0)/Rin;
#+end_src
#+begin_src matlab :results value replace :exports results
ans = in
#+end_src
#+RESULTS:
: 1.5305e-06
This validates the model of the ADC and the effectiveness of the added resistor.
** Sinus
#+begin_src matlab
load('force_sensor_sin.mat', 't', 'encoder', 'u', 'v');
u = u(t>25);
v = v(t>25);
encoder = encoder(t>25) - mean(encoder(t>25));
t = t(t>25);
#+end_src
The driving voltage is a sinus at 0.5Hz centered on 3V and with an amplitude of 3V (Figure [[fig:force_sensor_sin_u]]).
#+begin_src matlab :exports none
figure;
plot(t, u)
xlabel('Time [s]'); ylabel('Control Voltage [V]');
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/force_sensor_sin_u.pdf', 'width', 'normal', 'height', 'small');
#+end_src
#+name: fig:force_sensor_sin_u
#+caption: Driving Voltage
#+RESULTS:
[[file:figs/force_sensor_sin_u.png]]
The full stroke as measured by the encoder is:
#+begin_src matlab :results value replace
max(encoder)-min(encoder)
#+end_src
#+RESULTS:
: 5.005e-05
Its signal is shown in Figure [[fig:force_sensor_sin_encoder]].
#+begin_src matlab :exports none
figure;
plot(t, encoder)
xlabel('Time [s]'); ylabel('Encoder [m]');
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/force_sensor_sin_encoder.pdf', 'width', 'normal', 'height', 'small');
#+end_src
#+name: fig:force_sensor_sin_encoder
#+caption: Encoder measurement
#+RESULTS:
[[file:figs/force_sensor_sin_encoder.png]]
The generated voltage by the stack is shown in Figure
#+begin_src matlab :exports none
figure;
plot(t, v)
xlabel('Time [s]'); ylabel('Force Sensor Output [V]');
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/force_sensor_sin_stack.pdf', 'width', 'normal', 'height', 'small');
#+end_src
#+name: fig:force_sensor_sin_stack
#+caption: Voltage measured on the stack used as a sensor
#+RESULTS:
[[file:figs/force_sensor_sin_stack.png]]
The capacitance of the stack is
#+begin_src matlab
Cp = 4.4e-6; % [F]
#+end_src
The corresponding generated charge is then shown in Figure [[fig:force_sensor_sin_charge]].
#+begin_src matlab :exports none
figure;
plot(t, 1e6*Cp*(v-mean(v)))
xlabel('Time [s]'); ylabel('Generated Charge [$\mu C$]');
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/force_sensor_sin_charge.pdf', 'width', 'normal', 'height', 'small');
#+end_src
#+name: fig:force_sensor_sin_charge
#+caption: Generated Charge
#+RESULTS:
[[file:figs/force_sensor_sin_charge.png]]
The relation between the generated voltage and the measured displacement is almost linear as shown in Figure [[fig:force_sensor_linear_relation]].
#+begin_src matlab
b1 = encoder\(v-mean(v));
#+end_src
#+begin_src matlab :exports none
figure;
hold on;
plot(encoder, v-mean(v), 'DisplayName', 'Measured Voltage');
plot(encoder, encoder*b1, 'DisplayName', sprintf('Linear Fit: $U_s \\approx %.3f [V/\\mu m] \\cdot d$', 1e-6*abs(b1)));
hold off;
xlabel('Measured Displacement [m]'); ylabel('Generated Voltage [V]');
legend();
#+end_src
#+begin_src matlab :tangle no :exports results :results file replace
exportFig('figs/force_sensor_linear_relation.pdf', 'width', 'normal', 'height', 'small');
#+end_src
#+name: fig:force_sensor_linear_relation
#+caption: Almost linear relation between the relative displacement and the generated voltage
#+RESULTS:
[[file:figs/force_sensor_linear_relation.png]]
With a 16bits ADC, the resolution will then be equals to (in [nm]):
#+begin_src matlab :results value replace
abs((20/2^16)/(b1/1e9))
#+end_src
#+RESULTS:
: 3.9838