手を触れずにPCを操作できるデバイスとして話題となった Leap Motion を使って PaPeRo i に人の手を数えさせたりする方法の一例を、前回記事「Leap Motion で PaPeRo i に指の数を数えさせる」で紹介しましたが、今回は伸びている指の数によって、PaPeRo i にいろいろな動きをさせる方法の一例を紹介したいと思います。
Leap Motionは、WindowsPCに接続して使用します。
2021年1月現在Leap MotionSDKバージョン4.1.0 がリリースされているので、こちらを使った手順を紹介します。
手順(PaPeRo i 側)
(1) PaPeRo i 制御用WebSocket通信アドオンシナリオをまだインストールしていない場合は、「PaPeRo iをRaspberry Pi上のpythonから操作する」の「PaPeRo iにアドオンシナリオをインストール」に従ってインストール作業を行います。
手順(WindowsPC 側)
ここでは Windows10 Home がインストールされたノートPC上で行った作業手順を紹介します。
(1) https://developer.leapmotion.com/get-started から、Leap MotionのSDKをダウンロードします(要ユーザー登録)。
(2) ダウンロードしたzipファイル(LeapDeveloperKit_4.1.0+52211_win.zip)を、任意のフォルダに解凍します。
(3) 解凍してできた LeapDeveloperKit_4.1.0+52211_win というフォルダの中の、Leap_Motion_Installer_v4-2020-release-updates_public_win_x64_4.1.0+52211_ah1889.exe を実行し、画面の指示に従ってインストール作業を行います。
(4) Pythonをまだインストールしていない場合は、https://www.python.org/ からダウンロードしてインストールします。
(5) ws4py をまだインストールしていない場合は、コマンドプロンプトから下記のコマンドを入力する事によりインストールします。
pip install ws4py
(6) 「PaPeRo i でPythonを使えるようにする」にあるリンクから「PaPeRo i 制御用Pythonライブラリ」をダウンロードし、zipファイルの中身(pypapero.py)を任意のフォルダに置きます。
(7) numpyをインストールします。
pip install numpy
pipでのインストールが失敗する場合は、https://sourceforge.net/projects/numpy/files/NumPy/ からインストーラーをダウンロードしてインストールします。
(8) Leap MotionをPCに接続し、タスクバーの右端付近の ^ の部分をクリックして隠れているインジケーターを表示し、その中のLeap Motionのアイコンが緑色になっている事を確認します。
(9) Leap Motion SDK 4.1.0では、WebSocketによるデータ取得機能が既定では無効になっているので、有効にするために、(8)で確認した Leap Motion のアイコンを右クリックし、「設定…」をクリックします。
Leap Motion コントロールパネルが現れますので、「全般」タブの「システム」グループの「Webアプリケーションを許可」にチェックを入れて、「OK」ボタンをクリックします。
(10) 下記の内容をコピー・ペーストして、ppr_lmc_fcoumot.py を作成し、pypapero.py と同じフォルダに置きます。
import sys
import time
import json
from enum import Enum
import numpy as np
from ws4py.client.threadedclient import WebSocketClient
import pypapero
class State(Enum):
st0 = 10
st1 = 11
st2 = 12
st3 = 13
end = 999
class LeapMotionController(WebSocketClient):
def opened(self):
self.connected = True
print("Connected to LeapMotion")
def closed(self, code, reason):
print("Disconnected from LeapMotion")
def received_message(self, msgrcv):
message = str(msgrcv)
self.lastMsg = message
def get_hands_data(msg_dic_rcv):
hands_data = []
if "hands" in msg_dic_rcv:
hands = msg_dic_rcv["hands"]
for i in range(len(hands)):
hand_data = {}
hand_data["id"] = hands[i]["id"]
hand_data["direction"] = hands[i]["direction"]
hand_data["palmNormal"] = hands[i]["palmNormal"]
hand_data["type"] = hands[i]["type"]
hand_data["fingerBases"] = [None, None, None, None, None]
if "pointables" in msg_dic_rcv:
pointables = msg_dic_rcv["pointables"]
for i in range(len(pointables)):
if pointables[i]["handId"] == hand_data["id"]:
finger_type = pointables[i]["type"]
hand_data["fingerBases"][int(finger_type)] = pointables[i]["bases"]
hands_data.append(hand_data)
return hands_data
def get_fcount():
msg_dic_rcv = json.loads(ws_lmc.lastMsg)
hands_data = get_hands_data(msg_dic_rcv)
hcount = len(hands_data)
fcount = 0
for i_hand in range(hcount):
for i_finger in range(5):
vec_bone = (-1) * np.array(hands_data[i_hand]["fingerBases"][i_finger][2][2])
vec_hand = np.array(hands_data[i_hand]["direction"])
if i_finger == 0 :
vec_hand = np.cross(hands_data[i_hand]["direction"], hands_data[i_finger]["palmNormal"])
if hands_data[i_hand]["type"] == "left":
vec_hand = (-vec_hand)
val_dot = np.dot(vec_bone, vec_hand)
if val_dot > 0.0:
fcount += 1
return fcount
def get_hcount():
msg_dic_rcv = json.loads(ws_lmc.lastMsg)
hands_data = get_hands_data(msg_dic_rcv)
hcount = len(hands_data)
return hcount
def main(papero):
prev_time = time.monotonic()
past_time = 0
state = State.st0
while state != State.end:
messages = papero.papero_robot_message_recv(0.1)
now_time = time.monotonic()
delta_time = now_time - prev_time
prev_time = now_time
msg_dic_rcv = json.loads(ws_lmc.lastMsg)
hands_data = get_hands_data(msg_dic_rcv)
if messages is not None:
msg_dic_rcv = messages[0]
else:
msg_dic_rcv = None
if papero.errOccurred != 0:
print("------Error occured(main()). Detail : " + papero.errDetail)
break
if state == State.st0:
papero.send_start_speech("伸びている指の数に応じて動作します。" +
"座布団のボタンで終了します。")
past_time = 0.0
state = State.st1
elif state == State.st1:
past_time += delta_time
if past_time > 0.5:
papero.send_get_speech_status()
state = State.st2
elif state == State.st2:
if msg_dic_rcv is not None:
if msg_dic_rcv["Name"] == "getSpeechStatusRes":
if str(msg_dic_rcv["Return"]) == "0":
state = State.st3
else:
past_time = 0
state = State.st1
elif state == State.st3:
fcount = get_fcount()
hcount = get_hcount()
print("fcount=", fcount)
if fcount==0 and hcount==1:
papero.send_turn_led_on("ear",["W3W3", "2"])
elif fcount==1 :
papero.send_turn_led_on("cheek",
["R3R3", "2"])
elif fcount==2 :
papero.send_turn_led_on("mouth",
["R3R3R3R3R3R3R3R3R3", "2"])
elif fcount==3 :
papero.send_turn_led_on("mouth",
["G3G3G3G3G3G3G3G3G3", "2"])
elif fcount==4 :
papero.send_move_head(["A-15T500L", "R0T1000L", "A0T500L"],
["A0T500L", "R0T1500L"])
elif fcount==5 :
papero.send_start_speech("こんにちわ。パペロと申します。")
past_time = 0
state = State.st1
if msg_dic_rcv is not None:
if msg_dic_rcv["Name"] == "detectButton":
state = State.end
if __name__ == "__main__":
ws_lmc = LeapMotionController("ws://127.0.0.1:6437/v6.json", protocols=None)
ws_lmc.lastMsg = "{}"
ws_lmc.connected = False
ws_lmc.connect()
while not ws_lmc.connected:
time.sleep(0.1)
ws_lmc.send('{"optimizeHMD": false}')
ws_lmc.send('{"enableGestures": false}')
ws_lmc.send('{"background": false}')
simulator_id, robot_name, ws_server_addr = pypapero.get_params_from_commandline(sys.argv)
papero = pypapero.Papero(simulator_id, robot_name, ws_server_addr)
main(papero)
papero.papero_cleanup()
ws_lmc.close()
(11) コマンドプロンプトの cd コマンドで、カレントディレクトリを ppr_lmc_fcoumot.py 及び pypapero.py の置いてあるディレクトリにします。
(12) 下記のコマンドで、ppr_lmc_fcoumot.py を実行します。
python ppr_lmc_fcoumot.py -wssvr ws://PaPeRoiのIPアドレス:8088/papero
※PaPeRo i シュミレータで行う場合は、シュミレーターのページを表示させておき、下記のコマンドで実行します。
python ppr_lmc_fcoumot.py -sim シュミレータID
実行すると、PaPeRo i が「伸びている指の数に応じて動作します。座布団のボタンで終了します」と発話します。
その後、Leap Motionの上に手をかざすと、伸びている指の数に応じた動作をします。
本記事のプログラムは、以下のような動作をします。
指0本 → 耳が光る
指1本 → 頬が赤く光る
指2本 → 口が赤く光る
指3本 → 口が緑に光る
指4本 → お辞儀をする
指5本 → 「こんにちは。パペロと申します。」と発話する