[~,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];
EKF_output = zeros(length(time), length(x)); % preallocate memory
EKF_output(1,:) = x; % fill in the initial condition
% specify initial state covariance
% specify process covariance
Q = [(0.01 * deltaT)^2 0;
% give friendly names to each element in the state vector
% Calculate the jacobian for the prediction step
% This Jacobian was found using the symbolic differentiation above, and
% reformatted into separate lines for readability.
F = [1 - (deltaT^2*g*cos(theta))/(2*L), ...
-(deltaT*g*(cos(theta) - cos(theta + deltaT*theta_dot - (deltaT^2*g*sin(theta))/(2*L))*((deltaT^2*g*cos(theta))/(2*L) - 1)))/(2*L), ...
1 - (deltaT^2*g*cos(theta + deltaT*theta_dot - (deltaT^2*g*sin(theta))/(2*L)))/(2*L)];
% calculate the Jacobian for the measurement step
x_pred = [theta + deltaT*theta_dot - (deltaT^2*g*sin(theta))/(2*L);
theta_dot - (deltaT*g*(sin(theta + deltaT*theta_dot - (deltaT^2*g*sin(theta))/(2*L)) + sin(theta)))/(2*L)];
% Make sure to use the predicted value of the state when calculating
theta_dot_pred = x_pred(2);
z_pred = [(1/2)*m*(L*theta_dot_pred)^2 % kinetic energy
m*g*(L-L*cos(theta_pred))]; % potential energy
y = measurements(i,:)' - z_pred;
% measurement residual covariance:
% plot(time, measurements(:,1), '.');
plot(time, EKF_output(:,1));
ylabel('\theta (radians)');
xlabel('Time (seconds)');
title('Pendulum position');
plot(time, exact_theta_0);
% plot(time, measurements(:,2), '.');
plot(time, EKF_output(:,2));
ylabel('d\theta/dt (rad/s)');
xlabel('Time (seconds)');
title('Pendulum angular velocity');
linkaxes([ax1 ax2], 'x');