4 * @author Michal Sojka, Jose Maria Martin Laguna, Petr Beneš
17 #include <path_planner.h>
19 #include <movehelper.h>
23 #include "robot_config.h"
25 #include "motion-control.h"
29 UL_LOG_CUST(ulogd_fsmmove); /* Log domain name = ulogd + name of the file */
32 #define DBG(format, ...) ul_logdeb(format, ##__VA_ARGS__)
34 #define DBG(format, ...)
37 #define MAX_WAIT_FOR_CLOSE_MS 2000
39 // time to calculate new trajectory [seconds]
40 #define TRGEN_DELAY 0.3
43 // Target position of the current movement
44 static struct move_target current_target;
47 TARGET_OK, // We can go the the target
48 TARGET_INACC, // Target is inaccessible because of an obstacle
49 TARGET_ERROR // Fatal errror during planning
52 static double free_space_in_front_of_robot()
56 i1 = HOKUYO_DEG_TO_INDEX(-45);
57 i2 = HOKUYO_DEG_TO_INDEX(+45);
58 for (i=MIN(i1, i2); i < MAX(i1, i2); i++) {
59 m = robot.orte.hokuyo_scan.data[i];
60 if (m > 19 && m < min)
63 return min/1000.0 - (ROBOT_AXIS_TO_FRONT_M - HOKUYO_CENTER_OFFSET_M);
66 static bool obstackle_in_front_if_turn(Trajectory *t)
71 f = free_space_in_front_of_robot();
73 if (fabs(t->initialTurnBy) < DEG2RAD(95) &&
74 f > 1.2*(ROBOT_WIDTH_M/2.0)) {
76 } else if (f > 1.5*ROBOT_AXIS_TO_BACK_M) {
85 * @brief FIXME Calculates and simplifies a path to goal position avoiding obstacles.
86 * @param start_in_future is delay utilised to calculate the trajectory while robot
87 * is moving (zero means to start movement from the current position)
88 * @return True on succes, false on error.
90 static enum target_status new_goal(struct move_target *move_target, double start_in_future)
92 enum target_status ret;
94 PathPoint * path = NULL;
95 PathPoint * tmp_point = NULL;
96 Pos future_traj_point;
97 Point target(move_target->x, move_target->y);
98 Point center(PLAYGROUND_WIDTH_M/2.0, PLAYGROUND_HEIGHT_M/2.0);
100 bool backward = false;
102 // Where to start trajectory planning?
103 get_future_pos(start_in_future, future_traj_point, time_ts);
104 Point start(future_traj_point.x, future_traj_point.y);
106 Trajectory *t = new Trajectory(move_target->tc, backward);
108 if (move_target->heading.operation == TOP_ARRIVE_FROM) {
109 get_arrive_from_point(move_target->x, move_target->y, move_target->heading,
110 &target.x, &target.y);
113 if (move_target->use_planning) {
114 /// Calculates the path between actual position to goal position. If the goal is not reachable returns -1.
115 switch (path_planner(start.x, start.y, target.x, target.y, &path, &angle)) {
116 case PP_ERROR_MAP_NOT_INIT:
117 ret = TARGET_ERROR; break;
118 case PP_ERROR_GOAL_IS_OBSTACLE:
119 case PP_ERROR_GOAL_NOT_REACHABLE:
120 ret = TARGET_INACC; break;
122 ret = TARGET_OK; break;
125 if (ret != TARGET_OK)
127 // Add this path to the trajectory.
128 for (tmp_point = path; tmp_point!=NULL; tmp_point=tmp_point->next) {
129 DBG("ADDING POINT (%f, %f)\n", tmp_point->x, tmp_point->y);
130 t->addPoint(tmp_point->x, tmp_point->y);
132 freePathMemory(path);
134 t->addPoint(target.x, target.y);
137 if (move_target->heading.operation == TOP_ARRIVE_FROM) {
138 t->addPoint(move_target->x, move_target->y);
140 t->finalHeading = target2final_heading(move_target->heading);
142 if (t->prepare(future_traj_point)) {
143 if (obstackle_in_front_if_turn(t))
146 if (start_in_future) {
147 /* not to switch immediately
148 switch_trajectory_at(t, time_ts); */
149 // new test - appending trajectories
165 * Starts moving on trajectory prepared by functions in
166 * movehelper.cc. In case of error it sends the proper event to MAIN
169 * @return TARGET_OK on succes, TARGET_ERROR on error.
171 static enum target_status new_trajectory(Trajectory *t)
176 ul_logerr("ERROR: No trajectory\n");
179 // FIXME: This is duplicite code with new_goal. Remove this
180 // function and use always new_goal.
181 robot_get_est_pos(&pos.x, &pos.y, &pos.phi);
185 if (t->prepare(pos)) {
186 if (robot.check_turn_safety && obstackle_in_front_if_turn(t)) {
199 * Handles new target signal from main FSM
205 static enum target_status new_target(struct move_target *target)
207 enum target_status ret;
208 if (target->trajectory) {
209 Trajectory *t = (Trajectory*)target->trajectory;
210 target->tc = t->constr;
211 ret = new_trajectory(t);
212 target->trajectory = NULL;
213 // Trajectory is deleted by somebody else
215 ret = new_goal(target, 0);
217 if (ret != TARGET_ERROR) {
218 current_target = *target;
224 * Recalculates trajectory to previous move target specified by
230 recalculate_trajectory(void)
232 /* TODO: Different start for planning than esitmated position */
233 enum target_status ret;
234 current_target.use_planning = true;
235 ret = new_goal(¤t_target, TRGEN_DELAY);
240 ul_logerr("Target error on recalculation_in_progress\n");
247 sem_getvalue(&recalculation_not_running, &val);
249 sem_post(&recalculation_not_running);
253 /*******************************************************************************
255 *******************************************************************************/
257 FSM_STATE_DECL(movement);
258 FSM_STATE_DECL(close_to_target);
259 FSM_STATE_DECL(wait_for_command);
260 FSM_STATE_DECL(lost);
261 FSM_STATE_DECL(wait_and_try_again);
266 if (FSM_EVENT == EV_ENTRY) {
267 FSM_TRANSITION(wait_for_command);
271 FSM_STATE(wait_for_command)
273 enum target_status ret;
279 ret = new_target((struct move_target*)FSM_EVENT_PTR);
281 case TARGET_OK: FSM_TRANSITION(movement); break;
282 case TARGET_INACC: FSM_TRANSITION(wait_and_try_again); break;
283 case TARGET_ERROR: FSM_SIGNAL(MAIN, EV_MOTION_DONE, NULL); ul_logerr("Target error\n"); break;
289 case EV_TRAJECTORY_DONE:
290 case EV_TRAJECTORY_DONE_AND_CLOSE:
291 case EV_TRAJECTORY_LOST:
294 case EV_OBSTACLE_BEHIND:
295 case EV_OBSTACLE_SIDE:
297 DBG_PRINT_EVENT("unhandled event");
305 static int obstacle_cntr = 0;
310 case EV_TRAJECTORY_DONE:
311 // Skip close to target because sometimes it turn the robot to much
312 // FSM_TRANSITION(close_to_target);
314 case EV_TRAJECTORY_DONE_AND_CLOSE:
315 FSM_SIGNAL(MAIN, EV_MOTION_DONE, NULL);
316 FSM_TRANSITION(wait_for_command);
320 if (obstacle_cntr++ > 5) {
321 FSM_SIGNAL(MAIN, EV_MOTION_ERROR, NULL);
322 FSM_TRANSITION(wait_for_command);
325 switch (recalculate_trajectory()) {
326 // We can go to the target:
327 case TARGET_OK: break;
328 // Target inaccessible because of an obstacle
329 case TARGET_INACC: FSM_TRANSITION(wait_and_try_again); break;
330 // Fatal error during planning
331 case TARGET_ERROR: FSM_SIGNAL(MAIN, EV_MOTION_DONE, NULL);
332 ul_logerr("Target error\n");
333 /* FIXME (Filip): shouldn't transition to wait_for_command be here?
334 No, probably not, if an error occurs, robot won't move any more
335 and we can recognize the error. */
339 case EV_OBSTACLE_BEHIND:
340 if (robot.moves_backward && robot.use_rear_bumper)
341 FSM_TRANSITION(wait_and_try_again);
343 case EV_OBSTACLE_SIDE:
344 if ((robot.use_left_bumper && robot.orte.robot_bumpers.bumper_left) ||
345 (robot.use_right_bumper && robot.orte.robot_bumpers.bumper_right))
346 FSM_TRANSITION(wait_and_try_again);
348 case EV_TRAJECTORY_LOST:
349 FSM_TRANSITION(lost);
352 FSM_TRANSITION(wait_for_command);
359 DBG_PRINT_EVENT("unhandled event");
364 FSM_STATE(close_to_target)
368 FSM_TIMER(MAX_WAIT_FOR_CLOSE_MS);
370 case EV_TRAJECTORY_DONE_AND_CLOSE:
371 FSM_SIGNAL(MAIN, EV_MOTION_DONE, NULL);
372 FSM_TRANSITION(wait_for_command);
374 case EV_OBSTACLE_BEHIND:
375 if (robot.moves_backward && robot.use_rear_bumper)
376 FSM_TRANSITION(wait_and_try_again);
378 case EV_OBSTACLE_SIDE:
379 if ((robot.use_left_bumper && robot.orte.robot_bumpers.bumper_left) ||
380 (robot.use_right_bumper && robot.orte.robot_bumpers.bumper_right))
381 FSM_TRANSITION(wait_and_try_again);
383 case EV_TRAJECTORY_LOST:
385 FSM_TRANSITION(lost);
388 FSM_TRANSITION(wait_for_command);
395 case EV_TRAJECTORY_DONE:
397 DBG_PRINT_EVENT("unhandled event");
410 switch (recalculate_trajectory()) {
411 case TARGET_OK: FSM_TRANSITION(movement); break;
412 case TARGET_INACC: FSM_TRANSITION(wait_and_try_again); break;
413 case TARGET_ERROR: FSM_SIGNAL(MAIN, EV_MOTION_DONE, NULL); ul_logerr("Target error\n"); break;
417 FSM_TRANSITION(wait_for_command);
421 case EV_OBSTACLE_BEHIND:
422 case EV_OBSTACLE_SIDE:
423 case EV_TRAJECTORY_DONE:
425 case EV_TRAJECTORY_DONE_AND_CLOSE:
426 case EV_TRAJECTORY_LOST:
429 DBG_PRINT_EVENT("unhandled event");
433 FSM_STATE(wait_and_try_again)
435 static int target_inacc_cnt = 0;
439 stop(); //FIXME: Not hard stop
443 switch (recalculate_trajectory()) {
444 // We can go to the target:
445 case TARGET_OK: FSM_TRANSITION(movement); break;
446 // Target inaccessible because of an obstacle
448 if (++target_inacc_cnt < 3) {
449 FSM_TRANSITION(wait_and_try_again);
450 ul_logerr("Inaccessible\n");
452 /* FIXME (Filip): this could happen forever */
453 } else { /* go away if the point is not accessible, try max. 3x */
454 target_inacc_cnt = 0;
455 FSM_TRANSITION(wait_for_command);
456 FSM_SIGNAL(MAIN, EV_MOTION_ERROR, NULL);
457 DBG_PRINT_EVENT("Target inaccessible, go to new target.");
460 // Fatal error during planning
461 case TARGET_ERROR: FSM_SIGNAL(MAIN, EV_MOTION_DONE, NULL); ul_logerr("Target error\n"); break;
464 case EV_TRAJECTORY_DONE:
465 case EV_TRAJECTORY_DONE_AND_CLOSE:
466 FSM_TRANSITION(wait_for_command);
467 FSM_SIGNAL(MAIN, EV_MOTION_DONE, NULL);
470 FSM_TRANSITION(wait_for_command);
472 case EV_OBSTACLE_BEHIND:
473 case EV_OBSTACLE_SIDE:
476 case EV_TRAJECTORY_LOST:
479 DBG_PRINT_EVENT("unhandled event");