In [1]:
from plotly.offline import init_notebook_mode, iplot # from plotly.offline import init_notebook_mode, iplot #
from plotly.graph_objs import Scatter, Figure, Layout
init_notebook_mode(connected=True)
from IPython.display import display, HTML
In [2]:
import numpy as np
from scipy.integrate import odeint
In [3]:
x = list(range(0,10))
y = [c*c for c in x]
iplot([{"x": x, "y": y}])
In [4]:
# Equations of motion
def get_x_dotdot(x, x_dot, u):
m = 10
b = 1*0
k = 1
x_dotdot = (u-k*x-b*x_dot)/m
return x_dotdot
# ODE
def get_s_dot(y,t,u):
x, x_dot = y
x_dotdot = get_x_dotdot(x,x_dot, u)
s_dot = [x_dot, x_dotdot]
return s_dot
# PD controller
def get_u(x,x_dot):
kp = 10
kd = 6
u = -kp*x-kd*x_dot
return u
In [5]:
# Initial Conditions
s0 = [3, 0]
nStates = 2
# Initialize empty result vectors
t_all = np.empty(shape=[0, 1])
s_all = np.empty(shape=[0, 2])
end = -1
# Time settings
dt = 0.5 # Controller sampling time
t_end = 10 # Simulation time
Tdis = np.arange(0,t_end,dt) # Discrete time samples
# At every sample do
for tnow in Tdis:
# Given the current state, compute a control signal
x,x_dot = s0
u = get_u(x,x_dot)
# Integrate equation of motion until next sample while holding control
simsteps_per_sample = 10
t = np.linspace(tnow,tnow+dt,simsteps_per_sample+1)
s = odeint(get_s_dot, s0, t, args=(u,))
# The final y state is the initial state for next time
s0 = s[end,:]
# Append the results, except the final state. That will be appended as the initial state of the next one
t_all = np.append(t_all,t[0:end])
s_all = np.append(s_all,s[0:end,:], axis = 0)
# Match time and output signals for plotting
pos_plot = Scatter(x = t_all, y = s_all[:, 0], name = 'position')
vel_plot = Scatter(x = t_all, y = s_all[:, 1], name = 'speed')
# Plot results inline
iplot([pos_plot,vel_plot])
In [6]:
time_scale = 1 # Amount of time we speed up the animation. 1x = real time
fps = 20 # Frames per second
tpf = 1/fps # Time per frame (miliseconds)
# Create time vector of display instants
t_display = np.arange(0,t_end,tpf)
# Real time samples corresponding to display time
t_sample = t_display*time_scale
# Interpolate state data to match the display time vector
s_display = np.stack([np.interp(t_sample,t_all,s_all[:,i]) for i in range(nStates)]).T
In [7]:
# Origin coordinate in time (always 0,0)
coordinate_pair_ori = [[0,0] for i, t in enumerate(t_display)]
# Position in time (horizontal = 0, vertical = position)
coordinate_pair_pos = [[0,s_display[i,0]] for i, t in enumerate(t_display)]
# Velocity in time (horizontal = 0, vertical = velocity)
coordinate_pair_vel = [[0,s_display[i,1]] for i, t in enumerate(t_display)]
# Put data in the frames format. At each time sample this is a set of coordinates making up the line segments
framedata = [ {'data': [{'x': coordinate_pair_ori[i], 'y': coordinate_pair_pos[i]},
{'x': coordinate_pair_ori[i], 'y': coordinate_pair_vel[i]}
]} for i, t in enumerate(t_display)]
# We'll plot the first time instant so there's something there until we start the animation
firstframe = framedata[0]['data']
# Associate a legend with the data.
firstframe[0]['name'] = 'Position'
firstframe[1]['name'] = 'Velocity'
# We can only specify the inter frame time. So we have to account for how long it approximately takes to draw a frame
overhead_ms = 7
# Create the figure object
figure = {'data': firstframe,
'layout': {'xaxis': {'range': [-1, 1], 'autorange': False},
'yaxis': {'range': [-3, 3], 'autorange': False},
'title': 'Mass Spring Damper',
'updatemenus': [{'type': 'buttons',
'buttons':
[
{'label': 'Start','method': 'animate',
'args': [framedata, {'frame': {'duration': tpf*1000 - overhead_ms, 'redraw': False}}]
},
{'label': 'Stop','method': 'animate',
'args': [[None], {'frame': {'duration': 0, 'redraw': False}, 'mode': 'immediate','transition': {'duration': 0}}]
}
]
}]
}
}
# Create an inline plot of the figure
iplot(figure)