Windows 11でROS 2を動かそうとしたら_rclpyのNoModuleErrorに無限にハマった話


また無限に時間を溶かしてしまいました

概要

とある試みのため,WSLではなくWindows上でROS 2を入れて動かしたかったんですが,
Pythonのノードを立ち上げようとするとNoModuleErrorが出て立ち上がりませんでした.
ごちょごちょやってる間に動くようになったので備忘録を兼ねて解決方法を書きます
(Windows11で,と書いてますがたぶんWim10でも同様だと思います)

環境

  • AMD Ryzen 7 Pro 4750U
  • Windows 11 ※1
  • Visual Studio 2019 Community
  • ROS 2 foxy ※2
  • Python 3.8.3

※1: WSLgを使いたくて,プレビュー版のWin11を入れて試しています.Win10でも同様になると思っていますが未検証です
※2: ROS 2のインストールはhttps://docs.ros.org/en/foxy/Installation/Windows-Install-Binary.html#downloading-ros-2にあるとおり,githubのreleasesからzipをダウンロード・展開して行いました

起こった現象

注意:以下コマンドの入出力はVS2019 Developer Command Promptで行ってます.

  • 以下の記事の通りに,Pythonで実装するパッケージを作成しました
    ROS2で自分で作成したpythonプログラムを動かす方法
    ※ros-windowsはC直下,(あまりよくないと思いますが)ros2_wsはros-windows内に作りました
  • ros2 run my_python my_listenerしたところ,以下のエラーが出てノードが立ち上がりませんでした
    (talkerも同様)
C:\ros2-windows\ros2_ws>ros2 run my_python my_listener
Traceback (most recent call last):
  File "C:\ros2-windows\ros2_ws\install\my_python\lib\my_python\my_listener-script.py", line 33, in <module>
    sys.exit(load_entry_point('my-python==0.0.0', 'console_scripts', 'my_listener')())
  File "C:\ros2-windows\ros2_ws\install\my_python\lib\my_python\my_listener-script.py", line 25, in importlib_load_entry_point
    return next(matches).load()
  File "c:\python38\lib\importlib\metadata.py", line 77, in load
    module = import_module(match.group('module'))
  File "c:\python38\lib\importlib\__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1014, in _gcd_import
  File "<frozen importlib._bootstrap>", line 991, in _find_and_load
  File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 671, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 783, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "C:\ros2-windows\ros2_ws\install\my_python\Lib\site-packages\python_programs\listener.py", line 2, in <module>
    from rclpy.node import Node
  File "C:\ros2-windows\Lib\site-packages\rclpy\node.py", line 41, in <module>
    from rclpy.client import Client
  File "C:\ros2-windows\Lib\site-packages\rclpy\client.py", line 22, in <module>
    from rclpy.impl.implementation_singleton import rclpy_implementation as _rclpy
  File "C:\ros2-windows\Lib\site-packages\rclpy\impl\implementation_singleton.py", line 31, in <module>
    rclpy_implementation = _import('._rclpy')
  File "C:\ros2-windows\Lib\site-packages\rclpy\impl\__init__.py", line 28, in _import
    return importlib.import_module(name, package='rclpy')
  File "c:\python38\lib\importlib\__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
ModuleNotFoundError: No module named 'rclpy._rclpy'
The C extension 'C:\ros2-windows\Lib\site-packages\rclpy\_rclpy.pyd' isn't present on the system. Please refer to 'https://index.ros.org/doc/ros2/Troubleshooting/Installation-Troubleshooting/#import-failing-without-library-present-on-the-system' for possible solutions
  • rclpyはC:\ros2-windows\Lib\site-packages\rclpyを参照しており,同パスはPYTHONPATHにも登録されていました
  • C:\ros2-windows\Lib\site-packages\rclpyには_rclpy.lib_rclpy_d.pydがあるものの,_rclpy.pydはありませんでした

結論

  • Pythonは3.8系(公式ページでは3.8.3が示されています)を使う必要がありました
  • 通常のPython(python.exe)ではなく,Windows デバッグ版(python_d.exe)を使う必要がありました
  • デバッグ版Pythonを使う場合,numpyなど一部モジュールはそれ用にインストールしなおす必要がありました
  • デバッグ版Pythonを使う場合,colcon buildの時にそのためのコマンドを使う必要がありました

解決策

Pythonインストール

上述のとおり,デバッグ版のpython_d.exeを使う必要があります.
この実行ファイルは,インストールの時にオプションで選択しないと入らないようになっています.
Python公式サイト> Downloads > WindowsからPython*3.8.3*のインストーラをダウンロード・実行し,以下の画像のようにAdvanced Optionsからdebugging symbolsdebug binariesを入れてやるとよいです.

なお,ROS 2はPythonのインストールフォルダがC:\Python38\であると想定していますが,インストーラのデフォルト設定ではProgram Files以下になっているので,そこも修正が必要です.
また,chocoanaconda等からすでにPythonをインストールしている場合,インストーラが動作しません.
(私はchocoで入れたpythonをuninstallしてインストーラから入れなおしました)

インストールがうまくて来ていると,Pythonのインストールフォルダ(C:\Python38)に,python_d.exeが存在します.
また,コマンドプロンプトからpython_dコマンドでPythonの立ち上げが行えます

C:\ros2-windows\ros2_ws>python_d
Python 3.8.3 (tags/v3.8.3:6f8c832, May 13 2020, 22:20:16) [MSC v.1925 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.

Debug版Python向けの環境構築

python_dを利用する場合に必要な依存パッケージ類ですが,ROS 2ドキュメントのBuilding ROS 2 on Windowsページに記載があります
Extra stuff for Debug mode - Building ROS 2 on Windows, ROS 2 Documentation
このページにあるとおり,chocoを使って依存パッケージをインストールする必要があります.
(なお,インストールには管理者権限が必要です.

choco install -y peazip
choco install -y svn hg

また,pythonパッケージも一部再インストールが必要です

python_d -m pip install --force-reinstall https://github.com/ros2/ros2/releases/download/numpy-archives/numpy-1.18.4-cp38-cp38d-win_amd64.whl
python_d -m pip install --force-reinstall https://github.com/ros2/ros2/releases/download/lxml-archives/lxml-4.5.1-cp38-cp38d-win_amd64.whl

注:AMDのCPUを使用している場合

ここで,僕の環境(AMD)では以下のエラーのためインストールできませんでした.
(Intelの場合どうなるかは未検証です)

C:\ros2-windows\ros2_ws>python_d -m pip install --force-reinstall https://github.com/ros2/ros2/releases/download/numpy-archives/numpy-1.18.4-cp38-cp38d-win_amd64.whl
ERROR: numpy-1.18.4-cp38-cp38d-win_amd64.whl is not a supported wheel on this platform.

pipは.whlファイルのファイル名からバージョンを認識しているようで,次の手順のようにリネームしてやると解決します(ええんやろか)

  1. https://github.com/ros2/ros2/releases/download/numpy-archives/numpy-1.18.4-cp38-cp38d-win_amd64.whlおよびhttps://github.com/ros2/ros2/releases/download/lxml-archives/lxml-4.5.1-cp38-cp38d-win_amd64.whlにアクセスして,.whlファイルをダウンロードする
  2. それぞれのファイルについて,...win_amd64.whlとあるところを...win32.whlにリネームする
  3. 通常通り,python_d -m pip install --force-reinstall <ダウンロードした.whlファイル>を実行してインストールする

インストールがうまくいけば,エラーが出ずにそれぞれのパッケージをimportできるはずです

C:\ros2-windows\ros2_ws>python_d
Python 3.8.3 (tags/v3.8.3:6f8c832, May 13 2020, 22:20:16) [MSC v.1925 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from lxml import etree
>>> import numpy
#何もエラーが出ない

参考:
【Python】pipでwhlファイルをインストールするときのエラー - 俺言語.
numpyインストールで四苦八苦 - Hajime Satoさんのnote記事

パッケージのビルド・実行

あとは,インストールしたpython_dでパッケージをビルドし,実行します.
Extra stuff for Debug mode - Building ROS 2 on Windows, ROS 2 Documentationにもあるように,python_dcolconの実行ファイルのパスを与えます.
僕の場合,pythonをC:\Python38にインストールしていたので,コマンドは以下のようになります.
(なお,すでにcolcon buildでビルドしたものがある場合,--cmake-clean-cacheで消去してから実行する必要があります.)

C:\ros2-windows\ros2_ws>colcon build --cmake-clean-cache #すでにcolcon buildを走らしていた場合
C:\ros2-windows\ros2_ws>python_d C:\Python38\Scripts\colcon.exe build

ビルド後実行してやるとうまく動きました.

C:\ros2-windows\ros2_ws>ros2 run my_python my_talker
[INFO] [1627127739.241812500] [talker]: Publishing: "Hello World: 0"
[INFO] [1627127740.085704200] [talker]: Publishing: "Hello World: 1"

コマンドプロンプトを2つ立ち上げてtalkerとlistenerを同時に実行してみたところ,メッセージの送受信が行われていることも確認できました.めでたしめでたし.

補足

"_rclpy not found"みたいなので調べていると,以下のようにcolcon build時にオプションをつけるといいよという記述がありました.
ROS2: DebugModuleNotFoundError: No module named ‘rclpy._rclpy’ - ROS Discourse

こんな感じです

colcon build --cmake-args -DCMAKE_BUILD_TYPE=Debug

ただ,python_d colconした場合とこれとは振る舞いが違うみたいで,僕はこのargsをつけるやり方では_rclpy not foundエラーは解消できませんでした.
python_dを利用してbuildする場合でも,このオプションをつけるとうまくいかなかったです.
ちなみに,--cmake-argsをつけてビルドした場合,同様にcmake-clean-cacheする必要があるのですが,その時にも--camke-argsオプションは必要です.

colcon build --cmake-cache-clean --cmake-args -DCMAKE_BUILD_TYPE=Debug

おわりに

僕が調べた範囲だと日本語の記事等出てこなくて,結構苦労しました.
ほとんどの人WSLとかDocker使うやろうしWindows上で頑張ってもしゃあないってことですかね

公式ドキュメントのインストールガイドだと,動作確認としてdemo_nodes_pyを用いるんですが,これはpython_dを使わなくても動いちゃうんですよね.
なので,実はビルドしたPythonパッケージは動かない状態なのに,exampleが通ったので環境構築の時にはそれでオッケーにしちゃって,あとでハマるみたいなことになるじゃないかなと思いました.というかなりました.