Engee 文档
Notebook

使用Julia的钟摆运动动画

这个例子展示了使用微分方程模拟物理摆的运动并用Julia编程语言创建动画的实现。

导言

钟摆是最简单的物理系统,广泛用于研究周期性运动和振荡。 在这个例子中,我们考虑了使用二阶常微分方程组对简单摆的运动进行建模的问题,然后使用搭配方法(MIRK)在边界值问题的框架中求解。 找到解决方案后,使用卢克索以GIF动画格式可视化。基于开罗的jl图书馆。

主要目标是展示Julia语言在物理问题数值解和图形可视化生成方面的能力。 动画可以让你了解钟摆运动的动态—它是如何随着时间的推移移动的,从给定的位置开始,在一定的时间间隔后在相反的点结束。

In [ ]:
# 安装所需的软件包
import Pkg; Pkg.add("Luxor")
import Pkg; Pkg.add("Colors")
import Pkg; Pkg.add("BoundaryValueDiffEq")

连接图书馆

In [ ]:
using Luxor        # 用于绘制动画
using Colors       # 控制动画元素的颜色
using BoundaryValueDiffEq  # 用于求解常微分方程的边值问题

定义模型的常量和参数

物理模型和动画的常量

In [ ]:
const g = 9.81;     # 重力加速度(m/s2)
const L = 1.0;      # 摆线长度(米)
const bobd = 0.10;  # 摆锤重量(鲍勃)的直径(以米为单位)

动画参数

In [ ]:
const framerate = 50.0;                # 动画帧速率(fps)
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)));  # 动画的时间点数组

颜色和文件名

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文件的名称

微分方程的问题和解决方案的制定

钟摆运动方程函数的定义

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)

设置边界条件

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)

边界值问题的形成与解决

In [ ]:
bvp2 = BVProblem(simplependulum, bc2, [pi/2, pi/2], (tspan[1], tspan[end]));
sol2 = solve(bvp2, MIRK4(), dt=dtframe);   # 带修正的4阶Runge–Kutta方法

动画渲染函数

渲染帧的背景函数

In [ ]:
function backdrop(scene, framenumber)
    background(bgcolor)   # 设置黑色背景
end
Out[0]:
backdrop (generic function with 1 method)

绘制每帧的主要功能是

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)

创建和播放动画

创建具有指定窗口大小的动画对象

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

添加两个场景:背景和主要场景

In [ ]:
Luxor.animate(muv, [
    Luxor.Scene(muv, backdrop),             # 背景场景
    Luxor.Scene(muv, anim_frame, easingfunction=easeinoutcubic)  # 平稳过渡的主舞台
], creategif=true, pathname=joinpath(@__DIR__, giffilename))            # 创造它。gif并将其保存在"钟摆"的名称下。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

结论

我们回顾了Julia语言的一个程序,该程序解决了摆运动模型的边界值问题,并在此基础上构建了GIF格式的平滑动画。 关键要素:物理模型(运动方程),数值解(MIRK4方法)和可视化(使用卢克索)。 这种方法不仅适用于教育目的,而且适用于对初始或最终条件有约束的更复杂系统进行建模。 这个例子说明了Julia生态系统中科学计算和图形可视化的强大工具。

该示例是使用[Rosetta代码]的材料开发的(https://rosettacode.org/wiki/Animate_a_pendulum