ROS を用いたラジコン作成：ソフトウェア設計編

# 概要
この記事は[RaspberyPi , Arduino , ROS を使ってラジコンを作る！！](https://qiita.com/take4eng/items/0b21c4927d42b3d7cd62)のソフトウェア設計編です。
全体の概要や流れに関しては、本編をご覧ください。

[ROS を用いたラジコン作成：本編 (全体まとめ)](https://qiita.com/take4eng/items/0b21c4927d42b3d7cd62)
[ROS を用いたラジコン作成：要件定義編](https://qiita.com/take4eng/items/916fbbc68e2f7e34e384)
[ROS を用いたラジコン作成：ROS環境構築編](https://qiita.com/take4eng/items/70f167320ede46e4139c)
[ROS を用いたラジコン作成：Arduino制御編](https://qiita.com/take4eng/items/527793c9a1f902a0a62c)
[ROS を用いたラジコン作成：コントローラー制御編](https://qiita.com/take4eng/items/e067628971fa171d6d5a)
[ROS を用いたラジコン作成：ハードウェア設計編](https://qiita.com/take4eng/items/271924b986e072378439)
[ROS を用いたラジコン作成：ソフトウェア設計編](https://qiita.com/take4eng/items/1d5007ab623c3f7d3ad4) **← 今ここ**
[ROS を用いたラジコン作成：ソフトウェア制作編](https://qiita.com/take4eng/items/99df06b0e28d7c601632)
[ROS を用いたラジコン作成：ハードウェア制作編](https://qiita.com/take4eng/items/21d34d5bf56f99aba54e)
![S__24641539.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634805/b0d59ec3-4226-c14b-6730-acd771a44b9d.jpeg)

## 今回やること

- **入力から出力までの経路を確認する**
- **制御すべきハードの動作を把握する**
- **制御するために必要な信号の内容を確認する**
- **パブリッシュ及びサブスクライブの内容を考える**
- **ROSとして成り立つようにプログラムの構造を考える**
- **フローチャートを作成する**

## 今回できるもの
**ROSプログラム概要図**
![ROS-CAR-ノード関係図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634805/9e58ed30-d3a3-093e-29a7-dc98e651d853.png)
**フローチャート (ros_car_node)**
![ROS-CAR-制御フローチャート1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634805/ad1a466b-8112-2012-40ea-e5951e6f4b6b.png)
**フローチャート (Arduino)**
![ROS-CAR-制御フローチャート2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634805/71069fda-febf-558b-76b6-badfc0d514c6.png)

# ソフトウェア設計
前回の[ハードウェア設計編](https://qiita.com/take4eng/items/271924b986e072378439)により、『どんなパーツを使うのか』『どんな動かし方をするのか』が決定しました。
では次に、**『各パーツの制御方法』や『制御に必要な内容』**を考えていきます！！

# 制御パーツの確認
ハード設計のおさらいにはなりますが、これから作成するラジコンカーの概略図は下図のようになります。

1. コントローラーから RaspberryPi へ操作信号を送る
2. RaspberryPi から Arduino へ制御信号を送る
3. Arduino から各パーツへ制御信号を送る
4. 各パーツが動作する

という流れになります。
![ROS-CAR-概略図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634805/4358e494-c9a6-ab6b-ffb6-07e626662258.png)

# 制御プログラムの把握
制御すべき内容を把握したところで、どんなプログラムが必要なのかを考えます。
上記の内容より、下記の制御プログラムが必要になることが分かります。

1. **コントローラーの操作信号を RaspberryPi が受け取るプログラム**  
(コントローラー ⇒ RaspberryPi)
2. **受け取った操作信号を解析し、Arduino に制御信号を送るプログラム**  
(RaspberryPi ⇒ Arduino
3. **受け取った制御信号を解析し、各パーツに制御信号を送るプログラム**  
(Arduino ⇒ LED, サーボモーター, DCモーター)


# ROSのプログラム設計
上記の内容により、何となく制御すべき内容を把握することができました！
では次に、**今回の主役であるROSのプログラム**について考えていきましょう！！

今回、使用技術や細かい内容は事前準備編にて記載してあります。
コントローラーと RaspberryPi の通信は[『コントローラー制御編』](https://qiita.com/take4eng/items/e067628971fa171d6d5a)に、RaspberryPi と Arduino の通信は[『Arduino制御編』](https://qiita.com/take4eng/items/527793c9a1f902a0a62c)に記載しているので、詳細はそちらをご確認ください。

## ROSトピックの構想
先ほど、必要となる制御プログラムの内容を3点挙げました。
上記の3点をROSを中心に考えると、必要な内容は以下のようになります。

1. **コントローラー信号のパブリッシャー**
2. **コントローラー信号のサブスクライバー**
3. **ラスパイArduino間のシリアル通信を担うノード**
4. **ラスパイArduino間の信号のパブリッシャー**
5. **ラスパイArduino間の信号のサブスクライバー**
6. **その他電子部品の制御プログラム**

先ほど示した概要図に少しだけ内容を加えました。
ROSでのやり取りが、どこからどこへ流れているのか分かると思います。
![ROS-CAR-概略図2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634805/d6c8ed18-ce13-6c9f-1339-5fdc911bdd2b.png)


### 1. コントローラー信号のパブリッシャー
[『コントローラー制御編』](https://qiita.com/take4eng/items/e067628971fa171d6d5a)でも記載したように、**『joyパッケージ』の『joy_node』を実行することで『joyトピック』がパブリッシュ**されます。

### 2. コントローラー信号のサブスクライバー
同様に[『コントローラー制御編』](https://qiita.com/take4eng/items/e067628971fa171d6d5a)にサブスクライブの方法を記載しています。
**『joyトピック』の型は『sensor_msgs::Joy』**ですので、それを購読できるようにコールバック関数を作ってあげるだけでOKです。

### 3. ラスパイArduino間のシリアル通信を担うノード
こちらも、[『Arduino制御編』](https://qiita.com/take4eng/items/527793c9a1f902a0a62c)に詳細を記載しています。
このノードは**『ROS と Arduino の仲介役』**でしかないので、その役割に専念してもらいます。

2つの仲介以外に仕事は無いため、以降はこのノードに関する解説は省略します。

### 4. ラスパイArduino間の信号のパブリッシャー
ここでは RaspberryPi内のROS から Arduino に配信するトピックについて考えます。
今回は、最終出力である電子パーツの制御から逆算していきます。

出力となる電子パーツは、以下の3点です。

1. LED ×4
2. サーボモーター
3. DCモーター

これらをどのように動作させるのかで、パブリッシュする内容が変わってきます。
要件によってまちまちですが、今回は以下の場合に各パーツが動作することを考えています。
また、動作に必要な信号についても一覧にまとめてみました。

|パーツ|動作|必要な信号|型|備考|
| ---- | ---- | ---- | ---- | ---- |
|LED ×4|全部光る|オンオフの2種|boolean|走行中も一部光らせたい|
|サーボモーター|指定位置まで回転|指定角度の値|int|正面を0°とし±65°で動作|
|DCモーター|静止, 正回転, 逆回転|3動作の3種|int|スピード調整はいらない|

上記より、今回の制御に必要となる信号の種類を把握することができました。
それぞれのパーツに合わせて、**『3つのトピックを配信するパブリッシャーを作成する』**こととします。

> 備考
LEDの信号は『boolean』と記載しましたが、実装時は『int』に変更しました。
コントローラーのボタン情報は『0 or 1 のint型』で得られ、そのまま流用したためです。
>> 振り返ると、3つの値を1つのカスタムメッセージとして配信する方がスマートだったかなと思います。
パブリッシャーが3つもあると、それに合わせてサブスクライバーも3つ用意しないといけないので・・・

### 5. ラスパイArduino間の信号のサブスクライバー
次に、Arduino で購読するトピックについて考えていきます。
こちらは Arduino 内に組み込むプログラムとなります。

上記に示した通り、RaspberryPi内のROSから配信されているトピックは、各出力パーツに合わせた3つのトピックです。
Arduino内のプログラムでは、それに合わせて**『3つのトピックを購読するサブスクライバーを作成する』**こととします。

Arduino に組み込むプログラムの記述方法に関しては、[『Arduino制御編』](https://qiita.com/take4eng/items/527793c9a1f902a0a62c)に記載しています。
や開発環境の構築方法や必要パッケージについても記載しているので、詳細はそちらからご確認ください。

### 6. その他電子部品の制御プログラム
最後に、LED, サーボモーター, DCモーターの制御方法について考えていきます。

上述したように、Arduino内のプログラムでは『各出力パーツに合わせた3つのトピック』を購読しています。
つまり、**『トピック受信時のコールバック関数に、各出力パーツの制御プログラムを記述する』**ことで、各パーツの動作を制御することが可能となります。

## ROSノードの構想
『送受信するトピック』と『各パブリッシャー及びサブスクライバー』が決定したところで、最後に**『実行ノード』と『所属パッケージ』**について考えていきます。

考えることは単純で、**『上記の処理内容をどのノードとして実行するか』**、また**『そのノードの所属パッケージはどれか』**を決めるだけです。
今回はトピックの数が少ないので、非常に簡単です。

### ・joy_node
まず、**『1. コントローラー信号のパブリッシャー』**。
こちらは既存のパッケージをインストールするため、特に考える必要はありません。

### ・ros_car_node
次に、**『2. コントローラー信号のサブスクライバー』と『4. ラスパイArduino間の信号のパブリッシャー』**。

この2つには関連性があり、**『コントローラーの操作情報を受信し、それを解析して3つのトピックを送信する』**という一連の流れがあります。
こういった関連を持つものは1つのノードにまとめちゃいましょう。

**『ros_car_node』**というのはテキトーに名付けました。
パッケージ名も**『ros_car』**とでもしておきます。

### ・Arduino内プログラム
最後に、**『5. ラスパイArduino間の信号のサブスクライバー』と『6. その他電子部品の制御プログラム』**です。

上記と同様に、この2つにも関連性があります。
**『3つのトピックを受信し、そのコールバック関数が実行され出力パーツを制御する』**という一連の流れがあります。

この2つは Arduino内のプログラムに組み込んでみましょう。
Arduino内のプログラムにはノード名やパッケージが存在しないので、特に決める必要はありません。
## ROSプログラム概要図
ここまでグダグダと記述してきましたが、決まった内容を概要図として作成しました。
下図を見れば、何となく作成すべきプログラムが見えるのではないかと思います。
![ROS-CAR-ノード関係図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634805/9e58ed30-d3a3-093e-29a7-dc98e651d853.png)

また結果論にはなりますが、制作したラジコンカーが動作しているときの内容は内容は下記のようになっています。
こちらはプログラム実行中に`rqtgraph`コマンドにて確認したものです。

![topic.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634805/e694b64e-a207-730f-dbac-1773aa9ad2e5.png)

『serial_node』が受信しているように見えるトピックはそのままArduinoに流れるので、実際は下図のように考えてもらえればと思います。

![topic2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634805/aa4540b8-fdca-772a-cc28-a83daa63c5c2.png)

# 制御フロー
ROSプログラムの内容も決まったところで、最後に具体的な制御フローを考えていきます。
つまり、**『コントローラーの操作が各出力パーツの動作に反映されるまでの流れ』**を決めていきます。

## 制御内容の把握
ここで大事なことは、**『コントローラーの操作に対してどんな出力を求めているか』**です。
私の独断と偏見で、操作に対する制御内容を決定しました。

**ボタン1**
押すと全てのLEDが光る。
離すと消える。

**右スティック**
左右の傾きに合わせてサーボモーターの回転位置を指定する。
左に傾いているときは左後ろのLEDを光らせる。
右に傾いているときは右後ろのLEDを光らせる。

**左スティック**
上下の傾きが半分をえたらDCモーターを正回転または逆回転する。
モーターが正回転するとき、同時に前2つのLEDを光らせる。

**他の操作**
無視。
何の影響も与えない。

上記が、コントローラーの操作に対する出力パーツの動作です。
『ボタン1』やら『左後ろのLED』と言われてもピンと来ないと思うので、コントローラーの写真と概要図を示しておきます。(サーボ側が前、DCモーター側が後ろ)
![f160343710.1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634805/650be736-c19e-3191-92a6-4d3220fb16c7.jpeg)
![ros_car_ブレッドボード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634805/c39936ef-387d-e35d-30a3-2a32d0115e6b.png)

## フローチャートの作成
出ました！！私の中でプログラムの設計と言えば**『フローチャート』**です！！
今回のソフトウェア設計編の中では**最後の工程**になります！！

上記でコントローラー操作による制御内容を把握したので、希望する動作となるようなフローチャートを作成します。

### 『ros_car_node』のフローチャート
まずは『ros_car_node』のフローチャートを作成します。
ここでは、**『コントローラーの操作信号を受け取ってから Arduino に信号を送信するまで』**の具体的な流れを記載します。
![ROS-CAR-制御フローチャート1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634805/ad1a466b-8112-2012-40ea-e5951e6f4b6b.png)
コントローラーの信号を受け取ったら、ちょいと変換して各パーツに対する値を送信しています。
書いてある通りですね。

### 『Arduinoプログラム』のフローチャート
次にArduino内に組み込むプログラムのフローチャートを作成します。
ここでは、**『Arduino が信号を受け取ってから各出力パーツにが動作するまで』**の具体的な流れを記載します。
![ROS-CAR-制御フローチャート2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634805/71069fda-febf-558b-76b6-badfc0d514c6.png)
3種まとめて載せると小さくなってしまいますね・・・すみません。
内容が気になる方は、画像をクリックして拡大していただければと思います。

# まとめ
以上で、大まかなソフトウェア設計が完了しました！
ここまで書けたら、あとは設計通りにプログラムを作成するだけです！！
実際にプログラム作成は、次工程の[『ソフトウェア制作編』](https://qiita.com/take4eng/items/99df06b0e28d7c601632)に記載しています。

ROSプログラムの設計では、以下の点が重要だと思われます。

- **入力信号から出力信号までの経路はどうなっているのか**
- **経路内で使用されるトピックの型や内容はそれぞれ何なのか**
- **各ノードは何をパブリッシュし何をサブスクライブするのか**

ROSに限らず、全体の流れを把握てから細部を決定していくのが大事ですね。

事前準備の段階ではもう少し複雑な設計構想を練っていたのですが、ハードウェアとの兼ね合いによりこの規模での設計となりました。
比較的簡単な流れではありますが、それでも文章や図にすると結構長くなってしまいますね。