オリジナル Rviz Plugin をつくってみる 2. ROS1で .ui ファイルを使ってオリジナルパネルをつくる

# はじめに
本記事は，下記記事のシリーズ記事です．
関連記事等気になる方は是非下記記事から飛んでください！

https://qiita.com/RyodoTanaka/items/eadfb81bd52404dabdb4

では早速`rviz::Panel`を作ってみよう！と思います．
と言っても，ソースコードでパネル配置やらをゴリゴリ書く方法は既に多くの紹介があります．
本シリーズの最初に記事のリストがあるので，そちらをご覧ください．

本記事では，**「GUIからパネルのデザインを作って，Rviz Pluginにする」**ということに主眼を置きたいと思います．
まずは ROS1 環境（melodic, noetic）で動作するものを作ろうと思います．
ソースコードは下記にて公開しています（Apache2.0）

- melodic : https://github.com/RyodoTanaka/rviz_plugin_examples/tree/melodic-devel
- noetic : https://github.com/RyodoTanaka/rviz_plugin_examples/tree/noetic-devel

環境については，DockerでROSの環境ベースを作っているので，興味のある方は是非使ってみてください...！

- https://github.com/RyodoTanaka/ubuntu

（注意）今回の記事は長いです．が，必要最低限のことしか書かないように努めています...！


# 動作説明
というわけで早速動作してる様子です．
ソースコードはこちらです．（`noetic-devel`のURLです．）

- https://github.com/RyodoTanaka/rviz_plugin_examples/tree/noetic-devel

[![rviz-panel.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110699/6de4cffa-e3bc-b50f-72bb-f69e3ba74a70.gif)](https://youtu.be/xL5g6bMIP3w)

# コード解説
## `.ui`ファイルをつくる
タイトルにもなっている`.ui`ファイルを作ります．
このファイルはパネルのデザインを記述したファイルで，`qtcreator`で直感的に配置を作ることができます．
`qtcreator`は`apt`で入れれるので，入ってない方は入れてみてください．

```bash
$ sudo apt install qtcreator
```

何か既に出来上がっている`.ui`ファイルを開いてみましょう．
今回は，`rviz_plugin_examples/src/ui/dial_panel.ui`を開きます．
すると下記のようなWindowが出るはずです．

```bash
$ cd <rviz_plugin_examples>/src/ui
$ qtcreator dial_panel.ui
```

![Screenshot from 2020-12-16 20-31-21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110699/6cfe9212-b8cf-d937-910f-7a3de10ada97.png)

このツールのいじり方は割愛しますが，おそらく説明が無くても大体わかるんじゃないかと思います．
`qtcreator .ui 作り方`等でググるとたくさん記事が出てくるので，気になる方はそちらをどうぞ！

- https://www.qt.io/jp/blog/2010/08/19/qt-designer

要はここでWindow内の要素を配置していきます．
一点気をつけるポイントは，あとで出てくるんですが，各要素の名前が`SLOT`（コールバック関数）とつなげる際に必要になります．
今は一旦そんなもんかということで流してください．
そして，こいつが MoveItのパネルの設計を定義している正体です．
確かにこの方法なら複雑なパネルも作れますよね...！

- https://github.com/ros-planning/moveit/tree/master/moveit_ros/visualization/motion_planning_rviz_plugin/src/ui


あんまり長くなるのも嫌なので次に行きます！



## `dial_panel.h`

- https://github.com/RyodoTanaka/rviz_plugin_examples/blob/noetic-devel/include/rviz_plugin_examples/dial_panel.h

```cpp
#pragma once

#ifndef Q_MOC_RUN
#include <ros/ros.h>
#endif

#include <rviz/panel.h>

namespace Ui {
class DialUI;
}

namespace rviz_plugin_examples
{
class DialPanel: public rviz::Panel
{
  Q_OBJECT
 public:
  DialPanel(QWidget* parent = nullptr);
  ~DialPanel() override;

  void onInitialize() override;
  void onEnable();
  void onDisable();

private Q_SLOTS:
  void dialValueChanged(int value);
  void lineEditChanged();
  void buttonClicked();

protected:
  Ui::DialUI* ui_;
  int value_{0};
  std::string topic_name_{"dial"};

  ros::NodeHandle nh_;
  ros::Publisher pub_;
};
} // end namespace rviz_plugin_examples

```

```cpp
namespace Ui {
class DialUI;
}
```
先程`.ui`ファイルで作ったウインドウはコンパイル時に自動でクラスとヘッダファイルができます．
なので，ここでは宣言だけしておく必要があります．
`namespace`は`Ui`で固定．
`DialUI`は`.ui`ファイルの`QWidtet`の所で好きな名前を付けれます．今回は`DialUI`です．

```cpp
namespace rviz_plugin_examples
{
class DialPanel: public rviz::Panel
{
  Q_OBJECT
 public:
  DialPanel(QWidget* parent = nullptr);
  ~DialPanel() override;

  void onInitialize() override;
  void onEnable();
  void onDisable();
```
今回作るプラグインは，`rviz::Panel`を継承します．
QtのWidgetは入れ子にもできるので親の `QWidget`を引数に取っています．
`onInitialize()`等は読んだままの関数です．
windowの 初期化時，起動時，終了時 の挙動を記述します．

```cpp
private Q_SLOTS:
  void dialValueChanged(int value);
  void lineEditChanged();
  void buttonClicked();
```
前記事で紹介していた `SLOT`，平たく言うとコールバック関数です．
中身を好きに設計できます．

```cpp
protected:
  Ui::DialUI* ui_;
  int value_{0};
  std::string topic_name_{"dial"};

  ros::NodeHandle nh_;
  ros::Publisher pub_;
};
} // end namespace rviz_plugin_examples
```

あとは，ROSのPublishに必要なものと，前方宣言していた`Ui::DialUI`のポインタ宣言です．
`UI::DiaiLUI`の実体は，後で出てきますが，クラスのコンストラクタで確保します．

## `dial_panel.cpp`
- https://github.com/RyodoTanaka/rviz_plugin_examples/blob/noetic-devel/src/dial_panel.cpp

```cpp
#include <rviz_plugin_examples/dial_panel.h>
#include <pluginlib/class_list_macros.h>
#include <std_msgs/Float64.h>

#include "ui_dial_panel.h"

namespace rviz_plugin_examples
{
DialPanel::DialPanel(QWidget* parent) : Panel(parent),  ui_(new Ui::DialUI())
{
  ui_->setupUi(this);
}

DialPanel::~DialPanel() = default;

void DialPanel::onInitialize()
{
  connect(ui_->dial, SIGNAL(valueChanged(int)), this , SLOT(dialValueChanged(int)));
  connect(ui_->pushButton, SIGNAL(clicked()), this, SLOT(buttonClicked()));

  ui_->line_edit->setPlaceholderText("Input topic name (Default : dial)");

  connect(ui_->line_edit,  SIGNAL(textChanged(const QString &)), this, SLOT(lineEditChanged()));

  pub_ = nh_.advertise<std_msgs::Float64>("dial", 1);
  parentWidget()->setVisible(true);
}

void DialPanel::onEnable()
{
  show();
  parentWidget()->show();
}

void DialPanel::onDisable()
{
  hide();
  parentWidget()->hide();
}

void DialPanel::lineEditChanged()
{
  std::string old_topic_name = topic_name_;
  if(ui_->line_edit->text().isEmpty())
    topic_name_ = "dial";
  else
    topic_name_ = ui_->line_edit->text().toStdString();

  ROS_INFO("You set the topic name : %s", topic_name_.c_str());

  if(old_topic_name != topic_name_)
    pub_ = nh_.advertise<std_msgs::Float64>(topic_name_, 1);
}

void DialPanel::dialValueChanged(int value)
{
  ui_->lcd->display(value);
  value_ = value;
  ROS_INFO("You set the value : %d", value_);
}

void DialPanel::buttonClicked()
{
  std_msgs::Float64 msg;
  msg.data = static_cast<double>(value_);
  pub_.publish(msg);
  ROS_INFO("You pushed the button.");
}

}  // namespace rviz_plugin_examples

PLUGINLIB_EXPORT_CLASS(rviz_plugin_examples::DialPanel, rviz::Panel )
```

```cpp
#include <rviz_plugin_examples/dial_panel.h>
#include <pluginlib/class_list_macros.h>
#include <std_msgs/Float64.h>

#include "ui_dial_panel.h"
```
今回はダイアルをRviz上で回し，その値を任意のTopic名でPublishボタンを押したらPublishするという機能を実装します．
値は，`std_msgs::Float64`なので，これを include しています． 
`pluginlib/class_list_macros.h`は，ROSの`pluginlib`を利用するために必要です．
これは他のプログラム（ここではRviz）から，ライブラリとしてプログラムを呼び出すための仕組みで，Rviz Pluginでなくても使える仕組みです．
ここでは詳しくは触れませんが，下記本（私も一部関わっております）の11章に詳しく載っています．（森田さんが担当された箇所です）

- [実用ロボット開発のためのROSプログラミング](https://www.amazon.co.jp/%E5%AE%9F%E7%94%A8%E3%83%AD%E3%83%9C%E3%83%83%E3%83%88%E9%96%8B%E7%99%BA%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AEROS%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0-%E8%A5%BF%E7%94%B0-%E5%81%A5/dp/462767581X)

最後の`ui_dial_panel.h`はコンパイル時に自動生成されるヘッダです．
前項の`.ui`ファイル，ここでは`dial_panel.ui`の拡張子の前に接頭文字`ui_`がついたヘッダ`ui_dial_panel.h`が生成されます．
もちろん中身を利用するのでIncludeしています．

```cpp
DialPanel::DialPanel(QWidget* parent) : Panel(parent),  ui_(new Ui::DialUI())
{
  ui_->setupUi(this);
}

DialPanel::~DialPanel() = default;
```
コンストラクタとデストラクタです．
前述の通り，ここで`Ui::DialUI()`クラスの実態確保（new）とセットアップを行っています．
デストラクタはデフォルトコンストラクタです．

```cpp
void DialPanel::onInitialize()
{
  connect(ui_->dial, SIGNAL(valueChanged(int)), this , SLOT(dialValueChanged(int)));
  connect(ui_->pushButton, SIGNAL(clicked()), this, SLOT(buttonClicked()));

  ui_->line_edit->setPlaceholderText("Input topic name (Default : dial)");

  connect(ui_->line_edit,  SIGNAL(textChanged(const QString &)), this, SLOT(lineEditChanged()));

  pub_ = nh_.advertise<std_msgs::Float64>("dial", 1);
  parentWidget()->setVisible(true);
}
```
ウィンドウの初期化処理です．
とうとうここでWindow内の要素`SLOT`を`SIGNAL`でつなげます．
つなげる際には`connect()`関数を使います．
ここで書いている書き方は`Qt4`で用いられている書き方です．
この書き方は`Qt5`でも使用できる上，わかりやすいのでこれを使っています．
中身は日本語で書くと，

```cpp
connect(送信元, 送信SIGNAL，受信元, SLOT)
```
です．
きちんと型を知りたい方や，`Qt5`の書き方（`Fanctor`ベースの書き方）を知りたいという方は下記記事がおすすめです．

- [今更聞けないシグナル・スロット総整理](https://qiita.com/argama147/items/30da69e4c1088917d522)

例えば１つ目の`connect()`関数を見てみると

```cpp
 connect(ui_->dial, SIGNAL(valueChanged(int)), this , SLOT(dialValueChanged(int)));
```
となっています．
送信元は先程`qtcreator`で作ったダイアルのオブジェクト（`QObject`といいます）です．
`QObject`の名前（オブジェクト名）は下図のように，`qtcreator`上で入力したものと合わせる必要があります．
逆に言うと，好きな名前に変更できます．
![rviz-dial-ui.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110699/a81207cf-beb1-cfb5-9a4b-cfccb64c9ec3.png)

`SLOT`（コールバック関数）は自身のクラスに`ialValueChanged(int)`で登録しているので，これを指定しています．
同じ要領で，Publishボタンと，トピック名を入れるLinEditorも実装されています．

その後，

```cpp
  pub_ = nh_.advertise<std_msgs::Float64>("dial", 1);
```
ROSのパブリッシャーの設定（初期は`dial`というトピック名で Publish することにしています）を行い，

```cpp
  parentWidget()->setVisible(true);
```
このウィンドウ（パネル）が追加された時，表示された状態になるように設定しています．

```cpp
void DialPanel::onEnable()
{
  show();
  parentWidget()->show();
}

void DialPanel::onDisable()
{
  hide();
  parentWidget()->hide();
}
```
この部分は特に多くの説明は必要ないかと思います．
要は，

- Enableになったときに，見えるようにしてね
- Disableにしたときは隠してね

という処理を行っているだけです．

```cpp
void DialPanel::lineEditChanged()
{
  std::string old_topic_name = topic_name_;
  if(ui_->line_edit->text().isEmpty())
    topic_name_ = "dial";
  else
    topic_name_ = ui_->line_edit->text().toStdString();

  ROS_INFO("You set the topic name : %s", topic_name_.c_str());

  if(old_topic_name != topic_name_)
    pub_ = nh_.advertise<std_msgs::Float64>(topic_name_, 1);
}
```
この部分はトピック名を変更した時の処理が書かれています．
空の場合は "dial" に，それ以外は前のトピック名と異なればPublisherを再設定するようになっています．
また，この処理に入ったことがわかるように `ROS_INFO()`を使ってメッセージを表示させています．（これはなくても良い）

```cpp
void DialPanel::dialValueChanged(int value)
{
  ui_->lcd->display(value);
  value_ = value;
  ROS_INFO("You set the value : %d", value_);
}
```
この部分はダイアルの値が変わった時の処理が書かれています．
値が変わったら，ダイアルの隣の LCD 表示の数字を変更します．
具体的には`qtcreator`上でオブジェクト名を`lcd`とした LCD 表示を受け取った値に書き換えています．
その後，ローカル変数`value_`に値を格納し，`ROS_INFO()`でメッセージ表示をしています．
メッセージ表示は前と同様，あってもなくても良いです．

```cpp
void DialPanel::buttonClicked()
{
  std_msgs::Float64 msg;
  msg.data = static_cast<double>(value_);
  pub_.publish(msg);
  ROS_INFO("You pushed the button.");
}
```
この部分はPublishボタンが押されたときの処理が書かれています．
ローカル変数`value_`の値をPublishしているただけです．
またこれまでと同様，あってもなくても良いのですが，メッセージ表示をしています．

```cpp
PLUGINLIB_EXPORT_CLASS(rviz_plugin_examples::DialPanel, rviz::Panel )
```
とうとう最後の部分です！
この部分で，前述の`pluginlib`に本クラスを登録しています．
これがないと`pluginlib`から本クラスのライブラリが探せないので必ず記述してください．


さぁ，やっとこれでソースコードの設定は終了です．
全体の折返し地点ですね...！
今回の記事は長いですががんばりましょう...！

## `icon`を追加

- https://github.com/RyodoTanaka/rviz_plugin_examples/blob/noetic-devel/icons/classes/DialPanel.png

必ず必要なわけではないですが，せっかくなんで見た目をかっちょ良くしたいですよね...！
ということで小休憩的に`icon`の設定方法です．
ここでの`icon`というのは，Rviz上に表示されるときのアイコンのことです．
ルールとして，下記があります．

- `icons/classes`ディレクトリ下に，クラス名（ここでは`DialPanel`）と同じ名前のPNG画像を保存する．
- PNG画像は `16x16 px` 

今回は，FontoAwesome系の無料画像を適当に引っ張ってきたものを利用しています．

では，ラストスパート頑張っていきましょう...！

## `plugin_description.xml`
- https://github.com/RyodoTanaka/rviz_plugin_examples/blob/noetic-devel/plugin_description.xml

```xml
<library path="librviz_plugin_examples">
  <!-- Dial Panel -->
  <class name="rviz_plugin_examples/DialPanel" type="rviz_plugin_examples::DialPanel" base_class_type="rviz::Panel">
    <description>
      Dial Panel plugin.
    </description>
  </class>
</library>
```
`pluginlib`に渡す設定ファイルです．

```xml
<library path="librviz_plugin_examples">
```
ここで設定する名前は，ライブラリ（リンカ）の名前です．
今回のプログラムは，コンパイルすると

```bash
<catkin_ws>/devel/lib/librviz_plugin_examples.so
```
となって出来上がります．
このときの`.so`を除いた名前を設定する必要があります．

```xml
 <class name="rviz_plugin_examples/DialPanel" type="rviz_plugin_examples::DialPanel" base_class_type="rviz::Panel">
```
ここで設定する`name`は，`dial_panel.cpp`の最後に記載した名前と一致させる必要があります．
今回は，`rviz_plugin_examples::DialPanel`と登録していたので，`rviz_plugin_examples/DialPanel`としています．
要は`::`を`/`に変えておけばOKです．
`type`はクラス名そのものを記載します．今回は，`rviz_plugin_examples::DialPanel`です．
`base_class_type`は，継承元のクラス名を記載します．今回は，`rviz::Panel`です．

以上で必要最低限の設定ができました．
`<description>`タグには，Rviz上で選択した際に表示される説明文を書いておいてください．空でもOKです．

## `CMakeLists.txt`

- https://github.com/RyodoTanaka/rviz_plugin_examples/blob/noetic-devel/CMakeLists.txt

```cmake
cmake_minimum_required(VERSION 3.5)
project(rviz_plugin_examples)

###################
# compile options #
###################
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=c++17 -DEIGEN_RUNTIME_NO_MALLOC")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -fno-asm")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -O0")

#############
## Library ##
#############
## Eigen ##
find_package(Eigen3 REQUIRED)
include_directories(${EIGEN3_INCLUDE_DIR})
link_directories(${EIGEN3_LIBRARY_DIRS})

# Qt settings #
find_package(Qt5 ${rviz_QT_VERSION} REQUIRED Core Widgets)
macro(qt_wrap_ui)
  qt5_wrap_ui(${ARGN})
endmacro()
include_directories(${Qt5Core_INCLUDE_DIRS})
include_directories(${Qt5Widgets_INCLUDE_DIRS})
add_definitions(-DQT_NO_KEYWORDS)
# cmake settings #
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

# catkin #
set(DEPEND_ROS_PKGS
  class_loader
  pluginlib
  roscpp
  rviz
  std_msgs
  )
find_package(catkin REQUIRED
  COMPONENTS
  ${DEPEND_ROS_PKGS}
  )

catkin_package(
  INCLUDE_DIRS
  include
  LIBRARIES
  ${PROJECT_NAME}
  CATKIN_DEPENDS
  ${DEPEND_ROS_PKGS}
  DEPENDS
  EIGEN3
  )

#########
# Build #
#########
## add include directory ##
include_directories(
  include
  ${catkin_INCLUDE_DIRS}
  )
## add library path ##
link_directories(${catkin_LIBRARY_DIRS})

# source codes #
set(HEADERS
  include/rviz_plugin_examples/dial_panel.h
  )
qt5_wrap_ui(UIC_FILES
  src/ui/dial_panel.ui
  )
set(SOURCE_FILES
  src/dial_panel.cpp
  )

add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${HEADERS} ${UIC_FILES})
set_target_properties(${PROJECT_NAME} PROPERTIES VERSION "${${PROJECT_NAME}_VERSION}")
target_include_directories(${PROJECT_NAME} PRIVATE "${OGRE_PREFIX_DIR}/include")

###########
# Install #
###########
install(FILES plugin_description.xml
  DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
)
install(DIRECTORY icons DESTINATION share/${PROJECT_NAME})
install(DIRECTORY include/ DESTINATION include)
install(TARGETS ${PROJECT_NAME}
  ARCHIVE DESTINATION lib
  LIBRARY DESTINATION lib
  RUNTIME DESTINATION bin)
```

```cmake
cmake_minimum_required(VERSION 3.5)
project(rviz_plugin_examples)

###################
# compile options #
###################
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=c++17 -DEIGEN_RUNTIME_NO_MALLOC")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -fno-asm")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -O0")

#############
## Library ##
#############
## Eigen ##
find_package(Eigen3 REQUIRED)
include_directories(${EIGEN3_INCLUDE_DIR})
link_directories(${EIGEN3_LIBRARY_DIRS})
```
おなじみのプロジェクトの設定から，C++のコンパイルオプションの設定，`Eigen`のライブラリインポートの設定です．
Eigen3は使ってないんですが，いつも設定値に入れるので消し忘れてました :flushed:　見逃してください... :innocent:

```cmake
# Qt settings #
find_package(Qt5 ${rviz_QT_VERSION} REQUIRED Core Widgets)
macro(qt_wrap_ui)
  qt5_wrap_ui(${ARGN})
endmacro()
include_directories(${Qt5Core_INCLUDE_DIRS})
include_directories(${Qt5Widgets_INCLUDE_DIRS})
add_definitions(-DQT_NO_KEYWORDS)
# cmake settings #
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
```
Qt用の設定です．
後で`.ui`ファイルからライブラリを作り出すためのコマンド`qt5_wrap_ui()`のラッピングマクロ`qt_wrap_ui()`も定義しています．
今回は `QtCore`と`QtWidget`を使うので，これらの`include_directories`やらパッケージ設定等を行っています．

```cmake
catkin_package(
  INCLUDE_DIRS
  include
  LIBRARIES
  ${PROJECT_NAME}
  CATKIN_DEPENDS
  ${DEPEND_ROS_PKGS}
  DEPENDS
  EIGEN3
  )

#########
# Build #
#########
## add include directory ##
include_directories(
  include
  ${catkin_INCLUDE_DIRS}
  )
## add library path ##
link_directories(${catkin_LIBRARY_DIRS})
```
このあたりは ROSの普通の`CMakeLists.txt`の書き方と変わらないと思いますので詳しい解説は割愛します．

```cmake
# source codes #
set(HEADERS
  include/rviz_plugin_examples/dial_panel.h
  )
qt5_wrap_ui(UIC_FILES
  src/ui/dial_panel.ui
  )
set(SOURCE_FILES
  src/dial_panel.cpp
  )

add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${HEADERS} ${UIC_FILES})
set_target_properties(${PROJECT_NAME} PROPERTIES VERSION "${${PROJECT_NAME}_VERSION}")
target_include_directories(${PROJECT_NAME} PRIVATE "${OGRE_PREFIX_DIR}/include")
```

ここも基本的には普通の方法と変わらないのですが，唯一 `.ui`ファイルからライブラリを生成するための処理が書かれています．
出来上がったライブラリは，`add_library()`部分で追加されています．

```cmake
###########
# Install #
###########
install(FILES plugin_description.xml
  DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
)
install(DIRECTORY icons DESTINATION share/${PROJECT_NAME})
install(DIRECTORY include/ DESTINATION include)
install(TARGETS ${PROJECT_NAME}
  ARCHIVE DESTINATION lib
  LIBRARY DESTINATION lib
  RUNTIME DESTINATION bin)
```
最後にインストール処理です．

```cmake
install(FILES plugin_description.xml
  DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
)
```
この部分で，先程記述した`pluginlib`用の設定ファイル`plugin_description.xml`をインストールし，

```cmake
install(DIRECTORY icons DESTINATION share/${PROJECT_NAME})
install(DIRECTORY include/ DESTINATION include)
install(TARGETS ${PROJECT_NAME}
  ARCHIVE DESTINATION lib
  LIBRARY DESTINATION lib
  RUNTIME DESTINATION bin)
```
この部分でアイコン，`include`ディレクトリ以下すべて，作成されるライブラリ（今回は`librviz_plugin_examples.so`）がインストールされます．

## `package.xml`
- https://github.com/RyodoTanaka/rviz_plugin_examples/blob/noetic-devel/package.xml

```xml
<package format="2">
  <name>rviz_plugin_examples</name>
  <version>1.0.0</version>
  <description>Rviz plugin examples</description>

  <author email="groadpg@gmail.com">RyodoTanaka</author>
  <maintainer email="groadpg@gmail.com">RyodoTanaka</maintainer>

  <license>Appache2.0</license>

  <buildtool_depend>catkin</buildtool_depend>
  <buildtool_depend>pkg-config</buildtool_depend>

  <depend>class_loader</depend>
  <depend>pluginlib</depend>
  <depend>roscpp</depend>
  <depend>std_msgs</depend>
  <depend>rviz</depend>
  <depend>eigen</depend>
  <depend>qtbase5-dev</depend>

  <export>
    <rviz plugin="${prefix}/plugin_description.xml"/>
  </export>

</package>
```
`package.xml`は通常とほとんど何も変わらないので詳しい説明は割愛します．
ただし，`pluginlib`の設定値を Rviz のプラグイン設定に渡す必要があり，下記記述部分がこれを行っている部分です．

```xml
  <export>
    <rviz plugin="${prefix}/plugin_description.xml"/>
  </export>
```


# おわりに
以上で必要なファイルの説明はすべて完了しました！
なるべく簡素に駆け足で解説しているので，不足部分や間違いがあると思います．
その場合はぜひコメント欄にてご指摘いただけると嬉しいです．
よろしくおねがいします！

というわけで次は，これをROS2（foxy）で実装するにはどうすんの...？ という話です．
次記事 -> https://qiita.com/RyodoTanaka/items/4ca117672ad171472578