【ROS2RUN源码解析:解决ROS2 run命令找不到问题的详细流程】

文章目录

    • 概要
    • 整体架构流程
    • 技术名词解释
    • 小结

概要

当你在使用ROS2时遇到找不到可执行文件的错误时,首先需要执行以下步骤来诊断问题。首先,使用命令printenv AMENT_PREFIX_PATH(或者ros2 pkg prefix加上包的名称)来检查你的功能包路径是否被正确设置。如果路径没有被正确设置,检查你的环境变量和source命令是否配置正确。接着,前往AMENT_PREFIX_PATH/lib/package_name/路径下,查看是否生成了可执行文件。确保你自己的可执行文件命名正确,并且拥有执行权限。如果以上步骤都没有问题,那么问题可能出现在其他地方,需要进一步检查。

另外,还有两个关键点需要特别注意:

ROS2 run命令参数解析: ROS2 run命令会对传递的参数进行解析和处理。这意味着,如果你在使用ros2 run时带有参数,ROS2会按照一定的规则进行解析,确保参数被正确传递给可执行文件。这个解析过程通常可以在源码中找到,具体函数可能被命名为add_arguments,你可以在相关源码文件中查找。

在子进程中执行: ROS2 run命令实际上是在一个子进程中执行的。这意味着,当你调用ros2 run时,它会启动一个新的子进程来运行你指定的可执行文件。这种操作通常通过subprocess.Popen(cmd)这样的方式实现。子进程的独立性确保了在执行期间不会影响到主进程,同时也提供了更好的错误隔离和管理。

因此,在排查问题时,除了注意环境变量和可执行文件的路径,也要关注ROS2 run命令对参数的处理方式,以及它是如何在子进程中执行的。这些细节通常能够帮助你更快地定位问题所在,从而进行有效的修复。

整体架构流程

在ROS 2中,ros2 run 命令(源码地址:https://github.com/ros2/ros2cli.git)是一个至关重要的工具,它允许用户在指定的ROS包内运行可执行文件。本文将深入分析 ros2 run 命令的源代码,以及其中涉及的关键函数,以帮助您更好地理解其内部工作原理。

在我们的探讨中,将聚焦于 RunCommand 类及其所依赖的两个重要函数:get_executable_path 和 run_executable。通过解析这些关键函数,我们将揭示 ros2 run 命令背后的核心逻辑。

技术名词解释

在ROS 2中,ros2 run 命令的实现是通过 ros2cli 包提供的。具体而言,它的配置是通过 entry_points 来完成的。以下是相关的配置代码示例:

entry_points={
    'ros2cli.command': [
        'run = ros2run.command.run:RunCommand',
    ],
}

这段代码告诉ROS 2,当用户运行 ros2 run 命令时,应该调用 ros2run.command.run:RunCommand 这个类来处理。这种配置机制使得命令行工具的扩展变得非常灵活,开发者可以通过配置文件来定义新的命令和相应的处理类,从而实现命令行工具的功能扩展。

RunCommand 类是 ros2 run 命令的核心部分。以下是对 RunCommand 类的详细解读:

from argparse import REMAINDER
import shlex

from ros2cli.command import CommandExtension
from ros2pkg.api import package_name_completer
from ros2pkg.api import PackageNotFound
from ros2run.api import ExecutableNameCompleter
from ros2run.api import get_executable_path
from ros2run.api import MultipleExecutables
from ros2run.api import run_executable

class RunCommand(CommandExtension):
    """Run a package specific executable."""

    # add_arguments函数用于定义命令行参数
    def add_arguments(self, parser, cli_name):
        # --prefix参数用于指定命令的前缀
        arg = parser.add_argument(
            '--prefix',
            help='Prefix command, which should go before the executable. '
                 'Command must be wrapped in quotes if it contains spaces '
                 "(e.g. --prefix 'gdb -ex run --args').")
        try:
            from argcomplete.completers import SuppressCompleter
        except ImportError:
            pass
        else:
            arg.completer = SuppressCompleter()
        # package_name参数用于指定ROS包的名称
        arg = parser.add_argument(
            'package_name',
            help='Name of the ROS package')
        arg.completer = package_name_completer
        # executable_name参数用于指定可执行文件的名称
        arg = parser.add_argument(
            'executable_name',
            help='Name of the executable')
        arg.completer = ExecutableNameCompleter(
            package_name_key='package_name')
        # argv参数用于传递给可执行文件的额外参数
        parser.add_argument(
            'argv', nargs=REMAINDER,
            help='Pass arbitrary arguments to the executable')

    # main函数是命令的入口点,处理用户输入并执行相应的操作
    def main(self, *, parser, args):
        try:
            # 获取可执行文件的路径
            path = get_executable_path(
                package_name=args.package_name,
                executable_name=args.executable_name)
        except PackageNotFound:
            raise RuntimeError(f"Package '{args.package_name}' not found")
        except MultipleExecutables as e:
            msg = 'Multiple executables found:'
            for p in e.paths:
                msg += f'\n- {p}'
            raise RuntimeError(msg)
        if path is None:
            return 'No executable found'
        prefix = shlex.split(args.prefix) if args.prefix is not None else None
        # 运行可执行文件
        return run_executable(path=path, argv=args.argv, prefix=prefix)

在这段代码中,RunCommand 类继承自 CommandExtension 类,是一个用于解析命令行参数、查找可执行文件路径以及执行可执行文件的关键部分。以下是对这个类的主要功能进行解释:

add_arguments 函数定义了命令行参数。它包括 --prefix 参数(用于指定命令的前缀)、package_name 参数(指定ROS包的名称)、executable_name 参数(指定可执行文件的名称)以及 argv 参数(用于传递给可执行文件的额外参数)。

main 函数是命令的入口点。当用户输入命令并按下回车时,这个函数被调用。在 main 函数中,首先通过 get_executable_path 函数获取可执行文件的路径。如果找不到指定的包,会引发 PackageNotFound 异常。如果找到多个可执行文件,会引发 MultipleExecutables 异常。然后,如果找到可执行文件,会使用 run_executable 函数运行它。用户传递的前缀命令会被添加到执行命令之前。

这样,RunCommand 类负责处理用户输入的命令行参数,查找并运行相应的可执行文件。这个类的设计使得 ros2 run 命令具备了灵活性和可扩展性,可以方便地适应不同的使用场景。

get_executable_path 函数:

def get_executable_path(*, package_name, executable_name):
    paths = get_executable_paths(package_name=package_name)
    paths2base = {}
    for p in paths:
        basename = os.path.basename(p)
        if basename == executable_name:
            # 选择完全匹配的可执行文件
            paths2base[p] = basename
        elif sys.platform == 'win32':
            # 检查PATHEXT中列出的扩展名以进行匹配(无扩展名)
            pathext = os.environ.get('PATHEXT', '').lower().split(os.pathsep)
            ext = os.path.splitext(basename)[1].lower()
            if ext in pathext and basename[:-len(ext)] == executable_name:
                # 选择有已知扩展名的匹配项
                paths2base[p] = basename
    if not paths2base:
        return None
    if len(paths2base) > 1:
        raise MultipleExecutables(paths2base.keys())
    return list(paths2base.keys())[0]

get_executable_path 函数用于查找指定ROS包内的可执行文件的路径。它的主要功能是在给定的ROS包内寻找与 executable_name 匹配的可执行文件。函数会根据操作系统和文件扩展名来选择最合适的可执行文件。具体步骤如下:

获取指定ROS包内的所有可执行文件路径。
遍历每个路径,检查文件名是否与 executable_name 完全匹配。如果匹配,将该路径作为匹配项。
如果在Windows系统下,并且文件是Python脚本(.py 文件),检查文件扩展名是否在PATHEXT 环境变量中,如果是,则也将该路径作为匹配项。
如果没有找到匹配项,返回 None。
如果找到多个匹配项,抛出 MultipleExecutables 异常。
如果只找到一个匹配项,返回该路径。

run_executable 函数:

def run_executable(*, path, argv, prefix=None):
    cmd = [path] + argv

    # 在Windows上,Python脚本通过解释器调用
    if os.name == 'nt' and path.endswith('.py'):
        cmd.insert(0, sys.executable)

    if prefix is not None:
        cmd = prefix + cmd

    process = subprocess.Popen(cmd)
    while process.returncode is None:
        try:
            process.communicate()
        except KeyboardInterrupt:
            # 子进程也会收到信号并应该关闭
            # 因此我们继续,直到进程完成
            pass
    if process.returncode != 0:
        if -process.returncode in signal.valid_signals() and os.name == 'posix':
            # 负值 -N 表示子进程由信号 N 终止
            print(ROS2RUN_MSG_PREFIX, signal.strsignal(-process.returncode))
        else:
            #

 打印一般的失败消息
            print(ROS2RUN_MSG_PREFIX, 'Process exited with failure %d' % (process.returncode))
    return process.returncode

run_executable 函数用于执行指定的可执行文件。它的主要功能是构建命令,并在子进程中运行该命令。函数还能够处理命令前缀(例如,gdb),并捕获子进程的输出。具体步骤如下:

构建命令,包括可执行文件路径和传递给该可执行文件的参数。
如果在Windows系统下,且可执行文件是Python脚本(.py 文件),在命令之前插入Python解释器路径。
如果有命令前缀(prefix),将命令前缀和命令拼接在一起。
使用 subprocess.Popen 创建一个新的子进程,运行构建好的命令。
等待子进程执行完毕,并捕获其输出。
如果子进程返回的退出码不为0,根据退出码输出相应的错误消息。
返回子进程的退出码。

这两个函数共同确保了 ros2 run 命令的功能:通过 get_executable_path 查找可执行文件路径,然后通过 run_executable 在子进程中运行该可执行文件,最终实现了ROS包内特定可执行文件的执行功能。

在ROS 2中,ros2 run 命令是一个重要的工具,允许用户在特定的ROS包内运行可执行文件。当我们使用这个命令时,ROS 2会通过调用 get_executable_paths 函数来查找指定ROS包内的可执行文件路径。这个函数的核心部分如下所示:

def get_executable_paths(*, package_name):
    prefix_path = get_prefix_path(package_name)
    if prefix_path is None:
        raise PackageNotFound(package_name)
    base_path = os.path.join(prefix_path, 'lib', package_name)
    executable_paths = []
    for dirpath, dirnames, filenames in os.walk(base_path):
        # 忽略以.开头的文件夹
        dirnames[:] = [d for d in dirnames if d[0] not in ['.']]
        dirnames.sort()
        # 选择可执行文件
        for filename in sorted(filenames):
            path = os.path.join(dirpath, filename)
            if os.access(path, os.X_OK):
                executable_paths.append(path)
    return executable_paths

这段代码的关键步骤在于 base_path = os.path.join(prefix_path, ‘lib’, package_name)。在这一步,可执行文件被搜索在前缀路径下的 lib 目录,然后再进入对应的功能包名称文件夹。在这个路径下,函数遍历所有文件,并选出具有执行权限的文件,将其路径添加到 executable_paths 列表中。

至于 prefix 是从哪里来的,这里有一个环境变量定义:AMENT_PREFIX_PATH。在使用 source 命令后,AMENT_PREFIX_PATH 会被设置为相应的路径,将包含可执行文件的路径添加进去。你可以使用 ros2 pkg prefix package_name 命令来确认一个功能包的 prefix。

因此,当你遇到找不到可执行文件的错误时,首先可以运行 printenv AMENT_PREFIX_PATH 命令,查看环境变量是否包含你的功能包路径。如果没有找到,检查是否正确使用了 source 命令,以及在 install 目录下是否存在该功能包。如果存在,继续检查第二步,打开对应路径 AMENT_PREFIX_PATH/lib/package_name/,看看是否有生成可执行文件,并且确认可执行文件的名称是否正确。如果名称正确,最后检查文件是否具有执行权限。

【ROS2RUN源码解析:解决ROS2 run命令找不到问题的详细流程】_第1张图片

小结

总结来说,当在ROS 2中使用 ros2 run 命令时,确保按照以下步骤排查问题,以便成功找到并执行指定的可执行文件:

检查环境变量 AMENT_PREFIX_PATH: 运行 printenv AMENT_PREFIX_PATH 命令,确认环境变量是否包含你的功能包路径。如果没有找到,检查是否正确使用了 source 命令,以及在 install 目录下是否存在该功能包。

确认功能包的 prefix: 使用 ros2 pkg prefix package_name 命令确认功能包的 prefix。这个 prefix 是 AMENT_PREFIX_PATH 中的一个子路径,用于指定可执行文件的位置。

检查可执行文件的路径: 确认可执行文件是否位于 AMENT_PREFIX_PATH/lib/package_name/ 目录下。这是 ros2 run 命令搜索可执行文件的默认路径。

确认可执行文件的名称和权限: 确保可执行文件的名称正确,与在 ros2 run 命令中输入的名称一致。同时,确保文件具有执行权限,可以通过 chmod +x filename 命令来添加执行权限。

通过以上步骤的逐一排查,可以确保 ros2 run 命令能够顺利找到并执行指定的可执行文件,避免由于文件路径或权限问题引起的执行错误。

你可能感兴趣的:(人工智能,Ubuntu系统,#,学习笔记,linux,Ubuntu,机器学习,YOLO)