アフィリエイト広告
アフィリエイト広告

サーボモータを動かしてみる

すでに「Arduino のマルチタスク」でサーボモータを動かしているのですが、ちょっと基本的なことを試してみたいと思います。

どうすれば動く?

えーと、ググってください m(_ _;)m

まぁ簡単に言うと、スケッチ例「servo」の中の「knob」とか「sweep」とかです。ググると解説されているサイトがたくさん出てきます。

でも、いまひとつ肝心なことがわからない meyon さんでありました。

実際に動かしてみよう

わからないなら、実際に動かして試してみましょう。

サーボモータ実験回路

回路図です。

スケッチ例の「knob」も「sweep」も、これで動きます。
knob では、ボリューム VR1 を回すと、それに応じてサーボモータの角度が制御されます。
sweep は、0~180 度を行ったり来たりします。

ボリュームまわりですが、電源側は Arduino の 5V 出力を利用し、GND 側も Arduino の GND につないでいます。
これは、制御用電源をサーボモータの駆動用電源と分離することで、ノイズの影響を軽減するためです。

もちろん、サーボモータの 5V 電源は別の電源回路から供給します。1 台あたり 300~500mA は見込んでおきましょう。

ただし、この程度ではまだノイズの影響を受けます。実際に試してみるとわかると思いますが、制御用の電源も信号もふらつき、サーボモータがジリジリと小さく動いてしまいます。
対策についてはもっと調べてみるか、研究してみる必要がありますね。ロボットとか作っている人はどうしているんでしょう? 今後の課題。

パルスを 1 回だけ与えてみる

さて、どうすればサーボモータが動くのか、やってみましょう。

SG90 のデータシートによれば、周期 20ms で、デューティーサイクル 0.5~2.4ms のパルスを与えれば良いとなっています。

そこで、こんなスケッチを試してみました。

  1. void setup() {
  2.   pinMode(9, OUTPUT);
  3.   int pos = 2400;
  4.   digitalWrite(9, HIGH);
  5.   delayMicroseconds(pos);
  6.   digitalWrite(9, LOW);
  7. }
  8. void loop() {
  9. }

4 行目の値がデューティーサイクル。単位は μs ですので、2400 ならば 2.4ms です。その長さのパルスを 1 回だけ出力させます。

結果、サーボモータは 180 度の位置へ動きました。

次にデューティーサイクルを 1450μs に変更してみます。するとサーボモータは 90 度の位置へ戻ります。さらにデューティーサイクルを 500μs にすると、サーボモータは 0 度の位置となりました。

つまり、「サーボモータに 0.5~2.4ms 幅のパルスを 1 回与えてやると、そのパルス幅に応じた位置まで動く」ってこと。

俺が知りたかったことは、これです。
たったこれだけのことですが、ググってみた限りでは知り得なかった。

周期 20ms ってなに?

もっとも、パルスを 1 回与えるだけでは使い物にはなりません。

止まったサーボモータに荷重をかけると、サーボモータは回転してしまいます。ブレーキもありませんし、ウォームギヤを使っているわけでもありませんから当然ですね。
そこで、周期的にパルスを与えることで、その位置に止めておくことができるようになります。もし負荷によってサーボモータが回転したら、パルスによって元に戻され、あたかもその位置に止まっているようになるわけです。

では、パルス周期はなぜ 20ms なのでしょう?
こんなスケッチを試してみました。

  1. void setup() {
  2.   pinMode(9, OUTPUT);
  3. }
  4. void loop() {
  5.   static int pos = 500;
  6.   static int increment = 20;
  7.   digitalWrite(9, HIGH);
  8.   delayMicroseconds(pos);
  9.   digitalWrite(9, LOW);
  10.   delayMicroseconds(10000 - pos);
  11.   delay(10);
  12. // delay(20);
  13.   pos += increment;
  14.   if(500 >= pos || 2400 <= pos) increment = -increment;
  15. }

難しくはないと思いますが。
09〜13 行はデューティサイクル pos のパルスを 20ms 周期で発生させています。
16~17 行目は、180 度動いたら反転して元へ戻る処理。

さて、ここで 14 行のコメントアウトを外したらどうなるでしょう。周期が 40ms に延びますね。サーボモータの動きが半分の速さになりますが、特に問題なく動きます。速度が半分になったのは、1 周期ごとの増分が見かけ上半分になったためですから、increment を倍の 40 にすれば元の速さに戻ります。
では、13~14 行をコメントアウトして、increment を 10 にするとどうでしょうか。周期は 10ms になりますが、サーボモータは変わりなく動きます。

つまり、周期 20ms はかなりラフでいいってことのようです。俺にはわからんのですが、諸々の性能上の理由で、デューティー比はこれぐらいにするのがいいですよ、って値。たぶん、そーゆーこと。

ボリュームを追いかけるサーボモータ

サーボモータを動かす基本がわかりました。基本をわかった上で、でも実際に利用するときはライブラリ Servo.h を使うのが簡単で、便利です (^_^;)

ボリュームを動かすとその位置に応じてサーボモータが動くスケッチを作りましょう。基本はスケッチ例の knob ですが、knob は delay() を使っています。俺たちは Ditch the delay() でいきますよ。

  1. #include <Servo.h>
  2. Servo myservo;
  3. byte potentiometerPin = 0;
  4. byte servoPin = 9;
  5. void setup() {
  6.   myservo.attach(servoPin);
  7. }
  8. void loop() {
  9.   static int value = 0;
  10.   static byte interval = 15;
  11.   static unsigned long previousMillis = millis();
  12.   if(interval < millis() - previousMillis) {
  13.     value = analogRead(potentiometerPin);
  14.     value = map(value, 0, 1023, 0, 180);
  15.     myservo.write(value);
  16.     previousMillis = millis();
  17.   }
  18. }

更新時刻が過ぎたら (17行) 、ボリュームの位置を読み取り (18行) 、サーボモータへ出力 (20行) 、更新時刻を記憶 (21行) する。ステートマシンです (^_^;)

クラスの勉強したんだからね

自信ないけど、インスタンス化してみましょう。

  1. #include <Servo.h>
  2. class Follow {
  3.     Servo myservo;
  4.     byte potentiometerPin;
  5.     int value;
  6.     byte interval;
  7.     unsigned long previousMillis;
  8.   public:
  9.     Follow(byte pin) {
  10.       potentiometerPin = pin;
  11.       value = 0;
  12.       interval = 15;
  13.       previousMillis = millis();
  14.     }
  15.   void Attach(byte servoPin) {
  16.     myservo.attach(servoPin);
  17.   }
  18.   void Update() {
  19.     if(interval < millis() - previousMillis) {
  20.       value = analogRead(potentiometerPin);
  21.       value = map(value, 0, 1023, 0, 180);
  22.       myservo.write(value);
  23.       previousMillis = millis();
  24.     }
  25.   }
  26. };
  27. Follow follow1(0);
  28. void setup() {
  29.   follow1.Attach(9);
  30. }
  31. void loop() {
  32.   follow1.Update();
  33. }

どうだ。これでボリュームとサーボモータのセットをいくつでも増やせますぞ。
あ、そろそろサーボモータを追加注文しないといかんな (^_^;)

タイトルとURLをコピーしました