Skip to main content
emobility.academy
M.TechBattery & BMSAdvanced About 10 weeksKit + add-ons

Extended Kalman Filter SOC Estimation on STM32

Implement an extended Kalman filter that estimates the kit cell's state of charge from measured current (via an INA219 add-on), terminal voltage and a first-order equivalent-circuit model, running in real time on the STM32F103. It is compared with plain coulomb counting under wrong initial SOC and sensor offset.

Uses: ADC · UART

The problem

Coulomb counting drifts and OCV lookup needs long rests, so production BMS firmware fuses the two through a model-based observer, commonly a Kalman filter. Running such a filter on a low-cost MCU without an FPU raises real engineering questions about numerical precision, cycle budget and tuning. This project builds the filter end to end and measures how much it improves on a baseline.

Objectives

  • Identify a first-order RC model and an OCV–SOC curve for the kit cell.
  • Design and simulate the EKF on a PC with recorded data before porting it to firmware.
  • Implement the EKF on the STM32F103 and measure its execution time and memory use.
  • Compare EKF SOC error with coulomb counting for wrong initial SOC and current offset.
  • Study the effect of noise covariance tuning and numeric format on accuracy.

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
  • Serial UART interface
  • 16×2 LCD with contrast control

Software

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

Extra parts

  • INA219 I2C current and power monitor breakout — The filter's prediction step needs measured current in both directions; the kit lists no current sensor, and estimating current from load resistance would hide the sensor-offset effects the filter 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

First-order RC model
Terminal voltage = OCV(SOC) − I·R0 − V1, where V1 is the voltage across one resistor–capacitor branch representing polarisation. It is the simplest model that captures voltage sag and recovery.
Extended Kalman filter
A recursive estimator that predicts the state with the model, then corrects it using the difference between measured and predicted voltage, weighted by the estimated uncertainties. The 'extended' part linearises the non-linear OCV curve around the current estimate.
Process and measurement noise
Q describes how much the model's state is trusted to drift; R describes measurement noise. Their ratio sets how quickly the filter corrects a wrong SOC versus how much noise it lets through.
Fixed-point versus software float
The Cortex-M3 has no FPU, so float operations are emulated and take many cycles. Q-format integers are faster but need careful scaling to avoid overflow and lost precision.

Step-by-step guide

  1. 1

    Set requirements and baseline

    Define the target: SOC error below a stated bound (for example 3 per cent RMS) after convergence, convergence from a 30 per cent initial error within a set time, and a filter update every 100 ms within a fixed share of CPU time. Define the baseline as coulomb counting with the same current sensor. Plan the test profiles: constant discharge, pulsed profile and a sequence with rests.

    Checkpoint: A requirements table with accuracy, convergence and timing targets and a written baseline definition.

  2. 2

    Install the current sensor and data logging

    With the cell disconnected, insert the INA219 shunt in series with one cell lead so charge and discharge both pass through it, then connect it to the kit's I2C bus. Reconnect the cell checking polarity. Enable DIP switches for battery sense, T-sense, loads, relay, I2C, LCD and UART. Configure a 10 Hz timer that samples current and calibrated voltage and streams them over UART. Never leave charging unattended.

    Checkpoint: A logged 10 Hz stream of voltage, current and temperature with current matching V / R_load within a few per cent.

  3. 3

    Collect identification data

    Measure an OCV–SOC curve with a pulse-and-rest discharge (or reuse one from an earlier project) and record several pulse responses at different SOC levels. Also record two independent validation datasets with a different load profile, kept separate from identification. Store everything as CSV with timestamps and the measured capacity from a full discharge. Note the ambient temperature for every dataset, because model parameters change with temperature and the comparison must stay fair.

    Checkpoint: Identification and validation datasets stored separately, with a measured capacity value.

  4. 4

    Identify the model

    In Python, fit R0 from the instantaneous voltage step at each pulse edge and R1, C1 from the exponential recovery using scipy.optimize.curve_fit. Check whether the parameters vary strongly with SOC; if they do, store them as small tables. Validate the model by simulating the validation profile from measured current and comparing predicted with measured voltage. Keep the parameter tables small enough to store in flash and read cheaply in the filter.

    Checkpoint: Model voltage RMS error on the validation data below a stated bound, for example 15 mV.

  5. 5

    Design the EKF on the PC

    Write the two-state EKF in Python with state x = [SOC, V1]. Prediction uses coulomb counting for SOC and the discrete RC equation for V1; correction uses the voltage equation with the OCV slope dOCV/dSOC from the table. Run it on recorded data starting from wrong SOC values and with an added current offset. Tune Q and R and record their effect on convergence and steady error.

    Checkpoint: PC-side plots showing EKF convergence from a wrong initial SOC while coulomb counting stays wrong.

  6. 6

    Choose the number format

    Implement the update in single-precision float first, then measure its execution time on the STM32 using the DWT cycle counter (DWT->CYCCNT). Then write a Q-format version, with SOC, voltages and covariances scaled separately, and compare cycles and results against the PC reference. With only two states the matrices are tiny, so record whether the fixed-point effort is worth it for this size.

    Checkpoint: A table of cycles per update and maximum difference from the PC reference for float and fixed-point versions.

  7. 7

    Integrate on the board

    Run the filter in the 100 ms tick after reading current and voltage. Keep the covariance matrix symmetric and positive by using a stable update form and clamping SOC to 0–100 per cent. Show EKF SOC and coulomb-counted SOC side by side on the LCD and stream both, with the innovation (measured minus predicted voltage), over UART for analysis. Watch the stack and CPU load while the filter runs, so you know how much headroom is left.

    Checkpoint: Live on the board, the EKF SOC converges after being started from a deliberately wrong value.

  8. 8

    Run the comparison experiments

    Repeat each validation profile with initial SOC errors of −30, 0 and +30 per cent and with a small injected current offset. Use a reference SOC from a full charge and careful counting with the offset removed afterwards. Record RMS and maximum SOC error, convergence time and innovation statistics for both the EKF and the baseline. Run every combination at least twice and keep the order random so drift in room temperature does not favour one method.

    Checkpoint: A results table covering every combination of profile, initial error and offset for both estimators.

  9. 9

    Sensitivity analysis

    Vary Q and R over a decade either side of the tuned values and plot accuracy against convergence time. Perturb model parameters by ±20 per cent to show robustness. Check the innovation sequence: a well-tuned filter has innovations that look like zero-mean noise. Discuss what happens in the flat region of the OCV curve, where the measurement carries little SOC information.

    Checkpoint: Plots of SOC error against Q/R ratio and against parameter error, with written interpretation.

  10. 10

    Write the thesis chapter

    Document the model, identification, filter equations, number-format choice with cycle measurements, experiments and results against the baseline. Include all scripts, firmware and datasets so the results can be reproduced. State the limitations: one cell, a narrow temperature range, low currents and a single model order. Present the results as tables of mean and spread across repeats rather than single best runs, and say plainly where the EKF did not beat the baseline, for example when the initial SOC was already correct.

    Checkpoint: A chapter with reproducible quantified results and a clear comparison with the baseline.

Testing & validation

  • Model voltage error on held-out data.
  • EKF versus coulomb-counting SOC error for several initial errors and offsets.
  • Convergence time from a 30 per cent initial error.
  • Execution time per update measured with the DWT cycle counter, float and fixed-point.
  • Innovation statistics as a check on filter consistency.

Expected results

A real-time EKF SOC estimator on the STM32F103 with measured accuracy, convergence and cycle cost, compared quantitatively with coulomb counting under realistic error sources.

Research angle for your M.Tech thesis

Research questions: how much SOC accuracy does a two-state EKF gain over coulomb counting on a low-cost MCU, and what does it cost in cycles and memory with and without fixed-point arithmetic? Metrics: RMS and maximum SOC error, convergence time, cycles per update, flash and RAM use. Baselines: coulomb counting and OCV lookup. Offline validation on a public dataset such as CALCE strengthens the claims beyond one cell. A careful, reproducible accuracy-versus-cost study on a no-FPU MCU, with released code and data, is the kind of result that is suitable for a conference paper.

Take it further

  • Add a second RC branch and compare accuracy against cycle cost.
  • Replace the EKF with an unscented Kalman filter and compare.
  • Make R0 a slowly estimated state for ageing.
  • Validate the same filter offline on a public dataset such as NASA PCoE or CALCE.

Viva and review questions

Why does the EKF correct a wrong initial SOC but coulomb counting does not?

Coulomb counting only adds changes to its starting value. The EKF compares measured voltage with the voltage the model predicts for its SOC and moves the estimate to reduce the difference.

What do Q and R represent?

Q is the assumed uncertainty in the model's state evolution and R is the voltage measurement noise. A larger Q/R ratio makes the filter trust measurements more and converge faster but noisier.

Why is SOC hard to observe in the middle of the OCV curve?

The OCV slope is small there, so a change in SOC produces little change in voltage, and the measurement update carries little information.

Why did you measure cycles with DWT->CYCCNT?

It counts CPU cycles directly with no instrumentation overhead beyond two reads, giving an exact cost of the filter update on the target.

Related EV projects

All 100 EV projects