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

news/2024/7/11 0:03:12 标签: linux, Ubuntu, 机器学习, YOLO

文章目录

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

概要

当你在使用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/,看看是否有生成可执行文件,并且确认可执行文件的名称是否正确。如果名称正确,最后检查文件是否具有执行权限。

在这里插入图片描述

小结

总结来说,当在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 命令能够顺利找到并执行指定的可执行文件,避免由于文件路径或权限问题引起的执行错误。


http://www.niftyadmin.cn/n/5092050.html

相关文章

NetSuite ERP系统健康检查

这个题目来自最近的一个项目感受,“上线即停滞”。这是在中小型企业十分普遍的一个情况,一旦上线后,基本上信息化的建设就停止了。这是一个中小企业信息化的一个特点,因为其IT力量比较弱,所以在信息化的推动中缺乏话语…

CSS 样式简写

在CSS中有许多简写的样式,它们被广泛使用。简写最好按照如下顺序进行书写 font font: font-style font-weight font-size/line-height font-familyfont-style italic//斜体 normal//正常字体(默认)font-weight 一般填写数字 400 normal(默认值) 700 bold(默认值)f…

c#设计模式-行为型模式 之 访问者模式

🚀简介 封装一些作用于某种数据结构中的各元素的操作,它可以在不改变这个数据结构的前提下定义作用于这些元素的新的操作。 访问者模式包含以下主要角色 : 抽象访问者(Visitor)角色:定义了对每一个元素 (E…

CSS之实现线性渐变背景

1. background: linear-gradient() background: linear-gradient是CSS中用于创建线性渐变背景的属性,这个属性允许你定义一个在元素的背景中进行渐变的效果,可以从一个颜色过渡到另一个颜色。 基本语法 background: linear-gradient(direction, color-…

基于Springboot宠物医院管理系统

项目环境: mysql5.7 jdk1.8

ubuntu20 安装 cmake 3.27

1. 下载cmake3.27 建议从cmake官网下载安装,虽然比较慢,但从清华镜像里下载的cmake文件不全。 我下载的是:cmake-3.27.7.tar.gz 博客 ubuntu安装cmake的三种方法(超方便!)-CSDN博客 里面提供了三种方法&am…

P34~36第八章相量法

8.1复数 复数可表示平面矢量、也可表示正弦量。特别是: 当复数表示正弦量的时候,此时复数称为相量。 8.2复数运算 复数除法也可看做乘法,乘法的几何意义是旋转(辐角相加)( e^x e^y e^xy),同时伸缩(模变…

【opencv】windows10下opencv4.8.0-cuda Python版本源码编译教程

【opencv】windows10下opencv4.8.0-cuda Python版本源码编译教程 提示:博主取舍了很多大佬的博文并亲测有效,分享笔记邀大家共同学习讨论 文章目录 【opencv】windows10下opencv4.8.0-cuda Python版本源码编译教程前言准备工具anaconda/cuda/cudnnanaconda创建环境(选做)安装原…