MG996R × Pico 2W
はじめに
こんにちは。ニンジンです🥕
この記事では、サーボモーター「MG996R」のデータシートの主な値(電圧・電流・トルク・寸法)を公称値と実測値で比べたうえで、Raspberry Pi Pico 2WとMicroPythonでPWM制御する使い方を、実機でのキャリブレーション手順と合わせて解説します。
今回はRaspberry Pi Pico 2Wと、ホビー用の定番サーボモーター「MG996R」を組み合わせて動かしてみます。タクトスイッチを押すとサーボが指定した角度まで動く、というシンプルな工作ですが、実際にやってみると「コード通りの角度に動いてくれない」「サーボホーンが狙った位置に取り付けられない」など、実機ならではのつまずきがいろいろありました。この記事では、その試行錯誤も含めて紹介します。
この記事で分かること
- MG996Rのデータシートの主な値(電圧・電流・トルク・寸法)と、資料ごとの違い
- MicroPythonでPWM制御してサーボモーターを動かす基本
- 安価なサーボは公称スペック通りに動くとは限らない、実機キャリブレーションの手順
- サーボホーンの取付角度のズレをソフトウェアで補正する方法
- タクトスイッチで複数の動作パターンを切り替える実装
MG996Rのデータシート(スペック一覧)|電圧・電流・トルク・寸法
MG996Rは、ラジコンやロボット工作で定番のメタルギア・デジタルサーボです。まずはデータシートの主な値を一覧にしました。調べてみると、Tower Pro公式サイトの値と、ネットでよく出回っているPDF版データシートの値が一部食い違っていたので、両方を並べ、手持ちの実機で測った値も添えています。寸法・重量はどちらも公称値との差が0.2mm・0.2g以内で、ほぼ公称値どおりでした。
| 項目 | Tower Pro公式サイト | PDF版データシート | 今回の実機・備考 |
|---|---|---|---|
| 寸法 | 40.7×19.7×42.9mm | 40.7×19.7×42.9mm | 40.5×19.8×43.0mm(実測) |
| 重量 | 55g | 55g | 55.2g(実測) |
| ストールトルク | 9.4kgf·cm(4.8V) 11kgf·cm(6V) | 9.4kgf·cm(4.8V) 11kgf·cm(6V) | —(測定できず) |
| 動作速度 | 0.19秒/60°(4.8V) 0.15秒/60°(6V) | 0.17秒/60°(4.8V) 0.14秒/60°(6V) | sleep(0.5)では回り切らず、1〜3秒待って角度を確認 |
| 動作電圧 | 4.8〜6.6V | 4.8〜7.2V | 5V(USB電源)で動作 |
| 消費電流 | 待機 10mA 無負荷 170mA ストール 1,400mA | 動作 500〜900mA(6V) ストール 2.5A(6V) | 電源は5V/2AのUSB ACアダプタ |
| 制御信号 | 記載なし | PWM 周期20ms(50Hz) | 50Hz・パルス幅500〜2340usで使用 |
| 可動範囲 | 記載なし | 約120°(左右60°ずつ) | 500〜2500usで約198°回転 |
| 不感帯 | 1us | 5us | — |
| 配線色 | 記載なし | 茶=GND / 赤=電源 / 橙=PWM信号 | 同じ(配線は後述) |
| 使用温度 | 0〜55℃ | 0〜55℃ | — |
出典: Tower Pro公式 製品ページ(MG996R) / 販売店などで配布されているPDF版データシート「MG996R High Torque Metal Gear Dual Ball Bearing Servo」。「—」は今回測っていない項目です。
データシートの値が資料によって違う理由と、どちらを使うか
MG996Rは互換品やクローン品が非常に多く、販売元ごとに微妙に違う数値が載っています。公式サイトとPDF版でも、動作速度・電圧の上限・電流の値が違いました。販売ページでは「0〜180°」と書かれていることが多い可動範囲も、PDF版では「約120°」、今回の個体では約198°と、資料と実物でばらばらです。
実際に使うときは、電圧は低いほう(公式の6.6V以下)、電流は大きいほう(PDF版のストール2.5A)を基準に見積もると安全側になります。可動範囲は資料を当てにせず、後半で紹介するように実機で測るのが確実です。
MG996Rの消費電流と電源の選び方(ストール電流は1.4A〜2.5A)
サーボは、動き出す瞬間や、負荷がかかって止められた状態(ストール)のときに大きな電流が流れます。ストール電流は公式で1.4A、PDF版で2.5A(6V時)です。今回使った5V/2AのUSB ACアダプタは、1台を無負荷〜軽い負荷で動かす範囲では問題なく動きました。ただしPDF版の値で考えると、重いものを持ち上げて止まるような使い方では容量が足りない場面がありえます。サーボを2台以上つなぐ場合は、台数分の電流を足して電源を選んでください。
なお、PicoのVBUSや3V3ピンからサーボに給電するのは避けます。今回の電源の分け方と、突入電流対策の電解コンデンサ(220μF)については、後述の配線セクションで説明しています。
MG996Rの使い方の基本:PWMのパルス幅で角度を決める
サーボモーターは、送られてくるパルス幅(us)によって回転する角度が変わる仕組みです。一般的には「パルス幅500us=0度、パルス幅2500us=180度」という目安が使われますが、これはあくまで目安です。後述の通り、今回使用した個体はこの通りには動きませんでした。
配線方法:MG996RとRaspberry Pi Pico 2Wの結線
電源をPicoの3.3V/USBから取ってはいけない理由
MG996Rは動作時に数百mA、場合によっては1A近い電流が流れます。Pico側のUSBや3V3ピンから直接給電すると、Pico側のレギュレータの許容電流を超えてリセットや破損のリスクがあるため、サーボ用の電源はPicoとは別に用意します。今回はUSB ACアダプタ(5V/2A)から、USBケーブルを1本潰して電源線として使いました。
GPIOピンの割り当てと信号線の接続
| 線・部品 | 接続先 |
|---|---|
| MG996R 信号線(オレンジ) | Pico 2W GP15 |
| MG996R 電源線(赤) | USB電源 +5V |
| MG996R GND線(茶) | USB電源GND、かつPico 2WのGNDと共通接続 |
| タクトスイッチA | GP14 – GND間 |
| タクトスイッチB | GP13 – GND間 |
USB ACアダプタから伸びるUSBケーブルを1本潰し、赤線(+5V)・黒線(GND)をブレッドボードの電源レールに接続しています。
電源ライン(5V-GND間)に電解コンデンサ(16V/220μF)を入れることで、サーボ起動時の突入電流やノイズによる電源の乱れを抑えられます。
Freenoveブレークアウトボード経由で、GP15(サーボ信号線)・GP14(スイッチA)・GP13(スイッチB)・GNDへ配線しています。
共通GNDを接続しなかった場合の誤動作の様子
MicroPythonでPWM制御してMG996Rを動かす
サーボ制御の基本:PWM周波数とデューティサイクル(角度)の関係
サーボモーターの角度制御には、周波数50Hz(20ms周期)のPWM信号を使い、そのパルス幅(HIGHの時間)で角度を指定します。一般的な目安は「500us〜2500usで0〜180度」です。
まずは動かしてみる(サンプルコード)
from machine import Pin, PWM
import time
# GP15にMG996Rの信号線(オレンジ)を接続している前提
servo = PWM(Pin(15))
servo.freq(50) # サーボ制御の標準周波数: 50Hz
def set_angle(angle):
# 0〜180度をパルス幅500us〜2500usにマッピング
min_us = 500
max_us = 2500
pulse_width_us = min_us + (angle / 180) * (max_us - min_us)
servo.duty_ns(int(pulse_width_us * 1000))
while True:
set_angle(0)
time.sleep(1)
set_angle(90)
time.sleep(1)
set_angle(180)
time.sleep(1)対応ファイル: servo-moter-test001.py
0°→90°→180°で動かした様子
✓ 動作確認方法
- Thonnyでコードを開き、F5キーで実行する
- MG996Rが0度→90度→180度の順に往復動作すれば成功
実測したら公称180度のはずが198度まで回転した
sleep不足で「動いている途中」を最終角度と誤認していた
実は最初、set_angle(180)を実行した際に「180度のはずが実機は120度くらいしか動いていない」ように見えて混乱しました。原因を探るため、パルス幅を直接指定して3秒間静止させて確認するテストコードを作ったところ、同じus値でも今度は約190度動いていることが判明。これは、MG996Rの移動速度(0.17秒/60度程度)に対してtime.sleep(0.5)が短すぎて、まだ回転している「移動途中」の角度を最終角度と誤認していたことが原因でした。sleep時間を1〜3秒に伸ばすことで、正確な最終角度を確認できるようになりました。
実測キャリブレーション:公称180度のはずが実測198度だった
パルス幅を手動で指定して実機の角度を確認するテストコードです。
from machine import Pin, PWM
import time
servo = PWM(Pin(15))
servo.freq(50)
def set_pulse_us(us):
servo.duty_ns(int(us * 1000))
test_values = [500, 1500, 2500]
for us in test_values:
print(f"パルス幅 {us}us に設定します。実機の角度を確認してください。")
set_pulse_us(us)
time.sleep(3)
print("テスト終了")対応ファイル: servo-moter-test003.py
自作の簡易分度器(A4印刷)をサーボに当てて実測したところ、以下の結果になりました。
| us | 実測角度 |
|---|---|
| 500 | 0°(基準) |
| 1500 | 97° |
| 2500 | 198° |
想定していた「500〜2500usで0〜180度」という前提に対し、実際は「500〜2500usで0〜198度」動いてしまう個体でした。安価なクローン品では、この手の個体差がよくあります。
サーボホーンが狙った角度に物理的に取り付けられない問題
さらに、サーボホーン(アーム)はスプライン(ギザギザの溝)構造になっているため、任意の角度では取り付けられません。今回、狙った0度の位置には取り付けられず、実測で約-7度の位置にしか固定できませんでした(上のus=500の写真はこの状態です)。
これは物理的に付け直すのではなく、プログラム側にオフセット値を持たせて吸収します。
HORN_OFFSET_DEG = 7
def set_angle(angle):
corrected_angle = angle + HORN_OFFSET_DEG
pulse_width_us = MIN_US + (corrected_angle / 180) * (MAX_US - MIN_US)
servo.duty_ns(int(pulse_width_us * 1000))下限側はus=470でストールした:上限と下限で余裕が全然違った
補正の方向を検討する際、逆方向(HORN_OFFSET_DEG = -7)も試したところ、サーボが唸ってストールしてしまいました。下限側を細かく調べたところ、us=470で唸り始めることが判明。一方、上限側はus=2500(198度相当)まで唸らずに動いていたので、上限には約18度分の余裕があったのに対し、下限にはわずか3度分程度の余裕しかない、という非対称な結果でした。この結果、ホーンのズレ補正には安全な上限方向(+7)を採用し、ホーン自体もマイナス側に寄せて物理的に取り付けることにしました。
キャリブレーション後の実測確認
最終的なキャリブレーション値(MIN_US=500, MAX_US=2340, HORN_OFFSET_DEG=7)で、以下の値を実測しました。
| 指定 | us値 | 実測角度 |
|---|---|---|
| 0度 | 571 | 意図した0度付近 |
| 90度 | 1491 | 意図した90度付近 |
| 180度 | 2411 | 意図した180度付近 |
タクトスイッチ2個で「Aで90度サイクル」「Bで180度サイクル」を作る
タクトスイッチA(GP14)を押すと0度→90度→0度、タクトスイッチB(GP13)を押すと0度→180度→0度のサイクルで動作し、今何が行われているかをThonnyのコンソールにprint表示するプログラムです。
from machine import Pin, PWM
import time
# ===== サーボ設定 =====
servo = PWM(Pin(15))
servo.freq(50)
# 実測キャリブレーション値(us=500→0°, us=2340→180°)
MIN_US = 500
MAX_US = 2340
# サーボホーンの取付角度のズレ補正
HORN_OFFSET_DEG = 7
def set_angle(angle):
corrected_angle = angle + HORN_OFFSET_DEG
pulse_width_us = MIN_US + (corrected_angle / 180) * (MAX_US - MIN_US)
servo.duty_ns(int(pulse_width_us * 1000))
# ===== タクトスイッチ設定 =====
# NOタイプ、内部プルアップ使用(押下でLOWになる)
button_a = Pin(14, Pin.IN, Pin.PULL_UP)
button_b = Pin(13, Pin.IN, Pin.PULL_UP)
def run_cycle_a():
print("スイッチA押下: 0度 → 90度 → 0度 のサイクルを開始します")
print(" 0度に移動")
set_angle(0)
time.sleep(1)
print(" 90度に移動")
set_angle(90)
time.sleep(1)
print(" 0度に戻す")
set_angle(0)
time.sleep(1)
print("サイクル完了")
def run_cycle_b():
print("スイッチB押下: 0度 → 180度 → 0度 のサイクルを開始します")
print(" 0度に移動")
set_angle(0)
time.sleep(1)
print(" 180度に移動")
set_angle(180)
time.sleep(1)
print(" 0度に戻す")
set_angle(0)
time.sleep(1)
print("サイクル完了")
def wait_release(button):
while button.value() == 0:
time.sleep(0.05)
# ===== メイン処理 =====
set_angle(0) # 初期位置
print("準備完了。スイッチAまたはBを押してください。")
while True:
if button_a.value() == 0:
run_cycle_a()
wait_release(button_a)
time.sleep(0.2) # チャタリング対策の簡易ウェイト
elif button_b.value() == 0:
run_cycle_b()
wait_release(button_b)
time.sleep(0.2)対応ファイル: servo-moter-test005.py
✓ 動作確認方法
- タクトスイッチA・Bをそれぞれ押して、意図したサイクル(90度/180度)で動作するか確認する
- コンソールに動作内容(現在の移動先)が表示されるか確認する
- ボタンを押しっぱなしにしても、1回の押下につき1サイクルだけ実行され、最後は必ず0度に戻ることを確認する
ボタン押しっぱなしで0度に戻らなくなった話
このコードには当初wait_release()が無く、ボタンを押しっぱなしにすると「サイクル終了直後に即座に次のサイクルが再トリガーされる」という不具合がありました。0度に戻った瞬間にまた次のサイクルが始まってしまうため、見た目には「0度で止まってくれない」ように見えていたのです。ボタンが離されるまで次のトリガーを待つwait_release()を追加することで解決しました。
Q&A(よくある質問)
QMG996Rの動作電圧は何Vが正しいですか?5Vでも動きますか?
A資料によって上限が違い、Tower Pro公式サイトでは4.8〜6.6V、よく出回っているPDF版データシートでは4.8〜7.2Vとされています。安全側に見て6V前後までで使うのがおすすめです。今回は手持ちのUSB電源(5V)で問題なく動作しましたが、6V駆動時と比べるとトルク・スピードはやや控えめになります。より力強く動かしたい場合は、単3電池4本(6V)などの電源を検討してください。
QMG996Rのトルクはどれくらいですか?
Aストールトルクは4.8V時に9.4kgf·cm、6V時に11kgf·cmです(Tower Pro公式サイト・PDF版データシートとも同じ値)。軸から1cmの位置で約9.4〜11kgの力に相当し、ホビー用サーボとしては高トルクな部類です。
QMG996Rの消費電流は?電源は何A必要ですか?
A公式サイトでは待機10mA・無負荷170mA・ストール1,400mA、PDF版データシートでは動作時500〜900mA・ストール2.5A(6V時)とされています。電源はストール電流の大きいほう(2.5A)を目安に、台数分を足して選ぶと安全です。今回は1台を5V/2AのUSB ACアダプタで動かしました。
Qなぜコード上の「500〜2500usで0〜180度」という想定通りに動かなかったのですか?
A安価なクローン品はメーカー・ロットによって個体差が大きく、パルス幅と実際の回転角度の対応関係(特に可動域の上限・下限)が公称値からズレていることがよくあります。分度器などで実測し、コード側の数値を補正するのが確実です。
Qサーボホーンが狙った角度に取り付けられないときはどうすればいいですか?
Aホーンはスプライン(ギザギザの溝)構造のため、取り付けられる角度が離散的です。無理に物理的な取り付け角度を追い込むより、コード側にオフセット値を持たせて電気的に補正する方が実用的です。
Qパルス幅の安全な上限・下限はどうやって調べればいいですか?
A少しずつパルス幅を広げる/狭めるテストコードを実行し、サーボが唸り始める・振動し始める一歩手前を安全マージンとして採用します。今回の個体では上限に約18度分の余裕があった一方、下限はわずか3度分程度しか余裕がなく、上下で大きな非対称性がありました。
Qタクトスイッチを押しっぱなしにすると動作がおかしくなるのはなぜですか?
Aボタンの状態を単純にポーリングするだけのコードだと、押しっぱなしの間ずっと条件が真になり続け、サイクル終了直後に即座に再トリガーされてしまいます。ボタンが離されるまで待つ処理を入れることで、1回の押下につき1サイクルだけ実行されるようになります。
QPico側とサーボ側のGNDを共通化しないとどうなりますか?
APico側とサーボ側で電気的な基準電位がズレてしまい、PWM信号が正しく認識されず誤動作します。電源配線の際は、サーボのGND線をPicoのGNDピンにも必ず接続してください。
まとめ
MG996RとRaspberry Pi Pico 2WをPWM制御でつなぎ、タクトスイッチで角度サイクルを切り替えるところまで作りました。単に「動かしてみた」だけでなく、実測してみて初めて分かる個体差やクセ(可動域のズレ、ホーンの取付角度、上限下限の非対称性、ボタン制御のバグ)まで含めて紹介しました。
EXPLORE PY-VBALAB
まとめから探す
気になるテーマがあれば、こちらのまとめページから関連記事を一覧でチェックできます。
じっくり読める完結シリーズシリーズ完結
Follow me on X
本ブログの中の人「ニンジン🥕」はXやってます。新着記事はいち早くポストでお知らせしているので、見逃したくない方は @ninjin_py_vba をフォローしてみてください🥕
@ninjin_py_vba をフォローシリーズ記事一覧
電子工作・自動化ツールの製作記録をシリーズごとにまとめています。気になるテーマからお読みください。
-
FILE.01 — IoT
Raspberry Pi Pico W × GASラズパイPico W×GASで自作!「LINEで動く温湿度&スマートリモコン」完全ロードマップ
「外出中のペットの室温が心配…」そんな思いからスタートしたIoT自作プロジェクトの総集編。温湿度監視からエアコン遠隔操作まで、仕組みをゼロから作りたい方のための全5回の開発記録です。
シリーズを読む -
FILE.02 — CNC
Arduino × レーザー刻印CNCCNC自作シリーズ
高精度な加工を目指し、本格的な自作CNC製作に挑戦中。現在は基幹パーツであるオリエンタルモーターの納品を待つ「設計・準備編」を公開。ハードとソフトの両面から、理想のマシンを形にする過程をリアルタイムにお届けします。
シリーズを読む -
FILE.03 — XY軸制御
Raspberry Pi × ステッピングモーターXYテーブルシリーズ
Raspberry Piとステッパモーターを使い、ゼロから2軸制御に挑む記録。OS設定から回路設計、多軸制御のPythonコードまで、躓きやすいポイントを徹底図解。電子工作初心者が「動く感動」を味わうための実戦ガイドです。
シリーズを読む -
FILE.04 — 業務自動化
Python × Excel × CustomTkinter【Python開発記】NINJIN Mail制作記
PythonとExcelを連携させ、実務で即戦力となるメール送信ツールを開発。CustomTkinterによるUI構築や、ミスを防ぐテンプレート活用術など、現場の「痒い所に手が届く」自動化ノウハウを細部まで丁寧に解説します。
シリーズを読む -
FILE.05 — 業務自動化
Python × Excel × GUIアプリ化Invoice Maker(請求書・見積書自動作成)
手作業の請求書作成から卒業!PythonでExcelデータを読み込み一括PDF化する「Invoice_Maker」の作り方を全3回で解説。基本ロジックからGUIアプリ化まで、実務で役立つ自動化ノウハウが満載です。
シリーズを読む



コメント