PaPeRo i の照度センサーを用いて時間指定で発話させる

PaPeRo i の照度センサーと時間指定での発話を組み合わせた機能がなかったため、実装してみました。

PaPeRo i の照度センサーとは
照度センサーとはPaPeRo i に実装されている機能の一つで、周りの明るさを自動的に検知します。
検知した明るさの度合いによって発話内容を変化させることが可能です。

使用するハード
・PaPeRo i 実機、またはPaPeRo i シミュレーター

使用するソフト
・Windows 10
・テキストエディタ(Visual Studio Code)
・Google Chrome
・Python 3.7


1・下記のソースをコピーし、night.pyという名前でファイルを保存する。
2・papero.pyと同じフォルダにnight.pyを移動させる。

# -*- coding:utf-8 -*-
##############################################################
# 照度センサーを使った時間指定発話
# Copyright (c) 2016 Sophia Planning Inc. All Rights Reserved
# ライセンス:MIT
##############################################################
#from logging import basicConfig, DEBUG
#basicConfig(level=DEBUG)

import sys
import time
import datetime
from enum import Enum

import pypapero


class State(Enum):
    st0 = 10
    st1 = 11
    st2 = 12
    st3 = 13
    st4 = 14
    st5 = 15
    st6 = 16
    st7 = 17
    st8 = 18
    st9 = 19
    st10 = 20
    end = 999


class SpeechManager:
    def __init__(self):
        self.past_time = 0
        self.count_text = 0
        self.state = State.end

    def start(self, papero, list_pat, motor=False):
        self.motor = motor
        self.list_pat = list_pat
        self.count_text = 0
        self.speech_time = len(self.list_pat[self.count_text][0]) * 0.2
        if self.motor:
            papero.send_move_head(["A5T400L", "A-5T400L", "A5T400L", "A-5T400L",
                                   "A5T400L", "A-5T400L", "A5T400L", "A-5T400L", "A5T400L", "A0T400L"],
                                  ["A-9T400L", "A-12T400L", "A-3T400L", "A6T400L",
                                   "A15T400L", "A6T400L", "A-3T400L", "A-12T400L", "A-9T400L", "A0T400L"],
                                   repeat=True)
        papero.send_turn_led_on("mouth", self.list_pat[self.count_text][1])
        papero.send_turn_led_on("cheek", ["R3R3", "1"], repeat=True)
        papero.send_set_default_status_speech(pitch=120, speed=75)
        papero.send_start_speech(self.list_pat[self.count_text][0])
        self.past_time = 0
        self.state = State.st0

    def forward(self, papero, msg_dic_rcv, delta_time):
        if self.state == State.st0:
            self.past_time += delta_time
            if self.past_time >= self.speech_time:
                self.state = State.st1
        elif self.state == State.st1:
            papero.send_get_speech_status()
            self.past_time = 0
            self.state = State.st2
        elif self.state == State.st2:
            if (msg_dic_rcv is not None) and (msg_dic_rcv["Name"] == "getSpeechStatusRes"):
                if str(msg_dic_rcv["Return"]) == "0":
                    self.count_text += 1
                    if self.count_text >= len(self.list_pat):
                        self.state = State.st3
                    else:
                        self.speech_time = len(self.list_pat[self.count_text][0]) * 0.2
                        papero.send_turn_led_on("mouth", self.list_pat[self.count_text][1])
                        papero.send_start_speech(self.list_pat[self.count_text][0])
                        self.state = State.st0
            if self.state == State.st2:
                self.past_time += delta_time
                if self.past_time >= 1.0:
                    self.state = State.st1
        elif self.state == State.st3:
            papero.send_turn_led_off("cheek")
            if self.motor:
                papero.send_move_head(["A0T500L"], ["A0T500L"])
                self.state = State.st4
            else:
                papero.send_set_default_status_speech(pitch = 100)
                self.state = State.end
        elif self.state == State.st4:
            if (msg_dic_rcv is not None) and (msg_dic_rcv["Name"] == "moveFinish"):
                papero.send_set_default_status_speech(pitch=100, speed=100)
                self.state = State.end


class LuxMeasure:
    def __init__(self):
        self.state = State.st0
        self.valid = False
        self.value = 0
        self.past_time = 0

    def forward(self, papero, msg_dic_rcv, delta_time):
        if self.state == State.st0:
            papero.send_start_lum_sensor()
            papero.send_get_lum_sensor_value()
            self.past_time = 0
            self.state = State.st1
        elif self.state == State.st1:
            self.past_time += delta_time
            if self.past_time >= 3:
                self.state = State.st0
        if (msg_dic_rcv is not None) and (msg_dic_rcv["Name"] == "getLumSensorValueRes"):
            if "Return" in msg_dic_rcv:
                self.value = int(msg_dic_rcv["Return"])
                self.valid = True
                papero.send_stop_lum_sensor()
                print("lux = " + str(self.value))


def lengthen_sp_led(list_sp, sec):
    i = 0
    while i < len(list_sp):
        j = 1
        while j < len(list_sp[i][1]):
            list_sp[i][1][j] = str(sec)
            j += 4
        i += 1


def main(papero):
    state = State.st0
    lux_measure = LuxMeasure()
    lux_threshold = 100
    speech_manager = SpeechManager()
    prev_time = time.monotonic()
    cnt_wkup = 0
    tried = False
    sheep_unit = 10
    sheep_count = 0
    sheep_subcount = 0
    must_count = False
    now_time = 0
    past_time = 0
    btn_pushed = False
    list_sp_init = [["寝る時間になったよ!",
                      ["NNNG3G3G3NNN", "2", "NNG3G3G3G3G3NN", "2", "NNG3G3G3G3G3NN", "2",
                       "NG3G3G3G3G3G3G3N", "2", "G3NG3NG3NG3NG3", "2", "NNNG3G3G3NNN", "2",
                       "NG3G3G3G3G3G3G3N", "2", "NNNG3G3G3NNN", "3", "G3NG3NG3NG3NG3", "2",
                       "NG3G3G3G3G3G3G3N", "2", "NG3G3G3G3G3G3G3N", "2", "G3NG3NG3NG3NG3", "2"]],
                     ["今日もお疲れ様!おやすみなさい",
                      ["G3NG3NG3NG3NG3", "2", "NNNG3G3G3NNN", "2", "G3NG3NG3NG3NG3", "2",
                       "NNNG3G3G3NNN", "2", "NG3G3G3G3G3G3G3N", "2", "NNNG3G3G3NNN", "2",
                       "NNNG3G3G3NNN", "2", "NG3G3G3G3G3G3G3N", "2", "NNNG3G3G3NNN", "2",
                       "NNG3G3G3G3G3NN", "2", "NNNG3G3G3NNN", "2", "NNNG3G3G3NNN", "2",
                       "G3NG3NG3NG3NG3", "2", "G3NG3NG3NG3NG3", "2", "NG3G3G3G3G3G3G3N", "2",
                       "NNNG3G3G3NNN", "2", "G3NG3NG3NG3NG3", "2", "NNG3G3G3G3G3NN", "2",
                       "NG3G3G3G3G3G3G3N", "2", "NNG3G3G3G3G3NN", "2", "NNG3NG3NG3NN", "2",
                       "NNG3NG3NG3NN", "2"]]]                       
    lengthen_sp_led(list_sp_init, 3)

    list_sp_init2 = [["眠れないの? 眠れない時は、",
                      ["NNG3G3G3G3G3NN", "2", "NNNG3G3G3NNN", "2", "NNG3G3G3G3G3NN", "2",
                       "NG3G3G3G3G3G3G3N", "2", "G3NG3NG3NG3NG3", "2", "NNNG3G3G3NNN", "2",
                       "NNG3G3G3G3G3NN", "2", "NNNG3G3G3NNN", "2", "NG3G3G3G3G3G3G3N", "2",
                       "NNG3G3G3G3G3NN", "2", "NNG3NG3NG3NN", "2", "NNG3G3G3G3G3NN", "2",
                       "NNNG3G3G3NNN", "2", "NNG3G3G3G3G3NN", "2", "NG3G3G3G3G3G3G3N", "2",
                       "G3NG3NG3NG3NG3", "2", "NNNG3G3G3NNN", "2", "G3NG3NG3NG3NG3", "2"]],
                     ["羊の数をかぞえるといいかもしれないよ!",
                      ["G3NG3NG3NG3NG3", "2", "NNNG3G3G3NNN", "2", "G3NG3NG3NG3NG3", "2",
                       "NNNG3G3G3NNN", "2", "NG3G3G3G3G3G3G3N", "2", "NNNG3G3G3NNN", "2",
                       "NNNG3G3G3NNN", "2", "NG3G3G3G3G3G3G3N", "2", "NNNG3G3G3NNN", "2",
                       "NNG3G3G3G3G3NN", "2", "NNNG3G3G3NNN", "2", "NNNG3G3G3NNN", "2",
                       "G3NG3NG3NG3NG3", "2", "G3NG3NG3NG3NG3", "2", "NG3G3G3G3G3G3G3N", "2",
                       "NNNG3G3G3NNN", "2", "G3NG3NG3NG3NG3", "2", "NNG3G3G3G3G3NN", "2",
                       "NG3G3G3G3G3G3G3N", "2", "NNG3G3G3G3G3NN", "2", "NNG3NG3NG3NN", "2",
                       "NNG3NG3NG3NN", "2"]]]
    lengthen_sp_led(list_sp_init2, 3)

    list_sp_init3 = [["まだ眠れないの? それじゃ、",
                      ["NNNG3G3G3NNN", "2", "NNG3G3G3G3G3NN", "2", "NG3G3G3G3G3G3G3N", "2",
                       "G3NG3NG3NG3NG3", "2", "NNNG3G3G3NNN", "2", "NNG3G3G3G3G3NN", "2",
                       "NNNG3G3G3NNN", "2", "NG3G3G3G3G3G3G3N", "2", "NNG3G3G3G3G3NN", "2",
                       "NNNG3G3G3NNN", "2", "NNNG3G3G3NNN", "2", "NNG3G3G3G3G3NN", "2",
                       "G3NG3NG3NG3NG3", "2", "NG3G3G3G3G3G3G3N", "2", "NNG3NG3NG3NN", "2"]],
                     ["電気をけしてくれたら",
                      ["G3NG3NG3NG3NG3", "2", "NNNG3G3G3NNN", "2", "NNNG3G3G3NNN", "2",
                       "NNG3G3G3G3G3NN", "2", "G3NG3NG3NG3NG3", "2", "NNG3G3G3G3G3NN", "2",
                       "NNNG3G3G3NNN", "2", "NNG3G3G3G3G3NN", "2", "NG3G3G3G3G3G3G3N", "2",
                       "NG3G3G3G3G3G3G3N", "2"]],
                      ["パペロがかわりに羊を数えてあげるね。",
                       ["NG3G3G3G3G3G3G3N", "2", "NNG3G3G3G3G3NN", "2", "NNNG3G3G3NNN", "2",
                       "NG3G3G3G3G3G3G3N", "2", "NG3G3G3G3G3G3G3N", "2", "NG3G3G3G3G3G3G3N", "2",
                       "G3NG3NG3NG3NG3", "2", "G3NG3NG3NG3NG3", "2", "NNNG3G3G3NNN", "2",
                       "NNNG3G3G3NNN", "2", "NNG3NG3NG3NN", "2", "NNG3G3G3G3G3NN", "2",
                       "NNG3G3G3G3G3NN", "2", "NG3G3G3G3G3G3G3N", "2", "NNG3G3G3G3G3NN", "2",
                       "NNNG3G3G3NNN", "2", "NNG3G3G3G3G3NN", "2"]],
                     ["じゅっぴき言い終わるごとにやめるから、続けたい場合は",
                      ["G3NG3NG3NG3NG3", "2", "NNNG3G3G3NNN", "2", "NNNG3G3G3NNN", "2",
                       "G3NG3NG3NG3NG3", "2", "G3NG3NG3NG3NG3", "2", "NNNG3G3G3NNN", "2",
                       "NNNG3G3G3NNN", "2", "G3NG3NG3NG3NG3", "2", "NG3G3G3G3G3G3G3N", "2",
                       "NNG3G3G3G3G3NN", "2", "NNNG3G3G3NNN", "2", "NG3G3G3G3G3G3G3N", "2",
                       "NG3G3G3G3G3G3G3N", "2", "NNG3NG3NG3NN", "2", "NNNG3G3G3NNN", "2",
                       "NNNG3G3G3NNN", "2", "NNG3G3G3G3G3NN", "2", "NNG3G3G3G3G3NN", "2",
                       "NNNG3G3G3NNN", "2", "G3NG3NG3NG3NG3", "2", "NG3G3G3G3G3G3G3N", "2",
                       "NNNG3G3G3NNN", "2", "NG3G3G3G3G3G3G3N", "2", "NG3G3G3G3G3G3G3N", "2"]],
                     ["どれか座布団のボタンをおしてね",
                      ["NNNG3G3G3NNN", "2", "NNG3G3G3G3G3NN", "2", "NG3G3G3G3G3G3G3N", "2",
                       "NNNG3G3G3NNN", "2", "NG3G3G3G3G3G3G3N", "2", "NNG3NG3NG3NN", "2",
                       "NNNG3G3G3NNN", "2", "NNNG3G3G3NNN", "2", "G3NG3NG3NG3NG3", "2",
                       "NNG3G3G3G3G3NN", "2", "NNG3G3G3G3G3NN", "2"]]]
    lengthen_sp_led(list_sp_init3, 3)

    list_sp_wkup1 =  [["起きて起きて起きて!朝だよー!",
                      ["A15T500L", "R0T1500L", "A0T500L", "A-15T200L", "A15T500L", "A-15T500L", 
                      "A15T500L", "A0T300L", "A-15T200L", "A15T500L", "A-15T500L", "A15T500L", "A0T300L"]]]
    lengthen_sp_led(list_sp_wkup1, 3)

    list_sp_wkup2 =  [["おはよう!今日もいちにち頑張ろう!",
                      ["A15T500L", "R0T1500L", "A0T500L", "A-15T200L", "A15T500L", "A-15T500L", 
                      "A15T500L", "A0T300L", "A-15T200L", "A15T500L", "A-15T500L", "A15T500L", "A0T300L"]]]
    lengthen_sp_led(list_sp_wkup2, 3)


    speech_flag_morning = 0
    speech_flag_night = 0


    while state != State.end:
        messages = papero.papero_robot_message_recv(1.0)
        now_time = time.monotonic()
        delta_time = now_time - prev_time
        prev_time = now_time
        past_time += delta_time

        
        dt_now = datetime.datetime.now()
        dt_now = dt_now.replace(microsecond = 0)
        print(dt_now)

        #起床時刻の設定
        if dt_now.hour==7 and dt_now.minute==00 and dt_now.second==00:
            if speech_flag_morning == 0:
                if lux_measure.value >= lux_threshold:
                    speech_manager.start(papero, list_sp_wkup2, motor=True)
                    state = State.st9
                    speech_flag_morning = 1        
                elif lux_measure.value <= lux_threshold:
                    speech_manager.start(papero, list_sp_wkup1, motor=True)
                    state = State.st8
                    speech_flag_morning = 1
            else:
                time.sleep(1)
                speech_flag_morning = 0

        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
        lux_measure.forward(papero, msg_dic_rcv, delta_time)
        speech_manager.forward(papero, msg_dic_rcv, delta_time)

        #就寝時刻の設定
        if dt_now.hour==23 and dt_now.minute==00 and dt_now.second==00:
            if speech_flag_night == 0:
                if lux_measure.valid:
                    if lux_measure.value >= lux_threshold:
                        speech_manager.start(papero, list_sp_init, motor=True)
                        state = State.st1
                        speech_flag_night = 1
                    else:
                        state = State.st4
            else:
                time.sleep(1)
                speech_flag_night = 0

        elif state == State.st1:
            if speech_manager.state == State.end:
                past_time = 0
                state = State.st2

        elif state == State.st2:
            past_time += delta_time
            if past_time >= 10:
                if lux_measure.valid:
                    if lux_measure.value >= lux_threshold:
                        speech_manager.start(papero, list_sp_init2, motor=True)
                        state = State.st3
                    else:
                        past_time = 0
                        state = State.st4
        elif state == State.st3:
            if speech_manager.state == State.end:
                past_time = 0
                state = State.st2
        elif state == State.st4:
            past_time += delta_time
            if lux_measure.valid:
                if lux_measure.value >= lux_threshold:
                    if past_time >= 10:
                        if (cnt_wkup <= 0) or (not tried):
                            speech_manager.start(papero, list_sp_init3, motor=True)
                            must_count = True
                        state = State.st5
                else:
                    tried = True
                    if past_time >= 3:
                        state = State.st6
        elif state == State.st5:
            if speech_manager.state == State.end:
                past_time = 0
                state = State.st4
        elif state == State.st6:
            if lux_measure.valid:
                if lux_measure.value >= lux_threshold:
                    past_time = 0
                    state = State.st4
                else:
                    if (sheep_subcount >= sheep_unit) and btn_pushed:
                        must_count = True
                        sheep_subcount = 0
                        btn_pushed = False
                    if must_count:
                        if sheep_subcount < sheep_unit:
                            sheep_count += 1
                            sheep_subcount += 1
                            speech_manager.start(papero, [["羊が" + str(sheep_count) + "匹",
                                                 ["G3NG3NG3NG3NG3", "2", "NNNG3G3G3NNN", "2",
                                                  "G3NG3NG3NG3NG3", "2", "NG3G3G3G3G3G3G3N", "2",
                                                  "G3NG3NG3NG3NG3", "2", "NNNG3G3G3NNN", "2",
                                                  "G3NG3NG3NG3NG3", "2", "G3NG3NG3NG3NG3", "2"]]],
                                                 motor=False)
                            state = State.st7
                        else:
                            btn_pushed = False
                            must_count = False
                            past_time = 0
                            state = State.st4   
                    else:
                        state = State.st4
        elif state == State.st7:
            if speech_manager.state == State.end:
                state = State.st6
        elif state == State.st8:
            if lux_measure.value >= lux_threshold:
                speech_manager.start(papero, list_sp_wkup2, motor=True)
                state = State.st9
        elif state == State.st9:
            state = State.st10
        elif state == State.st10:
            state = State.st9
        
        # ボタンの監視
        if msg_dic_rcv is not None:
            if msg_dic_rcv is not None:
                if msg_dic_rcv["Name"] == "detectButton":
                    btn_pushed = True
        if state != State.st6 and state != State.st7:
            if (sheep_unit > sheep_subcount) and btn_pushed:
                state = State.end


if __name__ == "__main__":
    simulator_id, robot_name, ws_server_addr = pypapero.get_params_from_commandline(sys.argv)
    papero = pypapero.Papero(simulator_id, robot_name, ws_server_addr)
    if papero.errOccurred == 0:
        main(papero)
    papero.papero_cleanup()

3・DOS窓を開き、cdコマンドでカレントディレクトリを上記フォルダに変更
4・DOS窓に以下のコマンドを入力
  シミュレータの PaPeRo i を動かす場合:
  python night.py -sim シミュレータID
  実機を動かす場合:
  python night.py -wssvr ws://実機のIPアドレス:8088/papero


・起床時刻(デフォルトだと7時)になり、部屋が暗い場合は起床を促す発話を行います。
・起床時刻に既に部屋が明るい場合は、応援メッセージを発話します。
・起床を促す発話を行った後に部屋を明るくすると、応援メッセージを発話します。
・指定時刻(デフォルトだと23時)になると就寝を促す発話を行います。
・3秒毎に照度を測定し、就寝時刻に部屋が明るい(照度100ルクス以上)場合、羊を数える事を提案します。
・再度暗くなった後で再び明るくなると、今度は羊を数える事を申し出て、次に暗くなると、羊を数え始めます。
・羊を数える声は10回毎に止まりますが、座布団のボタンをどれか押せば続きを数えてくれます。
・羊の数を数える時以外で座布団のボタンを押すと、プロセスが終了します。