GrabBag/Module/HandEyeCalib/Doc/HandEyeCalib.md
2026-02-04 00:28:38 +08:00

11 KiB
Raw Blame History

HandEyeCalib 手眼标定模块

概述

HandEyeCalib 是一个基于 Eigen 库实现的手眼标定模块,支持 Eye-To-Hand眼在手外和 Eye-In-Hand眼在手上两种标定模式。

依赖

  • Eigen 3.3.9+(仅头文件)
  • C++11 或更高版本

两种标定模式

Eye-To-Hand眼在手外

相机固定安装在外部(如支架上),不随机器人运动。

┌─────────┐
│  相机   │ ← 固定位置
└────┬────┘
     │ 观测
     ▼
┌─────────┐      ┌─────────┐
│ 标定点  │ ←──→ │ 机器人  │
└─────────┘      └─────────┘

求解目标:相机坐标系到机器人基座坐标系的变换矩阵

数学模型

P_robot = R * P_camera + T

Eye-In-Hand眼在手上

相机安装在机器人末端,随机器人运动。

┌─────────┐
│ 机器人  │
└────┬────┘
     │
┌────┴────┐
│  末端   │
├─────────┤
│  相机   │ ← 随末端运动
└────┬────┘
     │ 观测
     ▼
┌─────────┐
│ 标定点  │ ← 固定位置
└─────────┘

求解目标:相机坐标系到末端坐标系的变换矩阵

数学模型AX=XB 问题):

T_end * T_cam * P_camera = P_base

算法原理

SVD 分解法Eye-To-Hand

  1. 计算两组点的质心
  2. 去中心化处理
  3. 构建协方差矩阵 W = Σ(q1 * q2^T)
  4. SVD 分解W = U * S * V^T
  5. 计算旋转矩阵R = V * M * U^T
  6. 计算平移向量T = p2 - R * p1

Tsai-Lenz 方法Eye-In-Hand

  1. 构建相邻位姿间的相对变换 A = T_end_i^(-1) * T_end_j
  2. 构建超定方程组
  3. SVD 求解旋转矩阵
  4. 最小二乘求解平移向量

欧拉角旋转顺序

支持6种常用旋转顺序

枚举值 旋转顺序 说明
HECEulerOrder::XYZ X→Y→Z Roll-Pitch-Yaw
HECEulerOrder::XZY X→Z→Y
HECEulerOrder::YXZ Y→X→Z
HECEulerOrder::YZX Y→Z→X
HECEulerOrder::ZXY Z→X→Y
HECEulerOrder::ZYX Z→Y→X Yaw-Pitch-Roll机器人常用

API 参考

创建/销毁实例

// 创建实例
IHandEyeCalib* calib = CreateHandEyeCalibInstance();

// 使用完毕后销毁
DestroyHandEyeCalibInstance(calib);

Eye-To-Hand 标定

int CalculateRT(
    const std::vector<HECPoint3D>& eyePoints,    // 相机坐标系下的点
    const std::vector<HECPoint3D>& robotPoints,  // 机器人坐标系下的点
    HECCalibResult& result);                     // 输出结果

Eye-In-Hand 标定

// 标定点位置未知
int CalculateEyeInHand(
    const std::vector<HECEyeInHandData>& calibData,  // 标定数据
    HECCalibResult& result);                         // 输出结果

// 标定点位置已知
int CalculateEyeInHandWithTarget(
    const std::vector<HECEyeInHandData>& calibData,  // 标定数据
    const HECPoint3D& targetInBase,                  // 标定点在基座下的坐标
    HECCalibResult& result);                         // 输出结果

坐标变换

// 完整变换: dst = R * src + T
void TransformPoint(
    const HECRotationMatrix& R,
    const HECTranslationVector& T,
    const HECPoint3D& srcPoint,
    HECPoint3D& dstPoint);

// 仅旋转: dst = R * src
void RotatePoint(
    const HECRotationMatrix& R,
    const HECPoint3D& srcPoint,
    HECPoint3D& dstPoint);

欧拉角转换

// 旋转矩阵 → 欧拉角
void RotationMatrixToEuler(
    const HECRotationMatrix& R,
    HECEulerOrder order,
    HECEulerAngles& angles);

// 欧拉角 → 旋转矩阵
void EulerToRotationMatrix(
    const HECEulerAngles& angles,
    HECEulerOrder order,
    HECRotationMatrix& R);

使用示例

示例1Eye-To-Hand 标定

#include "IHandEyeCalib.h"

void EyeToHandCalibration()
{
    // 创建标定实例
    IHandEyeCalib* calib = CreateHandEyeCalibInstance();

    // 准备标定数据至少3组对应点
    std::vector<HECPoint3D> cameraPoints;  // 相机坐标系下的点
    std::vector<HECPoint3D> robotPoints;   // 机器人坐标系下的点

    // 添加标定点(示例数据)
    cameraPoints.push_back(HECPoint3D(100.0, 50.0, 200.0));
    robotPoints.push_back(HECPoint3D(500.0, 300.0, 100.0));

    cameraPoints.push_back(HECPoint3D(150.0, 80.0, 210.0));
    robotPoints.push_back(HECPoint3D(550.0, 330.0, 110.0));

    cameraPoints.push_back(HECPoint3D(120.0, 60.0, 195.0));
    robotPoints.push_back(HECPoint3D(520.0, 310.0, 95.0));

    // 更多点...建议10组以上

    // 执行标定
    HECCalibResult result;
    int ret = calib->CalculateRT(cameraPoints, robotPoints, result);

    if (ret == 0) {
        // 标定成功,输出结果
        printf("标定误差: %.3f mm\n", result.error);

        // 使用标定结果进行坐标转换
        HECPoint3D cameraPoint(130.0, 70.0, 205.0);
        HECPoint3D robotPoint;
        calib->TransformPoint(result.R, result.T, cameraPoint, robotPoint);

        printf("相机点 (%.2f, %.2f, %.2f) -> 机器人点 (%.2f, %.2f, %.2f)\n",
               cameraPoint.x, cameraPoint.y, cameraPoint.z,
               robotPoint.x, robotPoint.y, robotPoint.z);
    }

    // 销毁实例
    DestroyHandEyeCalibInstance(calib);
}

示例2Eye-In-Hand 标定(标定点位置未知)

#include "IHandEyeCalib.h"

void EyeInHandCalibration()
{
    IHandEyeCalib* calib = CreateHandEyeCalibInstance();

    // 准备标定数据
    std::vector<HECEyeInHandData> calibData;

    // 在不同位姿下采集数据
    for (int i = 0; i < 10; i++) {
        HECEyeInHandData data;

        // 获取当前末端位姿(从机器人控制器读取)
        // data.endPose = getRobotEndPose();

        // 示例:构建末端位姿矩阵
        HECRotationMatrix R_end;
        HECTranslationVector T_end(500.0 + i * 10, 200.0, 300.0 - i * 5);
        data.endPose = HECHomogeneousMatrix(R_end, T_end);

        // 获取相机观测到的标定点坐标
        // data.targetInCamera = detectTargetInCamera();
        data.targetInCamera = HECPoint3D(100.0 - i * 2, 50.0 + i, 200.0);

        calibData.push_back(data);
    }

    // 执行标定
    HECCalibResult result;
    int ret = calib->CalculateEyeInHand(calibData, result);

    if (ret == 0) {
        printf("Eye-In-Hand 标定成功\n");
        printf("标定误差: %.3f mm\n", result.error);

        // 输出相机到末端的变换矩阵
        printf("旋转矩阵 R:\n");
        for (int i = 0; i < 3; i++) {
            printf("  [%.6f, %.6f, %.6f]\n",
                   result.R.at(i, 0), result.R.at(i, 1), result.R.at(i, 2));
        }
        printf("平移向量 T: [%.3f, %.3f, %.3f]\n",
               result.T.at(0), result.T.at(1), result.T.at(2));
    }

    DestroyHandEyeCalibInstance(calib);
}

示例3Eye-In-Hand 标定(标定点位置已知)

#include "IHandEyeCalib.h"

void EyeInHandCalibrationWithTarget()
{
    IHandEyeCalib* calib = CreateHandEyeCalibInstance();

    std::vector<HECEyeInHandData> calibData;

    // 采集多组数据同示例2
    // ...

    // 标定点在基座坐标系下的已知位置
    HECPoint3D targetInBase(600.0, 400.0, 50.0);

    // 执行标定
    HECCalibResult result;
    int ret = calib->CalculateEyeInHandWithTarget(calibData, targetInBase, result);

    if (ret == 0) {
        printf("标定成功,误差: %.3f mm\n", result.error);
    }

    DestroyHandEyeCalibInstance(calib);
}

示例4欧拉角转换

#include "IHandEyeCalib.h"

void EulerAngleConversion()
{
    IHandEyeCalib* calib = CreateHandEyeCalibInstance();

    // 从欧拉角创建旋转矩阵
    HECEulerAngles angles;
    angles.roll = 0.1;   // 弧度
    angles.pitch = 0.2;
    angles.yaw = 0.3;

    HECRotationMatrix R;

    // 使用 ZYX 顺序(机器人常用)
    calib->EulerToRotationMatrix(angles, HECEulerOrder::ZYX, R);

    // 使用 XYZ 顺序
    calib->EulerToRotationMatrix(angles, HECEulerOrder::XYZ, R);

    // 从旋转矩阵提取欧拉角
    HECEulerAngles extractedAngles;
    calib->RotationMatrixToEuler(R, HECEulerOrder::ZYX, extractedAngles);

    // 角度制转换
    double roll_deg, pitch_deg, yaw_deg;
    extractedAngles.toDegrees(roll_deg, pitch_deg, yaw_deg);
    printf("欧拉角 (度): Roll=%.2f, Pitch=%.2f, Yaw=%.2f\n",
           roll_deg, pitch_deg, yaw_deg);

    DestroyHandEyeCalibInstance(calib);
}

示例5完整的位姿转换

#include "IHandEyeCalib.h"

void PoseTransformation()
{
    IHandEyeCalib* calib = CreateHandEyeCalibInstance();

    // 假设已完成标定
    HECCalibResult calibResult;
    // ... 标定过程 ...

    // 相机检测到的目标位置
    HECPoint3D eyePoint(100.0, 50.0, 200.0);

    // 相机检测到的目标姿态X/Y/Z轴方向向量
    std::vector<HECPoint3D> eyeDirVectors;
    eyeDirVectors.push_back(HECPoint3D(1.0, 0.0, 0.0));  // X轴
    eyeDirVectors.push_back(HECPoint3D(0.0, 1.0, 0.0));  // Y轴
    eyeDirVectors.push_back(HECPoint3D(0.0, 0.0, 1.0));  // Z轴

    // 转换到机器人坐标系
    HECPoseResult poseResult;
    calib->TransformPose(calibResult, eyePoint, eyeDirVectors, false, poseResult);

    // 输出机器人位姿
    printf("机器人位置: X=%.2f, Y=%.2f, Z=%.2f\n",
           poseResult.position.x, poseResult.position.y, poseResult.position.z);

    double roll_deg, pitch_deg, yaw_deg;
    poseResult.angles.toDegrees(roll_deg, pitch_deg, yaw_deg);
    printf("机器人姿态: Roll=%.2f°, Pitch=%.2f°, Yaw=%.2f°\n",
           roll_deg, pitch_deg, yaw_deg);

    DestroyHandEyeCalibInstance(calib);
}

标定建议

  1. 采集点数:建议采集 10-20 组标定点
  2. 点分布:标定点应均匀分布在工作空间内
  3. 避免共面:标定点不应全部位于同一平面
  4. 精度要求:机器人示教精度和相机检测精度直接影响标定结果
  5. 验证:标定完成后,使用新的测试点验证标定精度

错误码

错误码 说明
0 (SUCCESS) 成功
APP_ERR_PARAM 参数错误(点数不足或不匹配)

文件结构

Module/HandEyeCalib/
├── Inc/
│   ├── HandEyeCalib_global.h    # 导出宏定义
│   ├── HandEyeCalibTypes.h      # 数据类型定义
│   └── IHandEyeCalib.h          # 接口定义
├── _Inc/
│   └── HandEyeCalib.h           # 实现类声明
├── Src/
│   └── HandEyeCalib.cpp         # 实现
├── Doc/
│   └── HandEyeCalib.md          # 本文档
└── HandEyeCalib.pro             # Qt 工程文件