OpenCV相机标定与3D重建(10)眼标定函数calibrateHandEye()的使用

news2024/12/12 21:36:01
  • 操作系统:ubuntu22.04
  • OpenCV版本:OpenCV4.9
  • IDE:Visual Studio Code
  • 编程语言:C++11

算法描述

计算手眼标定: g T c _{}^{g}\textrm{T}_c gTc
cv::calibrateHandEye 是 OpenCV 中用于手眼标定的函数。该函数通过已知的机器人末端执行器(gripper)相对于基座(base)和平板(target)相对于相机(cam)的姿态来计算相机相对于末端执行器的姿态。

该函数使用各种方法进行手眼标定。一种方法包括先估计旋转再估计平移(可分离解法),并且实现了以下方法:

  • R. Tsai, R. Lenz A New Technique for Fully Autonomous and Efficient 3D Robotics Hand/Eye Calibration [269]

  • F. Park, B. Martin Robot Sensor Calibration: Solving AX = XB on the Euclidean Group [208]

  • R. Horaud, F. Dornaika Hand-Eye Calibration [124]
    另一种方法包括同时估计旋转和平移(同时解法),并且实现了以下方法:

  • N. Andreff, R. Horaud, B. Espiau On-line Hand-Eye Calibration [12]

  • K. Daniilidis Hand-Eye Calibration Using Dual Quaternions [63]
    下图描述了手眼标定问题,其中需要估计安装在机器人末端执行器上的相机(“eye”)相对于末端执行器(“hand”)的变换。这种配置称为 eye-in-hand。

eye-to-hand 配置由一个静态相机观察安装在机器人末端执行器上的校准图案组成。然后可以通过输入合适的变换矩阵到函数中来估计从相机到机器人基座坐标系的变换,见下方说明。
在这里插入图片描述
标定过程如下:

使用静态校准图案来估计目标坐标系和相机坐标系之间的变换。
移动机器人末端执行器以获取多个姿态。
对于每个姿态,记录从末端执行器坐标系到机器人基座坐标系的齐次变换矩阵,例如使用机器人的运动学。
[ X b Y b Z b 1 ] = [ b R g b t g 0 1 × 3 1 ] [ X g Y g Z g 1 ] \begin{bmatrix} X_b\\ Y_b\\ Z_b\\ 1 \end{bmatrix} = \begin{bmatrix} _{}^{b}\textrm{R}_g & _{}^{b}\textrm{t}_g \\ 0_{1 \times 3} & 1 \end{bmatrix} \begin{bmatrix} X_g\\ Y_g\\ Z_g\\ 1 \end{bmatrix} XbYbZb1 =[bRg01×3btg1] XgYgZg1
对于每个姿态,记录从校准目标坐标系到相机坐标系的齐次变换矩阵,例如使用基于2D-3D点对应关系的姿态估计方法(PnP)。
[ X c Y c Z c 1 ] = [ c R t c t t 0 1 × 3 1 ] [ X t Y t Z t 1 ] \begin{bmatrix} X_c\\ Y_c\\ Z_c\\ 1 \end{bmatrix} = \begin{bmatrix} _{}^{c}\textrm{R}_t & _{}^{c}\textrm{t}_t \\ 0_{1 \times 3} & 1 \end{bmatrix} \begin{bmatrix} X_t\\ Y_t\\ Z_t\\ 1 \end{bmatrix} XcYcZc1 =[cRt01×3ctt1] XtYtZt1
手眼标定过程返回以下齐次变换矩阵:
[ X g Y g Z g 1 ] = [ g R c g t c 0 1 × 3 1 ] [ X c Y c Z c 1 ] \begin{bmatrix} X_g\\ Y_g\\ Z_g\\ 1 \end{bmatrix} = \begin{bmatrix} _{}^{g}\textrm{R}_c & _{}^{g}\textrm{t}_c \\ 0_{1 \times 3} & 1 \end{bmatrix} \begin{bmatrix} X_c\\ Y_c\\ Z_c\\ 1 \end{bmatrix} XgYgZg1 =[gRc01×3gtc1] XcYcZc1
这个问题也被称为求解 AX=XB 方程:

对于 eye-in-hand 配置

对于 eye-to-hand 配置
在这里插入图片描述
注意
更多信息可以在该网站上找到。
至少需要两个具有非平行旋转轴的运动来确定手眼变换。因此,至少需要 3 个不同的姿态,但强烈建议使用更多的姿态。

函数原型

void cv::calibrateHandEye
(
	InputArrayOfArrays 	R_gripper2base,
	InputArrayOfArrays 	t_gripper2base,
	InputArrayOfArrays 	R_target2cam,
	InputArrayOfArrays 	t_target2cam,
	OutputArray 	R_cam2gripper,
	OutputArray 	t_cam2gripper,
	HandEyeCalibrationMethod 	method = CALIB_HAND_EYE_TSAI 
)	

参数

  • 参数[in] R_gripper2base: 从齐次矩阵中提取的旋转部分,该矩阵将一个用末端执行器坐标系表示的点变换到机器人基座坐标系 ( b T g _{}^{b}\textrm{T}_g bTg)。这是一个包含所有从末端执行器坐标系到机器人基座坐标系变换的旋转矩阵(3x3)或旋转向量(3x1)的向量(vector)。
  • 参数[in] t_gripper2base: 从齐次矩阵中提取的平移部分,该矩阵将一个用末端执行器坐标系表示的点变换到机器人基座坐标系 ( b T g _{}^{b}\textrm{T}_g bTg)。这是一个包含所有从末端执行器坐标系到机器人基座坐标系变换的平移向量(3x1)的向量(vector)。
  • 参数[in] R_target2cam: 从齐次矩阵中提取的旋转部分,该矩阵将一个用校准目标坐标系表示的点变换到相机坐标系 ( c T t _{}^{c}\textrm{T}_t cTt)。这是一个包含所有从校准目标坐标系到相机坐标系变换的旋转矩阵(3x3)或旋转向量(3x1)的向量(vector)。
  • 参数[in] t_target2cam: 从齐次矩阵中提取的平移部分,该矩阵将一个用校准目标坐标系表示的点变换到相机坐标系 ( c T t _{}^{c}\textrm{T}_t cTt)。这是一个包含所有从校准目标坐标系到相机坐标系变换的平移向量(3x1)的向量(vector)。
  • 参数[out] R_cam2gripper: 估计的从齐次矩阵中提取的旋转部分,该矩阵将一个用相机坐标系表示的点变换到末端执行器坐标系 ( g T c _{}^{g}\textrm{T}_c gTc)。这是一个 3x3 的旋转矩阵。
  • 参数[out] t_cam2gripper: 估计的从齐次矩阵中提取的平移部分,该矩阵将一个用相机坐标系表示的点变换到末端执行器坐标系 ( g T c _{}^{g}\textrm{T}_c gTc)。这是一个 3x1 的平移向量。
  • 参数[in] method: 实现的手眼标定方法之一,见 cv::HandEyeCalibrationMethod。

代码示例


#include <iostream>
#include <opencv2/opencv.hpp>
#include <vector>

using namespace cv;
using namespace std;

int main()
{
    // 假设我们有四组数据,分别对应不同的抓取位置
    int num_poses = 4;

    // 从 gripper 到 base 的旋转矩阵和位移向量
    vector< Mat > R_gripper2base( num_poses );
    vector< Mat > t_gripper2base( num_poses );

    // 从 target 到 cam 的旋转矩阵和位移向量
    vector< Mat > R_target2cam( num_poses );
    vector< Mat > t_target2cam( num_poses );

    // 初始化示例数据
    R_gripper2base[ 0 ] = ( Mat_< double >( 3, 3 ) << 1, 0, 0, 0, 1, 0, 0, 0, 1 );
    t_gripper2base[ 0 ] = ( Mat_< double >( 3, 1 ) << 0.1, 0.2, 0.3 );

    R_gripper2base[ 1 ] = ( Mat_< double >( 3, 3 ) << 0, -1, 0, 1, 0, 0, 0, 0, 1 );
    t_gripper2base[ 1 ] = ( Mat_< double >( 3, 1 ) << 0.4, 0.5, 0.6 );

    R_gripper2base[ 2 ] = ( Mat_< double >( 3, 3 ) << 0, 0, -1, 0, 1, 0, 1, 0, 0 );
    t_gripper2base[ 2 ] = ( Mat_< double >( 3, 1 ) << 0.7, 0.8, 0.9 );

    R_gripper2base[ 3 ] = ( Mat_< double >( 3, 3 ) << 0, 0, 1, 0, 1, 0, -1, 0, 0 );
    t_gripper2base[ 3 ] = ( Mat_< double >( 3, 1 ) << 1.0, 1.1, 1.2 );

    R_target2cam[ 0 ] = ( Mat_< double >( 3, 3 ) << 1, 0, 0, 0, 1, 0, 0, 0, 1 );
    t_target2cam[ 0 ] = ( Mat_< double >( 3, 1 ) << 0.3, 0.4, 0.5 );

    R_target2cam[ 1 ] = ( Mat_< double >( 3, 3 ) << 0, -1, 0, 1, 0, 0, 0, 0, 1 );
    t_target2cam[ 1 ] = ( Mat_< double >( 3, 1 ) << 0.6, 0.7, 0.8 );

    R_target2cam[ 2 ] = ( Mat_< double >( 3, 3 ) << 0, 0, -1, 0, 1, 0, 1, 0, 0 );
    t_target2cam[ 2 ] = ( Mat_< double >( 3, 1 ) << 0.9, 1.0, 1.1 );

    R_target2cam[ 3 ] = ( Mat_< double >( 3, 3 ) << 0, 0, 1, 0, 1, 0, -1, 0, 0 );
    t_target2cam[ 3 ] = ( Mat_< double >( 3, 1 ) << 1.2, 1.3, 1.4 );

    // 输出变量
    Mat R_cam2gripper, t_cam2gripper;

    // 执行手眼标定
    calibrateHandEye( R_gripper2base, t_gripper2base, R_target2cam, t_target2cam, R_cam2gripper, t_cam2gripper, CALIB_HAND_EYE_TSAI );

    // 输出结果
    cout << "Rotation matrix from camera to gripper:\n" << R_cam2gripper << endl;
    cout << "Translation vector from camera to gripper:\n" << t_cam2gripper << endl;

    return 0;
}

运行结果

Rotation matrix from camera to gripper:
[0.7999999999999999, 0.6000000000000001, 0;
 -0.6000000000000001, 0.7999999999999999, 0;
 0, 0, 1]
Translation vector from camera to gripper:
[-0.4380000000000001;
 -0.6659999999999999;
 -0.63]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2256846.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【CSS in Depth 2 精译_072】第 12 章 CSS 排版与间距概述 + 12.1 间距设置(上):究竟该用 em 还是 px

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第四部分 视觉增强技术 ✔️【第 12 章 CSS 排版与间距】 ✔️ 12.1 间距设置 ✔️ 12.1.1 使用 em 还是 px ✔️12.1.2 对行高的深入思考12.1.3 行内元素的间距设置 文章目录 第 12 章 排版与间距…

数据结构代码归纳

1.线性表 线性表的顺序表示 定义与初始化 typedef struct SqList{ElemType data[MaxSize];//ElemType *data 开动态数组 int length; }Sqlist; void InitList(SqList &L){L.length0;//若静态数组//若动态数组 //L.data(ElemType*)malloc(sizeof(ElemType)*MaxSize); }…

数据结构 (36)各种排序方法的综合比较

一、常见排序方法分类 插入排序类 直接插入排序&#xff1a;通过构建有序序列&#xff0c;对于未排序数据&#xff0c;在已排序序列中从后向前扫描&#xff0c;找到相应位置并插入。希尔排序&#xff1a;是插入排序的一种改进版本&#xff0c;先将整个待排序的记录序列分割成为…

SpringMVC全局异常处理

一、Java中的异常 定义&#xff1a;异常是程序在运行过程中出现的一些错误&#xff0c;使用面向对象思想把这些错误用类来描述&#xff0c;那么一旦产生一个错误&#xff0c;即创建某一个错误的对象&#xff0c;这个对象就是异常对象。 类型&#xff1a; 声明异常&#xff1…

【高中生讲机器学习】28. 集成学习之 Bagging 随机森林!

创建时间&#xff1a;2024-12-09 首发时间&#xff1a;2024-12-09 最后编辑时间&#xff1a;2024-12-09 作者&#xff1a;Geeker_LStar 嘿嘿&#xff0c;你好呀&#xff01;我又来啦~~ 前面我们讲完了集成学习之 Boooooosting&#xff0c;这篇我们来看看集成学习的另一个分支…

springSecurity权限控制

权限控制&#xff1a;不同的用户可以使用不同的功能。 我们不能在前端判断用户权限来控制显示哪些按钮&#xff0c;因为这样&#xff0c;有人会获取该功能对应的接口&#xff0c;就不需要通过前端&#xff0c;直接发送请求实现功能了。所以需要在后端进行权限判断。&#xff0…

李飞飞的生成式3D场景,对数字孪生的未来影响几何?

大家好&#xff0c;我是日拱一卒的攻城师不浪&#xff0c;致力于技术与艺术的融合。这是2024年输出的第47/100篇文章。 前言 这两天&#xff0c;AI界的教母李飞飞团队重磅发布了空间智能生成式AI大模型。 仅通过一张图片就能够生成一个可操作和交互的3D空间场景。 空间智能的…

意图识别模型使用 基于BERT的对话意图和槽位联合识别 CPU运行BERT模型-亲测成功

意图识别模型使用 基于BERT的对话意图和槽位联合识别 CPU运行BERT模型-亲测成功 我们在开发AI-Agent智能体时&#xff0c;通常会使用提示词工程设置场景的带入&#xff0c;在实际项目中会有很多场景&#xff0c;如果所有提示词都放一起就会超过Token限制&#xff0c;则不得不拆…

OSG开发笔记(三十七):OSG基于windows平台msvc2017x64编译器官方稳定版本OSG3.4.1搭建环境并移植Demo

​若该文为原创文章&#xff0c;未经允许不得转载 本文章博客地址&#xff1a;https://blog.csdn.net/qq21497936/article/details/144258047 各位读者&#xff0c;知识无穷而人力有穷&#xff0c;要么改需求&#xff0c;要么找专业人士&#xff0c;要么自己研究 长沙红胖子Qt…

《毛泽东思想和中国特色社会理论概述》课程报告Latex版本

所需要的图片: 源码(可运行): \documentclass[12pt]{article} \usepackage{ctex} \usepackage{graphicx} \usepackage{booktabs} \usepackage{titlesec} \usepackage{geometry} \usepackage{float} \usepackage{tabularx} \usepackage{enumitem} …

2024.12.6——攻防世界PHP2

知识点&#xff1a;目录扫描 代码审计 传参知识点补充&#xff1a; 后缀名为.phps的文件出现在无法使用web浏览器查看php源代码的情况下&#xff0c;.phps文件就是php的源代码文件&#xff0c;通常用于提供给用户&#xff08;访问者&#xff09;查看php代码&#xff0c;因为用…

OSI模型及各层缺陷

1&#xff0e;TCP/IP概述 &#xff08;1&#xff09;TCP/IP基本结构 TCP/IP是一组Internet协议&#xff0c;不但包括TCP和IP两个关键协议&#xff0c;还包括其他协议&#xff0c;如UDP、ARP、ICMP、Telnet和FTP等。TCP/IP的设计目标是使不同的网络互相连接&#xff0c;即实现互…

【论文阅读】Fifty Years of the ISCA: A Data-Driven Retrospective

学习体会&#xff1a; ISCA会议近五十年文章分析, 了解论文热点方向, 处理器依旧是热点! AI和并行是大趋势, 做XPU相关目前来说还是热点~ 摘录自原文 摘录: 数据来源和分析方法&#xff1a; 作者收集了 ACM 数字图书馆中所有 ISCA 论文&#xff0c;并使用 DBLP、Google Schol…

什么是MMD Maximum Mean Discrepancy 最大均值差异?

9多次在迁移学习看到了&#xff0c;居然还是Bernhard Schlkopf大佬的论文&#xff0c;仔细看看。 一.什么是MMD&#xff1f; 1. MMD要做什么&#xff1f; 判断两个样本&#xff08;族&#xff09;是不是来自于同一分布 2.怎么做&#xff1f;&#xff08;直观上&#xff09;…

LDR6500:音频双C支持,数字与模拟的完美结合

在当今数字化快速发展的时代&#xff0c;音频设备的兼容性和性能成为了用户关注的重点。LDR6500&#xff0c;作为乐得瑞科技精心研发的USB Power Delivery&#xff08;PD&#xff09;协议芯片&#xff0c;凭借其卓越的性能和广泛的应用兼容性&#xff0c;为音频设备领域带来了新…

Leetcode 每日一题 1.两数之和

目录 问题描述 示例 示例 1 示例 2 示例 3 提示 解决方案 算法思路 过题图片 代码实现 复杂度分析 注意事项 题目链接 结论 问题描述 给定一个整数数组 nums 和一个目标值 target&#xff0c;请你找出数组中和为目标值的那两个整数&#xff0c;并返回它们的数组下…

【萤火工场CEM5826-M11测评】Arduino 采集雷达模块数据与串口绘图

【萤火工场CEM5826-M11测评】Arduino 采集雷达模块数据与串口绘图 当采用串口输出模式时&#xff0c;雷达检测到运动时&#xff0c;则输出 v0.0km/h, str1234 字样&#xff1b; v 表示目标速度大小&#xff0c;str 表示信号强度&#xff1b; 当雷达检测不到目标时&#xff0c…

【C++】选择排 序算法分析与扩展

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;代码回顾&#x1f4af;选择排序的算法流程&#x1f4af;代码详解外层循环初始化最小值内层循环比较与更新元素交换 &#x1f4af;选择排序的特性时间复杂度空间复杂度稳定性…

3D 生成重建024-LGM第一个开源的3D生成大模型!

3D 生成重建024-LGM第一个开源的3D生成大模型 文章目录 0 论文工作1 论文方法2 实验效果 0 论文工作 这篇论文介绍了一种名为LGM&#xff08;大型多视角高斯模型&#xff09;的新方法&#xff0c;用于从单视角图像或文本提示生成高分辨率的三维内容。该方法的核心思想是双重的…

微信 创建小程序码-有数量限制

获取小程序码&#xff1a;小程序码为圆图&#xff0c;有数量限制。 目录 文档 接口地址 功能描述 注意事项 请求参数 对接 获取小程序码 调用获取 小程序码示例 总结 文档 接口地址 https://api.weixin.qq.com/wxa/getwxacode?access_tokenaccess_token 功能描述 …