Tinkercad Circuits で Arduino 入門8～RCサーボモータ～

# 概要

第8回目です。今回は **RCサーボモータ**（あーるしーさーぼもーた） について扱います。<br><img width="491" alt="2020-06-01_14h53_21.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/6e6c8bbb-a698-3411-db44-6796a5cdbe33.png">

「RCサーボモータ」について、はじめて見聞きする方は、まず、[YouTubeの動画](https://www.youtube.com/results?search_query=rc+servo) を適当に $2$ ～ $3$ 本程みていただくと、以降の説明が分かりやすいと思います。

# RCサーボモータ

RCサーボモータとは「**回転角度**を指定できる機構とマイコンを内蔵したモータ」のことで、**小型ヒューマノイドロボットの関節**などに利用されています。このため、**ロボット用サーボ**と呼ばれることもあります。<br><img width="598" alt="2020-06-01_14h56_05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/c5b18279-e934-576f-76b5-177bffdec2ac.png">

普通のDCモータとの違いは**<font color='red'>クルクル回転するわけではなく、外部からパルスで指定した角度まで回転して、その位置を保持する</font>**という点です。また、最大動作角も180°程度（1回転しない）ということも大きな特徴的になります。

- []()

もともとは、**RC**（Radio Control）という名前のとおり、**ラジコンカーのステアリング制御（進行方向を決める車輪のハンドル制御）**、ラジコン飛行機の翼角度制御などに使われていました。

なお、一般にサーボモータといった場合は、産業用ロボットに使用される「[ACサーボモータ](https://www.google.com/search?q=ac%E3%82%B5%E3%83%BC%E3%83%9C%E3%83%A2%E3%83%BC%E3%82%BF)」を指します。ACサーボは基本的に専用アンプ（ドライバ）を通して駆動させるもので、ここで紹介する方法では制御・駆動できません（RCサーボとACサーボは基本的に別物と考えてください）。

## 構造

RCサーボは、ケースボックスのなかに「DCモータ」「歯車（ギア）減速機」「ポテンショメータ（角度センサ）」「制御マイコン」などが収められています。これらがシステムとして機能して、外部から指定した角度まで回転して、それを保持するように動作します。<br><img width="761" alt="2020-06-01_15h23_59.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/1b837dd7-86df-99e0-2d6a-a53ff3574edd.png">

ギアが内蔵されているため、トルクは非常に大きいです。写真の [S03T 2BB](http://akizukidenshi.com/catalog/g/gM-01794/) は、トルクが $7.4\, \mathrm{kgf}\cdot \mathrm{cm}$ です。[ホーン](https://www.google.com/search?q=%E3%82%B5%E3%83%BC%E3%83%9C%E3%83%9B%E3%83%BC%E3%83%B3&source=lnms&tbm=isch) の先端（回転軸から$2\mathrm{cm}$の位置）に $2.0\mathrm{L}$ のペットボトル を取付けても余裕で持ち上げることができます。

## スペック

RCサーボのスペックとしては、**トルク**、**動作電圧**、**動作角**、**動作速度**、**価格** などに注意してください。

- [秋月電子通商／RCサーボモータ](http://akizukidenshi.com/catalog/c/cservo/)
- [共立エレショップ／RCサーボモータ](https://eleshop.jp/shop/c/c112067/)
- [ロボットショップ@Vstone／RCサーボモータ](https://www.vstone.co.jp/robotshop/index.php?main_page=index&cPath=72_95)


**<font color='red'>トルク</font>**は、$\mathrm{kgf}\cdot \mathrm{cm}$ の単位で表記されます。トルクとは、**回転させる力の大きさ**で「回転軸（出力軸）から力が加わる点までの距離」に「力」を乗じたものになります。

たとえば、次のように回転軸から $1.5\mathrm{cm}$ のところに、$4.0\mathrm{kg}$ の負荷を取り付けた場合、$1.5\times 4.0 = 6.0\, \mathrm{kgf}\cdot \mathrm{cm}$ の**負荷トルク**がかかります。このとき、**RCサーボモータの出力トルク**が、$6.0 \,\mathrm{kgf}\cdot \mathrm{cm}$ よりも大きければ、負荷を上に引き上げることができます。<br><img width="565" alt="2020-06-04_11h37_39.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/0d0c2b2a-480a-d607-ccc7-02e556a7488c.png">

**<font color='red'>動作電圧</font>**は、小型のものでは $5.0\mathrm{V}$ 前後のものがあります。Arduinoのロジックレベルと同じなので使いやすいです。$4.8\mathrm{V}$ という表記のものを見かけますが、実用上、$5.0\mathrm{V}$ 電源で使用して問題ありません。なお、電圧は $5.0\mathrm{V}$ ですが、**駆動時・高負荷時には大きな電流を必要とします**。そのため、**外部電源できれば電池・バッテリーの使用が望ましい**です。

**<font color='red'>動作角（最大動作角度）</font>**は、ロボット用のものであれば、$\pm\,90$°（$180$°）の製品がメジャーです。ラジコン用では$\pm\,60$° の製品もあるので注意します。また、$360$°の連続回転が可能なローテーションサーボという商品もあります（回転角度でなく回転速度と方向が制御できるようです）。

- [360°連続回転サーボ（ローテーションサーボ）](http://akizukidenshi.com/catalog/g/gM-08305/)@共立エレショップ

**<font color='red'>価格</font>**は、性能により大幅に変わってきます。低トルクの安いものは数百円から、高トルクのものは1万円以上のものまであります。

- [近藤科学 KRS-5054HV](https://www.vstone.co.jp/robotshop/index.php?main_page=product_info&cPath=72_95_142&products_id=5239) トルク <font color='red'>$58.6$</font> $ \mathrm{kgf}\cdot \mathrm{cm}$（$22,000$円）

**<font color='red'>動作速度</font>**は、$60$°を回転させるために必要な時間として表記されます。例えば $0.1\,\mathrm{sec}/60\mathrm{deg}$ のように表記されます（度=degree）。内部に減速機があるので、トルクを求めると速度が犠牲になります。基本的にサーボモータ自体には**速度の制御機能はありません**（ゆっくり回転させたい場合は、小刻みに目標値（角度）を与えます）。

## 制御方法

回転させたい角度を指定するためには**PWM信号**（パルス）を使います（シリアル通信で制御するタイプの製品もあります）。一般には、$10$～$20\mathrm{ms}$ の周期で、パルス幅を $1$～$2\mathrm{ms}$ 程度の範囲で変化させることで角度を指定します。<br><img width="644" alt="2020-06-03_18h18_05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/23077a74-d9f0-b5d2-5152-d46ac19a7c08.png">

一般的なRCサーボモータでは、パルス幅の $1$～$2\mathrm{ms}$ の変化に対して、サーボホーンが約 $180$° 回転します。

ところで、周期が $10 \mathrm{ms}$ ということは、周波数 $100\mathrm{Hz}$ です。[第5回](https://qiita.com/code0327/items/2a224ae4f08b8fdac355) で学習しましたが、`analogWrite(...)` による PWM出力の周波数は基本的に固定で、5番と6番ポートは $970\mathrm{Hz}$ 、その他は $490\mathrm{Hz}$  でした。ということで、`analogWrite(...)` を使ったPWM信号で、RCサーボモータを制御することはできません。

制御には、専用のライブラリを使用します。

# 回路の構成

次のように回路を接続します。RCサーボモータは、動くときに大きな電流を必要とするので、それに対応できるように外部電源を準備します（Arduinoの $5\mathrm{V}$ 単位では電流容量が大きくないので使用しません）。回路はとてもシンプルです。<br><img width="621" alt="2020-06-03_18h35_18.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/df4ee731-934c-c717-555b-58d965de1128.png">

※ **外部電源のGND** と **ArduinoのGND** を接続することを忘れないでください。

ここでは、デジタル**12番ポート**を制御信号を出力するためのポートとして使用しましたが、12番以外でもOKです。

# ロジックの構成

まずは、ブロックを使ってロジックを構成してみましょう。<br><img width="559" alt="2020-06-03_18h43_50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/9bfa50a6-6731-65b7-a7e8-4326633ff5d7.png">

シミュレーションを開始するとモータが回転することが分かります。しっかりと観察すると、可動域（動作角度範囲）が$180$度よりも**少し狭い**ことがわかります。このように、可動域は、データシートを参照したり、実際に実験をしたりして確認する必要があります。

- 回路例は[こちら](https://www.tinkercad.com/things/keXYecScxH1)（TinkerCadに移動します）。

ここで、次のことを試してみて下ください。

- 「**2**秒を待機」を「**0.5**秒を待機」にしてシミュレーションを実行
- 角度の指定を「マイナス」や「270」にしてシミュレーションを実行
- Arduinoと外部電源の **GND** を接続せずにシミュレーションを実行<br>（サーボモータのGND（接地）を外部電源のGNDだけと接続）<br>（サーボモータのGND（接地）をArduinoのGNDだけと接続）

また、次のように**オシロスコープ**を接続して、制御信号（PWM信号）の変化を観測してみるのも面白いです。<br><img width="872" alt="2020-06-03_19h11_44.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/827b4133-1d04-bad6-1399-b263ffdb3f3a.png">



# コードの確認

エディタのモードをテキストに切り替えて、コードを確認してみましょう。<br><img width="503" alt="2020-06-03_19h38_34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/537d5a50-79bc-7e30-2988-0dfeeead074e.png">


次のようなコードになっています。

```cpp:サーボモータの制御.ino
#include <Servo.h>

Servo servo_12;

void setup()
{
  servo_12.attach(12);
}

void loop()
{
  servo_12.write(180);
  delay(2000); // Wait for 500 millisecond(s)
  servo_12.write(0);
  delay(2000); // Wait for 500 millisecond(s)
}
```

コードでは、まず最初に `#include <Servo.h>` により、サーボモータを制御するためのライブラリの読み込みを行なっています。

- **<font color='purple'>中級者向け補足</font>**：このライブラリを使用すると、デジタル入出力ピンの $9$ と $10$ からの PWM出力 が機能が無効になります。詳細はリファレンス参照。
  - 公式リファレンス：[Servo library](https://www.arduino.cc/en/reference/servo)

## コードの編集

ところで、次のように「setup関数」と「loop関数」の位置を変えると、どうなるでしょうか？

```cpp:サーボモータの制御（setupとloopの位置を逆転）.ino
#include <Servo.h>

Servo servo;

void loop()
{
  servo.write(180);
  delay(2000); // Wait for 500 millisecond(s)
  servo.write(0);
  delay(2000); // Wait for 500 millisecond(s)
}

void setup()
{
  servo.attach(12);
}
```

実際に書き換えて、そのあとシミュレーションを開始してみましょう。・・・結果、何ら問題なく動きますね。

では、次は `#include <Servo.h>` と `Servo servo;` の位置を入れ替えてシミュレーションを開始してみてください。今度は、エラーが発生しますね。

**<font color='red'>プログラムを効果的に理解・勉強するためには</font>**「**なぜ、その文（命令）が必要なのか（＝その文が抜けると何が問題なのか**」や「**その文（命令）は、その位置にある必要があるのか、それとも順序を入れ替え可能なのか**」といったことを**意識しながら読み書きすること**です。そして、自分の考えが正しいかを手間を惜しまずに、試すことです。

また「（結果に影響を与えずに）**書き換え可能なところはどこか？**」を考えることも勉強になります。たとえば、`servo_12` というのは**単なる変数名**なので、次のように `servo` に変えても何ら問題ありません（ただし、4カ所すべてをかえてください）。

```cpp:サーボモータの制御2.ino
#include <Servo.h>

Servo servo;

void setup()
{
  servo.attach(12);
}

void loop()
{
  servo.write(180);
  delay(2000); // Wait for 500 millisecond(s)
  servo.write(0);
  delay(2000); // Wait for 500 millisecond(s)
}
```

さらに、次のように書き換えることもできます。

```cpp:サーボモータの制御3.ino
#include <Servo.h>
int dTime;
Servo servo;

void setup(){
  dTime = 2000;
  servo.attach(12);
}

void loop(){
  servo.write(180);
  delay(dTime);
  servo.write(0);
  delay(dTime);
}
```

このように同じ処理をするプログラムでも、ほぼ無限通りの書き方ができるということを意識してください（どんなコードが良い／悪い、というのは状況によって変わり、一概にはいえません）。

# 複数のサーボモータの制御

[リファレンス](https://www.arduino.cc/en/reference/servo) によれば、最大で $12$ 個のRCサーボが同時に利用できるようです。以下は、2個のサーボモータを同時に使用しています。<br><img width="748" alt="2020-06-04_12h22_09.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/78df4f00-3e88-6e69-7176-efe39197735a.png">

実際に、2個のサーボモータを配置・配線して動かしてみてください。

- 回路例は[こちら](https://www.tinkercad.com/things/1ai2uBkHGaC)（TinkerCadに移動します）。

## 制御用ボード（RCサーボコントローラ）

実践において、複数のRCサーボを制御する場合は、専用の制御ボード「**RCサーボコントローラ**」を利用したほうが便利で確実です。ArduinoからRCサーボコントローラに対しては SPI や シリアル通信 で簡単な命令を送り、RCサーボコントローラでは、それを受け取って PWM信号 を生成・出力します。

- [RCサーボコントローラ](https://www.robotshop.com/jp/ja/servo-controllers.html)

# おまけ：手動操縦のロボットアーム

ここまでに学習した知識・技術を組み合わせて、[アナログスティック／ジョイスティック](https://eleshop.jp/shop/g/gF42121/)（内部回路は可変抵抗・ポテンショメータと同じ）と**RCサーボモータ**を組み合わせて、手動操縦ができる簡単なロボットアームをつくることができます。

- [RCサーボモータを使ったアームの製作例](https://www.google.com/search?q=RC%E3%82%B5%E3%83%BC%E3%83%9C%E3%83%A2%E3%83%BC%E3%82%BF+%E3%82%A2%E3%83%BC%E3%83%A0&source=lnms&tbm=isch)
- [アナログスティック](https://eleshop.jp/shop/g/gF42121/)

アナログスティックの操作量をアナログ入力 `analogRead(...)` で取り込み、その量を処理して、サーボモータ角度出力 `servo.write(...)` とします。

ただし「サーボモータ角度 $\propto$ アナログスティックの現在の入力量」でロジックをつくると、スティックから指を離したときにアームも最初位置に戻ってしまいます。「サーボモータ角度 $\propto$ アナログスティックの過去の入力量の累積（積分）」でロジックを構成するようにしないといけません。



# おまけ：消毒用アルコールの自動プッシュ装置

コロナウィルスの影響で、いろいろな場所に消毒用アルコールが設置されています。このなかで、既存の手動プッシュ式のボトルを取り付け、手をかざすとセンサーが反応し、電動機構が動いてポンプをプッシュしてくれるものがあります（ボトルに手を触れることなく手指の消毒が可能）。

- 参考：[自動アルコール噴霧器](https://www.cleancare.jp/item/purehygiene.html)

このシステムの自作することを考えてみましょう。

RCサーボモータは強力なので、特別な機構なしでポンプのプッシュが可能とします。あとは、どうやって「手がかざれたことを検出するか」「どんなセンサを使用するか」「センサから出力される信号で、サーボモータを駆動させるためには、どのようなロジックが必要か？」を具体的に考えてみてください。

また、可能であれば、TinkerCad Circuit で模擬的なシステムをつくってみましょう。