LINEMOD 训练流程与实施细节
本文面向当前仓库 /workspace/workflow/self/PVN3D,整理 LINEMOD 数据集在 PVN3D 中的训练流程、数据前提、目录要求、命令入口、检查项和常见问题。
本文不是论文解读,也不是对 README 的重复转述,重点是把“这套代码实际怎么训练”说清楚,尤其说明:
- 当前已有的 BOP 测试数据为什么不能直接用于训练
- 训练代码到底依赖哪些文件
- 哪些数据是硬前提,哪些是增强项
- 训练启动后会输出什么、保存什么、怎么判断是否正常
1. 当前环境与结论
当前环境:
- 仓库路径:
/workspace/workflow/self/PVN3D - 容器:
pvn3d-dev - conda 环境:
pvn3d - BOP 数据根目录:
/workspace/bop
先给结论:
- 当前仓库里已经有一套
BOP -> PVN3D的测试集转换流程,这条链路已经能跑eval和demo - 但当前转换结果只包含
test.txt和测试图像,不具备直接训练条件 - 训练代码明确依赖
train.txt - 训练阶段还会尝试读取
renders/{cls}/file_list.txt和fuse/{cls}/file_list.txt - 当前仓库原有的“测试转换结果”本身不够训练,但已经新增了
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. 训练代码的基本行为
训练脚本启动后会做以下几件事:
- 根据
--cls构造Config(dataset_name='linemod', cls_type=args.cls) - 初始化
LM_Dataset('train', cls_type=args.cls)作为训练集 - 初始化
LM_Dataset('val', cls_type=args.cls)作为验证集 - 构建
PVN3D网络 - 使用
Adam优化器 - 使用
CyclicLR - 使用
FocalLoss + OFLoss - 训练过程中定期在验证集上评估,并保存 checkpoint
训练默认配置在 common.py 中,当前 LineMOD 相关核心参数如下:
n_total_epoch = 25mini_batch_size = 24val_mini_batch_size = 24test_mini_batch_size = 1n_sample_points = 12288n_keypoints = 8n_min_points = 400num_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. models 和 renders 在训练中的作用
这两个目录很容易被当成“辅助资源”,但它们在训练里承担的角色并不一样。
5.1 models/ 的作用
目录位置:
Linemod_preprocessed/models/
├── obj_01.ply
├── obj_02.ply
└── ...
这个目录里的 .ply 模型文件不是拿来直接喂网络的,而是用于生成监督和做评估基准。
在当前仓库里,models/obj_xx.ply 主要有三类用途:
- 从模型点云中采样物体表面点
- 作为 ADD / ADDS 评估时的几何基准
- 作为 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 至少有这些字段:
rgbdepthmaskKRTrnd_typ
字段含义:
rgb
H x W x 3uint8- 彩色图像
depth
H x W- 浮点或可转换为浮点
- 深度图
mask
H x W- 二值或类别 mask
K
3 x 3- 相机内参矩阵
RT
3 x 4- 该实例的 GT 位姿
rnd_typ
- 字符串
- 当前代码里常见值包括
render和fuse
当前新增的训练转换脚本生成的 PBR 样本,实际结构已经对齐为:
rgbdepthmaskKRTrnd_typobj_idsource_scene_idsource_frame_idsource_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
只会做下面这些事:
- 拷贝测试图像
- 拷贝测试深度图
- 拷贝测试 mask
- 生成
gt.yml - 生成
test.txt - 拷贝模型文件到
models/obj_xx.ply
它不会做的事:
- 不会生成
train.txt - 不会从训练集生成真实训练帧索引
- 不会生成
renders - 不会生成
fuse - 不会自动构造 DenseFusion 风格的预处理训练集
所以原有那条“只处理测试集”的转换流程,天然只能用于:
evaldemo
不能直接用于:
train
这里要和当前代码状态区分开:
- 旧的测试转换脚本不能直接支持训练
- 当前仓库已经新增了训练转换脚本,可以把
lm_train和lm_train_pbr接到 PVN3D 的训练目录结构
新增脚本路径:
pvn3d/datasets/convert_bop_lm_train_to_pvn3d.py
7. 训练数据的两条可行路线
对当前仓库来说,训练 LineMOD 有两条实际可走的路线。
路线 A:使用现成的预处理版 Linemod_preprocessed
这是仓库 README 推荐的主路线,也是最省事的方式。
做法是:
- 下载已经预处理好的
Linemod_preprocessed - 放到:
pvn3d/datasets/linemod/Linemod_preprocessed
这个版本通常已经包含:
train.txttest.txt- 真实图像
- 模型文件
- 以及项目预期的其它辅助文件
如果你采用这条路线,训练的主要成本在环境和算力,不在数据整理。
路线 B:使用仓库内训练转换脚本补齐结构
如果不使用现成预处理数据,而是希望继续基于当前 /workspace/bop 的 BOP 数据训练,现在已经可以直接使用仓库新增脚本完成最小训练桥接。
脚本能力如下:
- 从
lm_train(.zip)生成每个类别的真实训练样本 - 在
data/{obj_id}下生成train.txt - 将真实训练帧写入
rgb/depth/mask - 将真实训练位姿合并写入
gt.yml - 从
lm_train_pbr(.zip)生成renders/{cls}/*.pkl - 生成
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}.pngmask/{id}.pngrgb/{id}.pnggt.yml中同编号的位姿标注
也就是说,train.txt 不只是一个“清单”,它决定了训练集到底有哪些帧会被喂给网络。
9. gt.yml 在训练中的作用
gt.yml 不只是测试时有用,训练时同样要依赖它。
对于每一个训练样本,代码会从 gt.yml 中读取:
cam_R_m2ccam_t_m2cobj_id
然后构造位姿矩阵 RT,再进一步生成:
- 中心点偏移监督
- 关键点偏移监督
训练监督中最重要的两个量:
kp_targ_ofstctr_targ_ofst
都是基于 GT 位姿和模型关键点投影生成的。
这意味着,如果 gt.yml 错了,训练不是“效果差一点”,而是监督本身就错了。
10. 训练时的数据组成
训练集并不是简单遍历 train.txt,而是按真实样本和合成样本混合抽样。
逻辑在 LM_Dataset.real_syn_gen() 中:
- 默认真实图像采样比例是
real_ratio=0.3 - 如果没有合成数据,真实比例会自动提升到
1.0
也就是说:
- 有
train.txt,没有renders/fuse,仍然可以训练 - 只是训练会退化为纯真实样本训练
而当 renders 和 fuse 都存在时,训练数据会由三部分组成:
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 频率动态触发。
逻辑大致是:
- 前期验证间隔较大
- 随着训练推进,验证频率逐渐变高
每次验证时会:
- 在验证集上跑一轮
- 计算
loss_target - 如果当前结果优于历史 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.txtfuse/{cls}/file_list.txt
13.4 类别对应关系
确认以下一致:
--clsobj_iddata/{obj_id}models/obj_{obj_id}.plygt.yml中的obj_id
15. 如何判断训练是否正常启动
训练刚开始时,先看三类信号。
14.1 数据集是否成功初始化
如果训练集初始化成功,通常会看到:
cls_id in lm_dataset.py ...train_dataset_size: ...val_dataset_size: ...
如果这里直接报错,优先检查:
train.txtgt.ymlrgb/depth/mask
14.2 损失是否正常回传
训练步中会打印或记录:
lossloss_rgbd_segloss_kp_ofloss_ctr_ofacc_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已可用
这部分建议保持稳定,不要和训练准备混在一起改。
阶段二:补训练数据
如果后续要训练,优先决定采用哪条数据路线:
- 下载现成的预处理版
Linemod_preprocessed - 或者自己基于 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_triangleTrain without fuse data from https://github.com/ethnhe/raster_triangle
这不一定是错误,只说明:
- 当前训练会退化为纯真实样本训练
17.3 训练启动后损失异常
优先检查:
gt.yml是否正确- 深度图单位是否符合预期
- mask 是否和图像帧号对齐
- 模型编号是否和类别一致
19. 最小训练准备清单
如果后续有人要真正启动 LineMOD 训练,最少先补齐下面这些内容:
pvn3d/datasets/linemod/Linemod_preprocessed/data/{obj_id}/train.txtpvn3d/datasets/linemod/Linemod_preprocessed/data/{obj_id}/rgb/*.pngpvn3d/datasets/linemod/Linemod_preprocessed/data/{obj_id}/depth/*.pngpvn3d/datasets/linemod/Linemod_preprocessed/data/{obj_id}/mask/*.pngpvn3d/datasets/linemod/Linemod_preprocessed/data/{obj_id}/gt.ymlpvn3d/datasets/linemod/Linemod_preprocessed/models/obj_{obj_id}.ply
建议进一步补齐:
pvn3d/datasets/linemod/Linemod_preprocessed/renders/{cls}/file_list.txtpvn3d/datasets/linemod/Linemod_preprocessed/fuse/{cls}/file_list.txt
只有这些条件满足后,才建议正式启动训练。
20. 维护建议
后续如果团队要长期维护这条训练链路,建议保持以下约定:
- 训练和测试数据分开管理,不要在同一轮修改里同时改两者
- 任何人补
train.txt时,都同步说明数据来源 gt.yml的单位和obj_id修改必须单独验证- 单类先跑通,再批量扩到 13 个类别
- 以
ape作为训练和测试的第一回归对象
当前最稳妥的路线仍然是优先获取或恢复完整的 Linemod_preprocessed 训练数据;如果数据来源受限,才使用新增的 convert_bop_lm_train_to_pvn3d.py 作为桥接方案。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)