机器人项目实战(2):识别物体、提取点云,再到抓取姿态
上一篇把相机选型、深度信息和手眼标定的过程整理了一遍。最终用上 Gemini 2,也建立了相机坐标系与机械臂基坐标系之间的转换关系。
接下来,该让机器人找东西、准备抓东西了。
这次的目标比较具体:橙子、苹果、香蕉、柠檬四种水果,再加上锤子和螺丝刀。
看起来只需要识别出物体,再把机械臂移过去。但真正接起来,还要回答几个问题:物体上的哪个位置可以作为抓取参考?手应该朝哪个方向?需要张开多大?
这一篇就记录从物体识别到抓取姿态计算的过程,灵巧手的具体控制留到后面再讲。
1. 先把桌上的目标认出来
最开始,我考虑的是 YOLO11s。
实际拿桌面画面尝试时,需要的目标没有识别出来。我先尝试调低置信度阈值,看看是不是结果被过滤掉了,之后改用了 YOLOE-11L 的分割模型。
这里要区分一下:调低阈值,是把原本置信度较低的结果也留下来;它不会让模型凭空多认识一种类别。对于这个项目,除了关注有没有检测结果,还要让识别范围覆盖需要抓取的水果和工具。
换成 YOLOE 后,我加入了对应的类别提示。实际配置里不只有简单的类别名,也用了带外观描述的文本,例如:
scss
orange
red apple
banana
yellow lemon
hand hammer
screw driver
螺丝刀还加入了颜色描述,方便针对桌面上的实际物体调整。
这一步先解决"找谁"。后面的几何计算并不直接根据名称决定抓取位置,而是使用识别结果对应的分割区域和深度信息。
2. 有了检测框,还要把物体分出来
检测框能告诉我物体大概在哪里,但它圈出来的是一块矩形区域,不是物体本身。
比如香蕉是弯的,螺丝刀是细长的。它们的检测框里会包含不少桌面。如果直接把框内的深度全部拿去计算,桌面上的点也会混进去。
所以,这里继续使用分割掩膜。
掩膜可以理解为一张与画面对应的区域标记:哪些像素属于目标,哪些不属于。得到它之后,就能从深度图里筛选出目标区域的数据,而不是处理整个检测框。
不过,掩膜和深度图必须对应到同一套像素位置。否则,彩色图里选中了苹果,深度图里取到的却可能是旁边的桌面。项目里先对齐彩色与深度,再让掩膜匹配深度图的尺寸。
到这里,识别结果不仅有"这是苹果",还有"这些像素属于这个苹果"。
3. 把目标区域变成三维点云
有了目标区域,下一步是把它从图像里提取到三维空间。
当前的处理方式是,保留掩膜内部有效的深度值,过滤掉零值和非有限值,再通过相机内参还原三维坐标:
ini
X = (u - cx) × Z / fx
Y = (v - cy) × Z / fy
其中,u、v 是像素位置,Z 是该像素的深度,fx、fy、cx、cy 来自相机内参。
对掩膜中的有效像素逐个计算,就得到了一组相机坐标系下的三维点,也就是目标点云。
这里得到的不是物体的完整模型,而是当前拍摄角度下能看见的表面。背面和被遮挡的部分,不会因为生成了点云就自动补出来。
提取之后,我在处理流程中加入了 Open3D 的统计离群点过滤。它根据点与邻域的距离,筛掉明显偏离主体的点,减少这些点对后续位置计算的影响。
4. 抓取位置,没有直接用几何中心
得到点云以后,一个直观的办法是求平均值,用点云质心作为抓取位置。
但质心是算出来的位置,不一定落在实际观测到的物体表面上。对于弯曲或者有凹陷的物体,这个区别尤其值得注意。
当前实现分了两步:
- 计算过滤后点云的质心;
- 在点云中找到距离质心最近的实际点。
第二步找到的点,才作为后续的位置参考。对应的核心代码很短:
ini
centroid = points.mean(axis=0)
distances_sq = np.sum((points - centroid) ** 2, axis=1)
center = points[np.argmin(distances_sq)]
这样选出来的位置来自实际测量,而不是一个可能落在空处的平均坐标。
它仍然只是一个几何位置参考,不代表已经找到了最适合接触、最容易抓稳的位置。手指具体接触哪里,还要结合手的形状和抓取姿态继续处理。
5. 位置确定了,手还得有个方向
一个三维点只能告诉机械臂"往哪里去",不能告诉它"手怎么摆"。
当前项目先利用分割轮廓,计算物体的最小外接旋转矩形。与普通检测框不同,这个矩形可以随着物体方向旋转。
处理时取最大外轮廓,计算矩形的长边方向,用它估计物体在画面中的主要朝向。对于细长轮廓,这样就能得到一个比较直观的方向参考。
在构造抓取姿态时,物体长边用于确定朝向,手指开合方向则沿着与长边垂直的方向。这也是为什么不能只说"算出了物体角度":还要明确这个角度对应手的哪个轴。
接近正方形的轮廓则单独处理。它的长短边差异很小,微小的轮廓变化就可能导致方向改变,因此当前实现使用默认方向兜底,不强行从长边里找朝向。
这套方法针对的是当前桌面场景中的几何近似,并不是恢复了物体完整的三维姿态。尤其是锤子这类形状不均匀的工具,外轮廓方向也不等于已经选中了合适的握持部位。
张开宽度怎么估计?
宽度同样从旋转矩形出发,但取的是短边。
先获得短边两个端点的像素坐标,再使用代表深度和相机内参,将端点还原到相机坐标系中。两点之间的距离,作为物体宽度的近似值,并在此基础上增加开口余量。
这里使用的代表深度,是矩形中心附近、掩膜内部有效深度的中位数;如果附近没有有效数据,就回退到整个掩膜的有效深度中位数。
需要注意,宽度估计采用了同一个代表深度,而生成物体点云时,每个像素使用的是自己的深度。两者服务的目的不同:前者做简化的开口估计,后者保留观测到的表面形状。
6. 把姿态放回机器人坐标系里看
前面的位置和方向都从相机观测出发,机械臂执行时需要的却是基坐标系下的目标。
这时就用到了上一篇的标定结果。
对于眼在手上的安装方式,相机位姿会随着机械臂运动改变。因此转换时不仅要使用标定得到的固定关系,还要结合拍摄时的机械臂 TCP 位姿,得到当前相机到基坐标系的变换。
转换对象也不只是一个位置点。抓取方向需要一起变换,否则位置到了,手的朝向仍然可能不对。
在这一步,项目先构造一个简化的夹爪姿态,用它表达位置、开合方向和接近方向,再交给后续的灵巧手姿态转换。
这里的"夹爪"是一种中间表示,实际末端仍然是灵巧手。用这种表示,可以把视觉侧计算与具体手模型的处理分开。
随后通过点云和手模型预览,检查目标与手的空间关系:位置有没有明显偏移,方向是否符合预期,手模型是否摆到了目标附近。
预览检查的是几何关系。实际能否抓稳,还涉及关节映射、闭合动作、接近路径和现场调整,这些留到下一篇展开。
写在最后
这一阶段,从最开始用 YOLO11s 尝试识别,到调整置信度、换用 YOLOE,再往后接上了分割、深度和抓取几何计算。
模型回答的是"画面里有什么",掩膜回答的是"哪些像素属于它"。真正准备抓取,还需要利用深度确定空间位置,再给出方向和开口宽度。