YAFL python extension is provided for prototyping and test purposes, to install it you need to:
install it:
# Cython, numpy, scipy, setuptools, wheel
# are needed at this point
pip install path_to/yaflpy-\<latest version\>.tar.gz
Python
import yaflpy
If you don't want to install the extension then you can do this:
import numpy as np
import pyximport
import os
import scipy.stats
import sys
# Write the rigth path here!
SRC_PATH = 'path_to_yafl/src'
EXT_PATH = os.path.join(SRC_PATH, 'yaflpy')
sys.path.insert(0, EXT_PATH)
pyximport.install(
pyimport=True,
reload_support=True,
language_level=3,
setup_args={
'include_dirs': [np.get_include(), SRC_PATH, EXT_PATH],
}
)
#Now we are ready to import yaflpy
import yaflpy
Actually the API is quite simmilar to FilterPy. We will focus on YAFL features, goto FilterPy documentation for the rest.
We have the following constants:
#Bit masks
yaflpy.ST_MSK_REGULARIZED = YAFL_ST_MSK_REGULARIZED
yaflpy.ST_MSK_GLITCH_SMALL = YAFL_ST_MSK_GLITCH_SMALL
yaflpy.ST_MSK_GLITCH_LARGE = YAFL_ST_MSK_GLITCH_LARGE
yaflpy.ST_MSK_ANOMALY = YAFL_ST_MSK_ANOMALY
yaflpy.ST_MSK_ERROR = 0xFF0
yaflpy.ST_MSK_WARNING = 0xF
#Everthing is OK
yaflpy.ST_OK = YAFL_ST_OK
#Error threshold
yaflpy.ST_ERR_THR = YAFL_ST_ERR_THR
#Errors:
# Invalid argument numer
yaflpy.ST_INV_ARG_1 = YAFL_ST_INV_ARG_1
yaflpy.ST_INV_ARG_2 = YAFL_ST_INV_ARG_2
yaflpy.ST_INV_ARG_3 = YAFL_ST_INV_ARG_3
yaflpy.ST_INV_ARG_4 = YAFL_ST_INV_ARG_4
yaflpy.ST_INV_ARG_5 = YAFL_ST_INV_ARG_5
yaflpy.ST_INV_ARG_6 = YAFL_ST_INV_ARG_6
yaflpy.ST_INV_ARG_7 = YAFL_ST_INV_ARG_7
yaflpy.ST_INV_ARG_8 = YAFL_ST_INV_ARG_8
yaflpy.ST_INV_ARG_9 = YAFL_ST_INV_ARG_9
yaflpy.ST_INV_ARG_10 = YAFL_ST_INV_ARG_10
yaflpy.ST_INV_ARG_11 = YAFL_ST_INV_ARG_11
These constants are used to determine statusses of predict
and update
methods calls.
See C-Manual for details.
The base class is
class yaflpy.yaflKalmanBase(dim_x, dim_z, dt, fx, hx, residual_z = None)
where:
dim_x :int
is state vector dimmension,dim_z: int
is measurement vector dimmension,dt :float
is timestep between measurements is seconds,fx :function(x,dt)
is state transistion function,hx :function(x)
is measurement function,residual_z :function(x, y)
, optional, is measurement residual function.All other classes extend yaflKalmanBase
. This base class is not supposed to be used directly.
Its API is based on UKF from FilterPy.
NOTE: Only predict
and update
methods are supported by filters in YAFL at the moment.
The Kalman filter has these attrbutes:
P = Up.dot(Dp.dot(Up.T))
which is state covariance matrixQ = Uq.dot(Dq.dot(Uq.T))
which is process noise matrixR = Ur.dot(Dr.dot(Ur.T))
which is measurement noise matrixrff
which is R
forgetting factor used to ajust measurement noice covariance at runtimeSince YAFL implements UD-factorized filters we don't have these attributes directly, but have vectors to store UD-decomposition elements of these attributes:
Up :np.array((max(1, (dim_x * (dim_x - 1))//2), ))
is vector with upper triangular elements of PDp :np.array((dim_x, ))
is vector with diagonal elements of PUq :np.array((max(1, (dim_x * (dim_x - 1))//2), ))
is vector with upper triangular elements of QDq :np.array((dim_x, ))
is vector with diagonal elements of QUr :np.array((max(1, (dim_z * (dim_z - 1))//2), ))
is vector with upper triangular elements of RDr :np.array((dim_z, ))
is vector with diagonal elements of RWe used this paper to implement optional Q and R adaptive adjustments. Here are som notes on our implementation:
rff
attribute to a small positive number e.g. 1e-4
.qff
attribute to a small positive number e.g. 1e-4
.The base class for all EKF variants is
class yaflpy.yaflExtendedBase(dim_x, dim_z, dt, fx, jfx, hx, jhx, residual_z = None)
where:
jfx :function(x,dt)
is function which computes the Jacobian of the fx
function. Returns: np.array((dim_x, dim_x))
.jhx :function(x)
is function which computes the Jacobian of the hx
function. Returns: np.array((dim_x, dim_x))
.This class extends yaflpy.yaflKalmanBase
. This class is not supposet to be used directly.
NOTE: In addition to rff
all EKF variants have qff
attribute which is Q
forgetting factor used to ajust process noice covariance at runtime
Basic UD-factorized EKF variants are:
class yaflpy.Bierman(dim_x, dim_z, dt, fx, jfx, hx, jhx, residual_z = None)
and
class yaflpy.Joseph(dim_x, dim_z, dt, fx, jfx, hx, jhx, residual_z = None)
Both classes extend yaflpy.yaflExtendedBase
by implemrnting _update(self)
method with corresponding update procedures.
The example code is:
from yaflpy import Bierman as KF
#from yaflpy import Joseph as KF
# Measurement std
STD = 100.
# Bootstrap the filter
def _fx(x, dt, **fx_args):
x = x.copy()
x[0] += x[1] * dt
x[2] += x[3] * dt
return x
def _jfx(x, dt, **fx_args):
F = np.array([
[1., dt, 0., 0.],
[0., 1., 0., 0.],
[0., 0., 1., dt],
[0., 0., 0., 1.],
])
return F
def _hx(x, **hx_args):
if hx_args:
print(hx_args)
return np.array([x[0], x[2]])
def _jhx(x, **hx_args):
H = np.array([
[1., 0., 0., 0.],
[0., 0., 1., 0.],
])
return H
def _zrf(a,b):
return a - b
kf = KF(4, 2, 1., _fx, _jfx, _hx, _jhx, residual_z=_zrf)
kf.x[0] = 0.
kf.x[1] = 0.3
kf.Dp *= .00001
kf.Dq *= 1.0e-8
kf.Dr *= STD*STD
kf.Dr[0] *= .75
kf.Ur += 0.5
# Generate some data
N = 6000
clean = np.zeros((N, 2))
noisy = np.zeros((N, 2))
kf_out = np.zeros((N, 2))
t = np.zeros((N,), dtype=np.float)
for i in range(1, len(clean)):
clean[i] = clean[i-1] + np.array([1.,1.])
noisy[i] = clean[i] + np.random.normal(scale=STD, size=2)
t[i] = i
#Run the filter
for i, z in enumerate(noisy):
kf.predict()
kf.update(z)
kf_out[i] = kf.x[::2]
Basic class for adaptive UD-factorized EKF variants is
class yaflpy.yaflAdaptiveBase(dim_x, dim_z, dt, fx, jfx, hx, jhx, residual_z = None)
This class is based on yaflpy.yaflExtendedBase
and adds chi2
attribute to it.
This class is not supposed to be used directly.
Adaptive UD-factorized EKF variants are:
class yaflpy.AdaptiveBierman(dim_x, dim_z, dt, fx, jfx, hx, jhx, residual_z = None)
and
class yaflpy.AdaptiveJoseph(dim_x, dim_z, dt, fx, jfx, hx, jhx, residual_z = None)
Both classes extend yaflpy.yaflAdaptiveBase
by implementing _update(self)
method with corresponding update procedures.
Both classes APIs are simmilar to basic variants.
Basic class for adaptive UD-factorized EKF variants is
class yaflpy.yaflRobustBase(dim_x, dim_z, dt, fx, jfx, hx, jhx, gz=None, gdotz=None, **kwargs)
This class is based on yaflpy.yaflExtendedBase
and adds the following hidden attrbutes to it:
gz :function(nu, **hx_args)
is influence function. Returns: float
.gdotz :function(nu, **hx_args)
is influence function first derivative. Returns: float
.
Both functions take hx_args
which are hx
function kwargsThis class is not supposed to be used directly.
Adaptive UD-factorized EKF variants are:
class yaflpy.RobustBierman(dim_x, dim_z, dt, fx, jfx, hx, jhx, residual_z = None)
and
class yaflpy.RobustJoseph(dim_x, dim_z, dt, fx, jfx, hx, jhx, residual_z = None)
Both classes extend yaflpy.yaflRobustBase
by implementing _update(self)
method with corresponding update procedures.
Both classes APIs are simmilar to basic variants.
Basic class for adaptive UD-factorized EKF variants is
class yaflpy.yaflAdaptiveRobustBase(dim_x, dim_z, dt, fx, jfx, hx, jhx, gz=None, gdotz=None, **kwargs)
This class is based on yaflpy.yaflRobustBase
and adds chi2
attribute to it.
This class is not supposed to be used directly.
Adaptive UD-factorized EKF variants are:
class yaflpy.AdaptiveRobustBierman(dim_x, dim_z, dt, fx, jfx, hx, jhx, residual_z = None)
and
class yaflpy.AdaptiveRobustJoseph(dim_x, dim_z, dt, fx, jfx, hx, jhx, residual_z = None)
Both classes extend yaflpy.yaflAdaptiveRobustBase
by implementing _update(self)
method with corresponding update procedures.
Both classes APIs are simmilar to basic variants.
The base class for sigma points generators is:
class yaflpy.yaflSigmaBase(dim_x, addf=None)
where:
addf: function(mean, delta, mult)
which computes sigma_point = mean + delta * mult
This class is a wrapper around yaflUKFBaseSt
type from src/yafl.h.
This class is not supposed to be used directly.
The generator class is:
class MerweSigmaPoints(dim_x, alpha, beta, kappa=0.0, **kwargs)
This class is a wrapper around yaflUKFMerweSt
. It extends yaflSigmaBase
and adds the following attributes:
alpha
is a scaling parameter that determines the width of the sigma point spread.beta
is used to account for prior knowledge about the distribution of the mean.kappa
is a secondary scaling parameter usually set to 0 or 3-dim_x.The generator class is:
class JulierSigmaPoints(dim_x, kappa=0.0, **kwargs)
This class is a wrapper around yaflUKFJulierSt
. It extends yaflSigmaBase
and adds the following attributes:
kappa
is a secondary scaling parameter usually set to 0
or 3-dim_x
.The base class for all UKF variants is
class yaflpy.yaflUnscentedBase(dim_x, dim_z, dt, hx, fx, points, x_mean_fn=None, z_mean_fn=None, residual_x=None, residual_z=None)
where:
points :yaflSigmaBase
is a sigma points generator object.x_mean_fn :function(state_sigmas)
is function which computes state sigma points mean. Takes np.array((points_number, dim_x))
, returns np.array((dim_x,))
.z_mean_fn :function(measurement_sigmas)
is function which computes measurements sigma points mean. Takes np.array((points_number, dim_z))
, returns np.array((dim_z,))
.residual_x :function(a, b)
, optional, is state residual function.This class is very simmilar to filterpy.kalman.UnscentedKalmanFilter
, see FilterPy docs.
This class extends yaflpy.yaflKalmanBase
. This class is not supposet to be used directly.
class yaflpy.UnscentedBierman(dim_x, dim_z, dt, hx, fx, points, x_mean_fn=None, z_mean_fn=None, residual_x=None, residual_z=None)
This class extends yaflpy.yaflUnscentedBase
by implementing _update(self)
method.
The example code is:
from yaflpy import MerweSigmaPoints as SP
from yaflpy import UnscentedBierman as KF
# Bootstrap the filter
STD = 100.
def _fx(x, dt, **fx_args):
x = x.copy()
x[0] += x[1] * dt
x[2] += x[3] * dt
return x
def _hx(x, **hx_args):
if hx_args:
print(hx_args)
return np.array([x[0], x[2]])
def _zrf(a,b):
return a - b
sp = SP(4, 0.1, 2., 0)
kf = KF(4, 2, 1., _hx, _fx, sp, residual_z=_zrf)
kf.x[0] = 0.
kf.x[1] = 0.3
kf.Dp *= .00001
kf.Dq *= 1.0e-8
kf.Dr *= STD*STD
kf.Dr[0] *= .75
kf.Ur += .5
# Generate some data
N = 6000
clean = np.zeros((N, 2))
noisy = np.zeros((N, 2))
kf_out = np.zeros((N, 2))
t = np.zeros((N,), dtype=np.float)
for i in range(1, len(clean)):
clean[i] = clean[i-1] + np.array([1.,1.])
noisy[i] = clean[i] + np.random.normal(scale=STD, size=2)
t[i] = i
# Run the filter
for i, z in enumerate(noisy):
kf.predict()
kf.update(z)
kf_out[i] = kf.zp
class yaflpy.UnscentedAdaptiveBierman(dim_x, dim_z, yaflFloat dt, hx, fx, points, **kwargs)
This class extends yaflpy.yaflUnscentedBase
by addition of chi2
attribute and implementation of _update(self)
method.
THe usage is similar to yaflpy.UnscentedBierman
.
class yaflpy.UnscentedRobustBierman(dim_x, dim_z, dt, hx, fx, points, gz=None, gdotz=None, **kwargs)
This class extends yaflpy.yaflRobustUKFBase
by implementation of _update(self)
method.
THe usage is similar to yaflpy.UnscentedBierman
.
The yaflpy.yaflRobustUKFBase
class extends yaflpy.yaflUnscentedBase
by addition of the following hidden attrbutes:
gz :function(nu, **hx_args)
is influence function. Returns: float
.gdotz :function(nu, **hx_args)
is influence function first derivative. Returns: float
.class yaflpy.UnscentedAdaptiveRobustBierman(dim_x, dim_z, dt, hx, fx, points, **kwargs)
This class extends yaflpy.yaflAdaptiveRobustUKFBase
by implementation of _update(self)
method.
THe usage is similar to yaflpy.UnscentedBierman
.
The yaflpy.yaflAdaptiveRobustUKFBase
class extends yaflpy.yaflRobustUKFBase
by addition chi2
attribute.
class yaflpy.Unscented(dim_x, dim_z, dt, hx, fx, points, **kwargs)
This class extends yaflpy.yaflUnscentedBase
by implementation of _update
method and by addition of the following attributes:
Us :np.array((max(1, (dim_x * (dim_x - 1))//2), ))
is vector with upper triangular elements of SDs :np.array((dim_x, ))
is vector with diagonal elements of S
See FilterPy docs for details.THe usage is similar to yaflpy.UnscentedBierman
.
class yaflpy.UnscentedAdaptive(dim_x, dim_z, dt, hx, fx, points, aplha = 0.000001, **kwargs)
where:
alpha: float
is probability margin which is use to initiate chi2
attribute by scipy.stats.chi2.ppf(1.0 - aplha, dim_z)
This class extends yaflpy.Unscented
by implementation of _update
method and by addition of chi2
attribute.
THe usage is similar to yaflpy.UnscentedBierman
.
Happy prototyping with YAFL python extension.