Statistics
| Branch: | Tag: | Revision:

humotion / examples / yarp_icub / src / icub_jointinterface.cpp @ 372eec67

History | View | Annotate | Download (16.146 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
}
55

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

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

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

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

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

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

    
109
}
110

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

    
119

    
120

    
121

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

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

    
141

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

    
146
        cout << "LR " << target_position_left << " " << target_position_right <<
147
                " PAN " << target_position_pan << " VERGENCE " << target_position_vergence << "\n";
148

    
149
        // calculate target velocities
150
        // for now just use the same velocity for pan and vergence
151
        float target_velocity_pan = (target_velocity_left + target_velocity_right) / 2.0;
152
        float target_velocity_tilt = target_velocity_pan;
153

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

    
166

    
167
//! set the target data for a given icub joint
168
//! \param id of joint
169
//! \param float value of position
170
void iCubJointInterface::store_icub_joint_target(int icub_id, float position, float velocity) {
171
    printf("> set icub joint %d -> p = %f, v = %f\n",icub_id,position,velocity);
172
    if (icub_id == ICUB_ID_NECK_PAN) {
173
        // icub uses an inverted neck pan specification
174
        position = -position;
175
        velocity = -velocity;
176
    }
177
    target_angle_[icub_id] = position;
178
    target_velocity_[icub_id] = velocity;
179
}
180

    
181
//! execute a move in velocity mode
182
//! \param id of joint
183
//! \param angle
184
void iCubJointInterface::set_target_in_velocitymode(int icub_id) {
185
    // fetch humotion id from icub joint id
186
    int humotion_id = convert_icub_jointid_to_humotion(icub_id);
187

    
188
    // fetch the target velocity
189
    float target_velocity = target_velocity_[icub_id];
190

    
191
    float vmax = 150.0;
192
    if (target_velocity > vmax)  target_velocity = vmax;
193
    if (target_velocity < -vmax) target_velocity = -vmax;
194

    
195
    //execute:
196
    //ivel->velocityMove(id, speed);
197
    /*if ((icub_id != ICUB_ID_NECK_PAN)  &&
198
        (icub_id != ICUB_ID_EYES_BOTH_UD) &&
199
        (icub_id != ICUB_ID_NECK_TILT) &&
200
        (icub_id != ICUB_ID_EYES_BOTH_UD) &&
201
        (icub_id != ICUB_ID_NECK_TILT)) {
202
        // limit to some joints for debugging...
203
        return;
204
    }*/
205

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

    
211
    // first: fetch the timstamp of the last known position
212
    humotion::Timestamp data_ts = get_ts_position(humotion_id).get_last_timestamp();
213

    
214
    // calculate position error:
215
    float position_error = target_angle_[icub_id] - get_ts_position(humotion_id).get_interpolated_value(data_ts);
216

    
217
    // calculate d term
218
    float error_d = (position_error - pv_mix_last_error_[icub_id]) / (framerate*1000.0);
219
    pv_mix_last_error_[icub_id] = position_error;
220

    
221
    // finally do a PD loop to get the target velocity
222
    float pv_mix_velocity = pv_mix_pid_p_[icub_id] * position_error
223
                            + pv_mix_pid_p_[icub_id]*error_d
224
                            + target_velocity;
225

    
226
    printf("%f %f %f %f %f %f PID%d\n",
227
           get_ts_position(humotion_id).get_interpolated_value(data_ts),
228
           target_angle_[icub_id],
229
           123.4, //NOT USED ANYMORE//get_ts_speed(humotion_id).get_interpolated_value(data_ts),
230
           pv_mix_velocity,
231
           target_velocity,
232
           position_error,
233
           icub_id
234
           );
235

    
236
    // execute velocity move
237
    yarp_ivel_->velocityMove(icub_id, pv_mix_velocity);
238
}
239

    
240
//! actually execute the scheduled motion commands
241
void iCubJointInterface::execute_motion(){
242

    
243
    // set up neck and eye motion commands in velocitymode
244
    for(int i=ICUB_ID_NECK_TILT; i<=ICUB_ID_EYES_VERGENCE; i++){
245
        set_target_in_velocitymode(i);
246
    }
247

    
248
    // eyelids: unfortuantely the icub has only 1dof for eyelids
249
    // therefore we can only use an opening value
250
    float opening_left  = target_angle_[ICUB_ID_EYES_LEFT_LID_UPPER]
251
                          - target_angle_[ICUB_ID_EYES_LEFT_LID_LOWER];
252
    float opening_right = target_angle_[ICUB_ID_EYES_RIGHT_LID_UPPER]
253
                          - target_angle_[ICUB_ID_EYES_RIGHT_LID_LOWER];
254
    float opening = (opening_left + opening_right) / 2.0;
255
    // send it to icub face if
256
    face_interface_->set_eyelid_angle(opening);
257

    
258
    //eyebrows are set using a special command as well:
259
    face_interface_->set_eyebrow_angle(ICUB_ID_EYES_LEFT_BROW, target_angle_);
260
    face_interface_->set_eyebrow_angle(ICUB_ID_EYES_RIGHT_BROW, target_angle_);
261

    
262
    //mouth
263
    face_interface_->set_mouth(target_angle_);
264

    
265

    
266
    //store joint values which we do not handle on icub here:
267
    double timestamp = humotion::Timestamp::now().to_seconds();
268
    JointInterface::store_incoming_position(ID_LIP_LEFT_UPPER,   target_angle_[ICUB_ID_LIP_LEFT_UPPER], timestamp);
269
    JointInterface::store_incoming_position(ID_LIP_LEFT_LOWER,   target_angle_[ICUB_ID_LIP_LEFT_LOWER], timestamp);
270
    JointInterface::store_incoming_position(ID_LIP_CENTER_UPPER, target_angle_[ICUB_ID_LIP_CENTER_UPPER], timestamp);
271
    JointInterface::store_incoming_position(ID_LIP_CENTER_LOWER, target_angle_[ICUB_ID_LIP_CENTER_LOWER], timestamp);
272
    JointInterface::store_incoming_position(ID_LIP_RIGHT_UPPER,  target_angle_[ICUB_ID_LIP_RIGHT_UPPER], timestamp);
273
    JointInterface::store_incoming_position(ID_LIP_RIGHT_LOWER,  target_angle_[ICUB_ID_LIP_RIGHT_LOWER], timestamp);
274
}
275

    
276

    
277
void iCubJointInterface::set_joint_enable_state(int humotion_id, bool enable) {
278
    int icub_jointid = convert_humotion_jointid_to_icub(humotion_id);
279
/*
280
    switch(e){
281
        default:
282
            break;
283

284
    case(ID_NECK_PAN):
285
        icub_jointid = ICUB_ID_NECK_PAN;
286
        break;
287

288
    case(ID_NECK_TILT):
289
        icub_jointid = ICUB_ID_NECK_TILT;
290
        break;
291

292
    case(ID_NECK_ROLL):
293
        icub_jointid = ICUB_ID_NECK_ROLL;
294
        break;
295

296
    case(ID_EYES_BOTH_UD):
297
        icub_jointid = ICUB_ID_EYES_BOTH_UD;
298
        break;
299

300
    // icub handles eyes as pan angle + vergence...
301
    // -> hack: left eye enables pan and right eye enables vergence
302
    case(ID_EYES_LEFT_LR):
303
        icub_jointid = ICUB_ID_EYES_PAN;
304
        break;
305

306
    case(ID_EYES_RIGHT_LR):
307
        icub_jointid = ICUB_ID_EYES_VERGENCE;
308
        break;
309
    }
310
    */
311

    
312
    if ((icub_jointid != -1) && (icub_jointid <= ICUB_ID_EYES_VERGENCE)) {
313
        if (enable) {
314
            yarp_amp_->enableAmp(icub_jointid);
315
            yarp_pid_->enablePid(icub_jointid);
316
        } else {
317
            yarp_pid_->disablePid(icub_jointid);
318
            yarp_amp_->disableAmp(icub_jointid);
319
        }
320
    }
321
}
322

    
323
//! prepare and enable a joint
324
//! NOTE: this should also prefill the min/max positions for this joint
325
//! \param the enum id of a joint
326
void iCubJointInterface::enable_joint(int e){
327
    set_joint_enable_state(e, true);
328
}
329

    
330
//! shutdown and disable a joint
331
//! \param the enum id of a joint
332
void iCubJointInterface::disable_joint(int e){
333
    set_joint_enable_state(e, false);
334
}
335

    
336
void iCubJointInterface::store_min_max(yarp::dev::IControlLimits *ilimits, int icub_id){
337
    double min, max;
338
    int humotion_id = convert_icub_jointid_to_humotion(icub_id);
339

    
340
    ilimits->getLimits(icub_id, &min, &max);
341
    joint_min[humotion_id] = min;
342
    joint_max[humotion_id] = max;
343
}
344

    
345
//! initialise a joint (set up controller mode etc)
346
void iCubJointInterface::init_joints(){
347
    store_min_max(yarp_ilimits_, ICUB_ID_NECK_TILT);
348
    store_min_max(yarp_ilimits_, ICUB_ID_NECK_ROLL);
349
    store_min_max(yarp_ilimits_, ICUB_ID_NECK_PAN);
350
    store_min_max(yarp_ilimits_, ICUB_ID_EYES_BOTH_UD);
351

    
352
    //icub handles eyes differently, we have to set pan angle + vergence
353
    double pan_min, pan_max, vergence_min, vergence_max;
354
    yarp_ilimits_->getLimits(ICUB_ID_EYES_PAN, &pan_min, &pan_max);
355
    yarp_ilimits_->getLimits(ICUB_ID_EYES_VERGENCE, &vergence_min, &vergence_max);
356

    
357
    //this is not 100% correct, should be fixed:
358
    joint_min[ID_EYES_LEFT_LR] = pan_min; // - vergence_max/2;
359
    joint_max[ID_EYES_LEFT_LR] = pan_max; // - vergence_max/2;
360
    joint_min[ID_EYES_RIGHT_LR] = joint_min[ID_EYES_LEFT_LR];
361
    joint_max[ID_EYES_RIGHT_LR] = joint_max[ID_EYES_LEFT_LR];
362

    
363
    //eyelids:
364
    joint_min[ID_EYES_RIGHT_LID_UPPER] = -50; //24-30;
365
    joint_max[ID_EYES_RIGHT_LID_UPPER] = 50; //48-30;
366
    //lid_angle = joint_max[ID_EYES_RIGHT_LID_UPPER];
367

    
368
    //eyebrows:
369
    joint_min[ID_EYES_LEFT_BROW] = -50;
370
    joint_max[ID_EYES_LEFT_BROW] = 50;
371
    joint_min[ID_EYES_RIGHT_BROW] = joint_min[ID_EYES_LEFT_BROW];
372
    joint_max[ID_EYES_RIGHT_BROW] = joint_max[ID_EYES_LEFT_BROW];
373

    
374
    //mouth:
375
    joint_min[ID_LIP_CENTER_UPPER] = 5;
376
    joint_max[ID_LIP_CENTER_UPPER] = 50;
377
    joint_min[ID_LIP_CENTER_LOWER] = 5;
378
    joint_max[ID_LIP_CENTER_LOWER] = 50;
379
    joint_min[ID_LIP_LEFT_UPPER] = 5;
380
    joint_max[ID_LIP_LEFT_UPPER] = 50;
381
    joint_min[ID_LIP_LEFT_LOWER] = 5;
382
    joint_max[ID_LIP_LEFT_LOWER] = 50;
383
    joint_min[ID_LIP_RIGHT_UPPER] = 5;
384
    joint_max[ID_LIP_RIGHT_UPPER] = 50;
385
    joint_min[ID_LIP_RIGHT_LOWER] = 5;
386
    joint_max[ID_LIP_RIGHT_LOWER] = 50;
387
}
388

    
389
//! conversion table for humotion joint id to icub joint id
390
//! \param int value for humotion joint id from JointInterface::JOINT_ID_ENUM
391
//! \return int value of icub joint id
392
int iCubJointInterface::convert_humotion_jointid_to_icub(int humotion_id){
393
    enum_id_bimap_t::right_const_iterator it = enum_id_bimap.right.find(humotion_id);
394
    if(it == enum_id_bimap.right.end()) {
395
        //key does not exist
396
        cerr << "ERROR: invalid humotion joint id (" << humotion_id << ") given. can not convert this. exiting\n";
397
        exit(EXIT_FAILURE);
398
    }
399
    return it->second;
400
}
401

    
402

    
403
//! conversion table for icub joint id to humotion joint id
404
//! \param  int value of icub joint id
405
//! \return int value of humotion joint id from JointInterface::JOINT_ID_ENUM
406
int iCubJointInterface::convert_icub_jointid_to_humotion(int icub_id){
407
    enum_id_bimap_t::left_const_iterator it = enum_id_bimap.left.find(icub_id);
408
    if(it == enum_id_bimap.left.end()) {
409
        //key does not exist
410
        cout << "ERROR: invalid icub joint id given. can not convert this. exiting\n";
411
        exit(EXIT_FAILURE);
412
    }
413
    return it->second;
414
}
415