ROS 2 Humble 環境構築 #インストール編

# はじめに
今回は、初めてROS2を触れる方向けに、ROS2の概要と特徴、Ubuntu上での環境構築方法、およびC++を使ったサンプルプログラムの作成手順を詳しく解説します。

# ROS1・ROS2とは？
ROS（Robot Operating System）は、ロボット開発を効率的に進めるためのオープンソースソフトウェア（OSS）のフレームワークです。以下の特徴があります：

- ロボットの動作をプログラムで制御するためのライブラリやツールを提供。
- センサーやアクチュエーター、シミュレーション環境との連携が容易。
- 世界中の研究者・開発者が利用しており、多数のリソースが公開されている。
- 移動ロボットやロボットマニピュレータ等のCADファイルをシミュレーターに呼ぶ出し、GUIのよる操作やC言語などのプログラムで動作の確認などを行うことも可能である。


ROS2は、ROS1の後継バージョンとして、以下の改善点を含んでいます：

- リアルタイム性の向上：通信にDDS（Data Distribution Service）を採用。
- マルチプラットフォーム対応：WindowsやMacにも対応。
- ライフサイクル管理：ROS1で課題だったノードの管理機能が強化。

現在、ROS1はサポートが終了（EOS）しており、ROS2への移行が推奨されています。

# 使用環境
・OS: Ubuntu 22.04 LTS
・ROS2: Humble Hawksbill（最新のLTSバージョン）


# ROS 2 Humbleのインストール


1. 必要なパッケージのインストール
以下のコマンドで、システムとソフトウェアをアップデートし、ROS2 Humbleのデスクトップ版をインストールします。`ros-humble-desktop`は、ROS2のデスクトップ版で、GUIツールやシミュレーターを含むフルセットのパッケージです。
```bash
sudo apt update && sudo apt upgrade -y
sudo apt install ros-humble-desktop
```
次にROS2の環境変数を設定します。環境変数とは、OSやアプリ内で何か動作が行われる際に使用される情報です。環境変数を設定することで、毎回手動で設定コマンドを実行する必要がなくなる、ROS2関連のコマンド（例：ros2 run）をどのディレクトリからでも実行可能になるなどのメリットがある。
```bash
echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc
source ~/.bashrc
```
##　ワークスペースの作成
ROS2では、ワークスペースというフォルダを利用して、独自のプログラムやパッケージを管理します。以下のコマンドで作成します。
```bash
mkdir -p ~/ros2_ws/src
cd ~/ros2_ws
colcon build
source install/setup.bash
```
- `mkdir -p ~/ros2_ws/src`: ワークスペース（ros2_ws）とソースコード用のフォルダを作成
- `colcon build`: ワークスペース内のパッケージをビルド
- `source install/setup.bash`: ビルド後の環境をターミナルに適用

これでROS2のプログラムを実行する環境が整ったので、次章でプログラムを作成したいと思います。

## サンプルプログラムの作成（C++)
### 1. パッケージの作成

以下のコマンドで、新しいC++パッケージを作成します。
```bash
cd ~/ros2_ws/src
ros2 pkg create --build-type ament_cmake my_cpp_demo
```
- `cd ~/`で移動したいディレクトリを指定します。Windows PCでいうところの、GUIでフォルダを移動しているような感じです。
- `--build-type ament_cmake`: ROS2推奨のビルドツールament_cmakeを指定
ament_cmakeとは,ROS2のCMakeベースのパッケージビルドシステムです。
- `my_cpp_demo`: パッケージ名（フォルダ名）



### 2. ソースコードの作成
先ほど作成した`my_cpp_demo/src/`ディレクトリ内に、`talker.cpp`と`listener.cpp`を作成します。`talker.cpp` と `listener.cpp` は、ROS 2 の基本的な通信機能である**パブリッシャー（Publisher）とサブスクライバー（Subscriber）を実装したプログラムです。これらは、ROS 2 の特徴的な通信モデルである「トピック」を介してデータを送受信します。

以下では、普通のC++プログラムとの相違点を比較しながら、それぞれのプログラムの役割と特徴を解説します。

## talker.cpp
```c++
talker.cpp
#include <rclcpp/rclcpp.hpp>
#include <std_msgs/msg/string.hpp>
#include <chrono>
#include <memory>

using namespace std::chrono_literals;

class Talker : public rclcpp::Node
{
public:
    Talker()
        : Node("talker"), count_(0)
    {
        publisher_ = this->create_publisher<std_msgs::msg::String>("topic", 10);
        timer_ = this->create_wall_timer(
            1s, std::bind(&Talker::publish_message, this));
    }

private:
    void publish_message()
    {
        auto message = std_msgs::msg::String();
        message.data = "Hello, ROS 2! " + std::to_string(count_++);
        RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", message.data.c_str());
        publisher_->publish(message);
    }

    rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
    rclcpp::TimerBase::SharedPtr timer_;
    size_t count_;
};

int main(int argc, char *argv[])
{
    rclcpp::init(argc, argv);
    rclcpp::spin(std::make_shared<Talker>());
    rclcpp::shutdown();
    return 0;
}
```

### talker.cppの解説
#### 1. ノードの定義
```c++
class Talker : public rclcpp::Node {
```
- ROS 2 では「ノード」が通信の基本単位です。
- `Talker` クラスは、rclcpp::Node を継承し、ROS 2 のノードとして機能します。

#### 2．トピックの作成
```c++
publisher_ = this->create_publisher<std_msgs::msg::String>("topic", 10);
```
- ` create_publisher` を使って、文字列型メッセージを送信するパブリッシャーを作成します。
- `topic` はトピック名で、送信先の識別子として使われます。
- `10` はキューサイズで、未処理メッセージの最大数を指定します。

#### 3. タイマーの利用
```c++
timer_ = this->create_wall_timer(1s, std::bind(&Talker::publish_message, this));
```
- 1秒ごとに publish_message 関数を実行するタイマーを設定しています。

#### 4. メッセージの送信
```c++
auto message = std_msgs::msg::String();
message.data = "Hello, ROS 2! " + std::to_string(count_++);
publisher_->publish(message);
```
- std_msgs::msg::String 型のメッセージを作成し、データを設定して送信します。






## listener.cpp
```c++
listener.cpp
#include <rclcpp/rclcpp.hpp>
#include <std_msgs/msg/string.hpp>

class Listener : public rclcpp::Node
{
public:
    Listener()
        : Node("listener")
    {
        subscription_ = this->create_subscription<std_msgs::msg::String>(
            "topic", 10, std::bind(&Listener::receive_message, this, std::placeholders::_1));
    }

private:
    void receive_message(const std_msgs::msg::String::SharedPtr msg) const
    {
        RCLCPP_INFO(this->get_logger(), "Received: '%s'", msg->data.c_str());
    }

    rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;
};

int main(int argc, char *argv[])
{
    rclcpp::init(argc, argv);
    rclcpp::spin(std::make_shared<Listener>());
    rclcpp::shutdown();
    return 0;
}
```
### listenerの解説

1. ノードの定義
```c++
class Listener : public rclcpp::Node {
```
- `Listener` クラスは、`rclcpp::Node` を継承し、ROS 2 のノードとして機能します。

2. トピックの購読（サブスクライブ）
```c++
subscription_ = this->create_subscription<std_msgs::msg::String>(
    "topic", 10, std::bind(&Listener::receive_message, this, std::placeholders::_1));
```
- `create_subscription` を使って、「topic」というトピックのメッセージを受信するサブスクライバーを作成します。
- `receive_message`関数をコールバックとして登録し、メッセージ受信時に処理します。

3. メッセージの受信
```c++
void receive_message(const std_msgs::msg::String::SharedPtr msg) const {
    RCLCPP_INFO(this->get_logger(), "Received: '%s'", msg->data.c_str());
}
```


## 3. CMakerLists.txtの修正
`my_cpp_demo/CMakeLists.txt`を以下のように編集して、依存関係を追加します。

### 必要な依存関係を追加
```cmake
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
```

### 実行可能ファイルのビルドを設定

```cmake
add_executable(talker src/talker.cpp)
ament_target_dependencies(talker rclcpp std_msgs)

add_executable(listener src/listener.cpp)
ament_target_dependencies(listener rclcpp std_msgs)

install(TARGETS
  talker
  listener
  DESTINATION lib/${PROJECT_NAME})
```


## 4. ビルド
以下のコマンドでパッケージをビルドします。
```bash
cd ~/ros2_ws
colcon build
```

## 5. プログラムの実行
ビルド後、環境を設定してプログラムを実行します。
#### 環境設定
```bash
source ~/ros2_ws/install/setup.bash
```

#### talker.cppを実行
新しいターミナルで以下を実行します。
```bash
ros2 run my_cpp_demo talker
```

#### listener.cppを実行
別のターミナルで以下を実行します。
```bash
ros2 run my_cpp_demo listener
```


## 6. 実行結果
- Talkerが"Publishing: 'Hello, ROS 2!'"とメッセージを出力します。
- Listenerが"Received: 'Hello, ROS 2!'"と受信メッセージを出力します。