ROSのlaunchファイルでは,ワイルドカード(グロブ, *.bag
のような表現)が使えません.例えば,気持ち的には
<?xml version="1.0"?>
<launch>
<arg name="bag_dir" default="." />
<node name="rosbag_player" pkg="rosbag" type="play"
args="$(arg bag_dir)/*.bag" />
</launch>
というようなことがやりたいのですが, *.bag
は展開されずに文字列として rosbag
ノードの引数になってしまいます.
これを 無理矢理 展開させるようにする方法を本記事で紹介します.かなりHackyな方法なので,推奨はしません.
TL;DR
Pythonの標準モジュールの一つである glob
をインポートして,展開します.ただし, import glob
や __import__("glob")
が使用不可なので,eval
をネストすることでインポートしています.
<?xml version="1.0"?>
<launch>
<arg name="bag_dir" default="." />
<arg name="bag_path" value="$(eval eval('_' + '_import_' + '_("glob").glob(' + 'bag_dir + "/*.bag")[0]'))" />
<node name="rosbag_player" pkg="rosbag" type="play"
args="$(arg bag_path)" />
</launch>
解説
本当なら以下のようにしたいですよね.
<?xml version="1.0"?>
<launch>
<arg name="bag_dir" default="." />
<arg name="bag_path" value="$(eval import glob; glob.glob(bag_dir + '/*.bag')[0]))" />
<node name="rosbag_player" pkg="rosbag" type="play"
args="$(arg bag_path)" />
</launch>
でも,Pythonでは eval("import glob")
が不正なので,エラーが出ます.
RLException: Invalid <arg> tag: invalid syntax (<string>, line 1).
じゃあ __import__("glob")
でいいじゃない.
<?xml version="1.0"?>
<launch>
<arg name="bag_dir" default="." />
<arg name="bag_path" value="$(eval __import__('glob').glob(bag_dir + '/*.bag')[0])" />
<node name="rosbag_player" pkg="rosbag" type="play"
args="$(arg bag_path)" />
</launch>
しかし,roslaunchのevalではアンダースコアを二つ含む表現を使ってはいけない模様.
RLException: Invalid <arg> tag: $(eval ...) may not contain double underscore expressions.
ならばevalをevalしてアンダースコアを連結させるしかない.
<?xml version="1.0"?>
<launch>
<arg name="bag_dir" default="." />
<arg name="bag_path" value="$(eval eval('_' + '_import_' + '_("glob").glob(' + 'bag_dir + "/*.bag")[0]'))" />
<node name="rosbag_player" pkg="rosbag" type="play"
args="$(arg bag_path)" />
</launch>
XMLのダブルクォートのせいで "
を使っていますが,実行しているPythonコードは以下の通りです.
eval(
eval(
'_' + '_import_' + '_("glob").glob(' + 'bag_dir + "/*.bag")[0]'
)
)
展開されたあと,roslaunchが実際に実行するコードは
__import__("glob").glob(bag_dir + "/*.bag")[0]
で, bag_dir
は roslaunchのarg展開により,argとして事前に設定されている bag_dir
に展開されます.
おまけ
[0]
を変えることで複数マッチがあった場合にどれを使うかや, /*.bag
を変えることでグロブ表現を変えることもできます.これらをargにして展開するようにしたら親切かも……?
<?xml version="1.0"?>
<launch>
<arg name="bag_dir" default="." />
<arg name="index" default="0" />
<arg name="glob_pattern" default="*.bag" />
<arg name="bag_path" value="$(eval eval('_' + '_import_' + '_("glob").glob(' + 'bag_dir + "/" + glob_pattern)[index]'))" />
<node name="rosbag_player" pkg="rosbag" type="play"
args="$(arg bag_path)" />
</launch>
おわりに
可読性がかなり低いので,利用するときは気をつけましょう...