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");
308 case EV_TRAJECTORY_DONE:
309 // Skip close to target because sometimes it turn the robot to much
310 // FSM_TRANSITION(close_to_target);
312 case EV_TRAJECTORY_DONE_AND_CLOSE:
313 FSM_SIGNAL(MAIN, EV_MOTION_DONE, NULL);
314 FSM_TRANSITION(wait_for_command);
318 switch (recalculate_trajectory()) {
319 // We can go to the target:
320 case TARGET_OK: break;
321 // Target inaccessible because of an obstacle
322 case TARGET_INACC: FSM_TRANSITION(wait_and_try_again); break;
323 // Fatal error during planning
324 case TARGET_ERROR: FSM_SIGNAL(MAIN, EV_MOTION_DONE, NULL);
325 ul_logerr("Target error\n");
326 /* FIXME (Filip): shouldn't transition to wait_for_command be here?
327 No, probably not, if an error occurs, robot won't move any more
328 and we can recognize the error. */
332 case EV_OBSTACLE_BEHIND:
333 if (robot.moves_backward && robot.use_rear_bumper)
334 FSM_TRANSITION(wait_and_try_again);
336 case EV_OBSTACLE_SIDE:
337 if ((robot.use_left_bumper && robot.orte.robot_bumpers.bumper_left) ||
338 (robot.use_right_bumper && robot.orte.robot_bumpers.bumper_right))
339 FSM_TRANSITION(wait_and_try_again);
341 case EV_TRAJECTORY_LOST:
342 FSM_TRANSITION(lost);
345 FSM_TRANSITION(wait_for_command);
352 DBG_PRINT_EVENT("unhandled event");
357 FSM_STATE(close_to_target)
361 FSM_TIMER(MAX_WAIT_FOR_CLOSE_MS);
363 case EV_TRAJECTORY_DONE_AND_CLOSE:
364 FSM_SIGNAL(MAIN, EV_MOTION_DONE, NULL);
365 FSM_TRANSITION(wait_for_command);
367 case EV_OBSTACLE_BEHIND:
368 if (robot.moves_backward && robot.use_rear_bumper)
369 FSM_TRANSITION(wait_and_try_again);
371 case EV_OBSTACLE_SIDE:
372 if ((robot.use_left_bumper && robot.orte.robot_bumpers.bumper_left) ||
373 (robot.use_right_bumper && robot.orte.robot_bumpers.bumper_right))
374 FSM_TRANSITION(wait_and_try_again);
376 case EV_TRAJECTORY_LOST:
378 FSM_TRANSITION(lost);
381 FSM_TRANSITION(wait_for_command);
388 case EV_TRAJECTORY_DONE:
390 DBG_PRINT_EVENT("unhandled event");
403 switch (recalculate_trajectory()) {
404 case TARGET_OK: FSM_TRANSITION(movement); break;
405 case TARGET_INACC: FSM_TRANSITION(wait_and_try_again); break;
406 case TARGET_ERROR: FSM_SIGNAL(MAIN, EV_MOTION_DONE, NULL); ul_logerr("Target error\n"); break;
410 FSM_TRANSITION(wait_for_command);
414 case EV_OBSTACLE_BEHIND:
415 case EV_OBSTACLE_SIDE:
416 case EV_TRAJECTORY_DONE:
418 case EV_TRAJECTORY_DONE_AND_CLOSE:
419 case EV_TRAJECTORY_LOST:
422 DBG_PRINT_EVENT("unhandled event");
426 FSM_STATE(wait_and_try_again)
428 static int target_inacc_cnt = 0;
432 stop(); //FIXME: Not hard stop
436 switch (recalculate_trajectory()) {
437 // We can go to the target:
438 case TARGET_OK: FSM_TRANSITION(movement); break;
439 // Target inaccessible because of an obstacle
441 if (++target_inacc_cnt < 5) {
442 FSM_TRANSITION(wait_and_try_again);
443 ul_logerr("Inaccessible\n");
445 /* FIXME (Filip): this could happen forever */
446 } else { /* go away if the point is not accessible, try max. 5x */
447 target_inacc_cnt = 0;
448 FSM_TRANSITION(wait_for_command);
449 FSM_SIGNAL(MAIN, EV_MOTION_ERROR, NULL);
450 DBG_PRINT_EVENT("Target inaccessible, go to new target.");
453 // Fatal error during planning
454 case TARGET_ERROR: FSM_SIGNAL(MAIN, EV_MOTION_DONE, NULL); ul_logerr("Target error\n"); break;
457 case EV_TRAJECTORY_DONE:
458 case EV_TRAJECTORY_DONE_AND_CLOSE:
459 FSM_TRANSITION(wait_for_command);
460 FSM_SIGNAL(MAIN, EV_MOTION_DONE, NULL);
463 FSM_TRANSITION(wait_for_command);
465 case EV_OBSTACLE_BEHIND:
466 case EV_OBSTACLE_SIDE:
469 case EV_TRAJECTORY_LOST:
472 DBG_PRINT_EVENT("unhandled event");