自定义数据集 Pose 生成与坐标系约定内部文档

张开发
2026/5/19 1:11:05 15 分钟阅读
自定义数据集 Pose 生成与坐标系约定内部文档
1. 目的本文用于统一团队内部对以下问题的理解和实施口径什么是物体坐标系什么是相机坐标系cam_R_m2c和cam_t_m2c到底表示什么自定义数据集中 pose 应该如何生成pose 应该如何存到 BOP / LINEMOD / PVN3D 使用的文件里本文不是训练脚本使用说明而是给数据采集、标注、转换、训练、评估几方统一坐标系和 pose 定义。2. 最核心的一句话在当前PVN3D/ BOP / LINEMOD 语境里pose 表示的是“物体模型坐标系中的点如何变换到当前相机坐标系。”也就是X_cam R * X_obj t其中X_obj是物体坐标系中的 3D 点R是3x3旋转矩阵t是3x1平移向量X_cam是这个点在相机坐标系中的位置这就是 BOP 里常写的cam_R_m2ccam_t_m2c其中m2c model to camera不是camera to model这个方向如果搞反后面所有投影、评估、可视化都会错。3. 三个坐标系必须先说死在团队内部至少要把下面三个坐标系区分清楚。3.1 物体坐标系 object / model frame也可以叫模型坐标系CAD 坐标系obj frame它的来源是物体.ply/ CAD 模型本身模型上的每个顶点、关键点、中心点初始都定义在这个坐标系里。比如corners.txtfarthest.txt.ply顶点本质上全都在物体坐标系里。3.2 相机坐标系 camera frame也可以叫cam frame当前帧相机坐标系这是每一帧 RGB-D 图像对应的相机局部坐标系。通常约定原点在相机光心Z轴朝前X轴向右Y轴向下或向上取决于具体约定和实现对当前仓库来说关键点不在于口头描述而在于RGB 投影深度反投影.ply变换必须都使用同一套约定。3.3 世界坐标系 world frame如果你的采集系统里有机械臂转台ViconArUco / AprilTag 参考板多相机外参系统那么往往还会有一个世界坐标系。但要强调PVN3D单帧训练与评估并不直接需要世界坐标系它最终只需要model - camera的变换世界坐标系的价值主要在于帮助采集时建立统一参考方便从多种传感器反推每帧R, t4. 变换关系到底是什么4.1 正向变换如果某个点在物体坐标系中的坐标是X_obj [x, y, z]^T则它在相机坐标系中的位置是X_cam R * X_obj t写成齐次矩阵形式[X_cam] [ R t ] [X_obj] [ 1 ] [ 0 1 ] [ 1 ]这里的T_cam_obj [R|t]就是当前帧的 6D pose。4.2 逆变换如果你手上拿到的是相机坐标系中的点想反推回物体坐标系则需要取逆X_obj R^T * (X_cam - t)也就是说R_inv R^Tt_inv -R^T * t这一步常见于可视化调试世界系到模型系的换算校验 pose 是否方向写反4.3 当前仓库是如何使用这个变换的在当前仓库中模型点变换的典型写法是demo.pyeval_icp.py核心形式都是pts_camnp.dot(pts_obj,pose[:,:3].T)pose[:,3]这和上面的公式本质一致只是点数组通常是按行存储因此写成右乘转置形式。5.cam_R_m2c和cam_t_m2c应该如何理解5.1 命名含义在 BOP / LINEMOD 常见标注中cam_R_m2ccam_t_m2c含义是R: model to camera 的旋转t: model to camera 的平移其中m modelc camera所以cam_R_m2c不是“相机的旋转”cam_t_m2c也不是“相机在世界系里的位置”它们描述的是“把模型上的点放到当前相机坐标系里时该怎么变换。”5.2 为什么很多人会弄反因为很多采集系统更自然得到的是相机相对于世界的位姿物体相对于世界的位姿或者相机相对于物体的位姿而训练与评估真正需要的是物体相对于当前相机的位姿这中间可能需要做一次或多次坐标系拼接与求逆。所以在团队协作里一定不能只写“pose 已有”而必须明确写pose 的方向pose 所在坐标系单位6. pose 与投影、深度、模型的关系pose 本身不是孤立存在的它必须同时和以下三者对齐相机内参深度图尺度物体模型尺度6.1 与相机内参的关系如果R, t是对的但相机内参K错了那么3D 点投到图像上的位置会错由深度反投影的点云也会错最终表现为可视化投影偏移训练监督不稳定测试评估异常6.2 与模型尺度的关系如果.ply的单位和cam_t_m2c单位不一致例如模型是米cam_t_m2c是毫米那么即使方向对了投影和评估也会严重失真。当前仓库的 LINEMOD 流程中模型读取时常假定.ply是毫米再除以1000.0转成米见basic_utils.py因此团队内部必须统一模型文件用什么单位cam_t_m2c用什么单位转换脚本在哪一步做单位换算6.3 与深度尺度的关系如果深度图单位是毫米而你当成米来用反投影得到的点云会整体缩放 1000 倍。这会直接影响点云输入偏移监督ADD / ADDS所以必须记录深度图原始单位depth_scale点云恢复时的换算公式7. 自定义数据集中 pose 的几种生成方法下面按团队工程实现来讲不只说“名字”而是说每种方法的输入、输出、优缺点和适用场景。7.1 方法一人工交互式标注这是最传统也最直接的方法。输入需要有目标物体 3D 模型.ply每帧 RGB 图像最好还要有深度图相机内参过程标注者在工具里手动调整模型旋转模型平移使其在图像中与真实物体对齐。如果工具支持深度或点云叠加通常会更准。手动调整时实际在调什么人工调整 6D pose本质上是在调 6 个自由度平移tx, ty, tz旋转roll, pitch, yaw在当前文档的统一定义下这些量最终都要落到X_cam R * X_obj t也就是cam_R_m2ccam_t_m2c这里尤其要强调交互界面里看到的欧拉角通常只是“编辑方式”最终落盘必须仍然是model - camera的R, t对手调人员来说可以按下面方式理解 6 个自由度tx主要影响投影在图像中的左右位置ty主要影响投影在图像中的上下位置tz主要影响模型投影大小和前后距离yaw常表现为物体左右转头pitch常表现为物体前后俯仰roll常表现为物体在图像平面内旋转但要注意不同软件和脚本使用的欧拉角顺序可能不同XYZ、ZYX、yaw-pitch-roll不能默认等价因此团队内部如果使用欧拉角作为中间表示必须明确旋转轴顺序角度单位是度还是弧度最终如何转换回旋转矩阵推荐的手调顺序不建议 6 个量同时乱调。实际操作中推荐固定为以下顺序先确认初始 pose、坐标系方向、单位没有根本性错误先调tz让模型投影大小接近真实物体再调tx, ty让模型整体位置落到目标上再调yaw, pitch, roll修正轮廓和局部结构朝向最后对 6 个自由度做小范围联调这样做的原因是tz决定整体尺度先调它最直观平移定下来后再看旋转更容易判断最后一轮联调可以修正“旋转改变后视觉重心变化”带来的小偏差如何判断“已经对齐好”手调不能只凭“差不多”建议至少检查下面四类信息。轮廓对齐模型外轮廓应和真实物体边界基本重合尖角、长边、圆弧边界优先看关键结构对齐孔洞缺口把手凹槽棱边这些局部结构往往比整体外轮廓更能暴露旋转误差。遮挡关系合理应该被遮挡的部分不能“穿出来”可见区域不能莫名缺失深度关系合理如果有深度图最好同时检查模型深度与实测深度是否贴合很多tz偏差在 RGB 上不明显但在深度上会非常明显推荐的交互能力一个实用的人工 pose 微调工具至少应支持加载.ply模型加载 RGB 图最好还能加载深度图输入相机内参输入初始R, t在 RGB 上实时投影模型调整tx ty tz roll pitch yaw导出cam_R_m2c和cam_t_m2c从内部实现角度建议至少提供两档步长粗调步长快速接近正确位姿细调步长最后精修例如平移粗调可按5 mm平移细调可按1 mm旋转粗调可按5 deg旋转细调可按1 deg如果后续需要提升效率可以再增加鼠标拖拽平移鼠标滚轮调tz深度叠加点云叠加轮廓高亮工具选型建议人工交互式标注常见有三类路线。通用 3D 软件典型工具有BlenderMeshLabCloudCompare适合查看模型做粗姿态摆放辅助检查点云和深度对齐但通常存在下面问题默认坐标系与工程代码未必一致导出的旋转和平移往往还要做额外转换不适合作为大批量逐帧精修的主工具其中Blender 更适合做姿态粗调和背景图对齐MeshLab / CloudCompare 更适合辅助检查点云、深度、几何贴合情况自研轻量交互工具最推荐的路线是Open3D OpenCV原因是可以直接围绕项目真实需要来做RGB 图深度图.ply模型相机内参R, t导出格式对于当前PVN3D项目这是最实用的方案尤其适合小规模高质量测试集已有初值、需要人工精修希望后续直接接训练和评估脚本面向 6D pose / 数据集制作的专用标注工具这类工具本质上也是把模型叠加到图像或点云上人工调整R, t但它们通常不是当前仓库内置能力需要单独选型或自研适配。团队内部的实际建议是小规模高质量测试集优先用Open3D OpenCV自写 pose 手调工具如果团队已经熟悉 3D 软件可以先用 Blender 做粗调不建议直接使用普通 2D 标注工具因为它们不适合 6D pose 微调人工手调时最容易犯的错误把model - camera写成了camera - model.ply单位与cam_t_m2c单位不一致相机内参填错导致越靠边缘偏差越大欧拉角顺序和旋转矩阵转换顺序不一致只看 RGB不看深度导致tz偏差长期未被发现因此在团队内部人工手调的验收标准不应只写“人工对齐完成”而建议至少记录是否检查了 RGB 投影轮廓是否检查了关键结构是否检查了深度或点云贴合最终导出的是否为cam_R_m2c、cam_t_m2c输出最终输出每帧Rt也就是cam_R_m2ccam_t_m2c优点不依赖外部定位系统对现有真实场景最容易落地适合少量高质量测试集缺点成本高容易有人工误差多帧、多对象时效率很低适用场景自采测试集规模不大需要高质量 benchmark当前没有机械臂 / 外部定位系统7.2 方法二基于 AprilTag / ArUco / 标定板 / 外部定位系统这是最常见的工程化真实数据方案。基本思路先建立一个外部参考系例如转台坐标系标定板坐标系机械臂基坐标系世界参考板坐标系然后分别求相机相对于参考系的位姿物体相对于参考系的位姿最后拼出T_cam_obj T_cam_world * T_world_obj或等价写法T_cam_obj T_cam_ref * T_ref_obj输入需要有相机标定结果参考板几何信息物体在参考系下的位置或姿态采集时每帧的参考观测输出输出仍然必须整理为cam_R_m2ccam_t_m2c优点精度通常比纯人工标注更高可以批量处理适合搭建稳定采集流水线缺点需要额外硬件或外部参考坐标系链路更复杂容易在求逆和矩阵乘法顺序上出错适用场景要批量构建训练集要长期重复采集团队能维护一套稳定标定流程7.3 方法三仿真 / Blender 自动生成这类方法本质上不是“真实标注”而是直接在仿真环境里知道物体和相机的位姿输入需要有目标物体 CAD /.ply虚拟相机参数场景布置渲染脚本过程在 Blender 或其他渲染器里放置物体放置相机自动输出 RGB / depth / mask同步导出精确R, t输出每帧输出RGBdepthmaskcam_R_m2ccam_t_m2c优点pose 精确标注成本低适合构造大量renders/缺点有 sim-to-real gap如果材质、光照、噪声不真实泛化会差适用场景构造 PBR 或渲染训练数据补充真实数据不足做数据增强8. 如果你的真实采集里有世界坐标系如何换成model - camera这部分是团队最容易出错的地方。8.1 常见已知量很多采集系统里容易直接拿到的是T_world_camT_world_obj或者T_cam_worldT_world_obj而训练要的是T_cam_obj8.2 常见换算方式如果你已知T_world_camT_world_obj则T_cam_obj inv(T_world_cam) * T_world_obj如果你已知T_cam_worldT_world_obj则T_cam_obj T_cam_world * T_world_obj如果你已知的是T_obj_cam也就是相机在物体坐标系下的位姿那么必须先取逆T_cam_obj inv(T_obj_cam)8.3 团队内部建议无论原始采集链路怎么设计最终落盘前统一转换成T_cam_objcam_R_m2ccam_t_m2c不要把多种方向的 pose 混在同一项目里。9. 如何验证一个 pose 是不是写对了这是比“怎么算”更重要的工程步骤。9.1 方法一投影验证最实用的方法是取物体模型点或包围盒角点用R, t把它们变换到相机坐标系再用相机内参投影到 RGB 图像上看投影轮廓是否与真实物体重合如果投影整体漂移常见原因是R, t方向写反内参错模型单位错cam_t_m2c单位错9.2 方法二mask 一致性验证如果有真实 mask可以用模型和 pose 渲染一个二值 mask和人工或自动生成的 mask 做重叠比较如果 IoU 很差说明pose 不准mask 不准或者两者之一的坐标系 / 单位不一致9.3 方法三深度一致性验证如果有深度图可以根据 pose 渲染模型深度与实测深度比较这一步尤其能发现平移尺度错误模型尺度错误9.4 方法四关键点验证如果你已经有corners.txtfarthest*.txt可以把这些关键点投影回图像看它们是否落在合理位置。10. 团队内部推荐的 pose 生产流程为了降低后续训练和评估阶段的排障成本建议团队按下面流程组织。10.1 第一步固定坐标系定义在任何采集和标注开始前先文档化模型坐标系定义相机坐标系定义深度单位模型单位pose 最终输出方向至少要明确写出最终统一输出T_cam_objcam_t_m2c用毫米还是米.ply用毫米还是米10.2 第二步统一原始数据存储原始采集建议至少保留raw_capture/ ├── rgb/ ├── depth/ ├── pose_raw/ ├── camera/ └── metadata/其中pose_raw/存的是原始来源例如机械臂输出标定板求解结果人工标注结果不要一开始就直接只保留转换后的gt.yml。10.3 第三步统一写一个 pose 标准化脚本建议团队内部维护一个脚本专门做单位转换坐标系方向统一世界系到相机系的变换输出cam_R_m2c和cam_t_m2c这个脚本应该是可重复执行的可追踪版本的不要手工散改10.4 第四步可视化验收后再导出正式标注不要只看数值。必须至少做一次模型投影到 RGB与实物对齐检查随机抽帧人工复核通过后再批量导出scene_gt.jsongt.yml10.5 第五步保留可追溯元信息建议每批数据额外存相机标定时间模型版本pose 生成方式单位说明转换脚本版本否则后面一旦发现某一批数据错了很难定位。11. pose 在文件中的推荐存储方式11.1 BOP 风格推荐保存到scene_gt.json scene_camera.json其中scene_gt.json记录每帧每个实例的cam_R_m2c、cam_t_m2cscene_camera.json记录每帧的cam_K、depth_scale11.2 LINEMOD / PVN3D 风格推荐保存到gt.yml train.txt test.txt其中gt.yml记录每帧的姿态和obj_idtrain.txt/test.txt记录参与训练或测试的帧号11.3 内部原始格式如果团队正在建设自己的采集流程建议同时保留一份更适合工程调试的内部格式例如pose_raw/000001.json每个 JSON 至少包括原始 pose 来源坐标系说明单位说明最终标准化后的R, t这样后续导出成 BOP / LINEMOD 时会更稳。12. 团队内最容易出错的地方12.1 把camera - model当成了model - camera这是第一大高频错误。症状通常是投影结果完全错位旋转看起来像对了但平移很奇怪12.2.ply单位和cam_t_m2c单位不一致症状通常是方向看起来对但投影大小严重不对ADD / ADDS 很差12.3 相机内参不匹配症状通常是中心位置差不多越靠图像边缘越偏12.4 参考系链路求逆顺序错在世界系、标定板系、相机系、物体系之间做矩阵拼接时最容易出错的就是先乘谁谁要取逆团队内部必须养成习惯每写一个公式都明确它的左右两端坐标系12.5 只保留最终gt.yml丢失原始 pose 来源这样一旦后面发现错误几乎无法追溯。13. 团队内部建议的统一口径为避免后续数据、训练、评估、可视化各说各话建议统一下面这些口径。pose 最终统一保存为model - camera团队内部统一使用T_cam_obj表示最终训练 / 评估所需姿态所有标注和转换脚本都必须显式写清单位原始采集结果与最终训练格式分开保存任何一批新数据都必须做模型投影验收14. 总结在自定义数据集中pose 不是“一个随手记下来的 3x4 矩阵”而是整个训练与评估链路里最核心的几何契约。对当前PVN3D项目来说必须始终坚持下面这条定义X_cam R * X_obj t也就是cam_R_m2ccam_t_m2c描述的是物体模型坐标系中的点如何变换到当前相机坐标系无论你的 pose 来源是人工交互式标注AprilTag / ArUco / 外部定位系统Blender / 仿真自动生成最终都必须转换到这一定义并经过单位统一坐标系统一投影验证文件格式导出只有这样后续的数据转换PVN3D 训练demoeval才能在同一套几何意义下正常工作。

更多文章