humotion / examples / yarp_icub / src / icub_faceinterface.cpp @ ade7a207
History | View | Annotate | Download (8.58 KB)
1 | 6a2d467f | Simon Schulz | /*
|
---|---|---|---|
2 | * This file is part of humotion
|
||
3 | *
|
||
4 | * Copyright(c) sschulz <AT> techfak.uni-bielefeld.de
|
||
5 | * http://opensource.cit-ec.de/projects/humotion
|
||
6 | *
|
||
7 | * This file may be licensed under the terms of the
|
||
8 | * GNU Lesser General Public License Version 3 (the ``LGPL''),
|
||
9 | * or (at your option) any later version.
|
||
10 | *
|
||
11 | * Software distributed under the License is distributed
|
||
12 | * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
|
||
13 | * express or implied. See the LGPL for the specific language
|
||
14 | * governing rights and limitations.
|
||
15 | *
|
||
16 | * You should have received a copy of the LGPL along with this
|
||
17 | * program. If not, go to http://www.gnu.org/licenses/lgpl.html
|
||
18 | * or write to the Free Software Foundation, Inc.,
|
||
19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||
20 | *
|
||
21 | * The development of this software was supported by the
|
||
22 | * Excellence Cluster EXC 277 Cognitive Interaction Technology.
|
||
23 | * The Excellence Cluster EXC 277 is a grant of the Deutsche
|
||
24 | * Forschungsgemeinschaft (DFG) in the context of the German
|
||
25 | * Excellence Initiative.
|
||
26 | */
|
||
27 | |||
28 | #include "humotion_yarp_icub/icub_faceinterface.h" |
||
29 | |||
30 | a5cc61d4 | sschulz | #include <boost/format.hpp> |
31 | 07e68eb7 | sschulz | #include <boost/lexical_cast.hpp> |
32 | a5cc61d4 | sschulz | |
33 | 6a2d467f | Simon Schulz | #include <algorithm> |
34 | #include <string> |
||
35 | |||
36 | using yarp::os::Network;
|
||
37 | using yarp::os::Bottle;
|
||
38 | using std::cout;
|
||
39 | using std::cerr;
|
||
40 | using std::string; |
||
41 | |||
42 | //! constructor
|
||
43 | iCubFaceInterface::iCubFaceInterface(std::string _scope) {
|
||
44 | scope = _scope; |
||
45 | |||
46 | // attach to facial expressions:
|
||
47 | std::string emotion_scope = scope + "/face/raw/in"; |
||
48 | cout << "opening connection to '"<< emotion_scope << "'\n"; |
||
49 | |||
50 | 07e68eb7 | sschulz | bool init_ok = true; |
51 | 6a2d467f | Simon Schulz | for (int i = 0; i < 4; i++) { |
52 | // strange, if we use one output port only the first command is executed?! flushing issues?
|
||
53 | 07e68eb7 | sschulz | std::string emotion_port_out = "/emotionwriter" + boost::lexical_cast<std::string>(i); |
54 | 6a2d467f | Simon Schulz | if (!emotion_port[i].open(emotion_port_out.c_str())) {
|
55 | cerr << "ERROR: failed to open emotion port '" << emotion_port_out << "'\n"; |
||
56 | 07e68eb7 | sschulz | init_ok = false;
|
57 | 6a2d467f | Simon Schulz | } |
58 | if (!Network::connect(emotion_port_out.c_str(), emotion_scope.c_str())) {
|
||
59 | cerr << "ERROR: failed to connect to emotion port '" << emotion_port_out << "'\n"; |
||
60 | 07e68eb7 | sschulz | init_ok = false;
|
61 | 6a2d467f | Simon Schulz | } |
62 | } |
||
63 | 07e68eb7 | sschulz | |
64 | if (!init_ok) {
|
||
65 | cerr << "ERROR: failed to set up emotion component\n";
|
||
66 | cerr << " please make sure that the faceExpressions yarpdev is started\n";
|
||
67 | cerr << " (e.g. yarpdev --name /icub/face/raw --device serial ... is running)\n";
|
||
68 | exit(EXIT_FAILURE); |
||
69 | } |
||
70 | 6a2d467f | Simon Schulz | } |
71 | |||
72 | //! destructor
|
||
73 | iCubFaceInterface::~iCubFaceInterface() { |
||
74 | } |
||
75 | |||
76 | //! special command to set eyelid angle
|
||
77 | //! \param angle in degrees
|
||
78 | void iCubFaceInterface::set_eyelid_angle(float angle) { |
||
79 | if (emotion_port[0].getOutputCount() > 0) { |
||
80 | // try to set the value based on the upper one
|
||
81 | // some guesses from the sim: S30 = 0° / S40 = 10°
|
||
82 | int opening = (25.0 + 0.8*angle); |
||
83 | opening = std::min(48, std::max(24, opening)); |
||
84 | |||
85 | if (opening == lid_opening_previous) {
|
||
86 | // no update necessary
|
||
87 | return;
|
||
88 | } |
||
89 | |||
90 | lid_angle = angle; |
||
91 | lid_opening_previous = opening; |
||
92 | |||
93 | char buf[20]; |
||
94 | snprintf(buf, sizeof(buf), "S%2d", opening); |
||
95 | |||
96 | // cout << "SETTING EYELID '" << buf << "' (" << angle << " -> " << opening << "\n";
|
||
97 | Bottle &cmd = emotion_port[0].prepare();
|
||
98 | cmd.clear(); |
||
99 | cmd.addString(buf); |
||
100 | emotion_port[0].writeStrict();
|
||
101 | } else {
|
||
102 | cerr << "ERROR: no icub emotion output\n";
|
||
103 | exit(EXIT_FAILURE); |
||
104 | } |
||
105 | } |
||
106 | |||
107 | //! special command to set the eyebrow angle
|
||
108 | //! \param id {0=left, 1=right)
|
||
109 | //! \param angle in degrees
|
||
110 | void iCubFaceInterface::set_eyebrow_angle(int id, float *target_angle) { |
||
111 | int port_id;
|
||
112 | if (id == iCubJointInterface::ICUB_ID_EYES_LEFT_BROW) {
|
||
113 | port_id = 1;
|
||
114 | } else {
|
||
115 | port_id = 2;
|
||
116 | } |
||
117 | |||
118 | if (emotion_port[port_id].getOutputCount() > 0) { |
||
119 | double angle = target_angle[id];
|
||
120 | int icub_val = 0; |
||
121 | |||
122 | // swap rotation direction for eyebrow
|
||
123 | if (id == iCubJointInterface::ICUB_ID_EYES_LEFT_BROW) {
|
||
124 | angle = -angle; |
||
125 | } |
||
126 | |||
127 | // convert to icub representation
|
||
128 | if (angle < -20) { |
||
129 | icub_val = 1;
|
||
130 | } else if (angle < 10) { |
||
131 | icub_val = 2;
|
||
132 | } else if (angle < 20) { |
||
133 | icub_val = 4;
|
||
134 | } else {
|
||
135 | icub_val = 8;
|
||
136 | } |
||
137 | |||
138 | // make sure to update only on new values
|
||
139 | if (icub_val == target_angle_previous[id]) {
|
||
140 | // no updata necessary
|
||
141 | return;
|
||
142 | } |
||
143 | |||
144 | // store actual value
|
||
145 | target_angle_previous[id] = icub_val; |
||
146 | |||
147 | |||
148 | std::string cmd_s;
|
||
149 | if (id == iCubJointInterface::ICUB_ID_EYES_LEFT_BROW) {
|
||
150 | 07e68eb7 | sschulz | cmd_s = "L0" + boost::lexical_cast<std::string>(icub_val); |
151 | 6a2d467f | Simon Schulz | } else {
|
152 | 07e68eb7 | sschulz | cmd_s = "R0" + boost::lexical_cast<std::string>(icub_val); |
153 | 6a2d467f | Simon Schulz | } |
154 | |||
155 | cout << "SETTING EYEBROW " << id << " (" << angle << " -> " << cmd_s << ")\n"; |
||
156 | |||
157 | Bottle &cmd = emotion_port[port_id].prepare(); |
||
158 | cmd.clear(); |
||
159 | cmd.addString(cmd_s); |
||
160 | emotion_port[port_id].writeStrict(); |
||
161 | } else {
|
||
162 | cerr << "ERROR: no icub emotion output\n";
|
||
163 | exit(EXIT_FAILURE); |
||
164 | } |
||
165 | } |
||
166 | |||
167 | void iCubFaceInterface::set_mouth(float *target_angle) { |
||
168 | // convert from 6DOF mouth displacement to icub leds:
|
||
169 | int led_value = 0; |
||
170 | |||
171 | // fetch center opening
|
||
172 | double center_opening = target_angle[iCubJointInterface::ICUB_ID_LIP_CENTER_LOWER] -
|
||
173 | target_angle[iCubJointInterface::ICUB_ID_LIP_CENTER_UPPER]; |
||
174 | bool mouth_open = (center_opening > 15.0) ? true : false; |
||
175 | |||
176 | // side of mouth high or low?
|
||
177 | double center_avg = (target_angle[iCubJointInterface::ICUB_ID_LIP_CENTER_LOWER] +
|
||
178 | target_angle[iCubJointInterface::ICUB_ID_LIP_CENTER_UPPER])/2.0; |
||
179 | double left_avg = (target_angle[iCubJointInterface::ICUB_ID_LIP_LEFT_LOWER] +
|
||
180 | target_angle[iCubJointInterface::ICUB_ID_LIP_LEFT_UPPER])/2.0; |
||
181 | double right_avg = (target_angle[iCubJointInterface::ICUB_ID_LIP_RIGHT_LOWER] +
|
||
182 | target_angle[iCubJointInterface::ICUB_ID_LIP_RIGHT_UPPER])/2.0; |
||
183 | |||
184 | // happy, neutral or sad?
|
||
185 | double diff_l = center_avg - left_avg;
|
||
186 | double diff_r = center_avg - right_avg;
|
||
187 | double diff = (diff_l+diff_r)/2.0; |
||
188 | |||
189 | if (diff > 2.0) { |
||
190 | if (mouth_open) {
|
||
191 | led_value = 0x14;
|
||
192 | } else {
|
||
193 | if (diff > 2.6) { |
||
194 | led_value = 0x0A;
|
||
195 | } else {
|
||
196 | led_value = 0x0B;
|
||
197 | } |
||
198 | } |
||
199 | } else if (diff < -3.0) { |
||
200 | if (mouth_open) {
|
||
201 | led_value = 0x06;
|
||
202 | } else {
|
||
203 | led_value = 0x18;
|
||
204 | } |
||
205 | } else if (diff < -2.0) { |
||
206 | if (mouth_open) {
|
||
207 | led_value = 0x04; // 0x25; |
||
208 | } else {
|
||
209 | led_value = 0x08;
|
||
210 | } |
||
211 | } else {
|
||
212 | if (mouth_open) {
|
||
213 | led_value = 0x16;
|
||
214 | } else {
|
||
215 | led_value = 0x08;
|
||
216 | } |
||
217 | } |
||
218 | |||
219 | |||
220 | if (led_value == previous_mouth_state) {
|
||
221 | // no update necessary
|
||
222 | return;
|
||
223 | } |
||
224 | |||
225 | previous_mouth_state = led_value; |
||
226 | |||
227 | // convert to string
|
||
228 | char buf[10]; |
||
229 | snprintf(buf, sizeof(buf), "M%02X", led_value); |
||
230 | |||
231 | /*
|
||
232 | cout << "sending mouth " << buf << "\n";
|
||
233 | cout << boost::format(" mouth angles: %3.2f %3.2f %3.2f\n")
|
||
234 | % target_angle[ICUB_ID_LIP_LEFT_UPPER]
|
||
235 | % target_angle[ICUB_ID_LIP_CENTER_UPPER]
|
||
236 | % target_angle[ICUB_ID_LIP_RIGHT_UPPER];
|
||
237 | cout << boost::format(" mouth %3.2f %3.2f %3.2f\n")
|
||
238 | % target_angle[ICUB_ID_LIP_LEFT_LOWER]
|
||
239 | % target_angle[ICUB_ID_LIP_CENTER_LOWER]
|
||
240 | % target_angle[ICUB_ID_LIP_RIGHT_LOWER];
|
||
241 | cout << boost::format(" mouth open=%3.2f diff=%3.2f\n")
|
||
242 | % center_opening
|
||
243 | % diff;
|
||
244 | */
|
||
245 | |||
246 | // add mouth
|
||
247 | Bottle &cmd = emotion_port[3].prepare();
|
||
248 | cmd.clear(); |
||
249 | cmd.addString(buf); |
||
250 | emotion_port[3].writeStrict();
|
||
251 | |||
252 | /*
|
||
253 | //store joint values which we do not handle on icub here:
|
||
254 | double timestamp = get_timestamp_ms();
|
||
255 | JointInterface::store_incoming_position(ID_LIP_LEFT_UPPER, target_angle[ICUB_ID_LIP_LEFT_UPPER], timestamp);
|
||
256 | JointInterface::store_incoming_position(ID_LIP_LEFT_LOWER, target_angle[ICUB_ID_LIP_LEFT_LOWER], timestamp);
|
||
257 | JointInterface::store_incoming_position(ID_LIP_CENTER_UPPER, target_angle[ICUB_ID_LIP_CENTER_UPPER], timestamp);
|
||
258 | JointInterface::store_incoming_position(ID_LIP_CENTER_LOWER, target_angle[ICUB_ID_LIP_CENTER_LOWER], timestamp);
|
||
259 | JointInterface::store_incoming_position(ID_LIP_RIGHT_UPPER, target_angle[ICUB_ID_LIP_RIGHT_UPPER], timestamp);
|
||
260 | JointInterface::store_incoming_position(ID_LIP_RIGHT_LOWER, target_angle[ICUB_ID_LIP_RIGHT_LOWER], timestamp);
|
||
261 | */
|
||
262 | } |