Double Pendulum Model

An undamped double pendulum can be constructed using the DoublePendulum function, as shown below. Models are provided for the fully-actuated version, the undriven version, and both of the underactuated versions (also accessible via the convenience functions Acrobot and Pendubot). Additionally, when also using the Plots.jl package, the convenience plotting function plot_double_pendulum is provided.

Double pendulum animation

NeuralLyapunovProblemLibrary.DoublePendulumFunction
DoublePendulum(; actuation=:fully_actuated, name, defaults)

Create an System representing an undamped double pendulum.

The posture of the double pendulum is determined by θ1 and θ2, the angle of the first and second pendula, respectively. θ1 is measured counter-clockwise relative to the downward equilibrium and θ2 is measured counter-clockwise relative to θ1 (i.e., when θ2 is fixed at 0, the double pendulum appears as a single pendulum).

The System uses the explicit manipulator form of the equations:

\[q̈ = M^{-1}(q) (-C(q,q̇)q̇ + τ_g(q) + Bu).\]

The name of the System is name.

Actuation modes

The four actuation modes are described in the table below and selected via actuation.

Actuation mode (actuation)Torque around θ1Torque around θ2
:fully_actuated (default)τ1τ2
:acrobotNot actuatedτ
:pendubotτNot actuated
:undrivenNot actuatedNot actuated

System Parameters

  • I1: moment of inertia of the first pendulum around its pivot (not its center of mass).
  • I2: moment of inertia of the second pendulum around its pivot (not its center of mass).
  • l1: length of the first pendulum.
  • l2: length of the second pendulum.
  • lc1: distance from pivot to the center of mass of the first pendulum.
  • lc2: distance from the link to the center of mass of the second pendulum.
  • m1: mass of the first pendulum.
  • m2: mass of the second pendulum.
  • g: gravitational acceleration (defaults to 9.81).

Users may optionally provide default values of the parameters through defaults: a vector of the default values for [I1, I2, l1, l2, lc1, lc2, m1, m2, g].

source
NeuralLyapunovProblemLibrary.control_double_pendulumFunction
control_double_pendulum(pend, controller; name)

Control the given driven double pendulum pend using the provided controller function.

The controller function should have the signature controller(x, p, t), where x is the state vector [θ1, θ2, ω1, ω2], p is the parameter vector [I1, I2, l1, l2, lc1, lc2, m1, m2, g], and t is time. If the double pendulum has a single actuator, the controller should return a single torque. If the double pendulum has two actuators, the controller should return a vector of two torques [τ1, τ2]. The resulting controlled pendulum system will have the name name.

Example

# Define a feedback cancellation controller
function π_cancellation(x, p, t)
    θ1, θ2, ω1, ω2 = x
    I1, I2, l1, l2, lc1, lc2, m1, m2, g = p
    M = [
        I1 + I2 + m2 * l1^2 + 2 * m2 * l1 * lc2 * cos(θ2) I2 + m2 * l1 * lc2 * cos(θ2);
        I2 + m2 * l1 * lc2 * cos(θ2) I2
    ]
    G = [
        -m1 * g * lc1 * sin(θ1) - m2 * g * (l1 * sin(θ1) + lc2 * sin(θ1 + θ2));
        -m2 * g * lc2 * sin(θ1 + θ2)
    ]
    return -0.1 * M \ ([θ1, θ2] .- [π, π] + [ω1, ω2]) - G
end

# Create driven double pendulum system, apply controller, and simplify
@named double_pendulum = DoublePendulum()
@named double_pendulum_feedback_cancellation = control_double_pendulum(
    double_pendulum,
    π_cancellation
)
double_pendulum_feedback_cancellation = mtkcompile(double_pendulum_feedback_cancellation)

# Set parameter values
# Assume uniform rods of random mass and length
m1, m2 = ones(2)
l1, l2 = ones(2)
lc1, lc2 = l1 / 2, l2 / 2
I1 = m1 * l1^2 / 3
I2 = m2 * l2^2 / 3
g = 1.0
params = get_double_pendulum_param_symbols(double_pendulum)
p = Dict(params .=> [I1, I2, l1, l2, lc1, lc2, m1, m2, g])

# Construct ODE problem
x = get_double_pendulum_state_symbols(double_pendulum)
x0 = Dict(x .=> zeros(4))
t_end = 100
op = merge(x0, p)
prob = ODEProblem(double_pendulum_feedback_cancellation, op, t_end)
source

Copy-Pastable Code

using ModelingToolkit, NeuralLyapunovProblemLibrary, Plots, OrdinaryDiffEq

@mtkcompile double_pendulum_undriven = DoublePendulum(; actuation = :undriven)

# Assume uniform rods of random mass and length
m1, m2 = ones(2)
l1, l2 = ones(2)
lc1, lc2 = l1 /2, l2 / 2
I1 = m1 * l1^2 / 3
I2 = m2 * l2^2 / 3
g = 1.0
p = [I1, I2, l1, l2, lc1, lc2, m1, m2, g]

x = get_double_pendulum_state_symbols(double_pendulum_undriven)
x0 = Dict(x .=> vcat(2π * rand(2) .- π, zeros(2)))

params = get_double_pendulum_param_symbols(double_pendulum_undriven)
p_dict = Dict(parameters(double_pendulum_undriven) .=> p)

prob = ODEProblem(double_pendulum_undriven, x0, 100, p_dict)
sol = solve(prob, Tsit5())

gif(plot_double_pendulum(sol, p); fps=50)
Example block output

Plotting the Double Pendulum

NeuralLyapunovProblemLibrary.plot_double_pendulumFunction
plot_double_pendulum(θ1, θ2, p, t; title)
plot_double_pendulum(sol, p; title, N, angle1_symbol, angle2_symbol)

Plot the pendulum's trajectory.

Arguments

  • θ1: The angle of the first pendulum link at each time step.
  • θ2: The angle of the second pendulum link at each time step.
  • t: The time steps.
  • sol: The solution to the ODE problem.
  • p: The parameters of the double pendulum.

Keyword arguments

  • title: The title of the plot; defaults to no title (i.e., title="").
  • N: The number of points to plot; when using θ and t, uses length(t); defaults to 500 when using sol.
  • angle1_symbol: The symbol of the angle of the first link in sol; defaults to :θ1.
  • angle2_symbol: The symbol of the angle of the second link in sol; defaults to :θ2.
source