Raspberry PiとChatGPTでつくるボイス・アシスタント・ロボット #4
ChatGPT実験ロボット "Voice Assistant Robot_GPT" がサーボモータを制御する様子
サーボモーター制御
実験ロボットのサンプルコード bot_motor_controller.py
ファイルで実行します。内容の説明にあたりり、一部抜粋した内容で解説します。
Pan-Tilt HAT
サーボモーター制御には、使いやすい拡張基板を使用します。ここではPimoroni社のPan-Tilt HATを選択しました。メーカーが提供するPythonライブラリにより、簡潔な記述で本格的な制御を行うことが出来ます。
この拡張基板は2つのサーボモーターの制御に加え、最大で24個までLEDを制御することが出来ます。また、NeoPixel Stick-8 というカラーLEDを取付けることも推奨しており、パンチルトユニットの上部に取付けることが出来ます。
仕組み
Pythonライブラリにはサンプルコード含まれており、それらを参考に手軽に制御することが出来ます。詳細なドキュメントはこちら documentation Pan Tilt HAT python library で確認できます。
サーボモーターの移動は、-90度から90度までの範囲で角度を指定することで制御できますが、速度制御は用意されていません。そのため独自に速度制御を実装しました。
サンプルプログラム
このサンプルでは、単純に移動角度の100分の1づつ100回繰り返すループ処理を行っています。ループ間にtime.sleep()
を使用して間隔を空け、移動速度を調整しています。
import time # ---(※1)
import pantilthat
# パンチルトモーター制御 ーpan tilt とも -90 ~ 90 ---(※2)
def pan_tilt(pan, tilt):
pantilthat.pan(pan)
pantilthat.tilt(tilt)
# マイクロサーボSG90では60°動かすのに0.1秒
# パンチルトモーターをゆっくり動かす speed=10 前後で調整
def pan_tilt_slow(pan, tilt, speed):
start_pan = pantilthat.get_pan() # ---(※3)
start_tilt = pantilthat.get_tilt()
print("start_pan: ",start_pan, " start_tilt: ",start_tilt)
move_pan = (pan - start_pan) / 100 # ---(※4)
move_tilt = (tilt - start_tilt) / 100
print("move_pan: ", move_pan, " move_tilt: ", move_tilt)
cnt = 0 # ---(※5)
while True:
pan_tilt(start_pan + move_pan*cnt, start_tilt + move_tilt*cnt) # ---(※6)
cnt += 1
if cnt == 100: # ---(※7)
break
else:
pass
time.sleep(0.001 * speed) # ---(※8)
if __name__ == "__main__":
pan_tilt(0, -90)
time.sleep(0.5)
pan_tilt(0, 0)
time.sleep(0.5)
pan_tilt_slow(0,-90, 10)
pan_tilt_slow(0,0,10)
time
モジュールとpantilthat
モジュールをインポートし(※1)、水平角度と垂直角度の代入により移動するように、関数を定義します(※2)。
pantilthat
モジュールは移動角度の指定だけでなく、現在の角度を取得することも出来ます(※3)。終了角度から開始角度の差を100で割り(※4)1回の移動角度を計算します。そして、ループ処理ごとにカウントを1つづつ増やし(※5)、移動角度を設定します(※6)。カウントが100に達したらループを終了します(※7)。
ループごとの間隔を(※8)で設定します。これは実際の動作に合わせて調整可能です。
実行結果は以下のようになります。
LED制御
サーボモーター制御と同様に、サンプルプログラム bot_motor_controller.py
から抜粋し、LED制御の説明をします。
adafruit NeoPixel Stick-8
LED制御は、Pan-Tilt HATに推奨されているAdafruit社のNeoPixel Stick-8 というカラーLEDを使用します。
仕組み
LEDスティックの制御には、Pan-Tilt HATのPythonライブラリ、ドキュメントを使用します。このLEDスティックにはカラーLEDが8個取り付けられており、モジュールによって全点灯、個別点灯が可能です。またRGBによるカラー指定も制御できます。
実験ロボットは、画像認識によるユーザーサインとして、白色の全点灯LEDを使用したり、認識時に緑色の全点灯LEDを使用することができます。さらに、合成音声再生中は、目を模した白色LEDを点滅させることができます。
それらの制御方法について、サンプルプログラムを通じて説明します。
サンプルプログラム
LEDの全点灯と消灯
まずは全点灯と消灯の制御方法を解説します。
import time # ---(※1)
import pantilthat
pantilthat.light_mode(pantilthat.WS2812) # NeoPixelsStick8はWS2812 ---(※2)
pantilthat.light_type( 1 ) # RGBで設定初期化 ---(※3)
# ネオピクセルLEDの制御 8つのLED全て指定 ---(※4)
def neopixels_all(r, g, b):
pantilthat.set_all(r, g, b)
pantilthat.show()
# ネオピクセルLEDの消灯 ---(※5)
def neopixels_off():
pantilthat.clear()
pantilthat.show()
if __name__ == "__main__":
neopixels_all(50, 50, 50)
time.sleep(3)
neopixels_all(0, 50, 0)
time.sleep(3)
neopixels_off()
time
モジュールとpantilthat
モジュールをインポートし(※1)、ライブラリのドキュメントに沿って、LEDの型番にあったモードを設定し、カラーコードの入力方法を指定します(※2)(※3)。
全点灯はモジュールのpantilthat.set_all(r, g, b)
関数を使い、pantilthat.show()
関数で点灯します(※4)。
消灯はpantilthat.clear()
を使い、pantilthat.show()
関数で消灯させます(※5)。
LEDの個別点灯
次に、個別にLEDの点灯を制御する方法を説明します。
import time # ---(※1)
import pantilthat
pantilthat.light_mode(pantilthat.WS2812) # NeoPixelsStick8はWS2812 ---(※2)
pantilthat.light_type( 1 ) # RGBで設定初期化 ---(※3)
# 目の設定 ネオピクセルLEDの制御 2つのLED指定 ---(※4)
def neopixels_face():
pantilthat.set_pixel(1, 50, 50, 50)
pantilthat.set_pixel(6, 50, 50, 50)
pantilthat.show()
# ネオピクセルLEDの消灯 ---(※5)
def neopixels_off():
pantilthat.clear()
pantilthat.show()
if __name__ == "__main__":
neopixels_face()
time.sleep(3)
neopixels_off()
(※1)~(※3)は全点灯と同様です。
個別のLED点灯はpantilthat.set(r, g, b)
関数を使い、pantilthat.show()
関数で点灯させます(※4)。
消灯はpantilthat.clear()
関数使い、pantilthat.show()
関数で消灯させます(※5)。
個別点灯させたLEDのフェードイン・フェードアウト
最後に、個別に点灯させたLEDをフェードイン・フェードアウトさせる制御方法を説明します。
import time, math # ---(※1)
import pantilthat
pantilthat.light_mode(pantilthat.WS2812) # NeoPixelsStick8はWS2812 ---(※2)
pantilthat.light_type( 1 ) # RGBで設定初期化 ---(※3)
# speskコマンド実施中 目を点滅させる
def neopixels_speak_flash_timeout():
time_start = time.perf_counter()
time_end = 0
while True:
brightness = (math.sin(time_end*4)+1) /2 # sin波の波形をプラスのみ計算 ---(※4)
print(brightness)
red, green, blue = 50, 50, 50 # ベースの色 (白) ---(※5)
red = int(brightness * red) # ベースの色に明るさを乗算 ---(※6)
green = int(brightness * green)
blue = int(brightness * blue)
pantilthat.set_pixel(1, red, green, blue)
pantilthat.set_pixel(6, red, green, blue)
pantilthat.show()
time.sleep(0.1) # 0.1秒ごとに明るさを更新 ---(※7)
pantilthat.clear()
time_end = time.perf_counter() - time_start
if time_end > 5: # 5秒で終了 ---(※8)
pantilthat.show()
break
if __name__ == "__main__":
neopixels_speak_flash_timeout()
(※1)~(※3)は上記サンプルと同様に設定し、個別のLED点灯・点灯に関しても同様です(※5)。
フェードイン・フェードアウトの効果を出すため、1を加算して2で割ったsin波形を利用して明るさを設定します(※4)。
time.perf_counter()
を利用して経過時間とともに明るさを変化させ(※6)(※7)、5秒でループを終了し、消灯します(※8)。
実行結果は以下のようになります。
Discussion