Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Import modules in package in ROS2

I have created a package for ROS2 and I have added a Python repository I downloaded. The problem I am having is that in the original repository the modules from the own repo were imported directly while in mine I have to import them adding the ROS2 package name before the module, even though I am importing a module from the same repo, like:

import planner_pkg.SimpleOneTrailerSystem as SimpleOneTrailerSystem

while I would like:

import SimpleOneTrailerSystem

My ROS2 project structure is like:

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

package.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',
        ],
    },
)

like image 415
Hector Esteban Avatar asked Jan 25 '23 23:01

Hector Esteban


1 Answers

First, according to the Module Search Path docs, when you do import something, Python looks for that something in the following places:

  • From the built-in modules
  • sys.path, which is a list containing:
    • The directory of the input script
    • PYTHONPATH, which is an environment variable containing a list of directories
    • The installation-dependent default

Second, when you build your ROS2 Python package (by calling colcon build invoking the ament_python build type), your Python codes will be copied over to an install folder with a tree structure like this:

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

Now, when you do import SimpleOneTrailerSystem, Python will first search for it from the built-in modules, which for sure it won't find there. Next on the list is from sys.path. You can add a print(sys.path) at the top of planner_node.py to see something like this list:

['/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...
]

First on the sys.path list is the bin folder of the input script. There are only executables there, no SimpleOneTrailerSystem.py file/module, so that import will fail.

The next on the list would be the planner_pkg/lib/pythonX.X/site-packages, and as you can see from the tree structure above, there is a SimpleOneTrailerSystem.py module BUT it is under a planner_pkg folder. So a direct import like this

import SimpleOneTrailerSystem

will not work. You need to qualify it with the package folder like this:

import planner_pkg.SimpleOneTrailerSystem

There are 2 ways to get around this.

  1. Modify sys.path before import-ing SimpleOneTrailerSystem

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

    This approach adds the path to the planner_pkg install directory to the sys.path list so that you don't need to specify it in subsequent imports.

  2. Modify PYTHONPATH before running your ROS2 node

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

    This approach is almost the same as the first one, but there is no code change involved (and no rebuilding involved), as you only need to modify the PYTHONPATH environment variable.

like image 56
Gino Mempin Avatar answered Jan 28 '23 15:01

Gino Mempin