-1

I am making a live plotter to show the analog changes from an Arduino Sensor. The Arduino prints a value to the serial with a Baudrate of 9600. The Python code looks as following:

import matplotlib.pyplot as plt
import matplotlib.animation as animation
import serial
import time

ser = serial.Serial("com3", 9600)
ser.readline()

optimal_frequency = 100

fig = plt.figure(figsize=(6, 6))
ax1 = fig.add_subplot(1, 1, 1)

# the following arrays must be initialized outside the loop

xar = []
yar = []

print(time.ctime())

def animate(i):
    global b, xar, yar # otherwise a

    for i in range(optimal_frequency):

        a = str(ser.readline(), 'utf-8')
        try:
            b = float(a)
        except ValueError:
            ser.readline()
        xar.append(str(time.time()))
        yar.append(b)
    ax1.clear()
    ax1.plot(xar, yar)

ani = animation.FuncAnimation(fig, animate, interval=optimal_frequency)
plt.show()

A get an ok response time in the plot, but when I have been plotting over 20 minutes the reaction times increase to about 1 min. I.e. it takes 1 min forthe graph to get updated with the new values. I have also tried with PyQtGraph but that is delayed from a start.

Besides the delay for times over 20 minutes, I am getting some overshoots and undershoots in the plot.

Any help?

4
  • I think that the problem is that xar and yar variables get huge after some time. You may try using List comprehensions and perform a dynamic read of the list instead. Commented Dec 19, 2016 at 10:05
  • After 20 minutes you might as well stop plotting since your plot with more than 10 million connected dots will have become completely unreadable. Commented Dec 19, 2016 at 10:38
  • @Jalo thanks. I see the variables get too big. The problem is that I need to re-plot all of the values again, since I am clearing the plot everytime to not to overload its memory. I'll go down in sampling frequency :-) Commented Dec 19, 2016 at 11:35
  • What you are trying to do manages quite too data to dynamically plot all of the historic data at every iteration. For a simpler approach, I would store received data at every iteration in a file for further analysis and meanwhile plot only the last N iterations. Commented Dec 19, 2016 at 11:57

2 Answers 2

1

as mentioned in the comments, you are doing two things wrong:

  • you keep appending incoming data to your arrays, which get huge after a while
  • you clear your axes and create a new ax.plot() at every iteration.

what you want to do instead is:

in an initialization function, create an empty Line2D object

def init():
    line, = ax.plot([], [], lw=2)
    return line,

then in your update function (animate()), you use line.set_data() to update the coordinates of your points. However, to maintain performance, you have to keep the size of your arrays to a reasonable size, and so you will have to drop the older data as newer data comes in

def animate(i):
    (...)
    xar.append(str(time.time()))
    yar.append(b)
    line.set_data(xar, yar)
    return line,

Check out some tutorials like this one. There are also plenty of questions on SO that already answers your question. For instance, this answer contains all the elements you need to get your code working.

Sign up to request clarification or add additional context in comments.

Comments

0

As the comments refered to, reading from the arrays gets naturally slow due to their great size. With a baudrate of 9600 I get around 130 serial readings and time-stamps per second in the xar and yar variables; which after 20 min get a size of 156000. Reducing the baudrate to 2400 decreases the length of the variables to 36000, and that can be easily replotted.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.