diff --git a/dialogflow_task_executive/CMakeLists.txt b/dialogflow_task_executive/CMakeLists.txt index 4d032badc..732d18053 100644 --- a/dialogflow_task_executive/CMakeLists.txt +++ b/dialogflow_task_executive/CMakeLists.txt @@ -1,9 +1,31 @@ cmake_minimum_required(VERSION 2.8.3) project(dialogflow_task_executive) +if($ENV{ROS_DISTRO} STREQUAL "indigo") + message(WARNING "following requirements.txt syntax is not support on 14.04") + message(WARNING "google-api-core[grpc]==1.31.5 # via google-cloud-language") + find_package(catkin) + catkin_package() + return() +endif() +execute_process(COMMAND bash -c "gcc -dumpmachine" OUTPUT_VARIABLE gcc_dump_machine OUTPUT_STRIP_TRAILING_WHITESPACE) +message("-- gcc dumpmachine returns ${gcc_dump_machine}") +if(NOT gcc_dump_machine MATCHES "x86_64-.*") + message(WARNING "pip -i requirements.txt work only with i686 ???") + message(WARNING "`pip install grpcio` fails with + third_party/boringssl-with-bazel/src/crypto/hrss/asm/poly_rq_mul.S: Assembler messages: + third_party/boringssl-with-bazel/src/crypto/hrss/asm/poly_rq_mul.S:306: Error: bad register name `%rbp' + third_party/boringssl-with-bazel/src/crypto/hrss/asm/poly_rq_mul.S:308: Error: bad register expression + third_party/boringssl-with-bazel/src/crypto/hrss/asm/poly_rq_mul.S:309: Error: bad register name `%rsp'") + find_package(catkin) + catkin_package() + return() +endif() + find_package(catkin REQUIRED COMPONENTS message_generation std_msgs + catkin_virtualenv ) add_message_files( @@ -20,10 +42,12 @@ catkin_package( CATKIN_DEPENDS message_runtime ) +catkin_generate_virtualenv(INPUT_REQUIREMENTS requirements.in) -install(DIRECTORY node_scripts +file(GLOB NODE_SCRIPTS_FILES node_scripts/*.py) +catkin_install_python( + PROGRAMS ${NODE_SCRIPTS_FILES} DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} - USE_SOURCE_PERMISSIONS ) install(DIRECTORY launch diff --git a/dialogflow_task_executive/README.md b/dialogflow_task_executive/README.md index f5dc25306..bf7e7d1f1 100644 --- a/dialogflow_task_executive/README.md +++ b/dialogflow_task_executive/README.md @@ -123,6 +123,19 @@ script end script ``` +## Sample programs for JSK users + +1. Open [Dialogflow](https://dialogflow.cloud.google.com/?authuser=1#/editAgent/eternal-byte-236613/) and setup Intents and Entities. + +2. Download google credentials file. You can download from [Google Drive](https://drive.google.com/file/d/1VxniytpH9J12ii9jphtBylydY1_k5nXf/view?usp=sharing) link. + + +3. Start sample code + +``` +roslaunch dialogflow_task_executive demo.launch google_cloud_credentials_json:=${HOME}/Downloads/eternal-byte-236613-4bc6962824d1.json +``` + ## Author Yuki Furuta diff --git a/dialogflow_task_executive/launch/dialogflow_task_executive.launch b/dialogflow_task_executive/launch/dialogflow_task_executive.launch index 76ac307a2..bde7393b2 100644 --- a/dialogflow_task_executive/launch/dialogflow_task_executive.launch +++ b/dialogflow_task_executive/launch/dialogflow_task_executive.launch @@ -1,7 +1,17 @@ - - + + + + + + + + + + + + @@ -12,10 +22,14 @@ pkg="dialogflow_task_executive" type="dialogflow_client.py" output="screen"> - use_audio: false - language: ja-JP - use_tts: true + use_audio: $(arg use_audio) + use_tts: $(arg use_tts) + language: $(arg language) + soundplay_action_name: $(arg soundplay_action_name) + volume: $(arg volume) project_id: $(arg project_id) + google_cloud_credentials_json: $(arg credential) + enable_hotword: $(arg enable_hotword) diff --git a/dialogflow_task_executive/node_scripts/dialogflow_client.py b/dialogflow_task_executive/node_scripts/dialogflow_client.py old mode 100755 new mode 100644 index 5f443346b..27af53644 --- a/dialogflow_task_executive/node_scripts/dialogflow_client.py +++ b/dialogflow_task_executive/node_scripts/dialogflow_client.py @@ -9,6 +9,7 @@ import rospy import threading import uuid +import os from audio_common_msgs.msg import AudioData from sound_play.msg import SoundRequest @@ -71,8 +72,13 @@ def __init__(self): # sample rate of audio data self.audio_sample_rate = rospy.get_param("~audio_sample_rate", 16000) + # if GOOLGE_APPLICATION_CREDENTIALS is not set, use google_cloud_credentials_json param + if not "GOOGLE_APPLICATION_CREDENTIALS" in os.environ: + os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = rospy.get_param("~google_cloud_credentials_json") + # use TTS feature self.use_tts = rospy.get_param("~use_tts", True) + self.volume = rospy.get_param('~volume', 1.0) # timeout for voice input activation by hotword self.timeout = rospy.get_param("~timeout", 10.0) @@ -88,8 +94,10 @@ def __init__(self): self.last_spoken = rospy.Time(0) if self.use_tts: + soundplay_action_name = rospy.get_param( + '~soundplay_action_name', 'robotsound_jp') self.sound_action = actionlib.SimpleActionClient( - "robotsound_jp", SoundRequestAction) + soundplay_action_name, SoundRequestAction) if not self.sound_action.wait_for_server(rospy.Duration(5.0)): self.sound_action = None else: @@ -115,6 +123,8 @@ def __init__(self): self.sub_speech = rospy.Subscriber( "speech_to_text", SpeechRecognitionCandidates, self.input_cb) + self.sub_text = rospy.Subscriber( + "text", String, self.input_cb) self.df_thread = threading.Thread(target=self.df_run) self.df_thread.daemon = True @@ -137,7 +147,12 @@ def input_cb(self, msg): self.state.set(State.LISTENING) elif not self.use_audio: # catch hotword from string - self.hotword_cb(String(data=msg.transcript[0])) + if isinstance(msg, SpeechRecognitionCandidates): + self.hotword_cb(String(data=msg.transcript[0])) + elif isinstance(msg, String): + self.hotword_cb(data) + else: + rospy.logerr("Unsupported data class {}".format(msg)) if self.state == State.LISTENING: self.queue.put(msg) @@ -164,11 +179,16 @@ def print_result(self, result): def publish_result(self, result): msg = DialogResponse() msg.header.stamp = rospy.Time.now() - if result.action is not 'input.unknown': + if result.action != 'input.unknown': rospy.logwarn("Unknown action") - msg.query = result.query_text.encode("utf-8") msg.action = result.action - msg.response = result.fulfillment_text.encode("utf-8") + + if self.language == 'ja-JP': + msg.query = result.query_text.encode("utf-8") + msg.response = result.fulfillment_text.encode("utf-8") + else: + msg.query = result.query_text + msg.response = result.fulfillment_text msg.fulfilled = result.all_required_params_present msg.parameters = MessageToJson(result.parameters) msg.speech_score = result.speech_recognition_confidence @@ -181,9 +201,17 @@ def speak_result(self, result): msg = SoundRequest( command=SoundRequest.PLAY_ONCE, sound=SoundRequest.SAY, - volume=1.0, + volume=self.volume, arg=result.fulfillment_text.encode('utf-8'), arg2=self.language) + + # for japanese or utf-8 languages + if self.language == 'ja-JP': + msg.arg = result.fulfillment_text.encode('utf-8') + msg.arg2 = self.language + else: + msg.arg = result.fulfillment_text + self.sound_action.send_goal_and_wait( SoundRequestGoal(sound_request=msg), rospy.Duration(10.0)) @@ -207,6 +235,9 @@ def df_run(self): elif isinstance(msg, SpeechRecognitionCandidates): result = self.detect_intent_text( msg.transcript[0], session) + elif isinstance(msg, String): + result = self.detect_intent_text( + msg.data, session) else: raise RuntimeError("Invalid data") self.print_result(result) diff --git a/dialogflow_task_executive/node_scripts/task_executive.py b/dialogflow_task_executive/node_scripts/task_executive.py old mode 100755 new mode 100644 diff --git a/dialogflow_task_executive/package.xml b/dialogflow_task_executive/package.xml index a2a94bd34..6a622fada 100644 --- a/dialogflow_task_executive/package.xml +++ b/dialogflow_task_executive/package.xml @@ -13,13 +13,15 @@ https://github.com/jsk-ros-pkg/jsk_3rdparty/issues catkin + catkin_virtualenv message_generation std_msgs app_manager message_runtime python-dialogflow-pip - + + requirements.txt diff --git a/dialogflow_task_executive/requirements.in b/dialogflow_task_executive/requirements.in new file mode 100644 index 000000000..69476ee2a --- /dev/null +++ b/dialogflow_task_executive/requirements.in @@ -0,0 +1,2 @@ +dialogflow==1.1.1 + diff --git a/dialogflow_task_executive/samples/client.l b/dialogflow_task_executive/samples/client.l new file mode 100755 index 000000000..4b23d7a0a --- /dev/null +++ b/dialogflow_task_executive/samples/client.l @@ -0,0 +1,70 @@ +#!/usr/bin/env roseus + +(ros::roseus-add-msgs "dialogflow_task_executive") +(ros::roseus "dialogflow_example") + +(defun json-to-list (str) + (let (ret sstr) + ;; skip first { + (setq str (string-trim (format nil "{}~% ") str)) + ;; get first elemnet + (while str + (if (position #\, str) + (setq sstr (subseq str 0 (position #\, str)) + str (subseq str (1+ (position #\, str)))) + (setq sstr str str nil)) + (push (cons (string-trim (format nil "\"~% ") (subseq sstr 0 (position #\: sstr))) + (string-trim (format nil "\"~% ") (subseq sstr (+ (position #\: sstr) 2)))) ret) + ) + ret)) +(defclass dialogflow-client + :super propertied-object + :slots (response-msg string-msg response-sub publish-tm)) + +(defmethod dialogflow-client + (:init + () + (setq response-sub (ros::subscribe "/dialog_response" dialogflow_task_executive::DialogResponse #'send self :dialog-cb)) + (setq response-msg (instance dialogflow_task_executive::DialogResponse :init)) + (setq string-msg (instance std_msgs::String :init)) + (ros::advertise "/text" std_msgs::string 1) + ;; wait for text publisher + (ros::rate 1) + (while (< (ros::get-num-subscribers "/text") 1) + (ros::sleep)) + self) + (:dialog-cb + (msg) + (setq response-msg msg)) + (:wait-for-response + () + (while (< (send (ros::time- (send response-msg :header :stamp) publish-tm) :to-sec) 0) + (ros::ros-info "wait for response ... ") + (ros::spin-once) + (ros::sleep)) + response-msg) + (:publish-dialog + (text) + (let (ret) + (setq publish-tm (ros::time-now)) + (send string-msg :data text) + (ros::publish "/text" string-msg) + (setq ret (send self :wait-for-response)) + (send ret :parameters (json-to-list (send ret :parameters))) + (ros::ros-info "action : ~A" (send ret :action)) + (ros::ros-info "query : ~A" (send ret :query)) + (ros::ros-info "response : ~A" (send ret :response)) + (ros::ros-info "parameters : ~A" (send ret :parameters)) + (ros::ros-info " speech : ~A" (send ret :speech_score)) + (ros::ros-info " intent : ~A" (send ret :intent_score)) + ret)) + ) + + +(ros::roseus "client") +(setq client (instance dialogflow-client :init)) +(send client :publish-dialog "一緒に映画見よう") +(send client :publish-dialog "壁紙がきれいだったよ") +(send client :publish-dialog "一緒に飾り付けしよう") +(send client :publish-dialog "風船を右に置いて") +(exit) diff --git a/dialogflow_task_executive/samples/demo.launch b/dialogflow_task_executive/samples/demo.launch new file mode 100644 index 000000000..1bd938e5c --- /dev/null +++ b/dialogflow_task_executive/samples/demo.launch @@ -0,0 +1,12 @@ + + + + + + + + + + +