kalman_smooth
This module implements constant-derivative model-based smoothers based on Kalman filtering and its generalization.
- pynumdiff.kalman_smooth.rtsdiff(x, dt_or_t, order, log_qr_ratio, forwardbackward=False, axis=0, circular=False)
Perform Rauch-Tung-Striebel smoothing with a naive constant derivative model. Makes use of
kalman_filterandrts_smooth, which are made public.constant_Xmethods in this module call this function.- Parameters:
x (np.array[float]) – data series to differentiate. May contain NaN values (missing data); NaNs are excluded from fitting and imputed by dynamical model evolution. May be multidimensional; see
axis.dt_or_t (float or array[float]) – This function supports variable step size. This parameter is either the constant \(\Delta t\) if given as a single float, or data locations if given as an array of same length as
x.order (int) – which derivative to stabilize in the constant-derivative model
log_qr_ratio – the log of the process noise level divided by the measurement noise level, because, per our analysis, the mean result is dependent on the relative rather than absolute size of \(q\) and \(r\).
forwardbackward (bool) – indicates whether to run smoother forwards and backwards (usually achieves better estimate at end points)
axis (int) – data dimension along which differentiation is performed
circular (bool) – if
True, treat the measured quantity as a circular variable in radians, wrapping the innovation to \([-\pi, \pi]\). The inputxmust be in radians; convert degrees withnp.deg2rad.
- Returns:
x_hat (np.array) – estimated (smoothed) x, same shape as input
x.
When
circular=True, wrapped to \([-\pi, \pi]\). - dxdt_hat (np.array) – estimated derivative of x, same shape as inputx
- pynumdiff.kalman_smooth.robustdiff(x, dt_or_t, order, log_q, log_r, proc_huberM=6, meas_huberM=0, axis=0)
Perform outlier-robust differentiation by solving the Maximum A Priori optimization problem: \(\text{argmin}_{\{x_n\}} \sum_{n=0}^{N-1} V(R^{-1/2}(y_n - C x_n)) + \sum_{n=1}^{N-1} J(Q_{n-1}^{-1/2}(x_n - A_{n-1} x_{n-1}))\), where \(A,Q,C,R\) come from an assumed constant derivative model and \(V,J\) are the \(\ell_1\) norm or Huber loss rather than the \(\ell_2\) norm optimized by RTS smoothing. This problem is convex, so this method calls
convex_smooth, which in turn forms a sparse CVXPY problem and invokes CLARABEL.Note that for Huber losses,
Mis the radius where the Huber loss function turns from quadratic to linear. Because all loss function inputs are normalized by noise level, \(q^{1/2}\) or \(r^{1/2}\),Mis in units of inlier standard deviation. In other words, this choice affects which portion of inliers might be treated as outliers. For example, assuming Gaussian inliers, the portion beyond \(M\sigma\) isoutlier_portion = 2*(1 - scipy.stats.norm.cdf(M)). The inverse of this isM = scipy.stats.norm.ppf(1 - outlier_portion/2). As \(M \to \infty\), Huber becomes the 1/2-sum-of-squares case, \(\frac{1}{2}\|\cdot\|_2^2\), because the normalization constant of the Huber loss (See \(c_2\) in section 6 of this paper, missing a \(\sqrt{\cdot}\) term there, see p2700) approaches 1 as \(M\) increases. Similarly, asMapproaches 0, Huber reduces to the \(\ell_1\) norm case, because the normalization constant approaches \(\frac{\sqrt{2}}{M}\), cancelling the \(M\) multiplying \(|\cdot|\) in the Huber function, and leaving behind \(\sqrt{2}\), the proper \(\ell_1\) normalization.Note that
log_qandproc_huberMare coupled, as arelog_randmeas_huberM, via the relation \(\text{Huber}(q^{-1/2}v, M) = q^{-1}\text{Huber}(v, Mq^{1/2})\), but these are still independent enough that for the purposes of optimization we cannot collapse them. Nor canlog_qandlog_rbe combined intolog_qr_ratioas in RTS smoothing without the addition of a new absolute scale parameter, becausee \(q\) and \(r\) interact with the distinct Huber \(M\) parameters.- Parameters:
x (np.array[float]) – data series to differentiate. May be multidimensional; see
axis.dt_or_t (float or array[float]) – This function supports variable step size. This parameter is either the constant \(\Delta t\) if given as a single float, or data locations if given as an array of same length as
x.order (int) – which derivative to stabilize in the constant-derivative model (1=velocity, 2=acceleration, 3=jerk)
log_q (float) – base 10 logarithm of process noise variance, so
q = 10**log_qlog_r (float) – base 10 logarithm of measurement noise variance, so
r = 10**log_rproc_huberM (float) – quadratic-to-linear transition point for process loss
meas_huberM (float) – quadratic-to-linear transition point for measurement loss
axis (int) – data dimension along which differentiation is performed
- Returns:
x_hat (np.array) – estimated (smoothed) x, same shape as input
xdxdt_hat (np.array) – estimated derivative of x, same shape as input
x
- pynumdiff.kalman_smooth.constant_velocity(x, dt, params=None, options=None, r=None, q=None, forwardbackward=True)
Run a forward-backward constant velocity RTS Kalman smoother to estimate the derivative.
Deprecated, prefer
rtsdiffwith order 1 instead.- Parameters:
x (np.array[float]) – data series to differentiate
dt (float) – step size
params (list[float]) – (deprecated, prefer
randq)options – (deprecated, prefer
forwardbackward) a dictionary consisting of {‘forwardbackward’: (bool)}r (float) – variance of the signal noise
q (float) – variance of the constant velocity model
forwardbackward (bool) – indicates whether to run smoother forwards and backwards (usually achieves better estimate at end points)
- Returns:
x_hat (np.array) – estimated (smoothed) x
dxdt_hat (np.array) – estimated derivative of x
- pynumdiff.kalman_smooth.constant_acceleration(x, dt, params=None, options=None, r=None, q=None, forwardbackward=True)
Run a forward-backward constant acceleration RTS Kalman smoother to estimate the derivative.
Deprecated, prefer
rtsdiffwith order 2 instead.- Parameters:
x (np.array[float]) – data series to differentiate
dt (float) – step size
params (list[float]) – (deprecated, prefer
randq)options – (deprecated, prefer
forwardbackward) a dictionary consisting of {‘forwardbackward’: (bool)}r (float) – variance of the signal noise
q (float) – variance of the constant acceleration model
forwardbackward (bool) – indicates whether to run smoother forwards and backwards (usually achieves better estimate at end points)
- Returns:
x_hat (np.array) – estimated (smoothed) x
dxdt_hat (np.array) – estimated derivative of x
- pynumdiff.kalman_smooth.constant_jerk(x, dt, params=None, options=None, r=None, q=None, forwardbackward=True)
Run a forward-backward constant jerk RTS Kalman smoother to estimate the derivative.
Deprecated, prefer
rtsdiffwith order 3 instead.- Parameters:
x (np.array[float]) – data series to differentiate
dt (float) – step size
params (list[float]) – (deprecated, prefer
randq)options – (deprecated, prefer
forwardbackward) a dictionary consisting of {‘forwardbackward’: (bool)}r (float) – variance of the signal noise
q (float) – variance of the constant jerk model
forwardbackward (bool) – indicates whether to run smoother forwards and backwards (usually achieves better estimate at end points)
- Returns:
x_hat (np.array) – estimated (smoothed) x
dxdt_hat (np.array) – estimated derivative of x
- pynumdiff.kalman_smooth.kalman_filter(y, xhat0, P0, A, Q, C, R, B=None, u=None, save_P=True, innovation_fn=None)
Run the forward pass of a Kalman filter. Expects discrete-time matrices; use
scipy.linalg.expm()in the caller to convert from continuous time if needed.- Parameters:
y (np.array) – series of measurements, stacked in the direction of axis 0.
xhat0 (np.array) – a priori guess of initial state at the time of the first measurement
P0 (np.array) – a priori guess of state covariance at the time of first measurement (often identity matrix)
A (np.array) – discrete-time state transition matrix. If 2D (\(m \times m\)), the same matrix is used for all steps; if 3D (\(N-1 \times m \times m\)),
A[n-1]is used for the transition from step \(n-1\) to \(n\).Q (np.array) – discrete-time process noise covariance. Same 2D or 3D shape convention as
A.C (np.array) – measurement matrix
R (np.array) – measurement noise covariance
B (np.array) – optional discrete-time control matrix, optionally stacked like
A.u (np.array) – optional control inputs, stacked in the direction of axis 0
save_P (bool) – whether to save history of error covariance and a priori state estimates, used with rts smoothing but nonstandard to compute for ordinary filtering
innovation_fn (callable) – optional function taking measurements and predicted measurements and returning the innovation. When
None, traditional subtraction is used. This is primarily exposed to handle angles, which have a wrapped domain, so alternative displacement measurelambda y, pred: (y - pred + np.pi) % (2*np.pi) - np.piis more appropriate.
- Returns:
xhat_pre (np.array) – a priori estimates of xhat, with axis=0 the batch dimension, so xhat[n] gets the nth step
xhat_post (np.array) – a posteriori estimates of xhat
P_pre (np.array) – a priori estimates of P
P_post (np.array) – a posteriori estimates of P
if
save_PisTrueelse only xhat_post to save memory
- pynumdiff.kalman_smooth.rts_smooth(A, xhat_pre, xhat_post, P_pre, P_post, compute_P_smooth=True)
Perform Rauch-Tung-Striebel smoothing, using information from forward Kalman filtering.
- Parameters:
A (np.array) – discrete-time state transition matrix. If 2D (\(m \times m\)), the same matrix is used for all steps; if 3D (\(N-1 \times m \times m\)),
A[n-1]is used for the transition from step \(n-1\) to \(n\).xhat_pre (np.array) – a priori estimates of xhat from a kalman_filter forward pass
xhat_post (np.array) – a posteriori estimates of xhat from a kalman_filter forward pass
P_pre (np.array) – a priori estimates of P from a kalman_filter forward pass
P_post (np.array) – a posteriori estimates of P from a kalman_filter forward pass
compute_P_smooth (bool) – it’s extra work if you don’t need it
- Returns:
xhat_smooth (np.array) – RTS smoothed xhat
P_smooth (np.array) – RTS smoothed P estimates
if
compute_P_smoothisTrueelse only xhat_smooth
- pynumdiff.kalman_smooth.convex_smooth(y, A, Q, C, R, B=None, u=None, proc_huberM=6, meas_huberM=0)
Solve the optimization problem for robust smoothing using CVXPY.
- Parameters:
y (np.array[float]) – measurements
A (np.array) – discrete-time state transition matrix. If 2D (\(m \times m\)), the same matrix is used for all steps; if 3D (\(N-1 \times m \times m\)),
A[n-1]is used for the transition from step \(n-1\) to \(n\).Q (np.array) – discrete-time process noise covariance. Same 2D or 3D shape convention as
A.C (np.array) – measurement matrix
R (np.array) – measurement noise covariance matrix
B (np.array) – optional discrete-time control matrix, optionally stacked like
A.u (np.array) – optional control inputs, stacked in the direction of axis 0
proc_huberM (float) – Huber loss parameter. \(M=0\) reduces to \(\sqrt{2}\ell_1\).
meas_huberM (float) – Huber loss parameter. \(M=\infty\) reduces to \(\frac{1}{2}\ell_2^2\).
- Returns:
(np.array) – state estimates (state_dim x N)