在 ROS 2 / Nav2 中进行性能分析

概述

本文档介绍了在ROS 2 / Nav2中对应用程序进行分析的一种方法。分析的目的是生成可以分析的文件,以查看程序执行过程中计算时间和资源的使用情况。这对于确定程序中的瓶颈所在以及可能需要改进的地方非常有用。

以下步骤向 ROS 2 用户展示如何修改 Nav2 栈以获取关于特定服务器/算法的性能分析信息,以便更好地理解他们遇到的情况。本教程适用于仿真和物理机器人。

准备工作

本教程使用了两个工具,即``Valgrind``工具集中的callgrind和``kcachegrind``。Valgrind用于获取程序的分析信息,而kcachegrind则是用于解释这些信息并进行有用工作的可视化引擎。

因此,我们必须安装它们。

sudo apt install valgrind kcachegrind

有关更多信息,请参阅`Valgrind手册<https://valgrind.org/docs/manual/cl-manual.html>`_,其中包括可以用于指定更多信息的其他valgrind参数。

一般而言,要使用Valgrind,我们需要在编译时加入调试信息。可以通过将``-g``作为编译选项传递或将``CMAKE_BUILD_TYPE``设置为``Debug``或``RelWithDebInfo``来完成。然后,我们使用Valgrind运行程序,以便捕获运行时统计信息以供后续分析。这些信息存储在名为``callgrind.out.XXX``的文件中,其中后缀是进程的PID。使用kcachegrind可视化和分析程序执行的结果。

      # CMakeLists.txt
add_compile_options(-g)
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo
valgrind --tool=callgrind [your-program] [program options]

kcachegrind callgrind.out.XXX

来自节点的配置文件

与我们的通用示例一样,对于给定的节点,我们需要使用调试标志进行编译,以便捕获用于使用Valgrind进行分析的信息。这可以很容易地通过命令行完成。请注意,我们使用``--packages-select``仅对我们想要在节点内进行分析的软件包使用此标志进行编译。

colcon build --packages-select <packages of interest> --cmake-args -DCMAKE_BUILD_TYPE=RelWithDebInfo

可选地,您可以将以下行添加到要分析的软件包的``CMakeLists.txt``中。当您有一个包含许多软件包的工作空间,但只想使用单个``colcon build``调用编译带有调试信息的子集时,这可能更可取。

如果您想要插件的运行时分析结果,这一点对主机服务器和插件软件包都很重要。

add_compile_options(-pg)

在编译完任一种方法后,应该在独立的终端中运行此节点,以使其与系统的其他部分隔离开来。因此,不应该在与其他节点相同的进程中组合使用。要使用valgrind运行ROS 2节点,可以通过以下命令行完成:

ros2 run --prefix 'valgrind --tool=callgrind' <pkg> <node> --all-other-launch arguments

一个例子是使用特定的控制器插件加载对控制器服务器进行分析。``nav2_controller``和所需的插件软件包都使用调试标志进行编译。在下面的示例中,我们正在运行一个具有重新映射主题和参数文件路径的ROS 2节点:

ros2 run --prefix 'valgrind --tool=callgrind' nav2_controller controller_server --ros-args -r __node:=controller_server -r cmd_vel:=cmd_vel_nav --params-file /path/to/nav2_bringup/params/nav2_params.yaml

在收集足够的数据后,使用Control+C干净地退出进程。

从启动文件中进行分析

与节点示例中一样,当从launch文件对节点进行性能分析时,我们还必须使用调试标志进行编译。我们可以使用与命令行相同的valgrind调用方式,也可以在launch文件中使用launch前缀完成。

与我们之前的示例一样,这是我们如何在启动文件中启动``controller_server``节点。

start_controller_server_node = Node(
    parameters=[
      get_package_share_directory("nav2_bringup") + '/params/nav2_params.yaml',
      {'use_sim_time': use_sim_time}
    ],
    package='nav2_controller',
    executable='controller_server',
    name='controller_server',
    prefix=['xterm -e valgrind --tool=callgrind'],
    output='screen')

请注意,与以前一样,我们应该将此进程与其他进程隔离开来。因此,在对特定节点进行性能分析时,不应该在此launch文件中与任何其他节点一起运行,也不应该使用节点组合。

在收集足够的数据后,使用Control+C干净地退出进程。

来自Nav2 Bringup

由于Nav2的引导过程中每个启动文件都有多个节点(并且在``use_composition=true``的情况下,每个进程还有多个节点),因此需要从整个系统中分离出您感兴趣的特定节点进行性能分析。如前所述,一旦它们被隔离在启动文件或作为要在命令行上启动的节点中,就可以轻松运行它们以收集callgrind信息。

在Nav2中的步骤如下:

  • 从``navigation_launch.py``中删除服务器节点,并确保从文件中的组合和非组合选项中删除该节点

  • 在单独的launch文件中或使用``ros2 run``命令行界面,按照上述说明启动要进行性能分析的节点

  • 像往常一样启动Nav2,缺少的节点会被忽略

  • 在收集完数据后,使用Control+C干净地结束正在进行性能分析的进程以及其余的导航过程

重要的是在Nav2之前启动分析器节点,这样它可以接收生命周期管理器发出的信号以进行状态转换。

解释结果

一旦您获得了``callgrind``结果,无论您是通过节点、启动文件、Nav2还是其他方式进行分析,现在我们可以通过分析分析器的结果来识别性能瓶颈或潜在改进的领域。使用``kcachegrind``:

kcachegrind callgrind.out.XXX

这将打开一个看起来如下图所示的窗口。左侧显示了所有调用及其所使用的计算时间及其子函数所占的相对比例。

../../_images/kcachegrind.png

如果您在左侧边栏上选择顶级条目,然后在右侧工作区的底部选择“调用图”,它将显示计算时间花费的调用图,即方法调用的图形表示。这对于找到花费最多时间的方法非常有帮助。

../../_images/call_graph.png