ROS学习笔记12——tf坐标变换

ops/2024/9/25 13:18:41/

以ros自带的小乌龟作为案例的载体,完成通信方式、坐标变换等的练习,实现程序启动之初 产生两只乌龟,中间的乌龟(turtle1) 和 左下乌龟(turtle2), turtle2 会自动运行至turtle1的位置,并且键盘控制时,只是控制 turtle1 的运动,但是 turtle2 可以跟随 turtle1 运行。

1、创建功能包

创建项目功能包依赖于roscpp、rospy、std_msgs、 tf2、tf2_ros、tf2_geometry_msgs、geometry_msgs、turtlesim

2、服务客户端(生成新的小乌龟turtle2)

#include "ros/ros.h"
#include "turtlesim/Spawn.h"
/*需求:向服务端发送请求,生成一只新乌龟话题:/spawn -> 通过rosservice list看到消息类型:turtlesim/Spawn -> 通过rosservice info /spawn看到消息的具体内容:。。。 -> 通过rossrv info turtlesim/Spawn看到
*/int main(int argc, char *argv[])
{/* code */setlocale(LC_ALL, "");ros::init(argc, argv,"create_new_turtle");ros::NodeHandle nh;//创建服务对象ros::ServiceClient client = nh.serviceClient<turtlesim::Spawn>("/spawn");//组织数据turtlesim::Spawn spawn;spawn.request.name = "turtle2";spawn.request.x = 1.0;spawn.request.y = 2.0;spawn.request.theta = 1.57;client.waitForExistence();//判断服务器状态//发送请求bool flag = client.call(spawn);//flag用来接收响应状态的,响应结果也会被设置进spawn对象if(flag){ROS_INFO("新乌龟 %s 成功生成", spawn.response.name.c_str());}else{ROS_INFO("新乌龟生成失败");}ros::spin();return 0;
}

3、发布方(发布两只小乌龟的坐标信息)

可以订阅乌龟的位姿信息,然后转换成坐标系关系,两只乌龟的实现逻辑是相同的,修改订阅话题和消息类型之后,新建一个cpp重复实现一遍逻辑当然是可以的,但是加大了代码量,并且降低了代码的复用率。这些细微的差异可以通过args设置参数来传入:

  • 该节点需要启动两次
  • 每次启动时都需要传入乌龟节点名称(第一次是 turtle1 第二次是 turtle2)
#include "ros/ros.h"
#include "turtlesim/Pose.h"
#include "tf2_ros/transform_broadcaster.h"
#include "geometry_msgs/TransformStamped.h"
#include "tf2/LinearMath/Quaternion.h"
/*
发布方实现需求:订阅乌龟的位姿信息,转换成相对于窗体的坐标关系,并发布准备:话题:/turtle1/pose、/turtle2/pose消息:turtlesim/Pose流程:1、包含头文件2、设置编码、初始化、NodeHandle等3、创建订阅对象、订阅话题:/turtle1/pose、/turtle2/pose4、**回调函数,处理订阅的消息:把订阅的位姿信息转换成坐标系相对关系,并发布5、spin()
*///声明接收传递参数的变量
std::string turtle_name;void doPose(const turtlesim::Pose::ConstPtr &pose)
{//获取位姿信息,转换成坐标系相对关系,并发布//5.1 创建发布对象static tf2_ros::TransformBroadcaster pub;   //设为static静态变量,每次回调使用同一个pub对象//5.2 组织被发布的数据  geometry_msgs::TransformStamped ts;ts.header.frame_id = "world";ts.header.stamp = ros::Time::now();//**子级坐标系名称动态传入ts.child_frame_id = turtle_name;//坐标系偏移量ts.transform.translation.x = pose->x;ts.transform.translation.y = pose->y;ts.transform.translation.z = 0;tf2::Quaternion qnt;qnt.setRPY(0,0,pose->theta);ts.transform.rotation.w = qnt.getW();ts.transform.rotation.x = qnt.getX();ts.transform.rotation.y = qnt.getY();ts.transform.rotation.z = qnt.getZ();//5.3 发布pub.sendTransform(ts);
}int main(int argc, char *argv[])
{/* code *//*解析 launch 文件通过 args 传入的参数*/if(argc != 4){ROS_INFO("请传入一个参数");return 1;}else{turtle_name = argv[1];}setlocale(LC_ALL,"");// 2、设置编码、初始化、NodeHandle等ros::init(argc,argv,"dynamic_pub_turtle");ros::NodeHandle nh;// **3、创建订阅对象、订阅话题:/turtle1 或者 turtle2 是动态传入的ros::Subscriber sub = nh.subscribe<turtlesim::Pose>(turtle_name + "/pose",100,doPose);// 4、**回调函数,处理订阅的消息:把订阅的位姿信息转换成坐标相对关系,并发布// 5、spin()    ros::spin();return 0;
}

4、订阅方(解析坐标信息并且生成速度信息)

#include "ros/ros.h"
#include "tf2_ros/transform_listener.h"
#include "tf2_ros/buffer.h"
#include "geometry_msgs/PointStamped.h"
#include "tf2_geometry_msgs/tf2_geometry_msgs.h"
#include "geometry_msgs/TransformStamped.h"
#include "geometry_msgs/Twist.h"
/*需求1:换算出 turtle1 相对于 turtle2 的关系需求2:计算角速度和线速度并发布
*/
int main(int argc, char *argv[])
{/* code */setlocale(LC_ALL, "");// 2、编码、初始化、NodeHandleros::init(argc, argv,"tfs_sub");ros::NodeHandle nh;// 3、创建订阅对象tf2_ros::Buffer buffer;tf2_ros::TransformListener sub(buffer);//A、创建发布对象ros::Publisher pub = nh.advertise<geometry_msgs::Twist>("/turtle2/cmd_vel",100);// 4、编写解析逻辑(循环,坐标关系和坐标点的转换)//ros::Duration(2).sleep();ros::Rate rate(10);while(ros::ok()){try{// 1、计算 turtle1 和 turtle2 之间的相对关系/* lookupTransform(参数1:B,参数2:A,参数3)## A 相对于 B 的坐标系关系参数1:"目标坐标系 B"参数2:"源坐标系 A"参数3:ros::Time(0) 取间隔最短的两个坐标系帧计算相对关系返回值:geometry_msgs::TransformStamped*/geometry_msgs::TransformStamped tfs = buffer.lookupTransform("turtle2","turtle1",ros::Time(0));ROS_INFO("turtle1 相对于 turtle2 的信息:父级名称:%s,子级名称:%s,偏移量:(%.2f,%.2f,%.2f)",tfs.header.frame_id.c_str(), //turtle2tfs.child_frame_id.c_str(),   //turtle1tfs.transform.translation.x,tfs.transform.translation.y,tfs.transform.translation.z); //B、根据相对位置计算并组织速度消息geometry_msgs::Twist twist;/*linear.x = 系数 * 开放(y^2 + x^2)angular.z = 反正切(对边,临边)*/twist.linear.x = 0.5 * sqrt(pow(tfs.transform.translation.x,2) + pow(tfs.transform.translation.y,2));twist.angular.z = 4 * atan2(tfs.transform.translation.y,tfs.transform.translation.x);//C、发布pub.publish(twist);}catch(const std::exception& e){//std::cerr << e.what() << '\n';ROS_INFO("异常消息:%s", e.what());}rate.sleep();// 5、spinOnce()ros::spinOnce();}return 0;
}

5、运行

使用 launch 文件组织需要运行的节点

<launch><!-- 启动小乌龟GUI --><node pkg="turtlesim" type="turtlesim_node" name="turtle1" output="screen" /><!-- 键盘控制节点 --><node pkg="turtlesim" type="turtle_teleop_key" name="key" output="screen" /><!-- 创建新的小乌龟 --><node pkg="tfs_test" type="create_new_turtle" name="turtle2" output="screen" /><!-- 需要启动两个乌龟相对于世界坐标系的坐标关系的发布节点 --><!-- 实现思路:1、只编写一个节点2、这个节点需要启动两次3、节点启动时,动态传参:第一次:turtle1第二次:turtle2--><node pkg="tfs_test" type="pub_turtle" name="pub_turtle1" args="turtle1" output="screen" /><node pkg="tfs_test" type="pub_turtle" name="pub_turtle2" args="turtle2" output="screen" /><!-- 订阅turtle1与 turtle2 相对于世界坐标系的坐标关系并生成 turtle1 相对于 turtle2 的坐标关系再生成速度消息 --><node pkg="tfs_test" type="control_turtle2" name="control" output="screen" />
</launch>

6、记得配置CMakeLists


http://www.ppmy.cn/ops/29300.html

相关文章

BKPUNIX

第二条等待寄存器同步&#xff0c;可以参考前边RTC框图部分。在图中可以看到有两个时钟&#xff0c;PCLK1和RTCCLK&#xff0c;PCLK1在主电源掉电时会停止&#xff0c;为了保证RTC主电源掉电正常工作&#xff0c;RTC里的寄存器都是在RTCCLK同步下进行变更的。当用PCLK驱动的总线…

Java线程池的七大参数说明

线程池中的七大参数如下&#xff1a; &#xff08;1&#xff09;corePoolSize&#xff1a;线程池中的常驻核心线程数。 &#xff08;2&#xff09;maximumPoolSize&#xff1a;线程池能够容纳同时执行的最大线程数&#xff0c;此值大于等于1。 &#xff08;3&#xff09;keepAl…

“Transformer挑战者”-Mamba之最全详解图解

-------------------------------------------------------------------------------------------------------------------- 1.AIGC未来发展前景 未完持续… 1.1 人工智能相关科研重要性 拥有一篇人工智能科研论文及专利软著竞赛是保研考研留学深造以及找工作的关键门票&a…

jvm面试题30问

什么是JVM的跨平台&#xff1f; 什么是JVM的语言无关性&#xff1f; 什么是JVM的解释执行 什么是JIT? JIT&#xff1a;在Java编程语言和环境中&#xff0c;即时编译器&#xff08;JIT compiler&#xff0c;just-in-time compiler&#xff09;是一个把Java的字节码&#xff08;…

【智能算法】鹦鹉优化算法(WO)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献 1.背景 2024年&#xff0c;J Lian等人受到鹦鹉学习行为启发&#xff0c;提出了鹦鹉优化算法&#xff08;Parrot Optimizer, PO&#xff09;。 2.算法原理 2.1算法思想 PO灵感来自于在驯养的鹦鹉中观察到的…

扭蛋机小程序带来了什么优势?扭蛋机收益攻略

在当下的潮流消费时代&#xff0c;人们对潮玩也日益个性化&#xff0c;扭蛋机作为一种新型的娱乐消费模式&#xff0c;深受大众喜爱。扭蛋机的价格低&#xff0c;各个年龄层的玩家都可以进行购买&#xff0c;潜在玩家量非常大。扭蛋机商品主打热门IP周边等&#xff0c;种类繁多…

MATLAB初学者入门(21)—— 霍夫曼树

霍夫曼编码是一种广泛用于数据压缩的有效技术。它基于字符频率或概率来构造最优的前缀码&#xff0c;使得常用字符的编码长度较短&#xff0c;不常用的字符编码长度较长&#xff0c;从而达到压缩数据的目的。MATLAB中可以通过一系列步骤来实现霍夫曼树的构建和相应的编码过程。…

Springboot自定义注解+aop实现redis自动清除缓存功能

假如我们是一个商家&#xff0c;我们在管理端 设置好菜品之后&#xff0c;每次不同的用户访问我们的菜品&#xff0c;都要查询数据库&#xff0c;当访问者多的时候&#xff0c;会造成数据库的卡顿&#xff0c;所以使用redis进行缓存&#xff0c;这样只需要读取一次数据库&#…