Statistics
| Branch: | Tag: | Revision:

humotion / src / server / eyelid_motion_generator.cpp @ 9c7ef5fc

History | View | Annotate | Download (10.202 KB)

1 8c6c1163 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 "server/eyelid_motion_generator.h"
29
#include "server/server.h"
30
31
using namespace boost;
32
using namespace std;
33
using namespace humotion;
34
using namespace humotion::server;
35
36
//saccade detection threshold in deg/s
37
const float EyelidMotionGenerator::SACCADE_SPEED_THRESHOLD = 15.0; //deg/s
38
//eyeblink duration:
39
const float EyelidMotionGenerator::EYEBLINK_DURATION_MS    = 150.0;//ms
40
//periodic eyeblinks: every 2..10s:
41
const float EyelidMotionGenerator::EYEBLINK_EYERY_MS_MIN =  2000.0; //ms
42
const float EyelidMotionGenerator::EYEBLINK_EYERY_MS_MAX = 10000.0; //ms
43
//how many seconds do we block further blinks after each eyeblink?
44
const float EyelidMotionGenerator::EYEBLINK_BLOCKING_TIME = 1000.0; //ms
45
46
//! constructor
47
EyelidMotionGenerator::EyelidMotionGenerator(JointInterface *j) : EyeMotionGenerator(j){
48
    saccade_blink_active = false;
49
    saccade_blink_requested = false;
50
    eyelid_closed[LEFT] = false;
51
    eyelid_closed[RIGHT] = false;
52
}
53
54
//! destructor
55
EyelidMotionGenerator::~EyelidMotionGenerator(){
56
}
57
58
59
//! calculate joint targets
60
//! \TODO: make up motion twice as slow as down motion
61
void EyelidMotionGenerator::calculate_targets(){
62
    //printf("> humotion: calculating eyelid targets\n");
63
64
    //fetch current angles:
65
    float eye_tilt_now  = get_current_position(JointInterface::ID_EYES_BOTH_UD);
66
67
    //calculate left eyelid targets:
68
    float eyelid_upper_left_target = eye_tilt_now + requested_gaze_state.eyelid_opening_upper;
69
    float eyelid_lower_left_target = eye_tilt_now - requested_gaze_state.eyelid_opening_lower;
70
71
    //calculate right eyelid targets:
72
    float eyelid_upper_right_target = eye_tilt_now + requested_gaze_state.eyelid_opening_upper;
73
    float eyelid_lower_right_target = eye_tilt_now - requested_gaze_state.eyelid_opening_lower;
74
75
    //limit target angles:
76
    eyelid_upper_left_target  = limit_target(JointInterface::ID_EYES_LEFT_LID_UPPER,  eyelid_upper_left_target);
77
    eyelid_lower_left_target  = limit_target(JointInterface::ID_EYES_LEFT_LID_LOWER,  eyelid_lower_left_target);
78
    eyelid_upper_right_target = limit_target(JointInterface::ID_EYES_RIGHT_LID_UPPER,  eyelid_upper_right_target);
79
    eyelid_lower_right_target = limit_target(JointInterface::ID_EYES_RIGHT_LID_LOWER,  eyelid_lower_right_target);
80
81
    //(temporarily) store the target
82
    joint_interface->set_target_position(JointInterface::ID_EYES_LEFT_LID_UPPER,  eyelid_upper_left_target);
83
    joint_interface->set_target_position(JointInterface::ID_EYES_LEFT_LID_LOWER,  eyelid_lower_left_target);
84
    joint_interface->set_target_position(JointInterface::ID_EYES_RIGHT_LID_UPPER,  eyelid_upper_right_target);
85
    joint_interface->set_target_position(JointInterface::ID_EYES_RIGHT_LID_LOWER,  eyelid_lower_right_target);
86
87
    //check for saccade
88
    check_for_saccade();
89
90
    //is there a blink request?
91
    start_external_eyeblinks(requested_gaze_state.eyeblink_request_left, requested_gaze_state.eyeblink_request_right);
92
93
    //do we have a saccade & do we want to exec an eyeblink?
94
    process_saccadic_eyeblinks();
95
96
    //execute for periodic eyeblinks every ? ms
97
    process_periodic_eyeblinks();
98
99
    //close eyes again after a given timeout
100
    handle_eyeblink_timeout();
101
102
    //eyeblinks override position target (if necessary)
103
    override_lids_for_eyeblink();    
104
}
105
106
//! process any external blink requests
107
void EyelidMotionGenerator::start_external_eyeblinks(int duration_left, int duration_right){
108
    //manual eyeblinks will ALWAYs get executed as we use a negative block timeout (=timout has already passed)
109 9c7ef5fc Simon Schulz
    if ((duration_left != 0) || (duration_right != 0)){
110
        eyeblink_blocked_timeout = get_system_time() - posix_time::seconds(100);
111
    }
112
113 8c6c1163 Simon Schulz
114
    if (duration_left == 0){
115
        //nothing to do
116
    }else if (duration_left < 0){
117
        //infinite sleep -> close
118
        eyelid_closed[LEFT] = true;
119
    }else{
120
        //timeout sleep
121
        start_eyeblink(LEFT, duration_left);
122
        eyelid_closed[LEFT] = false;
123
    }
124
125
    if (duration_right == 0){
126
        //nothing to do
127
    }else if (duration_right < 0){
128
        //infinite sleep -> close
129
        eyelid_closed[RIGHT] = true;
130
    }else{
131
        //timeout sleep
132
        start_eyeblink(RIGHT, duration_right);
133
        eyelid_closed[RIGHT] = false;
134
    }
135
}
136
137
//! process saccadic blink requests
138
//! -> when we do a saccade, the chances to blink are much higher
139
void EyelidMotionGenerator::process_saccadic_eyeblinks(){
140
    if (saccade_blink_requested){
141
        //every n-th's saccade requests an eyeblink
142
        //here: use 0.3 even though humans use bigger prob value (but that looks stupid on robot)
143
        if ((rand()%3) == 0){
144
            ///printf("> saccadic eyeblink:\n");
145
            start_eyeblink(LEFT);
146
            start_eyeblink(RIGHT);
147
        }
148
        saccade_blink_requested = false;
149
    }
150
}
151
152
//! process periodic blink requests
153
//! -> we want to have an eyeblink every n...m seconds
154
void EyelidMotionGenerator::process_periodic_eyeblinks(){
155
    if((eyeblink_active[LEFT]) || eyeblink_active[RIGHT]){
156
        //calculate next timeout for a new periodic eyeblink
157
        int milliseconds_to_next_blink = EYEBLINK_EYERY_MS_MIN + (rand()%(int)(EYEBLINK_EYERY_MS_MAX-EYEBLINK_EYERY_MS_MIN));
158
        periodic_blink_start_time = get_system_time() +   posix_time::milliseconds(milliseconds_to_next_blink);
159
    }
160
161
    if (get_system_time() > periodic_blink_start_time){
162
        ///printf("> periodic eyeblink:\n");
163
        start_eyeblink(LEFT);
164
        start_eyeblink(RIGHT);
165
    }
166
}
167
168
//! handle eyeblink timeouts.
169
//! this function will actually re-open the eyes after a predefined time
170
void EyelidMotionGenerator::handle_eyeblink_timeout(){
171
    boost::system_time now = get_system_time();
172
173
    //take care of re-opening eye timeout:
174
    for(int i=LEFT; i<=RIGHT; i++){
175
        if (eyeblink_active[i]){
176
            if (now >= eyeblink_timeout[i]){
177
                eyeblink_active[i] = false;
178
            }
179
        }
180
    }
181
182
    //take care of blocking time
183
    if (eyeblink_active[LEFT] || eyeblink_active[RIGHT]){
184
        eyeblink_blocked_timeout = get_system_time() + posix_time::milliseconds(EYEBLINK_BLOCKING_TIME);
185
    }
186
187
188
}
189
190
//! override eyelid positions
191
//! this will override (=close) the eyelids for all possible eyeblink requests
192
void EyelidMotionGenerator::override_lids_for_eyeblink(){
193
    //close the requested eyelids
194
    if (eyeblink_active[LEFT] || eyelid_closed[LEFT]){
195
        close_eyelid(JointInterface::ID_EYES_LEFT_LID_UPPER);
196
        close_eyelid(JointInterface::ID_EYES_LEFT_LID_LOWER);
197
    }
198
    if(eyeblink_active[RIGHT] || eyelid_closed[RIGHT]){
199
        close_eyelid(JointInterface::ID_EYES_RIGHT_LID_UPPER);
200
        close_eyelid(JointInterface::ID_EYES_RIGHT_LID_LOWER);
201
    }
202
}
203
204
205
206
//! overwrite the intermediate target to close the given eyelid:
207
void EyelidMotionGenerator::close_eyelid(int joint_id){
208
    float value = 0.0;
209
210
    //set upper to minimal allowed value (=as closed as possible) + a small offset
211
    //set lower to the same value (instead of max) in order to avoid collisions
212
    switch(joint_id){
213
        default:
214
            //no eyelid -> return
215
            return;
216
217
        case(JointInterface::ID_EYES_LEFT_LID_UPPER):
218
        case(JointInterface::ID_EYES_LEFT_LID_LOWER):
219
            //use the upper value + 10 deg as close state:
220
            value = joint_interface->get_joint_min(JointInterface::ID_EYES_LEFT_LID_UPPER) + 10.0;
221
            //overwrite last target_
222
            joint_interface->set_target_position(joint_id, value);
223
            break;
224
225
        case(JointInterface::ID_EYES_RIGHT_LID_UPPER):
226
        case(JointInterface::ID_EYES_RIGHT_LID_LOWER):
227
            //use the upper value + 10 deg as close state:
228
            value = joint_interface->get_joint_min(JointInterface::ID_EYES_RIGHT_LID_UPPER) + 10.0;
229
            //overwrite last target_
230
            joint_interface->set_target_position(joint_id, value);
231
            break;
232
    }
233
}
234
235
//! start an eyeblink request
236
//! this will actually set a flag and a timeout for reopening the given eyelid pair again
237
//! \param int id of side (LEFT or RIGHT)
238
void EyelidMotionGenerator::start_eyeblink(int side, int duration){
239
    if (get_system_time() < eyeblink_blocked_timeout){
240
        //return if we are still in the block time
241
        return;
242
    }
243
    //request for n ms eyeblink:
244
    eyeblink_timeout[side] = get_system_time() + posix_time::milliseconds(duration);
245
246
    eyeblink_active[side] = true;
247
}
248
249
//! check if there is an ongoing saccade:
250
void EyelidMotionGenerator::check_for_saccade(){
251
    if (get_eye_saccade_active()){
252
        //this is a saccade, check if not already in saccade:
253
        if (!saccade_blink_active){
254
            //this is a new saccade! restart blink timer
255
            saccade_blink_requested = true;
256
            saccade_blink_active = true;
257
        }
258
    }else{
259
        saccade_blink_active = false;
260
    }
261
}
262
263
264
//! publish targets to motor boards:
265
void EyelidMotionGenerator::publish_targets(){
266
    //publish values if there is an active gaze input within the last timerange
267
    if (gaze_target_input_active()){
268
        joint_interface->publish_target_position(JointInterface::ID_EYES_LEFT_LID_UPPER);
269
        joint_interface->publish_target_position(JointInterface::ID_EYES_LEFT_LID_LOWER);
270
        joint_interface->publish_target_position(JointInterface::ID_EYES_RIGHT_LID_UPPER);
271
        joint_interface->publish_target_position(JointInterface::ID_EYES_RIGHT_LID_LOWER);
272
    }
273
}