< >
Home » ROS2与Navigation2入门教程 » ROS2与Navigation2入门教程-在ROS 2/Nav2中获取错误回溯信息

ROS2与Navigation2入门教程-在ROS 2/Nav2中获取错误回溯信息

说明:

  • 介绍如何获取ROS 2和Nav2错误回溯信息的一系列方法

概述

  • 本教程将会说明获取ROS 2和Nav2错误回溯信息的一系列方法。

  • 有很多方法可以实现ROS 2和Nav2错误回溯,但对于没有GDB经验的C++开发人员新手来说,这是一个很好的起点。

  • 下面这些步骤会向ROS 2使用者说明在遇到问题时如何修改 Nav2软件堆栈以从特定服务器获取错误跟踪信息。本教程适用于仿真机器人和物理机器人。

  • 本教程将会涵盖如何使用ros2 run命令从特定节点、使用ros2 launch命令从含有单个节点的启动文件以及更复杂的节点组合中获取错误回溯信息。

  • 在本教程结束时,当遇到ROS 2中的服务器崩溃时,应该就有能力获取其错误回溯信息。

前提条件

  • GDB(GNU symbolic debugger)是Unix系统上最流行的C++调试器。它可用于确定崩溃的原因并跟踪线程。它还可用于在代码中添加断点(breakpoints)以检查软件中某个特定点在内存中的值。

  • 对于所有使用C/C++的软件开发人员来说,使用GDB都是一项关键技能。许多IDE会内置某种调试器或分析器,但对于ROS,可供选择的IDE很少。

  • 因此,很重要的一点就是要了解如何使用这些可用的原始调试工具,而不是依赖IDE来提供调试工具。

  • 而且,了解这些调试工具是C/C++开发的一项基本技能,如果您更改开发人员的这个角色且不再有能力使用调试工具,或者正在通过ssh会话用远程调试工具进行动态开发,则将调试工具留给IDE可能会出问题。

  • 幸运的是,在掌握了基础知识之后,使用GDB就相当简单了。第一步要将-g添加到要分析/调试的ROS软件包的编译器标志中。这个标志会构建GDB和valgrind(Linux内存调试工具)可以读取的调试符号,以告诉您该项目中失败的具体代码行及失败原因。如果没有设置这个标志,虽然仍可以获取错误回溯信息,但不会提供失败的代码行号。完成调试后请务必删除这个标志,因为在运行时它会降低性能。

  • 将下面这一行代码添加到您项目的CMakeLists.txt文件中应该就可以做到这一点。

  • 如果您的项目中已经有add_compile_options()代码行,则可以简单地向该代码行中添加-g标志即可。

  • 然后简单地用这个软件包运行colcon build --packages-select <package-name>命令来重建您的工作空间。

  • 添加了-g标志后编译可能需要比平时更长的时间。

add_compile_options(-g)
  • 现在您已准备就绪可以调试代码了!如果这是一个非ROS项目,这时可能需要执行下面这条命令。启动一个GDB会话并告诉程序立即运行。一旦程序崩溃,就会返回一个由(gdb)表示的gdb会话提示符。可以在这个提示符下获取您感兴趣的信息。

  • 但是,由于这是一个包含大量节点配置和其他任务的ROS项目,因此对于初学者或那些不喜欢大量命令行工作和了解文件系统的人来说,这并不是一个很好的选择。

gdb ex run --args /path/to/exe/program
  • 下面几节将会描述使用基于ROS 2的系统可能会遇到的3种主要情形。请阅读最符合您所遇问题情形的一节。

从节点进行回溯

  • 就像在刚才那个非ROS项目示例中一样,需要在启动ROS 2节点之前设置GDB会话。

  • 虽然可以在具有ROS 2文件系统的一些知识的情况下通过命令行进行GDB会话设置,但还可以使用Open Robotics的好心人提供的--prefix启动选项。

  • 选项--prefix会在ros2命令之前执行一些代码,允许插入一些信息。

  • 如果尝试执行类似于“前提条件”一节中的示例命令gdb ex run --args ros2 run ,则会发现它找不到ros2命令。

  • 甚至如果您更聪明而尝试对工作空间进行source,还是会发现因类似原因而失败。

  • 实际上,不必回头去查找可执行文件的安装路径并全部重新输入,而是使用--prefix选项。这允许使用您习惯的同一条ros2 run命令,而不必担心GDB的一些细节。具体命令语法如下:

ros2 run --prefix 'gdb -ex run --args' <pkg> <node> --all-other-launch arguments
  • 和前面一样,这个前缀会启动一个GDB会话并运行您请求的带有所有附加命令行参数的节点。

  • 现在系统应该会让您的节点运行,并且应该会与一些调试打印信息一起运行。

  • 服务器崩溃,就会看到如下提示符。此时,就可以获取错误回溯信息了。

(gdb)
  • 在这个会话中,键入backtrace命令,这样它就会为您提供一个错误回溯信息。

  • 根据您的需要复制这些错误回溯信息。

  • 下面是一个错误回溯信息的示例:

(gdb) backtrace

#0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50

#1 0x00007ffff79cc859 in __GI_abort () at abort.c:79

#2 0x00007ffff7c52951 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6

#3 0x00007ffff7c5e47c in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6

#4 0x00007ffff7c5e4e7 in std::terminate() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6

#5 0x00007ffff7c5e799 in __cxa_throw () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6

#6 0x00007ffff7c553eb in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6

#7 0x000055555555936c in std::vector<int, std::allocator<int> >::_M_range_check (

this=0x5555555cfdb0, __n=100) at /usr/include/c++/9/bits/stl_vector.h:1070

#8 0x0000555555558e1d in std::vector<int, std::allocator<int> >::at (this=0x5555555cfdb0,

__n=100) at /usr/include/c++/9/bits/stl_vector.h:1091

#9 0x000055555555828b in GDBTester::VectorCrash (this=0x5555555cfb40)

at /home/steve/Documents/nav2_ws/src/gdb_test_pkg/src/gdb_test_node.cpp:44

#10 0x0000555555559cfc in main (argc=1, argv=0x7fffffffc108)

at /home/steve/Documents/nav2_ws/src/gdb_test_pkg/src/main.cpp:25
  • 在这个示例中,应该从底部开始按照以下方式阅读错误回溯:

    • 在主(main)函数中的第25行,调用了一个函数VectorCrash。

    • 在VectorCrash函数中的第44行,在Vector的输入为100的at()方法中崩溃。

    • 在从范围检查失败中抛出异常后,在STL vector第1091行上的at()中崩溃。

  • 需要一些时间来习惯对这些跟踪的阅读,但一般来说,从底部开始并沿着堆栈向上跟踪,直到看到它崩溃的那一行。

  • 然后就可以推断出崩溃的原因。

  • 使用GDB完成错误回溯后,请键入quit,这样就会退出该会话并终止所有仍在运行的进程。

  • 最后系统可能会询问是否要关闭一些线程,回答“yes”即可。

从启动文件进行回溯

  • 就像在刚才那个非ROS项目示例中一样,需要在启动 ROS 2启动文件之前设置一个GDB会话。

  • 虽然可以通过命令行进行GDB会话设置,但还可以使用在ros2 run 节点示例中所采用的相同办法,只是现在使用的启动文件而不是可执行文件而已。

  • 在启动文件中,找到想要进行调试的节点。本节中假设启动文件只包含一个节点(且可能还有其他信息)。

  • 软件包launch_ros中使用的 Node函数可以接收一个字段prefix,该字段带有前缀参数列表。这里会在此处插入GDB代码段,但对节点示例进行了一项更改,即xterm的使用。xterm会弹出一个新的终端窗口来显示GDB并与之交互。

  • 之所以要这样做,是因为处理启动文件上的stdin这一议题(例如,如果您按下CTRL+C组合键,您是在与GDB对话还是在与launch对话?)。

  • 有关这一点的更多信息,请参阅这张工单。

  • 请参阅下面这个调试SLAM工具箱的示例。

start_sync_slam_toolbox_node = Node(
parameters=[
  get_package_share_directory("slam_toolbox") + '/config/mapper_params_online_sync.yaml',
  {'use_sim_time': use_sim_time}
],
package='slam_toolbox',
executable='sync_slam_toolbox_node',
name='slam_toolbox',
prefix=['xterm -e gdb -ex run --args'],
output='screen')
  • 和前面一样,这个前缀会启动一个GDB会话,但现在是在xterm中,并且会运行您请求的带有所有附加启动参数定义的启动文件。

  • 一旦服务器崩溃,就会在xterm会话中看到如下所示的提示符。此时,就可以获得错误回溯信息了。

(gdb)
  • 在这个会话中,键入backtrace命令,这样它就会提供一个错误回溯信息。根据需要复制这些错误回溯信息。有关示例,请参阅上一节中的示例跟踪。

  • 需要一些时间来习惯对这些跟踪的阅读,但一般来说,从底部开始并沿着堆栈向上跟踪,直到看到它崩溃的那一行。然后就可以推断出崩溃的原因。使用GDB完成错误回溯后,请键入quit,这样就会退出该会话并终止所有仍在运行的进程。

  • 最后系统可能会询问是否要关闭一些线程,回答“yes”即可。

从Nav Bringup软件包进行回溯

  • 使用具有多个节点的启动文件会有点不同,这样可以与GDB会话进行交互,而不会被同一终端中的其他日志记录所困扰。

  • 因此,在从更大型启动文件进行回溯时,最好把需要回溯的具体服务器节点从中独立出来并单独启动它。

  • 因此,对于这种情况,当看到想要调查的某个崩溃时,将这个服务器节点与其他节点分开是会有益处的。

  • 如果想要回溯的服务器节点是从一个嵌套的启动文件(例如包含另一个启动文件的启动文件)中启动的,则可能需要执行以下操作:

    • 注释掉启动文件中对父级启动文件进行包含的包含语句

    • 使用用于调试符号的-g标志重新编译感兴趣的软件包

    • 在终端中启动父级启动文件

    • 按照本教程“从启动文件进行回溯”一节中的说明,在另一个终端中启动服务器节点的启动文件。

  • 或者,如果感兴趣的服务器节点直接在这些文件(例如,您看到的Node、LifecycleNode 或位于ComponentContainer内部)中启动,则需要执行以下操作将该服务器节点与其他节点分开:

    • 注释掉节点对父级启动文件进行包含的包含语句

    • 使用用于调试符号的-g标志重新编译感兴趣的软件包

    • 在一个终端中启动父级启动文件

    • 按照本教程“从节点进行回溯”一节中的说明,在另一个终端中启动该服务器节点。

  • *注:请注意,在本例中,如果之前由启动文件提供重映射或参数文件,则可能需要对该节点进行重映射或向该节点提供参数文件。使用--ros-args选项可以为该节点指定新参数文件的路径、重映射或名称。有关所需命令行参数的更多信息,请参阅这个ROS 2教程。

  • 我们知道这可能会很痛苦,所以鼓励您将每个可能的节点都作为一个单独包含的启动文件来简化调试。一组示例参数可能是--ros-args -r __node:=<node_name> --params-file /absolute/path/to/params.yaml (这可以作为参数模板)。

  • 一旦服务器崩溃,就会在特定服务器的终端中看到如下所示的提示符。此时,就可以获取错误回溯信息了。

(gdb)
  • 在这个会话中,键入backtrace命令,这样它就会提供一个错误回溯信息。根据需要复制这些错误回溯信息。

  • 有关示例,请参阅上一节中的示例跟踪

  • 需要一些时间来习惯对这些跟踪的阅读,但一般来说,从底部开始并沿着堆栈向上跟踪,直到看到它崩溃的那一行。

  • 然后就可以推断出崩溃的原因。使用GDB完成错误回溯后,请键入quit,这样就会退出该会话并终止所有仍在运行的进程。

  • 最后系统可能会询问是否要关闭一些线程,回答“yes”即可。

对系统崩溃(crash)进行自动回溯

  • 库backward-cpp提供了漂亮的软件堆栈跟踪功能,而backward_ros封装器则简化了该库的集成使用。

  • 只需将该封装器添加为软件包的依赖项并在软件包的CMakeLists.txt文件中对该库使用find_package语句来查找它,这样上述两个backward库就会被植入到软件包的所有可执行文件和库中。

参考:

  • https://navigation.ros.org/tutorials/docs/get_backtrace.html

纠错,疑问,交流: 请进入讨论区点击加入Q群

获取最新文章: 扫一扫右上角的二维码加入“创客智造”公众号


标签: ros2与navigation2入门教程