Matplotlib实时绘图无数据显示问题求助(UDP+MCU模拟场景)
问题:Matplotlib双图实时绘图窗口为空,无法显示UDP传输的传感器数据
场景说明
基于Python的Matplotlib实现双图实时绘图,数据来自MCU模拟的两个传感器,通过Cisco Packet Tracer与PyCharm的Python服务间的UDP连接传输,另一台MCU代码已完成无需关注。已通过以下方式解决plt.show()阻塞问题:
# 绘图初始化 plt.ion() plt.show() # 循环内 plt.draw() plt.pause(0.1)
但当前绘图窗口为空,无法显示传入的数据。
原服务器端代码
# Libraries that will be used import socket from itertools import count import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation import time # initializing ip,ports for mcu1,2 and a boolean statement to signal mcu2 print("running") UDP_IP = "localhost" UDP_PORT = 4444 UDP_PORT2 = 5555 ventelate = False plotAction = False # create our figure and subplots fig = plt.figure() ax1 = fig.add_subplot(2, 1, 1) ax2 = fig.add_subplot(2, 1, 2) # Adding labels to subplots is a little different ax1.set_title('tempertioer') ax1.set_xlabel('time') ax1.set_ylabel('%') ax2.set_title('Carbon') ax2.set_xlabel('time') ax2.set_ylabel('CO') # Subplots_adjust change the space between subplots plt.subplots_adjust(hspace=0.5) def animate(i): ###plotting initalizaton x,y axis x_val = [] x_val2 = [] y_val = [] y_val2 = [] index = count() index2 = count() # plot values appenders x_val.append(next(index)) x_val2.append(next(index2)) y_val.append(y1) y_val2.append(y2) # griding the plots plt.subplot(211) plt.plot(x_val, y_val) plt.subplot(212) plt.plot(x_val2, y_val2) plt.ion() plt.show() # socket defining sock = socket.socket(socket.AF_INET, # Internet socket.SOCK_DGRAM) # UDP sock.bind((UDP_IP, UDP_PORT)) # binds ip,port ot sock(upove in line 38+39) # loop while True: data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes print("received message: %s" % data) dataString = str(data.decode('utf-8')) # decoding received data seperatedData = dataString.split(",") # arranging data into array y1 = seperatedData[0] y2 = seperatedData[1] # plot action if plotAction: ani = FuncAnimation(plt.gcf(), animate, interval=100) plotAction = False # Draw all the plots! plt.draw() plt.pause(0.1) # comparing values to either signal MCU2 or not if int(seperatedData[0]) >= 20 or int(seperatedData[1]) == 1023: ventelate = True; print("sending open") else: ventelate = False; print("sending close") if ventelate: MESSAGE = b"1" # encoding to utf-8 / byte condotion print(" open") sock2 = socket.socket(socket.AF_INET, # Internet socket.SOCK_DGRAM) # UDP sock2.sendto(MESSAGE, (UDP_IP, UDP_PORT2)) else: MESSAGE = b"0" print(" close") sock2 = socket.socket(socket.AF_INET, # Internet socket.SOCK_DGRAM) # UDP sock2.sendto(MESSAGE, (UDP_IP, UDP_PORT2))
原MCU端代码
from realudp import * from time import * from gpio import * from realudp import * IP = "127.0.0.1" PORT = 4444 def onUDPReceive(ip, port, data): print("received from " + ip + ":" + str(port) + ":" + data); def main(): sensor1= (((analogRead(A0) - 0) * (100 - -100)) / (1023 - 0)) + -100; sensor2= digitalRead(0); MESSAGE = sensor1, sensor2 print("UDP target IP: %s" % IP) print("UDP target port: %s" % PORT) print("message: %s" % MESSAGE) udp = RealUDPSocket() udp.begin(4444) udp.send(IP,PORT,MESSAGE) udp.stop() socket = RealUDPSocket() socket.onReceive(onUDPReceive) print(socket.begin(4444)) count = 0 while True: sensor1= (((analogRead(A0) - 0) * (100 - -100)) / (1023 - 0)) + -100; sensor2= digitalRead(0); MESSAGE = sensor1, sensor2 count += 1 socket.send(IP, PORT, MESSAGE," "+ str(count)) delay(100) if __name__ == "__main__": main()
问题分析
- 绘图逻辑冲突:同时混用
FuncAnimation和手动plt.draw()模式,且plotAction初始为False,FuncAnimation从未执行,绘图窗口无内容更新。 - 数据未持久化:
animate函数每次新建空列表和计数器,即使执行也只会绘制单个点,无法保留历史数据。 - 数据类型错误:UDP接收的
y1、y2是字符串类型,未转换为数值就传入绘图函数,导致无法正确绘制。 - Socket资源浪费:每次循环新建
sock2,重复创建Socket会导致资源冗余,甚至引发异常。 - MCU端数据格式问题:发送的
MESSAGE是元组,未转换为逗号分隔的字符串,服务器端split(",")无法正确拆分数据。
修复后的服务器端代码
import socket from itertools import count import matplotlib.pyplot as plt import time print("running") UDP_IP = "localhost" UDP_PORT = 4444 UDP_PORT2 = 5555 ventelate = False # 初始化绘图数据,全局保存历史值 x_index = count() x_val = [] y_val_temp = [] y_val_co = [] # 创建画布和子图 fig = plt.figure() ax1 = fig.add_subplot(2, 1, 1) ax2 = fig.add_subplot(2, 1, 2) # 设置子图标签 ax1.set_title('温度') ax1.set_xlabel('时间') ax1.set_ylabel('百分比') ax2.set_title('一氧化碳') ax2.set_xlabel('时间') ax2.set_ylabel('CO值') plt.subplots_adjust(hspace=0.5) # 开启交互模式,只执行一次 plt.ion() plt.show() # 初始化UDP Socket,复用sock2避免重复创建 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.bind((UDP_IP, UDP_PORT)) sock2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) while True: data, addr = sock.recvfrom(1024) print("received message: %s" % data) # 解析数据并转换为数值类型 dataString = data.decode('utf-8').strip() seperatedData = dataString.split(",") # 确保数据格式正确 if len(seperatedData) != 2: print("数据格式错误,跳过本次绘图") continue try: y1 = float(seperatedData[0]) y2 = int(seperatedData[1]) except ValueError: print("数据转换失败,跳过本次绘图") continue # 更新历史数据 x_val.append(next(x_index)) y_val_temp.append(y1) y_val_co.append(y2) # 清除子图内容并重新绘制 ax1.clear() ax1.set_title('温度') ax1.set_xlabel('时间') ax1.set_ylabel('百分比') ax1.plot(x_val, y_val_temp) ax2.clear() ax2.set_title('一氧化碳') ax2.set_xlabel('时间') ax2.set_ylabel('CO值') ax2.plot(x_val, y_val_co) # 更新绘图窗口 plt.draw() plt.pause(0.1) # 判断是否需要通风 if y1 >= 20 or y2 == 1023: ventelate = True print("sending open") else: ventelate = False print("sending close") # 发送控制指令 MESSAGE = b"1" if ventelate else b"0" sock2.sendto(MESSAGE, (UDP_IP, UDP_PORT2)) print("open" if ventelate else "close")
MCU端代码修复(数据格式部分)
将发送的元组转换为逗号分隔的字符串,确保服务器能正确解析:
# 替换原MESSAGE赋值部分 MESSAGE = f"{sensor1},{sensor2}" # 循环内同样修改 MESSAGE = f"{sensor1},{sensor2}"
内容的提问来源于stack exchange,提问作者Red
相关产品推荐
相关产品推荐

