Generated banner for Matlab Extract Battery Features
Generated logo for Matlab Extract Battery Features

Matlab Extract Battery Features

MathWorksOfficialCreated by MathWorks

  • Skill
  • Tool integration
  • Science and research
  • BSD-2-Clause
  • No issues found

From the author

Extract battery features in MATLAB from voltage, current, and temperature measurements: multi-cycle cycling tests, HPPC / pulse tests, EIS impedance sweeps, or a single in-field charge or discharge segment. Use when the user asks for battery features, or for SOH, RUL, degradation, internal-resistance, or impedance analysis that needs them. Output is a feature table; does not train SOH / RUL models. Do not trigger only because battery data is present. Triggers on battery* functions such as batteryTestDataParser, batteryTestFeatureExtractor, batteryPulseFeatures, batteryEISFeatures.
— the description by MathWorks

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a structured interface for the MATLAB Predictive Maintenance Toolbox to extract diagnostic features from battery test data. It supports multi-cycle cycling tests, HPPC/pulse tests, EIS impedance sweeps, and in-field operational segments. The skill automates the segmentation and feature extraction process, outputting a structured feature table ready for downstream analysis like SOH or RUL estimation. It is designed to handle various data shapes and provides clear reporting on column mappings and data quality, ensuring consistent and reproducible feature engineering.

What it does

  • Segments raw battery cycling data into charge, discharge, and rest phases.
  • Extracts capacity, energy, and cycle-level statistics from cycling tests.
  • Computes internal resistance and polarization features from pulse/HPPC tests.
  • Analyzes impedance spectroscopy (EIS) data to extract resistance and arc features.
  • Generates differential curves (IC/DV/DT) for aging mechanism analysis.
  • Processes single-segment in-field data for operational condition monitoring.

When to use it

  • When you need to extract features from raw voltage, current, and temperature traces.
  • For analyzing battery degradation, SOH, or RUL using extracted feature tables.
  • When performing cell screening or comparing performance across multiple battery cells.
  • For extracting physical aging indicators like IC peak shifts or impedance growth.
  • When you have raw data from bench cyclers, BMS logs, or drive/charge events.

Advantages

  • Uses performance-optimized, native MATLAB functions for reliable feature extraction.
  • Automates complex segmentation and peak detection tasks.
  • Provides a standardized workflow for diverse battery test types.
  • Ensures consistent data handling through explicit mapping and validation steps.
  • Reduces manual coding effort for common electrochemical analysis tasks.

Usage examples

“Extract features from my battery cycling data to help with SOH modeling.”
The agent identifies the cycling data structure, maps the voltage/current/time columns, runs the parser and extractor, and presents a feature table with cycle-level metrics.
“What is the charge-transfer resistance from this EIS file?”
The agent processes the frequency, Zreal, and Zimag data using the EIS feature extraction function and reports the extracted resistance values.
“Calculate the internal resistance from these HPPC pulse tests.”
The agent identifies the pulse segments, applies the pulse feature extraction function, and returns a table containing resistance features for each pulse.
“Can you extract features from this partial discharge segment logged by my BMS?”
The agent uses the measurement features function to compute statistical and cumulative features from the single discharge segment.

Install

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/matlab/matlab-extract-battery-features/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip matlab-extract-battery-features.zip -d .agents/skills

Documentation by MathWorks

Battery Feature Extraction

Router for MATLAB's Predictive Maintenance Toolbox battery feature extraction functions. Detailed workflows live in references/*.md — load only the file that matches the inspected data type.

When to Use

Trigger on the domain questions asked about battery voltage / current / temperature measurements — from bench cycler / HPPC / EIS tests or from in-field / in-use operational data logged by a BMS or captured during a drive or charge event. The skill produces the feature table that feeds each question's downstream analysis. Concretely:

  • Degradation, SOH, RUL, capacity/energy fade. "How much has this cell degraded over 1000 cycles?" · "Track SOH." · "Estimate remaining useful life." · "Which cell is fading fastest?"

  • Aging mechanism from differential curves. "How are the dQ/dV peaks shifting?" · "Loss of lithium inventory vs. active-material loss?" · "Plot IC curves at cycles 1, 250, 500, 1000." · "What's the DV signature of aging?"

  • Internal resistance from HPPC / pulse tests. "What's R0 at 50% SOC?" · "How has DCR grown?" · "Get polarization resistance and recovery rate per pulse."

  • EIS / impedance analysis. "Fit the Nyquist plot." · "What's the charge-transfer resistance?" · "How has R0 grown from EIS?" · "Extract arc widths and Warburg tail."

  • Segmentation of cycling data. "Split this into charge / discharge / rest." · "Separate CC and CV portions of each cycle." · "Give me only the discharge segments." (With or without downstream feature extraction.)

  • In-field / operational condition monitoring. Features from a single measured segment that has no multi-cycle test structure — a partial charge or discharge logged by a BMS, one CC segment, or a drive/charge event. "Extract features from this field charge segment." · "Summarize this discharge from the vehicle log." · "What condition-monitoring features can I get from this partial charge?" (Served by batteryMeasurementFeatures, which works stats-only when no cycle/time structure is present.)

  • Fast-charging stress / plating indicators. "Is CV duration growing?" · "Any thermal signature of plating (DT)?" · "Recovery slowdown at high SOC?"

  • Cell screening / cell-to-cell comparison. "Compare these 10 cells." · "Rank by fade rate." · "Which cells are outliers?"

  • Feature engineering for ML. "Give me a feature table for SOH modeling." · "Which features best correlate with capacity fade?"

  • Batch processing of multiple test files through any of the above.

When NOT to Use

  • First-look / data classification / orientation questions on battery data: "What kind of test is this?" · "Describe this data." · "Tell me about this file." · "Analyze cycling.csv and tell me what it contains." These are inspection; the skill fires later when the user asks one of the analyses above.

  • Role-framing without a specific analysis in the current turn. A system prompt setting a "battery reliability agent" persona, a filename containing "battery" / "cycling" / "SOH", or a bare "analyze this" verb is not enough — the current user turn must name one of the specific analyses.

  • Data import or vendor-format conversion (Arbin .res, Neware .nda, Maccor, BioLogic → MATLAB table).

  • Plotting raw voltage / current / temperature vs time or capacity without first segmenting or computing an IC/DV/DT curve — that's a plain plotting task.

  • Cleaning, resampling, interpolating, or filtering time-series that does not feed into segmentation, differential curves, or feature extraction.

  • SOH / RUL model training or prediction — or any ML training — on an already-extracted feature table. This skill is the upstream feature step; modeling and prediction are downstream.

  • Battery simulation, equivalent-circuit modeling, or Simulink battery plant models.

  • Non-electrochemical data (fuel cells, supercapacitors, generic sensor streams).

This skill covers the 7 PMT battery feature extraction functions. These functions work natively with MATLAB tables and vectors, handle segmentation and peak detection internally, and are performance-optimized. Prefer these PMT functions over manual feature computation (e.g., hand-coded cumtrapz loops, manual peak finding). Override only if the user explicitly requests otherwise.


API Overview

The Predictive Maintenance Toolbox provides 7 public functions for battery feature extraction, organized by the kind of measurement they consume:

                                Battery Data
                                     │
      ┌────────────────┬─────────────┴────────┬────────────────────┐
      │                │                      │                    │
 Multi-cycle     Single measured          Pulse tests          EIS tests
 cycling test    segment                  (HPPC / GITT)        (impedance)
      │          (bench or in-field)           │                    │
      ▼                │                        ▼                    ▼
 batteryTest-          │                  batteryPulseFeatures  batteryEISFeatures
 DataParser            │                        │                    │
     +                 │                        ▼                    ▼
 batteryTest-          │                  Pulse feature         EIS feature
 FeatureExtractor      │                  table                 table
      │                │
      ▼                ├── batteryMeasurementFeatures        → Measurement feature table
 Full-pipeline         │
 feature table         ├── batteryDifferentialCurves         → IC / DV / DT curves
                       │
                       └── batteryDifferentialCurveFeatures  → Curve peak feature table

The single measured segment branch is the in-field / operational path: its functions take raw V / I / T / t from one charge or discharge segment and need no multi-cycle test structure, so they serve BMS-logged or drive/charge-event data as well as a segment pulled from a bench test.

FunctionInputOutputUse WhenAvailable From
batteryTestDataParserRaw cycling tableParser object (segmented data)Multi-cycle data needing segmentationR2024b
batteryTestFeatureExtractorOptionsExtractor objectFull pipeline: parser → all cycling featuresR2024b
batteryMeasurementFeaturesV, I, T, t vectorsStatistical + cumulative feature tableAny single-phase segment (partial or full charge/discharge)R2026a
batteryDifferentialCurvesV, I, T, t vectorsdQ/dV, dV/dQ, dT/dV tablesConstant-current segment only (CC charge or CC discharge)R2026a
batteryDifferentialCurveFeaturesCurve + x-axisPeak feature tableExtracting features from differential curvesR2026a
batteryPulseFeaturesV, I, t (or table)Pulse feature table + pulse dataHPPC/pulse test resistance featuresR2026b
batteryEISFeaturesZr, Zi, F vectorsEIS feature tableImpedance spectroscopy analysisR2026b

Load the Right Reference Before Proceeding

Step 1 — inspect the data shape. Load the file directly from the exact path the user gave, resolved against the current working folder. Do not recursively search temp, home, or system directories to locate it — a recursive dir(..., '**', ...) over such a tree can stall for over a minute. Then read the file and answer two questions:

  1. Granularity. Is each row a per-sample raw record (many rows per cycle: voltage / current sampled at 1 Hz or faster), or is each row a per-cycle / per-cell summary (one row per cycle per cell, with columns like Capacity, SOH, CV_hold_duration)?

  2. Multi-cell. Is there a CellID / Cell / SerialNumber column identifying multiple cells in the same file?

Step 2 — route by shape:

Data shape inspectedRoute
Raw per-sample multi-cycle cycling table with cycle and step index columns (one cell, or multi-cell — see note below)references/cycling-pipeline.md
Raw per-sample cycling table with step index but no cycle index — either multiple cycles the parser can synthesize a cycle index for, or a single full charge-discharge cycle (see reference for the multi- vs single-cycle rule)references/cycling-pipeline.md
Raw per-sample single-phase cycling segment (partial charge, one discharge, CC segment) with no cycle index and no full charge-discharge boundaryreferences/individual-cycling-functions.md
Raw short current pulses separated by rest periods (HPPC / GITT)references/pulse-features.md
Raw impedance sweep with Frequency, Zreal, Zimagreferences/eis-features.md
Already-aggregated per-cell / per-cycle summary — one row per (cell, cycle) with pre-computed Capacity, SOH, CV_hold_duration, RPT_capacity, temperature summaries, etc. (no per-sample V/I/t traces)No reference loads. See § Data Is Already Aggregated below.
Additionally, when tuning Tolerance, WindowSize, IC/DV/DT smoothing, or diagnosing "not CC data" errorsAlso load references/parameter-tuning.md

Multi-battery-cell data. When the data covers more than one battery cell, see the loaded reference's § Preprocessing: Data Layout for detection and combine/iterate rules, and § Postprocessing: Feature Table Organization for the featuresPerCell{k} output convention.

Data Is Already Aggregated — Skill Does Not Apply

If each row is already a per-cycle or per-cell summary (e.g., a CSV with columns CellID, Cycle, Capacity_Ah, SOH, CV_hold_s, Temp_max_C), the PMT battery* functions covered by this skill have nothing to add — they compute those very features from raw traces, and you already have them. Tell the user this, then handle the cell-ranking / degradation-comparison task with plain table operations:

  • Rank cells by fade rate: fit a slope of Capacity vs Cycle per CellID, sort ascending.

  • Compare mechanisms by trending CV_hold_s, temperature statistics, or resistance summaries per cell.

  • Visualize with tiledlayout: capacity-vs-cycle families, SOH trajectories, CV-hold growth.

This is standard MATLAB table wrangling — no PMT function is required. If a downstream skill fits better (e.g. matlab-analyze-data for the ranking workflow), route there instead of loading a reference from this skill.

Application × data shape. When the user names an application (SOH, RUL, Fast Charging, Cell Screening) that could be served by multiple data types, still route by inspected shape first — application alone doesn't pick a workflow. Then read the matching reference's "Feature Selection for Common Applications" section for the recipe.


Workflow Skeleton

Every reference in this skill follows the same phase template. Load the matching reference for the phase-by-phase details; the skeleton is:

#PhaseWhat it does
1Identify inputsMap data columns to the function's parameters. Workflow-specific vocabulary table lives at the top of each reference. General rules (vocabulary-first, datetime > numeric, unit conversion) below.
2Data layoutDetect single-file vs multi-file, single-cell vs multi-cell, single-sweep vs multi-sweep; combine or split accordingly.
3Call functionsIssue the primary computation call(s).
4Verify resultsCross-check outputs against independent evidence. Cycling uses verifySegmentation + checkCyclingProtocol; individual-cycling uses verifySingleSegment. Pulse and EIS skip this phase — no verifier helper exists.
5Tune parametersOnly if Phase 4 flagged an issue (or the caller reports symptoms of a wrong setting). Sweep tuning parameters and re-call.
6Postprocess / organizeAttach tag columns (CellID, ExperimentIndex), keep per-cell tables in a cell array, etc.
7Report + next stepsPresent the feature table with the key-decisions summary and the Next Steps offer (§ Stop After Extraction rule below).

Reference-to-phase map:

ReferencePhasesVerify helperTuning reference
cycling-pipeline.md (+ cycling/*.md on demand)1-7verifySegmentation or verifyAgainstSchedule, + checkCyclingProtocolcycling/tolerance-tuning.md or cycling/schedule-verification.md; parameter-tuning.md (IC/DV/DT smoothing)
individual-cycling-functions.md1-7verifySingleSegmentparameter-tuning.md (IC/DV/DT smoothing, NoiseTolerance)
pulse-features.md1-3, 5-7 (no Verify)—inline (ValidPulseDurationRange, MinCurrentChange)
eis-features.md1-3, 5-7 (no Verify)—inline (NumArcs, multi-sweep splitting)

Interaction Rules

Cross-cutting rules that apply to every workflow — do not skip regardless of which reference you loaded.

Interaction Model: Stop or Proceed

Do not stop and ask before every extraction. Most decisions have a deterministic rule — apply it, extract, and report what you did so the user can correct it. Only stop when a decision is genuinely ambiguous and guessing it wrong would fail silently (plausible-but-wrong features). The rule applies to every workflow — cycling pipeline, individual cycling functions, pulse, and EIS.

STOP and ask the user first — but only when one of these is true:

General (all workflows):

  1. Two or more candidate columns of the same type for one variable (e.g. two numeric elapsed-time columns, two current-like columns) — no rule can choose between them.

  2. Raw unnamed numeric matrix — column roles must be inferred, not read from names.

  3. A required variable cannot be mapped — a needed column is missing or its role is unclear.

Cycling pipeline only:

  1. checkCyclingProtocol finds neither phase consistent — no phase can be recommended.

  2. Anomalous cycles detected — never silently exclude them.

Otherwise PROCEED, then REPORT. Apply the deterministic rules — datetime/duration beats numeric time; set DT=true iff temperature is present; set CC=true whenever IC/DV/DT is requested; for cycling, use checkCyclingProtocol's RecommendedPhase — write and run the extraction, then present in a single after-extraction turn:

  • the column mappings used (noting any alternatives considered and why the chosen one won),

  • any workflow-specific decisions — cycling: CyclingPhase and its consistency reason; pulse: pulse-detection thresholds if adjusted; EIS: frequency range or sweep selected if the file had multiple,

  • any data-quality findings (time-gap indices, invalid-point count, negative Zreal, missing rests, etc.) as a report line — not a gate,

  • any goal-vs-data tension, if present (see the loaded reference — e.g. cycling-pipeline.md Phase 4),

  • the resulting feature table, followed by the "what next?" offer.

Close the report with an explicit undo invitation, e.g. "If any mapping or the phase choice looks wrong, tell me and I'll re-run." This preserves the safety net without a blocking question.

Stop After Extraction — Offer Next Steps

Once the feature table is extracted and displayed, stop and present available next steps to the user. Do not autonomously proceed to visualization, feature ranking, or script saving. The stopping turn must have three parts:

  1. Feature-table summary — M features across N rows / cycles / pulses / sweeps.

  2. Key decisions + undo invitation — the autonomous choices you made, each stated so the user can override it, because these were decisions the user never explicitly approved:

    • Column mappings used (with any alternatives considered) and any unit conversion applied.

    • The workflow-specific settings the loaded reference tells you to report (cycling: CyclingPhase + consistency reason + feature categories; pulse: pulse-detection settings; EIS: frequency range / sweep selection; individual cycling: segment identified). The full workflow-specific template lives in each reference's § Report to the user after extraction.

    • Undo invitation, pointed at those choices — e.g. "I chose the time column, CyclingPhase, and feature categories automatically — tell me and I'll re-run with different ones." Do not use a generic "if anything looks wrong, let me know."

    • When Phase 4 flagged a concern, this is required and must name the finding — e.g. verifySegmentation returned TuningRecommended, checkCyclingProtocol reported anomalous cycles, or pulse/EIS detection returned empty/failed. State the flag and the concrete re-run it implies ("3 cycles segmented inconsistently — I can re-run with a tighter Tolerance, or exclude them").

  3. Next-steps offer — the four standing options:

    • Visualize: plots appropriate to the workflow (see the loaded reference's Phase 7 Visualize hints; cycling loads references/cycling/postprocess.md)

    • Rank features: identify most discriminative features for your analysis goal

    • Export: save the feature table to a .mat or .csv file

    • Refine: adjust feature categories, phase, or smoothing parameters

Only execute a next step when the user explicitly requests it. This applies even if the original prompt contains phrasing like "show me" or "plot" — extract first, present the table with the key decisions, then offer to visualize.

Column Mapping: General Rules

Workflow-specific column vocabularies live in the loaded reference's Phase 1 (Identify inputs). The rules below apply regardless of which vocabulary you're consulting.

Vocabulary first — try common vendor names before declaring a column missing. The 4 mainstream cyclers (Arbin, Neware, Maccor, BioLogic) and typical EIS/potentiostat exports each use different conventions. Match case-insensitively; strip units suffixes (_V, _A, /mA, /V, /Hz, /Ohm); a match on any name in the reference's Phase 1 vocabulary is sufficient — no need to stop-and-ask.

Unit conversion when the column name carries units. If the current column is I/mA (milliamps), convert to Amps before passing to the function (data.I_A = data.("I/mA") / 1000); same for mV → V. Report the conversion in the after-extraction summary.

  1. Prefer datetime/duration over numeric elapsed time. If one candidate is datetime/duration type and another is numeric seconds, the datetime column wins — this is a rule, not a judgment call. Apply it and report it rather than asking. Note the alternative you considered in the after-extraction report (e.g., "I found both Test_Time (numeric) and DateTime (datetime type) — used DateTime as it is a proper datetime column").

  2. Stop only when the rule can't decide. If two candidates share the same type (e.g. two numeric elapsed-time columns) so no rule can pick, ask. Likewise if a required variable has no clear column.

  3. Report the mappings after extracting — with any workflow-specific decisions, data-quality findings, and an undo invitation. See § Stop After Extraction above and the loaded reference's Phase 7 for the workflow-specific template.

Unnamed Data (Matrices Without Column Names)

When data is a raw numeric matrix (no column names), infer column roles from value characteristics, then confirm with the user before proceeding:

  • EIS data: Frequency spans orders of magnitude (all positive), Zreal is non-negative with narrow range, Zimag can be negative with similar magnitude to Zreal

  • Pulse data: Current has distinct pulse/rest pattern, voltage shows step responses, time is monotonically increasing

  • Otherwise: If the data does not clearly match EIS or pulse patterns, ask the user what type of data it is rather than guessing

Present the inferred mapping and ask the user to confirm. Example:

Your data is a 641×3 matrix with no column names. Based on value ranges:

  • Column 1: spans 0.05 to 10000 (likely Frequency)

  • Column 2: all positive, range 13–22 (likely Zreal)

  • Column 3: range -14 to 6, mixed sign (likely Zimag)

Does this mapping look correct?


Cross-Function Pitfalls

Only two pitfalls are truly cross-cutting. Workflow-specific pitfalls (time format, phase mixing, DT requirements, feature-count overfitting, CC data requirements, EIS sign conventions, pulse-detection thresholds, etc.) live in the loaded reference's own § Pitfalls section — consult it before extracting.

  • Manual loops when a PMT function exists. If the user asks for capacity fade, IC peaks, pulse R0, EIS Rct, etc., use the PMT function — do not hand-code cumtrapz, findpeaks, or manual segmentation loops.

  • Wrong units. Current in Amps (not mA), voltage in Volts. Convert unit-suffixed columns before passing them to any PMT function.


Copyright 2026 The MathWorks, Inc.


Files

references/parameter-tuning.md

Parameter Tuning — Tolerance and Differential Curves

Shared reference for tuning parameters that affect segmentation quality and IC/DV/DT curve fidelity. Load in addition to cycling-pipeline.md or individual-cycling-functions.md when:

  • verifySegmentation reports TuningRecommended == true (mechanics in § Tolerance Log-Scale Sweep below)

  • verifySingleSegment fails with an RSD or noise reason (§ Differential Curve Parameters)

  • The user reports noisy curves, missing peaks, wrong CC/CV split, or "not CC data" errors

  • Extracted feature counts differ across cells that share the same protocol (§ Feature-Set Consistency: VRange4Slope and VRange4TimeInterval)

Tolerance (CC/CV mode identification)

Tolerance controls how the parser distinguishes CC from CV segments. Internally it is a dV/dt threshold (V/s): samples where |dV/dt| < Tolerance are classified as CV (voltage held steady); samples where |dI/dt| < CurrentTolerance (fixed at 1e-4) are classified as CC (current held steady).

The default 5e-5 works for most lab data at ~1 Hz sampling. Adjust when CC/CV segmentation looks wrong:

SymptomLikely causeFix
Known CCCV protocol but parser labels everything as CC (no CV detected)Tolerance too low — voltage noise exceeds threshold even during true CVIncrease Tolerance (try 1e-4 or 5e-4)
CC segment appears truncated, CV starts too earlyTolerance too high — slow voltage rise at end of CC misclassified as CVDecrease Tolerance (try 1e-5 or 5e-6)
Very high sampling rate (>10 Hz) with noisy dataSmall dt amplifies dV/dt valuesIncrease Tolerance proportionally
Low sampling rate (<0.1 Hz) or averaged dataLarge dt yields very small dV/dtDecrease Tolerance

Diagnostic approach: After segmentData(parser), check the CyclingModes column. If the CC/CV split doesn't match the expected protocol (e.g., a CCCV charge should have both CC and CV portions), adjust Tolerance and re-segment:

parser.Tolerance = 1e-4;  % reset triggers re-segmentation on next call
segmented = segmentData(parser);

Typical useful range: 1e-6 to 1e-3. Start from the default and adjust by one order of magnitude at a time.

Tolerance Log-Scale Sweep (mechanics)

Invoked when verifySegmentation(parser).TuningRecommended == true. The verify helper decides when to tune; this section decides what value to pick.

Hint from sampling rate. Use report.DiagnosticDt (median seconds between samples):

  • dt <= 1 s → start the sweep at the default 5e-5.

  • 1 < dt <= 5 s → start at 5e-6.

  • dt > 5 s → start at 1e-6 (the default is essentially guaranteed to mislabel plateau CC as CV at low rates).

Sweep loop. Iterate on a log grid, pick the largest Tolerance that drops TuningRecommended back to false (largest = most robust to noise while still correct). Sporadic outlier cycles keeping MismatchCount > 0 are acceptable as long as MismatchFraction is below MinMismatchFraction (default 2%) — those should be inspected and optionally moved to ExcludedCycles, not driven to zero by shrinking Tolerance:

tolCandidates = [5e-5, 1e-5, 5e-6, 1e-6, 5e-7, 1e-7];
bestTol = NaN;
for t = tolCandidates
    parser.Tolerance = t;                  % assignment re-triggers segmentation
    report = verifySegmentation(parser);
    if ~report.TuningRecommended
        bestTol = t;
        break;                              % largest passing value wins
    end
end
if isnan(bestTol)
    % STOP — sweep exhausted. Present report.MismatchGroups, the achieved
    % MismatchFraction, and the tried values to the user; do not silently
    % proceed with a guess.
end

Assigning to parser.Tolerance invalidates the cached segmentation, so the next call to segmentData(parser) (or verifySegmentation, which calls segmentData internally) uses the new value automatically. No manual reset.

Why CV can be assigned on a falling voltage. The internal identifyMode rule is |dV/dt| < Tolerance → CV candidate — it does not require V to actually be constant. On a slow discharge plateau at low sampling rate, the true dV/dt can be below the default 5e-5 V/s even though voltage is monotonically decreasing. Lowering Tolerance below the plateau's dV/dt is what restores CC classification.

When the sweep can't fix it. If every candidate leaves at least one mismatch, the fix is not Tolerance:

  • Data may be pre-averaged (samples span multiple modes each) — no threshold recovers that.

  • Chemistry may have a plateau flatter than any usable Tolerance (e.g., LFP mid-SOC). Consider extracting only from the non-plateau region.

  • Column mapping may be wrong (Current column carries capacity, Voltage carries current, etc.). Re-verify column mappings before tuning.

  • Data-collection failure on a subset of cycles. A single cycle with a truncated step (e.g., logger disconnected mid-cycle → step has 20 samples instead of 200) will never satisfy any Tolerance and pulls the sweep toward wrong choices for the healthy cycles. Distinguish this failure shape before tuning — see § Sweep-and-Classify below.

Sweep-and-Classify (schedule-provided route)

When the user provides a tester schedule, use sweepScheduleAndReport(tbl, schedule, mapping, tolGrid) instead of the plain sweep. It classifies each cycle into one of three shapes and picks Tolerance only from the fixable shape:

ShapeDefinitionAction
alwaysPassPasses at every Tolerance in the gridIgnore; already correct.
tolSensitivePasses at some Tolerance values, fails at othersPick the largest Tolerance that satisfies every cycle in this set.
neverPassFails at every ToleranceReturn as ExcludedCycles. Do not tune around them.

The MinSamples gate inside verifyAgainstSchedule runs before the mode check, so a step with fewer than MinSamples samples is labeled "data-collection: only <n> samples (< <min>)" — a specific reason string, not a mode mismatch. This is what makes neverPass mean "data problem" rather than "wrong Tolerance." Without the gate, a truncated step looks like a Tolerance issue and the sweep either wastes cycles or picks a bad value trying to accommodate it.

mapping = struct( ...
    'CurrentVariable', "Current", 'VoltageVariable', "Voltage", ...
    'TimeVariable', "DateTime", 'CycleIndexVariable', "Cycle_Index", ...
    'StepIndexVariable', "Step_Index", 'TemperatureVariable', "Temperature");
out = sweepScheduleAndReport(tbl, schedule, mapping, [5e-5, 1e-5, 5e-6, 1e-6]);

parser.Tolerance = out.BestTol;
parser.ExcludedCycles = out.ExcludedCycles;  % excluded neverPass cycles

out fields: PassMap (numCyc × numTol logical), TolGrid, BestTol, ExcludedCycles, AlwaysPass, TolSensitive, FinalReport (from verifyAgainstSchedule at BestTol), PerToleranceReports (per-Tolerance report array for diagnostics).

Precheck the schedule when every cycle lands in neverPass. Before assuming a systemic data problem, review the schedule. Common specification errors that mimic a data problem:

  • Asserted MinCoverage is higher than the step's true CV/CC fraction (e.g., asserting 0.30 CV on a fast-CC step whose CV portion is 0-15%). Soften to 0.0 (presence check only).

  • Tester-artifact steps (1-3 samples wedged between real stages) declared as real steps. Drop them from schedule.

  • CCCV composite step declared as separate CC + CV entries (or vice versa). Use a single Type: "CCCV" entry with MinCC / MinCV.

Only if the schedule is right and every cycle still fails is this a systemic data problem — surface the report and stop.

Feature-Set Consistency: VRange4Slope and VRange4TimeInterval

batteryTestFeatureExtractor computes two per-CC-step features whose availability depends on how much CC data falls inside a voltage window:

  • Step<N>_CC_slope — linear fit of V vs t over samples where V ∈ VRange4Slope (default [2.9, 3.2] V).

  • Step<N>_CC_tInv — duration in that same window.

The extractor drops all-NaN columns per phase before returning the feature table (BatteryTestFeatureExtractor.m:344). If a given cell never has ≥ 2 CC samples inside the window on any cycle, the whole column disappears. Across cells with different aging or slightly different operating windows, this produces different feature counts for cells running the identical protocol — silent, hard to debug from the extracted table alone.

Diagnostic. Before extraction, inspect the CC-segment voltage range per cell:

seg = segmentData(parser);
ccMask = seg.CyclingModes == "CC";
vRange = [quantile(seg.(parser.VoltageVariable)(ccMask), 0.05), ...
          quantile(seg.(parser.VoltageVariable)(ccMask), 0.95)];

Fix — pick a chemistry-appropriate window. Pass explicit ranges when constructing the extractor so slope / tInv are computed on a window that actually contains CC samples for every cell:

extractor = batteryTestFeatureExtractor( ...
    CyclingPhase="Discharge", CC=true, ...
    VRange4Slope=[3.0, 3.6], ...            % pick from the CC-voltage-range diagnostic
    VRange4TimeInterval=[3.0, 3.6]);

Multi-cell rule. Compute the CC voltage range for every cell; take the intersection (max(mins), min(maxs)); use that as VRange4Slope and VRange4TimeInterval. This guarantees every cell contributes to slope and tInv on every cycle, so feature counts stay aligned across the batch and cross-cell comparisons are valid.

When the intersection is empty. Cells are on materially different operating windows and cannot share a single feature set. Report to the user and ask whether to (a) group cells and extract separate feature sets per group, or (b) drop slope/tInv (they will not be cross-comparable regardless).

Hidden Parameters (documented for symptom recognition, not user-tunable)

Some internal classifier constants are not exposed as public parser properties. Recognizing their symptoms lets the agent escalate to the user rather than retrying blindly:

ConstantValueWhereSymptom if it's the problem
CurrentTolerance (dI/dt threshold for CC)1e-4 A/sidentifyMode.mHigh-C-rate cells with steppy current: entire CC step marked Undefined.
NumMinConstantPoints (min segment length)10 samplesidentifyMode.mVery short CC segments (< 10 samples) are silently discarded — no CC features for that step.
currentAbsTolerance (CCCV cross-check)0.2 AidentifyMode.mCCCV protocols where the CC current level differs by > 0.2 A from the rounded median: partial CC coverage in the output.

Route to the user with the symptom explicitly named, e.g. "step 4 has 8 CC samples which is below the internal 10-sample minimum — cannot extract CC features for this step. Increase sampling rate on that step or exclude the step."

Differential Curve Parameters (IC/DV/DT quality)

These parameters control the resolution and smoothness of IC (dQ/dV), DV (dV/dQ), and DT (dT/dV) curves. Both the parser pipeline and batteryDifferentialCurves share the same underlying computation; their parameter names differ slightly:

ConceptbatteryTestDataParserbatteryDifferentialCurves
Interpolation resolutionNumInterpolatedPoints (default 1000)NumInterpolatedPoints (default 1000)
Post-computation smoothing windowWindowSize (default 10)PostSmoothingWindowSize (default 10)
Post-computation smoothing methodGaussian (fixed)PostSmoothingMethod (default 'gaussian')
Pre-computation smoothingN/APreSmoothingMethod (default 'none') + PreSmoothingWindowSize (default 10)
CC validation / noise floorHandled by Tolerance (above)NoiseTolerance (default 1e-4) — validates that 95% of samples have `

How they work together:

  1. Raw CC segment data is optionally pre-smoothed (standalone API only)

  2. Data is interpolated to NumInterpolatedPoints evenly-spaced points → sets curve resolution

  3. Differential is computed (dQ/dV, dV/dQ, dT/dV)

  4. Result is smoothed with the post-smoothing window → controls noise suppression

When to adjust:

SymptomLikely causeFix
Spurious small peaks in IC/DV curvesUnder-smoothing — window too small relative to interpolationIncrease WindowSize/PostSmoothingWindowSize (try 15–20)
Real peaks merged or washed out; peak location shiftedOver-smoothing — window too largeDecrease to 5, or switch to 'sgolay' (preserves peak shape better)
IC peaks look artificially sharp or show ringingShort CC segments (<200 raw points) over-interpolated to 1000Reduce NumInterpolatedPoints to ~2–5× the raw point count
IC peaks look blunt, fine structure missingHigh-resolution data (>5 Hz, thousands of raw points) under-sampled by interpolationIncrease NumInterpolatedPoints (try 2000–5000)
batteryDifferentialCurves throws "not CC data" errordI/dt noise exceeds NoiseTolerance — common with noisy current sensorsIncrease NoiseTolerance (try 5e-4 or 1e-3)
Raw data very noisy but you want to preserve peak positionsNoise corrupts interpolation before smoothing can helpEnable pre-smoothing: PreSmoothingMethod='sgolay', PreSmoothingWindowSize=15

Key relationship: WindowSize is a fraction of NumInterpolatedPoints. Default ratio is 10/1000 = 1%. If you change NumInterpolatedPoints, scale the window proportionally to maintain similar smoothing:

% Parser example
parser.NumInterpolatedPoints = 500;
parser.WindowSize = 5;  % keep ~1% ratio

% Standalone example
[dQdV, ~, ~] = batteryDifferentialCurves(V, I, T, t, ...
    NumInterpolatedPoints=2000, PostSmoothingWindowSize=20);

Diagnostic approach: Visually inspect one cycle's IC curve. Peaks should be smooth, clearly resolved, and located at physically meaningful voltages:

% Parser pipeline
diffTable = computeDifferentialCurves(parser, cyclingPhase="Charge");
icData = diffTable.IC{1};
plot(icData.interpolatedVoltage, icData.IC);

% Standalone
[dQdV, ~, ~] = batteryDifferentialCurves(V, I, [], t);
plot(dQdV.interpolatedVoltage, dQdV.IC);

Copyright 2026 The MathWorks, Inc.


Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Security

No issues found by our static scan· No issues found

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
MathWorks
Publisher
MathWorks (Official)
Source
matlab/matlab-agentic-toolkit · skills-catalog/control-systems/matlab-extract-battery-features · commit 2a7f215
Licence
BSD 2-Clause License (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE.md)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by MathWorks or MathWorks.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download matlab-extract-battery-features.zip