Battery SOC EKF
AdvancedThe Battery SOC EKF block estimates the state of charge of a battery cell from two signals
a battery management system actually has: the current and the terminal voltage. It runs an
extended Kalman filter on a one- or two-RC equivalent circuit, so it corrects the drift of plain
coulomb counting with the voltage, and the voltage's polarisation lag with the model. It
can also learn the offset of the current sensor — the error that makes coulomb counting
drift — and it reports its own uncertainty, so a BMS can show SOC ± a margin.
The open-circuit voltage curve is a table, evaluated either exactly like the LookupTable
block (linear) or as a monotone cubic without slope jumps (smooth). The filter
re-linearises that curve at its current estimate every sample, which is what keeps it
unbiased where a linear Kalman filter linearised at one point is not.
Mathematical Model
Where:
I_mis the measured current in A, positive when discharging, andVthe terminal voltage in VQis the capacity in Ah,ηthe charge efficiency while charging (1 when discharging)- The discretisation — noise included — is recomputed from the parameters, so the filter
follows a changed c1 or sampleTime and does not change character when Ts does
His the local OCV slope at the predicted SOC; prediction and correction are those of
the Kalman Filter block with this H
- A branch with R = 0 is absent, and with
pBias = qBias = 0the offset stays exactly 0:
with the defaults (R2 = 0, no offset) this is the classic two-state EKF
- One iteration is the classic EKF; more relinearise at the corrected estimate (IEKF)
Inputs & Outputs
| Direction | ID | Label | Type | Status |
|---|---|---|---|---|
| → In | in1 |
I | number | Required |
| → In | in2 |
V | number | Required |
| → In | in3 |
Reset | number | Optional |
| → In | in4 |
Dropout | number | Optional |
| ← Out | soc_hat |
SOC | number | Output |
| ← Out | soc_sigma |
σ SOC | number | Output |
| ← Out | v1_hat |
V₁ | number | Output |
| ← Out | v2_hat |
V₂ | number | Output |
| ← Out | bias_hat |
I bias | number | Output |
| ← Out | voc_hat |
OCV | number | Output |
| ← Out | jacobian |
dOCV/dSOC | number | Output |
| ← Out | k_gain_0 |
K_SOC | number | Output |
| ← Out | innovation |
Innov. | number | Output |
| ← Out | innovation_cov |
S | number | Output |
| ← Out | nis |
NIS | number | Output |
Parameters
| Parameters | Label | Type | Default | Description |
|---|---|---|---|---|
sampleTime |
Sample Time | number | 1 |
Filter period in seconds |
socInit |
Initial SOC Estimate | number | 0.5 |
Fraction 0…1 |
v1Init |
Initial V1 Estimate | number | 0 |
|
pSoc |
Initial SOC Variance | number | 0.01 |
How unsure the initial SOC is; 0.01 is σ = 10 % SOC |
pV1 |
Initial RC Voltage Variance | number | 0.000001 |
Per RC branch; small, since a rested cell has V1 ≈ V2 ≈ 0 |
pBias |
Initial Bias Variance | number | 0 |
How unsure the current sensor's offset is; 0 leaves the offset out of the model, 0.01 is σ = 0.1 A |
qSoc |
Process Noise SOC | number | 1e-8 |
Noise density per second, so the filter does not change with the sample time |
qV1 |
Process Noise RC Voltage | number | 0.000001 |
Noise density per second, per RC branch |
qBias |
Process Noise Bias | number | 0 |
How fast the sensor offset may drift, as a density per second; 0 for a constant offset |
rMeas |
Measurement Noise | number | 0.0001 |
Variance of the voltage measurement in V² |
r0 |
R0 (Ω) | number | 0.05 |
Ohmic resistance |
r1 |
R1 (Ω) | number | 0.02 |
Polarisation resistance of the fast branch |
c1 |
C1 (F) | number | 1000 |
Polarisation capacitance of the fast branch |
r2 |
R2 (Ω) | number | 0 |
Resistance of a second, slow RC branch; 0 leaves the branch out |
c2 |
C2 (F) | number | 30000 |
Capacitance of the second RC branch |
capacity |
Capacity (Ah) | number | 3.2 |
|
chargeEfficiency |
Charge Efficiency | number | 1 |
Coulombic efficiency while charging (current < 0); Li-ion cells are typically 0.99–0.999 |
ocvSoc |
OCV Table SOC | text | 0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1 |
SOC breakpoints, ascending |
ocvVoltage |
OCV Table Voltage | text | 3.00 3.50 3.65 3.72 3.78 3.84 3.90 3.97 4.05 4.14 4.20 |
Open-circuit voltage at each breakpoint in V |
ocvInterpolation |
OCV Interpolation | select | linear |
Linear matches a LookupTable exactly; Smooth (monotone cubic) has no slope jumps at the breakpoints, which steadies the filter's gain |
iterations |
Update Iterations | number | 1 |
1 is the classic EKF; 3 relinearises at the corrected estimate (iterated EKF), which helps where the OCV curve bends sharply |
Usage Examples
Finding the SOC of a Resting Cell
A rested cell at 3.72 V (30 % SOC), read by a noisy voltage sensor (σ = 50 mV) at 10 Hz.
The filter starts at 60 % and stays within 2 % of the true 30 % from 1.5 s on; its
soc_sigma output shrinks from 5.8 % to 0.8 % as the measurements accumulate.
Remarks & Best Practices
- SOC σ:
SOC ± 2σis a 95 % band if the model is right; NIS checks that — its
time average is 1 for a consistent filter, clearly above 1 when it is overconfident.
- Valid input: when the voltage reading drops out the filter coulomb-counts only, and
σ grows until readings return.
- Outside the table the curve is extended along its end slope, so the slope the filter
uses never drops to zero there — a flat curve would make SOC unobservable.
- A table whose SOC points are not ascending, or whose two rows differ in length, falls
back to the default table.
- Convergence depends on where the filter starts. From 50 % to a true 15 % with the linear
curve the plain EKF overshoots into the steep tail on its first correction, and the
covariance it spent on that jump keeps later corrections small: about a minute, where
most starting errors take a few seconds. Set iterations to 3 to remove that.
- Verified against an independent NumPy/SciPy implementation to 1e-9, for both curves and
with the offset state, a dropout, charging, a second RC branch and the iterated update
(scripts/kalman-reference.py), and by a Monte-Carlo NIS consistency test.
- Not modelled: temperature, hysteresis, capacity fade.
- Reset (> 0) restarts from
socInit,v1Initand the initial variances.
Related Components
- KalmanFilter" class="bw-link">KalmanFilter
- LookupTable" class="bw-link">LookupTable
- RandomNumber" class="bw-link">RandomNumber