The problem
Coulomb counting drifts with sensor errors and cannot recover from a wrong initial SOC, while voltage-based lookup fails under load. Model-based observers such as the EKF fuse both sources of information and are widely used in BMS software. Designing one well means getting the model, linearisation, noise tuning and validation right.
Objectives
- Build a reference cell model and an estimator model with deliberate parameter differences.
- Derive the discrete-time state-space model and Jacobians for the EKF.
- Implement the EKF in a MATLAB Function block suitable for code generation.
- Compare EKF and coulomb counting under initial error, sensor offset and model error.
- Analyse tuning, consistency and observability in flat OCV regions.
What you need
Tools and toolboxes
- MATLAB — Model derivation, Jacobians, tuning scripts and result analysis.
- Simulink — Reference plant, sensor error injection, the EKF in a MATLAB Function block, and the test harness.
- Control System Toolbox — Observability checks on the linearised model (obsv, rank) at several SOC points.
- Simscape Battery (optional) — A higher-fidelity reference plant; a Simulink ECM with different parameters also works.
Software
- MATLAB R2023b or later
- Simulink
- CSV or MAT test data for the chosen cell
Extra parts and data
- Published cell test data (for example a NASA PCoE or CALCE dataset) — To identify the ECM and to validate the estimator on measured current and voltage rather than only on simulated data.
Key concepts
- State-space cell model
- States x = [SOC, V1, V2]. Discrete dynamics: SOC(k+1) = SOC(k) − Ts·I/(3600·Q), Vi(k+1) = exp(−Ts/τi)·Vi(k) + Ri·(1 − exp(−Ts/τi))·I. Output y = OCV(SOC) − V1 − V2 − R0·I.
- Linearisation
- The EKF uses the Jacobian of the output, whose first element is the OCV slope dOCV/dSOC at the current estimate, to compute the Kalman gain.
- Noise covariances
- Q represents model uncertainty in each state and R the voltage-measurement noise. Their ratio decides how quickly the filter corrects SOC and how noisy the estimate is.
- Filter consistency
- A well-tuned filter has innovations (measured minus predicted voltage) that look like zero-mean white noise, with variance matching the predicted innovation covariance.
Step-by-step guide
- 1
Define requirements and test matrix
Set targets: SOC RMS error below 2 per cent after convergence and convergence from a 20 per cent initial error within 10 minutes of driving, for an example 50 Ah cell. Define the test matrix: initial SOC error (−20, 0, +20 per cent), current offset (0 and 0.5 A), capacity error (0 and 5 per cent), and profiles (WLTP-derived current, IDC-derived current, and a measured dataset profile). Define coulomb counting as the baseline.
Checkpoint: A requirements and test-matrix table covering every combination with targets.
- 2
Identify the cell model
Identify OCV–SOC, R0 and two RC branches from pulse data using the method from an ECM identification project, or reuse those tables. Validate on a held-out profile and report voltage RMS error. Then create a reference plant that differs deliberately from the estimator model (for example a higher-fidelity Simscape Battery block or parameters perturbed by 10 per cent) so the test is not unrealistically perfect.
Checkpoint: Model voltage RMS error on held-out data below about 20 mV, and a documented difference between plant and estimator model.
- 3
Derive the discrete model and Jacobians
Write the discrete-time state equations and output equation, and derive the Jacobians A = ∂f/∂x (diagonal for this model) and C = [dOCV/dSOC, −1, −1]. Compute dOCV/dSOC from the OCV table by central differences and store it as its own lookup table. Check observability with Control System Toolbox: build the linearised model at SOC points and compute rank(obsv(A, C)). Keep the derivation in the report appendix.
Checkpoint: The observability matrix has full rank at all SOC points, with its condition number reported and largest in the flat OCV region.
- 4
Implement the EKF
Implement predict and update steps in a MATLAB Function block with persistent state and covariance, sample time 1 s (or 0.1 s). Use the Joseph form for the covariance update to keep it symmetric and positive definite, and clamp SOC to [0, 1]. Avoid dynamic memory and unsupported functions so the block can later be used with Embedded Coder. Output SOC, its standard deviation and the innovation.
Checkpoint: With a perfect model and no errors, the EKF and true SOC agree within 0.5 per cent.
- 5
Build the test harness
Connect the reference plant, a sensor error-injection subsystem (offset, gain, noise, quantisation on current and voltage), the EKF and a coulomb-counting baseline. Log true SOC, both estimates, innovation and the filter's 3-sigma bounds to the Simulation Data Inspector. Drive the test matrix from a script using Simulink.SimulationInput objects. Add a summary block or script that computes RMS error, maximum error and convergence time automatically for every run, so the test matrix results are produced without manual reading of plots.
Checkpoint: One test run produces overlaid plots of true SOC, EKF SOC with 3-sigma bounds and coulomb-counted SOC.
- 6
Tune Q and R
Set R from the voltage sensor noise. Start Q small for SOC and larger for the RC voltages, then sweep the SOC process noise over two decades. For each value, record convergence time and steady-state RMS error on a tuning profile not used for final results. Choose values that meet both targets and check that the error stays inside the 3-sigma bounds most of the time.
Checkpoint: A plot of convergence time against steady RMS error over the sweep, with the chosen tuning marked.
- 7
Run the test matrix
Run every combination in the test matrix with the chosen tuning on the validation profiles. Record RMS and maximum SOC error after convergence, convergence time and innovation statistics for the EKF and the baseline. Summarise in a table and plot the most informative cases, such as +20 per cent initial error with current offset. Repeat noisy cases with several seeds and report mean and spread rather than single runs.
Checkpoint: A complete results table with the EKF meeting the targets in most cases, and any failures explained.
- 8
Consistency and flat-region analysis
Compute the normalised innovation squared and its average over time and compare with the expected chi-square bounds. Plot the innovation autocorrelation. Examine performance in the flat OCV region (for example 30–70 per cent SOC for LFP-like curves) and show how slowly SOC corrects there. Discuss how this links to the observability condition number found earlier. Show how the filter's own uncertainty grows in the flat region.
Checkpoint: Consistency plots and a discussion linking slow correction to small OCV slope.
- 9
Validate on measured data
Run the EKF on a measured dynamic profile from the public dataset using the identified model, with a reference SOC from careful coulomb counting from a known full charge. Start with a wrong initial SOC. Compare results with the simulation study and explain differences, for example due to temperature or hysteresis not in the model. Use the same tuning as in simulation so the test is honest.
Checkpoint: EKF SOC error on measured data reported with convergence from a wrong start.
- 10
Report
Document the model, derivation, observability, implementation, tuning, test matrix, consistency analysis and measured-data validation. State the limits: one temperature, no hysteresis, and a reference SOC that itself has some uncertainty. Release the scripts and models so the results can be reproduced. Present results as tables of mean and spread across repeats, and state plainly where the EKF did not beat coulomb counting, for example when the initial SOC was already correct and the sensors were accurate.
Checkpoint: A report with reproducible, quantified results against coulomb counting across the test matrix.
Testing & validation
- Observability confirmed at all SOC points.
- EKF matches truth with a perfect model and no errors.
- Estimation error stays within the filter's 3-sigma bounds most of the time.
- Normalised innovation statistics within chi-square bounds.
- Results reproduced on measured public data.
Expected results
A Simulink EKF SOC estimator with a quantified comparison against coulomb counting under initial error, sensor offset and model error, a tuning study, consistency checks and validation on measured data.
Research angle for your M.Tech thesis
Research questions: how robust is a second-order ECM-based EKF to model mismatch and sensor faults compared with coulomb counting and an unscented filter, and how should noise covariances be chosen systematically rather than by trial? Baselines: coulomb counting, OCV correction at rest, and an unscented Kalman filter. Metrics: RMS and maximum SOC error, convergence time, consistency (normalised innovation squared) and computation per update. Using public datasets and a plant–model mismatch that is clearly specified makes the comparison reproducible; a systematic tuning method or a robustness analysis across datasets would make it suitable for publication.
Take it further
- Implement an unscented Kalman filter and compare accuracy and cost.
- Add R0 or capacity as slowly varying states (dual or joint estimation).
- Add temperature-dependent parameters.
- Generate code with Embedded Coder and profile it on an MCU.
Viva and review questions
Why does the EKF need dOCV/dSOC?
The measurement update uses the Jacobian of the output with respect to the state; for SOC this is the OCV slope, which sets how much the voltage innovation tells the filter about SOC.
Why use a plant model different from the estimator model?
Testing an estimator on the same model it uses hides model error, which is always present in practice. A mismatched plant gives realistic results.
What does it mean when innovations are not white?
The filter is not using all the information in the measurements, usually because the model is wrong or Q and R are badly tuned.
Why use the Joseph form for the covariance update?
It keeps the covariance symmetric and positive definite despite rounding errors, which matters for long runs and for single-precision code.
Learn it at DIYguru
These DIYguru courses and programmes teach what this project needs.
