]> rtime.felk.cvut.cz Git - eurobot/public.git/blob - src/robofsm/motion-control.cc
Merge branch 'maint-demo'
[eurobot/public.git] / src / robofsm / motion-control.cc
1 /**
2  * @file   motion-control.cc
3  * @author Michal Sojka <sojkam1@fel.cvut.cz>, Petr Beneš
4  * @date   Fri Mar 20 10:36:59 2009
5  * 
6  * @brief  
7  * 
8  * 
9  */
10
11 //#define MOTION_DEBUG
12
13 #ifdef MOTION_DEBUG
14     #define DBG(format, ...) ul_logdeb(format, ##__VA_ARGS__)
15     #define DBGflush() fflush(stdout)
16 #else
17     #define DBG(format, ...)
18     #define DBGflush()
19 #endif
20
21 #include <sys/time.h>
22 #include <time.h>
23 #include "trgen.h"
24 #include "balet.h"
25 #include "robodata.h"
26 #include <robot.h>
27 #include <pthread.h>
28 #include <path_planner.h>
29 #include <signal.h>
30 #include <movehelper.h>
31 #include <sharp.h>
32 #include <unistd.h>
33 #include <map.h>
34 #include "robot_config.h"
35 #include <robomath.h>
36 #include <ul_log.h>
37
38 UL_LOG_CUST(ulogd_motion_control); /* Log domain name = ulogd + name of the file */
39
40 #define MOTION_CONTROL
41 #include "motion-control.h"
42
43 /* ULoPoS EKF */
44 #include <ekf.h>
45 #include <geom.h>
46
47 /* ULoPoS constants (-%-TEMPERATURE-%- dependent!) */
48 #define SOUND_VELOCITY   (331.3+0.606*20)
49 #define XCORR2METER      (SOUND_VELOCITY*(127.0/508.0)/3000.0)
50 #define D_MAX            (XCORR2METER*508.0)
51
52 /*******************************************************************************
53  * Controller thread and helper functions for that thread
54  *******************************************************************************/
55
56 /**
57  * If the distance of robot's estimated position from robot's
58  * requested position if above this value, the robot lost and we try
59  * to reset localization.
60  */
61 #define MAX_POS_ERROR_M 0.25
62
63 /**
64  * If trajectory end is reached and robot's estimated position is
65  * closer than this distance, the movement is considered as "done".
66  */
67 #define CLOSE_TO_TARGET_M 0.1
68
69 //Controller gains
70 const struct balet_params k = {
71   p_tangent:  3,                // dx gain
72   p_angle: 2,                   // dphi gain
73   p_perpen: 5                   // dy gain
74 //   p_tangent:  0.2,           // dx gain
75 //   p_angle: 0.15,                     // dphi gain
76 //   p_perpen: 1                        // dy gain
77 };
78
79 #define MOTION_PERIOD_NS (50/*ms*/*1000*1000)
80 #define MEASURE_TIMEOUT_NS (100/*ms*/*1000*1000)
81
82 #define SIG_DO_CONTROL_NOW (SIGRTMIN+1)
83
84 // Global varibles
85 static pthread_t thr_trajectory_follower;
86 static struct timeval tv_start; /**< Absolute time, when trajectory started. */
87
88 /** Stores the actually followed trajectory object */
89 static Trajectory *actual_trajectory;
90 static pthread_mutex_t actual_trajectory_lock;
91
92 // Trajectory recalculation 
93 sem_t recalculation_not_running;
94 sem_t measurement_received;
95
96 /**
97  * Determines way of thread_trajectory_follower() operation:
98  * - 0 measurement doesn't work, controller invocation based on time (formerly CONFIG_OPEN_LOOP)
99  * - 2 measurement works, controller invocation based on sem_post
100  * - 1 measurement doesn't work and stop() was called
101  */
102 int measurement_ok = 0;
103
104
105
106 static void delete_actual_trajectory()
107 {
108         Trajectory *old;
109         pthread_mutex_lock(&actual_trajectory_lock);
110         old = actual_trajectory;
111         actual_trajectory = NULL;
112         pthread_mutex_unlock(&actual_trajectory_lock);
113         robot_send_speed(0,0);
114         if (old) delete(old);
115 }
116
117 /** Sends events from follower thread to FSM. */
118 static void notify_fsm(bool done, double error)
119 {
120         static bool done_sent;
121         static bool lost_sent = false;
122
123         if (error > MAX_POS_ERROR_M) {
124                 if (!lost_sent) {
125                         lost_sent = true;
126                         FSM_SIGNAL(MOTION, EV_TRAJECTORY_LOST, NULL);
127                 }
128         } else {
129                 lost_sent = false;
130                 if (done) {
131                         if (error < CLOSE_TO_TARGET_M) {
132                                 FSM_SIGNAL(MOTION, EV_TRAJECTORY_DONE_AND_CLOSE, NULL);
133                         } else if (!done_sent) {
134                                 done_sent = true;
135                                 FSM_SIGNAL(MOTION, EV_TRAJECTORY_DONE, NULL);
136                         }
137                 } else {
138                         done_sent = false;
139                 }
140         }
141 }
142
143 static void check_for_collision_in_future(Trajectory *traj, double current_time)
144 {
145         Pos future_pos;
146         struct map *map = robot.map;
147         int xcell, ycell;
148         double x, y;
149         bool valid;
150         unsigned i;
151 //      const double times[] = { 0.5, 0.3, 0.1 }; // seconds
152         const double times[] = { 0.3, 0.4, 0.5, 0.7, 0.9, 1.1 }; // seconds
153
154
155         for (i=0; i < sizeof(times)/sizeof(times[0]); i++) {
156                 traj->getRefPos(current_time+times[i], future_pos);
157
158                 /* Ignore obstacles when turning */
159                 if (fabs(future_pos.v) < 0.01)
160                         continue;
161
162                 x = future_pos.x + cos(future_pos.phi)*ROBOT_AXIS_TO_FRONT_M;
163                 y = future_pos.y + sin(future_pos.phi)*ROBOT_AXIS_TO_FRONT_M;
164
165                 ShmapPoint2Cell(x, y, &xcell, &ycell, &valid);
166                 if (!valid)
167                         continue;
168                 if (map->cells[ycell][xcell].detected_obstacle > 0) {
169                         if (sem_trywait(&recalculation_not_running) == 0) {
170                                 FSM_SIGNAL(MOTION, EV_OBSTACLE, NULL);
171                                 break;
172                         }
173                 }
174         }
175 }
176
177 static void do_control()
178 {
179         double speedl, speedr;
180
181         double t;
182         struct timeval tv;
183
184         // Calculate reference position
185         /***FIXME:should not rely on system clock, the period is fixed***/
186         gettimeofday(&tv, NULL);
187         t = (double)(tv.tv_usec - tv_start.tv_usec) / 1000000.0;
188         t += (tv.tv_sec - tv_start.tv_sec);
189 /*
190         // check for new trajectory to switch
191         // only if the trajectory is already prepared
192         if (switch_to_trajectory != NULL && t >= switch_time) {
193                 pthread_mutex_lock(&switch_to_trajectory_lock);
194
195                 DBG("SWITCHING to new trajectory\n");
196
197                 go(switch_to_trajectory);
198                 // nothing prepared now
199                 switch_to_trajectory = NULL;
200                 pthread_mutex_unlock(&switch_to_trajectory_lock);
201         }
202 */
203         pthread_mutex_lock(&actual_trajectory_lock);
204         Trajectory *w = actual_trajectory;
205         if (w) {
206                 Pos ref_pos, est_pos, balet_out;
207                 bool done;
208
209                 // Calculate reference position
210                 gettimeofday(&tv, NULL);
211                 t = (double)(tv.tv_usec - tv_start.tv_usec) / 1000000.0;
212                 t += (tv.tv_sec - tv_start.tv_sec);
213
214                 // if switch_to_trajectory is being prepared, it can not stop calculation
215                 // and start to count again, it could evoke overloading
216                 if (robot.obstacle_avoidance_enabled)
217                         check_for_collision_in_future(w, t);
218
219
220                 done = w->getRefPos(t, ref_pos);
221
222                 if (ref_pos.omega > actual_trajectory->constr.maxomega)
223                         DBG("Omega constraint problem %lf, max %lf -------------------- \n", ref_pos.omega, actual_trajectory->constr.maxomega);
224
225                 ROBOT_LOCK(ref_pos);
226                 robot.ref_pos.x = ref_pos.x;
227                 robot.ref_pos.y = ref_pos.y;
228                 robot.ref_pos.phi = ref_pos.phi;
229                 ROBOT_UNLOCK(ref_pos);
230
231                 robot_get_est_pos(&est_pos.x, &est_pos.y, &est_pos.phi);
232
233 #ifdef MOTION_PRINT_REF
234                 static double last_t;
235                 if (t < last_t) last_t = t; // Switched to a new trajectory
236                 ul_logdeb("rx=%5.02f ry=%5.02f, rphi=%4.0f v=%-4.02f  omega=%-4.02f, time=%lf dt=%lf \n", ref_pos.x, ref_pos.y, ref_pos.phi/M_PI*180, ref_pos.v, ref_pos.omega, t, t-last_t);
237                 fflush(stdout);
238                 last_t = t;
239 #endif
240
241                 // Call the controller
242                 double error;
243                 error = balet(ref_pos, est_pos, k, balet_out);
244                 speedl = balet_out.v - ROBOT_ROTATION_RADIUS_M*balet_out.omega;
245                 speedr = balet_out.v + ROBOT_ROTATION_RADIUS_M*balet_out.omega;
246                 notify_fsm(done, error);
247         } else {
248                 speedl = 0;
249                 speedr = 0;
250         }
251
252
253         // Apply controller output
254         robot_send_speed(speedl, speedr);
255         pthread_mutex_unlock(&actual_trajectory_lock);
256 }
257
258 static inline void next_period(struct timespec *next, long long interval_ns)
259 {
260         next->tv_nsec += interval_ns;
261         if (next->tv_nsec >= 1000000000) {
262                 next->tv_sec++;
263                 next->tv_nsec -= 1000000000;
264         }
265 }
266
267 /**
268  * A thread running the controller.
269  *
270  * This (high priority) thread executes the motion control
271  * algorithm. It calculates repference position based on actual
272  * trajectory and current time. Then it calls "balet" controller to
273  * close feedback.
274  *
275  * @param arg
276  *
277  * @return
278  */
279 void *thread_trajectory_follower(void *arg)
280 {
281         struct timespec next;
282         int ret;
283         struct sched_param param;
284
285         param.sched_priority = THREAD_PRIO_TRAJ_FOLLOWER;
286         if (pthread_setschedparam(pthread_self(), SCHED_FIFO, &param) != 0)
287                 perror("Warning: Cannot set RT priority for thread_prio_traj_follower()");
288
289         clock_gettime(CLOCK_REALTIME, &next);
290         
291         while (1) {
292                 ret = sem_timedwait(&measurement_received, &next);
293                 
294                 if (ret == -1 && errno == ETIMEDOUT) {
295                         next_period(&next, MOTION_PERIOD_NS);
296                         if (measurement_ok) {
297                                 if (measurement_ok == 2) {
298                                         fprintf(stderr, "problem: measurement timeout!!!!!!!!!!!");
299                                 }
300                                 measurement_ok--;
301                         }               
302                 } else {
303                         next_period(&next, MEASURE_TIMEOUT_NS);
304                         if (measurement_ok < 2) {
305                                 measurement_ok++;
306                         }
307                 }
308                 do_control();
309         }
310         return NULL;
311 }
312
313 /**
314  * Tells trajctory_follower to start moving along trajectory @c t.
315  *
316  * @param t Trajectory to follow.
317  * @param append_time Relative time from the beginning of the @c actual_trajectory
318  * when to append the new one
319  */
320 void go(Trajectory *t, double append_time)
321 {
322         pthread_mutex_lock(&actual_trajectory_lock);
323         Trajectory *old;
324         if (actual_trajectory && append_time != 0) {
325                 // trajectory only connects a new one in some specific time
326                 if(!actual_trajectory->appendTrajectory(*t, append_time))
327                         DBG("Can not append trajectory\n");
328         } else {
329                 // trajectory starts from zero time
330                 old = actual_trajectory;
331                 gettimeofday(&tv_start, NULL);
332                 actual_trajectory = t;
333 #ifdef MOTION_LOG               
334                 t->logTraj(tv_start.tv_sec + 1e-6*tv_start.tv_usec);
335 #endif
336                 if (old)
337                         delete(old);
338         }
339         pthread_mutex_unlock(&actual_trajectory_lock);
340 }
341
342 /**
343  * switches to newly calculated trajectory to go on it at specific time
344  */
345 /*void switch_trajectory_at(Trajectory *t, double time)
346 {
347         pthread_mutex_lock(&switch_to_trajectory_lock);
348         switch_to_trajectory = t;
349         switch_time = time;    
350         pthread_mutex_unlock(&switch_to_trajectory_lock);
351
352         struct timeval tv;
353         gettimeofday(&tv, NULL);
354         double tm = (double)(tv.tv_usec - tv_start.tv_usec) / 1000000.0;
355         tm += (tv.tv_sec - tv_start.tv_sec);
356         if (switch_time <= tm)
357                 DBG("//// BAD SWITCH ////");
358 }
359 */
360 void stop()
361 {
362         delete_actual_trajectory();
363
364         // Interrupt sem_timedwait() in thread_trajectory_follower(),
365         // so we stop immediately.
366         sem_post(&measurement_received);
367 }
368
369 /** 
370  * Initializes motion controller.
371  * 
372  * 
373  * @return Zero on success, non-zero otherwise.
374  */
375 int motion_control_init()
376 {
377         pthread_mutexattr_t mattr;
378         int ret;
379
380         actual_trajectory = NULL;
381         //switch_to_trajectory = NULL;
382
383
384         ret = pthread_mutexattr_init(&mattr);
385 #ifdef HAVE_PRIO_INHERIT
386         ret = pthread_mutexattr_setprotocol(&mattr, PTHREAD_PRIO_INHERIT);
387 #endif
388         pthread_mutex_init(&actual_trajectory_lock, &mattr);
389
390         sem_init(&recalculation_not_running, 0, 1);
391
392         // Trajectory follower thread
393         sem_init(&measurement_received, 0, 0);
394         ret = pthread_create(&thr_trajectory_follower, NULL, thread_trajectory_follower, NULL);
395         if(ret) {
396                 perror("move_init: pthread_create");
397                 goto err;
398         }
399
400         return 0;
401   err:
402         return ret;
403 }
404
405 void motion_control_done()
406 {
407         pthread_cancel(thr_trajectory_follower);
408         pthread_join(thr_trajectory_follower, NULL);
409
410         robot.orte.motion_speed.right = 0;
411         robot.orte.motion_speed.left = 0;
412         ORTEPublicationSend(robot.orte.publication_motion_speed);                       
413 }
414
415
416 void get_future_pos(double rel_time_sec, Pos &pos, double &switch_time)
417 {
418         struct timeval tv;
419
420         gettimeofday(&tv, NULL);
421         switch_time = (double)(tv.tv_usec - tv_start.tv_usec) / 1000000.0;
422         switch_time += (tv.tv_sec - tv_start.tv_sec);
423         switch_time += rel_time_sec;
424
425         pthread_mutex_lock(&actual_trajectory_lock);
426         if (actual_trajectory) {
427                 actual_trajectory->getRefPos(switch_time, pos);
428                 pthread_mutex_unlock(&actual_trajectory_lock);
429         } else {
430                 // Robot doesn't move, so return current position
431                 pthread_mutex_unlock(&actual_trajectory_lock);
432
433                 robot_get_est_pos(&pos.x, &pos.y, &pos.phi);
434                 pos.v = 0;
435                 pos.omega = 0;
436         }
437 }