diff --git a/homer_navigation/include/homer_navigation/homer_navigation_node.h b/homer_navigation/include/homer_navigation/homer_navigation_node.h index 53447adf4a13e21d6e02a60cf679be2166916e6d..d452feb40237fe03ab2c75ec60f47a0adb1c4a88 100644 --- a/homer_navigation/include/homer_navigation/homer_navigation_node.h +++ b/homer_navigation/include/homer_navigation/homer_navigation_node.h @@ -40,304 +40,299 @@ class Explorer; * @brief Performs autonomous navigation */ class HomerNavigationNode { - public: - /** - * @brief States of the state machines - */ - enum ProcessState { - IDLE, - AWAITING_PATHPLANNING_MAP, - FOLLOWING_PATH, - AVOIDING_COLLISION, - FINAL_TURN - }; - - /** - * The constructor - */ - HomerNavigationNode(); - - /** - * The destructor - */ - virtual ~HomerNavigationNode(); - - /** @brief Is called in constant intervals. */ - void idleProcess(); - - protected: - /** @brief Handles incoming messages. */ - void mapCallback(const nav_msgs::OccupancyGrid::ConstPtr& msg); - void poseCallback(const geometry_msgs::PoseStamped::ConstPtr& msg); - void laserDataCallback(const sensor_msgs::LaserScan::ConstPtr& msg); - void downlaserDataCallback(const sensor_msgs::LaserScan::ConstPtr& msg); - void startNavigationCallback( - const homer_mapnav_msgs::StartNavigation::ConstPtr& msg); - void moveBaseSimpleGoalCallback( - const geometry_msgs::PoseStamped::ConstPtr& msg); - void stopNavigationCallback(const std_msgs::Empty::ConstPtr& msg); - - void navigateToPOICallback( - const homer_mapnav_msgs::NavigateToPOI::ConstPtr& msg); - void unknownThresholdCallback(const std_msgs::Int8::ConstPtr& msg); - void maxDepthMoveDistanceCallback(const std_msgs::Float32::ConstPtr& msg); - - /** @brief initializes and refreshs parameters */ - void loadParameters(); - - /** @brief Is called when all modules are loaded and thread has started. */ - virtual void init(); - - /** @brief Detect & handle possible collision */ - void handleCollision(); - - private: - /** @brief Start navigation to m_Target on last_map_data_ */ - void startNavigation(); - - /** @brief Check if obstacles are blocking the way in last_map_data_ */ - bool checkPath(); - - /** @brief calculate path from current robot position to target approximation - */ - void calculatePath(); - - /** @brief Send message containing current navigation path */ - void sendPathData(); - - /** @brief Sends target reached and stops the robot. */ - void sendTargetReachedMsg(); - - /** - * @brief Sends a target unreachable with given reason and stops the robot. - * @param reason reason for unreachable target (see - * homer_mapnav_msgs::TargetUnreachable for possible reasons) - */ - void sendTargetUnreachableMsg(int8_t reason); - - /** @brief reloads all params from the parameterserver */ - void refreshParamsCallback(const std_msgs::Empty::ConstPtr& msg); - - /** @brief Navigate robot to next waypoint */ - void performNextMove(); - - /** @brief Finishes navigation or starts turning to target direction if the - * target position has been reached */ - void targetPositionReached(); - - /** @return Angle from robot_pose_ to point in degrees */ - int angleToPointDeg(geometry_msgs::Point point); - - /** @brief Calculates current maximal backwards distance on map Data */ - bool backwardObstacle(); - - /** @brief stops the Robot */ - void stopRobot(); - - /** - * @brief Sets each cell of the map to -1 outside the bounding box - * containing the robot pose and the current target - */ - void maskMap(); - - /** - * @brief Current path was finished (either successful or not), - * sets state machine to path planning to check if the robot is already - * at the goal - */ - void currentPathFinished(); - - // convenience math functions - /** - * Computes minimum turn angle from angle 1 to angle 2 - * @param angle1 from angle - * @param angle2 to angle - * @return minimal angle needed to turn from angle 1 to angle 2 [-Pi..Pi] - */ - static float minTurnAngle(float angle1, float angle2); - - /** - * converts value from degree to radiant - * @param deg Value in degree - * @return value in radiants - */ - static float deg2Rad(float deg) { return deg / 180.0 * M_PI; } - - /** - * converts value from radiants to degrees - * @param rad Value in radiants - * @return value in degrees - */ - static float rad2Deg(float rad) { return rad / M_PI * 180.0; } - - bool drawPolygon(std::vector<geometry_msgs::Point> vertices); - void drawLine(std::vector<int>& data, int startX, int startY, int endX, - int endY, int value); - bool fillPolygon(std::vector<int>& data, int x, int y, int value); + public: + /** + * @brief States of the state machines + */ + enum ProcessState { IDLE, FOLLOWING_PATH, AVOIDING_COLLISION, FINAL_TURN }; + + /** + * The constructor + */ + HomerNavigationNode(); + + /** + * The destructor + */ + virtual ~HomerNavigationNode(); + + /** @brief Is called in constant intervals. */ + void idleProcess(); + + protected: + /** @brief Handles incoming messages. */ + void mapCallback(const nav_msgs::OccupancyGrid::ConstPtr& msg); + void poseCallback(const geometry_msgs::PoseStamped::ConstPtr& msg); + void laserDataCallback(const sensor_msgs::LaserScan::ConstPtr& msg); + void downlaserDataCallback(const sensor_msgs::LaserScan::ConstPtr& msg); + void startNavigationCallback( + const homer_mapnav_msgs::StartNavigation::ConstPtr& msg); + void moveBaseSimpleGoalCallback( + const geometry_msgs::PoseStamped::ConstPtr& msg); + void stopNavigationCallback(const std_msgs::Empty::ConstPtr& msg); + + void navigateToPOICallback( + const homer_mapnav_msgs::NavigateToPOI::ConstPtr& msg); + void unknownThresholdCallback(const std_msgs::Int8::ConstPtr& msg); + void maxDepthMoveDistanceCallback(const std_msgs::Float32::ConstPtr& msg); + + /** @brief initializes and refreshs parameters */ + void loadParameters(); + + /** @brief Is called when all modules are loaded and thread has started. */ + virtual void init(); + + void initNewTarget(); + + private: + /** @brief Start navigation to m_Target on last_map_data_ */ + void startNavigation(); + + /** @brief Check if obstacles are blocking the way in last_map_data_ */ + bool obstacleOnPath(); + + /** @brief calculate path from current robot position to target + * approximation + */ + void calculatePath(); + + void setExplorerMap(); + + /** @brief Send message containing current navigation path */ + void sendPathData(); + + /** @brief Sends target reached and stops the robot. */ + void sendTargetReachedMsg(); + + /** + * @brief Sends a target unreachable with given reason and stops the robot. + * @param reason reason for unreachable target (see + * homer_mapnav_msgs::TargetUnreachable for possible reasons) + */ + void sendTargetUnreachableMsg(int8_t reason); + + /** @brief reloads all params from the parameterserver */ + void refreshParamsCallback(const std_msgs::Empty::ConstPtr& msg); + + /** @brief Navigate robot to next waypoint */ + void performNextMove(); + + /** @brief Finishes navigation or starts turning to target direction if the + * target position has been reached */ + bool isTargetPositionReached(); + bool m_new_target; + + /** @return Angle from robot_pose_ to point in degrees */ + int angleToPointDeg(geometry_msgs::Point point); + + /** @brief Calculates current maximal backwards distance on map Data */ + bool backwardObstacle(); + + /** @brief stops the Robot */ + void stopRobot(); + + /** + * @brief Sets each cell of the map to -1 outside the bounding box + * containing the robot pose and the current target + */ + void maskMap(nav_msgs::OccupancyGrid& cmap); + + /** + * @brief Current path was finished (either successful or not), + * sets state machine to path planning to check if the robot is + * already + * at the goal + */ + void currentPathFinished(); - /** @brief calcs the maximal move distance from Laser and DepthData */ - void calcMaxMoveDist(); + // convenience math functions + /** + * Computes minimum turn angle from angle 1 to angle 2 + * @param angle1 from angle + * @param angle2 to angle + * @return minimal angle needed to turn from angle 1 to angle 2 [-Pi..Pi] + */ + static float minTurnAngle(float angle1, float angle2); - /// @brief Worker instances - Explorer* m_explorer; + /** + * converts value from degree to radiant + * @param deg Value in degree + * @return value in radiants + */ + static float deg2Rad(float deg) { return deg / 180.0 * M_PI; } + + /** + * converts value from radiants to degrees + * @param rad Value in radiants + * @return value in degrees + */ + static float rad2Deg(float rad) { return rad / M_PI * 180.0; } - /// @brief State machine - StateMachine<ProcessState> m_MainMachine; + bool drawPolygon(std::vector<geometry_msgs::Point> vertices); + void drawLine(std::vector<int>& data, int startX, int startY, int endX, + int endY, int value); + bool fillPolygon(std::vector<int>& data, int x, int y, int value); - /// @brief Navigation options & data + /** @brief calcs the maximal move distance from Laser and DepthData */ + void calcMaxMoveDist(); - /** list of waypoints subsampled from m_PixelPath */ - std::vector<geometry_msgs::PoseStamped> m_waypoints; + /// @brief Worker instances + Explorer* m_explorer; - /** Path planned by Explorer, pixel accuracy */ - std::vector<Eigen::Vector2i> m_pixel_path; - - /** target point */ - geometry_msgs::Point m_target_point; + /// @brief State machine + StateMachine<ProcessState> m_MainMachine; - /** target name if called via Navigate_to_POI */ - std::string m_target_name; + /// @brief Navigation options & data - /** orientation the robot should have at the target point */ - double m_target_orientation; + /** list of waypoints subsampled from m_PixelPath */ + std::vector<geometry_msgs::PoseStamped> m_waypoints; - /** allowed distance to target */ - float m_desired_distance; + /** Path planned by Explorer, pixel accuracy */ + std::vector<Eigen::Vector2i> m_pixel_path; - /** check if the final turn should be skipped */ - bool m_skip_final_turn; + /** target point */ + geometry_msgs::Point m_target_point; - /** - * check if navigation should perform fast planning. In this mode a path is - * only planned within - * a bounding box containing the robot pose and the target point - */ - bool m_fast_path_planning; + /** target name if called via Navigate_to_POI */ + std::string m_target_name; - /** current pose of the robot */ - geometry_msgs::Pose m_robot_pose; + /** orientation the robot should have at the target point */ + double m_target_orientation; - /** last pose of the robot */ - geometry_msgs::Pose m_robot_last_pose; - - /** time stamp of the last incoming laser scan */ - ros::Time m_last_laser_time; - /** time stamp of the last incoming pose */ - ros::Time m_last_pose_time; - - /** Distance factor of a frontier cell considered save for exploration */ - float m_FrontierSafenessFactor; - - double m_SafePathWeight; - - /// map parameters - double m_resolution; - double m_width; - double m_height; - geometry_msgs::Pose m_origin; - - /// @brief Configuration parameters - - /** Allowed distances of obstacles to robot. Robot must move within these - * bounds */ - std::pair<float, float> m_AllowedObstacleDistance; - - /** Safe distances of obstacles to robot. If possible, robot should move - * within these bounds */ - std::pair<float, float> m_SafeObstacleDistance; - - /** threshold to sample down waypoints */ - float m_waypoint_sampling_threshold; - - float m_max_move_distance; - float m_max_move_sick; - float m_max_move_down; - float m_max_move_depth; - - /** if distance to nearest obstacle is below collision distance trigger - * collision avoidance */ - float m_collision_distance; - - /** if distance to nearest obstacle is below collision distance don't drive - * backwards */ - float m_backward_collision_distance; - /** do not drive back in collision avoidance when this near target */ - float m_collision_distance_near_target; - - /** if true, obstacles in path will be detected and path will be replanned */ - bool m_check_path; - - /** waypoints will only be checked for obstacles if they are closer than - * check_path_max_distance to robot */ - float m_check_path_max_distance; - - /** do not replan if lisa avoids an obstacle, instead send target - * unreachable*/ - bool m_stop_before_obstacle; + /** allowed distance to target */ + float m_desired_distance; - bool m_avoided_collision; + /** check if the final turn should be skipped */ + bool m_skip_final_turn; - float m_min_turn_angle; - float m_max_turn_speed; - float m_min_turn_speed; - float m_max_move_speed; - float m_max_drive_angle; - float m_waypoint_radius_factor; - - float m_distance_to_target; - float m_act_speed; - float m_angular_avoidance; - - float m_map_speed_factor; - float m_waypoint_speed_factor; - float m_obstacle_speed_factor; - float m_target_distance_speed_factor; - - float m_min_y; - float m_min_x; - - float m_callback_error_duration; - - bool m_last_check_path_res; - bool m_use_ptu; - bool m_new_target; - - bool m_path_reaches_target; - bool m_initial_path_reaches_target; - int m_last_calculations_failed; - int m_unknown_threshold; - - /** last map data */ - std::vector<int8_t>* m_last_map_data; - - // ros specific members - tf::TransformListener m_transform_listener; - - // subscribers - ros::Subscriber m_map_sub; - ros::Subscriber m_pose_sub; - ros::Subscriber m_laser_data_sub; - ros::Subscriber m_down_laser_data_sub; - ros::Subscriber m_laser_back_data_sub; - ros::Subscriber m_start_navigation_sub; - ros::Subscriber m_stop_navigation_sub; - ros::Subscriber m_navigate_to_poi_sub; - ros::Subscriber m_unknown_threshold_sub; - ros::Subscriber m_refresh_param_sub; - ros::Subscriber m_max_move_depth_sub; - ros::Subscriber m_move_base_simple_goal_sub; - - // publishers - ros::Publisher m_cmd_vel_pub; - ros::Publisher m_target_reached_string_pub; - // ros::Publisher m_target_reached_empty_pub; - ros::Publisher m_target_unreachable_pub; - ros::Publisher m_path_pub; - ros::Publisher m_ptu_center_world_point_pub; - ros::Publisher m_set_pan_tilt_pub; - ros::Publisher m_debug_pub; - - // service clients - ros::ServiceClient m_get_POIs_client; + /** + * check if navigation should perform fast planning. In this mode a path is + * only planned within + * a bounding box containing the robot pose and the target point + */ + bool m_fast_path_planning; + + /** current pose of the robot */ + geometry_msgs::Pose m_robot_pose; + + /** last pose of the robot */ + geometry_msgs::Pose m_robot_last_pose; + + std::map<std::string, sensor_msgs::LaserScan::ConstPtr> m_scan_map; + + /** time stamp of the last incoming laser scan */ + ros::Time m_last_laser_time; + /** time stamp of the last incoming pose */ + ros::Time m_last_pose_time; + + /** Distance factor of a frontier cell considered save for exploration */ + float m_FrontierSafenessFactor; + + double m_SafePathWeight; + + /// @brief Configuration parameters + + /** Allowed distances of obstacles to robot. Robot must move within these + * bounds */ + std::pair<float, float> m_AllowedObstacleDistance; + + /** Safe distances of obstacles to robot. If possible, robot should move + * within these bounds */ + std::pair<float, float> m_SafeObstacleDistance; + + /** threshold to sample down waypoints */ + float m_waypoint_sampling_threshold; + + float m_max_move_distance; + float m_max_move_sick; + float m_max_move_down; + float m_max_move_depth; + + /** if distance to nearest obstacle is below collision distance trigger + * collision avoidance */ + float m_collision_distance; + + /** if distance to nearest obstacle is below collision distance don't drive + * backwards */ + float m_backward_collision_distance; + /** do not drive back in collision avoidance when this near target */ + float m_collision_distance_near_target; + + /** if true, obstacles in path will be detected and path will be replanned + */ + bool m_check_path; + + /** waypoints will only be checked for obstacles if they are closer than + * check_path_max_distance to robot */ + float m_check_path_max_distance; + ros::Time m_last_check_path_time; + + /** do not replan if lisa avoids an obstacle, instead send target + * unreachable*/ + bool m_stop_before_obstacle; + + bool m_avoided_collision; + + float m_min_turn_angle; + float m_max_turn_speed; + float m_min_turn_speed; + float m_max_move_speed; + float m_max_drive_angle; + float m_waypoint_radius_factor; + + float m_distance_to_target; + float m_act_speed; + float m_angular_avoidance; + + float m_map_speed_factor; + float m_waypoint_speed_factor; + float m_obstacle_speed_factor; + float m_target_distance_speed_factor; + + float m_min_y; + float m_min_x; + + float m_callback_error_duration; + + bool m_seen_obstacle_before; + bool m_use_ptu; + + bool m_path_reaches_target; + bool m_initial_path_reaches_target; + int m_last_calculations_failed; + int m_unknown_threshold; + + /** last map data */ + nav_msgs::OccupancyGrid::ConstPtr m_map; + + // ros specific members + tf::TransformListener m_transform_listener; + + // subscribers + ros::Subscriber m_map_sub; + ros::Subscriber m_pose_sub; + ros::Subscriber m_laser_data_sub; + ros::Subscriber m_down_laser_data_sub; + ros::Subscriber m_laser_back_data_sub; + ros::Subscriber m_start_navigation_sub; + ros::Subscriber m_stop_navigation_sub; + ros::Subscriber m_navigate_to_poi_sub; + ros::Subscriber m_unknown_threshold_sub; + ros::Subscriber m_refresh_param_sub; + ros::Subscriber m_max_move_depth_sub; + ros::Subscriber m_move_base_simple_goal_sub; + + // publishers + ros::Publisher m_cmd_vel_pub; + ros::Publisher m_target_reached_string_pub; + // ros::Publisher m_target_reached_empty_pub; + ros::Publisher m_target_unreachable_pub; + ros::Publisher m_path_pub; + ros::Publisher m_ptu_center_world_point_pub; + ros::Publisher m_set_pan_tilt_pub; + ros::Publisher m_debug_pub; + + // service clients + ros::ServiceClient m_get_POIs_client; }; #endif diff --git a/homer_navigation/src/homer_navigation_node.cpp b/homer_navigation/src/homer_navigation_node.cpp index c5de4ecce49f98698f240ff55adf9231a9a6560e..4f7d241f2807f0b6f8df1952158e04e84b680f21 100644 --- a/homer_navigation/src/homer_navigation_node.cpp +++ b/homer_navigation/src/homer_navigation_node.cpp @@ -1,1126 +1,1162 @@ #include "homer_navigation/homer_navigation_node.h" HomerNavigationNode::HomerNavigationNode() { - ros::NodeHandle nh; - - // subscribers - m_map_sub = nh.subscribe<nav_msgs::OccupancyGrid>( - "/map", 1, &HomerNavigationNode::mapCallback, this); - m_pose_sub = nh.subscribe<geometry_msgs::PoseStamped>( - "/pose", 1, &HomerNavigationNode::poseCallback, this); - m_laser_data_sub = nh.subscribe<sensor_msgs::LaserScan>( - "/scan", 1, &HomerNavigationNode::laserDataCallback, this); - m_down_laser_data_sub = nh.subscribe<sensor_msgs::LaserScan>( - "/front_scan", 1, &HomerNavigationNode::downlaserDataCallback, this); - m_start_navigation_sub = nh.subscribe<homer_mapnav_msgs::StartNavigation>( - "/homer_navigation/start_navigation", 1, - &HomerNavigationNode::startNavigationCallback, this); - m_move_base_simple_goal_sub = nh.subscribe<geometry_msgs::PoseStamped>( - "/move_base_simple/goal", 1, - &HomerNavigationNode::moveBaseSimpleGoalCallback, - this); // for RVIZ usage - m_stop_navigation_sub = nh.subscribe<std_msgs::Empty>( - "/homer_navigation/stop_navigation", 1, - &HomerNavigationNode::stopNavigationCallback, this); - m_navigate_to_poi_sub = nh.subscribe<homer_mapnav_msgs::NavigateToPOI>( - "/homer_navigation/navigate_to_POI", 1, - &HomerNavigationNode::navigateToPOICallback, this); - m_unknown_threshold_sub = nh.subscribe<std_msgs::Int8>( - "/homer_navigation/unknown_threshold", 1, - &HomerNavigationNode::unknownThresholdCallback, this); - m_refresh_param_sub = nh.subscribe<std_msgs::Empty>( - "/homer_navigation/refresh_params", 1, - &HomerNavigationNode::refreshParamsCallback, this); - m_max_move_depth_sub = nh.subscribe<std_msgs::Float32>( - "/homer_navigation/max_depth_move_distance", 1, - &HomerNavigationNode::maxDepthMoveDistanceCallback, this); - - m_cmd_vel_pub = - nh.advertise<geometry_msgs::Twist>("/robot_platform/cmd_vel", 1); - m_target_reached_string_pub = - nh.advertise<std_msgs::String>("/homer_navigation/target_reached", 1); - // m_target_reached_empty_pub = - // nh.advertise<std_msgs::Empty>("/homer_navigation/target_reached", 1); - m_target_unreachable_pub = nh.advertise<homer_mapnav_msgs::TargetUnreachable>( - "/homer_navigation/target_unreachable", 1); - m_path_pub = nh.advertise<nav_msgs::Path>("/homer_navigation/path", 1); - m_ptu_center_world_point_pub = nh.advertise<homer_ptu_msgs::CenterWorldPoint>( - "/ptu/center_world_point", 1); - m_set_pan_tilt_pub = - nh.advertise<homer_ptu_msgs::SetPanTilt>("/ptu/set_pan_tilt", 1); - m_debug_pub = nh.advertise<std_msgs::String>("/homer_navigation/debug", 1); - - m_get_POIs_client = nh.serviceClient<homer_mapnav_msgs::GetPointsOfInterest>( - "/map_manager/get_pois"); - - m_MainMachine.setName("HomerNavigation Main"); - ADD_MACHINE_STATE(m_MainMachine, IDLE); - ADD_MACHINE_STATE(m_MainMachine, AWAITING_PATHPLANNING_MAP); - ADD_MACHINE_STATE(m_MainMachine, FOLLOWING_PATH); - ADD_MACHINE_STATE(m_MainMachine, AVOIDING_COLLISION); - ADD_MACHINE_STATE(m_MainMachine, FINAL_TURN); - - init(); + ros::NodeHandle nh; + + // subscribers + m_map_sub = nh.subscribe<nav_msgs::OccupancyGrid>( + "/map", 1, &HomerNavigationNode::mapCallback, this); + m_pose_sub = nh.subscribe<geometry_msgs::PoseStamped>( + "/pose", 1, &HomerNavigationNode::poseCallback, this); + m_laser_data_sub = nh.subscribe<sensor_msgs::LaserScan>( + "/scan", 1, &HomerNavigationNode::laserDataCallback, this); + m_down_laser_data_sub = nh.subscribe<sensor_msgs::LaserScan>( + "/front_scan", 1, &HomerNavigationNode::downlaserDataCallback, this); + m_start_navigation_sub = nh.subscribe<homer_mapnav_msgs::StartNavigation>( + "/homer_navigation/start_navigation", 1, + &HomerNavigationNode::startNavigationCallback, this); + m_move_base_simple_goal_sub = nh.subscribe<geometry_msgs::PoseStamped>( + "/move_base_simple/goal", 1, + &HomerNavigationNode::moveBaseSimpleGoalCallback, + this); // for RVIZ usage + m_stop_navigation_sub = nh.subscribe<std_msgs::Empty>( + "/homer_navigation/stop_navigation", 1, + &HomerNavigationNode::stopNavigationCallback, this); + m_navigate_to_poi_sub = nh.subscribe<homer_mapnav_msgs::NavigateToPOI>( + "/homer_navigation/navigate_to_POI", 1, + &HomerNavigationNode::navigateToPOICallback, this); + m_unknown_threshold_sub = nh.subscribe<std_msgs::Int8>( + "/homer_navigation/unknown_threshold", 1, + &HomerNavigationNode::unknownThresholdCallback, this); + m_refresh_param_sub = nh.subscribe<std_msgs::Empty>( + "/homer_navigation/refresh_params", 1, + &HomerNavigationNode::refreshParamsCallback, this); + m_max_move_depth_sub = nh.subscribe<std_msgs::Float32>( + "/homer_navigation/max_depth_move_distance", 1, + &HomerNavigationNode::maxDepthMoveDistanceCallback, this); + + m_cmd_vel_pub = + nh.advertise<geometry_msgs::Twist>("/robot_platform/cmd_vel", 3); + m_target_reached_string_pub = + nh.advertise<std_msgs::String>("/homer_navigation/target_reached", 1); + // m_target_reached_empty_pub = + // nh.advertise<std_msgs::Empty>("/homer_navigation/target_reached", 1); + m_target_unreachable_pub = + nh.advertise<homer_mapnav_msgs::TargetUnreachable>( + "/homer_navigation/target_unreachable", 1); + m_path_pub = nh.advertise<nav_msgs::Path>("/homer_navigation/path", 1); + m_ptu_center_world_point_pub = + nh.advertise<homer_ptu_msgs::CenterWorldPoint>( + "/ptu/center_world_point", 1); + m_set_pan_tilt_pub = + nh.advertise<homer_ptu_msgs::SetPanTilt>("/ptu/set_pan_tilt", 1); + m_debug_pub = nh.advertise<std_msgs::String>("/homer_navigation/debug", 1); + + m_get_POIs_client = + nh.serviceClient<homer_mapnav_msgs::GetPointsOfInterest>( + "/map_manager/get_pois"); + + m_MainMachine.setName("HomerNavigation Main"); + ADD_MACHINE_STATE(m_MainMachine, IDLE); + ADD_MACHINE_STATE(m_MainMachine, FOLLOWING_PATH); + ADD_MACHINE_STATE(m_MainMachine, AVOIDING_COLLISION); + ADD_MACHINE_STATE(m_MainMachine, FINAL_TURN); + + init(); } void HomerNavigationNode::loadParameters() { - // Explorer constructor - ros::param::param("/homer_mapping/resolution", m_resolution, (double)0.05); - ros::param::param("/homer_navigation/allowed_obstacle_distance/min", - m_AllowedObstacleDistance.first, (float)0.3); - ros::param::param("/homer_navigation/allowed_obstacle_distance/max", - m_AllowedObstacleDistance.second, (float)5.0); - ros::param::param("/homer_navigation/safe_obstacle_distance/min", - m_SafeObstacleDistance.first, (float)0.7); - ros::param::param("/homer_navigation/safe_obstacle_distance/max", - m_SafeObstacleDistance.second, (float)1.5); - ros::param::param("/homer_navigation/frontier_safeness_factor", - m_FrontierSafenessFactor, (float)1.4); - ros::param::param("/homer_navigation/safe_path_weight", m_SafePathWeight, - (double)1.2); - ros::param::param("/homer_navigation/waypoint_sampling_threshold", - m_waypoint_sampling_threshold, (float)1.5); - m_AllowedObstacleDistance.first /= m_resolution; - m_AllowedObstacleDistance.second /= m_resolution; - m_SafeObstacleDistance.first /= m_resolution; - m_SafeObstacleDistance.second /= m_resolution; - - // check path - ros::param::param("/homer_navigation/check_path", m_check_path, (bool)true); - ros::param::param("/homer_navigation/check_path_max_distance", - m_check_path_max_distance, (float)2.0); - - // collision - ros::param::param("/homer_navigation/collision_distance", - m_collision_distance, (float)0.3); - ros::param::param("/homer_navigation/collision_distance_near_target", - m_collision_distance_near_target, (float)0.2); - ros::param::param("/homer_navigation/backward_collision_distance", - m_backward_collision_distance, (float)0.5); - - // cmd_vel config values - ros::param::param("/homer_navigation/min_turn_angle", m_min_turn_angle, - (float)0.15); - ros::param::param("/homer_navigation/max_turn_speed", m_max_turn_speed, - (float)0.6); - ros::param::param("/homer_navigation/min_turn_speed", m_min_turn_speed, - (float)0.3); - ros::param::param("/homer_navigation/max_move_speed", m_max_move_speed, - (float)0.4); - ros::param::param("/homer_navigation/max_drive_angle", m_max_drive_angle, - (float)0.6); - - // caution factors - ros::param::param("/homer_navigation/map_speed_factor", m_map_speed_factor, - (float)1.0); - ros::param::param("/homer_navigation/waypoint_speed_factor", - m_waypoint_speed_factor, (float)1.0); - ros::param::param("/homer_navigation/obstacle_speed_factor", - m_obstacle_speed_factor, (float)1.0); - ros::param::param("/homer_navigation/target_distance_speed_factor", - m_target_distance_speed_factor, (float)0.4); - - // robot dimensions - ros::param::param("/homer_navigation/min_x", m_min_x, (float)0.3); - ros::param::param("/homer_navigation/min_y", m_min_y, (float)0.27); - - // error durations - ros::param::param("/homer_navigation/callback_error_duration", - m_callback_error_duration, (float)0.3); - - ros::param::param("/homer_navigation/use_ptu", m_use_ptu, (bool)false); - - ros::param::param("/homer_navigation/unknown_threshold", m_unknown_threshold, - (int)50); - ros::param::param("/homer_navigation/waypoint_radius_factor", - m_waypoint_radius_factor, (float)0.25); + // Explorer constructor + ros::param::param("/homer_navigation/allowed_obstacle_distance/min", + m_AllowedObstacleDistance.first, (float)0.3); + ros::param::param("/homer_navigation/allowed_obstacle_distance/max", + m_AllowedObstacleDistance.second, (float)5.0); + ros::param::param("/homer_navigation/safe_obstacle_distance/min", + m_SafeObstacleDistance.first, (float)0.7); + ros::param::param("/homer_navigation/safe_obstacle_distance/max", + m_SafeObstacleDistance.second, (float)1.5); + ros::param::param("/homer_navigation/frontier_safeness_factor", + m_FrontierSafenessFactor, (float)1.4); + ros::param::param("/homer_navigation/safe_path_weight", m_SafePathWeight, + (double)1.2); + ros::param::param("/homer_navigation/waypoint_sampling_threshold", + m_waypoint_sampling_threshold, (float)1.5); + m_AllowedObstacleDistance.first; + m_AllowedObstacleDistance.second; + m_SafeObstacleDistance.first; + m_SafeObstacleDistance.second; + + // check path + ros::param::param("/homer_navigation/check_path", m_check_path, (bool)true); + ros::param::param("/homer_navigation/check_path_max_distance", + m_check_path_max_distance, (float)2.0); + + // collision + ros::param::param("/homer_navigation/collision_distance", + m_collision_distance, (float)0.3); + ros::param::param("/homer_navigation/collision_distance_near_target", + m_collision_distance_near_target, (float)0.2); + ros::param::param("/homer_navigation/backward_collision_distance", + m_backward_collision_distance, (float)0.5); + + // cmd_vel config values + ros::param::param("/homer_navigation/min_turn_angle", m_min_turn_angle, + (float)0.15); + ros::param::param("/homer_navigation/max_turn_speed", m_max_turn_speed, + (float)0.6); + ros::param::param("/homer_navigation/min_turn_speed", m_min_turn_speed, + (float)0.3); + ros::param::param("/homer_navigation/max_move_speed", m_max_move_speed, + (float)0.4); + ros::param::param("/homer_navigation/max_drive_angle", m_max_drive_angle, + (float)0.6); + + // caution factors + ros::param::param("/homer_navigation/map_speed_factor", m_map_speed_factor, + (float)1.0); + ros::param::param("/homer_navigation/waypoint_speed_factor", + m_waypoint_speed_factor, (float)1.0); + ros::param::param("/homer_navigation/obstacle_speed_factor", + m_obstacle_speed_factor, (float)1.0); + ros::param::param("/homer_navigation/target_distance_speed_factor", + m_target_distance_speed_factor, (float)0.4); + + // robot dimensions + ros::param::param("/homer_navigation/min_x", m_min_x, (float)0.3); + ros::param::param("/homer_navigation/min_y", m_min_y, (float)0.27); + + // error durations + ros::param::param("/homer_navigation/callback_error_duration", + m_callback_error_duration, (float)0.3); + + ros::param::param("/homer_navigation/use_ptu", m_use_ptu, (bool)false); + + ros::param::param("/homer_navigation/unknown_threshold", + m_unknown_threshold, (int)50); + ros::param::param("/homer_navigation/waypoint_radius_factor", + m_waypoint_radius_factor, (float)0.25); + if (m_explorer) { + delete m_explorer; + } } void HomerNavigationNode::init() { - m_max_move_sick = 40.0; - m_max_move_down = 40.0; - m_max_move_depth = 40.0; - m_robot_pose.position.x = 0.0; - m_robot_pose.position.y = 0.0; - m_robot_pose.position.z = 0.0; - m_robot_pose.orientation = tf::createQuaternionMsgFromYaw(0.0); - m_robot_last_pose = m_robot_pose; - m_avoided_collision = false; - m_act_speed = 0; - m_angular_avoidance = 0; - m_last_calculations_failed = 0; - m_last_check_path_res = false; - m_new_target = true; - - loadParameters(); - - m_explorer = new Explorer( - m_AllowedObstacleDistance.first, m_AllowedObstacleDistance.second, - m_SafeObstacleDistance.first, m_SafeObstacleDistance.second, - m_SafePathWeight, m_FrontierSafenessFactor, m_unknown_threshold); - m_last_map_data = new std::vector<int8_t>(0); - - m_MainMachine.setState(IDLE); + m_explorer = 0; + m_max_move_sick = 40.0; + m_max_move_down = 40.0; + m_max_move_depth = 40.0; + m_robot_pose.position.x = 0.0; + m_robot_pose.position.y = 0.0; + m_robot_pose.position.z = 0.0; + m_robot_pose.orientation = tf::createQuaternionMsgFromYaw(0.0); + m_robot_last_pose = m_robot_pose; + m_avoided_collision = false; + m_act_speed = 0; + m_angular_avoidance = 0; + m_last_calculations_failed = 0; + m_last_check_path_time = ros::Time::now(); + nav_msgs::OccupancyGrid tmp_map; + tmp_map.header.frame_id = "/map"; + tmp_map.info.resolution = 0; + tmp_map.info.width = 0; + tmp_map.info.height = 0; + tmp_map.info.origin.position.x = 0; + tmp_map.info.origin.position.y = 0; + tmp_map.info.origin.position.z = 0; + m_map = boost::make_shared<nav_msgs::OccupancyGrid>(tmp_map); + + loadParameters(); + + m_MainMachine.setState(IDLE); } HomerNavigationNode::~HomerNavigationNode() { - if (m_explorer) { - delete m_explorer; - } - if (m_last_map_data) { - delete m_last_map_data; - } + if (m_explorer) { + delete m_explorer; + } } void HomerNavigationNode::stopRobot() { - m_act_speed = 0; - geometry_msgs::Twist cmd_vel_msg; - cmd_vel_msg.linear.x = 0; - cmd_vel_msg.linear.y = 0; - cmd_vel_msg.linear.z = 0; - cmd_vel_msg.angular.x = 0; - cmd_vel_msg.angular.y = 0; - cmd_vel_msg.angular.z = 0; - m_cmd_vel_pub.publish(cmd_vel_msg); - ros::Duration(0.1).sleep(); - m_cmd_vel_pub.publish(cmd_vel_msg); - ros::Duration(0.1).sleep(); - m_cmd_vel_pub.publish(cmd_vel_msg); + m_act_speed = 0; + geometry_msgs::Twist cmd_vel_msg; + cmd_vel_msg.linear.x = 0; + cmd_vel_msg.linear.y = 0; + cmd_vel_msg.angular.z = 0; + m_cmd_vel_pub.publish(cmd_vel_msg); + // ros::Duration(0.1).sleep(); + m_cmd_vel_pub.publish(cmd_vel_msg); + // ros::Duration(0.1).sleep(); + m_cmd_vel_pub.publish(cmd_vel_msg); } void HomerNavigationNode::idleProcess() { - if (m_MainMachine.state() == FOLLOWING_PATH) { - if ((ros::Time::now() - m_last_laser_time) > - ros::Duration(m_callback_error_duration)) { - ROS_ERROR_STREAM("Laser data timeout!\n"); - stopRobot(); + if (m_MainMachine.state() != IDLE) { + if ((ros::Time::now() - m_last_laser_time) > + ros::Duration(m_callback_error_duration)) { + ROS_ERROR_STREAM("Laser data timeout!\n"); + stopRobot(); + } + if ((ros::Time::now() - m_last_pose_time) > + ros::Duration(m_callback_error_duration)) { + ROS_ERROR_STREAM("Pose timeout!\n"); + stopRobot(); + } } - if ((ros::Time::now() - m_last_pose_time) > - ros::Duration(m_callback_error_duration)) { - ROS_ERROR_STREAM("Pose timeout!\n"); - stopRobot(); +} + +void HomerNavigationNode::setExplorerMap() { + // adding lasers to map + nav_msgs::OccupancyGrid temp_map = *m_map; + for (const auto& scan : m_scan_map) { + std::vector<geometry_msgs::Point> scan_points; + scan_points = map_tools::laser_msg_to_points( + scan.second, m_transform_listener, "/map"); + for (const auto& point : scan_points) { + Eigen::Vector2i map_point = map_tools::toMapCoords(point, m_map); + int index = map_point.y() * m_map->info.width + map_point.x(); + if (index > 0 && index < m_map->info.width * m_map->info.height) { + temp_map.data[index] = (int8_t)100; + } + } + } + if (m_fast_path_planning) { + maskMap(temp_map); } - } + m_explorer->setOccupancyMap( + boost::make_shared<nav_msgs::OccupancyGrid>(temp_map)); } void HomerNavigationNode::calculatePath() { - if (m_distance_to_target < m_desired_distance && !m_new_target) { - m_path_reaches_target = true; - return; - } - m_explorer->setStart( - map_tools::toMapCoords(m_robot_pose.position, m_origin, m_resolution)); - - bool success; - m_pixel_path = m_explorer->getPath(success); - if (!success) { - ROS_WARN_STREAM("No path found for navigation"); - m_last_calculations_failed++; - ROS_INFO_STREAM("last_calculation_failed: " << m_last_calculations_failed); - if (m_last_calculations_failed > 8) { - sendTargetUnreachableMsg( - homer_mapnav_msgs::TargetUnreachable::NO_PATH_FOUND); + if (!m_explorer) { + return; } - } else { - m_last_calculations_failed = 0; - std::vector<Eigen::Vector2i> waypoint_pixels = - m_explorer->sampleWaypointsFromPath(m_pixel_path, - m_waypoint_sampling_threshold); - m_waypoints.clear(); - ROS_INFO_STREAM("homer_navigation::calculatePath - Path Size: " - << waypoint_pixels.size()); - if (waypoint_pixels.size() > 0) { - for (std::vector<Eigen::Vector2i>::iterator it = waypoint_pixels.begin(); - it != waypoint_pixels.end(); ++it) { - geometry_msgs::PoseStamped poseStamped; - poseStamped.header.frame_id = "/map"; - poseStamped.pose.position = - map_tools::fromMapCoords(*it, m_origin, m_resolution); - poseStamped.pose.orientation.x = 0.0; - poseStamped.pose.orientation.y = 0.0; - poseStamped.pose.orientation.z = 0.0; - poseStamped.pose.orientation.w = 1.0; - m_waypoints.push_back(poseStamped); - } - sendPathData(); - } else { - sendTargetUnreachableMsg( - homer_mapnav_msgs::TargetUnreachable::NO_PATH_FOUND); + if (isTargetPositionReached()) { + return; } - m_last_laser_time = ros::Time::now(); - m_last_pose_time = ros::Time::now(); - } + // setExplorerMap(); + m_explorer->setStart(map_tools::toMapCoords(m_robot_pose.position, m_map)); + + bool success; + m_pixel_path = m_explorer->getPath(success); + if (!success) { + ROS_WARN_STREAM("No path found for navigation"); + m_last_calculations_failed++; + ROS_INFO_STREAM( + "last_calculation_failed: " << m_last_calculations_failed); + if (m_last_calculations_failed > 8) { + sendTargetUnreachableMsg( + homer_mapnav_msgs::TargetUnreachable::NO_PATH_FOUND); + } + } else { + m_last_calculations_failed = 0; + std::vector<Eigen::Vector2i> waypoint_pixels = + m_explorer->sampleWaypointsFromPath(m_pixel_path, + m_waypoint_sampling_threshold); + m_waypoints.clear(); + ROS_INFO_STREAM("homer_navigation::calculatePath - Path Size: " + << waypoint_pixels.size()); + if (waypoint_pixels.size() > 0) { + for (std::vector<Eigen::Vector2i>::iterator it = + waypoint_pixels.begin(); + it != waypoint_pixels.end(); ++it) { + geometry_msgs::PoseStamped poseStamped; + poseStamped.header.frame_id = "/map"; + poseStamped.pose.position = + map_tools::fromMapCoords(*it, m_map); + poseStamped.pose.orientation.x = 0.0; + poseStamped.pose.orientation.y = 0.0; + poseStamped.pose.orientation.z = 0.0; + poseStamped.pose.orientation.w = 1.0; + m_waypoints.push_back(poseStamped); + } + if (m_path_reaches_target) { + geometry_msgs::PoseStamped poseStamped; + poseStamped.header.frame_id = "/map"; + poseStamped.pose.position = m_target_point; + poseStamped.pose.orientation.x = 0; + poseStamped.pose.orientation.y = 0; + poseStamped.pose.orientation.z = 0; + poseStamped.pose.orientation.w = 1; + m_waypoints.push_back(poseStamped); + } + sendPathData(); + } else { + sendTargetUnreachableMsg( + homer_mapnav_msgs::TargetUnreachable::NO_PATH_FOUND); + } + + m_last_laser_time = ros::Time::now(); + m_last_pose_time = ros::Time::now(); + } } void HomerNavigationNode::startNavigation() { - if (m_distance_to_target < m_desired_distance && !m_new_target) { + if (!m_explorer) { + return; + } + if (isTargetPositionReached()) { + return; + } + + ROS_INFO_STREAM("Distance to target still too large (" + << m_distance_to_target + << "m; requested: " << m_desired_distance << "m)"); + setExplorerMap(); + + // check if there still exists a path to the original target + if (m_avoided_collision && m_initial_path_reaches_target && + m_stop_before_obstacle) { + m_explorer->setStart( + map_tools::toMapCoords(m_robot_pose.position, m_map)); + + bool success; + m_pixel_path = m_explorer->getPath(success); + if (!success) { + ROS_INFO_STREAM( + "Initial path would have reached target, new path does not. " + << "Sending target unreachable."); + sendTargetUnreachableMsg( + homer_mapnav_msgs::TargetUnreachable::LASER_OBSTACLE); + return; + } + } + + m_explorer->setStart(map_tools::toMapCoords(m_robot_pose.position, m_map)); + Eigen::Vector2i new_target_approx = m_explorer->getNearestAccessibleTarget( + map_tools::toMapCoords(m_target_point, m_map)); + + geometry_msgs::Point new_target_approx_world = + map_tools::fromMapCoords(new_target_approx, m_map); ROS_INFO_STREAM( - "Will not (re-)plan path: Target position already reached."); - m_path_reaches_target = true; - targetPositionReached(); - return; - } - ROS_INFO_STREAM("Distance to target still too large (" - << m_distance_to_target - << "m; requested: " << m_desired_distance << "m)"); - - if (m_fast_path_planning) { - maskMap(); - } - - - // check if there still exists a path to the original target - if (m_avoided_collision && m_initial_path_reaches_target && - m_stop_before_obstacle) { - m_explorer->setStart( - map_tools::toMapCoords(m_robot_pose.position, m_origin, m_resolution)); + "start Navigation: Approx target: " << new_target_approx_world); - bool success; - m_pixel_path = m_explorer->getPath(success); - if (!success) { - ROS_INFO_STREAM( - "Initial path would have reached target, new path does not. " - << "Sending target unreachable."); - sendTargetUnreachableMsg( - homer_mapnav_msgs::TargetUnreachable::LASER_OBSTACLE); - return; + m_path_reaches_target = + (map_tools::distance(new_target_approx_world, m_target_point) < + m_desired_distance); + m_initial_path_reaches_target = m_path_reaches_target; + + if (map_tools::distance(m_robot_pose.position, new_target_approx_world) < + m_map->info.resolution * 1.5) { + ROS_WARN_STREAM("close to aprox target - final turn"); + m_MainMachine.setState(FINAL_TURN); + } + + bool new_approx_is_better = + (map_tools::distance(m_robot_pose.position, m_target_point) - + map_tools::distance(new_target_approx_world, m_target_point)) > + 2 * m_desired_distance; + if (!new_approx_is_better && !m_path_reaches_target) { + ROS_WARN_STREAM( + "No better way to target found, turning and then reporting as " + "unreachable." + << std::endl + << "Distance to target: " << m_distance_to_target + << "m; requested: " << m_desired_distance << "m"); + m_MainMachine.setState(FINAL_TURN); + } else { + m_explorer->setTarget(new_target_approx); + m_MainMachine.setState(FOLLOWING_PATH); + calculatePath(); } - } - - m_explorer->setStart( - map_tools::toMapCoords(m_robot_pose.position, m_origin, m_resolution)); - Eigen::Vector2i new_target_approx = m_explorer->getNearestAccessibleTarget( - map_tools::toMapCoords(m_target_point, m_origin, m_resolution)); - - geometry_msgs::Point new_target_approx_world = - map_tools::fromMapCoords(new_target_approx, m_origin, m_resolution); - ROS_INFO_STREAM( - "start Navigation: Approx target: " << new_target_approx_world); - - m_path_reaches_target = - (map_tools::distance(new_target_approx_world, m_target_point) < - m_desired_distance); - m_initial_path_reaches_target = m_path_reaches_target; - - bool new_approx_is_better = - (map_tools::distance(m_robot_pose.position, m_target_point) - - map_tools::distance(new_target_approx_world, m_target_point)) > - 2 * m_desired_distance; - if (!new_approx_is_better && !m_path_reaches_target) { - ROS_WARN_STREAM( - "No better way to target found, turning and then reporting as " - "unreachable." - << std::endl - << "Distance to target: " << m_distance_to_target - << "m; requested: " << m_desired_distance << "m"); - m_MainMachine.setState(FINAL_TURN); - } else { - m_explorer->setTarget(new_target_approx); - m_MainMachine.setState(FOLLOWING_PATH); - calculatePath(); - } } void HomerNavigationNode::sendPathData() { - nav_msgs::Path msg; - msg.header.frame_id = "/map"; - msg.header.stamp = ros::Time::now(); - if (m_waypoints.size() > 0) { - msg.poses = m_waypoints; - geometry_msgs::PoseStamped pose_stamped; - pose_stamped.pose = m_robot_pose; - pose_stamped.header.frame_id = "/map"; - pose_stamped.header.stamp = ros::Time::now(); - msg.poses.insert(msg.poses.begin(), pose_stamped); - } - m_path_pub.publish(msg); + nav_msgs::Path msg; + msg.header.frame_id = "/map"; + msg.header.stamp = ros::Time::now(); + if (m_waypoints.size() > 0) { + msg.poses = m_waypoints; + geometry_msgs::PoseStamped pose_stamped; + pose_stamped.pose = m_robot_pose; + pose_stamped.header.frame_id = "/map"; + pose_stamped.header.stamp = ros::Time::now(); + msg.poses.insert(msg.poses.begin(), pose_stamped); + } + m_path_pub.publish(msg); } void HomerNavigationNode::sendTargetReachedMsg() { - stopRobot(); - m_MainMachine.setState(IDLE); - std_msgs::String reached_string_msg; - reached_string_msg.data = m_target_name; - m_target_reached_string_pub.publish(reached_string_msg); - m_waypoints.clear(); - nav_msgs::Path empty_path_msg; - empty_path_msg.poses = m_waypoints; - m_path_pub.publish(empty_path_msg); - ROS_INFO_STREAM("=== Reached Target " << m_target_name << " ==="); + stopRobot(); + m_MainMachine.setState(IDLE); + std_msgs::String reached_string_msg; + reached_string_msg.data = m_target_name; + m_target_reached_string_pub.publish(reached_string_msg); + m_waypoints.clear(); + ROS_INFO_STREAM("=== Reached Target " << m_target_name << " ==="); } void HomerNavigationNode::sendTargetUnreachableMsg(int8_t reason) { - stopRobot(); - m_MainMachine.setState(IDLE); - homer_mapnav_msgs::TargetUnreachable unreachable_msg; - unreachable_msg.reason = reason; - m_target_unreachable_pub.publish(unreachable_msg); - m_waypoints.clear(); - nav_msgs::Path empty_path_msg; - empty_path_msg.poses = m_waypoints; - m_path_pub.publish(empty_path_msg); - ROS_INFO_STREAM("=== TargetUnreachableMsg ==="); -} - -void HomerNavigationNode::targetPositionReached() { - ROS_INFO_STREAM("Target position reached. Distance to target: " - << m_distance_to_target - << "m. Desired distance:" << m_desired_distance << "m"); - stopRobot(); - m_waypoints.clear(); - sendPathData(); - m_MainMachine.setState(FINAL_TURN); - ROS_INFO_STREAM("Turning to look-at point"); + stopRobot(); + m_MainMachine.setState(IDLE); + homer_mapnav_msgs::TargetUnreachable unreachable_msg; + unreachable_msg.reason = reason; + m_target_unreachable_pub.publish(unreachable_msg); + m_waypoints.clear(); + ROS_INFO_STREAM("=== TargetUnreachableMsg ==="); } -bool HomerNavigationNode::checkPath() { - if (m_pixel_path.size() != 0) { - for (unsigned i = 0; i < m_pixel_path.size() - 1; i++) { - geometry_msgs::Point p = - map_tools::fromMapCoords(m_pixel_path.at(i), m_origin, m_resolution); - if (map_tools::distance(m_robot_pose.position, p) > - m_check_path_max_distance) { +bool HomerNavigationNode::isTargetPositionReached() { + if (m_distance_to_target < m_desired_distance && !m_new_target) { + ROS_INFO_STREAM("Target position reached. Distance to target: " + << m_distance_to_target + << "m. Desired distance:" << m_desired_distance << "m"); + stopRobot(); + m_waypoints.clear(); + m_MainMachine.setState(FINAL_TURN); + ROS_INFO_STREAM("Turning to look-at point"); return true; - } - for (int a = 0; a < 5; a++) { - if (map_tools::findValue( - m_last_map_data, m_width, m_height, - m_pixel_path[i].x() + - (m_pixel_path[i + 1].x() - m_pixel_path[i].x()) * a / 4, - m_pixel_path[i].y() + - (m_pixel_path[i + 1].y() - m_pixel_path[i].y()) * a / 4, - 90, 2)) { - ROS_WARN_STREAM("Obstacle detected in current path recalculating"); - return false; - } - } + } else { + return false; } - } } -void HomerNavigationNode::handleCollision() { - if (m_MainMachine.state() == FOLLOWING_PATH) { - stopRobot(); - m_MainMachine.setState(AVOIDING_COLLISION); - ROS_WARN_STREAM("Collision detected while following path!"); - } +bool HomerNavigationNode::obstacleOnPath() { + m_last_check_path_time = ros::Time::now(); + if (m_pixel_path.size() != 0) { + for (auto const& scan : m_scan_map) { + std::vector<geometry_msgs::Point> scan_points; + scan_points = map_tools::laser_msg_to_points( + scan.second, m_transform_listener, "/map"); + + for (unsigned i = 0; i < m_pixel_path.size() - 1; i++) { + geometry_msgs::Point p = + map_tools::fromMapCoords(m_pixel_path.at(i), m_map); + if (map_tools::distance(m_robot_pose.position, p) > + m_check_path_max_distance) { + return false; + } + for (auto const& sp : scan_points) { + if (map_tools::distance(sp, p) < + m_AllowedObstacleDistance.first) { + ROS_INFO_STREAM("Found obstacle"); + return true; + } + } + } + } + } + return false; } void HomerNavigationNode::performNextMove() { - if (m_MainMachine.state() == FOLLOWING_PATH) { - if (m_distance_to_target < m_desired_distance && !m_new_target) { - ROS_INFO_STREAM("Desired distance to target was reached."); - targetPositionReached(); - return; - } - if (m_waypoints.size() == 0) { - ROS_WARN_STREAM( - "No waypoints but trying to perform next move! Skipping."); - return; - } - // if we have accidentaly skipped waypoints, recalculate path - float minDistance = FLT_MAX; - float distance; - unsigned nearestWaypoint = 0; - for (unsigned i = 0; i < m_waypoints.size(); i++) { - distance = map_tools::distance(m_robot_pose.position, - m_waypoints[i].pose.position); - if (distance < minDistance) { - nearestWaypoint = i; - minDistance = distance; - } - } - if (nearestWaypoint != 0) { - // if the target is nearer than the waypoint don't recalculate - if (m_waypoints.size() != 2) { - ROS_WARN_STREAM("Waypoints skipped. Recalculating path!"); - calculatePath(); - if (m_MainMachine.state() != FOLLOWING_PATH) { - return; + if (m_MainMachine.state() == FOLLOWING_PATH) { + if (isTargetPositionReached()) { + return; + } + // check if an obstacle is on the current path + if (m_check_path && + (ros::Time::now() - m_last_check_path_time) > ros::Duration(0.2)) { + if (obstacleOnPath()) { + if (m_seen_obstacle_before) { + stopRobot(); + calculatePath(); + return; + } + m_seen_obstacle_before = true; + } else { + m_seen_obstacle_before = false; + } } - } else { - m_waypoints.erase(m_waypoints.begin()); - } - } - Eigen::Vector2i waypointPixel = map_tools::toMapCoords( - m_waypoints[0].pose.position, m_origin, m_resolution); - float obstacleDistanceMap = m_explorer->getObstacleTransform()->getValue( - waypointPixel.x(), waypointPixel.y()) * - m_resolution; - float waypointRadius = m_waypoint_radius_factor * obstacleDistanceMap; + if (m_waypoints.size() == 0) { + ROS_WARN_STREAM( + "No waypoints but trying to perform next move! Skipping."); + return; + } + // if we have accidentaly skipped waypoints, recalculate path + float minDistance = FLT_MAX; + float distance; + unsigned nearestWaypoint = 0; + for (unsigned i = 0; i < m_waypoints.size(); i++) { + distance = map_tools::distance(m_robot_pose.position, + m_waypoints[i].pose.position); + if (distance < minDistance) { + nearestWaypoint = i; + minDistance = distance; + } + } + if (nearestWaypoint != 0) { + // if the target is nearer than the waypoint don't recalculate + if (m_waypoints.size() != 2) { + ROS_WARN_STREAM("Waypoints skipped. Recalculating path!"); + calculatePath(); + if (m_MainMachine.state() != FOLLOWING_PATH) { + return; + } + } else { + m_waypoints.erase(m_waypoints.begin()); + } + } - if ((waypointRadius < m_resolution) || (m_waypoints.size() == 1)) { - waypointRadius = m_resolution; - } + Eigen::Vector2i waypointPixel = + map_tools::toMapCoords(m_waypoints[0].pose.position, m_map); + float obstacleDistanceMap = + m_explorer->getObstacleTransform()->getValue(waypointPixel.x(), + waypointPixel.y()) * + m_map->info.resolution; + float waypointRadius = m_waypoint_radius_factor * obstacleDistanceMap; + + if ((waypointRadius < m_map->info.resolution) || + (m_waypoints.size() == 1)) { + waypointRadius = m_map->info.resolution; + } - if (m_waypoints.size() != 0) { - // calculate distance between last_pose->current_pose and waypoint - Eigen::Vector2f line; // direction of the line - line[0] = m_robot_pose.position.x - m_robot_last_pose.position.x; - line[1] = m_robot_pose.position.y - m_robot_last_pose.position.y; - Eigen::Vector2f - point_to_start; // vector from the point to the start of the line - point_to_start[0] = - m_robot_last_pose.position.x - m_waypoints[0].pose.position.x; - point_to_start[1] = - m_robot_last_pose.position.y - m_waypoints[0].pose.position.y; - float dot = - point_to_start[0] * line[0] + - point_to_start[1] * line[1]; // dot product of point_to_start * line - Eigen::Vector2f distance; // shortest distance vector from point to line - distance[0] = point_to_start[0] - dot * line[0]; - distance[1] = point_to_start[1] - dot * line[1]; - float distance_to_waypoint = - sqrt((double)(distance[0] * distance[0] + distance[1] * distance[1])); - - // check if current waypoint has been reached - if ((distance_to_waypoint < waypointRadius && m_waypoints.size() > 1) || - (m_distance_to_target < waypointRadius)) { - m_waypoints.erase(m_waypoints.begin()); - } - } + if (m_waypoints.size() != 0) { + // calculate distance between last_pose->current_pose and waypoint + Eigen::Vector2f line; // direction of the line + line[0] = m_robot_pose.position.x - m_robot_last_pose.position.x; + line[1] = m_robot_pose.position.y - m_robot_last_pose.position.y; + Eigen::Vector2f point_to_start; // vector from the point to the + // start of the line + point_to_start[0] = + m_robot_last_pose.position.x - m_waypoints[0].pose.position.x; + point_to_start[1] = + m_robot_last_pose.position.y - m_waypoints[0].pose.position.y; + float dot = point_to_start[0] * line[0] + + point_to_start[1] * + line[1]; // dot product of point_to_start * line + Eigen::Vector2f + distance; // shortest distance vector from point to line + distance[0] = point_to_start[0] - dot * line[0]; + distance[1] = point_to_start[1] - dot * line[1]; + float distance_to_waypoint = sqrt(( + double)(distance[0] * distance[0] + distance[1] * distance[1])); + + // check if current waypoint has been reached + if (distance_to_waypoint < waypointRadius && + m_waypoints.size() >= 1) { + m_waypoints.erase(m_waypoints.begin()); + } + } - sendPathData(); - // last wayoint reached - if (m_waypoints.size() == 0) { - ROS_INFO_STREAM("Last waypoint reached"); - currentPathFinished(); - return; - } + if (m_waypoints.size() == 0) { + ROS_INFO_STREAM("Last waypoint reached"); + currentPathFinished(); + return; + } - if (m_use_ptu) { - ROS_INFO_STREAM("Moving PTU to center next Waypoint"); - homer_ptu_msgs::CenterWorldPoint ptu_msg; - ptu_msg.point.x = m_waypoints[0].pose.position.x; - ptu_msg.point.y = m_waypoints[0].pose.position.y; - ptu_msg.point.z = 0; - ptu_msg.permanent = true; - m_ptu_center_world_point_pub.publish(ptu_msg); - } + sendPathData(); - float distanceToWaypoint = map_tools::distance( - m_robot_pose.position, m_waypoints[0].pose.position); - float angleToWaypoint = angleToPointDeg(m_waypoints[0].pose.position); - if (angleToWaypoint < -180) { - angleToWaypoint += 360; - } - float angle = deg2Rad(angleToWaypoint); - - // linear speed calculation - if (m_avoided_collision) { - if (std::abs(angleToWaypoint) < 10) { - m_avoided_collision = false; - } - } else if (abs(angle) > m_max_drive_angle) { - m_act_speed = 0.0; - } else { - float obstacleMapDistance = 1; - for (int wpi = -1; wpi < std::min((int)m_waypoints.size(), (int)2); - wpi++) { - Eigen::Vector2i robotPixel; - if (wpi == -1) { - robotPixel = map_tools::toMapCoords(m_robot_pose.position, m_origin, - m_resolution); - } else { - robotPixel = map_tools::toMapCoords(m_waypoints[wpi].pose.position, - m_origin, m_resolution); - } - obstacleMapDistance = - std::min((float)obstacleMapDistance, - (float)(m_explorer->getObstacleTransform()->getValue( - robotPixel.x(), robotPixel.y()) * - m_resolution)); - if (obstacleMapDistance <= 0.00001) { - ROS_ERROR_STREAM( - "obstacleMapDistance is below threshold to 0 setting to 1"); - obstacleMapDistance = 1; + if (m_use_ptu) { + ROS_INFO_STREAM("Moving PTU to center next Waypoint"); + homer_ptu_msgs::CenterWorldPoint ptu_msg; + ptu_msg.point.x = m_waypoints[0].pose.position.x; + ptu_msg.point.y = m_waypoints[0].pose.position.y; + ptu_msg.point.z = 0; + ptu_msg.permanent = true; + m_ptu_center_world_point_pub.publish(ptu_msg); } - } - - float max_move_distance_speed = - m_max_move_speed * m_max_move_distance * m_obstacle_speed_factor; - float max_map_distance_speed = - m_max_move_speed * obstacleMapDistance * m_map_speed_factor; - m_act_speed = std::min( - std::max((float)0.1, - m_distance_to_target * m_target_distance_speed_factor), - std::min(std::min(m_max_move_speed, max_move_distance_speed), - std::min(max_map_distance_speed, - distanceToWaypoint * m_waypoint_speed_factor))); - std_msgs::String tmp; - std::stringstream str; - str << "m_obstacle_speed " << max_move_distance_speed - << " max_map_distance_speed " << max_map_distance_speed; - tmp.data = str.str(); - m_debug_pub.publish(tmp); - } - // angular speed calculation - if (angle < 0) { - angle = std::max(angle * (float)0.8, -m_max_turn_speed); - m_act_speed = m_act_speed + angle / 2.0; - if (m_act_speed < 0) { - m_act_speed = 0; - } - } else { - angle = std::min(angle * (float)0.8, m_max_turn_speed); - m_act_speed = m_act_speed - angle / 2.0; - if (m_act_speed < 0) { - m_act_speed = 0; - } - } - geometry_msgs::Twist cmd_vel_msg; - cmd_vel_msg.linear.x = m_act_speed; - cmd_vel_msg.angular.z = angle; - m_cmd_vel_pub.publish(cmd_vel_msg); - - ROS_DEBUG_STREAM("Driving & turning" - << std::endl - << "linear: " << m_act_speed << " angular: " << angle - << std::endl - << "distanceToWaypoint:" << distanceToWaypoint - << "angleToWaypoint: " << angleToWaypoint << std::endl); - } else if (m_MainMachine.state() == AVOIDING_COLLISION) { - if (m_distance_to_target < m_desired_distance && !m_new_target) { - ROS_INFO_STREAM("Collision detected near target. Switch to final turn."); - targetPositionReached(); - } else if (m_max_move_distance <= m_collision_distance && - m_waypoints.size() > 1 || - m_max_move_distance <= m_collision_distance_near_target) { - ROS_WARN_STREAM("Maximum driving distance too short (" - << m_max_move_distance << "m)! Moving back."); - geometry_msgs::Twist cmd_vel_msg; - if (!HomerNavigationNode::backwardObstacle()) { - cmd_vel_msg.linear.x = -0.2; - } else { - if (m_angular_avoidance == 0) { - float angleToWaypoint = angleToPointDeg(m_waypoints[0].pose.position); - if (angleToWaypoint < -180) { + float distanceToWaypoint = map_tools::distance( + m_robot_pose.position, m_waypoints[0].pose.position); + float angleToWaypoint = angleToPointDeg(m_waypoints[0].pose.position); + if (angleToWaypoint < -180) { angleToWaypoint += 360; - } - if (angleToWaypoint < 0) { - m_angular_avoidance = -0.4; - } else { - m_angular_avoidance = 0.4; - } } - cmd_vel_msg.angular.z = m_angular_avoidance; - } - m_cmd_vel_pub.publish(cmd_vel_msg); - } else { - m_angular_avoidance = 0; - m_avoided_collision = true; - ROS_WARN_STREAM("Collision avoided. Updating path."); - currentPathFinished(); - } - } else if (m_MainMachine.state() == FINAL_TURN) { - if (m_use_ptu) { - // reset PTU - homer_ptu_msgs::SetPanTilt msg; - msg.absolute = true; - msg.panAngle = 0; - msg.tiltAngle = 0; - m_set_pan_tilt_pub.publish(msg); - } + float angle = deg2Rad(angleToWaypoint); + + // linear speed calculation + if (m_avoided_collision) { + if (std::abs(angleToWaypoint) < 10) { + m_avoided_collision = false; + } + } else if (abs(angle) > m_max_drive_angle) { + m_act_speed = 0.0; + } else { + float obstacleMapDistance = 1; + for (int wpi = -1; wpi < std::min((int)m_waypoints.size(), (int)2); + wpi++) { + Eigen::Vector2i robotPixel; + if (wpi == -1) { + robotPixel = + map_tools::toMapCoords(m_robot_pose.position, m_map); + } else { + robotPixel = map_tools::toMapCoords( + m_waypoints[wpi].pose.position, m_map); + } + obstacleMapDistance = std::min( + (float)obstacleMapDistance, + (float)(m_explorer->getObstacleTransform()->getValue( + robotPixel.x(), robotPixel.y()) * + m_map->info.resolution)); + if (obstacleMapDistance <= 0.00001) { + ROS_ERROR_STREAM( + "obstacleMapDistance is below threshold to 0 setting " + "to 1"); + obstacleMapDistance = 1; + } + } + + float max_move_distance_speed = m_max_move_speed * + m_max_move_distance * + m_obstacle_speed_factor; + float max_map_distance_speed = + m_max_move_speed * obstacleMapDistance * m_map_speed_factor; + m_act_speed = std::min( + {std::max((float)0.1, m_distance_to_target * + m_target_distance_speed_factor), + m_max_move_speed, max_move_distance_speed, + max_map_distance_speed, + distanceToWaypoint * m_waypoint_speed_factor}); + std_msgs::String tmp; + std::stringstream str; + str << "m_obstacle_speed " << max_move_distance_speed + << " max_map_distance_speed " << max_map_distance_speed; + tmp.data = str.str(); + m_debug_pub.publish(tmp); + } - if (m_skip_final_turn) { - ROS_INFO_STREAM("Final turn skipped. Target reached."); - if (m_path_reaches_target) { - sendTargetReachedMsg(); - } else { - sendTargetUnreachableMsg( - homer_mapnav_msgs::TargetUnreachable::NO_PATH_FOUND); - } - return; - } + // angular speed calculation + if (angle < 0) { + angle = std::max(angle * (float)0.8, -m_max_turn_speed); + m_act_speed = m_act_speed + angle / 2.0; + if (m_act_speed < 0) { + m_act_speed = 0; + } + } else { + angle = std::min(angle * (float)0.8, m_max_turn_speed); + m_act_speed = m_act_speed - angle / 2.0; + if (m_act_speed < 0) { + m_act_speed = 0; + } + } + geometry_msgs::Twist cmd_vel_msg; + cmd_vel_msg.linear.x = m_act_speed; + cmd_vel_msg.angular.z = angle; + m_cmd_vel_pub.publish(cmd_vel_msg); + + ROS_DEBUG_STREAM( + "Driving & turning" + << std::endl + << "linear: " << m_act_speed << " angular: " << angle << std::endl + << "distanceToWaypoint:" << distanceToWaypoint + << "angleToWaypoint: " << angleToWaypoint << std::endl); + } else if (m_MainMachine.state() == AVOIDING_COLLISION) { + if (isTargetPositionReached()) { + return; + } else if (m_max_move_distance <= m_collision_distance && + m_waypoints.size() > 1 || + m_max_move_distance <= m_collision_distance_near_target) { + ROS_WARN_STREAM("Maximum driving distance too short (" + << m_max_move_distance << "m)! Moving back."); + geometry_msgs::Twist cmd_vel_msg; + if (!HomerNavigationNode::backwardObstacle()) { + cmd_vel_msg.linear.x = -0.2; + } else { + if (m_angular_avoidance == 0) { + float angleToWaypoint = + angleToPointDeg(m_waypoints[0].pose.position); + if (angleToWaypoint < -180) { + angleToWaypoint += 360; + } + if (angleToWaypoint < 0) { + m_angular_avoidance = -0.4; + } else { + m_angular_avoidance = 0.4; + } + } + cmd_vel_msg.angular.z = m_angular_avoidance; + } + m_cmd_vel_pub.publish(cmd_vel_msg); + } else { + m_angular_avoidance = 0; + m_avoided_collision = true; + ROS_WARN_STREAM("Collision avoided. Updating path."); + currentPathFinished(); + } + } else if (m_MainMachine.state() == FINAL_TURN) { + if (m_use_ptu) { + // reset PTU + homer_ptu_msgs::SetPanTilt msg; + msg.absolute = true; + msg.panAngle = 0; + msg.tiltAngle = 0; + m_set_pan_tilt_pub.publish(msg); + } - float turnAngle = minTurnAngle(tf::getYaw(m_robot_pose.orientation), - m_target_orientation); - ROS_DEBUG_STREAM( - "homer_navigation::PerformNextMove:: Final Turn. Robot orientation: " - << rad2Deg(tf::getYaw(m_robot_pose.orientation)) - << ". Target orientation: " << rad2Deg(m_target_orientation) - << "homer_navigation::PerformNextMove:: turnAngle: " - << rad2Deg(turnAngle)); - - if (std::fabs(turnAngle) < m_min_turn_angle) { - ROS_INFO_STREAM(":::::::NEAREST WALKABLE TARGET REACHED BECAUSE lower " - << m_min_turn_angle); - ROS_INFO_STREAM("target angle = " << m_target_orientation); - ROS_INFO_STREAM("is angle = " << tf::getYaw(m_robot_pose.orientation)); - ROS_INFO_STREAM("m_distance_to_target = " << m_distance_to_target); - ROS_INFO_STREAM("m_desired_distance = " << m_desired_distance); - if (m_path_reaches_target) { - sendTargetReachedMsg(); - } else { - sendTargetUnreachableMsg( - homer_mapnav_msgs::TargetUnreachable::NO_PATH_FOUND); - } - return; - } else { - if (turnAngle < 0) { - turnAngle = - std::max(std::min(turnAngle, -m_min_turn_speed), -m_max_turn_speed); - } else { - turnAngle = - std::min(std::max(turnAngle, m_min_turn_speed), m_max_turn_speed); - } - geometry_msgs::Twist cmd_vel_msg; - cmd_vel_msg.angular.z = turnAngle; - m_cmd_vel_pub.publish(cmd_vel_msg); + if (m_skip_final_turn) { + ROS_INFO_STREAM("Final turn skipped. Target reached."); + if (m_path_reaches_target) { + sendTargetReachedMsg(); + } else { + sendTargetUnreachableMsg( + homer_mapnav_msgs::TargetUnreachable::NO_PATH_FOUND); + } + return; + } + + float turnAngle = minTurnAngle(tf::getYaw(m_robot_pose.orientation), + m_target_orientation); + ROS_DEBUG_STREAM( + "homer_navigation::PerformNextMove:: Final Turn. Robot " + "orientation: " + << rad2Deg(tf::getYaw(m_robot_pose.orientation)) + << ". Target orientation: " << rad2Deg(m_target_orientation) + << "homer_navigation::PerformNextMove:: turnAngle: " + << rad2Deg(turnAngle)); + + if (std::fabs(turnAngle) < m_min_turn_angle) { + ROS_INFO_STREAM( + ":::::::NEAREST WALKABLE TARGET REACHED BECAUSE lower " + << m_min_turn_angle); + ROS_INFO_STREAM("target angle = " << m_target_orientation); + ROS_INFO_STREAM( + "is angle = " << tf::getYaw(m_robot_pose.orientation)); + ROS_INFO_STREAM("m_distance_to_target = " << m_distance_to_target); + ROS_INFO_STREAM("m_desired_distance = " << m_desired_distance); + if (m_path_reaches_target) { + sendTargetReachedMsg(); + } else { + sendTargetUnreachableMsg( + homer_mapnav_msgs::TargetUnreachable::NO_PATH_FOUND); + } + return; + } else { + if (turnAngle < 0) { + turnAngle = std::max(std::min(turnAngle, -m_min_turn_speed), + -m_max_turn_speed); + } else { + turnAngle = std::min(std::max(turnAngle, m_min_turn_speed), + m_max_turn_speed); + } + geometry_msgs::Twist cmd_vel_msg; + cmd_vel_msg.angular.z = turnAngle; + m_cmd_vel_pub.publish(cmd_vel_msg); + } } - } } void HomerNavigationNode::currentPathFinished() { - ROS_INFO_STREAM("Current path was finished, initiating recalculation."); - m_waypoints.clear(); - stopRobot(); - m_MainMachine.setState(AWAITING_PATHPLANNING_MAP); + ROS_INFO_STREAM("Current path was finished, initiating recalculation."); + m_waypoints.clear(); + stopRobot(); + startNavigation(); } int HomerNavigationNode::angleToPointDeg(geometry_msgs::Point target) { - double cx = m_robot_pose.position.x; - double cy = m_robot_pose.position.y; - int targetAngle = rad2Deg(atan2(target.y - cy, target.x - cx)); - int currentAngle = rad2Deg(tf::getYaw(m_robot_pose.orientation)); - - int angleDiff = targetAngle - currentAngle; - angleDiff = (angleDiff + 180) % 360 - 180; - return angleDiff; + double cx = m_robot_pose.position.x; + double cy = m_robot_pose.position.y; + int targetAngle = rad2Deg(atan2(target.y - cy, target.x - cx)); + int currentAngle = rad2Deg(tf::getYaw(m_robot_pose.orientation)); + + int angleDiff = targetAngle - currentAngle; + angleDiff = (angleDiff + 180) % 360 - 180; + return angleDiff; } bool HomerNavigationNode::drawPolygon( std::vector<geometry_msgs::Point> vertices) { - if (vertices.size() == 0) { - ROS_INFO_STREAM("No vertices given!"); - return false; - } - // make temp. map - std::vector<int> data(m_width * m_height); - for (int i = 0; i < data.size(); i++) { - data[i] = 0; - } - - // draw the lines surrounding the polygon - for (unsigned int i = 0; i < vertices.size(); i++) { - int i2 = (i + 1) % vertices.size(); - drawLine(data, vertices[i].x, vertices[i].y, vertices[i2].x, vertices[i2].y, - 30); - } - // calculate a point in the middle of the polygon - float midX = 0; - float midY = 0; - for (unsigned int i = 0; i < vertices.size(); i++) { - midX += vertices[i].x; - midY += vertices[i].y; - } - midX /= vertices.size(); - midY /= vertices.size(); - // fill polygon - return fillPolygon(data, (int)midX, (int)midY, 30); + if (vertices.size() == 0) { + ROS_INFO_STREAM("No vertices given!"); + return false; + } + // make temp. map + std::vector<int> data(m_map->info.width * m_map->info.height); + for (int i = 0; i < data.size(); i++) { + data[i] = 0; + } + + // draw the lines surrounding the polygon + for (unsigned int i = 0; i < vertices.size(); i++) { + int i2 = (i + 1) % vertices.size(); + drawLine(data, vertices[i].x, vertices[i].y, vertices[i2].x, + vertices[i2].y, 30); + } + // calculate a point in the middle of the polygon + float midX = 0; + float midY = 0; + for (unsigned int i = 0; i < vertices.size(); i++) { + midX += vertices[i].x; + midY += vertices[i].y; + } + midX /= vertices.size(); + midY /= vertices.size(); + // fill polygon + return fillPolygon(data, (int)midX, (int)midY, 30); } void HomerNavigationNode::drawLine(std::vector<int>& data, int startX, int startY, int endX, int endY, int value) { - // bresenham algorithm - int x, y, t, dist, xerr, yerr, dx, dy, incx, incy; - // compute distances - dx = endX - startX; - dy = endY - startY; - - // compute increment - if (dx < 0) { - incx = -1; - dx = -dx; - } else { - incx = dx ? 1 : 0; - } - - if (dy < 0) { - incy = -1; - dy = -dy; - } else { - incy = dy ? 1 : 0; - } - - // which distance is greater? - dist = (dx > dy) ? dx : dy; - // initializing - x = startX; - y = startY; - xerr = dx; - yerr = dy; - - // compute cells - for (t = 0; t < dist; t++) { - int index = x + m_width * y; - if (index < 0 || index > data.size()) { - continue; + // bresenham algorithm + int x, y, t, dist, xerr, yerr, dx, dy, incx, incy; + // compute distances + dx = endX - startX; + dy = endY - startY; + + // compute increment + if (dx < 0) { + incx = -1; + dx = -dx; + } else { + incx = dx ? 1 : 0; } - data[index] = value; - xerr += dx; - yerr += dy; - if (xerr > dist) { - xerr -= dist; - x += incx; + if (dy < 0) { + incy = -1; + dy = -dy; + } else { + incy = dy ? 1 : 0; } - if (yerr > dist) { - yerr -= dist; - y += incy; + + // which distance is greater? + dist = (dx > dy) ? dx : dy; + // initializing + x = startX; + y = startY; + xerr = dx; + yerr = dy; + + // compute cells + for (t = 0; t < dist; t++) { + int index = x + m_map->info.width * y; + if (index < 0 || index > data.size()) { + continue; + } + data[index] = value; + + xerr += dx; + yerr += dy; + if (xerr > dist) { + xerr -= dist; + x += incx; + } + if (yerr > dist) { + yerr -= dist; + y += incy; + } } - } } bool HomerNavigationNode::fillPolygon(std::vector<int>& data, int x, int y, int value) { - int index = x + m_width * y; - bool tmp = false; - - if ((int)m_last_map_data->at(index) > 90) { - tmp = true; - } - if (data[index] != value) { - data[index] = value; - if (fillPolygon(data, x + 1, y, value)) { - tmp = true; - } - if (fillPolygon(data, x - 1, y, value)) { - tmp = true; - } - if (fillPolygon(data, x, y + 1, value)) { - tmp = true; + int index = x + m_map->info.width * y; + bool tmp = false; + + if ((int)m_map->data.at(index) > 90) { + tmp = true; } - if (fillPolygon(data, x, y - 1, value)) { - tmp = true; + if (data[index] != value) { + data[index] = value; + if (fillPolygon(data, x + 1, y, value)) { + tmp = true; + } + if (fillPolygon(data, x - 1, y, value)) { + tmp = true; + } + if (fillPolygon(data, x, y + 1, value)) { + tmp = true; + } + if (fillPolygon(data, x, y - 1, value)) { + tmp = true; + } } - } - return tmp; + return tmp; } bool HomerNavigationNode::backwardObstacle() { - std::vector<geometry_msgs::Point> vertices; - geometry_msgs::Point base_link_point; - geometry_msgs::Point map_point; - Eigen::Vector2i map_coord; - - std::vector<float> x; - std::vector<float> y; - - x.push_back(-m_min_x - m_backward_collision_distance); - y.push_back(m_min_y); - - x.push_back(-m_min_x - m_backward_collision_distance); - y.push_back(-m_min_y); - - x.push_back(-0.1); - y.push_back(-m_min_y); - - x.push_back(-0.1); - y.push_back(m_min_y); - - for (int i = 0; i < x.size(); i++) { - base_link_point.x = x[i]; - base_link_point.y = y[i]; - map_coord = map_tools::toMapCoords( - map_tools::transformPoint(base_link_point, m_transform_listener, - "/base_link", "/map"), - m_origin, m_resolution); - map_point.x = map_coord.x(); - map_point.y = map_coord.y(); - vertices.push_back(map_point); - } - - return drawPolygon(vertices); + std::vector<geometry_msgs::Point> vertices; + geometry_msgs::Point base_link_point; + geometry_msgs::Point map_point; + Eigen::Vector2i map_coord; + + std::vector<float> x; + std::vector<float> y; + + x.push_back(-m_min_x - m_backward_collision_distance); + y.push_back(m_min_y); + + x.push_back(-m_min_x - m_backward_collision_distance); + y.push_back(-m_min_y); + + x.push_back(-0.1); + y.push_back(-m_min_y); + + x.push_back(-0.1); + y.push_back(m_min_y); + + for (int i = 0; i < x.size(); i++) { + base_link_point.x = x[i]; + base_link_point.y = y[i]; + map_coord = map_tools::toMapCoords( + map_tools::transformPoint(base_link_point, m_transform_listener, + "/base_link", "/map"), + m_map); + map_point.x = map_coord.x(); + map_point.y = map_coord.y(); + vertices.push_back(map_point); + } + + return drawPolygon(vertices); } -void HomerNavigationNode::maskMap() { - // generate bounding box - ROS_INFO_STREAM("Calculating Bounding box for fast planning"); - Eigen::Vector2i pose_pixel = - map_tools::toMapCoords(m_robot_pose.position, m_origin, m_resolution); - Eigen::Vector2i target_pixel = - map_tools::toMapCoords(m_target_point, m_origin, m_resolution); - Eigen::Vector2i safe_pixel_distance(m_AllowedObstacleDistance.first * 4, - m_AllowedObstacleDistance.first * 4); - Eigen::AlignedBox2i planning_box; - planning_box.extend(pose_pixel); - planning_box.extend(target_pixel); - ROS_INFO_STREAM("Bounding Box: (" << planning_box.min() << " " - << planning_box.max()); - Eigen::AlignedBox2i safe_planning_box( - planning_box.min() - safe_pixel_distance, - planning_box.max() + safe_pixel_distance); - ROS_INFO_STREAM("safe Bounding Box: (" << safe_planning_box.min() << " " - << safe_planning_box.max()); - ROS_INFO_STREAM("min in m: " << map_tools::fromMapCoords( - safe_planning_box.min(), m_origin, m_resolution)); - ROS_INFO_STREAM("max in m: " << map_tools::fromMapCoords( - safe_planning_box.max(), m_origin, m_resolution)); - for (size_t x = 0; x < m_width; x++) { - for (size_t y = 0; y < m_height; y++){ - if (!safe_planning_box.contains(Eigen::Vector2i(x, y))) { - m_last_map_data->at(y * m_width + x) = -1; - } +void HomerNavigationNode::maskMap(nav_msgs::OccupancyGrid& cmap) { + // generate bounding box + ROS_INFO_STREAM("Calculating Bounding box for fast planning"); + Eigen::Vector2i pose_pixel = + map_tools::toMapCoords(m_robot_pose.position, m_map); + Eigen::Vector2i target_pixel = + map_tools::toMapCoords(m_target_point, m_map); + Eigen::Vector2i safe_pixel_distance(m_AllowedObstacleDistance.first * 4, + m_AllowedObstacleDistance.first * 4); + Eigen::AlignedBox2i planning_box; + planning_box.extend(pose_pixel); + planning_box.extend(target_pixel); + ROS_INFO_STREAM("Bounding Box: (" << planning_box.min() << " " + << planning_box.max()); + Eigen::AlignedBox2i safe_planning_box( + planning_box.min() - safe_pixel_distance, + planning_box.max() + safe_pixel_distance); + ROS_INFO_STREAM("safe Bounding Box: (" << safe_planning_box.min() << " " + << safe_planning_box.max()); + ROS_INFO_STREAM("min in m: " << map_tools::fromMapCoords( + safe_planning_box.min(), m_map)); + ROS_INFO_STREAM("max in m: " << map_tools::fromMapCoords( + safe_planning_box.max(), m_map)); + for (size_t x = 0; x < m_map->info.width; x++) { + for (size_t y = 0; y < m_map->info.height; y++) { + if (!safe_planning_box.contains(Eigen::Vector2i(x, y))) { + cmap.data.at(y * m_map->info.width + x) = -1; + } + } } - } } // convenience math functions float HomerNavigationNode::minTurnAngle(float angle1, float angle2) { - angle1 *= 180.0 / M_PI; - angle2 *= 180.0 / M_PI; + angle1 *= 180.0 / M_PI; + angle2 *= 180.0 / M_PI; - int diff = angle2 - angle1; - diff = (diff + 180) % 360 - 180; - if (diff < -180) { - diff += 360; - } + int diff = angle2 - angle1; + diff = (diff + 180) % 360 - 180; + if (diff < -180) { + diff += 360; + } - float ret = static_cast<double>(diff) * M_PI / 180.0; - return ret; + float ret = static_cast<double>(diff) * M_PI / 180.0; + return ret; } void HomerNavigationNode::refreshParamsCallback( const std_msgs::Empty::ConstPtr& msg) { - ROS_INFO_STREAM("Refreshing Parameters"); - loadParameters(); + ROS_INFO_STREAM("Refreshing Parameters"); + loadParameters(); } void HomerNavigationNode::mapCallback( const nav_msgs::OccupancyGrid::ConstPtr& msg) { - if (m_last_map_data) { - delete m_last_map_data; - } - - m_last_map_data = new std::vector<int8_t>(msg->data); - m_origin = msg->info.origin; - m_width = msg->info.width; - m_height = msg->info.height; - m_resolution = msg->info.resolution; - m_explorer->setOccupancyMap(m_width, m_height, m_origin, - &(*m_last_map_data)[0]); - - switch (m_MainMachine.state()) { - case AWAITING_PATHPLANNING_MAP: - startNavigation(); - break; - case FOLLOWING_PATH: { - if (m_check_path) { - if (!checkPath()) { - if (!m_last_check_path_res) { - calculatePath(); - } - m_last_check_path_res = false; - } else { - m_last_check_path_res = true; - } - } - break; + if (!m_explorer) { + float resolution = msg->info.resolution; + m_explorer = new Explorer(m_AllowedObstacleDistance.first / resolution, + m_AllowedObstacleDistance.second / resolution, + m_SafeObstacleDistance.first / resolution, + m_SafeObstacleDistance.second / resolution, + m_SafePathWeight, m_FrontierSafenessFactor, + m_unknown_threshold); + } + m_map = msg; + if (m_MainMachine.state() != IDLE) { + setExplorerMap(); } - } } void HomerNavigationNode::poseCallback( const geometry_msgs::PoseStamped::ConstPtr& msg) { - m_robot_last_pose = m_robot_pose; - m_robot_pose = msg->pose; - m_last_pose_time = ros::Time::now(); - m_distance_to_target = - map_tools::distance(m_robot_pose.position, m_target_point); - m_new_target = false; - performNextMove(); + m_robot_last_pose = m_robot_pose; + m_robot_pose = msg->pose; + m_last_pose_time = ros::Time::now(); + m_new_target = false; + m_distance_to_target = + map_tools::distance(m_robot_pose.position, m_target_point); + performNextMove(); } void HomerNavigationNode::calcMaxMoveDist() { - m_max_move_distance = - std::min(m_max_move_sick, std::min(m_max_move_down, m_max_move_depth)); - if (m_max_move_distance <= m_collision_distance && - std::fabs(m_act_speed) > 0.1 && m_waypoints.size() > 1 || - m_max_move_distance <= m_collision_distance_near_target && - std::fabs(m_act_speed) > 0.1 && m_waypoints.size() == 1 || - m_max_move_distance <= 0.1) { - handleCollision(); - } + m_max_move_distance = + std::min({m_max_move_sick, m_max_move_down, m_max_move_depth}); + if (m_max_move_distance <= m_collision_distance && + std::fabs(m_act_speed) > 0.1 && m_waypoints.size() > 1 || + m_max_move_distance <= m_collision_distance_near_target && + std::fabs(m_act_speed) > 0.1 && m_waypoints.size() == 1 || + m_max_move_distance <= 0.1) { + if (m_MainMachine.state() == FOLLOWING_PATH) { + stopRobot(); + m_MainMachine.setState(AVOIDING_COLLISION); + ROS_WARN_STREAM("Collision detected while following path!"); + } + } } void HomerNavigationNode::maxDepthMoveDistanceCallback( const std_msgs::Float32::ConstPtr& msg) { - m_max_move_depth = msg->data; - calcMaxMoveDist(); + m_max_move_depth = msg->data; + calcMaxMoveDist(); } void HomerNavigationNode::laserDataCallback( const sensor_msgs::LaserScan::ConstPtr& msg) { - m_last_laser_time = ros::Time::now(); - m_max_move_sick = map_tools::get_max_move_distance( - map_tools::laser_ranges_to_points(msg->ranges, msg->angle_min, - msg->angle_increment, msg->range_min, - msg->range_max, m_transform_listener, - msg->header.frame_id, "/base_link"), - m_min_x, m_min_y); - calcMaxMoveDist(); + m_scan_map[msg->header.frame_id] = msg; + m_last_laser_time = ros::Time::now(); + m_max_move_sick = map_tools::get_max_move_distance( + map_tools::laser_msg_to_points(msg, m_transform_listener, "/base_link"), + m_min_x, m_min_y); + calcMaxMoveDist(); } void HomerNavigationNode::downlaserDataCallback( const sensor_msgs::LaserScan::ConstPtr& msg) { - m_max_move_down = map_tools::get_max_move_distance( - map_tools::laser_ranges_to_points(msg->ranges, msg->angle_min, - msg->angle_increment, msg->range_min, - msg->range_max, m_transform_listener, - msg->header.frame_id, "/base_link"), - m_min_x, m_min_y); - calcMaxMoveDist(); + m_scan_map[msg->header.frame_id] = msg; + m_max_move_down = map_tools::get_max_move_distance( + map_tools::laser_msg_to_points(msg, m_transform_listener, "/base_link"), + m_min_x, m_min_y); + calcMaxMoveDist(); } +void HomerNavigationNode::initNewTarget() { + m_initial_path_reaches_target = false; + m_avoided_collision = false; + m_new_target = true; +} void HomerNavigationNode::startNavigationCallback( const homer_mapnav_msgs::StartNavigation::ConstPtr& msg) { - ROS_INFO_STREAM("This is MY node."); - m_avoided_collision = false; - m_target_point = msg->goal.position; - m_target_orientation = tf::getYaw(msg->goal.orientation); - m_desired_distance = - msg->distance_to_target < 0.1 ? 0.1 : msg->distance_to_target; - m_skip_final_turn = msg->skip_final_turn; - m_fast_path_planning = msg->fast_planning; - m_new_target = true; - m_target_name = ""; - m_initial_path_reaches_target = false; - - ROS_INFO_STREAM("Navigating to target " - << m_target_point.x << ", " << m_target_point.y - << "\nTarget orientation: " << m_target_orientation - << "Desired distance to target: " << m_desired_distance); - - m_MainMachine.setState(AWAITING_PATHPLANNING_MAP); + m_target_point = msg->goal.position; + m_target_orientation = tf::getYaw(msg->goal.orientation); + m_desired_distance = + msg->distance_to_target < 0.1 ? 0.1 : msg->distance_to_target; + m_skip_final_turn = msg->skip_final_turn; + m_fast_path_planning = msg->fast_planning; + m_target_name = ""; + + ROS_INFO_STREAM("Navigating to target " + << m_target_point.x << ", " << m_target_point.y + << "\nTarget orientation: " << m_target_orientation + << "Desired distance to target: " << m_desired_distance); + initNewTarget(); + startNavigation(); } void HomerNavigationNode::moveBaseSimpleGoalCallback( const geometry_msgs::PoseStamped::ConstPtr& msg) { - if (msg->header.frame_id != "map") { - tf::StampedTransform transform; - if (m_transform_listener.waitForTransform("map", msg->header.frame_id, - msg->header.stamp, - ros::Duration(1.0))) { - try { - m_transform_listener.lookupTransform("map", msg->header.frame_id, - msg->header.stamp, transform); - tf::Vector3 targetPos(msg->pose.position.x, msg->pose.position.y, - msg->pose.position.z); - targetPos = transform * targetPos; - m_target_point.x = targetPos.getX(); - m_target_point.y = targetPos.getY(); - m_target_orientation = tf::getYaw( - transform * - tf::Quaternion(msg->pose.orientation.x, msg->pose.orientation.y, - msg->pose.orientation.z, msg->pose.orientation.w)); - } catch (tf::TransformException ex) { - ROS_ERROR_STREAM(ex.what()); - return; - } + if (msg->header.frame_id != "map") { + tf::StampedTransform transform; + if (m_transform_listener.waitForTransform("map", msg->header.frame_id, + msg->header.stamp, + ros::Duration(1.0))) { + try { + m_transform_listener.lookupTransform( + "map", msg->header.frame_id, msg->header.stamp, transform); + tf::Vector3 targetPos(msg->pose.position.x, + msg->pose.position.y, + msg->pose.position.z); + targetPos = transform * targetPos; + m_target_point.x = targetPos.getX(); + m_target_point.y = targetPos.getY(); + m_target_orientation = tf::getYaw( + transform * tf::Quaternion(msg->pose.orientation.x, + msg->pose.orientation.y, + msg->pose.orientation.z, + msg->pose.orientation.w)); + } catch (tf::TransformException ex) { + ROS_ERROR_STREAM(ex.what()); + return; + } + } else { + try { + m_transform_listener.lookupTransform( + "map", msg->header.frame_id, ros::Time(0), transform); + tf::Vector3 targetPos(msg->pose.position.x, + msg->pose.position.y, + msg->pose.position.z); + targetPos = transform * targetPos; + m_target_point.x = targetPos.getX(); + m_target_point.y = targetPos.getY(); + m_target_orientation = tf::getYaw( + transform * tf::Quaternion(msg->pose.orientation.x, + msg->pose.orientation.y, + msg->pose.orientation.z, + msg->pose.orientation.w)); + } catch (tf::TransformException ex) { + ROS_ERROR_STREAM(ex.what()); + return; + } + } } else { - try { - m_transform_listener.lookupTransform("map", msg->header.frame_id, - ros::Time(0), transform); - tf::Vector3 targetPos(msg->pose.position.x, msg->pose.position.y, - msg->pose.position.z); - targetPos = transform * targetPos; - m_target_point.x = targetPos.getX(); - m_target_point.y = targetPos.getY(); - m_target_orientation = tf::getYaw( - transform * - tf::Quaternion(msg->pose.orientation.x, msg->pose.orientation.y, - msg->pose.orientation.z, msg->pose.orientation.w)); - } catch (tf::TransformException ex) { - ROS_ERROR_STREAM(ex.what()); - return; - } + m_target_point = msg->pose.position; + m_target_orientation = tf::getYaw(msg->pose.orientation); } - } else { - m_target_point = msg->pose.position; - m_target_orientation = tf::getYaw(msg->pose.orientation); - } - m_avoided_collision = false; - m_desired_distance = 0.1; - m_skip_final_turn = false; - m_fast_path_planning = false; - m_new_target = true; - - ROS_INFO_STREAM("Navigating to target via Move Base Simple x: " - << m_target_point.x << ", y: " << m_target_point.y - << "\nTarget orientation: " << m_target_orientation - << " Desired distance to target: " << m_desired_distance - << "\nframe_id: " << msg->header.frame_id); - - m_MainMachine.setState(AWAITING_PATHPLANNING_MAP); + m_desired_distance = 0.1; + m_skip_final_turn = false; + m_fast_path_planning = false; + + ROS_INFO_STREAM("Navigating to target via Move Base Simple x: " + << m_target_point.x << ", y: " << m_target_point.y + << "\nTarget orientation: " << m_target_orientation + << " Desired distance to target: " << m_desired_distance + << "\nframe_id: " << msg->header.frame_id); + initNewTarget(); + startNavigation(); } void HomerNavigationNode::navigateToPOICallback( const homer_mapnav_msgs::NavigateToPOI::ConstPtr& msg) { - homer_mapnav_msgs::GetPointsOfInterest srv; - m_get_POIs_client.call(srv); - std::vector<homer_mapnav_msgs::PointOfInterest>::iterator it; - for (it = srv.response.poi_list.pois.begin(); - it != srv.response.poi_list.pois.end(); ++it) { - if (it->name == msg->poi_name) { - m_avoided_collision = false; - m_target_point = it->pose.position; - m_target_orientation = tf::getYaw(it->pose.orientation); - m_desired_distance = - msg->distance_to_target < 0.1 ? 0.1 : msg->distance_to_target; - m_skip_final_turn = msg->skip_final_turn; - m_fast_path_planning = false; - m_new_target = true; - m_target_name = msg->poi_name; - m_stop_before_obstacle = msg->stop_before_obstacle; - - ROS_INFO_STREAM("Navigating to target " - << m_target_point.x << ", " << m_target_point.y - << "\nTarget orientation: " << m_target_orientation - << "Desired distance to target: " << m_desired_distance); - - m_MainMachine.setState(AWAITING_PATHPLANNING_MAP); - return; + homer_mapnav_msgs::GetPointsOfInterest srv; + m_get_POIs_client.call(srv); + std::vector<homer_mapnav_msgs::PointOfInterest>::iterator it; + for (it = srv.response.poi_list.pois.begin(); + it != srv.response.poi_list.pois.end(); ++it) { + if (it->name == msg->poi_name) { + m_target_point = it->pose.position; + m_target_orientation = tf::getYaw(it->pose.orientation); + m_desired_distance = + msg->distance_to_target < 0.1 ? 0.1 : msg->distance_to_target; + m_skip_final_turn = msg->skip_final_turn; + m_fast_path_planning = false; + m_target_name = msg->poi_name; + m_stop_before_obstacle = msg->stop_before_obstacle; + + ROS_INFO_STREAM("Navigating to target " + << m_target_point.x << ", " << m_target_point.y + << "\nTarget orientation: " << m_target_orientation + << "Desired distance to target: " + << m_desired_distance); + initNewTarget(); + startNavigation(); + return; + } } - } - ROS_ERROR_STREAM("No point of interest with name '" - << msg->poi_name << "' found in current poi list"); - sendTargetUnreachableMsg(homer_mapnav_msgs::TargetUnreachable::UNKNOWN); + ROS_ERROR_STREAM("No point of interest with name '" + << msg->poi_name << "' found in current poi list"); + sendTargetUnreachableMsg(homer_mapnav_msgs::TargetUnreachable::UNKNOWN); } void HomerNavigationNode::stopNavigationCallback( const std_msgs::Empty::ConstPtr& msg) { - ROS_INFO_STREAM("Stopping navigation."); - m_MainMachine.setState(IDLE); - m_avoided_collision = false; - stopRobot(); - - m_waypoints.clear(); - nav_msgs::Path empty_path_msg; - empty_path_msg.poses = m_waypoints; - m_path_pub.publish(empty_path_msg); + ROS_INFO_STREAM("Stopping navigation."); + m_MainMachine.setState(IDLE); + stopRobot(); + + m_waypoints.clear(); } void HomerNavigationNode::unknownThresholdCallback( const std_msgs::Int8::ConstPtr& msg) { - m_explorer->setUnknownThreshold(static_cast<int>(msg->data)); + if (m_explorer) { + m_explorer->setUnknownThreshold(static_cast<int>(msg->data)); + } } int main(int argc, char** argv) { - ros::init(argc, argv, "homer_navigation"); + ros::init(argc, argv, "homer_navigation"); - HomerNavigationNode node; + HomerNavigationNode node; - ros::Rate rate(50); + ros::Rate rate(50); - while (ros::ok()) { - ros::spinOnce(); - node.idleProcess(); - rate.sleep(); - } + while (ros::ok()) { + ros::spinOnce(); + node.idleProcess(); + rate.sleep(); + } - return 0; + return 0; }