ROS坐标系管理工具包-tf2

简介

ROS中存在许多不同的坐标系,例如传感器坐标系、机器人本体系、世界坐标系、地图坐标系等。虽然我们可以手动管理不同坐标系之间转换关系,但在大型项目中难免会出现混乱,为此ROS提供了坐标系管理工具包tf和tf2,两者的简介和区别可以参考官方文档ROS tf2简介

对于新用户来说,官方建议使用上手直接使用接口定义更为规范的tf2。本文是基于ROS官方tf2教程的记录,便于各位在学习过程中参考。

编写静态tf2发布器

对于静止不动的坐标变换关系,可以使用静态tf2发布器,该发布器属于tf2_ros工具包,支持欧拉角和四元数两种格式的调用:

// 欧拉角形式
rosrun static_transform_publisher x y z yaw pitch roll frame_id child_frame_id
// 四元数形式
rosrun static_transform_publisher x y z qx qy qz qw frame_id child_frame_id

其中,x, y, z为子坐标系(child_frame)原点在父坐标系(frame)中的坐标;欧拉角调用中yaw(Z), pitch(Y), roll(X)为父坐标系旋转到子坐标系的旋转角度,转序为Z-Y-X;四元数调用中qx, qy, qz为四元数的矢量部分,qw为四元数的标量部分。

编写动态tf2发布器

相比静态变换(static transformation),实际中使用更多的是动态变换,例如需要将IMU的量测信息由传感器坐标系变换到世界坐标系,而传感器坐标系是固联在体系上,随体系一起转动的,节点demo如下:

#include <ros/ros.h>
#include <tf2/LinearMath/Quaternion.h> // tf2四元数头文件
#include <tf2_ros/transform_broadcaster.h> //tf2 broadcaster头文件
#include <geometry_msgs/TransformStamped.h>// 带时戳的变换消息
#include <turtlesim/Pose.h>

std::string turtle_name; //子坐标系名字

void poseCallback(const turtlesim::PoseConstPtr& msg){
  //构建tf2 broadcaster
  static tf2_ros::TransformBroadcaster br;
  //构建用于tf2 broadcaster的带时戳变换消息
  geometry_msgs::TransformStamped transformStamped;
  // 构建消息头
  transformStamped.header.stamp = ros::Time::now();
  transformStamped.header.frame_id = "world";
  // 子坐标系ID(注意其不在消息头中)
  transformStamped.child_frame_id = turtle_name;
  // 子坐标系原点在world下的三轴坐标
  transformStamped.transform.translation.x = msg->x;
  transformStamped.transform.translation.y = msg->y;
  transformStamped.transform.translation.z = 0.0;
  // 子坐标系和world之间的姿态(转序为zyx)
  tf2::Quaternion q;
  q.setRPY(0, 0, msg->theta);
  transformStamped.transform.rotation.x = q.x();
  transformStamped.transform.rotation.y = q.y();
  transformStamped.transform.rotation.z = q.z();
  transformStamped.transform.rotation.w = q.w();
  // 广播变换
  br.sendTransform(transformStamped);
}
 
int main(int argc, char** argv){
  ros::init(argc, argv, "my_tf2_broadcaster");

  ros::NodeHandle private_node("~");
  //检查命令行或launch中是否给定了turtle name
  if (! private_node.hasParam("turtle"))
  {
    if (argc != 2){ROS_ERROR("need turtle name as argument"); return -1;};
    turtle_name = argv[1];
  }
  else
  {
    private_node.getParam("turtle", turtle_name);
  }
    
  ros::NodeHandle node;
  ros::Subscriber sub = node.subscribe(turtle_name+"/pose", 10, &poseCallback);

  ros::spin();
  return 0;
};

编写tf2接收器

上一节我们通过tf2_broadcaster发布了一组变换关系,本节演示如何获取这一变换关系以实现ROS tf2简介中的turtle2自动跟随turtle1效果。

#include <ros/ros.h>
#include <tf2_ros/transform_listener.h>
#include <geometry_msgs/TransformStamped.h>
#include <geometry_msgs/Twist.h>
#include <turtlesim/Spawn.h>

int main(int argc, char** argv){
    // 初始化节点与节点句柄
    ros::init(argc, argv, "tf2_listener");
    ros::NodeHandle nh;
    // 获取服务生成另一个turtle
    ros::service::waitForService("spawn");
    ros::ServiceClient spawner = nh.serviceClient<turtlesim::Spawn>("spawn");
    turtlesim::Spawn turtle;
    turtle.request.x = 4;
    turtle.request.y = 2;
    turtle.request.theta = 0;
    turtle.request.name = "turtle2";
    spawner.call(turtle);
    // 创建第二个turtle的速度消息publisher
    ros::Publisher turtle_vel = nh.advertise<geometry_msgs::Twist>("turtle2/cmd_vel", 10);
    // 创建tf缓存器和tf_listener(10s)
    tf2_ros::Buffer tfBuffer;
    tf2_ros::TransformListener tfListener(tfBuffer);

    ros::Rate rate(10.0);
    
    while(nh.ok()){
        geometry_msgs::TransformStamped tf_stamped;
        // 在最近的tfBuff中寻找是否存在turtle2和turtle1之间的变换
        try{
            tf_stamped = tfBuffer.lookupTransform("turtle2", "turtle1", ros::Time(0)); 
        }
        catch(tf2::TransformException &ex){
            ROS_WARN("%s", ex.what());
            ros::Duration(1.0).sleep();
            continue;
        }
 		// 根据制导率和接收到的tf消息计算速度并发布
        geometry_msgs::Twist vel_msg;
        vel_msg.angular.z = 4.0 * atan2(tf_stamped.transform.translation.y, 
                                        tf_stamped.transform.translation.x);
        vel_msg.linear.x = 0.5 * sqrt(pow(tf_stamped.transform.translation.x, 2) +
                                      pow(tf_stamped.transform.translation.y, 2));
        turtle_vel.publish(vel_msg);
        rate.sleep();
    }
    return 0;
}

代码中核心部分主要包括以下几部分:

  1. 创建tf2_ros::TransformListener对象,接受参数为tf2_ros::Buffer对象。该对象从创建开始自动向tf2_ros::Buffer添加所有的发布的geometry_msgs::TransformStampedgeometry_msgs::Transform消息,默认维持数据量为最近的10s;
// 创建tf缓存器和tf_listener(10s)
tf2_ros::Buffer tfBuffer;
tf2_ros::TransformListener tfListener(tfBuffer);
  1. 使用在tf2_ros::BufferlookupTransform函数在当前tf2_ros::Buffer对象中寻找指定的变换关系:
 tf_stamped = tfBuffer.lookupTransform("turtle2", "turtle1", ros::Time(0)); 
 	// 参数1: 目标frame
 	// 参数2: 参考frame
 	// 参数3: 所获取tf对应的时刻,若为ros::Time(0)则表示获取最新的tf

lookupTransform中的时间参数

在编写tf2_listener时,我们注意到lookupTransform函数的第三个参数为时间参数ros::Time,其最易混淆的两种用法是ros::Time::now()ros::Time(0),区别列举如下:

  • ros::Time::now():该参数表示从tf_buffer中获取和当前时戳一致的tf,由于传输延迟的存在,期望的tf很可能并不存在;
  • ros::Time(0):该参数表示从tf_buffer中获取时戳最新的tf,期望的tf一定存在,但由于传输延迟的存在,该tf并不一定是当前时刻的tf;

实际上,完整的lookupTransform共包括6个入口参数,说明如下:

lookupTransform(string& target_frame, ros::Time& target_time
				string& source_frame, ros::Time& source_time
				string& fixed_frame, ros::Time& timeout)
	// target_frame:目标坐标系名称
	// target_time:目标坐标系的时戳
	// source_frame:原坐标系名称
	// source_time:原坐标系时戳
	// fixed_frame:固定坐标系名称
	// timeout:等待时间

在默认调用中,target_time和source_time默认一致,固定坐标系默认为世界坐标系"/world"。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
<tf2/utils.h> 是一个头文件,它包含了一些常用的工具函数和宏定义,可以方便地在ROS 2中实现一些常见的功能。 该头文件中包含的函数和宏定义如下: - TF2_ROS_ASSERT(expr):用于在ROS中断言一个表达式,如果表达式不成立,则会打印错误信息并终止程序。 - TF2_ROS_WARN(...):用于在ROS中打印警告信息。 - TF2_ROS_ERROR(...):用于在ROS中打印错误信息。 - TF2_ROS_DEBUG(...):用于在ROS中打印调试信息。 - TF2_ROS_INFO(...):用于在ROS中打印一般信息。 - TF2_ROS_ASSERT_MSG(expr, ...):用于在ROS中断言一个表达式,并在表达式不成立时打印给定的错误信息。 - TF2_ROS_WARN_ONCE(...):用于在ROS中仅打印一次警告信息。 - TF2_ROS_ERROR_ONCE(...):用于在ROS中仅打印一次错误信息。 - TF2_ROS_DEBUG_ONCE(...):用于在ROS中仅打印一次调试信息。 - TF2_ROS_INFO_ONCE(...):用于在ROS中仅打印一次一般信息。 - tf2::get_transform_listener():用于获取一个用于监听Transform的TransformListener对象。 - tf2::convert():用于将一种Transform类型转换为另一种类型。 - tf2::fromMsg():用于从TF2消息类型转换为TF2数据类型。 - tf2::toMsg():用于将TF2数据类型转换为TF2消息类型。 - tf2::doTransform():用于将给定的数据类型从一个坐标系转换到另一个坐标系。 这些函数和宏定义使得在ROS 2中使用TF2库更加方便,可以快速实现常见的功能,如监听Transform、转换坐标系等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值