4. 手眼标定:AX=XB问题求解、Tsai法与Park法对比

手眼标定,说白了就是让机器人知道自己手里拿的相机到底在哪儿。

我刚开始做这个的时候,觉得不就是解个方程嘛。后来发现,这个方程AX=XB,看着简单,坑可不少。今天咱们就把这个事儿聊透。

4.1 手眼标定的两种模式

先搞清楚场景。你想想看,相机装在机器人手上,这叫“眼在手上”。相机固定在天花板上看机器人动,这叫“眼在手外”。

两种场景下,AX=XB的含义其实一样:

  • A:机器人末端从位姿1到位姿2的变换
  • B:相机从位姿1到位姿2的变换
  • X:相机到机器人末端的固定变换(我们要的)

嗯,这里要注意。A和B都是通过运动算出来的。A来自机器人正运动学,B来自相机外参估计。两个都有误差,所以X的求解是个统计问题。

4.2 AX=XB的数学本质

这个方程其实是个李群上的方程。为什么难?因为X既要满足旋转约束,又要满足平移约束。

展开写就是:

R_A * R_X = R_X * R_B
R_A * t_X + t_A = R_X * t_B + t_X

你看,旋转部分是个齐次方程。平移部分依赖于旋转解。所以一般先解旋转,再解平移。

核心思路:把非线性问题拆成两步。先解旋转矩阵,再代入求平移向量。两步都带最小二乘。

4.3 Tsai法:两步法经典

Tsai法是我个人比较喜欢的方法。它把旋转矩阵参数化成轴角表示,然后线性化求解。

具体步骤:

  1. 从多组A、B中提取旋转部分,构造线性方程组
  2. 用最小二乘解出旋转轴方向
  3. 反解出旋转角度,得到R_X
  4. 代入平移方程,解t_X

我在项目中遇到过一个问题:Tsai法对旋转角度很小的数据特别敏感。如果机器人每次只转几度,解出来的R_X会飘。所以我建议采集数据时,让机器人转大角度,至少30度以上。

我的习惯:采集20组以上数据,覆盖不同姿态。旋转角度尽量大,平移距离尽量大。数据越丰富,解越稳。

4.4 Park法:李代数上的优化

Park法走的是另一条路。它直接在李代数上做优化,把旋转和平移一起解。

Park法的核心:

  • 把旋转矩阵映射到so(3)李代数上
  • 构造一个关于X的代价函数
  • 用迭代法(比如Levenberg-Marquardt)求解

说实话,Park法比Tsai法更鲁棒。因为它同时优化旋转和平移,不会出现误差传递的问题。

但代价是计算量大。嵌入式平台上跑起来有点吃力。我一般在PC上离线标定,用Park法。在线标定或者快速验证,用Tsai法就够了。

避坑指南:我曾经在Park法里忘了初始化。直接给单位矩阵当初值,结果迭代半天不收敛。后来改成先用Tsai法算个初值,再给Park法做精调,效果好了很多。

4.5 两种方法对比

对比项 Tsai法 Park法
求解方式 两步法,先旋转后平移 整体优化,旋转平移同时解
计算复杂度 低,可实时 高,需迭代
对噪声鲁棒性 中等,旋转误差会传递 较好,整体优化抑制噪声
数据要求 需要大角度运动 对运动幅度不敏感
适用场景 快速标定、嵌入式 高精度离线标定

4.6 代码实现要点

这里给个伪代码框架,你写的时候可以参考:

// Tsai法核心
function tsai_calibration(A_list, B_list):
    // 1. 提取旋转矩阵
    for each pair (A, B):
        R_A = A[0:3, 0:3]
        R_B = B[0:3, 0:3]
        // 构造轴角方程
        P_A = rotation_to_axis_angle(R_A)
        P_B = rotation_to_axis_angle(R_B)
        // 堆叠成线性方程组
        H += cross_product_matrix(P_A + P_B)
        g += P_B - P_A
    // 2. 解最小二乘
    P_X = H \ g
    R_X = axis_angle_to_rotation(P_X)
    // 3. 解平移
    for each pair:
        t_X = solve_translation(R_A, t_A, R_B, t_B, R_X)
    return R_X, mean(t_X)

// Park法核心
function park_calibration(A_list, B_list):
    // 1. 用Tsai法初始化
    R_X_init, t_X_init = tsai_calibration(A_list, B_list)
    // 2. 构造代价函数
    function cost(X):
        total = 0
        for each (A, B):
            error = A * X - X * B
            total += norm(error)
        return total
    // 3. LM优化
    X_opt = levenberg_marquardt(cost, [R_X_init, t_X_init])
    return X_opt

小技巧:写代码时,记得把旋转矩阵归一化。数值误差会导致R_X不是正交矩阵,用SVD投影回SO(3)群上。

4.7 误差分析与补偿

标定完了,怎么知道准不准?我一般看两个指标:

  • 重投影误差:用标定结果把3D点投影到图像上,看像素偏差
  • 残差范数:把X代回AX=XB,看等式两边的差

如果误差大,先检查数据质量。我遇到过最坑的一次,是机器人运动学参数没校准,导致A矩阵本身就有问题。标了半天,越标越差。

补偿方法:

  • 用多组数据做加权最小二乘,剔除离群点
  • 对旋转和平移分别做RANSAC
  • 最后用非线性优化做全局精调

嗯,说白了就是:数据要干净,方法要合适,结果要验证。这三步走下来,手眼标定基本不会出大问题。

手眼标定核心流程 数据采集 机器人运动 + 图像 计算A和B矩阵 正运动学 + 外参估计 Tsai法 两步线性求解 Park法 整体非线性优化 结果验证 重投影误差 不满足精度则重新采集 关键点总结 • 数据质量决定标定上限:大角度、大平移、多姿态 • Tsai法适合快速标定和嵌入式场景,计算量小 • Park法精度更高,但需要迭代,适合离线精标 • 先用Tsai法初始化,再用Park法精调,是最佳实践 • 验证环节不可跳过,重投影误差应小于1像素

可可精品资料库