humotion / examples / yarp_icub / src / icub_faceinterface.cpp @ a682d53f
History | View | Annotate | Download (9.821 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 | 35b3ca25 | Simon Schulz | void iCubFaceInterface::set_eyelid_angle(float angle){ |
| 112 | 5d29c83f | Simon Schulz | 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 | 35b3ca25 | Simon Schulz | void iCubFaceInterface::set_eyebrow_angle(int id, float *target_angle){ |
| 144 | 5d29c83f | Simon Schulz | 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 | 35b3ca25 | Simon Schulz | void iCubFaceInterface::set_mouth(float *target_angle){ |
| 199 | 5d29c83f | Simon Schulz | //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 | } |