Statistics
| Branch: | Tag: | Revision:

humotion / examples / humotion_yarp_icub / src / icub_faceinterface.cpp @ 6a2d467f

History | View | Annotate | Download (8.236 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
#include <algorithm>
31
#include <string>
32
#include <boost/format.hpp>
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
    for (int i = 0; i < 4; i++) {
49
        // strange, if we use one output port only the first command is executed?! flushing issues?
50
        std::string emotion_port_out = "/emotionwriter" + std::to_string((unsigned long long)i);
51
        if (!emotion_port[i].open(emotion_port_out.c_str())) {
52
            cerr << "ERROR: failed to open emotion port '" << emotion_port_out << "'\n";
53
            exit(EXIT_FAILURE);
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
            exit(EXIT_FAILURE);
58
        }
59
    }
60
}
61
62
//! destructor
63
iCubFaceInterface::~iCubFaceInterface() {
64
}
65
66
//! special command to set eyelid angle
67
//! \param angle in degrees
68
void iCubFaceInterface::set_eyelid_angle(float angle) {
69
    if (emotion_port[0].getOutputCount() > 0) {
70
        // try to set the value based on the upper one
71
        // some guesses from the sim: S30 = 0° / S40 = 10°
72
        int opening = (25.0 + 0.8*angle);
73
        opening = std::min(48, std::max(24, opening));
74
75
        if (opening == lid_opening_previous) {
76
            // no update necessary
77
            return;
78
        }
79
80
        lid_angle = angle;
81
        lid_opening_previous = opening;
82
83
        char buf[20];
84
        snprintf(buf, sizeof(buf), "S%2d", opening);
85
86
        // cout << "SETTING EYELID '" << buf << "' (" << angle << " -> " << opening << "\n";
87
        Bottle &cmd = emotion_port[0].prepare();
88
        cmd.clear();
89
        cmd.addString(buf);
90
        emotion_port[0].writeStrict();
91
    } else {
92
        cerr << "ERROR: no icub emotion output\n";
93
        exit(EXIT_FAILURE);
94
    }
95
}
96
97
//! special command to set the eyebrow angle
98
//! \param id {0=left, 1=right)
99
//! \param angle in degrees
100
void iCubFaceInterface::set_eyebrow_angle(int id, float *target_angle) {
101
    int port_id;
102
    if (id == iCubJointInterface::ICUB_ID_EYES_LEFT_BROW) {
103
        port_id = 1;
104
    } else {
105
        port_id = 2;
106
    }
107
108
    if (emotion_port[port_id].getOutputCount() > 0) {
109
        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
            cmd_s = "L0" + std::to_string((unsigned long long)icub_val);
141
        } else {
142
            cmd_s = "R0" + std::to_string((unsigned long long)icub_val);
143
        }
144
145
        cout << "SETTING EYEBROW " << id << " (" << angle << " -> " << cmd_s << ")\n";
146
147
        Bottle &cmd = emotion_port[port_id].prepare();
148
        cmd.clear();
149
        cmd.addString(cmd_s);
150
        emotion_port[port_id].writeStrict();
151
    } else {
152
        cerr << "ERROR: no icub emotion output\n";
153
        exit(EXIT_FAILURE);
154
    }
155
}
156
157
void iCubFaceInterface::set_mouth(float *target_angle) {
158
    // convert from 6DOF mouth displacement to icub leds:
159
    int led_value = 0;
160
161
    // fetch center opening
162
    double center_opening = target_angle[iCubJointInterface::ICUB_ID_LIP_CENTER_LOWER] -
163
            target_angle[iCubJointInterface::ICUB_ID_LIP_CENTER_UPPER];
164
    bool mouth_open = (center_opening > 15.0) ? true : false;
165
166
    // side of mouth high or low?
167
    double center_avg = (target_angle[iCubJointInterface::ICUB_ID_LIP_CENTER_LOWER] +
168
            target_angle[iCubJointInterface::ICUB_ID_LIP_CENTER_UPPER])/2.0;
169
    double left_avg   = (target_angle[iCubJointInterface::ICUB_ID_LIP_LEFT_LOWER] +
170
            target_angle[iCubJointInterface::ICUB_ID_LIP_LEFT_UPPER])/2.0;
171
    double right_avg  = (target_angle[iCubJointInterface::ICUB_ID_LIP_RIGHT_LOWER] +
172
            target_angle[iCubJointInterface::ICUB_ID_LIP_RIGHT_UPPER])/2.0;
173
174
    // happy, neutral or sad?
175
    double diff_l = center_avg - left_avg;
176
    double diff_r = center_avg - right_avg;
177
    double diff   = (diff_l+diff_r)/2.0;
178
179
    if (diff > 2.0) {
180
        if (mouth_open) {
181
            led_value = 0x14;
182
        } else {
183
            if (diff > 2.6) {
184
                led_value = 0x0A;
185
            } else {
186
                led_value = 0x0B;
187
            }
188
        }
189
    } else if (diff < -3.0) {
190
        if (mouth_open) {
191
            led_value = 0x06;
192
        } else {
193
            led_value = 0x18;
194
        }
195
    } else if (diff < -2.0) {
196
        if (mouth_open) {
197
            led_value = 0x04;  // 0x25;
198
        } else {
199
            led_value = 0x08;
200
        }
201
    } else {
202
        if (mouth_open) {
203
            led_value = 0x16;
204
        } else {
205
            led_value = 0x08;
206
        }
207
    }
208
209
210
    if (led_value == previous_mouth_state) {
211
        // no update necessary
212
        return;
213
    }
214
215
    previous_mouth_state = led_value;
216
217
    // convert to string
218
    char buf[10];
219
    snprintf(buf, sizeof(buf), "M%02X", led_value);
220
221
    /*
222
    cout << "sending mouth " << buf << "\n";
223
    cout << boost::format("  mouth angles: %3.2f %3.2f %3.2f\n")
224
            % target_angle[ICUB_ID_LIP_LEFT_UPPER]
225
            % target_angle[ICUB_ID_LIP_CENTER_UPPER]
226
            % target_angle[ICUB_ID_LIP_RIGHT_UPPER];
227
    cout << boost::format("  mouth         %3.2f %3.2f %3.2f\n")
228
            % target_angle[ICUB_ID_LIP_LEFT_LOWER]
229
            % target_angle[ICUB_ID_LIP_CENTER_LOWER]
230
            % target_angle[ICUB_ID_LIP_RIGHT_LOWER];
231
    cout << boost::format("  mouth  open=%3.2f diff=%3.2f\n")
232
            % center_opening
233
            % diff;
234
    */
235
236
    // add mouth
237
    Bottle &cmd = emotion_port[3].prepare();
238
    cmd.clear();
239
    cmd.addString(buf);
240
    emotion_port[3].writeStrict();
241
242
/*
243
    //store joint values which we do not handle on icub here:
244
    double timestamp = get_timestamp_ms();
245
    JointInterface::store_incoming_position(ID_LIP_LEFT_UPPER,   target_angle[ICUB_ID_LIP_LEFT_UPPER], timestamp);
246
    JointInterface::store_incoming_position(ID_LIP_LEFT_LOWER,   target_angle[ICUB_ID_LIP_LEFT_LOWER], timestamp);
247
    JointInterface::store_incoming_position(ID_LIP_CENTER_UPPER, target_angle[ICUB_ID_LIP_CENTER_UPPER], timestamp);
248
    JointInterface::store_incoming_position(ID_LIP_CENTER_LOWER, target_angle[ICUB_ID_LIP_CENTER_LOWER], timestamp);
249
    JointInterface::store_incoming_position(ID_LIP_RIGHT_UPPER,  target_angle[ICUB_ID_LIP_RIGHT_UPPER], timestamp);
250
    JointInterface::store_incoming_position(ID_LIP_RIGHT_LOWER,  target_angle[ICUB_ID_LIP_RIGHT_LOWER], timestamp);
251
*/
252
}