警告

您正在阅读的 ROS 2 文档版本已达到 EOL(生命周期结束),不再受官方支持。如果您想了解最新信息,请访问 Jazzy.

调试

目标 学习如何使用系统方法调试与 tf2 相关的问题。

辅导水平: 中级

时间 10 分钟

背景介绍

本教程将引导您完成调试典型 tf2 问题的步骤。它还将使用许多 tf2 调试工具,如 tf2_echo, tf2_monitor视图框架.本教程假定您已经完成了 学习 tf2 教程。

调试示例

1 设置和启动示例

在本教程中,我们将设置一个存在大量问题的演示应用程序。本教程的目的是采用系统化的方法查找并解决这些问题。首先,让我们创建源文件。

前往 learning_tf2_cpp 中创建的软件包 tf2 教程.内部 来源 目录下复制一份源文件 turtle_tf2_listener.cpp 并将其重命名为 turtle_tf2_listener_debug.cpp.

使用自己喜欢的文本编辑器打开文件,将第 67 行中的

标准::字符串 toFrameRel = "turtle2";;

标准::字符串 toFrameRel = "turtle3";;

和变化 lookupTransform() 在第 75-79 行调用

尝试 {
   transformStamped = tf_buffer_->;查找变换(
     toFrameRel,
     fromFrameRel,
     tf2::时间点零);
} 捕捉 (tf2::转换异常 及样品; ) {

尝试 {
   transformStamped = tf_buffer_->;查找变换(
     toFrameRel,
     fromFrameRel,
     ->;现在());
} 捕捉 (tf2::转换异常 及样品; ) {

然后保存对文件的更改。为了运行这个演示,我们需要创建一个启动文件 start_tf2_debug_demo.launch.py启动 子目录 learning_tf2_cpp:

 启动 舶来品 启动说明
 launch.actions 舶来品 声明启动参数
 launch.substitutions 舶来品 启动配置

 launch_ros.actions 舶来品 节点

捍卫 生成发射描述():
   返回 启动说明([
      声明启动参数(
         'target_frame', 默认值='turtle1';,
         描述='目标帧名称;
      ),
      节点(
         包装='turtlesim';,
         可执行='turtlesim_node',
         名字='sim';,
         产量='屏幕';
      ),
      节点(
         包装='learning_tf2_cpp',
         可执行='turtle_tf2_broadcaster';,
         名字='broadcaster1',
         参数=[
               {'turtlename';: 'turtle1';}
         ]
      ),
      节点(
         包装='learning_tf2_cpp',
         可执行='turtle_tf2_broadcaster';,
         名字='broadcaster2',
         参数=[
               {'turtlename';: 'turtle2';}
         ]
      ),
      节点(
         包装='learning_tf2_cpp',
         可执行='turtle_tf2_listener_debug';,
         名字='listener_debug',
         参数=[
               {'target_frame': 启动配置('target_frame')}
         ]
      ),
   ])

不要忘记添加 turtle_tf2_listener_debug 可执行 CMakeLists.txt 并构建软件包。

现在让我们运行它,看看会发生什么:

ros2 launch learning_tf2_cpp start_tf2_debug_demo.launch.py

现在你会看到海龟模拟器出现了。同时,如果运行 乌龟遥控钥匙 在另一个终端窗口中,可以使用箭头键来驱动 乌龟1 左右。

ros2 run turtlesim turtle_teleop_key

你还会注意到在左下角有第二只乌龟。如果演示程序运行正常,这第二只乌龟就应该跟在你用方向键指挥的乌龟后面。但事实并非如此,因为我们必须先解决一些问题。你应该注意到以下信息:

[turtle_tf2_listener_debug-4] [INFO] [1630223454.942322623] [listener_debug]:无法
将 turtle3 转换为 turtle1:"turtle3" 传递给 lookupTransform 参数 target_frame
不存在

2 查找 tf2 请求

首先,我们需要弄清楚要求 tf2 做什么。因此,我们要进入使用 tf2 的代码部分。打开 src/turtle_tf2_listener_debug.cpp 文件,并查看第 67 行:

标准::字符串 至框架关系 = "turtle3";;

和第 75-79 行:

尝试 {
   transformStamped = tf_buffer_->;查找变换(
     toFrameRel,
     fromFrameRel,
     ->;现在());
} 捕捉 (tf2::转换异常 及样品; ) {

在这里,我们向 tf2 提出实际请求。三个参数直接告诉了我们向 tf2 提出的请求:从帧变换 乌龟3乌龟1 当时 现在.

现在,让我们来看看为什么向 tf2 提出的请求会失败。

3 检查框架

首先,要弄清楚 tf2 是否知道我们在 乌龟3乌龟1我们将使用 tf2_echo 工具

ros2 run tf2_ros tf2_echo turtle3 turtle1

输出结果显示,帧 乌龟3 不存在:

[INFO] [1630223557.477636052] [tf2_echo]:Waiting for transform turtle3 -> turtle1:
传给 canTransform 参数 target_frame 的帧 ID "turtle3"无效。
乌有

那么存在哪些框架呢?如果您想用图形表示,请使用 视图框架 工具

ros2 run tf2_tools view_frames.py

打开生成的 框架.pdf 文件,可以看到以下输出:

././././_images/turtlesim_frames.png

很明显,问题在于我们要求从帧 乌龟3不存在。要修复这个错误,只需替换 乌龟3乌龟2 在第 67 行。

现在停止正在运行的演示,构建它,然后再次运行它:

ros2 launch turtle_tf2 start_debug_demo.launch.py

我们马上就会遇到下一个问题:

[turtle_tf2_listener_debug-4] [INFO] [1630223704.617382464] [listener_debug]:无法
将海龟 2 转换为海龟 1:查找需要推断未来。要求
时间为 1630223704.617054,但最新数据的时间为 1630223704.616726,当查找时
从 [乌龟 1] 帧转换到 [乌龟 2] 帧

4 检查时间戳

现在我们解决了帧名问题,是时候查看时间戳了。请记住,我们正在尝试获取 乌龟2乌龟1 在当前时间(即 现在).要获取计时统计数据,请调用 tf2_monitor 与相应的帧。

ros2 运行 tf2_ros tf2_monitor turtle2 turtle1

结果应该是这样的

结果:从乌龟 2 到乌龟 1
链为: turtle1
净延迟平均值 = 0.00287347:最大值 = 0.0167241

框架
帧: turtle1, 由 <无授权发布>, 平均延迟:0.000295833, Max Delay:0.000755072

所有广播公司:
节点:<无可用授权> 125.246 Hz,平均延迟:0.000290237 Max Delay:0.000786781

这里的关键部分是链的延迟,从 乌龟2乌龟1.输出结果显示平均延迟约为 3 毫秒。这意味着只有在 3 毫秒后,tf2 才能在海龟之间进行转换。因此,如果我们要求 tf2 在 3 毫秒前而不是 现在,tf2 有时就能给出答案。让我们快速测试一下,将第 75-79 行改为

尝试 {
   transformStamped = tf_buffer_->;查找变换(
     toFrameRel,
     fromFrameRel,
     ->;现在() - rclcpp::持续时间::从秒(0.1));
} 捕捉 (tf2::转换异常 及样品; ) {

在新代码中,我们需要 100 毫秒前海龟之间的变换。通常情况下,我们会使用更长的时间段,以确保变换能够到达。停止演示,构建并运行:

ros2 launch turtle_tf2 start_debug_demo.launch.py

你应该终于看到乌龟动了!

.././././_images/turtlesim_follow1.png

我们最后做的修复并不是你真正想要做的,只是为了确保这是我们的问题所在。真正的解决方案是这样的

尝试 {
   transformStamped = tf_buffer_->;查找变换(
     toFrameRel,
     fromFrameRel,
     tf2::时间点零);
} 捕捉 (tf2::转换异常 及样品; ) {

或者像这样

尝试 {
   transformStamped = tf_buffer_->;查找变换(
     toFrameRel,
     fromFrameRel,
     tf2::时间点());
} 捕捉 (tf2::转换异常 及样品; ) {

您可以在 利用时间 教程,使用方法如下:

尝试 {
   transformStamped = tf_buffer_->;查找变换(
     toFrameRel,
     fromFrameRel,
     ->;现在(),
     rclcpp::持续时间::从秒(0.05));
} 捕捉 (tf2::转换异常 及样品; ) {

摘要

在本教程中,您将学习如何使用系统方法调试与 tf2 相关的问题。您还学会了如何使用 tf2 调试工具,如 tf2_echo, tf2_monitor视图框架 来帮你调试这些 TF2 问题。