humotion / examples / yarp_icub / src / icub_faceinterface.cpp @ 18e9b892
History | View | Annotate | Download (9.821 KB)
| 1 |
#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 |
string emotion_port_out = "/emotionwriter" + to_string((unsigned long long)i); |
| 94 |
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(float 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, float *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 |
int icub_val = 0; |
| 154 |
|
| 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 |
cmd_s = "L0" + to_string((unsigned long long)icub_val); |
| 182 |
}else{
|
| 183 |
cmd_s = "R0" + to_string((unsigned long long)icub_val); |
| 184 |
} |
| 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(float *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 |
} |