独立渲染引擎就绪引擎就绪
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>多源传感融合与加权表决原理</title>
<style>
:root {
--bg-color: #07090f;
--grid-color: rgba(30, 40, 70, 0.4);
--color-safe: #00f0ff;
--color-safe-dim: rgba(0, 240, 255, 0.2);
--color-safe-glow: rgba(0, 240, 255, 0.6);
--color-warn: #ffb800;
--color-danger: #ff0055;
--color-danger-dim: rgba(255, 0, 85, 0.2);
--color-danger-glow: rgba(255, 0, 85, 0.6);
--text-main: #a3adc2;
--text-bright: #ffffff;
--font-mono: 'Fira Code', 'Roboto Mono', 'Consolas', monospace;
--font-sans: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
body, html {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
background-color: var(--bg-color);
display: flex;
justify-content: center;
align-items: center;
font-family: var(--font-sans);
overflow: hidden;
color: var(--text-main);
}
.anim-container {
width: 100%;
height: 100%;
max-width: 1200px;
max-height: 800px;
position: relative;
box-shadow: inset 0 0 100px rgba(0, 0, 0, 0.8);
}
svg {
width: 100%;
height: 100%;
filter: drop-shadow(0 0 20px rgba(0,0,0,0.5));
}
/* Typography */
.text-sm { font-size: 11px; letter-spacing: 1px; }
.text-md { font-size: 14px; font-weight: 500; }
.text-lg { font-size: 24px; font-weight: bold; font-family: var(--font-mono); }
.font-mono { font-family: var(--font-mono); }
.label { fill: var(--text-main); text-anchor: start; alignment-baseline: middle; }
.label-right { fill: var(--text-main); text-anchor: end; alignment-baseline: middle; }
.value { fill: var(--text-bright); text-anchor: end; alignment-baseline: middle; }
/* Elements */
.sensor-box {
fill: rgba(15, 20, 35, 0.8);
stroke: rgba(255,255,255,0.1);
stroke-width: 1;
transition: all 0.3s ease;
}
.plc-core {
fill: rgba(10, 15, 30, 0.9);
stroke: var(--color-safe);
stroke-width: 2;
transition: stroke 0.3s ease;
}
.plc-inner {
fill: none;
stroke: var(--color-safe-dim);
stroke-width: 1;
stroke-dasharray: 4 4;
}
/* Data Streams */
.pipe-base {
fill: none;
stroke: rgba(255, 255, 255, 0.05);
stroke-width: 6;
stroke-linecap: round;
}
.pipe-flow {
fill: none;
stroke-width: 2;
stroke-linecap: round;
stroke-dasharray: 8 20;
animation: flow 1s linear infinite;
transition: stroke 0.3s ease;
}
.pipe-core {
fill: none;
stroke: #fff;
stroke-width: 1;
opacity: 0.3;
}
@keyframes flow {
to { stroke-dashoffset: -28; }
}
@keyframes rotate {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
@keyframes pulse-danger {
0% { filter: drop-shadow(0 0 5px var(--color-danger)); stroke: var(--color-danger); }
50% { filter: drop-shadow(0 0 20px var(--color-danger)); stroke: #ff4d79; }
100% { filter: drop-shadow(0 0 5px var(--color-danger)); stroke: var(--color-danger); }
}
/* States */
.state-safe .sensor-box { stroke: var(--color-safe-dim); }
.state-safe .pipe-flow { stroke: var(--color-safe); filter: drop-shadow(0 0 5px var(--color-safe)); }
.state-danger .sensor-box { stroke: var(--color-danger); fill: rgba(255, 0, 85, 0.1); }
.state-danger .pipe-flow { stroke: var(--color-danger); filter: drop-shadow(0 0 8px var(--color-danger)); }
.state-danger .val-text { fill: var(--color-danger); font-weight: bold; }
.vote-node { fill: #1a2235; stroke: #334155; stroke-width: 2; transition: all 0.3s; }
.vote-node.active-safe { fill: var(--color-safe); stroke: var(--color-safe-glow); box-shadow: 0 0 10px var(--color-safe); }
.vote-node.active-danger { fill: var(--color-danger); stroke: var(--color-danger-glow); }
.sys-danger .plc-core { animation: pulse-danger 1s infinite; }
.sys-danger .output-flow { stroke: var(--color-danger); filter: drop-shadow(0 0 10px var(--color-danger)); }
.ring { transform-origin: 550px 350px; }
.ring-spin-slow { animation: rotate 20s linear infinite; }
.ring-spin-fast { animation: rotate 10s linear infinite reverse; }
/* UI Overlays */
.info-panel {
position: absolute;
bottom: 20px;
left: 30px;
background: rgba(10, 15, 25, 0.8);
border-left: 3px solid var(--color-safe);
padding: 12px 20px;
backdrop-filter: blur(4px);
pointer-events: none;
}
.info-title { color: #fff; margin: 0 0 5px 0; font-size: 13px; font-weight: 600; letter-spacing: 1px;}
.info-desc { color: var(--text-main); margin: 0; font-size: 11px; line-height: 1.6; max-width: 300px;}
.stage-indicator {
position: absolute;
top: 20px;
left: 50%;
transform: translateX(-50%);
font-size: 12px;
color: var(--color-safe);
background: rgba(0, 240, 255, 0.1);
padding: 6px 16px;
border-radius: 20px;
border: 1px solid var(--color-safe-dim);
letter-spacing: 2px;
transition: all 0.5s;
}
</style>
</head>
<body>
<div class="anim-container">
<div class="stage-indicator font-mono" id="stageText">SYSTEM NORMAL - 监测中</div>
<svg viewBox="0 0 1100 700" preserveAspectRatio="xMidYMid meet">
<defs>
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="var(--grid-color)" stroke-width="0.5" />
<circle cx="0" cy="0" r="1" fill="var(--grid-color)" />
</pattern>
<linearGradient id="meter-grad" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="var(--color-danger)" />
<stop offset="50%" stop-color="var(--color-warn)" />
<stop offset="100%" stop-color="var(--color-safe)" />
</linearGradient>
<filter id="glow-core">
<feGaussianBlur stdDeviation="4" result="coloredBlur"/>
<feMerge>
<feMergeNode in="coloredBlur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
<!-- Background -->
<rect width="100%" height="100%" fill="url(#grid)" />
<!-- Decorative Tech Lines -->
<path d="M 50 50 L 150 50 L 170 70 L 1050 70 L 1070 50 L 1070 20" fill="none" stroke="rgba(255,255,255,0.05)" stroke-width="1" />
<text x="55" y="45" class="text-sm font-mono" fill="rgba(255,255,255,0.3)">SYS_FUSION_V2.1</text>
<text x="1065" y="45" class="text-sm font-mono label-right" fill="rgba(255,255,255,0.3)">CYCLE: 100ms</text>
<!-- Data Pipes -->
<g id="pipes">
<!-- S1 to PLC -->
<path d="M 220 180 C 350 180, 380 290, 480 290" class="pipe-base" />
<path d="M 220 180 C 350 180, 380 290, 480 290" class="pipe-flow" id="flow1" />
<path d="M 220 180 C 350 180, 380 290, 480 290" class="pipe-core" />
<!-- S2 to PLC -->
<path d="M 220 280 C 330 280, 380 330, 480 330" class="pipe-base" />
<path d="M 220 280 C 330 280, 380 330, 480 330" class="pipe-flow" id="flow2" />
<path d="M 220 280 C 330 280, 380 330, 480 330" class="pipe-core" />
<!-- S3 to PLC -->
<path d="M 220 420 C 330 420, 380 370, 480 370" class="pipe-base" />
<path d="M 220 420 C 330 420, 380 370, 480 370" class="pipe-flow" id="flow3" />
<path d="M 220 420 C 330 420, 380 370, 480 370" class="pipe-core" />
<!-- S4 to PLC -->
<path d="M 220 520 C 350 520, 380 410, 480 410" class="pipe-base" />
<path d="M 220 520 C 350 520, 380 410, 480 410" class="pipe-flow" id="flow4" />
<path d="M 220 520 C 350 520, 380 410, 480 410" class="pipe-core" />
<!-- PLC to Output -->
<path d="M 620 350 L 780 350" class="pipe-base" />
<path d="M 620 350 L 780 350" class="pipe-flow output-flow" id="flow-out" />
<path d="M 620 350 L 780 350" class="pipe-core" />
</g>
<!-- Sensors (Input) -->
<g id="sensors">
<!-- Sensor 1 -->
<g id="s1" transform="translate(60, 150)" class="state-safe">
<rect x="0" y="0" width="160" height="60" rx="4" class="sensor-box" />
<text x="15" y="25" class="text-sm label">CH1 液位 (雷达)</text>
<text x="145" y="45" class="text-lg font-mono value val-text" id="val1">2.4m</text>
<circle cx="10" cy="42" r="3" fill="var(--color-safe)" />
</g>
<!-- Sensor 2 -->
<g id="s2" transform="translate(60, 250)" class="state-safe">
<rect x="0" y="0" width="160" height="60" rx="4" class="sensor-box" />
<text x="15" y="25" class="text-sm label">CH2 进料流量 (电磁)</text>
<text x="145" y="45" class="text-lg font-mono value val-text" id="val2">120L/s</text>
<circle cx="10" cy="42" r="3" fill="var(--color-safe)" />
</g>
<!-- Sensor 3 -->
<g id="s3" transform="translate(60, 390)" class="state-safe">
<rect x="0" y="0" width="160" height="60" rx="4" class="sensor-box" />
<text x="15" y="25" class="text-sm label">CH3 压力 (变送器)</text>
<text x="145" y="45" class="text-lg font-mono value val-text" id="val3">0.4MPa</text>
<circle cx="10" cy="42" r="3" fill="var(--color-safe)" />
</g>
<!-- Sensor 4 -->
<g id="s4" transform="translate(60, 490)" class="state-safe">
<rect x="0" y="0" width="160" height="60" rx="4" class="sensor-box" />
<text x="15" y="25" class="text-sm label">CH4 筛网背压</text>
<text x="145" y="45" class="text-lg font-mono value val-text" id="val4">1.2kPa</text>
<circle cx="10" cy="42" r="3" fill="var(--color-safe)" />
</g>
</g>
<!-- Central PLC / Algorithm Engine -->
<g id="plc" transform="translate(0, 0)">
<!-- Decorative Rings -->
<circle cx="550" cy="350" r="110" fill="none" stroke="rgba(255,255,255,0.03)" stroke-width="20" class="ring ring-spin-slow" stroke-dasharray="100 20 40 20" />
<circle cx="550" cy="350" r="95" fill="none" stroke="rgba(0,240,255,0.1)" stroke-width="2" class="ring ring-spin-fast" stroke-dasharray="10 30" />
<!-- Main PLC Body -->
<path d="M 490 230 L 610 230 L 640 350 L 610 470 L 490 470 L 460 350 Z" class="plc-core" />
<path d="M 500 245 L 600 245 L 625 350 L 600 455 L 500 455 L 475 350 Z" class="plc-inner" />
<!-- Headers -->
<text x="550" y="270" text-anchor="middle" class="text-md text-bright" style="letter-spacing: 2px;">加权表决核心</text>
<text x="550" y="290" text-anchor="middle" class="text-sm font-mono" fill="var(--text-main)">ALGO_FUSION</text>
<!-- Voting Logic Visualization (2 out of 4) -->
<g transform="translate(550, 360)">
<rect x="-40" y="-30" width="80" height="60" rx="4" fill="rgba(0,0,0,0.5)" stroke="#334155" />
<text x="0" y="-40" text-anchor="middle" class="text-sm font-mono" fill="#a3adc2">2/4 LOGIC GATE</text>
<!-- 4 Signal Receivers -->
<circle cx="-20" cy="-10" r="6" id="vote1" class="vote-node" />
<circle cx="20" cy="-10" r="6" id="vote2" class="vote-node" />
<circle cx="-20" cy="15" r="6" id="vote3" class="vote-node" />
<circle cx="20" cy="15" r="6" id="vote4" class="vote-node" />
</g>
<!-- Internal condition line -->
<path d="M 550 400 L 550 430" stroke="#334155" stroke-width="2" stroke-dasharray="2 2"/>
<text x="550" y="445" text-anchor="middle" class="text-sm font-mono" id="core-status" fill="var(--color-safe)">≥ 2 THRESHOLD: FALSE</text>
</g>
<!-- Output Gauge / Action -->
<g id="output" transform="translate(780, 250)">
<!-- Outer Bezel -->
<rect x="0" y="0" width="220" height="200" rx="8" fill="rgba(10, 15, 25, 0.9)" stroke="rgba(255,255,255,0.1)" />
<text x="110" y="30" text-anchor="middle" class="text-md label">综合跑浆危险度</text>
<!-- Gauge Background -->
<path d="M 40 160 A 80 80 0 1 1 180 160" fill="none" stroke="#1a2235" stroke-width="12" stroke-linecap="round" />
<!-- Gauge Value (Animated via JS) -->
<path id="gauge-fill" d="M 40 160 A 80 80 0 1 1 180 160" fill="none" stroke="url(#meter-grad)" stroke-width="12" stroke-linecap="round" stroke-dasharray="351" stroke-dashoffset="351" style="transition: stroke-dashoffset 0.5s ease-out;" />
<!-- Center Value -->
<text x="110" y="110" text-anchor="middle" class="text-lg font-mono text-bright" id="risk-val" style="font-size: 32px;">0%</text>
<!-- Action Status -->
<rect x="40" y="140" width="140" height="30" rx="15" fill="#111" stroke="#334155" />
<text x="110" y="160" text-anchor="middle" class="text-sm font-mono" id="action-text" fill="var(--text-main)">系统运行正常</text>
</g>
</svg>
<!-- Contextual Information Panel (Strict constraint: small, unobtrusive) -->
<div class="info-panel">
<h3 class="info-title">IFR: 无冗余的高可靠决策</h3>
<p class="info-desc">利用现有4类物理信号进行交叉校验。避免增加额外硬件复杂度,通过 2/4 加权表决逻辑,彻底消除单一传感器故障(如液位计挂泥)导致的系统误动作,实现精准响应。</p>
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", () => {
// DOM Elements
const s1 = document.getElementById('s1');
const s3 = document.getElementById('s3');
const v1 = document.getElementById('val1');
const v3 = document.getElementById('val3');
const vote1 = document.getElementById('vote1');
const vote2 = document.getElementById('vote2');
const vote3 = document.getElementById('vote3');
const vote4 = document.getElementById('vote4');
const plc = document.getElementById('plc');
const coreStatus = document.getElementById('core-status');
const flowOut = document.getElementById('flow-out');
const gaugeFill = document.getElementById('gauge-fill');
const riskVal = document.getElementById('risk-val');
const actionText = document.getElementById('action-text');
const stageText = document.getElementById('stageText');
// Gauge circumference calculation for stroke-dashoffset
// r=80, angle= ~270 degrees. Length roughly 351.
const GAUGE_MAX = 351;
function setGauge(percent) {
const offset = GAUGE_MAX - (percent / 100) * GAUGE_MAX;
gaugeFill.style.strokeDashoffset = offset;
riskVal.textContent = Math.round(percent) + '%';
}
// Normal values with slight jitter
const baseV1 = 2.4;
const baseV3 = 0.40;
let time = 0;
const LOOP_DURATION = 15000; // 15 seconds per story loop
function simulationLoop() {
time += 100; // 100ms refresh cycle
if (time >= LOOP_DURATION) time = 0;
// --- Phase Logic ---
// 0 - 3s: Normal Operations
// 3 - 7s: Single Sensor Fault (Sensor 1 - Radar Level gives false high)
// 7 - 11s: Dual Sensor Fault (Sensor 1 + Sensor 3 - Real Danger)
// 11 - 15s: Recovery to Normal
let isS1Fault = time > 3000 && time < 11000;
let isS3Fault = time > 7000 && time < 11000;
// 1. Update Sensor Data & UI
if (isS1Fault) {
s1.className.baseVal = "state-danger";
v1.textContent = (8.5 + Math.random()*0.5).toFixed(1) + 'm ↑';
vote1.className.baseVal = "vote-node active-danger";
} else {
s1.className.baseVal = "state-safe";
v1.textContent = (baseV1 + Math.random()*0.2 - 0.1).toFixed(2) + 'm';
vote1.className.baseVal = "vote-node active-safe";
}
// Sensor 2 & 4 are stable for this demonstration
vote2.className.baseVal = "vote-node active-safe";
vote4.className.baseVal = "vote-node active-safe";
if (isS3Fault) {
s3.className.baseVal = "state-danger";
v3.textContent = (1.2 + Math.random()*0.1).toFixed(2) + 'MPa ↑';
vote3.className.baseVal = "vote-node active-danger";
} else {
s3.className.baseVal = "state-safe";
v3.textContent = (baseV3 + Math.random()*0.05 - 0.02).toFixed(2) + 'MPa';
vote3.className.baseVal = "vote-node active-safe";
}
// 2. PLC Logic (2 out of 4)
let faultCount = (isS1Fault ? 1 : 0) + (isS3Fault ? 1 : 0);
let isSystemAlarm = faultCount >= 2;
// 3. Update Central Core & Output
if (isSystemAlarm) {
plc.parentNode.classList.add('sys-danger'); // Add to root SVG or group
coreStatus.textContent = "≥ 2 THRESHOLD: TRUE (ALARM)";
coreStatus.setAttribute('fill', 'var(--color-danger)');
setGauge(92 + Math.random()*5);
actionText.textContent = "⚠ 动作触发:切断进料";
actionText.setAttribute('fill', 'var(--color-danger)');
stageText.textContent = "CRITICAL - 融合算法确认为真实危险,触发动作";
stageText.style.color = "var(--color-danger)";
stageText.style.borderColor = "var(--color-danger)";
stageText.style.background = "rgba(255,0,85,0.1)";
} else if (faultCount === 1) {
plc.parentNode.classList.remove('sys-danger');
coreStatus.textContent = "≥ 2 THRESHOLD: FALSE (IGNORE)";
coreStatus.setAttribute('fill', 'var(--color-warn)');
setGauge(35 + Math.random()*5);
actionText.textContent = "单一异常:交叉校验不通过";
actionText.setAttribute('fill', 'var(--color-warn)');
stageText.textContent = "WARNING - 单路传感器异常,表决拦截,避免误报";
stageText.style.color = "var(--color-warn)";
stageText.style.borderColor = "var(--color-warn)";
stageText.style.background = "rgba(255,184,0,0.1)";
} else {
plc.parentNode.classList.remove('sys-danger');
coreStatus.textContent = "≥ 2 THRESHOLD: FALSE";
coreStatus.setAttribute('fill', 'var(--color-safe)');
setGauge(15 + Math.random()*5);
actionText.textContent = "系统运行正常";
actionText.setAttribute('fill', 'var(--text-main)');
stageText.textContent = "SYSTEM NORMAL - 多源信号稳定融合中";
stageText.style.color = "var(--color-safe)";
stageText.style.borderColor = "var(--color-safe-dim)";
stageText.style.background = "rgba(0,240,255,0.1)";
}
}
// Start animation loop immediately
setInterval(simulationLoop, 100);
simulationLoop(); // Initial call
});
</script>
</body>
</html>
积分规则:每轮对话扣10分
等待动画代码生成...
