提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
6D位姿估计相关知识学习:
6D位姿估计学习
6-PACK是一种类别级的基于投票的方法,实际上是一种位姿追踪器。基本思想是通过距离加权投票出一个目标锚点(距离物体质心最近的锚点),再由目标锚点生成1组有顺序的关键点,通过连续帧之间关键点的坐标变化估计帧间的相对位姿Δp,则在已知初始位姿p0的情况下,t时刻的位姿可以表示为:pt=Δp(t)Δp(t-1)…Δp(1)p0(下一帧的预测姿势是由最后一次估计的帧间姿态变化推断出来的)
初始姿势是相对于标准帧的相机帧的平移R(33)和旋转T(31),同种类别的初始姿势一致。
其中,训练的模型只是得到每一帧的关键点、目标锚点的坐标和锚点置信分数,实际的位姿预测在eval.py中进行。
世界坐标系与相机坐标系:
Tc=Tm*RcmT+tcm,其中Tm为物体在世界坐标系的坐标,Tc为物体在相机坐标系的坐标。Rcm为由世界系到相机系的旋转,tcm为由世界系到相机系的平移。
相机坐标系与图片2D坐标系:
如下图所示,相机3D坐标为[x,y,z],像素平面坐标为[u,v,1]两者通过相机内参矩阵K变换
详见一文带你搞懂相机内参外参
使用在围绕物体的预测姿态生成的锚点网格(第IV-A节)上的注意机制。每个锚点用其周围RGB-D点的个体特征的距离加权和,表示其周围的体积。在预测当前物体位置周围设置三维锚点网格。每个锚都包含一个周围的体积特征表示。基于这个特征,模型学习关注最接近物体质心的锚点。
实现步骤:
首先根据物体bbox(bounding box)生成5 *5 *5 个锚点ai的anchor网络,再利用DenseFusion将颜色特征和几何特征结合,生成融合特征,即彩色点的1维embedding ϕ \phi ϕj:将DenseFusion[45]特征嵌入到锚点网格内的RGB-D图像的所有彩色3D点,并使用距离加权平均将它们汇聚到每个锚点中,从而生成锚点嵌入(embedding:低维特征表示).
假设网格有N个锚点ai,M个RGB-D图中的彩色点云的点xj。则对每个锚点,各个点云的点到他的距离为di=[di1,…,diM];设权值w=softemax(d),采用距离加权的平均池化得到嵌入锚点 ψ \psi ψi=Σjwj ϕ \phi ϕj
(其中 ϕ \phi ϕj为DenseFusion编码器生成的xj的一维嵌入)
以上过程已经得到锚点特征,再从 ψ \psi ψi中找最接近物体真实质心的点作为目标锚点。采用2层MLP作为注意力网络,网络输入为 ψ \psi ψi,输出为置信分数ci,损失函数为:
其中ogt为ground-truth的质心。取置信度最高的点作为锚点,关键点由锚点的偏移点生成(见step2)
设关键点有K个,[k1,…kK]
提出一种神经网络(卷积网络构成),输入为锚点的特征 ψ \psi ψi,输出一个K*3维有序的关键点列表,因为有序,所以相邻帧的关键点可以直接按照索引匹配。
由于多视图一致性损失只保证可见部分特征位置在帧间一致,不能保证对最终的目标–预测帧间位姿变化有效(有可能这些点位置一致)。故不适用。(多视图一致性:假设keypoints在每两个连续的帧中生成;训练目标是将当前视图中的关键点放置在与前一帧关键点对应的位置,并通过地面真值帧间运动进行变换)
实际使用的Loss是6种损失函数的加权和,权重由它们的相对大小和重要性决定。
多视图一致性损失Lmvc
其中,kit为视频t时刻帧种关键点的坐标,减号后的部分代表将t-1时刻关键点坐标通过真实的相对位姿变化推测出一个t时的坐标,多视图一致性损失即为推测出的关键点与实际关键点的平均距离。
平移损失Ltra
其中,kt-kt-1为两组关键点中心坐标的差,即根据关键点预测出来的两帧间的相对平移向量t,而为实际的相对平移,平移损失即为两帧间预测相对平移向量与实际平移向量的距离。
分离损失Lsep
迫使关键点之间保持一定的距离,以避免构型退化和改善姿态估计
轮廓损失Lsil
轮廓损失定义为8个Keypoints与他们最接近的bbox网格点的距离的平均值,该部分计算轮廓一致性的损失,迫使关键点更接近物体表面,以提高可解释性。
由于沿对称轴确定旋转角是个不可解问题,对于有旋转对称性的物体,找关键点时,定义一种旋转不变性的坐标变换将(x,y,z)坐标变换为(d,h, θ \theta θ),其中
变换后,损失函数中的旋转损失和多视图一致性损失改变:
该部分在eval.py中完成,需要说明eval.py并非对相邻帧求位姿变化,而是将每一帧与初始帧比对。当前帧和初始帧的关键点被传递给最小二乘优化[2],计算帧间的位姿变化
在NOCS-REAL275[46]数据集上运行,与多个baseline比较
部分文件意义如下:
model_pts:
其中".txt"文件为实例8个边框顶点的坐标(数据集自带,与data_preprocess生成的bbox文件有差异,需要变换)
".xyz"文件为实例点云坐标(世界坐标系下)
model_scales:
训练集中:最小拟合3D bbox的2个对角顶点坐标(归一化,使得他们相距1)
验证集:边框的8个顶点(不需要再生成bbox文件)
训练生成的models实际输出的并不是位姿RT,而是关键点坐标、锚点网络和置信分数。预测位姿RT的生成过程在eval.py中,通过初始帧&当前帧的关键点坐标和初始位姿来预测当前帧与初始帧之间的位姿变化,再得到当前帧的实际物体位姿。
【6PACK代码注解】train.py
【6PACK代码注解】数据集载入与预处理
【6PACK代码注解】网络结构
【6PACK代码注解】损失函数
【6PACK代码注解】测试过程数据处理
【6PACK代码注解】测试过程eval.py
【6PACK代码注解】评估指标benchmark.py
参照文章:手把手教你跑通6-PACK: Category-level 6D Pose Tracker with Anchor-Based Keypoints
可顺利调通(将train.py中参数改小处理)
1.在当前账户安装anaconda3
2.当前账户安装cuda9.0
由于服务器gcc编译版本太高,直接安装cuda不成功,故先将当前账户的gcc版本降低为6以下,这里用5.5.0,参照文章:非root权限修改当前用户linux gcc版本
(文章中contrib/download_prequisites有误,改为contrib/download_prerequisites)
成功降低gcc后,参照文章Linux服务器下给当前用户安装自己的CUDA、CUDA 还是自己的好用安装cuda9.0
3.将数据集上传至服务器大空间硬盘,并软链接至项目地址
4.为了能实时修改服务器内代码文件,将VScode链接到服务器,参考文章:
使用VScode连接远程服务器的配置方法
5.调整文件地址后,试跑成功
6.执行python train.py --category x --dataset_root '/home/robot413/My_NOCS' --workers 5
多次,将6个类别的模型训练出来,各类别最优模型如下:
7.执行原eval.py文件(不输出图片版,速度较快),先得到验证集的预测位姿文件。
8.执行修改后的benchmark.py文件,得到所有指标。
9.执行修改后的eval.py文件(TEST_2),输出标记边框和关键点的图片。
1.transfomations.py 中由于环境版本不同,自定义的import_module()函数报错,追踪发现其中调用的import_module 找不到对应文件,且是由于没有按要求忽略 " _ "造成(原因未知)
解决方法:调用时不加 " _ ",并引入sys,将文件路径添加至搜索路径:
2.关于upsample和size_average的warning
纯属因为版本不同造成的函数弃用和改动,但能向下兼容,不影响运行
3.train.py中在for i, data in enumerate(dataloader)
处经常卡住不动,或者有时成功运行但耗时很长
且后面遇到这样的警告:
起初怀疑前面卡住也是因为出现了除0的情况,由于只是warning不是error,先不管他,后面如果对效果影响比较大,考虑将该段代码中加入判别,当分母为0时跳过
解决除0问题后还是不行,有时候能成功运行几次,然后又卡住:
根据github中issue的相关回答,暂时去掉dataset_nocs中_item_函数的try-except后,显示出报错信息,发现问题在于简单粗暴的截取了一部分数据集做测试,但对应的数据没有做相应更改
该项目读取数据的过程要通过datalist中的list.txt获取,每个txt文件中的文件是随机分配的,很难解耦,要么直接用软链接接入所有train文件进行测试,要么更改datalist文件夹,重写list.txt,但这个重写的过程会很繁琐。
目前选择连接所有train文件,先生成真值数据再说(耗时会很长)
成功
4.重新保留dataset_nocs中_item_函数的try-except,修改train.py参数中的地址(outf),且在6test文件夹中新建一个models文件夹,可以成功运行,以此运行各类别得到models
4.eval.py—注意修改其中的model参数
#为读取gts文件中实例真实位姿做准备
obj_path = img_path + "_meta.txt"#图片中出现的实例列表
mask_path=img_path+"_mask.png"
mask_im=cv2.imread(mask_path)[:,:,2]#读取mask.png的各掩码id
mask_im=np.array(mask_im)
inst_ids=np.unique(mask_im)
ins_id = -1#实例在图片中的掩码id
num_idx = 0#目标实例在图片中的idx
with open(obj_path, "r") as obj_f:
for line in obj_f:
if int(line.split(" ")[1]) == cls_idx and line.split(" ")[-1].replace("\n","") == model_name:
ins_id = int(line.split(" ")[0])#实例在图片中的掩码idx
break
if int(line.split(" ")[0]) in inst_ids:
num_idx=num_idx+1
#num_idx = num_idx + 1
if ins_id == -1:
continue
使得在计数过程中判断*_meta.txt各行是否真的有效,修改代码后,如下图所示,问题解决。该部分代码注解详见【6PACK代码注解】评估指标benchmark.py