Contents

Double Pendulum Simulation

Author: Tyler Jones Contact: tjjones6@wisc.edu Institution: University of Wisconsin-Madison Date: 05.27.2024

Description: This code simulates a double pendulum using RK45 (ode45) as the chosen numerical scheme. The pendulum's trajectory is then mapped onto an animation figure. Finally, plots of angular evolution, angular velocity evolution, and respective phase spaces are generated.

clear all; close all; clc;

User Input

Define Parameters

g = 9.81;  % Acceleration due to gravity (m/s^2)
L1 = 1.0;  % Length of the first pendulum arm (m)
L2 = 1.0;  % Length of the second pendulum arm (m)
m1 = 1.0;  % Mass of the first pendulum bob (kg)
m2 = 1.0;  % Mass of the second pendulum bob (kg)

% Initial Conditions
theta1_0 = pi/2;  % Initial angle of the first pendulum (radians)
theta2_0 = pi/2;  % Initial angle of the second pendulum (radians)
omega1_0 = 0;      % Initial angular velocity of the first pendulum (rad/s)
omega2_0 = 0;      % Initial angular velocity of the second pendulum (rad/s)

% Time Settings
tspan = [0 25];  % Extend the time span for simulation (seconds)

Numerical Integration using ODE45

options = odeset('RelTol', 1e-6, 'AbsTol', 1e-6);
[t, y] = ode45(@(t, y) doublePendulumODE(t, y, g, L1, L2, m1, m2), tspan, [theta1_0, omega1_0, theta2_0, omega2_0], options);

% Extracting and Converting Data
theta1 = y(:, 1);
theta2 = y(:, 3);

x1 = L1*sin(theta1);
y1 = -L1*cos(theta1);
x2 = x1 + L2*sin(theta2);
y2 = y1 - L2*cos(theta2);

Animation

Initialize an array to store frames for the animation

frames = [];

% Create a figure for the animation (The figure will initially display as empty)
animationFig = figure('units','normalized','Position',[0.1 0.1 .8 .8]);
myfigpref

% Storing animation data
% myWriter = VideoWriter('DoublePendulumAnimation.mp4', 'MPEG-4');
% myWriter.FrameRate = 30;
% open(myWriter);

% Set manual axes limits for animation
animationLimits = [-2.5 2.5 -2.5 1];  % Adjust as needed

% Set initial axes limits
axis(animationLimits);

for i = 1:length(t)
    % Plot the trajectory
    plot(x2(1:i), y2(1:i), 'Color',[0.1 0.1 0.1 0.2])
    if i > 20
        plot(x2(i-20:i), y2(i-20:i), '-r')
    end

    % Update the position of the pendulum bobs and rods in the animation plot
    plot([0, x1(i)], [0, y1(i)], 'k', 'LineWidth', 2);  % Rod for Pendulum 1
    hold on;
    plot([x1(i), x2(i)], [y1(i), y2(i)], 'k', 'LineWidth', 2);  % Rod for Pendulum 2
    plot(x1(i), y1(i), 'bo', 'MarkerSize', 10, 'MarkerFaceColor', 'b');  % Mass for Pendulum 1
    plot(x2(i), y2(i), 'ro', 'MarkerSize', 10, 'MarkerFaceColor', 'r');  % Mass for Pendulum 2
    fig_xytit('x-axis','y-axis',['Double Pendulum Animation - Time: ', num2str(t(i), '%.2f'), 's'])

    % Set initial y-axis limits
    ylim(animationLimits(3:4));
    xlim(animationLimits(1:2));

    % Capture the current frame for the animation
    frame = getframe(animationFig);
    frames = [frames, frame];
%     writeVideo(myWriter,frame);

    % Clear the previous frame in the animation plot
    if i < length(t)
        cla(animationFig);
    end
end

% Close the animation figure
% close(animationFig);
% close(myWriter)

% Display the animation
% figure('units','normalized','Position',[0.1 0.1 .8 .8])
% movie(frames, 1, 30);  % Play the animation at 30 frames per second

Plotting Results

Plot angle evolution

figure('units','normalized','Position',[0.1 0.1 .8 .8])
hold on; grid on;
myfigpref
plot(t, y(:, 1), t, y(:, 3))
fig_xytit('Time (s)', 'Angle (rad)', 'Double Pendulum Motion');
legend('\phi_1', '\phi_2');
hold off;

% Plot angular velocity evolution
figure('units','normalized','Position',[0.1 0.1 .8 .8])
hold on; grid on;
myfigpref
plot(t, y(:, 2), t, y(:, 4))
fig_xytit('Time (s)', '$\dot{\phi}$ (rad/s)', 'Angular Velocity Evolution')
legend('\phi_1\_dot', '\phi_2\_dot');
hold off;

% Phase Space of Pendulum 1
figure('units','normalized','Position',[0.1 0.1 .8 .8])
hold on; grid on; axis padded;
myfigpref
plot(y(:,1), y(:,3))
fig_xytit('$\phi_1$ (rad)', '$\dot{\phi_1}$ (rad/s)', '$\phi_1$ Phase Space')
hold off;

% Phase Space of Pendulum 2
figure('units','normalized','Position',[0.1 0.1 .8 .8])
hold on; grid on; axis padded;
myfigpref
plot(y(:,2), y(:,4))
fig_xytit('$\phi_2$ (rad)', '$\dot{\phi_2}$ (rad/s)', '$\phi_2$ Phase Space')
hold off;

Define the function for the double pendulum ODEs

function dydt = doublePendulumODE(~, y, g, L1, L2, m1, m2)
    % Unpack the state variables
    theta1 = y(1);
    omega1 = y(2);
    theta2 = y(3);
    omega2 = y(4);

    % Equations of motion for the double pendulum
    dydt = zeros(4, 1); % empty row vector
    dydt(1) = omega1;
    dydt(2) = (-g * (2 * m1 + m2) * sin(theta1) - m2 * g * sin(theta1 - 2 * theta2) - 2 * sin(theta1 - theta2) * m2 * (omega2^2 * L2 + omega1^2 * L1 * cos(theta1 - theta2))) / (L1 * (2 * m1 + m2 - m2 * cos(2 * theta1 - 2 * theta2)));
    dydt(3) = omega2;
    dydt(4) = (2 * sin(theta1 - theta2) * (omega1^2 * L1 * (m1 + m2) + g * (m1 + m2) * cos(theta1) + omega2^2 * L2 * m2 * cos(theta1 - theta2))) / (L2 * (2 * m1 + m2 - m2 * cos(2 * theta1 - 2 * theta2)));
end

Reference Functions for Plotting

function myfigpref
    set(0, 'DefaultAxesFontSize', 20);
    set(0, 'DefaultAxesLineWidth', 2);
    set(0, 'DefaultLineLineWidth', 2);
    set(0, 'DefaultPatchLineWidth', .7);
    set(0, 'DefaultLineMarkerSize', 6);
    grid on;
    box on;
    h = gca;
    h.TickLabelInterpreter='latex';
    h.MinorGridAlpha=0.05;
    h.GridAlpha=0.05;
    h.FontSize=25;
    h.LineWidth=2;
    h = gcf;
    h.Color = [1,1,1];
end

function fig_xytit(xlab, ylab, tit)
    if nargin < 3
        tit = '';
    end
    xlabel(xlab, 'interpreter', 'latex');
    ylabel(ylab, 'interpreter', 'latex');
    title(tit, 'interpreter', 'latex');
end