Statistics
| Branch: | Tag: | Revision:

humotion / examples / humotion_yarp_icub / src / icub_faceinterface.cpp @ a5cc61d4

History | View | Annotate | Download (8.237 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
32 6a2d467f Simon Schulz
#include <algorithm>
33
#include <string>
34
35
using yarp::os::Network;
36
using yarp::os::Bottle;
37
using std::cout;
38
using std::cerr;
39
using std::string;
40
41
//! constructor
42
iCubFaceInterface::iCubFaceInterface(std::string _scope) {
43
    scope = _scope;
44
45
    // attach to facial expressions:
46
    std::string emotion_scope = scope + "/face/raw/in";
47
    cout << "opening connection to '"<< emotion_scope << "'\n";
48
49
    for (int i = 0; i < 4; i++) {
50
        // strange, if we use one output port only the first command is executed?! flushing issues?
51
        std::string emotion_port_out = "/emotionwriter" + std::to_string((unsigned long long)i);
52
        if (!emotion_port[i].open(emotion_port_out.c_str())) {
53
            cerr << "ERROR: failed to open emotion port '" << emotion_port_out << "'\n";
54
            exit(EXIT_FAILURE);
55
        }
56
        if (!Network::connect(emotion_port_out.c_str(), emotion_scope.c_str())) {
57
            cerr << "ERROR: failed to connect to emotion port '" << emotion_port_out << "'\n";
58
            exit(EXIT_FAILURE);
59
        }
60
    }
61
}
62
63
//! destructor
64
iCubFaceInterface::~iCubFaceInterface() {
65
}
66
67
//! special command to set eyelid angle
68
//! \param angle in degrees
69
void iCubFaceInterface::set_eyelid_angle(float angle) {
70
    if (emotion_port[0].getOutputCount() > 0) {
71
        // try to set the value based on the upper one
72
        // some guesses from the sim: S30 = 0° / S40 = 10°
73
        int opening = (25.0 + 0.8*angle);
74
        opening = std::min(48, std::max(24, opening));
75
76
        if (opening == lid_opening_previous) {
77
            // no update necessary
78
            return;
79
        }
80
81
        lid_angle = angle;
82
        lid_opening_previous = opening;
83
84
        char buf[20];
85
        snprintf(buf, sizeof(buf), "S%2d", opening);
86
87
        // cout << "SETTING EYELID '" << buf << "' (" << angle << " -> " << opening << "\n";
88
        Bottle &cmd = emotion_port[0].prepare();
89
        cmd.clear();
90
        cmd.addString(buf);
91
        emotion_port[0].writeStrict();
92
    } else {
93
        cerr << "ERROR: no icub emotion output\n";
94
        exit(EXIT_FAILURE);
95
    }
96
}
97
98
//! special command to set the eyebrow angle
99
//! \param id {0=left, 1=right)
100
//! \param angle in degrees
101
void iCubFaceInterface::set_eyebrow_angle(int id, float *target_angle) {
102
    int port_id;
103
    if (id == iCubJointInterface::ICUB_ID_EYES_LEFT_BROW) {
104
        port_id = 1;
105
    } else {
106
        port_id = 2;
107
    }
108
109
    if (emotion_port[port_id].getOutputCount() > 0) {
110
        double angle = target_angle[id];
111
        int icub_val = 0;
112
113
        // swap rotation direction for eyebrow
114
        if (id == iCubJointInterface::ICUB_ID_EYES_LEFT_BROW) {
115
            angle = -angle;
116
        }
117
118
        // convert to icub representation
119
        if (angle < -20) {
120
            icub_val = 1;
121
        } else if (angle < 10) {
122
            icub_val = 2;
123
        } else if (angle < 20) {
124
            icub_val = 4;
125
        } else {
126
            icub_val = 8;
127
        }
128
129
        // make sure to update only on new values
130
        if (icub_val == target_angle_previous[id]) {
131
                // no updata necessary
132
                return;
133
        }
134
135
        // store actual value
136
        target_angle_previous[id] = icub_val;
137
138
139
        std::string cmd_s;
140
        if (id == iCubJointInterface::ICUB_ID_EYES_LEFT_BROW) {
141
            cmd_s = "L0" + std::to_string((unsigned long long)icub_val);
142
        } else {
143
            cmd_s = "R0" + std::to_string((unsigned long long)icub_val);
144
        }
145
146
        cout << "SETTING EYEBROW " << id << " (" << angle << " -> " << cmd_s << ")\n";
147
148
        Bottle &cmd = emotion_port[port_id].prepare();
149
        cmd.clear();
150
        cmd.addString(cmd_s);
151
        emotion_port[port_id].writeStrict();
152
    } 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
    Bottle &cmd = emotion_port[3].prepare();
239
    cmd.clear();
240
    cmd.addString(buf);
241
    emotion_port[3].writeStrict();
242
243
/*
244
    //store joint values which we do not handle on icub here:
245
    double timestamp = get_timestamp_ms();
246
    JointInterface::store_incoming_position(ID_LIP_LEFT_UPPER,   target_angle[ICUB_ID_LIP_LEFT_UPPER], timestamp);
247
    JointInterface::store_incoming_position(ID_LIP_LEFT_LOWER,   target_angle[ICUB_ID_LIP_LEFT_LOWER], timestamp);
248
    JointInterface::store_incoming_position(ID_LIP_CENTER_UPPER, target_angle[ICUB_ID_LIP_CENTER_UPPER], timestamp);
249
    JointInterface::store_incoming_position(ID_LIP_CENTER_LOWER, target_angle[ICUB_ID_LIP_CENTER_LOWER], timestamp);
250
    JointInterface::store_incoming_position(ID_LIP_RIGHT_UPPER,  target_angle[ICUB_ID_LIP_RIGHT_UPPER], timestamp);
251
    JointInterface::store_incoming_position(ID_LIP_RIGHT_LOWER,  target_angle[ICUB_ID_LIP_RIGHT_LOWER], timestamp);
252
*/
253
}