☰
起重机YOLO目标检测训练全流程:从标注格式到避坑指南
2026/10/7 12:56:00 网站建设 项目流程

简介:面向起重机(crane)目标检测任务,这份已标注图像数据可供计算机视觉学习者、算法工程师及项目开发者直接用于YOLO系列模型的训练与效果验证,既适合初学者熟悉标准标注流程,也适合有经验者快速评估模型。数据集合计约2900张图像,类别仅含起重机一种,已完成训练集、验证集划分,省去额外拆分与格式转换的中间环节,整体适配工业场景目标检测的常见需求。资源包共2000个文件,主体为1999个txt格式的YOLO标注文件,另有1个Python脚本show.py用于标注结果可视化,压缩包为7z格式,整体约146.53MB,文件命名规范、目录结构清晰。目前已有498人学习下载。借助该资源,学习者能跳过人工采集与标注环节,直接获得统一格式的标签数据,高效开展模型训练、参数调优与精度对比;同时可结合标注预览功能逐一核对目标框质量,降低误标漏标对训练效果的影响,系统掌握目标检测从数据准备到模型评估的完整流程。

1. 起重机crane图像目标检测数据:拿到2900张YOLO标签图后,为什么你的模型还是不会数起重机

起重机crane图像目标检测数据,约2900张图和YOLO格式标签,听起来像是可以直接拿去训练模型的半成品。但说句实话,我拿到手第一版直接训,结果在工地测试时塔吊和履带吊互相误检,夜晚场景更是惨不忍睹。这份数据的价值不在“已标注”三个字,而在于你愿不愿意花时间做格式核对、类别映射和分层划分。它适合两类人:一是想快速验证YOLO训练流程的学生,二是做工业吊装视觉识别、需要私有数据但不想从零标注的工程师。如果你正要拿这类数据跑YOLO,这篇笔记能帮你少踩一半坑。

2. 先看懂YOLO标注格式:train.txt、images、labels三类文件怎么协同

很多刚接触YOLO数据集的同学,以为标注格式就是画个框、存成txt。实际上YOLO标注格式在三处容易出问题:类别id从0开始、坐标归一化、图片和标签同名对应。先把这套格式彻底看透,后面训练才不会翻车。

一份标准的YOLO目标检测数据集目录里,images和labels是成对存在的。images放图片,labels放同名txt文件,train.txt/val.txt列出参与训练的图片路径,三者缺一不可。如果数据交付只有images和labels,那train.txt需要自己生成,这一章我会把检查标签的脚本一并写出来。

2.1 YOLO格式的txt标注到底存了什么:类别、中心坐标、宽高

一张图片对应一个同名的txt文件,比如images/0001.jpg对应labels/0001.txt。txt里每一行代表一个目标框,五个数值:类别id、中心点x坐标、中心点y坐标、框宽度、框高度,所有坐标都除以图片宽高做了归一化。例如:

# labels/0001.txt - 对应 images/0001.jpg # 第一行:类别0,中心点(0.5234, 0.4812),框宽0.3124,高0.2513 0 0.5234 0.4812 0.3124 0.2513 # 第二行:类别1,中心点(0.8012, 0.6123),框宽0.1820,高0.1345 1 0.8012 0.6123 0.1820 0.1345

注意YOLO格式不保存目标的类别名,只保存数字。类别名单独放在classes.txt或data.yaml里,顺序必须和数字一一对应。这种设计的麻烦在于:如果标注工具导出时,类别列表顺序和准备给训练用的顺序不同,模型训出来就是错位的。我在一次从LabelImg的xml转YOLO时,就因为没检查顺序,训练一个礼拜后才发现识别结果全反了。

归一化坐标的意思不是像素,而是相对图片宽高的比例。假设图片宽1600、高1200,实际边界框左上角是(300, 400),右下角是(1100, 1000),那么中心点x等于(300+1100)除以2再除以1600,结果是0.4375,中心点y等于(400+1000)除以2再除以1200,结果是0.5833,宽是800除以1600等于0.5,高是600除以1200等于0.5。这个换算一旦搞错,最直接的结果就是框全偏到角落。

我自己拿到数据集,第一件事就是写个Python函数,把标注转回像素坐标,然后叠在图片上人工抽查:

import os def load_yolo_label(txt_path, img_w, img_h): boxes = [] with open(txt_path, 'r', encoding='utf-8') as f: for line in f: line = line.strip() if not line: continue parts = line.split() if len(parts) != 5: print(f"格式异常: {txt_path}: {line}") continue cls_id, cx, cy, w, h = parts # 转成像素坐标,便于可视化或后处理 x1 = (float(cx) - float(w)/2) * img_w y1 = (float(cy) - float(h)/2) * img_h x2 = (float(cx) + float(w)/2) * img_w y2 = (float(cy) + float(h)/2) * img_h boxes.append((int(cls_id), x1, y1, x2, y2)) return boxes

这个函数把每一行5个数值拆出来,先还原成像素坐标,再交给可视化或后处理。如果某一行的数值不是5个,多半是转换脚本没处理空框或多余逗号。另外,从COCO或VOC转来的数据还有一个隐蔽问题:COCO的框是左上角x、左上角y、宽、高,VOC是左上角和右下角,转换时很容易把宽高和坐标搞混。我一般要求转换脚本保留至少6位小数,别在归一化时四舍五入成整数,否则框的边界会漂移几个像素。

2.2 目录结构和类别映射:从0开始的类别id不能乱

交付的数据集目录通常是这样的:

crane_dataset/ ├── images/ │ ├── 0001.jpg │ ├── 0002.jpg │ └── ... ├── labels/ │ ├── 0001.txt │ ├── 0002.txt │ └── ... └── classes.txt

有些版本还会附train.txt和val.txt,每行是图片的完整或相对路径,这在做YOLOv5/v8训练时可以直接在data.yaml里引用。没有也不要紧,用脚本自己生成,下一章给方案。

classes.txt里每一行是一个类别名,第一行对应id 0,第二行对应id 1。如果数据里只有truck_crane和tower_crane两类,那所有txt里只会出现0和1。一旦出现数字2,就说明要么类别没列全,要么labels里混了其他目标。

用Ultralytics YOLO训练时,data.yaml里的names列表顺序必须和标注txt里的id一致。比如data.yaml里names第一个是tower_crane,而classes.txt第一行是truck_crane,那么所有标注为0的框都会被当塔吊训练,推理结果自然不对。所以拿到数据集的第一步不是急着训练,而是写个命令统计每个txt里的类别id分布:

# 抽查前5张图片的标注类别id分布 for f in labels/*.txt; do echo "$f: $(awk '{print $1}' "$f" | sort | uniq -c)"; done | head -5

这条命令会把每个文件里出现的类别id及次数打出来。如果classes.txt只有两个类别,但输出里出现了id=2,那就要把对应的图片找出来删掉或重标。我习惯把这一步叫做“标签向量校准”,虽然听起来玄学,但它能在训练前拦截90%的类别错位问题。

我还会顺手统计整个数据集的标注框宽高分布,用一段Python代码打印宽高中的中位数、最大值和长宽比异常值。这里的数据决定了后续训练该怎么选imgsz和尺度先验,而不是闷头跑默认参数。

import os from statistics import median ws, hs = [], [] for txt in os.listdir('labels'): with open(os.path.join('labels', txt)) as f: for line in f: parts = line.strip().split() if len(parts) != 5: continue _, _, _, w, h = map(float, parts) ws.append(w) hs.append(h) print('中位宽:', median(ws), '中位高:', median(hs)) print('最大宽:', max(ws), '最大高:', max(hs))

如果中位宽高都在0.2到0.5之间,说明目标框普遍占图面积较大,用640训练就够了。如果大量框小于0.05,那就要考虑提高imgsz到1280,或者把图片中目标区域截出来单独训练。搞懂了YOLO格式,你可以开始动手训练了。

3. 用起重机数据集训练YOLOv8:从目录整理到训练命令

看懂了格式,接下来就是把2900张图喂给YOLO。我习惯先用Ultralytics YOLOv8,因为一条pip install ultralytics就能装好,而且它原生支持这种标注格式,不需要额外写数据加载器。

3.1 把2900张图划分成train/val/test:随机划分脚本与比例选择

我平时会按8:1:1划分,2900张图大约变成2320训练、290验证、290测试。如果场景单一,验证集10%也够;如果包含多种机型,建议把验证集提到20%。但最关键的是按“设备来源”或“采集场景”分层,而不是简单地对每张图随机打乱。否则同一个塔吊拍摄的连续帧会同时出现在训练集和验证集,mAP虚高,部署后立刻打回原形。

先写一个基础脚本,把有效的图片过滤出来再随机划分:

import os import random from collections import defaultdict random.seed(42) img_dir = "crane_dataset/images" label_dir = "crane_dataset/labels" # 只保留有对应txt且txt非空的jpg,避免后面报错 valid_imgs = [] for img_name in os.listdir(img_dir): stem = os.path.splitext(img_name)[0] label_path = os.path.join(label_dir, stem + ".txt") if os.path.exists(label_path) and os.path.getsize(label_path) > 0: valid_imgs.append(img_name) else: print("跳过无效样本:", img_name) random.shuffle(valid_imgs) train_ratio = 0.8 n_train = int(len(valid_imgs) * train_ratio) n_val = int(len(valid_imgs) * 0.1) train_imgs = valid_imgs[:n_train] val_imgs = valid_imgs[n_train:n_train + n_val] test_imgs = valid_imgs[n_train + n_val:] with open("train.txt", "w") as f: for img in train_imgs: f.write(os.path.join(img_dir, img) + "\n") with open("val.txt", "w") as f: for img in val_imgs: f.write(os.path.join(img_dir, img) + "\n") with open("test.txt", "w") as f: for img in test_imgs: f.write(os.path.join(img_dir, img) + "\n") print(f"可用图 {len(valid_imgs)} 张 -> train {len(train_imgs)} / val {len(val_imgs)} / test {len(test_imgs)}")

脚本里我先把没有对应标签或标签为空的图过滤掉,免得训练时找不到标签。随机种子固定成42,保证每次复现结果一致。写到train.txt里的路径可以是相对路径,但YAML里path字段配置好后,最好写相对路径,这样换机器不用改路径。如果你需要分层划分,就在valid_imgs之前按图片名的前缀或目录分组,然后对组进行随机分配。

提示:如果这批数据是从同一个监控视频里抽帧得到的,请先按时间连续段做分组,再划分。否则连续帧高度相似,验证结果会非常乐观。

3.2 写data.yaml并启动训练:YOLOv8的命令与关键参数

划分好文件后,在数据集根目录写一个crane.yaml:

# crane.yaml path: /home/yourname/crane_dataset # 改成你的实际绝对路径 train: train.txt # 也可以写 images/train val: val.txt names: 0: truck_crane 1: tower_crane 2: crawler_crane

然后启动训练:

pip install ultralytics yolo detect train data=crane.yaml model=yolov8s.pt imgsz=640 epochs=100 batch=16 device=0

这里解释一下参数:model=yolov8s.pt会从官方预训练权重开始训练,迁移学习能加快收敛。如果数据只有2900张图,不要轻易用yolov8x,参数太多反而容易过拟合。imgsz=640是训练分辨率,batch=16受显存限制,8G卡建议降到8。epochs=100对10类以内的小数据集足够,如果只是想验证数据质量,先跑到50轮看趋势。

训练日志里每轮会打印Box(P, R, mAP50, mAP50-95)、box_loss、cls_loss、dfl_loss。其中box_loss是边界框回归损失,cls_loss是分类损失,dfl_loss是分布焦点损失,三者一起构成YOLOv8的损失函数。如果cls_loss一直不降,先怀疑标签错位;如果box_loss很大,先检查坐标归一化。约2900张图,yolov8s在单张3060上100个epoch大约2到3小时。如果中途发现loss在抖,可以先停掉,改学习率或batch。

4. 起重机目标检测的3个必调参数:尺度先验、imgsz和yolo损失函数怎么设

很多人拿到数据集直接跑默认参数,训练完后发现精度不够,就怪数据不行。实际上,起重机这个目标的特殊性在于:既有横跨整张图的塔吊臂架,也有远处只有几十个像素的小吊车。默认配置不是为这种尺度分布设计的,必须手动调三个东西。

4.1 尺度先验(anchor机制):别让网络从零猜目标的大小

如果你用的是YOLOv5或YOLOv7,anchor是必调的。YOLOv8虽然是anchor-free架构,但检测头依然在不同尺度特征图上做预测,所以理解目标尺度分布依然关键。起重机目标的长宽比很极端,塔吊臂架往往是扁长的横条,普通预设框偏向正方形,导致回归难收敛。

我一般会把所有标注框的宽高统计出来,看散点图。常用的做法是运行YOLOv5自带的聚类脚本,或者手动算一下中位宽高。比如数据里中位宽0.35、中位高0.2,长宽比接近1.75,那预设框就要偏横向。YOLOv5可以在模型yaml里直接指定anchors,格式是:

anchors: - [10, 13, 16, 30, 33, 23] - [30, 61, 62, 45, 59, 119]

如果不是很熟练,不要手写,用python train.py --data crane.yaml --cfg models/yolov5s.yaml让它自动计算,训练时会打印新的anchor。对于YOLOv8,虽然没有anchor参数,但你同样可以观察训练时各特征层的检测收益,如果大目标层持续损失很高,说明应该增强高分辨率输入。

4.2 imgsz:640还是1280?

训练分辨率直接影响小目标检出能力。我用同一份数据分别跑640和1280,mAP50-95能差4到7个百分点,但显存和训练时间也翻了一倍。起重机场景要分清楚:如果摄像机固定在工地入口,目标都很近很大,640足够,而且推理速度快;如果监控画面覆盖整个堆场,塔吊在几百米外,那就必须用1280。

显存紧张时,别直接放弃1280,可以试试多尺度训练。Ultralytics支持imgsz=640同时开启deterministic和cache,但我常用的做法是训练时把imgsz设为[640, 1280]之间的随机值,让模型适应不同尺度。具体命令里可以写作imgsz=640,然后在超参数里开Mosaic和MixUp增强。如果推理端用TensorRT部署,分辨率尽量固定,因为动态分辨率会增加优化复杂度。

4.3 yolo损失函数:分类、定位和置信度权重的影响

YOLO的损失函数在不同的版本里写法不一样。YOLOv5在hyp.scratch-low.yaml里暴露cls和box权重,默认cls=0.5、box=0.05。把box调高会让模型更关注框的位置精度,但也可能让分类变差。YOLOv8把权重固化在源码里,新手建议不要直接改损失权重,先观察三个loss的下降趋势。

如果box_loss降了但cls_loss震荡,说明类别分布不均衡。2900张图里如果90%是塔吊、5%是履带吊,模型会倾向于把所有东西都识别成塔吊。这时调损失权重只能缓解,真正有效的是对少数类别做复制增强,或者换一种更平衡的分类损失。如果精度卡在0.8上不去,先去看是不是某些类别的正样本太少,别急着动损失函数。

5. YOLO标注数据避坑:5条现场排查记录

这一章是我自己的血泪经验。每一次翻车,最后都不是模型结构的问题,而是数据细节的问题。下面5条按“现象、原因、解决”写清楚,拿到数据集后先对着过一遍。

5.1 空标注文件导致训练中断

现象:训练到一半,报错说某张jpg没有对应的label,或读取label时维度为0,进程直接退出。

原因:标注工具在导出时对没有目标的图片生成了空txt,或者漏导出,导致图片和标签数量对不上。

解决:划分数据集前先跑一条命令过滤:

find images -name "*.jpg" | while read f; do stem=`basename "$f" .jpg` [ -s "labels/$stem.txt" ] || echo "缺少标签或为空: $f" done

这条命令会把所有“有图片但没有非空标签”的样本列出来。我遇到过一次,2900张图里有17张空标签,全是从标注平台导出时漏掉了。删掉或补标后,训练才正常。

5.2 类别id错位

现象:训练时loss正常,验证mAP也不错,但实际测试把塔吊识别成履带吊,且错得很有规律。

原因:classes.txt里的顺序和data.yaml里的names顺序不一致,导致类别id语义错位。比如classes.txt第一行是truck_crane,而data.yaml里names第一个是tower_crane,那么所有id为0的框都会被当塔吊训练。

解决:用可视化脚本抽查前5张图,把类别id画在框上:

import cv2 import glob for txt in glob.glob('labels/*.txt')[:5]: img_path = txt.replace('labels', 'images').replace('.txt', '.jpg') img = cv2.imread(img_path) with open(txt) as f: for line in f: cid, cx, cy, w, h = map(float, line.split()) x1 = int((cx - w/2) * img.shape[1]) y1 = int((cy - h/2) * img.shape[0]) x2 = int((cx + w/2) * img.shape[1]) y2 = int((cy + h/2) * img.shape[0]) cv2.rectangle(img, (x1, y1), (x2, y2), (0,255,0), 2) cv2.putText(img, str(int(cid)), (x1, y1-5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,0,255), 1) cv2.imshow('check', img) cv2.waitKey(0)

框上的数字就是类别id。对照classes.txt看对不对,不对就改data.yaml,不要动标注文件。

5.3 “crane”一词带来的数据污染

现象:数据集中有一些图片,框的目标完全不是起重机,而是大型机械臂、桥梁,甚至鸟类。

原因:收集数据时用了“crane”做关键词,英文里起重机与鹤是同一个词。如果是公开爬取的数据,混入鹤的图片非常常见。

解决:逐张过滤掉没有起重机的图片。如果标注是现成的,不可能一张张看,至少把预测置信度最高的误检样本抽出来人工复查。我在一份数据里遇到过把白鹤当成塔吊的情况,模型学到的特征是“翅膀”而不是“吊臂”。这种污染样本只要占3%,就会明显拉低准确率。

5.4 边界框越界导致gt宽高为0

现象:训练日志出现NaN,或者loss突然暴涨然后卡住。

原因:标注框的部分坐标落在图片外,比如x1等于-2。归一化坐标如果没裁剪,还原成像素后宽高可能是负数。

解决:写一个清洗脚本,把所有框裁剪到[0,1]区间内,并过滤掉宽高小于0.001的值。

def clean_label(txt_path): lines = [] with open(txt_path) as f: for line in f: vals = list(map(float, line.strip().split())) if len(vals) != 5: continue cid, cx, cy, w, h = vals # 先把坐标裁剪到[0,1],避免越界 cx, cy = max(0, min(1, cx)), max(0, min(1, cy)) w, h = max(0, min(1, w)), max(0, min(1, h)) if w < 0.001 or h < 0.001: continue lines.append(f"{int(cid)} {cx} {cy} {w} {h}") with open(txt_path, 'w') as f: f.write("\n".join(lines))

需要注意,如果框越界很多,单纯clip会让中心点偏移。更严格的做法是先把框还原成像素坐标,裁到图片边界后重新计算中心点和宽高。

5.5 验证集划分不当导致指标虚高

现象:训练时验证集mAP达到0.95,部署到工地上直接降到0.6。

原因:划分数据集时对每张图独立随机,同一个设备在相邻视频帧里同时出现在训练集和验证集,模型相当于“背过答案”。只要换一个没见过的机位,泛化能力立刻打回原形。

解决:按采集视频段分组,把同一个视频段的所有帧放到同一个集合里。如果数据集没有元数据,可以用感知哈希对图片去重,把相似帧聚类后每类只保留一张,或者把相似组整体划分到同一侧。这样虽然会减少有效训练帧数,但换来的泛化能力是值得的。

6. 用mAP曲线和误检图验证数据集质量:小样本快速判读法

训练跑完,先别急着导出部署。我习惯做三件事。第一,看PR曲线。YOLO训练完会输出PR_curve.png,如果曲线在召回率0.7时突然掉头向下,说明这个类别的样本太少;如果曲线是阶梯状,说明目标尺度差异太大,imgsz可能不够。第二,看混淆矩阵。如果两个类互相混淆,大概率是标注类别边界不清,比如塔吊和履带吊的吊臂外观太像,要么重新定义类别,要么合并类别。第三,也是我最依赖的:用验证集输出一批置信度在0.25到0.4之间的误检图,人工看一遍。这些图才是模型真正拿不准的地方。

我一般会跑一条推理命令,把低置信度的结果保留下来:

yolo detect predict model=runs/train/exp/weights/best.pt source=val_images/ conf=0.25 save_txt=False save_conf=True

然后翻看runs/detect/predict里的图。凡是框错位的,十有八九是标签画错;凡是漏检的,十有八九是目标太小或遮挡。有一次我训练的模型mAP50到了0.85,但实际工地上一片夕阳就把塔吊漏了,原因就是训练集里没有逆光样本。后来我刻意把一些低亮度图片复制三份并调低对比度,loss分布才恢复正常。从那以后,我每次训练都会单独留5%的现场照片做validation,而不是盲目用数据集自带的划分。

如果你拿到这份2900张的起重机数据集,我的建议是:先花半天核对标签、清洗坐标、分层划分,再小跑50个epoch看损失曲线。这套流程走通,再花时间调imgsz和损失函数权重。数据本身是死的,真正让模型好用的是你对数据边界和场景坑的判断。希望帮到你。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询