Unscented_Kalman_Filter

所属分类:C/C++基础
开发工具:Jupyter Notebook
文件大小:5318KB
下载次数:0
上传日期:2019-11-27 20:58:57
上 传 者sh-1993
说明:  Python和C++中用于跟踪和定位应用程序的无中心卡尔曼滤波
(Unscented Kalman filtering in Python and C++ for tracking and localization applications)

文件列表:
.ipynb_checkpoints (0, 2019-11-28)
.ipynb_checkpoints\ukf_ctrv_test-checkpoint.ipynb (112254, 2019-11-28)
Performance_CTRV.md (1392, 2019-11-28)
__pycache__ (0, 2019-11-28)
__pycache__\process_measurement.cpython-35.pyc (2892, 2019-11-28)
__pycache__\ukf.cpython-35.pyc (8335, 2019-11-28)
figs (0, 2019-11-28)
figs\Qa.gif (1118, 2019-11-28)
figs\aug_P.gif (1010, 2019-11-28)
figs\aug_sigma_pts.gif (4513, 2019-11-28)
figs\aug_state.gif (1410, 2019-11-28)
figs\lidar_2D_tracking.gif (4052584, 2019-11-28)
figs\perf_x_y_a_1.png (45537, 2019-11-28)
figs\perf_x_y_a_100.png (40804, 2019-11-28)
figs\prediction.gif (2564, 2019-11-28)
figs\process.gif (6469, 2019-11-28)
figs\process_augmented.gif (9875, 2019-11-28)
figs\sigma_pts.gif (4030, 2019-11-28)
figs\state.gif (937, 2019-11-28)
figs\tracking_a_1.gif (368310, 2019-11-28)
figs\tracking_a_100.gif (664511, 2019-11-28)
figs\ukf_tracking.gif (257225, 2019-11-28)
figs\update.gif (6173, 2019-11-28)
figs\weights1.gif (4378, 2019-11-28)
figs\weights2.gif (3424, 2019-11-28)
gt.npz (14636, 2019-11-28)
gt_car3.npz (14636, 2019-11-28)
lidar.npz (9836, 2019-11-28)
lidar_car3.npz (9836, 2019-11-28)
process_measurement.py (3205, 2019-11-28)
ukf.py (11360, 2019-11-28)
ukf_augmented_ctrv1.py (16375, 2019-11-28)
ukf_ctrv_test.ipynb (146527, 2019-11-28)
ukf_notes.md (4160, 2019-11-28)

# Unscented_Kalman_Filter Unscented Kalman filtering in Python and C++ for tracking and localization ![img](https://github.com/kcg2015/Unscented_Kalman_Filter/blob/master/figs/tracking_a_100.gif) ![img](https://github.com/kcg2015/Unscented_Kalman_Filter/blob/master/figs/lidar_2D_tracking.gif) ## Introduction This repo implements an unscented Kalman filter (UKF) class in python, to be further integrated into tracking and localization related projects. I take inspiration from and am informed by the repos such as: * https://github.com/rlabbe/filterpy * https://github.com/AtsushiSakai/PythonRobotics/tree/master/Localization. The requirements for the UKF class are: * The UKF class should be self-contained -- i.e. it should include all the function blocks within a single class, including the Sigma point generation and update. * The UKF class should have the flexibility of taking any forms of process and measurement functions as input. * The UKF class should be able to deal with both additive and non-additive process noise. In particular, the UKF class should have the capability of implementing Sigma points augmentation as often required for non-additive process noise. This capability, to the best of my knowledge, is not adequately addressed in the two repos mentioned above. ### Key Files * `ukf.py` -- implements the UKF class * `process_measurement.py` -- implements a few process and measurement modeld. This script will be regularly updated. * `ukf_ctrv_test.ipynb` -- an Ipython notebook that illustrates the tracking of highway vehicle using UKF and constant turn rate and velocity (CTRV) model. ## Implementation Notes #### Sigma Points Augmentation The possible confusions and difficulties in implementing UKF could happen in Sigma points augmentation. The augmentation is often needed when the process noise is non-additive. To avoid possible confusions, I summarize in the following table the dimension of Sigma points and augmented Sigma points used in state, process, and measurement, respectively. Note that n and n_\a represent the dimension of the (regular) state and augmented state, respectively. | | Sigma Points |Augmented Sigma points | |--- |--- |--- | | State | (2n + 1, n ) | (2n\_a + 1, n_a) | | Process | (2n + 1, n ) | (2n\_a + 1, n ) | | Measurement| (2n + 1, n\_z) | (2n\_a + 1, n_z) | #### Weights and Sigma Points The calculation of the weights is implemented as in the following function: ``` def calculate_weights(self): """ Calculate the weights associated with sigma points. The weights depend on parameters dim_x, alpha, beta, and gamma. The number of sigma points required is 2 * dim_x + 1 """ # dimension for determining the number of sigma points generated dim = self.dim_x if not self.augmentation else self.dim_xa if self.sigma_mode == 1: lambda_ = self.alpha_**2 * (dim + self.kappa_) - dim Wc = np.full(2*dim + 1, 1. / (2*(dim + lambda_))) # Wm = np.full(2*dim + 1, 1. / (2*(dim + lambda_))) Wc[0] = lambda_ / (dim + lambda_) + (1. - self.alpha_**2 + self.beta_) Wm[0] = lambda_ / (dim + lambda_) elif self.sigma_mode == 2: lambda_ = 3 - dim Wc = np.full(2*dim + 1, 1./ (2*(dim + lambda_))) Wm = np.full(2*dim + 1, 1./ (2*(dim + lambda_))) Wc[0], Wm[0] = lambda_ /(dim + lambda_), lambda_ /(dim + lambda_) return (Wc, Wm) ``` Here we consider two approaches (modes). In Mode 1, /lambda not only depends on the dimension of the state (or augmented state), but also on parameters such as /alpha and /kappa. Weights for mean and covariance matrix are calculated (slight) differently. ![img](https://github.com/kcg2015/Unscented_Kalman_Filter/blob/master/figs/weights1.gif) In Mode 2, /lambda depends only on the dimension. In addition, the weights for mean and covariance are the same. ![img](https://github.com/kcg2015/Unscented_Kalman_Filter/blob/master/figs/weights2.gif) The Sigma points are calculated as the following: ![img](https://github.com/kcg2015/Unscented_Kalman_Filter/blob/master/figs/sigma_pts.gif) The augmented Sigma points are calculated as the following: ![img](https://github.com/kcg2015/Unscented_Kalman_Filter/blob/master/figs/aug_sigma_pts.gif) The `update_sigma_pts` calculated the Sigma points or augmented Sigma points ``` def update_sigma_pts(self): """ Create (update) Sigma points during the prediction stage """ if not self.augmentation: dim, x, P = self.dim_x, self.x, self.P else: dim = self.dim_xa x = np.concatenate([self.x, self.xa]) P = block_diag(self.P, self.Qa) if self.sigma_mode == 1: lambda_ = self.alpha_**2 * (dim + self.kappa_) - dim elif self.sigma_mode == 2: lambda_ = 3 - dim U = cholesky((dim + lambda_) * P) self.sigma_pts[0] = x for k in range (dim): self.sigma_pts[k + 1] = x + U[k] self.sigma_pts[dim + k + 1] = x - U[k] ``` Consider an example of CTRV model, if the impact of the noise is not considered, the process model is as following: ![img](https://github.com/kcg2015/Unscented_Kalman_Filter/blob/master/figs/process.gif) If we also consider the impact of non-additive noise, the process model is as following: ![img](https://github.com/kcg2015/Unscented_Kalman_Filter/blob/master/figs/process_augmented.gif). In this case, the dimension of the state need to augmented. That is, from a regular state of dimension of five, to an augmented state of dimension seven. ![img](https://github.com/kcg2015/Unscented_Kalman_Filter/blob/master/figs/state.gif) ![img](https://github.com/kcg2015/Unscented_Kalman_Filter/blob/master/figs/aug_state.gif) Note that to calculate the augmented Sigma points, we need to use \Sigma_a as follows, ![img](https://github.com/kcg2015/Unscented_Kalman_Filter/blob/master/figs/aug_P.gif) The process noise matrix Q_a is given by ![img](https://github.com/kcg2015/Unscented_Kalman_Filter/blob/master/figs/Qa.gif) This line implements the above operations: ``` P = block_diag(self.P, self.Qa) ``` #### Prediction The formula of the prediction stage is given by: ![img](https://github.com/kcg2015/Unscented_Kalman_Filter/blob/master/figs/prediction.gif) Note that for non-additive noise, the process noise covariance matrix Q, by default, is set to zero. The computation of process Sigma points is implemented in `compute_process_sigma_pts ()`: ``` def compute_process_sigma_pts(self, input_sigma_pts, **fx_args): """ Calculate the sigam points transformed the process function fx Input: input_sigma_pts: input sigma points **fx_args: keywords/arguments associated with process/system function defined as fx Output: output_sigma_pts: sigma points transformed by the process """ fx, dt = self.fx, self.dt n_sigmas, _ = input_sigma_pts.shape output_sigma_pts = zeros([n_sigmas, self.dim_x]) for i, s in enumerate(input_sigma_pts): output_sigma_pts[i] = fx(s, dt, **fx_args) return output_sigma_pts ``` The prediction stage is implemented in `prediction ()`, which calls `calculate_mean_covariance (0` ``` def prediction(self, **fx_args): """ Prediction, calculated the prior state estimate and covariance Input: **fx_args: keywords/arguments associated with process/system function defined as fx """ fx = self.fx self.update_sigma_pts( ) # update the sigma points sigma_pts = self.sigma_pts process_sigma_pts = self.compute_process_sigma_pts(sigma_pts, **fx_args) # sigma points transformed by the process self.sigma_pts_f = process_sigma_pts # mean and covariance of sigma transformed mean and covariance if not self.x_resid: self.x, self.P = self.calculate_mean_covariance(process_sigma_pts, self.Q) else: self.x, self.P = self.calculate_mean_covariance(process_sigma_pts, self.Q, adjust = True, indices = self.x_resid_indices) self.x_prior, self.P_prior = np.copy(self.x), np.copy(self.P) ``` #### Update The update stage is implemented in `update()`, which calls `compute_measurement_sigma_pts()`. ![img](https://github.com/kcg2015/Unscented_Kalman_Filter/blob/master/figs/update.gif) ``` def update(self, z, **hx_args): """ Update step, calculate the (new) posterior state and covariance Input: z: measuremnt **hx_args: keywords/arguments associated with measurement function defined in hx """ hx = self.hx sigmas_f = self.sigma_pts_f n_sigmas = sigmas_f.shape[0] # Transform sigma points from state space to measurement space sigmas_h = self.compute_measurement_sigma_pts(sigmas_f, **hx_args) self.sigma_pts_h = sigmas_h if not self.z_resid: zp, Pz = self.calculate_mean_covariance(sigmas_h, self.R) else: zp, Pz = self.calculate_mean_covariance(sigmas_h, self.R, adjust = True, indices = self.z_resid_indices) self.S = Pz # Compute cross variance of the state and the measurement Pxz = np.zeros((self.dim_x, self.dim_z)) for i in range(n_sigmas): x_r = sigmas_f[i] - self.x if self.x_resid: x_r = self.residual(x_r, self.x_resid_indices) z_r = sigmas_h[i] - zp if self.z_resid: z_r = self.residual(z_r, self.z_resid_indices) Pxz += self.Wc[i] * outer(x_r, z_r) self.SI = inv(Pz) K = dot(Pxz, inv(Pz)) # Kalman gain self.K = K # New state estimae and covariance maxtrix self.y = z - zp self.x = self.x + dot(K, z - zp) self.P = self.P - dot(K, Pz).dot(K.T) self.x_post = self.x.copy() self.P_post = self.P.copy() self.z = deepcopy(z) ```

近期下载者

相关文件


收藏者