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

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

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

タイピング練習

今回のタイピングの練習も、日本語入力にしました。使う教材は、下のサイトのWebアプリです。

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

今回から、正タイプ率95%以上で、今までの自分の最高値(1分あたりの入力字数)を超えた字数がもっとも多いメンバーに賞品をわたすことにしました。このルールだと、あまり多く字数を入力できないメンバーでも、商品をもらうチャンスがあります

敵は過去の自分です。真面目にがんばれば、いつでもチャンスがあります

そして、ぼちぼち100字/分を超えるメンバーが何人も出てきました。すばらしい上達ぶりです。わたしはもう大きく伸びることはないので、越されるのを待つだけです。

サーボモーター(SG90)のプログラムでわざとエラーをだしてみる

サーボモーター(SG90)を動かすプログラムは、下に示す通りみじかいコードです。メンバーのほとんどが、前回のうちに入力し終わってしまいました。そこで、今回はわざとプログラムを間違えて、どのようなエラーが出るか確かめてみます

プログラムを下に再掲します。出典はいつものとおり、『ラズパイPico完全ガイド』(日経BP社 福田和宏著)です。

from machine import Pin, PWM
import time

#プログラムの中の設定値
SERVO_PIN = 16
PWM_FREQ = 50

#デューティー比を求める関数
def pulse_witdh(val, freq=PWM_FREQ, resol=65535):
    pulse = freq * val * 1e-6 * resol
    return int(pulse)

#サーボモーターの実体化
servo = PWM(Pin(SERVO_PIN))
servo.freq(PWM_FREQ)

while True:
    #回転角0度
    duty = pulse_width(1500)
    servo.duty_u16(duty)
    time.sleep(1)

    #回転角右90度
    duty = pulse_width(500)
    servo.duty_u16(duty)
    time.sleep(1)

    #回転角0度
    duty = pulse_width(1500)
    servo.duty_u16(duty)
    time.sleep(1)

    #回転角左90度
    duty = pulse_width(2500)
    servo.duty_u16(duty)
    time.sleep(1)

変数名を間違えるとNameErrorがでる

5行目の「SERVO_PIN」を「SERV0_PIN」と、わざと誤入力してみます。「O(オー)」ではなく「0(ゼロ)」入力しました。

すると「NameError」がでて、「14行目がおかしい」とメッセージがでます。14行目ではじめて「SERVO_PIN」が出現したのですが、この行より前に定義されていません。定義されていない変数の値を使おうと思っても、使えないのです。

今度は逆に、14行目の「SERVO_PIN」を「SERV0_PIN」と誤入力します。5行目は「SERVO_PIN」と正しく入力します。

これで実行すると、やはり14行目で止まって「NameError」がでます。でも今度は、間違っているのは14行目です。

このように、NameErrorが出力された場合は、間違いの場所が違うことがあります。もう少し学習を進めると、「なぜ」が解決します。まずは、NameErrorが何たるかを知ってください。

「:」を忘れてSyntaxError

次は、17行目の「while True:」の最後のコロンをわざと消してみます。

すると、「SyntaxError」が18行目にあるとメッセージが出ます。Syntaxとは「文法」のことです。プログラムを書く決まり事に従っていないと教えてくれているのです。

コロン( : )の入力忘れは、今まで何度となく見てきました。わたしも、たまに忘れます。でも、実行したらすぐにエラーで教えてくれます。

一点注意があります。コロンを忘れたのは18行目ですが、エラーは19行目にあるとメッセージが出ます。19行目をいくら探しても間違いは見つかりません。

SyntaxErrorに限ったことではありませんが、メッセージで指摘された行だけでなく、その上の行も数行にわたって確認しましょう。そうすれば間違った箇所を見つけやすくなります。

大文字小文字を間違えてImportError

1行目の「from machine import Pin, PWM」を「from machine import Pin, pwm」と書き直してみましょう。「pwm」というように、本来なら大文字で書くべきところを小文字で書いてみます。

この変更をしてプログラムを実行すると「ImportError」がでます。

Importするのは、あらかじめ決められた名前のモジュールです。「PWM」というモジュールが用意されているのであり、「pwm」というモジュールは存在しません。存在しないモジュールはImportできないので、Errorがでます

以上、よくあるErrorをわざと表示させてみました。これで自分でエラーを解決できるようになるかな?

SG90を動かすプログラムをスマートにする

先ほど再掲したプログラムコードは、あまり美しくありません。18~36行目が4回ほどおなじようなコードが並んでいるのがわかるでしょうか。手入力するならば、18~21行目までを入力して、それを3回ほどコピペしたかもしれません。

著者がこのようなコードを公開しているのは、コードの美しさよりも理解しやすさを優先しているからだと思います。現実にservo.pyようなコードを書くとださいのです。

繰り返しがあるプログラムは、修正のときにおなじような修正を繰り返さなければなりません。プログラム全体の見通しも悪くなり、修正ミスや修正忘れがおきやすくなります。要は、手間ばかりかかるコードということです。これを、「ださい」「美しくない」と表現しているのです

具体的にどのように修正するか、考えてみましょう。実は、すでに何度も使っている「関数」を使って繰り返しを排除します。下のようにプログラムを修正します。

from machine import Pin, PWM
import time

#プログラムの中の設定値
SERVO_PIN = 16
PWM_FREQ = 50

#デューティー比を求める関数
def pulse_witdh(val, freq=PWM_FREQ, resol=65535):
    pulse = freq * val * 1e-6 * resol
    return int(pulse)

#サーボモータの軸を動かす関数
def rotmotor(pulse):
    duty = pulse_width(pluse)
    servo.duty_u16(duty)
    time.sleep(1)

#サーボモーターの実体化
servo = PWM(Pin(SERVO_PIN))
servo.freq(PWM_FREQ)

while True:
    #回転角0度
    rotmotor(1500)

    #回転角右90度
    rotmotor(500)

    #回転角0度
    rotmotor(1500)

    #回転角左90度
    rotmotor(2500)

servo2.pyのように、関数を使って13~17行と24~34行を書き換えます。「rotmotor」は自分の好きな関数名に書き換えて問題ありません。最初に示したservo.pyより繰り返しの行数が減ったのがわかると思います。

今回はここで時間切れです。見て分かるとおり、servo2.pyも24~34行でまだ繰り返しがあります。これも「ださい」ので、次回はもっとスマートにコードを変更します