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