Statistics
| Branch: | Tag: | Revision:

humotion / examples / yarp_icub / src / icub_faceinterface.cpp @ 95dd1012

History | View | Annotate | Download (8.392 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 6a2d467f Simon Schulz
#include <algorithm>
32
#include <string>
33
34
using yarp::os::Network;
35
using yarp::os::Bottle;
36
using std::cout;
37
using std::cerr;
38
using std::string;
39
40
//! constructor
41
iCubFaceInterface::iCubFaceInterface(std::string _scope) {
42
    scope = _scope;
43
44
    // attach to facial expressions:
45
    std::string emotion_scope = scope + "/face/raw/in";
46
    cout << "opening connection to '"<< emotion_scope << "'\n";
47
48 07e68eb7 sschulz
    bool init_ok = true;
49 0c8d22a5 sschulz
    // open emotion port
50
    std::string emotion_port_out = "/emotionwriter";
51
    if (!emotion_port.open(emotion_port_out.c_str())) {
52
        cerr << "ERROR: failed to open emotion port '" << emotion_port_out << "'\n";
53
        init_ok = false;
54
    }
55
    if (!Network::connect(emotion_port_out.c_str(), emotion_scope.c_str())) {
56
        cerr << "ERROR: failed to connect to emotion port '" << emotion_port_out << "'\n";
57
        init_ok = false;
58 6a2d467f Simon Schulz
    }
59 07e68eb7 sschulz
60
    if (!init_ok) {
61
        cerr << "ERROR: failed to set up emotion component\n";
62
        cerr << "       please make sure that the faceExpressions yarpdev is started\n";
63
        cerr << "       (e.g.  yarpdev --name /icub/face/raw --device serial ... is running)\n";
64
        exit(EXIT_FAILURE);
65
    }
66 6a2d467f Simon Schulz
}
67
68
//! destructor
69
iCubFaceInterface::~iCubFaceInterface() {
70
}
71
72
//! special command to set eyelid angle
73
//! \param angle in degrees
74
void iCubFaceInterface::set_eyelid_angle(float angle) {
75 0c8d22a5 sschulz
    if (emotion_port.getOutputCount() > 0) {
76 6a2d467f Simon Schulz
        // try to set the value based on the upper one
77
        // some guesses from the sim: S30 = 0° / S40 = 10°
78
        int opening = (25.0 + 0.8*angle);
79
        opening = std::min(48, std::max(24, opening));
80
81
        if (opening == lid_opening_previous) {
82
            // no update necessary
83
            return;
84
        }
85
86
        lid_angle = angle;
87
        lid_opening_previous = opening;
88
89
        char buf[20];
90
        snprintf(buf, sizeof(buf), "S%2d", opening);
91
92
        // cout << "SETTING EYELID '" << buf << "' (" << angle << " -> " << opening << "\n";
93 0c8d22a5 sschulz
        Bottle &cmd = emotion_port.prepare();
94 6a2d467f Simon Schulz
        cmd.clear();
95
        cmd.addString(buf);
96 0c8d22a5 sschulz
        // NOTE: writeStrict is important in order not to loose packets
97
        emotion_port.writeStrict();
98 6a2d467f Simon Schulz
    } else {
99
        cerr << "ERROR: no icub emotion output\n";
100
        exit(EXIT_FAILURE);
101
    }
102
}
103
104
//! special command to set the eyebrow angle
105
//! \param id {0=left, 1=right)
106
//! \param angle in degrees
107
void iCubFaceInterface::set_eyebrow_angle(int id, float *target_angle) {
108 0c8d22a5 sschulz
    if (emotion_port.getOutputCount() > 0) {
109 6a2d467f Simon Schulz
        double angle = target_angle[id];
110
        int icub_val = 0;
111
112
        // swap rotation direction for eyebrow
113
        if (id == iCubJointInterface::ICUB_ID_EYES_LEFT_BROW) {
114
            angle = -angle;
115
        }
116
117
        // convert to icub representation
118
        if (angle < -20) {
119
            icub_val = 1;
120
        } else if (angle < 10) {
121
            icub_val = 2;
122
        } else if (angle < 20) {
123
            icub_val = 4;
124
        } else {
125
            icub_val = 8;
126
        }
127
128
        // make sure to update only on new values
129
        if (icub_val == target_angle_previous[id]) {
130
                // no updata necessary
131
                return;
132
        }
133
134
        // store actual value
135
        target_angle_previous[id] = icub_val;
136
137
138
        std::string cmd_s;
139
        if (id == iCubJointInterface::ICUB_ID_EYES_LEFT_BROW) {
140 07e68eb7 sschulz
            cmd_s = "L0" + boost::lexical_cast<std::string>(icub_val);
141 6a2d467f Simon Schulz
        } else {
142 07e68eb7 sschulz
            cmd_s = "R0" + boost::lexical_cast<std::string>(icub_val);
143 6a2d467f Simon Schulz
        }
144
145 0c8d22a5 sschulz
        // cout << "SETTING EYEBROW " << id << " (" << angle << " -> " << cmd_s << ")\n";
146 6a2d467f Simon Schulz
147 0c8d22a5 sschulz
        Bottle &cmd = emotion_port.prepare();
148 6a2d467f Simon Schulz
        cmd.clear();
149
        cmd.addString(cmd_s);
150 0c8d22a5 sschulz
        // NOTE: writeStrict is important in order not to loose packets
151
        emotion_port.writeStrict();
152 6a2d467f Simon Schulz
    } else {
153
        cerr << "ERROR: no icub emotion output\n";
154
        exit(EXIT_FAILURE);
155
    }
156
}
157
158
void iCubFaceInterface::set_mouth(float *target_angle) {
159
    // convert from 6DOF mouth displacement to icub leds:
160
    int led_value = 0;
161
162
    // fetch center opening
163
    double center_opening = target_angle[iCubJointInterface::ICUB_ID_LIP_CENTER_LOWER] -
164
            target_angle[iCubJointInterface::ICUB_ID_LIP_CENTER_UPPER];
165
    bool mouth_open = (center_opening > 15.0) ? true : false;
166
167
    // side of mouth high or low?
168
    double center_avg = (target_angle[iCubJointInterface::ICUB_ID_LIP_CENTER_LOWER] +
169
            target_angle[iCubJointInterface::ICUB_ID_LIP_CENTER_UPPER])/2.0;
170
    double left_avg   = (target_angle[iCubJointInterface::ICUB_ID_LIP_LEFT_LOWER] +
171
            target_angle[iCubJointInterface::ICUB_ID_LIP_LEFT_UPPER])/2.0;
172
    double right_avg  = (target_angle[iCubJointInterface::ICUB_ID_LIP_RIGHT_LOWER] +
173
            target_angle[iCubJointInterface::ICUB_ID_LIP_RIGHT_UPPER])/2.0;
174
175
    // happy, neutral or sad?
176
    double diff_l = center_avg - left_avg;
177
    double diff_r = center_avg - right_avg;
178
    double diff   = (diff_l+diff_r)/2.0;
179
180
    if (diff > 2.0) {
181
        if (mouth_open) {
182
            led_value = 0x14;
183
        } else {
184
            if (diff > 2.6) {
185
                led_value = 0x0A;
186
            } else {
187
                led_value = 0x0B;
188
            }
189
        }
190
    } else if (diff < -3.0) {
191
        if (mouth_open) {
192
            led_value = 0x06;
193
        } else {
194
            led_value = 0x18;
195
        }
196
    } else if (diff < -2.0) {
197
        if (mouth_open) {
198
            led_value = 0x04;  // 0x25;
199
        } else {
200
            led_value = 0x08;
201
        }
202
    } else {
203
        if (mouth_open) {
204
            led_value = 0x16;
205
        } else {
206
            led_value = 0x08;
207
        }
208
    }
209
210
211
    if (led_value == previous_mouth_state) {
212
        // no update necessary
213
        return;
214
    }
215
216
    previous_mouth_state = led_value;
217
218
    // convert to string
219
    char buf[10];
220
    snprintf(buf, sizeof(buf), "M%02X", led_value);
221
222
    /*
223
    cout << "sending mouth " << buf << "\n";
224
    cout << boost::format("  mouth angles: %3.2f %3.2f %3.2f\n")
225
            % target_angle[ICUB_ID_LIP_LEFT_UPPER]
226
            % target_angle[ICUB_ID_LIP_CENTER_UPPER]
227
            % target_angle[ICUB_ID_LIP_RIGHT_UPPER];
228
    cout << boost::format("  mouth         %3.2f %3.2f %3.2f\n")
229
            % target_angle[ICUB_ID_LIP_LEFT_LOWER]
230
            % target_angle[ICUB_ID_LIP_CENTER_LOWER]
231
            % target_angle[ICUB_ID_LIP_RIGHT_LOWER];
232
    cout << boost::format("  mouth  open=%3.2f diff=%3.2f\n")
233
            % center_opening
234
            % diff;
235
    */
236
237
    // add mouth
238 0c8d22a5 sschulz
    Bottle &cmd = emotion_port.prepare();
239 6a2d467f Simon Schulz
    cmd.clear();
240
    cmd.addString(buf);
241 0c8d22a5 sschulz
    // NOTE: writeStrict is important in order not to loose packets
242
    emotion_port.writeStrict();
243 6a2d467f Simon Schulz
244
/*
245
    //store joint values which we do not handle on icub here:
246
    double timestamp = get_timestamp_ms();
247
    JointInterface::store_incoming_position(ID_LIP_LEFT_UPPER,   target_angle[ICUB_ID_LIP_LEFT_UPPER], timestamp);
248
    JointInterface::store_incoming_position(ID_LIP_LEFT_LOWER,   target_angle[ICUB_ID_LIP_LEFT_LOWER], timestamp);
249
    JointInterface::store_incoming_position(ID_LIP_CENTER_UPPER, target_angle[ICUB_ID_LIP_CENTER_UPPER], timestamp);
250
    JointInterface::store_incoming_position(ID_LIP_CENTER_LOWER, target_angle[ICUB_ID_LIP_CENTER_LOWER], timestamp);
251
    JointInterface::store_incoming_position(ID_LIP_RIGHT_UPPER,  target_angle[ICUB_ID_LIP_RIGHT_UPPER], timestamp);
252
    JointInterface::store_incoming_position(ID_LIP_RIGHT_LOWER,  target_angle[ICUB_ID_LIP_RIGHT_LOWER], timestamp);
253
*/
254
}