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 // we may need something similar in future
53 void invalidate(Point point)
55 //ShmapSetRectangleFlag(x-s, y-s, x+s, y+s, MAP_FLAG_INVALIDATE_WALL, 0)
58 static double free_space_in_front_of_robot()
62 i1 = HOKUYO_DEG_TO_INDEX(-45);
63 i2 = HOKUYO_DEG_TO_INDEX(+45);
64 for (i=MIN(i1, i2); i < MAX(i1, i2); i++) {
65 m = robot.orte.hokuyo_scan.data[i];
66 if (m > 19 && m < min)
69 return min/1000.0 - HOKUYO_CENTER_OFFSET_M;;
72 static bool obstackle_in_front_if_turn(Trajectory *t)
77 f = free_space_in_front_of_robot();
79 if (fabs(t->initialTurnBy) < DEG2RAD(95) &&
80 f > 1.2*(ROBOT_WIDTH_M/2.0)) {
82 } else if (f > 1.5*ROBOT_AXIS_TO_BACK_M) {
91 * @brief FIXME Calculates and simplifies a path to goal position avoiding obstacles.
92 * @param start_in_future is delay utilised to calculate the trajectory while robot
93 * is moving (zero means to start movement from the current position)
94 * @return True on succes, false on error.
96 static enum target_status new_goal(struct move_target *move_target, double start_in_future)
98 enum target_status ret;
100 PathPoint * path = NULL;
101 PathPoint * tmp_point = NULL;
102 Pos future_traj_point;
103 Point target(move_target->x, move_target->y);
104 Point center(PLAYGROUND_WIDTH_M/2.0, PLAYGROUND_HEIGHT_M/2.0);
106 bool backward = false;
108 // Where to start trajectory planning?
109 get_future_pos(start_in_future, future_traj_point, time_ts);
110 Point start(future_traj_point.x, future_traj_point.y);
112 Trajectory *t = new Trajectory(move_target->tc, backward);
115 // Clear all invalidate flags;
116 ShmapSetRectangleFlag(0, 0, PLAYGROUND_WIDTH_M, PLAYGROUND_HEIGHT_M, 0, MAP_FLAG_INVALIDATE_WALL);
117 // Set invalidate flags if we are going to walled area
118 if (false) // we may need this in future
122 if (move_target->heading.operation == TOP_ARRIVE_FROM) {
123 get_arrive_from_point(move_target->x, move_target->y, move_target->heading,
124 &target.x, &target.y);
127 if (move_target->use_planning) {
128 /// Calculates the path between actual position to goal position. If the goal is not reachable returns -1.
129 switch (path_planner(start.x, start.y, target.x, target.y, &path, &angle)) {
130 case PP_ERROR_MAP_NOT_INIT:
131 ret = TARGET_ERROR; break;
132 case PP_ERROR_GOAL_IS_OBSTACLE:
133 case PP_ERROR_GOAL_NOT_REACHABLE:
134 ret = TARGET_INACC; break;
136 ret = TARGET_OK; break;
139 if (ret != TARGET_OK)
141 // Add this path to the trajectory.
142 for (tmp_point = path; tmp_point!=NULL; tmp_point=tmp_point->next) {
143 DBG("ADDING POINT (%f, %f)\n", tmp_point->x, tmp_point->y);
144 t->addPoint(tmp_point->x, tmp_point->y);
146 freePathMemory(path);
148 t->addPoint(target.x, target.y);
151 if (move_target->heading.operation == TOP_ARRIVE_FROM) {
152 t->addPoint(move_target->x, move_target->y);
154 t->finalHeading = target2final_heading(move_target->heading);
156 if (t->prepare(future_traj_point)) {
157 if (obstackle_in_front_if_turn(t))
160 if (start_in_future) {
161 /* not to switch immediately
162 switch_trajectory_at(t, time_ts); */
163 // new test - appending trajectories
179 * Starts moving on trajectory prepared by functions in
180 * movehelper.cc. In case of error it sends the proper event to MAIN
183 * @return TARGET_OK on succes, TARGET_ERROR on error.
185 static enum target_status new_trajectory(Trajectory *t)
190 ul_logerr("ERROR: No trajectory\n");
193 // FIXME: This is duplicite code with new_goal. Remove this
194 // function and use always new_goal.
195 robot_get_est_pos(&pos.x, &pos.y, &pos.phi);
199 if (t->prepare(pos)) {
200 if (robot.check_turn_safety && obstackle_in_front_if_turn(t)) {
213 * Handles new target signal from main FSM
219 static enum target_status new_target(struct move_target *target)
221 enum target_status ret;
222 if (target->trajectory) {
223 Trajectory *t = (Trajectory*)target->trajectory;
224 target->tc = t->constr;
225 ret = new_trajectory(t);
226 target->trajectory = NULL;
227 // Trajectory is deleted by somebody else
229 ret = new_goal(target, 0);
231 if (ret != TARGET_ERROR) {
232 current_target = *target;
238 * Recalculates trajectory to previous move target specified by
244 recalculate_trajectory(void)
246 /* TODO: Different start for planning than esitmated position */
247 enum target_status ret;
248 current_target.use_planning = true;
249 ret = new_goal(¤t_target, TRGEN_DELAY);
254 ul_logerr("Target error on recalculation_in_progress\n");
261 sem_getvalue(&recalculation_not_running, &val);
263 sem_post(&recalculation_not_running);
267 /*******************************************************************************
269 *******************************************************************************/
271 FSM_STATE_DECL(movement);
272 FSM_STATE_DECL(close_to_target);
273 FSM_STATE_DECL(wait_for_command);
274 FSM_STATE_DECL(lost);
275 FSM_STATE_DECL(wait_and_try_again);
280 if (FSM_EVENT == EV_ENTRY) {
281 FSM_TRANSITION(wait_for_command);
285 FSM_STATE(wait_for_command)
287 enum target_status ret;
293 ret = new_target((struct move_target*)FSM_EVENT_PTR);
295 case TARGET_OK: FSM_TRANSITION(movement); break;
296 case TARGET_INACC: FSM_TRANSITION(wait_and_try_again); break;
297 case TARGET_ERROR: FSM_SIGNAL(MAIN, EV_MOTION_DONE, NULL); ul_logerr("Target error\n"); break;
303 case EV_TRAJECTORY_DONE:
304 case EV_TRAJECTORY_DONE_AND_CLOSE:
305 case EV_TRAJECTORY_LOST:
308 case EV_OBSTACLE_BEHIND:
309 case EV_OBSTACLE_SIDE:
311 DBG_PRINT_EVENT("unhandled event");
321 if (robot.localization_works) {
323 ROBOT_LOCK(est_pos_uzv);
324 p = robot.est_pos_uzv;
325 ROBOT_UNLOCK(est_pos_uzv);
327 robot_set_est_pos_notrans(p.x, p.y, p.phi);
330 case EV_TRAJECTORY_DONE:
331 // Skip close to target because sometimes it turn the robot to much
332 // FSM_TRANSITION(close_to_target);
334 case EV_TRAJECTORY_DONE_AND_CLOSE:
335 FSM_SIGNAL(MAIN, EV_MOTION_DONE, NULL);
336 FSM_TRANSITION(wait_for_command);
339 switch (recalculate_trajectory()) {
340 // We can go to the target:
341 case TARGET_OK: break;
342 // Target inaccessible because of an obstacle
343 case TARGET_INACC: FSM_TRANSITION(wait_and_try_again); break;
344 // Fatal error during planning
345 case TARGET_ERROR: FSM_SIGNAL(MAIN, EV_MOTION_DONE, NULL);
346 ul_logerr("Target error\n");
347 /* FIXME (Filip): shouldn't transition to wait_for_command be here?
348 No, probably not, if an error occurs, robot won't move any more
349 and we can recognize the error. */
353 case EV_OBSTACLE_BEHIND:
354 if (robot.moves_backward && robot.use_back_switch)
355 FSM_TRANSITION(wait_and_try_again);
357 case EV_OBSTACLE_SIDE:
358 if ((robot.use_left_switch && robot.orte.robot_switches.bumper_left) ||
359 (robot.use_right_switch && robot.orte.robot_switches.bumper_right))
360 FSM_TRANSITION(wait_and_try_again);
362 case EV_TRAJECTORY_LOST:
363 FSM_TRANSITION(lost);
366 FSM_TRANSITION(wait_for_command);
373 DBG_PRINT_EVENT("unhandled event");
378 FSM_STATE(close_to_target)
382 FSM_TIMER(MAX_WAIT_FOR_CLOSE_MS);
384 case EV_TRAJECTORY_DONE_AND_CLOSE:
385 FSM_SIGNAL(MAIN, EV_MOTION_DONE, NULL);
386 FSM_TRANSITION(wait_for_command);
388 case EV_OBSTACLE_BEHIND:
389 if (robot.moves_backward && robot.use_back_switch)
390 FSM_TRANSITION(wait_and_try_again);
392 case EV_OBSTACLE_SIDE:
393 if ((robot.use_left_switch && robot.orte.robot_switches.bumper_left) ||
394 (robot.use_right_switch && robot.orte.robot_switches.bumper_right))
395 FSM_TRANSITION(wait_and_try_again);
397 case EV_TRAJECTORY_LOST:
399 FSM_TRANSITION(lost);
402 FSM_TRANSITION(wait_for_command);
409 case EV_TRAJECTORY_DONE:
411 DBG_PRINT_EVENT("unhandled event");
424 switch (recalculate_trajectory()) {
425 case TARGET_OK: FSM_TRANSITION(movement); break;
426 case TARGET_INACC: FSM_TRANSITION(wait_and_try_again); break;
427 case TARGET_ERROR: FSM_SIGNAL(MAIN, EV_MOTION_DONE, NULL); ul_logerr("Target error\n"); break;
431 FSM_TRANSITION(wait_for_command);
435 case EV_OBSTACLE_BEHIND:
436 case EV_OBSTACLE_SIDE:
437 case EV_TRAJECTORY_DONE:
439 case EV_TRAJECTORY_DONE_AND_CLOSE:
440 case EV_TRAJECTORY_LOST:
443 DBG_PRINT_EVENT("unhandled event");
447 FSM_STATE(wait_and_try_again)
451 stop(); //FIXME: Not hard stop
455 switch (recalculate_trajectory()) {
456 // We can go to the target:
457 case TARGET_OK: FSM_TRANSITION(movement); break;
458 // Target inaccessible because of an obstacle
460 FSM_TRANSITION(wait_and_try_again);
461 ul_logerr("Inaccessible\n");
463 /* FIXME (Filip): this could happen forever */
465 // Fatal error during planning
466 case TARGET_ERROR: FSM_SIGNAL(MAIN, EV_MOTION_DONE, NULL); ul_logerr("Target error\n"); break;
469 case EV_TRAJECTORY_DONE:
470 case EV_TRAJECTORY_DONE_AND_CLOSE:
471 FSM_TRANSITION(wait_for_command);
472 FSM_SIGNAL(MAIN, EV_MOTION_DONE, NULL);
475 FSM_TRANSITION(wait_for_command);
477 case EV_OBSTACLE_BEHIND:
478 case EV_OBSTACLE_SIDE:
481 case EV_TRAJECTORY_LOST:
484 DBG_PRINT_EVENT("unhandled event");