【ROS 2 Humble】rviz panel pluginのおおまかなつくりかた
RViz のプラグインの作り方についての公式な情報はros2/rvizのrviz/docs/plugin_development.mdにありますが,十分な情報があってわかりやすいとは言い難い状態です. 日本語の情報源も乏しく存在していても古いものや ROS 1 から ROS 2 への移行を中心としたものです. ここにパネルプラグインを作るときに知っておくべき全体像をまとめます.
この記事の内容
- RViz のプラグインは ROS のパッケージの一種である.
- プラグインは RViz の画面を構成する Qt の一つのウィジェットであり,編集は
qtcreatorで行う. - ROS の世界(トピックやサービス)にアクセスするためにノードをたてられる.
- ROS ノードから Qt のウィジェットの表示を変更するために Qt のシグナルとスロットを用いる.
プラグインは ROS パッケージの一種
rviz/docs/plugin_development.mdにあるとおり,プラグインは普通のワークスペースで作ります.
mkdir -p plugin_ws/src
cd plugin_ws/src
ros2 pkg create my_plugin
普段のノードを作るときと異なる点として
rviz_commonとリンクするpluginlibとリンクするplugins_description.xmlが必要
といった点があります.
rviz_common はプラグインの開発に役立つ機能の集合です.
パネルプラグインはrviz_common::Panelを拡張して作ることになりますし,
ROS 世界のトピックやサービスにアクセスするときにも使います.
rviz_common とのリンクのために CMakeLists での find_package が必要です.
pluginlib とリンクするために CMakeLists にtarget_compile_definitions(${PROJECT_NAME} PRIVATE "RVIZ_DEFAULT_PLUGINS_BUILDING_LIBRARY")と記述しなければなりません.
.cppファイルにPLUGINLIB_EXPORT_CLASSマクロを書くことで cpp に書いたプログラムが実行可能になります.
plugins_description.xmlには RViz 上でプラグインを選択するときに表示される説明文などを書きます.
CMakeLists にpluginlib_export_plugin_description_file(rviz_common plugins_description.xml)と書いておくと適切にエクスポートされます.
プラグインは Qt のウィジェットであり,編集はqtcreatorで行う
RViz の画面は Qt で作られています.
Qt で画面に表示される要素はウィジェットです.
従ってパネルプラグインもウィジェットでなければなりません.
rviz_common::Panelが既にウィジェットになっているので,これを拡張すれば自作のプラグインもウィジェットになります.
ウィジェット上の画面に表示する要素をすべて C++のプログラム手打ちで記述するのは現実的ではありません.
そこで,.uiファイルをqtcreatorの GUI で編集し,.uiをもとにソースコードを自動で生成してもらいます.
qtcreatorはsudo apt install qtcreatorでインストールできます.
.uiを白紙から作成する場合の手順は以下の通りです.
この手順によって qt ディレクトリの中にmywidget.cpp, mywidget.h, mywidget.uiの 3 つのファイルが作られます.
- qtcreator を起動したら
Ctrl + nでNew File or Projectのウインドウを開きます.左下のFiles and Classesの中のQtを選択し,中央のQt Designer Form Classを選択して右下のChoose...をクリックします. - テンプレートは
Widgetを選択します. - クラス名は
MyWidgetとし,ファイルの位置はplugin_ws/src/my_plugin/src/qtに設定します. - バージョン管理は何もせずに
Finishします.
既存のファイルを編集する場合は,Nautilus(ファイル)で .uiファイルをダブルクリックするとQt Designerが開いてウィジェットを編集できます.
編集が済んだら以下の手順でヘッダファイルを作ります.
- 画面の左上のメニューから Form -> View C++ Code…と進めるとヘッダファイルのウインドウが表示されます.
- フロッピーディスクの保存ボタンでこのヘッダを保存します.
- ファイル名はデフォルトの
ui_mywidget.hのままにします.
ROS の世界にアクセスするためのノード
ノードを用意するためには以下の操作が必要です.
- メンバ変数
rclcpp::Node::SharedPtr node_;の宣言 - ノードをイニシャライズ時にたてるため,
void onInitialize() override;でメンバ関数のオーバーライドを宣言 onInitialize()にnode_ = getDisplayContext()->getRosNodeAbstraction().lock()->get_raw_node();と書いてnode_に代入
これで得たnode_は普通のノードへのポインタとして振舞いますので,sub_ = node_->create_subscription<interfaces::msg::type>("topic_name", rclcpp::QoS(10), callback);などとすれば好きな topic を購読できます.
ROS ノードから Qt のウィジェットの表示を変更する
最後に ROS ノードと Qt のウィジェットをつなぐ必要があります. Qt の世界にはシグナルとスロットという仕組みによってオブジェクト間の通信が実現されています. Qt のウィジェットが持つスロットは情報を受信するための機構です. そこに ROS ノードからシグナルを届けることでトピックの内容などを反映することができます.
例えば,QLabel Class にはvoid setText(const QString &)なるスロットが存在しています.
このスロットにシグナルが到達すると,ラベルの表示文字列がシグナルの中に書かれていた文字列に変更されます.
これを用いれば ROS ノードから好きな文字列を送って表示に反映させることができます.
まとめ
RViz プラグインを作るときに知っておくべき大まかな情報をまとめました. 全体像を把握することで,既存のプログラムを理解しやすくなることに貢献できれば幸いです. ここに書いた内容を調べながら作ったプラグインを github で公開しているのでよろしければ参考になさってください.
team-re-boot/communication_rviz_plugin