跳转至

最速降线

背景

最速降线问题要求寻找这样一条曲线:小球从静止释放,在均匀重力场中无摩擦运动,并以最短时间到达较低的目标点。它的解不是直线而是摆线,因此常用于检验最优控制和变分法。

问题定义

这里规定 \(y\) 轴向下为正,路径角 \(\theta\) 从竖直向下方向量起。最短时间问题为

\[ \begin{aligned} \min_{x,y,v,\theta,t_f}\quad & t_f=\int_0^{t_f}1\,\mathrm{d}t \\ \text{约束}\quad & \dot{x}=v\sin\theta, \\ & \dot{y}=v\cos\theta, \\ & \dot{v}=g\cos\theta, \qquad g=9.81\ \mathrm{m/s^2}, \\ & [x(0),y(0),v(0)]=[0,0,0], \\ & [x(t_f),y(t_f)]=[2,2], \\ & v\ge0, \qquad 0\le\theta\le\frac{\pi}{2}. \end{aligned} \]

终端速度和终止时间均为自由变量。

变量与单位

符号 含义 单位
\(t\) 时间 s
\(x\) 水平位移 m
\(y\) 向下位移 m
\(v\) 沿曲线运动的速率 m/s
\(\theta\) 相对竖直向下方向的路径角 rad
\(g\) 重力加速度 \(9.81\ \mathrm{m/s^2}\)

建模选择

采用向下为正的位移,可以让模型中的重力加速度保持正号。速率约束排除了路径参数发生非物理反向的情况,角度边界则选定向右、向下的单调运动。初始猜测采用直线下降路径,并令速率满足 \(v=\sqrt{2gy}\),从而符合机械能守恒。

运行示例

在仓库根目录运行:

python -m examples.brachistochrone

如需保存图片且不打开窗口:

python -m examples.brachistochrone --save brachistochrone.png --no-show

关键实现

import numpy as np
import sympy as sp

from pockit.lobatto import System, linear_guess

system = System(0)
phase = system.new_phase(["x", "y", "speed"], ["path_angle"])
_, _, speed = phase.x
(path_angle,) = phase.u

phase.set_dynamics(
    [
        speed * sp.sin(path_angle),
        speed * sp.cos(path_angle),
        9.81 * sp.cos(path_angle),
    ]
)
phase.set_integral([1.0])
phase.set_phase_constraint(
    [speed, path_angle], [0.0, 0.0], [np.inf, np.pi / 2.0]
)
phase.set_boundary_condition([0.0, 0.0, 0.0], [2.0, 2.0, None], 0.0, None)
phase.set_discretization(10, 8)
system.set_phase([phase])
system.set_objective(phase.I[0])

guess = linear_guess(phase, 0.0)
guess.t_f = 1.0

完整示例使用 Ipopt 求解,根据端点几何关系构造解析摆线,并检查数值运动时间是否与解析结果一致。

已验证结果

Pockit 得到的最短运动时间为 \(0.824338670697\ \mathrm{s}\),解析摆线结果为 \(0.824338669439\ \mathrm{s}\),两者相差约 \(1.3\times10^{-9}\ \mathrm{s}\)。图中的数值路径与摆线在视觉上重合。

数值最速降线、解析摆线、速率和路径角

源码

完整可运行示例见 examples/brachistochrone.py