简介这份ArUco标记检测数据集面向机器人定位导航、增强现实研发及计算机视觉研究者提供真实场景采集的标记识别训练素材。包内共2000个文件以1300个YOLO格式txt标注、698张jpg实拍图像为主另含1个yaml配置文件与1份docx说明文档压缩包约42.21MB。数据划分为905张训练图与395张验证图覆盖多角度、多光照及遮挡条件下的ArUco标记实例标注兼容主流深度学习框架可直接用于目标检测与实例分割任务。已有81人学习下载。读者可借助该数据集快速搭建标记检测模型用于机器人空间定位、AR虚实叠加交互及SLAM算法验证也可作为工程教育中标记识别与定位算法的实践素材减少从采集到部署的偏差。1. ArUco标记检测数据集从一张打印纸到可训练样本的完整链路如果你手上只有一张打印出来的 ArUco 标定板想把它变成能喂给检测模型的数据集中间缺的不是算法而是一整套「采集—标注—增强—校验」的流水线。ArUco 标记检测数据集.zip 这个标题背后真正要解决的是如何批量生成带标注的 ArUco 图像样本让 YOLO 这类检测器学会在复杂光照、遮挡、倾斜下稳定框出每一个 marker。它适合做机器人视觉、相机标定、AR 定位的工程师也适合想拿 ArUco 当练手目标、跑通「自制数据集训练检测模型」全流程的人。我见过太多人卡在第一步——以为找现成数据集就行结果发现公开的 ArUco 数据要么分辨率低要么标注格式对不上自己的训练框架。所以这篇不讲空话直接讲怎么从零造一份能用的 ArUco 标记检测数据集以及每一步会翻车的地方。2. 先搞清楚 ArUco 检测任务到底要标什么2.1 ArUco 标记的物理结构与检测目标定义ArUco 标记本质是一个黑白二值方格外围黑色边框内部是 N×N 的二进制编码矩阵常见 4×4、5×5、6×6 字典。检测任务要模型输出的不是「有没有标记」而是每个标记的四个角点或外接矩形框。如果你按普通目标检测只标一个矩形框后续做位姿估计时精度会掉得厉害因为 ArUco 的位姿解算依赖角点顺序和亚像素精度。所以造数据集前先定标注粒度只做存在性检测就标外接矩形要做位姿估计必须标四个角点再转成矩形框加角点回归头。我一般建议新手先做矩形框版本跑通后再升级到角点。字典选择直接影响类别数。比如 DICT_4X4_50 有 50 个不同 ID你可以把每个 ID 当一个类别也可以把所有 ArUco 归为一个类「marker」。前者适合需要区分 ID 的场景后者适合只关心「有没有标记」的定位任务。类别数决定了标注文件里 class_id 的取值范围也决定了模型输出头维度。别小看这个决定后面改起来要重标一遍。2.2 为什么公开数据集常常不够用公开的 ArUco 数据大多来自标定场景背景干净、光照均匀、标记正对相机模型在这种数据上训出来一到真实场景就崩。真实场景里标记可能贴在移动机器人底盘上、被机械臂遮挡一半、在强反光地面产生高光、或者相机快速运动导致运动模糊。这些退化因素公开数据集很少覆盖。所以自制数据集的核心价值不是「有数据」而是「有你场景里的退化数据」。你要采集的不是标准标定图而是你部署环境下的各种极端样本。另一个坑是标注格式。公开数据常用 COCO JSON 或 Pascal VOC XML而你训练 YOLOv8 需要 YOLO txt 格式训练 MMDetection 又可能要 COCO。格式转换本身不难难的是坐标归一化和类别映射对不上。我习惯在采集阶段就用统一中间格式存一份再写脚本转成各框架需要的格式避免反复转换丢精度。3. 用 OpenCV 批量生成 ArUco 样本与标注3.1 生成单张 ArUco 标记并合成到背景最可控的方式是用 OpenCV 的 aruco 模块生成标记图再随机贴到背景图上同时记录贴图位置作为标注。这样你能精确控制标记大小、旋转角度、透视变换和背景多样性。下面这段代码生成一张合成样本并输出 YOLO 格式标注。import cv2 import numpy as np import random # 使用 4x4 字典50 个 ID aruco_dict cv2.aruco.getPredefinedDictionary(cv2.aruco.DICT_4X4_50) def generate_marker(marker_id, size200): 生成指定 ID 的 ArUco 标记图白底黑码 marker_img cv2.aruco.generateImageMarker(aruco_dict, marker_id, size) return cv2.cvtColor(marker_img, cv2.COLOR_GRAY2BGR) def random_perspective(img, max_warp0.15): 对标记图做随机透视变换模拟倾斜视角 h, w img.shape[:2] d max_warp * min(h, w) pts_src np.float32([[0,0],[w,0],[w,h],[0,h]]) pts_dst pts_src np.float32([ [random.uniform(-d,d), random.uniform(-d,d)], [random.uniform(-d,d), random.uniform(-d,d)], [random.uniform(-d,d), random.uniform(-d,d)], [random.uniform(-d,d), random.uniform(-d,d)] ]) M cv2.getPerspectiveTransform(pts_src, pts_dst) return cv2.warpPerspective(img, M, (w,h), borderValue(255,255,255)) def paste_marker(bg, marker, x, y, scale1.0, angle0): 把标记缩放旋转后贴到背景指定位置返回新图和矩形框 mh, mw marker.shape[:2] new_w, new_h int(mw*scale), int(mh*scale) marker_resized cv2.resize(marker, (new_w, new_h)) # 旋转 M cv2.getRotationMatrix2D((new_w/2, new_h/2), angle, 1.0) cos, sin abs(M[0,0]), abs(M[0,1]) nW, nH int(new_h*sin new_w*cos), int(new_h*cos new_w*sin) M[0,2] (nW/2) - new_w/2 M[1,2] (nH/2) - new_h/2 rotated cv2.warpAffine(marker_resized, M, (nW, nH), borderValue(255,255,255)) # 贴图简单覆盖 bh, bw bg.shape[:2] x1, y1 max(0,x), max(0,y) x2, y2 min(bw, xnW), min(bh, ynH) if x2 x1 or y2 y1: return bg, None bg[y1:y2, x1:x2] rotated[:y2-y1, :x2-x1] return bg, (x1, y1, x2, y2) # 主流程生成一张 640x640 合成图 bg np.full((640,640,3), 180, dtypenp.uint8) # 灰色背景实际可换真实照片 marker generate_marker(random.randint(0,49), 200) marker random_perspective(marker) bg, box paste_marker(bg, marker, 200, 180, scale1.2, anglerandom.uniform(-30,30)) if box: x1,y1,x2,y2 box # YOLO 格式class_id cx cy w h全部归一化到 0-1 cx (x1x2)/2/640 cy (y1y2)/2/640 w (x2-x1)/640 h (y2-y1)/640 print(f0 {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) cv2.imwrite(synth_sample.jpg, bg)这段代码的逻辑是先生成标记图再做透视变换模拟倾斜然后缩放旋转贴到背景上最后把贴图区域转成 YOLO 归一化坐标。参数上max_warp控制透视畸变强度0.15 大约对应 30 度以内倾斜scale控制标记在画面中的占比太小会导致小目标检测困难建议 0.15 到 0.5 之间随机angle控制平面内旋转ArUco 检测本身对旋转鲁棒但训练时加入旋转能提升模型泛化。背景不要只用纯色换成真实场景照片效果更好但要注意背景里不能已经有 ArUco 标记否则标注会冲突。3.2 批量生成与标注文件组织单张生成只是验证真正要造数据集得批量跑并且按训练集/验证集划分。下面脚本批量生成 2000 张同时写 images 和 labels 目录。import os from tqdm import tqdm def batch_generate(out_dir, num2000, img_size640): img_dir os.path.join(out_dir, images) lbl_dir os.path.join(out_dir, labels) os.makedirs(img_dir, exist_okTrue) os.makedirs(lbl_dir, exist_okTrue) for i in tqdm(range(num)): bg np.full((img_size,img_size,3), random.randint(100,220), dtypenp.uint8) # 每张图随机贴 1-3 个标记 n_markers random.randint(1,3) lines [] for _ in range(n_markers): mid random.randint(0,49) m generate_marker(mid, random.randint(120,260)) m random_perspective(m, max_warprandom.uniform(0.05,0.2)) x random.randint(0, img_size-100) y random.randint(0, img_size-100) bg, box paste_marker(bg, m, x, y, scalerandom.uniform(0.6,1.5), anglerandom.uniform(-45,45)) if box: x1,y1,x2,y2 box cx (x1x2)/2/img_size cy (y1y2)/2/img_size w (x2-x1)/img_size h (y2-y1)/img_size lines.append(f0 {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) cv2.imwrite(os.path.join(img_dir, faruco_{i:05d}.jpg), bg) with open(os.path.join(lbl_dir, faruco_{i:05d}.txt), w) as f: f.write(\n.join(lines)) batch_generate(./aruco_dataset, num2000)这里每张图随机贴 1 到 3 个标记模拟多标记场景。n_markers上限别设太高否则标记重叠严重标注框会互相覆盖模型学不清。实际项目中如果标记密集建议用角点标注而不是矩形框。生成完后目录结构是aruco_dataset/images/*.jpg和aruco_dataset/labels/*.txt这是 YOLOv8 默认读取格式。验证集可以从这批里按 8:2 随机划分或者单独生成一批不同随机种子的。注意合成数据不能完全替代真实采集。合成图边缘干净、无噪声、无运动模糊模型在合成数据上 mAP 可能虚高。建议合成数据占 70%真实采集占 30%混合训练。4. 真实场景采集与半自动标注流程4.1 用 OpenCV 检测器预标注再人工修正真实场景采集的图如果全靠手标2000 张能标到怀疑人生。ArUco 本身有成熟的检测算法可以用cv2.aruco.detectMarkers先跑一遍预标注把检测到的角点转成矩形框再人工筛掉误检和漏检。下面代码对文件夹内所有图做预标注输出 YOLO 格式。import glob def prelabel_with_aruco(img_dir, out_lbl_dir, dict_idcv2.aruco.DICT_4X4_50): detector_params cv2.aruco.DetectorParameters() aruco_dict cv2.aruco.getPredefinedDictionary(dict_id) detector cv2.aruco.ArucoDetector(aruco_dict, detector_params) os.makedirs(out_lbl_dir, exist_okTrue) for img_path in glob.glob(os.path.join(img_dir, *.jpg)): img cv2.imread(img_path) h, w img.shape[:2] corners, ids, rejected detector.detectMarkers(img) lines [] if ids is not None: for corner in corners: pts corner[0] # 4x2 x1, y1 pts[:,0].min(), pts[:,1].min() x2, y2 pts[:,0].max(), pts[:,1].max() cx (x1x2)/2/w cy (y1y2)/2/h bw (x2-x1)/w bh (y2-y1)/h lines.append(f0 {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) name os.path.splitext(os.path.basename(img_path))[0] with open(os.path.join(out_lbl_dir, name.txt), w) as f: f.write(\n.join(lines))detectMarkers返回的 corners 是每个标记的四个角点顺序固定。转矩形框时取 min/max 即可。DetectorParameters里可以调minMarkerPerimeterRate控制最小标记尺寸默认 0.03如果小目标漏检多就降到 0.01。预标注后一定要人工过一遍重点看漏检——ArUco 检测器在强光、模糊、遮挡下会漏这些漏掉的样本恰恰是模型最该学的。4.2 标注格式转换与数据集划分预标注和合成标注都是 YOLO txt但如果你要用 MMDetection 或 Detectron2得转 COCO JSON。转换时注意类别 id 从 0 开始还是从 1 开始YOLO 从 0COCO 通常从 1。下面是一个最小转换脚本。import json def yolo_to_coco(img_dir, lbl_dir, out_json, class_names[marker]): coco {images:[], annotations:[], categories:[]} for i, name in enumerate(class_names): coco[categories].append({id:i1, name:name, supercategory:none}) ann_id 1 for idx, img_path in enumerate(glob.glob(os.path.join(img_dir,*.jpg))): img cv2.imread(img_path) h, w img.shape[:2] coco[images].append({id:idx1, file_name:os.path.basename(img_path), width:w, height:h}) lbl_path os.path.join(lbl_dir, os.path.splitext(os.path.basename(img_path))[0].txt) if not os.path.exists(lbl_path): continue with open(lbl_path) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue cid, cx, cy, bw, bh map(float, parts) x (cx - bw/2)*w y (cy - bh/2)*h coco[annotations].append({ id:ann_id, image_id:idx1, category_id:int(cid)1, bbox:[x, y, bw*w, bh*h], area:bw*w*bh*h, iscrowd:0 }) ann_id 1 with open(out_json,w) as f: json.dump(coco, f)转换后建议用pycocotools加载一遍验证看有没有 bbox 越界或面积为 0 的标注。数据集划分用train:val 8:2如果样本少于 500 张用 7:3 并做交叉验证。划分时按场景分不要把同一段视频的连续帧分到训练和验证两边否则验证集精度虚高。5. ArUco 数据集制作避坑与排查清单5.1 标注框贴边导致训练报错现象训练时 YOLOv8 报Label class x is out of bounds或 loss 变 NaN。原因合成时标记贴到图像边缘矩形框超出图像范围归一化坐标出现负数或大于 1。解决在paste_marker里做裁剪后重新计算框或者生成时限制贴图位置在[0, img_size - marker_size]内。已经生成的数据可以用脚本过滤把越界标注裁到[0,1]并丢弃面积过小的框。5.2 合成数据与真实数据分布差异过大现象合成数据训练 mAP 0.95真实测试掉到 0.4。原因合成背景太干净、标记边缘太锐利、无噪声无模糊。解决合成时加高斯噪声、运动模糊、亮度对比度抖动背景用真实场景照片而不是纯色。更彻底的做法是只把合成数据当预训练再用真实标注做微调。我一般会保留 20% 合成数据混入真实训练集防止模型遗忘合成域特征。5.3 多标记重叠导致标注互相覆盖现象一张图里两个标记挨太近矩形框大面积重叠模型分不清。原因ArUco 标记本身有黑色边框重叠后视觉上连成一片。解决生成时限制标记间距或者改用角点标注加实例分割。如果场景确实密集建议把检测任务拆成两步先检测标记区域再在区域内做 ID 分类。另外标注时确保每个框只包一个标记不要把两个标记框成一个。5.4 字典 ID 与类别映射错位现象训练时类别数设成 50但标注里 class_id 只出现 0 到 10模型输出大量空类。原因生成时只用了部分 ID但类别数按字典全量设了。解决统计标注里实际出现的 class_id重新映射成连续类别或者把所有 ID 统一归为 class 0。如果确实要区分 ID确保每个 ID 至少有 50 张样本否则长尾类别训不动。5.5 验证集泄漏导致精度虚高现象验证集 mAP 和训练集差不多但部署后效果差。原因验证集图片和训练集来自同一段视频或同一合成批次随机种子接近分布几乎一样。解决划分时按采集批次或场景分合成数据用不同随机种子生成验证集。更严格的做法是留一个完全独立的真实场景测试集不参与任何训练和调参。6. 用角点回归把 ArUco 检测精度再提一档矩形框检测只能给你标记的大致位置做位姿估计时角点误差直接放大成旋转误差。如果你要拿 ArUco 做相机标定或 AR 锚点建议在检测头加一个角点回归分支。具体做法把标注从矩形框升级为四个角点坐标训练时用 YOLOv8-pose 或自定义关键点检测头输出 4 个关键点。数据格式从class cx cy w h变成class cx cy w h px1 py1 px2 py2 px3 py3 px4 py4其中角点顺序按 ArUco 检测器返回的顺时针顺序从左上开始。角点标注的生成可以直接用detectMarkers返回的 corners不用手工标。但要注意角点顺序必须一致否则模型学乱。我一般固定顺序为标记自身坐标系下的左上、右上、右下、左下。合成数据生成时透视变换后的四个角点就是标注直接存下来即可。训练时角点损失用 Wing Loss 或 OKS比 L2 对小误差更敏感。验证角点精度的方法用训练好的模型预测角点再拿角点做solvePnP解位姿和真实位姿比旋转误差。如果旋转误差在 2 度以内基本够用。我踩过的坑是角点顺序搞反导致位姿解算出来标记朝向完全错误排查了一整天才发现是标注顺序问题。所以角点数据集一定要写个可视化脚本把标注画到图上肉眼检查一遍别偷这个懒。最后说个习惯每做完一份 ArUco 数据集我都会拿 20 张图跑一遍检测器预标注和人工标注比 IoU低于 0.7 的挑出来看是标注问题还是检测器问题。这个校验步骤花不了半小时但能挡住大部分脏数据。希望帮到你。本文还有配套的精品资源点击获取