Meet kalbee: State Estimation Without the Boilerplate

Meet kalbee: State Estimation Without the Boilerplate

1 5
calendar_today agoschedule5 min read

Kalman filters shouldn't require a graduate seminar and 200 lines of matrix setup. Here's a small Python library that tries to fix that.

Visit: pypi/kalbee


The problem with Kalman filters

Kalman filters are everywhere — in your phone's GPS, in drones holding position against the wind, in the object trackers behind every "follow subject" camera mode. They're one of the most useful algorithms ever written.

They're also a pain to actually use.

Open any tutorial and you'll hit a wall of matrices: a transition matrix F, a process-noise covariance Q, a measurement matrix H, a measurement-noise covariance R — all of which you're expected to assemble by hand, get exactly right, and tune by trial and error. Get one sign wrong and your filter silently diverges. And once you've finally got a working filter, you discover it only tracks one thing. Tracking many objects, or handling motion that switches between "cruising" and "turning," means building a whole new layer on top.

kalbee is a clean, modular Python library that tries to smooth over all of this — a single, consistent toolkit that scales from a five-line filter to a full multi-object tracker.

A five-line filter

At its core, kalbee gives every algorithm the same tiny interface: predict() and update(). Here's a standard Kalman filter tracking position and velocity:

import numpy as np
from kalbee import KalmanFilter

kf = KalmanFilter(
    state=np.zeros((2, 1)),          # [position, velocity]
    covariance=np.eye(2),
    transition_matrix=np.array([[1, 1], [0, 1]]),
    transition_covariance=np.eye(2) * 0.01,
    measurement_matrix=np.array([[1, 0]]),
    measurement_covariance=np.array([[0.1]]),
)

kf.predict()
kf.update(np.array([[1.2]]))
print(kf.x)   # updated state estimate

That same predict/update rhythm carries across ten filters — Extended and Unscented Kalman filters for nonlinear systems, a Particle filter for non-Gaussian problems, Ensemble and Information filters, a Square-Root filter for numerical stability, and a Vectorized filter that tracks a thousand targets at once. There's also an Interacting Multiple Model (IMM) estimator that blends several filters to follow maneuvering targets. Learn the interface once, and you've learned all of them.

Stop hand-writing matrices

The biggest friction in that first example is those matrices. kalbee ships a models module with ready-made building blocks, so you describe your problem in words instead of linear algebra:

from kalbee.models import constant_velocity, position_measurement_model

# 2-D constant-velocity target: state = [x, vx, y, vy]
F, Q = constant_velocity(dt=1.0, process_var=0.1, n_dims=2)
H, R = position_measurement_model(order=1, n_dims=2, measurement_var=0.5)

Need acceleration? constant_acceleration. Tracking something that turns? constant_turn gives you the coordinated-turn model — which pairs beautifully with the IMM estimator (one model for straight lines, one for turns). No more squinting at a 4×4 matrix wondering whether that sin(ωΔt)/ω term has the right sign.

From one object to many: the tracker

Here's where it gets interesting. Every modern multi-object tracker you've heard of — SORT, ByteTrack, StrongSORT — is really just "a Kalman filter as a motion model, plus two extra ingredients": deciding which detection belongs to which track, and knowing when to start and stop tracks.

kalbee provides those two ingredients as MultiObjectTracker, built on top of the filter core rather than baked into it:

import numpy as np
from kalbee import KalmanFilter, MultiObjectTracker
from kalbee.models import constant_velocity, position_measurement_model

F, Q = constant_velocity(dt=1.0, process_var=0.05, n_dims=2)
H, R = position_measurement_model(order=1, n_dims=2, measurement_var=0.5)

def new_track(z):
    x0 = np.array([[z[0]], [0.0], [z[1]], [0.0]])
    return KalmanFilter(x0, np.eye(4) * 10.0, F, Q, H, R)

tracker = MultiObjectTracker(new_track, n_init=3, max_age=5)

# Feed detections (e.g. box centers from YOLO) frame by frame
for detections in detection_stream:
    for t in tracker.update(detections):
        print(f"id={t.id}  pos=({t.state[0,0]:.1f}, {t.state[2,0]:.1f})")

Under the hood it predicts every track forward, matches them to detections with the Hungarian algorithm (gated by Mahalanobis distance so implausible matches are rejected), and runs each track through a tentative → confirmed → deleted lifecycle so a single spurious detection never becomes a phantom object. Point it at the output of an object detector and you have a working tracker — one that holds stable identities even when targets cross paths.

Let the data tune your filter

The other classic headache is choosing Q and R — the noise covariances that make or break a filter's accuracy. Most people guess and fiddle.

kalbee's em_kalman learns them from data by maximum likelihood using the Expectation-Maximization algorithm:

from kalbee import em_kalman

result = em_kalman(measurements, F, H, n_iter=50)
print("Learned Q:\n", result.Q)
print("Learned R:\n", result.R)

It runs a filter forward and a smoother backward on each pass, and it's mathematically guaranteed to improve the fit every iteration. Fit your noise once on a representative recording, then deploy the learned values in a live filter. It's the offline complement to kalbee's online Adaptive filter, which adjusts noise on the fly.

Where kalbee fits

The Python state-estimation landscape has two poles. On one end, FilterPy — the beloved, pedagogical standard that explicitly chooses clarity over speed and stops at individual filters. On the other, Stone Soup — a heavyweight, enterprise tracking framework with a steep learning curve.

kalbee aims for the middle: friendlier and faster than FilterPy (its vectorized Particle and Ensemble filters process whole populations at once), but far lighter than Stone Soup. And it covers the part of the pipeline where most real work actually happens — turning filters into trackers.

It's also engineered to be trustworthy. A shared numerical core guards every matrix inverse against the silent-garbage failure mode where near-singular matrices return nonsense without raising an error. Covariance updates use the numerically stable Joseph form. The whole thing sits at ~95% test coverage across modern Python versions, with only NumPy and SciPy as runtime dependencies.

Try it

pip install kalbee

Then browse the documentation for theory-plus-code walkthroughs of every filter, the tracker, and the EM learner — or jump straight into the runnable multi-object tracking example.

The design philosophy is simple: the core stays a clean set of filters, and everything applied — models, tracking, learning — is a thin, optional layer that builds on it. Learn one interface, and scale from a five-line filter to a full multi-object tracker without ever rewriting what you already know.

Whether you're fusing IMU and GPS on a robot, following objects across video frames, or smoothing a noisy sensor on a small embedded problem, kalbee tries to get out of your way and let you estimate.

kalbee is open source under the Apache 2.0 license. Stars, issues, and contributions welcome.

1 Comment

0 votes
🔥 Join developers growing publicly
Share your knowledge, build in public, and grow your developer presence with a global community.

More Posts

I’m a Senior Dev and I’ve Forgotten How to Think Without a Prompt

Karol Modelskiverified - Mar 19

Is Google Meet HIPAA Compliant? Healthcare Video Conferencing Guide

Huifer - Feb 14

Split-Brain: Analyst-Grade Reasoning Without Raw Transactions on the Server

Pocket Portfolio - Apr 8

Local-First: The Browser as the Vault

Pocket Portfolio - Apr 20

TypeScript Complexity Has Finally Reached the Point of Total Absurdity

Karol Modelskiverified - Apr 23
chevron_left
160 Points6 Badges
Viet Nam, Ho Chi Minh cityminlee0210.github.io
2Posts
0Comments
Just another DEV guy.
Explore new tech, focused on applied AI.

Related Jobs

View all jobs →

Commenters (This Week)

1 comment
1 comment
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!