I am trying to plot an animation of a simple pendulum
using the model https://matplotlib.org/gallery/animation/double_pendulum_sgskip.html.
My code is as follows :
from numpy import sin, cos
import numpy as np
import matplotlib.pyplot as plt
import scipy.integrate as integrate
import matplotlib.animation as animation
#some constants
g = 9.81
l = 0.1
m = 0.01
def sh(r,t):
theta = r[0]
omega = r[1]
sh_theta = omega
sh_omega = -g/l*sin(theta)
return np.array([sh_theta,sh_omega],float)
init_state = [np.radians(89.0),0]
time = np.arange(0,50.0,0.025)
time_elapsed = time
def step_solver(eq, ist, dt):
"""
Execute one time step of length dt and update status
"""
global time_elapsed
state1,state2 = integrate.odeint(eq,ist,[0,dt])
time_elapsed += dt
return state1, state2,time_elapsed
dt = 1/30
ysol,ysolv,timex = step_solver(sh,init_state,dt)
print("This is the y0 values: ", ysol,"y values",ysolv,"This is the time ", timex)
##===================================
##Setting up figure and animation
#======================================
fig = plt.figure()
ax = plt.axes(xlim = (0,2), ylim = (-2,2))
line, = ax.plot([],[],lw=2)
#time_text = ax.text(0.02,0.95,'',transform = ax.transAxes)
#==========================================
##initialisation function: plot the background of each frame
def init():
line.set_data([],[])
#time_text.set_text('')
return line,
def animate(ysol,timex):
x = timex
y = ysol
line.set_data(x,y)
#time_text.set_text(str(i))
return line,
#================================================
#Call the animator
#================================================
anim = animation.FuncAnimation(fig,animate(ysolv,timex), init_func = init, frames = 200, interval =20, blit = True)
anim.save('basic_animation.mp4', fps=30, extra_args=['-vcodec', 'libx264'])
plt.show()
EDIT :
I am getting the error message :
ValueError: shape mismatch: objects cannot be broadcast to a single shape
<Figure size 432x288 with 1 Axes>
I have checked what my function step_solver is printing with
print(len(ysol),len(ysolv),len(timex))
and after recommendation the other y output for odeint which gave only 2 values for my ysol and ysolv variable for 2200 time values.
I was expecting to get a range of y values for each time step.
I am not sure how to sort this. Is my function step_solver wrongly coded?
Why am I only getting 2 values, how can I animate the solution in the same way it was done for the double pendulum?
Any suggestions on where the problem may lie?
Many thanks in advance.
Let's follow the code from the example more closely. Which means to use just one integration call and then using slices of the result of that call in the animation.
from numpy import sin, cos
import numpy as np
import matplotlib.pyplot as plt
import scipy.integrate as integrate
import matplotlib.animation as animation
#some constants
g = 9.81
l = 0.1
m = 0.01
def sh(r,t):
theta, omega = r
sh_theta = omega
sh_omega = -g/l*sin(theta)
return np.array([sh_theta,sh_omega],float)
init_state = np.radians([89.0,0])
dt = 1.0/30
time = np.arange(0,50.0,dt)
state = integrate.odeint(sh,init_state,time)
##===================================
##Setting up figure and animation
#======================================
fig = plt.figure()
ax = plt.axes(xlim = (0,10), ylim = (-2,2))
line, = ax.plot([],[],lw=2)
#==========================================
##initialisation function: plot the background of each frame
def init():
return line,
def animate(i):
x = time[i:i+30]
y = state[i:i+30,0]
line.set_data(x,y)
return line,
#================================================
#Call the animator
#================================================
anim = animation.FuncAnimation(fig,animate, init_func = init, frames = 200, interval =20, blit = True)
plt.show()
For a variant using a step generator using yield see my answer in Error in RK4 algorithm in Python. This allows to encapsulate the data for the integration loop without defining a class for it. However it is not clear how a function graph with two samples would be helpful, even if it is animated.
To animate the pendulum itself, use
##===================================
##Setting up figure and animation
#======================================
fig = plt.figure(figsize=(8,6))
ax = plt.axes(xlim = (-2*l,2*l), ylim = (-2*l,l))
line, = ax.plot([],[],'-o',lw=2,ms=8)
#==========================================
##initialisation function: plot the background of each frame
def init():
return line,
def animate(i):
phi = state[i,0]
line.set_data([0,l*sin(phi)],[0,-l*cos(phi)])
return line,
#================================================
#Call the animator
#================================================
anim = animation.FuncAnimation(fig,animate, init_func = init, frames = len(time), interval =100, blit = True)
plt.show()
Related
I have the following code which creates a graph animation. The graph should start from 0, but the 1st interval graph isn't coming.
Below is the code:
import matplotlib.pylab as plt
import matplotlib.animation as animation
import numpy as np
fig, ax = plt.subplots()
left = -1
right = 2*np.pi - 1
def animate(i):
global left, right
left = left + 1
right = right + 1
x = np.linspace(left, right, 50)
y = np.cos(x)
ax.cla()
ax.set_xlim(left, right)
ax.plot(x, y, lw=2)
ani = animation.FuncAnimation(fig, animate, interval = 1000)
plt.show()
For the 1st interval [0, 2π] the graph isn't coming.
What's the mistake?
I changed a little bit your code:
first of all I plot the first frame outside the animate function and I generate a line object from it
then I update the line data within animate function
I suggest to use i counter (which starts from 0 and increases by 1 in each frame) to update your data, in place of calling global variables and change them
Complete Code
import matplotlib.pylab as plt
import matplotlib.animation as animation
import numpy as np
fig, ax = plt.subplots()
left = 0
right = 2*np.pi
x = np.linspace(left, right, 50)
y = np.cos(x)
line, = ax.plot(x, y)
ax.set_xlim(left, right)
def animate(i):
x = np.linspace(left + i, right + i, 50)
y = np.cos(x)
line.set_data(x, y)
ax.set_xlim(left + i, right + i)
return line,
ani = animation.FuncAnimation(fig = fig, func = animate, interval = 1000)
plt.show()
I am trying to create a 3D animation scatter plot where each point is plotted as a sphere with radius of r proportional to value M (please see the code below), I guess it should be done by using argument s in ax.scatter, but since this value is unique for each (x,y,z), I don't know how to pass that to graph._offsets3d which accepts (x,y,z) touple. This is the first part of the task, the other part is that the data should appear at their specific time t (please see the code below).
I am currently struggling to change the size of each point according to their corresponding value in M, and color code the point with its corresponding time t, do you know how could I do this?
It would my next task to add a play/pause button to the figure and be able to rotate the the graph?
Does anyone have similar experiences that I could benefit from?
Many thanks!
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.animation as animation
#####Data Generation####
# Space Coordinate
X = np.random.random((100,)) * 255 * 2 - 255
Y = np.random.random((100,)) * 255 * 2 - 255
Z = np.random.random((100,)) * 255 * 2 - 255
# Magnitude of each point
M = np.random.random((100,))*-1+0.5
# Time
t = np.sort(np.random.random((100,))*10)
#ID each point should be color coded. Moreover, each point belongs to a cluster `ID`
ID = np.sort(np.round([np.random.random((100,))*5]))
def update_lines(num):
for i in range (df_IS["EASTING [m]"].size):
dx = X[i]
dy = Y[i]
dz = Z[i]
text.set_text("{:d}: [{:.0f}] Mw[{:.2f}]".format(ID[i], t[i],ID[i])) # for debugging
x.append(dx)
y.append(dy)
z.append(dz)
graph._offsets3d = (x, y, z)
return graph,
fig = plt.figure(figsize=(5, 5))
ax = fig.add_subplot(111, projection="3d")
graph = ax.scatter(X, Y, Z, color='orange') # s argument here
text = fig.text(0, 1, "TEXT", va='top') # for debugging
ax.set_xlim3d(X.min(), X.max())
ax.set_ylim3d(Y.min(), Y.max())
ax.set_zlim3d(Z.min(),Z.max())
# Creating the Animation object
ani = animation.FuncAnimation(fig, update_lines, frames=200, interval=500, blit=False)
plt.show()
In the animation function was looped by the size of the data frame, but rewrote your code partly because the animation argument is linked to the number of frames. Please correct me if I'm wrong. You can also pass in the size with graph.set_sizes(), which you can specify there. Your size variable had a negative value, so I'm recreating it as an integer. I've used a separate library in part because of my working environment.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.animation as animation
from IPython.display import HTML # Animation on jupyter lab
from matplotlib.animation import PillowWriter # For GIF animation
#####Data Generation####
# Space Coordinate
X = np.random.random((100,)) * 255 * 2 - 255
Y = np.random.random((100,)) * 255 * 2 - 255
Z = np.random.random((100,)) * 255 * 2 - 255
# Magnitude of each point
# M = np.random.random((100,))*-1+0.5
M = np.random.randint(1,70, size=100)
# Time
t = np.sort(np.random.random((100,))*10)
#ID each point should be color coded. Moreover, each point belongs to a cluster `ID`
ID = np.sort(np.round([np.random.random((100,))*5]))
x = []
y = []
z = []
m = []
def update_lines(i):
# for i in range (df_IS["EASTING [m]"].size):
dx = X[i]
dy = Y[i]
dz = Z[i]
dm = M[i]
# text.set_text("{:d}: [{:.0f}] Mw[{:.2f}]".format(ID[i], t[i],ID[i])) # for debugging
x.append(dx)
y.append(dy)
z.append(dz)
m.append(dm)
graph._offsets3d = (x, y, z)
graph.set_sizes(m)
return graph,
fig = plt.figure(figsize=(5, 5))
ax = fig.add_subplot(111, projection="3d")
graph = ax.scatter(X, Y, Z, s=M, color='orange') # s argument here
text = fig.text(0, 1, "TEXT", va='top') # for debugging
ax.set_xlim3d(X.min(), X.max())
ax.set_ylim3d(Y.min(), Y.max())
ax.set_zlim3d(Z.min(), Z.max())
# Creating the Animation object
ani = animation.FuncAnimation(fig, update_lines, frames=100, interval=500, blit=False, repeat=False)
# plt.show()
ani.save('test3Dscatter.gif', writer='pillow')
plt.close()
HTML(ani.to_html5_video())
Edit:
# Time
t = np.sort(np.random.random((100,))*10)
# datapoint for color
cm_name = 'jet'
cm = plt.get_cmap(cm_name, 100)
C = [cm(n) for n in range(cm.N)]
# list for colors add
x = []
y = []
z = []
m = []
c = []
# animation function update
dm = M[i]
dc = C[i] # update
m.append(dm)
c.append(dc) # update
graph._facecolor3d = c # scatter color defined
return graph,
The problem I am simulating is a simple pendulum. While I have done it before using PyGame I now decided to use matplotlib's animation tools. It is working but not with the desired effect. Simulating it in real time seems to be working. I have tweeked the interval and amount of frames but the fps is way too low. How do you increase the fps while still playing it in real time. I would greatly appreciate it. Anyway here is my code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
g = 9.80665
L = 2
mu = 0.1
t = 100
theta_0 = np.pi/3
d_theta_0 = 0
def get_d2_theta(theta,d_theta):
return -mu*d_theta-(g/L)*np.sin(theta)
def theta(t):
theta = theta_0
d_theta = d_theta_0
delta_t = 1./60
for time in np.arange(0,t,delta_t):
d2_theta = get_d2_theta(theta,d_theta)
theta += d_theta*delta_t
d_theta += d2_theta*delta_t
return theta
x_data = [0,0]
y_data = [0,0]
fig, ax = plt.subplots()
ax.set_xlim(-2, 2)
ax.set_ylim(-2.5,1)
line, = ax.plot(0, 0)
def animation_frame(i):
x = L*np.sin(theta(i))
y = -L*np.cos(theta(i))
x_data[1] = x
y_data[1] = y
line.set_xdata(x_data)
line.set_ydata(y_data)
return line,
animation = FuncAnimation(fig, func=animation_frame, frames=np.arange(0, 60, (1./60)),interval = 10)
plt.show()
The problem
I need the animation to go fluid, but it is ploting frame by frame. The code is running in Jupyter Notebook.
Here are the libraries
import numpy as np
from matplotlib import pyplot as plt
from scipy import signal as sp
Creating the functions to convolve
t_ini=0
t_final = 11
dt=0.1
t = np.arange(t_ini,t_final,dt)
expo = np.exp(-t)*np.piecewise(t,t>=0,[1,0])
t1 = np.arange(0,10,0.1)
s = np.sin(t1)
conv_=sp.convolve(s,expo,'full')
n_conv=np.arange(min(t1)+min(t),max(t1)+max(t)+0.1,0.1)
y = [0] * len(conv_)
t2 = [0] * len(n_conv)
Here is the plotting
i = 0
for x in n_conv:
y[i] = conv_[i]
plt.cla()
t2[i] = n_conv[i]
plt.plot(t2,y)
plt.show()
plt.pause(0.5)
i = i+1
matplotlib provides for instance ArtistAnimation that allows a seamless animation of precalculated graphs. I just added a couple of lines to your code. Only thing I changed was to use enumerate to improve your code
import numpy as np
from matplotlib import pyplot as plt
from scipy import signal as sp
import matplotlib.animation as anim
t_ini=0
t_final = 11
dt=0.1
t = np.arange(t_ini,t_final,dt)
expo = np.exp(-t)*np.piecewise(t,t>=0,[1,0])
t1 = np.arange(0,10,0.1)
s = np.sin(t1)
conv_=sp.convolve(s,expo,'full')
n_conv=np.arange(min(t1)+min(t),max(t1)+max(t)+0.1,0.1)
y = [0] * len(conv_)
t2 = [0] * len(n_conv)
#prepare figure for display
fig = plt.figure()
ax = plt.axes()
#create list to collect graphs for animation
img = []
for i, x in enumerate(n_conv):
y[i] = conv_[i]
t2[i] = n_conv[i]
#append new graphs to list
newpic, = ax.plot(t2, y, c= "blue")
img.append([newpic])
#animate the list of precalculated graphs
ani = anim.ArtistAnimation(fig, img, interval = 50)
plt.show()
Output:
I'm coding a function which would animate a random walk in 3D but unfortunately the code isn't working. Where is a plot, no errors occur but nothing happens. I'm using %matplotlib tk.
There is my code:
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d.axes3d as p3
import matplotlib.animation as animation
def path_generator(steps, step):
path = np.empty((3, steps))
for i in range(1, steps):
x_ran, y_ran, z_ran = np.random.rand(3)
sgnX = (x_ran - 0.5)/abs(x_ran - 0.5)
sgnY = (y_ran - 0.5)/abs(y_ran - 0.5)
sgnZ = (z_ran - 0.5)/abs(z_ran - 0.5)
dis = np.array([step*sgnX, step*sgnY, step*sgnZ])
path[:, i] = path[:, i - 1] + dis
return path
def animate(i):
global particles, trajectories
for trajectory, particle in zip(trajectories, particles):
trajectory.set_data(particle[0:2, :i])
trajectory.set_3d_properties(particle[2, :i])
return trajectories
def random_walk_3D_animated(n, traj = 1):
fig = plt.figure()
ax = p3.Axes3D(fig)
particles = [path_generator(n, 1) for i in range(traj)]
trajectories = [ax.plot(particle[0, 0:1], particle[1, 0:1], particle[2,
0:1])[0] for particle in particles]
ax.set_xlim3d([-100, 100])
ax.set_ylim3d([-100, 100])
ax.set_zlim3d([-100, 100])
animacion = animation.FuncAnimation(fig, animate, 1000, interval=50,
blit=False)
plt.show()
What is strange, the code do work when there is no function random_walk_3D_animated(n, traj = 1) and the values n and traj are given. And sometimes the code doesn't start the random walks from (0,0,0). I wonder why.
The start position will be the content of the emty array. This may be any value so it is not really useful here. Instead initialize path with zeros.
You need to return a reference to the animation. From the animation documentation: "[..] it is critical to keep a reference to the instance object."
Complete example:
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d.axes3d as p3
import matplotlib.animation as animation
def path_generator(steps, step):
path = np.zeros((3, steps))
for i in range(1, steps):
x_ran, y_ran, z_ran = np.random.rand(3)
sgnX = (x_ran - 0.5)/abs(x_ran - 0.5)
sgnY = (y_ran - 0.5)/abs(y_ran - 0.5)
sgnZ = (z_ran - 0.5)/abs(z_ran - 0.5)
dis = np.array([step*sgnX, step*sgnY, step*sgnZ])
path[:, i] = path[:, i - 1] + dis
return path
def animate(i):
global particles, trajectories
for trajectory, particle in zip(trajectories, particles):
trajectory.set_data(particle[0:2, :i])
trajectory.set_3d_properties(particle[2, :i])
def random_walk_3D_animated(n, traj = 1):
global particles, trajectories
fig = plt.figure()
ax = p3.Axes3D(fig)
particles = [path_generator(n, 1) for i in range(traj)]
trajectories = [ax.plot(particle[0, 0:1], particle[1, 0:1], particle[2,
0:1])[0] for particle in particles]
ax.set_xlim3d([-100, 100])
ax.set_ylim3d([-100, 100])
ax.set_zlim3d([-100, 100])
animacion = animation.FuncAnimation(fig, animate, 1000, interval=50,
blit=False)
return animacion
ani = random_walk_3D_animated(100, traj = 1)
plt.show()