ROS - tf(四)

本文涉及的产品
资源编排,不限时长
简介: ROS - tf

ROS - tf(三)+https://developer.aliyun.com/article/1585199

7.2 编写代码

希望上面的示例能帮助我们从概念上理解 tf。现在,我们需要用代码来创建转换树。在本例中,我们假定对 ROS 非常熟悉,因此如果有不熟悉的术语或概念,请务必查看 ROS 文档。

假设我们要完成上面描述的高级任务,即把 "base_laser "坐标系中的点转换到 "base_link "坐标系中。首先,我们需要创建一个节点,负责在系统中发布转换结果。接下来,我们需要创建一个节点来监听通过 ROS 发布的变换数据,并将其应用于点的变换。首先,我们将为源代码创建一个包,并给它起一个简单的名字,比如 "robot_setup_tf"。我们将依赖于 roscpp、tf 和 geometry_msgs。

cd %TOP_DIR_YOUR_CATKIN_WS%/src
catkin_create_pkg robot_setup_tf roscpp tf geometry_msgs

请注意,您必须在有权限的地方运行上述命令(例如,您可能为之前的教程创建的 ~/ros)。

在 fuerte、groovy 和 hydro 中的替代方法:在 navigation_tutorials 堆栈中有一个标准的 robot_setup_tf_tutorial 包。您可以按照以下方法安装(%YOUR_ROS_DISTRO% 可以是 { fuerte, groovy } 等):

sudo apt-get install ros-%YOUR_ROS_DISTRO%-navigation-tutorials

7.3 广播变换

现在我们已经有了自己的软件包,需要创建一个节点,在 ROS 上广播 base_laser → base_link 变换。在刚刚创建的机器人安装包(robot_setup_tf)中,启动你喜欢的编辑器,将以下代码粘贴到 src/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();
  }
}

现在,让我们详细看看与发布 base_link → base_laser 变换相关的代码。

#include <tf/transform_broadcaster.h>

tf 软件包提供了一个 tf::TransformBroadcaster 实现,帮助我们更轻松地发布变换。要使用 TransformBroadcaster,我们需要包含 tf/transform_broadcaster.h 头文件。

tf::TransformBroadcaster broadcaster;

在这里,我们创建了一个 TransformBroadcaster 对象,稍后我们将用它通过电线发送 base_link → base_laser 变换。

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"));

这才是真正的工作。使用 TransformBroadcaster 发送变换需要五个参数。首先,我们传递旋转变换,它由一个 btQuaternion 来指定两个坐标系之间需要进行的任何旋转。在本例中,我们不希望应用任何旋转,因此我们输入一个由俯仰、滚动和偏航值构成的 btQuaternion,其值等于零。其次,一个 btVector3 表示我们想要应用的任何平移。不过,我们确实想要应用平移,因此我们创建了一个 btVector3,对应于激光器与机器人基座之间 10 厘米的 x 偏移量和 20 厘米的 z 偏移量。第三,我们需要给正在发布的变换加一个时间戳,我们只需用 ros::Time::now() 给它盖个戳。第四,我们需要传入所创建刚体的父节点名称,本例中为 "base_link"。第五,我们需要传递所创建刚体的子节点的名称,本例中为 "base_laser"。

7.4 使用变换

上面,我们创建了一个节点,通过 ROS 发布 base_laser → base_link 变换。现在,我们要编写一个节点,使用该变换将 "base_laser "坐标系中的一个点变换为 "base_link "坐标系中的一个点。同样,我们先将下面的代码粘贴到一个文件中,然后再进行更详细的解释。在 robot_setup_tf 软件包中创建一个名为 src/tf_listener.cpp 的文件,并粘贴以下代码:

#include <ros/ros.h>
#include <geometry_msgs/PointStamped.h>
#include <tf/transform_listener.h>
void transformPoint(const tf::TransformListener& listener){
  //we'll create a point in the base_laser frame that we'd like to transform to the base_link frame
  geometry_msgs::PointStamped laser_point;
  laser_point.header.frame_id = "base_laser";
  //we'll just use the most recent transform available for our simple example
  laser_point.header.stamp = ros::Time();
  //just an arbitrary point in space
  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));
  //we'll transform a point once every second
  ros::Timer timer = n.createTimer(ros::Duration(1.0), boost::bind(&transformPoint, boost::ref(listener)));
  ros::spin();
}

好了......现在我们一步步来介绍重要的部分。

#include <tf/transform_listener.h>

在这里,我们包含了创建 tf::TransformListener 所需的 tf/transform_listener.h 头文件。TransformListener 对象会自动订阅 ROS 上的变换消息主题,并管理所有通过线路传送的变换数据。

void transformPoint(const tf::TransformListener& listener){

我们将创建一个函数,该函数在给定 TransformListener 的情况下,接收 "base_laser "坐标系中的一个点,并将其转换到 "base_link "坐标系中。该函数将作为在程序的 main() 中创建的 ros::Timer 的回调函数,每秒触发一次。

//we'll create a point in the base_laser frame that we'd like to transform to the base_link frame
  geometry_msgs::PointStamped laser_point;
  laser_point.header.frame_id = "base_laser";
  //we'll just use the most recent transform available for our simple example
  laser_point.header.stamp = ros::Time();
  //just an arbitrary point in space
  laser_point.point.x = 1.0;
  laser_point.point.y = 0.2;
  laser_point.point.z = 0.0;

在这里,我们将以几何_msgs::PointStamped 的形式创建点。信息名称末尾的 "Stamped"(加盖)仅表示它包含一个标头,允许我们将时间戳和帧 ID 与信息关联起来。我们将把 laser_point 信息的 stamp 字段设置为 ros::Time(),这是一个特殊的时间值,允许我们向 TransformListener 询问最新的可用变换。至于报文头的 frame_id 字段,我们将其设置为 "base_laser",因为我们要在 "base_laser "坐标系中创建一个点。最后,我们将为 point.... 设置一些数据,取值为 x:1.0、y: 0.2 和 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());
  }

现在,我们已经在 "base_laser "坐标系中找到了点,我们要将它转换到 "base_link "坐标系中。为此,我们将使用 TransformListener 对象,并调用带有三个参数的 transformPoint():我们要将点转换到的坐标系名称(本例中为 "base_link")、我们要转换的点以及转换后点的存储空间。因此,在调用 transformPoint() 之后,base_point 保存的信息与之前 laser_point 保存的信息相同,只是现在保存在 "base_link "坐标系中。

catch(tf::TransformException& ex){
    ROS_ERROR("Received an exception trying to transform a point from \"base_laser\" to \"base_link\": %s", ex.what());
  }

如果由于某种原因,base_laser → base_link 变换不可用(可能是 tf_broadcaster 没有运行),那么当我们调用 transformPoint() 时,TransformListener 可能会抛出异常。为确保从容应对,我们将捕获异常并打印出错误信息提供给用户。

7.5 构建代码

现在我们已经写好了我们的小示例,我们需要构建它。打开由 roscreate-pkg 自动生成的 CMakeLists.txt 文件,在文件底部添加以下几行。

add_executable(tf_broadcaster src/tf_broadcaster.cpp)
add_executable(tf_listener src/tf_listener.cpp)
target_link_libraries(tf_broadcaster ${catkin_LIBRARIES})
target_link_libraries(tf_listener ${catkin_LIBRARIES})

接下来,确保保存文件并构建软件包。

cd %TOP_DIR_YOUR_CATKIN_WS%
catkin_make

7.6 运行代码

好了......我们都建好了。让我们试试看,看看 ROS 上到底发生了什么。在本节中,你需要打开三个终端。

在第一个终端,运行一个核心。

roscore

在第二个终端,我们将运行 tf_broadcaster

rosrun robot_setup_tf tf_broadcaster

好极了。现在我们使用第三个终端运行 tf_listener,将模拟点从 "base_laser "坐标系转换到 "base_link "坐标系。

rosrun robot_setup_tf tf_listener

如果一切顺利,您将看到以下输出,显示一个点每秒一次从 "base_laser "坐标系转换到 "base_link "坐标系。

[ INFO] 1248138528.200823000: base_laser: (1.00, 0.20. 0.00) -----> base_link: (1.10, 0.20, 0.20) at time 1248138528.19
[ INFO] 1248138529.200820000: base_laser: (1.00, 0.20. 0.00) -----> base_link: (1.10, 0.20, 0.20) at time 1248138529.19
[ INFO] 1248138530.200821000: base_laser: (1.00, 0.20. 0.00) -----> base_link: (1.10, 0.20, 0.20) at time 1248138530.19
[ INFO] 1248138531.200835000: base_laser: (1.00, 0.20. 0.00) -----> base_link: (1.10, 0.20, 0.20) at time 1248138531.19
[ INFO] 1248138532.200849000: base_laser: (1.00, 0.20. 0.00) -----> base_link: (1.10, 0.20, 0.20) at time 1248138532.19

恭喜你,你刚刚为平面激光器编写了一个成功的变换广播器。下一步是将本示例中使用的 PointStamped 替换为通过 ROS 传输的传感器流。幸运的是,你可以在这里找到关于这部分过程的文档。

八、在机器人上使用机器人状态发布器 (robot state publisher)

当您使用的机器人有许多相关坐标系时,将它们全部发布到 tf 就成了一项相当艰巨的任务。机器人状态发布器就是一个能帮您完成这项工作的工具。

机器人状态发布器可帮助您向 tf 变换库广播机器人的状态。机器人状态发布器内部有一个机器人运动学模型;因此,在给定机器人关节位置的情况下,机器人状态发布器可以计算并发布机器人每个环节的三维姿态。

您可以将机器人状态发布器作为独立的 ROS 节点或库来使用:

8.1作为 ROS 节点运行

8.1.1 机器人状态发布器(robot_state_publisher)

运行机器人状态发布器最简单的方法就是将其作为一个节点。对于普通用户来说,建议使用这种方式。运行机器人状态发布器需要两样东西:

  • 参数服务器上加载的 urdf xml 机器人描述。
  • 以 sensor_msgs/JointState 形式发布关节位置的源。

请阅读以下章节,了解如何配置机器人状态发布器的参数和主题。

8.1.1.1 订阅主题

joint_states (sensor_msgs/JointState)

关节位置信息

8.1.1.2 参数

机器人描述(urdf 地图)

urdf xml 机器人描述。可通过 `urdf_model::initParam` 访问

tf_prefix (string)

设置 tf 前缀,用于按名称空间发布变换。详情请参阅 tf_prefix。

publish_frequency (double)

状态发布器的发布频率,默认为 50Hz。

8.1.2 启动文件示例

设置好 XML 机器人描述和关节位置信息源后,只需创建一个类似的启动文件即可:

<launch>
   <!-- Load the urdf into the parameter server. -->
   <param name="my_robot_description" textfile="$(find mypackage)/urdf/robotmodel.xml"/>
    
   <node pkg="robot_state_publisher" type="robot_state_publisher" name="rob_st_pub" >
      <remap from="robot_description" to="my_robot_description" />
      <remap from="joint_states" to="different_joint_states" />
    </node>
  </launch>

ROS - tf(五)+https://developer.aliyun.com/article/1585203

相关实践学习
使用ROS创建VPC和VSwitch
本场景主要介绍如何利用阿里云资源编排服务,定义资源编排模板,实现自动化创建阿里云专有网络和交换机。
阿里云资源编排ROS使用教程
资源编排(Resource Orchestration)是一种简单易用的云计算资源管理和自动化运维服务。用户通过模板描述多个云计算资源的依赖关系、配置等,并自动完成所有资源的创建和配置,以达到自动化部署、运维等目的。编排模板同时也是一种标准化的资源和应用交付方式,并且可以随时编辑修改,使基础设施即代码(Infrastructure as Code)成为可能。 产品详情:https://www.aliyun.com/product/ros/
目录
相关文章
|
25天前
|
缓存 数据可视化 机器人
07 ROS的TF坐标管理工具
本文详细介绍了ROS(机器人操作系统)中TF(Transform)坐标管理工具的使用方法,包括如何监听和广播坐标变换消息,使用相关命令行工具查看TF关系,以及如何通过编写节点代码来创建TF广播器和监听器,并展示了如何在launch文件中配置TF相关的节点。
39 0
|
1月前
|
机器人 定位技术 C++
ROS - tf(五)
ROS - tf(五)
46 0
|
1月前
|
存储 传感器 机器人
|
1月前
|
Python
|
1月前
|
数据可视化 Ubuntu 机器人
ROS - tf(一)
ROS - tf(一)
28 0
|
机器人 API
[ROS基础] --- TF坐标转换
[ROS基础] --- TF坐标转换
166 0
|
C++
ROS学习-写一个tf broadcaster(C++)
ROS学习-写一个tf broadcaster(C++)
167 0
ROS学习-写一个tf broadcaster(C++)
|
数据可视化 Ubuntu 机器人
ROS学习-tf介绍
ROS学习-tf介绍
238 0
ROS学习-tf介绍
|
传感器 存储 机器人
ROS TF 将传感器数据转换为机器人坐标系下
ROS TF 将传感器数据转换为机器人坐标系下
ROS TF 将传感器数据转换为机器人坐标系下
|
XML C++ 数据格式
【古月21讲】ROS入门系列(4)——参数使用与编程方法、坐标管理系统、tf坐标系广播与监听的编程实现、launch启动文件的使用方法
【古月21讲】ROS入门系列(4)——参数使用与编程方法、坐标管理系统、tf坐标系广播与监听的编程实现、launch启动文件的使用方法
251 0
【古月21讲】ROS入门系列(4)——参数使用与编程方法、坐标管理系统、tf坐标系广播与监听的编程实现、launch启动文件的使用方法

推荐镜像

更多