简介围绕中国机器人大赛暨Robocup机器人世界杯中国赛机器人先进视觉赛道3D识别赛项的完整参赛资料包面向备战机器人大赛的高校学生、科研人员与视觉开发爱好者。代码经严格测试可运行并附有设计文档既可直接借鉴也适合作为课程设计、毕业设计或项目立项的起步模板。压缩包共112个文件内容以png图像、cpp源码、py脚本和h头文件为主搭配md笔记、docx说明文档、Qt相关pro/ui文件及json配置整体仅4.54MB轻量易下载。从代码结构可看出包内代码覆盖图像预处理、矩形检测、最外层轮廓提取、界面交互等3D识别关键环节并按开发阶段保存了多个版本便于对照学习与二次修改。目前已有254人学习或浏览适合需要在机器人视觉赛项中快速搭建方案、借鉴代码架构与排错思路的读者。1. 中国机器人大赛3D识别赛项为什么模型认得准分数却上不去当年我们队第一次参加中国机器人大赛暨Robocup机器人世界杯中国赛的机器人先进视觉赛道3D识别赛项带着一套在2D检测上拿过高分的方案进场结果被位姿评分按在地上摩擦。复盘时才反应过来这个赛项考的根本不是“认得全”而是“找得准”——网络要在深度相机给出的场景点云里同时输出目标类别、三维包围盒和6D位姿任何一个环节偏几厘米、歪几度分就没了。这篇笔记面向准备参赛或做视觉抓取落地的工程师把赛项拆成任务定义、数据准备、模型训练、避坑和验证几条线讲清楚到底怎么做、参数怎么设、坑在哪。与其说它是算法赛不如说它是一场数据工程和调试能力的综合考核。2. 赛项拆解Robocup视觉赛道的3D识别任务、数据格式与计分逻辑2.1 任务定义检测、分割与6D位姿估计三件事所谓3D识别赛项任务从来不只有“识别”两个字。拆开看是三件事叠加第一3D目标检测在场景点云或深度图里找到目标在哪、是哪个类别输出三维包围盒第二语义分割部分年份要求逐点区分前景背景把桌面、托盘、干扰物从目标里剥出去第三位姿估计输出目标相对相机坐标系的平移向量 t 和旋转矩阵 R一共6个自由度。前两件事解决“它是什么、在哪”第三件事解决“它现在是什么朝向、机械臂能不能抓”。这套任务定义决定了你的方案架构必须同时建模检测和位姿而不是像2D检测那样画个框就交差。从赛项名字也能看出来它属于机器人先进视觉赛道最终服务对象是机器人操作。所以评测不会停留在“检测框准不准”而是看“给你一个位姿机械臂能不能抓起来”。这也解释了为什么那些在2D目标检测上表现很好的队伍到了这个赛项会集体翻车2D检测的精度单位是像素3D识别的精度单位是毫米和度。像素框稍微偏一点视觉上看不出来但深度方向误差3厘米就足以让夹爪撞到物体边缘。2.2 计分逻辑AP、ADD、ADD-S与5cm/5°判定计分方式是整个赛项里最容易被忽视、又最值得先读的部分。不同年份、不同赛项可能会在mAP、ADD、ADD-S、5cm/5°这几种指标里做组合权重还不一样。我在备赛时习惯先把官方评分脚本读一遍确认到底哪个指标占大头再决定训练往哪个方向使劲。下面这张表是这几类指标的常见计算方式和对方案的影响指标常见计算方式对方案的影响mAP3D IoU超过阈值常见0.5或0.25判为检测正确按类别平均只要检测框不够贴IoU不达标直接丢分ADD把物体模型点用预测位姿变换到相机系与真值模型点算最近点平均距离小于阈值常见10mm/20mm才算正确平移误差和旋转误差耦合一个5°的朝向偏差在100mm尺寸物体上可能产生近8mm的模型点偏移ADD-S对称物体按最近点匹配允许绕对称轴翻转后的最近点圆柱、长方体这类对称物要是没用ADD-S分数低得没法看5cm/5°平移误差小于5cm且旋转误差小于5°判定为一次成功匹配直观、好报告很多赛项直接拿它做最终排序依据这里有个血泪经验优先保位姿及格率而不是保mAP。mAP刷到0.9但位姿及格率只有0.3总分照样垫底。原因很简单检测框差一点还有IoU容错位姿差一点直接偏离阈值。而且位姿误差里旋转误差比平移误差更隐蔽模型输出看起来“方向对了”其实绕某个轴转了十几度在ADD口径下就会暴露出来。2.3 官方资料包怎么拆数据集结构、标注格式与baseline代码拿到这套参赛资料包第一件事不是解压后急着找代码而是先把目录结构搞清楚。这类资料包通常有四块内容赛项规则文档、训练和验证数据集、baseline代码、评分脚本。数据集部分常见的组织方式是每个样本一个文件夹里面放场景点云或深度图加相机内参以及一个标注文件。标注格式大多为JSON或文本字段大致是目标类别、3D中心坐标(x,y,z)、长宽高、旋转四元数或旋转矩阵。不同年份标注字段名可能不一样我一般会先写一个“标注转通用格式”的脚本把所有数据统一成一套自己能解析的中间格式再喂给训练框架。by官方资料包里给的baseline代码不一定是性能最好的但它是“口径最准”的。我见过不止一支队伍放着baseline不管自己另起炉灶写训练流程最后指标对不上官方评分白忙半个月。正确做法是先把baseline在本地跑通用它的输出配合评分脚本得到一个分数作为参考线再在这个基础上去换网络、调参数。评分脚本的指标口径一旦确认后面所有本地验证都有了统一标尺。提示赛前第一周只做两件事——跑通官方评分脚本、确认指标口径。口径没定之前所有训练都是盲人摸象。3. 深度相机选型与点云预处理把原始数据变成能训练的样子3.1 深度相机选型结构光、ToF、双目怎么选3D识别的数据源直接决定后续所有工作的上限。如果官方数据集是用某一类相机采集的你实际比赛用的相机最好和它同类型否则域差异会让模型精度掉一截。常见三种深度相机方案在赛项里的表现差别很大结构光、ToF、双目。下面这张表是我在室内桌面场景下几种方案的实际感受方案代表类型优点缺点赛项适配度结构光RealSense D435类近距离精度高纹理重建细强光、黑色物体容易丢深度室内桌面首选ToF工业ToF相机帧率高对环境光不敏感点云噪声大边缘飞点明显可以用但预处理要加重双目工业双目相机户外可用成本可控弱纹理表面直接失效标定繁琐不太推荐做桌面密集识别我一般会优先选择与官方数据集接近的结构光相机而且固定安装位置、固定高度。深度相机安装在机械臂上方45到70厘米处视场角正好覆盖整个工作台是比较省事的布置。如果赛项规则允许带自己的传感器提前两周就要把相机固定好因为后面所有滤波参数、ROI范围都依赖这个安装位姿。临时换相机位置等于所有预处理参数重调一遍这种事情赛前干一次就够了。3.2 点云预处理降采样、离群点滤波与ROI直通原始深度点云不能直接进网络噪声和背景占比太高。我常用的预处理管线是三步体素降采样、统计离群点滤波、直通滤波裁ROI。下面是典型实现用Open3D处理单帧场景点云import open3d as o3d import numpy as np def clean_scene_pcd(path, voxel0.006, nb20, ratio2.0, z_lim(0.2, 1.0)): # 读取原始场景点云 pcd o3d.io.read_point_cloud(path) # 1) 体素降采样控制点密度6mm体素在桌面场景是安全起点 pcd pcd.voxel_down_sample(voxel) # 2) 统计离群点滤波每个点看周围20个邻居距离均值超过2倍标准差则剔除 pcd, _ pcd.remove_statistical_outlier(nb_neighborsnb, std_ratioratio) # 3) 直通滤波只保留相机坐标系下z0.2~1.0m范围内的点裁掉桌面以下和天花板 pts np.asarray(pcd.points) mask (pts[:, 2] z_lim[0]) (pts[:, 2] z_lim[1]) pcd pcd.select_by_index(np.where(mask)[0]) return pcd三个参数里最值得调的是voxel和z_lim。voxel设0.0055mm会让点云更密、细节更清楚但训练显存和速度压力上来了设0.0110mm速度快不少但小物件的边缘细节会丢位姿精度跟着下降。z_lim要根据相机安装高度来设不要照抄我的范围否则直通滤波可能把目标物体一半切掉。统计滤波的ratio也不宜调太大2.0到3.0之间够用调太大容易把薄壁物体边缘的真实点当成噪声删掉。这步预处理在训练和推理阶段必须用同一份代码、同一组参数。我见过有人训练时用干净的离线数据线上推理时又换了一套滤波参数结果模型输入分布都变了精度直接崩掉。预处理管线一旦定下来就把它固化成脚本训练、验证、推理共用。3.3 坐标系与手眼标定把相机位姿变成机器人位姿如果赛项只要求在相机坐标系下输出位姿那坐标系问题可以暂时不管。但只要涉及机械臂抓取加分项或者评测方要求输出机器人基座坐标系下的位姿手眼标定就是绕不开的一关。固定相机朝下的安装方式属于eye-to-hand构型标定目标是求相机坐标系到机器人基座坐标系的齐次变换矩阵 T_cam2base。常见做法是用棋盘格或圆点标定板让机械臂末端带着标定板走十几组位姿用OpenCV或ROS2生态里的标定工具求解。拿到T_cam2base后网络输出的位姿要再做一次变换才能交给机器人import numpy as np def transform_pose_to_base(trans, rot, T_cam2base): # trans: (3,) 相机系下目标平移 # rot: (3,3) 相机系下目标旋转矩阵 T np.eye(4) T[:3, :3] rot T[:3, 3] trans T_base T_cam2base T # 齐次矩阵左乘完成坐标系变换 return T_base[:3, 3], T_base[:3, :3]这里的坑是很容易把T_cam2base的方向搞反写成求逆之前的矩阵。验证方法很简单把一个已知位姿的物体放在机器人可到达的位置先用相机识别它的位姿再用上面这个函数变换到基座系最后用示教器手动把机械臂末端移到那个点看误差是否在合理范围。这个闭环验证只要做一次就能确认标定结果到底对不对。视觉slam里常用的ICP精配准也能用来做标定验证但桌面场景用标定板更直接、更稳。4. 训练3D识别模型VoteNet路线的标注、超参与最小推理闭环4.1 网络选型VoteNet、PointPillars与模板匹配怎么选3D识别不是必须上深度学习但想在赛项里拿到稳定高分端到端网络通常是性价比最高的选择。先说几种常见路线的边界。模板匹配或特征匹配方案不需要训练、也不需要GPU适合目标姿态固定、数量少的情况但桌面上一旦出现堆叠、遮挡、多目标它就崩。PointPillars是自动驾驶场景优化的擅长稀疏大场景而赛项桌面场景目标密集、尺寸小体素化的分辨率会成为瓶颈。VoteNet这类直接从场景点云做vote聚类、输出3D包围盒和语义的网络天然适配室内小场景多目标识别我一般会先拿它跑通整个闭环再根据剩余时间决定要不要加位姿精修。选型时还要注意一个边界如果官方提供的是RGB-D数据而非纯点云也可以走2D检测加深度反投影的路线但位姿精度受深度边缘噪声影响很大物体表面反光时深度值直接失真抓取场景下不太够用。相比之下直接从点云学习位姿的网络对深度噪声鲁棒一些因为它在训练时就见过各种残缺的点云形态。4.2 训练超参数输入点数、batch、学习率怎么定VoteNet训练配置里最影响结果的是输入点数和学习率调度。输入点数决定了每次前向看到的场景密度也直接关系显存占用学习率调度则决定模型能不能在赛项那种中低数据量下收敛到能用的位姿精度。下面是我在单卡RTX 3090级别机器上常用的起点配置参数推荐起点说明输入点数20000~40000采样太多显存吃紧太少小物体细节丢失voxel_size0.005~0.01建议与预处理阶段保持一致batch_size8显存不够降到4但要相应降低初始学习率learning_rate0.001配合余弦退火或step衰减最后收敛到0.0001附近optimAdamW权重衰减设0.0001比SGD更稳epochs50~100看验证ADD及格率曲线决定是否提前停loss权重vote 1.0、bbox 1.0、sem 0.5位姿优先就把bbox权重提到1.5怎么判断训练有没有实际效果别盯着训练loss曲线要看验证集上的ADD及格率。训练loss掉到平台但ADD不涨优先怀疑标注位姿出错而不是模型容量不够。我遇到过数据里四元数转旋转矩阵时符号搞反的情况训练loss看似正常验证位姿却绕了180°这种问题看loss曲线根本看不出来只能靠验证集的位姿误差分布暴露。4.3 推理脚本从场景点云到6D位姿的最短实现训练完成后推理阶段最关键的是把网络输出从归一化坐标系还原到真实尺度。很多队伍在这一步出错是因为归一化和反归一化处理不对称。VoteNet的常见输入要求是把点云缩放到单位球内推理时就要用同样的中心和平移缩放因子把预测的box还原回去。一个最小推理实现大致是这样的import numpy as np import torch def predict_pose(points, model, voxel, max_scaleNone): # points: (N,3) 预处理后的场景点云 center (points.max(0) points.min(0)) / 2.0 scale max_scale if max_scale is not None else np.abs(points - center).max() pts_n (points - center) / scale with torch.no_grad(): out model(torch.from_numpy(pts_n).float().unsqueeze(0).cuda()) # out中含pred_box、pred_sem等pred_box尺寸约为(M,333) boxes out[pred_box][0].cpu().numpy() * scale center scores out[pred_sem][0].softmax(-1).max(-1).values.cpu().numpy() return boxes, scores, center, scale这段代码里最容易出问题的是scale。如果训练时用的是max-all所有坐标轴一起取最大值推理时也必须用max-all不能用per-axis逐轴缩放否则坐标轴各向异性缩放box还原后形状对不上。我建议把scale策略写成一个全局配置项训练和推理共用同一个函数来归一化点云彻底避开这类不对称问题。后处理阶段还要按score阈值过滤掉低置信度预测再用3D IoU做NMS合并重叠proposal这两步直接影响mAP后面避坑章节再细说。5. 避坑3D识别赛项最容易翻车的五个环节5.1 深度噪声导致点云“飘”位姿跟着歪现象模型在干净数据上验证很好一接真实深度相机输出位姿抖动尤其物体边缘和反光面附近物体中心位置忽左忽右。原因深度相机在物体边缘、黑色塑料表面、强反射区域会产生飞点或空洞点云表面像长了毛刺。网络对输入点云的整体分布敏感毛刺改变了表面几何vote中心被带偏。解决预处理阶段在统计滤波之外再加一步边界噪声过滤思路是把每个点的法向量算出来法向量方向突变异常的点直接剔除。另外可以在滤波时把体素降到5mm让边缘细节更完整而不是靠加大滤波强度“硬洗”。记住这个教训脏数据靠调网络参数救不回来先花钱花时间把数据洗干净。5.2 坐标系“差了一个姿态”位姿看着对但抓取永远偏现象检测得分很高位姿可视化出来也贴在物体表面上但把位姿发给机械臂执行末端永远偏离真实位置有时绕轴转了个诡异角度。原因手眼标定的变换矩阵方向搞反或者把相机的RGB坐标系和深度坐标系混用了。常见的是T_cam2base直接用了标定工具输出的原始矩阵没做求逆也没有做闭环验证。解决每次换相机位置后都做一次完整的闭环验证。找一个明确的方块物体放在工作台上识别出位姿后变换到基座系再用机械臂示教器手动移动末端到这个点对比。误差超过5毫米就要怀疑标定矩阵有问题先检查是不是需要求逆再检查RGB与深度对齐是否完成。这套验证流程二十分钟就能做完但能避免赛场上浪费几个小时反复试错。5.3 训练数据覆盖不了评测场景本地好、现场崩现象本地验证集ADD及格率0.8现场评测掉到0.4模型还是同一个数据也来自同一个赛项但就是不稳定。原因训练集和评测场景存在域差异常见变量包括相机安装高度、桌面纹理、物体摆位角度、环境光照。深度相机对光照尤其敏感同一台相机在不同光强下深度噪声水平差很多。解决把数据增强当成后悔药赛前多做点比赛后多调参强。可用做法是对点云做随机翻转、随机旋转、随机遮挡模拟把物体可能出现的姿态分布撑大。翻转要小心对称物体圆柱绕轴翻转是合法的但不对称物体翻了就错。另一个实用手段是深度归一化把评测场景深度缩放到训练分布范围内能压掉一部分相机型号差异带来的误差。5.4 对称物体位姿“反了”ADD-S救了评分却救不了抓取现象圆柱、盒子这类对称物体评分用ADD-S时看起来及格率不低但把位姿发给机械臂绕对称轴的方向经常乱转抓取直接失败。原因对称物体在观测上天然存在位姿等价性模型学到的是“绕轴角度无所谓”的分布。ADD-S评估允许最近点匹配掩盖了绕轴角度的错误。这不是评分方的问题而是任务物理属性决定的。解决如果赛项要求真实抓取训练时对对称物体要额外约束或者推理阶段加一个ICP精配准。ICP的输入是用预测位姿把物体CAD模型变换到场景中然后在预测box邻域内做最近点迭代通常能把绕轴误差压到几度以内。这一步能明显提升抓取成功率但前提是物体CAD模型要准而且推理速度要能接受。5.5 本地复现分数与官方对不上问题出在评分脚本口径现象同一份预测结果自己跑一个分数官方评测系统返回另一个分数差距大到没法解释。原因评分脚本的处理口径不同。常见分叉点包括位姿平移量单位是米还是毫米、判断成功的阈值用的是10mm还是20mm、是否处理对称物体、预测结果是否经过时间平滑滤波。另一个常见问题是自己推理时做了后处理平滑官方脚本直接对原始输出做评估。解决拿到评分脚本第一件事是逐行读它的判定逻辑把阈值、单位、对称处理三个关键点确认清楚。然后用baseline的预测结果做一次“交叉验证”确保自己本地运行的分数和官方给出的baseline分数一致再接自己的模型。我见过有人花一周调模型结果问题出在单位换算上这种亏吃一次就够了。6. 位姿精度验证与上分技巧5cm/5°规则与失败样例归因6.1 用5cm/5°规则做离线验证训练完不要只看训练loss我习惯写一个独立的位姿精度验证脚本用5cm/5°这个直观标准给每一类目标打分。代码不复杂关键是旋转误差的写法要处理数值边界import numpy as np def pose_accuracy(pred_R, pred_t, gt_R, gt_t, t_thr0.05, r_thr5.0): # 平移误差欧氏距离单位米 t_err np.linalg.norm(pred_t - gt_t) # 旋转误差通过旋转矩阵迹求夹角clip防数值越界 cos_val np.clip((np.trace(pred_R.T gt_R) - 1.0) / 2.0, -1.0, 1.0) r_err np.degrees(np.arccos(cos_val)) return t_err t_thr and r_err r_thr, t_err, r_errarccos输入必须clip到[-1, 1]否则float精度会导致少量样本算出nan统计时莫名少了几十个正确样本。用这个脚本统计每类物体的及格率同时按距离分桶很容易看出掉分集中在哪个距离段。我见过最典型的分布近距离0.4米内及格率0.90.7米开外掉到0.5这种问题用分桶统计一眼就能定位到深度噪声随距离恶化而不是模型能力问题。6.2 给失败样本归因而不是堆epoch最后一招也是最容易被忽略的每次现场评测都录一份原始深度录像败了之后从头回放按“距离过远、目标遮挡、黑色物体、反光、对称歧义”给失败样本打标签。多数情况下你会发现最大掉分组不是网络容量不够而是近距离遮挡和深度边缘这两个桶。针对遮挡做数据增强比把epoch从60加到120有效得多。我现在每次跑这类赛项都会先做两件事跑通官方评分脚本、存一份现场录像。这个习惯救过我很多次——有一年就是反复看录像才发现某个失败集中出现在下午阳光方向的物体上而不是模型本身的问题。位姿验证用5cm/5°这把尺子卡自己比自我感觉良好有用得多。希望帮到你。本文还有配套的精品资源点击获取