SLAM从入门到精通(tf的使用)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】

        在ros的机器人学习过程中,有一件事情是肯定少不了的。那就是坐标系的转换。其实这也很容易理解。假设有一个机器人,它有一个3d camera、有一个机械手臂。这个时候有一个需求,需要通过3d camera告知物体的位置,然后通知另外一个机械手臂取走。

        看上去这个任务很简单,但是这中间就涉及到了坐标系的转换。比如说,摄像头识别到物体,这个时候物体是摄像头坐标系下的一个pose,需要通过robot1坐标系、全局坐标系的转换关系,进一步变成世界坐标系下的位置。这样,camera看到的物体pose,就有一个全局pose信息,虽然还是同一个物品。

        那么这个物体的位姿信息怎么通知到机械手臂呢?那么又需要进行反向的坐标系转换。即将物体的全局pose,转成robot2坐标系下的pose,然后进一步转换成机械手臂下的局部pose。这样机械手臂,就可以根据这个局部的pose信息,实现物体的抓取了。

1、tf的作用

        tf的主要作用,其实就是实现坐标系的转换。它的作用,有可能是局部坐标系到世界坐标系,也有可能是世界坐标系到局部坐标系。

2、tf的主要构成

        坐标系的主要沟通有两部分,一部分是xyz,一部分是围绕xyz坐标轴的旋转。其中xyz就是直接用浮点数表示,旋转一般用xyzw的四元数来表示。如果要转成rpy的角度转换,需要用tf提供的公式转换一下。

3、tf的计算方法

        目前计算的方法很多,不过主要还是利用矩阵计算,如果是计算一个点在另外一个坐标系下的坐标,过程中仅仅涉及到旋转的话,一般是

p1 = r * p0

        中间如果涉及到位移的话,一般还会添加一个offset,

p1=r*p0+offset

        这样的公式虽然比较简单,但是转成矩阵不太方便,我们可以通过补齐1来处理,

\begin{bmatrix} p1\\ 1 \end{bmatrix} = \begin{bmatrix} r & offset\\ 0& 1 \end{bmatrix} * \begin{bmatrix} p0\\ 1 \end{bmatrix}

        这样看上去公式完美一些了,可以进一步简化一下,

P1=R*P0

        公式上面似乎回到了原点,但是每一个变量的含义其实都发生了改变。当然,这里的R仅仅表示P0到P1的转变。很多人也许会问了,如果是P1到P0的转变,这个时候应该怎么处理呢。这个时候矩阵的优势就发挥出来了,

R^{-1} * P 1=P0

        所以,如果是需要P1坐标系下面的一个点,此时需要秀姐P0坐标系下的坐标,它所需要的就是求解旋转矩阵R的逆即可。有了单一的坐标转换,那么连续的坐标转换就变得容易了,

P\_final = Rn * ... * R1 * P\_start

        反之也是一样,

R1^{-1} *R2^{-1} * ... * Rn^{-1} * P\_final = P\_start

4、tf中的静态消息和动态消息

        坐标系转换中,有的是静态转换,有的是动态转换。所谓的静态转换,就是那种确定了之后,就一直不变化的。比如传感器和robot之间的固定位置。还有一种就是动态变换,它所指向的就是那种一直在变化的坐标映射关系,比如robot和map之间的位置转换关系。

5、amcl举例说明

        关于amcl算法,大家可以参考这个链接http://wiki.ros.org/amcl。在这中间就包含了大家想学习的tf信息。

        根据输入,它所以依赖的消息有scan雷达、tf坐标系转换、初始位置、map地图四个数据。第1、3、4都比较好理解。而第2个数据就和今天学习的知识相关,包含了lidar到robot、robot到odom的转换等。一开始的时候,我们还在寻找算法里面怎么没有里程计odom的数据,其实答案就在tf里面。

        经过算法求解,这个时候会输出三种数据,分别是amcl位姿、粒子云和tf。第1、2都比较好理解,还有一个tf数据。根据英文解释,它发布的就是map坐标系下odom的里程数据。

6、tf的编程范例

        了解一些tf的编程接口,也对我们理解和认识tf很有帮助。如果是tf的发布,一般会涉及到这样的接口,

tf::TransformBroadcaster
tf::Transform
tf::Quaternion

        相反,如果是一些接收的接口,也会有一些tf的数据结构,

tf::TransformListener
geometry_msgs::PointStamped

        为了说明如何使用这些代码,我们可以通过编写两个程序来说明一下。一个是book_tfpub.cpp,一个是book_tflis.cpp,前者的代码如下所示,

#include <iostream>
#include "ros/ros.h"
#include "tf/transform_broadcaster.h"
#include "geometry_msgs/Point.h"
#include "tf/tf.h"int main(int argc, char* argv[])
{ros::init(argc, argv, "tf_transformpub");ros::NodeHandle nh;static tf::TransformBroadcaster transfpub;tf::Transform base2laser;base2laser.setOrigin(tf::Vector3(1,0,0));tf::Quaternion q;q.setRPY(0,0,0);base2laser.setRotation(q);ros::Rate rate(10);while(nh.ok()){transfpub.sendTransform(tf::StampedTransform(base2laser, ros::Time::now(), "base_link", "laser_link"));rate.sleep();}return 0;
}

        后者的代码如下所示,


#include <iostream>
#include "ros/ros.h"
#include "tf/transform_listener.h"
#include "geometry_msgs/PointStamped.h"using namespace  std;int main(int argc, char* argv[])
{ros::init(argc, argv, "tf_transformlis");ros::NodeHandle nh;tf::TransformListener tflis;geometry_msgs::PointStamped plaser;plaser.header.frame_id = "laser_link";plaser.point.x = 1;plaser.point.y = 0;plaser.point.z = 0;geometry_msgs::PointStamped pbase;ros::Rate rate(10);while(nh.ok()){cout << "start listening" << endl;tflis.waitForTransform("base_link", "laser_link", ros::Time(0), ros::Duration(3));tflis.transformPoint("base_link", plaser, pbase);cout << "pbase is: (" << pbase.point.x << "," << pbase.point.y << "," << pbase.point.z << ")" << endl;rate.sleep();}return 0;
}

        为了让两个代码都能顺利编译通过。有两个地方需要修改下。一个是package.xml,

  <build_depend>tf</build_depend>

        另外一个就是CMakeLists.txt,

## Find catkin and any catkin packages
find_package(catkin REQUIRED COMPONENTS message_generation roscpp rospy std_msgs genmsg tf)add_executable(book_tfpub src/book_tfpub.cpp)
target_link_libraries(book_tfpub ${catkin_LIBRARIES})
add_dependencies(book_tfpub beginner_tutorials_generate_messages_cpp)add_executable(book_tflis src/book_tflis.cpp)
target_link_libraries(book_tflis ${catkin_LIBRARIES})
add_dependencies(book_tflis beginner_tutorials_generate_messages_cpp)

        经过catkin_make编译和rosrun启动,就可以看到这样的打印信息了,

start listening
pbase is: (2,0,0)
start listening
pbase is: (2,0,0)
start listening
pbase is: (2,0,0)
start listening
pbase is: (2,0,0)
start listening
pbase is: (2,0,0)
start listening
pbase is: (2,0,0)

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

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

相关文章

推荐算法——Apriori算法原理

0、前言&#xff1a; 首先名字别读错&#xff1a;an pu ruo ao rui 【拼音发音】Apriori是一种推荐算法推荐系统&#xff1a;从海量数据中&#xff0c;帮助用户进行信息的过滤和选择。主要推荐方法有&#xff1a;基于内容的推荐、协同过滤推荐、基于关联规则的推荐、基于知识的…

Spring整合RabbitMQ——生产者

1.生产者整合步骤 添加依赖坐标&#xff0c;在producer和consumer模块的pom文件中各复制一份。 配置producer的配置文件 配置producer的xml配置文件 编写测试类发送消息

《HelloGitHub》第 90 期

兴趣是最好的老师&#xff0c;HelloGitHub 让你对编程感兴趣&#xff01; 简介 HelloGitHub 分享 GitHub 上有趣、入门级的开源项目。 https://github.com/521xueweihan/HelloGitHub 这里有实战项目、入门教程、黑科技、开源书籍、大厂开源项目等&#xff0c;涵盖多种编程语言 …

javascript: Sorting Algorithms

// Sorting Algorithms int JavaScript https://www.geeksforgeeks.org/sorting-algorithms/ /** * file Sort.js * 1. Bubble Sort冒泡排序法 * param arry * param nszie */ function BubbleSort(arry, nszie) {var i, j, temp;var swapped;for (i 0; i < nszie - 1; i)…

动态规划算法(1)--矩阵连乘和凸多边形剖分

目录 一、动态数组 1、创建动态数组 2、添加元素 3、删除修改元素 4、访问元素 5、返回数组长度 6、for each遍历数组 二、输入多个数字 1、正则表达式 2、has.next()方法 三、矩阵连乘 1、什么是矩阵连乘&#xff1f; 2、动态规划思路 3、手推m和s矩阵 4、完…

【生物信息学】计算图网络中节点的中心性指标:聚集系数、介数中心性、度中心性

目录 一、实验介绍 二、实验环境 1. 配置虚拟环境 2. 库版本介绍 3. IDE 三、实验内容 0. 导入必要的工具 1. 生成邻接矩阵simulate_G: 2. 计算节点的聚集系数 CC(G): 3.计算节点的介数中心性 BC(G) 4. 计算节点的度中心性 DC(G) 5. 综合centrality(G) 6. 代…

xilinx的原语的使用

xilinx的原语的使用 在学习FPGA实现千兆网时需要GMII转RGMII&#xff0c;这就涉及了原语的使用&#xff0c;特此记录&#xff01; 一、原语 与RGMII接口相关的原语&#xff1a; BUFG:全局时钟网络 BUFIO&#xff1a;只能采集IO的数据&#xff0c;采集IO数据的时候延时是最低的…

【【萌新的Risc-V学习之再看读不懂的流水线设计-10】】

萌新的Risc-V学习之再看读不懂的流水线设计-10 我们将流水线和之前案例中洗衣服的例子进行对照 我们把整个流水线分为5个阶段 也就是做成五级流水线 IF: 取指令ID: 指令译码和读寄存器堆EX: 执行或计算地址MEM: 数据存储器访问WB: 写回 我先在这里表述一下基本的几个指令的用…

【Java基础】抽象类和接口的使用

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【JavaSE_primary】 本专栏旨在分享学习JavaSE的一点学习心得&#xff0c;欢迎大家在评论区讨论&#x1f48c; 目录 一、抽象类抽象类概念…

无设计经验也能制作专业国庆微传单

如果你正在计划一个国庆活动&#xff0c;或者想要创建一个微传单来宣传你的品牌或产品&#xff0c;那么你可以尝试使用乔拓云微传单平台。通过这个平台&#xff0c;你可以轻松地创建和发布一个精美的微传单&#xff0c;而且完全免费。 以下是制作国庆微传单H5的步骤&#xff1a…

stl格式-3D三角形

文章目录 什么是stl文件?格式首选stl的语法1.这是一个stl格式的文件:(ASCII码)2.下面先举个例子(难度略微提示)补充:关于\<\<我试了一下:这个法线你随便写好像也没问题\>> 3.来个立方体4.最后再写一个由三个直角形组成的立方体(直棱锥)5.amend 修正(右手定则,法线…

【ArcGIS Pro二次开发】(69):使用MapTool实现隐藏和隔离图层

一、MapTool简介 在ArcGIS Pro SDK中&#xff0c;MapTool是一个重要的组件&#xff0c;用于自定义地图操作工具&#xff0c;使用户能够在ArcGIS Pro中执行特定的地图交互操作。 在VS中添加新项&#xff0c;可以找到ArcGIS Pro 地图工具&#xff0c;即为MapTool。 新建后打开c…

优雅的写Controller 层代码这样写才可以

前 言 本篇主要要介绍的就是controller层的处理&#xff0c;一个完整的后端请求由4部分组成&#xff1a; 接口地址(也就是URL地址)请求方式(一般就是get、set&#xff0c;当然还有put、delete)请求数据(request&#xff0c;有head跟body)响应数据(response) 本篇将解决以下3个…

Vue+element开发Simple Admin后端管理系统页面

最近看到各种admin&#xff0c;头大&#xff0c;内容太多&#xff0c;根本不知道怎么改。所以制作了这个项目&#xff0c;只包含框架、和开发中最常用的表格和表单&#xff0c;不用自己从头搭建架构&#xff0c;同时也容易上手二次开发。可以轻松从其他开源项目整合到本项目。项…

基于Qt Creator开发的坦克大战小游戏

目录 介绍开发环境技术介绍安装说明项目目录设计思想项目介绍运行演示知识点记录Gitee源码链接 介绍 &#xff01;&#xff01;&#xff01;资源图片是从网上免费下载&#xff0c;源码都是原创&#xff0c;供个人学习使用&#xff0c;非盈利&#xff01;&#xff01;&#xff…

UE5报错及解决办法

1、编译报错&#xff0c;内容如下&#xff1a; Unable to build while Live Coding is active. Exit the editor and game, or press CtrlAltF11 if iterating on code in the editor or game 解决办法 取消Enable Live Coding勾选

Java编码

Java编码问题 Unicode与码点 所谓Unicode就是全世界的字符字典&#xff0c;也就是把字符给一个编号&#xff0c;这个编码就是码点。比如 2. 编码 由于这种分配的编码无论从占用空间角度&#xff0c;还是读取速度&#xff0c;以及逻辑划分角度&#xff0c;都不是完善。所以出…

了解”变分下界“

“变分下界”&#xff1a;在变分推断中&#xff0c;我们试图找到一个近似概率分布q(x)来逼近真实的概率分布p(x)。变分下界是一种用于评估近似概率分布质量的指标&#xff0c;通常用来求解最优的近似分布。它的计算涉及到对概率分布的积分或期望的估计

分布式搜索引擎es-3

文章目录 数据聚合聚合的种类RestAPI实现聚合 自动补全自定义拼音分词器自动补全查询案例&#xff1a;实现酒店搜索框自动补全自动补全的javaAPI实现搜索框自动补全 口述自动补全数据同步集群集群的分布式存储集群分布式查询集群故障转移 数据聚合 什么是聚合&#xff1f; 聚合…

【面试经典150 | 矩阵】旋转图像

文章目录 写在前面Tag题目来源题目解读解题思路方法一&#xff1a;原地旋转方法二&#xff1a;翻转代替旋转 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢迎催更…… 专栏内容以分析题目为主&#xff0c;并附带…