主要内容

Estimate Battery State of Health from EIS Measurements

R2026b

This example shows how to estimate battery state of health (SOH) from electrochemical impedance spectroscopy (EIS) measurements. EIS is a non-invasive technique that probes the internal state of a battery by applying a small AC voltage across a range of frequencies and measuring the complex impedance response. As a battery ages, changes in the electrode structure, electrolyte, and interfaces alter the impedance spectrum in characteristic ways.

The workflow follows these steps:

  1. Explore how impedance spectra evolve with aging.

  2. Extract geometric features from Nyquist plots using batteryEISFeatures.

  3. Select features that correlate strongly with SOH.

  4. Train a Gaussian process regression (GPR) model to predict SOH from the selected features.

  5. Evaluate prediction accuracy on unseen cells.

Dataset

This example uses the KIT NMC Battery Aging Dataset [1]. It contains EIS measurements from 131 commercial nickel-rich (NMC) lithium-ion cells aged under various conditions (different charge/discharge rates, temperatures, and depth-of-discharge windows) for over one year. The diversity of aging conditions ensures the model learns general degradation patterns rather than condition-specific trends.

The data is preprocessed to include only valid measurements at 50% SOC, room temperature (25°C), and charging direction. This standardized measurement condition ensures consistent comparison across check-ups and cells — impedance varies significantly with SOC and temperature, so controlling these factors isolates the aging signal.

Load and Explore Data

Load the preprocessed EIS dataset. The table contains 131 cells and 1330 EIS sweeps with SOH ranging from 37.7% to 100.0%. Each row represents one EIS sweep with the following columns:

  • cellIndex — identifier for the cell

  • soh — state of health at the time of measurement (%)

  • f — frequency vector (Hz)

  • z_re — real part of impedance (mΩ)

  • z_im — negative imaginary part of impedance (mΩ)

url = 'https://ssd.mathworks.com/supportfiles/predmaint/batteryagingdata/KIT/EISDataSingleOC.zip';
downloadFolder = fullfile(tempdir,tempname);
loc = websave('WindTurbine_FarmA', url);
unzip(loc, pwd)
load("eisCyclesData.mat", "eisData")
head(eisData, 5)
    cellIndex     soh            f              z_re             z_im     
    _________    ______    _____________    _____________    _____________

        1           100    {28×1 double}    {28×1 double}    {28×1 double}
        1        99.974    {28×1 double}    {28×1 double}    {28×1 double}
        2           100    {28×1 double}    {28×1 double}    {28×1 double}
        2        99.866    {28×1 double}    {28×1 double}    {28×1 double}
        2        99.967    {28×1 double}    {28×1 double}    {28×1 double}

Visualize how EIS spectra change with aging for a single cell (cell 121) that has a wide SOH range. The Nyquist plot shows impedance spectra trends at different aging stages, colored by SOH:

  • Ohmic resistance increases: the high-frequency intercept shifts to the right

  • Charge-transfer arc expands: the semicircular arc widens and its peak moves to higher imaginary impedance

  • Low-frequency tail elongates: the diffusion tail extends further

These systematic shape changes in the Nyquist plot motivate extracting geometric features (intercepts, arc dimensions, and tail slope) as quantitative health indicators for SOH estimation.

oneCellSweeps = eisData(eisData.cellIndex == 121, :);
sweepIndices = round(linspace(1, height(oneCellSweeps), 12));
colors = flipud(parula(numel(sweepIndices)));
figure
hold on
for k = 1:numel(sweepIndices)
    s = sweepIndices(k);
    Zr = oneCellSweeps.z_re{s};
    Zi = oneCellSweeps.z_im{s};
    plot(Zr, Zi, "-o", Color=colors(k,:), MarkerSize=3, LineWidth=1.2)
end
hold off
xlabel("Z_{real} (m\Omega)")
ylabel("-Z_{imag} (m\Omega)")
title("Nyquist Plot - Aging Progression Cell 121")
axis equal
grid on
cb = colorbar;
clim([min(oneCellSweeps.soh), max(oneCellSweeps.soh)])
ylabel(cb, "SOH (%)")

Figure contains an axes object. The axes object with title Nyquist Plot - Aging Progression Cell 121, xlabel Z indexOf real baseline (m Omega ), ylabel -Z indexOf imag baseline (m Omega ) contains 12 objects of type line.

Extract and Select EIS Features

Extract EIS features using batteryEISFeatures to quantify the Nyquist plot shape changes. The function identifies R0, arc features and tail features from each spectrum. These geometric features capture the physical degradation mechanisms that affect battery impedance. Call the function on the first sweep with plotting enabled to visualize which geometric features are extracted from the Nyquist plot. The first few high-frequency points are excluded here because they fall in the inductive region (negative imaginary impedance) and are not relevant to the capacitive arc and tail features.

allFeatures = batteryEISFeatures(eisData.z_re{1}(6:end), eisData.z_im{1}(6:end), eisData.f{1}(6:end), Plot=true);

Figure contains an axes object. The axes object with title EIS Data with Extracted Features, xlabel Real(Z) [ Omega ], ylabel -Imag(Z) [ Omega ] contains 7 objects of type line, text. These objects represent EIS Data, Fitted Tail.

Loop over the remaining sweeps to build a complete feature table with one row per EIS sweep and one column per feature. Each value is a scalar measurement extracted from the impedance spectrum geometry. This compact representation reduces a full frequency-domain spectrum into a small set of interpretable health indicators. The first sweep was already processed above to initialize the table structure.

for s = 2:height(eisData)
    allFeatures(s,:) = batteryEISFeatures(eisData.z_re{s}, eisData.z_im{s}, eisData.f{s});
end
head(allFeatures, 5)
      R0      R0_Frequency    TailSlope    TailIntercept_Zreal    Arc1_End_Zreal    Arc1_End_Zimg    Arc1_Width    Arc1_Peak_Zreal    Arc1_Peak_Zimg    Arc1_Peak_Frequency    Arc1_End_Frequency
    ______    ____________    _________    ___________________    ______________    _____________    __________    _______________    ______________    ___________________    __________________

    14.976       884.15        1.6334            18.341               18.241            0.366          3.2646          16.657             1.226               147.06                 6.7568      
    14.988       851.79         1.373             18.08               18.269             0.46          3.2814          16.245              1.15                  100                  3.125      
    14.374       899.11        1.3936            18.392                18.46            0.323          4.0865          16.382             1.201               147.06                 6.7568      
    14.383       792.57        1.2695            18.189                18.52            0.309          4.1374          16.198             1.227               147.06                      5      
    14.432       786.81        1.3322            18.227               18.467            0.444          4.0355          16.944              1.12               67.568                      5      

Explore Feature Correlation with SOH

Examine which of these features correlate most strongly with SOH to identify the best predictors for the regression model. Compute the Pearson correlation coefficient between each EIS feature and SOH across all cells and sweeps. The Pearson coefficient measures the strength and direction of the linear relationship between a feature and SOH.

featureNames = allFeatures.Properties.VariableNames;
numFeatures = numel(featureNames);
allSOH = eisData.soh;
allCellIdx = eisData.cellIndex;
corrValues = zeros(numFeatures, 1);
for i = 1:numFeatures
    corrValues(i) = corr(allFeatures.(featureNames{i}), allSOH, 'Rows', 'complete');
end

Visualize the top two most correlated features as scatter plots. Each point represents one EIS sweep, colored by cell index to verify that the trend holds across different cells (not just within a single cell). Strong monotonic trends that are consistent across cells indicate features that generalize well as SOH predictors.

[~, sortIdx] = sort(abs(corrValues), "descend");
tiledlayout(1, 2)
for p = 1:2
    nexttile
    fi = sortIdx(p);
    vals = allFeatures.(featureNames{fi});
    scatter(allSOH, vals, 15, allCellIdx, "filled", MarkerFaceAlpha=0.5)
    xlabel("SOH (%)")
    fLabel = strrep(featureNames{fi}, "_", " ");
    if contains(featureNames{fi}, ["R0" "Zreal" "Zimg" "Width" "Intercept"])
        fLabel = fLabel + " (m\Omega)";
    end
    ylabel(fLabel)
    title(strrep(featureNames{fi}, "_", " ") + " (r=" + corrValues(fi) + ")")
    grid on
end

Figure contains 2 axes objects. Axes object 1 with title TailIntercept Zreal (r=-0.99485), xlabel SOH (%), ylabel TailIntercept Zreal (m\Omega) contains an object of type scatter. Axes object 2 with title Arc1 End Zreal (r=-0.9881), xlabel SOH (%), ylabel Arc1 End Zreal (m\Omega) contains an object of type scatter.

Select Key Features

Select a compact subset of features that are most predictive of SOH, discarding noisy or redundant inputs that could lead to overfitting. Rank all features by their absolute Pearson correlation with SOH and apply a threshold of 0.8. The bar chart below visualizes this selection — features are sorted in descending order of |r|, and the red dashed line marks the cutoff.

figure
bar(abs(corrValues(sortIdx)))
hold on
yline(0.8, "r--", "Threshold", LineWidth=1.5)
hold off
xticks(1:numFeatures)
xticklabels(strrep(featureNames(sortIdx), "_", " "))
xtickangle(45)
ylabel("|Correlation with SOH|")
title("Feature Correlation with SOH")
grid on

Figure contains an axes object. The axes object with title Feature Correlation with SOH, ylabel |Correlation with SOH| contains 2 objects of type bar, constantline.

Features above the threshold are retained as model inputs. This reduces the feature space to a compact, interpretable subset that captures the dominant impedance changes associated with aging. Fewer input features also improve training efficiency and reduce the risk of multicollinearity. With informative features identified, the next step is to split the data and build a predictive model that maps these EIS features to SOH.

topFeatureIdx = find(abs(corrValues) >= 0.8);
topFeatureNames = featureNames(topFeatureIdx);

Build SOH Prediction Model

Split data into training and test sets at the cell level using an 80/20 hold-out partition. Cell-level splitting ensures all sweeps from a given cell appear in only one set. This is important because consecutive EIS measurements from the same cell are highly correlated — if some sweeps from a cell appear in training and others in testing, the model can exploit this correlation rather than learning generalizable degradation patterns. By splitting at the cell level, the test set evaluates how well the model predicts SOH for entirely unseen cells, which better reflects real-world deployment where the model encounters new batteries.

rng(0, "twister");
uniqueCells = unique(allCellIdx);
cv = cvpartition(numel(uniqueCells), "HoldOut", 0.2);
trainCells = uniqueCells(training(cv));
testCells = uniqueCells(test(cv));

trainIdx = ismember(allCellIdx, trainCells);
testIdx = ismember(allCellIdx, testCells);

xTrain = allFeatures{trainIdx, topFeatureNames};
yTrain = allSOH(trainIdx);
xTest = allFeatures{testIdx, topFeatureNames};
yTest = allSOH(testIdx);

Train Model

Train a Gaussian process regression (GPR) model to map the selected EIS features to SOH. GPR is a non-parametric Bayesian method that models the relationship between inputs and outputs as a distribution over functions. It is well-suited for this problem because it captures nonlinear relationships without requiring manual specification of the model form.

The squared exponential kernel assumes smooth, continuous degradation trends, which aligns with the gradual nature of battery aging. Input standardization ensures features with different scales contribute equally.

gprModel = fitrgp(xTrain, yTrain, ...
    KernelFunction="squaredexponential", ...
    Standardize=true);

Evaluate Model

Predict SOH on the held-out test cells. The GPR model returns both a point estimate and a prediction standard deviation for each sample. The standard deviation quantifies how confident the model is — larger values indicate the test point lies far from the training data in feature space.

Compute performance metrics to assess prediction quality:

  • RMSE — root mean squared error, penalizes large errors more heavily.

  • R² — coefficient of determination, indicates what fraction of SOH variance the model explains (1.0 = perfect).

[yPred, yStd] = predict(gprModel, xTest);
rmseVal = sqrt(mean((yTest - yPred).^2));
ssRes = sum((yTest - yPred).^2);
ssTot = sum((yTest - mean(yTest)).^2);
r2Val = 1 - ssRes/ssTot;

Visualize prediction accuracy with a predicted-vs-actual scatter plot. The black dashed line is the ideal reference — points on this line represent perfect predictions. The shaded blue band shows ±3σ of the average prediction uncertainty, giving a visual sense of the model's confidence envelope. Points scattered tightly around the diagonal with a narrow band indicate a well-calibrated, accurate model.

figure
hold on
scatter(yTest, yPred, 20, "filled", MarkerFaceAlpha=0.6)
sohLim = [min(yTest)-2, max(yTest)+2];
plot(sohLim, sohLim, "k--", LineWidth=1.5)
xRange = linspace(sohLim(1), sohLim(2), 100);
fill([xRange, fliplr(xRange)], ...
    [xRange+3*mean(yStd), fliplr(xRange-3*mean(yStd))], ...
    "b", FaceAlpha=0.2, EdgeColor="none")
hold off
xlabel("Actual SOH (%)")
ylabel("Predicted SOH (%)")
title("Predicted vs Actual SOH (RMSE=" + rmseVal + ", R^2=" + r2Val + ")")
legend("Test data", "Perfect Prediction", "\pm3\sigma band", Location="southeast")
grid on
xlim(sohLim)
ylim(sohLim)
axis square

Figure contains an axes object. The axes object with title Predicted vs Actual SOH (RMSE= 0 . 86321 , R Squared baseline = 0 . 99518 ), xlabel Actual SOH (%), ylabel Predicted SOH (%) contains 3 objects of type scatter, line, patch. These objects represent Test data, Perfect Prediction, \pm3\sigma band.

References

[1] Luh, M., Blank, T. et al. "Comprehensive battery aging dataset: capacity and impedance fade measurements of a lithium-ion NMC/C-SiO cell." Scientific Data 11, 1285 (2024). https://doi.org/10.1038/s41597-024-03831-x

See Also