Unscented Kalman Filter (additive noise) Example

Introduction

This notebook is designed to demonstrate how to use the StateSpace.jl package to execute the Unscented Kalman filter with additive noise for a non-linear State Space model. The example that has been used here closely follows the example given in a paper by Rambabu Kandepu, Bjarne Foss and Lars Imsland "Applying the unscented Kalman filter for nonlinear state estimation". The example is the Van der Pol oscillator (Section 4.1 on page 6 of the paper). You can read the paper for full details.

For those of you that do not need/want the explanation of the model and the code, you can skip right to the end of this notebook where the entire section of code required to run this example is given.

The Problem

The problem that we will consider here is that of predicting the position $\mathbf{x} = (x_1, x_2)$ of a Van der Pol oscillator given some noisy measurements of its position.

The Process Model

The dynamic equations describing the rate of change of position of the oscillator are:

$$ \begin{align} \frac{\mathrm{d}x_1}{\mathrm{d}t} &= -x_2, \\ \frac{\mathrm{d}x_2}{\mathrm{d}t} &= -\mu \left(1 - x_1^2 \right)x_2 + x_1, \\ \end{align} $$

where $\mu$ is a scalar parameter that defines the strength of the damping.
These equations can be written in a discrete manner required for solution with the unscented Kalman filter as follows:

$$ \begin{align} x_1^n &= x_1^{n-1} - \Delta t \times x_2^{n-1}, \\ x_2^n &= x_2^{n-1} - \Delta t \times \mu \left(1 - (x_1^{n-1})^2 \right)x_2^{n-1} + x_1^{n-1}, \\ \end{align} $$

where $\mathbf{x}^n = \left( x_1^n, x_2^n \right)$ represents the position of the oscillator at time step $n$ and $\Delta t$ is the time step between consecutive measurements.

The Observation Model

We assume that we measure the position of the oscillator directly and hence the observation function is simply $$ y^n(\mathbf{x}^n) = \left( x_1^n, x_2^n \right) $$

Setting up the problem

First we'll import the required modules


In [1]:
using StateSpace
using Distributions
using Gadfly
using DataFrames
using Colors
Define Kalman Filter Parameters

We now need to define the process and observation model according to the model described above.


In [12]:
Δt = 0.1 # Set the time step

#Set the process function
function processFunction(x::Vector, Δt::Float64=0.1, μ::Float64=0.3)
    x1 = zeros(x)
    x1[1] = x[1] + Δt * -x[2]
    x1[2] = x[2] + Δt * (-μ * (1 - x[1]^2) * x[2] + x[1])
    x1
end

#Set the observation function.
observationFunction(x::Vector) = x

#Set process noise covariance matrix
processCovariance = 1e-1*[0.1 0.0; 0.0 1e-3]

#Set the observation noise covariance
observationCovariance = 3e-2*eye(2)

#Create additive noise UKF model
ukfStateModel = AdditiveNonLinUKFSSM(processFunction, processCovariance, observationFunction, observationCovariance)


Out[12]:
StateSpace.AdditiveNonLinUKFSSM{Float64}(processFunction,2x2 Array{Float64,2}:
 0.01  0.0   
 0.0   0.0001,observationFunction,2x2 Array{Float64,2}:
 0.03  0.0 
 0.0   0.03)
Generate noisy observations

Here we'll generate the noisy observations that will be passed to the Unscented Kalman Filter.

First we define the true initial position of the system


In [13]:
trueInitialState = [1.4, 0.0] #Set the true initial state of the oscillator


Out[13]:
2-element Array{Float64,1}:
 1.4
 0.0

Now we can define the number of observations and generate the true and noisy observations


In [14]:
#Set the number of observations and generate the true values and the noisy
#observations.
numObs = 200
trueState = zeros(2,numObs)
noisyObs = zeros(2,numObs)
trueState[:,1] = trueInitialState
noisyObs[:,1] = trueInitialState + sqrt(observationCovariance)*randn(2)
for i in 2:numObs
    trueState[:,i] = processFunction(trueState[:,i-1])
    noisyObs[:,i] = trueState[:,i] + sqrt(observationCovariance)*randn(2)
end

Guess of initial state

Now we define the guess of the initial state of the system


In [15]:
initial_guess = MvNormal([0.0,2.0], 1.0*eye(2))


Out[15]:
FullNormal(
dim: 2
μ: [0.0,2.0]
Σ: 2x2 Array{Float64,2}:
 1.0  0.0
 0.0  1.0
)
Perform Unscented Kalman Filter (additive noise) Algorithm

Now we have all of the parameters:

  1. noisy observations
  2. process (transition) and observation (emission) model paramaters
  3. initial guess of state

We can use the Unscented Kalman Filter to predict the true underlying state (position of the oscillator).


In [16]:
filtered_state = filter(ukfStateModel, noisyObs, initial_guess)


SmoothedState{Float64}
Out[16]:

201 estimates of 2-D process from 2-D observations
Log-likelihood: 701.8763985625116

NOTE: The log likelihood hasn't been implemented for the Unscented Kalman Filter and so 0.0 is just the default value set. It doesn't mean anything yet. We will sort this out soon.

Plot the results

Now we will plot the results of the $x_1$ position of the oscillator.

First we extract the mean $x_1$ positions and their variances from the filtered estimates.


In [17]:
x_data = 1:numObs
x1_array = Vector{Float64}(numObs)
x1Var_array = Vector{Float64}(numObs)
x1_Guess = initial_guess.μ[1]
x1Var_Guess = 2*sqrt(initial_guess.Σ.mat[1,1])
for i in x_data
    current_state = filtered_state.state[i]
    x1_array[i] = current_state.μ[1]
    x1Var_array[i] = 2*sqrt(current_state.Σ.mat[1,1])
end

Next we will create a dataframe. This is simply so the syntax is simple for plotting the ribbon digram which will represent the state along with the 95% confidence interval


In [18]:
df_fs = DataFrame(
    x = x_data*Δt,
    y = x1_array,
    ymin = x1_array - x1Var_array,
    ymax = x1_array + x1Var_array,
    f = "Filtered values"
    )


Out[18]:
xyyminymaxf
10.10.0-2.02.0Filtered values
20.21.40821832224295581.01256894309291831.8038677013929934Filtered values
30.300000000000000041.32703433784545680.99831304223563241.6557556334552812Filtered values
40.41.36690120293919181.0557909318773061.6780114740010776Filtered values
50.51.20775464711964390.90223281331181631.5132764809274715Filtered values
60.61.22175321037098780.91815601972478571.52535040101719Filtered values
70.70000000000000011.0316878563992310.72882531087746471.3345504019209975Filtered values
80.81.09478321489670580.79218595005327891.3973804797401328Filtered values
90.90.92582918812808650.62343889099485821.2282194852613149Filtered values
101.01.02204034401508870.71971556236092771.3243651256692497Filtered values
111.10.92386425267370510.62172978709668051.2259987182507297Filtered values
121.20000000000000020.81235844299130620.51028854114734091.1144283448352716Filtered values
131.30000000000000030.7314488254076980.42936765910732391.033529991708072Filtered values
141.40000000000000010.54854540626595430.246436417394164160.8506543951377443Filtered values
151.50000000000000020.18075414491182812-0.121519704645365770.483027994469022Filtered values
161.60.26437599860254213-0.038247564053154990.5669995612582392Filtered values
171.70000000000000020.14441207634414913-0.15825665271196130.4470808054002595Filtered values
181.8000000000000003-0.14837234530998245-0.45114101165144070.15439632103147582Filtered values
191.9000000000000001-0.19242752586392095-0.49540611934766910.11055106761982716Filtered values
202.0-0.24205808341285984-0.54512329522119640.06100712839547673Filtered values
212.1-0.3822325989378521-0.6853454681064522-0.07911972976925197Filtered values
222.2-0.5881898289882574-0.8913700270104367-0.28500963096607823Filtered values
232.3000000000000003-0.5970455348178784-0.900302233730638-0.29378883590511873Filtered values
242.4000000000000004-0.7197779164279073-1.023052185760254-0.4165036470955608Filtered values
252.5000000000000004-0.7468184649139971-1.0501156387546082-0.4435212910733858Filtered values
262.6-0.8809464568843236-1.1842437827708043-0.5776491309978429Filtered values
272.7-0.9373369430642993-1.2406418851680265-0.6340320009605719Filtered values
282.8000000000000003-1.0442395949223962-1.34753730399854-0.7409418858462522Filtered values
292.9000000000000004-1.178699589832252-1.481985554421409-0.8754136252430953Filtered values
303.0000000000000004-1.1774740704604292-1.4807458036131589-0.8742023373076994Filtered values
&vellip&vellip&vellip&vellip&vellip&vellip

Next we create 3 distinguishable colors for the plot


In [19]:
n = 3
getColors = distinguishable_colors(n, Color[LCHab(70, 60, 240)],
                                   transform=c -> deuteranopic(c, 0.5),
                                   lchoices=Float64[65, 70, 75, 80],
                                   cchoices=Float64[0, 50, 60, 70],
                                   hchoices=linspace(0, 330, 24))


Out[19]:

Finally we display the plot


In [25]:
oscillatorx1_state_plot = plot(
    layer(x=x_data*Δt, y=noisyObs[1,:], Geom.point, Theme(default_color=getColors[2])),
    layer(x=x_data*Δt, y=trueState[1,:], Geom.line, Theme(default_color=getColors[3])),
    layer(df_fs, x=:x, y=:y, ymin=:ymin, ymax=:ymax, Geom.line, Geom.ribbon),
    Guide.xlabel("Time (seconds)"), Guide.ylabel("x1"),
    Guide.manual_color_key("Colour Key",["Filtered Estimate", "Measurements","True Value "],[getColors[1],getColors[2],getColors[3]]),
    Guide.title("Unscented Kalman Filter (Additive) Example")
    )
display(oscillatorx1_state_plot)


Time (seconds) -30 -25 -20 -15 -10 -5 0 5 10 15 20 25 30 35 40 45 50 55 -25 -24 -23 -22 -21 -20 -19 -18 -17 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 -25 0 25 50 -26 -24 -22 -20 -18 -16 -14 -12 -10 -8 -6 -4 -2 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 Filtered Estimate Measurements True Value Colour Key -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 -6.0 -5.8 -5.6 -5.4 -5.2 -5.0 -4.8 -4.6 -4.4 -4.2 -4.0 -3.8 -3.6 -3.4 -3.2 -3.0 -2.8 -2.6 -2.4 -2.2 -2.0 -1.8 -1.6 -1.4 -1.2 -1.0 -0.8 -0.6 -0.4 -0.2 0.0 0.2 0.4 0.6 0.8 1.0 1.2 1.4 1.6 1.8 2.0 2.2 2.4 2.6 2.8 3.0 3.2 3.4 3.6 3.8 4.0 4.2 4.4 4.6 4.8 5.0 5.2 5.4 5.6 5.8 6.0 -6 -3 0 3 6 -6.0 -5.5 -5.0 -4.5 -4.0 -3.5 -3.0 -2.5 -2.0 -1.5 -1.0 -0.5 0.0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5 6.0 x1 Unscented Kalman Filter (Additive) Example

Unscented Rauch-Tung-Striebel Smoother

The Unscented RTS smoother has been implemented to perform smoothing on the filtered estimates to get a better estimate of the true underlying state (as they say, hindsight is 20/20). This can be performed in an identical manner to the Linear case like so:


In [21]:
smoothed_state = smooth(ukfStateModel, filtered_state)


SmoothedState{Float64}
Out[21]:

200 estimates of 2-D process from 2-D observations
Log-likelihood: 0.0

Now we can the results in the same way as above to visualize the smoothed result.


In [23]:
x_data = 1:numObs
x1_array = Vector{Float64}(numObs)
x1Var_array = Vector{Float64}(numObs)
x1_Guess = initial_guess.μ[1]
x1Var_Guess = 2*sqrt(initial_guess.Σ.mat[1,1])
for i in x_data
    current_state = smoothed_state.state[i]
    x1_array[i] = current_state.μ[1]
    x1Var_array[i] = 2*sqrt(current_state.Σ.mat[1,1])
end
df_fs = DataFrame(
    x = x_data*Δt,
    y = x1_array,
    ymin = x1_array - x1Var_array,
    ymax = x1_array + x1Var_array,
    f = "Smoothed values"
    )

n = 3
getColors = distinguishable_colors(n, Color[LCHab(70, 60, 240)],
                                   transform=c -> deuteranopic(c, 0.5),
                                   lchoices=Float64[65, 70, 75, 80],
                                   cchoices=Float64[0, 50, 60, 70],
                                   hchoices=linspace(0, 330, 24))
oscillatorx1_state_plot_smooted = plot(
    layer(x=x_data*Δt, y=noisyObs[1,:], Geom.point, Theme(default_color=getColors[2])),
    layer(x=x_data*Δt, y=trueState[1,:], Geom.line, Theme(default_color=getColors[3])),
    layer(df_fs, x=:x, y=:y, ymin=:ymin, ymax=:ymax, Geom.line, Geom.ribbon),
    Guide.xlabel("Time (seconds)"), Guide.ylabel("x1"),
    Guide.manual_color_key("Colour Key",["Filtered Estimate", "Measurements","True Value "],[getColors[1],getColors[2],getColors[3]]),
    Guide.title("Unscented Kalman Smoother (Additive) Example")
    )
display(oscillatorx1_state_plot_smooted)


Time (seconds) -30 -25 -20 -15 -10 -5 0 5 10 15 20 25 30 35 40 45 50 55 -25 -24 -23 -22 -21 -20 -19 -18 -17 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 -25 0 25 50 -26 -24 -22 -20 -18 -16 -14 -12 -10 -8 -6 -4 -2 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 Filtered Estimate Measurements True Value Colour Key -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 -6.0 -5.8 -5.6 -5.4 -5.2 -5.0 -4.8 -4.6 -4.4 -4.2 -4.0 -3.8 -3.6 -3.4 -3.2 -3.0 -2.8 -2.6 -2.4 -2.2 -2.0 -1.8 -1.6 -1.4 -1.2 -1.0 -0.8 -0.6 -0.4 -0.2 0.0 0.2 0.4 0.6 0.8 1.0 1.2 1.4 1.6 1.8 2.0 2.2 2.4 2.6 2.8 3.0 3.2 3.4 3.6 3.8 4.0 4.2 4.4 4.6 4.8 5.0 5.2 5.4 5.6 5.8 6.0 -6 -3 0 3 6 -6.0 -5.5 -5.0 -4.5 -4.0 -3.5 -3.0 -2.5 -2.0 -1.5 -1.0 -0.5 0.0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5 6.0 x1 Unscented Kalman Smoother (Additive) Example

For those that just want the code, without the explanation, you have come to the right place. Here it is:


In [26]:
using StateSpace
using Distributions
using Gadfly
using DataFrames
using Colors

Δt = 0.1 # Set the time step

#Set the process function
function processFunction(x::Vector, Δt::Float64=0.1, μ::Float64=0.3)
    x1 = zeros(x)
    x1[1] = x[1] + Δt * -x[2]
    x1[2] = x[2] + Δt * (-μ * (1 - x[1]^2) * x[2] + x[1])
    x1
end

#Set the observation function.
observationFunction(x::Vector) = x

#Set process noise covariance matrix
processCovariance = 1e-1*[0.1 0.0; 0.0 1e-3]

#Set the observation noise covariance
observationCovariance = 3e-2*eye(2)

#Create additive noise UKF model
ukfStateModel = AdditiveNonLinUKFSSM(processFunction, processCovariance, observationFunction, observationCovariance)

trueInitialState = [1.4, 0.0] #Set the true initial state of the oscillator

#Set the number of observations and generate the true values and the noisy
#observations.
numObs = 200
trueState = zeros(2,numObs)
noisyObs = zeros(2,numObs)
trueState[:,1] = trueInitialState
noisyObs[:,1] = trueInitialState + sqrt(observationCovariance)*randn(2)
for i in 2:numObs
    trueState[:,i] = processFunction(trueState[:,i-1])
    noisyObs[:,i] = trueState[:,i] + sqrt(observationCovariance)*randn(2)
end

initial_guess = MvNormal([0.0,2.0], 1.0*eye(2))

filtered_state = filter(ukfStateModel, noisyObs, initial_guess)

x_data = 1:numObs
x1_array = Vector{Float64}(numObs)
x1Var_array = Vector{Float64}(numObs)
x1_Guess = initial_guess.μ[1]
x1Var_Guess = 2*sqrt(initial_guess.Σ.mat[1,1])
for i in x_data
    current_state = filtered_state.state[i]
    x1_array[i] = current_state.μ[1]
    x1Var_array[i] = 2*sqrt(current_state.Σ.mat[1,1])
end

df_fs = DataFrame(
    x = x_data*Δt,
    y = x1_array,
    ymin = x1_array - x1Var_array,
    ymax = x1_array + x1Var_array,
    f = "Filtered values"
    )

n = 3
getColors = distinguishable_colors(n, Color[LCHab(70, 60, 240)],
                                   transform=c -> deuteranopic(c, 0.5),
                                   lchoices=Float64[65, 70, 75, 80],
                                   cchoices=Float64[0, 50, 60, 70],
                                   hchoices=linspace(0, 330, 24))

oscillatorx1_state_plot = plot(
    layer(x=x_data*Δt, y=noisyObs[1,:], Geom.point, Theme(default_color=getColors[2])),
    layer(x=x_data*Δt, y=trueState[1,:], Geom.line, Theme(default_color=getColors[3])),
    layer(df_fs, x=:x, y=:y, ymin=:ymin, ymax=:ymax, Geom.line, Geom.ribbon),
    Guide.xlabel("Time (seconds)"), Guide.ylabel("x1"),
    Guide.manual_color_key("Colour Key",["Filtered Estimate", "Measurements","True Value "],[getColors[1],getColors[2],getColors[3]]),
    Guide.title("Unscented Kalman Filter (Additive) Example")
    )
display(oscillatorx1_state_plot)

#Perform Smoothing of the filtered estimates
smoothed_state = smooth(ukfStateModel, filtered_state)

#Plot the results
x_data = 1:numObs
x1_array = Vector{Float64}(numObs)
x1Var_array = Vector{Float64}(numObs)
x1_Guess = initial_guess.μ[1]
x1Var_Guess = 2*sqrt(initial_guess.Σ.mat[1,1])
for i in x_data
    current_state = smoothed_state.state[i]
    x1_array[i] = current_state.μ[1]
    x1Var_array[i] = 2*sqrt(current_state.Σ.mat[1,1])
end
df_fs = DataFrame(
    x = x_data*Δt,
    y = x1_array,
    ymin = x1_array - x1Var_array,
    ymax = x1_array + x1Var_array,
    f = "Smoothed values"
    )

n = 3
getColors = distinguishable_colors(n, Color[LCHab(70, 60, 240)],
                                   transform=c -> deuteranopic(c, 0.5),
                                   lchoices=Float64[65, 70, 75, 80],
                                   cchoices=Float64[0, 50, 60, 70],
                                   hchoices=linspace(0, 330, 24))
oscillatorx1_state_plot_smooted = plot(
    layer(x=x_data*Δt, y=noisyObs[1,:], Geom.point, Theme(default_color=getColors[2])),
    layer(x=x_data*Δt, y=trueState[1,:], Geom.line, Theme(default_color=getColors[3])),
    layer(df_fs, x=:x, y=:y, ymin=:ymin, ymax=:ymax, Geom.line, Geom.ribbon),
    Guide.xlabel("Time (seconds)"), Guide.ylabel("x1"),
    Guide.manual_color_key("Colour Key",["Filtered Estimate", "Measurements","True Value "],[getColors[1],getColors[2],getColors[3]]),
    Guide.title("Unscented Kalman Smoother (Additive) Example")
    )
display(oscillatorx1_state_plot_smooted)


Time (seconds) -30 -25 -20 -15 -10 -5 0 5 10 15 20 25 30 35 40 45 50 55 -25 -24 -23 -22 -21 -20 -19 -18 -17 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 -25 0 25 50 -26 -24 -22 -20 -18 -16 -14 -12 -10 -8 -6 -4 -2 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 Filtered Estimate Measurements True Value Colour Key -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 -6.0 -5.8 -5.6 -5.4 -5.2 -5.0 -4.8 -4.6 -4.4 -4.2 -4.0 -3.8 -3.6 -3.4 -3.2 -3.0 -2.8 -2.6 -2.4 -2.2 -2.0 -1.8 -1.6 -1.4 -1.2 -1.0 -0.8 -0.6 -0.4 -0.2 0.0 0.2 0.4 0.6 0.8 1.0 1.2 1.4 1.6 1.8 2.0 2.2 2.4 2.6 2.8 3.0 3.2 3.4 3.6 3.8 4.0 4.2 4.4 4.6 4.8 5.0 5.2 5.4 5.6 5.8 6.0 -6 -3 0 3 6 -6.0 -5.5 -5.0 -4.5 -4.0 -3.5 -3.0 -2.5 -2.0 -1.5 -1.0 -0.5 0.0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5 6.0 x1 Unscented Kalman Filter (Additive) Example
Time (seconds) -30 -25 -20 -15 -10 -5 0 5 10 15 20 25 30 35 40 45 50 55 -25 -24 -23 -22 -21 -20 -19 -18 -17 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 -25 0 25 50 -26 -24 -22 -20 -18 -16 -14 -12 -10 -8 -6 -4 -2 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 Filtered Estimate Measurements True Value Colour Key -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 -6.0 -5.8 -5.6 -5.4 -5.2 -5.0 -4.8 -4.6 -4.4 -4.2 -4.0 -3.8 -3.6 -3.4 -3.2 -3.0 -2.8 -2.6 -2.4 -2.2 -2.0 -1.8 -1.6 -1.4 -1.2 -1.0 -0.8 -0.6 -0.4 -0.2 0.0 0.2 0.4 0.6 0.8 1.0 1.2 1.4 1.6 1.8 2.0 2.2 2.4 2.6 2.8 3.0 3.2 3.4 3.6 3.8 4.0 4.2 4.4 4.6 4.8 5.0 5.2 5.4 5.6 5.8 6.0 -6 -3 0 3 6 -6.0 -5.5 -5.0 -4.5 -4.0 -3.5 -3.0 -2.5 -2.0 -1.5 -1.0 -0.5 0.0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5 6.0 x1 Unscented Kalman Smoother (Additive) Example