ラズPicoでサーボモーター(SG90)を動かそう〜その3〜 プログラムを、さらにかっこよくしよう

2024年2月2日(金)は、7割ほどの出席率でした。今回は、ひきつづきサーボモーター(SG90)を動かすプログラムを、さらにかっこよくします。

  • 18時00分~18時25分 タイピング練習
  • 18時25分~19時50分 サーボモーターのプログラミング
  • 19時50分~20時00分 あとかたづけ

タイピング練習

今回のタイピングの練習は、英語入力です。使う教材は、下のサイトのWebアプリです。

https://manabi.benesse.ne.jp/gakushu/typing/

プログラムコードは、ほとんど英語です。したがって、日本語入力だけでなく、英語入力を練習しておくとコーディングがスムーズにできます。日本語入力と違う点は、「日本語で使わないキーを入力すること」「単語の区切りは、かならずスペースであること」です。

これも、正しい練習をすることで、確実にタイピングスキルが身についていきます。

サーボモーター(SG90)のプログラムをかっこよくする

前回、サーボモーター(SG90)を動かして、さらに繰り返しを少しだけはぶくプログラムを書いてみました。

ラズPicoでサーボモーター(SG90)を動かそう〜その2〜 プログラムをかっこよくしよう

2024年1月26日(金)は、6割ほどの出席率でした。今回も、サーボモーター(SG90)を動かすプログラミングに挑戦します。 Contents1 タイピング練習2 サーボモーター(SG90…

今回は、さらにコードをかっこよくします。

繰り返しはforでまとめる

プログラムを学習するときに、おそらくごく初期にまなぶのが、繰り返しを制御する文法です。具体的には、while文、for文を学習します。

前回のservo2.pyで、最後の方に「rotmotor」の繰り返しがあるのが、かっこ悪いと説明しました。ここが学習の成果のみせどころです。繰り返しをfor文でまとめます。下のコードのハイライト箇所が変更箇所です。

from machine import Pin, PWM
import time

SERVO_PIN = 16
PWM_FREQ = 50

def pulse_width(val, freq = PWM_FREQ, resol = 65535):
    pulse = freq * val * 1e-6 * resol
    return int(pulse)

def rotmotor(pulse):
    duty = pulse_width(pulse)
    servo.duty_u16(duty)
    time.sleep(1)

servo = PWM(Pin(SERVO_PIN))
servo.freq(PWM_FREQ)

motion = [1500, 500, 1500, 2500]

while True:
    for i in range(4):
        rotmotor(motion[i])

「rotmotor」に直接数値をわたしてプログラムを書いていたのを、数値をリストにまとめて(19行)、for文でひとつずつ読み出す(22〜23行)ようにしました。

この改修により、同じような記述を繰り返すことがなくなりました。それに、サーボモーターの動きを細かく制御したくなったら、19行の「motion」のリストに数値を入力し、22行の「range」のカッコ内の数字を変更するだけです。コピペを繰り返す必要がなくなりました。

コピペを繰り返すようなコードを書くと、なぜ悪いのでしょうか。かっこ悪いだけではありません。実は、コード全体の見通しが悪くなり、ミスしやすくなります。さらに、変更も難しくなります。コードは短く、簡潔に書きましょう。

サーボモーターの動きを角度で指定する

ここまでプログラムを書いてきて、「なぜサーボモーターの角度を1500とか500とか、バカでかい数値で指示するのだろう?」と思ったのではないでしょうか。実際にサーボモーターが動く角度は、「-90〜0〜90度」の180度です。ならば、「-90〜0〜90」の数値でプログラムできたほうが、わかりやすいと思いませんか?

そのご期待にそって書き換えたプログラムが、下記のservo4.pyです。

from machine import Pin, PWM
import time

SERVO_PIN = 16
PWM_FREQ = 50

def pulse_width(val, freq = PWM_FREQ, resol = 65535):
    pulse = freq * val * 1e-6 * resol
    return int(pulse)

def rotmotor(pulse):
    duty = pulse_width(pulse)
    servo.duty_u16(duty)
    time.sleep(1)

def cnvPulse(degrees):
    if degrees < -90:
         degrees = -90
    if degrees > 90:
         degrees = 90
    return -degrees*1000/90+1500

servo = PWM(Pin(SERVO_PIN))
servo.freq(PWM_FREQ)

degrees = [0, 90, 0, -90]

while True:
    for deg in degrees:
        pulse = cnvPulse(deg)
        rotmotor(pulse)

まず、関数cnvPulseを作りました(16〜22行)。これが、角度をパルス幅に変換する関数です。サーボモーターは角度ではなく、パルス幅を指定しないと動きません。そこで、直感的にわかる角度をパルス幅に変換する関数を作りました。

なお、変換するだけなら「return -degrees*1000/90+1500」(21行)だけでもOKです。下の17〜20行は、サーボモーター(SG90)が「-90〜90度」の範囲でしか動かないので、範囲を超えた数値をはじく処理です。

    if degrees < -90:
         degrees = -90
    if degrees > 90:
         degrees = 90

SG90には「-90〜90度」しか指定できないと知っていても、人間は必ず間違えます。そこで、あらかじめ予測できるミスを防ぐ工夫が大切です。コーディングミスをしても、「動かない!」を防ぐことができます。

さて、もう一つの工夫は「for deg in degrees:」(29行)です。サーボモーターを制御する回数が増えたとき(degreesの要素が増えたとき)、servo3.pyだと数字を毎度書き換える必要がありました。しかし、servo4.pyの書き方だと、そもそも要素数を示す数値が書かれていません。「リストdegreesの中の要素を一つずつすべて読み込む」という記述なので、要素数を示す必要がないのです。

したがって、degreesリストを下のように細かく設定しても、変更箇所はこの行だけすむのです。

degrees = [0, 90, 0, -90, 30,-50, 3, -90, 46, 49, 0, -32]

変更する箇所が減るということは、「ミスをする可能性自体が減る」ということです。プログラムを組む場合は、常にあとから変更することを考えながら組むと、良いプログラムが書けるようになります。

今回でサーボモーター(SG90)のプログラムは一区切りです。次回からは、超音波距離センサーを使ったプログラムに挑戦します。