1. Course overview

Course overview 学习卡片

Crash safety engineering depends on a small set of scalar metrics that compress a complex, high-rate physical event into numbers a regulator, a program manager, or a design engineer can compare against a limit. The most familiar are HIC (Head Injury Criterion), a3ms (the 3 ms clip of the resultant chest acceleration), Nij (Neck Injury Criterion), and Dmax (chest deflection). Each is derived from filtered accelerometer or displacement channels recorded during a sled or full-vehicle test, and each has a defined time window, filter class, and pass/fail threshold. This chapter establishes the vocabulary, the software stack, and the pipeline that the remaining 29 chapters build on.

What the metrics actually are

HIC is defined over a window bounded by t1 and t2, with a maximum duration of 36 ms:

HIC = max over (t1, t2) of [ (t2 - t1) * ( (1/(t2-t1)) * ∫ a(t) dt )^2.5 ]
      subject to t2 - t1 ≤ 0.036 s

The acceleration a(t) is the resultant of the three head-CG channels, expressed in g, and is filtered per SAE J211 (typically CFC 1000 for head). The 2.5 exponent makes HIC disproportionately sensitive to short, high-amplitude spikes, which is why filter class and window search strategy matter as much as the raw data.

a3ms is simpler: compute the resultant chest acceleration, apply the CFC 180 filter, then slide a 3 ms window and take the maximum mean. Nij combines axial force and bending moment, each normalized by a critical intercept value, and is evaluated as the maximum of four combinations (tension-extension, tension-flexion, compression-extension, compression-flexion). Dmax is the peak chest deflection from a displacement sensor, filtered at CFC 180.

All four metrics share a common dependency chain: channel selection → unit normalization → SAE J211 filtering → resultant or component combination → windowed extremum. Get the filter wrong and every downstream number is wrong, even if the code runs cleanly.

The Python toolchain

The reference stack for this course is deliberately small and reproducible:

  • NumPy — array math, integration, windowed operations.
  • SciPy — scipy.signal.butter and sosfiltfilt for zero-phase SAE J211 filtering.
  • pandas — channel tables, test metadata, and result aggregation across runs.
  • iso13499 / in-house injury package — reference implementations of HIC, Nij, and a3ms; used here as a cross-check, not a black box.
  • Matplotlib — time-history plots and window overlays for QC.
  • Jinja2 + WeasyPrint — HTML/PDF report generation from a template.
  • pytest — regression tests against known-answer fixtures.

Install with a pinned environment so results are reproducible across machines:

python -m venv .venv
source .venv/bin/activate
pip install numpy==1.26.4 scipy==1.13.0 pandas==2.2.2 \
            matplotlib==3.8.4 jinja2==3.1.4 weasyprint==61.2 pytest==8.2.0

The end-to-end workflow

The pipeline has five stages, and each stage has a failure mode that shows up as a plausible-but-wrong number rather than an exception. The diagram below shows the data flow and the QC checkpoint at each stage.

1. Ingest ISO MME / DIAdem channel table + test metadata 2. Condition unit → g, mm SAE J211 filter (CFC 1000 / 180) 3. Compute HIC, a3ms, Nij, Dmax window search 4. Validate limits vs. FMVSS / Euro NCAP pass / fail flags 5. Report HTML → PDF plots + table + traceability QC: sample rate uniform? gaps? t0 aligned? QC: filter class matches channel? zero-phase? QC: window t1/t2 reported ≤ 36 ms? QC: cross-check vs. reference ±0.5% tolerance QC: units g vs m/s² mm vs in Reference frame convention (used throughout this course): +X = forward, +Y = left, +Z = up (SAE J211 / ISO 6487 vehicle coordinate system). Accelerations are reported in g (1 g = 9.80665 m/s²); deflections in mm. Sign changes must be documented.

Stage 1 — Ingest and metadata

Test data arrives as ISO MME, DIAdem, or CSV exports. The critical step is not reading the file but binding each channel to its physical location and filter class. A minimal channel table looks like this:

channel_id, location, direction, unit, cfc, sample_rate_hz
HEAD_CG_X,  head_cg,  x,  g,  1000, 10000
HEAD_CG_Y,  head_cg,  y,  g,  1000, 10000
HEAD_CG_Z,  head_cg,  z,  g,  1000, 10000
CHEST_X,    chest,    x,  g,   180, 10000
CHEST_Y,    chest,    y,  g,   180, 10000
CHEST_Z,    chest,    z,  g,   180, 10000
CHEST_DEF,  chest,    y,  mm,  180, 10000

If the sample rate is not uniform, resample to a common grid before filtering. If t0 is not aligned across channels, the resultant computation will mix non-simultaneous samples and produce a physically impossible vector magnitude.

Stage 2 — Conditioning

SAE J211 filtering is a zero-phase, second-order Butterworth applied forward and backward. In SciPy:

from scipy.signal import butter, sosfiltfilt

def j211_filter(x, fs, cfc):
    # 3 dB cutoff at cfc; standard J211 uses a 2nd-order Butterworth
    sos = butter(2, cfc / (fs / 2.0), btype='low', output='sos')
    return sosfiltfilt(sos, x)

Two mistakes are common. First, using lfilter instead of sosfiltfilt introduces phase shift and inflates the peak. Second, applying the filter twice (once per channel, then again on the resultant) over-attenuates the signal. Filter each component once, then compute the resultant.

Stage 3 — Computation

For HIC, the window search is the expensive part. A vectorized approach using cumulative integration is standard:

import numpy as np

def hic(a_g, dt, max_window_s=0.036):
    # a_g: filtered resultant head acceleration in g
    n = len(a_g)
    max_n = int(max_window_s / dt)
    # cumulative integral of a
    cum = np.concatenate(([0.0], np.cumsum(a_g) * dt))
    best = 0.0
    best_t1 = best_t2 = 0
    for i in range(n):
        j_end = min(i + max_n, n)
        # mean acceleration over [i, j]
        for j in range(i + 1, j_end + 1):
            mean_a = (cum[j] - cum[i]) / ((j - i) * dt)
            hic_val = ((j - i) * dt) * (mean_a ** 2.5)
            if hic_val > best:
                best = hic_val
                best_t1, best_t2 = i, j
    return best, best_t1 * dt, best_t2 * dt

For a 10 kHz signal over 200 ms, the naive double loop is slow; in practice, use the standard optimization that fixes the window end and searches backward, or use numba/numpy vectorization. The result must always be reported with its t1 and t2, because a HIC value without a window is not auditable.

Stage 4 — Validation against limits

Typical limits used in this course (illustrative, not a substitute for the current regulation text):

MetricTypical limitFilterWindow
HIC700 (FMVSS 208, 36 ms)CFC 1000≤ 36 ms
a3ms60 gCFC 1803 ms clip
Nij1.0CFC 600peak
Dmax63 mmCFC 180peak

Each computed value is compared to its limit and tagged PASS or FAIL. A margin column (limit - value) is more useful than the flag alone, because it shows how close the design is to the boundary.

Stage 5 — Reporting

The report is generated from a Jinja2 template that receives a results dictionary and a set of Matplotlib figures. The template renders a summary table, one time-history plot per metric with the critical window shaded, and a traceability block listing the input file hash, filter parameters, and code version. This is what makes the result defensible in a design review.

A report that shows only the final numbers is not reproducible. Always include the input file hash, the filter class and cutoff, the window bounds, and the library versions. If any of these is missing, the result cannot be independently verified.

What you will build across this course

By the end of the 30 chapters, you will have a working Python package that ingests a channel table, applies the correct J211 filters, computes HIC, a3ms, Nij, and Dmax, validates them against a configurable limit set, and emits a PDF report with plots and traceability metadata. The package will be covered by pytest fixtures using synthetic signals with known analytical answers, so that every change to the filtering or window logic is caught before it reaches a report.

The first practical step is to set up the environment and load a sample channel table. The next chapter walks through that ingestion layer in detail, including how to handle missing channels, unit mismatches, and non-uniform sample rates without silently corrupting the downstream metrics.