Engee 文档
Notebook

解决弹道学的边界值问题

考虑一下奥运会上的铅球挑战。


  1. 给定:
    -飞行时间
    -初始位置
    -结束位置

确定核心的初始速度和抛出的角度


弹道方程的推导

为了解决这个问题,有必要制定抛出物体的运动方程。:
ballistics.png

根据该图,我们将绘制弹道运动方程。:

通过减少 ,并传递到一阶ODE系统,我们获得:

添加(Pkg.add)并连接库:

  • BoundaryValueDiffEq 求解微分方程的边值问题
  • Plots -显示解决方案的图表
  • LinearAlgebra: norm 找到规范
  • PhysicalConstants 它允许您使用自由落体的标准加速度的值
In [ ]:
using Pkg
Pkg.add(["BoundaryValueDiffEq","LinearAlgebra","PhysicalConstants"])
using BoundaryValueDiffEq
using Plots
import LinearAlgebra: norm  # используем не весь модуль, а только определённую функцию
import PhysicalConstants.CODATA2018: g_n 

g_n 它不仅仅是一个数字,而是一个物理常数(值,维度,幅度的不确定性)。 要获得"干净"值,您需要使用 float(g_n). 她会回来的 9.80665 m s^-2. 要仅获取值,您需要引用该字段 val

In [ ]:
g_n
Out[0]:
Standard acceleration of gravitation (g_n)
Value                         = 9.80665 m s^-2
Standard uncertainty          = (exact)
Relative standard uncertainty = (exact)
Reference                     = CODATA 2018
In [ ]:
const g = float(g_n).val
Out[0]:
9.80665
In [ ]:
_,b,_,d = [1 2 3 4]
print("b=$b, d=$d")
b=2, d=4

让我们写下我们的方程组

作为函数 ballistic!(du,u,p,t).
p -系统参数, t -时间(但我们的系统是[自治](https://ru.wikipedia.org/wiki/Автономная_система_дифференциальных_уравнений )(静止),即它显然不依赖于 t).

不要害怕引入新的变量 dx, ..., v_y 为提高可读性的缘故。 大多数情况下,这不会影响性能。

In [ ]:
function ballistic!(du, u, p, t)
    _, _, v_x, v_y = u
    
    du[1] =  dx  = v_x
    du[2] =  dy  = v_y
    du[3] = dv_x = 0
    du[4] = dv_y = -g
end
Out[0]:
ballistic! (generic function with 1 method)

定义问题的边界条件

现在让我们介绍初始*(a)和最终(b)*时间点中的边界条件。

这些限制以以下形式介绍: .

也就是说,如果条件 然后

我们将根据铅球的世界纪录来获取任务值本身。:

  • -基于第13页исследования
  • -2023年奥运会纪录
  • = 0
  • (基于记录本身的shot_put.mp4视频取景)
In [ ]:
function bca!(resid_a, u_a, p)
    resid_a[1] = u_a[1]        #   x(0) = 0
    resid_a[2] = u_a[2] - 2.1  #   y(0) = 2
end

function bcb!(resid_b, u_b, p)
    resid_b[1] = u_b[1] - 23.1  # x(t_f) = 95
    resid_b[2] = u_b[2]       # y(t_f) = 0
end

tspan = (0., 2)  
Out[0]:
(0.0, 2)

让我们对初始条件进行假设

In [ ]:
α = deg2rad(45)
x₀ = 0
y₀ = 2.1
v₀ = 10
v_x_0 = v₀ * cos(α)
v_y_0 = v₀ * sin(α)
u₀ = [x₀, y₀, v_x_0, v_y_0]
Out[0]:
4-element Vector{Float64}:
 0.0
 2.1
 7.0710678118654755
 7.071067811865475

求解两点边界值问题

让我们设置一个两点边界值问题(TwoPointBVProblem).

该问题由微分方程组、边界条件、初始假设和积分时间间隔组成。

关键词 bcresid_prototype 允许您设置初始假设的类型(我们可以在一个点上有4个条件 a 和4个条件在一个点 b. 但我们只指定了两个限制,因为速度在开始和结束时没有限制。

In [ ]:
bvp = TwoPointBVProblem(ballistic!, (bca!, bcb!), u₀, tspan;
        bcresid_prototype=(zeros(2), zeros(2)))
Out[0]:
BVProblem with uType Vector{Float64} and tType Float64. In-place: true
timespan: (0.0, 2.0)
u0: 4-element Vector{Float64}:
 0.0
 2.1
 7.0710678118654755
 7.071067811865475

现在仍然是通过选择两种方法来解决问题 - Shooting,用求解器 Verns7(). 有关求解边界值问题的算法的详细信息,请参阅документации.

In [ ]:
sol = solve(bvp, Shooting(Vern7()), saveat=0.1)
Out[0]:
retcode: Success
Interpolation: 1st order linear
t: 21-element Vector{Float64}:
 0.0
 0.1
 0.2
 0.3
 0.4
 0.5
 0.6
 0.7
 0.8
 0.9
 1.0
 1.1
 1.2
 1.3
 1.4
 1.5
 1.6
 1.7
 1.8
 1.9
 2.0
u: 21-element Vector{Vector{Float64}}:
 [0.0, 2.1, 11.55, 8.756650000000016]
 [1.1549999999999925, 2.926631749999996, 11.55, 7.7759850000000235]
 [2.3100000000000005, 3.6551970000000034, 11.55, 6.795320000000016]
 [3.465, 4.285695750000003, 11.55, 5.814655000000018]
 [4.619999999999997, 4.818128000000005, 11.55, 4.8339900000000195]
 [5.774999999999997, 5.252493750000007, 11.55, 3.85332500000002]
 [6.9300000000000015, 5.588793000000004, 11.55, 2.872660000000015]
 [8.084999999999988, 5.827025749999997, 11.55, 1.8919950000000263]
 [9.24000000000005, 5.967191999999978, 11.55, 0.9113299999999747]
 [10.395000000000012, 6.009291750000039, 11.55, -0.0693349999999926]
 [11.549999999999898, 5.953325000000021, 11.55, -1.049999999999896]
 [12.704999999999917, 5.799291749999795, 11.55, -2.030664999999912]
 [13.860000000000117, 5.547191999999763, 11.55, -3.011330000000079]
 [15.014999999999933, 5.197025750000018, 11.55, -3.9919949999999247]
 [16.170000000000005, 4.7487929999997505, 11.55, -4.972659999999985]
 [17.325000000000557, 4.202493749999437, 11.55, -5.953325000000454]
 [18.4800000000015, 3.5581279999992574, 11.55, -6.933990000001257]
 [19.635000000000005, 2.815695750000004, 11.55, -7.914654999999986]
 [20.790000000000003, 1.9751970000000036, 11.55, -8.895319999999986]
 [21.945, 1.0366317500000068, 11.55, -9.875984999999982]
 [23.100000000000005, 5.879140493979664e-15, 11.55, -10.856649999999986]
In [ ]:
gr()  # используем статическое отображение графиков
plt_record = plot(  sol[1, :], sol[2, :],
                    aspect_ratio=1.0, xlabel="x, м",ylabel="y, м", label = "траектория")
scatter!(plt_record, [0,23.1],[2.1, 0],label="краевые условия")
Out[0]:

从图中可以看出,ODE系统的解确实满足边界条件。

现在我们找出投掷时刻速度矢量的模数和角度的值 .

In [ ]:
norm([3 4]) # √(3²+4²)
Out[0]:
5.0
In [ ]:
norm([sol[1][3],sol[1][4]])  #√(v_x²+v_y²)
Out[0]:
14.49418570401595
In [ ]:
atan(sol[1][4]/sol[1][3]) |> rad2deg
Out[0]:
37.16764031481598

因此,投掷的速度是≈ 14.5 м/с,以及投掷的角度? 37.16°

确定投掷的角度,使您能够以最低的初始速度到达目标。

现在让我们来看看一组轨迹,并确保45°的角度允许您以最低的初始速度到达目标。

允许您根据初始速度更改轨迹颜色的功能

在构建轨迹之前,我们定义了函数,允许我们根据初始速度的值显示线的颜色。

我们来写一个函数 map_range,按比例显示段中的数字 [a,b] 段中 [A,B].

作为参数,它包含:

  1. 两个数字的元组 (a,b) -计划显示的初始部分
  2. 两个数字的元组 (A,B) -其中的部分
    3计划显示。 -的数量 [a,b],计划在 [A,B]

如果 ,然后函数抛出исключение。在我们的例子中,这个异常的类型是[DomainError](https://engee.com/helpcenter/stable/ru/julia/base/base.html#Core.DomainError ),自 x 离开函数定义的范围。

In [ ]:
map_range((a, b), (A, B), x) = a < x < b ?
                           A + (x - a) / (b - a) * (B - A) :
                           throw(DomainError(x, "x must be in domain ($a, $b)"))
map_range((14,18),(0,1),15)

# |14---15___16___17___18| |0---0.25___0.5___0.75___1|
Out[0]:
0.25
In [ ]:
# map_range((14,18),(0,1),19) # раскомментируйте, чтобы проверить работу исключений

为了使用颜色和使用某些颜色集,必须连接库。 ColorsColorSchemes

In [ ]:
Pkg.add(["Colors","ColorSchemes"])
using Colors
using ColorSchemes
   Resolving package versions...
    Updating `~/.project/Project.toml`
 [35d6a980] + ColorSchemes v3.26.0
 [5ae59095] + Colors v0.12.11
  No Changes to `~/.project/Manifest.toml`

让我们创建一组20种颜色,从蓝色到红色。:

In [ ]:
COLORS_NUMBER = 20
ndr = ColorScheme(get(ColorSchemes.darkrainbow, range(0.0, 1.0, length=COLORS_NUMBER)))
Out[0]:
No description has been provided for this image
In [ ]:
[ndr[1],ndr[end]]

让我们假设我们将有一组轨迹,其初始速度将从14到17变化。 我们想找到对应于速度15的颜色,如果蓝色是最低的速度,红色是最高的。

In [ ]:
round(Int,3.5)
Out[0]:
4
In [ ]:
ndr[round(Int,map_range((14,17),(1,COLORS_NUMBER),15))]
Out[0]:
No description has been provided for this image

让我们添加空图,我们将在每次迭代中添加新数据。:

In [ ]:
plt_trajectories = plot()
scatter_v_0_α = scatter();
In [ ]:
for tspan in map(x -> (0, x), 1.5:0.15:3)
    # решение краевой задачи для заданного (t_0,t_f)
    bvp = TwoPointBVProblem(ballistic!, (bca!, bcb!), u₀, tspan;
        bcresid_prototype=(zeros(2), zeros(2)))
    sol = solve(bvp, Shooting(Vern7()), saveat=0.1)
    # определение v_0 и α 
    v_0 = round(norm([sol[1][3],sol[1][4]]),digits=2)   # (round, π, digits=4) = 3.1416
    α = atan(sol[1][4]/sol[1][3]) |> rad2deg
    # построение графиков
    ## построение траекторий
    plot!(  plt_trajectories, sol[1, :], sol[2, :], aspect_ratio=1.0,
            color=ndr[round(Int,map_range((14,17), (1,COLORS_NUMBER),v_0))],
            label=nothing, w=3,xlabel="x",ylabel="y", title="траектории толкания ядра")  
    ## для построения зависимости v_0(α)
    scatter!(scatter_v_0_α,[α],[v_0],label=nothing,
             xlabel="α", ylabel="v_0",title="Зависимость начальной скорости от угла толкания",
              color=ndr[round(Int,map_range((14,17), (1,COLORS_NUMBER),v_0))])  
end
display(plt_trajectories)
In [ ]:
display(scatter_v_0_α)

结论

我们可以得出结论,实际上,为了达到设定的目标(在一定距离上投掷一个镜头),45°的角度提供了这种投掷可能的最小初始速度。 换句话说,如果指定的初始速度是固定的,45°的角度提供了核心可以复盖的最大距离。

由于完成了这项任务,审议了下列问题:

-求解两点边界值问题
-物理常数的使用
-创建超出函数定义区域边界时抛出异常的函数
-根据轨迹对应的数据(在我们的例子中为v_0),轨迹的自定义颜色变化