apriltag使用之三:机械臂与Apriltag码对齐

2023-12-02 01:08

本文主要是介绍apriltag使用之三:机械臂与Apriltag码对齐,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

相机到Apriltag的对齐

上面是一个简单的手画的关于机器人、摄像头以及Apriltag码之间的位置关系图。机器人的基坐标系{s}由 ( x s , y s , z s , t s ) (\mathbf{x_s,y_s,z_s,t_s}) (xs,ys,zs,ts)表示,摄像头的初始位置的坐标系{c}由 ( x c , y c , z c , t c ) (\mathbf{x_c,y_c,z_c,t_c}) (xc,yc,zc,tc)表示,apriltag码的坐标系{a}由 ( x a , y a , z a , t a ) \mathbf{(x_a,y_a,z_a,t_a)} (xa,ya,za,ta)表示。其中 t \mathbf{t} t是各坐标系原点相对于全局参考坐标系中的位置, x , y , z \mathbf{x,y,z} x,y,z则是各坐标系的坐标轴单位向量在全局参考坐标系的表示,这也形成各坐标系相对于全局参考坐标系的旋转矩阵 R = [ x , y , z ] \mathbf{R=[x,y,z]} R=[x,y,z]。同时我们使用坐上标表示父坐标系,右下标表示子坐标系,例如相机坐标系的旋转矩阵为 s R c = [ s x c , s y c , s z c ] ^s\mathbf{R}_c=[^s\mathbf{x}_c,^s\mathbf{y}_c,^s\mathbf{z}_c] sRc=[sxc,syc,szc]

通常我们可以将全局参考坐标系就设在机器人的基坐标系,因此有
t s = ( 0 , 0 , 0 ) T x s = ( 1 , 0 , 0 ) T y s = ( 0 , 1 , 0 ) T z s = ( 0 , 0 , 1 ) T \begin{aligned} \mathbf{t}_s=(0,0,0)^T \\ \mathbf{x}_s=(1,0,0)^T \\ \mathbf{y}_s=(0,1,0)^T \\ \mathbf{z}_s=(0,0,1)^T \\ \end{aligned} ts=(0,0,0)Txs=(1,0,0)Tys=(0,1,0)Tzs=(0,0,1)T

通过Apriltag检测,我们知道了Apriltag在摄像头坐标系{c}下的方位为 [ c R a ∣ c t a ] [^c\mathbf{R}_a|^c\mathbf{t}_a] [cRacta],目标是:获得相机坐标系相对与全局参考坐标系的方位 [ s R c ′ ∣ s t c ′ ] [^s\mathbf{R}_{c'}|^s\mathbf{t}_{c'}] [sRcstc],使得相机在保持相同距离的情况下视角正对于Apriltag码。

从上图中我们可知,新的相机是与Apriltag码的方向相同的,既有 s R c ′ = s R a ^s\mathbf{R}_{c'}=^s\mathbf{R}_a sRc=sRa。而我们肯定是知道运动中的机器人末端的相机在全局坐标系下的方位的,即 [ s R c ∣ s t c ] [^s\mathbf{R}_c|^s\mathbf{t}_c] [sRcstc]。从相机坐标到全局参考坐标的变换为:
s T c = [ s R c s t c 0 1 ] ^s\mathbf{T}_c=\left[\begin{array}{ccc} ^s\mathbf{R}_c & ^s\mathbf{t}_c \\ 0 & 1 \end{array}\right] sTc=[sRc0stc1]
Apriltag码在全局坐标系中的位置为 s T c c t a ^s\mathbf{T}_c ^c\mathbf{t}_a sTccta。所以Apriltag码在全局参考坐标系下的方位就为 [ s R a ∣ s t a ] = [ s R c ⋅ c R a ∣ s T c ⋅ c t a ] [^s\mathbf{R}_a|^s\mathbf{t}_a]=[^s\mathbf{R}_c\cdot ^c\mathbf{R}_a|^s\mathbf{T}_c\cdot^c\mathbf{t}_a] [sRasta]=[sRccRasTccta]。另外可以得出Apriltag码坐标系到全局坐标系的 s T a ^s\mathbf{T}_a sTa为:
s T a = [ s R a s t a 0 1 ] = [ s R c ⋅ c R a s T c ⋅ c t a 0 1 ] ^s\mathbf{T}_a=\left[\begin{array}{ccc} ^s\mathbf{R}_a & ^s\mathbf{t}_a \\ 0 & 1 \end{array}\right] = \left[\begin{array}{ccc} ^s\mathbf{R}_c\cdot ^c\mathbf{R}_a & ^s\mathbf{T}_c\cdot^c\mathbf{t}_a \\ 0 & 1 \end{array}\right] sTa=[sRa0sta1]=[sRccRa0sTccta1]

然后需要确定的就是新的相机位置 s t c ^s\mathbf{t}_c stc,该位置其实在Apriltag码的坐标系下是 a t c = ( 0 , 0 , − ∣ ∣ c t a ∣ ∣ ) ^a\mathbf{t}_c=(0,0,-||^c\mathbf{t}_a||) atc=(0,0,cta),将其通过 s T a ^s\mathbf{T}_a sTa变换到全局坐标系下即可,即$^s\mathbf{t}_c = s\mathbf{T}_a\cdota\mathbf{t}_c $。

关于摄像头在机械臂上的位置

一般情况下,摄像头都是安装在机械臂的末端执行器的一侧的,所以就存在一个 t T c ^t\mathbf{T}_c tTc表示相机到机械臂末端的位置变换,并且有 s T c = s T t ⋅ t T c ^s\mathbf{T}_c=^s\mathbf{T}_t\cdot^t\mathbf{T}_c sTc=sTttTc。因此当新的 s T c ^s\mathbf{T}_c sTc计算出来后,还要计算 s T t = s T c ⋅ t T c − 1 ^s\mathbf{T}_t=^s\mathbf{T}_c\cdot ^t\mathbf{T}^{-1}_c sTt=sTctTc1来得到机械臂末端的方位,以便使用Moveit来控制机械臂运动。

代码

首先,我们假设以及执行了另外一个ROS节点在不断地检测摄像头图像中的Apriltag码,并将检测结果存放到名为"/apriltag/pose"的Topic中。

下面给出主要的执行代码。注意其中aligning函数执行了两次。因为第一次摄像头距离Apriltag码比较远,对方位的估计会比较粗糙,所以当第一次使得摄像头比较近时,再一次执行该操作可以使对齐更加精确。

import cv2
import numpy as np
import rospy
import moveit_commander
from geometry_msgs.msg import *
import tf_conversionsclass FindApriltag(object):def __init__(self):super(FindApriltag, self).__init__()group_name = "arm"  # 这是对机器人进行moveit配置定义的关节组名称self.group = moveit_commander.MoveGroupCommander(group_name)print("effector_link=", self.group.get_end_effector_link())self.pose_pub = rospy.Subscriber("/apriltag/pose", PoseArray, self.poseCB)self.poseArr =PoseArray()# 等待Topic订阅rospy.sleep(1)def poseCB(self, poseArr):self.poseArr = poseArrdef aligning(self):pose_st = self.group.get_current_pose()# 获得Apriltag相对于摄像头的方位for i in (0,1,2,3):if len(self.poseArr.poses)<1:print "There is no Apriltag"else:print "Apriltag found"breakrospy.sleep(0.1)pose_ca= self.poseArr.poses[0]# 计算机械臂末端到全局坐标系中的方位new_pose_st = self.compute_new_pose_st(pose_st.pose, pose_ca)# 运动到新位置pose_st.pose = new_pose_stself.move_to_pose(pose_st)def run(self):#1. 运动到指定位置,以便查找Apriltagpose_st = self.group.get_current_pose()print("cur pose_st: ", pose_st.pose)pose_st.pose.orientation.x = 1; pose_st.pose.orientation.y=0pose_st.pose.orientation.z = 0; pose_st.pose.orientation.w=0pose_st.pose.position.x = 0;  pose_st.pose.position.z =0.5pose_st.pose.position.y = 0.5self.move_to_pose(pose_st)self.aligning()self.aligning() # 再一次def main():rospy.init_node('find_apriltag', anonymous=True)app = FindApriltag()app.run()rospy.spin()returnif __name__ == '__main__':main()

实验

下面的动画显示了一个对齐的过程。其中将相机到Apriltag码的距离设置为0.15米。

这篇关于apriltag使用之三:机械臂与Apriltag码对齐的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/443382

相关文章

使用Python删除Excel中的行列和单元格示例详解

《使用Python删除Excel中的行列和单元格示例详解》在处理Excel数据时,删除不需要的行、列或单元格是一项常见且必要的操作,本文将使用Python脚本实现对Excel表格的高效自动化处理,感兴... 目录开发环境准备使用 python 删除 Excphpel 表格中的行删除特定行删除空白行删除含指定

深入理解Go语言中二维切片的使用

《深入理解Go语言中二维切片的使用》本文深入讲解了Go语言中二维切片的概念与应用,用于表示矩阵、表格等二维数据结构,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧... 目录引言二维切片的基本概念定义创建二维切片二维切片的操作访问元素修改元素遍历二维切片二维切片的动态调整追加行动态

prometheus如何使用pushgateway监控网路丢包

《prometheus如何使用pushgateway监控网路丢包》:本文主要介绍prometheus如何使用pushgateway监控网路丢包问题,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录监控网路丢包脚本数据图表总结监控网路丢包脚本[root@gtcq-gt-monitor-prome

Python通用唯一标识符模块uuid使用案例详解

《Python通用唯一标识符模块uuid使用案例详解》Pythonuuid模块用于生成128位全局唯一标识符,支持UUID1-5版本,适用于分布式系统、数据库主键等场景,需注意隐私、碰撞概率及存储优... 目录简介核心功能1. UUID版本2. UUID属性3. 命名空间使用场景1. 生成唯一标识符2. 数

SpringBoot中如何使用Assert进行断言校验

《SpringBoot中如何使用Assert进行断言校验》Java提供了内置的assert机制,而Spring框架也提供了更强大的Assert工具类来帮助开发者进行参数校验和状态检查,下... 目录前言一、Java 原生assert简介1.1 使用方式1.2 示例代码1.3 优缺点分析二、Spring Fr

Android kotlin中 Channel 和 Flow 的区别和选择使用场景分析

《Androidkotlin中Channel和Flow的区别和选择使用场景分析》Kotlin协程中,Flow是冷数据流,按需触发,适合响应式数据处理;Channel是热数据流,持续发送,支持... 目录一、基本概念界定FlowChannel二、核心特性对比数据生产触发条件生产与消费的关系背压处理机制生命周期

java使用protobuf-maven-plugin的插件编译proto文件详解

《java使用protobuf-maven-plugin的插件编译proto文件详解》:本文主要介绍java使用protobuf-maven-plugin的插件编译proto文件,具有很好的参考价... 目录protobuf文件作为数据传输和存储的协议主要介绍在Java使用maven编译proto文件的插件

SpringBoot线程池配置使用示例详解

《SpringBoot线程池配置使用示例详解》SpringBoot集成@Async注解,支持线程池参数配置(核心数、队列容量、拒绝策略等)及生命周期管理,结合监控与任务装饰器,提升异步处理效率与系统... 目录一、核心特性二、添加依赖三、参数详解四、配置线程池五、应用实践代码说明拒绝策略(Rejected

C++ Log4cpp跨平台日志库的使用小结

《C++Log4cpp跨平台日志库的使用小结》Log4cpp是c++类库,本文详细介绍了C++日志库log4cpp的使用方法,及设置日志输出格式和优先级,具有一定的参考价值,感兴趣的可以了解一下... 目录一、介绍1. log4cpp的日志方式2.设置日志输出的格式3. 设置日志的输出优先级二、Window

Ubuntu如何分配​​未使用的空间

《Ubuntu如何分配​​未使用的空间》Ubuntu磁盘空间不足,实际未分配空间8.2G因LVM卷组名称格式差异(双破折号误写)导致无法扩展,确认正确卷组名后,使用lvextend和resize2fs... 目录1:原因2:操作3:报错5:解决问题:确认卷组名称​6:再次操作7:验证扩展是否成功8:问题已解