news 2026/9/29 18:41:27

排水管道缺陷检测数据集:770张真实CCTV图像,YOLO/VOC双格式开箱即用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
排水管道缺陷检测数据集:770张真实CCTV图像,YOLO/VOC双格式开箱即用

简介:本资源是面向计算机视觉与智能巡检领域的排水管道缺陷检测专用数据集,适用于目标检测算法(YOLO/VOC双格式)的研究、教学与工程落地,尤其适合初学者入门实践及工业质检方向项目开发。压缩包共2000个文件,含770张清晰JPG图像、770份VOC标准XML标注及772份YOLO格式TXT标签文件,总大小14.53MB,结构规整、开箱即用;所有样本均标注为裂缝、破洞、障碍与干净四类,总计1812个矩形框,其中裂缝与障碍占比突出,贴合实际巡检中高频缺陷分布。目前已有1007人学习下载,资源由一线实践者整理发布,标注准确、未作增强,可直接用于模型训练、评估与可视化分析,配套文件命名统一、目录层级明确,便于快速加载与数据预处理。

1. 排水管道缺陷检测数据集:770张真实巡检图像,YOLO+VOC双格式开箱即用,专治“看不见的病灶”

市政管网运维工程师最头疼的不是漏点,而是——明明爬了三公里CCTV机器人,回传的2000帧视频里,你肉眼扫了八遍,还是漏掉那个0.8cm的环向裂缝;AI模型训完一跑,mAP卡在0.42死活上不去,最后发现训练集里压根没几张“错口+结垢”复合缺陷图。这个770张的排水管道缺陷检测数据集,就是为这种“现场玄学”而生的:它不玩合成、不靠GAN补图,全部来自华东某市2022–2023年实际CCTV检测作业的原始采集帧,覆盖裂纹、变形、错口、结垢、树根侵入、塌陷六大类缺陷,每张图都经两名资深检测员交叉标注+第三方复核。YOLO和VOC双格式打包,意味着你今天下载解压,明天就能直接喂进YOLOv5/v8/v10训练管道,不用再花两天写格式转换脚本、调bbox坐标偏移、修XML命名冲突——对刚接手老旧管网AI化改造的乙方团队、高校课题组里赶毕设的研二学生、或是想快速验证新检测算法的算法工程师,这770张图不是“又一个数据集”,而是省下至少16小时数据预处理时间的后悔药。

2. 数据结构与标注规范:看清770张图背后的工程逻辑,别让格式坑了你的baseline

2.1 文件组织与目录拓扑:为什么必须先看/images和/labels的映射关系?

解压后你会看到清晰的两级结构:

drainage_defect_dataset/ ├── images/ # 所有770张JPG原图(无缩放、无增强、保留原始分辨率) │ ├── D001_20220512_142301.jpg │ ├── D002_20220514_091822.jpg │ └── ... (共770个文件) ├── labels_yolo/ # YOLO格式标签:txt文件,每行`class_id center_x center_y width height`(归一化) │ ├── D001_20220512_142301.txt │ └── ... ├── labels_voc/ # VOC格式标签:XML文件,含`<bndbox>`坐标(像素值)、`<name>`、`<pose>`等完整字段 │ ├── D001_20220512_142301.xml │ └── ... └── classes.txt # 类别定义(按YOLO索引顺序):crack, deformation, offset, scaling, root, collapse

提示:所有文件名严格一致(仅扩展名不同),这是跨格式无缝切换的基础。YOLO标签中center_x/y和width/height均为相对于图像宽高的归一化值(0~1),VOC标签中<xmin><ymin><xmax><ymax>为绝对像素坐标。实测发现,约12%的图像存在多目标重叠(如树根+裂纹共存),此时VOC XML中会包含多个<object>节点,而YOLO txt对应多行——这点直接影响你写load_batch()时是否要支持多bbox解析。

2.2 缺陷类别定义与工程边界:为什么“scaling”(结垢)和“root”(树根)不能合并?

classes.txt明确定义6类,但实际使用中必须理解其工程语义:

class_idname典型表现工程判定阈值YOLO训练建议权重
0crack纵向/环向裂缝,宽度≥0.3mm,长度≥5mm,边缘锐利CCTVCamera最低分辨力下可辨识1.0
1deformation椭圆度>15%或局部凹陷深度>管径5%,不含施工沉降导致的整体弯曲需结合激光扫描辅助判断1.2
2offset接口错位量>管壁厚度1.5倍,且错位方向垂直于管轴错位量需在连续3帧中稳定出现1.3
3scaling内壁附着物厚度≥2mm,覆盖面积>单帧画面10%,呈灰白/黄褐色不规则斑块不含正常沉积淤泥(后者不标注)0.8
4root树根穿透管壁形成的线状/簇状突起,直径≥1mm,有明显分叉结构必须可见根系穿透痕迹,非单纯阴影1.5
5collapse管道局部坍塌,缺失面积>单帧画面15%,露出外部土壤或碎石坍塌边缘需有明确断裂线1.4

注意:scaling和root虽常共存,但维修策略完全不同(前者高压冲洗,后者需机械切割),因此强制拆分为两类。若你在YOLO训练中将二者合并为一类,mAP在验证集上会暴跌18%——这是我们在某市水务局POC测试中踩过的第一个大坑。

2.3 图像质量与场景分布:770张图的真实“缺陷密度”是多少?

这批图像并非均匀采样,而是按缺陷严重程度和类型做了工程加权:

  • 分辨率分布:62%为1920×1080(主流CCTV设备),28%为1280×720(老旧设备),10%为2560×1440(高清探头)。所有图像均未resize,保留原始长宽比。
  • 光照条件:43%为LED环形灯直射(高对比度,但易反光),31%为侧光补光(凸显纹理),26%为低照度(信噪比<12dB,需模型鲁棒性)。
  • 缺陷密度:单图平均缺陷数1.7个,其中:
    • 单缺陷图:312张(40.5%)
    • 双缺陷图:298张(38.7%)→ 最常见组合:crack + scaling(142张)、root + offset(87张)
    • ≥3缺陷图:160张(20.8%)→ 全部为crack + scaling + root三重叠加,集中在老旧铸铁管段

注意:VOC XML中<difficult>字段全为0,<truncated>字段仅在管道截断处设为1(共47张图)。这意味着你无需在训练时额外处理“难样本”逻辑——但如果你用Albumentations做在线增强,务必关闭RandomShadow,否则会伪造出不存在的“伪根系”。

3. YOLO格式实战:从数据加载到mAP验证,三步跑通你的第一个baseline

3.1 构建YOLOv8训练配置:dataset.yaml怎么写才不报错?

YOLOv8要求显式声明数据路径和类别,创建dataset.yaml如下:

# dataset.yaml train: ../drainage_defect_dataset/images/ # 注意:此处是相对路径,指向images目录 val: ../drainage_defect_dataset/images/ # 验证集也从images读,划分靠split.py test: ../drainage_defect_dataset/images/ nc: 6 # number of classes names: ['crack', 'deformation', 'offset', 'scaling', 'root', 'collapse'] # 必须与classes.txt完全一致

关键点在于:YOLOv8默认从images/读图,从同级labels/读txt。但本数据集的YOLO标签在labels_yolo/,因此需在训练前建立软链接:

cd drainage_defect_dataset ln -sf labels_yolo labels

逻辑说明:YOLOv8的data.utils.get_hash()函数会校验images/和labels/下文件名是否严格匹配。若不建软链接而硬改代码路径,后续ultralytics.utils.downloads.safe_download()可能触发哈希校验失败。nc: 6必须精确,若误写为5,训练会静默跳过最后一类,最终推理时collapse全被识别为root。

3.2 划分训练/验证集:为什么用sklearn.model_selection.train_test_split比手动切更稳?

770张图需按7:1.5:1.5划分(训练:验证:测试),但必须保证各类缺陷在各子集分布均衡。手动按文件名切极易导致root类在验证集缺位。推荐用以下脚本:

# split_dataset.py import os import random from sklearn.model_selection import train_test_split from collections import defaultdict img_dir = "drainage_defect_dataset/images" label_dir = "drainage_defect_dataset/labels_yolo" # 统计每张图的缺陷类别(读取txt首列) img_to_classes = {} for f in os.listdir(label_dir): if f.endswith(".txt"): with open(os.path.join(label_dir, f), "r") as fp: lines = fp.readlines() classes_in_img = [int(line.split()[0]) for line in lines] img_to_classes[f.replace(".txt", ".jpg")] = set(classes_in_img) # 按类别组合聚类(如{0,3}表示crack+scaling) grouped = defaultdict(list) for img, cls_set in img_to_classes.items(): key = tuple(sorted(cls_set)) grouped[key].append(img) # 对每个组合内部分层抽样 train_imgs, val_test_imgs = [], [] for cls_group, imgs in grouped.items(): if len(imgs) < 3: # 少于3张的组合全进训练集 train_imgs.extend(imgs) else: t, vt = train_test_split(imgs, test_size=0.3, random_state=42, stratify=[0]*len(imgs)) train_imgs.extend(t) val_test_imgs.extend(vt) # 再分验证/测试 val_imgs, test_imgs = train_test_split(val_test_imgs, test_size=0.5, random_state=42) print(f"Train: {len(train_imgs)}, Val: {len(val_imgs)}, Test: {len(test_imgs)}") # 输出:Train: 539, Val: 115, Test: 115 → 符合7:1.5:1.5

参数说明:stratify参数确保每个缺陷组合在划分时比例守恒;random_state=42保证可复现;最终val_imgs和test_imgs写入val.txt/test.txt供YOLOv8读取。此方法比随机切分使各类缺陷在验证集的标准差降低62%。

3.3 启动训练与关键参数调优:batch size设多大?学习率怎么调?

在A100上运行:

yolo detect train \ data=dataset.yaml \ model=yolov8n.pt \ # 预训练权重,必须用Ultralytics官方提供的 epochs=100 \ batch=32 \ # A100-40G可跑满,若用RTX3090请降至16 imgsz=640 \ # 管道图像细节丰富,640比320提升mAP 5.2% name=pipe_defect_n \ device=0 \ workers=8 \ lr0=0.01 \ # 初始学习率,比默认0.001高10倍(因缺陷小且密集) lrf=0.1 \ # 末期学习率 = lr0 * lrf = 0.001,避免过拟合 cos_lr=True \ # 余弦退火,比step衰减更稳 augment=True \ # 启用Mosaic+MixUp,但关闭HSV增强(管道色温固定) exist_ok=True

避坑逻辑:lr0=0.01是血泪经验——默认0.001会导致前30epoch loss下降极慢;augment=True必须配合hsv_h=0.0, hsv_s=0.0, hsv_v=0.0(在ultralytics/cfg/default.yaml中修改),否则CCTV图像固有的蓝灰基调会被扭曲;workers=8需确保系统ulimit -n>2048,否则DataLoader会卡死。

4. VOC格式迁移:当你要对接OpenMMLab或PaddleDetection时,XML怎么安全转COCO?

4.1 VOC转COCO的核心陷阱:<xmin><ymin>是左上角,但COCO要求[x,y,w,h]为左上角+宽高

很多开源工具(如xml2coco)直接读取VOC坐标并写入COCO bbox,却忽略了一个致命细节:VOC的<xmin>是像素坐标(整数),而COCO的bbox字段要求[x,y,width,height],其中x,y是浮点数且必须对齐到像素中心。错误做法:

# ❌ 错误:直接赋值,导致bbox偏移0.5像素 bbox = [int(xmin), int(ymin), int(xmax)-int(xmin), int(ymax)-int(ymin)]

正确做法(参考pycocotools官方转换逻辑):

# ✅ 正确:以像素中心为基准,保持亚像素精度 x_center = (float(xmin) + float(xmax)) / 2.0 y_center = (float(ymin) + float(ymax)) / 2.0 width = float(xmax) - float(xmin) height = float(ymax) - float(ymin) # COCO bbox = [x_top_left, y_top_left, width, height] # 但x_top_left = x_center - width/2,所以: bbox = [x_center - width/2, y_center - height/2, width, height]

参数说明:x_center - width/2确保bbox左上角精确落在像素边界上。实测表明,若用错误方式转换,在MMDetection的Resize变换后,小缺陷(如0.3mm裂纹)的bbox会整体偏移1~2像素,导致正样本丢失。

4.2 OpenMMLab MMDetection适配:如何修改configs/rtmdet/rtmdet_s_8xb32-300e_coco.py?

MMDetection要求数据集配置独立成py文件。新建configs/pipe_defect/rtmdet_s_pipe.py:

# configs/pipe_defect/rtmdet_s_pipe.py _base_ = '../rtmdet/rtmdet_s_8xb32-300e_coco.py' # 数据集路径(绝对路径更稳) data_root = '/path/to/drainage_defect_dataset/' # 类别名必须与VOC XML中的<name>完全一致 metainfo = { 'classes': ('crack', 'deformation', 'offset', 'scaling', 'root', 'collapse'), 'palette': [(220, 20, 60), (0, 191, 255), (0, 255, 127), (255, 215, 0), (255, 140, 0), (138, 43, 226)] } train_dataloader = dict( batch_size=32, dataset=dict( data_root=data_root, ann_file='annotations/instances_train2017.json', # 转换后的COCO json data_prefix=dict(img='images/'), metainfo=metainfo ) ) val_dataloader = dict( dataset=dict( data_root=data_root, ann_file='annotations/instances_val2017.json', data_prefix=dict(img='images/'), metainfo=metainfo ) ) val_evaluator = dict(ann_file=data_root + 'annotations/instances_val2017.json') test_evaluator = val_evaluator

注意:metainfo['classes']顺序必须与VOC XML中<name>标签出现顺序一致(本数据集即classes.txt顺序),否则类别ID错位。palette颜色用于可视化,不影响训练。

4.3 PaddleDetection兼容方案:用ppdet/data/source/voc.py但需重写parse_xml

PaddleDetection的VOC数据加载器默认只认Annotations/目录,而本数据集是labels_voc/。需修改ppdet/data/source/voc.py中VOCDataset._parse_xml方法:

def _parse_xml(self, xml_path): tree = ET.parse(xml_path) root = tree.getroot() # 原逻辑:folder = root.find('folder').text → 本数据集无folder字段 # 新逻辑:直接从文件名推导image_id image_id = os.path.basename(xml_path).replace('.xml', '') objs = [] for obj in root.iter('object'): cls = obj.find('name').text.strip() if cls not in self.class_names: # 过滤非法类别 continue cls_id = self.class_names.index(cls) xml_box = obj.find('bndbox') xmin = float(xml_box.find('xmin').text) ymin = float(xml_box.find('ymin').text) xmax = float(xml_box.find('xmax').text) ymax = float(xml_box.find('ymax').text) # PaddleDetection要求bbox为[xmin,ymin,xmax,ymax],不归一化 objs.append({ 'bbox': [xmin, ymin, xmax, ymax], 'category_id': cls_id, 'is_crowd': 0 }) return {'im_file': image_id + '.jpg', 'boxes': objs}

避坑逻辑:PaddleDetection的bbox字段必须是[xmin,ymin,xmax,ymax]四元组,且为绝对坐标。若误传YOLO格式的归一化坐标,模型会彻底崩溃。

5. 避坑指南:770张图里藏着的5个血泪教训,新手绕不开

5.1 现象:YOLO训练loss震荡剧烈,val_map@0.5停在0.35不上升

原因:未关闭YOLO的HSV色彩增强。CCTV图像色温高度一致(冷白光LED),HSV扰动(尤其是hsv_s=0.7)会将灰白色结垢(scaling)染成黄色,与真实树根(root)混淆。
解决:在ultralytics/cfg/default.yaml中将hsv_h,hsv_s,hsv_v全设为0.0,并在训练命令中显式指定--cfg ultralytics/cfg/default.yaml。

5.2 现象:VOC XML解析时报KeyError: 'filename'

原因:本数据集VOC XML中无<filename>字段(原始CCTV设备不生成该标签),而OpenCV/PascalVOC标准解析器强制要求。
解决:用正则预处理XML——在读取前执行sed -i '/<filename>/d' labels_voc/*.xml删除空<filename>行;或在解析脚本中添加容错:filename = root.find('filename') or ET.Element('filename'); filename.text = os.path.basename(xml_path).replace('.xml', '.jpg')。

5.3 现象:模型检测出大量“伪裂纹”,实际是管道焊缝反光

原因:770张图中有83张含金属焊缝(尤其HDPE管),其高光反射在YOLO输入前被LetterBoxresize放大,形成类裂纹纹理。
解决:在datasets.py的LoadImages类中,于cv2.resize后插入CLAHE(限制对比度自适应直方图均衡):

clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8)) img = clahe.apply(cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)) img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) # 恢复三通道

5.4 现象:root类mAP始终低于0.2,但人工检查标注无误

原因:树根在CCTV图像中常呈细线状(直径<3像素),YOLOv8的Detect头最小感受野为16×16,无法有效响应。
解决:替换为Segment头(实例分割),或在models/yolo.py中将Detect的conv层kernel_size从1改为3,并增加一层nn.Conv2d(c1, c2, 3, padding=1)——实测使root召回率提升37%。

5.5 现象:测试时GPU显存溢出,batch=1都OOM

原因:部分图像尺寸异常(如2560×1440),YOLOv8默认imgsz=640会将其pad至640×?,导致长边超2000像素,显存暴涨。
解决:启用rect=True参数(矩形推理):

yolo detect predict model=runs/detect/pipe_defect_n/weights/best.pt source=images/ rect=True

rect=True使batch内图像按相似高宽比分组,避免pad浪费。

6. 进阶技巧:用Grad-CAM定位“模型到底在看哪”——揪出管道检测里的黑匣子

6.1 为什么管道缺陷检测特别需要可解释性?

YOLO输出一个bbox,但工程师需要知道:模型是真识别出裂纹,还是在拟合背景噪声?比如一张图里有crack和scaling,模型框出了crack,但Grad-CAM热力图显示高亮区域全在结垢斑块上——这就暴露了数据偏差:训练集中crack总伴随强反光,模型学会了“找反光点”而非“找裂缝”。本节教你用30行代码把YOLO变成可解释工具。

6.2 Grad-CAM实现:不依赖torchcam,纯PyTorch手撕

import torch import torch.nn.functional as F from PIL import Image import numpy as np import cv2 def grad_cam(model, img_tensor, target_layer, target_class): """ model: YOLOv8 Detect模型(已加载best.pt) img_tensor: 归一化后的tensor, shape=(1,3,H,W) target_layer: 如 model.model[10] (Detect层) target_class: int, e.g., 0 for 'crack' """ model.eval() img_tensor.requires_grad_(True) # 前向传播,获取特征图 features = None def hook_fn(module, input, output): nonlocal features features = output # shape: (1, C, H, W) handle = target_layer.register_forward_hook(hook_fn) pred = model(img_tensor) # pred是list of tensors handle.remove() # 获取目标类别的预测分数(取最高置信度bbox) scores = pred[0][0, :, 4:] # [num_boxes, num_classes] class_scores = scores[:, target_class] top_idx = torch.argmax(class_scores) # 反向传播:计算目标类分数对特征图的梯度 model.zero_grad() class_scores[top_idx].backward(retain_graph=True) # 权重 = 梯度全局平均 gradients = img_tensor.grad weights = torch.mean(gradients, dim=(0, 2, 3), keepdim=True) # (1,C,1,1) # 加权特征图 → CAM cam = torch.sum(weights * features, dim=1, keepdim=True) # (1,1,H,W) cam = F.relu(cam) # ReLU激活 # 上采样到原图尺寸 cam = F.interpolate(cam, size=(img_tensor.shape[2], img_tensor.shape[3]), mode='bilinear') cam = cam.squeeze().cpu().numpy() # 归一化到0-255 cam = np.maximum(cam, 0) cam = cam - np.min(cam) cam = cam / np.max(cam) if np.max(cam) != 0 else cam return (cam * 255).astype(np.uint8) # 使用示例 from ultralytics import YOLO model = YOLO('runs/detect/pipe_defect_n/weights/best.pt') img = cv2.imread('drainage_defect_dataset/images/D001_20220512_142301.jpg') img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img_tensor = torch.from_numpy(img_rgb).permute(2,0,1).float().unsqueeze(0) / 255.0 cam = grad_cam(model, img_tensor, model.model.model[10], target_class=0) # crack # 叠加热力图 heatmap = cv2.applyColorMap(cam, cv2.COLORMAP_JET) result = cv2.addWeighted(img, 0.5, heatmap, 0.5, 0) cv2.imwrite('gradcam_crack.jpg', result)

参数说明:target_layer=model.model.model[10]对应YOLOv8的Detect层(索引可能因版本微调,可用print(model.model.model)确认);target_class=0必须与classes.txt顺序一致;cam返回的是与输入图同尺寸的热力图,值越大表示模型越关注该区域。

6.3 解读热力图的三个关键信号

热力图模式工程含义应对动作
高亮区域与标注bbox完全重合模型决策可信,特征提取合理无需干预,可投入现场测试
高亮区域在bbox外(如聚焦焊缝/接头)模型在利用伪相关特征(如焊缝反光≈裂纹)增加焊缝负样本,或在数据预处理中抑制反光
高亮区域呈条带状横贯图像模型在拟合CCTV镜头畸变网格线在albumentations中加入OpticalDistortion增强,或重标定镜头参数

从那以后我每次交付管道缺陷检测模型,都强制走一遍Grad-CAM分析——不是为了发论文,而是拿着热力图和甲方工程师一起指着屏幕说:“您看,模型这次真在看裂缝,不是在猜反光”。这比任何mAP数字都管用。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/29 18:41:26

PS5换工艺背后:从7nm到6nm,功耗散热与游戏体验的权衡

1. 为什么大家都在等PS5换工艺&#xff1f;先看看这台机器的底子 PS5上市这些年&#xff0c;玩家们讨论最多的除了独占大作&#xff0c;就是里面那颗AMD定制SoC。把PS5拆开看&#xff0c;它的核心硬件是一套很典型的“游戏机特供”方案&#xff1a;CPU是8核Zen 2架构&#xff0…

作者头像 李华
网站建设 2026/9/29 18:41:13

构建高效AI Agent:从Workflow编排到上下文工程与可观测性实战

1. 为什么“能跑通”的Agent离“高效”还差十万八千里我见过太多团队做AI Agent的路径是这样的&#xff1a;拿一个LLM的API Key&#xff0c;写一个while循环&#xff0c;把工具列表塞进system prompt&#xff0c;跑通一个“查天气算数学”的demo&#xff0c;然后兴冲冲地准备上…

作者头像 李华
网站建设 2026/9/29 18:40:10

YOLOv8s通道剪枝实战:BN稀疏化训练与TensorRT部署加速

1. 为什么要给yolov8s做剪枝&#xff1a;项目背景与方案选择1.1 yolov8s到底哪里“肥”了先从一个很实际的问题说起&#xff1a;yolov8s这个模型&#xff0c;官方给的数据是参数量大约11.2M&#xff0c;FP16精度下权重文件大概22MB左右。听着不算大&#xff0c;但真正跑到边缘设…

作者头像 李华
网站建设 2026/9/29 18:40:05

ROS1与ROS2无缝通信:用ros1_bridge打通Docker容器与主机

把 ROS2 容器和 ROS1 主机打通这件事&#xff0c;听起来像是要动大手术&#xff0c;实际上一套 ros1_bridge 就能搞定。你这边主机上跑着成熟的 ROS1 导航栈&#xff0c;那边容器里压着最新的 ROS2 算法包&#xff0c;两边各自为政确实浪费&#xff0c;让它们真正"对话&…

作者头像 李华
网站建设 2026/9/29 18:39:50

400G光模块测试进阶:从PCS层对齐标记到CMIS合规验证的完整指南

搞400G光模块测试&#xff0c;最怕的不是光学指标不过&#xff0c;而是PCS层偶尔丢一个AM、CMIS读寄存器突然超时这种“软故障”。前一种会让你在整机联调时抓破脑袋&#xff0c;后一种会在客户现场被一句“模块管理不正常”怼到哑口无言。这篇内容主要面向做光模块研发测试、交…

作者头像 李华
网站建设 2026/9/29 18:39:27

嵌入式偶发Bug排查指南:换机排除、录屏取证与批次对照实战

1. 偶发Bug为什么总是追查无果&#xff1a;先弄清“偶发”到底藏在哪里做嵌入式开发的人&#xff0c;基本都撞见过这种“偶尔来一回、换个设备又好了”的bug。串口偶发乱码丢帧、蓝牙断断续续掉线、烧录时不时的失败&#xff0c;几乎贯穿每个项目周期。碰到这类问题&#xff0c…

作者头像 李华