ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

OpenCV手眼标定实战:从AX=XB原理到机械臂抓取代码实现

OpenCV手眼标定实战:从AX=XB原理到机械臂抓取代码实现 1. 手眼标定到底在解决什么问题机械臂抓取这件事听起来简单——看到目标伸过去夹住。但真正上手做过的人都知道从“相机看到目标”到“机械臂准确到达目标”中间隔着一道必须跨过去的坎坐标系转换。相机有相机的坐标系机械臂有机械臂的坐标系这两个坐标系之间的变换关系如果不确定相机告诉机械臂“目标在图像中心偏左30像素”机械臂根本不知道这30像素对应到自己的世界里是多少毫米、往哪个方向走。手眼标定要做的就是求出这两个坐标系之间的刚性变换矩阵。具体来说是求相机坐标系到机械臂末端坐标系eye-in-hand或者相机坐标系到机械臂基座坐标系eye-to-hand的变换关系。这个变换矩阵一旦求出来相机检测到的任何目标位置都可以通过这个矩阵转换到机械臂坐标系下进而驱动机械臂去抓取。OpenCV从4.1版本开始在calib3d模块中正式提供了calibrateHandEye函数把原本需要自己手写求解的AXXB问题封装成了一个API调用。但API调用只是最后一步真正的工作量在于数据采集、坐标变换链的构建、标定板位姿计算、以及标定结果的验证。这篇文章我会从原理讲到代码实现把我在实际项目中踩过的坑和总结的经验都摊开来说。这篇文章适合谁看如果你正在做机械臂视觉抓取项目或者准备入门手眼标定又或者你已经调用了calibrateHandEye但结果总是不对那这篇内容应该能帮你省下不少调试时间。我会用Python和OpenCV来演示但原理部分对C用户同样适用。2. 手眼标定的数学原理拆解2.1 AXXB到底是怎么来的手眼标定的核心方程是AXXB这个方程看起来简单但理解它的推导过程对后续调试至关重要。先定义几个坐标系机械臂基座坐标系记为B机械臂末端坐标系记为E相机坐标系记为C标定板坐标系记为T。对于eye-in-hand配置相机固定在机械臂末端我们要求的是相机坐标系到末端坐标系的变换矩阵X即X T_E←C。假设机械臂运动了两次第一次末端位姿为T_B←E1第二次为T_B←E2。相机在两次运动中分别拍摄标定板得到标定板在相机坐标系下的位姿T_C1←T和T_C2←T。由于标定板是固定不动的机械臂基座也是固定不动的我们可以建立以下关系第一次T_B←E1 · X · T_C1←T T_B←T标定板在基座坐标系下的位姿第二次T_B←E2 · X · T_C2←T T_B←T两式相等消去T_B←TT_B←E1 · X · T_C1←T T_B←E2 · X · T_C2←T整理后得到(T_B←E2)^(-1) · T_B←E1 · X X · T_C2←T · (T_C1←T)^(-1)令A (T_B←E2)^(-1) · T_B←E1B T_C2←T · (T_C1←T)^(-1)就得到了AXXB。这里的A是机械臂末端两次运动的相对变换B是相机两次观测到的标定板相对变换。X就是我们要解的相机到末端的变换矩阵。注意这个推导假设标定板固定不动机械臂基座固定不动。如果标定板在标定过程中移动了整个方程就不成立了。这是实际操中最容易犯的错误之一。2.2 旋转和平移为什么要分开解AXXB这个方程不能直接线性求解因为旋转矩阵有正交约束。OpenCV的calibrateHandEye内部采用了Tsai-Lenz方法或Park-Martin方法等经典算法这些算法的共同思路是先解旋转再解平移。为什么因为旋转部分可以转化为一个线性问题。将AXXB展开旋转部分R_A · R_X R_X · R_B平移部分R_A · t_X t_A R_X · t_B t_X旋转部分的方程只涉及旋转矩阵可以通过罗德里格斯变换将旋转矩阵转为旋转向量然后构建线性方程组求解。一旦R_X求出来了平移部分的方程就变成了关于t_X的线性方程直接最小二乘就能解。这个“先旋转后平移”的策略有一个重要含义旋转标定的精度直接决定平移标定的精度。如果旋转矩阵解偏了平移向量再怎么优化也救不回来。所以在实际标定中机械臂的旋转运动要充分激励不能只做平移运动。2.3 为什么至少需要两组运动从AXXB的推导可以看出每两组运动可以构建一个方程。但一个方程不足以唯一确定X因为旋转矩阵有3个自由度平移向量有3个自由度总共6个自由度。每组运动提供的约束数量取决于运动的形式。实际使用中OpenCV的calibrateHandEye要求至少提供2组运动即3个位姿但强烈建议采集15到20组以上的数据。原因很简单噪声。机械臂的运动学误差、标定板的角点检测误差、相机内参的标定误差都会传导到最终结果中。数据量越大最小二乘的解越稳定。我个人的经验是少于10组数据结果基本不可信15到20组是比较稳妥的范围超过30组后精度提升有限但采集时间成本明显增加。3. 标定前的准备工作3.1 硬件配置与坐标系定义在开始写代码之前先把硬件和坐标系理清楚。以eye-in-hand为例典型的硬件配置包括机械臂比如UR、Piper、Franka等相机RGB或RGB-D固定在机械臂末端标定板棋盘格或ArUco码计算机运行标定程序坐标系定义必须明确坐标系符号说明基座坐标系B机械臂底座中心末端坐标系E机械臂法兰盘中心相机坐标系C相机光心标定板坐标系T标定板左上角或中心提示不同品牌的机械臂对末端坐标系的定义可能不同有的在法兰盘中心有的在工具尖端。标定前一定要确认机械臂SDK返回的位姿是相对于哪个坐标系的。3.2 标定板的选择与制作标定板的选择直接影响角点检测精度。常见的有两种棋盘格标定板OpenCV的findChessboardCorners对棋盘格的支持最成熟角点检测精度高。缺点是只能提供角点信息不能提供方向信息所以标定板不能旋转到太偏的角度。ArUco标定板OpenCV的aruco模块可以检测ArUco码每个码有唯一的ID能提供方向信息。适合标定板需要大角度旋转的场景。但ArUco码的角点检测精度通常略低于棋盘格。我的建议是如果机械臂的运动范围不大用棋盘格就够了如果需要大角度旋转来激励旋转轴ArUco更合适。标定板的制作要注意打印精度要高贴在硬质平板上平板本身要平整。我见过有人直接把打印纸贴在纸板上结果纸板受潮变形标定结果一塌糊涂。3.3 相机内参标定不能省calibrateHandEye需要的是标定板在相机坐标系下的位姿这个位姿是通过solvePnP计算出来的而solvePnP需要相机内参和畸变系数。所以相机内参标定是手眼标定的前置步骤不能跳过。相机内参标定用calibrateCamera函数采集15到20张不同角度的棋盘格图像具体流程这里不展开但有几个要点棋盘格要覆盖图像的不同区域包括边缘棋盘格要有不同的倾斜角度畸变系数要标定准确特别是广角镜头注意如果相机内参不准solvePnP算出的标定板位姿就会有系统偏差这个偏差会直接传导到手眼标定结果中。我遇到过内参标定只用了5张图的情况结果手眼标定怎么调都不对重新标定内参后问题消失。4. 数据采集的实操要点4.1 机械臂位姿的采集与记录数据采集是整个标定过程中最耗时、也最容易出错的环节。每一组数据需要记录两个东西机械臂末端位姿和对应的标定板图像。机械臂末端位姿通常以(x, y, z, rx, ry, rz)的形式给出其中(x, y, z)是平移(rx, ry, rz)是旋转向量罗德里格斯角或欧拉角。不同品牌的机械臂可能使用不同的旋转表示需要统一转换成旋转矩阵。import numpy as np import cv2 def pose_to_matrix(x, y, z, rx, ry, rz): 将机械臂位姿转换为4x4变换矩阵 输入平移(x,y,z)和旋转向量(rx,ry,rz) 输出4x4齐次变换矩阵 R, _ cv2.Rodrigues(np.array([rx, ry, rz], dtypenp.float64)) T np.eye(4) T[:3, :3] R T[:3, 3] [x, y, z] return T如果机械臂返回的是欧拉角需要先转换成旋转矩阵。OpenCV没有直接的欧拉角转旋转矩阵函数但可以用scipy.spatial.transform.Rotation或者手动构建。提示记录位姿时建议同时记录时间戳和图像文件名方便后续核对。我习惯用CSV文件记录每行对应一组数据包含位姿、图像路径和备注。4.2 运动轨迹的设计原则机械臂的运动轨迹不是随便走几个位置就行的。为了让AXXB有稳定的解运动需要满足以下条件旋转轴要充分激励。如果机械臂只绕一个轴旋转旋转矩阵的约束就不足解会不稳定。理想情况下机械臂应该绕至少两个不同的轴旋转而且旋转角度要足够大建议每次旋转15度以上。平移运动要覆盖工作空间。平移运动帮助确定平移向量的尺度。如果机械臂只在很小的范围内移动平移向量的解会有很大的不确定性。标定板始终在相机视野内。这是最基本的约束但实际操中很容易忽略。机械臂运动后标定板可能跑出视野或者被机械臂自身遮挡。避免奇异位形。机械臂在奇异位形附近时运动学解算不稳定位姿读数可能跳变。采集数据时要避开这些区域。我通常采用的采集策略是以标定板为中心机械臂从不同方向、不同角度“看”标定板每次运动后确认标定板完整出现在图像中且角点检测成功。4.3 图像采集的注意事项图像采集看似简单但有几个细节直接影响标定精度光照要均匀。标定板上的光照不均匀会导致角点检测偏差。避免强光直射和阴影。对焦要清晰。模糊的图像会导致角点检测精度下降。如果相机是自动对焦建议锁定焦距。曝光要合适。过曝或欠曝都会影响角点检测。建议手动设置曝光保证标定板黑白分明。采集时机械臂要静止。机械臂运动过程中采集的图像会有运动模糊而且位姿读数也不准确。每次运动到位后等待机械臂完全停止再采集。注意我见过有人在机械臂运动过程中连续采集图像结果标定结果偏差很大。机械臂的位姿读数和图像采集必须严格对应同一时刻。5. calibrateHandEye的代码实现5.1 完整代码框架下面是一个完整的Python实现使用OpenCV的calibrateHandEye函数import numpy as np import cv2 import glob import yaml # 1. 加载相机内参 with open(camera_calibration.yaml, r) as f: calib_data yaml.safe_load(f) camera_matrix np.array(calib_data[camera_matrix]) dist_coeffs np.array(calib_data[dist_coeffs]) # 2. 标定板参数 pattern_size (11, 8) # 棋盘格内角点数 square_size 0.025 # 方格边长单位米 # 生成标定板坐标系下的角点坐标 objp np.zeros((pattern_size[0] * pattern_size[1], 3), np.float32) objp[:, :2] np.mgrid[0:pattern_size[0], 0:pattern_size[1]].T.reshape(-1, 2) objp * square_size # 3. 读取机械臂位姿数据 # 假设CSV格式image_path, x, y, z, rx, ry, rz data np.loadtxt(robot_poses.csv, delimiter,, dtypestr) image_paths data[:, 0] poses data[:, 1:].astype(np.float64) # 4. 计算每组数据的标定板位姿 R_target2cam [] t_target2cam [] R_gripper2base [] t_gripper2base [] for i, img_path in enumerate(image_paths): img cv2.imread(img_path) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 检测棋盘格角点 ret, corners cv2.findChessboardCorners(gray, pattern_size, None) if not ret: print(f警告{img_path} 角点检测失败跳过) continue # 亚像素精化 criteria (cv2.TERM_CRITERIA_EPS cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) corners_refined cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria) # solvePnP计算标定板到相机的变换 ret, rvec, tvec cv2.solvePnP(objp, corners_refined, camera_matrix, dist_coeffs) if not ret: print(f警告{img_path} solvePnP失败跳过) continue R, _ cv2.Rodrigues(rvec) R_target2cam.append(R) t_target2cam.append(tvec.flatten()) # 机械臂位姿转变换矩阵 x, y, z, rx, ry, rz poses[i] R_gb, _ cv2.Rodrigues(np.array([rx, ry, rz])) R_gripper2base.append(R_gb) t_gripper2base.append(np.array([x, y, z])) # 5. 手眼标定 R_cam2gripper, t_cam2gripper cv2.calibrateHandEye( R_gripper2base, t_gripper2base, R_target2cam, t_target2cam, methodcv2.CALIB_HAND_EYE_TSAI ) print(相机到末端的旋转矩阵) print(R_cam2gripper) print(相机到末端的平移向量) print(t_cam2gripper.flatten()) # 6. 构建完整的变换矩阵 T_cam2gripper np.eye(4) T_cam2gripper[:3, :3] R_cam2gripper T_cam2gripper[:3, 3] t_cam2gripper.flatten() print(相机到末端的变换矩阵) print(T_cam2gripper)5.2 参数选择与算法对比calibrateHandEye提供了四种算法算法常量特点Tsai-LenzCALIB_HAND_EYE_TSAI经典方法速度快对噪声敏感Park-MartinCALIB_HAND_EYE_PARK精度较高推荐使用HoraudCALIB_HAND_EYE_HORAUD对旋转噪声鲁棒AndreffCALIB_HAND_EYE_ANDREFF线性方法速度快DaniilidisCALIB_HAND_EYE_DANIILIDIS对偶四元数方法精度高我的实测经验是Park-Martin和Daniilidis的精度最好Tsai-Lenz在数据质量好时也够用。如果标定结果不理想可以换几种算法对比取重投影误差最小的。5.3 标定结果的验证方法标定完成后必须验证结果是否可靠。最直接的方法是重投影验证选取一组未参与标定的数据用标定得到的T_cam2gripper将标定板在相机坐标系下的位姿转换到机械臂基座坐标系下然后与机械臂实际位姿对比。如果误差在合理范围内通常小于2-3毫米说明标定结果可用。def validate_hand_eye(T_cam2gripper, R_gripper2base, t_gripper2base, R_target2cam, t_target2cam): 验证手眼标定结果 计算标定板在基座坐标系下的位姿检查一致性 errors [] for i in range(len(R_gripper2base)): # 标定板在基座坐标系下的位姿 T_gb np.eye(4) T_gb[:3, :3] R_gripper2base[i] T_gb[:3, 3] t_gripper2base[i] T_tc np.eye(4) T_tc[:3, :3] R_target2cam[i] T_tc[:3, 3] t_target2cam[i] T_bt T_gb T_cam2gripper T_tc errors.append(T_bt[:3, 3]) errors np.array(errors) mean_error np.mean(errors, axis0) std_error np.std(errors, axis0) print(f标定板位置均值{mean_error}) print(f标定板位置标准差{std_error}) return mean_error, std_error如果标准差很大说明标定结果不稳定需要检查数据质量或增加数据量。6. 常见问题与排查技巧实录6.1 标定结果偏差大的排查思路标定结果偏差大是最常见的问题排查要按以下顺序进行第一步检查相机内参。用标定板重新投影看重投影误差是否小于0.5像素。如果大于1像素内参标定有问题。第二步检查角点检测。把检测到的角点画在图像上肉眼确认是否准确。如果角点偏移检查光照和对焦。第三步检查机械臂位姿。确认位姿的旋转表示是否正确旋转向量还是欧拉角确认位姿对应的坐标系是否正确。第四步检查数据量。少于10组数据基本不可靠建议15组以上。第五步检查运动激励。如果机械臂只做了平移运动旋转矩阵解不稳定。需要增加旋转运动。6.2 角点检测失败的常见原因问题现象可能原因解决方法完全检测不到角点标定板不在视野内/光照太暗调整机械臂位置/增加光照角点数量不对标定板被遮挡/棋盘格参数错误检查遮挡/核对pattern_size角点位置偏移镜头畸变/对焦不准重新标定内参/手动对焦角点顺序错乱标定板旋转角度过大减小旋转角度/使用ArUco板6.3 机械臂位姿读取的坑不同品牌的机械臂位姿的表示方式不同。有的用旋转向量有的用欧拉角有的用四元数。而且欧拉角还有不同的旋转顺序XYZ、ZYX等。如果搞错了标定结果会完全错误。注意UR机械臂返回的是旋转向量但有些SDK返回的是轴角。Piper机械臂的位姿定义需要查阅具体文档。建议先用一个已知的简单运动验证位姿转换是否正确。6.4 标定结果的精度评估手眼标定的精度通常用重投影误差来评估。具体做法是用标定结果将标定板在相机坐标系下的位姿转换到基座坐标系然后与机械臂实际位姿对比。误差来源包括相机内参误差角点检测误差机械臂运动学误差标定算法误差实际项目中1-2毫米的误差是可以接受的如果要求更高精度需要更精密的标定板和更稳定的机械臂。7. 提高标定精度的实战经验7.1 数据采集的优化策略数据采集的质量直接决定标定精度。我总结了几条经验采集时机械臂要“慢走稳停”。快速运动会导致机械臂振动位姿读数不稳定。每次运动后等待1-2秒再采集。标定板要覆盖图像的不同区域。不要总是把标定板放在图像中心要让它出现在图像的四个角和边缘。这样能更好地约束相机的畸变和标定板的位姿。旋转角度要足够大。每次旋转至少15度最好能达到30度以上。旋转角度太小旋转矩阵的约束不足。避免共面运动。如果机械臂的所有运动都在同一个平面内标定结果会有很大的不确定性。要让机械臂在三维空间中充分运动。7.2 标定板位姿的优化solvePnP计算标定板位姿时可以使用SOLVEPNP_ITERATIVE或SOLVEPNP_IPPE等不同方法。对于平面标定板SOLVEPNP_IPPE通常更稳定。另外可以使用cv2.solvePnPRefineLM对标定板位姿进行非线性优化进一步提高精度。# 使用IPPE方法求解然后用LM优化 ret, rvec, tvec cv2.solvePnP(objp, corners_refined, camera_matrix, dist_coeffs, flagscv2.SOLVEPNP_IPPE) rvec, tvec cv2.solvePnPRefineLM(objp, corners_refined, camera_matrix, dist_coeffs, rvec, tvec)7.3 多次标定取平均如果对精度要求很高可以多次标定取平均。具体做法是采集多组数据分成几个子集分别标定然后对结果取平均。这样可以减小随机误差的影响。但要注意取平均之前要先剔除异常值。如果某次标定的结果与其他次差异很大说明那次标定的数据有问题应该剔除而不是平均。7.4 标定结果的在线验证标定完成后不要急着上生产线。先做在线验证让机械臂去抓取一个已知位置的目标看实际抓取位置与预期位置的偏差。如果偏差在允许范围内再正式使用。我通常会在标定后做这样一个测试在标定板上选一个角点用标定结果计算机械臂应该到达的位置然后让机械臂实际运动到那个位置测量偏差。这个偏差就是标定精度的直接体现。8. 从标定到抓取完整链路打通8.1 坐标系变换链的构建手眼标定只是整个视觉抓取链路中的一环。完整的变换链是目标在图像中的像素坐标 → 相机坐标系下的3D坐标 → 机械臂基座坐标系下的3D坐标 → 机械臂运动指令第一步需要相机内参和深度信息如果是RGB-D相机或者平面假设如果是RGB相机。第二步需要手眼标定矩阵。第三步需要机械臂逆运动学。def pixel_to_robot_base(u, v, depth, camera_matrix, T_cam2gripper, T_gripper2base): 将像素坐标转换为机械臂基座坐标系下的3D坐标 u, v: 像素坐标 depth: 深度值米 # 像素坐标转相机坐标系 fx camera_matrix[0, 0] fy camera_matrix[1, 1] cx camera_matrix[0, 2] cy camera_matrix[1, 2] x_cam (u - cx) * depth / fx y_cam (v - cy) * depth / fy z_cam depth # 相机坐标系转基座坐标系 P_cam np.array([x_cam, y_cam, z_cam, 1.0]) T_base2cam T_gripper2base T_cam2gripper P_base T_base2cam P_cam return P_base[:3]8.2 抓取位姿的计算得到目标在基座坐标系下的位置后还需要确定抓取姿态。对于简单的平行夹爪抓取姿态通常由目标物体的朝向决定。如果目标物体是任意朝向的还需要估计物体的6D位姿。这部分内容超出了手眼标定的范围但它是手眼标定的直接应用。手眼标定的精度直接影响抓取的成功率。8.3 实际项目中的调试经验在实际项目中手眼标定完成后通常还需要做一些微调。比如工具坐标系补偿如果夹爪的抓取点不在法兰盘中心需要在变换链中加入工具坐标系。抓取高度补偿对于平面上的物体抓取时夹爪需要下降到物体表面以下一定距离。视觉伺服如果标定精度不够可以在接近目标时使用视觉伺服进行微调。我做过的一个项目中手眼标定的重投影误差在1.5毫米左右但实际抓取时偏差达到了5毫米。后来发现是工具坐标系没有正确补偿。加入工具坐标系后抓取偏差降到了2毫米以内。提示手眼标定求的是相机到法兰盘的变换但实际抓取时夹爪的抓取点可能不在法兰盘中心。这个偏差必须补偿否则标定再准也没用。9. 不同配置下的标定差异9.1 eye-in-hand与eye-to-hand的区别前面讲的都是eye-in-hand配置相机固定在机械臂末端。另一种常见配置是eye-to-hand相机固定在外部不随机械臂运动。eye-to-hand的标定方程略有不同。此时要求的是相机坐标系到机械臂基座坐标系的变换。方程变为T_B←E1 · T_E←C · T_C1←T T_B←E2 · T_E←C · T_C2←T其中T_E←C是末端到相机的变换固定不变需要求解。整理后仍然是AXXB的形式但A和B的定义不同。OpenCV的calibrateHandEye函数同时支持这两种配置只需要正确设置输入参数即可。对于eye-to-handR_gripper2base和t_gripper2base应该替换为R_base2gripper和t_base2gripper。9.2 不同机械臂的适配要点不同品牌的机械臂位姿的表示方式和坐标系定义不同。以下是一些常见机械臂的适配要点机械臂位姿表示坐标系定义注意事项UR旋转向量基座在底部中心位姿是法兰盘中心Piper欧拉角基座在底部中心需要确认旋转顺序Franka四元数基座在底部中心需要转换为旋转矩阵Kinova欧拉角基座在底部中心注意角度单位注意有些机械臂的位姿是相对于工具坐标系的不是法兰盘。标定前一定要确认清楚。9.3 相机类型的影响RGB相机和RGB-D相机在手眼标定中的差异主要在于深度信息的获取。RGB相机需要通过标定板的已知尺寸来推算深度而RGB-D相机可以直接获取深度。但RGB-D相机的深度图通常有噪声而且深度范围有限。如果标定板距离相机太远深度值可能不准确。建议标定板距离相机在0.3到1.0米之间。另外RGB-D相机的深度图和彩色图可能没有对齐需要先做对齐处理。OpenCV提供了cv2.rgbd.registerDepth等函数但实际使用中通常用相机的SDK来做对齐。10. 标定流程的自动化与工程化10.1 自动化采集脚本的设计手动采集数据效率低且容易出错。可以写一个自动化脚本让机械臂自动走一系列预设位姿在每个位姿处自动采集图像并记录位姿。def auto_calibration(robot, camera, poses_list, save_dir): 自动化手眼标定数据采集 robot: 机械臂控制对象 camera: 相机对象 poses_list: 预设的机械臂位姿列表 save_dir: 数据保存目录 data_records [] for i, pose in enumerate(poses_list): # 移动机械臂 robot.move_to(pose) time.sleep(1.0) # 等待机械臂稳定 # 读取实际位姿 actual_pose robot.get_pose() # 采集图像 img camera.capture() img_path f{save_dir}/image_{i:03d}.png cv2.imwrite(img_path, img) # 记录数据 data_records.append([img_path] list(actual_pose)) # 保存CSV np.savetxt(f{save_dir}/robot_poses.csv, data_records, delimiter,, fmt%s) return data_records10.2 标定结果的持久化标定结果需要保存下来供后续抓取程序使用。推荐用YAML或JSON格式保存方便读取和修改。import yaml def save_hand_eye_calibration(T_cam2gripper, filepath): 保存手眼标定结果到YAML文件 data { rotation_matrix: T_cam2gripper[:3, :3].tolist(), translation_vector: T_cam2gripper[:3, 3].tolist(), transform_matrix: T_cam2gripper.tolist() } with open(filepath, w) as f: yaml.dump(data, f)10.3 标定质量的自动评估在自动化流程中可以加入自动评估环节。如果标定质量不达标自动提示重新采集数据。def evaluate_calibration(T_cam2gripper, R_gripper2base, t_gripper2base, R_target2cam, t_target2cam, threshold0.003): 评估标定质量返回是否通过 errors [] for i in range(len(R_gripper2base)): T_gb np.eye(4) T_gb[:3, :3] R_gripper2base[i] T_gb[:3, 3] t_gripper2base[i] T_tc np.eye(4) T_tc[:3, :3] R_target2cam[i] T_tc[:3, 3] t_target2cam[i] T_bt T_gb T_cam2gripper T_tc errors.append(T_bt[:3, 3]) errors np.array(errors) std np.std(errors, axis0) max_std np.max(std) if max_std threshold: print(f标定质量合格最大标准差{max_std*1000:.2f}mm) return True else: print(f标定质量不合格最大标准差{max_std*1000:.2f}mm) return False11. 我踩过的那些坑11.1 标定板移动了这是最隐蔽的坑。标定过程中标定板必须固定不动。但有时候标定板会被碰到或者固定不牢导致微小移动。这种移动在单张图像上看不出来但会导致标定结果完全错误。解决方法标定板用夹具固定标定前后拍照确认标定板位置没有变化。11.2 机械臂位姿读数延迟有些机械臂的位姿读数有延迟即get_pose()返回的是上一次运动后的位姿而不是当前位姿。如果运动后立即读取可能读到旧数据。解决方法运动后等待足够长时间1-2秒或者用机械臂的回调机制确认运动完成。11.3 相机内参不匹配如果标定手眼时使用的相机内参与实际相机不匹配比如换了镜头但没重新标定内参标定结果会有系统偏差。解决方法每次更换镜头或调整焦距后重新标定相机内参。11.4 标定板角点检测的亚像素精度findChessboardCorners返回的角点坐标是整数像素需要用cornerSubPix做亚像素精化。如果跳过这一步角点精度只有1像素左右标定结果会有明显偏差。解决方法始终使用cornerSubPix窗口大小设为(11,11)或(5,5)迭代次数30次以上。11.5 机械臂运动学误差工业机械臂的重复定位精度通常很高0.1毫米级别但绝对定位精度可能差很多几毫米。如果机械臂的绝对定位精度不高手眼标定的精度也会受限。解决方法如果机械臂绝对定位精度不够可以考虑用激光跟踪仪等外部测量设备辅助标定或者使用视觉伺服在抓取时进行微调。12. 标定之后的验证与迭代手眼标定不是一次性的工作。随着机械臂的使用机械结构可能会有微小变化标定结果会逐渐漂移。建议定期比如每周或每月重新标定或者在抓取精度下降时重新标定。验证标定结果是否仍然有效最简单的方法是让机械臂去抓取一个固定位置的目标看抓取成功率。如果成功率下降说明标定需要更新。另外如果机械臂的工作空间发生了变化比如更换了底座位置也需要重新标定。我在实际项目中的做法是把标定程序做成一个独立的工具每次需要标定时运行一遍10分钟内完成数据采集和标定。这样即使需要频繁标定也不会占用太多时间。最后分享一个小技巧标定完成后把标定结果和标定时的数据一起存档。如果以后出现问题可以回溯到标定时的状态对比分析是标定问题还是其他问题。这个习惯帮我省了很多排查时间。
返回列表