Statistics
| Branch: | Tag: | Revision:

humotion / src / server / eyelid_motion_generator.cpp @ 6de0fc2e

History | View | Annotate | Download (11.086 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

    
45
    for (int i = 0; i < 2; i++) {
46
        eyelid_closed_[i] = false;
47
        eyeblink_active_[i] = false;
48
        eyeblink_timeout_[i] = boost::get_system_time();
49
    }
50

    
51
    eyeblink_blocked_timeout_ = boost::get_system_time();
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
    float eyelid_target = 0.0;
68
    if (config->eyelids_follow_eyemotion) {
69
        // the eyelids shoudl follow the eye motion
70
        // therefore copy the eyeballs tilt position
71
        eyelid_target = eye_tilt_now;
72
    }
73

    
74
    // calculate left eyelid targets
75
    float eyelid_upper_left_target = eyelid_target + requested_gaze_state_.eyelid_opening_upper;
76
    float eyelid_lower_left_target = eyelid_target - requested_gaze_state_.eyelid_opening_lower;
77

    
78
    // calculate right eyelid targets
79
    float eyelid_upper_right_target = eyelid_target + requested_gaze_state_.eyelid_opening_upper;
80
    float eyelid_lower_right_target = eyelid_target - requested_gaze_state_.eyelid_opening_lower;
81

    
82
    // limit target angles
83
    eyelid_upper_left_target  = limit_target(JointInterface::ID_EYES_LEFT_LID_UPPER,
84
                                             eyelid_upper_left_target);
85
    eyelid_lower_left_target  = limit_target(JointInterface::ID_EYES_LEFT_LID_LOWER,
86
                                             eyelid_lower_left_target);
87
    eyelid_upper_right_target = limit_target(JointInterface::ID_EYES_RIGHT_LID_UPPER,
88
                                             eyelid_upper_right_target);
89
    eyelid_lower_right_target = limit_target(JointInterface::ID_EYES_RIGHT_LID_LOWER,
90
                                             eyelid_lower_right_target);
91

    
92
    // (temporarily) store the target
93
    joint_interface_->set_target(JointInterface::ID_EYES_LEFT_LID_UPPER,
94
                                eyelid_upper_left_target, 0.0);
95
    joint_interface_->set_target(JointInterface::ID_EYES_LEFT_LID_LOWER,
96
                                eyelid_lower_left_target, 0.0);
97
    joint_interface_->set_target(JointInterface::ID_EYES_RIGHT_LID_UPPER,
98
                                eyelid_upper_right_target, 0.0);
99
    joint_interface_->set_target(JointInterface::ID_EYES_RIGHT_LID_LOWER,
100
                                eyelid_lower_right_target, 0.0);
101

    
102
    // check for saccade
103
    check_for_saccade();
104

    
105
    // is there a blink request?
106
    start_external_eyeblinks(requested_gaze_state_.eyeblink_request_left,
107
                             requested_gaze_state_.eyeblink_request_right);
108

    
109
    // do we have a saccade & do we want to exec an eyeblink?
110
    process_saccadic_eyeblinks();
111

    
112
    // execute for periodic eyeblinks every ? ms
113
    process_periodic_eyeblinks();
114

    
115
    // close eyes again after a given timeout
116
    handle_eyeblink_timeout();
117

    
118
    // eyeblinks override position target (if necessary)
119
    override_lids_for_eyeblink();
120
}
121

    
122
//! process any external blink requests
123
void EyelidMotionGenerator::start_external_eyeblinks(int duration_left, int duration_right) {
124
    // manual eyeblinks will ALWAYs get executed as we use
125
    // a negative block timeout (=timeout has already passed)
126
    if ((duration_left != 0) || (duration_right != 0)) {
127
        eyeblink_blocked_timeout_ = boost::get_system_time() - boost::posix_time::seconds(100);
128
    }
129

    
130

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

    
142
    if (duration_right == 0) {
143
        // nothing to do
144
    } else if (duration_right < 0) {
145
        // infinite sleep -> close
146
        eyelid_closed_[RIGHT] = true;
147
    } else {
148
        // timeout sleep
149
        start_eyeblink(RIGHT, duration_right);
150
        eyelid_closed_[RIGHT] = false;
151
    }
152
}
153

    
154
//! process saccadic blink requests
155
//! -> when we do a saccade, the chances to blink are much higher
156
void EyelidMotionGenerator::process_saccadic_eyeblinks() {
157
    if (saccade_blink_requested_) {
158
        // every n-th's saccade requests an eyeblink
159
        float frnd = static_cast <float> (std::rand()) / static_cast <float> (RAND_MAX);
160
        if (frnd <= config->eyeblink_probability_after_saccade) {
161
            printf("> saccadic eyeblink:\n");
162
            start_eyeblink(LEFT, config->eyeblink_duration * 1000.0);
163
            start_eyeblink(RIGHT, config->eyeblink_duration * 1000.0);
164
        }
165
        saccade_blink_requested_ = false;
166
    }
167
}
168

    
169
//! process periodic blink requests
170
//! -> we want to have an eyeblink every n...m seconds
171
void EyelidMotionGenerator::process_periodic_eyeblinks() {
172
    if (eyeblink_active_[LEFT] || eyeblink_active_[RIGHT]) {
173
        float range = config->eyeblink_periodic_distribution_upper
174
                - config->eyeblink_periodic_distribution_lower;
175

    
176
        // random number 0...1
177
        float frnd = static_cast <float> (std::rand()) / static_cast <float> (RAND_MAX);
178

    
179
        // calculate next timeout for a new periodic eyeblink
180
        float seconds_to_next_blink =
181
                config->eyeblink_periodic_distribution_lower  + frnd * range;
182

    
183
        periodic_blink_start_time_ = boost::get_system_time()
184
                + boost::posix_time::seconds(seconds_to_next_blink);
185
    }
186

    
187
    if (boost::get_system_time() > periodic_blink_start_time_) {
188
        // printf("> periodic eyeblink:\n");
189
        start_eyeblink(LEFT, config->eyeblink_duration * 1000.0);
190
        start_eyeblink(RIGHT, config->eyeblink_duration * 1000.0);
191
    }
192
}
193

    
194
//! handle eyeblink timeouts.
195
//! this function will actually re-open the eyes after a predefined time
196
void EyelidMotionGenerator::handle_eyeblink_timeout() {
197
    boost::system_time now = boost::get_system_time();
198

    
199
    // take care of re-opening eye timeout
200
    for (int i=LEFT; i <= RIGHT; i++) {
201
        if (eyeblink_active_[i]) {
202
            if (now >= eyeblink_timeout_[i]) {
203
                eyeblink_active_[i] = false;
204
            }
205
        }
206
    }
207

    
208
    // take care of blocking time
209
    if (eyeblink_active_[LEFT] || eyeblink_active_[RIGHT]) {
210
        eyeblink_blocked_timeout_ = boost::get_system_time()
211
                + boost::posix_time::seconds(config->eyeblink_blocked_time);
212
    }
213
}
214

    
215
//! override eyelid positions
216
//! this will override (=close) the eyelids for all possible eyeblink requests
217
void EyelidMotionGenerator::override_lids_for_eyeblink() {
218
    // close the requested eyelids
219
    if (eyeblink_active_[LEFT] || eyelid_closed_[LEFT]) {
220
        close_eyelid(JointInterface::ID_EYES_LEFT_LID_UPPER);
221
        close_eyelid(JointInterface::ID_EYES_LEFT_LID_LOWER);
222
    }
223
    if (eyeblink_active_[RIGHT] || eyelid_closed_[RIGHT]) {
224
        close_eyelid(JointInterface::ID_EYES_RIGHT_LID_UPPER);
225
        close_eyelid(JointInterface::ID_EYES_RIGHT_LID_LOWER);
226
    }
227
}
228

    
229

    
230

    
231
//! overwrite the intermediate target to close the given eyelid:
232
void EyelidMotionGenerator::close_eyelid(int joint_id) {
233
    float value = 0.0;
234

    
235
    // set upper to minimal allowed value (=as closed as possible) + a small offset
236
    // set lower to the same value (instead of max) in order to avoid collisions
237
    switch (joint_id) {
238
    default:
239
        // no eyelid -> return
240
        return;
241

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

    
250
    case(JointInterface::ID_EYES_RIGHT_LID_UPPER):
251
    case(JointInterface::ID_EYES_RIGHT_LID_LOWER):
252
        // use the upper value + 10 deg as close state:
253
        value = joint_interface_->get_joint_min(JointInterface::ID_EYES_RIGHT_LID_UPPER) + 10.0;
254
        // overwrite last target_
255
        joint_interface_->set_target(joint_id, value, 0.0);
256
        break;
257
    }
258
}
259

    
260
//! start an eyeblink request
261
//! this will actually set a flag and a timeout for reopening the given eyelid pair again
262
//! \param int id of side (LEFT or RIGHT)
263
void EyelidMotionGenerator::start_eyeblink(int side, int duration) {
264
    // cout << "BLOCKED UNTIL " << eyeblink_blocked_timeout << "\n";
265
    if (boost::get_system_time() < eyeblink_blocked_timeout_) {
266
        // return if we are still in the block time
267
        return;
268
    }
269
    // request for n ms eyeblink:
270
    eyeblink_timeout_[side] = boost::get_system_time() + boost::posix_time::milliseconds(duration);
271

    
272
    eyeblink_active_[side] = true;
273
}
274

    
275
//! check if there is an ongoing saccade:
276
void EyelidMotionGenerator::check_for_saccade() {
277
    if (get_eye_saccade_active()) {
278
        // this is a saccade, check if not already in saccade:
279
        if (!saccade_blink_active_) {
280
            // this is a new saccade! restart blink timer
281
            saccade_blink_requested_ = true;
282
            saccade_blink_active_ = true;
283
        }
284
    } else {
285
        saccade_blink_active_ = false;
286
    }
287
}
288

    
289

    
290
//! publish targets to motor boards:
291
void EyelidMotionGenerator::publish_targets() {
292
    // publish values if there is an active gaze input within the last timerange
293
    if (gaze_target_input_active()) {
294
        joint_interface_->publish_target(JointInterface::ID_EYES_LEFT_LID_UPPER);
295
        joint_interface_->publish_target(JointInterface::ID_EYES_LEFT_LID_LOWER);
296
        joint_interface_->publish_target(JointInterface::ID_EYES_RIGHT_LID_UPPER);
297
        joint_interface_->publish_target(JointInterface::ID_EYES_RIGHT_LID_LOWER);
298
    }
299
}