Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (11.086 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 0c8d22a5 sschulz
#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 6d13138a sschulz
using humotion::server::Config;
38 8c6c1163 Simon Schulz
39
//! constructor
40 6d13138a sschulz
EyelidMotionGenerator::EyelidMotionGenerator(JointInterface *j, Config *cfg) :
41
    EyeMotionGenerator(j, cfg) {
42 ea068cf1 sschulz
    saccade_blink_active_ = false;
43
    saccade_blink_requested_ = false;
44 95dd1012 sschulz
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 40dc3e35 Simon Schulz
51 ea068cf1 sschulz
    eyeblink_blocked_timeout_ = boost::get_system_time();
52 8c6c1163 Simon Schulz
}
53
54
//! destructor
55 0c8d22a5 sschulz
EyelidMotionGenerator::~EyelidMotionGenerator() {
56 8c6c1163 Simon Schulz
}
57
58
59
//! calculate joint targets
60
//! \TODO: make up motion twice as slow as down motion
61 0c8d22a5 sschulz
void EyelidMotionGenerator::calculate_targets() {
62
    // printf("> humotion: calculating eyelid targets\n");
63 8c6c1163 Simon Schulz
64 0c8d22a5 sschulz
    // fetch current angles
65 8c6c1163 Simon Schulz
    float eye_tilt_now  = get_current_position(JointInterface::ID_EYES_BOTH_UD);
66
67 f311c844 sschulz
    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 0c8d22a5 sschulz
    // calculate left eyelid targets
75 f311c844 sschulz
    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 8c6c1163 Simon Schulz
78 0c8d22a5 sschulz
    // calculate right eyelid targets
79 f311c844 sschulz
    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 8c6c1163 Simon Schulz
82 0c8d22a5 sschulz
    // 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 ea068cf1 sschulz
    joint_interface_->set_target(JointInterface::ID_EYES_LEFT_LID_UPPER,
94 0c8d22a5 sschulz
                                eyelid_upper_left_target, 0.0);
95 ea068cf1 sschulz
    joint_interface_->set_target(JointInterface::ID_EYES_LEFT_LID_LOWER,
96 0c8d22a5 sschulz
                                eyelid_lower_left_target, 0.0);
97 ea068cf1 sschulz
    joint_interface_->set_target(JointInterface::ID_EYES_RIGHT_LID_UPPER,
98 0c8d22a5 sschulz
                                eyelid_upper_right_target, 0.0);
99 ea068cf1 sschulz
    joint_interface_->set_target(JointInterface::ID_EYES_RIGHT_LID_LOWER,
100 0c8d22a5 sschulz
                                eyelid_lower_right_target, 0.0);
101
102
    // check for saccade
103 8c6c1163 Simon Schulz
    check_for_saccade();
104
105 0c8d22a5 sschulz
    // is there a blink request?
106 ea068cf1 sschulz
    start_external_eyeblinks(requested_gaze_state_.eyeblink_request_left,
107
                             requested_gaze_state_.eyeblink_request_right);
108 8c6c1163 Simon Schulz
109 0c8d22a5 sschulz
    // do we have a saccade & do we want to exec an eyeblink?
110 8c6c1163 Simon Schulz
    process_saccadic_eyeblinks();
111
112 0c8d22a5 sschulz
    // execute for periodic eyeblinks every ? ms
113 8c6c1163 Simon Schulz
    process_periodic_eyeblinks();
114
115 0c8d22a5 sschulz
    // close eyes again after a given timeout
116 8c6c1163 Simon Schulz
    handle_eyeblink_timeout();
117
118 0c8d22a5 sschulz
    // eyeblinks override position target (if necessary)
119 7ed40bef Simon Schulz
    override_lids_for_eyeblink();
120 8c6c1163 Simon Schulz
}
121
122
//! process any external blink requests
123 0c8d22a5 sschulz
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 ea068cf1 sschulz
        eyeblink_blocked_timeout_ = boost::get_system_time() - boost::posix_time::seconds(100);
128 9c7ef5fc Simon Schulz
    }
129
130 8c6c1163 Simon Schulz
131 0c8d22a5 sschulz
    if (duration_left == 0) {
132
        // nothing to do
133
    } else if (duration_left < 0) {
134
        // infinite sleep -> close
135 ea068cf1 sschulz
        eyelid_closed_[LEFT] = true;
136 0c8d22a5 sschulz
    } else {
137
        // timeout sleep
138 8c6c1163 Simon Schulz
        start_eyeblink(LEFT, duration_left);
139 ea068cf1 sschulz
        eyelid_closed_[LEFT] = false;
140 8c6c1163 Simon Schulz
    }
141
142 0c8d22a5 sschulz
    if (duration_right == 0) {
143
        // nothing to do
144
    } else if (duration_right < 0) {
145
        // infinite sleep -> close
146 ea068cf1 sschulz
        eyelid_closed_[RIGHT] = true;
147 0c8d22a5 sschulz
    } else {
148
        // timeout sleep
149 8c6c1163 Simon Schulz
        start_eyeblink(RIGHT, duration_right);
150 ea068cf1 sschulz
        eyelid_closed_[RIGHT] = false;
151 8c6c1163 Simon Schulz
    }
152
}
153
154
//! process saccadic blink requests
155
//! -> when we do a saccade, the chances to blink are much higher
156 0c8d22a5 sschulz
void EyelidMotionGenerator::process_saccadic_eyeblinks() {
157 ea068cf1 sschulz
    if (saccade_blink_requested_) {
158 0c8d22a5 sschulz
        // every n-th's saccade requests an eyeblink
159 6d13138a sschulz
        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 8c6c1163 Simon Schulz
        }
165 ea068cf1 sschulz
        saccade_blink_requested_ = false;
166 8c6c1163 Simon Schulz
    }
167
}
168
169
//! process periodic blink requests
170
//! -> we want to have an eyeblink every n...m seconds
171 0c8d22a5 sschulz
void EyelidMotionGenerator::process_periodic_eyeblinks() {
172 ea068cf1 sschulz
    if (eyeblink_active_[LEFT] || eyeblink_active_[RIGHT]) {
173 6d13138a sschulz
        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 0c8d22a5 sschulz
        // calculate next timeout for a new periodic eyeblink
180 6d13138a sschulz
        float seconds_to_next_blink =
181
                config->eyeblink_periodic_distribution_lower  + frnd * range;
182
183 ea068cf1 sschulz
        periodic_blink_start_time_ = boost::get_system_time()
184 6d13138a sschulz
                + boost::posix_time::seconds(seconds_to_next_blink);
185 8c6c1163 Simon Schulz
    }
186
187 ea068cf1 sschulz
    if (boost::get_system_time() > periodic_blink_start_time_) {
188 0c8d22a5 sschulz
        // printf("> periodic eyeblink:\n");
189 6d13138a sschulz
        start_eyeblink(LEFT, config->eyeblink_duration * 1000.0);
190
        start_eyeblink(RIGHT, config->eyeblink_duration * 1000.0);
191 8c6c1163 Simon Schulz
    }
192
}
193
194
//! handle eyeblink timeouts.
195
//! this function will actually re-open the eyes after a predefined time
196 0c8d22a5 sschulz
void EyelidMotionGenerator::handle_eyeblink_timeout() {
197
    boost::system_time now = boost::get_system_time();
198 8c6c1163 Simon Schulz
199 0c8d22a5 sschulz
    // take care of re-opening eye timeout
200
    for (int i=LEFT; i <= RIGHT; i++) {
201 ea068cf1 sschulz
        if (eyeblink_active_[i]) {
202
            if (now >= eyeblink_timeout_[i]) {
203
                eyeblink_active_[i] = false;
204 8c6c1163 Simon Schulz
            }
205
        }
206
    }
207
208 0c8d22a5 sschulz
    // take care of blocking time
209 ea068cf1 sschulz
    if (eyeblink_active_[LEFT] || eyeblink_active_[RIGHT]) {
210
        eyeblink_blocked_timeout_ = boost::get_system_time()
211 6d13138a sschulz
                + boost::posix_time::seconds(config->eyeblink_blocked_time);
212 8c6c1163 Simon Schulz
    }
213
}
214
215
//! override eyelid positions
216
//! this will override (=close) the eyelids for all possible eyeblink requests
217 0c8d22a5 sschulz
void EyelidMotionGenerator::override_lids_for_eyeblink() {
218
    // close the requested eyelids
219 ea068cf1 sschulz
    if (eyeblink_active_[LEFT] || eyelid_closed_[LEFT]) {
220 8c6c1163 Simon Schulz
        close_eyelid(JointInterface::ID_EYES_LEFT_LID_UPPER);
221
        close_eyelid(JointInterface::ID_EYES_LEFT_LID_LOWER);
222
    }
223 ea068cf1 sschulz
    if (eyeblink_active_[RIGHT] || eyelid_closed_[RIGHT]) {
224 8c6c1163 Simon Schulz
        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 0c8d22a5 sschulz
void EyelidMotionGenerator::close_eyelid(int joint_id) {
233 8c6c1163 Simon Schulz
    float value = 0.0;
234
235 0c8d22a5 sschulz
    // 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 7ed40bef Simon Schulz
    default:
239 0c8d22a5 sschulz
        // no eyelid -> return
240 7ed40bef Simon Schulz
        return;
241
242
    case(JointInterface::ID_EYES_LEFT_LID_UPPER):
243
    case(JointInterface::ID_EYES_LEFT_LID_LOWER):
244 0c8d22a5 sschulz
        // use the upper value + 10 deg as close state:
245 ea068cf1 sschulz
        value = joint_interface_->get_joint_min(JointInterface::ID_EYES_LEFT_LID_UPPER) + 10.0;
246 0c8d22a5 sschulz
        // overwrite last target_
247 ea068cf1 sschulz
        joint_interface_->set_target(joint_id, value, 0.0);
248 7ed40bef Simon Schulz
        break;
249
250
    case(JointInterface::ID_EYES_RIGHT_LID_UPPER):
251
    case(JointInterface::ID_EYES_RIGHT_LID_LOWER):
252 0c8d22a5 sschulz
        // use the upper value + 10 deg as close state:
253 ea068cf1 sschulz
        value = joint_interface_->get_joint_min(JointInterface::ID_EYES_RIGHT_LID_UPPER) + 10.0;
254 0c8d22a5 sschulz
        // overwrite last target_
255 ea068cf1 sschulz
        joint_interface_->set_target(joint_id, value, 0.0);
256 7ed40bef Simon Schulz
        break;
257 8c6c1163 Simon Schulz
    }
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 0c8d22a5 sschulz
void EyelidMotionGenerator::start_eyeblink(int side, int duration) {
264
    // cout << "BLOCKED UNTIL " << eyeblink_blocked_timeout << "\n";
265 ea068cf1 sschulz
    if (boost::get_system_time() < eyeblink_blocked_timeout_) {
266 0c8d22a5 sschulz
        // return if we are still in the block time
267 8c6c1163 Simon Schulz
        return;
268
    }
269 0c8d22a5 sschulz
    // request for n ms eyeblink:
270 ea068cf1 sschulz
    eyeblink_timeout_[side] = boost::get_system_time() + boost::posix_time::milliseconds(duration);
271 8c6c1163 Simon Schulz
272 ea068cf1 sschulz
    eyeblink_active_[side] = true;
273 8c6c1163 Simon Schulz
}
274
275
//! check if there is an ongoing saccade:
276 0c8d22a5 sschulz
void EyelidMotionGenerator::check_for_saccade() {
277
    if (get_eye_saccade_active()) {
278
        // this is a saccade, check if not already in saccade:
279 ea068cf1 sschulz
        if (!saccade_blink_active_) {
280 0c8d22a5 sschulz
            // this is a new saccade! restart blink timer
281 ea068cf1 sschulz
            saccade_blink_requested_ = true;
282
            saccade_blink_active_ = true;
283 8c6c1163 Simon Schulz
        }
284 0c8d22a5 sschulz
    } else {
285 ea068cf1 sschulz
        saccade_blink_active_ = false;
286 8c6c1163 Simon Schulz
    }
287
}
288
289
290
//! publish targets to motor boards:
291 0c8d22a5 sschulz
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 ea068cf1 sschulz
        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 8c6c1163 Simon Schulz
    }
299
}