4 * @author Michal Sojka, Jose Maria Martin Laguna, Petr Beneš
16 #include <path_planner.h>
18 #include <movehelper.h>
22 #include "robot_config.h"
24 #include "motion-control.h"
28 #include <boost/mpl/list.hpp>
29 #include <boost/statechart/transition.hpp>
30 #include <boost/statechart/custom_reaction.hpp>
32 UL_LOG_CUST(ulogd_fsmmove); /* Log domain name = ulogd + name of the file */
35 #define DBG(format, ...) ul_logdeb(format, ##__VA_ARGS__)
37 #define DBG(format, ...)
40 // time to calculate new trajectory [seconds]
41 const float TRGEN_DELAY = 0.3;
44 // Target position of the current movement
45 static move_target current_target;
48 TARGET_OK, // We can go the the target
49 TARGET_INACC, // Target is inaccessible because of an obstacle
50 TARGET_ERROR // Fatal errror during planning
53 // we may need something similar in future
54 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() {
61 i1 = HOKUYO_DEG_TO_INDEX(-45);
62 i2 = HOKUYO_DEG_TO_INDEX(+45);
63 for (i=MIN(i1, i2); i < MAX(i1, i2); i++) {
64 m = robot.orte.hokuyo_scan.data[i];
65 if (m > 19 && m < min)
68 return min/1000.0 - HOKUYO_CENTER_OFFSET_M;;
71 static bool obstackle_in_front_if_turn(Trajectory *t) {
75 f = free_space_in_front_of_robot();
77 if (fabs(t->initialTurnBy) < DEG2RAD(95) &&
78 f > 1.2*(ROBOT_WIDTH_M/2.0)) {
80 } else if (f > 1.5*ROBOT_AXIS_TO_BACK_M) {
89 * @brief FIXME Calculates and simplifies a path to goal position avoiding obstacles.
90 * @param start_in_future is delay utilised to calculate the trajectory while robot
91 * is moving (zero means to start movement from the current position)
92 * @return True on succes, false on error.
94 static target_status new_goal(move_target *move_trget, double start_in_future) {
97 PathPoint * path = NULL;
98 PathPoint * tmp_point = NULL;
99 Pos future_traj_point;
100 Point target(move_trget->x, move_trget->y);
101 Point center(PLAYGROUND_WIDTH_M/2.0, PLAYGROUND_HEIGHT_M/2.0);
103 bool backward = false;
105 // Where to start trajectory planning?
106 get_future_pos(start_in_future, future_traj_point, time_ts);
107 Point start(future_traj_point.x, future_traj_point.y);
109 Trajectory *t = new Trajectory(move_trget->tc, backward);
112 // Clear all invalidate flags;
113 ShmapSetRectangleFlag(0, 0, PLAYGROUND_WIDTH_M, PLAYGROUND_HEIGHT_M, 0, MAP_FLAG_INVALIDATE_WALL);
114 // Set invalidate flags if we are going to walled area
115 if (false) // we may need this in future
119 if (move_trget->heading.operation == TOP_ARRIVE_FROM) {
120 robot.move_helper.get_arrive_from_point(move_trget->x, move_trget->y, move_trget->heading,
124 if (move_trget->use_planning) {
125 /// Calculates the path between actual position to goal position. If the goal is not reachable returns -1.
126 switch (path_planner(start.x, start.y, target.x, target.y, &path, &angle)) {
127 case PP_ERROR_MAP_NOT_INIT:
128 ret = TARGET_ERROR; break;
129 case PP_ERROR_GOAL_IS_OBSTACLE:
130 case PP_ERROR_GOAL_NOT_REACHABLE:
131 ret = TARGET_INACC; break;
133 ret = TARGET_OK; break;
136 if (ret != TARGET_OK)
138 // Add this path to the trajectory.
139 for (tmp_point = path; tmp_point!=NULL; tmp_point=tmp_point->next) {
140 DBG("ADDING POINT (%f, %f)\n", tmp_point->x, tmp_point->y);
141 t->addPoint(tmp_point->x, tmp_point->y);
143 freePathMemory(path);
145 t->addPoint(target.x, target.y);
148 if (move_trget->heading.operation == TOP_ARRIVE_FROM) {
149 t->addPoint(move_trget->x, move_trget->y);
151 t->finalHeading = target2final_heading(move_trget->heading);
153 if (t->prepare(future_traj_point)) {
154 if (obstackle_in_front_if_turn(t))
157 if (start_in_future) {
158 /* not to switch immediately
159 switch_trajectory_at(t, time_ts); */
160 // new test - appending trajectories
176 * Starts moving on trajectory prepared by functions in
177 * movehelper.cc. In case of error it sends the proper event to MAIN
180 * @return TARGET_OK on succes, TARGET_ERROR on error.
182 static target_status new_trajectory(Trajectory *t) {
186 ul_logerr("ERROR: No trajectory\n");
189 // FIXME: This is duplicite code with new_goal. Remove this
190 // function and use always new_goal.
191 robot.get_est_pos(pos.x, pos.y, pos.phi);
195 if (t->prepare(pos)) {
196 if (robot.check_turn_safety && obstackle_in_front_if_turn(t)) {
209 * Handles new target signal from main FSM
215 static target_status new_target(move_target *target) {
217 if (target->trajectory) {
218 Trajectory *t = (Trajectory*)target->trajectory;
219 target->tc = t->constr;
220 ret = new_trajectory(t);
221 target->trajectory = NULL;
222 // Trajectory is deleted by somebody else
224 ret = new_goal(target, 0);
226 if (ret != TARGET_ERROR) {
227 current_target = *target;
233 * Recalculates trajectory to previous move target specified by
238 target_status recalculate_trajectory(void) {
239 /* TODO: Different start for planning than esitmated position */
241 current_target.use_planning = true;
242 ret = new_goal(¤t_target, TRGEN_DELAY);
247 ul_logerr("Target error on recalculation_in_progress\n");
254 sem_getvalue(&recalculation_not_running, &val);
256 sem_post(&recalculation_not_running);
260 inline bool back_bumpers() {
261 return robot.moves_backward && robot.use_back_bumpers &&
262 (robot.orte.robot_bumpers.bumper_left_across ||
263 robot.orte.robot_bumpers.bumper_right_across ||
264 robot.orte.robot_bumpers.bumper_rear_left ||
265 robot.orte.robot_bumpers.bumper_rear_right);
269 inline bool side_bumpers() {
270 return (robot.use_left_bumper && robot.orte.robot_bumpers.bumper_left) ||
271 (robot.use_right_bumper && robot.orte.robot_bumpers.bumper_right);
273 /*******************************************************************************
275 *******************************************************************************/
278 struct wait_for_command;
280 struct wait_and_try_again;
283 struct FSMMotion : boost::statechart::asynchronous_state_machine< FSMMotion, MotionBase, Scheduler > {
284 FSMMotion(my_context ctx) : my_base(ctx) {}
287 struct MotionBase : sc::simple_state< MotionBase, FSMMotion, wait_for_command > {
288 sc::result react(const sc::event_base &) {
289 // DBG_PRINT_EVENT("unhandled event");
290 return discard_event();
292 typedef sc::custom_reaction< sc::event_base > reactions;
295 struct wait_for_command : TimedSimpleState< wait_for_command, MotionBase > {
298 printf("FSMMotion - wait_for_command\n");
301 sc::result react(const evNewTarget &event) {
302 ret = new_target((move_target*)event.ev_ptr);
305 case TARGET_OK: return transit<movement>();
306 case TARGET_INACC: return transit<wait_and_try_again>();
308 robot.sched.queue_event(robot.MAIN, new evMotionDone());
309 ul_logerr("Target error\n");
311 return discard_event();
313 typedef sc::custom_reaction< evNewTarget > reactions;
316 struct movement : TimedSimpleState< movement, MotionBase > {
318 printf("FSMMotion - movement\n");
320 sc::result react(const evTrajectoryDone &) {
321 robot.sched.queue_event(robot.MAIN, new evMotionDone());
322 return transit<wait_for_command>();
324 sc::result react(const evObstacle &) {
325 switch (recalculate_trajectory()) {
326 // We can go to the target:
327 case TARGET_OK: return discard_event();
328 // Target inaccessible because of an obstacle
329 case TARGET_INACC: return transit<wait_and_try_again>();
330 // Fatal error during planning
332 robot.sched.queue_event(robot.MAIN, new evMotionDone());
333 ul_logerr("Target error\n");
334 /* FIXME (Filip): shouldn't transition to wait_for_command be here?
335 No, probably not, if an error occurs, robot won't move any more
336 and we can recognize the error. */
338 return discard_event();
340 sc::result react(const evObstacleBehind &) {
342 return transit<wait_and_try_again>();
343 return discard_event();
345 sc::result react(const evObstacleSide &) {
347 return transit<wait_and_try_again>();
348 return discard_event();
351 sc::transition< evTrajectoryLost, lost >,
352 sc::transition< evMoveStop, wait_for_command >,
353 sc::custom_reaction< evObstacleSide >,
354 sc::custom_reaction< evObstacleBehind >,
355 sc::custom_reaction< evObstacle >,
356 sc::custom_reaction< evTrajectoryDone > > reactions;
359 struct lost : TimedState< lost, MotionBase > {
361 lost(my_context ctx) : base_state(ctx) {
362 printf("FSMMotion - lost\n");
364 runTimer(lost_tout, 1000, new evTimer());
366 sc::result react(const evTimer &) {
367 switch (recalculate_trajectory()) {
368 case TARGET_OK: return transit<movement>();
369 case TARGET_INACC: return transit<wait_and_try_again>();
371 robot.sched.queue_event(robot.MAIN, new evMotionDone());
372 ul_logerr("Target error\n");
374 return discard_event();
377 sc::custom_reaction< evTimer >,
378 sc::transition< evMoveStop, wait_for_command > > reactions;
381 struct wait_and_try_again : TimedState< wait_and_try_again, MotionBase > {
383 wait_and_try_again(my_context ctx) : base_state(ctx) {
384 printf("FSMMotion - wait_and_try_again\n");
386 runTimer(wait_tout, 1000, new evTimer());
388 sc::result react(const evTimer &) {
389 switch (recalculate_trajectory()) {
390 // We can go to the target:
391 case TARGET_OK: return transit<movement>();
392 // Target inaccessible because of an obstacle
394 ul_logerr("Inaccessible\n");
395 return transit<wait_and_try_again>();
396 /* FIXME (Filip): this could happen forever */
397 // Fatal error during planning
399 robot.sched.queue_event(robot.MAIN, new evMotionDone());
400 ul_logerr("Target error\n");
402 return discard_event();
404 sc::result react(const evTrajectoryDone &) {
405 robot.sched.queue_event(robot.MAIN, new evMotionDone());
406 return transit<wait_for_command>();
409 sc::custom_reaction< evTimer >,
410 sc::custom_reaction< evTrajectoryDone >,
411 sc::transition< evMoveStop, wait_for_command > > reactions;