Skip to main content
emobility.academy
PhDBattery & BMSResearch About 16 weeksKit + add-ons

Joint SOC–SOH Estimation with Dual Adaptive Kalman Filtering on a Resource-Constrained MCU

Develop a dual adaptive Kalman filter that tracks state of charge on a fast time scale and capacity and resistance on a slow one, running within the STM32F103's memory and cycle budget. Current comes from an INA219 add-on; public ageing datasets provide validation.

Uses: ADC · UART · SPI

The problem

SOC estimators assume a known capacity and resistance, but both change as a cell ages; an estimator tuned for a new cell becomes biased on an old one. Joint SOC–SOH estimation addresses this, yet most published methods run on PCs or high-end processors. Whether they remain accurate and stable on a low-cost, no-FPU MCU with real sensor errors is an open engineering question.

Objectives

  • Formulate a dual estimator: a fast filter for SOC and polarisation and a slow filter for capacity and R0.
  • Add online adaptation of noise covariances and compare with fixed tuning.
  • Implement the method on the STM32F103 in fixed point and characterise cycles, memory and numerical stability.
  • Validate accuracy offline on public ageing datasets and on the kit cell with deliberate capacity errors.
  • Compare against single-filter and fixed-capacity baselines.

What you need

From the EV Embedded Development Kit

  • STM32F103C8T6 microcontroller unit (Arm Cortex-M3)
  • 1S battery with connector and T-sense input
  • Load section: three switchable resistive loads with LEDs
  • Battery charging unit with its own switch
  • Charge/discharge control relay
  • I2C and SPI buses to the onboard devices
  • SPI flash memory
  • Serial UART interface

Software

  • STM32CubeIDE
  • Embedded C with STM32 HAL
  • Python with NumPy, SciPy, pandas and matplotlib
  • MATLAB/Simulink (optional, for reference implementations)

Extra parts

  • INA219 I2C current and power monitor breakout — Both filters are driven by measured current in charge and discharge, and the kit lists no current sensor. Realistic sensor offset and noise are part of what the research must handle.

This project uses the kit's lithium cell. Respect polarity, never short the terminals, and don't leave the cell charging unattended.

Key concepts

Dual estimation
Two filters run together: one updates fast states every sample, the other updates slowly varying parameters at a lower rate using the first filter's results. It separates time scales and keeps each filter small.
Adaptive covariance
Q and R are re-estimated from the innovation sequence over a moving window, so the filter adjusts when noise or model error changes rather than relying on fixed tuning.
Observability of capacity
Capacity is only visible when SOC changes significantly; during rest or small swings the capacity estimate should barely move. Good designs gate the slow update on enough charge throughput.
Numerical stability in fixed point
Covariance updates can lose symmetry or positive definiteness with rounding. Joseph-form updates, scaling and clamping help keep a fixed-point filter stable over long runs.

Step-by-step guide

  1. 1

    Define research questions and protocol

    Write precise questions: does dual adaptive estimation keep SOC error within a target on an aged cell when capacity is initialised wrongly, and what is its cost on a Cortex-M3? Define metrics, baselines (fixed-capacity EKF, joint augmented-state EKF) and datasets: the NASA PCoE battery set and CALCE ageing data for offline work, and kit-cell experiments for embedded validation. Pre-register the evaluation so results are not tuned after the fact.

    Checkpoint: A protocol document with questions, metrics, baselines, datasets and evaluation splits.

  2. 2

    Build the offline reference implementation

    In Python, implement the cell model (OCV table plus 1RC or 2RC), the fast filter for SOC and V1, and the slow filter for capacity and R0, updated after each block of sufficient charge throughput. Run it on the public datasets with ground-truth capacity from their reference tests. Add adaptive Q and R from innovation statistics and compare with fixed tuning on the same data.

    Checkpoint: Offline results on at least one public dataset showing SOC and capacity tracking against ground truth for both fixed and adaptive versions.

  3. 3

    Prepare the hardware bench

    Disconnect the cell, install the INA219 shunt in series with one cell lead and connect it to the kit's I2C bus, then reconnect checking polarity. Enable DIP switches for battery sense, T-sense, loads, relay, I2C, SPI flash and UART. Configure synchronised 10 Hz sampling of current, voltage and temperature with logging to SPI flash and UART. Never leave charging unattended.

    Checkpoint: A multi-hour log stored in flash and dumped to the PC without gaps.

  4. 4

    Characterise the kit cell

    Measure capacity by full discharge, an OCV–SOC curve by pulse-and-rest and RC parameters from pulses, using the same procedures as the offline model. Measure current-sensor offset and noise statistics. These measurements are the ground truth against which the embedded filter's capacity and R0 estimates are judged. Repeat the capacity test at the start and end of the experimental campaign to detect any change during the work. Keep these characterisation data separate from the data used to evaluate the filters.

    Checkpoint: Measured capacity, OCV table, RC parameters and sensor noise statistics with uncertainties.

  5. 5

    Design the fixed-point implementation

    Choose Q-formats for each state, parameter and covariance entry based on their ranges, using the offline runs to find maximum values. Use a Joseph-form or square-root update for numerical robustness. Write the filters in portable C that compiles on the PC and on the STM32, so the same code can be tested against the Python reference bit for bit where possible.

    Checkpoint: PC-compiled fixed-point C matches the Python reference within stated tolerances on public data.

  6. 6

    Port to the STM32 and profile

    Run the fast filter in a 100 ms timer task and the slow filter in the background when the throughput gate opens. Measure cycles per update with the DWT cycle counter, stack use by stack painting and flash and RAM use from the map file. Compare a software-float version against fixed point for cost and accuracy. Check that the worst case fits within the 100 ms period with clear margin for other BMS tasks.

    Checkpoint: A profile table: cycles per fast and slow update, worst-case CPU load, RAM and flash use, for float and fixed point.

  7. 7

    Embedded experiments on the kit cell

    Start the filter with capacity deliberately wrong by ±20 per cent and initial SOC wrong by 30 per cent. Run multiple charge and discharge cycles with varied load profiles. Record SOC error against a carefully counted reference, capacity and R0 estimates against measured values, and innovation statistics. Repeat with and without covariance adaptation. Use the same random order of configurations across days so ambient changes do not favour one variant over another.

    Checkpoint: Plots showing capacity converging towards the measured value and SOC error for each configuration.

  8. 8

    Long-run stability tests

    Run the embedded filter continuously for many hours, including long rests and small SOC swings, and look for covariance growth, loss of positive definiteness or drift in the slow states. Feed recorded public-dataset currents and voltages to the board over UART as a hardware-in-the-loop test, so the embedded code sees full ageing trajectories. Log covariance diagonal values and their minimum eigenvalue estimate over time so any numerical trouble is visible before it causes a failure.

    Checkpoint: Hardware-in-the-loop runs over full public-dataset ageing trajectories with no numerical failure.

  9. 9

    Compare with baselines

    Run the fixed-capacity EKF and an augmented-state joint EKF on the same data and hardware. Compare SOC RMS and maximum error, capacity error, convergence time, cycles and memory. Use statistical tests across cycles or cells to show whether differences are significant rather than chance. Tune each baseline with the same effort and the same identification data as the proposed method, and report the tuning procedure, so the comparison is fair and cannot be criticised for using a weak baseline.

    Checkpoint: A comparison table with significance tests across all methods and datasets.

  10. 10

    Prepare publication-ready outputs

    Write the method, implementation details, numerical-stability findings, experimental results and comparisons. Release code and the kit-cell dataset in a form others can rerun. Discuss limitations: one physical cell, low C-rates and a narrow temperature range on the bench, and how the public datasets cover part of that gap. Write the related-work section by comparing your platform, metrics and results with published joint estimators, noting where the hardware or datasets differ so readers can judge the comparison fairly.

    Checkpoint: A thesis chapter and paper draft with reproducible figures generated from released scripts.

Testing & validation

  • Offline SOC and capacity accuracy on NASA PCoE and CALCE data against their reference capacities.
  • Embedded SOC error and capacity convergence with deliberate initial errors.
  • Bit-level or tolerance-level match between fixed-point C and the Python reference.
  • Cycle, RAM and flash budgets measured on target.
  • Long-run numerical stability in hardware-in-the-loop tests.

Expected results

A dual adaptive SOC–SOH estimator running on a no-FPU MCU, with quantified accuracy on public ageing data and on a real cell, measured computational cost, and a comparison against standard baselines.

Research angle for your PhD

Research questions: does dual adaptive estimation remain accurate and numerically stable on a 72 MHz no-FPU MCU, and how does covariance adaptation affect robustness to sensor offset and ageing? Metrics: SOC RMS and maximum error, capacity estimation error, convergence time, cycles per update, RAM and flash. Baselines: fixed-capacity EKF and augmented-state joint EKF. Datasets: NASA PCoE and CALCE for ageing trajectories, plus the kit bench for real sensor effects and embedded execution. A publishable contribution would be a rigorous accuracy-versus-cost and stability study with open code and data, or a fixed-point formulation that is shown to be stable where a naive one is not.

Take it further

  • Add temperature as a model input and adapt parameters with it.
  • Compare with a lightweight data-driven capacity estimator trained offline.
  • Extend to a multi-cell scenario in simulation with cell-to-cell variation.
  • Study the effect of current-sensor offset adaptation as an extra slow state.

Viva and review questions

Why separate SOC and SOH into two filters?

They change on very different time scales. Separating them keeps each filter small and lets the slow filter update only when there is enough information.

When is capacity observable?

Only when SOC changes enough; with small swings the voltage response carries little information about capacity, so the slow update is gated on charge throughput.

How do you keep a fixed-point covariance update stable?

By using numerically robust forms such as the Joseph form, choosing scales from measured ranges, enforcing symmetry and clamping to keep the matrix positive.

Why use public datasets?

They contain full ageing trajectories with reference capacity tests, which a single kit cell cannot provide during a doctoral timeline.

Related EV projects

All 100 EV projects