Skip to content

サーボモーター(SG90)の使い方

SG90は、指定した角度にシャフトを回転させ、その位置を保持できる、小型で安価なサーボモーターです。ロボットアームの関節やラジコンのステアリングなど、正確な位置決めが必要な用途で活躍します。

制御方式:サーボ用PWM

サーボモーターはPWM信号で制御されますが、これはChapter2でLEDの明るさを変えたPWMとは少し意味合いが異なります。 サーボを制御するPWMは、信号の周期(周波数)と、パルスの幅(HIGHになっている時間)の両方が重要になります。

  • 周期: 一般的なサーボモーターは 20ms(ミリ秒)、つまり 50Hz の周期で信号を受け取ります。
  • パルス幅: 20msの周期の中で、パルスがHIGHになっている時間の長さ(パルス幅)によって、サーボの角度が決まります。
    • パルス幅 1.5ms: 中間点(約90度)
    • パルス幅 0.5ms: 片方の端(約0度)
    • パルス幅 2.4ms: もう片方の端(約180度)

この特殊なPWM信号をマイコンで正確に生成するのは少し手間がかかりますが、Arduinoフレームワークには便利な Servo ライブラリが用意されています。

横道:サーボモーターの内部構造 サーボモーターの筐体の中には、実は以下の4つの部品が入っています。 1. **DCモーター**: 普通のモーターです。これが動力源となります。 2. **ギアボックス**: モーターの高速な回転を、低速で力強い(高トルクな)回転に変換します。 3. **ポテンショメータ(可変抵抗器)**: 出力軸の現在の角度を検出するためのセンサーです。軸の回転に応じて抵抗値が変化します。 4. **制御回路**: 外部からのPWM信号と、ポテンショメータからの現在の角度情報を比較し、その差がなくなるようにDCモーターを回したり止めたりする、フィードバック制御を行っています。 つまり、サーボモーターはそれ自体が一個の閉ループ制御システムなのです。

回路図と電源に関する最重要事項

サーボモーターは、動き始めに大きな電流(突入電流)を消費します。マイコンの3.3Vピンから電源を取ると、電圧が不安定になり、マイコンがリセットされたり、動作がおかしくなったりする原因の筆頭です。

安定した動作のためには、サーボモーターの電源は、マイコンとは別の外部電源から供給するのが鉄則です。

今回は学習のため、Raspberry Pi Picoの VBUS ピン(USBからの5V電源)から電源を取ります。これでも動作しますが、複数のサーボを同時に動かしたり、大きな負荷をかけたりする場合は、必ず外部電源を使用してください。

(ここに circuits.drawio で作成したSG90の回路図を挿入)

SG90ケーブル 接続先 役割
茶色 GND グラウンド
赤色 VBUS (5V) 電源
オレンジ色 GP15 (任意のGPIOピン) 信号線

重要: 赤い電源線は 3.3V ではなく VBUS に接続します。また、PicoのGNDとサーボのGND(茶色線)を必ず共通に接続してください。GNDが接続されていないと、信号の基準電圧が定まらず、サーボは絶対に正しく動作しません。

横道:なぜGNDの共通化が必要なのか? 「GNDを共通に」は電子工作の合言葉ですが、なぜでしょうか。それは、**電圧とは2点間の電位「差」**だからです。 マイコンが信号線(オレンジ)に3.3Vを出力するとき、それは「マイコン自身のGNDを基準(0V)として、そこから+3.3Vの電位を持つ信号」を出している、という意味になります。 サーボモーターの制御回路は、この信号を受け取って「自分のGNDを基準として、信号線は何Vだろう?」と測定します。もし、マイコンとサーボのGNDが繋がっていないと、サーボは信号の基準点がどこか分からなくなってしまいます。人間が海抜ゼロメートルという基準なしに山の高さを語れないのと同じです。 GNDを共通化することで、初めてマイコンとサーボが「GND」という共通の基準(海抜ゼロメートル)を持つことができ、マイコンが出力した3.3Vの信号を、サーボも正しく3.3Vの信号として解釈できるようになるのです。これは、異なる電源を使う全ての電子回路に共通する、最も重要な原則の一つです。
横道:電源の安定化(パスコンのススメ) モーターのように急に大きな電流を消費する部品は、電源ラインにノイズを発生させ、マイコンの動作を不安定にさせることがあります。これを防ぐためのおまじないとして、**サーボの電源(赤線)とGND(茶線)の間に、100μF〜470μF程度の電解コンデンサを接続する**(これを**パスコンを入れる**と言います)と、動作が劇的に安定することがあります。 コンデンサは電気を一時的に蓄えるダムのような役割を果たし、サーボが急に大電流を必要としたときに、それを供給して電源全体の電圧降下を防いでくれます。これはノイズ対策の基本テクニックです。

ライブラリとプログラム

platformio.iniarduino-libraries/Servo を追加してください。

lib_deps =
    arduino-libraries/Servo

基本的な動作

まずは、サーボモーターを0度と90度で交互に動かす簡単なプログラムです。

#include <Arduino.h>
#include <Servo.h>

#define SERVO_PIN 20
Servo myservo;


void setup(){
    myservo.attach(SERVO_PIN);
}

void loop(){
    myservo.write(90); // Set servo to 90 degrees
    delay(1000);
    myservo.write(0); // Set servo to 0 degrees
    delay(1000);
}

コードの解説

  • myservo.attach(pin): サーボオブジェクトをピンに割り当てます。
  • myservo.write(angle): 0度から180度の角度を指示します。ライブラリの内部で、指定された角度に対応するパルス幅のPWM信号を自動的に生成・出力してくれます。

アナログ入力で角度を制御する

次に、これまでの知識を組み合わせてみましょう。可変抵抗器のアナログ値を読み取り、その値に応じてサーボの角度をリアルタイムに制御するプログラムに改造します。

--- a/src/servo/main.ino
+++ b/src/servo/main.ino
@@ -1,19 +1,21 @@
 #include <Servo.h>

 Servo myservo;
 const int servoPin = 15;
+const int analogPin = 26; // アナログ入力ピン

 void setup() {
     myservo.attach(servoPin);
+    // アナログピンはpinMode不要
 }

 void loop() {
-    // 0度から180度まで動かす
-    for (int angle = 0; angle <= 180; angle++) {
-        myservo.write(angle);
-        delay(15);
-    }
-    // 180度から0度まで戻す
-    for (int angle = 180; angle >= 0; angle--) {
-        myservo.write(angle);
-        delay(15);
-    }
+    // アナログ値を読み取る (0-4095)
+    int sensorValue = analogRead(analogPin);
+
+    // センサーの値をサーボの角度(0-180)に変換する
+    // map(value, fromLow, fromHigh, toLow, toHigh)
+    int angle = map(sensorValue, 0, 4095, 0, 180);
+
+    // サーボに角度を指示
+    myservo.write(angle);
+    delay(15);
 }

forループで角度を機械的に変えていた部分を、analogReadでセンサーの値を取得し、map関数でその値をサーボの可動範囲である0〜180度に変換してmyservo.writeに渡すように変更しました。 map関数は、ある範囲の数値を別の範囲の数値に線形に変換してくれる便利な関数です。これにより、センサーの値に応じてサーボが直感的に動くシステムが完成します。

横道:Servoライブラリの裏側 `Servo`ライブラリは、`analogWrite`とは異なる方法でPWM信号を生成しています。`analogWrite`はデューティ比を変えるだけで、周波数は固定です。一方、サーボ制御には特定の周波数(50Hz)と精密なパルス幅(0.5ms〜2.4ms)が必要です。 `Servo`ライブラリは、マイコンの**ハードウェアタイマー**と**割り込み**を駆使して、これらの精密な信号をバックグラウンドで生成し続けています。これにより、`loop()`内で`delay()`を使っても、サーボの角度はピタッと保持され続けます。これはChapter2で学んだノンブロッキング処理や割り込みの、非常に高度で実用的な応用例なのです。