Examples & Gallery¶
Seeing a filter run is the fastest way to get state estimation. This page
collects animated demos, copy-paste recipes, and pointers to the runnable
examples in examples/ and scripts/.
Every animation below is produced with kalbee's own public API by
scripts/generate_demo_gif.py and scripts/mot16_demo.py.
New to state estimation? Read the intuition-first intro first — it derives the one-number case by hand and maps the math to kalbee's API.
1. Watch a Kalman filter denoise a signal¶
import numpy as np
from kalbee import KalmanFilter
from kalbee.models import constant_velocity, position_measurement_model
dt = 0.05
F, Q = constant_velocity(dt=dt, process_var=0.02, n_dims=1) # motion model
H, R = position_measurement_model(order=1, n_dims=1, measurement_var=0.35**2)
kf = KalmanFilter(np.array([[0.0], [0.6]]), np.eye(2) * 5.0, F, Q, H, R)
for z in noisy_measurements: # your data
kf.predict() # propagate: physics
kf.update(np.array([[z]])) # correct: evidence
pos, vel, unc = kf.x[0, 0], kf.x[1, 0], kf.P[0, 0]
The lesson: predict + update is the whole filter. Everything else in
kalbee exists so you can keep that two-line loop for harder problems
(nonlinear dynamics, maneuvering targets, many objects, bad measurements).
2. One model can't follow a maneuver — blend two with IMM¶
from kalbee import KalmanFilter, InteractingMultipleModel
from kalbee.models import constant_velocity, position_measurement_model
# two KFs on a 6-D [x, vx, y, vy, ax, ay] state: one nearly noiseless CV, one CA
cv = KalmanFilter(x0, P0, F_cv, np.eye(6) * 0.02, H, R)
ca = KalmanFilter(x0, P0, F_ca, np.eye(6) * 2.00, H, R)
imm = InteractingMultipleModel(
[cv, ca],
np.array([[0.97, 0.03], [0.03, 0.97]]), # model-switch probabilities
np.array([0.8, 0.2]), # prior model probabilities
)
imm.predict()
imm.update(measurement)
See the full maneuver scenario in
docs/features/maneuvering_target.md and
examples/tracking_demo.py.
3. Multi-object tracking on real footage (MOT Challenge)¶
Tracking many objects is not many Kalman filters glued by hand — kalbee provides a SORT-style tracker that owns association and lifecycle:
from kalbee import KalmanFilter, MultiObjectTracker
from kalbee.models import constant_velocity, position_measurement_model
# Each detection is a box [x1, y1, x2, y2]; track the 4 corners as CV axes.
F, Q = constant_velocity(dt=dt, process_var=1.0, n_dims=4)
H, R = position_measurement_model(order=1, n_dims=4, measurement_var=36.0)
def new_track(box):
x0 = np.concatenate([np.array([[zi], [0.0]]) for zi in box])
return KalmanFilter(x0, np.eye(8) * 50.0, F, Q, H, R)
tracker = MultiObjectTracker(new_track, n_init=2, max_age=30, gate=8.0)
for frame_boxes in detection_stream:
confirmed = tracker.update(frame_boxes) # (N,4) or None when absent
for t in confirmed:
print(t.id, t.state[[0, 2, 4, 6], 0]) # id + filtered box
Reproduce the demo yourself (downloads only MOT16-02, ~15 MB):
uv run python scripts/fetch_mot16_02.py --frames 90 # -> data/MOT16-02
uv run python examples/mot16_pedestrian_tracking.py # console metrics
uv run python scripts/mot16_demo.py --gif # re-render the gif
Detection-driven (YOLO) equivalents for vehicles and people live in
examples/yolo_mot.py, examples/yolo_vehicles.py and examples/yolo_people.py — see
YOLO Object Tracking.
4. Which filter should you pick?¶
| Your problem | Start with | Then try |
|---|---|---|
| Linear system, clean model | KalmanFilter |
SquareRootKalmanFilter if long-running |
| Nonlinear dynamics / measurements | ExtendedKalmanFilter (have Jacobians) or UnscentedKalmanFilter (don't) |
CubatureKalmanFilter for n > 5 |
| Non-Gaussian noise | ParticleFilter |
RaoBlackwellizedParticleFilter if partially linear |
| Maneuvering target | InteractingMultipleModel (CV + CA) |
FadingMemoryKalmanFilter (cheap alternative) |
Unknown noise Q / R |
AdaptiveKalmanFilter |
offline em_kalman / tune_kalman_filter |
| Many objects at once | MultiObjectTracker |
JPDAAssociation, PMBMTracker in clutter |
| Worst-case robustness | HInfinityFilter |
— |
| Batch of independent series | VectorizedKalmanFilter |
— |
Prefer AutoFilter when you want to switch strategies without rewriting the
loop:
from kalbee import AutoFilter
filt = AutoFilter.from_filter(*linear_args, mode="srkf") # or "kf","akf","hinf",...
5. Recipes for real workflows¶
From a measurement array to a smooth output in three lines¶
from kalbee import KalmanFilter, em_kalman
from kalbee.models import constant_velocity, position_measurement_model
F, _ = constant_velocity(dt=0.1, n_dims=1)
H, _ = position_measurement_model(order=1, n_dims=1)
result = em_kalman(measurements, F, H, n_iter=50) # learn Q, R from data
print(result.Q, result.R)
Report the numbers a reviewer will ask about¶
from kalbee import FilterDiagnostics, rmse, nis
kf = AutoFilter.from_filter(...)
diag = FilterDiagnostics(m=1, n=2)
for z, truth in zip(stream, ground_truth):
kf.predict(); kf.update(z)
diag.collect(kf, truth)
report = diag.summary() # NIS mean, covariance trace, consistency flags
Reject outliers before they corrupt the state¶
from kalbee.modules.utils.gating import chi2_gate
passed, nis_val, threshold = chi2_gate(innovation, innovation_cov, confidence=0.99)
Where to go next¶
examples/gps_imu_fusion.py— loosely-coupled GPS + IMU, a two-line predict loopexamples/quaternion_attitude_ekf.py— attitude from gyro + accelerometerexamples/multi_object_tracking.py— SORT-style MOT on synthetic detectionsexamples/yolo_*.py— bounding-box tracking of vehicles/people in real videoexamples/mot16_pedestrian_tracking.py— MOT Challenge pedestriansdocs/features/experiments.md—run_experimentto compare filtersdocs/architecture.md— design and how to add your own filter
All animations regenerate with: