#ifndef EXPLORER_H #define EXPLORER_H #include <vector> #include <geometry_msgs/Pose.h> #include "GridMap.h" #include "tools/tools.h" namespace ExplorerConstants { static int8_t UNKNOWN; static const int8_t NOT_SEEN_YET = -1; static const double MAX_DISTANCE = DBL_MAX; static const double MAX_COST = DBL_MAX; static const int OBSTACLE = INT_MAX; } /** * @class Explorer * @author Malte Knauf, Stephan Wirth, David Gossow (RX) * @brief Path planning & exploration class * * Usage: * * - Call setOccupancyMap() to set the base map for path finding. * - Set a start point by calling setStart() * * - For path planning: * +Choose a target by calling setTarget() * +To correct a target to the nearest approachable position, * call getNearestAccessibleTarget * +Call getPathTransformPath() * * -For exploration: * +Call resetExploration() * +Call getExplorationTransformPath() * +The calculated target is the last element in the returned path * * - Call sampleWaypointsFromPath() to extract waypoints from a calculated path * * This class uses a couple of "maps" for computation and storing data: * * - m_OccupancyMap stores the occupancy probabilities in double values. A value of 100 means * totally occupied, 0 totally free. * - m_ObstacleDistanceMap stores in each cell the distance (one unit = one cell) to the nearest obstacle. * This map is computed by an eucledian distance transformation from m_OccupancyMap. * - m_FrontierMap is a bool map which has 1 in frontier cells and 0 in all others. A frontier * is defined as a free cell that has one of its four direct neighbours in unknown space and is "safe" for * the robot (m_ObstacleDistanceMap is used for that). * - m_DrivingDistanceMap is a double map that stores for each cell the distance to m_Start. It is computed * by a flood-fill (seed-fill) algorithm. The values are therefor only an approximation and not exact. * m_DrivingDistanceMap is used to search the nearest frontier when requesting an auto target. * - m_TargetMap is a double map that stores for each cell the distance to m_Target. It is computed * like m_DrivingDistanceMap. This map is used as heuristic for the A*-Pathfinding algorithm. * - m_NavigationMap is used to mark the cells that are touched by the A*-Pathfinding algorithm. * * * The coordinate system and units that are used in this class are based on map cells. * @see GridMap * */ class Explorer { public: /** * @brief Default constructor. * @param minAllowedObstacleDistance,maxAllowedObstacleDistance Range of allowed distances to next obstacle [Pixels] * @param minSafeObstacleDistance,maxSafeObstacleDistance Range of distances to next obstacle considered as safe [Pixels] * @param safePathWeight Weight for safer path */ Explorer ( double minAllowedObstacleDistance, double maxAllowedObstacleDistance, double minSafeObstacleDistance, double maxSafeObstacleDistance, double safePathWeight, double frontierSafenessFactor=1.0, int unknownThreshold=50 ); /** * @brief Destructor deletes all dynamically allocated memory used by the maps */ ~Explorer(); void setUnknownThreshold(int unknownTresh); void setAllowedObstacleDistance ( double min, double max ); void setSafeObstacleDistance ( double min, double max ); void setFrontierSafenessFactor ( double frontierSafenessFactor ); void setSafePathWeight ( double weight ); /** * @brief Copies and sets the occupancy map. * @param width Width of the map * @param height Height of the map * @param origin Real-world pose of the cell (0,0) in the map * @param data GridMap-data (occupancy probabilities: 0 = free, 100 = occupied) of size width * height */ void setOccupancyMap ( int width, int height, geometry_msgs::Pose origin, int8_t* mapData); /** only update occupied areas in current occupancy map */ void updateObstacles ( int width, int height, geometry_msgs::Pose origin, int8_t* mapData ); /** * @brief Sets the start position for the path finding algorithm. * m_Start is set to the given value. * If startPixel lies outside the map, m_Start remains untouched. * @param startPixel Start position for path finding in pixel (map-) coordinates. */ void setStart ( Eigen::Vector2i start ); /** * @brief Resets the internal state of the exploration mode. * Sets m_DesiredDistance to 0, such that getExplorationTransformPath() triggers * a frontier exploration if there is no prior call of setTarget(point, distance). * Call this method once before every exploration. */ void resetExploration(); /** * Sets the target position for path finding. m_Target is set to the given value. * If endPixel lies outside of the map, m_Target remains untouched. * computeTargetDistanceMap() is called at the end of this method. m * @param targetPixel Target to reach from startPixel */ void setTarget ( Eigen::Vector2i targetPixel ); /** * Sets the target region for path finding. m_ExplorationMap is set to the given region. * If targetPixel lies outside of the map, the exploration map is set empty. * @param targetPixel Center of the target region to reach from startPixel * @param radius Radius of the target region in pixels */ void setTarget( Eigen::Vector2i targetPixel, int radius ); /** * @brief find the nearest position to target that is approachble from the start position */ Eigen::Vector2i getNearestAccessibleTarget ( Eigen::Vector2i target ); /** * @brief find the nearest position to target surpassing the minimum obstacle distance */ Eigen::Vector2i getNearestWalkablePoint ( Eigen::Vector2i target ); /** * @brief Returns the map-coordinates of the nearest frontier to m_Start. * Uses m_DrivingDistanceMap and m_ObstacleDistanceMap. If there is no frontier left, * nextFrontier remains untouched. * @param[out] nextFrontier Nearest frontier in map-coordinates. * @return true if frontier found and stored in nextFrontier, false if no frontier found (nextFrontier * remains untouched). */ bool getNearestFrontier ( Eigen::Vector2i& nextFrontier ); /** * Computes the path from m_Start to m_Target with path transform. * The result is returned. If the returned vector contains no elements, there is no path. * @return vector with path points */ std::vector<Eigen::Vector2i> getPath( bool &success ); /** * Computes the path from m_Start to the next frontier using exploration transform. * The result is returned. If the returned vector contains no elements, there is no path. * @return vector with path points */ std::vector<Eigen::Vector2i> getExplorationTransformPath( bool &success ); /** * @brief Returns a version of the path that contains less vertices. * @note The nearer the next obstacle, the more waypoints are created. * @param path List of vertices to be simplified * @param treshold[0..1] a lower threshold results in more waypoints (default:1.0) * @return Vector of (sampled) waypoints. */ std::vector<Eigen::Vector2i> sampleWaypointsFromPath ( std::vector<Eigen::Vector2i> path, float threshold=1.0 ); /** * Getters for the different transforms (see constructor for description) */ GridMap<int8_t>* getOccupancyMap(); GridMap<double>* getObstacleTransform(); GridMap<double>* getCostTransform(); GridMap<bool>* getTargetMap(); GridMap<double>* getDrivingDistanceTransform(); GridMap<double>* getTargetDistanceTransform(); GridMap<double>* getPathTransform(); GridMap<double>* getExplorationTransform(); /** * @return Start position */ Eigen::Vector2i getStart() const; /** * @return Target position */ Eigen::Vector2i getTarget() const; private: /** @brief Delete the given map and set pointer to 0 */ template <class T> void releaseMap ( GridMap<T>*& map ) { if ( map ) { delete map; map=0; } } /** @brief Delete and re-create given map */ template <class T> void resetMap ( GridMap<T>*& map ) { if ( !m_OccupancyMap ) { ROS_ERROR ( "Occupancy map is missing." ); return; } releaseMap ( map ); map = new GridMap<T> ( m_OccupancyMap->width(), m_OccupancyMap->height() ); } /** * @return true if the robot can stand on the given position without touching an obstacle, false otherwise * @warning Call computeWalkableMaps before */ inline bool isWalkable ( int x, int y ) const { return ( ( m_OccupancyMap->getValue ( x, y ) < ExplorerConstants::UNKNOWN ) && ( m_ObstacleTransform->getValue ( x, y ) > m_MinAllowedObstacleDistance ) ); } /** * @return true if point is approachable from the current start position, false otherwise. * @warning m_OccupancyMap, m_ObstacleTransform and m_DrivingDistanceTransform have to be present! * @warning Call computeApproachableMaps before */ inline bool isApproachable ( int x, int y ) const { return ( m_DrivingDistanceTransform->getValue ( x, y ) < ExplorerConstants::MAX_DISTANCE ); } /** @brief Releases all memory of the member maps */ void releaseMaps(); /** * @brief Helper function for computeDistanceTransformation. * @param f 1D-Array for distance transformation * @param n Number of elements in f * @return Distance transformation of f */ double* distanceTransform1D ( double *f, int n ); /** * @brief Fills the given map from given start point with distance values to this point. * The filling will only be performed on cells that are marked as free in m_OccupancyMap and * that have an obstacle distance value between m_MinimumObstacleDistance and m_MaximumObstacleDistance. * The map that is passed as argument will be fully overwritten by this function. * @param map GridMap to fill * @param start Start point for the fill algorithm */ void distanceFloodFill ( GridMap<double>* map, Eigen::Vector2i start ); /** @brief Compute map needed for path calculation */ void computePathTransform(); /** @brief Compute map needed for exploration path calculation */ void computeExplorationTransform(); /** @brief Compute the distances to the next obstacle with eucledian distance transform from m_OccupancyMap. */ void computeObstacleTransform(); /** @brief Compute cost function based on obstacle transform */ void computeCostTransform(); /** @brief Compute the frontiers between free and unknown space. Depends on OccupancyMap and ObstacleTransform. */ void computeFrontierMap(); /** @brief Compute the target region (a circle of radius m_DesiredDistance around m_Target). */ void computeRegionMap(); /** @brief Compute the target map, which is either a frontier map or a region map. */ void computeTargetMap(); /** @brief Compute a map of driving distances from the start point */ void computeDrivingDistanceTransform(); /** @brief Compute a map of driving distances to the target point */ void computeTargetDistanceTransform(); /** @brief Compute maps needed for isWalkable */ void computeWalkableMaps(); /** @brief Compute maps needed for isApproachable */ void computeApproachableMaps(); /** @brief Start point for the way search algorithm. */ Eigen::Vector2i m_Start; /** @brief Target for the way search algorithm */ Eigen::Vector2i m_Target; /** @brief Desired distance to target in pixels */ int m_DesiredDistance; /** @brief Occupancy map */ GridMap<int8_t>* m_OccupancyMap; /** @see computeObstacleTransform */ GridMap<double>* m_ObstacleTransform; /** @see computeCostTransform */ GridMap<double>* m_CostTransform; /** @see computeTargetMap */ GridMap<bool>* m_TargetMap; /** computeDrivingDistanceTransform */ GridMap<double>* m_DrivingDistanceTransform; /** @see computeTargetDistanceTransform */ GridMap<double>* m_TargetDistanceTransform; /** @see computePathTransform */ GridMap<double>* m_PathTransform; /** @see computeExplorationTransform */ GridMap<double>* m_ExplorationTransform; /** @see constructor */ double m_MinAllowedObstacleDistance; double m_MaxAllowedObstacleDistance; double m_MinSafeObstacleDistance; double m_MaxSafeObstacleDistance; /** * Weight for safer path */ double m_SafePathWeight; /** * Factor for minObstacleDistance that determines if a frontier pixel is valid */ double m_FrontierSafenessFactor; /** * Real-world pose of the point (0,0) in the map */ geometry_msgs::Pose m_Origin; }; #endif