baoshiwei
2026-04-01 81b0ad0124847f083990d574dc8d20961ec6e713
feat(参数调节): 添加优化版挤出机参数调节页面

- 新增optimized_parameter_adjustment.py页面,实现基于多目标优化的参数调节建议功能
- 在dashboard.py中添加新页面导航入口
- 调整metered_weight_advanced.py中的默认特征列表和稳态阈值
- 在metered_weight_correlation.py中添加稳态识别功能和相关分析图表
已修改3个文件
已添加1个文件
1275 ■■■■■ 文件已修改
app/pages/metered_weight_advanced.py 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/pages/metered_weight_correlation.py 448 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/pages/optimized_parameter_adjustment.py 811 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
dashboard.py 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/pages/metered_weight_advanced.py
@@ -141,11 +141,11 @@
    if 'ma_steady_window' not in st.session_state:
        st.session_state['ma_steady_window'] = 20
    if 'ma_steady_threshold' not in st.session_state:
        st.session_state['ma_steady_threshold'] = 0.5
        st.session_state['ma_steady_threshold'] = 1.5
        
    # é»˜è®¤ç‰¹å¾åˆ—表(不再允许用户选择)
    default_features = ['螺杆转速', '机头压力', '流程主速', '螺杆温度',
                       '后机筒温度', '前机筒温度', '机头温度']
    default_features = ['螺杆转速', '机头压力', '流程主速', '后机筒温度']
    # å®šä¹‰å›žè°ƒå‡½æ•°
    def update_dates(qs):
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="勾选后,相关性分析仅使用识别出的稳态数据"
            )
    # è½¬æ¢ä¸ºdatetime对象
    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轴范围,用于确定矩形高度
                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'
                    }
                    # æ·»åŠ é¢å¤–çš„y轴配置
                    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)
app/pages/optimized_parameter_adjustment.py
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,811 @@
import streamlit as st
import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
import numpy as np
import joblib
import os
from datetime import datetime
from sklearn.preprocessing import MinMaxScaler
# é¡µé¢å‡½æ•°å®šä¹‰
def show_optimized_parameter_adjustment():
    # é¡µé¢æ ‡é¢˜
    st.title("优化版挤出机参数调节建议")
    # æ·»åŠ æ“ä½œæŒ‡å¼•
    with st.expander("📖 æ“ä½œæŒ‡å¼•", expanded=True):
        st.markdown("""
        æ¬¢è¿Žä½¿ç”¨ä¼˜åŒ–版挤出机参数调节建议功能!本功能基于多目标优化算法,根据您输入的米重数据和当前参数,为您提供科学合理的参数调整建议。
        **操作步骤:**
        1. é€‰æ‹©ä¸€ä¸ªå·²è®­ç»ƒå¥½çš„æ¨¡åž‹
        2. è¾“入米重标准值、上下限和当前挤出机参数
        3. è¾“入当前实际米重测量值
        4. è°ƒæ•´ä¼˜åŒ–参数(可选)
        5. ç‚¹å‡»"计算优化调节建议"按钮
        6. æŸ¥çœ‹ç³»ç»Ÿç”Ÿæˆçš„参数调整建议
        **注意事项:**
        - è¯·ç¡®ä¿è¾“入的参数值在设备允许的范围内
        - ä¼˜åŒ–算法会平衡米重偏差和调整幅度
        - åŽ†å²è°ƒèŠ‚è®°å½•å¯åœ¨é¡µé¢åº•éƒ¨æŸ¥çœ‹
        """)
    # åˆå§‹åŒ–会话状态
    if 'optimized_adjustment_history' not in st.session_state:
        st.session_state['optimized_adjustment_history'] = []
    # 1. æ¨¡åž‹é€‰æ‹©åŒºåŸŸ
    with st.expander("🔍 æ¨¡åž‹é€‰æ‹©", expanded=True):
        # åˆ›å»ºæ¨¡åž‹ç›®å½•(如果不存在)
        model_dir = "saved_models"
        os.makedirs(model_dir, exist_ok=True)
        # èŽ·å–æ‰€æœ‰å·²ä¿å­˜çš„æ¨¡åž‹æ–‡ä»¶
        model_files = [f for f in os.listdir(model_dir) if f.endswith('.joblib')]
        model_files.sort(reverse=True)  # æœ€æ–°çš„æ¨¡åž‹æŽ’在前面
        if not model_files:
            st.warning("尚未保存任何模型,请先训练模型并保存。")
            return
        # æ¨¡åž‹é€‰æ‹©ä¸‹æ‹‰æ¡†
        selected_model_file = st.selectbox(
            "选择已保存的模型",
            options=model_files,
            help="选择要用于预测的模型文件"
        )
        # åŠ è½½å¹¶æ˜¾ç¤ºæ¨¡åž‹ä¿¡æ¯
        if selected_model_file:
            model_path = os.path.join(model_dir, selected_model_file)
            model_info = joblib.load(model_path)
            # æ˜¾ç¤ºæ¨¡åž‹åŸºæœ¬ä¿¡æ¯
            st.subheader("📊 æ¨¡åž‹ä¿¡æ¯")
            info_cols = st.columns(2)
            with info_cols[0]:
                st.metric("模型类型", model_info['model_type'])
                st.metric("创建时间", model_info['created_at'].strftime('%Y-%m-%d %H:%M:%S'))
                st.metric("使用稳态数据", "是" if model_info.get('use_steady_data', False) else "否")
            with info_cols[1]:
                st.metric("R² å¾—分", f"{model_info['r2_score']:.4f}")
                st.metric("均方误差 (MSE)", f"{model_info['mse']:.6f}")
                st.metric("均方根误差 (RMSE)", f"{model_info['rmse']:.6f}")
            # æ˜¾ç¤ºæ¨¡åž‹ç‰¹å¾
            st.write("🔑 æ¨¡åž‹ä½¿ç”¨çš„特征:")
            st.code(", ".join(model_info['features']))
            # å¦‚果是深度学习模型,显示序列长度
            if 'sequence_length' in model_info:
                st.metric("序列长度", model_info['sequence_length'])
            # ä¿å­˜æ¨¡åž‹ä¿¡æ¯åˆ°ä¼šè¯çŠ¶æ€
            st.session_state['selected_model'] = model_info
            st.session_state['selected_model_file'] = selected_model_file
    # 2. å‚数输入区域
    st.subheader("📝 å‚数输入")
    # 2.1 ç±³é‡æ ‡å‡†å€¼ã€ä¸Šä¸‹é™è¾“å…¥
    with st.expander("⚖️ ç±³é‡æ ‡å‡†ä¸Žä¸Šä¸‹é™", expanded=True):
        weight_cols = st.columns(3)
        with weight_cols[0]:
            standard_weight = st.number_input(
                "标准米重",
                key="standard_weight",
                value=5.20,
                min_value=0.01,
                max_value=10.0,
                step=0.0001,
                format="%.4f",
                help="输入目标米重标准值"
            )
            st.caption("单位: Kg/m")
        with weight_cols[1]:
            upper_limit = st.number_input(
                "米重上限",
                key="upper_limit",
                value=5.46,
                min_value=standard_weight,
                max_value=10.0,
                step=0.0001,
                format="%.4f",
                help="输入米重允许的上限值"
            )
            st.caption("单位: Kg/m")
        with weight_cols[2]:
            lower_limit = st.number_input(
                "米重下限",
                key="lower_limit",
                value=5.02,
                min_value=0.01,
                max_value=standard_weight,
                step=0.0001,
                format="%.4f",
                help="输入米重允许的下限值"
            )
            st.caption("单位: Kg/m")
    # 2.2 æŒ¤å‡ºæœºå½“前参数输入
    with st.expander("🔧 æŒ¤å‡ºæœºå½“前参数", expanded=True):
        param_cols = st.columns(3)
        with param_cols[0]:
            current_screw_speed = st.number_input(
                "螺杆转速",
                key="current_screw_speed",
                value=230.0,
                min_value=0.0,
                max_value=500.0,
                step=0.1,
                help="输入当前螺杆转速"
            )
            st.caption("单位: rpm")
            current_head_pressure = st.number_input(
                "机头压力",
                key="current_head_pressure",
                value=0.26,
                min_value=0.0,
                max_value=500.0,
                format="%.4f",
                step=1.0,
                help="输入当前机头压力"
            )
            st.caption("单位: bar")
            current_process_speed = st.number_input(
                "流程主速",
                key="current_process_speed",
                value=6.6,
                min_value=0.0,
                max_value=300.0,
                step=0.1,
                help="输入当前流程主速"
            )
            st.caption("单位: m/min")
        with param_cols[1]:
            current_screw_temperature = st.number_input(
                "螺杆温度",
                key="current_screw_temperature",
                value=79.9,
                min_value=0.0,
                max_value=300.0,
                step=1.0,
                help="输入当前螺杆温度"
            )
            st.caption("单位: Â°C")
            current_rear_barrel_temperature = st.number_input(
                "后机筒温度",
                key="current_rear_barrel_temperature",
                value=79.9,
                min_value=0.0,
                max_value=300.0,
                step=1.0,
                help="输入当前后机筒温度"
            )
            st.caption("单位: Â°C")
        with param_cols[2]:
            current_front_barrel_temperature = st.number_input(
                "前机筒温度",
                key="current_front_barrel_temperature",
                value=80.1,
                min_value=0.0,
                max_value=300.0,
                step=1.0,
                help="输入当前前机筒温度"
            )
            st.caption("单位: Â°C")
            current_head_temperature = st.number_input(
                "机头温度",
                key="current_head_temperature",
                value=95.1,
                min_value=0.0,
                max_value=300.0,
                step=1.0,
                help="输入当前机头温度"
            )
            st.caption("单位: Â°C")
    # 2.3 å½“前实际米重测量值输入
    with st.expander("📏 å½“前实际米重", expanded=True):
        actual_weight = st.number_input(
            "当前实际米重",
            key="actual_weight",
            value=5.115,
            min_value=0.01,
            max_value=10.0,
            step=0.0001,
            format="%.4f",
            help="输入当前实际测量的米重值"
        )
        st.caption("单位: Kg/m")
    # 3. ä¼˜åŒ–参数设置
    st.subheader("⚙️ ä¼˜åŒ–参数设置")
    with st.expander("🎯 å¤šç›®æ ‡ä¼˜åŒ–参数", expanded=True):
        # ç›®æ ‡æƒé‡è®¾ç½®
        st.write("### ç›®æ ‡æƒé‡")
        st.markdown("调整以下权重来平衡米重偏差和调整幅度的重要性:")
        weight_cols = st.columns(2)
        with weight_cols[0]:
            weight_deviation = st.slider(
                "米重偏差权重",
                min_value=0.1,
                max_value=1.0,
                value=0.8,
                step=0.1,
                help="权重越高,越优先考虑减少米重偏差"
            )
        with weight_cols[1]:
            weight_adjustment = st.slider(
                "调整幅度权重",
                min_value=0.1,
                max_value=1.0,
                value=0.2,
                step=0.1,
                help="权重越高,越优先考虑减少调整幅度"
            )
        # è°ƒæ•´æƒé‡ï¼Œç¡®ä¿æ€»å’Œä¸º1
        total_weight = weight_deviation + weight_adjustment
        weight_deviation = weight_deviation / total_weight
        weight_adjustment = weight_adjustment / total_weight
        st.write(f"### è°ƒæ•´åŽæƒé‡ï¼ˆæ€»å’Œä¸º1)")
        st.write(f"- ç±³é‡åå·®æƒé‡: {weight_deviation:.2f}")
        st.write(f"- è°ƒæ•´å¹…度权重: {weight_adjustment:.2f}")
        # å‚数调整约束
        st.write("### å‚数调整约束")
        constraint_cols = st.columns(2)
        with constraint_cols[0]:
            max_screw_speed_adjust = st.slider(
                "螺杆转速最大调整幅度(%)",
                min_value=5.0,
                max_value=20.0,
                value=15.0,
                step=1.0,
                help="单次调整的最大百分比"
            )
        with constraint_cols[1]:
            max_process_speed_adjust = st.slider(
                "流程主速最大调整幅度(%)",
                min_value=3.0,
                max_value=15.0,
                value=10.0,
                step=1.0,
                help="单次调整的最大百分比"
            )
    # 4. è®¡ç®—优化调节建议
    st.subheader("🚀 è®¡ç®—优化调节建议")
    if st.button("📊 è®¡ç®—优化调节建议", key="calculate_optimized_adjustment"):
        # å‚数验证
        validation_errors = []
        if standard_weight <= 0:
            validation_errors.append("标准米重必须大于0")
        if upper_limit <= standard_weight:
            validation_errors.append("米重上限必须大于标准米重")
        if lower_limit >= standard_weight:
            validation_errors.append("米重下限必须小于标准米重")
        if current_screw_speed <= 0:
            validation_errors.append("螺杆转速必须大于0")
        if current_process_speed <= 0:
            validation_errors.append("流程主速必须大于0")
        if actual_weight <= 0:
            validation_errors.append("实际米重必须大于0")
        if validation_errors:
            st.error("参数输入错误:")
            for error in validation_errors:
                st.error(f"- {error}")
        else:
            with st.spinner("正在计算优化调节建议..."):
                # èŽ·å–é€‰ä¸­çš„æ¨¡åž‹
                if 'selected_model' not in st.session_state:
                    st.error("请先选择一个模型")
                    return
                model_info = st.session_state['selected_model']
                model = model_info['model']
                # æ£€æŸ¥æ˜¯å¦æœ‰ç¼©æ”¾å™¨
                has_scalers = ('scaler_X' in model_info and model_info['scaler_X'] is not None and
                              'scaler_y' in model_info and model_info['scaler_y'] is not None)
                if has_scalers:
                    scaler_X = model_info['scaler_X']
                    scaler_y = model_info['scaler_y']
                features = model_info['features']
                # å¤šç›®æ ‡ä¼˜åŒ–算法 - ç²’子群优化
                def predict_weight(screw_speed, process_speed):
                    """使用模型预测米重"""
                    # å‡†å¤‡è¾“入数据
                    input_data = {
                        '螺杆转速': screw_speed,
                        '机头压力': current_head_pressure,
                        '流程主速': process_speed,
                        '螺杆温度': current_screw_temperature,
                        '后机筒温度': current_rear_barrel_temperature,
                        '前机筒温度': current_front_barrel_temperature,
                        '机头温度': current_head_temperature
                    }
                    # æ ¹æ®æ¨¡åž‹ç‰¹å¾åˆ›å»ºè¾“å…¥DataFrame
                    input_df = pd.DataFrame([input_data])[features]
                    if model_info['model_type'] in ['LSTM', 'GRU', 'BiLSTM']:
                        # æ·±åº¦å­¦ä¹ æ¨¡åž‹å¤„理逻辑
                        import torch
                        # æ£€æŸ¥æ˜¯å¦æœ‰ç¼©æ”¾å™¨
                        if has_scalers:
                            scaled_input = scaler_X.transform(input_df)
                        else:
                            scaled_input = input_df.values
                        # èŽ·å–åºåˆ—é•¿åº¦
                        sequence_length = model_info.get('sequence_length', 10)
                        # åˆ›å»ºåºåˆ—数据
                        input_seq = np.tile(scaled_input, (sequence_length, 1)).reshape(1, sequence_length, -1)
                        # è½¬æ¢ä¸ºPyTorch张量
                        device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
                        input_tensor = torch.tensor(input_seq, dtype=torch.float32).to(device)
                        # é¢„测
                        model.eval()
                        with torch.no_grad():
                            y_pred_scaled_tensor = model(input_tensor)
                            y_pred_scaled = y_pred_scaled_tensor.cpu().numpy().ravel()[0]
                            if has_scalers:
                                # åç¼©æ”¾é¢„测结果
                                predicted_weight = scaler_y.inverse_transform(np.array([[y_pred_scaled]]))[0][0]
                            else:
                                predicted_weight = y_pred_scaled
                    elif has_scalers:
                        # ç‰¹å¾ç¼©æ”¾
                        scaled_input = scaler_X.transform(input_df)
                        # æ¨¡åž‹é¢„测
                        scaled_prediction = model.predict(scaled_input)[0]
                        # åç¼©æ”¾é¢„测结果
                        predicted_weight = scaler_y.inverse_transform(np.array([[scaled_prediction]]))[0][0]
                    else:
                        # ç›´æŽ¥é¢„测,不使用缩放器
                        predicted_weight = model.predict(input_df)[0]
                    return predicted_weight
                def objective_function(params):
                    """多目标目标函数"""
                    screw_speed_val, process_speed_val = params
                    # é¢„测米重
                    predicted_weight_val = predict_weight(screw_speed_val, process_speed_val)
                    # è®¡ç®—目标1:米重偏差
                    deviation_val = abs(predicted_weight_val - standard_weight)
                    deviation_percent_val = abs((predicted_weight_val - standard_weight) / standard_weight * 100)
                    # è®¡ç®—目标2:调整幅度
                    screw_speed_adjustment_val = abs(screw_speed_val - current_screw_speed)
                    process_speed_adjustment_val = abs(process_speed_val - current_process_speed)
                    screw_speed_adjustment_percent_val = abs((screw_speed_val - current_screw_speed) / current_screw_speed * 100)
                    process_speed_adjustment_percent_val = abs((process_speed_val - current_process_speed) / current_process_speed * 100)
                    # åŠ æƒç›®æ ‡å‡½æ•°
                    objective_val = (weight_deviation * deviation_percent_val +
                                    weight_adjustment * (screw_speed_adjustment_percent_val + process_speed_adjustment_percent_val) / 2)
                    return objective_val, predicted_weight_val, deviation_percent_val, \
                           screw_speed_adjustment_percent_val, process_speed_adjustment_percent_val
                # å‚数约束
                def is_within_constraints(params):
                    """检查参数是否在约束范围内"""
                    screw_speed, process_speed = params
                    # èžºæ†è½¬é€Ÿçº¦æŸ
                    if not (30 <= screw_speed <= 500):
                        return False
                    # æµç¨‹ä¸»é€Ÿçº¦æŸ
                    if not (0 <= process_speed <= 200):
                        return False
                    # å•次调整幅度约束
                    screw_speed_adjustment_percent = abs((screw_speed - current_screw_speed) / current_screw_speed * 100)
                    if screw_speed_adjustment_percent > max_screw_speed_adjust:
                        return False
                    process_speed_adjustment_percent = abs((process_speed - current_process_speed) / current_process_speed * 100)
                    if process_speed_adjustment_percent > max_process_speed_adjust:
                        return False
                    return True
                # ç²’子群优化算法
                def particle_swarm_optimization(objective, constraints, bounds, n_particles=30, n_iterations=50):
                    """粒子群优化算法"""
                    # åˆå§‹åŒ–粒子
                    particles = []
                    velocities = []
                    best_positions = []
                    best_scores = []
                    # è¾¹ç•Œ
                    (screw_speed_min, screw_speed_max), (process_speed_min, process_speed_max) = bounds
                    for _ in range(n_particles):
                        # éšæœºåˆå§‹åŒ–位置
                        while True:
                            screw_speed = np.random.uniform(screw_speed_min, screw_speed_max)
                            process_speed = np.random.uniform(process_speed_min, process_speed_max)
                            pos = [screw_speed, process_speed]
                            if constraints(pos):
                                break
                        particles.append(pos)
                        velocities.append([0.0, 0.0])
                        best_positions.append(pos.copy())
                        # è®¡ç®—初始得分
                        score, _, _, _, _ = objective(pos)
                        best_scores.append(score)
                    # å…¨å±€æœ€ä¼˜
                    global_best_idx = np.argmin(best_scores)
                    global_best_position = best_positions[global_best_idx].copy()
                    global_best_score = best_scores[global_best_idx]
                    # PSO参数
                    w = 0.5  # æƒ¯æ€§æƒé‡
                    c1 = 1.5  # è®¤çŸ¥ç³»æ•°
                    c2 = 1.5  # ç¤¾ä¼šç³»æ•°
                    # è¿­ä»£ä¼˜åŒ–
                    for _ in range(n_iterations):
                        for i in range(n_particles):
                            # æ›´æ–°é€Ÿåº¦
                            r1, r2 = np.random.rand(), np.random.rand()
                            velocities[i][0] = (w * velocities[i][0] +
                                              c1 * r1 * (best_positions[i][0] - particles[i][0]) +
                                              c2 * r2 * (global_best_position[0] - particles[i][0]))
                            velocities[i][1] = (w * velocities[i][1] +
                                              c1 * r1 * (best_positions[i][1] - particles[i][1]) +
                                              c2 * r2 * (global_best_position[1] - particles[i][1]))
                            # æ›´æ–°ä½ç½®
                            new_pos = [
                                particles[i][0] + velocities[i][0],
                                particles[i][1] + velocities[i][1]
                            ]
                            # æ£€æŸ¥çº¦æŸ
                            if constraints(new_pos):
                                particles[i] = new_pos
                                # æ›´æ–°ä¸ªä½“最优
                                current_score, _, _, _, _ = objective(new_pos)
                                if current_score < best_scores[i]:
                                    best_scores[i] = current_score
                                    best_positions[i] = new_pos.copy()
                                    # æ›´æ–°å…¨å±€æœ€ä¼˜
                                    if current_score < global_best_score:
                                        global_best_score = current_score
                                        global_best_position = new_pos.copy()
                    return global_best_position, global_best_score
                # è®¾ç½®å‚数边界
                screw_speed_bounds = [
                    current_screw_speed * (1 - max_screw_speed_adjust / 100),
                    current_screw_speed * (1 + max_screw_speed_adjust / 100)
                ]
                process_speed_bounds = [
                    current_process_speed * (1 - max_process_speed_adjust / 100),
                    current_process_speed * (1 + max_process_speed_adjust / 100)
                ]
                # æ‰§è¡Œä¼˜åŒ–
                best_params, best_score = particle_swarm_optimization(
                    objective_function,
                    is_within_constraints,
                    [(screw_speed_bounds[0], screw_speed_bounds[1]),
                     (process_speed_bounds[0], process_speed_bounds[1])]
                )
                # èŽ·å–æœ€ä¼˜å‚æ•°çš„è¯¦ç»†ä¿¡æ¯
                best_screw_speed, best_process_speed = best_params
                best_objective, best_predicted_weight, best_deviation_percent, \
                best_screw_adjust_percent, best_process_adjust_percent = objective_function(best_params)
                # è®¡ç®—当前参数的预测结果
                current_predicted_weight = predict_weight(current_screw_speed, current_process_speed)
                current_deviation_percent = abs((current_predicted_weight - standard_weight) / standard_weight * 100)
                # ç”Ÿæˆè°ƒæ•´å»ºè®®æ–‡æœ¬
                def generate_recommendation():
                    """生成调整建议文本"""
                    screw_speed_change = "提高" if best_screw_speed > current_screw_speed else "降低"
                    process_speed_change = "提高" if best_process_speed > current_process_speed else "降低"
                    recommendation = f"米重偏差 {current_deviation_percent:.2f}%,建议调整:\n"
                    recommendation += f"1. å°†èžºæ†è½¬é€Ÿä»Ž {current_screw_speed:.1f} rpm {screw_speed_change} è‡³ {best_screw_speed:.1f} rpm "
                    recommendation += f"( {screw_speed_change} {abs(best_screw_adjust_percent):.2f}% )\n"
                    recommendation += f"2. å°†æµç¨‹ä¸»é€Ÿä»Ž {current_process_speed:.1f} m/min {process_speed_change} è‡³ {best_process_speed:.1f} m/min "
                    recommendation += f"( {process_speed_change} {abs(best_process_adjust_percent):.2f}% )\n"
                    recommendation += f"\n预期效果:米重偏差将降至 {best_deviation_percent:.2f}%"
                    return recommendation
                recommendation = generate_recommendation()
                # æž„建调整结果
                adjustment_result = {
                    'status': '正常范围' if best_predicted_weight >= lower_limit and best_predicted_weight <= upper_limit else \
                              '超上限' if best_predicted_weight > upper_limit else '超下限',
                    'real_time_weight': actual_weight,
                    'standard_weight': standard_weight,
                    'upper_limit': upper_limit,
                    'lower_limit': lower_limit,
                    'current_screw_speed': current_screw_speed,
                    'current_process_speed': current_process_speed,
                    'new_screw_speed': best_screw_speed,
                    'new_process_speed': best_process_speed,
                    'predicted_weight': best_predicted_weight,
                    'current_predicted_weight': current_predicted_weight,
                    'deviation_percentage': (actual_weight - standard_weight) / standard_weight * 100,
                    'current_predicted_deviation_percent': current_deviation_percent,
                    'predicted_deviation_percent': best_deviation_percent,
                    'screw_speed_adjustment': best_screw_speed - current_screw_speed,
                    'process_speed_adjustment': best_process_speed - current_process_speed,
                    'screw_speed_adjust_percent': best_screw_adjust_percent,
                    'process_speed_adjust_percent': best_process_adjust_percent,
                    'recommendation': recommendation,
                    'weight_deviation': weight_deviation,
                    'weight_adjustment': weight_adjustment,
                    'best_objective_score': best_score
                }
                # ä¿å­˜åˆ°åŽ†å²è®°å½•
                history_record = {
                    'timestamp': datetime.now(),
                    'model_file': st.session_state.get('selected_model_file', '未知模型'),
                    'standard_weight': standard_weight,
                    'upper_limit': upper_limit,
                    'lower_limit': lower_limit,
                    'actual_weight': actual_weight,
                    'current_screw_speed': current_screw_speed,
                    'current_process_speed': current_process_speed,
                    'adjustment_result': adjustment_result
                }
                # æ·»åŠ åˆ°ä¼šè¯çŠ¶æ€çš„åŽ†å²è®°å½•
                st.session_state['optimized_adjustment_history'].append(history_record)
                # é™åˆ¶åŽ†å²è®°å½•æ•°é‡
                if len(st.session_state['optimized_adjustment_history']) > 100:
                    st.session_state['optimized_adjustment_history'] = st.session_state['optimized_adjustment_history'][-100:]
                # 5. ç»“果展示
                st.success("优化调节建议计算完成!")
                st.subheader("📋 ä¼˜åŒ–调节建议结果")
                # 5.1 ç±³é‡çŠ¶æ€
                if adjustment_result['status'] == "正常范围":
                    st.success(f"米重状态: {adjustment_result['status']}")
                else:
                    st.warning(f"米重状态: {adjustment_result['status']}")
                # 5.2 åå·®ä¿¡æ¯
                st.markdown("### ðŸ“Š åå·®ä¿¡æ¯")
                info_cols = st.columns(3)
                info_cols[0].metric("实时米重", f"{adjustment_result['real_time_weight']:.4f} Kg/m")
                info_cols[1].metric("标准米重", f"{adjustment_result['standard_weight']:.4f} Kg/m")
                info_cols[2].metric("偏差百分比", f"{adjustment_result['deviation_percentage']:.2f}%")
                # 5.3 é¢„测结果对比
                st.markdown("### ðŸ“ˆ é¢„测结果对比")
                pred_cols = st.columns(3)
                pred_cols[0].metric("当前参数预测米重", f"{adjustment_result['current_predicted_weight']:.4f} Kg/m")
                pred_cols[1].metric("优化后预测米重", f"{adjustment_result['predicted_weight']:.4f} Kg/m")
                pred_cols[2].metric("预测偏差改善", f"{adjustment_result['current_predicted_deviation_percent'] - adjustment_result['predicted_deviation_percent']:.2f}%")
                # 5.4 å…³é”®è°ƒæ•´å»ºè®®
                st.markdown("### ðŸ”‘ å…³é”®è°ƒæ•´å»ºè®®")
                st.info(adjustment_result['recommendation'])
                # 5.5 å‚数调整对比
                st.markdown("### ðŸ“Š å‚æ•°è°ƒæ•´å¯¹æ¯”")
                param_compare_df = pd.DataFrame({
                    '参数名称': ['螺杆转速', '流程主速'],
                    '当前值': [adjustment_result['current_screw_speed'], adjustment_result['current_process_speed']],
                    '优化建议值': [adjustment_result['new_screw_speed'], adjustment_result['new_process_speed']],
                    '调整幅度': [f"{adjustment_result['screw_speed_adjust_percent']:.2f}%",
                               f"{adjustment_result['process_speed_adjust_percent']:.2f}%"]
                })
                # é«˜äº®æ˜¾ç¤ºè°ƒæ•´å¹…度
                def highlight_adjustment(val):
                    if isinstance(val, str) and '%' in val:
                        try:
                            percent = float(val.strip('%'))
                            if percent > 0:
                                return 'background-color: #90EE90'  # ç»¿è‰²è¡¨ç¤ºå¢žåŠ 
                            elif percent < 0:
                                return 'background-color: #FFB6C1'  # çº¢è‰²è¡¨ç¤ºå‡å°‘
                        except:
                            pass
                    return ''
                styled_df = param_compare_df.style.applymap(highlight_adjustment, subset=['调整幅度'])
                st.dataframe(styled_df, use_container_width=True, hide_index=True)
                # 5.6 å¯è§†åŒ–对比
                fig = go.Figure()
                fig.add_trace(go.Bar(
                    x=param_compare_df['参数名称'],
                    y=param_compare_df['当前值'],
                    name='当前值',
                    marker_color='blue'
                ))
                fig.add_trace(go.Bar(
                    x=param_compare_df['参数名称'],
                    y=param_compare_df['优化建议值'],
                    name='优化建议值',
                    marker_color='green'
                ))
                fig.update_layout(
                    barmode='group',
                    title='参数调整对比',
                    yaxis_title='数值',
                    height=400
                )
                st.plotly_chart(fig, use_container_width=True)
                # 5.7 ä¼˜åŒ–效果可视化
                st.markdown("### ðŸ“‰ ä¼˜åŒ–效果")
                # ç›®æ ‡å‡½æ•°å€¼å¯¹æ¯”
                fig_objective = go.Figure()
                fig_objective.add_trace(go.Bar(
                    x=['当前参数', '优化后参数'],
                    y=[
                        weight_deviation * current_deviation_percent + weight_adjustment * 0,
                        weight_deviation * best_deviation_percent + weight_adjustment * \
                        (abs(best_screw_adjust_percent) + abs(best_process_adjust_percent)) / 2
                    ],
                    marker_color=['red', 'green']
                ))
                fig_objective.update_layout(
                    title='多目标优化函数值对比',
                    yaxis_title='目标函数值(越小越好)',
                    height=300
                )
                st.plotly_chart(fig_objective, use_container_width=True)
    # 6. åŽ†å²è®°å½•å±•ç¤º
    st.subheader("📚 åŽ†å²è°ƒèŠ‚è®°å½•")
    if 'optimized_adjustment_history' in st.session_state and st.session_state['optimized_adjustment_history']:
        # æ˜¾ç¤ºåŽ†å²è®°å½•æ•°é‡
        st.write(f"共 {len(st.session_state['optimized_adjustment_history'])} æ¡åŽ†å²è®°å½•")
        # åˆ†é¡µæ˜¾ç¤º
        page_size = 10
        total_pages = (len(st.session_state['optimized_adjustment_history']) + page_size - 1) // page_size
        page = st.selectbox(
            "选择页码",
            options=range(1, total_pages + 1),
            key="history_page"
        )
        start_idx = (page - 1) * page_size
        end_idx = start_idx + page_size
        paginated_history = st.session_state['optimized_adjustment_history'][start_idx:end_idx]
        # åå‘显示,最新记录在前面
        for record in reversed(paginated_history):
            with st.expander(f"记录时间: {record['timestamp'].strftime('%Y-%m-%d %H:%M:%S')} | æ¨¡åž‹: {record['model_file']}"):
                history_cols = st.columns(3)
                with history_cols[0]:
                    st.write("**米重参数**")
                    st.write(f"- æ ‡å‡†ç±³é‡: {record['standard_weight']:.4f} Kg/m")
                    st.write(f"- ç±³é‡ä¸Šé™: {record['upper_limit']:.4f} Kg/m")
                    st.write(f"- ç±³é‡ä¸‹é™: {record['lower_limit']:.4f} Kg/m")
                    st.write(f"- å®žé™…米重: {record['actual_weight']:.4f} Kg/m")
                with history_cols[1]:
                    st.write("**速度参数**")
                    st.write(f"- å½“前螺杆转速: {record['current_screw_speed']:.1f} rpm")
                    st.write(f"- å½“前流程主速: {record['current_process_speed']:.1f} m/min")
                    st.write(f"- å»ºè®®èžºæ†è½¬é€Ÿ: {record['adjustment_result']['new_screw_speed']:.1f} rpm")
                    st.write(f"- å»ºè®®æµç¨‹ä¸»é€Ÿ: {record['adjustment_result']['new_process_speed']:.1f} m/min")
                with history_cols[2]:
                    st.write("**预测结果**")
                    st.write(f"- å½“前预测米重: {record['adjustment_result']['current_predicted_weight']:.4f} Kg/m")
                    st.write(f"- ä¼˜åŒ–后预测米重: {record['adjustment_result']['predicted_weight']:.4f} Kg/m")
                    st.write(f"- å½“前预测偏差: {record['adjustment_result']['current_predicted_deviation_percent']:.2f}%")
                    st.write(f"- ä¼˜åŒ–后预测偏差: {record['adjustment_result']['predicted_deviation_percent']:.2f}%")
                st.write("**调整建议**")
                st.write(record['adjustment_result']['recommendation'])
    else:
        st.info("暂无历史调节记录")
    # 7. å¸®åŠ©è¯´æ˜Ž
    with st.expander("❓ å¸®åŠ©è¯´æ˜Ž"):
        st.markdown("""
        ### åŠŸèƒ½è¯´æ˜Ž
        æœ¬åŠŸèƒ½æ¨¡å—åŸºäºŽå¤šç›®æ ‡ä¼˜åŒ–ç®—æ³•ï¼Œæ ¹æ®å½“å‰ç±³é‡æµ‹é‡å€¼å’ŒæŒ¤å‡ºæœºå‚æ•°ï¼Œä¸ºç”¨æˆ·æä¾›ç§‘å­¦åˆç†çš„å‚æ•°è°ƒæ•´å»ºè®®ã€‚
        ### ä¼˜åŒ–算法
        - **算法类型**: ç²’子群优化算法(PSO)
        - **优化目标**:
          1. æœ€å°åŒ–米重偏差
          2. æœ€å°åŒ–参数调整幅度
        - **约束条件**:
          - èžºæ†è½¬é€Ÿå’Œæµç¨‹ä¸»é€Ÿä¸Šä¸‹é™
          - å•次最大调整幅度
        ### ç»“果解读
        - **米重状态**: æ˜¾ç¤ºä¼˜åŒ–后预测米重是否在允许范围内
        - **偏差信息**: å½“前米重与标准米重的偏差
        - **预测结果对比**: å½“前参数和优化后参数的预测米重对比
        - **调整建议**: è¯¦ç»†çš„参数调整建议
        - **参数对比**: ç›´è§‚展示参数调整幅度
        - **优化效果**: å¤šç›®æ ‡ä¼˜åŒ–函数值对比
        ### æ³¨æ„äº‹é¡¹
        1. è¯·ç¡®ä¿è¾“入的参数值准确反映设备当前状态
        2. ä¼˜åŒ–建议仅供参考,实际操作时请结合现场经验
        3. å»ºè®®åœ¨è°ƒæ•´å‚数后密切观察米重变化
        4. å®šæœŸæ›´æ–°æ¨¡åž‹ä»¥æé«˜å»ºè®®çš„准确性
        """)
# é¡µé¢å…¥å£
if __name__ == "__main__":
    show_optimized_parameter_adjustment()
dashboard.py
@@ -12,6 +12,7 @@
from app.pages.metered_weight_prediction import show_metered_weight_prediction
from app.pages.metered_weight_forecast import show_metered_weight_forecast
from app.pages.extruder_parameter_adjustment import show_extruder_parameter_adjustment
from app.pages.optimized_parameter_adjustment import show_optimized_parameter_adjustment
# è®¾ç½®é¡µé¢é…ç½®
st.set_page_config(
@@ -112,6 +113,13 @@
    url_path="extruder_parameter_adjustment"
)
optimized_parameter_adjustment_page = st.Page(
    show_optimized_parameter_adjustment,
    title="优化版挤出机参数调节",
    icon="🚀",
    url_path="optimized_parameter_adjustment"
)
# ä¾§è¾¹æ é¡µè„šä¿¡æ¯
def show_footer():
    st.sidebar.markdown("---")
@@ -119,7 +127,7 @@
# å¯¼èˆªé…ç½®
pg = st.navigation({
    "综合分析": [comprehensive_page, metered_weight_page, metered_weight_correlation_page, metered_weight_regression_page, metered_weight_advanced_page, metered_weight_deep_learning_page, metered_weight_steady_state_page, metered_weight_prediction_page, metered_weight_forecast_page, extruder_parameter_adjustment_page],
    "综合分析": [comprehensive_page, metered_weight_page, metered_weight_correlation_page, metered_weight_regression_page, metered_weight_advanced_page, metered_weight_deep_learning_page, metered_weight_steady_state_page, metered_weight_prediction_page, metered_weight_forecast_page, extruder_parameter_adjustment_page, optimized_parameter_adjustment_page],
    "分项分析": [sorting_page, extruder_page, main_process_page]
})