制造业 AI 落地——质量检测和预测性维护的工程实现
制造业 AI 落地——质量检测和预测性维护的工程实现
制造业的 AI 项目,是我做过的最有挑战的。
不是技术上最难,而是对工程质量的要求远超互联网场景。
互联网 AI:推荐结果不准,用户换个商品就行,最多降低满意度。
制造业 AI:质量检测判断错误,一批有缺陷的产品流出,可能是客户投诉,严重的是安全事故;预测性维护判断错误,该停机维护的没停,导致设备故障,一条生产线停下来,损失按小时计。
所以制造业 AI 的第一要求不是"准确率高",而是"可解释、可追溯、失效安全"。
制造业 AI 场景和互联网 AI 场景的本质差异
这不是程度上的差异,是本质上的不同。
| 维度 | 互联网 AI | 制造业 AI |
|---|---|---|
| 响应时间要求 | 秒级 | 毫秒级到秒级(生产线不等人) |
| 可用性要求 | 99.9% | 99.99%+(生产线停不得) |
| 错误代价 | 用户体验变差 | 产品缺陷、设备损坏、安全风险 |
| 数据类型 | 文本、图片(相对标准) | 传感器信号、工业图像(噪声大) |
| 模型可解释性 | 可选 | 必须(生产质量管理需要原因) |
| 部署环境 | 云端 | 边缘设备(可能无网络) |
制造业的 AI 系统必须在设计阶段就考虑:这个 AI 判断错了,系统怎么兜底?
质量检测:视觉 + LLM 的联合判断
传统工业视觉检测:训练一个分类模型,图片输入,缺陷/正常输出。
这个方案的问题:
- 每种缺陷类型需要单独训练,新缺陷类型出现需要重新训练模型
- 模型说"有缺陷",但不说是什么缺陷、缺陷在哪、严重程度如何
- 无法利用人类的专业知识来辅助判断
改进方案:视觉模型检测异常区域 + LLM 综合判断
视觉模型做的事:定位"哪里不对劲",输出异常区域坐标和置信度。
LLM 做的事:结合视觉检测结果 + 产品规格要求 + 历史缺陷知识,综合判断:这是什么缺陷,严重程度如何,是否需要报废。
质量检测数据模型
@Data
public class QualityInspectionRequest {
private String productId;
private String productType;
private String batchId;
private String imageBase64; // 产品图片
private String productionLineId; // 生产线ID
private Map<String, Object> processParams; // 生产工艺参数(温度、压力等)
private LocalDateTime inspectionTime;
}
@Data
public class VisionDetectionResult {
private String imageId;
private boolean hasAnomaly;
private double overallAnomalyScore; // 0-1,越高越异常
private List<AnomalyRegion> anomalyRegions;
@Data
public static class AnomalyRegion {
private int x, y, width, height; // 异常区域坐标
private double confidence;
private String anomalyType; // 视觉模型分类的异常类型
}
}
@Data
public class QualityJudgment {
private String productId;
private String batchId;
// 最终判断
private QualityLevel qualityLevel; // PASS/BORDERLINE/FAIL
private boolean requiresManualReview;
// LLM 分析结果
private String defectDescription; // 缺陷描述(可读性强的自然语言)
private List<String> defectTypes; // 缺陷类型列表
private String defectSeverity; // 轻微/中等/严重
private String recommendedAction; // 建议处理方式
private String rootCauseHypothesis; // 缺陷可能的根本原因
private double confidence; // 判断置信度
// 追溯信息
private String visionModelVersion;
private String llmModel;
private LocalDateTime judgedAt;
public enum QualityLevel {
PASS, // 合格
BORDERLINE, // 边界(需人工复核)
FAIL // 不合格
}
}视觉检测服务(对接工业视觉模型)
@Service
@Slf4j
public class IndustrialVisionService {
// 工业视觉模型通常是自训练的,通过HTTP或gRPC调用
private final VisionModelClient visionModelClient;
public VisionDetectionResult detect(String imageBase64, String productType) {
// 调用工业视觉模型
VisionModelRequest modelRequest = new VisionModelRequest();
modelRequest.setImage(imageBase64);
modelRequest.setProductType(productType);
VisionModelResponse modelResponse = visionModelClient.detect(modelRequest);
VisionDetectionResult result = new VisionDetectionResult();
result.setImageId(modelResponse.getImageId());
result.setHasAnomaly(modelResponse.getAnomalyScore() > 0.5);
result.setOverallAnomalyScore(modelResponse.getAnomalyScore());
// 转换异常区域
List<VisionDetectionResult.AnomalyRegion> regions = modelResponse.getDetections().stream()
.filter(d -> d.getConfidence() > 0.4) // 置信度过滤
.map(d -> {
VisionDetectionResult.AnomalyRegion region = new VisionDetectionResult.AnomalyRegion();
region.setX(d.getBbox()[0]);
region.setY(d.getBbox()[1]);
region.setWidth(d.getBbox()[2] - d.getBbox()[0]);
region.setHeight(d.getBbox()[3] - d.getBbox()[1]);
region.setConfidence(d.getConfidence());
region.setAnomalyType(d.getClassName());
return region;
})
.collect(Collectors.toList());
result.setAnomalyRegions(regions);
return result;
}
}LLM 联合判断服务
@Service
@Slf4j
public class QualityJudgmentService {
private final IndustrialVisionService visionService;
private final ChatClient chatClient;
private final VectorStore defectKnowledgeBase; // 历史缺陷知识库
private final ProductSpecRepository specRepository;
private static final String QUALITY_JUDGMENT_PROMPT = """
你是一个工业产品质量检测专家。请根据以下信息,对产品质量做出判断。
【产品信息】
产品类型:{productType}
批次号:{batchId}
生产工艺参数:{processParams}
【视觉检测结果】
是否检测到异常:{hasAnomaly}
整体异常评分:{anomalyScore}(0-1,越高越异常)
异常区域数量:{anomalyCount}
异常区域详情:{anomalyRegions}
【产品规格要求】
{productSpec}
【历史相似缺陷案例】
{historicalCases}
请做出以下判断:
1. 质量等级(PASS/BORDERLINE/FAIL)
2. 缺陷类型(如果有)
3. 缺陷严重程度(轻微/中等/严重)
4. 建议处理方式(直接放行/人工复核/直接报废/隔离等待)
5. 可能的缺陷根本原因(从生产工艺角度)
6. 判断置信度(0-1)
重要:如果视觉检测结果不明确(异常评分在 0.4-0.6 之间),必须建议人工复核。
以JSON格式返回:
{
"qualityLevel": "PASS/BORDERLINE/FAIL",
"requiresManualReview": true/false,
"defectDescription": "缺陷描述",
"defectTypes": ["缺陷类型1"],
"defectSeverity": "轻微/中等/严重",
"recommendedAction": "建议操作",
"rootCauseHypothesis": "可能的根本原因",
"confidence": 0.0-1.0
}
""";
public QualityJudgment inspect(QualityInspectionRequest request) {
// 1. 视觉检测
VisionDetectionResult visionResult = visionService.detect(
request.getImageBase64(),
request.getProductType()
);
// 2. 从知识库检索历史相似缺陷
String searchQuery = buildDefectSearchQuery(visionResult, request.getProductType());
List<Document> historicalCases = defectKnowledgeBase.similaritySearch(
SearchRequest.query(searchQuery).withTopK(3)
);
// 3. 获取产品规格
ProductSpec spec = specRepository.findByProductType(request.getProductType());
// 4. LLM 联合判断
String prompt = buildJudgmentPrompt(request, visionResult, spec, historicalCases);
String response = chatClient.prompt()
.user(prompt)
.call()
.content();
QualityJudgment judgment = parseJudgmentResult(response);
judgment.setProductId(request.getProductId());
judgment.setBatchId(request.getBatchId());
judgment.setJudgedAt(LocalDateTime.now());
// 5. 安全兜底:视觉检测异常评分高但LLM判断PASS时,强制转为BORDERLINE
if (visionResult.getOverallAnomalyScore() > 0.7 &&
judgment.getQualityLevel() == QualityJudgment.QualityLevel.PASS) {
log.warn("视觉评分高但LLM判断PASS,强制转为BORDERLINE: productId={}", request.getProductId());
judgment.setQualityLevel(QualityJudgment.QualityLevel.BORDERLINE);
judgment.setRequiresManualReview(true);
}
return judgment;
}
private String buildJudgmentPrompt(QualityInspectionRequest request,
VisionDetectionResult visionResult,
ProductSpec spec,
List<Document> historicalCases) {
String anomalyRegionsDesc = visionResult.getAnomalyRegions().stream()
.map(r -> String.format("位置(%d,%d) 大小%dx%d 类型:%s 置信度:%.2f",
r.getX(), r.getY(), r.getWidth(), r.getHeight(),
r.getAnomalyType(), r.getConfidence()))
.collect(Collectors.joining("\n"));
String historicalCasesText = historicalCases.stream()
.map(doc -> "- " + doc.getContent().substring(0, Math.min(300, doc.getContent().length())))
.collect(Collectors.joining("\n"));
String processParamsText = request.getProcessParams() != null
? request.getProcessParams().entrySet().stream()
.map(e -> e.getKey() + ": " + e.getValue())
.collect(Collectors.joining(", "))
: "无";
return QUALITY_JUDGMENT_PROMPT
.replace("{productType}", request.getProductType())
.replace("{batchId}", request.getBatchId())
.replace("{processParams}", processParamsText)
.replace("{hasAnomaly}", String.valueOf(visionResult.isHasAnomaly()))
.replace("{anomalyScore}", String.format("%.3f", visionResult.getOverallAnomalyScore()))
.replace("{anomalyCount}", String.valueOf(visionResult.getAnomalyRegions().size()))
.replace("{anomalyRegions}", anomalyRegionsDesc.isEmpty() ? "无" : anomalyRegionsDesc)
.replace("{productSpec}", spec != null ? spec.getDescription() : "规格信息不可用")
.replace("{historicalCases}", historicalCasesText.isEmpty() ? "无历史案例" : historicalCasesText);
}
}预测性维护:设备传感器数据 + 故障知识库 RAG
预测性维护的核心逻辑:
- 持续采集设备传感器数据(温度、振动、电流、压力等)
- 统计模型检测异常信号
- 结合故障知识库(以往的维护记录、设备手册),判断可能是什么故障、紧急程度、建议操作
设备故障 RAG 系统
@Service
@Slf4j
public class EquipmentFaultRAGService {
private final ChatClient chatClient;
private final VectorStore equipmentKnowledgeBase;
private final AnomalyDetector sensorAnomalyDetector;
private static final String FAULT_DIAGNOSIS_PROMPT = """
你是一个工业设备故障诊断专家。请根据以下传感器数据异常和设备知识,
对设备状态做出诊断。
【设备信息】
设备ID:{equipmentId}
设备类型:{equipmentType}
设备年龄:{equipmentAge}年
最近一次维护:{lastMaintenanceDate}
【当前传感器数据(异常指标)】
{sensorAnomalies}
【传感器历史趋势(最近24小时)】
{sensorTrend}
【设备手册和故障知识】
{equipmentKnowledge}
【历史故障记录】
{historyFaults}
请提供:
1. 故障诊断(可能是什么问题)
2. 故障严重程度(正常运行/需要关注/建议维护/立即停机)
3. 预计在不干预情况下的故障时间窗口
4. 建议的维护操作(具体步骤)
5. 维护所需备件(如果已知)
以JSON格式返回:
{
"faultDiagnosis": "故障诊断",
"severity": "NORMAL/ATTENTION/MAINTENANCE_NEEDED/SHUTDOWN",
"estimatedTimeToFailure": "预计故障时间(如:2-4小时/1-3天/1-2周)",
"maintenanceActions": ["维护步骤1", "步骤2"],
"requiredParts": ["备件1"],
"confidence": 0.0-1.0,
"urgencyNote": "紧急说明(给维修人员看的)"
}
""";
public FaultDiagnosisResult diagnose(EquipmentSensorData sensorData) {
// 1. 检测传感器异常
List<SensorAnomaly> anomalies = detectSensorAnomalies(sensorData);
if (anomalies.isEmpty()) {
return FaultDiagnosisResult.normal(sensorData.getEquipmentId());
}
// 2. 从设备知识库检索相关知识
String searchQuery = buildFaultSearchQuery(sensorData, anomalies);
List<Document> equipmentKnowledge = equipmentKnowledgeBase.similaritySearch(
SearchRequest.query(searchQuery)
.withTopK(5)
.withFilterExpression("equipmentType == '" + sensorData.getEquipmentType() + "'")
);
// 3. 构建诊断 Prompt
String prompt = buildDiagnosisPrompt(sensorData, anomalies, equipmentKnowledge);
// 4. LLM 诊断
String response = chatClient.prompt()
.user(prompt)
.call()
.content();
FaultDiagnosisResult result = parseDiagnosisResult(response);
result.setEquipmentId(sensorData.getEquipmentId());
result.setDiagnosedAt(LocalDateTime.now());
result.setAnomalies(anomalies);
// 5. 严重故障立即触发报警
if ("SHUTDOWN".equals(result.getSeverity())) {
triggerEmergencyAlert(sensorData.getEquipmentId(), result);
}
return result;
}
private List<SensorAnomaly> detectSensorAnomalies(EquipmentSensorData data) {
List<SensorAnomaly> anomalies = new ArrayList<>();
// 检查各传感器指标是否超出正常范围
data.getSensorReadings().forEach((sensorName, reading) -> {
SensorThreshold threshold = getSensorThreshold(data.getEquipmentType(), sensorName);
if (threshold != null) {
if (reading.getValue() > threshold.getCriticalHigh() ||
reading.getValue() < threshold.getCriticalLow()) {
anomalies.add(new SensorAnomaly(
sensorName, reading.getValue(), threshold,
SensorAnomaly.Level.CRITICAL
));
} else if (reading.getValue() > threshold.getWarningHigh() ||
reading.getValue() < threshold.getWarningLow()) {
anomalies.add(new SensorAnomaly(
sensorName, reading.getValue(), threshold,
SensorAnomaly.Level.WARNING
));
}
}
});
return anomalies;
}
private String buildFaultSearchQuery(EquipmentSensorData data, List<SensorAnomaly> anomalies) {
// 把异常传感器信息构建成自然语言查询
String anomalyDesc = anomalies.stream()
.map(a -> a.getSensorName() + "异常")
.collect(Collectors.joining(" "));
return data.getEquipmentType() + " " + anomalyDesc + " 故障";
}
private String buildDiagnosisPrompt(EquipmentSensorData data,
List<SensorAnomaly> anomalies,
List<Document> knowledge) {
String anomaliesDesc = anomalies.stream()
.map(a -> String.format("%s: 当前值%.2f %s (正常范围:%.2f~%.2f)",
a.getSensorName(), a.getCurrentValue(), a.getLevel().name(),
a.getThreshold().getWarningLow(), a.getThreshold().getWarningHigh()))
.collect(Collectors.joining("\n"));
String knowledgeText = knowledge.stream()
.map(doc -> doc.getContent().substring(0, Math.min(400, doc.getContent().length())))
.collect(Collectors.joining("\n\n---\n\n"));
return FAULT_DIAGNOSIS_PROMPT
.replace("{equipmentId}", data.getEquipmentId())
.replace("{equipmentType}", data.getEquipmentType())
.replace("{equipmentAge}", String.valueOf(data.getEquipmentAge()))
.replace("{lastMaintenanceDate}",
data.getLastMaintenanceDate() != null ? data.getLastMaintenanceDate().toString() : "未知")
.replace("{sensorAnomalies}", anomaliesDesc)
.replace("{sensorTrend}", formatSensorTrend(data.getHistoricalReadings()))
.replace("{equipmentKnowledge}", knowledgeText.isEmpty() ? "无相关知识" : knowledgeText)
.replace("{historyFaults}", "暂无历史故障记录"); // 实际项目中从数据库查询
}
private String formatSensorTrend(Map<String, List<Double>> historicalReadings) {
if (historicalReadings == null || historicalReadings.isEmpty()) return "无历史数据";
StringBuilder sb = new StringBuilder();
historicalReadings.forEach((sensor, values) -> {
if (values.size() > 0) {
double min = values.stream().mapToDouble(Double::doubleValue).min().orElse(0);
double max = values.stream().mapToDouble(Double::doubleValue).max().orElse(0);
double last = values.get(values.size() - 1);
sb.append(sensor).append(": 最小=").append(String.format("%.2f", min))
.append(" 最大=").append(String.format("%.2f", max))
.append(" 最新=").append(String.format("%.2f", last)).append("\n");
}
});
return sb.toString();
}
private void triggerEmergencyAlert(String equipmentId, FaultDiagnosisResult result) {
log.error("【紧急】设备 {} 需要立即停机!诊断:{}", equipmentId, result.getFaultDiagnosis());
// 发送紧急报警:短信、电话、大屏显示等
// ...
}
}制造业 AI 应用架构
设备故障知识库的构建
这个知识库是整套系统的知识基础,要持续运营。
@Service
public class EquipmentKnowledgeIngestionService {
private final VectorStore knowledgeBase;
/**
* 导入设备手册
*/
public void ingestEquipmentManual(String equipmentType, String manualContent) {
// 设备手册通常有清晰的章节结构
// "故障排查"、"常见问题"这类章节是最有价值的
List<ProcessedDocument.DocumentChunk> chunks = processManual(manualContent, equipmentType);
List<Document> docs = chunks.stream()
.map(chunk -> {
Map<String, Object> metadata = new HashMap<>(chunk.getMetadata());
metadata.put("equipmentType", equipmentType);
metadata.put("knowledgeType", "EQUIPMENT_MANUAL");
return new Document(chunk.getContextualContent(), metadata);
})
.collect(Collectors.toList());
knowledgeBase.add(docs);
log.info("设备手册已导入: equipmentType={}, chunks={}", equipmentType, docs.size());
}
/**
* 导入历史维修记录
*/
public void ingestMaintenanceRecord(MaintenanceRecord record) {
String content = String.format("""
设备类型:%s
设备ID:%s
故障时间:%s
故障描述:%s
传感器异常:%s
诊断结果:%s
处理方式:%s
维修时长:%s小时
使用备件:%s
故障根本原因:%s
预防建议:%s
""",
record.getEquipmentType(),
record.getEquipmentId(),
record.getFaultTime(),
record.getFaultDescription(),
record.getSensorAnomalies(),
record.getDiagnosis(),
record.getRepairActions(),
record.getRepairDuration(),
String.join(", ", record.getPartsUsed()),
record.getRootCause(),
record.getPreventionAdvice()
);
Map<String, Object> metadata = new HashMap<>();
metadata.put("equipmentType", record.getEquipmentType());
metadata.put("faultType", record.getFaultType());
metadata.put("knowledgeType", "MAINTENANCE_RECORD");
metadata.put("recordDate", record.getFaultTime().toString());
knowledgeBase.add(List.of(new Document(content, metadata)));
}
}失效安全设计
这是制造业 AI 最重要的工程要求,单独说一下。
@Component
public class QualityInspectionSafetyGuard {
/**
* 安全兜底逻辑:当AI判断不确定时,默认选择更保守的决策
*/
public QualityJudgment applyFailSafe(QualityJudgment llmJudgment,
VisionDetectionResult visionResult,
String productType) {
// 规则1:LLM置信度低时,转为人工复核
if (llmJudgment.getConfidence() < 0.7) {
llmJudgment.setQualityLevel(QualityJudgment.QualityLevel.BORDERLINE);
llmJudgment.setRequiresManualReview(true);
llmJudgment.setRecommendedAction("AI置信度不足,建议人工复核");
}
// 规则2:视觉检测评分与LLM判断不一致时,取更严格的
if (visionResult.getOverallAnomalyScore() > 0.6 &&
llmJudgment.getQualityLevel() == QualityJudgment.QualityLevel.PASS) {
llmJudgment.setQualityLevel(QualityJudgment.QualityLevel.BORDERLINE);
llmJudgment.setRequiresManualReview(true);
}
// 规则3:对于安全关键产品(如汽车零件),任何BORDERLINE都必须人工复核
if (isSafetyCritical(productType)) {
if (llmJudgment.getQualityLevel() != QualityJudgment.QualityLevel.PASS) {
llmJudgment.setRequiresManualReview(true);
}
}
return llmJudgment;
}
private boolean isSafetyCritical(String productType) {
// 安全关键产品列表(根据实际业务配置)
Set<String> safetyCriticalTypes = Set.of(
"BRAKE_COMPONENT", "STEERING_PART", "AIRBAG_MODULE", "ENGINE_VALVE"
);
return safetyCriticalTypes.contains(productType);
}
}总结
制造业 AI 不是互联网 AI 的简单复制,有三点本质差异要记住:
实时性更严苛:生产线不会等你的 AI 响应。质量检测需要在生产节拍内完成,设备监控要毫秒级延迟。
错误代价更高:漏检一个缺陷产品,可能是客诉,可能是召回,可能是安全事故。系统设计必须有"失效安全"机制,AI 不确定时默认选更保守的决策。
可解释性是刚需:质检报告需要说明"为什么判不合格",维护建议需要说明"判断依据是什么"。纯黑盒的 AI 在制造业很难被接受。
LLM 在制造业 AI 里的最大价值,是把设备和工艺的专业知识结构化地利用起来——不管是从设备手册里提取的知识,还是积累的历史维修记录,都可以通过 RAG 在实时判断时被调用。
这是传统规则系统做不到的,也是制造业 AI 最有价值的创新点。
