Use Case: A Linear Battery Model using The Kalman Filter

Hahnsang Kim
9 min readMay 8, 2024

What do you do when making dough? Yes, we sift the flour through a mesh strainer to make it fine-grained. Have you ever noticed that the shape of the mesh determines the shape of the piled flour beneath it? When the mesh is pointy, the piled flour is pointy, and when it’s round, the piled flour is also round. The Kalman Filter is analogous to a mesh strainer, with the grains of the flour representing measurement data points. The center of the piled flour represents an expected state, and the degree to which it is spread represents the variance of the state. The expected state is updated based on a system of intent, known as a state transition function, while the variance is updated with measurements over time. It’s like intentionally moving the strainer and reshaping the mesh.

The figure below visually demonstrates how a Kalman filter updates our estimation of a state. Initially, we have updated information about the state’s location, symbolized by the first curve on the left. This situation is akin to using a wide mesh strainer that lets many possibilities through, reflecting uncertainty about the expected state.

Kalman Filter as a probability density function

As we move to the prediction phase (1. predict: middle black curve), we use a model to predict the next state and its variance. Subsequently, we receive a new piece of information from measurements, illustrated by the orange dots (2. measurement: orange curve), which is like getting a more precise clue about the location. Finally, in the update phase (3. update: blue curve), we update our prediction with this new information, narrowing down the possibilities and thus sharpening our estimate. This final, narrower curve indicates increased confidence in the state, much like using a finer mesh strainer to sift through data and retain the most probable outcomes. Each step in the Kalman filter process methodically refines our guesses, improving the accuracy and reliability of our estimation over time.

Applied Kalman Filter

As you notice, the Kalman Filter is a powerful statistical tool used for estimating the state of a linear dynamic system over time from incomplete and noisy measurements. In particular, it is widely used to track the status of lithium-ion batteries in battery management systems (BMS) for electric vehicles, energy storage systems, and robotics, among other applications.

How Kalman Filter Works

The Kalman Filter operates in a recursive process, including prediction and update phases. In the prediction phase, it statistically estimates the next state along with its estimated covariance, given the previous updates, where x^ is the state and P^ is the covariance. Based on these estimates, an instance of measurements is predicted. In the update phase, we read noisy measurements and compare them with predicted ones. Then, the state and the covariance are updated based on the results of this comparison. These prediction and update phases are repeated.

The figure below outlines the computational steps to break down these two phases. The horizontal line at the top represents the progression of time, from a time step t=k−1 to the next time step t=k.

Recursive Process of Kalman Filter

The variables used are:

  • x^(k): The predicted state estimate at time k given information up to time k−1.
  • x(k): The updated state estimate at time k after measurement has been incorporated.
  • u(k): The control input at time k.
  • y(k): The measurement at time k.
  • P: The covariance of the state estimate which indicates the uncertainty of the state estimate.
  • A, B, C, D: Matrices that relate the state, input, and measurement to the next state and output.
  • w, v: The process and measurement noise respectively.

The Prediction phase uses the previous state to predict the next state at time t=k.

  • Step 1: Predict the state x(k)​ using the state transition model involving the state x^(k−1), control input u(k−1), and process noise w.
  • Step 2: Predict the covariance P(k)​ of the state estimate using the previous covariance P(k−1), the state transition matrix A, and the process noise w.
  • Step 3: Compute the measurement prediction y^(​k)​ using the measurement matrix C(k)​ and the control input u(k)​ via matrix D(k),​ which maps the predicted state x^(k)​ into the measurement space.

The Update phase corrects the predicted state using the new measurement at time t=k.

  • Step 4: Compute the Kalman gain L(k), which weighs the importance of the prediction versus the new measurement.
  • Step 5: Update the state x(k)​ using the measurement y(k), the predicted state x^(k), and the Kalman gain L(k).
  • Step 6: Update the state covariance P(k)​ using the Kalman gain L(k)​ and the measurement noise v.

The recursive process includes two linear (or linearized) functions: A state transition function and an emission function. The state transition function defines the relationship between the current state and the next, describing an unobservable subsystem of battery states. The emission function generates an output based on the state and values from noisy measurements, characterizing an observable subsystem of battery measurements. Together, these two functions constitute the battery state system, forming a system of intent.

A Linear Battery Model in Kalman Filter

The battery’s state of charge cannot directly be measured; it is an unobserved subsystem. However, it is correlated with measurements of current, cumulative Coulomb counts. The current battery charge state equals the previous battery charge state plus Coulomb counts accumulated during the specific time interval (which can be positive or negative, depending on a charging or discharging activity). This relationship defines a state transition function.

The battery’s state of charge is also correlated with the battery voltage observed outside the battery; this is an observed subsystem. A state of charge is converted into an open circuit voltage. The terminal voltage as observed, is a sum of the open circuit voltage and an ohmic voltage drop, commonly referred to as an IR drop. This relationship defines an emission function.

Implemented Kalman Filter using Python

Here is the implementation of a Kalman Filter-based battery state system using Python. The code is available in this GitHub repository.

def kalman_filter_with_control(max_iter, x_hat, P, A, B, C, D, sigma_w, sigma_v, data_path, start_point=30000):
"""
Apply Kalman Filter with control inputs to estimate the state of a linear dynamic system.Parameters:
- max_iter: Number of iterations for the Kalman filter.
- x_hat: Initial state estimate (numpy array).
- P: Initial error covariance estimate (numpy array).
- A, B, C, D: System matrices (numpy arrays).
- sigma_w: Process noise covariance matrix.
- sigma_v: Measurement noise covariance matrix.
- data_path: Path to the CSV file containing the input data.
- start_point: Starting index for data extraction.
Returns:
- x_store: True state over iterations.
- x_hat_store: Estimated state over iterations.
- P_store: Error covariance over iterations.
"""
# Read and preprocess data
file = pd.read_csv(data_path)
current = pd.to_numeric(file[' Current (A)']) / 1000
soc = pd.to_numeric(file['SOC'].str.rstrip('%'))
u = np.array([[current[start_point]]])
x_true = np.array([[soc[start_point]]])
# Initialize storage arrays
x_store = np.zeros((max_iter + 1, len(x_true)))
x_hat_store = np.zeros((max_iter, len(x_hat)))
P_store = np.zeros((max_iter, len(x_hat)**2))
# Store the initial state
x_store[0, :] = x_true
for k in range(1, max_iter):
# Prediction Step
# State transition function
# Step 1
x_hat = A @ x_hat + B @ u
# Step 2
P = A @ P @ A.T + sigma_w
# Update the control input and true state from data
u = np.array([[current[k + start_point]]])
x_true = np.array([[soc[k + start_point]]])
# Simulate process and measurement noise
w = np.linalg.cholesky(sigma_w) @ np.random.randn(len(x_true))
v = np.linalg.cholesky(sigma_v) @ np.random.randn(len(C @ x_true))
x_true = A @ x_true + B * u + w
y_true = C @ x_true + D * u + v
# Estimate system output
# Emission function
# Step 3
y_hat = C @ x_hat + D @ u
# Measurement Update Step
# Step 4
S = C @ P @ C.T + sigma_v
K = P @ C.T @ np.linalg.inv(S)
# Step 5
x_hat = x_hat + K @ (y_true - y_hat)
# Step 6
P = (np.eye(P.shape[0]) - K @ C) @ P
# Store results
x_store[k, :] = x_true
x_hat_store[k - 1, :] = x_hat
P_store[k - 1, :] = P.flatten()
return x_store, x_hat_store, P_store

The kalman_filter_with_control function is the main function that defines the state transition and emission functions and performs the prediction and update phases. It takes the following arguments as parameters:

  • max_iter: Total number of iterations to run the Kalman filter.
  • x_hat: Initial estimate of the state.
  • P: Initial error covariance estimate.
  • A, B, C, D: System matrices used in the Kalman filter model.
  • sigma_w: Process noise covariance matrix.
  • sigma_v: Measurement noise covariance matrix.
  • data_path: Path to the CSV file containing the input data.
  • start_point: Index to start reading data from.

And returns the following values:

  • x_store: True state values throughout the iterations.
  • x_hat_store: Estimated state values.
  • P_store: Error covariance values.

Measurements of current are loaded from a CSV file and preprocessed. The CSV file is read, extracting the 'Current (A)' and 'SOC' columns. Current values are converted from milliamps (A) to amps. The SOC is stripped of any ‘%’ symbols and converted to numeric. They are as follows:

  • u: The control input vector with the starting current.
  • x_true: The true state vector using the starting SOC.
  • Arrays x_store, x_hat_store, and P_store are created to hold the results across all iterations.

The prediction phase proceeds from Steps 1 to 3.

Step 1: Predict the next state x_hat using matrix A (state transition) and control matrix B.

x_hat = A @ x_hat + B @ u

Step 2: Updates the predicted error Pcovariance using process noise sigma_w.

P = A @ P @ A.T + sigma_w

Step 3: Calculate a predicted system output y_hatas a sum of the predicted state and accumulated Coulomb counts.

y_hat = C @ x_hat + D @ u

The update phase proceeds from Steps 4 to 6.

Step 4: Calculate Kalman Gain (K) using S (total uncertainty).

S = C @ P @ C.T + sigma_v
K = P @ C.T @ np.linalg.inv(S)

Step 5: Update x_hat with the measurement error correction using K.

x_hat = x_hat + K @ (y_true - y_hat)

Step 6: Update the error covariance P with a correction.

P = (np.eye(P.shape[0]) - K @ C) @ P

Kalman Filter Execution Results

The function kalman_filter_with_control is called using the defined parameters and system matrices. (Refer to this GitHub repository for details in the code.) The results are then stored in x_store (true state values), x_hat_store (estimated state values), and P_store (error covariance values).

The following plots provide two main points:

In the upper plot, initially, there is a noticeable gap between the actual and estimated states as the Kalman filter starts with a rough estimate. As the filter runs through more iterations, the estimated state aligns with the true state, indicating the Kalman filter is refining its prediction to match reality.

In the lower plot, initially, the prediction error is quite high due to the large initial uncertainty and limited information. Over time, the error decreases significantly as the Kalman filter incorporates new data and corrects its estimates. The error gradually reaches near zero, indicating that the Kalman filter’s estimation is very close to the true SOC.

Conclusions

The Kalman Filter consists of Prediction and Update phases, broken down into six steps. Specifically, a state transition function defines a linear battery model, and an emission function defines a measurement output correlated with the predicted state. The full code for this implementation is available in this GitHub repository.

For simplicity, we use a linear battery state model. In real-world scenarios, however, more sophisticated filtering methods that capture the non-linear behavior of batteries might be more suitable, such as Unscented Kalman Filter (UKF) and Particle Filter.

Limitations Explained

As mentioned, the Kalman filter is designed to work with linear systems, where relationships between state variables and their measurements can be represented using linear equations. Unfortunately, real-world battery systems are far from linear.

Here are some limitations which often exhibit non-linear behavior:

— Battery systems often have non-linear state-of-charge (SOC) to open-circuit voltage (OCV) relationships throughout the entire charge-discharge cycle, meaning the Kalman filter may struggle with accurate SOC estimation.
— Battery systems are affected by non-linear phenomena such as temperature variations, aging, and internal chemical processes, which makes prediction less accurate. For instance, high discharge currents can cause voltage drops that do not follow simple linear dynamics.
— The Kalman filter assumes Gaussian (normally distributed) process and measurement noise. However, the errors in a non-linear battery system may not always adhere to this pattern.
— The filter uses fixed parameters (e.g., process and measurement noise covariance matrices) that may not account for changing conditions such as temperature fluctuations, load variations, or different battery chemistries. This limitation can result in overconfident estimates (underestimating uncertainty) or overly conservative predictions (overestimating uncertainty).

--

--