坐标系与变换:世界坐标系、相机坐标系、机械臂基坐标系、工具坐标系之间的变换关系

各位同学,今天我们来聊聊坐标系与变换。说实话,这是整个抓取姿态优化的地基。你想想看,如果坐标系都搞不清楚,后面那些花哨的算法根本跑不起来。

我记得刚入行那会儿,带我的老工程师跟我说了一句话,我一直记到现在:「机器人抓取,本质上就是坐标系的来回跳舞。」当时觉得他在故弄玄虚,后来踩了无数坑才明白——这话一点不夸张。

四个核心坐标系,你得刻在脑子里

做抓取,你至少得跟四个坐标系打交道。我习惯把它们分成两组:固定组和移动组。

坐标系 类型 固定点 典型用途
世界坐标系 固定 场景中的某个物理点 全局定位、多设备协同
相机坐标系 固定/移动 相机光心 视觉感知、物体检测
机械臂基坐标系 固定 机械臂底座 运动规划、逆解计算
工具坐标系 移动 夹爪/吸盘末端 抓取姿态、TCP控制

嗯,这里要注意:相机坐标系有时候是固定的(比如眼在手外),有时候是移动的(比如眼在手上)。这个区别直接影响你的变换链路。

变换的本质:就是矩阵乘法

说白了,坐标系变换就是齐次变换矩阵的连乘。一个4x4矩阵,把旋转和平移打包在一起。我见过不少新手喜欢把旋转矩阵和平移向量分开算,结果算到一半就乱了。

我个人习惯用 T_ab 这种记法——T_ab 表示「从b坐标系到a坐标系的变换」。举个例子:

  • T_wc:相机坐标系 → 世界坐标系
  • T_cb:机械臂基坐标系 → 相机坐标系
  • T_bt:工具坐标系 → 机械臂基坐标系

那么从工具坐标系到世界坐标系的完整变换就是:

T_wt = T_wc × T_cb × T_bt

注意顺序!矩阵乘法不满足交换律。我在项目中遇到过有人把顺序写反了,结果机械臂直接往桌子上怼——还好当时速度设得慢。

眼在手外 vs 眼在手上

这两种配置,变换链路完全不同。我画了一张图,你看一眼就明白了。

两种视觉配置的变换链路对比 眼在手外(Eye-to-Hand) 相机固定,不随机械臂移动 变换链:T_wt = T_wc × T_cb × T_bt 标定目标:求 T_cb(手眼标定) 优势:视野稳定,精度高 劣势:易被遮挡 眼在手上(Eye-in-Hand) 相机装在机械臂末端 变换链:T_wt = T_wb × T_bc × T_ct 标定目标:求 T_bc(手眼标定) 优势:灵活,可多角度观察 劣势:精度受臂展影响 关键区别 眼在手外:相机看到机械臂 → 需要知道相机与基座的相对位姿 通用变换流程 物体检测 坐标转换 姿态优化 执行抓取

核心要点:无论哪种配置,最终目标都是把物体在相机坐标系下的坐标,转换到工具坐标系下。中间经过的变换矩阵,一个都不能少,一个都不能错。

手眼标定:绕不开的硬骨头

说到变换,就绕不开手眼标定。说白了,就是求相机和机械臂之间的那个变换矩阵。

我曾经在一个项目里,标定做了整整三天。为什么?因为标定板放的位置不对,每次算出来的结果都不一样。后来发现,标定板必须放在机械臂工作空间的中心区域,而且至少要采集15-20组不同姿态的数据。

我的标定小技巧:

  • 采集数据时,让机械臂走「米」字形路径,覆盖整个工作空间
  • 每组数据之间,机械臂的姿态变化要大(至少30°旋转)
  • 标定完成后,用一组独立数据做验证——误差超过2mm就重来

工具坐标系:TCP的精度决定一切

工具坐标系(TCP)的精度,直接影响抓取的成功率。我见过太多人把TCP标定当成走过场,结果抓取时总是偏那么几毫米。

TCP标定有两种常用方法:

  1. 三点法:让工具末端触碰一个固定尖点,从三个不同姿态记录数据
  2. 六点法:在三点法基础上,再确定工具坐标系的X轴和Z轴方向

我个人强烈推荐六点法。虽然多花几分钟,但精度能提升一个数量级。

避坑指南:我曾经在TCP标定时,用了磨损严重的尖点工具,结果标定误差达到5mm。后来换了新的标定尖点,误差直接降到0.3mm。标定工具本身的质量,千万别忽视。

实战中的变换链路

好了,理论说完了,咱们看看实际代码怎么写。下面是一个典型的抓取变换流程:

import numpy as np

# 假设我们已经标定好了以下变换矩阵
T_wc = np.eye(4)  # 世界→相机(已知)
T_cb = np.eye(4)  # 相机→机械臂基座(手眼标定结果)
T_bt = np.eye(4)  # 机械臂基座→工具(机器人正解)

# 物体在相机坐标系下的位姿(来自视觉检测)
obj_in_cam = np.array([0.1, -0.05, 0.3, 0, 0, 0, 1])  # x,y,z,qx,qy,qz,qw

# 转换为齐次矩阵
def pose_to_matrix(pose):
    x, y, z, qx, qy, qz, qw = pose
    # 这里省略四元数转旋转矩阵的代码
    # 实际项目中用 scipy.spatial.transform 或 eigen
    pass

# 物体在世界坐标系下的位姿
obj_in_world = T_wc @ obj_in_cam_matrix

# 物体在工具坐标系下的位姿(最终抓取姿态)
obj_in_tool = np.linalg.inv(T_bt) @ np.linalg.inv(T_cb) @ obj_in_world

print("抓取姿态(工具坐标系下):", obj_in_tool)

注意最后一步:我们用了两次逆矩阵。为什么?因为我们要把物体坐标「反向」变换到工具坐标系下。这个逻辑你捋一遍就明白了——物体在世界上,工具也在世界上,我们要知道工具怎么动才能抓到物体。

常见错误与排查思路

做坐标系变换,最容易出问题的就三个地方:

错误类型 现象 排查方法
矩阵顺序错误 机械臂往反方向跑 检查乘法顺序,用单位矩阵做基准测试
标定精度不足 抓取点偏移2-5mm 重新做手眼标定,增加数据量
TCP参数错误 姿态正确但位置偏 用六点法重新标定TCP

嗯,最后说一句:坐标系变换这东西,纸上谈兵永远不够。我建议你找个真实的机械臂,哪怕是小型的桌面机械臂,亲手跑一遍完整的变换链路。踩过坑,才能真正理解。