From 81b0ad0124847f083990d574dc8d20961ec6e713 Mon Sep 17 00:00:00 2001
From: baoshiwei <baoshiwei@shlanbao.cn>
Date: 星期三, 01 四月 2026 14:12:55 +0800
Subject: [PATCH] feat(参数调节): 添加优化版挤出机参数调节页面
---
app/pages/metered_weight_correlation.py | 448 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 448 insertions(+), 0 deletions(-)
diff --git a/app/pages/metered_weight_correlation.py b/app/pages/metered_weight_correlation.py
index 0afb421..d6d862f 100644
--- a/app/pages/metered_weight_correlation.py
+++ b/app/pages/metered_weight_correlation.py
@@ -6,6 +6,118 @@
from datetime import datetime, timedelta
from app.services.extruder_service import ExtruderService
from app.services.main_process_service import MainProcessService
+from app.services.data_processing_service import DataProcessingService
+
+# 绋虫�佽瘑鍒被瀹氫箟
+class SteadyStateDetector:
+ def __init__(self):
+ self.data_processor = DataProcessingService()
+
+ def preprocess_data(self, df, weight_col='metered_weight', window_size=20):
+ if df is None or df.empty:
+ return df
+
+ df_processed = df.copy()
+
+ df_processed[weight_col] = df_processed[weight_col].ffill().bfill()
+ df_processed['smoothed_weight'] = df_processed[weight_col]
+ df_processed['rolling_std'] = df_processed[weight_col].rolling(window=window_size, min_periods=1).std()
+ df_processed['rolling_mean'] = df_processed[weight_col].rolling(window=window_size, min_periods=1).mean()
+
+ return df_processed
+
+ def detect_steady_state(self, df, weight_col='smoothed_weight', window_size=20, std_threshold=0.5, duration_threshold=60):
+ if df is None or df.empty:
+ return df, []
+
+ df['time'] = pd.to_datetime(df['time'])
+ df['time_diff'] = df['time'].diff().dt.total_seconds().fillna(0)
+ df['is_steady'] = 0
+
+ df['window_std'] = df['smoothed_weight'].rolling(window=window_size, min_periods=5).std()
+ df['window_mean'] = df['smoothed_weight'].rolling(window=window_size, min_periods=5).mean()
+ df['fluctuation_range'] = (df['window_std'] / df['window_mean']) * 100
+ df['fluctuation_range'] = df['fluctuation_range'].fillna(0)
+
+ df.loc[(df['fluctuation_range'] < std_threshold) & (df['smoothed_weight'] >= 0.1), 'is_steady'] = 1
+
+ steady_segments = []
+ current_segment = {}
+
+ for i, row in df.iterrows():
+ if row['is_steady'] == 1:
+ if not current_segment:
+ current_segment = {
+ 'start_time': row['time'],
+ 'start_idx': i,
+ 'weights': [row['smoothed_weight']]
+ }
+ else:
+ current_segment['weights'].append(row['smoothed_weight'])
+ else:
+ if current_segment:
+ current_segment['end_time'] = df.loc[i-1, 'time'] if i > 0 else df.loc[i, 'time']
+ current_segment['end_idx'] = i-1
+ duration = (current_segment['end_time'] - current_segment['start_time']).total_seconds()
+
+ if duration >= duration_threshold:
+ weights_array = np.array(current_segment['weights'])
+ current_segment['duration'] = duration
+ current_segment['mean_weight'] = np.mean(weights_array)
+ current_segment['std_weight'] = np.std(weights_array)
+ current_segment['min_weight'] = np.min(weights_array)
+ current_segment['max_weight'] = np.max(weights_array)
+ current_segment['fluctuation_range'] = (current_segment['std_weight'] / current_segment['mean_weight']) * 100
+
+ confidence = 100 - (current_segment['fluctuation_range'] / std_threshold) * 50
+ confidence = max(50, min(100, confidence))
+ current_segment['confidence'] = confidence
+
+ steady_segments.append(current_segment)
+
+ current_segment = {}
+
+ if current_segment:
+ current_segment['end_time'] = df['time'].iloc[-1]
+ current_segment['end_idx'] = len(df) - 1
+ duration = (current_segment['end_time'] - current_segment['start_time']).total_seconds()
+
+ if duration >= duration_threshold:
+ weights_array = np.array(current_segment['weights'])
+ current_segment['duration'] = duration
+ current_segment['mean_weight'] = np.mean(weights_array)
+ current_segment['std_weight'] = np.std(weights_array)
+ current_segment['min_weight'] = np.min(weights_array)
+ current_segment['max_weight'] = np.max(weights_array)
+ current_segment['fluctuation_range'] = (current_segment['std_weight'] / current_segment['mean_weight']) * 100
+
+ confidence = 100 - (current_segment['fluctuation_range'] / std_threshold) * 50
+ confidence = max(50, min(100, confidence))
+ current_segment['confidence'] = confidence
+
+ steady_segments.append(current_segment)
+
+ for segment in steady_segments:
+ df.loc[segment['start_idx']:segment['end_idx'], 'is_steady'] = 1
+
+ return df, steady_segments
+
+ def get_steady_state_metrics(self, steady_segments):
+ if not steady_segments:
+ return {}
+
+ avg_duration = np.mean([seg['duration'] for seg in steady_segments])
+ avg_fluctuation = np.mean([seg['fluctuation_range'] for seg in steady_segments])
+ avg_confidence = np.mean([seg['confidence'] for seg in steady_segments])
+ total_steady_duration = sum([seg['duration'] for seg in steady_segments])
+
+ return {
+ 'total_steady_segments': len(steady_segments),
+ 'average_steady_duration': avg_duration,
+ 'average_fluctuation_range': avg_fluctuation,
+ 'average_confidence': avg_confidence,
+ 'total_steady_duration': total_steady_duration
+ }
def show_metered_weight_correlation():
# 鍒濆鍖栨湇鍔�
@@ -14,6 +126,11 @@
# 椤甸潰鏍囬
st.title("绫抽噸鐩稿叧鎬у垎鏋�")
+
+ # 鍒濆鍖栨湇鍔�
+ extruder_service = ExtruderService()
+ main_process_service = MainProcessService()
+ steady_state_detector = SteadyStateDetector()
# 鍒濆鍖栦細璇濈姸鎬佺敤浜庢棩鏈熷悓姝�
if 'mc_start_date' not in st.session_state:
@@ -24,6 +141,16 @@
st.session_state['mc_quick_select'] = "鏈�杩�7澶�"
if 'mc_time_offset' not in st.session_state:
st.session_state['mc_time_offset'] = 0.0
+
+ # 鍒濆鍖栫ǔ鎬佽瘑鍒浉鍏冲弬鏁�
+ if 'mc_ss_window_size' not in st.session_state:
+ st.session_state['mc_ss_window_size'] = 20
+ if 'mc_ss_std_threshold' not in st.session_state:
+ st.session_state['mc_ss_std_threshold'] = 1.5
+ if 'mc_ss_duration_threshold' not in st.session_state:
+ st.session_state['mc_ss_duration_threshold'] = 60
+ if 'mc_use_steady_only' not in st.session_state:
+ st.session_state['mc_use_steady_only'] = False
# 瀹氫箟鍥炶皟鍑芥暟
def update_dates(qs):
@@ -114,6 +241,55 @@
st.session_state['mc_time_offset'] = time_offset
with offset_cols[2]:
st.write(f"褰撳墠鍋忕Щ: {time_offset} 鍒嗛挓")
+
+ # 绋虫�佸弬鏁伴厤缃�
+ st.markdown("---")
+ steady_state_cols = st.columns(4)
+
+ with steady_state_cols[0]:
+ st.write("鈿欙笍 **绋虫�佸弬鏁伴厤缃�**")
+ window_size = st.slider(
+ "婊戝姩绐楀彛澶у皬 (绉�)",
+ min_value=5,
+ max_value=60,
+ value=st.session_state['mc_ss_window_size'],
+ step=5,
+ key="mc_ss_window_size",
+ help="鐢ㄤ簬骞虫粦鏁版嵁鍜岃绠楃粺璁$壒寰佺殑婊戝姩绐楀彛澶у皬"
+ )
+
+ with steady_state_cols[1]:
+ st.write("馃搹 **娉㈠姩闃堝�奸厤缃�**")
+ std_threshold = st.slider(
+ "鏍囧噯宸槇鍊�",
+ min_value=0.1,
+ max_value=2.0,
+ value=st.session_state['mc_ss_std_threshold'],
+ step=0.1,
+ key="mc_ss_std_threshold",
+ help="绫抽噸娉㈠姩鐨勬爣鍑嗗樊闃堝�硷紝浣庝簬姝ゅ�艰涓虹ǔ鎬�"
+ )
+
+ with steady_state_cols[2]:
+ st.write("鈴憋笍 **鎸佺画鏃堕棿閰嶇疆**")
+ duration_threshold = st.slider(
+ "绋虫�佹寔缁椂闂� (绉�)",
+ min_value=30,
+ max_value=300,
+ value=st.session_state['mc_ss_duration_threshold'],
+ step=10,
+ key="mc_ss_duration_threshold",
+ help="绋虫�佹寔缁殑鏈�灏忔椂闂达紝浣庝簬姝ゅ�间笉瑙嗕负绋虫�佹"
+ )
+
+ with steady_state_cols[3]:
+ st.write("馃幆 **鍒嗘瀽鏁版嵁閫夋嫨**")
+ use_steady_only = st.checkbox(
+ "浠呬娇鐢ㄧǔ鎬佹暟鎹�",
+ value=st.session_state['mc_use_steady_only'],
+ key="mc_use_steady_only",
+ help="鍕鹃�夊悗锛岀浉鍏虫�у垎鏋愪粎浣跨敤璇嗗埆鍑虹殑绋虫�佹暟鎹�"
+ )
# 杞崲涓篸atetime瀵硅薄
start_dt = datetime.combine(start_date, datetime.min.time())
@@ -264,6 +440,32 @@
return df_merged
+ # 鎵ц绋虫�佽瘑鍒�
+ df_extruder_steady, steady_segments = None, []
+ if df_extruder_filtered is not None and not df_extruder_filtered.empty:
+ # 鏁版嵁棰勫鐞�
+ df_processed = steady_state_detector.preprocess_data(
+ df_extruder_filtered,
+ weight_col='metered_weight',
+ window_size=st.session_state['mc_ss_window_size']
+ )
+
+ # 绋虫�佹娴�
+ df_extruder_steady, steady_segments = steady_state_detector.detect_steady_state(
+ df_processed,
+ weight_col='smoothed_weight',
+ window_size=st.session_state['mc_ss_window_size'],
+ std_threshold=st.session_state['mc_ss_std_threshold'],
+ duration_threshold=st.session_state['mc_ss_duration_threshold']
+ )
+
+ # 灏嗙ǔ鎬佹爣璁版坊鍔犲埌df_extruder_filtered涓紝浠ヤ究鍦ㄨ秼鍔垮浘涓娇鐢�
+ df_extruder_filtered = df_extruder_filtered.merge(
+ df_extruder_steady[['time', 'is_steady', 'smoothed_weight', 'fluctuation_range']],
+ on='time',
+ how='left'
+ )
+
# 鎵ц鏁版嵁鏁村悎
df_analysis = integrate_data(df_extruder_filtered, df_main_speed, df_temp)
@@ -273,6 +475,26 @@
# 閲嶅懡鍚嶇背閲嶅垪
df_analysis.rename(columns={'metered_weight': '绫抽噸'}, inplace=True)
+
+ # 濡傛灉閫夋嫨浠呬娇鐢ㄧǔ鎬佹暟鎹紝杩囨护鎺夐潪绋虫�佹暟鎹�
+ if st.session_state['mc_use_steady_only']:
+ # 纭繚df_analysis鍖呭惈is_steady鍒�
+ if 'is_steady' not in df_analysis.columns:
+ # 灏嗙ǔ鎬佹爣璁板悎骞跺埌鍒嗘瀽鏁版嵁涓�
+ df_analysis = df_analysis.merge(
+ df_extruder_steady[['time', 'is_steady']],
+ on='time',
+ how='left'
+ )
+ # 杩囨护绋虫�佹暟鎹�
+ df_analysis = df_analysis[df_analysis['is_steady'] == 1]
+ if df_analysis.empty:
+ st.warning("鏈壘鍒扮ǔ鎬佹暟鎹紝璇疯皟鏁寸ǔ鎬佸弬鏁版垨鍙栨秷'浠呬娇鐢ㄧǔ鎬佹暟鎹�'閫夐」銆�")
+ return
+
+ # 缂撳瓨绋虫�佹暟鎹埌浼氳瘽鐘舵��
+ st.session_state['cached_steady_segments'] = steady_segments
+ st.session_state['cached_extruder_steady'] = df_extruder_steady
# --- 鍘熷鏁版嵁瓒嬪娍鍥� ---
st.subheader("馃搱 鍘熷鏁版嵁瓒嬪娍鍥�")
@@ -360,6 +582,24 @@
yaxis='y5'
))
+ # 娣诲姞绋虫�佸尯鍩熸爣璁�
+ for segment in steady_segments:
+ # 鑾峰彇绫抽噸鏁版嵁鐨剏杞磋寖鍥达紝鐢ㄤ簬纭畾鐭╁舰楂樺害
+ y_min = df_extruder_filtered['metered_weight'].min() * 0.95
+ y_max = df_extruder_filtered['metered_weight'].max() * 1.05
+
+ fig_trend.add_shape(
+ type="rect",
+ x0=segment['start_time'],
+ y0=y_min,
+ x1=segment['end_time'],
+ y1=y_max,
+ fillcolor="rgba(0, 255, 0, 0.2)",
+ line=dict(color="rgba(0, 200, 0, 0.5)", width=1),
+ name="绋虫�佸尯鍩�",
+ layer="below"
+ )
+
# 閰嶇疆瓒嬪娍鍥惧竷灞�
fig_trend.update_layout(
title=f'鍘熷鏁版嵁瓒嬪娍 (绫抽噸鍚戝墠鍋忕Щ {st.session_state["mc_time_offset"]} 鍒嗛挓)',
@@ -423,6 +663,214 @@
# 鏄剧ず瓒嬪娍鍥�
selection = st.plotly_chart(fig_trend, width='stretch', config={'scrollZoom': True}, on_select='rerun' )
+ # 绋虫�佺粺璁℃寚鏍�
+ st.subheader("馃搳 绋虫�佽瘑鍒粺璁�")
+ steady_metrics = steady_state_detector.get_steady_state_metrics(steady_segments)
+ metrics_cols = st.columns(5)
+
+ with metrics_cols[0]:
+ st.metric(
+ "绋虫�佹鎬绘暟",
+ steady_metrics.get('total_steady_segments', 0),
+ help="璇嗗埆鍒扮殑绋虫�佹鏁伴噺"
+ )
+
+ with metrics_cols[1]:
+ st.metric(
+ "骞冲潎绋虫�佹椂闀�",
+ f"{steady_metrics.get('average_steady_duration', 0):.2f} 绉�",
+ help="鎵�鏈夌ǔ鎬佹鐨勫钩鍧囨寔缁椂闂�"
+ )
+
+ with metrics_cols[2]:
+ st.metric(
+ "骞冲潎娉㈠姩鑼冨洿",
+ f"{steady_metrics.get('average_fluctuation_range', 0):.2f}%",
+ help="绋虫�佹鍐呯背閲嶇殑骞冲潎娉㈠姩鑼冨洿锛堢浉瀵逛簬鍧囧�肩殑鐧惧垎姣旓級"
+ )
+
+ with metrics_cols[3]:
+ st.metric(
+ "骞冲潎缃俊搴�",
+ f"{steady_metrics.get('average_confidence', 0):.1f}%",
+ help="绋虫�佽瘑鍒粨鏋滅殑骞冲潎缃俊搴�"
+ )
+
+ with metrics_cols[4]:
+ st.metric(
+ "鎬荤ǔ鎬佹椂闀�",
+ f"{steady_metrics.get('total_steady_duration', 0)/60:.2f} 鍒嗛挓",
+ help="鎵�鏈夌ǔ鎬佹鐨勬�绘寔缁椂闂�"
+ )
+
+ # --- 绋虫�佹暟鎹秼鍔垮浘 ---
+ st.subheader("馃搳 绋虫�佹暟鎹秼鍔垮浘")
+
+ # 鍒涘缓绋虫�佹暟鎹秼鍔垮浘
+ if df_extruder_steady is not None and not df_extruder_steady.empty:
+ fig_steady = go.Figure()
+
+ # 娣诲姞鍘熷绫抽噸鏁版嵁
+ fig_steady.add_trace(go.Scatter(
+ x=df_extruder_steady['time'],
+ y=df_extruder_steady['metered_weight'],
+ name='鍘熷绫抽噸',
+ mode='lines',
+ opacity=0.6,
+ line=dict(color='lightblue', width=1)
+ ))
+
+ # 娣诲姞骞虫粦鍚庣殑绫抽噸鏁版嵁
+ fig_steady.add_trace(go.Scatter(
+ x=df_extruder_steady['time'],
+ y=df_extruder_steady['smoothed_weight'],
+ name='骞虫粦绫抽噸',
+ mode='lines',
+ line=dict(color='blue', width=2)
+ ))
+
+ # 娣诲姞娉㈠姩鑼冨洿锛堜綔涓洪潰绉浘锛�
+ fig_steady.add_trace(go.Scatter(
+ x=df_extruder_steady['time'],
+ y=df_extruder_steady['metered_weight'] + df_extruder_steady['rolling_std'],
+ name='娉㈠姩涓婇檺',
+ mode='lines',
+ line=dict(color='rgba(255,0,0,0)'),
+ showlegend=True
+ ))
+
+ fig_steady.add_trace(go.Scatter(
+ x=df_extruder_steady['time'],
+ y=df_extruder_steady['metered_weight'] - df_extruder_steady['rolling_std'],
+ name='娉㈠姩涓嬮檺',
+ mode='lines',
+ line=dict(color='rgba(255,0,0,0)'),
+ fill='tonexty',
+ fillcolor='rgba(255,0,0,0.1)'
+ ))
+
+ # 娣诲姞绋虫�佹爣璁�
+ fig_steady.add_trace(go.Scatter(
+ x=df_extruder_steady['time'],
+ y=df_extruder_steady['is_steady'] * (df_extruder_steady['metered_weight'].max() * 1.1),
+ name='绋虫�佹爣璁�',
+ mode='lines',
+ line=dict(color='green', width=1, dash='dash')
+ ))
+
+ # 閰嶇疆绋虫�佹暟鎹秼鍔垮浘甯冨眬
+ fig_steady.update_layout(
+ title="绋虫�佹暟鎹秼鍔垮垎鏋�",
+ xaxis=dict(
+ title='鏃堕棿',
+ rangeslider=dict(visible=True),
+ type='date'
+ ),
+ yaxis=dict(
+ title='绫抽噸 (Kg/m)',
+ title_font=dict(color='blue'),
+ tickfont=dict(color='blue')
+ ),
+ yaxis2=dict(
+ title='绋虫�佹爣璁�',
+ title_font=dict(color='green'),
+ tickfont=dict(color='green'),
+ overlaying='y',
+ side='right',
+ range=[-0.1, 1.1],
+ showgrid=False
+ ),
+ legend=dict(
+ orientation="h",
+ yanchor="bottom",
+ y=1.02,
+ xanchor="right",
+ x=1
+ ),
+ height=500,
+ margin=dict(l=100, r=100, t=100, b=100),
+ hovermode='x unified'
+ )
+
+ # 鏄剧ず绋虫�佹暟鎹秼鍔垮浘
+ st.plotly_chart(fig_steady, width='stretch', config={'scrollZoom': True})
+
+ # --- 绋虫�佸弬鏁扮浉鍏虫�ц秼鍔垮浘 ---
+ st.subheader("馃搱 绋虫�佸弬鏁扮浉鍏虫�ц秼鍔垮浘")
+
+ if df_analysis is not None and not df_analysis.empty:
+ # 鍒涘缓绋虫�佸弬鏁扮浉鍏虫�ц秼鍔垮浘
+ fig_steady_corr = go.Figure()
+
+ # 閫夋嫨鐩稿叧绯绘暟鏈�楂樼殑鍓�3涓弬鏁�
+ if '绫抽噸' in df_analysis.columns:
+ # 璁$畻鍚勫弬鏁颁笌绫抽噸鐨勭浉鍏崇郴鏁�
+ corr_with_weight = df_analysis.corr()[['绫抽噸']].sort_values('绫抽噸', ascending=False)
+ top_params = corr_with_weight.index[1:4] # 鎺掗櫎绫抽噸鏈韩锛屽彇鍓�3涓�
+
+ # 娣诲姞绫抽噸鏁版嵁
+ fig_steady_corr.add_trace(go.Scatter(
+ x=df_analysis['time'],
+ y=df_analysis['绫抽噸'],
+ name='绫抽噸',
+ mode='lines',
+ line=dict(color='blue', width=2),
+ yaxis='y'
+ ))
+
+ # 娣诲姞鐩稿叧鍙傛暟鏁版嵁
+ colors = ['red', 'green', 'orange']
+ for i, param in enumerate(top_params):
+ if param in df_analysis.columns:
+ fig_steady_corr.add_trace(go.Scatter(
+ x=df_analysis['time'],
+ y=df_analysis[param],
+ name=param,
+ mode='lines',
+ line=dict(color=colors[i], width=1.5),
+ yaxis=f'y{i+2}'
+ ))
+
+ # 閰嶇疆鍥捐〃甯冨眬
+ layout = {
+ 'title': f'绫抽噸涓庣浉鍏冲弬鏁拌秼鍔匡紙鍓�3涓浉鍏冲弬鏁帮級',
+ 'xaxis': {
+ 'title': '鏃堕棿',
+ 'rangeslider': dict(visible=True),
+ 'type': 'date'
+ },
+ 'yaxis': {
+ 'title': '绫抽噸 (Kg/m)',
+ 'title_font': dict(color='blue'),
+ 'tickfont': dict(color='blue')
+ },
+ 'legend': {
+ 'orientation': "h",
+ 'yanchor': "bottom",
+ 'y': 1.02,
+ 'xanchor': "right",
+ 'x': 1
+ },
+ 'height': 600,
+ 'margin': dict(l=100, r=200, t=100, b=100),
+ 'hovermode': 'x unified'
+ }
+
+ # 娣诲姞棰濆鐨剏杞撮厤缃�
+ for i, param in enumerate(top_params):
+ layout[f'yaxis{i+2}'] = {
+ 'title': param,
+ 'title_font': dict(color=colors[i]),
+ 'tickfont': dict(color=colors[i]),
+ 'overlaying': 'y',
+ 'side': 'right',
+ 'anchor': 'free',
+ 'position': 1 - (i+1)*0.15
+ }
+
+ fig_steady_corr.update_layout(layout)
+ st.plotly_chart(fig_steady_corr, width='stretch', config={'scrollZoom': True})
+
# 璋冭瘯杈撳嚭
# st.write("鍘熷 selection 瀵硅薄:", selection)
--
Gitblit v1.9.3