Unscented Kalman Filter Project
Self-Driving Car Engineer Nanodegree Program
In this project I implemented an Unscented Kalman Filter to estimate the state of a moving object of interest with noisy LIDAR and RADAR measurements.
Project Explanation
In this project I implemented an UKF using the CTRV motion model.
As a quick reminder, a Kalman Filter only handles models with linear equations whereas the Extended and Unscented Kalman Filters handle non-linear equations. I have implemented an Extended Kalman Filter in my previous project that you can find here.
The CTRV model stands for Constant Turn Rate and Velocity magnitude model. It takes into account a turn rate phi dot into its state vector and would therefore predict the position of the car more accurately when it turns.
Such as the Extended KF, the Unscented KF has a prediction step and a measurement step. I’ll break them down:
Prediction Step
We need to compute the integral of these values over a small time step (delta t) to obtain equations giving us the values at the time t+1.
To model the stochastic part of the process model we add some process noise.
The Extended Kalman Filter uses the Jacobian matrix to linearize non-linear functions. Instead of using this technique, the Unscented Kalman Filter takes the Posterior Gaussian distribution at time k and predicts the Prior Gaussian distribution at time k+1 through a process called Unscented Transformation. The tricky part is that we do not have any linear equations to do so.
The Unscented Transformation chooses Sigma points. These points are supposed to represent accurately the Posterior Gaussian distribution. Here is a summary of how to generate them:
I generated 2 * n_x + 1
points where n_x
is the dimension of the state vector plus
the dimension of the process noise. This technique is called Augmentation. It is because
we do have non-linear process noise effects that it must be accounted for when generating
the sigma points.
In this project the dimensionality of my state vector is 5 and the process noise is 2. That makes it 15 sigma points. Lambda governs the spreading from the mean value.
They are then inserted into the non-linear process model function f().
From the obtained points, I then extracted a new Gaussian distribution. Here are the formulas I’ve used to compute the new mean and covariance at each prediction step.
There are multiple ways to determine the weights omega. The only thing to remember is that it is linked to lambda, as you want to recover from the spreading inferred to the sigma points when they were calculated.
Measurement Step
The measurement step moves the gaussian distribution predicted by the prediction step according to the receive actual sensor measurement. The shortcut that is often used is to generated sigma points from the prediction step.
To be able to use the generated sigma points from the prediction step, I mapped them onto the measurement space.
The major difference at this step is that we add the measurement covariance noise instead to applying the non-linear process function as I did in the previous step. This is due to the fact that the impact of the noise on the measurement is purely additive and doesn’t have a non-linear relationship.
For the laser data, it was even easier since the sigma points are already expressed in the measurement space (px and py).
Parameter Tuning
Process noise
I tweaked the process noise standard deviations (longitudinal and rotation accelerations) to obtain the best ones.
The way to find a first estimate of these values is to think about what they represent in real life. The square root of std_a
is expressed in meters / second. The question is what is the expected acceleration of a bicycle at any given time.
I first started with
std_a = 3;
std_yawdd = 0.8;
and obtained the following RMSE (root mean squared error):
I then observed that by constraining them a little more, the result did improve.
I ended up with these process noises:
std_a = 2;
std_yawdd = 0.3;
Measurement noise
I calculated the NIS (Normalized Innovation Squared) for each measurements to assess whether my measurement noises were set properly.
I obtained the following result at the end of the simulation using the dataset 1:
82.7309 % radar measurement below 95% limit.
97.992 % laser measurement below 95% limit.
Having only 82% of epsilon values under the 95% limit means that I was underestimating the uncertainty (therefore the noise) of the radar measurements.
On the contrary, having 98% of the laser measurements below the 95% limit (taken this time with 2 degrees of freedom instead of 3) means that I was overestimating the uncertainty of the laser measurements.
As a result, I changed the radar measurement noise standard deviation angle from std_radphi_ = 0.0175;
to std_radphi_ = 0.03;
.
I also changed reduced the laser measurement noise by 0.01
to:
std_laspx_ = 0.14;
std_laspy_ = 0.14;
I finally obtained NIS values as follow:
95.9839 % radar measurement below 95% limit.
95.9839 % laser measurement below 95% limit.
As a caveat, it seemed to have increased a tiny bit the RMSE of each observed parameters except the speed error in the x direction vx
which is the highest of them all.
Second caveat, I could not actually change these values to pass the project …
Unit Tests
It is the first time I created unit tests for a project of the SDC Nanodegree and I’m very glad I took the time to do it. After implementing every functions I needed to complete each step of the UKF, my project was done ! No debugging time was needed whatsoever.
It will also save me some time if later on I want to merge this code into a larger project because if something goes wrong the individual functions won’t be the cause. I’ll just have to write integration tests.
To run the tests:
cmake -Dtest=ON
make test
or
./unit_tests
Here is my unit tests output:
[==========] Running 12 tests from 4 test cases.
[----------] Global test environment set-up.
[----------] 1 test from Normalize
[ RUN ] Normalize.EdgeCases
[ OK ] Normalize.EdgeCases (0 ms)
[----------] 1 test from Normalize (0 ms total)
[----------] 2 tests from NIS
[ RUN ] NIS.NIS_ZeroVectors
[ OK ] NIS.NIS_ZeroVectors (0 ms)
[ RUN ] NIS.NIS_ProperFunctionning
[ OK ] NIS.NIS_ProperFunctionning (0 ms)
[----------] 2 tests from NIS (0 ms total)
[----------] 4 tests from isNISAboveLimit
[ RUN ] isNISAboveLimit.DF_2_Below
[ OK ] isNISAboveLimit.DF_2_Below (0 ms)
[ RUN ] isNISAboveLimit.DF_2_Above
[ OK ] isNISAboveLimit.DF_2_Above (0 ms)
[ RUN ] isNISAboveLimit.DF_3_Below
[ OK ] isNISAboveLimit.DF_3_Below (0 ms)
[ RUN ] isNISAboveLimit.DF_3_Above
[ OK ] isNISAboveLimit.DF_3_Above (0 ms)
[----------] 4 tests from isNISAboveLimit (0 ms total)
[----------] 5 tests from UKFTest
[ RUN ] UKFTest.AugmentedSigmaPointGeneration
[ OK ] UKFTest.AugmentedSigmaPointGeneration (1 ms)
[ RUN ] UKFTest.PredictSigmaPoint
[ OK ] UKFTest.PredictSigmaPoint (0 ms)
[ RUN ] UKFTest.PredictMeanAndCovariance
[ OK ] UKFTest.PredictMeanAndCovariance (0 ms)
[ RUN ] UKFTest.PredictRadarMeasurement
[ OK ] UKFTest.PredictRadarMeasurement (1 ms)
[ RUN ] UKFTest.UpdateState
[ OK ] UKFTest.UpdateState (0 ms)
[----------] 5 tests from UKFTest (2 ms total)
[----------] Global test environment tear-down
[==========] 12 tests from 4 test cases ran. (3 ms total)
[ PASSED ] 12 tests.
Program ended with exit code: 0
Final result
Here is a screenshot of the final result:
If we zoom closer, it is quite impressive to see that the green marks (the filtered position of the car) seems very accurate and filters out very well the sparse measurement from both sensors.
Installation
This project involves the Term 2 Simulator which can be downloaded here
This repository includes two files that can be used to set up and intall uWebSocketIO for either Linux or Mac systems. For windows you can use either Docker, VMware, or even Windows 10 Bash on Ubuntu to install uWebSocketIO. Please see this concept in the classroom for the required version and installation scripts.
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
Tips for setting up your environment can be found here
Important Dependencies
- cmake >= 3.5
- All OSes: click here for installation instructions
- make >= 4.1 (Linux, Mac), 3.81 (Windows)
- Linux: make is installed by default on most Linux distros
- Mac: install Xcode command line tools to get make
- Windows: Click here for installation instructions
- gcc/g++ >= 5.4
- Linux: gcc / g++ is installed by default on most Linux distros
- Mac: same deal as make - install Xcode command line tools
- Windows: recommend using MinGW
Generating Additional Data
This is optional!
If you’d like to generate your own radar and lidar data, see the utilities repo for Matlab scripts that can generate additional data.
Project Instructions and Rubric
This information is only accessible by people who are already enrolled in Term 2 of CarND. If you are enrolled, see the project page for instructions and the project rubric.