4 * @author Michal Sojka, Jose Maria Martin Laguna
14 #include <robot_eb2008.h>
16 #include <path_planner.h>
18 #include <movehelper_eb2008.h>
22 #include "robot_config.h"
25 /*******************************************************************************
26 * Controller thread and helper functions for that thread
27 *******************************************************************************/
31 const struct balet_params k = {
32 p_tangent: 2, // dx gain
33 p_angle: 2, // dphi gain
34 p_perpen: 10 // dy gain
37 #define MAX_POS_ERROR_M 0.5
38 #define CLOSE_TO_TARGET_M 0.1
39 #define MAX_WAIT_FOR_CLOSE_MS 2000
41 //#define MOTION_PERIOD_NS (500/*ms*/*1000*1000)
42 #define MOTION_PERIOD_NS (50/*ms*/*1000*1000)
43 #define SIG_TIMER (SIGRTMIN+0)
44 #define SIG_NEWDATA (SIGRTMIN+1)
47 static pthread_t thr_trajectory_follower;
48 static struct timeval tv_start; /**< Absolute time, when trajectory started. */
50 // Trajectory recalculation thread and synchoronization
51 pthread_t thr_trajctory_recalculation;
52 sem_t start_recalculation;
53 sem_t recalculation_not_running;
55 /** Stores the actually followed trajectory object */
56 static Trajectory *actual_trajectory = NULL;
57 bool actual_trajectory_reverse;
58 pthread_mutex_t actual_trajectory_lock = PTHREAD_MUTEX_INITIALIZER;
59 pthread_mutex_t switch_to_trajectory_lock = PTHREAD_MUTEX_INITIALIZER;
62 // Target position of the current movement
63 static struct move_target current_target;
66 //static Trajectory *switch_to_trajectory = NULL;
67 //static double switch_time;
71 static void delete_actual_trajectory()
74 pthread_mutex_lock(&actual_trajectory_lock);
75 old = actual_trajectory;
76 actual_trajectory = NULL;
77 pthread_mutex_unlock(&actual_trajectory_lock);
78 robot_send_speed(0,0);
79 if (old) delete(actual_trajectory);
82 /** Sends events from follower thread to FSM. */
83 static void notify_fsm(bool done, double error)
85 static bool done_sent;
87 if (error > MAX_POS_ERROR_M) {
88 FSM_SIGNAL(MOTION, EV_TRAJECTORY_LOST, NULL);
91 if (error < CLOSE_TO_TARGET_M) {
92 FSM_SIGNAL(MOTION, EV_TRAJECTORY_DONE_AND_CLOSE, NULL);
93 } else if (!done_sent) {
95 FSM_SIGNAL(MOTION, EV_TRAJECTORY_DONE, NULL);
103 static void check_for_collision_in_future(Trajectory *traj, double current_time)
106 struct map *map = robot.map;
111 traj->getRefPos(current_time+0.5/*s*/, future_pos);
113 /* Ignore obstacles when turning */
114 if (fabs(future_pos.v) < 0.01)
117 x = future_pos.x + cos(future_pos.phi)*ROBOT_AXIS_TO_BRUSH_M;
118 y = future_pos.y + sin(future_pos.phi)*ROBOT_AXIS_TO_BRUSH_M;
120 ShmapPoint2Cell(x, y, &xcell, &ycell, &valid);
123 if (map->cells[ycell][xcell].detected_obstacle > 0) {
124 if (sem_trywait(&recalculation_not_running) == 0) {
125 sem_post(&start_recalculation);
130 static void do_control()
132 double speedl, speedr;
133 pthread_mutex_lock(&actual_trajectory_lock);
134 Trajectory *w = actual_trajectory;
136 Pos ref_pos, est_pos, balet_out;
141 // Calculate reference position
142 gettimeofday(&tv, NULL);
143 t = (double)(tv.tv_usec - tv_start.tv_usec) / 1000000.0;
144 t += (tv.tv_sec - tv_start.tv_sec);
146 if (robot.obstacle_avoidance_enabled)
147 check_for_collision_in_future(w, t);
149 done = w->getRefPos(t, ref_pos);
152 robot.ref_pos.x = ref_pos.x;
153 robot.ref_pos.y = ref_pos.y;
154 robot.ref_pos.phi = ref_pos.phi;
155 ROBOT_UNLOCK(ref_pos);
158 est_pos.x = robot.est_pos.x;
159 est_pos.y = robot.est_pos.y;
160 est_pos.phi = robot.est_pos.phi;
161 ROBOT_UNLOCK(est_pos);
163 #ifdef CONFIG_OPEN_LOOP
164 // We don't use any feedback now. It is
165 // supposed that the estimated position is
166 // equal to the reference position.
167 robot_set_est_pos_notrans(ref_pos.x, ref_pos.y, ref_pos.phi);
168 est_pos.x = ref_pos.x;
169 est_pos.y = ref_pos.y;
170 est_pos.phi = ref_pos.phi;
174 printf("rx=%5.02f ry=%5.02f, rphi=%4.0f v=%-4.02f omega=%-4.02f\r", ref_pos.x, ref_pos.y, ref_pos.phi/M_PI*180, ref_pos.v, ref_pos.omega);
177 // Call the controller
179 error = balet(ref_pos, est_pos, k, balet_out);
180 speedl = balet_out.v - ROBOT_ROTATION_RADIUS_M*balet_out.omega;
181 speedr = balet_out.v + ROBOT_ROTATION_RADIUS_M*balet_out.omega;
182 notify_fsm(done, error);
189 // Apply controller output
190 robot_send_speed(speedl, speedr);
191 pthread_mutex_unlock(&actual_trajectory_lock);
195 * A thread running the controller.
197 * This (high priority) thread executes the motion control
198 * algorithm. It calculates repference position based on actual
199 * trajectory and current time. Then it calls "balet" controller to
206 static void *thread_trajectory_follower(void *arg)
208 struct itimerspec ts;
210 struct sigevent sigevent;
213 ts.it_value.tv_sec = 0;
214 ts.it_value.tv_nsec = MOTION_PERIOD_NS;
215 ts.it_interval.tv_sec = 0;
216 ts.it_interval.tv_nsec = MOTION_PERIOD_NS;
218 sigemptyset(&sigset);
219 sigaddset(&sigset, SIG_TIMER);
220 sigaddset(&sigset, SIG_NEWDATA);
222 pthread_sigmask(SIG_BLOCK, &sigset, (sigset_t*) NULL);
224 sigevent.sigev_notify = SIGEV_SIGNAL;
225 sigevent.sigev_signo = SIG_TIMER;
226 sigevent.sigev_value.sival_int = 0;
228 if (timer_create(CLOCK_REALTIME, &sigevent, &timer) < 0) {
229 perror("move: timer_create");
233 if (timer_settime(timer, 0, &ts, NULL) < 0) {
234 perror("move: timer_settimer");
240 // Wait for start of new period or new data
241 sigwait(&sigset, &sig);
242 //DBG("signal %d ", sig - SIGRTMIN);
248 * Tells trajctory_follower to start moving along trajectory @c t.
250 * @param t Trajectory to follow.
252 static void go(Trajectory *t)
255 pthread_mutex_lock(&actual_trajectory_lock);
256 old = actual_trajectory;
257 gettimeofday(&tv_start, NULL);
258 actual_trajectory = t;
259 pthread_mutex_unlock(&actual_trajectory_lock);
266 delete_actual_trajectory();
267 pthread_kill(thr_trajectory_follower, SIG_NEWDATA);
272 TARGET_NOT_REACHABLE,
276 * @brief Calculates and simplify a path to goal position avoiding obstacles.
277 * @return True on succes, false on error.
279 static enum target_status new_goal(struct move_target *target)
281 enum target_status ret;
282 double startx, starty, angle;
283 PathPoint * path = NULL;
284 PathPoint * tmp_point = NULL;
286 struct TrajectoryConstraints tc;
291 /// Get actual position.
293 startx = robot.est_pos.x;
294 starty = robot.est_pos.y;
297 start.phi = robot.est_pos.phi;
298 ROBOT_UNLOCK(est_pos);
301 //DBG("Going from (%.2f, %.2f) to (%.2f, %.2f)\n", startx, starty, goalx, goaly);
302 /// Calculates the path between actual position to goal position. If the goal is not reachable returns -1.
303 switch (path_planner(startx, starty, target->x, target->y, &path, &angle)) {
304 case PP_ERROR_MAP_NOT_INIT:
305 ret = TARGET_ERROR; break;
306 case PP_ERROR_GOAL_IS_OBSTACLE:
307 case PP_ERROR_GOAL_NOT_REACHABLE:
308 ret = TARGET_NOT_REACHABLE; break;
310 ret = TARGET_OK; break;
313 if (ret != TARGET_OK)
316 bool backward = false;
317 Trajectory *t = new Trajectory(tc, backward);
319 // Add this path to the trajectory.
320 for (tmp_point = path; tmp_point!=NULL; tmp_point=tmp_point->next) {
321 //DBG("ADDDING POINT (%f, %f)\n", tmp_point->x, tmp_point->y);
322 t->addPoint(tmp_point->x, tmp_point->y);
325 t->finalHeading = target->h;
326 freePathMemory(path);
328 if (t->prepare(start)) {
339 * Starts moving on trajectory prepared by functions in
340 * movehelper.cc. In case of error it sends the proper event to MAIN
343 * @return true on succes, false on error.
345 static enum target_status new_trajectory(Trajectory *t)
350 printf("ERROR: No trajectory\n");
354 pos.x = robot.est_pos.x;
355 pos.y = robot.est_pos.y;
356 pos.phi = robot.est_pos.phi;
357 ROBOT_UNLOCK(est_pos);
359 if (t->prepare(pos)) {
368 static enum target_status new_target(struct move_target *target)
370 enum target_status ret;
371 if (target->trajectory) {
372 ret = new_trajectory((Trajectory*)target->trajectory);
373 // Trajectory is deleted by somebody else
374 target->trajectory = NULL;
376 ret = new_goal(target);
378 if (ret != TARGET_ERROR) {
379 current_target = *target;
384 /* This recalculation must only be triggered from mvoement state. */
385 void *thread_trajectory_realculation(void *arg)
388 sem_wait(&start_recalculation);
389 /* TODO: Different start for planning than esitmated position */
390 enum target_status ret = new_goal(¤t_target);
395 printf("Target error on recalculation_in_progress\n");
396 FSM_SIGNAL(MOTION, EV_RECALCULATION_FAILED, NULL);
398 case TARGET_NOT_REACHABLE:
399 FSM_SIGNAL(MOTION, EV_RECALCULATION_FAILED, NULL);
402 sem_post(&recalculation_not_running);
406 /*******************************************************************************
408 *******************************************************************************/
410 FSM_STATE_DECL(movement);
411 FSM_STATE_DECL(close_to_target);
412 FSM_STATE_DECL(wait_for_command);
413 FSM_STATE_DECL(reset_mcl);
414 FSM_STATE_DECL(wait_and_try_again);
419 pthread_attr_t tattr;
423 if (FSM_EVENT == EV_ENTRY) {
424 actual_trajectory = NULL;
426 // Trajectory follower thread
427 pthread_attr_init (&tattr);
428 pthread_attr_getschedparam (&tattr, ¶m);
429 param.sched_priority = THREAD_PRIO_TRAJ_FOLLOWER;
430 ret = pthread_attr_setschedparam (&tattr, ¶m);
432 perror("move_init: pthread_attr_setschedparam(follower)");
435 ret = pthread_create(&thr_trajectory_follower, &tattr, thread_trajectory_follower, NULL);
437 perror("move_init: pthread_create");
441 // Trajectory follower thread
442 sem_init(&recalculation_not_running, 0, 1);
443 sem_init(&start_recalculation, 0, 0);
444 pthread_attr_init (&tattr);
445 pthread_attr_getschedparam (&tattr, ¶m);
446 param.sched_priority = THREAD_PRIO_TRAJ_RECLAC;
447 ret = pthread_attr_setschedparam (&tattr, ¶m);
449 perror("move_init: pthread_attr_setschedparam(follower)");
452 ret = pthread_create(&thr_trajctory_recalculation, &tattr, thread_trajectory_realculation, NULL);
454 perror("move_init: pthread_create");
458 FSM_TRANSITION(wait_for_command);
462 #define FIND_NEW_WAY(target) \
464 enum target_status ret = new_goal(target); \
467 FSM_TRANSITION(movement); \
470 printf("Target error\n"); \
471 FSM_SIGNAL(MAIN, EV_MOTION_DONE, NULL); \
473 case TARGET_NOT_REACHABLE: \
474 FSM_TRANSITION(wait_and_try_again); \
480 FSM_STATE(wait_for_command)
482 enum target_status ret;
488 ret = new_target((struct move_target*)FSM_EVENT_PTR);
491 FSM_TRANSITION(movement);
494 printf("Target error\n");
495 FSM_SIGNAL(MAIN, EV_MOTION_DONE, NULL);
497 case TARGET_NOT_REACHABLE:
498 FSM_TRANSITION(wait_and_try_again);
511 case EV_TRAJECTORY_DONE:
512 FSM_TRANSITION(close_to_target);
514 case EV_TRAJECTORY_DONE_AND_CLOSE:
515 FSM_SIGNAL(MAIN, EV_MOTION_DONE, NULL);
516 FSM_TRANSITION(wait_for_command);
518 case EV_RECALCULATION_FAILED:
519 FSM_TRANSITION(wait_and_try_again);
521 case EV_TRAJECTORY_LOST:
522 FSM_TRANSITION(reset_mcl);
525 FSM_TRANSITION(wait_for_command);
533 case EV_FOUND_AFTER_RESET:
534 DBG_PRINT_EVENT("unhandled event");
539 FSM_STATE(close_to_target)
543 if (FSM_EVENT_INT == EV_MOVE_STOP)
544 FSM_TRANSITION(wait_for_command);
547 FSM_TIMER(MAX_WAIT_FOR_CLOSE_MS);
549 case EV_TRAJECTORY_DONE_AND_CLOSE:
550 FSM_SIGNAL(MAIN, EV_MOTION_DONE, NULL);
551 FSM_TRANSITION(wait_for_command);
553 case EV_TRAJECTORY_LOST:
555 FSM_TRANSITION(reset_mcl);
558 FSM_TRANSITION(wait_for_command);
563 case EV_TRAJECTORY_DONE:
565 case EV_FOUND_AFTER_RESET:
566 case EV_RECALCULATION_FAILED:
567 DBG_PRINT_EVENT("unhandled event");
577 FSM_SIGNAL(LOC, EV_RESET, NULL);
579 case EV_FOUND_AFTER_RESET:
580 FIND_NEW_WAY(¤t_target);
583 FSM_TRANSITION(wait_for_command);
587 case EV_TRAJECTORY_DONE:
589 case EV_TRAJECTORY_DONE_AND_CLOSE:
590 case EV_TRAJECTORY_LOST:
593 case EV_RECALCULATION_FAILED:
594 DBG_PRINT_EVENT("unhandled event");
599 FSM_STATE(wait_and_try_again)
603 stop(); //FIXME: Not hard stop
607 FIND_NEW_WAY(¤t_target);
610 FSM_TRANSITION(wait_for_command);
612 case EV_TRAJECTORY_DONE:
614 case EV_TRAJECTORY_DONE_AND_CLOSE:
615 case EV_TRAJECTORY_LOST:
617 case EV_FOUND_AFTER_RESET:
618 case EV_RECALCULATION_FAILED:
619 DBG_PRINT_EVENT("unhandled event");