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