本文面向当前仓库 /workspace/workflow/self/PVN3D,整理 LINEMOD 数据集在 PVN3D 中的训练流程、数据前提、目录要求、命令入口、检查项和常见问题。

本文不是论文解读,也不是对 README 的重复转述,重点是把“这套代码实际怎么训练”说清楚,尤其说明:

  • 当前已有的 BOP 测试数据为什么不能直接用于训练
  • 训练代码到底依赖哪些文件
  • 哪些数据是硬前提,哪些是增强项
  • 训练启动后会输出什么、保存什么、怎么判断是否正常

1. 当前环境与结论

当前环境:

  • 仓库路径:/workspace/workflow/self/PVN3D
  • 容器:pvn3d-dev
  • conda 环境:pvn3d
  • BOP 数据根目录:/workspace/bop

先给结论:

  1. 当前仓库里已经有一套 BOP -> PVN3D 的测试集转换流程,这条链路已经能跑 evaldemo
  2. 但当前转换结果只包含 test.txt 和测试图像,不具备直接训练条件
  3. 训练代码明确依赖 train.txt
  4. 训练阶段还会尝试读取 renders/{cls}/file_list.txtfuse/{cls}/file_list.txt
  5. 当前仓库原有的“测试转换结果”本身不够训练,但已经新增了 BOP train -> PVN3D train 转换脚本,可补齐真实训练样本和 renders

这点需要特别明确,避免后面同事误把当前测试数据当成训练集使用。

2. 训练入口在哪里

LineMOD 的训练入口只有一个:

pvn3d/train/train_linemod_pvn3d.py

仓库里提供的启动脚本是:

pvn3d/train_linemod.sh

它本质上就是执行:

python3 -m train.train_linemod_pvn3d --cls ${cls}

建议统一在以下目录执行训练命令:

cd /workspace/workflow/self/PVN3D/pvn3d

原因:

  • 代码里存在大量相对导入
  • 数据目录和日志目录都依赖当前工作目录

工作目录错了,最常见的问题是:

  • 模块导入失败
  • 找不到数据目录
  • 找不到 checkpoint

3. 训练代码的基本行为

训练脚本启动后会做以下几件事:

  1. 根据 --cls 构造 Config(dataset_name='linemod', cls_type=args.cls)
  2. 初始化 LM_Dataset('train', cls_type=args.cls) 作为训练集
  3. 初始化 LM_Dataset('val', cls_type=args.cls) 作为验证集
  4. 构建 PVN3D 网络
  5. 使用 Adam 优化器
  6. 使用 CyclicLR
  7. 使用 FocalLoss + OFLoss
  8. 训练过程中定期在验证集上评估,并保存 checkpoint

训练默认配置在 common.py 中,当前 LineMOD 相关核心参数如下:

  • n_total_epoch = 25
  • mini_batch_size = 24
  • val_mini_batch_size = 24
  • test_mini_batch_size = 1
  • n_sample_points = 12288
  • n_keypoints = 8
  • n_min_points = 400
  • num_mini_batch_per_epoch = 4000

注意:

  • 脚本虽然提供了 -epochs 参数,但当前训练主循环实际使用的是 config.n_total_epoch
  • 也就是说,默认情况下有效 epoch 数来自 Config

4. 训练数据到底需要什么

这是当前最重要的部分。

LM_Dataset('train', cls_type=args.cls) 在初始化时,会按下面逻辑找数据:

4.1 必需数据

每个类别目录下必须存在:

Linemod_preprocessed/data/{obj_id:02d}/train.txt
Linemod_preprocessed/data/{obj_id:02d}/rgb/*.png
Linemod_preprocessed/data/{obj_id:02d}/depth/*.png
Linemod_preprocessed/data/{obj_id:02d}/mask/*.png
Linemod_preprocessed/data/{obj_id:02d}/gt.yml

其中最关键的是:

  • train.txt

训练集初始化时会直接读取:

self.cls_root / "train.txt"

如果这个文件不存在,训练集构造会直接失败。

4.2 可选增强数据

训练代码还会尝试读取:

Linemod_preprocessed/renders/{cls}/file_list.txt
Linemod_preprocessed/fuse/{cls}/file_list.txt

这两类数据不是硬前提,但它们决定训练时能否混合真实图像和合成图像。

代码逻辑如下:

  • train.txt 中的真实样本是硬依赖
  • renders 不存在时,只是打印提示,不会立刻报错
  • fuse 不存在时,也只是打印提示,不会立刻报错

也就是说:

  • 没有 renders / fuse,理论上仍可训练
  • 但没有 train.txt,训练无法启动

4.3 当前仓库里的实际情况

当前 Linemod_preprocessed 目录中已经确认:

  • 所有类别都有 test.txt
  • 当前没有任何 train.txt
  • 当前没有 renders/
  • 当前没有 fuse/

因此,当前仓库中的 Linemod_preprocessed 只满足测试,不满足训练。

5. modelsrenders 在训练中的作用

这两个目录很容易被当成“辅助资源”,但它们在训练里承担的角色并不一样。

5.1 models/ 的作用

目录位置:

Linemod_preprocessed/models/
├── obj_01.ply
├── obj_02.ply
└── ...

这个目录里的 .ply 模型文件不是拿来直接喂网络的,而是用于生成监督和做评估基准。

在当前仓库里,models/obj_xx.ply 主要有三类用途:

  1. 从模型点云中采样物体表面点
  2. 作为 ADD / ADDS 评估时的几何基准
  3. 作为 pose 可视化投影时的 3D 模型来源

在代码中,Basic_Utils.get_pointxyz() 会从:

datasets/linemod/Linemod_preprocessed/models/obj_%02d.ply

读取点云顶点,并随机下采样到大约 2000 个点。

训练和评测时,这些模型点会被用于:

  • 构建物体 3D 参考点集
  • 计算 ADD / ADDS
  • 将预测位姿下的模型点重新投影到图像中

因此 models 目录虽然不直接参与 dataloader 采样,但它是训练和评测几何一致性的基础。

5.2 models/ 的文件要求

每个类别至少需要一个模型文件:

obj_01.ply
obj_02.ply
...
obj_15.ply

要求:

  • 编号要和 obj_id 一致
  • 单位要和现有 LineMOD 代码预期一致
  • 文件可被当前 ply_vtx() 读取

当前代码在读取后会执行:

pointxyz = self.ply_vtx(ptxyz_pth) / 1000.0

这意味着它假设 .ply 里的坐标单位是毫米,并在内部转成米。

如果模型单位不对,后果会直接体现在:

  • 关键点位置错误
  • 位姿监督尺度错误
  • ADD / ADDS 指标异常

5.3 renders/ 的作用

目录位置:

Linemod_preprocessed/renders/{cls}/
├── *.pkl
└── file_list.txt

renders 目录存放的是合成训练样本。对当前 LM_Dataset 来说,它不是普通图片目录,而是一个“样本缓存目录”。

训练模式下,数据集会尝试读取:

renders/{cls}/file_list.txt

里面每一行都是一个 .pkl 文件路径。训练时如果采样到 render 样本,就会直接把这行路径对应的 .pkl 读进来。

代码中这部分逻辑体现在:

  • self.rnd_lst = self.bs_utils.read_lines(rnd_img_pth)
  • 后续 if "pkl" in item_name: 分支

也就是说,renders 的输入粒度不是“图像文件”,而是“已经打包好的单样本 pickle 文件”。

5.4 renders/*.pkl 里应该包含什么

当前 LM_Dataset 读取 render / fuse 样本时,会期望 .pkl 至少有这些字段:

  • rgb
  • depth
  • mask
  • K
  • RT
  • rnd_typ

字段含义:

rgb

  • H x W x 3
  • uint8
  • 彩色图像

depth

  • H x W
  • 浮点或可转换为浮点
  • 深度图

mask

  • H x W
  • 二值或类别 mask

K

  • 3 x 3
  • 相机内参矩阵

RT

  • 3 x 4
  • 该实例的 GT 位姿

rnd_typ

  • 字符串
  • 当前代码里常见值包括 renderfuse

当前新增的训练转换脚本生成的 PBR 样本,实际结构已经对齐为:

  • rgb
  • depth
  • mask
  • K
  • RT
  • rnd_typ
  • obj_id
  • source_scene_id
  • source_frame_id
  • source_ann_id

其中训练代码直接依赖的是前六项,后四项主要是为了排查和追溯来源。

5.5 renders 在训练中的具体收益

没有 renders,训练仍然可以跑,但本质上会退化成纯真实样本训练。

renders 之后,训练阶段会在真实样本和合成样本之间做混合抽样,这带来的好处通常包括:

  • 增加姿态分布的覆盖范围
  • 增加背景和遮挡变化
  • 缓解真实样本数量不足的问题

对当前 LineMOD 代码来说,renders 的价值不在于“锦上添花”,而在于它是设计上就预留好的合成数据入口。

5.6 为什么 renders 不等于 fuse

两者在当前代码里都是可选项,但用途不完全一样。

renders

  • 主要是单物体渲染样本
  • 通过 .pkl 直接读取

fuse

  • 一般是融合场景数据
  • 也是通过 .pkl 读取
  • 训练时会单独作为第三类样本来源

当前新增转换脚本只补了 renders,没有补 fuse。这意味着:

  • 当前已经能形成“真实样本 + PBR render”两路训练输入
  • 但还没有恢复原始项目里 fuse 这一支增强数据

6. 为什么原有 BOP 测试转换结果不能直接训练

我们之前已经把 BOP 的 lm_test_all/test 转成了 PVN3D 需要的测试布局,但那个脚本的职责非常有限。

当前转换脚本:

pvn3d/datasets/convert_bop_lm_to_pvn3d.py

只会做下面这些事:

  1. 拷贝测试图像
  2. 拷贝测试深度图
  3. 拷贝测试 mask
  4. 生成 gt.yml
  5. 生成 test.txt
  6. 拷贝模型文件到 models/obj_xx.ply

它不会做的事:

  • 不会生成 train.txt
  • 不会从训练集生成真实训练帧索引
  • 不会生成 renders
  • 不会生成 fuse
  • 不会自动构造 DenseFusion 风格的预处理训练集

所以原有那条“只处理测试集”的转换流程,天然只能用于:

  • eval
  • demo

不能直接用于:

  • train

这里要和当前代码状态区分开:

  • 旧的测试转换脚本不能直接支持训练
  • 当前仓库已经新增了训练转换脚本,可以把 lm_trainlm_train_pbr 接到 PVN3D 的训练目录结构

新增脚本路径:

pvn3d/datasets/convert_bop_lm_train_to_pvn3d.py

7. 训练数据的两条可行路线

对当前仓库来说,训练 LineMOD 有两条实际可走的路线。

路线 A:使用现成的预处理版 Linemod_preprocessed

这是仓库 README 推荐的主路线,也是最省事的方式。

做法是:

  1. 下载已经预处理好的 Linemod_preprocessed
  2. 放到:
pvn3d/datasets/linemod/Linemod_preprocessed

这个版本通常已经包含:

  • train.txt
  • test.txt
  • 真实图像
  • 模型文件
  • 以及项目预期的其它辅助文件

如果你采用这条路线,训练的主要成本在环境和算力,不在数据整理。

路线 B:使用仓库内训练转换脚本补齐结构

如果不使用现成预处理数据,而是希望继续基于当前 /workspace/bop 的 BOP 数据训练,现在已经可以直接使用仓库新增脚本完成最小训练桥接。

脚本能力如下:

  1. lm_train(.zip) 生成每个类别的真实训练样本
  2. data/{obj_id} 下生成 train.txt
  3. 将真实训练帧写入 rgb/depth/mask
  4. 将真实训练位姿合并写入 gt.yml
  5. lm_train_pbr(.zip) 生成 renders/{cls}/*.pkl
  6. 生成 renders/{cls}/file_list.txt

当前脚本仍然不做的事:

  • 不生成 fuse
  • 不替代完整的 DenseFusion 预处理流程
  • 不保证和历史预处理版数据完全等价

推荐命令:

cd /workspace/workflow/self/PVN3D
python pvn3d/datasets/convert_bop_lm_train_to_pvn3d.py \
  --bop-root /workspace/bop \
  --output-root /workspace/workflow/self/PVN3D/pvn3d/datasets/linemod/Linemod_preprocessed \
  --overwrite

如果只想先做最小验证,可以拆成两步:

python pvn3d/datasets/convert_bop_lm_train_to_pvn3d.py \
  --bop-root /workspace/bop \
  --output-root /workspace/workflow/self/PVN3D/pvn3d/datasets/linemod/Linemod_preprocessed \
  --real-only --scene-ids 1 --overwrite
python pvn3d/datasets/convert_bop_lm_train_to_pvn3d.py \
  --bop-root /workspace/bop \
  --output-root /workspace/workflow/self/PVN3D/pvn3d/datasets/linemod/Linemod_preprocessed \
  --pbr-only --pbr-scenes 0 --overwrite

对当前仓库维护成本来说,这条路线已经比“手工补目录”可控很多,但仍然比直接使用现成预处理版数据更重。

8. train.txt 的作用

如果后续有人要自己补训练集,train.txt 的作用必须先理解清楚。

训练模式下,数据集会读取:

data/{obj_id}/train.txt

这个文件本质上是一个“样本索引列表”,每一行对应一个训练样本编号。后续代码会基于这一行的编号去找:

  • depth/{id}.png
  • mask/{id}.png
  • rgb/{id}.png
  • gt.yml 中同编号的位姿标注

也就是说,train.txt 不只是一个“清单”,它决定了训练集到底有哪些帧会被喂给网络。

9. gt.yml 在训练中的作用

gt.yml 不只是测试时有用,训练时同样要依赖它。

对于每一个训练样本,代码会从 gt.yml 中读取:

  • cam_R_m2c
  • cam_t_m2c
  • obj_id

然后构造位姿矩阵 RT,再进一步生成:

  • 中心点偏移监督
  • 关键点偏移监督

训练监督中最重要的两个量:

  • kp_targ_ofst
  • ctr_targ_ofst

都是基于 GT 位姿和模型关键点投影生成的。

这意味着,如果 gt.yml 错了,训练不是“效果差一点”,而是监督本身就错了。

10. 训练时的数据组成

训练集并不是简单遍历 train.txt,而是按真实样本和合成样本混合抽样。

逻辑在 LM_Dataset.real_syn_gen() 中:

  • 默认真实图像采样比例是 real_ratio=0.3
  • 如果没有合成数据,真实比例会自动提升到 1.0

也就是说:

  • train.txt,没有 renders / fuse,仍然可以训练
  • 只是训练会退化为纯真实样本训练

而当 rendersfuse 都存在时,训练数据会由三部分组成:

  • train.txt 对应的真实数据
  • renders/{cls} 的渲染数据
  • fuse/{cls} 的融合数据

11. 训练命令

10.1 最基本的启动命令

cd /workspace/workflow/self/PVN3D/pvn3d
python -m train.train_linemod_pvn3d --cls ape

10.2 从已有 checkpoint 继续训练

cd /workspace/workflow/self/PVN3D/pvn3d
python -m train.train_linemod_pvn3d \
  --cls ape \
  -checkpoint train_log/linemod/checkpoints/ape/ape_pvn3d_best.pth.tar

10.3 使用脚本启动

cd /workspace/workflow/self/PVN3D/pvn3d
bash ../pvn3d/train_linemod.sh

但这个脚本本身只选了 cls_lst[0],也就是 ape。实际使用时,更建议直接显式写 --cls

12. 训练输出和保存位置

LineMOD 训练日志和权重默认保存到:

pvn3d/train_log/linemod/

其中:

11.1 checkpoint 目录

train_log/linemod/checkpoints/{cls}/

例如 ape

train_log/linemod/checkpoints/ape/

训练过程中会保存:

  • {cls}_pvn3d.pth.tar
  • {cls}_pvn3d_best.pth.tar
  • 以及按验证 loss 命名的 best 副本

11.2 评测结果目录

train_log/linemod/eval_results/{cls}/

这个目录主要用于:

  • 训练中间评估
  • 后续 demo 可视化

13. 训练过程中的验证行为

训练脚本不是每个 epoch 都做验证,而是按 iteration 频率动态触发。

逻辑大致是:

  • 前期验证间隔较大
  • 随着训练推进,验证频率逐渐变高

每次验证时会:

  1. 在验证集上跑一轮
  2. 计算 loss_target
  3. 如果当前结果优于历史 best,则更新 best checkpoint

当前 LineMOD 验证集实际上也是通过:

LM_Dataset('val', cls_type=args.cls)

但在数据集代码里,非 train 模式最终都会走 test.txt 这条读取逻辑。所以这里的 val 本质上还是基于 test.txt 的同类读取方式。

14. 训练前检查清单

在真正启动训练前,建议逐项确认以下内容。

13.1 环境

  • 已进入容器 pvn3d-dev
  • 已激活 conda activate pvn3d
  • python-pcl 已安装
  • PointNet++ 扩展已可用
  • CUDA 环境正常

13.2 数据

  • pvn3d/datasets/linemod/Linemod_preprocessed 存在
  • 目标类别目录 data/{obj_id} 存在
  • train.txt 存在
  • test.txt 存在
  • rgb/depth/mask/gt.yml 完整
  • models/obj_xx.ply 存在

13.3 可选增强数据

如果期望使用渲染和融合数据,再检查:

  • renders/{cls}/file_list.txt
  • fuse/{cls}/file_list.txt

13.4 类别对应关系

确认以下一致:

  • --cls
  • obj_id
  • data/{obj_id}
  • models/obj_{obj_id}.ply
  • gt.yml 中的 obj_id

15. 如何判断训练是否正常启动

训练刚开始时,先看三类信号。

14.1 数据集是否成功初始化

如果训练集初始化成功,通常会看到:

  • cls_id in lm_dataset.py ...
  • train_dataset_size: ...
  • val_dataset_size: ...

如果这里直接报错,优先检查:

  • train.txt
  • gt.yml
  • rgb/depth/mask

14.2 损失是否正常回传

训练步中会打印或记录:

  • loss
  • loss_rgbd_seg
  • loss_kp_of
  • loss_ctr_of
  • acc_rgbd

如果 loss 一开始就是 nan 或异常大,优先检查:

  • 数据是否损坏
  • 深度单位是否错
  • gt.yml 是否异常

14.3 checkpoint 是否开始写入

一旦触发验证并达到保存条件,应能在:

train_log/linemod/checkpoints/{cls}/

看到新的 pth.tar 文件。

16. 训练后如何做测试

训练完成后,用下面命令评估目标类别:

cd /workspace/workflow/self/PVN3D/pvn3d
cls='ape'
tst_mdl=train_log/linemod/checkpoints/${cls}/${cls}_pvn3d_best.pth.tar
python -m train.train_linemod_pvn3d -checkpoint $tst_mdl -eval_net --test --cls $cls

做可视化 demo:

cd /workspace/workflow/self/PVN3D/pvn3d
cls='ape'
tst_mdl=train_log/linemod/checkpoints/${cls}/${cls}_pvn3d_best.pth.tar
python -m demo -dataset linemod -checkpoint $tst_mdl -cls $cls

17. 当前仓库状态下的实际建议

结合当前仓库现状,建议按下面顺序推进,而不是直接开训。

阶段一:先保留现有测试链路

当前测试链路已经打通:

  • BOP 测试集已转换
  • ape 权重已存在
  • eval / demo 已可用

这部分建议保持稳定,不要和训练准备混在一起改。

阶段二:补训练数据

如果后续要训练,优先决定采用哪条数据路线:

  1. 下载现成的预处理版 Linemod_preprocessed
  2. 或者自己基于 BOP / 其它来源补齐 train.txt + renders + fuse

对成本和稳定性而言,优先推荐:

  • 直接使用现成预处理版 Linemod_preprocessed

阶段三:在单类上先跑通

训练也建议从 ape 开始:

  • 类别简单
  • 当前仓库已有现成 ape 权重可对照
  • 方便判断训练结果是否偏离已有基线

18. 常见问题

17.1 直接训练时报 train.txt 不存在

这是当前最可能出现的问题,也是当前仓库状态下的预期问题。

原因:

  • 现有 BOP 转换结果只生成了 test.txt
  • 没有生成训练所需的 train.txt

17.2 训练集初始化成功,但没有渲染数据

如果看到类似提示:

  • Train without rendered data from https://github.com/ethnhe/raster_triangle
  • Train without fuse data from https://github.com/ethnhe/raster_triangle

这不一定是错误,只说明:

  • 当前训练会退化为纯真实样本训练

17.3 训练启动后损失异常

优先检查:

  1. gt.yml 是否正确
  2. 深度图单位是否符合预期
  3. mask 是否和图像帧号对齐
  4. 模型编号是否和类别一致

19. 最小训练准备清单

如果后续有人要真正启动 LineMOD 训练,最少先补齐下面这些内容:

  1. pvn3d/datasets/linemod/Linemod_preprocessed/data/{obj_id}/train.txt
  2. pvn3d/datasets/linemod/Linemod_preprocessed/data/{obj_id}/rgb/*.png
  3. pvn3d/datasets/linemod/Linemod_preprocessed/data/{obj_id}/depth/*.png
  4. pvn3d/datasets/linemod/Linemod_preprocessed/data/{obj_id}/mask/*.png
  5. pvn3d/datasets/linemod/Linemod_preprocessed/data/{obj_id}/gt.yml
  6. pvn3d/datasets/linemod/Linemod_preprocessed/models/obj_{obj_id}.ply

建议进一步补齐:

  1. pvn3d/datasets/linemod/Linemod_preprocessed/renders/{cls}/file_list.txt
  2. pvn3d/datasets/linemod/Linemod_preprocessed/fuse/{cls}/file_list.txt

只有这些条件满足后,才建议正式启动训练。

20. 维护建议

后续如果团队要长期维护这条训练链路,建议保持以下约定:

  1. 训练和测试数据分开管理,不要在同一轮修改里同时改两者
  2. 任何人补 train.txt 时,都同步说明数据来源
  3. gt.yml 的单位和 obj_id 修改必须单独验证
  4. 单类先跑通,再批量扩到 13 个类别
  5. ape 作为训练和测试的第一回归对象

当前最稳妥的路线仍然是优先获取或恢复完整的 Linemod_preprocessed 训练数据;如果数据来源受限,才使用新增的 convert_bop_lm_train_to_pvn3d.py 作为桥接方案。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐