[~,exact] = ode45(@(t,x)[x(2); -g/L * sin(x(1))], time, [theta_0; theta_dot_0]);
exact_theta = exact(:,1);
exact_theta_0 = exact(:,2);
ylabel('\theta (radians)');
xlabel('Time (seconds)');
title('Pendulum position');
plot(time, exact_theta_0);
ylabel('d\theta/dt (rad/s)');
xlabel('Time (seconds)');
title('Pendulum angular velocity');
linkaxes([ax1 ax2], 'x');
sgtitle('Accurate solution');
% calculate the exact kinetic and potential energy
exact_energy = [(1/2)*m*(L*exact(:,2)).^2, m*g*(L-L*cos(exact(:,1)))];
% generate random numbers with given standard deviations
R = [sigma_KE^2 rho*sigma_KE*sigma_PE
rho*sigma_KE*sigma_PE sigma_PE^2];
measurements = [mvnrnd(exact_energy, R)];
plot(time, exact_energy(:,1));
plot(time, measurements(:,1), '.');
ylabel('Kinetic energy (J)');
xlabel('Time (seconds)');
plot(time, exact_energy(:,2));
plot(time, measurements(:,2), '.');
ylabel('Potential energy (J)');
xlabel('Time (seconds)');
title('Potential energy');
linkaxes([ax1 ax2], 'x');
sgtitle('Noisy sensors');
% specify the initial condition for each state variable
x = [theta_0; theta_dot_0];
UKF_output = zeros(length(time), length(x)); % preallocate memory
UKF_output(1,:) = x; % fill in the initial condition
% Specify initial state covariance.
% This cannot be 0 for the UKF.
% specify process covariance
Q = [(0.001 * deltaT)^2 0;
% Unscented transform parameters
lambda = alpha^2 * n - n;
% Unscented transform weights
w_m = [lambda/(n+lambda) ones(1,2*n)*(1/(2*(n+lambda)))];
w_c(1) = lambda/(n+lambda)+1-alpha^2+beta;
A = sqrt(n+lambda)*chol(P);
sigma_points = [x (x+A') (x-A')];
% each column is one of the sigma points
% propagate sigma points through the prediction function
% process each column by pulling out the state variables
theta = sigma_points(1,i);
theta_dot = sigma_points(2,i);
F(1,i) = theta + theta_dot*deltaT - g/(2*L)*sin(theta)*deltaT^2; % prediction for theta
F(2,i) = theta_dot - g/(2*L) * (sin(theta) + sin(F(1,i))) * deltaT; % prediction for theta_dot
% ^^ this could in fact be any arbitrary computer code
% compute output statistics
P_pred = w_c .* (F - x_pred) * (F - x_pred)' + Q; % add process covariance
% Propagate the predction through the measurement function
% Calculate new sigma points
A = sqrt(n+lambda)*chol(P_pred);
sigma_points = [x_pred (x_pred+A') (x_pred-A')];
% Pass each column through the measurement function
% process each column by pulling out the state variables
theta = sigma_points(1,i);
theta_dot = sigma_points(2,i);
H(1,i) = (1/2)*m*(L*theta_dot).^2; % kinetic energy
H(2,i) = m*g*(L-L*cos(theta)); % potential energy
z_pred = sum(w_m .* H, 2);
% Predicted measurement covariance:
S = w_c .* (H - z_pred) * (H - z_pred)' + R; % add measurement covariance
y = measurements(k,:)' - z_pred;
T = w_c .* (F - x_pred) * (H - z_pred)';
% plot(time, measurements(:,1), '.');
plot(time, UKF_output(:,1));
ylabel('\theta (radians)');
xlabel('Time (seconds)');
title('Pendulum position');
plot(time, exact_theta_0);
% plot(time, measurements(:,2), '.');
plot(time, UKF_output(:,2));
ylabel('d\theta/dt (rad/s)');
xlabel('Time (seconds)');
title('Pendulum angular velocity');
linkaxes([ax1 ax2], 'x');