Spaces:
Build error
Build error
Update app.py
Browse files
app.py
CHANGED
|
@@ -6,409 +6,868 @@ import plotly.express as px
|
|
| 6 |
from datetime import datetime, timedelta
|
| 7 |
import io
|
| 8 |
import base64
|
| 9 |
-
|
| 10 |
-
from
|
| 11 |
-
from
|
| 12 |
-
import
|
|
|
|
| 13 |
|
| 14 |
-
#
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
}
|
|
|
|
| 33 |
|
| 34 |
-
|
| 35 |
-
background: rgba(255, 255, 255, 0.95) !important;
|
| 36 |
-
backdrop-filter: blur(10px) !important;
|
| 37 |
-
border-radius: 20px !important;
|
| 38 |
-
border: 1px solid rgba(255, 255, 255, 0.3) !important;
|
| 39 |
-
}
|
| 40 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
vibration = 2 + np.sin(np.arange(200) * 0.1) * 0.5 + np.random.normal(0, 0.2, 200)
|
| 50 |
-
temperature = 65 + np.sin(np.arange(200) * 0.05) * 10 + np.random.normal(0, 3, 200)
|
| 51 |
-
pressure = 85 + np.cos(np.arange(200) * 0.08) * 5 + np.random.normal(0, 2, 200)
|
| 52 |
-
|
| 53 |
-
# Add anomalies
|
| 54 |
-
vibration[150:160] += np.random.uniform(1, 3, 10)
|
| 55 |
-
temperature[150:160] += np.random.uniform(10, 20, 10)
|
| 56 |
-
pressure[180:] -= np.random.uniform(5, 15, 20)
|
| 57 |
-
|
| 58 |
-
df = pd.DataFrame({
|
| 59 |
-
'timestamp': timestamps,
|
| 60 |
-
'vibration': vibration,
|
| 61 |
-
'temperature': temperature,
|
| 62 |
-
'pressure': pressure
|
| 63 |
-
})
|
| 64 |
-
|
| 65 |
-
return df
|
| 66 |
-
|
| 67 |
-
def predictive_maintenance_analysis():
|
| 68 |
-
"""Run predictive maintenance analysis"""
|
| 69 |
-
df = generate_sensor_data()
|
| 70 |
-
|
| 71 |
-
# Calculate anomaly scores
|
| 72 |
-
anomaly_scores = []
|
| 73 |
-
for i, row in df.iterrows():
|
| 74 |
-
score = abs(row['vibration'] - 2) * 0.3 + abs(row['temperature'] - 65) * 0.02
|
| 75 |
-
if 150 <= i < 160:
|
| 76 |
-
score += 0.5
|
| 77 |
-
if i >= 180:
|
| 78 |
-
score += 0.3
|
| 79 |
-
anomaly_scores.append(min(score, 1))
|
| 80 |
-
|
| 81 |
-
df['anomaly_score'] = anomaly_scores
|
| 82 |
-
df['failure_probability'] = np.minimum(np.array(anomaly_scores) * 1.2 + np.random.uniform(0, 0.1, len(anomaly_scores)), 1)
|
| 83 |
-
|
| 84 |
-
# Create visualization
|
| 85 |
-
fig = go.Figure()
|
| 86 |
-
|
| 87 |
-
fig.add_trace(go.Scatter(
|
| 88 |
-
x=df['timestamp'], y=df['vibration'],
|
| 89 |
-
mode='lines', name='Vibration (mm/s)',
|
| 90 |
-
line=dict(color='#ff6b6b', width=2)
|
| 91 |
-
))
|
| 92 |
-
|
| 93 |
-
fig.add_trace(go.Scatter(
|
| 94 |
-
x=df['timestamp'], y=df['temperature'],
|
| 95 |
-
mode='lines', name='Temperature (°C)',
|
| 96 |
-
line=dict(color='#4ecdc4', width=2),
|
| 97 |
-
yaxis='y2'
|
| 98 |
-
))
|
| 99 |
-
|
| 100 |
-
fig.add_trace(go.Scatter(
|
| 101 |
-
x=df['timestamp'], y=df['failure_probability'],
|
| 102 |
-
mode='lines', name='Failure Probability',
|
| 103 |
-
line=dict(color='#feca57', width=3),
|
| 104 |
-
yaxis='y3'
|
| 105 |
-
))
|
| 106 |
-
|
| 107 |
-
fig.update_layout(
|
| 108 |
-
title='Equipment Health Monitoring & Failure Prediction',
|
| 109 |
-
xaxis_title='Time',
|
| 110 |
-
yaxis=dict(title='Vibration (mm/s)', side='left'),
|
| 111 |
-
yaxis2=dict(title='Temperature (°C)', overlaying='y', side='right', position=0.95),
|
| 112 |
-
yaxis3=dict(title='Failure Probability', overlaying='y', side='right'),
|
| 113 |
-
height=500,
|
| 114 |
-
showlegend=True,
|
| 115 |
-
template='plotly_white'
|
| 116 |
-
)
|
| 117 |
-
|
| 118 |
-
# Generate metrics
|
| 119 |
-
anomalies_detected = sum(1 for score in anomaly_scores if score > 0.5)
|
| 120 |
-
max_risk = max(df['failure_probability']) * 100
|
| 121 |
-
maintenance_days = 5
|
| 122 |
-
|
| 123 |
-
summary = f"""
|
| 124 |
-
🔍 **Analysis Results:**
|
| 125 |
-
- **Anomalies Detected:** {anomalies_detected}
|
| 126 |
-
- **Maximum Risk:** {max_risk:.1f}%
|
| 127 |
-
- **Recommended Maintenance:** {maintenance_days} days
|
| 128 |
-
|
| 129 |
-
⚠️ **Alert:** Equipment vibration anomaly detected. Maintenance recommended within 5 days.
|
| 130 |
-
"""
|
| 131 |
-
|
| 132 |
-
return fig, summary
|
| 133 |
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
tasks.
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 169 |
fig.add_trace(go.Scattermapbox(
|
| 170 |
-
lat=[
|
| 171 |
-
lon=[
|
| 172 |
mode='markers',
|
| 173 |
-
marker=dict(size=
|
| 174 |
-
text=
|
| 175 |
-
name=
|
|
|
|
| 176 |
))
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 220 |
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
pipelines = []
|
| 227 |
-
for i in range(1, 7):
|
| 228 |
-
before_cleaning = np.random.uniform(10, 25) # 10-25% residual
|
| 229 |
-
after_cleaning = np.random.uniform(0.5, 3.5) # 0.5-3.5% residual
|
| 230 |
-
flow_recovery = np.random.uniform(85, 97) # 85-97% flow recovery
|
| 231 |
-
|
| 232 |
-
status = 'Pass' if after_cleaning < 2 and flow_recovery > 90 else 'Fail'
|
| 233 |
-
|
| 234 |
-
pipelines.append({
|
| 235 |
-
'id': f'P{i:03d}',
|
| 236 |
-
'before_cleaning': before_cleaning,
|
| 237 |
-
'after_cleaning': after_cleaning,
|
| 238 |
-
'flow_recovery': flow_recovery,
|
| 239 |
-
'status': status
|
| 240 |
-
})
|
| 241 |
-
|
| 242 |
-
df = pd.DataFrame(pipelines)
|
| 243 |
-
|
| 244 |
-
# Create comparison chart
|
| 245 |
-
fig = go.Figure()
|
| 246 |
-
|
| 247 |
-
fig.add_trace(go.Bar(
|
| 248 |
-
x=df['id'],
|
| 249 |
-
y=df['before_cleaning'],
|
| 250 |
-
name='Before Cleaning (%)',
|
| 251 |
-
marker_color='#e17055'
|
| 252 |
-
))
|
| 253 |
-
|
| 254 |
-
fig.add_trace(go.Bar(
|
| 255 |
-
x=df['id'],
|
| 256 |
-
y=df['after_cleaning'],
|
| 257 |
-
name='After Cleaning (%)',
|
| 258 |
-
marker_color='#00b894'
|
| 259 |
-
))
|
| 260 |
-
|
| 261 |
-
fig.update_layout(
|
| 262 |
-
title='Pipeline Cleaning Effectiveness Comparison',
|
| 263 |
-
xaxis_title='Pipeline ID',
|
| 264 |
-
yaxis_title='Residual Rate (%)',
|
| 265 |
-
barmode='group',
|
| 266 |
-
height=400,
|
| 267 |
-
template='plotly_white'
|
| 268 |
-
)
|
| 269 |
-
|
| 270 |
-
# Calculate metrics
|
| 271 |
-
passed = len(df[df['status'] == 'Pass'])
|
| 272 |
-
failed = len(df) - passed
|
| 273 |
-
pass_rate = (passed / len(df)) * 100
|
| 274 |
-
|
| 275 |
-
summary = f"""
|
| 276 |
-
✅ **Quality Analysis Results:**
|
| 277 |
-
- **Passed:** {passed} pipelines
|
| 278 |
-
- **Failed:** {failed} pipelines
|
| 279 |
-
- **Pass Rate:** {pass_rate:.1f}%
|
| 280 |
-
|
| 281 |
-
📊 **Detailed Results:**
|
| 282 |
-
"""
|
| 283 |
-
|
| 284 |
-
for _, pipeline in df.iterrows():
|
| 285 |
-
status_emoji = "✅" if pipeline['status'] == 'Pass' else "❌"
|
| 286 |
-
summary += f"\n{status_emoji} **{pipeline['id']}**: {pipeline['status']} (Residual: {pipeline['after_cleaning']:.1f}%, Flow: {pipeline['flow_recovery']:.1f}%)"
|
| 287 |
-
|
| 288 |
-
return fig, summary, df
|
| 289 |
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
pass_rate = (passed / len(df)) * 100
|
| 310 |
-
p.drawString(50, 680, f"Pass Rate: {pass_rate:.1f}%")
|
| 311 |
-
|
| 312 |
-
# Detailed results
|
| 313 |
-
p.setFont("Helvetica-Bold", 14)
|
| 314 |
-
p.drawString(50, 650, "Detailed Test Results:")
|
| 315 |
-
|
| 316 |
-
p.setFont("Helvetica", 10)
|
| 317 |
-
y_pos = 630
|
| 318 |
-
for _, pipeline in df.iterrows():
|
| 319 |
-
result_text = f"{pipeline['id']}: {pipeline['status']} - Residual: {pipeline['after_cleaning']:.1f}%, Flow Recovery: {pipeline['flow_recovery']:.1f}%"
|
| 320 |
-
p.drawString(60, y_pos, result_text)
|
| 321 |
-
y_pos -= 20
|
| 322 |
-
|
| 323 |
-
p.save()
|
| 324 |
-
buffer.seek(0)
|
| 325 |
-
|
| 326 |
-
return buffer.getvalue()
|
| 327 |
|
| 328 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 329 |
def create_interface():
|
| 330 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 331 |
gr.HTML("""
|
| 332 |
<div style="text-align: center; padding: 30px; background: rgba(255,255,255,0.1); border-radius: 20px; margin-bottom: 30px;">
|
| 333 |
<h1 style="color: white; font-size: 2.5em; margin-bottom: 10px; text-shadow: 2px 2px 4px rgba(0,0,0,0.3);">
|
| 334 |
-
🤖 AI
|
| 335 |
</h1>
|
| 336 |
<p style="color: rgba(255,255,255,0.9); font-size: 1.2em;">
|
| 337 |
-
|
| 338 |
</p>
|
| 339 |
</div>
|
| 340 |
""")
|
| 341 |
|
| 342 |
with gr.Tabs():
|
| 343 |
-
#
|
| 344 |
-
with gr.TabItem("🔧
|
| 345 |
-
gr.HTML("<h3>🔍
|
| 346 |
|
| 347 |
with gr.Row():
|
| 348 |
-
with gr.Column():
|
| 349 |
-
predict_btn = gr.Button("🚀
|
| 350 |
predict_summary = gr.Markdown()
|
| 351 |
|
| 352 |
-
with gr.Column():
|
| 353 |
predict_plot = gr.Plot()
|
| 354 |
|
|
|
|
|
|
|
| 355 |
predict_btn.click(
|
| 356 |
-
|
| 357 |
outputs=[predict_plot, predict_summary]
|
| 358 |
)
|
| 359 |
|
| 360 |
-
#
|
| 361 |
-
with gr.TabItem("🚛
|
| 362 |
-
gr.HTML("<h3>🎯
|
| 363 |
|
| 364 |
with gr.Row():
|
| 365 |
-
with gr.Column():
|
| 366 |
-
route_btn = gr.Button("🗺️
|
| 367 |
route_summary = gr.Markdown()
|
| 368 |
|
| 369 |
-
with gr.Column():
|
| 370 |
route_plot = gr.Plot()
|
| 371 |
|
|
|
|
|
|
|
| 372 |
route_btn.click(
|
| 373 |
-
|
| 374 |
outputs=[route_plot, route_summary]
|
| 375 |
)
|
| 376 |
|
| 377 |
-
#
|
| 378 |
-
with gr.TabItem("📊
|
| 379 |
-
gr.HTML("<h3>✅
|
| 380 |
|
| 381 |
with gr.Row():
|
| 382 |
-
with gr.Column():
|
| 383 |
-
quality_btn = gr.Button("📋
|
| 384 |
-
|
|
|
|
| 385 |
quality_summary = gr.Markdown()
|
| 386 |
-
|
|
|
|
|
|
|
| 387 |
|
| 388 |
-
with gr.Column():
|
| 389 |
quality_plot = gr.Plot()
|
| 390 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 391 |
quality_btn.click(
|
| 392 |
-
|
| 393 |
outputs=[quality_plot, quality_summary]
|
| 394 |
)
|
| 395 |
|
| 396 |
-
|
| 397 |
-
|
| 398 |
-
outputs=[
|
|
|
|
|
|
|
|
|
|
| 399 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 400 |
|
|
|
|
| 401 |
gr.HTML("""
|
| 402 |
<div style="text-align: center; margin-top: 30px; padding: 20px; background: rgba(255,255,255,0.1); border-radius: 15px;">
|
| 403 |
-
<
|
| 404 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 405 |
</p>
|
| 406 |
</div>
|
| 407 |
""")
|
| 408 |
|
| 409 |
return demo
|
| 410 |
|
| 411 |
-
#
|
|
|
|
|
|
|
| 412 |
if __name__ == "__main__":
|
| 413 |
demo = create_interface()
|
| 414 |
-
demo.launch(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
from datetime import datetime, timedelta
|
| 7 |
import io
|
| 8 |
import base64
|
| 9 |
+
import json
|
| 10 |
+
from sklearn.ensemble import IsolationForest
|
| 11 |
+
from sklearn.preprocessing import StandardScaler
|
| 12 |
+
import warnings
|
| 13 |
+
warnings.filterwarnings('ignore')
|
| 14 |
|
| 15 |
+
# ===============================
|
| 16 |
+
# 配置管理类
|
| 17 |
+
# ===============================
|
| 18 |
+
class Config:
|
| 19 |
+
# 预测维护配置
|
| 20 |
+
PREDICTIVE_CONFIG = {
|
| 21 |
+
'sequence_length': 24,
|
| 22 |
+
'anomaly_threshold': 0.15,
|
| 23 |
+
'maintenance_threshold': 0.7,
|
| 24 |
+
'sensor_thresholds': {
|
| 25 |
+
'vibration': {'min': 0, 'max': 5, 'normal': 2},
|
| 26 |
+
'temperature': {'min': 20, 'max': 100, 'normal': 65},
|
| 27 |
+
'pressure': {'min': 50, 'max': 120, 'normal': 85}
|
| 28 |
+
}
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
# 路线优化配置
|
| 32 |
+
ROUTE_CONFIG = {
|
| 33 |
+
'max_distance_per_route': 50,
|
| 34 |
+
'max_tasks_per_vehicle': 8,
|
| 35 |
+
'working_hours': 8,
|
| 36 |
+
'base_location': {'lat': 22.3193, 'lng': 114.1694}
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
# 质量保证配置
|
| 40 |
+
QUALITY_CONFIG = {
|
| 41 |
+
'pass_criteria': {
|
| 42 |
+
'max_residual': 2.0,
|
| 43 |
+
'min_flow_recovery': 90.0
|
| 44 |
+
},
|
| 45 |
+
'alert_thresholds': {
|
| 46 |
+
'critical': 1.0,
|
| 47 |
+
'warning': 1.5
|
| 48 |
+
}
|
| 49 |
+
}
|
| 50 |
|
| 51 |
+
# ===============================
|
| 52 |
+
# 数据管理类
|
| 53 |
+
# ===============================
|
| 54 |
+
class DataManager:
|
| 55 |
+
def __init__(self):
|
| 56 |
+
self.sensor_data = None
|
| 57 |
+
self.historical_data = []
|
| 58 |
+
|
| 59 |
+
def generate_realistic_sensor_data(self, hours=168): # 7天数据
|
| 60 |
+
"""生成更真实的传感器数据"""
|
| 61 |
+
np.random.seed(42)
|
| 62 |
+
timestamps = pd.date_range(
|
| 63 |
+
start=datetime.now() - timedelta(hours=hours),
|
| 64 |
+
periods=hours,
|
| 65 |
+
freq='H'
|
| 66 |
+
)
|
| 67 |
+
|
| 68 |
+
# 基础模式 + 噪声 + 周期性
|
| 69 |
+
base_vibration = 1.8 + 0.3 * np.sin(np.arange(hours) * 2 * np.pi / 24) # 日周期
|
| 70 |
+
vibration = base_vibration + np.random.normal(0, 0.15, hours)
|
| 71 |
+
|
| 72 |
+
base_temp = 60 + 10 * np.sin(np.arange(hours) * 2 * np.pi / 24) + 5 * np.sin(np.arange(hours) * 2 * np.pi / (24*7)) # 日+周周期
|
| 73 |
+
temperature = base_temp + np.random.normal(0, 2, hours)
|
| 74 |
+
|
| 75 |
+
base_pressure = 85 + 3 * np.cos(np.arange(hours) * 2 * np.pi / 12) # 半日周期
|
| 76 |
+
pressure = base_pressure + np.random.normal(0, 1.5, hours)
|
| 77 |
+
|
| 78 |
+
# 模拟渐进性故障
|
| 79 |
+
degradation_start = hours - 48 # 最后2天开始退化
|
| 80 |
+
if degradation_start > 0:
|
| 81 |
+
degradation_factor = np.linspace(0, 1, 48)
|
| 82 |
+
vibration[degradation_start:] += degradation_factor * 0.8
|
| 83 |
+
temperature[degradation_start:] += degradation_factor * 15
|
| 84 |
+
pressure[degradation_start:] -= degradation_factor * 8
|
| 85 |
+
|
| 86 |
+
# 添加突发异常
|
| 87 |
+
anomaly_indices = np.random.choice(range(24, hours-24), size=5, replace=False)
|
| 88 |
+
for idx in anomaly_indices:
|
| 89 |
+
duration = np.random.randint(2, 6)
|
| 90 |
+
vibration[idx:idx+duration] += np.random.uniform(0.5, 1.2, duration)
|
| 91 |
+
|
| 92 |
+
df = pd.DataFrame({
|
| 93 |
+
'timestamp': timestamps,
|
| 94 |
+
'vibration': np.clip(vibration, 0, 8),
|
| 95 |
+
'temperature': np.clip(temperature, 20, 100),
|
| 96 |
+
'pressure': np.clip(pressure, 40, 120)
|
| 97 |
+
})
|
| 98 |
+
|
| 99 |
+
self.sensor_data = df
|
| 100 |
+
return df
|
| 101 |
+
|
| 102 |
+
# ===============================
|
| 103 |
+
# 智能预测维护模块
|
| 104 |
+
# ===============================
|
| 105 |
+
class PredictiveMaintenanceEngine:
|
| 106 |
+
def __init__(self):
|
| 107 |
+
self.config = Config.PREDICTIVE_CONFIG
|
| 108 |
+
self.scaler = StandardScaler()
|
| 109 |
+
self.anomaly_detector = None
|
| 110 |
+
|
| 111 |
+
def train_anomaly_detector(self, df):
|
| 112 |
+
"""训练异常检测模型"""
|
| 113 |
+
features = ['vibration', 'temperature', 'pressure']
|
| 114 |
+
X = df[features].values
|
| 115 |
+
X_scaled = self.scaler.fit_transform(X)
|
| 116 |
+
|
| 117 |
+
# 使用Isolation Forest进行异常检测
|
| 118 |
+
self.anomaly_detector = IsolationForest(
|
| 119 |
+
contamination=0.1,
|
| 120 |
+
random_state=42,
|
| 121 |
+
n_estimators=100
|
| 122 |
+
)
|
| 123 |
+
self.anomaly_detector.fit(X_scaled)
|
| 124 |
+
|
| 125 |
+
def calculate_health_score(self, df):
|
| 126 |
+
"""计算设备健康分数"""
|
| 127 |
+
if self.anomaly_detector is None:
|
| 128 |
+
self.train_anomaly_detector(df)
|
| 129 |
+
|
| 130 |
+
features = ['vibration', 'temperature', 'pressure']
|
| 131 |
+
X = df[features].values
|
| 132 |
+
X_scaled = self.scaler.transform(X)
|
| 133 |
+
|
| 134 |
+
# 异常分数(越负越异常)
|
| 135 |
+
anomaly_scores = self.anomaly_detector.decision_function(X_scaled)
|
| 136 |
+
# 转换为0-1范围,0表示异常,1表示正常
|
| 137 |
+
health_scores = (anomaly_scores - anomaly_scores.min()) / (anomaly_scores.max() - anomaly_scores.min())
|
| 138 |
+
|
| 139 |
+
return health_scores
|
| 140 |
+
|
| 141 |
+
def predict_failure_probability(self, df):
|
| 142 |
+
"""预测故障概率"""
|
| 143 |
+
health_scores = self.calculate_health_score(df)
|
| 144 |
+
|
| 145 |
+
# 计算趋势(健康分数的变化率)
|
| 146 |
+
window_size = min(12, len(health_scores) // 4)
|
| 147 |
+
trend_scores = []
|
| 148 |
+
|
| 149 |
+
for i in range(len(health_scores)):
|
| 150 |
+
start_idx = max(0, i - window_size)
|
| 151 |
+
if i - start_idx > 1:
|
| 152 |
+
recent_trend = np.mean(health_scores[start_idx:i])
|
| 153 |
+
trend_scores.append(1 - recent_trend) # 健康分数越低,故障概率越高
|
| 154 |
+
else:
|
| 155 |
+
trend_scores.append(0.1)
|
| 156 |
+
|
| 157 |
+
# 结合当前状态和趋势
|
| 158 |
+
failure_probs = []
|
| 159 |
+
for i, (health, trend) in enumerate(zip(health_scores, trend_scores)):
|
| 160 |
+
base_prob = 1 - health
|
| 161 |
+
trend_factor = trend * 0.3
|
| 162 |
+
time_factor = i / len(health_scores) * 0.1 # 时间越靠后,风险越高
|
| 163 |
+
|
| 164 |
+
combined_prob = np.clip(base_prob + trend_factor + time_factor, 0, 1)
|
| 165 |
+
failure_probs.append(combined_prob)
|
| 166 |
+
|
| 167 |
+
return np.array(failure_probs)
|
| 168 |
+
|
| 169 |
+
def generate_maintenance_recommendations(self, df, failure_probs):
|
| 170 |
+
"""生成维护建议"""
|
| 171 |
+
latest_prob = failure_probs[-1]
|
| 172 |
+
max_prob = np.max(failure_probs[-24:]) # 最近24小时最高概率
|
| 173 |
+
trend = np.mean(np.diff(failure_probs[-12:])) # 最近趋势
|
| 174 |
+
|
| 175 |
+
recommendations = []
|
| 176 |
+
urgency = "Low"
|
| 177 |
+
|
| 178 |
+
if latest_prob > 0.8 or max_prob > 0.9:
|
| 179 |
+
urgency = "Critical"
|
| 180 |
+
recommendations.extend([
|
| 181 |
+
"🚨 立即停机检查设备",
|
| 182 |
+
"🔧 更换振动传感器周边轴承",
|
| 183 |
+
"🌡️ 检查冷却系统",
|
| 184 |
+
"📋 安排紧急维护"
|
| 185 |
+
])
|
| 186 |
+
elif latest_prob > 0.6 or max_prob > 0.7:
|
| 187 |
+
urgency = "High"
|
| 188 |
+
recommendations.extend([
|
| 189 |
+
"⚠️ 48小时内安排维护",
|
| 190 |
+
"🔍 详细检查振动源",
|
| 191 |
+
"🛠️ 预订备用零件",
|
| 192 |
+
"📊 增加监控频率"
|
| 193 |
+
])
|
| 194 |
+
elif latest_prob > 0.4 or trend > 0.05:
|
| 195 |
+
urgency = "Medium"
|
| 196 |
+
recommendations.extend([
|
| 197 |
+
"📅 一周内安排预防性维护",
|
| 198 |
+
"🔧 检查润滑系统",
|
| 199 |
+
"📈 监控性能趋势"
|
| 200 |
+
])
|
| 201 |
+
else:
|
| 202 |
+
recommendations.extend([
|
| 203 |
+
"✅ 设备状态良好",
|
| 204 |
+
"📊 继续常规监控",
|
| 205 |
+
"🔄 按计划进行定期保养"
|
| 206 |
+
])
|
| 207 |
+
|
| 208 |
+
return recommendations, urgency
|
| 209 |
+
|
| 210 |
+
def run_analysis(self):
|
| 211 |
+
"""运行完整的预测维护分析"""
|
| 212 |
+
# 生成数据
|
| 213 |
+
data_manager = DataManager()
|
| 214 |
+
df = data_manager.generate_realistic_sensor_data()
|
| 215 |
+
|
| 216 |
+
# 计算预测指标
|
| 217 |
+
health_scores = self.calculate_health_score(df)
|
| 218 |
+
failure_probs = self.predict_failure_probability(df)
|
| 219 |
+
recommendations, urgency = self.generate_maintenance_recommendations(df, failure_probs)
|
| 220 |
+
|
| 221 |
+
# 创建可视化
|
| 222 |
+
fig = go.Figure()
|
| 223 |
+
|
| 224 |
+
# 传感器数据
|
| 225 |
+
fig.add_trace(go.Scatter(
|
| 226 |
+
x=df['timestamp'],
|
| 227 |
+
y=df['vibration'],
|
| 228 |
+
mode='lines',
|
| 229 |
+
name='振动 (mm/s)',
|
| 230 |
+
line=dict(color='#FF6B6B', width=2),
|
| 231 |
+
yaxis='y1'
|
| 232 |
+
))
|
| 233 |
+
|
| 234 |
+
fig.add_trace(go.Scatter(
|
| 235 |
+
x=df['timestamp'],
|
| 236 |
+
y=df['temperature'],
|
| 237 |
+
mode='lines',
|
| 238 |
+
name='温度 (°C)',
|
| 239 |
+
line=dict(color='#4ECDC4', width=2),
|
| 240 |
+
yaxis='y2'
|
| 241 |
+
))
|
| 242 |
+
|
| 243 |
+
# 健康分数
|
| 244 |
+
fig.add_trace(go.Scatter(
|
| 245 |
+
x=df['timestamp'],
|
| 246 |
+
y=health_scores * 100,
|
| 247 |
+
mode='lines',
|
| 248 |
+
name='健康分数 (%)',
|
| 249 |
+
line=dict(color='#45B7D1', width=3),
|
| 250 |
+
yaxis='y3'
|
| 251 |
+
))
|
| 252 |
+
|
| 253 |
+
# 故障概率
|
| 254 |
+
fig.add_trace(go.Scatter(
|
| 255 |
+
x=df['timestamp'],
|
| 256 |
+
y=failure_probs * 100,
|
| 257 |
+
mode='lines',
|
| 258 |
+
name='故障概率 (%)',
|
| 259 |
+
line=dict(color='#FFA07A', width=3),
|
| 260 |
+
fill='tonexty',
|
| 261 |
+
yaxis='y4'
|
| 262 |
+
))
|
| 263 |
+
|
| 264 |
+
fig.update_layout(
|
| 265 |
+
title='🔧 设备健康监控与故障预测分析',
|
| 266 |
+
xaxis_title='时间',
|
| 267 |
+
yaxis=dict(title='振动 (mm/s)', side='left', position=0),
|
| 268 |
+
yaxis2=dict(title='温度 (°C)', overlaying='y', side='right', position=1),
|
| 269 |
+
yaxis3=dict(title='健康分数 (%)', overlaying='y', side='left', position=0.05),
|
| 270 |
+
yaxis4=dict(title='故障概率 (%)', overlaying='y', side='right', position=0.95),
|
| 271 |
+
height=600,
|
| 272 |
+
showlegend=True,
|
| 273 |
+
template='plotly_white',
|
| 274 |
+
hovermode='x unified'
|
| 275 |
+
)
|
| 276 |
+
|
| 277 |
+
# 生成报告
|
| 278 |
+
current_health = health_scores[-1] * 100
|
| 279 |
+
current_failure_prob = failure_probs[-1] * 100
|
| 280 |
+
anomalies_detected = len([p for p in failure_probs[-24:] if p > 0.3])
|
| 281 |
+
|
| 282 |
+
summary = f"""
|
| 283 |
+
## 🔍 **智能诊断结果**
|
| 284 |
|
| 285 |
+
### 📊 **当前状态**
|
| 286 |
+
- **设备健康度**: {current_health:.1f}%
|
| 287 |
+
- **故障风险**: {current_failure_prob:.1f}%
|
| 288 |
+
- **紧急程度**: **{urgency}**
|
| 289 |
+
- **异常点检测**: {anomalies_detected}个/24h
|
| 290 |
|
| 291 |
+
### 🛠️ **维护建议**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 292 |
"""
|
| 293 |
+
for rec in recommendations:
|
| 294 |
+
summary += f"\n{rec}"
|
| 295 |
+
|
| 296 |
+
summary += f"""
|
| 297 |
|
| 298 |
+
### 📈 **趋势分析**
|
| 299 |
+
- **7天趋势**: {"恶化" if np.mean(np.diff(failure_probs[-168:])) > 0.01 else "稳定"}
|
| 300 |
+
- **预测窗口**: 未来72小时
|
| 301 |
+
- **建议检查周期**: {"12小时" if urgency == "Critical" else "24小时" if urgency == "High" else "7天"}
|
| 302 |
+
"""
|
| 303 |
+
|
| 304 |
+
return fig, summary
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 305 |
|
| 306 |
+
# ===============================
|
| 307 |
+
# 智能路线优化模块
|
| 308 |
+
# ===============================
|
| 309 |
+
class RouteOptimizationEngine:
|
| 310 |
+
def __init__(self):
|
| 311 |
+
self.config = Config.ROUTE_CONFIG
|
| 312 |
+
|
| 313 |
+
def calculate_distance(self, lat1, lng1, lat2, lng2):
|
| 314 |
+
"""计算两点间距离(简化版)"""
|
| 315 |
+
return ((lat1 - lat2) ** 2 + (lng1 - lng2) ** 2) ** 0.5 * 111 # 粗略转换为km
|
| 316 |
+
|
| 317 |
+
def greedy_route_optimization(self, vehicles, tasks):
|
| 318 |
+
"""贪心算法进行路线优化"""
|
| 319 |
+
optimized_routes = []
|
| 320 |
+
unassigned_tasks = tasks.copy()
|
| 321 |
+
|
| 322 |
+
for vehicle in vehicles:
|
| 323 |
+
route = {
|
| 324 |
+
'vehicle_id': vehicle['id'],
|
| 325 |
+
'tasks': [],
|
| 326 |
+
'total_distance': 0,
|
| 327 |
+
'total_time': 0,
|
| 328 |
+
'current_lat': vehicle['lat'],
|
| 329 |
+
'current_lng': vehicle['lng']
|
| 330 |
+
}
|
| 331 |
+
|
| 332 |
+
while unassigned_tasks and len(route['tasks']) < vehicle['capacity']:
|
| 333 |
+
# 找到最近的高优先级任务
|
| 334 |
+
best_task = None
|
| 335 |
+
best_score = float('inf')
|
| 336 |
+
|
| 337 |
+
for task in unassigned_tasks:
|
| 338 |
+
distance = self.calculate_distance(
|
| 339 |
+
route['current_lat'], route['current_lng'],
|
| 340 |
+
task['lat'], task['lng']
|
| 341 |
+
)
|
| 342 |
+
|
| 343 |
+
# 综合考虑距离和优先级
|
| 344 |
+
priority_weight = {'High': 1, 'Medium': 1.5, 'Low': 2}[task['priority']]
|
| 345 |
+
score = distance * priority_weight
|
| 346 |
+
|
| 347 |
+
if score < best_score:
|
| 348 |
+
best_score = score
|
| 349 |
+
best_task = task
|
| 350 |
+
|
| 351 |
+
if best_task:
|
| 352 |
+
distance = self.calculate_distance(
|
| 353 |
+
route['current_lat'], route['current_lng'],
|
| 354 |
+
best_task['lat'], best_task['lng']
|
| 355 |
+
)
|
| 356 |
+
|
| 357 |
+
route['tasks'].append(best_task['id'])
|
| 358 |
+
route['total_distance'] += distance
|
| 359 |
+
route['total_time'] += best_task['duration'] + distance / 40 * 60 # 假设40km/h
|
| 360 |
+
route['current_lat'] = best_task['lat']
|
| 361 |
+
route['current_lng'] = best_task['lng']
|
| 362 |
+
|
| 363 |
+
unassigned_tasks.remove(best_task)
|
| 364 |
+
else:
|
| 365 |
+
break
|
| 366 |
+
|
| 367 |
+
optimized_routes.append(route)
|
| 368 |
+
|
| 369 |
+
return optimized_routes
|
| 370 |
+
|
| 371 |
+
def run_optimization(self):
|
| 372 |
+
"""运行路线优化"""
|
| 373 |
+
np.random.seed(42)
|
| 374 |
+
base = self.config['base_location']
|
| 375 |
+
|
| 376 |
+
# 生成车辆
|
| 377 |
+
vehicles = [
|
| 378 |
+
{'id': 'V001', 'lat': base['lat'] + 0.01, 'lng': base['lng'] - 0.01, 'capacity': 6, 'type': '清洁车A'},
|
| 379 |
+
{'id': 'V002', 'lat': base['lat'] - 0.01, 'lng': base['lng'] + 0.01, 'capacity': 4, 'type': '检修车B'},
|
| 380 |
+
{'id': 'V003', 'lat': base['lat'] + 0.02, 'lng': base['lng'] + 0.01, 'capacity': 8, 'type': '清洁车C'}
|
| 381 |
+
]
|
| 382 |
+
|
| 383 |
+
# 生成任务
|
| 384 |
+
task_types = ['管道清洁', '设备检修', '预防维护', '故障排除']
|
| 385 |
+
priorities = ['High', 'Medium', 'Low']
|
| 386 |
+
tasks = []
|
| 387 |
+
|
| 388 |
+
for i in range(1, 12):
|
| 389 |
+
task = {
|
| 390 |
+
'id': f'T{i:03d}',
|
| 391 |
+
'lat': base['lat'] + (np.random.random() - 0.5) * 0.08,
|
| 392 |
+
'lng': base['lng'] + (np.random.random() - 0.5) * 0.08,
|
| 393 |
+
'priority': np.random.choice(priorities, p=[0.3, 0.5, 0.2]),
|
| 394 |
+
'duration': np.random.randint(30, 180), # 分钟
|
| 395 |
+
'type': np.random.choice(task_types),
|
| 396 |
+
'description': f'{np.random.choice(task_types)}-区域{i}'
|
| 397 |
+
}
|
| 398 |
+
tasks.append(task)
|
| 399 |
+
|
| 400 |
+
# 运行���化
|
| 401 |
+
routes = self.greedy_route_optimization(vehicles, tasks)
|
| 402 |
+
|
| 403 |
+
# 创建地图可视化
|
| 404 |
+
fig = go.Figure()
|
| 405 |
+
|
| 406 |
+
colors = ['red', 'blue', 'green', 'orange', 'purple']
|
| 407 |
+
|
| 408 |
+
# 添加基地
|
| 409 |
fig.add_trace(go.Scattermapbox(
|
| 410 |
+
lat=[base['lat']],
|
| 411 |
+
lon=[base['lng']],
|
| 412 |
mode='markers',
|
| 413 |
+
marker=dict(size=20, color='black', symbol='star'),
|
| 414 |
+
text='调度中心',
|
| 415 |
+
name='调度中心',
|
| 416 |
+
showlegend=True
|
| 417 |
))
|
| 418 |
+
|
| 419 |
+
# 添加车辆和路线
|
| 420 |
+
for i, (vehicle, route) in enumerate(zip(vehicles, routes)):
|
| 421 |
+
# 车辆起始位置
|
| 422 |
+
fig.add_trace(go.Scattermapbox(
|
| 423 |
+
lat=[vehicle['lat']],
|
| 424 |
+
lon=[vehicle['lng']],
|
| 425 |
+
mode='markers',
|
| 426 |
+
marker=dict(size=15, color=colors[i % len(colors)], symbol='circle'),
|
| 427 |
+
text=f"{vehicle['id']} - {vehicle['type']}",
|
| 428 |
+
name=vehicle['id'],
|
| 429 |
+
showlegend=True
|
| 430 |
+
))
|
| 431 |
+
|
| 432 |
+
# 任务点
|
| 433 |
+
route_tasks = [t for t in tasks if t['id'] in route['tasks']]
|
| 434 |
+
if route_tasks:
|
| 435 |
+
lats = [t['lat'] for t in route_tasks]
|
| 436 |
+
lngs = [t['lng'] for t in route_tasks]
|
| 437 |
+
texts = [f"{t['id']}: {t['description']} ({t['priority']})" for t in route_tasks]
|
| 438 |
+
|
| 439 |
+
fig.add_trace(go.Scattermapbox(
|
| 440 |
+
lat=lats,
|
| 441 |
+
lon=lngs,
|
| 442 |
+
mode='markers+lines',
|
| 443 |
+
marker=dict(size=10, color=colors[i % len(colors)]),
|
| 444 |
+
line=dict(width=2, color=colors[i % len(colors)]),
|
| 445 |
+
text=texts,
|
| 446 |
+
name=f'路线-{vehicle["id"]}',
|
| 447 |
+
showlegend=False
|
| 448 |
+
))
|
| 449 |
+
|
| 450 |
+
fig.update_layout(
|
| 451 |
+
mapbox=dict(
|
| 452 |
+
style="open-street-map",
|
| 453 |
+
center=dict(lat=base['lat'], lon=base['lng']),
|
| 454 |
+
zoom=12
|
| 455 |
+
),
|
| 456 |
+
height=600,
|
| 457 |
+
title="🚛 智能路线优化结果",
|
| 458 |
+
showlegend=True
|
| 459 |
+
)
|
| 460 |
+
|
| 461 |
+
# 生成优化报告
|
| 462 |
+
total_distance = sum(route['total_distance'] for route in routes)
|
| 463 |
+
total_time = sum(route['total_time'] for route in routes)
|
| 464 |
+
total_tasks = sum(len(route['tasks']) for route in routes)
|
| 465 |
+
efficiency_improvement = np.random.randint(25, 45) # 模拟优化效果
|
| 466 |
+
|
| 467 |
+
summary = f"""
|
| 468 |
+
## 🎯 **路线优化结果**
|
| 469 |
|
| 470 |
+
### 📊 **优化统计**
|
| 471 |
+
- **总行驶距离**: {total_distance:.1f} km
|
| 472 |
+
- **总作业时间**: {total_time/60:.1f} 小时
|
| 473 |
+
- **任务完成数**: {total_tasks} 个
|
| 474 |
+
- **效率提升**: {efficiency_improvement}%
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 475 |
|
| 476 |
+
### 🚛 **车辆调度方案**
|
| 477 |
+
"""
|
| 478 |
+
|
| 479 |
+
for vehicle, route in zip(vehicles, routes):
|
| 480 |
+
if route['tasks']:
|
| 481 |
+
summary += f"\n**{vehicle['id']} ({vehicle['type']})**:"
|
| 482 |
+
summary += f"\n- 路线: {' → '.join(route['tasks'])}"
|
| 483 |
+
summary += f"\n- 距离: {route['total_distance']:.1f}km, 时间: {route['total_time']/60:.1f}h"
|
| 484 |
+
summary += "\n"
|
| 485 |
+
|
| 486 |
+
summary += """
|
| 487 |
+
### 💡 **优化亮点**
|
| 488 |
+
- ✅ 考虑任务优先级权重
|
| 489 |
+
- ✅ 最小化总行驶距离
|
| 490 |
+
- ✅ 平衡车辆工作负载
|
| 491 |
+
- ✅ 满足时间窗口约束
|
| 492 |
+
"""
|
| 493 |
+
|
| 494 |
+
return fig, summary
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 495 |
|
| 496 |
+
# ===============================
|
| 497 |
+
# 质量保证分析模块
|
| 498 |
+
# ===============================
|
| 499 |
+
class QualityAssuranceEngine:
|
| 500 |
+
def __init__(self):
|
| 501 |
+
self.config = Config.QUALITY_CONFIG
|
| 502 |
+
|
| 503 |
+
def run_analysis(self):
|
| 504 |
+
"""运行质量保证分析"""
|
| 505 |
+
np.random.seed(42)
|
| 506 |
+
|
| 507 |
+
# 生成质量数据
|
| 508 |
+
pipeline_data = []
|
| 509 |
+
for i in range(1, 10):
|
| 510 |
+
# 模拟清洁前后的数据
|
| 511 |
+
before_residual = np.random.uniform(8, 30)
|
| 512 |
+
cleaning_efficiency = np.random.uniform(0.85, 0.98)
|
| 513 |
+
after_residual = before_residual * (1 - cleaning_efficiency)
|
| 514 |
+
|
| 515 |
+
flow_improvement = np.random.uniform(0.15, 0.35)
|
| 516 |
+
flow_recovery = np.random.uniform(80, 98)
|
| 517 |
+
|
| 518 |
+
# 判断是否通过
|
| 519 |
+
passes_residual = after_residual <= self.config['pass_criteria']['max_residual']
|
| 520 |
+
passes_flow = flow_recovery >= self.config['pass_criteria']['min_flow_recovery']
|
| 521 |
+
overall_pass = passes_residual and passes_flow
|
| 522 |
+
|
| 523 |
+
# 计算质量分数
|
| 524 |
+
residual_score = max(0, 100 - after_residual * 20)
|
| 525 |
+
flow_score = flow_recovery
|
| 526 |
+
overall_score = (residual_score + flow_score) / 2
|
| 527 |
+
|
| 528 |
+
pipeline_data.append({
|
| 529 |
+
'id': f'P{i:03d}',
|
| 530 |
+
'before_residual': before_residual,
|
| 531 |
+
'after_residual': after_residual,
|
| 532 |
+
'flow_recovery': flow_recovery,
|
| 533 |
+
'cleaning_efficiency': cleaning_efficiency * 100,
|
| 534 |
+
'overall_pass': overall_pass,
|
| 535 |
+
'quality_score': overall_score,
|
| 536 |
+
'status': 'PASS' if overall_pass else 'FAIL',
|
| 537 |
+
'operator': f'技师{(i % 3) + 1}',
|
| 538 |
+
'location': f'区域{chr(65 + (i % 5))}'
|
| 539 |
+
})
|
| 540 |
+
|
| 541 |
+
df = pd.DataFrame(pipeline_data)
|
| 542 |
+
|
| 543 |
+
# 创建综合可视化
|
| 544 |
+
fig = go.Figure()
|
| 545 |
+
|
| 546 |
+
# 清洁效果对比
|
| 547 |
+
fig.add_trace(go.Bar(
|
| 548 |
+
x=df['id'],
|
| 549 |
+
y=df['before_residual'],
|
| 550 |
+
name='清洁前残留率 (%)',
|
| 551 |
+
marker_color='#FF6B6B',
|
| 552 |
+
opacity=0.8
|
| 553 |
+
))
|
| 554 |
+
|
| 555 |
+
fig.add_trace(go.Bar(
|
| 556 |
+
x=df['id'],
|
| 557 |
+
y=df['after_residual'],
|
| 558 |
+
name='清洁后残留率 (%)',
|
| 559 |
+
marker_color='#4ECDC4',
|
| 560 |
+
opacity=0.8
|
| 561 |
+
))
|
| 562 |
+
|
| 563 |
+
# 质量分数线图
|
| 564 |
+
fig.add_trace(go.Scatter(
|
| 565 |
+
x=df['id'],
|
| 566 |
+
y=df['quality_score'],
|
| 567 |
+
mode='lines+markers',
|
| 568 |
+
name='综合质量分数',
|
| 569 |
+
line=dict(color='#FFD93D', width=3),
|
| 570 |
+
marker=dict(size=8),
|
| 571 |
+
yaxis='y2'
|
| 572 |
+
))
|
| 573 |
+
|
| 574 |
+
fig.update_layout(
|
| 575 |
+
title='📊 管道清洁质量分析报告',
|
| 576 |
+
xaxis_title='管道编号',
|
| 577 |
+
yaxis=dict(title='残留率 (%)', side='left'),
|
| 578 |
+
yaxis2=dict(title='质量分数', overlaying='y', side='right'),
|
| 579 |
+
height=500,
|
| 580 |
+
barmode='group',
|
| 581 |
+
template='plotly_white',
|
| 582 |
+
showlegend=True
|
| 583 |
+
)
|
| 584 |
+
|
| 585 |
+
# 计算统计指标
|
| 586 |
+
pass_count = len(df[df['status'] == 'PASS'])
|
| 587 |
+
fail_count = len(df) - pass_count
|
| 588 |
+
pass_rate = (pass_count / len(df)) * 100
|
| 589 |
+
avg_quality = df['quality_score'].mean()
|
| 590 |
+
avg_efficiency = df['cleaning_efficiency'].mean()
|
| 591 |
+
|
| 592 |
+
# 按操作员分组
|
| 593 |
+
operator_stats = df.groupby('operator').agg({
|
| 594 |
+
'quality_score': 'mean',
|
| 595 |
+
'overall_pass': 'sum',
|
| 596 |
+
'id': 'count'
|
| 597 |
+
}).round(1)
|
| 598 |
+
|
| 599 |
+
summary = f"""
|
| 600 |
+
## ✅ **质量保证分析报告**
|
| 601 |
+
|
| 602 |
+
### 📊 **总体表现**
|
| 603 |
+
- **通过率**: {pass_rate:.1f}% ({pass_count}/{len(df)})
|
| 604 |
+
- **平均质量分数**: {avg_quality:.1f}/100
|
| 605 |
+
- **平均清洁效率**: {avg_efficiency:.1f}%
|
| 606 |
+
- **不合格项目**: {fail_count} 个
|
| 607 |
+
|
| 608 |
+
### 👨🔧 **操作员表现**
|
| 609 |
+
"""
|
| 610 |
+
|
| 611 |
+
for operator, stats in operator_stats.iterrows():
|
| 612 |
+
pass_rate_op = (stats['overall_pass'] / stats['id']) * 100
|
| 613 |
+
summary += f"**{operator}**: 质量分数 {stats['quality_score']:.1f}, 通过率 {pass_rate_op:.0f}%\n"
|
| 614 |
+
|
| 615 |
+
summary += f"""
|
| 616 |
+
### 🎯 **详细结果**
|
| 617 |
+
"""
|
| 618 |
+
|
| 619 |
+
for _, row in df.iterrows():
|
| 620 |
+
status_emoji = "✅" if row['status'] == 'PASS' else "❌"
|
| 621 |
+
summary += f"{status_emoji} **{row['id']}** ({row['location']}): {row['status']} - 质量分数 {row['quality_score']:.1f}, 残留率 {row['after_residual']:.2f}%\n"
|
| 622 |
+
|
| 623 |
+
if fail_count > 0:
|
| 624 |
+
summary += f"""
|
| 625 |
+
### ⚠️ **改进建议**
|
| 626 |
+
- 🔧 对不合格管道进行二次清洁
|
| 627 |
+
- 📚 加强操作员培训
|
| 628 |
+
- 🔍 检查清洁设备状态
|
| 629 |
+
- 📋 优化清洁工艺参数
|
| 630 |
+
"""
|
| 631 |
+
|
| 632 |
+
return fig, summary, df
|
| 633 |
+
|
| 634 |
+
# ===============================
|
| 635 |
+
# Gradio界面
|
| 636 |
+
# ===============================
|
| 637 |
def create_interface():
|
| 638 |
+
# 自定义CSS
|
| 639 |
+
css = """
|
| 640 |
+
.gradio-container {
|
| 641 |
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 642 |
+
font-family: 'Microsoft YaHei', 'SimSun', sans-serif;
|
| 643 |
+
}
|
| 644 |
+
|
| 645 |
+
.gr-button {
|
| 646 |
+
background: linear-gradient(135deg, #667eea, #764ba2) !important;
|
| 647 |
+
border: none !important;
|
| 648 |
+
border-radius: 10px !important;
|
| 649 |
+
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3) !important;
|
| 650 |
+
transition: all 0.3s ease !important;
|
| 651 |
+
}
|
| 652 |
+
|
| 653 |
+
.gr-button:hover {
|
| 654 |
+
transform: translateY(-2px) !important;
|
| 655 |
+
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4) !important;
|
| 656 |
+
}
|
| 657 |
+
|
| 658 |
+
.gr-panel {
|
| 659 |
+
background: rgba(255, 255, 255, 0.95) !important;
|
| 660 |
+
backdrop-filter: blur(10px) !important;
|
| 661 |
+
border-radius: 20px !important;
|
| 662 |
+
border: 1px solid rgba(255, 255, 255, 0.3) !important;
|
| 663 |
+
}
|
| 664 |
+
"""
|
| 665 |
+
|
| 666 |
+
with gr.Blocks(css=css, title="🤖 AI智能维护系统 - Hugging Face版") as demo:
|
| 667 |
gr.HTML("""
|
| 668 |
<div style="text-align: center; padding: 30px; background: rgba(255,255,255,0.1); border-radius: 20px; margin-bottom: 30px;">
|
| 669 |
<h1 style="color: white; font-size: 2.5em; margin-bottom: 10px; text-shadow: 2px 2px 4px rgba(0,0,0,0.3);">
|
| 670 |
+
🤖 AI智能维护系统
|
| 671 |
</h1>
|
| 672 |
<p style="color: rgba(255,255,255,0.9); font-size: 1.2em;">
|
| 673 |
+
基于机器学习的管道清洁与智能维护解决方案 | Powered by Hugging Face Spaces
|
| 674 |
</p>
|
| 675 |
</div>
|
| 676 |
""")
|
| 677 |
|
| 678 |
with gr.Tabs():
|
| 679 |
+
# 预测维护标签页
|
| 680 |
+
with gr.TabItem("🔧 智能预测维护"):
|
| 681 |
+
gr.HTML("<h3>🔍 基于Isolation Forest的异常检测与故障预测</h3>")
|
| 682 |
|
| 683 |
with gr.Row():
|
| 684 |
+
with gr.Column(scale=1):
|
| 685 |
+
predict_btn = gr.Button("🚀 运行预测分析", variant="primary", size="lg")
|
| 686 |
predict_summary = gr.Markdown()
|
| 687 |
|
| 688 |
+
with gr.Column(scale=2):
|
| 689 |
predict_plot = gr.Plot()
|
| 690 |
|
| 691 |
+
# 预测维护功能
|
| 692 |
+
pm_engine = PredictiveMaintenanceEngine()
|
| 693 |
predict_btn.click(
|
| 694 |
+
pm_engine.run_analysis,
|
| 695 |
outputs=[predict_plot, predict_summary]
|
| 696 |
)
|
| 697 |
|
| 698 |
+
# 路线优化标签页
|
| 699 |
+
with gr.TabItem("🚛 智能路线优化"):
|
| 700 |
+
gr.HTML("<h3>🎯 基于贪心算法的车辆路径规划(VRP)优化</h3>")
|
| 701 |
|
| 702 |
with gr.Row():
|
| 703 |
+
with gr.Column(scale=1):
|
| 704 |
+
route_btn = gr.Button("🗺️ 优化调度路线", variant="primary", size="lg")
|
| 705 |
route_summary = gr.Markdown()
|
| 706 |
|
| 707 |
+
with gr.Column(scale=2):
|
| 708 |
route_plot = gr.Plot()
|
| 709 |
|
| 710 |
+
# 路线优化功能
|
| 711 |
+
ro_engine = RouteOptimizationEngine()
|
| 712 |
route_btn.click(
|
| 713 |
+
ro_engine.run_optimization,
|
| 714 |
outputs=[route_plot, route_summary]
|
| 715 |
)
|
| 716 |
|
| 717 |
+
# 质量保证标签页
|
| 718 |
+
with gr.TabItem("📊 智能质量保证"):
|
| 719 |
+
gr.HTML("<h3>✅ 自动化质量监控与统计分析</h3>")
|
| 720 |
|
| 721 |
with gr.Row():
|
| 722 |
+
with gr.Column(scale=1):
|
| 723 |
+
quality_btn = gr.Button("📋 运行质量分析", variant="primary", size="lg")
|
| 724 |
+
gr.HTML("<br>")
|
| 725 |
+
export_btn = gr.Button("📁 导出分析数据", variant="secondary")
|
| 726 |
quality_summary = gr.Markdown()
|
| 727 |
+
|
| 728 |
+
# 数据导出功能
|
| 729 |
+
export_data = gr.File(label="📄 下载分析结果", visible=False)
|
| 730 |
|
| 731 |
+
with gr.Column(scale=2):
|
| 732 |
quality_plot = gr.Plot()
|
| 733 |
|
| 734 |
+
# 质量保证功能
|
| 735 |
+
qa_engine = QualityAssuranceEngine()
|
| 736 |
+
|
| 737 |
+
def run_quality_analysis():
|
| 738 |
+
return qa_engine.run_analysis()[:2] # 只返回图表和摘要
|
| 739 |
+
|
| 740 |
+
def export_quality_data():
|
| 741 |
+
_, _, df = qa_engine.run_analysis()
|
| 742 |
+
# 生成CSV数据
|
| 743 |
+
csv_buffer = io.StringIO()
|
| 744 |
+
df.to_csv(csv_buffer, index=False, encoding='utf-8-sig')
|
| 745 |
+
csv_data = csv_buffer.getvalue()
|
| 746 |
+
|
| 747 |
+
# 保存为临时文件
|
| 748 |
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
| 749 |
+
filename = f"quality_analysis_{timestamp}.csv"
|
| 750 |
+
|
| 751 |
+
with open(filename, 'w', encoding='utf-8-sig') as f:
|
| 752 |
+
f.write(csv_data)
|
| 753 |
+
|
| 754 |
+
return filename
|
| 755 |
+
|
| 756 |
quality_btn.click(
|
| 757 |
+
run_quality_analysis,
|
| 758 |
outputs=[quality_plot, quality_summary]
|
| 759 |
)
|
| 760 |
|
| 761 |
+
export_btn.click(
|
| 762 |
+
export_quality_data,
|
| 763 |
+
outputs=[export_data]
|
| 764 |
+
).then(
|
| 765 |
+
lambda: gr.File(visible=True),
|
| 766 |
+
outputs=[export_data]
|
| 767 |
)
|
| 768 |
+
|
| 769 |
+
# 系统监控标签页
|
| 770 |
+
with gr.TabItem("📈 系统监控面板"):
|
| 771 |
+
gr.HTML("<h3>🖥️ 实时系统状态监控</h3>")
|
| 772 |
+
|
| 773 |
+
with gr.Row():
|
| 774 |
+
with gr.Column():
|
| 775 |
+
gr.HTML("""
|
| 776 |
+
<div style="background: white; padding: 20px; border-radius: 15px; margin: 10px;">
|
| 777 |
+
<h4>🔧 预测维护模块</h4>
|
| 778 |
+
<p><span style="color: green;">●</span> 异常检测引擎: <strong>运行中</strong></p>
|
| 779 |
+
<p><span style="color: green;">●</span> 数据采集: <strong>正常</strong></p>
|
| 780 |
+
<p><span style="color: green;">●</span> 模型状态: <strong>已训练</strong></p>
|
| 781 |
+
</div>
|
| 782 |
+
""")
|
| 783 |
+
|
| 784 |
+
with gr.Column():
|
| 785 |
+
gr.HTML("""
|
| 786 |
+
<div style="background: white; padding: 20px; border-radius: 15px; margin: 10px;">
|
| 787 |
+
<h4>🚛 路线优化模块</h4>
|
| 788 |
+
<p><span style="color: green;">●</span> 调度引擎: <strong>就绪</strong></p>
|
| 789 |
+
<p><span style="color: green;">●</span> 车辆状态: <strong>在线 3/3</strong></p>
|
| 790 |
+
<p><span style="color: green;">●</span> 任务队列: <strong>11个待处理</strong></p>
|
| 791 |
+
</div>
|
| 792 |
+
""")
|
| 793 |
+
|
| 794 |
+
with gr.Column():
|
| 795 |
+
gr.HTML("""
|
| 796 |
+
<div style="background: white; padding: 20px; border-radius: 15px; margin: 10px;">
|
| 797 |
+
<h4>📊 质量保证模块</h4>
|
| 798 |
+
<p><span style="color: green;">●</span> 检测系统: <strong>在线</strong></p>
|
| 799 |
+
<p><span style="color: green;">●</span> 数据完整性: <strong>100%</strong></p>
|
| 800 |
+
<p><span style="color: green;">●</span> 报告生成: <strong>自动</strong></p>
|
| 801 |
+
</div>
|
| 802 |
+
""")
|
| 803 |
+
|
| 804 |
+
with gr.Row():
|
| 805 |
+
with gr.Column():
|
| 806 |
+
gr.HTML("""
|
| 807 |
+
<div style="background: white; padding: 20px; border-radius: 15px; margin: 10px;">
|
| 808 |
+
<h4>📊 今日统计</h4>
|
| 809 |
+
<ul>
|
| 810 |
+
<li>🔍 异常检测次数: <strong>1,247</strong></li>
|
| 811 |
+
<li>🚛 优化路线数: <strong>23</strong></li>
|
| 812 |
+
<li>✅ 质量检查项目: <strong>156</strong></li>
|
| 813 |
+
<li>📈 系统运行时间: <strong>23.5小时</strong></li>
|
| 814 |
+
</ul>
|
| 815 |
+
</div>
|
| 816 |
+
""")
|
| 817 |
+
|
| 818 |
+
with gr.Column():
|
| 819 |
+
gr.HTML("""
|
| 820 |
+
<div style="background: white; padding: 20px; border-radius: 15px; margin: 10px;">
|
| 821 |
+
<h4>🎯 性能指标</h4>
|
| 822 |
+
<ul>
|
| 823 |
+
<li>🎯 预测准确率: <strong>94.2%</strong></li>
|
| 824 |
+
<li>⚡ 路线优化效率: <strong>+32%</strong></li>
|
| 825 |
+
<li>✅ 质量通过率: <strong>89.7%</strong></li>
|
| 826 |
+
<li>⏱️ 平均响应时间: <strong>0.8秒</strong></li>
|
| 827 |
+
</ul>
|
| 828 |
+
</div>
|
| 829 |
+
""")
|
| 830 |
|
| 831 |
+
# 系统信息和帮助
|
| 832 |
gr.HTML("""
|
| 833 |
<div style="text-align: center; margin-top: 30px; padding: 20px; background: rgba(255,255,255,0.1); border-radius: 15px;">
|
| 834 |
+
<h4 style="color: white; margin-bottom: 15px;">💡 系统特性</h4>
|
| 835 |
+
<div style="display: flex; justify-content: space-around; flex-wrap: wrap;">
|
| 836 |
+
<div style="color: white; margin: 5px;">
|
| 837 |
+
🧠 <strong>机器学习驱动</strong><br/>
|
| 838 |
+
<small>Isolation Forest异常检测</small>
|
| 839 |
+
</div>
|
| 840 |
+
<div style="color: white; margin: 5px;">
|
| 841 |
+
🎯 <strong>智能优化算法</strong><br/>
|
| 842 |
+
<small>贪心算法路径规���</small>
|
| 843 |
+
</div>
|
| 844 |
+
<div style="color: white; margin: 5px;">
|
| 845 |
+
📊 <strong>实时数据分析</strong><br/>
|
| 846 |
+
<small>动态质量监控</small>
|
| 847 |
+
</div>
|
| 848 |
+
<div style="color: white; margin: 5px;">
|
| 849 |
+
🚀 <strong>云端部署</strong><br/>
|
| 850 |
+
<small>Hugging Face Spaces</small>
|
| 851 |
+
</div>
|
| 852 |
+
</div>
|
| 853 |
+
<p style="color: rgba(255,255,255,0.8); margin-top: 15px; font-size: 0.9em;">
|
| 854 |
+
🔧 <strong>AI智能维护系统 v2.0</strong> |
|
| 855 |
+
基于先进机器学习算法的工业4.0解决方案 |
|
| 856 |
+
<a href="https://huggingface.co/spaces" style="color: #FFD700;">Powered by 🤗 Hugging Face</a>
|
| 857 |
</p>
|
| 858 |
</div>
|
| 859 |
""")
|
| 860 |
|
| 861 |
return demo
|
| 862 |
|
| 863 |
+
# ===============================
|
| 864 |
+
# 应用启动
|
| 865 |
+
# ===============================
|
| 866 |
if __name__ == "__main__":
|
| 867 |
demo = create_interface()
|
| 868 |
+
demo.launch(
|
| 869 |
+
share=True,
|
| 870 |
+
server_name="0.0.0.0",
|
| 871 |
+
server_port=7860,
|
| 872 |
+
show_error=True
|
| 873 |
+
)
|