You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python基于TKinter的Arduino串口监视器绘图不显示问题求助

代码修复说明
  • 删除无效导入import self,补充缺失的线程类导入语句from threading import Thread
  • 替换已弃用的time.clock()接口为time.perf_counter(),兼容Python3.8及以上版本
  • 修正所有HTML转义的引号"为正常双引号
  • 将动画实例anim绑定到Tkinter主窗口对象上,避免被垃圾回收导致绘图无法渲染更新
  • 补充说明:如果你的Arduino输出数值范围不在默认的-1~1之间,请调整main函数中ymin和ymax参数匹配你的实际数据范围;同时请确保Arduino端发送的二进制数据格式与Python端配置一致,比如numPlots=3、dataNumBytes=4对应Arduino连续发送3个float类型的二进制数据。
修复后完整代码
import serial
import time
import collections
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import struct
import copy
from threading import Thread
from matplotlib.backends.backend_tkagg import FigureCanvasTk, NavigationToolbar2Tk
import tkinter as Tk
from tkinter.ttk import Frame
import pandas as pd


class serialPlot:
    def __init__(self, serialPort='COM12', serialBaud=9600, plotLength=100, dataNumBytes=2, numPlots=1):
        self.port = serialPort
        self.baud = serialBaud
        self.plotMaxLength = plotLength
        self.dataNumBytes = dataNumBytes
        self.numPlots = numPlots
        self.rawData = bytearray(numPlots * dataNumBytes)
        self.dataType = None
        if dataNumBytes == 2:
            self.dataType = 'h'  # 2字节整数
        elif dataNumBytes == 4:
            self.dataType = 'f'  # 4字节浮点数
        self.data = []
        for me in range(numPlots):
            self.data.append(collections.deque([0] * plotLength, maxlen=plotLength))
        self.isRun = True
        self.isReceiving = False
        self.thread = None
        self.plotTimer = 0
        self.previousTimer = 0

        print('尝试连接: ' + str(serialPort) + ' 波特率' + str(serialBaud))
        try:
            self.serialConnection = serial.Serial(serialPort, serialBaud, timeout=4)
            print('已连接到' + str(serialPort) + ' 波特率' + str(serialBaud))
        except:
            print("连接失败" + str(serialPort) + ' 波特率' + str(serialBaud))

    def readSerialStart(self):
        if self.thread is None:
            self.thread = Thread(target=self.backgroundThread)
            self.thread.start()
            # 阻塞直到收到第一组数据
            while not self.isReceiving:
                time.sleep(0.1)

    def getSerialData(self, frame, lines, lineValueText, lineLabel, timeText):
        currentTimer = time.perf_counter()
        self.plotTimer = int((currentTimer - self.previousTimer) * 1000)
        self.previousTimer = currentTimer
        timeText.set_text('绘图间隔 = ' + str(self.plotTimer) + 'ms')
        privateData = copy.deepcopy(self.rawData[:])
        for me in range(self.numPlots):
            data = privateData[(me * self.dataNumBytes):(self.dataNumBytes + me * self.dataNumBytes)]
            value, = struct.unpack(self.dataType, data)
            self.data[me].append(value)
            lines[me].set_data(range(self.plotMaxLength), self.data[me])
            lineValueText[me].set_text('[' + lineLabel[me] + '] = ' + str(value))

    def backgroundThread(self):
        time.sleep(1.0)
        self.serialConnection.reset_input_buffer()
        while self.isRun:
            self.serialConnection.readinto(self.rawData)
            self.isReceiving = True

    def sendSerialData(self, data):
        self.serialConnection.write(data.encode('utf-8'))

    def close(self):
        self.isRun = False
        self.thread.join()
        self.serialConnection.close()
        print('已断开连接...')


class Window(Frame):
    def __init__(self, figure, master, SerialReference):
        Frame.__init__(self, master)
        self.entry = None
        self.setPoint = None
        self.master = master
        self.serialReference = SerialReference
        self.initWindow(figure)

    def initWindow(self, figure):
        self.master.title("实时绘图")
        canvas = FigureCanvasTk(figure, master=self.master)
        toolbar = NavigationToolbar2Tk(canvas, self.master)
        canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)

        lbl1 = Tk.Label(self.master, text="缩放系数")
        lbl1.pack(padx=5, pady=5)
        self.entry = Tk.Entry(self.master)
        self.entry.insert(0, '1.0')
        self.entry.pack(padx=5)
        SendButton = Tk.Button(self.master, text='发送', command=self.sendFactorToMCU)
        SendButton.pack(padx=5)

    def sendFactorToMCU(self):
        self.serialReference.sendSerialData(self.entry.get() + '%')


def main():
    portName = 'COM12' # 请修改为你的实际串口号
    baudRate = 9600 # 请修改为你的实际波特率
    maxPlotLength = 100
    dataNumBytes = 4
    numPlots = 3
    s = serialPlot(portName, baudRate, maxPlotLength, dataNumBytes, numPlots)
    s.readSerialStart()

    pltInterval = 50
    xmin = 0
    xmax = maxPlotLength
    ymin = -1 # 请根据实际数据范围调整
    ymax = 1 # 请根据实际数据范围调整
    fig = plt.figure(figsize=(10, 8))
    ax = plt.axes(xlim=(xmin, xmax), ylim=(float(ymin - (ymax - ymin) / 10), float(ymax + (ymax - ymin) / 10)))
    ax.set_title('Arduino加速度计数据')
    ax.set_xlabel("时间")
    ax.set_ylabel("加速度计输出")

    root = Tk.Tk()
    app = Window(fig, root, s)

    lineLabel = ['X', 'Y', 'Z']
    style = ['r-', 'c-', 'b-']
    timeText = ax.text(0.70, 0.95, '', transform=ax.transAxes)
    lines = []
    lineValueText = []
    for me in range(numPlots):
        lines.append(ax.plot([], [], style[me], label=lineLabel[me])[0])
        lineValueText.append(ax.text(0.70, 0.90 - me * 0.05, '', transform=ax.transAxes))
    anim = animation.FuncAnimation(fig, s.getSerialData, fargs=(lines, lineValueText, lineLabel, timeText),
                                   interval=pltInterval)
    root.anim = anim # 绑定动画实例到主窗口,避免被回收

    root.mainloop()

    s.close()


if __name__ == '__main__':
    main()

内容的提问来源于stack exchange,提问作者Luqman sadozai

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.21 08:57:02