Statistics
| Branch: | Tag: | Revision:

humotion / src / server / eyelid_motion_generator.cpp @ 83864a21

History | View | Annotate | Download (10.764 KB)

1
/*
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 "humotion/server/eyelid_motion_generator.h"
29
#include "humotion/server/server.h"
30

    
31
// using namespace boost;
32
// using namespace std;
33
// using namespace humotion;
34
// using namespace humotion::server;
35

    
36
using humotion::server::EyelidMotionGenerator;
37
using humotion::server::Config;
38

    
39
//! constructor
40
EyelidMotionGenerator::EyelidMotionGenerator(JointInterface *j, Config *cfg) :
41
    EyeMotionGenerator(j, cfg) {
42
    saccade_blink_active_ = false;
43
    saccade_blink_requested_ = false;
44
    eyelid_closed_[LEFT] = false;
45
    eyelid_closed_[RIGHT] = false;
46

    
47
    eyeblink_blocked_timeout_ = boost::get_system_time();
48
}
49

    
50
//! destructor
51
EyelidMotionGenerator::~EyelidMotionGenerator() {
52
}
53

    
54

    
55
//! calculate joint targets
56
//! \TODO: make up motion twice as slow as down motion
57
void EyelidMotionGenerator::calculate_targets() {
58
    // printf("> humotion: calculating eyelid targets\n");
59

    
60
    // fetch current angles
61
    float eye_tilt_now  = get_current_position(JointInterface::ID_EYES_BOTH_UD);
62

    
63
    // calculate left eyelid targets
64
    float eyelid_upper_left_target = eye_tilt_now + requested_gaze_state_.eyelid_opening_upper;
65
    float eyelid_lower_left_target = eye_tilt_now - requested_gaze_state_.eyelid_opening_lower;
66

    
67
    // calculate right eyelid targets
68
    float eyelid_upper_right_target = eye_tilt_now + requested_gaze_state_.eyelid_opening_upper;
69
    float eyelid_lower_right_target = eye_tilt_now - requested_gaze_state_.eyelid_opening_lower;
70

    
71
    // limit target angles
72
    eyelid_upper_left_target  = limit_target(JointInterface::ID_EYES_LEFT_LID_UPPER,
73
                                             eyelid_upper_left_target);
74
    eyelid_lower_left_target  = limit_target(JointInterface::ID_EYES_LEFT_LID_LOWER,
75
                                             eyelid_lower_left_target);
76
    eyelid_upper_right_target = limit_target(JointInterface::ID_EYES_RIGHT_LID_UPPER,
77
                                             eyelid_upper_right_target);
78
    eyelid_lower_right_target = limit_target(JointInterface::ID_EYES_RIGHT_LID_LOWER,
79
                                             eyelid_lower_right_target);
80

    
81
    // (temporarily) store the target
82
    joint_interface_->set_target(JointInterface::ID_EYES_LEFT_LID_UPPER,
83
                                eyelid_upper_left_target, 0.0);
84
    joint_interface_->set_target(JointInterface::ID_EYES_LEFT_LID_LOWER,
85
                                eyelid_lower_left_target, 0.0);
86
    joint_interface_->set_target(JointInterface::ID_EYES_RIGHT_LID_UPPER,
87
                                eyelid_upper_right_target, 0.0);
88
    joint_interface_->set_target(JointInterface::ID_EYES_RIGHT_LID_LOWER,
89
                                eyelid_lower_right_target, 0.0);
90

    
91
    // check for saccade
92
    check_for_saccade();
93

    
94
    // is there a blink request?
95
    start_external_eyeblinks(requested_gaze_state_.eyeblink_request_left,
96
                             requested_gaze_state_.eyeblink_request_right);
97

    
98
    // do we have a saccade & do we want to exec an eyeblink?
99
    process_saccadic_eyeblinks();
100

    
101
    // execute for periodic eyeblinks every ? ms
102
    process_periodic_eyeblinks();
103

    
104
    // close eyes again after a given timeout
105
    handle_eyeblink_timeout();
106

    
107
    // eyeblinks override position target (if necessary)
108
    override_lids_for_eyeblink();
109
}
110

    
111
//! process any external blink requests
112
void EyelidMotionGenerator::start_external_eyeblinks(int duration_left, int duration_right) {
113
    // manual eyeblinks will ALWAYs get executed as we use
114
    // a negative block timeout (=timeout has already passed)
115
    if ((duration_left != 0) || (duration_right != 0)) {
116
        eyeblink_blocked_timeout_ = boost::get_system_time() - boost::posix_time::seconds(100);
117
    }
118

    
119

    
120
    if (duration_left == 0) {
121
        // nothing to do
122
    } else if (duration_left < 0) {
123
        // infinite sleep -> close
124
        eyelid_closed_[LEFT] = true;
125
    } else {
126
        // timeout sleep
127
        start_eyeblink(LEFT, duration_left);
128
        eyelid_closed_[LEFT] = false;
129
    }
130

    
131
    if (duration_right == 0) {
132
        // nothing to do
133
    } else if (duration_right < 0) {
134
        // infinite sleep -> close
135
        eyelid_closed_[RIGHT] = true;
136
    } else {
137
        // timeout sleep
138
        start_eyeblink(RIGHT, duration_right);
139
        eyelid_closed_[RIGHT] = false;
140
    }
141
}
142

    
143
//! process saccadic blink requests
144
//! -> when we do a saccade, the chances to blink are much higher
145
void EyelidMotionGenerator::process_saccadic_eyeblinks() {
146
    if (saccade_blink_requested_) {
147
        // every n-th's saccade requests an eyeblink
148
        float frnd = static_cast <float> (std::rand()) / static_cast <float> (RAND_MAX);
149
        if (frnd <= config->eyeblink_probability_after_saccade) {
150
            printf("> saccadic eyeblink:\n");
151
            start_eyeblink(LEFT, config->eyeblink_duration * 1000.0);
152
            start_eyeblink(RIGHT, config->eyeblink_duration * 1000.0);
153
        }
154
        saccade_blink_requested_ = false;
155
    }
156
}
157

    
158
//! process periodic blink requests
159
//! -> we want to have an eyeblink every n...m seconds
160
void EyelidMotionGenerator::process_periodic_eyeblinks() {
161
    if (eyeblink_active_[LEFT] || eyeblink_active_[RIGHT]) {
162
        float range = config->eyeblink_periodic_distribution_upper
163
                - config->eyeblink_periodic_distribution_lower;
164

    
165
        // random number 0...1
166
        float frnd = static_cast <float> (std::rand()) / static_cast <float> (RAND_MAX);
167

    
168
        // calculate next timeout for a new periodic eyeblink
169
        float seconds_to_next_blink =
170
                config->eyeblink_periodic_distribution_lower  + frnd * range;
171

    
172
        periodic_blink_start_time_ = boost::get_system_time()
173
                + boost::posix_time::seconds(seconds_to_next_blink);
174
    }
175

    
176
    if (boost::get_system_time() > periodic_blink_start_time_) {
177
        // printf("> periodic eyeblink:\n");
178
        start_eyeblink(LEFT, config->eyeblink_duration * 1000.0);
179
        start_eyeblink(RIGHT, config->eyeblink_duration * 1000.0);
180
    }
181
}
182

    
183
//! handle eyeblink timeouts.
184
//! this function will actually re-open the eyes after a predefined time
185
void EyelidMotionGenerator::handle_eyeblink_timeout() {
186
    boost::system_time now = boost::get_system_time();
187

    
188
    // take care of re-opening eye timeout
189
    for (int i=LEFT; i <= RIGHT; i++) {
190
        if (eyeblink_active_[i]) {
191
            if (now >= eyeblink_timeout_[i]) {
192
                eyeblink_active_[i] = false;
193
            }
194
        }
195
    }
196

    
197
    // take care of blocking time
198
    if (eyeblink_active_[LEFT] || eyeblink_active_[RIGHT]) {
199
        eyeblink_blocked_timeout_ = boost::get_system_time()
200
                + boost::posix_time::seconds(config->eyeblink_blocked_time);
201
    }
202
}
203

    
204
//! override eyelid positions
205
//! this will override (=close) the eyelids for all possible eyeblink requests
206
void EyelidMotionGenerator::override_lids_for_eyeblink() {
207
    // close the requested eyelids
208
    if (eyeblink_active_[LEFT] || eyelid_closed_[LEFT]) {
209
        close_eyelid(JointInterface::ID_EYES_LEFT_LID_UPPER);
210
        close_eyelid(JointInterface::ID_EYES_LEFT_LID_LOWER);
211
    }
212
    if (eyeblink_active_[RIGHT] || eyelid_closed_[RIGHT]) {
213
        close_eyelid(JointInterface::ID_EYES_RIGHT_LID_UPPER);
214
        close_eyelid(JointInterface::ID_EYES_RIGHT_LID_LOWER);
215
    }
216
}
217

    
218

    
219

    
220
//! overwrite the intermediate target to close the given eyelid:
221
void EyelidMotionGenerator::close_eyelid(int joint_id) {
222
    float value = 0.0;
223

    
224
    // set upper to minimal allowed value (=as closed as possible) + a small offset
225
    // set lower to the same value (instead of max) in order to avoid collisions
226
    switch (joint_id) {
227
    default:
228
        // no eyelid -> return
229
        return;
230

    
231
    case(JointInterface::ID_EYES_LEFT_LID_UPPER):
232
    case(JointInterface::ID_EYES_LEFT_LID_LOWER):
233
        // use the upper value + 10 deg as close state:
234
        value = joint_interface_->get_joint_min(JointInterface::ID_EYES_LEFT_LID_UPPER) + 10.0;
235
        // overwrite last target_
236
        joint_interface_->set_target(joint_id, value, 0.0);
237
        break;
238

    
239
    case(JointInterface::ID_EYES_RIGHT_LID_UPPER):
240
    case(JointInterface::ID_EYES_RIGHT_LID_LOWER):
241
        // use the upper value + 10 deg as close state:
242
        value = joint_interface_->get_joint_min(JointInterface::ID_EYES_RIGHT_LID_UPPER) + 10.0;
243
        // overwrite last target_
244
        joint_interface_->set_target(joint_id, value, 0.0);
245
        break;
246
    }
247
}
248

    
249
//! start an eyeblink request
250
//! this will actually set a flag and a timeout for reopening the given eyelid pair again
251
//! \param int id of side (LEFT or RIGHT)
252
void EyelidMotionGenerator::start_eyeblink(int side, int duration) {
253
    // cout << "BLOCKED UNTIL " << eyeblink_blocked_timeout << "\n";
254
    if (boost::get_system_time() < eyeblink_blocked_timeout_) {
255
        // return if we are still in the block time
256
        return;
257
    }
258
    // request for n ms eyeblink:
259
    eyeblink_timeout_[side] = boost::get_system_time() + boost::posix_time::milliseconds(duration);
260

    
261
    eyeblink_active_[side] = true;
262
}
263

    
264
//! check if there is an ongoing saccade:
265
void EyelidMotionGenerator::check_for_saccade() {
266
    if (get_eye_saccade_active()) {
267
        // this is a saccade, check if not already in saccade:
268
        if (!saccade_blink_active_) {
269
            // this is a new saccade! restart blink timer
270
            saccade_blink_requested_ = true;
271
            saccade_blink_active_ = true;
272
        }
273
    } else {
274
        saccade_blink_active_ = false;
275
    }
276
}
277

    
278

    
279
//! publish targets to motor boards:
280
void EyelidMotionGenerator::publish_targets() {
281
    // publish values if there is an active gaze input within the last timerange
282
    if (gaze_target_input_active()) {
283
        joint_interface_->publish_target(JointInterface::ID_EYES_LEFT_LID_UPPER);
284
        joint_interface_->publish_target(JointInterface::ID_EYES_LEFT_LID_LOWER);
285
        joint_interface_->publish_target(JointInterface::ID_EYES_RIGHT_LID_UPPER);
286
        joint_interface_->publish_target(JointInterface::ID_EYES_RIGHT_LID_LOWER);
287
    }
288
}