Here is what I have observed across BMS development and deployment on commercial EV platforms: when a BMS fails in the field, the root cause is almost never the choice of algorithm.
It is a 50 mA shunt offset that was never calibrated. A temperature sensor on the edge cell while the center cell runs 7°C hotter. A Q_max value pulled from the datasheet on day one that nobody updated in two years of fleet operation. A DBC file with the wrong byte order that the vehicle controller team worked around and never told anyone.
The algorithms — Coulomb counting, EKF, passive balancing — are well understood. They work. The failures happen in the implementation details that don't appear on the block diagram.
This article is about those details.
LFP SOC Estimation Is Broken By Design — And Everyone Ships It Anyway🔗
The OCV-SOC curve for LFP has a plateau between roughly 20–80% SOC where the voltage changes by approximately 20–40 mV across a 60% SOC range. At a typical AFE measurement accuracy of ±2 mV, this means the OCV measurement alone can only resolve SOC to within ±5–8% in the plateau region.
The Kalman filter knows this. When dV/dSOC → 0, the Kalman gain K → 0, and the filter stops trusting voltage measurements. The SOC estimate in the plateau is effectively Coulomb counting with a thermodynamic correction that contributes almost nothing.
This is not a solvable problem with a better algorithm. It is a property of the chemistry.
What this means in practice: For an LFP pack, everything that affects Coulomb counting accuracy directly affects SOC accuracy during normal operation. The shunt is more important than the filter. Specifically:
A 50 mA DC offset on a 100 Ah cell accumulates 1.2 Ah per day of parking — 1.2% SOC drift per day. After a week of low-utilization parking (common in fleet vehicles over weekends), the displayed SOC can be off by 8% before the vehicle leaves the yard Monday morning.
Zero-current detection threshold must be set aggressively. If the BMS considers anything below 200 mA as "zero current" and stops integrating, but the actual quiescent draw (BMS itself, telematics, wake-up circuits) is 150 mA, it is silently accumulating error.
Q_max must be updated. If Q_max is 90 Ah at BOL and the cell is now delivering 83 Ah, a Coulomb count that normalizes by 90 Ah will show SOC = 92% when the cell is actually at 100% physical SOC. This causes premature charge termination and OCV corrections that land at the wrong point on the lookup table.
The OCV correction at key-off is the only reliable reset for LFP SOC. For it to work, the pack needs at least 15–30 minutes of zero-current rest — not 2 minutes. Many BMS implementations use a 2–5 minute rest threshold because longer rest delays the next vehicle dispatch. The SOC correction they get is partial. They accept the residual error.
image source : A Brief Review of Different Estimation Methods of SOC for Li-ion Battery | Springer Nature Link
Fixed Q_max Is a Slow Lie🔗
The single most common BMS implementation error in commercial deployments: Q_max is set once from the cell datasheet at commissioning and never updated.
A new LFP cell might deliver 90 Ah at 25°C, C/3 discharge, fresh from the factory. After 800 cycles at a mixed commercial duty profile, it delivers 83 Ah. After 1500 cycles, 76 Ah.
A BMS with fixed Q_max = 90 Ah in year three is normalizing all SOC calculations against a capacity that no longer exists. The SOC axis is stretched. 100% SOC on the BMS display corresponds to filling an 83 Ah cell — but the BMS thinks it's a 90 Ah cell, so it calls it 92%. The remaining 8% is phantom capacity the driver is promised but doesn't get.
Over a fleet of 50 buses, this translates directly into route planning failures and operator complaints about range — which get escalated as warranty claims about battery degradation, when the actual issue is a Q_max calibration problem that costs nothing to fix.
The fix is not complicated. Track Coulomb counting across complete or near-complete cycles (ΔSOC > 30%). Compute Q_actual = ΔAh / ΔSOC for each such cycle. Apply a low-pass filter over the last 10–20 such measurements. Update Q_max quarterly or every 50 cycles. This adds maybe 20 lines of firmware and a single persistent memory write. Almost nobody does it.
The EKF Tuning Nobody Talks About🔗
The Kalman filter has two tuning matrices: process noise covariance Q and measurement noise covariance R.
The textbook describes Q as reflecting uncertainty in the process model and R as reflecting measurement noise. In practice, both are tuning knobs, and they have one correct answer: they must vary with operating conditions.
A fixed Q calibrated for steady highway driving is wrong for aggressive commercial duty cycles. Under full-load acceleration on a grade — 400A+ discharge on a 96S pack — the cell model diverges from reality because the ECM parameters (R0, R1, R2, C1, C2) were characterized at C/3 and now the cell is at 4C. The model error is high. Q should be large — trust the measurement more.
During a 30-minute rest at key-off, the model error is low and the voltage measurement is extremely informative (OCV conditions). Q should be small, R should be small — trust everything.
Most implementations ship with a single fixed Q and R calibrated on a benchtop at C/3. In the field, the filter is consistently over-trusting or under-trusting depending on the duty cycle.
The adaptive EKF (AEKF) addresses this by adjusting Q and R based on the innovation sequence — the difference between predicted and measured voltage over the last N timesteps. Large sustained innovation → the model is wrong → increase Q. Small innovation → model is accurate → decrease Q.
The innovation-based adaptation adds perhaps 50 lines of firmware. The improvement in SOC accuracy on aggressive duty cycles is measurable and real — typically ±2–3% SOC tighter than a fixed-parameter EKF on variable load profiles.
Image : Created from Claude
Temperature Sensor Placement Is a Structural Decision That Gets Made Too Late🔗
A typical commercial battery pack has 1 NTC thermistor per 8–12 cells. In a 96S1P pack, that is 8–12 sensors across what might be 200–300 kg of cells arranged in a specific 3D geometry.
The thermal gradient across a forced-air or liquid-cooled pack under 2C discharge is not uniform. Edge cells adjacent to cooling plates run 4–6°C cooler than geometric-center cells. The actual maximum temperature in the pack at any moment is not what any single sensor reads — it is an interpolated estimate with significant uncertainty.
The consequence: thermal protection thresholds are set based on sensor readings, not actual cell temperatures. If the hottest cell runs 7°C above the nearest sensor, and the protection threshold is 60°C, the BMS will allow continued operation up to a sensor reading of 60°C — which corresponds to an actual cell temperature of 67°C in the center.
This is not a minor margin violation. 67°C sustained in an LFP cell accelerates electrolyte oxidation significantly. Above 70°C, SEI film decomposition begins.
The fix requires adding sensors in the right locations during pack mechanical design — not during BMS firmware development. This is a structural decision. By the time the BMS team flags insufficient sensor coverage, the pack tooling is finalized and adding sensor mounting points costs more than anyone wants to spend.
The right answer: in the thermal analysis phase of pack design, simulate the temperature distribution at worst-case current load. Identify the hottest predicted locations. Put sensors there. Not on the edge cells where the wiring is convenient.
Contactor Weld Detection Is Usually Wrong🔗
Most BMS implementations run weld detection once — at key-on, before closing the contactors. The logic checks whether pack voltage appears on the bus side before the contactors are commanded closed. If it does, a contactor is welded.
This catches welds that exist before the drive cycle. It does not catch welds that occur during the drive cycle — which is exactly when high-current events that cause weld failures happen.
A contactor weld mid-operation means:
The positive and negative contactors cannot both open on command
The battery cannot be isolated in an emergency
If a second fault occurs — say, a ground fault — there is no electrical isolation
The detection gap is a design choice driven by the complexity of mid-operation detection. Checking for weld during operation requires monitoring bus voltage decay after commanded opens and current sensor readings after cutoff — in a system that may be simultaneously managing braking events, thermal alerts, and CAN traffic.
The minimum credible implementation: Run weld detection at key-off as well as key-on. After commanding contactors open at end of drive cycle, verify that bus voltage decays within the expected RC time constant. If it doesn't — held up by current through a welded contact — flag the fault before the vehicle is released for the next dispatch.
A welded contactor found at key-off is a maintenance alert. A welded contactor found mid-operation by a protection trip is a safety incident.
Passive Balancing That Never Runs🔗
Passive balancing operates at high SOC — typically above 90–95% — where cell voltage differences are most visible and the correction target is clear.
In a fleet of electric buses doing opportunity charging (top-up charges during layovers rather than full overnight charges), the pack rarely reaches 95% SOC. It cycles between 30–70% SOC. The balancing algorithm's trigger condition is never met.
Over 6–12 months, imbalance accumulates. Cells that aged slightly faster drift 3–5% SOC below the pack average. The weakest cell terminates discharge for the entire pack. Usable range shrinks — not because of capacity fade, but because of correctable imbalance that the BMS never addressed.
The fix: implement a periodic full charge specifically for balancing — once per week or per month, regardless of operational schedule. BMS firmware supports this. Fleet operations teams often don't know it exists as an option.
An alternative: run passive balancing continuously whenever ΔSOC across cells exceeds a threshold, not just at top of charge. Some AFE ICs support this natively. It allows balancing to happen during partial charge events. The dissipated power is trivial — 100 mA at 3.3V is 0.33W per cell — but the cumulative effect over a week is significant.
DCIR Trends Without Temperature Normalization Are Noise🔗
DC Internal Resistance is one of the most useful in-field SOH indicators available. It is also the most misinterpreted metric in fleet telematics dashboards.
DCIR varies with temperature following an approximate Arrhenius relationship. For a typical LFP cell, DCIR at 0°C is 2.0–2.5× higher than at 25°C. For NMC, similar magnitude.
A DCIR trend extracted from field data without temperature normalization will show:
Sharp resistance increases every winter
Sharp resistance decreases every summer
A long-term trend buried underneath that actually reflects degradation
Operations teams see the winter spikes and file degradation alerts. The BMS vendor explains it is seasonal. This conversation happens repeatedly. The real degradation signal — which is there, increasing gradually under the seasonal noise — is missed until it becomes large enough to dominate despite the temperature variation.
The normalization approach: For each DCIR measurement logged from the pack, record the concurrent temperature. Apply an Arrhenius correction to normalize to 25°C:
Where Ea for LFP is approximately 0.2–0.3 eV, R is 8.314 J/mol·K, and T is in Kelvin.
Plot normalized DCIR over time. The seasonal noise disappears. The degradation trend becomes visible. This is the only way to use DCIR for SOH monitoring in a fleet that operates year-round.
SOP Estimates That Ignore RC Polarization🔗
State of Power — the maximum charge and discharge power the BMS broadcasts to the vehicle controller — is typically calculated from a simplified model:
CopyI_max = (V_OCV − V_min_limit) / R0
Where R0 is the ohmic (instantaneous) resistance and V_min_limit is the per-cell minimum voltage.
This model ignores the RC polarization voltage — the slow-building voltage drop across the R1-C1 and R2-C2 pairs in the equivalent circuit. These polarization terms build up over the duration of a high-current pulse and can add 50–100 mV per cell at sustained 2C currents.
The consequence: the BMS broadcasts a discharge SOP based on R0 alone. The vehicle controller pulls that current. Initially the cell voltage is above the limit. But as polarization builds over 5–10 seconds, the terminal voltage drops below V_min_limit. The hardware protection trips — a voltage fault — and the vehicle loses power mid-maneuver.
From the vehicle controller's perspective, the BMS sent a valid SOP limit and then tripped before the vehicle could draw to that limit. It files a warranty claim. The root cause is a SOP calculation that underestimates the polarization component.
Where V_RC1 and V_RC2 at t = 10s are estimated from the current RC state and the first-order dynamics. This requires the BMS to maintain the RC state in the SOC estimator — which it already does if it's running an EKF — and use it in the SOP calculation.
The CAN DBC File Is Not a Formality🔗
The DBC file defines the encoding of every signal on the CAN bus: start bit, length, byte order (Intel vs Motorola), scaling, offset, units. It is the interface contract between the BMS and every other system in the vehicle.
The failure mode: BMS sends signals in Motorola byte order. Vehicle controller was told Motorola, implemented Intel. Integration testing on day one shows garbage values. Developer adds a byte-swap in the vehicle controller firmware and marks the issue as resolved. Nobody updates the DBC file.
Eighteen months later, a different vehicle controller supplier is brought in for a new platform. They implement from the DBC file — which still says Motorola. Their signals are wrong. Two weeks of debug time before anyone checks byte order.
The principle: the DBC file is the single source of truth. It must reflect exactly what the BMS firmware transmits — not what anyone intended, not what was documented in a meeting, not what worked with one specific partner. Any deviation between the DBC file and the firmware is a latent integration bug.
Keep it under version control alongside the firmware. Tag releases together. Make sign-off on the DBC file part of the system integration gate review. This sounds like process overhead. It pays for itself the first time it prevents a two-week debug session.
Thermal Runaway: The BMS Cannot Save You, But It Can Give You Time🔗
Thermal runaway in a lithium-ion cell is a self-sustaining exothermic reaction. Once it begins, the heat generated by decomposition reactions exceeds the heat that can be dissipated — and the reaction accelerates. Cell temperatures can reach 600–900°C within seconds. Adjacent cells are heated above their own thermal stability thresholds. Propagation follows.
The BMS cannot stop this once it begins. By the time cell temperature is rising at several degrees per second, the contactor open command buys seconds — not minutes. The relevant containment happens at the pack structural and module level (venting paths, propagation barriers, thermal isolation) — not in firmware.
What the BMS can do is detect precursors early enough to allow a controlled response:
dT/dt monitoring: A cell with an early-stage internal short circuit generates excess heat before any voltage anomaly is visible. Monitor rate of temperature rise per cell. Any cell showing sustained dT/dt > 1°C/s under conditions where the load model predicts less than 0.3°C/s increase should trigger a Level 3 derating response — not a Level 4 trip, which cuts power abruptly, but a rapid ramp-down and isolation with driver alert.
Voltage divergence during rest: A cell with an active internal short self-discharges faster than its neighbors. During a long parking event, plot each cell's voltage decay rate. A cell decaying 50% faster than the pack average warrants investigation — not necessarily immediate action, but a maintenance flag.
The hard constraint on the firmware response: Once a thermal runaway precursor is confirmed, the BMS should: open contactors, broadcast fault on CAN with maximum severity, and stop. Do not attempt to "manage" the event by modulating power or selectively bypassing cells. There is nothing to manage. The electrical isolation is the only BMS action that matters. Everything else is the vehicle's physical containment design.
Key Takeaways🔗
The algorithm is rarely the problem. Shunt calibration, temperature sensor placement, Q_max updates, and DBC file discipline cause more field failures than algorithm choice.
LFP SOC accuracy lives and dies on the shunt. The OCV plateau makes voltage-based correction nearly useless during normal operation. A 50 mA offset error accumulates 1.2% SOC drift per day. This is not theoretical — it shows up in fleet SOC displays every Monday morning.
Q_max must be updated dynamically. A fixed Q_max from the datasheet is wrong by design after 12 months in service. It causes phantom SOC errors that look like degradation.
Weld detection at key-on only is insufficient. Run it at key-off too. A welded contactor found at maintenance is a maintenance event. Found during a safety incident, it is a liability.
DCIR trends without temperature normalization are not SOH data. They are seasonal temperature trends with SOH buried underneath. Apply Arrhenius normalization before plotting anything.
SOP that ignores RC polarization will trip protection at the limit. Include polarization voltage prediction in the 10-second SOP calculation or accept recurring mid-operation protection trips that generate false warranty claims.
The BMS cannot extinguish thermal runaway. Its job in that scenario is isolation and alerting. That is all. Design the pack containment accordingly.
Waag, W. et al. (2014). Critical Review of the Methods for Monitoring of Lithium-Ion Batteries in Electric and Hybrid Vehicles. Journal of Power Sources, 258, 321–339. https://doi.org/10.1016/j.jpowsour.2014.02.064
Dubarry, M. et al. (2012). Identify Capacity Fading Mechanism in a Commercial LiFePO4 Cell. Journal of Power Sources, 214, 46–56.
SAE International. (2015). SAE J1939-21: Data Link Layer.
Feng, X. et al. (2019). Thermal Runaway Mechanism of Lithium Ion Battery for Electric Vehicles. Energy Storage Materials, 10, 246–267. https://doi.org/10.1016/j.ensm.2017.05.013
This is the Expert level of the VoltPulse BMS series.
→ Next: BMS Design Under Uncertainty — Master — parameter uncertainty, multi-chemistry state estimation, electrode slippage detection, and the limits of model-based approaches in deployed systems.
Published on VoltPulse — the most technically rigorous source for battery technology and EV engineering coverage.
How was this article?💡Learned something🔖Saving this🤔Need more context
SD
Written by
Sai Chaitanya Dasari
Battery Systems Engineer | Volvo Eicher Commercial Vehicles
3+ years in commercial EV pack development. Writing about real battery engineering from the bench.