Statistics
| Branch: | Tag: | Revision:

humotion / src / server / eyelid_motion_generator.cpp @ 0c8d22a5

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