Statistics
| Branch: | Tag: | Revision:

humotion / src / server / eyelid_motion_generator.cpp @ 01fafd4d

History | View | Annotate | Download (10.987 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
    eyelid_closed_[LEFT] = false;
45
    eyelid_closed_[RIGHT] = false;
46 40dc3e35 Simon Schulz
47 ea068cf1 sschulz
    eyeblink_blocked_timeout_ = boost::get_system_time();
48 8c6c1163 Simon Schulz
}
49
50
//! destructor
51 0c8d22a5 sschulz
EyelidMotionGenerator::~EyelidMotionGenerator() {
52 8c6c1163 Simon Schulz
}
53
54
55
//! calculate joint targets
56
//! \TODO: make up motion twice as slow as down motion
57 0c8d22a5 sschulz
void EyelidMotionGenerator::calculate_targets() {
58
    // printf("> humotion: calculating eyelid targets\n");
59 8c6c1163 Simon Schulz
60 0c8d22a5 sschulz
    // fetch current angles
61 8c6c1163 Simon Schulz
    float eye_tilt_now  = get_current_position(JointInterface::ID_EYES_BOTH_UD);
62
63 f311c844 sschulz
    float eyelid_target = 0.0;
64
    if (config->eyelids_follow_eyemotion) {
65
        // the eyelids shoudl follow the eye motion
66
        // therefore copy the eyeballs tilt position
67
        eyelid_target = eye_tilt_now;
68
    }
69
70 0c8d22a5 sschulz
    // calculate left eyelid targets
71 f311c844 sschulz
    float eyelid_upper_left_target = eyelid_target + requested_gaze_state_.eyelid_opening_upper;
72
    float eyelid_lower_left_target = eyelid_target - requested_gaze_state_.eyelid_opening_lower;
73 8c6c1163 Simon Schulz
74 0c8d22a5 sschulz
    // calculate right eyelid targets
75 f311c844 sschulz
    float eyelid_upper_right_target = eyelid_target + requested_gaze_state_.eyelid_opening_upper;
76
    float eyelid_lower_right_target = eyelid_target - requested_gaze_state_.eyelid_opening_lower;
77 8c6c1163 Simon Schulz
78 0c8d22a5 sschulz
    // limit target angles
79
    eyelid_upper_left_target  = limit_target(JointInterface::ID_EYES_LEFT_LID_UPPER,
80
                                             eyelid_upper_left_target);
81
    eyelid_lower_left_target  = limit_target(JointInterface::ID_EYES_LEFT_LID_LOWER,
82
                                             eyelid_lower_left_target);
83
    eyelid_upper_right_target = limit_target(JointInterface::ID_EYES_RIGHT_LID_UPPER,
84
                                             eyelid_upper_right_target);
85
    eyelid_lower_right_target = limit_target(JointInterface::ID_EYES_RIGHT_LID_LOWER,
86
                                             eyelid_lower_right_target);
87
88
    // (temporarily) store the target
89 ea068cf1 sschulz
    joint_interface_->set_target(JointInterface::ID_EYES_LEFT_LID_UPPER,
90 0c8d22a5 sschulz
                                eyelid_upper_left_target, 0.0);
91 ea068cf1 sschulz
    joint_interface_->set_target(JointInterface::ID_EYES_LEFT_LID_LOWER,
92 0c8d22a5 sschulz
                                eyelid_lower_left_target, 0.0);
93 ea068cf1 sschulz
    joint_interface_->set_target(JointInterface::ID_EYES_RIGHT_LID_UPPER,
94 0c8d22a5 sschulz
                                eyelid_upper_right_target, 0.0);
95 ea068cf1 sschulz
    joint_interface_->set_target(JointInterface::ID_EYES_RIGHT_LID_LOWER,
96 0c8d22a5 sschulz
                                eyelid_lower_right_target, 0.0);
97
98
    // check for saccade
99 8c6c1163 Simon Schulz
    check_for_saccade();
100
101 0c8d22a5 sschulz
    // is there a blink request?
102 ea068cf1 sschulz
    start_external_eyeblinks(requested_gaze_state_.eyeblink_request_left,
103
                             requested_gaze_state_.eyeblink_request_right);
104 8c6c1163 Simon Schulz
105 0c8d22a5 sschulz
    // do we have a saccade & do we want to exec an eyeblink?
106 8c6c1163 Simon Schulz
    process_saccadic_eyeblinks();
107
108 0c8d22a5 sschulz
    // execute for periodic eyeblinks every ? ms
109 8c6c1163 Simon Schulz
    process_periodic_eyeblinks();
110
111 0c8d22a5 sschulz
    // close eyes again after a given timeout
112 8c6c1163 Simon Schulz
    handle_eyeblink_timeout();
113
114 0c8d22a5 sschulz
    // eyeblinks override position target (if necessary)
115 7ed40bef Simon Schulz
    override_lids_for_eyeblink();
116 8c6c1163 Simon Schulz
}
117
118
//! process any external blink requests
119 0c8d22a5 sschulz
void EyelidMotionGenerator::start_external_eyeblinks(int duration_left, int duration_right) {
120
    // manual eyeblinks will ALWAYs get executed as we use
121
    // a negative block timeout (=timeout has already passed)
122
    if ((duration_left != 0) || (duration_right != 0)) {
123 ea068cf1 sschulz
        eyeblink_blocked_timeout_ = boost::get_system_time() - boost::posix_time::seconds(100);
124 9c7ef5fc Simon Schulz
    }
125
126 8c6c1163 Simon Schulz
127 0c8d22a5 sschulz
    if (duration_left == 0) {
128
        // nothing to do
129
    } else if (duration_left < 0) {
130
        // infinite sleep -> close
131 ea068cf1 sschulz
        eyelid_closed_[LEFT] = true;
132 0c8d22a5 sschulz
    } else {
133
        // timeout sleep
134 8c6c1163 Simon Schulz
        start_eyeblink(LEFT, duration_left);
135 ea068cf1 sschulz
        eyelid_closed_[LEFT] = false;
136 8c6c1163 Simon Schulz
    }
137
138 0c8d22a5 sschulz
    if (duration_right == 0) {
139
        // nothing to do
140
    } else if (duration_right < 0) {
141
        // infinite sleep -> close
142 ea068cf1 sschulz
        eyelid_closed_[RIGHT] = true;
143 0c8d22a5 sschulz
    } else {
144
        // timeout sleep
145 8c6c1163 Simon Schulz
        start_eyeblink(RIGHT, duration_right);
146 ea068cf1 sschulz
        eyelid_closed_[RIGHT] = false;
147 8c6c1163 Simon Schulz
    }
148
}
149
150
//! process saccadic blink requests
151
//! -> when we do a saccade, the chances to blink are much higher
152 0c8d22a5 sschulz
void EyelidMotionGenerator::process_saccadic_eyeblinks() {
153 ea068cf1 sschulz
    if (saccade_blink_requested_) {
154 0c8d22a5 sschulz
        // every n-th's saccade requests an eyeblink
155 6d13138a sschulz
        float frnd = static_cast <float> (std::rand()) / static_cast <float> (RAND_MAX);
156
        if (frnd <= config->eyeblink_probability_after_saccade) {
157
            printf("> saccadic eyeblink:\n");
158
            start_eyeblink(LEFT, config->eyeblink_duration * 1000.0);
159
            start_eyeblink(RIGHT, config->eyeblink_duration * 1000.0);
160 8c6c1163 Simon Schulz
        }
161 ea068cf1 sschulz
        saccade_blink_requested_ = false;
162 8c6c1163 Simon Schulz
    }
163
}
164
165
//! process periodic blink requests
166
//! -> we want to have an eyeblink every n...m seconds
167 0c8d22a5 sschulz
void EyelidMotionGenerator::process_periodic_eyeblinks() {
168 ea068cf1 sschulz
    if (eyeblink_active_[LEFT] || eyeblink_active_[RIGHT]) {
169 6d13138a sschulz
        float range = config->eyeblink_periodic_distribution_upper
170
                - config->eyeblink_periodic_distribution_lower;
171
172
        // random number 0...1
173
        float frnd = static_cast <float> (std::rand()) / static_cast <float> (RAND_MAX);
174
175 0c8d22a5 sschulz
        // calculate next timeout for a new periodic eyeblink
176 6d13138a sschulz
        float seconds_to_next_blink =
177
                config->eyeblink_periodic_distribution_lower  + frnd * range;
178
179 ea068cf1 sschulz
        periodic_blink_start_time_ = boost::get_system_time()
180 6d13138a sschulz
                + boost::posix_time::seconds(seconds_to_next_blink);
181 8c6c1163 Simon Schulz
    }
182
183 ea068cf1 sschulz
    if (boost::get_system_time() > periodic_blink_start_time_) {
184 0c8d22a5 sschulz
        // printf("> periodic eyeblink:\n");
185 6d13138a sschulz
        start_eyeblink(LEFT, config->eyeblink_duration * 1000.0);
186
        start_eyeblink(RIGHT, config->eyeblink_duration * 1000.0);
187 8c6c1163 Simon Schulz
    }
188
}
189
190
//! handle eyeblink timeouts.
191
//! this function will actually re-open the eyes after a predefined time
192 0c8d22a5 sschulz
void EyelidMotionGenerator::handle_eyeblink_timeout() {
193
    boost::system_time now = boost::get_system_time();
194 8c6c1163 Simon Schulz
195 0c8d22a5 sschulz
    // take care of re-opening eye timeout
196
    for (int i=LEFT; i <= RIGHT; i++) {
197 ea068cf1 sschulz
        if (eyeblink_active_[i]) {
198
            if (now >= eyeblink_timeout_[i]) {
199
                eyeblink_active_[i] = false;
200 8c6c1163 Simon Schulz
            }
201
        }
202
    }
203
204 0c8d22a5 sschulz
    // take care of blocking time
205 ea068cf1 sschulz
    if (eyeblink_active_[LEFT] || eyeblink_active_[RIGHT]) {
206
        eyeblink_blocked_timeout_ = boost::get_system_time()
207 6d13138a sschulz
                + boost::posix_time::seconds(config->eyeblink_blocked_time);
208 8c6c1163 Simon Schulz
    }
209
}
210
211
//! override eyelid positions
212
//! this will override (=close) the eyelids for all possible eyeblink requests
213 0c8d22a5 sschulz
void EyelidMotionGenerator::override_lids_for_eyeblink() {
214
    // close the requested eyelids
215 ea068cf1 sschulz
    if (eyeblink_active_[LEFT] || eyelid_closed_[LEFT]) {
216 8c6c1163 Simon Schulz
        close_eyelid(JointInterface::ID_EYES_LEFT_LID_UPPER);
217
        close_eyelid(JointInterface::ID_EYES_LEFT_LID_LOWER);
218
    }
219 ea068cf1 sschulz
    if (eyeblink_active_[RIGHT] || eyelid_closed_[RIGHT]) {
220 8c6c1163 Simon Schulz
        close_eyelid(JointInterface::ID_EYES_RIGHT_LID_UPPER);
221
        close_eyelid(JointInterface::ID_EYES_RIGHT_LID_LOWER);
222
    }
223
}
224
225
226
227
//! overwrite the intermediate target to close the given eyelid:
228 0c8d22a5 sschulz
void EyelidMotionGenerator::close_eyelid(int joint_id) {
229 8c6c1163 Simon Schulz
    float value = 0.0;
230
231 0c8d22a5 sschulz
    // set upper to minimal allowed value (=as closed as possible) + a small offset
232
    // set lower to the same value (instead of max) in order to avoid collisions
233
    switch (joint_id) {
234 7ed40bef Simon Schulz
    default:
235 0c8d22a5 sschulz
        // no eyelid -> return
236 7ed40bef Simon Schulz
        return;
237
238
    case(JointInterface::ID_EYES_LEFT_LID_UPPER):
239
    case(JointInterface::ID_EYES_LEFT_LID_LOWER):
240 0c8d22a5 sschulz
        // use the upper value + 10 deg as close state:
241 ea068cf1 sschulz
        value = joint_interface_->get_joint_min(JointInterface::ID_EYES_LEFT_LID_UPPER) + 10.0;
242 0c8d22a5 sschulz
        // overwrite last target_
243 ea068cf1 sschulz
        joint_interface_->set_target(joint_id, value, 0.0);
244 7ed40bef Simon Schulz
        break;
245
246
    case(JointInterface::ID_EYES_RIGHT_LID_UPPER):
247
    case(JointInterface::ID_EYES_RIGHT_LID_LOWER):
248 0c8d22a5 sschulz
        // use the upper value + 10 deg as close state:
249 ea068cf1 sschulz
        value = joint_interface_->get_joint_min(JointInterface::ID_EYES_RIGHT_LID_UPPER) + 10.0;
250 0c8d22a5 sschulz
        // overwrite last target_
251 ea068cf1 sschulz
        joint_interface_->set_target(joint_id, value, 0.0);
252 7ed40bef Simon Schulz
        break;
253 8c6c1163 Simon Schulz
    }
254
}
255
256
//! start an eyeblink request
257
//! this will actually set a flag and a timeout for reopening the given eyelid pair again
258
//! \param int id of side (LEFT or RIGHT)
259 0c8d22a5 sschulz
void EyelidMotionGenerator::start_eyeblink(int side, int duration) {
260
    // cout << "BLOCKED UNTIL " << eyeblink_blocked_timeout << "\n";
261 ea068cf1 sschulz
    if (boost::get_system_time() < eyeblink_blocked_timeout_) {
262 0c8d22a5 sschulz
        // return if we are still in the block time
263 8c6c1163 Simon Schulz
        return;
264
    }
265 0c8d22a5 sschulz
    // request for n ms eyeblink:
266 ea068cf1 sschulz
    eyeblink_timeout_[side] = boost::get_system_time() + boost::posix_time::milliseconds(duration);
267 8c6c1163 Simon Schulz
268 ea068cf1 sschulz
    eyeblink_active_[side] = true;
269 8c6c1163 Simon Schulz
}
270
271
//! check if there is an ongoing saccade:
272 0c8d22a5 sschulz
void EyelidMotionGenerator::check_for_saccade() {
273
    if (get_eye_saccade_active()) {
274
        // this is a saccade, check if not already in saccade:
275 ea068cf1 sschulz
        if (!saccade_blink_active_) {
276 0c8d22a5 sschulz
            // this is a new saccade! restart blink timer
277 ea068cf1 sschulz
            saccade_blink_requested_ = true;
278
            saccade_blink_active_ = true;
279 8c6c1163 Simon Schulz
        }
280 0c8d22a5 sschulz
    } else {
281 ea068cf1 sschulz
        saccade_blink_active_ = false;
282 8c6c1163 Simon Schulz
    }
283
}
284
285
286
//! publish targets to motor boards:
287 0c8d22a5 sschulz
void EyelidMotionGenerator::publish_targets() {
288
    // publish values if there is an active gaze input within the last timerange
289
    if (gaze_target_input_active()) {
290 ea068cf1 sschulz
        joint_interface_->publish_target(JointInterface::ID_EYES_LEFT_LID_UPPER);
291
        joint_interface_->publish_target(JointInterface::ID_EYES_LEFT_LID_LOWER);
292
        joint_interface_->publish_target(JointInterface::ID_EYES_RIGHT_LID_UPPER);
293
        joint_interface_->publish_target(JointInterface::ID_EYES_RIGHT_LID_LOWER);
294 8c6c1163 Simon Schulz
    }
295
}