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

基于Dash的超声波传感器计数器UI实时更新故障排查

Dash超声波计数器无实时更新问题修复

问题根源分析

  • 回调未绑定定时触发源:当前回调仅监听输入框变化,未加入interval-component的n_intervals输入,无法定时读取串口数据并更新界面。
  • 串口重复初始化:每次回调都重新打开串口,易引发端口占用冲突,且资源未正确释放。
  • 变量未安全初始化:values_number可能在未匹配到传感器数据时未定义,导致运行时错误。
  • 循环逻辑不符合实时需求:for i in range(blocks)会循环指定次数,而非持续检测传感器状态。
  • 返回值未使用更新后的数值:最终返回的是原始输入的blocks,而非递减后的数值,界面自然无变化。
  • 无状态存储机制:Dash回调是无状态的,每次回调都会重置计数器数值,需要用dcc.Store保存当前剩余数量。

修正后的完整代码

import dash
from dash import dcc, html, Input, Output, State
import serial
import re

app = dash.Dash(__name__)
app.title = "Companyname COUNTER"

# 全局初始化串口(仅打开一次)
try:
    ser = serial.Serial(port='COM7', baudrate=57600, timeout=1)
except serial.SerialException as e:
    print(f"串口打开失败: {e}")
    ser = None

app.layout = html.Div(style={'fontFamily': 'Arial, sans-serif', 'backgroundColor': '#f0f4f8', 'padding': '30px'},
                      children=[
    html.H1("Companyname BLOCK COUNTER", style={'textAlign': 'center', 'color': '#333'}),

    html.Div([
        html.Div([
            html.Label("Type:", style={'color': '#fff'}),
            dcc.Input(
                id='input-1',
                type='text',
                value='EPS70',
                debounce=True,
                style={'width': '100%', 'padding': '10px', 'border': 'none', 'borderRadius': '5px'}
            )
        ], style={'backgroundColor': 'blue', 'padding': '20px', 'margin': '10px', 'borderRadius': '10px', 'width': '200px'}),
        html.Div([
            html.Label("Colour:", style={'color': '#fff'}),
            dcc.Input(
                id='input-2',
                type='text',
                value='Silver',
                debounce=True,
                style={'width': '100%', 'padding': '10px', 'border': 'none', 'borderRadius': '5px'}
            )
        ], style={'backgroundColor': 'blue', 'padding': '20px', 'margin': '10px', 'borderRadius': '10px', 'width': '200px'}),

        html.Div([
            html.Label("Size:", style={'color': '#fff'}),
            dcc.Input(
                id='input-3',
                type='text',
                value='0',
                debounce=True,
                style={'width': '100%', 'padding': '10px', 'border': 'none', 'borderRadius': '5px'}
            )
        ], style={'backgroundColor': 'blue', 'padding': '20px', 'margin': '10px', 'borderRadius': '10px', 'width': '200px'}),

        html.Div([
            html.Label("Amount:", style={'color': '#fff'}),
            dcc.Input(
                id='input-4',
                type='number',
                value=0,
                debounce=True,
                style={'width': '100%', 'padding': '10px', 'border': 'none', 'borderRadius': '5px'}
            )
        ], style={'backgroundColor': 'blue', 'padding': '20px', 'margin': '10px', 'borderRadius': '10px', 'width': '200px'}),

    ], style={'display': 'flex', 'justifyContent': 'center', 'flexWrap': 'wrap'}),

    html.Hr(),

    html.Div(id='output-display', style={
        'textAlign': 'center',
        'fontSize': '100px',
        'marginTop': '20px',
        'backgroundColor': '#ffffff',
        'padding': '20px',
        'borderRadius': '10px',
        'boxShadow': '0 4px 8px rgba(0, 0, 0, 0.1)'
    }),
    dcc.Interval(
        id='interval-component',
        interval=1000,  # 每秒触发一次检测
        n_intervals=0
    ),
    # 存储当前剩余数量的状态容器
    dcc.Store(id='remaining-blocks', data=0)
])


@app.callback(
    [Output('output-display', 'children'),
     Output('remaining-blocks', 'data')],
    [Input('interval-component', 'n_intervals'),
     Input('input-4', 'value')],
    [State('remaining-blocks', 'data'),
     State('input-1', 'value'),
     State('input-2', 'value'),
     State('input-3', 'value')]
)
def update_counter(n_intervals, input_blocks, remaining_blocks, name, color, category):
    # 当输入的总量变化时,重置剩余数量
    if input_blocks is not None and input_blocks != remaining_blocks:
        remaining_blocks = input_blocks
    
    # 串口未连接时直接返回当前状态
    if ser is None:
        return f"串口未连接 | Type: {name} | Colour: {color} | Size: {category} | Amount: {remaining_blocks}", remaining_blocks
    
    # 读取并解析传感器数据
    value = ser.readline(8)
    values_number = None
    if value.startswith(b'R'):
        values_string = str(value)
        values_string = re.sub('[^0-9]', '', values_string)
        if values_string:
            values_number = int(values_string)
    
    # 检测到物体距离小于3500且剩余数量大于0时,递减计数
    if values_number is not None and values_number < 3500 and remaining_blocks > 0:
        remaining_blocks -= 1
    
    # 返回更新后的显示文本和剩余数量
    return (f"Type: {name} | Colour: {color} | Size: {category} | Amount: {remaining_blocks}",
            remaining_blocks)


if __name__ == '__main__':
    app.run(debug=True)

关键修改说明

  • 全局初始化串口,避免重复打开引发的端口占用错误。
  • 加入dcc.Store组件保存剩余数量,解决Dash回调无状态的问题。
  • 回调绑定interval-component实现每秒自动触发检测,同时监听输入框变化以重置计数器。
  • 安全解析传感器数据,避免变量未定义导致的运行时错误。
  • 实时读取串口数据并判断距离,符合机器操作员的实时监控需求。
  • 返回更新后的剩余数量,确保界面显示最新计数结果。

内容的提问来源于stack exchange,提问作者Shade-Swift

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 00:07:03