Statistics
| Branch: | Tag: | Revision:

humotion / examples / yarp_icub / src / icub_jointinterface.cpp @ 708960ff

History | View | Annotate | Download (18.326 KB)

1
#include "icub_jointinterface.h"
2
#include "icub_faceinterface.h"
3

    
4
#include <yarp/os/Property.h>
5

    
6
using std::cout;
7
using std::cerr;
8
using std::string;
9

    
10
//! constructor
11
iCubJointInterface::iCubJointInterface(string _scope) : humotion::server::JointInterface() {
12
    scope = _scope;
13

    
14
    // add mappings from icub ids to humotion ids
15
    init_id_map();
16

    
17
    // initialise the pd controller for the velocity and position mixer
18
    init_pv_mix_pid();
19

    
20
    // intantiate the face interface
21
    face_interface_ = new iCubFaceInterface(scope);
22

    
23
    // intantiate the polydriver
24
    yarp::os::Property options;
25
    options.put("device", "remote_controlboard");
26
    options.put("local", "/local/head");
27
    options.put("remote", scope + "/head");
28
    yarp_polydriver_.open(options);
29

    
30
    // fetch yarp views:
31
    bool success = true;
32
    //success &= yarp_polydriver_.view(yarp_iencs_);
33
    //success &= yarp_polydriver_.view(yarp_ipos_);
34
    success &= yarp_polydriver_.view(yarp_ivel_);
35
    success &= yarp_polydriver_.view(yarp_ilimits_);
36
    success &= yarp_polydriver_.view(yarp_pid_);
37
    success &= yarp_polydriver_.view(yarp_amp_);
38

    
39
    if (!success) {
40
        cout << "ERROR: failed to fetch one or more yarp views... exiting\n";
41
        exit(EXIT_FAILURE);
42
    }
43

    
44

    
45
    //tell humotion about min/max joint values:
46
    init_joints();
47

    
48
    //initialise joint controller
49
    init_controller();
50
}
51

    
52
//! destructor
53
iCubJointInterface::~iCubJointInterface(){
54
    //stop velocity controller on exit
55
    yarp_ivel_->stop();
56
}
57

    
58
//! init the controller that allows to write target angles or velocities
59
void iCubJointInterface::init_controller() {
60
    int number_of_joints;
61

    
62
    // use position controller, first fetch no of axes:
63
    yarp_ivel_->getAxes(&number_of_joints);
64

    
65
    // set ref acceleration to a value for all axes:
66
    yarp_commands_.resize(number_of_joints);
67
    yarp_commands_ = 1e6;
68
    yarp_ivel_->setRefAccelerations(yarp_commands_.data());
69
    yarp_ivel_->setVelocityMode();
70
}
71

    
72
//! initialise icub joint id to humotion joint id mappings
73
void iCubJointInterface::init_id_map() {
74
        insert_icupid_to_humotionid_mapping(ICUB_ID_LIP_LEFT_UPPER,   ID_LIP_LEFT_UPPER);
75
        insert_icupid_to_humotionid_mapping(ICUB_ID_LIP_LEFT_LOWER,   ID_LIP_LEFT_LOWER);
76
        insert_icupid_to_humotionid_mapping(ICUB_ID_LIP_CENTER_UPPER, ID_LIP_CENTER_UPPER);
77
        insert_icupid_to_humotionid_mapping(ICUB_ID_LIP_CENTER_LOWER, ID_LIP_CENTER_LOWER);
78
        insert_icupid_to_humotionid_mapping(ICUB_ID_LIP_RIGHT_UPPER,  ID_LIP_RIGHT_UPPER);
79
        insert_icupid_to_humotionid_mapping(ICUB_ID_LIP_RIGHT_LOWER,  ID_LIP_RIGHT_LOWER);
80
        insert_icupid_to_humotionid_mapping(ICUB_ID_NECK_PAN,    ID_NECK_PAN);
81
        insert_icupid_to_humotionid_mapping(ICUB_ID_NECK_TILT,   ID_NECK_TILT);
82
        insert_icupid_to_humotionid_mapping(ICUB_ID_NECK_ROLL,   ID_NECK_ROLL);
83
        // FIXME: remove this hack tha repurposes LEFT/RIGHT eye pan from humotion as vergence/pan
84
        insert_icupid_to_humotionid_mapping(ICUB_ID_EYES_PAN,   ID_EYES_LEFT_LR);
85
        insert_icupid_to_humotionid_mapping(ICUB_ID_EYES_VERGENCE,   ID_EYES_RIGHT_LR);
86
        insert_icupid_to_humotionid_mapping(ICUB_ID_EYES_BOTH_UD,   ID_EYES_BOTH_UD);
87
        insert_icupid_to_humotionid_mapping(ICUB_ID_EYES_LEFT_LID_LOWER, ID_EYES_LEFT_LID_LOWER);
88
        insert_icupid_to_humotionid_mapping(ICUB_ID_EYES_LEFT_LID_UPPER, ID_EYES_LEFT_LID_UPPER);
89
        insert_icupid_to_humotionid_mapping(ICUB_ID_EYES_LEFT_BROW, ID_EYES_LEFT_BROW);
90
        insert_icupid_to_humotionid_mapping(ICUB_ID_EYES_RIGHT_LID_LOWER, ID_EYES_RIGHT_LID_LOWER);
91
        insert_icupid_to_humotionid_mapping(ICUB_ID_EYES_RIGHT_LID_UPPER,ID_EYES_RIGHT_LID_UPPER);
92
        insert_icupid_to_humotionid_mapping(ICUB_ID_EYES_RIGHT_BROW, ID_EYES_RIGHT_BROW);
93
}
94

    
95
//! initialize the position and velocity mixer PD controller
96
void iCubJointInterface::init_pv_mix_pid() {
97
    // init control variables and last error variable for the internal
98
    // position and velocity mixer PD controller:
99
    pv_mix_pid_p_.resize(ICUB_JOINT_ID_ENUM_SIZE);
100
    pv_mix_pid_d_.resize(ICUB_JOINT_ID_ENUM_SIZE);
101
    pv_mix_last_error_.resize(ICUB_JOINT_ID_ENUM_SIZE);
102

    
103
    enum_id_bimap_t::const_iterator it;
104
    for(it = enum_id_bimap.begin(); it != enum_id_bimap.end(); ++it) {
105
        int id = it->left;
106
        pv_mix_pid_p_[id] = 15.5;
107
        pv_mix_pid_d_[id] = 0.3;
108
        pv_mix_last_error_[id] = 0.0;
109
    }
110

    
111
}
112

    
113
//! add mapping from icub joint ids to humotion ids
114
//! this might look strange at the first sight but we need to have a generic
115
//! way to acces joints from libhumotion. therefore the lib uses its enum with ID_* enum ids
116
//! to access the joints. now we need to define a mapping to map those to the icub motor ids.
117
void iCubJointInterface::insert_icupid_to_humotionid_mapping(int icubid, int humotionid) {
118
    enum_id_bimap.insert(enum_id_bimap_entry_t(icubid, humotionid));
119
}
120

    
121

    
122

    
123

    
124
void iCubJointInterface::run(){
125
    float loop_duration_ms = 1000.0 / MAIN_LOOP_FREQUENCY;
126
    iCubDataReceiver *data_receiver = new iCubDataReceiver(loop_duration_ms, this);
127
    data_receiver->start();
128
}
129

    
130
//! stores the target position & velocity of a given joint
131
//! \param enum id of joint
132
//! \param float value
133
void iCubJointInterface::publish_target(int humotion_id, float position, float velocity){
134
    // special handler for eye joints
135
    if ((humotion_id == JointInterface::ID_EYES_LEFT_LR) ||
136
        (humotion_id == JointInterface::ID_EYES_RIGHT_LR)){
137
        // the icub has a combined pan angle for both eyes, so seperate this:
138
        float target_position_left  = get_target_position(JointInterface::ID_EYES_LEFT_LR);
139
        float target_position_right = get_target_position(JointInterface::ID_EYES_RIGHT_LR);
140
        float target_velocity_left  = get_target_velocity(JointInterface::ID_EYES_LEFT_LR);
141
        float target_velocity_right = get_target_velocity(JointInterface::ID_EYES_RIGHT_LR);
142

    
143

    
144
        // calculate target angles
145
        float target_position_pan      = (target_position_right + target_position_left) / 2;
146
        float target_position_vergence = (target_position_right - target_position_left);
147

    
148
        cout << "LR " << target_position_left << " " << target_position_right <<
149
                " PAN " << target_position_pan << " VERGENCE " << target_position_vergence << "\n";
150

    
151
        // calculate target velocities
152
        // for now just use the same velocity for pan and vergence
153
        float target_velocity_pan = (target_velocity_left + target_velocity_right) / 2.0;
154
        float target_velocity_vergence = target_velocity_left - target_velocity_right;
155

    
156
        store_icub_joint_target(ICUB_ID_EYES_PAN,
157
                                target_position_pan, target_velocity_pan);
158
        store_icub_joint_target(ICUB_ID_EYES_VERGENCE,
159
                                target_position_vergence, target_velocity_vergence);
160
    }else{
161
        // convert to icub joint id
162
        int icub_id = convert_humotion_jointid_to_icub(humotion_id);
163
        // store target data
164
        store_icub_joint_target(icub_id, position, velocity);
165
    }
166
}
167

    
168

    
169
//! set the target data for a given icub joint
170
//! \param id of joint
171
//! \param float value of position
172
void iCubJointInterface::store_icub_joint_target(int icub_id, float position, float velocity) {
173
    cout << "store_icub_joint_target(" << icub_id << ", " << position << ", ..)\n";
174

    
175
    if ((icub_id == ICUB_ID_NECK_PAN) || (icub_id == ICUB_ID_EYES_VERGENCE)){
176
        // icub uses an inverted neck pan specification
177
        position = -position;
178
        velocity = -velocity;
179
    }
180

    
181
    // store values
182
    target_angle_[icub_id] = position;
183
    target_velocity_[icub_id] = velocity;
184
}
185

    
186
//! execute a move in velocity mode
187
//! \param id of joint
188
//! \param angle
189
void iCubJointInterface::set_target_in_velocitymode(int icub_id) {
190
    // fetch humotion id from icub joint id
191
    int humotion_id = convert_icub_jointid_to_humotion(icub_id);
192

    
193
    // fetch the target velocity
194
    float target_velocity = target_velocity_[icub_id];
195

    
196
    float vmax = 350.0;
197
    if (target_velocity > vmax)  target_velocity = vmax;
198
    if (target_velocity < -vmax) target_velocity = -vmax;
199

    
200
    //execute:
201
    //ivel->velocityMove(id, speed);
202
    if ((icub_id != ICUB_ID_NECK_PAN)
203
        && (icub_id != ICUB_ID_EYES_BOTH_UD)
204
        && (icub_id != ICUB_ID_NECK_TILT)
205
        && (icub_id != ICUB_ID_EYES_PAN)
206
        && (icub_id != ICUB_ID_EYES_VERGENCE)
207
        //&& (icub_id != ICUB_ID_NECK_TILT)
208
            ){
209
        // limit to some joints for debugging...
210
        return;
211
    }
212

    
213
    // we now add a pd control loop for velocity moves in order to handle position errors
214
    // TODO: add position interpolation into future. this requires to enable the velocity
215
    //       broadcast in the torso and head ini file and fetch that velocity in the
216
    //       icub_data_receiver as well. TODO: check if the can bus has enough bandwidth for that...
217

    
218
    // first: fetch the timstamp of the last known position
219
    humotion::Timestamp data_ts = get_ts_position(humotion_id).get_last_timestamp();
220

    
221
    // fetch current position & velocity
222
    float current_position = get_ts_position(humotion_id).get_interpolated_value(data_ts);
223
    float current_velocity = get_ts_speed(humotion_id).get_interpolated_value(data_ts);
224

    
225
    // the icub has a combined eye pan actuator, therefore
226
    // we have to calculate pan angle and vergence based on the left and right target angles:
227
    if (icub_id == ICUB_ID_EYES_PAN) {
228
        // fetch timestamp
229
        data_ts = get_ts_position(ID_EYES_LEFT_LR).get_last_timestamp();
230

    
231
        // get the left and right positions
232
        float pos_left  = get_ts_position(ID_EYES_LEFT_LR).get_interpolated_value(data_ts);
233
        float pos_right = get_ts_position(ID_EYES_RIGHT_LR).get_interpolated_value(data_ts);
234
        current_position = (pos_left + pos_right) / 2.0;
235

    
236
        // same for velocities:
237
        float vel_left  = get_ts_speed(ID_EYES_LEFT_LR).get_interpolated_value(data_ts);
238
        float vel_right = get_ts_speed(ID_EYES_RIGHT_LR).get_interpolated_value(data_ts);
239
        current_velocity = (vel_left + vel_right) / 2.0;
240

    
241
    }else if (icub_id == ICUB_ID_EYES_VERGENCE) {
242
        // fetch timestamp
243
        data_ts = get_ts_position(ID_EYES_LEFT_LR).get_last_timestamp();
244

    
245
        // get the left and right positions
246
        float pos_left  = get_ts_position(ID_EYES_LEFT_LR).get_interpolated_value(data_ts);
247
        float pos_right = get_ts_position(ID_EYES_RIGHT_LR).get_interpolated_value(data_ts);
248

    
249
        current_position = pos_left - pos_right;
250

    
251
        // same for velocities:
252
        float vel_left  = get_ts_speed(ID_EYES_LEFT_LR).get_interpolated_value(data_ts);
253
        float vel_right = get_ts_speed(ID_EYES_RIGHT_LR).get_interpolated_value(data_ts);
254
        current_velocity = (vel_left - vel_right);
255

    
256
    }
257

    
258
    // calculate position error:
259
    float position_error = target_angle_[icub_id] - current_position;
260

    
261
    // calculate d term
262
    float error_d = (position_error - pv_mix_last_error_[icub_id]) / (framerate*1000.0);
263
    pv_mix_last_error_[icub_id] = position_error;
264

    
265
    // finally do a PD loop to get the target velocity
266
    float pv_mix_velocity = pv_mix_pid_p_[icub_id] * position_error
267
                            + pv_mix_pid_p_[icub_id]*error_d
268
                            + target_velocity;
269

    
270

    
271
    //if (icub_id == ICUB_ID_EYES_VERGENCE) { target_velocity = 0.0; pv_mix_velocity = 0.0;}
272

    
273
    cout << "\n"
274
         << current_position << " "
275
         << target_angle_[icub_id] << " "
276
         << current_velocity << " "
277
         << pv_mix_velocity  << " "
278
         << target_velocity  << " "
279
         << position_error << " "
280
         << " PID" << icub_id << "\n";
281

    
282
    // execute velocity move
283
    bool success;
284
    int count = 0;
285
    do{
286
        success = yarp_ivel_->velocityMove(icub_id, pv_mix_velocity);
287
        if (count++ >= 10 ) {
288
            cerr << "ERROR: failed to send velocity move command! gave up after 10 trials\n";
289
            yarp_ivel_->stop();
290
            exit(EXIT_FAILURE);
291
        }
292
    } while(!success);
293
}
294

    
295
//! actually execute the scheduled motion commands
296
void iCubJointInterface::execute_motion(){
297

    
298
    // set up neck and eye motion commands in velocitymode
299
    for(int i=ICUB_ID_NECK_TILT; i<=ICUB_ID_EYES_VERGENCE; i++){
300
        set_target_in_velocitymode(i);
301
    }
302
/*
303
    // eyelids: unfortuantely the icub has only 1dof for eyelids
304
    // therefore we can only use an opening value
305
    float opening_left  = target_angle_[ICUB_ID_EYES_LEFT_LID_UPPER]
306
                          - target_angle_[ICUB_ID_EYES_LEFT_LID_LOWER];
307
    float opening_right = target_angle_[ICUB_ID_EYES_RIGHT_LID_UPPER]
308
                          - target_angle_[ICUB_ID_EYES_RIGHT_LID_LOWER];
309
    float opening = (opening_left + opening_right) / 2.0;
310
    // send it to icub face if
311
    face_interface_->set_eyelid_angle(opening);
312

313
    //eyebrows are set using a special command as well:
314
    face_interface_->set_eyebrow_angle(ICUB_ID_EYES_LEFT_BROW, target_angle_);
315
    face_interface_->set_eyebrow_angle(ICUB_ID_EYES_RIGHT_BROW, target_angle_);
316

317
    //mouth
318
    face_interface_->set_mouth(target_angle_);
319
*/
320

    
321
    //store joint values which we do not handle on icub here:
322
    double timestamp = humotion::Timestamp::now().to_seconds();
323
    JointInterface::store_incoming_position(ID_LIP_LEFT_UPPER,   target_angle_[ICUB_ID_LIP_LEFT_UPPER], timestamp);
324
    JointInterface::store_incoming_position(ID_LIP_LEFT_LOWER,   target_angle_[ICUB_ID_LIP_LEFT_LOWER], timestamp);
325
    JointInterface::store_incoming_position(ID_LIP_CENTER_UPPER, target_angle_[ICUB_ID_LIP_CENTER_UPPER], timestamp);
326
    JointInterface::store_incoming_position(ID_LIP_CENTER_LOWER, target_angle_[ICUB_ID_LIP_CENTER_LOWER], timestamp);
327
    JointInterface::store_incoming_position(ID_LIP_RIGHT_UPPER,  target_angle_[ICUB_ID_LIP_RIGHT_UPPER], timestamp);
328
    JointInterface::store_incoming_position(ID_LIP_RIGHT_LOWER,  target_angle_[ICUB_ID_LIP_RIGHT_LOWER], timestamp);
329
}
330

    
331

    
332
void iCubJointInterface::set_joint_enable_state(int humotion_id, bool enable) {
333
    int icub_jointid = convert_humotion_jointid_to_icub(humotion_id);
334
/*
335
    switch(e){
336
        default:
337
            break;
338

339
    case(ID_NECK_PAN):
340
        icub_jointid = ICUB_ID_NECK_PAN;
341
        break;
342

343
    case(ID_NECK_TILT):
344
        icub_jointid = ICUB_ID_NECK_TILT;
345
        break;
346

347
    case(ID_NECK_ROLL):
348
        icub_jointid = ICUB_ID_NECK_ROLL;
349
        break;
350

351
    case(ID_EYES_BOTH_UD):
352
        icub_jointid = ICUB_ID_EYES_BOTH_UD;
353
        break;
354

355
    // icub handles eyes as pan angle + vergence...
356
    // -> hack: left eye enables pan and right eye enables vergence
357
    case(ID_EYES_LEFT_LR):
358
        icub_jointid = ICUB_ID_EYES_PAN;
359
        break;
360

361
    case(ID_EYES_RIGHT_LR):
362
        icub_jointid = ICUB_ID_EYES_VERGENCE;
363
        break;
364
    }
365
    */
366

    
367
    if ((icub_jointid != -1) && (icub_jointid <= ICUB_ID_EYES_VERGENCE)) {
368
        if (enable) {
369
            yarp_amp_->enableAmp(icub_jointid);
370
            yarp_pid_->enablePid(icub_jointid);
371
        } else {
372
            yarp_pid_->disablePid(icub_jointid);
373
            yarp_amp_->disableAmp(icub_jointid);
374
        }
375
    }
376
}
377

    
378
//! prepare and enable a joint
379
//! NOTE: this should also prefill the min/max positions for this joint
380
//! \param the enum id of a joint
381
void iCubJointInterface::enable_joint(int e){
382
    set_joint_enable_state(e, true);
383
}
384

    
385
//! shutdown and disable a joint
386
//! \param the enum id of a joint
387
void iCubJointInterface::disable_joint(int e){
388
    set_joint_enable_state(e, false);
389
}
390

    
391
void iCubJointInterface::store_min_max(yarp::dev::IControlLimits *ilimits, int icub_id){
392
    double min, max;
393
    int humotion_id = convert_icub_jointid_to_humotion(icub_id);
394

    
395
    ilimits->getLimits(icub_id, &min, &max);
396
    joint_min[humotion_id] = min;
397
    joint_max[humotion_id] = max;
398
}
399

    
400
//! initialise a joint (set up controller mode etc)
401
void iCubJointInterface::init_joints(){
402
    store_min_max(yarp_ilimits_, ICUB_ID_NECK_TILT);
403
    store_min_max(yarp_ilimits_, ICUB_ID_NECK_ROLL);
404
    store_min_max(yarp_ilimits_, ICUB_ID_NECK_PAN);
405
    store_min_max(yarp_ilimits_, ICUB_ID_EYES_BOTH_UD);
406

    
407
    //icub handles eyes differently, we have to set pan angle + vergence
408
    double pan_min, pan_max, vergence_min, vergence_max;
409
    yarp_ilimits_->getLimits(ICUB_ID_EYES_PAN, &pan_min, &pan_max);
410
    yarp_ilimits_->getLimits(ICUB_ID_EYES_VERGENCE, &vergence_min, &vergence_max);
411

    
412
    //this is not 100% correct, should be fixed:
413
    joint_min[ID_EYES_LEFT_LR] = pan_min; // - vergence_max/2;
414
    joint_max[ID_EYES_LEFT_LR] = pan_max; // - vergence_max/2;
415
    joint_min[ID_EYES_RIGHT_LR] = joint_min[ID_EYES_LEFT_LR];
416
    joint_max[ID_EYES_RIGHT_LR] = joint_max[ID_EYES_LEFT_LR];
417

    
418
    //eyelids:
419
    joint_min[ID_EYES_RIGHT_LID_UPPER] = -50; //24-30;
420
    joint_max[ID_EYES_RIGHT_LID_UPPER] = 50; //48-30;
421
    //lid_angle = joint_max[ID_EYES_RIGHT_LID_UPPER];
422

    
423
    //eyebrows:
424
    joint_min[ID_EYES_LEFT_BROW] = -50;
425
    joint_max[ID_EYES_LEFT_BROW] = 50;
426
    joint_min[ID_EYES_RIGHT_BROW] = joint_min[ID_EYES_LEFT_BROW];
427
    joint_max[ID_EYES_RIGHT_BROW] = joint_max[ID_EYES_LEFT_BROW];
428

    
429
    //mouth:
430
    joint_min[ID_LIP_CENTER_UPPER] = 5;
431
    joint_max[ID_LIP_CENTER_UPPER] = 50;
432
    joint_min[ID_LIP_CENTER_LOWER] = 5;
433
    joint_max[ID_LIP_CENTER_LOWER] = 50;
434
    joint_min[ID_LIP_LEFT_UPPER] = 5;
435
    joint_max[ID_LIP_LEFT_UPPER] = 50;
436
    joint_min[ID_LIP_LEFT_LOWER] = 5;
437
    joint_max[ID_LIP_LEFT_LOWER] = 50;
438
    joint_min[ID_LIP_RIGHT_UPPER] = 5;
439
    joint_max[ID_LIP_RIGHT_UPPER] = 50;
440
    joint_min[ID_LIP_RIGHT_LOWER] = 5;
441
    joint_max[ID_LIP_RIGHT_LOWER] = 50;
442
}
443

    
444
//! conversion table for humotion joint id to icub joint id
445
//! \param int value for humotion joint id from JointInterface::JOINT_ID_ENUM
446
//! \return int value of icub joint id
447
int iCubJointInterface::convert_humotion_jointid_to_icub(int humotion_id){
448
    enum_id_bimap_t::right_const_iterator it = enum_id_bimap.right.find(humotion_id);
449
    if(it == enum_id_bimap.right.end()) {
450
        //key does not exist
451
        cerr << "ERROR: invalid humotion joint id (" << humotion_id << ") given. can not convert this. exiting\n";
452
        exit(EXIT_FAILURE);
453
    }
454
    return it->second;
455
}
456

    
457

    
458
//! conversion table for icub joint id to humotion joint id
459
//! \param  int value of icub joint id
460
//! \return int value of humotion joint id from JointInterface::JOINT_ID_ENUM
461
int iCubJointInterface::convert_icub_jointid_to_humotion(int icub_id){
462
    enum_id_bimap_t::left_const_iterator it = enum_id_bimap.left.find(icub_id);
463
    if(it == enum_id_bimap.left.end()) {
464
        //key does not exist
465
        cout << "ERROR: invalid icub joint id given. can not convert this. exiting\n";
466
        exit(EXIT_FAILURE);
467
    }
468
    return it->second;
469
}
470