Skip to content

Filters

kalman_py.KalmanFilter

KalmanFilter(
    F: ArrayLike,
    H: ArrayLike,
    Q: ArrayLike,
    R: ArrayLike,
    x0: ArrayLike,
    P0: ArrayLike,
    *,
    square_root: bool = False,
)

Linear Kalman filter for x_k = F x_{k-1} + w, z_k = H x_k + v.

w ~ N(0, Q) and v ~ N(0, R). (x0, P0) is the prior before the first prediction, so every measurement is preceded by a predict step (the FilterPy convention).

Use predict and update in real-time loops, or filter to process a whole measurement sequence at once, then smooth to refine it with future data.

square_root=True propagates a factor S of the covariance (P = S S') with QR decompositions instead of using the Joseph form. The covariance then stays positive semi-definite even for extremely precise sensors, at the cost of a QR per step. In this mode P is derived from the factor after every step, so assigning to it has no effect.

predict

predict(
    F: ArrayLike | None = None, Q: ArrayLike | None = None
) -> None

Advance the current estimate (x, P) by one time step.

F and Q replace the model's matrices for this step only, e.g. for a varying time step.

update

update(
    z: ArrayLike,
    H: ArrayLike | None = None,
    R: ArrayLike | None = None,
) -> None

Condition the current estimate on one measurement.

H and R replace the measurement model for this update only, e.g. to fuse sensors that measure different things: H may then have any number of rows k, with z of length k and R of shape (k, k) (R is required when k differs from the model's measurement dimension).

filter

filter(
    zs: ArrayLike, backend: Literal["numpy"] = ...
) -> FilterResult[Array]
filter(
    zs: ArrayLike | Array, backend: Literal["jax"]
) -> FilterResult[Array]
filter(
    zs: ArrayLike | Array, backend: Backend = "numpy"
) -> FilterResult[Array] | FilterResult[Array]

Filter a measurement sequence of shape (T, dim_z), starting from (x0, P0).

This does not change the step-by-step state (x, P). A 1-D zs is accepted when dim_z == 1. With backend="jax" the result holds JAX arrays, and JAX arrays passed as zs stay on their device.

smooth

smooth(
    result: FilterResult[Array],
) -> SmootherResult[Array]
smooth(
    result: FilterResult[Array],
) -> SmootherResult[Array]
smooth(
    result: FilterResult[Array] | FilterResult[Array],
) -> SmootherResult[Array] | SmootherResult[Array]

Run the RTS smoother over the output of filter, on the same backend.

kalman_py.ExtendedKalmanFilter

ExtendedKalmanFilter(
    f: TransitionFn,
    h: MeasurementFn,
    Q: ArrayLike,
    R: ArrayLike,
    x0: ArrayLike,
    P0: ArrayLike,
    *,
    jac_f: TransitionFn | None = None,
    jac_h: MeasurementFn | None = None,
    residual_z: ResidualFn | None = None,
    square_root: bool = False,
)

Extended Kalman filter for x_k = f(x_{k-1}, dt) + w, z_k = h(x_k) + v.

w ~ N(0, Q) and v ~ N(0, R); (x0, P0) is the prior before the first prediction.

Jacobians jac_f(x, dt) and jac_h(x) are optional: when omitted they are derived automatically with jax.jacfwd, which needs JAX and requires f and h to be written with jax.numpy. The JAX backend also traces f, h and residual_z, so they must be jax.numpy code there too. residual_z(z, z_pred) replaces z - z_pred, e.g. to wrap angle differences into [-pi, pi).

For the JAX backend, pass module-level functions rather than fresh lambdas: compiled code is cached per function object.

square_root=True selects the square-root covariance form; see KalmanFilter.

predict

predict(dt: float) -> None

Advance the current estimate (x, P) by dt.

update

update(z: ArrayLike) -> None

Condition the current estimate on one measurement.

filter

filter(
    zs: ArrayLike,
    dt: ArrayLike,
    backend: Literal["numpy"] = ...,
) -> ExtendedFilterResult[Array]
filter(
    zs: ArrayLike | Array,
    dt: ArrayLike,
    backend: Literal["jax"],
) -> ExtendedFilterResult[Array]
filter(
    zs: ArrayLike | Array,
    dt: ArrayLike,
    backend: Backend = "numpy",
) -> (
    ExtendedFilterResult[Array]
    | ExtendedFilterResult[Array]
)

Filter a (T, dim_z) measurement sequence, starting from (x0, P0).

dt is the time step before each measurement: a scalar, or one value per measurement for irregular sampling. This does not change the step-by-step state.

smooth

smooth(
    result: ExtendedFilterResult[Array],
) -> SmootherResult[Array]
smooth(
    result: ExtendedFilterResult[Array],
) -> SmootherResult[Array]
smooth(
    result: ExtendedFilterResult[Array]
    | ExtendedFilterResult[Array],
) -> SmootherResult[Array] | SmootherResult[Array]

Extended RTS smoother over the output of filter, on the same backend.

kalman_py.UnscentedKalmanFilter

UnscentedKalmanFilter(
    f: TransitionFn,
    h: MeasurementFn,
    Q: ArrayLike,
    R: ArrayLike,
    x0: ArrayLike,
    P0: ArrayLike,
    *,
    alpha: float = 1.0,
    beta: float = 2.0,
    kappa: float = 0.0,
    residual_z: ResidualFn | None = None,
    square_root: bool = False,
    vectorized: bool = False,
)

Unscented Kalman filter for x_k = f(x_{k-1}, dt) + w, z_k = h(x_k) + v.

w ~ N(0, Q) and v ~ N(0, R); (x0, P0) is the prior before the first prediction. No Jacobians are needed: f and h are evaluated at Van der Merwe scaled sigma points.

The defaults alpha=1, beta=2, kappa=0 keep every covariance weight nonnegative, so the predicted covariance cannot become indefinite. Small alpha (e.g. 1e-3, common in the literature) concentrates the points but makes the central weight strongly negative.

residual_z(z, z_pred) replaces z - z_pred, e.g. to wrap angles into [-pi, pi). It is also used to average the sigma-point measurements, so angle measurements near +-pi need no separate mean function. Sigma points are redrawn from the predicted distribution before each update, so the process noise is reflected in the measurement prediction.

The JAX backend vmaps f, h and residual_z over sigma points, so they must be jax.numpy code there; pass module-level functions so compiled code is reused.

vectorized=True declares that f(X, dt), h(X) and residual_z(Z, z) accept a stack of points with a leading axis (shape (N, n)) and return one row per point. The NumPy backend then makes one call per step instead of one per sigma point, which is much faster; write the functions with [..., i] indexing so they also work on single points.

square_root=True propagates a Cholesky factor of P (the square-root UKF of Van der Merwe and Wan), so covariances stay positive-definite by construction. With a negative central weight (small alpha) a factor downdate can fail; that raises CovarianceDowndateError rather than continuing with an invalid covariance. P0 must be positive-definite in this mode.

predict

predict(dt: float) -> None

Advance the current estimate (x, P) by dt.

update

update(z: ArrayLike) -> None

Condition the current estimate on one measurement.

filter

filter(
    zs: ArrayLike,
    dt: ArrayLike,
    backend: Literal["numpy"] = ...,
) -> UnscentedFilterResult[Array]
filter(
    zs: ArrayLike | Array,
    dt: ArrayLike,
    backend: Literal["jax"],
) -> UnscentedFilterResult[Array]
filter(
    zs: ArrayLike | Array,
    dt: ArrayLike,
    backend: Backend = "numpy",
) -> (
    UnscentedFilterResult[Array]
    | UnscentedFilterResult[Array]
)

Filter a (T, dim_z) measurement sequence, starting from (x0, P0).

dt is the time step before each measurement: a scalar, or one value per measurement. This does not change the step-by-step state.

smooth

smooth(
    result: UnscentedFilterResult[Array],
) -> SmootherResult[Array]
smooth(
    result: UnscentedFilterResult[Array],
) -> SmootherResult[Array]
smooth(
    result: UnscentedFilterResult[Array]
    | UnscentedFilterResult[Array],
) -> SmootherResult[Array] | SmootherResult[Array]

Unscented RTS smoother over the output of filter, on the same backend.