今回は ステッピングモーター を使って、「1 ステップずつカチカチと角度を刻むモーター」 の制御を学びます。#12 のサーボは 「絶対角度 0〜180° を指定」、#13 の DC モーターは 「ずっと回り続ける」 のが特徴でしたが、ステッピングモーターは 「指定したステップ数だけ正確に進む」 のが最大の強み。位置センサー(エンコーダ)なしで絶対位置を制御できる ため、3D プリンタ・CNC マシン・スキャナ・カメラの自動フォーカス・天体望遠鏡の自動追尾 など、精密な角度制御が要る現場では 圧倒的に主役 です。
今回使う 28BYJ-48 + ULN2003 ドライバボード は教育用ステッピングモーターの定番セット。4 つのコイル(IN1〜IN4)を順番に ON/OFF するシーケンス で内部のローターが少しずつ回り、ギア比 1/64 の減速ギア を通すことで 出力軸 1 ステップ ≒ 0.088° という高い角度分解能 を実現しています。4096 ステップでちょうど 1 回転(360°) という覚えやすい数字も嬉しいポイント。
- ステッピングモーターの仕組み(コイルを順番に励磁してローターを引き寄せる)
- 28BYJ-48 の構造:内部 4 極ステーター + 永久磁石ローター + 1/64 ギア
- ULN2003 ドライバ:マイコンの弱い信号を 4 コイルの大きな電流に変換
- 1-2 相励磁(半ステップ駆動) の 8 パターンシーケンス
- 4096 ステップ = 1 回転 の換算式と角度指定の使い分け
- ボタンや超音波センサーと組み合わせた イベント駆動の角度制御
- サーボ・DC モーターとの 「精度 vs トルク vs 速度」 使い分け
今回は PycoBlocks(MicroPythonモード) の 「モーター」カテゴリ にある「ステッパー」 ブロックを使います。IN1〜IN4 の 4 ピン指定 + ステップ数 / 角度 + 遅延(ms) を選ぶだけで、内部では 1-2 相励磁の 8 パターンシーケンス + 4 コイルの順次励磁ループ が自動生成されます。

ステッピングモーターと 28BYJ-48 の基礎
ステッピングモーター は、内部の 複数のコイル(電磁石)を順番に ON/OFF する ことで、中心の 永久磁石ローター を 1 ステップずつ一定角度だけ回す モーターです。コイルに電流が流れている間は そのコイルにロックされて止まる ため、「電源を切らない限り正確な角度で保持される」 のが大きな特徴。サーボのような 位置センサー(ポテンショメータ)が不要 で、「ステップ数を数えるだけで絶対位置がわかる」 構造になっています。
28BYJ-48 は 「内部 5V・ユニポーラ駆動・4 相 5 線式・1/64 ギア内蔵」 の小型ステッピングモーター。赤・橙・黄・桃・青の 5 色リード線 がドライバボードの白いコネクタにそのまま差し込めるようになっており、ULN2003 ドライバボード(赤色 LED 4 個と緑色基板が特徴)とセットで 200〜500 円程度。Amazon・秋月電子・千石電商 で「28BYJ-48 ULN2003」で検索すれば必ずヒットします。
| 項目 | 28BYJ-48 仕様 | 備考 |
|---|---|---|
| 動作電圧 | 5V DC | 本記事では 電池ボックス(単3×4本) から給電 |
| 消費電流 | 約 240mA(4 コイル合計) | USB からは取らず電池から供給する |
| ステップ角(モーター単体) | 5.625° / 64 ステップ | 1-2 相励磁時。フル相励磁なら 11.25° |
| ギア比 | 1 : 64 | 正確には 1 : 63.68395、誤差約 0.5% |
| 出力軸 1 回転 | 4096 ステップ(1-2 相励磁) | フル相励磁なら 2048 ステップ |
| 出力軸 1 ステップ | 約 0.088°(= 360 ÷ 4096) | かなり高い角度分解能 |
| 最大トルク | 約 34mN·m(300 g·cm) | ギア減速のおかげで意外と力強い |
| 回転速度 | 最大 15 rpm 程度 | サーボや DC モーターより遅め |
| 動作音 | 「カリカリ」と少し騒がしい | 静音化したいなら DRV8825 等 |
| 価格 | 200〜500 円(ドライバ付き) | 教育用に最高コスパ |
1-2 相励磁シーケンス:なぜ「8 パターン」で動くのか
ステッピングモーター内部の 4 つのコイル(IN1〜IN4) は、ローターを取り囲むように 90°ずつ配置 されています。「1 つだけ ON → ローターがそのコイルの方向に引かれる」 という単純な動作を8 つの組み合わせで順番に切り替える ことで、ローターが滑らかに 1 ステップずつ回ります。これが 1-2 相励磁(半ステップ駆動) と呼ばれる手法で、「1 個 ON → 2 個 ON → 1 個 ON → ...」 を繰り返すのが名前の由来です。

# 1-2 相励磁(半ステップ駆動)の 8 パターン
# IN1 IN2 IN3 IN4 コメント
# 1 0 0 0 ステップ1: コイル1のみ ON(ローターは IN1 側に固定)
# 1 1 0 0 ステップ2: コイル1+2 ON(中間位置に引かれる)
# 0 1 0 0 ステップ3: コイル2のみ ON(ローターは IN2 側に固定)
# 0 1 1 0 ステップ4: コイル2+3 ON(次の中間位置)
# 0 0 1 0 ステップ5: コイル3のみ ON
# 0 0 1 1 ステップ6: コイル3+4 ON
# 0 0 0 1 ステップ7: コイル4のみ ON
# 1 0 0 1 ステップ8: コイル4+1 ON(一周してステップ1の前に戻る)
#
# このパターンを順番に出すと → 正転(時計回り)
# このパターンを逆順に出すと → 逆転(反時計回り)
# 8 パターンで 5.625°(モーター内部角)、ギア 1/64 で出力軸は約 0.088° 進む
「フル相励磁(4 ステップ)」 という単純な駆動方法もありますが、1-2 相励磁(8 ステップ) の方が ステップ角が半分(より精密) で、振動と騒音も少なく滑らか。PycoBlocks の 「ステッパー」ブロック も内部でこの 8 パターンシーケンスを自動生成 しているので、利用者は「ステップ数」「ピン」「遅延」だけを指定すれば 細かい励磁制御を意識せずに済みます。
出力軸 1 回転 = 4096 ステップ の根拠:モーター内部のローター 1 回転には 64 ステップ(1-2 相励磁の場合) 必要で、ギア比 1/64(モーター 64 回転で出力軸 1 回転)なので、64 × 64 = 4096 ステップ でちょうど出力軸が 1 周します。1 ステップあたりの角度 は 360° ÷ 4096 ≒ 0.088°。これはサーボの「角度ジッタ ±1°」より遥かに精密 な世界です。
配線:ULN2003 ドライバボード経由で Pico に繋ぐ
28BYJ-48 + ULN2003 セット は配線が非常にシンプル。モーター → ドライバボードの白コネクタ はワンタッチで挿せて、ドライバボード → Pico は 4 本の信号線 だけで、電源 2 本は電池ボックス につなぎます。今回は IN1=GP8・IN2=GP9・IN3=GP10・IN4=GP11(連続した GP ピン)を使います。
| ULN2003 ボード | 繋ぎ先 | 役割 |
|---|---|---|
| IN1 | Pico GP8 | コイル1 ON/OFF |
| IN2 | Pico GP9 | コイル2 ON/OFF |
| IN3 | Pico GP10 | コイル3 ON/OFF |
| IN4 | Pico GP11 | コイル4 ON/OFF |
| + (VCC) | 電池ボックスの +(単3×4本) | モーター用電源 |
| - (GND) | 電池ボックスの − と Pico GND | 共通グランド |
| 白コネクタ | 28BYJ-48 の 5 色リード | 赤 = COM、橙黄桃青 = 4 コイル |
📝 電源の注意

ステップ1:4096 ステップでちょうど 1 回転させてみよう
まずは 「ステップ数」を直接指定するシンプルな例 から。「ステッパー IN1:GP8 IN2:GP9 IN3:GP10 IN4:GP11 ステップ数 4096 遅延 2 ms」 ブロックを 1 個置くだけ。実行ボタンを押すと、出力軸が ちょうど 1 周(360°) 回って止まります。遅延 2ms はステップ間の待ち時間で、これより短くするとモーターが追いつかず脱調(同期外れで止まる)するため 1〜5ms 程度が標準。

from machine import Pin
import utime
_st_pins = [Pin(p, Pin.OUT) for p in [8, 9, 10, 11]]
_st_seq = [[1,0,0,0],[1,1,0,0],[0,1,0,0],[0,1,1,0],
[0,0,1,0],[0,0,1,1],[0,0,0,1],[1,0,0,1]]
_st_n = abs(4096)
_st_dir = 1 if 4096 >= 0 else -1
for _s in range(_st_n):
_st_i = (_s * _st_dir) % 8
for _j in range(4):
_st_pins[_j].value(_st_seq[_st_i][_j])
utime.sleep_ms(2)
動作確認のコツ:シャフトにマスキングテープを巻いて目印 を付けると、「カチカチカチ」 と小さな音を立てながら 約 8 秒かけて 1 周 する様子が観察できます。ULN2003 の 4 つの赤 LED も 順番に点灯パターンを変えている のが見えるので、「内部で 8 ステップシーケンスが動いている」 ことを目で確かめられます。ステップ数を負値(-4096) にすると逆転(反時計回り)に 1 周します。
⚠️ 動かない時のチェックリスト
- ULN2003 の 4 個の赤 LED が 順番にパターンを変えて点灯 しているか(点いていれば信号は正しく出ている)
- 白コネクタが 奥までしっかり挿さっている か(半挿しだとコイル接触不良)
- 電池ボックスの + と − がドライバボードにつながり、− が Pico の GND ともつながっている か(電池の向きと残量も確認)
- IN1〜IN4 の 順番が GP8/9/10/11 と一致 しているか(順番がずれると振動するだけで回らない)
ステップ2:90° ⇔ -90° を往復スイープしてみよう
ステッピングモーターの真骨頂は 「絶対角度を指定できる」 こと。「ステッパー角度」ブロック を使えば、「-360°〜+360°」を 1° 単位 で指定でき、内部で 「角度 × 4096 ÷ 360」 でステップ数に変換されます。今回は 「+90° 回す → 0.5 秒待つ → -90° 戻す → 0.5 秒待つ」 を forever ループで繰り返し、サーボのような往復動作 を再現します。

from machine import Pin
import utime
while True:
_st_pins = [Pin(p, Pin.OUT) for p in [8, 9, 10, 11]]
_st_seq = [[1,0,0,0],[1,1,0,0],[0,1,0,0],[0,1,1,0],
[0,0,1,0],[0,0,1,1],[0,0,0,1],[1,0,0,1]]
_st_n = int(abs(90) * 4096 / 360)
_st_dir = 1 if 90 >= 0 else -1
for _s in range(_st_n):
_st_i = (_s * _st_dir) % 8
for _j in range(4):
_st_pins[_j].value(_st_seq[_st_i][_j])
utime.sleep_ms(2)
utime.sleep(0.5)
# 逆方向 -90°
_st_n = int(abs(-90) * 4096 / 360)
_st_dir = 1 if -90 >= 0 else -1
for _s in range(_st_n):
_st_i = (_s * _st_dir) % 8
for _j in range(4):
_st_pins[_j].value(_st_seq[_st_i][_j])
utime.sleep_ms(2)
utime.sleep(0.5)
動作確認のコツ:出力軸が 右に 90° → 左に 90° を 2 秒周期 で繰り返し、ちょうど レーダーアンテナのスイープ のような動きを見せます。「90 → 180 → 90」 のように 絶対位置ではなく相対回転 しているので、実用ロボットでは 「現在位置を変数 pos で記憶」 しておき、「目標位置 - 現在位置」 を毎回計算するパターンがよく使われます(演習 3 で扱います)。
角度のチューニング:「90° 指定したのに実際には 89.5° しか回らない」 ような誤差が出る場合があります。これは 28BYJ-48 のギア比が厳密には 1:63.68395(4096 ではなく 4076 ステップ/回転)のためで、精密用途では実機キャリブレーション(実際に何ステップで 1 回転するか目視測定 → 換算式を補正)が必要です。教育目的なら 「4096 ≒ 1 回転」 の近似で十分実用になります。
演習課題
課題3-14-1:ステップ角・回転速度・分解能の計算問題
ステッピングモーターの特性を 計算で理解 しましょう。28BYJ-48 + ULN2003(1-2 相励磁・出力軸 4096 ステップ/回転・遅延 2ms)を前提に答えてください。
問1: 出力軸を 180° 回すには何ステップ必要か。また、遅延 2ms の場合、180° 回るのに何秒かかるか計算せよ。
問2: 出力軸の 最大回転速度(rpm = 1分間の回転数) を求めよ。遅延 2ms の場合と 遅延 1ms の場合の両方を計算せよ。
問3: 3D プリンタのリードスクリュー(1 回転で 8mm 送り)に 28BYJ-48 を直結したとき、「Z 軸を 0.1mm だけ送る」 には何ステップ必要か。「このステッパーは Z 軸の精密送りに使える分解能を持っているか」 検証せよ。
問4: 28BYJ-48 のギア比は実は 1 : 63.68395(厳密値)であり、出力軸 1 回転 = 64 × 63.68395 ≒ 4076 ステップ が正しい。ブロックで 「100 回転」 させたつもりが、実際には何回転になるか計算せよ。
▶ 模範解答と解説を見る
# 問1: 180° 回すのに必要なステップ数と時間
# ステップ数 = 180 × 4096 / 360 = 2048 ステップ
# 所要時間 = 2048 × 2ms = 4096ms ≒ 4.1 秒
# 問2: 最大回転速度(rpm)
# 遅延 2ms: 1 ステップ 2ms → 4096 ステップで 8192ms = 8.192 秒/回転
# → 60秒 / 8.192秒 ≒ 7.3 rpm
# 遅延 1ms: 1 ステップ 1ms → 4096 ステップで 4096ms = 4.096 秒/回転
# → 60秒 / 4.096秒 ≒ 14.6 rpm
# ※ ただし遅延を短くしすぎると脱調するので実用は 2ms 程度
# 問3: 3D プリンタ Z 軸 0.1mm 送り
# リードスクリュー 8mm/回転 ÷ 0.1mm = 80 分の 1 回転
# 1 回転 = 4096 ステップ → 0.1mm = 4096 / 80 = 51.2 ステップ
# つまり 51 ステップ送ると 0.0996mm、52 ステップで 0.1016mm
# 1 ステップあたり 8 / 4096 = 約 0.00195mm の分解能
# → 0.1mm 送りに対して 51 か 52 ステップで誤差約 ±0.001mm
# → 3D プリンタ用途には少しトルク不足だが、分解能は十分
# 問4: ギア比誤差
# 100 回転をブロック指示 → 100 × 4096 = 409600 ステップ送る
# 実際の 1 回転は 4076 ステップなので → 409600 / 4076 ≒ 100.49 回転
# 100 回転指示に対して 0.49 回転 = 約 176° 多く回ってしまう
# → 精密用途では「4076 ステップ/回転」で換算する補正が必要
解説: 解説:ステッピングモーターは 「ステップ数 ÷ 単位回転ステップ数」 で角度を完全に決定できるため、「指示通り正確に動く」 ことが最大の強み。ただし 「機械的なギア精度」 による誤差は避けられず、「累積回転すると徐々にズレる」 弱点もあります。本格的な CNC・3D プリンタでは 「ホームポジションセンサー(リミットスイッチ)」 で定期的に基準位置にリセット することで累積誤差をリセットしています。4076 vs 4096 の差(約 0.5%) は教育目的では無視できる範囲ですが、「設計値と実機の違いがある」 という工学の鉄則を覚えておくと「指示通りに動かない!」 のトラブル原因切り分けが速くなります。
課題3-14-2:タクトスイッチを押すたびに 90° ずつ回そう
#03 で使ったタクトスイッチ(GP14・プルアップ) と組み合わせて、「ボタンを押すたびに 90° ずつ回転」 するインターフェースを作ります。4 回押すと 1 周 という分かりやすい挙動で、ロータリースイッチ・カラクリ仕掛け・ガチャ機構 の制御の基礎になります。
配線追加:ステッパー回路(GP8〜11 + 電池ボックス + GND)に加えて、タクトスイッチを3V3 + GP14(プルアップ)+ GND に繋ぎます。ステッパーの GP8〜11 とタクトスイッチの GP14 はぶつからない ので問題ありません。プルアップ回路の詳しい説明は #03 を参照。
▶ 模範解答と解説を見る
ブロックの組み合わせ例:

from machine import Pin
import utime
_btn14 = Pin(14, Pin.IN, Pin.PULL_UP)
while True:
if _btn14.value() == 0:
_st_pins = [Pin(p, Pin.OUT) for p in [8, 9, 10, 11]]
_st_seq = [[1,0,0,0],[1,1,0,0],[0,1,0,0],[0,1,1,0],
[0,0,1,0],[0,0,1,1],[0,0,0,1],[1,0,0,1]]
_st_n = int(abs(90) * 4096 / 360)
_st_dir = 1 if 90 >= 0 else -1
for _s in range(_st_n):
_st_i = (_s * _st_dir) % 8
for _j in range(4):
_st_pins[_j].value(_st_seq[_st_i][_j])
utime.sleep_ms(2)
utime.sleep(0.1)
解説: 動作確認のコツ:ボタンを 1 回チョン押し すると 「カチカチカチ…」と約 2 秒 かけて 90° 回ります。4 回押せば 1 周(360°)。シャフトに 4 等分の目印 を付けておくと「絶対角度が正確に決まる」 ステッピングモーターの強みが体感できます。発展:
- 2 つのボタンで時計回り/反時計回り:GP14 → 90°、GP15 → -90° で双方向操作
- チャタリング対策:押した瞬間に複数回検出される問題を
utime.sleep_ms(50)で防ぐ(押した直後に少し待つ) - 「押した回数を覚える」:変数 count に加算して、4 回押したら自動的に元の位置に戻る「カラクリ機構」を実装

課題3-14-3:超音波センサーで自動開閉する近接ゲートを作ろう
#09 の HC-SR04 と組み合わせて、「20cm 以内に近づくと 90° 開いて、離れると 0° に戻る」 自動ゲート(門)を作ります。#12 演習3 と同じ仕様 ですが、ステッピングモーターならではの工夫として 「現在位置を変数 state で記憶」 し、「同じ動作を 2 回繰り返さない(無駄回転防止)」 ロジックを入れます。
💡 なぜ state 変数が必要?
配線追加:ステッパー回路(GP8〜11 + 電池ボックス + GND)に加えて、HC-SR04 を3V3・GP14(TRIG)・GP15(ECHO)・GND に繋ぎます。ステッパー GP8〜11 と HC-SR04 GP14/15 はぶつからない ので、HC-SR04 の標準ピンのまま使えます。HC-SR04 は #09 と同じく 3.3V 版(HC-SR04+ / HC-SR04P)を 3V3 に直結する前提なので、分圧抵抗は要りません。
▶ 模範解答と解説を見る
ブロックの組み合わせ例:

from machine import Pin, time_pulse_us
import utime
state = 0
while True:
_trig = Pin(14, Pin.OUT)
_echo = Pin(15, Pin.IN)
_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:
if state == 0:
_st_pins = [Pin(p, Pin.OUT) for p in [8, 9, 10, 11]]
_st_seq = [[1,0,0,0],[1,1,0,0],[0,1,0,0],[0,1,1,0],
[0,0,1,0],[0,0,1,1],[0,0,0,1],[1,0,0,1]]
_st_n = int(abs(90) * 4096 / 360)
_st_dir = 1 if 90 >= 0 else -1
for _s in range(_st_n):
_st_i = (_s * _st_dir) % 8
for _j in range(4):
_st_pins[_j].value(_st_seq[_st_i][_j])
utime.sleep_ms(2)
state = 1
else:
if state == 1:
_st_pins = [Pin(p, Pin.OUT) for p in [8, 9, 10, 11]]
_st_seq = [[1,0,0,0],[1,1,0,0],[0,1,0,0],[0,1,1,0],
[0,0,1,0],[0,0,1,1],[0,0,0,1],[1,0,0,1]]
_st_n = int(abs(-90) * 4096 / 360)
_st_dir = 1 if -90 >= 0 else -1
for _s in range(_st_n):
_st_i = (_s * _st_dir) % 8
for _j in range(4):
_st_pins[_j].value(_st_seq[_st_i][_j])
utime.sleep_ms(2)
state = 0
utime.sleep(0.3)
解説: 動作確認のコツ:手を 20cm 以内に近づけるとゲートが 「カチカチカチ…」と約 2 秒 かけて 90° 開き、手を離すと同じく 2 秒かけて閉じます。「すでに開いている状態でさらに手を近づけても、もう一度開かない」 のが state 変数のおかげ。ガレージのシャッター や 自動ドア と同じ 状態遷移制御(FSM)の入門例として、組み込みプログラミングの定番パターンです。発展:
- 3 段階開閉:dist<10 → 90°、dist<30 → 45°、それ以上 → 0° の 3 状態で段階的に開く
- 「タイムアウトで自動閉鎖」:開いたまま 5 秒経過したら強制的に閉じる安全機能を追加
- LCD で状態表示:#08 の I2C LCD を組み合わせて「状態: 開 / 距離: 15cm」を表示

まとめ
- ステッピングモーター は 「コイルを順番に励磁してローターを 1 ステップずつ回す」 「位置センサー不要・絶対角度制御可能」 なモーター。
- 28BYJ-48 + ULN2003 セットは教育用の定番。5V 動作・電源は電池ボックスから・約 240mA・ 出力軸 4096 ステップ/回転。
- 1-2 相励磁(半ステップ駆動) の 8 パターンシーケンス を順番に出力することでローターが滑らかに回る。逆順なら逆回転。
- PycoBlocks の 「ステッパー」「ステッパー角度」ブロック は内部でこのシーケンスを自動生成。ステップ数または角度を指定 するだけで使える。
- 角度指定の換算式:ステップ数 = 角度 × 4096 ÷ 360。1 ステップ ≒ 0.088° という高い分解能。
- 相対回転なので 「現在位置を変数で記憶」 しないと累積角度がズレる。実用ロボットでは FSM(状態機械) で動作を整理する。
📝 ステッピングモーターの仲間いろいろ
#14 はモーター制御の最終回。サーボ・DC モーター・ステッピングモーターという 「アクチュエータ三銃士」 をすべて触りました。次回 #15 は Pico W 専用の Wi-Fi 入門。「LED をクラウドから ON/OFF する」 など、IoT の世界に踏み込みます。ハードウェア制御 → ネットワーク制御へ、新しい広がりにご期待ください。