引言
抓取任务的失败往往被归因于「视觉不准」,但真实原因分布很分散:手眼标定有 5 mm 偏差、深度相机在黑色反光表面测距失效、抓取位姿的接近方向与料箱壁干涉、夹爪闭合时把物体推走、力控阈值太高捏碎或太低滑脱。这些问题跨越标定、感知、规划、控制四个环节,任何一环不合格都会表现为「抓不住」。
机器人抓取与通用视觉任务的根本差异在于输出不是标签而是动作。检测框偏 10 个像素在分类任务里无关紧要,但在抓取里意味着末端偏 5 mm,可能就抓空了。因此抓取对几何精度的要求远高于对语义精度的要求,这也决定了抓取系统更依赖深度信息与标定精度,而非模型容量。
第三个特点是闭环性。抓取有天然的验证信号——物体是否被抬起、力传感器是否检测到接触、夹爪开度是否符合预期。这允许系统在失败时重试、调整位姿、换抓取点,而不是一次决策定生死。工程上把抓取当作「带反馈的短程控制问题」而非「一次性分类问题」,成功率会有显著提升。
本文聚焦机器人侧的特有环节:标定、点云处理、抓取表示与判据、抓取检测方法、以及料箱抓取的完整流水线。视觉模型本身的训练与部署(检测、分割、推理加速)在计算机视觉专题 已有系统覆盖,这里只讲与机器人相关的接口与约束,例如目标检测 的推理延迟如何进入抓取节拍。
目录
- 相机模型与内参标定
- 手眼标定:AX=XB 的工程解
- 深度相机的误差特性
- 点云滤波、分割与位姿估计
- 抓取位姿的表示与力闭合判据
- 夹爪与吸盘的选择与建模
- 抓取检测:几何法与学习方法
- 料箱抓取流水线实现
- 失败分析与成功率提升
1. 相机模型与内参标定
相机内参把像素坐标映射到归一化平面,是后续所有几何计算的基础。
针孔模型:
[u] [fx 0 cx] [X/Z]
[v] = [0 fy cy] [Y/Z]
[1] [0 0 1] [ 1 ]
畸变模型(径向 + 切向,OpenCV 的 5 参数):
x_dist = x(1 + k1 r² + k2 r⁴ + k3 r⁶) + 2p1 xy + p2(r² + 2x²)
y_dist = y(1 + k1 r² + k2 r⁴ + k3 r⁶) + p1(r² + 2y²) + 2p2 xy
r² = x² + y²
典型内参(1280×720 工业相机,6mm 镜头):
fx ≈ fy ≈ 1100 像素,cx ≈ 640,cy ≈ 360
k1 ≈ -0.28,k2 ≈ 0.11(鱼眼或广角时绝对值更大)
import cv2, numpy as np
# 张正友标定:20~30 张不同姿态的棋盘格
objp = np.zeros((6*9, 3), np.float32)
objp[:, :2] = np.mgrid[0:9, 0:6].T.reshape(-1, 2) * 0.025 # 格子 25mm
objpoints, imgpoints = [], []
for f in images:
gray = cv2.cvtColor(cv2.imread(f), cv2.COLOR_BGR2GRAY)
ok, corners = cv2.findChessboardCorners(gray, (9, 6), None)
if ok:
corners = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1),
(cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001))
objpoints.append(objp); imgpoints.append(corners)
ret, K, dist, rvecs, tvecs = cv2.calibrateCamera(
objpoints, imgpoints, gray.shape[::-1], None, None)
print(f"重投影误差: {ret:.3f} 像素") # 应 < 0.3,> 0.5 说明标定质量差
标定质量的关键在采集:棋盘格要覆盖画面的四角与中心、倾斜角度要多样(俯仰 ±30°)、每张要有明显不同的姿态。全部正对镜头的标定会得到看似很小的重投影误差,但畸变系数不可信。
2. 手眼标定:AX=XB 的工程解
手眼标定求相机与末端(或基座)之间的固定变换,分两种配置。
眼在手外(Eye-to-Hand):相机固定,看整个工作区
求 T_camera_base(相机到基座)
优点:视野大、标定一次长期有效
缺点:精度受距离与分辨率限制,遮挡问题
眼在手上(Eye-in-Hand):相机装在末端
求 T_camera_ee(相机到末端)
优点:近距离精度高、可绕开遮挡
缺点:每个位姿都要标定、线缆与重量
标定的数学形式是解 AX = XB,其中 A 是末端相邻位姿的相对变换(由机器人运动学给出),B 是相机观测到的标定板相对变换。
import cv2, numpy as np
def hand_eye_axxb(R_gripper2base, t_gripper2base,
R_target2cam, t_target2cam, method=cv2.CALIB_HAND_EYE_TSAI):
"""最少需要 3 组不同位姿,建议 15~25 组且旋转轴要多样"""
R, t = cv2.calibrateHandEye(
R_gripper2base, t_gripper2base,
R_target2cam, t_target2cam, method=method)
return R, t
# 采集要求:
# 1. 每个位姿都用机器人正解算出 T_base_ee(不要用示教器的读数)
# 2. 位姿要有多样的旋转轴,只有平移或绕单轴旋转会退化
# 3. 标定板在画面中的位置与角度要有变化
# 4. 至少 15 组,用 3 组做验证
标定精度验证方法:用标定结果把相机看到的标定板角点变换到机器人基座系,与机器人正解算出的实际角点位置对比。误差应小于 2 mm。若误差大,先检查时间同步(相机曝光时刻与机器人位姿读数是否对齐)——动态采集时这是首要误差源。
3. 深度相机的误差特性
深度误差直接转化为抓取位置误差,必须理解其分布规律。
结构光 / 双目 / ToF 的误差特性对比:
结构光(RealSense D435 的主动立体、D405 等)
误差 ∝ z²,D435 在 0.5m 处约 1~2mm,1m 处约 5~10mm
对黑色、反光、透明表面失效
双目(被动)
误差 ∝ z²,依赖纹理,弱纹理区域空洞
ToF(如 Azure Kinect、PMD)
误差相对与距离弱相关,但有多径干扰(multipath)
对反光与透明表面同样有问题
经验公式(主动立体,基线 b、焦距 f、视差精度 δd):
δz = z² · δd / (f · b)
视差精度 δd 约 0.1~0.2 像素
D435:f ≈ 1400 px(1280 宽下),b = 50 mm
z = 0.5 m → δz ≈ 0.5² × 0.15 / (1400 × 0.05) = 0.54 mm
z = 1.5 m → δz ≈ 1.5² × 0.15 / (1400 × 0.05) = 4.8 mm
工程对策:把相机装近。距离减半,深度误差降到四分之一,这是投入产出比最高的优化。其次是选对传感器:金属反光件用结构光往往失败,需要 ToF 或加偏振滤光片;黑色橡胶件需要更高的曝光或主动补光。
4. 点云滤波、分割与位姿估计
原始点云不能直接用于抓取检测,必须经过一系列预处理。
标准预处理流水线:
1. 直通滤波(PassThrough):裁掉工作区外的点,减少 80% 数据量
2. 体素下采样(VoxelGrid):1~3 mm 体素,降低密度与噪声
3. 统计离群点去除(StatisticalOutlierRemoval):去掉飞点
4. 平面分割(RANSAC):去掉桌面/料箱底面
5. 欧式聚类(Euclidean Cluster Extraction):分出每个物体
6. 位姿估计:PCA 求主轴,或配准到 CAD 模型(ICP)
#include <pcl/segmentation/sac_segmentation.h>
#include <pcl/segmentation/extract_clusters.h>
#include <pcl/filters/voxel_grid.h>
// 1. 下采样
pcl::VoxelGrid<pcl::PointXYZ> vg;
vg.setInputCloud(raw); vg.setLeafSize(0.002f, 0.002f, 0.002f);
auto cloud = std::make_shared<pcl::PointCloud<pcl::PointXYZ>>();
vg.filter(*cloud);
// 2. RANSAC 平面分割(去桌面)
pcl::SACSegmentation<pcl::PointXYZ> seg;
seg.setOptimizeCoefficients(true);
seg.setModelType(pcl::SACMODEL_PLANE);
seg.setMethodType(pcl::SAC_RANSAC);
seg.setDistanceThreshold(0.005); // 5mm 内视为平面内点
pcl::ModelCoefficients coeffs;
pcl::PointIndices inliers;
seg.setInputCloud(cloud);
seg.segment(inliers, coeffs);
// 3. 欧式聚类(剩余点分簇)
pcl::EuclideanClusterExtraction<pcl::PointXYZ> ec;
ec.setClusterTolerance(0.01); // 1cm 内视为同簇
ec.setMinClusterSize(100);
ec.setMaxClusterSize(25000);
参数取值的经验:体素大小取目标物体最小特征尺寸的 1/31/2;RANSAC 的距离阈值取深度噪声的 23 倍(1.5 m 处约 5 mm);聚类容差略大于体素大小以避免过分割。这些参数应随相机距离自适应,固定值在变距离场景下必然失效。
5. 抓取位姿的表示与力闭合判据
抓取位姿是一个 6 维刚体变换,定义在夹爪坐标系上。约定的正确性至关重要。
抓取位姿的约定(必须与运动学、夹爪 URDF 一致):
z 轴:接近方向(approach direction),指向物体
原点:两指中心,位于指腹接触面的中点
x 轴:两指开合方向
y 轴:与指腹平行(由右手系确定)
这个约定下:
预抓取位姿 = 抓取位姿沿 z 轴后退 d(d 取 5~10 cm)
夹爪开度 = 物体在 x 方向的宽度 + 2~5 mm 余量
抓取质量的经典判据是力闭合(Force Closure):接触力能否抵抗任意方向的外力旋量。
力闭合的判据(接触旋量在凸包内):
对每个接触点 i,接触法向 n_i 与切向 t_i 张成摩擦锥
摩擦锥半角 α = atan(μ),μ 为摩擦系数(橡胶-纸约 0.5,金属-金属约 0.2)
所有接触的摩擦锥的凸包若覆盖整个 R^6 空间,则力闭合
工程简化的实用判据:
1. 对跖(antipodal):两接触点连线与两法向夹角均小于 α
2. 抓取矩阵 G 的最小奇异值 > 阈值
3. 在重力方向施加扰动,检查是否仍能抵抗
import numpy as np
def is_force_closure(contacts, mu=0.4, num_edges=8):
"""contacts: [(p, n), ...],p 为接触点,n 为单位法向
用摩擦锥的多面体近似构造抓取旋量矩阵,检查是否张成 R^6"""
G = []
alpha = np.arctan(mu)
for p, n in contacts:
# 构造接触点的切向基
t1 = np.cross(n, [0, 0, 1.0])
if np.linalg.norm(t1) < 1e-6:
t1 = np.cross(n, [0, 1.0, 0])
t1 /= np.linalg.norm(t1)
t2 = np.cross(n, t1)
# 在摩擦锥内均匀取 num_edges 个方向
for k in range(num_edges):
phi = 2 * np.pi * k / num_edges
f = (np.cos(alpha) * n
+ np.sin(alpha) * (np.cos(phi) * t1 + np.sin(phi) * t2))
wrench = np.concatenate([f, np.cross(p, f)]) # 力 + 力矩
G.append(wrench)
G = np.array(G).T # 6 x (num_contacts*num_edges)
# 力闭合 <=> 存在正系数使 G·λ = 0 且 λ > 0,等价于原点在凸包内部
# 实用检查:G 的秩为 6 且存在内部解
return np.linalg.matrix_rank(G) == 6
实际系统中很少直接算力闭合,而是用「启发式评分」:接近方向与表面法向夹角小、抓取宽度接近夹爪量程中段、抓取点远离物体边缘、物体质心在抓取轴附近。这些启发式组合起来的成功率往往不低于严格的力闭合计算,且快几个数量级。
6. 夹爪与吸盘的选择与建模
末端执行器的选择直接决定了抓取策略,是系统设计阶段就要定的。
平行夹爪(Parallel Jaw):
优势:力闭合稳定、对物体形状不敏感、可测开度做反馈
劣势:需要物体两侧可接近、开度受量程限制
适用:规则几何体、料箱抓取、装配
典型:Robotiq 2F-85(开度 85mm,力 20~235N)、OnRobot RG2
吸盘(Vacuum):
优势:只需一个可接近面、节拍快、对薄片与纸箱极佳
劣势:依赖表面平整与气密、对多孔或粗糙面失效
适用:纸箱、玻璃、塑料片、码垛
典型:单吸盘节拍可达 1s 以内,多吸盘阵列抓大件
三指/多指手:
优势:可做精细操作(in-hand manipulation)、包络抓取
劣势:控制复杂、成本高、抓取规划难
适用:研究、精密装配、不规则物体
选型判据:
物体有两侧可接近 → 平行夹爪
物体只有一个面可接近 → 吸盘
物体表面有纹理/多孔 → 不能吸盘
需要被抓后调整姿态 → 多指手
夹爪的建模要包含开度与力的映射关系。多数电动夹爪的开度与力不是独立可控的:达到力上限后停止闭合,因此控制接口通常是「目标开度 + 目标力」。仿真中要建模这个耦合,否则 sim-to-real 会有偏差。
7. 抓取检测:几何法与学习方法
抓取检测的输出是抓取位姿候选及其评分,两条技术路线。
几何法(GPD、传统 antipodal 搜索):
1. 在点云上采样大量候选抓取(位置 + 接近方向)
2. 对每个候选,找对跖接触点对,检查摩擦锥条件
3. 用启发式评分排序
优点:可解释、无需训练数据、对新物体泛化好
缺点:对薄壁、可变形、复杂形状效果差
典型速度:GPD 在 GPU 上约 20~50 ms/帧
学习方法(6-DoF GraspNet、AnyGrasp、Dex-Net):
1. 用深度网络直接从点云预测抓取位姿与质量分
2. 训练数据来自仿真(Dex-Net 的做法)或真实标注
优点:对复杂形状与杂乱场景效果好
缺点:需要数据、泛化到新域有风险、可解释性差
典型速度:AnyGrasp 约 50~100 ms/帧
混合(工业界常用):
学习模型生成候选 + 几何法做验证与重排序
验证包括:碰撞检查、可达性检查、力闭合评分
抓取检测的接口设计要注意:模型输出的是相机坐标系下的位姿,必须经过手眼变换到机器人基座系,再做可达性与碰撞检查。这三步中任何一步失败都应丢弃该候选而非勉强执行。
8. 料箱抓取流水线实现
料箱抓取(bin picking)是抓取最典型的工业场景,也是最能暴露系统缺陷的场景。
完整流水线(单次抓取,目标节拍 3~6 秒):
1. 采集:触发相机,等待深度图(30~80 ms)
2. 预处理:点云滤波、裁剪、下采样(20~50 ms)
3. 分割:去料箱壁与底面,分出物体(30~100 ms)
4. 抓取检测:生成候选并评分(50~150 ms)
5. 排序与筛选:
剔除与料箱壁碰撞的候选
剔除不可达(逆解失败)的候选
剔除与已抓取历史冲突的候选
6. 运动规划:预抓取位姿 → 抓取位姿(200~500 ms)
7. 执行:接近(低速)→ 闭合夹爪 → 抬起(验证)
8. 验证:夹爪开度 + 力反馈 + 视觉确认
9. 放置:移动到放置点,张开夹爪
10. 失败处理:换下一个候选(最多重试 3 次),全失败则请求人工
关键约束:
抓取位姿的接近方向必须避开料箱壁(料箱深度/宽度比决定可用角度)
料箱越深,可用接近方向越少,抓取难度越大
经验:料箱深度 < 2 倍物体尺寸时成功率显著下降
节拍的瓶颈通常在运动规划与物理移动,而非感知。感知 200 ms 只占 5 秒节拍的 4%,因此优化感知的边际收益低;而减少一次重试(每次约 2 秒)能直接省下 40% 节拍。提升首次抓取成功率比加速感知更有效。
9. 失败分析与成功率提升
抓取系统的优化必须建立在失败分类统计上,而不是凭感觉调参。
失败分类(建议按此打点统计):
1. 感知失败:没检测到抓取候选(占比 10%~30%)
原因:物体反光/透明、遮挡严重、模型域差异
对策:改善照明、换传感器、补数据微调模型
2. 候选筛选失败:有候选但全被碰撞/可达性过滤(10%~20%)
原因:料箱太深、物体贴壁、机械臂工作空间不足
对策:调整相机位置、优化料箱摆放、放宽筛选阈值
3. 规划失败:有候选但规划不出轨迹(5%~15%)
原因:时间预算不足、场景过复杂、ACM 配置错误
对策:延长规划超时、优化碰撞检测、配置 ACM
4. 执行失败:规划成功但没抓住(20%~40%)
原因:标定偏差、抓取点偏移、夹爪推走物体、力不足
对策:提高标定精度、增加预抓取调整、调夹爪力
5. 验证失败:抓住了但抬起时掉落(5%~15%)
原因:力闭合不足、物体重心偏、加速度过大
对策:提高夹爪力、降低抬起加速度、改用包络抓取
成功率提升的优先级应按上表占比排序。工程上最有效的三项改进通常是:把手眼标定误差压到 1 mm 以内、在接近阶段加一次「视觉微调」(用近距离相机重新估计抓取位姿)、以及给夹爪加接触力反馈而不是纯靠位置控制。
权衡取舍
| 决策 | 选 A | 选 B |
|---|---|---|
| 相机安装 | 眼在手外:视野大、标定一次 | 眼在手上:精度高、绕遮挡 |
| 抓取检测 | 几何法:可解释、泛化好 | 学习法:复杂形状、杂乱场景 |
| 末端 | 平行夹爪:稳定、可反馈 | 吸盘:快、单面可接近 |
| 传感器 | 结构光:近距离精度高 | ToF:反光面更稳、多径干扰 |
| 抓取验证 | 仅开度:简单 | 开度 + 力 + 视觉:可靠 |
| 失败处理 | 重试同物体:简单 | 换物体:节拍更稳 |
核心原则:把抓取当作带反馈的闭环问题。任何一次抓取都有验证信号,利用它做重试与调整,比追求一次性 99% 成功率更现实。工业系统通常以「3 次内成功」为指标而非单次成功率。
常见坑清单
- 手眼标定采集时位姿旋转轴单一,标定结果退化——必须有多样的旋转轴与姿态。
- 标定时用示教器读数而非机器人正解,模型与实物不一致——一律用正解算出的
T_base_ee。 - 深度相机装在 1.5 m 外,深度误差 5 mm 导致抓空——距离减半,误差降到四分之一。
- 抓取位姿的 z 轴约定与夹爪 URDF 不一致,接近方向全错——统一约定并写进接口文档。
- 体素下采样过粗,小物体被滤掉——体素取最小特征尺寸的 1/3~1/2。
- RANSAC 平面阈值过大,把物体表面当桌面去掉——阈值取深度噪声的 2~3 倍。
- 抓取候选不做碰撞与可达性检查直接执行,机械臂撞料箱——执行前必须双重过滤。
- 夹爪力按位置控制设定,物体被推走——加接触检测或力控闭合。
- 抬起加速度过大,物体因惯性滑脱——抬起阶段降低加速度到 0.5 m/s² 以下。
- 只统计总成功率不做失败分类,优化方向靠猜——按感知/筛选/规划/执行/验证五类打点。
小结
机器人抓取的核心是几何精度与闭环验证。标定精度决定了理论下限,点云处理与抓取判据决定了候选质量,而验证与重试决定了实际表现。视觉模型的语义能力是必要的但不是决定性的——多数工业抓取场景里,几何法的表现不逊于学习法且更可控。
下一步建议阅读机械臂控制与抓取规划 ,看抓取位姿如何变成可执行的轨迹;机器人仿真 会讲清如何用仿真做大规模抓取回归测试;如果视觉部分需要更深的背景,计算机视觉专题的图像分割与推理加速两篇会补足模型侧的知识。
最后一句:先把手眼标定做到 1 mm,再谈模型优化。标定误差是所有抓取误差的乘性放大器,它不修好,其他环节的改进都会被淹没。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。