Below is a code, fully working, that animates a one-lane road with periodic boundaries. Now I'd like to animate two, or even more lanes. I have a working code for that where the position vector now is a matrix, each column representing a lane. From that, I can create another theta vector which I'd like to be shown outside of the first one in the animation (by setting the radius a bit bigger). I have tried to put matrices (theta and r) in the ax.scatter(theta, r, c=color), but that does not work. Does anyone have a suggestion on how to approach this problem? I could use something else than matplotlib's animation, but as it worked fine for the one-lane problem it would be easiest for me.
To wrap it up. How can I animate two or more vectors at the same time? For example, if I have r1, r2 and theta1, theta2 and want to 'plot' them both at each time, instead of just r and theta as in the code.
Thanks a lot for any help.
import numpy as np
from numpy.random import random as rnd
import matplotlib.pyplot as plt
from matplotlib import animation
roadlength = 50
numcars = 10
numframes = 1000 #Time
v_max = 5
p = 0.5
positions = np.zeros(numcars)
velocities = np.zeros(numcars)
theta = np.zeros(numcars)
color = np.linspace(0,numcars-1,numcars)
#Initiate r so roadlength = circumference of one lap
r = []
for i in range(numcars):
r.append(roadlength/(2*np.pi))
#Initiate positions so the cars are spread out over the road
for i in range(1,numcars):
positions[i] = positions[i-1] + (roadlength/numcars)
#Create figure
fig = plt.figure()
ax = fig.add_subplot(111, projection='polar')
ax.axis('off')
#Update positions, animate function runs framenr times
def animate(framenr):
positions_tmp = np.array(positions, copy=True)
#Update position and velocity for each car
for i in range(numcars):
#Increase velocity if below max
if velocities[i] < v_max:
velocities[i] += 1
#Decrease velocity if car in front is close
d = positions_tmp[(i+1)%numcars] - positions_tmp[i]
if d <= 0:
d += roadlength
if velocities[i] >= d:
velocities[i] = d-1
#Decrease velocity randomly
if velocities[i] > 0:
if rnd() < p:
velocities[i] -= 1
positions[i] = positions_tmp[i] + velocities[i]
theta[i] = positions[i]*2*np.pi/roadlength
return ax.scatter(theta, r, c=color),
# Call the animator, blit=True means only re-draw parts that have changed
anim = animation.FuncAnimation(fig, animate, frames=numframes, interval=100, blit=True, repeat=False)
plt.show()
FuncAnimation()
expects the animate()
function to return an array of artists that have been updated at each frame. If you need to animate several "plots" (or artists, or whatever), simply store the result of each plot, and return a list of all the variables
In addition, your code is calling ax.scatter()
repeatedly in the animate
function, which causes new points to be plotted everytime and the time it takes to draw the frame to increase over time. I don't know if that's really what you really wanted to do. The best practice for animations is to create the artist(s) before the start of the animation, and then update their properties inside the animate()
function rather than create new ones.
import numpy as np
from numpy.random import random as rnd
import matplotlib.pyplot as plt
from matplotlib import animation
roadlength = 50
numcars = 10
numframes = 1000 #Time
v_max = 5
p = 0.5
positions = np.zeros(numcars)
velocities = np.zeros(numcars)
theta = np.zeros(numcars)
color = np.linspace(0,numcars-1,numcars)
#Initiate r so roadlength = circumference of one lap
r = roadlength/(2*np.pi)
#Initiate positions so the cars are spread out over the road
for i in range(1,numcars):
positions[i] = positions[i-1] + (roadlength/numcars)
#Create figure
fig = plt.figure()
ax = fig.add_subplot(111, projection='polar')
ax.axis('off')
plot1 = ax.scatter(theta, [r]*numcars, c=color)
plot2 = ax.scatter(theta, [r+1]*numcars, c=color)
ax.set_ylim(0,r+2)
#Update positions, animate function runs framenr times
def animate(framenr):
positions_tmp = np.array(positions, copy=True)
#Update position and velocity for each car
for i in range(numcars):
#Increase velocity if below max
if velocities[i] < v_max:
velocities[i] += 1
#Decrease velocity if car in front is close
d = positions_tmp[(i+1)%numcars] - positions_tmp[i]
if d <= 0:
d += roadlength
if velocities[i] >= d:
velocities[i] = d-1
#Decrease velocity randomly
if velocities[i] > 0:
if rnd() < p:
velocities[i] -= 1
positions[i] = positions_tmp[i] + velocities[i]
theta[i] = positions[i]*2*np.pi/roadlength
plot1.set_offsets(np.c_[theta, [r]*numcars])
plot2.set_offsets(np.c_[theta, [r+1]*numcars])
return [plot1, plot2]
# Call the animator, blit=True means only re-draw parts that have changed
anim = animation.FuncAnimation(fig, animate, frames=numframes, interval=100, blit=True, repeat=True)
plt.show()