Skip to content

Design & Architecture

Project Structure

kalbee/
├── __init__.py                  # Top-level exports
├── modules/
│   ├── filters/
│   │   ├── base.py              # BaseFilter ABC
│   │   ├── kf_filter.py         # Kalman Filter
│   │   ├── ekf_filter.py        # Extended KF
│   │   ├── ukf_filter.py        # Unscented KF
│   │   ├── particle_filter.py   # Particle Filter
│   │   ├── enkf_filter.py       # Ensemble KF
│   │   ├── information_filter.py # Information Filter
│   │   ├── abg_filter.py        # Alpha-Beta-Gamma
│   │   ├── adaptive_kf.py       # Adaptive KF
│   │   └── auto_filter.py       # Factory
│   ├── smoothers/
│   │   └── rts_smoother.py      # RTS Smoother
│   └── utils/
│       └── metrics.py           # RMSE, NEES, NIS
├── experiments/
│   ├── signals.py               # Signal generators
│   ├── runner.py                # Experiment runner
│   └── results.py               # Results container
└── tests/
    └── test_*.py                # 58 tests

Design Principles

1. Common Interface via BaseFilter

Every filter inherits from BaseFilter and implements:

class BaseFilter(ABC):
    def predict(self, dt: float = 1.0, **kwargs) -> np.ndarray: ...
    def update(self, measurement: np.ndarray, **kwargs) -> np.ndarray: ...
    def measure(self, state=None) -> np.ndarray: ...

This means you can swap filters without changing calling code.

2. Numerical Stability

  • Joseph form for covariance updates in KF and EKF
  • Symmetry enforcement after every covariance update: P = (P + P.T) / 2
  • Cholesky fallback in UKF sigma point generation

3. Extensibility

Add a new filter by:

  1. Create kalbee/modules/filters/my_filter.py
  2. Inherit from BaseFilter
  3. Implement predict() and update()
  4. Register in __init__.py and AutoFilter
from kalbee.modules.filters.base import BaseFilter

class MyFilter(BaseFilter):
    def predict(self, dt=1.0, **kwargs):
        # Your predict logic
        return self.state

    def update(self, measurement, **kwargs):
        # Your update logic
        return self.state

Dependencies

Package Why
numpy Core matrix operations
scipy Cholesky decomposition (UKF)

Filter Comparison

Filter Linear Non-linear Non-Gaussian Jacobians Complexity
KF Not needed \(O(n^3)\)
EKF Required \(O(n^3)\)
UKF Not needed \(O(n^3)\)
PF Not needed \(O(Nn^2)\)
EnKF Not needed \(O(Nn^2)\)
IF Not needed \(O(n^3)\)
ABG Not needed \(O(1)\)
AKF Not needed \(O(n^3)\)