

Картинки из сети, качество желает лучшего, но они достаточно точно отражают суть опыта по визуализации фигур. Зри в корень – основа мудрости поколений.
Немного истории
Ещё в школе на уроках физики я вглядывался в осциллограф, на экране которого, сменяя друг друга, появлялись разные фигуры: сначала простые – линия, парабола, круг, эллипс, потом фигуры становились всё более насыщенные непрерывными волнообразными линиями, напоминающие мне кружева. Автором этого кружевного дива был Жюль Антуан Лиссажу французский физик, член — корреспондент Парижской АН (1879) [1]. Сами фигуры — это замкнутые траектории, прочерчиваемые точкой, совершающей одновременно два гармонических колебания в двух взаимно перпендикулярных направлениях [2]. Думаю, что в те далёкие от современности годы основной заслугой Жюля, кроме конечно накопленных опытом знаний математики и физики, была простая механическая визуализация этих фигур подручными средствами. Захотелось конструировать подобно Жулю максимально просто и наглядно, реализовать его идеи применительно к современной задаче линейных измерений. Но сделать это путём математического моделирования с графической визуализацией его результатов на Python. Но сначала рассмотрим классический вариант [3] построения фигур.
Какими должны быть фигуры Лиссажу
Для этого воспользуемся системой уравнений, описывающих фигуры:
x(t), y(t) в общем случае зависящие от времени гармонические колебания вдоль взаимно перпендикулярных плоскостей, частоты b, a и начальная фаза d. Для анализа фигур в вычислениях принимают постоянным модуль разности частот |b — a| = 1. Будем рассматривать отношение круговых частот b / a и начальную фазу d. Имеем для линии A = B d = 0, окружности
Код для построения графиков каждой из фигур на отдельных графиках
#!/usr/bin/env python
#coding=utf8
import numpy as np
from numpy import sin,pi
import matplotlib.pyplot as plt
m=[[0],[2,2],[2,1],[1,2],[3,2],[3,4],[5,4],[5,6],[9,8]]# отношение круговых частот
for i in m:
if i[0]==0:
a=1
x=[sin(a*t) for t in np.arange(0.,2*pi,0.01)]
y=[sin(a*t) for t in np.arange(0.,2*pi,0.01)]
plt.plot(x, y, 'r')# график для линии
plt.grid(True)
plt.show()
else:
a=i[0]
b=i[1]
d=0.5*pi
x=[sin(a*t+d) for t in np.arange(0.,2*pi,0.01)]
y=[sin(b*t) for t in np.arange(0.,2*pi,0.01)]
plt.plot(x, y, 'r') # график для различных отношений a/b
#круговых частот
plt.grid (True)
plt.show()
Результат не привожу, отдельные фигуры не впечатляют. Хочу коллаж из «кружев».
Код программы для построения на одной форме графиков для четырёх фигур при m= [3,4], [5,4],[5,6],[9,8]]
#!/usr/bin/env python
#coding=utf8
import numpy as np
from numpy import sin,pi
import matplotlib.pyplot as plt
m=[[3,4],[5,4],[5,6],[9,8]] # отношение круговых частот
plt.figure(1)
for i in m:
a=i[0]
b=i[1]
d=0.5*pi
x=[sin(a*t+d) for t in np.arange(0.,2*pi,0.01)]
y=[sin(b*t) for t in np.arange(0.,2*pi,0.01)]
if m.index(i)==0:
plt.subplot(221)
plt.plot(x, y, 'k') # график для различных отношений a/b круговых частот
plt.grid(True)
elif m.index(i)==1:
plt.subplot(222)
plt.plot(x, y, 'g')
plt.grid(True)
elif m.index(i)==2:
plt.subplot(223)
plt.plot(x, y, 'b')
plt.grid(True)
else:
plt.subplot(224)
plt.plot(x, y, 'r')
plt.grid(True )
plt.show()
И вот они «кружева».
Что нельзя отнести к фигурам Лиссажу по определению о их замкнутости
Зачем нам |b — a| = 1, “за флажки!” попробуем например так m=[[1,3],[1,5],[1,7],[1,9]]

На втором графике при m=0,2 получена незамкнутая траектория, которая по определению не является фигурой Лbссажу.
В поисках механических аналогов
Поищем аналогии фигур в измерительной технике и вот вибрационный уровнемер с резонатором в виде эллиптической трубки [4].
Упруго закреплённая трубка эллиптического сечения с помощью систем возбуждения 5,6,7 совершает автоколебания в одной плоскости, а с помощью систем 8, 9, 10 в другой плоскости перпендикулярной первой. Трубка колеблется в двух взаимно перпендикулярных плоскостях с разными частотами близкими к собственным. Масса трубки зависит от уровня заполняющей её жидкости. С изменением массы меняются и частоты колебаний трубки, которые и являются выходными сигналами уровнемера. Частоты несут дополнительную информацию о мультипликативных и аддитивных дополнительных погрешностях, компенсируемых при обработке частот микропроцессором 11.
Условия адекватного моделирования
Для более-менее корректной привязки фигур Лиссажу к работе упомянутого уровнемера, следует учесть следующие обстоятельства. Во-первых, закреплённая одним концом трубка эллиптического сечения — это колебательная система с распределёнными параметрами, что сильно усложняет анализ её колебаний. Во-вторых, отношение частот колебаний трубки не может изменяться произвольно, оно зависит от эллипсности сечения и допустимых зазоров в системе возбуждения колебаний. Для отношения частот можно получить простое соотношение.
К чему принадлежат переменные, a, b, a0, b0 ясно из рисунка и кроме того формула для циклической частоты осциллятора известна из школьного курса физики. Для «реализации на Python в последнее отношение введём толщину стенки и показатель эллипсности внутреннего сечения трубки, тогда вместо четырёх переменных получим три.
Код программы для определения. допустимого изменения отношения частот
#!/usr/bin/env python
#coding=utf8
import numpy as np
from numpy import sqrt
import matplotlib.pyplot as plt
import matplotlib as mpl
mpl.rcParams['font.family'] = 'fantasy'
mpl.rcParams['font.fantasy'] = 'Comic Sans MS, Arial'
d=0.5
a=9
x=[w for w in np.linspace(0.8,0.95,15)]
y=[sqrt(x**-2*((1+d/a)**3*(1+d/(x*a))-1)/((1+d/a)*(1+d/(x*a))**3-1)) for x in np.linspace(0.8,0.95,15)]
plt.plot(x, y, 'r', label='Толщина стенки трубки в мм. -- %s' %str(d))
d=0.7
y=[sqrt(x**-2*((1+d/a)**3*(1+d/(x*a))-1)/((1+d/a)*(1+d/(x*a))**3-1)) for x in np.linspace(0.8,0.95,15)]
plt.plot(x, y, 'b',label='Толщина стенки трубки в мм.-- %s' %str(d))
d=1.0
y=[sqrt(x**-2*((1+d/a)**3*(1+d/(x*a))-1)/((1+d/a)*(1+d/(x*a))**3-1)) for x in np.linspace(0.8,0.95,15)]
plt.plot(x, y, 'g', label='Толщина стенки трубки в мм.-- %s' %str(d))
plt.ylabel('Отношение частот колебаний эллиптической трубки')
plt.xlabel('Отношение длин малой и большой полуосей')
plt.title('Определение допустимого диапазона для отношения частот')
plt.legend(loc='best')
plt.grid(True)
plt.show()
В результате работы программы получим график.

График построен для малой внутренней полуоси в 9 мм. Для конструктивно допустимого отношения малой к большой полуоси сечения в диапазоне от 0.8 до 0.95. Это основной фактор влияния на отношение частот, которое изменяется от 1.18 до 1.04. Толщина стенки влияет незначительно. Теперь у нас есть диапазон отношений и ним можно воспользоваться для дальнейшего моделирования.
Формы колебаний вертикальной оси трубки
Что касается распределённых механических параметров консольной трубки, то они при помощи равенства собственных частот и импеданса могут быть приведены к сосредоточенной массе жёсткости и демпфированию. Кроме того, для определения форм изгибных колебаний консольной трубки можно получить выражение для распределённых параметров. Уравнение для форм – балочные функции имеет вид:
где
Следует отметить что, не смотря на большое количество публикаций о формах и частотах колебаний консольного стержня, балки или трубки уравнения (4) нигде не приводяться, только рисунки без координат. Поэтому уравнение (4), я вывел через условия на концах и балочные функции, проверил по корням (5) и расположению узлов. Однако это тривиальное уравнение, о котором просто забыли.
Код программы для численного определения корней уравнения 1.1 и построения трёх форм изгибных колебаний оси трубки
1.1 — 
#!/usr/bin/env python
#coding=utf8
from scipy.optimize import *
import numpy as np
from numpy import pi,cos,cosh,sin,sinh
import matplotlib.pyplot as plt
import matplotlib as mpl
mpl.rcParams['font.family'] = 'fantasy'
mpl.rcParams['font.fantasy'] = 'Comic Sans MS, Arial'
d=[]
for i in range(0,4):
x=brentq(lambda x:cosh(x)*cos(x)+1,0+pi*i,pi+pi*i)
p=round(x,3)
if p not in d:
d.append(p)
x=[w for w in np.linspace(0,1,100)]
k=d[0]
z=[sin(k*x)-sinh(k*x)+((cosh(k) -cos(k))/(sin(k)-sinh(k)))*(cos(k*x)-cosh(k *x) )for x in np.linspace(0,1,100)]
plt.plot(z, x, 'g', label='Первая форма для корня - %s' %str(k))
k=d[1]
z=[sin(k*x)-sinh(k*x)+((cosh(k) -cos(k))/(sin(k)-sinh(k)))*(cos(k*x)-cosh(k *x)) for x in np.linspace(0,1,100)]
plt.plot(z, x, 'b', label='Вторая форма для корня - %s' %str(k))
k=d[2]
z=[sin(k*x)-sinh(k*x)+((cosh(k) -cos(k))/(sin(k)-sinh(k)))*(cos(k*x)-cosh(k *x)) for x in np.linspace(0,1,100)]
plt.plot(z, x, 'r', label='Третья форма для корня - %s' %str(k))
plt.title('Первые три формы изгибных колебаний осевой линии трубки')
plt.xlabel(' Координата вдоль оси OX ')
plt.ylabel(' Координата положения осевой линии трубки вдоль оси OZ ')
plt.legend(loc='best')
plt.grid(True)
plt.show()
В результате работы программы получим график построенный с учётом вертикального положения трубки.

На графике координата осевой линии приведена к длине трубки, а амплитуда нормирована. Положение узлов колебаний трубки относительно места её крепления в точности соответствует теории колебаний.
По каким траекториям движется конец трубки
Последнее препятствие — сложность получения осмысленного численного решения дифференциальных уравнений колебаний, при условии варьирования несколькими параметрами одновременно. Тут на помощь пришли две мои статьи о колебательном звене на Python [5,6], в которых приведена методика получения точных символьных решений дифференциальных уравнений.
Запишем два условно независимых уравнения для колебаний трубки в плоскости OX и OY с разными частотами a и b отношение между которыми выбрано из ранее установленного диапазона. Остальные параметры выбраны во правильной взаимосвязи, но произвольно для лучшей демонстрации результата.
Здесь введены следующие обозначения (для упрощения без индексов).
Код программы для решения каждого дифференциального уравнения системы (6), с последующем сложением для получения траектории движения конца трубки.
import numpy as np
from sympy import *
from IPython.display import *
import matplotlib.pyplot as plt
import matplotlib as mpl
mpl.rcParams['font.family'] = 'fantasy'
mpl.rcParams['font.fantasy'] = 'Comic Sans MS, Arial'
def solution(w,v,i,n1,n2,B,f,N):
t=Symbol('t')
var('t C1 C2')
u = Function("u")(t)
de = Eq(u.diff(t, t) +2*B*u.diff(t) +w**2* u, f*sin(w*t+v))
des = dsolve(de,u)
eq1=des.rhs.subs(t,0)
eq2=des.rhs.diff(t).subs(t,0)
seq=solve([eq1,eq2],C1,C2)
rez=des.rhs.subs([(C1,seq[C1]),(C2,seq[C2])])
g= lambdify(t, rez, "numpy")
t= np.linspace(n1,n2,N)
plt.figure(1)
if i==1:
plt.subplot(221)
plt.plot(t,g(t),color='b', linewidth=3,label='x=%s*sin(%s*t+%s)' %(str(f),str(w),str(v)))
plt.legend(loc='best')
plt.grid(True)
else:
plt.subplot(222)
plt.plot(t,g(t),color='g', linewidth=3,label='y=%s*sin(%s*t+%s)' %(str(f),str(w),str(v)))
plt.legend(loc='best')
plt.plot(t,g(t),color='r', linewidth=3)
plt.grid(True)
return g(t)
N=1000#Число точек оцифровки временного интервала
B=0.2#Установка демпфирования
f=1#Установка амплитуды
n1=0#Нижняя граница временной развертки
n2=20#Верхняя граница временной развёртки
w1=5.0#Частота колебаний трубки вдоль оси ОХ
w2=10.0#Частота колебаний трубки вдоль оси ОУ
v1=0#Начальная фаза при колебании вдоль оси ОХ
v2=0#Начальная фаза при колебании вдоль оси ОУ
g1=solution(w1,v1,1,n1,n2,B,f,N)
g2=solution(w2,v2,2,n1,n2,B,f,N)
plt.subplot(223)
plt.plot(g1,g2,color='b', linewidth=3,label='w1/w2=%s'%str(w1/w2))
plt.legend(loc='best')
plt.grid(True)
plt.subplot(224)
x=[w for w in np.linspace(0,1,100)]
k=1.875
z=[sin(k*x)-sinh(k*x)+((cosh(k) -cos(k))/(sin(k)-sinh(k)))*(cos(k*x)-cosh(k *x) )for x in np.linspace(0,1,100)]
plt.plot(z, x, 'g',label='Форма -%s'%str(k))
plt.legend(loc='best')
plt.grid(True)
plt.show()
Программа позволяет менять все параметры модели, например, для:
N=1000, B=0.2, f=1, n1=0, n2=20, w1=5.0, w2=10.0, v1=0, v2=0

Для отношения частот 0.5 переходной процесс множит фигуры. Поставим “ворота” времени n15=0, n2=20, получим.

Снимем” ворота” и введём начальную фазу v2=-pi/2, получим:

С учётом изложенного выше, графики комментарий не требую.
Для интриги
Если эта статья найдёт своих читателей или читатели её найдут, не устрашившись теней прошлого, то я опубликую трёхмерные анимационные графики сложных пространственных колебаний трубки при изменении в ней уровня заполняющей жидкости.
Вместо выводов
Изобретение Жюля Антуана Лиссажу продолжает свой путь во времени, но уже и на Python. Надеюсь, что представленная интерпретация, конечно далёкая от совершенства, позволит продолжить знакомство с работами гениального математика Лиссажу.
Ссылки
Поделиться с друзьями
Комментарии (2)
red_andr
05.05.2017 19:16Занятно, спасибо. Фигуры Лиссажу были моей первой серьёзной программой, ещё на Бейсике.
hardtop
Отлично. Как раз ребенку показываю всякие несложные штуки на питоне. Спасибо!