Импорт модулей в пакете в ROS2

Я создал пакет для ROS2 и добавил загруженный репозиторий Python. Проблема, с которой я сталкиваюсь, заключается в том, что в исходном репозитории модули из собственного репо были импортированы напрямую, а в моем мне нужно импортировать их, добавляя имя пакета ROS2 перед модулем, хотя я импортирую модуль из того же репо, например :

import planner_pkg.SimpleOneTrailerSystem as SimpleOneTrailerSystem

в то время как я хотел бы:

import SimpleOneTrailerSystem

Структура моего проекта ROS2 выглядит так:

ros2_ws
  src
    planner
      planner_pkg
        __init__.py
        SimpleOneTrailerSystem.py
        planner_node.py
        ...
      package.xml
      setup.py

пакет.xml

<?xml version="1.0"?>
<package format="2">
  <name>planner_pkg</name>
  <version>0.0.1</version>
  <description>This package contains algorithm for park planner</description>

  <maintainer email=""></maintainer>
  <license>Apache License 2.0</license>

  <exec_depend>rclpy</exec_depend>
  <exec_depend>std_msgs</exec_depend>

  <!-- These test dependencies are optional
  Their purpose is to make sure that the code passes the linters -->
  <test_depend>ament_copyright</test_depend>
  <test_depend>ament_flake8</test_depend>
  <test_depend>ament_pep257</test_depend>
  <test_depend>python3-pytest</test_depend>

  <export>
    <build_type>ament_python</build_type>
  </export>
</package>

setup.py:

from setuptools import setup

package_name = 'planner_pkg'

setup(
    name=package_name,
    version='0.0.0',
    packages=[package_name],
    data_files=[
        ('share/ament_index/resource_index/packages',
            ['resource/' + package_name]),
        ('share/' + package_name, ['package.xml']),
    ],
    install_requires=['setuptools'],
    zip_safe=True,
    author='',
    author_email='',
    maintainer='',
    maintainer_email='',
    keywords=['ROS'],
    classifiers=[
        'Intended Audience :: Developers',
        'License :: OSI Approved :: Apache Software License',
        'Programming Language :: Python',
        'Topic :: Software Development',
    ],
    description='Package containing examples of how to use the rclpy API.',
    license='Apache License, Version 2.0',
    tests_require=['pytest'],
    entry_points={
        'console_scripts': [
            'planner_node = planner_pkg.planner_node:main',
        ],
    },
)


person Hector Esteban    schedule 09.08.2019    source источник
comment
Размещение внешних зависимостей в вашем пакете обычно не рекомендуется. Не могли бы вы использовать такой инструмент, как pip, чтобы установить свою зависимость?   -  person Thomas Moulard    schedule 13.08.2019
comment
Под внешней зависимостью я подразумеваю модуль, который я разработал в другом пакете, а не модуль, разработанный другим человеком. Это объясняется в моем вопросе   -  person Hector Esteban    schedule 14.08.2019


Ответы (1)


Во-первых, в соответствии с путем поиска модуля. docs, когда вы делаете import something, Python ищет этот something в следующих местах:

  • Из встроенных модулей
  • sys.path, which is a list containing:
    • The directory of the input script
    • PYTHONPATH, которая представляет собой переменную среды, содержащую список каталогов.
    • Значение по умолчанию, зависящее от установки

Во-вторых, когда вы создаете пакет Python для ROS2 (вызывая colcon build с типом сборки ament_python), ваши коды Python будут скопированы в папку install с такой древовидной структурой:

install
...
├── planner_pkg
│   ├── bin
│   │   └── planner_node
│   ├── lib
│   │   └── python3.6
│   │       └── site-packages
│   │           ├── planner_pkg
│   │           │   ├── __init__.py
│   │           │   ├── planner_node.py
│   │           │   └── SimpleOneTrailerSystem.py
...

Теперь, когда вы делаете import SimpleOneTrailerSystem, Python сначала будет искать его во встроенных модулях, которых он там точно не найдет. Следующий в списке — от sys.path. Вы можете добавить print(sys.path) вверху planner_node.py, чтобы увидеть что-то вроде этого списка:

['/path/to/install/planner_pkg/bin', 
 '/path/to/install/planner_pkg/lib/python3.6/site-packages', 
 '/opt/ros/eloquent/lib/python3.6/site-packages', 
 '/usr/lib/python36.zip', 
 '/usr/lib/python3.6', 
 ...other Python3.6 installation-dependent dirs...
]

Первой в списке sys.path находится папка bin входного скрипта. Там только исполняемые файлы, нет файла/модуля SimpleOneTrailerSystem.py, так что импорт не удастся.

Следующим в списке будет planner_pkg/lib/pythonX.X/site-packages, и, как вы можете видеть из древовидной структуры выше, есть SimpleOneTrailerSystem.py модуль, НО он находится в папке planner_pkg. Итак, прямой импорт, подобный этому

import SimpleOneTrailerSystem

не будет работать. Вам нужно квалифицировать его с помощью папки пакета следующим образом:

import planner_pkg.SimpleOneTrailerSystem

Есть 2 способа обойти это.

  1. Изменить sys.path перед import SimpleOneTrailerSystem

    import sys
    sys.path.append("/path/to/install/planner_pkg/lib/python3.6/site-packages/planner_pkg")
    
    import SimpleOneTrailerSystem
    

    При таком подходе путь к каталогу planner_pkg install добавляется в список sys.path, чтобы вам не нужно было указывать его при последующем импорте.

  2. Измените PYTHONPATH перед запуском узла ROS2.

    $ colcon build
    $ source install/setup.bash
    $ export PYTHONPATH=$PYTHONPATH:/path/to/install/planner_pkg/lib/python3.6/site-packages/planner_pkg
    $ planner_node
    

    Этот подход почти такой же, как и первый, но не требует изменения кода (и не требует перестроения), так как вам нужно только изменить переменную среды PYTHONPATH.

person Gino Mempin    schedule 22.10.2019