Polivia Bot Python Raspberry Pi Pico プログラム ロボット制御 電子工作 高専3年生

【Poliviabot】【Raspberry Pi Pico】ロボット制御入門⑤PWMとモータ制御【Python】

本サイトでは様々な環境で気軽に本格的なロボット教育を行ってもらうべく、独自のロボット教材を開発しました。

現在、一般販売へ向けて準備をしております。

本Webページではβ版の先行体験をしていただく方向けに公開している資料になっております。

一般の方は販売をお待ちください。

前回はこちらです

1 この章の目的

この章では、PoliviaBot UMEのモータを使って、

  • モータはなぜ回るのか
  • モータドライバ回路の役割とは何か
  • デジタル出力(ON/OFF)だけでは速さが変えられない理由
  • PWM(Pulse Width Modulation:パルス幅変調)とは何か
  • PythonでPWMを使ってモータの回転速度を制御する方法

を学びます。

最終的に、

「速い・遅い」を数字で指定できる
ようになることがゴールです。

2 モータはなぜ回るのか

DCモータは、とてもシンプルな仕組みです。

  • 電気が流れる
    → 磁力が発生する
    → 中のコイルが回ろうとする
    → 軸が回る

重要なのは、電気が流れなければ、回らない
という点です。

3 マイコンとモータの「力の違い」

Raspberry Pi Pico WのGPIOピンは、

  • 電圧:3.3V
  • 流せる電流:とても小さい

一方、モータは、

  • 大きな電流が必要
  • 回り始めるときに特に電流を食う

つまり、GPIOから直接モータをつなぐと、壊れるか、動かない
という問題が起きます。

そこで間に入るのが、モータドライバ回路です。

本ロボットではSS8837という小さなICを使っています。

どこについているか探してみてください。

https://www.lcsc.com/product-image/C17179517.html

4 モータドライバの役割

モータドライバは、マイコンの小さな信号で、モータ用の大きな電流をコントロールする
ための回路です。

イメージは、

  • マイコン →「指示役」
  • モータドライバ →「力仕事担当」
  • モータ →「実際に回る」

という関係です。

5 PoliviaBot UMEの回路構成

PoliviaBot UMEでは、左右のモータをHブリッジ構成で制御しています。

ピン配置(復習)

  • 左モータ
    • Motor_L+:GP0
    • Motor_L-:GP1
  • 右モータ
    • Motor_R+:GP2
    • Motor_R-:GP3

電気の流れの考え方

モータには「+側」と「−側」があります。
どちらに電圧をかけるかで、回る向きが変わります。

6 正転・逆転・停止の論理

左モータを例にします。

GP0(L+)GP1(L-)状態
10正転
01逆転
00停止

※ 1 = HIGH(約3.3V)、0 = LOW(0V)

この考え方は、右モータ(GP2/GP3)も同じです。

モータを回してみる(正転)

左右のモータを前に進む方向に回します。

from machine import Pin
import time

L_P = Pin(0, Pin.OUT)
L_M = Pin(1, Pin.OUT)
R_P = Pin(2, Pin.OUT)
R_M = Pin(3, Pin.OUT)

# 正転
L_P.value(1)
L_M.value(0)
R_P.value(1)
R_M.value(0)

time.sleep(3)

# 停止
L_P.value(0)
L_M.value(0)
R_P.value(0)
R_M.value(0)

3秒で停止すれば成功です。

課題 5

前進 → 停止 → 後退 → 停止
を、2秒ずつ繰り返すプログラムを作ってください。

解答例はこちら
from machine import Pin
import time

# モータピン
L_P = Pin(0, Pin.OUT)  # Motor_L+
L_M = Pin(1, Pin.OUT)  # Motor_L-
R_P = Pin(2, Pin.OUT)  # Motor_R+
R_M = Pin(3, Pin.OUT)  # Motor_R-

def stop():
    L_P.value(0); L_M.value(0)
    R_P.value(0); R_M.value(0)

def forward():
    L_P.value(1); L_M.value(0)
    R_P.value(1); R_M.value(0)

def reverse():
    L_P.value(0); L_M.value(1)
    R_P.value(0); R_M.value(1)

while True:
    forward()          # 前進
    time.sleep(2)

    stop()            # 停止
    time.sleep(2)

    reverse()         # 後退
    time.sleep(2)

    stop()            # 停止
    time.sleep(2)

課題 6

スイッチ(SW1:GP13)を押している間だけ前進するように、
④章のif文を組み合わせて制御してください。

解答例はこちら
from machine import Pin
import time

# モータ
L_P = Pin(0, Pin.OUT)
L_M = Pin(1, Pin.OUT)
R_P = Pin(2, Pin.OUT)
R_M = Pin(3, Pin.OUT)

# スイッチ
SW1 = Pin(13, Pin.IN)  # プルダウン方式(押すと1)

DEBOUNCE_MS = 20
last_read = SW1.value()
stable = last_read
last_change = time.ticks_ms()

def stop():
    L_P.value(0); L_M.value(0)
    R_P.value(0); R_M.value(0)

def forward():
    L_P.value(1); L_M.value(0)
    R_P.value(1); R_M.value(0)

# 初期状態
stop()

while True:
    now = time.ticks_ms()
    reading = SW1.value()

    # 変化を検出
    if reading != last_read:
        last_read = reading
        last_change = now

    # 一定時間同じなら確定
    if time.ticks_diff(now, last_change) >= DEBOUNCE_MS:
        if stable != last_read:
            stable = last_read

            if stable == 1:
                forward()   # 押されたら前進
                print("FORWARD")
            else:
                stop()      # 離したら停止
                print("STOP")

    time.sleep_ms(1)

7 PWMの考え方

ここまでの制御では、モータは

  • 回る(全力)
  • 止まる

の2つの状態しかありませんでした。

しかし、実際のロボットでは次のような動きが必要になります。

  • ゆっくり発進する
  • カーブでは片側だけ少し遅くする
  • なめらかに止まる

このように、「回る強さ」を連続的に変える方法が必要になります。
そのために使うのが PWM です。

PWM(Pulse Width Modulation)は、
電圧をなめらかに変えるのではなく、

とても速く「ON」と「OFF」を切り替えて、
その平均の強さでモータを動かす方法

です。

イメージ

  • ONの時間が短い → 弱い → 遅い
  • ONの時間が長い → 強い → 速い

Raspberry Pi Pico W(MicroPython)では、PWMの強さを
0〜65535 の数値で指定します。

数値意味動き
00%停止
約30000中くらい中速
65535100%全速

この数字を変えるだけで、ロボットの動きが変わります。

8 PWMの実装

PWMピンを作る

まず、モータの「+側」をPWM出力にします。
ここでは左モータ(GP0)から始めます。

from machine import Pin, PWM

L_P = PWM(Pin(0))     # Motor_L+ をPWMにする
L_M = Pin(1, Pin.OUT)  # Motor_L- は通常の出力

周波数の設定

L_P.freq(1000)  # 1kHz(扱いやすい設定)

左モータをPWMで回してみる

正転方向に、ゆっくり回すテストです。

from machine import Pin, PWM
import time

L_P = PWM(Pin(0))
L_M = Pin(1, Pin.OUT)

L_P.freq(1000)

# 正転
L_M.value(0)
L_P.duty_u16(15000)   # ゆっくり回る

time.sleep(3)

# 停止
L_P.duty_u16(0)
L_M.value(0)

観察

  • 1500030000 に変えるとどうなるか
  • 5000 にすると回り方はどう変わるか

両モータを同じ速さで前進させる

左右のモータを同じPWM値で制御します。

from machine import Pin, PWM
import time

# 左モータ
L_P = PWM(Pin(0))
L_M = Pin(1, Pin.OUT)

# 右モータ
R_P = PWM(Pin(2))
R_M = Pin(3, Pin.OUT)

L_P.freq(1000)
R_P.freq(1000)

def forward(speed):
    # speed: 0〜65535
    L_M.value(0)
    R_M.value(0)
    L_P.duty_u16(speed)
    R_P.duty_u16(speed)

def stop():
    L_P.duty_u16(0)
    R_P.duty_u16(0)
    L_M.value(0)
    R_M.value(0)

# テスト
forward(10000)
time.sleep(2)

forward(30000)
time.sleep(2)

forward(60000)
time.sleep(2)

stop()

課題 7

forward(speed) を使って、
1秒ごとに speed が 10000 → 20000 → 30000 → 40000 → 50000 → 0
と変化するプログラムを作ってください。

解答例はこちら
from machine import Pin, PWM
import time

# 左モータ
L_P = PWM(Pin(0))
L_M = Pin(1, Pin.OUT)

# 右モータ
R_P = PWM(Pin(2))
R_M = Pin(3, Pin.OUT)

L_P.freq(1000)
R_P.freq(1000)

def forward(speed):
    # 正転:+側PWM、-側LOW
    L_M.value(0)
    R_M.value(0)
    L_P.duty_u16(speed)
    R_P.duty_u16(speed)

def stop():
    L_P.duty_u16(0)
    R_P.duty_u16(0)
    L_M.value(0)
    R_M.value(0)

pattern = [10000, 20000, 30000, 40000, 50000, 0]

while True:
    for sp in pattern:
        if sp == 0:
            stop()
            print("STOP")
        else:
            forward(sp)
            print("SPEED:", sp)
        time.sleep(1)

課題 8

SW1(GP13)で「加速」、SW2(GP14)で「減速」できるようにしてください。

  • 押すたびに ±5000
  • 0〜65535の範囲を超えないようにする
解答例はこちら
from machine import Pin, PWM
import time

# モータ
L_P = PWM(Pin(0))
L_M = Pin(1, Pin.OUT)
R_P = PWM(Pin(2))
R_M = Pin(3, Pin.OUT)

L_P.freq(1000)
R_P.freq(1000)

def forward(speed):
    L_M.value(0)
    R_M.value(0)
    L_P.duty_u16(speed)
    R_P.duty_u16(speed)

# スイッチ(プルダウン:押すと1)
SW1 = Pin(13, Pin.IN)  # 加速
SW2 = Pin(14, Pin.IN)  # 減速

speed = 0
STEP = 5000

last1 = 0
last2 = 0

forward(speed)

while True:
    now1 = SW1.value()
    now2 = SW2.value()

    # SW1 押した瞬間(0→1)
    if last1 == 0 and now1 == 1:
        speed += STEP
        if speed > 65535:
            speed = 65535
        forward(speed)
        print("UP   speed =", speed)
        time.sleep(0.25)  # 簡易デバウンス

    # SW2 押した瞬間(0→1)
    if last2 == 0 and now2 == 1:
        speed -= STEP
        if speed < 0:
            speed = 0
        forward(speed)
        print("DOWN speed =", speed)
        time.sleep(0.25)  # 簡易デバウンス

    last1 = now1
    last2 = now2
    time.sleep_ms(5)

次回はこちら

Follow me!

-Polivia Bot, Python, Raspberry Pi Pico, プログラム, ロボット制御, 電子工作, 高専3年生
-, , , , ,

PAGE TOP