humotion / examples / yarp_icub / src / icub_faceinterface.cpp @ 58c9f56c
History | View | Annotate | Download (9.824 KB)
1 | 5d29c83f | Simon Schulz | #include "icub_faceinterface.h" |
---|---|---|---|
2 | using namespace std; |
||
3 | |||
4 | using yarp::os::Network;
|
||
5 | using yarp::os::Bottle;
|
||
6 | |||
7 | //! constructor
|
||
8 | iCubFaceInterface::iCubFaceInterface(string _scope) {
|
||
9 | scope = _scope; |
||
10 | |||
11 | /*
|
||
12 | //add mapping from ids to enums:
|
||
13 | //this might look strange at the first sight but we need to have a generic
|
||
14 | //way to acces joints from libhumotion. therefore the lib uses its enum with ID_* enum ids
|
||
15 | //to access the joints. now we need to define a mapping to map those to our motor ids.
|
||
16 | //this is what we use the enum bimap for (convertion fro/to motorid is handled
|
||
17 | //by \sa convert_enum_to_motorid() and \sa convert_motorid_to_enum() lateron
|
||
18 | |||
19 | //MOUTH
|
||
20 | enum_id_bimap.insert(enum_id_bimap_entry_t(ICUB_ID_LIP_LEFT_UPPER, ID_LIP_LEFT_UPPER));
|
||
21 | enum_id_bimap.insert(enum_id_bimap_entry_t(ICUB_ID_LIP_LEFT_LOWER, ID_LIP_LEFT_LOWER));
|
||
22 | enum_id_bimap.insert(enum_id_bimap_entry_t(ICUB_ID_LIP_CENTER_UPPER, ID_LIP_CENTER_UPPER));
|
||
23 | enum_id_bimap.insert(enum_id_bimap_entry_t(ICUB_ID_LIP_CENTER_LOWER, ID_LIP_CENTER_LOWER));
|
||
24 | enum_id_bimap.insert(enum_id_bimap_entry_t(ICUB_ID_LIP_RIGHT_UPPER, ID_LIP_RIGHT_UPPER));
|
||
25 | enum_id_bimap.insert(enum_id_bimap_entry_t(ICUB_ID_LIP_RIGHT_LOWER, ID_LIP_RIGHT_LOWER));
|
||
26 | |||
27 | //NECK
|
||
28 | enum_id_bimap.insert(enum_id_bimap_entry_t(ICUB_ID_NECK_PAN, ID_NECK_PAN));
|
||
29 | enum_id_bimap.insert(enum_id_bimap_entry_t(ICUB_ID_NECK_TILT, ID_NECK_TILT));
|
||
30 | enum_id_bimap.insert(enum_id_bimap_entry_t(ICUB_ID_NECK_ROLL, ID_NECK_ROLL));
|
||
31 | |||
32 | //EYES
|
||
33 | enum_id_bimap.insert(enum_id_bimap_entry_t(ICUB_ID_EYES_PAN, ID_EYES_LEFT_LR));
|
||
34 | enum_id_bimap.insert(enum_id_bimap_entry_t(ICUB_ID_EYES_VERGENCE, ID_EYES_RIGHT_LR));
|
||
35 | enum_id_bimap.insert(enum_id_bimap_entry_t(ICUB_ID_EYES_BOTH_UD, ID_EYES_BOTH_UD));
|
||
36 | |||
37 | //EYELIDS
|
||
38 | enum_id_bimap.insert(enum_id_bimap_entry_t(ICUB_ID_EYES_LEFT_LID_LOWER, ID_EYES_LEFT_LID_LOWER));
|
||
39 | enum_id_bimap.insert(enum_id_bimap_entry_t(ICUB_ID_EYES_LEFT_LID_UPPER, ID_EYES_LEFT_LID_UPPER));
|
||
40 | enum_id_bimap.insert(enum_id_bimap_entry_t(ICUB_ID_EYES_LEFT_BROW, ID_EYES_LEFT_BROW));
|
||
41 | |||
42 | enum_id_bimap.insert(enum_id_bimap_entry_t(ICUB_ID_EYES_RIGHT_LID_LOWER, ID_EYES_RIGHT_LID_LOWER));
|
||
43 | enum_id_bimap.insert(enum_id_bimap_entry_t(ICUB_ID_EYES_RIGHT_LID_UPPER,ID_EYES_RIGHT_LID_UPPER));
|
||
44 | enum_id_bimap.insert(enum_id_bimap_entry_t(ICUB_ID_EYES_RIGHT_BROW, ID_EYES_RIGHT_BROW));
|
||
45 | |||
46 | Property options;
|
||
47 | options.put("device", "remote_controlboard");
|
||
48 | options.put("local", "/local/head");
|
||
49 | options.put("remote", scope+"/head");
|
||
50 | dd.open(options);
|
||
51 | |||
52 | //fetch views:
|
||
53 | dd.view(iencs);
|
||
54 | dd.view(ipos);
|
||
55 | dd.view(ivel);
|
||
56 | dd.view(ilimits);
|
||
57 | dd.view(pid);
|
||
58 | dd.view(amp);
|
||
59 | |||
60 | |||
61 | if ( (!iencs) || (!ipos) || (!ilimits) || (!ivel) || (!amp) || (!pid)){
|
||
62 | printf("> ERROR: failed to open icub views\n");
|
||
63 | exit(EXIT_FAILURE);
|
||
64 | }
|
||
65 | |||
66 | int joints;
|
||
67 | |||
68 | //tell humotion about min/max joint values:
|
||
69 | init_joints();
|
||
70 | |||
71 | iencs->getAxes(&joints);
|
||
72 | positions.resize(joints);
|
||
73 | velocities.resize(joints);
|
||
74 | commands.resize(joints);
|
||
75 | |||
76 | //set position mode:
|
||
77 | if (POSITION_CONTROL){
|
||
78 | commands=200000.0;
|
||
79 | ipos->setRefAccelerations(commands.data());
|
||
80 | ipos->setPositionMode();
|
||
81 | }else{
|
||
82 | ivel->setVelocityMode();
|
||
83 | commands=100.0;
|
||
84 | ivel->setRefAccelerations(commands.data());
|
||
85 | }*/
|
||
86 | |||
87 | //attach to facial expressions:
|
||
88 | string emotion_scope = scope + "/face/raw/in"; |
||
89 | printf("> opening connection to %s\n", emotion_scope.c_str());
|
||
90 | |||
91 | for(int i=0; i<4; i++){ |
||
92 | //strange, if we use one output port only the first command is executed?! flushing issues?
|
||
93 | fc7a174f | Simon Schulz | string emotion_port_out = "/emotionwriter" + to_string((unsigned long long)i); |
94 | 5d29c83f | Simon Schulz | if (!emotion_port[i].open(emotion_port_out.c_str())){
|
95 | printf("> ERROR: failed to open to %s\n",emotion_port_out.c_str());
|
||
96 | exit(EXIT_FAILURE); |
||
97 | } |
||
98 | if (!Network::connect(emotion_port_out.c_str(), emotion_scope.c_str())){
|
||
99 | printf("> ERROR: failed to connect emotion ports\n");
|
||
100 | exit(EXIT_FAILURE); |
||
101 | } |
||
102 | } |
||
103 | } |
||
104 | |||
105 | //! destructor
|
||
106 | iCubFaceInterface::~iCubFaceInterface(){ |
||
107 | } |
||
108 | |||
109 | //! special command to set eyelid angle
|
||
110 | //! \param angle in degrees
|
||
111 | void iCubFaceInterface::set_eyelid_angle(double angle){ |
||
112 | if (emotion_port[0].getOutputCount()>0){ |
||
113 | //try to set the value based on the upper one
|
||
114 | //some guesses from the sim: S30 = 0° / S40 = 10°
|
||
115 | int opening = (25.0 + 0.8*angle); |
||
116 | opening = min(48, max(24, opening)); |
||
117 | |||
118 | if (opening == lid_opening_previous){
|
||
119 | //no update necessary
|
||
120 | return;
|
||
121 | } |
||
122 | |||
123 | lid_angle = angle; |
||
124 | lid_opening_previous = opening; |
||
125 | |||
126 | char buf[20]; |
||
127 | sprintf(buf, "S%2d", opening);
|
||
128 | |||
129 | //printf("> SETTING EYELID '%s' (%f -> %d)\n",buf,angle,opening);
|
||
130 | Bottle &cmd = emotion_port[0].prepare();
|
||
131 | cmd.clear(); |
||
132 | cmd.addString(buf); |
||
133 | emotion_port[0].writeStrict();
|
||
134 | }else{
|
||
135 | printf("> ERROR: no icub emotion output\n");
|
||
136 | exit(EXIT_FAILURE); |
||
137 | } |
||
138 | } |
||
139 | |||
140 | //! special command to set the eyebrow angle
|
||
141 | //! \param id {0=left, 1=right)
|
||
142 | //! \param angle in degrees
|
||
143 | void iCubFaceInterface::set_eyebrow_angle(int id, double *target_angle){ |
||
144 | int port_id;
|
||
145 | if (id == iCubJointInterface::ICUB_ID_EYES_LEFT_BROW){
|
||
146 | port_id = 1;
|
||
147 | }else{
|
||
148 | port_id = 2;
|
||
149 | } |
||
150 | |||
151 | if (emotion_port[port_id].getOutputCount()>0){ |
||
152 | double angle = target_angle[id];
|
||
153 | fc7a174f | Simon Schulz | int icub_val = 0; |
154 | 5d29c83f | Simon Schulz | |
155 | //swap rotation direction:
|
||
156 | if (id==iCubJointInterface::ICUB_ID_EYES_LEFT_BROW) angle = -angle;
|
||
157 | |||
158 | //convert to icub representation
|
||
159 | if (angle < -20){ |
||
160 | icub_val = 1;
|
||
161 | }else if (angle<10){ |
||
162 | icub_val = 2;
|
||
163 | }else if (angle<20){ |
||
164 | icub_val = 4;
|
||
165 | }else{
|
||
166 | icub_val = 8;
|
||
167 | } |
||
168 | |||
169 | //make sure to update only on new values:
|
||
170 | if (icub_val == target_angle_previous[id]){
|
||
171 | //no updata necessary
|
||
172 | return;
|
||
173 | } |
||
174 | |||
175 | //store actual value:
|
||
176 | target_angle_previous[id] = icub_val; |
||
177 | |||
178 | |||
179 | string cmd_s;
|
||
180 | if (id==iCubJointInterface::ICUB_ID_EYES_LEFT_BROW){
|
||
181 | fc7a174f | Simon Schulz | cmd_s = "L0" + to_string((unsigned long long)icub_val); |
182 | 5d29c83f | Simon Schulz | }else{
|
183 | fc7a174f | Simon Schulz | cmd_s = "R0" + to_string((unsigned long long)icub_val); |
184 | 5d29c83f | Simon Schulz | } |
185 | |||
186 | printf("> SETTING EYEBROW %d (%f -> %s)\n",id,angle,cmd_s.c_str());
|
||
187 | |||
188 | Bottle &cmd = emotion_port[port_id].prepare(); |
||
189 | cmd.clear(); |
||
190 | cmd.addString(cmd_s); |
||
191 | emotion_port[port_id].writeStrict(); |
||
192 | }else{
|
||
193 | printf("> ERROR: no icub emotion output\n");
|
||
194 | exit(EXIT_FAILURE); |
||
195 | } |
||
196 | } |
||
197 | |||
198 | void iCubFaceInterface::set_mouth(double *target_angle){ |
||
199 | //convert from 6DOF mouth displacement to icub leds:
|
||
200 | int led_value = 0; |
||
201 | |||
202 | //fetch center opening:
|
||
203 | double center_opening = target_angle[iCubJointInterface::ICUB_ID_LIP_CENTER_LOWER] -
|
||
204 | target_angle[iCubJointInterface::ICUB_ID_LIP_CENTER_UPPER]; |
||
205 | bool mouth_open = (center_opening>15.0)?true:false; |
||
206 | |||
207 | //side of mouth high or low?
|
||
208 | double center_avg = (target_angle[iCubJointInterface::ICUB_ID_LIP_CENTER_LOWER] +
|
||
209 | target_angle[iCubJointInterface::ICUB_ID_LIP_CENTER_UPPER])/2.0; |
||
210 | double left_avg = (target_angle[iCubJointInterface::ICUB_ID_LIP_LEFT_LOWER] +
|
||
211 | target_angle[iCubJointInterface::ICUB_ID_LIP_LEFT_UPPER])/2.0; |
||
212 | double right_avg = (target_angle[iCubJointInterface::ICUB_ID_LIP_RIGHT_LOWER] +
|
||
213 | target_angle[iCubJointInterface::ICUB_ID_LIP_RIGHT_UPPER])/2.0; |
||
214 | |||
215 | //happy, neutral or sad?
|
||
216 | double diff_l = center_avg - left_avg;
|
||
217 | double diff_r = center_avg - right_avg;
|
||
218 | double diff = (diff_l+diff_r)/2.0; |
||
219 | |||
220 | if (diff > 2.0){ |
||
221 | if (mouth_open){
|
||
222 | led_value = 0x14;
|
||
223 | }else{
|
||
224 | if (diff > 2.6){ |
||
225 | led_value = 0x0A;
|
||
226 | }else{
|
||
227 | led_value = 0x0B;
|
||
228 | } |
||
229 | } |
||
230 | }else if (diff < -3.0){ |
||
231 | if (mouth_open){
|
||
232 | led_value = 0x06;
|
||
233 | }else{
|
||
234 | led_value = 0x18;
|
||
235 | } |
||
236 | }else if (diff < -2.0){ |
||
237 | if (mouth_open){
|
||
238 | led_value = 0x04; //0x25; |
||
239 | }else{
|
||
240 | led_value = 0x08;
|
||
241 | } |
||
242 | }else{
|
||
243 | if (mouth_open){
|
||
244 | led_value = 0x16;
|
||
245 | }else{
|
||
246 | led_value = 0x08;
|
||
247 | } |
||
248 | } |
||
249 | |||
250 | |||
251 | if (led_value == previous_mouth_state){
|
||
252 | //no update necessary
|
||
253 | return;
|
||
254 | } |
||
255 | |||
256 | previous_mouth_state = led_value; |
||
257 | |||
258 | //convert to string:
|
||
259 | char buf[10]; |
||
260 | sprintf(buf, "M%02X",led_value);
|
||
261 | |||
262 | /*printf("> sending mouth '%s'\n",buf);
|
||
263 | printf("> mouth angles: %3.2f %3.2f %3.2f\n",target_angle[ICUB_ID_LIP_LEFT_UPPER],target_angle[ICUB_ID_LIP_CENTER_UPPER],target_angle[ICUB_ID_LIP_RIGHT_UPPER]);
|
||
264 | printf(" mouth %3.2f %3.2f %3.2f\n",target_angle[ICUB_ID_LIP_LEFT_LOWER],target_angle[ICUB_ID_LIP_CENTER_LOWER],target_angle[ICUB_ID_LIP_RIGHT_LOWER]);
|
||
265 | printf(" mouth open=%3.2f diff=%3.2f\n", center_opening, diff);*/
|
||
266 | |||
267 | //add mouth:
|
||
268 | Bottle &cmd = emotion_port[3].prepare();
|
||
269 | cmd.clear(); |
||
270 | cmd.addString(buf); |
||
271 | emotion_port[3].writeStrict();
|
||
272 | |||
273 | /*
|
||
274 | //store joint values which we do not handle on icub here:
|
||
275 | double timestamp = get_timestamp_ms();
|
||
276 | JointInterface::store_incoming_position(ID_LIP_LEFT_UPPER, target_angle[ICUB_ID_LIP_LEFT_UPPER], timestamp);
|
||
277 | JointInterface::store_incoming_position(ID_LIP_LEFT_LOWER, target_angle[ICUB_ID_LIP_LEFT_LOWER], timestamp);
|
||
278 | JointInterface::store_incoming_position(ID_LIP_CENTER_UPPER, target_angle[ICUB_ID_LIP_CENTER_UPPER], timestamp);
|
||
279 | JointInterface::store_incoming_position(ID_LIP_CENTER_LOWER, target_angle[ICUB_ID_LIP_CENTER_LOWER], timestamp);
|
||
280 | JointInterface::store_incoming_position(ID_LIP_RIGHT_UPPER, target_angle[ICUB_ID_LIP_RIGHT_UPPER], timestamp);
|
||
281 | JointInterface::store_incoming_position(ID_LIP_RIGHT_LOWER, target_angle[ICUB_ID_LIP_RIGHT_LOWER], timestamp);
|
||
282 | */
|
||
283 | } |