admin管理员组

文章数量:1415697

I try to display sensor data using set_xdata/set_ydata instead of plot so it refreshes faster

I found this tutorial

Problem: nothing shows up

Expected: I should see the plotted data (used to work with ax1.plot)

import pprint
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import time
from matplotlib.widgets import Button  # Import Button

fig,ax1 = plt.subplots(figsize=(10, 8))

# Initialize the plot lines with some dummy data
line1, = ax1.plot([0], [0], label="LEFT", color="gray")
line2, = ax1.plot([0], [0], label="RIGHT", color="gray")
line3, = ax1.plot([0], [0], label="LEFT smoothed", color="red")
line4, = ax1.plot([0], [0], label="RIGHT smoothed", color="green")

plt.title("Phil VL53L0X sensors", fontsize=20)
 
# setting x-axis label and y-axis label
plt.xlabel("time")
plt.ylabel("distance")

xarr = []
yarr1 = []
yarr2 = []
yarr1_s = []
yarr2_s = []
count = 0

WINDOW_SIZE = 10
MIN_DISTANCE = 20

# Create the button to clear data
def clear_data(event):
    global xarr, yarr1, yarr2, count, yarr1_s, yarr2_s
    xarr.clear()
    yarr1.clear()
    yarr2.clear()
    yarr1_s.clear()
    yarr2_s.clear()
    count = 0
    ax1.clear()
    ax1.set_xlabel("Time")
    ax1.set_ylabel("Distance (mm)")
    ax1.legend()
    plt.draw()

def animate(i):
    global count

    # faking data, this is usually read by a sensor returning values between 0 and 100
    distance1 = count
    distance2 = count

    distance1 = distance1 - MIN_DISTANCE
    if distance1 < 0:
        distance1 = 0
    distance2 = distance2 - MIN_DISTANCE
    if distance2 < 0:
        distance2 = 0

    count += 1
    
    # Append new data
    xarr.append(count)
    yarr1.append(distance1)
    yarr2.append(distance2)
    
    # Apply moving average
    smoothed_distance1 = moving_average(yarr1)
    smoothed_distance2 = moving_average(yarr2)

    yarr1_s.append(smoothed_distance1)
    yarr2_s.append(smoothed_distance2)
    
    # Limit size of arrays to avoid excessive memory usage
    if len(yarr1) > 100:
        yarr1.pop(0)
        yarr2.pop(0)
        yarr1_s.pop(0)
        yarr2_s.pop(0)
        xarr.pop(0)
        
    pprint.pprint(xarr)
    pprint.pprint(yarr1)    

    # Update the X data (only once)
    line1.set_xdata(xarr)
    line2.set_xdata(xarr)
    line3.set_xdata(xarr)
    line4.set_xdata(xarr)

    # Update the plot (without clearing it)
    line1.set_ydata(yarr1)
    line2.set_ydata(yarr2)
    line3.set_ydata(yarr1_s)
    line4.set_ydata(yarr2_s)

    # Update the plot (this works)
    #ax1.plot(xarr, yarr1, label="LEFT", color="gray")
    #ax1.plot(xarr, yarr2, label="RIGHT", color="gray")
    #ax1.plot(xarr, yarr1_s, label="LEFT s", color="red")
    #ax1.plot(xarr, yarr2_s, label="RIGHT s", color="green")

    fig.canvas.draw()
    fig.canvas.flush_events()
    time.sleep(0.1)


ani = animation.FuncAnimation(fig, animate, interval=1)  # Increased interval for smoother updates

# Add a button to clear data
ax_button = plt.axes([0.8, 0.05, 0.1, 0.075])
btn = Button(ax_button, "Clear Data")
btn.on_clicked(clear_data)

plt.show()

I pprint the data as it animates, it is clearly there.

What am I missing here?

I try to display sensor data using set_xdata/set_ydata instead of plot so it refreshes faster

I found this tutorial

Problem: nothing shows up

Expected: I should see the plotted data (used to work with ax1.plot)

import pprint
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import time
from matplotlib.widgets import Button  # Import Button

fig,ax1 = plt.subplots(figsize=(10, 8))

# Initialize the plot lines with some dummy data
line1, = ax1.plot([0], [0], label="LEFT", color="gray")
line2, = ax1.plot([0], [0], label="RIGHT", color="gray")
line3, = ax1.plot([0], [0], label="LEFT smoothed", color="red")
line4, = ax1.plot([0], [0], label="RIGHT smoothed", color="green")

plt.title("Phil VL53L0X sensors", fontsize=20)
 
# setting x-axis label and y-axis label
plt.xlabel("time")
plt.ylabel("distance")

xarr = []
yarr1 = []
yarr2 = []
yarr1_s = []
yarr2_s = []
count = 0

WINDOW_SIZE = 10
MIN_DISTANCE = 20

# Create the button to clear data
def clear_data(event):
    global xarr, yarr1, yarr2, count, yarr1_s, yarr2_s
    xarr.clear()
    yarr1.clear()
    yarr2.clear()
    yarr1_s.clear()
    yarr2_s.clear()
    count = 0
    ax1.clear()
    ax1.set_xlabel("Time")
    ax1.set_ylabel("Distance (mm)")
    ax1.legend()
    plt.draw()

def animate(i):
    global count

    # faking data, this is usually read by a sensor returning values between 0 and 100
    distance1 = count
    distance2 = count

    distance1 = distance1 - MIN_DISTANCE
    if distance1 < 0:
        distance1 = 0
    distance2 = distance2 - MIN_DISTANCE
    if distance2 < 0:
        distance2 = 0

    count += 1
    
    # Append new data
    xarr.append(count)
    yarr1.append(distance1)
    yarr2.append(distance2)
    
    # Apply moving average
    smoothed_distance1 = moving_average(yarr1)
    smoothed_distance2 = moving_average(yarr2)

    yarr1_s.append(smoothed_distance1)
    yarr2_s.append(smoothed_distance2)
    
    # Limit size of arrays to avoid excessive memory usage
    if len(yarr1) > 100:
        yarr1.pop(0)
        yarr2.pop(0)
        yarr1_s.pop(0)
        yarr2_s.pop(0)
        xarr.pop(0)
        
    pprint.pprint(xarr)
    pprint.pprint(yarr1)    

    # Update the X data (only once)
    line1.set_xdata(xarr)
    line2.set_xdata(xarr)
    line3.set_xdata(xarr)
    line4.set_xdata(xarr)

    # Update the plot (without clearing it)
    line1.set_ydata(yarr1)
    line2.set_ydata(yarr2)
    line3.set_ydata(yarr1_s)
    line4.set_ydata(yarr2_s)

    # Update the plot (this works)
    #ax1.plot(xarr, yarr1, label="LEFT", color="gray")
    #ax1.plot(xarr, yarr2, label="RIGHT", color="gray")
    #ax1.plot(xarr, yarr1_s, label="LEFT s", color="red")
    #ax1.plot(xarr, yarr2_s, label="RIGHT s", color="green")

    fig.canvas.draw()
    fig.canvas.flush_events()
    time.sleep(0.1)


ani = animation.FuncAnimation(fig, animate, interval=1)  # Increased interval for smoother updates

# Add a button to clear data
ax_button = plt.axes([0.8, 0.05, 0.1, 0.075])
btn = Button(ax_button, "Clear Data")
btn.on_clicked(clear_data)

plt.show()

I pprint the data as it animates, it is clearly there.

What am I missing here?

Share Improve this question edited Feb 5 at 9:28 DavidG 25.4k14 gold badges100 silver badges86 bronze badges asked Feb 4 at 18:47 philphil 527 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 0

I think the main problem is that you need to make sure that the axis limits are set such that they include your data. Another issue is using:

fig.canvas.draw()
fig.canvas.flush_events()
time.sleep(0.1)

in your animate function while also using the FuncAnimation class - you should do one or the other (see the FuncAnimation examples here).

An working example (simplified further from your example) would be:

import matplotlib.pyplot as plt
import matplotlib.animation as animation
import time
from matplotlib.widgets import Button  # Import Button

fig,ax1 = plt.subplots(figsize=(10, 8))

# Initialize the plot lines with some dummy data
line1, = ax1.plot([], [], label="LEFT", color="C0")
line2, = ax1.plot([], [], label="RIGHT", color="C1")

plt.title("Phil VL53L0X sensors", fontsize=20)

# setting x-axis label and y-axis label
plt.xlabel("time")
plt.ylabel("distance")

xarr = []
yarr1 = []
yarr2 = []
count = 0

WINDOW_SIZE = 10
MIN_DISTANCE = -10

# set initial x and y bounds of the plot
INI_X_BOUNDS = [-10, 10]
INI_Y_BOUNDS = [0, 10]

ax1.set_xlim(INI_X_BOUNDS)
ax1.set_ylim(INI_Y_BOUNDS)

# Create the button to clear data
def clear_data(event):
    global xarr, yarr1, yarr2, count
    xarr.clear()
    yarr1.clear()
    yarr2.clear()
    count = 0

    ax1.set_xlim(INI_X_BOUNDS)
    ax1.set_ylim(INI_Y_BOUNDS)


def animate(i):
    global count

    # faking data, this is usually read by a sensor returning values between 0 and 100
    distance1 = count
    distance2 = count / 2

    distance1 = distance1 - MIN_DISTANCE
    if distance1 < 0:
        distance1 = 0
    distance2 = distance2 - MIN_DISTANCE
    if distance2 < 0:
        distance2 = 0

    # Append new data
    xarr.append(count)
    yarr1.append(distance1)
    yarr2.append(distance2)

    # Limit size of arrays to avoid excessive memory usage
    if len(yarr1) > 100:
        yarr1.pop(0)
        yarr2.pop(0)
        xarr.pop(0)

    # Update the X data (only once)
    line1.set_data(xarr, yarr1)
    line2.set_data(xarr, yarr2)

    # update the upper limit on the x-y axis ranges
    # play around with this as you see fit
    xlims = ax1.get_xlim()
    ylims = ax1.get_ylim()

    if xarr[-1] > xlims[-1]:
        # double x limit
        ax1.set_xbound(upper=xlims[-1] * 2)

    if max(yarr1[-1], yarr2[-1]) > ylims[-1]:
        # double y limit
        ax1.set_ybound(upper=ylims[-1] * 2)

    count += 1

# using frames=None will give infinite running
ani = animation.FuncAnimation(fig, animate, frames=None, interval=0.05)

# Add a button to clear data
ax_button = plt.axes([0.8, 0.05, 0.1, 0.075])
btn = Button(ax_button, "Clear Data")
btn.on_clicked(clear_data)

plt.show()

The main things this does is:

  • set the initial axes x-y bounds
  • update the upper x-y bounds if the data gets out the current range (play about with this as you require)
  • remove the updating of the fig.canvas from within the animate function
  • to simplify things, it just uses set_data rather than set_xdata and set_ydata separately.

本文标签: Python 3matplotlibsetxdatasetydata not workingStack Overflow