简体   繁体   中英

How to Auto scale y and x axis of a graph in real time python

I modified the code of this tutorial to create my own real time plot:

https://learn.sparkfun.com/tutorials/graph-sensor-data-with-python-and-matplotlib/speeding-up-the-plot-animation

I needed to plot the data from a proximity sensor in real time, the data is sent through USB cable to the computer and I read it with the serial port, so the code is already working how I wanted to, but I also want to modify the y-axis and x-axis, not let it static, because sometimes the peaks are 3000 and sometimes 2000 and when the sensor is not being touched, the peaks are at around 200 because it also detects the ambient light. any clue how can I make it?

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


# Data from serial port
port = 'COM8'
baudrate = 9600
tout = 0.01  # Miliseconds

# Time to update the data of the sensor signal real time Rs=9600baud T=1/Rs
tiempo = (1 / baudrate) * 1000

# Parameters
x_len = 200         # Number of points to display
y_range = [20000, 40000]  # Range of Y values to display

# Create figure for plotting
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
xs = list(range(0, x_len))
ys = [0] * x_len
ax.set_ylim(y_range)

# Create a blank line. We will update the line in animate
line, = ax.plot(xs, ys)

# Markers
startMarker = 60  # Start marker "<"
endMarker = 62  # End marker ">"

# Begin Arduino communication, Port COM8, speed 9600
serialPort = serial.Serial(port, baudrate, timeout=tout)

# Begin to save the arduino data
def arduinodata():

    global startMarker, endMarker

    ck = ""
    x = "z"  # any value that is not an end- or startMarker
    bytecount = -1  # to allow for the fact that the last increment will be one too many

    # wait for the start character
    while ord(x) != startMarker:
        x = serialPort.read()

    # save data until the end marker is found
    while ord(x) != endMarker:
        if ord(x) != startMarker:
            ck = ck + x.decode()
            bytecount += 1
        x = serialPort.read()

    return ck


def readarduino():

    # Wait until the Arduino sends '<Arduino Ready>' - allows time for Arduino reset
    # It also ensures that any bytes left over from a previous message are discarded

    msg = ""

    while msg.find("<Arduino is ready>") == -1:

        while serialPort.inWaiting() == 0:
            pass
        # delete for example the "\r\n" that may contain the message
        msg = arduinodata()
        msg = msg.split("\r\n")
        msg = ''.join(msg)

        # If the sensor send very big numbers over 90000 they will be deleted
        if msg and len(msg) <= 5:
            msg = int(msg)
            return msg

        elif msg and len(msg) >= 4:
            msg = int(msg)
            return msg


# This function is called periodically from FuncAnimation
def animate(i, ys):

    # Read pulse from PALS2
    pulse = readarduino()

    # Add y to list
    ys.append(pulse)

    # Limit x and y lists to set number of items
    ys = ys[-x_len:]

    # Update line with new Y values
    line.set_ydata(ys)
    return line,


# Plot labels
plt.title('Heart frequency vs Time')
plt.ylabel('frequency ')
plt.xlabel('Samples')

# Set up plot to call animate() function periodically
ani = animation.FuncAnimation(fig, animate, fargs=(ys,), interval=tiempo, blit=True)
plt.show()
plt.close()
serialPort.close()

This is how the graph looks like, the x and y axis are always the same:

在此处输入图像描述

If you want to autoscale the y-axis, then you can simply adjust the y-axis limits in your animate() function:

def animate(i, ys):

    # Read pulse from PALS2
    pulse = readarduino()

    # Add y to list
    ys.append(pulse)

    # Limit x and y lists to set number of items
    ys = ys[-x_len:]
    ymin = np.min(ys)
    ymax = np.max(ys)
    ax.set_ylim(ymin, ymax)

    # Update line with new Y values
    line.set_ydata(ys)
    return line,

HOWEVER , the result will not be what you expect if you use blit=True . This is because blitting is attempting to only draw the parts of the graphs that have changed, and the ticks on the axes are excluded from that. If you need to change the limits and therefore the ticks, you should use blit=False in your call to FuncAnimation . Note that you will encounter a performance hit since matplotlib will have to redraw the whole plot at every frame, but if you want to change the limits, there is no way around that.

So I made some changes to the last code of this link https://www.learnpyqt.com/courses/graphics-plotting/plotting-pyqtgraph/ and I could solve the problem. x and Y axis are now autoscaling

import PyQt5
from PyQt5 import QtWidgets, QtCore
from pyqtgraph import PlotWidget, plot
import pyqtgraph as pg
import sys  # We need sys so that we can pass argv to QApplication
import os
from random import randint
import serial

class MainWindow(QtWidgets.QMainWindow):

    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        self.graphWidget = pg.PlotWidget()
        self.setCentralWidget(self.graphWidget)

        # Data from serial port
        self.port = 'COM8'
        self.baudrate = 9600
        self.tout = 0.01  # Miliseconds

        # Time to update the data of the sensor signal Rs=9600baud T=1/Rs
        self.tiempo = (1 / self.baudrate) * 1000

        self.x_len = 200
        self.x = list(range(0, self.x_len))  # 100 time points
        self.y = [0] * self.x_len # 100 data points

        self.graphWidget.setBackground('w')

        # Markers
        self.startMarker = 60  # Start marker "<"
        self.endMarker = 62  # End marker ">"

        # Begin Arduino communication, Port COM8, speed 9600
        self.serialPort = serial.Serial(self.port, self.baudrate, timeout=self.tout)

        pen = pg.mkPen(color=(255, 0, 0))
        self.data_line = self.graphWidget.plot(self.x, self.y, pen=pen)

        self.timer = QtCore.QTimer()
        self.timer.setInterval(self.tiempo)
        self.timer.timeout.connect(self.update_plot_data)
        self.timer.start()

    # Begin to save the arduino data
    def arduinodata(self):

        ck = ""
        x = "z"  # any value that is not an end- or startMarker
        bytecount = -1  # to allow for the fact that the last increment will be one too many

        # wait for the start character
        while ord(x) != self.startMarker:
            x = self.serialPort.read()

        # save data until the end marker is found
        while ord(x) != self.endMarker:
            if ord(x) != self.startMarker:
                ck = ck + x.decode()
                bytecount += 1
            x = self.serialPort.read()

        return ck

    def readarduino(self):

        # Wait until the Arduino sends '<Arduino Ready>' - allows time for Arduino reset
        # It also ensures that any bytes left over from a previous message are discarded

        msg = ""

        while msg.find("<Arduino is ready>") == -1:

            while self.serialPort.inWaiting() == 0:
                pass
            # delete for example the "\r\n" that may contain the message
            msg = self.arduinodata()
            msg = msg.split("\r\n")
            msg = ''.join(msg)

            # If the sensor send very big numbers over 90000 they will be deleted
            if msg and len(msg) <= 5:
                msg = int(msg)
                return msg

            elif msg and len(msg) >= 4:
                msg = int(msg)
                return msg


    def update_plot_data(self):
        pulse = self.readarduino()
        self.x = self.x[1:]  # Remove the first y element.
        self.x.append(self.x[-1] + 1)  # Add a new value 1 higher than the last.

        self.y = self.y[1:]  # Remove the first
        self.y.append(pulse)  # Add a new random value.

        self.data_line.setData(self.x, self.y)  # Update the data.


app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM