ros2_python编程_多个文件python打包_目录拷贝_解决import错误问题ModuleNotFoundError

1.问题

ros2 python编写程序, 有多个python文件

  1. 如何打包多个python文件?
  2. 解决import错误问题
  3. 如何打包 有python目录结构的工程

1.ros2 多个python文件示例

代码目录结构, gitee 在线代码

tree 7_multi_file_setup/
7_multi_file_setup/
├── file1.py
├── main_node.py

file1.py

file1_config={"lili":123, "makr": 222}

main_node.py
导入file1.py 的 file1_config文件

# 导入本地模块
from file1 import *

def main():
    print("file1_config:", file1_config)

if __name__ == "__main__":
    main()

setup.py

from setuptools import find_packages, setup

package_name = '7_multi_file_setup'

setup(
    name=package_name,
    version='0.0.0',
    packages=find_packages(exclude=['test']),
    data_files=[
        ('share/ament_index/resource_index/packages',
            ['resource/' + package_name]),
        ('share/' + package_name, ['package.xml']),
    ],
    install_requires=['setuptools'],
    zip_safe=True,
    maintainer='liuj',
    maintainer_email='lj1637664504@outlook.com',
    description='TODO: Package description',
    license='TODO: License declaration',
    tests_require=['pytest'],
    entry_points={
        'console_scripts': [
            'main_node = 7_multi_file_setup.main_node:main',
        ],
    },
)

2.运行错误: import error "No module named ‘file1’ "

编译: colcon build --packages-select 7_multi_file_setup

运行:
source install/setup.bash
ros2 run 7_multi_file_setup main_node

Traceback (most recent call last):
  File "/home/liuj/3_work/7_ros-robot-example/ros2/install/7_multi_file_setup/lib/7_multi_file_setup/main_node", line 33, in <module>
    sys.exit(load_entry_point('7-multi-file-setup==0.0.0', 'console_scripts', 'main_node')())
  File "/home/liuj/3_work/7_ros-robot-example/ros2/install/7_multi_file_setup/lib/7_multi_file_setup/main_node", line 25, in importlib_load_entry_point
    return next(matches).load()
  File "/usr/lib/python3.10/importlib/metadata/__init__.py", line 171, in load
    module = import_module(match.group('module'))
  File "/usr/lib/python3.10/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1050, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 688, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 883, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/home/liuj/3_work/7_ros-robot-example/ros2/install/7_multi_file_setup/lib/python3.10/site-packages/7_multi_file_setup/main_node.py", line 2, in <module>
    from file1 import *
ModuleNotFoundError: No module named 'file1'
[ros2run]: Process exited with failure 1

3.问题原因: ros2 run的import 路径, 与 ros2 python 打包安装路径不一致

main_node.py 打印 import sys.path导入模块路径

import sys

def show_ros_module_import_path():
    # 获取当前脚本所在的目录
    print("ros2 import module path:")
    for path in sys.path:
        print(path)
show_ros_module_import_path()

# 导入本地模块
from file1 import *

def main():
    print("file1_config:", file1_config)

if __name__ == "__main__":
    main()

3.1 ros2 run 打印 import module 导入模块路径结果
ros2 run 7_multi_file_setup main_node

ros2 import module path:
/home/liuj/3_work/7_ros-robot-example/ros2/install/7_multi_file_setup/lib/7_multi_file_setup
/home/liuj/3_work/7_ros-robot-example/ros2/install/7_multi_file_setup/lib/python3.10/site-packages
/opt/ros/humble/lib/python3.10/site-packages
/opt/ros/humble/local/lib/python3.10/dist-packages
/usr/lib/python310.zip
/usr/lib/python3.10
/usr/lib/python3.10/lib-dynload
/home/liuj/.local/lib/python3.10/site-packages
/usr/local/lib/python3.10/dist-packages
/usr/lib/python3/dist-packages

3.2 实际的文件路径

tree install/7_multi_file_setup/
install/7_multi_file_setup/
├── lib
│ ├── 7_multi_file_setup
│ │ └── main_node
│ └── python3.10
│ └── site-packages
│ ├── 7_multi_file_setup
│ │ ├── file1.py
│ │ ├── main_node.py

在这里插入图片描述

4.问题小结

  • ros2 run 程序 import 导入模块路径: /home/liuj/3_work/7_ros-robot-example/ros2/install/7_multi_file_setup/lib/python3.10/site-packages
  • 实际文件路径: /home/liuj/3_work/7_ros-robot-example/ros2/install/7_multi_file_setup/lib/python3.10/site-packages/7_multi_file_setup/file1.py
  • ros2 run 运行时sys.path import路径, 与 实际 ros2/install/package 打包安装代码路径不一致

2.解决方法

解决方案1: 拷贝 python 代码到 import 路径

ros2 install安装路径: ros2/install/package/lib/package
示例安装路径: ros2/install/7_multi_file_setup/lib/7_multi_file_setup

修改setup.py, 拷贝python代码到 import路径
setup.py

++ import os
from glob import glob
from setuptools import find_packages, setup

    data_files=[
        ('share/ament_index/resource_index/packages',
            ['resource/' + package_name]),
        ('share/' + package_name, ['package.xml']),
++      (os.path.join('lib', package_name), glob('7_multi_file_setup/*.py')),
    ],
    install_requires=['setuptools'],
    zip_safe=True,

验证测试
编译: colcon build --packages-select 7_multi_file_setup

打包路径: ls install/7_multi_file_setup/lib/7_multi_file_setup/
2_添加模块路径_append_module_dir.py file1.py main_node main_node.py

运行结果: ros2 run 7_multi_file_setup main_node

file1_config: {‘lili’: 123, ‘makr’: 222}

解决方案2: sys.path 添加 install/package/lib/sitepackage代码路径

在ros2 python代码, 添加本地模块之前, 加入下面的函数

import os
import sys

def ros_append_module_dir():
    # 获取当前脚本的绝对路径
    script_path = os.path.abspath(__file__)
    # 获取当前脚本所在的目录
    script_dir = os.path.dirname(script_path)
    sys.path.append(script_dir)

ros_append_module_dir()

# 重要
# 重要
# 重要
# 导入本地模块之前

重要:

  1. ros_append_module_dir() 添加到本地模块之前
  2. ros_append_module_dir() 添加到本地模块之前
  3. ros_append_module_dir() 添加到本地模块之前
  4. ros_append_module_dir() insert before import local_module

原理解析:
在这里插入图片描述
本质是将 ros2 run package main_node --> main_node.py 文件路径添加到 sys.path中

3.python代码目录拷贝

如下, 代码目录中有 protobuf/ 目录, 如何拷贝目录
tree -L 7_multi_file_setup/

7_multi_file_setup/
├── main_node.py
├── protobuf
│ ├── websocket.py
│ └── websocket.proto

解决方法
setup.py

    data_files=[
        ('share/ament_index/resource_index/packages',['resource/' + package_name]),
        ('share/' + package_name, ['package.xml']),
        (os.path.join('share', package_name), glob('launch/*_launch.py')),
++        (os.path.join('lib', package_name,'protobuf'), glob('network_proxy/protobuf/*.py')),
    ],

打包安装目录:

install/7_multi_file_setup/lib/7_multi_file_setup/protobuf/websocket.py

4.推荐方案 与 总结

1.推荐方案

个人推荐使用 解决方案1: 拷贝代码到import路径
理由:

  1. 无需修改源代码
  2. 解决 带 ros2 python 多个子目录结构时, site-packages 不打包子目录的问题

缺点:
4. install 目录代码多拷贝一份, 安装包文件体积变大

疑问:
是否有其它更优解决方法?

  1. 不修改源码, 只修改配置项

2.总结

  1. 要熟悉python import 与 sys.path 之间的关系
  2. 要熟悉setup.py 如何打包

个人签名
一个linux-网络-系统-固件-内核驱动-嵌入式-机器人开发工程师

个人微信
微信名片

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值