Migrating from FilterPy¶
kalman-py follows FilterPy's conventions where it can: the filter predicts before the first
update, and update accepts per-call H and R. On the same linear problem the two produce the
same estimates to rounding error, so switching shouldn't change your results.
The main differences¶
| FilterPy | kalman-py | |
|---|---|---|
| Construction | KalmanFilter(dim_x, dim_z), then assign kf.F, kf.H, ... |
KalmanFilter(F, H, Q, R, x0, P0) |
| State shape | column vector (n, 1) |
1-D array (n,) |
| Step API | predict(u, B, F, Q), update(z, R, H) |
predict(F, Q), update(z, H, R) |
Control input B u |
supported | not supported yet |
| Batch | batch_filter(zs) → means, covs, priors |
filter(zs) → result object; backend="jax" to compile |
| Smoother | rts_smoother(Xs, Ps) |
smooth(result) |
| Likelihood | kf.log_likelihood (last step) |
result.log_likelihood (whole sequence) |
| Mahalanobis distance | kf.mahalanobis |
sqrt(result.nis), per step |
| EKF Jacobians | passed to every update(z, HJacobian, Hx) |
h, optional jac_h, given once; derived by autodiff if omitted |
EKF nonlinear f |
override predict_x |
pass f(x, dt), optional jac_f |
| UKF sigma points | points=MerweScaledSigmaPoints(n, alpha, beta, kappa) |
alpha=, beta=, kappa= |
| UKF angle mean | z_mean_fn plus residual_z |
residual_z only |
| UKF state angles | x_mean_fn, residual_x |
not supported yet |
| Covariance form | standard / Joseph | Joseph, or square_root=True |
Side by side¶
A FilterPy filter and the equivalent kalman-py one:
import warnings
import numpy as np
warnings.filterwarnings("ignore", category=SyntaxWarning) # FilterPy's docstrings
from filterpy.kalman import KalmanFilter as FilterPyKalmanFilter
from kalman_py import KalmanFilter
dt = 0.1
F = np.array([[1.0, dt], [0.0, 1.0]])
H = np.array([[1.0, 0.0]])
Q = 0.1 * np.array([[dt**3 / 3, dt**2 / 2], [dt**2 / 2, dt]])
R = np.array([[0.5]])
zs = np.sin(np.arange(50) * dt)[:, None]
# FilterPy
fp = FilterPyKalmanFilter(dim_x=2, dim_z=1)
fp.F, fp.H, fp.Q, fp.R = F, H, Q, R
fp.x, fp.P = np.array([[0.0], [1.0]]), np.eye(2)
fp_means, fp_covs, _, _ = fp.batch_filter(zs)
# kalman-py
kf = KalmanFilter(F, H, Q, R, x0=[0.0, 1.0], P0=np.eye(2))
result = kf.filter(zs)
print(np.abs(result.means - fp_means[:, :, 0]).max()) # ~1e-16
Smoothing:
fp_smoothed, fp_smoothed_covs, _, _ = fp.rts_smoother(fp_means, fp_covs)
smoothed = kf.smooth(result)
print(np.abs(smoothed.means - fp_smoothed[:, :, 0]).max())
Process noise¶
FilterPy's Q_discrete_white_noise(dim=2, dt=dt, var=q) is the discrete white-noise
acceleration model. kalman-py has no helper; the continuous white-noise acceleration model
used throughout these docs is
q = 0.1
Q = q * np.array([[dt**3 / 3, dt**2 / 2], [dt**2 / 2, dt]])
These are two different standard models, not two spellings of one: pick the one your problem calls for.
From pykalman¶
pykalman treats initial_state_mean as the state at the first measurement (it updates
before predicting). To reproduce its numbers, give kalman-py the prior one step earlier, or
equivalently give pykalman F @ x0 and F @ P0 @ F.T + Q. pykalman's EM corresponds to
fit_noise, which also offers gradient-based maximum likelihood.