项目实战:从0到1搭建系统 | 高炉炼铁智能体系列
📌 系列导航 :第1期-高炉炼铁基础 | 第18期-模型调优 | 第19期-项目实战 | 第20期-效果评估
📖 引言
理论学了很多,如何落地实践?
从本篇文章开始,我们将进入一个全新的阶段------项目实战 。前面我们学习了高炉炼铁的基础知识、智能体设计原理、数据采集处理、预测模型构建、预警调控系统等多个模块。现在,是时候把这些知识串联起来,从零开始搭建一个完整的高炉智能体系统了!
本文将详细介绍项目的整体规划、技术架构、核心模块实现,以及项目实施的最佳实践,帮助你将理论知识转化为可运行的工业级系统。
🎯 一、项目规划与目标
1.1 项目背景
某钢铁企业拥有4座大型高炉,设计年产铁水量800万吨。传统的高炉监控系统存在以下问题:
┌─────────────────────────────────────────────────────────────────┐
│ 传统系统痛点分析 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 🔴 数据孤岛:各系统数据分散,无法综合分析 │
│ 🔴 人工经验:依赖老师傅经验,传承困难 │
│ 🔴 响应滞后:问题发生后才处理,损失已造成 │
│ 🔴 效率低下:工艺优化靠试错,成本高、周期长 │
│ 🔴 预测缺失:无法预知设备故障和工艺异常 │
│ │
└─────────────────────────────────────────────────────────────────┘
1.2 项目目标
| 目标类型 | 具体指标 | 量化标准 |
|---|---|---|
| 预测精度 | 温度预测误差 | MAE < 10°C |
| 预警能力 | 提前预警时间 | 提前30分钟以上 |
| 预警准确率 | 正确预警比例 | > 90% |
| 经济效益 | 焦比降低 | > 5 kg/t |
| 产量提升 | 铁水产量增加 | > 2% |
1.3 项目里程碑
┌─────────────────────────────────────────────────────────────────────┐
│ 项目实施计划 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 第1-2周 │ ████ │ 数据准备:数据采集、清洗、标注 │
│ │ │ Deliverable: 清洗后的训练数据集 │
│ │
│ 第3-5周 │ ████████ │ 模型开发:预测模型、决策引擎 │
│ │ │ Deliverable: 模型训练代码、调优后的模型 │
│ │
│ 第6-8周 │ ██████████████ │ 系统开发:后端服务、前端界面 │
│ │ │ Deliverable: 完整的Web系统 │
│ │
│ 第9-10周 │ ██████████ │ 测试验证:功能测试、性能测试 │
│ │ │ Deliverable: 测试报告、上线许可 │
│ │
│ 第11周 │ ██████ │ 上线部署:生产环境部署、监控 │
│ │ │ Deliverable: 正式运行的智能体系统 │
│ │
└─────────────────────────────────────────────────────────────────────┘
🏗️ 二、系统架构设计
2.1 整体技术架构
┌──────────────────────────────────────────────────────────────────────┐
│ 展示层 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌───────────┐ │
│ │ PC监控台 │ │ 移动APP │ │ 数据大屏 │ │ 告警推送 │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └─────┬─────┘ │
└─────────┼─────────────────┼─────────────────┼────────────────┼──────┘
│ │ │ │
└─────────────────┴────────┬────────┴────────────────┘
│ REST API / WebSocket
┌────────────────────────────────────┼────────────────────────────────────┐
│ 服务层 │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────┐ │
│ │ API网关 │ │ 业务服务 │ │ 消息服务 │ │ 调度服务 │ │
│ │ (Kong/Nginx)│ │ (FastAPI) │ │ (RabbitMQ) │ │ (Celery) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ └──────────┘ │
└────────────────────────────────────┬────────────────────────────────────┘
│
┌────────────────────────────────────┼────────────────────────────────────┐
│ 数据层 │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────┐ │
│ │ 时序数据库 │ │ 关系数据库 │ │ 缓存 │ │ 文件存储 │ │
│ │ (InfluxDB) │ │ (PostgreSQL)│ │ (Redis) │ │ (MinIO) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ └──────────┘ │
└────────────────────────────────────┬────────────────────────────────────┘
│
┌────────────────────────────────────┼────────────────────────────────────┐
│ 感知层 │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 温度传感器 │ │ 压力传感器 │ │ 流量传感器 │ │
│ │ (热电偶) │ │ (压力变送器) │ │ (电磁流量计)│ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└──────────────────────────────────────────────────────────────────────────┘
2.2 核心模块划分
python
# 项目模块结构
blast_furnace_system/
├── data/ # 数据模块
│ ├── collectors/ # 数据采集
│ ├── processors/ # 数据处理
│ └── storage/ # 数据存储
│
├── models/ # 模型模块
│ ├── predictors/ # 预测模型
│ ├── detectors/ # 异常检测
│ └── optimizers/ # 工艺优化
│
├── services/ # 服务模块
│ ├── api/ # API服务
│ ├── websocket/ # WebSocket服务
│ └── alarm/ # 告警服务
│
├── frontend/ # 前端模块
│ ├── web/ # Web应用
│ └── mobile/ # 移动应用
│
└── utils/ # 工具模块
├── logger/ # 日志工具
├── monitor/ # 监控工具
└── config/ # 配置工具
📊 三、数据准备阶段
3.1 数据采集方案
python
# 数据采集器实现
import time
from datetime import datetime
import pymysql
from influxdb import InfluxDBClient
class BlastFurnaceDataCollector:
"""高炉数据采集器"""
def __init__(self, config: dict):
self.influx_client = InfluxDBClient(
host=config['influxdb']['host'],
port=config['influxdb']['port'],
database=config['influxdb']['database']
)
self.mysql_client = pymysql.connect(**config['mysql'])
self.collection_interval = config.get('interval', 10) # 10秒采集一次
def collect_realtime_data(self, furnace_id: int):
"""采集实时传感器数据"""
# 从PLC/SCADA系统获取数据
data_points = [
{
"measurement": "furnace_sensor",
"tags": {
"furnace_id": furnace_id,
"sensor_type": "temperature"
},
"fields": {
"value": self.read_temperature(furnace_id),
"unit": "celsius"
},
"time": datetime.utcnow().isoformat()
},
{
"measurement": "furnace_sensor",
"tags": {
"furnace_id": furnace_id,
"sensor_type": "pressure"
},
"fields": {
"value": self.read_pressure(furnace_id),
"unit": "kpa"
},
"time": datetime.utcnow().isoformat()
},
{
"measurement": "furnace_sensor",
"tags": {
"furnace_id": furnace_id,
"sensor_type": "blast_volume"
},
"fields": {
"value": self.read_blast_volume(furnace_id),
"unit": "nm3_per_min"
},
"time": datetime.utcnow().isoformat()
}
]
# 写入InfluxDB
self.influx_client.write_points(data_points)
return True
def run(self):
"""持续运行采集"""
while True:
try:
for furnace_id in range(1, 5): # 4座高炉
self.collect_realtime_data(furnace_id)
time.sleep(self.collection_interval)
except Exception as e:
print(f"Collection error: {e}")
time.sleep(60) # 出错后等待1分钟再重试
3.2 数据清洗与标注
python
# 数据清洗流程
class DataCleaner:
"""数据清洗器"""
def clean(self, df: pd.DataFrame) -> pd.DataFrame:
# 1. 缺失值处理
df = self.handle_missing_values(df)
# 2. 异常值处理
df = self.remove_outliers(df)
# 3. 数据标准化
df = self.normalize(df)
# 4. 数据对齐
df = self.align_timestamps(df)
return df
def handle_missing_values(self, df: pd.DataFrame) -> pd.DataFrame:
"""使用线性插值处理缺失值"""
return df.interpolate(method='linear', limit_direction='both')
def remove_outliers(self, df: pd.DataFrame, n_std=3) -> pd.DataFrame:
"""使用3σ原则移除异常值"""
numeric_cols = df.select_dtypes(include=[np.number]).columns
for col in numeric_cols:
mean = df[col].mean()
std = df[col].std()
lower = mean - n_std * std
upper = mean + n_std * std
df = df[(df[col] >= lower) & (df[col] <= upper)]
return df
class DataLabeler:
"""数据标注器 - 标注异常标签"""
def __init__(self, thresholds: dict):
self.thresholds = thresholds
def label(self, df: pd.DataFrame) -> pd.DataFrame:
"""标注异常标签"""
df['is_abnormal'] = 0
# 温度超限
df.loc[df['temperature'] > self.thresholds['temp_high'], 'is_abnormal'] = 1
df.loc[df['temperature'] < self.thresholds['temp_low'], 'is_abnormal'] = 1
# 压力异常
df.loc[df['pressure'] > self.thresholds['pressure_high'], 'is_abnormal'] = 1
df.loc[df['pressure'] < self.thresholds['pressure_low'], 'is_abnormal'] = 1
# 变化率异常
df['temp_change'] = df['temperature'].diff()
df.loc[df['temp_change'].abs() > self.thresholds['temp_change_rate'], 'is_abnormal'] = 1
return df
🧠 四、模型开发阶段
4.1 预测模型构建
python
# 温度预测模型
import torch
import torch.nn as nn
class BlastFurnacePredictor(nn.Module):
"""高炉温度预测模型 - CNN-LSTM-Attention架构"""
def __init__(self, input_dim=20, seq_len=60, hidden_dim=128, num_layers=2):
super().__init__()
# CNN特征提取
self.conv1 = nn.Conv1d(input_dim, 64, kernel_size=3, padding=1)
self.conv2 = nn.Conv1d(64, 128, kernel_size=3, padding=1)
self.pool = nn.MaxPool1d(2)
self.relu = nn.ReLU()
self.dropout = nn.Dropout(0.2)
# LSTM时序建模
self.lstm = nn.LSTM(
input_size=128,
hidden_size=hidden_dim,
num_layers=num_layers,
batch_first=True,
bidirectional=True
)
# 注意力机制
self.attention = nn.MultiheadAttention(
embed_dim=hidden_dim * 2,
num_heads=8,
dropout=0.1
)
# 全连接层
self.fc = nn.Sequential(
nn.Linear(hidden_dim * 2, 64),
nn.ReLU(),
self.dropout,
nn.Linear(64, 1)
)
def forward(self, x):
# x: [batch, seq_len, features]
x = x.permute(0, 2, 1) # [batch, features, seq_len]
# CNN特征提取
x = self.relu(self.conv1(x))
x = self.pool(x)
x = self.relu(self.conv2(x))
x = self.pool(x)
x = x.permute(0, 2, 1) # [batch, seq_len/4, features]
# LSTM
lstm_out, _ = self.lstm(x)
# Attention
attn_out, _ = self.attention(lstm_out, lstm_out, lstm_out)
# 取最后时间步
out = self.fc(attn_out[:, -1, :])
return out
class ModelPipeline:
"""模型训练流水线"""
def __init__(self, config: dict):
self.config = config
self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
self.model = None
def prepare_data(self):
"""准备训练数据"""
# 加载数据
df = pd.read_csv('data/blast_furnace_data.csv')
# 特征工程
engineer = FeatureEngineer()
df = engineer.create_features(df)
# 数据分割
train_size = int(len(df) * 0.8)
train_df = df[:train_size]
test_df = df[train_size:]
# 转换为张量
X_train = torch.FloatTensor(train_df.drop('temperature', axis=1).values)
y_train = torch.FloatTensor(train_df['temperature'].values)
X_test = torch.FloatTensor(test_df.drop('temperature', axis=1).values)
y_test = torch.FloatTensor(test_df['temperature'].values)
return X_train, y_train, X_test, y_test
def train(self):
"""训练模型"""
X_train, y_train, X_test, y_test = self.prepare_data()
# 创建模型
self.model = BlastFurnacePredictor(
input_dim=X_train.shape[1],
seq_len=self.config.get('seq_len', 60)
).to(self.device)
# 训练循环
criterion = nn.MSELoss()
optimizer = torch.optim.AdamW(self.model.parameters(), lr=0.001)
for epoch in range(100):
# 训练
self.model.train()
optimizer.zero_grad()
outputs = self.model(X_train.to(self.device))
loss = criterion(outputs, y_train.unsqueeze(1).to(self.device))
loss.backward()
optimizer.step()
# 验证
if epoch % 10 == 0:
self.model.eval()
with torch.no_grad():
val_outputs = self.model(X_test.to(self.device))
val_loss = criterion(val_outputs, y_test.unsqueeze(1).to(self.device))
print(f"Epoch {epoch}: Train Loss = {loss.item():.4f}, Val Loss = {val_loss.item():.4f}")
# 保存模型
torch.save(self.model.state_dict(), 'models/best_predictor.pth')
4.2 异常检测模型
python
# 异常检测器
class AnomalyDetector:
"""基于孤立森林的异常检测器"""
def __init__(self):
from sklearn.ensemble import IsolationForest
self.model = IsolationForest(
n_estimators=200,
contamination=0.05, # 假设5%的数据是异常的
random_state=42
)
self.threshold = None
def fit(self, X: np.ndarray):
"""训练"""
self.model.fit(X)
# 计算异常分数阈值
scores = self.model.decision_function(X)
self.threshold = np.percentile(scores, 5)
def predict(self, X: np.ndarray) -> np.ndarray:
"""预测 - 返回异常标签"""
scores = self.model.decision_function(X)
return (scores < self.threshold).astype(int)
def score(self, X: np.ndarray) -> np.ndarray:
"""返回异常分数"""
return self.model.decision_function(X)
🏗️ 五、系统开发阶段
5.1 后端API服务
python
# FastAPI后端服务
from fastapi import FastAPI, WebSocket, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from typing import List, Optional
import torch
import numpy as np
app = FastAPI(title="高炉智能体API", version="1.0.0")
# CORS配置
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 全局变量
predictor = None
anomaly_detector = None
@app.on_event("startup")
async def load_models():
"""启动时加载模型"""
global predictor, anomaly_detector
# 加载预测模型
predictor = BlastFurnacePredictor()
predictor.load_state_dict(torch.load('models/best_predictor.pth'))
predictor.eval()
# 加载异常检测器
anomaly_detector = AnomalyDetector()
anomaly_detector.model = joblib.load('models/anomaly_detector.pkl')
class MonitorData(BaseModel):
furnace_id: int
temperature: float
pressure: float
blast_volume: float
humidity: float
coke_ratio: float
ore_ratio: float
class PredictionResponse(BaseModel):
furnace_id: int
predicted_temp: float
confidence: float
is_anomaly: bool
anomaly_score: float
@app.get("/health")
async def health_check():
"""健康检查"""
return {"status": "healthy", "service": "blast-furnace-api"}
@app.get("/api/v1/furnaces")
async def get_furnaces():
"""获取所有高炉状态"""
# 从InfluxDB获取最新数据
data = query_influxdb("SELECT * FROM furnace_status ORDER BY time DESC LIMIT 4")
return {"furnaces": data}
@app.post("/api/v1/predict", response_model=PredictionResponse)
async def predict(data: MonitorData):
"""温度预测"""
try:
# 准备输入
features = np.array([[
data.temperature, data.pressure, data.blast_volume,
data.humidity, data.coke_ratio, data.ore_ratio
]])
# 预测
with torch.no_grad():
pred = predictor(torch.FloatTensor(features))
pred_temp = pred.item()
# 异常检测
is_anomaly = anomaly_detector.predict(features)[0]
anomaly_score = anomaly_detector.score(features)[0]
return PredictionResponse(
furnace_id=data.furnace_id,
predicted_temp=round(pred_temp, 2),
confidence=0.95,
is_anomaly=bool(is_anomaly),
anomaly_score=round(anomaly_score, 4)
)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.websocket("/ws/monitor")
async def websocket_monitor(websocket: WebSocket):
"""WebSocket实时推送"""
await websocket.accept()
try:
while True:
# 获取最新数据
data = get_latest_monitor_data()
# 推送数据
await websocket.send_json(data)
# 等待下次推送
await asyncio.sleep(3)
except WebSocketDisconnect:
print("Client disconnected")
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
5.2 前端监控界面
vue
<!-- Vue3 + Element Plus 监控界面 -->
<template>
<div class="monitor-container">
<!-- 顶部导航 -->
<el-header class="header">
<h1>🏭 高炉智能监控系统</h1>
<el-button @click="refreshData" :loading="loading">刷新数据</el-button>
</el-header>
<!-- 高炉卡片 -->
<el-row :gutter="20" class="furnace-cards">
<el-col :span="6" v-for="furnace in furnaces" :key="furnace.id">
<el-card :class="getCardClass(furnace)">
<template #header>
<div class="card-header">
<span>{{ furnace.name }}</span>
<el-tag :type="getStatusType(furnace.status)">
{{ furnace.status_text }}
</el-tag>
</div>
</template>
<!-- 温度 -->
<div class="metric">
<span class="label">当前温度</span>
<span class="value temperature">{{ furnace.temperature }}°C</span>
</div>
<!-- 压力 -->
<div class="metric">
<span class="label">炉顶压力</span>
<span class="value">{{ furnace.pressure }} kPa</span>
</div>
<!-- 预测温度 -->
<div class="metric prediction">
<span class="label">预测温度(30min后)</span>
<span class="value">{{ furnace.predicted_temp }}°C</span>
</div>
<!-- 预警状态 -->
<div v-if="furnace.alarm" class="alarm-info">
<el-alert
:title="furnace.alarm.message"
:type="furnace.alarm.level"
:closable="false"
/>
</div>
</el-card>
</el-col>
</el-row>
<!-- 趋势图 -->
<el-card class="chart-card">
<template #header>
<span>温度趋势</span>
</template>
<div ref="chartRef" style="height: 400px;"></div>
</el-card>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import * as echarts from 'echarts'
import axios from 'axios'
const furnaces = ref([])
const loading = ref(false)
const chartRef = ref(null)
let chart = null
let ws = null
// 获取高炉数据
const fetchFurnaces = async () => {
try {
const response = await axios.get('/api/v1/furnaces')
furnaces.value = response.data.furnaces
} catch (error) {
console.error('获取数据失败:', error)
}
}
// 初始化图表
const initChart = () => {
chart = echarts.init(chartRef.value)
const option = {
tooltip: { trigger: 'axis' },
legend: { data: ['1#高炉', '2#高炉', '3#高炉', '4#高炉'] },
xAxis: { type: 'category', data: [] },
yAxis: { type: 'value', name: '温度(°C)' },
series: [
{ name: '1#高炉', type: 'line', smooth: true, data: [] },
{ name: '2#高炉', type: 'line', smooth: true, data: [] },
{ name: '3#高炉', type: 'line', smooth: true, data: [] },
{ name: '4#高炉', type: 'line', smooth: true, data: [] }
]
}
chart.setOption(option)
}
// WebSocket连接
const connectWebSocket = () => {
ws = new WebSocket('ws://localhost:8000/ws/monitor')
ws.onmessage = (event) => {
const data = JSON.parse(event.data)
updateChart(data)
}
ws.onclose = () => {
console.log('WebSocket连接断开,5秒后重连')
setTimeout(connectWebSocket, 5000)
}
}
// 更新图表
const updateChart = (data) => {
chart.setOption({
xAxis: { data: data.timestamps },
series: data.temperatures
})
}
onMounted(() => {
fetchFurnaces()
initChart()
connectWebSocket()
})
onUnmounted(() => {
ws?.close()
chart?.dispose()
})
</script>
🧪 六、测试验证阶段
6.1 测试用例设计
python
# pytest测试用例
import pytest
import torch
from fastapi.testclient import TestClient
# 导入应用
from api.main import app
client = TestClient(app)
class TestPredictAPI:
"""预测API测试"""
def test_health_check(self):
"""健康检查测试"""
response = client.get("/health")
assert response.status_code == 200
assert response.json()["status"] == "healthy"
def test_predict_success(self):
"""正常预测测试"""
payload = {
"furnace_id": 1,
"temperature": 1250.0,
"pressure": 180.0,
"blast_volume": 450.0,
"humidity": 0.02,
"coke_ratio": 0.45,
"ore_ratio": 1.6
}
response = client.post("/api/v1/predict", json=payload)
assert response.status_code == 200
data = response.json()
assert "predicted_temp" in data
assert "is_anomaly" in data
def test_predict_invalid_data(self):
"""无效数据测试"""
payload = {
"furnace_id": 1,
"temperature": "invalid", # 应该是数字
"pressure": 180.0
}
response = client.post("/api/v1/predict", json=payload)
assert response.status_code == 422 # 验证错误
class TestModelPerformance:
"""模型性能测试"""
@pytest.fixture
def test_data(self):
"""测试数据fixture"""
return torch.randn(100, 60, 20) # batch=100, seq=60, features=20
def test_model_output_shape(self, test_data):
"""测试模型输出形状"""
from models.predictor import BlastFurnacePredictor
model = BlastFurnacePredictor(input_dim=20)
output = model(test_data)
assert output.shape == (100, 1)
def test_model_prediction_range(self, test_data):
"""测试预测值范围"""
from models.predictor import BlastFurnacePredictor
model = BlastFurnacePredictor(input_dim=20)
output = model(test_data)
# 温度应该在合理范围内
assert output.min() > 500
assert output.max() < 2000
✅ 七、总结与展望
7.1 项目交付物
┌─────────────────────────────────────────────────────────────────┐
│ 项目交付清单 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 📦 代码交付 │
│ ├── ✅ 数据采集模块 (data/collectors/) │
│ ├── ✅ 数据处理模块 (data/processors/) │
│ ├── ✅ 预测模型 (models/predictors/) │
│ ├── ✅ 异常检测 (models/detectors/) │
│ ├── ✅ 后端API服务 (services/api/) │
│ └── ✅ 前端监控界面 (frontend/web/) │
│ │
│ 📄 文档交付 │
│ ├── ✅ 系统设计文档 │
│ ├── ✅ API接口文档 │
│ ├── ✅ 部署手册 │
│ └── ✅ 用户操作手册 │
│ │
│ 📊 数据交付 │
│ ├── ✅ 训练数据集 (清洗后) │
│ ├── ✅ 测试数据集 │
│ └── ✅ 标注数据 │
│ │
└─────────────────────────────────────────────────────────────────┘
7.2 后续学习
📌 下期预告 :第20期:效果评估:数据说话
🏷️ 标签
#项目实战 #系统搭建 #FastAPI #Vue3 #PyTorch #落地应用 #高炉炼铁 #工业智能化
💡 温馨提示:本文通过一个完整的项目案例,展示了从需求分析到系统上线的全过程。建议读者按照本文的思路,动手实践一个自己的项目,真正掌握工业AI系统的开发流程!
👍 如果觉得有帮助,请点赞、收藏、转发!
版权归作者所有,未经许可请勿抄袭,套用,商用(或其它具有利益性行为) 。
🔔 关注专栏,不错过后续精彩内容!