Engee documentation
Notebook

Animation of the movement of a pendulum using Julia

This example shows the implementation of simulating the movement of a physical pendulum using differential equations and creating animations in the Julia programming language.

Introduction

A pendulum is the simplest physical system widely used to study periodic motion and oscillations. In this example, we consider the problem of modeling the motion of a simple pendulum using a system of second-order ordinary differential equations, which is then solved in the framework of a boundary value problem using the collocation method (MIRK). After finding a solution, it is visualized in GIF animation format using the Luxor.jl library based on Cairo.

The main goal is to demonstrate the capabilities of the Julia language in numerical solution of physical problems and generation of graphical visualization. Animation allows you to understand the dynamics of a pendulum's movement — how it moves through time, starting from a given position and ending at the opposite point after a certain time interval.

In [ ]:
# Установка требуемых пакетов
import Pkg; Pkg.add("Luxor")
import Pkg; Pkg.add("Colors")
import Pkg; Pkg.add("BoundaryValueDiffEq")

Connecting libraries

In [ ]:
using Luxor        # для рисования анимации
using Colors       # для управления цветами элементов анимации
using BoundaryValueDiffEq  # для решения краевых задач обыкновенных дифференциальных уравнений

Defining constants and parameters for the model

Constants of the physical model and animation

In [ ]:
const g = 9.81;     # ускорение свободного падения (м/с²)
const L = 1.0;      # длина нити маятника (метры)
const bobd = 0.10;  # диаметр груза (bob) маятника (в метрах)

Animation Parameters

In [ ]:
const framerate = 50.0;                # частота кадров анимации (кадр/сек)
const t_0 = 0.0;                        # начальное время моделирования (сек)
const tf = 2.3;                        # конечное время моделирования (сек)
const dtframe = 1.0/framerate;         # длительность 1 кадра в секундах
const tspan = LinRange(t_0, tf, Int(floor(tf*framerate)));  # массив моментов времени для анимации

Colors and file names

In [ ]:
const bgcolor = "black";               # цвет фона анимации
const leaderhue = (0.80, 0.70, 0.20);  # светло-золотой цвет для рычага
const hslcolors = [HSL(col) for col in (distinguishable_colors(
                   Int(floor(tf*framerate)+3),[RGB(1,1,1)])[2:end])];  # набор цветов для элементов
const giffilename = "pendulum.gif";    # имя выходного файла GIF

Formulation of the problem and solution of the differential equation

Definition of a function for the equations of motion of a pendulum

In [ ]:
function simplependulum(du, u, p, t)
    θ = u[1]   # угол отклонения
     = u[2]  # скорость изменения угла (угловая скорость)
    
    du[1] =                      # производная угла = угловая скорость
    du[2] = -(g/L) * sin(θ)        # уравнение движения: θ'' = -(g/L) * sin(θ)
end
Out[0]:
simplependulum (generic function with 1 method)

Setting boundary conditions

In [ ]:
function bc2(residual, u, p, t)
    residual[1] = u[end÷2][1] + pi/2     # значение угла в середине интервала равно π/2 (положение слева)
    residual[2] = u[end][1] - pi/2       # значение угла в конце равно -π/2 (положение справа)
end
Out[0]:
bc2 (generic function with 1 method)

Formation and solution of the boundary value problem

In [ ]:
bvp2 = BVProblem(simplependulum, bc2, [pi/2, pi/2], (tspan[1], tspan[end]));
sol2 = solve(bvp2, MIRK4(), dt=dtframe);   # метод Рунге–Кутты 4-го порядка с коррекцией

Animation rendering functions

Background function for rendering a frame

In [ ]:
function backdrop(scene, framenumber)
    background(bgcolor)   # установка черного фона
end
Out[0]:
backdrop (generic function with 1 method)

The main function of drawing each frame is

In [ ]:
function anim_frame(scene, framenumber)
    # Получаем текущие значения угла и угловой скорости
    u1, u2 = sol2.u[framenumber]
    # Вычисляем координаты центра боба по формулам тригонометрии
    y = L * cos(u1)
    x = L * sin(u1)
    
    # Нарисовать рычаг (от точки подвеса до груза)
    sethue(leaderhue)
    poly([Point(-4.0, 0.0), Point(4.0, 0.0),
          Point(160.0x, 160.0y)], :fill)
    
    # Нарисовать груз (bob) маятника
    sethue(Colors.HSV(framenumber*4.0, 1, 1))  # меняющийся цвет в зависимости от номера кадра
    circle(Point(160.0x, 160.0y), 160*bobd, :fill)

    # Напечатать информацию о текущем кадре
    Luxor.text(string("frame $framenumber of $(scene.framerange.stop)"),
         Point(0.0, -190.0), halign=:center)
end
Out[0]:
anim_frame (generic function with 1 method)

Creating and playing animations

Creating an animation object with the specified window size

In [ ]:
muv = Movie(400, 400, "Pendulum Demo", 1:length(tspan))
Out[0]:
Movie(400.0, 400.0, "Pendulum Demo", 1:114)

Adding two scenes: the background and the main one

In [ ]:
Luxor.animate(muv, [
    Luxor.Scene(muv, backdrop),             # фоновая сцена
    Luxor.Scene(muv, anim_frame, easingfunction=easeinoutcubic)  # главная сцена с плавным переходом
], creategif=true, pathname=joinpath(@__DIR__, giffilename))            # создаём .gif и сохраняем его под именем "pendulum.gif"
Info: Frames for animation "Pendulum Demo" are being stored in directory: 
	 /tmp/jl_1cbAyz
Info: ... 114 frames saved in directory:
	 /tmp/jl_1cbAyz
[ Info: GIF is: /user/animate_a_pendulum/pendulum.gif
Out[0]:
No description has been provided for this image

Conclusion

We have reviewed a program in the Julia language that solves a boundary value problem for a pendulum motion model and builds a smooth animation in GIF format based on it. Key elements: physical model (equations of motion), numerical solution (MIRK4 method), and visualization (using Luxor). This approach is useful not only for educational purposes, but also for modeling more complex systems with constraints on initial or final conditions. This example illustrates the powerful scientific computing and graphical visualization tools in the Julia ecosystem.

The example was developed using materials from Rosetta Code