2016年1月18日月曜日

Arduino でサーボモータを制御してみた

概要

Arduino でサーボモータを制御してみました
Arduino にデフォルトで含まれている Servo ライブラリと独自で PWM を作成して制御する方法の 2 つを試してみました

環境

配線

Arduino UNO はデフォルトで PWM をサポートしているため配線は非常に簡単です
arduino_servo_circuit.jpg

サーボモータから出ている線の

  • 茶色 -> Arduino GND
  • 赤色 -> Arduino 5v電源
  • オレンジ色 -> Arduino GPIO 8 番

に接続します
GPIO は他の好きなところで OK ですが、今回は 8 番を使って進めていきます

制御用スクリプト

2 つ紹介します
どちらも流れとしては Serial.read で受け取った角度情報にサーボモータを回すという処理になります

独自の実装で PWM を送信

まずは自分で PWM 信号を作ってサーボモータを制御するスクリプトです

int deg = 610; // msec.
int pin = 8;

void setup() {
  pinMode(pin, OUTPUT);
  Serial.begin(9600);
}

void move_servo(int deg) {
  digitalWrite(pin, HIGH);
  delayMicroseconds(deg);
  digitalWrite(pin, LOW);
  delay(20);
}

int serialRead() {
  char c[4];
  for (int i = 0; i < 3; i++) {
    c[i] = Serial.read();
    if (c[i] == '\0') {
      break;
    }
  }
  return atoi(c);
}

void loop() {
  move_servo(deg);
  if (Serial.available() > 0) {
    delay(10);
    int input = serialRead();
    if ( input >= 0 && input <= 180 ) {
      deg = 610 + input / 180.0 * ( 2350 - 610 );
    }
  }
}

SG90 のサーボモータは送信するパルス幅の長さが約 610ms から 2350ms で 0 度 から 180 度の動きをするようです
そのパルスを送信する周期を 20ms にしています

送信の仕方は

digitalWrite(pin, HIGH);
delayMicroseconds(deg);
digitalWrite(pin, LOW);

で実現しており、HIGH の状態を指定ミリ秒続けることで幅の違うパルスを送信しています

あとは Serial.read で受け取った度数情報を delayMicroseconds に渡せるように変換してあげます

deg = 610 + input / 180.0 * ( 2350 - 610 );

loop メソッドはその名の通りずっとループしています
なので、move_servo は 20ms の wait をおいて常にコールされているため入力を変えない限り 20ms の間隔で同じパルス幅の信号が送信されていることになります

このプログラムで実際に動作させてみると入力した角度に動くのは動くのですが、サーボモータが動いていないときに「ジリジリ」という音と微妙な振動が発生してしまいました
おそらくですが、これは正確に PWM を送信されていないのが原因で、毎回 20ms 待つようにしていますが、Serial の読み込み等の他の処理があり周期が毎回正確になっておらず、サーボモータが不安定な状態になっているのだと思います

次に Servo ライブラリを使った制御を紹介しますが、結論から先に言うとライブラリを使った制御のほうが圧倒的に安定します
とりあえず今回は PWM の仕組みも知りたかったの、独自の制御方式も試してみた感じです

Servo ライブラリを使って PWM を送信

では、次に Arduino がデフォルトで提供している Servo ライブラリを使って制御してみたいと思います

#include <Servo.h>
Servo servo;

int pin = 8;
int deg = 0;

void setup() {
  servo.attach(pin);
  Serial.begin(9600);
}

void move_servo(int deg) {
  servo.write(deg);
  delay(200);
  servo.detach();
}

int serialRead() {
  char c[4];
  for (int i = 0; i < 3; i++) {
    c[i] = Serial.read();
    if (c[i] == '\0') {
      break;
    }
  }
  return atoi(c);
}

void loop() {
  move_servo(deg);
  if (Serial.available() > 0) {
    delay(10);
    int input = serialRead();
    if ( input >= 0 && input <= 180 ) {
      servo.attach(pin);
      deg = input;
    }
  }
}

ソースもかなり簡潔になりました
Servo ライブラリを使うと servo.write で角度の情報をそのまま渡せます
なので、パルス幅を計算するロジックがなくなります

独自のパルスを送信しているときにジリジリ言っていた問題は

delay(200);
servo.detach();

のロジックでほぼ言わなくなりました
当たり前といえば当たり前ですが、サーボモータを回したくないときは detach することで無駄な信号の送信を抑制します
再度サーボモータを回したいときに attach しています

サーボモータを servo.write で回してからdetach するまでに 200ms の delay を入れています
これは、回した直後に detach してしまうと write の情報がサーボモータに届く前にサーボモータを detach してしまうので、サーボモータが上手く回ってくれないためです

実行してみる

どちらもコードを Arduino IDE に貼り付けて配線して Run すれば OK です
動画等はなくて恐縮ですが、実行して比較してみると Servo ライブラリを使ったほうが断然いい感じにサーボモータが動きます

最後に

Arduino でサーボモータを制御してみました
PWM に詳しく実際に PWM でモータやセンサー等を制御したことある場合は独自の PWM 実装でもいいかもしれませんが、PWM について良くわからないということであれば素直に Servo ライブラリを使うことをオススメします

サーボモータは RaspberryPi でも制御できるので機会があれば RPi でも制御してみたいと思います
Arduino と RaspberryPi どちらでもできるけど、アナログ信号や PWM を扱う場合は Arduino のほうが簡単に制御できる気がします
両方使うとボードが増えて大変ですが、、

参考サイト

0 件のコメント:

コメントを投稿