In this project an Unscented Kalman Filter is implemented in C++ to track a moving object (bicycle), while assuming a "Constant Turn Rate & Velocity Magnitude" (CTRV) Model, fusing both LIDAR and RADAR sensor data to accurately estimate the object's position.
*For More Information check the C++ code in the src directory.
This project involves the Udacity's 2-D Simulator which is connected to the UKF via an open source package called uWebSocketIO. This package facilitates the connection between the simulator and the code executed. The package does this by setting up a web socket server connection from the C++ program to the simulator, which acts as the host.
This repository includes two files that can be used to set up and intall uWebSocketIO for either Linux or Mac systems.
Once the install for uWebSocketIO is complete, the main program can be built and ran by doing the following from the project top directory.
- mkdir build
- cd build
- cmake ..
- make
- ./UnscentedKF
Here is the main protcol that main.cpp uses for uWebSocketIO in communicating with the simulator:
-
INPUT: values provided by the simulator to the c++ program
- ["sensor_measurement"] => the measurment that the simulator observed (either lidar or radar)
-
OUTPUT: values provided by the c++ program to the simulator
- ["estimate_x"] <= kalman filter estimated position x
- ["estimate_y"] <= kalman filter estimated position y
- ["rmse_x"]
- ["rmse_y"]
- ["rmse_vx"]
- ["rmse_vy"]
- cmake >= 3.5
- make >= 4.1
- gcc/g++ >= 5.4
- Clone this repo.
- Make a build directory:
mkdir build && cd build
- Compile:
cmake .. && make
- Run it:
./UnscentedKF
The part implemented is all executed in the UKF instance method UKF::ProcessMeasurement()
using the data input in the MeasurementPackage
object: meas_package
.
The input indicates:
- Timestamp
- Raw Measurements from Sensor
- Sensor Type (RADAR or LASER)
-
Initialize the UKF instant attributes
- Declare the Dimensions of the State Vector
n_x_ = 5;
- Declare the Dimensions of the Augmented State Vector
n_aug_ = 7;
- Declare the tuned standard deviation constants representing the process noise covariance:
- Longitudinal Acceleration Noise - Standard Deviation
std_a_ = 0.75;
- YAW Acceleration Noise - Standard Deviation
std_yawdd_ = 0.75;
- Longitudinal Acceleration Noise - Standard Deviation
- Calculate the Sigma Points' Spread Parameter
lambda_ = 3 - n_aug_;
- Calculate the Sigma Points' Weight Vector of Size
2*n_aug + 1
for each of the 15 sigma point:- First Weight:
weights_(0) = lambda_ / (lambda_ + n_aug_);
- All Remaining Weights:
weights_ = 1 / (2*(lambda_ + n_aug_));
- First Weight:
- Declare the Dimensions of the State Vector
-
Initialize UKF Vectors and Matrices:
-
State Vector x_: [Px, Py, v, yaw, yawdot]
-
LIDAR Measurement: [Px, Py]
- X Position =
Px;
- Y Position =
Py;
- Initialize Velocity v, Yaw Angle yaw, Turn Rate yawdot to
0
.
- X Position =
-
RADAR Measurement: [rho, phi, rhodot]
- X Position =
rho * cos(phi);
- Y Position =
rho * sin(phi);
- Initialize Velocity v, Yaw Angle yaw, Turn Rate yawdot to
0
. - Calculations based on the following visualization of the RADAR Measurements.
- X Position =
-
-
State Covariance Matrix P_:
-
Initialization for P_ is tuned manually for both LIDAR and RADAR measurements and the matrix below acquired the lowest RMSE results:
P_ : Size (n_x_ x n_x_) --> (5 x 5)
Px Var 0.15 0 0 0 0 Py Var 0 0.15 0 0 0 V Var 0 0 50 0 0 Yaw Var 0 0 0 50 0 YawDot Var 0 0 0 0 50
-
-
Predicted Sigma Points Matrix
Xsig_pred_
: This matrix is initialized with zeros of size(n_x_ x 2*n_aug_+1)
--> (5 x 15)
-
-
Initialize time to
MeasurementPackage::timestamp_
to calculatedelta_t
between both current and next measurement. -
Set
is_initialized_
totrue
to start Prediction and Update Steps upon the next sensor measurement.
Regardless of the type of input sensor type meas_package.sensor_type_
:RADAR and LIDAR - the algorithm executes the same prediction functions
- Calculate the Augmented State Vector
x_aug
and Augmented State CovarianceP_aug
. - Generate 15 Augmented Sigma Points
Xsig_aug
for Previous estimated State Vector. Size >(n_aug_ x 2*n_aug_+1)
: (7 x 15) - Predict Sigma Points representing the Current State Vector
Xsig_pred_
. Size >(n_x_ x 2*n_aug_+1)
: (5 x 15) - Use predicted sigma points to estimate state mean
x_
and covarianceP_
using the precalculatedweights_
.
LIDAR and RADAR sensor data are treated differently since LIDAR data are in CARTESIAN coordinates, while RADAR data are in POLAR coordinates
-
Use Predicted Sigma Points
Xsig_pred_
and transform it to measurement space according to the sensor type:- LIDAR - 2 Dimensions Measurement Space:
- Calculate Sigma Points in measurement space
Zsig
using Px and Py from the Predicted Sigma PointsXsig_pred_
. Size (2 x 15) - Calculate Mean State
z_
and CovarianceS
fromZsig
. z_ - Size: 2x1 and S - Size: 2x2 - Add Measurement Noise Covariance
R
toS
R
Matrix includes sensor covariance for Px and Py. Size (2 x 2)
- Calculate Sigma Points in measurement space
- RADAR - 3 Dimensions Measurement Space:
- Calculate Sigma Points in measurement space
Zsig
using Px, Py, v and yaw from the Predicted Sigma PointsXsig_pred_
. Size (3 x 15)rho = sqrt( pow(px,2) + pow(py,2) );
phi = atan2(py,px);
rhodot = (px * cos(yaw) * v + py * sin(yaw) * v) / rho;
- Calculate Mean State
z_
and CovarianceS
fromZsig
. z_ - Size: 3x1 and S - Size: 3x3 - Add Measurement Noise Covariance
R
toS
R
Matrix includes sensor covariance for rho, phi and rhodot. Size (3 x 3)
- Calculate Sigma Points in measurement space
- LIDAR - 2 Dimensions Measurement Space:
-
Calculate Cross-correlation matrix
T
between Sigma Points in state space (5-d) and measurement space (2-d for LIDAR and 3-d for RADAR) -
Calculate Kalman Gain
K
-
Update (Correct) predicted State Vector
x_
and CovarianceP_
-
RMSE values are calculated by comparing the estimated state positions with the ground truth.
-
NIS values are calculated seperately and output in separate files for RADAR and LIDAR, determining the difference between predicted and actual measurement, while relating to the predicted measurement covariance matrix
S
.- RADAR has 3 Degrees of Freedom. (3-D) X^2.950: 0.35 - X^2.050: 7.82
- LIDAR has 2 Degrees of Freedom. (2-D) X^2.950: 0.10 - X^2.050: 5.99
- NIS follow chi squared distribution.
The following table represents a comparison of the RMSE values on Dataset 1 resulting from the Unscented vs the Extended Kalman Filters: It's obvious that the UKF results are entirely more accurate than the EKF (less erroneous).
RMSE | UKF | ? | EKF |
---|---|---|---|
x_position | 0.0632 | < | 0.0973 |
y_position | 0.0834 | < | 0.0855 |
x_velocity | 0.3092 | < | 0.4513 |
y_velocity | 0.2000 | < | 0.4399 |
*The green markers in the image above shows the estimated position of the object, which regardless of the noisy measurements is really accurate.
- Position Plot of Estimated (Blue Markers) vs Ground Truth (Red Line)
- Velocity Plot of Estimated (Blue Markers) vs Ground Truth (Red Line)
- LIDAR Data with respect to X^2.950: 0.10 - X^2.050: 5.99
- RADAR Data with respect to X^2.950: 0.35 - X^2.050: 7.82
The following table represents a comparison of the RMSE values on Dataset 2 resulting from the Unscented vs the Extended Kalman Filters: It's obvious that the UKF results are entirely more accurate than the EKF (less erroneous).
RMSE | UKF | ? | EKF |
---|---|---|---|
x_position | 0.0637 | < | 0.0726 |
y_position | 0.0604 | < | 0.0967 |
x_velocity | 0.3451 | < | 0.4579 |
y_velocity | 0.1802 | < | 0.4966 |
*The green markers in the image above shows the estimated position of the object, which regardless of the noisy measurements is really accurate.
- Position Plot of Estimated (Blue Markers) vs Ground Truth (Red Line)
- Velocity Plot of Estimated (Blue Markers) vs Ground Truth (Red Line)
- LIDAR Data with respect to X^2.950: 0.10 - X^2.050: 5.99
- RADAR Data with respect to X^2.950: 0.35 - X^2.050: 7.82