Statistics
| Branch: | Tag: | Revision:

humotion / src / server / eyelid_motion_generator.cpp @ 13a5e223

History | View | Annotate | Download (10.144 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 13a5e223 Simon Schulz
const float EyelidMotionGenerator::EYEBLINK_DURATION_MS    = 100.0;//ms
40 8c6c1163 Simon Schulz
//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
    eyeblink_blocked_timeout = get_system_time() - posix_time::seconds(100);
110
111
    if (duration_left == 0){
112
        //nothing to do
113
    }else if (duration_left < 0){
114
        //infinite sleep -> close
115
        eyelid_closed[LEFT] = true;
116
    }else{
117
        //timeout sleep
118
        start_eyeblink(LEFT, duration_left);
119
        eyelid_closed[LEFT] = false;
120
    }
121
122
    if (duration_right == 0){
123
        //nothing to do
124
    }else if (duration_right < 0){
125
        //infinite sleep -> close
126
        eyelid_closed[RIGHT] = true;
127
    }else{
128
        //timeout sleep
129
        start_eyeblink(RIGHT, duration_right);
130
        eyelid_closed[RIGHT] = false;
131
    }
132
}
133
134
//! process saccadic blink requests
135
//! -> when we do a saccade, the chances to blink are much higher
136
void EyelidMotionGenerator::process_saccadic_eyeblinks(){
137
    if (saccade_blink_requested){
138
        //every n-th's saccade requests an eyeblink
139
        //here: use 0.3 even though humans use bigger prob value (but that looks stupid on robot)
140
        if ((rand()%3) == 0){
141
            ///printf("> saccadic eyeblink:\n");
142
            start_eyeblink(LEFT);
143
            start_eyeblink(RIGHT);
144
        }
145
        saccade_blink_requested = false;
146
    }
147
}
148
149
//! process periodic blink requests
150
//! -> we want to have an eyeblink every n...m seconds
151
void EyelidMotionGenerator::process_periodic_eyeblinks(){
152
    if((eyeblink_active[LEFT]) || eyeblink_active[RIGHT]){
153
        //calculate next timeout for a new periodic eyeblink
154
        int milliseconds_to_next_blink = EYEBLINK_EYERY_MS_MIN + (rand()%(int)(EYEBLINK_EYERY_MS_MAX-EYEBLINK_EYERY_MS_MIN));
155
        periodic_blink_start_time = get_system_time() +   posix_time::milliseconds(milliseconds_to_next_blink);
156
    }
157
158
    if (get_system_time() > periodic_blink_start_time){
159
        ///printf("> periodic eyeblink:\n");
160
        start_eyeblink(LEFT);
161
        start_eyeblink(RIGHT);
162
    }
163
}
164
165
//! handle eyeblink timeouts.
166
//! this function will actually re-open the eyes after a predefined time
167
void EyelidMotionGenerator::handle_eyeblink_timeout(){
168
    boost::system_time now = get_system_time();
169
170
    //take care of re-opening eye timeout:
171
    for(int i=LEFT; i<=RIGHT; i++){
172
        if (eyeblink_active[i]){
173
            if (now >= eyeblink_timeout[i]){
174
                eyeblink_active[i] = false;
175
            }
176
        }
177
    }
178
179
    //take care of blocking time
180
    if (eyeblink_active[LEFT] || eyeblink_active[RIGHT]){
181
        eyeblink_blocked_timeout = get_system_time() + posix_time::milliseconds(EYEBLINK_BLOCKING_TIME);
182
    }
183
184
185
}
186
187
//! override eyelid positions
188
//! this will override (=close) the eyelids for all possible eyeblink requests
189
void EyelidMotionGenerator::override_lids_for_eyeblink(){
190
    //close the requested eyelids
191
    if (eyeblink_active[LEFT] || eyelid_closed[LEFT]){
192
        close_eyelid(JointInterface::ID_EYES_LEFT_LID_UPPER);
193
        close_eyelid(JointInterface::ID_EYES_LEFT_LID_LOWER);
194
    }
195
    if(eyeblink_active[RIGHT] || eyelid_closed[RIGHT]){
196
        close_eyelid(JointInterface::ID_EYES_RIGHT_LID_UPPER);
197
        close_eyelid(JointInterface::ID_EYES_RIGHT_LID_LOWER);
198
    }
199
}
200
201
202
203
//! overwrite the intermediate target to close the given eyelid:
204
void EyelidMotionGenerator::close_eyelid(int joint_id){
205
    float value = 0.0;
206
207
    //set upper to minimal allowed value (=as closed as possible) + a small offset
208
    //set lower to the same value (instead of max) in order to avoid collisions
209
    switch(joint_id){
210
        default:
211
            //no eyelid -> return
212
            return;
213
214
        case(JointInterface::ID_EYES_LEFT_LID_UPPER):
215
        case(JointInterface::ID_EYES_LEFT_LID_LOWER):
216
            //use the upper value + 10 deg as close state:
217
            value = joint_interface->get_joint_min(JointInterface::ID_EYES_LEFT_LID_UPPER) + 10.0;
218
            //overwrite last target_
219
            joint_interface->set_target_position(joint_id, value);
220
            break;
221
222
        case(JointInterface::ID_EYES_RIGHT_LID_UPPER):
223
        case(JointInterface::ID_EYES_RIGHT_LID_LOWER):
224
            //use the upper value + 10 deg as close state:
225
            value = joint_interface->get_joint_min(JointInterface::ID_EYES_RIGHT_LID_UPPER) + 10.0;
226
            //overwrite last target_
227
            joint_interface->set_target_position(joint_id, value);
228
            break;
229
    }
230
}
231
232
//! start an eyeblink request
233
//! this will actually set a flag and a timeout for reopening the given eyelid pair again
234
//! \param int id of side (LEFT or RIGHT)
235
void EyelidMotionGenerator::start_eyeblink(int side, int duration){
236
    if (get_system_time() < eyeblink_blocked_timeout){
237
        //return if we are still in the block time
238
        return;
239
    }
240
    //request for n ms eyeblink:
241
    eyeblink_timeout[side] = get_system_time() + posix_time::milliseconds(duration);
242
243
    eyeblink_active[side] = true;
244
}
245
246
//! check if there is an ongoing saccade:
247
void EyelidMotionGenerator::check_for_saccade(){
248
    if (get_eye_saccade_active()){
249
        //this is a saccade, check if not already in saccade:
250
        if (!saccade_blink_active){
251
            //this is a new saccade! restart blink timer
252
            saccade_blink_requested = true;
253
            saccade_blink_active = true;
254
        }
255
    }else{
256
        saccade_blink_active = false;
257
    }
258
}
259
260
261
//! publish targets to motor boards:
262
void EyelidMotionGenerator::publish_targets(){
263
    //publish values if there is an active gaze input within the last timerange
264
    if (gaze_target_input_active()){
265
        joint_interface->publish_target_position(JointInterface::ID_EYES_LEFT_LID_UPPER);
266
        joint_interface->publish_target_position(JointInterface::ID_EYES_LEFT_LID_LOWER);
267
        joint_interface->publish_target_position(JointInterface::ID_EYES_RIGHT_LID_UPPER);
268
        joint_interface->publish_target_position(JointInterface::ID_EYES_RIGHT_LID_LOWER);
269
    }
270
}