humotion / examples / yarp_icub / src / icub_faceinterface.cpp @ ade7a207
History | View | Annotate | Download (8.58 KB)
1 |
/*
|
---|---|
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 |
#include <boost/format.hpp> |
31 |
#include <boost/lexical_cast.hpp> |
32 |
|
33 |
#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 |
bool init_ok = true; |
51 |
for (int i = 0; i < 4; i++) { |
52 |
// strange, if we use one output port only the first command is executed?! flushing issues?
|
53 |
std::string emotion_port_out = "/emotionwriter" + boost::lexical_cast<std::string>(i); |
54 |
if (!emotion_port[i].open(emotion_port_out.c_str())) {
|
55 |
cerr << "ERROR: failed to open emotion port '" << emotion_port_out << "'\n"; |
56 |
init_ok = false;
|
57 |
} |
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 |
init_ok = false;
|
61 |
} |
62 |
} |
63 |
|
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 |
} |
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 |
cmd_s = "L0" + boost::lexical_cast<std::string>(icub_val); |
151 |
} else {
|
152 |
cmd_s = "R0" + boost::lexical_cast<std::string>(icub_val); |
153 |
} |
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 |
} |