encoder-test-bench/index.org

27 KiB
Raw Blame History

Encoder - Test Bench

Experimental Setup

The experimental Setup is schematically represented in Figure fig:exp_setup_schematic.

The mass can be vertically moved using the amplified piezoelectric actuator. The displacement of the mass (relative to the mechanical frame) is measured both by the interferometer and by the encoder.

/tdehaeze/encoder-test-bench/media/commit/093e4fd1e9ed8ada460d0b4de674962bd203e156/figs/exp_setup_schematic.png
Schematic of the Experiment
/tdehaeze/encoder-test-bench/media/commit/093e4fd1e9ed8ada460d0b4de674962bd203e156/figs/IMG_20201023_153905.jpg
Side View of the encoder
/tdehaeze/encoder-test-bench/media/commit/093e4fd1e9ed8ada460d0b4de674962bd203e156/figs/IMG_20201023_153914.jpg
Front View of the encoder

Huddle Test

Introduction   ignore

The goal in this section is the estimate the noise of both the encoder and the intereferometer.

Nothing is then to the actuator such that the relative motion between the mass and the frame is as small as possible. Ideally, a mechanical part would clamp the two together, we here suppose that the APA is still enough to clamp the two together.

Load Data

  load('mat/int_enc_huddle_test.mat', 'interferometer', 'encoder', 't');
  interferometer = detrend(interferometer, 0);
  encoder = detrend(encoder, 0);

Time Domain Results

/tdehaeze/encoder-test-bench/media/commit/093e4fd1e9ed8ada460d0b4de674962bd203e156/figs/huddle_test_time_domain.png

Huddle test - Time domain signals
  G_lpf = 1/(1 + s/2/pi/10);

/tdehaeze/encoder-test-bench/media/commit/093e4fd1e9ed8ada460d0b4de674962bd203e156/figs/huddle_test_time_domain_filtered.png

Huddle test - Time domain signals filtered with a LPF at 10Hz

Frequency Domain Noise

  Ts = 1e-4;
  win = hann(ceil(10/Ts));

  [p_i, f] = pwelch(interferometer, win, [], [], 1/Ts);
  [p_e, ~] = pwelch(encoder,        win, [], [], 1/Ts);

/tdehaeze/encoder-test-bench/media/commit/093e4fd1e9ed8ada460d0b4de674962bd203e156/figs/huddle_test_asd.png

Amplitude Spectral Density of the signals during the Huddle test

Comparison Interferometer / Encoder

Introduction   ignore

The goal here is to make sure that the interferometer and encoder measurements are coherent. We may see non-linearity in the interferometric measurement.

Load Data

  load('mat/int_enc_comp.mat', 'interferometer', 'encoder', 'u', 't');
  interferometer = detrend(interferometer, 0);
  encoder = detrend(encoder, 0);
  u = detrend(u, 0);

Time Domain Results

/tdehaeze/encoder-test-bench/media/commit/093e4fd1e9ed8ada460d0b4de674962bd203e156/figs/int_enc_one_cycle.png

One cycle measurement

/tdehaeze/encoder-test-bench/media/commit/093e4fd1e9ed8ada460d0b4de674962bd203e156/figs/int_enc_one_cycle_error.png

Difference between the Encoder and the interferometer during one cycle

Difference between Encoder and Interferometer as a function of time

  Ts = 1e-4;
  d_i_mean = reshape(interferometer, [2/Ts floor(Ts/2*length(interferometer))]);
  d_e_mean = reshape(encoder,        [2/Ts floor(Ts/2*length(encoder))]);
  w0 = 2*pi*5; % [rad/s]
  xi = 0.7;

  G_lpf = 1/(1 + 2*xi/w0*s + s^2/w0^2);

  d_err_mean = reshape(lsim(G_lpf, encoder - interferometer, t), [2/Ts floor(Ts/2*length(encoder))]);
  d_err_mean = d_err_mean - mean(d_err_mean);

/tdehaeze/encoder-test-bench/media/commit/093e4fd1e9ed8ada460d0b4de674962bd203e156/figs/int_enc_error_mean_time.png

Difference between the two measurement in the time domain, averaged for all the cycles

Difference between Encoder and Interferometer as a function of position

Compute the mean of the interferometer measurement corresponding to each of the encoder measurement.

  [e_sorted, ~, e_ind] = unique(encoder);

  i_mean = zeros(length(e_sorted), 1);
  for i = 1:length(e_sorted)
    i_mean(i) = mean(interferometer(e_ind == i));
  end

  i_mean_error = (i_mean - e_sorted);

/tdehaeze/encoder-test-bench/media/commit/093e4fd1e9ed8ada460d0b4de674962bd203e156/figs/int_enc_error_mean_position.png

Difference between the two measurement as a function of the measured position by the encoder, averaged for all the cycles

The period of the non-linearity seems to be $1.53 \mu m$ which corresponds to the wavelength of the Laser.

  win_length = 1530; % length of the windows (corresponds to 1.53 um)
  num_avg = floor(length(e_sorted)/win_length); % number of averaging

  i_init = ceil((length(e_sorted) - win_length*num_avg)/2); % does not start at the extremity

  e_sorted_mean_over_period = mean(reshape(i_mean_error(i_init:i_init+win_length*num_avg-1), [win_length num_avg]), 2);

/tdehaeze/encoder-test-bench/media/commit/093e4fd1e9ed8ada460d0b4de674962bd203e156/figs/int_non_linearity_period_wavelength.png

Non-Linearity of the Interferometer over the period of the wavelength

Identification

Load Data

  load('mat/int_enc_id_noise_bis.mat', 'interferometer', 'encoder', 'u', 't');
  interferometer = detrend(interferometer, 0);
  encoder = detrend(encoder, 0);
  u = detrend(u, 0);

Identification

  Ts = 1e-4; % Sampling Time [s]
  win = hann(ceil(10/Ts));
  [tf_i_est, f] = tfestimate(u, interferometer, win, [], [], 1/Ts);
  [co_i_est, ~] = mscohere(u, interferometer, win, [], [], 1/Ts);

  [tf_e_est, ~] = tfestimate(u, encoder, win, [], [], 1/Ts);
  [co_e_est, ~] = mscohere(u, encoder, win, [], [], 1/Ts);

/tdehaeze/encoder-test-bench/media/commit/093e4fd1e9ed8ada460d0b4de674962bd203e156/figs/identification_dynamics_coherence.png

/tdehaeze/encoder-test-bench/media/commit/093e4fd1e9ed8ada460d0b4de674962bd203e156/figs/identification_dynamics_bode.png

Change of Stiffness due to Sensors stack being open/closed circuit

Load Data

  oc = load('./mat/identification_open_circuit.mat', 't', 'encoder', 'u');
  sc = load('./mat/identification_short_circuit.mat', 't', 'encoder', 'u');

Transfer Functions

  Ts = 1e-4; % Sampling Time [s]
  win = hann(ceil(10/Ts));
  [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);

/tdehaeze/encoder-test-bench/media/commit/093e4fd1e9ed8ada460d0b4de674962bd203e156/figs/stiffness_force_sensor_coherence.png

/tdehaeze/encoder-test-bench/media/commit/093e4fd1e9ed8ada460d0b4de674962bd203e156/figs/stiffness_force_sensor_bode.png

/tdehaeze/encoder-test-bench/media/commit/093e4fd1e9ed8ada460d0b4de674962bd203e156/figs/stiffness_force_sensor_bode_zoom.png

Zoom on the change of resonance

The change of resonance frequency / stiffness is very small and is not important here.

Generated Number of Charge / Voltage

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).

Steps

  load('./mat/force_sensor_steps.mat', 't', 'encoder', 'u', 'v');
  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]');

/tdehaeze/encoder-test-bench/media/commit/093e4fd1e9ed8ada460d0b4de674962bd203e156/figs/force_sen_steps_time_domain.png

Time domain signal during the 3 actuator voltage steps

Three steps are performed at the following time intervals:

  t_s = [ 2.5, 23;
         23.8, 35;
         35.8, 50];

Fit function:

  f = @(b,x) b(1).*exp(b(2).*x) + b(3);

We are interested by the b(2) term, which is the time constant of the exponential.

  tau = zeros(size(t_s, 1),1);
  V0  = zeros(size(t_s, 1),1);
  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
$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:

  Cp = 4.4e-6; % [F]
  Rin = abs(mean(tau))/Cp;
1494100.0

The input impedance of the Speedgoat's ADC should then be close to $1.5\,M\Omega$ (specified at $1\,M\Omega$).

How can we explain the voltage offset?

As shown in Figure fig:piezo_sensor_model_instrumentation (taken from cite:reza06_piezoel_trans_vibrat_contr_dampin), an input voltage offset is due to the input bias current $i_n$.

/tdehaeze/encoder-test-bench/media/commit/093e4fd1e9ed8ada460d0b4de674962bd203e156/figs/piezo_sensor_model_instrumentation.png
Model of a piezoelectric transducer (left) and instrumentation amplifier (right)

The estimated input bias current is then:

  in = mean(V0)/Rin;
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:

  fc = 3;
  Ra = Rin/(fc*Cp*Rin - 1);
79804

With this parallel resistance value, the voltage offset would be:

  V_offset = Ra*Rin/(Ra + Rin) * in;
0.11454

Which is much more acceptable.

Add Parallel Resistor

A resistor of $\approx 100\,k\Omega$ is added in parallel with the force sensor and the same kin.

  load('./mat/force_sensor_steps_R_82k7.mat', 't', 'encoder', 'u', 'v');
  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]');

/tdehaeze/encoder-test-bench/media/commit/093e4fd1e9ed8ada460d0b4de674962bd203e156/figs/force_sen_steps_time_domain_par_R.png

Time domain signal during the actuator voltage steps

Three steps are performed at the following time intervals:

  t_s = [1.9,  6;
         8.5, 13;
        15.5, 21;
        22.6, 26;
        30.0, 36;
        37.5, 41;
        46.2, 49.5]

Fit function:

  f = @(b,x) b(1).*exp(b(2).*x) + b(3);

We are interested by the b(2) term, which is the time constant of the exponential.

  tau = zeros(size(t_s, 1),1);
  V0  = zeros(size(t_s, 1),1);
  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

And indeed, we obtain a much smaller offset voltage and a much faster time constant.

$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$):

  Cp = 4.4e-6; % [F]
  Rin = abs(mean(tau))/Cp;
101200.0

And we can verify that the bias current estimation stays the same:

  in = mean(V0)/Rin;
1.5305e-06

This validates the model of the ADC and the effectiveness of the added resistor.

Sinus

  load('./mat/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);

The driving voltage is a sinus at 0.5Hz centered on 3V and with an amplitude of 3V (Figure fig:force_sensor_sin_u).

/tdehaeze/encoder-test-bench/media/commit/093e4fd1e9ed8ada460d0b4de674962bd203e156/figs/force_sensor_sin_u.png

Driving Voltage

The full stroke as measured by the encoder is:

  max(encoder)-min(encoder)
5.005e-05

Its signal is shown in Figure fig:force_sensor_sin_encoder.

/tdehaeze/encoder-test-bench/media/commit/093e4fd1e9ed8ada460d0b4de674962bd203e156/figs/force_sensor_sin_encoder.png

Encoder measurement

The generated voltage by the stack is shown in Figure

/tdehaeze/encoder-test-bench/media/commit/093e4fd1e9ed8ada460d0b4de674962bd203e156/figs/force_sensor_sin_stack.png

Voltage measured on the stack used as a sensor

The capacitance of the stack is

  Cp = 4.4e-6; % [F]

The corresponding generated charge is then shown in Figure fig:force_sensor_sin_charge.

/tdehaeze/encoder-test-bench/media/commit/093e4fd1e9ed8ada460d0b4de674962bd203e156/figs/force_sensor_sin_charge.png

Generated Charge

The relation between the generated voltage and the measured displacement is almost linear as shown in Figure fig:force_sensor_linear_relation.

  b1 = encoder\(v-mean(v));

/tdehaeze/encoder-test-bench/media/commit/093e4fd1e9ed8ada460d0b4de674962bd203e156/figs/force_sensor_linear_relation.png

Almost linear relation between the relative displacement and the generated voltage

With a 16bits ADC, the resolution will then be equals to (in [nm]):

  abs((20/2^16)/(b1/1e9))
3.9838