Ship roll-decay simulation
This is a short introduction to how a ship roll-decay test can be simulated.
- Roll damping
- Ship roll-decay test
- Simulation
- Think of something that could be simulated in your field of work or research?
This post shows how simulations with State Space Models is used in my research. This is not "rocket science", since it is not very complicated, but actually it is, since these methods were developed during the Apollo project! :)
A systems ability to damp reconance motions can be crucial as in the case of the Tacoma-narrows-bridge-collapse:
Resonance phenomena can also occur for ships, where for instance something called parametric roll can happen as can be seen in this video:
Having enough roll damping is one way to deal with this problem.
Ship roll-decay test
One way to determine the roll damping is to conduct a so called Roll-decay test. This test is conducted by giving the ship an initial roll angle and then observing the resulting oscillation. The oscillation motion will decrease with time due to energy losses from friction with the water and other things such as wave and eddy generation. Here is an example of a roll decay model test:
from scipy.integrate import solve_ivp
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from ipywidgets import interactive
from IPython.core.display import HTML
def calculate_acceleration(F,m):
a = F/m
return a
Lets simulate a 1 kg object falling without any drag. It can be done by:
- calculate acceleration
- calculate the velocity by adding acceleration
- calculate the position by adding the velocity
dt = 1/10
ts = np.arange(0,10,dt)
g=9.81
F=-g
m=1
velocity=0.0 # initial velocity
position=0.0 # initial position
positions=[]
for n in range(len(ts)):
acceleration = calculate_acceleration(F=F, m=m)
velocity = velocity + acceleration*dt
position = position + velocity*dt
positions.append(position)
positions = np.array(positions)
fig,ax=plt.subplots()
ax.plot(ts,positions);
ax.set_xlabel('time [s]')
ax.set_ylabel('position [m]');
ax.grid(True)
And here is an interactive animation of the same thing (use the Binder link at the top to get this running):
#collapse
from matplotlib.lines import Line2D
import matplotlib.animation as animation
class SubplotAnimation(animation.TimedAnimation):
def __init__(self):
fig,axes = plt.subplots(ncols=2)
fig.set_size_inches(12,8)
self.fig = fig
self.axes=axes
ax = axes[0]
ax.set_ylim(-500,0)
ax.set_xlim(0,0)
ax.set_ylabel('Position [m]')
ax.grid(True)
ax = axes[1]
ax.set_ylim(-500,0)
ax.set_xlim(0,ts[-1])
ax.set_xlabel('Time [s]')
ax.set_ylabel('Position [m]')
ax.grid(True)
self.t = ts
self.y = positions
self.ball = Line2D([], [], linestyle='', marker='o',color='b', ms=20)
self.ball_time = Line2D([], [], color='red', linewidth=2)
axes[0].add_line(self.ball)
axes[1].add_line(self.ball_time)
return animation.TimedAnimation.__init__(self, fig, interval=50, blit=True)
def _draw_frame(self, framedata):
i = framedata
x = 0
position=positions[i]
y = position
self.ball.set_data(x, y)
times = np.arange(i, dtype=int)
self.ball_time.set_data(self.t[times],positions[times])
self._drawn_artists = [self.ball,self.ball_time]
def new_frame_seq(self):
return iter(range(self.t.size))
def _init_draw(self):
lines = [self.ball,self.ball_time]
for l in lines:
l.set_data([], [])
anim_ball = SubplotAnimation()
plt.close()
#collapse
plt.rcParams["animation.html"] = "jshtml"
HTML(anim_ball.to_jshtml())
$$ A\cdot\ddot{\phi} + C\cdot\phi = 0 $$
dt = 0.1
ts = np.arange(0,30,dt)
g=9.81
A=1
C=1
phi1d=0.0 # initial velocity
phi=np.deg2rad(10) # initial roll angle
phis=[]
for n in range(len(ts)):
phi2d = -C*phi/A
phi1d = phi1d + phi2d*dt
phi = phi + phi1d*dt
phis.append(phi)
phis = np.array(phis)
fig,ax=plt.subplots()
ax.plot(ts,np.rad2deg(phis));
ax.set_xlabel('time [s]')
ax.set_ylabel('phi [deg]');
ax.grid(True)
#collapse
from matplotlib.lines import Line2D
import matplotlib.animation as animation
from matplotlib.patches import Rectangle
from matplotlib import transforms
def add_ship(ax):
beam=1
height=0.5
ship = Rectangle(xy=(-beam/2,-height/2),width=1,height=0.5)
ax.add_patch(ship)
ax.axis('equal')
xlim=1.3*beam/2*np.array([-1,1])
ax.set_xlim(xlim)
ax.set_ylim(xlim)
ax.plot(xlim,[0,0],'b-');
return ship
def rotate_ship(ship,ax,angle):
t2 = transforms.Affine2D().rotate_deg(-np.rad2deg(angle)) + ax.transData
ship.set_transform(t2)
class RollDecayAnimation(animation.TimedAnimation):
def __init__(self, ts, phis):
fig,axes = plt.subplots(ncols=2)
fig.set_size_inches(12,8)
self.fig = fig
self.axes=axes
self.t = ts
self.phis = phis
ax = axes[0]
self.ship=add_ship(ax=ax)
ax = axes[1]
ax.set_ylim(np.min(np.rad2deg(self.phis)),np.rad2deg(np.max(self.phis)))
ax.set_xlim(0,self.t[-1])
ax.set_xlabel('Time [s]')
ax.set_ylabel('roll [deg]')
ax.grid(True)
self.phi_time = Line2D([], [], color='red', linewidth=2)
#axes[0].add_line(self.ball)
axes[1].add_line(self.phi_time)
return animation.TimedAnimation.__init__(self, fig, interval=30, blit=True)
def _draw_frame(self, framedata):
i = framedata
phi=self.phis[i]
times = np.arange(i, dtype=int)
self.phi_time.set_data(self.t[times],np.rad2deg(self.phis[times]))
rotate_ship(ship=self.ship, ax=self.axes[0], angle=phi)
self._drawn_artists = [self.phi_time, self.ship]
def new_frame_seq(self):
return iter(range(self.t.size))
def _init_draw(self):
lines = [self.phi_time]
for l in lines:
l.set_data([], [])
anim_roll = RollDecayAnimation(ts=ts, phis=phis)
plt.close()
plt.rcParams["animation.html"] = "jshtml"
HTML(anim_roll.to_jshtml())
This is however not a damped motions as the motion does not decay. In order to get the decay one must also include a damping term $B$: $$ A\cdot\ddot{\phi} + B\cdot\dot{\phi} + C\cdot\phi = 0 $$
B=0.2
phi1d=0.0 # initial velocity
phi=np.deg2rad(10) # initial roll angle
phis=[]
for n in range(len(ts)):
phi2d = (-C*phi - B*phi1d)/A
phi1d = phi1d + phi2d*dt
phi = phi + phi1d*dt
phis.append(phi)
phis = np.array(phis)
fig,ax=plt.subplots()
ax.plot(ts,np.rad2deg(phis));
ax.set_xlabel('time [s]')
ax.set_ylabel('phi [deg]');
ax.grid(True)
#collapse
anim_rolldecay = RollDecayAnimation(ts=ts, phis=phis)
plt.close()
plt.rcParams["animation.html"] = "jshtml"
HTML(anim_rolldecay.to_jshtml())