import numpy as np
from resonance.linear_systems import BallChannelPendulumSystem
import matplotlib.pyplot as plt
%matplotlib inline
sys = BallChannelPendulumSystem()
def can_coeffs(mp, mb, l, g, r):
M = np.array([[mp * l**2 + mb * r**2, -mb * r**2],
[-mb * r**2, mb * r**2]])
C = np.zeros((2, 2))
K = np.array([[g * l * mp, g * mb * r],
[g * mb * r, g * mb * r]])
return M, C, K
sys.canonical_coeffs_func = can_coeffs
M, C, K = sys.canonical_coefficients()
M
C
K
L = np.linalg.cholesky(M)
L
L.T
L @ L.T
np.linalg.inv(L.T)
np.linalg.inv(L) @ M @ np.linalg.inv(L.T)
Ktilde = np.linalg.inv(L) @ K @ np.linalg.inv(L.T)
Ktilde
k11 = Ktilde[0, 0]
k12 = Ktilde[0, 1]
k21 = Ktilde[1, 0]
k22 = Ktilde[1, 1]
lam1 = (k11 + k22) / 2 + np.sqrt((k11 + k22)**2 - 4 * (k11 * k22 - k12*k21)) / 2
lam1
lam2 = (k11 + k22) / 2 - np.sqrt((k11 + k22)**2 - 4 * (k11 * k22 - k12*k21)) / 2
lam2
omega1 = np.sqrt(lam1)
omega1
omega2 = np.sqrt(lam2)
omega2
v1 = np.array([-k12 / (k11 - lam1), 1])
v1 / np.linalg.norm(v1)
v2 = np.array([-k12 / (k11 - lam2), 1])
v2 / np.linalg.norm(v2)
evals, evecs = np.linalg.eig(Ktilde)
evals
evecs
P = evecs
P.T @ P
Lam = P.T @ Ktilde @ P
Lam
The eigenvectors can be put back into units of x with:
S = np.linalg.inv(L.T) @ P
S
The trajectory of building's coordinates can be found with:
$$ \mathbf{x}(t) = \sum_{i=1}^n c_i \sin(\omega_i t + \phi_i) \mathbf{u}_i $$where
$$ \phi_i = \arctan \frac{\omega_i \mathbf{v}_i^T \mathbf{q}_0}{\mathbf{v}_i^T \dot{\mathbf{q}}_0} $$and
$$ c_i = \frac{\mathbf{v}^T_i \mathbf{q}_0}{\sin\phi_i} $$$d_i$ are the modal participation factors and reflect what propotional of each mode is excited given specific initial conditions. If the initial conditions are the eigenmode, $\mathbf{u}_i$, the all but the $i$th $d_i$ will be zero.
x0 = S[:, 1] / 300
np.rad2deg(x0)
x0[0]/x0[1]
xd0 = np.zeros(2)
xd0
q0 = L.T @ x0
q0
q0[0]/q0[1]
ws = np.sqrt(evals)
ws
phis = np.arctan2(ws * P.T @ q0, P.T @ xd0)
phis
cs = P.T @ q0 / np.sin(phis)
cs
t = np.linspace(0, 5, num=1000)
x = np.zeros((2, 1000))
for ci, wi, phii, ui in zip(cs, ws, phis, S.T):
x += ci * np.sin(wi * t + phii) * np.tile(ui, (len(t), 1)).T
def sim(x0, xd0, t):
q0 = L.T @ x0
ws = np.sqrt(evals)
phis = np.arctan2(ws * P.T @ q0, P.T @ xd0)
cs = P.T @ q0 / np.sin(phis)
x = np.zeros((2, 1000))
for ci, wi, phii, ui in zip(cs, ws, phis, S.T):
x += ci * np.sin(wi * t + phii) * np.tile(ui, (len(t), 1)).T
return x
t = np.linspace(0, 5, num=1000)
x0 = S[:, 0] / np.max(S[:, 0]) * np.deg2rad(10)
xd0 = np.zeros(2)
plt.figure()
plt.plot(t, sim(x0, xd0, t).T)
t = np.linspace(0, 5, num=1000)
x0 = S[:, 1] / np.max(S[:, 1]) * np.deg2rad(10)
xd0 = np.zeros(2)
plt.figure()
plt.plot(t, sim(x0, xd0, t).T)
sys.coordinates['theta'] = x0[0]
sys.coordinates['phi'] = x0[1]
sys.speeds['alpha'] = 0
sys.speeds['beta'] = 0
traj = sys.free_response(2 * np.pi / ws[1] * 10)
traj[['theta', 'phi']].plot()
sys.animate_configuration()