🦾

Raspberry PiとChatGPTでつくるボイス・アシスタント・ロボット #4

2023/09/06に公開

voice_assistant_robot_turn_left_down
ChatGPT実験ロボット "Voice Assistant Robot_GPT" がサーボモータを制御する様子

サーボモーター制御

実験ロボットのサンプルコード bot_motor_controller.py ファイルで実行します。内容の説明にあたりり、一部抜粋した内容で解説します。

Pan-Tilt HAT

https://shop.pimoroni.com/products/pan-tilt-hat?variant=22408353287

サーボモーター制御には、使いやすい拡張基板を使用します。ここではPimoroni社のPan-Tilt HATを選択しました。メーカーが提供するPythonライブラリにより、簡潔な記述で本格的な制御を行うことが出来ます。

この拡張基板は2つのサーボモーターの制御に加え、最大で24個までLEDを制御することが出来ます。また、NeoPixel Stick-8 というカラーLEDを取付けることも推奨しており、パンチルトユニットの上部に取付けることが出来ます。

仕組み

https://github.com/pimoroni/pantilt-hat

Pythonライブラリにはサンプルコード含まれており、それらを参考に手軽に制御することが出来ます。詳細なドキュメントはこちら documentation Pan Tilt HAT python library で確認できます。

サーボモーターの移動は、-90度から90度までの範囲で角度を指定することで制御できますが、速度制御は用意されていません。そのため独自に速度制御を実装しました。

サンプルプログラム

このサンプルでは、単純に移動角度の100分の1づつ100回繰り返すループ処理を行っています。ループ間にtime.sleep()を使用して間隔を空け、移動速度を調整しています。

ex_bot_motor_controller_1.py
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)で設定します。これは実際の動作に合わせて調整可能です。

実行結果は以下のようになります。

voice_assistant_robot_turn_right

LED制御

サーボモーター制御と同様に、サンプルプログラム bot_motor_controller.py から抜粋し、LED制御の説明をします。

adafruit NeoPixel Stick-8

https://www.adafruit.com/product/1426

LED制御は、Pan-Tilt HATに推奨されているAdafruit社のNeoPixel Stick-8 というカラーLEDを使用します。

仕組み

LEDスティックの制御には、Pan-Tilt HATのPythonライブラリ、ドキュメントを使用します。このLEDスティックにはカラーLEDが8個取り付けられており、モジュールによって全点灯、個別点灯が可能です。またRGBによるカラー指定も制御できます。

実験ロボットは、画像認識によるユーザーサインとして、白色の全点灯LEDを使用したり、認識時に緑色の全点灯LEDを使用することができます。さらに、合成音声再生中は、目を模した白色LEDを点滅させることができます。

それらの制御方法について、サンプルプログラムを通じて説明します。

サンプルプログラム

LEDの全点灯と消灯

まずは全点灯と消灯の制御方法を解説します。

ex_bot_motor_controller_2.py
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の点灯を制御する方法を説明します。

ex_bot_motor_controller_3.py
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をフェードイン・フェードアウトさせる制御方法を説明します。

ex_bot_motor_controller_4.py
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)。

sin_wave

time.perf_counter()を利用して経過時間とともに明るさを変化させ(※6)(※7)、5秒でループを終了し、消灯します(※8)。

実行結果は以下のようになります。

voice_assistant_robot_blink

Discussion