ROSによるArduinoの制御方法

# 概要

ROS を用いた Arduino の制御方法や、その環境構築について解説していきます。
公式のページ：http://wiki.ros.org/rosserial_arduino/Tutorials/Arduino%20IDE%20Setup

## 説明すること
- ROSでArduinoを制御するための環境構築方法
- Arduinoに組み込むプログラムの内容

## 動作環境
- Raspberry Pi 4 (4GB)
- Ubuntu server 20.4LTS (64bit)
- ROS Noetic
- ELEGOO R3 UNO (Arduino互換ボード)

# 環境構築手順

1. Arduino IDE のインストール
1. 依存パッケージのインストール
1. Arduinoライブラリのインストール
1. 動作確認

## 1. Arduino IDE のインストール

### 1-1. インストール手順
1. 下記サイトの『Linux ARM 64bits』から圧縮ファイルをダウンロード  
https://www.arduino.cc/en/software  
1. ダウンロードしたファイルをHOMEフォルダに展開
1. 展開したフォルダ内の『install.sh』を実行  
1. デスクトップに『Arduino IDE』のアイコンが作成される

### 1-2. 動作確認
まずはROSを絡めずに、Arduinoが問題なく動作するかを確認する。
![Untitled Sketch 2_ブレッドボード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634805/4f4239d3-ccbc-c6a7-7d37-dc57ae2c98a9.png)

1. Arduinoの11番ピンでLEDが光るように配線 (上図)
1. Raspberry Pi と Arduino を接続
1. ツール ⇒ ボード で対象ボードを設定
1. ツール ⇒ シリアルポート 対象ポートを設定
1. Arduino IDE を起動し、下記ソースを記述
1. コンパイルし、マイコンボードに書き込む
1. LEDが点滅すればOK

```
void setup() {
  pinMode(11, OUTPUT);
}

void loop() {
    digitalWrite(11,HIGH);
    delay(100);
    digitalWrite(11,LOW);
    delay(100);
}
```

書き込み時にパーミッションエラーとなる場合、ユーザーを『dialout』グループに所属させる必要がある。

```
sudo usermod -a -G dialout <ユーザ名>
```

ここで動作しないなら、設定が間違っているかArduinoが壊れている。  

## 2. ROS依存パッケージのインストール
[ROS公式のページ](http://wiki.ros.org/rosserial_arduino/Tutorials/Arduino%20IDE%20Setup)を見ると、インストール方法には2種類の方法がある。  

- apt によるインストール
- ソースビルドによるインストール

本来は上記のどちらかでインストールできるはずだが、うまく動作しなかったため両方行った。  

**apt によるインストール**

```
sudo apt install ros-noetic-rosserial-arduino
sudo apt install ros-noetic-rosserial
```

**ソースビルドによるインストール**

```
cd ~/catkin_ws/src
git clone https://github.com/ros-drivers/rosserial.git
cd ~/catkin_ws
catkin_make
```

> 
**※ 公式の方法でトラブル発生**
上記のソースビルドの場合、公式では最後に`catkin_make install`をしろと書いてある。
が、これを実行すると他パッケージのビルド関連が無効になってしまった。
`catkin_make install`は無くても動作するため、今回は省略している。

## 3. Arduinoライブラリのインストール
ライブラリ自体は『Arduino IDE』から取ってくる。(非公式？)  

1. Arduino IDE を起動
1. スケッチ ⇒ ライブラリをインクルード ⇒ ライブラリを管理
1. 『rosserial』と検索  
1. Michael Furguson 氏の『Rosserial Arduino Library』のバージョン0.7.9をインストール
1. \<ros.h> のインクルードが使用可能

参考ページ：https://answers.ros.org/question/361930/rosserial-arduino-compilation-error-no-cstring/

> 
**※ 公式の方法でエラー発生** (Noeticだけかも)
公式では下記方法でライブラリが作成されるが、本環境ではコンパイル時にエラーとなる。
もし上手くいかなかったら、『ros_lib』を削除し、上記の方法でやり直す。
参考までに。  

>
```
cd Arduino/libraries
rm -rf ros_lib
rosrun rosserial_arduino make_libraries.py .
```
上記コマンドにより『ros_lib』が生成される。

## 4. 動作確認
上記までで一通りの動作環境が整ったので、実際に動作するかの確認を行う。

**実装内容：PC操作でROSから信号を受け取ったときにLチカする**  

### 4-1. Arduino にプログラムを組み込む
下記コードをArduino IDEに記述し、マイコンボードに書き込む。

```
#include <ros.h>
#include <std_msgs/Empty.h>

ros::NodeHandle nh;

void messageCb( const std_msgs::Empty& toggle_msg) {
  digitalWrite(11, HIGH);
  delay(100);
  digitalWrite(11, LOW);
  delay(100);
}

ros::Subscriber<std_msgs::Empty> sub("toggle_led", &messageCb );

void setup(){
  pinMode(11, OUTPUT);
  digitalWrite(11, HIGH); 
  nh.initNode();
  nh.subscribe(sub);
}

void loop(){
  nh.spinOnce();
  delay(1);
}
```

### 4-2. ROSでの制御
ターミナルを3つ立ち上げ、それぞれ下記コマンドを実行。

**1つ目：ROSマスターの起動**

```:ターミナル1
roscore
```

**2つ目：ROSシリアル通信の開始**
最後の『/dev/ttyACM0』は各環境に合わせる。

```:ターミナル2
rosrun rosserial_python serial_node.py /dev/ttyACM0
```

**3つ目：トピックを送信**
コマンドを送信するたびにLEDが光る。

```:ターミナル3
rostopic pub toggle_led std_msgs/Empty --once
```

ここまで問題なく動作していればOK。
あとはArduinoに組み込むプログラムやROSのパブリッシャーを用意することで、自由に Arduino を制御することが可能となる。

# Arduino内のサブスクライバー

Arduino に組み込むプログラムは、『ROSライクな記述ができる』だけであって、ROSとは記述方法が異なる。
が、だいたいは通常のパブリッシャー・サブスクライバーと同じように記述することになる。  

上記で使用したコードを例に解説を行う。
PC内の ROS からトピックを受け取ってLチカするという事は、**『Arduino内ではサブスクライバーが実行されている』**と考えてもらいたい。  

## 各コードの解説
**ROSヘッダー、トピックの型のインクルード**
ROSヘッダーは必須。
以降は使用するトピックに合わせて必要な分だけインクルードする。

```
#include <ros.h>
#include <std_msgs/Empty.h>
```

**ROSノードハンドルの宣言**
setup関数、loop関数の両方で使用するため、グローバル変数として宣言する。

```
ros::NodeHandle nh;
```

**コールバック関数**
トピックを受け取った時の動作内容をコールバック関数内に記載する。
普段 Arduino に記述している内容はここに書くことが多くなる。

```
void messageCb( const std_msgs::Empty& toggle_msg) {
  digitalWrite(11, HIGH);
  delay(100);
  digitalWrite(11, LOW);
  delay(100);
}
```

**トピックとコールバック関数を紐付け**
サブスクライバーに、上記で作成したコールバック関数とトピック名を紐付ける。
本家と異なり、サブスクライバー宣言時にトピックの型を指定する必要がある。
サブスクライブするトピックの名前や型を変更する場合、ここで指定する。

```
ros::Subscriber<std_msgs::Empty> sub("toggle_led", &messageCb );
```

**setpu関数 (初期化)**
Arduino 起動時に一度だけ実行される関数。
通常の Arduino の初期化コードに加え、**ノードハンドルの初期化、サブスクライバーの追加**を行う必要がある。

```
void setup(){
  pinMode(11, OUTPUT);
  digitalWrite(11, HIGH); 
  nh.initNode();
  nh.subscribe(sub);
}
```

**loop関数 (無限ループ)**
Arduino 起動中に繰り返される関数。
基本的にはコールバック関数を実行するための`nh.spinOnce();`を記述するだけで良い。

```
void loop(){
  nh.spinOnce();
  delay(1);
}
```

# 参考ページ
http://wiki.ros.org/rosserial_arduino/Tutorials
https://qiita.com/nnn112358/items/059487952eb3f9a5489b