From da65028595c1d1c8cf9cf052c4a1ea04d3e13abc Mon Sep 17 00:00:00 2001
From: Andres Suarez <23503930+glicerico@users.noreply.github.com>
Date: Tue, 11 Mar 2025 01:19:32 -0600
Subject: [PATCH] Open source performances module
---
modules/performances/.gitignore | 2 +
modules/performances/CMakeLists.txt | 215 ++++++
modules/performances/README.md | 125 +++
modules/performances/cfg/Generator.cfg | 42 +
modules/performances/cfg/Performances.cfg | 29 +
modules/performances/cfg/Rules.cfg | 58 ++
modules/performances/cfg/Wholeshow.cfg | 27 +
modules/performances/cfg/rules_schema.yaml | 292 +++++++
modules/performances/cfg/yaml_config.py | 36 +
.../performances/depends/performances.deps | 1 +
modules/performances/launch/sdk.launch | 27 +
modules/performances/msg/Event.msg | 2 +
modules/performances/package.xml | 41 +
modules/performances/package/common.sh | 138 ++++
modules/performances/package/package.sh | 54 ++
modules/performances/scripts/runner.py | 730 ++++++++++++++++++
.../scripts/speech_motion_controller.py | 243 ++++++
modules/performances/setup.py | 28 +
.../performances/src/performances/__init__.py | 0
.../performances/src/performances/config.yaml | 209 +++++
.../performances/src/performances/nodes.py | 599 ++++++++++++++
.../src/performances/speech_motion.py | 595 ++++++++++++++
.../src/performances/weak_method.py | 33 +
modules/performances/srv/Current.srv | 5 +
modules/performances/srv/Load.srv | 4 +
modules/performances/srv/LoadPerformance.srv | 3 +
modules/performances/srv/Pause.srv | 3 +
modules/performances/srv/Resume.srv | 3 +
modules/performances/srv/Run.srv | 3 +
modules/performances/srv/RunByName.srv | 3 +
modules/performances/srv/SetProperties.srv | 4 +
modules/performances/srv/Stop.srv | 3 +
modules/performances/test/test_speech.py | 84 ++
modules/performances/version | 1 +
34 files changed, 3642 insertions(+)
create mode 100644 modules/performances/.gitignore
create mode 100644 modules/performances/CMakeLists.txt
create mode 100644 modules/performances/README.md
create mode 100755 modules/performances/cfg/Generator.cfg
create mode 100755 modules/performances/cfg/Performances.cfg
create mode 100755 modules/performances/cfg/Rules.cfg
create mode 100755 modules/performances/cfg/Wholeshow.cfg
create mode 100755 modules/performances/cfg/rules_schema.yaml
create mode 100644 modules/performances/cfg/yaml_config.py
create mode 100644 modules/performances/depends/performances.deps
create mode 100644 modules/performances/launch/sdk.launch
create mode 100644 modules/performances/msg/Event.msg
create mode 100644 modules/performances/package.xml
create mode 100644 modules/performances/package/common.sh
create mode 100755 modules/performances/package/package.sh
create mode 100755 modules/performances/scripts/runner.py
create mode 100755 modules/performances/scripts/speech_motion_controller.py
create mode 100644 modules/performances/setup.py
create mode 100644 modules/performances/src/performances/__init__.py
create mode 100644 modules/performances/src/performances/config.yaml
create mode 100644 modules/performances/src/performances/nodes.py
create mode 100644 modules/performances/src/performances/speech_motion.py
create mode 100644 modules/performances/src/performances/weak_method.py
create mode 100644 modules/performances/srv/Current.srv
create mode 100644 modules/performances/srv/Load.srv
create mode 100644 modules/performances/srv/LoadPerformance.srv
create mode 100644 modules/performances/srv/Pause.srv
create mode 100644 modules/performances/srv/Resume.srv
create mode 100644 modules/performances/srv/Run.srv
create mode 100644 modules/performances/srv/RunByName.srv
create mode 100644 modules/performances/srv/SetProperties.srv
create mode 100644 modules/performances/srv/Stop.srv
create mode 100755 modules/performances/test/test_speech.py
create mode 100644 modules/performances/version
diff --git a/modules/performances/.gitignore b/modules/performances/.gitignore
new file mode 100644
index 0000000..2a45ed7
--- /dev/null
+++ b/modules/performances/.gitignore
@@ -0,0 +1,2 @@
+*.pyc
+.vscode
diff --git a/modules/performances/CMakeLists.txt b/modules/performances/CMakeLists.txt
new file mode 100644
index 0000000..f0b2818
--- /dev/null
+++ b/modules/performances/CMakeLists.txt
@@ -0,0 +1,215 @@
+##
+## Copyright (C) 2017-2025 Hanson Robotics
+##
+## This program is free software: you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see .
+##
+
+cmake_minimum_required(VERSION 2.8.3)
+project(performances)
+
+## Find catkin macros and libraries
+## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
+## is used, also find other catkin packages
+find_package(catkin REQUIRED COMPONENTS
+ rospy
+ std_msgs
+ message_generation
+ dynamic_reconfigure
+)
+
+## System dependencies are found with CMake's conventions
+# find_package(Boost REQUIRED COMPONENTS system)
+
+
+## Uncomment this if the package has a setup.py. This macro ensures
+## modules and global scripts declared therein get installed
+## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html
+catkin_python_setup()
+
+################################################
+## Declare ROS messages, services and actions ##
+################################################
+
+## To declare and build messages, services or actions from within this
+## package, follow these steps:
+## * Let MSG_DEP_SET be the set of packages whose message types you use in
+## your messages/services/actions (e.g. std_msgs, actionlib_msgs, ...).
+## * In the file package.xml:
+## * add a build_depend tag for "message_generation"
+## * add a build_depend and a run_depend tag for each package in MSG_DEP_SET
+## * If MSG_DEP_SET isn't empty the following dependency has been pulled in
+## but can be declared for certainty nonetheless:
+## * add a run_depend tag for "message_runtime"
+## * In this file (CMakeLists.txt):
+## * add "message_generation" and every package in MSG_DEP_SET to
+## find_package(catkin REQUIRED COMPONENTS ...)
+## * add "message_runtime" and every package in MSG_DEP_SET to
+## catkin_package(CATKIN_DEPENDS ...)
+## * uncomment the add_*_files sections below as needed
+## and list every .msg/.srv/.action file to be processed
+## * uncomment the generate_messages entry below
+## * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...)
+
+## Generate messages in the 'msg' folder
+add_message_files(
+ FILES
+ Event.msg
+)
+
+## Generate services in the 'srv' folder
+add_service_files(
+ FILES
+ Run.srv
+ RunByName.srv
+ Stop.srv
+ Pause.srv
+ Resume.srv
+ Current.srv
+ Load.srv
+ LoadPerformance.srv
+ SetProperties.srv
+)
+
+## Generate actions in the 'action' folder
+# add_action_files(
+# FILES
+# Action1.action
+# Action2.action
+# )
+
+## Generate added messages and services with any dependencies listed here
+generate_messages(
+ DEPENDENCIES
+ std_msgs
+)
+
+################################################
+## Declare ROS dynamic reconfigure parameters ##
+################################################
+
+## To declare and build dynamic reconfigure parameters within this
+## package, follow these steps:
+## * In the file package.xml:
+## * add a build_depend and a run_depend tag for "dynamic_reconfigure"
+## * In this file (CMakeLists.txt):
+## * add "dynamic_reconfigure" to
+## find_package(catkin REQUIRED COMPONENTS ...)
+## * uncomment the "generate_dynamic_reconfigure_options" section below
+## and list every .cfg file to be processed
+
+## Generate dynamic reconfigure parameters in the 'cfg' folder
+generate_dynamic_reconfigure_options(
+ cfg/Performances.cfg
+ cfg/Rules.cfg
+ cfg/Generator.cfg
+
+)
+
+###################################
+## catkin specific configuration ##
+###################################
+## The catkin_package macro generates cmake config files for your package
+## Declare things to be passed to dependent projects
+## INCLUDE_DIRS: uncomment this if you package contains header files
+## LIBRARIES: libraries you create in this project that dependent projects also need
+## CATKIN_DEPENDS: catkin_packages dependent projects also need
+## DEPENDS: system dependencies of this project that dependent projects also need
+catkin_package(
+# INCLUDE_DIRS include
+# LIBRARIES performances
+# CATKIN_DEPENDS rospy std_msgs
+# DEPENDS system_lib
+)
+
+###########
+## Build ##
+###########
+
+## Specify additional locations of header files
+## Your package locations should be listed before other locations
+# include_directories(include)
+include_directories(
+ ${catkin_INCLUDE_DIRS}
+)
+
+## Declare a C++ library
+# add_library(performances
+# src/${PROJECT_NAME}/performances.cpp
+# )
+
+## Add cmake target dependencies of the library
+## as an example, code may need to be generated before libraries
+## either from message generation or dynamic reconfigure
+# add_dependencies(performances ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
+
+## Declare a C++ executable
+# add_executable(performances_node src/performances_node.cpp)
+
+## Add cmake target dependencies of the executable
+## same as for the library above
+# add_dependencies(performances_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
+
+## Specify libraries to link a library or executable target against
+# target_link_libraries(performances_node
+# ${catkin_LIBRARIES}
+# )
+
+#############
+## Install ##
+#############
+
+# all install targets should use catkin DESTINATION variables
+# See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html
+
+## Mark executable scripts (Python etc.) for installation
+## in contrast to setup.py, you can choose the destination
+install(PROGRAMS
+ scripts/runner.py
+ scripts/speech_motion_controller.py
+ DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
+)
+
+## Mark executables and/or libraries for installation
+# install(TARGETS performances performances_node
+# ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
+# LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
+# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
+# )
+
+## Mark cpp header files for installation
+# install(DIRECTORY include/${PROJECT_NAME}/
+# DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
+# FILES_MATCHING PATTERN "*.h"
+# PATTERN ".svn" EXCLUDE
+# )
+
+## Mark other files for installation (e.g. launch and bag files, etc.)
+# install(FILES
+# # myfile1
+# # myfile2
+# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
+# )
+
+#############
+## Testing ##
+#############
+
+## Add gtest based cpp test target and link libraries
+# catkin_add_gtest(${PROJECT_NAME}-test test/test_performances.cpp)
+# if(TARGET ${PROJECT_NAME}-test)
+# target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME})
+# endif()
+
+## Add folders to be run by python nosetests
+# catkin_add_nosetests(test)
diff --git a/modules/performances/README.md b/modules/performances/README.md
new file mode 100644
index 0000000..b72a1f0
--- /dev/null
+++ b/modules/performances/README.md
@@ -0,0 +1,125 @@
+# Performances
+
+A ROS-based system for controlling robot behaviors, animations, and speech for Hanson Robotics robots. This package provides a framework for sequencing and executing robot performances through a combination of speech, gestures, animations, and interactive behaviors.
+
+The Performances package allows you to:
+- Load and run predefined performance sequences
+- Control robot animations, gestures, and speech
+- Manage interactive behaviors through dialog rules
+- Coordinate multiple performance timelines
+- Configure automated gestures and expressions
+
+## Available Services:
+
+#### `/performances/reload_properties`
+`std_srvs.srv.Trigger`
+Reads and loads performance configurations from .properties files.
+##### Response
+* `boolean success`
+
+#### `/performances/set_properties`
+`performances.srv.SetProperties`
+Sets variables for a specific performance.
+
+##### Arguments
+* `string id` - performance id
+* `string properties` - a json string of an object containing key value pairs of variables to be set
+
+##### Response
+* `boolean success`
+
+#### `/performances/load`
+`performances.srv.Load`
+Loads performance with the specified id into current session.
+
+##### Arguments
+* `id` - performance id
+
+##### Response
+* `boolean success`
+* `string performance` - json data string of a loaded performance
+
+#### `/performances/load_sequence`
+`performances.srv.LoadSequence`
+Loads a sequence of performances.
+
+##### Arguments
+* `string[] ids` - ids of performances to load
+
+##### Response
+* `boolean success`
+* `string performances` - json data string of loaded performances
+
+#### `/performances/load_performance`
+`performances.srv.LoadPerformance`
+Load performance data directly.
+
+##### Arguments
+* `string performance` - json data of a performance to load
+
+##### Response
+* `boolean success`
+
+#### `/performances/run`
+`performances.srv.Run`
+Run currently loaded performance(s) at the given time.
+
+##### Arguments
+* `float64 startTime` - start time
+
+##### Response
+* `boolean success`
+
+#### `/performances/run_by_name`
+`performances.srv.RunByName`
+Load performance by id and run immediately.
+
+##### Arguments
+* `string id` - performance id
+
+##### Response
+* `boolean success`
+
+#### `/performances/run_full_performance`
+`performances.srv.RunByName`
+Load all performances in the specified directory and run immediately.
+
+##### Arguments
+* `string id` - path of the directory
+
+##### Response
+* `boolean success`
+
+#### `/performances/resume`
+`performances.srv.Resume`
+Resume runner if paused.
+
+##### Response
+* `boolean success`
+* `float64 time`
+
+#### `/performances/pause`
+`performances.srv.Pause`
+Pause runner if currently running.
+
+##### Response
+* `boolean success`
+* `float64 time`
+
+#### `/performances/stop`
+`performances.srv.Stop`
+Stop runner if active.
+
+##### Response
+* `boolean success`
+* `float64 time`
+
+#### `/performances/current`
+`performances.srv.Current`
+Get information about the current state of runner.
+
+##### Response
+`string performances` - json array of currently loaded performances
+`float32 current_time` - current run time
+`bool running` - run status
+`bool paused` - pause status
diff --git a/modules/performances/cfg/Generator.cfg b/modules/performances/cfg/Generator.cfg
new file mode 100755
index 0000000..f39c9fb
--- /dev/null
+++ b/modules/performances/cfg/Generator.cfg
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+
+##
+## Copyright (C) 2017-2025 Hanson Robotics
+##
+## This program is free software: you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see .
+##
+
+PACKAGE = 'performances'
+
+from dynamic_reconfigure.parameter_generator_catkin import *
+import yaml_config
+
+gen = ParameterGenerator()
+# Tracking parameters
+gen.add("name", str_t, 0, "Performance path", '')
+gen.add("lines", str_t, 0, "Sophia lines", '')
+gen.add("pause_between_sentences", double_t, 0, "Pause between sentences", 0.5, 0, 4)
+#gen.add("other_rules", str_t, 0, "other_rules", '[]')
+# UI schema for gestures and expressions
+gen.add("min_speed", int_t, 0, "Min speed of speech ", 100,50,150)
+gen.add("max_speed", int_t, 0, "Max speed of speech ", 100,50,150)
+gen.add("split_sentences", bool_t, 0, "Do not split to sentences", True)
+
+gen.add("lang", str_t, 0, "Language code (en-US or similar that supported by TTS)", 'en-US')
+
+gen.add("start", bool_t, 0, "Start Generation", True)
+gen.add("progress", str_t, 0, "Current Progress", 'idle')
+gen.add("node_schema", str_t, 0, '', '{"lines":{"type": "string","format": "textarea"}}')
+# package name, node name, config name
+exit(gen.generate(PACKAGE, "generator", "Generator"))
diff --git a/modules/performances/cfg/Performances.cfg b/modules/performances/cfg/Performances.cfg
new file mode 100755
index 0000000..ec3643e
--- /dev/null
+++ b/modules/performances/cfg/Performances.cfg
@@ -0,0 +1,29 @@
+#!/usr/bin/env python
+
+##
+## Copyright (C) 2017-2025 Hanson Robotics
+##
+## This program is free software: you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see .
+##
+
+PACKAGE = 'performances'
+
+from dynamic_reconfigure.parameter_generator_catkin import *
+
+gen = ParameterGenerator()
+
+gen.add("autopause", bool_t, 0, "Enable autopause", True)
+
+# package name, node name, config name
+exit(gen.generate(PACKAGE, "performances", "Performances"))
diff --git a/modules/performances/cfg/Rules.cfg b/modules/performances/cfg/Rules.cfg
new file mode 100755
index 0000000..4f61961
--- /dev/null
+++ b/modules/performances/cfg/Rules.cfg
@@ -0,0 +1,58 @@
+#!/usr/bin/env python
+
+##
+## Copyright (C) 2017-2025 Hanson Robotics
+##
+## This program is free software: you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see .
+##
+
+PACKAGE = 'performances'
+
+from dynamic_reconfigure.parameter_generator_catkin import *
+import yaml_config
+
+gen = ParameterGenerator()
+# Tracking parameters
+gen.add("interactive", bool_t, 0, "Only in interactive mode", True)
+gen.add("enable", bool_t, 0, "Enable keyword rules", True)
+gen.add("automated_arm_gestures", bool_t, 0, "Automated arm gestures", False)
+gen.add("automated_shoulder_gestures", bool_t, 0, "Automated shoulder gestures", False)
+gen.add("min_arms_hold_time", double_t, 0, "Min hold time for arm animations", 3,0,6)
+gen.add("max_arms_hold_time", double_t, 0, "Max hold time for arm animations", 5,0,6)
+gen.add("global_probability", double_t, 0, "Adjust all probabilities (-1 no rules applied, 1 all rules, 0 Unchanged)", 0,-1,1)
+# Add more positive gestures
+gen.add("generic_gestures", bool_t, 0, "Generic gestures", False)
+gen.add("generic_gestures_categories_excluded", str_t, 0, "Do not use these categories as generic gestures","")
+gen.add("generic_gestures_probability", double_t, 0, "Probability for each word to get gestures",0.1,0.01,1.0)
+gen.add("generic_gestures_head_filter", double_t, 0, "Generic head movement animation filter",0.9,0.0,1.0)
+gen.add("generic_gestures_brow_filter", double_t, 0, "Generic brow movement animation filter",0.6,0.0,1.0)
+gen.add("generic_gestures_eyelids_filter", double_t, 0, "Generic eyelids movement animation filter",0.3,0.0,1.0)
+gen.add("generic_gestures_eyes_filter", double_t, 0, "Generic eyes movement animation filter",0.3,0.0,1.0)
+gen.add("generic_gestures_arms_filter", double_t, 0, "Generic arms movement animation filter",0.9,0.0,1.0)
+gen.add("generic_gestures_happy_filter", double_t, 0, "Add some happy animations (0- none, 1 - every 2 words)",0.5,0.0,1.0)
+# Filters animations (can remove the head)
+gen.add("rules_head_filter", double_t, 0, "Head movement filter",1.0,0.0,1.0)
+gen.add("rules_brow_filter", double_t, 0, "Brow movement filter",1.0,0.0,1.0)
+gen.add("rules_eyelids_filter", double_t, 0, "Eyelid movement filter",1.0,0.0,1.0)
+gen.add("rules_eyes_filter", double_t, 0, "Eyes movement filter",1.0,0.0,1.0)
+gen.add("reduce_negativity", double_t, 0, "Reduce negativite categories of animations", 0.0, 0.0, 1.0)
+gen.add("negative_gesture_categories", str_t, 0, "Remove brows down, head shakes", "shake-short, shake-long, shake-firmly, brows-down, brows-doubt")
+
+#gen.add("other_rules", str_t, 0, "other_rules", '[]')
+# UI schema for gestures and expressions
+gen.add("da_rules", str_t, 0, "Dialog Act Rules", '[]')
+gen.add("keyword_rules", str_t, 0, "Keyword based Rules", '[]')
+gen.add("node_schema", str_t, 0, '', yaml_config.parse('rules_schema') or '{}')
+# package name, node name, config name
+exit(gen.generate(PACKAGE, "rules", "Rules"))
diff --git a/modules/performances/cfg/Wholeshow.cfg b/modules/performances/cfg/Wholeshow.cfg
new file mode 100755
index 0000000..be13e0a
--- /dev/null
+++ b/modules/performances/cfg/Wholeshow.cfg
@@ -0,0 +1,27 @@
+#!/usr/bin/env python
+
+##
+## Copyright (C) 2017-2025 Hanson Robotics
+##
+## This program is free software: you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see .
+##
+
+PACKAGE = 'wholeshow'
+
+from dynamic_reconfigure.parameter_generator_catkin import *
+
+gen = ParameterGenerator()
+gen.add("chat_during_performance", bool_t, 0, "Chat while running performances", False)
+gen.add("filter_stt", bool_t, 0, "Filter STT", True)
+exit(gen.generate(PACKAGE, "wholeshow", "Wholeshow"))
diff --git a/modules/performances/cfg/rules_schema.yaml b/modules/performances/cfg/rules_schema.yaml
new file mode 100755
index 0000000..ab0edbc
--- /dev/null
+++ b/modules/performances/cfg/rules_schema.yaml
@@ -0,0 +1,292 @@
+##
+## Copyright (C) 2017-2025 Hanson Robotics
+##
+## This program is free software: you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see .
+##
+
+keyword_rules_ui:
+ type: array
+ format: table
+ items:
+ type: object
+ properties:
+ keywords:
+ title: 'Keywords'
+ propertyOrder: 1
+ type: string
+ priority:
+ title: 'Priority'
+ propertyOrder: 2
+ type: number
+ default: 50
+ probability:
+ title: 'Probability'
+ propertyOrder: 3
+ type: number
+ default: 0.5
+ headAA:
+ title: 'head AA'
+ propertyOrder: 4
+ type: string
+ default: "-"
+ enum:
+ - "-"
+ - "nod-short"
+ - "head-shake"
+ - "look-up"
+ - "look-away"
+ - "head-tilt"
+ browAA:
+ title: 'Brow AA'
+ propertyOrder: 5
+ type: string
+ default: "-"
+ enum:
+ - "-"
+ - "brows-up"
+ - "brows-down"
+ - "brow-center-up"
+ eyesAA:
+ title: 'Eyes AA'
+ propertyOrder: 6
+ type: string
+ default: "-"
+ enum:
+ - "-"
+ - "eyes-away"
+ - "eyes-up"
+ armsAA:
+ title: 'Arms AA'
+ propertyOrder: 7
+ type: string
+ default: "-"
+ enum:
+ - "-"
+ - "arm_unsure"
+ - "arm_beat"
+ - "arm_me"
+ - "arm_you"
+ - "arm_many"
+ - "arm_vawe"
+ - "arm_point"
+ - "arm_greeting"
+ - "arm_heart"
+ - "arm_frame_small"
+ - "arm_frame_big"
+ - "arm_compare_smaller"
+ - "arm_compare_bigger"
+ - "arm_contrast"
+ - "arm_offer"
+ - "arm_process"
+ - "arm_reject"
+da_rules:
+ type: array
+ format: table
+ items:
+ type: object
+ properties:
+ act:
+ title: 'Dialog Act'
+ propertyOrder: 1
+ type: string
+ default: "-"
+ enum:
+ - "STATEMENT_NON_OPINION"
+ - "ACKNOWLEDGE"
+ - "STATEMENT_OPINION"
+ - "AGREE"
+ - "ABANDONED"
+ - "APPRECIATION"
+ - "YES_NO_QUESTION"
+ - "NON_VERBAL"
+ - "YES_ANSWER"
+ - "CONVENTIONAL_CLOSING"
+ - "UNINTERPRETABLE"
+ - "WH_QUESTION"
+ - "NO_ANSWER"
+ - "RESPONSE_ACKNOWLEDGEMENT"
+ - "HEDGE"
+ - "DECLARATIVE_YES_NO_QUESTION"
+ - "OTHER"
+ - "BLACKCHANNEL_IN_QUESTION_FORM"
+ - "QUOTATION"
+ - "SUMMARIZE"
+ - "AFFIRMATIVE_NON_YES_ANSWER"
+ - "ACTION_DIRECTIVE"
+ - "COLLABORATIVE_COMPLETION"
+ - "REPEAT_PHRASE"
+ - "OPEN_QUESTION"
+ - "RHETORICAL_QUESTIONS"
+ - "HOLD_BEFORE_ANSWER"
+ - "REJECT"
+ - "NEGATIVE_NON_NO_ANSWER"
+ - "SIGNAL_NON_UNDERSTANDING"
+ - "OTHER_ANSWER"
+ - "CONVENTIONAL_OPENING"
+ - "OR_CLAUSE"
+ - "DISPREFERRED_ANSWER"
+ - "THIRD_PARTY_TALK"
+ - "OFFER"
+ - "SELF_TALK"
+ - "DOWNPLAYER"
+ - "MAYBE"
+ - "TAG_QUESTION"
+ - "DECLARATIVE_WH_QUESTION"
+ - "APOLOGY"
+ - "THANKING"
+ threshold:
+ title: 'Threshold'
+ propertyOrder: 2
+ type: number
+ default: 0.5
+ priority:
+ title: 'Priority'
+ propertyOrder: 3
+ type: number
+ default: 50
+ probability:
+ title: 'Probability'
+ propertyOrder: 4
+ type: number
+ default: 0.5
+ headAA:
+ title: 'head AA'
+ propertyOrder: 5
+ type: string
+ default: "-"
+ enum:
+ - "-"
+ - "head-up"
+ - "head-down"
+ - "head-down"
+ - "head-left"
+ - "head-right"
+ - "head-tilt"
+ - "head-tilt-long"
+ - "nod-short"
+ - "nod-long"
+ - "nod-heavy"
+ - "shake-short"
+ - "shake-long"
+ - "shake-firmly"
+ - "shake-heavy"
+ - "wobbling"
+ browAA:
+ title: 'Brow AA'
+ propertyOrder: 6
+ type: string
+ default: "-"
+ enum:
+ - "-"
+ - "brow-up-short"
+ - "brow-up-normal"
+ - "brow-up-long"
+ - "brow-up-normal"
+ - "brow-up-long"
+ - "brow-center-up"
+ - "brow-center-up-long"
+ - "brow-down-short"
+ - "brow-down-normal"
+ - "brow-down-long"
+ - "brow-doubt"
+ - "brow-doubt-long"
+ eyelidsAA:
+ title: 'EyeLids AA'
+ propertyOrder: 7
+ type: string
+ default: "-"
+ enum:
+ - "-"
+ - "eyelid-half-close"
+ - "eyelid-widely-open"
+ eyesAA:
+ title: 'Eyes AA'
+ propertyOrder: 7
+ type: string
+ default: "-"
+ enum:
+ - "-"
+ - "eye-left"
+ - "eye-right"
+ - "eye-up"
+ - "eye-down"
+ - "eye-around"
+ - "eye-corner"
+ - "eye-side"
+ armsAA:
+ title: 'Arms AA'
+ propertyOrder: 8
+ type: string
+ default: "-"
+ enum:
+ - "-"
+ - "arm-unsure"
+ - "arm-enjoy"
+ - "arm-beat"
+ - "arm-me"
+ - "arm-you"
+ - "arm-many"
+ - "arm-vawe"
+ - "arm-point"
+ - "arm-greeting"
+ - "arm-call"
+ - "arm-heart"
+ - "arm-frame-small"
+ - "arm-frame-big"
+ - "arm-compare-smaller"
+ - "arm-compare-bigger"
+ - "arm-contrast"
+ - "arm-offer"
+ - "arm-process-control"
+ - "arm-rejection"
+ - "arm-rolling"
+ - "arm-notkown"
+ - "arm-peace"
+ - "arm-stop"
+ - "arm-round"
+ - "arm-different"
+ apply:
+ title: "Which word of sentence to select"
+ type: string
+ propertyOrder: 9
+ default: "random"
+ enum:
+ - "random"
+ - "first"
+ - "last"
+other_rules:
+ type: array
+ format: table
+ items:
+ type: object
+ properties:
+ keywords:
+ title: 'Rule Desc'
+ propertyOrder: 1
+ type: string
+ default: "-"
+ enum:
+ - "Stress words based on impoortance"
+ - "Keep alive animations"
+ - "Emphasize question ending"
+ priority:
+ title: 'Priority'
+ propertyOrder: 2
+ type: number
+ default: 50
+ probability:
+ title: 'Probability'
+ propertyOrder: 3
+ type: number
+ default: 0.5
\ No newline at end of file
diff --git a/modules/performances/cfg/yaml_config.py b/modules/performances/cfg/yaml_config.py
new file mode 100644
index 0000000..a8d41c1
--- /dev/null
+++ b/modules/performances/cfg/yaml_config.py
@@ -0,0 +1,36 @@
+##
+## Copyright (C) 2017-2025 Hanson Robotics
+##
+## This program is free software: you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see .
+##
+
+import yaml
+import json
+import os
+
+current_dir = os.path.dirname(os.path.realpath(__file__))
+
+def parse(filename):
+ with open(os.path.join(current_dir, filename + '.yaml'), 'r') as stream:
+ try:
+ return json.dumps(yaml.safe_load(stream))
+ except yaml.YAMLError as exc:
+ return False
+
+def load(filename):
+ with open(os.path.join(current_dir, filename + '.yaml'), 'r') as stream:
+ try:
+ return yaml.safe_load(stream)
+ except yaml.YAMLError as exc:
+ return False
diff --git a/modules/performances/depends/performances.deps b/modules/performances/depends/performances.deps
new file mode 100644
index 0000000..63e00ef
--- /dev/null
+++ b/modules/performances/depends/performances.deps
@@ -0,0 +1 @@
+debian:head-hr-msgs>=0.2.2
diff --git a/modules/performances/launch/sdk.launch b/modules/performances/launch/sdk.launch
new file mode 100644
index 0000000..87ce53d
--- /dev/null
+++ b/modules/performances/launch/sdk.launch
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/performances/msg/Event.msg b/modules/performances/msg/Event.msg
new file mode 100644
index 0000000..682c6e0
--- /dev/null
+++ b/modules/performances/msg/Event.msg
@@ -0,0 +1,2 @@
+string event
+float64 time
diff --git a/modules/performances/package.xml b/modules/performances/package.xml
new file mode 100644
index 0000000..b2dea06
--- /dev/null
+++ b/modules/performances/package.xml
@@ -0,0 +1,41 @@
+
+
+
+ performances
+ 0.3.11
+ The performances package.
+ GPLv3
+ Wenwei Huang
+ Vytas Krisciunas
+ Wenwei Huang
+ Vytas Krisciunas
+
+ catkin
+ rospy
+ std_msgs
+ dynamic_reconfigure
+ rospy
+ std_msgs
+
+
+
+
+
+
+
+
diff --git a/modules/performances/package/common.sh b/modules/performances/package/common.sh
new file mode 100644
index 0000000..3bc738d
--- /dev/null
+++ b/modules/performances/package/common.sh
@@ -0,0 +1,138 @@
+##
+## Copyright (C) 2017-2025 Hanson Robotics
+##
+## This program is free software: you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see .
+##
+
+env() {
+ export HR_PREFIX=/opt/hansonrobotics
+ export HR_BIN_PREFIX=$HR_PREFIX/bin
+ export HRTOOL_PREFIX=${HR_PREFIX}/hrtool
+ export HR_ROS_PREFIX=${HR_PREFIX}/ros
+ export HR_TOOLS_PREFIX=$HR_PREFIX/tools
+ export HR_DATA_PREFIX=$HR_PREFIX/data
+ export VOICE_CACHE_DIR=$HOME/.hr/tts/voice
+ export URL_PREFIX=https://github.com/hansonrobotics
+ export GITHUB_STORAGE_URL=https://raw.githubusercontent.com/hansonrobotics/binary_dependency/master
+ export GITHUB_STORAGE_URL2=https://$GITHUB_TOKEN@raw.githubusercontent.com/hansonrobotics/binary_dependency2/master
+ export VENDOR="Hanson Robotics"
+ export PYTHON_PKG_PREFIX=$HR_PREFIX/py2env/lib/python2.7/dist-packages
+ export PYTHON3_PKG_PREFIX=$HR_PREFIX/py3env/lib/python3.6/dist-packages
+ export ROS_PYTHON_PKG_PREFIX=$HR_ROS_PREFIX/lib/python2.7/dist-packages
+}
+
+install_deps() {
+ if ! hash gem >/dev/null 2>&1; then
+ echo "Installing ruby-full"
+ sudo apt-get install ruby-full
+ fi
+
+ if ! hash fpm >/dev/null 2>&1; then
+ gem install fpm
+ gem install deb-s3
+ fi
+
+ if ! hash chrpath >/dev/null 2>&1; then
+ echo "Installing chrpath"
+ sudo apt-get install chrpath
+ fi
+
+ if ! hash autoconf >/dev/null 2>&1; then
+ echo "Installing autoconf"
+ sudo apt-get install autoconf
+ fi
+
+ if ! hash jq >/dev/null 2>&1; then
+ echo "Installing jq"
+ sudo apt-get install jq
+ fi
+
+ if [[ ! -f /usr/local/go/bin/go ]]; then
+ echo "Installing go"
+ wget https://dl.google.com/go/go1.14.2.linux-amd64.tar.gz -O /tmp/go1.14.2.linux-amd64.tar.gz
+ sudo tar -C /usr/local -xzf /tmp/go1.14.2.linux-amd64.tar.gz
+ fi
+
+ export PATH=/usr/local/go/bin:$PATH
+}
+
+COLOR_INFO='\033[32m'
+COLOR_WARN='\033[33m'
+COLOR_ERROR='\033[31m'
+COLOR_RESET='\033[0m'
+info() {
+ printf "${COLOR_INFO}[INFO] ${1}${COLOR_RESET}\n" >&2
+}
+warn() {
+ printf "${COLOR_WARN}[WARN] ${1}${COLOR_RESET}\n" >&2
+}
+error() {
+ printf "${COLOR_ERROR}[ERROR] ${1}${COLOR_RESET}\n" >&2
+}
+
+source_ros() {
+ local ros_dists=(noetic melodic kinetic indigo)
+ for ros_dist in ${ros_dists[@]}; do
+ if [[ -e /opt/ros/$ros_dist/setup.bash ]]; then
+ info "ROS distribution $ros_dist"
+ source /opt/ros/$ros_dist/setup.bash
+ return
+ fi
+ done
+}
+
+add_control_scripts() {
+ local root_dir=${1:-${PACKAGE_DIR}/control}
+ local preinst="${root_dir}/preinst.sh"
+ local postinst="${root_dir}/postinst.sh"
+ local prerm="${root_dir}/prerm.sh"
+ local postrm="${root_dir}/postrm.sh"
+
+ local ms=""
+ [[ -f ${preinst} ]] && ms="$ms --before-install ${preinst}"
+ [[ -f ${postinst} ]] && ms="$ms --after-install ${postinst}"
+ [[ -f ${prerm} ]] && ms="$ms --before-remove ${prerm}"
+ [[ -f ${postrm} ]] && ms="$ms --after-remove ${postrm}"
+
+ if [[ -z $ms ]]; then
+ echo "Empty maintainer scripts"
+ return 1
+ fi
+ echo $ms
+}
+
+cleanup_ros_package_build() {
+ # clean up
+ pushd $1 >/dev/null
+ rm -r src build_isolated devel_isolated .catkin_workspace install
+ popd >/dev/null
+}
+
+get_version() {
+ local date=$(date +%Y%m%d%H%M%S)
+ local version_file=$BASEDIR/src/$reponame/version
+ local tag=$(git describe --tags --candidates=0)
+ if [[ -f $version_file ]]; then
+ version=$(head -n 1 $version_file)
+ # if 1 is present or the latest tag equals to version
+ if [[ $1 != 1 && ${tag#v} != $version ]]; then
+ version=${version}-${date}
+ fi
+ else
+ version=$date
+ fi
+}
+
+env
+install_deps
diff --git a/modules/performances/package/package.sh b/modules/performances/package/package.sh
new file mode 100755
index 0000000..7635a45
--- /dev/null
+++ b/modules/performances/package/package.sh
@@ -0,0 +1,54 @@
+#!/usr/bin/env bash
+
+##
+## Copyright (C) 2017-2025 Hanson Robotics
+##
+## This program is free software: you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see .
+##
+
+package() {
+ local reponame=performances
+
+ mkdir -p $BASEDIR/src
+ rsync -r --delete \
+ --exclude ".git" \
+ --exclude "package" \
+ $BASEDIR/../ $BASEDIR/src/$reponame
+
+ get_version $1
+ source_ros
+ catkin_make_isolated --directory $BASEDIR --install --install-space $BASEDIR/install -DCMAKE_BUILD_TYPE=Release
+
+ local name=head-performances
+ local desc="Robot performances API"
+ local url="https://api.github.com/repos/hansonrobotics/$reponame/releases"
+
+ fpm -C "${BASEDIR}" -s dir -t deb -n "${name}" -v "${version#v}" --vendor "${VENDOR}" \
+ --url "${url}" --description "${desc}" ${ms} --force \
+ --deb-no-default-config-files \
+ -p $BASEDIR/${name}_VERSION_ARCH.deb \
+ install/include=${HR_ROS_PREFIX}/ \
+ install/share=${HR_ROS_PREFIX}/ \
+ install/lib=${HR_ROS_PREFIX}/
+
+ cleanup_ros_package_build $BASEDIR
+}
+
+if [[ $(readlink -f ${BASH_SOURCE[0]}) == $(readlink -f $0) ]]; then
+ BASEDIR=$(dirname $(readlink -f ${BASH_SOURCE[0]}))
+ source $BASEDIR/common.sh
+ set -e
+
+ package $1
+fi
diff --git a/modules/performances/scripts/runner.py b/modules/performances/scripts/runner.py
new file mode 100755
index 0000000..f253183
--- /dev/null
+++ b/modules/performances/scripts/runner.py
@@ -0,0 +1,730 @@
+#!/usr/bin/env python
+
+##
+## Copyright (C) 2017-2025 Hanson Robotics
+##
+## This program is free software: you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see .
+##
+
+from threading import Thread, Lock, Condition
+import logging
+import json
+import time
+import yaml
+import os
+import fnmatch
+import random
+import copy
+
+try:
+ from pathlib import Path
+except:
+ # python2.7
+ from pathlib2 import Path
+
+import rospy
+import hr_msgs.srv as srv
+from dynamic_reconfigure.server import Server
+from hr_msgs.msg import ChatMessage
+from hr_msgs.msg import Event
+from hr_msgs.msg import MakeFaceExpr, PlayAnimation
+from hr_msgs.msg import SetAnimation, SetExpression, Target, SomaState
+from hr_msgs.msg import TTS
+from natsort import natsorted, ns
+from performances.cfg import PerformancesConfig
+from performances.nodes import Node
+from performances.weak_method import WeakMethod
+from std_msgs.msg import String, Int32, Float32
+from std_srvs.srv import Trigger, TriggerResponse
+from topic_tools.srv import MuxSelect
+
+logger = logging.getLogger('hr.performances')
+
+
+class Runner:
+ def __init__(self, ros, namespace = '~', background = False):
+ self.performances_dir = os.path.join(os.environ.get('PERFORMANCES_DIR', '/home/hr/workspace/hrsdk_configs/performances'))
+ self.robot_name = rospy.get_param('/hr/robot_name')
+ self.running = False
+ self.paused = False
+ self.autopause = False
+ self.pause_time = 0
+ self.start_time = 0
+ self.start_timestamp = 0
+ self.lock = Lock()
+ self.run_condition = Condition()
+ self.running_performance = None
+ self.running_nodes = []
+ self.unload_finished = False
+ # References to event subscribing node callbacks
+ self.interrupted_performances = []
+ # Performances that already played as alternatives. Used to maximize different performance in single demo
+ self.performances_played = {}
+ self.worker = Thread(target=self.worker_loop)
+ self.worker.setDaemon(True)
+ self.queue = []
+ logger.info('Starting performances node')
+ self.ros = ros
+ self.running_performance_pub = rospy.Publisher('{}running_performance'.format(namespace), String, queue_size=1)
+ self.events_pub = rospy.Publisher('{}events'.format(namespace), Event, queue_size=1)
+ self.running_performance_pub = rospy.Publisher('{}running_performance'.format(namespace), String, queue_size=1)
+ self.interrupt_tts = True
+ self.load_properties()
+ rospy.Service('{}reload_properties'.format(namespace), Trigger, self.reload_properties_callback)
+ rospy.Service('{}set_properties'.format(namespace), srv.SetProperties, self.set_properties_callback)
+ rospy.Service('{}load'.format(namespace), srv.Load, self.load_callback)
+ rospy.Service('{}load_performance'.format(namespace), srv.LoadPerformance, self.load_performance_callback)
+ rospy.Service('{}unload'.format(namespace), Trigger, self.unload_callback)
+ rospy.Service('{}run'.format(namespace), srv.Run, self.run_callback)
+ rospy.Service('{}run_by_name'.format(namespace), srv.RunByName, self.run_by_name_callback)
+ rospy.Service('{}run_full_performance'.format(namespace).format(namespace), srv.RunByName, self.run_full_performance_callback)
+ rospy.Service('{}resume'.format(namespace), srv.PerformanceCommand, self.resume_callback)
+ rospy.Service('{}pause'.format(namespace), srv.PerformanceCommand, self.pause_callback)
+ rospy.Service('{}stop'.format(namespace), srv.PerformanceCommand, self.stop_callback)
+ rospy.Service('{}current'.format(namespace), srv.Current, self.current_callback)
+ rospy.Service('{}is_performance_loaded'.format(namespace), Trigger, self.check_is_performance_loaded)
+ # Shared subscribers for nodes
+ rospy.Subscriber('{}events'.format(namespace), Event, self.runner_event_callback)
+ # Shared subscribers for nodes
+ if namespace != '~':
+ # only default runner to interrupt performance
+ self.interrupt_tts = False
+ Server(PerformancesConfig, self.reconfig, namespace=namespace)
+ else:
+ if not background:
+ Server(PerformancesConfig, self.reconfig)
+
+ self.worker.start()
+
+ def reconfig(self, config, level):
+ with self.lock:
+ self.autopause = config.autopause
+
+ return config
+
+ def reload_properties_callback(self, request):
+ self.load_properties()
+ return TriggerResponse(success=True)
+
+ def unload_callback(self, request):
+ self.unload()
+ return TriggerResponse(success=True)
+
+ def unload(self):
+ self.stop()
+ with self.lock:
+ if self.running_performance:
+ logger.info('unloading')
+ self.running_performance = None
+ self.unload_attention_regions()
+ self.running_performance_pub.publish(String(json.dumps(None)))
+
+ def set_properties_callback(self, request):
+ self.ros.set_variable(request.id, json.loads(request.properties))
+ return srv.SetPropertiesResponse(success=True)
+
+ def load_callback(self, request):
+ return srv.LoadResponse(success=True, performance=json.dumps(self.load(request.id)))
+
+ def load_performance_callback(self, request):
+ self.load_performance(json.loads(request.performance))
+ return srv.LoadPerformanceResponse(True)
+
+ def run_by_name_callback(self, request):
+ self.stop()
+ # Load random timeline if its folder of timelines
+ if not self.load(request.id, random_timeline=True):
+ return srv.RunByNameResponse(False)
+ return srv.RunByNameResponse(self.run(0.0))
+
+ def run_full_performance(self, id, start_time=0.0, unload_finished=False):
+ self.stop()
+ performances = self.load_folder(id) or self.load(id)
+ if not performances:
+ return False
+ return self.run(start_time, unload_finished=unload_finished)
+
+ def run_full_performance_callback(self, request):
+ return self.run_full_performance(request.id, unload_finished=True)
+
+ def load_folder(self, id):
+ if id.startswith('shared'):
+ robot_name = 'common'
+ else:
+ robot_name = rospy.get_param('/hr/robot_name')
+
+ dir_path = os.path.join(self.get_path_by_robot_name(robot_name), id)
+ if os.path.isdir(dir_path):
+ root, dirs, files = next(os.walk(dir_path))
+
+ files = fnmatch.filter(files, "*.yaml")
+ if not files:
+ # If no folder is picked one directory
+ # Sub-directories are counted as sub-performances
+ if not dirs:
+ return []
+ if id in self.performances_played:
+ # All performances played. Pick any but last played
+ if set(self.performances_played[id]) == set(dirs):
+ dirs = self.performances_played[id][:-1]
+ self.performances_played[id] = []
+ else:
+ # Pick from not played performances
+ dirs = list(set(dirs) - set(self.performances_played[id]))
+ else:
+ self.performances_played[id] = []
+ # Pick random performance
+ p = random.choice(dirs)
+ self.performances_played[id].append(p)
+ return self.load_folder(os.path.join(id, p))
+ # make names in folder/file format
+ return self.load(id)
+ return []
+
+ def load(self, id, random_timeline=False):
+ robot_name = 'common' if id.startswith('shared') else rospy.get_param('/hr/robot_name')
+ p = os.path.join(self.get_path_by_robot_name(robot_name), id)
+
+ if os.path.isdir(p):
+ root, dirs, files = next(os.walk(p))
+ files = natsorted(fnmatch.filter(files, "*.yaml"), key=lambda f: f.lower())
+ ids = ["{}/{}".format(id, f[:-5]) for f in files]
+ timelines = [self.get_timeline(i) for i in ids]
+ timelines = [t for t in timelines if t]
+ if random_timeline:
+ performance = random.choice(timelines)
+ else:
+ performance = {'id': id, 'name': os.path.basename(id), 'path': os.path.dirname(id),
+ 'timelines': timelines, 'nodes': self.get_merged_timeline_nodes(timelines)}
+ else:
+ performance = self.get_timeline(id)
+
+ if performance:
+ self.load_performance(performance)
+ return performance
+ else:
+ return None
+
+ def get_path_by_robot_name(self, name):
+ return os.path.join(self.performances_dir, name)
+
+ def get_timeline(self, id):
+ timeline = None
+ robot_name = 'common' if id.startswith('shared') else rospy.get_param('/hr/robot_name')
+
+ # search timeline, case insensitive
+ performance_dir = self.get_path_by_robot_name(robot_name)
+ target = '%s.yaml' % id.lower()
+ p = None
+ for f in Path(performance_dir).glob('**/*.yaml'):
+ name = str(f.relative_to(performance_dir))
+ if name.lower() == target:
+ p = f
+ if p:
+ try:
+ with p.open() as f:
+ timeline = yaml.safe_load(f.read())
+ timeline['id'] = id
+ timeline['name'] = os.path.basename(id)
+ timeline['path'] = os.path.dirname(id)
+ self.validate_timeline(timeline)
+ except Exception as ex:
+ logger.error(ex)
+ else:
+ logger.error("Timeline with id %s was not found", id)
+ return timeline
+
+ def get_timeline_duration(self, timeline):
+ duration = 0
+
+ if 'nodes' in timeline and isinstance(timeline['nodes'], list):
+ for node in timeline['nodes']:
+ duration = max(duration, (node['duration'] if 'duration' in node else 0) + node['start_time'])
+
+ return duration
+
+ def get_merged_timeline_nodes(self, timelines):
+ merged = []
+ offset = 0
+
+ for timeline in timelines:
+ if 'enabled' in timeline and not timeline['enabled']:
+ continue
+
+ duration = 0
+ nodes = timeline.get('nodes', [])
+ nodes = copy.deepcopy(nodes)
+
+ for node in nodes:
+ duration = max(duration, node['duration'] + node['start_time'])
+ node['start_time'] += offset
+
+ merged += nodes
+ offset += duration
+
+ return merged
+
+ def validate_performance(self, performance):
+ self.validate_timeline(performance)
+ if 'timelines' in performance:
+ for timeline in performance['timelines']:
+ self.validate_timeline(timeline)
+ return performance
+
+ def validate_timeline(self, timeline):
+ if 'nodes' not in timeline or not isinstance(timeline['nodes'], list):
+ timeline['nodes'] = []
+
+ for node in timeline['nodes']:
+ if 'start_time' not in node:
+ node['start_time'] = 0
+ if node['name'] == 'pause':
+ node['duration'] = 0.1
+ if 'duration' not in node or not node['duration']:
+ node['duration'] = 0
+
+ return timeline
+
+ def load_performance(self, performance):
+ with self.lock:
+ logger.info('load: {0}'.format(performance.get('id', 'NO ID')))
+ self.validate_performance(performance)
+ self.load_attention_regions(performance.get('id','invalid'))
+ self.running_performance = performance
+ self.running_performance_pub.publish(String(json.dumps(performance)))
+
+ def run_callback(self, request):
+ return srv.RunResponse(self.run(request.startTime))
+
+ def load_attention_regions(self, id):
+ regions = rospy.get_param(
+ '/' + os.path.join('/hr', "control/performances", id,
+ "properties/regions"), [])
+ rospy.set_param('/hr/control/performance_regions', regions)
+
+ def unload_attention_regions(self):
+ rospy.set_param('/hr/control/performance_regions', [])
+
+ def run(self, start_time, unload_finished=False):
+ start_time = float(start_time or 0)
+ self.stop()
+ # Wait for worker to stop performance and enter waiting before proceeding
+ self.run_condition.acquire()
+ with self.lock:
+ success = self.running_performance and len(self.running_performance) > 0
+ if success:
+ self.unload_finished = unload_finished
+ self.running = True
+ self.start_time = start_time
+ self.start_timestamp = time.time()
+ log_data = {
+ 'performance_report': True,
+ 'performance_id': self.running_performance.get('id', ''),
+ 'performance_time': start_time,
+ 'performance_action': 'run'
+ }
+ logger.info('Running performance #{} at: {}'.format(log_data['performance_id'], start_time), extra={'data': log_data})
+ # notify worker thread
+ self.run_condition.notify()
+
+
+ self.run_condition.release()
+ return success
+
+ def resume_callback(self, request):
+ success = self.resume()
+ with self.lock:
+ run_time = self.get_run_time()
+
+ return srv.PerformanceCommandResponse(success, run_time)
+
+ def resume(self):
+ success = False
+ with self.lock:
+ if self.running and self.paused:
+ run_time = self.get_run_time()
+ self.paused = False
+ self.start_timestamp = time.time() - run_time
+ self.start_time = 0
+ self.events_pub.publish(Event('resume', run_time))
+ log_data = {
+ 'performance_report': True,
+ 'performance_id': self.running_performance.get('id', ''),
+ 'performance_time': run_time,
+ 'performance_action': 'resume'
+ }
+ logger.info('Resume performance #{} at: {}'.format(log_data['performance_id'], run_time), extra={'data': log_data})
+ success = True
+
+ return success
+
+ def stop(self):
+ stop_time = 0.0
+ with self.lock:
+ if self.running:
+ stop_time = self.get_run_time()
+ for node in self.running_nodes:
+ node.stop(stop_time)
+ self.running = False
+ self.paused = False
+ if self.interrupt_tts:
+ self.ros.topics['tts_control'].publish('shutup')
+ log_data = {
+ 'performance_report': True,
+ 'performance_id': self.running_performance.get('id', '') if self.running_performance else '',
+ 'performance_time': stop_time,
+ 'performance_action': 'stop'
+ }
+ logger.info('Stopping performance #{} at: {}'.format(log_data['performance_id'], stop_time), extra={'data': log_data})
+ return stop_time
+
+ def stop_callback(self, request=None):
+ return srv.PerformanceCommandResponse(True, self.stop())
+
+ def pause_callback(self, request):
+ if self.pause():
+ with self.lock:
+ return srv.PerformanceCommandResponse(True, self.get_run_time())
+ else:
+ return srv.PerformanceCommandResponse(False, 0)
+
+ # Pauses current
+ def pause(self):
+ with self.lock:
+ if self.running and not self.paused:
+ self.pause_time = time.time()
+ self.paused = True
+ paused_time = self.get_run_time()
+ self.events_pub.publish(Event('paused', paused_time))
+ log_data = {
+ 'performance_report': True,
+ 'performance_id': self.running_performance.get('id', ''),
+ 'performance_time': paused_time,
+ 'performance_action': 'paused'
+ }
+ logger.info('Pause performance #{} at: {}'.format(log_data['performance_id'], paused_time), extra={'data': log_data})
+ return True
+ else:
+ return False
+
+ # Returns current performance
+ def current_callback(self, request):
+ with self.lock:
+ current_time = self.get_run_time()
+ running = self.running and not self.paused
+ return srv.CurrentResponse(performance=json.dumps(self.running_performance),
+ current_time=current_time,
+ running=running)
+
+ def check_is_performance_loaded(self, request):
+ success = self.running_performance and len(self.running_performance) > 0
+ return TriggerResponse(success=success)
+
+ def interrupt(self):
+ with self.lock:
+ if self.running_performance:
+ self.interrupted_performances.append({
+ 'performance': self.running_performance,
+ 'time': self.get_run_time(),
+ # need to store this for node objects to stay alive and for callbacks to work
+ 'nodes': self.running_nodes
+ })
+ self.stop()
+
+ def resume_interrupted(self):
+ found = len(self.interrupted_performances)
+
+ if found:
+ data = self.interrupted_performances.pop()
+ time = data['time']
+ logger.info('Resuming interrupted #{0} at {1}. {2} performances left'.format(data['performance']
+ .get('id', ''), time, found - 1))
+ self.load_performance(data['performance'])
+ self.run(time)
+
+ return found
+
+ def append_to_queue(self, id, time=0):
+ logger.info('Adding performance #{0} to the queue scheduled to run at {1}'.format(id, time))
+ self.queue.append({'id': id, 'time': time})
+
+ def load_scheduled(self):
+ data = None
+ with self.lock:
+ not_empty = len(self.queue)
+ if not_empty:
+ data = self.queue.pop()
+
+ if not_empty:
+ logger.info('Loading performance #{0} at {1} from the queue'.format(data['id'], data['time']))
+ self.run_full_performance(data['id'], start_time=data['time'])
+ elif not self.resume_interrupted():
+ return False
+
+ return True
+
+ def worker_loop(self):
+ self.run_condition.acquire()
+ while True:
+ with self.lock:
+ self.paused = False
+ self.running = False
+
+ self.events_pub.publish(Event('idle', 0))
+ self.run_condition.wait()
+ self.events_pub.publish(Event('running', self.start_time))
+
+ with self.lock:
+ if not self.running_performance:
+ continue
+
+ behavior = True
+ offset = 0
+ timelines = self.running_performance['timelines'] if 'timelines' in self.running_performance else [
+ self.running_performance]
+
+ for i, timeline in enumerate(timelines):
+ if 'enabled' in timeline and not timeline['enabled']:
+ continue
+
+ # check if performance is finished without starting
+ running = True
+ self.running_nodes = [Node.createNode(node, ros, self.start_time - offset, timeline.get('id', ''),
+ runner=self) for node in timeline['nodes']]
+ pid = timeline.get('id', '')
+ finished = None
+ run_time = 0
+ pause = pid and self.get_property(os.path.dirname(pid), 'pause_behavior')
+ # Pause must be either enabled or not set (by default all performances are
+ # pausing behavior if its not set)
+
+ with self.lock:
+ if not self.running:
+ break
+
+ while running:
+ if not finished:
+ # Wait for a bit.
+ time.sleep(0.02)
+ with self.lock:
+ run_time = self.get_run_time()
+ if not self.running:
+ self.events_pub.publish(Event('finished', run_time))
+ break
+ if self.paused:
+ continue
+
+ running = False
+ # checks if any nodes still running
+ for k, node in enumerate(self.running_nodes):
+ running = node.run(run_time - offset) or running
+
+ if finished is None:
+ # true if all performance nodes are already finished
+ finished = not running
+
+
+ log_data = {
+ 'performance_report': True,
+ 'performance_id': self.running_performance.get('id', '') if self.running_performance else '',
+ 'performance_time': run_time,
+ 'performance_action': 'finished'
+ }
+ if i == len(timelines) - 1:
+ logger.warning('Finished performance #{} at: {}'.format(log_data['performance_id'], run_time), extra={'data': log_data})
+
+ offset += self.get_timeline_duration(timeline)
+
+ with self.lock:
+ autopause = self.autopause and finished is False and i < len(timelines) - 1
+
+ if autopause:
+ self.pause()
+
+ if self.unload_finished:
+ self.unload_finished = False
+ self.unload()
+
+ def get_run_time(self):
+ """
+ Must acquire self.lock in order to safely use this method
+ :return:
+ """
+ run_time = 0
+
+ if self.running:
+ run_time = self.start_time
+ if self.paused:
+ run_time += self.pause_time - self.start_timestamp
+ else:
+ run_time += time.time() - self.start_timestamp
+
+ return run_time
+
+
+
+ def load_properties(self):
+ robot_name = rospy.get_param('/hr/robot_name')
+ robot_path = os.path.join(self.performances_dir, robot_name)
+ common_path = os.path.join(self.performances_dir, 'common')
+ for path in [common_path, robot_path]:
+ for root, dirnames, filenames in os.walk(path):
+ if '.properties' in filenames:
+ filename = os.path.join(root, '.properties')
+ if os.path.isfile(filename):
+ try:
+ with open(filename) as f:
+ properties = yaml.safe_load(f.read())
+ dir = os.path.relpath(root, path)
+ rospy.set_param(os.path.join('/hr/control/performances', dir).strip(
+ "/.") + '/properties', properties)
+ except:
+ rospy.logerr("Cant load properties file for {}".format(dir))
+
+
+ def get_property(self, path, name):
+ param_name = os.path.join('/hr/control/performances', path, 'properties', name)
+ return rospy.get_param(param_name, None)
+
+
+
+
+ def runner_event_callback(self, msg):
+ logger.info('Runner event: {0}'.format(msg.event))
+ self.ros.notify('RUNNER', msg.event)
+ if msg.event == 'idle':
+ self.load_scheduled()
+
+ @staticmethod
+ def is_param(param):
+ """ Checks if value is valid param.
+ Has to start with slash
+ """
+ validator = rospy.names.global_name("param_name")
+ try:
+ validator(param, False)
+ return True
+ except rospy.names.ParameterInvalid:
+ return False
+
+class SharedROSConnector:
+ # Class that provide ros interface for executing nodes, and can be shared by multiple runners
+ def __init__(self):
+ self.observers = {}
+ self.robot_name = rospy.get_param('/hr/robot_name')
+ self.variables = {}
+ self.topics = {
+ 'look_at': rospy.Publisher('/hr/animation/set_face_target', Target, queue_size=1),
+ 'gaze_at': rospy.Publisher('/hr/animation/set_gaze_target', Target, queue_size=1),
+ 'head_rotation': rospy.Publisher('/hr/animation/set_head_tilt', Float32, queue_size=1),
+ 'emotion': rospy.Publisher('/hr/animation/set_expression', SetExpression, queue_size=3),
+ 'gesture': rospy.Publisher('/hr/animation/set_animation', SetAnimation, queue_size=3),
+ 'arm_animation': rospy.Publisher('/hr/animation/set_arm_animation', SetAnimation, queue_size=3),
+ # 'interaction': rospy.Publisher('/behavior_switch', String, queue_size=1),
+ # 'bt_control': rospy.Publisher('/behavior_control', Int32, queue_size=1),
+ # 'chatbot': rospy.Publisher('/' + self.robot_name + '/speech', ChatMessage, queue_size=1),
+ # 'speech_events': rospy.Publisher('/' + self.robot_name + '/speech_events', String, queue_size=1),
+ 'soma_state': rospy.Publisher("/hr/animation/set_soma_state", SomaState, queue_size=2),
+ 'tts': rospy.Publisher('/hr/control/speech/say', TTS, queue_size=1),
+ 'tts_control': rospy.Publisher('/hr/control/speech/tts_control', String, queue_size=1),
+ 'arf': rospy.Publisher('/hr/interaction/arf', String, queue_size=1)
+ }
+ # rospy.Subscriber('/' + self.robot_name + '/speech_events', String,
+ # lambda msg: self.notify('speech_events', msg))
+ # rospy.Subscriber('/' + self.robot_name + '/speech', ChatMessage, self.speech_callback)
+
+ @staticmethod
+ def is_param(param):
+ """ Checks if value is valid param.
+ Has to start with slash
+ """
+ validator = rospy.names.global_name("param_name")
+ try:
+ validator(param, False)
+ return True
+ except rospy.names.ParameterInvalid:
+ return False
+
+ def set_variable(self, id, properties):
+ for key, val in properties.items():
+ rospy.logerr("id {} key {} val {}".format(id, key, val))
+ if id in self.variables:
+ self.variables[id][key] = val
+ else:
+ self.variables[id] = {key: val}
+
+ def get_variable(self, id, name):
+ if os.path.dirname(id) in self.variables and name in self.variables[os.path.dirname(id)] \
+ and self.variables[os.path.dirname(id)][name]:
+ return self.variables[os.path.dirname(id)][name]
+ else:
+ val = None
+ param_name = os.path.join('/hr/webui/performances', os.path.dirname(id),
+ 'properties/variables', name)
+ if rospy.has_param(param_name):
+ val = rospy.get_param(param_name)
+ if self.is_param(val):
+ if rospy.has_param(val):
+ return str(rospy.get_param(val))
+ if rospy.has_param("/{}{}".format(self.robot_name, val)):
+ return str(rospy.get_param("/{}{}".format(self.robot_name, val)))
+
+ return None
+ return val
+
+ # Notifies register nodes on the events from ROS.
+ def notify(self, event, msg):
+ if event not in list(self.observers.keys()):
+ return
+ for i in range(len(self.observers[event]) - 1, -1, -1):
+ try:
+ self.observers[event][i](msg)
+ except TypeError as e:
+ # Remove dead methods
+ del self.observers[event][i]
+
+ # Registers callbacks for specific events. Uses weak reference to allow nodes cleanup after finish.
+ def register(self, event, cb):
+ if not event in self.observers:
+ self.observers[event] = []
+ m = WeakMethod(cb)
+ self.observers[event].append(m)
+ logger.info('Registering event for "{0}" which now has {1} handlers'.format(event, len(self.observers[event])))
+ return m
+
+ # Allows nodes to unsubscribe from events
+ def unregister(self, event, ref):
+ if event in self.observers:
+ if ref in self.observers[event]:
+ self.observers[event].remove(ref)
+ logger.info('Unregistering event handler for "{0}" which now has {1} handlers'
+ .format(event, len(self.observers[event])))
+
+ def speech_callback(self, msg):
+ self.notify('SPEECH', msg.utterance)
+
+
+
+
+if __name__ == '__main__':
+ rospy.init_node('performances')
+ ros = SharedROSConnector()
+ # Runner for UI
+ r1 = Runner(ros)
+ # Runner for background performances
+ r2 = Runner(ros, '/hr/control/performances/background/', background=True)
+ # Runner for automated gestures
+ r3 = Runner(ros, '/hr/control/performances/speech_animation/', background=True)
+
+ rospy.spin()
diff --git a/modules/performances/scripts/speech_motion_controller.py b/modules/performances/scripts/speech_motion_controller.py
new file mode 100755
index 0000000..fff5753
--- /dev/null
+++ b/modules/performances/scripts/speech_motion_controller.py
@@ -0,0 +1,243 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+##
+## Copyright (C) 2017-2025 Hanson Robotics
+##
+## This program is free software: you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see .
+##
+
+
+import os
+import rospy
+import yaml
+import json
+import threading
+import time
+import logging
+import re
+from performances.nodes import Node
+from haipy.speech_motion.speech_motion import SpeechMotionUtils, SpeechMotionController
+#from performances.speech_motion import SpeechMotionUtils, SpeechMotionController
+from std_msgs.msg import String
+from performances.cfg import RulesConfig, GeneratorConfig
+from hr_msgs.srv import LoadPerformance, Run
+from hr_msgs.srv import TTSData
+from dynamic_reconfigure.server import Server
+from copy import deepcopy
+import random
+logger = logging.getLogger('hr.performances.speech_motion_controller')
+
+# RosNode for speech motion
+# HEAD Specific ROS Controller
+class SpeechMotionNode():
+
+ def __init__(self):
+ # Rules server
+ self.config = None
+ aa_library = rospy.get_param('/hr/control/animation_actions', {})
+ nlu_server = rospy.get_param('/hr/control/nlu_server', "http://127.0.0.1:8210")
+ lipsync_delay = rospy.get_param('/hr/control/speech/tts_talker/tts_delay', 0.2)
+ self.controller = SpeechMotionController(aa_library, nlu_server=nlu_server, lipsync_delay=lipsync_delay)
+ self.rules_server = Server(RulesConfig, self.rules_config_cb, '/hr/control/speech_motions')
+ self.current_state = 'idle'
+ rospy.Subscriber('/hr/behavior/current_state', String, self.update_state)
+ rospy.Subscriber('/hr/control/speech/data', String, self.get_tts_data)
+ self.loader = rospy.ServiceProxy('/hr/control/performances/speech_animation/load_performance', LoadPerformance)
+ self.runner = rospy.ServiceProxy('/hr/control/performances/speech_animation/run', Run)
+ self.tts_ready = rospy.Publisher('/hr/control/speech/tts_control', String, queue_size=2)
+ self.id = 1
+
+ def get_tts_data(self, msg):
+ # no generation on interacting, also dont animate perfromances itself to avoid confusion
+ if (self.config.interactive and not 'interacting' in self.current_state) or '|p|' in msg.data:
+ self.tts_ready.publish('ready')
+ return
+ # Prefer JSON loads, if only json format is used
+ try:
+ data = json.loads(msg.data)
+ except Exception as e :
+ data = yaml.safe_load(msg.data)
+
+ nodes = SpeechMotionUtils.create_speech_data_from_tts_data(data)
+ animated = self.controller.animate_motion(nodes, animate_arms=self.config.automated_arm_gestures)
+ SpeechMotionController.clean_nodes(animated)
+ if len(animated) > len(nodes):
+ self.loader(json.dumps({'nodes': animated, 'id': "auto/{}".format(self.id)}, ensure_ascii=True))
+ self.id += 1
+ self.tts_ready.publish('ready')
+ self.runner(0)
+ else:
+ self.tts_ready.publish('ready')
+
+ def update_state(self, msg):
+ self.current_state = msg.data
+
+
+ def rules_config_cb(self, config, level):
+ if self.config is None:
+ self.controller.update_keyword_rules(yaml.safe_load(config.keyword_rules))
+ # # Sorted Rules on first load, later on the order should remain same to prevent unexpected UI sorting
+ config.keyword_rules = json.dumps(self.controller.get_keyword_rules())
+ self.controller.update_da_rules(yaml.safe_load(config.da_rules))
+ # # Sorted Rules on first load, later on the order should remain same to prevent unexpected UI sorting
+ config.da_rules = json.dumps(self.controller.get_da_rules())
+ self.config = config
+ if self.config.keyword_rules != config.keyword_rules:
+ self.controller.update_keyword_rules(yaml.safe_load(config.keyword_rules))
+ if self.config.da_rules != config.da_rules:
+ self.controller.update_da_rules(yaml.safe_load(config.da_rules))
+ self.config = config
+ self.controller.animated_shoulders = config.automated_shoulder_gestures
+ self.controller.min_arms_hold_time = config.min_arms_hold_time
+ self.controller.max_arms_hold_time = config.max_arms_hold_time
+ self.controller.skip_keyword_rules = not config.enable
+ self.controller.library.filter = config.negative_gesture_categories
+ self.controller.library.reduce_robability = config.reduce_negativity
+ self.controller.negative_gesture_categories = config.negative_gesture_categories
+ self.controller.set_gesture_filters(
+ config.rules_head_filter, config.rules_brow_filter, config.rules_eyelids_filter, config.rules_eyes_filter)
+ self.controller.happy_gestures_probability = config.generic_gestures_happy_filter
+ self.controller.set_generic_gestures_config(config.generic_gestures, config.generic_gestures_categories_excluded, \
+ config.generic_gestures_probability, config.generic_gestures_head_filter, \
+ config.generic_gestures_brow_filter, config.generic_gestures_eyelids_filter, \
+ config.generic_gestures_eyes_filter, config.generic_gestures_arms_filter)
+ return config
+
+
+# Performance generator for HEAD
+class PerformanceGenerator():
+
+ def __init__(self, speech_motion):
+ self.speech_motion = speech_motion
+
+ self.performances_dir = os.path.join(
+ os.environ.get('PERFORMANCES_DIR', '/home/hr/workspace/hrsdk_configs/performances'))
+ self.robot_name = rospy.get_param('/hr/robot_name')
+ self.lipsync_delay = rospy.get_param('/hr/control/speech/tts_talker/tts_delay', 0.2)
+ self.tts_data = rospy.ServiceProxy('/hr/control/speech/data', TTSData)
+ self.in_progress = False
+ self.generator = None
+ self.cfg = None
+ self.srv = Server(GeneratorConfig, self.update_cfg, namespace='/hr/control/performance_generation')
+
+ def update_cfg(self, cfg, level):
+ if self.cfg is None:
+ self.cfg = cfg
+ return cfg
+ # Update progress
+ if self.in_progress:
+ self.cfg.progress = cfg.progress
+ return self.cfg
+ min_speed = min(cfg.min_speed, cfg.max_speed)
+ max_speed = max(cfg.min_speed, cfg.max_speed)
+ cfg.min_speed = min_speed
+ cfg.max_speed = max_speed
+ # Start Generation
+ cfg.name = cfg.name.strip()
+ cfg.name = cfg.name.replace(' ', '_')
+ cfg.name = cfg.name.replace('.', '_')
+ cfg.name = re.sub('[\W]&&[^/]+', '', cfg.name)
+ cfg.name =cfg.name.lower()
+ if cfg.start:
+ cfg.start = False
+ if cfg.name == "":
+ cfg.status = "Performance name is required"
+ self.cfg = cfg
+ return cfg
+ self.in_progress = True
+ self.cfg = cfg
+ self.generator = threading.Thread(target=self.genearate)
+ self.generator.setDaemon(True)
+ self.generator.start()
+ else:
+ self.cfg = cfg
+ return self.cfg
+
+ def add_speed_ssml(self, text, speed):
+ try:
+ if speed == 100:
+ return text
+ return '{}'.format(speed, text)
+ except Exception:
+ return text
+
+ def genearate(self):
+ current_loc = 'start'
+ try:
+ lines = self.cfg.lines.splitlines()
+ for i, l in enumerate(lines):
+ current_loc = str(i+1)
+ self.srv.update_configuration({'progress': 'Working on {} of {}'.format(i + 1, len(lines))})
+ sentences = SpeechMotionUtils.split_text(l, split_sentences=self.cfg.split_sentences)
+ nodes = []
+ current_sentence_start = 0
+ for s in sentences:
+ try:
+ s = s.lower()
+ try:
+ s = str(s)
+ except:
+ pass
+ # Try add speed
+ try:
+ speed = random.randint(self.cfg.min_speed, self.cfg.max_speed)
+ except Exception:
+ speed = 100
+
+ s_with_speed = self.add_speed_ssml(s, speed)
+ tts_data = self.tts_data(s_with_speed, self.cfg.lang)
+ tts_data = yaml.safe_load(tts_data.data)
+ # matched with TTS words
+ text, text_words, words = SpeechMotionUtils.match_tts(tts_data, s)
+ if not text:
+ continue
+ # lipsync_delay needed to be added as sound file needs to be slightly delayed for lipsynch,
+ # therefore actual duration is longer
+ node = Node.create_empty_node_data(type='speech', duration=text['duration']+0.3, text=s,
+ start_time=current_sentence_start,
+ tts_data=tts_data, words=words, text_words=text_words, lang=self.cfg.lang, speed=round(speed/100.0, 2))
+ nodes.append(node)
+ current_sentence_start += text['duration'] + self.lipsync_delay + self.cfg.pause_between_sentences
+ except Exception as e:
+ logger.error(u"Exception {} while generating {}".format(e, s))
+ # Wait for TTS server to recover
+ time.sleep(10)
+ pass
+ animated = self.speech_motion.controller.animate_motion(nodes)
+ nodes = nodes + animated
+ SpeechMotionController.clean_nodes(nodes)
+ if self.cfg.name != '':
+ # Allow shared performances generations
+ robot_name = 'common' if self.cfg.name.startswith('shared') else self.robot_name
+ filename = os.path.join(self.performances_dir, robot_name, self.cfg.name, "{}.yaml".format(i + 1))
+ if not os.path.exists(os.path.dirname(filename)):
+ os.makedirs(os.path.dirname(filename))
+ with open(filename, 'w') as f:
+ yaml.dump({'nodes': deepcopy(nodes)}, f)
+
+ self.srv.update_configuration({'progress': 'idle'})
+ self.in_progress = False
+ # Unhandled exceptions should show in UI, and not prevent to retry
+ except Exception as e:
+ logger.error(u"Exception {} while generating {}".format(e, current_loc))
+ self.srv.update_configuration({'progress': f'Error while generating line {current_loc} exception:{e}'})
+ self.in_progress = False
+
+
+if __name__ == "__main__":
+ rospy.init_node('SpeechMotionController')
+ rosnode = SpeechMotionNode()
+ generator = PerformanceGenerator(rosnode)
+ rospy.spin()
diff --git a/modules/performances/setup.py b/modules/performances/setup.py
new file mode 100644
index 0000000..886e8f8
--- /dev/null
+++ b/modules/performances/setup.py
@@ -0,0 +1,28 @@
+##
+## Copyright (C) 2017-2025 Hanson Robotics
+##
+## This program is free software: you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see .
+##
+
+# DO NOT USE
+# python setup.py install
+
+from distutils.core import setup
+
+setup(
+ version='0.3.11',
+ name='performances',
+ packages=['performances'],
+ package_dir={'': 'src'}
+)
diff --git a/modules/performances/src/performances/__init__.py b/modules/performances/src/performances/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/modules/performances/src/performances/config.yaml b/modules/performances/src/performances/config.yaml
new file mode 100644
index 0000000..a032881
--- /dev/null
+++ b/modules/performances/src/performances/config.yaml
@@ -0,0 +1,209 @@
+##
+## Copyright (C) 2017-2025 Hanson Robotics
+##
+## This program is free software: you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see .
+##
+
+speech_motions:
+ da_rules: '[{"browAA":"-","probability":1,"armsAA":"arm-vawe","priority":90,"eyelidsAA":"-","headAA":"-","act":"CONVENTIONAL_OPENING","threshold":0.5,"apply":"first","eyesAA":"-"},{"browAA":"-","probability":1,"armsAA":"arm-vawe","priority":90,"eyelidsAA":"-","headAA":"-","act":"CONVENTIONAL_CLOSING","threshold":0.5,"apply":"last","eyesAA":"-"}]'
+ enable: true
+ keyword_rules: '[{"priority": 30, "browAA": "-", "probability": 0.5,"headAA": "head-down","keywords": "me, my, mine, we, our, ours, ourselves, own, we, our, ours, ourselves, own, ", "armsAA": "-", "eyesAA": "-"},{"priority": 30, "browAA": "-", "probability": 0.5,"headAA": "head-aside","keywords": "yours, yourself, them, those, that, these, you, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.8,"headAA": "nod-short","keywords": " yes, yeah, i do, i am, we have, we do, you have, true, ok, i like, i love, you like, ya, ", "armsAA": "-", "eyesAA": "-"},{"priority": 100, "browAA": "-", "probability": 0.5,"headAA": "head-tilt","keywords": "love you, big love, big heart, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "nod-heavy","keywords": " agree, admit, accept, accustomed, acknowledged, allowed, approved, authorized, established, confirmed, supported, permitted, allow, comply, welcomed, agreed, grant, granted, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.8,"headAA": "shake-heavy","keywords": "no, nothing, cannot, can''t, unable, never, don''t know", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.8,"headAA": "nod-short","keywords": "correct, right, appropriate, equitable, factual, legitimate, perfect, precise, proper, strict, true, okay, accurately, justly, nicely, perfectly, precisely, properly, rightly, ", "armsAA": "-", "eyesAA": "-"},{"priority": 80, "browAA": "-", "probability": 0.8,"headAA": "shake-short","keywords": "incorrect, wrong, erroneous, false, faulty, flawed, imprecise, improper, inaccurate, inappropriate, mistaken, unreliable, unsound, untrue, not true, not okay, fake, untrue, ", "armsAA": "-", "eyesAA": "-"},{"priority": 80, "browAA": "-", "probability": 0.8,"headAA": "shake-short","keywords": "refuse, reject, decline, deny, ignore, trash, waste, wasted, dismiss, ", "armsAA": "-", "eyesAA": "-"},{"priority": 40, "browAA": "-", "probability": 0.5,"headAA": "head-tilt-long","keywords": "what, where, when, how, ", "armsAA": "-", "eyesAA": "-"},{"priority": 55, "browAA": "-", "probability": 0.5,"headAA": "head-tilt-long","keywords": "can you, could you, do you, ", "armsAA": "-", "eyesAA": "-"},{"priority": 50, "browAA": "-", "probability": 0.8,"headAA": "head-tilt-long","keywords": "well, i guess , i suppose, i think, maybe, perhaps, could, probably, might, actually, only, i believe, i suggest, likely, ", "armsAA": "-", "eyesAA": "-"},{"priority": 85, "browAA": "-", "probability": 0.5,"headAA": "head-aside","keywords": "well, i guess , i suppose, i think, maybe, perhaps, could, probably, might, actually, only, i believe, i suggest, likely, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.8,"headAA": "nod-short","keywords": "have to, must, need to, ought to, ought to, should, shall, going to, will, willing to, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.8,"headAA": "nod-heavy","keywords": "must, should, ought to, must need, ", "armsAA": "-", "eyesAA": "-"},{"priority": 50, "browAA": "-", "probability": 0.5,"headAA": "head-tilt","keywords": "you know, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "wobbling","keywords": "um, uh, well, let''s see, let me think", "armsAA": "-", "eyesAA": "-"},{"priority": 35, "browAA": "-", "probability": 0.8,"headAA": "head-tilt-long","keywords": "but, although, however, nevertheless, on the other hand, though, yet, aside from, other than, nonetheless, after all, despite, rather", "armsAA": "-", "eyesAA": "-"},{"priority": 35, "browAA": "-", "probability": 0.5,"headAA": "head-tilt-long","keywords": "and, along with, also, as a consequence, as well as, including, together with, besides, additionally, also, still, too, additionally, along with, as well, besides, in addition, likewise, withal, again, ", "armsAA": "-", "eyesAA": "-"},{"priority": 30, "browAA": "-", "probability": 0.5,"headAA": "head-tilt","keywords": "likewise, again, more, still, further, besides, additionally, furthermore, moreover, beyond, besides, as a consequence, better, longer, beyond, in addition, what''s more, better, longer, higher, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.8,"headAA": "head-down","keywords": "really, very, quite, completely, great, absolutely, just, quite, incredible, epic, certainly, actually, indeed, literally, surely, truly, undoubtely, unquestionably", "armsAA": "-", "eyesAA": "-"},{"priority": 50, "browAA": "-", "probability": 0.5,"headAA": "head-aside","keywords": "normal, ordinary, simple, general, familiar, typical, common, so so, regular, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.8,"headAA": "wobbling","keywords": "Profound, progressive, advanced, outstanding, Knowledgeable, enlightened, forward-looking, Visionary, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "nod-short","keywords": "alike, comparable, related, similar, familiar", "armsAA": "-", "eyesAA": "-"},{"priority": 65, "browAA": "-", "probability": 0.5,"headAA": "shake-short","keywords": "abnormal, atypical, different, irregular, special, uncommon, unconventional, untraditional, unusual, exceptional, rare, anomalous, changing, disorderly, eccentric, extraordinary, imbalanced, inconsistent, infrequent, unsteady, variable, outstanding, exclusive, ", "armsAA": "-", "eyesAA": "-"},{"priority": 65, "browAA": "-", "probability": 0.5,"headAA": "head-tilt","keywords": "contradict, diverge, vary, bracket, collate, conflict, depart, deviate, differentiate, mismatch, oppose, separate", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "head-tilt","keywords": "rare, extraordinary, limited, occasional, scarce, singular, strange, subtle, uncommon, unique, unlikely, unusual, narrow, cramped, definite, limited, ", "armsAA": "-", "eyesAA": "-"},{"priority": 75, "browAA": "-", "probability": 0.8,"headAA": "head-aside","keywords": "sad, bad, disgust, worse, shame, evil, inappropriate, rotten, nasty, horrible, unhappy, pathetic, deplorable, feeble, heartbreaking, miserable, pitiful, poignant, sorry, woeful, ", "armsAA": "-", "eyesAA": "-"},{"priority": 75, "browAA": "-", "probability": 0.8,"headAA": "head-tilt-long","keywords": "always, consistently, constantly, invariably, regularly, repeatedly, perpetually, ", "armsAA": "-", "eyesAA": "-"},{"priority": 50, "browAA": "-", "probability": 0.5,"headAA": "head-down","keywords": "stop, pause, freeze, bar, block, break, conclusion, pause, drop, end, hold on, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.8,"headAA": "head-tilt","keywords": " continue, advance, endure, extend, go on, will last, linger, maintain, progress, promote, pursue, remain, will stay, survive, sustain, hold, sustaining, continuing, lasting, enduring, extending, remaining, promoting, progressing, lingering, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "head-down","keywords": "done, finished, completed, compassed, complete, completed, concluded, consummated, depleted, drained, effected, ended, executed, exhausted, fixed, fulfilled, perfected, performed, realized, rendered, succeeded, terminated, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "shake-long","keywords": "uncertain, ambiguous,ambivalent, dubious, erratic, hazy, hesitant, insecure, precarious, questionable, risky, unclear, undecided, undetermined, unpredictable, unreliable,unresolved, unsettled, unsure, suspicous, doubtable, doubtful, questionable, ambiguous, arguable, puzzling, ", "armsAA": "-", "eyesAA": "-"},{"priority": 50, "browAA": "-", "probability": 0.5,"headAA": "head-tilt-long","keywords": "kind of, slightly, lightly, somewhat, at least, rather, moderately, sort of, to some extent, at least", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.8,"headAA": "shake-firmly","keywords": "of course, sure, absolutely, actually, certainly, genuinely, honestly, indeed, legitimately, literally, surely, truly, undoubtedly, unquestionably, well, actual, certain, definite, substantial, substantive, absolute, authentic, concrete, confirmed, factual, for real, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "head-down","keywords": "conflicting, oppose, argue, assail, attack, debate, defy, deny, disagree, dispute, fight, prevent, protest, resist, assault, bar, battle, bombard, combat, confront, contradict, controvert, counter, counterattack, disapprove, encounter, expose, gainsay, hinder neutralize, reverse, taunt, thwart, withstand, object to, opposed, abide, combat, confront, continue, curb, defy, endure, forgo, maintain, prevent, refuse, repel, thwart, turn down, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.8,"headAA": "Wobbling","keywords": "adaptable, adventurous, affable, affectionate, agreeable, ambitious, amiable, amicable, amusing, brave, bright, broad-minded, calm, careful, charming, communicative, compassionate, conscientious, considerate, convivial, courageous, courteous, creative, decisive, determined, diligent, diplomatic, discreet, dynamic, easygoing, emotional, energetic, enthusiastic, exuberant, fair-minded, faithful, fearless, forceful, friendly, funny, generous, gentle, good, gregarious, hard-working, helpful, honest, humorous, imaginative, impartial, independent, intellectual, intelligent, intuitive, inventive, kind, loving, loyal, modest, neat, nice, optimistic, passionate, patient, persistent, pioneering, philosophical, placid, plucky, polite, powerful, practical, pro-active, quick-witted, quiet, rational, reliable, reserved, resourceful, romantic, self-confident, self-disciplined, sensible, sensitive, shy, sincere, sociable, straightforward, sympathetic, thoughtful, tidy, tough, unassuming, understanding, versatile, warmhearted, willing, witty, attractive, absorbing, alluring, amiable, appealing, attractive, charismatic, cute, delightful, elegant, engaging, engrossing, fascinating, glamorous, graceful, inviting, likable, lovable, lovely, pleasant, provocative, sweet, loyal, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "head-tilt-long","keywords": "beautiful, appealing, charming, cute, dazzling, delicate, delightful, elegant, exquisite, fascinating, good-looking, gorgeous, graceful, grand, handsome, lovely, magnificent, marvelous, pleasing, pretty, splendid, stunning, superb, wonderful, admirable, angelic, beauteous, bewitching, classy, famous, honored, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "shake-long","keywords": "Arrogant, Big-headed, Self-centred, Vain, Boastful, Pompous, Aloof, Impolite, Inconsiderate, Thoughtless, Confrontational, Defensive, Hostile, Belligerent, Bitchy, Nasty, Bossy, Cruel, Domineering, Sneaky, Impatient, Unreliable, Jealous, Careless, Irresponsible, Untidy, Cowardly, Foolish, Gullible, Indecisive, Weak-willed, Grumpy, Silly, infamous, disgraceful, egregious, hateful, heinous, ignominious, despicable, humiliating, shameful, immoral, corrupt, depraved, dishonest, indecent, nefarious, obscene, pornographic, shameless, sinful, unethical, unscrupulous, corrupted, ", "armsAA": "-", "eyesAA": "-"},{"priority": 75, "browAA": "-", "probability": 0.5,"headAA": "shake-firmly","keywords": "evil, gloomy, nasty, ominous, rough, evil, gloomy, nasty, ominous, rough, unattractive, unpleasant, disagreeable,disgusting, repugnant, repulsive, unappealing, bad-looking, gross, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.8,"headAA": "head-aside","keywords": "suspicious, doubtful", "armsAA": "-", "eyesAA": "-"},{"priority": 65, "browAA": "-", "probability": 0.8,"headAA": "nod-long","keywords": "cheerful, contented, delighted, ecstatic, elated, glad, joyful, joyous, jubilant, livelymerry,overjoyed, peaceful, pleasant, pleased, thrilled, upbeat, blessed", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "head-up","keywords": "exciting, appealing, astonishing, breathtaking, dramatic, flashy, hectic, impressive, interesting, intriguing, lively, provocative, stimulating, thrilling, agitated, annoyed, delighted, disturbed, eager, enthusiastic, hysterical, nervous, passionate, thrilled, animated, aroused, awakened, charged, discomposed, disconcerted, inflamed, moved, piqued, provoked, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "shake-long","keywords": "sad, bad, disgust, worse, shame, evil, inappropriate, rotten, nasty, horrible, unhappy, pathetic, deplorable, feeble, heartbreaking, miserable, pitiful, poignant, sorry, woeful, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "head-tilt","keywords": "sad, bad, disgust, worse, shame, evil, inappropriate, rotten, nasty, horrible, unhappy, pathetic, deplorable, feeble, heartbreaking, miserable, pitiful, poignant, sorry, woeful, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "shake-long","keywords": "Sorry, sad, pitiful, apologies, apology, sorrowful,unhappy,", "armsAA": "-", "eyesAA": "-"},{"priority": 30, "browAA": "-", "probability": 0.5,"headAA": "head-tilt","keywords": "hello, welcome, bye, see you, good bye, Hi, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "shake-long","keywords": "panic, scare, anxious, nervous, scared, scary, frightened, suspicious, chilling, concern, worry, horror, horrify, fear, spooky, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "shake-short","keywords": "contradict, diverge, vary, bracket, collate, conflict, depart, deviate, differentiate, mismatch, oppose, separate, ", "armsAA": "-", "eyesAA": "-"},{"priority": 80, "browAA": "-", "probability": 0.8,"headAA": "shake-firmly","keywords": "angry, annoyed, bitter, enraged, exasperated, furious, heated, impassioned, indignant, irate, irritable, irritated, offended, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.8,"headAA": "shake-long","keywords": "depressing, disconcerting, discouraging, disheartening, distasteful, frustrating, mediocre, unpleasant, unsatisfying, bitter, displeasing, failing, ", "armsAA": "-", "eyesAA": "-"},{"priority": 40, "browAA": "-", "probability": 0.8,"headAA": "wobbling","keywords": "love, like, wonder, admire, adore, approve, cherish, love, commend, compliment", "armsAA": "-", "eyesAA": "-"},{"priority": 40, "browAA": "-", "probability": 0.5,"headAA": "nod-short","keywords": "valuable, good at, useful, worthy, skilled, experienced, proficient, expert, competent, efficient, experienced, licensed, qualified, skillful, professional, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "shake-long","keywords": "unhappy, aversion, disapproval, disgust, displeasure, dislike,", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "head-down","keywords": "hate", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.8,"headAA": "wobbling","keywords": "complex, headache, baffle, befuddle, bemuse, complicate, confound, daze, demoralize, disconcert, disorient, distract, embarrass, fluster, frustrate, involve, misinform, mislead, baffling, bewildering, complex, complicated, confounding, difficult, disconcerting, perplexing, upsetting", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "head-tilt","keywords": "call me. contact me, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "head-down","keywords": "altercation, argue, battle, brawl,clash, combat, conflict, confrontation, contest, controversy, disagreement, dispute, duel, exchange, feud, match, melee, quarrel, riot, rivalry, scuffle, skirmish, struggle, war, wrangling, fight, attack, battle, challenge, clash, protect, resist, argue, combat, engage in, force, oppose, resist, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "head-tilt","keywords": "altercation, arguing, clashing, combating, conflicting, confrontation, Struggling, wrangling, fighting, attacking, clashing, resisting, arguing, combating, forcing, opposing, resisting,", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "shake-firmly","keywords": "destroy, consume, crush, damage, dismantle, eradicate, gut, impair, kill, maim, ravage, raze, ruin, sabotage, shatter, smash, wipe out, wreck, destroying, damaging, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "nod-short","keywords": "dependable, enduring, lasting, permanent, reliable, stable, strong, tenacious, abiding, constant, diuturnal, fast, firm, fixed, reliable, safe, solid, substantial, abiding, enduring, high tech, advanced, leading, cutting-edge, forward, excellent, distinguished, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "shake-short","keywords": "poor, poorly, impoverished, inconsiderable, insignificant, miniature, poor, teeny, thin, tiny, unimportant, delicate, feeble, frail, weak, fragile, junky, crummy, cheesy, crappy, cruddy, garbage, disappointing, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "head-up","keywords": "huge, enormous, extensive, giant, gigantic, great, humongous, immense, magnificent, mammoth, massive, tremendous, vast, immeasurable, oversize, big, large, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "head-tilt","keywords": "cramped, limited, meager, microscopic, miniature, minuscule, modest, narrow, paltry, poor, short, slight, small-scale, young, baby, bantam, diminutive, mini, cramped, limited, meager, microscopic, miniature, minuscule, narrow, paltry, poor, short, slight, small-scale, young, baby, bantam, diminutive, little, miniminute, petite, petty, scanty, shrimp, toy, trifling, bitty, humble, immature, inadequate, inconsequential, inconsiderable, insufficient, picayune, piddling, pint-sized, pitiful, pocket-sized, puny, runty, scrubby, stunted, tensy, teeny, trivial, undersized, unpretentious", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.2,"headAA": "head-tilt-long","keywords": "long, deep, great, high, lengthy, protracted, tall, continued, elongate, elongated, enduring, enlarged, expanded, lasting, lengthened, lingering, prolonged, running, stretch, stretching, sustained, towering, distant, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "head-tilt","keywords": "short", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "head-aside","keywords": "longer, larger", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.8,"headAA": "head-down","keywords": "shorter, briefer, curtailed, diminished, less, lessened, lower,more concise, reduced, reducing, diminishing, briefing, lowering, lesser ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.8,"headAA": "nod-short","keywords": "enlarge, bigger, more, larger, largest, better, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.2,"headAA": "head-tilt","keywords": "a few, a little, a bit, barely, hardly, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "shake-firmly","keywords": "none, nothing, noway, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "head-tilt","keywords": "a lot, many, massive, lots of, countless, substantial, considerable, extensive, enormous, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.8,"headAA": "Wobbling","keywords": "entirely, exactly, fully, purely, totally, wholly, utterly, completely", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "wobbling","keywords": "everything, all, whole, several, plenty, full, entire, exact, full, quite, total, altogether, all in all, just, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.8,"headAA": "head-tilt","keywords": "exclusive, integral, total, unabridged, choate, completed, concentrated, conclusive, consummate, every, exhaustive, fixed, fulfilled, full-length, in one piece, inclusive, outright, plenary, rounded, unabbreviated, uncut, undivided, unexpurgated, unqualified, utter, exceptional, conclusive, certain, accomplished, concentrated, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "wobbling","keywords": "important, considerable, leading, main, popular, powerful, serious, significant, substantial, valuable, big league, big-time, consequential, eminent, heavy-duty, heavyweight, influential, major league, material, meaningful, momentous, paramount, prime, principal, prominent, super, super colossal, weighty, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.8,"headAA": "nod-long","keywords": "contradict, diverge, vary, bracket, collate, conflict, depart, deviate, differentiate, mismatch, oppose, separate", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.8,"headAA": "nod-heavy","keywords": "amazing, affected, affect, affecting, astonishing, astounding, impressing, perplex, , stunning, awesome, amazed, astonish, impresive, outstanding, phenomenal", "armsAA": "-", "eyesAA": "-"},{"priority": , "browAA": "-", "probability": 0.5,"headAA": "head-tilt-long","keywords": "comfortable, elementary, reachable, possible, comfy, peaceful, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "nod-short","keywords": "lovely, nicely, cute, sweet, cheerful,delighted, glad, pleasant, alluring, captivating, delicate, delicious, delightful, enchanting, engaging, exquisite, gorgeous, graceful, handsome, pleasant, pleasing, pretty, splendid, stunning, sweet, adorable, charming", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.8,"headAA": "nod-heavy","keywords": "astonishing, astounding, awesome, breathtaking, fantastic, incredible, marvelous, outrageous, phenomenal, remarkable, spectacular, superb, terrific, unbelievable, fabulous, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "shake-short","keywords": "narrow, cramped, definite, limited, precarious, precise, slender, slim, thin, tight, sad, bad, disgust, worse, shame, evil, inappropriate, rotten, nasty, horrible, debris, droppings, junk, litter, residue, rubbish, rubble, sediment, waste,awful, cheap, crummy, dreadful, lousy, poor, rough, sad, unacceptable, blah, bummer, diddly, downer, garbage, gross, imperfect, inferior, junky, synthetic, abominable, amiss, bad news, beastly, bottom out, careless, cheesy, crappy, cruddy, defective ,deficient, dissatisfactory, erroneous, fallacious, faulty, godawful, grody, grungy, depressing, disconcerting, discouraging, disheartening, distasteful, frustrating, mediocre, unpleasant, unsatisfying, awkward, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.8,"headAA": "shake-heavy","keywords": "awful, distressing, dreadful, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "head-tilt","keywords": "difficult, tough, ambitious, arduous, burdensome, challenging, troublesome, puzzle, impossible, hard, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "head-tilt","keywords": "easy, accessible,clear,effortless,obvious,painless,simple,smooth,straightforward,uncomplicated, reachable, cozy", "armsAA": "-", "eyesAA": "-"},{"priority": 30, "browAA": "-", "probability": 0.5,"headAA": "head-tilt","keywords": "me, my, mine, we, our, ours, ourselves, own, we, our, ours, ourselves, own, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.8,"headAA": "head-tilt","keywords": " yes, yeah, i do, i am, we have, we do, you have, true, ok, i like, i love, you like, ya, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "nod-long","keywords": " agree, admit, accept, accustomed, acknowledged, allowed, approved, authorized, established, confirmed, supported, permitted, allow, comply, welcomed, agreed, grant, granted, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.8,"headAA": "shake-short","keywords": "no, nothing, cannot, can''t, unable, never, don''t know", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.8,"headAA": "head-tilt","keywords": "correct, right, appropriate, equitable, factual, legitimate, perfect, precise, proper, strict, true, okay, accurately, justly, nicely, perfectly, precisely, properly, rightly, ", "armsAA": "-", "eyesAA": "-"},{"priority": 80, "browAA": "-", "probability": 0.8,"headAA": "nod-long","keywords": "incorrect, wrong, erroneous, false, faulty, flawed, imprecise, improper, inaccurate, inappropriate, mistaken, unreliable, unsound, untrue, not true, not okay, fake, untrue, ", "armsAA": "-", "eyesAA": "-"},{"priority": 80, "browAA": "-", "probability": 0.8,"headAA": "head-aside","keywords": "refuse, reject, decline, deny, ignore, trash, waste, wasted, dismiss, ", "armsAA": "-", "eyesAA": "-"},{"priority": 40, "browAA": "-", "probability": 0.5,"headAA": "head-aside","keywords": "what, where, when, how, ", "armsAA": "-", "eyesAA": "-"},{"priority": 55, "browAA": "-", "probability": 0.5,"headAA": "head-aside","keywords": "can you, could you, do you, ", "armsAA": "-", "eyesAA": "-"},{"priority": 50, "browAA": "-", "probability": 0.8,"headAA": "wobbling","keywords": "well, i guess , i suppose, i think, maybe, perhaps, could, probably, might, actually, only, i believe, i suggest, likely, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.8,"headAA": "head-tilt","keywords": "have to, must, need to, ought to, ought to, should, shall, going to, will, willing to, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.8,"headAA": "shake-firmly","keywords": "must, should, ought to, must need, ", "armsAA": "-", "eyesAA": "-"},{"priority": 50, "browAA": "-", "probability": 0.5,"headAA": "wobbling","keywords": "you know, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "head-tilt","keywords": "um, uh, well, let''s see, let me think", "armsAA": "-", "eyesAA": "-"},{"priority": 35, "browAA": "-", "probability": 0.8,"headAA": "shake-short","keywords": "but, although, however, nevertheless, on the other hand, though, yet, aside from, other than, nonetheless, after all, despite, rather", "armsAA": "-", "eyesAA": "-"},
+{"priority": 30, "browAA": "-", "probability": 0.5,"headAA": "head-down","keywords": "likewise, again, more, still, further, besides, additionally, furthermore, moreover, beyond, besides, as a consequence, better, longer, beyond, in addition, what''s more, better, longer, higher, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.8,"headAA": "wobbling","keywords": "really, very, quite, completely, great, absolutely, just, quite, incredible, epic, certainly, actually, indeed, literally, surely, truly, undoubtely, unquestionably", "armsAA": "-", "eyesAA": "-"},{"priority": 50, "browAA": "-", "probability": 0.5,"headAA": "Wobbling","keywords": "normal, ordinary, simple, general, familiar, typical, common, so so, regular, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.8,"headAA": "head-tilt","keywords": "Profound, progressive, advanced, outstanding, Knowledgeable, enlightened, forward-looking, Visionary, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "wobbling","keywords": "alike, comparable, related, similar, familiar", "armsAA": "-", "eyesAA": "-"},{"priority": 65, "browAA": "-", "probability": 0.5,"headAA": "shake-long","keywords": "abnormal, atypical, different, irregular, special, uncommon, unconventional, untraditional, unusual, exceptional, rare, anomalous, changing, disorderly, eccentric, extraordinary, imbalanced, inconsistent, infrequent, unsteady, variable, outstanding, exclusive, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "wobbling","keywords": "rare, extraordinary, limited, occasional, scarce, singular, strange, subtle, uncommon, unique, unlikely, unusual, narrow, cramped, definite, limited, ", "armsAA": "-", "eyesAA": "-"},{"priority": 75, "browAA": "-", "probability": 0.8,"headAA": "shake-long","keywords": "sad, bad, disgust, worse, shame, evil, inappropriate, rotten, nasty, horrible, unhappy, pathetic, deplorable, feeble, heartbreaking, miserable, pitiful, poignant, sorry, woeful, ", "armsAA": "-", "eyesAA": "-"},{"priority": 75, "browAA": "-", "probability": 0.8,"headAA": "wobbling","keywords": "always, consistently, constantly, invariably, regularly, repeatedly, perpetually, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "shake-short","keywords": "uncertain, ambiguous,ambivalent, dubious, erratic, hazy, hesitant, insecure, precarious, questionable, risky, unclear, undecided, undetermined, unpredictable, unreliable,unresolved, unsettled, unsure, suspicous, doubtable, doubtful, questionable, ambiguous, arguable, puzzling, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.8,"headAA": "nod-heavy","keywords": "of course, sure, absolutely, actually, certainly, genuinely, honestly, indeed, legitimately, literally, surely, truly, undoubtedly, unquestionably, well, actual, certain, definite, substantial, substantive, absolute, authentic, concrete, confirmed, factual, for real, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.8,"headAA": "nod-short","keywords": "adaptable, adventurous, affable, affectionate, agreeable, ambitious, amiable, amicable, amusing, brave, bright, broad-minded, calm, careful, charming, communicative, compassionate, conscientious, considerate, convivial, courageous, courteous, creative, decisive, determined, diligent, diplomatic, discreet, dynamic, easygoing, emotional, energetic, enthusiastic, exuberant, fair-minded, faithful, fearless, forceful, friendly, funny, generous, gentle, good, gregarious, hard-working, helpful, honest, humorous, imaginative, impartial, independent, intellectual, intelligent, intuitive, inventive, kind, loving, loyal, modest, neat, nice, optimistic, passionate, patient, persistent, pioneering, philosophical, placid, plucky, polite, powerful, practical, pro-active, quick-witted, quiet, rational, reliable, reserved, resourceful, romantic, self-confident, self-disciplined, sensible, sensitive, shy, sincere, sociable, straightforward, sympathetic, thoughtful, tidy, tough, unassuming, understanding, versatile, warmhearted, willing, witty, attractive, absorbing, alluring, amiable, appealing, attractive, charismatic, cute, delightful, elegant, engaging, engrossing, fascinating, glamorous, graceful, inviting, likable, lovable, lovely, pleasant, provocative, sweet, loyal, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "shake-short","keywords": "Arrogant, Big-headed, Self-centred, Vain, Boastful, Pompous, Aloof, Impolite, Inconsiderate, Thoughtless, Confrontational, Defensive, Hostile, Belligerent, Bitchy, Nasty, Bossy, Cruel, Domineering, Sneaky, Impatient, Unreliable, Jealous, Careless, Irresponsible, Untidy, Cowardly, Foolish, Gullible, Indecisive, Weak-willed, Grumpy, Silly, infamous, disgraceful, egregious, hateful, heinous, ignominious, despicable, humiliating, shameful, immoral, corrupt, depraved, dishonest, indecent, nefarious, obscene, pornographic, shameless, sinful, unethical, unscrupulous, corrupted, ", "armsAA": "-", "eyesAA": "-"},{"priority": 75, "browAA": "-", "probability": 0.5,"headAA": "shake-heavy","keywords": "evil, gloomy, nasty, ominous, rough, evil, gloomy, nasty, ominous, rough, unattractive, unpleasant, disagreeable,disgusting, repugnant, repulsive, unappealing, bad-looking, gross, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.8,"headAA": "head-tilt","keywords": "suspicious, doubtful", "armsAA": "-", "eyesAA": "-"},{"priority": 65, "browAA": "-", "probability": 0.8,"headAA": "wobbling","keywords": "cheerful, contented, delighted, ecstatic, elated, glad, joyful, joyous, jubilant, livelymerry,overjoyed, peaceful, pleasant, pleased, thrilled, upbeat, blessed", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "shake-firmly","keywords": "exciting, appealing, astonishing, breathtaking, dramatic, flashy, hectic, impressive, interesting, intriguing, lively, provocative, stimulating, thrilling, agitated, annoyed, delighted, disturbed, eager, enthusiastic, hysterical, nervous, passionate, thrilled, animated, aroused, awakened, charged, discomposed, disconcerted, inflamed, moved, piqued, provoked, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "wobbling","keywords": "sad, bad, disgust, worse, shame, evil, inappropriate, rotten, nasty, horrible, unhappy, pathetic, deplorable, feeble, heartbreaking, miserable, pitiful, poignant, sorry, woeful, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "head-down","keywords": "panic, scare, anxious, nervous, scared, scary, frightened, suspicious, chilling, concern, worry, horror, horrify, fear, spooky, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "shake-long","keywords": "contradict, diverge, vary, bracket, collate, conflict, depart, deviate, differentiate, mismatch, oppose, separate, ", "armsAA": "-", "eyesAA": "-"},{"priority": 80, "browAA": "-", "probability": 0.8,"headAA": "head-down","keywords": "angry, annoyed, bitter, enraged, exasperated, furious, heated, impassioned, indignant, irate, irritable, irritated, offended, ", "armsAA": "-", "eyesAA": "-"},{"priority": 40, "browAA": "-", "probability": 0.8,"headAA": "head-tilt","keywords": "love, like, wonder, admire, adore, approve, cherish, love, commend, compliment", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "head-tilt","keywords": "unhappy, aversion, disapproval, disgust, displeasure, dislike,", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.8,"headAA": "head-aside","keywords": "complex, headache, baffle, befuddle, bemuse, complicate, confound, daze, demoralize, disconcert, disorient, distract, embarrass, fluster, frustrate, involve, misinform, mislead, baffling, bewildering, complex, complicated, confounding, difficult, disconcerting, perplexing, upsetting", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "wobbling","keywords": "dependable, enduring, lasting, permanent, reliable, stable, strong, tenacious, abiding, constant, diuturnal, fast, firm, fixed, reliable, safe, solid, substantial, abiding, enduring, high tech, advanced, leading, cutting-edge, forward, excellent, distinguished, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "head-right","keywords": "poor, poorly, impoverished, inconsiderable, insignificant, miniature, poor, teeny, thin, tiny, unimportant, delicate, feeble, frail, weak, fragile, junky, crummy, cheesy, crappy, cruddy, garbage, disappointing, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "head-aside","keywords": "huge, enormous, extensive, giant, gigantic, great, humongous, immense, magnificent, mammoth, massive, tremendous, vast, immeasurable, oversize, big, large, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.2,"headAA": "head-aside","keywords": "long, deep, great, high, lengthy, protracted, tall, continued, elongate, elongated, enduring, enlarged, expanded, lasting, lengthened, lingering, prolonged, running, stretch, stretching, sustained, towering, distant, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.2,"headAA": "wobbling","keywords": "a few, a little, a bit, barely, hardly, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "shake-firmly","keywords": "none, nothing, noway, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "shake-firmly","keywords": "a lot, many, massive, lots of, countless, substantial, considerable, extensive, enormous, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.8,"headAA": "wobbling","keywords": "exclusive, integral, total, unabridged, choate, completed, concentrated, conclusive, consummate, every, exhaustive, fixed, fulfilled, full-length, in one piece, inclusive, outright, plenary, rounded, unabbreviated, uncut, undivided, unexpurgated, unqualified, utter, exceptional, conclusive, certain, accomplished, concentrated, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "nod-long","keywords": "important, considerable, leading, main, popular, powerful, serious, significant, substantial, valuable, big league, big-time, consequential, eminent, heavy-duty, heavyweight, influential, major league, material, meaningful, momentous, paramount, prime, principal, prominent, super, super colossal, weighty, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.8,"headAA": "head-tilt-long","keywords": "contradict, diverge, vary, bracket, collate, conflict, depart, deviate, differentiate, mismatch, oppose, separate", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.8,"headAA": "shake-firmly","keywords": "amazing, affected, affect, affecting, astonishing, astounding, impressing, perplex, , stunning, awesome, amazed, astonish, impresive, outstanding, phenomenal", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "nod-long","keywords": "lovely, nicely, cute, sweet, cheerful,delighted, glad, pleasant, alluring, captivating, delicate, delicious, delightful, enchanting, engaging, exquisite, gorgeous, graceful, handsome, pleasant, pleasing, pretty, splendid, stunning, sweet, adorable, charming", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.8,"headAA": "shake-firmly","keywords": "astonishing, astounding, awesome, breathtaking, fantastic, incredible, marvelous, outrageous, phenomenal, remarkable, spectacular, superb, terrific, unbelievable, fabulous, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "shake-long","keywords": "narrow, cramped, definite, limited, precarious, precise, slender, slim, thin, tight, sad, bad, disgust, worse, shame, evil, inappropriate, rotten, nasty, horrible, debris, droppings, junk, litter, residue, rubbish, rubble, sediment, waste,awful, cheap, crummy, dreadful, lousy, poor, rough, sad, unacceptable, blah, bummer, diddly, downer, garbage, gross, imperfect, inferior, junky, synthetic, abominable, amiss, bad news, beastly, bottom out, careless, cheesy, crappy, cruddy, defective ,deficient, dissatisfactory, erroneous, fallacious, faulty, godawful, grody, grungy, depressing, disconcerting, discouraging, disheartening, distasteful, frustrating, mediocre, unpleasant, unsatisfying, awkward, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.8,"headAA": "shake-firmly","keywords": "awful, distressing, dreadful, ", "armsAA": "-", "eyesAA": "-"},{"priority": 30, "browAA": "brow-up-short", "probability": 0.5,"headAA": "-","keywords": "me, my, mine, we, our, ours, ourselves, own, we, our, ours, ourselves, own, ", "armsAA": "-", "eyesAA": "-"},{"priority": 30, "browAA": "brow-up-short", "probability": 0.8,"headAA": "-","keywords": "yours, yourself, them, those, that, these, you, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-up-short", "probability": 0.5,"headAA": "-","keywords": " yes, yeah, i do, i am, we have, we do, you have, true, ok, i like, i love, you like, ya, ", "armsAA": "-", "eyesAA": "-"},{"priority": 100, "browAA": "brow-up-long", "probability": 0.5,"headAA": "-","keywords": "love you, big love, big heart, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-up-normal", "probability": 0.5,"headAA": "-","keywords": " agree, admit, accept, accustomed, acknowledged, allowed, approved, authorized, established, confirmed, supported, permitted, allow, comply, welcomed, agreed, grant, granted, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-center-up", "probability": 0.8,"headAA": "-","keywords": "no, nothing, cannot, can''t, unable, never, don''t know", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-up-long", "probability": 0.5,"headAA": "-","keywords": "correct, right, appropriate, equitable, factual, legitimate, perfect, precise, proper, strict, true, okay, accurately, justly, nicely, perfectly, precisely, properly, rightly, ", "armsAA": "-", "eyesAA": "-"},{"priority": 80, "browAA": "brow-down-normal", "probability": 0.5,"headAA": "-","keywords": "incorrect, wrong, erroneous, false, faulty, flawed, imprecise, improper, inaccurate, inappropriate, mistaken, unreliable, unsound, untrue, not true, not okay, fake, untrue, ", "armsAA": "-", "eyesAA": "-"},{"priority": 80, "browAA": "brow-center-up", "probability": 0.5,"headAA": "-","keywords": "refuse, reject, decline, deny, ignore, trash, waste, wasted, dismiss, ", "armsAA": "-", "eyesAA": "-"},{"priority": 40, "browAA": "brow-up-normal", "probability": 0.5,"headAA": "-","keywords": "what, where, when, how, ", "armsAA": "-", "eyesAA": "-"},{"priority": 55, "browAA": "brow-up-normal", "probability": 0.5,"headAA": "-","keywords": "can you, could you, do you, ", "armsAA": "-", "eyesAA": "-"},{"priority": 50, "browAA": "brow-center-up-long", "probability": 0.8,"headAA": "-","keywords": "well, i guess , i suppose, i think, maybe, perhaps, could, probably, might, actually, only, i believe, i suggest, likely, ", "armsAA": "-", "eyesAA": "-"},{"priority": 85, "browAA": "brow-up-normal", "probability": 0.5,"headAA": "-","keywords": "well, i guess , i suppose, i think, maybe, perhaps, could, probably, might, actually, only, i believe, i suggest, likely, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-up-short", "probability": 0.5,"headAA": "-","keywords": "have to, must, need to, ought to, ought to, should, shall, going to, will, willing to, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-down-short", "probability": 0.8,"headAA": "-","keywords": "must, should, ought to, must need, ", "armsAA": "-", "eyesAA": "-"},{"priority": 50, "browAA": "brow-center-up", "probability": 0.5,"headAA": "-","keywords": "you know, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-center-up", "probability": 0.5,"headAA": "-","keywords": "um, uh, well, let''s see, let me think", "armsAA": "-", "eyesAA": "-"},{"priority": 35, "browAA": "brow-up-normal", "probability": 0.5,"headAA": "-","keywords": "but, although, however, nevertheless, on the other hand, though, yet, aside from, other than, nonetheless, after all, despite, rather", "armsAA": "-", "eyesAA": "-"},{"priority": 35, "browAA": "brow-center-up", "probability": 0.5,"headAA": "-","keywords": "and, along with, also, as a consequence, as well as, including, together with, besides, additionally, also, still, too, additionally, along with, as well, besides, in addition, likewise, withal, again, ", "armsAA": "-", "eyesAA": "-"},{"priority": 30, "browAA": "brow-up-long", "probability": 0.5,"headAA": "-","keywords": "likewise, again, more, still, further, besides, additionally, furthermore, moreover, beyond, besides, as a consequence, better, longer, beyond, in addition, what''s more, better, longer, higher, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-up-long", "probability": 0.8,"headAA": "-","keywords": "really, very, quite, completely, great, absolutely, just, quite, incredible, epic, certainly, actually, indeed, literally, surely, truly, undoubtely, unquestionably", "armsAA": "-", "eyesAA": "-"},{"priority": 50, "browAA": "brow-center-up", "probability": 0.5,"headAA": "-","keywords": "normal, ordinary, simple, general, familiar, typical, common, so so, regular, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-up-normal", "probability": 0.5,"headAA": "-","keywords": "Profound, progressive, advanced, outstanding, Knowledgeable, enlightened, forward-looking, Visionary, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-up-normal", "probability": 0.2,"headAA": "-","keywords": "alike, comparable, related, similar, familiar", "armsAA": "-", "eyesAA": "-"},{"priority": 65, "browAA": "brow-center-up", "probability": 0.5,"headAA": "-","keywords": "abnormal, atypical, different, irregular, special, uncommon, unconventional, untraditional, unusual, exceptional, rare, anomalous, changing, disorderly, eccentric, extraordinary, imbalanced, inconsistent, infrequent, unsteady, variable, outstanding, exclusive, ", "armsAA": "-", "eyesAA": "-"},{"priority": 65, "browAA": "brow-down-long", "probability": 0.5,"headAA": "-","keywords": "contradict, diverge, vary, bracket, collate, conflict, depart, deviate, differentiate, mismatch, oppose, separate", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-center-up", "probability": 0.5,"headAA": "-","keywords": "rare, extraordinary, limited, occasional, scarce, singular, strange, subtle, uncommon, unique, unlikely, unusual, narrow, cramped, definite, limited, ", "armsAA": "-", "eyesAA": "-"},{"priority": 75, "browAA": "brow-center-up-long", "probability": 0.5,"headAA": "-","keywords": "sad, bad, disgust, worse, shame, evil, inappropriate, rotten, nasty, horrible, unhappy, pathetic, deplorable, feeble, heartbreaking, miserable, pitiful, poignant, sorry, woeful, ", "armsAA": "-", "eyesAA": "-"},{"priority": 75, "browAA": "brow-center-up", "probability": 0.5,"headAA": "-","keywords": "always, consistently, constantly, invariably, regularly, repeatedly, perpetually, ", "armsAA": "-", "eyesAA": "-"},{"priority": 50, "browAA": "brow-up-short", "probability": 0.5,"headAA": "-","keywords": "stop, pause, freeze, bar, block, break, conclusion, pause, drop, end, hold on, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-up-normal", "probability": 0.5,"headAA": "-","keywords": " continue, advance, endure, extend, go on, will last, linger, maintain, progress, promote, pursue, remain, will stay, survive, sustain, hold, sustaining, continuing, lasting, enduring, extending, remaining, promoting, progressing, lingering, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-up-normal", "probability": 0.5,"headAA": "-","keywords": "done, finished, completed, compassed, complete, completed, concluded, consummated, depleted, drained, effected, ended, executed, exhausted, fixed, fulfilled, perfected, performed, realized, rendered, succeeded, terminated, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-center-up", "probability": 0.5,"headAA": "-","keywords": "uncertain, ambiguous,ambivalent, dubious, erratic, hazy, hesitant, insecure, precarious, questionable, risky, unclear, undecided, undetermined, unpredictable, unreliable,unresolved, unsettled, unsure, suspicous, doubtable, doubtful, questionable, ambiguous, arguable, puzzling, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-center-up-long", "probability": 0.5,"headAA": "-","keywords": "of course, sure, absolutely, actually, certainly, genuinely, honestly, indeed, legitimately, literally, surely, truly, undoubtedly, unquestionably, well, actual, certain, definite, substantial, substantive, absolute, authentic, concrete, confirmed, factual, for real, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-down-normal", "probability": 0.5,"headAA": "-","keywords": "conflicting, oppose, argue, assail, attack, debate, defy, deny, disagree, dispute, fight, prevent, protest, resist, assault, bar, battle, bombard, combat, confront, contradict, controvert, counter, counterattack, disapprove, encounter, expose, gainsay, hinder neutralize, reverse, taunt, thwart, withstand, object to, opposed, abide, combat, confront, continue, curb, defy, endure, forgo, maintain, prevent, refuse, repel, thwart, turn down, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-up-long", "probability": 0.5,"headAA": "-","keywords": "forget, ignore, avoid, discount, fail, forget, neglect, overlook, reject, scorn, forgeting, ignoring, avoiding, discounting, failling, forgeting, neglect, overlooking, rejecting, scorn, forgetful, ignored, avoided, discounted, failed, forgot, overlooked, rejected, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-up-normal", "probability": 0.5,"headAA": "-","keywords": "adaptable, adventurous, affable, affectionate, agreeable, ambitious, amiable, amicable, amusing, brave, bright, broad-minded, calm, careful, charming, communicative, compassionate, conscientious, considerate, convivial, courageous, courteous, creative, decisive, determined, diligent, diplomatic, discreet, dynamic, easygoing, emotional, energetic, enthusiastic, exuberant, fair-minded, faithful, fearless, forceful, friendly, funny, generous, gentle, good, gregarious, hard-working, helpful, honest, humorous, imaginative, impartial, independent, intellectual, intelligent, intuitive, inventive, kind, loving, loyal, modest, neat, nice, optimistic, passionate, patient, persistent, pioneering, philosophical, placid, plucky, polite, powerful, practical, pro-active, quick-witted, quiet, rational, reliable, reserved, resourceful, romantic, self-confident, self-disciplined, sensible, sensitive, shy, sincere, sociable, straightforward, sympathetic, thoughtful, tidy, tough, unassuming, understanding, versatile, warmhearted, willing, witty, attractive, absorbing, alluring, amiable, appealing, attractive, charismatic, cute, delightful, elegant, engaging, engrossing, fascinating, glamorous, graceful, inviting, likable, lovable, lovely, pleasant, provocative, sweet, loyal, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-center-up", "probability": 0.5,"headAA": "-","keywords": "beautiful, appealing, charming, cute, dazzling, delicate, delightful, elegant, exquisite, fascinating, good-looking, gorgeous, graceful, grand, handsome, lovely, magnificent, marvelous, pleasing, pretty, splendid, stunning, superb, wonderful, admirable, angelic, beauteous, bewitching, classy, famous, honored, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-down-normal", "probability": 0.5,"headAA": "-","keywords": "Arrogant, Big-headed, Self-centred, Vain, Boastful, Pompous, Aloof, Impolite, Inconsiderate, Thoughtless, Confrontational, Defensive, Hostile, Belligerent, Bitchy, Nasty, Bossy, Cruel, Domineering, Sneaky, Impatient, Unreliable, Jealous, Careless, Irresponsible, Untidy, Cowardly, Foolish, Gullible, Indecisive, Weak-willed, Grumpy, Silly, infamous, disgraceful, egregious, hateful, heinous, ignominious, despicable, humiliating, shameful, immoral, corrupt, depraved, dishonest, indecent, nefarious, obscene, pornographic, shameless, sinful, unethical, unscrupulous, corrupted, ", "armsAA": "-", "eyesAA": "-"},{"priority": 75, "browAA": "brow-down-normal", "probability": 0.5,"headAA": "-","keywords": "evil, gloomy, nasty, ominous, rough, evil, gloomy, nasty, ominous, rough, unattractive, unpleasant, disagreeable,disgusting, repugnant, repulsive, unappealing, bad-looking, gross, ", "armsAA": "-", "eyesAA": "-"},{"priority": 65, "browAA": "brow-up-long", "probability": 0.5,"headAA": "-","keywords": "cheerful, contented, delighted, ecstatic, elated, glad, joyful, joyous, jubilant, livelymerry,overjoyed, peaceful, pleasant, pleased, thrilled, upbeat, blessed", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-center-up", "probability": 0.5,"headAA": "-","keywords": "exciting, appealing, astonishing, breathtaking, dramatic, flashy, hectic, impressive, interesting, intriguing, lively, provocative, stimulating, thrilling, agitated, annoyed, delighted, disturbed, eager, enthusiastic, hysterical, nervous, passionate, thrilled, animated, aroused, awakened, charged, discomposed, disconcerted, inflamed, moved, piqued, provoked, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-down-long", "probability": 0.5,"headAA": "-","keywords": "sad, bad, disgust, worse, shame, evil, inappropriate, rotten, nasty, horrible, unhappy, pathetic, deplorable, feeble, heartbreaking, miserable, pitiful, poignant, sorry, woeful, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-center-up", "probability": 0.5,"headAA": "-","keywords": "sad, bad, disgust, worse, shame, evil, inappropriate, rotten, nasty, horrible, unhappy, pathetic, deplorable, feeble, heartbreaking, miserable, pitiful, poignant, sorry, woeful, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-center-up-long", "probability": 0.5,"headAA": "-","keywords": "Sorry, sad, pitiful, apologies, apology, sorrowful,unhappy,", "armsAA": "-", "eyesAA": "-"},{"priority": 30, "browAA": "brow-up-normal", "probability": 0.8,"headAA": "-","keywords": "hello, welcome, bye, see you, good bye, Hi, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-center-up", "probability": 0.5,"headAA": "-","keywords": "panic, scare, anxious, nervous, scared, scary, frightened, suspicious, chilling, concern, worry, horror, horrify, fear, spooky, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-up-long", "probability": 0.5,"headAA": "-","keywords": "impact, astonishing, extraordinary, shocking, amazed, amazing, astonish, astonished, impacted, shocked, impacting, surprise, surprising, dominant, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-center-up", "probability": 0.5,"headAA": "-","keywords": "collapse, earthquake, shocking, shock, collapsing, scare, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-down-normal", "probability": 0.5,"headAA": "-","keywords": "contradict, diverge, vary, bracket, collate, conflict, depart, deviate, differentiate, mismatch, oppose, separate, ", "armsAA": "-", "eyesAA": "-"},{"priority": 80, "browAA": "brow-down-normal", "probability": 0.5,"headAA": "-","keywords": "angry, annoyed, bitter, enraged, exasperated, furious, heated, impassioned, indignant, irate, irritable, irritated, offended, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-down-normal", "probability": 0.5,"headAA": "-","keywords": "depressing, disconcerting, discouraging, disheartening, distasteful, frustrating, mediocre, unpleasant, unsatisfying, bitter, displeasing, failing, ", "armsAA": "-", "eyesAA": "-"},{"priority": 40, "browAA": "brow-up-normal", "probability": 0.5,"headAA": "-","keywords": "love, like, wonder, admire, adore, approve, cherish, love, commend, compliment", "armsAA": "-", "eyesAA": "-"},{"priority": 40, "browAA": "brow-up-normal", "probability": 0.5,"headAA": "-","keywords": "valuable, good at, useful, worthy, skilled, experienced, proficient, expert, competent, efficient, experienced, licensed, qualified, skillful, professional, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-down-long", "probability": 0.5,"headAA": "-","keywords": "unhappy, aversion, disapproval, disgust, displeasure, dislike,", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-down-normal", "probability": 0.5,"headAA": "-","keywords": "hate", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-center-up-long", "probability": 0.8,"headAA": "-","keywords": "complex, headache, baffle, befuddle, bemuse, complicate, confound, daze, demoralize, disconcert, disorient, distract, embarrass, fluster, frustrate, involve, misinform, mislead, baffling, bewildering, complex, complicated, confounding, difficult, disconcerting, perplexing, upsetting", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-center-up-long", "probability": 0.5,"headAA": "-","keywords": "altercation, argue, battle, brawl,clash, combat, conflict, confrontation, contest, controversy, disagreement, dispute, duel, exchange, feud, match, melee, quarrel, riot, rivalry, scuffle, skirmish, struggle, war, wrangling, fight, attack, battle, challenge, clash, protect, resist, argue, combat, engage in, force, oppose, resist, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-down-normal", "probability": 0.5,"headAA": "-","keywords": "altercation, arguing, clashing, combating, conflicting, confrontation, Struggling, wrangling, fighting, attacking, clashing, resisting, arguing, combating, forcing, opposing, resisting,", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-center-up", "probability": 0.5,"headAA": "-","keywords": "destroy, consume, crush, damage, dismantle, eradicate, gut, impair, kill, maim, ravage, raze, ruin, sabotage, shatter, smash, wipe out, wreck, destroying, damaging, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-up-normal", "probability": 0.5,"headAA": "-","keywords": "dependable, enduring, lasting, permanent, reliable, stable, strong, tenacious, abiding, constant, diuturnal, fast, firm, fixed, reliable, safe, solid, substantial, abiding, enduring, high tech, advanced, leading, cutting-edge, forward, excellent, distinguished, ", "armsAA": "-", "eyesAA": "-"},
+{"priority": 70, "browAA": "brow-center-up", "probability": 0.5,"headAA": "-","keywords": "poor, poorly, impoverished, inconsiderable, insignificant, miniature, poor, teeny, thin, tiny, unimportant, delicate, feeble, frail, weak, fragile, junky, crummy, cheesy, crappy, cruddy, garbage, disappointing, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-up-long", "probability": 0.5,"headAA": "-","keywords": "huge, enormous, extensive, giant, gigantic, great, humongous, immense, magnificent, mammoth, massive, tremendous, vast, immeasurable, oversize, big, large, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-up-long", "probability": 0.5,"headAA": "-","keywords": "huge, enormous, extensive, giant, gigantic, great, humongous, immense, magnificent, mammoth, massive, tremendous, vast, immeasurable, oversize, big, large, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-center-up", "probability": 0.5,"headAA": "-","keywords": "cramped, limited, meager, microscopic, miniature, minuscule, modest, narrow, paltry, poor, short, slight, small-scale, young, baby, bantam, diminutive, mini, cramped, limited, meager, microscopic, miniature, minuscule, narrow, paltry, poor, short, slight, small-scale, young, baby, bantam, diminutive, little, miniminute, petite, petty, scanty, shrimp, toy, trifling, bitty, humble, immature, inadequate, inconsequential, inconsiderable, insufficient, picayune, piddling, pint-sized, pitiful, pocket-sized, puny, runty, scrubby, stunted, tensy, teeny, trivial, undersized, unpretentious", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-up-normal", "probability": 0.5,"headAA": "-","keywords": "long, deep, great, high, lengthy, protracted, tall, continued, elongate, elongated, enduring, enlarged, expanded, lasting, lengthened, lingering, prolonged, running, stretch, stretching, sustained, towering, distant, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-center-up-long", "probability": 0.5,"headAA": "-","keywords": "short", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-up-normal", "probability": 0.5,"headAA": "-","keywords": "longer, larger", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-up-normal", "probability": 0.5,"headAA": "-","keywords": "shorter, briefer, curtailed, diminished, less, lessened, lower,more concise, reduced, reducing, diminishing, briefing, lowering, lesser ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-up-long", "probability": 0.5,"headAA": "-","keywords": "enlarge, bigger, more, larger, largest, better, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-center-up", "probability": 0.5,"headAA": "-","keywords": "smaller, lesser", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-center-up", "probability": 0.5,"headAA": "-","keywords": "a few, a little, a bit, barely, hardly, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-down-normal", "probability": 0.5,"headAA": "-","keywords": "none, nothing, noway, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-up-normal", "probability": 0.5,"headAA": "-","keywords": "a lot, many, massive, lots of, countless, substantial, considerable, extensive, enormous, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-center-up", "probability": 0.5,"headAA": "-","keywords": "entirely, exactly, fully, purely, totally, wholly, utterly, completely", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-center-up-long", "probability": 0.5,"headAA": "-","keywords": "everything, all, whole, several, plenty, full, entire, exact, full, quite, total, altogether, all in all, just, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-center-up", "probability": 0.5,"headAA": "-","keywords": "exclusive, integral, total, unabridged, choate, completed, concentrated, conclusive, consummate, every, exhaustive, fixed, fulfilled, full-length, in one piece, inclusive, outright, plenary, rounded, unabbreviated, uncut, undivided, unexpurgated, unqualified, utter, exceptional, conclusive, certain, accomplished, concentrated, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-up-normal", "probability": 0.5,"headAA": "-","keywords": "important, considerable, leading, main, popular, powerful, serious, significant, substantial, valuable, big league, big-time, consequential, eminent, heavy-duty, heavyweight, influential, major league, material, meaningful, momentous, paramount, prime, principal, prominent, super, super colossal, weighty, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-up-normal", "probability": 0.5,"headAA": "-","keywords": "contradict, diverge, vary, bracket, collate, conflict, depart, deviate, differentiate, mismatch, oppose, separate", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-up-normal", "probability": 0.8,"headAA": "-","keywords": "amazing, affected, affect, affecting, astonishing, astounding, impressing, perplex, , stunning, awesome, amazed, astonish, impresive, outstanding, phenomenal", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-center-up", "probability": 0.5,"headAA": "-","keywords": "lovely, nicely, cute, sweet, cheerful,delighted, glad, pleasant, alluring, captivating, delicate, delicious, delightful, enchanting, engaging, exquisite, gorgeous, graceful, handsome, pleasant, pleasing, pretty, splendid, stunning, sweet, adorable, charming", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-center-up-long", "probability": 0.5,"headAA": "-","keywords": "astonishing, astounding, awesome, breathtaking, fantastic, incredible, marvelous, outrageous, phenomenal, remarkable, spectacular, superb, terrific, unbelievable, fabulous, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-center-up", "probability": 0.5,"headAA": "-","keywords": "narrow, cramped, definite, limited, precarious, precise, slender, slim, thin, tight, sad, bad, disgust, worse, shame, evil, inappropriate, rotten, nasty, horrible, debris, droppings, junk, litter, residue, rubbish, rubble, sediment, waste,awful, cheap, crummy, dreadful, lousy, poor, rough, sad, unacceptable, blah, bummer, diddly, downer, garbage, gross, imperfect, inferior, junky, synthetic, abominable, amiss, bad news, beastly, bottom out, careless, cheesy, crappy, cruddy, defective ,deficient, dissatisfactory, erroneous, fallacious, faulty, godawful, grody, grungy, depressing, disconcerting, discouraging, disheartening, distasteful, frustrating, mediocre, unpleasant, unsatisfying, awkward, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-down-normal", "probability": 0.8,"headAA": "-","keywords": "awful, distressing, dreadful, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-center-up", "probability": 0.5,"headAA": "-","keywords": "difficult, tough, ambitious, arduous, burdensome, challenging, troublesome, puzzle, impossible, hard, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-center-up", "probability": 0.5,"headAA": "-","keywords": "easy, accessible,clear,effortless,obvious,painless,simple,smooth,straightforward,uncomplicated, reachable, cozy", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-up-normal", "probability": 0.5,"headAA": "-","keywords": " yes, yeah, i do, i am, we have, we do, you have, true, ok, i like, i love, you like, ya, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-center-up", "probability": 0.5,"headAA": "-","keywords": " agree, admit, accept, accustomed, acknowledged, allowed, approved, authorized, established, confirmed, supported, permitted, allow, comply, welcomed, agreed, grant, granted, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-down-normal", "probability": 0.8,"headAA": "-","keywords": "no, nothing, cannot, can''t, unable, never, don''t know", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-up-normal", "probability": 0.5,"headAA": "-","keywords": "correct, right, appropriate, equitable, factual, legitimate, perfect, precise, proper, strict, true, okay, accurately, justly, nicely, perfectly, precisely, properly, rightly, ", "armsAA": "-", "eyesAA": "-"},{"priority": 80, "browAA": "brow-doubt", "probability": 0.5,"headAA": "-","keywords": "incorrect, wrong, erroneous, false, faulty, flawed, imprecise, improper, inaccurate, inappropriate, mistaken, unreliable, unsound, untrue, not true, not okay, fake, untrue, ", "armsAA": "-", "eyesAA": "-"},{"priority": 80, "browAA": "brow-down-normal", "probability": 0.5,"headAA": "-","keywords": "refuse, reject, decline, deny, ignore, trash, waste, wasted, dismiss, ", "armsAA": "-", "eyesAA": "-"},{"priority": 40, "browAA": "brow-up-long", "probability": 0.5,"headAA": "-","keywords": "what, where, when, how, ", "armsAA": "-", "eyesAA": "-"},{"priority": 55, "browAA": "brow-center-up", "probability": 0.5,"headAA": "-","keywords": "can you, could you, do you, ", "armsAA": "-", "eyesAA": "-"},{"priority": 50, "browAA": "brow-doubt", "probability": 0.8,"headAA": "-","keywords": "well, i guess , i suppose, i think, maybe, perhaps, could, probably, might, actually, only, i believe, i suggest, likely, ", "armsAA": "-", "eyesAA": "-"},{"priority": 85, "browAA": "brow-center-up", "probability": 0.5,"headAA": "-","keywords": "well, i guess , i suppose, i think, maybe, perhaps, could, probably, might, actually, only, i believe, i suggest, likely, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-center-up", "probability": 0.5,"headAA": "-","keywords": "have to, must, need to, ought to, ought to, should, shall, going to, will, willing to, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-up-normal", "probability": 0.8,"headAA": "-","keywords": "must, should, ought to, must need, ", "armsAA": "-", "eyesAA": "-"},{"priority": 50, "browAA": "brow-up-short", "probability": 0.5,"headAA": "-","keywords": "you know, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-up-long", "probability": 0.5,"headAA": "-","keywords": "um, uh, well, let''s see, let me think", "armsAA": "-", "eyesAA": "-"},{"priority": 35, "browAA": "brow-center-up-long", "probability": 0.5,"headAA": "-","keywords": "but, although, however, nevertheless, on the other hand, though, yet, aside from, other than, nonetheless, after all, despite, rather", "armsAA": "-", "eyesAA": "-"},{"priority": 35, "browAA": "brow-up-normal", "probability": 0.5,"headAA": "-","keywords": "and, along with, also, as a consequence, as well as, including, together with, besides, additionally, also, still, too, additionally, along with, as well, besides, in addition, likewise, withal, again, ", "armsAA": "-", "eyesAA": "-"},{"priority": 30, "browAA": "brow-doubt", "probability": 0.5,"headAA": "-","keywords": "likewise, again, more, still, further, besides, additionally, furthermore, moreover, beyond, besides, as a consequence, better, longer, beyond, in addition, what''s more, better, longer, higher, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-center-up-long", "probability": 0.8,"headAA": "-","keywords": "really, very, quite, completely, great, absolutely, just, quite, incredible, epic, certainly, actually, indeed, literally, surely, truly, undoubtely, unquestionably", "armsAA": "-", "eyesAA": "-"},{"priority": 50, "browAA": "brow-doubt", "probability": 0.5,"headAA": "-","keywords": "normal, ordinary, simple, general, familiar, typical, common, so so, regular, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-up-long", "probability": 0.2,"headAA": "-","keywords": "alike, comparable, related, similar, familiar", "armsAA": "-", "eyesAA": "-"},{"priority": 65, "browAA": "brow-down-normal", "probability": 0.5,"headAA": "-","keywords": "abnormal, atypical, different, irregular, special, uncommon, unconventional, untraditional, unusual, exceptional, rare, anomalous, changing, disorderly, eccentric, extraordinary, imbalanced, inconsistent, infrequent, unsteady, variable, outstanding, exclusive, ", "armsAA": "-", "eyesAA": "-"},{"priority": 65, "browAA": "brow-doubt", "probability": 0.5,"headAA": "-","keywords": "contradict, diverge, vary, bracket, collate, conflict, depart, deviate, differentiate, mismatch, oppose, separate", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-up-short", "probability": 0.5,"headAA": "-","keywords": "rare, extraordinary, limited, occasional, scarce, singular, strange, subtle, uncommon, unique, unlikely, unusual, narrow, cramped, definite, limited, ", "armsAA": "-", "eyesAA": "-"},{"priority": 75, "browAA": "brow-down-normal", "probability": 0.5,"headAA": "-","keywords": "sad, bad, disgust, worse, shame, evil, inappropriate, rotten, nasty, horrible, unhappy, pathetic, deplorable, feeble, heartbreaking, miserable, pitiful, poignant, sorry, woeful, ", "armsAA": "-", "eyesAA": "-"},{"priority": 50, "browAA": "brow-down-short", "probability": 0.5,"headAA": "-","keywords": "stop, pause, freeze, bar, block, break, conclusion, pause, drop, end, hold on, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-down-short", "probability": 0.5,"headAA": "-","keywords": "done, finished, completed, compassed, complete, completed, concluded, consummated, depleted, drained, effected, ended, executed, exhausted, fixed, fulfilled, perfected, performed, realized, rendered, succeeded, terminated, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-doubt", "probability": 0.5,"headAA": "-","keywords": "uncertain, ambiguous,ambivalent, dubious, erratic, hazy, hesitant, insecure, precarious, questionable, risky, unclear, undecided, undetermined, unpredictable, unreliable,unresolved, unsettled, unsure, suspicous, doubtable, doubtful, questionable, ambiguous, arguable, puzzling, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-up-short", "probability": 0.5,"headAA": "-","keywords": "of course, sure, absolutely, actually, certainly, genuinely, honestly, indeed, legitimately, literally, surely, truly, undoubtedly, unquestionably, well, actual, certain, definite, substantial, substantive, absolute, authentic, concrete, confirmed, factual, for real, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-center-up", "probability": 0.5,"headAA": "-","keywords": "conflicting, oppose, argue, assail, attack, debate, defy, deny, disagree, dispute, fight, prevent, protest, resist, assault, bar, battle, bombard, combat, confront, contradict, controvert, counter, counterattack, disapprove, encounter, expose, gainsay, hinder neutralize, reverse, taunt, thwart, withstand, object to, opposed, abide, combat, confront, continue, curb, defy, endure, forgo, maintain, prevent, refuse, repel, thwart, turn down, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-up-normal", "probability": 0.5,"headAA": "-","keywords": "forget, ignore, avoid, discount, fail, forget, neglect, overlook, reject, scorn, forgeting, ignoring, avoiding, discounting, failling, forgeting, neglect, overlooking, rejecting, scorn, forgetful, ignored, avoided, discounted, failed, forgot, overlooked, rejected, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-doubt", "probability": 0.5,"headAA": "-","keywords": "Arrogant, Big-headed, Self-centred, Vain, Boastful, Pompous, Aloof, Impolite, Inconsiderate, Thoughtless, Confrontational, Defensive, Hostile, Belligerent, Bitchy, Nasty, Bossy, Cruel, Domineering, Sneaky, Impatient, Unreliable, Jealous, Careless, Irresponsible, Untidy, Cowardly, Foolish, Gullible, Indecisive, Weak-willed, Grumpy, Silly, infamous, disgraceful, egregious, hateful, heinous, ignominious, despicable, humiliating, shameful, immoral, corrupt, depraved, dishonest, indecent, nefarious, obscene, pornographic, shameless, sinful, unethical, unscrupulous, corrupted, ", "armsAA": "-", "eyesAA": "-"},{"priority": 75, "browAA": "brow-doubt", "probability": 0.5,"headAA": "-","keywords": "evil, gloomy, nasty, ominous, rough, evil, gloomy, nasty, ominous, rough, unattractive, unpleasant, disagreeable,disgusting, repugnant, repulsive, unappealing, bad-looking, gross, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-doubt", "probability": 0.8,"headAA": "-","keywords": "suspicious, doubtful", "armsAA": "-", "eyesAA": "-"},{"priority": 65, "browAA": "brow-up-normal", "probability": 0.5,"headAA": "-","keywords": "cheerful, contented, delighted, ecstatic, elated, glad, joyful, joyous, jubilant, livelymerry,overjoyed, peaceful, pleasant, pleased, thrilled, upbeat, blessed", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-up-normal", "probability": 0.5,"headAA": "-","keywords": "exciting, appealing, astonishing, breathtaking, dramatic, flashy, hectic, impressive, interesting, intriguing, lively, provocative, stimulating, thrilling, agitated, annoyed, delighted, disturbed, eager, enthusiastic, hysterical, nervous, passionate, thrilled, animated, aroused, awakened, charged, discomposed, disconcerted, inflamed, moved, piqued, provoked, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-doubt", "probability": 0.5,"headAA": "-","keywords": "sad, bad, disgust, worse, shame, evil, inappropriate, rotten, nasty, horrible, unhappy, pathetic, deplorable, feeble, heartbreaking, miserable, pitiful, poignant, sorry, woeful, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-down-normal", "probability": 0.5,"headAA": "-","keywords": "panic, scare, anxious, nervous, scared, scary, frightened, suspicious, chilling, concern, worry, horror, horrify, fear, spooky, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-up-long", "probability": 0.5,"headAA": "-","keywords": "collapse, earthquake, shocking, shock, collapsing, scare, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-doubt", "probability": 0.5,"headAA": "-","keywords": "contradict, diverge, vary, bracket, collate, conflict, depart, deviate, differentiate, mismatch, oppose, separate, ", "armsAA": "-", "eyesAA": "-"},{"priority": 80, "browAA": "brow-doubt", "probability": 0.5,"headAA": "-","keywords": "angry, annoyed, bitter, enraged, exasperated, furious, heated, impassioned, indignant, irate, irritable, irritated, offended, ", "armsAA": "-", "eyesAA": "-"},{"priority": 40, "browAA": "brow-up-short", "probability": 0.5,"headAA": "-","keywords": "love, like, wonder, admire, adore, approve, cherish, love, commend, compliment", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-center-up-long", "probability": 0.5,"headAA": "-","keywords": "unhappy, aversion, disapproval, disgust, displeasure, dislike,", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-doubt", "probability": 0.8,"headAA": "-","keywords": "complex, headache, baffle, befuddle, bemuse, complicate, confound, daze, demoralize, disconcert, disorient, distract, embarrass, fluster, frustrate, involve, misinform, mislead, baffling, bewildering, complex, complicated, confounding, difficult, disconcerting, perplexing, upsetting", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-center-up-long", "probability": 0.5,"headAA": "-","keywords": "altercation, arguing, clashing, combating, conflicting, confrontation, Struggling, wrangling, fighting, attacking, clashing, resisting, arguing, combating, forcing, opposing, resisting,", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-center-up-long", "probability": 0.5,"headAA": "-","keywords": "destroy, consume, crush, damage, dismantle, eradicate, gut, impair, kill, maim, ravage, raze, ruin, sabotage, shatter, smash, wipe out, wreck, destroying, damaging, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-up-short", "probability": 0.5,"headAA": "-","keywords": "dependable, enduring, lasting, permanent, reliable, stable, strong, tenacious, abiding, constant, diuturnal, fast, firm, fixed, reliable, safe, solid, substantial, abiding, enduring, high tech, advanced, leading, cutting-edge, forward, excellent, distinguished, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-down-short", "probability": 0.5,"headAA": "-","keywords": "poor, poorly, impoverished, inconsiderable, insignificant, miniature, poor, teeny, thin, tiny, unimportant, delicate, feeble, frail, weak, fragile, junky, crummy, cheesy, crappy, cruddy, garbage, disappointing, ", "armsAA": "-", "eyesAA": "-"},{"priority": 70, "browAA": "brow-center-up-long", "probability": 0.5,"headAA": "-","keywords": "cramped, limited, meager, microscopic, miniature, minuscule, modest, narrow, paltry, poor, short, slight, small-scale, young, baby, bantam, diminutive, mini, cramped, limited, meager, microscopic, miniature, minuscule, narrow, paltry, poor, short, slight, small-scale, young, baby, bantam, diminutive, little, miniminute, petite, petty, scanty, shrimp, toy, trifling, bitty, humble, immature, inadequate, inconsequential, inconsiderable, insufficient, picayune, piddling, pint-sized, pitiful, pocket-sized, puny, runty, scrubby, stunted, tensy, teeny, trivial, undersized, unpretentious", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-up-long", "probability": 0.5,"headAA": "-","keywords": "long, deep, great, high, lengthy, protracted, tall, continued, elongate, elongated, enduring, enlarged, expanded, lasting, lengthened, lingering, prolonged, running, stretch, stretching, sustained, towering, distant, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-center-up-long", "probability": 0.5,"headAA": "-","keywords": "entirely, exactly, fully, purely, totally, wholly, utterly, completely", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-center-up", "probability": 0.5,"headAA": "-","keywords": "everything, all, whole, several, plenty, full, entire, exact, full, quite, total, altogether, all in all, just, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-center-up-long", "probability": 0.5,"headAA": "-","keywords": "exclusive, integral, total, unabridged, choate, completed, concentrated, conclusive, consummate, every, exhaustive, fixed, fulfilled, full-length, in one piece, inclusive, outright, plenary, rounded, unabbreviated, uncut, undivided, unexpurgated, unqualified, utter, exceptional, conclusive, certain, accomplished, concentrated, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-center-up-long", "probability": 0.5,"headAA": "-","keywords": "important, considerable, leading, main, popular, powerful, serious, significant, substantial, valuable, big league, big-time, consequential, eminent, heavy-duty, heavyweight, influential, major league, material, meaningful, momentous, paramount, prime, principal, prominent, super, super colossal, weighty, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-center-up", "probability": 0.5,"headAA": "-","keywords": "contradict, diverge, vary, bracket, collate, conflict, depart, deviate, differentiate, mismatch, oppose, separate", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-down-normal", "probability": 0.5,"headAA": "-","keywords": "astonishing, astounding, awesome, breathtaking, fantastic, incredible, marvelous, outrageous, phenomenal, remarkable, spectacular, superb, terrific, unbelievable, fabulous, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-doubt", "probability": 0.5,"headAA": "-","keywords": "narrow, cramped, definite, limited, precarious, precise, slender, slim, thin, tight, sad, bad, disgust, worse, shame, evil, inappropriate, rotten, nasty, horrible, debris, droppings, junk, litter, residue, rubbish, rubble, sediment, waste,awful, cheap, crummy, dreadful, lousy, poor, rough, sad, unacceptable, blah, bummer, diddly, downer, garbage, gross, imperfect, inferior, junky, synthetic, abominable, amiss, bad news, beastly, bottom out, careless, cheesy, crappy, cruddy, defective ,deficient, dissatisfactory, erroneous, fallacious, faulty, godawful, grody, grungy, depressing, disconcerting, discouraging, disheartening, distasteful, frustrating, mediocre, unpleasant, unsatisfying, awkward, ", "armsAA": "-", "eyesAA": "-"},{"priority": 60, "browAA": "brow-up-normal", "probability": 0.5,"headAA": "-","keywords": "easy, accessible,clear,effortless,obvious,painless,simple,smooth,straightforward,uncomplicated, reachable, cozy", "armsAA": "-", "eyesAA": "-"},{"priority": 100, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "love you, big love, big heart, ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-half-close",},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": " agree, admit, accept, accustomed, acknowledged, allowed, approved, authorized, established, confirmed, supported, permitted, allow, comply, welcomed, agreed, grant, granted, ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-half-close",},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "no, nothing, cannot, can''t, unable, never, don''t know", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-half-close",},{"priority": 80, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "refuse, reject, decline, deny, ignore, trash, waste, wasted, dismiss, ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-half-close",},{"priority": 55, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "can you, could you, do you, ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-half-close",},{"priority": 50, "browAA": "-", "probability": 0.2,"headAA": "-","keywords": "well, i guess , i suppose, i think, maybe, perhaps, could, probably, might, actually, only, i believe, i suggest, likely, ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-half-close",},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "must, should, ought to, must need, ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-half-close",},{"priority": 60, "browAA": "-", "probability": 0.2,"headAA": "-","keywords": "um, uh, well, let''s see, let me think", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-half-close",},{"priority": 35, "browAA": "-", "probability": 0.2,"headAA": "-","keywords": "but, although, however, nevertheless, on the other hand, though, yet, aside from, other than, nonetheless, after all, despite, rather", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-half-close",},{"priority": 30, "browAA": "-", "probability": 0.2,"headAA": "-","keywords": "likewise, again, more, still, further, besides, additionally, furthermore, moreover, beyond, besides, as a consequence, better, longer, beyond, in addition, what''s more, better, longer, higher, ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-widely-open",},{"priority": 60, "browAA": "-", "probability": 0.2,"headAA": "-","keywords": "really, very, quite, completely, great, absolutely, just, quite, incredible, epic, certainly, actually, indeed, literally, surely, truly, undoubtely, unquestionably", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-widely-open",},{"priority": 50, "browAA": "-", "probability": 0.2,"headAA": "-","keywords": "normal, ordinary, simple, general, familiar, typical, common, so so, regular, ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-half-close",},{"priority": 60, "browAA": "-", "probability": 0.2,"headAA": "-","keywords": "Profound, progressive, advanced, outstanding, Knowledgeable, enlightened, forward-looking, Visionary, ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-half-close",},{"priority": 65, "browAA": "-", "probability": 0.2,"headAA": "-","keywords": "abnormal, atypical, different, irregular, special, uncommon, unconventional, untraditional, unusual, exceptional, rare, anomalous, changing, disorderly, eccentric, extraordinary, imbalanced, inconsistent, infrequent, unsteady, variable, outstanding, exclusive, ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-half-close",},{"priority": 50, "browAA": "-", "probability": 0.2,"headAA": "-","keywords": "stop, pause, freeze, bar, block, break, conclusion, pause, drop, end, hold on, ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-half-close",},{"priority": 70, "browAA": "-", "probability": 0.2,"headAA": "-","keywords": "done, finished, completed, compassed, complete, completed, concluded, consummated, depleted, drained, effected, ended, executed, exhausted, fixed, fulfilled, perfected, performed, realized, rendered, succeeded, terminated, ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-half-close",},{"priority": 70, "browAA": "-", "probability": 0.8,"headAA": "-","keywords": "suspicious, doubtful", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-half-close",},{"priority": 65, "browAA": "-", "probability": 0.2,"headAA": "-","keywords": "cheerful, contented, delighted, ecstatic, elated, glad, joyful, joyous, jubilant, livelymerry,overjoyed, peaceful, pleasant, pleased, thrilled, upbeat, blessed", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-half-close",},
+{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "exciting, appealing, astonishing, breathtaking, dramatic, flashy, hectic, impressive, interesting, intriguing, lively, provocative, stimulating, thrilling, agitated, annoyed, delighted, disturbed, eager, enthusiastic, hysterical, nervous, passionate, thrilled, animated, aroused, awakened, charged, discomposed, disconcerted, inflamed, moved, piqued, provoked, ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-widely-open",},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "sad, bad, disgust, worse, shame, evil, inappropriate, rotten, nasty, horrible, unhappy, pathetic, deplorable, feeble, heartbreaking, miserable, pitiful, poignant, sorry, woeful, ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-half-close",},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "Sorry, sad, pitiful, apologies, apology, sorrowful,unhappy,", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-half-close",},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "panic, scare, anxious, nervous, scared, scary, frightened, suspicious, chilling, concern, worry, horror, horrify, fear, spooky, ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-widely-open",},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "impact, astonishing, extraordinary, shocking, amazed, amazing, astonish, astonished, impacted, shocked, impacting, surprise, surprising, dominant, ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-widely-open",},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "collapse, earthquake, shocking, shock, collapsing, scare, ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-widely-open",},{"priority": 70, "browAA": "-", "probability": 0.2,"headAA": "-","keywords": "contradict, diverge, vary, bracket, collate, conflict, depart, deviate, differentiate, mismatch, oppose, separate, ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-half-close",},{"priority": 80, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "angry, annoyed, bitter, enraged, exasperated, furious, heated, impassioned, indignant, irate, irritable, irritated, offended, ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-half-close",},{"priority": 40, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "love, like, wonder, admire, adore, approve, cherish, love, commend, compliment", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-half-close",},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "unhappy, aversion, disapproval, disgust, displeasure, dislike,", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-half-close",},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "hate", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-half-close",},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "altercation, argue, battle, brawl,clash, combat, conflict, confrontation, contest, controversy, disagreement, dispute, duel, exchange, feud, match, melee, quarrel, riot, rivalry, scuffle, skirmish, struggle, war, wrangling, fight, attack, battle, challenge, clash, protect, resist, argue, combat, engage in, force, oppose, resist, ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-widely-open",},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "destroy, consume, crush, damage, dismantle, eradicate, gut, impair, kill, maim, ravage, raze, ruin, sabotage, shatter, smash, wipe out, wreck, destroying, damaging, ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-widely-open",},{"priority": 70, "browAA": "-", "probability": 0.2,"headAA": "-","keywords": "poor, poorly, impoverished, inconsiderable, insignificant, miniature, poor, teeny, thin, tiny, unimportant, delicate, feeble, frail, weak, fragile, junky, crummy, cheesy, crappy, cruddy, garbage, disappointing, ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-half-close",},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "huge, enormous, extensive, giant, gigantic, great, humongous, immense, magnificent, mammoth, massive, tremendous, vast, immeasurable, oversize, big, large, ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-widely-open",},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "huge, enormous, extensive, giant, gigantic, great, humongous, immense, magnificent, mammoth, massive, tremendous, vast, immeasurable, oversize, big, large, ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-widely-open",},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "cramped, limited, meager, microscopic, miniature, minuscule, modest, narrow, paltry, poor, short, slight, small-scale, young, baby, bantam, diminutive, mini, cramped, limited, meager, microscopic, miniature, minuscule, narrow, paltry, poor, short, slight, small-scale, young, baby, bantam, diminutive, little, miniminute, petite, petty, scanty, shrimp, toy, trifling, bitty, humble, immature, inadequate, inconsequential, inconsiderable, insufficient, picayune, piddling, pint-sized, pitiful, pocket-sized, puny, runty, scrubby, stunted, tensy, teeny, trivial, undersized, unpretentious", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-half-close",},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "long, deep, great, high, lengthy, protracted, tall, continued, elongate, elongated, enduring, enlarged, expanded, lasting, lengthened, lingering, prolonged, running, stretch, stretching, sustained, towering, distant, ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-widely-open",},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "shorter, briefer, curtailed, diminished, less, lessened, lower,more concise, reduced, reducing, diminishing, briefing, lowering, lesser ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-half-close",},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "entirely, exactly, fully, purely, totally, wholly, utterly, completely", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-widely-open",},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "everything, all, whole, several, plenty, full, entire, exact, full, quite, total, altogether, all in all, just, ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-widely-open",},{"priority": 60, "browAA": "-", "probability": 0.2,"headAA": "-","keywords": "exclusive, integral, total, unabridged, choate, completed, concentrated, conclusive, consummate, every, exhaustive, fixed, fulfilled, full-length, in one piece, inclusive, outright, plenary, rounded, unabbreviated, uncut, undivided, unexpurgated, unqualified, utter, exceptional, conclusive, certain, accomplished, concentrated, ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-half-close",},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "contradict, diverge, vary, bracket, collate, conflict, depart, deviate, differentiate, mismatch, oppose, separate", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-half-close",},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "amazing, affected, affect, affecting, astonishing, astounding, impressing, perplex, , stunning, awesome, amazed, astonish, impresive, outstanding, phenomenal", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-widely-open",},{"priority": , "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "comfortable, elementary, reachable, possible, comfy, peaceful, ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-half-close",},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "lovely, nicely, cute, sweet, cheerful,delighted, glad, pleasant, alluring, captivating, delicate, delicious, delightful, enchanting, engaging, exquisite, gorgeous, graceful, handsome, pleasant, pleasing, pretty, splendid, stunning, sweet, adorable, charming", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-half-close",},{"priority": 60, "browAA": "-", "probability": 0.2,"headAA": "-","keywords": "narrow, cramped, definite, limited, precarious, precise, slender, slim, thin, tight, sad, bad, disgust, worse, shame, evil, inappropriate, rotten, nasty, horrible, debris, droppings, junk, litter, residue, rubbish, rubble, sediment, waste,awful, cheap, crummy, dreadful, lousy, poor, rough, sad, unacceptable, blah, bummer, diddly, downer, garbage, gross, imperfect, inferior, junky, synthetic, abominable, amiss, bad news, beastly, bottom out, careless, cheesy, crappy, cruddy, defective ,deficient, dissatisfactory, erroneous, fallacious, faulty, godawful, grody, grungy, depressing, disconcerting, discouraging, disheartening, distasteful, frustrating, mediocre, unpleasant, unsatisfying, awkward, ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-half-close",},{"priority": 30, "browAA": "-", "probability": 0.2,"headAA": "-","keywords": "likewise, again, more, still, further, besides, additionally, furthermore, moreover, beyond, besides, as a consequence, better, longer, beyond, in addition, what''s more, better, longer, higher, ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-half-close",},{"priority": 60, "browAA": "-", "probability": 0.2,"headAA": "-","keywords": "really, very, quite, completely, great, absolutely, just, quite, incredible, epic, certainly, actually, indeed, literally, surely, truly, undoubtely, unquestionably", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-half-close",},{"priority": 50, "browAA": "-", "probability": 0.2,"headAA": "-","keywords": "stop, pause, freeze, bar, block, break, conclusion, pause, drop, end, hold on, ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-widely-open",},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "collapse, earthquake, shocking, shock, collapsing, scare, ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-half-close",},{"priority": 80, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "angry, annoyed, bitter, enraged, exasperated, furious, heated, impassioned, indignant, irate, irritable, irritated, offended, ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-widely-open",},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "altercation, argue, battle, brawl,clash, combat, conflict, confrontation, contest, controversy, disagreement, dispute, duel, exchange, feud, match, melee, quarrel, riot, rivalry, scuffle, skirmish, struggle, war, wrangling, fight, attack, battle, challenge, clash, protect, resist, argue, combat, engage in, force, oppose, resist, ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-half-close",},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "destroy, consume, crush, damage, dismantle, eradicate, gut, impair, kill, maim, ravage, raze, ruin, sabotage, shatter, smash, wipe out, wreck, destroying, damaging, ", "armsAA": "-", "eyesAA": "-", "eyelidsAA": "eyelid-widely-open",},{"priority": 50, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "well, i guess , i suppose, i think, maybe, perhaps, could, probably, might, actually, only, i believe, i suggest, likely, ", "armsAA": "-", "eyesAA": "eye-side"},{"priority": 85, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "well, i guess , i suppose, i think, maybe, perhaps, could, probably, might, actually, only, i believe, i suggest, likely, ", "armsAA": "-", "eyesAA": "eye-around"},{"priority": 50, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "you know, ", "armsAA": "-", "eyesAA": "eye-corner"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "um, uh, well, let''s see, let me think", "armsAA": "-", "eyesAA": "eye-corner"},{"priority": 35, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "but, although, however, nevertheless, on the other hand, though, yet, aside from, other than, nonetheless, after all, despite, rather", "armsAA": "-", "eyesAA": "eye-corner"},{"priority": 35, "browAA": "-", "probability": 0.2,"headAA": "-","keywords": "and, along with, also, as a consequence, as well as, including, together with, besides, additionally, also, still, too, additionally, along with, as well, besides, in addition, likewise, withal, again, ", "armsAA": "-", "eyesAA": "eye-corner"},{"priority": 50, "browAA": "-", "probability": 0.2,"headAA": "-","keywords": "normal, ordinary, simple, general, familiar, typical, common, so so, regular, ", "armsAA": "-", "eyesAA": "eye-corner"},{"priority": 60, "browAA": "-", "probability": 0.2,"headAA": "-","keywords": "alike, comparable, related, similar, familiar", "armsAA": "-", "eyesAA": "eye-side"},{"priority": 65, "browAA": "-", "probability": 0.2,"headAA": "-","keywords": "abnormal, atypical, different, irregular, special, uncommon, unconventional, untraditional, unusual, exceptional, rare, anomalous, changing, disorderly, eccentric, extraordinary, imbalanced, inconsistent, infrequent, unsteady, variable, outstanding, exclusive, ", "armsAA": "-", "eyesAA": "eye-corner"},{"priority": 75, "browAA": "-", "probability": 1,"headAA": "-","keywords": "sad, bad, disgust, worse, shame, evil, inappropriate, rotten, nasty, horrible, unhappy, pathetic, deplorable, feeble, heartbreaking, miserable, pitiful, poignant, sorry, woeful, ", "armsAA": "-", "eyesAA": "eye-down"},{"priority": 70, "browAA": "-", "probability": 0.2,"headAA": "-","keywords": "uncertain, ambiguous,ambivalent, dubious, erratic, hazy, hesitant, insecure, precarious, questionable, risky, unclear, undecided, undetermined, unpredictable, unreliable,unresolved, unsettled, unsure, suspicous, doubtable, doubtful, questionable, ambiguous, arguable, puzzling, ", "armsAA": "-", "eyesAA": "eye-around"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "sad, bad, disgust, worse, shame, evil, inappropriate, rotten, nasty, horrible, unhappy, pathetic, deplorable, feeble, heartbreaking, miserable, pitiful, poignant, sorry, woeful, ", "armsAA": "-", "eyesAA": "eye-down"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "Sorry, sad, pitiful, apologies, apology, sorrowful,unhappy,", "armsAA": "-", "eyesAA": "eye-down"},{"priority": 70, "browAA": "-", "probability": 1,"headAA": "-","keywords": "collapse, earthquake, shocking, shock, collapsing, scare, ", "armsAA": "-", "eyesAA": "eye-down"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "depressing, disconcerting, discouraging, disheartening, distasteful, frustrating, mediocre, unpleasant, unsatisfying, bitter, displeasing, failing, ", "armsAA": "-", "eyesAA": "eye-down"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "unhappy, aversion, disapproval, disgust, displeasure, dislike,", "armsAA": "-", "eyesAA": "eye-down"},{"priority": 70, "browAA": "-", "probability": 0.2,"headAA": "-","keywords": "poor, poorly, impoverished, inconsiderable, insignificant, miniature, poor, teeny, thin, tiny, unimportant, delicate, feeble, frail, weak, fragile, junky, crummy, cheesy, crappy, cruddy, garbage, disappointing, ", "armsAA": "-", "eyesAA": "eye-corner"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "cramped, limited, meager, microscopic, miniature, minuscule, modest, narrow, paltry, poor, short, slight, small-scale, young, baby, bantam, diminutive, mini, cramped, limited, meager, microscopic, miniature, minuscule, narrow, paltry, poor, short, slight, small-scale, young, baby, bantam, diminutive, little, miniminute, petite, petty, scanty, shrimp, toy, trifling, bitty, humble, immature, inadequate, inconsequential, inconsiderable, insufficient, picayune, piddling, pint-sized, pitiful, pocket-sized, puny, runty, scrubby, stunted, tensy, teeny, trivial, undersized, unpretentious", "armsAA": "-", "eyesAA": "eye-side"},{"priority": 60, "browAA": "-", "probability": 0.2,"headAA": "-","keywords": "none, nothing, noway, ", "armsAA": "-", "eyesAA": "eye-down"},{"priority": 85, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "well, i guess , i suppose, i think, maybe, perhaps, could, probably, might, actually, only, i believe, i suggest, likely, ", "armsAA": "-", "eyesAA": "eye-corner"},{"priority": 50, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "you know, ", "armsAA": "-", "eyesAA": "eye-side"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "um, uh, well, let''s see, let me think", "armsAA": "-", "eyesAA": "eye-around"},{"priority": 35, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "but, although, however, nevertheless, on the other hand, though, yet, aside from, other than, nonetheless, after all, despite, rather", "armsAA": "-", "eyesAA": "eye-side"},{"priority": 35, "browAA": "-", "probability": 0.2,"headAA": "-","keywords": "and, along with, also, as a consequence, as well as, including, together with, besides, additionally, also, still, too, additionally, along with, as well, besides, in addition, likewise, withal, again, ", "armsAA": "-", "eyesAA": "eye-side"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "unhappy, aversion, disapproval, disgust, displeasure, dislike,", "armsAA": "-", "eyesAA": "eye-corner"},{"priority": 70, "browAA": "-", "probability": 0.2,"headAA": "-","keywords": "poor, poorly, impoverished, inconsiderable, insignificant, miniature, poor, teeny, thin, tiny, unimportant, delicate, feeble, frail, weak, fragile, junky, crummy, cheesy, crappy, cruddy, garbage, disappointing, ", "armsAA": "-", "eyesAA": "eye-around"},{"priority": 30, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "me, my, mine, we, our, ours, ourselves, own, we, our, ours, ourselves, own, ", "armsAA": "arm-me", "eyesAA": "-"},{"priority": 30, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "yours, yourself, them, those, that, these, you, ", "armsAA": "arm-you", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.8,"headAA": "-","keywords": " yes, yeah, i do, i am, we have, we do, you have, true, ok, i like, i love, you like, ya, ", "armsAA": "arm-offer", "eyesAA": "-"},{"priority": 100, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "love you, big love, big heart, ", "armsAA": "arm-heart", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": " agree, admit, accept, accustomed, acknowledged, allowed, approved, authorized, established, confirmed, supported, permitted, allow, comply, welcomed, agreed, grant, granted, ", "armsAA": "arm-point", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.8,"headAA": "-","keywords": "no, nothing, cannot, can''t, unable, never, don''t know", "armsAA": "arm-notknow", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "correct, right, appropriate, equitable, factual, legitimate, perfect, precise, proper, strict, true, okay, accurately, justly, nicely, perfectly, precisely, properly, rightly, ", "armsAA": "arm-point", "eyesAA": "-"},{"priority": 80, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "incorrect, wrong, erroneous, false, faulty, flawed, imprecise, improper, inaccurate, inappropriate, mistaken, unreliable, unsound, untrue, not true, not okay, fake, untrue, ", "armsAA": "arm-point", "eyesAA": "-"},{"priority": 80, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "refuse, reject, decline, deny, ignore, trash, waste, wasted, dismiss, ", "armsAA": "arm-rejection", "eyesAA": "-"},{"priority": 40, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "what, where, when, how, ", "armsAA": "arm-offer", "eyesAA": "-"},{"priority": 55, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "can you, could you, do you, ", "armsAA": "arm-you", "eyesAA": "-"},{"priority": 50, "browAA": "-", "probability": 0.8,"headAA": "-","keywords": "well, i guess , i suppose, i think, maybe, perhaps, could, probably, might, actually, only, i believe, i suggest, likely, ", "armsAA": "arm-unsure", "eyesAA": "-"},{"priority": 85, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "well, i guess , i suppose, i think, maybe, perhaps, could, probably, might, actually, only, i believe, i suggest, likely, ", "armsAA": "arm-unsure", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.8,"headAA": "-","keywords": "have to, must, need to, ought to, ought to, should, shall, going to, will, willing to, ", "armsAA": "arm-beat", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.8,"headAA": "-","keywords": "must, should, ought to, must need, ", "armsAA": "arm-beat", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "um, uh, well, let''s see, let me think", "armsAA": "arm-notknow", "eyesAA": "-"},{"priority": 35, "browAA": "-", "probability": 0.8,"headAA": "-","keywords": "but, although, however, nevertheless, on the other hand, though, yet, aside from, other than, nonetheless, after all, despite, rather", "armsAA": "arm-notknow", "eyesAA": "-"},{"priority": 35, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "and, along with, also, as a consequence, as well as, including, together with, besides, additionally, also, still, too, additionally, along with, as well, besides, in addition, likewise, withal, again, ", "armsAA": "arm-contrast", "eyesAA": "-"},{"priority": 30, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "likewise, again, more, still, further, besides, additionally, furthermore, moreover, beyond, besides, as a consequence, better, longer, beyond, in addition, what''s more, better, longer, higher, ", "armsAA": "arm-contrast", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 1,"headAA": "-","keywords": "really, very, quite, completely, great, absolutely, just, quite, incredible, epic, certainly, actually, indeed, literally, surely, truly, undoubtely, unquestionably", "armsAA": "arm-frame-big", "eyesAA": "-"},{"priority": 50, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "normal, ordinary, simple, general, familiar, typical, common, so so, regular, ", "armsAA": "arm-unsure", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "Profound, progressive, advanced, outstanding, Knowledgeable, enlightened, forward-looking, Visionary, ", "armsAA": "arm-contrast", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "alike, comparable, related, similar, familiar", "armsAA": "arm-rolling", "eyesAA": "-"},{"priority": 65, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "abnormal, atypical, different, irregular, special, uncommon, unconventional, untraditional, unusual, exceptional, rare, anomalous, changing, disorderly, eccentric, extraordinary, imbalanced, inconsistent, infrequent, unsteady, variable, outstanding, exclusive, ", "armsAA": "arm-unsure", "eyesAA": "-"},{"priority": 65, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "contradict, diverge, vary, bracket, collate, conflict, depart, deviate, differentiate, mismatch, oppose, separate", "armsAA": "arm-beat", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "rare, extraordinary, limited, occasional, scarce, singular, strange, subtle, uncommon, unique, unlikely, unusual, narrow, cramped, definite, limited, ", "armsAA": "arm-frame-small", "eyesAA": "-"},{"priority": 75, "browAA": "-", "probability": 1,"headAA": "-","keywords": "sad, bad, disgust, worse, shame, evil, inappropriate, rotten, nasty, horrible, unhappy, pathetic, deplorable, feeble, heartbreaking, miserable, pitiful, poignant, sorry, woeful, ", "armsAA": "arm-frame-small", "eyesAA": "-"},{"priority": 75, "browAA": "-", "probability": 1,"headAA": "-","keywords": "always, consistently, constantly, invariably, regularly, repeatedly, perpetually, ", "armsAA": "arm-rolling", "eyesAA": "-"},{"priority": 50, "browAA": "-", "probability": 0.2,"headAA": "-","keywords": "stop, pause, freeze, bar, block, break, conclusion, pause, drop, end, hold on, ", "armsAA": "arm-process-control", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": " continue, advance, endure, extend, go on, will last, linger, maintain, progress, promote, pursue, remain, will stay, survive, sustain, hold, sustaining, continuing, lasting, enduring, extending, remaining, promoting, progressing, lingering, ", "armsAA": "arm-rolling", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "done, finished, completed, compassed, complete, completed, concluded, consummated, depleted, drained, effected, ended, executed, exhausted, fixed, fulfilled, perfected, performed, realized, rendered, succeeded, terminated, ", "armsAA": "arm-beat", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "uncertain, ambiguous,ambivalent, dubious, erratic, hazy, hesitant, insecure, precarious, questionable, risky, unclear, undecided, undetermined, unpredictable, unreliable,unresolved, unsettled, unsure, suspicous, doubtable, doubtful, questionable, ambiguous, arguable, puzzling, ", "armsAA": "arm-unsure", "eyesAA": "-"},{"priority": 50, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "kind of, slightly, lightly, somewhat, at least, rather, moderately, sort of, to some extent, at least", "armsAA": "arm-frame-small", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.8,"headAA": "-","keywords": "of course, sure, absolutely, actually, certainly, genuinely, honestly, indeed, legitimately, literally, surely, truly, undoubtedly, unquestionably, well, actual, certain, definite, substantial, substantive, absolute, authentic, concrete, confirmed, factual, for real, ", "armsAA": "arm-offer", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "conflicting, oppose, argue, assail, attack, debate, defy, deny, disagree, dispute, fight, prevent, protest, resist, assault, bar, battle, bombard, combat, confront, contradict, controvert, counter, counterattack, disapprove, encounter, expose, gainsay, hinder neutralize, reverse, taunt, thwart, withstand, object to, opposed, abide, combat, confront, continue, curb, defy, endure, forgo, maintain, prevent, refuse, repel, thwart, turn down, ", "armsAA": "arm-process-control", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "forget, ignore, avoid, discount, fail, forget, neglect, overlook, reject, scorn, forgeting, ignoring, avoiding, discounting, failling, forgeting, neglect, overlooking, rejecting, scorn, forgetful, ignored, avoided, discounted, failed, forgot, overlooked, rejected, ", "armsAA": "arm-notknow", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.8,"headAA": "-","keywords": "adaptable, adventurous, affable, affectionate, agreeable, ambitious, amiable, amicable, amusing, brave, bright, broad-minded, calm, careful, charming, communicative, compassionate, conscientious, considerate, convivial, courageous, courteous, creative, decisive, determined, diligent, diplomatic, discreet, dynamic, easygoing, emotional, energetic, enthusiastic, exuberant, fair-minded, faithful, fearless, forceful, friendly, funny, generous, gentle, good, gregarious, hard-working, helpful, honest, humorous, imaginative, impartial, independent, intellectual, intelligent, intuitive, inventive, kind, loving, loyal, modest, neat, nice, optimistic, passionate, patient, persistent, pioneering, philosophical, placid, plucky, polite, powerful, practical, pro-active, quick-witted, quiet, rational, reliable, reserved, resourceful, romantic, self-confident, self-disciplined, sensible, sensitive, shy, sincere, sociable, straightforward, sympathetic, thoughtful, tidy, tough, unassuming, understanding, versatile, warmhearted, willing, witty, attractive, absorbing, alluring, amiable, appealing, attractive, charismatic, cute, delightful, elegant, engaging, engrossing, fascinating, glamorous, graceful, inviting, likable, lovable, lovely, pleasant, provocative, sweet, loyal, ", "armsAA": "arm-offer", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "beautiful, appealing, charming, cute, dazzling, delicate, delightful, elegant, exquisite, fascinating, good-looking, gorgeous, graceful, grand, handsome, lovely, magnificent, marvelous, pleasing, pretty, splendid, stunning, superb, wonderful, admirable, angelic, beauteous, bewitching, classy, famous, honored, ", "armsAA": "arm-offer", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "Arrogant, Big-headed, Self-centred, Vain, Boastful, Pompous, Aloof, Impolite, Inconsiderate, Thoughtless, Confrontational, Defensive, Hostile, Belligerent, Bitchy, Nasty, Bossy, Cruel, Domineering, Sneaky, Impatient, Unreliable, Jealous, Careless, Irresponsible, Untidy, Cowardly, Foolish, Gullible, Indecisive, Weak-willed, Grumpy, Silly, infamous, disgraceful, egregious, hateful, heinous, ignominious, despicable, humiliating, shameful, immoral, corrupt, depraved, dishonest, indecent, nefarious, obscene, pornographic, shameless, sinful, unethical, unscrupulous, corrupted, ", "armsAA": "arm-unsure", "eyesAA": "-"},
+{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "suspicious, doubtful", "armsAA": "arm-point", "eyesAA": "-"},{"priority": 65, "browAA": "-", "probability": 0.8,"headAA": "-","keywords": "cheerful, contented, delighted, ecstatic, elated, glad, joyful, joyous, jubilant, livelymerry,overjoyed, peaceful, pleasant, pleased, thrilled, upbeat, blessed", "armsAA": "arm-offer", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "exciting, appealing, astonishing, breathtaking, dramatic, flashy, hectic, impressive, interesting, intriguing, lively, provocative, stimulating, thrilling, agitated, annoyed, delighted, disturbed, eager, enthusiastic, hysterical, nervous, passionate, thrilled, animated, aroused, awakened, charged, discomposed, disconcerted, inflamed, moved, piqued, provoked, ", "armsAA": "arm-offer", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "sad, bad, disgust, worse, shame, evil, inappropriate, rotten, nasty, horrible, unhappy, pathetic, deplorable, feeble, heartbreaking, miserable, pitiful, poignant, sorry, woeful, ", "armsAA": "arm-notknow", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "Sorry, sad, pitiful, apologies, apology, sorrowful,unhappy,", "armsAA": "arm-notknow", "eyesAA": "-"},{"priority": 30, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "hello, welcome, bye, see you, good bye, Hi, ", "armsAA": "arm-wave", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "panic, scare, anxious, nervous, scared, scary, frightened, suspicious, chilling, concern, worry, horror, horrify, fear, spooky, ", "armsAA": "arm-frame-small", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "impact, astonishing, extraordinary, shocking, amazed, amazing, astonish, astonished, impacted, shocked, impacting, surprise, surprising, dominant, ", "armsAA": "arm-beat", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 1,"headAA": "-","keywords": "collapse, earthquake, shocking, shock, collapsing, scare, ", "armsAA": "arm-beat", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "contradict, diverge, vary, bracket, collate, conflict, depart, deviate, differentiate, mismatch, oppose, separate, ", "armsAA": "arm-unsure", "eyesAA": "-"},{"priority": 80, "browAA": "-", "probability": 0.8,"headAA": "-","keywords": "angry, annoyed, bitter, enraged, exasperated, furious, heated, impassioned, indignant, irate, irritable, irritated, offended, ", "armsAA": "arm-process-control", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.8,"headAA": "-","keywords": "depressing, disconcerting, discouraging, disheartening, distasteful, frustrating, mediocre, unpleasant, unsatisfying, bitter, displeasing, failing, ", "armsAA": "arm-rejection", "eyesAA": "-"},{"priority": 40, "browAA": "-", "probability": 0.8,"headAA": "-","keywords": "love, like, wonder, admire, adore, approve, cherish, love, commend, compliment", "armsAA": "arm-offer", "eyesAA": "-"},{"priority": 40, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "valuable, good at, useful, worthy, skilled, experienced, proficient, expert, competent, efficient, experienced, licensed, qualified, skillful, professional, ", "armsAA": "arm-offer", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "unhappy, aversion, disapproval, disgust, displeasure, dislike,", "armsAA": "arm-unsure", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 1,"headAA": "-","keywords": "hate", "armsAA": "arm-beat", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.8,"headAA": "-","keywords": "complex, headache, baffle, befuddle, bemuse, complicate, confound, daze, demoralize, disconcert, disorient, distract, embarrass, fluster, frustrate, involve, misinform, mislead, baffling, bewildering, complex, complicated, confounding, difficult, disconcerting, perplexing, upsetting", "armsAA": "arm-unsure", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "call me. contact me, ", "armsAA": "arm-call", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "altercation, argue, battle, brawl,clash, combat, conflict, confrontation, contest, controversy, disagreement, dispute, duel, exchange, feud, match, melee, quarrel, riot, rivalry, scuffle, skirmish, struggle, war, wrangling, fight, attack, battle, challenge, clash, protect, resist, argue, combat, engage in, force, oppose, resist, ", "armsAA": "arm-beat", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "altercation, arguing, clashing, combating, conflicting, confrontation, Struggling, wrangling, fighting, attacking, clashing, resisting, arguing, combating, forcing, opposing, resisting,", "armsAA": "arm-beat", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "destroy, consume, crush, damage, dismantle, eradicate, gut, impair, kill, maim, ravage, raze, ruin, sabotage, shatter, smash, wipe out, wreck, destroying, damaging, ", "armsAA": "arm-beat", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "poor, poorly, impoverished, inconsiderable, insignificant, miniature, poor, teeny, thin, tiny, unimportant, delicate, feeble, frail, weak, fragile, junky, crummy, cheesy, crappy, cruddy, garbage, disappointing, ", "armsAA": "arm-frame-small", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 1,"headAA": "-","keywords": "huge, enormous, extensive, giant, gigantic, great, humongous, immense, magnificent, mammoth, massive, tremendous, vast, immeasurable, oversize, big, large, ", "armsAA": "arm-frame-big", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 1,"headAA": "-","keywords": "cramped, limited, meager, microscopic, miniature, minuscule, modest, narrow, paltry, poor, short, slight, small-scale, young, baby, bantam, diminutive, mini, cramped, limited, meager, microscopic, miniature, minuscule, narrow, paltry, poor, short, slight, small-scale, young, baby, bantam, diminutive, little, miniminute, petite, petty, scanty, shrimp, toy, trifling, bitty, humble, immature, inadequate, inconsequential, inconsiderable, insufficient, picayune, piddling, pint-sized, pitiful, pocket-sized, puny, runty, scrubby, stunted, tensy, teeny, trivial, undersized, unpretentious", "armsAA": "arm-frame-small", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.2,"headAA": "-","keywords": "long, deep, great, high, lengthy, protracted, tall, continued, elongate, elongated, enduring, enlarged, expanded, lasting, lengthened, lingering, prolonged, running, stretch, stretching, sustained, towering, distant, ", "armsAA": "arm-compare-bigger", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.2,"headAA": "-","keywords": "short", "armsAA": "arm-compare-smaller", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 1,"headAA": "-","keywords": "longer, larger", "armsAA": "arm-compare-bigger", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "shorter, briefer, curtailed, diminished, less, lessened, lower,more concise, reduced, reducing, diminishing, briefing, lowering, lesser ", "armsAA": "arm-compare-smaller", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.2,"headAA": "-","keywords": "enlarge, bigger, more, larger, largest, better, ", "armsAA": "arm-compare-bigger", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "smaller, lesser", "armsAA": "arm-compare-smaller", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "several, some", "armsAA": "arm-unsure", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.2,"headAA": "-","keywords": "a few, a little, a bit, barely, hardly, ", "armsAA": "arm-unsure", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "none, nothing, noway, ", "armsAA": "arm-rejection", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "a lot, many, massive, lots of, countless, substantial, considerable, extensive, enormous, ", "armsAA": "arm-frame-big", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "entirely, exactly, fully, purely, totally, wholly, utterly, completely", "armsAA": "arm-compare-bigger", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "everything, all, whole, several, plenty, full, entire, exact, full, quite, total, altogether, all in all, just, ", "armsAA": "arm-frame-big", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.8,"headAA": "-","keywords": "exclusive, integral, total, unabridged, choate, completed, concentrated, conclusive, consummate, every, exhaustive, fixed, fulfilled, full-length, in one piece, inclusive, outright, plenary, rounded, unabbreviated, uncut, undivided, unexpurgated, unqualified, utter, exceptional, conclusive, certain, accomplished, concentrated, ", "armsAA": "arm-rolling", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "important, considerable, leading, main, popular, powerful, serious, significant, substantial, valuable, big league, big-time, consequential, eminent, heavy-duty, heavyweight, influential, major league, material, meaningful, momentous, paramount, prime, principal, prominent, super, super colossal, weighty, ", "armsAA": "arm-offer", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.8,"headAA": "-","keywords": "contradict, diverge, vary, bracket, collate, conflict, depart, deviate, differentiate, mismatch, oppose, separate", "armsAA": "arm-compare-bigger", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "amazing, affected, affect, affecting, astonishing, astounding, impressing, perplex, , stunning, awesome, amazed, astonish, impresive, outstanding, phenomenal", "armsAA": "arm-greeting", "eyesAA": "-"},{"priority": , "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "comfortable, elementary, reachable, possible, comfy, peaceful, ", "armsAA": "arm-offer", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.8,"headAA": "-","keywords": "astonishing, astounding, awesome, breathtaking, fantastic, incredible, marvelous, outrageous, phenomenal, remarkable, spectacular, superb, terrific, unbelievable, fabulous, ", "armsAA": "arm-greeting", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "narrow, cramped, definite, limited, precarious, precise, slender, slim, thin, tight, sad, bad, disgust, worse, shame, evil, inappropriate, rotten, nasty, horrible, debris, droppings, junk, litter, residue, rubbish, rubble, sediment, waste,awful, cheap, crummy, dreadful, lousy, poor, rough, sad, unacceptable, blah, bummer, diddly, downer, garbage, gross, imperfect, inferior, junky, synthetic, abominable, amiss, bad news, beastly, bottom out, careless, cheesy, crappy, cruddy, defective ,deficient, dissatisfactory, erroneous, fallacious, faulty, godawful, grody, grungy, depressing, disconcerting, discouraging, disheartening, distasteful, frustrating, mediocre, unpleasant, unsatisfying, awkward, ", "armsAA": "arm-notknow", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.8,"headAA": "-","keywords": "awful, distressing, dreadful, ", "armsAA": "arm-rejection", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "difficult, tough, ambitious, arduous, burdensome, challenging, troublesome, puzzle, impossible, hard, ", "armsAA": "arm-frame-small", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "easy, accessible,clear,effortless,obvious,painless,simple,smooth,straightforward,uncomplicated, reachable, cozy", "armsAA": "arm-unsure", "eyesAA": "-"},{"priority": 30, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "yours, yourself, them, those, that, these, you, ", "armsAA": "arm-offer", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.8,"headAA": "-","keywords": " yes, yeah, i do, i am, we have, we do, you have, true, ok, i like, i love, you like, ya, ", "armsAA": "arm-me", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": " agree, admit, accept, accustomed, acknowledged, allowed, approved, authorized, established, confirmed, supported, permitted, allow, comply, welcomed, agreed, grant, granted, ", "armsAA": "arm-offer", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.8,"headAA": "-","keywords": "no, nothing, cannot, can''t, unable, never, don''t know", "armsAA": "arm-beat", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "correct, right, appropriate, equitable, factual, legitimate, perfect, precise, proper, strict, true, okay, accurately, justly, nicely, perfectly, precisely, properly, rightly, ", "armsAA": "arm-greeting", "eyesAA": "-"},{"priority": 80, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "incorrect, wrong, erroneous, false, faulty, flawed, imprecise, improper, inaccurate, inappropriate, mistaken, unreliable, unsound, untrue, not true, not okay, fake, untrue, ", "armsAA": "arm-notknow", "eyesAA": "-"},{"priority": 55, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "can you, could you, do you, ", "armsAA": "arm-offer", "eyesAA": "-"},{"priority": 50, "browAA": "-", "probability": 0.8,"headAA": "-","keywords": "well, i guess , i suppose, i think, maybe, perhaps, could, probably, might, actually, only, i believe, i suggest, likely, ", "armsAA": "arm-point", "eyesAA": "-"},{"priority": 85, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "well, i guess , i suppose, i think, maybe, perhaps, could, probably, might, actually, only, i believe, i suggest, likely, ", "armsAA": "arm-point", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.8,"headAA": "-","keywords": "have to, must, need to, ought to, ought to, should, shall, going to, will, willing to, ", "armsAA": "arm-offer", "eyesAA": "-"},{"priority": 35, "browAA": "-", "probability": 0.8,"headAA": "-","keywords": "but, although, however, nevertheless, on the other hand, though, yet, aside from, other than, nonetheless, after all, despite, rather", "armsAA": "arm-offer", "eyesAA": "-"},{"priority": 35, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "and, along with, also, as a consequence, as well as, including, together with, besides, additionally, also, still, too, additionally, along with, as well, besides, in addition, likewise, withal, again, ", "armsAA": "arm-rolling", "eyesAA": "-"},{"priority": 30, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "likewise, again, more, still, further, besides, additionally, furthermore, moreover, beyond, besides, as a consequence, better, longer, beyond, in addition, what''s more, better, longer, higher, ", "armsAA": "arm-rolling", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 1,"headAA": "-","keywords": "really, very, quite, completely, great, absolutely, just, quite, incredible, epic, certainly, actually, indeed, literally, surely, truly, undoubtely, unquestionably", "armsAA": "arm-frame-small", "eyesAA": "-"},{"priority": 50, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "normal, ordinary, simple, general, familiar, typical, common, so so, regular, ", "armsAA": "arm-notknow", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "Profound, progressive, advanced, outstanding, Knowledgeable, enlightened, forward-looking, Visionary, ", "armsAA": "arm-point", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "alike, comparable, related, similar, familiar", "armsAA": "arm-offer", "eyesAA": "-"},{"priority": 65, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "abnormal, atypical, different, irregular, special, uncommon, unconventional, untraditional, unusual, exceptional, rare, anomalous, changing, disorderly, eccentric, extraordinary, imbalanced, inconsistent, infrequent, unsteady, variable, outstanding, exclusive, ", "armsAA": "arm-point", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "rare, extraordinary, limited, occasional, scarce, singular, strange, subtle, uncommon, unique, unlikely, unusual, narrow, cramped, definite, limited, ", "armsAA": "arm-unsure", "eyesAA": "-"},{"priority": 75, "browAA": "-", "probability": 1,"headAA": "-","keywords": "sad, bad, disgust, worse, shame, evil, inappropriate, rotten, nasty, horrible, unhappy, pathetic, deplorable, feeble, heartbreaking, miserable, pitiful, poignant, sorry, woeful, ", "armsAA": "arm-unsure", "eyesAA": "-"},{"priority": 50, "browAA": "-", "probability": 0.2,"headAA": "-","keywords": "stop, pause, freeze, bar, block, break, conclusion, pause, drop, end, hold on, ", "armsAA": "arm-point", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": " continue, advance, endure, extend, go on, will last, linger, maintain, progress, promote, pursue, remain, will stay, survive, sustain, hold, sustaining, continuing, lasting, enduring, extending, remaining, promoting, progressing, lingering, ", "armsAA": "arm-compare-bigger", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "done, finished, completed, compassed, complete, completed, concluded, consummated, depleted, drained, effected, ended, executed, exhausted, fixed, fulfilled, perfected, performed, realized, rendered, succeeded, terminated, ", "armsAA": "arm-point", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "uncertain, ambiguous,ambivalent, dubious, erratic, hazy, hesitant, insecure, precarious, questionable, risky, unclear, undecided, undetermined, unpredictable, unreliable,unresolved, unsettled, unsure, suspicous, doubtable, doubtful, questionable, ambiguous, arguable, puzzling, ", "armsAA": "arm-notknow", "eyesAA": "-"},{"priority": 50, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "kind of, slightly, lightly, somewhat, at least, rather, moderately, sort of, to some extent, at least", "armsAA": "arm-unsure", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "conflicting, oppose, argue, assail, attack, debate, defy, deny, disagree, dispute, fight, prevent, protest, resist, assault, bar, battle, bombard, combat, confront, contradict, controvert, counter, counterattack, disapprove, encounter, expose, gainsay, hinder neutralize, reverse, taunt, thwart, withstand, object to, opposed, abide, combat, confront, continue, curb, defy, endure, forgo, maintain, prevent, refuse, repel, thwart, turn down, ", "armsAA": "arm-beat", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.8,"headAA": "-","keywords": "adaptable, adventurous, affable, affectionate, agreeable, ambitious, amiable, amicable, amusing, brave, bright, broad-minded, calm, careful, charming, communicative, compassionate, conscientious, considerate, convivial, courageous, courteous, creative, decisive, determined, diligent, diplomatic, discreet, dynamic, easygoing, emotional, energetic, enthusiastic, exuberant, fair-minded, faithful, fearless, forceful, friendly, funny, generous, gentle, good, gregarious, hard-working, helpful, honest, humorous, imaginative, impartial, independent, intellectual, intelligent, intuitive, inventive, kind, loving, loyal, modest, neat, nice, optimistic, passionate, patient, persistent, pioneering, philosophical, placid, plucky, polite, powerful, practical, pro-active, quick-witted, quiet, rational, reliable, reserved, resourceful, romantic, self-confident, self-disciplined, sensible, sensitive, shy, sincere, sociable, straightforward, sympathetic, thoughtful, tidy, tough, unassuming, understanding, versatile, warmhearted, willing, witty, attractive, absorbing, alluring, amiable, appealing, attractive, charismatic, cute, delightful, elegant, engaging, engrossing, fascinating, glamorous, graceful, inviting, likable, lovable, lovely, pleasant, provocative, sweet, loyal, ", "armsAA": "arm-rolling", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "Arrogant, Big-headed, Self-centred, Vain, Boastful, Pompous, Aloof, Impolite, Inconsiderate, Thoughtless, Confrontational, Defensive, Hostile, Belligerent, Bitchy, Nasty, Bossy, Cruel, Domineering, Sneaky, Impatient, Unreliable, Jealous, Careless, Irresponsible, Untidy, Cowardly, Foolish, Gullible, Indecisive, Weak-willed, Grumpy, Silly, infamous, disgraceful, egregious, hateful, heinous, ignominious, despicable, humiliating, shameful, immoral, corrupt, depraved, dishonest, indecent, nefarious, obscene, pornographic, shameless, sinful, unethical, unscrupulous, corrupted, ", "armsAA": "arm-rejection", "eyesAA": "-"},{"priority": 75, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "evil, gloomy, nasty, ominous, rough, evil, gloomy, nasty, ominous, rough, unattractive, unpleasant, disagreeable,disgusting, repugnant, repulsive, unappealing, bad-looking, gross, ", "armsAA": "arm-rejection", "eyesAA": "-"},{"priority": 65, "browAA": "-", "probability": 0.8,"headAA": "-","keywords": "cheerful, contented, delighted, ecstatic, elated, glad, joyful, joyous, jubilant, livelymerry,overjoyed, peaceful, pleasant, pleased, thrilled, upbeat, blessed", "armsAA": "arm-frame-small", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "sad, bad, disgust, worse, shame, evil, inappropriate, rotten, nasty, horrible, unhappy, pathetic, deplorable, feeble, heartbreaking, miserable, pitiful, poignant, sorry, woeful, ", "armsAA": "arm-unsure", "eyesAA": "-"},{"priority": 30, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "hello, welcome, bye, see you, good bye, Hi, ", "armsAA": "arm-frame-big", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "impact, astonishing, extraordinary, shocking, amazed, amazing, astonish, astonished, impacted, shocked, impacting, surprise, surprising, dominant, ", "armsAA": "arm-frame-big", "eyesAA": "-"},{"priority": 80, "browAA": "-", "probability": 0.8,"headAA": "-","keywords": "angry, annoyed, bitter, enraged, exasperated, furious, heated, impassioned, indignant, irate, irritable, irritated, offended, ", "armsAA": "arm-rejection", "eyesAA": "-"},{"priority": 40, "browAA": "-", "probability": 0.8,"headAA": "-","keywords": "love, like, wonder, admire, adore, approve, cherish, love, commend, compliment", "armsAA": "arm-greeting", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "unhappy, aversion, disapproval, disgust, displeasure, dislike,", "armsAA": "arm-notknow", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 1,"headAA": "-","keywords": "hate", "armsAA": "arm-rejection", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "poor, poorly, impoverished, inconsiderable, insignificant, miniature, poor, teeny, thin, tiny, unimportant, delicate, feeble, frail, weak, fragile, junky, crummy, cheesy, crappy, cruddy, garbage, disappointing, ", "armsAA": "arm-notknow", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 1,"headAA": "-","keywords": "huge, enormous, extensive, giant, gigantic, great, humongous, immense, magnificent, mammoth, massive, tremendous, vast, immeasurable, oversize, big, large, ", "armsAA": "arm-compare-bigger", "eyesAA": "-"},{"priority": 70, "browAA": "-", "probability": 1,"headAA": "-","keywords": "cramped, limited, meager, microscopic, miniature, minuscule, modest, narrow, paltry, poor, short, slight, small-scale, young, baby, bantam, diminutive, mini, cramped, limited, meager, microscopic, miniature, minuscule, narrow, paltry, poor, short, slight, small-scale, young, baby, bantam, diminutive, little, miniminute, petite, petty, scanty, shrimp, toy, trifling, bitty, humble, immature, inadequate, inconsequential, inconsiderable, insufficient, picayune, piddling, pint-sized, pitiful, pocket-sized, puny, runty, scrubby, stunted, tensy, teeny, trivial, undersized, unpretentious", "armsAA": "arm-compare-smaller", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.2,"headAA": "-","keywords": "enlarge, bigger, more, larger, largest, better, ", "armsAA": "arm-contrast", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "several, some", "armsAA": "arm-point", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.2,"headAA": "-","keywords": "a few, a little, a bit, barely, hardly, ", "armsAA": "arm-point", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "a lot, many, massive, lots of, countless, substantial, considerable, extensive, enormous, ", "armsAA": "arm-many", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "entirely, exactly, fully, purely, totally, wholly, utterly, completely", "armsAA": "arm-frame-big", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "everything, all, whole, several, plenty, full, entire, exact, full, quite, total, altogether, all in all, just, ", "armsAA": "arm-offer", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.8,"headAA": "-","keywords": "exclusive, integral, total, unabridged, choate, completed, concentrated, conclusive, consummate, every, exhaustive, fixed, fulfilled, full-length, in one piece, inclusive, outright, plenary, rounded, unabbreviated, uncut, undivided, unexpurgated, unqualified, utter, exceptional, conclusive, certain, accomplished, concentrated, ", "armsAA": "arm-frame-small", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "important, considerable, leading, main, popular, powerful, serious, significant, substantial, valuable, big league, big-time, consequential, eminent, heavy-duty, heavyweight, influential, major league, material, meaningful, momentous, paramount, prime, principal, prominent, super, super colossal, weighty, ", "armsAA": "arm-beat", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.8,"headAA": "-","keywords": "contradict, diverge, vary, bracket, collate, conflict, depart, deviate, differentiate, mismatch, oppose, separate", "armsAA": "arm-greeting", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "amazing, affected, affect, affecting, astonishing, astounding, impressing, perplex, , stunning, awesome, amazed, astonish, impresive, outstanding, phenomenal", "armsAA": "arm-beat", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.8,"headAA": "-","keywords": "astonishing, astounding, awesome, breathtaking, fantastic, incredible, marvelous, outrageous, phenomenal, remarkable, spectacular, superb, terrific, unbelievable, fabulous, ", "armsAA": "arm-compare-bigger", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.5,"headAA": "-","keywords": "narrow, cramped, definite, limited, precarious, precise, slender, slim, thin, tight, sad, bad, disgust, worse, shame, evil, inappropriate, rotten, nasty, horrible, debris, droppings, junk, litter, residue, rubbish, rubble, sediment, waste,awful, cheap, crummy, dreadful, lousy, poor, rough, sad, unacceptable, blah, bummer, diddly, downer, garbage, gross, imperfect, inferior, junky, synthetic, abominable, amiss, bad news, beastly, bottom out, careless, cheesy, crappy, cruddy, defective ,deficient, dissatisfactory, erroneous, fallacious, faulty, godawful, grody, grungy, depressing, disconcerting, discouraging, disheartening, distasteful, frustrating, mediocre, unpleasant, unsatisfying, awkward, ", "armsAA": "arm-rejection", "eyesAA": "-"},{"priority": 60, "browAA": "-", "probability": 0.8,"headAA": "-","keywords": "awful, distressing, dreadful, ", "armsAA": "arm-beat", "eyesAA": "-"}]'
+animation_actions:
+ head-up:
+ - {name: 'ADD_HeadUp', duration: 1.6, begin: 0.21, max: 0.73, end: 0.92, channel: ['head']}
+ head-down:
+ - {name: 'ADD_HeadDownShort', duration: 1.6, begin: 0.21, max: 0.73, end: 0.94, channel: ['head']}
+ - {name: 'ADD_HeadDownLong', duration: 2.46, begin: 0.21, max: 0.75, end: 1.69, channel: ['head']}
+ head-left:
+ - {name: 'ADD_HeadL', duration: 1.85, begin: 0.02, max: 0.52, end: 1.08, channel: ['head']}
+ head-right:
+ - {name: 'ADD_HeadR', duration: 2.04, begin: 0.02, max: 0.54, end: 1.19, channel: ['head']}
+ head-aside:
+ - {name: 'ADD_HeadRUp', duration: 1.81, begin: 0.02, max: 0.42, end: 1.06, channel: ['head']}
+ - {name: 'ADD_HeadR', duration: 2.04, begin: 0.02, max: 0.54, end: 1.19, channel: ['head']}
+ - {name: 'ADD_HeadL', duration: 1.85, begin: 0.02, max: 0.52, end: 1.08, channel: ['head']}
+ - {name: 'ADD_HeadLUp', duration: 1.6, begin: 0.02, max: 0.38, end: 0.94, channel: ['head']}
+ head-tilt:
+ - {name: 'ADD_TiltRightShort', duration: 2.38, begin: 0.21, max: 0.73, end: 1.77, channel: ['head']}
+ head-tilt-long:
+ - {name: 'ADD_TiltRightLong', duration: 4.02, begin: 0.42, max: 0.94, end: 3.33, channel: ['head']}
+ nod-short:
+ - {name: 'ADD_AgreeTiltFront', duration: 2.1, begin: 0.04, max: 1.6, end: 1.67, channel: ['head']}
+ - {name: 'ADD_NodShort', duration: 2.38, begin: 0.33, max: 1.98, end: 2.08, channel: ['head']}
+ nod-long:
+ - {name: 'ADD_NodLong', duration: 2.77, begin: 0.19, max: 2.02, end: 2.21, channel: ['head']}
+ nod-heavy:
+ - {name: 'ADD_NodHeavy', duration: 1.81, begin: 0.08, max: 1.1, end: 1.46, channel: ['head']}
+ - {name: 'ADD_NodShort', duration: 2.38, begin: 0.33, max: 1.98, end: 2.08, channel: ['head']}
+ shake-short:
+ - {name: 'ADD_ShakeShort', duration: 1.19, begin: 0.02, max: 0.88, end: 1.08, channel: ['head']}
+ - {name: 'ADD_Shakeshort2', duration: 1.25, begin: 0.06, max: 0.83, end: 1.02, channel: ['head']}
+ shake-long:
+ - {name: 'ADD_ShakeLong', duration: 2.67, begin: 0.02, max: 1.85, end: 2.31, channel: ['head']}
+ - {name: 'ADD_ShakeSoft', duration: 2.98, begin: 0.31, max: 1.46, end: 1.77, channel: ['head']}
+ - {name: 'ADD_ShakeSoft2', duration: 0, begin: 0, max: 0, end: 0, channel: ['head']}
+ shake-firmly:
+ - {name: 'ADD_ShakeFirmly', duration: 2.15, begin: 0.25, max: 1.5, end: 1.75, channel: ['head']}
+ shake-heavy:
+ - {name: 'ADD_ShakeHeavy', duration: 1.79, begin: 0.21, max: 1.15, end: 1.35, channel: ['head']}
+ wobbling:
+ - {name: 'ADD_Wobbling1', duration: 2.35, begin: 0.21, max: 0.58, end: 1.71, channel: ['head']}
+ - {name: 'ADD_Wobbling2', duration: 2.48, begin: 0.21, max: 0.83, end: 1.83, channel: ['head']}
+ - {name: 'ADD_Wobbling3', duration: 2.38, begin: 0.04, max: 0.71, end: 1.69, channel: ['head']}
+ brow-up-short:
+ - {name: 'ADD_BrowsUpShort', duration: 1.54, begin: 0.02, max: 0.48, end: 0.88, channel: ['brows']}
+ brow-up-normal:
+ - {name: 'ADD_BrowsUpNormal', duration: 1.31, begin: 0.02, max: 0.81, end: 0.92, channel: ['brows']}
+ brow-up-long:
+ - {name: 'ADD_BrowsUpLong', duration: 2.98, begin: 0.02, max: 2.19, end: 2.42, channel: ['brows']}
+ brow-center-up:
+ - {name: 'ADD_CenterUpShort', duration: 1.33, begin: 0.21, max: 0.6, end: 0.73, channel: ['brows']}
+ brow-center-up-long:
+ - {name: 'ADD_CenterUpLong', duration: 3.25, begin: 0.21, max: 2.42, end: 2.63, channel: ['brows']}
+ - {name: 'ADD_CenterUpLong2', duration: 4.85, begin: 0.25, max: 1.83, end: 3.54, channel: ['brows']}
+ brow-down-short:
+ - {name: 'ADD_BrowsDownShort', duration: 1.08, begin: 0.02, max: 0.48, end: 0.65, channel: ['brows']}
+ - {name: 'ADD_BrowsDown', duration: 2.79, begin: 0.21, max: 1.29, end: 2.08, channel: ['brows']}
+ brow-down-normal:
+ - {name: 'ADD_BrowsDownNormal', duration: 1.31, begin: 0.02, max: 0.71, end: 0.88, channel: ['brows']}
+ brow-down-long:
+ - {name: 'ADD_BrowsDownLong', duration: 2.27, begin: 0.02, max: 1.67, end: 1.83, channel: ['brows']}
+ brow-doubt:
+ - {name: 'ADD_DoubtShort', duration: 1.79, begin: 0.13, max: 1.15, end: 1.25, channel: ['brows']}
+ brow-doubt-long:
+ - {name: 'ADD_DoubtLong', duration: 3.04, begin: 0.19, max: 2.58, end: 2.71, channel: ['brows']}
+ - {name: 'ADD_DoubtLong2', duration: 3.98, begin: 0.21, max: 2.19, end: 2.71, channel: ['brows']}
+ - {name: 'ADD_DoubtLong3', duration: 2.79, begin: 0.21, max: 1.29, end: 2.08, channel: ['brows']}
+ eyelid-half-close:
+ - {name: 'ADD_EyeHalfCloseLong', duration: 1.94, begin: 0.02, max: 1.38, end: 1.46, channel: ['eyelids']}
+ - {name: 'ADD_EyeHalfCloseShort', duration: 1.21, begin: 0.02, max: 0.23, end: 0.67, channel: ['eyelids']}
+ eyelid-widely-open:
+ - {name: 'ADD_EyesWideOpenSmall', duration: 2.5, begin: 0.02, max: 1.56, end: 2.13, channel: ['eyelids']}
+ - {name: 'ADD_EyesWideOpenBig', duration: 2.19, begin: 0.02, max: 1.21, end: 1.88, channel: ['eyelids']}
+ eye-left:
+ - {name: 'ADD_EyesLeft', duration: 1.54, begin: 0.02, max: 0.23, end: 0.96, channel: ['eyes']}
+ eye-right:
+ - {name: 'ADD_EyesRight', duration: 1.48, begin: 0.02, max: 0.25, end: 1, channel: ['eyes']}
+ eye-up:
+ - {name: 'ADD_EyesUp', duration: 1.58, begin: 0.02, max: 0.38, end: 1.06, channel: ['eyes']}
+ eye-down:
+ - {name: 'ADD_EyesDown', duration: 2.19, begin: 0.02, max: 0.44, end: 1.23, channel: ['eyes']}
+ eye-around:
+ - {name: 'ADD_EyesLeftRightUp', duration: 4.81, begin: 0.02, max: 2.29, end: 3.23, channel: ['eyes']}
+ eye-corner:
+ - {name: 'ADD_EyesRightDown', duration: 1.9, begin: 0.02, max: 0.4, end: 1.02, channel: ['eyes']}
+ - {name: 'ADD_EyesRightUp', duration: 2.21, begin: 0.02, max: 0.42, end: 1.17, channel: ['eyes']}
+ - {name: 'ADD_EyesLeftDown', duration: 1.56, begin: 0.02, max: 0.31, end: 0.83, channel: ['eyes']}
+ - {name: 'ADD_EyesLeftUp', duration: 1.69, begin: 0.02, max: 0.25, end: 0.94, channel: ['eyes']}
+ eye-side:
+ - {name: 'ADD_EyesLeft', duration: 1.54, begin: 0.02, max: 0.23, end: 0.96, channel: ['eyes']}
+ - {name: 'ADD_EyesRight', duration: 1.48, begin: 0.02, max: 0.25, end: 1, channel: ['eyes']}
+ - {name: 'ADD_EyesUp', duration: 1.58, begin: 0.02, max: 0.38, end: 1.06, channel: ['eyes']}
+ - {name: 'ADD_EyesDown', duration: 2.19, begin: 0.02, max: 0.44, end: 1.23, channel: ['eyes']}
+ arm-unsure:
+ - {name: 'SIT_0-R_approximation', duration: 3.38, begin: 0.71, max: 2.21, end: 2.63, channel: ['arms']}
+ arm-beat:
+ - {name: 'SIT_1-R_Important', duration: 4.67, begin: 0.83, max: 3.25, end: 3.71, channel: ['arms']}
+ - {name: 'SIT_1-L_Important', duration: 4.13, begin: 0.29, max: 2.42, end: 3.13, channel: ['arms']}
+ - {name: 'SIT_0-R_beat_chop_up', duration: 3.25, begin: 0.63, max: 1.67, end: 2.5, channel: ['arms']}
+ - {name: 'SIT_0-R_beat_chop_down', duration: 2.58, begin: 0.63, max: 1.25, end: 1.88, channel: ['arms']}
+ - {name: 'SIT_0-R_beat_fist_in', duration: 2.5, begin: 0.75, max: 1.46, end: 2.08, channel: ['arms']}
+ - {name: 'SIT_0-R_beat_fist_out', duration: 2.92, begin: 0.42, max: 1.13, end: 1.67, channel: ['arms']}
+ arm-me:
+ - {name: 'SIT_0-R_deictic_gestures_me', duration: 3.25, begin: 0.92, max: 1.75, end: 2.42, channel: ['arms']}
+ - {name: 'SIT_1-R_Me', duration: 4.75, begin: 0.83, max: 2, end: 3.25, channel: ['arms']}
+ - {name: 'SIT_1-L_Me', duration: 5.08, begin: 0.83, max: 2.33, end: 3.58, channel: ['arms']}
+ arm-you:
+ - {name: 'SIT_0-R_deictic_gestures_you', duration: 3.42, begin: 0.67, max: 1.46, end: 2.33, channel: ['arms']}
+ - {name: 'SIT_0-L_deictic_gestures_left', duration: 3.92, begin: 0.79, max: 1.67, end: 2.5, channel: ['arms']}
+ - {name: 'SIT_0-R_deictic_gestures_right', duration: 3.71, begin: 0.67, max: 1.67, end: 2.5, channel: ['arms']}
+ - {name: 'SIT_0-L_deictic_gestures_we', duration: 3.13, begin: 0.71, max: 2.29, end: 2.75, channel: ['arms']}
+ - {name: 'SIT_1-L_Youall', duration: 5.13, begin: 0.63, max: 3.33, end: 3.83, channel: ['arms']}
+ - {name: 'SIT_1-R_Youall', duration: 5.13, begin: 0.63, max: 3.33, end: 3.83, channel: ['arms']}
+ - {name: 'SIT_1-R_Likesth', duration: 4.63, begin: 0.67, max: 1.71, end: 2.71, channel: ['arms']}
+ - {name: 'SIT_1-L_Likesth', duration: 4.63, begin: 0.67, max: 1.71, end: 2.71, channel: ['arms']}
+ - {name: 'SIT_1-L_Intorducesomeone', duration: 3.58, begin: 0.5, max: 1.83, end: 2.67, channel: ['arms']}
+ - {name: 'SIT_1-R_Intorducesomeone', duration: 3.58, begin: 0.79, max: 1.83, end: 2.67, channel: ['arms']}
+ - {name: 'SIT_1-R_Present', duration: 4.17, begin: 0.71, max: 1.58, end: 2.5, channel: ['arms']}
+ - {name: 'SIT_1-L_Present', duration: 4.17, begin: 0.71, max: 1.58, end: 2.5, channel: ['arms']}
+ arm-many:
+ - {name: 'SIT_1-L_3things', duration: 8.13, begin: 0.71, max: 5.08, end: 6.25, channel: ['arms']}
+ - {name: 'SIT_1-R_3things', duration: 8.13, begin: 0.71, max: 5.08, end: 6.25, channel: ['arms']}
+ - {name: 'SIT_1-L_Thanksfor3', duration: 7.54, begin: 0.83, max: 5, end: 5.83, channel: ['arms']}
+ - {name: 'SIT_1-R_Thanksfor3', duration: 7.54, begin: 0.83, max: 5, end: 5.83, channel: ['arms']}
+ arm-vawe:
+ - {name: 'SIT_0-L_hello', duration: 2.83, begin: 0.63, max: 2.08, end: 2.5, channel: ['arms']}
+ - {name: 'SIT_1-R_Hello', duration: 3.83, begin: 0.42, max: 2.33, end: 2.71, channel: ['arms']}
+ - {name: 'SIT_1-L_Hello', duration: 3.83, begin: 0.42, max: 2.33, end: 2.71, channel: ['arms']}
+ - {name: 'SIT_0-R_bye', duration: 2.54, begin: 0.63, max: 1.25, end: 1.67, channel: ['arms']}
+ arm-point:
+ - {name: 'SIT_1-R_Idea', duration: 4.79, begin: 0.5, max: 1.25, end: 2.29, channel: ['arms']}
+ - {name: 'SIT_1-L_Idea', duration: 4.79, begin: 0.5, max: 1.25, end: 2.29, channel: ['arms']}
+ - {name: 'SIT_1-L_Click', duration: 5, begin: 0.75, max: 2.13, end: 3.13, channel: ['arms']}
+ - {name: 'SIT_1-R_Click', duration: 5, begin: 0.75, max: 2.13, end: 3.13, channel: ['arms']}
+ arm-greeting:
+ - {name: 'SIT_1-L_Thumb_up', duration: 2.88, begin: 0.58, max: 1.33, end: 1.96, channel: ['arms']}
+ - {name: 'SIT_1-R_Thumb_up', duration: 2.88, begin: 0.58, max: 1.33, end: 1.96, channel: ['arms']}
+ - {name: 'SIT_1-B_Yo', duration: 3.79, begin: 0.83, max: 1.88, end: 2.88, channel: ['arms','arms']}
+ arm-heart:
+ - {name: 'SIT_1-B_Heart', duration: 6.5, begin: 1.33, max: 4.5, end: 5.46, channel: ['arms','arms']}
+ arm-frame-small:
+ - {name: 'SIT_0-B_frame_small', duration: 3.42, begin: 0.83, max: 1.42, end: 2.38, channel: ['arms','arms']}
+ arm-frame-big:
+ - {name: 'SIT_0-B_frame_big', duration: 3.25, begin: 0.42, max: 1.25, end: 1.83, channel: ['arms','arms']}
+ arm-compare-smaller:
+ - {name: 'SIT_0-B_contrasts_smaller', duration: 5.08, begin: 0.63, max: 3.33, end: 3.63, channel: ['arms','arms']}
+ arm-compare-bigger:
+ - {name: 'SIT_0-B_contrasts_bigger', duration: 4.21, begin: 0.5, max: 2.5, end: 2.92, channel: ['arms','arms']}
+ - {name: 'SIT_1-B_Smallandbig', duration: 6.21, begin: 1.04, max: 3.92, end: 4.79, channel: ['arms','arms']}
+ arm-contrast:
+ - {name: 'SIT_1-R_1and2', duration: 5.17, begin: 0.58, max: 3.33, end: 4.17, channel: ['arms']}
+ - {name: 'SIT_1-L_1and2', duration: 5.17, begin: 0.58, max: 3.33, end: 4.17, channel: ['arms']}
+ arm-offer:
+ - {name: 'SIT_0-L_offer', duration: 3.54, begin: 0.92, max: 1.88, end: 2.5, channel: ['arms']}
+ - {name: 'SIT_0-R_deictic_gestures_you', duration: 3.42, begin: 0.67, max: 1.46, end: 2.33, channel: ['arms']}
+ - {name: 'SIT_0-L_deictic_gestures_left', duration: 3.92, begin: 0.79, max: 1.67, end: 2.5, channel: ['arms']}
+ - {name: 'SIT_0-R_deictic_gestures_right', duration: 3.71, begin: 0.67, max: 1.67, end: 2.5, channel: ['arms']}
+ - {name: 'SIT_1-L_Intorducesomeone', duration: 3.58, begin: 0.5, max: 1.83, end: 2.67, channel: ['arms']}
+ - {name: 'SIT_1-R_Intorducesomeone', duration: 3.58, begin: 0.5, max: 1.83, end: 2.67, channel: ['arms']}
+ - {name: 'SIT_1-R_Present', duration: 4.17, begin: 0.71, max: 1.58, end: 2.5, channel: ['arms']}
+ - {name: 'SIT_1-L_Present', duration: 4.17, begin: 0.71, max: 1.58, end: 2.5, channel: ['arms']}
+ - {name: 'SIT_1-L_Youall', duration: 5.13, begin: 0.63, max: 3.33, end: 3.83, channel: ['arms']}
+ - {name: 'SIT_1-R_Youall', duration: 5.13, begin: 0.63, max: 3.33, end: 3.83, channel: ['arms']}
+ - {name: 'SIT_1-R_Likesth', duration: 4.63, begin: 0.67, max: 1.71, end: 2.71, channel: ['arms']}
+ - {name: 'SIT_1-L_Likesth', duration: 4.63, begin: 0.67, max: 1.71, end: 2.71, channel: ['arms']}
+ arm-process:
+ - {name: 'SIT_0-L_process_control', duration: 2.67, begin: 0.63, max: 1.54, end: 1.88, channel: ['arms']}
+ - {name: 'SIT_0-R_process_control', duration: 2.54, begin: 0.5, max: 1.38, end: 1.71, channel: ['arms']}
+ - {name: 'SIT_1-R_Important', duration: 4.67, begin: 0.83, max: 3.25, end: 3.71, channel: ['arms']}
+ - {name: 'SIT_1-L_Important', duration: 4.13, begin: 0.29, max: 2.42, end: 3.13, channel: ['arms']}
+ - {name: 'SIT_1-R_Idea', duration: 3.79, begin: 0.5, max: 1.42, end: 2.29, channel: ['arms']}
+ - {name: 'SIT_1-L_Idea', duration: 3.79, begin: 0.5, max: 1.42, end: 2.29, channel: ['arms']}
+ - {name: 'SIT_1-L_Click', duration: 5, begin: 0.75, max: 2.13, end: 3.13, channel: ['arms']}
+ - {name: 'SIT_1-R_Click', duration: 5, begin: 0.75, max: 2.13, end: 3.13, channel: ['arms']}
+ arm-reject:
+ - {name: 'SIT_0-R_rejection', duration: 2.88, begin: 0.63, max: 1.33, end: 2.08, channel: ['arms']}
+ - {name: 'SIT_0-R_rejection2', duration: 3.83, begin: 1.46, max: 2.75, end: 3.33, channel: ['arms']}
+ - {name: 'SIT_1-R_No', duration: 4.79, begin: 0.71, max: 3, end: 3.5, channel: ['arms']}
+ - {name: 'SIT_1-L_No', duration: 4.79, begin: 0.71, max: 3, end: 3.5, channel: ['arms']}
+ arm-rolling:
+ - {name: 'SIT_1-B_rolling', duration: 4.17, begin: 0.83, max: 2.92, end: 4.17, channel: ['arms']}
+ arm-notknow:
+ - {name: 'SIT_1-B_notknow', duration: 1.92, begin: 0.42, max: 0.88, end: 1.33, channel: ['arms']}
+ - {name: 'SIT_1-L_notknow', duration: 1.92, begin: 0.42, max: 0.88, end: 1.33, channel: ['arms']}
+ - {name: 'SIT_1-R_notknow', duration: 1.92, begin: 0.42, max: 0.88, end: 1.33, channel: ['arms']}
\ No newline at end of file
diff --git a/modules/performances/src/performances/nodes.py b/modules/performances/src/performances/nodes.py
new file mode 100644
index 0000000..2fa54c2
--- /dev/null
+++ b/modules/performances/src/performances/nodes.py
@@ -0,0 +1,599 @@
+#!/usr/bin/env python
+
+##
+## Copyright (C) 2017-2025 Hanson Robotics
+##
+## This program is free software: you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see .
+##
+
+# Nodes factory
+import os
+import pprint
+import time
+import logging
+import random
+import urllib
+import re
+
+from hr_msgs.msg import ChatMessage
+from hr_msgs.msg import Event
+from hr_msgs.msg import SetAnimation, SetExpression, Target, SomaState
+from hr_msgs.msg import TTS
+from performances.srv import RunByNameRequest
+from std_msgs.msg import String, Int32, Float32
+from threading import Timer
+from topic_tools.srv import MuxSelect
+import dynamic_reconfigure.client
+import requests
+import rospy
+from six import string_types
+
+logger = logging.getLogger('hr.performances.nodes')
+
+
+class Node(object):
+ # Create new Node from JSON
+ @staticmethod
+ def subClasses(cls):
+ return cls.__subclasses__() + [g for s in cls.__subclasses__()
+ for g in cls.subClasses(s)]
+
+ @classmethod
+ def createNode(cls, data, ros, start_time=0, id='', runner=None):
+ for s_cls in cls.subClasses(cls):
+ if data['name'] == s_cls.__name__:
+ # Runner is still needed for Pause
+ node = s_cls(data, ros, runner=runner)
+ node.id = id
+ if start_time > node.start_time:
+ # Start time should be before or on node starting
+ node.finished = True
+
+ if start_time < node.end_time():
+ node.started = True
+
+ return node
+ logger.error("Wrong node description: {0}".format(str(data)))
+
+ @classmethod
+ def create_empty_node_data(cls, type='speech', *args, **kwargs):
+ data = {
+ 'name': type,
+ 'start_time': 0.0,
+ 'duration': 0.0,
+ }
+ for s_cls in cls.subClasses(cls):
+ if data['name'] == s_cls.__name__:
+ if hasattr(s_cls, 'data'):
+ data.update(s_cls.data)
+ data.update(kwargs)
+ return data
+
+
+ def replace_variables_text(self, text):
+ variables = re.findall("{(\w*?)}", text)
+ for var in variables:
+ val = self.ros.get_variable(self.id, var) or ''
+ text = text.replace('{' + var + '}', val)
+ return text
+
+ def __init__(self, data, ros, **kwargs):
+ self.data = data
+ self.duration = max(0.1, float(data['duration']))
+ self.start_time = data['start_time']
+ self.started = False
+ self.started_at = 0
+ self.finished = False
+ self.id = ''
+ # Ros connections for accessing ROS topics and method
+ # TODO make ROS topics and services singletons class for shared use.
+ self.ros = ros
+
+ # By default end time is started + duration for every node
+ def end_time(self):
+ return self.start_time + self.duration
+
+ # Manages node states. Currently start, finish is implemented.
+ # Returns True if its active, and False if its inactive.
+ # TODO make sure to allow node publishing pause and stop
+ def run(self, run_time):
+ # ignore the finished nodes
+ if self.finished:
+ return False if not self.started else run_time < self.end_time()
+
+ if self.started:
+ # Time to finish:
+ if run_time >= self.end_time():
+ self.finished = True
+ self.stop(run_time)
+ return False
+ # elif self.ros.paused:
+ # self.paused(run_time)
+ else:
+ self.cont(run_time)
+ else:
+ if run_time > self.start_time:
+ try:
+ self.start(run_time)
+ except Exception as ex:
+ logger.error(ex)
+ self.started = True
+ self.started_at = time.time()
+ return True
+
+ def __str__(self):
+ return pprint.pformat(self.data)
+
+ # Method to execute if node needs to start
+ def start(self, run_time):
+ pass
+
+ # Method to execute while node is stopping
+ def stop(self, run_time):
+ pass
+
+ # Method to call while node is running
+ def cont(self, run_time):
+ pass
+
+ # # Method to call while runner is paused
+ # def paused(self, run_time):
+ # pass
+
+ # Method to get magnitude from either one number or range
+ @staticmethod
+ def _magnitude(magnitude):
+ try:
+ return float(magnitude)
+ except TypeError:
+ try:
+ # Randomize magnitude
+ return random.uniform(float(magnitude[0]), float(magnitude[1]))
+ except:
+ return 0.0
+
+
+class speech(Node):
+
+ data = {
+ 'lang': 'en-US',
+ 'voice': '',
+ }
+
+ def __init__(self, data, ros, **kwargs):
+ Node.__init__(self, data, ros)
+ if 'pitch' not in data:
+ self.data['pitch'] = 1.0
+ if 'speed' not in data:
+ self.data['speed'] = 1.0
+ if 'volume' not in data:
+ self.data['volume'] = 1.0
+ if 'voice' not in data:
+ self.data['voice'] = ''
+ # Backward compatibility
+ if self.data['lang'] in ['en', 'zh']:
+ self.data['lang'] = {'en': 'en-US', 'zh': 'cmn-Hans-CN'}[self.data['lang']]
+
+ def start(self, run_time):
+ self.say(self.data['text'], self.data['lang'], self.data['voice'])
+
+ def say(self, text, lang, voice):
+ # SSML tags for non-Cantonese
+ text = "|p|"+text
+ if 'HK' not in lang:
+ text = self._add_ssml(text)
+ if 'NONE' in lang:
+ return
+ text = self.replace_variables_text(text)
+ tts = TTS()
+ tts.text = text
+ tts.lang = lang
+ tts.voice = voice
+ self.ros.topics['tts'].publish(tts)
+
+ # adds SSML tags for whole text returns updated text.
+ def _add_ssml(self, txt):
+ # Ignore SSML if simplified syntax is used.
+ if re.search(r"[\*\@]\w+", txt):
+ return txt
+ attrs = ""
+ if self.data['speed'] != 1:
+ attrs += " rate=\"{:.2f}\"".format(self.data['speed'])
+ if self.data['pitch'] != 1:
+ attrs += " pitch=\"{:+.2f}%\"".format((self.data['pitch']-1)*100)
+ if self.data['volume'] != 1:
+ attrs += " volume=\"{:+.0f}dB\"".format((self.data['volume']-1)*100)
+ if len(attrs) > 0:
+ txt = "" + txt + ""
+ return txt
+
+class gesture(Node):
+ def start(self, run_time):
+ self.ros.topics['gesture'].publish(
+ SetAnimation(self.data['gesture'], 1, float(self.data['speed']), self._magnitude(self.data['magnitude'])))
+
+ def stop(self, run_time):
+ if not self.finished and self.started:
+ self.ros.topics['gesture'].publish(
+ SetAnimation(self.data['gesture'], 1, 0, -0.5))
+
+class arm_animation(Node):
+ def start(self, run_time):
+ self.ros.topics['arm_animation'].publish(
+ SetAnimation(self.data['arm_animation'], 1, float(self.data['speed']), self._magnitude(self.data['magnitude'])))
+
+ def stop(self, run_time):
+ if not self.finished and self.started:
+ # Remove arm animation by setting magnitude to 0
+ self.ros.topics['arm_animation'].publish(
+ SetAnimation(self.data['arm_animation'], 1, 0, 0))
+
+
+class emotion(Node):
+ def start(self, run_time):
+ self.ros.topics['emotion'].publish(
+ SetExpression(self.data['emotion'], self._magnitude(self.data['magnitude']),
+ rospy.Duration.from_sec(self.data['duration'])))
+
+
+# Behavior tree
+class interaction(Node):
+ def start(self, run_time):
+ self.ros.topics['bt_control'].publish(Int32(self.data['mode']))
+ if self.data['chat'] == 'listening':
+ self.ros.topics['speech_events'].publish(String('listen_start'))
+ if self.data['chat'] == 'talking':
+ self.ros.topics['speech_events'].publish(String('start'))
+ time.sleep(0.02)
+ self.ros.topics['interaction'].publish(String('btree_on'))
+
+ def stop(self, run_time):
+ # Disable all outputs
+ self.ros.topics['bt_control'].publish(Int32(0))
+
+ if self.data['chat'] == 'listening':
+ self.ros.topics['speech_events'].publish(String('listen_stop'))
+ if self.data['chat'] == 'talking':
+ self.ros.topics['speech_events'].publish(String('stop'))
+ time.sleep(0.02)
+ self.ros.topics['interaction'].publish(String('btree_off'))
+
+
+# Rotates head by given angle
+class head_rotation(Node):
+ def start(self, run_time):
+ self.ros.topics['head_rotation'].publish(Float32(self.data['angle']))
+
+
+class soma(Node):
+ def start(self, run_time):
+ s = SomaState()
+ s.magnitude = 1
+ s.ease_in.secs = 0
+ s.ease_in.nsecs = 1000000 * 300
+ s.name = self.data['soma']
+ self.ros.topics['soma_state'].publish(s)
+
+ def stop(self, run_time):
+ s = SomaState()
+ s.magnitude = 0
+ s.ease_in.secs = 0
+ s.ease_in.nsecs = 0
+ s.name = self.data['soma']
+ self.ros.topics['soma_state'].publish(s)
+
+
+class expression(Node):
+ pass
+ # def __init__(self, data, ros, **kwargs):
+ # Node.__init__(self, data, ros)
+ # self.shown = False
+ #
+ # def start(self, run_time):
+ # try:
+ # self.ros.services['head_pau_mux']("/" + self.ros.robot_name + "/no_pau")
+ # logger.info("Call head_pau_mux topic {}".format("/" + self.ros.robot_name + "/no_pau"))
+ # except Exception as ex:
+ # logger.error(ex)
+ # self.shown = False
+ #
+ # def cont(self, run_time):
+ # # Publish expression message after some delay once node is started
+ # if (not self.shown) and (run_time > self.start_time + 0.05):
+ # self.shown = True
+ # self.ros.topics['expression'].publish(
+ # MakeFaceExpr(self.data['expression'], self._magnitude(self.data['magnitude'])))
+ # logger.info("Publish expression {}".format(self.data))
+ #
+ # def stop(self, run_time):
+ # try:
+ # self.ros.topics['expression'].publish(
+ # MakeFaceExpr('Neutral', self._magnitude(self.data['magnitude'])))
+ # time.sleep(min(1, self.duration))
+ # logger.info("Neutral expression")
+ # self.ros.services['head_pau_mux']("/blender_api/get_pau")
+ # logger.info("Call head_pau_mux topic {}".format("/blender_api/get_pau"))
+ # except Exception as ex:
+ # logger.error(ex)
+
+
+class kfanimation(Node):
+ pass
+ # def __init__(self, data, ros, **kwargs):
+ # Node.__init__(self, data, ros)
+ # self.shown = False
+ # self.blender_disable = 'off'
+ # if 'blender_mode' in self.data.keys():
+ # self.blender_disable = self.data['blender_mode']
+ #
+ # def start(self, run_time):
+ # self.shown = False
+ # try:
+ # if self.blender_disable in ['face', 'all']:
+ # self.ros.services['head_pau_mux']("/" + self.ros.robot_name + "/no_pau")
+ # if self.blender_disable == 'all':
+ # self.ros.services['neck_pau_mux']("/" + self.ros.robot_name + "/cmd_neck_pau")
+ # except Exception as ex:
+ # # Dont start animation to prevent the conflicts
+ # self.shown = True
+ # logger.error(ex)
+ #
+ # def cont(self, run_time):
+ # # Publish expression message after some delay once node is started
+ # if (not self.shown) and (run_time > self.start_time + 0.05):
+ # self.shown = True
+ # self.ros.topics['kfanimation'].publish(
+ # PlayAnimation(self.data['animation'], int(self.data['fps'])))
+ #
+ # def stop(self, run_time):
+ # try:
+ # if self.blender_disable in ['face', 'all']:
+ # self.ros.services['head_pau_mux']("/blender_api/get_pau")
+ # if self.blender_disable == 'all':
+ # self.ros.services['neck_pau_mux']("/blender_api/get_pau")
+ # except Exception as ex:
+ # logger.error(ex)
+
+
+class pause(Node):
+ def __init__(self, data, ros, runner, **kwargs):
+ Node.__init__(self, data, ros)
+ self.runner = runner
+ self.event_callback_ref = False
+ self.timer = False
+
+ if 'topic' not in self.data.keys():
+ self.data['topic'] = False
+ if 'on_event' not in self.data.keys():
+ self.data['on_event'] = False
+ if 'event_param' not in self.data.keys():
+ self.data['event_param'] = False
+
+ def start_performance(self):
+ if self.timer:
+ self.timer.cancel()
+
+ if 'break' in self.data and not self.data['break']:
+ self.runner.interrupt()
+ self.runner.append_to_queue(self.data['on_event'])
+ else:
+ self.runner.run_full_performance(self.data['on_event'])
+
+ # This function needs to be reused in wholeshow to make sure consistent matching
+ @staticmethod
+ def event_matched(param, msg):
+ params = str(param).lower().split(',')
+ matched = False
+ for p in params:
+ try:
+ str(msg or '').lower().index(p.strip())
+ matched = True
+ continue
+ except ValueError:
+ matched = matched or False
+ return matched
+
+ def event_callback(self, msg=None):
+ self.delete_callback_ref()
+
+ if self.data['event_param']:
+ # Check if any comma separated
+ if not self.event_matched(self.data['event_param'], msg):
+ return False
+
+ if self.data['on_event']:
+ self.start_performance()
+ else:
+ self.resume()
+
+ def resume(self):
+ if not self.finished:
+ self.runner.resume()
+ if self.timer:
+ self.timer.cancel()
+
+ def start(self, run_time):
+ self.runner.pause()
+
+ if 'topic' in self.data:
+ topic = str(self.data['topic'] or '').strip()
+ if topic != 'ROSPARAM':
+ self.event_callback_ref = self.ros.register(topic, self.event_callback)
+ # Paused SPEECH event should not be forwarded to chatbot if its enabled.
+ # The filtering is in wholeshow node
+ if self.data['event_param']:
+ # Currently only single PAUSE node can listen for keywords, so global param is fine.
+ rospy.set_param('/performances/keywords_listening', self.data['event_param'])
+ else:
+ if self.data['event_param']:
+ if rospy.get_param(self.data['event_param'], False):
+ # Resume current performance or play performance specified
+ self.timer = Timer(0.0, lambda: self.event_callback(self.data['event_param']))
+ self.timer.start()
+ return
+ try:
+ timeout = float(self.data['timeout'])
+ if timeout > 0.1:
+ self.timer = Timer(timeout, self.resume)
+ self.timer.start()
+ except (ValueError, KeyError) as e:
+ logger.error(e)
+
+ def delete_callback_ref(self):
+ if self.event_callback_ref:
+ self.ros.unregister(str(self.data['topic'] or '').strip(), self.event_callback_ref)
+ self.event_callback_ref = None
+
+ def stop(self, run_time):
+ self.delete_callback_ref()
+ if self.timer:
+ self.timer.cancel()
+
+ def end_time(self):
+ return self.start_time + 0.1
+
+
+class attention(Node):
+ # Find current region at runtime
+ def __init__(self, data, ros, **kwargs):
+ Node.__init__(self, data, ros)
+ self.topic = ['look_at', 'gaze_at']
+ self.times_shown = 0
+
+ @staticmethod
+ def get_random_axis_position(regions, axis):
+ """
+ :param regions: list of dictionaries
+ :param axis: string 'x' or 'y'
+ :return: position and matched regions
+ """
+
+ position = 0
+ matched = []
+
+ if regions:
+ regions = sorted(regions, key=lambda r: r[axis])
+ prev_end = regions[0][axis]
+ length = 0
+ lengths = []
+
+ for r in regions:
+ begin = r[axis]
+ end = begin + (r['width'] if axis == 'x' else r['height'])
+
+ if prev_end > begin:
+ diff = prev_end - begin
+ lengths.append([length - diff, length - diff + end - begin])
+ begin = prev_end
+ else:
+ lengths.append([length, length + end - begin])
+ length += max(0, end - begin)
+ prev_end = max(begin, end)
+
+ rval = random.random() * length
+
+ for i, length in enumerate(lengths):
+ if length[0] <= rval <= length[1]:
+ matched.append(regions[i])
+ if not position:
+ position = regions[i][axis] + (regions[i]['width'] if axis == 'x' else regions[i]['height']) * (
+ (rval - length[0]) / (length[1] - length[0]))
+ return position, matched
+
+ @staticmethod
+ # Gets x,y,z from given regions based on region type
+ def get_point_from_regions(all_regions, region_type):
+ regions = [{'x': r['x'], 'y': r['y'] - r['height'], 'width': r['width'], 'height': r['height']} for r in
+ all_regions
+ if r['type'] == region_type]
+ if regions:
+ y, matched = attention.get_random_axis_position(regions, 'x')
+ z, matched = attention.get_random_axis_position(matched, 'y')
+ # invert Y to match image in bg
+ return {
+ 'x': 1,
+ 'y': -y,
+ 'z': z,
+ }
+ else:
+ # Look forward
+ return {'x': 1, 'y': 0, 'z': 0}
+
+ # returns random coordinate from the region
+ def get_point(self, region):
+ # regions = rospy.get_param(
+ # '/' + os.path.join("/hr/control/performances", os.path.dirname(self.id),
+ # "properties/regions"), [])
+ # if not(len(regions)):
+ regions = rospy.get_param('/hr/control/regions', [])
+ return self.get_point_from_regions(regions, region)
+
+ def set_point(self, point):
+ speed = 1 if 'speed' not in self.data else self.data['speed']
+ for topic in self.topic:
+ self.ros.topics[topic].publish(Target(point['x'], point['y'], point['z'], speed))
+
+ def cont(self, run_time):
+ if 'attention_region' in self.data and self.data['attention_region'] != 'custom':
+ if 'interval' in self.data and run_time > self.times_shown * self.data['interval'] or not self.times_shown:
+ self.set_point(self.get_point(self.data['attention_region']))
+ self.times_shown += 1
+
+ if not self.times_shown:
+ self.set_point(self.data)
+ self.times_shown += 1
+
+
+class look_at(attention):
+ # Find current region at runtime
+ def __init__(self, data, ros, **kwargs):
+ attention.__init__(self, data, ros)
+ self.topic = ['look_at', 'gaze_at']
+
+
+class gaze_at(attention):
+ # Find current region at runtime
+ def __init__(self, data, ros, **kwargs):
+ attention.__init__(self, data, ros)
+ self.topic = ['gaze_at']
+
+class scene(Node):
+ def start(self, run_time):
+ self.ros.topics['arf'].publish(self.data['scene'])
+
+ def stop(self, run_time):
+ pass
+
+class settings(Node):
+
+ def setParameters(self, rosnode, params):
+ try:
+ cl = dynamic_reconfigure.client.Client(rosnode, timeout=0.1)
+ params = self.set_variables(params)
+ cl.update_configuration(params)
+ cl.close()
+ except:
+ pass
+
+ def set_variables(self, params):
+ for k, v in params.items():
+ if isinstance(v, string_types):
+ params[k] = self.replace_variables_text(v)
+ else:
+ params[k] = v
+ return params
+
+ def start(self, run_time):
+ if self.data['rosnode']:
+ self.setParameters(self.data['rosnode'], self.data['values'])
diff --git a/modules/performances/src/performances/speech_motion.py b/modules/performances/src/performances/speech_motion.py
new file mode 100644
index 0000000..e6f3b69
--- /dev/null
+++ b/modules/performances/src/performances/speech_motion.py
@@ -0,0 +1,595 @@
+# -*- coding: utf-8 -*-
+
+##
+## Copyright (C) 2017-2025 Hanson Robotics
+##
+## This program is free software: you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see .
+##
+
+import os
+import yaml
+import requests
+import random
+import re
+import logging
+from copy import deepcopy
+import random
+
+logger = logging.getLogger('hr')
+if not logger.handlers:
+ logging.basicConfig()
+
+class SpeechMotionUtils:
+ SENTENCE_DELIMITERS = '(? 0:
+ actions = [a for a in actions if a['duration'] > 0]
+ animation = deepcopy(random.choice(actions))
+ except:
+ return None
+ return animation
+
+
+class SpeechMotionController:
+
+ def __init__(self, animation_library, nlu_server='http://127.0.0.1:8210/da', lipsync_delay=0.1, arms_main=False, min_arms_hold_time = 3, max_arms_hold_time = 5, animated_shoulders = False):
+ self.lipsync_delay = lipsync_delay
+ self.nlu_cache = {}
+ self.nlu_timeout = 2
+ self.keyword_rules = []
+ self.da_rules = []
+ self.library = AnimationActionLibrary(animation_library)
+ self.probability_modifier = 0 # 0 unchanged, -1 none rules applied and 1 all rules applied
+ self.min_arms_hold_time = min_arms_hold_time
+ self.max_arms_hold_time = max_arms_hold_time
+ self.animated_arms = True
+ self.animated_shoulders = animated_shoulders
+ self.nlu_server = nlu_server
+ self.skip_keyword_rules = False
+ pass
+
+ def arms_interval(self):
+ return random.uniform(self.min_arms_hold_time, self.max_arms_hold_time)
+
+ # Currently timeline format with TTS nodes are supported. All other nodes will be left untouched.
+ def animate_motion(self, motion, animate_arms=True):
+ animated = []
+ arms = self.animated_arms
+ self.animated_arms = animate_arms
+ self.motion_length = 0
+ for n in motion:
+ if n['name'] == 'speech':
+ animated += self.animate_speech_node(n)
+ if n.get('start_time', 0) + n.get('duration', 0) > self.motion_length:
+ self.motion_length = n.get('start_time', 0) + n.get('duration', 0)
+ # Restore original setting
+ self.animated_arms = arms
+ return self.filter_animations(animated)
+
+ def apply_probability_modifier(self, probability):
+ return probability + probability * self.probability_modifier if self.probability_modifier < 0 \
+ else probability + (1 - probability) * self.probability_modifier
+
+ def neutral_arms(self, time):
+ return SpeechMotionUtils.create_node_data(name='arm_animation', duration=1, speed=1, magnitude=1, start_time=time, arm_animation='MAIN-2')
+
+ def filter_animations(self, animated):
+ # Needs to shuffle, so its not dependant on rule order
+ if len(animated) == 0:
+ return animated
+ random.shuffle(animated)
+ prioritized = sorted(animated, key=lambda k: 0 if 'sc_meta' not in k.keys() else k['sc_meta']['priority'],
+ reverse=True)
+ i = 0
+ arm_animations = []
+
+ # Remove conflicting gestures by priority on channel
+ while i < len(prioritized):
+ if 'sc_meta' in prioritized[i].keys():
+ try:
+ channels = prioritized[i]['sc_meta']['channel']
+ # Gesture begin time
+ block_time = [prioritized[i]['sc_meta']['begin'] + prioritized[i]['start_time'],
+ prioritized[i]['sc_meta']['end'] + prioritized[i]['start_time'],
+ ]
+ j = i + 1
+ while j < len(prioritized):
+ try:
+ if len(set(channels).intersection(prioritized[j]['sc_meta']['channel'])) > 0:
+ # Same channel, so check if any lower priority animations are interupting
+ if max(block_time[0], prioritized[j]['start_time']) <= \
+ min(block_time[1], prioritized[j]['start_time'] + prioritized[j]['duration']):
+ prioritized.pop(j)
+ continue
+ j += 1
+ except:
+ j += 1
+ except Exception as e:
+ logger.error("Filter animation error {}".format(e))
+ # capture all arm animation ending times, in case need to reset to neutral pose
+ if prioritized[i].get('name','') == 'arm_animation':
+ arm_animations.append(prioritized[i])
+ # update length of motion if it exceeds previous
+ if prioritized[i]['start_time'] + prioritized[i]['duration'] > self.motion_length:
+ self.motion_length = prioritized[i]['start_time'] + prioritized[i]['duration']
+ i += 1
+ # Need to reset to neutral after 5 seconds or so
+ if len(arm_animations) > 0:
+ #Sort by start time
+ arm_animations = sorted(arm_animations, key=lambda k: k['start_time'])
+ prev = {}
+ start_with_main = False
+ for a in arm_animations:
+ curr_name = a['arm_animation']
+ if not start_with_main:
+ # Check for first arm animation, and if its not MAIN it together it will neutralize the psoe
+ start_with_main = True
+ if not 'MAIN' in curr_name:
+ prioritized.append(self.neutral_arms(max(0,a['start_time']-0.5)))
+
+ prev_name = prev.get('arm_animation', '')
+ # Return to neutral pose for main animations only
+ if 'MAIN' in prev_name:
+ # If there is transition in MAIN, he neutral should be added only if othe MAIN starts after more than max hold time
+ if 'MAIN' in curr_name:
+ if prev['start_time'] + prev['duration'] + self.max_arms_hold_time < a['start_time']:
+ # Neutralize arms after interval
+ prioritized.append(self.neutral_arms(prev['start_time'] + prev['duration'] + self.arms_interval()))
+ # If next animation not a MAIN then always return to neutral, either at a time of the animation or (3-5secs later) or then the next animation starts
+ else:
+ prioritized.append(self.neutral_arms(min(prev['start_time'] + prev['duration'] + self.arms_interval(), a['start_time'])))
+ prev = a
+ if 'MAIN' in prev.get('arm_animation', ''):
+ prioritized.append(self.neutral_arms(min(prev['start_time'] + prev['duration'] + self.arms_interval(), self.motion_length)))
+
+ return prioritized
+
+ # returns update speech node merged with animations
+ def animate_speech_node(self, node):
+ try:
+ if not node.get('NLUData', False):
+ self.get_nlu_data(node)
+ except Exception as e:
+ logger.exception("NLU data exception {}".format(e))
+
+ added = []
+ if not self.skip_keyword_rules:
+ added += self.apply_keyword_rules(node)
+ added += self.apply_da_rules(node)
+
+ # # Add the speech for visualization
+ # added.append(Node.create_empty_node_data(type='speech', duration=node['duration'],
+ # text=node['text'], lang='NONE', start_time=node['start_time']))
+ return added
+
+ # tries get NLUData, Should execute on another thread with timeout
+ def get_nlu_data(self, node):
+ r = requests.get(self.nlu_server, {'language': 'en-US', 'text': node['text']})
+ node['nlu_data'] = yaml.safe_load(r.text)
+ node['nlu_data']['word_tokens'] = SpeechMotionUtils.match_NLU_to_text_words(node['text_words'],
+ node['nlu_data']['tokens'])
+ lemma = [(j, i['lemma'], i['pos']) for j,t in enumerate(node['nlu_data']['word_tokens']) for i in t]
+ node['nlu_data']['words_index'] = [x[0] for x in lemma]
+ node['nlu_data']['lemma_words'] = [x[1] for x in lemma]
+ node['nlu_data']['pos_words'] = [x[2] for x in lemma]
+
+ def update_keyword_rules(self, rules):
+ sorted_rules = sorted(rules, key=lambda k: k.get('priority',0))
+ for r in sorted_rules:
+ r['words'] = [SpeechMotionUtils.split_text(k.strip(), to_words=True) for k in r['keywords'].split(',') if
+ len(k.strip()) > 0]
+ r['index_words'] = [str(w.encode('ascii', 'ignore').decode()) for l in r['words'] for w in l]
+ self.keyword_rules = sorted_rules
+
+ def get_keyword_rules(self):
+ return self.keyword_rules
+
+ def apply_keyword_rules(self, node):
+ added = []
+ for rule in self.keyword_rules:
+ try:
+ added += self.process_keyword_rule(node, rule)
+ except Exception as e:
+ #logger.error("ERROR processing rule {} ith exception {}".format(rule, e))
+ pass
+ return added
+
+ @staticmethod
+ def subset(words, sentence):
+ matches = [sentence[i:i + len(words)] == words for i in range(0, len(sentence) - len(words) + 1)]
+ try:
+ return matches.index(True)+len(words)-1
+ except:
+ return False
+
+ @staticmethod
+ def negative_word_around(node, w):
+ # skip rules if they have negative words: no, not, can't don't
+ negatives = ['no', 'not', 'don\'t', 'can\'t']
+ if w > 0:
+ if node['text_words'][w - 1] in negatives:
+ return True
+ if w + 1 < len(node['words']):
+ if node['text_words'][w + 1] in negatives:
+ return True
+ return False
+
+
+ def process_keyword_rule(self, node, rule):
+
+ nodes = []
+ pos = rule.get('pos', False)
+ lemma = rule.get('lemma', False)
+ if pos and lemma:
+ # Fast check
+ matched = set(node['nlu_data']['lemma_words']).intersection(rule['index_words'])
+ if not matched:
+ return nodes
+ for word in matched:
+ try:
+ lw = node['nlu_data']['lemma_words'].index(word)
+ w = node['nlu_data']['words_index'][lw]
+ if self.negative_word_around(node, w):
+ continue
+ nodes += self.apply_rule(rule, node, node['words'][w])
+ except Exception as e:
+ logger.error("Error {} processing lemmas {} in {}".format(e, w, node['text_words']))
+
+ # Check if there any overlapping words, for fast checking
+ if not set(node['text_words']).intersection(rule['index_words']):
+ return nodes
+ for phrase in rule['words']:
+ w = self.subset(phrase, node['text_words'])
+ if w is not False:
+ if self.negative_word_around(node, w):
+ continue
+
+ if pos:
+ skip = True
+ # Need to match single POS for any of the phrase words
+ for i in range (w-len(phrase)+1, w+1):
+ for t in node['nlu_data']['word_tokens'][i]:
+ if t['pos'] == pos:
+ skip = False
+ if skip:
+ continue
+ try:
+ nodes += self.apply_rule(rule, node, node['words'][w])
+ except:
+ logger.info("Word {} {} failed.".format(w, node['words'][w]))
+ return nodes
+
+
+ def apply_rule(self, rule, node, word, ):
+ # Adds animations to rule KW and DA rule
+ nodes = []
+ if self.apply_probability_modifier(rule['probability']) > random.random():
+ # Add keyword gestures
+ animation = self.library.pick_animation(rule.get('browAA','-'))
+ if animation:
+ nodes.append(self.create_gesture(animation, node, word, rule))
+ animation = self.library.pick_animation(rule.get('headAA','-'))
+ if animation:
+ nodes.append(self.create_gesture(animation, node, word, rule))
+ animation = self.library.pick_animation(rule.get('eyesAA','-'))
+ if animation:
+ nodes.append(self.create_gesture(animation, node, word, rule))
+ animation = self.library.pick_animation(rule.get('eyelidsAA', 'none'))
+ if animation:
+ nodes.append(self.create_gesture(animation, node, word, rule))
+ if self.animated_arms:
+ animation = self.library.pick_animation(rule.get('armsAA','-'))
+ if animation:
+ nodes.append(self.create_gesture(animation, node, word, rule, head_gestrue=False))
+ if self.animated_shoulders:
+ animation = self.library.pick_animation(rule.get('shouldersAA','-'))
+ if animation:
+ nodes.append(self.create_gesture(animation, node, word, rule, head_gestrue=False))
+
+ # Special case for procedural animations:
+ # if rule['headAA'] == 'head-tilt':
+ # nodes += self.create_head_tilt(node, word, rule)
+ return nodes
+
+
+ def update_da_rules(self, rules):
+ sorted_rules = sorted(rules, key=lambda k: k['priority'])
+ self.da_rules = sorted_rules
+
+
+ def get_da_rules(self):
+ return self.da_rules
+
+
+ def apply_da_rules(self, node):
+ added = []
+
+ for rule in self.da_rules:
+ try:
+ added += self.process_da_rule(node, rule)
+ except Exception as e:
+ logger.error("ERROR with DA rule: {}, exception {}".format(rule, e))
+ return added
+
+
+ def process_da_rule(self, node, rule):
+ try:
+ for da in node['nlu_data']['dialog_act_ranking']:
+ if da['confidence'] > rule['threshold']:
+ if da['name'] == rule['act']:
+ word = random.choice(node['words'])
+ if rule['apply'] == 'first':
+ word = node['words'][0]
+ if rule['apply'] == 'last':
+ word = node['words'][-1]
+ if rule['apply'] == 'most important':
+ try:
+ m = w = -1
+ for i, k in enumerate(node['nlu_data']['tokens']):
+ if k['attention'] > m:
+ m = k['attention']
+ w = i
+ word = node['words'][w]
+ except:
+ pass
+ return self.apply_rule(rule, node, word)
+
+ else:
+ break
+ except:
+ pass
+ # nodes += self.apply_rule(rule, node, word)
+ return []
+
+ def create_gesture(self, animation, node, word, rule, head_gestrue=True):
+ start_time = node['start_time'] + word['end'] - animation['max']+self.lipsync_delay
+ start_time = max(0, start_time)
+ # speech_controller metadata
+ sc_meta = animation
+ sc_meta['priority'] = rule['priority']
+ if rule.get('act', False):
+ debug = 'Dialog ACT: {}'.format(rule.get('act'))
+ else:
+ debug = 'Keyword rule for word {} lemma {}'.format(word['name'], rule.get('lemma', False))
+ if head_gestrue:
+ gesture = SpeechMotionUtils.create_node_data(name='gesture', duration=animation['duration'], speed=round(random.uniform(animation.get('speed_min',1.0), animation.get('speed_max',1)),2),
+ magnitude=[animation.get('magnitude_min',1), animation.get('magnitude_max',1)],
+ start_time=start_time, gesture=animation['name'], sc_meta=sc_meta, debug=debug)
+ else:
+ gesture = SpeechMotionUtils.create_node_data(name='arm_animation', duration=animation['duration'], speed=round(random.uniform(animation.get('speed_min',1.0), animation.get('speed_max',1)),2),
+ magnitude=[animation.get('magnitude_min',1), animation.get('magnitude_max',1)],
+ start_time=start_time, arm_animation=animation['name'], sc_meta=sc_meta, debug=debug)
+ return gesture
+
+
+ def create_head_tilt(self, node, word, rule):
+ start_time = max(0, node['start_time'] + word['end'] - 0.2)
+ duration = round(0.5 + 1 * random.random(), 2)
+ rad = round((0.05 + 0.1 * random.random()) * random.choice([1, -1]), 2)
+ sc_meta = {
+ 'begin': 0.1,
+ 'max': 0.2,
+ 'end': duration + 0.05,
+ 'channel': ['head_tilt'],
+ 'priority': rule['priority']
+ }
+ nodes = []
+ nodes.append(SpeechMotionUtils.create_node_data(name='head_rotation', duration=duration, speed=2,
+ angle=rad, start_time=start_time, sc_meta=sc_meta))
+ # Reset
+ nodes.append(SpeechMotionUtils.create_node_data(name='head_rotation', duration=0.1, speed=2,
+ angle=0, start_time=start_time + duration))
+ return nodes
+
+ # Cleanup metadata, and ttsdata, not required
+
+
+ @staticmethod
+ def clean_nodes(nodes):
+ for n in nodes:
+ try:
+ del (n['sc_meta'])
+ except KeyError:
+ pass
+ try:
+ del (n['tts_data'])
+ except KeyError:
+ pass
+ try:
+ del (n['words'])
+ except KeyError:
+ pass
+ try:
+ del (n['nlu_data'])
+ except KeyError:
+ pass
+ try:
+ del (n['text_words'])
+ except KeyError:
+ pass
+
+class SpeechMotionAPI():
+ def __init__(self, cfg = None, nlu_server="http://127.0.0.1:8210/da"):
+ if cfg is None:
+ cfg=os.path.join(os.path.dirname(__file__), 'config.yaml')
+ with open(cfg, 'r') as stream:
+ try:
+ self.config = yaml.safe_load(stream)
+ except yaml.YAMLError as exc:
+ logger.error("error reading config. Exception: {}".format(exc))
+ return
+ self.controller = SpeechMotionController(self.config['animation_actions'], nlu_server=nlu_server)
+ self.controller.update_keyword_rules(yaml.safe_load(self.config['speech_motions']['keyword_rules']))
+ self.controller.update_da_rules(yaml.safe_load(self.config['speech_motions']['da_rules']))
+
+ def get_animations(self, ttsdata):
+ # no generation on interacting
+ nodes = SpeechMotionUtils.create_speech_data_from_tts_data(ttsdata)
+ animated = self.controller.animate_motion(nodes, animate_arms=True)
+ SpeechMotionController.clean_nodes(animated)
+ return animated
diff --git a/modules/performances/src/performances/weak_method.py b/modules/performances/src/performances/weak_method.py
new file mode 100644
index 0000000..efedc10
--- /dev/null
+++ b/modules/performances/src/performances/weak_method.py
@@ -0,0 +1,33 @@
+##
+## Copyright (C) 2017-2025 Hanson Robotics
+##
+## This program is free software: you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see .
+##
+
+import weakref
+
+
+class WeakMethod:
+
+ def __init__(self, f):
+ try:
+ self.f = f.im_func
+ except Exception:
+ self.f = f.__func__
+ self.c = weakref.ref(self.f)
+
+ def __call__(self, *arg):
+ if self.c() is None:
+ raise TypeError('Method called on dead object')
+ self.f((self.c(), ) + arg)
diff --git a/modules/performances/srv/Current.srv b/modules/performances/srv/Current.srv
new file mode 100644
index 0000000..69efeb6
--- /dev/null
+++ b/modules/performances/srv/Current.srv
@@ -0,0 +1,5 @@
+---
+string performance
+float32 current_time
+bool running
+bool paused
diff --git a/modules/performances/srv/Load.srv b/modules/performances/srv/Load.srv
new file mode 100644
index 0000000..c3c2e35
--- /dev/null
+++ b/modules/performances/srv/Load.srv
@@ -0,0 +1,4 @@
+string id
+---
+bool success
+string performance
diff --git a/modules/performances/srv/LoadPerformance.srv b/modules/performances/srv/LoadPerformance.srv
new file mode 100644
index 0000000..393469b
--- /dev/null
+++ b/modules/performances/srv/LoadPerformance.srv
@@ -0,0 +1,3 @@
+string performance
+---
+bool success
diff --git a/modules/performances/srv/Pause.srv b/modules/performances/srv/Pause.srv
new file mode 100644
index 0000000..9f6a297
--- /dev/null
+++ b/modules/performances/srv/Pause.srv
@@ -0,0 +1,3 @@
+---
+bool success
+float64 time
diff --git a/modules/performances/srv/Resume.srv b/modules/performances/srv/Resume.srv
new file mode 100644
index 0000000..9f6a297
--- /dev/null
+++ b/modules/performances/srv/Resume.srv
@@ -0,0 +1,3 @@
+---
+bool success
+float64 time
diff --git a/modules/performances/srv/Run.srv b/modules/performances/srv/Run.srv
new file mode 100644
index 0000000..96e421b
--- /dev/null
+++ b/modules/performances/srv/Run.srv
@@ -0,0 +1,3 @@
+float64 startTime
+---
+bool success
diff --git a/modules/performances/srv/RunByName.srv b/modules/performances/srv/RunByName.srv
new file mode 100644
index 0000000..04d5d1b
--- /dev/null
+++ b/modules/performances/srv/RunByName.srv
@@ -0,0 +1,3 @@
+string id
+---
+bool success
\ No newline at end of file
diff --git a/modules/performances/srv/SetProperties.srv b/modules/performances/srv/SetProperties.srv
new file mode 100644
index 0000000..dd072b9
--- /dev/null
+++ b/modules/performances/srv/SetProperties.srv
@@ -0,0 +1,4 @@
+string id
+string properties
+---
+bool success
diff --git a/modules/performances/srv/Stop.srv b/modules/performances/srv/Stop.srv
new file mode 100644
index 0000000..9f6a297
--- /dev/null
+++ b/modules/performances/srv/Stop.srv
@@ -0,0 +1,3 @@
+---
+bool success
+float64 time
diff --git a/modules/performances/test/test_speech.py b/modules/performances/test/test_speech.py
new file mode 100755
index 0000000..2d85b74
--- /dev/null
+++ b/modules/performances/test/test_speech.py
@@ -0,0 +1,84 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+##
+## Copyright (C) 2017-2025 Hanson Robotics
+##
+## This program is free software: you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see .
+##
+
+TEST_DATA = [{'duration': 3.605771,
+ 'end': 3.605771,
+ 'name': 'Hello there. I am Sophia. What is your name?',
+ 'start': 0.0,
+ 'type': 'text'},
+ {'end': 0.33539584279060364,
+ 'name': 'hello',
+ 'start': 0.030000001192092896,
+ 'type': 'word'},
+ {'end': 0.6391458511352539,
+ 'name': 'there',
+ 'start': 0.33539584279060364,
+ 'type': 'word'},
+ {'end': 1.1891666650772095,
+ 'name': 'i',
+ 'start': 1.0391458421945572,
+ 'type': 'word'},
+ {'end': 1.2893333435058594,
+ 'name': 'am',
+ 'start': 1.1891666650772095,
+ 'type': 'word'},
+ {'end': 1.8942708373069763,
+ 'name': 'sophia',
+ 'start': 1.2893333435058594,
+ 'type': 'word'},
+ {'end': 2.4396458864212036,
+ 'name': 'what',
+ 'start': 2.294270873069763,
+ 'type': 'word'},
+ {'end': 2.5999792218208313,
+ 'name': 'is',
+ 'start': 2.4396458864212036,
+ 'type': 'word'},
+ {'end': 2.777625024318695,
+ 'name': 'your',
+ 'start': 2.5999792218208313,
+ 'type': 'word'},
+ {'end': 3.205770790576935,
+ 'name': 'name',
+ 'start': 2.777625024318695,
+ 'type': 'word'}]
+
+
+
+from performances.speech_motion import SpeechMotionAPI
+import yaml
+import os
+if __name__ == '__main__':
+ if os.path.dirname(__file__):
+ fn = os.path.dirname(__file__)+'/test.yaml'
+ else:
+ fn = 'test.yaml'
+ with open(fn, 'r') as f:
+ data = yaml.safe_load(f)
+ data = yaml.safe_load(data['data'])
+ api =SpeechMotionAPI(nlu_server='http://127.0.0.1:8102/v1.0/nlu/da')
+ import time
+ print('start')
+ t = time.time()
+ animations = api.get_animations(data)
+ print(time.time() -t)
+ #print(animations)
+
+
diff --git a/modules/performances/version b/modules/performances/version
new file mode 100644
index 0000000..0b9c019
--- /dev/null
+++ b/modules/performances/version
@@ -0,0 +1 @@
+0.3.12