公開日:2018/12/20
title.png

3rd Season


#3-4 サーボモータ

 個人的には、あまり興味が無いのですが、Iduinoスタータキットにモータがいくつか入っていたので、その中から、サーボモータを回してみたいと思います。

サーボモータの仕様確認

 Iduinoスタータキットに入っていたのは、マイクロサーボSG90と言うものでした。メーカ名がどこにも入っていないのだけれど、互換品??

ar34_servo.jpg

 実物を見る前に、写真で見ていたイメージと比べると、「小っさい」です(横幅2.2cmですもの)。まずは、データシートを探してみましょう。

  SG90+データシートを検索する

 と言っても、大したものは見つかりませんでした。精々、2ページくらいのもの。まあ、回すだけなら十分かな?読み取れたのは以下(だけ)。

ar34_srvo_pwm.png

 改めて考えてみると、本当にこれ(↑)だけの情報だったら、回せたかな……?上で検索した中に、マイコンを使って回した例とかも一緒に出てきましたよね?いくつか読ませてもらって、補間した方が良いと思われます。

 筆者が見つけたデータシートでは、モータを回転する時に流れる電流値についての記述が見つかりませんでした。こんな小さなモータですし、大した電流を食う訳ではないのでしょうが、どうなのでしょう?まあ一応、AC電源モジュールを使っておきますか……。

ar26_acpow.jpg

 Webで見つかったいくつかの記事では、ArduinoのUSB電源で回している例もありましたので、1個くらいなら、大丈夫かも?です。保証はできかねますが……。

Servoクラス

 要は、ArduinoのI/Oポートを使って、PWM波形を作ってあげれば良いわけですね。
 Arduino HP ⇒ REFERENCE ⇒ LIBRARIES ⇒ Standard Librariesの中に、Servoクラスが用意されています。これで回せるのでしょうか?Servoクラスの説明中にもいくつかサンプルコードが載っています(KnobとSweep)。どちらも、ArduinoのUSBから電源を取っているみたいですね。
 #includeは、<Servo.h>。このクラスは、SerialクラスやWireクラスなどと異なり、まずは、インスタンスを生成する必要があるみたいです。今回は、デジタル端子D4にサーボモータのPWM端子を繋ごうと思いますので、以下の様なインスタンス名(srvo_pin4)としました。

  #include <Servo.h>
  const int pin_SRVO=4;
  Servo srvo_pin4;

  void setup (void)
  {
    srvo_pin4.attach (pin_SRVO);
  }


 これで準備完了。後は、write()メソッドで、サーボモータの回転角度を与えたら終了。なんですが、write()メソッドに渡す値は、0〜180だと記載がありました。SG90のデータシートには−90〜90と記載があったのですが、大丈夫なの?
 いや、そもそも、このServoクラス、生成するPWM波形の周波数の説明も設定も無い。……もしかして、20ms (50Hz)ってのが、サーボ界のデファクトなのかな?
 ちなみに、attach()する時に、PWMの最小パルス幅と最大パルス幅が指定可能で、上の様に省略した場合は、最小幅が544μs、最大幅が2400μsと言うことで、SG90のデータシートに謳われたパルス幅とほぼ一致しています。

※SG90のデータシートに合わせて、以下の様にすべきなのかも?
    srvo_pin4.attach (pin_SRVO, 500,2400);


 定かではないので、念のため、SG90を繋ぐ前にLogiscopeでPWM波形を確認しておきます。

ar34_srvo_pwm2.png

 なるほど。やっぱり、周波数が50Hz(20ms間隔)なのは、デファクトなのでしょうか?Servoクラスが出力するPWMの周期も、SG90と同じ20msでした。また、write()の引数はintなので、0〜180までの整数でしか指定できません。もっと細かい制御が必要な場合は、writeMicroseconds()メソッドを使います。こちらはパルス幅を直接時間(μs)で指定することができます。

 完成版のテストプログラム(test_servo.ino)ではコメントにしておきましたが、D2端子にスイッチを接続して割り込みをかけ、その割り込み処理中でわざと時間待ちして、他の割り込み処理をディスターブしてみる実験をしました。

  void int_switch (void)
  {
    // interrupt service routine for SW falling edge

    delayMicroseconds (16383);  // わざと時間待ち
    delayMicroseconds (16383);  // この間、他の割り込みは禁止されている
  }
  void setup (void)
  {
      :
    // interrupt …… この2行がコメントになっています。
    attachInterrupt (digitalPinToInterrupt (pin_SW), int_switch, FALLING);
      :
  }


 ISR内での時間待ちでは、PWMの周期20ms以上の時間待ちをしたかったのですが、何しろISR内の処理なので、delay()が使えません。ISR内でも使用可能なdelayMicroseconds()を使ってみたものの、最大値が16,383μs≒16msと言うことで、20msに少し足りず。仕方なく、2回に分けて時間待ちしています。

 波形がこちら(↓)。

ar34_srvo_pwm3.png

 ServoクラスのPWMも、タイマ割り込みを使ったソフトウェアで波形を作り出しているのでしょう。他の割り込みで手間取って、Servoクラスのタイマ割り込みをディスターブした結果、PWM波形が崩れてしまいました。実物のサーボモータを繋いでいたら、どうなるのでしょうか?20ms間隔のPWMで1回や2回デューティが崩れても、動作に影響しないかもしれないし、何か、予期せぬ動作(変な振動が起きたりとか)をするかもしれませんね。
 複数のサーボを精密に制御したいとか、サーボ以外の処理が複雑になって、他の割り込みをガンガン使うような用途では、Arduinoにサーボモータの面倒を見させるのは厳しいかもしれません。

 「割り込みなんて使わないよ」な〜んて、思っているかもしれませんが、Serialクラスも、Wireクラスも、みんな割り込み使ってますからね。知らず知らずのうちに使っているのですよ。要注意です。

サーボモータを回す

 では、実際にサーボモータSG90をArduinoに接続して、回転させてみましょう。以下のように接続します。

Arduino 接続 SG90
5V ●Vcc
GND ●GND
D4 ●PWM

 こんな小さなサーボモータ1個だし、今回は負荷もかけないので大丈夫でしょう、と、言うことで、USBから給電してみます(前節のAC電源モジュールは不使用)。別電源で実験する場合は、別電源−Arduino−SG90のGNDをすべて同電位に接続して、SG90のVcc端子だけ、別電源に接続します(Arduinoには接続しない)。

※本ページ下の方の「後日のアップデート」をご覧ください。使い方次第では、結構な電流を食います。別電源から供給した方が無難です。

 完成したプログラムをリンクしておきます。

  完成版プログラム:test_servo.ino

 プログラムをコンパイルして実行すると、まず、いきなりサーボモータが動きますので、注意してください。Servoクラスが初期状態で、角度90°(左右中央位置)になるようなPWM波形を出力するためです。前の実験が90°位置で終了していた場合は、動きません。
 プログラムは以下のコマンドを受け付けるようになっています。

 回してみましょう。

  実験サンプル:【 】内はシリアルモニタからの送信データ
  【angl,0】
  OK: set pwm 0[deg]

  【angl,180】
  OK: set pwm 180[deg]

  【angl,90】
  OK: set pwm 90[deg]

 回りました。SG90のデータシートには、−90〜90[°]と表記されていましたが、Servoクラスでは、0〜180[°]が同じ回転角度に相当するようです。解り易い様、回転軸に、付属のアームをつけて実験したのですが、0°と180°でピッタリ正反対にはなっていないような……。筆者の所には分度器が無かったので正確な角度はわかりません。改めて中央の90°に動かしてみても、直角には程遠い感じです。見た目の感じでは、【angl,80】辺りで直角位置に止まりました。

 回転のスピードについては、データシートに記載がありましたね、0.1s/60°。0→180[°]まで回転するのに要する時間は、180°×0.1s÷60°=0.3sがスペック。動画を撮影して、回転速度を計測してみたところ、0→180[°]まで0.53sでした。データシートより遅いような……。

 遅い方はどうでしょうか?【rot】コマンドを使って、0〜180[°]まで300s(=5分)かけて回転させてみます。

  実験サンプル:【 】内はシリアルモニタからの送信データ
  【rot,0,180,300000】
  OK+ set rot 500~2400[us], 300000[ms]
    …… 5分後
  OK: stop rot



 動画(タイムラプス)では、問題ない様に見えます。ですが、逆に、実速度で見ていると、滑らか〜に回っているわけではなく、何と言うのでしょう?間欠的に、動いているようです。何か、最小の回転角度が存在するようです。それに、回転自体もリニアではなくて、「ジー、ジジ、ジッ、ジッ、ジジジジ、……」みたいな不規則な感じ(ごめんなさいね、擬音でわかり難くて……(1^^;))。
 回転角度の制御が可能で、精密な感じのするサーボモータですが、1°単位の回転角度を精密に再現するには程遠い様子です。ただ、わざわざ、ServoクラスにはwriteMicroseconds()と言う、精密なPWM波形生成用メソッドが用意されているので、サーボモータの種類によっては、もっと精密な制御が可能なのでしょう。Iduinoスタータキットに入っていたサーボモータ(見るからに「おもちゃ」だしね、冒頭写真)は、「この程度」でした。

サーボモータドライバ

 サーボモータドライバと称する製品が販売されています。こちらは、I2C接続で、複数(12)チャンネルのPWM出力を拡張するためのモジュールです。外から電源を取ることも可能な構成になっているので、たくさんのサーボモータを回す場合には、便利でしょう。
 前節で実験したように、マイクロサーボ1個くらいなら、Arduinoのソフトウェア制御で何とでもなりますが、処理が重くなって、割り込みがディスターブされると、動作が不安定になることもありますので、本格的にサーボモータを使うなら、専用のサーボドライバが合った方が良いと思われます。I2Cでコマンドを送ってしまいさえすれば、安定して(ハードウェアが)PWM波形を出力し続けてくれますので。

 最後に、もし、Arduinoのプログラムが暴走して、PWM端子がLOWまたはHIGHに固定してしまったときに、サーボモータがどういう挙動を示すのか?と思い、やってみたところ、何も起きませんでした。決まったデューティでトグルするPWM波形にしか反応しないみたいです。


 モータって、回すのは良いのですが、回して何に応用しようか?と考えると、なかなか難しいのですよね。ラジオコントロールカーを自分で組み立てたり、プラモデルが趣味だったりすると、色々な工作ができるのかもしれませんが、そっち系には疎いのです。そうすると、そもそも、モータの軸からどうやって動力を取り出すか?と考えただけでも、ちょっと手に負えません。「回せる」ことは分かりましたが、はてさて、何に応用したものか……。


後日のアップデート

 「#7-4 USB電圧・電流計で電流を測定する」にて、USB電圧・電流計を使って、サーボモータの消費電流を測定しました。小さなサーボモータの割には、思いの他、電流が流れることが判りましたので、ご参照ください。

 その際、このページで作成したプログラム(test_servo.ino)に機能を追加しました。本ページ中から参照しているプログラムも、更新版に挿し変わっているので、ご注意ください。


btn_arduino.png

btn_prev.png btn_next.png

home