From 77c9d1abe463372f2b22e22bd2bc462741d90524 Mon Sep 17 00:00:00 2001 From: Karl Hudgell Date: Mon, 11 Nov 2019 08:53:53 +0000 Subject: [PATCH] Add our Robot Code --- 3_HeyRobot/voice/Hey Robot.pmdl | Bin 0 -> 13703 bytes .../voice/assistant_grpc_demo_snowboy.py | 77 ++++++++++ ...istant_library_with_local_commands_demo.py | 90 ++++++++++++ .../assistant_library_with_snowboy_demo.py | 135 ++++++++++++++++++ 3_HeyRobot/voice/light.py | 40 ++++++ 3_HeyRobot/voice/voice_recorder.py | 36 +++++ 6 files changed, 378 insertions(+) create mode 100644 3_HeyRobot/voice/Hey Robot.pmdl create mode 100644 3_HeyRobot/voice/assistant_grpc_demo_snowboy.py create mode 100644 3_HeyRobot/voice/assistant_library_with_local_commands_demo.py create mode 100644 3_HeyRobot/voice/assistant_library_with_snowboy_demo.py create mode 100644 3_HeyRobot/voice/light.py create mode 100644 3_HeyRobot/voice/voice_recorder.py diff --git a/3_HeyRobot/voice/Hey Robot.pmdl b/3_HeyRobot/voice/Hey Robot.pmdl new file mode 100644 index 0000000000000000000000000000000000000000..59e1f267588ad126090ac5ce638b1d5238524806 GIT binary patch literal 13703 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 100: + raise argparse.ArgumentTypeError('Volume must be in [0...100] range.') + return value + +def locale_language(): + language, _ = locale.getdefaultlocale() + return language + +interrupted = False + +def signal_handler(signal, frame): + global interrupted + interrupted = True + +def interrupt_callback(): + global interrupted + return interrupted + +def main(): + logging.basicConfig(level=logging.DEBUG) + signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit(0)) + + parser = argparse.ArgumentParser(description='Assistant service example.') + parser.add_argument('--language', default=locale_language()) + parser.add_argument('--volume', type=volume, default=80) + parser.add_argument('--model', default='src/examples/voice/Hey Robot.pmdl') + args = parser.parse_args() + + detector = snowboydecoder.HotwordDetector(args.model, sensitivity=0.5) + with Board() as board: + assistant = AssistantServiceClientWithLed(board=board, + volume_percentage=args.volume, + language_code=args.language) + while True: + #green_led = Light(18) + #red_led = Light(17) + logging.info('Speak own hotword and speak') + detector.start() + logging.info('Conversation started!') + assistant.conversation() + + +if __name__ == '__main__': + main() diff --git a/3_HeyRobot/voice/assistant_library_with_local_commands_demo.py b/3_HeyRobot/voice/assistant_library_with_local_commands_demo.py new file mode 100644 index 0000000..482757e --- /dev/null +++ b/3_HeyRobot/voice/assistant_library_with_local_commands_demo.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +# Copyright 2017 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Run a recognizer using the Google Assistant Library. + +The Google Assistant Library has direct access to the audio API, so this Python +code doesn't need to record audio. Hot word detection "OK, Google" is supported. + +It is available for Raspberry Pi 2/3 only; Pi Zero is not supported. +""" + +import logging +import platform +import subprocess +import sys + +from google.assistant.library.event import EventType + +from aiy.assistant import auth_helpers +from aiy.assistant.library import Assistant +from aiy.board import Board, Led +from aiy.voice import tts + +def power_off_pi(): + tts.say('Good bye!') + subprocess.call('sudo shutdown now', shell=True) + + +def reboot_pi(): + tts.say('See you in a bit!') + subprocess.call('sudo reboot', shell=True) + + +def say_ip(): + ip_address = subprocess.check_output("hostname -I | cut -d' ' -f1", shell=True) + tts.say('My IP address is %s' % ip_address.decode('utf-8')) + + +def process_event(assistant, led, event): + logging.info(event) + if event.type == EventType.ON_START_FINISHED: + led.state = Led.BEACON_DARK # Ready. + print('Say "OK, Google" then speak, or press Ctrl+C to quit...') + elif event.type == EventType.ON_CONVERSATION_TURN_STARTED: + led.state = Led.ON # Listening. + elif event.type == EventType.ON_RECOGNIZING_SPEECH_FINISHED and event.args: + print('You said:', event.args['text']) + text = event.args['text'].lower() + if text == 'power off': + assistant.stop_conversation() + power_off_pi() + elif text == 'reboot': + assistant.stop_conversation() + reboot_pi() + elif text == 'ip address': + assistant.stop_conversation() + say_ip() + elif event.type == EventType.ON_END_OF_UTTERANCE: + led.state = Led.PULSE_QUICK # Thinking. + elif (event.type == EventType.ON_CONVERSATION_TURN_FINISHED + or event.type == EventType.ON_CONVERSATION_TURN_TIMEOUT + or event.type == EventType.ON_NO_RESPONSE): + led.state = Led.BEACON_DARK # Ready. + elif event.type == EventType.ON_ASSISTANT_ERROR and event.args and event.args['is_fatal']: + sys.exit(1) + + +def main(): + logging.basicConfig(level=logging.INFO) + + credentials = auth_helpers.get_assistant_credentials() + with Board() as board, Assistant(credentials) as assistant: + for event in assistant.start(): + process_event(assistant, board.led, event) + + +if __name__ == '__main__': + main() diff --git a/3_HeyRobot/voice/assistant_library_with_snowboy_demo.py b/3_HeyRobot/voice/assistant_library_with_snowboy_demo.py new file mode 100644 index 0000000..8ff100f --- /dev/null +++ b/3_HeyRobot/voice/assistant_library_with_snowboy_demo.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 +# Copyright 2017 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Run a recognizer using the Google Assistant Library with button support. + +The Google Assistant Library has direct access to the audio API, so this Python +code doesn't need to record audio. Hot word detection "OK, Google" is supported. + +It is available for Raspberry Pi 2/3 only; Pi Zero is not supported. +""" + +import logging +import platform +import sys +import threading + +import aiy.assistant.auth_helpers +from aiy.assistant.library import Assistant +import aiy.voicehat +from google.assistant.library.event import EventType + +import mod.snowboydecoder as snowboydecoder + +if len(sys.argv) == 1: + print("Error: need to specify model name") + print("Usage: python demo.py your.model") + sys.exit(-1) + +model = sys.argv[1] + +logger = logging.getLogger("main") +logging.basicConfig( + level=logging.INFO, + format="[%(asctime)s] %(levelname)s:%(name)s:%(message)s" +) + + +class MyAssistant(object): + """An assistant that runs in the background. + + The Google Assistant Library event loop blocks the running thread entirely. + To support the button trigger, we need to run the event loop in a separate + thread. Otherwise, the on_button_pressed() method will never get a chance to + be invoked. + """ + + def __init__(self): + self._task = threading.Thread(target=self._run_task) + self._hotword = threading.Thread(target=self._run_hotword) + self._can_start_conversation = False + self._assistant = None + + def start(self): + """Starts the assistant. + + Starts the assistant event loop and begin processing events. + """ + self._task.start() + self._hotword.start() + + def _run_task(self): + credentials = aiy.assistant.auth_helpers.get_assistant_credentials() + with Assistant(credentials) as assistant: + self._assistant = assistant + for event in assistant.start(): + self._process_event(event) + + def _run_hotword(self): + detector = snowboydecoder.HotwordDetector(model, sensitivity=0.5) + with aiy.audio.get_recorder(): + while True: + if self._can_start_conversation: + detector.start(detected_callback=self._on_button_pressed, + interrupt_check=lambda: not(self._can_start_conversation), + sleep_time=0.03) + detector.terminate() + + def _process_event(self, event): + logger.debug(event) + status_ui = aiy.voicehat.get_status_ui() + if event.type == EventType.ON_START_FINISHED: + status_ui.status('ready') + self._can_start_conversation = True + # Start the voicehat button trigger. + aiy.voicehat.get_button().on_press(self._on_button_pressed) + if sys.stdout.isatty(): + print('Say "OK, Google" or press the button, then speak. ' + 'Press Ctrl+C to quit...') + + elif event.type == EventType.ON_CONVERSATION_TURN_STARTED: + self._can_start_conversation = False + status_ui.status('listening') + + elif event.type == EventType.ON_END_OF_UTTERANCE: + status_ui.status('thinking') + + elif (event.type == EventType.ON_CONVERSATION_TURN_FINISHED + or event.type == EventType.ON_CONVERSATION_TURN_TIMEOUT + or event.type == EventType.ON_NO_RESPONSE): + status_ui.status('ready') + self._can_start_conversation = True + + elif event.type == EventType.ON_ASSISTANT_ERROR and event.args and event.args['is_fatal']: + sys.exit(1) + + def _on_button_pressed(self): + # Check if we can start a conversation. 'self._can_start_conversation' + # is False when either: + # 1. The assistant library is not yet ready; OR + # 2. The assistant library is already in a conversation. + if self._can_start_conversation: + self._assistant.start_conversation() + + +def main(): + if platform.machine() == 'armv6l': + print('Cannot run hotword demo on Pi Zero!') + exit(-1) + MyAssistant().start() + + +if __name__ == '__main__': + main() diff --git a/3_HeyRobot/voice/light.py b/3_HeyRobot/voice/light.py new file mode 100644 index 0000000..f4fd709 --- /dev/null +++ b/3_HeyRobot/voice/light.py @@ -0,0 +1,40 @@ +import RPi.GPIO as GPIO +import time + +class Light(object): + def __init__(self, port): + self.port = port + GPIO.setmode(GPIO.BCM) + GPIO.setup(self.port, GPIO.OUT) + self.on_state = GPIO.HIGH + self.off_state = not self.on_state + + def set_on(self): + GPIO.output(self.port, self.on_state) + + def set_off(self): + GPIO.output(self.port, self.off_state) + + def is_on(self): + return GPIO.input(self.port) == self.on_state + + def is_off(self): + return GPIO.input(self.port) == self.off_state + + def toggle(self): + if self.is_on(): + self.set_off() + else: + self.set_on() + + def blink(self, t=0.3): + self.set_off() + self.set_on() + time.sleep(t) + self.set_off() + +if __name__ == "__main__": + light = Light(17) + while True: + light.blink() + time.sleep(0.7) diff --git a/3_HeyRobot/voice/voice_recorder.py b/3_HeyRobot/voice/voice_recorder.py new file mode 100644 index 0000000..677d3a0 --- /dev/null +++ b/3_HeyRobot/voice/voice_recorder.py @@ -0,0 +1,36 @@ +import argparse +import time +import threading + +from aiy.board import Board +from aiy.voice.audio import AudioFormat, play_wav, record_file, Recorder + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--filename', '-f', default='recording.wav') + args = parser.parse_args() + + with Board() as board: + print('Press button to start recording.') + board.button.wait_for_press() + + done = threading.Event() + board.button.when_pressed = done.set + + def wait(): + start = time.monotonic() + while not done.is_set(): + duration = time.monotonic() - start + print('Recording: %.02f seconds [Press button to stop]' % duration) + time.sleep(0.5) + + record_file(AudioFormat.CD, filename=args.filename, wait=wait, filetype='wav') + print('Press button to play recorded sound.') + board.button.wait_for_press() + + print('Playing...') + play_wav(args.filename) + print('Done.') + +if __name__ == '__main__': + main()