十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

手眼矩阵怎么用?从标定到机器人抓取的完整坐标转换指南

手眼矩阵怎么用?从标定到机器人抓取的完整坐标转换指南 很多朋友标定完手眼矩阵后第一反应是拿着那个4x4的矩阵发懵这玩意儿我存下来了然后呢怎么把相机里看到的像素坐标变成机器人能用的X、Y、Z怎么让机械臂真的去抓那个螺丝这篇文章不聊标定过程本身专门讲标定完成之后手眼矩阵到底该怎么用、数据流怎么走、代码怎么写、坑在哪里。我会把眼在手上和眼在手外两种情况分开讲直接给出坐标转换链、代码示例和排查思路让你标定完就能立刻接进项目里。1. 手眼标定的核心思路与本质解析1.1 手眼标定到底在解什么方程手眼标定这名字听起来玄乎其实就是求一个变换矩阵把相机看到的坐标转换到机器人能理解的坐标。要搞懂矩阵怎么用得先搞懂标定在解什么。常用的两种标定模型Eye-to-Hand眼在手外相机固定安装不动看着机械臂工作。求解的是相机坐标系到机器人基坐标系的变换方程形式是AX XB。Eye-in-Hand眼在手上相机装在机械臂末端跟着机械臂动。求解的是相机坐标系到机械臂末端坐标系的变换方程形式也是AX XB只是各矩阵含义不一样。不管是哪种等式里的A、B都是标定过程中记录的位姿变化X就是我们最终要的手眼矩阵。看着X在等式两边夹着本质上是说不管机械臂怎么动相机和机器人之间的相对位置关系不变所以这个X是固定的。1.2 手眼矩阵的物理意义手眼矩阵是一个4x4的齐次变换矩阵由旋转矩阵R3x3和平移向量t3x1组成X [ R t ] [ 0 1 ]它的物理意义非常直接Eye-to-HandX表示“相机坐标系 → 机器人基坐标系”的变换。相机拍到的某一点乘以这个矩阵就变成机器人基坐标系下的点机器人可以直接去。Eye-in-HandX表示“相机坐标系 → 机械臂末端坐标系”的变换。相机看到的点乘以X先变成机械臂末端坐标系下的点再结合机械臂当前的末端位姿才换算到机器人基坐标系。很多项目翻车就翻在这里坐标系方向搞反了。有人把相机到基座的矩阵拿去做基座到相机的变换换算出来的坐标歪到天边。后面我会讲怎么核对方向。1.3 为什么标定完了不会用我见过太多人卡在这一步。标定完拿到矩阵OpenCV或者Halcon输出个4x4看着挺完整但真要算坐标就不知道从哪里下手了。根本原因是手眼矩阵只是坐标转换链里的一环不是全部。实际项目里从像素到机器人坐标中间要串联好几个坐标系变换。标定只是把其中最关键的一环解出来了剩下的链路需要你自己去搭。举个例子相机识别到一个工件的像素坐标(u, v)要先通过相机内参和畸变系数反投影成相机坐标系下的三维点(x, y, z)这步叫“像素坐标转相机坐标”。然后这个三维点乘以手眼矩阵才变成机器人能用的坐标。很多新手把这步搞反以为手眼矩阵可以直接吃像素坐标那肯定算不对。搞清楚这个逻辑后面的内容就好理解了。手眼矩阵是坐标链里的“换乘站”你手里的像素坐标先上车换乘一次再到机器人坐标系下车。2. 手眼矩阵的核心用法坐标转换链路全解析2.1 先搞清楚自己的安装方式拿到手眼矩阵第一步不是急着写代码而是确认自己是哪种安装方式。判断方法很简单把机械臂末端随意动一下看相机动不动。相机跟着机械臂一起动就是Eye-in-Hand。相机纹丝不动就是Eye-to-Hand。这个决定了你后面的整个坐标链怎么搭。很多人标定的时候用的方法不对或者从别处复制了一套标定代码最后算出来的矩阵自己也搞不清是哪种项目一跑就露馅。安装方式手眼矩阵含义标定方程模型实际使用逻辑Eye-in-Hand相机→末端AXXB像素→相机→手眼矩阵→末端→基座Eye-to-Hand相机→基座AXXB像素→相机→手眼矩阵→基座2.2 完整的坐标转换链长什么样以最常见的二维相机抓取为例完整的数据流是这样像素坐标 (u, v)视觉识别的原始输出。相机坐标系 (x_c, y_c, z_c)通过相机内参和畸变系数反投影得到。这里需要注意如果用的是平面相机需要自己设定z_c的值比如工件高度或标定平面高度。机器人基座坐标系 (x_b, y_b, z_b)相机坐标乘以手眼矩阵得到。机器人执行把(x_b, y_b, z_b)发给机器人控制器机器人按这个坐标走。写成矩阵乘法的形式P_base X_handeye * P_camera其中P_camera是相机坐标系下的齐次坐标4x1X_handeye是手眼矩阵4x4P_base是机器人基座坐标系下的齐次坐标4x1就这么简单对就这么简单。手眼矩阵的“用”本质上就是一次矩阵乘法。但问题往往出在P_camera本身怎么算以及乘法顺序怎么写、坐标系是用列向量还是行向量等细节上。2.3 像素坐标转相机坐标的细节这是很多人踩坑的第二道坎。手眼矩阵吃的是“相机坐标”不是“像素坐标”。像素坐标必须先转换。先看OpenCV里常见的反投影代码import cv2 import numpy as np def pixel_to_camera(u, v, depth, camera_matrix): # camera_matrix: 3x3 内参矩阵 fx camera_matrix[0, 0] fy camera_matrix[1, 1] cx camera_matrix[0, 2] cy camera_matrix[1, 2] # 假设相机光轴垂直工件平面深度为depth x_c (u - cx) * depth / fx y_c (v - cy) * depth / fy z_c depth return np.array([x_c, y_c, z_c, 1.0]) # 齐次坐标这里有个隐藏前提depth从哪来如果是3D相机直接读点云即可。如果是2D相机常见做法是固定工件高度把相机光心到工作平面的距离作为depth。这也是很多项目“看起来标定了但抓取精度忽高忽低”的原因工件堆叠高度不一致但你用了固定的depth那坐标必然偏移。2.4 两种坐标系方向的区分技巧手眼矩阵有方向性用反了就是灾难。给你一个验证技巧看平移向量t的正负和大小。假设相机和机器人的位置关系是相机装在机械臂正上方看下去那从相机到基座的z方向平移向量通常都是负的相机在基座上方z方向是负的如果算出来t的z分量是正的且还挺大多半是方向反了。更靠谱的方式还是做一次物理验证把相机拍到的一个特征点的像素坐标转成相机坐标。乘以手眼矩阵得到基座坐标。手动控制机器人末端去点这个点看坐标是否对得上。不用完全精确大差不差就行。反了的话会差得离谱一眼就能看出来。3. 实测案例手眼矩阵真正用在项目里的完整流程3.1 Eye-to-Hand抓取一个工件假设项目是这样的相机固定在工作站顶部机械臂在旁边。视觉识别到工件的位置机械臂去抓。代码流程如下import cv2 import numpy as np # 手眼矩阵相机-基座 (Eye-to-Hand) T_cam_to_base np.array([ [0.998, -0.045, 0.032, 250.5], [0.046, 0.999, -0.015, -120.3], [-0.031, 0.017, 0.999, 620.8], [0, 0, 0, 1] ]) # 相机内参示例值 camera_matrix np.array([ [1450.0, 0, 640.0], [0, 1450.0, 360.0], [0, 0, 1.0] ]) dist_coeffs np.array([0.01, -0.02, 0.001, 0.0005, 0.001]) def detect_workpiece_pixel(): # 这里替换成你的视觉识别代码 # 返回工件中心像素坐标 return 350.0, 265.0 def pick_workpiece(): u, v detect_workpiece_pixel() # 像素坐标转相机坐标 # 固定深度假设工件平面距离相机650mm depth 650.0 fx camera_matrix[0, 0] fy camera_matrix[1, 1] cx camera_matrix[0, 2] cy camera_matrix[1, 2] # 注意这里要先做畸变校正简化起见略过 x_c (u - cx) * depth / fx y_c (v - cy) * depth / fy z_c depth p_camera np.array([x_c, y_c, z_c, 1.0]) # 手眼矩阵转换 p_base T_cam_to_base p_camera robot_x p_base[0] robot_y p_base[1] robot_z p_base[2] # 发送给机器人 print(f目标位置: X{robot_x:.1f}, Y{robot_y:.1f}, Z{robot_z:.1f}) send_robot_command(robot_x, robot_y, robot_z)别急着把这段代码拷走有几个关键细节你没注意到第一畸变校正不能省。上面的代码为了清晰省略了畸变校正实际项目里必须处理。否则图像边缘的坐标误差轻松超过几个毫米甚至一厘米抓取根本没法用。正确做法是先用cv2.undistortPoints把像素坐标校正再反投影。或者更稳妥的做法是直接对整个图像去畸变再跑视觉识别。第二机器人接收的坐标可能需要加偏移。视觉识别的是工件的像素中心但你抓取时机器人末端不一定要到这个点可能是这个点上方某个安全高度或者考虑到夹爪的中心偏移需要再加一个工具坐标偏移。3.2 Eye-in-Hand抓取工件眼在手上的情况稍微复杂一点因为相机在动你算出来的基座坐标其实要实时结合机械臂当前的位姿。数据链是这样的P_camera相机坐标下的目标点。P_end X_handeye P_camera目标点在机械臂末端坐标系下的坐标。P_base T_base_to_end P_end目标点在基座坐标系下的坐标。其中T_base_to_end就是机械臂控制器实时上报的当前末端位姿矩阵。代码逻辑如下import numpy as np # 手眼矩阵相机-末端 (Eye-in-Hand) T_cam_to_end np.array([ [0.999, -0.02, 0.01, 30.0], [0.02, 0.999, 0.01, -15.0], [-0.01, -0.01, 0.999, 80.0], [0, 0, 0, 1] ]) def pick_workpiece_eih(u, v, depth, current_end_pose): current_end_pose: 4x4矩阵机械臂当前末端位姿基座-末端 # 像素转相机坐标同上略 x_c (u - cx) * depth / fx y_c (v - cy) * depth / fy z_c depth p_camera np.array([x_c, y_c, z_c, 1.0]) # 相机坐标 - 末端坐标 p_end T_cam_to_end p_camera # 末端坐标 - 基座坐标 p_base current_end_pose p_end return p_base[0], p_base[1], p_base[2]这里有个常见的工程陷阱机械臂上位机给的末端位姿有的厂家给的是4x4矩阵有的给的是平移加欧拉角有的给的是平移加四元数。你需要拿到数据后先转换成4x4矩阵再乘。这个转换做错了精度直接报废。3.3 坐标转换中旋转矩阵的用途很多项目里抓取不仅要移动到位还要调整工件的姿态。比如工件是圆的不需要调整姿态工件是方形的就得让夹爪的姿态跟工件对齐。手眼矩阵的旋转部分正好可以把工件的方向也转换过去。像素坐标里视觉识别通常只能给你一个2D旋转角度θ。要把它变成机器人能用的姿态有两种做法简单做法直接给机器人下发一个固定的姿态比如法兰朝下只改X、Y、Z这时候角度调整靠机器人端的坐标旋转指令。标准做法视觉给2D旋转角度你把它编码成绕Z轴的旋转矩陈R_z(θ)然后通过手眼矩阵的旋转部分转换到基座坐标系下的旋转矩阵再发RPY或四元数给机器人。标准做法的代码示例import numpy as np def angle_to_rotation2d(theta_deg): theta np.deg2rad(theta_deg) return np.array([ [np.cos(theta), -np.sin(theta), 0], [np.sin(theta), np.cos(theta), 0], [0, 0, 1] ]) # 假设视觉识别出的角度是30度相对于图像坐标系 R_obj_cam angle_to_rotation2d(30.0) # 手眼矩阵的旋转部分 R_handeye T_cam_to_base[:3, :3] # 转换到基座坐标系下 R_obj_base R_handeye R_obj_cam # 从旋转矩阵提取欧拉角发给机器人注意这里没有考虑相机安装时的非水平倾斜带来的额外旋转如果相机光轴不垂直于工作平面2D角度转换会引入误差这就是为什么很多高精度项目坚持用3D视觉或者把相机安装得绝对垂直。3.4 标定板、内参对实际使用的影响手眼矩阵再准相机内参不准那也是白搭。有的朋友项目里用的是低畸变镜头就觉得可以不做畸变校正这是个误区。即使低畸变镜头边缘区域也照样有几像素的误差。用2000万像素的相机几像素在物理空间可能就是0.5毫米。对于装配类应用来说0.5毫米已经非常致命了。我的建议是标定手眼之前务必先把相机内参和畸变系数标定好用OpenCV的calibrateCamera或Halcon的caltab功能都行。标定内参时拍15到20张不同姿态的标定板图不要少于10张。标定完成之后用cv2.undistort做一次全图去畸变在去畸变后的图像上做视觉识别这样坐标最稳。很多项目精度不稳定问题就出在这儿——视觉识别在原始图上跑然后直接用去畸变前的像素坐标去反投影坐标全是歪的。4. 常见问题与排查技巧实录4.1 手眼矩阵“看着没问题”但实际抓取偏差大这是最常遇到的疑难杂症。矩阵是标定程序算出来的理论上没问题但一实跑就偏有时候偏2毫米有时候偏5毫米。排查顺序我建议这样来先验证图像坐标转相机坐标这一步。放一个特征明显的工件到已知位置用卡尺量好它的物理坐标再算一下视觉识别后反投影出来的相机坐标对照是否合理。很多项目偏差就是depth设错了。再验证手眼矩阵方向。用前面说的物理验证法手动控制机器人末端去点相机视野里一个特征点看坐标对不对。检查机械臂TCP校准。这一步经常被忽略。机器人末端的工具坐标TCP不准手眼标定结果依然“收敛”但那是在错误的TCP下“收敛”的实际使用必然偏差。确认超时/回差。机械臂运动到位后要等它完全停稳再拍照否则图像上的工件位置和实际位置有偏差。如果你做的项目是相机固定、机械臂先移动到某个位置再拍照识别Move-Shoot-Move那还要检查第二次移动的重复定位精度是否影响抓取。4.2 Eye-in-Hand标定结果不稳定眼在手上标定对姿态变化的要求很高。如果你采集标定数据时机械臂姿态变化太小或者平移和旋转耦在一起解出来的矩阵就会不稳定。常见表现是连续标定两次结果差异很大或者手眼矩阵的旋转部分看起来“很怪”。解决办法标定数据采集时让机械臂末端在视野范围内做大姿态变化至少包含3个不同高度平面、每组不同姿态。每次移动后保证标定板尽量充满整个视野。标定板的角度尽量多样化正对、左倾、右倾、俯仰都要有。连续采集10到15组以上数据不要少于8组。这个和标定内参很像数据姿态越丰富解算越稳定。别嫌麻烦这一步做好后面能省很多事。4.3 手眼矩阵验证的正规方法建议在做任何实际抓取之前先做一个纯数学层面的验证。原理很简单利用标定过程的原始数据把标定板某个角点通过坐标链重新投影回图像看投影误差多大。粗略版做法标定完成后用标定板放在机器人工作平面。用相机拍一张图识别标定板原点的像素坐标。通过手眼矩阵把它变成机器人基座坐标。控制机器人末端带个尖针去这个点看是否对准标定板原点。需要强调的是这一步验证的是整条坐标链不光是手眼矩阵。如果测试结果有2到3毫米偏差先别急着改手眼标定先检查TCP、depth值、畸变校正这几项它们引入误差往往比手眼矩阵本身大得多。4.4 旋转矩阵的正交化问题标定程序算出来的R矩阵理论上应该是正交矩阵但数值上难免有微小误差。如果后续计算里要频繁使用这个矩阵最好做一次正交化避免误差累积。常用的方法是SVD正交化import numpy as np def orthogonalize(R): U, _, Vt np.linalg.svd(R) R_ortho U Vt # 确保是旋转矩阵而不是反射矩阵 if np.linalg.det(R_ortho) 0: U[:, -1] * -1 R_ortho U Vt return R_ortho这样处理完旋转矩阵就满足 R^T R I用起来更放心。不过别指望正交化能解决标定误差——它只能消除数值噪声改不了系统误差。4.5 问题速查表问题现象可能原因排查优先级目标坐标偏差几毫米到十几毫米depth设置错误、畸变未校正高转换后的坐标系统性偏移一个固定值机器人TCP不准高坐标随机械臂位置变化而变化手眼矩阵方向反了或标定数据姿态不足高某个区域准、其他区域不准相机畸变模型不准确或标定板未全视野覆盖中角度对、位置不对手眼矩阵平移向量错误中偶发坐标跳动视觉识别本身不稳定非标定问题中手眼矩阵标定两次结果差异很大采集数据姿态变化不足高5. 收个尾手眼矩阵工程的个人体会做手眼标定相关项目这么多年我最大的感受是标定本身是数学题把矩阵用起来是工程题。数学题有标准答案工程题全是细节。很多团队标定几分钟搞定却在“怎么用”上卡了一整天原因往往不是数学不行而是坐标系乱、链路不清晰、TCP没校准、深度值设置想当然。我自己每次拿到一个新项目不管客户催多紧都会坚持先跑一遍“坐标链闭环验证”把一个工件放在相机视野里算出坐标让机器人去碰看误差。这个过程花不了十分钟但能把大半隐患提前排掉。这个习惯救了我无数次建议你也养成。还有一个建议代码里给每个坐标系起名字的时候一定要带上“从哪来到哪去”的信息比如T_cam_to_base、T_end_to_base别只写个matrix1、matrix2。矩阵乘法谁乘谁一搞混整个坐标链就崩了而这种低级错误最容易在项目收尾赶工时发生。规范命名不仅是为了别人能看懂更是为了让你自己调试的时候少掉头发。最后手眼标定只是整个机器视觉项目里的一环。它很重要但不是全部。把它接进整套系统里让坐标流畅通地跑起来这才是真正的工程能力。希望这篇文章能帮你在这一步少走点弯路。
返回列表