ros-tf总结

ros tf

tf 说明

tf是ros中一个很重要的模块,避免了我们在使用过程中不同坐标系下数据转换之间的困扰。目前使用主要是以下几种场景需要用到tf,

  • 希望将安装在车辆前端的激光雷达采集到的点云数据的坐标系从激光雷达坐标系转换为车辆坐标系
  • 希望将车辆位置通过全局坐标表示

下面是我在学习中的一些整理和理解。

tf 使用

tf的使用主要分为两个部分,广播和监听。广播的作用是向系统中广播参考系之间的坐标变换关系,监听则是接收并缓存系统中发布的所有参考系变换。上面说的比较抽象,下面附上深蓝学院一个例子,以帮助更好理解。

广播部分的用法,比较固定。就是声明一个broadcaster,然后用其sendTransform方法发布一个tf::StampedTransform。这里面的核心在于tf::StampedTransform的构建。

tf::StampedTransform(const tf::Transform &input, const ros::Time &timestamp, const std::string &frame_id, const std::string &child_frame_id )   

tf::StampedTransfor继承自tf::Transform,其第一个参数就是tf::Transform,第二个参数是timestamp,其中比较重要的是第三个和第四个参数,同时要注意一下第三个和第四个参数的名字,分别对应tf tree的父节点和子节点,例如例子中间的frame_id是base_link, child_frame_id是base_laser。在确定完第三、四个参数后,就需要来思考第一个参数的矩阵是如何构建的。深蓝学院的例子中,可以给我们参考,其实指的是child_frame_id在frame_id下的坐标,在例子中,右手坐标系下,laser在车辆坐标系下的坐标就是(0.1, 0.0, 0.2)。

转换矩阵的构建方法请参考

// name: tf_broadcaster.cpp
#include <ros/ros.h>
#include <tf/transform_broadcaster.h>

int main(int argc, char** argv){
  ros::init(argc, argv, "robot_tf_publisher");
  ros::NodeHandle n;
  ros::Rate r(100);
  tf::TransformBroadcaster broadcaster;

  while(n.ok()){
    broadcaster.sendTransform(
      tf::StampedTransform(
        tf::Transform(tf::Quaternion(0, 0, 0, 1), tf::Vector3(0.1, 0.0, 0.2)), ros::Time::now(), "base_link", "base_laser"));
    r.sleep();
  }
}

listener部分也是比较死板的。还是首先需要一个监听器的对象,后面的做法有两种,一个是像例子中,使用listener.transformPoint("base_link", laser_point, base_point)。

void tf::TransformListener::transformDATATYPE (const std::string &target_frame, const geometry_msgs::DATATYPEStamped &stamped_in, geometry_msgs::DATATYPEStamped &stamped_out) const 

这类方法是有3个输入参数,其中第一个是目标的坐标系,比如例子中,就是要在车的坐标系下进行观察。这个地方可以是任意你想切换到的坐标系,但是必须要是在tf tree中存在的。后面的两个是需要进行构建的,不同的transformDATATYPE是不一样的,需要查阅资料。在第二个参数中,会带上第二个参数所在的坐标系,由此就有了原坐标系,再由第一个参数,便可以再tf tree中查询到tf,在进行转换后会将结果储存在第三个参数中。

另外是一种保存转移矩阵,

void tf::TransformListener::lookupTransform (const std::string &target_frame, const std::string &source_frame, const ros::Time &time, StampedTransform &transform) const 

这个部分的转移矩阵是结果,要注意其含义,是将在source_frame坐标系下的数据转换为在target_frame坐标系下的数据。比如source_frame是激光雷达的坐标系,而father_id_是车辆坐标起。

// name: tf_listener.cpp
#include <ros/ros.h>
#include <geometry_msgs/PointStamped.h>
#include <tf/transform_listener.h>

void transformPoint(const tf::TransformListener& listener){
  geometry_msgs::PointStamped laser_point;
  laser_point.header.frame_id = "base_laser";
  laser_point.header.stamp = ros::Time();
  laser_point.point.x = 1.0;
  laser_point.point.y = 0.2;
  laser_point.point.z = 0.0;

  try{
    geometry_msgs::PointStamped base_point;
    listener.transformPoint("base_link", laser_point, base_point);
    ROS_INFO("base_laser: (%.2f, %.2f. %.2f) -----> base_link: (%.2f, %.2f, %.2f) at time %.2f",
        laser_point.point.x, laser_point.point.y, laser_point.point.z,
        base_point.point.x, base_point.point.y, base_point.point.z, base_point.header.stamp.toSec());
  }
  catch(tf::TransformException& ex){
    ROS_ERROR("Received an exception trying to transform a point from \"base_laser\" to \"base_link\": %s", ex.what());
  }
}

int main(int argc, char** argv){
  ros::init(argc, argv, "robot_tf_listener");
  ros::NodeHandle n;
  tf::TransformListener listener(ros::Duration(10));
  ros::Timer timer = n.createTimer(ros::Duration(1.0), boost::bind(&transformPoint, boost::ref(listener)));
  ros::spin();
}

猜你喜欢

转载自www.cnblogs.com/ztyu/p/12424042.html