はじめに
rqt_topicのソースを眺めている時にselected_topics
がコンストラクタで設定できるように実装されていますが、rqt_gui経由では設定できなかったため、今回その仕組みを調べました。
前提条件
環境 | バージョン |
---|---|
開発機 | Ubuntu 16.04.6 LTS |
ROS | Kinetic Kame |
rqt_guiで引数を設定する
ドキュメントには詳細がないため、まずヘルプを参照します。
$ python /opt/ros/kinetic/lib/rqt_gui/rqt_gui -h
usage: rqt_gui [-b BINDING] [--clear-config] [-f] [--force-discover] [-h] [-l]
[-ht] [-p PERSPECTIVE] [--perspective-file PERSPECTIVE_FILE]
[--reload-import] [-s PLUGIN] [-t] [-v]
[--args [PLUGIN_ARGS [PLUGIN_ARGS ...]]] [--list-perspectives]
[--list-plugins] [--command-pid PID]
[--command-start-plugin PLUGIN]
[--command-switch-perspective PERSPECTIVE]
Options for GUI instance:
-b BINDING, --qt-binding BINDING
choose Qt bindings to be used [pyqt|pyside]
--clear-config clear the configuration (including all perspectives
and plugin settings)
-f, --freeze-layout freeze the layout of the GUI (prevent rearranging
widgets, disable undock/redock)
--force-discover force a rediscover of plugins
-h, --help show this help message and exit
-l, --lock-perspective
lock the GUI to the used perspective (hide menu bar
and close buttons of plugins)
-ht, --hide-title hide the title label, the icon, and the help button
(combine with -l and -f to eliminate the entire title
bar and reclaim the space)
-p PERSPECTIVE, --perspective PERSPECTIVE
start with this named perspective
--perspective-file PERSPECTIVE_FILE
start with a perspective loaded from a file
--reload-import reload every imported module
-s PLUGIN, --standalone PLUGIN
start only this plugin (implies -l). To pass arguments
to the plugin use --args
-t, --on-top set window mode to always on top
-v, --verbose output qDebug messages
--args [PLUGIN_ARGS [PLUGIN_ARGS ...]]
arbitrary arguments which are passes to the plugin
(only with -s, --command-start-plugin or --embed-
plugin). It must be the last option since it collects
all following options.
Options to query information without starting a GUI instance:
These options can be used to query information about valid arguments for
various options.
--list-perspectives list available perspectives
--list-plugins list available plugins
Options to operate on a running GUI instance:
These options can be used to perform actions on a running GUI instance.
--command-pid PID pid of the GUI instance to operate on, defaults to
oldest running GUI instance
--command-start-plugin PLUGIN
start plugin
--command-switch-perspective PERSPECTIVE
switch perspective
Special options for embedding widgets from separate processes:
These options should never be used on the CLI but only from the GUI code
itself.
rqt_guiでプラグインに渡す引数は、rqt_gui -s {プラグイン名} --args {引数}
となるようです。ただし注意点としてIt must be the last option since it collects all following options.
とあるように、このオプションは、全てのそれ以降の引数を対象としているため、最後の引数とする必要があります。
rqt_topicの場合
rqt_topicにselected_topicsを設定します。
$ python /opt/ros/kinetic/lib/rqt_gui/rqt_gui -s rqt_topic --args /rosout
しかし選択したトピック以外にも表示がされてしまいます。
rqt_plotの場合
rqt_plotに対象のトピックを設定します。
$ python /opt/ros/kinetic/lib/rqt_gui/rqt_gui -s rqt_plot --args /my_controller/cmd_vel/linear
このプラグインでは、意図した引数として設定できていることが確認できます。そのため、引数の設定方法は間違っていないようです。
rqt_guiの仕組みを調べる
rqt_guiを実行するとコンストラクタが呼ばれ、superに渡ります。これは、from qt_gui.main import Main as Base
で定義されており、Baseのコンストラクタが呼ばれます。初期化が終わるとmain.main()からsuper(Main, self).mainが呼ばれ、mainが実行されます。mainを実行するとargvは、argumentsに格納されます。
main.py#L224でstandaloneでないときの処理分岐がありますが、今回はstandalone=NoneのためTrueになり、plugin_argsが設定され、最終的にself._options.plugin_argsに設定されます。
コマンドラインから-s
オプションで設定したプラグインパラメータは、standaloneがfalseの時にmain.py#L112でstandalone_plugin
に設定され、pluginに格納され、plugin_managerによってロードされます。
load_plugin
では、argvという変数になり、内部のself._load_plugin_loadを呼び出します。今回は、 direct handler for in-process plugins
になるためPluginHandlerDirectでhandler
を設定します。PluginHandlerDirectは、PluginHandlerを継承しているため、super(PluginHandlerDirect, self)でargvが設定され、self._argvに格納されます。最後にhandlerでloadが実行されます。
loadが実行されるとself._context
にPluginContextが生成され、self._handlerとして設定されるため、argvでhundlerのargv
を参照することができます。PluginHandlerのロードが行われると、self._loadが呼ばれますが、これはNotImplementedError
からわかる通りhandlerによってオーバライドされるものです。実装は、plugin_handler_direct.py#L54となり、self._context
(先ほど設定したPluginContext)がロードに使用されていることが確認できました。
プラグイン側の実装の確認
rqt_guiでcontextという形でプラグイン側に渡されることがわかりました。それでは、前述でパラメータの設定の確認ができたrqt_plotの実装を確認してみます。
rqt_plotの引数について
コンストラクタのcontextが、前述のPluginContextで設定したものであり、parse_argsによってargv
がパースされます。parse_argsは、Plot.add_argumentsとなり、rqt_gui
で設定した引数がプラグインまで実際にわたされていることが確認できました。
rqt_topicの引数について
では、引数を設定しても反映されなかったrqt_topicについて確認してみます。contextは、同様ですが、実装を確認するとcontext
からargv
を呼び出している部分がありません。同様にこのプラグインにはargparseの実装がありません。つまり、コンストラクタにはselected_topics
を設定すればできるように実装されているだけでインターフェースが存在しませんでした。これが、今回引数を設定しても反映されない原因でした。
おまけ
今回の調査では、rqt_guiを経由した非スタンドアロンで起動しましたが、スタンドアロンもそれぞれ用意されています。コマンドとして登録されているものは、下記のみですが、
$ which rqt_plot
/opt/ros/kinetic/bin/rqt_plot
$ ls /opt/ros/kinetic/bin/rqt* | grep rqt
-rwxr-xr-x 1 root root 255 6月 8 2019 /opt/ros/kinetic/bin/rqt
-rwxr-xr-x 1 root root 229 6月 8 2019 /opt/ros/kinetic/bin/rqt_bag
-rwxr-xr-x 1 root root 148 6月 8 2019 /opt/ros/kinetic/bin/rqt_console
-rwxr-xr-x 1 root root 156 6月 8 2019 /opt/ros/kinetic/bin/rqt_dep
-rwxr-xr-x 1 root root 149 6月 8 2019 /opt/ros/kinetic/bin/rqt_graph
-rwxr-xr-x 1 root root 162 6月 8 2019 /opt/ros/kinetic/bin/rqt_logger_level
-rwxr-xr-x 1 root root 236 6月 8 2019 /opt/ros/kinetic/bin/rqt_plot
-rwxr-xr-x 1 root root 243 6月 8 2019 /opt/ros/kinetic/bin/rqt_shell
standaloneの実行は、
$ ls /opt/ros/kinetic/lib/rqt* | grep -v / | grep -v '^\s*$'
rqt_bag
rqt_console
rqt_controller_manager
rqt_dep
rqt_graph
rqt_gui
rqt_joint_trajectory_controller
rqt_launch
rqt_logger_level
rqt_moveit
rqt_msg
rqt_nav_view
rqt_plot
rqt_pose_view
rqt_publisher
rqt_py_console
rqt_reconfigure
rqt_robot_monitor
rqt_robot_steering
rqt_runtime_monitor
rqt_rviz
rqt_service_caller
rqt_shell
rqt_srv
rqt_tf_tree
rqt_top
rqt_topic
rqt_web
から実行することができます。これらの違いは、rqt_topic/setup.pyとrqt_plot/setup.pyからなるもので、generate_distutils_setup
にscripts
という引数を渡すかどうかの違いです。これは、setup_dot_py.html#using-package-xml-in-setup-pyの
ROS Users should generally not use the scripts argument, as in ROS, executables should be executed using rosrun rather than being installed to the global bin folder.
よりわかる通りscripts
引数を使うとROS Global Binにインストールされます。これは、catkinで設定されるCATKIN_GLOBAL_BIN_DESTINATIONのことです。setup.pyを使用したcatkin_python_setupがあった場合、インストールの挙動がCMakeLists.txtだけで完結しないので注意が必要です。
話を戻すと実装は、rqt_topicやrqt_plotで確認でき、これまでのソースの確認ですでにわかる通り、standalone=plugin
でスタンドアローンで起動し、plugin_argument_provider=Plot.add_arguments
でプラグインのパーサを設定していることがわかります。
まとめ
- rqt_guiプラグインでの引数の扱い方を確認できた
- プラグインで引数を扱うためにはcontextからargvを参照すると共にargparseの実装が必要である
- インストールは、CMakeLists.txtで完結せずsetup.pyで追加の設定が可能である