分享图
动画工坊
引擎就绪
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>边缘AI智能工况自适应控制系统 - IFR演示</title>
    <style>
        :root {
            --bg-color: #030712;
            --panel-bg: rgba(17, 24, 39, 0.7);
            --border-color: rgba(55, 65, 81, 0.5);
            --text-main: #E5E7EB;
            --text-muted: #9CA3AF;
            --accent-cyan: #00F0FF;
            --accent-amber: #FFB000;
            --accent-green: #00FF66;
            --font-ui: 'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
            --font-mono: 'JetBrains Mono', 'Courier New', monospace;
        }

        body, html {
            margin: 0;
            padding: 0;
            width: 100vw;
            height: 100vh;
            background-color: var(--bg-color);
            background-image: 
                radial-gradient(circle at 50% 50%, rgba(0, 240, 255, 0.05) 0%, transparent 60%),
                linear-gradient(rgba(255, 255, 255, 0.02) 1px, transparent 1px),
                linear-gradient(90deg, rgba(255, 255, 255, 0.02) 1px, transparent 1px);
            background-size: 100% 100%, 40px 40px, 40px 40px;
            overflow: hidden;
            font-family: var(--font-ui);
            color: var(--text-main);
            display: flex;
            justify-content: center;
            align-items: center;
        }

        /* 绝对定位的角标文字,严格控制空间占比 */
        .overlay-ui {
            position: absolute;
            z-index: 10;
            pointer-events: none;
            font-size: 12px;
            letter-spacing: 0.5px;
        }

        .top-left { top: 20px; left: 24px; }
        .top-right { top: 20px; right: 24px; text-align: right; }
        .bottom-left { bottom: 20px; left: 24px; }
        .bottom-right { bottom: 20px; right: 24px; text-align: right; }

        .ui-panel {
            background: var(--panel-bg);
            backdrop-filter: blur(8px);
            border: 1px solid var(--border-color);
            border-radius: 6px;
            padding: 12px 16px;
            box-shadow: 0 4px 20px rgba(0,0,0,0.5);
        }

        .title {
            font-size: 14px;
            font-weight: 600;
            color: var(--accent-cyan);
            margin-bottom: 4px;
            text-transform: uppercase;
            letter-spacing: 1px;
        }

        .subtitle {
            color: var(--text-muted);
            font-size: 11px;
        }

        .status-indicator {
            display: flex;
            align-items: center;
            gap: 6px;
            margin-top: 8px;
            font-family: var(--font-mono);
        }

        .dot {
            width: 6px;
            height: 6px;
            border-radius: 50%;
            background: var(--accent-green);
            box-shadow: 0 0 8px var(--accent-green);
            animation: pulse 2s infinite;
        }

        .data-row {
            display: flex;
            justify-content: space-between;
            gap: 16px;
            margin-top: 4px;
            font-family: var(--font-mono);
            color: var(--accent-amber);
        }

        /* 核心动画容器 */
        #animation-container {
            width: 100%;
            height: 100%;
            max-width: 1200px;
            max-height: 800px;
            position: relative;
        }

        svg {
            width: 100%;
            height: 100%;
            display: block;
        }

        /* SVG 样式与动画 */
        .path-line {
            fill: none;
            stroke: var(--border-color);
            stroke-width: 2;
        }
        
        .path-flow {
            fill: none;
            stroke: var(--accent-cyan);
            stroke-width: 2;
            stroke-dasharray: 8 12;
            opacity: 0.3;
            animation: flowDash 30s linear infinite;
        }

        .data-particle {
            fill: var(--accent-cyan);
            filter: drop-shadow(0 0 5px var(--accent-cyan));
        }

        .cmd-particle {
            fill: var(--accent-amber);
            filter: drop-shadow(0 0 5px var(--accent-amber));
        }

        .node-bg {
            fill: rgba(17, 24, 39, 0.8);
            stroke: rgba(55, 65, 81, 1);
            stroke-width: 1.5;
        }

        .text-svg {
            font-family: var(--font-ui);
            font-size: 12px;
            fill: var(--text-muted);
            text-anchor: middle;
            user-select: none;
        }
        
        .text-svg-value {
            font-family: var(--font-mono);
            font-size: 14px;
            fill: var(--text-main);
            text-anchor: middle;
            font-weight: bold;
        }

        .ai-core-hex {
            fill: rgba(0, 240, 255, 0.05);
            stroke: var(--accent-cyan);
            stroke-width: 2;
            transition: all 0.3s ease;
        }

        .ai-core-glow {
            fill: none;
            stroke: var(--accent-cyan);
            stroke-width: 1;
            opacity: 0;
            transition: opacity 0.3s;
        }

        .optimizing .ai-core-hex {
            fill: rgba(0, 240, 255, 0.2);
            filter: drop-shadow(0 0 20px rgba(0, 240, 255, 0.6));
        }
        .optimizing .ai-core-glow {
            opacity: 1;
            animation: pulse-ring 1s cubic-bezier(0.215, 0.61, 0.355, 1) infinite;
        }

        .bar-bg { fill: rgba(255,255,255,0.05); rx: 2; }
        .bar-fill { fill: var(--accent-cyan); rx: 2; transition: height 0.5s ease, y 0.5s ease; }
        
        .actuator-part {
            fill: rgba(255, 176, 0, 0.1);
            stroke: var(--accent-amber);
            stroke-width: 1.5;
            transition: transform 1.5s cubic-bezier(0.4, 0, 0.2, 1);
        }

        @keyframes flowDash {
            to { stroke-dashoffset: -1000; }
        }

        @keyframes pulse {
            0%, 100% { opacity: 1; }
            50% { opacity: 0.4; }
        }

        @keyframes pulse-ring {
            0% { transform: scale(0.95); stroke-width: 2; opacity: 1; }
            100% { transform: scale(1.3); stroke-width: 0; opacity: 0; }
        }
    </style>
</head>
<body>

    <!-- 左上角:标题与状态 -->
    <div class="overlay-ui top-left ui-panel">
        <div class="title">理想最终解 (IFR) 演示</div>
        <div class="subtitle">基于历史数据的参数自适应优化</div>
        <div class="status-indicator">
            <div class="dot"></div>
            <span>边缘 AI 在线监听中</span>
        </div>
    </div>

    <!-- 右上角:约束条件 -->
    <div class="overlay-ui top-right ui-panel">
        <div class="subtitle" style="color:var(--text-main)">系统约束指标</div>
        <div class="data-row">
            <span>优化周期:</span>
            <span>30 min (加速展示)</span>
        </div>
        <div class="data-row">
            <span>执行器动作限制:</span>
            <span>≤ 6次 / 班</span>
        </div>
        <div class="data-row">
            <span>当前班次动作:</span>
            <span id="ui-action-count" style="color:var(--accent-green)">2 / 6</span>
        </div>
    </div>

    <!-- 左下角:原理说明 -->
    <div class="overlay-ui bottom-left ui-panel" style="max-width: 240px;">
        <div class="subtitle">资源利用与机理</div>
        <div style="color: var(--text-muted); margin-top: 4px; line-height: 1.4;">
            利用现有传感器数据,通过离线贝叶斯优化建立模型,在线下发参数至 PLC。消除人工经验偏差,在极少动作下实现防跑浆。
        </div>
    </div>

    <!-- 中央核心动画区域 -->
    <div id="animation-container">
        <svg viewBox="0 0 1000 600" preserveAspectRatio="xMidYMid meet">
            <defs>
                <!-- 滤镜与渐变 -->
                <filter id="glow-cyan" x="-20%" y="-20%" width="140%" height="140%">
                    <feGaussianBlur stdDeviation="5" result="blur" />
                    <feComposite in="SourceGraphic" in2="blur" operator="over" />
                </filter>
                <filter id="glow-amber" x="-20%" y="-20%" width="140%" height="140%">
                    <feGaussianBlur stdDeviation="5" result="blur" />
                    <feComposite in="SourceGraphic" in2="blur" operator="over" />
                </filter>
                <linearGradient id="grad-bar" x1="0" y1="1" x2="0" y2="0">
                    <stop offset="0%" stop-color="#0055ff"/>
                    <stop offset="100%" stop-color="#00F0FF"/>
                </linearGradient>
            </defs>

            <!-- 连线路径 (底层) -->
            <g id="connections">
                <!-- 传感器到AI -->
                <path id="path-s1-ai" class="path-line" d="M 250 150 C 350 150, 400 300, 470 300" />
                <path id="path-s2-ai" class="path-line" d="M 250 300 C 350 300, 350 300, 470 300" />
                <path id="path-s3-ai" class="path-line" d="M 250 450 C 350 450, 400 300, 470 300" />
                
                <path class="path-flow" d="M 250 150 C 350 150, 400 300, 470 300" />
                <path class="path-flow" d="M 250 300 C 350 300, 350 300, 470 300" />
                <path class="path-flow" d="M 250 450 C 350 450, 400 300, 470 300" />

                <!-- AI到执行器 (PLC接口) -->
                <path id="path-ai-a1" class="path-line" d="M 530 300 C 600 300, 650 200, 750 200" />
                <path id="path-ai-a2" class="path-line" d="M 530 300 C 600 300, 650 400, 750 400" />
            </g>

            <!-- 动态粒子容器 -->
            <g id="particles-layer"></g>

            <!-- 左侧:实时工况传感器 -->
            <g id="sensors" transform="translate(100, 0)">
                <!-- 密度 -->
                <g transform="translate(0, 120)">
                    <rect x="0" y="0" width="150" height="60" class="node-bg" rx="4"/>
                    <text x="75" y="22" class="text-svg">泥浆密度 (g/cm³)</text>
                    <text x="75" y="45" class="text-svg-value" id="val-density" fill="#00F0FF">1.24</text>
                    <!-- 动态波形条 -->
                    <rect x="15" y="52" width="120" height="2" fill="rgba(255,255,255,0.1)"/>
                    <rect id="bar-density" x="15" y="52" width="60" height="2" fill="var(--accent-cyan)"/>
                </g>

                <!-- 排量 -->
                <g transform="translate(0, 270)">
                    <rect x="0" y="0" width="150" height="60" class="node-bg" rx="4"/>
                    <text x="75" y="22" class="text-svg">排量 (L/s)</text>
                    <text x="75" y="45" class="text-svg-value" id="val-flow" fill="#00F0FF">45.2</text>
                    <rect x="15" y="52" width="120" height="2" fill="rgba(255,255,255,0.1)"/>
                    <rect id="bar-flow" x="15" y="52" width="80" height="2" fill="var(--accent-cyan)"/>
                </g>

                <!-- 振动 -->
                <g transform="translate(0, 420)">
                    <rect x="0" y="0" width="150" height="60" class="node-bg" rx="4"/>
                    <text x="75" y="22" class="text-svg">振动速度 (mm/s)</text>
                    <text x="75" y="45" class="text-svg-value" id="val-vib" fill="#00F0FF">6.8</text>
                    <rect x="15" y="52" width="120" height="2" fill="rgba(255,255,255,0.1)"/>
                    <rect id="bar-vib" x="15" y="52" width="40" height="2" fill="var(--accent-cyan)"/>
                </g>
                
                <!-- 装饰性虚线框 -->
                <rect x="-15" y="90" width="180" height="420" fill="none" stroke="rgba(255,255,255,0.05)" stroke-dasharray="4 4" rx="8"/>
                <text x="75" y="105" class="text-svg" style="font-size: 10px;">实时工况感知层</text>
            </g>

            <!-- 中央:边缘AI优化器 -->
            <g id="ai-module" transform="translate(500, 300)">
                <!-- 装饰环 -->
                <circle cx="0" cy="0" r="70" fill="none" stroke="rgba(255,255,255,0.05)" stroke-width="1" />
                <circle cx="0" cy="0" r="85" fill="none" stroke="rgba(255,255,255,0.02)" stroke-width="20" />
                
                <!-- 发光动效外环 -->
                <polygon points="0,-60 52,-30 52,30 0,60 -52,30 -52,-30" class="ai-core-glow" />
                <!-- 核心六边形 -->
                <polygon points="0,-50 43,-25 43,25 0,50 -43,25 -43,-25" class="ai-core-hex" />
                
                <!-- 内部元素 -->
                <circle cx="0" cy="0" r="20" fill="rgba(0, 240, 255, 0.1)" stroke="var(--accent-cyan)" stroke-width="1"/>
                <path d="M -10 -5 L 10 0 L -10 5 Z" fill="var(--accent-cyan)" opacity="0.8">
                    <animateTransform attributeName="transform" type="rotate" from="0" to="360" dur="4s" repeatCount="indefinite"/>
                </path>
                
                <text x="0" y="-80" class="text-svg" style="fill: var(--accent-cyan); font-weight: bold;">边缘 AI 优化核心</text>
                <text x="0" y="85" class="text-svg" style="font-size:10px;">多目标遗传算法寻优</text>
                
                <!-- 优化状态提示 -->
                <g id="opt-status" opacity="0" transform="translate(0, -20)">
                    <rect x="-40" y="-12" width="80" height="24" rx="12" fill="var(--accent-green)" />
                    <text x="0" y="4" class="text-svg" style="fill: #000; font-weight: bold; font-size: 11px;">参数更新</text>
                </g>
            </g>

            <!-- 右侧:物理执行器 (PLC控制) -->
            <g id="actuators" transform="translate(750, 0)">
                <rect x="-20" y="120" width="180" height="340" fill="none" stroke="rgba(255,176,0,0.15)" stroke-dasharray="4 4" rx="8"/>
                <text x="70" y="140" class="text-svg" style="fill: var(--accent-amber); font-size: 10px;">PLC 控制执行层</text>

                <!-- 堰板抬升机构 -->
                <g transform="translate(0, 170)">
                    <rect x="0" y="0" width="140" height="80" class="node-bg" rx="4"/>
                    <text x="70" y="20" class="text-svg">最佳堰板高度 (mm)</text>
                    <text x="70" y="40" class="text-svg-value" id="val-weir" fill="#FFB000">120</text>
                    
                    <!-- 机械结构示意 -->
                    <g transform="translate(70, 70)">
                        <rect x="-30" y="-15" width="60" height="4" fill="rgba(255,255,255,0.2)"/>
                        <!-- 动态抬升的堰板 -->
                        <g id="mech-weir" class="actuator-part">
                            <rect x="-20" y="-15" width="40" height="6" rx="2" />
                            <path d="M 0 -15 L 0 -25" stroke="var(--accent-amber)" stroke-width="2"/>
                        </g>
                    </g>
                </g>

                <!-- 放液阀门机构 -->
                <g transform="translate(0, 360)">
                    <rect x="0" y="0" width="140" height="80" class="node-bg" rx="4"/>
                    <text x="70" y="20" class="text-svg">放液开度匹配 (%)</text>
                    <text x="70" y="40" class="text-svg-value" id="val-valve" fill="#FFB000">45</text>
                    
                    <!-- 阀门开度示意 -->
                    <g transform="translate(70, 65)">
                        <circle cx="0" cy="0" r="15" fill="none" stroke="rgba(255,255,255,0.2)" stroke-width="3"/>
                        <!-- 动态旋转的阀门 -->
                        <g id="mech-valve" class="actuator-part">
                            <line x1="-12" y1="0" x2="12" y2="0" stroke="var(--accent-amber)" stroke-width="4" stroke-linecap="round"/>
                        </g>
                    </g>
                </g>
            </g>
        </svg>
    </div>

    <script>
        document.addEventListener("DOMContentLoaded", () => {
            // 系统状态变量
            const state = {
                density: 1.20,
                flow: 40.0,
                vib: 5.0,
                weirHeight: 100, // 初始高度
                valveOpen: 30,   // 初始开度
                actionCount: 2,
                maxActions: 6
            };

            // DOM 元素引用
            const els = {
                dVal: document.getElementById('val-density'),
                fVal: document.getElementById('val-flow'),
                vVal: document.getElementById('val-vib'),
                dBar: document.getElementById('bar-density'),
                fBar: document.getElementById('bar-flow'),
                vBar: document.getElementById('bar-vib'),
                aiModule: document.getElementById('ai-module'),
                optStatus: document.getElementById('opt-status'),
                weirVal: document.getElementById('val-weir'),
                valveVal: document.getElementById('val-valve'),
                mechWeir: document.getElementById('mech-weir'),
                mechValve: document.getElementById('mech-valve'),
                particles: document.getElementById('particles-layer'),
                actionCountUi: document.getElementById('ui-action-count')
            };

            // SVG 路径长度用于动画
            const paths = {
                s1: document.getElementById('path-s1-ai'),
                s2: document.getElementById('path-s2-ai'),
                s3: document.getElementById('path-s3-ai'),
                a1: document.getElementById('path-ai-a1'),
                a2: document.getElementById('path-ai-a2')
            };

            // 缓动函数
            const lerp = (start, end, amt) => (1 - amt) * start + amt * end;

            // 1. 模拟传感器实时数据波动 (每100ms微调)
            function updateSensors() {
                // 加入随机游走
                state.density = Math.max(1.0, Math.min(1.8, state.density + (Math.random() - 0.5) * 0.05));
                state.flow = Math.max(20, Math.min(80, state.flow + (Math.random() - 0.5) * 2));
                state.vib = Math.max(1, Math.min(15, state.vib + (Math.random() - 0.5) * 0.5));

                // 更新UI数值
                els.dVal.textContent = state.density.toFixed(2);
                els.fVal.textContent = state.flow.toFixed(1);
                els.vVal.textContent = state.vib.toFixed(1);

                // 更新条形图宽度映射
                els.dBar.setAttribute('width', ((state.density - 1) / 0.8) * 120);
                els.fBar.setAttribute('width', (state.flow / 80) * 120);
                els.vBar.setAttribute('width', (state.vib / 15) * 120);
            }
            setInterval(updateSensors, 200);

            // 2. 数据流粒子系统
            function createParticle(pathNode, isCommand = false) {
                const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
                circle.setAttribute("r", isCommand ? "4" : "3");
                circle.setAttribute("class", isCommand ? "cmd-particle" : "data-particle");
                els.particles.appendChild(circle);

                const length = pathNode.getTotalLength();
                let start = null;
                const duration = isCommand ? 1000 : 1500; // 命令下发更快

                function animate(timestamp) {
                    if (!start) start = timestamp;
                    const progress = (timestamp - start) / duration;
                    
                    if (progress < 1) {
                        const pt = pathNode.getPointAtLength(progress * length);
                        circle.setAttribute("cx", pt.x);
                        circle.setAttribute("cy", pt.y);
                        // 添加拖尾透明度效果
                        circle.setAttribute("opacity", 1 - Math.pow(progress, 3));
                        requestAnimationFrame(animate);
                    } else {
                        circle.remove();
                    }
                }
                requestAnimationFrame(animate);
            }

            // 持续生成传感器到AI的数据流
            setInterval(() => {
                createParticle(paths.s1);
                createParticle(paths.s2);
                createParticle(paths.s3);
            }, 800);

            // 3. 核心优化周期 (模拟30min触发一次,动画中设为8秒)
            function triggerOptimization() {
                // A. AI 处理动效
                els.aiModule.classList.add('optimizing');
                
                // 状态文本浮现
                els.optStatus.style.transition = "opacity 0.3s, transform 0.5s ease-out";
                els.optStatus.setAttribute("opacity", "1");
                els.optStatus.setAttribute("transform", "translate(0, -40)");

                // B. 计算理想解 (简化的规则,代表复杂的遗传算法结果)
                // 密度高,排量大 -> 需要更高堰板和更小开度防跑浆
                let targetWeir = 100 + (state.density - 1.2) * 100 + (state.flow - 40) * 0.5;
                let targetValve = 30 - (state.density - 1.2) * 50 + (state.flow - 40) * 0.2;
                
                // 限制物理边界
                targetWeir = Math.max(50, Math.min(250, targetWeir));
                targetValve = Math.max(10, Math.min(90, targetValve));

                // 检查是否需要动作 (为了满足≤6次的约束,只有差异大于阈值才动作)
                const weirDiff = Math.abs(targetWeir - state.weirHeight);
                const valveDiff = Math.abs(targetValve - state.valveOpen);
                
                setTimeout(() => {
                    els.aiModule.classList.remove('optimizing');
                    els.optStatus.setAttribute("opacity", "0");
                    els.optStatus.setAttribute("transform", "translate(0, -20)");

                    if (weirDiff > 10 || valveDiff > 5) {
                        // 发送控制指令
                        createParticle(paths.a1, true);
                        createParticle(paths.a2, true);
                        
                        // 延迟等待指令到达后执行机械动作
                        setTimeout(() => executeActuators(targetWeir, targetValve), 1000);
                    }
                }, 1500); // AI处理耗时
            }

            // 4. 执行器动作
            function executeActuators(tWeir, tValve) {
                // 更新动作计数 (约束展示)
                if (state.actionCount < state.maxActions) {
                    state.actionCount++;
                    els.actionCountUi.textContent = `${state.actionCount} / ${state.maxActions}`;
                    if(state.actionCount === state.maxActions) {
                        els.actionCountUi.style.color = "var(--accent-amber)";
                    }
                }

                // 数值动画
                let startWeir = state.weirHeight;
                let startValve = state.valveOpen;
                let startTime = null;
                const duration = 1500;

                function animateValues(timestamp) {
                    if(!startTime) startTime = timestamp;
                    const progress = Math.min(1, (timestamp - startTime) / duration);
                    // easeInOutQuad
                    const ease = progress < 0.5 ? 2 * progress * progress : 1 - Math.pow(-2 * progress + 2, 2) / 2;

                    state.weirHeight = lerp(startWeir, tWeir, ease);
                    state.valveOpen = lerp(startValve, tValve, ease);

                    els.weirVal.textContent = Math.round(state.weirHeight);
                    els.valveVal.textContent = Math.round(state.valveOpen);

                    // 物理形态变化映射
                    // 堰板高度: Y轴负向位移 (-15 是基准,位移量基于数值)
                    const yOffset = -15 - ((state.weirHeight - 50) / 200) * 20; 
                    els.mechWeir.style.transform = `translateY(${yOffset - (-15)}px)`;
                    
                    // 阀门开度: 旋转角度映射
                    const rotAngle = ((state.valveOpen - 10) / 80) * 90; // 0-90度
                    els.mechValve.style.transform = `rotate(${rotAngle}deg)`;

                    if(progress < 1) {
                        requestAnimationFrame(animateValues);
                    }
                }
                requestAnimationFrame(animateValues);
            }

            // 启动循环
            setTimeout(() => {
                triggerOptimization(); // 首次较快触发
                setInterval(triggerOptimization, 8000); // 后续每8秒循环
            }, 1000);
        });
    </script>
</body>
</html>
积分规则:每轮对话扣10分