This project implements an Extended Kalman Filter (EKF) to estimate the State of Charge (SoC) of a Lithium-Ion battery. It simulates a realistic Battery Management System (BMS) environment, demonstrating how to compensate for sensor noise and voltage relaxation dynamics using sensor fusion.
Accurate SoC estimation is critical for Electric Vehicle (EV) safety and range prediction. Simple voltage reading is insufficient due to the flat OCV curve of Li-Ion cells and internal resistance drops during acceleration.
This project solves this by:
- Modeling the battery physics using a Thevenin Equivalent Circuit (1st Order).
- Simulating real-world driving cycles (UDDS-like current pulses, discharge, regeneration).
- Applying an EKF to fuse current integration (Coulomb Counting) with voltage feedback, correcting initialization errors and sensor noise.
The system state evolves according to:
Where the state vector
-
$x[0] = SoC$ (State of Charge) -
$x[1] = V_{c1}$ (Polarization Voltage across the RC pair)
The measured terminal voltage
The EKF linearizes the
📂 BMS-SoC-Estimator
├── data/
│ └── drive_cycle.csv
├── images/
├── notebooks/
├── src/
│ ├── battery_model.py
│ ├── ekf.py
│ └── utils.py
├── tests/
├── validation/
│ └── validate_model.m
├── main.py
└── requirements.txt
git clone https://github.com/VoIkmer/BMS-SoC-Estimator.git
cd BMS-SoC-Estimator
pip install -r requirements.txtpython generate_data.pypython main.pyThe filter successfully converges from an incorrect initial estimate:
- Real SoC: 80% (0.8)
- EKF initial guess: 50% (0.5)
Figure: Full simulation showing the EKF (Red) correcting the initial error and tracking the Real SoC (Green) despite current fluctuations.
Component Technology Description
Core Logic Python 3.12 EKF implementation Math Engine NumPy Matrices and Jacobians Visualization Matplotlib Plotting Validation MATLAB Cross-check implementation
Coulomb Counting (current integration) is precise in the short term but suffers from drift—small sensor errors accumulate over time, leading to huge inaccuracies after hours of operation. The EKF fuses this integration with voltage measurements to "correct" the SoC estimate in real-time, preventing drift even if the initial guess is wrong.
Figure: Comparison showing Coulomb Counting (Blue) failing to correct an initial error, while EKF (Red) converges to the True SoC (Green).
Lithium-Ion batteries have a very flat OCV (Open Circuit Voltage) curve between 20% and 80% charge. A voltage change of just 0.01V could represent a 10% difference in charge. Additionally, under load, the terminal voltage drops due to internal resistance (
Figure: Example of a flat OCV curve.
This Python project serves as a Model-in-the-Loop (MIL) validation. For embedded deployment, the matrix operations (currently handled by NumPy) would need to be ported to C/C++ using a library like CMSIS-DSP or Eigen. However, the logic and the State-Space matrices remain exactly the same.
The model was cross-validated using a MATLAB script (available in the validation/ folder). The outputs from both Python and MATLAB were compared, yielding a Mean Squared Error (MSE) lower than
Figure: Overlay of Python implementation vs MATLAB reference. The curves are indistinguishable, validating the math engine.
This is the relaxation effect caused by the polarization of Lithium ions. It is modeled in this project by the RC parallel branch (
Figure: The OCV curve of a Li-Ion battery. Note the flat region where voltage barely changes despite huge capacity variation.
Carlos Eduardo
Electrical Engineering Student — UFBA
- Email: cguimaraesbarbosa03@gmail.com
- GitHub: VoIkmer
- LinkedIn: carl0sedu
Licensed under the MIT License.




