2. Pulse Baseline Drift Correction Before Kinematic Integration

Why Baseline Drift Corrupts Kinematic Reconstruction

An accelerometer channel rarely returns a pulse that sits exactly on zero before impact. Thermal offset, amplifier bias, ADC quantization, and residual sensor bias all contribute a small constant or slowly varying component. When you integrate acceleration once to obtain velocity, any constant offset a_bias becomes a linear ramp a_bias · t; integrate again for displacement and the error grows quadratically. A bias of only 0.5 m/s² held across a 100 ms integration window already injects 0.05 m/s of spurious velocity — comparable to the resolution you need when judging occupant kinematics. The task in this case is therefore not to "clean up" the pulse cosmetically, but to remove the drift component before integration and then prove, against an independent reference velocity, how much kinematic error the drift would have caused.

Signal Chain and the Two Correction Stages

The correction pipeline has two distinct stages that must not be conflated. Pre-trigger zeroing estimates the static offset from a window that precedes the crash event (where true acceleration is nominally zero) and subtracts it. High-pass filtering then removes any residual low-frequency wander that survives zeroing — for example a slow thermal ramp that changes during the event itself. Zeroing handles the DC term; the high-pass handles the drift term. Applying only one of them leaves a characteristic residual that the reference-velocity comparison will expose.

Raw pulse a(t) offset + drift + true event Pre-trigger zeroing a_bias = mean(t < t0) a'(t) = a(t) − a_bias High-pass filter fc ≈ 0.5–2 Hz removes residual drift Integrate v(t) = ∫ a_c(t) dt trapezoidal Reference v_ref independent source (e.g. sled / GPS) Error metric Δv = v_int − v_ref RMS + peak Accept / reject |Δv|_peak ≤ tol ? tol e.g. 0.1 m/s Residual drift if zeroing only: ramp in v(t) Zeroing removes DC offset · High-pass removes slow wander · Reference comparison quantifies residual kinematic error Order matters: zero first (static estimate), then filter (dynamic removal), then integrate

Step 1 — Estimate and Remove the Pre-Trigger Offset

Define the pre-trigger window as the samples strictly before the event onset. In practice a 50–100 ms window ending 5–10 ms before the first threshold crossing is robust; the guard gap avoids contaminating the estimate with the rising edge of the pulse. The offset estimate is the arithmetic mean of that window, and the corrected signal is the raw signal minus that mean. The following Python fragment is self-contained and operates on a synthetic pulse so the whole pipeline can be exercised without external data.

import numpy as np

# --- synthetic teaching pulse (example data, not a measured record) ---
fs = 10000.0                      # 10 kHz sample rate
t = np.arange(0, 0.20, 1/fs)      # 200 ms window
t0 = 0.05                         # nominal event onset (s)

# true event: half-sine pulse, 40 ms duration, 300 m/s^2 peak
dur, peak = 0.040, 300.0
a_true = np.where((t >= t0) & (t < t0 + dur),
                  peak * np.sin(np.pi * (t - t0) / dur), 0.0)

# injected defects: constant bias + slow thermal drift
a_bias = 0.8                      # m/s^2
a_drift = 1.5 * (t - t0)          # m/s^2, linear ramp
a_raw = a_true + a_bias + a_drift

# --- pre-trigger zeroing ---
pre_mask = (t >= t0 - 0.080) & (t < t0 - 0.005)
a_bias_est = a_raw[pre_mask].mean()
a_zeroed = a_raw - a_bias_est
print(f"estimated bias = {a_bias_est:.3f} m/s^2 (true {a_bias:.3f})")

With the example parameters the estimate lands close to 0.8 m/s² plus the small average of the drift over the pre-trigger window; the residual is what the high-pass stage must handle. If the estimate is far from the expected bias — say several m/s² — suspect a mis-scaled channel or a pre-trigger window that overlaps the event, and re-check the onset time before proceeding.

Step 2 — High-Pass Filter the Zeroed Signal

A first-order Butterworth high-pass at 0.5–2 Hz is sufficient for crash pulses whose meaningful content sits well above 5 Hz. Use a zero-phase implementation (filtfilt) so the filter does not introduce a group delay that would later masquerade as a timing error. The cutoff must be low enough to preserve the pulse shape and high enough to reject the drift ramp; 1 Hz is a reasonable default for a 40–100 ms event.

from scipy.signal import butter, filtfilt

fc = 1.0                          # Hz
b, a = butter(1, fc / (fs / 2), btype='high')
a_corr = filtfilt(b, a, a_zeroed)

# --- integrate to velocity (trapezoidal) ---
v_int = np.concatenate(([0.0], np.cumsum(0.5*(a_corr[1:]+a_corr[:-1])/fs)))

Step 3 — Compare Against Reference Velocity

The reference velocity is an independent measurement of the same event — a sled encoder, a differentiated displacement channel, or a validated GPS/IMU fusion. Compute the error time series Δv(t) = v_int(t) − v_ref(t) and report both its RMS and its peak magnitude over the event window. The peak is the metric that matters for occupant kinematics, because a large transient error at the moment of peak occupant excursion will bias any subsequent displacement estimate.

Pipeline variantPeak |Δv| (m/s)RMS Δv (m/s)Interpretation
Raw (no correction)≈ 0.09≈ 0.05Bias ramp dominates; unusable for kinematics
Zeroing only≈ 0.03≈ 0.02DC removed, drift ramp remains
Zeroing + 1 Hz HP≈ 0.01≈ 0.006Within typical 0.1 m/s tolerance

The values above are illustrative outputs of the example parameters, not measured results; run the pipeline on your own channel and substitute the actual numbers. The pattern to look for is monotonic reduction in both metrics as each stage is added. If the high-pass stage increases peak error, the cutoff is too high and is clipping genuine low-frequency pulse content — lower fc and re-check.

Always keep the uncorrected, zeroed-only, and fully corrected velocity traces side by side. The difference between them is the drift-induced kinematic error you are quantifying, and it is the evidence that justifies the correction in a diagnostic report.
Do not apply the high-pass before zeroing. A high-pass filter will itself remove the DC component, but it does so with a transient response that depends on the initial offset; the resulting velocity can show a startup artifact that is easily mistaken for a real pre-impact motion. Zero first, filter second.

Verification Checklist

  • Pre-trigger window ends at least 5 ms before the first threshold crossing and contains no event energy.
  • Estimated bias is within a plausible range for the channel (typically < 2 m/s² for a calibrated accelerometer).
  • High-pass cutoff is at least 5× below the dominant pulse frequency content.
  • Zero-phase filtering is used so no group delay is introduced.
  • Peak and RMS Δv are reported for all three pipeline variants, and the fully corrected variant meets the agreed tolerance (e.g. 0.1 m/s peak).
  • Any residual ramp visible in the corrected velocity trace is documented as a known limitation rather than silently accepted.