PIC AVR 工作室別館 arduinoの館->TopPage->各種ライブラリ->サーボモーター

サーボモーター制御ライブラリ

arduinoはフィジカルコンピューティング用に作られ、発展してきたモノなので、サーボモーターを簡単に扱えるようなライブラリが Contributed Librariesに2種類公開されています(バージョン0011公開時点)。

以下に、サーボモーターの、arduinoとサーボモーターを繋いだ使い方やサンプルスケッチを纏めます。

写真は、手持ちのreduino-nanoに秋月で買ってきたサーボモーターを取り付けて実験しているところです。

サーボモーターについて

arduinoを入手するまではサーボモーターをマイコンに繋いで使ったことが無かった程度の私が書くのもなんなんですが、 ライブラリの使い方にも関係することが色々あるので、その辺りについて一通り整理しておいた方が良いと思うことを 以下に整理しておきます。

サーボモーターは、その名のとおりモーターの一種です。ただし、マブチの130モーターのような一般的なDCモーターとは違い、 VCC・GNDの電源以外にもう1本、制御信号を繋ぐ必要があります。この信号は、モーターの「角度」を制御するためのものです。

DCモーターは電源を繋ぐと勢い良くグルグルと回転し、逆に電源を繋ぐと逆回転します。そして電源電圧を変えると 回るスピードやトルク(回す力)が増したり減ったりします。サーボモーターはこれらのDCモーターとは動作がずいぶん違います。 DCモーターの軸が左右約180度程度の範囲内でしか回りません。その角度を制御するための信号が必要になるので、もう1本の 線を接続する必要があるわけです。

例えば自動車のラジコンを思い浮かべてください。ラジコン自動車にはDCモーターとサーボモーターの両方が使われています。

タイヤをDCモーターでグルグル回すことで、自動車は前や後ろに走ります。一方、ハンドルはそういうわけには行きません。 DCモーターでグルグルと際限なくハンドルを回しつづけたら、クルマはどこに向かって走って行くのか判りません。 (そういえば幼い頃、三輪車のタイヤ際限なくグルグル回しながら変な方向に走っていくのを楽しんだりした記憶が…)

ラジコン自動車のハンドルはサーボモーターを使って制御されています。プロポ(送信機)から指示したハンドルの角度 にあわせて前輪の角度を制御するようになっています。ラジコン自動車の前輪を思い浮かべれば、サーボモーターがどんな動きを するのかは簡単に想像できるかと思います。

サーボモーターの制御方法について

ライブラリの使い方の前に、マイコンとサーボモーターの間を流れる制御信号と電源線に関して少し整理しておきたいと思います。

サーボモーターに繋ぐ3つの線

サーボモーターの制御方法は厳格に規格化されているというわけではないようなのですが、大体どのサーボモーターも 大同小異のようです。電源(Vcc、GND)を繋ぎつつ、角度を制御する信号としてPWMのようなパルスの形の制御信号を 与えることによって制御します。その制御信号のHIGH状態の時間の長さに比例して角度が決定されます。

このように、Vcc、GND、制御信号の3つのピンがサーボモーターに接続されます。このピン配置は、 FUTABAやJRなどメーカーによって違いがあるようです。なので、サーボモーターを購入する時にはどのタイプなのか 注意する必要があるようです。

PWMと異なるのは、PWMはHIGHとLOWの信号幅の比率(0%~100%)がいわゆるアナログ値(サーボなら 角度)に相当するわけですが、サーボモーターに与える信号の場合、HIGH状態の長さにだけ意味があって、LOW状態の長さは 多少ブレてもあまり関係が無いと言う点です。PWMとはその点だけちょっと異なります。とはいえ、マイコンに 内蔵されているPWM機能を使ってはいけないということではありません。実際、後述する"ServoTimer1"は 内蔵PWM機能を使って制御しています。

サーボモーターを制御するパルス信号

サーボモーターの一般的な制御信号をグラフにして見てみてみましょう。サーボモーターを制御する信号は、 HIGHとLOWを一定周期で繰り返すパルス信号の形をしています。

パルスとパルスの間隔はどのサーボモーターでも約20ミリ秒程度とされていますが、実はこの時間は少しくらいいい加減でも 問題無いようです(角度には影響がない)。

先ほども触れたように、サーボモーターの角度を制御するのはHIGH状態を出力している時間の長さです。 この図のように1ミリ秒~2ミリ秒の長さのパルスを約20ミリ秒間隔で与えると、0度~180度の角度となって現れます。 なので、HIGHの時間はそれなりに厳密にする必要があるようです。

と言っても、幅を1ミリ秒にすれば0度に、2ミリ秒にすれば180度にキッチリ制御されるのかというと実はそんなことは無く 若干の誤差が生じるようです。そしてその誤差にも個体差が有り、多少のキャリブレーションが必要になるようです。

ラジコンのプロポにもトリマーのツマミが付いてますが、アレに相当する機能を考える必要があるということだと思います。 後述しますが、arduinoのライブラリにもその辺りの機能が盛り込まれています。

パルス信号の作り方、繋ぎ方

arduinoにはdelayMicroseconds()という関数があるので、これを使うことで1ミリ秒以下の短いパルスをある程度正確に 作り出すことが出来ます。が、ライブラリを使うともっと簡単に制御可能です。

ライブラリを使うと、パルスの幅ではなく角度(0度~180度)を数値で指定することが出来ます。また 個体差のある角度の差について、ある程度キャリブレーションをすることが出来ます(上記のラジコンプロポでいうところの トリマーのツマミに相当)。この辺りについては、ライブラリの使用方法と絡めて後でまた触れます。

arduinoからの出力端子は5Vなので(5V版の場合)、この出力がそのまま直接制御用の信号に使えるのですが、 実際は470Ω程度の電流制限抵抗を直列に入れて接続すると良いかと思います。まぁ、普通のサーボモーターは制御線に 直接5Vを掛けても壊れないようにはなっているようです。

なお、サーボモーターの信号線から入力される制御信号はあくまで「制御情報」であって、電力をこの端子から取り込んでいる わけではありません。マイコンの出力端子からモーターを動かせるほどの電力が取り出せないのは言うまでも無いですが。 DCモーターやステッピングモーター(stepper motor)は各入力端子から(制御信号を含めた)電源を供給することになっているのですが、 その点ではサーボモーターはちょっと異質です。電力はVccとGNDによって供給され、制御信号はHIGHかLOWを 表す純粋な制御信号です。

なお余談ですが、arduino準互換ボードのreduino-nanoはCN3コネクタにフタバ製サーボモーターのコネクタが直接挿せる ような形状・配置になっており便利です。ただしこの場合はUSBの電源から電力を取り出しているので、USBの仕様上最大で500mAしか 電流が取り出せません。よってあまり大きなサーボモーターはつなげません。(そもそもreduino-nanoの場合はあまり沢山の電流を 取り出すとボード上のポリスイッチが働き電流が遮断されます) そういう場合は、CN3ではなく別途電源を設ける必要があります。

ライブラリその1 "Servo"

さぁ、ようやくライブラリについてのお話です。サーボモーター関係のライブラリはarduinoバージョン0011では2つのバージョンが 存在します。この"Servo"ライブラリはそのうちの一つです。

サンプルスケッチ

まずはサンプルスケッチを見てみましょう。公式サイトのサンプルプログラムと殆どそのままですが、公式サイトのサンプル では2つのサーボを繋げるようになっているのに対し1個だけ繋げるように修正したのと、アライメント位置の設定をコメントアウト して省いたりしています。

あと、例によって#include文の不等号は全角になっています。適宜半角に戻してお使いください。

#include <Servo.h>

Servo servo1;

int servoPin = 10;


void setup(){
  servo1.attach(servoPin);
  //servo1.setMaximumPuls(2000);

  Serial.begin(9600);
  Serial.print("Ready");
}


void loop(){
  static int v = 0;

  if ( Serial.available()) {
    char ch = Serial.read();

    switch(ch) {
      case '0'...'9':
        v = v * 10 + ch - '0';
        break;
      case 's':
        servo1.write(v);
        v = 0;
        break;
    }
  }

  Servo::refresh();

}

スケッチの意味

まず冒頭の Servo servo1; ですが、Servo型オブジェクトのインスタンスservo1を生成しています。インスタンスを たくさん生成すれば、たくさんのサーボモーターを制御することが可能です。

servo1.attach(servoPin); ではservo1のピンをservoPin(この場合はデジタル10番)に割り当てています。

メインループでは、シリアル入力から数値(0~180)を入力し("s"が区切り文字)、その数値を角度として、 servo1.write(v); でservo1インスタンスに角度を渡します。

今回はテストということでアライメントは省略したのですが(上記スケッチの通りコメントアウトしてある)、 必要に応じて servo1.setMinimumPulse(マイクロ秒); や servo1.setMaximumPuls(マイクロ秒); でそれぞれ0度、180度における パルスの長さをミリ秒単位で指定してください。デフォルト値はそれぞれ544ミリ秒、2400ミリ秒です。 (1~2ミリ秒といってもこのくらいの幅があるということなのでしょうね)

最後の Servo::refresh(); というのは、各サーボインスタンスをリフレッシュする(次のパルスのトリガとなる)ために 50ミリ秒以内に1回の 割合で呼び出す必要がある命令です。ライブラリの中身を大まかに眺めてみたところ、1回の リフレッシュ動作 を行ってから20ミリ秒の間は、この命令を使っても何もせずに終了し、20ミリ秒を超えた時だけ リフレッシュ動作を しています。つまり、上記でも説明した通り約20ミリ秒毎に1回の割合で角度に応じたパルスを 発生させると言うことを 行っていることが判ります。要は、こまめにこのrefresh()を呼び出しておけばいいわけです。

ちなみに、ライブラリ"Servo"ではこの refresh() という動作が必要になるのですが、もう一つの方の ライブラリ"Servotimer1" ではこのリフレッシュ動作は不要のようです。(詳しくはそちらの説明をご参照)

動作風景

実際に動かして見ます。reduino-nanoとフタバのサーボモーター(とピン互換のモノ)を繋ぎ、arduino-IDEの シリアルモニタから角度を入力してみた時のムービーです。クリックすると再生します。

最初の状態は90度("90s"をシリアルモニタから入力した状態)で、45度("45s")、135度("135s")、90度("90s")、 と次々指定した場合の動きをムービーに撮って見ました。アライメントの指定を省略したので角度が微妙にズレています。 必要に応じてアライメントを行ってください。

ご覧の通り、右が0度、上が90度、左が180度という具合に「角度が増加→反時計回り(CCW)」となっていますが、 これ以外のサーボモーターを持っていないので、すべてのサーボモーターの回転方向がこの向きなのかは正直判りません。 すみません。

ライブラリその2 "Servotimer1"

さて、もう一つのライブラリを見てみましょう。"Servotimer1"です。このライブラリは、"Servo"と大体似たような 使い方になるのですが、2点ほど相違があります。

サンプルスケッチ

早速サンプルスケッチを見てみましょう。公式サイトのサンプルプログラムを少しだけ手直ししたものです。

(例によって#include文の不等号は全角になっています。適宜半角に戻してお使いください)

#include <ServoTimer1.h>

ServoTimer1 servo1;

void setup()
{
  pinMode(1,OUTPUT);
  servo1.attach(10);
  servo1.setMinimumPulse(750);
    // not "setMinimumPulseWidth"
  servo1.setMaximumPulse(2300);
    // not "setMaximumPulseWidth"
  Serial.begin(9600);
  Serial.println("Ready");
}

void loop()
{
  static int v = 0;

  if ( Serial.available()) {
    char ch = Serial.read();

    switch(ch) {
      case '0'...'9':
        v = v * 10 + ch - '0';
        break;
      case 's':
        servo1.write(v);
        v = 0;
        break;
    }
  }
}

まずコメント部分について補足

コメント部分になにやら書いてありますが、これは、公式サイトのライブラリの使い方について文中で説明されている メソッド名に間違えがあったので(バージョン0011公開時点)、その点に関してコメントを記載しました。

キャリブレーションを行うためのメソッドですが、後ろにwidthは不要です。setMinimumPuls()、setMaximumPulse() が正解です。いずれ、解説文かライブラリ内部かどちらかが訂正されるのではないでしょうか?

スケッチの解説

基本的な処理内容は、"Servo"と一緒です。見比べてみてください。異なるのは、およそ以下の2点に関することです。

timer1を使って制御する

ライブラリ"ServoTimer1"は、名前の通りタイマー1を使って制御されています。なので、出力に使えるピンは デジタル9かデジタル10のどちらかしか選択できません。よって、このライブラリで制御可能なサーボモーターの 数は2個以内です。

refreshが不要になっている

タイマー1のPWMを使って制御されるので、refreshメソッドを用いなくても自動的に次々パルスが生成されます。 ライブラリ内部ではタイマー1のTOP値にはICR1レジスタを指定し、ICR1には20000マイクロ秒(20ミリ秒) を指定していて、正確に20ミリ秒毎に自動的にパルスが出力されることになっています。重要なのは refreshメソッドの代わりにタイマー1のハードウェアが常時自動的にパルスを作りつづけてくれるという点です。

これら2点の違いがあり、メリット/デメリットを考慮してどちらのライブラリを使用するか判断すれば よろしいかと思います。一度に3個以上のサーボモーターを使うとか、タイマー1は別の機能に使用したいという 場合には選択肢は"Servo"しかないでしょうし、そうでなければ"ServoTimer1"の方が便利でしょう。

これ以外の部分については"Servo"用のスケッチと大きく変わらないので、詳説は省きます。

まとめ

"Servo"、"ServoTimer1"、どちらのライブラリもサーボモーターの角度を「パルスの時間」ではなく直接「角度」で 指示することができ、簡単に制御出来るということがお判りいただけたかと思います。

また、サーボモーターの配線に必要な入出力ピンには、モーター1個につき1端子しか使わないということも お判りいただけたかと思います。

なお、ブログの記事ではライブラリを 使わずにサーボモーターを接続する(パルスの長さで直接制御する)スケッチについても記載してあるので、併せてご覧ください。