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

如何将蓝牙数据接收与实时绘图两个Python程序合并为一个应用?

合并蓝牙数据接收与实时绘图程序的实用方案

针对你遇到的阻塞问题,核心解决思路是把阻塞的蓝牙IO操作和绘图UI操作拆分为独立的执行单元,通过线程/进程间的安全通信来实现数据共享,避免单线程下的阻塞冲突。下面是几种可行的方案和关键技巧:

方案1:多线程 + 内存队列(最推荐,适合IO密集场景)

把蓝牙数据接收放到单独的子线程,通过queue.Queue在主线程(绘图)和子线程之间传递数据,完全替代“写文件再读文件”的低效方式,同时避免阻塞主线程的UI更新。

代码示例

import socket
import threading
import queue
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

# 线程间安全传递数据的队列
data_queue = queue.Queue(maxsize=100)

class BluetoothReceiver(threading.Thread):
    def __init__(self, bluetooth_addr, port):
        super().__init__(daemon=True)  # 守护线程,主程序退出时自动终止
        self.bluetooth_addr = bluetooth_addr
        self.port = port
        self.running = True

    def run(self):
        # 蓝牙socket连接逻辑(根据你的设备协议调整)
        sock = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_STREAM, socket.BTPROTO_RFCOMM)
        try:
            sock.connect((self.bluetooth_addr, self.port))
            while self.running:
                # 接收并解析数据(示例为读取字符串转数字)
                raw_data = sock.recv(1024).decode().strip()
                if raw_data:
                    try:
                        sensor_value = float(raw_data)
                        if not data_queue.full():
                            data_queue.put(sensor_value)
                    except ValueError:
                        print(f"跳过无效数据: {raw_data}")
        except Exception as e:
            print(f"蓝牙连接异常: {e}")
        finally:
            sock.close()

    def stop(self):
        self.running = False

# 实时绘图初始化
fig, ax = plt.subplots()
x_points = []
y_points = []
line, = ax.plot(x_points, y_points)
ax.set_ylim(0, 100)  # 根据你的数据范围调整

def update_plot(frame):
    # 一次性取出队列中所有新数据
    while not data_queue.empty():
        val = data_queue.get()
        x_points.append(len(x_points))
        y_points.append(val)
        # 只保留最近100个数据点,防止内存溢出和图表卡顿
        if len(x_points) > 100:
            x_points.pop(0)
            y_points.pop(0)
    line.set_data(x_points, y_points)
    ax.set_xlim(0, len(x_points))
    return line,

if __name__ == "__main__":
    # 替换为你的蓝牙设备MAC地址和端口
    BLUETOOTH_ADDR = "XX:XX:XX:XX:XX:XX"
    PORT = 1

    # 启动蓝牙接收线程
    receiver_thread = BluetoothReceiver(BLUETOOTH_ADDR, PORT)
    receiver_thread.start()

    # 启动实时绘图动画
    ani = FuncAnimation(fig, update_plot, interval=100, blit=True)
    plt.show()

    # 绘图窗口关闭后停止线程
    receiver_thread.stop()
    receiver_thread.join()

方案2:多进程 + 进程队列

如果你的绘图逻辑涉及大量CPU计算(比如复杂数据渲染),可以用多进程绕过Python的GIL限制。核心逻辑和线程版类似,只是把threading替换为multiprocessing,用multiprocessing.Queue做进程间通信。

核心代码片段

import multiprocessing

class BluetoothReceiver(multiprocessing.Process):
    def __init__(self, bluetooth_addr, port, data_queue):
        super().__init__(daemon=True)
        self.bluetooth_addr = bluetooth_addr
        self.port = port
        self.data_queue = data_queue
        self.running = True

    # run方法逻辑和线程版一致,略...

if __name__ == "__main__":
    data_queue = multiprocessing.Queue(maxsize=100)
    receiver_proc = BluetoothReceiver(BLUETOOTH_ADDR, PORT, data_queue)
    receiver_proc.start()
    # 绘图逻辑和线程版一致,略...

关键技巧总结

  • 绝对不要阻塞主线程:蓝牙接收的while循环必须放到后台线程/进程,主线程只负责UI更新。
  • 用内存队列替代文件共享:避免文件IO的延迟和同步问题,数据传递更高效。
  • 限制数据缓存大小:绘图时只保留最近的N个数据点,防止内存占用过高导致程序卡顿。
  • 异常防护:在蓝牙接收逻辑中加入异常捕获,避免连接中断导致整个程序崩溃。
  • 守护线程/进程:设置daemon=True,确保主程序退出时后台任务自动终止,不会残留进程。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 00:20:28