Revision 6d13138a
CMakeLists.txt | ||
---|---|---|
106 | 106 |
|
107 | 107 |
## Declare a cpp library |
108 | 108 |
add_library(${PROJECT_NAME} |
109 |
src/mouth_state.cpp |
|
110 |
src/gaze_state.cpp |
|
111 |
|
|
112 | 109 |
src/client/client.cpp |
113 | 110 |
src/client/middleware.cpp |
114 | 111 |
src/client/middleware_ros.cpp |
115 | 112 |
|
116 | 113 |
src/server/server.cpp |
114 |
src/server/config.cpp |
|
117 | 115 |
src/server/middleware.cpp |
118 | 116 |
src/server/middleware_ros.cpp |
119 |
|
|
120 | 117 |
src/server/controller.cpp |
121 | 118 |
src/server/joint_interface.cpp |
122 | 119 |
src/server/motion_generator.cpp |
... | ... | |
127 | 124 |
src/server/eyelid_motion_generator.cpp |
128 | 125 |
src/server/eyebrow_motion_generator.cpp |
129 | 126 |
src/server/neck_motion_generator.cpp |
127 |
|
|
128 |
src/mouth_state.cpp |
|
129 |
src/gaze_state.cpp |
|
130 | 130 |
src/timestamp.cpp |
131 | 131 |
src/timestamped_list.cpp |
132 | 132 |
) |
cfg/humotion.cfg | ||
---|---|---|
4 | 4 |
|
5 | 5 |
gen = ParameterGenerator() |
6 | 6 |
|
7 |
group = gen.add_group("My Group") |
|
8 |
group.add("my_group_param", int_t, 0, "An int within My Group", 0) |
|
7 |
general_group = gen.add_group("general") |
|
8 |
general_group.add("eye_saccade_velocity_threshold", double_t, 0, "eye velocity threshold for saccade detection (in deg/s)", 15.0, 1.0, 30.0) |
|
9 |
general_group.add("neck_saccade_threshold", double_t, 0, "magnitude of gaze change that triggers neck saccade (in deg)", 15.0, 1.0, 30.0) |
|
10 |
general_group.add("neck_saccade_omr_trigger", double_t, 0, "a deflection exceeding <VAL> * OMR will trigger a correction neck saccade", 0.95, 0.1, 1.0) |
|
11 |
|
|
12 |
neck_group = gen.add_group("neck") |
|
13 |
neck_group.add("neck_max_acceleration", int_t, 0, "maximum neck acceleration limit (in deg/s^2)", 1000, 100, 4000) |
|
14 |
neck_group.add("neck_max_velocity", int_t, 0, "maximum neck velocity limit (in deg/s)", 1000, 100, 800) |
|
15 |
neck_group.add("neck_velocity_scale", double_t, 0, "scaling factor for neck accelerations (1.0 = full human velocities)", 0.7, 0.1, 1.0) |
|
16 |
|
|
17 |
#float max_speed = (CONST_GUITTON87_A * distance_abs + CONST_GUITTON87_B); |
|
18 |
neck_group.add("neck_velocity_lin_eq_const_a", double_t, 0, "constant A (scale) for linear velocity equation (guitton 4.39)", 4.39 / 2.0, 1.0, 10.0) |
|
19 |
neck_group.add("neck_velocity_lin_eq_const_b", double_t, 0, "constant B (offset) for linear velocity equation (guitton 4.39)", 106.0 / 2.0, 1, 200) |
|
20 |
|
|
21 |
#others |
|
22 |
neck_group.add("neck_breath_period", int_t, 0, "breath period (inhale+pause+exhale) in ms)", 3*1500, 3000, 5000) |
|
23 |
neck_group.add("neck_breath_amplitude", double_t, 0, "amplitude of head tilt deflection during breath (given in deg)", 1.0, 0.0, 5.0) |
|
24 |
|
|
25 |
eye_group = gen.add_group("eye") |
|
26 |
eye_group.add("eye_max_acceleration", int_t, 0, "maximum eye acceleration limit (in deg/s^2)", 80000, 1000, 80000) |
|
27 |
eye_group.add("eye_max_velocity", int_t, 0, "maximum eye velocity limit (in deg/s)", 700, 100, 700) |
|
28 |
eye_group.add("eye_velocity_scale", double_t, 0, "scaling factor for eye accelerations (1.0 = full human velocities)", 1.0, 0.1, 1.0) |
|
29 |
|
|
30 |
eyeblink_group = gen.add_group("eyeblink") |
|
31 |
eyeblink_group.add("eyeblink_duration", int_t, 0, "eyeblink duration (in ms)", 150, 50, 500) |
|
32 |
eyeblink_group.add("eyeblink_every_min", int_t, 0, "eyeblink every n ms, lower bound (in ms)", 2000, 1000, 10000) |
|
33 |
eyeblink_group.add("eyeblink_every_max", int_t, 0, "eyeblink every n ms, upper bound (in ms)", 10000, 1000, 20000) |
|
34 |
eyeblink_group.add("eyeblink_blocking_time", int_t, 0, "eyeblink blocked timeout (in ms)", 1000, 100, 5000) |
|
35 |
|
|
36 |
|
|
37 |
|
|
38 |
|
|
39 |
#eyelid: |
|
40 |
# static const float SACCADE_SPEED_THRESHOLD; |
|
41 |
# static const float EYEBLINK_DURATION_MS; |
|
42 |
# static const float EYEBLINK_EYERY_MS_MIN; |
|
43 |
# static const float EYEBLINK_EYERY_MS_MAX; |
|
44 |
# static const float EYEBLINK_BLOCKING_TIME; |
|
45 |
# |
|
46 |
|
|
47 |
# static const float MOUTH_MIN_OPENING; |
|
48 |
# |
|
49 |
#neck |
|
50 |
# static const float CONST_GUITTON87_A; |
|
51 |
# static const float CONST_GUITTON87_B; |
|
52 |
# |
|
53 |
# static const float CONST_BREATH_PERIOD; |
|
54 |
# static const float CONST_BREATH_AMPLITUDE; |
|
55 |
# |
|
56 |
#misc for neck and eyes: |
|
57 |
#max accel |
|
58 |
#v_scale |
|
9 | 59 |
|
10 | 60 |
#timing_mode_enum = gen.enum([ |
11 | 61 |
# gen.const("free_running", int_t, 0, "camera acquires images at a maximum possible framerate"), |
include/humotion/server/config.h | ||
---|---|---|
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 |
|
|
29 |
#ifndef INCLUDE_HUMOTION_SERVER_CONFIG_H_ |
|
30 |
#define INCLUDE_HUMOTION_SERVER_CONFIG_H_ |
|
31 |
|
|
32 |
namespace humotion { |
|
33 |
namespace server { |
|
34 |
|
|
35 |
class Config { |
|
36 |
public: |
|
37 |
Config(); |
|
38 |
~Config(); |
|
39 |
void init_defaults(); |
|
40 |
|
|
41 |
// ******************************************************** |
|
42 |
// NOTE: See config.cpp for explanations and default values |
|
43 |
// ******************************************************** |
|
44 |
|
|
45 |
// saccade detection thresholds |
|
46 |
float threshold_velocity_eye_saccade; |
|
47 |
float threshold_angle_neck_saccade; |
|
48 |
float threshold_angle_omr_limit; |
|
49 |
|
|
50 |
|
|
51 |
// neck motion generation configuration |
|
52 |
float scale_velocity_neck; |
|
53 |
float scale_acceleration_neck; |
|
54 |
float limit_velocity_neck; |
|
55 |
float limit_acceleration_neck; |
|
56 |
|
|
57 |
// eye motion generation configuration |
|
58 |
float scale_velocity_eye; |
|
59 |
float scale_acceleration_eye; |
|
60 |
float limit_velocity_eye; |
|
61 |
float limit_acceleration_eye; |
|
62 |
|
|
63 |
// parameters fo the breathing pattern |
|
64 |
float breath_period; |
|
65 |
float breath_amplitude; |
|
66 |
|
|
67 |
// parameters for eye blinking |
|
68 |
float eyeblink_duration; |
|
69 |
float eyeblink_periodic_distribution_lower; |
|
70 |
float eyeblink_periodic_distribution_upper; |
|
71 |
float eyeblink_probability_after_saccade; |
|
72 |
float eyeblink_blocked_time; |
|
73 |
}; |
|
74 |
|
|
75 |
} // namespace server |
|
76 |
} // namespace humotion |
|
77 |
|
|
78 |
#endif // INCLUDE_HUMOTION_SERVER_CONFIG_H_ |
include/humotion/server/controller.h | ||
---|---|---|
32 | 32 |
|
33 | 33 |
#include "humotion/gaze_state.h" |
34 | 34 |
#include "humotion/mouth_state.h" |
35 |
#include "humotion/server/config.h" |
|
35 | 36 |
#include "humotion/server/joint_interface.h" |
36 | 37 |
#include "humotion/server/motion_generator.h" |
37 | 38 |
#include "humotion/timestamp.h" |
... | ... | |
60 | 61 |
motion_generator_vector_t motion_generator_vector_; |
61 | 62 |
|
62 | 63 |
JointInterface *joint_interface_; |
64 |
Config *config_; |
|
63 | 65 |
|
64 | 66 |
bool activated_; |
65 | 67 |
|
include/humotion/server/eye_motion_generator.h | ||
---|---|---|
36 | 36 |
|
37 | 37 |
class EyeMotionGenerator : public GazeMotionGenerator { |
38 | 38 |
public: |
39 |
explicit EyeMotionGenerator(JointInterface *j);
|
|
39 |
EyeMotionGenerator(JointInterface *j, Config *cfg);
|
|
40 | 40 |
~EyeMotionGenerator(); |
41 | 41 |
|
42 | 42 |
void calculate_targets(); |
43 | 43 |
void publish_targets(); |
44 | 44 |
|
45 | 45 |
|
46 |
static const float SACCADE_SPEED_THRESHOLD; |
|
46 |
// static const float SACCADE_SPEED_THRESHOLD;
|
|
47 | 47 |
private: |
48 | 48 |
void setup_eyemotion(int dof, float target, float current_position, |
49 | 49 |
float current_velocity, Timestamp timestamp); |
include/humotion/server/eyebrow_motion_generator.h | ||
---|---|---|
35 | 35 |
|
36 | 36 |
class EyebrowMotionGenerator : public MotionGenerator { |
37 | 37 |
public: |
38 |
explicit EyebrowMotionGenerator(JointInterface *j); |
|
38 |
explicit EyebrowMotionGenerator(JointInterface *j, Config *cfg);
|
|
39 | 39 |
~EyebrowMotionGenerator(); |
40 | 40 |
void calculate_targets(); |
41 | 41 |
void publish_targets(); |
include/humotion/server/eyelid_motion_generator.h | ||
---|---|---|
38 | 38 |
|
39 | 39 |
class EyelidMotionGenerator : public EyeMotionGenerator { |
40 | 40 |
public: |
41 |
explicit EyelidMotionGenerator(JointInterface *j);
|
|
41 |
EyelidMotionGenerator(JointInterface *j, Config *cfg);
|
|
42 | 42 |
~EyelidMotionGenerator(); |
43 | 43 |
|
44 | 44 |
void calculate_targets(); |
... | ... | |
53 | 53 |
void handle_eyeblink_timeout(); |
54 | 54 |
void override_lids_for_eyeblink(); |
55 | 55 |
void check_for_saccade(); |
56 |
void start_eyeblink(int side, int duration = EYEBLINK_DURATION_MS);
|
|
56 |
void start_eyeblink(int side, int duration); |
|
57 | 57 |
|
58 | 58 |
void close_eyelid(int joint_id); |
59 | 59 |
|
... | ... | |
69 | 69 |
bool eyeblink_active_[SIDE_ID_SIZE]; |
70 | 70 |
bool eyelid_closed_[SIDE_ID_SIZE]; |
71 | 71 |
|
72 |
static const float SACCADE_SPEED_THRESHOLD; |
|
73 |
static const float EYEBLINK_DURATION_MS; |
|
74 |
static const float EYEBLINK_EYERY_MS_MIN; |
|
75 |
static const float EYEBLINK_EYERY_MS_MAX; |
|
76 |
static const float EYEBLINK_BLOCKING_TIME; |
|
77 |
|
|
78 | 72 |
boost::system_time periodic_blink_start_time_; |
79 | 73 |
boost::system_time eyeblink_timeout_[SIDE_ID_SIZE]; |
80 | 74 |
boost::system_time eyeblink_blocked_timeout_; |
include/humotion/server/gaze_motion_generator.h | ||
---|---|---|
36 | 36 |
|
37 | 37 |
class GazeMotionGenerator : public ReflexxesMotionGenerator { |
38 | 38 |
public: |
39 |
GazeMotionGenerator(JointInterface *j, int dof, float t); |
|
39 |
GazeMotionGenerator(JointInterface *j, Config *cfg, int dof, float t);
|
|
40 | 40 |
~GazeMotionGenerator(); |
41 | 41 |
|
42 | 42 |
void set_gaze_target(GazeState s); |
43 | 43 |
|
44 |
static const float SACCADE_SPEED_THRESHOLD; |
|
45 |
static const float SACCADE_LATENCY; |
|
46 |
|
|
47 | 44 |
protected: |
48 | 45 |
GazeState get_current_gaze(); |
49 | 46 |
|
50 | 47 |
bool get_eye_saccade_active(); |
51 | 48 |
bool neck_saccade_requested; |
52 | 49 |
bool neck_saccade_omr; |
53 |
|
|
54 |
private: |
|
55 |
// constants |
|
56 |
static const float NECK_SACCADE_THRESHOLD; |
|
57 |
static const float EYE_SACCADE_SPEED_THRESHOLD; |
|
58 |
static const float OMR_LIMIT_TRIGGERS_NECK_SACCADE; |
|
59 | 50 |
}; |
60 | 51 |
|
61 | 52 |
} // namespace server |
include/humotion/server/motion_generator.h | ||
---|---|---|
32 | 32 |
|
33 | 33 |
#include <string> |
34 | 34 |
|
35 |
#include "humotion/server/config.h" |
|
35 | 36 |
#include "humotion/server/joint_interface.h" |
36 | 37 |
|
37 | 38 |
namespace humotion { |
... | ... | |
39 | 40 |
|
40 | 41 |
class MotionGenerator { |
41 | 42 |
public: |
42 |
explicit MotionGenerator(JointInterface *j);
|
|
43 |
MotionGenerator(JointInterface *j, Config *cfg);
|
|
43 | 44 |
~MotionGenerator(); |
44 | 45 |
|
45 | 46 |
virtual void calculate_targets() = 0; |
... | ... | |
49 | 50 |
virtual void set_mouth_target(MouthState s); |
50 | 51 |
|
51 | 52 |
protected: |
53 |
Config *config; |
|
54 |
|
|
52 | 55 |
float get_current_position(int joint_id); |
53 | 56 |
float get_current_speed(int joint_id); |
54 | 57 |
humotion::Timestamp get_timestamped_state(int joint_id, float *position, float *velocity); |
include/humotion/server/mouth_motion_generator.h | ||
---|---|---|
35 | 35 |
|
36 | 36 |
class MouthMotionGenerator : public MotionGenerator { |
37 | 37 |
public: |
38 |
explicit MouthMotionGenerator(JointInterface *j);
|
|
38 |
MouthMotionGenerator(JointInterface *j, Config *cfg);
|
|
39 | 39 |
~MouthMotionGenerator(); |
40 | 40 |
void calculate_targets(); |
41 | 41 |
void publish_targets(); |
include/humotion/server/neck_motion_generator.h | ||
---|---|---|
34 | 34 |
|
35 | 35 |
class NeckMotionGenerator : public GazeMotionGenerator { |
36 | 36 |
public: |
37 |
explicit NeckMotionGenerator(JointInterface *j);
|
|
37 |
NeckMotionGenerator(JointInterface *j, Config *cfg);
|
|
38 | 38 |
~NeckMotionGenerator(); |
39 | 39 |
void calculate_targets(); |
40 | 40 |
void publish_targets(); |
... | ... | |
49 | 49 |
bool neck_saccade_reached_goal_; |
50 | 50 |
bool neck_saccade_active_; |
51 | 51 |
float breath_time_; |
52 |
|
|
53 |
static const float CONST_GUITTON87_A; |
|
54 |
static const float CONST_GUITTON87_B; |
|
55 |
|
|
56 |
static const float CONST_BREATH_PERIOD; |
|
57 |
static const float CONST_BREATH_AMPLITUDE; |
|
58 | 52 |
}; |
59 | 53 |
|
60 | 54 |
} // namespace server |
include/humotion/server/reflexxes_motion_generator.h | ||
---|---|---|
40 | 40 |
|
41 | 41 |
class ReflexxesMotionGenerator : public MotionGenerator{ |
42 | 42 |
public: |
43 |
ReflexxesMotionGenerator(JointInterface *j, int dof, float t); |
|
43 |
ReflexxesMotionGenerator(JointInterface *j, Config *cfg, int dof, float t);
|
|
44 | 44 |
~ReflexxesMotionGenerator(); |
45 | 45 |
|
46 | 46 |
protected: |
src/server/config.cpp | ||
---|---|---|
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/config.h" |
|
29 |
|
|
30 |
using humotion::server::Config; |
|
31 |
|
|
32 |
Config::Config() { |
|
33 |
init_defaults(); |
|
34 |
} |
|
35 |
|
|
36 |
|
|
37 |
Config::~Config() { |
|
38 |
} |
|
39 |
|
|
40 |
void Config::init_defaults() { |
|
41 |
// saccade detection thresholds: |
|
42 |
// 1) velocity threshold, values above this value trigger an eye saccade |
|
43 |
// value is given in deg/s |
|
44 |
threshold_velocity_eye_saccade = 15.0; |
|
45 |
|
|
46 |
// 2) angular threshold, a target change higher than this value will trigger a neck saccade |
|
47 |
// value is given in deg |
|
48 |
threshold_angle_neck_saccade = 15.0; |
|
49 |
|
|
50 |
// 3) eyes reaching ocolumotor limits will trigger correction saccade |
|
51 |
// value given in percent (NOTE: 1.0 = 100%) |
|
52 |
threshold_angle_omr_limit = 0.95; |
|
53 |
|
|
54 |
|
|
55 |
// neck motion generation configuration |
|
56 |
// humotion calculates neck velocities based on the linear equation Hmax from |
|
57 |
// [Guitton87] "Gaze control in humans: eye-head coordination during orienting movements ..." |
|
58 |
// In order to allow better adaption to the robot capabilities humotion allows to |
|
59 |
// scale the calculated velocity, value is given in percent (NOTE: 1.0 = 100% human velocity) |
|
60 |
scale_velocity_neck = 0.5; |
|
61 |
|
|
62 |
// scale acceleration |
|
63 |
scale_acceleration_neck = 0.7; |
|
64 |
|
|
65 |
// additionally humotion allows to limit the maximum velocity, value is given in deg/s |
|
66 |
limit_velocity_neck = 700.0; |
|
67 |
|
|
68 |
// limit the maximum acceleration, value is given in deg/s^2 |
|
69 |
limit_acceleration_neck = 1000.0; |
|
70 |
|
|
71 |
// eye motion generation configuration |
|
72 |
// scale the calculated velocity, value is given in percent (NOTE: 1.0 = 100% human velocity) |
|
73 |
scale_velocity_eye = 1.0; |
|
74 |
|
|
75 |
// scale acceleration |
|
76 |
scale_acceleration_eye = 1.0; |
|
77 |
|
|
78 |
// additionally humotion allows to limit the maximum velocity, value is given in deg/s |
|
79 |
limit_velocity_eye = 700.0; |
|
80 |
|
|
81 |
// limit the maximum acceleration, value is given in deg/s^2 |
|
82 |
limit_acceleration_eye = 80000; |
|
83 |
|
|
84 |
|
|
85 |
// parameters fo the breathing pattern |
|
86 |
// healthy adult human: 12-15 breaths/min (see e.g. "Ganong's review of medical physiology") |
|
87 |
// total breathe: 60/12-15 = 3-5s |
|
88 |
// inhale 1.5-2s |
|
89 |
// exhale 1.5-2s |
|
90 |
// pause 2s |
|
91 |
// overall period given in seconds |
|
92 |
breath_period = 1.5 + 1.5 + 1.5; // inhale, pause & exhale |
|
93 |
// amplitude given in degrees |
|
94 |
breath_amplitude = 1.0; |
|
95 |
|
|
96 |
|
|
97 |
// parameters for eye blinking |
|
98 |
// duration for one eyeblink, value is given in seconds |
|
99 |
eyeblink_duration = 0.15; |
|
100 |
|
|
101 |
// occurance of periodic eyeblinks, uniformly distributed over the given range |
|
102 |
// typical values for a human are one blink every 2...10s |
|
103 |
// values are given in seconds |
|
104 |
eyeblink_periodic_distribution_lower = 2.0; |
|
105 |
eyeblink_periodic_distribution_upper = 10.0; |
|
106 |
|
|
107 |
// probability that one eye saccade causes an eyeblink, value given in percent |
|
108 |
// for humans this is up to 95%, this gets quite annoying on the robot so the default is lower |
|
109 |
eyeblink_probability_after_saccade = 0.33; |
|
110 |
|
|
111 |
// blocking time where further eyeblinks are suppressed, value given in seconds |
|
112 |
eyeblink_blocked_time = 1000.0; |
|
113 |
} |
src/server/controller.cpp | ||
---|---|---|
38 | 38 |
// using namespace humotion::server; |
39 | 39 |
|
40 | 40 |
using humotion::server::Controller; |
41 |
using humotion::server::Config; |
|
41 | 42 |
|
42 | 43 |
//! constructor |
43 | 44 |
Controller::Controller(JointInterface *j) { |
44 | 45 |
activated_ = false; |
45 | 46 |
joint_interface_ = j; |
47 |
|
|
48 |
config_ = new Config(); |
|
46 | 49 |
} |
47 | 50 |
|
48 | 51 |
//! destructor |
... | ... | |
55 | 58 |
// (i.e. the neck generator must be added after the eye generator!) |
56 | 59 |
|
57 | 60 |
// eye motion generation: |
58 |
add_motion_generator(new EyeMotionGenerator(joint_interface_)); |
|
61 |
add_motion_generator(new EyeMotionGenerator(joint_interface_, config_));
|
|
59 | 62 |
|
60 | 63 |
// eyelid motion generator |
61 |
add_motion_generator(new EyelidMotionGenerator(joint_interface_)); |
|
64 |
add_motion_generator(new EyelidMotionGenerator(joint_interface_, config_));
|
|
62 | 65 |
|
63 | 66 |
// neck motion generator |
64 |
add_motion_generator(new NeckMotionGenerator(joint_interface_)); |
|
67 |
add_motion_generator(new NeckMotionGenerator(joint_interface_, config_));
|
|
65 | 68 |
|
66 | 69 |
// mouth motion generator |
67 |
add_motion_generator(new MouthMotionGenerator(joint_interface_)); |
|
70 |
add_motion_generator(new MouthMotionGenerator(joint_interface_, config_));
|
|
68 | 71 |
|
69 | 72 |
// eyebrow motion generator |
70 |
add_motion_generator(new EyebrowMotionGenerator(joint_interface_)); |
|
73 |
add_motion_generator(new EyebrowMotionGenerator(joint_interface_, config_));
|
|
71 | 74 |
} |
72 | 75 |
|
73 | 76 |
//! add a single motion genrator |
src/server/eye_motion_generator.cpp | ||
---|---|---|
34 | 34 |
|
35 | 35 |
using humotion::server::EyeMotionGenerator; |
36 | 36 |
|
37 |
// saccade detection threshold in deg/s |
|
38 |
const float EyeMotionGenerator::SACCADE_SPEED_THRESHOLD = 15.0; |
|
39 |
|
|
40 | 37 |
//! constructor |
41 |
EyeMotionGenerator::EyeMotionGenerator(JointInterface *j) : |
|
42 |
GazeMotionGenerator(j, 3, 1.0/Server::MOTION_UPDATERATE) { |
|
38 |
EyeMotionGenerator::EyeMotionGenerator(JointInterface *j, Config *cfg) :
|
|
39 |
GazeMotionGenerator(j, cfg, 3, 1.0/Server::MOTION_UPDATERATE) {
|
|
43 | 40 |
} |
44 | 41 |
|
45 | 42 |
|
... | ... | |
58 | 55 |
humotion::Timestamp timestamp) { |
59 | 56 |
// get distance to target: |
60 | 57 |
float distance_abs = fabs(target - current_position); |
58 |
|
|
61 | 59 |
// get max speed: factor can be found in encyc britannica: |
62 | 60 |
// "linear.... being 300° per second for 10° and 500° per second for 30°" (max=700) |
63 |
float max_speed = fmin(700.0, 10.0*distance_abs + 200.0); |
|
61 |
float max_velocity = fmin(700.0, 10.0*distance_abs + 200.0); |
|
62 |
|
|
63 |
// scale and limit max speed |
|
64 |
max_velocity = max_velocity * config->scale_velocity_eye; |
|
65 |
max_velocity = fmin(max_velocity, config->limit_velocity_eye); |
|
66 |
|
|
64 | 67 |
// max accel: use data from: |
65 | 68 |
// "Speed and Accuracy of Saccadic Eye Movements: |
66 | 69 |
// Characteristics of Impulse Variability in the Oculomotor System" |
67 | 70 |
// http://www-personal.umich.edu/~kornblum/files/journal_exp_psych_HPP_15-3.pdf [table 2] |
68 | 71 |
float max_accel = fmin(80000.0, 1526.53*distance_abs + 10245.4); |
69 | 72 |
|
73 |
// scale and limit max acceleration |
|
74 |
max_accel = max_accel * config->scale_acceleration_eye; |
|
75 |
max_accel = fmin(max_accel, config->limit_acceleration_eye); |
|
76 |
|
|
70 | 77 |
// feed reflexxes api with data |
71 | 78 |
reflexxes_set_input(dof, target, current_position, current_velocity, |
72 |
timestamp, max_speed, max_accel);
|
|
79 |
timestamp, max_velocity, max_accel);
|
|
73 | 80 |
} |
74 | 81 |
|
75 | 82 |
//! calculate joint targets |
src/server/eyebrow_motion_generator.cpp | ||
---|---|---|
35 | 35 |
using humotion::server::EyebrowMotionGenerator; |
36 | 36 |
|
37 | 37 |
//! constructor |
38 |
EyebrowMotionGenerator::EyebrowMotionGenerator(JointInterface *j) : MotionGenerator(j) { |
|
38 |
EyebrowMotionGenerator::EyebrowMotionGenerator(JointInterface *j, Config *cfg) : |
|
39 |
MotionGenerator(j, cfg) { |
|
39 | 40 |
} |
40 | 41 |
|
41 | 42 |
|
src/server/eyelid_motion_generator.cpp | ||
---|---|---|
34 | 34 |
// using namespace humotion::server; |
35 | 35 |
|
36 | 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 |
|
37 |
using humotion::server::Config; |
|
47 | 38 |
|
48 | 39 |
//! constructor |
49 |
EyelidMotionGenerator::EyelidMotionGenerator(JointInterface *j) : EyeMotionGenerator(j) { |
|
40 |
EyelidMotionGenerator::EyelidMotionGenerator(JointInterface *j, Config *cfg) : |
|
41 |
EyeMotionGenerator(j, cfg) { |
|
50 | 42 |
saccade_blink_active_ = false; |
51 | 43 |
saccade_blink_requested_ = false; |
52 | 44 |
eyelid_closed_[LEFT] = false; |
... | ... | |
153 | 145 |
void EyelidMotionGenerator::process_saccadic_eyeblinks() { |
154 | 146 |
if (saccade_blink_requested_) { |
155 | 147 |
// 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 |
start_eyeblink(LEFT); |
|
160 |
start_eyeblink(RIGHT); |
|
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);
|
|
161 | 153 |
} |
162 | 154 |
saccade_blink_requested_ = false; |
163 | 155 |
} |
... | ... | |
167 | 159 |
//! -> we want to have an eyeblink every n...m seconds |
168 | 160 |
void EyelidMotionGenerator::process_periodic_eyeblinks() { |
169 | 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 |
|
|
170 | 168 |
// 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)); |
|
169 |
float seconds_to_next_blink = |
|
170 |
config->eyeblink_periodic_distribution_lower + frnd * range; |
|
171 |
|
|
173 | 172 |
periodic_blink_start_time_ = boost::get_system_time() |
174 |
+ boost::posix_time::milliseconds(milliseconds_to_next_blink);
|
|
173 |
+ boost::posix_time::seconds(seconds_to_next_blink);
|
|
175 | 174 |
} |
176 | 175 |
|
177 | 176 |
if (boost::get_system_time() > periodic_blink_start_time_) { |
178 | 177 |
// printf("> periodic eyeblink:\n"); |
179 |
start_eyeblink(LEFT); |
|
180 |
start_eyeblink(RIGHT); |
|
178 |
start_eyeblink(LEFT, config->eyeblink_duration * 1000.0);
|
|
179 |
start_eyeblink(RIGHT, config->eyeblink_duration * 1000.0);
|
|
181 | 180 |
} |
182 | 181 |
} |
183 | 182 |
|
... | ... | |
198 | 197 |
// take care of blocking time |
199 | 198 |
if (eyeblink_active_[LEFT] || eyeblink_active_[RIGHT]) { |
200 | 199 |
eyeblink_blocked_timeout_ = boost::get_system_time() |
201 |
+ boost::posix_time::milliseconds(EYEBLINK_BLOCKING_TIME);
|
|
200 |
+ boost::posix_time::seconds(config->eyeblink_blocked_time);
|
|
202 | 201 |
} |
203 | 202 |
} |
204 | 203 |
|
src/server/gaze_motion_generator.cpp | ||
---|---|---|
29 | 29 |
#include "humotion/server/server.h" |
30 | 30 |
|
31 | 31 |
using humotion::server::GazeMotionGenerator; |
32 |
|
|
33 |
// saccade detection threshold in deg/s |
|
34 |
// const float GazeMotionGenerator::SACCADE_SPEED_THRESHOLD = 15.0; |
|
35 |
// some constants to decide if the requested move will trigger a neck or eye saccade |
|
36 |
const float GazeMotionGenerator::NECK_SACCADE_THRESHOLD = 15.0; // deg |
|
37 |
const float GazeMotionGenerator::EYE_SACCADE_SPEED_THRESHOLD = 15.0; // deg/s |
|
38 |
|
|
39 |
// close to ocolumotor-range? |
|
40 |
// 0.95 = reaching 95% of OMR will trigger a correction |
|
41 |
const float GazeMotionGenerator::OMR_LIMIT_TRIGGERS_NECK_SACCADE = 0.95; |
|
42 |
|
|
32 |
using humotion::server::Config; |
|
43 | 33 |
|
44 | 34 |
//! constructor |
45 |
GazeMotionGenerator::GazeMotionGenerator(JointInterface *j, int dof, float t) |
|
46 |
: ReflexxesMotionGenerator(j, dof, t) { |
|
35 |
GazeMotionGenerator::GazeMotionGenerator(JointInterface *j, Config *cfg, int dof, float t)
|
|
36 |
: ReflexxesMotionGenerator(j, cfg, dof, t) {
|
|
47 | 37 |
} |
48 | 38 |
|
49 | 39 |
//! destructor |
... | ... | |
66 | 56 |
/ (new_gaze_target.timestamp.to_seconds()-requested_gaze_state_.timestamp.to_seconds()); |
67 | 57 |
|
68 | 58 |
// check magnitude and speed of gaze change to detect eye-neck saccades |
69 |
if (dist > NECK_SACCADE_THRESHOLD) {
|
|
59 |
if (dist > config->threshold_angle_neck_saccade) {
|
|
70 | 60 |
// the next saccade has to use neck motion as well |
71 |
if (speed > EYE_SACCADE_SPEED_THRESHOLD) {
|
|
61 |
if (speed > config->threshold_velocity_eye_saccade) {
|
|
72 | 62 |
neck_saccade_requested = true; |
73 | 63 |
} |
74 | 64 |
} else { |
... | ... | |
83 | 73 |
float eye_target_ud = joint_interface_->get_target_position(JointInterface::ID_EYES_BOTH_UD); |
84 | 74 |
|
85 | 75 |
// min/max bounds |
86 |
float left_min = OMR_LIMIT_TRIGGERS_NECK_SACCADE *
|
|
76 |
float left_min = config->threshold_angle_omr_limit *
|
|
87 | 77 |
joint_interface_->get_joint_min(JointInterface::ID_EYES_LEFT_LR); |
88 |
float left_max = OMR_LIMIT_TRIGGERS_NECK_SACCADE *
|
|
78 |
float left_max = config->threshold_angle_omr_limit *
|
|
89 | 79 |
joint_interface_->get_joint_max(JointInterface::ID_EYES_LEFT_LR); |
90 |
float right_min = OMR_LIMIT_TRIGGERS_NECK_SACCADE *
|
|
80 |
float right_min = config->threshold_angle_omr_limit *
|
|
91 | 81 |
joint_interface_->get_joint_min(JointInterface::ID_EYES_RIGHT_LR); |
92 |
float right_max = OMR_LIMIT_TRIGGERS_NECK_SACCADE *
|
|
82 |
float right_max = config->threshold_angle_omr_limit *
|
|
93 | 83 |
joint_interface_->get_joint_max(JointInterface::ID_EYES_RIGHT_LR); |
94 |
float ud_min = OMR_LIMIT_TRIGGERS_NECK_SACCADE *
|
|
84 |
float ud_min = config->threshold_angle_omr_limit *
|
|
95 | 85 |
joint_interface_->get_joint_min(JointInterface::ID_EYES_BOTH_UD); |
96 |
float ud_max = OMR_LIMIT_TRIGGERS_NECK_SACCADE *
|
|
86 |
float ud_max = config->threshold_angle_omr_limit *
|
|
97 | 87 |
joint_interface_->get_joint_max(JointInterface::ID_EYES_BOTH_UD); |
98 | 88 |
|
99 | 89 |
if ( |
... | ... | |
125 | 115 |
float speed_total_r = sqrt(speed_right*speed_right + speed_tilt*speed_tilt); |
126 | 116 |
|
127 | 117 |
// thresholding |
128 |
if ((speed_total_l > EYE_SACCADE_SPEED_THRESHOLD) ||
|
|
129 |
(speed_total_r > EYE_SACCADE_SPEED_THRESHOLD)) {
|
|
118 |
if ((speed_total_l > config->threshold_velocity_eye_saccade) ||
|
|
119 |
(speed_total_r > config->threshold_velocity_eye_saccade)) {
|
|
130 | 120 |
// this is a saccade |
131 | 121 |
saccade_active = true; |
132 | 122 |
} else { |
src/server/middleware_ros.cpp | ||
---|---|---|
109 | 109 |
//! attach to dynamic reconfigure server |
110 | 110 |
void MiddlewareROS::attach_to_reconfiguration_server(ros::NodeHandle priv_nodehandle) { |
111 | 111 |
ROS_DEBUG("connecting to dynamic reconfiguration server"); |
112 |
ros::NodeHandle reconf_node(priv_nodehandle, "configuration"); |
|
112 |
ros::NodeHandle reconf_node(priv_nodehandle, "humotion/configuration");
|
|
113 | 113 |
|
114 | 114 |
reconf_server_ = new dynamic_reconfigure::Server<humotion::humotionConfig>(reconf_node); |
115 | 115 |
reconf_server_->setCallback(boost::bind( |
src/server/motion_generator.cpp | ||
---|---|---|
29 | 29 |
#include "humotion/server/motion_generator.h" |
30 | 30 |
|
31 | 31 |
using humotion::server::MotionGenerator; |
32 |
using humotion::server::Config; |
|
32 | 33 |
|
33 | 34 |
//! constructor |
34 |
MotionGenerator::MotionGenerator(JointInterface *j) { |
|
35 |
MotionGenerator::MotionGenerator(JointInterface *j, Config *cfg) { |
|
36 |
config = cfg; |
|
35 | 37 |
joint_interface_ = j; |
36 | 38 |
last_mouth_target_update_ = boost::posix_time::ptime(boost::posix_time::min_date_time); |
37 | 39 |
last_gaze_target_update_ = boost::posix_time::ptime(boost::posix_time::min_date_time); |
src/server/mouth_motion_generator.cpp | ||
---|---|---|
28 | 28 |
#include "humotion/server/mouth_motion_generator.h" |
29 | 29 |
|
30 | 30 |
using humotion::server::MouthMotionGenerator; |
31 |
using humotion::server::Config; |
|
31 | 32 |
|
32 | 33 |
// minimum mouth opening |
33 | 34 |
const float MouthMotionGenerator::MOUTH_MIN_OPENING = 9.0; // mm |
34 | 35 |
|
35 | 36 |
//! constructor |
36 |
MouthMotionGenerator::MouthMotionGenerator(JointInterface *j) : MotionGenerator(j) { |
|
37 |
MouthMotionGenerator::MouthMotionGenerator(JointInterface *j, Config *cfg) : |
|
38 |
MotionGenerator(j, cfg) { |
|
37 | 39 |
} |
38 | 40 |
|
39 | 41 |
//! destructor |
src/server/neck_motion_generator.cpp | ||
---|---|---|
32 | 32 |
#include "humotion/server/server.h" |
33 | 33 |
|
34 | 34 |
using humotion::server::NeckMotionGenerator; |
35 |
|
|
36 |
const float NeckMotionGenerator::CONST_GUITTON87_A = 4.39/2.0; |
|
37 |
const float NeckMotionGenerator::CONST_GUITTON87_B = 106.0/2.0; |
|
38 |
|
|
39 |
// healthy adult human: 12-15 breaths/min (see "Ganong's review of medical physiology") |
|
40 |
// total: 60/12-15 = 3-5s |
|
41 |
// inhale 1.5-2s |
|
42 |
// exhale 1.5-2s |
|
43 |
// pause 2s |
|
44 |
const float NeckMotionGenerator::CONST_BREATH_PERIOD = 1500.0+1500.0+1500.0; // given in ms |
|
45 |
const float NeckMotionGenerator::CONST_BREATH_AMPLITUDE = 1.0; // degrees |
|
46 |
|
|
35 |
using humotion::server::Config; |
|
47 | 36 |
|
48 | 37 |
//! constructor |
49 |
NeckMotionGenerator::NeckMotionGenerator(JointInterface *j) : |
|
50 |
GazeMotionGenerator(j, 3, 1.0/Server::MOTION_UPDATERATE) { |
|
38 |
NeckMotionGenerator::NeckMotionGenerator(JointInterface *j, Config *cfg) :
|
|
39 |
GazeMotionGenerator(j, cfg, 3, 1.0/Server::MOTION_UPDATERATE) {
|
|
51 | 40 |
breath_time_ = 0.0; |
52 | 41 |
} |
53 | 42 |
|
... | ... | |
62 | 51 |
// we want to have a constant acceleration |
63 | 52 |
// -> triangular wave as speeds -> (x<0.5)? 2*x*x: 1- 2*(1-x)**2 = 4x - 2x**2 - 1 |
64 | 53 |
float breath_offset = 0.0; |
54 |
|
|
65 | 55 |
// 0...1 -> move up, 1..2 -> return, 2..3 -> still |
66 |
float breath_time_normalized = (breath_time_ * 3)/CONST_BREATH_PERIOD;
|
|
56 |
float breath_time_normalized = (breath_time_ * 3)/config->breath_period;
|
|
67 | 57 |
|
68 | 58 |
if (breath_time_normalized <= 0.5) { |
69 | 59 |
// accelerated motion |
70 |
breath_offset = CONST_BREATH_AMPLITUDE * (2.0 * pow(breath_time_normalized, 2));
|
|
60 |
breath_offset = config->breath_amplitude * (2.0 * pow(breath_time_normalized, 2));
|
|
71 | 61 |
} else if (breath_time_normalized <= 1.0) { |
72 | 62 |
// deaccelerate |
73 |
breath_offset = CONST_BREATH_AMPLITUDE * (1.0 - 2.0 * pow(1.0 - breath_time_normalized, 2)); |
|
63 |
breath_offset = config->breath_amplitude * (1.0 - 2.0 |
|
64 |
* pow(1.0 - breath_time_normalized, 2)); |
|
74 | 65 |
} else if (breath_time_normalized <= 1.5) { |
75 | 66 |
// accelerate again |
76 |
breath_offset = CONST_BREATH_AMPLITUDE * (1.0 - (2.0 * pow(breath_time_normalized-1, 2)));
|
|
67 |
breath_offset = config->breath_amplitude * (1.0 - (2.0 * pow(breath_time_normalized-1, 2)));
|
|
77 | 68 |
} else if (breath_time_normalized <= 2.0) { |
78 |
breath_offset = CONST_BREATH_AMPLITUDE * (2.0 * pow(2.0 - breath_time_normalized, 2));
|
|
69 |
breath_offset = config->breath_amplitude * (2.0 * pow(2.0 - breath_time_normalized, 2));
|
|
79 | 70 |
} else if (breath_time_normalized <= 3.0) { |
80 | 71 |
// pause for some time |
81 | 72 |
breath_offset = 0; |
... | ... | |
83 | 74 |
|
84 | 75 |
// fetch next time |
85 | 76 |
breath_time_ += 1000.0/Server::MOTION_UPDATERATE; |
86 |
if (breath_time_ >= CONST_BREATH_PERIOD) { |
|
87 |
breath_time_ -= CONST_BREATH_PERIOD; |
|
77 |
|
|
78 |
if (breath_time_ >= config->breath_period) { |
|
79 |
breath_time_ -= config->breath_period; |
|
88 | 80 |
} |
89 | 81 |
|
90 | 82 |
return breath_offset; |
... | ... | |
205 | 197 |
// get distance to target |
206 | 198 |
float distance_abs = fabs(target - current_position); |
207 | 199 |
|
208 |
// get max speed: according to [guitton87] there is a relation
|
|
200 |
// get max speed: according to the equation Hmax from [guitton87] there is a linear relation
|
|
209 | 201 |
// between distance_abs and v_max_head: |
210 | 202 |
// v_max = 4.39 * d_total + 106.0 (in degrees) |
211 |
float max_speed = (CONST_GUITTON87_A * distance_abs + CONST_GUITTON87_B); |
|
203 |
float max_velocity = 4.39 * distance_abs + 106.0; |
|
204 |
|
|
205 |
// scale and limit max speed: |
|
206 |
max_velocity = max_velocity * config->scale_velocity_neck; |
|
207 |
max_velocity = fmin(max_velocity, config->limit_velocity_neck); |
|
212 | 208 |
|
213 | 209 |
// max accel: assuming linear acceleration we have: |
214 | 210 |
/* v ^ _ |
... | ... | |
225 | 221 |
// and therefore |
226 | 222 |
// a = v_max^2 / d_total |
227 | 223 |
float max_accel = 0.0; |
224 |
|
|
228 | 225 |
if (distance_abs > 0.0) { |
229 |
max_accel = pow(max_speed, 2) / distance_abs;
|
|
226 |
max_accel = pow(max_velocity, 2) / distance_abs;
|
|
230 | 227 |
} |
231 | 228 |
|
232 |
// smoother motion |
|
233 |
max_accel = max_accel * 0.7; |
|
229 |
// scale and limit acceleration |
|
230 |
max_accel = max_accel * config->scale_acceleration_neck; |
|
231 |
max_accel = fmin(max_accel, config->limit_acceleration_neck); |
|
234 | 232 |
|
235 |
// limit maximum acceleration to reduce noise FIXME! |
|
236 |
if (max_accel > 1000) { |
|
237 |
max_accel = 1000; |
|
238 |
} |
|
239 | 233 |
// printf("MAX SPEED %4.2f / max accel %4.2f\n",max_speed, max_accel); |
240 | 234 |
|
241 | 235 |
// feed reflexxes api with data |
242 | 236 |
reflexxes_set_input(dof, target, current_position, current_velocity, |
243 |
timestamp, max_speed, max_accel);
|
|
237 |
timestamp, max_velocity, max_accel);
|
|
244 | 238 |
} |
src/server/reflexxes_motion_generator.cpp | ||
---|---|---|
28 | 28 |
#include "humotion/server/eye_motion_generator.h" |
29 | 29 |
|
30 | 30 |
using humotion::server::ReflexxesMotionGenerator; |
31 |
using humotion::server::Config; |
|
31 | 32 |
|
32 | 33 |
//! constructor |
33 |
ReflexxesMotionGenerator::ReflexxesMotionGenerator(JointInterface *j, int dof, float t) : |
|
34 |
MotionGenerator(j) { |
|
34 |
ReflexxesMotionGenerator::ReflexxesMotionGenerator(JointInterface *j, Config *c, int dof, float t) :
|
|
35 |
MotionGenerator(j, c) {
|
|
35 | 36 |
dof_count = dof; |
36 | 37 |
|
37 | 38 |
// create Reflexxes API for <dof> DOF actuator |
Also available in: Unified diff