当前位置: 首页 > news >正文

线激光相机 眼在手上六轴机器人手眼标定 备忘记录

线激光相机 眼在手上六轴机器人手眼标定

之前一直有用线激光做跟踪和寻位焊接,但是手眼标定一直是使用公司的库做的,自己根据理解和查的资料写一下,有点问题,还需要右面再看看,先记录一下吧
说明:公司那套软甲是拿一个具有明显直角边上面绘制一个点,使用机器人不同位姿,线激光穿过那个点,记录相机坐标和机器人位姿,大于6个点拟合的
相机坐标是直线相交交点,或者标定球拟合球心也行,方式不一样
核心原理:
P(标定点机器人坐标) = T(工具->基座) x T(相机->工具,待求矩阵) x P(相机点)
由于P(标定点机器人坐标) 是固定的,所以可以列出多个方程
T(点1:工具->基座) x T(相机->工具,待求矩阵) x P(点1:相机点) = T(点2:工具->基座) x T(相机->工具,待求矩阵) x P(点2:相机点)
但是这个和传统的手眼标定(AX=XB)还不一样,获取的直接是点坐标,不是转换矩阵,所以就难住了,不太会解方程组,所以想来想去,还是把P(标定点机器人坐标) 当成已知量来用吧

原理部分:

可以直接借鉴【线激光手眼标定】这篇文章吧,这篇文章和该硕士论文【面向焊缝跟踪的线激光检测技术研究】 原理一致,都可以参考,把相机坐标点、机器人位姿和标定点在机器人下的坐标当成已知量做,后面会解方程了,再把(标定点在机器人下的坐标)这种方式写一下

原理简介:

1.这个就是基础的方程,和我上面那个一样
在这里插入图片描述
2.这个方程拆解出来如下:
左侧T(BF)为机器人位姿的旋转矩阵,这个组合的时候需要看三轴的旋转顺序,如果是ABB这种是四元素表示的,需要对应变换到旋转矩阵
XL 和 ZL 为线激光某个点的坐标,是在线激光平面,所以只有两维,Z表示深度,X表示线激光走向方向,Y方向默认0或者其他常数
右侧xyz 为基座坐标系下标记点的坐标,可以直接用机器人末端指点获取
在这里插入图片描述
3.机器人POS矩阵左乘逆矩阵,变换如下
在这里插入图片描述
4.上面等号右侧为已知量,直接用XYZ代替
在这里插入图片描述
5.把4公式拆出来
在这里插入图片描述
6.再变换成XA=B格式,此处公式显示的是3个点对,如果是10个点,就是3x3 x 3x10 = 3x10
在这里插入图片描述
7.这个方程模仿AX=B,也可以做如下变换
XA = B
XAA(T) = BA(T) 此时AA(T)是方阵 有逆矩阵
X = BA(T)(AA(T))(-1) 按这个求解
8. 求出来X了,即代表求出来了r11 r21 r31 r13 r23 r33 x y z但是还缺r12 r22 r23三个变量,由于旋转矩阵是两两正交的,所以可以如下求出来,但是要注意该向量的朝向,如果我r11 r21 r31 是X旋转,r13 r23 r33是Z旋转,那我的Y就是Z叉乘X,下面公式右侧要对调一下,叉乘前看是否要对向量归一化,看需求吧
在这里插入图片描述
9.就算求完了,把旋转矩阵组合出来就行

实践一下:

我找了三组数据,各有缺陷,也不知道算不算能验证,先将就看吧
第一组数据
数据是XYZ旋转模式下的,但是我的数据只有相机坐标系点和位姿,没有实际标定物的基座坐标,我直接用了公司标定计算出来的那个位置当已知量(我记得这个和实际机器人指出来还是有误差得)用了

	//机器人位姿std::vector<Pos>BasePos;BasePos.resize(8);BasePos[0].x = 1092.010; BasePos[0].y = 126.6960; BasePos[0].z = 117.653; BasePos[0].u = 36.310; BasePos[0].v = -1.360; BasePos[0].w = -90.776;BasePos[1].x = 1146.710; BasePos[1].y = 102.007; BasePos[1].z = 151.027; BasePos[1].u = 20.003; BasePos[1].v = 23.049; BasePos[1].w = -100.17;BasePos[2].x = 1097.360; BasePos[2].y = 78.745; BasePos[2].z = 188.125; BasePos[2].u = 10.537; BasePos[2].v = 37.359; BasePos[2].w = -105.62;BasePos[3].x = 1097.310; BasePos[3].y = 26.030; BasePos[3].z = 212.116; BasePos[3].u = 13.159; BasePos[3].v = 53.515; BasePos[3].w = -121.90;BasePos[4].x = 1097.170; BasePos[4].y = 127.129; BasePos[4].z = 61.216; BasePos[4].u = 24.756; BasePos[4].v = -33.425; BasePos[4].w = -82.095;BasePos[5].x = 1096.990; BasePos[5].y = 106.164; BasePos[5].z = 23.957; BasePos[5].u = 31.359; BasePos[5].v = -53.551; BasePos[5].w = -77.848;BasePos[6].x = 1062.870; BasePos[6].y = 110.995; BasePos[6].z = 48.866; BasePos[6].u = 5.284; BasePos[6].v = -55.024; BasePos[6].w = -48.495;BasePos[7].x = 1071.050; BasePos[7].y = 111.934; BasePos[7].z = 24.186; BasePos[7].u = 22.083; BasePos[7].v = -53.473; BasePos[7].w = -84.305;//相机坐标std::vector<Eigen::Vector3d>src;src.push_back(Eigen::Vector3d(5.207, 3.058, 212.184));src.push_back(Eigen::Vector3d(-14.651, 3.058, 187.370));src.push_back(Eigen::Vector3d(33.843, 3.058, 223.949));src.push_back(Eigen::Vector3d(-3.927, 3.058, 257.037));src.push_back(Eigen::Vector3d(4.244, 3.058, 195.328));src.push_back(Eigen::Vector3d(-1.143, 3.058, 189.084));src.push_back(Eigen::Vector3d(-6.016, 3.058, 218.697));src.push_back(Eigen::Vector3d(37.936, 3.058, 200.879));//世界坐标Eigen::Vector4d Points(1156.62, 36.5753, 73.0081, 1.0);

这组数据,之前标定结果如下,我直接把最下边的坐标当成已知量用了,人家这个相比于上面这个少了三个已知量,更简单一点,后面再研究研究吧:
在这里插入图片描述
用这组数据算法来的转换矩阵,反正是有误差的,Z方向误差有4mm,我们就理解是世界坐标这个已知量有问题吧
在这里插入图片描述
第二组数据
第二组数据用的是上面链接博客【线激光手眼标定】里的数据
在这里插入图片描述
这个是四元素形式的,变成转换矩阵可以参考我另一篇博客【ABB机器人四元素处理】
计算下来,我理解和他这个应该是一样的,但是不知道他第二列第三列反了一下还有有个轴取反了是啥意思,看不太懂
在这里插入图片描述
第三组数据
这个数据是上面论文里的数据,算出来也和他稍微不一样,不知,道问题出在哪里,唉,我确定我没抄错,将就着看吧,反正算出来看着是不一样,差点意思,中间这个向量反向了,都行吧,不太影响
在这里插入图片描述
在这里插入图片描述
代码贴上来吧,如果大家发现了什么问题,或者哪里有问题,自己修改了别忘记给我留个评论说一下,让我也同步一下,有参考意义的话,可以留下点赞和收藏

#include <Eigen/Dense>
#include <Eigen/SVD>
#include <iostream>
#include <vector>
#include <cmath>
#include <vector>
#include <unsupported/Eigen/KroneckerProduct>
#define M_PI 3.141592653
using namespace Eigen;
struct Pos
{double x;double y;double z;double u;double v;double w;double q;Pos() {}Pos(double x_, double y_, double z_, double u_, double v_, double w_) {x = x_;y = y_;z = z_;u = u_;v = v_;w = w_;}
};
Eigen::Matrix4d PosToMatrix4d(Pos pos) {//构建三个单独的旋转矩阵Eigen::Matrix3d Matrix_rx;Eigen::Matrix3d Matrix_ry;Eigen::Matrix3d Matrix_rz;Matrix_rx << 1, 0, 0,0, cos(pos.u*M_PI / 180), -1.0*sin(pos.u*M_PI / 180),0, sin(pos.u*M_PI / 180), cos(pos.u*M_PI / 180);Matrix_ry << cos(pos.v*M_PI / 180), 0, sin(pos.v*M_PI / 180),0, 1, 0,-1.0*sin(pos.v*M_PI / 180), 0, cos(pos.v*M_PI / 180);Matrix_rz << cos(pos.w*M_PI / 180), -1.0*sin(pos.w*M_PI / 180), 0,sin(pos.w*M_PI / 180), cos(pos.w*M_PI / 180), 0,0, 0, 1;//构建绕XYZ旋转顺序的旋转矩阵,从右到左Eigen::Matrix3d rxyz = Matrix_rz*Matrix_ry*Matrix_rx;//构建4*4的旋转矩阵Eigen::Matrix4d xyzdelt;xyzdelt << rxyz(0, 0), rxyz(0, 1), rxyz(0, 2), pos.x,rxyz(1, 0), rxyz(1, 1), rxyz(1, 2), pos.y,rxyz(2, 0), rxyz(2, 1), rxyz(2, 2), pos.z,0, 0, 0, 1;return xyzdelt;
}
Eigen::Matrix4d ABBPosToMatrix4d(Pos pos) {double w = pos.u;double x = pos.v;double y = pos.w;double z = pos.q;//构建旋转矩阵double a00 = 1.0 - 2.0 * y * y - 2.0 * z * z;double a01 = 2.0 * x * y - 2.0 * w * z;double a02 = 2.0 * x * z + 2.0 * w * y;double a10 = 2.0 * x * y + 2.0 * w * z;double a11 = 1.0 - 2.0 * x * x - 2.0 * z * z;double a12 = 2.0 * y * z - 2.0 * w * x;double a20 = 2.0 * x * z - 2.0 * w * y;double a21 = 2.0 * y * z + 2.0 * w * x;double a22 = 1.0 - 2.0 * x * x - 2.0 * y * y;Eigen::Matrix4d xyzdelt;xyzdelt << a00, a01, a02, pos.x,a10, a11, a12, pos.y,a20, a21, a22, pos.z,0, 0, 0, 1;return xyzdelt;
}
int main() {//机器人位姿//std::vector<Pos>BasePos;//BasePos.resize(8);//BasePos[0].x = 1092.010; BasePos[0].y = 126.6960; BasePos[0].z = 117.653; BasePos[0].u = 36.310; BasePos[0].v = -1.360; BasePos[0].w = -90.776;//BasePos[1].x = 1146.710; BasePos[1].y = 102.007; BasePos[1].z = 151.027; BasePos[1].u = 20.003; BasePos[1].v = 23.049; BasePos[1].w = -100.17;//BasePos[2].x = 1097.360; BasePos[2].y = 78.745; BasePos[2].z = 188.125; BasePos[2].u = 10.537; BasePos[2].v = 37.359; BasePos[2].w = -105.62;//BasePos[3].x = 1097.310; BasePos[3].y = 26.030; BasePos[3].z = 212.116; BasePos[3].u = 13.159; BasePos[3].v = 53.515; BasePos[3].w = -121.90;//BasePos[4].x = 1097.170; BasePos[4].y = 127.129; BasePos[4].z = 61.216; BasePos[4].u = 24.756; BasePos[4].v = -33.425; BasePos[4].w = -82.095;//BasePos[5].x = 1096.990; BasePos[5].y = 106.164; BasePos[5].z = 23.957; BasePos[5].u = 31.359; BasePos[5].v = -53.551; BasePos[5].w = -77.848;//BasePos[6].x = 1062.870; BasePos[6].y = 110.995; BasePos[6].z = 48.866; BasePos[6].u = 5.284; BasePos[6].v = -55.024; BasePos[6].w = -48.495;//BasePos[7].x = 1071.050; BasePos[7].y = 111.934; BasePos[7].z = 24.186; BasePos[7].u = 22.083; BasePos[7].v = -53.473; BasePos[7].w = -84.305;////相机坐标//std::vector<Eigen::Vector3d>src;//src.push_back(Eigen::Vector3d(5.207, 3.058, 212.184));//src.push_back(Eigen::Vector3d(-14.651, 3.058, 187.370));//src.push_back(Eigen::Vector3d(33.843, 3.058, 223.949));//src.push_back(Eigen::Vector3d(-3.927, 3.058, 257.037));//src.push_back(Eigen::Vector3d(4.244, 3.058, 195.328));//src.push_back(Eigen::Vector3d(-1.143, 3.058, 189.084));//src.push_back(Eigen::Vector3d(-6.016, 3.058, 218.697));//src.push_back(Eigen::Vector3d(37.936, 3.058, 200.879));////世界坐标//Eigen::Vector4d Points(1156.62, 36.5753, 73.0081, 1.0);std::vector<Pos>BasePos;BasePos.resize(10);BasePos[0].x = 611.927; BasePos[0].y = -337.748; BasePos[0].z = 444.929; BasePos[0].u = -179.9914; BasePos[0].v = -29.6960; BasePos[0].w = -0.0045;BasePos[1].x = 612.985; BasePos[1].y = -318.608; BasePos[1].z = 450.754; BasePos[1].u = -179.9907; BasePos[1].v = -29.6987; BasePos[1].w = -0.0065;BasePos[2].x = 607.345; BasePos[2].y = -380.231; BasePos[2].z = 444.163; BasePos[2].u = -174.5669; BasePos[2].v = -29.8622; BasePos[2].w = -6.3606;BasePos[3].x = 516.088; BasePos[3].y = -229.873; BasePos[3].z = 393.906; BasePos[3].u = 174.6992;  BasePos[3].v = -46.6999; BasePos[3].w = -16.7722;BasePos[4].x = 811.703; BasePos[4].y = -280.223; BasePos[4].z = 473.294; BasePos[4].u = 173.4753;  BasePos[4].v = -0.7399;  BasePos[4].w = 20.8908;BasePos[5].x = 681.667; BasePos[5].y = -594.331; BasePos[5].z = 364.829; BasePos[5].u = -145.8659; BasePos[5].v = 10.2829;  BasePos[5].w = -42.5905;BasePos[6].x = 903.080; BasePos[6].y = -147.626; BasePos[6].z = 400.438; BasePos[6].u = 168.824;   BasePos[6].v = 24.2405;  BasePos[6].w = 32.2090;BasePos[7].x = 530.374; BasePos[7].y = -144.912; BasePos[7].z = 338.787; BasePos[7].u = 134.6953;  BasePos[7].v = -16.2830; BasePos[7].w = 46.1090;BasePos[8].x = 423.499; BasePos[8].y = -305.380; BasePos[8].z = 316.866; BasePos[8].u = -133.0162; BasePos[8].v = -32.3433; BasePos[8].w = -77.3643;BasePos[9].x = 428.198; BasePos[9].y = -175.619; BasePos[9].z = 272.064; BasePos[9].u = 142.1562;  BasePos[9].v = -58.9630; BasePos[9].w = 20.7953;std::vector<Eigen::Vector3d>src;src.push_back(Eigen::Vector3d(-1.2943, 0, 135.1660));src.push_back(Eigen::Vector3d(18.8565, 0, 141.0535));src.push_back(Eigen::Vector3d(-22.1264, 0, 139.9686));src.push_back(Eigen::Vector3d(-20.0656, 0, 156.3136));src.push_back(Eigen::Vector3d(-5.4819, 0, 127.0132));src.push_back(Eigen::Vector3d(-24.2554, 0, 118.1987));src.push_back(Eigen::Vector3d(18.0071, 0, 99.6658));src.push_back(Eigen::Vector3d(9.9690, 0, 125.6090));src.push_back(Eigen::Vector3d(-18.7726, 0, 157.2780));src.push_back(Eigen::Vector3d(-2.5979, 0, 151.5664));//世界坐标Eigen::Vector4d Points(792.691, -330.187, 29.959, 1.0);//std::vector<Pos>BasePos;//BasePos.resize(4);//BasePos[0].x = 1431.49; BasePos[0].y = 253.30; BasePos[0].z = 676.29; BasePos[0].u = 0.528101; BasePos[0].v = -0.391551; BasePos[0].w = 0.574547;	 BasePos[0].q = -0.487538;//BasePos[1].x = 1444.86; BasePos[1].y = 213.27; BasePos[1].z = 723.87; BasePos[1].u = 0.580717; BasePos[1].v = -0.308164; BasePos[1].w = 0.495077;	 BasePos[1].q = -0.568068;//BasePos[2].x = 1396.88; BasePos[2].y = 271.10; BasePos[2].z = 624.55; BasePos[2].u = 0.445193; BasePos[2].v = -0.483734; BasePos[2].w = 0.655849;	 BasePos[2].q = -0.371036;//BasePos[3].x = 1424.70; BasePos[3].y = 216.22; BasePos[3].z = 802.07; BasePos[3].u = 0.632536; BasePos[3].v = -0.236723; BasePos[3].w = 0.653778;	 BasePos[3].q = -0.341225;//std::vector<Eigen::Vector3d>src;//src.push_back(Eigen::Vector3d(46.575, 0, -19.748));//src.push_back(Eigen::Vector3d(43.3, 0, 6.772));//src.push_back(Eigen::Vector3d(51.675, 0, -19.808));//src.push_back(Eigen::Vector3d(56.025, 0, -5.94));////世界坐标//Eigen::Vector4d Points(1216.45, 66.48, 689.80, 1.0);int n = BasePos.size(); // 位姿数量std::vector<Eigen::Matrix4d>B;B.resize(n);for (int i = 0; i < n; i++) {	//正常XYZ旋转的机器人B[i] = PosToMatrix4d(BasePos[i]);//ABB机器人//B[i] = ABBPosToMatrix4d(BasePos[i]);}Eigen::Matrix3d TT;MatrixXd AAA, BBB;AAA.resize(3, n);BBB.resize(3, n);for (int i = 0; i < n; i++) {AAA(0, i) = src[i][0];AAA(1, i) = src[i][2];AAA(2, i) = 1;Eigen::Vector4d XYZ = B[i].inverse()*Points;BBB(0, i) = XYZ[0];BBB(1, i) = XYZ[1];BBB(2, i) = XYZ[2];}TT = BBB * AAA.transpose()*(AAA * AAA.transpose()).inverse();Eigen::Vector3d r1(TT(0, 0), TT(1, 0), TT(2, 0));Eigen::Vector3d r3(TT(0, 1), TT(1, 1), TT(2, 1));r1.normalize();r3.normalize();Eigen::Vector3d r2 = r3.cross(r1);std::cout << "r1: " << r1 << std::endl;std::cout << "r2: " << r2 << std::endl;std::cout << "r3: " << r3 << std::endl;Eigen::Matrix4d RT;RT << r1[0], r2[0], r3[0], TT(0,2),r1[1], r2[1], r3[1], TT(1, 2),r1[2], r2[2], r3[2], TT(2, 2),0, 0, 0, 1;std::cout << "RT :\n" << RT << std::endl;return 0;
}

文章转载自:

http://RJkcXmSY.kxnxf.cn
http://EfhD0ifG.kxnxf.cn
http://9ZOnAwcC.kxnxf.cn
http://rF9SB67t.kxnxf.cn
http://iNxB0MjR.kxnxf.cn
http://cxaq1ZnD.kxnxf.cn
http://wYWXAiDf.kxnxf.cn
http://8s3Ht7iI.kxnxf.cn
http://X4pVNHc3.kxnxf.cn
http://f5h2NAPz.kxnxf.cn
http://BUIH0oz0.kxnxf.cn
http://tmzSfyrX.kxnxf.cn
http://nrHz72G1.kxnxf.cn
http://OSfX297N.kxnxf.cn
http://FTKFRJg0.kxnxf.cn
http://xGwCrgLn.kxnxf.cn
http://LkIVQ7IE.kxnxf.cn
http://BQMCDdUj.kxnxf.cn
http://O6mlUY60.kxnxf.cn
http://1CLJ3rMs.kxnxf.cn
http://Ncp0fazn.kxnxf.cn
http://qtf5HSgg.kxnxf.cn
http://lxzMlAFb.kxnxf.cn
http://pVD3OLV5.kxnxf.cn
http://H1uZbsQp.kxnxf.cn
http://YXFzJV8Z.kxnxf.cn
http://FsawGFg6.kxnxf.cn
http://PNMsAm6w.kxnxf.cn
http://tldtgHwS.kxnxf.cn
http://bSMrSnHc.kxnxf.cn
http://www.dtcms.com/a/387561.html

相关文章:

  • QML学习笔记(一)基本了解和工程配置
  • 大数据毕业设计选题推荐-基于大数据的牛油果数据可视化分析系统-Hadoop-Spark-数据可视化-BigData
  • Hadoop单机模式下运行grep实例,output文件目录不存在
  • 【docker】清理中断构建后产生的镜像和缓存
  • Vue2项目集成打包分析工具webpack-bundle-analyzer
  • 【阶梯波发生器如何控制电压和周期】2022-12-9
  • Java 设计模式之桥接模式(Bridge Pattern)
  • Android 端启动 HTTP 服务:从基础实现到实战应用
  • 《2D横版平台跳跃游戏中角色二段跳失效与碰撞体穿透的耦合性Bug解析》
  • 基于本机知识库 + 豆包(火山引擎)+ MCP的落地方案
  • OpenCV 风格迁移、DNN模块 案例解析及实现
  • php实现火山引擎 【双向流式websocket-V3-支持复刻2.0/混音mix】开箱即用,可用于各种PHP框架。
  • 【lua】Windows环境下cffi-lua使用指南:编译、安装与测试
  • 我优化了昨天的C++/Lua插件系统:添加了插件沙箱、Lua 状态池
  • 【数据库】SQLite安装部署与使用指南
  • Android Kotlin 请求方法代码
  • 【easy_tools】一个跨平台裸机工具库,包含任务/堆栈/消息/定时器/日志等实现
  • ARM(11) - LM75
  • FPGA实现SRIO数据回环传输,基于Serial Rapidlo Gen2架构,提供6套工程源码和技术支持
  • 第十九章 Arm C1-Premium TRBE技术解析
  • HTB writeup
  • 科学研究系统性思维的理论基础:数字化研究工具
  • 基于有限元-元胞自动机法(CAFE)的增材制造过程组织模拟
  • 电视行业复兴,数字化制造如何重塑“视界”新格局?
  • 从兼容到极致性能——qData数据中台商业版核心指标解读
  • MAC-枚举反射工具类
  • 搜索百科(1):Lucene —— 打开现代搜索世界的第一扇门
  • 学习日记-JS+DOM-day57-9.17
  • Java异常处理最佳实践指南
  • Ansible简介