基于FANUC工业机器人的坐标系转换、多视角拼接与三维重建

news2025/1/16 4:03:29

0.简介

  • 总体任务:机械臂末端安装三维相机,绕着工件进行拍摄,并在计算机中将每次拍摄的点云合并在同一个坐标系下,从而获得更加完整全面的点云。
  • 机械臂:FANAUC
  • 相机:梅卡曼德
  • 技术方案:使用相机外参、机械臂位姿进行坐标系转换,将不同视角点云坐标系都转换到机器人基座(基于ICP或深度学习的点云匹配也可以实现类似效果,但实际应用鲁棒性不够,对视角间点云的重叠度要求较高或是难以获取相似度高的大量训练数据)

1.多位姿点云拍摄与读取

  • 机械臂移动拍摄工件不同视角,记录下每个视角的点云、机械臂位姿
  • 相关数据
  • 在这里插入图片描述
  • 点云读取及简单处理可以参考这里
  • 注意:梅卡曼德拍摄后点云的xyz坐标单位是m,而FANUC等转换矩阵的单位是mm,所以要注意单位统一
pcd = o3d.io.read_point_cloud('datasFANUC/' + 'point_cloud_00000' + '.ply')
pcd_np = np.array(pcd.points) * 1000
pcd.points = o3d.utility.Vector3dVector(pcd_np)
pcd.paint_uniform_color([0, 1.0, 0])
o3d.visualization.draw_geometries([pcd])

在这里插入图片描述

2.手眼标定的外参转换矩阵

  • 通过梅卡曼德官方软件与FANUC配合完成相机内外参的标定,外参标定结果如下(使用和FANUC相同的表示方式):
x,y,z,w,p,r = 70.5, 269.927, -33.947, -0.85, 0.78, 147.95

3.FANUC机械臂转换矩阵

  • FANUC工业机器人采用的是定角旋转方式,法兰(机械臂末端坐标系)到机械臂基座坐的相对位置由X- Y-Z,W-P-R表示,W表示绕X轴旋转的角度;P表示绕Y轴旋转的角度;R表示绕Z轴旋转的角度;它们的旋转方向由右手定则判断。(参考资料)
  • 定角旋转:定角旋转与欧拉角旋转类似,只不过定角旋转的转轴一直是最原始的坐标系转轴(该轴不随着旋转而变化)
  • 三次绕固定轴旋转(定角旋转)的最终姿态和以相反顺序三次绕运动坐标轴旋转(欧拉角旋转)的最终姿态相同(机器人学——姿态描述方法(欧拉角,固定角,D-H法,绕定轴旋转))
  • 机器人默认显示旋转顺序是WPR,即依次绕XYZ轴进行旋转,对应的旋转矩阵为RZRYRX
  • 绕定轴旋转的相关程序如下:
'''
旋转矩阵相关
'''
def Rx(theta):
    '''
    绕X轴旋转矩阵
    '''
    RX = np.array([[1, 0, 0],
                   [0, math.cos(theta), -1*math.sin(theta)],
                   [0, math.sin(theta), math.cos(theta)]])
    return RX
def Ry(theta):
    '''
    绕Y轴旋转矩阵
    '''
    RY = np.array([[math.cos(theta), 0, math.sin(theta)],
                   [0, 1, 0],
                   [-1*math.sin(theta), 0, math.cos(theta)]])
    return RY
def Rz(theta):
    '''
    绕Z轴旋转矩阵
    '''
    RZ = np.array([[math.cos(theta), -1*math.sin(theta), 0],
                   [math.sin(theta), math.cos(theta), 0],
                   [0, 0, 1]])
    return RZ

  • FANUC机械臂转换矩阵相关程序:
'''
机械臂转换矩阵相关
'''
def WPR2R(w,p,r):
    '''
    定角旋转WPR转为旋转矩阵(角度)
    '''
    R = Rz(r/180*math.pi)@Ry(p/180*math.pi)@Rx(w/180*math.pi)
    return R
def FANUC2trans(x,y,z,w,p,r):
    '''
    FANUC转换矩阵(mm,角度,wpr)
    '''
    t = np.zeros((4, 4))
    t[0:3, 0:3] = WPR2R(w,p,r)
    t[:,3] = x,y,z,1
    t[3,0:3] = 0,0,0
    return t

4.多视角拼接

  • 初始化一个点云pcd_all用来保存完成坐标变换后的点云
  • 为了与转换矩阵相匹配,为点云添加一列:
pcd_np = np.concatenate((pcd_np, np.expand_dims(np.ones(pcd_np.shape[0]), axis=1)), axis=1)
  • 点云左乘相机外参转换矩阵,再左乘机械臂转换矩阵,即得到点云在机械臂基座的坐标
  • 最终效果:
    在这里插入图片描述在这里插入图片描述

5.完整代码

import numpy as np
import math
import open3d as o3d
from Transformations import xyzrpw_to_H

'''
旋转矩阵相关
'''
def Rx(theta):
    '''
    绕X轴旋转矩阵
    '''
    RX = np.array([[1, 0, 0],
                   [0, math.cos(theta), -1*math.sin(theta)],
                   [0, math.sin(theta), math.cos(theta)]])
    return RX
def Ry(theta):
    '''
    绕Y轴旋转矩阵
    '''
    RY = np.array([[math.cos(theta), 0, math.sin(theta)],
                   [0, 1, 0],
                   [-1*math.sin(theta), 0, math.cos(theta)]])
    return RY
def Rz(theta):
    '''
    绕Z轴旋转矩阵
    '''
    RZ = np.array([[math.cos(theta), -1*math.sin(theta), 0],
                   [math.sin(theta), math.cos(theta), 0],
                   [0, 0, 1]])
    return RZ

'''
机械臂转换矩阵相关
'''
def WPR2R(w,p,r):
    '''
    定角旋转WPR转为旋转矩阵(角度)
    '''
    R = Rz(r/180*math.pi)@Ry(p/180*math.pi)@Rx(w/180*math.pi)
    return R
def FANUC2trans(x,y,z,w,p,r):
    '''
    FANUC转换矩阵(mm,角度,wpr)
    '''
    t = np.zeros((4, 4))
    t[0:3, 0:3] = WPR2R(w,p,r)
    t[:,3] = x,y,z,1
    t[3,0:3] = 0,0,0
    return t

'''
相机外参
'''
def get_waican(x,y,z,rx,ry,rz):
    '''
    获取外参转换矩阵,mm,角度,欧拉角xyz
    '''
    R = Rx(rx/180*math.pi)@Ry(ry/180*math.pi)@Rz(rz/180*math.pi)
    t = np.zeros((4, 4))
    t[0:3, 0:3] = R
    t[:, 3] = x, y, z, 1
    t[3, 0:3] = 0, 0, 0
    return t

if __name__ == '__main__':

    root = 'datasFANUC'

    #外参转换矩阵
    # t_waican = get_waican(70.609, 264.977, -33.942, -0.80, 0.73, 147.95+10)
    t_waican = FANUC2trans(70.5, 269.927, -33.947, -0.85, 0.78, 147.95)
    print(t_waican)
    #t_waican = xyzrpw_to_H(np.array([70.5, 269.927, -33.947, -0.85, 0.78, 147.95]))
    print('外参转换矩阵:')
    print(t_waican)

    '''
    文本文件读取机械臂姿态
    索引,文件名(无后缀),X,Y,Z(mm),W,P,R(角度)
    '''
    flag = 1
    robot_data = root + '/datas.txt'
    f = open(robot_data)
    for line in f.readlines():
        line = line.strip('\n').split(',')
        print('索引:', line[0])
        file_data = line[1]
        print('处理文件:', file_data)
        # 转换矩阵
        line = line[2:]
        line = [float(i) for i in line]
        x,y,z,w,p,r = line
        t_robot = FANUC2trans(x,y,z,w,p,r)
        #print('机器人转换矩阵:', t_robot)
        #t_robot = xyzrpw_to_H(np.array([x,y,z,w,p,r]))
        t = t_robot@t_waican
        print(t)

        # 读取点云
        pcd = o3d.io.read_point_cloud(root + '/' + file_data + '.ply')
        pcd.paint_uniform_color([1.0, 0, 0])
        # o3d.visualization.draw_geometries([pcd])
        pcd_np = np.array(pcd.points) * 1000
        pcd_np = np.concatenate((pcd_np, np.expand_dims(np.ones(pcd_np.shape[0]), axis=1)), axis=1)
        #print(pcd_np.shape)
        pcd_np = pcd_np.T
        pcd_np = t@pcd_np
        pcd_np = pcd_np.T



        # 使用第一个点云初始化最终点云
        if flag:
            pcd_all = pcd_np
            flag = 0
        else:
            pcd_all = np.concatenate((pcd_all, pcd_np))
            #print(pcd_all.shape)
            #print(pcd_all)
            #break

    pcd_show = o3d.geometry.PointCloud()
    pcd_show.points = o3d.utility.Vector3dVector(pcd_all[:, :3])
    pcd_show.paint_uniform_color([1.0, 0, 0])

    # zero = o3d.geometry.PointCloud()
    # zero.points = o3d.utility.Vector3dVector(np.zeros((1, 3)))
    # zero.paint_uniform_color([0, 1.0, 0])

    #pcd = o3d.io.read_point_cloud('datasFANUC/' + 'point_cloud_00000' + '.ply')
    #pcd_np = np.array(pcd.points) * 1000
    #pcd.points = o3d.utility.Vector3dVector(pcd_np)
    #pcd.paint_uniform_color([0, 1.0, 0])
    o3d.visualization.draw_geometries([pcd_show])
    # zero,

6.调包获得转换矩阵

  • Transformations下载地址
from Transformations import xyzrpw_to_H
t_waican = xyzrpw_to_H(np.array([70.5, 269.927, -33.947, -0.85, 0.78, 147.95]))
  • 注意:包里使用的是欧拉角旋转,计算顺序和FANUC的定角旋转是相反的,所以使用的是xyzrpw_to_H函数而不是xyzwpr_to_H

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

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

相关文章

有趣的 TCP 抢带宽行为

昨天发了一篇 非技术文章,很多人找我讨论,浓缩成一句话,就是 “死道友而不死贫道”,我的简历上写着这些把戏能带来什么,我的 blog 上写着这么做是多么无耻,哈哈。 看看共享链路上如何挤占带宽: …

Mysql数据库 13.SQL语言 触发器

一、触发器&#xff08;操作日志表&#xff09; 1.介绍 不需要主动调用的一种储存过程&#xff0c;是一个能够完成特定过程&#xff0c;存储在数据库服务器上的SQL片段。 对当前表中数据增删改查的一种记录<日志表>&#xff0c;根据触发器自动执行&#xff0c;记录当前…

html+css+javascript打造网页内容浮动导航菜单

1需求分析 前段时间把“圳品”信息发布到网站上了&#xff0c;内容包括四大块&#xff1a; 按分布区域统计分析按产品类别统计分析按认定时间统计分析河池市“圳品”清单 导致网页很长&#xff0c;有同事反映说查看起来不是很方便&#xff0c;于是决定加上一个网页内容浮动导…

x3daudio1_7.dll怎么解决?x3daudio1_7.dll丢失的5个详细处理方法

首先&#xff0c;让我们来了解一下X3DAudio1_7.dll丢失的原因。X3DAudio1_7.dll是一个非常重要的动态链接库文件&#xff0c;它负责处理计算机中的音频输出。然而&#xff0c;由于各种原因&#xff0c;例如软件安装错误、病毒感染、系统升级等&#xff0c;我们可能会遇到X3DAud…

C语言--从键盘输入当月利润I,求应发奖金总数。

题目描述&#xff1a; 企业发放的奖金根据利润提成。利润I低于或等于100000元的&#xff0c;奖金可提成10%; 利润高于100000 元&#xff0c;低于200000元(1000001000000时&#xff0c;超过1000000元的部分按 1%提成。从键盘输入当月利润I,求应发奖金总数。 int main() {int m…

自动驾驶学习笔记(八)——路线规划

#Apollo开发者# 学习课程的传送门如下&#xff0c;当您也准备学习自动驾驶时&#xff0c;可以和我一同前往&#xff1a; 《自动驾驶新人之旅》免费课程—> 传送门 《Apollo Beta宣讲和线下沙龙》免费报名—>传送门 文章目录 前言 路线规划 路由元素 路径搜索 最优…

【C++】函数指针 ① ( 函数三要素 | 函数类型 | 函数指针类型 | 函数类型重命名 )

文章目录 一、函数类型 和 函数指针类型1、函数三要素2、函数类型3、函数指针类型4、函数类型重命名 二、代码示例 - 函数类型重命名1、代码分析2、完整代码示例 一、函数类型 和 函数指针类型 1、函数三要素 函数原型有三个重要要素 : 函数名称 : 使用 标识符 为函数命名 ; 用…

Fortran 中的指针

Fortran 中的指针 指针可以看作一种数据类型 指针存储与之关联的数据的内存地址变量指针&#xff1a;指向变量数组指针&#xff1a;指向数组过程指针&#xff1a;指向函数或子程序指针状态 未定义未关联 integer, pointer::p1>null() !或者 nullify(p1) 已关联 指针操作 指…

SpringBoot项目调用openCV报错:nested exception is java.lang.UnsatisfiedLinkError

今天在通过web项目调用openCV的时候提示如下错误&#xff1a; nested exception is java.lang.UnsatisfiedLinkError:org.opencv.imgcodecs.Imgcodecs.imread_0(Ljava/la如下图所示&#xff1a; 但是通过直接启动java main函数确正常&#xff0c;初步诊断和SpringBoot热加载…

文件管理技巧:按文件容量大小分类,自动移动至目标文件夹的方法

按文件容量大小分类可以帮助快速识别和筛选出不同大小的文件。这样做有很多好处。首先&#xff0c;可以轻松地查找和访问特定大小的文件&#xff0c;提高工作效率。其次&#xff0c;通过将不同大小的文件分类&#xff0c;可以更好地了解和掌控文件的使用情况&#xff0c;避免存…

leetcode刷题日记:110. Balanced Binary Tree(平衡二叉树)

题目给了我们一个二叉树要让我们来判断这一个二叉树是不是平衡二叉树。 要想判断一棵树是不是平衡二叉树&#xff0c;我们得首先知道平衡二叉树的定义是什么&#xff0c;平衡二叉树指的是这样的树它的左子树的高度与右子树高度的差的绝对值不能超过1&#xff0c;而且它的左子树…

MySQL 数据库查询与数据操作:使用 ORDER BY 排序和 DELETE 删除记录

使用 ORDER BY 进行排序 使用 ORDER BY 语句按升序或降序对结果进行排序。 ORDER BY 关键字默认按升序排序。要按降序排序结果&#xff0c;使用 DESC 关键字。 示例按名称按字母顺序排序结果&#xff1a; import mysql.connectormydb mysql.connector.connect(host"l…

gorm之项目实战-使用gen以及定义表间关系

gorm之项目实战 ER图 关系整理 一对一关系&#xff1a; User 和 UserLog&#xff1a; 一个用户对应一个用户日志&#xff0c;通过 User 模型的主键与 UserLog 模型的外键建立一对一关系。 一对多关系&#xff1a; User 和 Teacher&#xff1a; 一个用户可以对应多个老师&…

电路中模拟地和数字地的分割方法

电路中只要是地&#xff0c;最终都要接到一起&#xff0c;然后入大地。如果不接在一起就是“浮地”&#xff0c;存在压差&#xff0c;容易积累电荷&#xff0c;造成静电。 地是参考0电位&#xff0c;所有电压都是参考地得出的&#xff0c;地的标准一致&#xff0c;故各种地应短…

rasa train nlu详解:1.1-train_nlu()函数

本文使用《使用ResponseSelector实现校园招聘FAQ机器人》中的例子&#xff0c;主要详解介绍train_nlu()函数中变量的具体值。 一.rasa/model_training.py/train_nlu()函数   train_nlu()函数实现&#xff0c;如下所示&#xff1a; def train_nlu(config: Text,nlu_data: Op…

transformers安装避坑

1.4 下载rust编辑器 看到这里你肯定会疑惑了&#xff0c;我们不是要用python的吗&#xff1f; 这个我也不知道&#xff0c;你下了就对了&#xff0c;不然后面的transformers无法安装 因为是windows到官网选择推荐的下载方式https://www.rust-lang.org/tools/install。 执行文…

使用ResponseSelector实现校园招聘FAQ机器人

本文主要介绍使用ResponseSelector实现校园招聘FAQ机器人&#xff0c;回答面试流程和面试结果查询的FAQ问题。FAQ机器人功能分为业务无关的功能和业务相关的功能2类。 一.data/nlu.yml文件   与普通意图相比&#xff0c;ResponseSelector训练数据中的意图采用group/intent格…

【Armstrong公理】【求闭包和候选码】【判断范式】

1. Armstrong公理 2.求闭包和候选码 3.判断范式

WebSphere Liberty 8.5.5.9 (二)

WebSphere Liberty 8.5.5.9 &#xff08;二&#xff09; encode and decode Pre WebSphere Liberty 8.5.5.9 xor and AES 提取 D:\wlp-webProfile7-java8-8.5.5.9\wlp\lib 下必要加解密包 com.ibm.ws.crypto.certificateutil_1.0.12.jar com.ibm.ws.crypto.passwordutil_1…

2023 年合成数据的用例和应用

合成数据&#xff0c;也称为人工生成的数据&#xff0c;为数据科学应用中经常遇到的问题&#xff08;例如数据隐私和小数据量&#xff09;提供了解决方案。我们列出了不同行业和部门/业务单位中合成数据的功能和最常见的用例。 合成数据支持哪些与行业无关的用例/功能&#xf…