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

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()

问题分析

  1. 绘图逻辑冲突:同时混用FuncAnimation和手动plt.draw()模式,且plotAction初始为False,FuncAnimation从未执行,绘图窗口无内容更新。
  2. 数据未持久化:animate函数每次新建空列表和计数器,即使执行也只会绘制单个点,无法保留历史数据。
  3. 数据类型错误:UDP接收的y1、y2是字符串类型,未转换为数值就传入绘图函数,导致无法正确绘制。
  4. Socket资源浪费:每次循环新建sock2,重复创建Socket会导致资源冗余,甚至引发异常。
  5. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 03:10:43