基于三维点云的机器人杆件目标识别与抓取(四)

8 篇文章 0 订阅
2 篇文章 1 订阅

@基于点云特征描述匹配的目标识别识别

基于点云特征描述匹配的目标识别方法主要分为离线注册与在线识别两个过程。离线注册过程,需要创建待识别目标的模型点云,可以通过CAD模型的点云化获得,也可以通过数据采集设备获得。对获得的模型点云进行特征描述并生成特征描述文件。在线识别阶段,需要对经点云数据分割后得到的模型点云数据子集进行特征描述,分别与离线注册生成的目标特征描述文件进行特征匹配,累计投票得到预测的中心点,并计算匹配相似度,提取匹配相似度最高的点云子集,作为场景中所要识别的目标,进而完成场景桁架杆件目标的识别。

目标杆件CAD模型的离线注册

通过创建目标的CAD模型,对CAD模型进行点云化处理,从而得到待识别目标的模型点云数据。如图3-8所示为目标杆件CAD模型离线注册的基本过程。根据待识别目标的外形轮廓,创建CAD三维模型,对模型文件进行格式转换与点云化操作,为了降低计算的复杂度,需要对点云数据进行数据压缩,对压缩后的点云数据进行特征描述与注册训练并生成点云数据特征描述文件。
在这里插入图片描述
目标杆件CAD模型的离线注册可分为以下过程:
(1)创建目标杆件CAD物理模型
通过SolidWorks、Pro/Engineer、AutoCAD等三维建模软件,根据目标的实际外型尺寸进行建模,进而创建目标杆件的CAD模型。
(2)物理模型点云化与数据压缩
将目标的物理模型以STL格式的文件,导入到三维几何处理系统Meshlab中。首先,需要使模型的坐标中心对齐,在Filters菜单栏下的Normals,Curvatures and Orientation工具栏下的Transform工具,对界面中的目标进行位姿调整;然后需要对模型进行点云化,在点云化过程中设置采样精度进行数据压缩,选择Filters菜单栏下Sampling工具栏,通过Poisson-disk-Sampling实现模型的点云化与数据压缩。
(3)特征描述离线注册
场景中待识别目标杆件的识别是基于隐式形状模型(Implicit Shape Model,ISM)算法实现的。该算法主要分为两部分,一为注册训练,二为在线识别。本段主要介绍注册训练部分的内容。通过以上两步可以得到目标杆件压缩后的点云数据。对点云数据进行点云法线估计以及快速点特征直方图描述,提取相应的特征构建字典索引。注册训练部分主要分为以下步骤:
(1)检测关键点,对于点分布稠密且数据量较大的点云数据,检测其关键点,通过关键点近似表示整体数据,简化数据特征注册与训练的过程;
(2)关键点FPFH,计算关键点的法线特征以及FPFH特征;
(3)通过K-means聚类算法对所有关键点构建(几何)词典索引;
(4)计算每一个聚类簇中的关键点到聚类中心关键点是方向向量;
(5)对于每一个聚类簇,根据其每个关键点与中心点的方向向量,由式(3-18)计算其统计权重;
在这里插入图片描述
式中:
——聚类对的投票统计权重;
——聚类的票数;
——聚类对的投票数;
——投给的所有聚类的得票数;
——类的集合;
——特征点数。
(6)计算每一个关键点学习权重,该权重与关键点到聚类中心的距离相关,如式(3-19):
在这里插入图片描述
式中:
——聚类对训练的形状的投票;
——聚类与训练形状的中心距离;
——所有投票的实例集合;
——比例因子;
——投票中心与实际中心的欧氏距离。
理想目标杆件模型的点云数据经过以上步骤注册训练处理后得到目标杆件的特征描述文件,接下来利用该特征描述文件进行实际场景中的目标在线识别。

场景中目标杆件在线识别

获取后的场景点云数据经点云数据预处理得到不同部件对应的点云子集。分别对点云子集进行特征描述并与训练的目标特征描述文件进行匹配,根据投票预测中心点,利用距离均值间的关系进行目标识别,如图3-9所示基于模型特征描述匹配的场景目标识别过程。
在这里插入图片描述
在线识别部分主要分为以下步骤:
(1)对聚类分割后的点云数据子集进行关键点检测;
(2)对关键点进行FPFH描述;
(3)对于每个关键点对应的FPFH,搜索训练模型中最近的词;
(4)对于每一个关键点,计算其类别投票权重;
(5)根据以上步骤投票预测聚类点云子集的中心点;
(6)利用预测的中心点计算聚类点云子集的距离均值,同时与模型点云的距离均值进行比较,提出匹配相似度,如式(3-20):
在这里插入图片描述
(7)提取匹配相似度最大值对应的聚类点云子集作为所要识别的目标,最终实现场景目标的识别。

目标杆件在线识别实验与结果分析

针对获取的场景点云数据实施基于模型特征描述匹配的目标识别方法。如图3-10所示,对于某一场景1下,输入的场景点云数据经数据分割处理后得到三个聚类点云子集,对聚类点云子集分别与模型点云进行特征描述与匹配,其匹配相似度分别为60.2%、93.1%、82.2%,提取最高匹配相似度对应的聚类点云作为所要识别的目标点云,即聚类点云1为桁架杆件目标点云,经验证结果正确,其中表示聚类点云1预测的中心点。但对于另一场景2下,如图3-11所示,输入的场景点云数据经处理后得到5个聚类点云子集,其匹配度相似度Sim分别为92.3%、53.8%、-25.6%、25.1%、76.2%,同样提取最高匹配相似度对应的聚类点云作为所要识别的目标点云,即聚类点云0为桁架杆件目标点云,经验证结果错误,两场景下的数据处理信息如表3-1所示。
在这里插入图片描述
综上可知,基于模型特征描述匹配的目标识别方法对于实际场景的目标识别效果一般。究其原因主要有两个,一为相机采集的数据精度相对较低,加之所识别的桁架杆件目标表面特征单一,在进行特征描述时,无法建立更多有效的特征匹配点对;二为相机采用Eye-in-hand的安装方式,相机下获取的场景是随机械臂末端手爪实时变化的,当前视角下设定的识别参数,并不有效满足另一视角。因此,需要采用更加有效稳定的识别方法,根据当前环境和任务进行场景目标识别。
在这里插入图片描述 在这里插入图片描述

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
首先,需要了解桁架的基本知识和数学模型。桁架是由许多杆件和节点组成的结构,杆件只能承受轴向力,不能承受弯矩。一般情况下,桁架是由三维的杆件和五个节点组成的单元桁架。 求解节点位移需要进行有限元分析。有限元分析是指将一个连续的物体或结构分成许多小的单元,将其离散化,用数学方法进行计算。下面是实现该算法的一些步骤: 1. 将桁架划分为小的单元,每个单元内的节点和杆件数量应一致。 2. 将每个单元的杆件的应力和应变计算出来,可以使用虚功原理或变分原理来求解。 3. 将每个单元的刚度矩阵组合成整个桁架的刚度矩阵。 4. 将桁架的载荷矢量组合成一个整体载荷矢量。 5. 求解未知位移矢量,可以使用矩阵求逆、高斯消元或迭代法等方法。 6. 计算每个节点的位移向量和应力向量。 下面是一个简单的 C++ 代码示例实现上述算法: ```cpp #include <iostream> #include <vector> using namespace std; struct Node { double x, y, z; double u, v, w; // Node displacement }; struct Element { int node1, node2, node3, node4, node5; double E, A, L; double stress; // Axial stress vector<vector<double>> Ke; // Element stiffness matrix }; // Calculate element stiffness matrix vector<vector<double>> calcKe(const Element& e) { double cosTheta = (e.node3 - e.node2) / e.L; double sinTheta = (e.node4 - e.node1) / e.L; double C = cosTheta; double S = sinTheta; vector<vector<double>> Ke = { {C*C, C*S, -C*C, -C*S, 0, 0}, {C*S, S*S, -C*S, -S*S, 0, 0}, {-C*C, -C*S, C*C, C*S, 0, 0}, {-C*S, -S*S, C*S, S*S, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0} }; double factor = e.E * e.A / e.L; for (int i = 0; i < 6; i++) { for (int j = 0; j < 6; j++) { Ke[i][j] *= factor; } } return Ke; } // Assemble global stiffness matrix void assembleK(vector<vector<double>>& K, const Element& e) { K[e.node1][e.node1] += e.Ke[0][0]; K[e.node1][e.node2] += e.Ke[0][1]; K[e.node1][e.node3] += e.Ke[0][2]; K[e.node1][e.node4] += e.Ke[0][3]; K[e.node1][e.node5] += e.Ke[0][4]; K[e.node2][e.node1] += e.Ke[1][0]; K[e.node2][e.node2] += e.Ke[1][1]; K[e.node2][e.node3] += e.Ke[1][2]; K[e.node2][e.node4] += e.Ke[1][3]; K[e.node2][e.node5] += e.Ke[1][4]; K[e.node3][e.node1] += e.Ke[2][0]; K[e.node3][e.node2] += e.Ke[2][1]; K[e.node3][e.node3] += e.Ke[2][2]; K[e.node3][e.node4] += e.Ke[2][3]; K[e.node3][e.node5] += e.Ke[2][4]; K[e.node4][e.node1] += e.Ke[3][0]; K[e.node4][e.node2] += e.Ke[3][1]; K[e.node4][e.node3] += e.Ke[3][2]; K[e.node4][e.node4] += e.Ke[3][3]; K[e.node4][e.node5] += e.Ke[3][4]; K[e.node5][e.node1] += e.Ke[4][0]; K[e.node5][e.node2] += e.Ke[4][1]; K[e.node5][e.node3] += e.Ke[4][2]; K[e.node5][e.node4] += e.Ke[4][3]; K[e.node5][e.node5] += e.Ke[4][4]; } // Solve for nodal displacements void solve(vector<Node>& nodes, const vector<Element>& elements, const vector<double>& loads) { int nNodes = nodes.size(); int nDofs = nNodes * 3; vector<vector<double>> K(nDofs, vector<double>(nDofs)); vector<double> F(nDofs); for (const auto& e : elements) { e.Ke = calcKe(e); assembleK(K, e); } for (int i = 0; i < nNodes; i++) { F[3*i] = loads[3*i]; F[3*i+1] = loads[3*i+1]; F[3*i+2] = loads[3*i+2]; } // Apply boundary conditions nodes[0].u = 0; nodes[0].v = 0; nodes[0].w = 0; F[0] = 0; F[1] = 0; F[2] = 0; // Solve for displacements vector<vector<double>> Kinv(nDofs, vector<double>(nDofs)); for (int i = 0; i < nDofs; i++) { Kinv[i][i] = 1.0 / K[i][i]; } vector<double> U(nDofs); for (int i = 0; i < nDofs; i++) { double sum = 0; for (int j = 0; j < nDofs; j++) { sum += Kinv[i][j] * F[j]; } U[i] = sum; } // Update node displacements for (int i = 0; i < nNodes; i++) { nodes[i].u = U[3*i]; nodes[i].v = U[3*i+1]; nodes[i].w = U[3*i+2]; } // Calculate element stresses for (auto& e : elements) { double deltaU = nodes[e.node3].u - nodes[e.node2].u; double deltaV = nodes[e.node3].v - nodes[e.node2].v; double deltaW = nodes[e.node3].w - nodes[e.node2].w; e.L = sqrt(deltaU*deltaU + deltaV*deltaV + deltaW*deltaW); e.stress = e.E * (deltaU/e.L); } } int main() { vector<Node> nodes = { {0, 0, 0}, {0, 0, 1}, {0, 1, 0}, {1, 0, 0}, {1, 1, 0}, {1, 0, 1}, {1, 1, 1} }; vector<Element> elements = { {0, 1, 2, 3, 4, 200e9, 1e-3}, {0, 1, 2, 5, 6, 200e9, 1e-3}, {0, 1, 3, 5, 4, 200e9, 1e-3}, {0, 2, 3, 6, 4, 200e9, 1e-3} }; vector<double> loads = {0, 0, 0, 0, 0, 0, -10}; solve(nodes, elements, loads); for (const auto& n : nodes) { cout << "Node " << (&n - &nodes[0]) << ": " << n.u << " " << n.v << " " << n.w << endl; } for (const auto& e : elements) { cout << "Element " << (&e - &elements[0]) << " stress: " << e.stress << endl; } return 0; } ``` 在上述代码中,节点和元素分别以 `Node` 和 `Element` 结构体的形式存储。`calcKe` 函数计算每个单元的刚度矩阵,`assembleK` 函数将每个单元的刚度矩阵组合成整个桁架的刚度矩阵。`solve` 函数计算节点的位移向量和应力向量,其中使用了高斯消元法求解未知位移矢量。最后将节点的位移和应力输出到控制台。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值