workshop/ServoBasis/main

はじめに

サーボモータは電源,グラウンド,制御ピンの3つのピンだけで制御ができ,お手軽に操作できる反面,基礎を しっかり固めておかないとその制御で困る人が多いようです.サーボは動作時のノイズが多いので私もよく困ります. ウェブ上のノウハウだと動かしてハイおしまい的なものが多かったので,ここではちゃんと振る舞いを理解することで サーボの気持ちになることを目指します.

PWMの制御幅で角度コントロール

サーボモータは角度を指定して,シャフトを自由に動かすことができます.サーボ内部はモータとギアによって, 結構な力(トルク)を持っています.この角度指定にはHIGH, LOWを繰り返すパルス幅変動を利用しています. 下記の回路を「Start Simulation」ボタンを押して実行してください.サーボシャフトの角度を0,90,180度と 5秒おきに動かしています.その際のパルス幅が変化しているのが見えるかと思います.0度はパルス幅が狭く,180度は 最もパルス幅広くなっているかと思います.こんな感じで制御します.実はこのパルス幅と角度の関係は,利用する サーボモータの種類によって微妙に異なります.それについては次で説明,紹介します.まずは下記回路を実行し, ソースコードを確認して下さい.

ArduinoのServoクラス

このパルス幅を制御するには,ArduinoのPWM制御を利用すると楽ちんになります.さらにArduinoにはサーボモータを制御する 為のライブラリも用意されていますので,それでさらに楽ちんになります.上記回路はServoライブラリを利用して制御しています. では実際に自分でもテストしてみましょう.まずは下記を準備します.うちの研究室で大量ストックしているのは,GWS社製2BBMGという 小型のサーボモータになります.これ以外にも9gサーボと呼ばれるような小型サーボは手に入りやすく安価なのでおすすめです.

  1. Arduino
  2. サーボモータ(GWS 2BBMG)
  3. ジャンパー線(3本)

サーボコネクタにジャンパー線を指し,それぞれをArduino上の赤(5V),黒(Gnd),白(Digital 9)に接続してください. 回路の接続は以上で終わりです.では次にプログラムを記述します.プログラムを記述する際にサーボモータのパルス幅に関して 理解しておく必要があります.先に述べた通り,パルス幅と角度の関係はサーボモータの種類によって異なります.今回利用している のは台湾のGWSというメーカのサーボモータになり, http://www.gws.com.tw/english/product/servo/servo.htm のSERVO INPUT SIGNAL FORMATIONから詳細を確認できます.とりあえず,800us〜2300usで0〜180度の角度設定ができるということです.


#include <Servo.h>
Servo sv;
void setup()
{
  // 9番ピンをServoの制御ピンにする.パルス幅は800〜2300msとする
  sv.attach(9, 800, 2300);
}

void loop()
{
  sv.write(0);  // 0度に設定
  delay(5000);  // 5秒待つ
  sv.write(90); // 90度に設定
  delay(5000);  // 5秒待つ
  sv.write(180);// 180度に設定
  delay(5000);  // 5秒待つ
}

Servoクラスのメソッドを理解する

Servoはクラスなので,メンバ変数やメソッドが登録されています.具体的にServo.hファイルの最初に下記のような記述があります.

  A servo is activated by creating an instance of the Servo class passing the desired pin to the attach() method.
  The servos are pulsed in the background using the value most recently written using the write() method

  Note that analogWrite of PWM on pins associated with the timer are disabled when the first servo is attached.
  Timers are seized as needed in groups of 12 servos - 24 servos use two timers, 48 servos will use four.
  The sequence used to sieze timers is defined in timers.h

  The methods are:

   Servo - Class for manipulating servo motors connected to Arduino pins.

   attach(pin )  - Attaches a servo motor to an i/o pin.
   attach(pin, min, max  ) - Attaches to a pin setting min and max values in microseconds
   default min is 544, max is 2400  
 
   write()     - Sets the servo angle in degrees.  (invalid angle that is valid as pulse in microseconds is treated as microseconds)
   writeMicroseconds() - Sets the servo pulse width in microseconds 
   read()      - Gets the last written servo pulse width as an angle between 0 and 180. 
   readMicroseconds()   - Gets the last written servo pulse width in microseconds. (was read_us() in first release)
   attached()  - Returns true if there is a servo attached. 
   detach()    - Stops an attached servos from pulsing its i/o pin. 

ここから解るように,attachの際,デフォルトではパルス幅は最小544,最大2400にて設定されています. また,このパルス幅を直接指定するwriteMicroseconds関数や設定されたパルス幅や角度を知ることができるread(), readMicroseconds()関数が あるのがわかります.この他サーボピンがサーボに接続されているかどうかを確認するattached()や,サーボを切り離す(PWM出力をとめる)detach() 関数があります.

では最後に下記プログラムをArduinoに書き込み,シリアルモニタから'a', 'd', 'r', '0'-'9'のコマンドを打ち, 段階的にサーボモータ動作を確認してください.

ServoChecker.ino

#include <Servo.h>

// サーボモータ動作確認用プログラム
/*
シリアルモニタからサーボを操作可能
r: サーボ値読み取り
0-9: 0度〜90度設定
a: attach(9)
d: detach()
*/
Servo sv;

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  int ret;
  if( Serial.available() > 0 ){
    int d = Serial.read();
    if( d == 'a' ){
      sv.attach(9, 800, 2200);
    }
    else if( d == 'd' ){
      sv.detach();
    }
    else if( d == 'r' ){
      Serial.println(sv.read());
    }
    else if( '0' <= d && d <= '9' ){
      int angle = (d - '0')*10;
      sv.write(angle);
    }
  }
}

どちらがいいのか?:角度設定/パルス幅設定

ここまででサーボモータ制御にある程度慣れてきたことかと思います.実際にシャフトを制御するには write()関数とwriteMicroseconds()の2つを利用することができます.前者は角度指定,後者はパルス幅指定 になりますが,どのように使い分ければよいでしょうか?正解は,なるべくwriteMicrosecondsを利用しましょう,です. 角度指定は直感的で使いやすいのですが,例えば0〜90度に移動させる際,角度指定では91段階になりますが,パルス幅指定 では,(2300-1500)/2 = 400程度の段階でシャフト位置を制御できます.つまり, スムーズに動かしたければ,writeMicroseconds()関数を利用する事を覚えておいてください.さらに パルス幅制御の方が,角度指定よりも無駄なトルクが掛からず(パルス幅飛ばしが無いため),安定して動作します.実際に下記 プログラムを実行してください.どちらも同じ速度でシャフトが動きますが,片方が動作音がうるさいのに気づくと思います. どちらがうるさいほうかはもうわかりますね.


#include <Servo.h>
Servo sv;
void setup()
{
  sv.attach(9, 800, 2300);
}

void loop()
{
  for( int i = 0; i < 180; i++ ){
    sv.write(i);
    delayMicroseconds(4166);
  }
  delay(1000);
 for( int i = 2300; i >= 800; i-- ){
    sv.writeMicroseconds(i);
    delay(1);
  }
  delay(3000);
}

シャフト動作速度制御関数

では最後に,サーボシャフトの動作をスムーズに制御可能なクラスを作成します. シャフトは角度やパルス幅指定を行えば任意の角度に回転することが出来ましたが,そのままでは動作速度までを 制御できません.そこで先のサンプルのように待ち時間を入れながら少しずつシャフト角度をずらしていくことで 移動速度もコントロールできます.例えば制御角度とdelay時間を指定すると自動でその速度でシャフトを回転する ような関数があるととても使いやすそうですよね.実際に作ってみましょう.


#include <Servo.h>

Servo sv;

void moveShaft(int angle, long int delay_ms)
{
  int pulse = map(angle, 0, 180, 800, 2300);
  int pulse_now = sv.readMicroseconds();

  if( pulse > pulse_now ){
    for( int i = pulse_now; i <= pulse; i++ ){
      sv.writeMicroseconds(i);
      delay(delay_ms);
    }
  }
  else if( pulse < pulse_now ){
    for( int i = pulse_now; i >= pulse; i-- ){
      sv.writeMicroseconds(i);
      delay(delay_ms);
    }
  }
  else{
    // do nothing
  }
}

void setup()
{
  sv.attach(9, 800, 2300);
}

void loop()
{
 moveShaft(0, 1);
 delay(1000);
 moveShaft(180, 5);
 delay(1000);
}


attachとdetachを動的に制御する

attach()関数を呼び出すと,サーボが一度にデフォルトの位置(パルス幅1500us)に移動されます. 以後指定されたシャフト角度を維持するため,PWMは常時出力され,シャフトが動く必要のないときも ブレーキの機能としてPWM制御が行われています.これはシャフト位置を固定する意味で状況によっては 重要だったりするのですが,余りトルクを必要としないものにおいては常時PWM制御をしておく必要は 余りありません.実際にPWMを送りつづけることで,時折サーボから動かしてもいないのに「カリ」という 音が聞こえることがあると思います.このようなノイズを除去したい場合は,一旦サーボピンをdetachする ことをお薦めします.これによりサーボが静止しているときにノイズが減るだけでなく,消費電力も ぐっと抑えることができます.

以上,サーボの基礎を抑えてみました.少し長かったですが,ここまでの内容を理解できていれば サーボ制御で困ることはほぼ無いかと思います.


Copyright (c) 2015 Tetsuaki BABA all rights reserved.