Python × Pico 電子工作 | #12 サーボモーターを動かそう

今回は SG90 サーボモーター を使って、「角度を数値で指定して位置を決める」 モーター制御の基本を学びます。サーボモーターは 「0° から 180° まで自由な角度に止まるモーター」 で、ロボットアームの関節、ラジコンの舵、自動開閉ドアのレバーなど、「決まった位置に正確に動かしたい場面」 で大活躍する部品です。ラジコンサーボとも呼ばれ、SG90 は世界で最もよく使われる入門用サーボの一つです。

  • SG90 サーボの 3 本線(VCC・GND・PWM 信号)の役割
  • PWM 50Hz(周期 20ms) と パルス幅 500〜2400μs で角度が決まる仕組み
  • 角度を 0°〜180° でリテラル指定する基本ブロック
  • 角度に 変数や式 を差し込める pico_servo_angle_val 新ブロック
  • #05 の可変抵抗や #09 の超音波センサーと組み合わせた センサー連動制御
  • Pico の 3.3V ピンでは電流不足。VBUS(5V) または外部電源 を使う注意

今回は PycoBlocks(MicroPythonモード) の 「モーター」カテゴリ にある「サーボ」 ブロックを使います。角度を 0〜180 の数字で指定するだけ でサーボが回転するシンプルなブロックで、内部では PWM(Pin(15), freq=50) を初期化し duty_u16 値をパルス幅に換算して送る処理が自動で行われます。

サーボモーターを動かそう

SG90 サーボの基礎:仕様と外観

SG90 は青いプラスチックボディに 白いプラスチックホーン(U字型・十字型・円盤型の付属品から選ぶ)が付いた、重さ約 9g・出力 1.8kg・cm の小型サーボです。3 本のリード線(茶=GND・赤=VCC・橙またはオレンジ=PWM 信号)が出ており、コネクタは 2.54mm ピッチの 3 ピンメス。ジャンパーワイヤー(オス→オス)でブレッドボードに繋ぐか、オス→メスのジャンパーで直接 Pico に繋ぎます。

項目SG90 仕様備考
動作電圧4.8〜6V(推奨 5V)Pico の VBUS が便利
動作角度約 180°(0°〜180°)個体差で 170〜200° 程度
トルク1.8kg・cm(@4.8V)小型ロボット・小物制御に十分
速度0.1秒/60°180° 動くのに約 0.3 秒
制御信号PWM 50Hz周期 20ms 固定
パルス幅500〜2400μs0°=500μs / 90°=1500μs / 180°=2400μs
消費電流動作時 100〜300mA、停動時 800mAPico の 3.3V では足りない
重さ9gドローン・小型ロボに最適
価格200〜500円互換品も多数

PWM 50Hz の仕組み:パルス幅で角度を決める

サーボの角度は 「PWM 信号のパルス幅(HIGH の長さ)」 で決まります。サーボに送る信号は 「周期 20ms(50Hz)の中で短いパルスを出す」 形で、パルスの幅(HIGH の時間)を変える ことでサーボ内部の電子回路が「今この角度にいなさい」 と判断します。

PWM パルス幅とサーボ角度の対応図(0.5ms→0度/1.5ms→90度/2.5ms→180度)
パルス幅とサーボ角度の関係。周期 20ms(50Hz)の信号の中で、HIGH の長さ(パルス幅)を 0.5ms → 0°、1.5ms → 90°(中央)、2.5ms → 180° と変えると、サーボのホーンがその角度に止まります。
# PWM 信号の波形イメージ(周期 20ms = 50Hz)
#
#   HIGH ──┐                        ┌─
#         │  幅 W μs               │
#   LOW  └────────────────────────┘
#         |←     20ms 周期       →|
#
# W = 500μs  → 角度 0°
# W = 1500μs → 角度 90°(中央)
# W = 2400μs → 角度 180°
#
# Pico では PWM(Pin(15), freq=50) で周期 20ms (=1/50秒) を作る
# duty_u16 値(0〜65535)= W ÷ 20000 × 65535
#   500μs  → duty_u16 ≈ 1638
#   1500μs → duty_u16 ≈ 4915
#   2400μs → duty_u16 ≈ 7864

PycoBlocks の 「サーボ」ブロック は、この計算を内部で自動的に行います。ユーザーは 「サーボ ピン GP15 を 90 度に回す」 と書くだけで、PWM(Pin(15), freq=50).duty_u16(1638 + 90 × 8192 // 180) という MicroPython コードが生成されます。数式の 1638 は 0° のパルス幅(500μs に相当する duty_u16)、8192 は 180° で増える分(2400 - 500 = 1900μs ≈ 6226 だが SG90 個体差に合わせ少し広め)です。

配線:3 本線をどう繋ぐか

SG90 の 3 本線をそれぞれ正しく繋ぎます。順番を間違えるとサーボが壊れることがあるので慎重に。

SG90 のリード色(一般的)Pico の繋ぎ先役割
VCC赤VBUS(5V) 推奨/3.3V は NGサーボの電源
GND茶 または 黒GND(どれか)共通グランド
PWM 信号橙 または 黄GP15 など好きな PWM ピン角度指令

⚠️ 電源の重要な注意

Pico の 3.3V ピン は最大 300mA 程度しか流せないため、SG90 を 3.3V で動かすと Pico がリセットしたり動作が不安定 になります。必ず VBUS(USB から来る 5V) または 別電源(単3×4本電池ボックス・USB 充電器など)から給電してください。外部電源を使う場合は必ず Pico の GND と外部電源の GND を接続 して、共通グランド を作るのを忘れずに(さもないと信号が伝わりません)。
SG90 サーボを Pico に接続した実体配線図
配線:SG90 の 3 本線を Pico の GP15(PWM 信号)・VBUS(5V)・GND に繋ぎます。SG90 1 個なら VBUS(USB の 5V)で足りますが、サーボを複数使ったり負荷をかけるときは外部電源を併用するのが安全です。(実体配線図は Fritzing の部品画像をもとに作図・CC BY-SA 3.0)

ステップ1:サーボを 90° に動かしてみよう

まずは 1 回だけ角度を指定して動かす 基本コードです。「サーボ」ブロック の ピン GP15・角度 90 度 をひとつ置くだけ。実行ボタンを押すと、サーボのホーン(白い羽根)が 中央位置(90°)に瞬時に動いて止まるのが見えます。

サーボを 90 度に回すブロック
ブロック構成:サーボ ピン GP15 を 90 度に回す(1ブロックのみ)。
from machine import PWM, Pin
_sv = PWM(Pin(15), freq=50)
_sv.duty_u16(int(1638 + 90 * 8192 // 180))

動作確認のコツ:サーボのホーンの初期位置はランダムなので、最初の 1 回目の実行で「ガッ」と中央に動く様子が見えます。2 回目以降は すでに 90° にいる ので動きません。角度を 0、45、135、180 などに変えて再実行 すると、その都度ホーンが該当位置にジャンプします。ホーンに鉛筆を貼り付けて 0/90/180 の位置を分度器で測ってみる と、実際の角度が指定値通りかを確認できます。

ステップ2:0° → 180° を行ったり来たりスイープしよう

サーボらしい動きを見るために、角度を 0° から 180° まで連続的に変えてみます。「変数 a を 0 から 180 まで 10 ずつ繰り返す」 ループの中で、新ブロック 「サーボ角度 a」(角度に変数を差し込めるバージョン)を呼び出します。0.05 秒ごとに 10° ずつ動かす と、ちょうどよい速さでホーンが扇形にスイープする様子が見られます。戻りは 180° → 0°(ステップ -10) のループでもう一度書き、forever でこれを繰り返します。

サーボを0度から180度までスイープするブロック
ブロック構成:ずっと繰り返す → for a in 0..180 step 10:サーボ角度a, 0.05秒待つ → for a in 180..0 step -10:サーボ角度a, 0.05秒待つ。「サーボ角度 a」 は今回新しく追加した pico_servo_angle_val ブロックで、角度に変数や計算式を差し込めます。
from machine import PWM, Pin
import utime
while True:
    for a in range(0, 181, 10):
        _sv = PWM(Pin(15), freq=50)
        _sv.duty_u16(int(1638 + max(0, min(180, a)) * 8192 // 180))
        utime.sleep(0.05)
    for a in range(180, -1, -10):
        _sv = PWM(Pin(15), freq=50)
        _sv.duty_u16(int(1638 + max(0, min(180, a)) * 8192 // 180))
        utime.sleep(0.05)

📝 新ブロック pico_servo_angle_val について

標準の 「サーボ」ブロック は角度を 0〜180 の 数字(リテラル) でしか指定できません(プルダウンや変数を差し込めない)。今回のように 角度を変数に入れたい、または センサーの値から角度を計算したい 場合は、「サーボ角度」ブロック(_val 版) を使います。見た目はほぼ同じですが、角度欄が 白いまる(値の差し込み口) になっていて、変数ブロックや計算ブロックを挿し込めるのが違いです。

動作確認のコツ:「ジー、ジー」と低い駆動音 が聞こえながら、ホーンが扇のように左右に振れます。動きがカクついたり、途中で止まる 場合は電源不足のサインなので、Pico の VBUS から直接給電 しているか確認してください。スピードを速くしたい なら 0.05 を 0.02 に、ゆっくり動かしたい なら 0.1 に変えて感触を試しましょう。

演習課題

課題3-12-1:角度・パルス幅・duty_u16 の対応表を作ろう

サーボ制御は 「角度 → パルス幅 → duty_u16」 の 3 段階の変換が肝です。公式を整理して、いくつかの角度について計算してみましょう。

パルス幅の式(今回採用): W [μs] = 500 + 角度 × (2400 - 500) / 180 ≒ 500 + 角度 × 10.56
duty_u16 の式(Pico の PWM 50Hz): duty_u16 = W ÷ 20000 × 65535 = 1638 + 角度 × 8192 / 180

問1: 角度 0°、45°、90°、135°、180° のとき、それぞれの パルス幅 W [μs] と duty_u16 を求めよ。
問2: duty_u16 = 3000 のとき、サーボはおよそ何度を指すか。
問3: もし freq=100(100Hz、周期 10ms)に変えてしまうと、サーボはどうなるか予想せよ。

▶ 模範解答と解説を見る
# 問1: 角度 → パルス幅 → duty_u16
for a in [0, 45, 90, 135, 180]:
    W = 500 + a * (2400 - 500) / 180
    d = 1638 + a * 8192 // 180
    print(f'{a}° : W={W:.0f}μs / duty_u16={d}')
# 0°   : W=500μs   / duty_u16=1638
# 45°  : W=975μs   / duty_u16=3686
# 90°  : W=1450μs  / duty_u16=5734(=1638+4096)
# 135° : W=1925μs  / duty_u16=7782
# 180° : W=2400μs  / duty_u16=9830(=1638+8192)

# 問2: duty_u16=3000 から角度を逆算
# d = 1638 + a × 8192/180
# a = (3000 - 1638) × 180 / 8192 ≈ 29.9°

# 問3: freq=100Hz に変えると周期は 10ms。
# パルス幅 500〜2400μs はそのままでも、サーボ側は
# 「20ms 周期の信号」を前提に作られているので、
# 動きが不安定になる、唸る、過熱する等の不具合が起きる。
# SG90 は freq=50(=20ms 周期)固定で使うのがルール。

解説: 解説:サーボ制御の 「3 段階変換」 を頭に入れておくと、別のサーボ(例:MG996R / DS3218 など)でパルス幅範囲が違うときも、「最小パルス幅と最大パルス幅をデータシートで確認 → 1638 と 8192 を置き換える」 という流れで対応できます。キャリブレーション:個体差で 0° のホーン位置がズレているサーボには、1638 を 1500 に下げるなどの調整が必要なことがあります(ピアノの調律のように)。SG90 の連続回転改造版(FS90R など)では 「角度」ではなく「速度と方向」 を duty_u16 で指定する仕様に変わります。

課題3-12-2:可変抵抗ノブでサーボの角度をコントロールしよう

#05 で使った可変抵抗ノブ(ADC:GP26)を回すと、サーボがそれに連動して動く ような直感的なコントローラを作ります。ADC 値(0〜65535)を 「× 180 ÷ 65535」 の式で 0〜180 に変換し、新ブロック 「サーボ角度」 に差し込みます。ロボットアームを手元のつまみで操作するイメージです。

配線追加:サーボ(GP15 + VBUS + GND)に加えて、可変抵抗ノブを3V3・GP26・GND(3 ピン)に繋ぎます。GP15(サーボ)と GP26(ADC)はぶつからない ので安心です。

▶ 模範解答と解説を見る

ブロックの組み合わせ例:

可変抵抗ノブでサーボの角度を制御するブロック
ブロック構成:ずっと繰り返す → アナログ入力 GP26 → pot → サーボ角度 (pot × 180 ÷ 65535) → 0.02秒待つ。角度の式は py_math_op を2回ネストして (pot × 180) ÷ 65535 を作っています。
from machine import ADC, PWM, Pin
import utime
_adc26 = ADC(26)
while True:
    pot = _adc26.read_u16()
    _sv = PWM(Pin(15), freq=50)
    _sv.duty_u16(int(1638 + max(0, min(180, pot * 180 / 65535)) * 8192 // 180))
    utime.sleep(0.02)

解説: 動作確認のコツ:ノブを左いっぱい → サーボは 0° に、ノブを右いっぱい → サーボは 180° に、ノブを真ん中 → サーボは 90° に動きます。動きがガクガクする 場合は wait を 0.02 → 0.05 に伸ばすか、ADC 値のスムージング(移動平均:直前 5 回の平均を使う)を加えると滑らかになります。発展:

  • 角度範囲を狭める:式を pot × 90 / 65535 + 45 にすると 45°〜135° の範囲のみ動かせる(限られたスペースで使うロボット関節向け)
  • 2 つのサーボを同時制御:可変抵抗を 2 個(GP26 と GP27)使って、X軸サーボと Y軸サーボを別々に動かせばパン・チルト雲台が作れる
  • 逆向き連動:式を 180 - pot × 180 / 65535 にすると、ノブを右に回すとサーボが左に動く逆連動になる

可変抵抗ノブとサーボを Pico に接続した実体配線図
課題3-12-2 の配線:サーボ(GP15・VBUS・GND)と可変抵抗ノブ(3V3・GP26・GND)を併用。外部電源を使う場合は、その GND と Pico の GND を 必ず共通 にしてください。

課題3-12-3:超音波センサーで自動開閉ゲートを作ろう

#09 で使った超音波センサー HC-SR04 と組み合わせて、「20cm 以内に物が近づいたらゲートを開ける」 自動ドアを作ります。距離が 20cm 未満ならサーボ 90°(ゲート開)、それ以上なら 0°(ゲート閉) という条件分岐。実際にレゴブロック等で ホーンの先に小さな板(ゲート) を付ければミニチュア自動ドアの完成です。

配線追加:サーボ(GP15 + VBUS + GND)に加えて、HC-SR04 を3V3・GP2(TRIG)・GP3(ECHO)・GND に繋ぎます。HC-SR04 の標準ピン GP14/GP15 はサーボ(GP15)とぶつかる ので、今回は HC-SR04 を GP2/GP3 に変更しています。HC-SR04 は 5V 動作が標準 ですが、Pico は 3.3V なので#09 で学んだ 3.3V 改造版(VCC=3V3) または 5V 給電+ECHO に分圧抵抗 のどちらかを選んでください。

▶ 模範解答と解説を見る

ブロックの組み合わせ例:

距離センサーで自動開閉ゲートを制御するブロック
ブロック構成:ずっと繰り返す → 超音波距離 TRIG GP2 ECHO GP3 → dist → もし dist < 20 ならサーボ 90°(開)、そうでなければサーボ 0°(閉)→ 0.3秒待つ。
from machine import Pin, PWM, time_pulse_us
import utime
_trig = Pin(2, Pin.OUT)
_echo = Pin(3, Pin.IN)
while True:
    _trig.value(0); utime.sleep_us(2)
    _trig.value(1); utime.sleep_us(10); _trig.value(0)
    _t = time_pulse_us(_echo, 1, 30000)
    dist = _t / 58 if _t > 0 else -1
    if dist < 20:
        _sv = PWM(Pin(15), freq=50)
        _sv.duty_u16(int(1638 + 90 * 8192 // 180))
    else:
        _sv = PWM(Pin(15), freq=50)
        _sv.duty_u16(int(1638 + 0 * 8192 // 180))
    utime.sleep(0.3)

解説: 動作確認のコツ:手を 20cm 以内に近づけるとサーボがガッと 90° に動いて、離すと元の 0° に戻る挙動を確認しましょう。0.3 秒の wait は HC-SR04 の測定間隔(最小 60ms) とサーボの動作時間(180° で約 0.3 秒) の両方を満たす値です。反応が遅い・チラチラする 場合は wait を 0.5 秒に伸ばしてみてください。発展:

  • 段階的にゲートを開ける:dist < 10 → 180°(全開)、dist < 20 → 90°(半開)、else → 0°(閉) の 3 段階制御で、近づくほど大きく開く賢いゲートに進化
  • 距離→角度の連続マッピング:angle = 180 - dist × 3(dist=0→180°、dist=60→0°)のように 距離に比例した角度に動かすと、リニアなジェスチャーセンサー風に
  • LCD で距離・角度を表示:#08 の I2C LCD を組み合わせて「距離 12 cm / 角度 90°」のような状態表示を加えると、デバッグも楽しい

超音波センサーとサーボを Pico に接続した実体配線図
課題3-12-3 の配線:サーボ(GP15・VBUS・GND)と HC-SR04(3V3・GP2(TRIG)・GP3(ECHO)・GND)。HC-SR04 の 標準 5V 動作 で使う場合は ECHO に 1kΩ + 2kΩ の分圧抵抗(#09 で学習済み)を必ず入れてください。

まとめ

  • SG90 サーボ は 3 本線(VCC・GND・PWM)で動く 0〜180° の小型角度制御モーター。
  • 角度は PWM 50Hz(周期 20ms)のパルス幅(500〜2400μs) で決まる。Pico の duty_u16 値(1638〜9830) として PWM ブロックに渡される。
  • 標準ブロック 「サーボ」 は角度をリテラル指定(0〜180)。今回追加した 「サーボ角度」(_val 版) は変数や計算式を差し込める。
  • 電源は VBUS(5V)または外部電源 を使う。3.3V ピンでは電流不足 で Pico がリセットする。外部電源時は 共通グランド を忘れずに。
  • 可変抵抗(#05)・超音波センサー(#09)と組み合わせれば、手回しコントローラ・自動開閉ドア・ロボットアーム など多彩な応用が可能。
  • ピン衝突に注意:今回は HC-SR04 を GP2/GP3 に変更してサーボ GP15 と分けた。

📝 サーボの種類いろいろ

今回扱った SG90(180° 動作・小型)以外にも、連続回転サーボ(360° 自由回転、速度制御に近い)、デジタルサーボ(PWM 信号の応答が速く保持力も強い)、金属ギアの強力版(MG90S / MG996R / DS3218)、シリアル通信サーボ(XYZrobot A1-16 / Dynamixel) など多彩な仲間がいます。ロボットアーム・二足歩行ロボット・ドローン など、用途に応じて使い分けるのが楽しいところです。

次の記事は #13「DC モーターを動かそう」。L298N モータードライバを使って、ラジコンカーや扇風機のようにグルグル回り続けるモーターを速度と方向の両方 でコントロールします。

トップへ戻る