ラジコンサーボを動かそう１

# はじめに

ここでは、Arduino UNOで <font color=red>**ラジコンサーボ**</font> を動かす実験をします。


目次へ戻るには [ここ](https://qiita.com/hsgucci/items/085680123f758c8c3eb4#%E5%AE%9F%E9%A8%93%E6%89%8B%E9%A0%86) をクリック

※本ページは実験のテキストです。

# ラジコンサーボとは

一般に「サーボモーター」と言うと、産業用の機械に使われるモーター・ギアボックス・回転角度センサが一体となったシステムの事を指します。
![servo_motor.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256470/24493d2f-6f44-ab78-509f-553977ab64da.png)

一方、トイ・ホビー用途であるラジコン（ラジオ・コントロールを略してRCとも言う）の世界では，RCカーやRC飛行機、RCヘリコプターに使用するプロポーショナル・サーボシステムの事を「サーボモーター」「サーボ」「ラジコンサーボ」「RCサーボ」などと呼んでいます．
![rc_servos.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256470/c34a8f34-df9a-aad4-5eef-e56de6b78993.png)

簡単に言えば<font color=red>**「通称『サーボモーター』には，ガチとオモチャの２種類がある」**</font>ということです．

ガチのサーボモーターを使うには，「モータ駆動回路」「角度検出回路」などの回路を別途用意する必要があります．
ホビーの方はそれら周辺回路基板も含めてモーターケースの中に内蔵されているので、単独で使うことができるのが特徴です．

## ラジコンサーボの用途

前述したように，ラジコンサーボは元々，

- RCカーではステアリングの動作
- RC飛行機では動作
- ヘリコプターではプロペラピッチの動作
 
に使われていました．
![servo_usecase.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256470/a1aac7f2-7c85-13b6-86c5-f310867cc3d0.png)

しかし，ラジコンサーボはモーターの回転角度を比較的簡単な信号で変えることができるので，
ロボットアームや二足歩行ロボット，四脚ロボットなどの様々なロボットが作られるようになりました．

近年「[Maker(メイカー)](https://ja.wikipedia.org/wiki/%E3%83%A1%E3%82%A4%E3%82%AB%E3%83%BC%E3%82%BA%E3%83%A0%E3%83%BC%E3%83%96%E3%83%A1%E3%83%B3%E3%83%88)」と呼ばれている，電子工作やものづくりの世界では，ラジコンサーボは多用されています．


# ラジコンサーボの信号

ラジコンサーボを動かすためには，「ラジコンサーボ信号」と呼ばれる特殊な信号を送ってやる必要があります．
長いので，以降は「サーボ信号」と略させてもらいます．

![servo_pulse.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256470/464abe0e-6a95-58d9-bca7-3e5feb373c04.png)

上の図を見ながら読んで下さい．
サーボ信号は，20ミリ秒おきに１回送信します．
１回送信するだけではダメで，常に送り続ける必要があります．
信号の状態は通常はLOW(0[V])ですが，HIGH(5[V])にするパルス幅の長さでラジコンサーボの角度が変わります．
1500マイクロ秒(1.5ミリ秒)が基準になる中央の値です（厳密に言うとフタバは1520マイクロ秒ですが）．
1500±700マイクロ秒にHIGHの時間を変えることで，サーボの角度が変わります．
 **ラジコンサーボ信号は<font color=blue>PPM信号</font>と言います．** 
「Pulse Position Modulation」の略で，日本語だと「パルス位置変調」になります．
 **<font color=red>PWM</font>** (Pulse Width Modulation＝パルス幅変調)に非常によく似ていますが，PWMのパルス幅は0～100%の全域で使うのに対して，PPMは20ミリ秒のうち4～11%の部分しか使いません．
サーボ信号をPWM信号と解説している方もいますが，厳密には「 **広い意味でのPWMで，PWMの特定デューティ比しか使わない** 」となります．

図では800マイクロ秒が-90度とか2200マイクロ秒が+90度と書いていますが，これ正確に90度じゃないです．
製品によって動作範囲が違うので，実物で試して使うしかありません．

Amazon等で売っている「HJサーボテスター」を使うと，800～2200マイクロ秒の値を確認しながらサーボ信号が出せます．
　・[Amazon 「HJサーボテスター」](https://amzn.asia/d/gyWFVIM)

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">HJサーボテスターの3つの動作モードの説明。<br>・ツマミで800〜2200usに可変<br>・1500usのニュートラルで固定<br>・800〜2200を往復(ツマミはスイープ速度) <a href="https://t.co/eoDvb9yw6A">pic.twitter.com/eoDvb9yw6A</a></p>&mdash; hsgucci404 (@hsgucci404) <a href="https://twitter.com/hsgucci404/status/1648884114341380098?ref_src=twsrc%5Etfw">April 20, 2023</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

Qiitaにも，HJサーボテスターの記事を書かれている方がいました．
　・[HJサーボテスターを使ってみた](https://qiita.com/Toshiaki0315/items/01192ab0be7467ced075)

ラジコンサーボを使ったロボットをつくる人は，動作確認用に「サーボテスター」を１つ持っておくといいでしょう．
HJサーボテスター以外にも，もっと簡易的な製品もありますし，Arduinoで作ることもできます．

# ラジコンサーボのケーブル色

ラジコンサーボは，製造会社によってケーブルの色が異なります．
真ん中の **<font color=red>5V(電源)が赤色</font>** という点は共通ですが，サーボ信号とGNDの色が異なっています．

- JRPROPO系だと **<font color=orange>サーボ信号が橙色</font>**， **<font color=brown>GNDが茶色</font>** です．
- フタバ・近藤科学系だと **<font color=gray>サーボ信号が白色</font>** ， **<font color=black>GNDが黒色</font>** です．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256470/900a9af0-5c85-526e-18ff-72483719f5b6.png)

分からなくなったら， **色が暗い＆濃い方がGND** と覚えておくと良いでしょう．

# Arduinoとラジコンサーボの配線

まずは物理的な配線をします．

下の図のように，Arduinoとラジコンサーボを配線してください．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256470/03110cb0-feb5-e6aa-3f5d-1ee52965cd9f.png)

ラジコンサーボのコネクタと，Arduinoとをジャンパ線で接続します．
 **サーボ信号は9番ピンに接続** してください．

参考として，実際の写真を掲載しておきます．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256470/11caaabb-03a3-e1df-c965-ce425be84b62.png)


# サンプルスケッチを読み込む

Arduinoには，ラジコンサーボを動かすサンプルプログラムが標準で準備されています．

メニューバーから，[ **ファイル** ]-[ **スケッチ例** ]-[ **Servo** ]-[ **Sweep** ]を開きます．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256470/e277a2f5-1c85-65d6-1127-29bb8f86abab.png)

# コンパイル，プログラムの書き込み，実行

それでは，これまでと同様に、コンパイルし，マイコンへの書き込みを行ってください。

 **<font color=red>書き込みが完了すると，プログラムが実行されて，いきなりサーボが動き始めます．</font>**

サーボが右へ左へと永遠に動き続けます．

 **プログラム的には0～180度を動くように命令していますが，実際はどのくらい動いているのか確認してみましょう．** 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256470/0d543cd6-9f1e-8d98-fddc-9bf4028814c7.png)

うるさいと思ったら，電源の5Vの線を外して止めましょう．


# スケッチの解説

実際のプログラムは英語でコメントが書かれていますが，日本語にしたSweep.inoを掲載しておきます．

```cpp:Sweep.ino
#include <Servo.h>    // Servo.hをインクルード

Servo myservo;  // Servoクラスの実体(インスタンス)をmyservoとして作成
                // ほとんどのボードで12個のクラスが作れる＝12個のサーボが動かせる

int pos = 0;    // サーボ角度の変数

// 初期化関数
void setup() {
  myservo.attach(9);  // myservoクラスに9番ピンを接続する
}

// ループ関数
void loop() {
  for (pos = 0; pos <= 180; pos += 1) { // 0 -> 180度へ，1度ずつ増加
    myservo.write(pos);              // write関数で出力
    delay(15);                       // 15ミリ秒のウェイトを入れる．ウェイトがないと一瞬で終わってしまう
  }
  for (pos = 180; pos >= 0; pos -= 1) { // 180 -> 0度へ，1度ずつ減少
    myservo.write(pos);
    delay(15);
  }
}
```

それぞれ解説していきます．

```cpp:インクルードとクラス・型の宣言
#include <Servo.h>    // Servo.hをインクルード

Servo myservo;  // Servoクラスの実体(インスタンス)をmyservoとして作成
                // ほとんどのボードで12個のクラスが作れる＝12個のサーボが動かせる

int pos = 0;    // サーボ角度の変数
```

 **Servo.h** は，Arduino開発環境であらかじめ準備されているので，いきなり `#include` して使えます．

`Servo myservo;` でServoクラスのインスタンスmyservoを宣言しています．
ところで，`int a;`は「int型の変数aを宣言する」と言いますが，「Servo型の変数myservoを宣言」とは言いません．
クラスは内部に変数と関数を持っていて，intやfloatのような単純な型とは別の存在なので，インスタンスとかオブジェクトという言い方をするのです．

`int pos = 0;`でサーボの角度の数値が入る変数posを宣言しています．0～180の値が入ります．

---
次は初期化関数です．

```cpp:初期化関数
// 初期化関数
void setup() {
  myservo.attach(9);  // myservoクラスに9番ピンを接続する
}
```

myservoが持っている`attach`関数を使って，使うピンを指定しています．
attachは「取り付ける」という意味ですから，myservoと実際のピンを関係づける，というような感覚ですね．
解説：[servo.attach関数](http://www.musashinodenpa.com/arduino/ref/index.php?f=1&pos=687)

---
次はループ関数です．

```cpp:ループ関数
// ループ関数
void loop() {
  for (pos = 0; pos <= 180; pos += 1) { // 0 -> 180度へ，1度ずつ増加
    myservo.write(pos);              // write関数で出力
    delay(15);                       // 15ミリ秒のウェイトを入れる．ウェイトがないと一瞬で終わってしまう
  }
  for (pos = 180; pos >= 0; pos -= 1) { // 180 -> 0度へ，1度ずつ減少
    myservo.write(pos);
    delay(15);
  }
}
```

for文を使って，posの値を0から180まで増やし，そのposを`myservo.write`関数を使って出力しています．
解説：[servo.write関数](http://www.musashinodenpa.com/arduino/ref/index.php?f=1&pos=720)

その後，同じ要領で180から0まで減らして出力します．
動作としては，下図のような動きを繰り返すことになりますね．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256470/8d1cdfdf-4026-ccff-bc4c-2fc4164321de.png)

ループが１回まわる度に`delay(15);`で待ち時間を入れているのは，
これが無いとforループが一瞬で終わってしまい，サーボの角度が変わる前に次の角度がwriteされてしまうからです．

# 補足
このサンプルプログラムは，0->180->0という動きでループ関数が１周まわるように書かれています．
正直，このような書き方は望ましくありません．
本来ループ関数は短い時間で高速に何回も回るべき関数です．
今は，１周に15ミリ秒 x 360回 = 5.4秒かかっています．
このようなプログラムだと，例えば他のセンサ入力に反応するプログラムを書いたとしても，5.4秒後にしか対応できないことになります．(もちろん，書き方次第でfor文の中でも対応することも可能ですが)
「他事(ほかごと)できないやん！」とツッコミたくなるプログラムなのです．

例えばですが，以下のプログラムのように，
loop関数の繰り返しをうまく利用するほうが，マルチタスクで同時並行的に動作するプログラムへと発展させる余裕が残ります．

```cpp:for文の代わりにloopを使う
// グローバル変数
int pos = 0;
int val = 1;

// 初期化関数は省略

// ループ関数
void loop() {
  if( pos == 0 ) val = 1;
  if( pos == 180 ) val = -1;
  pos += val;
  myservo.write(pos);
  delay(15);
}
```

シリアル通信の際のブロッキング関数ではありませんが，１回のループに長時間かけてはいけないのです．

# まとめ

ラジコンサーボでは，

- Servo.hを#includeすれば使えるようになる
- 変数を作るのと同様に，Servoクラスのインスタンスを作る
- attach関数で，どのピンに出力するのかを決める
- write関数に0～180の数値を書くことで，サーボが動く
- 1周に時間がかかるloop関数を書くのは良くない

という事が分かったと思います。

[目次](https://qiita.com/hsgucci/items/085680123f758c8c3eb4#%E5%AE%9F%E9%A8%93%E6%89%8B%E9%A0%86) へ戻って次の作業を行ってください。