【趣味小题】悬链线的力学模拟



2020年01月25日    Author:Guofei

文章归类: 趣文    文章编号:

版权声明:本文作者是郭飞。转载随意,但需要标明原文链接,并通知本人
原文链接:https://www.guofei.site/2020/01/25/catenary.html


悬链线

$y=acosh\dfrac{x}{a}$

1. 画图

import numpy as np
import matplotlib.pyplot as plt


def catenary(x, a):
    return a * np.cosh((x - 0.5) / a) - a * np.cosh((-0.5) / a)


x = np.linspace(0, 1, 100)
for a in [0.35, 0.5, 0.8]:
    plt.plot(x, catenary(x, a), label='a={:g}'.format(a))
plt.legend()
plt.show()

catenary1

2. 计算长度

曲线长度可以这么计算:

$s=\int\sqrt{1+(\dfrac{dy}{dx})^2}dx$

先用粗略的方法估计:

y=catenary(x,0.35)
(((np.diff(y))**2+np.diff(x)**2)**0.5).sum()

1.3765522648842718

3. 力学模拟法

# 悬链线的力学模拟
import numpy as np


class Catenary:
    def __init__(self):
        self.length = 2
        self.n = 31
        self.dump = 0.2  # 阻尼系数
        self.k = 1  # 弹簧系数
        self.l = self.length / (self.n - 1)  # 每段长度
        self.g = 0.01  # 重力加速度

        self.x = np.concatenate([np.linspace(0, 1, self.n).reshape(-1, 1), np.zeros((self.n, 1))],
                                axis=1)
        self.v = np.zeros_like(self.x)

        # 重力加速度
        self.acc_gravity = np.zeros_like(self.x)
        self.acc_gravity[:, 1] = -self.g

    def cal_accelerate(self, a, b):
        # 输入x,和x左/右相邻点坐标,记为a,b
        # 输出弹力对应的加速度
        k, l = self.k, self.l
        ab = b - a
        ab_m = np.sqrt(np.square(ab[:, [0]]) + np.square(ab[:, [1]]))
        acc_scale_tmp = k * (ab_m - l)
        acc_scale = np.where(acc_scale_tmp > 0, acc_scale_tmp, 0)
        acc = ab / ab_m * k * acc_scale
        return acc

    def update_v(self, x, v):
        n, dump = self.n, self.dump
        acc_left = self.cal_accelerate(x[1:n - 1, :], x[:n - 2, :])  # 受到左边的拉力
        acc_right = self.cal_accelerate(x[1:n - 1, :], x[2:, :])  # 受到右边的拉力
        acc1 = np.concatenate([[[0, 0]], acc_left + acc_right, [[0, 0]]], axis=0)
        v = (1 - dump) * v + acc1 + self.acc_gravity
        v[0, :] = 0
        v[-1, :] = 0
        return v

    def update_location(self, x, v):
        x = x + v
        return x

    def update_all(self):
        self.v = self.update_v(self.x, self.v)
        self.x = self.update_location(self.x, self.v)
        return self.x


catenary = Catenary()

frame = [catenary.update_all() for i in range(100)]

# %%
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

fig, ax = plt.subplots()

line, = ax.plot([0], [0], '.-', animated=True)
ax.set_xlim(0, 1)
ax.set_ylim(-5, 1)


def update_line(i):
    plt.setp(line, 'xdata', i[:, 0], 'ydata', i[:, 1])
    return [line]


ani = FuncAnimation(fig, update_line, blit=True, interval=25, frames=frame)
# ani.save('catenary.gif', writer='pillow')

catenary2

后面会震荡,这是因为一阶模拟动力系统不准

4. 能量最小

只写提要

  1. 忽略弹性势能
  2. 整个系统的参数不用质点的坐标表示,而用每段的角度表示,如此,每个质点的位置可以表示为 $y_i=\sum\limits_{k=0}^i l \sin \theta_k$
  3. 然后就转化为一个纯粹的最优化问题了

变分法

另一篇文章有写,不多说了


您的支持将鼓励我继续创作!