Kalman Filtering Tutorial ========================= This tutorial demonstrates how to implement a Kalman filter for tracking a moving object using noisy position measurements. .. raw:: html
Problem Setup ------------- We will track a 2D object moving with constant velocity. The state vector contains position and velocity in both x and y dimensions: .. math:: x = [x, \dot{x}, y, \dot{y}]^T Step 1: Import Required Modules ------------------------------- .. code-block:: python import numpy as np from pytcl.dynamic_estimation import kf_predict, kf_update Step 2: Define the System Model ------------------------------- The constant velocity model uses the following state transition: .. code-block:: python dt = 0.1 # Time step # State transition matrix (constant velocity model) F = np.array([ [1, dt, 0, 0], [0, 1, 0, 0], [0, 0, 1, dt], [0, 0, 0, 1] ]) # Process noise covariance q = 0.1 # Process noise intensity Q = q * np.array([ [dt**3/3, dt**2/2, 0, 0], [dt**2/2, dt, 0, 0], [0, 0, dt**3/3, dt**2/2], [0, 0, dt**2/2, dt] ]) # Measurement matrix (we observe position only) H = np.array([ [1, 0, 0, 0], [0, 0, 1, 0] ]) # Measurement noise covariance R = np.eye(2) * 0.5 Step 3: Initialize the Filter ----------------------------- .. code-block:: python # Initial state estimate x = np.array([0.0, 1.0, 0.0, 0.5]) # Initial covariance (high uncertainty) P = np.eye(4) * 10.0 Step 4: Generate Simulated Data ------------------------------- .. code-block:: python np.random.seed(42) n_steps = 100 # True trajectory true_states = [] x_true = np.array([0.0, 1.0, 0.0, 0.5]) for _ in range(n_steps): true_states.append(x_true.copy()) x_true = F @ x_true + np.random.multivariate_normal(np.zeros(4), Q) # Noisy measurements measurements = [H @ s + np.random.multivariate_normal(np.zeros(2), R) for s in true_states] Step 5: Run the Kalman Filter ----------------------------- .. code-block:: python estimates = [] for z in measurements: # Predict pred = kf_predict(x, P, F, Q) x, P = pred.x, pred.P # Update upd = kf_update(x, P, z, H, R) x, P = upd.x, upd.P estimates.append(x.copy()) Step 6: Analyze Results ----------------------- .. code-block:: python # Convert to arrays true_states = np.array(true_states) estimates = np.array(estimates) measurements = np.array(measurements) # Compute position errors pos_errors = np.sqrt( (true_states[:, 0] - estimates[:, 0])**2 + (true_states[:, 2] - estimates[:, 2])**2 ) print(f"Mean position error: {np.mean(pos_errors):.3f}") print(f"Final position error: {pos_errors[-1]:.3f}") Complete Example ---------------- Here is the complete code: .. code-block:: python import numpy as np from pytcl.dynamic_estimation import kf_predict, kf_update # System parameters dt = 0.1 F = np.array([[1, dt, 0, 0], [0, 1, 0, 0], [0, 0, 1, dt], [0, 0, 0, 1]]) q = 0.1 Q = q * np.array([[dt**3/3, dt**2/2, 0, 0], [dt**2/2, dt, 0, 0], [0, 0, dt**3/3, dt**2/2], [0, 0, dt**2/2, dt]]) H = np.array([[1, 0, 0, 0], [0, 0, 1, 0]]) R = np.eye(2) * 0.5 # Initialize x = np.array([0.0, 1.0, 0.0, 0.5]) P = np.eye(4) * 10.0 # Generate data np.random.seed(42) x_true = np.array([0.0, 1.0, 0.0, 0.5]) true_states, measurements = [], [] for _ in range(100): true_states.append(x_true.copy()) measurements.append(H @ x_true + np.random.multivariate_normal( np.zeros(2), R)) x_true = F @ x_true + np.random.multivariate_normal(np.zeros(4), Q) # Filter estimates = [] for z in measurements: pred = kf_predict(x, P, F, Q) upd = kf_update(pred.x, pred.P, z, H, R) x, P = upd.x, upd.P estimates.append(x.copy()) # Results true_states = np.array(true_states) estimates = np.array(estimates) rmse = np.sqrt(np.mean((true_states[:, 0] - estimates[:, 0])**2 + (true_states[:, 2] - estimates[:, 2])**2)) print(f"Position RMSE: {rmse:.3f}") Next Steps ---------- - Try the :doc:`nonlinear_filtering` tutorial for EKF and UKF examples - See :doc:`/user_guide/filtering` for more filter variants - Explore IMM estimators for maneuvering targets