Неправильный период времени для моделируемой системы маятников с N телами по методу Канеса - PullRequest
0 голосов
/ 15 января 2019

Мы можем использовать метод Кейна для интегрирования уравнений движения для системы n маятников с произвольными массами и длинами ( см. ). Реализация программы вдохновлена ​​ jakevdp . Однако, когда я использую этот метод аппроксимации, чтобы найти псевдовременный период T (определенный как среднее всех периодов времени для каждого маятника в системе n маятников, то есть average(period(theta_1),...,period(theta_n))), график T как функция n кажется невероятно отключенным с точки зрения амплитуды.

Цель: визуализировать взаимосвязь между числом маятников n и псевдовременным периодом T=2π/omega bar всей системы, где omega bar является средним значением всех угловых скоростей omega.

Следующая реализация определяет и решает уравнения движения для системы n маятников, с произвольными массами и длинами (в этом случае мы разрешим m[i]=1 для всех i). Это начальная проблема с theta_1(0)=135,...,theta_n(0)=135 и theta_1'(0)=0,...,theta_n'(0)=0.

# %matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

from sympy import symbols
from sympy.physics import mechanics

from sympy import Dummy, lambdify
from scipy.integrate import odeint


def integrate_pendulum(n, times,
                   initial_positions=135,
                   initial_velocities=0,
                   lengths=None, masses=1):
"""Integrate a multi-pendulum with `n` sections"""
#-------------------------------------------------
# Step 1: construct the pendulum model

# Generalized coordinates and velocities
# (in this case, angular positions & velocities of each mass) 
q = mechanics.dynamicsymbols('q:{0}'.format(n))
u = mechanics.dynamicsymbols('u:{0}'.format(n))

# mass and length
m = symbols('m:{0}'.format(n))
l = symbols('l:{0}'.format(n))

# gravity and time symbols
g, t = symbols('g,t')

#--------------------------------------------------
# Step 2: build the model using Kane's Method

# Create pivot point reference frame
A = mechanics.ReferenceFrame('A')
P = mechanics.Point('P')
P.set_vel(A, 0)

# lists to hold particles, forces, and kinetic ODEs
# for each pendulum in the chain
particles = []
forces = []
kinetic_odes = []

for i in range(n):
    # Create a reference frame following the i^th mass
    Ai = A.orientnew('A' + str(i), 'Axis', [q[i], A.z])
    Ai.set_ang_vel(A, u[i] * A.z)

    # Create a point in this reference frame
    Pi = P.locatenew('P' + str(i), l[i] * Ai.x)
    Pi.v2pt_theory(P, A, Ai)

    # Create a new particle of mass m[i] at this point
    Pai = mechanics.Particle('Pa' + str(i), Pi, m[i])
    particles.append(Pai)

    # Set forces & compute kinematic ODE
    forces.append((Pi, m[i] * g * A.x))
    kinetic_odes.append(q[i].diff(t) - u[i])

    P = Pi

# Generate equations of motion
KM = mechanics.KanesMethod(A, q_ind=q, u_ind=u,
                           kd_eqs=kinetic_odes)
fr, fr_star = KM.kanes_equations(particles, forces)

#-----------------------------------------------------
# Step 3: numerically evaluate equations and integrate

# initial positions and velocities – assumed to be given in degrees
y0 = np.deg2rad(np.concatenate([np.broadcast_to(initial_positions, n),
                                np.broadcast_to(initial_velocities, n)]))

# lengths and masses
if lengths is None:
    lengths = np.ones(n) / n
lengths = np.broadcast_to(lengths, n)
masses = np.broadcast_to(masses, n)

# Fixed parameters: gravitational constant, lengths, and masses
parameters = [g] + list(l) + list(m)
parameter_vals = [9.81] + list(lengths) + list(masses)

# define symbols for unknown parameters
unknowns = [Dummy() for i in q + u]
unknown_dict = dict(zip(q + u, unknowns))
kds = KM.kindiffdict()

# substitute unknown symbols for qdot terms
mm_sym = KM.mass_matrix_full.subs(kds).subs(unknown_dict)
fo_sym = KM.forcing_full.subs(kds).subs(unknown_dict)

# create functions for numerical calculation 
mm_func = lambdify(unknowns + parameters, mm_sym)
fo_func = lambdify(unknowns + parameters, fo_sym)

# function which computes the derivatives of parameters
def gradient(y, t, args):
    vals = np.concatenate((y, args))
    sol = np.linalg.solve(mm_func(*vals), fo_func(*vals))
    return np.array(sol).T[0]

# ODE integration
return odeint(gradient, y0, times, args=(parameter_vals,)) 

Выше приведены обобщенные физические координаты, то есть угловое положение theta и скорость omega каждого сегмента маятника относительно вертикали. Далее извлекаются координаты (x,y) из обобщенных координат.

def get_xy_coords(p, lengths=None):
    """Get (x, y) coordinates from generalized coordinates p"""
    p = np.atleast_2d(p)
    n = p.shape[1] // 2
    if lengths is None:
        lengths = np.ones(n) / n
    zeros = np.zeros(p.shape[0])[:, None]
    x = np.hstack([zeros, lengths * np.sin(p[:, :n])])
    y = np.hstack([zeros, -lengths * np.cos(p[:, :n])])
    return np.cumsum(x, 1), np.cumsum(y, 1)

Затем мы фиксируем число маятников, скажем, n=10 и определяем период псевдо-времени следующим образом:

n = 10
nperiod = [] 

#Array containing pseudo-period for a system of `n` pendulums

for i in range(1,n + 1):
    t = np.linspace(0, 10, 1000)
    p = integrate_pendulum(i, times=t)

#x, y has first column of zeros

    x, y = get_xy_coords(p) 
    r,s = np.shape(y)

#Call method to find pseudo-period

    nperiod.append(computeperiod()) 

#Takes `j`, denoting `j`-th pendulum, as input and returns `theta_j` for all times where `1≤j≤n`. This information corresponds to the `j`-th column of the `y` matrix, transformed into polar coordinates.

    def theta(j):
        theta_j = []
        for i in range(0, r):
            theta_j.append(math.acos(abs(y[i][j-1]-y[i][j])))         
#We should technically divide by the length of the pendulum in `abs(.)`

    timenew = [i for i in range(1,r + 1)]
    graph_j = pd.Series(data=theta_j, index=timenew)

#Returns array omega_j which is the numerical time derivative of all values contained in the `r x 1` array `theta_j`, i.e. the value of the `j`-th angle theta_j for all times

    return pd.Series(data=np.gradient(graph_j.values), index=graph_j.index) 

#Returns pseudo-time period using formula `T=2π/omega`

def computeperiod():
    series = []
    for j in range(1,s):
        series.append(2 * math.pi/(theta(j).mean()))
    numberline = [i for i in range(1,s)] #Here s=n+1
    timeperiod = pd.Series(data=series, index=numberline)
    return abs(timeperiod.mean())
print(nperiod)
numbers = [i for i in range(len(nperiod))]
finalperiods = pd.Series(data=nperiod, index=numbers)
finalperiods.plot()

Когда мы строим его, мы получаем следующий график для n=10: Однако он должен вести себя асимптотически как O(n^(3/2)) в соответствии с T(n) = 2 * π * n^(3/2)(l/g)^(1/2), где g = 9.8 и l = 1, как показано ниже.

Кроме того, его амплитуда должна быть уменьшена примерно в 100,000. Таким образом, кажется, что с этим методом что-то явно не так.

...